Merge branch 'origin/release-2020' into merge-release-2020-into-master
authorPaul Bauer <paul.bauer.q@gmail.com>
Wed, 7 Oct 2020 06:55:19 +0000 (08:55 +0200)
committerPaul Bauer <paul.bauer.q@gmail.com>
Wed, 7 Oct 2020 06:55:19 +0000 (08:55 +0200)
Resolved Conflicts:
admin/gitlab-ci/archive.gitlab-ci.yml
admin/gitlab-ci/global.gitlab-ci.yml
admin/gitlab-ci/gromacs.gitlab-ci.yml
admin/gitlab-ci/lint.gitlab-ci.yml
admin/gitlab-ci/python-gmxapi.gitlab-ci.yml
admin/gitlab-ci/rules.gitlab-ci.yml
admin/gitlab-ci/sample_restraint-regression.gitlab-ci.yml
cmake/gmxVersionInfo.cmake
src/gromacs/fileio/checkpoint.cpp
src/gromacs/hardware/printhardware.cpp
src/gromacs/mdlib/md_support.cpp
src/gromacs/mdlib/trajectory_writing.cpp
src/gromacs/mdrun/runner.cpp
src/gromacs/modularsimulator/domdechelper.cpp
src/gromacs/modularsimulator/domdechelper.h
src/gromacs/modularsimulator/freeenergyperturbationelement.cpp
src/gromacs/modularsimulator/freeenergyperturbationelement.h
src/gromacs/modularsimulator/modularsimulator.cpp
src/gromacs/tools/trjcat.cpp
src/gromacs/topology/topology.h
src/gromacs/utility/fatalerror.cpp
src/gromacs/utility/futil.cpp
src/gromacs/utility/init.cpp
src/programs/mdrun/tests/CMakeLists.txt
tests/CMakeLists.txt

Change-Id: Icd5d9c78ff2cfb0598c3cb55b057487ca098a1f0

2164 files changed:
.gitattributes
CMakeLists.txt
COPYING
admin/builds/clang-analyzer.py [deleted file]
admin/builds/clang-format-update.py [deleted file]
admin/builds/clang-format.py [deleted file]
admin/builds/coverage.py [deleted file]
admin/builds/documentation.py [deleted file]
admin/builds/gpucomm-matrix.txt [deleted file]
admin/builds/gpuupdate-matrix.txt [deleted file]
admin/builds/gromacs.py [deleted file]
admin/builds/nightly-matrix.txt [deleted file]
admin/builds/post-submit-matrix.txt [deleted file]
admin/builds/pre-submit-matrix.txt [deleted file]
admin/builds/regressiontests-update.py [deleted file]
admin/builds/release-matrix.txt [deleted file]
admin/builds/uncrustify.py [deleted file]
admin/ci-scripts/build-and-test-py-gmxapi-0.1.sh
admin/ci-scripts/build-and-test-py-gmxapi-0.2.sh
admin/ci-scripts/build-and-test-sample_restraint-2020.sh
admin/ci-scripts/build-and-test-sample_restraint-2021.sh
admin/clang-format.sh
admin/clang-tidy.sh
admin/containers/buildall.sh
admin/containers/scripted_gmx_docker_builds.py
admin/containers/utility.py
admin/copyright.py
admin/copyright.sh
admin/dockerfiles/base/Dockerfile [deleted file]
admin/dockerfiles/buildall.sh [deleted file]
admin/dockerfiles/ci-clang/Dockerfile [deleted file]
admin/dockerfiles/ci-docs-clang/Dockerfile [deleted file]
admin/dockerfiles/ci-docs-gcc/Dockerfile [deleted file]
admin/dockerfiles/ci-gcc-8-cuda-10.2/Dockerfile [deleted file]
admin/dockerfiles/ci-gcc/Dockerfile [deleted file]
admin/git-pre-commit
admin/gitlab-ci/archive.gitlab-ci.yml
admin/gitlab-ci/documentation.gitlab-ci.yml
admin/gitlab-ci/global.gitlab-ci.yml
admin/gitlab-ci/gromacs.gitlab-ci.yml
admin/gitlab-ci/lint.gitlab-ci.yml
admin/gitlab-ci/python-gmxapi.gitlab-ci.yml
admin/gitlab-ci/rules.gitlab-ci.yml
admin/gitlab-ci/sample_restraint-regression.gitlab-ci.yml
admin/gitlab-ci/sample_restraint.gitlab-ci.yml
admin/lsan-suppressions.txt [new file with mode: 0644]
admin/split_copyright_years.sh [moved from admin/builds/update-regtest-hash.py with 63% similarity, mode: 0755]
admin/ubsan-suppressions.txt [new file with mode: 0644]
admin/uncrustify.cfg [deleted file]
admin/uncrustify.sh [deleted file]
api/CMakeLists.txt [moved from admin/builds/source-package.py with 57% similarity]
api/gmxapi/CMakeLists.txt [new file with mode: 0644]
api/gmxapi/cpp/CMakeLists.txt [new file with mode: 0644]
api/gmxapi/cpp/cmake/gmxapi-config.cmake.in [moved from src/api/cpp/cmake/gmxapi-config.cmake.in with 84% similarity]
api/gmxapi/cpp/context.cpp [moved from src/api/cpp/context.cpp with 56% similarity]
api/gmxapi/cpp/context_impl.h [new file with mode: 0644]
api/gmxapi/cpp/createsession.h [moved from src/api/cpp/createsession.h with 94% similarity]
api/gmxapi/cpp/exceptions.cpp [moved from src/api/cpp/exceptions.cpp with 96% similarity]
api/gmxapi/cpp/gmxapi.cpp [moved from src/api/cpp/gmxapi.cpp with 96% similarity]
api/gmxapi/cpp/md.cpp [moved from src/api/cpp/md.cpp with 98% similarity]
api/gmxapi/cpp/md_impl.h [moved from src/api/cpp/md_impl.h with 97% similarity]
api/gmxapi/cpp/mdmodule.cpp [moved from src/api/cpp/mdmodule.cpp with 96% similarity]
api/gmxapi/cpp/mdsignals.cpp [moved from src/api/cpp/mdsignals.cpp with 98% similarity]
api/gmxapi/cpp/mdsignals.h [moved from src/api/cpp/mdsignals.h with 97% similarity]
api/gmxapi/cpp/resourceassignment.cpp [new file with mode: 0644]
api/gmxapi/cpp/session.cpp [moved from src/api/cpp/session.cpp with 86% similarity]
api/gmxapi/cpp/session_impl.h [moved from src/api/cpp/session_impl.h with 93% similarity]
api/gmxapi/cpp/sessionresources.h [moved from src/api/cpp/sessionresources.h with 98% similarity]
api/gmxapi/cpp/status.cpp [moved from src/api/cpp/status.cpp with 97% similarity]
api/gmxapi/cpp/system.cpp [moved from src/api/cpp/system.cpp with 98% similarity]
api/gmxapi/cpp/system_impl.h [moved from src/api/cpp/system_impl.h with 97% similarity]
api/gmxapi/cpp/tpr.cpp [moved from src/api/cpp/tpr.cpp with 98% similarity]
api/gmxapi/cpp/version.cpp [moved from src/api/cpp/version.cpp with 97% similarity]
api/gmxapi/cpp/workflow.cpp [moved from src/api/cpp/workflow.cpp with 96% similarity]
api/gmxapi/cpp/workflow.h [moved from src/api/cpp/workflow.h with 98% similarity]
api/gmxapi/cpp/workflow_impl.h [moved from src/api/cpp/workflow_impl.h with 96% similarity]
api/gmxapi/include/gmxapi/compat/mdparams.h [moved from src/api/cpp/include/gmxapi/compat/mdparams.h with 97% similarity]
api/gmxapi/include/gmxapi/compat/tpr.h [moved from src/api/cpp/include/gmxapi/compat/tpr.h with 98% similarity]
api/gmxapi/include/gmxapi/context.h [moved from src/api/cpp/include/gmxapi/context.h with 69% similarity]
api/gmxapi/include/gmxapi/exceptions.h [moved from src/api/cpp/include/gmxapi/exceptions.h with 98% similarity]
api/gmxapi/include/gmxapi/gmxapi.h [moved from src/api/cpp/include/gmxapi/gmxapi.h with 99% similarity]
api/gmxapi/include/gmxapi/gmxapicompat.h [moved from src/api/cpp/include/gmxapi/gmxapicompat.h with 99% similarity]
api/gmxapi/include/gmxapi/gromacsfwd.h [moved from src/api/cpp/include/gmxapi/gromacsfwd.h with 97% similarity]
api/gmxapi/include/gmxapi/md.h [moved from src/api/cpp/include/gmxapi/md.h with 98% similarity]
api/gmxapi/include/gmxapi/md/mdmodule.h [moved from src/api/cpp/include/gmxapi/md/mdmodule.h with 98% similarity]
api/gmxapi/include/gmxapi/md/mdsignals.h [moved from src/api/cpp/include/gmxapi/md/mdsignals.h with 98% similarity]
api/gmxapi/include/gmxapi/mpi/gmxapi_mpi.h [new file with mode: 0644]
api/gmxapi/include/gmxapi/session.h [moved from src/api/cpp/include/gmxapi/session.h with 99% similarity]
api/gmxapi/include/gmxapi/session/resources.h [moved from src/api/cpp/include/gmxapi/session/resources.h with 96% similarity]
api/gmxapi/include/gmxapi/status.h [moved from src/api/cpp/include/gmxapi/status.h with 98% similarity]
api/gmxapi/include/gmxapi/system.h [moved from src/api/cpp/include/gmxapi/system.h with 99% similarity]
api/gmxapi/include/gmxapiversion.h.in [new file with mode: 0644]
api/gmxapi/include/resourceassignment.cmakein.h [new file with mode: 0644]
api/nblib/CMakeLists.txt [new file with mode: 0644]
api/nblib/basicdefinitions.h [new file with mode: 0644]
api/nblib/box.cpp [new file with mode: 0644]
api/nblib/box.h [new file with mode: 0644]
api/nblib/exception.h [new file with mode: 0644]
api/nblib/forcecalculator.cpp [new file with mode: 0644]
api/nblib/forcecalculator.h [new file with mode: 0644]
api/nblib/gmxcalculator.cpp [new file with mode: 0644]
api/nblib/gmxcalculator.h [new file with mode: 0644]
api/nblib/gmxsetup.cpp [new file with mode: 0644]
api/nblib/gmxsetup.h [new file with mode: 0644]
api/nblib/integrator.cpp [new file with mode: 0644]
api/nblib/integrator.h [new file with mode: 0644]
api/nblib/interactions.cpp [new file with mode: 0644]
api/nblib/interactions.h [new file with mode: 0644]
api/nblib/kerneloptions.h [new file with mode: 0644]
api/nblib/molecules.cpp [new file with mode: 0644]
api/nblib/molecules.h [new file with mode: 0644]
api/nblib/nblib.h [new file with mode: 0644]
api/nblib/particletype.cpp [moved from src/gromacs/nbnxm/opencl/nbnxm_ocl_internal.h with 68% similarity]
api/nblib/particletype.h [new file with mode: 0644]
api/nblib/samples/CMakeLists.txt [new file with mode: 0644]
api/nblib/samples/argon-forces-integration.cpp [new file with mode: 0644]
api/nblib/samples/tests/CMakeLists.txt [new file with mode: 0644]
api/nblib/samples/tests/samples.cpp [moved from src/gromacs/gpu_utils/tests/gputest.h with 72% similarity]
api/nblib/simulationstate.cpp [new file with mode: 0644]
api/nblib/simulationstate.h [new file with mode: 0644]
api/nblib/simulationstateimpl.h [new file with mode: 0644]
api/nblib/tests/CMakeLists.txt [new file with mode: 0644]
api/nblib/tests/box.cpp [new file with mode: 0644]
api/nblib/tests/gmxcalculator.cpp [new file with mode: 0644]
api/nblib/tests/integrator.cpp [new file with mode: 0644]
api/nblib/tests/interactions.cpp [new file with mode: 0644]
api/nblib/tests/molecules.cpp [new file with mode: 0644]
api/nblib/tests/nbkernelsystem.cpp [new file with mode: 0644]
api/nblib/tests/nbnxnsetup.cpp [moved from src/gromacs/gpu_utils/tests/gputest.cpp with 69% similarity]
api/nblib/tests/particletype.cpp [new file with mode: 0644]
api/nblib/tests/refdata/NBlibTest_ArgonForcesAreCorrect.xml [moved from src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_80.xml with 76% similarity]
api/nblib/tests/refdata/NBlibTest_SpcMethanolForcesAreCorrect.xml [new file with mode: 0644]
api/nblib/tests/simstate.cpp [new file with mode: 0644]
api/nblib/tests/testhelpers.cpp [new file with mode: 0644]
api/nblib/tests/testhelpers.h [new file with mode: 0644]
api/nblib/tests/testsystems.cpp [new file with mode: 0644]
api/nblib/tests/testsystems.h [new file with mode: 0644]
api/nblib/tests/topology.cpp [new file with mode: 0644]
api/nblib/topology.cpp [new file with mode: 0644]
api/nblib/topology.h [new file with mode: 0644]
api/nblib/topologyhelpers.cpp [new file with mode: 0644]
api/nblib/topologyhelpers.h [new file with mode: 0644]
api/nblib/util/CMakeLists.txt [new file with mode: 0644]
api/nblib/util/internal.h [new file with mode: 0644]
api/nblib/util/tests/CMakeLists.txt [new file with mode: 0644]
api/nblib/util/tests/refdata/NBlibTest_GeneratedVelocitiesAreCorrect.xml [new file with mode: 0644]
api/nblib/util/tests/user.cpp [new file with mode: 0644]
api/nblib/util/user.cpp [new file with mode: 0644]
api/nblib/util/user.h [new file with mode: 0644]
api/nblib/vector.h [new file with mode: 0644]
cmake/FindCUDA.cmake [deleted file]
cmake/FindCUDA/make2cmake.cmake [deleted file]
cmake/FindCUDA/parse_cubin.cmake [deleted file]
cmake/FindCUDA/run_nvcc.cmake [deleted file]
cmake/FindFFTW.cmake
cmake/FindHWLOC.cmake
cmake/FindLibStdCpp.cmake
cmake/FindMuparser.cmake [new file with mode: 0644]
cmake/FindPythonModule.cmake
cmake/FindSphinx.cmake
cmake/TestARMv7.cpp [deleted file]
cmake/TestARMv7CycleCounters.cpp [deleted file]
cmake/TestAtomics.cpp [deleted file]
cmake/TestPipes.cpp
cmake/ThreadMPI.cmake
cmake/gmxBuildTypeASAN.cmake
cmake/gmxBuildTypeReference.cmake
cmake/gmxBuildTypeUBSAN.cmake [new file with mode: 0644]
cmake/gmxCFlags.cmake
cmake/gmxCPackUtilities.cmake
cmake/gmxDetectCpu.cmake
cmake/gmxDetectGpu.cmake [deleted file]
cmake/gmxDetectSimd.cmake
cmake/gmxDetectTargetArchitecture.cmake
cmake/gmxGenerateVersionInfo.cmake
cmake/gmxManageCuda.cmake [new file with mode: 0644]
cmake/gmxManageCycleCounters.cmake [deleted file]
cmake/gmxManageFFTLibraries.cmake
cmake/gmxManageGPU.cmake [deleted file]
cmake/gmxManageLinearAlgebraLibraries.cmake
cmake/gmxManageMPI.cmake
cmake/gmxManageMuparser.cmake [new file with mode: 0644]
cmake/gmxManageNvccConfig.cmake
cmake/gmxManageOpenCL.cmake
cmake/gmxManageOpenMP.cmake
cmake/gmxManagePluginSupport.cmake
cmake/gmxManageSYCL.cmake [new file with mode: 0644]
cmake/gmxManageSimd.cmake
cmake/gmxOptionUtilities.cmake
cmake/gmxPythonDiscovery.cmake [new file with mode: 0644]
cmake/gmxSetBuildInformation.cmake
cmake/gmxSimdFlags.cmake
cmake/gmxTestCompilerProblems.cmake
cmake/gmxTestImageMagick.cmake
cmake/gmxTestInlineASM.cmake
cmake/gmxTestMMMalloc.cmake
cmake/gmxTestMPI_IN_PLACE.cmake
cmake/gmxTestdlopen.cmake
cmake/gmxVersionInfo.cmake
docs/CMakeLists.txt
docs/OpenCLTODOList.txt
docs/conf-vars.py.cmakein [deleted file]
docs/conf.cmakein.py [moved from docs/conf.py with 85% similarity]
docs/dev-manual/build-system.rst
docs/dev-manual/change-management.rst
docs/dev-manual/code-formatting.rst
docs/dev-manual/commitstyle.rst
docs/dev-manual/contribute.rst
docs/dev-manual/documentation-generation.rst
docs/dev-manual/gitlab.rst [new file with mode: 0644]
docs/dev-manual/infrastructure.rst [new file with mode: 0644]
docs/dev-manual/jenkins.rst
docs/dev-manual/language-features.rst
docs/dev-manual/overview.rst
docs/dev-manual/releng/index.rst
docs/dev-manual/reportstyle.rst
docs/dev-manual/style.rst
docs/dev-manual/tools.rst
docs/doxygen/CMakeLists.txt
docs/doxygen/Doxyfile-common.cmakein
docs/doxygen/cycle-suppressions.txt
docs/doxygen/doxygenxml.py
docs/doxygen/gmxtree.py
docs/doxygen/graphbuilder.py
docs/doxygen/includesorter.py
docs/doxygen/lib/modularsimulator.md
docs/doxygen/suppressions.txt
docs/doxygen/user/usinglibrary.md
docs/gmxapi/reference/datamodel.rst
docs/gmxapi/userguide/install.rst
docs/gmxapi/userguide/usage.rst
docs/gmxapi/userguide/userguide.rst
docs/index.rst
docs/install-guide/index.rst
docs/manual/CMakeLists.txt
docs/manual/UseLATEX.cmake
docs/nblib/guide-to-writing-MD-programs.rst [new file with mode: 0644]
docs/nblib/index.rst [new file with mode: 0644]
docs/reference-manual/algorithms/constraint-algorithms.rst
docs/reference-manual/algorithms/molecular-dynamics.rst
docs/reference-manual/file-formats.rst
docs/reference-manual/functions/interaction-methods.rst
docs/reference-manual/index.rst
docs/reference-manual/preface.rst
docs/reference-manual/references.rst
docs/reference-manual/special/awh.rst
docs/reference-manual/special/density-guided-simulation.rst
docs/reference-manual/special/qmmm.rst
docs/release-notes/2016/major/bugs-fixed.rst
docs/release-notes/2018/major/miscellaneous.rst
docs/release-notes/2019/2019.2.rst
docs/release-notes/2019/2019.3.rst
docs/release-notes/2019/2019.4.rst
docs/release-notes/2019/2019.5.rst
docs/release-notes/2019/2019.6.rst
docs/release-notes/2020/2020.1.rst
docs/release-notes/2020/2020.2.rst
docs/release-notes/2020/major/bugs-fixed.rst
docs/release-notes/2020/major/deprecated-functionality.rst
docs/release-notes/2020/major/features.rst
docs/release-notes/2020/major/highlights.rst
docs/release-notes/2020/major/miscellaneous.rst
docs/release-notes/2020/major/performance.rst
docs/release-notes/2020/major/portability.rst
docs/release-notes/2020/major/removed-functionality.rst
docs/release-notes/2020/major/tools.rst
docs/release-notes/2021/major/bugs-fixed.rst [new file with mode: 0644]
docs/release-notes/2021/major/deprecated-functionality.rst [new file with mode: 0644]
docs/release-notes/2021/major/features.rst [new file with mode: 0644]
docs/release-notes/2021/major/highlights.rst [new file with mode: 0644]
docs/release-notes/2021/major/miscellaneous.rst [new file with mode: 0644]
docs/release-notes/2021/major/performance.rst [new file with mode: 0644]
docs/release-notes/2021/major/portability.rst [new file with mode: 0644]
docs/release-notes/2021/major/removed-functionality.rst [new file with mode: 0644]
docs/release-notes/2021/major/tools.rst [new file with mode: 0644]
docs/release-notes/index.rst
docs/release-notes/older/index.rst
docs/user-guide/deprecation-policy.rst
docs/user-guide/environment-variables.rst
docs/user-guide/faq.rst
docs/user-guide/force-fields.rst
docs/user-guide/index.rst
docs/user-guide/mdp-options.rst
docs/user-guide/mdrun-performance.rst
docs/user-guide/run-time-errors.rst
python_packaging/CMakeLists.txt
python_packaging/README.md
python_packaging/docker/README.md
python_packaging/docker/ci.dockerfile
python_packaging/documentation/conf.py
python_packaging/requirements-test.txt
python_packaging/sample_restraint/CMakeLists.pybind.in
python_packaging/sample_restraint/CMakeLists.txt
python_packaging/sample_restraint/README.md
python_packaging/sample_restraint/src/cpp/ensemblepotential.h
python_packaging/sample_restraint/tests/CMakeLists.txt
python_packaging/sample_restraint/tests/conftest.py
python_packaging/sample_restraint/tests/test_binding.py
python_packaging/scripts/run_flake8 [new file with mode: 0755]
python_packaging/scripts/run_gmxapi_unittest
python_packaging/src/CMakeLists.txt
python_packaging/src/external/README.md
python_packaging/src/external/pybind/include/pybind11/buffer_info.h
python_packaging/src/external/pybind/include/pybind11/cast.h
python_packaging/src/external/pybind/include/pybind11/detail/class.h
python_packaging/src/external/pybind/include/pybind11/detail/common.h
python_packaging/src/external/pybind/include/pybind11/detail/internals.h
python_packaging/src/external/pybind/include/pybind11/embed.h
python_packaging/src/external/pybind/include/pybind11/pybind11.h
python_packaging/src/external/pybind/include/pybind11/pytypes.h
python_packaging/src/external/pybind/include/pybind11/stl_bind.h
python_packaging/src/external/pybind/tools/FindPythonLibsNew.cmake
python_packaging/src/external/pybind/tools/pybind11Tools.cmake
python_packaging/src/gmxapi/.gitignore [new file with mode: 0644]
python_packaging/src/gmxapi/_logging.py
python_packaging/src/gmxapi/commandline.py
python_packaging/src/gmxapi/datamodel.py
python_packaging/src/gmxapi/export_context.cpp
python_packaging/src/gmxapi/export_tprfile.cpp
python_packaging/src/gmxapi/gmxconfig.json.in [new file with mode: 0644]
python_packaging/src/gmxapi/operation.py
python_packaging/src/gmxapi/pycontext.cpp
python_packaging/src/gmxapi/simulation/mdrun.py
python_packaging/src/gmxapi/testsupport.py
python_packaging/src/gmxapi/version.py
python_packaging/src/pyproject.toml [new file with mode: 0644]
python_packaging/src/requirements.txt
python_packaging/src/setup.py
python_packaging/src/test/conftest.py
python_packaging/src/test/pytest.ini [new file with mode: 0644]
python_packaging/src/test/test_commandline.py
python_packaging/src/test/test_mdrun.py
python_packaging/test/README.md
python_packaging/test/conftest.py
python_packaging/test/pytest.ini
python_packaging/test/pytesthelpers.py
python_packaging/test/test_fr01.py
python_packaging/test/test_fr02.py [deleted file]
python_packaging/test/test_fr03.py
python_packaging/test/test_fr07.py
python_packaging/test/test_fr08.py
python_packaging/test/test_fr15.py
scripts/CMakeLists.txt
scripts/GMXRC.bash.cmakein
scripts/GMXRC.csh.cmakein
share/CMakeLists.txt
share/template/CMakeLists.txt
share/template/template.cpp
share/top/amber03.ff/tip5p.itp
share/top/amber03.ff/watermodels.dat
share/top/amber94.ff/tip5p.itp
share/top/amber94.ff/watermodels.dat
share/top/amber96.ff/tip5p.itp
share/top/amber96.ff/watermodels.dat
share/top/amber99.ff/tip5p.itp
share/top/amber99.ff/watermodels.dat
share/top/amber99sb-ildn.ff/tip5p.itp
share/top/amber99sb-ildn.ff/watermodels.dat
share/top/amber99sb.ff/tip5p.itp
share/top/amber99sb.ff/watermodels.dat
share/top/amberGS.ff/tip5p.itp
share/top/amberGS.ff/watermodels.dat
share/top/charmm27.ff/tip5p.itp
share/top/charmm27.ff/watermodels.dat
share/top/oplsaa.ff/tip5p.itp
share/top/oplsaa.ff/watermodels.dat
src/.clang-tidy
src/CMakeLists.txt
src/api/CMakeLists.txt
src/api/cpp/CMakeLists.txt
src/api/cpp/context_impl.h [deleted file]
src/api/cpp/include/version.h.in [deleted file]
src/api/cpp/tests/CMakeLists.txt
src/api/cpp/tests/context.cpp [new file with mode: 0644]
src/api/cpp/tests/restraint.cpp
src/api/cpp/tests/runner.cpp
src/api/cpp/tests/stopsignaler.cpp
src/api/cpp/tests/testingconfiguration.h
src/api/cpp/tests/version.cpp
src/api/cpp/workflow/tests/CMakeLists.txt
src/api/cpp/workflow/tests/workflow.cpp
src/buildinfo.h.cmakein
src/config.h.cmakein
src/external/CMakeLists.txt
src/external/boost/CMakeLists.txt [moved from admin/builds/get-version-info.py with 82% similarity]
src/external/boost/README [new file with mode: 0644]
src/external/boost/stl_interfaces/fwd.hpp [new file with mode: 0644]
src/external/boost/stl_interfaces/iterator_interface.hpp [new file with mode: 0644]
src/external/build-fftw/CMakeLists.txt
src/external/clFFT/README.Gromacs
src/external/clFFT/src/CMakeLists.txt
src/external/clFFT/src/statTimer/statisticalTimer.CPU.cpp
src/external/clFFT/src/statTimer/statisticalTimer.GPU.cpp
src/external/googletest/README.Gromacs
src/external/googletest/README.md
src/external/googletest/googlemock/CMakeLists.txt
src/external/googletest/googlemock/docs/CheatSheet.md
src/external/googletest/googlemock/docs/CookBook.md
src/external/googletest/googlemock/docs/DevGuide.md [deleted file]
src/external/googletest/googlemock/docs/Documentation.md
src/external/googletest/googlemock/docs/ForDummies.md
src/external/googletest/googlemock/docs/FrequentlyAskedQuestions.md
src/external/googletest/googlemock/include/gmock/gmock-spec-builders.h
src/external/googletest/googletest/CMakeLists.txt
src/external/googletest/googletest/README.md
src/external/googletest/googletest/cmake/Config.cmake.in [new file with mode: 0644]
src/external/googletest/googletest/cmake/gtest.pc.in [new file with mode: 0644]
src/external/googletest/googletest/cmake/gtest_main.pc.in [new file with mode: 0644]
src/external/googletest/googletest/cmake/internal_utils.cmake
src/external/googletest/googletest/docs/AdvancedGuide.md [deleted file]
src/external/googletest/googletest/docs/DevGuide.md [deleted file]
src/external/googletest/googletest/docs/Documentation.md [deleted file]
src/external/googletest/googletest/docs/FAQ.md [deleted file]
src/external/googletest/googletest/docs/Pkgconfig.md [new file with mode: 0644]
src/external/googletest/googletest/docs/Primer.md [deleted file]
src/external/googletest/googletest/docs/PumpManual.md
src/external/googletest/googletest/docs/Samples.md [deleted file]
src/external/googletest/googletest/docs/XcodeGuide.md
src/external/googletest/googletest/docs/advanced.md [new file with mode: 0644]
src/external/googletest/googletest/docs/faq.md [new file with mode: 0644]
src/external/googletest/googletest/docs/primer.md [new file with mode: 0644]
src/external/googletest/googletest/docs/samples.md [new file with mode: 0644]
src/external/googletest/googletest/include/gtest/gtest-death-test.h
src/external/googletest/googletest/include/gtest/gtest-message.h
src/external/googletest/googletest/include/gtest/gtest-param-test.h
src/external/googletest/googletest/include/gtest/gtest-printers.h
src/external/googletest/googletest/include/gtest/gtest-spi.h
src/external/googletest/googletest/include/gtest/gtest-test-part.h
src/external/googletest/googletest/include/gtest/gtest-typed-test.h
src/external/googletest/googletest/include/gtest/gtest.h
src/external/googletest/googletest/include/gtest/gtest_pred_impl.h
src/external/googletest/googletest/include/gtest/gtest_prod.h
src/external/googletest/googletest/include/gtest/internal/custom/README.md [new file with mode: 0644]
src/external/googletest/googletest/include/gtest/internal/custom/gtest-port.h
src/external/googletest/googletest/include/gtest/internal/custom/gtest-printers.h
src/external/googletest/googletest/include/gtest/internal/custom/gtest.h
src/external/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h
src/external/googletest/googletest/include/gtest/internal/gtest-filepath.h
src/external/googletest/googletest/include/gtest/internal/gtest-internal.h
src/external/googletest/googletest/include/gtest/internal/gtest-linked_ptr.h
src/external/googletest/googletest/include/gtest/internal/gtest-param-util-generated.h
src/external/googletest/googletest/include/gtest/internal/gtest-param-util.h
src/external/googletest/googletest/include/gtest/internal/gtest-port-arch.h
src/external/googletest/googletest/include/gtest/internal/gtest-port.h
src/external/googletest/googletest/include/gtest/internal/gtest-string.h
src/external/googletest/googletest/include/gtest/internal/gtest-tuple.h
src/external/googletest/googletest/include/gtest/internal/gtest-type-util.h
src/external/googletest/googletest/src/gtest-all.cc
src/external/googletest/googletest/src/gtest-death-test.cc
src/external/googletest/googletest/src/gtest-filepath.cc
src/external/googletest/googletest/src/gtest-internal-inl.h
src/external/googletest/googletest/src/gtest-port.cc
src/external/googletest/googletest/src/gtest-printers.cc
src/external/googletest/googletest/src/gtest-test-part.cc
src/external/googletest/googletest/src/gtest-typed-test.cc
src/external/googletest/googletest/src/gtest.cc
src/external/googletest/googletest/src/gtest_main.cc
src/external/muparser/README [new file with mode: 0644]
src/external/muparser/muParser.cpp [new file with mode: 0644]
src/external/muparser/muParser.h [new file with mode: 0644]
src/external/muparser/muParserBase.cpp [new file with mode: 0644]
src/external/muparser/muParserBase.h [new file with mode: 0644]
src/external/muparser/muParserBytecode.cpp [new file with mode: 0644]
src/external/muparser/muParserBytecode.h [new file with mode: 0644]
src/external/muparser/muParserCallback.cpp [new file with mode: 0644]
src/external/muparser/muParserCallback.h [new file with mode: 0644]
src/external/muparser/muParserDLL.cpp [new file with mode: 0644]
src/external/muparser/muParserDLL.h [new file with mode: 0644]
src/external/muparser/muParserDef.h [new file with mode: 0644]
src/external/muparser/muParserError.cpp [new file with mode: 0644]
src/external/muparser/muParserError.h [new file with mode: 0644]
src/external/muparser/muParserFixes.h [new file with mode: 0644]
src/external/muparser/muParserInt.cpp [new file with mode: 0644]
src/external/muparser/muParserInt.h [new file with mode: 0644]
src/external/muparser/muParserTemplateMagic.h [new file with mode: 0644]
src/external/muparser/muParserTest.cpp [new file with mode: 0644]
src/external/muparser/muParserTest.h [new file with mode: 0644]
src/external/muparser/muParserToken.h [new file with mode: 0644]
src/external/muparser/muParserTokenReader.cpp [new file with mode: 0644]
src/external/muparser/muParserTokenReader.h [new file with mode: 0644]
src/external/nonstd/README [deleted file]
src/external/nonstd/optional.hpp [deleted file]
src/external/thread_mpi/include/thread_mpi/atomic/cycles.h
src/gromacs/CMakeLists.txt
src/gromacs/InstallLibInfo.cmake
src/gromacs/analysisdata/CMakeLists.txt
src/gromacs/analysisdata/abstractdata.cpp
src/gromacs/analysisdata/abstractdata.h
src/gromacs/analysisdata/arraydata.cpp
src/gromacs/analysisdata/arraydata.h
src/gromacs/analysisdata/dataframe.h
src/gromacs/analysisdata/datamodule.h
src/gromacs/analysisdata/datamodulemanager.cpp
src/gromacs/analysisdata/datamodulemanager.h
src/gromacs/analysisdata/dataproxy.cpp
src/gromacs/analysisdata/dataproxy.h
src/gromacs/analysisdata/datastorage.cpp
src/gromacs/analysisdata/framelocaldata.h
src/gromacs/analysisdata/modules/average.cpp
src/gromacs/analysisdata/modules/average.h
src/gromacs/analysisdata/modules/displacement.h
src/gromacs/analysisdata/modules/histogram.cpp
src/gromacs/analysisdata/modules/histogram.h
src/gromacs/analysisdata/modules/lifetime.cpp
src/gromacs/analysisdata/modules/plot.cpp
src/gromacs/analysisdata/modules/plot.h
src/gromacs/analysisdata/tests/CMakeLists.txt
src/gromacs/analysisdata/tests/analysisdata.cpp
src/gromacs/analysisdata/tests/datatest.cpp
src/gromacs/analysisdata/tests/datatest.h
src/gromacs/analysisdata/tests/mock_datamodule.cpp
src/gromacs/analysisdata/tests/mock_datamodule.h
src/gromacs/applied_forces/CMakeLists.txt
src/gromacs/applied_forces/awh/CMakeLists.txt [new file with mode: 0644]
src/gromacs/applied_forces/awh/awh.cpp [moved from src/gromacs/awh/awh.cpp with 64% similarity]
src/gromacs/applied_forces/awh/awh.h [moved from src/gromacs/awh/awh.h with 73% similarity]
src/gromacs/applied_forces/awh/bias.cpp [moved from src/gromacs/awh/bias.cpp with 83% similarity]
src/gromacs/applied_forces/awh/bias.h [moved from src/gromacs/awh/bias.h with 83% similarity]
src/gromacs/applied_forces/awh/biasgrid.cpp [moved from src/gromacs/awh/grid.cpp with 78% similarity]
src/gromacs/applied_forces/awh/biasgrid.h [moved from src/gromacs/awh/grid.h with 77% similarity]
src/gromacs/applied_forces/awh/biasparams.cpp [moved from src/gromacs/awh/biasparams.cpp with 87% similarity]
src/gromacs/applied_forces/awh/biasparams.h [moved from src/gromacs/awh/biasparams.h with 99% similarity]
src/gromacs/applied_forces/awh/biassharing.cpp [moved from src/gromacs/awh/biassharing.cpp with 92% similarity]
src/gromacs/applied_forces/awh/biassharing.h [moved from src/gromacs/awh/biassharing.h with 97% similarity]
src/gromacs/applied_forces/awh/biasstate.cpp [moved from src/gromacs/awh/biasstate.cpp with 84% similarity]
src/gromacs/applied_forces/awh/biasstate.h [moved from src/gromacs/awh/biasstate.h with 79% similarity]
src/gromacs/applied_forces/awh/biaswriter.cpp [moved from src/gromacs/awh/biaswriter.cpp with 98% similarity]
src/gromacs/applied_forces/awh/biaswriter.h [moved from src/gromacs/awh/biaswriter.h with 98% similarity]
src/gromacs/applied_forces/awh/coordstate.cpp [moved from src/gromacs/awh/coordstate.cpp with 94% similarity]
src/gromacs/applied_forces/awh/coordstate.h [moved from src/gromacs/awh/coordstate.h with 89% similarity]
src/gromacs/applied_forces/awh/correlationgrid.cpp [moved from src/gromacs/awh/correlationgrid.cpp with 98% similarity]
src/gromacs/applied_forces/awh/correlationgrid.h [moved from src/gromacs/awh/correlationgrid.h with 97% similarity]
src/gromacs/applied_forces/awh/correlationhistory.cpp [moved from src/gromacs/awh/correlationhistory.cpp with 99% similarity]
src/gromacs/applied_forces/awh/correlationhistory.h [moved from src/gromacs/awh/correlationhistory.h with 97% similarity]
src/gromacs/applied_forces/awh/correlationtensor.cpp [moved from src/gromacs/awh/correlationtensor.cpp with 99% similarity]
src/gromacs/applied_forces/awh/correlationtensor.h [moved from src/gromacs/awh/correlationtensor.h with 99% similarity]
src/gromacs/applied_forces/awh/dimparams.h [moved from src/gromacs/awh/dimparams.h with 54% similarity]
src/gromacs/applied_forces/awh/histogramsize.cpp [moved from src/gromacs/awh/histogramsize.cpp with 98% similarity]
src/gromacs/applied_forces/awh/histogramsize.h [moved from src/gromacs/awh/histogramsize.h with 98% similarity]
src/gromacs/applied_forces/awh/pointstate.cpp [moved from src/gromacs/awh/pointstate.cpp with 91% similarity]
src/gromacs/applied_forces/awh/pointstate.h [moved from src/gromacs/awh/pointstate.h with 98% similarity]
src/gromacs/applied_forces/awh/read_params.cpp [moved from src/gromacs/awh/read_params.cpp with 67% similarity]
src/gromacs/applied_forces/awh/read_params.h [moved from src/gromacs/awh/read_params.h with 76% similarity]
src/gromacs/applied_forces/awh/tests/CMakeLists.txt [moved from src/gromacs/awh/tests/CMakeLists.txt with 89% similarity]
src/gromacs/applied_forces/awh/tests/bias.cpp [moved from src/gromacs/awh/tests/bias.cpp with 93% similarity]
src/gromacs/applied_forces/awh/tests/bias_fep_lambda_state.cpp [new file with mode: 0644]
src/gromacs/applied_forces/awh/tests/biasgrid.cpp [moved from src/gromacs/awh/tests/grid.cpp with 92% similarity]
src/gromacs/applied_forces/awh/tests/biasstate.cpp [moved from src/gromacs/awh/tests/biasstate.cpp with 91% similarity]
src/gromacs/applied_forces/awh/tests/pmf_target_format0.xvg [moved from src/gromacs/awh/tests/pmf_target_format0.xvg with 100% similarity]
src/gromacs/applied_forces/awh/tests/pmf_target_format1.xvg [moved from src/gromacs/awh/tests/pmf_target_format1.xvg with 100% similarity]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_0.xml [new file with mode: 0644]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_1.xml [new file with mode: 0644]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_2.xml [new file with mode: 0644]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_3.xml [new file with mode: 0644]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_4.xml [new file with mode: 0644]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_5.xml [new file with mode: 0644]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_6.xml [new file with mode: 0644]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_7.xml [new file with mode: 0644]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_0.xml [moved from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_0.xml with 81% similarity]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_1.xml [moved from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_1.xml with 81% similarity]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_2.xml [moved from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_2.xml with 66% similarity]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_3.xml [moved from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_3.xml with 66% similarity]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_4.xml [moved from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_4.xml with 81% similarity]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_5.xml [moved from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_5.xml with 81% similarity]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_6.xml [moved from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_6.xml with 66% similarity]
src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_7.xml [moved from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_7.xml with 66% similarity]
src/gromacs/applied_forces/densityfitting/CMakeLists.txt [moved from src/api/cpp/include/CMakeLists.txt with 78% similarity]
src/gromacs/applied_forces/densityfitting/densityfitting.cpp [moved from src/gromacs/applied_forces/densityfitting.cpp with 70% similarity]
src/gromacs/applied_forces/densityfitting/densityfitting.h [moved from src/gromacs/applied_forces/densityfitting.h with 93% similarity]
src/gromacs/applied_forces/densityfitting/densityfittingamplitudelookup.cpp [moved from src/gromacs/applied_forces/densityfittingamplitudelookup.cpp with 98% similarity]
src/gromacs/applied_forces/densityfitting/densityfittingamplitudelookup.h [moved from src/gromacs/applied_forces/densityfittingamplitudelookup.h with 90% similarity]
src/gromacs/applied_forces/densityfitting/densityfittingforceprovider.cpp [moved from src/gromacs/applied_forces/densityfittingforceprovider.cpp with 72% similarity]
src/gromacs/applied_forces/densityfitting/densityfittingforceprovider.h [moved from src/gromacs/applied_forces/densityfittingforceprovider.h with 59% similarity]
src/gromacs/applied_forces/densityfitting/densityfittingoptions.cpp [moved from src/gromacs/applied_forces/densityfittingoptions.cpp with 88% similarity]
src/gromacs/applied_forces/densityfitting/densityfittingoptions.h [moved from src/gromacs/applied_forces/densityfittingoptions.h with 96% similarity]
src/gromacs/applied_forces/densityfitting/densityfittingoutputprovider.cpp [moved from src/gromacs/applied_forces/densityfittingoutputprovider.cpp with 97% similarity]
src/gromacs/applied_forces/densityfitting/densityfittingoutputprovider.h [moved from src/gromacs/applied_forces/densityfittingoutputprovider.h with 97% similarity]
src/gromacs/applied_forces/densityfitting/densityfittingparameters.cpp [moved from src/gromacs/applied_forces/densityfittingparameters.cpp with 97% similarity]
src/gromacs/applied_forces/densityfitting/densityfittingparameters.h [moved from src/gromacs/applied_forces/densityfittingparameters.h with 92% similarity]
src/gromacs/applied_forces/densityfitting/tests/CMakeLists.txt [new file with mode: 0644]
src/gromacs/applied_forces/densityfitting/tests/densityfitting.cpp [moved from src/gromacs/applied_forces/tests/densityfitting.cpp with 96% similarity]
src/gromacs/applied_forces/densityfitting/tests/densityfittingamplitudelookup.cpp [moved from src/gromacs/applied_forces/tests/densityfittingamplitudelookup.cpp with 96% similarity]
src/gromacs/applied_forces/densityfitting/tests/densityfittingforceprovider.cpp [new file with mode: 0644]
src/gromacs/applied_forces/densityfitting/tests/densityfittingoptions.cpp [moved from src/gromacs/applied_forces/tests/densityfittingoptions.cpp with 98% similarity]
src/gromacs/applied_forces/electricfield.cpp
src/gromacs/applied_forces/tests/CMakeLists.txt
src/gromacs/applied_forces/tests/electricfield.cpp
src/gromacs/commandline/cmdlinehelpcontext.cpp
src/gromacs/commandline/cmdlinehelpmodule.cpp
src/gromacs/commandline/cmdlinehelpmodule.h
src/gromacs/commandline/cmdlinehelpwriter.h
src/gromacs/commandline/cmdlineinit.cpp
src/gromacs/commandline/cmdlinemodulemanager.cpp
src/gromacs/commandline/cmdlinemodulemanager.h
src/gromacs/commandline/cmdlineoptionsmodule.cpp
src/gromacs/commandline/cmdlineparser.cpp
src/gromacs/commandline/cmdlineparser.h
src/gromacs/commandline/cmdlineprogramcontext.cpp
src/gromacs/commandline/cmdlineprogramcontext.h
src/gromacs/commandline/filenm.cpp
src/gromacs/commandline/filenm.h
src/gromacs/commandline/pargs.cpp
src/gromacs/commandline/pargs.h
src/gromacs/commandline/shellcompletions.cpp
src/gromacs/commandline/tests/CMakeLists.txt
src/gromacs/commandline/tests/cmdlinehelpwriter.cpp
src/gromacs/commandline/tests/cmdlinemodulemanager.cpp
src/gromacs/commandline/tests/cmdlinemodulemanagertest.h
src/gromacs/commandline/tests/cmdlineprogramcontext.cpp
src/gromacs/commandline/tests/pargs.cpp
src/gromacs/commandline/viewit.cpp
src/gromacs/compat/pointers.h
src/gromacs/compat/string_view.h [deleted file]
src/gromacs/compat/tests/.gitattributes
src/gromacs/compat/tests/CMakeLists.txt
src/gromacs/compat/tests/optional.cpp [deleted file]
src/gromacs/compat/tests/string_view.cpp [deleted file]
src/gromacs/compat/utility.h
src/gromacs/coordinateio/coordinatefile.cpp
src/gromacs/coordinateio/coordinatefile.h
src/gromacs/coordinateio/enums.h
src/gromacs/coordinateio/outputadapters/setatoms.cpp
src/gromacs/coordinateio/outputadapters/setatoms.h
src/gromacs/coordinateio/requirements.cpp
src/gromacs/coordinateio/tests/CMakeLists.txt
src/gromacs/coordinateio/tests/requirements.h
src/gromacs/coordinateio/tests/setatoms.cpp
src/gromacs/coordinateio/tests/setbothtime.cpp
src/gromacs/coordinateio/tests/setstarttime.cpp
src/gromacs/coordinateio/tests/settimestep.cpp
src/gromacs/correlationfunctions/autocorr.cpp
src/gromacs/correlationfunctions/integrate.cpp
src/gromacs/correlationfunctions/tests/CMakeLists.txt
src/gromacs/correlationfunctions/tests/autocorr.cpp
src/gromacs/domdec/CMakeLists.txt
src/gromacs/domdec/box.cpp
src/gromacs/domdec/box.h
src/gromacs/domdec/builder.h
src/gromacs/domdec/cellsizes.cpp
src/gromacs/domdec/cellsizes.h
src/gromacs/domdec/collect.cpp
src/gromacs/domdec/collect.h
src/gromacs/domdec/distribute.cpp
src/gromacs/domdec/distribute.h
src/gromacs/domdec/domdec.cpp
src/gromacs/domdec/domdec.h
src/gromacs/domdec/domdec_constraints.cpp
src/gromacs/domdec/domdec_constraints.h
src/gromacs/domdec/domdec_internal.h
src/gromacs/domdec/domdec_network.h
src/gromacs/domdec/domdec_setup.cpp
src/gromacs/domdec/domdec_setup.h
src/gromacs/domdec/domdec_specatomcomm.cpp
src/gromacs/domdec/domdec_specatomcomm.h
src/gromacs/domdec/domdec_struct.h
src/gromacs/domdec/domdec_topology.cpp
src/gromacs/domdec/domdec_vsite.cpp
src/gromacs/domdec/domdec_vsite.h
src/gromacs/domdec/dump.cpp
src/gromacs/domdec/ga2la.h
src/gromacs/domdec/gpuhaloexchange.h
src/gromacs/domdec/gpuhaloexchange_impl.cpp
src/gromacs/domdec/gpuhaloexchange_impl.cu
src/gromacs/domdec/gpuhaloexchange_impl.cuh
src/gromacs/domdec/hashedmap.h
src/gromacs/domdec/localatomset.h
src/gromacs/domdec/localatomsetdata.h
src/gromacs/domdec/localatomsetmanager.h
src/gromacs/domdec/mdsetup.cpp
src/gromacs/domdec/mdsetup.h
src/gromacs/domdec/partition.cpp
src/gromacs/domdec/partition.h
src/gromacs/domdec/redistribute.cpp
src/gromacs/domdec/redistribute.h
src/gromacs/domdec/tests/CMakeLists.txt
src/gromacs/domdec/tests/localatomsetmanager.cpp
src/gromacs/domdec/utility.cpp
src/gromacs/domdec/utility.h
src/gromacs/energyanalysis/tests/CMakeLists.txt
src/gromacs/essentialdynamics/edsam.cpp
src/gromacs/essentialdynamics/edsam.h
src/gromacs/ewald/CMakeLists.txt
src/gromacs/ewald/calculate_spline_moduli.cpp
src/gromacs/ewald/calculate_spline_moduli.h
src/gromacs/ewald/ewald.cpp
src/gromacs/ewald/ewald.h
src/gromacs/ewald/long_range_correction.cpp
src/gromacs/ewald/long_range_correction.h
src/gromacs/ewald/pme.cpp
src/gromacs/ewald/pme.cuh
src/gromacs/ewald/pme.h
src/gromacs/ewald/pme_coordinate_receiver_gpu.h
src/gromacs/ewald/pme_coordinate_receiver_gpu_impl.cpp
src/gromacs/ewald/pme_coordinate_receiver_gpu_impl.cu
src/gromacs/ewald/pme_coordinate_receiver_gpu_impl.h
src/gromacs/ewald/pme_force_sender_gpu.h
src/gromacs/ewald/pme_force_sender_gpu_impl.cpp
src/gromacs/ewald/pme_force_sender_gpu_impl.cu
src/gromacs/ewald/pme_force_sender_gpu_impl.h
src/gromacs/ewald/pme_gather.clh
src/gromacs/ewald/pme_gather.cpp
src/gromacs/ewald/pme_gather.cu
src/gromacs/ewald/pme_gather.h
src/gromacs/ewald/pme_gpu.cpp
src/gromacs/ewald/pme_gpu_3dfft.cu
src/gromacs/ewald/pme_gpu_3dfft.h
src/gromacs/ewald/pme_gpu_3dfft_ocl.cpp
src/gromacs/ewald/pme_gpu_calculate_splines.clh [moved from src/gromacs/ewald/pme_gpu_utils.clh with 78% similarity]
src/gromacs/ewald/pme_gpu_calculate_splines.cuh [moved from src/gromacs/ewald/pme_calculate_splines.cuh with 74% similarity]
src/gromacs/ewald/pme_gpu_calculate_splines.h [moved from src/gromacs/ewald/pme_gpu_utils.h with 71% similarity]
src/gromacs/ewald/pme_gpu_constants.h
src/gromacs/ewald/pme_gpu_internal.cpp
src/gromacs/ewald/pme_gpu_internal.h
src/gromacs/ewald/pme_gpu_program.cpp
src/gromacs/ewald/pme_gpu_program.h
src/gromacs/ewald/pme_gpu_program_impl.cpp
src/gromacs/ewald/pme_gpu_program_impl.cu
src/gromacs/ewald/pme_gpu_program_impl.h
src/gromacs/ewald/pme_gpu_program_impl_ocl.cpp
src/gromacs/ewald/pme_gpu_settings.h [new file with mode: 0644]
src/gromacs/ewald/pme_gpu_staging.h [new file with mode: 0644]
src/gromacs/ewald/pme_gpu_timings.cpp
src/gromacs/ewald/pme_gpu_timings.h
src/gromacs/ewald/pme_gpu_types.h
src/gromacs/ewald/pme_gpu_types_host.h
src/gromacs/ewald/pme_gpu_types_host_impl.h
src/gromacs/ewald/pme_grid.cpp
src/gromacs/ewald/pme_grid.h
src/gromacs/ewald/pme_internal.h
src/gromacs/ewald/pme_load_balancing.cpp
src/gromacs/ewald/pme_load_balancing.h
src/gromacs/ewald/pme_only.cpp
src/gromacs/ewald/pme_only.h [moved from src/gromacs/utility/mpiinplacebuffers.h with 61% similarity]
src/gromacs/ewald/pme_output.h [new file with mode: 0644]
src/gromacs/ewald/pme_pp.cpp
src/gromacs/ewald/pme_pp.h [new file with mode: 0644]
src/gromacs/ewald/pme_pp_comm_gpu.h
src/gromacs/ewald/pme_pp_comm_gpu_impl.cpp
src/gromacs/ewald/pme_pp_comm_gpu_impl.cu
src/gromacs/ewald/pme_pp_comm_gpu_impl.h
src/gromacs/ewald/pme_pp_communication.h
src/gromacs/ewald/pme_program.cl
src/gromacs/ewald/pme_simd4.h
src/gromacs/ewald/pme_solve.clh
src/gromacs/ewald/pme_solve.cpp
src/gromacs/ewald/pme_solve.cu
src/gromacs/ewald/pme_solve.h
src/gromacs/ewald/pme_spline_work.cpp
src/gromacs/ewald/pme_spread.clh
src/gromacs/ewald/pme_spread.cpp
src/gromacs/ewald/pme_spread.cu
src/gromacs/ewald/spline_vectors.h [new file with mode: 0644]
src/gromacs/ewald/tests/CMakeLists.txt
src/gromacs/ewald/tests/pmebsplinetest.cpp
src/gromacs/ewald/tests/pmegathertest.cpp
src/gromacs/ewald/tests/pmesolvetest.cpp
src/gromacs/ewald/tests/pmesplinespreadtest.cpp
src/gromacs/ewald/tests/pmetestcommon.cpp
src/gromacs/ewald/tests/pmetestcommon.h
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_10.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_100.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_101.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_102.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_103.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_104.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_105.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_106.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_107.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_108.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_109.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_11.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_110.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_111.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_112.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_113.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_114.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_115.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_116.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_117.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_118.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_119.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_12.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_120.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_121.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_122.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_123.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_124.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_125.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_126.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_127.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_128.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_129.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_13.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_130.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_131.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_132.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_133.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_134.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_135.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_136.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_137.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_138.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_139.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_14.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_140.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_141.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_142.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_143.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_15.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_16.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_17.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_18.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_19.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_20.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_21.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_22.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_23.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_24.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_25.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_26.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_27.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_28.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_29.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_3.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_30.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_31.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_32.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_33.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_34.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_35.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_36.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_37.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_38.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_39.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_4.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_40.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_41.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_42.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_43.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_44.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_45.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_46.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_47.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_48.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_49.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_5.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_50.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_51.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_52.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_53.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_54.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_55.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_56.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_57.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_58.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_59.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_6.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_60.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_61.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_62.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_63.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_64.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_65.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_66.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_67.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_68.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_69.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_7.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_70.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_71.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_72.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_73.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_74.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_75.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_76.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_77.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_78.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_79.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_8.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_81.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_82.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_83.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_84.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_85.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_86.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_87.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_88.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_89.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_9.xml
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_90.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_91.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_92.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_93.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_94.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_95.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_96.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_97.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_98.xml [deleted file]
src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_99.xml [deleted file]
src/gromacs/fft/calcgrid.cpp
src/gromacs/fft/calcgrid.h
src/gromacs/fft/fft.cpp
src/gromacs/fft/fft5d.cpp
src/gromacs/fft/fft_fftpack.cpp
src/gromacs/fft/fft_fftw3.cpp
src/gromacs/fft/fft_mkl.cpp
src/gromacs/fft/tests/CMakeLists.txt
src/gromacs/fft/tests/fft.cpp
src/gromacs/fileio/CMakeLists.txt
src/gromacs/fileio/checkpoint.cpp
src/gromacs/fileio/checkpoint.h
src/gromacs/fileio/confio.cpp
src/gromacs/fileio/confio.h
src/gromacs/fileio/enxio.cpp
src/gromacs/fileio/enxio.h
src/gromacs/fileio/espio.cpp
src/gromacs/fileio/espio.h
src/gromacs/fileio/filetypes.cpp
src/gromacs/fileio/g96io.cpp
src/gromacs/fileio/g96io.h
src/gromacs/fileio/gmxfio.cpp
src/gromacs/fileio/gmxfio_xdr.cpp
src/gromacs/fileio/gmxfio_xdr.h
src/gromacs/fileio/groio.cpp
src/gromacs/fileio/groio.h
src/gromacs/fileio/libxdrf.cpp
src/gromacs/fileio/matio.cpp
src/gromacs/fileio/md5.cpp
src/gromacs/fileio/md5.h
src/gromacs/fileio/mrcdensitymap.cpp
src/gromacs/fileio/mrcdensitymap.h
src/gromacs/fileio/mrcserializer.cpp
src/gromacs/fileio/mtxio.cpp
src/gromacs/fileio/oenv.cpp
src/gromacs/fileio/oenv.h
src/gromacs/fileio/pdbio.cpp
src/gromacs/fileio/pdbio.h
src/gromacs/fileio/readinp.cpp
src/gromacs/fileio/readinp.h
src/gromacs/fileio/tests/CMakeLists.txt
src/gromacs/fileio/tests/checkpoint.cpp [new file with mode: 0644]
src/gromacs/fileio/tests/confio.cpp
src/gromacs/fileio/tests/fileioxdrserializer.cpp
src/gromacs/fileio/tests/mrcdensitymapheader.cpp
src/gromacs/fileio/tests/readinp.cpp
src/gromacs/fileio/tests/tngio.cpp
src/gromacs/fileio/tests/xvgio.cpp [new file with mode: 0644]
src/gromacs/fileio/tngio.cpp
src/gromacs/fileio/tngio.h
src/gromacs/fileio/tpxio.cpp
src/gromacs/fileio/tpxio.h
src/gromacs/fileio/trrio.cpp
src/gromacs/fileio/trrio.h
src/gromacs/fileio/trxio.cpp
src/gromacs/fileio/trxio.h
src/gromacs/fileio/vmdio.h
src/gromacs/fileio/warninp.cpp
src/gromacs/fileio/warninp.h
src/gromacs/fileio/writeps.cpp
src/gromacs/fileio/xdrd.cpp
src/gromacs/fileio/xtcio.cpp
src/gromacs/fileio/xtcio.h
src/gromacs/fileio/xvgr.cpp
src/gromacs/fileio/xvgr.h
src/gromacs/gmxana/anadih.cpp
src/gromacs/gmxana/angle_correction.cpp
src/gromacs/gmxana/angle_correction.h
src/gromacs/gmxana/binsearch.h
src/gromacs/gmxana/cmat.cpp
src/gromacs/gmxana/dens_filter.cpp
src/gromacs/gmxana/dlist.cpp
src/gromacs/gmxana/eigio.cpp
src/gromacs/gmxana/eigio.h
src/gromacs/gmxana/fitahx.cpp
src/gromacs/gmxana/gmx_ana.h
src/gromacs/gmxana/gmx_anaeig.cpp
src/gromacs/gmxana/gmx_analyze.cpp
src/gromacs/gmxana/gmx_angle.cpp
src/gromacs/gmxana/gmx_awh.cpp
src/gromacs/gmxana/gmx_bar.cpp
src/gromacs/gmxana/gmx_bundle.cpp
src/gromacs/gmxana/gmx_chi.cpp
src/gromacs/gmxana/gmx_cluster.cpp
src/gromacs/gmxana/gmx_clustsize.cpp
src/gromacs/gmxana/gmx_confrms.cpp
src/gromacs/gmxana/gmx_covar.cpp
src/gromacs/gmxana/gmx_current.cpp
src/gromacs/gmxana/gmx_density.cpp
src/gromacs/gmxana/gmx_densmap.cpp
src/gromacs/gmxana/gmx_densorder.cpp
src/gromacs/gmxana/gmx_dielectric.cpp
src/gromacs/gmxana/gmx_dipoles.cpp
src/gromacs/gmxana/gmx_disre.cpp
src/gromacs/gmxana/gmx_do_dssp.cpp
src/gromacs/gmxana/gmx_dos.cpp
src/gromacs/gmxana/gmx_dyecoupl.cpp
src/gromacs/gmxana/gmx_enemat.cpp
src/gromacs/gmxana/gmx_energy.cpp
src/gromacs/gmxana/gmx_filter.cpp
src/gromacs/gmxana/gmx_gyrate.cpp
src/gromacs/gmxana/gmx_h2order.cpp
src/gromacs/gmxana/gmx_hbond.cpp
src/gromacs/gmxana/gmx_helix.cpp
src/gromacs/gmxana/gmx_helixorient.cpp
src/gromacs/gmxana/gmx_hydorder.cpp
src/gromacs/gmxana/gmx_lie.cpp
src/gromacs/gmxana/gmx_make_edi.cpp
src/gromacs/gmxana/gmx_mdmat.cpp
src/gromacs/gmxana/gmx_mindist.cpp
src/gromacs/gmxana/gmx_msd.cpp
src/gromacs/gmxana/gmx_nmeig.cpp
src/gromacs/gmxana/gmx_nmens.cpp
src/gromacs/gmxana/gmx_nmr.cpp
src/gromacs/gmxana/gmx_nmtraj.cpp
src/gromacs/gmxana/gmx_order.cpp
src/gromacs/gmxana/gmx_polystat.cpp
src/gromacs/gmxana/gmx_potential.cpp
src/gromacs/gmxana/gmx_principal.cpp
src/gromacs/gmxana/gmx_rms.cpp
src/gromacs/gmxana/gmx_rmsdist.cpp
src/gromacs/gmxana/gmx_rmsf.cpp
src/gromacs/gmxana/gmx_rotacf.cpp
src/gromacs/gmxana/gmx_rotmat.cpp
src/gromacs/gmxana/gmx_saltbr.cpp
src/gromacs/gmxana/gmx_sans.cpp
src/gromacs/gmxana/gmx_saxs.cpp
src/gromacs/gmxana/gmx_sham.cpp
src/gromacs/gmxana/gmx_sigeps.cpp
src/gromacs/gmxana/gmx_sorient.cpp
src/gromacs/gmxana/gmx_spatial.cpp
src/gromacs/gmxana/gmx_spol.cpp
src/gromacs/gmxana/gmx_tcaf.cpp
src/gromacs/gmxana/gmx_traj.cpp
src/gromacs/gmxana/gmx_trjorder.cpp
src/gromacs/gmxana/gmx_vanhove.cpp
src/gromacs/gmxana/gmx_velacc.cpp
src/gromacs/gmxana/gmx_wham.cpp
src/gromacs/gmxana/gmx_wheel.cpp
src/gromacs/gmxana/gmx_xpm2ps.cpp
src/gromacs/gmxana/gstat.h
src/gromacs/gmxana/hxprops.cpp
src/gromacs/gmxana/hxprops.h
src/gromacs/gmxana/interf.h
src/gromacs/gmxana/nrama.cpp
src/gromacs/gmxana/nrama.h
src/gromacs/gmxana/nsfactor.cpp
src/gromacs/gmxana/nsfactor.h
src/gromacs/gmxana/powerspect.cpp
src/gromacs/gmxana/powerspect.h
src/gromacs/gmxana/pp2shift.cpp
src/gromacs/gmxana/princ.cpp
src/gromacs/gmxana/princ.h
src/gromacs/gmxana/sfactor.cpp
src/gromacs/gmxana/sfactor.h
src/gromacs/gmxana/tests/CMakeLists.txt
src/gromacs/gmxana/tests/entropy.cpp
src/gromacs/gmxana/tests/gmx_traj.cpp
src/gromacs/gmxana/thermochemistry.h
src/gromacs/gmxlib/CMakeLists.txt
src/gromacs/gmxlib/network.cpp
src/gromacs/gmxlib/network.h
src/gromacs/gmxlib/nonbonded/CMakeLists.txt
src/gromacs/gmxlib/nonbonded/nb_free_energy.cpp
src/gromacs/gmxlib/nonbonded/nb_free_energy.h
src/gromacs/gmxlib/nonbonded/nb_kernel.h
src/gromacs/gmxlib/nrnb.cpp
src/gromacs/gmxlib/nrnb.h
src/gromacs/gmxlib/tests/CMakeLists.txt
src/gromacs/gmxpreprocess/add_par.cpp
src/gromacs/gmxpreprocess/add_par.h
src/gromacs/gmxpreprocess/convparm.cpp
src/gromacs/gmxpreprocess/convparm.h
src/gromacs/gmxpreprocess/editconf.cpp
src/gromacs/gmxpreprocess/fflibutil.cpp
src/gromacs/gmxpreprocess/gen_ad.cpp
src/gromacs/gmxpreprocess/gen_ad.h
src/gromacs/gmxpreprocess/gen_maxwell_velocities.cpp
src/gromacs/gmxpreprocess/gen_maxwell_velocities.h
src/gromacs/gmxpreprocess/gen_vsite.cpp
src/gromacs/gmxpreprocess/gen_vsite.h
src/gromacs/gmxpreprocess/genconf.cpp
src/gromacs/gmxpreprocess/genhydro.h
src/gromacs/gmxpreprocess/genion.cpp
src/gromacs/gmxpreprocess/genrestr.cpp
src/gromacs/gmxpreprocess/gmxcpp.cpp
src/gromacs/gmxpreprocess/gpp_atomtype.cpp
src/gromacs/gmxpreprocess/gpp_atomtype.h
src/gromacs/gmxpreprocess/gpp_bond_atomtype.cpp
src/gromacs/gmxpreprocess/gpp_nextnb.cpp
src/gromacs/gmxpreprocess/gpp_nextnb.h
src/gromacs/gmxpreprocess/grompp.cpp
src/gromacs/gmxpreprocess/grompp_impl.h
src/gromacs/gmxpreprocess/h_db.cpp
src/gromacs/gmxpreprocess/hackblock.cpp
src/gromacs/gmxpreprocess/hackblock.h
src/gromacs/gmxpreprocess/hizzie.cpp
src/gromacs/gmxpreprocess/insert_molecules.cpp
src/gromacs/gmxpreprocess/makeexclusiondistances.cpp
src/gromacs/gmxpreprocess/nm2type.cpp
src/gromacs/gmxpreprocess/pdb2gmx.cpp
src/gromacs/gmxpreprocess/pdb2top.cpp
src/gromacs/gmxpreprocess/pdb2top.h
src/gromacs/gmxpreprocess/pgutil.cpp
src/gromacs/gmxpreprocess/readir.cpp
src/gromacs/gmxpreprocess/readir.h
src/gromacs/gmxpreprocess/readpull.cpp
src/gromacs/gmxpreprocess/readrot.cpp
src/gromacs/gmxpreprocess/resall.cpp
src/gromacs/gmxpreprocess/resall.h
src/gromacs/gmxpreprocess/solvate.cpp
src/gromacs/gmxpreprocess/specbond.cpp
src/gromacs/gmxpreprocess/ter_db.cpp
src/gromacs/gmxpreprocess/ter_db.h
src/gromacs/gmxpreprocess/tests/CMakeLists.txt
src/gromacs/gmxpreprocess/tests/readir.cpp
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_AcceptsDefineParametersWithValuesIncludingAssignment.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_AcceptsElectricField.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_AcceptsElectricFieldOscillating.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_AcceptsElectricFieldPulsed.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_AcceptsEmptyLines.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_AcceptsImplicitSolventNo.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_AcceptsKeyWithoutValue.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_AcceptsMimic.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_HandlesDifferentKindsOfMdpLines.xml
src/gromacs/gmxpreprocess/tests/solvate.cpp
src/gromacs/gmxpreprocess/tomorse.cpp
src/gromacs/gmxpreprocess/tomorse.h
src/gromacs/gmxpreprocess/topdirs.cpp
src/gromacs/gmxpreprocess/topio.cpp
src/gromacs/gmxpreprocess/topio.h
src/gromacs/gmxpreprocess/toppush.cpp
src/gromacs/gmxpreprocess/toppush.h
src/gromacs/gmxpreprocess/topshake.cpp
src/gromacs/gmxpreprocess/topshake.h
src/gromacs/gmxpreprocess/toputil.cpp
src/gromacs/gmxpreprocess/toputil.h
src/gromacs/gmxpreprocess/vsite_parm.cpp
src/gromacs/gmxpreprocess/vsite_parm.h
src/gromacs/gmxpreprocess/x2top.cpp
src/gromacs/gmxpreprocess/xlate.cpp
src/gromacs/gmxpreprocess/xlate.h
src/gromacs/gpu_utils/CMakeLists.txt
src/gromacs/gpu_utils/clfftinitializer.cpp
src/gromacs/gpu_utils/clfftinitializer.h
src/gromacs/gpu_utils/cuda_arch_utils.cuh
src/gromacs/gpu_utils/cudautils.cu
src/gromacs/gpu_utils/cudautils.cuh
src/gromacs/gpu_utils/device_context.cpp [moved from src/gromacs/utility/mpiinplacebuffers.cpp with 77% similarity]
src/gromacs/gpu_utils/device_context.h [new file with mode: 0644]
src/gromacs/gpu_utils/device_context_ocl.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/device_context_sycl.cpp [moved from src/gromacs/gpu_utils/gpu_testutils.cpp with 73% similarity]
src/gromacs/gpu_utils/device_stream.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/device_stream.cu [new file with mode: 0644]
src/gromacs/gpu_utils/device_stream.h [new file with mode: 0644]
src/gromacs/gpu_utils/device_stream_manager.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/device_stream_manager.h [new file with mode: 0644]
src/gromacs/gpu_utils/device_stream_ocl.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/device_stream_sycl.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/devicebuffer.cuh
src/gromacs/gpu_utils/devicebuffer.h
src/gromacs/gpu_utils/devicebuffer_datatype.h
src/gromacs/gpu_utils/devicebuffer_ocl.h
src/gromacs/gpu_utils/devicebuffer_sycl.cpp [moved from src/gromacs/mdlib/qm_orca.h with 79% similarity]
src/gromacs/gpu_utils/devicebuffer_sycl.h [new file with mode: 0644]
src/gromacs/gpu_utils/gmxsycl.h [moved from src/gromacs/nbnxm/gpu_types.h with 73% similarity]
src/gromacs/gpu_utils/gpu_macros.h
src/gromacs/gpu_utils/gpu_utils.cpp
src/gromacs/gpu_utils/gpu_utils.cu
src/gromacs/gpu_utils/gpu_utils.h
src/gromacs/gpu_utils/gpu_vec.cuh [deleted file]
src/gromacs/gpu_utils/gpueventsynchronizer.cuh
src/gromacs/gpu_utils/gpueventsynchronizer_ocl.h
src/gromacs/gpu_utils/gpueventsynchronizer_sycl.h [new file with mode: 0644]
src/gromacs/gpu_utils/gpuregiontimer.cuh
src/gromacs/gpu_utils/gpuregiontimer.h
src/gromacs/gpu_utils/gpuregiontimer_ocl.h
src/gromacs/gpu_utils/gputraits.cuh
src/gromacs/gpu_utils/gputraits.h
src/gromacs/gpu_utils/gputraits_ocl.h
src/gromacs/gpu_utils/gputraits_sycl.h [moved from src/gromacs/gpu_utils/gpu_testutils.h with 60% similarity]
src/gromacs/gpu_utils/hostallocator.h
src/gromacs/gpu_utils/ocl_caching.cpp
src/gromacs/gpu_utils/ocl_caching.h
src/gromacs/gpu_utils/ocl_compiler.cpp
src/gromacs/gpu_utils/ocl_compiler.h
src/gromacs/gpu_utils/oclutils.cpp
src/gromacs/gpu_utils/oclutils.h
src/gromacs/gpu_utils/pmalloc_cuda.h
src/gromacs/gpu_utils/tests/CMakeLists.txt
src/gromacs/gpu_utils/tests/device_availability.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/tests/device_buffer.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/tests/device_stream_manager.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/tests/devicetransfers.cpp
src/gromacs/gpu_utils/tests/devicetransfers.cu
src/gromacs/gpu_utils/tests/devicetransfers.h
src/gromacs/gpu_utils/tests/devicetransfers_ocl.cpp
src/gromacs/gpu_utils/tests/devicetransfers_sycl.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/tests/hostallocator.cpp
src/gromacs/gpu_utils/tests/pinnedmemorychecker.cpp
src/gromacs/gpu_utils/tests/typecasts.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/tests/typecasts_runner.cpp [new file with mode: 0644]
src/gromacs/gpu_utils/tests/typecasts_runner.cu [new file with mode: 0644]
src/gromacs/gpu_utils/tests/typecasts_runner.h [new file with mode: 0644]
src/gromacs/gpu_utils/typecasts.cuh [new file with mode: 0644]
src/gromacs/gpu_utils/vectype_ops.cuh
src/gromacs/gromacs-config.cmake.cmakein
src/gromacs/hardware/CMakeLists.txt
src/gromacs/hardware/cpuinfo.cpp
src/gromacs/hardware/cpuinfo.h
src/gromacs/hardware/detecthardware.cpp
src/gromacs/hardware/detecthardware.h
src/gromacs/hardware/device_information.h [new file with mode: 0644]
src/gromacs/hardware/device_management.cpp [new file with mode: 0644]
src/gromacs/hardware/device_management.cu [new file with mode: 0644]
src/gromacs/hardware/device_management.h [new file with mode: 0644]
src/gromacs/hardware/device_management_common.cpp [new file with mode: 0644]
src/gromacs/hardware/device_management_ocl.cpp [moved from src/gromacs/gpu_utils/gpu_utils_ocl.cpp with 60% similarity]
src/gromacs/hardware/device_management_sycl.cpp [new file with mode: 0644]
src/gromacs/hardware/gpu_hw_info.h [deleted file]
src/gromacs/hardware/hardwaretopology.cpp
src/gromacs/hardware/hw_info.h
src/gromacs/hardware/printhardware.cpp
src/gromacs/hardware/printhardware.h
src/gromacs/hardware/tests/CMakeLists.txt
src/gromacs/hardware/tests/device_management.cpp [new file with mode: 0644]
src/gromacs/imd/imd.cpp
src/gromacs/imd/imd.h
src/gromacs/imd/imdsocket.cpp
src/gromacs/linearalgebra/CMakeLists.txt
src/gromacs/linearalgebra/eigensolver.cpp
src/gromacs/linearalgebra/gmx_arpack.cpp
src/gromacs/linearalgebra/matrix.cpp
src/gromacs/linearalgebra/nrjac.cpp
src/gromacs/listed_forces/CMakeLists.txt
src/gromacs/listed_forces/bonded.cpp
src/gromacs/listed_forces/bonded.h
src/gromacs/listed_forces/disre.cpp
src/gromacs/listed_forces/disre.h
src/gromacs/listed_forces/gpubonded.h
src/gromacs/listed_forces/gpubonded_impl.cpp
src/gromacs/listed_forces/gpubonded_impl.cu
src/gromacs/listed_forces/gpubonded_impl.h
src/gromacs/listed_forces/gpubondedkernels.cu
src/gromacs/listed_forces/listed_forces.cpp
src/gromacs/listed_forces/listed_forces.h
src/gromacs/listed_forces/listed_internal.h
src/gromacs/listed_forces/manage_threading.cpp
src/gromacs/listed_forces/manage_threading.h
src/gromacs/listed_forces/orires.cpp
src/gromacs/listed_forces/orires.h
src/gromacs/listed_forces/pairs.cpp
src/gromacs/listed_forces/pairs.h
src/gromacs/listed_forces/position_restraints.cpp
src/gromacs/listed_forces/position_restraints.h
src/gromacs/listed_forces/tests/CMakeLists.txt
src/gromacs/listed_forces/tests/bonded.cpp
src/gromacs/listed_forces/tests/refdata/AngleZero_ListedForcesTest_Ifunc_0.xml [new file with mode: 0644]
src/gromacs/listed_forces/tests/refdata/AngleZero_ListedForcesTest_Ifunc_1.xml [new file with mode: 0644]
src/gromacs/listed_forces/tests/refdata/AngleZero_ListedForcesTest_Ifunc_2.xml [new file with mode: 0644]
src/gromacs/listed_forces/tests/refdata/BondZeroLength_ListedForcesTest_Ifunc_0.xml [new file with mode: 0644]
src/gromacs/listed_forces/tests/refdata/BondZeroLength_ListedForcesTest_Ifunc_1.xml [new file with mode: 0644]
src/gromacs/listed_forces/tests/refdata/BondZeroLength_ListedForcesTest_Ifunc_2.xml [new file with mode: 0644]
src/gromacs/listed_forces/utilities.h
src/gromacs/math/3dtransforms.cpp
src/gromacs/math/CMakeLists.txt
src/gromacs/math/arrayrefwithpadding.h
src/gromacs/math/coordinatetransformation.cpp
src/gromacs/math/coordinatetransformation.h
src/gromacs/math/densityfit.cpp
src/gromacs/math/densityfit.h
src/gromacs/math/densityfittingforce.cpp
src/gromacs/math/do_fit.cpp
src/gromacs/math/do_fit.h
src/gromacs/math/gausstransform.h
src/gromacs/math/matrix.cpp [new file with mode: 0644]
src/gromacs/math/matrix.h
src/gromacs/math/multidimarray.h
src/gromacs/math/neldermead.cpp [new file with mode: 0644]
src/gromacs/math/neldermead.h [new file with mode: 0644]
src/gromacs/math/optimization.cpp [new file with mode: 0644]
src/gromacs/math/optimization.h [new file with mode: 0644]
src/gromacs/math/paddedvector.h
src/gromacs/math/tests/CMakeLists.txt
src/gromacs/math/tests/coordinatetransformation.cpp
src/gromacs/math/tests/matrix.cpp
src/gromacs/math/tests/neldermead.cpp [new file with mode: 0644]
src/gromacs/math/tests/optimization.cpp [new file with mode: 0644]
src/gromacs/math/tests/paddedvector.cpp
src/gromacs/math/tests/testarrayrefs.h
src/gromacs/math/units.cpp
src/gromacs/math/utilities.cpp
src/gromacs/math/utilities.h
src/gromacs/math/vec.h
src/gromacs/math/vecdump.cpp
src/gromacs/math/vectypes.h
src/gromacs/mdlib/CMakeLists.txt
src/gromacs/mdlib/boxdeformation.cpp
src/gromacs/mdlib/boxdeformation.h
src/gromacs/mdlib/broadcaststructs.cpp
src/gromacs/mdlib/broadcaststructs.h
src/gromacs/mdlib/calc_verletbuf.cpp
src/gromacs/mdlib/calc_verletbuf.h
src/gromacs/mdlib/calcmu.cpp
src/gromacs/mdlib/calcmu.h
src/gromacs/mdlib/calcvir.cpp
src/gromacs/mdlib/calcvir.h
src/gromacs/mdlib/compute_io.cpp
src/gromacs/mdlib/constr.cpp
src/gromacs/mdlib/constr.h
src/gromacs/mdlib/constraintrange.cpp
src/gromacs/mdlib/coupling.cpp
src/gromacs/mdlib/coupling.h [new file with mode: 0644]
src/gromacs/mdlib/dispersioncorrection.cpp
src/gromacs/mdlib/dispersioncorrection.h
src/gromacs/mdlib/ebin.cpp
src/gromacs/mdlib/ebin.h
src/gromacs/mdlib/enerdata_utils.cpp
src/gromacs/mdlib/enerdata_utils.h
src/gromacs/mdlib/energyoutput.cpp
src/gromacs/mdlib/energyoutput.h
src/gromacs/mdlib/expanded.cpp
src/gromacs/mdlib/force.cpp
src/gromacs/mdlib/force.h
src/gromacs/mdlib/force_flags.h
src/gromacs/mdlib/forcerec.cpp
src/gromacs/mdlib/forcerec.h
src/gromacs/mdlib/freeenergyparameters.cpp [new file with mode: 0644]
src/gromacs/mdlib/freeenergyparameters.h [new file with mode: 0644]
src/gromacs/mdlib/gmx_omp_nthreads.cpp
src/gromacs/mdlib/gmx_omp_nthreads.h
src/gromacs/mdlib/gpuforcereduction.h [new file with mode: 0644]
src/gromacs/mdlib/gpuforcereduction_impl.cpp [new file with mode: 0644]
src/gromacs/mdlib/gpuforcereduction_impl.cu [new file with mode: 0644]
src/gromacs/mdlib/gpuforcereduction_impl.cuh [new file with mode: 0644]
src/gromacs/mdlib/groupcoord.cpp
src/gromacs/mdlib/leapfrog_gpu.cu [moved from src/gromacs/mdlib/leapfrog_cuda.cu with 88% similarity]
src/gromacs/mdlib/leapfrog_gpu.cuh [moved from src/gromacs/mdlib/leapfrog_cuda.cuh with 84% similarity]
src/gromacs/mdlib/lincs.cpp
src/gromacs/mdlib/lincs.h
src/gromacs/mdlib/lincs_gpu.cu [moved from src/gromacs/mdlib/lincs_cuda.cu with 92% similarity]
src/gromacs/mdlib/lincs_gpu.cuh [moved from src/gromacs/mdlib/lincs_cuda.cuh with 80% similarity]
src/gromacs/mdlib/md_support.cpp
src/gromacs/mdlib/md_support.h
src/gromacs/mdlib/mdatoms.cpp
src/gromacs/mdlib/mdatoms.h
src/gromacs/mdlib/mdebin_bar.cpp
src/gromacs/mdlib/mdebin_bar.h
src/gromacs/mdlib/mdoutf.cpp
src/gromacs/mdlib/mdoutf.h
src/gromacs/mdlib/membed.cpp
src/gromacs/mdlib/membed.h
src/gromacs/mdlib/nsgrid.cpp
src/gromacs/mdlib/perf_est.cpp
src/gromacs/mdlib/qm_gamess.cpp [deleted file]
src/gromacs/mdlib/qm_gamess.h [deleted file]
src/gromacs/mdlib/qm_gaussian.cpp [deleted file]
src/gromacs/mdlib/qm_gaussian.h [deleted file]
src/gromacs/mdlib/qm_mopac.cpp [deleted file]
src/gromacs/mdlib/qm_mopac.h [deleted file]
src/gromacs/mdlib/qm_orca.cpp [deleted file]
src/gromacs/mdlib/qmmm.cpp [deleted file]
src/gromacs/mdlib/qmmm.h [deleted file]
src/gromacs/mdlib/resethandler.cpp
src/gromacs/mdlib/rf_util.cpp
src/gromacs/mdlib/settle.cpp
src/gromacs/mdlib/settle.h
src/gromacs/mdlib/settle_cuda.cuh [deleted file]
src/gromacs/mdlib/settle_gpu.cu [moved from src/gromacs/mdlib/settle_cuda.cu with 82% similarity]
src/gromacs/mdlib/settle_gpu.cuh [new file with mode: 0644]
src/gromacs/mdlib/shake.cpp
src/gromacs/mdlib/shake.h
src/gromacs/mdlib/sighandler.cpp
src/gromacs/mdlib/sim_util.cpp
src/gromacs/mdlib/simulationsignal.cpp
src/gromacs/mdlib/simulationsignal.h
src/gromacs/mdlib/splitter.cpp
src/gromacs/mdlib/splitter.h
src/gromacs/mdlib/stat.cpp
src/gromacs/mdlib/stat.h
src/gromacs/mdlib/tests/CMakeLists.txt
src/gromacs/mdlib/tests/constr.cpp
src/gromacs/mdlib/tests/constrtestdata.cpp
src/gromacs/mdlib/tests/constrtestdata.h
src/gromacs/mdlib/tests/constrtestrunners.cpp
src/gromacs/mdlib/tests/constrtestrunners.cu
src/gromacs/mdlib/tests/constrtestrunners.h
src/gromacs/mdlib/tests/energyoutput.cpp
src/gromacs/mdlib/tests/freeenergyparameters.cpp [new file with mode: 0644]
src/gromacs/mdlib/tests/leapfrog.cpp
src/gromacs/mdlib/tests/leapfrogtestdata.cpp
src/gromacs/mdlib/tests/leapfrogtestrunners.cpp
src/gromacs/mdlib/tests/leapfrogtestrunners.cu
src/gromacs/mdlib/tests/leapfrogtestrunners.h
src/gromacs/mdlib/tests/settle.cpp
src/gromacs/mdlib/tests/settletestdata.cpp
src/gromacs/mdlib/tests/settletestdata.h
src/gromacs/mdlib/tests/settletestrunners.cpp
src/gromacs/mdlib/tests/settletestrunners.cu
src/gromacs/mdlib/tests/settletestrunners.h
src/gromacs/mdlib/tests/shake.cpp
src/gromacs/mdlib/tests/updategroupscog.cpp
src/gromacs/mdlib/tgroup.cpp
src/gromacs/mdlib/trajectory_writing.cpp
src/gromacs/mdlib/trajectory_writing.h
src/gromacs/mdlib/update.cpp
src/gromacs/mdlib/update.h
src/gromacs/mdlib/update_constrain_gpu.h [moved from src/gromacs/mdlib/update_constrain_cuda.h with 77% similarity]
src/gromacs/mdlib/update_constrain_gpu_impl.cpp [moved from src/gromacs/mdlib/update_constrain_cuda_impl.cpp with 51% similarity]
src/gromacs/mdlib/update_constrain_gpu_impl.cu [moved from src/gromacs/mdlib/update_constrain_cuda_impl.cu with 50% similarity]
src/gromacs/mdlib/update_constrain_gpu_impl.h [moved from src/gromacs/mdlib/update_constrain_cuda_impl.h with 77% similarity]
src/gromacs/mdlib/updategroups.cpp
src/gromacs/mdlib/vcm.cpp
src/gromacs/mdlib/vcm.h
src/gromacs/mdlib/vsite.cpp
src/gromacs/mdlib/vsite.h
src/gromacs/mdlib/wall.cpp
src/gromacs/mdlib/wall.h
src/gromacs/mdlib/wholemoleculetransform.cpp [new file with mode: 0644]
src/gromacs/mdlib/wholemoleculetransform.h [new file with mode: 0644]
src/gromacs/mdrun/CMakeLists.txt
src/gromacs/mdrun/isimulator.h
src/gromacs/mdrun/legacymdrunoptions.cpp
src/gromacs/mdrun/legacysimulator.h
src/gromacs/mdrun/md.cpp
src/gromacs/mdrun/mdmodules.cpp
src/gromacs/mdrun/mdmodules.h
src/gromacs/mdrun/membedholder.cpp [new file with mode: 0644]
src/gromacs/mdrun/membedholder.h [new file with mode: 0644]
src/gromacs/mdrun/mimic.cpp
src/gromacs/mdrun/minimize.cpp
src/gromacs/mdrun/replicaexchange.cpp
src/gromacs/mdrun/replicaexchange.h
src/gromacs/mdrun/rerun.cpp
src/gromacs/mdrun/runner.cpp
src/gromacs/mdrun/runner.h
src/gromacs/mdrun/shellfc.cpp
src/gromacs/mdrun/shellfc.h
src/gromacs/mdrun/simulationcontext.cpp
src/gromacs/mdrun/simulationcontext.h
src/gromacs/mdrun/simulationinput.cpp [new file with mode: 0644]
src/gromacs/mdrun/simulationinput.h [new file with mode: 0644]
src/gromacs/mdrun/simulationinputhandle.cpp [new file with mode: 0644]
src/gromacs/mdrun/simulationinputhandle.h [new file with mode: 0644]
src/gromacs/mdrun/simulatorbuilder.cpp [new file with mode: 0644]
src/gromacs/mdrun/simulatorbuilder.h
src/gromacs/mdrun/tpi.cpp
src/gromacs/mdrunutility/handlerestart.cpp
src/gromacs/mdrunutility/multisim.cpp
src/gromacs/mdrunutility/multisim.h
src/gromacs/mdrunutility/printtime.cpp
src/gromacs/mdrunutility/tests/CMakeLists.txt
src/gromacs/mdrunutility/tests/threadaffinity_mpi.cpp
src/gromacs/mdrunutility/tests/threadaffinitytest.cpp
src/gromacs/mdrunutility/tests/threadaffinitytest.h
src/gromacs/mdrunutility/threadaffinity.cpp
src/gromacs/mdrunutility/threadaffinity.h
src/gromacs/mdspan/extensions.h
src/gromacs/mdspan/mdspan.h
src/gromacs/mdspan/tests/CMakeLists.txt
src/gromacs/mdtypes/CMakeLists.txt
src/gromacs/mdtypes/awh_params.h
src/gromacs/mdtypes/checkpointdata.cpp [new file with mode: 0644]
src/gromacs/mdtypes/checkpointdata.h [new file with mode: 0644]
src/gromacs/mdtypes/commrec.h
src/gromacs/mdtypes/enerdata.h
src/gromacs/mdtypes/energyhistory.cpp [new file with mode: 0644]
src/gromacs/mdtypes/energyhistory.h
src/gromacs/mdtypes/fcdata.h
src/gromacs/mdtypes/forcebuffers.cpp [new file with mode: 0644]
src/gromacs/mdtypes/forcebuffers.h [new file with mode: 0644]
src/gromacs/mdtypes/forceoutput.h
src/gromacs/mdtypes/forcerec.h
src/gromacs/mdtypes/imdmodule.h
src/gromacs/mdtypes/inputrec.cpp
src/gromacs/mdtypes/inputrec.h
src/gromacs/mdtypes/interaction_const.cpp [moved from src/gromacs/hardware/gpu_hw_info.cpp with 70% similarity]
src/gromacs/mdtypes/interaction_const.h
src/gromacs/mdtypes/md_enums.cpp
src/gromacs/mdtypes/md_enums.h
src/gromacs/mdtypes/mdatom.h
src/gromacs/mdtypes/multipletimestepping.cpp [new file with mode: 0644]
src/gromacs/mdtypes/multipletimestepping.h [new file with mode: 0644]
src/gromacs/mdtypes/nblist.h
src/gromacs/mdtypes/simulation_workload.h
src/gromacs/mdtypes/state.cpp
src/gromacs/mdtypes/state.h
src/gromacs/mdtypes/state_propagator_data_gpu.h
src/gromacs/mdtypes/state_propagator_data_gpu_impl.cpp
src/gromacs/mdtypes/state_propagator_data_gpu_impl.h
src/gromacs/mdtypes/state_propagator_data_gpu_impl_gpu.cpp
src/gromacs/mdtypes/tests/CMakeLists.txt [moved from src/gromacs/awh/CMakeLists.txt with 87% similarity]
src/gromacs/mdtypes/tests/checkpointdata.cpp [new file with mode: 0644]
src/gromacs/mdtypes/tests/forcebuffers.cpp [new file with mode: 0644]
src/gromacs/mimic/communicator.cpp
src/gromacs/mimic/communicator.h
src/gromacs/modularsimulator/CMakeLists.txt
src/gromacs/modularsimulator/checkpointhelper.cpp
src/gromacs/modularsimulator/checkpointhelper.h
src/gromacs/modularsimulator/compositesimulatorelement.cpp
src/gromacs/modularsimulator/compositesimulatorelement.h
src/gromacs/modularsimulator/computeglobalselement.cpp
src/gromacs/modularsimulator/computeglobalselement.h
src/gromacs/modularsimulator/constraintelement.cpp
src/gromacs/modularsimulator/constraintelement.h
src/gromacs/modularsimulator/domdechelper.cpp
src/gromacs/modularsimulator/domdechelper.h
src/gromacs/modularsimulator/energydata.cpp [moved from src/gromacs/modularsimulator/energyelement.cpp with 51% similarity]
src/gromacs/modularsimulator/energydata.h [moved from src/gromacs/modularsimulator/energyelement.h with 59% similarity]
src/gromacs/modularsimulator/forceelement.cpp
src/gromacs/modularsimulator/forceelement.h
src/gromacs/modularsimulator/freeenergyperturbationdata.cpp [new file with mode: 0644]
src/gromacs/modularsimulator/freeenergyperturbationdata.h [moved from src/gromacs/modularsimulator/freeenergyperturbationelement.h with 50% similarity]
src/gromacs/modularsimulator/freeenergyperturbationelement.cpp [deleted file]
src/gromacs/modularsimulator/modularsimulator.cpp
src/gromacs/modularsimulator/modularsimulator.h
src/gromacs/modularsimulator/modularsimulatorinterfaces.h
src/gromacs/modularsimulator/parrinellorahmanbarostat.cpp
src/gromacs/modularsimulator/parrinellorahmanbarostat.h
src/gromacs/modularsimulator/pmeloadbalancehelper.cpp
src/gromacs/modularsimulator/pmeloadbalancehelper.h
src/gromacs/modularsimulator/propagator.cpp
src/gromacs/modularsimulator/propagator.h
src/gromacs/modularsimulator/shellfcelement.cpp [deleted file]
src/gromacs/modularsimulator/shellfcelement.h [deleted file]
src/gromacs/modularsimulator/signallers.cpp
src/gromacs/modularsimulator/signallers.h
src/gromacs/modularsimulator/simulatoralgorithm.cpp [new file with mode: 0644]
src/gromacs/modularsimulator/simulatoralgorithm.h [new file with mode: 0644]
src/gromacs/modularsimulator/statepropagatordata.cpp
src/gromacs/modularsimulator/statepropagatordata.h
src/gromacs/modularsimulator/topologyholder.cpp
src/gromacs/modularsimulator/topologyholder.h
src/gromacs/modularsimulator/trajectoryelement.cpp
src/gromacs/modularsimulator/trajectoryelement.h
src/gromacs/modularsimulator/velocityscalingtemperaturecoupling.cpp [new file with mode: 0644]
src/gromacs/modularsimulator/velocityscalingtemperaturecoupling.h [new file with mode: 0644]
src/gromacs/modularsimulator/vrescalethermostat.cpp [deleted file]
src/gromacs/modularsimulator/vrescalethermostat.h [deleted file]
src/gromacs/nbnxm/CMakeLists.txt
src/gromacs/nbnxm/atomdata.cpp
src/gromacs/nbnxm/atomdata.h
src/gromacs/nbnxm/benchmark/bench_setup.cpp
src/gromacs/nbnxm/benchmark/bench_system.cpp
src/gromacs/nbnxm/benchmark/bench_system.h
src/gromacs/nbnxm/boundingboxes.h
src/gromacs/nbnxm/clusterdistancekerneltype.h
src/gromacs/nbnxm/constants.h [deleted file]
src/gromacs/nbnxm/cuda/CMakeLists.txt
src/gromacs/nbnxm/cuda/nbnxm_buffer_ops_kernels.cuh
src/gromacs/nbnxm/cuda/nbnxm_cuda.cu
src/gromacs/nbnxm/cuda/nbnxm_cuda_data_mgmt.cu
src/gromacs/nbnxm/cuda/nbnxm_cuda_kernel.cuh
src/gromacs/nbnxm/cuda/nbnxm_cuda_kernel_pruneonly.cu
src/gromacs/nbnxm/cuda/nbnxm_cuda_kernel_pruneonly.cuh
src/gromacs/nbnxm/cuda/nbnxm_cuda_kernel_utils.cuh
src/gromacs/nbnxm/cuda/nbnxm_cuda_kernels.cuh
src/gromacs/nbnxm/cuda/nbnxm_cuda_types.h
src/gromacs/nbnxm/gpu_common.h
src/gromacs/nbnxm/gpu_common_utils.h
src/gromacs/nbnxm/gpu_data_mgmt.h
src/gromacs/nbnxm/gpu_jit_support.h
src/gromacs/nbnxm/gpu_types_common.h
src/gromacs/nbnxm/grid.cpp
src/gromacs/nbnxm/grid.h
src/gromacs/nbnxm/gridset.cpp
src/gromacs/nbnxm/gridset.h
src/gromacs/nbnxm/gridsetdata.h
src/gromacs/nbnxm/kernel_common.cpp
src/gromacs/nbnxm/kernel_common.h
src/gromacs/nbnxm/kernel_file_generator/kernel_simd_template.h.pre
src/gromacs/nbnxm/kernel_file_generator/make_verlet_simd_kernel_files.py
src/gromacs/nbnxm/kerneldispatch.cpp
src/gromacs/nbnxm/kernels_reference/kernel_gpu_ref.cpp
src/gromacs/nbnxm/kernels_reference/kernel_gpu_ref.h
src/gromacs/nbnxm/kernels_reference/kernel_ref.cpp
src/gromacs/nbnxm/kernels_reference/kernel_ref.h
src/gromacs/nbnxm/kernels_reference/kernel_ref_inner.h
src/gromacs/nbnxm/kernels_reference/kernel_ref_outer.h
src/gromacs/nbnxm/kernels_simd_2xmm/CMakeLists.txt [new file with mode: 0644]
src/gromacs/nbnxm/kernels_simd_2xmm/kernel_common.h
src/gromacs/nbnxm/kernels_simd_2xmm/kernel_inner.h
src/gromacs/nbnxm/kernels_simd_2xmm/kernel_outer.h
src/gromacs/nbnxm/kernels_simd_4xm/CMakeLists.txt [new file with mode: 0644]
src/gromacs/nbnxm/kernels_simd_4xm/kernel_common.h
src/gromacs/nbnxm/kernels_simd_4xm/kernel_inner.h
src/gromacs/nbnxm/kernels_simd_4xm/kernel_outer.h
src/gromacs/nbnxm/nbnxm.cpp
src/gromacs/nbnxm/nbnxm.h
src/gromacs/nbnxm/nbnxm_geometry.cpp
src/gromacs/nbnxm/nbnxm_geometry.h
src/gromacs/nbnxm/nbnxm_gpu.h
src/gromacs/nbnxm/nbnxm_gpu_data_mgmt.cpp [new file with mode: 0644]
src/gromacs/nbnxm/nbnxm_gpu_data_mgmt.h [new file with mode: 0644]
src/gromacs/nbnxm/nbnxm_setup.cpp
src/gromacs/nbnxm/nbnxm_simd.h
src/gromacs/nbnxm/opencl/CMakeLists.txt
src/gromacs/nbnxm/opencl/nbnxm_ocl.cpp
src/gromacs/nbnxm/opencl/nbnxm_ocl_consts.h
src/gromacs/nbnxm/opencl/nbnxm_ocl_data_mgmt.cpp
src/gromacs/nbnxm/opencl/nbnxm_ocl_jit_support.cpp
src/gromacs/nbnxm/opencl/nbnxm_ocl_kernel.clh
src/gromacs/nbnxm/opencl/nbnxm_ocl_kernel_pruneonly.clh
src/gromacs/nbnxm/opencl/nbnxm_ocl_kernel_utils.clh
src/gromacs/nbnxm/opencl/nbnxm_ocl_kernels.clh
src/gromacs/nbnxm/opencl/nbnxm_ocl_types.h
src/gromacs/nbnxm/pairlist.cpp
src/gromacs/nbnxm/pairlist.h
src/gromacs/nbnxm/pairlist_simd_2xmm.h
src/gromacs/nbnxm/pairlist_simd_4xm.h
src/gromacs/nbnxm/pairlist_tuning.cpp
src/gromacs/nbnxm/pairlistparams.cpp
src/gromacs/nbnxm/pairlistparams.h
src/gromacs/nbnxm/pairlistset.cpp
src/gromacs/nbnxm/pairlistset.h
src/gromacs/nbnxm/pairlistsets.h
src/gromacs/nbnxm/pairlistwork.h
src/gromacs/nbnxm/pairsearch.cpp
src/gromacs/nbnxm/pairsearch.h
src/gromacs/nbnxm/prunekerneldispatch.cpp
src/gromacs/onlinehelp/helpformat.cpp
src/gromacs/onlinehelp/helptopic.cpp
src/gromacs/onlinehelp/helpwritercontext.cpp
src/gromacs/onlinehelp/tests/CMakeLists.txt
src/gromacs/onlinehelp/tests/helpmanager.cpp
src/gromacs/onlinehelp/tests/helpwritercontext.cpp
src/gromacs/options.h
src/gromacs/options/CMakeLists.txt
src/gromacs/options/abstractoptionstorage.h
src/gromacs/options/basicoptions.h
src/gromacs/options/filenameoption.cpp
src/gromacs/options/filenameoption.h
src/gromacs/options/filenameoptionmanager.cpp
src/gromacs/options/filenameoptionstorage.h
src/gromacs/options/optionflags.h
src/gromacs/options/options.h
src/gromacs/options/optionsassigner.h
src/gromacs/options/optionstoragetemplate.h
src/gromacs/options/optionsvisitor.cpp
src/gromacs/options/optionsvisitor.h
src/gromacs/options/tests/CMakeLists.txt
src/gromacs/options/tests/abstractoptionstorage.cpp
src/gromacs/options/tests/filenameoption.cpp
src/gromacs/options/tests/option.cpp
src/gromacs/options/tests/optionsassigner.cpp
src/gromacs/options/tests/refdata/TreeValueSupportTest_SupportsEnumerationArrayOption.xml [moved from src/gromacs/options/tests/refdata/TreeValueSupportTest_SupportsEnumIntOption.xml with 100% similarity]
src/gromacs/options/tests/timeunitmanager.cpp
src/gromacs/options/tests/treesupport.cpp
src/gromacs/options/timeunitmanager.cpp
src/gromacs/options/timeunitmanager.h
src/gromacs/pbcutil/CMakeLists.txt
src/gromacs/pbcutil/com.cpp [new file with mode: 0644]
src/gromacs/pbcutil/com.h [new file with mode: 0644]
src/gromacs/pbcutil/mshift.cpp
src/gromacs/pbcutil/mshift.h
src/gromacs/pbcutil/pbc.cpp
src/gromacs/pbcutil/pbc.h
src/gromacs/pbcutil/pbc_aiuc.h
src/gromacs/pbcutil/pbc_aiuc_cuda.cuh
src/gromacs/pbcutil/pbc_simd.cpp
src/gromacs/pbcutil/pbcenums.h
src/gromacs/pbcutil/pbcmethods.cpp
src/gromacs/pbcutil/pbcmethods.h
src/gromacs/pbcutil/rmpbc.cpp
src/gromacs/pbcutil/rmpbc.h
src/gromacs/pbcutil/tests/CMakeLists.txt
src/gromacs/pbcutil/tests/com.cpp [new file with mode: 0644]
src/gromacs/pbcutil/tests/mshift.cpp [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_0.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_1.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_10.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_11.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_12.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_13.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_14.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_15.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_16.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_17.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_18.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_19.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_2.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_20.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_21.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_22.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_23.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_24.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_25.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_26.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_3.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_4.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_5.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_6.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_7.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_8.xml [new file with mode: 0644]
src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_9.xml [new file with mode: 0644]
src/gromacs/pulling/output.cpp
src/gromacs/pulling/output.h
src/gromacs/pulling/pull.cpp
src/gromacs/pulling/pull.h
src/gromacs/pulling/pull_internal.h
src/gromacs/pulling/pull_rotation.cpp
src/gromacs/pulling/pull_rotation.h
src/gromacs/pulling/pullutil.cpp
src/gromacs/pulling/tests/CMakeLists.txt
src/gromacs/pulling/tests/pull.cpp
src/gromacs/random/tests/CMakeLists.txt
src/gromacs/random/uniformrealdistribution.h
src/gromacs/restraint/restraintmdmodule.cpp
src/gromacs/restraint/restraintmdmodule.h
src/gromacs/restraint/restraintmdmodule_impl.h
src/gromacs/restraint/tests/CMakeLists.txt
src/gromacs/selection.h
src/gromacs/selection/CMakeLists.txt
src/gromacs/selection/centerofmass.h
src/gromacs/selection/compiler.cpp
src/gromacs/selection/compiler.h
src/gromacs/selection/evaluate.cpp
src/gromacs/selection/evaluate.h
src/gromacs/selection/indexutil.cpp
src/gromacs/selection/keywords.h
src/gromacs/selection/mempool.cpp
src/gromacs/selection/nbsearch.cpp
src/gromacs/selection/nbsearch.h
src/gromacs/selection/params.cpp
src/gromacs/selection/parser.cpp
src/gromacs/selection/parser.h
src/gromacs/selection/parser.y
src/gromacs/selection/parser_internal.h
src/gromacs/selection/parsetree.cpp
src/gromacs/selection/poscalc.cpp
src/gromacs/selection/position.h
src/gromacs/selection/scanner.cpp
src/gromacs/selection/scanner.h
src/gromacs/selection/scanner.l
src/gromacs/selection/scanner_internal.cpp
src/gromacs/selection/selection.cpp
src/gromacs/selection/selection.h
src/gromacs/selection/selectioncollection.cpp
src/gromacs/selection/selectioncollection.h
src/gromacs/selection/selectioncollection_impl.h
src/gromacs/selection/selectionenums.h
src/gromacs/selection/selectionfileoptionstorage.h
src/gromacs/selection/selectionoption.h
src/gromacs/selection/selectionoptionbehavior.cpp
src/gromacs/selection/selectionoptionmanager.cpp
src/gromacs/selection/selectionoptionmanager.h
src/gromacs/selection/selelem.cpp
src/gromacs/selection/selhelp.cpp
src/gromacs/selection/selhelp.h
src/gromacs/selection/selparam.h
src/gromacs/selection/selvalue.cpp
src/gromacs/selection/sm_compare.cpp
src/gromacs/selection/sm_distance.cpp
src/gromacs/selection/sm_insolidangle.cpp
src/gromacs/selection/sm_keywords.cpp
src/gromacs/selection/sm_merge.cpp
src/gromacs/selection/sm_permute.cpp
src/gromacs/selection/sm_position.cpp
src/gromacs/selection/sm_same.cpp
src/gromacs/selection/sm_simple.cpp
src/gromacs/selection/symrec.h
src/gromacs/selection/tests/CMakeLists.txt
src/gromacs/selection/tests/indexutil.cpp
src/gromacs/selection/tests/nbsearch.cpp
src/gromacs/selection/tests/poscalc.cpp
src/gromacs/selection/tests/selectioncollection.cpp
src/gromacs/selection/tests/toputils.cpp
src/gromacs/selection/tests/toputils.h
src/gromacs/simd/impl_arm_neon/impl_arm_neon_definitions.h
src/gromacs/simd/impl_arm_neon/impl_arm_neon_util_float.h
src/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_definitions.h
src/gromacs/simd/impl_arm_sve/impl_arm_sve.h [new file with mode: 0644]
src/gromacs/simd/impl_arm_sve/impl_arm_sve_definitions.h [new file with mode: 0644]
src/gromacs/simd/impl_arm_sve/impl_arm_sve_general.h [new file with mode: 0644]
src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_double.h [new file with mode: 0644]
src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_float.h [new file with mode: 0644]
src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_double.h [new file with mode: 0644]
src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_float.h [new file with mode: 0644]
src/gromacs/simd/impl_arm_sve/impl_arm_sve_util_double.h [new file with mode: 0644]
src/gromacs/simd/impl_arm_sve/impl_arm_sve_util_float.h [new file with mode: 0644]
src/gromacs/simd/impl_ibm_vmx/impl_ibm_vmx_definitions.h
src/gromacs/simd/impl_ibm_vmx/impl_ibm_vmx_util_float.h
src/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_definitions.h
src/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd_double.h
src/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd_float.h
src/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_util_double.h
src/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_util_float.h
src/gromacs/simd/impl_none/impl_none.h
src/gromacs/simd/impl_reference/impl_reference_util_double.h
src/gromacs/simd/impl_reference/impl_reference_util_float.h
src/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_definitions.h
src/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_definitions.h
src/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_definitions.h
src/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_definitions.h
src/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_double.h
src/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_float.h
src/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_util_float.h
src/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_definitions.h
src/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_double.h
src/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_float.h
src/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_util_double.h
src/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_util_float.h
src/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_definitions.h
src/gromacs/simd/impl_x86_mic/impl_x86_mic_definitions.h
src/gromacs/simd/impl_x86_mic/impl_x86_mic_util_double.h
src/gromacs/simd/impl_x86_mic/impl_x86_mic_util_float.h
src/gromacs/simd/impl_x86_sse2/impl_x86_sse2_definitions.h
src/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_definitions.h
src/gromacs/simd/simd.h
src/gromacs/simd/simd_math.h
src/gromacs/simd/simd_memory.h
src/gromacs/simd/support.cpp
src/gromacs/simd/support.h
src/gromacs/simd/tests/CMakeLists.txt
src/gromacs/simd/tests/data.h
src/gromacs/simd/tests/scalar.cpp
src/gromacs/simd/tests/scalar_math.cpp
src/gromacs/simd/tests/scalar_util.cpp
src/gromacs/simd/tests/simd.cpp
src/gromacs/simd/tests/simd.h
src/gromacs/simd/tests/simd4_floatingpoint.cpp
src/gromacs/simd/tests/simd_floatingpoint.cpp
src/gromacs/simd/tests/simd_floatingpoint_util.cpp
src/gromacs/simd/tests/simd_integer.cpp
src/gromacs/simd/tests/simd_math.cpp
src/gromacs/simd/tests/simd_memory.cpp
src/gromacs/statistics/statistics.cpp
src/gromacs/swap/swapcoords.cpp
src/gromacs/swap/swapcoords.h
src/gromacs/tables/forcetable.cpp
src/gromacs/tables/forcetable.h
src/gromacs/tables/quadraticsplinetable.cpp
src/gromacs/tables/tests/CMakeLists.txt
src/gromacs/taskassignment/CMakeLists.txt
src/gromacs/taskassignment/decidegpuusage.cpp
src/gromacs/taskassignment/decidegpuusage.h
src/gromacs/taskassignment/decidesimulationworkload.cpp
src/gromacs/taskassignment/decidesimulationworkload.h
src/gromacs/taskassignment/resourcedivision.cpp
src/gromacs/taskassignment/resourcedivision.h
src/gromacs/taskassignment/taskassignment.cpp
src/gromacs/taskassignment/taskassignment.h
src/gromacs/taskassignment/tests/CMakeLists.txt
src/gromacs/taskassignment/usergpuids.cpp
src/gromacs/taskassignment/usergpuids.h
src/gromacs/timing/cyclecounter.cpp
src/gromacs/timing/cyclecounter.h
src/gromacs/timing/gpu_timing.h
src/gromacs/timing/wallcycle.cpp
src/gromacs/timing/wallcycle.h
src/gromacs/timing/wallcyclereporting.h
src/gromacs/timing/walltime_accounting.cpp
src/gromacs/tools/check.cpp
src/gromacs/tools/convert_tpr.cpp
src/gromacs/tools/convert_tpr.h
src/gromacs/tools/dump.cpp
src/gromacs/tools/eneconv.cpp
src/gromacs/tools/make_ndx.cpp
src/gromacs/tools/mk_angndx.cpp
src/gromacs/tools/pme_error.cpp
src/gromacs/tools/report_methods.h
src/gromacs/tools/tests/CMakeLists.txt
src/gromacs/tools/tests/dump.cpp
src/gromacs/tools/tests/helpwriting.cpp [new file with mode: 0644]
src/gromacs/tools/tests/refdata/HelpwritingTest_ConvertTprWritesHelp.xml [new file with mode: 0644]
src/gromacs/tools/tests/refdata/HelpwritingTest_DumpWritesHelp.xml [new file with mode: 0644]
src/gromacs/tools/tests/refdata/HelpwritingTest_ReportMethodsWritesHelp.xml [new file with mode: 0644]
src/gromacs/tools/tests/report_methods.cpp
src/gromacs/tools/tests/trjconv.cpp
src/gromacs/tools/trjcat.cpp
src/gromacs/tools/trjconv.cpp
src/gromacs/tools/tune_pme.cpp
src/gromacs/topology/atomprop.cpp
src/gromacs/topology/atoms.cpp
src/gromacs/topology/atoms.h
src/gromacs/topology/block.cpp
src/gromacs/topology/block.h
src/gromacs/topology/exclusionblocks.cpp
src/gromacs/topology/exclusionblocks.h
src/gromacs/topology/forcefieldparameters.cpp
src/gromacs/topology/forcefieldparameters.h
src/gromacs/topology/idef.cpp
src/gromacs/topology/idef.h
src/gromacs/topology/ifunc.cpp
src/gromacs/topology/ifunc.h
src/gromacs/topology/index.cpp
src/gromacs/topology/index.h
src/gromacs/topology/mtop_lookup.h
src/gromacs/topology/mtop_util.cpp
src/gromacs/topology/mtop_util.h
src/gromacs/topology/residuetypes.cpp
src/gromacs/topology/residuetypes.h
src/gromacs/topology/symtab.cpp
src/gromacs/topology/symtab.h
src/gromacs/topology/tests/CMakeLists.txt
src/gromacs/topology/tests/exclusionblocks.cpp
src/gromacs/topology/tests/idef.cpp [new file with mode: 0644]
src/gromacs/topology/tests/mtop.cpp
src/gromacs/topology/tests/refdata/LegacySymtabTest_AddLargeNumberOfEntries.xml [moved from src/gromacs/topology/tests/refdata/SymtabTest_AddLargeNumberOfEntries.xml with 100% similarity]
src/gromacs/topology/tests/refdata/LegacySymtabTest_NoDuplicatesInLargeTable.xml [moved from src/gromacs/topology/tests/refdata/SymtabTest_NoDuplicatesInLargeTable.xml with 100% similarity]
src/gromacs/topology/tests/refdata/StringTableTest_AddLargeNumberOfEntries.xml [new file with mode: 0644]
src/gromacs/topology/tests/refdata/StringTableTest_AddSingleEntry.xml [new file with mode: 0644]
src/gromacs/topology/tests/refdata/StringTableTest_AddTwoDistinctEntries.xml [new file with mode: 0644]
src/gromacs/topology/tests/refdata/StringTableTest_CanAccessWithAt.xml [new file with mode: 0644]
src/gromacs/topology/tests/refdata/StringTableTest_CanAccessWithBracket.xml [new file with mode: 0644]
src/gromacs/topology/tests/refdata/StringTableTest_NoDuplicatesInLargeTable.xml [new file with mode: 0644]
src/gromacs/topology/tests/refdata/StringTableTest_StringCompareIsCorrect.xml [new file with mode: 0644]
src/gromacs/topology/tests/refdata/StringTableTest_ThrowsOutOfRange.xml [new file with mode: 0644]
src/gromacs/topology/tests/refdata/StringTableTest_TryToAddDuplicates.xml [new file with mode: 0644]
src/gromacs/topology/tests/symtab.cpp
src/gromacs/topology/topology.cpp
src/gromacs/topology/topology.h
src/gromacs/topology/topsort.cpp
src/gromacs/topology/topsort.h
src/gromacs/trajectory/energyframe.h
src/gromacs/trajectory/trajectoryframe.cpp
src/gromacs/trajectory/trajectoryframe.h
src/gromacs/trajectoryanalysis/CMakeLists.txt
src/gromacs/trajectoryanalysis/analysismodule.cpp
src/gromacs/trajectoryanalysis/analysismodule.h
src/gromacs/trajectoryanalysis/analysissettings_impl.h
src/gromacs/trajectoryanalysis/cmdlinerunner.cpp
src/gromacs/trajectoryanalysis/cmdlinerunner.h
src/gromacs/trajectoryanalysis/modules.cpp
src/gromacs/trajectoryanalysis/modules/angle.cpp
src/gromacs/trajectoryanalysis/modules/angle.h
src/gromacs/trajectoryanalysis/modules/convert_trj.cpp
src/gromacs/trajectoryanalysis/modules/distance.cpp
src/gromacs/trajectoryanalysis/modules/distance.h
src/gromacs/trajectoryanalysis/modules/extract_cluster.cpp
src/gromacs/trajectoryanalysis/modules/freevolume.cpp
src/gromacs/trajectoryanalysis/modules/pairdist.cpp
src/gromacs/trajectoryanalysis/modules/rdf.cpp
src/gromacs/trajectoryanalysis/modules/sasa.cpp
src/gromacs/trajectoryanalysis/modules/select.cpp
src/gromacs/trajectoryanalysis/modules/surfacearea.cpp
src/gromacs/trajectoryanalysis/modules/trajectory.cpp
src/gromacs/trajectoryanalysis/runnercommon.cpp
src/gromacs/trajectoryanalysis/runnercommon.h
src/gromacs/trajectoryanalysis/tests/CMakeLists.txt
src/gromacs/trajectoryanalysis/tests/moduletest.cpp
src/gromacs/trajectoryanalysis/tests/refdata/TrajectoryAnalysisCommandLineRunnerTest_WritesHelp.xml
src/gromacs/trajectoryanalysis/tests/surfacearea.cpp
src/gromacs/trajectoryanalysis/tests/topologyinformation.cpp
src/gromacs/trajectoryanalysis/topologyinformation.cpp
src/gromacs/trajectoryanalysis/topologyinformation.h
src/gromacs/utility.h
src/gromacs/utility/CMakeLists.txt
src/gromacs/utility/allocator.h
src/gromacs/utility/any.h
src/gromacs/utility/arrayref.h
src/gromacs/utility/basedefinitions.h
src/gromacs/utility/basenetwork.cpp
src/gromacs/utility/baseversion-gen.cpp.cmakein
src/gromacs/utility/baseversion.cpp
src/gromacs/utility/baseversion.h
src/gromacs/utility/baseversion_gen.h
src/gromacs/utility/binaryinformation.cpp
src/gromacs/utility/binaryinformation.h
src/gromacs/utility/classhelpers.h
src/gromacs/utility/compare.cpp
src/gromacs/utility/compare.h
src/gromacs/utility/coolstuff.cpp
src/gromacs/utility/cstringutil.cpp
src/gromacs/utility/cstringutil.h
src/gromacs/utility/cuda_version_information.cu
src/gromacs/utility/datafilefinder.cpp
src/gromacs/utility/defaultinitializationallocator.h
src/gromacs/utility/dir_separator.h
src/gromacs/utility/directoryenumerator.cpp
src/gromacs/utility/enumerationhelpers.h
src/gromacs/utility/errorcodes.cpp
src/gromacs/utility/errorcodes.h
src/gromacs/utility/exceptions.cpp
src/gromacs/utility/exceptions.h
src/gromacs/utility/fatalerror.cpp
src/gromacs/utility/filestream.cpp
src/gromacs/utility/filestream.h
src/gromacs/utility/fixedcapacityvector.h
src/gromacs/utility/flags.h
src/gromacs/utility/futil.cpp
src/gromacs/utility/gmxmpi.h
src/gromacs/utility/gmxomp.cpp
src/gromacs/utility/gmxomp.h
src/gromacs/utility/init.cpp
src/gromacs/utility/inmemoryserializer.cpp
src/gromacs/utility/inmemoryserializer.h
src/gromacs/utility/iserializer.h
src/gromacs/utility/keyvaluetree.cpp
src/gromacs/utility/keyvaluetreetransform.cpp
src/gromacs/utility/listoflists.h [new file with mode: 0644]
src/gromacs/utility/loggerbuilder.h
src/gromacs/utility/mdmodulenotification-impl.h [new file with mode: 0644]
src/gromacs/utility/mdmodulenotification.h
src/gromacs/utility/path.cpp
src/gromacs/utility/path.h
src/gromacs/utility/pleasecite.cpp
src/gromacs/utility/programcontext.cpp
src/gromacs/utility/range.h
src/gromacs/utility/real.h
src/gromacs/utility/smalloc.cpp
src/gromacs/utility/smalloc.h
src/gromacs/utility/snprintf.h
src/gromacs/utility/strconvert.h
src/gromacs/utility/strdb.cpp
src/gromacs/utility/sysinfo.cpp
src/gromacs/utility/tests/CMakeLists.txt
src/gromacs/utility/tests/arrayref.cpp
src/gromacs/utility/tests/inmemoryserializer.cpp
src/gromacs/utility/tests/keyvaluetreeserializer.cpp
src/gromacs/utility/tests/listoflists.cpp [new file with mode: 0644]
src/gromacs/utility/tests/mdmodulenotification-impl.cpp [moved from src/programs/mdrun/tests/mdmodulenotification.cpp with 94% similarity]
src/gromacs/utility/tests/mutex.cpp
src/gromacs/utility/tests/range.cpp [new file with mode: 0644]
src/gromacs/utility/tests/strconvert.cpp [new file with mode: 0644]
src/gromacs/utility/tests/stringutil.cpp
src/gromacs/utility/txtdump.cpp
src/gromacs/utility/unique_cptr.h
src/programs/CMakeLists.txt
src/programs/gmx.cpp
src/programs/legacymodules.cpp
src/programs/mdrun/mdrun.cpp
src/programs/mdrun/nonbonded_bench.cpp
src/programs/mdrun/tests/CMakeLists.txt
src/programs/mdrun/tests/comparison_helpers.h [moved from src/gromacs/compat/optional.h with 62% similarity]
src/programs/mdrun/tests/densityfittingmodule.cpp
src/programs/mdrun/tests/energycomparison.cpp
src/programs/mdrun/tests/energycomparison.h
src/programs/mdrun/tests/energyreader.h
src/programs/mdrun/tests/ewaldsurfaceterm.cpp [new file with mode: 0644]
src/programs/mdrun/tests/exactcontinuation.cpp
src/programs/mdrun/tests/freeenergy.cpp [new file with mode: 0644]
src/programs/mdrun/tests/interactiveMD.cpp
src/programs/mdrun/tests/moduletest.cpp
src/programs/mdrun/tests/moduletest.h
src/programs/mdrun/tests/multiple_time_stepping.cpp [new file with mode: 0644]
src/programs/mdrun/tests/multisim.cpp
src/programs/mdrun/tests/multisimtest.cpp
src/programs/mdrun/tests/multisimtest.h
src/programs/mdrun/tests/normalmodes.cpp
src/programs/mdrun/tests/outputfiles.cpp
src/programs/mdrun/tests/periodicactions.cpp
src/programs/mdrun/tests/pmetest.cpp
src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductIdentityMatrix.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductTranslation.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductTranslationAndTransformationMatrix.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/EwaldSurfaceTerm_EwaldSurfaceTermTest_WithinTolerances_0.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/EwaldSurfaceTerm_EwaldSurfaceTermTest_WithinTolerances_1.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/EwaldSurfaceTerm_EwaldSurfaceTermTest_WithinTolerances_2.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_coul_d.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_coul_s.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_vdw_d.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_vdw_s.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwtogether_d.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwtogether_s.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_expanded_d.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_expanded_s.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_d.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_position_restraints_d.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_position_restraints_s.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_s.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_restraints_d.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_restraints_s.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_simtemp_d.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_simtemp_s.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_transformAtoB_d.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_transformAtoB_s.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_vdwalone_d.xml [new file with mode: 0644]
src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_vdwalone_s.xml [new file with mode: 0644]
src/programs/mdrun/tests/replicaexchange.cpp
src/programs/mdrun/tests/rerun.cpp
src/programs/mdrun/tests/simple_mdrun.cpp
src/programs/mdrun/tests/simulator.cpp
src/programs/mdrun/tests/simulatorcomparison.cpp
src/programs/mdrun/tests/simulatorcomparison.h
src/programs/mdrun/tests/swapcoords.cpp
src/programs/mdrun/tests/termination.cpp
src/programs/mdrun/tests/trajectory_writing.cpp
src/programs/mdrun/tests/trajectorycomparison.cpp
src/programs/mdrun/tests/trajectorycomparison.h
src/programs/view/dialogs.cpp
src/programs/view/fgrid.cpp
src/programs/view/manager.cpp
src/programs/view/manager.h
src/programs/view/nmol.cpp
src/programs/view/nmol.h
src/programs/view/view.cpp
src/testutils/CMakeLists.txt
src/testutils/TestMacros.cmake
src/testutils/cmdlinetest.cpp
src/testutils/cmdlinetest.h
src/testutils/refdata.cpp
src/testutils/refdata.h
src/testutils/simulationdatabase/dipoles.gro [new file with mode: 0644]
src/testutils/simulationdatabase/dipoles.ndx [new file with mode: 0644]
src/testutils/simulationdatabase/dipoles.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/README.txt [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwintramol/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwintramol/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwintramol/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_coul/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_coul/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_coul/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/ana.itp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwtogether/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwtogether/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/coulandvdwtogether/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/expanded/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/expanded/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/expanded/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/relative-position-restraints/README [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/relative-position-restraints/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/relative-position-restraints/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/relative-position-restraints/no-nb-gpu-support [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/relative-position-restraints/posre.itp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/relative-position-restraints/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/relative/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/relative/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/relative/no-nb-gpu-support [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/relative/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/restraints/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/restraints/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/restraints/no-nb-gpu-support [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/restraints/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/simtemp/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/simtemp/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/simtemp/no-nb-gpu-support [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/simtemp/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/transformAtoB/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/transformAtoB/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/transformAtoB/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/vdwalone/conf.gro [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/vdwalone/grompp.mdp [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/vdwalone/no-nb-gpu-support [new file with mode: 0644]
src/testutils/simulationdatabase/freeenergy/vdwalone/topol.top [new file with mode: 0644]
src/testutils/simulationdatabase/one-tip5p.top
src/testutils/stringtest.h
src/testutils/test_device.cpp [new file with mode: 0644]
src/testutils/test_device.h [new file with mode: 0644]
src/testutils/test_hardware_environment.cpp [moved from src/gromacs/ewald/tests/testhardwarecontexts.cpp with 65% similarity]
src/testutils/test_hardware_environment.h [moved from src/gromacs/ewald/tests/testhardwarecontexts.h with 55% similarity]
src/testutils/testasserts.h
src/testutils/testfilemanager.cpp
src/testutils/testfilemanager.h
src/testutils/testinit.cpp
src/testutils/testinit.h
src/testutils/testoptions.h
src/testutils/tests/CMakeLists.txt
src/testutils/tests/interactivetest.cpp
src/testutils/tests/refdata_tests.cpp
src/testutils/tests/xvgtest_tests.cpp
src/testutils/unittest_main.cpp
tests/CMakeLists.txt
tests/physicalvalidation/physical_validation/util/gromacs_interface.py
tests/physicalvalidation/systems.json
tests/physicalvalidation/systems/ens_argon_md_verlet_pme_vr_cr/input/system.gro [new file with mode: 0644]
tests/physicalvalidation/systems/ens_argon_md_verlet_pme_vr_cr/input/system.mdp [new file with mode: 0644]
tests/physicalvalidation/systems/ens_argon_md_verlet_pme_vr_cr/input/system.top [new file with mode: 0644]
tests/physicalvalidation/systems/ens_water_md_verlet_settle_pme_vr_cr/input/system.gro [new file with mode: 0644]
tests/physicalvalidation/systems/ens_water_md_verlet_settle_pme_vr_cr/input/system.mdp [new file with mode: 0644]
tests/physicalvalidation/systems/ens_water_md_verlet_settle_pme_vr_cr/input/system.top [new file with mode: 0644]
tests/physicalvalidation/systems_d.json

index 3d701ccad29b8da2e96d66404655db73f2ab0e03..1e83da92ea5067c41ddf1ee3f5820ec222cec0b7 100644 (file)
@@ -30,7 +30,6 @@ cmake/*.c.cmakein                       !filter
 cmake/*.h                               !filter
 cmake/*.cu                              !filter
 cmake/*.cuh                             !filter
-cmake/FindCUDA/*.cmake                  !filter
 docs/doxygen/Doxyfile-*.cmakein         !filter
 docs/doxygen/*.cpp                      !filter
 docs/doxygen/examples/*.cpp             filter=clangformat
index 0911eb2a45f1f70ffeb8e3a24421741d524127c8..268786863e6d417f0eca4527464bfc40806a75ef 100644 (file)
@@ -1,8 +1,9 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2009,2010,2011,2012,2013,2014, The GROMACS development team.
-# Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+# Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-cmake_minimum_required(VERSION 3.9.6)
-if(POLICY CMP0074) #3.12
-    cmake_policy(SET CMP0074 NEW)
-endif()
+cmake_minimum_required(VERSION 3.13)
+cmake_policy(SET CMP0074 NEW) # From CMake 3.12
 cmake_policy(SET CMP0068 NEW) # From CMake-3.9
 
 # CMake modules/macros are in a subdirectory to keep this file cleaner
@@ -44,11 +43,10 @@ cmake_policy(SET CMP0068 NEW) # From CMake-3.9
 list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Platform)
 
 if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
-    # Providing a default value >=10.9 helps to find modern C++ compatibility,
+    # Providing a default value >=10.14 helps to find modern C++ compatibility,
     # such as by defaulting to the Clang libc++ instead of libstdc++.
-    set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9 CACHE STRING
-        "OS X deployment target affects default SDK version and compiler flags."
-        FORCE)
+    set(CMAKE_OSX_DEPLOYMENT_TARGET 10.14 CACHE STRING
+        "OS X deployment target affects default SDK version and compiler flags.")
     # By default, limit the binary architecture to a single 64-bit build.
     set(CMAKE_OSX_ARCHITECTURES x86_64 CACHE STRING
         "OS X architecture affects the compatibility of the (potentially fat) binaries produced."
@@ -57,7 +55,7 @@ endif()
 
 project(Gromacs)
 
-set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
 
@@ -69,6 +67,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 
 find_package(LibStdCpp)
 
+# Python is first referenced in gmxVersionInfo, so we perform the search early
+# to find a suitable installation for all components.
+include(gmxPythonDiscovery)
 # Set up common version variables, as well as general information about
 # the build tree (whether the build is from a source package or from a git
 # repository).  Also declares a few functions that will be used for generating
@@ -88,13 +89,14 @@ include(gmxBuildTypeProfile)
 include(gmxBuildTypeTSAN)
 include(gmxBuildTypeASAN)
 include(gmxBuildTypeMSAN)
+include(gmxBuildTypeUBSAN)
 include(gmxBuildTypeReleaseWithAssert)
 
 if(NOT CMAKE_BUILD_TYPE)
-    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel Reference RelWithAssert Profile TSAN ASAN MSAN." FORCE)
+    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel Reference RelWithAssert Profile TSAN ASAN MSAN UBSAN." FORCE)
     # Set the possible values of build type for cmake-gui
     set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
-        "MinSizeRel" "RelWithDebInfo" "Reference" "RelWithAssert" "Profile" "TSAN" "ASAN" "MSAN")
+        "MinSizeRel" "RelWithDebInfo" "Reference" "RelWithAssert" "Profile" "TSAN" "ASAN" "MSAN" "UBSAN")
 endif()
 if(CMAKE_CONFIGURATION_TYPES)
     # Add appropriate GROMACS-specific build types for the Visual
@@ -196,43 +198,19 @@ option(GMX_COOL_QUOTES "Enable GROMACS cool quotes" ON)
 mark_as_advanced(GMX_COOL_QUOTES)
 gmx_add_cache_dependency(GMX_COOL_QUOTES BOOL "NOT GMX_FAHCORE" OFF)
 
-option(GMX_USE_OPENCL "Enable OpenCL acceleration" OFF)
-
 option(GMX_INSTALL_LEGACY_API "Install legacy headers" OFF)
 
-# The earliest version of the CUDA toolkit that supports c++14 is 9.0
-set(REQUIRED_CUDA_VERSION 9.0)
-set(REQUIRED_CUDA_COMPUTE_CAPABILITY 3.0)
-
-# OpenCL required version: 1.2 or newer
-set(REQUIRED_OPENCL_MIN_VERSION_MAJOR 1)
-set(REQUIRED_OPENCL_MIN_VERSION_MINOR 2)
-set(REQUIRED_OPENCL_MIN_VERSION ${REQUIRED_OPENCL_MIN_VERSION_MAJOR}.${REQUIRED_OPENCL_MIN_VERSION_MINOR})
-
-if(NOT GMX_USE_OPENCL)
-    # CUDA detection is done only if GMX_USE_OPENCL is OFF.
-    include(gmxManageGPU)
-    set(GMX_USE_CUDA ${GMX_GPU})
-    if(GMX_GPU)
-        set(GMX_GPU_ACCELERATION_FRAMEWORK "GMX_GPU_CUDA")
-    else()
-        set(GMX_GPU_ACCELERATION_FRAMEWORK "GMX_GPU_NONE")
-    endif()
-else()
-    #Now the OpenCL path (for both AMD and NVIDIA)
-    if(GMX_GPU)
-        include(gmxManageOpenCL)
-        set(GMX_GPU_ACCELERATION_FRAMEWORK "GMX_GPU_OPENCL")
-    else()
-        message(FATAL_ERROR "OpenCL requested but GPU option is not enabled (try -DGMX_GPU=on) ")
-    endif()
-endif()
+gmx_option_multichoice(
+    GMX_GPU
+    "Framework for GPU acceleration"
+    OFF
+    OFF CUDA OpenCL SYCL)
 
 gmx_option_multichoice(
     GMX_SIMD
     "SIMD instruction set for CPU kernels and compiler optimization"
     "AUTO"
-    AUTO None SSE2 SSE4.1 AVX_128_FMA AVX_256 AVX2_256 AVX2_128 AVX_512 AVX_512_KNL MIC ARM_NEON ARM_NEON_ASIMD IBM_VMX IBM_VSX Sparc64_HPC_ACE Reference)
+    AUTO None SSE2 SSE4.1 AVX_128_FMA AVX_256 AVX2_256 AVX2_128 AVX_512 AVX_512_KNL MIC ARM_NEON ARM_NEON_ASIMD ARM_SVE IBM_VMX IBM_VSX Sparc64_HPC_ACE Reference)
 
 if(GMX_TARGET_MIC)
     set(GMX_FFT_LIBRARY_DEFAULT "mkl")
@@ -258,12 +236,6 @@ gmx_dependent_option(
 mark_as_advanced(GMX_BUILD_OWN_FFTW)
 mark_as_advanced(GMX_DISABLE_FFTW_MEASURE)
 
-gmx_option_multichoice(
-    GMX_QMMM_PROGRAM
-    "QM package for QM/MM"
-    None
-    none gaussian mopac gamess orca)
-
 gmx_dependent_cache_variable(GMX_SIMD_REF_FLOAT_WIDTH  "Reference SIMD single precision width" STRING "4" "GMX_SIMD STREQUAL REFERENCE")
 gmx_dependent_cache_variable(GMX_SIMD_REF_DOUBLE_WIDTH "Reference SIMD double precision width" STRING "2" "GMX_SIMD STREQUAL REFERENCE")
 
@@ -456,6 +428,31 @@ include(gmxManageMimic)
 include(gmxManageSharedLibraries)
 
 
+########################################################################
+# Specify install locations
+########################################################################
+# Use GNUInstallDirs to set paths on multiarch systems.
+include(GNUInstallDirs)
+
+set(GMX_INSTALL_DATASUBDIR "gromacs" CACHE STRING "Subdirectory for GROMACS data under CMAKE_INSTALL_DATADIR")
+mark_as_advanced(GMX_INSTALL_DATASUBDIR)
+
+# Internal convenience so we do not have to join two path segments in the code
+set(GMX_INSTALL_GMXDATADIR ${CMAKE_INSTALL_DATADIR}/${GMX_INSTALL_DATASUBDIR})
+
+# If the nesting level wrt. the installation root is changed,
+# gromacs-config.cmake.cmakein needs to be adapted.
+set(GMX_INSTALL_CMAKEDIR  ${CMAKE_INSTALL_DATAROOTDIR}/cmake)
+
+# TODO: Make GMXRC adapt if this is changed
+set(GMX_INSTALL_PKGCONFIGDIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+
+list(APPEND INSTALLED_HEADER_INCLUDE_DIRS ${CMAKE_INSTALL_INCLUDEDIR})
+
+# Binary and library suffix options
+include(gmxManageSuffixes)
+
+
 ########################################################################
 # Find external packages                                               #
 ########################################################################
@@ -483,10 +480,7 @@ include(gmxManageSharedLibraries)
 #    set(XML_LIBRARIES ${LIBXML2_LIBRARIES})
 #endif()
 
-gmx_option_trivalue(
-    GMX_HWLOC
-    "Use hwloc portable hardware locality library"
-    "AUTO")
+option(GMX_HWLOC "Use hwloc portable hardware locality library" OFF)
 
 if (GMX_HWLOC)
     # Find quietly the second time.
@@ -573,13 +567,22 @@ include(gmxManageTNG)
 
 include(gmxManageLmfit)
 
+include(gmxManageMuparser)
+
 if(GMX_GPU)
-    # now that we have detected the dependencies, do the second configure pass
-    gmx_gpu_setup()
-    if (GMX_CLANG_CUDA)
-        list(APPEND GMX_EXTRA_LIBRARIES ${GMX_CUDA_CLANG_LINK_LIBS})
-        link_directories("${GMX_CUDA_CLANG_LINK_DIRS}")
+
+    string(TOUPPER "${GMX_GPU}" _gmx_gpu_uppercase)
+    if(${_gmx_gpu_uppercase} STREQUAL "CUDA")
+        include(gmxManageCuda)
+    elseif(${_gmx_gpu_uppercase} STREQUAL "OPENCL")
+        include(gmxManageOpenCL)
+    elseif(${_gmx_gpu_uppercase} STREQUAL "SYCL")
+        include(gmxManageSYCL)
     endif()
+    if(NOT GMX_OPENMP)
+        message(WARNING "To use GPU acceleration efficiently, mdrun requires OpenMP multi-threading, which is currently not enabled.")
+    endif()
+
 endif()
 
 if(CYGWIN)
@@ -602,6 +605,7 @@ gmx_add_cache_dependency(GMX_BUILD_UNITTESTS BOOL BUILD_TESTING OFF)
 ########################################################################
 
 include_directories(BEFORE ${CMAKE_SOURCE_DIR}/src)
+include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/src/external)
 # Required for config.h, maybe should only be set in src/CMakeLists.txt
 include_directories(BEFORE ${CMAKE_BINARY_DIR}/src)
 
@@ -611,51 +615,11 @@ gmx_test_inline_asm_gcc_x86(GMX_X86_GCC_INLINE_ASM)
 include(gmxSetBuildInformation)
 gmx_set_build_information()
 
-gmx_option_multichoice(
-    GMX_USE_RDTSCP
-    "Use low-latency RDTSCP instruction for CPU-based timers for mdrun execution; might need to be off when compiling for heterogeneous environments)"
-    "AUTO"
-    OFF ON AUTO DETECT)
+# Anything but truly ancient x86 hardware should support rdtscp, so we enable it by default.
+# The inline assembly calling it is only ever compiled on x86, so defaulting to ON is OK.
+option(GMX_USE_RDTSCP "Use low-latency RDTSCP instruction for x86 CPU-based timers for mdrun execution; might need to be off when compiling for heterogeneous environments" ON)
 mark_as_advanced(GMX_USE_RDTSCP)
 
-macro(gmx_check_rdtscp)
-    if (CPU_DETECTION_FEATURES MATCHES "rdtscp")
-        set(HAVE_RDTSCP 1)
-        set(RDTSCP_DETECTION_MESSAGE " - detected on the build host")
-    else()
-        set(RDTSCP_DETECTION_MESSAGE " - not detected on the build host")
-    endif()
-endmacro()
-
-set(HAVE_RDTSCP 0)
-if (GMX_USE_RDTSCP STREQUAL "ON")
-    set(HAVE_RDTSCP 1)
-elseif(GMX_USE_RDTSCP STREQUAL "DETECT")
-    gmx_check_rdtscp()
-elseif(GMX_USE_RDTSCP STREQUAL "AUTO")
-    # If the user specified automated SIMD selection, that the choice
-    # is made based on detection on the build host. If so, then RDTSCP
-    # should be chosen the same way.
-    #
-    # If the user specified an AVX SIMD level (e.g. when
-    # cross-compiling GROMACS) then they will get our best guess, ie
-    # that in practice AVX mostly correlates with rdtscp (and anyway
-    # is only relevant in rather old x86 hardware).
-    if (GMX_SIMD STREQUAL "AUTO")
-        gmx_check_rdtscp()
-    elseif (GMX_SIMD MATCHES "AVX")
-        set(HAVE_RDTSCP 1)
-    endif()
-endif()
-gmx_check_if_changed(HAVE_RDTSCP_CHANGED HAVE_RDTSCP)
-if (HAVE_RDTSCP_CHANGED)
-    if (HAVE_RDTSCP)
-        message(STATUS "Enabling RDTSCP support${RDTSCP_DETECTION_MESSAGE}")
-    else()
-        message(STATUS "Disabling RDTSCP support${RDTSCP_DETECTION_MESSAGE}")
-    endif()
-endif()
-
 include(gmxTestLargeFiles)
 gmx_test_large_files(GMX_LARGEFILES)
 
@@ -691,25 +655,6 @@ endif()
 include(gmxManageSimd)
 gmx_manage_simd()
 
-include(gmxManageCycleCounters)
-gmx_manage_cycle_counters()
-
-# Process QM/MM Settings
-if(${GMX_QMMM_PROGRAM} STREQUAL "GAUSSIAN")
-    set(GMX_QMMM_GAUSSIAN 1)
-elseif(${GMX_QMMM_PROGRAM} STREQUAL "MOPAC")
-    set(GMX_QMMM_MOPAC 1)
-elseif(${GMX_QMMM_PROGRAM} STREQUAL "GAMESS")
-    set(GMX_QMMM_GAMESS 1)
-elseif(${GMX_QMMM_PROGRAM} STREQUAL "ORCA")
-    set(GMX_QMMM_ORCA 1)
-elseif(${GMX_QMMM_PROGRAM} STREQUAL "NONE")
-    # nothing to do
-else()
-    gmx_invalid_option_value(GMX_QMMM_PROGRAM)
-endif()
-
-
 ##################################################
 # Process FFT library settings
 ##################################################
@@ -719,8 +664,9 @@ include(gmxManageFFTLibraries)
 include(gmxManageLinearAlgebraLibraries)
 
 include(gmxManagePluginSupport)
+gmx_manage_plugin_support()
 
-if (GMX_USE_PLUGINS)
+if(GMX_USE_PLUGINS)
     if(NOT GMX_VMD_PLUGIN_PATH)
         find_package(VMD)
     endif()
@@ -759,13 +705,7 @@ if(GMX_FAHCORE)
   include_directories(${COREWRAP_INCLUDE_DIR})
 endif()
 
-# Value of GMX_BUILD_HELP=AUTO tries to generate things, but will only
-# produce warnings if that fails.
-set(build_help_default AUTO)
-if (SOURCE_IS_SOURCE_DISTRIBUTION OR CMAKE_CROSSCOMPILING)
-    set(build_help_default OFF)
-endif()
-gmx_option_trivalue(GMX_BUILD_HELP "Build completions automatically (requires that compiled binaries can be executed on the build host) and install man pages if built (requires building the 'man' target manually)" ${build_help_default})
+option(GMX_BUILD_HELP "Build completions (requires that compiled binaries can be executed on build host) and install man pages if built (requires building the 'man' target manually)" OFF)
 mark_as_advanced(GMX_BUILD_HELP)
 if (GMX_BUILD_HELP AND SOURCE_IS_SOURCE_DISTRIBUTION AND BUILD_IS_INSOURCE)
     message(FATAL_ERROR
@@ -777,8 +717,8 @@ endif()
 # # # # # # # # # # NO MORE TESTS AFTER THIS LINE! # # # # # # # # # # #
 # these are set after everything else
 if (NOT GMX_SKIP_DEFAULT_CFLAGS)
-    set(CMAKE_EXE_LINKER_FLAGS "${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
-    set(CMAKE_SHARED_LINKER_FLAGS "${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
+    set(CMAKE_EXE_LINKER_FLAGS "${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS} ${DISABLE_SYCL_CXX_FLAGS}")
+    set(CMAKE_SHARED_LINKER_FLAGS "${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS} ${DISABLE_SYCL_CXX_FLAGS}")
 else()
     message("Recommended flags which are not added because GMX_SKIP_DEFAULT_CFLAGS=yes:")
     message("CMAKE_C_FLAGS: ${SIMD_C_FLAGS};${MPI_COMPILE_FLAGS};${EXTRA_C_FLAGS};${GMXC_CFLAGS}")
@@ -792,31 +732,8 @@ else()
     message("CMAKE_EXE_LINKER_FLAGS: ${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS}")
     message("CMAKE_SHARED_LINKER_FLAGS: ${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS}")
 endif()
-
-########################################################################
-# Specify install locations
-########################################################################
-# Use GNUInstallDirs to set paths on multiarch systems.
-include(GNUInstallDirs)
-
-set(GMX_INSTALL_DATASUBDIR "gromacs" CACHE STRING "Subdirectory for GROMACS data under CMAKE_INSTALL_DATADIR")
-mark_as_advanced(GMX_INSTALL_DATASUBDIR)
-
-# Internal convenience so we do not have to join two path segments in the code
-set(GMX_INSTALL_GMXDATADIR ${CMAKE_INSTALL_DATADIR}/${GMX_INSTALL_DATASUBDIR})
-
-# If the nesting level wrt. the installation root is changed,
-# gromacs-config.cmake.cmakein needs to be adapted.
-set(GMX_INSTALL_CMAKEDIR  ${CMAKE_INSTALL_DATAROOTDIR}/cmake)
-
-# TODO: Make GMXRC adapt if this is changed
-set(GMX_INSTALL_PKGCONFIGDIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
-set(GMX_INSTALL_OCLDIR       ${GMX_INSTALL_GMXDATADIR}/opencl)
-
-list(APPEND INSTALLED_HEADER_INCLUDE_DIRS ${CMAKE_INSTALL_INCLUDEDIR})
-
-# Binary and library suffix options
-include(gmxManageSuffixes)
+# Allow `admin` directory to be easily conveyed to nested CMake commands.
+set(GMX_ADMIN_DIR ${CMAKE_SOURCE_DIR}/admin)
 
 ################################################################
 # Shared library load path settings
@@ -865,30 +782,11 @@ if (BUILD_TESTING)
 endif()
 
 # TODO: Determine control flow and defaults for package installation and testing use cases.
-# Ref: http://redmine.gromacs.org/issues/2896
+# Ref: https://gitlab.com/gromacs/gromacs/-/issues/2896
 option(GMX_PYTHON_PACKAGE "Configure gmxapi Python package" OFF)
 mark_as_advanced(GMX_PYTHON_PACKAGE)
 
 if (NOT GMX_BUILD_MDRUN_ONLY)
-    # Note: Though only documented as an output variable, PYTHON_EXECUTABLE is
-    # also effective as a CMake input variable to effectively hint the location
-    # of the Python interpreter. This may be helpful in environments with both
-    # Python 2 and Python 3 on the default PATH.
-    # Ref: https://cmake.org/cmake/help/latest/module/FindPythonInterp.html
-    if(FIND_PACKAGE_MESSAGE_DETAILS_PythonInterp)
-        # Keep quiet on subsequent runs of cmake
-        set(PythonInterp_FIND_QUIETLY ON)
-    endif()
-    # Older CMake versions might not search for Python newer than 3.7.
-    set(Python_ADDITIONAL_VERSIONS 3.8)
-    if(GMX_PYTHON_PACKAGE)
-        find_package(PythonInterp 3.5 REQUIRED)
-        # Note: PythonLibs will be found later by pybind11.
-        # TODO: (issue #2998) When CMake >= 3.12 is required, update detection.
-        # I.e.  find_package(Python3 3.5 COMPONENTS Interpreter Development REQUIRED)
-    else()
-        find_package(PythonInterp 3.5)
-    endif()
     find_package(ImageMagick QUIET COMPONENTS convert)
     include(gmxTestImageMagick)
     GMX_TEST_IMAGEMAGICK(IMAGE_CONVERT_POSSIBLE)
@@ -897,6 +795,7 @@ if (NOT GMX_BUILD_MDRUN_ONLY)
     add_subdirectory(share)
     add_subdirectory(scripts)
 endif()
+add_subdirectory(api)
 add_subdirectory(src)
 
 if (BUILD_TESTING)
@@ -909,13 +808,6 @@ endif()
 
 gmx_cpack_write_config()
 
-# Issue a warning if NVIDIA GPUs were detected, but CUDA was not found.
-# Don't bother the user after the first configure pass.
-if ((CUDA_NOTFOUND_AUTO AND GMX_DETECT_GPU_AVAILABLE) AND NOT GMX_GPU_DETECTION_DONE)
-    message(WARNING "${CUDA_NOTFOUND_MESSAGE}")
-endif()
-set(GMX_GPU_DETECTION_DONE TRUE CACHE INTERNAL "Whether GPU detection has already been done")
-
 #######################
 ## uninstall target
 #######################
diff --git a/COPYING b/COPYING
index 0f7f155b3d4bd436f717e4e621ce1062de4cd25d..cf0d7ffe643af66cdd80d224174dc31f031ca88f 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -29,6 +29,8 @@ This file contains the licenses for the following bodies of code:
 18. pybind11
 19. Reference implementation of std::string_view
 20. Reference implementation of std::optional
+21. stl_interfaces
+22. muparser
 
 Our chosen method for packaging distributions (CPack) only permits a
 package to have a single license file, so we are unfortunately forced
@@ -1369,7 +1371,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 Questions? Contact Christian R. Trott (crtrott@sandia.gov)
 
-18. pybind11 release 2.2.4 from https://github.com/pybind/pybind11
+18. pybind11 release 2.5.0 from https://github.com/pybind/pybind11
 ==================================================================
 
     Files: python_packaging/src/external/pybind/*
@@ -1439,7 +1441,7 @@ DEALINGS IN THE SOFTWARE.
 20. Reference implementation of std::optional
 =============================================
 
-Used with minor modifications from commit a08fda6c674ae4c9f of
+Used with minor modifications from commit 096ce6d7e45ef184d of
 https://github.com/martinmoene/optional-lite.git, which is licensed as below.
 
 Boost Software License - Version 1.0 - August 17th, 2003
@@ -1465,3 +1467,67 @@ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 DEALINGS IN THE SOFTWARE.
+
+21. stl_interfaces
+==================
+
+Used with minor modification from commit 486db0a5543cea2901ed of
+https://github.com/boostorg/stl_interfaces, which is licensed as below.
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+22. muparser
+============
+
+Used version 2.3.2 from https://github.com/beltoforion/muparser/releases/tag/v2.3.2,
+which is licensed as below.
+
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
diff --git a/admin/builds/clang-analyzer.py b/admin/builds/clang-analyzer.py
deleted file mode 100644 (file)
index 1b1129e..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-# These options need to match the node label in Jenkins and the
-# capabilities in releng/agents.py for the agent where the analysis is
-# intended to run.
-build_options = ['clang-8', 'clang-static-analyzer-8']
-
-# Policy global variables
-use_stdlib_through_env_vars = False
-
-def do_build(context):
-    cmake_opts = {
-            'CMAKE_BUILD_TYPE': 'Debug',
-            # Build tests as part of the all target.
-            'GMX_DEVELOPER_BUILD': 'ON',
-            'GMX_GPU': 'OFF',
-            'GMX_OPENMP': 'OFF',
-            'GMX_SIMD': 'None',
-            'GMX_USE_RDTSCP': 'OFF',
-            'GMX_FFT_LIBRARY': 'fftpack',
-            'GMX_CLANG_ANALYZER' : 'ON'
-        }
-    context.env.append_to_env_var('CXXFLAGS', '-stdlib=libc++')
-
-    context.run_cmake(cmake_opts)
-    context.build_target(target=None)
-    context.process_clang_analyzer_results()
diff --git a/admin/builds/clang-format-update.py b/admin/builds/clang-format-update.py
deleted file mode 100644 (file)
index 237b95e..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2019, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-def do_build(context):
-    context.env.set_env_var('CLANG_FORMAT', context.env.get_clang_format_command('7'))
-    clangformat_log = context.workspace.get_path_for_logfile('clang-format.log', category='clang-format')
-    cmd = ['admin/clang-format.sh', 'update', '--rev=HEAD^', '--warnings=' + clangformat_log]
-    ret = context.run_cmd(cmd, use_return_code=True)
-    if ret == 1:
-        with open(clangformat_log, 'r') as f:
-            warnings = f.readlines()
-        if len(warnings) <= 5:
-            details = [x.rstrip() for x in warnings]
-        else:
-            format_count = 0
-            for w in warnings:
-                if 'clang-format performed' in w:
-                    format_count += 1
-            details = []
-            if format_count > 0:
-                details.append('fixed formatting issues in {0} files'.format(format_count))
-    elif ret != 0:
-        raise BuildError('clang-format.sh failed to run')
-
-    context.workspace.upload_revision(project=Project.GROMACS, file_glob='*')
diff --git a/admin/builds/clang-format.py b/admin/builds/clang-format.py
deleted file mode 100644 (file)
index 6582cd9..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2019, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-def do_build(context):
-    context.env.set_env_var('CLANG_FORMAT', context.env.get_clang_format_command('7'))
-    clangformat_log = context.workspace.get_path_for_logfile('clang-format.log', category='clang-format')
-    copyright_log  = context.workspace.get_path_for_logfile('copyright.log', category='copyright')
-    cmd = ['admin/clang-format.sh', 'check', '--rev=HEAD^', '--warnings=' + clangformat_log]
-    ret = context.run_cmd(cmd, use_return_code=True)
-    if ret == 1:
-        with open(clangformat_log, 'r') as f:
-            warnings = f.readlines()
-        if len(warnings) <= 5:
-            details = [x.rstrip() for x in warnings]
-        else:
-            format_count = 0
-            for w in warnings:
-                if 'needs formatting' in w:
-                    format_count += 1
-            details = []
-            if format_count > 0:
-                details.append('formatting issues in {0} files'.format(format_count))
-        context.mark_unstable(reason='clang-format.sh found issues', details=details)
-    elif ret != 0:
-        raise BuildError('clang-format.sh failed to run')
-
-    cmd = ['admin/copyright.sh', 'check', '--rev=HEAD^', '--warnings=' + copyright_log]
-    ret = context.run_cmd(cmd, use_return_code=True)
-    if ret == 1:
-        with open(copyright_log, 'r') as f:
-            warnings = f.readlines()
-        if len(warnings) <= 5:
-            details = [x.rstrip() for x in warnings]
-        else:
-            cpyear_count = 0
-            cpheader_count = 0
-            for w in warnings:
-                if 'copyright year' in w:
-                    cpyear_count += 1
-                if 'copyright header' in w:
-                    cpheader_count += 1
-            details = []
-            if cpyear_count > 0:
-                details.append('copyright year missing in {0} files'.format(cpyear_count))
-            if cpheader_count > 0:
-                details.append('copyright header issues in {0} files'.format(cpheader_count))
-        context.mark_unstable(reason='copyright.sh found issues', details=details)
-    elif ret != 0:
-        raise BuildError('copyright.sh failed to run')
diff --git a/admin/builds/coverage.py b/admin/builds/coverage.py
deleted file mode 100644 (file)
index 9c3b29d..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2015,2016,2017,2019, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-import os.path
-
-build_options = ['gcc-7', 'gcov-7']
-extra_projects = [Project.REGRESSIONTESTS]
-
-def do_build(context):
-    cmake_opts=dict()
-    cmake_opts['CMAKE_BUILD_TYPE'] = 'Debug'
-    cmake_opts['GMX_BUILD_FOR_COVERAGE'] = 'ON'
-    cmake_opts['CMAKE_C_FLAGS'] = '--coverage -g'
-    cmake_opts['CMAKE_CXX_FLAGS'] = '--coverage -g'
-    cmake_opts['GMX_GPU'] = 'OFF'
-    # Ideally, this would be Reference, but running the regression tests is way
-    # too slow that way (and also with None)...
-    cmake_opts['GMX_SIMD'] = 'SSE4.1'
-    cmake_opts['GMX_USE_RDTSCP'] = 'DETECT'
-
-    context.env.set_env_var('GMX_NO_TERM', '1')
-
-    context.run_cmake(cmake_opts)
-    context.build_target(target=None)
-    context.build_target(target='tests')
-
-    context.run_ctest(args=['--output-on-failure'])
-
-    context.env.prepend_path_env(os.path.join(context.workspace.build_dir, 'bin'))
-    context.chdir(context.workspace.get_project_dir(Project.REGRESSIONTESTS))
-    cmd = ['perl', 'gmxtest.pl', '-xml', 'all', '-nt', '2']
-    context.run_cmd(cmd, failure_message='Regression tests failed to execute')
-
-    context.process_coverage_results(exclude=['^src/external/'])
diff --git a/admin/builds/documentation.py b/admin/builds/documentation.py
deleted file mode 100644 (file)
index 386f26e..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-import os
-import re
-
-build_options = ['doxygen-1.8.5']
-build_out_of_source = True
-
-extra_options = {
-    'source-md5': Option.string
-}
-
-def do_build(context):
-    cmake_opts = {
-            'GMX_BUILD_HELP': 'ON',
-            'GMX_BUILD_MANUAL': 'ON',
-            'SOURCE_MD5SUM': context.opts.source_md5,
-            'CMAKE_BUILD_TYPE': 'Debug',
-            'GMX_GPU': 'OFF',
-            'GMX_OPENMP': 'OFF',
-            'GMX_SIMD': 'None',
-            'GMX_USE_RDTSCP': 'OFF'
-        }
-    release = (context.job_type == JobType.RELEASE)
-    if release:
-        cmake_opts['GMX_BUILD_TARBALL'] = 'ON'
-    elif context.job_type == JobType.GERRIT:
-        cmake_opts['GMX_COMPACT_DOXYGEN'] = 'ON'
-    cmake_opts['DOXYGEN_EXECUTABLE'] = context.env.doxygen_command
-    context.run_cmake(cmake_opts)
-
-    # we keep the individual build targets here to ensure some
-    # granularity of the resulting error messages (if any).
-    # it would be possible to run everything at once with
-    # target=webpage, but then debugging a failed build would
-    # become exceedingly tedious
-    context.build_target(target='sphinx-input', parallel=True,
-            failure_string='Generating Sphinx input failed',
-            continue_on_failure=True)
-    context.build_target(target='sphinx-programs', parallel=True,
-            failure_string='Running gmx help -export rst failed',
-            continue_on_failure=True)
-
-    context.build_target(target='manual', parallel=True,
-            target_descr='PDF manual', continue_on_failure=True)
-    logfile = os.path.join(context.workspace.build_dir, 'docs/manual/gromacs.log')
-    if os.path.isfile(logfile):
-        with open(logfile, 'r') as f:
-            manual_log = f.read()
-        if re.search(r'LaTeX Warning: Reference .* on page .* undefined', manual_log):
-            context.mark_unstable('undefined references in PDF manual')
-    context.publish_logs([logfile])
-
-    # check-source is not necessary for a release build, and building these
-    # separately causes many of the Doxygen targets to get built twice if run
-    # from a tarball.
-    if not release:
-        context.build_target(target='doxygen-all', parallel=True,
-                target_descr='Doxygen documentation', continue_on_failure=True)
-        context.build_target(target='check-source', parallel=True,
-                failure_string='check-source failed to run', continue_on_failure=True)
-        logs = []
-        for target in ('check-source', 'doxygen-xml', 'doxygen-user',
-                'doxygen-lib', 'doxygen-full'):
-            logfile = os.path.join(context.workspace.build_dir,
-                    'docs/doxygen/{0}.log'.format(target))
-            if os.path.isfile(logfile) and os.stat(logfile).st_size > 0:
-                context.mark_unstable('{0} produced warnings'.format(target))
-            logs.append(logfile)
-        context.publish_logs(logs, category='doxygen')
-        if context.failed:
-            return
-
-    if context.failed:
-        return
-
-    sphinx_targets = [ ('webpage-sphinx', 'html', 'HTML') ]
-    if not release:
-        sphinx_targets.extend((
-                ('man', 'man', 'man page'),
-                ('install-guide', 'install', 'install-guide')
-            ))
-    logs = []
-    for target, log, descr in sphinx_targets:
-        context.build_target(target=target, parallel=True,
-                failure_string='Sphinx: {0} generation failed'.format(descr),
-                continue_on_failure=True)
-        logfile = os.path.join(context.workspace.build_dir,
-                'docs/sphinx-{0}.log'.format(log))
-        if os.path.isfile(logfile) and os.stat(logfile).st_size > 0:
-            context.mark_unstable('Sphinx: {0} generation produced warnings'.format(descr))
-        logs.append(logfile)
-    context.publish_logs(logs, category='sphinx')
-    if context.failed:
-        return
-
-    context.build_target(target='webpage', parallel=True)
-    if context.failed:
-        return
-
-    ignore_urls = ['html-full', 'html-user', 'html-lib', '.tar.gz', '_sources']
-    cmd = ['linkchecker', 'docs/html/index.html', '-f',
-            context.workspace.get_project_dir(Project.GROMACS) + '/docs/linkcheckerrc',
-            '-Fxml']
-    for url in ignore_urls:
-        cmd.extend(['--ignore-url', url])
-
-    context.run_cmd(cmd, ignore_failure=False)
-
-    logfile = os.path.join(context.workspace.build_dir,
-        'docs/linkchecker-out.xml')
-    if os.path.isfile(logfile):
-        with open(logfile, 'r') as f:
-            manual_log = f.read()
-            if re.search(r'URLError:', manual_log):
-                context.mark_unstable('non resolvable URL in webpage')
-            if re.search(r'warnings', manual_log):
-                context.mark_unstable('empty pages in web documentation')
-        context.publish_logs([logfile], category='webpage')
-
-    if context.failed:
-        return
-
-    if release:
-        version_info = context.read_cmake_variable_file('VersionInfo.cmake')
-        version = version_info['GMX_VERSION_STRING']
-        package_name = 'website-' + version
-        context.make_archive(package_name, root_dir='docs/html', prefix=package_name)
diff --git a/admin/builds/gpucomm-matrix.txt b/admin/builds/gpucomm-matrix.txt
deleted file mode 100644 (file)
index 644334d..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-# This matrix is intended to permit Jenkins:
-# - nigtly testing of the GPU direct communations experimental features in the 2020 release
-# - on-demand testing of the GPU direct communication code path
-# This is neceassary due to:
-# i) the limitations of the integration tests which only test the deafult code-path
-# ii) limitations of the regresssion tests which are not able to rerun update with a
-#     different offload mode unless the default is observed to be GPU.
-#
-# Comment line(s) preceding each configuration document the main
-# intent behind that configuration, so that we can correctly judge
-# whether to preserve that during maintenance decisions.
-#
-# Both configurations currently target bs_nix1204, for better load
-# balance with pre-submit matrix, which makes heavier use of
-# bs_nix1310 agent.
-
-# Test an older version of CUDA
-# Test MPI with CUDA
-# Test multiple PP ranks with separate PME rank
-# Test with thread-MPI (library MPI not supported initially)
-# Test GPU comm features in the above combination
-gcc-5 gpuhw=nvidia cuda-9.0 npme=1 nranks=3 thread-mpi openmp gpucomm
-
-# Test newest gcc supported by newest CUDA at time of release
-# Test multiple PP ranks without separate PME rank
-# Test thread-MPI with CUDA
-# Test GPU comm features in the above combination
-gcc-8 gpuhw=nvidia nranks=2 gpu_id=1 cuda-10.1 thread-mpi openmp cmake-3.10.0 release-with-assert simd=avx2_256 hwloc libhwloc-2.0.4 gpucomm
-
-# Test non-default use of mdrun -gpu_id
-# Test GPU-sharing among 4 PP ranks
-# Test no hwloc build, tests internal CPU topology detection (mainly for x86)
-# Test GPU comm features in the above combination
-gcc-7 gpuhw=nvidia nranks=4 gpu_id=1 cuda-10.0 no-hwloc release-with-assert gpucomm
-
-# Test CUDA build on a agent with no CUDA devices
-# Test without TNG support
-# Test GPU comm features in the above combination
-gcc-7 gpuhw=none cuda-10.0 openmp no-tng release-with-assert gpucomm
-
-# Test OpenCL build with gpudev features
-# Test GPU comm on the OpenCL path where it is unsupported
-clang-8 openmp gpuhw=amd opencl-1.2 clFFT-2.14 simd=None gpucomm
-
-# Test a non-GPU build with the gpucomm optiom set
-gcc-8 double x11 no-tng fftpack simd=sse4.1 gpucomm
diff --git a/admin/builds/gpuupdate-matrix.txt b/admin/builds/gpuupdate-matrix.txt
deleted file mode 100644 (file)
index f31acc2..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-# This matrix is intended to permit Jenkins:
-# - nigtly testing of the GPU update features in the 2020 release
-# - on-demand testing of the GPU update code path
-# This is neceassary due to:
-# i) the limitations of the integration tests which only test the deafult code-path
-# ii) limitations of the regresssion tests which are not able to rerun update with a
-#     different offload mode unless the default is observed to be GPU.
-#
-# Comment line(s) preceding each configuration document the main
-# intent behind that configuration, so that we can correctly judge
-# whether to preserve that during maintenance decisions.
-
-# Test GPU update-constraints features on a single PP+PME rank
-gcc-8 gpuhw=nvidia nranks=1 gpu_id=1 cuda-10.1 thread-mpi openmp cmake-3.10.0 release-with-assert simd=avx2_256 hwloc libhwloc-2.0.4 gpuupdate
-
-# Test GPU update-constraints features in a CUDA build without CUDA devices
-gcc-7 gpuhw=none cuda-10.0 openmp no-tng release-with-assert gpuupdate
-
-# Test GPU update-constraints on the OpenCL path where it is unsupported
-clang-8 openmp gpuhw=amd opencl-1.2 clFFT-2.14 simd=None gpuupdate
-
-# Test GPU update-constraints features with multiple PP ranks and one PME rank
-# Note: this should fall back correctly to the CPU codepath
-gcc-5 gpuhw=nvidia cuda-9.0 cmake-3.9.6 thread-mpi npme=1 nranks=3 release-with-assert gpuupdate
diff --git a/admin/builds/gromacs.py b/admin/builds/gromacs.py
deleted file mode 100644 (file)
index cb9d78e..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-import os.path
-
-# Policy global variables
-use_stdlib_through_env_vars = False
-
-# These are accessible later in the script, just like other declared
-# options, via e.g. context.opts.release.  Keep these in alphabetical
-# order for more convenient rebasing
-extra_options = {
-    'asan': Option.simple,
-    'buildfftw': Option.simple,
-    'clang_cuda': Option.bool,
-    'double': Option.simple,
-    'fftpack': Option.simple,
-    'gpu_id': Option.string,
-    'hwloc': Option.bool,
-    'mdrun-only': Option.simple,
-    'mkl': Option.simple,
-    'mpiinplace': Option.bool,
-    'npme': Option.string,
-    'nranks': Option.string,
-    'openmp': Option.bool,
-    'reference': Option.simple,
-    'release': Option.simple,
-    'release-with-assert': Option.simple,
-    'release-with-debug-info': Option.simple,
-    'static': Option.simple,
-    'thread-mpi': Option.bool,
-    'tng' : Option.bool,
-    # The following options cater for testing code in Jenkins that is
-    # currently behind feature flags in master branch.
-    'gpubufferops' : Option.bool,
-    'gpucomm': Option.bool,
-    'gpuupdate': Option.bool,
-}
-
-extra_projects = [Project.REGRESSIONTESTS]
-
-def do_build(context):
-    cmake_opts = dict()
-    cmake_opts['GMX_COMPILER_WARNINGS'] = 'ON'
-    cmake_opts['GMX_DEFAULT_SUFFIX'] = 'OFF'
-    cmake_opts['CMAKE_BUILD_TYPE'] = 'Debug'
-    cmake_opts['GMX_USE_RDTSCP'] = 'DETECT'
-
-    if not context.opts.msvc and not context.opts.mdrun_only and not context.opts.static:
-        cmake_opts['GMXAPI'] = 'ON'
-
-    if context.opts.reference:
-        cmake_opts['CMAKE_BUILD_TYPE'] = 'Reference'
-    elif context.opts['release']:
-        cmake_opts['CMAKE_BUILD_TYPE'] = 'Release'
-    elif context.opts['release-with-assert']:
-        cmake_opts['CMAKE_BUILD_TYPE'] = 'RelWithAssert'
-    elif context.opts['release-with-debug-info']:
-        cmake_opts['CMAKE_BUILD_TYPE'] = 'RelWithDebInfo'
-    elif context.opts.asan:
-        cmake_opts['CMAKE_BUILD_TYPE'] = 'ASAN'
-    elif context.opts.tsan:
-        cmake_opts['CMAKE_BUILD_TYPE'] = 'TSAN'
-
-    if context.opts.static:
-        cmake_opts['BUILD_SHARED_LIBS'] = 'OFF'
-
-    if context.opts.phi:
-        cmake_opts['CMAKE_TOOLCHAIN_FILE'] = 'Platform/XeonPhi'
-
-    if context.opts.double:
-        cmake_opts['GMX_DOUBLE'] = 'ON'
-
-    if context.opts.simd is None:
-        cmake_opts['GMX_SIMD'] = 'None'
-    else:
-        cmake_opts['GMX_SIMD'] = context.opts.simd
-    if context.opts.cuda or context.opts.opencl:
-        cmake_opts['GMX_GPU'] = 'ON'
-        if context.opts.opencl:
-            context.env.set_env_var('CUDA_PATH', context.env.cuda_root)
-            cmake_opts['GMX_USE_OPENCL'] = 'ON'
-        else:
-            cmake_opts['CUDA_TOOLKIT_ROOT_DIR'] = context.env.cuda_root
-            if context.opts.clang_cuda:
-                cmake_opts['GMX_CLANG_CUDA'] = 'ON'
-            else:
-                cmake_opts['CUDA_HOST_COMPILER'] = context.env.cuda_host_compiler
-    else:
-        cmake_opts['GMX_GPU'] = 'OFF'
-    if context.opts.thread_mpi is False:
-        cmake_opts['GMX_THREAD_MPI'] = 'OFF'
-    if context.opts.mpi:
-        cmake_opts['GMX_MPI'] = 'ON'
-    if context.opts.mpiinplace is False:
-        cmake_opts['GMX_MPI_IN_PLACE'] = 'OFF'
-    if context.opts.openmp is False:
-        cmake_opts['GMX_OPENMP'] = 'OFF'
-    if context.opts.tng is False:
-        cmake_opts['GMX_USE_TNG'] = 'OFF'
-
-    if context.opts.mkl:
-        cmake_opts['GMX_FFT_LIBRARY'] = 'mkl'
-    elif context.opts.fftpack:
-        cmake_opts['GMX_FFT_LIBRARY'] = 'fftpack'
-    elif context.opts.buildfftw:
-        cmake_opts['GMX_BUILD_OWN_FFTW'] = 'ON'
-        cmake_opts['GMX_BUILD_OWN_FFTW_URL'] = 'ftp://ftp.gromacs.org/misc/fftw-3.3.8.tar.gz'
-        cmake_opts['GMX_BUILD_OWN_FFTW_MD5'] = '8aac833c943d8e90d51b697b27d4384d'
-    if context.opts.mkl or context.opts.atlas or context.opts.armpl:
-        cmake_opts['GMX_EXTERNAL_BLAS'] = 'ON'
-        cmake_opts['GMX_EXTERNAL_LAPACK'] = 'ON'
-    if context.opts.clFFT:
-        cmake_opts['GMX_EXTERNAL_CLFFT'] = 'ON'
-        cmake_opts['clFFT_ROOT'] = context.env.clFFT_root
-
-    if context.opts.armpl:
-        cmake_opts['FFTWF_LIBRARY']     = os.path.join(context.env.armpl_dir, 'lib/libarmpl_lp64.so')
-        cmake_opts['FFTWF_INCLUDE_DIR'] = os.path.join(context.env.armpl_dir, 'include')
-        cmake_opts['GMX_BLAS_USER']     = os.path.join(context.env.armpl_dir, 'lib/libarmpl_lp64.so')
-        cmake_opts['GMX_LAPACK_USER']   = os.path.join(context.env.armpl_dir, 'lib/libarmpl_lp64.so')
-
-    if context.opts.hwloc is False:
-        cmake_opts['GMX_HWLOC'] = 'OFF'
-    elif context.opts.hwloc is True:
-        cmake_opts['GMX_HWLOC'] = 'ON'
-    else:
-        cmake_opts['GMX_HWLOC'] = 'AUTO'
-
-    if context.opts.tng is False:
-        cmake_opts['GMX_USE_TNG'] = 'OFF'
-
-    if context.opts.x11:
-        cmake_opts['GMX_X11'] = 'ON'
-
-    if context.opts.tidy:
-        cmake_opts['GMX_CLANG_TIDY'] = 'ON'
-        cmake_opts['CLANG_TIDY'] = context.env.cxx_compiler.replace("clang++", "clang-tidy")
-
-    # At least hwloc on Jenkins produces a massive amount of reports about
-    # memory leaks, which cannot be reasonably suppressed because ASAN cannot
-    # produce a reasonable stack trace for them.
-    if context.opts.asan:
-        cmake_opts['GMX_HWLOC'] = 'OFF'
-
-    if context.opts.gpubufferops:
-        context.env.set_env_var('GMX_USE_GPU_BUFFER_OPS', "1")
-
-    # GPU comm flag enables both DD and PP-PME comm as well as buffer ops (hard dependency)
-    if context.opts.gpucomm:
-        context.env.set_env_var('GMX_USE_GPU_BUFFER_OPS', "1")
-        context.env.set_env_var('GMX_GPU_DD_COMMS', "1")
-        context.env.set_env_var('GMX_GPU_PME_PP_COMMS', "1")
-
-    # GPU update flag changes the default for '-update auto' to GPU
-    if context.opts.gpuupdate:
-        context.env.set_env_var('GMX_FORCE_UPDATE_DEFAULT_GPU', "1")
-
-    regressiontests_path = context.workspace.get_project_dir(Project.REGRESSIONTESTS)
-
-    if context.job_type == JobType.RELEASE:
-        cmake_opts['REGRESSIONTEST_PATH'] = regressiontests_path
-    else:
-        if context.opts.mdrun_only:
-            cmake_opts['GMX_BUILD_MDRUN_ONLY'] = 'ON'
-
-    # The build configuration has constructed the environment of the
-    # context so that a particular c++ standard library can be used,
-    # which may come from a different installation of gcc. Here, we
-    # tell CMake how to react to this.
-    #
-    # TODO Once gerrit 9051 and 9053 are both submitted on master,
-    # remove the hasattr part of the predicate, which will then be
-    # redundant.
-    if hasattr(context.env, 'gcc_exe') and context.env.gcc_exe is not None:
-        cmake_opts['GMX_GPLUSPLUS_PATH'] = context.env.gcc_exe
-        # TODO are these needed?
-        # gcc_exe_dirname = os.path.dirname(self.gcc_exe)
-        # gcc_toolchain_path = os.path.join(gcc_exe_dirname, '..')
-        # format_for_linker_flags="-Wl,-rpath,{gcctoolchain}/lib64 -L{gcctoolchain}/lib64"
-        # cmake_opts['CMAKE_CXX_LINK_FLAGS'] = format_for_linker_flags.format(gcctoolchain=gcc_toolchain_path)
-
-    context.env.set_env_var('GMX_NO_TERM', '1')
-
-    context.run_cmake(cmake_opts)
-    context.build_target(target=None, keep_going=True)
-
-    # TODO: Consider if it would be better to split this into a separate build
-    # script, since it is somewhat different, even though it benefits from some
-    # of the same build options.
-    if context.job_type == JobType.RELEASE:
-        context.build_target(target='check', keep_going=True)
-        context.build_target(target='install')
-        if context.opts.mdrun_only:
-            context.workspace.clean_build_dir()
-            cmake_opts['REGRESSIONTEST_PATH'] = None
-            cmake_opts['GMX_BUILD_MDRUN_ONLY'] = 'ON'
-            context.run_cmake(cmake_opts)
-            context.build_target(target=None, keep_going=True)
-            context.build_target(target='check', keep_going=True)
-            context.build_target(target='install')
-        gmxrc_cmd = '. ' + os.path.join(context.workspace.install_dir, 'bin', 'GMXRC')
-        context.env.run_env_script(gmxrc_cmd)
-        cmd = [os.path.join(regressiontests_path, 'gmxtest.pl'), '-nosuffix', 'all']
-        if context.opts.mpi:
-            cmd += ['-np', '1']
-        if context.opts.double:
-            cmd += ['-double']
-        if context.opts.mdrun_only:
-            cmd += ['-mdrun', 'mdrun']
-        context.run_cmd(cmd, failure_message='Regression tests failed to execute')
-        # TODO: Add testing for building the template.
-        # TODO: Generalize the machinery here such that it can easily be used
-        # also for non-release builds.
-    else:
-        context.build_target(target='tests', keep_going=True)
-
-        context.run_ctest(args=['--output-on-failure', '--label-exclude', 'SlowTest'], memcheck=context.opts.asan)
-
-        context.build_target(target='install')
-        # TODO: Consider what could be tested about the installed binaries.
-
-        # run OpenCL offline compile tests on clang tidy builds
-        if (context.opts.tidy and context.opts.opencl):
-            context.build_target(target='ocl_nbnxm_kernels')
-
-        if not context.opts.mdrun_only:
-            context.env.prepend_path_env(os.path.join(context.workspace.build_dir, 'bin'))
-            context.chdir(regressiontests_path)
-
-            use_tmpi = not context.opts.mpi and context.opts.thread_mpi is not False
-
-            cmd = 'perl gmxtest.pl -mpirun mpirun -xml -nosuffix all'
-
-            # setting this stuff below is just a temporary solution,
-            # it should all be passed as a proper the runconf from outside
-            # The whole mechanism should be rethought in #1587.
-            if context.opts.phi:
-                cmd += ' -ntomp 28'
-            elif context.opts.openmp:
-                # OpenMP should always work when compiled in! Currently not set if
-                # not explicitly set
-                cmd += ' -ntomp 2'
-
-            if context.opts.gpuhw == Gpuhw.NONE:
-                context.env.set_env_var('GMX_DISABLE_GPU_DETECTION', '1')
-
-            if context.opts.gpu_id:
-                cmd += ' -gpu_id ' + context.opts.gpu_id
-
-            if context.opts.nranks:
-                nranks = context.opts.nranks
-            else:
-                nranks = '2'
-
-            if context.opts.npme:
-                cmd += ' -npme ' + context.opts.npme
-
-            if context.opts.mpi:
-                cmd += ' -np ' + nranks
-            elif use_tmpi:
-                cmd += ' -nt ' + nranks
-            if context.opts.double:
-                cmd += ' -double'
-            if context.opts.asan:
-                context.env.set_env_var('ASAN_OPTIONS', 'detect_leaks=0')
-            context.run_cmd(cmd, shell=True, failure_message='Regression tests failed to execute')
diff --git a/admin/builds/nightly-matrix.txt b/admin/builds/nightly-matrix.txt
deleted file mode 100644 (file)
index 5d4b2a5..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-# Comment line(s) preceding each configuration document the main
-# intent behind that configuration, so that we can correctly judge
-# whether to preserve that during maintenance decisions.
-#
-# Other configurations might coincidentally test such configurations
-# (e.g. because they are the current default), but it is appropriate
-# to intend to teach each feature (or a feature combination) exactly
-# once, and for the intent to be reflected precisely in the
-# configuration syntax, so that the configurations are stable even
-# if the defaults change in future.
-#
-# The phrase "at time of release" refers to either the initial
-# GROMACS release from that branch, or the next expected release
-# from that branch. (We will tend not to retroactively support
-# newer infrastructure in CI, unless it's either easy or of
-# high impact.)
-
-# Test clang on ARM v8
-# Test ARM_NEON SIMD
-clang-3.9 no-openmp simd=ARM_NEON_ASIMD release-with-assert
-
-# Test AVX2_256 SIMD
-# Test icc with SIMD in mixed precision in release mode
-# Test icc with MPI
-icc-19 mpi release simd=avx2_256 no-hwloc
-
-# Test AVX_128_FMA SIMD in mixed precision
-# Test build of downloaded FFTW
-gcc-5 openmp simd=avx_128_fma cmake-3.11.4 buildfftw
-
-# Test with SSE2 + double
-gcc-9 simd=sse2 fftpack double
-
-# Test OpenMP with clang-tidy
-# Test AVX2_256 SIMD with clang-tidy
-# Test OpenCL build with clang-tidy
-# Test offline OpenCL kernel build with clang-tidy
-clang-8 simd=avx2_256 opencl-1.2 cmake-3.14.5 tidy
diff --git a/admin/builds/post-submit-matrix.txt b/admin/builds/post-submit-matrix.txt
deleted file mode 100644 (file)
index afa2fda..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-# Comment line(s) preceding each configuration document the main
-# intent behind that configuration, so that we can correctly judge
-# whether to preserve that during maintenance decisions.
-#
-# Other configurations might coincidentally test such configurations
-# (e.g. because they are the current default), but it is appropriate
-# to intend to teach each feature (or a feature combination) exactly
-# once, and for the intent to be reflected precisely in the
-# configuration syntax, so that the configurations are stable even
-# if the defaults change in future.
-
-# Test on ARM v7
-# Test ARM_NEON SIMD
-gcc-5 simd=ARM_NEON no-hwloc release-with-assert
-
-# Test on ARM v8
-# Test ARM_NEON_ASIMD SIMD
-gcc-5 simd=ARM_NEON_ASIMD release-with-assert
-
-# Test the mdrun-only build
-# Test newest gcc at time of release
-# TODO In combination with gmx from another build, arrange to run regressiontests
-gcc-9 mpi no-openmp fftpack mdrun-only
-
-# Test MPMD PME with thread-MPI
-# Test oldest supported icc
-# Test icc with AVX256 in double precision in release mode
-icc-17 simd=avx_256 npme=1 nranks=2 no-openmp  double fftpack release
-
-# Test CUDA build on a agent with no CUDA devices
-# Test without TNG support
-gcc-7 gpuhw=none cuda-10.0 openmp no-tng release-with-assert
-
-# Test non-default GMX_PREFER_STATIC_LIBS behavior
-# TODO enable this
-# msvc-2017 double no-threadmpi no-prefer-static-libs release-with-assert
-
-# Test newest icc at time of release
-# TODO Reinstate this when SIMD exp works again
-#   Test icc with SIMD in mixed precision in release mode
-icc-19 simd=none mkl release
-
-# Test SSE4.1 SIMD
-# Test single-rank GPU
-# Test clang host and device compilation, with OpenMP and CUDA
-# NOTE: using release-with-assert as debug build trigers some ptx warnings
-clang-8 simd=sse4.1 openmp nranks=1 gpuhw=nvidia cuda-10.0 clang_cuda release-with-assert
-
-# Test MPMD PME with library MPI
-# Test clang + OpenMP
-# Test clang in double precision
-# Test AVX_128_FMA SIMD + Double (Important for Simd4N=Simd4 and sizeof(SimdInt32)!=4*GMX_SIMD_REAL_WIDTH)
-clang-8 double openmp simd=avx_128_fma npme=1 nranks=2 mpi
-
-# Test SSE2 SIMD
-# Test CMAKE_BUILD_TYPE=Release
-# Test OpenCL
-# Test bundled clFFT (developed by AMD) on NVIDIA OpenCL
-gcc-6 npme=1 nranks=2 opencl-1.2 gpuhw=nvidia simd=sse2 release
-
-# Test non-default use of mdrun -gpu_id
-# Test GPU-sharing among 4 PP ranks
-# Test no hwloc build, tests internal CPU topology detection (mainly for x86)
-# Test clang host compilation with CUDA on Linux
-clang-8 gpuhw=nvidia openmp nranks=4 gpu_id=1 cuda-10.1 no-hwloc release-with-assert
-
-# Test ARM HPC compier toolchain with gcc-7
-#gcc-7 armhpc-18.2 openmp simd=ARM_NEON_ASIMD release
-
-# Test ARM HPC compier toolchain with armclang (most recent version at the time if the release)
-# TODO: upgrade to 19.3 before that lands (ref #3011)
-# Test ARMPL for FFTs
-# Test linking against ARMPL for BLAS/LAPACK
-armclang-19.3 armhpc-19.3 armpl openmp simd=ARM_NEON_ASIMD release-with-assert
-
-# TODO
-# Add SIMD + OpenMP + CUDA asan build
-# Add OpenMP + CUDA + device sharing TSAN build
-# Test statically linked hwloc support (if/when it can work well)
-# Test 3D DD (2D is partially covered in regressiontests)
-# Test mdrun -tunepme (e.g. with relaxed tolerances, for now)
-# Consider testing of other CMake option paths
-# Test behaviour when dlopen is not present
diff --git a/admin/builds/pre-submit-matrix.txt b/admin/builds/pre-submit-matrix.txt
deleted file mode 100644 (file)
index 0cfc48e..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-# Comment line(s) preceding each configuration document the main
-# intent behind that configuration, so that we can correctly judge
-# whether to preserve that during maintenance decisions.
-#
-# Other configurations might coincidentally test such configurations
-# (e.g. because they are the current default), but it is appropriate
-# to intend to teach each feature (or a feature combination) exactly
-# once, and for the intent to be reflected precisely in the
-# configuration syntax, so that the configurations are stable even
-# if the defaults change in future.
-#
-# The phrase "at time of release" refers to either the initial
-# GROMACS release from that branch, or the next expected release
-# from that branch. (We will tend not to retroactively support
-# newer infrastructure in CI, unless it's either easy or of
-# high impact.)
-
-# Test oldest supported gcc
-# Test oldest supported CUDA
-# Test oldest supported Ubuntu
-# Test MPI with CUDA
-# Test MPI with gcc
-# Test MPMD PME with library MPI
-gcc-5 gpuhw=nvidia cuda-9.0 cmake-3.9.6 mpi npme=1 nranks=2 openmp
-
-# Test newest cmake at time of release
-# Test non-default use of mdrun -gpu_id
-# Test newest gcc supported by newest CUDA at time of release
-# Test thread-MPI with CUDA
-# Test SIMD implementation of pair search for GPU code-path
-# Test hwloc-2 support
-gcc-8 gpuhw=nvidia gpu_id=1 cuda-10.1 thread-mpi openmp cmake-3.15.1 release-with-assert simd=avx2_256 hwloc libhwloc-2.0.4
-
-# Test with libcxx
-# Test with ThreadSanitizer + OpenMP + SIMD
-clang-7 no-hwloc tsan libcxx-7 openmp cmake-3.12.1 simd=avx2_256
-
-# Test on MacOS (the gcc-8 and/or x11 flags implicitly steer jobs to the OSX agent)
-# Test X11 build
-# Test gcc in double precision
-# Test without TNG support
-# Test 128-bit SIMD in double precision (to cover SimdInt32 support better)
-# Test fftpack fallback
-gcc-8 double x11 no-tng fftpack simd=sse4.1
-
-# Test oldest supported clang (while avoiding AVX codegen issues in it)
-# Test without OpenMP
-# Test thread-MPI
-# Test with TNG support
-clang-3.6 simd=sse2 thread-mpi no-openmp fftpack cmake-3.11.4 tng
-
-# Test newest clang at time of release
-# Test with AddressSanitizer (without OpenMP, see below)
-clang-8 no-openmp asan cmake-3.11.4
-
-# Test with clang-tidy (without OpenMP)
-clang-8 no-openmp cmake-3.12.1 tidy
-
-# Test oldest supported MSVC on Windows
-# Test newest supported MSVC on Windows
-msvc-2017 openmp release-with-assert
-
-# Test newest icc at the time of release
-# Test icc without SIMD in double precision in release mode
-# Test MKL
-# Test without any MPI
-# Test on CentOS (because cmake-3.9.6 is available there)
-icc-19 no-thread-mpi double openmp mkl cmake-3.9.6 simd=none release
-
-# Test oldest supported cmake
-# Test NVIDIA OpenCL
-# Test external clFFT (for build speed)
-# Test MPI + OpenCL
-# Test AVX2_256 SIMD
-# Test without MPI_IN_PLACE
-gcc-6 openmp gpuhw=nvidia opencl-1.2 clFFT-2.14 cmake-3.9.6 mpi no-mpiinplace simd=avx2_256
-
-# Test AMD OpenCL
-# Test external clFFT (for build speed)
-# Test AVX2_256 SIMD
-# Test newest gcc at time of release
-# Test hwloc-1 support
-gcc-9 openmp simd=avx2_256 gpuhw=amd opencl-1.2 clFFT-2.14 hwloc libhwloc-1.11.2 cmake-3.14.5
-
-# TODO
-# Add OpenMP support to ASAN build (but libomp.so in clang-4 reports leaks, so might need a suitable build or suppression)
-# Test newest supported LTS Ubuntu
-# Test AVX-512 when hardware is available
diff --git a/admin/builds/regressiontests-update.py b/admin/builds/regressiontests-update.py
deleted file mode 100644 (file)
index f1dcdaf..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2016,2019, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-import os.path
-
-build_options = ['gcc-5', 'cmake-3.9.6']
-extra_projects = [Project.REGRESSIONTESTS]
-
-def run_build(context, cmake_opts):
-    context.chdir(context.workspace.build_dir)
-    context.run_cmake(cmake_opts)
-    context.build_target(target=None)
-
-    context.chdir(context.workspace.get_project_dir(Project.REGRESSIONTESTS))
-    cmd = ['perl', 'gmxtest.pl', 'all']
-    if cmake_opts['GMX_DOUBLE'] == 'ON':
-        cmd += ['-double']
-    context.run_cmd(cmd, failure_message='Regression tests failed to execute')
-
-def do_build(context):
-    cmake_opts=dict()
-    cmake_opts['CMAKE_BUILD_TYPE'] = 'Reference'
-    context.env.set_env_var('GMX_NO_TERM', '1')
-    context.env.prepend_path_env(os.path.join(context.workspace.build_dir, 'bin'))
-
-    cmake_opts['GMX_DOUBLE'] = 'ON'
-    run_build(context, cmake_opts)
-
-    cmake_opts['GMX_DOUBLE'] = 'OFF'
-    run_build(context, cmake_opts)
-
-    context.workspace.upload_revision(project=Project.REGRESSIONTESTS, file_glob='*reference*')
diff --git a/admin/builds/release-matrix.txt b/admin/builds/release-matrix.txt
deleted file mode 100644 (file)
index 2aaf4dd..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-# Comment line(s) preceding each configuration document the main
-# intent behind that configuration, so that we can correctly judge
-# whether to preserve that during maintenance decisions.
-#
-# Other configurations might coincidentally test such configurations
-# (e.g. because they are the current default), but it is appropriate
-# to intend to teach each feature (or a feature combination) exactly
-# once, and for the intent to be reflected precisely in the
-# configuration syntax, so that the configurations are stable even
-# if the defaults change in future.
-#
-# The phrase "at time of release" refers to either the initial
-# GROMACS release from that branch, or the next expected release
-# from that branch. (We will tend not to retroactively support
-# newer infrastructure in CI, unless it's either easy or of
-# high impact.)
-#
-# These configurations will be used to build and test the tarballs
-# before the releases. These all build in release mode (ie with
-# minimal assertions, as we intend the code to be built and used
-# by end users).
-
-# Test mpi support
-# Test mdrun-only build
-gcc-9 mpi mdrun-only release
-
-# Test static linking with gcc
-# Test newest gcc at time of release
-gcc-9 static release
-
-# Test double precision
-# Test with newest supported Intel compiler
-icc-19 double release
-
-# Test static linking with clang
-# Test double precision
-# Test newest supported clang version
-clang-8 static double release cmake-3.10.0
-
-# Test oldest supported gcc at time of release
-# Test with GPU support
-# Test with newest CUDA at time of release
-gcc-5 gpuhw=nvidia cuda-10.0 release
-
-# Test with OpenCL support
-gcc-8 simd=avx2_256 gpuhw=amd opencl-1.2 release
-
-# TODO items
-# Avoid specifying cmake versions just to move jobs away from bs_nix-amd
diff --git a/admin/builds/uncrustify.py b/admin/builds/uncrustify.py
deleted file mode 100644 (file)
index 2054ef1..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2015,2016,2019, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-def do_build(context):
-    context.env.set_env_var('UNCRUSTIFY', context.env.get_uncrustify_command())
-    uncrustify_log = context.workspace.get_path_for_logfile('uncrustify.log', category='uncrustify')
-    copyright_log  = context.workspace.get_path_for_logfile('copyright.log', category='copyright')
-    cmd = ['admin/uncrustify.sh', 'check', '--rev=HEAD^', '--warnings=' + uncrustify_log]
-    ret = context.run_cmd(cmd, use_return_code=True)
-    if ret == 1:
-        with open(uncrustify_log, 'r') as f:
-            warnings = f.readlines()
-        if len(warnings) <= 5:
-            details = [x.rstrip() for x in warnings]
-        else:
-            uncrustify_count = 0
-            for w in warnings:
-                if 'needs uncrustify' in w:
-                    uncrustify_count += 1
-            details = []
-            if uncrustify_count > 0:
-                details.append('formatting issues in {0} files'.format(uncrustify_count))
-        context.mark_unstable(reason='uncrustify.sh found issues', details=details)
-    elif ret != 0:
-        raise BuildError('uncrustify.sh failed to run')
-
-    cmd = ['admin/copyright.sh', 'check', '--rev=HEAD^', '--warnings=' + copyright_log]
-    ret = context.run_cmd(cmd, use_return_code=True)
-    if ret == 1:
-        with open(copyright_log, 'r') as f:
-            warnings = f.readlines()
-        if len(warnings) <= 5:
-            details = [x.rstrip() for x in warnings]
-        else:
-            cpyear_count = 0
-            cpheader_count = 0
-            for w in warnings:
-                if 'copyright year' in w:
-                    cpyear_count += 1
-                if 'copyright header' in w:
-                    cpheader_count += 1
-            details = []
-            if cpyear_count > 0:
-                details.append('copyright year missing in {0} files'.format(cpyear_count))
-            if cpheader_count > 0:
-                details.append('copyright header issues in {0} files'.format(cpheader_count))
-        context.mark_unstable(reason='copyright.sh found issues', details=details)
-    elif ret != 0:
-        raise BuildError('copyright.sh failed to run')
index 7c97ec3fe48da7c0666b8e6d59ee3d318b19d29a..a48cd4ca19ba70276277bac53b7e25641eab4604 100644 (file)
@@ -36,28 +36,42 @@ popd
 
 # Run Python unit tests.
 python -m pytest $PWD/python_packaging/src/test --junitxml=$PY_UNIT_TEST_XML
+
+# Note: Multiple pytest processes getting --junitxml output file argument
+# may cause problems, so we set the option on only one of the launched processes.
+# See also Multiple Instruction Multiple Data Model for OpenMPI mpirun:
+# https://www.open-mpi.org/doc/v3.0/man1/mpiexec.1.php
+PROGRAM=(`which python` -m mpi4py -m pytest \
+        -p no:cacheprovider \
+        $PWD/python_packaging/src/test)
+# shellcheck disable=SC2068
 if [ -x `which mpiexec` ]; then
     PYTHONDONTWRITEBYTECODE=1 \
     mpiexec --allow-run-as-root \
+      -x OMP_NUM_THREADS=1 \
       --mca opal_warn_on_missing_libcuda 0 \
       --mca orte_base_help_aggregate 0 \
-      -n 2 \
-      `which python` -m pytest \
-        -p no:cacheprovider \
-        $PWD/python_packaging/src/test \
-        --junitxml=$PY_MPI_UNIT_TEST_XML
+      -n 1 ${PROGRAM[@]} --junitxml=$PLUGIN_MPI_TEST_XML : \
+      -n 1 ${PROGRAM[@]}
 fi
 
 # Run Python acceptance tests.
 python -m pytest $PWD/python_packaging/test --junitxml=$PY_ACCEPTANCE_TEST_XML
+
+# Note: Multiple pytest processes getting --junitxml output file argument
+# may cause problems, so we set the option on only one of the launched processes.
+# See also Multiple Instruction Multiple Data Model for OpenMPI mpirun:
+# https://www.open-mpi.org/doc/v3.0/man1/mpiexec.1.php
+PROGRAM=(`which python` -m mpi4py -m pytest \
+        -p no:cacheprovider \
+        $PWD/python_packaging/test)
+# shellcheck disable=SC2068
 if [ -x `which mpiexec` ]; then
     PYTHONDONTWRITEBYTECODE=1 \
     mpiexec --allow-run-as-root \
+      -x OMP_NUM_THREADS=1 \
       --mca opal_warn_on_missing_libcuda 0 \
       --mca orte_base_help_aggregate 0 \
-      -n 2 \
-      `which python` -m pytest \
-        -p no:cacheprovider \
-        $PWD/python_packaging/test \
-        --junitxml=$PY_MPI_ACCEPTANCE_TEST_XML
+      -n 1 ${PROGRAM[@]} --junitxml=$PLUGIN_MPI_TEST_XML : \
+      -n 1 ${PROGRAM[@]}
 fi
index 0162b4f12625cafd5cf64a385a32f01b8db104d9..828538828bd245a78a4ba4ab9fcaa1c48986179a 100644 (file)
@@ -39,31 +39,45 @@ pushd python_packaging/src
 popd
 
 # Run Python unit tests.
-python -m pytest python_packaging/src/test --junitxml=$PY_UNIT_TEST_XML
-# TODO: enable MPI tests
-#if [ -x `which mpiexec` ]; then
-#    PYTHONDONTWRITEBYTECODE=1 \
-#    mpiexec --allow-run-as-root \
-#      --mca opal_warn_on_missing_libcuda 0 \
-#      --mca orte_base_help_aggregate 0 \
-#      -n 2 \
-#      `which python` -m pytest \
-#        -p no:cacheprovider \
-#        $PWD/python_packaging/src/test \
-#        --junitxml=$PY_MPI_UNIT_TEST_XML
-#fi
+python -m pytest python_packaging/src/test --junitxml=$PY_UNIT_TEST_XML --threads=2
+
+# Note: Multiple pytest processes getting --junitxml output file argument
+# may cause problems, so we set the option on only one of the launched processes.
+# See also Multiple Instruction Multiple Data Model for OpenMPI mpirun:
+# https://www.open-mpi.org/doc/v3.0/man1/mpiexec.1.php
+PROGRAM=(`which python` -m mpi4py -m pytest \
+        -p no:cacheprovider \
+        $PWD/python_packaging/src/test \
+        --threads=1)
+# shellcheck disable=SC2068
+if [ -x `which mpiexec` ]; then
+    PYTHONDONTWRITEBYTECODE=1 \
+    mpiexec --allow-run-as-root \
+      -x OMP_NUM_THREADS=1 \
+      --mca opal_warn_on_missing_libcuda 0 \
+      --mca orte_base_help_aggregate 0 \
+      -n 1 ${PROGRAM[@]} --junitxml=$PLUGIN_MPI_TEST_XML : \
+      -n 1 ${PROGRAM[@]}
+fi
 
 # Run Python acceptance tests.
 python -m pytest python_packaging/test --junitxml=$PY_ACCEPTANCE_TEST_XML
-# TODO: enable MPI tests
-#if [ -x `which mpiexec` ]; then
-#    PYTHONDONTWRITEBYTECODE=1 \
-#    mpiexec --allow-run-as-root \
-#      --mca opal_warn_on_missing_libcuda 0 \
-#      --mca orte_base_help_aggregate 0 \
-#      -n 2 \
-#      `which python` -m pytest \
-#        -p no:cacheprovider \
-#        $PWD/python_packaging/test \
-#        --junitxml=$PY_MPI_ACCEPTANCE_TEST_XML
-#fi
+
+# Note: Multiple pytest processes getting --junitxml output file argument
+# may cause problems, so we set the option on only one of the launched processes.
+# See also Multiple Instruction Multiple Data Model for OpenMPI mpirun:
+# https://www.open-mpi.org/doc/v3.0/man1/mpiexec.1.php
+PROGRAM=(`which python` -m mpi4py -m pytest \
+        -p no:cacheprovider \
+        $PWD/python_packaging/test \
+        --threads=1)
+# shellcheck disable=SC2068
+if [ -x `which mpiexec` ]; then
+    PYTHONDONTWRITEBYTECODE=1 \
+    mpiexec --allow-run-as-root \
+      -x OMP_NUM_THREADS=1 \
+      --mca opal_warn_on_missing_libcuda 0 \
+      --mca orte_base_help_aggregate 0 \
+      -n 1 ${PROGRAM[@]} --junitxml=$PLUGIN_MPI_TEST_XML : \
+      -n 1 ${PROGRAM[@]}
+fi
index 41a17769ae8560421edd5745b6b10abf5ea1684d..e82ffe71d924471fbeeaaa4c9e615d14d9b77265 100644 (file)
@@ -40,15 +40,22 @@ pushd python_packaging/sample_restraint
   popd
 
   python -m pytest $PWD/tests --junitxml=$PLUGIN_TEST_XML
+
+  # Note: Multiple pytest processes getting --junitxml output file argument
+  # may cause problems, so we set the option on only one of the launched processes.
+  # See also Multiple Instruction Multiple Data Model for OpenMPI mpirun:
+  # https://www.open-mpi.org/doc/v3.0/man1/mpiexec.1.php
+  PROGRAM=(`which python` -m mpi4py -m pytest \
+          -p no:cacheprovider \
+          $PWD/tests)
+  # shellcheck disable=SC2068
   if [ -x `which mpiexec` ]; then
       PYTHONDONTWRITEBYTECODE=1 \
       mpiexec --allow-run-as-root \
+        -x OMP_NUM_THREADS=1 \
         --mca opal_warn_on_missing_libcuda 0 \
         --mca orte_base_help_aggregate 0 \
-        -n 2 \
-        `which python` -m pytest \
-          -p no:cacheprovider \
-          $PWD/tests \
-          --junitxml=$PLUGIN_MPI_TEST_XML
+        -n 1 ${PROGRAM[@]} --junitxml=$PLUGIN_MPI_TEST_XML : \
+        -n 1 ${PROGRAM[@]}
   fi
 popd
index 45bafbd626d70762f24e1bb108feb4a9f3a8a2b2..d8762577847c93b232e68de053c89157e9262c06 100644 (file)
@@ -65,17 +65,24 @@ pushd python_packaging/sample_restraint
     make install
   popd
 
-  python -m pytest $PWD/tests --junitxml=$PLUGIN_TEST_XML
-# TODO: enable MPI tests
-#  if [ -x `which mpiexec` ]; then
-#      PYTHONDONTWRITEBYTECODE=1 \
-#      mpiexec --allow-run-as-root \
-#        --mca opal_warn_on_missing_libcuda 0 \
-#        --mca orte_base_help_aggregate 0 \
-#        -n 2 \
-#        `which python` -m pytest \
-#          -p no:cacheprovider \
-#          $PWD/tests \
-#          --junitxml=$PLUGIN_MPI_TEST_XML
-#  fi
+  python -m pytest $PWD/tests --junitxml=$PLUGIN_TEST_XML --threads=2
+
+  # Note: Multiple pytest processes getting --junitxml output file argument
+  # may cause problems, so we set the option on only one of the launched processes.
+  # See also Multiple Instruction Multiple Data Model for OpenMPI mpirun:
+  # https://www.open-mpi.org/doc/v3.0/man1/mpiexec.1.php
+  PROGRAM=(`which python` -m mpi4py -m pytest \
+          -p no:cacheprovider \
+          $PWD/tests \
+          --threads=1)
+  # shellcheck disable=SC2068
+  if [ -x `which mpiexec` ]; then
+      PYTHONDONTWRITEBYTECODE=1 \
+      mpiexec --allow-run-as-root \
+        -x OMP_NUM_THREADS=1 \
+        --mca opal_warn_on_missing_libcuda 0 \
+        --mca orte_base_help_aggregate 0 \
+        -n 1 ${PROGRAM[@]} --junitxml=$PLUGIN_MPI_TEST_XML : \
+        -n 1 ${PROGRAM[@]}
+  fi
 popd
index b2a28924dafb97c687feeca2a0e0411f55bb626d..36fc12d9e315dd5ecc9edba0ceb4743b0d22b484 100755 (executable)
@@ -49,7 +49,7 @@ function usage() {
 
 action="check-workdir"
 declare -a diffargs
-baserev="HEAD"
+baserev="origin/master"
 force=
 format_mode=check
 warning_file=
index 5d867cd5a3f61d67ce669261741b516ac67a7915..9668b506e8a0f52e59b4c3d40bb2c3fcded7400e 100755 (executable)
@@ -105,14 +105,14 @@ then
     if [ -z "$RUN_CLANG_TIDY" ]
     then
         echo "Please set the path to run-clang-tidy using the git hook"
-        echo "git config hooks.runclangtidypath /path/to/run-clang-tidy-8.py"
+        echo "git config hooks.runclangtidypath /path/to/run-clang-tidy-9.py"
         echo "or by setting an environment variable, e.g."
-        echo "RUN_CLANG_TIDY=/path/to/run-clang-tidy-8.py"
+        echo "RUN_CLANG_TIDY=/path/to/run-clang-tidy-9.py"
         exit 2
     fi
     if ! which "$RUN_CLANG_TIDY" 1>/dev/null
     then
-        echo "run-clang-tidy-8.py not found: $RUN_CLANG_TIDY"
+        echo "run-clang-tidy-9.py not found: $RUN_CLANG_TIDY"
         exit 2
     fi
 fi
index 1492f0f775e8df87509e397ad5d0722fc4f52c42..b9e20e8505bb754334e57b9eaeb5fec44e1ac9c3 100644 (file)
@@ -9,56 +9,49 @@ SCRIPT=$PWD/scripted_gmx_docker_builds.py
 # images needed, because the same one can test library,
 # thread and no MPI configurations.
 
-tag="gromacs/cmake-3.9.6-gcc-5-cuda-9.0-openmpi:2020"
+tag="gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master"
 tags[${#tags[@]}]=$tag
-python3 $SCRIPT --cmake 3.9.6 --gcc 5 --cuda 9.0 --ubuntu 16.04 --mpi openmpi | docker build -t $tag -
+python3 $SCRIPT --cmake 3.15.7 --gcc 8 --cuda 11.0 --opencl --clfft --mpi openmpi \
+| docker build -t $tag -
 
-tag="gromacs/cmake-3.9.6-gcc-6-cuda-10.1-nvidiaopencl-clfft-openmpi:2020"
+tag="gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master"
 tags[${#tags[@]}]=$tag
-python3 $SCRIPT --cmake 3.9.6 --gcc 6 --cuda 10.1 --opencl --clfft --mpi openmpi | docker build -t $tag -
+python3 $SCRIPT --cmake 3.13.0 --gcc 7 --opencl amd --clfft --mpi openmpi --ubuntu 18.04 | docker build -t $tag -
 
-tag="gromacs/cmake-3.9.6-gcc-7-amdopencl-clfft-openmpi:2020"
+tag="gromacs/cmake-3.13.0-llvm-8-tsan-master"
 tags[${#tags[@]}]=$tag
-python3 $SCRIPT --cmake 3.9.6 --gcc 7 --opencl amd --clfft --mpi openmpi | docker build -t $tag -
+python3 $SCRIPT --cmake 3.13.0 --llvm 8 --tsan | docker build -t $tag -
 
-tag="gromacs/cmake-3.15.7-gcc-8-cuda-10.1-openmpi:2020"
+tag="gromacs/cmake-3.15.7-llvm-8-cuda-10.0-openmpi-master"
 tags[${#tags[@]}]=$tag
-python3 $SCRIPT --cmake 3.15.7 --gcc 8 --cuda 10.1 --mpi openmpi | docker build -t $tag -
+python3 $SCRIPT --cmake 3.15.7 --llvm 8 --cuda 10.0 --mpi openmpi | docker build -t $tag -
 
-tag="gromacs/cmake-3.9.6-gcc-9-cuda-10.0-openmpi:2020"
+tag="gromacs/cmake-3.15.7-llvm-8-cuda-11.0-openmpi-master"
 tags[${#tags[@]}]=$tag
-python3 $SCRIPT --cmake 3.9.6 --gcc 9 --cuda 10.0 --mpi openmpi | docker build -t $tag -
+python3 $SCRIPT --cmake 3.15.7 --llvm 8 --cuda 11.0 --mpi openmpi | docker build -t $tag -
 
-tag="gromacs/cmake-3.11.4-llvm-8-openmpi:2020"
+tag="gromacs/cmake-3.15.7-llvm-9-openmpi-master"
 tags[${#tags[@]}]=$tag
-python3 $SCRIPT --cmake 3.11.4 --llvm 8 --mpi openmpi | docker build -t $tag -
+python3 $SCRIPT --cmake 3.15.7 --llvm 9 --mpi openmpi | docker build -t $tag -
 
-tag="gromacs/cmake-3.15.7-llvm-8-tsan:2020"
+tag="gromacs/cmake-3.13.0-llvm-9-intelopencl-openmpi-master"
 tags[${#tags[@]}]=$tag
-python3 $SCRIPT --cmake 3.15.7 --llvm 8 --tsan | docker build -t $tag -
+python3 $SCRIPT --cmake 3.13.0 --llvm 9 --opencl intel --mpi openmpi | docker build -t $tag -
 
-tag="gromacs/cmake-3.15.7-llvm-8-cuda-10.1-openmpi:2020"
+tag="gromacs/cmake-3.13.0-llvm-9-amdopencl-openmpi-master"
 tags[${#tags[@]}]=$tag
-python3 $SCRIPT --cmake 3.15.7 --llvm 8 --cuda 10.1 --mpi openmpi | docker build -t $tag -
+python3 $SCRIPT --cmake 3.13.0 --llvm 9 --opencl amd --mpi openmpi --ubuntu 18.04 | docker build -t $tag -
 
-tag="gromacs/cmake-3.15.7-llvm-8-intelopencl-openmpi:2020"
+tag="gromacs/cmake-3.17.2-oneapi-2021.1-beta08-master"
 tags[${#tags[@]}]=$tag
-python3 $SCRIPT --cmake 3.15.7 --llvm 8 --opencl intel --mpi openmpi | docker build -t $tag -
+python3 $SCRIPT --cmake 3.17.2 --oneapi 2021.1-beta08 | docker build -t $tag -
 
-tag="gromacs/cmake-3.9.6-llvm-3.6-amdopencl-openmpi:2020"
+tag="gromacs/ci-docs-llvm-master"
 tags[${#tags[@]}]=$tag
-python3 $SCRIPT --ubuntu 16.04 --cmake 3.9.6 --llvm 3.6 --opencl amd --mpi openmpi | docker build -t $tag -
+python3 $SCRIPT --cmake 3.17.2 --llvm --doxygen | docker build -t $tag -
 
-tag=gromacs/ci-docs-llvm:2020
-tags[${#tags[@]}]=$tag
-python3 $SCRIPT --llvm --doxygen | docker build -t $tag -
-
-tag=gromacs/ci-docs-gcc:2020
-tags[${#tags[@]}]=$tag
-python3 $SCRIPT --gcc --doxygen | docker build -t $tag -
-
-docker login
+echo "Run the following to upload the updated images."
+echo "docker login"
 for tag in "${tags[@]}"; do
-  echo "Pushing $tag"
-  #docker push $tag
+  echo "docker push $tag"
 done
index 1efd05b5dca874499c26c136c0d9924f308d77a9..af4aad09dad16502cebae9f356acd78295bbd344 100644 (file)
@@ -48,6 +48,7 @@ Authors:
     * Paul Bauer <paul.bauer.q@gmail.com>
     * Eric Irrgang <ericirrgang@gmail.com>
     * Joe Jordan <e.jjordan12@gmail.com>
+    * Mark Abraham <mark.j.abraham@gmail.com>
 
 Usage::
 
@@ -126,6 +127,7 @@ _docs_extra_packages = ['autoconf',
                         'linkchecker',
                         'mscgen',
                         'm4',
+                        'openssh-client',
                         'texinfo',
                         'texlive-latex-base',
                         'texlive-latex-extra',
@@ -133,8 +135,7 @@ _docs_extra_packages = ['autoconf',
                         'texlive-fonts-extra']
 
 # Supported Python versions for maintained branches.
-# TODO: Remove '3.5.9' from defaults in master once script in release-2020 diverges.
-_python_versions = ['3.5.9', '3.6.10', '3.7.7', '3.8.2']
+_python_versions = ['3.6.10', '3.7.7', '3.8.2']
 
 # Parse command line arguments
 parser = argparse.ArgumentParser(description='GROMACS CI image creation script', parents=[utility.parser])
@@ -173,13 +174,14 @@ def get_llvm_packages(args) -> typing.Iterable[str]:
     # If we use the package version of LLVM, we need to install extra packages for it.
     if (args.llvm is not None) and (args.tsan is None):
         return ['libomp-dev',
+                'libomp5',
                 'clang-format-' + str(args.llvm),
                 'clang-tidy-' + str(args.llvm)]
     else:
         return []
 
 
-def get_compiler(args, tsan_stage: hpccm.Stage = None) -> bb_base:
+def get_compiler(args, compiler_build_stage: hpccm.Stage = None) -> bb_base:
     # Compiler
     if args.icc is not None:
         raise RuntimeError('Intel compiler toolchain recipe not implemented yet')
@@ -187,15 +189,27 @@ def get_compiler(args, tsan_stage: hpccm.Stage = None) -> bb_base:
     if args.llvm is not None:
         # Build our own version instead to get TSAN + OMP
         if args.tsan is not None:
-            if tsan_stage is not None:
-                compiler = tsan_stage.runtime(_from='tsan')
+            if compiler_build_stage is not None:
+                compiler = compiler_build_stage.runtime(_from='tsan')
             else:
-                raise RuntimeError('No TSAN stage!')
+                raise RuntimeError('No TSAN compiler build stage!')
         # Build the default compiler if we don't need special support
         else:
             compiler = hpccm.building_blocks.llvm(extra_repository=True, version=args.llvm)
 
-    elif (args.gcc is not None):
+    elif args.oneapi is not None:
+        if compiler_build_stage is not None:
+            compiler = compiler_build_stage.runtime(_from='oneapi')
+            # Prepare the toolchain (needed only for builds done within the Dockerfile, e.g.
+            # OpenMPI builds, which don't currently work for other reasons)
+            oneapi_toolchain = hpccm.toolchain(CC='/opt/intel/oneapi/compiler/latest/linux/bin/intel64/icc',
+                                               CXX='/opt/intel/oneapi/compiler/latest/linux/bin/intel64/icpc')
+            setattr(compiler, 'toolchain', oneapi_toolchain)
+
+        else:
+            raise RuntimeError('No oneAPI compiler build stage!')
+
+    elif args.gcc is not None:
         compiler = hpccm.building_blocks.gnu(extra_repository=True,
                                              version=args.gcc,
                                              fortran=False)
@@ -213,11 +227,19 @@ def get_mpi(args, compiler):
                 use_cuda = True
 
             if hasattr(compiler, 'toolchain'):
+                if args.oneapi is not None:
+                    raise RuntimeError('oneAPI building OpenMPI is not supported')
                 return hpccm.building_blocks.openmpi(toolchain=compiler.toolchain, cuda=use_cuda, infiniband=False)
             else:
                 raise RuntimeError('compiler is not an HPCCM compiler building block!')
 
         elif args.mpi == 'impi':
+            # TODO Intel MPI from the oneAPI repo is not working reliably,
+            # reasons are unclear. When solved, add packagages called:
+            # 'intel-oneapi-mpi', 'intel-oneapi-mpi-devel'
+            # during the compiler stage.
+            # TODO also consider hpccm's intel_mpi package if that doesn't need
+            # a license to run.
             raise RuntimeError('Intel MPI recipe not implemented yet.')
         else:
             raise RuntimeError('Requested unknown MPI implementation.')
@@ -235,6 +257,8 @@ def get_opencl(args):
             return hpccm.building_blocks.packages(ospackages=['nvidia-opencl-dev'])
 
         elif args.opencl == 'intel':
+            # Note, when using oneapi, there is bundled OpenCL support, so this
+            # installation is not needed.
             return hpccm.building_blocks.packages(
                     apt_ppas=['ppa:intel-opencl/intel-opencl'],
                     ospackages=['opencl-headers', 'ocl-icd-libopencl1',
@@ -246,7 +270,7 @@ def get_opencl(args):
             return hpccm.building_blocks.packages(
                     apt_keys=['http://repo.radeon.com/rocm/apt/debian/rocm.gpg.key'],
                     apt_repositories=['deb [arch=amd64] http://repo.radeon.com/rocm/apt/debian/ xenial main'],
-                    ospackages=['ocl-icd-libopencl1', 'ocl-icd-opencl-dev', 'opencl-headers', 'libelf1', 'rocm-opencl'])
+                    ospackages=['ocl-icd-libopencl1', 'ocl-icd-opencl-dev', 'opencl-headers', 'libelf1', 'rocm-opencl', 'rocm-dev', 'clinfo'])
     else:
         return None
 
@@ -260,7 +284,7 @@ def get_clfft(args):
         return None
 
 
-def add_tsan_stage(input_args, output_stages: typing.Mapping[str, hpccm.Stage]):
+def add_tsan_compiler_build_stage(input_args, output_stages: typing.Mapping[str, hpccm.Stage]):
     """Isolate the expensive TSAN preparation stage.
 
     This is a very expensive stage, but has few and disjoint dependencies, and
@@ -295,13 +319,50 @@ def add_tsan_stage(input_args, output_stages: typing.Mapping[str, hpccm.Stage]):
                      'ln -s /usr/local/bin/clang-format /usr/local/bin/clang-format-' + str(input_args.llvm),
                      'ln -s /usr/local/bin/clang-tidy /usr/local/bin/clang-tidy-' + str(input_args.llvm),
                      'ln -s /usr/local/libexec/c++-analyzer /usr/local/bin/c++-analyzer-' + str(input_args.llvm)])
-    output_stages['tsan'] = tsan_stage
+    output_stages['compiler_build'] = tsan_stage
+
+def oneapi_runtime(_from='0'):
+    oneapi_runtime_stage = hpccm.Stage()
+    oneapi_runtime_stage += hpccm.primitives.copy(_from='oneapi-build',
+                                                  files={"/opt/intel": "/opt/intel",
+                                                         "/etc/bash.bashrc": "/etc/bash.bashrc"})
+    return oneapi_runtime_stage
+
+def add_oneapi_compiler_build_stage(input_args, output_stages: typing.Mapping[str, hpccm.Stage]):
+    """Isolate the oneAPI preparation stage.
 
+    This stage is isolated so that its installed components are minimized in the
+    final image (chiefly /opt/intel) and its environment setup script can be
+    sourced. This also helps with rebuild time and final image size.
+
+    Note that the ICC compiler inside oneAPI on linux also needs
+    gcc to build other components and provide libstdc++.
+    """
+    if not isinstance(output_stages, collections.abc.MutableMapping):
+        raise RuntimeError('Need output_stages container.')
+    oneapi_stage = hpccm.Stage()
+    oneapi_stage += hpccm.primitives.baseimage(image=base_image_tag(input_args), _as='oneapi-build')
+
+    # Add required components for the next stage (both for hpccm and Intel's setvars.sh script)
+    oneapi_stage += hpccm.building_blocks.packages(ospackages=['wget', 'gnupg2', 'ca-certificates', 'lsb-release'])
+    oneapi_stage += hpccm.building_blocks.packages(
+        apt_keys=['https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB'],
+        apt_repositories=['deb https://apt.repos.intel.com/oneapi all main'],
+        # Add minimal packages (not the whole HPC toolkit!)
+        ospackages=['intel-oneapi-dpcpp-compiler', 'intel-oneapi-icc', 'intel-oneapi-mkl', 'intel-oneapi-mkl-devel']
+    )
+    # Ensure that all bash shells on the final container will have access to oneAPI
+    oneapi_stage += hpccm.primitives.shell(
+            commands=['echo "source /opt/intel/oneapi/setvars.sh" >> /etc/bash.bashrc']
+            )
+    setattr(oneapi_stage, 'runtime', oneapi_runtime)
+
+    output_stages['compiler_build'] = oneapi_stage
 
 def prepare_venv(version: StrictVersion) -> typing.Sequence[str]:
     """Get shell commands to set up the venv for the requested Python version."""
     major = version.version[0]
-    minor = version.version[1]
+    minor = version.version[1]  # type: int
 
     pyenv = '$HOME/.pyenv/bin/pyenv'
 
@@ -320,15 +381,20 @@ def prepare_venv(version: StrictVersion) -> typing.Sequence[str]:
     # WARNING: Please keep this list synchronized with python_packaging/requirements-test.txt
     # TODO: Get requirements.txt from an input argument.
     commands.append("""{path}/bin/python -m pip install --upgrade \
-            'cmake>=3.9.6' \
+            'cmake>=3.13' \
             'flake8>=3.7.7' \
-            'mpi4py>=2' \
+            'mpi4py>=3.0.3' \
             'networkx>=2.0' \
             'numpy>=1' \
             'pip>=10.1' \
             'pytest>=3.9' \
-            'setuptools>=28.0.0' \
-            'scikit-build>=0.7'""".format(path=venv_path))
+            'setuptools>=42' \
+            'scikit-build>=0.10'""".format(path=venv_path))
+
+    # TODO: Remove 'importlib_resources' dependency when Python >=3.7 is required.
+    if minor == 6:
+        commands.append("""{path}/bin/python -m pip install --upgrade \
+                'importlib_resources'""".format(path=venv_path))
 
     return commands
 
@@ -403,6 +469,47 @@ def add_python_stages(building_blocks: typing.Mapping[str, bb_base],
     output_stages['pyenv'] = pyenv_stage
 
 
+def add_documentation_dependencies(input_args,
+                                   output_stages: typing.MutableMapping[str, hpccm.Stage]):
+    """Add appropriate layers according to doxygen input arguments."""
+    if input_args.doxygen is None:
+        return
+    output_stages['main'] += hpccm.primitives.shell(
+        commands=['sed -i \'/\"XPS\"/d;/\"PDF\"/d;/\"PS\"/d;/\"EPS\"/d;/disable ghostscript format types/d\' /etc/ImageMagick-6/policy.xml'])
+    output_stages['main'] += hpccm.building_blocks.pip(pip='pip3', packages=['sphinx==1.6.1', 'gcovr'])
+    if input_args.doxygen == '1.8.5':
+        doxygen_commit = 'ed4ed873ab0e7f15116e2052119a6729d4589f7a'
+        output_stages['main'] += hpccm.building_blocks.generic_autotools(
+            repository='https://github.com/westes/flex.git',
+            commit='f7788a9a0ecccdc953ed12043ccb59ca25714018',
+            prefix='/tmp/install-of-flex',
+            configure_opts=['--disable-shared'],
+            preconfigure=['./autogen.sh'])
+        output_stages['main'] += hpccm.building_blocks.generic_autotools(
+            repository='https://github.com/doxygen/doxygen.git',
+            commit=doxygen_commit,
+            prefix='',
+            configure_opts=[
+                '--flex /tmp/install-of-flex/bin/flex',
+                '--static'])
+    else:
+        version = input_args.doxygen
+        archive_name = 'doxygen-{}.linux.bin.tar.gz'.format(version)
+        archive_url = 'https://sourceforge.net/projects/doxygen/files/rel-{}/{}'.format(
+            version,
+            archive_name
+        )
+        binary_path = 'doxygen-{}/bin/doxygen'.format(version)
+        commands = [
+            'mkdir doxygen && cd doxygen',
+            'wget {}'.format(archive_url),
+            'tar xf {} {}'.format(archive_name, binary_path),
+            'cp {} /usr/local/bin/'.format(binary_path),
+            'cd .. && rm -rf doxygen'
+        ]
+        output_stages['main'] += hpccm.primitives.shell(commands=commands)
+
+
 def build_stages(args) -> typing.Iterable[hpccm.Stage]:
     """Define and sequence the stages for the recipe corresponding to *args*."""
 
@@ -415,22 +522,28 @@ def build_stages(args) -> typing.Iterable[hpccm.Stage]:
     # object early in this function.
     stages = collections.OrderedDict()
 
-    # If we need the TSAN compilers, the early build is more involved.
+    # If we need TSAN or oneAPI support the early build is more complex,
+    # so that our compiler images don't have all the cruft needed to get those things
+    # installed.
     if args.llvm is not None and args.tsan is not None:
-        add_tsan_stage(input_args=args, output_stages=stages)
+        add_tsan_compiler_build_stage(input_args=args, output_stages=stages)
+    if args.oneapi is not None:
+        add_oneapi_compiler_build_stage(input_args=args, output_stages=stages)
 
     # Building blocks are chunks of container-builder instructions that can be
     # copied to any build stage with the addition operator.
     building_blocks = collections.OrderedDict()
 
     # These are the most expensive and most reusable layers, so we put them first.
-    building_blocks['compiler'] = get_compiler(args, tsan_stage=stages.get('tsan'))
+    building_blocks['compiler'] = get_compiler(args, compiler_build_stage=stages.get('compiler_build'))
     building_blocks['mpi'] = get_mpi(args, building_blocks['compiler'])
 
     # Install additional packages early in the build to optimize Docker build layer cache.
     os_packages = _common_packages + get_llvm_packages(args)
     if args.doxygen is not None:
         os_packages += _docs_extra_packages
+    if args.oneapi is not None:
+        os_packages += ['lsb-release']
     building_blocks['ospackages'] = hpccm.building_blocks.packages(ospackages=os_packages)
 
     building_blocks['cmake'] = hpccm.building_blocks.cmake(eula=True, version=args.cmake)
@@ -456,27 +569,8 @@ def build_stages(args) -> typing.Iterable[hpccm.Stage]:
                                                 packages=['pytest', 'networkx', 'numpy'])
 
     # Add documentation requirements (doxygen and sphinx + misc).
-    if (args.doxygen is not None):
-        if (args.doxygen == '1.8.5'):
-            doxygen_commit = 'ed4ed873ab0e7f15116e2052119a6729d4589f7a'
-        else:
-            doxygen_commit = 'a6d4f4df45febe588c38de37641513fd576b998f'
-        stages['main'] += hpccm.building_blocks.generic_autotools(
-            repository='https://github.com/westes/flex.git',
-            commit='f7788a9a0ecccdc953ed12043ccb59ca25714018',
-            prefix='/tmp/install-of-flex',
-            configure_opts=['--disable-shared'],
-            preconfigure=['./autogen.sh'])
-        stages['main'] += hpccm.building_blocks.generic_autotools(
-            repository='https://github.com/doxygen/doxygen.git',
-            commit=doxygen_commit,
-            prefix='',
-            configure_opts=[
-                '--flex /tmp/install-of-flex/bin/flex',
-                '--static'],
-            postinstall=[
-                'sed -i \'/\"XPS\"/d;/\"PDF\"/d;/\"PS\"/d;/\"EPS\"/d;/disable ghostscript format types/d\' /etc/ImageMagick-6/policy.xml'])
-        stages['main'] += hpccm.building_blocks.pip(pip='pip3', packages=['sphinx==1.6.1'])
+    if args.doxygen is not None:
+        add_documentation_dependencies(args, stages)
 
     if 'pyenv' in stages and stages['pyenv'] is not None:
         stages['main'] += hpccm.primitives.copy(_from='pyenv', _mkdir=True, src=['/root/.pyenv/'],
@@ -488,6 +582,11 @@ def build_stages(args) -> typing.Iterable[hpccm.Stage]:
         # stages['main'] += hpccm.primitives.copy(_from='pyenv', src=['/root/.bashrc'],
         #                                         dest='/root/')
 
+    # Make sure that `python` resolves to something.
+    stages['main'] += hpccm.primitives.shell(commands=['test -x /usr/bin/python || '
+                                                       'update-alternatives --install /usr/bin/python python /usr/bin/python3 1 && '
+                                                       '/usr/bin/python --version'])
+
     # Note that the list of stages should be sorted in dependency order.
     for build_stage in stages.values():
         if build_stage is not None:
index e413a19be0c7f1fb661ddf9cd54f33dfd8b62e7b..edd65dfac5130b8192faeb6b0a84764be5b51c9c 100644 (file)
@@ -40,6 +40,7 @@ Authors:
     * Paul Bauer <paul.bauer.q@gmail.com>
     * Eric Irrgang <ericirrgang@gmail.com>
     * Joe Jordan <e.jjordan12@gmail.com>
+    * Mark Abraham <mark.j.abraham@gmail.com>
 
 """
 
@@ -57,52 +58,45 @@ parsers for tools.
     Instead, inherit from it with the *parents* argument to :py:class:`argparse.ArgumentParser`
 """
 
-# TODO: Try using distutils.version.StrictVersion.
-parser.add_argument('--cmake', type=str, default='3.9.6',
-                    choices=['3.9.6', '3.11.4', '3.15.7'],
+parser.add_argument('--cmake', type=str, default='3.13.0',
                     help='Selection of CMake version to provide to base image')
 compiler_group = parser.add_mutually_exclusive_group()
 compiler_group.add_argument('--gcc', type=int, nargs='?', const=7, default=7,
-                            choices=[5, 6, 7, 8, 9],
                             help='Select GNU compiler tool chain. (Default) '
                                  'Some checking is implemented to avoid incompatible combinations')
 compiler_group.add_argument('--llvm', type=str, nargs='?', const='7', default=None,
-                            choices=['3.6', '6', '7', '8'],
                             help='Select LLVM compiler tool chain. '
                                  'Some checking is implemented to avoid incompatible combinations')
 compiler_group.add_argument('--icc', type=int, nargs='?', const=19, default=None,
-                            choices=[19],
                             help='Select Intel compiler tool chain. '
                                  'Some checking is implemented to avoid incompatible combinations')
+# TODO currently the installation merely gets the latest beta version of oneAPI,
+# not a specific version. GROMACS probably doesn't need to address that until
+# oneAPI makes an official release.
+compiler_group.add_argument('--oneapi', type=str, nargs='?', const="2021.1-beta08", default=None,
+                            help='Select Intel oneAPI package version.')
 
 linux_group = parser.add_mutually_exclusive_group()
+# Ubuntu 20+ is not yet tested. See issue #3680
 linux_group.add_argument('--ubuntu', type=str, nargs='?', const='18.04', default='18.04',
-                         choices=['16.04', '18.04'],
                          help='Select Ubuntu Linux base image. (default: ubuntu 18.04)')
 linux_group.add_argument('--centos', type=str, nargs='?', const='7', default=None,
-                         choices=['6', '7'],
                          help='Select Centos Linux base image.')
 
 parser.add_argument('--cuda', type=str, nargs='?', const='10.2', default=None,
-                    choices=['9.0', '10.0', '10.1', '10.2'],
                     help='Select a CUDA version for a base Linux image from NVIDIA.')
 
 parser.add_argument('--mpi', type=str, nargs='?', const='openmpi', default=None,
-                    choices=['openmpi', 'impi'],
                     help='Enable MPI (default disabled) and optionally select distribution (default: openmpi)')
 
 parser.add_argument('--tsan', type=str, nargs='?', const='llvm', default=None,
-                    choices=['llvm'],
                     help='Build special compiler versions with TSAN OpenMP support')
 
 parser.add_argument('--opencl', type=str, nargs='?', const='nvidia', default=None,
-                    choices=['nvidia', 'intel', 'amd'],
                     help='Provide environment for OpenCL builds')
 
 parser.add_argument('--clfft', type=str, nargs='?', const='master', default=None,
-                    choices=['master', 'develop'],
                     help='Add external clFFT libraries to the build image')
 
 parser.add_argument('--doxygen', type=str, nargs='?', const='1.8.5', default=None,
-                    choices=['1.8.5', '1.8.11'],
                     help='Add doxygen environment for documentation builds. Also adds other requirements needed for final docs images.')
index 4923fdab9a958c2d972626af80b23711f0e3960f..e54d602851034a93132373b1ebb92738d7e8d610 100755 (executable)
@@ -2,7 +2,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013,2014,2015,2016,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index d66e8b11468967d9aeb1af7ce80bfe8df0bec492..e6e2e967b3efa00b93d24b0b461fabcdf5cbbcff 100755 (executable)
@@ -2,7 +2,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -50,7 +50,7 @@ function usage() {
 
 action="check-workdir"
 declare -a diffargs
-baserev="HEAD"
+baserev="origin/master"
 force=
 copyright_mode=update
 warning_file=
diff --git a/admin/dockerfiles/base/Dockerfile b/admin/dockerfiles/base/Dockerfile
deleted file mode 100644 (file)
index 25f8a63..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-# Make an image that has the basic dependencies for building GROMACS.
-# This is the same for all other build images and gets used by those.
-
-# Some optional GROMACS dependencies are obtained from the
-# distribution, e.g.  fftw3, hwloc, blas and lapack so that the build
-# is as fast as possible.
-FROM ubuntu:18.04
-ENV DEBIAN_FRONTEND=noninteractive
-WORKDIR /tmp
-RUN \
-  apt-get update && \
-  apt-get -y -q=2 --no-install-suggests --no-install-recommends install \
-    build-essential \
-    ccache \
-    cmake \
-    git \
-    libfftw3-dev \
-    libhwloc-dev \
-    liblapack-dev \
-    moreutils \
-    ninja-build \
-    python3-pip \
-    rsync \
-    wget \
-    xsltproc \
-    && \
-  rm -rf /var/lib/apt/lists/* && \
-  rm -rf /var/cache/apt/archives/*
diff --git a/admin/dockerfiles/buildall.sh b/admin/dockerfiles/buildall.sh
deleted file mode 100644 (file)
index b713aa6..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/bash
-
-export TARGET=$0
-export TARGET_VERSION=$1
-export MATRIX="$TARGET-$TARGET_VERSION"
-export SLUG="ci-$MATRIX"
-
-docker login
-
-tags[0]=gromacs/base:2020
-docker pull ${tags[0]} || true
-docker build -t ${tags[0]} --cache-from ${tags[0]} base
-
-tool=clang
-for tool_version in 6 7 8; do
-  MATRIX="$tool-$tool_version"
-  SLUG="ci-$MATRIX"
-  tag=gromacs/$SLUG:2020
-  tags[${#tags[@]}]=$tag
-  docker build \
-    -t $tag \
-    --build-arg TOOL_VERSION=$tool_version \
-    ci-$tool
-done
-
-tool=gcc
-for tool_version in 5 6 7 8; do
-  MATRIX="$tool-$tool_version"
-  SLUG="ci-$MATRIX"
-  tag=gromacs/$SLUG:2020
-  tags[${#tags[@]}]=$tag
-  docker build \
-    -t $tag \
-    --build-arg TOOL_VERSION=$tool_version \
-    ci-$tool
-done
-
-tag=gromacs/ci-docs-clang:2020
-tags[${#tags[@]}]=$tag
-docker build -t $tag \
-             ci-docs-clang
-
-tag=gromacs/ci-docs-gcc:2020
-tags[${#tags[@]}]=$tag
-docker build -t $tag \
-             ci-docs-gcc
-
-for tag in ${tags[@]}; do
-  docker push $tag
-done
diff --git a/admin/dockerfiles/ci-clang/Dockerfile b/admin/dockerfiles/ci-clang/Dockerfile
deleted file mode 100644 (file)
index 3f9a726..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-# Make an image that has the dependencies for building GROMACS with clang.
-# Note when specifying TOOL_VERSION that clang 6.0 packages use the minor version
-# in the name, while 7 and 8 do not.
-FROM gromacs/base:2020
-WORKDIR /tmp
-ARG TOOL_VERSION
-RUN \
-  apt-get update && \
-  apt_version=$TOOL_VERSION && \
-  if [ "$TOOL_VERSION" -lt "7" ] ; then apt_version="$apt_version.0"; fi && \
-  apt-get -y -q=2 --no-install-suggests --no-install-recommends install \
-    apt-utils \
-    software-properties-common \
-    gpg-agent && \
-  wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \
-  apt-add-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-$apt_version main" && \
-  apt-get -qq update && \
-  apt-get -qqy --no-install-suggests --no-install-recommends install \
-    clang++-$apt_version \
-    clang-tools-$apt_version \
-    libomp-dev && \
-  if [ "$apt_version" != "$TOOL_VERSION" ] ; then \
-    ln -s /usr/bin/clang-$apt_version /usr/bin/clang-$TOOL_VERSION; \
-    ln -s /usr/bin/clang++-$apt_version /usr/bin/clang++-$TOOL_VERSION; \
-    ln -s /usr/bin/clang-format-$apt_version /usr/bin/clang-format-$TOOL_VERSION; \
-    ln -s /usr/bin/clang-tidy-$apt_version /usr/bin/clang-tidy-$TOOL_VERSION; fi && \
-  rm -rf /var/lib/apt/lists/* && \
-  rm -rf /var/cache/apt/archives/*
diff --git a/admin/dockerfiles/ci-docs-clang/Dockerfile b/admin/dockerfiles/ci-docs-clang/Dockerfile
deleted file mode 100644 (file)
index 7488e7a..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-# Make an image that has the dependencies for building GROMACS documentation.
-
-# Make an intermediate image that can build a static Doxygen 1.8.5 that other
-# containers will be able to use.
-
-FROM ubuntu:18.04 as doxygen-builder
-ENV DEBIAN_FRONTEND=noninteractive
-WORKDIR /tmp
-RUN \
-  apt-get update && \
-  apt-get -y -q=2 --no-install-suggests --no-install-recommends install \
-      bison \
-      build-essential \
-      gcc \
-      m4 \
-      wget \
-  && \
-  wget --no-check-certificate https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/flex/2.5.35-10ubuntu3/flex_2.5.35.orig.tar.gz && \
-  tar xf flex_2.5.35.orig.tar.gz && \
-  cd flex-2.5.35 && \
-  ./configure --prefix=/tmp/install-of-flex --disable-shared && \
-  make -j && make install && cd .. && rm -rf flex* && \
-  wget --no-check-certificate https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/doxygen/1.8.5-1/doxygen_1.8.5.orig.tar.gz && \
-  tar xf doxygen_1.8.5.orig.tar.gz && \
-  cd doxygen-1.8.5 && \
-  ./configure --flex /tmp/install-of-flex/bin/flex --static && \
-  make -j && make install && cd .. && rm -rf doxygen* && \
-  rm -rf /var/lib/apt/lists/*
-
-# The ImageMagick package from apt has highly secure settings by
-# default, suitable for use behind a webserver, which we don't
-# need. So we use sed to remove those.
-# We also install it separatly because it pulls in some dependencies
-# that are needed for the documentation build.
-
-FROM gromacs/ci-clang-7:2020
-WORKDIR /tmp
-COPY --from=doxygen-builder /usr/local/bin/* /usr/local/bin/
-RUN \
-  apt-get update && \
-  apt-get -y -q=2 --no-install-suggests --no-install-recommends install \
-    graphviz \
-    linkchecker \
-    mscgen \
-    texlive-latex-base \
-    texlive-latex-extra \
-    texlive-fonts-recommended \
-    texlive-fonts-extra && \
-  apt-get -y install imagemagick && \
-  rm -rf /var/lib/apt/lists/*
-RUN \
-  sed -i \
-    '/\"XPS\"/d;/\"PDF\"/d;/\"PS\"/d;/\"EPS\"/d;/disable ghostscript format types/d' \
-    /etc/ImageMagick-6/policy.xml && \
-  pip3 install sphinx==1.6.1
diff --git a/admin/dockerfiles/ci-docs-gcc/Dockerfile b/admin/dockerfiles/ci-docs-gcc/Dockerfile
deleted file mode 100644 (file)
index cc594c9..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-# Make an image that has the dependencies for building GROMACS documentation.
-
-# Make an intermediate image that can build a static Doxygen 1.8.5 that other
-# containers will be able to use.
-
-FROM ubuntu:18.04 as doxygen-builder
-ENV DEBIAN_FRONTEND=noninteractive
-WORKDIR /tmp
-RUN \
-  apt-get update && \
-  apt-get -y -q=2 --no-install-suggests --no-install-recommends install \
-      bison \
-      build-essential \
-      gcc \
-      m4 \
-      wget \
-  && \
-  wget --no-check-certificate https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/flex/2.5.35-10ubuntu3/flex_2.5.35.orig.tar.gz && \
-  tar xf flex_2.5.35.orig.tar.gz && \
-  cd flex-2.5.35 && \
-  ./configure --prefix=/tmp/install-of-flex --disable-shared && \
-  make -j && make install && cd .. && rm -rf flex* && \
-  wget --no-check-certificate https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/doxygen/1.8.5-1/doxygen_1.8.5.orig.tar.gz && \
-  tar xf doxygen_1.8.5.orig.tar.gz && \
-  cd doxygen-1.8.5 && \
-  ./configure --flex /tmp/install-of-flex/bin/flex --static && \
-  make -j && make install && cd .. && rm -rf doxygen* && \
-  rm -rf /var/lib/apt/lists/*
-
-# The ImageMagick package from apt has highly secure settings by
-# default, suitable for use behind a webserver, which we don't
-# need. So we use sed to remove those.
-# We also install it separatly because it pulls in some dependencies
-# that are needed for the documentation build.
-
-FROM gromacs/ci-gcc-7:2020
-WORKDIR /tmp
-COPY --from=doxygen-builder /usr/local/bin/* /usr/local/bin/
-RUN \
-  apt-get update && \
-  apt-get -y -q=2 --no-install-suggests --no-install-recommends install \
-    graphviz \
-    linkchecker \
-    mscgen \
-    texlive-latex-base \
-    texlive-latex-extra \
-    texlive-fonts-recommended \
-    texlive-fonts-extra && \
-  apt-get -y install imagemagick && \
-  rm -rf /var/lib/apt/lists/*
-RUN \
-  sed -i \
-    '/\"XPS\"/d;/\"PDF\"/d;/\"PS\"/d;/\"EPS\"/d;/disable ghostscript format types/d' \
-    /etc/ImageMagick-6/policy.xml && \
-  pip3 install sphinx==1.6.1
diff --git a/admin/dockerfiles/ci-gcc-8-cuda-10.2/Dockerfile b/admin/dockerfiles/ci-gcc-8-cuda-10.2/Dockerfile
deleted file mode 100644 (file)
index 86d2299..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-# Make an image that has the basic dependencies for building GROMACS.
-# This is the same for all other build images and gets used by those.
-
-# Some optional GROMACS dependencies are obtained from the
-# distribution, e.g.  fftw3, hwloc, blas and lapack so that the build
-# is as fast as possible.
-FROM nvidia/cuda:10.2-devel as cuda-ci-basic-dependencies
-ENV DEBIAN_FRONTEND=noninteractive
-WORKDIR /tmp
-RUN \
-  apt-get update && \
-  apt-get install -y \
-    cmake \
-    git \
-    ninja-build \
-    ccache \
-    build-essential \
-    wget \
-    moreutils \
-    rsync \
-    libfftw3-dev \
-    libhwloc-dev \
-    liblapack-dev \
-    xsltproc \
-    python3-pip
-
-# Make an image that has the dependencies for building GROMACS with gcc-8.
-FROM cuda-ci-basic-dependencies as ci-gcc-8-cuda-10.2
-WORKDIR /tmp
-RUN apt-get -qqy --no-install-suggests --no-install-recommends install \
-  gcc-8 \
-  g++-8 && \
-  rm -rf /var/lib/apt/lists/*
diff --git a/admin/dockerfiles/ci-gcc/Dockerfile b/admin/dockerfiles/ci-gcc/Dockerfile
deleted file mode 100644 (file)
index 6b92aa0..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# Make an image that has the dependencies for building GROMACS with gcc.
-
-
-FROM gromacs/base:2020
-WORKDIR /tmp
-ARG TOOL_VERSION
-RUN \
-  apt-get update && \
-  apt-get -qqy --no-install-suggests --no-install-recommends install \
-      gcc-$TOOL_VERSION \
-      g++-$TOOL_VERSION && \
-  rm -rf /var/lib/apt/lists/* && \
-  rm -rf /var/cache/apt/archives/*
index c52d55914837e63b29998f4f7ea81895c729828b..6638b414fcbabc9869fbeb34bd0edb5fcd8a8071 100755 (executable)
@@ -2,7 +2,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
+# Copyright (c) 2013,2014,2015,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To disable the hook temporarily for a commit, set NO_FORMAT_CHECK environment
 # variable.  For example,
 #   NO_FORMAT_CHECK=1 git commit -a
-# You can also run git commit --no-verify, but that also disables other hooks,
-# such as the Change-Id hook used by Gerrit.
+# You can also run git commit --no-verify, but that also disables other hooks.
 #
-# See docs/dev-manual/uncrustify.rst for more details.
+# See docs/dev-manual/code-formatting.rst for more details.
 
 if [ ! -z "$NO_FORMAT_CHECK" ]
 then
@@ -66,12 +65,12 @@ fi
 # Redirect output to stderr.
 exec 1>&2
 
-uncrustify_mode=off
+clangtidy_mode=`git config hooks.clangtidymode`
 clangformat_mode=`git config hooks.clangformatmode`
 copyright_mode=`git config hooks.copyrightmode`
-if [ -z "$uncrustify_mode" ]
+if [ -z "$clangtidy_mode" ]
 then
-    uncrustify_mode=off
+    clangtidy_mode=off
 fi
 if [ -z "$clangformat_mode" ]
 then
@@ -82,24 +81,24 @@ then
     copyright_mode=off
 fi
 
-if [[ -f admin/uncrustify.sh && \
-      ( "$uncrustify_mode" != "off" ) ]]
+if [[ -f admin/clang-tidy.sh && \
+      ( "$clangtidy_mode" != "off" ) ]]
 then
-    uncrustify_path=`git config hooks.uncrustifypath`
-    if [ -z "$uncrustify_path" ]
+    runclangtidy_path=`git config hooks.runclangtidypath`
+    if [ -z "$runclangtidy_path" ]
     then
-        echo "Please set the path to uncrustify using 'git config hooks.uncrustifypath'."
-        echo "Note that you need a custom version of uncrustify."
+        echo "Please set the path to run-clang-tidy using 'git config hooks.runclangtidypath'."
+        echo "Note that you need at least clang-tidy-9."
         exit 1
     fi
-    export UNCRUSTIFY="$uncrustify_path"
-    admin/uncrustify.sh check-index --rev=$against \
-        --uncrustify="$uncrustify_mode"
+    export RUN_CLANG_TIDY="$runclangtidy_path"
+    admin/clang-tidy.sh check-index --rev=$against \
+        --tidy="$clangtidy_mode"
     stat=$?
     if [ $stat -eq 1 ] ; then
         exit 1
     elif [ $stat -ne 0 ] ; then
-        echo "Source code formatting check with uncrustify failed"
+        echo "Source code checking with clang-tidy failed"
         exit 1
     fi
 fi
index 5ff56fa279fd9200b3263f6642e6164d0a2eb928..15877d35ae55bfa99e6ae77b8f641326f6626f1d 100644 (file)
@@ -7,7 +7,7 @@ prepare-release-version:
   cache: {}
   # Docker image uploaded to dockerhub by user eriklindahl
   # TODO: Get DockerFile for admin/dockerfiles
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   stage: configure-build
   variables:
     KUBERNETES_CPU_LIMIT: 1
@@ -27,7 +27,7 @@ regressiontests:prepare:
     - .variables:default
     - .rules:merge-and-post-merge-acceptance
   cache: {}
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   stage: configure-build
   variables:
     KUBERNETES_CPU_LIMIT: 1
@@ -35,7 +35,7 @@ regressiontests:prepare:
     KUBERNETES_MEMORY_REQUEST: 2Gi
   # Always clone the default version for this branch, master in this case
   script:
-    - export REGTESTBRANCH=release-2020
+    - export REGTESTBRANCH=master
     - if [[ ! -z $REGRESSIONTESTBRANCH ]] ; then
       export REGTESTBRANCH=$REGRESSIONTESTBRANCH ;
       echo "Using $REGTESTBRANCH instead of default" ;
@@ -71,7 +71,7 @@ regressiontests:package:
   cache: {}
   # Docker image uploaded to dockerhub by user eriklindahl
   # TODO: Get DockerFile for admin/dockerfiles
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   stage: release-package
   variables:
     KUBERNETES_CPU_LIMIT: 1
@@ -86,7 +86,7 @@ regressiontests:package:
     - if [[ $GROMACS_RELEASE != "true" ]] ; then
       REGTESTNAME=$REGTESTNAME-dev ;
       fi
-    - export REGTESTBRANCH=release-2020
+    - export REGTESTBRANCH=master
     - if [[ $CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME == "release-20"[1-2][0-9] ]] ; then
       export REGTESTBRANCH=$CI_COMMIT_REF_NAME ;
       fi
@@ -120,7 +120,7 @@ archive:configure:nightly:
   variables:
     BUILD_DIR: build-package
     CMAKE_SIMD_OPTIONS: -DGMX_SIMD=None
-    CMAKE_EXTRA_OPTIONS: -DGMX_BUILD_HELP=on -DGMX_USE_RDTSCP=OFF
+    CMAKE_EXTRA_OPTIONS: -DGMX_BUILD_HELP=on -DGMX_USE_RDTSCP=OFF -DGMX_USE_SIMD_KERNELS=off
     CMAKE_MPI_OPTIONS: -DGMX_THREAD_MPI=OFF -DGMX_OPENMP=OFF
 
 archive:configure:release:
@@ -130,7 +130,7 @@ archive:configure:release:
   variables:
     BUILD_DIR: build-package
     CMAKE_SIMD_OPTIONS: -DGMX_SIMD=None
-    CMAKE_EXTRA_OPTIONS: -DGMX_BUILD_HELP=on -DGMX_USE_RDTSCP=OFF
+    CMAKE_EXTRA_OPTIONS: -DGMX_BUILD_HELP=on -DGMX_USE_RDTSCP=OFF -DGMX_USE_SIMD_KERNELS=off
     CMAKE_MPI_OPTIONS: -DGMX_THREAD_MPI=OFF -DGMX_OPENMP=OFF
 
 # Right now we need to use an ugly hack to move the original build directory
@@ -145,7 +145,7 @@ archive:configure:release:
     - .use-ccache
     - .before_script:default
     - .docs:build
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   variables:
     KUBERNETES_CPU_LIMIT: 4
     KUBERNETES_CPU_REQUEST: 2
@@ -208,7 +208,7 @@ archive:package:
     BUILD_DIR: build-package
 
 release-verify:
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   stage: release-verify
   extends:
     - .variables:default
@@ -256,8 +256,17 @@ archive:nightly-webpage:
     - job: webpage:build
   variables:
     BUILD_DIR: build-docs
+  before_script:
+    - eval $(ssh-agent -s)
+    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
+    - mkdir -p ~/.ssh
+    - chmod 700 ~/.ssh
+    - ssh-keyscan www.gromacs.org > ~/.ssh/known_hosts # Force overwrite the known hosts so we only have that one key in it.
+    - chmod 644 ~/.ssh/known_hosts
   script:
     - tar czf webpage.tar.gz $BUILD_DIR/docs/html/
+    - rsync --chmod=u+rwX,g+rwX,o+rX -av $BUILD_DIR/docs/html/* $BUILD_DIR/docs/html/.[a-z]* pbauer@www.gromacs.org:/var/www/manual/nightly/
+
   artifacts:
     when: always
     paths:
index 1fcdd113da2aae54d471abba90340c99f8c8baad..6402d8c43b4860aa61b6330b4c16986870e379f3 100644 (file)
@@ -6,7 +6,7 @@
     - .gromacs:base:configure
     - .before_script:default
   # TODO (#3480) this should be organized more like the current documentation.py script
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   stage: configure-build
   cache: {}
   variables:
@@ -72,7 +72,7 @@
     - .before_script:default
     - .rules:nightly-only-for-release
   # TODO (#3480) this should be organized more like the current documentation.py script
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   stage: release-configure
   cache: {}
   variables:
@@ -178,11 +178,11 @@ docs:configure:
     - .rules:basic-push
   variables:
     BUILD_DIR: build-docs
-    CMAKE_EXTRA_OPTIONS: -DGMX_BUILD_HELP=on -DGMX_BUILD_MANUAL=on
+    CMAKE_EXTRA_OPTIONS: -DGMX_BUILD_HELP=on -DGMX_BUILD_MANUAL=on -DGMX_USE_SIMD_KERNELS=off
 
 .docs:build:
   # TODO (#3480) this should be organized more like the current documentation.py script
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   script:
     - cd $BUILD_DIR
     - cmake --build . --target gmx -- -j8
@@ -222,7 +222,7 @@ docs:build:
     - .gromacs:base:build
     - .before_script:default
   # TODO (#3480) this should be organized more like the current documentation.py script
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   cache: {}
   variables:
     KUBERNETES_CPU_LIMIT: 4
@@ -278,7 +278,7 @@ webpage:configure:
     - regressiontests:package
     - prepare-release-version
   variables:
-    CMAKE_EXTRA_OPTIONS: -DGMX_BUILD_HELP=on -DGMX_BUILD_MANUAL=on
+    CMAKE_EXTRA_OPTIONS: -DGMX_BUILD_HELP=on -DGMX_BUILD_MANUAL=on -DGMX_USE_SIMD_KERNELS=off
 
 
 webpage:dependencies:
index 302a58e05efe6987ea812544f6c54b4f6f40d5d7..49056b495926395a77fdc6ffbd485d8b9aeb6923 100644 (file)
@@ -15,7 +15,7 @@
     KUBERNETES_MEMORY_LIMIT: 8Gi
     KUBERNETES_EXTENDED_RESOURCE_NAME: ""
     KUBERNETES_EXTENDED_RESOURCE_LIMIT: 0
-    CACHE_FALLBACK_KEY: "$CI_JOB_NAME-$CI_JOB_STAGE-release-2020"
+    CACHE_FALLBACK_KEY: "$CI_JOB_NAME-$CI_JOB_STAGE-master"
     BUILD_DIR: build
     INSTALL_DIR: install
     CMAKE_GMXAPI_OPTIONS: ""
@@ -41,7 +41,7 @@
 .use-cuda:
   variables:
     CMAKE_PRECISION_OPTIONS: "-DGMX_DOUBLE=OFF"
-    CMAKE_GPU_OPTIONS: -DGMX_GPU=ON -DGMX_USE_CUDA=ON
+    CMAKE_GPU_OPTIONS: -DGMX_GPU=CUDA
 
 .use-mpi:
   variables:
@@ -50,7 +50,7 @@
 .use-opencl:
   variables:
     CMAKE_PRECISION_OPTIONS: "-DGMX_DOUBLE=OFF"
-    CMAKE_GPU_OPTIONS: -DGMX_GPU=ON -DGMX_USE_OPENCL=ON
+    CMAKE_GPU_OPTIONS: -DGMX_GPU=OpenCL
 
 # Base definition for using gcc.
 .use-gcc:base:
     - export CCACHE_DIR=${PWD}/ccache
     - export ASAN_SYMBOLIZER_PATH=/usr/local/bin/llvm-symbolizer
 
+# Base definition for using oneAPI.
+.use-oneapi:base:
+  variables:
+    # Use the HPC variants of icc and icpc so that OpenMP is active
+    CMAKE_COMPILER_SCRIPT: -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DCMAKE_INCLUDE_PATH=/opt/intel/oneapi/compiler/latest/linux/include/sycl -DCMAKE_PREFIX_PATH=/opt/intel/oneapi/compiler/latest/linux
+    CMAKE_EXTRA_OPTIONS: -DGMX_FFT_LIBRARY=mkl
+  before_script:
+    # Necessary to override gitlab default 'set -e' which breaks Intel's
+    # setvar.sh script
+    - set +e
+    - source /opt/intel/oneapi/setvars.sh
+    - mkdir -p ccache
+    - export CCACHE_BASEDIR=${PWD}
+    - export CCACHE_DIR=${PWD}/ccache
index 4d0faf04e9092078b8b335342fb7890389242198..04b41d7a0ae04842aa67ae410f381070272faabc 100644 (file)
@@ -5,10 +5,10 @@ simple-build:
   extends:
     - .variables:default
     - .use-ccache
-    - .use-gcc:base
+    - .use-clang:base
     - .rules:basic-push
   stage: pre-build
-  image: gromacs/cmake-3.9.6-gcc-5-cuda-9.0-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   variables:
     KUBERNETES_CPU_LIMIT: 8
     KUBERNETES_CPU_REQUEST: 4
@@ -19,7 +19,7 @@ simple-build:
     CMAKE_BUILD_TYPE_OPTIONS: "-DCMAKE_BUILD_TYPE=Debug"
     CMAKE_GPU_OPTIONS: "-DGMX_GPU=OFF"
     CMAKE_GMXAPI_OPTIONS: "-DGMX_PYTHON_PACKAGE=OFF"
-    COMPILER_MAJOR_VERSION: 5
+    COMPILER_MAJOR_VERSION: 9
     BUILD_DIR: simple-build
   script:
     - echo $CMAKE_COMPILER_SCRIPT
@@ -220,31 +220,43 @@ gromacs:gcc-7:configure:
   extends:
     - .gromacs:base:configure
     - .use-gcc:base
+    - .use-opencl
     - .rules:merge-and-post-merge-acceptance
-  image: gromacs/cmake-3.9.6-gcc-7-amdopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master
   variables:
     CMAKE_SIMD_OPTIONS: "-DGMX_SIMD=AVX2_256"
-    CMAKE_GPU_OPTIONS: "-DGMX_GPU=ON -DGMX_USE_OPENCL=ON"
+    CMAKE_EXTRA_OPTIONS: "-DGMX_EXTERNAL_CLFFT=ON"
     COMPILER_MAJOR_VERSION: 7
 
-gromacs:gcc-6:configure:
+gromacs:gcc-8-cuda-11.0:configure:
   extends:
     - .gromacs:base:configure
     - .use-gcc:base
-    - .use-opencl
+    - .use-cuda
     - .rules:merge-and-post-merge-acceptance
-  image: gromacs/cmake-3.9.6-gcc-6-cuda-10.1-nvidiaopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
   variables:
     CMAKE_SIMD_OPTIONS: "-DGMX_SIMD=SSE4.1"
-    COMPILER_MAJOR_VERSION: 6
-    CMAKE_EXTRA_OPTIONS: "-DGMX_EXTERNAL_CLFFT=ON"
+    COMPILER_MAJOR_VERSION: 8
+
+gromacs:gcc-8-cuda-11.0:configureMPI:
+  extends:
+    - .gromacs:base:configure
+    - .use-gcc:base
+    - .use-cuda
+    - .use-mpi
+    - .rules:merge-and-post-merge-acceptance
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
+  variables:
+    CMAKE_SIMD_OPTIONS: "-DGMX_SIMD=SSE4.1"
+    COMPILER_MAJOR_VERSION: 8
 
 gromacs:clang-TSAN:configure:
   extends:
     - .gromacs:base:configure
     - .use-clang:base
     - .rules:merge-and-post-merge-acceptance
-  image: gromacs/cmake-3.15.7-llvm-8-tsan:2020
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
   variables:
     COMPILER_MAJOR_VERSION: 8
     CMAKE_BUILD_TYPE_OPTIONS: "-DCMAKE_BUILD_TYPE=TSAN"
@@ -258,25 +270,23 @@ gromacs:clang-8:configure:
   variables:
     COMPILER_MAJOR_VERSION: 8
 
-gromacs:clang-3.6:configure:
+gromacs:clang-9-mpi:configure:
   extends:
-    - .gromacs:base:configure
-    - .use-clang:base
-    - .use-mpi
-    - .rules:merge-requests
-  image: gromacs/cmake-3.9.6-llvm-3.6-amdopencl-openmpi:2020
+   - .gromacs:base:configure
+   - .use-clang:base
+   - .use-mpi
+   - .rules:merge-requests
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   variables:
-    COMPILER_MAJOR_VERSION: "3.6"
-    CMAKE_PRECISION_OPTIONS: "-DGMX_DOUBLE=ON"
-    CMAKE_SIMD_OPTIONS: "-DGMX_SIMD=SSE4.1"
-    CMAKE_EXTRA_OPTIONS: "-DGMX_OPENMP=OFF"
+    COMPILER_MAJOR_VERSION: 9
+    CMAKE_PRECISION_OPTIONS: -DGMX_DOUBLE=ON
 
 gromacs:clang-static-analyzer:configure:
   extends:
     - .gromacs:base:configure
     - .use-clang:base
     - .rules:merge-requests
-  image: gromacs/cmake-3.15.7-llvm-8-tsan:2020
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
   variables:
      CMAKE_COMPILER_SCRIPT: "-DCMAKE_CXX_COMPILER=/usr/local/libexec/c++-analyzer -DCMAKE_C_COMPILER=gcc"
      CMAKE_EXTRA_OPTIONS: "-DGMX_CLANG_ANALYZER=ON -DGMX_OPENMP=OFF -DGMX_USE_RDTSCP=OFF -DGMX_FFT_LIBRARY=fftpack -DGMX_DEVELOPER_BUILD=ON"
@@ -287,20 +297,41 @@ gromacs:clang-ASAN:configure:
     - .gromacs:base:configure
     - .use-clang:base
     - .rules:merge-requests
-  image: gromacs/cmake-3.15.7-llvm-8-tsan:2020
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
   variables:
     COMPILER_MAJOR_VERSION: 8
     CMAKE_BUILD_TYPE_OPTIONS: "-DCMAKE_BUILD_TYPE=ASAN"
 
-gromacs:gcc-6:release:configure:
+gromacs:oneapi-2021.1-beta08-opencl:configure:
+  extends:
+   - .gromacs:base:configure
+   - .use-oneapi:base
+   - .use-opencl
+   - .rules:merge-requests
+  image: gromacs/cmake-3.17.2-oneapi-2021.1-beta08-master
+  variables:
+    COMPILER_MAJOR_VERSION: 2021
+
+gromacs:clang-UBSAN:configure:
+  extends:
+    - .gromacs:base:configure
+    - .use-clang:base
+    - .rules:merge-and-post-merge-acceptance
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
+  variables:
+    COMPILER_MAJOR_VERSION: 8
+    CMAKE_BUILD_TYPE_OPTIONS: "-DCMAKE_BUILD_TYPE=UBSAN"
+
+gromacs:gcc-8-cuda-11.0:release:configure:
   extends:
     - .gromacs:base:release:configure
     - .use-gcc:base
+    - .use-mpi
     - .use-cuda
     - .rules:nightly-only-for-release
-  image: gromacs/cmake-3.9.6-gcc-6-cuda-10.1-nvidiaopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
   variables:
-    COMPILER_MAJOR_VERSION: 6
+    COMPILER_MAJOR_VERSION: 8
     RELEASE_BUILD_DIR: release-builds-gcc
     CMAKE_EXTRA_OPTIONS: "-DGMX_BUILD_MDRUN_ONLY=ON"
     CMAKE_BUILD_TYPE_OPTIONS : "-DCMAKE_BUILD_TYPE=RelWithAssert"
@@ -314,10 +345,12 @@ gromacs:gcc-7:release:configure:
   extends:
     - .gromacs:base:release:configure
     - .use-gcc:base
+    - .use-opencl
     - .rules:nightly-only-for-release
-  image: gromacs/cmake-3.9.6-gcc-7-amdopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master
   variables:
     COMPILER_MAJOR_VERSION: 7
+    CMAKE_EXTRA_OPTIONS: "-DGMX_EXTERNAL_CLFFT=ON"
     RELEASE_BUILD_DIR: release-builds-gcc
     CMAKE_BUILD_TYPE_OPTIONS: "-DCMAKE_BUILD_TYPE=RelWithAssert"
   dependencies:
@@ -325,37 +358,46 @@ gromacs:gcc-7:release:configure:
     - regressiontests:package
     - prepare-release-version
 
-gromacs:clang-3.6:release:configure:
+gromacs:clang-9:release:configure:
   extends:
     - .gromacs:base:release:configure
     - .use-clang:base
     - .use-mpi
     - .rules:nightly-only-for-release
-  image: gromacs/cmake-3.9.6-llvm-3.6-amdopencl-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   variables:
-    COMPILER_MAJOR_VERSION: "3.6"
+    COMPILER_MAJOR_VERSION: 9
     RELEASE_BUILD_DIR: release-builds-clang
     CMAKE_PRECISION_OPTIONS: "-DGMX_DOUBLE=ON"
-    CMAKE_SIMD_OPTIONS: "-DGMX_SIMD=SSE4.1"
     CMAKE_BUILD_TYPE_OPTIONS: "-DCMAKE_BUILD_TYPE=RelWithAssert"
-    CMAKE_EXTRA_OPTIONS: "-DGMX_OPENMP=OFF"
   dependencies:
     - archive:package
     - regressiontests:package
     - prepare-release-version
 
-gromacs:clang-8:release:configure:
+gromacs:clang-8-cuda-10.1:release:configure:
   extends:
     - .gromacs:base:release:configure
     - .use-clang:base
     - .use-cuda
     - .rules:nightly-only-for-release
-  image: gromacs/cmake-3.15.7-llvm-8-cuda-10.1-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-8-cuda-10.1-openmpi-master
   variables:
     COMPILER_MAJOR_VERSION: 8
     RELEASE_BUILD_DIR: release-builds-clang
     CMAKE_BUILD_TYPE_OPTIONS: "-DCMAKE_BUILD_TYPE=RelWithAssert"
 
+gromacs:oneapi-2021.1-beta08-opencl:release:configure:
+  extends:
+   - .gromacs:base:release:configure
+   - .use-oneapi:base
+   - .use-opencl
+   - .rules:nightly-only-for-release
+  image: gromacs/cmake-3.17.2-oneapi-2021.1-beta08-master
+  variables:
+    COMPILER_MAJOR_VERSION: 2021
+    RELEASE_BUILD_DIR: release-builds-oneapi
+
 # Jobs running during build stage
 
 .gromacs:base:build:
@@ -399,21 +441,32 @@ gromacs:gcc-7:build:
     - .before_script:default
     - .use-ccache
     - .rules:merge-and-post-merge-acceptance
-  image: gromacs/cmake-3.9.6-gcc-7-amdopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master
   needs:
     - job: gromacs:gcc-7:configure
       artifacts: true
 
-gromacs:gcc-6:build:
+gromacs:gcc-8-cuda-11.0:build:
   extends:
     - .variables:default
     - .gromacs:base:build
     - .before_script:default
     - .use-ccache
     - .rules:merge-and-post-merge-acceptance
-  image: gromacs/cmake-3.9.6-gcc-6-cuda-10.1-nvidiaopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
   needs:
-    - job: gromacs:gcc-6:configure
+    - job: gromacs:gcc-8-cuda-11.0:configure
+
+gromacs:gcc-8-cuda-11.0:buildMPI:
+  extends:
+    - .variables:default
+    - .gromacs:base:build
+    - .before_script:default
+    - .use-ccache
+    - .rules:merge-and-post-merge-acceptance
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
+  needs:
+    - job: gromacs:gcc-8-cuda-11.0:configureMPI
 
 gromacs:clang-TSAN:build:
   extends:
@@ -422,7 +475,7 @@ gromacs:clang-TSAN:build:
     - .use-clang:base
     - .use-ccache
     - .rules:merge-and-post-merge-acceptance
-  image: gromacs/cmake-3.15.7-llvm-8-tsan:2020
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
   needs:
     - job: gromacs:clang-TSAN:configure
 
@@ -433,12 +486,25 @@ gromacs:clang-ASAN:build:
     - .use-clang:base
     - .use-ccache
     - .rules:merge-requests
-  image: gromacs/cmake-3.15.7-llvm-8-tsan:2020
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
   tags:
     - k8s-scilifelab
   needs:
     - job: gromacs:clang-ASAN:configure
 
+gromacs:clang-UBSAN:build:
+  extends:
+    - .variables:default
+    - .gromacs:base:build
+    - .use-clang:base
+    - .use-ccache
+    - .rules:merge-and-post-merge-acceptance
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
+  tags:
+    - k8s-scilifelab
+  needs:
+    - job: gromacs:clang-UBSAN:configure
+
 gromacs:clang-static-analyzer:build:
   extends:
     - .variables:default
@@ -446,7 +512,7 @@ gromacs:clang-static-analyzer:build:
     - .use-clang:base
     - .use-ccache
     - .rules:merge-requests
-  image: gromacs/cmake-3.15.7-llvm-8-tsan:2020
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
   tags:
     - k8s-scilifelab
   needs:
@@ -463,18 +529,29 @@ gromacs:clang-8:build:
   needs:
     - job: gromacs:clang-8:configure
 
-gromacs:clang-3.6:build:
+gromacs:oneapi-2021.1-beta08-opencl:build:
+  extends:
+    - .variables:default
+    - .gromacs:base:build
+    - .use-ccache
+    - .use-oneapi:base
+    - .rules:merge-requests
+  image: gromacs/cmake-3.17.2-oneapi-2021.1-beta08-master
+  needs:
+    - job: gromacs:oneapi-2021.1-beta08-opencl:configure
+
+gromacs:clang-9-mpi:build:
   extends:
     - .variables:default
     - .gromacs:base:build
     - .before_script:default
     - .use-ccache
     - .rules:merge-requests
-  image: gromacs/cmake-3.9.6-llvm-3.6-amdopencl-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   needs:
-    - job: gromacs:clang-3.6:configure
+    - job: gromacs:clang-9-mpi:configure
 
-gromacs:gcc-6:release:build:
+gromacs:gcc-8-cuda-11.0:release:build:
   extends:
     - .variables:default  
     - .gromacs:base:build
@@ -484,9 +561,9 @@ gromacs:gcc-6:release:build:
   stage: release-build
   variables:
     BUILD_DIR: release-builds-gcc
-  image: gromacs/cmake-3.9.6-gcc-6-cuda-10.1-nvidiaopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
   needs:
-    - job: gromacs:gcc-6:release:configure
+    - job: gromacs:gcc-8-cuda-11.0:release:configure
 
 gromacs:gcc-7:release:build:
   extends:
@@ -498,11 +575,11 @@ gromacs:gcc-7:release:build:
   stage: release-build
   variables:
     BUILD_DIR: release-builds-gcc
-  image: gromacs/cmake-3.9.6-gcc-7-amdopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master
   needs:
     - job: gromacs:gcc-7:release:configure
 
-gromacs:clang-3.6:release:build:
+gromacs:clang-9:release:build:
   extends:
     - .variables:default
     - .gromacs:base:build
@@ -512,11 +589,11 @@ gromacs:clang-3.6:release:build:
   stage: release-build
   variables:
     BUILD_DIR: release-builds-clang
-  image: gromacs/cmake-3.9.6-llvm-3.6-amdopencl-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   needs:
-    - job: gromacs:clang-3.6:release:configure
+    - job: gromacs:clang-9:release:configure
 
-gromacs:clang-8:release:build:
+gromacs:clang-8-cuda-10.1:release:build:
   extends:
     - .variables:default
     - .gromacs:base:build
@@ -526,9 +603,25 @@ gromacs:clang-8:release:build:
   stage: release-build
   variables:
     BUILD_DIR: release-builds-clang
-  image: gromacs/cmake-3.15.7-llvm-8-cuda-10.1-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-8-cuda-10.1-openmpi-master
   needs:
-    - job: gromacs:clang-8:release:configure
+    - job: gromacs:clang-8-cuda-10.1:release:configure
+
+gromacs:oneapi-2021.1-beta08-opencl:release:build:
+  extends:
+    - .variables:default
+    - .gromacs:base:build
+    - .use-oneapi:base
+    - .use-opencl
+    - .use-ccache
+    - .rules:nightly-only-for-release
+  stage: release-build
+  variables:
+    BUILD_DIR: release-builds-oneapi
+    COMPILER_MAJOR_VERSION: 2021
+  image: gromacs/cmake-3.17.2-oneapi-2021.1-beta08-master
+  needs:
+    - job: gromacs:oneapi-2021.1-beta08-opencl:release:configure
 
 # Jobs running during test stage
 
@@ -543,9 +636,21 @@ gromacs:clang-8:release:build:
     CTEST_RUN_MODE: "ExperimentalTest"
   script:
     - cd $BUILD_DIR
+    - export UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1:suppressions=$CI_PROJECT_DIR/admin/ubsan-suppressions.txt
     # Needed to run MPI enabled code in the docker images, until we set up different users
     - export OMPI_ALLOW_RUN_AS_ROOT=1
     - export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1
+    - export ASAN_OPTIONS="check_initialization_order=1:detect_invalid_pointer_pairs=1:strict_init_order=true:strict_string_checks=true:detect_stack_use_after_return=true"
+    # If $GMX_TEST_REQUIRED_NUMBER_OF_DEVICES is not set and we have GPUs, set it
+    - if [ -z $GMX_TEST_REQUIRED_NUMBER_OF_DEVICES ] && [ -n $KUBERNETES_EXTENDED_RESOURCE_NAME ] ; then
+      if grep -q '/gpu$' <<< "$KUBERNETES_EXTENDED_RESOURCE_NAME"; then
+      echo "export GMX_TEST_REQUIRED_NUMBER_OF_DEVICES=\"$KUBERNETES_EXTENDED_RESOURCE_LIMIT\"";
+      export GMX_TEST_REQUIRED_NUMBER_OF_DEVICES="$KUBERNETES_EXTENDED_RESOURCE_LIMIT";
+      fi
+      fi
+    - if grep -qF 'nvidia.com/gpu' <<< "$KUBERNETES_EXTENDED_RESOURCE_NAME"; then
+      nvidia-smi || true;
+      fi
     - ctest -D $CTEST_RUN_MODE --output-on-failure | tee ctestLog.log || true
     - awk '/The following tests FAILED/,/^Errors while running CTest|^$/'
       ctestLog.log | tee ctestErrors.log
@@ -582,8 +687,8 @@ gromacs:clang-8:release:build:
   retry:
     max: 1
   script:
-    # This should go away once we are able to run ASAN on mdrun
-    - export ASAN_OPTIONS="detect_leaks=0"
+    - export LSAN_OPTIONS="suppressions=$CI_PROJECT_DIR/admin/lsan-suppressions.txt:print_suppressions=0"
+    - export ASAN_OPTIONS="check_initialization_order=1:detect_invalid_pointer_pairs=1:strict_init_order=true:strict_string_checks=true:detect_stack_use_after_return=true"
     # Needed to run MPI enabled code in the docker images, until we set up different users
     - export OMPI_ALLOW_RUN_AS_ROOT=1
     - export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1
@@ -601,7 +706,7 @@ gromacs:gcc-7:test:
   extends:
     - .gromacs:base:test
     - .rules:merge-requests
-  image: gromacs/cmake-3.9.6-gcc-7-amdopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master
   variables:
     KUBERNETES_EXTENDED_RESOURCE_NAME: "amd.com/gpu"
     KUBERNETES_EXTENDED_RESOURCE_LIMIT: 1
@@ -611,18 +716,34 @@ gromacs:gcc-7:test:
   needs:
     - job: gromacs:gcc-7:build
 
-gromacs:gcc-6:test:
+gromacs:gcc-8-cuda-11.0:test:
   extends:
     - .gromacs:base:test
     - .rules:merge-requests
-  image: gromacs/cmake-3.9.6-gcc-6-cuda-10.1-nvidiaopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
+  variables:
+    KUBERNETES_EXTENDED_RESOURCE_NAME: "nvidia.com/gpu"
+    KUBERNETES_EXTENDED_RESOURCE_LIMIT: 1
+  tags:
+    - k8s-scilifelab
+  needs:
+    - job: gromacs:gcc-8-cuda-11.0:build
+
+gromacs:gcc-8-cuda-11.0:test-gpucommupd:
+  extends:
+    - .gromacs:base:test
+    - .rules:post-merge-acceptance
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
   variables:
     KUBERNETES_EXTENDED_RESOURCE_NAME: "nvidia.com/gpu"
     KUBERNETES_EXTENDED_RESOURCE_LIMIT: 1
+    GMX_GPU_DD_COMMS: 1
+    GMX_GPU_PME_PP_COMMS: 1
+    GMX_FORCE_UPDATE_DEFAULT_GPU: 1
   tags:
     - k8s-scilifelab
   needs:
-    - job: gromacs:gcc-6:build
+    - job: gromacs:gcc-8-cuda-11.0:build
 
 gromacs:clang-8:test:
   extends:
@@ -636,7 +757,7 @@ gromacs:clang-TSAN:test:
   extends:
     - .gromacs:base:test
     - .rules:post-merge-acceptance
-  image: gromacs/cmake-3.15.7-llvm-8-tsan:2020
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
   needs:
     - job: gromacs:clang-TSAN:build
 
@@ -645,7 +766,7 @@ gromacs:clang-ASAN:test:
     - .gromacs:base:test
     - .use-clang:base
     - .rules:merge-requests
-  image: gromacs/cmake-3.15.7-llvm-8-tsan:2020
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
   variables:
     CTEST_RUN_MODE: "ExperimentalMemCheck"
   tags:
@@ -653,19 +774,41 @@ gromacs:clang-ASAN:test:
   needs:
     - job: gromacs:clang-ASAN:build
 
-gromacs:clang-3.6:test:
+gromacs:clang-UBSAN:test:
+  extends:
+    - .gromacs:base:test
+    - .use-clang:base
+    - .rules:post-merge-acceptance
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
+  tags:
+    - k8s-scilifelab
+  needs:
+    - job: gromacs:clang-UBSAN:build
+
+gromacs:oneapi-2021.1-beta08-opencl:test:
+  extends:
+    - .gromacs:base:test
+    - .use-oneapi:base
+    - .rules:merge-requests
+  image: gromacs/cmake-3.17.2-oneapi-2021.1-beta08-master
+  needs:
+    - job: gromacs:oneapi-2021.1-beta08-opencl:build
+
+gromacs:clang-9-mpi:test:
   extends:
     - .gromacs:base:test
     - .rules:merge-requests
-  image: gromacs/cmake-3.9.6-llvm-3.6-amdopencl-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
+  tags:
+    - k8s-scilifelab
   needs:
-    - job: gromacs:clang-3.6:build
+    - job: gromacs:clang-9-mpi:build
 
 gromacs:gcc-7:regressiontest:
   extends:
     - .gromacs:base:regressiontest
     - .rules:post-merge-acceptance
-  image: gromacs/cmake-3.9.6-gcc-7-amdopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master
   variables:
     KUBERNETES_EXTENDED_RESOURCE_NAME: "amd.com/gpu"
     KUBERNETES_EXTENDED_RESOURCE_LIMIT: 1
@@ -679,11 +822,11 @@ gromacs:gcc-7:regressiontest:
     - job: gromacs:gcc-7:build
     - job: regressiontests:prepare
 
-gromacs:gcc-6:regressiontest:
+gromacs:gcc-8-cuda-11.0:regressiontest:
   extends:
     - .gromacs:base:regressiontest
     - .rules:merge-requests
-  image: gromacs/cmake-3.9.6-gcc-6-cuda-10.1-nvidiaopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
   variables:
     KUBERNETES_EXTENDED_RESOURCE_NAME: "nvidia.com/gpu"
     KUBERNETES_EXTENDED_RESOURCE_LIMIT: 1
@@ -693,8 +836,59 @@ gromacs:gcc-6:regressiontest:
   tags:
     - k8s-scilifelab
   needs:
-    - job: gromacs:gcc-6:build
+    - job: gromacs:gcc-8-cuda-11.0:build
+    - job: regressiontests:prepare
+
+gromacs:gcc-8-cuda-11.0:regressiontest-gpucommupd-tMPI:
+  extends:
+    - .gromacs:base:regressiontest
+    - .rules:post-merge-acceptance
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
+  variables:
+    KUBERNETES_EXTENDED_RESOURCE_NAME: "nvidia.com/gpu"
+    KUBERNETES_EXTENDED_RESOURCE_LIMIT: 2
+    REGRESSIONTEST_PME_RANK_NUMBER: 0
+    REGRESSIONTEST_TOTAL_RANK_NUMBER: 4
+    REGRESSIONTEST_OMP_RANK_NUMBER: 1
+    GMX_GPU_DD_COMMS: 1
+    GMX_GPU_PME_PP_COMMS: 1
+    GMX_FORCE_UPDATE_DEFAULT_GPU: 1
+  tags:
+    - k8s-scilifelab
+  needs:
+    - job: gromacs:gcc-8-cuda-11.0:build
+    - job: regressiontests:prepare
+  artifacts:
+    paths:
+      - regressiontests
+    when: always
+    expire_in: 1 week
+
+gromacs:gcc-8-cuda-11.0:regressiontest-gpucommupd-MPI:
+  extends:
+    - .gromacs:base:regressiontest
+    - .rules:post-merge-acceptance
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
+  variables:
+    KUBERNETES_EXTENDED_RESOURCE_NAME: "nvidia.com/gpu"
+    KUBERNETES_EXTENDED_RESOURCE_LIMIT: 2
+    REGRESSIONTEST_PME_RANK_NUMBER: 0
+    REGRESSIONTEST_TOTAL_RANK_NUMBER: 4
+    REGRESSIONTEST_OMP_RANK_NUMBER: 1
+    REGRESSIONTEST_PARALLEL: "-np"
+    GMX_GPU_DD_COMMS: 1  
+    GMX_GPU_PME_PP_COMMS: 1
+    GMX_FORCE_UPDATE_DEFAULT_GPU: 1
+  tags:
+    - k8s-scilifelab
+  needs:
+    - job: gromacs:gcc-8-cuda-11.0:buildMPI
     - job: regressiontests:prepare
+  artifacts:
+    paths:
+      - regressiontests
+    when: always
+    expire_in: 1 week
 
 gromacs:clang-8:regressiontest:
   extends:
@@ -711,7 +905,7 @@ gromacs:clang-TSAN:regressiontest:
   extends:
     - .gromacs:base:regressiontest
     - .rules:post-merge-acceptance
-  image: gromacs/cmake-3.15.7-llvm-8-tsan:2020
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
   tags:
     - k8s-scilifelab
   needs:
@@ -723,34 +917,43 @@ gromacs:clang-ASAN:regressiontest:
     - .gromacs:base:regressiontest
     - .use-clang:base
     - .rules:merge-requests
-  image: gromacs/cmake-3.15.7-llvm-8-tsan:2020
+  image: gromacs/cmake-3.13.0-llvm-8-tsan-master
   tags:
     - k8s-scilifelab
   needs:
     - job: gromacs:clang-ASAN:build
     - job: regressiontests:prepare
 
-gromacs:clang-3.6:regressiontest:
+gromacs:clang-9:regressiontest:
   extends:
     - .gromacs:base:regressiontest
     - .rules:merge-requests
-  image: gromacs/cmake-3.9.6-llvm-3.6-amdopencl-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   variables:
     REGRESSIONTEST_DOUBLE: "-double"
-    REGRESSIONTEST_OMP_RANK_NUMBER: 0
     REGRESSIONTEST_PARALLEL: "-np"
   tags:
     - k8s-scilifelab
   needs:
-    - job: gromacs:clang-3.6:build
+    - job: gromacs:clang-9-mpi:build
+    - job: regressiontests:prepare
+
+gromacs:oneapi-2021.1-beta08-opencl:regressiontest:
+  extends:
+    - .gromacs:base:regressiontest
+    - .use-oneapi:base
+    - .rules:merge-requests
+  image: gromacs/cmake-3.17.2-oneapi-2021.1-beta08-master
+  needs:
+    - job: gromacs:oneapi-2021.1-beta08-opencl:build
     - job: regressiontests:prepare
 
-gromacs:gcc-6:release:test:
+gromacs:gcc-8-cuda-11.0:release:test:
   extends:
     - .gromacs:base:test
     - .rules:nightly-only-for-release
   stage: release-tests
-  image: gromacs/cmake-3.9.6-gcc-6-cuda-10.1-nvidiaopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.15.7-gcc-8-cuda-11.0-nvidiaopencl-clfft-openmpi-master
   variables:
     KUBERNETES_EXTENDED_RESOURCE_NAME: "nvidia.com/gpu"
     KUBERNETES_EXTENDED_RESOURCE_LIMIT: 1
@@ -758,15 +961,15 @@ gromacs:gcc-6:release:test:
   tags:
     - k8s-scilifelab
   needs:
-    - job: gromacs:gcc-6:release:configure
-    - job: gromacs:gcc-6:release:build
+    - job: gromacs:gcc-8-cuda-11.0:release:configure
+    - job: gromacs:gcc-8-cuda-11.0:release:build
 
 gromacs:gcc-7:release:test:
   extends:
     - .gromacs:base:test
     - .rules:nightly-only-for-release
   stage: release-tests
-  image: gromacs/cmake-3.9.6-gcc-7-amdopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master
   variables:
     BUILD_DIR: release-builds-gcc
     KUBERNETES_EXTENDED_RESOURCE_NAME: "amd.com/gpu"
@@ -778,24 +981,24 @@ gromacs:gcc-7:release:test:
     - job: gromacs:gcc-7:release:configure
     - job: gromacs:gcc-7:release:build
 
-gromacs:clang-3.6:release:test:
+gromacs:clang-9:release:test:
   extends:
     - .gromacs:base:test
     - .rules:nightly-only-for-release
   stage: release-tests
-  image: gromacs/cmake-3.9.6-llvm-3.6-amdopencl-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   variables:
     BUILD_DIR: release-builds-clang
   needs:
-    - job: gromacs:clang-3.6:release:configure
-    - job: gromacs:clang-3.6:release:build
+    - job: gromacs:clang-9:release:configure
+    - job: gromacs:clang-9:release:build
 
-gromacs:clang-8:release:test:
+gromacs:clang-8-cuda-10.1:release:test:
   extends:
     - .gromacs:base:test
     - .rules:nightly-only-for-release
   stage: release-tests
-  image: gromacs/cmake-3.15.7-llvm-8-cuda-10.1-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-8-cuda-10.1-openmpi-master
   variables:
     KUBERNETES_EXTENDED_RESOURCE_NAME: "nvidia.com/gpu"
     KUBERNETES_EXTENDED_RESOURCE_LIMIT: 1
@@ -803,44 +1006,61 @@ gromacs:clang-8:release:test:
   tags:
     - k8s-scilifelab
   needs:
-    - job: gromacs:clang-8:release:configure
-    - job: gromacs:clang-8:release:build
+    - job: gromacs:clang-8-cuda-10.1:release:configure
+    - job: gromacs:clang-8-cuda-10.1:release:build
+
+gromacs:oneapi-2021.1-beta08-opencl:release:test:
+  extends:
+    - .gromacs:base:test
+    - .use-oneapi:base
+    - .rules:nightly-only-for-release
+  stage: release-tests
+  image: gromacs/cmake-3.17.2-oneapi-2021.1-beta08-master
+  variables:
+    BUILD_DIR: release-builds-oneapi
+  needs:
+    - job: gromacs:oneapi-2021.1-beta08-opencl:release:configure
+    - job: gromacs:oneapi-2021.1-beta08-opencl:release:build
 
 gromacs:gcc-7:release:regressiontest:
   extends:
     - .gromacs:base:regressiontest
     - .rules:nightly-only-for-release
   stage: release-tests
-  image: gromacs/cmake-3.9.6-gcc-7-amdopencl-clfft-openmpi:2020
+  image: gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master
   variables:
     BUILD_DIR: release-builds-gcc
+    KUBERNETES_EXTENDED_RESOURCE_NAME: "amd.com/gpu"
+    KUBERNETES_EXTENDED_RESOURCE_LIMIT: 1
+    LD_LIBRARY_PATH: "/opt/rocm-3.5.0/opencl/lib"
+  tags:
+    - k8s-scilifelab
   needs:
     - job: gromacs:gcc-7:release:build
     - job: regressiontests:package
 
-gromacs:clang-3.6:release:regressiontest:
+gromacs:clang-9:release:regressiontest:
   extends:
     - .gromacs:base:regressiontest
     - .rules:nightly-only-for-release
   stage: release-tests
-  image: gromacs/cmake-3.9.6-llvm-3.6-amdopencl-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   variables:
     BUILD_DIR: release-builds-clang
     REGRESSIONTEST_DOUBLE: "-double"
-    REGRESSIONTEST_OMP_RANK_NUMBER: 0
     REGRESSIONTEST_PARALLEL: "-np"
   tags:
     - k8s-scilifelab
   needs:
-    - job: gromacs:clang-3.6:release:build
+    - job: gromacs:clang-9:release:build
     - job: regressiontests:package
 
-gromacs:clang-8:release:regressiontest:
+gromacs:clang-8-cuda-10.1:release:regressiontest:
   extends:
     - .gromacs:base:regressiontest
     - .rules:nightly-only-for-release
   stage: release-tests
-  image: gromacs/cmake-3.15.7-llvm-8-cuda-10.1-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-8-cuda-10.1-openmpi-master
   variables:
     BUILD_DIR: release-builds-clang
     KUBERNETES_EXTENDED_RESOURCE_NAME: "nvidia.com/gpu"
@@ -851,6 +1071,21 @@ gromacs:clang-8:release:regressiontest:
   tags:
     - k8s-scilifelab
   needs:
-    - job: gromacs:clang-8:release:build
+    - job: gromacs:clang-8-cuda-10.1:release:build
     - job: regressiontests:package
 
+gromacs:oneapi-2021.1-beta08-opencl:release:regressiontest:
+  extends:
+    - .gromacs:base:regressiontest
+    - .use-oneapi:base
+    - .rules:nightly-only-for-release
+  stage: release-tests
+  image: gromacs/cmake-3.17.2-oneapi-2021.1-beta08-master
+  variables:
+    BUILD_DIR: release-builds-oneapi
+    REGRESSIONTEST_PME_RANK_NUMBER: 0
+    REGRESSIONTEST_TOTAL_RANK_NUMBER: 2
+    REGRESSIONTEST_OMP_RANK_NUMBER: 1
+  needs:
+    - job: gromacs:oneapi-2021.1-beta08-opencl:release:build
+    - job: regressiontests:package
index 0cc3391829c05a3cbee5a8de5fea9cd6264949a0..c750d2ca7315aeb8a72d1d1d26d9ebff90a62373 100644 (file)
@@ -5,9 +5,9 @@ clang-tidy:configure-push:
     - .gromacs:base:configure
     - .use-clang:base
     - .rules:basic-push
-  image: gromacs/cmake-3.11.4-llvm-8-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   variables:
-    COMPILER_MAJOR_VERSION: 8
+    COMPILER_MAJOR_VERSION: 9
     BUILD_DIR: build-clang-tidy
     CMAKE_EXTRA_OPTIONS: -DCLANG_TIDY=clang-tidy-$COMPILER_MAJOR_VERSION -DGMX_CLANG_TIDY=ON -DGMX_COMPILER_WARNINGS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
 
@@ -16,9 +16,9 @@ clang-tidy:configure-schedule:
     - .gromacs:base:configure
     - .use-clang:base
     - .rules:nightly-not-for-release
-  image: gromacs/cmake-3.11.4-llvm-8-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   variables:
-    COMPILER_MAJOR_VERSION: 8
+    COMPILER_MAJOR_VERSION: 9
     BUILD_DIR: build-clang-tidy
     CMAKE_EXTRA_OPTIONS: -DCLANG_TIDY=clang-tidy-$COMPILER_MAJOR_VERSION -DGMX_CLANG_TIDY=ON -DGMX_COMPILER_WARNINGS=ON
 
@@ -29,7 +29,7 @@ clang-tidy:build:
     - .variables:default
     - .rules:nightly-not-for-release
   stage: source-check
-  image: gromacs/cmake-3.11.4-llvm-8-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   needs:
     - job: clang-tidy:configure-schedule
   variables:
@@ -41,11 +41,11 @@ clang-tidy:test:
     - .variables:default
     - .rules:basic-push
   stage: source-check
-  image: gromacs/cmake-3.11.4-llvm-8-openmpi:2020
+  image: gromacs/cmake-3.15.7-llvm-9-openmpi-master
   needs:
     - job: clang-tidy:configure-push
   variables:
-    COMPILER_MAJOR_VERSION: 8
+    COMPILER_MAJOR_VERSION: 9
     BUILD_DIR: build-clang-tidy
     EXTRA_INSTALLS: clang-tidy-$COMPILER_MAJOR_VERSION
     KUBERNETES_CPU_LIMIT: 4
@@ -54,7 +54,8 @@ clang-tidy:test:
   script:
     # Make sure that a Python interpreter can be found for `/bin/env python`
     - test -x /usr/bin/python || update-alternatives --install /usr/bin/python python /usr/bin/python3 1
-    - REV=$(git fetch -q https://gitlab.com/gromacs/gromacs.git release-2020 && git show -s --pretty=format:"%h" `git merge-base FETCH_HEAD HEAD`)
+    # TODO (issue #3272) `master` is not appropriate for use on release-xxxx branches, how should we handle that?
+    - REV=$(git fetch -q https://gitlab.com/gromacs/gromacs.git master && git show -s --pretty=format:"%h" `git merge-base FETCH_HEAD HEAD`)
     - HEAD_REV=$(git show -s --pretty=format:"%h" HEAD)
     - if [[ "$REV" == "$HEAD_REV" ]] ; then
         REV="HEAD~1" ;
@@ -76,7 +77,7 @@ clang-format:
     - .rules:basic-push
   cache: {}
   stage: pre-build
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   variables:
     COMPILER_MAJOR_VERSION: 7
     KUBERNETES_CPU_LIMIT: 1
@@ -84,7 +85,8 @@ clang-format:
     KUBERNETES_MEMORY_REQUEST: 2Gi
     EXTRA_INSTALLS: clang-format-$COMPILER_MAJOR_VERSION
   script:
-    - REV=$(git fetch -q https://gitlab.com/gromacs/gromacs.git release-2020 && git show -s --pretty=format:"%h" `git merge-base FETCH_HEAD HEAD`)
+    # TODO (issue #3272) `master` is not appropriate for use on release-xxxx branches, how should we handle that?
+    - REV=$(git fetch -q https://gitlab.com/gromacs/gromacs.git master && git show -s --pretty=format:"%h" `git merge-base FETCH_HEAD HEAD`)
     - HEAD_REV=$(git show -s --pretty=format:"%h" HEAD)
     - if [[ "$REV" == "$HEAD_REV" ]] ; then
         REV="HEAD~1" ;
@@ -106,13 +108,14 @@ copyright-check:
     - .rules:basic-push
   cache: {}
   stage: pre-build
-  image: gromacs/ci-docs-llvm:2020
+  image: gromacs/ci-docs-llvm-master
   variables:
     KUBERNETES_CPU_LIMIT: 1
     KUBERNETES_CPU_REQUEST: 1
     KUBERNETES_MEMORY_REQUEST: 2Gi
   script:
-    - REV=$(git fetch -q https://gitlab.com/gromacs/gromacs.git release-2020 && git show -s --pretty=format:"%h" `git merge-base FETCH_HEAD HEAD`)
+    # TODO (issue #3272) `master` is not appropriate for use on release-xxxx branches, how should we handle that?
+    - REV=$(git fetch -q https://gitlab.com/gromacs/gromacs.git master && git show -s --pretty=format:"%h" `git merge-base FETCH_HEAD HEAD`)
     - HEAD_REV=$(git show -s --pretty=format:"%h" HEAD)
     - if [[ "$REV" == "$HEAD_REV" ]] ; then
         REV="HEAD~1" ;
index 3e7a0666d102746dca267d89f648ec6fd28bc96c..5715a736a136f11022ee2e128c2f7e720ded8e28 100644 (file)
@@ -12,6 +12,7 @@
   variables:
     KUBERNETES_CPU_LIMIT: 2
     KUBERNETES_CPU_REQUEST: 2
+    KUBERNETES_MEMORY_LIMIT: 2Gi
     KUBERNETES_MEMORY_REQUEST: 2Gi
     PY_UNIT_TEST_XML: $CI_PROJECT_DIR/py-JUnitTestResults.xml
     PY_MPI_UNIT_TEST_XML: $CI_PROJECT_DIR/py-mpi-JUnitTestResults.xml
     EXTRA_INSTALLS: "curl libbz2-dev libffi-dev liblzma-dev libncurses5-dev libncursesw5-dev libreadline-dev libsqlite3-dev libssl-dev llvm python-openssl tk-dev zlib1g-dev"
   script:
     - source $INSTALL_DIR/bin/GMXRC
-    - source $VENVPATH/bin/activate && INSTALL_DIR=$PWD/$INSTALL_DIR bash admin/ci-scripts/build-and-test-py-gmxapi-0.1.sh
+    - source $VENVPATH/bin/activate && INSTALL_DIR=$PWD/$INSTALL_DIR OMP_NUM_THREADS=1 bash admin/ci-scripts/build-and-test-py-gmxapi-0.1.sh
+  # Note that the XML output is concatenated when multiple reports are produced
+  # by a single job. If this presents problems, we may have to split the tests
+  # into multiple jobs. In particular, test names will appear once for MPI and
+  # once for non-MPI runs, which may confuse the test result parser.
   artifacts:
     reports:
       junit:
@@ -69,21 +74,24 @@ gmxapi-0.1:clang-8:py-3.8.2:
     VENVPATH: "/root/venv/py3.8"
     PY_VER: "3.8.2"
 
-# Base job definition for gmxapi 0.2 tests against GROMACS 2021 in clang-8 environments.
-.gmxapi-0.2:clang-8:gmx2021:
+.gmxapi-0.2:gcc-7:gmx2021:
   extends:
     - .variables:default
     - .use-clang:base
-  image: gromacs/cmake-3.15.7-llvm-8-intelopencl-openmpi:2020
+  image: gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master
   stage: test
   variables:
+    KUBERNETES_CPU_LIMIT: 2
+    KUBERNETES_CPU_REQUEST: 2
+    KUBERNETES_MEMORY_LIMIT: 2Gi
+    KUBERNETES_MEMORY_REQUEST: 2Gi
     PY_UNIT_TEST_XML: $CI_PROJECT_DIR/py-JUnitTestResults.xml
     PY_MPI_UNIT_TEST_XML: $CI_PROJECT_DIR/py-mpi-JUnitTestResults.xml
     PY_ACCEPTANCE_TEST_XML: $CI_PROJECT_DIR/gmxapi-acceptance-JUnitTestResults.xml
     PY_MPI_ACCEPTANCE_TEST_XML: $CI_PROJECT_DIR/gmxapi-acceptance-mpi-JUnitTestResults.xml
   script:
     - source $INSTALL_DIR/bin/GMXRC
-    - source $VENVPATH/bin/activate && INSTALL_DIR=$PWD/$INSTALL_DIR bash admin/ci-scripts/build-and-test-py-gmxapi-0.2.sh
+    - source $VENVPATH/bin/activate && INSTALL_DIR=$PWD/$INSTALL_DIR OMP_NUM_THREADS=1 bash admin/ci-scripts/build-and-test-py-gmxapi-0.2.sh
   artifacts:
     reports:
       junit:
@@ -97,28 +105,28 @@ gmxapi-0.1:clang-8:py-3.8.2:
     - k8s-scilifelab
   # The dependency means we need to use the same tag restriction as upstream.
   needs:
-    - job: gromacs:clang-8:build
+    - job: gromacs:gcc-7:build
       artifacts: true
 
-gmxapi-0.2:clang-8:gmx2021:py-3.6.10:
+gmxapi-0.2:gcc-7:gmx2021:py-3.6.10:
   extends:
-    - .gmxapi-0.2:clang-8:gmx2021
+    - .gmxapi-0.2:gcc-7:gmx2021
     - .rules:merge-requests:master
   variables:
     VENVPATH: "/root/venv/py3.6"
     PY_VER: "3.6.10"
 
-gmxapi-0.2:clang-8:gmx2021:py-3.7.7:
+gmxapi-0.2:gcc-7:gmx2021:py-3.7.7:
   extends:
-    - .gmxapi-0.2:clang-8:gmx2021
+    - .gmxapi-0.2:gcc-7:gmx2021
     - .rules:merge-requests:master
   variables:
     VENVPATH: "/root/venv/py3.7"
     PY_VER: "3.7.7"
 
-gmxapi-0.2:clang-8:gmx2021:py-3.8.2:
+gmxapi-0.2:gcc-7:gmx2021:py-3.8.2:
   extends:
-    - .gmxapi-0.2:clang-8:gmx2021
+    - .gmxapi-0.2:gcc-7:gmx2021
     - .rules:merge-requests:master
   variables:
     VENVPATH: "/root/venv/py3.8"
index 86bf421545e17fde9f3daff974aa13e80e12a16d..7bf1648b47f83a2508677c5ea10e9a723e99854d 100644 (file)
 .rules-element:if-post-merge-acceptance-or-mr-then-always: &if-post-merge-acceptance-or-mr-then-always
   if: '$CI_PIPELINE_SOURCE == "merge_request_event" ||
        ($CI_PIPELINE_SOURCE == "push" &&
-        $CI_COMMIT_REF_NAME == "release-2020")'
+        $CI_COMMIT_REF_NAME == "master")'
   when: always
 
 # Include job only for post submit push
 .rules-element:if-post-merge-acceptance-then-always: &if-post-merge-acceptance-then-always
   if: '$CI_PIPELINE_SOURCE == "push" &&
-       $CI_COMMIT_REF_NAME == "release-2020"'
+       $CI_COMMIT_REF_NAME == "master"'
   when: always
 
 # When composing a rule set, note that the first matching rule is applied.
index b54a1d8c3fd56f1d67d059f39668e0a08ff6e213..f7c7f60efb0c9c85d834ede7e3caf3da990953ff 100644 (file)
   variables:
     KUBERNETES_CPU_LIMIT: 2
     KUBERNETES_CPU_REQUEST: 2
+    KUBERNETES_MEMORY_LIMIT: 2Gi
     KUBERNETES_MEMORY_REQUEST: 2Gi
     PLUGIN_TEST_XML: $CI_PROJECT_DIR/py-JUnitTestResults.xml
     PLUGIN_MPI_TEST_XML: $CI_PROJECT_DIR/py-mpi-JUnitTestResults.xml
     EXTRA_INSTALLS: "curl libbz2-dev libffi-dev liblzma-dev libncurses5-dev libncursesw5-dev libreadline-dev libsqlite3-dev libssl-dev llvm python-openssl tk-dev zlib1g-dev"
   script:
     - source $INSTALL_DIR/bin/GMXRC
-    - source $VENVPATH/bin/activate && INSTALL_DIR=$PWD/$INSTALL_DIR bash admin/ci-scripts/build-and-test-sample_restraint-2020.sh
+    - source $VENVPATH/bin/activate && INSTALL_DIR=$PWD/$INSTALL_DIR OMP_NUM_THREADS=1 bash admin/ci-scripts/build-and-test-sample_restraint-2020.sh
   artifacts:
     reports:
       junit:
index 1c99c8fb97dc51c3d182505651c311fd192e288d..25d2fc6734400123abe52aac913dfcbc5a7a8af6 100644 (file)
@@ -1,17 +1,21 @@
 # Base job definition for sample_restraint tests against GROMACS 2021.
-.sample_restraint:clang-8:gmx2021:
+.sample_restraint:gcc-7:gmx2021:
   extends:
     - .variables:default
     - .use-clang:base
-  image: gromacs/cmake-3.15.7-llvm-8-intelopencl-openmpi:2020
+  image: gromacs/cmake-3.13.0-gcc-7-amdopencl-clfft-openmpi-master
   stage: test
   variables:
+    KUBERNETES_CPU_LIMIT: 2
+    KUBERNETES_CPU_REQUEST: 2
+    KUBERNETES_MEMORY_LIMIT: 2Gi
+    KUBERNETES_MEMORY_REQUEST: 2Gi
     PLUGIN_TEST_XML: $CI_PROJECT_DIR/py-JUnitTestResults.xml
     PLUGIN_MPI_TEST_XML: $CI_PROJECT_DIR/py-mpi-JUnitTestResults.xml
     EXTRA_INSTALLS: "curl libbz2-dev libffi-dev liblzma-dev libncurses5-dev libncursesw5-dev libreadline-dev libsqlite3-dev libssl-dev llvm python-openssl tk-dev zlib1g-dev"
   script:
     - source $INSTALL_DIR/bin/GMXRC
-    - source $VENVPATH/bin/activate && INSTALL_DIR=$PWD/$INSTALL_DIR bash admin/ci-scripts/build-and-test-sample_restraint-2020.sh
+    - source $VENVPATH/bin/activate && INSTALL_DIR=$PWD/$INSTALL_DIR OMP_NUM_THREADS=1 bash admin/ci-scripts/build-and-test-sample_restraint-2020.sh
   artifacts:
     reports:
       junit:
     - k8s-scilifelab
   # The dependency means we need to use the same tag restriction as upstream.
   needs:
-    - job: gromacs:clang-8:build
+    - job: gromacs:gcc-7:build
       artifacts: true
 
-sample_restraint:clang-8:gmx2021:py-3.6.10:
+sample_restraint:gcc-7:gmx2021:py-3.6.10:
   extends:
-    - .sample_restraint:clang-8:gmx2021
+    - .sample_restraint:gcc-7:gmx2021
     - .rules:merge-requests:master
   variables:
     VENVPATH: "/root/venv/py3.6"
     PY_VER: "3.6.10"
 
-sample_restraint:clang-8:gmx2021:py-3.7.7:
+sample_restraint:gcc-7:gmx2021:py-3.7.7:
   extends:
-    - .sample_restraint:clang-8:gmx2021
+    - .sample_restraint:gcc-7:gmx2021
     - .rules:merge-requests:master
   variables:
     VENVPATH: "/root/venv/py3.7"
     PY_VER: "3.7.7"
 
-sample_restraint:clang-8:gmx2021:py-3.8.2:
+sample_restraint:gcc-7:gmx2021:py-3.8.2:
   extends:
-    - .sample_restraint:clang-8:gmx2021
+    - .sample_restraint:gcc-7:gmx2021
     - .rules:merge-requests:master
   variables:
     VENVPATH: "/root/venv/py3.8"
diff --git a/admin/lsan-suppressions.txt b/admin/lsan-suppressions.txt
new file mode 100644 (file)
index 0000000..f04a688
--- /dev/null
@@ -0,0 +1,55 @@
+# Known leaks - leak keywords are the lowest-level function/method of the leak call stack that is not reused by other
+# routines.
+leak:add_to_list
+leak:atoms_to_constraints
+leak:atoms_to_settles
+leak:balance_fep_lists
+leak:cpp_opts
+leak:dd_init_bondeds
+leak:dd_make_local_constraints
+leak:dd_move_f
+leak:dd_partition_system
+leak:diagonalize_orires_tensors
+leak:do_cpte_matrices
+leak:do_edsam
+leak:do_inputrec
+leak:do_single_flood
+leak:get_ir
+leak:get_zone_pulse_cgs
+leak:gmx::DomainDecompositionBuilder::Impl::build
+leak:gmx_check
+leak:gmx_make_edi
+leak:gmx_mtop_ilistloop_init
+leak:gmx_pme_init
+leak:gmx_pme_receive_f
+leak:init_buffer_flags
+leak:init_disres
+leak:init_dfhist_state
+leak:init_df_history
+leak:init_edsam
+leak:init_ekinstate
+leak:init_interaction_const
+leak:init_orires
+leak:init_rot
+leak:init_swapcoords
+leak:make_bondeds_zone
+leak:make_dd_indices
+leak:make_exclusions_zone
+leak:make_fep_list
+leak:make_ljpme_c6grid
+leak:make_pull_groups
+leak:make_reverse_top
+leak:make_rotation_groups
+leak:make_swap_groups
+leak:make_tables
+leak:mdoutf_write_to_trajectory_files
+# Stack trace does not report a function beyond gmx_srenew_impl, so the file is suppressed instead.
+leak:pairlist.cpp
+leak:read_rotparams
+leak:set_ddgrid_parameters
+leak:set_reference_positions
+leak:set_state_entries
+leak:visitOption
+
+# External suppressions
+leak:xdr_string
old mode 100644 (file)
new mode 100755 (executable)
similarity index 63%
rename from admin/builds/update-regtest-hash.py
rename to admin/split_copyright_years.sh
index ee73d0e..4e583c3
@@ -1,7 +1,8 @@
+#!/bin/bash
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2016, by the GROMACS development team, led by
+# Copyright (c) 2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-import json
-
-extra_options = {
-    'md5sum': Option.string
-}
-
-def do_build(context):
-    info_path = 'cmake/gmxVersionInfo.cmake'
-    cmd = [context.env.cmake_command, '-P', info_path]
-    info_json = context.run_cmd(cmd, use_output=True)
-    values = json.loads(info_json)
-    old_md5sum = values['regressiontest-md5sum']
-    new_md5sum = context.opts.md5sum
-    if new_md5sum != old_md5sum:
-        context.replace_in_file(info_path, r'set\(REGRESSIONTEST_MD5SUM "(\w*)"',
-                lambda x: do_replacement(x, new_md5sum))
-        context.workspace.upload_revision(project=Project.GROMACS, file_glob=info_path)
+# Finds copyright statements that have more than five years, such as
+#
+#   ... Copyright (c) 2012,2013,2016,2017,2018,2019, by the GROMACS ...
+#
+# and splits them into multiple lines, like
+#
+#   ... Copyright (c) 2012,2013,2016,2017,2018, by the GROMACS development team.
+#   ... Copyright (c) 2019, by the GROMACS development team, led by
+#
+# so that the copyright checker recognizes the second line as one that can be extended.
+# This will need to be re-rerun every few years as the length of the last line grows.
 
-def do_replacement(match, new_md5sum):
-    result = match.group(0)
-    start = match.start(1) - match.start(0)
-    end = match.end(1) - match.end(0)
-    return result[:start] + new_md5sum + result[end:]
+sed -i 's/\( \?.\) Copyright (c) \([0-9][0-9][0-9][0-9],[0-9][0-9][0-9][0-9],[0-9][0-9][0-9][0-9],[0-9][0-9][0-9][0-9],[0-9][0-9][0-9][0-9]\),\([0-9][0-9][0-9][0-9],.*,\) by the GROMACS/\1 Copyright (c) \2 by the GROMACS development team.\n\1 Copyright (c) \3 by the GROMACS/g' $(git grep -l "by the GROMACS")
diff --git a/admin/ubsan-suppressions.txt b/admin/ubsan-suppressions.txt
new file mode 100644 (file)
index 0000000..055ba22
--- /dev/null
@@ -0,0 +1,9 @@
+# Add UBSAN suppressions here, with a leading comment about how and
+# why it is appropriate, and any circumstances about when it should be
+# removed.
+
+# This is only a problem when using the built-in XDR implementation of
+# xdr_vector. It supplies an extra argument needed for xdr_string
+# which is unused by other xdr_* primitives like xdr_float. The issue
+# is harmless and only active when not using a system XDR library.
+function:xdr_vector
diff --git a/admin/uncrustify.cfg b/admin/uncrustify.cfg
deleted file mode 100644 (file)
index 24e80a0..0000000
+++ /dev/null
@@ -1,1586 +0,0 @@
-# Uncrustify 0.59
-
-#
-# General options
-#
-
-# The type of line endings
-newlines                                 = auto     # auto/lf/crlf/cr
-
-# The original size of tabs in the input
-input_tab_size                           = 4        # number
-
-# The size of tabs in the output (only used if align_with_tabs=true)
-output_tab_size                          = 4        # number
-
-# The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn)
-string_escape_char                       = 92       # number
-
-# Alternate string escape char for Pawn. Only works right before the quote char.
-string_escape_char2                      = 0        # number
-
-# Allow interpreting '>=' and '>>=' as part of a template in 'void f(list<list<B>>=val);'.
-# If true (default), 'assert(x<0 && y>=3)' will be broken.
-# Improvements to template detection may make this option obsolete.
-tok_split_gte                            = false    # false/true
-
-# Control what to do with the UTF-8 BOM (recommend 'remove')
-utf8_bom                                 = ignore   # ignore/add/remove/force
-
-# If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8
-utf8_byte                                = false    # false/true
-
-# Force the output encoding to UTF-8
-utf8_force                               = false    # false/true
-
-#
-# Indenting
-#
-
-# The number of columns to indent per level.
-# Usually 2, 3, 4, or 8.
-indent_columns                           = 4        # number
-
-# The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents.
-# For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level
-indent_continue                          = 0        # number
-
-# How to use tabs when indenting code
-# 0=spaces only
-# 1=indent with tabs to brace level, align with spaces
-# 2=indent and align with tabs, using spaces when not on a tabstop
-indent_with_tabs                         = 0        # number
-
-# Comments that are not a brace level are indented with tabs on a tabstop.
-# Requires indent_with_tabs=2. If false, will use spaces.
-indent_cmt_with_tabs                     = false    # false/true
-
-# Whether to indent strings broken by '\' so that they line up
-indent_align_string                      = false    # false/true
-
-# The number of spaces to indent multi-line XML strings.
-# Requires indent_align_string=True
-indent_xml_string                        = 0        # number
-
-# Spaces to indent '{' from level
-indent_brace                             = 0        # number
-
-# Whether braces are indented to the body level
-indent_braces                            = false    # false/true
-
-# Disabled indenting function braces if indent_braces is true
-indent_braces_no_func                    = true     # false/true
-
-# Disabled indenting class braces if indent_braces is true
-indent_braces_no_class                   = true     # false/true
-
-# Disabled indenting struct braces if indent_braces is true
-indent_braces_no_struct                  = true     # false/true
-
-# Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc.
-indent_brace_parent                      = false    # false/true
-
-# Whether the 'namespace' body is indented
-indent_namespace                         = false    # false/true
-
-# The number of spaces to indent a namespace block
-indent_namespace_level                   = 0        # number
-
-# If the body of the namespace is longer than this number, it won't be indented.
-# Requires indent_namespace=true. Default=0 (no limit)
-indent_namespace_limit                   = 0        # number
-
-# Whether the 'extern "C"' body is indented
-indent_extern                            = false    # false/true
-
-# Whether the 'class' body is indented
-indent_class                             = false    # false/true
-
-# Whether to indent the stuff after a leading class colon
-indent_class_colon                       = true    # false/true
-
-# Virtual indent from the ':' for member initializers. Default is 2
-indent_ctor_init_leading                 = 2        # number
-
-# Additional indenting for constructor initializer list
-indent_ctor_init                         = 0        # number
-
-# False=treat 'else\nif' as 'else if' for indenting purposes
-# True=indent the 'if' one level
-indent_else_if                           = false    # false/true
-
-# Amount to indent variable declarations after a open brace. neg=relative, pos=absolute
-indent_var_def_blk                       = 0        # number
-
-# Indent continued variable declarations instead of aligning.
-indent_var_def_cont                      = false    # false/true
-
-# True:  force indentation of function definition to start in column 1
-# False: use the default behavior
-indent_func_def_force_col1               = false    # false/true
-
-# True:  indent continued function call parameters one indent level
-# False: align parameters under the open paren
-indent_func_call_param                   = false    # false/true
-
-# Same as indent_func_call_param, but for function defs
-indent_func_def_param                    = false    # false/true
-
-# Same as indent_func_call_param, but for function protos
-indent_func_proto_param                  = false    # false/true
-
-# Same as indent_func_call_param, but for class declarations
-indent_func_class_param                  = false    # false/true
-
-# Same as indent_func_call_param, but for class variable constructors
-indent_func_ctor_var_param               = false    # false/true
-
-# Same as indent_func_call_param, but for templates
-indent_template_param                    = false    # false/true
-
-# Double the indent for indent_func_xxx_param options
-indent_func_param_double                 = true     # false/true
-
-# Indentation column for standalone 'const' function decl/proto qualifier
-indent_func_const                        = 0        # number
-
-# Indentation column for standalone 'throw' function decl/proto qualifier
-indent_func_throw                        = 0        # number
-
-# The number of spaces to indent a continued '->' or '.'
-# Usually set to 0, 1, or indent_columns.
-indent_member                            = 4        # number
-
-# Spaces to indent single line ('//') comments on lines before code
-indent_sing_line_comments                = 0        # number
-
-# If set, will indent trailing single line ('//') comments relative
-# to the code instead of trying to keep the same absolute column
-indent_relative_single_line_comments     = false    # false/true
-
-# Spaces to indent 'case' from 'switch'
-# Usually 0 or indent_columns.
-indent_switch_case                       = 4        # number
-
-# Spaces to shift the 'case' line, without affecting any other lines
-# Usually 0.
-indent_case_shift                        = 0        # number
-
-# Spaces to indent '{' from 'case'.
-# By default, the brace will appear under the 'c' in case.
-# Usually set to 0 or indent_columns.
-indent_case_brace                        = 0        # number
-
-# Whether to indent comments found in first column
-indent_col1_comment                      = false    # false/true
-
-# How to indent goto labels
-#  >0 : absolute column where 1 is the leftmost column
-#  <=0 : subtract from brace indent
-indent_label                             = 1        # number
-
-# Same as indent_label, but for access specifiers that are followed by a colon
-indent_access_spec                       = 1        # number
-
-# Indent the code after an access specifier by one level.
-# If set, this option forces 'indent_access_spec=0'
-indent_access_spec_body                  = true     # false/true
-
-# If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended)
-indent_paren_nl                          = false    # false/true
-
-# Controls the indent of a close paren after a newline.
-# 0: Indent to body level
-# 1: Align under the open paren
-# 2: Indent to the brace level
-indent_paren_close                       = 0        # number
-
-# Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren
-indent_comma_paren                       = false    # false/true
-
-# Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren
-indent_bool_paren                        = false    # false/true
-
-# If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones
-indent_first_bool_expr                   = false    # false/true
-
-# If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended)
-indent_square_nl                         = false    # false/true
-
-# Don't change the relative indent of ESQL/C 'EXEC SQL' bodies
-indent_preserve_sql                      = false    # false/true
-
-# Align continued statements at the '='. Default=True
-# If FALSE or the '=' is followed by a newline, the next line is indent one tab.
-indent_align_assign                      = false    # false/true
-
-# Indent OC blocks at brace level instead of usual rules.
-indent_oc_block                          = false    # false/true
-
-# Minimum indent for subsequent parameters
-indent_oc_msg_colon                      = 0        # number
-
-#
-# Spacing options
-#
-
-# Add or remove space around arithmetic operator '+', '-', '/', '*', etc
-sp_arith                                 = add      # ignore/add/remove/force
-
-# Add or remove space around assignment operator '=', '+=', etc
-sp_assign                                = add      # ignore/add/remove/force
-
-# Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign
-sp_cpp_lambda_assign                     = ignore   # ignore/add/remove/force
-
-# Add or remove space after the capture specification in C++11 lambda.
-sp_cpp_lambda_paren                      = ignore   # ignore/add/remove/force
-
-# Add or remove space around assignment operator '=' in a prototype
-sp_assign_default                        = add      # ignore/add/remove/force
-
-# Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign.
-sp_before_assign                         = add      # ignore/add/remove/force
-
-# Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign.
-sp_after_assign                          = add      # ignore/add/remove/force
-
-# Add or remove space around assignment '=' in enum
-sp_enum_assign                           = add      # ignore/add/remove/force
-
-# Add or remove space before assignment '=' in enum. Overrides sp_enum_assign.
-sp_enum_before_assign                    = ignore   # ignore/add/remove/force
-
-# Add or remove space after assignment '=' in enum. Overrides sp_enum_assign.
-sp_enum_after_assign                     = ignore   # ignore/add/remove/force
-
-# Add or remove space around preprocessor '##' concatenation operator. Default=Add
-sp_pp_concat                             = add      # ignore/add/remove/force
-
-# Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator.
-sp_pp_stringify                          = ignore   # ignore/add/remove/force
-
-# Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'.
-sp_before_pp_stringify                   = ignore   # ignore/add/remove/force
-
-# Add or remove space around boolean operators '&&' and '||'
-sp_bool                                  = add      # ignore/add/remove/force
-
-# Add or remove space around compare operator '<', '>', '==', etc
-sp_compare                               = add      # ignore/add/remove/force
-
-# Add or remove space inside '(' and ')'
-sp_inside_paren                          = ignore   # ignore/add/remove/force
-
-# Add or remove space between nested parens
-sp_paren_paren                           = ignore   # ignore/add/remove/force
-
-# Whether to balance spaces inside nested parens
-sp_balance_nested_parens                 = false    # false/true
-
-# Add or remove space between ')' and '{'
-sp_paren_brace                           = ignore   # ignore/add/remove/force
-
-# Add or remove space before pointer star '*'
-sp_before_ptr_star                       = ignore   # ignore/add/remove/force
-
-# Add or remove space before pointer star '*' that isn't followed by a variable name
-# If set to 'ignore', sp_before_ptr_star is used instead.
-sp_before_unnamed_ptr_star               = ignore   # ignore/add/remove/force
-
-# Add or remove space between pointer stars '*'
-sp_between_ptr_star                      = ignore   # ignore/add/remove/force
-
-# Add or remove space after pointer star '*', if followed by a word.
-sp_after_ptr_star                        = ignore   # ignore/add/remove/force
-
-# Add or remove space after a pointer star '*', if followed by a func proto/def.
-sp_after_ptr_star_func                   = ignore   # ignore/add/remove/force
-
-# Add or remove space after a pointer star '*', if followed by an open paren (function types).
-sp_ptr_star_paren                        = ignore   # ignore/add/remove/force
-
-# Add or remove space before a pointer star '*', if followed by a func proto/def.
-sp_before_ptr_star_func                  = ignore   # ignore/add/remove/force
-
-# Add or remove space before a reference sign '&'
-sp_before_byref                          = add      # ignore/add/remove/force
-
-# Add or remove space before a reference sign '&' that isn't followed by a variable name
-# If set to 'ignore', sp_before_byref is used instead.
-sp_before_unnamed_byref                  = ignore   # ignore/add/remove/force
-
-# Add or remove space after reference sign '&', if followed by a word.
-sp_after_byref                           = ignore      # ignore/add/remove/force
-
-# Add or remove space after a reference sign '&', if followed by a func proto/def.
-sp_after_byref_func                      = ignore   # ignore/add/remove/force
-
-# Add or remove space before a reference sign '&', if followed by a func proto/def.
-sp_before_byref_func                     = ignore   # ignore/add/remove/force
-
-# Add or remove space between type and word. Default=Force
-sp_after_type                            = ignore   # ignore/add/remove/force
-
-# Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('.
-sp_before_template_paren                 = ignore   # ignore/add/remove/force
-
-# Add or remove space in 'template <' vs 'template<'.
-# If set to ignore, sp_before_angle is used.
-sp_template_angle                        = add      # ignore/add/remove/force
-
-# Add or remove space before '<>'
-sp_before_angle                          = ignore   # ignore/add/remove/force
-
-# Add or remove space inside '<' and '>'
-sp_inside_angle                          = ignore   # ignore/add/remove/force
-
-# Add or remove space after '<>'
-sp_after_angle                           = ignore   # ignore/add/remove/force
-
-# Add or remove space between '<>' and '(' as found in 'new List<byte>();'
-sp_angle_paren                           = ignore   # ignore/add/remove/force
-
-# Add or remove space between '<>' and a word as in 'List<byte> m;'
-sp_angle_word                            = ignore   # ignore/add/remove/force
-
-# Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add
-sp_angle_shift                           = add      # ignore/add/remove/force
-
-# Permit removal of the space between '>>' in 'foo<bar<int> >' (C++11 only). Default=False
-# sp_angle_shift cannot remove the space without this option.
-sp_permit_cpp11_shift                    = false    # false/true
-
-# Add or remove space before '(' of 'if', 'for', 'switch', and 'while'
-sp_before_sparen                         = add      # ignore/add/remove/force
-
-# Add or remove space inside if-condition '(' and ')'
-sp_inside_sparen                         = remove   # ignore/add/remove/force
-
-# Add or remove space before if-condition ')'. Overrides sp_inside_sparen.
-sp_inside_sparen_close                   = ignore   # ignore/add/remove/force
-
-# Add or remove space before if-condition '('. Overrides sp_inside_sparen.
-sp_inside_sparen_open                    = ignore   # ignore/add/remove/force
-
-# Add or remove space after ')' of 'if', 'for', 'switch', and 'while'
-sp_after_sparen                          = ignore   # ignore/add/remove/force
-
-# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while'
-sp_sparen_brace                          = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'invariant' and '(' in the D language.
-sp_invariant_paren                       = ignore   # ignore/add/remove/force
-
-# Add or remove space after the ')' in 'invariant (C) c' in the D language.
-sp_after_invariant_paren                 = ignore   # ignore/add/remove/force
-
-# Add or remove space before empty statement ';' on 'if', 'for' and 'while'
-sp_special_semi                          = ignore   # ignore/add/remove/force
-
-# Add or remove space before ';'. Default=Remove
-sp_before_semi                           = remove   # ignore/add/remove/force
-
-# Add or remove space before ';' in non-empty 'for' statements
-sp_before_semi_for                       = ignore   # ignore/add/remove/force
-
-# Add or remove space before a semicolon of an empty part of a for statement.
-sp_before_semi_for_empty                 = ignore   # ignore/add/remove/force
-
-# Add or remove space after ';', except when followed by a comment. Default=Add
-sp_after_semi                            = add      # ignore/add/remove/force
-
-# Add or remove space after ';' in non-empty 'for' statements. Default=Force
-sp_after_semi_for                        = force    # ignore/add/remove/force
-
-# Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; <here> ).
-sp_after_semi_for_empty                  = ignore   # ignore/add/remove/force
-
-# Add or remove space before '[' (except '[]')
-sp_before_square                         = ignore   # ignore/add/remove/force
-
-# Add or remove space before '[]'
-sp_before_squares                        = ignore   # ignore/add/remove/force
-
-# Add or remove space inside a non-empty '[' and ']'
-sp_inside_square                         = ignore   # ignore/add/remove/force
-
-# Add or remove space after ','
-sp_after_comma                           = add      # ignore/add/remove/force
-
-# Add or remove space before ','
-sp_before_comma                          = remove   # ignore/add/remove/force
-
-# Add or remove space between an open paren and comma: '(,' vs '( ,'
-sp_paren_comma                           = force    # ignore/add/remove/force
-
-# Add or remove space before the variadic '...' when preceded by a non-punctuator
-sp_before_ellipsis                       = ignore   # ignore/add/remove/force
-
-# Add or remove space after class ':'
-sp_after_class_colon                     = add      # ignore/add/remove/force
-
-# Add or remove space before class ':'
-sp_before_class_colon                    = add      # ignore/add/remove/force
-
-# Add or remove space before case ':'. Default=Remove
-sp_before_case_colon                     = remove   # ignore/add/remove/force
-
-# Add or remove space between 'operator' and operator sign
-sp_after_operator                        = remove   # ignore/add/remove/force
-
-# Add or remove space between the operator symbol and the open paren, as in 'operator ++('
-sp_after_operator_sym                    = ignore   # ignore/add/remove/force
-
-# Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a'
-sp_after_cast                            = ignore   # ignore/add/remove/force
-
-# Add or remove spaces inside cast parens
-sp_inside_paren_cast                     = ignore   # ignore/add/remove/force
-
-# Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)'
-sp_cpp_cast_paren                        = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'sizeof' and '('
-sp_sizeof_paren                          = ignore   # ignore/add/remove/force
-
-# Add or remove space after the tag keyword (Pawn)
-sp_after_tag                             = ignore   # ignore/add/remove/force
-
-# Add or remove space inside enum '{' and '}'
-sp_inside_braces_enum                    = ignore   # ignore/add/remove/force
-
-# Add or remove space inside struct/union '{' and '}'
-sp_inside_braces_struct                  = ignore   # ignore/add/remove/force
-
-# Add or remove space inside '{' and '}'
-sp_inside_braces                         = ignore   # ignore/add/remove/force
-
-# Add or remove space inside '{}'
-sp_inside_braces_empty                   = ignore   # ignore/add/remove/force
-
-# Add or remove space between return type and function name
-# A minimum of 1 is forced except for pointer return types.
-sp_type_func                             = ignore   # ignore/add/remove/force
-
-# Add or remove space between function name and '(' on function declaration
-sp_func_proto_paren                      = ignore   # ignore/add/remove/force
-
-# Add or remove space between function name and '(' on function definition
-sp_func_def_paren                        = ignore   # ignore/add/remove/force
-
-# Add or remove space inside empty function '()'
-sp_inside_fparens                        = ignore   # ignore/add/remove/force
-
-# Add or remove space inside function '(' and ')'
-sp_inside_fparen                         = ignore   # ignore/add/remove/force
-
-# Add or remove space inside the first parens in the function type: 'void (*x)(...)'
-sp_inside_tparen                         = ignore   # ignore/add/remove/force
-
-# Add or remove between the parens in the function type: 'void (*x)(...)'
-sp_after_tparen_close                    = ignore   # ignore/add/remove/force
-
-# Add or remove space between ']' and '(' when part of a function call.
-sp_square_fparen                         = ignore   # ignore/add/remove/force
-
-# Add or remove space between ')' and '{' of function
-sp_fparen_brace                          = ignore   # ignore/add/remove/force
-
-# Add or remove space between function name and '(' on function calls
-sp_func_call_paren                       = ignore   # ignore/add/remove/force
-
-# Add or remove space between function name and '()' on function calls without parameters.
-# If set to 'ignore' (the default), sp_func_call_paren is used.
-sp_func_call_paren_empty                 = ignore   # ignore/add/remove/force
-
-# Add or remove space between the user function name and '(' on function calls
-# You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file.
-sp_func_call_user_paren                  = ignore   # ignore/add/remove/force
-
-# Add or remove space between a constructor/destructor and the open paren
-sp_func_class_paren                      = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'return' and '('
-sp_return_paren                          = ignore   # ignore/add/remove/force
-
-# Add or remove space between '__attribute__' and '('
-sp_attribute_paren                       = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'defined' and '(' in '#if defined (FOO)'
-sp_defined_paren                         = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'throw' and '(' in 'throw (something)'
-sp_throw_paren                           = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'throw' and anything other than '(' as in '@throw [...];'
-sp_after_throw                           = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'catch' and '(' in 'catch (something) { }'
-# If set to ignore, sp_before_sparen is used.
-sp_catch_paren                           = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'version' and '(' in 'version (something) { }' (D language)
-# If set to ignore, sp_before_sparen is used.
-sp_version_paren                         = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language)
-# If set to ignore, sp_before_sparen is used.
-sp_scope_paren                           = ignore   # ignore/add/remove/force
-
-# Add or remove space between macro and value
-sp_macro                                 = ignore   # ignore/add/remove/force
-
-# Add or remove space between macro function ')' and value
-sp_macro_func                            = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'else' and '{' if on the same line
-sp_else_brace                            = ignore   # ignore/add/remove/force
-
-# Add or remove space between '}' and 'else' if on the same line
-sp_brace_else                            = ignore   # ignore/add/remove/force
-
-# Add or remove space between '}' and the name of a typedef on the same line
-sp_brace_typedef                         = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'catch' and '{' if on the same line
-sp_catch_brace                           = ignore   # ignore/add/remove/force
-
-# Add or remove space between '}' and 'catch' if on the same line
-sp_brace_catch                           = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'finally' and '{' if on the same line
-sp_finally_brace                         = ignore   # ignore/add/remove/force
-
-# Add or remove space between '}' and 'finally' if on the same line
-sp_brace_finally                         = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'try' and '{' if on the same line
-sp_try_brace                             = ignore   # ignore/add/remove/force
-
-# Add or remove space between get/set and '{' if on the same line
-sp_getset_brace                          = ignore   # ignore/add/remove/force
-
-# Add or remove space before the '::' operator
-sp_before_dc                             = ignore   # ignore/add/remove/force
-
-# Add or remove space after the '::' operator
-sp_after_dc                              = ignore   # ignore/add/remove/force
-
-# Add or remove around the D named array initializer ':' operator
-sp_d_array_colon                         = ignore   # ignore/add/remove/force
-
-# Add or remove space after the '!' (not) operator. Default=Remove
-sp_not                                   = remove   # ignore/add/remove/force
-
-# Add or remove space after the '~' (invert) operator. Default=Remove
-sp_inv                                   = remove   # ignore/add/remove/force
-
-# Add or remove space after the '&' (address-of) operator. Default=Remove
-# This does not affect the spacing after a '&' that is part of a type.
-sp_addr                                  = remove   # ignore/add/remove/force
-
-# Add or remove space around the '.' or '->' operators. Default=Remove
-sp_member                                = remove   # ignore/add/remove/force
-
-# Add or remove space after the '*' (dereference) operator. Default=Remove
-# This does not affect the spacing after a '*' that is part of a type.
-sp_deref                                 = remove   # ignore/add/remove/force
-
-# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove
-sp_sign                                  = remove   # ignore/add/remove/force
-
-# Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove
-sp_incdec                                = remove   # ignore/add/remove/force
-
-# Add or remove space before a backslash-newline at the end of a line. Default=Add
-sp_before_nl_cont                        = add      # ignore/add/remove/force
-
-# Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;'
-sp_after_oc_scope                        = ignore   # ignore/add/remove/force
-
-# Add or remove space after the colon in message specs
-# '-(int) f:(int) x;' vs '-(int) f: (int) x;'
-sp_after_oc_colon                        = ignore   # ignore/add/remove/force
-
-# Add or remove space before the colon in message specs
-# '-(int) f: (int) x;' vs '-(int) f : (int) x;'
-sp_before_oc_colon                       = ignore   # ignore/add/remove/force
-
-# Add or remove space after the colon in immutable dictionary expression
-# 'NSDictionary *test = @{@"foo" :@"bar"};'
-sp_after_oc_dict_colon                   = ignore   # ignore/add/remove/force
-
-# Add or remove space before the colon in immutable dictionary expression
-# 'NSDictionary *test = @{@"foo" :@"bar"};'
-sp_before_oc_dict_colon                  = ignore   # ignore/add/remove/force
-
-# Add or remove space after the colon in message specs
-# '[object setValue:1];' vs '[object setValue: 1];'
-sp_after_send_oc_colon                   = ignore   # ignore/add/remove/force
-
-# Add or remove space before the colon in message specs
-# '[object setValue:1];' vs '[object setValue :1];'
-sp_before_send_oc_colon                  = ignore   # ignore/add/remove/force
-
-# Add or remove space after the (type) in message specs
-# '-(int)f: (int) x;' vs '-(int)f: (int)x;'
-sp_after_oc_type                         = ignore   # ignore/add/remove/force
-
-# Add or remove space after the first (type) in message specs
-# '-(int) f:(int)x;' vs '-(int)f:(int)x;'
-sp_after_oc_return_type                  = ignore   # ignore/add/remove/force
-
-# Add or remove space between '@selector' and '('
-# '@selector(msgName)' vs '@selector (msgName)'
-# Also applies to @protocol() constructs
-sp_after_oc_at_sel                       = ignore   # ignore/add/remove/force
-
-# Add or remove space between '@selector(x)' and the following word
-# '@selector(foo) a:' vs '@selector(foo)a:'
-sp_after_oc_at_sel_parens                = ignore   # ignore/add/remove/force
-
-# Add or remove space inside '@selector' parens
-# '@selector(foo)' vs '@selector( foo )'
-# Also applies to @protocol() constructs
-sp_inside_oc_at_sel_parens               = ignore   # ignore/add/remove/force
-
-# Add or remove space before a block pointer caret
-# '^int (int arg){...}' vs. ' ^int (int arg){...}'
-sp_before_oc_block_caret                 = ignore   # ignore/add/remove/force
-
-# Add or remove space after a block pointer caret
-# '^int (int arg){...}' vs. '^ int (int arg){...}'
-sp_after_oc_block_caret                  = ignore   # ignore/add/remove/force
-
-# Add or remove space between the receiver and selector in a message.
-# '[receiver selector ...]'
-sp_after_oc_msg_receiver                 = ignore   # ignore/add/remove/force
-
-# Add or remove space after @property.
-sp_after_oc_property                     = ignore   # ignore/add/remove/force
-
-# Add or remove space around the ':' in 'b ? t : f'
-sp_cond_colon                            = add      # ignore/add/remove/force
-
-# Add or remove space around the '?' in 'b ? t : f'
-sp_cond_question                         = add      # ignore/add/remove/force
-
-# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here.
-sp_case_label                            = ignore   # ignore/add/remove/force
-
-# Control the space around the D '..' operator.
-sp_range                                 = ignore   # ignore/add/remove/force
-
-# Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java)
-sp_after_for_colon                       = ignore   # ignore/add/remove/force
-
-# Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java)
-sp_before_for_colon                      = ignore   # ignore/add/remove/force
-
-# Control the spacing in 'extern (C)' (D)
-sp_extern_paren                          = ignore   # ignore/add/remove/force
-
-# Control the space after the opening of a C++ comment '// A' vs '//A'
-sp_cmt_cpp_start                         = ignore   # ignore/add/remove/force
-
-# Controls the spaces between #else or #endif and a trailing comment
-sp_endif_cmt                             = ignore   # ignore/add/remove/force
-
-# Controls the spaces after 'new', 'delete', and 'delete[]'
-sp_after_new                             = ignore   # ignore/add/remove/force
-
-# Controls the spaces before a trailing or embedded comment
-sp_before_tr_emb_cmt                     = ignore   # ignore/add/remove/force
-
-# Number of spaces before a trailing or embedded comment
-sp_num_before_tr_emb_cmt                 = 0        # number
-
-# Control space between a Java annotation and the open paren.
-sp_annotation_paren                      = ignore   # ignore/add/remove/force
-
-#
-# Code alignment (not left column spaces/tabs)
-#
-
-# Whether to keep non-indenting tabs
-align_keep_tabs                          = false    # false/true
-
-# Whether to keep whitespace not required for alignment
-align_keep_extra_space                  = false    # false/true
-
-# Whether to use tabs for aligning
-align_with_tabs                          = false    # false/true
-
-# Whether to bump out to the next tab when aligning
-align_on_tabstop                         = false    # false/true
-
-# Whether to left-align numbers
-align_number_left                        = false    # false/true
-
-# Align variable definitions in prototypes and functions
-align_func_params                        = true     # false/true
-
-# Align parameters in single-line functions that have the same name.
-# The function names must already be aligned with each other.
-align_same_func_call_params              = false    # false/true
-
-# The span for aligning variable definitions (0=don't align)
-align_var_def_span                       = 1        # number
-
-# How to align the star in variable definitions.
-#  0=Part of the type     'void *   foo;'
-#  1=Part of the variable 'void     *foo;'
-#  2=Dangling             'void    *foo;'
-align_var_def_star_style                 = 0        # number
-
-# How to align the '&' in variable definitions.
-#  0=Part of the type
-#  1=Part of the variable
-#  2=Dangling
-align_var_def_amp_style                  = 0        # number
-
-# The threshold for aligning variable definitions (0=no limit)
-align_var_def_thresh                     = 0        # number
-
-# The gap for aligning variable definitions
-align_var_def_gap                        = 1        # number
-
-# Whether to align the colon in struct bit fields
-align_var_def_colon                      = false    # false/true
-
-# Whether to align any attribute after the variable name
-align_var_def_attribute                  = false    # false/true
-
-# Whether to align inline struct/enum/union variable definitions
-align_var_def_inline                     = false    # false/true
-
-# The span for aligning on '=' in assignments (0=don't align)
-align_assign_span                        = 1        # number
-
-# The threshold for aligning on '=' in assignments (0=no limit)
-align_assign_thresh                      = 0        # number
-
-# The span for aligning on '=' in enums (0=don't align)
-align_enum_equ_span                      = 1        # number
-
-# The threshold for aligning on '=' in enums (0=no limit)
-align_enum_equ_thresh                    = 0        # number
-
-# The span for aligning struct/union (0=don't align)
-align_var_struct_span                    = 1        # number
-
-# The threshold for aligning struct/union member definitions (0=no limit)
-align_var_struct_thresh                  = 0        # number
-
-# The gap for aligning struct/union member definitions
-align_var_struct_gap                     = 1        # number
-
-# The span for aligning struct initializer values (0=don't align)
-align_struct_init_span                   = 0        # number
-
-# The minimum space between the type and the synonym of a typedef
-align_typedef_gap                        = 0        # number
-
-# The span for aligning single-line typedefs (0=don't align)
-align_typedef_span                       = 0        # number
-
-# How to align typedef'd functions with other typedefs
-# 0: Don't mix them at all
-# 1: align the open paren with the types
-# 2: align the function type name with the other type names
-align_typedef_func                       = 0        # number
-
-# Controls the positioning of the '*' in typedefs. Just try it.
-# 0: Align on typedef type, ignore '*'
-# 1: The '*' is part of type name: typedef int  *pint;
-# 2: The '*' is part of the type, but dangling: typedef int *pint;
-align_typedef_star_style                 = 0        # number
-
-# Controls the positioning of the '&' in typedefs. Just try it.
-# 0: Align on typedef type, ignore '&'
-# 1: The '&' is part of type name: typedef int  &pint;
-# 2: The '&' is part of the type, but dangling: typedef int &pint;
-align_typedef_amp_style                  = 0        # number
-
-# The span for aligning comments that end lines (0=don't align)
-align_right_cmt_span                     = 3        # number
-
-# If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment
-align_right_cmt_mix                      = true    # false/true
-
-# If a trailing comment is more than this number of columns away from the text it follows,
-# it will qualify for being aligned. This has to be > 0 to do anything.
-align_right_cmt_gap                      = 0        # number
-
-# Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore)
-align_right_cmt_at_col                   = 0        # number
-
-# The span for aligning function prototypes (0=don't align)
-align_func_proto_span                    = 0        # number
-
-# Minimum gap between the return type and the function name.
-align_func_proto_gap                     = 0        # number
-
-# Align function protos on the 'operator' keyword instead of what follows
-align_on_operator                        = false    # false/true
-
-# Whether to mix aligning prototype and variable declarations.
-# If true, align_var_def_XXX options are used instead of align_func_proto_XXX options.
-align_mix_var_proto                      = false    # false/true
-
-# Align single-line functions with function prototypes, uses align_func_proto_span
-align_single_line_func                   = false    # false/true
-
-# Aligning the open brace of single-line functions.
-# Requires align_single_line_func=true, uses align_func_proto_span
-align_single_line_brace                  = false    # false/true
-
-# Gap for align_single_line_brace.
-align_single_line_brace_gap              = 0        # number
-
-# The span for aligning ObjC msg spec (0=don't align)
-align_oc_msg_spec_span                   = 0        # number
-
-# Whether to align macros wrapped with a backslash and a newline.
-# This will not work right if the macro contains a multi-line comment.
-align_nl_cont                            = false    # false/true
-
-# # Align macro functions and variables together
-align_pp_define_together                 = false    # false/true
-
-# The minimum space between label and value of a preprocessor define
-align_pp_define_gap                      = 0        # number
-
-# The span for aligning on '#define' bodies (0=don't align)
-align_pp_define_span                     = 0        # number
-
-# Align lines that start with '<<' with previous '<<'. Default=true
-align_left_shift                         = false    # false/true
-
-# Span for aligning parameters in an Obj-C message call on the ':' (0=don't align)
-align_oc_msg_colon_span                  = 0        # number
-
-# If true, always align with the first parameter, even if it is too short.
-align_oc_msg_colon_first                 = false    # false/true
-
-# Aligning parameters in an Obj-C '+' or '-' declaration on the ':'
-align_oc_decl_colon                      = false    # false/true
-
-#
-# Newline adding and removing options
-#
-
-# Whether to collapse empty blocks between '{' and '}'
-nl_collapse_empty_body                   = false    # false/true
-
-# Don't split one-line braced assignments - 'foo_t f = { 1, 2 };'
-nl_assign_leave_one_liners               = true     # false/true
-
-# Don't split one-line braced statements inside a class xx { } body
-nl_class_leave_one_liners                = true     # false/true
-
-# Don't split one-line enums: 'enum foo { BAR = 15 };'
-nl_enum_leave_one_liners                 = false    # false/true
-
-# Don't split one-line get or set functions
-nl_getset_leave_one_liners               = false    # false/true
-
-# Don't split one-line function definitions - 'int foo() { return 0; }'
-nl_func_leave_one_liners                 = true    # false/true
-
-# Don't split one-line if/else statements - 'if(a) b++;'
-nl_if_leave_one_liners                   = false    # false/true
-
-# Don't split one-line OC messages
-nl_oc_msg_leave_one_liner                = false    # false/true
-
-# Add or remove newlines at the start of the file
-nl_start_of_file                         = ignore   # ignore/add/remove/force
-
-# The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force'
-nl_start_of_file_min                     = 0        # number
-
-# Add or remove newline at the end of the file
-nl_end_of_file                           = force    # ignore/add/remove/force
-
-# The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force')
-nl_end_of_file_min                       = 1        # number
-
-# Add or remove newline between '=' and '{'
-nl_assign_brace                          = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '=' and '[' (D only)
-nl_assign_square                         = ignore   # ignore/add/remove/force
-
-# Add or remove newline after '= [' (D only). Will also affect the newline before the ']'
-nl_after_square_assign                   = ignore   # ignore/add/remove/force
-
-# The number of blank lines after a block of variable definitions at the top of a function body
-# 0 = No change (default)
-nl_func_var_def_blk                      = 0        # number
-
-# The number of newlines before a block of typedefs
-# 0 = No change (default)
-nl_typedef_blk_start                     = 0        # number
-
-# The number of newlines after a block of typedefs
-# 0 = No change (default)
-nl_typedef_blk_end                       = 0        # number
-
-# The maximum consecutive newlines within a block of typedefs
-# 0 = No change (default)
-nl_typedef_blk_in                        = 0        # number
-
-# The number of newlines before a block of variable definitions not at the top of a function body
-# 0 = No change (default)
-nl_var_def_blk_start                     = 0        # number
-
-# The number of newlines after a block of variable definitions not at the top of a function body
-# 0 = No change (default)
-nl_var_def_blk_end                       = 0        # number
-
-# The maximum consecutive newlines within a block of variable definitions
-# 0 = No change (default)
-nl_var_def_blk_in                        = 0        # number
-
-# Add or remove newline between a function call's ')' and '{', as in:
-# list_for_each(item, &list) { }
-nl_fcall_brace                           = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'enum' and '{'
-nl_enum_brace                            = add      # ignore/add/remove/force
-
-# Add or remove newline between 'struct and '{'
-nl_struct_brace                          = add      # ignore/add/remove/force
-
-# Add or remove newline between 'union' and '{'
-nl_union_brace                           = add      # ignore/add/remove/force
-
-# Add or remove newline between 'if' and '{'
-nl_if_brace                              = add      # ignore/add/remove/force
-
-# Add or remove newline between '}' and 'else'
-nl_brace_else                            = add      # ignore/add/remove/force
-
-# Add or remove newline between 'else if' and '{'
-# If set to ignore, nl_if_brace is used instead
-nl_elseif_brace                          = add      # ignore/add/remove/force
-
-# Add or remove newline between 'else' and '{'
-nl_else_brace                            = add      # ignore/add/remove/force
-
-# Add or remove newline between 'else' and 'if'
-nl_else_if                               = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '}' and 'finally'
-nl_brace_finally                         = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'finally' and '{'
-nl_finally_brace                         = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'try' and '{'
-nl_try_brace                             = add      # ignore/add/remove/force
-
-# Add or remove newline between get/set and '{'
-nl_getset_brace                          = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'for' and '{'
-nl_for_brace                             = add      # ignore/add/remove/force
-
-# Add or remove newline between 'catch' and '{'
-nl_catch_brace                           = add      # ignore/add/remove/force
-
-# Add or remove newline between '}' and 'catch'
-nl_brace_catch                           = add      # ignore/add/remove/force
-
-# Add or remove newline between 'while' and '{'
-nl_while_brace                           = add      # ignore/add/remove/force
-
-# Add or remove newline between 'scope (x)' and '{' (D)
-nl_scope_brace                           = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'unittest' and '{' (D)
-nl_unittest_brace                        = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'version (x)' and '{' (D)
-nl_version_brace                         = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'using' and '{'
-nl_using_brace                           = ignore   # ignore/add/remove/force
-
-# Add or remove newline between two open or close braces.
-# Due to general newline/brace handling, REMOVE may not work.
-nl_brace_brace                           = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'do' and '{'
-nl_do_brace                              = add      # ignore/add/remove/force
-
-# Add or remove newline between '}' and 'while' of 'do' statement
-nl_brace_while                           = remove   # ignore/add/remove/force
-
-# Add or remove newline between 'switch' and '{'
-nl_switch_brace                          = add      # ignore/add/remove/force
-
-# Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc.
-# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace.
-nl_multi_line_cond                       = false    # false/true
-
-# Force a newline in a define after the macro name for multi-line defines.
-nl_multi_line_define                     = false    # false/true
-
-# Whether to put a newline before 'case' statement
-nl_before_case                           = false    # false/true
-
-# Add or remove newline between ')' and 'throw'
-nl_before_throw                          = ignore   # ignore/add/remove/force
-
-# Whether to put a newline after 'case' statement
-nl_after_case                            = false    # false/true
-
-# Add or remove a newline between a case ':' and '{'. Overrides nl_after_case.
-nl_case_colon_brace                      = add      # ignore/add/remove/force
-
-# Newline between namespace and {
-nl_namespace_brace                       = add      # ignore/add/remove/force
-
-# Add or remove newline between 'template<>' and whatever follows.
-nl_template_class                        = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'class' and '{'
-nl_class_brace                           = add      # ignore/add/remove/force
-
-# Add or remove newline after each ',' in the constructor member initialization
-nl_class_init_args                       = ignore   # ignore/add/remove/force
-
-# Add or remove newline between return type and function name in a function definition
-nl_func_type_name                        = remove   # ignore/add/remove/force
-
-# Add or remove newline between return type and function name inside a class {}
-# Uses nl_func_type_name or nl_func_proto_type_name if set to ignore.
-nl_func_type_name_class                  = ignore   # ignore/add/remove/force
-
-# Add or remove newline between function scope and name in a definition
-# Controls the newline after '::' in 'void A::f() { }'
-nl_func_scope_name                       = ignore   # ignore/add/remove/force
-
-# Add or remove newline between return type and function name in a prototype
-nl_func_proto_type_name                  = ignore   # ignore/add/remove/force
-
-# Add or remove newline between a function name and the opening '('
-nl_func_paren                            = remove   # ignore/add/remove/force
-
-# Add or remove newline between a function name and the opening '(' in the definition
-nl_func_def_paren                        = ignore   # ignore/add/remove/force
-
-# Add or remove newline after '(' in a function declaration
-nl_func_decl_start                       = ignore   # ignore/add/remove/force
-
-# Add or remove newline after '(' in a function definition
-nl_func_def_start                        = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_decl_start when there is only one parameter.
-nl_func_decl_start_single                = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_def_start when there is only one parameter.
-nl_func_def_start_single                 = ignore   # ignore/add/remove/force
-
-# Add or remove newline after each ',' in a function declaration
-nl_func_decl_args                        = ignore   # ignore/add/remove/force
-
-# Add or remove newline after each ',' in a function definition
-nl_func_def_args                         = ignore   # ignore/add/remove/force
-
-# Add or remove newline before the ')' in a function declaration
-nl_func_decl_end                         = ignore   # ignore/add/remove/force
-
-# Add or remove newline before the ')' in a function definition
-nl_func_def_end                          = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_decl_end when there is only one parameter.
-nl_func_decl_end_single                  = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_def_end when there is only one parameter.
-nl_func_def_end_single                   = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '()' in a function declaration.
-nl_func_decl_empty                       = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '()' in a function definition.
-nl_func_def_empty                        = ignore   # ignore/add/remove/force
-
-# Whether to put each OC message parameter on a separate line
-# See nl_oc_msg_leave_one_liner
-nl_oc_msg_args                           = false    # false/true
-
-# Add or remove newline between function signature and '{'
-nl_fdef_brace                            = add      # ignore/add/remove/force
-
-# Add or remove a newline between the return keyword and return expression.
-nl_return_expr                           = ignore   # ignore/add/remove/force
-
-# Whether to put a newline after semicolons, except in 'for' statements
-nl_after_semicolon                       = false    # false/true
-
-# Whether to put a newline after brace open.
-# This also adds a newline before the matching brace close.
-nl_after_brace_open                      = true     # false/true
-
-# If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is
-# placed between the open brace and a trailing single-line comment.
-nl_after_brace_open_cmt                  = false    # false/true
-
-# Whether to put a newline after a virtual brace open with a non-empty body.
-# These occur in un-braced if/while/do/for statement bodies.
-nl_after_vbrace_open                     = false    # false/true
-
-# Whether to put a newline after a virtual brace open with an empty body.
-# These occur in un-braced if/while/do/for statement bodies.
-nl_after_vbrace_open_empty               = false    # false/true
-
-# Whether to put a newline after a brace close.
-# Does not apply if followed by a necessary ';'.
-nl_after_brace_close                     = false    # false/true
-
-# Whether to put a newline after a virtual brace close.
-# Would add a newline before return in: 'if (foo) a++; return;'
-nl_after_vbrace_close                    = false    # false/true
-
-# Control the newline between the close brace and 'b' in: 'struct { int a; } b;'
-# Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close
-nl_brace_struct_var                      = ignore   # ignore/add/remove/force
-
-# Whether to alter newlines in '#define' macros
-nl_define_macro                          = false    # false/true
-
-# Whether to not put blanks after '#ifxx', '#elxx', or before '#endif'
-nl_squeeze_ifdef                         = false    # false/true
-
-# Add or remove blank line before 'if'
-nl_before_if                             = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'if' statement
-nl_after_if                              = ignore   # ignore/add/remove/force
-
-# Add or remove blank line before 'for'
-nl_before_for                            = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'for' statement
-nl_after_for                             = ignore   # ignore/add/remove/force
-
-# Add or remove blank line before 'while'
-nl_before_while                          = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'while' statement
-nl_after_while                           = ignore   # ignore/add/remove/force
-
-# Add or remove blank line before 'switch'
-nl_before_switch                         = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'switch' statement
-nl_after_switch                          = ignore   # ignore/add/remove/force
-
-# Add or remove blank line before 'do'
-nl_before_do                             = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'do/while' statement
-nl_after_do                              = ignore   # ignore/add/remove/force
-
-# Whether to double-space commented-entries in struct/enum
-nl_ds_struct_enum_cmt                    = false    # false/true
-
-# Whether to double-space before the close brace of a struct/union/enum
-# (lower priority than 'eat_blanks_before_close_brace')
-nl_ds_struct_enum_close_brace            = false    # false/true
-
-# Add or remove a newline around a class colon.
-# Related to pos_class_colon, nl_class_init_args, and pos_comma.
-nl_class_colon                           = ignore   # ignore/add/remove/force
-
-# Change simple unbraced if statements into a one-liner
-# 'if(b)\n i++;' => 'if(b) i++;'
-nl_create_if_one_liner                   = false    # false/true
-
-# Change simple unbraced for statements into a one-liner
-# 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);'
-nl_create_for_one_liner                  = false    # false/true
-
-# Change simple unbraced while statements into a one-liner
-# 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);'
-nl_create_while_one_liner                = false    # false/true
-
-#
-# Positioning options
-#
-
-# The position of arithmetic operators in wrapped expressions
-pos_arith                                = lead     # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
-
-# The position of assignment in wrapped expressions.
-# Do not affect '=' followed by '{'
-pos_assign                               = lead     # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
-
-# The position of boolean operators in wrapped expressions
-pos_bool                                 = lead     # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
-
-# The position of comparison operators in wrapped expressions
-pos_compare                              = lead     # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
-
-# The position of conditional (b ? t : f) operators in wrapped expressions
-pos_conditional                          = lead     # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
-
-# The position of the comma in wrapped expressions
-pos_comma                                = trail    # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
-
-# The position of the comma in the constructor initialization list
-pos_class_comma                          = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
-
-# The position of colons between constructor and member initialization
-pos_class_colon                          = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
-
-#
-# Line Splitting options
-#
-
-# Try to limit code width to N number of columns
-code_width                               = 0        # number
-
-# Whether to fully split long 'for' statements at semi-colons
-ls_for_split_full                        = false    # false/true
-
-# Whether to fully split long function protos/calls at commas
-ls_func_split_full                       = false    # false/true
-
-# Whether to split lines as close to code_width as possible and ignore some groupings
-ls_code_width                            = false    # false/true
-
-#
-# Blank line options
-#
-
-# The maximum consecutive newlines
-nl_max                                   = 0        # number
-
-# The number of newlines after a function prototype, if followed by another function prototype
-nl_after_func_proto                      = 0        # number
-
-# The number of newlines after a function prototype, if not followed by another function prototype
-nl_after_func_proto_group                = 0        # number
-
-# The number of newlines after '}' of a multi-line function body
-nl_after_func_body                       = 0        # number
-
-# The number of newlines after '}' of a multi-line function body in a class declaration
-nl_after_func_body_class                 = 0        # number
-
-# The number of newlines after '}' of a single line function body
-nl_after_func_body_one_liner             = 0        # number
-
-# The minimum number of newlines before a multi-line comment.
-# Doesn't apply if after a brace open or another multi-line comment.
-nl_before_block_comment                  = 0        # number
-
-# The minimum number of newlines before a single-line C comment.
-# Doesn't apply if after a brace open or other single-line C comments.
-nl_before_c_comment                      = 0        # number
-
-# The minimum number of newlines before a CPP comment.
-# Doesn't apply if after a brace open or other CPP comments.
-nl_before_cpp_comment                    = 0        # number
-
-# Whether to force a newline after a multi-line comment.
-nl_after_multiline_comment               = false    # false/true
-
-# The number of newlines after '}' or ';' of a struct/enum/union definition
-nl_after_struct                          = 0        # number
-
-# The number of newlines after '}' or ';' of a class definition
-nl_after_class                           = 0        # number
-
-# The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label.
-# Will not change the newline count if after a brace open.
-# 0 = No change.
-nl_before_access_spec                    = 0        # number
-
-# The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label.
-# 0 = No change.
-nl_after_access_spec                     = 0        # number
-
-# The number of newlines between a function def and the function comment.
-# 0 = No change.
-nl_comment_func_def                      = 0        # number
-
-# The number of newlines after a try-catch-finally block that isn't followed by a brace close.
-# 0 = No change.
-nl_after_try_catch_finally               = 0        # number
-
-# The number of newlines before and after a property, indexer or event decl.
-# 0 = No change.
-nl_around_cs_property                    = 0        # number
-
-# The number of newlines between the get/set/add/remove handlers in C#.
-# 0 = No change.
-nl_between_get_set                       = 0        # number
-
-# Add or remove newline between C# property and the '{'
-nl_property_brace                        = ignore   # ignore/add/remove/force
-
-# Whether to remove blank lines after '{'
-eat_blanks_after_open_brace              = false    # false/true
-
-# Whether to remove blank lines before '}'
-eat_blanks_before_close_brace            = false    # false/true
-
-# How aggressively to remove extra newlines not in preproc.
-# 0: No change
-# 1: Remove most newlines not handled by other config
-# 2: Remove all newlines and reformat completely by config
-nl_remove_extra_newlines                 = 0        # number
-
-# Whether to put a blank line before 'return' statements, unless after an open brace.
-nl_before_return                         = false    # false/true
-
-# Whether to put a blank line after 'return' statements, unless followed by a close brace.
-nl_after_return                          = false    # false/true
-
-# Whether to put a newline after a Java annotation statement.
-# Only affects annotations that are after a newline.
-nl_after_annotation                      = ignore   # ignore/add/remove/force
-
-# Controls the newline between two annotations.
-nl_between_annotation                    = ignore   # ignore/add/remove/force
-
-#
-# Code modifying options (non-whitespace)
-#
-
-# Add or remove braces on single-line 'do' statement
-mod_full_brace_do                        = add      # ignore/add/remove/force
-
-# Add or remove braces on single-line 'for' statement
-mod_full_brace_for                       = add      # ignore/add/remove/force
-
-# Add or remove braces on single-line function definitions. (Pawn)
-mod_full_brace_function                  = add      # ignore/add/remove/force
-
-# Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'.
-mod_full_brace_if                        = add      # ignore/add/remove/force
-
-# Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if.
-# If any must be braced, they are all braced.  If all can be unbraced, then the braces are removed.
-mod_full_brace_if_chain                  = false    # false/true
-
-# Don't remove braces around statements that span N newlines
-mod_full_brace_nl                        = 0        # number
-
-# Add or remove braces on single-line 'while' statement
-mod_full_brace_while                     = add      # ignore/add/remove/force
-
-# Add or remove braces on single-line 'using ()' statement
-mod_full_brace_using                     = ignore   # ignore/add/remove/force
-
-# Add or remove unnecessary paren on 'return' statement
-mod_paren_on_return                      = ignore   # ignore/add/remove/force
-
-# Whether to change optional semicolons to real semicolons
-mod_pawn_semicolon                       = false    # false/true
-
-# Add parens on 'while' and 'if' statement around bools
-mod_full_paren_if_bool                   = false    # false/true
-
-# Whether to remove superfluous semicolons
-mod_remove_extra_semicolon               = false    # false/true
-
-# If a function body exceeds the specified number of newlines and doesn't have a comment after
-# the close brace, a comment will be added.
-mod_add_long_function_closebrace_comment = 0        # number
-
-# If a switch body exceeds the specified number of newlines and doesn't have a comment after
-# the close brace, a comment will be added.
-mod_add_long_switch_closebrace_comment   = 0        # number
-
-# If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after
-# the #endif, a comment will be added.
-mod_add_long_ifdef_endif_comment         = 0        # number
-
-# If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after
-# the #else, a comment will be added.
-mod_add_long_ifdef_else_comment          = 0        # number
-
-# If TRUE, will sort consecutive single-line 'import' statements [Java, D]
-mod_sort_import                          = false    # false/true
-
-# If TRUE, will sort consecutive single-line 'using' statements [C#]
-mod_sort_using                           = false    # false/true
-
-# If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C]
-# This is generally a bad idea, as it may break your code.
-mod_sort_include                         = false    # false/true
-
-# If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace.
-mod_move_case_break                      = false    # false/true
-
-# Will add or remove the braces around a fully braced case statement.
-# Will only remove the braces if there are no variable declarations in the block.
-mod_case_brace                           = ignore   # ignore/add/remove/force
-
-# If TRUE, it will remove a void 'return;' that appears as the last statement in a function.
-mod_remove_empty_return                  = false    # false/true
-
-#
-# Comment modifications
-#
-
-# Try to wrap comments at cmt_width columns
-cmt_width                                = 0        # number
-
-# Set the comment reflow mode (default: 0)
-# 0: no reflowing (apart from the line wrapping due to cmt_width)
-# 1: no touching at all
-# 2: full reflow
-cmt_reflow_mode                          = 0        # number
-
-# If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars.
-# Default is true.
-cmt_indent_multi                         = true     # false/true
-
-# Whether to group c-comments that look like they are in a block
-cmt_c_group                              = false    # false/true
-
-# Whether to put an empty '/*' on the first line of the combined c-comment
-cmt_c_nl_start                           = false    # false/true
-
-# Whether to put a newline before the closing '*/' of the combined c-comment
-cmt_c_nl_end                             = false    # false/true
-
-# Whether to group cpp-comments that look like they are in a block
-cmt_cpp_group                            = false    # false/true
-
-# Whether to put an empty '/*' on the first line of the combined cpp-comment
-cmt_cpp_nl_start                         = false    # false/true
-
-# Whether to put a newline before the closing '*/' of the combined cpp-comment
-cmt_cpp_nl_end                           = false    # false/true
-
-# Whether to change cpp-comments into c-comments
-cmt_cpp_to_c                             = false    # false/true
-
-# Whether to put a star on subsequent comment lines
-cmt_star_cont                            = false    # false/true
-
-# The number of spaces to insert at the start of subsequent comment lines
-cmt_sp_before_star_cont                  = 0        # number
-
-# The number of spaces to insert after the star on subsequent comment lines
-cmt_sp_after_star_cont                   = 0        # number
-
-# For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of
-# the comment are the same length. Default=True
-cmt_multi_check_last                     = false    # false/true
-
-# The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment.
-# Will substitute $(filename) with the current file's name.
-cmt_insert_file_header                   = ""         # string
-
-# The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment.
-# Will substitute $(filename) with the current file's name.
-cmt_insert_file_footer                   = ""         # string
-
-# The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment.
-# Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff.
-# Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... }
-cmt_insert_func_header                   = ""         # string
-
-# The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment.
-# Will substitute $(class) with the class name.
-cmt_insert_class_header                  = ""         # string
-
-# The filename that contains text to insert before a Obj-C message specification if the method isn't preceeded with a C/C++ comment.
-# Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff.
-cmt_insert_oc_msg_header                 = ""         # string
-
-# If a preprocessor is encountered when stepping backwards from a function name, then
-# this option decides whether the comment should be inserted.
-# Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header.
-cmt_insert_before_preproc                = false    # false/true
-
-#
-# Preprocessor options
-#
-
-# Control indent of preprocessors inside #if blocks at brace level 0
-pp_indent                                = ignore   # ignore/add/remove/force
-
-# Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false)
-pp_indent_at_level                       = false    # false/true
-
-# If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1.
-pp_indent_count                          = 1        # number
-
-# Add or remove space after # based on pp_level of #if blocks
-pp_space                                 = ignore   # ignore/add/remove/force
-
-# Sets the number of spaces added with pp_space
-pp_space_count                           = 0        # number
-
-# The indent for #region and #endregion in C# and '#pragma region' in C/C++
-pp_indent_region                         = 0        # number
-
-# Whether to indent the code between #region and #endregion
-pp_region_indent_code                    = false    # false/true
-
-# If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level
-pp_indent_if                             = 0        # number
-
-# Control whether to indent the code between #if, #else and #endif when not at file-level
-pp_if_indent_code                        = false    # false/true
-
-# Whether to indent '#define' at the brace level (true) or from column 1 (false)
-pp_define_at_level                       = false    # false/true
-
-# You can force a token to be a type with the 'type' option.
-# Example:
-# type myfoo1 myfoo2
-#
-# You can create custom macro-based indentation using macro-open,
-# macro-else and macro-close.
-# Example:
-# macro-open  BEGIN_TEMPLATE_MESSAGE_MAP
-# macro-open  BEGIN_MESSAGE_MAP
-# macro-close END_MESSAGE_MAP
-#
-# You can assign any keyword to any type with the set option.
-# set func_call_user _ N_
-#
-# The full syntax description of all custom definition config entries
-# is shown below:
-#
-# define custom tokens as:
-# - embed whitespace in token using '' escape character, or
-#   put token in quotes
-# - these: ' " and ` are recognized as quote delimiters
-#
-# type token1 token2 token3 ...
-#             ^ optionally specify multiple tokens on a single line
-# define def_token output_token
-#                  ^ output_token is optional, then NULL is assumed
-# macro-open token
-# macro-close token
-# macro-else token
-# set id token1 token2 ...
-#               ^ optionally specify multiple tokens on a single line
-#     ^ id is one of the names in token_enum.h sans the CT_ prefix,
-#       e.g. PP_PRAGMA
-#
-# all tokens are separated by any mix of ',' commas, '=' equal signs
-# and whitespace (space, tab)
-#
-
-# Teach uncrustify about the GROMACS attribute aliases that we use
-# to hide compiler differences. This means that declarations like
-#
-# int i, j;
-# int nthreads gmx_unused;
-#
-# does not align i with gmx_unused.
-set ATTRIBUTE gmx_unused gmx_inline gmx_restrict
diff --git a/admin/uncrustify.sh b/admin/uncrustify.sh
deleted file mode 100755 (executable)
index 5e98a11..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-#!/bin/bash
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-# This script runs uncrustify on modified files and
-# reports/applies the necessary changes.
-#
-# See `uncrustify.sh -h` for a brief usage, and docs/dev-manual/code-formatting.rst
-# for more details.
-
-# Parse command-line arguments
-function usage() {
-    echo "usage: uncrustify.sh [-f|--force] [--rev=REV]"
-    echo "           [--uncrustify=(off|check)]"
-    echo "           [--warnings=<file>] [<action>]"
-    echo "<action>: (check*|diff|update)[-(index|workdir*)] (*=default)"
-}
-
-action="check-workdir"
-declare -a diffargs
-baserev="HEAD"
-force=
-uncrustify_mode=check
-warning_file=
-for arg in "$@" ; do
-    if [[ "$arg" == "check-index" || "$arg" == "check-workdir" || \
-          "$arg" == "diff-index" || "$arg" == "diff-workdir" || \
-          "$arg" == "update-index" || "$arg" == "update-workdir" ]]
-    then
-        action=$arg
-    elif [[ "$arg" == "check" || "$arg" == "diff" || "$arg" == "update" ]] ; then
-        action=$arg-workdir
-    elif [[ "$action" == diff-* ]] ; then
-        diffargs+=("$arg")
-    elif [[ "$arg" == --uncrustify=* ]] ; then
-        uncrustify_mode=${arg#--uncrustify=}
-        if [[ "$uncrustify_mode" != "off" && "$uncrustify_mode" != "check" ]] ; then
-            echo "Unknown option: $arg"
-            echo
-            usage
-            exit 2
-        fi
-    elif [[ "$arg" == "-f" || "$arg" == "--force" ]] ; then
-        force=1
-    elif [[ "$arg" == --rev=* ]] ; then
-        baserev=${arg#--rev=}
-    elif [[ "$arg" == --warnings=* ]] ; then
-        warning_file=${arg#--warnings=}
-    elif [[ "$arg" == "-h" || "$arg" == "--help" ]] ; then
-        usage
-        exit 0
-    else
-        echo "Unknown option: $arg"
-        echo
-        usage
-        exit 2
-    fi
-done
-
-# Check that uncrustify is present
-if [[ "$uncrustify_mode" != "off" ]]
-then
-    if [ -z "$UNCRUSTIFY" ]
-    then
-        UNCRUSTIFY=`git config hooks.uncrustifypath`
-    fi
-    if [ -z "$UNCRUSTIFY" ]
-    then
-        echo "Please set the path to uncrustify using UNCRUSTIFY or"
-        echo "git config hooks.uncrustifypath."
-        echo "Note that you need a custom version of uncrustify."
-        echo "See docs/dev-manual/uncrustify.rst for how to get one."
-        exit 2
-    fi
-    if ! which "$UNCRUSTIFY" 1>/dev/null
-    then
-        echo "Uncrustify not found: $UNCRUSTIFY"
-        exit 2
-    fi
-fi
-
-# Switch to the root of the source tree and check the config file
-srcdir=`git rev-parse --show-toplevel`
-pushd $srcdir >/dev/null
-admin_dir=$srcdir/admin
-cfg_file=$admin_dir/uncrustify.cfg
-if [ ! -f "$cfg_file" ]
-then
-    echo "Uncrustify configuration file not found: $cfg_file"
-    exit 2
-fi
-
-# Actual processing starts: create a temporary directory
-tmpdir=`mktemp -d -t gmxuncrust.XXXXXX`
-
-# Produce a list of changed files
-# Only include files that have proper filter set in .gitattributes
-internal_diff_args=
-if [[ $action == *-index ]]
-then
-    internal_diff_args="--cached"
-fi
-git diff-index $internal_diff_args --diff-filter=ACMR $baserev >$tmpdir/difflist
-cut -f2 <$tmpdir/difflist | \
-    git check-attr --stdin filter | \
-    sed -e 's/.*: filter: //' | \
-    paste $tmpdir/difflist - | \
-    grep -E '(complete_formatting|uncrustify|copyright|includesort)$' >$tmpdir/filtered
-cut -f2 <$tmpdir/filtered >$tmpdir/filelist_all
-grep -E '(uncrustify)$' <$tmpdir/filtered | \
-    cut -f2 >$tmpdir/filelist_uncrustify
-git diff-files --name-only | grep -Ff $tmpdir/filelist_all >$tmpdir/localmods
-
-# Extract changed files to a temporary directory
-mkdir $tmpdir/org
-if [[ $action == *-index ]] ; then
-    git checkout-index --prefix=$tmpdir/org/ --stdin <$tmpdir/filelist_all
-else
-    rsync --files-from=$tmpdir/filelist_all $srcdir $tmpdir/org
-fi
-# Duplicate the original files to a separate directory, where all changes will
-# be made.
-cp -r $tmpdir/org $tmpdir/new
-
-# Create output file for what was done (in case no messages get written)
-touch $tmpdir/messages
-
-# Run uncrustify on the temporary directory
-cd $tmpdir/new
-if [[ $uncrustify_mode != "off" ]] ; then
-    if ! $UNCRUSTIFY -c $cfg_file -F $tmpdir/filelist_uncrustify --no-backup >$tmpdir/uncrustify.out 2>&1 ; then
-        echo "Reformatting failed. Check uncrustify output below for errors:"
-        cat $tmpdir/uncrustify.out
-        rm -rf $tmpdir
-        exit 2
-    fi
-    # Find the changed files if necessary
-    if [[ $action != diff-* ]] ; then
-        msg="needs uncrustify"
-        if [[ $action == update-* ]] ; then
-            msg="uncrustified"
-        fi
-        git diff --no-index --name-only ../org/ . | \
-            awk -v msg="$msg" '{sub(/.\//,""); print $0 ": " msg}' >> $tmpdir/messages
-    fi
-    # TODO: Consider checking whether rerunning uncrustify causes additional changes
-fi
-
-cd $tmpdir
-
-# If a diff was requested, show it and we are done
-if [[ $action == diff-* ]] ; then
-    git diff --no-index --no-prefix "${diffargs[@]}" org/ new/
-    rm -rf $tmpdir
-    exit 0
-fi
-
-# Find the changed files
-git diff --no-index --name-only --exit-code org/ new/ | \
-    sed -e 's#new/##' > $tmpdir/changed
-changes=
-if [[ -s $tmpdir/changed ]]
-then
-    changes=1
-fi
-
-# Check if changed files have changed outside the index
-if grep -Ff $tmpdir/localmods $tmpdir/changed > $tmpdir/conflicts
-then
-    awk '{print $0 ": has changes in work tree"}' $tmpdir/conflicts \
-        >> $tmpdir/messages
-    if [[ ! $force && $action == update-* ]] ; then
-        echo "Modified files found in work tree, skipping update. Use -f to override."
-        echo "The following would have been done:"
-        sort $tmpdir/messages
-        rm -rf $tmpdir
-        exit 2
-    fi
-fi
-
-# Update the index/work tree if requested
-if [[ $action == update-index ]] ; then
-    grep -Ff $tmpdir/changed $tmpdir/filtered > $tmpdir/tohash
-    cd $tmpdir/new
-    IFS='
-'
-    for change in `cut -f2 $tmpdir/tohash | \
-                   git --git-dir=$srcdir/.git hash-object -w --stdin-paths --no-filters | \
-                   paste - $tmpdir/tohash`
-    do
-        # NOTE: the patterns below contain literal tabs
-        sha1=${change%%        *}
-        rest=${change#*        }
-        mode=${rest:8:6}
-        path=${rest#*  }
-        path=${path%%  *}
-        # Contains a literal tab
-        echo "$mode $sha1      $path" >> $tmpdir/toindex
-    done
-    unset IFS
-    git --git-dir=$srcdir/.git update-index --index-info < $tmpdir/toindex
-elif [[ $action == update-workdir ]] ; then
-    rsync --files-from=$tmpdir/changed $tmpdir/new/ $srcdir/
-fi
-
-# Get back to the original directory
-popd >/dev/null
-
-# Report what was done
-sort $tmpdir/messages | tee $warning_file
-
-rm -rf $tmpdir
-exit $changes
similarity index 57%
rename from admin/builds/source-package.py
rename to api/CMakeLists.txt
index f3d52e0c09b31815659464126459b32015085a97..557e6bb44e57c9d9f22815db283a643de7556319 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2016,2017,2019, by the GROMACS development team, led by
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-import os.path
 
-build_out_of_source = True
+# Activate targets for new C++ API components and docs.
+if(GMX_NATIVE_WINDOWS OR GMX_BUILD_MDRUN_ONLY)
+    # GMXAPI has not been tested in Microsoft environments.
+    # GMXAPI relies on libgromacs and is incompatible with an `mdrun`-only build.
+    # GMXAPI requires position-independent code
+    set(_GMXAPI_DEFAULT OFF)
+else()
+    set(_GMXAPI_DEFAULT ON)
+endif()
+option(GMXAPI "Install GROMACS API." ${_GMXAPI_DEFAULT})
+if (GMXAPI)
+    add_subdirectory(gmxapi)
+endif()
 
-def do_build(context):
-    cmake_opts = {
-            'GMX_BUILD_HELP': 'ON',
-            'CMAKE_BUILD_TYPE': 'Debug',
-            'GMX_SIMD': 'None',
-            'GMX_USE_RDTSCP': 'OFF',
-            'GMX_THREAD_MPI': 'OFF',
-            'GMX_OPENMP': 'OFF',
-            'GMX_GPU': 'OFF'
-        }
-    if context.params.get('RELEASE', Parameter.bool):
-        cmake_opts['GMX_BUILD_TARBALL'] = 'ON'
-
-    context.run_cmake(cmake_opts)
-    context.build_target(target='gmx')
-    context.build_target(target='man')
-    context.build_target(target='completion')
-    context.build_target(target='install-guide')
-    context.build_target(target='reference_checksum')
-
-    context.build_target(target='package_source')
-
-    cpack_config_path = os.path.join(context.workspace.build_dir, 'CPackSourceConfig.cmake')
-    cpack_config = context.read_cmake_variable_file(cpack_config_path)
-    package_name = cpack_config['CPACK_PACKAGE_FILE_NAME'] + '.tar.gz'
-    version = cpack_config['CPACK_PACKAGE_VERSION']
-    context.write_package_info(Project.GROMACS, package_name, version)
+# Activate targets NBLIB
+if(GMX_NATIVE_WINDOWS OR GMX_BUILD_MDRUN_ONLY)
+    # NBLIB has not been tested in Microsoft environments.
+    # NBLIB relies on libgromacs and is incompatible with an `mdrun`-only build.
+    # NBLIB requires position-independent code
+    set(_NBLIB_DEFAULT OFF)
+else()
+    set(_NBLIB_DEFAULT ON)
+endif()
+option(GMX_INSTALL_NBLIB_API "Install nblib headers" ${_NBLIB_DEFAULT})
+if (GMX_INSTALL_NBLIB_API)
+    if(NOT ${BUILD_SHARED_LIBS})
+        message(FATAL_ERROR "NBLIB requires position-independent code. Set -GMX_INSTALL_NBLIB_API=OFF or -DBUILD_SHARED_LIBS=ON.")
+    else()
+        add_subdirectory(nblib)
+    endif()
+endif()
diff --git a/api/gmxapi/CMakeLists.txt b/api/gmxapi/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a72b7f8
--- /dev/null
@@ -0,0 +1,115 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+# This list file provides the Gromacs::gmxapi cmake module.
+
+##########################
+# Set up public interface.
+
+# Note: GROMACS releases have a single-integer monotonic version in GMX_API_VERSION
+# and LIBRARY_VERSION annotates the shared object for libgromacs. GMXAPI versioning
+# is not synchronized to releases and may increment faster or slower.
+#
+# Prior to 0.1, GMXAPI patch levels are used to mark short term development cycles
+# and allow compatibility checks for client software of the early releases.
+#
+# gmxapi 0.2 will be the first release candidate for gmxapi 1.0 and will attempt
+# to establish compatibility guarantees consistent with semantic versioning.
+# (https://semver.org). When the API is deemed suitably stable, gmxapi 1.0 should
+# be tagged. Official GROMACS releases should be mappable to a distinct gmxapi
+# release string. For roadmap details, see https://gitlab.com/gromacs/gromacs/-/issues/2585
+set(GMXAPI_MAJOR 0)
+set(GMXAPI_MINOR 1)
+set(GMXAPI_PATCH 0)
+set(GMXAPI_RELEASE ${GMXAPI_MAJOR}.${GMXAPI_MINOR}.${GMXAPI_PATCH})
+
+add_library(gmxapi)
+set_target_properties(gmxapi PROPERTIES
+                      VERSION_MAJOR ${GMXAPI_MAJOR}
+                      VERSION_MINOR ${GMXAPI_MINOR}
+                      VERSION_PATCH ${GMXAPI_PATCH}
+                      RELEASE ${GMXAPI_RELEASE})
+
+
+if (GMX_LIB_MPI)
+    # GROMACS is built against an MPI library.
+    # Clarification should be possible with resolution of #3672.
+    set(_gmx_mpi_type "library")
+elseif(GMX_THREAD_MPI)
+    # GROMACS is built with its internal thread-MPI implementation.
+    set(_gmx_mpi_type "tmpi")
+else()
+    # GROMACS is built without MPI or thread-MPI.
+    set(_gmx_mpi_type "none")
+endif ()
+define_property(TARGET PROPERTY MPI
+                BRIEF_DOCS "MPI capability of the GROMACS library."
+                FULL_DOCS "Values of 'library', 'tmpi', or 'none' indicate the configure-time options of the GROMACS
+                 library build.")
+set_target_properties(gmxapi PROPERTIES
+                      MPI ${_gmx_mpi_type})
+unset(_gmx_mpi_type)
+
+##########################
+# Define public interface.
+#
+# The include directory should be mostly empty so that we can use it internally as
+# the public interface include directory during build and testing.
+configure_file(include/gmxapiversion.h.in include/gmxapi/version.h)
+configure_file(include/resourceassignment.cmakein.h include/gmxapi/mpi/resourceassignment.h)
+target_include_directories(gmxapi PUBLIC
+                           $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+                           $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
+                           $<INSTALL_INTERFACE:include>
+                           )
+
+add_subdirectory(cpp)
+
+###############################
+# Install the public interface.
+#
+# If any item begins in a generator expression it must evaluate to a full path,
+# so we can't just use something like $<TARGET_PROPERTIES:gmxapiPublicHeaders,SOURCES>.
+# Instead, we use a canonical list defined in the parent scope.
+
+# Install public header directories.
+install(DIRECTORY include/gmxapi
+        DESTINATION include)
+
+# Install "configured" files from the build tree.
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/gmxapi/version.h
+        DESTINATION include/gmxapi)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/gmxapi/mpi/resourceassignment.h
+        DESTINATION include/gmxapi/mpi)
+
diff --git a/api/gmxapi/cpp/CMakeLists.txt b/api/gmxapi/cpp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..52d88c5
--- /dev/null
@@ -0,0 +1,138 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+# This list file provides the Gromacs::gmxapi cmake target.
+
+target_sources(gmxapi PRIVATE
+               resourceassignment.cpp
+               context.cpp
+               exceptions.cpp
+               gmxapi.cpp
+               md.cpp
+               mdmodule.cpp
+               mdsignals.cpp
+               session.cpp
+               status.cpp
+               system.cpp
+               version.cpp
+               workflow.cpp
+               tpr.cpp
+               )
+set_target_properties(gmxapi PROPERTIES POSITION_INDEPENDENT_CODE ON)
+gmx_target_compile_options(gmxapi)
+target_compile_definitions(gmxapi PRIVATE HAVE_CONFIG_H)
+target_include_directories(gmxapi SYSTEM BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include)
+# Should be possible to change this when resolving #3290
+target_include_directories(gmxapi SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/src/external)
+
+# Define implementation interface
+target_include_directories(gmxapi PRIVATE
+                           ${CMAKE_CURRENT_SOURCE_DIR}
+                           )
+
+###############################
+# Install the public interface.
+#
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+    # Instruct a linking client to use its RPATH to resolve the libgmxapi location.
+    #
+    # Explicitly specify library "install name" so that the correct loading
+    # instruction is produced in client code. Client code should be able to find the
+    # library relative to the client code RPATH. Without explicitly specifying,
+    # INSTALL_NAME_DIR is inherited from the global CMAKE_INSTALL_NAME_DIR, which is
+    # not appropriate for libgmxapi if it uses an install name relative to the
+    # executable_path or loader_path.
+    set_target_properties(gmxapi PROPERTIES INSTALL_NAME_DIR "@rpath")
+endif()
+
+set_target_properties(gmxapi PROPERTIES
+                      OUTPUT_NAME "gmxapi${GMX_LIBS_SUFFIX}"
+                      SOVERSION ${GMXAPI_MAJOR}
+                      VERSION ${GMXAPI_RELEASE}
+                      )
+
+target_link_libraries(gmxapi PRIVATE libgromacs)
+
+
+################################################
+# Install and export gmxapi and Gromacs::gmxapi.
+#
+# Install the gmxapi target and simultaneously define the export target for
+# which CMake will create a helper file. Specify the directory for clients to
+# add to their include path to be able to `#include "gmxapi/some_header.h"`
+install(TARGETS gmxapi
+        EXPORT gmxapi
+        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        INCLUDES DESTINATION include
+        )
+
+# Create the CMake exports file to help other projects build against libgmxapi
+# as a CMake import target Gromacs::gmxapi.
+install(EXPORT gmxapi
+        NAMESPACE Gromacs::
+        DESTINATION share/cmake/gmxapi/
+        )
+add_library(Gromacs::gmxapi ALIAS gmxapi )
+
+include(CMakePackageConfigHelpers)
+
+get_target_property(_mpi Gromacs::gmxapi MPI)
+configure_package_config_file(
+    cmake/gmxapi-config.cmake.in
+    "${CMAKE_CURRENT_BINARY_DIR}/cmake/gmxapi-config.cmake"
+    INSTALL_DESTINATION share/cmake/gmxapi/
+)
+unset(_mpi)
+get_target_property(GMXAPI_RELEASE gmxapi RELEASE)
+write_basic_package_version_file(
+    ${CMAKE_CURRENT_BINARY_DIR}/cmake/gmxapi-config-version.cmake
+    VERSION ${GMXAPI_RELEASE}
+    COMPATIBILITY SameMajorVersion
+)
+
+install(
+    FILES
+    ${CMAKE_CURRENT_BINARY_DIR}/cmake/gmxapi-config-version.cmake
+    ${CMAKE_CURRENT_BINARY_DIR}/cmake/gmxapi-config.cmake
+    DESTINATION share/cmake/gmxapi/
+)
+
+# We need a CMake target to provide the internal interface(s) of the gmxapi
+# library implementation.
+add_library(gmxapi-detail INTERFACE)
+target_include_directories(gmxapi-detail
+                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
similarity index 84%
rename from src/api/cpp/cmake/gmxapi-config.cmake.in
rename to api/gmxapi/cpp/cmake/gmxapi-config.cmake.in
index cf4e0f9e2852b662a73acfd12c11c86335f64628..fcb7de77e103d4ca06183f2ba758d71aae34fd70 100644 (file)
@@ -4,3 +4,4 @@ set(gmxapi_VERSION @GMXAPI_RELEASE@)
 # https://cmake.org/cmake/help/v3.4/module/CMakePackageConfigHelpers.html#command:configure_package_config_file
 include("${CMAKE_CURRENT_LIST_DIR}/gmxapi.cmake")
 check_required_components(gmxapi)
+set_property(TARGET Gromacs::gmxapi PROPERTY MPI "@_mpi@")
similarity index 56%
rename from src/api/cpp/context.cpp
rename to api/gmxapi/cpp/context.cpp
index ad3de9be6b07f817c92c624a147c31351cb608a3..903f3ce56d6ba6fcc24eff3359e56ffb1821928b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/commandline/pargs.h"
 #include "gromacs/commandline/filenm.h"
 #include "gromacs/commandline/pargs.h"
+#include "gromacs/gmxlib/network.h"
 #include "gromacs/mdlib/stophandler.h"
 #include "gromacs/mdrunutility/logging.h"
 #include "gromacs/mdrunutility/multisim.h"
 #include "gromacs/mdrun/runner.h"
 #include "gromacs/mdrunutility/handlerestart.h"
 #include "gromacs/utility/arraysize.h"
+#include "gromacs/utility/basenetwork.h"
 #include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/gmxmpi.h"
+#include "gromacs/utility/init.h"
 #include "gromacs/utility/smalloc.h"
 
+#include "gmxapi/mpi/resourceassignment.h"
 #include "gmxapi/exceptions.h"
 #include "gmxapi/session.h"
 #include "gmxapi/status.h"
 namespace gmxapi
 {
 
-ContextImpl::ContextImpl()
+// Support some tag dispatch. Warning: These are just aliases (not strong types).
+/*!
+ * \brief Logical helper alias to convert preprocessor constant to type.
+ *
+ * \tparam Value Provide the GMX\_LIB\_MPI macro.
+ */
+template<bool Value>
+using hasLibraryMpi = std::bool_constant<Value>;
+/* Note that a no-MPI build still uses the tMPI headers to define MPI_Comm for the
+ * gmx::SimulationContext definition. The dispatching in this file accounts for
+ * these two definitions of SimulationContext. gmxThreadMpi here does not imply
+ * that the library was necessarily compiled with thread-MPI enabled.
+ */
+using gmxThreadMpi = hasLibraryMpi<false>;
+using gmxLibMpi    = hasLibraryMpi<true>;
+using MpiType      = std::conditional_t<GMX_LIB_MPI, gmxLibMpi, gmxThreadMpi>;
+
+using MpiContextInitializationError = BasicException<struct MpiContextInitialization>;
+
+
+/*!
+ * \brief Helpers to evaluate the correct precondition for the library build.
+ *
+ * TODO: (#3650) Consider distinct MpiContextManager types for clearer definition of preconditions.
+ */
+namespace
+{
+
+[[maybe_unused]] MPI_Comm validCommunicator(const MPI_Comm& communicator, const gmxThreadMpi&)
 {
+    if (communicator != MPI_COMM_NULL)
+    {
+        throw MpiContextInitializationError(
+                "Provided communicator must be MPI_COMM_NULL for GROMACS built without MPI "
+                "library.");
+    }
+    return communicator;
+}
+
+[[maybe_unused]] MPI_Comm validCommunicator(const MPI_Comm& communicator, const gmxLibMpi&)
+{
+    if (communicator == MPI_COMM_NULL)
+    {
+        throw MpiContextInitializationError("MPI-enabled GROMACS requires a valid communicator.");
+    }
+    return communicator;
+}
+
+/*!
+ * \brief Return the communicator if it is appropriate for the environment.
+ *
+ * \throws MpiContextInitializationError if communicator does not match the
+ *  MpiContextManager precondition for the current library configuration.
+ */
+MPI_Comm validCommunicator(const MPI_Comm& communicator)
+{
+    return validCommunicator(communicator, MpiType());
+}
+
+//! \brief Provide a reasonable default value.
+MPI_Comm validCommunicator()
+{
+    return GMX_LIB_MPI ? MPI_COMM_WORLD : MPI_COMM_NULL;
+}
+
+} // anonymous namespace
+
+MpiContextManager::MpiContextManager(MPI_Comm communicator) :
+    communicator_(std::make_unique<MPI_Comm>(validCommunicator(communicator)))
+{
+    // Safely increments the GROMACS MPI initialization counter after checking
+    // whether the MPI library is already initialized. After this call, MPI_Init
+    // or MPI_Init_thread has been called exactly once.
+    gmx::init(nullptr, nullptr);
+    GMX_RELEASE_ASSERT(!GMX_LIB_MPI || gmx_mpi_initialized(),
+                       "MPI should be initialized before reaching this point.");
+    if (this->communicator() != MPI_COMM_NULL)
+    {
+        // Synchronise at the point of acquiring a MpiContextManager.
+        gmx_barrier(this->communicator());
+    }
+};
+
+MpiContextManager::~MpiContextManager()
+{
+    if (communicator_)
+    {
+        // This is always safe to call. It is a no-op if
+        // thread-MPI, and if the constructor completed then the
+        // MPI library is initialized with reference counting.
+        gmx::finalize();
+    }
+}
+
+MpiContextManager::MpiContextManager() : MpiContextManager(validCommunicator()) {}
+
+MPI_Comm MpiContextManager::communicator() const
+{
+    if (!communicator_)
+    {
+        throw UsageError("Invalid MpiContextManager. Accessed after `move`?");
+    }
+    return *communicator_;
+}
+
+ContextImpl::~ContextImpl() = default;
+
+[[maybe_unused]] static Context createContext(const ResourceAssignment& resources, const gmxLibMpi&)
+{
+    CommHandle handle;
+    resources.applyCommunicator(&handle);
+    if (handle.communicator == MPI_COMM_NULL)
+    {
+        throw UsageError("MPI-enabled Simulator contexts require a valid communicator.");
+    }
+    auto contextmanager = MpiContextManager(handle.communicator);
+    auto impl           = ContextImpl::create(std::move(contextmanager));
+    GMX_ASSERT(impl, "ContextImpl creation method should not be able to return null.");
+    auto context = Context(impl);
+    return context;
+}
+
+[[maybe_unused]] static Context createContext(const ResourceAssignment& resources, const gmxThreadMpi&)
+{
+    if (resources.size() > 1)
+    {
+        throw UsageError("Only one thread-MPI Simulation per Context is supported.");
+    }
+    // Thread-MPI Context does not yet have a need for user-provided resources.
+    // However, see #3650.
+    return createContext();
+}
+
+Context createContext(const ResourceAssignment& resources)
+{
+    return createContext(resources, hasLibraryMpi<GMX_LIB_MPI>());
+}
+
+Context createContext()
+{
+    MpiContextManager contextmanager;
+    auto              impl = ContextImpl::create(std::move(contextmanager));
+    GMX_ASSERT(impl, "ContextImpl creation method should not be able to return null.");
+    auto context = Context(impl);
+    return context;
+}
+
+ContextImpl::ContextImpl(MpiContextManager&& mpi) noexcept(std::is_nothrow_constructible_v<gmx::LegacyMdrunOptions>) :
+    mpi_(std::move(mpi))
+{
+    // Confirm our understanding of the MpiContextManager invariant.
+    GMX_ASSERT(mpi_.communicator() == MPI_COMM_NULL ? !GMX_LIB_MPI : GMX_LIB_MPI,
+               "Precondition violated: inappropriate communicator for the library environment.");
+    // Make sure we didn't change the data members and overlook implementation details.
     GMX_ASSERT(session_.expired(),
                "This implementation assumes an expired weak_ptr at initialization.");
 }
 
-std::shared_ptr<gmxapi::ContextImpl> ContextImpl::create()
+std::shared_ptr<ContextImpl> ContextImpl::create(MpiContextManager&& mpi)
 {
-    std::shared_ptr<ContextImpl> impl = std::make_shared<ContextImpl>();
+    std::shared_ptr<ContextImpl> impl;
+    impl.reset(new ContextImpl(std::move(mpi)));
     return impl;
 }
 
@@ -109,13 +267,18 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow& work)
             filename = mdNode->params();
         }
 
-        /* As default behavior, automatically extend trajectories from the checkpoint file.
+        /* Mock up the argv interface used by option processing infrastructure.
+         *
+         * As default behavior, automatically extend trajectories from the checkpoint file.
          * In the future, our API for objects used to initialize a simulation needs to address the fact that currently a
          * microstate requires data from both the TPR and checkpoint file to be fully specified. Put another way,
          * current
          * GROMACS simulations can take a "configuration" as input that does not constitute a complete microstate in
          * terms of hidden degrees of freedom (integrator/thermostat/barostat/PRNG state), but we want a clear notion of
          * a microstate for gmxapi interfaces.
+         *
+         * TODO: Remove `-s` and `-cpi` arguments.
+         *       Ref: https://gitlab.com/gromacs/gromacs/-/issues/3652
          */
 
         // Set input TPR name
@@ -132,7 +295,7 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow& work)
          * process starts and instead manage files (paths) in an absolute and
          * immutable way, with abstraction provided through the Context chain-of-responsibility.
          * TODO: API abstractions for initializing simulations that may be new or partially
-         * complete. Reference gmxapi milestone 13 at https://redmine.gromacs.org/issues/2585
+         * complete. Reference gmxapi milestone 13 at https://gitlab.com/gromacs/gromacs/-/issues/2585
          */
 
         // Create a mock argv. Note that argv[0] is expected to hold the program name.
@@ -159,14 +322,16 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow& work)
 
         ArrayRef<const std::string> multiSimDirectoryNames =
                 opt2fnsIfOptionSet("-multidir", ssize(options_.filenames), options_.filenames.data());
-        // Set up the communicator, where possible (see docs for
-        // SimulationContext).
-        MPI_Comm communicator = GMX_LIB_MPI ? MPI_COMM_WORLD : MPI_COMM_NULL;
+
         // The SimulationContext is necessary with gmxapi so that
         // resources owned by the client code can have suitable
         // lifetime. The gmx wrapper binary uses the same infrastructure,
         // but the lifetime is now trivially that of the invocation of the
         // wrapper binary.
+        auto communicator = mpi_.communicator();
+        // Confirm the precondition for simulationContext().
+        GMX_ASSERT(communicator == MPI_COMM_NULL ? !GMX_LIB_MPI : GMX_LIB_MPI,
+                   "Context communicator does not have an appropriate value for the environment.");
         SimulationContext simulationContext(communicator, multiSimDirectoryNames);
 
 
@@ -174,9 +339,9 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow& work)
         LogFilePtr       logFileGuard     = nullptr;
         gmx_multisim_t*  ms               = simulationContext.multiSimulation_.get();
         std::tie(startingBehavior, logFileGuard) =
-                handleRestart(findIsSimulationMasterRank(ms, communicator), communicator, ms,
-                              options_.mdrunOptions.appendingBehavior, ssize(options_.filenames),
-                              options_.filenames.data());
+                handleRestart(findIsSimulationMasterRank(ms, simulationContext.simulationCommunicator_),
+                              communicator, ms, options_.mdrunOptions.appendingBehavior,
+                              ssize(options_.filenames), options_.filenames.data());
 
         auto builder = MdrunnerBuilder(std::move(mdModules),
                                        compat::not_null<SimulationContext*>(&simulationContext));
@@ -192,8 +357,13 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow& work)
         builder.addReplicaExchange(options_.replExParams);
         // Need to establish run-time values from various inputs to provide a resource handle to Mdrunner
         builder.addHardwareOptions(options_.hw_opt);
+
         // \todo File names are parameters that should be managed modularly through further factoring.
         builder.addFilenames(options_.filenames);
+        // TODO: Remove `s` and `-cpi` from LegacyMdrunOptions before launch(). #3652
+        auto simulationInput = makeSimulationInput(options_);
+        builder.addInput(simulationInput);
+
         // Note: The gmx_output_env_t life time is not managed after the call to parse_common_args.
         // \todo Implement lifetime management for gmx_output_env_t.
         // \todo Output environment should be configured outside of Mdrunner and provided as a resource.
@@ -205,6 +375,8 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow& work)
                                         std::move(simulationContext), std::move(logFileGuard));
 
         // Clean up argv once builder is no longer in use
+        // TODO: Remove long-lived references to argv so this is no longer necessary.
+        //       Ref https://gitlab.com/gromacs/gromacs/-/issues/2877
         for (auto&& string : argv)
         {
             if (string != nullptr)
@@ -227,12 +399,6 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow& work)
     return launchedSession;
 }
 
-// As of gmxapi 0.0.3 there is only one Context type
-Context::Context() : Context{ ContextImpl::create() }
-{
-    GMX_ASSERT(impl_, "Context requires a non-null implementation member.");
-}
-
 std::shared_ptr<Session> Context::launch(const Workflow& work)
 {
     return impl_->launch(work);
@@ -240,7 +406,10 @@ std::shared_ptr<Session> Context::launch(const Workflow& work)
 
 Context::Context(std::shared_ptr<ContextImpl> impl) : impl_{ std::move(impl) }
 {
-    GMX_ASSERT(impl_, "Context requires a non-null implementation member.");
+    if (!impl_)
+    {
+        throw UsageError("Context requires a non-null implementation member.");
+    }
 }
 
 void Context::setMDArgs(const MDArgs& mdArgs)
diff --git a/api/gmxapi/cpp/context_impl.h b/api/gmxapi/cpp/context_impl.h
new file mode 100644 (file)
index 0000000..4f9296d
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMXAPI_CONTEXT_IMPL_H
+#define GMXAPI_CONTEXT_IMPL_H
+/*! \file
+ * \brief Declare gmxapi::ContextImpl
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ * \ingroup gmxapi
+ */
+
+#include <memory>
+#include <string>
+
+#include "gromacs/mdrun/legacymdrunoptions.h"
+#include "gromacs/mdtypes/mdrunoptions.h"
+#include "gromacs/utility/gmxmpi.h"
+
+#include "gmxapi/context.h"
+#include "gmxapi/session.h"
+
+namespace gmxapi
+{
+
+/*!
+ * \brief Provide RAII management of library MPI context.
+ *
+ * To acquire an MpiContextManager is to have assurance that any external MPI
+ * environment is ready to use. When the MpiContextManager is released or
+ * goes out of scope, the destructor finalizes the resources.
+ *
+ * It is plausible that one or more ranks may not participate in an API session,
+ * but we have not historically handled such use cases at this level.
+ * For GROMACS built with an MPI library, the root communicator in the MpiContextManager
+ * must be valid. For other builds (e.g. tMPI), the communicator member must be MPI_COMM_NULL.
+ *
+ * If the client provides a communicator, the client promises to maintain the validity
+ * of the communicator for the life of the MpiContextManager.
+ *
+ * Note that thread-MPI chooses the number of ranks and constructs its
+ * MPI communicator internally, so does not and is unlikely to ever
+ * participate here.
+ *
+ * \todo Log errors during set up or tear down.
+ * There is no resource for logging or reporting errors during initialization or when misused.
+ *
+ * \ingroup gmxapi
+ */
+class MpiContextManager
+{
+public:
+    /*!
+     * \brief Default constructor.
+     *
+     * Construct a valid instance with an appropriate default value for the
+     * base communicator. (Note that appropriate default value depends on whether
+     * the library was compiled with an external MPI library.)
+     *
+     * \throws BasicException if library cannot be initialized.
+     */
+    MpiContextManager();
+
+    /*!
+     * \brief Allow the communicator to be specified.
+     *
+     * Supports use cases in which a client provides a non-default communicator.
+     * Ensures that library environment is properly initialized and finalized,
+     * whether or not an externally managed communicator has been provided.
+     *
+     * \param communicator Optional communicator representing a client-managed MPI environment.
+     *
+     * Note that the communicator must be MPI_COMM_NULL if and only if GROMACS was built without an
+     * external MPI library.
+     *
+     * \throws BasicException if library cannot be initialized.
+     *
+     * \todo (#3650?) Decide whether to exclude this from tMPI environments or find a sensible invariant.
+     */
+    explicit MpiContextManager(MPI_Comm communicator);
+
+    ~MpiContextManager();
+
+    /*!
+     * \brief Exclusive ownership of a scoped context means copying is impossible.
+     *
+     * \{
+     */
+    MpiContextManager(const MpiContextManager&) = delete;
+    MpiContextManager& operator=(const MpiContextManager&) = delete;
+    //! \}
+
+    /*!
+     * \brief Transfer ownership of the managed GROMACS MPI context.
+     *
+     * \{
+     */
+    MpiContextManager(MpiContextManager&& source) noexcept = default;
+    MpiContextManager& operator=(MpiContextManager&& source) noexcept = default;
+    //! \}
+
+    /*!
+     * \brief Get the communicator for this context.
+     *
+     * \return Communicator with an appropriate initialized state for the current library
+     * configuration.
+     *
+     * \throws gmxapi::UsageError if MpiContextManager is in an invalid state, such as after
+     *  being moved from.
+     */
+    [[nodiscard]] MPI_Comm communicator() const;
+
+private:
+    /*!
+     * \brief The resources provided by this manager.
+     *
+     * A valid MpiContextManager has a value for communicator_ that is appropriate
+     * for the library configuration.
+     */
+    std::unique_ptr<MPI_Comm> communicator_;
+};
+
+/*!
+ * \brief Context implementation.
+ *
+ * Execution contexts have a uniform interface specified by the API. Implementations for
+ * particular execution environments can specialize / derive from this base.
+ *
+ * \todo Separate interface and implementation.
+ *
+ * \warning Definition and semantics depend on configure-time details. For example,
+ *          MPI-enabled libraries always hold a valid MPI communicator via MpiContextManager,
+ *          whereas tMPI and non-MPI builds hold a meaningless MpiContextManager.
+ *
+ * \todo Provide functions or traits for introspection.
+ *
+ * \ingroup gmxapi
+ */
+class ContextImpl final : public std::enable_shared_from_this<ContextImpl>
+{
+public:
+    ~ContextImpl();
+
+    /*!
+     * \brief Factory function
+     *
+     * Since this class provides `shared_from_this`, we need to make sure
+     * that it never exists without a shared_ptr owning it.
+     *
+     * If we can confirm `shared_from_this` is no longer necessary, implementation may change.
+     * \todo: Use registration/deregistration of launched Sessions to log warnings on shutdown
+     *        instead of letting Session keep ContextImpl alive.
+     *
+     * \return ownership of a new object
+     */
+    static std::shared_ptr<ContextImpl> create(MpiContextManager&& mpi);
+
+    /*!
+     * \brief Copy disallowed because Session state would become ambiguous.
+     *
+     * The API implementation needs to unambiguously determine
+     * which Sessions and Contexts are associated with each other.
+     * \{
+     */
+    ContextImpl(const ContextImpl&) = delete;
+    ContextImpl& operator=(const ContextImpl&) = delete;
+    //! \}
+
+    /*!
+     * \brief Objects are not trivial to move.
+     *
+     * \todo Implement move semantics. Requires a moveable const MpiContextManager and
+     *       LegacyMdrunOptions members.
+     *
+     * \{
+     */
+    ContextImpl(ContextImpl&&) = delete;
+    ContextImpl& operator=(ContextImpl&&) = delete;
+    //! \}
+
+    /*!
+     * \brief Translate the workflow to the execution context and launch.
+     *
+     * \param work workflow graph
+     * \return ownership of a new session
+     *
+     * \todo This probably makes more sense as a free function, but we need to determine access policies.
+     *
+     * Session is returned with shared_ptr ownership so that Context
+     * can hold a weak_ptr and because Session Resources handles
+     * are still evolving.
+     * \todo Hide lifetime management and ownership from handle object.
+     * We can achieve the necessary aspects of this shared_ptr at a lower level of implementation.
+     * Note also that returned value policies can be implemented in higher level wrappers to ensure
+     * correct object lifetime scope. (See pybind, for instance.)
+     */
+    std::shared_ptr<Session> launch(const Workflow& work);
+
+    /*!
+     * \brief Retain the ability to find a launched session while it exists.
+     *
+     * The client owns the Session launched by a Context, but it is helpful
+     * for the Context to know if it has an active Session associated with it.
+     *
+     * \todo Use registration/deregistration protocol instead.
+     *       Requires logging facility.
+     */
+    std::weak_ptr<Session> session_;
+
+    /*!
+     * \brief mdrun command line arguments.
+     *
+     * Store arguments provided by the client and pass them when launching
+     * a simulation runner. This allows client code to access the same
+     * options as are available to mdrun on the command line while the API
+     * evolves.
+     */
+    MDArgs mdArgs_;
+
+    /*!
+     * \brief Legacy option-handling and set up for mdrun.
+     *
+     * This object should not exist, but is necessary now to introduce
+     * the API in a way that means CLI and API work similarly and do not
+     * duplicate definitions e.g. of command-line options.
+     */
+    gmx::LegacyMdrunOptions options_;
+
+    /*!
+     * \brief Scoped MPI management.
+     *
+     * Part of the ContextImpl invariant establishes a point where MPI initialization status is
+     * known.
+     *
+     * Note that the MpiContextManager invariant is dependent on configure-time options to the
+     * GROMACS library. Specifically, MpiContextManager::communicator() is guaranteed to be
+     * MPI_COMM_NULL if and only if the library was built without an external MPI library.
+     * This is confusing and we should split either this class or the
+     * MpiContextManager class. Another alternative would be to use a std::optional here and to
+     * allow the std::unique_ptr<MpiContextManager> provided to ContextImpl::create() to be null,
+     * but that means a slight change of documented behavior protocol. See #3688 and #3650 for
+     * follow up.
+     *
+     * To ensure the MpiContextManager is initialized only once, we use a const member that must
+     * be initialized at construction.
+     */
+    const MpiContextManager mpi_;
+
+private:
+    /*!
+     * \brief Basic constructor.
+     *
+     * Don't use this. Use create() to get a shared pointer right away.
+     * Otherwise, shared_from_this() is potentially dangerous.
+     */
+    explicit ContextImpl(MpiContextManager&& mpi) noexcept(std::is_nothrow_constructible_v<gmx::LegacyMdrunOptions>);
+};
+
+/*!
+ * \brief Allow client code to hold a library communicator.
+ *
+ * MPI-enabled client code and thread-MPI GROMACS libraries cannot share headers
+ * in which MPI symbols (such as MPI_Comm) are defined. This class allows a
+ * client to refer to a library communicator opaquely. See gmxapi::offerComm().
+ *
+ * In the initial gmxapi::ResourceAssignment implementation, CommHandle objects
+ * are the only possible recipients of assigned resources. If future implementations
+ * allow richer resource assignment, CommHandle may be superseded by a more
+ * elaborate interface. Otherwise, all we need is this simple type proxy.
+ */
+class CommHandle
+{
+public:
+    using commType = MPI_Comm;
+    MPI_Comm communicator{ MPI_COMM_NULL };
+};
+
+} // end namespace gmxapi
+#endif // GMXAPI_CONTEXT_IMPL_H
similarity index 94%
rename from src/api/cpp/createsession.h
rename to api/gmxapi/cpp/createsession.h
index dd28e05c248fbc3e7f3d1d372b4b26471860fe54..fd6b2fe0c4d659e6fc2d46f98048dd3a6983171c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +43,8 @@
 
 #include "gromacs/mdrunutility/logging.h"
 
+// Above are headers for dependencies.
+// Following are public headers for the current module.
 #include "gmxapi/context.h"
 #include "gmxapi/session.h"
 
similarity index 96%
rename from src/api/cpp/exceptions.cpp
rename to api/gmxapi/cpp/exceptions.cpp
index 04365db5c833ea90c0740b509e9886188ac6cade..4400b899bbc369cbcb9afb7629348108a605316e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 96%
rename from src/api/cpp/gmxapi.cpp
rename to api/gmxapi/cpp/gmxapi.cpp
index 8711b4fe34aaf098a106b9f4a212f01a3cb662b6..73dc05b2edd52c20cf21bc24c5b8f95068d052ab 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 98%
rename from src/api/cpp/md.cpp
rename to api/gmxapi/cpp/md.cpp
index 7eb8d5d5d31e613fea9e846e1494613016ec8971..d238c938e8a1251be85691abaa22d6f984e0cd24 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 97%
rename from src/api/cpp/md_impl.h
rename to api/gmxapi/cpp/md_impl.h
index 26a91a00fdea6a5050495831b5cd7ca153ea3456..55862744409d1e094f37ade04b1e22c15a79648d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 96%
rename from src/api/cpp/mdmodule.cpp
rename to api/gmxapi/cpp/mdmodule.cpp
index b4c20040b226dad62c88315de4a9e256702716e6..571b2af242584e6844009818bd0eaa19eb4910e1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 98%
rename from src/api/cpp/mdsignals.cpp
rename to api/gmxapi/cpp/mdsignals.cpp
index 3be314b2b78814f22921a29b1ce34e8ae9bc0a06..6cf8f6158a9ee6f29def2e89707dc5319618c70b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  */
 
 #include <algorithm>
-
 #include <atomic>
-
 #include <memory>
+
 #include "gromacs/mdlib/simulationsignal.h"
 #include "gromacs/mdrun/runner.h"
 #include "gromacs/utility/gmxassert.h"
similarity index 97%
rename from src/api/cpp/mdsignals.h
rename to api/gmxapi/cpp/mdsignals.h
index a2fee9c456fc24ab5786ba82bae22c65858eacd5..efe388cc6fca2d9c6515088e89fd4e48f9cabca6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <atomic>
 #include <functional>
-
 #include <memory>
+
 #include "gromacs/mdlib/simulationsignal.h"
 #include "gromacs/mdlib/stophandler.h"
 #include "gromacs/mdrun/runner.h"
 
+// Public gmxapi headers.
 #include "gmxapi/session.h"
 #include "gmxapi/md/mdsignals.h"
 
+// Internal gmxapi headers.
 #include "session_impl.h"
 
 namespace gmxapi
diff --git a/api/gmxapi/cpp/resourceassignment.cpp b/api/gmxapi/cpp/resourceassignment.cpp
new file mode 100644 (file)
index 0000000..877ce94
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \brief Provide a bridge to communication resources appropriate for the library.
+ *
+ * Define the helper functions that the library provides for to help the client
+ * implement the interfaces. (This is dependent on the library configuration.)
+ *
+ * \author "M. Eric Irrgang <ericirrgang@gmail.com"
+ */
+
+#include "gmxapi/mpi/resourceassignment.h"
+
+#include "config.h"
+
+#include "gromacs/utility/gmxmpi.h"
+
+#include "context_impl.h"
+
+namespace gmxapi
+{
+
+ResourceAssignment::~ResourceAssignment() = default;
+
+// Base implementation is only overridden by client-provided code in certain
+// combinations of library and client build configurations.
+void ResourceAssignment::applyCommunicator(CommHandle* dst) const
+{
+    dst->communicator = MPI_COMM_NULL;
+}
+
+#if GMX_LIB_MPI
+void offerComm(MPI_Comm src, CommHandle* dst)
+{
+    dst->communicator = src;
+}
+#endif
+
+} // end namespace gmxapi
similarity index 86%
rename from src/api/cpp/session.cpp
rename to api/gmxapi/cpp/session.cpp
index 567c064cdb4057cce20f955767ced863ee40b151..2b909663b094a2d9b579957fb6bccbffe6a333cd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 
-#include "gmxpre.h"
-
-#include "config.h"
-
 #include "gmxapi/session.h"
 
 #include <memory>
+
 #include "gromacs/mdlib/sighandler.h"
 #include "gromacs/mdrunutility/logging.h"
 #include "gromacs/restraint/restraintpotential.h"
 namespace gmxapi
 {
 
-/*!
- * \brief Provide RAII management of communications resource state.
- *
- * To acquire an MpiContextManager is to have assurance that any external MPI
- * environment is ready to use. When the MpiContextManager is released or
- * goes out of scope, the destructor finalizes the resources.
- *
- * Note that thread-MPI chooses the number of ranks and constructs its
- * MPI communicator internally, so does not and is unlikely to ever
- * participate here.
- *
- * \todo There is no resource for logging or reporting errors during initialization
- * \todo Remove this class by managing the MPI context with mpi4py and so
- *       configuring the SimulationContext externally
- *
- * \ingroup gmxapi
- */
-class MpiContextManager
-{
-public:
-    MpiContextManager()
-    {
-        gmx::init(nullptr, nullptr);
-        GMX_RELEASE_ASSERT(!GMX_LIB_MPI || gmx_mpi_initialized(),
-                           "MPI should be initialized before reaching this point.");
-    };
-
-    ~MpiContextManager()
-    {
-        // This is always safe to call. It is a no-op if
-        // thread-MPI, and if the constructor completed then the
-        // MPI library is initialized with reference counting.
-        gmx::finalize();
-    }
-
-    /*!
-     * \brief Exclusive ownership of a scoped context means copying is impossible.
-     *
-     * \{
-     */
-    MpiContextManager(const MpiContextManager&) = delete;
-    MpiContextManager& operator=(const MpiContextManager&) = delete;
-    //! \}
-
-    /*!
-     * \brief Move semantics are trivial.
-     *
-     * \{
-     */
-    MpiContextManager(MpiContextManager&&) noexcept = default;
-    MpiContextManager& operator=(MpiContextManager&&) noexcept = default;
-    //! \}
-};
-
 SignalManager::SignalManager(gmx::StopHandlerBuilder* stopHandlerBuilder) :
     state_(std::make_shared<gmx::StopSignal>(gmx::StopSignal::noSignal))
 {
@@ -188,12 +131,10 @@ SessionImpl::SessionImpl(std::shared_ptr<ContextImpl> context,
                          gmx::SimulationContext&&     simulationContext,
                          gmx::LogFilePtr              fplog) :
     context_(std::move(context)),
-    mpiContextManager_(std::make_unique<MpiContextManager>()),
     simulationContext_(std::move(simulationContext)),
     logFilePtr_(std::move(fplog))
 {
     GMX_ASSERT(context_, "SessionImpl invariant implies valid ContextImpl handle.");
-    GMX_ASSERT(mpiContextManager_, "SessionImpl invariant implies valid MpiContextManager guard.");
 
     // \todo Session objects can have logic specialized for the runtime environment.
 
similarity index 93%
rename from src/api/cpp/session_impl.h
rename to api/gmxapi/cpp/session_impl.h
index a223519a425014d066a5d7dd8404ac099430896f..3d9699e5cf06765d1565fe09be312dac32052b98 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <map>
 
-#include "gromacs/mdrunutility/logging.h"
 #include "gromacs/mdrun/runner.h"
 #include "gromacs/mdrun/simulationcontext.h"
+#include "gromacs/mdrunutility/logging.h"
 
+// Above are the public headers from other modules.
+// Following are public headers for the current module.
 #include "gmxapi/context.h"
 #include "gmxapi/md.h"
+#include "gmxapi/status.h"
 #include "gmxapi/md/mdmodule.h"
 #include "gmxapi/session/resources.h"
-#include "gmxapi/status.h"
+
 
 namespace gmxapi
 {
 
 // Forward declaration
-class MpiContextManager; // Locally defined in session.cpp
-class ContextImpl;       // locally defined in context.cpp
-class SignalManager;     // defined in mdsignals_impl.h
+class ContextImpl;   // locally defined in context.cpp
+class SignalManager; // defined in mdsignals_impl.h
 
 /*!
  * \brief Implementation class for executing sessions.
@@ -199,18 +201,12 @@ private:
      * \brief Extend the life of the owning context.
      *
      * The session will get handles for logging, UI status messages,
-     * and other facilities through this interface.
+     * and other facilities through this interface. This is a facility
+     * provided by the client to the Session implementation during
+     * Context.launch().
      */
     std::shared_ptr<ContextImpl> context_;
 
-    /*!
-     * \brief RAII management of gmx::init() and gmx::finalize()
-     *
-     * Uses smart pointer to avoid exposing type definition.
-     * \todo Not fully implemented.
-     */
-    std::unique_ptr<MpiContextManager> mpiContextManager_;
-
     /*!
      * \brief Simulation runner object.
      *
@@ -221,6 +217,10 @@ private:
 
     /*!
      * \brief An active session owns the resources it is using.
+     *
+     * This encapsulate details of the run time context that the
+     * Session makes available to the simulator, tied to the
+     * lifetime of the Session.
      */
     gmx::SimulationContext simulationContext_;
 
similarity index 98%
rename from src/api/cpp/sessionresources.h
rename to api/gmxapi/cpp/sessionresources.h
index bf3082e6a0903510b0c3c9d286affac019a334a8..3bdaf680183ce704145f2658eee420d03c02193c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * \ingroup gmxapi
  */
 
-#include "gmxapi/session/resources.h"
-
 #include <string>
 
-#include "gmxapi/md/mdsignals.h"
 #include "gmxapi/session.h"
+#include "gmxapi/md/mdsignals.h"
+#include "gmxapi/session/resources.h"
 
 namespace gmxapi
 {
similarity index 97%
rename from src/api/cpp/status.cpp
rename to api/gmxapi/cpp/status.cpp
index cd7858669f532a2099dbaa74fc294524b973a980..ac064deefb999fde883600c36a3a9dfe2ef388c2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 98%
rename from src/api/cpp/system.cpp
rename to api/gmxapi/cpp/system.cpp
index 604f5c069f1ad27ca7a97fc348722b7ad40e52cc..2a25b842905026ee8c42a1c323b7515da05e55a6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -36,9 +36,9 @@
 #include "gmxapi/system.h"
 
 #include <array>
+#include <memory>
 
 #include "gromacs/utility.h"
-#include <memory>
 #include "gromacs/mdrun/runner.h"
 
 #include "gmxapi/context.h"
similarity index 97%
rename from src/api/cpp/system_impl.h
rename to api/gmxapi/cpp/system_impl.h
index 91e2c4c19df727be60110bb209c04b9c174a4c89..b9823e2e644177578236be9b38b19e905efba01b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 98%
rename from src/api/cpp/tpr.cpp
rename to api/gmxapi/cpp/tpr.cpp
index 1ddcd43cb82ffafbec9e0025c7ac0aecd2c07365..2d498079f87a400a8053c5ac105233cba6c1f4b0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -764,12 +764,9 @@ bool rewrite_tprfile(const std::string& inFile, const std::string& outFile, doub
 
     /* set program name, command line, and default values for output options */
     gmx_output_env_t* oenv;
-    gmx::TimeUnit     timeUnit = gmx::TimeUnit_Default;
+    gmx::TimeUnit     timeUnit = gmx::TimeUnit::Default;
     bool              bView{ false }; // argument that says we don't want to view graphs.
-    int               xvgFormat{ 0 };
-    output_env_init(&oenv, gmx::getProgramContext(),
-                    static_cast<time_unit_t>(timeUnit + 1), // NOLINT(misc-misplaced-widening-cast)
-                    bView, static_cast<xvg_format_t>(xvgFormat + 1), 0);
+    output_env_init(&oenv, gmx::getProgramContext(), timeUnit, bView, XvgFormat::Xmgrace, 0);
 
     double run_t = irInstance.init_step * irInstance.delta_t + irInstance.init_t;
 
similarity index 97%
rename from src/api/cpp/version.cpp
rename to api/gmxapi/cpp/version.cpp
index 6a06b3c173474767a882b2b0bbd363f84b6627d9..f707e1cea9f4d2ac28ed7b23894cc1cf081c188d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 96%
rename from src/api/cpp/workflow.cpp
rename to api/gmxapi/cpp/workflow.cpp
index d37e6b6f7bcac7c91cfa181376ca4dff3f5f4f82..79e2861b9c73448b9c4491e6222cdebd0a790491 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -68,7 +68,7 @@ NodeSpecification::paramsType MDNodeSpecification::params() const noexcept
 NodeKey Workflow::addNode(std::unique_ptr<NodeSpecification> spec)
 {
     // TODO capture provided NodeSpecification.
-    // Relates to gmxapi milestone 7, described at https://redmine.gromacs.org/issues/2585
+    // Relates to gmxapi milestone 7, described at https://gitlab.com/gromacs/gromacs/-/issues/2585
     throw gmxapi::NotImplementedError("Member function not yet implemented or used.");
     (void)spec;
     return {};
similarity index 98%
rename from src/api/cpp/workflow.h
rename to api/gmxapi/cpp/workflow.h
index ccba142620957e31c142d9cad7f8ba2fbface9dd..0571395ec263cd050cf113cb4ec0d52421bb7546 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -149,7 +149,7 @@ public:
      *
      * \todo Not yet implemented.
      */
-    NodeKey addNode(std::unique_ptr<NodeSpecification> spec);
+    static NodeKey addNode(std::unique_ptr<NodeSpecification> spec);
 
     /*!
      * \brief Get the node specification for a provided key.
similarity index 96%
rename from src/api/cpp/workflow_impl.h
rename to api/gmxapi/cpp/workflow_impl.h
index 4a20aaf3d807af3d3380395574b382a5a520a4d0..0a3b7b4688853c6573a1aa1620efca15ce95d888 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * \ingroup gmxapi
  */
 
-#include "workflow.h"
-
 #include <memory>
 #include <string>
 
 #include "gmxapi/exceptions.h"
 
+// Local module internal headers.
+#include "workflow.h"
+
 namespace gmxapi
 {
 
similarity index 97%
rename from src/api/cpp/include/gmxapi/compat/mdparams.h
rename to api/gmxapi/include/gmxapi/compat/mdparams.h
index 0f40d4ce82b6cbde16fa17074bf967fe93ca7d4c..4d4837cbb6546fa965ab2d7c51faf86c36a3a28c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,8 +48,8 @@
 #include <string>
 #include <vector>
 
-#include "gmxapi/gmxapicompat.h"
 #include "gmxapi/gmxapi.h"
+#include "gmxapi/gmxapicompat.h"
 
 namespace gmxapicompat
 {
similarity index 98%
rename from src/api/cpp/include/gmxapi/compat/tpr.h
rename to api/gmxapi/include/gmxapi/compat/tpr.h
index e3f79985adb2df053efd344a7efec4e957d34978..87527acb77060952c69c9cafe1f04279d253b23a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 69%
rename from src/api/cpp/include/gmxapi/context.h
rename to api/gmxapi/include/gmxapi/context.h
index 855599ce2d539401a5f0882e3f41c62b0c12f8f6..296b24d7e3ec5007cb78373d3a93590828f02cdb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  *
  * \author M. Eric Irrgang <ericirrgang@gmail.com>
  * \ingroup gmxapi
+ *
+ * # Notes on scope
+ *
+ * Client owns Context and MultiProcessingResources, which exist entirely in client scope.
+ *
+ * Note that MpiContextManager and Session live in library scope.
+ *
+ * # MPI-enabled clients
+ *
+ * When an MPI toolchain is compatible with the GROMACS build
+ * (see [#3672](https://gitlab.com/gromacs/gromacs/-/issues/3672)),
+ * multi-process clients may determine how processes are allocated to GROMACS
+ * simulations by providing Contexts with specific MPI Communicators to
+ * createContext(const MultiProcessingResources& resources)
+ *
+ * MultiProcessingResources is an opaque type of library-internal resources.
+ * Clients acquire such resources with the assignResource() template helper.
+ * See gmxapi_mpi.h
  */
 
 #include <memory>
@@ -105,7 +123,7 @@ public:
     /*!
      * \brief Get a handle to a new default context object.
      */
-    Context();
+    Context() = delete;
     ~Context();
 
     /*!
@@ -168,6 +186,45 @@ private:
     std::shared_ptr<ContextImpl> impl_;
 };
 
+// Forward declaration for interoperation with the library.
+// Client code implements a concrete ResourceAssignment indirectly by instantiating
+// assignResource() through the gmxapi_mpi.h template header.
+class ResourceAssignment;
+
+// See template header gmxapi_mpi.h
+template<typename CommT>
+std::unique_ptr<ResourceAssignment> assignResource(CommT communicator);
+
+/*!
+ * \brief Initialize a new API Context to manage resources and software environment.
+ *
+ * The client is responsible for keeping the Context instance alive for at least
+ * as long as any API objects initialized from it. We allow this responsibility
+ * to be placed on the client (rather than using a global Singleton) because the
+ * library is theoretically reentrant, and multiple Context objects may exist.
+ *
+ * Client may explicitly assign run time computing resources for use within the Context.
+ * Otherwise, the library will try to automatically detect and allocate computing resources.
+ *
+ * Currently, only MPI communication scope can be assigned explicitly.
+ * Future development is needed before this interface can influence the subdivision
+ * of available GPUs or thread-based processor allocation. See #3650 and #3688.
+ *
+ * \return Initialized Context instance.
+ *
+ * \internal
+ * Use cases:
+ *
+ * 1. tMPI and client-provided comm: provide a place for safety checks, then construct a suitable
+ * dummy MpiContextManager.
+ * 2. tMPI and no client-provided comm: construct suitable dummy MpiContextManager
+ * 3. MPI and client-provided comm: use compatible comm. error if COMM_NULL
+ * 4. MPI and no client-provided comm: generate MpiContextManager with COMM_WORLD
+ *
+ */
+Context createContext();
+Context createContext(const ResourceAssignment& resources);
+
 } // end namespace gmxapi
 
 #endif // header guard
similarity index 98%
rename from src/api/cpp/include/gmxapi/exceptions.h
rename to api/gmxapi/include/gmxapi/exceptions.h
index 705b3aeb17a0fbc23e00a6a4de13d711f9ee5a13..9c2493e73ec8d5184e677503686cc848c63c023f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 99%
rename from src/api/cpp/include/gmxapi/gmxapi.h
rename to api/gmxapi/include/gmxapi/gmxapi.h
index 4e4bc2752d7d8bc7876a5f657ddb3599332fbb35..5331146922220f457997d42e6f329f6257f31a04 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -509,7 +509,7 @@ private:
  * e.g. MPI, Python, numpy, GROMACS, JSON, etc.
  * \todo Actually check the size of the types.
  *
- * \see https://redmine.gromacs.org/issues/2993 for discussion.
+ * \see https://gitlab.com/gromacs/gromacs/-/issues/2993 for discussion.
  */
 enum class GmxapiType
 {
similarity index 99%
rename from src/api/cpp/include/gmxapi/gmxapicompat.h
rename to api/gmxapi/include/gmxapi/gmxapicompat.h
index 67b33ed8668dfc22f86a03641a0b742a1704a4c4..436441d4876c2f4445a8c69078f33b9bc201aafb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 97%
rename from src/api/cpp/include/gmxapi/gromacsfwd.h
rename to api/gmxapi/include/gmxapi/gromacsfwd.h
index e40afa29955af2fe213da4377573fd81a16f0f4d..63e49b0b0e0e19b96a3c692cae4cc5846a94d239 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 98%
rename from src/api/cpp/include/gmxapi/md.h
rename to api/gmxapi/include/gmxapi/md.h
index a48411aa4ebf67db1370bf1f2e3531da62499679..97d7bb97d30cbef1d82364eb527a26ef9b0e545e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 98%
rename from src/api/cpp/include/gmxapi/md/mdmodule.h
rename to api/gmxapi/include/gmxapi/md/mdmodule.h
index e03726f9a859c2cd3d60c7920df1b89c4707e3a3..6618b8c3787928a5f218f04b9034d2e82395fd10 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 98%
rename from src/api/cpp/include/gmxapi/md/mdsignals.h
rename to api/gmxapi/include/gmxapi/md/mdsignals.h
index b6b9243bc823a106af3e984154bc37f6b2d0ccce..3aee6383b4e826f7f3c05e938e21f1a8cef1fca9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
diff --git a/api/gmxapi/include/gmxapi/mpi/gmxapi_mpi.h b/api/gmxapi/include/gmxapi/mpi/gmxapi_mpi.h
new file mode 100644 (file)
index 0000000..48ef552
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMXAPI_MPI_H
+#define GMXAPI_MPI_H
+
+#include <memory>
+
+#include <mpi.h>
+
+#include "gmxapi/context.h"
+#include "gmxapi/exceptions.h"
+
+#include "gmxapi/mpi/resourceassignment.h"
+
+/*! \file
+ * \brief Provide details of any MPI implementation used when building the library.
+ *
+ * If the gmxapi library was built with MPI-enabled GROMACS, client software
+ * using a compatible MPI implementation may use this header to access additional
+ * functionality.
+ *
+ * Client software should use the CMake infrastructure to ensure a compatible
+ * MPI implementation.
+ *
+ * Use of an incompatible MPI implementation will produce linking errors.
+ *
+ * \todo With resolution of #3672, we can include additional checks.
+ *
+ */
+
+namespace gmxapi
+{
+
+/*!
+ * \brief Convey resources assigned by the client to the new library Context.
+ *
+ * Only enabled if the required MPI library calls are available.
+ */
+template<typename CommT, decltype(MPI_Comm_rank(CommT(), nullptr)) = 0, decltype(MPI_Comm_size(CommT(), nullptr)) = 0>
+class ResourceAssignmentImpl : public ResourceAssignment
+{
+public:
+    ~ResourceAssignmentImpl() override = default;
+
+    /*!
+     * \brief Initialize from borrowed communicator.
+     *
+     * \param communicator Client-provided communicator relayed through assignResource()
+     *
+     * Create an abstract wrapper for client-provided values with which to initialize
+     * simulation resources. When this wrapper is used, the client is responsible for providing
+     * a valid communicator that will remain valid for the life of the consumer.
+     *
+     * \throws UsageError if client has not provided a valid communicator.
+     */
+    explicit ResourceAssignmentImpl(const CommT& communicator) : communicator_{ communicator }
+    {
+        if (communicator_ == MPI_COMM_NULL)
+        {
+            throw UsageError("Cannot assign a Null communicator.");
+        }
+        int flag = 0;
+        MPI_Initialized(&flag);
+        if (!flag)
+        {
+            throw UsageError("Client has not initialized MPI context.");
+        }
+    }
+
+    [[nodiscard]] int size() const override
+    {
+        assert(communicator_ != MPI_COMM_NULL && "Class invariant broken: invalid communicator.");
+        int size = 0;
+        MPI_Comm_size(communicator_, &size);
+        return size;
+    }
+
+    [[nodiscard]] int rank() const override
+    {
+        assert(communicator_ != MPI_COMM_NULL && "Class invariant broken: invalid communicator.");
+        // This default value will never be read, but the compiler can't tell
+        // that it is initialized by the MPI call.
+        int rank = -1;
+        MPI_Comm_rank(communicator_, &rank);
+        return rank;
+    }
+
+    void applyCommunicator(CommHandle* dst) const override
+    {
+        assert(communicator_ != MPI_COMM_NULL && "Class invariant broken: invalid communicator.");
+        offerComm(communicator_, dst);
+    }
+
+    CommT communicator_;
+};
+
+/*!
+ * \brief Template header utility for connecting to MPI implementations.
+ *
+ * The client provides a communicator for work executed within the scope of the Context.
+ * Client remains responsible for freeing the communicator and finalizing the MPI environment.
+ *
+ * To use this helper function, the client software build environment must be
+ * configured for an MPI implementation compatible with the target GROMACS library.
+ *
+ * If the library and client software are built with compatible MPI implementations,
+ * the template will capture the communicator in an opaque container that can be
+ * passed to the ContextImpl creation function. Otherwise, the opaque container
+ * will carry a suitable NULL object. The template definition is necessarily
+ * provided by a generated template header, since the definition depends on the
+ * GROMACS installation.
+ *
+ * \tparam CommT Deduced type of client-provided communicator.
+ * \param communicator MPI (sub)communicator linking collaborating processes in the new Context.
+ * \return Opaque resource handle usable when setting up execution context.
+ *
+ * \see createContext(std::unique_ptr<ResourceAssignment> resources)
+ *
+ * The communicator resource type is a template parameter because MPI_Comm is commonly a C
+ * typedef that varies between implementations, so we do not want to couple our
+ * API to it, but we cannot forward-declare it.
+ *
+ * See also https://gitlab.com/gromacs/gromacs/-/issues/3650
+ *
+ * \throws UsageError if the provided resource is not usable.
+ */
+template<typename CommT>
+std::unique_ptr<ResourceAssignment> assignResource(CommT communicator)
+{
+    if (communicator == MPI_COMM_NULL)
+    {
+        throw UsageError("Cannot assign a Null communicator.");
+    }
+    return std::make_unique<ResourceAssignmentImpl<CommT>>(communicator);
+}
+
+// Also ref https://stackoverflow.com/questions/49259704/pybind11-possible-to-use-mpi4py
+
+} // end namespace gmxapi
+
+#endif // GMXAPI_MPI_H
similarity index 99%
rename from src/api/cpp/include/gmxapi/session.h
rename to api/gmxapi/include/gmxapi/session.h
index a3c60f29396b3069ccb3d977d1e69f911f869f40..b71feed4960b22c286aa7ed42301ba199f102b2d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 96%
rename from src/api/cpp/include/gmxapi/session/resources.h
rename to api/gmxapi/include/gmxapi/session/resources.h
index af90c87c3abbd13b6c326d373bdd5aeca3e4e4b8..0acd225daf7156637ee812eca20f998dcc170783 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 98%
rename from src/api/cpp/include/gmxapi/status.h
rename to api/gmxapi/include/gmxapi/status.h
index ffc12a3f80e133b3a2562cfa91f913e97690af8f..f4922cf2a8e5f1c7a6a0834d90eb6a769ab53fce 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 99%
rename from src/api/cpp/include/gmxapi/system.h
rename to api/gmxapi/include/gmxapi/system.h
index eac1f6ca961f9ad9b82c96737aed09ee08d15308..2eced6e4d93fe00093bbe3d1870f36439115ed86 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
diff --git a/api/gmxapi/include/gmxapiversion.h.in b/api/gmxapi/include/gmxapiversion.h.in
new file mode 100644 (file)
index 0000000..7977171
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMXAPI_VERSION_H
+#define GMXAPI_VERSION_H
+/*! \file
+ * \brief Implement versioning API for C++ external GROMACS interface.
+ *
+ *  Defines a class Version in the gmxapi namespace providing static methods
+ *  for use by API client code at compile time and run time.
+ *
+ *  External interface uses semantic versioning scheme in which the major version
+ *  specifies the API compatibility level, and minor version indicates additional
+ *  features that may or may not be ABI compatible. Feature availability may be
+ *  implied by API level, but the library can be queried for availability of
+ *  explicitly named features. This is essential to allow compatibility and
+ *  flexibility during development.
+ *
+ *  \ingroup gmxapi
+ *
+ *  \internal
+ *   The version numbers for gmxapi are encoded in the repository solely in the
+ * `src/api/CMakeLists.txt` file. During CMake configuration, the
+ * `src/api/cpp/include/gmxapi/version.h` file is created so that the built library can report this
+ * version through the `gmxapi::Version` interface. Client code should include the installed `gmxapi/version.h`
+ * header in order to embed the constants `gmxapi::c_majorVersion`, `gmxapi::c_minorVersion`, and
+ * `gmxapi::c_patchVersion` so that compatibility checks can be performed at runtime. Additional
+ * CMake `check` utilities will probably be necessary to allow GROMACS installations to be forward
+ * compatible with client code developed against the GROMACS master branch.
+ *
+ * When a new software release is tagged, the next commit on the development branch should increment
+ * the patch level to distinguish development builds from the tagged release. As incompatibilities
+ * are introduced in feature branches, minor or major version number should be incremented as
+ * appropriate. At this time, client code has no indication of whether the version presented in a
+ * development build of gmxapi is an officially specified API revision or is subject to change.
+ * Developers coding against development branches should keep this in mind. If this becomes
+ * problematic, please offer your suggestions or propose a revision to the `gmxapi::Version` API.
+ *
+ *
+ */
+
+#include <string>
+
+namespace gmxapi
+{
+
+/*!
+ * \brief Type alias for version data type.
+ *
+ * \ingroup gmxapi
+ */
+using version_t = int;
+
+//! Major version number of gmxapi API support.
+static constexpr const version_t c_majorVersion = @GMXAPI_MAJOR@;
+//! Minor version number of gmxapi API support.
+static constexpr const version_t c_minorVersion = @GMXAPI_MINOR@;
+//! Patch level of gmxapi API support.
+static constexpr const version_t c_patchVersion = @GMXAPI_PATCH@;
+//! C string representation of gmxapi release.
+static const char c_release[] = "@GMXAPI_RELEASE@";
+
+/*!
+ * \brief Provide API library version information for client code.
+ *
+ * Allow client code to query the currently loaded gmxapi library object to find the built version. Provide helpers
+ * to compare against the features for which the client was written and the headers against which it was compiled.
+ *
+ * \ingroup gmxapi
+ */
+class Version
+{
+public:
+    /*!
+     * \brief Query gmxapi major version.
+     *
+     * \returns major version number
+     */
+    static version_t majorVersion();
+
+    /*! \brief Query gmxapi minor version.
+     *
+     * \returns minor version number
+     */
+    static version_t minorVersion();
+
+    /*! \brief Query gmxapi patch level.
+     *
+     * \returns patch level number
+     */
+    static version_t patchVersion();
+
+    /*! \brief Get formatted release string.
+     *
+     * Format is major.minor.patch
+     * \returns release as string
+     */
+    static std::string release();
+
+    /*! \brief Check features availability
+     *
+     * Features may be named in the documentation
+     * to improve readability of client code and to simplify development. Prefer
+     * this mechanism when checking for features still under development or to
+     * distinguish between interface levels of a specific feature.
+     * \param featurename Feature name described in the feature's documentation.
+     * \returns `true` if the named feature is available.
+     */
+    static bool hasFeature(const std::string& featurename);
+
+    /*! \brief Check for sufficiently high API version number.
+     *
+     *  \returns `true` if gmxapi library version is the same or greater than the argument(s).
+     *  \param major gmxapi major version number.
+     *  \param minor gmxapi minor version number (optional).
+     *  \param patch patch level of the api library (optional).
+     */
+    static bool isAtLeast(version_t major, version_t minor = 0, version_t patch = 0);
+};
+
+} // namespace gmxapi
+
+#endif // version.h include guard
diff --git a/api/gmxapi/include/resourceassignment.cmakein.h b/api/gmxapi/include/resourceassignment.cmakein.h
new file mode 100644 (file)
index 0000000..420b595
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMXAPI_RESOURCEASSIGNMENT_H
+#define GMXAPI_RESOURCEASSIGNMENT_H
+
+/*! \file
+ * \brief Provide build-specific overloads for client-MPI-dependent stuff.
+ *
+ * Define the interfaces that a client must implement to generate MpiContextManager for the library.
+ * Client code should use the gmxapi_mpi.h template header to generate code supporting
+ * the required interface. (Client code should not need to include this header directly.)
+ *
+ * \note This is a generated header. Some definitions are determined when the GROMACS library
+ * build is configured.
+ *
+ * If the library is built with tMPI, CommHandle is empty and offerComm is not defined.
+ *
+ * If the library is built with an MPI library, CommHandle holds a native MPI_Comm object and
+ * the library-provided offerComm is used by the client MultiProcessingResources implementation
+ * to support passing a copy of the communicator.
+ *
+ * Note: The communicator is not `dup`ed in this call. If the library later duplicates
+ * the offered communicator, the library will be responsible for freeing the duplicate.
+ * However, the caller is responsible for keeping any MPI environment valid while the library is
+ * in use. For details, \see MpiContextManager.
+ *
+ * \author "M. Eric Irrgang <ericirrgang@gmail.com>"
+ */
+
+#include <functional>
+#include <memory>
+
+// The interface in this header is determined when the GROMACS library build is configured.
+#cmakedefine01 GMX_LIB_MPI
+#if GMX_LIB_MPI
+#    include <mpi.h>
+#endif
+
+namespace gmxapi
+{
+
+// Forward declaration for library resources.
+// CommHandle is opaque to the public API, but allows extensions to implement
+// required library interfaces by composing the behavior of helpers like offerComm().
+// The abstraction is important because the library implementations depend on
+// the options with which GROMACS was built.
+class CommHandle;
+
+/*!
+ * \brief Convey the resources that the client has directed the library to use within a Context.
+ *
+ * The object itself does not convey ownership of resources. However, the interfaces
+ * for declaring assigned resources must have well-specified (and documented)
+ * semantics for resource ownership. See assignResource().
+ * (Note: the initial implementation only allows for assignment of an MPI Communicator.)
+ *
+ * The library and client share this interface definition. The implementation is
+ * provided by client code with the aid of a template header.
+ * Client developers should refer instead to gmxapi_mpi.h.
+ */
+class ResourceAssignment
+{
+public:
+    virtual ~ResourceAssignment();
+    [[nodiscard]] virtual int size() const = 0;
+    [[nodiscard]] virtual int rank() const = 0;
+    virtual void              applyCommunicator(class CommHandle* dst) const;
+};
+
+/*! \brief Offer the client communicator to the library.
+ *
+ * Helper function allowing clients to provide the MPI communicator for the library.
+ * \param src client-provided communicator.
+ * \param dst library recipient, abstracted to hide library MPI type details.
+ *
+ */
+template<typename CommT>
+void offerComm([[maybe_unused]] CommT src, [[maybe_unused]] CommHandle* dst)
+{
+    // Default overload does nothing.
+}
+
+#if GMX_LIB_MPI
+/*! \brief Offer the client communicator to the library.
+ *
+ * Helper function allowing clients to provide communicator to library.
+ * \param src communicator offered by client
+ * \param dst opaque pointer to library resource destination
+ *
+ * This function is only available in MPI-enabled GROMACS.
+ * Used indirectly in template helpers for client code to implement library interfaces.
+ * Clients should use the assignResource() higher level function in explicit code,
+ * which will evaluable appropriately for the target GROMACS library.
+ *
+ * \todo Provide a stub for docs even when not available.
+ */
+void offerComm(MPI_Comm src, CommHandle* dst);
+#endif
+
+} // end namespace gmxapi
+
+#endif // GMXAPI_RESOURCEASSIGNMENT_H
diff --git a/api/nblib/CMakeLists.txt b/api/nblib/CMakeLists.txt
new file mode 100644 (file)
index 0000000..409e34e
--- /dev/null
@@ -0,0 +1,145 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+#
+# \author Victor Holanda <victor.holanda@cscs.ch>
+# \author Joe Jordan <ejjordan@kth.se>
+# \author Prashanth Kanduri <kanduri@cscs.ch>
+# \author Sebastian Keller <keller@cscs.ch>
+#
+
+# The following are copied directly from src/CMakeLists.txt
+set(IGNORED_CLANG_ALL_WARNINGS
+        "-Wno-c++98-compat -Wno-c++98-compat-pedantic" #No intention of C++98 compability
+        "-Wno-source-uses-openmp" #Don't warn for no-omp build
+        "-Wno-c++17-extensions"   #Allowed in attributes (compilers are required to ignore unknown attributes)
+        "-Wno-documentation-unknown-command" #Custom commands are used
+        "-Wno-covered-switch-default" #GCC gives maybe-uninitialized without default label and checks for illegal enum values.
+        "-Wno-switch-enum" # default statement for enum is OK
+
+        # We need to use macros like
+        # GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR. Those will look strange
+        # if they don't have a semicolon after them, and might confuse
+        # tools like IDEs also.
+        "-Wno-extra-semi-stmt"
+
+        #Following ones are undecided/TODO
+        "-Wno-disabled-macro-expansion"
+        "-Wno-cast-align"
+        "-Wno-reserved-id-macro"
+        "-Wno-global-constructors"
+        "-Wno-exit-time-destructors"
+        "-Wno-unused-macros"
+        "-Wno-weak-vtables"
+        "-Wno-conditional-uninitialized"
+        "-Wno-format-nonliteral"
+        "-Wno-shadow"
+        "-Wno-cast-qual"
+        "-Wno-documentation"
+        "-Wno-used-but-marked-unused"
+        "-Wno-padded"
+        "-Wno-float-equal"
+        "-Wno-old-style-cast"
+        "-Wno-conversion"
+        "-Wno-double-promotion")
+string(REPLACE " " ";" IGNORED_CLANG_ALL_WARNINGS "${IGNORED_CLANG_ALL_WARNINGS}")
+
+set(TESTUTILS_DIR ${PROJECT_SOURCE_DIR}/src/testutils)
+include(${PROJECT_SOURCE_DIR}/src/testutils/TestMacros.cmake)
+
+# this allows all nblib tests to be run with "make check-nblib"
+add_custom_target(check-nblib
+        COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -R NbLib
+        COMMENT "Running nblib tests"
+        USES_TERMINAL VERBATIM)
+
+add_library(nblib SHARED "")
+
+target_sources(nblib
+        PRIVATE
+        box.cpp
+        forcecalculator.cpp
+        gmxcalculator.cpp
+        gmxsetup.cpp
+        integrator.cpp
+        interactions.cpp
+        molecules.cpp
+        particletype.cpp
+        simulationstate.cpp
+        topologyhelpers.cpp
+        topology.cpp
+        )
+
+
+set_target_properties(nblib
+        PROPERTIES
+        LINKER_LANGUAGE CXX
+        OUTPUT_NAME "nblib"
+        )
+
+target_link_libraries(nblib PRIVATE libgromacs)
+target_include_directories(nblib PRIVATE ${PROJECT_SOURCE_DIR}/api)
+include_directories(BEFORE ${CMAKE_SOURCE_DIR}/api)
+
+install(TARGETS nblib
+        EXPORT nblib
+        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        INCLUDES DESTINATION include
+        )
+
+if(GMX_INSTALL_NBLIB_API)
+    install(FILES
+            basicdefinitions.h
+            box.h
+            exception.h
+            forcecalculator.h
+            integrator.h
+            interactions.h
+            molecules.h
+            kerneloptions.h
+            nblib.h
+            particletype.h
+            simulationstate.h
+            topology.h
+            topologyhelpers.h
+            DESTINATION include/nblib)
+endif()
+
+add_subdirectory(samples)
+add_subdirectory(util)
+
+if(BUILD_TESTING)
+    add_subdirectory(tests)
+endif()
diff --git a/api/nblib/basicdefinitions.h b/api/nblib/basicdefinitions.h
new file mode 100644 (file)
index 0000000..41dabd3
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements some definitions that are identical to those of gromacs
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef NBLIB_BASICDEFINITIONS_H
+#define NBLIB_BASICDEFINITIONS_H
+
+#include <cmath>
+
+// from utility/real.h
+#if GMX_DOUBLE
+#    ifndef HAVE_REAL
+typedef double real;
+#        define HAVE_REAL
+#    endif
+#else /* GMX_DOUBLE */
+#    ifndef HAVE_REAL
+typedef float real;
+#        define HAVE_REAL
+#    endif
+#endif /* GMX_DOUBLE */
+
+namespace nblib
+{
+
+namespace detail
+{
+// from math/units.h
+constexpr const float  KILO      = 1e3;             /* Thousand        */
+constexpr const double NANO      = 1e-9;            /* A Number        */
+constexpr const double E_CHARGE  = 1.602176634e-19; /* Exact definition, Coulomb NIST 2018 CODATA */
+constexpr const double BOLTZMANN = 1.380649e-23;    /* (J/K, Exact definition, NIST 2018 CODATA */
+constexpr const double AVOGADRO  = 6.02214076e23;   /* 1/mol, Exact definition, NIST 2018 CODATA */
+constexpr const double RGAS      = (BOLTZMANN * AVOGADRO); /* (J/(mol K))  */
+constexpr const double EPSILON0_SI = 8.8541878128e-12;     /* F/m,  NIST 2018 CODATA */
+constexpr const double EPSILON0 = ((EPSILON0_SI * NANO * KILO) / (E_CHARGE * E_CHARGE * AVOGADRO));
+
+// from pbc/ishift.h
+constexpr const int D_BOX_Z = 1;
+constexpr const int D_BOX_Y = 1;
+constexpr const int D_BOX_X = 2;
+constexpr const int N_BOX_Z = (2 * D_BOX_Z + 1);
+constexpr const int N_BOX_Y = (2 * D_BOX_Y + 1);
+constexpr const int N_BOX_X = (2 * D_BOX_X + 1);
+constexpr const int N_IVEC  = (N_BOX_Z * N_BOX_Y * N_BOX_X);
+} // namespace detail
+
+//! Needed for generating Bolzmann velocity distribution (kJ/(mol K))
+constexpr const real BOLTZ = (detail::RGAS / detail::KILO); /*  */
+
+//! Charge multiplication factor for Coulomb interactions
+constexpr const real ONE_4PI_EPS0 = (1.0 / (4.0 * M_PI * detail::EPSILON0));
+
+//! Conversion factor from degrees to radians
+constexpr const real DEG2RAD = M_PI / 180.0;
+
+//! The number of shift vectors needed for pbc
+constexpr const int numShiftVectors = detail::N_IVEC;
+
+// from math/vectypes.h
+constexpr const int dimX = 0; /* Defines for indexing in vectors */
+constexpr const int dimY = 1;
+constexpr const int dimZ = 2;
+
+constexpr const int dimSize = 3;
+typedef real        rvec[dimSize];
+typedef real        matrix[dimSize][dimSize];
+} // namespace nblib
+
+
+#endif // NBLIB_BASICDEFINITIONS_H
diff --git a/api/nblib/box.cpp b/api/nblib/box.cpp
new file mode 100644 (file)
index 0000000..8d3c377
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements nblib simulation box
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include <cmath>
+
+#include "nblib/box.h"
+#include "nblib/exception.h"
+
+namespace nblib
+{
+
+Box::Box(real l) : Box(l, l, l) {}
+
+Box::Box(real x, real y, real z) : legacyMatrix_{ 0 }
+{
+    if (std::isnan(x) || std::isinf(x) || std::isnan(y) || std::isinf(y) || std::isnan(z) || std::isinf(z))
+    {
+        throw InputException("Cannot have NaN or Inf box length.");
+    }
+
+    legacyMatrix_[dimX][dimX] = x;
+    legacyMatrix_[dimY][dimY] = y;
+    legacyMatrix_[dimZ][dimZ] = z;
+}
+
+} // namespace nblib
diff --git a/api/nblib/box.h b/api/nblib/box.h
new file mode 100644 (file)
index 0000000..c1aaead
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib simulation box
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#ifndef NBLIB_BOX_H
+#define NBLIB_BOX_H
+
+#include "nblib/basicdefinitions.h"
+
+namespace nblib
+{
+
+/*! \brief Box specifies the size of periodic simulation systems
+ *
+ * \inpublicapi
+ * \ingroup nblib
+ *
+ * Currently only cubic and rectangular boxes are supported.
+ *
+ */
+class Box final
+{
+public:
+    using LegacyMatrix = matrix;
+
+    //! Construct a cubic box.
+    explicit Box(real l);
+
+    //! Construct a rectangular box.
+    Box(real x, real y, real z);
+
+    //! Return the full matrix that specifies the box. Used for gromacs setup code.
+    [[nodiscard]] LegacyMatrix const& legacyMatrix() const { return legacyMatrix_; }
+
+private:
+    LegacyMatrix legacyMatrix_;
+};
+
+} // namespace nblib
+#endif // NBLIB_BOX_H
diff --git a/api/nblib/exception.h b/api/nblib/exception.h
new file mode 100644 (file)
index 0000000..c83a53b
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib exception class
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#ifndef NBLIB_EXCEPTION_H
+#define NBLIB_EXCEPTION_H
+
+#include <exception>
+#include <string>
+
+namespace nblib
+{
+
+/*! \brief Base nblib exception class
+ *
+ * All nblib exceptions derive from this class and simply forward their message. This allows
+ * exceptions to be handled uniformly across different exception types.
+ */
+class NbLibException : public std::exception
+{
+public:
+    [[maybe_unused]] explicit NbLibException(const std::string& message) :
+        message_("NbLib Exception: " + message)
+    {
+    }
+
+    //! Overrides the what() in std::exception
+    [[nodiscard]] const char* what() const noexcept override { return message_.c_str(); }
+
+    //! Convenience call in case a string is wanted instead of a const char*
+    [[nodiscard]] const std::string& reason() const& { return message_; }
+
+private:
+    std::string message_;
+};
+
+/*! \brief The exception type for user input errors
+ *
+ * The message should give users some hint as to how to remedy the error.
+ */
+class InputException final : NbLibException
+{
+public:
+    using NbLibException::NbLibException;
+};
+
+} // namespace nblib
+#endif // NBLIB_EXCEPTION_H
diff --git a/api/nblib/forcecalculator.cpp b/api/nblib/forcecalculator.cpp
new file mode 100644 (file)
index 0000000..61dc95a
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Implements nblib ForceCalculator
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include "nblib/forcecalculator.h"
+#include "nblib/gmxcalculator.h"
+#include "nblib/gmxsetup.h"
+
+namespace nblib
+{
+
+ForceCalculator::~ForceCalculator() = default;
+
+ForceCalculator::ForceCalculator(const SimulationState& system, const NBKernelOptions& options)
+{
+    gmxForceCalculator_ = nblib::GmxSetupDirector::setupGmxForceCalculator(system, options);
+}
+
+void ForceCalculator::compute(gmx::ArrayRef<const Vec3> coordinates, gmx::ArrayRef<Vec3> forces)
+{
+    return gmxForceCalculator_->compute(coordinates, forces);
+}
+
+void ForceCalculator::updatePairList(gmx::ArrayRef<const int> particleInfoAllVdW,
+                                     gmx::ArrayRef<Vec3>      coordinates,
+                                     const Box&               box)
+{
+    gmxForceCalculator_->setParticlesOnGrid(particleInfoAllVdW, coordinates, box);
+}
+
+} // namespace nblib
diff --git a/api/nblib/forcecalculator.h b/api/nblib/forcecalculator.h
new file mode 100644 (file)
index 0000000..7083da5
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib ForceCalculator
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#ifndef NBLIB_FORCECALCULATOR_H
+#define NBLIB_FORCECALCULATOR_H
+
+#include "nblib/interactions.h"
+#include "nblib/kerneloptions.h"
+#include "nblib/simulationstate.h"
+
+namespace gmx
+{
+template<typename T>
+class ArrayRef;
+} // namespace gmx
+
+namespace nblib
+{
+class NbvSetupUtil;
+class GmxForceCalculator;
+
+/*! \brief Setups up and computes forces using gromacs backend.
+ *
+ * The ForceCalculator uses the data in the SimulationState and NBKernelOptions to opaquely
+ * construct all gromacs data structures needed to perform nonbonded force calculations. It is
+ * costly to create this object since much of the SimulationState and NBKernelOptions has to be
+ * passed to the gromacs backend. However, once constructed, compute can be called repeatedly only
+ * paying the cost of the actual nonbonded force calculation. Repeated calls to compute on the same
+ * coordinated will always return the same forces (within precision), so the user must update the
+ * positions using the forces generated here to advance a simulation. If the coordinates move
+ * sufficiently far from their positions at construction time, the efficiency of the calculation
+ * will suffer. To alleviate this, the user can call updatePairList.
+ *
+ */
+class ForceCalculator final
+{
+public:
+    ForceCalculator(const SimulationState& system, const NBKernelOptions& options);
+
+    ~ForceCalculator();
+
+    /*! \brief Dispatch the nonbonded force kernels and reduce the forces
+     *
+     * This function zeroes out all values in the passed in forces buffer, so it can be regarded as
+     * an output only param.
+     *
+     * \param[in] coordinates to be used for the force calculation
+     * \param[out] forces buffer to store the output forces
+     */
+    void compute(gmx::ArrayRef<const Vec3> coordinates, gmx::ArrayRef<Vec3> forces);
+
+    /*! \brief Puts particles on a grid based on bounds specified by the box
+     *
+     * As compute is called repeatedly, the particles drift apart and the force computation becomes
+     * progressively less efficient. Calling this function recomputes the particle-particle pair
+     * lists so that computation can proceed efficiently. Should be called around every 100 steps.
+     *
+     * \param particleInfoAllVdW The types of the particles to be placed on grids
+     * \param coordinates The coordinates to be placed on grids
+     * \param[in] box The system simulation box
+     */
+    void updatePairList(gmx::ArrayRef<const int> particleInfoAllVdW,
+                        gmx::ArrayRef<Vec3>      coordinates,
+                        const Box&               box);
+
+private:
+    //! GROMACS force calculator to compute forces
+    std::unique_ptr<GmxForceCalculator> gmxForceCalculator_;
+};
+
+} // namespace nblib
+
+#endif // NBLIB_FORCECALCULATOR_H
diff --git a/api/nblib/gmxcalculator.cpp b/api/nblib/gmxcalculator.cpp
new file mode 100644 (file)
index 0000000..30e2594
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Implements a force calculator based on GROMACS data structures.
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include "nblib/gmxcalculator.h"
+#include "gromacs/ewald/ewald_utils.h"
+#include "gromacs/gmxlib/nrnb.h"
+#include "gromacs/mdlib/rf_util.h"
+#include "gromacs/mdtypes/enerdata.h"
+#include "gromacs/mdtypes/forcerec.h"
+#include "gromacs/mdtypes/interaction_const.h"
+#include "gromacs/mdtypes/simulation_workload.h"
+#include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/utility/range.h"
+#include "nblib/exception.h"
+#include "nblib/simulationstate.h"
+
+namespace nblib
+{
+
+GmxForceCalculator::GmxForceCalculator()
+{
+    enerd_            = std::make_unique<gmx_enerdata_t>(1, 0);
+    forcerec_         = std::make_unique<t_forcerec>();
+    interactionConst_ = std::make_unique<interaction_const_t>();
+    stepWork_         = std::make_unique<gmx::StepWorkload>();
+    nrnb_             = std::make_unique<t_nrnb>();
+}
+
+GmxForceCalculator::~GmxForceCalculator() = default;
+
+void GmxForceCalculator::compute(gmx::ArrayRef<const gmx::RVec> coordinateInput,
+                                 gmx::ArrayRef<gmx::RVec>       forceOutput)
+{
+    // update the coordinates in the backend
+    nbv_->convertCoordinates(gmx::AtomLocality::Local, false, coordinateInput);
+
+    // set forces to zero
+    std::fill(forceOutput.begin(), forceOutput.end(), gmx::RVec{ 0, 0, 0 });
+
+    nbv_->dispatchNonbondedKernel(gmx::InteractionLocality::Local, *interactionConst_, *stepWork_,
+                                  enbvClearFYes, *forcerec_, enerd_.get(), nrnb_.get());
+
+    nbv_->atomdata_add_nbat_f_to_f(gmx::AtomLocality::All, forceOutput);
+}
+
+void GmxForceCalculator::setParticlesOnGrid(gmx::ArrayRef<const int>       particleInfoAllVdw,
+                                            gmx::ArrayRef<const gmx::RVec> coordinates,
+                                            const Box&                     box)
+{
+    auto legacyBox = box.legacyMatrix();
+
+    if (TRICLINIC(legacyBox))
+    {
+        throw InputException("Only rectangular unit-cells are supported here");
+    }
+    const rvec lowerCorner = { 0, 0, 0 };
+    const rvec upperCorner = { legacyBox[dimX][dimX], legacyBox[dimY][dimY], legacyBox[dimZ][dimZ] };
+
+    const real particleDensity = coordinates.size() / det(legacyBox);
+
+    nbnxn_put_on_grid(nbv_.get(), legacyBox, 0, lowerCorner, upperCorner, nullptr,
+                      { 0, int(coordinates.size()) }, particleDensity, particleInfoAllVdw,
+                      coordinates, 0, nullptr);
+}
+
+} // namespace nblib
diff --git a/api/nblib/gmxcalculator.h b/api/nblib/gmxcalculator.h
new file mode 100644 (file)
index 0000000..610983c
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Implements a force calculator based on GROMACS data structures.
+ *
+ * Intended for internal use inside the ForceCalculator.
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+
+#ifndef NBLIB_GMXCALCULATOR_H
+#define NBLIB_GMXCALCULATOR_H
+
+#include <memory>
+
+#include "nblib/vector.h"
+
+struct nonbonded_verlet_t;
+struct t_forcerec;
+struct t_nrnb;
+struct interaction_const_t;
+struct gmx_enerdata_t;
+
+namespace gmx
+{
+template<typename T>
+class ArrayRef;
+class StepWorkload;
+} // namespace gmx
+
+namespace nblib
+{
+class Box;
+class NbvSetupUtil;
+class SimulationState;
+struct NBKernelOptions;
+
+class GmxForceCalculator final
+{
+public:
+    GmxForceCalculator();
+
+    ~GmxForceCalculator();
+
+    //! Compute forces and return
+    void compute(gmx::ArrayRef<const gmx::RVec> coordinateInput, gmx::ArrayRef<gmx::RVec> forceOutput);
+
+    //! Puts particles on a grid based on bounds specified by the box (for every NS step)
+    void setParticlesOnGrid(gmx::ArrayRef<const int>       particleInfoAllVdw,
+                            gmx::ArrayRef<const gmx::RVec> coordinates,
+                            const Box&                     box);
+
+private:
+    friend class NbvSetupUtil;
+
+    //! Non-Bonded Verlet object for force calculation
+    std::unique_ptr<nonbonded_verlet_t> nbv_;
+
+    //! Only nbfp and shift_vec are used
+    std::unique_ptr<t_forcerec> forcerec_;
+
+    //! Parameters for various interactions in the system
+    std::unique_ptr<interaction_const_t> interactionConst_;
+
+    //! Tasks to perform in an MD Step
+    std::unique_ptr<gmx::StepWorkload> stepWork_;
+
+    //! Energies of different interaction types; currently only needed as an argument for dispatchNonbondedKernel
+    std::unique_ptr<gmx_enerdata_t> enerd_;
+
+    //! Non-bonded flop counter; currently only needed as an argument for dispatchNonbondedKernel
+    std::unique_ptr<t_nrnb> nrnb_;
+
+    //! Legacy matrix for box
+    matrix box_{ { 0 } };
+};
+
+} // namespace nblib
+
+#endif // NBLIB_GMXCALCULATOR_H
diff --git a/api/nblib/gmxsetup.cpp b/api/nblib/gmxsetup.cpp
new file mode 100644 (file)
index 0000000..66afb08
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Translation layer to GROMACS data structures for force calculations.
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include "nblib/gmxsetup.h"
+#include "gromacs/ewald/ewald_utils.h"
+#include "gromacs/gmxlib/nrnb.h"
+#include "gromacs/mdlib/forcerec.h"
+#include "gromacs/mdlib/gmx_omp_nthreads.h"
+#include "gromacs/mdlib/rf_util.h"
+#include "gromacs/mdtypes/forcerec.h"
+#include "gromacs/mdtypes/interaction_const.h"
+#include "gromacs/mdtypes/simulation_workload.h"
+#include "gromacs/nbnxm/atomdata.h"
+#include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/nbnxm/nbnxm_simd.h"
+#include "gromacs/nbnxm/pairlistset.h"
+#include "gromacs/nbnxm/pairlistsets.h"
+#include "gromacs/nbnxm/pairsearch.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/logger.h"
+#include "gromacs/utility/smalloc.h"
+#include "nblib/exception.h"
+#include "nblib/kerneloptions.h"
+#include "nblib/particletype.h"
+#include "nblib/simulationstate.h"
+
+namespace nblib
+{
+
+//! Helper to translate between the different enumeration values.
+static Nbnxm::KernelType translateBenchmarkEnum(const SimdKernels& kernel)
+{
+    int kernelInt = static_cast<int>(kernel);
+    return static_cast<Nbnxm::KernelType>(kernelInt);
+}
+
+/*! \brief Checks the kernel setup
+ *
+ * Returns an error string when the kernel is not available.
+ */
+static void checkKernelSetup(const NBKernelOptions& options)
+{
+    if (options.nbnxmSimd >= SimdKernels::Count && options.nbnxmSimd == SimdKernels::SimdAuto)
+    {
+        throw InputException("Need a valid kernel SIMD type");
+    }
+    // Check SIMD support
+    if ((options.nbnxmSimd != SimdKernels::SimdNo && !GMX_SIMD)
+#ifndef GMX_NBNXN_SIMD_4XN
+        || options.nbnxmSimd == SimdKernels::Simd4XM
+#endif
+#ifndef GMX_NBNXN_SIMD_2XNN
+        || options.nbnxmSimd == SimdKernels::Simd2XMM
+#endif
+    )
+    {
+        throw InputException("The requested SIMD kernel was not set up at configuration time");
+    }
+}
+
+NbvSetupUtil::NbvSetupUtil() : gmxForceCalculator_(std::make_unique<GmxForceCalculator>()) {}
+
+void NbvSetupUtil::setExecutionContext(const NBKernelOptions& options)
+{
+    // Todo: find a more general way to initialize hardware
+    gmx_omp_nthreads_set(emntPairsearch, options.numOpenMPThreads);
+    gmx_omp_nthreads_set(emntNonbonded, options.numOpenMPThreads);
+}
+
+Nbnxm::KernelSetup NbvSetupUtil::getKernelSetup(const NBKernelOptions& options)
+{
+    checkKernelSetup(options);
+
+    Nbnxm::KernelSetup kernelSetup;
+
+    // The int enum options.nbnxnSimd is set up to match Nbnxm::KernelType + 1
+    kernelSetup.kernelType = translateBenchmarkEnum(options.nbnxmSimd);
+    // The plain-C kernel does not support analytical ewald correction
+    if (kernelSetup.kernelType == Nbnxm::KernelType::Cpu4x4_PlainC)
+    {
+        kernelSetup.ewaldExclusionType = Nbnxm::EwaldExclusionType::Table;
+    }
+    else
+    {
+        kernelSetup.ewaldExclusionType = options.useTabulatedEwaldCorr
+                                                 ? Nbnxm::EwaldExclusionType::Table
+                                                 : Nbnxm::EwaldExclusionType::Analytical;
+    }
+
+    return kernelSetup;
+}
+
+void NbvSetupUtil::setParticleInfoAllVdv(const size_t numParticles)
+
+{
+    particleInfoAllVdw_.resize(numParticles);
+    for (size_t particleI = 0; particleI < numParticles; particleI++)
+    {
+        SET_CGINFO_HAS_VDW(particleInfoAllVdw_[particleI]);
+        SET_CGINFO_HAS_Q(particleInfoAllVdw_[particleI]);
+    }
+}
+
+void NbvSetupUtil::setNonBondedParameters(const std::vector<ParticleType>& particleTypes,
+                                          const NonBondedInteractionMap&   nonBondedInteractionMap)
+{
+    /* Todo: Refactor nbnxm to take nonbondedParameters_ directly
+     *
+     * initial self-handling of combination rules
+     * size: 2*(numParticleTypes^2)
+     */
+    nonbondedParameters_.reserve(2 * particleTypes.size() * particleTypes.size());
+
+    constexpr real c6factor  = 6.0;
+    constexpr real c12factor = 12.0;
+
+    for (const ParticleType& particleType1 : particleTypes)
+    {
+        for (const ParticleType& particleType2 : particleTypes)
+        {
+            nonbondedParameters_.push_back(
+                    nonBondedInteractionMap.getC6(particleType1.name(), particleType2.name()) * c6factor);
+            nonbondedParameters_.push_back(
+                    nonBondedInteractionMap.getC12(particleType1.name(), particleType2.name()) * c12factor);
+        }
+    }
+}
+
+void NbvSetupUtil::setAtomProperties(const std::vector<int>&  particleTypeIdOfAllParticles,
+                                     const std::vector<real>& charges)
+{
+    gmxForceCalculator_->nbv_->setAtomProperties(particleTypeIdOfAllParticles, charges, particleInfoAllVdw_);
+}
+
+//! Sets up and returns a Nbnxm object for the given options and system
+void NbvSetupUtil::setupNbnxmInstance(const size_t numParticleTypes, const NBKernelOptions& options)
+{
+    const auto pinPolicy  = (options.useGpu ? gmx::PinningPolicy::PinnedIfSupported
+                                           : gmx::PinningPolicy::CannotBePinned);
+    const int  numThreads = options.numOpenMPThreads;
+    // Note: the options and Nbnxm combination rule enums values should match
+    const int combinationRule = static_cast<int>(options.ljCombinationRule);
+
+    checkKernelSetup(options); // throws exception is setup is invalid
+
+    Nbnxm::KernelSetup kernelSetup = getKernelSetup(options);
+
+    PairlistParams pairlistParams(kernelSetup.kernelType, false, options.pairlistCutoff, false);
+    Nbnxm::GridSet gridSet(PbcType::Xyz, false, nullptr, nullptr, pairlistParams.pairlistType,
+                           false, numThreads, pinPolicy);
+    auto           pairlistSets = std::make_unique<PairlistSets>(pairlistParams, false, 0);
+    auto           pairSearch =
+            std::make_unique<PairSearch>(PbcType::Xyz, false, nullptr, nullptr,
+                                         pairlistParams.pairlistType, false, numThreads, pinPolicy);
+
+    auto atomData = std::make_unique<nbnxn_atomdata_t>(pinPolicy);
+
+    // Put everything together
+    auto nbv = std::make_unique<nonbonded_verlet_t>(std::move(pairlistSets), std::move(pairSearch),
+                                                    std::move(atomData), kernelSetup, nullptr, nullptr);
+
+    // Needs to be called with the number of unique ParticleTypes
+    nbnxn_atomdata_init(gmx::MDLogger(), nbv->nbat.get(), kernelSetup.kernelType, combinationRule,
+                        numParticleTypes, nonbondedParameters_, 1, numThreads);
+
+    gmxForceCalculator_->nbv_ = std::move(nbv);
+}
+
+//! Computes the Ewald splitting coefficient for Coulomb
+static real ewaldCoeff(const real ewald_rtol, const real pairlistCutoff)
+{
+    return calc_ewaldcoeff_q(pairlistCutoff, ewald_rtol);
+}
+
+void NbvSetupUtil::setupStepWorkload(const NBKernelOptions& options)
+{
+    gmxForceCalculator_->stepWork_->computeForces          = true;
+    gmxForceCalculator_->stepWork_->computeNonbondedForces = true;
+
+    if (options.computeVirialAndEnergy)
+    {
+        gmxForceCalculator_->stepWork_->computeVirial = true;
+        gmxForceCalculator_->stepWork_->computeEnergy = true;
+    }
+}
+
+void NbvSetupUtil::setupInteractionConst(const NBKernelOptions& options)
+{
+    gmxForceCalculator_->interactionConst_->vdwtype      = evdwCUT;
+    gmxForceCalculator_->interactionConst_->vdw_modifier = eintmodPOTSHIFT;
+    gmxForceCalculator_->interactionConst_->rvdw         = options.pairlistCutoff;
+
+    switch (options.coulombType)
+    {
+        case CoulombType::Pme: gmxForceCalculator_->interactionConst_->eeltype = eelPME; break;
+        case CoulombType::Cutoff: gmxForceCalculator_->interactionConst_->eeltype = eelCUT; break;
+        case CoulombType::ReactionField:
+            gmxForceCalculator_->interactionConst_->eeltype = eelRF;
+            break;
+        case CoulombType::Count: throw InputException("Unsupported electrostatic interaction");
+    }
+    gmxForceCalculator_->interactionConst_->coulomb_modifier = eintmodPOTSHIFT;
+    gmxForceCalculator_->interactionConst_->rcoulomb         = options.pairlistCutoff;
+    // Note: values correspond to ic->coulomb_modifier = eintmodPOTSHIFT
+    gmxForceCalculator_->interactionConst_->dispersion_shift.cpot =
+            -1.0 / gmx::power6(gmxForceCalculator_->interactionConst_->rvdw);
+    gmxForceCalculator_->interactionConst_->repulsion_shift.cpot =
+            -1.0 / gmx::power12(gmxForceCalculator_->interactionConst_->rvdw);
+
+    // These are the initialized values but we leave them here so that later
+    // these can become options.
+    gmxForceCalculator_->interactionConst_->epsilon_r  = 1.0;
+    gmxForceCalculator_->interactionConst_->epsilon_rf = 1.0;
+
+    /* Set the Coulomb energy conversion factor */
+    if (gmxForceCalculator_->interactionConst_->epsilon_r != 0)
+    {
+        gmxForceCalculator_->interactionConst_->epsfac =
+                ONE_4PI_EPS0 / gmxForceCalculator_->interactionConst_->epsilon_r;
+    }
+    else
+    {
+        /* eps = 0 is infinite dieletric: no Coulomb interactions */
+        gmxForceCalculator_->interactionConst_->epsfac = 0;
+    }
+
+    calc_rffac(nullptr, gmxForceCalculator_->interactionConst_->epsilon_r,
+               gmxForceCalculator_->interactionConst_->epsilon_rf,
+               gmxForceCalculator_->interactionConst_->rcoulomb,
+               &gmxForceCalculator_->interactionConst_->k_rf,
+               &gmxForceCalculator_->interactionConst_->c_rf);
+
+    if (EEL_PME_EWALD(gmxForceCalculator_->interactionConst_->eeltype))
+    {
+        // Ewald coefficients, we ignore the potential shift
+        gmxForceCalculator_->interactionConst_->ewaldcoeff_q = ewaldCoeff(1e-5, options.pairlistCutoff);
+        if (gmxForceCalculator_->interactionConst_->ewaldcoeff_q <= 0)
+        {
+            throw InputException("Ewald coefficient should be > 0");
+        }
+        gmxForceCalculator_->interactionConst_->coulombEwaldTables =
+                std::make_unique<EwaldCorrectionTables>();
+        init_interaction_const_tables(nullptr, gmxForceCalculator_->interactionConst_.get(), 0);
+    }
+}
+
+void NbvSetupUtil::setupForceRec(const matrix& box)
+{
+    assert((gmxForceCalculator_->forcerec_ && "Forcerec not initialized"));
+    gmxForceCalculator_->forcerec_->nbfp = nonbondedParameters_;
+    snew(gmxForceCalculator_->forcerec_->shift_vec, numShiftVectors);
+    calc_shifts(box, gmxForceCalculator_->forcerec_->shift_vec);
+}
+
+void NbvSetupUtil::setParticlesOnGrid(const std::vector<Vec3>& coordinates, const Box& box)
+{
+    gmxForceCalculator_->setParticlesOnGrid(particleInfoAllVdw_, coordinates, box);
+}
+
+void NbvSetupUtil::constructPairList(const gmx::ListOfLists<int>& exclusions)
+{
+    gmxForceCalculator_->nbv_->constructPairlist(gmx::InteractionLocality::Local, exclusions, 0,
+                                                 gmxForceCalculator_->nrnb_.get());
+}
+
+
+std::unique_ptr<GmxForceCalculator> GmxSetupDirector::setupGmxForceCalculator(const SimulationState& system,
+                                                                              const NBKernelOptions& options)
+{
+    NbvSetupUtil nbvSetupUtil;
+    nbvSetupUtil.setExecutionContext(options);
+    nbvSetupUtil.setNonBondedParameters(system.topology().getParticleTypes(),
+                                        system.topology().getNonBondedInteractionMap());
+    nbvSetupUtil.setParticleInfoAllVdv(system.topology().numParticles());
+
+    nbvSetupUtil.setupInteractionConst(options);
+    nbvSetupUtil.setupStepWorkload(options);
+    nbvSetupUtil.setupNbnxmInstance(system.topology().getParticleTypes().size(), options);
+    nbvSetupUtil.setParticlesOnGrid(system.coordinates(), system.box());
+    nbvSetupUtil.constructPairList(system.topology().getGmxExclusions());
+    nbvSetupUtil.setAtomProperties(system.topology().getParticleTypeIdOfAllParticles(),
+                                   system.topology().getCharges());
+    nbvSetupUtil.setupForceRec(system.box().legacyMatrix());
+
+    return nbvSetupUtil.getGmxForceCalculator();
+}
+
+} // namespace nblib
diff --git a/api/nblib/gmxsetup.h b/api/nblib/gmxsetup.h
new file mode 100644 (file)
index 0000000..628172c
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Translation layer to GROMACS data structures for force calculations.
+ *
+ * Implements the translation layer between the user scope and
+ * GROMACS data structures for force calculations. Sets up the
+ * non-bonded verlet.
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+
+#ifndef NBLIB_GMXSETUP_H
+#define NBLIB_GMXSETUP_H
+
+#include "nblib/gmxcalculator.h"
+#include "nblib/simulationstate.h"
+
+namespace Nbnxm
+{
+struct KernelSetup;
+}
+
+namespace nblib
+{
+
+class NbvSetupUtil final
+{
+public:
+    NbvSetupUtil();
+
+    //! Sets hardware params from the execution context
+    void setExecutionContext(const NBKernelOptions& options);
+
+    //! Sets non-bonded parameters to be used to build GMX data structures
+    void setNonBondedParameters(const std::vector<ParticleType>& particleTypes,
+                                const NonBondedInteractionMap&   nonBondedInteractionMap);
+
+    //! Marks particles to have Van der Waals interactions
+    void setParticleInfoAllVdv(size_t numParticles);
+
+    //! Returns the kernel setup
+    Nbnxm::KernelSetup getKernelSetup(const NBKernelOptions& options);
+
+    //! Set up StepWorkload data
+    void setupStepWorkload(const NBKernelOptions& options);
+
+    //! Return an interaction constants struct with members set appropriately
+    void setupInteractionConst(const NBKernelOptions& options);
+
+    //! Sets Particle Types and Charges and VdW params
+    void setAtomProperties(const std::vector<int>&  particleTypeIdOfAllParticles,
+                           const std::vector<real>& charges);
+
+    //! Sets up non-bonded verlet on the GmxForceCalculator
+    void setupNbnxmInstance(size_t numParticleTypes, const NBKernelOptions& options);
+
+    //! Puts particles on a grid based on bounds specified by the box
+    void setParticlesOnGrid(const std::vector<Vec3>& coordinates, const Box& box);
+
+    //! Constructs pair lists
+    void constructPairList(const gmx::ListOfLists<int>& exclusions);
+
+    //! Sets up t_forcerec object on the GmxForceCalculator
+    void setupForceRec(const matrix& box);
+
+    std::unique_ptr<GmxForceCalculator> getGmxForceCalculator()
+    {
+        return std::move(gmxForceCalculator_);
+    }
+
+private:
+    //! Storage for parameters for short range interactions.
+    std::vector<real> nonbondedParameters_;
+
+    //! Particle info where all particles are marked to have Van der Waals interactions
+    std::vector<int> particleInfoAllVdw_;
+
+    //! GROMACS force calculator to compute forces
+    std::unique_ptr<GmxForceCalculator> gmxForceCalculator_;
+};
+
+class GmxSetupDirector
+{
+public:
+    //! Sets up and returns a GmxForceCalculator
+    static std::unique_ptr<GmxForceCalculator> setupGmxForceCalculator(const SimulationState& system,
+                                                                       const NBKernelOptions& options);
+};
+
+} // namespace nblib
+#endif // NBLIB_GMXSETUP_H
diff --git a/api/nblib/integrator.cpp b/api/nblib/integrator.cpp
new file mode 100644 (file)
index 0000000..970eea0
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements nblib integrator
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include "nblib/integrator.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/arrayref.h"
+#include "nblib/topology.h"
+
+namespace nblib
+{
+// NOLINTNEXTLINE(performance-unnecessary-value-param)
+LeapFrog::LeapFrog(const Topology& topology, const Box& box) : box_(box)
+{
+    inverseMasses_.resize(topology.numParticles());
+    for (int i = 0; i < topology.numParticles(); i++)
+    {
+        int typeIndex     = topology.getParticleTypeIdOfAllParticles()[i];
+        inverseMasses_[i] = 1.0 / topology.getParticleTypes()[typeIndex].mass();
+    }
+}
+
+void LeapFrog::integrate(const real dt, gmx::ArrayRef<Vec3> x, gmx::ArrayRef<Vec3> v, gmx::ArrayRef<const Vec3> f)
+{
+    for (size_t i = 0; i < x.size(); i++)
+    {
+        for (int dim = 0; dim < dimSize; dim++)
+        {
+            v[i][dim] += f[i][dim] * dt * inverseMasses_[i];
+            x[i][dim] += v[i][dim] * dt;
+        }
+    }
+    put_atoms_in_box(PbcType::Xyz, box_.legacyMatrix(), x);
+}
+
+} // namespace nblib
diff --git a/api/nblib/integrator.h b/api/nblib/integrator.h
new file mode 100644 (file)
index 0000000..f72d6b4
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib integrator
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef NBLIB_INTEGRATOR_H
+#define NBLIB_INTEGRATOR_H
+
+#include <vector>
+
+#include "nblib/box.h"
+#include "nblib/vector.h"
+
+namespace gmx
+{
+template<typename T>
+class ArrayRef;
+} // namespace gmx
+
+namespace nblib
+{
+
+class Topology;
+
+/*! \brief Simple integrator
+ *
+ */
+class LeapFrog final
+{
+public:
+    /*! \brief Constructor.
+     *
+     * \param[in] topology  Topology object to build list of inverse masses.
+     * \param[in] box       Box object for ensuring that coordinates remain within bounds
+     */
+    LeapFrog(const Topology& topology, const Box& box);
+
+    /*! \brief Integrate
+     *
+     * Integrates the equation of motion using Leap-Frog algorithm.
+     * Updates coordinates and velocities.
+     *
+     * \param[in]  dt          Timestep.
+     * \param[out] coordinates Coordinate array that would be modified in-place.
+     * \param[out] velocities  Velocity array that would be modified in-place.
+     * \param[in]  forces      Force array to be read.
+     *
+     */
+    void integrate(real                      dt,
+                   gmx::ArrayRef<Vec3>       coordinates,
+                   gmx::ArrayRef<Vec3>       velocities,
+                   gmx::ArrayRef<const Vec3> forces);
+
+private:
+    //! 1/mass for all atoms
+    std::vector<real> inverseMasses_;
+    //! Box for PBC conformity
+    Box box_;
+};
+
+} // namespace nblib
+
+#endif // NBLIB_INTEGRATOR_H
diff --git a/api/nblib/interactions.cpp b/api/nblib/interactions.cpp
new file mode 100644 (file)
index 0000000..b63ec9e
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements nblib particle-types interactions
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include <cmath>
+#include <set>
+
+#include "nblib/exception.h"
+#include "nblib/interactions.h"
+#include "nblib/util/internal.h"
+
+namespace nblib
+{
+
+C6 NonBondedInteractionMap::getC6(const ParticleTypeName& first, const ParticleTypeName& second) const
+{
+    return std::get<0>(interactionMap_.at(std::make_tuple(first, second)));
+}
+
+C12 NonBondedInteractionMap::getC12(const ParticleTypeName& first, const ParticleTypeName& second) const
+{
+    return std::get<1>(interactionMap_.at(std::make_tuple(first, second)));
+}
+
+void NonBondedInteractionMap::setInteractions(const ParticleTypeName& first,
+                                              const ParticleTypeName& second,
+                                              const C6                c6_combo,
+                                              const C12               c12_combo)
+{
+    auto interactionKey             = std::make_tuple(first, second);
+    interactionMap_[interactionKey] = std::make_tuple(c6_combo, c12_combo);
+}
+
+size_t NonBondedInteractionMap::count(const NonBondedInteractionMap::NamePairTuple& namePairTuple)
+{
+    return interactionMap_.count(namePairTuple);
+}
+
+namespace detail
+{
+
+//! Combines the non-bonded parameters from two particles for pairwise interactions
+real combineNonbondedParameters(real v, real w, CombinationRule combinationRule)
+{
+    if (combinationRule == CombinationRule::Geometric)
+    {
+        return std::sqrt(v * w);
+    }
+    else
+    {
+        throw InputException("unknown LJ Combination rule specified\n");
+    }
+}
+
+} // namespace detail
+
+ParticleTypesInteractions::ParticleTypesInteractions(CombinationRule cr) : combinationRule_(cr) {}
+
+ParticleTypesInteractions& ParticleTypesInteractions::add(const ParticleTypeName& particleTypeName,
+                                                          C6                      c6,
+                                                          C12                     c12)
+{
+    auto insertLocation = singleParticleInteractionsMap_.insert(
+            std::make_pair(particleTypeName, std::make_tuple(c6, c12)));
+
+    if (!insertLocation.second) // if particleTypeName already existed
+    {
+        if (std::get<0>(insertLocation.first->second) != c6
+            || std::get<1>(insertLocation.first->second) != c12)
+        {
+            std::string message = formatString(
+                    "Attempting to add nonbonded interaction parameters for particle "
+                    "type {} twice",
+                    particleTypeName.value());
+            throw InputException(message);
+        }
+    }
+    return *this;
+}
+
+ParticleTypesInteractions& ParticleTypesInteractions::add(const ParticleTypeName& particleTypeName1,
+                                                          const ParticleTypeName& particleTypeName2,
+                                                          C6                      c6,
+                                                          C12                     c12)
+{
+    auto interactionKey         = std::make_tuple(particleTypeName1, particleTypeName2);
+    auto possibleInteractionKey = std::make_tuple(particleTypeName2, particleTypeName1);
+
+    auto insertLocation = twoParticlesInteractionsMap_.insert(
+            std::make_pair(interactionKey, std::make_tuple(c6, c12)));
+    twoParticlesInteractionsMap_.insert(std::make_pair(possibleInteractionKey, std::make_tuple(c6, c12)));
+
+    if (!insertLocation.second) // if particleTypeName already existed
+    {
+        if (std::get<0>(insertLocation.first->second) != c6
+            || std::get<1>(insertLocation.first->second) != c12)
+        {
+            std::string message = formatString(
+                    "Attempting to add nonbonded interaction parameters between the particle types "
+                    "{} {} twice",
+                    particleTypeName1.value(), particleTypeName2.value());
+            throw InputException(message);
+        }
+    }
+    return *this;
+}
+
+NonBondedInteractionMap ParticleTypesInteractions::generateTable() const
+{
+    NonBondedInteractionMap nonbondedParameters_;
+
+    // creating the combination rule based interaction matrix
+    for (const auto& particleType1 : singleParticleInteractionsMap_)
+    {
+        C6  c6_1  = std::get<0>(particleType1.second);
+        C12 c12_1 = std::get<1>(particleType1.second);
+
+        for (const auto& particleType2 : singleParticleInteractionsMap_)
+        {
+            C6  c6_2  = std::get<0>(particleType2.second);
+            C12 c12_2 = std::get<1>(particleType2.second);
+
+            C6  c6_combo{ detail::combineNonbondedParameters(c6_1, c6_2, combinationRule_) };
+            C12 c12_combo{ detail::combineNonbondedParameters(c12_1, c12_2, combinationRule_) };
+
+            nonbondedParameters_.setInteractions(particleType1.first, particleType2.first, c6_combo,
+                                                 c12_combo);
+        }
+    }
+
+    // updating the interaction matrix based on the user fine tuned parameters
+    for (const auto& particleTypeTuple : twoParticlesInteractionsMap_)
+    {
+        const auto& first  = std::get<0>(particleTypeTuple.first);
+        const auto& second = std::get<1>(particleTypeTuple.first);
+
+        C6  c6_combo  = std::get<0>(particleTypeTuple.second);
+        C12 c12_combo = std::get<1>(particleTypeTuple.second);
+
+        nonbondedParameters_.setInteractions(first, second, c6_combo, c12_combo);
+    }
+
+    std::set<ParticleTypeName> particleTypes;
+    for (auto const& typeKey : nonbondedParameters_)
+    { // we don't need to get<1> because the list is guaranteed to be symmetric
+        particleTypes.insert(std::get<0>(typeKey.first));
+    }
+
+    // check whether there is any missing interaction
+    for (const ParticleTypeName& particleTypeName1 : particleTypes)
+    {
+        for (const ParticleTypeName& particleTypeName2 : particleTypes)
+        {
+            auto interactionKey = std::make_tuple(particleTypeName1, particleTypeName2);
+            if (nonbondedParameters_.count(interactionKey) == 0)
+            {
+                std::string message = formatString("Missing interaction between {} {}",
+                                                   particleTypeName1.value(), particleTypeName2.value());
+                throw InputException(message);
+            }
+        }
+    }
+    return nonbondedParameters_;
+}
+
+CombinationRule ParticleTypesInteractions::getCombinationRule() const
+{
+    return combinationRule_;
+}
+
+void ParticleTypesInteractions::merge(const ParticleTypesInteractions& other)
+{
+    for (const auto& keyval : other.singleParticleInteractionsMap_)
+    {
+        add(keyval.first, std::get<0>(keyval.second), std::get<1>(keyval.second));
+    }
+
+    for (const auto& keyval : other.twoParticlesInteractionsMap_)
+    {
+        add(std::get<0>(keyval.first), std::get<1>(keyval.first), std::get<0>(keyval.second),
+            std::get<1>(keyval.second));
+    }
+}
+
+} // namespace nblib
diff --git a/api/nblib/interactions.h b/api/nblib/interactions.h
new file mode 100644 (file)
index 0000000..7a3e245
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib particle-types interactions
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef NBLIB_INTERACTIONS_H
+#define NBLIB_INTERACTIONS_H
+
+#include <map>
+#include <tuple>
+#include <unordered_map>
+
+#include "nblib/kerneloptions.h"
+#include "nblib/particletype.h"
+#include "nblib/util/user.h"
+
+namespace nblib
+{
+
+//! Shorthand for a map used for looking up non-bonded parameters using particle types
+//! Named type for the C6 parameter in the Lennard-Jones potential
+using C6 = StrongType<real, struct C6Parameter>;
+//! Named type for the C12 parameter in the Lennard-Jones potential
+using C12 = StrongType<real, struct C12Parameter>;
+
+using NonBondedInteractionMapImpl =
+        std::map<std::tuple<ParticleTypeName, ParticleTypeName>, std::tuple<C6, C12>>;
+
+//! Map used for looking up non-bonded parameters using particle types
+class NonBondedInteractionMap final
+{
+private:
+    using NamePairTuple   = std::tuple<ParticleTypeName, ParticleTypeName>;
+    using ComboParameters = std::tuple<C6, C12>;
+    using InteractionMap  = std::map<NamePairTuple, ComboParameters>;
+    InteractionMap interactionMap_;
+
+public:
+    void   setInteractions(const ParticleTypeName&, const ParticleTypeName&, const C6, const C12);
+    size_t count(const NamePairTuple&);
+
+    [[nodiscard]] C6  getC6(const ParticleTypeName&, const ParticleTypeName&) const;
+    [[nodiscard]] C12 getC12(const ParticleTypeName&, const ParticleTypeName&) const;
+
+    InteractionMap::iterator begin() { return interactionMap_.begin(); }
+    InteractionMap::iterator end() { return interactionMap_.end(); }
+};
+
+/*! \brief Non-Bonded Interactions between Particle Types
+ *
+ * \inpublicapi
+ * \ingroup nblib
+ *
+ * A class to hold a mapping between pairs of particle types and the non-bonded
+ * interactions between them. One may add the non-bonded parameters, namely the
+ * C6/C12 params for each particle type individually and construct a pair-wise
+ * mapping using combination rules or manually specify the parameters between
+ * a specific pair.
+ *
+ */
+class ParticleTypesInteractions final
+{
+public:
+    //! Initialized with the default geometric combination rule
+    explicit ParticleTypesInteractions(CombinationRule = CombinationRule::Geometric);
+
+    //! Specify non-bonded params of a particle type
+    ParticleTypesInteractions& add(const ParticleTypeName& particleTypeName, C6 c6, C12 c12);
+
+    //! Specify the non-bonded params of a specific pair of particle types
+    ParticleTypesInteractions& add(const ParticleTypeName& particleTypeName1,
+                                   const ParticleTypeName& particleTypeName2,
+                                   C6                      c6,
+                                   C12                     c12);
+
+    //! Generate table based on the parameters stored
+    [[nodiscard]] NonBondedInteractionMap generateTable() const;
+
+    //! Get combination rule enabled in this object
+    [[nodiscard]] CombinationRule getCombinationRule() const;
+
+    //! Merge with the information stored in another ParticleTypesInteractions object
+    void merge(const ParticleTypesInteractions&);
+
+private:
+    CombinationRule combinationRule_;
+
+    std::map<ParticleTypeName, std::tuple<C6, C12>> singleParticleInteractionsMap_;
+    std::map<std::tuple<ParticleTypeName, ParticleTypeName>, std::tuple<C6, C12>> twoParticlesInteractionsMap_;
+};
+
+} // namespace nblib
+#endif // NBLIB_INTERACTIONS_H
diff --git a/api/nblib/kerneloptions.h b/api/nblib/kerneloptions.h
new file mode 100644 (file)
index 0000000..dfd72e7
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib kernel setup options
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#ifndef NBLIB_KERNELOPTIONS_H
+#define NBLIB_KERNELOPTIONS_H
+
+#include <memory>
+
+#include "nblib/basicdefinitions.h"
+
+namespace nblib
+{
+
+//! Enum for selecting the SIMD kernel type
+enum class SimdKernels : int
+{
+    SimdAuto,
+    SimdNo,
+    Simd4XM,
+    Simd2XMM,
+    Count
+};
+
+//! Enum for selecting the combination rule
+enum class CombinationRule : int
+{
+    Geometric,
+    LorentzBerthelot,
+    None,
+    Count
+};
+
+//! Enum for selecting coulomb type
+enum class CoulombType : int
+{
+    Pme,
+    Cutoff,
+    ReactionField,
+    Count
+};
+
+/*! \internal \brief
+ * The options for the nonbonded kernel caller
+ */
+struct NBKernelOptions final
+{
+    //! Whether to use a GPU, currently GPUs are not supported
+    bool useGpu = false;
+    //! The number of OpenMP threads to use
+    int numOpenMPThreads = 1;
+    //! The SIMD type for the kernel
+    SimdKernels nbnxmSimd = SimdKernels::SimdAuto;
+    //! The LJ combination rule
+    CombinationRule ljCombinationRule = CombinationRule::Geometric;
+    //! Use i-cluster half-LJ optimization for clusters with <= half LJ
+    bool useHalfLJOptimization = false;
+    //! The pairlist and interaction cut-off
+    real pairlistCutoff = 1.0;
+    //! Whether to compute energies (shift forces for virial are always computed on CPU)
+    bool computeVirialAndEnergy = false;
+    //! The Coulomb interaction function
+    CoulombType coulombType = CoulombType::Pme;
+    //! Whether to use tabulated PME grid correction instead of analytical, not applicable with simd=no
+    bool useTabulatedEwaldCorr = false;
+    //! The number of iterations for each kernel
+    int numIterations = 100;
+    //! Print cycles/pair instead of pairs/cycle
+    bool cyclesPerPair = false;
+    //! The time step
+    real timestep = 0.001;
+};
+
+} // namespace nblib
+
+#endif // NBLIB_KERNELOPTIONS_H
diff --git a/api/nblib/molecules.cpp b/api/nblib/molecules.cpp
new file mode 100644 (file)
index 0000000..b8f70ee
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements nblib Molecule
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include <algorithm>
+#include <tuple>
+
+#include "nblib/exception.h"
+#include "nblib/molecules.h"
+#include "nblib/particletype.h"
+#include "nblib/util/internal.h"
+
+namespace nblib
+{
+
+
+Molecule::Molecule(MoleculeName moleculeName) : name_(std::move(moleculeName)) {}
+
+MoleculeName Molecule::name() const
+{
+    return name_;
+}
+
+Molecule& Molecule::addParticle(const ParticleName& particleName,
+                                const ResidueName&  residueName,
+                                const Charge&       charge,
+                                ParticleType const& particleType)
+{
+    auto found = particleTypes_.find(particleType.name());
+    if (found == particleTypes_.end())
+    {
+        particleTypes_.insert(std::make_pair(particleType.name(), particleType));
+    }
+    else
+    {
+        if (!(found->second == particleType))
+        {
+            throw InputException(
+                    "Differing ParticleTypes with identical names encountered in the same "
+                    "molecule.");
+        }
+    }
+
+    particles_.emplace_back(ParticleData{ particleName, residueName, particleType.name(), charge });
+
+    // Add self exclusion. We just added the particle, so we know its index and that the exclusion doesn't exist yet
+    std::size_t id = particles_.size() - 1;
+    exclusions_.emplace_back(id, id);
+
+    return *this;
+}
+
+Molecule& Molecule::addParticle(const ParticleName& particleName,
+                                const ResidueName&  residueName,
+                                ParticleType const& particleType)
+{
+    addParticle(particleName, residueName, Charge(0), particleType);
+
+    return *this;
+}
+
+Molecule& Molecule::addParticle(const ParticleName& particleName,
+                                const Charge&       charge,
+                                ParticleType const& particleType)
+{
+    addParticle(particleName, ResidueName(name_), charge, particleType);
+
+    return *this;
+}
+
+Molecule& Molecule::addParticle(const ParticleName& particleName, const ParticleType& particleType)
+{
+    addParticle(particleName, ResidueName(name_), Charge(0), particleType);
+
+    return *this;
+}
+
+int Molecule::numParticlesInMolecule() const
+{
+    return particles_.size();
+}
+
+void Molecule::addExclusion(const int particleIndex, const int particleIndexToExclude)
+{
+    // We do not need to add exclusion in case the particle indexes are the same
+    // because self exclusion are added by addParticle
+    if (particleIndex != particleIndexToExclude)
+    {
+        exclusions_.emplace_back(particleIndex, particleIndexToExclude);
+        exclusions_.emplace_back(particleIndexToExclude, particleIndex);
+    }
+}
+
+void Molecule::addExclusion(std::tuple<std::string, std::string> particle,
+                            std::tuple<std::string, std::string> particleToExclude)
+{
+    // duplication for the swapped pair happens in getExclusions()
+    exclusionsByName_.emplace_back(std::make_tuple(std::get<0>(particle), std::get<1>(particle),
+                                                   std::get<0>(particleToExclude),
+                                                   std::get<1>(particleToExclude)));
+}
+
+void Molecule::addExclusion(const std::string& particleName, const std::string& particleNameToExclude)
+{
+    addExclusion(std::make_tuple(particleName, name_), std::make_tuple(particleNameToExclude, name_));
+}
+
+const ParticleType& Molecule::at(const std::string& particleTypeName) const
+{
+    return particleTypes_.at(particleTypeName);
+}
+
+ParticleName Molecule::particleName(int i) const
+{
+    return ParticleName(particles_[i].particleName_);
+}
+
+ResidueName Molecule::residueName(int i) const
+{
+    return ResidueName(particles_[i].residueName_);
+}
+
+std::vector<std::tuple<int, int>> Molecule::getExclusions() const
+{
+    // tuples of (particleName, residueName, index)
+    std::vector<std::tuple<std::string, std::string, int>> indexKey;
+    indexKey.reserve(numParticlesInMolecule());
+
+    for (int i = 0; i < numParticlesInMolecule(); ++i)
+    {
+        indexKey.emplace_back(particles_[i].particleName_, particles_[i].residueName_, i);
+    }
+
+    std::sort(std::begin(indexKey), std::end(indexKey));
+
+    std::vector<std::tuple<int, int>> ret = exclusions_;
+    ret.reserve(exclusions_.size() + exclusionsByName_.size());
+
+    // normal operator<, except ignore third element
+    auto sortKey = [](const auto& tup1, const auto& tup2) {
+        if (std::get<0>(tup1) < std::get<0>(tup2))
+        {
+            return true;
+        }
+        else
+        {
+            return std::get<1>(tup1) < std::get<1>(tup2);
+        }
+    };
+
+    // convert exclusions given by names to indices and append
+    for (auto& tup : exclusionsByName_)
+    {
+        const std::string& particleName1 = std::get<0>(tup);
+        const std::string& residueName1  = std::get<1>(tup);
+        const std::string& particleName2 = std::get<2>(tup);
+        const std::string& residueName2  = std::get<3>(tup);
+
+        // look up first index (binary search)
+        auto it1 = std::lower_bound(std::begin(indexKey), std::end(indexKey),
+                                    std::make_tuple(particleName1, residueName2, 0), sortKey);
+
+        // make sure we have the (particleName,residueName) combo
+        if (it1 == std::end(indexKey) or std::get<0>(*it1) != particleName1 or std::get<1>(*it1) != residueName1)
+        {
+            throw std::runtime_error(
+                    (std::string("Particle ") += particleName1 + std::string(" in residue ") +=
+                     residueName1 + std::string(" not found in list of particles\n")));
+        }
+
+        int firstIndex = std::get<2>(*it1);
+
+        // look up second index (binary search)
+        auto it2 = std::lower_bound(std::begin(indexKey), std::end(indexKey),
+                                    std::make_tuple(particleName2, residueName2, 0), sortKey);
+
+        // make sure we have the (particleName,residueName) combo
+        if (it2 == std::end(indexKey) or std::get<0>(*it2) != particleName2 or std::get<1>(*it2) != residueName2)
+        {
+            throw std::runtime_error(
+                    (std::string("Particle ") += particleName2 + std::string(" in residue ") +=
+                     residueName2 + std::string(" not found in list of particles\n")));
+        }
+
+        int secondIndex = std::get<2>(*it2);
+
+        ret.emplace_back(firstIndex, secondIndex);
+        ret.emplace_back(secondIndex, firstIndex);
+    }
+
+    std::sort(std::begin(ret), std::end(ret));
+
+    auto uniqueEnd = std::unique(std::begin(ret), std::end(ret));
+    if (uniqueEnd != std::end(ret))
+    {
+        printf("[nblib] Warning: exclusionList for molecule %s contained duplicates",
+               name_.value().c_str());
+    }
+
+    ret.erase(uniqueEnd, std::end(ret));
+    return ret;
+}
+
+std::unordered_map<std::string, ParticleType> Molecule::particleTypesMap() const
+{
+    return particleTypes_;
+}
+
+std::vector<ParticleData> Molecule::particleData() const
+{
+    return particles_;
+}
+
+} // namespace nblib
diff --git a/api/nblib/molecules.h b/api/nblib/molecules.h
new file mode 100644 (file)
index 0000000..afcae8c
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Implements nblib Molecule
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inpublicapi
+ * \ingroup nblib
+ */
+#ifndef NBLIB_MOLECULES_H
+#define NBLIB_MOLECULES_H
+
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include "nblib/particletype.h"
+
+namespace nblib
+{
+//! Named type for unique identifier for a particle in a molecule
+using ParticleName = StrongType<std::string, struct ParticleNameParameter>;
+
+//! Named type for charges on a particle within a molecule
+using Charge = StrongType<real, struct ChargeParameter>;
+
+//! Named type for residue name used to diffentiate between sections of a molecule
+using ResidueName = StrongType<std::string, struct ResidueNameParameter>;
+
+//! Named type for the name of a molecule
+using MoleculeName = StrongType<std::string, struct MoleculeNameParameter>;
+
+struct ParticleData
+{
+    std::string particleName_;
+    std::string residueName_;
+    std::string particleTypeName_;
+    real        charge_;
+};
+
+class Molecule final
+{
+public:
+    explicit Molecule(MoleculeName moleculeName);
+
+    //! Add a particle to the molecule with full specification of parameters.
+    Molecule& addParticle(const ParticleName& particleName,
+                          const ResidueName&  residueName,
+                          const Charge&       charge,
+                          ParticleType const& particleType);
+
+    //! Add a particle to the molecule with implicit charge of 0
+    Molecule& addParticle(const ParticleName& particleName,
+                          const ResidueName&  residueName,
+                          ParticleType const& particleType);
+
+    //! Add a particle to the molecule with residueName set using particleName
+    Molecule& addParticle(const ParticleName& particleName, const Charge& charge, ParticleType const& particleType);
+
+    //! Add a particle to the molecule with residueName set using particleName with implicit charge of 0
+    Molecule& addParticle(const ParticleName& particleName, ParticleType const& particleType);
+
+    // TODO: add exclusions based on the unique ID given to the particle of the molecule
+    void addExclusion(int particleIndex, int particleIndexToExclude);
+
+    //! Specify an exclusion with particle and residue names that have been added to molecule
+    void addExclusion(std::tuple<std::string, std::string> particle,
+                      std::tuple<std::string, std::string> particleToExclude);
+
+    //! Specify an exclusion with particle names that have been added to molecule
+    void addExclusion(const std::string& particleName, const std::string& particleNameToExclude);
+
+    //! The number of molecules
+    int numParticlesInMolecule() const;
+
+    //! Return the ParticleType data for a specific particle name that has been added to the molecule
+    const ParticleType& at(const std::string& particlesTypeName) const;
+
+    //! Convert exclusions given by name to indices and unify with exclusions given by indices
+    //! returns a sorted vector containing no duplicates of particles to exclude by indices
+    std::vector<std::tuple<int, int>> getExclusions() const;
+
+    //! Return name of ith particle
+    ParticleName particleName(int i) const;
+
+    //! Return name of ith residue
+    ResidueName residueName(int i) const;
+
+    //! Return array of data structs on particle types
+    std::vector<ParticleData> particleData() const;
+
+    //! Return map of particle types and their names
+    std::unordered_map<std::string, ParticleType> particleTypesMap() const;
+
+    //! The molecule name
+    MoleculeName name() const;
+
+private:
+    //! Name of the molecule
+    MoleculeName name_;
+
+    //! one entry per particle in molecule
+    std::vector<ParticleData> particles_;
+
+    //! collection of distinct particle types in molecule
+    std::unordered_map<std::string, ParticleType> particleTypes_;
+
+    //! Used for calculated exclusions based on particle indices in molecule
+    std::vector<std::tuple<int, int>> exclusions_;
+
+    //! we cannot efficiently compute indices during the build-phase
+    //! so we delay the conversion until TopologyBuilder requests it
+    std::vector<std::tuple<std::string, std::string, std::string, std::string>> exclusionsByName_;
+};
+
+} // namespace nblib
+#endif // NBLIB_MOLECULES_H
diff --git a/api/nblib/nblib.h b/api/nblib/nblib.h
new file mode 100644 (file)
index 0000000..55c510a
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Aggregates nblib public API headers
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef NBLIB_HEADERS_H
+#define NBLIB_HEADERS_H
+
+#include "nblib/basicdefinitions.h"
+#include "nblib/box.h"
+#include "nblib/forcecalculator.h"
+#include "nblib/integrator.h"
+#include "nblib/interactions.h"
+#include "nblib/kerneloptions.h"
+#include "nblib/molecules.h"
+#include "nblib/particletype.h"
+#include "nblib/simulationstate.h"
+#include "nblib/topology.h"
+#include "nblib/topologyhelpers.h"
+#include "nblib/util/user.h"
+
+#endif // NBLIB_HEADERS_H
similarity index 68%
rename from src/gromacs/nbnxm/opencl/nbnxm_ocl_internal.h
rename to api/nblib/particletype.cpp
index 7b533050732d8eaaf284aa90c3921effd81d9fc8..c14db11cead3d25ab8138c9c40fab291159001f7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \internal \file
- *  \brief Internal API of the OpenCL non-bonded module.
+ * \brief
+ * Implements nblib ParticleType
  *
- *  \author Szilárd Páll <pall.szilard@gmail.com>
- *  \ingroup module_nbnxm
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
  */
+#include "nblib/particletype.h"
 
-#include "gmxpre.h"
-
-#include "nbnxm_ocl_types.h"
+namespace nblib
+{
 
-#ifndef NBNXN_OCL_INTERNAL_H
-#    define NBNXN_OCL_INTERNAL_H
+ParticleType::ParticleType(ParticleTypeName name, Mass mass) : name_(std::move(name)), mass_(mass)
+{
+}
 
-namespace Nbnxm
+ParticleTypeName ParticleType::name() const
 {
+    return name_;
+}
 
-/*! \brief Returns true if LJ combination rules are used in the non-bonded kernels.
- *
- *  \param[in] vdwType The VdW interaction/implementation type as defined by evdwOcl in
- * nbnxn_ocl_types.h. \returns           True if combination rules are used by the run
- */
-bool useLjCombRule(int vdwType);
+Mass ParticleType::mass() const
+{
+    return mass_;
+}
 
-} // namespace Nbnxm
+bool operator==(const ParticleType& a, const ParticleType& b)
+{
+    return a.name() == b.name() && a.mass() == b.mass();
+}
 
-#endif /* NBNXN_OCL_INTERNAL_H */
+} // namespace nblib
diff --git a/api/nblib/particletype.h b/api/nblib/particletype.h
new file mode 100644 (file)
index 0000000..432bdb4
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares nblib ParticleTypes
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inpublicapi
+ * \ingroup nblib
+ */
+#ifndef NBLIB_PARTICLETYPE_H
+#define NBLIB_PARTICLETYPE_H
+
+#include <string>
+
+#include "nblib/util/user.h"
+
+namespace nblib
+{
+class TopologyBuilder;
+
+//! Named type for particle type name
+using ParticleTypeName = StrongType<std::string, struct ParticleTypeNameParameter>;
+//! Named type for particle mass
+using Mass = StrongType<real, struct MassParameter>;
+
+/*! \brief Class that represents the particle type.
+ *
+ * The particle type is used in lookup tables for masses, non-bonded parameters, etc.
+ * Every particle has to assigned an atom type.
+ */
+class ParticleType final
+{
+public:
+    /*! \brief Constructor with explicit name and mass specification.
+     *
+     * \param[in] name The unique name to reference the particle type.
+     * \param[in] mass The mass of the particle of this type.
+     */
+    ParticleType(ParticleTypeName name, Mass mass);
+
+    //! Get the type name
+    [[nodiscard]] ParticleTypeName name() const;
+
+    //! Get the mass
+    [[nodiscard]] Mass mass() const;
+
+private:
+    //! The name
+    ParticleTypeName name_;
+    //! The mass
+    Mass mass_;
+};
+
+/*! \brief Comparison operator
+ *
+ * \param[in] a First type.
+ * \param[in] b Second type.
+ * \returns If the types are identical.
+ */
+bool operator==(const ParticleType& a, const ParticleType& b);
+
+} // namespace nblib
+#endif // NBLIB_PARTICLETYPE_H
diff --git a/api/nblib/samples/CMakeLists.txt b/api/nblib/samples/CMakeLists.txt
new file mode 100644 (file)
index 0000000..28f8349
--- /dev/null
@@ -0,0 +1,58 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+#
+# \author Victor Holanda <victor.holanda@cscs.ch>
+# \author Joe Jordan <ejjordan@kth.se>
+# \author Prashanth Kanduri <kanduri@cscs.ch>
+# \author Sebastian Keller <keller@cscs.ch>
+#
+
+add_executable(argon-forces-integration "")
+
+target_sources(argon-forces-integration
+        PRIVATE
+        argon-forces-integration.cpp
+        )
+
+target_link_libraries(argon-forces-integration PRIVATE nblib)
+install(
+        TARGETS
+        argon-forces-integration
+        DESTINATION
+        ${CMAKE_INSTALL_BINDIR}
+)
+
+if(BUILD_TESTING)
+    add_subdirectory(tests)
+endif()
diff --git a/api/nblib/samples/argon-forces-integration.cpp b/api/nblib/samples/argon-forces-integration.cpp
new file mode 100644 (file)
index 0000000..9aacc2a
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This tests that sample code can run
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+
+#include <cstdio>
+
+// The entire nblib public API can be included with a single header or individual components
+// can be included via their respective headers.
+#include "nblib/nblib.h"
+
+// User defined coordinates.
+std::vector<nblib::Vec3> coordinates = {
+    { 0.794, 1.439, 0.610 }, { 1.397, 0.673, 1.916 }, { 0.659, 1.080, 0.573 },
+    { 1.105, 0.090, 3.431 }, { 1.741, 1.291, 3.432 }, { 1.936, 1.441, 5.873 },
+    { 0.960, 2.246, 1.659 }, { 0.382, 3.023, 2.793 }, { 0.053, 4.857, 4.242 },
+    { 2.655, 5.057, 2.211 }, { 4.114, 0.737, 0.614 }, { 5.977, 5.104, 5.217 },
+};
+
+// User defined velocities.
+std::vector<nblib::Vec3> velocities = {
+    { 0.0055, -0.1400, 0.2127 },   { 0.0930, -0.0160, -0.0086 }, { 0.1678, 0.2476, -0.0660 },
+    { 0.1591, -0.0934, -0.0835 },  { -0.0317, 0.0573, 0.1453 },  { 0.0597, 0.0013, -0.0462 },
+    { 0.0484, -0.0357, 0.0168 },   { 0.0530, 0.0295, -0.2694 },  { -0.0550, -0.0896, 0.0494 },
+    { -0.0799, -0.2534, -0.0079 }, { 0.0436, -0.1557, 0.1849 },  { -0.0214, 0.0446, 0.0758 },
+};
+
+// Force buffer initialization for each particle.
+std::vector<nblib::Vec3> forces = {
+    { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+    { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+    { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+    { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+};
+
+// Main function to write the MD program.
+int main(); // Keep the compiler happy
+
+int main()
+{
+    // Create an argon particle with a name and a mass.
+    nblib::ParticleType argonAtom(nblib::ParticleTypeName("Ar"), nblib::Mass(39.94800));
+    // Create an argon molecule.
+    nblib::Molecule argonMolecule(nblib::MoleculeName("AR"));
+    // Add the argon particle to a molecule. The names are for bookkeeping and need not match.
+    argonMolecule.addParticle(nblib::ParticleName("Argon"), argonAtom);
+    // Define Lennard-Jones params for argon (parameters from gromos43A1).
+    const nblib::C6  ArC6{ 0.0062647225 };  // C6 parameter
+    const nblib::C12 ArC12{ 9.847044e-06 }; // C12 parameter
+    // Holder for non-bonded interactions.
+    nblib::ParticleTypesInteractions interactions;
+    // Add non-bonded interactions for argon.
+    interactions.add(argonAtom.name(), ArC6, ArC12);
+    // The TopologyBuilder builds the Topology!
+    nblib::TopologyBuilder topologyBuilder;
+    // Number of Argon particles (molecules) in the system.
+    const int numParticles = 12;
+    // Add the requested number of argon molecules to a topology.
+    topologyBuilder.addMolecule(argonMolecule, numParticles);
+    // Add the argon interactions to the topology.
+    topologyBuilder.addParticleTypesInteractions(interactions);
+    // Build the topology.
+    nblib::Topology topology = topologyBuilder.buildTopology();
+    // The system needs a bounding box. Only cubic and rectangular boxes are supported.
+    nblib::Box box(6.05449);
+    // A simulation state contains all the molecular information about the system.
+    nblib::SimulationState simState(coordinates, velocities, forces, box, topology);
+    // Kernel options are flags needed for force calculation.
+    nblib::NBKernelOptions options = nblib::NBKernelOptions();
+    // Use a simple cutoff rule for Coulomb
+    options.coulombType = nblib::CoulombType::Cutoff;
+    // Some performance flags can be set a run time
+    options.nbnxmSimd = nblib::SimdKernels::SimdNo;
+    // The force calculator contains all the data needed to compute forces.
+    nblib::ForceCalculator forceCalculator(simState, options);
+    // Integration requires masses, positions, and forces
+    nblib::LeapFrog integrator(simState.topology(), simState.box());
+    // Print some diagnostic info
+    printf("initial forces on particle 0: x %4f y %4f z %4f\n", forces[0][0], forces[0][1], forces[0][2]);
+    // The forces are computed for the user
+    gmx::ArrayRef<nblib::Vec3> userForces(simState.forces());
+    forceCalculator.compute(simState.coordinates(), userForces);
+    // Print some diagnostic info
+    printf("  final forces on particle 0: x %4f y %4f z %4f\n", userForces[0][0], userForces[0][1],
+           userForces[0][2]);
+    // User may modify forces stored in simState.forces() if needed
+    // Print some diagnostic info
+    printf("initial position of particle 0: x %4f y %4f z %4f\n", simState.coordinates()[0][0],
+           simState.coordinates()[0][1], simState.coordinates()[0][2]);
+    // Integrate with a time step of 1 fs
+    integrator.integrate(1.0, simState.coordinates(), simState.velocities(), simState.forces());
+    // Print some diagnostic info
+
+    printf("  final position of particle 0: x %4f y %4f z %4f\n", simState.coordinates()[0][0],
+           simState.coordinates()[0][1], simState.coordinates()[0][2]);
+    return 0;
+}
diff --git a/api/nblib/samples/tests/CMakeLists.txt b/api/nblib/samples/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c51b090
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+#
+# \author Victor Holanda <victor.holanda@cscs.ch>
+# \author Joe Jordan <ejjordan@kth.se>
+# \author Prashanth Kanduri <kanduri@cscs.ch>
+# \author Sebastian Keller <keller@cscs.ch>
+#
+
+
+set(testname "NbLibSamplesTests")
+set(exename "nblib-samples-test")
+
+gmx_add_gtest_executable(
+    ${exename}
+    CPP_SOURCE_FILES
+    # files with code for tests
+        samples.cpp
+    )
+#The following line is a not so nice hack to prevent compiler warnings that are not relevant for the sample scripts
+set_source_files_properties(samples.cpp PROPERTIES COMPILE_FLAGS "-Wno-header-hygiene")
+target_link_libraries(nblib-samples-test PRIVATE nblib)
+gmx_register_gtest_test(${testname} ${exename})
+add_dependencies(check-nblib ${exename})
similarity index 72%
rename from src/gromacs/gpu_utils/tests/gputest.h
rename to api/nblib/samples/tests/samples.cpp
index 788725170fec74bc3610fc7917e977129483ede3..cdb289d23eb27e41695dba222781c346b23bdb3a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  */
 /*! \internal \file
  * \brief
- * Declares test fixture testing GPU utility components.
+ * This tests that sample code can run
  *
- * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
  */
-#ifndef GMX_GPU_UTILS_TESTS_GPUTEST_H
-#define GMX_GPU_UTILS_TESTS_GPUTEST_H
-
 #include "gmxpre.h"
 
-#include <gtest/gtest.h>
-
-struct gmx_gpu_info_t;
-
-namespace gmx
-{
-namespace test
-{
+#include "testutils/refdata.h"
+#include "testutils/testasserts.h"
 
-class GpuTest : public ::testing::Test
+//! Google Test defines a main function so we rename the main function of the sample scripts
+#define main ArgonSampleMain
+#include "nblib/samples/argon-forces-integration.cpp"
+TEST(NBlibTest, ArgonSampleDoesNotThrow)
 {
-public:
-    //! Information about GPUs that are present.
-    gmx_gpu_info_t* gpuInfo_;
-
-    GpuTest();
-    ~GpuTest() override;
-    //! Getter for convenience in testing
-    bool haveValidGpus() const;
-};
-
-} // namespace test
-} // namespace gmx
-
-#endif
+    EXPECT_NO_FATAL_FAILURE(main());
+}
+#undef main
diff --git a/api/nblib/simulationstate.cpp b/api/nblib/simulationstate.cpp
new file mode 100644 (file)
index 0000000..9e38433
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements nblib SimulationState
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include <vector>
+
+#include "gromacs/pbcutil/pbc.h"
+#include "nblib/exception.h"
+#include "nblib/simulationstate.h"
+#include "nblib/simulationstateimpl.h"
+
+namespace nblib
+{
+
+SimulationState::SimulationState(const std::vector<Vec3>& coordinates,
+                                 const std::vector<Vec3>& velocities,
+                                 const std::vector<Vec3>& forces,
+                                 Box                      box,
+                                 Topology                 topology) :
+    simulationStatePtr_(std::make_shared<Impl>(coordinates, velocities, forces, box, topology))
+{
+}
+
+SimulationState::Impl::Impl(const std::vector<Vec3>& coordinates,
+                            const std::vector<Vec3>& velocities,
+                            const std::vector<Vec3>& forces,
+                            const Box&               box,
+                            Topology                 topology) :
+    box_(box),
+    topology_(std::move(topology))
+{
+    if (!checkNumericValues(coordinates))
+    {
+        throw InputException("Input coordinates has at least one NaN");
+    }
+    coordinates_ = coordinates;
+    if (!checkNumericValues(velocities))
+    {
+        throw InputException("Input velocities has at least one NaN");
+    }
+
+    velocities_ = velocities;
+
+    forces_ = forces;
+
+    // Ensure that the coordinates are in a box following PBC
+    put_atoms_in_box(PbcType::Xyz, box.legacyMatrix(), coordinates_);
+}
+
+const Topology& SimulationState::Impl::topology() const
+{
+    return topology_;
+}
+
+Box SimulationState::Impl::box() const
+{
+    return box_;
+}
+
+std::vector<Vec3>& SimulationState::Impl::coordinates()
+{
+    return coordinates_;
+}
+
+std::vector<Vec3>& SimulationState::Impl::velocities()
+{
+    return velocities_;
+}
+
+std::vector<Vec3>& SimulationState::Impl::forces()
+{
+    return forces_;
+}
+
+const Topology& SimulationState::topology() const
+{
+    return simulationStatePtr_->topology();
+}
+
+Box SimulationState::box() const
+{
+    return simulationStatePtr_->box();
+}
+
+std::vector<Vec3>& SimulationState::coordinates()
+{
+    return simulationStatePtr_->coordinates();
+}
+
+const std::vector<Vec3>& SimulationState::coordinates() const
+{
+    return simulationStatePtr_->coordinates();
+}
+
+std::vector<Vec3>& SimulationState::velocities()
+{
+    return simulationStatePtr_->velocities();
+}
+
+std::vector<Vec3>& SimulationState::forces()
+{
+    return simulationStatePtr_->forces();
+}
+
+} // namespace nblib
diff --git a/api/nblib/simulationstate.h b/api/nblib/simulationstate.h
new file mode 100644 (file)
index 0000000..db93eb5
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib SimulationState
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef NBLIB_SIMULATIONSTATE_H
+#define NBLIB_SIMULATIONSTATE_H
+
+#include <memory>
+#include <vector>
+
+#include "nblib/box.h"
+#include "nblib/topology.h"
+
+namespace nblib
+{
+
+/*! \libinternal
+ * \ingroup nblib
+ * \brief Simulation State
+ *
+ * Simulation state description that serves as a snapshot of the system
+ * being analysed. Needed to init an MD program. Allows hot-starting simulations.
+ */
+
+class SimulationState final
+{
+public:
+    //! Constructor
+    SimulationState(const std::vector<Vec3>& coordinates,
+                    const std::vector<Vec3>& velocities,
+                    const std::vector<Vec3>& forces,
+                    Box                      box,
+                    Topology                 topology);
+
+    //! Returns topology of the current state
+    const Topology& topology() const;
+
+    //! Returns the box
+    Box box() const;
+
+    //! Returns a reference to a (modifiable) vector of particle coordinates
+    std::vector<Vec3>& coordinates();
+
+    //! Returns a read-only vector of particle coordinates
+    const std::vector<Vec3>& coordinates() const;
+
+    //! Returns a reference to a (modifiable) vector of particle velocities
+    std::vector<Vec3>& velocities();
+
+    //! Returns a reference to a (modifiable) vector of forces
+    std::vector<Vec3>& forces();
+
+private:
+    class Impl;
+    std::shared_ptr<SimulationState::Impl> simulationStatePtr_;
+};
+
+} // namespace nblib
+
+#endif // NBLIB_SIMULATIONSTATE_H
diff --git a/api/nblib/simulationstateimpl.h b/api/nblib/simulationstateimpl.h
new file mode 100644 (file)
index 0000000..8ed604b
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib SimulationState
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef NBLIB_SIMULATIONSTATE_IMPL_H
+#define NBLIB_SIMULATIONSTATE_IMPL_H
+
+namespace nblib
+{
+
+class SimulationState::Impl final
+{
+public:
+    //! Constructor
+    Impl(const std::vector<Vec3>& coordinates,
+         const std::vector<Vec3>& velocities,
+         const std::vector<Vec3>& forces,
+         const Box&               box,
+         Topology                 topology);
+
+    //! Returns topology of the current state
+    const Topology& topology() const;
+
+    //! Returns the box
+    Box box() const;
+
+    //! Returns a vector of particle coordinates
+    std::vector<Vec3>& coordinates();
+
+    //! Returns a vector of particle velocities
+    std::vector<Vec3>& velocities();
+
+    //! Returns a vector of forces
+    std::vector<Vec3>& forces();
+
+private:
+    std::vector<Vec3> coordinates_ = {};
+    std::vector<Vec3> velocities_  = {};
+    std::vector<Vec3> forces_      = {};
+    Box               box_;
+    Topology          topology_;
+};
+
+} // namespace nblib
+
+#endif // NBLIB_SIMULATIONSTATE_IMPL_H
diff --git a/api/nblib/tests/CMakeLists.txt b/api/nblib/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3660273
--- /dev/null
@@ -0,0 +1,98 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+#
+# \author Victor Holanda <victor.holanda@cscs.ch>
+# \author Joe Jordan <ejjordan@kth.se>
+# \author Prashanth Kanduri <kanduri@cscs.ch>
+# \author Sebastian Keller <keller@cscs.ch>
+#
+
+# Make a static library for test infrastructure code that we re-use
+# in multiple test executables across the repository.
+gmx_add_unit_test_library(nblib_test_infrastructure
+        testhelpers.cpp
+        testsystems.cpp
+    )
+target_include_directories(nblib_test_infrastructure PRIVATE ${PROJECT_SOURCE_DIR}/api)
+target_include_directories(nblib_test_infrastructure SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/src/external)
+
+set(testname "NbLibSetupTests")
+set(exename "nblib-setup-test")
+
+gmx_add_gtest_executable(
+    ${exename}
+    CPP_SOURCE_FILES
+    # files with code for tests
+        box.cpp
+        interactions.cpp
+        particletype.cpp
+        molecules.cpp
+        topology.cpp
+    )
+target_link_libraries(${exename} PRIVATE nblib_test_infrastructure nblib)
+target_include_directories(${exename} PRIVATE ${PROJECT_SOURCE_DIR}/api)
+gmx_register_gtest_test(${testname} ${exename} INTEGRATION_TEST)
+add_dependencies(check-nblib ${exename})
+
+set(testname "NbLibIntegrationTests")
+set(exename "nblib-integration-test")
+
+gmx_add_gtest_executable(
+    ${exename}
+    CPP_SOURCE_FILES
+    # files with code for tests
+        gmxcalculator.cpp
+        nbkernelsystem.cpp
+        nbnxnsetup.cpp
+        simstate.cpp
+    )
+target_link_libraries(${exename} PRIVATE nblib_test_infrastructure nblib)
+target_include_directories(${exename} PRIVATE ${PROJECT_SOURCE_DIR}/api)
+gmx_register_gtest_test(${testname} ${exename} INTEGRATION_TEST)
+add_dependencies(check-nblib ${exename})
+
+# The integrator sometimes times out on TSAN so it gets its own test harness
+set(testname "NbLibIntegratorTests")
+set(exename "nblib-integrator-test")
+
+gmx_add_gtest_executable(
+        ${exename}
+        CPP_SOURCE_FILES
+        integrator.cpp
+)
+target_link_libraries(${exename} PRIVATE nblib_test_infrastructure nblib)
+target_include_directories(${exename} PRIVATE ${PROJECT_SOURCE_DIR}/api)
+gmx_register_gtest_test(${testname} ${exename} INTEGRATION_TEST)
+add_dependencies(check-nblib ${exename})
+
diff --git a/api/nblib/tests/box.cpp b/api/nblib/tests/box.cpp
new file mode 100644 (file)
index 0000000..45900e7
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements basic nblib box tests
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include <cmath>
+
+#include "nblib/box.h"
+#include "nblib/exception.h"
+
+#include "testutils/refdata.h"
+#include "testutils/testasserts.h"
+
+using gmx::test::defaultRealTolerance;
+
+namespace nblib
+{
+
+TEST(NBlibTest, CubicBoxCannotHaveNaN)
+{
+    real number = NAN;
+    EXPECT_THROW(Box box(number), InputException);
+}
+
+TEST(NBlibTest, CubicBoxCannotHaveInf)
+{
+    real number = INFINITY;
+    EXPECT_THROW(Box box(number), InputException);
+}
+
+TEST(NBlibTest, RectangularBoxCannotHaveNaN)
+{
+    real number = NAN;
+    EXPECT_THROW(Box box(number, real(1.), real(1.)), InputException);
+}
+
+TEST(NBlibTest, RectangularBoxCannotHaveInf)
+{
+    real number = INFINITY;
+    EXPECT_THROW(Box box(number, real(1.), real(1.)), InputException);
+}
+
+TEST(NBlibTest, CubicBoxWorks)
+{
+    real              length = 3;
+    Box::LegacyMatrix ref    = { { length, 0, 0 }, { 0, length, 0 }, { 0, 0, length } };
+    Box               test   = Box(length);
+
+    for (int i = 0; i < dimSize; ++i)
+    {
+        for (int j = 0; j < dimSize; ++j)
+        {
+            EXPECT_REAL_EQ_TOL(ref[i][j], test.legacyMatrix()[i][j], defaultRealTolerance());
+        }
+    }
+}
+
+} // namespace nblib
diff --git a/api/nblib/tests/gmxcalculator.cpp b/api/nblib/tests/gmxcalculator.cpp
new file mode 100644 (file)
index 0000000..c50b2c8
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements basic nblib utility tests
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include <gtest/gtest.h>
+
+#include "nblib/gmxsetup.h"
+#include "nblib/kerneloptions.h"
+#include "nblib/simulationstate.h"
+#include "nblib/tests/testsystems.h"
+
+namespace nblib
+{
+namespace test
+{
+namespace
+{
+TEST(NBlibTest, GmxForceCalculatorCanCompute)
+{
+    ArgonSimulationStateBuilder argonSystemBuilder;
+    SimulationState             simState = argonSystemBuilder.setupSimulationState();
+    NBKernelOptions             options  = NBKernelOptions();
+    options.nbnxmSimd                    = SimdKernels::SimdNo;
+    std::unique_ptr<GmxForceCalculator> gmxForceCalculator =
+            nblib::GmxSetupDirector::setupGmxForceCalculator(simState, options);
+    EXPECT_NO_THROW(gmxForceCalculator->compute(simState.coordinates(), simState.forces()));
+}
+
+TEST(NBlibTest, CanSetupStepWorkload)
+{
+    NBKernelOptions options;
+    EXPECT_NO_THROW(NbvSetupUtil{}.setupStepWorkload(options));
+}
+
+TEST(NBlibTest, GmxForceCalculatorCanSetupInteractionConst)
+{
+    NBKernelOptions options;
+    EXPECT_NO_THROW(NbvSetupUtil{}.setupInteractionConst(options));
+}
+} // namespace
+} // namespace test
+} // namespace nblib
diff --git a/api/nblib/tests/integrator.cpp b/api/nblib/tests/integrator.cpp
new file mode 100644 (file)
index 0000000..a1ee57f
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements molecule setup tests
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include "nblib/integrator.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "nblib/molecules.h"
+#include "nblib/particletype.h"
+#include "nblib/simulationstate.h"
+#include "nblib/topology.h"
+#include "nblib/util/internal.h"
+
+#include "testutils/testasserts.h"
+
+namespace nblib
+{
+namespace test
+{
+namespace
+{
+
+TEST(NBlibTest, IntegratorWorks)
+{
+    int  numAtoms = 1;
+    int  numSteps = 100;
+    real dt       = 0.001;
+
+    ParticleType particleType(ParticleTypeName("H"), Mass(1.0));
+    Molecule     molecule(MoleculeName("SomeMolecule"));
+    molecule.addParticle(ParticleName("SomeAtom"), particleType);
+
+    ParticleTypesInteractions interactions;
+    interactions.add(particleType.name(), C6{ 0 }, C12{ 0 });
+
+    TopologyBuilder topologyBuilder;
+    topologyBuilder.addMolecule(molecule, numAtoms);
+    topologyBuilder.addParticleTypesInteractions(interactions);
+    Topology topology = topologyBuilder.buildTopology();
+
+    std::vector<Vec3> x(numAtoms);
+    std::vector<Vec3> v(numAtoms);
+    std::vector<Vec3> f(numAtoms);
+
+    f[0][XX] = 1.0;
+    f[0][YY] = 2.0;
+    f[0][ZZ] = 0.0;
+
+    Box box(100);
+
+    std::vector<Vec3> x0(x);
+    std::vector<Vec3> v0(v);
+
+    SimulationState simulationState(x, v, f, box, topology);
+    put_atoms_in_box(PbcType::Xyz, box.legacyMatrix(), x0);
+
+    LeapFrog integrator(simulationState.topology(), simulationState.box());
+
+    gmx::test::FloatingPointTolerance tolerance = gmx::test::absoluteTolerance(numSteps * 0.000005);
+    for (int step = 0; step < numSteps; step++)
+    {
+        real totalTime = step * dt;
+
+        Vec3 xAnalytical;
+        Vec3 vAnalytical;
+
+        for (int i = 0; i < numAtoms; i++)
+        {
+            for (int d = 0; d < dimSize; d++)
+            {
+                // Analytical solution for constant-force particle movement
+                int  typeIndex = simulationState.topology().getParticleTypeIdOfAllParticles()[i];
+                real im = 1.0 / simulationState.topology().getParticleTypes()[typeIndex].mass();
+                xAnalytical[d] =
+                        x0[i][d] + v0[i][d] * totalTime + 0.5 * f[i][d] * totalTime * totalTime * im;
+                vAnalytical[d] = v0[i][d] + f[i][d] * totalTime * im;
+
+                EXPECT_REAL_EQ_TOL(xAnalytical[d], simulationState.coordinates()[i][d], tolerance)
+                        << formatString(
+                                   "Coordinate {} of atom {} is different from analytical solution "
+                                   "at step {}.",
+                                   d, i, step);
+
+                EXPECT_REAL_EQ_TOL(vAnalytical[d], simulationState.velocities()[i][d], tolerance)
+                        << formatString(
+                                   "Velocity component {} of atom {} is different from analytical "
+                                   "solution at step {}.",
+                                   d, i, step);
+            }
+            integrator.integrate(dt, simulationState.coordinates(), simulationState.velocities(),
+                                 simulationState.forces());
+        }
+    }
+}
+
+} // namespace
+} // namespace test
+} // namespace nblib
diff --git a/api/nblib/tests/interactions.cpp b/api/nblib/tests/interactions.cpp
new file mode 100644 (file)
index 0000000..bb93f5d
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements molecule setup tests
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include "nblib/interactions.h"
+#include "nblib/exception.h"
+#include "nblib/particletype.h"
+#include "nblib/tests/testsystems.h"
+
+#include "testutils/testasserts.h"
+
+namespace nblib
+{
+namespace test
+{
+namespace
+{
+
+TEST(NBlibTest, NonBondedForceParamsCorrect)
+{
+    ParticleType atom1(ParticleTypeName("a1"), Mass(1));
+    ParticleType atom2(ParticleTypeName("a2"), Mass(1));
+    ParticleType atom3(ParticleTypeName("a3"), Mass(1));
+
+    ParticleTypesInteractions interactions;
+
+    auto c6_1  = C6(1.6);
+    auto c12_1 = C12(1.12);
+    C6   c6_2{ 2.6 };
+    C12  c12_2{ 2.12 };
+    C6   c6_3  = C6(3.6);
+    C12  c12_3 = C12(3.12);
+
+    auto c6comb  = C6{ 40 };
+    auto c12comb = C12{ 41 };
+
+    interactions.add(atom1.name(), c6_1, c12_1);
+    interactions.add(atom2.name(), c6_2, c12_2);
+    interactions.add(atom3.name(), c6_3, c12_3);
+    interactions.add(atom2.name(), atom3.name(), c6comb, c12comb);
+
+    auto nbfp = interactions.generateTable();
+
+    //! self interaction for c6
+    EXPECT_REAL_EQ_TOL(c6_1, nbfp.getC6(atom1.name(), atom1.name()), gmx::test::defaultRealTolerance());
+    //! + symmetric pair
+    EXPECT_REAL_EQ_TOL(c12_1, nbfp.getC12(atom1.name(), atom1.name()), gmx::test::defaultRealTolerance());
+
+    //! geometric comb rule for c6
+    EXPECT_REAL_EQ_TOL(std::sqrt(c6_1 * c6_2), nbfp.getC6(atom1.name(), atom2.name()),
+                       gmx::test::defaultRealTolerance());
+    //! + symmetric pair
+    EXPECT_REAL_EQ_TOL(std::sqrt(c6_1 * c6_2), nbfp.getC6(atom2.name(), atom1.name()),
+                       gmx::test::defaultRealTolerance());
+
+    //! geometric comb rule for c12
+    EXPECT_REAL_EQ_TOL(std::sqrt(c12_1 * c12_2), nbfp.getC12(atom1.name(), atom2.name()),
+                       gmx::test::defaultRealTolerance());
+
+    //! + symmetric par
+    EXPECT_REAL_EQ_TOL(std::sqrt(c12_1 * c12_2), nbfp.getC12(atom2.name(), atom1.name()),
+                       gmx::test::defaultRealTolerance());
+
+    //! explicit pairwise interaction c6
+    EXPECT_REAL_EQ_TOL(c6comb, nbfp.getC6(atom2.name(), atom3.name()), gmx::test::defaultRealTolerance());
+    EXPECT_REAL_EQ_TOL(c6comb, nbfp.getC6(atom3.name(), atom2.name()), gmx::test::defaultRealTolerance());
+
+    //! explicit pairwise interaction c12
+    EXPECT_REAL_EQ_TOL(c12comb, nbfp.getC12(atom2.name(), atom3.name()),
+                       gmx::test::defaultRealTolerance());
+    EXPECT_REAL_EQ_TOL(c12comb, nbfp.getC12(atom3.name(), atom2.name()),
+                       gmx::test::defaultRealTolerance());
+
+    ParticleType atom4(ParticleTypeName("a4"), Mass(1));
+    interactions.add(atom3.name(), atom4.name(), C6(1), C12(2));
+    //! need to have self-interaction for all particles
+    EXPECT_THROW(auto interactionsTable = interactions.generateTable(), InputException);
+}
+
+TEST(NBlibTest, CanMergeInteractions)
+{
+    ParticleType atom1(ParticleTypeName("a1"), Mass(1));
+    ParticleType atom2(ParticleTypeName("a2"), Mass(1));
+    ParticleType atom3(ParticleTypeName("a3"), Mass(1));
+
+    ParticleTypesInteractions interactions;
+
+    auto c6_1  = C6(1.6);
+    auto c12_1 = C12(1.12);
+    C6   c6_2{ 2.6 };
+    C12  c12_2{ 2.12 };
+    C6   c6_3  = C6(3.6);
+    C12  c12_3 = C12(3.12);
+
+    auto c6comb  = C6{ 40 };
+    auto c12comb = C12{ 41 };
+
+    interactions.add(atom1.name(), c6_1, c12_1);
+    interactions.add(atom2.name(), c6_2, c12_2);
+    interactions.add(atom3.name(), c6_3, c12_3);
+    interactions.add(atom2.name(), atom3.name(), c6comb, c12comb);
+
+    ParticleType atom4(ParticleTypeName("a4"), Mass(1));
+    ParticleType atom5(ParticleTypeName("a5"), Mass(1));
+    auto         c6_4  = C6{ 4.6 };
+    auto         c12_4 = C12{ 4.12 };
+
+    C6  c6_5{ 5.6 };
+    C12 c12_5{ 5.12 };
+
+    C6  c6_override  = C6(45.6);
+    C12 c12_override = C12(45.12);
+
+    ParticleTypesInteractions otherInteractions;
+    otherInteractions.add(atom4.name(), c6_4, c12_4);
+    otherInteractions.add(atom5.name(), c6_5, c12_5);
+    otherInteractions.add(atom4.name(), atom5.name(), c6_override, c12_override);
+
+    interactions.merge(otherInteractions);
+
+    auto nbfp = interactions.generateTable();
+
+    EXPECT_REAL_EQ_TOL(std::sqrt(c6_3 * c6_4), nbfp.getC6(atom3.name(), atom4.name()),
+                       gmx::test::defaultRealTolerance());
+    EXPECT_REAL_EQ_TOL(std::sqrt(c12_3 * c12_4), nbfp.getC12(atom3.name(), atom4.name()),
+                       gmx::test::defaultRealTolerance());
+    EXPECT_REAL_EQ_TOL(c6_override, nbfp.getC6(atom4.name(), atom5.name()),
+                       gmx::test::defaultRealTolerance());
+    EXPECT_REAL_EQ_TOL(c12_override, nbfp.getC12(atom4.name(), atom5.name()),
+                       gmx::test::defaultRealTolerance());
+}
+
+} // namespace
+} // namespace test
+} // namespace nblib
diff --git a/api/nblib/tests/molecules.cpp b/api/nblib/tests/molecules.cpp
new file mode 100644 (file)
index 0000000..52e5248
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements molecule setup tests
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include "nblib/molecules.h"
+#include "nblib/exception.h"
+#include "nblib/particletype.h"
+#include "nblib/tests/testsystems.h"
+
+#include "testutils/testasserts.h"
+
+namespace nblib
+{
+namespace test
+{
+namespace
+{
+
+TEST(NBlibTest, CanConstructMoleculeWithoutChargeOrResidueName)
+{
+    ArAtom       arAtom;
+    ParticleType Ar(arAtom.particleTypeName, arAtom.mass);
+    Molecule     argon(arAtom.moleculeName);
+    EXPECT_NO_THROW(argon.addParticle(arAtom.particleName, Ar));
+}
+
+TEST(NBlibTest, CanConstructMoleculeWithChargeWithoutResidueName)
+{
+    ArAtom       arAtom;
+    ParticleType Ar(arAtom.particleTypeName, arAtom.mass);
+    Molecule     argon(arAtom.moleculeName);
+    EXPECT_NO_THROW(argon.addParticle(arAtom.particleName, Charge(0), Ar));
+}
+
+TEST(NBlibTest, CanConstructMoleculeWithoutChargeWithResidueName)
+{
+    ArAtom       arAtom;
+    ParticleType Ar(arAtom.particleTypeName, arAtom.mass);
+    Molecule     argon(arAtom.moleculeName);
+    EXPECT_NO_THROW(argon.addParticle(arAtom.particleName, ResidueName("ar2"), Ar));
+}
+
+TEST(NBlibTest, CanConstructMoleculeWithChargeWithResidueName)
+{
+    ArAtom       arAtom;
+    ParticleType Ar(arAtom.particleTypeName, arAtom.mass);
+    Molecule     argon(arAtom.moleculeName);
+    EXPECT_NO_THROW(argon.addParticle(arAtom.particleName, ResidueName("ar2"), Charge(0), Ar));
+}
+
+TEST(NBlibTest, CanGetNumParticlesInMolecule)
+{
+    WaterMoleculeBuilder waterMolecule;
+    Molecule             water        = waterMolecule.waterMolecule();
+    auto                 numParticles = water.numParticlesInMolecule();
+
+    EXPECT_EQ(3, numParticles);
+}
+
+TEST(NBlibTest, CanConstructExclusionListFromNames)
+{
+    WaterMoleculeBuilder waterMolecule;
+    Molecule             water = waterMolecule.waterMolecule();
+
+    std::vector<std::tuple<int, int>> exclusions = water.getExclusions();
+
+    std::vector<std::tuple<int, int>> reference{ { 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 0 }, { 1, 1 },
+                                                 { 1, 2 }, { 2, 0 }, { 2, 1 }, { 2, 2 } };
+
+    ASSERT_EQ(exclusions.size(), 9);
+    for (std::size_t i = 0; i < exclusions.size(); ++i)
+    {
+        EXPECT_EQ(exclusions[i], reference[i]);
+    }
+}
+
+TEST(NBlibTest, CanConstructExclusionListFromIndices)
+{
+    WaterMoleculeBuilder waterMolecule;
+    Molecule             water = waterMolecule.waterMoleculeWithoutExclusions();
+
+    //! Add the exclusions
+    water.addExclusion(1, 0);
+    water.addExclusion(2, 0);
+    water.addExclusion(1, 2);
+
+    std::vector<std::tuple<int, int>> exclusions = water.getExclusions();
+
+    std::vector<std::tuple<int, int>> reference{ { 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 0 }, { 1, 1 },
+                                                 { 1, 2 }, { 2, 0 }, { 2, 1 }, { 2, 2 } };
+
+    ASSERT_EQ(exclusions.size(), 9);
+    for (std::size_t i = 0; i < exclusions.size(); ++i)
+    {
+        EXPECT_EQ(exclusions[i], reference[i]);
+    }
+}
+
+TEST(NBlibTest, CanConstructExclusionListFromNamesAndIndicesMixed)
+{
+    WaterMoleculeBuilder waterMolecule;
+    Molecule             water = waterMolecule.waterMoleculeWithoutExclusions();
+
+    //! Add the exclusions
+    water.addExclusion("H1", "Oxygen");
+    water.addExclusion("H2", "Oxygen");
+    water.addExclusion(1, 2);
+
+    std::vector<std::tuple<int, int>> exclusions = water.getExclusions();
+
+    std::vector<std::tuple<int, int>> reference{ { 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 0 }, { 1, 1 },
+                                                 { 1, 2 }, { 2, 0 }, { 2, 1 }, { 2, 2 } };
+
+    ASSERT_EQ(exclusions.size(), 9);
+    for (std::size_t i = 0; i < exclusions.size(); ++i)
+    {
+        EXPECT_EQ(exclusions[i], reference[i]);
+    }
+}
+
+TEST(NBlibTest, AtWorks)
+{
+    WaterMoleculeBuilder waterMolecule;
+    Molecule             water = waterMolecule.waterMolecule();
+    EXPECT_NO_THROW(water.at("Ow"));
+    EXPECT_NO_THROW(water.at("H"));
+}
+
+TEST(NBlibTest, AtThrows)
+{
+    WaterMoleculeBuilder waterMolecule;
+    Molecule             water = waterMolecule.waterMolecule();
+    EXPECT_THROW_GMX(water.at("Hw"), std::out_of_range);
+}
+
+TEST(NBlibTest, MoleculeThrowsSameParticleTypeNameDifferentMass)
+{
+    //! User error: Two different ParticleTypes with the same name
+    ParticleType atom1(ParticleTypeName("Atom"), Mass(1));
+    ParticleType atom2(ParticleTypeName("Atom"), Mass(2));
+
+    Molecule molecule(MoleculeName("UraniumDimer"));
+    EXPECT_NO_THROW(molecule.addParticle(ParticleName("U1"), atom1));
+    EXPECT_THROW(molecule.addParticle(ParticleName("U2"), atom2), InputException);
+}
+
+TEST(NBlibTest, MoleculeDontThrowsSameParticleTypeNameDifferentMass)
+{
+    //! User error: Two different ParticleTypes with the same name
+    ParticleType atom1(ParticleTypeName("Atom"), Mass(1));
+    ParticleType atom2(ParticleTypeName("Atom"), Mass(1));
+
+    Molecule molecule(MoleculeName("UraniumDimer"));
+    EXPECT_NO_THROW(molecule.addParticle(ParticleName("U1"), atom1));
+    EXPECT_NO_THROW(molecule.addParticle(ParticleName("U2"), atom2));
+}
+
+TEST(NBlibTest, MoleculeNoThrowsSameParticleTypeName)
+{
+    //! User error: Two different ParticleTypes with the same name
+    ParticleType atom1(ParticleTypeName("Atom"), Mass(1));
+    ParticleType atom2(ParticleTypeName("Atom"), Mass(1));
+
+    Molecule molecule(MoleculeName("UraniumDimer"));
+    EXPECT_NO_THROW(molecule.addParticle(ParticleName("U1"), atom1));
+    EXPECT_NO_THROW(molecule.addParticle(ParticleName("U2"), atom2));
+}
+
+} // namespace
+} // namespace test
+} // namespace nblib
diff --git a/api/nblib/tests/nbkernelsystem.cpp b/api/nblib/tests/nbkernelsystem.cpp
new file mode 100644 (file)
index 0000000..1d022f5
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements topology setup tests
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include <gtest/gtest.h>
+
+#include "gromacs/topology/exclusionblocks.h"
+#include "nblib/forcecalculator.h"
+#include "nblib/gmxsetup.h"
+#include "nblib/integrator.h"
+#include "nblib/tests/testhelpers.h"
+#include "nblib/tests/testsystems.h"
+#include "nblib/topology.h"
+
+namespace nblib
+{
+namespace test
+{
+namespace
+{
+
+// This is defined in src/gromacs/mdtypes/forcerec.h but there is also a
+// legacy C6 macro defined there that conflicts with the nblib C6 type.
+// Todo: Once that C6 has been refactored into a regular function, this
+//       file can just include forcerec.h
+//! Macro to set Van der Waals interactions to atoms
+#define SET_CGINFO_HAS_VDW(cgi) (cgi) = ((cgi) | (1 << 23))
+
+TEST(NBlibTest, SpcMethanolForcesAreCorrect)
+{
+    auto options        = NBKernelOptions();
+    options.nbnxmSimd   = SimdKernels::SimdNo;
+    options.coulombType = CoulombType::Cutoff;
+
+    SpcMethanolSimulationStateBuilder spcMethanolSystemBuilder;
+
+    auto simState        = spcMethanolSystemBuilder.setupSimulationState();
+    auto forceCalculator = ForceCalculator(simState, options);
+
+    gmx::ArrayRef<Vec3> forces(simState.forces());
+    ASSERT_NO_THROW(forceCalculator.compute(simState.coordinates(), forces));
+
+    Vector3DTest forcesOutputTest;
+    forcesOutputTest.testVectors(forces, "SPC-methanol forces");
+}
+
+TEST(NBlibTest, ExpectedNumberOfForces)
+{
+    auto options      = NBKernelOptions();
+    options.nbnxmSimd = SimdKernels::SimdNo;
+
+    SpcMethanolSimulationStateBuilder spcMethanolSystemBuilder;
+
+    auto simState        = spcMethanolSystemBuilder.setupSimulationState();
+    auto forceCalculator = ForceCalculator(simState, options);
+
+    gmx::ArrayRef<Vec3> forces(simState.forces());
+    forceCalculator.compute(simState.coordinates(), forces);
+    EXPECT_EQ(simState.topology().numParticles(), forces.size());
+}
+
+TEST(NBlibTest, CanIntegrateSystem)
+{
+    auto options          = NBKernelOptions();
+    options.nbnxmSimd     = SimdKernels::SimdNo;
+    options.numIterations = 1;
+
+    SpcMethanolSimulationStateBuilder spcMethanolSystemBuilder;
+
+    auto simState        = spcMethanolSystemBuilder.setupSimulationState();
+    auto forceCalculator = ForceCalculator(simState, options);
+
+    LeapFrog integrator(simState.topology(), simState.box());
+
+    for (int iter = 0; iter < options.numIterations; iter++)
+    {
+        gmx::ArrayRef<Vec3> forces(simState.forces());
+        forceCalculator.compute(simState.coordinates(), simState.forces());
+        EXPECT_NO_THROW(integrator.integrate(1.0, simState.coordinates(), simState.velocities(),
+                                             simState.forces()));
+    }
+}
+
+/*!
+ * Check if the following aspects of the ForceCalculator and
+ * LeapFrog (integrator) work as expected:
+ *
+ * 1. Calling the ForceCalculator::compute() function makes no change
+ *    to the internal representation of the system. Calling it repeatedly
+ *    without update should return the same vector of forces.
+ *
+ * 2. Once the LeapFrog objects integrates for the given time using the
+ *    forces, there the coordinates in SimulationState must change.
+ *    Calling the compute() function must now generate a new set of forces.
+ *
+ */
+TEST(NBlibTest, UpdateChangesForces)
+{
+    auto options          = NBKernelOptions();
+    options.nbnxmSimd     = SimdKernels::SimdNo;
+    options.numIterations = 1;
+
+    SpcMethanolSimulationStateBuilder spcMethanolSystemBuilder;
+
+    auto simState        = spcMethanolSystemBuilder.setupSimulationState();
+    auto forceCalculator = ForceCalculator(simState, options);
+
+    LeapFrog integrator(simState.topology(), simState.box());
+
+    // step 1
+    gmx::ArrayRef<Vec3> forces(simState.forces());
+    forceCalculator.compute(simState.coordinates(), simState.forces());
+
+    std::vector<Vec3> forces_1(forces.size());
+    std::copy(forces.begin(), forces.end(), begin(forces_1));
+
+    // check if forces change without update step
+    forceCalculator.compute(simState.coordinates(), forces);
+
+    // check if forces change without update
+    for (size_t i = 0; i < forces_1.size(); i++)
+    {
+        for (int j = 0; j < dimSize; j++)
+        {
+            EXPECT_EQ(forces[i][j], forces_1[i][j]);
+        }
+    }
+
+    // update
+    integrator.integrate(1.0, simState.coordinates(), simState.velocities(), simState.forces());
+
+    // step 2
+    forceCalculator.compute(simState.coordinates(), simState.forces());
+    std::vector<Vec3> forces_2(forces.size());
+    std::copy(forces.begin(), forces.end(), begin(forces_2));
+
+    // check if forces change after update
+    for (size_t i = 0; i < forces_1.size(); i++)
+    {
+        for (int j = 0; j < dimSize; j++)
+        {
+            EXPECT_NE(forces_1[i][j], forces_2[i][j]);
+        }
+    }
+}
+
+TEST(NBlibTest, ArgonForcesAreCorrect)
+{
+    auto options        = NBKernelOptions();
+    options.nbnxmSimd   = SimdKernels::SimdNo;
+    options.coulombType = CoulombType::Cutoff;
+
+    ArgonSimulationStateBuilder argonSystemBuilder;
+
+    auto simState        = argonSystemBuilder.setupSimulationState();
+    auto forceCalculator = ForceCalculator(simState, options);
+
+    gmx::ArrayRef<Vec3> testForces(simState.forces());
+    forceCalculator.compute(simState.coordinates(), simState.forces());
+
+    Vector3DTest forcesOutputTest;
+    forcesOutputTest.testVectors(testForces, "Argon forces");
+}
+
+} // namespace
+} // namespace test
+} // namespace nblib
similarity index 69%
rename from src/gromacs/gpu_utils/tests/gputest.cpp
rename to api/nblib/tests/nbnxnsetup.cpp
index 2012d97da3b8bb56c3ba1f0e18154cb1eadf8bb0..ada24d504458297f9991b022fd5e09a9f04135db 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  */
 /*! \internal \file
  * \brief
- * Tests utilities for GPU device allocation and free.
+ * This implements nbnxn setup tests
  *
- * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
  */
-#include "gmxpre.h"
+#include "nblib/gmxsetup.h"
+#include "nblib/tests/testhelpers.h"
+#include "nblib/tests/testsystems.h"
 
-#include "gputest.h"
+#include "testutils/testasserts.h"
 
-#include <gtest/gtest.h>
-
-#include "gromacs/gpu_utils/gpu_utils.h"
-#include "gromacs/hardware/gpu_hw_info.h"
-#include "gromacs/utility/smalloc.h"
-
-namespace gmx
-{
-namespace test
-{
-
-GpuTest::GpuTest()
+namespace nblib
 {
-    snew(gpuInfo_, 1);
-    if (isGpuDetectionFunctional(nullptr))
-    {
-        findGpus(gpuInfo_);
-    }
-    // Failing to find valid GPUs does not require further action
-}
 
-GpuTest::~GpuTest()
+namespace test
 {
-    free_gpu_info(gpuInfo_);
-    sfree(gpuInfo_);
-}
 
-bool GpuTest::haveValidGpus() const
+TEST(NBlibTest, CanConstructNbvSetupUtil)
 {
-    return gpuInfo_->n_dev_compatible > 0;
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    SimulationState             system = argonSimulationStateBuilder.setupSimulationState();
+    EXPECT_NO_THROW(NbvSetupUtil());
 }
 
 } // namespace test
-} // namespace gmx
+
+} // namespace nblib
diff --git a/api/nblib/tests/particletype.cpp b/api/nblib/tests/particletype.cpp
new file mode 100644 (file)
index 0000000..f9eaa39
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements basic nblib AtomType tests
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ */
+#include <cmath>
+
+#include "nblib/particletype.h"
+#include "nblib/tests/testsystems.h"
+
+#include "testutils/testasserts.h"
+
+namespace nblib
+{
+
+TEST(NBlibTest, ParticleTypeNameCanBeConstructed)
+{
+    ArAtom       arAtom;
+    ParticleType argonAtom(arAtom.particleTypeName, arAtom.mass);
+    EXPECT_EQ(ParticleTypeName(argonAtom.name()), arAtom.particleTypeName);
+}
+
+TEST(NBlibTest, ParticleTypeMassCanBeConstructed)
+{
+    ArAtom       arAtom;
+    ParticleType argonAtom(arAtom.particleTypeName, arAtom.mass);
+    EXPECT_EQ(argonAtom.mass(), arAtom.mass);
+}
+
+} // namespace nblib
similarity index 76%
rename from src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_80.xml
rename to api/nblib/tests/refdata/NBlibTest_ArgonForcesAreCorrect.xml
index b008c5693fb115cef92823f396da5567642c503c..ddaa587c238d4b2f892ff2dcdf69d6cb51bc548f 100644 (file)
@@ -1,12 +1,12 @@
 <?xml version="1.0"?>
 <?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
 <ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
+  <Sequence Name="Argon forces">
+    <Int Name="Length">12</Int>
     <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
+      <Real Name="X">-0.41298821868424429</Real>
+      <Real Name="Y">-1.0982427445010643</Real>
+      <Real Name="Z">-0.11318936363938557</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
@@ -14,9 +14,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
+      <Real Name="X">0.41298821868424429</Real>
+      <Real Name="Y">1.0982427445010643</Real>
+      <Real Name="Z">0.11318936363938557</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Y">0</Real>
       <Real Name="Z">0</Real>
     </Vector>
-    <Vector>
-      <Real Name="X">-4.0944609423338374</Real>
-      <Real Name="Y">-7.9446698242436193</Real>
-      <Real Name="Z">-1.3877955568392644</Real>
-    </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Y">0</Real>
diff --git a/api/nblib/tests/refdata/NBlibTest_SpcMethanolForcesAreCorrect.xml b/api/nblib/tests/refdata/NBlibTest_SpcMethanolForcesAreCorrect.xml
new file mode 100644 (file)
index 0000000..9f812d2
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="SPC-methanol forces">
+    <Int Name="Length">6</Int>
+    <Vector>
+      <Real Name="X">-0.38183025207307253</Real>
+      <Real Name="Y">0.87922789140299651</Real>
+      <Real Name="Z">-6.1406494302583212</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.3033346966856811</Real>
+      <Real Name="Y">-7.3388265216240605</Real>
+      <Real Name="Z">27.783758884487042</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.4221671563754512</Real>
+      <Real Name="Y">6.2920799125341684</Real>
+      <Real Name="Z">-33.986135630631082</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">27.494322535207331</Real>
+      <Real Name="Y">8.3914914141170893</Real>
+      <Real Name="Z">39.693768750211909</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.009852871290562</Real>
+      <Real Name="Y">-5.3975006967248795</Real>
+      <Real Name="Z">-15.545639878395072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.983806952153925</Real>
+      <Real Name="Y">-2.8264719997053138</Real>
+      <Real Name="Z">-11.805102695414478</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/api/nblib/tests/simstate.cpp b/api/nblib/tests/simstate.cpp
new file mode 100644 (file)
index 0000000..01a0bf5
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements SimulationState tests
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include <vector>
+
+#include "nblib/box.h"
+#include "nblib/exception.h"
+#include "nblib/simulationstate.h"
+#include "nblib/simulationstateimpl.h"
+#include "nblib/tests/testhelpers.h"
+#include "nblib/tests/testsystems.h"
+#include "nblib/topology.h"
+
+#include "testutils/testasserts.h"
+
+namespace nblib
+{
+namespace test
+{
+namespace
+{
+
+//! Utility function to compare 2 std::vectors of gmx::RVec used to compare cartesians
+void compareValues(const std::vector<Vec3>& ref, const std::vector<Vec3>& test)
+{
+    for (size_t i = 0; i < ref.size(); i++)
+    {
+        for (int j = 0; j < dimSize; j++)
+        {
+            EXPECT_EQ(ref[i][j], test.at(i)[j]);
+        }
+    }
+}
+
+TEST(NBlibTest, CanConstructSimulationState)
+{
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    EXPECT_NO_THROW(argonSimulationStateBuilder.setupSimulationState());
+}
+
+TEST(NBlibTest, SimulationStateThrowsCoordinateNAN)
+{
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    argonSimulationStateBuilder.setCoordinate(2, 0, NAN);
+    EXPECT_THROW(argonSimulationStateBuilder.setupSimulationState(), InputException);
+}
+
+TEST(NBlibTest, SimulationStateThrowsCoordinateINF)
+{
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    argonSimulationStateBuilder.setCoordinate(2, 0, INFINITY);
+    EXPECT_THROW(argonSimulationStateBuilder.setupSimulationState(), InputException);
+}
+
+TEST(NBlibTest, SimulationStateThrowsVelocityNAN)
+{
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    argonSimulationStateBuilder.setVelocity(2, 0, NAN);
+    EXPECT_THROW(argonSimulationStateBuilder.setupSimulationState(), InputException);
+}
+
+TEST(NBlibTest, SimulationStateThrowsVelocityINF)
+{
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    argonSimulationStateBuilder.setVelocity(2, 0, INFINITY);
+    EXPECT_THROW(argonSimulationStateBuilder.setupSimulationState(), InputException);
+}
+
+TEST(NBlibTest, SimulationStateCanMove)
+{
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    SimulationState             simState = argonSimulationStateBuilder.setupSimulationState();
+    EXPECT_NO_THROW(SimulationState movedSimState = std::move(simState));
+}
+
+TEST(NBlibTest, SimulationStateCanAssign)
+{
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    SimulationState             simState = argonSimulationStateBuilder.setupSimulationState();
+    EXPECT_NO_THROW(const SimulationState& gmx_unused AssignedSimState = simState);
+}
+
+TEST(NBlibTest, SimulationStateHasBox)
+{
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    SimulationState             simState = argonSimulationStateBuilder.setupSimulationState();
+    const Box&                  testBox  = simState.box();
+    const Box&                  refBox   = argonSimulationStateBuilder.box();
+    // GTEST does not like the comparison operator in a different namespace
+    const bool compare = (refBox == testBox);
+    EXPECT_TRUE(compare);
+}
+
+TEST(NBlibTest, SimulationStateHasCorrectCoordinates)
+{
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    SimulationState             simState = argonSimulationStateBuilder.setupSimulationState();
+    std::vector<Vec3>           test     = simState.coordinates();
+    std::vector<Vec3>           ref      = argonSimulationStateBuilder.coordinates();
+    compareValues(ref, test);
+}
+
+TEST(NBlibTest, SimulationStateHasCorrectVelocities)
+{
+    ArgonSimulationStateBuilder argonSimulationStateBuilder;
+    SimulationState             simState = argonSimulationStateBuilder.setupSimulationState();
+    std::vector<Vec3>           test     = simState.velocities();
+    std::vector<Vec3>           ref      = argonSimulationStateBuilder.velocities();
+    compareValues(ref, test);
+}
+
+} // namespace
+} // namespace test
+} // namespace nblib
diff --git a/api/nblib/tests/testhelpers.cpp b/api/nblib/tests/testhelpers.cpp
new file mode 100644 (file)
index 0000000..0884a06
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements basic nblib test systems
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include "nblib/tests/testhelpers.h"
+
+namespace nblib
+{
+namespace test
+{
+
+//! Compare between two instances of the Box object
+bool operator==(const Box& a, const Box& b)
+{
+    if (a.legacyMatrix()[XX][XX] != b.legacyMatrix()[XX][XX])
+    {
+        return false;
+    }
+    if (a.legacyMatrix()[YY][YY] != b.legacyMatrix()[YY][YY])
+    {
+        return false;
+    }
+    if (a.legacyMatrix()[ZZ][ZZ] != b.legacyMatrix()[ZZ][ZZ])
+    {
+        return false;
+    }
+    return true;
+}
+
+} // namespace test
+} // namespace nblib
diff --git a/api/nblib/tests/testhelpers.h b/api/nblib/tests/testhelpers.h
new file mode 100644 (file)
index 0000000..8f84602
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * This implements basic nblib test systems
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#ifndef NBLIB_TESTS_TESTHELPERS_H
+#define NBLIB_TESTS_TESTHELPERS_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "nblib/box.h"
+#include "nblib/vector.h"
+
+#include "testutils/refdata.h"
+#include "testutils/testasserts.h"
+
+namespace nblib
+{
+
+namespace test
+{
+
+//! Compare between two instances of the Box object
+bool operator==(const Box& a, const Box& b);
+
+/*! \internal \brief
+ *  Simple test harness for checking 3D vectors like coordinates, velocities,
+ *  forces against reference data
+ *
+ */
+class Vector3DTest
+{
+public:
+    Vector3DTest() : checker_(refData_.rootChecker())
+    {
+        gmx::test::FloatingPointTolerance tolerance(
+                gmx::test::FloatingPointTolerance(1e-8, 1.0e-12, 1e-8, 1.0e-12, 200, 100, true));
+        checker_.setDefaultTolerance(tolerance);
+    }
+
+    //! Compare a given input vector of cartesians with the reference data
+    void testVectors(gmx::ArrayRef<Vec3> forces, const std::string& testString)
+    {
+        checker_.checkSequence(forces.begin(), forces.end(), testString.c_str());
+    }
+
+private:
+    gmx::test::TestReferenceData    refData_;
+    gmx::test::TestReferenceChecker checker_;
+};
+
+//! Macros to compare floats and doubles with a specified tolerance
+/// \cond DO_NOT_DOCUMENT
+#if GMX_DOUBLE
+#    define EXPECT_FLOAT_DOUBLE_EQ_TOL(value, refFloat, refDouble, tolerance) \
+        EXPECT_DOUBLE_EQ_TOL(value, refDouble, tolerance)
+#    define ASSERT_FLOAT_DOUBLE_EQ_TOL(value, refFloat, refDouble, tolerance) \
+        ASSERT_DOUBLE_EQ_TOL(value, refDouble, tolerance)
+#else
+#    define EXPECT_FLOAT_DOUBLE_EQ_TOL(value, refFloat, refDouble, tolerance) \
+        EXPECT_FLOAT_EQ_TOL(value, refFloat, tolerance)
+#    define ASSERT_FLOAT_DOUBLE_EQ_TOL(value, refFloat, refDouble, tolerance) \
+        ASSERT_FLOAT_EQ_TOL(value, refFloat, tolerance)
+#endif
+/// \endcond
+
+} // namespace test
+} // namespace nblib
+#endif // NBLIB_TESTS_TESTHELPERS_H
diff --git a/api/nblib/tests/testsystems.cpp b/api/nblib/tests/testsystems.cpp
new file mode 100644 (file)
index 0000000..70e86dd
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements nblib test systems
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include "nblib/tests/testsystems.h"
+#include "nblib/exception.h"
+
+namespace nblib
+{
+
+//! User class to hold ParticleTypes
+//! Note: this is not part of NBLIB, users should write their own
+class ParticleLibrary
+{
+public:
+    ParticleLibrary()
+    {
+        ParticleType Ow(ParticleTypeName("Ow"), Mass(15.99940));
+        ParticleType H(ParticleTypeName("H"), Mass(1.008));
+        ParticleType OMet(ParticleTypeName("OMet"), Mass(15.999));
+        ParticleType CMet(ParticleTypeName("CMet"), Mass(15.035));
+        ParticleType Ar(ParticleTypeName("Ar"), Mass(39.94800));
+
+        particles_.insert(std::make_pair(Ow.name(), Ow));
+        particles_.insert(std::make_pair(H.name(), H));
+        particles_.insert(std::make_pair(OMet.name(), OMet));
+        particles_.insert(std::make_pair(CMet.name(), CMet));
+        particles_.insert(std::make_pair(Ar.name(), Ar));
+
+        c6_[Ow.name()]   = 0.0026173456;
+        c6_[H.name()]    = 0;
+        c6_[OMet.name()] = 0.0022619536;
+        c6_[CMet.name()] = 0.0088755241;
+        c6_[Ar.name()]   = 0.0062647225;
+
+        c12_[Ow.name()]   = 2.634129e-06;
+        c12_[H.name()]    = 0;
+        c12_[OMet.name()] = 1.505529e-06;
+        c12_[CMet.name()] = 2.0852922e-05;
+        c12_[Ar.name()]   = 9.847044e-06;
+    }
+
+    //! Get particle type using the string identifier
+    [[nodiscard]] ParticleType type(const std::string& particleTypeName) const
+    {
+        return particles_.at(ParticleTypeName(particleTypeName));
+    }
+
+    //! Get C6 parameter of a given particle
+    [[nodiscard]] C6 c6(const ParticleName& particleName) const
+    {
+        return c6_.at(ParticleTypeName(particleName.value()));
+    }
+
+    //! Get C12 parameter of a given particle
+    [[nodiscard]] C12 c12(const ParticleName& particleName) const
+    {
+        return c12_.at(ParticleTypeName(particleName.value()));
+    }
+
+private:
+    std::map<ParticleTypeName, ParticleType> particles_;
+    std::map<ParticleTypeName, C6>           c6_;
+    std::map<ParticleTypeName, C12>          c12_;
+};
+
+std::unordered_map<std::string, Charge> Charges{ { "Ow", Charge(-0.82) },
+                                                 { "Hw", Charge(+0.41) },
+                                                 { "OMet", Charge(-0.574) },
+                                                 { "CMet", Charge(+0.176) },
+                                                 { "HMet", Charge(+0.398) } };
+
+WaterMoleculeBuilder::WaterMoleculeBuilder() : water_(MoleculeName("SOL"))
+{
+    ParticleLibrary plib;
+
+    //! Add the particles
+    water_.addParticle(ParticleName("Oxygen"), Charges.at("Ow"), plib.type("Ow"));
+    water_.addParticle(ParticleName("H1"), Charges.at("Hw"), plib.type("H"));
+    water_.addParticle(ParticleName("H2"), Charges.at("Hw"), plib.type("H"));
+}
+
+Molecule WaterMoleculeBuilder::waterMolecule()
+{
+    addExclusionsFromNames();
+    return water_;
+}
+
+Molecule WaterMoleculeBuilder::waterMoleculeWithoutExclusions()
+{
+    return water_;
+}
+
+void WaterMoleculeBuilder::addExclusionsFromNames()
+{
+    water_.addExclusion("H1", "Oxygen");
+    water_.addExclusion("H2", "Oxygen");
+    water_.addExclusion("H1", "H2");
+}
+
+MethanolMoleculeBuilder::MethanolMoleculeBuilder() : methanol_(MoleculeName("MeOH"))
+{
+    ParticleLibrary library;
+
+    //! Add the particles
+    methanol_.addParticle(ParticleName("Me1"), Charges.at("CMet"), library.type("CMet"));
+    methanol_.addParticle(ParticleName("O2"), Charges.at("OMet"), library.type("OMet"));
+    methanol_.addParticle(ParticleName("H3"), Charges.at("HMet"), library.type("H"));
+
+    // Add the exclusions
+    methanol_.addExclusion("Me1", "O2");
+    methanol_.addExclusion("Me1", "H3");
+    methanol_.addExclusion("H3", "O2");
+}
+
+Molecule MethanolMoleculeBuilder::methanolMolecule()
+{
+    return methanol_;
+}
+
+
+Topology WaterTopologyBuilder::buildTopology(int numMolecules)
+{
+    ParticleLibrary library;
+
+    ParticleTypesInteractions interactions;
+    std::vector<std::string>  typeNames = { "Ow", "H" };
+    for (const auto& name : typeNames)
+    {
+        interactions.add(ParticleTypeName(name), library.c6(ParticleName(name)),
+                         library.c12(ParticleName(name)));
+    }
+
+    // Add some molecules to the topology
+    TopologyBuilder topologyBuilder;
+    topologyBuilder.addMolecule(water(), numMolecules);
+
+    // Add non-bonded interaction information
+    topologyBuilder.addParticleTypesInteractions(interactions);
+
+    Topology topology = topologyBuilder.buildTopology();
+    return topology;
+}
+
+Molecule WaterTopologyBuilder::water()
+{
+    return waterMolecule_.waterMolecule();
+}
+
+Topology SpcMethanolTopologyBuilder::buildTopology(int numWater, int numMethanol)
+{
+    ParticleLibrary library;
+
+    ParticleTypesInteractions interactions;
+    std::vector<std::string>  typeNames = { "Ow", "H", "OMet", "CMet" };
+    for (const auto& name : typeNames)
+    {
+        interactions.add(ParticleTypeName(name), library.c6(ParticleName(name)),
+                         library.c12(ParticleName(name)));
+    }
+
+    // Add some molecules to the topology
+    TopologyBuilder topologyBuilder;
+    topologyBuilder.addMolecule(methanol(), numMethanol);
+    topologyBuilder.addMolecule(water(), numWater);
+
+    // Add non-bonded interaction information
+    topologyBuilder.addParticleTypesInteractions(interactions);
+
+    Topology topology = topologyBuilder.buildTopology();
+    return topology;
+}
+
+Molecule SpcMethanolTopologyBuilder::methanol()
+{
+    return methanolMolecule_.methanolMolecule();
+}
+
+Molecule SpcMethanolTopologyBuilder::water()
+{
+    return waterMolecule_.waterMolecule();
+}
+
+ArgonTopologyBuilder::ArgonTopologyBuilder(const int& numParticles)
+{
+    ParticleLibrary library;
+
+    ParticleTypesInteractions nbinteractions;
+    nbinteractions.add(ParticleTypeName("Ar"), library.c6(ParticleName("Ar")),
+                       library.c12(ParticleName("Ar")));
+
+    Molecule argonMolecule(MoleculeName("AR"));
+    argonMolecule.addParticle(ParticleName("AR"), library.type("Ar"));
+
+    topologyBuilder_.addMolecule(argonMolecule, numParticles);
+    topologyBuilder_.addParticleTypesInteractions((nbinteractions));
+}
+
+Topology ArgonTopologyBuilder::argonTopology()
+{
+    return topologyBuilder_.buildTopology();
+}
+
+ArgonSimulationStateBuilder::ArgonSimulationStateBuilder() :
+    box_(6.05449),
+    topology_(ArgonTopologyBuilder(12).argonTopology())
+{
+
+    coordinates_ = {
+        { 0.794, 1.439, 0.610 }, { 1.397, 0.673, 1.916 }, { 0.659, 1.080, 0.573 },
+        { 1.105, 0.090, 3.431 }, { 1.741, 1.291, 3.432 }, { 1.936, 1.441, 5.873 },
+        { 0.960, 2.246, 1.659 }, { 0.382, 3.023, 2.793 }, { 0.053, 4.857, 4.242 },
+        { 2.655, 5.057, 2.211 }, { 4.114, 0.737, 0.614 }, { 5.977, 5.104, 5.217 },
+    };
+
+    velocities_ = {
+        { 0.0055, -0.1400, 0.2127 },   { 0.0930, -0.0160, -0.0086 }, { 0.1678, 0.2476, -0.0660 },
+        { 0.1591, -0.0934, -0.0835 },  { -0.0317, 0.0573, 0.1453 },  { 0.0597, 0.0013, -0.0462 },
+        { 0.0484, -0.0357, 0.0168 },   { 0.0530, 0.0295, -0.2694 },  { -0.0550, -0.0896, 0.0494 },
+        { -0.0799, -0.2534, -0.0079 }, { 0.0436, -0.1557, 0.1849 },  { -0.0214, 0.0446, 0.0758 },
+    };
+    forces_ = {
+        { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+        { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+        { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+        { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+    };
+}
+
+void ArgonSimulationStateBuilder::setCoordinate(int particleNum, int dimension, real value)
+{
+    if (dimension < 0 or dimension > 2)
+    {
+        throw InputException("Must provide a valid dimension\n");
+    }
+    coordinates_.at(particleNum)[dimension] = value;
+}
+
+void ArgonSimulationStateBuilder::setVelocity(int particleNum, int dimension, real value)
+{
+    if (dimension < 0 or dimension > 2)
+    {
+        throw InputException("Must provide a valid dimension\n");
+    }
+    velocities_.at(particleNum)[dimension] = value;
+}
+
+SimulationState ArgonSimulationStateBuilder::setupSimulationState()
+{
+    return SimulationState(coordinates_, velocities_, forces_, box_, topology_);
+}
+
+const Topology& ArgonSimulationStateBuilder::topology() const
+{
+    return topology_;
+}
+
+Box& ArgonSimulationStateBuilder::box()
+{
+    return box_;
+}
+
+std::vector<Vec3>& ArgonSimulationStateBuilder::coordinates()
+{
+    return coordinates_;
+}
+
+std::vector<Vec3>& ArgonSimulationStateBuilder::velocities()
+{
+    return velocities_;
+}
+
+SpcMethanolSimulationStateBuilder::SpcMethanolSimulationStateBuilder() :
+    box_(3.01000),
+    topology_(SpcMethanolTopologyBuilder().buildTopology(1, 1))
+{
+    coordinates_ = {
+        { 1.970, 1.460, 1.209 }, // Me1
+        { 1.978, 1.415, 1.082 }, // O2
+        { 1.905, 1.460, 1.030 }, // H3
+        { 1.555, 1.511, 0.703 }, // Ow
+        { 1.498, 1.495, 0.784 }, // Hw1
+        { 1.496, 1.521, 0.623 }, // Hw2
+    };
+
+    velocities_ = {
+        { -0.8587, -0.1344, -0.0643 }, { 0.0623, -0.1787, 0.0036 }, { -0.5020, -0.9564, 0.0997 },
+        { 0.869, 1.245, 1.665 },       { 0.169, 0.275, 1.565 },     { 0.269, 2.275, 1.465 },
+    };
+
+    forces_ = {
+        { 0.000, 0.000, 0.000 }, { 0.000, 0.000, 0.000 }, { 0.000, 0.000, 0.000 },
+        { 0.000, 0.000, 0.000 }, { 0.000, 0.000, 0.000 }, { 0.000, 0.000, 0.000 },
+    };
+}
+
+SimulationState SpcMethanolSimulationStateBuilder::setupSimulationState()
+{
+    return SimulationState(coordinates_, velocities_, forces_, box_, topology_);
+}
+
+std::vector<Vec3>& SpcMethanolSimulationStateBuilder::coordinates()
+{
+    return coordinates_;
+}
+
+std::vector<Vec3>& SpcMethanolSimulationStateBuilder::velocities()
+{
+    return velocities_;
+}
+
+} // namespace nblib
diff --git a/api/nblib/tests/testsystems.h b/api/nblib/tests/testsystems.h
new file mode 100644 (file)
index 0000000..f4d4dac
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements basic nblib test systems
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef NBLIB_TESTSYSTEMS_H
+#define NBLIB_TESTSYSTEMS_H
+
+#include <cmath>
+
+#include "nblib/box.h"
+#include "nblib/molecules.h"
+#include "nblib/particletype.h"
+#include "nblib/simulationstate.h"
+#include "nblib/topology.h"
+
+namespace nblib
+{
+
+//! \internal \brief Parameters from gromos43A1
+struct ArAtom
+{
+    //! Argon Particle Name
+    ParticleName particleName = ParticleName("Ar");
+    //! Argon particle type name
+    ParticleTypeName particleTypeName = ParticleTypeName("Ar");
+    //! Argon molecule name
+    MoleculeName moleculeName = MoleculeName("Ar");
+    //! Argon Particle Mass
+    Mass mass = Mass(39.94800);
+    //! Argon C6 parameter
+    C6 c6{ 0.0062647225 };
+    //! Argon C12 parameter
+    C12 c12{ 9.847044e-06 };
+};
+
+//! Lookup table for charges needed for building topologies
+extern std::unordered_map<std::string, Charge> Charges;
+
+//! \internal \brief Make an SPC water molecule with parameters from gromos43A1
+class WaterMoleculeBuilder
+{
+public:
+    // There is no default ctor for a Molecule so it must be initialized
+    WaterMoleculeBuilder();
+
+    //! Return the initialized water Molecule, with exclusions
+    Molecule waterMolecule();
+
+    //! Return the initialized water Molecule, without exclusions
+    Molecule waterMoleculeWithoutExclusions();
+
+private:
+    //! The molecule
+    Molecule water_;
+
+    //! Add the exclusions from particle names. Private to prevent multiple calls
+    void addExclusionsFromNames();
+};
+
+//! \internal \brief Make a methanol molecule with parameters from gromos43A1
+class MethanolMoleculeBuilder
+{
+public:
+    // There is no default ctor for a Molecule so it must be initialized
+    MethanolMoleculeBuilder();
+
+    //! Return the initialized water Molecule, with exclusions
+    Molecule methanolMolecule();
+
+private:
+    //! The molecule
+    Molecule methanol_;
+};
+
+//! \internal \brief Build topology of water molecules of a specified number
+class WaterTopologyBuilder
+{
+public:
+    //! Return a topology with specified SPC water molecules
+    Topology buildTopology(int numMolecules);
+
+    //! Return the actual water Molecule used in the topology
+    Molecule water();
+
+private:
+    WaterMoleculeBuilder waterMolecule_;
+};
+
+//! \internal \brief Build topology of methanol+water molecules from specified numbers
+class SpcMethanolTopologyBuilder
+{
+public:
+    //! Return a topology with specified methanol molecules
+    Topology buildTopology(int numWater, int numMethanol);
+
+    //! Return the actual methanol Molecule used in the topology
+    Molecule methanol();
+
+    //! Return the actual water Molecule used in the topology
+    Molecule water();
+
+private:
+    MethanolMoleculeBuilder methanolMolecule_;
+    WaterMoleculeBuilder    waterMolecule_;
+};
+
+//! \internal \brief Build topology of argon molecules of a specified number
+class ArgonTopologyBuilder
+{
+public:
+    //! Build a topology with specified argon molecules
+    ArgonTopologyBuilder(const int& numParticles);
+
+    //! Get the topology with specified argon molecules
+    Topology argonTopology();
+
+private:
+    TopologyBuilder topologyBuilder_;
+};
+
+//! \internal \brief Build simulation state for the argon example
+class ArgonSimulationStateBuilder
+{
+public:
+    ArgonSimulationStateBuilder();
+
+    //! Set coordinates of particles in the defined system
+    void setCoordinate(int particleNum, int dimension, real value);
+
+    //! Set particle velocities
+    void setVelocity(int particleNum, int dimension, real value);
+
+    //! Setup simulation state
+    SimulationState setupSimulationState();
+
+    //! Get the topology
+    const Topology& topology() const;
+
+    //! Get the box bounding the system
+    Box& box();
+
+    //! Get current coordinates
+    std::vector<Vec3>& coordinates();
+
+    //! Get current velocities
+    std::vector<Vec3>& velocities();
+
+private:
+    std::vector<Vec3> coordinates_;
+    std::vector<Vec3> velocities_;
+    std::vector<Vec3> forces_;
+
+    Box      box_;
+    Topology topology_;
+};
+
+//! \internal \brief Build simulation state for the SPC-Methanol example
+class SpcMethanolSimulationStateBuilder
+{
+public:
+    SpcMethanolSimulationStateBuilder();
+
+    //! Setup simulation state
+    SimulationState setupSimulationState();
+
+    //! Get current coordinates
+    std::vector<Vec3>& coordinates();
+
+    //! Get current velocities
+    std::vector<Vec3>& velocities();
+
+private:
+    std::vector<Vec3> coordinates_;
+    std::vector<Vec3> velocities_;
+    std::vector<Vec3> forces_;
+
+    Box      box_;
+    Topology topology_;
+};
+
+} // namespace nblib
+#endif // NBLIB_TESTSYSTEMS_H
diff --git a/api/nblib/tests/topology.cpp b/api/nblib/tests/topology.cpp
new file mode 100644 (file)
index 0000000..63c326a
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements topology setup tests
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "gromacs/topology/exclusionblocks.h"
+#include "nblib/exception.h"
+#include "nblib/particletype.h"
+#include "nblib/tests/testsystems.h"
+#include "nblib/topology.h"
+
+namespace nblib
+{
+namespace test
+{
+namespace
+{
+
+using ::testing::Eq;
+using ::testing::Pointwise;
+
+//! Compares all element between two lists of lists
+//! Todo: unify this with the identical function in nbkernelsystem test make this a method
+//!       of ListOfLists<>
+template<typename T>
+void compareLists(const gmx::ListOfLists<T>& list, const std::vector<std::vector<T>>& v)
+{
+    ASSERT_EQ(list.size(), v.size());
+    for (std::size_t i = 0; i < list.size(); i++)
+    {
+        ASSERT_EQ(list[i].size(), v[i].size());
+        EXPECT_THAT(list[i], Pointwise(Eq(), v[i]));
+    }
+}
+
+// This is defined in src/gromacs/mdtypes/forcerec.h but there is also a
+// legacy C6 macro defined there that conflicts with the nblib C6 type.
+// Todo: Once that C6 has been refactored into a regular function, this
+//       file can just include forcerec.h
+//! Macro to marks particles to have Van der Waals interactions
+#define SET_CGINFO_HAS_VDW(cgi) (cgi) = ((cgi) | (1 << 23))
+
+TEST(NBlibTest, TopologyHasNumParticles)
+{
+    WaterTopologyBuilder waters;
+    Topology             watersTopology = waters.buildTopology(2);
+    const int            test           = watersTopology.numParticles();
+    const int            ref            = 6;
+    EXPECT_EQ(ref, test);
+}
+
+TEST(NBlibTest, TopologyHasCharges)
+{
+    WaterTopologyBuilder     waters;
+    Topology                 watersTopology = waters.buildTopology(2);
+    const std::vector<real>& test           = watersTopology.getCharges();
+    const std::vector<real>& ref = { Charges.at("Ow"), Charges.at("Hw"), Charges.at("Hw"),
+                                     Charges.at("Ow"), Charges.at("Hw"), Charges.at("Hw") };
+    EXPECT_EQ(ref, test);
+}
+
+TEST(NBlibTest, TopologyHasMasses)
+{
+    WaterTopologyBuilder waters;
+    Topology             watersTopology = waters.buildTopology(2);
+
+    const Mass              refOwMass = waters.water().at("Ow").mass();
+    const Mass              refHwMass = waters.water().at("H").mass();
+    const std::vector<Mass> ref = { refOwMass, refHwMass, refHwMass, refOwMass, refHwMass, refHwMass };
+    const std::vector<Mass> test = expandQuantity(watersTopology, &ParticleType::mass);
+    EXPECT_EQ(ref, test);
+}
+
+TEST(NBlibTest, TopologyHasParticleTypes)
+{
+    WaterTopologyBuilder             waters;
+    Topology                         watersTopology = waters.buildTopology(2);
+    const std::vector<ParticleType>& test           = watersTopology.getParticleTypes();
+    const ParticleType               refOw          = waters.water().at("Ow");
+    const ParticleType               refHw          = waters.water().at("H");
+    const std::vector<ParticleType>& ref            = { refOw, refHw };
+    const std::vector<ParticleType>& ref2           = { refHw, refOw };
+    EXPECT_TRUE(ref == test || ref2 == test);
+}
+
+TEST(NBlibTest, TopologyHasParticleTypeIds)
+{
+    WaterTopologyBuilder waters;
+    Topology             watersTopology = waters.buildTopology(2);
+
+    const std::vector<int>&          testIds   = watersTopology.getParticleTypeIdOfAllParticles();
+    const std::vector<ParticleType>& testTypes = watersTopology.getParticleTypes();
+
+    std::vector<ParticleType> testTypesExpanded;
+    testTypesExpanded.reserve(testTypes.size());
+    for (int i : testIds)
+    {
+        testTypesExpanded.push_back(testTypes[i]);
+    }
+
+    const ParticleType              refOw = waters.water().at("Ow");
+    const ParticleType              refHw = waters.water().at("H");
+    const std::vector<ParticleType> ref   = { refOw, refHw, refHw, refOw, refHw, refHw };
+
+    EXPECT_TRUE(ref == testTypesExpanded);
+}
+
+TEST(NBlibTest, TopologyThrowsIdenticalParticleType)
+{
+    //! User error: Two different ParticleTypes with the same name
+    ParticleType U235(ParticleTypeName("Uranium"), Mass(235));
+    ParticleType U238(ParticleTypeName("Uranium"), Mass(238));
+
+    Molecule ud235(MoleculeName("UraniumDimer235"));
+    ud235.addParticle(ParticleName("U1"), U235);
+    ud235.addParticle(ParticleName("U2"), U235);
+
+    Molecule ud238(MoleculeName("UraniumDimer238"));
+    ud238.addParticle(ParticleName("U1"), U238);
+    ud238.addParticle(ParticleName("U2"), U238);
+
+    TopologyBuilder topologyBuilder;
+    topologyBuilder.addMolecule(ud235, 1);
+    EXPECT_THROW(topologyBuilder.addMolecule(ud238, 1), InputException);
+}
+
+TEST(NBlibTest, TopologyHasExclusions)
+{
+    WaterTopologyBuilder         waters;
+    Topology                     watersTopology = waters.buildTopology(2);
+    const gmx::ListOfLists<int>& testExclusions = watersTopology.getGmxExclusions();
+
+    const std::vector<std::vector<int>>& refExclusions = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+                                                           { 3, 4, 5 }, { 3, 4, 5 }, { 3, 4, 5 } };
+
+    compareLists(testExclusions, refExclusions);
+}
+
+TEST(NBlibTest, TopologyHasSequencing)
+{
+    WaterTopologyBuilder waters;
+    Topology             watersTopology = waters.buildTopology(2);
+
+    EXPECT_EQ(0, watersTopology.sequenceID(MoleculeName("SOL"), 0, ResidueName("SOL"),
+                                           ParticleName("Oxygen")));
+    EXPECT_EQ(1, watersTopology.sequenceID(MoleculeName("SOL"), 0, ResidueName("SOL"), ParticleName("H1")));
+    EXPECT_EQ(2, watersTopology.sequenceID(MoleculeName("SOL"), 0, ResidueName("SOL"), ParticleName("H2")));
+    EXPECT_EQ(3, watersTopology.sequenceID(MoleculeName("SOL"), 1, ResidueName("SOL"),
+                                           ParticleName("Oxygen")));
+    EXPECT_EQ(4, watersTopology.sequenceID(MoleculeName("SOL"), 1, ResidueName("SOL"), ParticleName("H1")));
+    EXPECT_EQ(5, watersTopology.sequenceID(MoleculeName("SOL"), 1, ResidueName("SOL"), ParticleName("H2")));
+}
+
+TEST(NBlibTest, toGmxExclusionBlockWorks)
+{
+    std::vector<std::tuple<int, int>> testInput{ { 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 0 }, { 1, 1 },
+                                                 { 1, 2 }, { 2, 0 }, { 2, 1 }, { 2, 2 } };
+
+    std::vector<gmx::ExclusionBlock> reference;
+
+    gmx::ExclusionBlock localBlock;
+    localBlock.atomNumber.push_back(0);
+    localBlock.atomNumber.push_back(1);
+    localBlock.atomNumber.push_back(2);
+
+    reference.push_back(localBlock);
+    reference.push_back(localBlock);
+    reference.push_back(localBlock);
+
+    std::vector<gmx::ExclusionBlock> probe = detail::toGmxExclusionBlock(testInput);
+
+    ASSERT_EQ(reference.size(), probe.size());
+    for (size_t i = 0; i < reference.size(); ++i)
+    {
+        ASSERT_EQ(reference[i].atomNumber.size(), probe[i].atomNumber.size());
+        for (size_t j = 0; j < reference[i].atomNumber.size(); ++j)
+        {
+            EXPECT_EQ(reference[i].atomNumber[j], probe[i].atomNumber[j]);
+        }
+    }
+}
+
+} // namespace
+} // namespace test
+} // namespace nblib
diff --git a/api/nblib/topology.cpp b/api/nblib/topology.cpp
new file mode 100644 (file)
index 0000000..4e48107
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements nblib Topology and TopologyBuilder
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include <numeric>
+
+#include "gromacs/topology/exclusionblocks.h"
+#include "gromacs/utility/listoflists.h"
+#include "gromacs/utility/smalloc.h"
+#include "nblib/exception.h"
+#include "nblib/particletype.h"
+#include "nblib/topology.h"
+#include "nblib/util/internal.h"
+
+namespace nblib
+{
+
+TopologyBuilder::TopologyBuilder() : numParticles_(0) {}
+
+gmx::ListOfLists<int> TopologyBuilder::createExclusionsListOfLists() const
+{
+    const auto& moleculesList = molecules_;
+
+    std::vector<gmx::ExclusionBlock> exclusionBlockGlobal;
+    exclusionBlockGlobal.reserve(numParticles_);
+
+    size_t particleNumberOffset = 0;
+    for (const auto& molNumberTuple : moleculesList)
+    {
+        const Molecule& molecule   = std::get<0>(molNumberTuple);
+        size_t          numMols    = std::get<1>(molNumberTuple);
+        const auto&     exclusions = molecule.getExclusions();
+
+        assert((!exclusions.empty()
+                && std::string("No exclusions found in the " + molecule.name().value() + " molecule.")
+                           .c_str()));
+
+        std::vector<gmx::ExclusionBlock> exclusionBlockPerMolecule =
+                detail::toGmxExclusionBlock(exclusions);
+
+        // duplicate the exclusionBlockPerMolecule for the number of Molecules of (numMols)
+        for (size_t i = 0; i < numMols; ++i)
+        {
+            auto offsetExclusions =
+                    detail::offsetGmxBlock(exclusionBlockPerMolecule, particleNumberOffset);
+
+            std::copy(std::begin(offsetExclusions), std::end(offsetExclusions),
+                      std::back_inserter(exclusionBlockGlobal));
+
+            particleNumberOffset += molecule.numParticlesInMolecule();
+        }
+    }
+
+    gmx::ListOfLists<int> exclusionsListOfListsGlobal;
+    for (const auto& block : exclusionBlockGlobal)
+    {
+        exclusionsListOfListsGlobal.pushBack(block.atomNumber);
+    }
+
+    return exclusionsListOfListsGlobal;
+}
+
+template<typename T, class Extractor>
+std::vector<T> TopologyBuilder::extractParticleTypeQuantity(Extractor&& extractor)
+{
+    auto& moleculesList = molecules_;
+
+    // returned object
+    std::vector<T> ret;
+    ret.reserve(numParticles_);
+
+    for (auto& molNumberTuple : moleculesList)
+    {
+        Molecule& molecule = std::get<0>(molNumberTuple);
+        size_t    numMols  = std::get<1>(molNumberTuple);
+
+        for (size_t i = 0; i < numMols; ++i)
+        {
+            for (auto& particleData : molecule.particleData())
+            {
+                auto particleTypesMap = molecule.particleTypesMap();
+                ret.push_back(extractor(particleData, particleTypesMap));
+            }
+        }
+    }
+
+    return ret;
+}
+
+Topology TopologyBuilder::buildTopology()
+{
+    topology_.numParticles_ = numParticles_;
+
+    topology_.exclusions_ = createExclusionsListOfLists();
+    topology_.charges_    = extractParticleTypeQuantity<real>([](const auto& data, auto& map) {
+        ignore_unused(map);
+        return data.charge_;
+    });
+
+    // map unique ParticleTypes to IDs
+    std::unordered_map<std::string, int> nameToId;
+    for (auto& name_particleType_tuple : particleTypes_)
+    {
+        topology_.particleTypes_.push_back(name_particleType_tuple.second);
+        nameToId[name_particleType_tuple.first] = nameToId.size();
+    }
+
+    topology_.particleTypeIdOfAllParticles_ =
+            extractParticleTypeQuantity<int>([&nameToId](const auto& data, auto& map) {
+                ignore_unused(map);
+                return nameToId[data.particleTypeName_];
+            });
+
+    detail::ParticleSequencer particleSequencer;
+    particleSequencer.build(molecules_);
+    topology_.particleSequencer_ = std::move(particleSequencer);
+
+    topology_.combinationRule_         = particleTypesInteractions_.getCombinationRule();
+    topology_.nonBondedInteractionMap_ = particleTypesInteractions_.generateTable();
+
+    // Check whether there is any missing term in the particleTypesInteractions compared to the
+    // list of particletypes
+    for (const auto& particleType1 : particleTypes_)
+    {
+        for (const auto& particleType2 : particleTypes_)
+        {
+            auto interactionKey = std::make_tuple(ParticleTypeName(particleType1.first),
+                                                  ParticleTypeName(particleType2.first));
+            if (topology_.nonBondedInteractionMap_.count(interactionKey) == 0)
+            {
+                std::string message =
+                        formatString("Missing nonbonded interaction parameters for pair {} {}",
+                                     particleType1.first, particleType2.first);
+                throw InputException(message);
+            }
+        }
+    }
+
+    return topology_;
+}
+
+TopologyBuilder& TopologyBuilder::addMolecule(const Molecule& molecule, const int nMolecules)
+{
+    /*
+     * 1. Push-back a tuple of molecule type and nMolecules
+     * 2. Append exclusion list into the data structure
+     */
+
+    molecules_.emplace_back(molecule, nMolecules);
+    numParticles_ += nMolecules * molecule.numParticlesInMolecule();
+
+    auto particleTypesInMolecule = molecule.particleTypesMap();
+
+    for (const auto& name_type_tuple : particleTypesInMolecule)
+    {
+        // If we already have the particleType, we need to make
+        // sure that the type's parameters are actually the same
+        // otherwise we would overwrite them
+        if (particleTypes_.count(name_type_tuple.first) > 0)
+        {
+            if (!(particleTypes_.at(name_type_tuple.first) == name_type_tuple.second))
+            {
+                throw InputException("Differing ParticleTypes with identical names encountered");
+            }
+        }
+    }
+
+    // Note: insert does nothing if the key already exists
+    particleTypes_.insert(particleTypesInMolecule.begin(), particleTypesInMolecule.end());
+
+    return *this;
+}
+
+void TopologyBuilder::addParticleTypesInteractions(const ParticleTypesInteractions& particleTypesInteractions)
+{
+    particleTypesInteractions_.merge(particleTypesInteractions);
+}
+
+int Topology::numParticles() const
+{
+    return numParticles_;
+}
+
+std::vector<real> Topology::getCharges() const
+{
+    return charges_;
+}
+
+std::vector<ParticleType> Topology::getParticleTypes() const
+{
+    return particleTypes_;
+}
+
+std::vector<int> Topology::getParticleTypeIdOfAllParticles() const
+{
+    return particleTypeIdOfAllParticles_;
+}
+
+int Topology::sequenceID(MoleculeName moleculeName, int moleculeNr, ResidueName residueName, ParticleName particleName) const
+{
+    return particleSequencer_(moleculeName, moleculeNr, residueName, particleName);
+}
+
+NonBondedInteractionMap Topology::getNonBondedInteractionMap() const
+{
+    return nonBondedInteractionMap_;
+}
+
+CombinationRule Topology::getCombinationRule() const
+{
+    return combinationRule_;
+}
+
+gmx::ListOfLists<int> Topology::getGmxExclusions() const
+{
+    return exclusions_;
+}
+
+} // namespace nblib
diff --git a/api/nblib/topology.h b/api/nblib/topology.h
new file mode 100644 (file)
index 0000000..380daf7
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib Topology and TopologyBuilder
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef NBLIB_TOPOLOGY_H
+#define NBLIB_TOPOLOGY_H
+
+#include <vector>
+
+#include "nblib/interactions.h"
+#include "nblib/molecules.h"
+#include "nblib/topologyhelpers.h"
+
+namespace gmx
+{
+template<typename>
+class ListOfLists;
+} // namespace gmx
+
+namespace nblib
+{
+
+/*! \inpublicapi
+ * \ingroup nblib
+ * \brief System Topology
+ *
+ * Contains all topology information meant to be used by the simulation
+ * engine internally. Private constructor ensures that a Topology object
+ * exists in a scope in a valid state after it has been built using a
+ * Topology Builder.
+ */
+class Topology final
+{
+
+public:
+    //! Returns the total number of particles in the system
+    int numParticles() const;
+
+    //! Returns a vector of particle types
+    std::vector<ParticleType> getParticleTypes() const;
+
+    //! Return the ParticleType ID of all particles
+    std::vector<int> getParticleTypeIdOfAllParticles() const;
+
+    //! Returns a vector of particles partial charges
+    std::vector<real> getCharges() const;
+
+    //! Returns exclusions in proper, performant, GROMACS layout
+    gmx::ListOfLists<int> getGmxExclusions() const;
+
+    //! Returns the unique ID of a specific particle belonging to a molecule in the global space
+    int sequenceID(MoleculeName moleculeName, int moleculeNr, ResidueName residueName, ParticleName particleName) const;
+
+    //! Returns a map of non-bonded force parameters indexed by ParticleType names
+    NonBondedInteractionMap getNonBondedInteractionMap() const;
+
+    //! Returns the combination rule used to generate the NonBondedInteractionMap
+    CombinationRule getCombinationRule() const;
+
+private:
+    Topology() = default;
+
+    friend class TopologyBuilder;
+
+    //! Total number of particles in the system
+    int numParticles_;
+    //! unique collection of ParticleTypes
+    std::vector<ParticleType> particleTypes_;
+    //! store an ID of each particles's type
+    std::vector<int> particleTypeIdOfAllParticles_;
+    //! Storage for particles partial charges
+    std::vector<real> charges_;
+    //! Information about exclusions.
+    gmx::ListOfLists<int> exclusions_;
+    //! Associate molecule, residue and particle names with sequence numbers
+    detail::ParticleSequencer particleSequencer_;
+    //! Map that should hold all nonbonded interactions for all particle types
+    NonBondedInteractionMap nonBondedInteractionMap_;
+    //! Combination Rule used to generate the nonbonded interactions
+    CombinationRule combinationRule_;
+};
+
+/*! \brief Topology Builder
+ *
+ * \libinternal
+ * \ingroup nblib
+ *
+ * A helper class to assist building of topologies. They also ensure that
+ * topologies only exist in a valid state within the scope of the
+ * simulation program.
+ *
+ */
+class TopologyBuilder final
+{
+public:
+    //! Constructor
+    TopologyBuilder();
+
+    /*! \brief
+     * Builds and Returns a valid Topology
+     *
+     * This function accounts for all the molecules added along with their
+     * exclusions and returns a topology with a valid state that is usable
+     * by the GROMACS back-end.
+     */
+    Topology buildTopology();
+
+    //! Adds a molecules of a certain type into the topology
+    TopologyBuilder& addMolecule(const Molecule& moleculeType, int nMolecules);
+
+    //! Add non-bonded interaction map to the topology
+    void addParticleTypesInteractions(const ParticleTypesInteractions& particleTypesInteractions);
+
+private:
+    //! Internally stored topology
+    Topology topology_;
+
+    //! Total number of particles in the system
+    int numParticles_;
+
+    //! List of molecule types and number of molecules
+    std::vector<std::tuple<Molecule, int>> molecules_;
+
+    //! Builds a GROMACS-compliant performant exclusions list aggregating exclusions from all molecules
+    gmx::ListOfLists<int> createExclusionsListOfLists() const;
+
+    //! Helper function to extract quantities like mass, charge, etc from the system
+    template<typename T, class Extractor>
+    std::vector<T> extractParticleTypeQuantity(Extractor&& extractor);
+
+    //! Distinct collection of ParticleTypes
+    std::unordered_map<std::string, ParticleType> particleTypes_;
+
+    //! ParticleType nonbonded parameters
+    ParticleTypesInteractions particleTypesInteractions_;
+};
+
+//! utility function to extract Particle quantities and expand them to the full
+//! array of length numParticles()
+template<class F>
+inline auto expandQuantity(const Topology& topology, F&& particleTypeExtractor)
+{
+    using ValueType = decltype((std::declval<ParticleType>().*std::declval<F>())());
+
+    std::vector<ValueType> ret;
+    ret.reserve(topology.numParticles());
+
+    const std::vector<ParticleType>& particleTypes = topology.getParticleTypes();
+
+    for (size_t id : topology.getParticleTypeIdOfAllParticles())
+    {
+        ret.push_back((particleTypes[id].*particleTypeExtractor)());
+    }
+
+    return ret;
+}
+
+} // namespace nblib
+
+#endif // NBLIB_TOPOLOGY_H
diff --git a/api/nblib/topologyhelpers.cpp b/api/nblib/topologyhelpers.cpp
new file mode 100644 (file)
index 0000000..68b109a
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements nblib Topology helpers
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include <numeric>
+
+#include "gromacs/topology/exclusionblocks.h"
+#include "gromacs/utility/smalloc.h"
+#include "nblib/exception.h"
+#include "nblib/topologyhelpers.h"
+#include "nblib/util/internal.h"
+
+namespace nblib
+{
+
+namespace detail
+{
+
+std::vector<gmx::ExclusionBlock> toGmxExclusionBlock(const std::vector<std::tuple<int, int>>& tupleList)
+{
+    std::vector<gmx::ExclusionBlock> ret;
+
+    auto firstLowerThan = [](auto const& tup1, auto const& tup2) {
+        return std::get<0>(tup1) < std::get<0>(tup2);
+    };
+
+    // initialize pair of iterators delimiting the range of exclusions for
+    // the first particle in the list
+    assert((!tupleList.empty() && "tupleList must not be empty\n"));
+    auto range = std::equal_range(std::begin(tupleList), std::end(tupleList), tupleList[0], firstLowerThan);
+    auto it1 = range.first;
+    auto it2 = range.second;
+
+    // loop over all exclusions in molecule, linear in tupleList.size()
+    while (it1 != std::end(tupleList))
+    {
+        gmx::ExclusionBlock localBlock;
+        // loop over all exclusions for current particle
+        for (; it1 != it2; ++it1)
+        {
+            localBlock.atomNumber.push_back(std::get<1>(*it1));
+        }
+
+        ret.push_back(localBlock);
+
+        // update the upper bound of the range for the next particle
+        if (it1 != end(tupleList))
+        {
+            it2 = std::upper_bound(it1, std::end(tupleList), *it1, firstLowerThan);
+        }
+    }
+
+    return ret;
+}
+
+std::vector<gmx::ExclusionBlock> offsetGmxBlock(std::vector<gmx::ExclusionBlock> inBlock, int offset)
+{
+    // shift particle numbers by offset
+    for (auto& localBlock : inBlock)
+    {
+        std::transform(std::begin(localBlock.atomNumber), std::end(localBlock.atomNumber),
+                       std::begin(localBlock.atomNumber), [offset](auto i) { return i + offset; });
+    }
+
+    return inBlock;
+}
+
+int ParticleSequencer::operator()(const MoleculeName& moleculeName,
+                                  int                 moleculeNr,
+                                  const ResidueName&  residueName,
+                                  const ParticleName& particleName) const
+{
+    try
+    {
+        return data_.at(moleculeName).at(moleculeNr).at(residueName).at(particleName);
+    }
+    catch (const std::out_of_range& outOfRange)
+    {
+        // TODO: use string format function once we have it
+        if (moleculeName.value() == residueName.value())
+        {
+            printf("No particle %s in residue %s in molecule %s found\n", particleName.value().c_str(),
+                   residueName.value().c_str(), moleculeName.value().c_str());
+        }
+        else
+        {
+            printf("No particle %s in molecule %s found\n", particleName.value().c_str(),
+                   moleculeName.value().c_str());
+        }
+
+        throw InputException(outOfRange.what());
+    };
+}
+
+void ParticleSequencer::build(const std::vector<std::tuple<Molecule, int>>& moleculesList)
+{
+    int currentID = 0;
+    for (auto& molNumberTuple : moleculesList)
+    {
+        const Molecule& molecule = std::get<0>(molNumberTuple);
+        const size_t    numMols  = std::get<1>(molNumberTuple);
+
+        auto& moleculeMap = data_[molecule.name()];
+
+        for (size_t i = 0; i < numMols; ++i)
+        {
+            auto& moleculeNrMap = moleculeMap[i];
+            for (int j = 0; j < molecule.numParticlesInMolecule(); ++j)
+            {
+                moleculeNrMap[molecule.residueName(j)][molecule.particleName(j)] = currentID++;
+            }
+        }
+    }
+}
+
+
+template<class I>
+std::tuple<std::vector<size_t>, std::vector<I>> eliminateDuplicateInteractions(const std::vector<I>& aggregatedInteractions)
+{
+    std::vector<size_t> uniqueIndices(aggregatedInteractions.size());
+    std::vector<I>      uniquInteractionsInstances;
+    // if there are no interactions of type B we're done now
+    if (aggregatedInteractions.empty())
+    {
+        return std::make_tuple(uniqueIndices, uniquInteractionsInstances);
+    }
+
+    // create 0,1,2,... sequence
+    std::iota(begin(uniqueIndices), end(uniqueIndices), 0);
+
+    std::vector<std::tuple<I, size_t>> enumeratedBonds(aggregatedInteractions.size());
+    // append each interaction with its index
+    std::transform(begin(aggregatedInteractions), end(aggregatedInteractions), begin(uniqueIndices),
+                   begin(enumeratedBonds), [](I b, size_t i) { return std::make_tuple(b, i); });
+
+    auto sortKey = [](const auto& t1, const auto& t2) { return std::get<0>(t1) < std::get<0>(t2); };
+    // sort w.r.t bonds. the result will contain contiguous segments of identical bond instances
+    // the associated int indicates the original index of each BondType instance in the input vector
+    std::sort(begin(enumeratedBonds), end(enumeratedBonds), sortKey);
+
+    // initialize it1 and it2 to delimit first range of equal BondType instances
+    auto range = std::equal_range(begin(enumeratedBonds), end(enumeratedBonds), enumeratedBonds[0], sortKey);
+    auto it1 = range.first;
+    auto it2 = range.second;
+
+    // number of unique instances of BondType B = number of contiguous segments in enumeratedBonds =
+    //         number of iterations in the outer while loop below
+    while (it1 != end(enumeratedBonds))
+    {
+        uniquInteractionsInstances.push_back(std::get<0>(*it1));
+
+        // loop over all identical BondType instances;
+        for (; it1 != it2; ++it1)
+        {
+            // we note down that the BondType instance at index <interactionIndex>
+            // can be found in the uniqueBondInstances container at index <uniqueBondInstances.size()>
+            int interactionIndex            = std::get<1>(*it1);
+            uniqueIndices[interactionIndex] = uniquInteractionsInstances.size() - 1;
+        }
+
+        // Note it1 has been incremented and is now equal to it2
+        if (it1 != end(enumeratedBonds))
+        {
+            it2 = std::upper_bound(it1, end(enumeratedBonds), *it1, sortKey);
+        }
+    }
+
+    return make_tuple(uniqueIndices, uniquInteractionsInstances);
+}
+
+} // namespace detail
+
+} // namespace nblib
diff --git a/api/nblib/topologyhelpers.h b/api/nblib/topologyhelpers.h
new file mode 100644 (file)
index 0000000..e67f646
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements helper functions needed for the nblib topology
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef NBLIB_TOPOLOGY_HELPERS_H
+#define NBLIB_TOPOLOGY_HELPERS_H
+
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include "gromacs/utility/listoflists.h"
+#include "nblib/molecules.h"
+
+namespace gmx
+{
+struct ExclusionBlock;
+}
+
+namespace nblib
+{
+
+namespace detail
+{
+
+//! Converts tuples of particle indices to exclude to the gmx::ExclusionBlock format
+std::vector<gmx::ExclusionBlock> toGmxExclusionBlock(const std::vector<std::tuple<int, int>>& tupleList);
+
+//! Add offset to all indices in inBlock
+std::vector<gmx::ExclusionBlock> offsetGmxBlock(std::vector<gmx::ExclusionBlock> inBlock, int offset);
+
+//! Helper class for Topology to keep track of particle IDs
+class ParticleSequencer
+{
+    //! Alias for storing by (molecule name, molecule nr, residue name, particle name)
+    using DataType = std::unordered_map<
+            std::string,
+            std::unordered_map<int, std::unordered_map<std::string, std::unordered_map<std::string, int>>>>;
+
+public:
+    //! Build sequence from a list of molecules
+    void build(const std::vector<std::tuple<Molecule, int>>& moleculesList);
+
+    //! Access ID by (molecule name, molecule nr, residue name, particle name)
+    int operator()(const MoleculeName&, int, const ResidueName&, const ParticleName&) const;
+
+private:
+    DataType data_;
+};
+
+} // namespace detail
+
+} // namespace nblib
+
+#endif // NBLIB_TOPOLOGY_HELPERS_H
diff --git a/api/nblib/util/CMakeLists.txt b/api/nblib/util/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a695013
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+#
+# \author Victor Holanda <victor.holanda@cscs.ch>
+# \author Joe Jordan <ejjordan@kth.se>
+# \author Prashanth Kanduri <kanduri@cscs.ch>
+# \author Sebastian Keller <keller@cscs.ch>
+#
+
+
+target_sources(nblib
+        PRIVATE
+        user.cpp
+        )
+
+if(GMX_INSTALL_NBLIB_API)
+    install(FILES
+            user.h
+            DESTINATION include/nblib/util)
+endif()
+
+if(BUILD_TESTING)
+    add_subdirectory(tests)
+endif()
diff --git a/api/nblib/util/internal.h b/api/nblib/util/internal.h
new file mode 100644 (file)
index 0000000..63a8658
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib utilities
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+
+#ifndef NBLIB_UTIL_INTERNAL_H
+#define NBLIB_UTIL_INTERNAL_H
+
+#include <cassert>
+
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <vector>
+
+#include "nblib/basicdefinitions.h"
+#include "nblib/vector.h"
+
+namespace nblib
+{
+
+namespace detail
+{
+//! Format strings for use in error messages
+std::string next_token(std::string& s, const std::string& delimiter);
+
+// Like std::void_t but for values
+template<auto...>
+using void_value_t = void;
+
+template<class T, class = void>
+struct HasValueMember : std::false_type
+{
+};
+
+template<class T>
+struct HasValueMember<T, void_value_t<T::value>> : std::true_type
+{
+};
+
+template<class T, class = void>
+struct AccessTypeMemberIfPresent
+{
+    typedef T type;
+};
+
+template<class T>
+struct AccessTypeMemberIfPresent<T, typename std::void_t<typename T::type>>
+{
+    typedef typename T::type type;
+};
+
+template<class T>
+using AccessTypeMemberIfPresent_t = typename AccessTypeMemberIfPresent<T>::type;
+
+//! this trait evaluates to std::true_type if T is the same as Tuple[N]
+//! OR if T is the same as the type member of Tuple[N]
+template<int N, typename T, typename Tuple>
+struct MatchTypeOrTypeMember :
+    std::disjunction<std::is_same<T, std::tuple_element_t<N, Tuple>>,
+                     std::is_same<T, AccessTypeMemberIfPresent_t<std::tuple_element_t<N, Tuple>>>>
+{
+};
+
+//! recursion to check the next field N+1
+template<int N, class T, class Tuple, template<int, class, class> class Comparison, bool Match = false>
+struct MatchField_ :
+    std::integral_constant<size_t, MatchField_<N + 1, T, Tuple, Comparison, Comparison<N + 1, T, Tuple>{}>{}>
+{
+};
+
+//! recursion stop when Comparison<N, T, Tuple>::value is true
+template<int N, class T, class Tuple, template<int, class, class> class Comparison>
+struct MatchField_<N, T, Tuple, Comparison, true> : std::integral_constant<size_t, N>
+{
+};
+
+} // namespace detail
+
+/*! \brief The value member of this struct evaluates to the integral constant N for which
+ *  the value member of Comparison<N, T, Tuple> is true
+ *  and generates a compiler error if there is no such N
+ */
+template<class T, class Tuple, template<int, class, class> class Comparison>
+struct MatchField : detail::MatchField_<0, T, Tuple, Comparison, Comparison<0, T, Tuple>{}>
+{
+};
+
+/*! \brief Function to return the index in Tuple whose type matches T
+ *  - If there are more than one, the first occurrence will be returned
+ *  - If there is no such type, a compiler error from accessing a tuple out of range is generated
+ *  Note that the default comparison operation supplied here also matches if the type member of Tuple[N] matches T
+ */
+template<typename T, typename Tuple, template<int, class, class> class Comparison = detail::MatchTypeOrTypeMember>
+struct FindIndex : std::integral_constant<size_t, MatchField<T, Tuple, Comparison>{}>
+{
+};
+
+//! Function to return the element in Tuple whose type matches T
+//! Note: if there are more than one, the first occurrence will be returned
+template<typename T, typename Tuple>
+decltype(auto) pickType(Tuple& tup)
+{
+    return std::get<FindIndex<T, Tuple>{}>(tup);
+}
+
+//! Utility to call function with each element in tuple_
+template<class F, class... Ts>
+void for_each_tuple(F&& func, std::tuple<Ts...>& tuple_)
+{
+    std::apply(
+            [f = func](auto&... args) {
+                [[maybe_unused]] auto list = std::initializer_list<int>{ (f(args), 0)... };
+            },
+            tuple_);
+}
+
+//! Utility to call function with each element in tuple_ with const guarantee
+template<class F, class... Ts>
+void for_each_tuple(F&& func, const std::tuple<Ts...>& tuple_)
+{
+    std::apply(
+            [f = func](auto&... args) {
+                [[maybe_unused]] auto list = std::initializer_list<int>{ (f(args), 0)... };
+            },
+            tuple_);
+}
+
+//! Format strings for use in error messages
+template<class... Args>
+std::string formatString(std::string fmt, Args... args)
+{
+    std::ostringstream os;
+    std::string        delimiter = "{}";
+
+    std::initializer_list<int> unused{ 0, (os << detail::next_token(fmt, delimiter) << args, 0)... };
+    static_cast<void>(unused); // unused is not actually used
+
+    os << detail::next_token(fmt, delimiter);
+
+    return os.str();
+}
+
+} // namespace nblib
+
+#endif // NBLIB_UTIL_INTERNAL_H
diff --git a/api/nblib/util/tests/CMakeLists.txt b/api/nblib/util/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4169b07
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+#
+# \author Victor Holanda <victor.holanda@cscs.ch>
+# \author Joe Jordan <ejjordan@kth.se>
+# \author Prashanth Kanduri <kanduri@cscs.ch>
+# \author Sebastian Keller <keller@cscs.ch>
+#
+
+set(testname "NbLibUtilTests")
+set(exename "nblib-util-test")
+
+gmx_add_gtest_executable(
+        ${exename}
+        CPP_SOURCE_FILES
+        user.cpp
+)
+target_link_libraries(${exename} PRIVATE nblib_test_infrastructure nblib)
+gmx_register_gtest_test(${testname} ${exename})
+add_dependencies(check-nblib ${exename})
+
diff --git a/api/nblib/util/tests/refdata/NBlibTest_GeneratedVelocitiesAreCorrect.xml b/api/nblib/util/tests/refdata/NBlibTest_GeneratedVelocitiesAreCorrect.xml
new file mode 100644 (file)
index 0000000..a78f037
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="generated-velocities">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2.6981033066448674</Real>
+      <Real Name="Y">0.5539709822574701</Real>
+      <Real Name="Z">-0.66999586711644377</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.2082614830533767</Real>
+      <Real Name="Y">-0.94750311926855424</Real>
+      <Real Name="Z">-0.3939451998729408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.25639656387655385</Real>
+      <Real Name="Y">0.15094422478273684</Real>
+      <Real Name="Z">-1.9023008166840518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.6653384151271498</Real>
+      <Real Name="Y">1.0284873953993694</Real>
+      <Real Name="Z">1.8633553870427515</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.51905872128588526</Real>
+      <Real Name="Y">-1.5808470201670317</Real>
+      <Real Name="Z">0.59660484580158024</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5358923264099191</Real>
+      <Real Name="Y">-4.0045494036796079</Real>
+      <Real Name="Z">2.3295413128004117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.0461371078863495</Real>
+      <Real Name="Y">-0.65718768377791814</Real>
+      <Real Name="Z">-0.84789603041054018</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.52471564906472223</Real>
+      <Real Name="Y">2.0471783903038139</Real>
+      <Real Name="Z">1.0757782548241039</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.53067549253173119</Real>
+      <Real Name="Y">1.0085627678362028</Real>
+      <Real Name="Z">1.5091817455269219</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.71045843460233404</Real>
+      <Real Name="Y">-1.4262273469861517</Real>
+      <Real Name="Z">2.2175722402808948</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/api/nblib/util/tests/user.cpp b/api/nblib/util/tests/user.cpp
new file mode 100644 (file)
index 0000000..0fccd6e
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements basic nblib utility tests
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include <vector>
+
+#include "nblib/tests/testhelpers.h"
+#include "nblib/util/user.h"
+
+#include "testutils/testasserts.h"
+
+
+namespace nblib
+{
+namespace test
+{
+namespace
+{
+
+TEST(NBlibTest, checkNumericValues)
+{
+    std::vector<Vec3> vec;
+    vec.emplace_back(1., 1., 1.);
+    vec.emplace_back(2., 2., 2.);
+
+    bool ret = checkNumericValues(vec);
+    EXPECT_EQ(ret, true);
+}
+
+TEST(NBlibTest, checkNumericValuesHasNan)
+{
+    std::vector<Vec3> vec;
+    vec.emplace_back(1., 1., 1.);
+    vec.emplace_back(2., 2., 2.);
+
+    vec.emplace_back(NAN, NAN, NAN);
+
+    bool ret = checkNumericValues(vec);
+    EXPECT_EQ(ret, false);
+}
+
+TEST(NBlibTest, checkNumericValuesHasInf)
+{
+    std::vector<Vec3> vec;
+    vec.emplace_back(1., 1., 1.);
+    vec.emplace_back(2., 2., 2.);
+
+    vec.emplace_back(INFINITY, INFINITY, INFINITY);
+
+    bool ret = checkNumericValues(vec);
+    EXPECT_EQ(ret, false);
+}
+
+
+TEST(NBlibTest, GeneratedVelocitiesAreCorrect)
+{
+    constexpr size_t  N = 10;
+    std::vector<real> masses(N, 1.0);
+    std::vector<Vec3> velocities;
+    velocities = generateVelocity(300.0, 1, masses);
+
+    Vector3DTest velocitiesTest;
+    velocitiesTest.testVectors(velocities, "generated-velocities");
+}
+TEST(NBlibTest, generateVelocitySize)
+{
+    constexpr int     N = 10;
+    std::vector<real> masses(N, 1.0);
+    auto              out = generateVelocity(300.0, 1, masses);
+    EXPECT_EQ(out.size(), N);
+}
+
+TEST(NBlibTest, generateVelocityCheckNumbers)
+{
+    constexpr int     N = 10;
+    std::vector<real> masses(N, 1.0);
+    auto              out = generateVelocity(300.0, 1, masses);
+    bool              ret = checkNumericValues(out);
+    EXPECT_EQ(ret, true);
+}
+
+} // namespace
+} // namespace test
+} // namespace nblib
diff --git a/api/nblib/util/user.cpp b/api/nblib/util/user.cpp
new file mode 100644 (file)
index 0000000..99f8501
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements nblib utility functions
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+
+#include "nblib/util/user.h"
+#include "gromacs/random/tabulatednormaldistribution.h"
+#include "gromacs/random/threefry.h"
+#include "gromacs/utility/fatalerror.h"
+
+namespace nblib
+{
+
+namespace detail
+{
+
+std::string next_token(std::string& s, const std::string& delimiter)
+{
+    std::string token = s.substr(0, s.find(delimiter));
+
+    std::size_t next = s.find(delimiter);
+    if (next == std::string::npos)
+        s.clear();
+    else
+        s.erase(0, next + delimiter.length());
+
+    return token;
+}
+
+} // namespace detail
+
+//! Generates an array of particle velocities based on the Maxwell-Boltzmann distribution
+//! using temperature, masses and a random number generator
+static std::vector<Vec3> low_mspeed(real tempi, std::vector<real> const& masses, gmx::ThreeFry2x64<>* rng)
+{
+    int                                    nrdf;
+    real                                   boltz;
+    real                                   ekin, temp;
+    gmx::TabulatedNormalDistribution<real> normalDist;
+
+    std::vector<Vec3> velocities(masses.size());
+
+    boltz = BOLTZ * tempi;
+    ekin  = 0.0;
+    nrdf  = 0;
+    for (size_t i = 0; i < masses.size(); i++)
+    {
+        real mass = masses[i];
+        if (mass > 0)
+        {
+            rng->restart(i, 0);
+            real sd = std::sqrt(boltz / mass);
+            for (int m = 0; (m < dimSize); m++)
+            {
+                velocities[i][m] = sd * normalDist(*rng);
+                ekin += 0.5 * mass * velocities[i][m] * velocities[i][m];
+            }
+            nrdf += dimSize;
+        }
+    }
+    temp = (2.0 * ekin) / (nrdf * BOLTZ);
+    if (temp > 0)
+    {
+        real scal = std::sqrt(tempi / temp);
+        for (auto& vel : velocities)
+        {
+            for (int m = 0; (m < dimSize); m++)
+            {
+                vel[m] *= scal;
+            }
+        }
+    }
+    fprintf(stderr, "Velocities were taken from a Maxwell distribution at %g K\n", tempi);
+    if (debug)
+    {
+        fprintf(debug,
+                "Velocities were taken from a Maxwell distribution\n"
+                "Initial generated temperature: %12.5e (scaled to: %12.5e)\n",
+                temp, tempi);
+    }
+
+    return velocities;
+}
+
+//! Generate velocities from a Maxwell Boltzmann distribution, masses should be the
+//! same as the ones specified for the Topology object
+std::vector<Vec3> generateVelocity(real tempi, unsigned int seed, std::vector<real> const& masses)
+{
+
+    if (seed == 0)
+    {
+        seed = static_cast<int>(gmx::makeRandomSeed());
+        fprintf(stderr, "Using random seed %u for generating velocities\n", seed);
+    }
+    gmx::ThreeFry2x64<> rng(seed, gmx::RandomDomain::MaxwellVelocities);
+
+    return low_mspeed(tempi, masses, &rng);
+}
+
+//! Check within the container of gmx::RVecs for a NaN or inf
+bool checkNumericValues(const std::vector<Vec3>& values)
+{
+    for (auto val : values)
+    {
+        for (int m = 0; (m < dimSize); m++)
+        {
+            if (std::isnan(val[m]) or std::isinf(val[m]))
+            {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+} // namespace nblib
diff --git a/api/nblib/util/user.h b/api/nblib/util/user.h
new file mode 100644 (file)
index 0000000..9d19189
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib utilities
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+
+#ifndef NBLIB_UTIL_USER_H
+#define NBLIB_UTIL_USER_H
+
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <vector>
+
+#include "nblib/basicdefinitions.h"
+#include "nblib/vector.h"
+
+namespace nblib
+{
+
+//! Generate velocities from a Maxwell Boltzmann distribution, masses should be the
+//! same as the ones specified for the Topology object
+std::vector<Vec3> generateVelocity(real Temperature, unsigned int seed, std::vector<real> const& masses);
+
+//! Check within the container of gmx::RVecs for a NaN or inf
+bool checkNumericValues(const std::vector<Vec3>& values);
+
+//! Used to ignore unused arguments of a lambda functions
+inline void ignore_unused() {}
+
+//! Variadic argument version of the ignore_unused function
+template<class T, class... Ts>
+inline void ignore_unused(T& x, Ts&... xs)
+{
+    static_cast<void>(x);
+    ignore_unused(xs...);
+}
+
+/*! \brief A template to create structs as a type-safe alternative to using declarations
+ *
+ * \inpublicapi
+ * \ingroup nblib
+ *
+ * Used in public API functions where a distinction between different
+ * arguments of the same underlying type is desired. This provides a type-safe
+ * version to using declarations. Instead of naming a type alias, the name
+ * is used to define a struct that inherits from StrongType<T>, where T is
+ * the underlying type. For example:
+ *
+ * struct C6 : StrongType<real>
+ * {
+ *     using StrongType::StrongType;
+ *     using StrongType::operator=;
+ * };
+ *
+ * Due to the T() conversion and assignment from T,
+ * an instance of the resulting C6 struct behaves essentially like a real, while construction
+ * from real is disabled. This makes it impossible to pass a real as a function parameter
+ * of type C6.
+ */
+template<class T, class Phantom>
+struct StrongType
+{
+    //! default ctor
+    StrongType() : value_{} {}
+    //! construction from the underlying type T, implicit conversions disabled
+    explicit StrongType(T v) : value_(std::move(v)) {}
+
+    //! assignment from T
+    StrongType& operator=(T v)
+    {
+        value_ = std::move(v);
+        return *this;
+    }
+
+    //! conversion to T
+    operator T() const { return value_; }
+
+    //! access the underlying value
+    T value() const { return value_; }
+
+private:
+    T value_;
+};
+
+//! Equality comparison. For the case where a comparison between StrongTypes with matching T, but differing Phantom
+//! parameters is desired, the underlying value attribute should be compared instead
+template<class T, class Phantom>
+[[maybe_unused]] inline bool operator==(const StrongType<T, Phantom>& lhs, const StrongType<T, Phantom>& rhs)
+{
+    return lhs.value() == rhs.value();
+}
+
+//! comparison function <
+template<class T, class Phantom>
+inline bool operator<(const StrongType<T, Phantom>& lhs, const StrongType<T, Phantom>& rhs)
+{
+    return lhs.value() < rhs.value();
+}
+
+//! comparison function >
+template<class T, class Phantom>
+inline bool operator>(const StrongType<T, Phantom>& lhs, const StrongType<T, Phantom>& rhs)
+{
+    return lhs.value() > rhs.value();
+}
+
+
+//! Base template for a holder of entries of different data types
+template<class... Ts>
+struct TypeList
+{
+};
+
+//! Base template for mapping between a datatype P templated separately with instances of type list L
+template<template<class...> class P, class L>
+struct Map_
+{
+};
+
+//! this is a specialization of the Map_ base template
+//! for the case that the L template parameter itself has template parameters
+//! in this case, the template parameters of L are caught in Ts...
+template<template<class...> class P, template<class...> class L, class... Ts>
+struct Map_<P, L<Ts...>>
+{
+    //! resulting type is a TypeList of the P-template instantiated
+    //! with all template parameters of L
+    typedef TypeList<P<Ts>...> type;
+};
+
+//! Maps a datatype P to create instances where each is templated with entries of type list L
+template<template<class...> class P, class L>
+using Map = typename Map_<P, L>::type;
+
+//! Base template for expressing a datatype P templated with all the entries in type list L
+template<template<class...> class P, class L>
+struct Reduce_
+{
+};
+
+//! Specialization of the Reduce_ base template
+template<template<class...> class P, template<class...> class L, class... Ts>
+struct Reduce_<P, L<Ts...>>
+{
+    //! resulting type is P instantiated
+    //! with all template parameters of L
+    typedef P<Ts...> type;
+};
+
+//! Expresses a data type P instantiated with all the entries in list L as template arguments
+template<template<class...> class P, class L>
+using Reduce = typename Reduce_<P, L>::type;
+
+} // namespace nblib
+
+#endif // NBLIB_UTIL_USER_H
diff --git a/api/nblib/vector.h b/api/nblib/vector.h
new file mode 100644 (file)
index 0000000..4adec4c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \inpublicapi \file
+ * \brief
+ * Implements nblib Cartesian coordinate vector
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#ifndef NBLIB_BASICVECTOR_H
+#define NBLIB_BASICVECTOR_H
+
+#include "gromacs/math/vectypes.h"
+#include "nblib/basicdefinitions.h"
+
+namespace nblib
+{
+using Vec3 = gmx::BasicVector<real>;
+} // namespace nblib
+#endif // NBLIB_BASICVECTOR_H
diff --git a/cmake/FindCUDA.cmake b/cmake/FindCUDA.cmake
deleted file mode 100644 (file)
index fcfb78a..0000000
+++ /dev/null
@@ -1,1865 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2017, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-#.rst:
-# FindCUDA
-# --------
-#
-# GROMACS developers README
-# -------------------------
-#
-# This file is taken from CMake git tag v.3.4.3 Modules/FindCUDA.cmake
-# and modified to use --include-path and --system-include for the
-# include paths required for nvcc to function. Those are the supported
-# flags for nvcc, so should continue to function. Several other
-# supporting cmake files from Modules/FindCUDA/*cmake are also
-# required, and these are also copied from the CMake repo, unmodified.
-#
-# The main file is modified further to make
-# FindPackageHandleStandardArgs able to find those required supporting
-# cmake files.
-#
-# Once CMake 3.7.0 is required for GROMACS, then the fix for CMake
-# issue #14201 contained therein may remove the need for GROMACS to
-# import these files. Or CMake 3.8 supports CUDA natively (but 3.9
-# required for MSVC support also) if that proves useful for
-# GROMACS. However, the FindCUDA.cmake in CMake 3.9.0 still does not
-# use include functionality for headers internal to CUDA that would
-# permit the GROMACS use of -Wundef to lead to warning-free CUDA
-# compilation.
-#
-# Standard FindCUDA documentation
-# -------------------------------
-#
-# Tools for building CUDA C files: libraries and build dependencies.
-#
-# This script locates the NVIDIA CUDA C tools.  It should work on linux,
-# windows, and mac and should be reasonably up to date with CUDA C
-# releases.
-#
-# This script makes use of the standard find_package arguments of
-# <VERSION>, REQUIRED and QUIET.  CUDA_FOUND will report if an
-# acceptable version of CUDA was found.
-#
-# The script will prompt the user to specify CUDA_TOOLKIT_ROOT_DIR if
-# the prefix cannot be determined by the location of nvcc in the system
-# path and REQUIRED is specified to find_package().  To use a different
-# installed version of the toolkit set the environment variable
-# CUDA_BIN_PATH before running cmake (e.g.
-# CUDA_BIN_PATH=/usr/local/cuda1.0 instead of the default
-# /usr/local/cuda) or set CUDA_TOOLKIT_ROOT_DIR after configuring.  If
-# you change the value of CUDA_TOOLKIT_ROOT_DIR, various components that
-# depend on the path will be relocated.
-#
-# It might be necessary to set CUDA_TOOLKIT_ROOT_DIR manually on certain
-# platforms, or to use a cuda runtime not installed in the default
-# location.  In newer versions of the toolkit the cuda library is
-# included with the graphics driver- be sure that the driver version
-# matches what is needed by the cuda runtime version.
-#
-# The following variables affect the behavior of the macros in the
-# script (in alphebetical order).  Note that any of these flags can be
-# changed multiple times in the same directory before calling
-# CUDA_ADD_EXECUTABLE, CUDA_ADD_LIBRARY, CUDA_COMPILE, CUDA_COMPILE_PTX,
-# CUDA_COMPILE_FATBIN, CUDA_COMPILE_CUBIN or CUDA_WRAP_SRCS::
-#
-#   CUDA_64_BIT_DEVICE_CODE (Default matches host bit size)
-#   -- Set to ON to compile for 64 bit device code, OFF for 32 bit device code.
-#      Note that making this different from the host code when generating object
-#      or C files from CUDA code just won't work, because size_t gets defined by
-#      nvcc in the generated source.  If you compile to PTX and then load the
-#      file yourself, you can mix bit sizes between device and host.
-#
-#   CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE (Default ON)
-#   -- Set to ON if you want the custom build rule to be attached to the source
-#      file in Visual Studio.  Turn OFF if you add the same cuda file to multiple
-#      targets.
-#
-#      This allows the user to build the target from the CUDA file; however, bad
-#      things can happen if the CUDA source file is added to multiple targets.
-#      When performing parallel builds it is possible for the custom build
-#      command to be run more than once and in parallel causing cryptic build
-#      errors.  VS runs the rules for every source file in the target, and a
-#      source can have only one rule no matter how many projects it is added to.
-#      When the rule is run from multiple targets race conditions can occur on
-#      the generated file.  Eventually everything will get built, but if the user
-#      is unaware of this behavior, there may be confusion.  It would be nice if
-#      this script could detect the reuse of source files across multiple targets
-#      and turn the option off for the user, but no good solution could be found.
-#
-#   CUDA_BUILD_CUBIN (Default OFF)
-#   -- Set to ON to enable and extra compilation pass with the -cubin option in
-#      Device mode. The output is parsed and register, shared memory usage is
-#      printed during build.
-#
-#   CUDA_BUILD_EMULATION (Default OFF for device mode)
-#   -- Set to ON for Emulation mode. -D_DEVICEEMU is defined for CUDA C files
-#      when CUDA_BUILD_EMULATION is TRUE.
-#
-#   CUDA_GENERATED_OUTPUT_DIR (Default CMAKE_CURRENT_BINARY_DIR)
-#   -- Set to the path you wish to have the generated files placed.  If it is
-#      blank output files will be placed in CMAKE_CURRENT_BINARY_DIR.
-#      Intermediate files will always be placed in
-#      CMAKE_CURRENT_BINARY_DIR/CMakeFiles.
-#
-#   CUDA_HOST_COMPILATION_CPP (Default ON)
-#   -- Set to OFF for C compilation of host code.
-#
-#   CUDA_HOST_COMPILER (Default CMAKE_C_COMPILER, $(VCInstallDir)/bin for VS)
-#   -- Set the host compiler to be used by nvcc.  Ignored if -ccbin or
-#      --compiler-bindir is already present in the CUDA_NVCC_FLAGS or
-#      CUDA_NVCC_FLAGS_<CONFIG> variables.  For Visual Studio targets
-#      $(VCInstallDir)/bin is a special value that expands out to the path when
-#      the command is run from withing VS.
-#
-#   CUDA_NVCC_FLAGS
-#   CUDA_NVCC_FLAGS_<CONFIG>
-#   -- Additional NVCC command line arguments.  NOTE: multiple arguments must be
-#      semi-colon delimited (e.g. --compiler-options;-Wall)
-#
-#   CUDA_PROPAGATE_HOST_FLAGS (Default ON)
-#   -- Set to ON to propagate CMAKE_{C,CXX}_FLAGS and their configuration
-#      dependent counterparts (e.g. CMAKE_C_FLAGS_DEBUG) automatically to the
-#      host compiler through nvcc's -Xcompiler flag.  This helps make the
-#      generated host code match the rest of the system better.  Sometimes
-#      certain flags give nvcc problems, and this will help you turn the flag
-#      propagation off.  This does not affect the flags supplied directly to nvcc
-#      via CUDA_NVCC_FLAGS or through the OPTION flags specified through
-#      CUDA_ADD_LIBRARY, CUDA_ADD_EXECUTABLE, or CUDA_WRAP_SRCS.  Flags used for
-#      shared library compilation are not affected by this flag.
-#
-#   CUDA_SEPARABLE_COMPILATION (Default OFF)
-#   -- If set this will enable separable compilation for all CUDA runtime object
-#      files.  If used outside of CUDA_ADD_EXECUTABLE and CUDA_ADD_LIBRARY
-#      (e.g. calling CUDA_WRAP_SRCS directly),
-#      CUDA_COMPUTE_SEPARABLE_COMPILATION_OBJECT_FILE_NAME and
-#      CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS should be called.
-#
-#   CUDA_SOURCE_PROPERTY_FORMAT
-#   -- If this source file property is set, it can override the format specified
-#      to CUDA_WRAP_SRCS (OBJ, PTX, CUBIN, or FATBIN).  If an input source file
-#      is not a .cu file, setting this file will cause it to be treated as a .cu
-#      file. See documentation for set_source_files_properties on how to set
-#      this property.
-#
-#   CUDA_USE_STATIC_CUDA_RUNTIME (Default ON)
-#   -- When enabled the static version of the CUDA runtime library will be used
-#      in CUDA_LIBRARIES.  If the version of CUDA configured doesn't support
-#      this option, then it will be silently disabled.
-#
-#   CUDA_VERBOSE_BUILD (Default OFF)
-#   -- Set to ON to see all the commands used when building the CUDA file.  When
-#      using a Makefile generator the value defaults to VERBOSE (run make
-#      VERBOSE=1 to see output), although setting CUDA_VERBOSE_BUILD to ON will
-#      always print the output.
-#
-# The script creates the following macros (in alphebetical order)::
-#
-#   CUDA_ADD_CUFFT_TO_TARGET( cuda_target )
-#   -- Adds the cufft library to the target (can be any target).  Handles whether
-#      you are in emulation mode or not.
-#
-#   CUDA_ADD_CUBLAS_TO_TARGET( cuda_target )
-#   -- Adds the cublas library to the target (can be any target).  Handles
-#      whether you are in emulation mode or not.
-#
-#   CUDA_ADD_EXECUTABLE( cuda_target file0 file1 ...
-#                        [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] [OPTIONS ...] )
-#   -- Creates an executable "cuda_target" which is made up of the files
-#      specified.  All of the non CUDA C files are compiled using the standard
-#      build rules specified by CMAKE and the cuda files are compiled to object
-#      files using nvcc and the host compiler.  In addition CUDA_INCLUDE_DIRS is
-#      added automatically to include_directories().  Some standard CMake target
-#      calls can be used on the target after calling this macro
-#      (e.g. set_target_properties and target_link_libraries), but setting
-#      properties that adjust compilation flags will not affect code compiled by
-#      nvcc.  Such flags should be modified before calling CUDA_ADD_EXECUTABLE,
-#      CUDA_ADD_LIBRARY or CUDA_WRAP_SRCS.
-#
-#   CUDA_ADD_LIBRARY( cuda_target file0 file1 ...
-#                     [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [OPTIONS ...] )
-#   -- Same as CUDA_ADD_EXECUTABLE except that a library is created.
-#
-#   CUDA_BUILD_CLEAN_TARGET()
-#   -- Creates a convience target that deletes all the dependency files
-#      generated.  You should make clean after running this target to ensure the
-#      dependency files get regenerated.
-#
-#   CUDA_COMPILE( generated_files file0 file1 ... [STATIC | SHARED | MODULE]
-#                 [OPTIONS ...] )
-#   -- Returns a list of generated files from the input source files to be used
-#      with ADD_LIBRARY or ADD_EXECUTABLE.
-#
-#   CUDA_COMPILE_PTX( generated_files file0 file1 ... [OPTIONS ...] )
-#   -- Returns a list of PTX files generated from the input source files.
-#
-#   CUDA_COMPILE_FATBIN( generated_files file0 file1 ... [OPTIONS ...] )
-#   -- Returns a list of FATBIN files generated from the input source files.
-#
-#   CUDA_COMPILE_CUBIN( generated_files file0 file1 ... [OPTIONS ...] )
-#   -- Returns a list of CUBIN files generated from the input source files.
-#
-#   CUDA_COMPUTE_SEPARABLE_COMPILATION_OBJECT_FILE_NAME( output_file_var
-#                                                        cuda_target
-#                                                        object_files )
-#   -- Compute the name of the intermediate link file used for separable
-#      compilation.  This file name is typically passed into
-#      CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS.  output_file_var is produced
-#      based on cuda_target the list of objects files that need separable
-#      compilation as specified by object_files.  If the object_files list is
-#      empty, then output_file_var will be empty.  This function is called
-#      automatically for CUDA_ADD_LIBRARY and CUDA_ADD_EXECUTABLE.  Note that
-#      this is a function and not a macro.
-#
-#   CUDA_INCLUDE_DIRECTORIES( path0 path1 ... )
-#   -- Sets the directories that should be passed to nvcc
-#      (e.g. nvcc -Ipath0 -Ipath1 ... ). These paths usually contain other .cu
-#      files.
-#
-#
-#
-#   CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS( output_file_var cuda_target
-#                                            nvcc_flags object_files)
-#
-#   -- Generates the link object required by separable compilation from the given
-#      object files.  This is called automatically for CUDA_ADD_EXECUTABLE and
-#      CUDA_ADD_LIBRARY, but can be called manually when using CUDA_WRAP_SRCS
-#      directly.  When called from CUDA_ADD_LIBRARY or CUDA_ADD_EXECUTABLE the
-#      nvcc_flags passed in are the same as the flags passed in via the OPTIONS
-#      argument.  The only nvcc flag added automatically is the bitness flag as
-#      specified by CUDA_64_BIT_DEVICE_CODE.  Note that this is a function
-#      instead of a macro.
-#
-#   CUDA_WRAP_SRCS ( cuda_target format generated_files file0 file1 ...
-#                    [STATIC | SHARED | MODULE] [OPTIONS ...] )
-#   -- This is where all the magic happens.  CUDA_ADD_EXECUTABLE,
-#      CUDA_ADD_LIBRARY, CUDA_COMPILE, and CUDA_COMPILE_PTX all call this
-#      function under the hood.
-#
-#      Given the list of files (file0 file1 ... fileN) this macro generates
-#      custom commands that generate either PTX or linkable objects (use "PTX" or
-#      "OBJ" for the format argument to switch).  Files that don't end with .cu
-#      or have the HEADER_FILE_ONLY property are ignored.
-#
-#      The arguments passed in after OPTIONS are extra command line options to
-#      give to nvcc.  You can also specify per configuration options by
-#      specifying the name of the configuration followed by the options.  General
-#      options must preceed configuration specific options.  Not all
-#      configurations need to be specified, only the ones provided will be used.
-#
-#         OPTIONS -DFLAG=2 "-DFLAG_OTHER=space in flag"
-#         DEBUG -g
-#         RELEASE --use_fast_math
-#         RELWITHDEBINFO --use_fast_math;-g
-#         MINSIZEREL --use_fast_math
-#
-#      For certain configurations (namely VS generating object files with
-#      CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE set to ON), no generated file will
-#      be produced for the given cuda file.  This is because when you add the
-#      cuda file to Visual Studio it knows that this file produces an object file
-#      and will link in the resulting object file automatically.
-#
-#      This script will also generate a separate cmake script that is used at
-#      build time to invoke nvcc.  This is for several reasons.
-#
-#        1. nvcc can return negative numbers as return values which confuses
-#        Visual Studio into thinking that the command succeeded.  The script now
-#        checks the error codes and produces errors when there was a problem.
-#
-#        2. nvcc has been known to not delete incomplete results when it
-#        encounters problems.  This confuses build systems into thinking the
-#        target was generated when in fact an unusable file exists.  The script
-#        now deletes the output files if there was an error.
-#
-#        3. By putting all the options that affect the build into a file and then
-#        make the build rule dependent on the file, the output files will be
-#        regenerated when the options change.
-#
-#      This script also looks at optional arguments STATIC, SHARED, or MODULE to
-#      determine when to target the object compilation for a shared library.
-#      BUILD_SHARED_LIBS is ignored in CUDA_WRAP_SRCS, but it is respected in
-#      CUDA_ADD_LIBRARY.  On some systems special flags are added for building
-#      objects intended for shared libraries.  A preprocessor macro,
-#      <target_name>_EXPORTS is defined when a shared library compilation is
-#      detected.
-#
-#      Flags passed into add_definitions with -D or /D are passed along to nvcc.
-#
-#
-#
-# The script defines the following variables::
-#
-#   CUDA_VERSION_MAJOR    -- The major version of cuda as reported by nvcc.
-#   CUDA_VERSION_MINOR    -- The minor version.
-#   CUDA_VERSION
-#   CUDA_VERSION_STRING   -- CUDA_VERSION_MAJOR.CUDA_VERSION_MINOR
-#
-#   CUDA_TOOLKIT_ROOT_DIR -- Path to the CUDA Toolkit (defined if not set).
-#   CUDA_SDK_ROOT_DIR     -- Path to the CUDA SDK.  Use this to find files in the
-#                            SDK.  This script will not directly support finding
-#                            specific libraries or headers, as that isn't
-#                            supported by NVIDIA.  If you want to change
-#                            libraries when the path changes see the
-#                            FindCUDA.cmake script for an example of how to clear
-#                            these variables.  There are also examples of how to
-#                            use the CUDA_SDK_ROOT_DIR to locate headers or
-#                            libraries, if you so choose (at your own risk).
-#   CUDA_INCLUDE_DIRS     -- Include directory for cuda headers.  Added automatically
-#                            for CUDA_ADD_EXECUTABLE and CUDA_ADD_LIBRARY.
-#   CUDA_LIBRARIES        -- Cuda RT library.
-#   CUDA_CUFFT_LIBRARIES  -- Device or emulation library for the Cuda FFT
-#                            implementation (alternative to:
-#                            CUDA_ADD_CUFFT_TO_TARGET macro)
-#   CUDA_CUBLAS_LIBRARIES -- Device or emulation library for the Cuda BLAS
-#                            implementation (alterative to:
-#                            CUDA_ADD_CUBLAS_TO_TARGET macro).
-#   CUDA_cudart_static_LIBRARY -- Statically linkable cuda runtime library.
-#                                 Only available for CUDA version 5.5+
-#   CUDA_cupti_LIBRARY    -- CUDA Profiling Tools Interface library.
-#                            Only available for CUDA version 4.0+.
-#   CUDA_curand_LIBRARY   -- CUDA Random Number Generation library.
-#                            Only available for CUDA version 3.2+.
-#   CUDA_cusolver_LIBRARY -- CUDA Direct Solver library.
-#                            Only available for CUDA version 7.0+.
-#   CUDA_cusparse_LIBRARY -- CUDA Sparse Matrix library.
-#                            Only available for CUDA version 3.2+.
-#   CUDA_npp_LIBRARY      -- NVIDIA Performance Primitives lib.
-#                            Only available for CUDA version 4.0+.
-#   CUDA_nppc_LIBRARY     -- NVIDIA Performance Primitives lib (core).
-#                            Only available for CUDA version 5.5+.
-#   CUDA_nppi_LIBRARY     -- NVIDIA Performance Primitives lib (image processing).
-#                            Only available for CUDA version 5.5+.
-#   CUDA_npps_LIBRARY     -- NVIDIA Performance Primitives lib (signal processing).
-#                            Only available for CUDA version 5.5+.
-#   CUDA_nvcuvenc_LIBRARY -- CUDA Video Encoder library.
-#                            Only available for CUDA version 3.2+.
-#                            Windows only.
-#   CUDA_nvcuvid_LIBRARY  -- CUDA Video Decoder library.
-#                            Only available for CUDA version 3.2+.
-#                            Windows only.
-#
-
-#   James Bigler, NVIDIA Corp (nvidia.com - jbigler)
-#   Abe Stephens, SCI Institute -- http://www.sci.utah.edu/~abe/FindCuda.html
-#
-#   Copyright (c) 2008 - 2009 NVIDIA Corporation.  All rights reserved.
-#
-#   Copyright (c) 2007-2009
-#   Scientific Computing and Imaging Institute, University of Utah
-#
-#   This code is licensed under the MIT License.  See the FindCUDA.cmake script
-#   for the text of the license.
-
-# The MIT License
-#
-# License for the specific language governing rights and limitations under
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-#
-###############################################################################
-
-# FindCUDA.cmake
-
-# This macro helps us find the location of helper files we will need the full path to
-macro(CUDA_FIND_HELPER_FILE _name _extension)
-  set(_full_name "${_name}.${_extension}")
-  # CMAKE_CURRENT_LIST_FILE contains the full path to the file currently being
-  # processed.  Using this variable, we can pull out the current path, and
-  # provide a way to get access to the other files we need local to here.
-  get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
-  set(CUDA_${_name} "${CMAKE_CURRENT_LIST_DIR}/FindCUDA/${_full_name}")
-  if(NOT EXISTS "${CUDA_${_name}}")
-    set(error_message "${_full_name} not found in ${CMAKE_CURRENT_LIST_DIR}/FindCUDA")
-    if(CUDA_FIND_REQUIRED)
-      message(FATAL_ERROR "${error_message}")
-    else()
-      if(NOT CUDA_FIND_QUIETLY)
-        message(STATUS "${error_message}")
-      endif()
-    endif()
-  endif()
-  # Set this variable as internal, so the user isn't bugged with it.
-  set(CUDA_${_name} ${CUDA_${_name}} CACHE INTERNAL "Location of ${_full_name}" FORCE)
-endmacro()
-
-#####################################################################
-## CUDA_INCLUDE_NVCC_DEPENDENCIES
-##
-
-# So we want to try and include the dependency file if it exists.  If
-# it doesn't exist then we need to create an empty one, so we can
-# include it.
-
-# If it does exist, then we need to check to see if all the files it
-# depends on exist.  If they don't then we should clear the dependency
-# file and regenerate it later.  This covers the case where a header
-# file has disappeared or moved.
-
-macro(CUDA_INCLUDE_NVCC_DEPENDENCIES dependency_file)
-  set(CUDA_NVCC_DEPEND)
-  set(CUDA_NVCC_DEPEND_REGENERATE FALSE)
-
-
-  # Include the dependency file.  Create it first if it doesn't exist .  The
-  # INCLUDE puts a dependency that will force CMake to rerun and bring in the
-  # new info when it changes.  DO NOT REMOVE THIS (as I did and spent a few
-  # hours figuring out why it didn't work.
-  if(NOT EXISTS ${dependency_file})
-    file(WRITE ${dependency_file} "#FindCUDA.cmake generated file.  Do not edit.\n")
-  endif()
-  # Always include this file to force CMake to run again next
-  # invocation and rebuild the dependencies.
-  #message("including dependency_file = ${dependency_file}")
-  include(${dependency_file})
-
-  # Now we need to verify the existence of all the included files
-  # here.  If they aren't there we need to just blank this variable and
-  # make the file regenerate again.
-#   if(DEFINED CUDA_NVCC_DEPEND)
-#     message("CUDA_NVCC_DEPEND set")
-#   else()
-#     message("CUDA_NVCC_DEPEND NOT set")
-#   endif()
-  if(CUDA_NVCC_DEPEND)
-    #message("CUDA_NVCC_DEPEND found")
-    foreach(f ${CUDA_NVCC_DEPEND})
-      # message("searching for ${f}")
-      if(NOT EXISTS ${f})
-        #message("file ${f} not found")
-        set(CUDA_NVCC_DEPEND_REGENERATE TRUE)
-      endif()
-    endforeach()
-  else()
-    #message("CUDA_NVCC_DEPEND false")
-    # No dependencies, so regenerate the file.
-    set(CUDA_NVCC_DEPEND_REGENERATE TRUE)
-  endif()
-
-  #message("CUDA_NVCC_DEPEND_REGENERATE = ${CUDA_NVCC_DEPEND_REGENERATE}")
-  # No incoming dependencies, so we need to generate them.  Make the
-  # output depend on the dependency file itself, which should cause the
-  # rule to re-run.
-  if(CUDA_NVCC_DEPEND_REGENERATE)
-    set(CUDA_NVCC_DEPEND ${dependency_file})
-    #message("Generating an empty dependency_file: ${dependency_file}")
-    file(WRITE ${dependency_file} "#FindCUDA.cmake generated file.  Do not edit.\n")
-  endif()
-
-endmacro()
-
-###############################################################################
-###############################################################################
-# Setup variables' defaults
-###############################################################################
-###############################################################################
-
-# Allow the user to specify if the device code is supposed to be 32 or 64 bit.
-if(CMAKE_SIZEOF_VOID_P EQUAL 8)
-  set(CUDA_64_BIT_DEVICE_CODE_DEFAULT ON)
-else()
-  set(CUDA_64_BIT_DEVICE_CODE_DEFAULT OFF)
-endif()
-option(CUDA_64_BIT_DEVICE_CODE "Compile device code in 64 bit mode" ${CUDA_64_BIT_DEVICE_CODE_DEFAULT})
-
-# Attach the build rule to the source file in VS.  This option
-option(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE "Attach the build rule to the CUDA source file.  Enable only when the CUDA source file is added to at most one target." ON)
-
-# Prints out extra information about the cuda file during compilation
-option(CUDA_BUILD_CUBIN "Generate and parse .cubin files in Device mode." OFF)
-
-# Set whether we are using emulation or device mode.
-option(CUDA_BUILD_EMULATION "Build in Emulation mode" OFF)
-
-# Where to put the generated output.
-set(CUDA_GENERATED_OUTPUT_DIR "" CACHE PATH "Directory to put all the output files.  If blank it will default to the CMAKE_CURRENT_BINARY_DIR")
-
-# Parse HOST_COMPILATION mode.
-option(CUDA_HOST_COMPILATION_CPP "Generated file extension" ON)
-
-# Extra user settable flags
-set(CUDA_NVCC_FLAGS "" CACHE STRING "Semi-colon delimit multiple arguments.")
-
-if(CMAKE_GENERATOR MATCHES "Visual Studio")
-  set(CUDA_HOST_COMPILER "$(VCInstallDir)bin" CACHE FILEPATH "Host side compiler used by NVCC")
-else()
-  if(APPLE
-      AND "${CMAKE_C_COMPILER_ID}" MATCHES "Clang"
-      AND "${CMAKE_C_COMPILER}" MATCHES "/cc$")
-    # Using cc which is symlink to clang may let NVCC think it is GCC and issue
-    # unhandled -dumpspecs option to clang. Also in case neither
-    # CMAKE_C_COMPILER is defined (project does not use C language) nor
-    # CUDA_HOST_COMPILER is specified manually we should skip -ccbin and let
-    # nvcc use its own default C compiler.
-    # Only care about this on APPLE with clang to avoid
-    # following symlinks to things like ccache
-    if(DEFINED CMAKE_C_COMPILER AND NOT DEFINED CUDA_HOST_COMPILER)
-      get_filename_component(c_compiler_realpath "${CMAKE_C_COMPILER}" REALPATH)
-      # if the real path does not end up being clang then
-      # go back to using CMAKE_C_COMPILER
-      if(NOT "${c_compiler_realpath}" MATCHES "/clang$")
-        set(c_compiler_realpath "${CMAKE_C_COMPILER}")
-      endif()
-    else()
-      set(c_compiler_realpath "")
-    endif()
-    set(CUDA_HOST_COMPILER "${c_compiler_realpath}" CACHE FILEPATH "Host side compiler used by NVCC")
-  else()
-    set(CUDA_HOST_COMPILER "${CMAKE_C_COMPILER}"
-      CACHE FILEPATH "Host side compiler used by NVCC")
-  endif()
-endif()
-
-# Propagate the host flags to the host compiler via -Xcompiler
-option(CUDA_PROPAGATE_HOST_FLAGS "Propage C/CXX_FLAGS and friends to the host compiler via -Xcompile" ON)
-
-# Enable CUDA_SEPARABLE_COMPILATION
-option(CUDA_SEPARABLE_COMPILATION "Compile CUDA objects with separable compilation enabled.  Requires CUDA 5.0+" OFF)
-
-# Specifies whether the commands used when compiling the .cu file will be printed out.
-option(CUDA_VERBOSE_BUILD "Print out the commands run while compiling the CUDA source file.  With the Makefile generator this defaults to VERBOSE variable specified on the command line, but can be forced on with this option." OFF)
-
-mark_as_advanced(
-  CUDA_64_BIT_DEVICE_CODE
-  CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE
-  CUDA_GENERATED_OUTPUT_DIR
-  CUDA_HOST_COMPILATION_CPP
-  CUDA_NVCC_FLAGS
-  CUDA_PROPAGATE_HOST_FLAGS
-  CUDA_BUILD_CUBIN
-  CUDA_BUILD_EMULATION
-  CUDA_VERBOSE_BUILD
-  CUDA_SEPARABLE_COMPILATION
-  )
-
-# Makefile and similar generators don't define CMAKE_CONFIGURATION_TYPES, so we
-# need to add another entry for the CMAKE_BUILD_TYPE.  We also need to add the
-# standerd set of 4 build types (Debug, MinSizeRel, Release, and RelWithDebInfo)
-# for completeness.  We need run this loop in order to accomodate the addition
-# of extra configuration types.  Duplicate entries will be removed by
-# REMOVE_DUPLICATES.
-set(CUDA_configuration_types ${CMAKE_CONFIGURATION_TYPES} ${CMAKE_BUILD_TYPE} Debug MinSizeRel Release RelWithDebInfo)
-list(REMOVE_DUPLICATES CUDA_configuration_types)
-foreach(config ${CUDA_configuration_types})
-    string(TOUPPER ${config} config_upper)
-    set(CUDA_NVCC_FLAGS_${config_upper} "" CACHE STRING "Semi-colon delimit multiple arguments.")
-    mark_as_advanced(CUDA_NVCC_FLAGS_${config_upper})
-endforeach()
-
-###############################################################################
-###############################################################################
-# Locate CUDA, Set Build Type, etc.
-###############################################################################
-###############################################################################
-
-macro(cuda_unset_include_and_libraries)
-  unset(CUDA_TOOLKIT_INCLUDE CACHE)
-  unset(CUDA_CUDART_LIBRARY CACHE)
-  unset(CUDA_CUDA_LIBRARY CACHE)
-  # Make sure you run this before you unset CUDA_VERSION.
-  if(CUDA_VERSION VERSION_EQUAL "3.0")
-    # This only existed in the 3.0 version of the CUDA toolkit
-    unset(CUDA_CUDARTEMU_LIBRARY CACHE)
-  endif()
-  unset(CUDA_cudart_static_LIBRARY CACHE)
-  unset(CUDA_cublas_LIBRARY CACHE)
-  unset(CUDA_cublasemu_LIBRARY CACHE)
-  unset(CUDA_cufft_LIBRARY CACHE)
-  unset(CUDA_cufftemu_LIBRARY CACHE)
-  unset(CUDA_cupti_LIBRARY CACHE)
-  unset(CUDA_curand_LIBRARY CACHE)
-  unset(CUDA_cusolver_LIBRARY CACHE)
-  unset(CUDA_cusparse_LIBRARY CACHE)
-  unset(CUDA_npp_LIBRARY CACHE)
-  unset(CUDA_nppc_LIBRARY CACHE)
-  unset(CUDA_nppi_LIBRARY CACHE)
-  unset(CUDA_npps_LIBRARY CACHE)
-  unset(CUDA_nvcuvenc_LIBRARY CACHE)
-  unset(CUDA_nvcuvid_LIBRARY CACHE)
-
-  unset(CUDA_USE_STATIC_CUDA_RUNTIME CACHE)
-endmacro()
-
-# Check to see if the CUDA_TOOLKIT_ROOT_DIR and CUDA_SDK_ROOT_DIR have changed,
-# if they have then clear the cache variables, so that will be detected again.
-if(NOT "${CUDA_TOOLKIT_ROOT_DIR}" STREQUAL "${CUDA_TOOLKIT_ROOT_DIR_INTERNAL}")
-  unset(CUDA_TOOLKIT_TARGET_DIR CACHE)
-  unset(CUDA_NVCC_EXECUTABLE CACHE)
-  cuda_unset_include_and_libraries()
-  unset(CUDA_VERSION CACHE)
-endif()
-
-if(NOT "${CUDA_TOOLKIT_TARGET_DIR}" STREQUAL "${CUDA_TOOLKIT_TARGET_DIR_INTERNAL}")
-  cuda_unset_include_and_libraries()
-endif()
-
-if(NOT "${CUDA_SDK_ROOT_DIR}" STREQUAL "${CUDA_SDK_ROOT_DIR_INTERNAL}")
-  # No specific variables to catch.  Use this kind of code before calling
-  # find_package(CUDA) to clean up any variables that may depend on this path.
-
-  #   unset(MY_SPECIAL_CUDA_SDK_INCLUDE_DIR CACHE)
-  #   unset(MY_SPECIAL_CUDA_SDK_LIBRARY CACHE)
-endif()
-
-# Search for the cuda distribution.
-if(NOT CUDA_TOOLKIT_ROOT_DIR)
-
-  # Search in the CUDA_BIN_PATH first.
-  find_path(CUDA_TOOLKIT_ROOT_DIR
-    NAMES nvcc nvcc.exe
-    PATHS
-      ENV CUDA_PATH
-      ENV CUDA_BIN_PATH
-    PATH_SUFFIXES bin bin64
-    DOC "Toolkit location."
-    NO_DEFAULT_PATH
-    )
-  # Now search default paths
-  find_path(CUDA_TOOLKIT_ROOT_DIR
-    NAMES nvcc nvcc.exe
-    PATHS /usr/local/bin
-          /usr/local/cuda/bin
-    DOC "Toolkit location."
-    )
-
-  if (CUDA_TOOLKIT_ROOT_DIR)
-    string(REGEX REPLACE "[/\\\\]?bin[64]*[/\\\\]?$" "" CUDA_TOOLKIT_ROOT_DIR ${CUDA_TOOLKIT_ROOT_DIR})
-    # We need to force this back into the cache.
-    set(CUDA_TOOLKIT_ROOT_DIR ${CUDA_TOOLKIT_ROOT_DIR} CACHE PATH "Toolkit location." FORCE)
-  endif()
-  if (NOT EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
-    if(CUDA_FIND_REQUIRED)
-      message(FATAL_ERROR "Specify CUDA_TOOLKIT_ROOT_DIR")
-    elseif(NOT CUDA_FIND_QUIETLY)
-      message("CUDA_TOOLKIT_ROOT_DIR not found or specified")
-    endif()
-  endif ()
-endif ()
-
-# CUDA_NVCC_EXECUTABLE
-find_program(CUDA_NVCC_EXECUTABLE
-  NAMES nvcc
-  PATHS "${CUDA_TOOLKIT_ROOT_DIR}"
-  ENV CUDA_PATH
-  ENV CUDA_BIN_PATH
-  PATH_SUFFIXES bin bin64
-  NO_DEFAULT_PATH
-  )
-# Search default search paths, after we search our own set of paths.
-find_program(CUDA_NVCC_EXECUTABLE nvcc)
-mark_as_advanced(CUDA_NVCC_EXECUTABLE)
-
-if(CUDA_NVCC_EXECUTABLE AND NOT CUDA_VERSION)
-  # Compute the version.
-  execute_process (COMMAND ${CUDA_NVCC_EXECUTABLE} "--version" OUTPUT_VARIABLE NVCC_OUT)
-  string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\1" CUDA_VERSION_MAJOR ${NVCC_OUT})
-  string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\2" CUDA_VERSION_MINOR ${NVCC_OUT})
-  set(CUDA_VERSION "${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}" CACHE STRING "Version of CUDA as computed from nvcc.")
-  mark_as_advanced(CUDA_VERSION)
-else()
-  # Need to set these based off of the cached value
-  string(REGEX REPLACE "([0-9]+)\\.([0-9]+).*" "\\1" CUDA_VERSION_MAJOR "${CUDA_VERSION}")
-  string(REGEX REPLACE "([0-9]+)\\.([0-9]+).*" "\\2" CUDA_VERSION_MINOR "${CUDA_VERSION}")
-endif()
-
-# Always set this convenience variable
-set(CUDA_VERSION_STRING "${CUDA_VERSION}")
-
-# Support for arm cross compilation with CUDA 5.5
-if(CUDA_VERSION VERSION_GREATER "5.0" AND CMAKE_CROSSCOMPILING AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm" AND EXISTS "${CUDA_TOOLKIT_ROOT_DIR}/targets/armv7-linux-gnueabihf")
-  set(CUDA_TOOLKIT_TARGET_DIR "${CUDA_TOOLKIT_ROOT_DIR}/targets/armv7-linux-gnueabihf" CACHE PATH "Toolkit target location.")
-else()
-  set(CUDA_TOOLKIT_TARGET_DIR "${CUDA_TOOLKIT_ROOT_DIR}" CACHE PATH "Toolkit target location.")
-endif()
-mark_as_advanced(CUDA_TOOLKIT_TARGET_DIR)
-
-# Target CPU architecture
-if(CUDA_VERSION VERSION_GREATER "5.0" AND CMAKE_CROSSCOMPILING AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
-  set(_cuda_target_cpu_arch_initial "ARM")
-else()
-  set(_cuda_target_cpu_arch_initial "")
-endif()
-set(CUDA_TARGET_CPU_ARCH ${_cuda_target_cpu_arch_initial} CACHE STRING "Specify the name of the class of CPU architecture for which the input files must be compiled.")
-mark_as_advanced(CUDA_TARGET_CPU_ARCH)
-
-# CUDA_TOOLKIT_INCLUDE
-find_path(CUDA_TOOLKIT_INCLUDE
-  device_functions.h # Header included in toolkit
-  PATHS "${CUDA_TOOLKIT_TARGET_DIR}" "${CUDA_TOOLKIT_ROOT_DIR}"
-  ENV CUDA_PATH
-  ENV CUDA_INC_PATH
-  PATH_SUFFIXES include
-  NO_DEFAULT_PATH
-  )
-# Search default search paths, after we search our own set of paths.
-find_path(CUDA_TOOLKIT_INCLUDE device_functions.h)
-mark_as_advanced(CUDA_TOOLKIT_INCLUDE)
-
-# Set the user list of include dir to nothing to initialize it.
-set (CUDA_NVCC_INCLUDE_ARGS_USER "")
-set (CUDA_INCLUDE_DIRS ${CUDA_TOOLKIT_INCLUDE})
-
-macro(cuda_find_library_local_first_with_path_ext _var _names _doc _path_ext )
-  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
-    # CUDA 3.2+ on Windows moved the library directories, so we need the new
-    # and old paths.
-    set(_cuda_64bit_lib_dir "${_path_ext}lib/x64" "${_path_ext}lib64" "${_path_ext}libx64" )
-  endif()
-  # CUDA 3.2+ on Windows moved the library directories, so we need to new
-  # (lib/Win32) and the old path (lib).
-  find_library(${_var}
-    NAMES ${_names}
-    PATHS "${CUDA_TOOLKIT_TARGET_DIR}" "${CUDA_TOOLKIT_ROOT_DIR}"
-    ENV CUDA_PATH
-    ENV CUDA_LIB_PATH
-    PATH_SUFFIXES ${_cuda_64bit_lib_dir} "${_path_ext}lib/Win32" "${_path_ext}lib" "${_path_ext}libWin32"
-    DOC ${_doc}
-    NO_DEFAULT_PATH
-    )
-  # Search default search paths, after we search our own set of paths.
-  find_library(${_var}
-    NAMES ${_names}
-    PATHS "/usr/lib/nvidia-current"
-    DOC ${_doc}
-    )
-endmacro()
-
-macro(cuda_find_library_local_first _var _names _doc)
-  cuda_find_library_local_first_with_path_ext( "${_var}" "${_names}" "${_doc}" "" )
-endmacro()
-
-macro(find_library_local_first _var _names _doc )
-  cuda_find_library_local_first( "${_var}" "${_names}" "${_doc}" "" )
-endmacro()
-
-
-# CUDA_LIBRARIES
-cuda_find_library_local_first(CUDA_CUDART_LIBRARY cudart "\"cudart\" library")
-if(CUDA_VERSION VERSION_EQUAL "3.0")
-  # The cudartemu library only existed for the 3.0 version of CUDA.
-  cuda_find_library_local_first(CUDA_CUDARTEMU_LIBRARY cudartemu "\"cudartemu\" library")
-  mark_as_advanced(
-    CUDA_CUDARTEMU_LIBRARY
-    )
-endif()
-if(NOT CUDA_VERSION VERSION_LESS "5.5")
-  cuda_find_library_local_first(CUDA_cudart_static_LIBRARY cudart_static "static CUDA runtime library")
-  mark_as_advanced(CUDA_cudart_static_LIBRARY)
-endif()
-if(CUDA_cudart_static_LIBRARY)
-  # Set whether to use the static cuda runtime.
-  option(CUDA_USE_STATIC_CUDA_RUNTIME "Use the static version of the CUDA runtime library if available" ON)
-else()
-  option(CUDA_USE_STATIC_CUDA_RUNTIME "Use the static version of the CUDA runtime library if available" OFF)
-endif()
-
-if(CUDA_USE_STATIC_CUDA_RUNTIME)
-  if(UNIX)
-    # Check for the dependent libraries.  Here we look for pthreads.
-    if (DEFINED CMAKE_THREAD_PREFER_PTHREAD)
-      set(_cuda_cmake_thread_prefer_pthread ${CMAKE_THREAD_PREFER_PTHREAD})
-    endif()
-    set(CMAKE_THREAD_PREFER_PTHREAD 1)
-
-    # Many of the FindXYZ CMake comes with makes use of try_compile with int main(){return 0;}
-    # as the source file.  Unfortunately this causes a warning with -Wstrict-prototypes and
-    # -Werror causes the try_compile to fail.  We will just temporarily disable other flags
-    # when doing the find_package command here.
-    set(_cuda_cmake_c_flags ${CMAKE_C_FLAGS})
-    set(CMAKE_C_FLAGS "-fPIC")
-    find_package(Threads REQUIRED)
-    set(CMAKE_C_FLAGS ${_cuda_cmake_c_flags})
-
-    if (DEFINED _cuda_cmake_thread_prefer_pthread)
-      set(CMAKE_THREAD_PREFER_PTHREAD ${_cuda_cmake_thread_prefer_pthread})
-      unset(_cuda_cmake_thread_prefer_pthread)
-    else()
-      unset(CMAKE_THREAD_PREFER_PTHREAD)
-    endif()
-    if (NOT APPLE)
-      # Here is librt that has things such as, clock_gettime, shm_open, and shm_unlink.
-      find_library(CUDA_rt_LIBRARY rt)
-      find_library(CUDA_dl_LIBRARY dl)
-      if (NOT CUDA_rt_LIBRARY)
-        message(WARNING "Expecting to find librt for libcudart_static, but didn't find it.")
-      endif()
-      if (NOT CUDA_dl_LIBRARY)
-        message(WARNING "Expecting to find libdl for libcudart_static, but didn't find it.")
-      endif()
-    endif()
-  endif()
-endif()
-
-# CUPTI library showed up in cuda toolkit 4.0
-if(NOT CUDA_VERSION VERSION_LESS "4.0")
-  cuda_find_library_local_first_with_path_ext(CUDA_cupti_LIBRARY cupti "\"cupti\" library" "extras/CUPTI/")
-  mark_as_advanced(CUDA_cupti_LIBRARY)
-endif()
-
-# Set the CUDA_LIBRARIES variable.  This is the set of stuff to link against if you are
-# using the CUDA runtime.  For the dynamic version of the runtime, most of the
-# dependencies are brough in, but for the static version there are additional libraries
-# and linker commands needed.
-# Initialize to empty
-set(CUDA_LIBRARIES)
-
-# If we are using emulation mode and we found the cudartemu library then use
-# that one instead of cudart.
-if(CUDA_BUILD_EMULATION AND CUDA_CUDARTEMU_LIBRARY)
-  list(APPEND CUDA_LIBRARIES ${CUDA_CUDARTEMU_LIBRARY})
-elseif(CUDA_USE_STATIC_CUDA_RUNTIME AND CUDA_cudart_static_LIBRARY)
-  list(APPEND CUDA_LIBRARIES ${CUDA_cudart_static_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
-  if (CUDA_rt_LIBRARY)
-    list(APPEND CUDA_LIBRARIES ${CUDA_rt_LIBRARY})
-  endif()
-  if (CUDA_dl_LIBRARY)
-    list(APPEND CUDA_LIBRARIES ${CUDA_dl_LIBRARY})
-  endif()
-  if(APPLE)
-    # We need to add the default path to the driver (libcuda.dylib) as an rpath, so that
-    # the static cuda runtime can find it at runtime.
-    list(APPEND CUDA_LIBRARIES -Wl,-rpath,/usr/local/cuda/lib)
-  endif()
-else()
-  list(APPEND CUDA_LIBRARIES ${CUDA_CUDART_LIBRARY})
-endif()
-
-# 1.1 toolkit on linux doesn't appear to have a separate library on
-# some platforms.
-cuda_find_library_local_first(CUDA_CUDA_LIBRARY cuda "\"cuda\" library (older versions only).")
-
-mark_as_advanced(
-  CUDA_CUDA_LIBRARY
-  CUDA_CUDART_LIBRARY
-  )
-
-#######################
-# Look for some of the toolkit helper libraries
-macro(FIND_CUDA_HELPER_LIBS _name)
-  cuda_find_library_local_first(CUDA_${_name}_LIBRARY ${_name} "\"${_name}\" library")
-  mark_as_advanced(CUDA_${_name}_LIBRARY)
-endmacro()
-
-#######################
-# Disable emulation for v3.1 onward
-if(CUDA_VERSION VERSION_GREATER "3.0")
-  if(CUDA_BUILD_EMULATION)
-    message(FATAL_ERROR "CUDA_BUILD_EMULATION is not supported in version 3.1 and onwards.  You must disable it to proceed.  You have version ${CUDA_VERSION}.")
-  endif()
-endif()
-
-# Search for additional CUDA toolkit libraries.
-if(CUDA_VERSION VERSION_LESS "3.1")
-  # Emulation libraries aren't available in version 3.1 onward.
-  find_cuda_helper_libs(cufftemu)
-  find_cuda_helper_libs(cublasemu)
-endif()
-find_cuda_helper_libs(cufft)
-find_cuda_helper_libs(cublas)
-if(NOT CUDA_VERSION VERSION_LESS "3.2")
-  # cusparse showed up in version 3.2
-  find_cuda_helper_libs(cusparse)
-  find_cuda_helper_libs(curand)
-  if (WIN32)
-    find_cuda_helper_libs(nvcuvenc)
-    find_cuda_helper_libs(nvcuvid)
-  endif()
-endif()
-if(CUDA_VERSION VERSION_GREATER "5.0")
-  # In CUDA 5.5 NPP was splitted onto 3 separate libraries.
-  find_cuda_helper_libs(nppc)
-  find_cuda_helper_libs(nppi)
-  find_cuda_helper_libs(npps)
-  set(CUDA_npp_LIBRARY "${CUDA_nppc_LIBRARY};${CUDA_nppi_LIBRARY};${CUDA_npps_LIBRARY}")
-elseif(NOT CUDA_VERSION VERSION_LESS "4.0")
-  find_cuda_helper_libs(npp)
-endif()
-if(NOT CUDA_VERSION VERSION_LESS "7.0")
-  # cusolver showed up in version 7.0
-  find_cuda_helper_libs(cusolver)
-endif()
-
-if (CUDA_BUILD_EMULATION)
-  set(CUDA_CUFFT_LIBRARIES ${CUDA_cufftemu_LIBRARY})
-  set(CUDA_CUBLAS_LIBRARIES ${CUDA_cublasemu_LIBRARY})
-else()
-  set(CUDA_CUFFT_LIBRARIES ${CUDA_cufft_LIBRARY})
-  set(CUDA_CUBLAS_LIBRARIES ${CUDA_cublas_LIBRARY})
-endif()
-
-########################
-# Look for the SDK stuff.  As of CUDA 3.0 NVSDKCUDA_ROOT has been replaced with
-# NVSDKCOMPUTE_ROOT with the old CUDA C contents moved into the C subdirectory
-find_path(CUDA_SDK_ROOT_DIR common/inc/cutil.h
- HINTS
-  "$ENV{NVSDKCOMPUTE_ROOT}/C"
-  ENV NVSDKCUDA_ROOT
-  "[HKEY_LOCAL_MACHINE\\SOFTWARE\\NVIDIA Corporation\\Installed Products\\NVIDIA SDK 10\\Compute;InstallDir]"
- PATHS
-  "/Developer/GPU\ Computing/C"
-  )
-
-# Keep the CUDA_SDK_ROOT_DIR first in order to be able to override the
-# environment variables.
-set(CUDA_SDK_SEARCH_PATH
-  "${CUDA_SDK_ROOT_DIR}"
-  "${CUDA_TOOLKIT_ROOT_DIR}/local/NVSDK0.2"
-  "${CUDA_TOOLKIT_ROOT_DIR}/NVSDK0.2"
-  "${CUDA_TOOLKIT_ROOT_DIR}/NV_CUDA_SDK"
-  "$ENV{HOME}/NVIDIA_CUDA_SDK"
-  "$ENV{HOME}/NVIDIA_CUDA_SDK_MACOSX"
-  "/Developer/CUDA"
-  )
-
-# Example of how to find an include file from the CUDA_SDK_ROOT_DIR
-
-# find_path(CUDA_CUT_INCLUDE_DIR
-#   cutil.h
-#   PATHS ${CUDA_SDK_SEARCH_PATH}
-#   PATH_SUFFIXES "common/inc"
-#   DOC "Location of cutil.h"
-#   NO_DEFAULT_PATH
-#   )
-# # Now search system paths
-# find_path(CUDA_CUT_INCLUDE_DIR cutil.h DOC "Location of cutil.h")
-
-# mark_as_advanced(CUDA_CUT_INCLUDE_DIR)
-
-
-# Example of how to find a library in the CUDA_SDK_ROOT_DIR
-
-# # cutil library is called cutil64 for 64 bit builds on windows.  We don't want
-# # to get these confused, so we are setting the name based on the word size of
-# # the build.
-
-# if(CMAKE_SIZEOF_VOID_P EQUAL 8)
-#   set(cuda_cutil_name cutil64)
-# else()
-#   set(cuda_cutil_name cutil32)
-# endif()
-
-# find_library(CUDA_CUT_LIBRARY
-#   NAMES cutil ${cuda_cutil_name}
-#   PATHS ${CUDA_SDK_SEARCH_PATH}
-#   # The new version of the sdk shows up in common/lib, but the old one is in lib
-#   PATH_SUFFIXES "common/lib" "lib"
-#   DOC "Location of cutil library"
-#   NO_DEFAULT_PATH
-#   )
-# # Now search system paths
-# find_library(CUDA_CUT_LIBRARY NAMES cutil ${cuda_cutil_name} DOC "Location of cutil library")
-# mark_as_advanced(CUDA_CUT_LIBRARY)
-# set(CUDA_CUT_LIBRARIES ${CUDA_CUT_LIBRARY})
-
-
-
-#############################
-# Check for required components
-set(CUDA_FOUND TRUE)
-
-set(CUDA_TOOLKIT_ROOT_DIR_INTERNAL "${CUDA_TOOLKIT_ROOT_DIR}" CACHE INTERNAL
-  "This is the value of the last time CUDA_TOOLKIT_ROOT_DIR was set successfully." FORCE)
-set(CUDA_TOOLKIT_TARGET_DIR_INTERNAL "${CUDA_TOOLKIT_TARGET_DIR}" CACHE INTERNAL
-  "This is the value of the last time CUDA_TOOLKIT_TARGET_DIR was set successfully." FORCE)
-set(CUDA_SDK_ROOT_DIR_INTERNAL "${CUDA_SDK_ROOT_DIR}" CACHE INTERNAL
-  "This is the value of the last time CUDA_SDK_ROOT_DIR was set successfully." FORCE)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(CUDA
-  REQUIRED_VARS
-    CUDA_TOOLKIT_ROOT_DIR
-    CUDA_NVCC_EXECUTABLE
-    CUDA_INCLUDE_DIRS
-    CUDA_CUDART_LIBRARY
-  VERSION_VAR
-    CUDA_VERSION
-  )
-
-
-
-###############################################################################
-###############################################################################
-# Macros
-###############################################################################
-###############################################################################
-
-###############################################################################
-# Add include directories to pass to the nvcc command.
-macro(CUDA_INCLUDE_DIRECTORIES)
-  foreach(dir ${ARGN})
-    list(APPEND CUDA_NVCC_INCLUDE_ARGS_USER -I${dir})
-  endforeach()
-endmacro()
-
-
-##############################################################################
-cuda_find_helper_file(parse_cubin cmake)
-cuda_find_helper_file(make2cmake cmake)
-cuda_find_helper_file(run_nvcc cmake)
-
-##############################################################################
-# Separate the OPTIONS out from the sources
-#
-macro(CUDA_GET_SOURCES_AND_OPTIONS _sources _cmake_options _options)
-  set( ${_sources} )
-  set( ${_cmake_options} )
-  set( ${_options} )
-  set( _found_options FALSE )
-  foreach(arg ${ARGN})
-    if("x${arg}" STREQUAL "xOPTIONS")
-      set( _found_options TRUE )
-    elseif(
-        "x${arg}" STREQUAL "xWIN32" OR
-        "x${arg}" STREQUAL "xMACOSX_BUNDLE" OR
-        "x${arg}" STREQUAL "xEXCLUDE_FROM_ALL" OR
-        "x${arg}" STREQUAL "xSTATIC" OR
-        "x${arg}" STREQUAL "xSHARED" OR
-        "x${arg}" STREQUAL "xMODULE"
-        )
-      list(APPEND ${_cmake_options} ${arg})
-    else()
-      if ( _found_options )
-        list(APPEND ${_options} ${arg})
-      else()
-        # Assume this is a file
-        list(APPEND ${_sources} ${arg})
-      endif()
-    endif()
-  endforeach()
-endmacro()
-
-##############################################################################
-# Parse the OPTIONS from ARGN and set the variables prefixed by _option_prefix
-#
-macro(CUDA_PARSE_NVCC_OPTIONS _option_prefix)
-  set( _found_config )
-  foreach(arg ${ARGN})
-    # Determine if we are dealing with a perconfiguration flag
-    foreach(config ${CUDA_configuration_types})
-      string(TOUPPER ${config} config_upper)
-      if (arg STREQUAL "${config_upper}")
-        set( _found_config _${arg})
-        # Set arg to nothing to keep it from being processed further
-        set( arg )
-      endif()
-    endforeach()
-
-    if ( arg )
-      list(APPEND ${_option_prefix}${_found_config} "${arg}")
-    endif()
-  endforeach()
-endmacro()
-
-##############################################################################
-# Helper to add the include directory for CUDA only once
-function(CUDA_ADD_CUDA_INCLUDE_ONCE)
-  get_directory_property(_include_directories INCLUDE_DIRECTORIES)
-  set(_add TRUE)
-  if(_include_directories)
-    foreach(dir ${_include_directories})
-      if("${dir}" STREQUAL "${CUDA_INCLUDE_DIRS}")
-        set(_add FALSE)
-      endif()
-    endforeach()
-  endif()
-  if(_add)
-    include_directories(${CUDA_INCLUDE_DIRS})
-  endif()
-endfunction()
-
-function(CUDA_BUILD_SHARED_LIBRARY shared_flag)
-  set(cmake_args ${ARGN})
-  # If SHARED, MODULE, or STATIC aren't already in the list of arguments, then
-  # add SHARED or STATIC based on the value of BUILD_SHARED_LIBS.
-  list(FIND cmake_args SHARED _cuda_found_SHARED)
-  list(FIND cmake_args MODULE _cuda_found_MODULE)
-  list(FIND cmake_args STATIC _cuda_found_STATIC)
-  if( _cuda_found_SHARED GREATER -1 OR
-      _cuda_found_MODULE GREATER -1 OR
-      _cuda_found_STATIC GREATER -1)
-    set(_cuda_build_shared_libs)
-  else()
-    if (BUILD_SHARED_LIBS)
-      set(_cuda_build_shared_libs SHARED)
-    else()
-      set(_cuda_build_shared_libs STATIC)
-    endif()
-  endif()
-  set(${shared_flag} ${_cuda_build_shared_libs} PARENT_SCOPE)
-endfunction()
-
-##############################################################################
-# Helper to avoid clashes of files with the same basename but different paths.
-# This doesn't attempt to do exactly what CMake internals do, which is to only
-# add this path when there is a conflict, since by the time a second collision
-# in names is detected it's already too late to fix the first one.  For
-# consistency sake the relative path will be added to all files.
-function(CUDA_COMPUTE_BUILD_PATH path build_path)
-  #message("CUDA_COMPUTE_BUILD_PATH([${path}] ${build_path})")
-  # Only deal with CMake style paths from here on out
-  file(TO_CMAKE_PATH "${path}" bpath)
-  if (IS_ABSOLUTE "${bpath}")
-    # Absolute paths are generally unnessary, especially if something like
-    # file(GLOB_RECURSE) is used to pick up the files.
-
-    string(FIND "${bpath}" "${CMAKE_CURRENT_BINARY_DIR}" _binary_dir_pos)
-    if (_binary_dir_pos EQUAL 0)
-      file(RELATIVE_PATH bpath "${CMAKE_CURRENT_BINARY_DIR}" "${bpath}")
-    else()
-      file(RELATIVE_PATH bpath "${CMAKE_CURRENT_SOURCE_DIR}" "${bpath}")
-    endif()
-  endif()
-
-  # This recipe is from cmLocalGenerator::CreateSafeUniqueObjectFileName in the
-  # CMake source.
-
-  # Remove leading /
-  string(REGEX REPLACE "^[/]+" "" bpath "${bpath}")
-  # Avoid absolute paths by removing ':'
-  string(REPLACE ":" "_" bpath "${bpath}")
-  # Avoid relative paths that go up the tree
-  string(REPLACE "../" "__/" bpath "${bpath}")
-  # Avoid spaces
-  string(REPLACE " " "_" bpath "${bpath}")
-
-  # Strip off the filename.  I wait until here to do it, since removin the
-  # basename can make a path that looked like path/../basename turn into
-  # path/.. (notice the trailing slash).
-  get_filename_component(bpath "${bpath}" PATH)
-
-  set(${build_path} "${bpath}" PARENT_SCOPE)
-  #message("${build_path} = ${bpath}")
-endfunction()
-
-##############################################################################
-# This helper macro populates the following variables and setups up custom
-# commands and targets to invoke the nvcc compiler to generate C or PTX source
-# dependent upon the format parameter.  The compiler is invoked once with -M
-# to generate a dependency file and a second time with -cuda or -ptx to generate
-# a .cpp or .ptx file.
-# INPUT:
-#   cuda_target         - Target name
-#   format              - PTX, CUBIN, FATBIN or OBJ
-#   FILE1 .. FILEN      - The remaining arguments are the sources to be wrapped.
-#   OPTIONS             - Extra options to NVCC
-# OUTPUT:
-#   generated_files     - List of generated files
-##############################################################################
-##############################################################################
-
-macro(CUDA_WRAP_SRCS cuda_target format generated_files)
-
-  # If CMake doesn't support separable compilation, complain
-  if(CUDA_SEPARABLE_COMPILATION AND CMAKE_VERSION VERSION_LESS "2.8.10.1")
-    message(SEND_ERROR "CUDA_SEPARABLE_COMPILATION isn't supported for CMake versions less than 2.8.10.1")
-  endif()
-
-  # Set up all the command line flags here, so that they can be overridden on a per target basis.
-
-  set(nvcc_flags "")
-
-  # Emulation if the card isn't present.
-  if (CUDA_BUILD_EMULATION)
-    # Emulation.
-    set(nvcc_flags ${nvcc_flags} --device-emulation -D_DEVICEEMU -g)
-  else()
-    # Device mode.  No flags necessary.
-  endif()
-
-  if(CUDA_HOST_COMPILATION_CPP)
-    set(CUDA_C_OR_CXX CXX)
-  else()
-    if(CUDA_VERSION VERSION_LESS "3.0")
-      set(nvcc_flags ${nvcc_flags} --host-compilation C)
-    else()
-      message(WARNING "--host-compilation flag is deprecated in CUDA version >= 3.0.  Removing --host-compilation C flag" )
-    endif()
-    set(CUDA_C_OR_CXX C)
-  endif()
-
-  set(generated_extension ${CMAKE_${CUDA_C_OR_CXX}_OUTPUT_EXTENSION})
-
-  if(CUDA_64_BIT_DEVICE_CODE)
-    set(nvcc_flags ${nvcc_flags} -m64)
-  else()
-    set(nvcc_flags ${nvcc_flags} -m32)
-  endif()
-
-  if(CUDA_TARGET_CPU_ARCH)
-    set(nvcc_flags ${nvcc_flags} "--target-cpu-architecture=${CUDA_TARGET_CPU_ARCH}")
-  endif()
-
-  # This needs to be passed in at this stage, because VS needs to fill out the
-  # value of VCInstallDir from within VS.  Note that CCBIN is only used if
-  # -ccbin or --compiler-bindir isn't used and CUDA_HOST_COMPILER matches
-  # $(VCInstallDir)/bin.
-  if(CMAKE_GENERATOR MATCHES "Visual Studio")
-    set(ccbin_flags -D "\"CCBIN:PATH=$(VCInstallDir)bin\"" )
-  else()
-    set(ccbin_flags)
-  endif()
-
-  # Figure out which configure we will use and pass that in as an argument to
-  # the script.  We need to defer the decision until compilation time, because
-  # for VS projects we won't know if we are making a debug or release build
-  # until build time.
-  if(CMAKE_GENERATOR MATCHES "Visual Studio")
-    set( CUDA_build_configuration "$(ConfigurationName)" )
-  else()
-    set( CUDA_build_configuration "${CMAKE_BUILD_TYPE}")
-  endif()
-
-  # Initialize our list of includes with the user ones followed by the CUDA system ones.
-  set(CUDA_NVCC_INCLUDE_ARGS ${CUDA_NVCC_INCLUDE_ARGS_USER} --system-include;${CUDA_INCLUDE_DIRS})
-  # Get the include directories for this directory and use them for our nvcc command.
-  # Remove duplicate entries which may be present since include_directories
-  # in CMake >= 2.8.8 does not remove them.
-  get_directory_property(CUDA_NVCC_INCLUDE_DIRECTORIES INCLUDE_DIRECTORIES)
-  list(REMOVE_DUPLICATES CUDA_NVCC_INCLUDE_DIRECTORIES)
-  if(CUDA_NVCC_INCLUDE_DIRECTORIES)
-    foreach(dir ${CUDA_NVCC_INCLUDE_DIRECTORIES})
-      list(APPEND CUDA_NVCC_INCLUDE_ARGS --include-path;${dir})
-    endforeach()
-  endif()
-
-  # Reset these variables
-  set(CUDA_WRAP_OPTION_NVCC_FLAGS)
-  foreach(config ${CUDA_configuration_types})
-    string(TOUPPER ${config} config_upper)
-    set(CUDA_WRAP_OPTION_NVCC_FLAGS_${config_upper})
-  endforeach()
-
-  CUDA_GET_SOURCES_AND_OPTIONS(_cuda_wrap_sources _cuda_wrap_cmake_options _cuda_wrap_options ${ARGN})
-  CUDA_PARSE_NVCC_OPTIONS(CUDA_WRAP_OPTION_NVCC_FLAGS ${_cuda_wrap_options})
-
-  # Figure out if we are building a shared library.  BUILD_SHARED_LIBS is
-  # respected in CUDA_ADD_LIBRARY.
-  set(_cuda_build_shared_libs FALSE)
-  # SHARED, MODULE
-  list(FIND _cuda_wrap_cmake_options SHARED _cuda_found_SHARED)
-  list(FIND _cuda_wrap_cmake_options MODULE _cuda_found_MODULE)
-  if(_cuda_found_SHARED GREATER -1 OR _cuda_found_MODULE GREATER -1)
-    set(_cuda_build_shared_libs TRUE)
-  endif()
-  # STATIC
-  list(FIND _cuda_wrap_cmake_options STATIC _cuda_found_STATIC)
-  if(_cuda_found_STATIC GREATER -1)
-    set(_cuda_build_shared_libs FALSE)
-  endif()
-
-  # CUDA_HOST_FLAGS
-  if(_cuda_build_shared_libs)
-    # If we are setting up code for a shared library, then we need to add extra flags for
-    # compiling objects for shared libraries.
-    set(CUDA_HOST_SHARED_FLAGS ${CMAKE_SHARED_LIBRARY_${CUDA_C_OR_CXX}_FLAGS})
-  else()
-    set(CUDA_HOST_SHARED_FLAGS)
-  endif()
-  # Only add the CMAKE_{C,CXX}_FLAGS if we are propagating host flags.  We
-  # always need to set the SHARED_FLAGS, though.
-  if(CUDA_PROPAGATE_HOST_FLAGS)
-    set(_cuda_host_flags "set(CMAKE_HOST_FLAGS ${CMAKE_${CUDA_C_OR_CXX}_FLAGS} ${CUDA_HOST_SHARED_FLAGS})")
-  else()
-    set(_cuda_host_flags "set(CMAKE_HOST_FLAGS ${CUDA_HOST_SHARED_FLAGS})")
-  endif()
-
-  set(_cuda_nvcc_flags_config "# Build specific configuration flags")
-  # Loop over all the configuration types to generate appropriate flags for run_nvcc.cmake
-  foreach(config ${CUDA_configuration_types})
-    string(TOUPPER ${config} config_upper)
-    # CMAKE_FLAGS are strings and not lists.  By not putting quotes around CMAKE_FLAGS
-    # we convert the strings to lists (like we want).
-
-    if(CUDA_PROPAGATE_HOST_FLAGS)
-      # nvcc chokes on -g3 in versions previous to 3.0, so replace it with -g
-      set(_cuda_fix_g3 FALSE)
-
-      if(CMAKE_COMPILER_IS_GNUCC)
-        if (CUDA_VERSION VERSION_LESS  "3.0" OR
-            CUDA_VERSION VERSION_EQUAL "4.1" OR
-            CUDA_VERSION VERSION_EQUAL "4.2"
-            )
-          set(_cuda_fix_g3 TRUE)
-        endif()
-      endif()
-      if(_cuda_fix_g3)
-        string(REPLACE "-g3" "-g" _cuda_C_FLAGS "${CMAKE_${CUDA_C_OR_CXX}_FLAGS_${config_upper}}")
-      else()
-        set(_cuda_C_FLAGS "${CMAKE_${CUDA_C_OR_CXX}_FLAGS_${config_upper}}")
-      endif()
-
-      set(_cuda_host_flags "${_cuda_host_flags}\nset(CMAKE_HOST_FLAGS_${config_upper} ${_cuda_C_FLAGS})")
-    endif()
-
-    # Note that if we ever want CUDA_NVCC_FLAGS_<CONFIG> to be string (instead of a list
-    # like it is currently), we can remove the quotes around the
-    # ${CUDA_NVCC_FLAGS_${config_upper}} variable like the CMAKE_HOST_FLAGS_<CONFIG> variable.
-    set(_cuda_nvcc_flags_config "${_cuda_nvcc_flags_config}\nset(CUDA_NVCC_FLAGS_${config_upper} ${CUDA_NVCC_FLAGS_${config_upper}} ;; ${CUDA_WRAP_OPTION_NVCC_FLAGS_${config_upper}})")
-  endforeach()
-
-  # Process the C++11 flag.  If the host sets the flag, we need to add it to nvcc and
-  # remove it from the host. This is because -Xcompile -std=c++ will choke nvcc (it uses
-  # the C preprocessor).  In order to get this to work correctly, we need to use nvcc's
-  # specific c++11 flag.
-  if( "${_cuda_host_flags}" MATCHES "-std=c\\+\\+11")
-    # Add the c++11 flag to nvcc if it isn't already present.  Note that we only look at
-    # the main flag instead of the configuration specific flags.
-    if( NOT "${CUDA_NVCC_FLAGS}" MATCHES "-std;c\\+\\+11" )
-      list(APPEND nvcc_flags --std c++11)
-    endif()
-    string(REGEX REPLACE "[-]+std=c\\+\\+11" "" _cuda_host_flags "${_cuda_host_flags}")
-  endif()
-
-  # Get the list of definitions from the directory property
-  get_directory_property(CUDA_NVCC_DEFINITIONS COMPILE_DEFINITIONS)
-  if(CUDA_NVCC_DEFINITIONS)
-    foreach(_definition ${CUDA_NVCC_DEFINITIONS})
-      list(APPEND nvcc_flags "-D${_definition}")
-    endforeach()
-  endif()
-
-  if(_cuda_build_shared_libs)
-    list(APPEND nvcc_flags "-D${cuda_target}_EXPORTS")
-  endif()
-
-  # Reset the output variable
-  set(_cuda_wrap_generated_files "")
-
-  # Iterate over the macro arguments and create custom
-  # commands for all the .cu files.
-  foreach(file ${ARGN})
-    # Ignore any file marked as a HEADER_FILE_ONLY
-    get_source_file_property(_is_header ${file} HEADER_FILE_ONLY)
-    # Allow per source file overrides of the format.  Also allows compiling non-.cu files.
-    get_source_file_property(_cuda_source_format ${file} CUDA_SOURCE_PROPERTY_FORMAT)
-    if((${file} MATCHES "\\.cu$" OR _cuda_source_format) AND NOT _is_header)
-
-      if(NOT _cuda_source_format)
-        set(_cuda_source_format ${format})
-      endif()
-      # If file isn't a .cu file, we need to tell nvcc to treat it as such.
-      if(NOT ${file} MATCHES "\\.cu$")
-        set(cuda_language_flag -x=cu)
-      else()
-        set(cuda_language_flag)
-      endif()
-
-      if( ${_cuda_source_format} MATCHES "OBJ")
-        set( cuda_compile_to_external_module OFF )
-      else()
-        set( cuda_compile_to_external_module ON )
-        if( ${_cuda_source_format} MATCHES "PTX" )
-          set( cuda_compile_to_external_module_type "ptx" )
-        elseif( ${_cuda_source_format} MATCHES "CUBIN")
-          set( cuda_compile_to_external_module_type "cubin" )
-        elseif( ${_cuda_source_format} MATCHES "FATBIN")
-          set( cuda_compile_to_external_module_type "fatbin" )
-        else()
-          message( FATAL_ERROR "Invalid format flag passed to CUDA_WRAP_SRCS or set with CUDA_SOURCE_PROPERTY_FORMAT file property for file '${file}': '${_cuda_source_format}'.  Use OBJ, PTX, CUBIN or FATBIN.")
-        endif()
-      endif()
-
-      if(cuda_compile_to_external_module)
-        # Don't use any of the host compilation flags for PTX targets.
-        set(CUDA_HOST_FLAGS)
-        set(CUDA_NVCC_FLAGS_CONFIG)
-      else()
-        set(CUDA_HOST_FLAGS ${_cuda_host_flags})
-        set(CUDA_NVCC_FLAGS_CONFIG ${_cuda_nvcc_flags_config})
-      endif()
-
-      # Determine output directory
-      cuda_compute_build_path("${file}" cuda_build_path)
-      set(cuda_compile_intermediate_directory "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${cuda_target}.dir/${cuda_build_path}")
-      if(CUDA_GENERATED_OUTPUT_DIR)
-        set(cuda_compile_output_dir "${CUDA_GENERATED_OUTPUT_DIR}")
-      else()
-        if ( cuda_compile_to_external_module )
-          set(cuda_compile_output_dir "${CMAKE_CURRENT_BINARY_DIR}")
-        else()
-          set(cuda_compile_output_dir "${cuda_compile_intermediate_directory}")
-        endif()
-      endif()
-
-      # Add a custom target to generate a c or ptx file. ######################
-
-      get_filename_component( basename ${file} NAME )
-      if( cuda_compile_to_external_module )
-        set(generated_file_path "${cuda_compile_output_dir}")
-        set(generated_file_basename "${cuda_target}_generated_${basename}.${cuda_compile_to_external_module_type}")
-        set(format_flag "-${cuda_compile_to_external_module_type}")
-        file(MAKE_DIRECTORY "${cuda_compile_output_dir}")
-      else()
-        set(generated_file_path "${cuda_compile_output_dir}/${CMAKE_CFG_INTDIR}")
-        set(generated_file_basename "${cuda_target}_generated_${basename}${generated_extension}")
-        if(CUDA_SEPARABLE_COMPILATION)
-          set(format_flag "-dc")
-        else()
-          set(format_flag "-c")
-        endif()
-      endif()
-
-      # Set all of our file names.  Make sure that whatever filenames that have
-      # generated_file_path in them get passed in through as a command line
-      # argument, so that the ${CMAKE_CFG_INTDIR} gets expanded at run time
-      # instead of configure time.
-      set(generated_file "${generated_file_path}/${generated_file_basename}")
-      set(cmake_dependency_file "${cuda_compile_intermediate_directory}/${generated_file_basename}.depend")
-      set(NVCC_generated_dependency_file "${cuda_compile_intermediate_directory}/${generated_file_basename}.NVCC-depend")
-      set(generated_cubin_file "${generated_file_path}/${generated_file_basename}.cubin.txt")
-      set(custom_target_script "${cuda_compile_intermediate_directory}/${generated_file_basename}.cmake")
-
-      # Setup properties for obj files:
-      if( NOT cuda_compile_to_external_module )
-        set_source_files_properties("${generated_file}"
-          PROPERTIES
-          EXTERNAL_OBJECT true # This is an object file not to be compiled, but only be linked.
-          )
-      endif()
-
-      # Don't add CMAKE_CURRENT_SOURCE_DIR if the path is already an absolute path.
-      get_filename_component(file_path "${file}" PATH)
-      if(IS_ABSOLUTE "${file_path}")
-        set(source_file "${file}")
-      else()
-        set(source_file "${CMAKE_CURRENT_SOURCE_DIR}/${file}")
-      endif()
-
-      if( NOT cuda_compile_to_external_module AND CUDA_SEPARABLE_COMPILATION)
-        list(APPEND ${cuda_target}_SEPARABLE_COMPILATION_OBJECTS "${generated_file}")
-      endif()
-
-      # Bring in the dependencies.  Creates a variable CUDA_NVCC_DEPEND #######
-      cuda_include_nvcc_dependencies(${cmake_dependency_file})
-
-      # Convience string for output ###########################################
-      if(CUDA_BUILD_EMULATION)
-        set(cuda_build_type "Emulation")
-      else()
-        set(cuda_build_type "Device")
-      endif()
-
-      # Build the NVCC made dependency file ###################################
-      set(build_cubin OFF)
-      if ( NOT CUDA_BUILD_EMULATION AND CUDA_BUILD_CUBIN )
-         if ( NOT cuda_compile_to_external_module )
-           set ( build_cubin ON )
-         endif()
-      endif()
-
-      # Configure the build script
-      configure_file("${CUDA_run_nvcc}" "${custom_target_script}" @ONLY)
-
-      # So if a user specifies the same cuda file as input more than once, you
-      # can have bad things happen with dependencies.  Here we check an option
-      # to see if this is the behavior they want.
-      if(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE)
-        set(main_dep MAIN_DEPENDENCY ${source_file})
-      else()
-        set(main_dep DEPENDS ${source_file})
-      endif()
-
-      if(CUDA_VERBOSE_BUILD)
-        set(verbose_output ON)
-      elseif(CMAKE_GENERATOR MATCHES "Makefiles")
-        set(verbose_output "$(VERBOSE)")
-      else()
-        set(verbose_output OFF)
-      endif()
-
-      # Create up the comment string
-      file(RELATIVE_PATH generated_file_relative_path "${CMAKE_BINARY_DIR}" "${generated_file}")
-      if(cuda_compile_to_external_module)
-        set(cuda_build_comment_string "Building NVCC ${cuda_compile_to_external_module_type} file ${generated_file_relative_path}")
-      else()
-        set(cuda_build_comment_string "Building NVCC (${cuda_build_type}) object ${generated_file_relative_path}")
-      endif()
-
-      # Build the generated file and dependency file ##########################
-      add_custom_command(
-        OUTPUT ${generated_file}
-        # These output files depend on the source_file and the contents of cmake_dependency_file
-        ${main_dep}
-        DEPENDS ${CUDA_NVCC_DEPEND}
-        DEPENDS ${custom_target_script}
-        # Make sure the output directory exists before trying to write to it.
-        COMMAND ${CMAKE_COMMAND} -E make_directory "${generated_file_path}"
-        COMMAND ${CMAKE_COMMAND} ARGS
-          -D verbose:BOOL=${verbose_output}
-          ${ccbin_flags}
-          -D build_configuration:STRING=${CUDA_build_configuration}
-          -D "generated_file:STRING=${generated_file}"
-          -D "generated_cubin_file:STRING=${generated_cubin_file}"
-          -P "${custom_target_script}"
-        WORKING_DIRECTORY "${cuda_compile_intermediate_directory}"
-        COMMENT "${cuda_build_comment_string}"
-        )
-
-      # Make sure the build system knows the file is generated.
-      set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE)
-
-      list(APPEND _cuda_wrap_generated_files ${generated_file})
-
-      # Add the other files that we want cmake to clean on a cleanup ##########
-      list(APPEND CUDA_ADDITIONAL_CLEAN_FILES "${cmake_dependency_file}")
-      list(REMOVE_DUPLICATES CUDA_ADDITIONAL_CLEAN_FILES)
-      set(CUDA_ADDITIONAL_CLEAN_FILES ${CUDA_ADDITIONAL_CLEAN_FILES} CACHE INTERNAL "List of intermediate files that are part of the cuda dependency scanning.")
-
-    endif()
-  endforeach()
-
-  # Set the return parameter
-  set(${generated_files} ${_cuda_wrap_generated_files})
-endmacro()
-
-function(_cuda_get_important_host_flags important_flags flag_string)
-  if(CMAKE_GENERATOR MATCHES "Visual Studio")
-    string(REGEX MATCHALL "/M[DT][d]?" flags "${flag_string}")
-    list(APPEND ${important_flags} ${flags})
-  else()
-    string(REGEX MATCHALL "-fPIC" flags "${flag_string}")
-    list(APPEND ${important_flags} ${flags})
-  endif()
-  set(${important_flags} ${${important_flags}} PARENT_SCOPE)
-endfunction()
-
-###############################################################################
-###############################################################################
-# Separable Compilation Link
-###############################################################################
-###############################################################################
-
-# Compute the filename to be used by CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS
-function(CUDA_COMPUTE_SEPARABLE_COMPILATION_OBJECT_FILE_NAME output_file_var cuda_target object_files)
-  if (object_files)
-    set(generated_extension ${CMAKE_${CUDA_C_OR_CXX}_OUTPUT_EXTENSION})
-    set(output_file "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${cuda_target}.dir/${CMAKE_CFG_INTDIR}/${cuda_target}_intermediate_link${generated_extension}")
-  else()
-    set(output_file)
-  endif()
-
-  set(${output_file_var} "${output_file}" PARENT_SCOPE)
-endfunction()
-
-# Setup the build rule for the separable compilation intermediate link file.
-function(CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS output_file cuda_target options object_files)
-  if (object_files)
-
-    set_source_files_properties("${output_file}"
-      PROPERTIES
-      EXTERNAL_OBJECT TRUE # This is an object file not to be compiled, but only
-                           # be linked.
-      GENERATED TRUE       # This file is generated during the build
-      )
-
-    # For now we are ignoring all the configuration specific flags.
-    set(nvcc_flags)
-    CUDA_PARSE_NVCC_OPTIONS(nvcc_flags ${options})
-    if(CUDA_64_BIT_DEVICE_CODE)
-      list(APPEND nvcc_flags -m64)
-    else()
-      list(APPEND nvcc_flags -m32)
-    endif()
-    # If -ccbin, --compiler-bindir has been specified, don't do anything.  Otherwise add it here.
-    list( FIND nvcc_flags "-ccbin" ccbin_found0 )
-    list( FIND nvcc_flags "--compiler-bindir" ccbin_found1 )
-    if( ccbin_found0 LESS 0 AND ccbin_found1 LESS 0 AND CUDA_HOST_COMPILER )
-      list(APPEND nvcc_flags -ccbin "\"${CUDA_HOST_COMPILER}\"")
-    endif()
-
-    # Create a list of flags specified by CUDA_NVCC_FLAGS_${CONFIG} and CMAKE_${CUDA_C_OR_CXX}_FLAGS*
-    set(config_specific_flags)
-    set(flags)
-    foreach(config ${CUDA_configuration_types})
-      string(TOUPPER ${config} config_upper)
-      # Add config specific flags
-      foreach(f ${CUDA_NVCC_FLAGS_${config_upper}})
-        list(APPEND config_specific_flags $<$<CONFIG:${config}>:${f}>)
-      endforeach()
-      set(important_host_flags)
-      _cuda_get_important_host_flags(important_host_flags "${CMAKE_${CUDA_C_OR_CXX}_FLAGS_${config_upper}}")
-      foreach(f ${important_host_flags})
-        list(APPEND flags $<$<CONFIG:${config}>:-Xcompiler> $<$<CONFIG:${config}>:${f}>)
-      endforeach()
-    endforeach()
-    # Add CMAKE_${CUDA_C_OR_CXX}_FLAGS
-    set(important_host_flags)
-    _cuda_get_important_host_flags(important_host_flags "${CMAKE_${CUDA_C_OR_CXX}_FLAGS}")
-    foreach(f ${important_host_flags})
-      list(APPEND flags -Xcompiler ${f})
-    endforeach()
-
-    # Add our general CUDA_NVCC_FLAGS with the configuration specifig flags
-    set(nvcc_flags ${CUDA_NVCC_FLAGS} ${config_specific_flags} ${nvcc_flags})
-
-    file(RELATIVE_PATH output_file_relative_path "${CMAKE_BINARY_DIR}" "${output_file}")
-
-    # Some generators don't handle the multiple levels of custom command
-    # dependencies correctly (obj1 depends on file1, obj2 depends on obj1), so
-    # we work around that issue by compiling the intermediate link object as a
-    # pre-link custom command in that situation.
-    set(do_obj_build_rule TRUE)
-    if (MSVC_VERSION GREATER 1599 AND MSVC_VERSION LESS 1800)
-      # VS 2010 and 2012 have this problem.
-      set(do_obj_build_rule FALSE)
-    endif()
-
-    if (do_obj_build_rule)
-      add_custom_command(
-        OUTPUT ${output_file}
-        DEPENDS ${object_files}
-        COMMAND ${CUDA_NVCC_EXECUTABLE} ${nvcc_flags} -dlink ${object_files} -o ${output_file}
-        ${flags}
-        COMMENT "Building NVCC intermediate link file ${output_file_relative_path}"
-        )
-    else()
-      get_filename_component(output_file_dir "${output_file}" DIRECTORY)
-      add_custom_command(
-        TARGET ${cuda_target}
-        PRE_LINK
-        COMMAND ${CMAKE_COMMAND} -E echo "Building NVCC intermediate link file ${output_file_relative_path}"
-        COMMAND ${CMAKE_COMMAND} -E make_directory "${output_file_dir}"
-        COMMAND ${CUDA_NVCC_EXECUTABLE} ${nvcc_flags} ${flags} -dlink ${object_files} -o "${output_file}"
-        )
-    endif()
- endif()
-endfunction()
-
-###############################################################################
-###############################################################################
-# ADD LIBRARY
-###############################################################################
-###############################################################################
-macro(CUDA_ADD_LIBRARY cuda_target)
-
-  CUDA_ADD_CUDA_INCLUDE_ONCE()
-
-  # Separate the sources from the options
-  CUDA_GET_SOURCES_AND_OPTIONS(_sources _cmake_options _options ${ARGN})
-  CUDA_BUILD_SHARED_LIBRARY(_cuda_shared_flag ${ARGN})
-  # Create custom commands and targets for each file.
-  CUDA_WRAP_SRCS( ${cuda_target} OBJ _generated_files ${_sources}
-    ${_cmake_options} ${_cuda_shared_flag}
-    OPTIONS ${_options} )
-
-  # Compute the file name of the intermedate link file used for separable
-  # compilation.
-  CUDA_COMPUTE_SEPARABLE_COMPILATION_OBJECT_FILE_NAME(link_file ${cuda_target} "${${cuda_target}_SEPARABLE_COMPILATION_OBJECTS}")
-
-  # Add the library.
-  add_library(${cuda_target} ${_cmake_options}
-    ${_generated_files}
-    ${_sources}
-    ${link_file}
-    )
-
-  # Add a link phase for the separable compilation if it has been enabled.  If
-  # it has been enabled then the ${cuda_target}_SEPARABLE_COMPILATION_OBJECTS
-  # variable will have been defined.
-  CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS("${link_file}" ${cuda_target} "${_options}" "${${cuda_target}_SEPARABLE_COMPILATION_OBJECTS}")
-
-  target_link_libraries(${cuda_target}
-    ${CUDA_LIBRARIES}
-    )
-
-  # We need to set the linker language based on what the expected generated file
-  # would be. CUDA_C_OR_CXX is computed based on CUDA_HOST_COMPILATION_CPP.
-  set_target_properties(${cuda_target}
-    PROPERTIES
-    LINKER_LANGUAGE ${CUDA_C_OR_CXX}
-    )
-
-endmacro()
-
-
-###############################################################################
-###############################################################################
-# ADD EXECUTABLE
-###############################################################################
-###############################################################################
-macro(CUDA_ADD_EXECUTABLE cuda_target)
-
-  CUDA_ADD_CUDA_INCLUDE_ONCE()
-
-  # Separate the sources from the options
-  CUDA_GET_SOURCES_AND_OPTIONS(_sources _cmake_options _options ${ARGN})
-  # Create custom commands and targets for each file.
-  CUDA_WRAP_SRCS( ${cuda_target} OBJ _generated_files ${_sources} OPTIONS ${_options} )
-
-  # Compute the file name of the intermedate link file used for separable
-  # compilation.
-  CUDA_COMPUTE_SEPARABLE_COMPILATION_OBJECT_FILE_NAME(link_file ${cuda_target} "${${cuda_target}_SEPARABLE_COMPILATION_OBJECTS}")
-
-  # Add the library.
-  add_executable(${cuda_target} ${_cmake_options}
-    ${_generated_files}
-    ${_sources}
-    ${link_file}
-    )
-
-  # Add a link phase for the separable compilation if it has been enabled.  If
-  # it has been enabled then the ${cuda_target}_SEPARABLE_COMPILATION_OBJECTS
-  # variable will have been defined.
-  CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS("${link_file}" ${cuda_target} "${_options}" "${${cuda_target}_SEPARABLE_COMPILATION_OBJECTS}")
-
-  target_link_libraries(${cuda_target}
-    ${CUDA_LIBRARIES}
-    )
-
-  # We need to set the linker language based on what the expected generated file
-  # would be. CUDA_C_OR_CXX is computed based on CUDA_HOST_COMPILATION_CPP.
-  set_target_properties(${cuda_target}
-    PROPERTIES
-    LINKER_LANGUAGE ${CUDA_C_OR_CXX}
-    )
-
-endmacro()
-
-
-###############################################################################
-###############################################################################
-# (Internal) helper for manually added cuda source files with specific targets
-###############################################################################
-###############################################################################
-macro(cuda_compile_base cuda_target format generated_files)
-
-  # Separate the sources from the options
-  CUDA_GET_SOURCES_AND_OPTIONS(_sources _cmake_options _options ${ARGN})
-  # Create custom commands and targets for each file.
-  CUDA_WRAP_SRCS( ${cuda_target} ${format} _generated_files ${_sources} ${_cmake_options}
-    OPTIONS ${_options} )
-
-  set( ${generated_files} ${_generated_files})
-
-endmacro()
-
-###############################################################################
-###############################################################################
-# CUDA COMPILE
-###############################################################################
-###############################################################################
-macro(CUDA_COMPILE generated_files)
-  cuda_compile_base(cuda_compile OBJ ${generated_files} ${ARGN})
-endmacro()
-
-###############################################################################
-###############################################################################
-# CUDA COMPILE PTX
-###############################################################################
-###############################################################################
-macro(CUDA_COMPILE_PTX generated_files)
-  cuda_compile_base(cuda_compile_ptx PTX ${generated_files} ${ARGN})
-endmacro()
-
-###############################################################################
-###############################################################################
-# CUDA COMPILE FATBIN
-###############################################################################
-###############################################################################
-macro(CUDA_COMPILE_FATBIN generated_files)
-  cuda_compile_base(cuda_compile_fatbin FATBIN ${generated_files} ${ARGN})
-endmacro()
-
-###############################################################################
-###############################################################################
-# CUDA COMPILE CUBIN
-###############################################################################
-###############################################################################
-macro(CUDA_COMPILE_CUBIN generated_files)
-  cuda_compile_base(cuda_compile_cubin CUBIN ${generated_files} ${ARGN})
-endmacro()
-
-
-###############################################################################
-###############################################################################
-# CUDA ADD CUFFT TO TARGET
-###############################################################################
-###############################################################################
-macro(CUDA_ADD_CUFFT_TO_TARGET target)
-  if (CUDA_BUILD_EMULATION)
-    target_link_libraries(${target} ${CUDA_cufftemu_LIBRARY})
-  else()
-    target_link_libraries(${target} ${CUDA_cufft_LIBRARY})
-  endif()
-endmacro()
-
-###############################################################################
-###############################################################################
-# CUDA ADD CUBLAS TO TARGET
-###############################################################################
-###############################################################################
-macro(CUDA_ADD_CUBLAS_TO_TARGET target)
-  if (CUDA_BUILD_EMULATION)
-    target_link_libraries(${target} ${CUDA_cublasemu_LIBRARY})
-  else()
-    target_link_libraries(${target} ${CUDA_cublas_LIBRARY})
-  endif()
-endmacro()
-
-###############################################################################
-###############################################################################
-# CUDA BUILD CLEAN TARGET
-###############################################################################
-###############################################################################
-macro(CUDA_BUILD_CLEAN_TARGET)
-  # Call this after you add all your CUDA targets, and you will get a convience
-  # target.  You should also make clean after running this target to get the
-  # build system to generate all the code again.
-
-  set(cuda_clean_target_name clean_cuda_depends)
-  if (CMAKE_GENERATOR MATCHES "Visual Studio")
-    string(TOUPPER ${cuda_clean_target_name} cuda_clean_target_name)
-  endif()
-  add_custom_target(${cuda_clean_target_name}
-    COMMAND ${CMAKE_COMMAND} -E remove ${CUDA_ADDITIONAL_CLEAN_FILES})
-
-  # Clear out the variable, so the next time we configure it will be empty.
-  # This is useful so that the files won't persist in the list after targets
-  # have been removed.
-  set(CUDA_ADDITIONAL_CLEAN_FILES "" CACHE INTERNAL "List of intermediate files that are part of the cuda dependency scanning.")
-endmacro()
diff --git a/cmake/FindCUDA/make2cmake.cmake b/cmake/FindCUDA/make2cmake.cmake
deleted file mode 100644 (file)
index c433fa8..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#  James Bigler, NVIDIA Corp (nvidia.com - jbigler)
-#  Abe Stephens, SCI Institute -- http://www.sci.utah.edu/~abe/FindCuda.html
-#
-#  Copyright (c) 2008 - 2009 NVIDIA Corporation.  All rights reserved.
-#
-#  Copyright (c) 2007-2009
-#  Scientific Computing and Imaging Institute, University of Utah
-#
-#  This code is licensed under the MIT License.  See the FindCUDA.cmake script
-#  for the text of the license.
-
-# The MIT License
-#
-# License for the specific language governing rights and limitations under
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-#
-
-#######################################################################
-# This converts a file written in makefile syntax into one that can be included
-# by CMake.
-
-file(READ ${input_file} depend_text)
-
-if (NOT "${depend_text}" STREQUAL "")
-
-  # message("FOUND DEPENDS")
-
-  string(REPLACE "\\ " " " depend_text ${depend_text})
-
-  # This works for the nvcc -M generated dependency files.
-  string(REGEX REPLACE "^.* : " "" depend_text ${depend_text})
-  string(REGEX REPLACE "[ \\\\]*\n" ";" depend_text ${depend_text})
-
-  set(dependency_list "")
-
-  foreach(file ${depend_text})
-
-    string(REGEX REPLACE "^ +" "" file ${file})
-
-    # OK, now if we had a UNC path, nvcc has a tendency to only output the first '/'
-    # instead of '//'.  Here we will test to see if the file exists, if it doesn't then
-    # try to prepend another '/' to the path and test again.  If it still fails remove the
-    # path.
-
-    if(NOT EXISTS "${file}")
-      if (EXISTS "/${file}")
-        set(file "/${file}")
-      else()
-        message(WARNING " Removing non-existent dependency file: ${file}")
-        set(file "")
-      endif()
-    endif()
-
-    if(NOT IS_DIRECTORY "${file}")
-      # If softlinks start to matter, we should change this to REALPATH.  For now we need
-      # to flatten paths, because nvcc can generate stuff like /bin/../include instead of
-      # just /include.
-      get_filename_component(file_absolute "${file}" ABSOLUTE)
-      list(APPEND dependency_list "${file_absolute}")
-    endif()
-
-  endforeach()
-
-else()
-  # message("FOUND NO DEPENDS")
-endif()
-
-# Remove the duplicate entries and sort them.
-list(REMOVE_DUPLICATES dependency_list)
-list(SORT dependency_list)
-
-foreach(file ${dependency_list})
-  set(cuda_nvcc_depend "${cuda_nvcc_depend} \"${file}\"\n")
-endforeach()
-
-file(WRITE ${output_file} "# Generated by: make2cmake.cmake\nSET(CUDA_NVCC_DEPEND\n ${cuda_nvcc_depend})\n\n")
diff --git a/cmake/FindCUDA/parse_cubin.cmake b/cmake/FindCUDA/parse_cubin.cmake
deleted file mode 100644 (file)
index 626c8a2..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-#  James Bigler, NVIDIA Corp (nvidia.com - jbigler)
-#  Abe Stephens, SCI Institute -- http://www.sci.utah.edu/~abe/FindCuda.html
-#
-#  Copyright (c) 2008 - 2009 NVIDIA Corporation.  All rights reserved.
-#
-#  Copyright (c) 2007-2009
-#  Scientific Computing and Imaging Institute, University of Utah
-#
-#  This code is licensed under the MIT License.  See the FindCUDA.cmake script
-#  for the text of the license.
-
-# The MIT License
-#
-# License for the specific language governing rights and limitations under
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-#
-
-#######################################################################
-# Parses a .cubin file produced by nvcc and reports statistics about the file.
-
-
-file(READ ${input_file} file_text)
-
-if (NOT "${file_text}" STREQUAL "")
-
-  string(REPLACE ";" "\\;" file_text ${file_text})
-  string(REPLACE "\ncode" ";code" file_text ${file_text})
-
-  list(LENGTH file_text len)
-
-  foreach(line ${file_text})
-
-    # Only look at "code { }" blocks.
-    if(line MATCHES "^code")
-
-      # Break into individual lines.
-      string(REGEX REPLACE "\n" ";" line ${line})
-
-      foreach(entry ${line})
-
-        # Extract kernel names.
-        if (${entry} MATCHES "[^g]name = ([^ ]+)")
-          set(entry "${CMAKE_MATCH_1}")
-
-          # Check to see if the kernel name starts with "_"
-          set(skip FALSE)
-          # if (${entry} MATCHES "^_")
-            # Skip the rest of this block.
-            # message("Skipping ${entry}")
-            # set(skip TRUE)
-          # else ()
-            message("Kernel:    ${entry}")
-          # endif ()
-
-        endif()
-
-        # Skip the rest of the block if necessary
-        if(NOT skip)
-
-          # Registers
-          if (${entry} MATCHES "reg([ ]+)=([ ]+)([^ ]+)")
-            set(entry "${CMAKE_MATCH_3}")
-            message("Registers: ${entry}")
-          endif()
-
-          # Local memory
-          if (${entry} MATCHES "lmem([ ]+)=([ ]+)([^ ]+)")
-            set(entry "${CMAKE_MATCH_3}")
-            message("Local:     ${entry}")
-          endif()
-
-          # Shared memory
-          if (${entry} MATCHES "smem([ ]+)=([ ]+)([^ ]+)")
-            set(entry "${CMAKE_MATCH_3}")
-            message("Shared:    ${entry}")
-          endif()
-
-          if (${entry} MATCHES "^}")
-            message("")
-          endif()
-
-        endif()
-
-
-      endforeach()
-
-    endif()
-
-  endforeach()
-
-else()
-  # message("FOUND NO DEPENDS")
-endif()
-
-
diff --git a/cmake/FindCUDA/run_nvcc.cmake b/cmake/FindCUDA/run_nvcc.cmake
deleted file mode 100644 (file)
index 8032309..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-#  James Bigler, NVIDIA Corp (nvidia.com - jbigler)
-#
-#  Copyright (c) 2008 - 2009 NVIDIA Corporation.  All rights reserved.
-#
-#  This code is licensed under the MIT License.  See the FindCUDA.cmake script
-#  for the text of the license.
-
-# The MIT License
-#
-# License for the specific language governing rights and limitations under
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-
-
-##########################################################################
-# This file runs the nvcc commands to produce the desired output file along with
-# the dependency file needed by CMake to compute dependencies.  In addition the
-# file checks the output of each command and if the command fails it deletes the
-# output files.
-
-# Input variables
-#
-# verbose:BOOL=<>          OFF: Be as quiet as possible (default)
-#                          ON : Describe each step
-#
-# build_configuration:STRING=<> Typically one of Debug, MinSizeRel, Release, or
-#                               RelWithDebInfo, but it should match one of the
-#                               entries in CUDA_HOST_FLAGS. This is the build
-#                               configuration used when compiling the code.  If
-#                               blank or unspecified Debug is assumed as this is
-#                               what CMake does.
-#
-# generated_file:STRING=<> File to generate.  This argument must be passed in.
-#
-# generated_cubin_file:STRING=<> File to generate.  This argument must be passed
-#                                                   in if build_cubin is true.
-
-if(NOT generated_file)
-  message(FATAL_ERROR "You must specify generated_file on the command line")
-endif()
-
-# Set these up as variables to make reading the generated file easier
-set(CMAKE_COMMAND "@CMAKE_COMMAND@") # path
-set(source_file "@source_file@") # path
-set(NVCC_generated_dependency_file "@NVCC_generated_dependency_file@") # path
-set(cmake_dependency_file "@cmake_dependency_file@") # path
-set(CUDA_make2cmake "@CUDA_make2cmake@") # path
-set(CUDA_parse_cubin "@CUDA_parse_cubin@") # path
-set(build_cubin @build_cubin@) # bool
-set(CUDA_HOST_COMPILER "@CUDA_HOST_COMPILER@") # path
-# We won't actually use these variables for now, but we need to set this, in
-# order to force this file to be run again if it changes.
-set(generated_file_path "@generated_file_path@") # path
-set(generated_file_internal "@generated_file@") # path
-set(generated_cubin_file_internal "@generated_cubin_file@") # path
-
-set(CUDA_NVCC_EXECUTABLE "@CUDA_NVCC_EXECUTABLE@") # path
-set(CUDA_NVCC_FLAGS @CUDA_NVCC_FLAGS@ ;; @CUDA_WRAP_OPTION_NVCC_FLAGS@) # list
-@CUDA_NVCC_FLAGS_CONFIG@
-set(nvcc_flags @nvcc_flags@) # list
-set(CUDA_NVCC_INCLUDE_ARGS "@CUDA_NVCC_INCLUDE_ARGS@") # list (needs to be in quotes to handle spaces properly).
-set(format_flag "@format_flag@") # string
-set(cuda_language_flag @cuda_language_flag@) # list
-
-if(build_cubin AND NOT generated_cubin_file)
-  message(FATAL_ERROR "You must specify generated_cubin_file on the command line")
-endif()
-
-# This is the list of host compilation flags.  It C or CXX should already have
-# been chosen by FindCUDA.cmake.
-@CUDA_HOST_FLAGS@
-
-# Take the compiler flags and package them up to be sent to the compiler via -Xcompiler
-set(nvcc_host_compiler_flags "")
-# If we weren't given a build_configuration, use Debug.
-if(NOT build_configuration)
-  set(build_configuration Debug)
-endif()
-string(TOUPPER "${build_configuration}" build_configuration)
-#message("CUDA_NVCC_HOST_COMPILER_FLAGS = ${CUDA_NVCC_HOST_COMPILER_FLAGS}")
-foreach(flag ${CMAKE_HOST_FLAGS} ${CMAKE_HOST_FLAGS_${build_configuration}})
-  # Extra quotes are added around each flag to help nvcc parse out flags with spaces.
-  set(nvcc_host_compiler_flags "${nvcc_host_compiler_flags},\"${flag}\"")
-endforeach()
-if (nvcc_host_compiler_flags)
-  set(nvcc_host_compiler_flags "-Xcompiler" ${nvcc_host_compiler_flags})
-endif()
-#message("nvcc_host_compiler_flags = \"${nvcc_host_compiler_flags}\"")
-# Add the build specific configuration flags
-list(APPEND CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS_${build_configuration}})
-
-# Any -ccbin existing in CUDA_NVCC_FLAGS gets highest priority
-list( FIND CUDA_NVCC_FLAGS "-ccbin" ccbin_found0 )
-list( FIND CUDA_NVCC_FLAGS "--compiler-bindir" ccbin_found1 )
-if( ccbin_found0 LESS 0 AND ccbin_found1 LESS 0 AND CUDA_HOST_COMPILER )
-  if (CUDA_HOST_COMPILER STREQUAL "$(VCInstallDir)bin" AND DEFINED CCBIN)
-    set(CCBIN -ccbin "${CCBIN}")
-  else()
-    set(CCBIN -ccbin "${CUDA_HOST_COMPILER}")
-  endif()
-endif()
-
-# cuda_execute_process - Executes a command with optional command echo and status message.
-#
-#   status  - Status message to print if verbose is true
-#   command - COMMAND argument from the usual execute_process argument structure
-#   ARGN    - Remaining arguments are the command with arguments
-#
-#   CUDA_result - return value from running the command
-#
-# Make this a macro instead of a function, so that things like RESULT_VARIABLE
-# and other return variables are present after executing the process.
-macro(cuda_execute_process status command)
-  set(_command ${command})
-  if(NOT "x${_command}" STREQUAL "xCOMMAND")
-    message(FATAL_ERROR "Malformed call to cuda_execute_process.  Missing COMMAND as second argument. (command = ${command})")
-  endif()
-  if(verbose)
-    execute_process(COMMAND "${CMAKE_COMMAND}" -E echo -- ${status})
-    # Now we need to build up our command string.  We are accounting for quotes
-    # and spaces, anything else is left up to the user to fix if they want to
-    # copy and paste a runnable command line.
-    set(cuda_execute_process_string)
-    foreach(arg ${ARGN})
-      # If there are quotes, excape them, so they come through.
-      string(REPLACE "\"" "\\\"" arg ${arg})
-      # Args with spaces need quotes around them to get them to be parsed as a single argument.
-      if(arg MATCHES " ")
-        list(APPEND cuda_execute_process_string "\"${arg}\"")
-      else()
-        list(APPEND cuda_execute_process_string ${arg})
-      endif()
-    endforeach()
-    # Echo the command
-    execute_process(COMMAND ${CMAKE_COMMAND} -E echo ${cuda_execute_process_string})
-  endif()
-  # Run the command
-  execute_process(COMMAND ${ARGN} RESULT_VARIABLE CUDA_result )
-endmacro()
-
-# Delete the target file
-cuda_execute_process(
-  "Removing ${generated_file}"
-  COMMAND "${CMAKE_COMMAND}" -E remove "${generated_file}"
-  )
-
-# For CUDA 2.3 and below, -G -M doesn't work, so remove the -G flag
-# for dependency generation and hope for the best.
-set(depends_CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS}")
-set(CUDA_VERSION @CUDA_VERSION@)
-if(CUDA_VERSION VERSION_LESS "3.0")
-  cmake_policy(PUSH)
-  # CMake policy 0007 NEW states that empty list elements are not
-  # ignored.  I'm just setting it to avoid the warning that's printed.
-  cmake_policy(SET CMP0007 NEW)
-  # Note that this will remove all occurances of -G.
-  list(REMOVE_ITEM depends_CUDA_NVCC_FLAGS "-G")
-  cmake_policy(POP)
-endif()
-
-# nvcc doesn't define __CUDACC__ for some reason when generating dependency files.  This
-# can cause incorrect dependencies when #including files based on this macro which is
-# defined in the generating passes of nvcc invokation.  We will go ahead and manually
-# define this for now until a future version fixes this bug.
-set(CUDACC_DEFINE -D__CUDACC__)
-
-# Generate the dependency file
-cuda_execute_process(
-  "Generating dependency file: ${NVCC_generated_dependency_file}"
-  COMMAND "${CUDA_NVCC_EXECUTABLE}"
-  -M
-  ${CUDACC_DEFINE}
-  "${source_file}"
-  -o "${NVCC_generated_dependency_file}"
-  ${CCBIN}
-  ${nvcc_flags}
-  ${nvcc_host_compiler_flags}
-  ${depends_CUDA_NVCC_FLAGS}
-  -DNVCC
-  ${CUDA_NVCC_INCLUDE_ARGS}
-  )
-
-if(CUDA_result)
-  message(FATAL_ERROR "Error generating ${generated_file}")
-endif()
-
-# Generate the cmake readable dependency file to a temp file.  Don't put the
-# quotes just around the filenames for the input_file and output_file variables.
-# CMake will pass the quotes through and not be able to find the file.
-cuda_execute_process(
-  "Generating temporary cmake readable file: ${cmake_dependency_file}.tmp"
-  COMMAND "${CMAKE_COMMAND}"
-  -D "input_file:FILEPATH=${NVCC_generated_dependency_file}"
-  -D "output_file:FILEPATH=${cmake_dependency_file}.tmp"
-  -P "${CUDA_make2cmake}"
-  )
-
-if(CUDA_result)
-  message(FATAL_ERROR "Error generating ${generated_file}")
-endif()
-
-# Copy the file if it is different
-cuda_execute_process(
-  "Copy if different ${cmake_dependency_file}.tmp to ${cmake_dependency_file}"
-  COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${cmake_dependency_file}.tmp" "${cmake_dependency_file}"
-  )
-
-if(CUDA_result)
-  message(FATAL_ERROR "Error generating ${generated_file}")
-endif()
-
-# Delete the temporary file
-cuda_execute_process(
-  "Removing ${cmake_dependency_file}.tmp and ${NVCC_generated_dependency_file}"
-  COMMAND "${CMAKE_COMMAND}" -E remove "${cmake_dependency_file}.tmp" "${NVCC_generated_dependency_file}"
-  )
-
-if(CUDA_result)
-  message(FATAL_ERROR "Error generating ${generated_file}")
-endif()
-
-# Generate the code
-cuda_execute_process(
-  "Generating ${generated_file}"
-  COMMAND "${CUDA_NVCC_EXECUTABLE}"
-  "${source_file}"
-  ${cuda_language_flag}
-  ${format_flag} -o "${generated_file}"
-  ${CCBIN}
-  ${nvcc_flags}
-  ${nvcc_host_compiler_flags}
-  ${CUDA_NVCC_FLAGS}
-  -DNVCC
-  ${CUDA_NVCC_INCLUDE_ARGS}
-  )
-
-if(CUDA_result)
-  # Since nvcc can sometimes leave half done files make sure that we delete the output file.
-  cuda_execute_process(
-    "Removing ${generated_file}"
-    COMMAND "${CMAKE_COMMAND}" -E remove "${generated_file}"
-    )
-  message(FATAL_ERROR "Error generating file ${generated_file}")
-else()
-  if(verbose)
-    message("Generated ${generated_file} successfully.")
-  endif()
-endif()
-
-# Cubin resource report commands.
-if( build_cubin )
-  # Run with -cubin to produce resource usage report.
-  cuda_execute_process(
-    "Generating ${generated_cubin_file}"
-    COMMAND "${CUDA_NVCC_EXECUTABLE}"
-    "${source_file}"
-    ${CUDA_NVCC_FLAGS}
-    ${nvcc_flags}
-    ${CCBIN}
-    ${nvcc_host_compiler_flags}
-    -DNVCC
-    -cubin
-    -o "${generated_cubin_file}"
-    ${CUDA_NVCC_INCLUDE_ARGS}
-    )
-
-  # Execute the parser script.
-  cuda_execute_process(
-    "Executing the parser script"
-    COMMAND  "${CMAKE_COMMAND}"
-    -D "input_file:STRING=${generated_cubin_file}"
-    -P "${CUDA_parse_cubin}"
-    )
-
-endif()
index fc8439f2c761ac16fb95bc3e0d643c8d28271250..b1f5827d34ba143e7fe1f8921aa6aaab5eebb754 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -105,56 +106,58 @@ if (${FFTW}_FOUND)
       message(FATAL_ERROR "Could not find ${${FFTW}_FUNCTION_PREFIX}_plan_many_[r2c|c2r] in ${${FFTW}_LIBRARY}, take a look at the error message in ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log to find out what went wrong. If you are using a static lib (.a) make sure you have specified all dependencies of ${${FFTW}_PKG} in ${FFTW}_LIBRARY by hand (e.g. -D${FFTW}_LIBRARY='/path/to/lib${${FFTW}_PKG}.so;/path/to/libm.so') !")
   endif()
 
-  # Check for FFTW3 compiled with --enable-sse
-  foreach(SSE_FUNCTION ${${FFTW}_FUNCTION_PREFIX}_have_simd_sse ${${FFTW}_FUNCTION_PREFIX}_have_sse)
-    if (FFTW_LIBRARY_CHANGED)
-      unset(${FFTW}_HAVE_${SSE_FUNCTION} CACHE)
-    endif()
-    check_library_exists("${${FFTW}_LIBRARIES}" "${SSE_FUNCTION}" "" ${FFTW}_HAVE_${SSE_FUNCTION})
-    if(${FFTW}_HAVE_${SSE_FUNCTION})
-      set(${FFTW}_HAVE_SSE TRUE)
-      set(${FFTW}_HAVE_SIMD TRUE)
-      break()
-    endif()
-  endforeach()
-
-  # Check for FFTW3 compiled with --enable-sse2
-  foreach(SSE2_FUNCTION ${${FFTW}_FUNCTION_PREFIX}_have_simd_sse2 ${${FFTW}_FUNCTION_PREFIX}_have_sse2)
-    if (FFTW_LIBRARY_CHANGED)
-      unset(${FFTW}_HAVE_${SSE2_FUNCTION} CACHE)
-    endif()
-    check_library_exists("${${FFTW}_LIBRARIES}" "${SSE2_FUNCTION}" "" ${FFTW}_HAVE_${SSE2_FUNCTION})
-    if(${FFTW}_HAVE_${SSE2_FUNCTION})
-      set(${FFTW}_HAVE_SSE2 TRUE)
-      set(${FFTW}_HAVE_SIMD TRUE)
-      break()
+  if(NOT WIN32) # *_have_* functions aren't external. Thus aren't visible on Windows.
+    # Check for FFTW3 compiled with --enable-sse
+    foreach(SSE_FUNCTION ${${FFTW}_FUNCTION_PREFIX}_have_simd_sse ${${FFTW}_FUNCTION_PREFIX}_have_sse)
+      if (FFTW_LIBRARY_CHANGED)
+        unset(${FFTW}_HAVE_${SSE_FUNCTION} CACHE)
+      endif()
+      check_library_exists("${${FFTW}_LIBRARIES}" "${SSE_FUNCTION}" "" ${FFTW}_HAVE_${SSE_FUNCTION})
+      if(${FFTW}_HAVE_${SSE_FUNCTION})
+        set(${FFTW}_HAVE_SSE TRUE)
+        set(${FFTW}_HAVE_SIMD TRUE)
+        break()
+      endif()
+    endforeach()
+    
+    # Check for FFTW3 compiled with --enable-sse2
+    foreach(SSE2_FUNCTION ${${FFTW}_FUNCTION_PREFIX}_have_simd_sse2 ${${FFTW}_FUNCTION_PREFIX}_have_sse2)
+      if (FFTW_LIBRARY_CHANGED)
+        unset(${FFTW}_HAVE_${SSE2_FUNCTION} CACHE)
+      endif()
+      check_library_exists("${${FFTW}_LIBRARIES}" "${SSE2_FUNCTION}" "" ${FFTW}_HAVE_${SSE2_FUNCTION})
+      if(${FFTW}_HAVE_${SSE2_FUNCTION})
+        set(${FFTW}_HAVE_SSE2 TRUE)
+        set(${FFTW}_HAVE_SIMD TRUE)
+        break()
+      endif()
+    endforeach()
+    
+    # Check for any other SIMD support in FFTW
+    if (NOT ${FFTW}_HAVE_SIMD)
+        foreach(SIMD_FCT
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx2
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx2_128
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx512
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx_128_fma
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx_512
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_kcvi
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_altivec
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_neon
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_vsx
+                ${${FFTW}_FUNCTION_PREFIX}_have_simd_altivec
+                ${${FFTW}_FUNCTION_PREFIX}_have_altivec) # Name used before FFTW 3.3
+            if (FFTW_LIBRARY_CHANGED)
+                unset(${FFTW}_HAVE_${SIMD_FCT} CACHE)
+            endif()
+            check_library_exists("${${FFTW}_LIBRARIES}" "${SIMD_FCT}" "" ${FFTW}_HAVE_${SIMD_FCT})
+            if(${FFTW}_HAVE_${SIMD_FCT})
+                set(${FFTW}_HAVE_SIMD TRUE)
+                break()
+            endif()
+        endforeach()
     endif()
-  endforeach()
-
-  # Check for any other SIMD support in FFTW
-  if (NOT ${FFTW}_HAVE_SIMD)
-      foreach(SIMD_FCT
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx2
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx2_128
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx512
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx_128_fma
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_avx_512
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_kcvi
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_altivec
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_neon
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_vsx
-              ${${FFTW}_FUNCTION_PREFIX}_have_simd_altivec
-              ${${FFTW}_FUNCTION_PREFIX}_have_altivec) # Name used before FFTW 3.3
-          if (FFTW_LIBRARY_CHANGED)
-              unset(${FFTW}_HAVE_${SIMD_FCT} CACHE)
-          endif()
-          check_library_exists("${${FFTW}_LIBRARIES}" "${SIMD_FCT}" "" ${FFTW}_HAVE_${SIMD_FCT})
-          if(${FFTW}_HAVE_${SIMD_FCT})
-              set(${FFTW}_HAVE_SIMD TRUE)
-              break()
-          endif()
-      endforeach()
   endif()
 
   #Verify FFTW is compiled with fPIC (necessary for shared libraries)
index 0fbc381f2e72d06da83cc06b2d770f9bc3cd1793..2bd7ee368de831d39f472547a27b753d2719517d 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -402,4 +402,4 @@ find_package_handle_standard_args(HWLOC
                                   REQUIRED_VARS HWLOC_LIBRARIES HWLOC_INCLUDE_DIRS
                                   VERSION_VAR HWLOC_VERSION)
 
-mark_as_advanced(HWLOC_INCLUDE_DIRS HWLOC_LIBRARIES HWLOC_VERSION)
+mark_as_advanced(HWLOC_INCLUDE_DIRS HWLOC_LIBRARIES HWLOC_VERSION GMX_HWLOC_API_VERSION)
index ee909f54f41ef9a4c82665448a2648a12d4c55ca..f3566fe0ca871d00e7a85868430fd101bce95e98 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -147,18 +147,20 @@ endif()
 # Now run a sanity check on the compiler using libstdc++, regardless
 # of how it was specified or found.
 
-# Test a feature which was added in libstdc++ 5
-check_cxx_source_compiles("#include <iterator>
-int main() { int a[2]; std::cbegin(a); }" CXX14_COMPILES)
+# Test required 2017 standard library features.
+check_cxx_source_compiles("
+#include <string_view>
+#include <optional>
+int main() { std::string_view(); std::optional<int>(); }" CXX17_COMPILES)
 
-if (NOT CXX14_COMPILES)
+if (NOT CXX17_COMPILES)
     if (NEED_TO_FIND_GPLUSPLUS)
         set (EXTRA_MESSAGE " The g++ found at ${GMX_GPLUSPLUS_PATH} had a suitable version, so "
             "something else must be the problem")
     else()
         set (EXTRA_MESSAGE " Check your toolchain documentation or environment flags so that "
-            "they will find a suitable C++14 standard library")
+            "they will find a suitable C++17 standard library")
     endif()
-    message(FATAL_ERROR "GROMACS requires C++14, but a test of such functionality in the C++ standard "
+    message(FATAL_ERROR "GROMACS requires C++17, but a test of such functionality in the C++ standard "
         "library failed to compile.${EXTRA_MESSAGE}")
 endif()
diff --git a/cmake/FindMuparser.cmake b/cmake/FindMuparser.cmake
new file mode 100644 (file)
index 0000000..9cfbf3d
--- /dev/null
@@ -0,0 +1,102 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+# This package tries to find an external muparser library, version
+# 2.3.
+#
+# MUPARSER_FOUND       - muparser was found
+# MUPARSER_INCLUDE_DIR - muparser include directory
+# MUPARSER_LIBRARY     - muparser library
+# MUPARSER_LINKS_OK    - muparser libraries link correctly
+# MUPARSER_VERSION     - muparser version string as "major.minor"
+#
+# CMake will search the CMAKE_PREFIX_PATH in the usual way, but if you
+# need more control then setting MUPARSER_INCLUDE_DIR and MUPARSER_LIBRARY
+# on the cmake command line to suitable values will work.
+
+include(CMakePushCheckState)
+cmake_push_check_state()
+
+find_package(PkgConfig QUIET)
+if(PKG_CONFIG_FOUND)
+    if(MUPARSER_FIND_VERSION)
+        if(MUPARSER_FIND_VERSION_EXACT)
+            pkg_check_modules(PC_MUPARSER QUIET muparser=${MUPARSER_FIND_VERSION})
+        else()
+            pkg_check_modules(PC_MUPARSER QUIET muparser>=${MUPARSER_FIND_VERSION})
+        endif()
+    else()
+        pkg_check_modules(PC_MUPARSER QUIET muparser)
+        if (PC_MUPARSER_VERSION)
+            string(REGEX REPLACE "^([0-9]+):([0-9]+)" "\\1.\\2" MUPARSER_VERSION "${PC_MUPARSER_VERSION}")
+        endif()
+    endif()
+endif()
+
+# Try to find muparser, perhaps with help from pkg-config
+find_path(MUPARSER_INCLUDE_DIR muParser.h HINTS "${PC_MUPARSER_INCLUDE_DIRS}" PATH_SUFFIXES include)
+find_library(MUPARSER_LIBRARY NAMES muparser HINTS "${PC_MUPARSER_LIBRARY_DIRS}" PATH_SUFFIXES lib64 lib)
+
+# Make sure we can also link, so that cross-compilation is properly supported
+if (MUPARSER_INCLUDE_DIR AND MUPARSER_LIBRARY)
+    include(CheckCXXSourceCompiles)
+    set(CMAKE_REQUIRED_INCLUDES ${MUPARSER_INCLUDE_DIR})
+    set(CMAKE_REQUIRED_LIBRARIES ${MUPARSER_LIBRARY})
+    check_cxx_source_compiles("#include <muParser.h>\nint main(){mu::Parser parser;}" MUPARSER_LINKS_OK)
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(muparser
+    FOUND_VAR
+    MUPARSER_FOUND
+    REQUIRED_VARS
+    MUPARSER_INCLUDE_DIR
+    MUPARSER_LIBRARY
+    MUPARSER_LINKS_OK
+    VERSION_VAR
+    MUPARSER_VERSION)
+
+mark_as_advanced(MUPARSER_INCLUDE_DIR MUPARSER_LIBRARY)
+
+# Make a target that other targets can depend on just like this was a
+# library built in the main project.
+if (MUPARSER_FOUND)
+    add_library(muparser INTERFACE IMPORTED)
+    set_target_properties(muparser PROPERTIES
+        INTERFACE_INCLUDE_DIRECTORIES "${MUPARSER_INCLUDE_DIR}"
+        INTERFACE_LINK_LIBRARIES "${MUPARSER_LIBRARY}"
+        )
+endif()
+
+cmake_pop_check_state()
index 4d000a7e784d4f8c4cddda59344555836f848641..a5328dd8b8e379b80bfa4887f4bf7aa79dfb6734 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2019, by the GROMACS development team, led by
+# Copyright (c) 2015,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -34,7 +34,6 @@
 
 # Adapted from code posted on cmake-users by Mark Moll (the execute_process()
 # call remains, but other things have been rewritten for nicer behavior).
-find_package(PythonInterp 3.5)
 
 function (find_python_module module)
     string(TOUPPER ${module} _module_upper)
index f78aae177d195a7691c075c8d4ec5fd33aa338d2..13bd5d4edc1e026e37dbeb61205e1e3e15378c32 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -65,10 +65,6 @@ if (SPHINX_EXECUTABLE AND NOT DEFINED SPHINX_EXECUTABLE_VERSION)
     set(SPHINX_EXECUTABLE_VERSION ${_version} CACHE INTERNAL "Version of ${SPHINX_EXECUTABLE}")
 endif()
 
-set(_find_deps_options)
-if (Sphinx_FIND_QUIETLY)
-    set(_find_deps_options ERROR_QUIET)
-endif()
 
 if (NOT Sphinx_pygments_FOUND)
     # Check if pygments module is available via the Unix error code (ie. 0
@@ -76,7 +72,7 @@ if (NOT Sphinx_pygments_FOUND)
     execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
         "import pygments"
         RESULT_VARIABLE _pygments_status
-        ${_find_deps_options}
+        ERROR_QUIET
         )
     if (_pygments_status EQUAL 0)
         set(Sphinx_pygments_FOUND TRUE CACHE BOOL "Whether pygments module is available for Sphinx")
diff --git a/cmake/TestARMv7.cpp b/cmake/TestARMv7.cpp
deleted file mode 100644 (file)
index 2b6a3ae..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-int main()
-{
-#ifdef __ARM_ARCH_7A__
-    return 0;
-#else
-#error This compiler is not targetting 32-bit ARMv7
-#endif
-}
diff --git a/cmake/TestARMv7CycleCounters.cpp b/cmake/TestARMv7CycleCounters.cpp
deleted file mode 100644 (file)
index 81b9251..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-int main()
-{
-#if defined(__ARM_ARCH_7A__) && defined(__GNUC__)
-    unsigned int cycles_lo, cycles_hi;
-    asm volatile("mrrc p15, 1, %0, %1, c14" : "=r" (cycles_lo), "=r" (cycles_hi));
-
-    // Return 0 (success) if low or high 32 bits contained anything non-trivial
-    return !(cycles_lo > 0 || cycles_hi > 0);
-#else
-#error This architecture/compiler does not support ARMv7 32-bit cycle counters
-#endif
-}
diff --git a/cmake/TestAtomics.cpp b/cmake/TestAtomics.cpp
deleted file mode 100644 (file)
index b567956..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-
-#define TMPI_CHECK_ATOMICS
-#include "thread_mpi/atomic.h"
-
-int main(void)
-{
-    int i;
-    void *ptr;
-    tMPI_Atomic_t some_atomic;
-    tMPI_Atomic_ptr_t *some_atomic_ptr = NULL;
-    tMPI_Spinlock_t some_spinlock;
-
-    /* Make the compiler actually emit code for these functions, so
-       that things like inability to emit inline assembly get
-       tested. It is not expected that the code below can run. */
-    tMPI_Atomic_memory_barrier();
-    tMPI_Atomic_memory_barrier_acq();
-    tMPI_Atomic_memory_barrier_rel();
-    i = tMPI_Atomic_get(&some_atomic);
-    tMPI_Atomic_set(&some_atomic, 0);
-    ptr = tMPI_Atomic_ptr_get(some_atomic_ptr);
-    tMPI_Atomic_ptr_set(some_atomic_ptr, ptr);
-    tMPI_Atomic_add_return(&some_atomic, 0);
-    tMPI_Atomic_fetch_add(&some_atomic, 0);
-    tMPI_Atomic_cas(&some_atomic, 0, 1);
-    tMPI_Atomic_ptr_cas(some_atomic_ptr, ptr, ptr);
-    tMPI_Atomic_swap(&some_atomic, 0);
-    tMPI_Atomic_ptr_swap(some_atomic_ptr, ptr);
-    tMPI_Spinlock_init(&some_spinlock);
-    tMPI_Spinlock_lock(&some_spinlock);
-    tMPI_Spinlock_trylock(&some_spinlock);
-    tMPI_Spinlock_unlock(&some_spinlock);
-    tMPI_Spinlock_islocked(&some_spinlock);
-    tMPI_Spinlock_wait(&some_spinlock);
-return 0;
-}
index f6f55ab3821bfb0a5481a2cda137daafba8cffb3..b48e6215ca69207b5c1f5ff9281f91d73a074391 100644 (file)
@@ -1,3 +1,7 @@
+#ifdef __CYGWIN__
+    /* Pipes need POSIX things, not just std ones */
+    #define _POSIX_C_SOURCE 200809L
+#endif
 #include <stdio.h>
 
 int
index dc55cf610a5f9becfe669f0b4d324978e57aeae5..c0847c429e7f7eabd9e8c5b5cf3daac4f55fcac7 100644 (file)
@@ -39,13 +39,51 @@ include(CheckCXXSourceCompiles)
 
 # sets TMPI_ATOMICS to 1 if atomic operations are found, unset otherwise
 # Options:
-# include directory for thread_mpi/atomic.h
-MACRO(TMPI_TEST_ATOMICS INCDIR)
+# include path for thread_mpi/atomic.h
+function(TMPI_TEST_ATOMICS INCDIR)
 
     if (NOT DEFINED TMPI_ATOMICS)
-        try_compile(TEST_ATOMICS "${CMAKE_BINARY_DIR}"
-                "${CMAKE_SOURCE_DIR}/cmake/TestAtomics.cpp"
-                COMPILE_DEFINITIONS "-I${INCDIR} -DTMPI_ATOMICS")
+        set(CMAKE_REQUIRED_INCLUDES ${INCDIR})
+        check_cxx_source_compiles("
+// Set a define that forces a compilation error if this platform
+// is not yet supported.
+#define TMPI_CHECK_ATOMICS
+
+// Include the portable atomics implementation to test
+#include \"thread_mpi/atomic.h\"
+
+int main(void)
+{
+    int i;
+    void *ptr;
+    tMPI_Atomic_t some_atomic;
+    tMPI_Atomic_ptr_t *some_atomic_ptr = NULL;
+    tMPI_Spinlock_t some_spinlock;
+
+    /* Make the compiler actually emit code for these functions, so
+       that things like inability to emit inline assembly get
+       tested. It is not expected that the code below can run. */
+    tMPI_Atomic_memory_barrier();
+    tMPI_Atomic_memory_barrier_acq();
+    tMPI_Atomic_memory_barrier_rel();
+    tMPI_Atomic_set(&some_atomic, 0);
+    i   = tMPI_Atomic_get(&some_atomic);
+    ptr = tMPI_Atomic_ptr_get(some_atomic_ptr);
+    tMPI_Atomic_ptr_set(some_atomic_ptr, ptr);
+    tMPI_Atomic_add_return(&some_atomic, 0);
+    tMPI_Atomic_fetch_add(&some_atomic, 0);
+    tMPI_Atomic_cas(&some_atomic, 0, 1);
+    tMPI_Atomic_ptr_cas(some_atomic_ptr, ptr, ptr);
+    tMPI_Atomic_swap(&some_atomic, 0);
+    tMPI_Atomic_ptr_swap(some_atomic_ptr, ptr);
+    tMPI_Spinlock_init(&some_spinlock);
+    tMPI_Spinlock_lock(&some_spinlock);
+    tMPI_Spinlock_trylock(&some_spinlock);
+    tMPI_Spinlock_unlock(&some_spinlock);
+    tMPI_Spinlock_islocked(&some_spinlock);
+    tMPI_Spinlock_wait(&some_spinlock);
+    return 0;
+}" TEST_ATOMICS)
         if (TEST_ATOMICS)
             message(STATUS "Atomic operations found")
             # If the check fails, we want to be able to check again,
@@ -56,11 +94,11 @@ MACRO(TMPI_TEST_ATOMICS INCDIR)
             set(TMPI_ATOMICS_INCDIR ${INCDIR} CACHE INTERNAL "Atomic operations check include dir")
         else ()
             message(STATUS "Atomic operations not found")
-            unset(TEST_ATOMICS)
+            unset(TEST_ATOMICS CACHE)
         endif()
     endif()
 
-ENDMACRO(TMPI_TEST_ATOMICS VARIABLE)
+endfunction()
 
 try_compile(HAVE_PROCESSOR_NUMBER ${CMAKE_BINARY_DIR} "${CMAKE_SOURCE_DIR}/cmake/TestWinProcNum.cpp")
 
@@ -125,7 +163,6 @@ set(TMPI_CXX_LIB 1)
 
 # Turns on thread_mpi MPI functions.
 MACRO(TMPI_ENABLE)
-    TMPI_TEST_ATOMICS(TMPI_ATOMICS_INCDIR)
     if(NOT DEFINED TMPI_ATOMICS)
         message(WARNING "Atomic operations not found for this CPU+compiler combination. Thread support will be unbearably slow: disable threads. Atomic operations should work on all but the most obscure CPU+compiler combinations; if your system is not obscure -- like, for example, x86 with gcc --  please contact the developers.")
     endif()
index 0896fdf91167e8a0b2eea35c1a50b0bb6dcb5b5c..5d854e3ab0e0170bc5cff8574136a5dee8c1c0d9 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2016,2018, by the GROMACS development team, led by
+# Copyright (c) 2014,2016,2018,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,7 +33,7 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 # Custom build type "ASAN", to be used to run the
-# AddressSanatizer memory checker.
+# AddressSanitizer memory checker.
 
 set(_flags "-O1 -g -fsanitize=address -fno-omit-frame-pointer")
 
index 9d5b99ac6e95d05d42a4c653df2f3900930d8b25..daa3d501f495d262be2ca22b1ff734c56c149454 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
diff --git a/cmake/gmxBuildTypeUBSAN.cmake b/cmake/gmxBuildTypeUBSAN.cmake
new file mode 100644 (file)
index 0000000..3e26087
--- /dev/null
@@ -0,0 +1,43 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+# Custom build type "UBSAN", to be used to run the
+# undefined behaviour memory checker.
+
+set(_flags "-O1 -g -fsanitize=undefined -fno-omit-frame-pointer")
+foreach(_language C CXX)
+    string(REPLACE "X" "+" _human_readable_language ${_language})
+    set(CMAKE_${_language}_FLAGS_UBSAN ${_flags} CACHE STRING "${_human_readable_language} flags for undefined behavior sanitizer" FORCE)
+    mark_as_advanced(CMAKE_${_language}_FLAGS_UBSAN)
+endforeach()
index a7a68a617cb06d72e97057b2146d38aa5d884c54..7e42383db61c647f539891d59c27efd39be45300 100644 (file)
@@ -1,7 +1,9 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+# Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -58,7 +60,10 @@ ENDMACRO(GMX_TEST_CXXFLAG VARIABLE FLAGS CXXFLAGSVAR)
 # works the same way.
 function(gmx_target_compile_options_inner)
     set (CFLAGS "${SIMD_C_FLAGS};${MPI_COMPILE_FLAGS};${EXTRA_C_FLAGS};${GMXC_CFLAGS}" PARENT_SCOPE)
-    set (CXXFLAGS "${SIMD_CXX_FLAGS};${MPI_COMPILE_FLAGS};${EXTRA_CXX_FLAGS};${GMXC_CXXFLAGS}" PARENT_SCOPE)
+    # When SYCL support has been enabled (so the flag is non-empty), we still *disable* things
+    # by default to avoid running each file three passes through the compiler. Then we'll explicitly
+    # enable SYCL for the few files using it, as well as the linker.
+    set (CXXFLAGS "${SIMD_CXX_FLAGS};${MPI_COMPILE_FLAGS};${DISABLE_SYCL_CXX_FLAGS};${EXTRA_CXX_FLAGS};${GMXC_CXXFLAGS}" PARENT_SCOPE)
 endfunction()
 
 # Implementation function to add compiler flags expected for all
@@ -125,7 +130,31 @@ function(gmx_cuda_target_compile_options VARIABLE_NAME)
     if (GMX_SKIP_DEFAULT_CFLAGS)
         set (CXXFLAGS "")
     else()
+        # Prepare the generic compiler options
         gmx_target_compile_options_inner()
+        # CUDA headers issue lots of warnings when compiled with
+        # -Wundef because they use old-style #ifdef a lot. We'd prefer
+        # to have FindCUDA.cmake treat CUDA internal headers with
+        # -isystem so that these warnings are naturally suppressed,
+        # but there's no way to do that without bundling a modified
+        # form of FindCUDA.cmake. That creates its own problems,
+        # because people either don't know we do that, or don't
+        # remember that we don't do that in user tarballs.
+        #
+        # We have make check-source ensuring that we have included
+        # config.h any time we use such symbols in commits in a merge
+        # request. Local development could run that too. So, we can
+        # tolerate any remaining risk from accidentally using
+        # e.g. #ifdef GMX_MPI rather than #if GMX_MPI in CUDA source
+        # files.
+        #
+        # So we disable -Wundef by the simple hack of appending
+        # -Wno-undef after it. That's more maintainable than having
+        # logic to avoid adding -Wundef to GMXC_CXXFLAGS, given the
+        # current approach to adding them. Hopefully this will improve
+        # if/when we have more CMake object libraries, and/or native
+        # CUDA compilation.
+        GMX_TEST_CXXFLAG(CXXFLAGS_WARN_NOUNDEF "-Wno-undef" CXXFLAGS)
     endif()
 
     # Only C++ compilation is supported with CUDA code in GROMACS
@@ -187,7 +216,7 @@ macro (gmx_c_flags)
         endif()
         if (GMX_COMPILER_WARNINGS)
             GMX_TEST_CFLAG(CFLAGS_WARN "-Wall;-Wno-unused;-Wunused-value;-Wunused-parameter" GMXC_CFLAGS)
-            GMX_TEST_CFLAG(CFLAGS_WARN_EXTRA "-Wextra;-Wno-missing-field-initializers;-Wno-sign-compare;-Wpointer-arith" GMXC_CFLAGS)
+            GMX_TEST_CFLAG(CFLAGS_WARN_EXTRA "-Wextra;-Wno-sign-compare;-Wpointer-arith" GMXC_CFLAGS)
             GMX_TEST_CFLAG(CFLAGS_WARN_UNDEF "-Wundef" GMXC_CFLAGS)
             GMX_TEST_CFLAG(CFLAGS_WARN_REL "-Wno-array-bounds" GMXC_CFLAGS_RELEASE_ONLY)
             if(CYGWIN)
@@ -195,6 +224,7 @@ macro (gmx_c_flags)
             endif()
             GMX_TEST_CFLAG(CFLAGS_STRINGOP_TRUNCATION "-Werror=stringop-truncation" GMXC_CFLAGS)
         endif()
+        GMX_TEST_CFLAG(CFLAGS_WARN_NO_MISSING_FIELD_INITIALIZERS "-Wno-missing-field-initializers" GMXC_CFLAGS)
         # new in gcc 4.5
         GMX_TEST_CFLAG(CFLAGS_EXCESS_PREC "-fexcess-precision=fast" GMXC_CFLAGS_RELEASE)
         GMX_TEST_CFLAG(CFLAGS_COPT "-funroll-all-loops"
@@ -210,18 +240,15 @@ macro (gmx_c_flags)
             GMX_TEST_CXXFLAG(CXXFLAGS_WARN "-Wall" GMXC_CXXFLAGS)
             # Problematic with CUDA
             # GMX_TEST_CXXFLAG(CXXFLAGS_WARN_EFFCXX "-Wnon-virtual-dtor" GMXC_CXXFLAGS)
-            GMX_TEST_CXXFLAG(CXXFLAGS_WARN_EXTRA "-Wextra;-Wno-missing-field-initializers;-Wpointer-arith;-Wmissing-declarations" GMXC_CXXFLAGS)
-            # CUDA versions prior to 7.5 come with a header (math_functions.h) which uses the _MSC_VER macro
-            # unconditionally, so we don't use -Wundef for earlier CUDA versions.
-            if(NOT(GMX_GPU AND CUDA_VERSION VERSION_LESS "7.5"))
-                GMX_TEST_CXXFLAG(CXXFLAGS_WARN_UNDEF "-Wundef" GMXC_CXXFLAGS)
-            endif()
+            GMX_TEST_CXXFLAG(CXXFLAGS_WARN_EXTRA "-Wextra;-Wpointer-arith;-Wmissing-declarations" GMXC_CXXFLAGS)
+            GMX_TEST_CXXFLAG(CXXFLAGS_WARN_UNDEF "-Wundef" GMXC_CXXFLAGS)
             GMX_TEST_CFLAG(CXXFLAGS_WARN_REL "-Wno-array-bounds" GMXC_CXXFLAGS_RELEASE_ONLY)
             GMX_TEST_CXXFLAG(CXXFLAGS_STRINGOP_TRUNCATION "-Wstringop-truncation" GMXC_CXXFLAGS)
             if (CXXFLAGS_STRINGOP_TRUNCATION)
                 set(CXXFLAGS_NO_STRINGOP_TRUNCATION "-Wno-stringop-truncation")
             endif()
         endif()
+        GMX_TEST_CXXFLAG(CXXFLAGS_WARN_NO_MISSING_FIELD_INITIALIZERS "-Wno-missing-field-initializers" GMXC_CXXFLAGS)
         # new in gcc 4.5
         GMX_TEST_CXXFLAG(CXXFLAGS_EXCESS_PREC "-fexcess-precision=fast" GMXC_CXXFLAGS_RELEASE)
         GMX_TEST_CXXFLAG(CXXFLAGS_COPT "-funroll-all-loops"
@@ -383,6 +410,7 @@ GMX_TEST_CFLAG(CFLAGS_WARN "/W3;/wd161;/wd177;/wd411;/wd593;/wd981;/wd1418;/wd14
         else() # MSVC projects only use the C++ flags
             GMX_TEST_CXXFLAG(CXXFLAGS_WARN "/wd4800;/wd4355;/wd4996;/wd4305;/wd4244;/wd4101;/wd4267;/wd4090;/wd4068;" GMXC_CXXFLAGS)
         endif()
+        GMX_TEST_CXXFLAG(CXXFLAGS_LANG "/permissive-" GMXC_CXXFLAGS)
     endif()
 
     if (CMAKE_C_COMPILER_ID MATCHES "Clang")
@@ -393,6 +421,7 @@ GMX_TEST_CFLAG(CFLAGS_WARN "/W3;/wd161;/wd177;/wd411;/wd593;/wd981;/wd1418;/wd14
             GMX_TEST_CFLAG(CFLAGS_WARN "-Wall;-Wno-unused;-Wunused-value;-Wunused-parameter" GMXC_CFLAGS)
             GMX_TEST_CFLAG(CFLAGS_WARN_EXTRA "-Wpointer-arith" GMXC_CFLAGS_EXTRA)
         endif()
+        GMX_TEST_CFLAG(CFLAGS_WARN_NO_MISSING_FIELD_INITIALIZERS "-Wno-missing-field-initializers" GMXC_CFLAGS)
     endif()
 
     if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
@@ -402,7 +431,7 @@ GMX_TEST_CFLAG(CFLAGS_WARN "/W3;/wd161;/wd177;/wd411;/wd593;/wd981;/wd1418;/wd14
             if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6.0") #LLVM BUG #21629
                 GMX_TEST_CXXFLAG(CXXFLAGS_WARN_NO_BRACES "-Wno-missing-braces" GMXC_CXXFLAGS)
             endif()
-            GMX_TEST_CXXFLAG(CXXFLAGS_WARN_EXTRA "-Wextra;-Wno-missing-field-initializers;-Wpointer-arith;-Wmissing-prototypes" GMXC_CXXFLAGS)
+            GMX_TEST_CXXFLAG(CXXFLAGS_WARN_EXTRA "-Wextra;-Wpointer-arith;-Wmissing-prototypes" GMXC_CXXFLAGS)
             GMX_TEST_CXXFLAG(CXXFLAGS_DEPRECATED "-Wdeprecated" GMXC_CXXFLAGS)
             # Functions placed in headers for inlining are not always
             # used in every translation unit that includes the files,
@@ -413,6 +442,7 @@ GMX_TEST_CFLAG(CFLAGS_WARN "/W3;/wd161;/wd177;/wd411;/wd593;/wd981;/wd1418;/wd14
         if(NOT GMX_OPENMP)
             GMX_TEST_CXXFLAG(CXXFLAGS_PRAGMA "-Wno-unknown-pragmas" GMXC_CXXFLAGS)
         endif()
+        GMX_TEST_CXXFLAG(CXXFLAGS_WARN_NO_MISSING_FIELD_INITIALIZERS "-Wno-missing-field-initializers" GMXC_CXXFLAGS)
     endif()
 
     # Apple bastardized version of Clang
index 81690b627c9c2192f44e89bd623a3835dd0c8ff3..c1b8351467534de99493363ac3c80b85eeb13517 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -124,8 +125,6 @@ function (gmx_cpack_write_config)
         \\\\.gitignore
         \\\\.gitattributes
         INSTALL-dev
-        cmake/FindCUDA\\\\.cmake
-        cmake/FindCUDA
         # entry below is needed for CI not to include the cache directory
         ccache)
     set(CPACK_SOURCE_IGNORE_FILES ${FILES_NOT_INCLUDED_IN_SOURCE_PACKAGE})
index 8af7337adc0cc61f6f20940915088be0ef9cb06e..73b80e9eb3e059350c0278ea7aaf625a3245ffee 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
diff --git a/cmake/gmxDetectGpu.cmake b/cmake/gmxDetectGpu.cmake
deleted file mode 100644 (file)
index 7ccdade..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2012,2018, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-# The gmx_detect_gpu() macro aims to detect GPUs available in the build machine
-# and provide the number, names, and compute-capabilities of these devices.
-#
-# The current version is limited to checking the availability of NVIDIA GPUs
-# without compute-capability information.
-#
-# The current detection relies on the following checks in the order of listing:
-# - output of nvidia-smi (if available);
-# - presence and content of of /proc/driver/nvidia/gpus/*/information (Linux)
-# - output of lspci (Linux)
-#
-# If any of the checks succeeds in finding devices, consecutive checks will not
-# be carried out. Additionally, when lspci is used and a device with unknown
-# PCI ID is encountered, lspci tries to check the online PCI ID database. If
-# this is not possible or the device is simply not recognized, no device names
-# will be available.
-#
-# The following advanced variables are defined:
-# - GMX_DETECT_GPU_AVAILABLE - TRUE if any GPUs were detected, otherwise FALSE
-# - GMX_DETECT_GPU_COUNT     - # of GPUs detected
-# - GMX_DETECT_GPU_INFO      - list of information strings of the detected GPUs
-#
-# NOTE: The proper solution is to detect hardware compatible with the native
-# GPU acceleration. However, this requires checking the compute capability
-# of the device which is not possible with the current checks and requires
-# interfacing with the CUDA driver API.
-#
-
-# check whether the number of GPUs machetes the number of elements in the GPU info list
-macro(check_num_gpu_info NGPU GPU_INFO)
-    list(LENGTH ${GPU_INFO} _len)
-    if (NOT NGPU EQUAL _len)
-        list(APPEND ${GMX_DETECT_GPU_INFO} "NOTE: information about some GPU(s) missing!")
-    endif()
-endmacro()
-
-macro(gmx_detect_gpu)
-
-    if (NOT DEFINED GMX_DETECT_GPU_AVAILABLE OR
-        NOT DEFINED GMX_DETECT_GPU_COUNT OR
-        NOT DEFINED GMX_DETECT_GPU_INFO)
-
-        set(GMX_DETECT_GPU_COUNT 0)
-        set(GMX_DETECT_GPU_INFO  "")
-
-        message(STATUS "Looking for NVIDIA GPUs present in the system")
-
-        # nvidia-smi-based detection.
-        # Requires the nvidia-smi tool to be installed and available in the path
-        # or in one of the default search locations
-        if (NOT DEFINED GMX_DETECT_GPU_COUNT_NVIDIA_SMI)
-            # try to find the nvidia-smi binary
-            # TODO add location hints
-            find_program(_nvidia_smi "nvidia-smi")
-            if (_nvidia_smi)
-                set(GMX_DETECT_GPU_COUNT_NVIDIA_SMI 0)
-                # execute nvidia-smi -L to get a short list of GPUs available
-                exec_program(${_nvidia_smi_path} ARGS -L
-                    OUTPUT_VARIABLE _nvidia_smi_out
-                    RETURN_VALUE    _nvidia_smi_ret)
-                # process the stdout of nvidia-smi
-                if (_nvidia_smi_ret EQUAL 0)
-                    # convert string with newlines to list of strings
-                    string(REGEX REPLACE "\n" ";" _nvidia_smi_out "${_nvidia_smi_out}")
-                    foreach(_line ${_nvidia_smi_out})
-                        if (_line MATCHES "^GPU [0-9]+:")
-                            math(EXPR GMX_DETECT_GPU_COUNT_NVIDIA_SMI "${GMX_DETECT_GPU_COUNT_NVIDIA_SMI}+1")
-                            # the UUID is not very useful for the user, remove it
-                            string(REGEX REPLACE " \\(UUID:.*\\)" "" _gpu_info "${_line}")
-                            if (NOT _gpu_info STREQUAL "")
-                                list(APPEND GMX_DETECT_GPU_INFO "${_gpu_info}")
-                            endif()
-                        endif()
-                    endforeach()
-
-                    check_num_gpu_info(${GMX_DETECT_GPU_COUNT_NVIDIA_SMI} GMX_DETECT_GPU_INFO)
-                    set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT_NVIDIA_SMI})
-                endif()
-            endif()
-
-            unset(_nvidia_smi CACHE)
-            unset(_nvidia_smi_ret)
-            unset(_nvidia_smi_out)
-            unset(_gpu_name)
-            unset(_line)
-        endif()
-
-        if (UNIX AND NOT (APPLE OR CYGWIN))
-            # /proc/driver/nvidia/gpus/*/information-based detection.
-            # Requires the NVDIA closed source driver to be installed and loaded
-            if (NOT DEFINED GMX_DETECT_GPU_COUNT_PROC AND GMX_DETECT_GPU_COUNT EQUAL 0)
-                set(GMX_DETECT_GPU_COUNT_PROC 0)
-                file(GLOB _proc_nv_gpu_info "/proc/driver/nvidia/gpus/*/information")
-                foreach (_file ${_proc_nv_gpu_info})
-                    math(EXPR GMX_DETECT_GPU_COUNT_PROC "${GMX_DETECT_GPU_COUNT_PROC}+1")
-                    # assemble information strings similar to the nvidia-smi output
-                    # GPU ID = directory name on /proc/driver/nvidia/gpus/
-                    string(REGEX REPLACE "/proc/driver/nvidia/gpus.*([0-9]+).*information" "\\1" _gpu_id ${_file})
-                    # GPU name
-                    file(STRINGS ${_file} _gpu_name LIMIT_COUNT 1 REGEX "^Model:.*" NO_HEX_CONVERSION)
-                    string(REGEX REPLACE "^Model:[ \t]*(.*)" "\\1" _gpu_name "${_gpu_name}")
-                    if (NOT _gpu_id STREQUAL "" AND NOT _gpu_name STREQUAL "")
-                        list(APPEND GMX_DETECT_GPU_INFO "GPU ${_gpu_id}: ${_gpu_name}")
-                    endif()
-                endforeach()
-
-                check_num_gpu_info(${GMX_DETECT_GPU_COUNT_PROC} GMX_DETECT_GPU_INFO)
-                set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT_PROC})
-
-                unset(_proc_nv_gpu_info)
-                unset(_gpu_name)
-                unset(_gpu_id)
-                unset(_file)
-            endif()
-
-            # lspci-based detection (does not provide GPU information).
-            # Requires lspci and for GPU names to be fetched from the central
-            # PCI ID db if not available locally.
-            if (NOT DEFINED GMX_DETECT_GPU_COUNT_LSPCI AND GMX_DETECT_GPU_COUNT EQUAL 0)
-                set(GMX_DETECT_GPU_COUNT_LSPCI 0)
-                exec_program(lspci ARGS -q
-                    OUTPUT_VARIABLE _lspci_out
-                    RETURN_VALUE    _lspci_ret)
-                # prehaps -q is not supported, try running without
-                if (NOT RETURN_VALUE EQUAL 0)
-                    exec_program(lspci
-                        OUTPUT_VARIABLE _lspci_out
-                        RETURN_VALUE    _lspci_ret)
-                endif()
-                if (_lspci_ret EQUAL 0)
-                    # convert string with newlines to list of strings
-                    STRING(REGEX REPLACE ";" "\\\\;" _lspci_out "${_lspci_out}")
-                    string(REGEX REPLACE "\n" ";" _lspci_out "${_lspci_out}")
-                    foreach(_line ${_lspci_out})
-                        string(TOUPPER "${_line}" _line_upper)
-                        if (_line_upper MATCHES ".*VGA.*NVIDIA.*" OR _line_upper MATCHES ".*3D.*NVIDIA.*")
-                            math(EXPR GMX_DETECT_GPU_COUNT_LSPCI "${GMX_DETECT_GPU_COUNT_LSPCI}+1")
-                            # Try to parse out the device name which should be
-                            # included in the lspci -q output between []-s
-                            string(REGEX REPLACE ".*\\[(.*)\\].*" "\\1" _gpu_name "${_line}")
-                            if (NOT _gpu_name EQUAL "")
-                                list(APPEND GMX_DETECT_GPU_INFO "${_gpu_name}")
-                            endif()
-                        endif()
-                    endforeach()
-
-                    check_num_gpu_info(${GMX_DETECT_GPU_COUNT_LSPCI} GMX_DETECT_GPU_INFO)
-                    set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT_LSPCI})
-                endif()
-
-                unset(_lspci_ret)
-                unset(_lspci_out)
-                unset(_gpu_name)
-                unset(_line)
-                unset(_line_upper)
-            endif()
-        endif()
-
-        if (GMX_DETECT_GPU_COUNT GREATER 0)
-            set(GMX_DETECT_GPU_AVAILABLE YES)
-        else()
-            set(GMX_DETECT_GPU_AVAILABLE NO)
-        endif()
-        set(GMX_DETECT_GPU_AVAILABLE ${GMX_DETECT_GPU_AVAILABLE} CACHE BOOL "Whether any NVIDIA GPU was detected" FORCE)
-
-        set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT}
-            CACHE STRING "Number of NVIDIA GPUs detected")
-        set(GMX_DETECT_GPU_INFO ${GMX_DETECT_GPU_INFO}
-            CACHE STRING "basic information on the detected NVIDIA GPUs")
-
-        set(GMX_DETECT_GPU_COUNT_NVIDIA_SMI ${GMX_DETECT_GPU_COUNT_NVIDIA_SMI}
-            CACHE INTERNAL "Number of NVIDIA GPUs detected using nvidia-smi")
-        set(GMX_DETECT_GPU_COUNT_PROC ${GMX_DETECT_GPU_COUNT_PROC}
-            CACHE INTERNAL "Number of NVIDIA GPUs detected in /proc/driver/nvidia/gpus")
-        set(GMX_DETECT_GPU_COUNT_LSPCI ${GMX_DETECT_GPU_COUNT_LSPCI}
-            CACHE INTERNAL "Number of NVIDIA GPUs detected using lspci")
-
-        mark_as_advanced(GMX_DETECT_GPU_AVAILABLE
-                         GMX_DETECT_GPU_COUNT
-                         GMX_DETECT_GPU_INFO)
-
-        if (GMX_DETECT_GPU_AVAILABLE)
-            message(STATUS "Number of NVIDIA GPUs detected: ${GMX_DETECT_GPU_COUNT} ")
-        else()
-            message(STATUS "Could not detect NVIDIA GPUs")
-        endif()
-
-    endif()
-endmacro(gmx_detect_gpu)
index 97d7f5b7ac239f6ebda6246999ec542dfd9f3187..781e9a2e5619d76303a26b4848893af5eaf83057 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -127,6 +128,8 @@ function(gmx_suggest_simd _suggested_simd)
                 set(OUTPUT_SIMD "IBM_VSX")
             elseif(CPU_DETECTION_FEATURES MATCHES " vmx ")
                 set(OUTPUT_SIMD "IBM_VMX")
+            elseif(CPU_DETECTION_FEATURES MATCHES " sve ")
+                set(OUTPUT_SIMD "ARM_SVE")
             elseif(CPU_DETECTION_FEATURES MATCHES " neon_asimd ")
                 set(OUTPUT_SIMD "ARM_NEON_ASIMD")
             elseif(CPU_DETECTION_FEATURES MATCHES " neon " AND NOT GMX_DOUBLE)
index 12cc0fa97094031f2e0ff0647cbaef3ff52e5355..8b44c7dcb07039b8b54be03ce6460e992c32b79a 100644 (file)
@@ -48,10 +48,6 @@ function(gmx_detect_target_architecture)
         try_compile(GMX_TARGET_MIC ${CMAKE_BINARY_DIR}
             "${CMAKE_SOURCE_DIR}/cmake/TestMIC.cpp")
     endif()
-    if (NOT DEFINED GMX_TARGET_ARMV7)
-        try_compile(GMX_TARGET_ARMV7 ${CMAKE_BINARY_DIR}
-            "${CMAKE_SOURCE_DIR}/cmake/TestARMv7.cpp")
-    endif()
     if (NOT DEFINED GMX_TARGET_FUJITSU_SPARC64)
         try_compile(GMX_TARGET_FUJITSU_SPARC64 ${CMAKE_BINARY_DIR}
             "${CMAKE_SOURCE_DIR}/cmake/TestFujitsuSparc64.cpp")
index 9c64eb580c95588f6504df61123d2fce7e8947f7..a2a3291bc3b86132d1012ffeaba24575afeaa505 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
+# Copyright (c) 2010,2011,2012,2013,2014,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -129,9 +129,9 @@ string(REGEX REPLACE "-" "" HEAD_DATE "${HEAD_DATE}")
 set(VERSION_STR_SUFFIX "${HEAD_DATE}-${HEAD_HASH_SHORT}${DIRTY_STR}")
 
 # find the names of remotes that are located on the official gromacs
-# git/gerrit servers
+# git servers
 execute_process(COMMAND ${GIT_EXECUTABLE} config --get-regexp
-                "remote\\..*\\.url" "\\.gromacs\\.org[:/].*gromacs(\\.git)?$"
+                "remote\\..*\\.url" "\\gitlab\\.com[:/]gromacs.gromacs(\\.git)?$"
     WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
     OUTPUT_VARIABLE GMX_REMOTES
     ERROR_VARIABLE EXEC_ERR
diff --git a/cmake/gmxManageCuda.cmake b/cmake/gmxManageCuda.cmake
new file mode 100644 (file)
index 0000000..96aaa27
--- /dev/null
@@ -0,0 +1,112 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+
+# The earliest version of the CUDA toolkit that supports c++14 is 9.0
+set(REQUIRED_CUDA_VERSION 9.0)
+set(REQUIRED_CUDA_COMPUTE_CAPABILITY 3.0)
+
+set(GMX_GPU_CUDA ON)
+
+option(GMX_CLANG_CUDA "Use clang for CUDA" OFF)
+
+if(GMX_DOUBLE)
+    message(FATAL_ERROR "CUDA acceleration is not available in double precision")
+endif()
+
+find_package(CUDA ${REQUIRED_CUDA_VERSION} REQUIRED)
+
+# Try to execute ${CUDA_NVCC_EXECUTABLE} --version and set the output
+# (or an error string) in the argument variable.
+# Note that semicolon is used as separator for nvcc.
+#
+# Parameters:
+#   COMPILER_INFO         - [output variable] string with compiler path, ID and
+#                           some compiler-provided information
+#   DEVICE_COMPILER_FLAGS - [output variable] device flags for the compiler
+#   HOST_COMPILER_FLAGS   - [output variable] host flags for the compiler, if propagated
+#
+macro(get_cuda_compiler_info COMPILER_INFO DEVICE_COMPILER_FLAGS HOST_COMPILER_FLAGS)
+    if(NOT GMX_CLANG_CUDA)
+        if(CUDA_NVCC_EXECUTABLE)
+
+            # Get the nvcc version string. This is multi-line, but since it is only 4 lines
+            # and might change in the future it is better to store than trying to parse out
+            # the version from the current format.
+            execute_process(COMMAND ${CUDA_NVCC_EXECUTABLE} --version
+                RESULT_VARIABLE _nvcc_version_res
+                OUTPUT_VARIABLE _nvcc_version_out
+                ERROR_VARIABLE  _nvcc_version_err
+                OUTPUT_STRIP_TRAILING_WHITESPACE)
+            if (${_nvcc_version_res} EQUAL 0)
+                # Fix multi-line mess: Replace newline with ";" so we can use it in a define
+                string(REPLACE "\n" ";" _nvcc_info_singleline ${_nvcc_version_out})
+                SET(${COMPILER_INFO} "${CUDA_NVCC_EXECUTABLE} ${_nvcc_info_singleline}")
+                string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type)
+                SET(_compiler_flags "${CUDA_NVCC_FLAGS_${_build_type}}")
+                if(CUDA_PROPAGATE_HOST_FLAGS)
+                    set(${HOST_COMPILER_FLAGS} BUILD_CXXFLAGS)
+                else()
+                    set(${HOST_COMPILER_FLAGS} "")
+                endif()
+                SET(${DEVICE_COMPILER_FLAGS} "${CUDA_NVCC_FLAGS}${CUDA_NVCC_FLAGS_${_build_type}}")
+            else()
+                SET(${COMPILER_INFO} "N/A")
+                SET(${COMPILER_FLAGS} "N/A")
+            endif()
+        endif()
+    else()
+        # CXX compiler is the CUDA compiler
+        set(${COMPILER_INFO} "${CMAKE_CXX_COMPILER}  ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
+        # there are some extra flags
+        set(${COMPILER_FLAGS} "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${_build_type}} ${GMX_CUDA_CLANG_FLAGS}")
+    endif()
+endmacro ()
+
+if(GMX_CLANG_CUDA)
+    include(gmxManageClangCudaConfig)
+    list(APPEND GMX_EXTRA_LIBRARIES ${GMX_CUDA_CLANG_LINK_LIBS})
+    link_directories("${GMX_CUDA_CLANG_LINK_DIRS}")
+else()
+    # Using NVIDIA compiler
+    if(NOT CUDA_NVCC_EXECUTABLE)
+        message(FATAL_ERROR "nvcc is required for a CUDA build, please set CUDA_TOOLKIT_ROOT_DIR appropriately")
+    endif()
+    # set up nvcc options
+    include(gmxManageNvccConfig)
+endif()
+
+option(GMX_CUDA_NB_SINGLE_COMPILATION_UNIT "Whether to compile the CUDA non-bonded module using a single compilation unit." OFF)
+mark_as_advanced(GMX_CUDA_NB_SINGLE_COMPILATION_UNIT)
diff --git a/cmake/gmxManageCycleCounters.cmake b/cmake/gmxManageCycleCounters.cmake
deleted file mode 100644 (file)
index df3f0c7..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-# - Decide whether to use CPU cycle counters
-#
-# gmx_manage_cycle_counters()
-#
-# By default, we enable GMX_CYCLECOUNTERS for all architectures except ARMv7.
-# On ARMv7, we enable it if we are not cross-compiling and can run a small
-# test to confirm that the support is present in the kernel, otherwise we
-# disable it.
-#
-macro(gmx_manage_cycle_counters)
-
-    if(NOT DEFINED GMX_CYCLECOUNTERS)
-
-        if(GMX_TARGET_ARMV7)
-
-            if(NOT CMAKE_CROSSCOMPILING)
-
-                try_run(ARMV7_COUNTER_RUN_VAR ARMV7_COUNTER_COMPILE_VAR
-                        ${CMAKE_BINARY_DIR} "${CMAKE_SOURCE_DIR}/cmake/TestARMv7CycleCounters.cpp")
-
-                # Enable cycle counter usage if the test ran fine and exited with 0 return code
-                if(${ARMV7_COUNTER_COMPILE_VAR} AND ("${ARMV7_COUNTER_RUN_VAR}" EQUAL "0"))
-                    set(GMX_CYCLECOUNTERS ON CACHE BOOL "Use CPU cycle counters timing")
-                else()
-                    set(GMX_CYCLECOUNTERS OFF CACHE BOOL "Use CPU cycle counters for timing")
-                endif()
-
-            else()
-
-                # Disable cycle counters when cross-compiling for ARMv7
-                set(GMX_CYCLECOUNTERS OFF CACHE BOOL "Use CPU cycle counters for timing")
-
-            endif()
-
-        else()
-
-            # For now we (try to) enable cycle counters on all other platforms
-            set(GMX_CYCLECOUNTERS ON CACHE BOOL "Use CPU cycle counters timing")
-
-        endif()
-
-        mark_as_advanced(GMX_CYCLECOUNTERS)
-
-    endif()
-
-endmacro()
-
index eab2f486fd02c912bc721c2c561dd783d7d2ee26..f59e011ecc86f7f72d27d668424ce369b5c0e6ec 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -95,14 +96,16 @@ if(${GMX_FFT_LIBRARY} STREQUAL "FFTW3")
         set(PKG_FFT "${${FFTW}_PKG}")
         include_directories(SYSTEM ${${FFTW}_INCLUDE_DIRS})
 
-        if ((${GMX_SIMD_ACTIVE} MATCHES "SSE" OR ${GMX_SIMD_ACTIVE} MATCHES "AVX") AND NOT ${FFTW}_HAVE_SIMD)
-            set(FFT_WARNING_MESSAGE "The fftw library found is compiled without SIMD support, which makes it slow. Consider recompiling it or contact your admin")
-        else()
-            if(${GMX_SIMD_ACTIVE} MATCHES "AVX" AND NOT (${FFTW}_HAVE_SSE OR ${FFTW}_HAVE_SSE2))
-                # If we end up here we have an AVX Gromacs build, and
-                # FFTW with SIMD.
-                set(FFT_WARNING_MESSAGE "The FFTW library was compiled with neither --enable-sse nor --enable-sse2; those would have enabled SSE(2) SIMD instructions. This will give suboptimal performance. You should (re)compile the FFTW library with --enable-sse2 and --enable-avx (and --enable-avx2 or --enable-avx512 if supported).")
-            endif()
+        if(NOT WIN32) # Detection doesn't work on Windows
+          if ((${GMX_SIMD_ACTIVE} MATCHES "SSE" OR ${GMX_SIMD_ACTIVE} MATCHES "AVX") AND NOT ${FFTW}_HAVE_SIMD)
+              set(FFT_WARNING_MESSAGE "The fftw library found is compiled without SIMD support, which makes it slow. Consider recompiling it or contact your admin")
+          else()
+              if(${GMX_SIMD_ACTIVE} MATCHES "AVX" AND NOT (${FFTW}_HAVE_SSE OR ${FFTW}_HAVE_SSE2))
+                  # If we end up here we have an AVX Gromacs build, and
+                  # FFTW with SIMD.
+                  set(FFT_WARNING_MESSAGE "The FFTW library was compiled with neither --enable-sse nor --enable-sse2; those would have enabled SSE(2) SIMD instructions. This will give suboptimal performance. You should (re)compile the FFTW library with --enable-sse2 and --enable-avx (and --enable-avx2 or --enable-avx512 if supported).")
+              endif()
+          endif()
         endif()
 
         find_path(ARMPL_INCLUDE_DIR "armpl.h" HINTS ${${FFTW}_INCLUDE_DIRS}
diff --git a/cmake/gmxManageGPU.cmake b/cmake/gmxManageGPU.cmake
deleted file mode 100644 (file)
index 66f2061..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-# If the user did not set GMX_GPU we'll consider this option to be
-# in "auto" mode meaning that we will:
-# - search for CUDA and set GMX_GPU=ON we find it
-# - check whether GPUs are present
-# - if CUDA is not found but GPUs were detected issue a warning
-if (NOT DEFINED GMX_GPU)
-    set(GMX_GPU_AUTO TRUE CACHE INTERNAL "GPU acceleration will be selected automatically")
-else()
-    set(GMX_GPU_AUTO FALSE CACHE INTERNAL "GPU acceleration will be selected automatically")
-endif()
-option(GMX_GPU "Enable GPU acceleration" OFF)
-
-option(GMX_CLANG_CUDA "Use clang for CUDA" OFF)
-
-if(GMX_GPU AND GMX_DOUBLE)
-    message(FATAL_ERROR "GPU acceleration is not available in double precision!")
-endif()
-if(GMX_GPU_AUTO AND GMX_DOUBLE)
-    message(WARNING "GPU acceleration is not available in double precision, disabled!")
-    set_property(CACHE GMX_GPU PROPERTY VALUE OFF)
-    set_property(CACHE GMX_GPU_AUTO PROPERTY VALUE OFF)
-endif()
-
-# detect GPUs in the build host machine
-if ((GMX_GPU OR GMX_GPU_AUTO) AND NOT GMX_GPU_DETECTION_DONE)
-    include(gmxDetectGpu)
-    gmx_detect_gpu()
-endif()
-
-# We need to call find_package even when we've already done the detection/setup
-if(GMX_GPU OR GMX_GPU_AUTO)
-    if(NOT GMX_GPU AND NOT GMX_DETECT_GPU_AVAILABLE)
-        # Stay quiet when detection has occured and found no GPU.
-        # Noise is acceptable when there is a GPU or the user required one.
-        set(FIND_CUDA_QUIETLY QUIET)
-    endif()
-
-    # Cmake tries to use the static cuda runtime by default,
-    # but this leads to unusable GPU builds on OS X.
-    if(APPLE)
-        set(CUDA_USE_STATIC_CUDA_RUNTIME OFF CACHE STRING "Use the static version of the CUDA runtime library if available")
-    endif()
-
-    find_package(CUDA ${REQUIRED_CUDA_VERSION} ${FIND_CUDA_QUIETLY})
-endif()
-
-# Depending on the current vale of GMX_GPU and GMX_GPU_AUTO:
-# - OFF, FALSE: Will skip this detection/setup.
-# - OFF, TRUE : Will keep GMX_GPU=OFF if no CUDA is detected, but will assemble
-#               a warning message which will be issued at the end of the
-#               configuration if GPU(s) were found in the build system.
-# - ON , FALSE: The user requested GPU build and this requires CUDA, so we will
-#               fail if it is not available.
-# - ON , TRUE : Can't happen (GMX_GPU=ON can only be user-set at this point)
-if((GMX_GPU OR GMX_GPU_AUTO) AND NOT GMX_GPU_DETECTION_DONE)
-    # assemble warning/error message
-    if (GMX_DETECT_GPU_AVAILABLE)
-        set(_msg "${GMX_DETECT_GPU_COUNT} NVIDIA GPU(s) found in the system")
-
-        # append GPU names
-        if (NOT GMX_DETECT_GPU_INFO STREQUAL "")
-            set(_msg "${_msg}:")
-            foreach(gpu ${GMX_DETECT_GPU_INFO})
-                set(_msg "${_msg}
-${gpu}")
-            endforeach()
-        endif()
-
-        # TODO remove the second part of the message when we'll have compute
-        # capability information from the detection.
-        set(_msg "${_msg}
-Compute capability information not available, consult the NVIDIA website:
-https://developer.nvidia.com/cuda-gpus")
-    endif()
-
-        set(CUDA_NOTFOUND_MESSAGE "mdrun supports native GPU acceleration on NVIDIA hardware with compute capability >= ${REQUIRED_CUDA_COMPUTE_CAPABILITY} (Kepler or later). This requires the NVIDIA CUDA toolkit, which was not found. Its location can be hinted by setting the CUDA_TOOLKIT_ROOT_DIR CMake option (does not work as an environment variable). The typical location would be /usr/local/cuda[-version]. Note that CPU or GPU acceleration can be selected at runtime.
-
-${_msg}")
-        unset(_msg)
-
-    if (NOT CUDA_FOUND)
-        if (GMX_GPU_AUTO)
-            # Disable GPU acceleration in auto mode
-            message(STATUS "No compatible CUDA toolkit found (v5.0+), disabling native GPU acceleration")
-            set_property(CACHE GMX_GPU PROPERTY VALUE OFF)
-            set(CUDA_NOTFOUND_AUTO ON)
-        else()
-            # the user requested CUDA, but it wasn't found
-            message(FATAL_ERROR "${CUDA_NOTFOUND_MESSAGE}")
-        endif()
-    else()
-        if (GMX_GPU_AUTO)
-            message(STATUS "Enabling native GPU acceleration")
-            set_property(CACHE GMX_GPU PROPERTY VALUE ON)
-        endif()
-    endif()
-endif()
-
-# Annoyingly enough, FindCUDA leaves a few variables behind as non-advanced.
-# We need to mark these advanced outside the conditional, otherwise, if the
-# user turns GMX_GPU=OFF after a failed cmake pass, these variables will be
-# left behind in the cache.
-mark_as_advanced(CUDA_SDK_ROOT_DIR
-                 CUDA_USE_STATIC_CUDA_RUNTIME
-                 CUDA_dl_LIBRARY CUDA_rt_LIBRARY
-                 )
-if(NOT GMX_GPU)
-    mark_as_advanced(CUDA_TOOLKIT_ROOT_DIR)
-    mark_as_advanced(CUDA_HOST_COMPILER)
-endif()
-
-# Try to execute ${CUDA_NVCC_EXECUTABLE} --version and set the output
-# (or an error string) in the argument variable.
-# Note that semicolon is used as separator for nvcc.
-#
-# Parameters:
-#   COMPILER_INFO         - [output variable] string with compiler path, ID and
-#                           some compiler-provided information
-#   DEVICE_COMPILER_FLAGS - [output variable] device flags for the compiler
-#   HOST_COMPILER_FLAGS   - [output variable] host flags for the compiler, if propagated
-#
-macro(get_cuda_compiler_info COMPILER_INFO DEVICE_COMPILER_FLAGS HOST_COMPILER_FLAGS)
-    if(NOT GMX_CLANG_CUDA)
-        if(CUDA_NVCC_EXECUTABLE)
-
-            # Get the nvcc version string. This is multi-line, but since it is only 4 lines
-            # and might change in the future it is better to store than trying to parse out
-            # the version from the current format.
-            execute_process(COMMAND ${CUDA_NVCC_EXECUTABLE} --version
-                RESULT_VARIABLE _nvcc_version_res
-                OUTPUT_VARIABLE _nvcc_version_out
-                ERROR_VARIABLE  _nvcc_version_err
-                OUTPUT_STRIP_TRAILING_WHITESPACE)
-            if (${_nvcc_version_res} EQUAL 0)
-                # Fix multi-line mess: Replace newline with ";" so we can use it in a define
-                string(REPLACE "\n" ";" _nvcc_info_singleline ${_nvcc_version_out})
-                SET(${COMPILER_INFO} "${CUDA_NVCC_EXECUTABLE} ${_nvcc_info_singleline}")
-                string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type)
-                SET(_compiler_flags "${CUDA_NVCC_FLAGS_${_build_type}}")
-                if(CUDA_PROPAGATE_HOST_FLAGS)
-                    set(${HOST_COMPILER_FLAGS} BUILD_CXXFLAGS)
-                else()
-                    set(${HOST_COMPILER_FLAGS} "")
-                endif()
-                SET(${DEVICE_COMPILER_FLAGS} "${CUDA_NVCC_FLAGS}${CUDA_NVCC_FLAGS_${_build_type}}")
-            else()
-                SET(${COMPILER_INFO} "N/A")
-                SET(${COMPILER_FLAGS} "N/A")
-            endif()
-        endif()
-    else()
-        # CXX compiler is the CUDA compiler
-        set(${COMPILER_INFO} "${CMAKE_CXX_COMPILER}  ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
-        # there are some extra flags
-        set(${HOST_COMPILER_FLAGS} BUILD_CXXFLAGS)
-        set(${DEVICE_COMPILER_FLAGS} "${GMX_CUDA_CLANG_FLAGS}")
-    endif()
-endmacro ()
-
-macro(enable_multiple_cuda_compilation_units)
-    message(STATUS "Enabling multiple compilation units for the CUDA non-bonded module.")
-    set_property(CACHE GMX_CUDA_NB_SINGLE_COMPILATION_UNIT PROPERTY VALUE OFF)
-endmacro()
-
-include(CMakeDependentOption)
-include(gmxOptionUtilities)
-macro(gmx_gpu_setup)
-    if(GMX_GPU)
-        if(NOT GMX_CLANG_CUDA)
-            if(NOT CUDA_NVCC_EXECUTABLE)
-                message(FATAL_ERROR "nvcc is required for a CUDA build, please set CUDA_TOOLKIT_ROOT_DIR appropriately")
-            endif()
-            # set up nvcc options
-            include(gmxManageNvccConfig)
-        else()
-            include(gmxManageClangCudaConfig)
-        endif()
-
-        # no OpenMP is no good!
-        if(NOT GMX_OPENMP)
-            message(WARNING "To use GPU acceleration efficiently, mdrun requires OpenMP multi-threading. Without OpenMP a single CPU core can be used with a GPU which is not optimal. Note that with MPI multiple processes can be forced to use a single GPU, but this is typically inefficient. You need to set both C and C++ compilers that support OpenMP (CC and CXX environment variables, respectively) when using GPUs.")
-        endif()
-    endif() # GMX_GPU
-
-    option(GMX_CUDA_NB_SINGLE_COMPILATION_UNIT "Whether to compile the CUDA non-bonded module using a single compilation unit." OFF)
-    mark_as_advanced(GMX_CUDA_NB_SINGLE_COMPILATION_UNIT)
-
-endmacro()
index aeb7937c7405d06c7d3213dcff64df7c7fe83258..bb5a5e61655e5ff2bd9227cb92f633914353f5fc 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013,2014,2016, by the GROMACS development team, led by
+# Copyright (c) 2013,2014,2016,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -160,7 +160,7 @@ endmacro()
 # there is need to (say) use vendor BLAS with MKL for FFTs.
 #
 # If the vendor BLAS and/or LAPACK have abnormal library names, then
-# the default searching procedure will fail (e.g. Redmine #771). The
+# the default searching procedure will fail (e.g. Issue #771). The
 # GMX_(BLAS|LAPACK)_USER variables can be used to indicate the correct
 # libraries. If these do not work, a warning is emitted and we try to
 # use them anyway, assuming the user knows what they are doing.
index f5d8fd637dc6dc54931b656fa2dea4cc9630f66c..82737aaed8c31c4a29876299c65afbe665ca7beb 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
diff --git a/cmake/gmxManageMuparser.cmake b/cmake/gmxManageMuparser.cmake
new file mode 100644 (file)
index 0000000..ee17397
--- /dev/null
@@ -0,0 +1,92 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+set(GMX_MUPARSER_REQUIRED_VERSION "2.3")
+
+include(gmxOptionUtilities)
+
+# Make a three-state enumeration, defaulting to 
+gmx_option_multichoice(GMX_USE_MUPARSER
+    "How to handle the muparser dependency of GROMACS"
+    INTERNAL
+    INTERNAL EXTERNAL NONE)
+mark_as_advanced(GMX_USE_MUPARSER)
+
+# Make a fully functional muparser library target that libgromacs can
+# depend on regardless of how the user directed muparser support and/or
+# linking to work.
+function(gmx_manage_muparser)
+    if(GMX_USE_MUPARSER STREQUAL "INTERNAL")
+        # Create an object library for the muparser sources
+        set(BUNDLED_MUPARSER_DIR "${CMAKE_SOURCE_DIR}/src/external/muparser")
+        file(GLOB MUPARSER_SOURCES ${BUNDLED_MUPARSER_DIR}/*.cpp)
+        add_library(muparser_objlib OBJECT ${MUPARSER_SOURCES})
+        # Ensure that the objects can be used in both STATIC and SHARED
+        # libraries.
+        set_target_properties(muparser_objlib PROPERTIES POSITION_INDEPENDENT_CODE ON)
+
+        # Create an INTERFACE (ie. fake) library for muparser, that
+        # libgromacs can depend on. The generator expression for the
+        # target_sources expands to nothing when cmake builds the
+        # export for libgromacs, so that it understands that we don't
+        # install anything for this library - using plain source files
+        # would not convey the right information.
+        add_library(muparser INTERFACE)
+        target_sources(muparser INTERFACE $<TARGET_OBJECTS:muparser_objlib>)
+        target_include_directories(muparser SYSTEM INTERFACE $<BUILD_INTERFACE:${BUNDLED_MUPARSER_DIR}>)
+        # Add the muparser interface library to the libgromacs Export name, even though
+        # we will not be installing any content.
+        install(TARGETS muparser EXPORT libgromacs)
+
+        set(HAVE_MUPARSER 1 CACHE INTERNAL "Is muparser found?")
+    elseif(GMX_USE_MUPARSER STREQUAL "EXTERNAL")
+        # Find an external muparser library.
+        find_package(muparser ${GMX_MUPARSER_REQUIRED_VERSION})
+        if(NOT MUPARSER_FOUND OR MUPARSER_VERSION VERSION_LESS GMX_MUPARSER_REQUIRED_VERSION)
+            message(FATAL_ERROR "External muparser >= ${GMX_MUPARSER_REQUIRED_VERSION} could not be found, please adjust your pkg-config path to include the muparser.pc file")
+        endif()
+
+        set(HAVE_MUPARSER 1 CACHE INTERNAL "Is muparser found?")
+    else()
+        # Create a dummy link target so the calling code doesn't need to know
+        # whether muparser support is being compiled.
+        add_library(muparser INTERFACE)
+        # Add the muparser interface library to the libgromacs Export name, even though
+        # we will not be installing any content.
+        install(TARGETS muparser EXPORT libgromacs)
+
+        set(HAVE_MUPARSER 0 CACHE INTERNAL "Is muparser found?")
+    endif()
+        mark_as_advanced(HAVE_MUPARSER)
+endfunction()
index 7a4afab2a7a4938be182515afe9aeb4062ef994d..59109cd23b15f17feba7a00a96ef02cc46036707 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -50,7 +51,7 @@
 # glibc source shows that _FORCE_INLINES is only used in this string.h
 # feature and performance of memcpy variants is unimportant for CUDA
 # code in GROMACS. So this workaround is good enough to keep problems
-# away from users installing GROMACS. See Redmine 1942.
+# away from users installing GROMACS. See Issue #1942.
 function(work_around_glibc_2_23)
     try_compile(IS_GLIBC_2_23_OR_HIGHER ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/TestGlibcVersion.cpp)
     if(IS_GLIBC_2_23_OR_HIGHER)
@@ -85,8 +86,8 @@ endif()
 # If any of these manual override variables for target CUDA GPU architectures
 # or virtual architecture is set, parse the values and assemble the nvcc
 # command line for these. Otherwise use our defaults.
-# Note that the manual override variables require a semicolon separated
-# architectures codes.
+# Note that the manual override variables require a semicolon separating
+# architecture codes.
 if (GMX_CUDA_TARGET_SM OR GMX_CUDA_TARGET_COMPUTE)
     set(GMX_CUDA_NVCC_GENCODE_FLAGS)
     set(_target_sm_list ${GMX_CUDA_TARGET_SM})
@@ -108,7 +109,7 @@ else()
 
     # First add flags that trigger SASS (binary) code generation for physical arch
     if(CUDA_VERSION VERSION_LESS "11.0")
-       list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_30,code=sm_30")
+        list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_30,code=sm_30")
     endif()
     list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_35,code=sm_35")
     list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_37,code=sm_37")
@@ -155,6 +156,26 @@ list(APPEND GMX_CUDA_NVCC_FLAGS "-use_fast_math")
 # assemble the CUDA host compiler flags
 list(APPEND GMX_CUDA_NVCC_FLAGS "${CUDA_HOST_COMPILER_OPTIONS}")
 
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    # CUDA header cuda_runtime_api.h in at least CUDA 10.1 uses 0
+    # where nullptr would be preferable. GROMACS can't fix these, so
+    # must suppress them.
+    GMX_TEST_CXXFLAG(CXXFLAGS_NO_ZERO_AS_NULL_POINTER_CONSTANT "-Wno-zero-as-null-pointer-constant" NVCC_CLANG_SUPPRESSIONS_CXXFLAGS)
+    if (CUDA_VERSION VERSION_LESS 11.0)
+        # CUDA header crt/math_functions.h before CUDA 11.0 used
+        # throw() specifications that are deprecated in more recent
+        # C++ versions. GROMACS can't fix these, so must suppress
+        # them.
+        GMX_TEST_CXXFLAG(CXXFLAGS_NO_DEPRECATED_DYNAMIC_EXCEPTION_SPEC "-Wno-deprecated-dynamic-exception-spec" NVCC_CLANG_SUPPRESSIONS_CXXFLAGS)
+    endif()
+    # Add these flags to those used for the host compiler. The
+    # "-Xcompiler" prefix directs nvcc to only use them for host
+    # compilation, which is all that is needed in this case.
+    foreach(_flag ${NVCC_CLANG_SUPPRESSIONS_CXXFLAGS})
+        list(APPEND GMX_CUDA_NVCC_FLAGS "-Xcompiler ${_flag}")
+    endforeach()
+endif()
+
 string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type)
 gmx_check_if_changed(_cuda_nvcc_executable_or_flags_changed CUDA_NVCC_EXECUTABLE CUDA_NVCC_FLAGS CUDA_NVCC_FLAGS_${_build_type})
 
@@ -210,6 +231,9 @@ function(gmx_cuda_add_library TARGET)
     # Source files generated by NVCC can include gmxmpi.h, and so
     # need access to thread-MPI.
     include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include)
+    # Source files can also contain topology related files and need access to
+    # the remaining external headers
+    include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/src/external)
 
     # Now add all the compilation options
     gmx_cuda_target_compile_options(CUDA_${TARGET}_CXXFLAGS)
index 8170b531c0269a89b77a396316ecbc0758f8bdd4..3f849e9e2893d2fb63ac116ee316a7e7a210631f 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
+# OpenCL required version: 1.2 or newer
+set(REQUIRED_OPENCL_MIN_VERSION_MAJOR 1)
+set(REQUIRED_OPENCL_MIN_VERSION_MINOR 2)
+set(REQUIRED_OPENCL_MIN_VERSION ${REQUIRED_OPENCL_MIN_VERSION_MAJOR}.${REQUIRED_OPENCL_MIN_VERSION_MINOR})
+
+set(GMX_GPU_OPENCL ON)
+
 if(GMX_DOUBLE)
-    message(FATAL_ERROR "OpenCL not available in double precision - Yet!")
+    message(FATAL_ERROR "OpenCL acceleration is not available in double precision")
 endif()
 
 # for some reason FindOpenCL checks CUDA_PATH but not CUDA_HOME
@@ -74,9 +82,4 @@ endif()
 set(GMX_OPENCL_NB_CLUSTER_SIZE 8 CACHE STRING "Cluster size used by nonbonded OpenCL kernel. Set to 4 for Intel GPUs.")
 mark_as_advanced(GMX_OPENCL_NB_CLUSTER_SIZE)
 
-macro(gmx_gpu_setup)
-    # no OpenMP is no good!
-    if(NOT GMX_OPENMP)
-        message(WARNING "To use GPU acceleration efficiently, mdrun requires OpenMP multi-threading. Without OpenMP a single CPU core can be used with a GPU which is not optimal. Note that with MPI multiple processes can be forced to use a single GPU, but this is typically inefficient. You need to set both C and C++ compilers that support OpenMP (CC and CXX environment variables, respectively) when using GPUs.")
-    endif()
-endmacro()
+set(GMX_INSTALL_OCLDIR       ${GMX_INSTALL_GMXDATADIR}/opencl)
index 3673f84d98927279d85c9cb3fc1319c2ceac4ea0..f9328810fd039faa9ed437e377e035b6f93f40d2 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -41,8 +42,11 @@ if(GMX_OPENMP)
     # OpenMP check must come before other CFLAGS!
     find_package(OpenMP)
     if(NOT OPENMP_FOUND)
-        message(WARNING
-                "The compiler you are using does not support OpenMP parallelism. This might hurt your performance a lot, in particular with GPUs. Try using a more recent version, or a different compiler. For now, we are proceeding by turning off OpenMP.")
+        if(CMAKE_CXX_COMPILER MATCHES "dpcpp")
+           message(WARNING "The Intel dpcpp compiler does not support OpenMP; consider using the icx (C) & icpx (C++) compilers from the Intel HPC toolkit instead. For now, we are turning off OpenMP.")
+       else()
+            message(WARNING "The compiler you are using does not support OpenMP parallelism. This might hurt your performance a lot, in particular with GPUs. Try using a more recent version, or a different compiler. For now, we are proceeding by turning off OpenMP.")
+       endif()
         set(GMX_OPENMP OFF CACHE STRING "Whether GROMACS will use OpenMP parallelism." FORCE)
     endif()
 endif()
index e3af82761e3cf698127305f0023b1c501f350ede..83babae4850b7afb835f4100ec28059c751c805e 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2016,2017, by the GROMACS development team, led by
+# Copyright (c) 2016,2017,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-include(gmxOptionUtilities)
+# Executing this macro will add the option GMX_USE_PLUGINS, which
+# makes it possible to dynamically load modules at runtime.
+# This is a very neat feature, and should virtually always work
+# on Linux, but for now it will not work without shared libraries.
+# For this reason we disable it by default, to avoid triggering
+# errors here when dynamic libraries are disabled elsewhere.
+macro(gmx_manage_plugin_support)
+    option(GMX_USE_PLUGINS "Enable support for dynamic plugins (e.g. VMD-supported file formats)" OFF)
+    mark_as_advanced(GMX_USE_PLUGINS)
 
-# Sets GMX_USE_PLUGINS=ON in the parent scope if the toolchain and
-# user selections permit the build to support plugin loading.
-function(gmx_manage_plugin_support)
-    gmx_option_trivalue(GMX_LOAD_PLUGINS "Compile with plugin support, needed to read VMD supported file formats" AUTO)
-    mark_as_advanced(GMX_LOAD_PLUGINS)
+    if(GMX_USE_PLUGINS)
 
-    # Find out if non-Windows builds can support plugins. Native Windows
-    # neither needs nor has library support.
-    if (NOT WIN32)
-        # TODO Make a proper find_package for dlopen to find
-        # dlfcn.h. The CMake variable CMAKE_DL_LIBS works magically
-        # for the library, however.
-        include(gmxTestdlopen)
-        gmx_test_dlopen(HAVE_DLOPEN)
-    endif()
-
-    # Keep the status line quiet unless something relevant has
-    # changed.
-    gmx_check_if_changed(EMIT_STATUS_MESSAGES GMX_LOAD_PLUGINS BUILD_SHARED_LIBS HAVE_DLOPEN)
-
-    # Whether GROMACS will really try to compile support for VMD
-    # plugins.
-    set(GMX_USE_PLUGINS OFF)
+        message(STATUS "Checking build environment for dynamic plugins")
 
-    # Plugins are supported natively on Windows
-    if (WIN32 OR (BUILD_SHARED_LIBS AND HAVE_DLOPEN))
-        set(GMX_USE_PLUGINS ${GMX_LOAD_PLUGINS})
-    elseif(GMX_LOAD_PLUGINS)
-        # Can't support plugins for some reason. If the user required
-        # plugins, emit fatal errors. Otherwise, emit status messages
-        # for AUTO and be silent for OFF.
-        set(message "")
-        if (NOT HAVE_DLOPEN)
-            set(message "${message}dlopen() support for using dynamic plugins for VMD-supported file formats is missing. ")
-        endif()
         if(NOT BUILD_SHARED_LIBS)
-            set(message "${message}GROMACS only supports plugins in a build that uses shared libraries, which can be disabled for various reasons. BUILD_SHARED_LIBS=on and a toolchain that supports dynamic linking is required. (Hint: GMX_PREFER_STATIC_LIBS and GMX_BUILD_MDRUN_ONLY can influence the default BUILD_SHARED_LIBS, so if you need plugins, reconsider those choices.) ")
-        endif()
-        if (GMX_LOAD_PLUGINS_FORCE)
-            message(FATAL_ERROR "${message}Cannot build with GMX_LOAD_PLUGINS=${GMX_LOAD_PLUGINS}.")
-        endif()
-        if (GMX_LOAD_PLUGINS_AUTO AND EMIT_STATUS_MESSAGES)
-            message(STATUS "${message}")
+            message(FATAL_ERROR "Shared libraries not built - required for dynamic plugins")
         endif()
-    endif()
 
-    if(EMIT_STATUS_MESSAGES)
-        if(GMX_USE_PLUGINS)
-            MESSAGE(STATUS "Using dynamic plugins (e.g VMD-supported file formats)")
-        else()
-            MESSAGE(STATUS "Not using dynamic plugins (e.g VMD-supported file formats)")
+        # Plugins are supported natively on Windows, so nothing to check if WIN32 is set
+
+        if (NOT WIN32)
+            include(gmxTestdlopen)
+            gmx_test_dlopen(HAVE_DLOPEN)
+           if(NOT HAVE_DLOPEN)
+               message(FATAL_ERROR "dlopen() support missing - required for dynamic plugins")
+            endif()
         endif()
-    endif()
-    set(GMX_USE_PLUGINS ${GMX_USE_PLUGINS} PARENT_SCOPE)
-endfunction()
 
-gmx_manage_plugin_support()
+       message(STATUS "Checking build environment for dynamic plugins - supported")
 
-if(GMX_USE_PLUGINS)
-    list(APPEND GMX_EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) # magic cross-platform pre-set variable for dlopen library
-    set(PKG_DL_LIBS "-l${CMAKE_DL_LIBS}")
-endif()
+       list(APPEND GMX_EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) # magic cross-platform pre-set variable for dlopen library
+       set(PKG_DL_LIBS "-l${CMAKE_DL_LIBS}")
+
+    endif()
+endmacro()
diff --git a/cmake/gmxManageSYCL.cmake b/cmake/gmxManageSYCL.cmake
new file mode 100644 (file)
index 0000000..57c1338
--- /dev/null
@@ -0,0 +1,114 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+# OpenCL required version: 1.2 or newer
+set(REQUIRED_SYCL_MIN_VERSION_MAJOR 1)
+set(REQUIRED_SYCL_MIN_VERSION_MINOR 2)
+set(REQUIRED_SYCL_MIN_VERSION ${REQUIRED_SYCL_MIN_VERSION_MAJOR}.${REQUIRED_SYCL_MIN_VERSION_MINOR})
+
+set(GMX_GPU_SYCL ON)
+
+if(GMX_DOUBLE)
+    message(FATAL_ERROR "SYCL acceleration is not available in double precision")
+endif()
+
+include(gmxFindFlagsForSource)
+
+if(CMAKE_CXX_COMPILER MATCHES "dpcpp")
+    # At least Intel dpcpp defaults to having SYCL enabled for all code. This leads to two problems:
+    #
+    # 1. Compiles take ~3x longer, since every file has to be compiled for multiple targets.
+    # 2. We get a ton of warnings for the device-specific pass when the compiler sees our SIMD code.
+    #
+    # To avoid this, we attempt to find a flag to disable SYCL for non-SYCL files. Unfortunately,
+    # when using gmx_find_flag_for_source() that includes calling check_cxx_compiler_flag(),  
+    # this in turn exposes a bug in dpcpp, where an object file compiles with -fno-sycl leads to
+    # a failed link stage (when the same flag is not used). Since none of this is critical, we handle
+    # it by merely checking if it works to compile a source fils with this flag, and choking if SYCL
+    # is still enabled.
+
+    if(NOT CHECK_DISABLE_SYCL_CXX_FLAGS_QUIETLY)
+        message(STATUS "Checking for flags to disable SYCL")
+    endif()
+
+    gmx_check_source_compiles_with_flags(
+        "int main() { return 0; }"
+        "-fno-sycl"
+        "CXX"
+        DISABLE_SYCL_CXX_FLAGS_RESULT)
+
+    if(DISABLE_SYCL_CXX_FLAGS_RESULT)
+        set(DISABLE_SYCL_CXX_FLAGS "-fno-sycl")
+    endif()
+    if(NOT CHECK_DISABLE_SYCL_CXX_FLAGS_QUIETLY)
+        if(DISABLE_SYCL_CXX_FLAGS_RESULT)
+            message(STATUS "Checking for flags to disable SYCL - -fno-sycl")
+        else()
+            message(WARNING "Cannot find flags to disable SYCL for non-SYCL hardware-specific C++ code. Expect many warnings, but they are likely benign.")
+        endif()
+        set(CHECK_DISABLE_SYCL_CXX_FLAGS_QUIETLY 1 CACHE INTERNAL "Keep quiet on future calls to detect no-SYCL flags" FORCE)
+    endif()
+endif()
+
+# Find the flags to enable (or re-enable) SYCL with Intel extensions. In case we turned it off above,
+# it's important that we check the combination of both flags, to make sure the second one re-enables SYCL.
+if(NOT CHECK_SYCL_CXX_FLAGS_QUIETLY)
+    message(STATUS "Checking for flags to enable SYCL")
+endif()
+gmx_find_flag_for_source(SYCL_CXX_FLAGS_RESULT
+    "#include <CL/sycl.hpp>
+     namespace sycl = cl::sycl;
+     int main(){
+         constexpr int length = 1000;
+         sycl::queue q(sycl::default_selector{});
+         // Check USM extension
+         sycl::usm_allocator<int, sycl::usm::alloc::shared> q_alloc{q};
+         std::vector<int, sycl::usm_allocator<int, sycl::usm::alloc::shared>> v(q_alloc);
+         v.reserve(length);
+         for(int i = 0; i < length ; i++) { v.push_back(i); }
+         q.parallel_for<class whatever>(sycl::range<1>{length}, [=, ptr = v.data()] (sycl::id<1> i){ ptr[i] *= 2; }).wait();
+         return 0;
+     }
+     " "CXX" DISABLE_SYCL_CXX_FLAGS SYCL_CXX_FLAGS "-fsycl")
+
+if(NOT CHECK_SYCL_CXX_FLAGS_QUIETLY)
+    if(SYCL_CXX_FLAGS_RESULT)
+        message(STATUS "Checking for flags to enable SYCL - ${SYCL_CXX_FLAGS}")
+    endif()
+    set(CHECK_SYCL_CXX_FLAGS_QUIETLY 1 CACHE INTERNAL "Keep quiet on future calls to detect SYCL flags" FORCE)
+endif()
+
+if(NOT SYCL_CXX_FLAGS_RESULT)
+    message(ERROR "Cannot compile SYCL with Intel extensions. Try a different compiler or disable SYCL.")
+endif()
index cd507c166be8f868a9c87a413c5330678b0a8421..a946ea6460e3d6878f8bd8f379cbbf07253152ad 100644 (file)
@@ -1,8 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015, The GROMACS development team.
-# Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -247,6 +247,29 @@ elseif(GMX_SIMD_ACTIVE STREQUAL "ARM_NEON_ASIMD")
     set(GMX_SIMD_${GMX_SIMD_ACTIVE} 1)
     set(SIMD_STATUS_MESSAGE "Enabling ARM (AArch64) NEON Advanced SIMD instructions using CXX flags: ${SIMD_ARM_NEON_ASIMD_CXX_FLAGS}")
 
+elseif(GMX_SIMD_ACTIVE STREQUAL "ARM_SVE")
+
+    # Note that GMX_RELAXED_DOUBLE_PRECISION is enabled by default in the top-level CMakeLists.txt
+
+    gmx_option_multichoice(
+        GMX_SIMD_ARM_SVE_LENGTH
+        "SVE vector length"
+        "512"
+        128 256 512 1024 2048)
+
+    gmx_find_simd_arm_sve_flags(SIMD_ARM_SVE_C_SUPPORTED SIMD_ARM_SVE_CXX_SUPPORTED
+                                SIMD_ARM_SVE_C_FLAGS SIMD_ARM_SVE_CXX_FLAGS)
+
+    if(NOT SIMD_ARM_SVE_C_SUPPORTED OR NOT SIMD_ARM_SVE_CXX_SUPPORTED)
+        gmx_give_fatal_error_when_simd_support_not_found("ARM (AArch64) SVE SIMD" "particularly gcc version 10.1 or later, or disable SIMD support (slower)" "${SUGGEST_BINUTILS_UPDATE}")
+    endif()
+
+    # If multiple flags are neeed, make them into a list
+    string(REPLACE " " ";" SIMD_C_FLAGS ${SIMD_ARM_SVE_C_FLAGS})
+    string(REPLACE " " ";" SIMD_CXX_FLAGS ${SIMD_ARM_SVE_CXX_FLAGS})
+    set(GMX_SIMD_${GMX_SIMD_ACTIVE} 1)
+    set(SIMD_STATUS_MESSAGE "Enabling ARM (AArch64) SVE Advanced SIMD instructions using CXX flags: ${SIMD_ARM_SVE_CXX_FLAGS}")
+
 elseif(GMX_SIMD_ACTIVE STREQUAL "IBM_VMX")
 
     gmx_find_simd_ibm_vmx_flags(SIMD_IBM_VMX_C_SUPPORTED SIMD_IBM_VMX_CXX_SUPPORTED
@@ -278,7 +301,7 @@ elseif(GMX_SIMD_ACTIVE STREQUAL "IBM_VSX")
     # is not required for SIMD support on this platform. cmake through
     # at least version 3.7 cannot pass this check with the C compiler
     # in the latest xlc 13.1.5, but the C++ compiler has different
-    # behaviour and is OK. See Redmine #2102.
+    # behaviour and is OK. See Issue #2102.
     if(NOT SIMD_IBM_VSX_CXX_SUPPORTED)
         gmx_give_fatal_error_when_simd_support_not_found("IBM VSX" "disable SIMD support (slower)" "${SUGGEST_BINUTILS_UPDATE}")
     endif()
@@ -397,7 +420,7 @@ if(NOT DEFINED GMX_SIMD_CALLING_CONVENTION)
         # ignored (e.g. clang on ARM), and in such cases we want this
         # check to lead to using no attribute in subsequent GROMACS
         # compilation, to avoid issuing the warning for lots of files.
-        check_c_source_compiles("
+        check_cxx_source_compiles("
 #pragma GCC diagnostic error \"-Wignored-attributes\"
 int ${callconv} f(int i) {return i;} int main(void) {return f(0);}
 " ${callconv_compile_var})
index 01b55c93ce2b74c72a240ade7ae692907d6e1e21..c893a4f02a731dd413e30cd5b8687a49d459bb46 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013,2014,2015,2017, by the GROMACS development team, led by
+# Copyright (c) 2013,2014,2015,2017,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -88,39 +88,6 @@ function(GMX_INVALID_OPTION_VALUE NAME)
     message(FATAL_ERROR "Invalid value for ${NAME}: ${${NAME}}")
 endfunction()
 
-# Declares a cache variable with ON/OFF/AUTO values
-#
-# Usage:
-#   gmx_option_trivalue(VAR "Description" DEFAULT)
-#
-# Output:
-#   VAR is created in the cache, and the caller can assume that the value is
-#   always one of ON/OFF/AUTO.  Additionally, VAR_AUTO is set if value is AUTO,
-#   and VAR_FORCE is set if value is ON.
-#   These make it convenient to check for any combination of states with simple
-#   if() statements (simple if(VAR) matches AUTO and ON).
-function(GMX_OPTION_TRIVALUE NAME DESCRIPTION DEFAULT)
-    set(_description "${DESCRIPTION}. ON/OFF/AUTO")
-    set(${NAME} ${DEFAULT} CACHE STRING "${_description}")
-    set_property(CACHE ${NAME} PROPERTY STRINGS ON OFF AUTO)
-
-    set(${NAME}_AUTO OFF)
-    set(${NAME}_FORCE OFF)
-    string(TOUPPER "${${NAME}}" ${NAME})
-    if ("${${NAME}}" STREQUAL "AUTO")
-        set(${NAME}_AUTO ON)
-    elseif (${NAME})
-        set(${NAME}_FORCE ON)
-        set(${NAME} ON)
-    else()
-        set(${NAME} OFF)
-    endif()
-    # Always provide the sanitized value to the caller
-    set(${NAME}       "${${NAME}}"       PARENT_SCOPE)
-    set(${NAME}_AUTO  "${${NAME}_AUTO}"  PARENT_SCOPE)
-    set(${NAME}_FORCE "${${NAME}_FORCE}" PARENT_SCOPE)
-endfunction()
-
 # Hides or shows a cache value based on conditions
 #
 # Usage:
diff --git a/cmake/gmxPythonDiscovery.cmake b/cmake/gmxPythonDiscovery.cmake
new file mode 100644 (file)
index 0000000..d5bcbaf
--- /dev/null
@@ -0,0 +1,70 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+# Perform Python installation discovery early and in one place, for consistency.
+#
+# Note: If necessary, the Python location can be hinted with Python3_ROOT_DIR
+# For additional parameters affecting Python installation discovery, see
+# https://cmake.org/cmake/help/latest/module/FindPython3.html#hints
+if(FIND_PACKAGE_MESSAGE_DETAILS_Python3)
+    # Keep quiet on subsequent runs of cmake
+    set(Python3_FIND_QUIETLY ON)
+    set(PythonInterp_FIND_QUIETLY ON)
+endif()
+# Older CMake versions might not search for Python newer than 3.7.
+set(Python_ADDITIONAL_VERSIONS 3.8 3.9 3.10)
+# We advocate using Python venvs to manage package availability, so by default
+# we want to preferentially discover user-space software.
+set(Python3_FIND_REGISTRY LAST)
+# Make package discovery consistent with Unix behavior and our documented
+# suggestions for installing dependencies.
+set(CMAKE_FIND_FRAMEWORK LAST)
+if(GMX_PYTHON_PACKAGE)
+    find_package(Python3 3.6 COMPONENTS Interpreter Development)
+    if (NOT Python3_FOUND OR NOT Python3_Development_FOUND)
+        message(FATAL_ERROR "Could not locate Python development requirements. \
+                Provide appropriate CMake hints or set GMX_PYTHON_PACKAGE=OFF")
+    endif ()
+else()
+    find_package(Python3 3.6 COMPONENTS Interpreter)
+endif()
+# Other components, such as pybind and googletest, may expect the
+# PYTHON_EXECUTABLE variable from pre-3.12 FindPythonInterp.cmake.
+if (Python3_Interpreter_FOUND)
+    set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE} CACHE FILEPATH "Location hint for Python interpreter.")
+endif ()
+# We've already generated all of the output we need, even though other subcomponents
+# may call find_package(PythonInterp) later on.
+set(Python3_FIND_QUIETLY ON)
+set(PythonInterp_FIND_QUIETLY ON)
index 4368f02b22428e525a29c17c7e1a5a02375b968b..8ecf94d84dcc31d07e8d6e1f06f40b307b49fe6c 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index c8e9a8e75d3c008db7fe8e5783ac8cfaebd80f4b..bfa178d1932be71c3c1f110bc002cbe487007c70 100644 (file)
@@ -341,6 +341,28 @@ function(gmx_find_simd_arm_neon_asimd_flags C_FLAGS_RESULT CXX_FLAGS_RESULT C_FL
     set(${CXX_FLAGS_RESULT} ${SIMD_ARM_NEON_ASIMD_CXX_FLAGS_RESULT} CACHE INTERNAL "Result of test for Arm Neon Asimd C++ flags" FORCE)
 endfunction()
 
+# Arm SVE (64-bit ARM)
+function(gmx_find_simd_arm_sve_flags C_FLAGS_RESULT CXX_FLAGS_RESULT C_FLAGS_VARIABLE CXX_FLAGS_VARIABLE)
+
+    gmx_find_flags(SIMD_ARM_SVE_C_FLAGS_RESULT SIMD_ARM_SVE_CXX_FLAGS_RESULT
+        "#include<arm_sve.h>
+         int main(){float32_t x __attribute((vector_size(${GMX_SIMD_ARM_SVE_LENGTH}/8))) = svdup_f32(0.5f); return 0;}"
+        TOOLCHAIN_C_FLAGS TOOLCHAIN_CXX_FLAGS
+        SIMD_ARM_SVE_C_FLAGS SIMD_ARM_SVE_CXX_FLAGS
+        "-msve-vector-bits=${GMX_SIMD_ARM_SVE_LENGTH}"
+        "-march=armv8.2-a+sve -msve-vector-bits=${GMX_SIMD_ARM_SVE_LENGTH}"
+        "-march=armv8.2a+sve -msve-vector-bits=${GMX_SIMD_ARM_SVE_LENGTH}")
+
+    if(${SIMD_ARM_SVE_C_FLAGS_RESULT})
+        set(${C_FLAGS_VARIABLE} "${TOOLCHAIN_C_FLAGS} ${SIMD_ARM_SVE_C_FLAGS}" CACHE INTERNAL "C flags required for Arm SVE instructions")
+    endif()
+    if(${SIMD_ARM_SVE_CXX_FLAGS_RESULT})
+        set(${CXX_FLAGS_VARIABLE} "${TOOLCHAIN_CXX_FLAGS} ${SIMD_ARM_SVE_CXX_FLAGS}" CACHE INTERNAL "C++ flags required for Arm SVE instructions")
+    endif()
+    set(${C_FLAGS_RESULT} ${SIMD_ARM_SVE_C_FLAGS_RESULT} CACHE INTERNAL "Result of test for Arm SVE C flags" FORCE)
+    set(${CXX_FLAGS_RESULT} ${SIMD_ARM_SVE_CXX_FLAGS_RESULT} CACHE INTERNAL "Result of test for Arm SVE C++ flags" FORCE)
+endfunction()
+
 # IBM VMX (power6)
 function(gmx_find_simd_ibm_vmx_flags C_FLAGS_RESULT CXX_FLAGS_RESULT C_FLAGS_VARIABLE CXX_FLAGS_VARIABLE)
 
index 53e534eb4fe4d7c73800380ed24656824e33156f..ecce618630abaa6f2c5bcc877eaa425ba27b9bef 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index bc06d5af8f8db630991c2d7cf7a340d931a03d95..b008a791706a1fe048a254414282af8921a9db44 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 93001daf603c9118327d5a6bf3797891ffe6ab0a..9f8214dcbd9a699afdfc460308c2410daebe42fb 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2009,2010,2012,2013,2014,2015, by the GROMACS development team, led by
+# Copyright (c) 2009,2010,2012,2013,2014 by the GROMACS development team.
+# Copyright (c) 2015,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 83e1a7e9f52f4cc2bd88b8db4352ab50bbf7b660..5fbde1e86be6ca053a7053b04f6bebbeaf71a561 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2009,2010,2012,2013,2014,2015, by the GROMACS development team, led by
+# Copyright (c) 2009,2010,2012,2013,2014 by the GROMACS development team.
+# Copyright (c) 2015,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -32,7 +33,6 @@
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-#
 #  GMX_TEST_MM_MALLOC(VARIABLE)
 #
 #  VARIABLE will be set to true if we find _mm_malloc() and _mm_free().
index c56e05c3ae94d8b87fa91b5dfdb0baee27bcc5d2..54cdb1046ddf90d31dcace41b9592f42c84e41ec 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2009,2011,2012,2014,2015,2016,2020, by the GROMACS development team, led by
+# Copyright (c) 2009,2011,2012,2014,2015 by the GROMACS development team.
+# Copyright (c) 2016,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 3a454711783c32d6ee33f1cc2b365d1144d59430..00aee08091cba4307e99b68559dc87ae1935c9cf 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2015, by the GROMACS development team, led by
+# Copyright (c) 2012,2015,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -44,6 +44,9 @@ MACRO(GMX_TEST_DLOPEN VARIABLE)
     MESSAGE(STATUS "Checking for dlopen")
 
     set(CMAKE_REQUIRED_INCLUDES "dlfcn.h")
+    # TODO Make a proper find_package for dlopen to find
+    # dlfcn.h. The CMake variable CMAKE_DL_LIBS works magically
+    # for the library, however.
     set(CMAKE_REQUIRED_LIBRARIES "dl")
     check_c_source_compiles(
       "#include <dlfcn.h>
index 88cfd71d7b022feafe8062038a2afd93c06ad021..58f3ff6efc8389ca82ee090cc010f03057a7e155 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -58,6 +59,7 @@
 #         GROMACS     2018   3
 #         GROMACS     2019   4
 #         GROMACS     2020   5
+#         GROMACS     2021   6
 #   LIBRARY_SOVERSION_MINOR so minor version for the built libraries.
 #       Should be increased for each release that changes only the implementation.
 #       In GROMACS, the typical policy is to increase it for each patch version
 
 # The GROMACS convention is that these are the version number of the next
 # release that is going to be made from this branch.
-set(GMX_VERSION_MAJOR 2020)
-set(GMX_VERSION_PATCH 5)
+set(GMX_VERSION_MAJOR 2021)
+set(GMX_VERSION_PATCH 0)
 # The suffix, on the other hand, is used mainly for betas and release
 # candidates, where it signifies the most recent such release from
 # this branch; it will be empty before the first such release, as well
@@ -212,7 +214,7 @@ set(GMX_VERSION_SUFFIX "")
 # here. The important thing is to minimize the chance of third-party
 # code being able to dynamically link with a version of libgromacs
 # that might not work.
-set(LIBRARY_SOVERSION_MAJOR 5)
+set(LIBRARY_SOVERSION_MAJOR 6)
 set(LIBRARY_SOVERSION_MINOR 0)
 set(LIBRARY_VERSION ${LIBRARY_SOVERSION_MAJOR}.${LIBRARY_SOVERSION_MINOR}.0)
 
@@ -254,13 +256,13 @@ if (NOT SOURCE_IS_SOURCE_DISTRIBUTION AND
 endif()
 
 set(REGRESSIONTEST_VERSION "${GMX_VERSION_STRING}")
-set(REGRESSIONTEST_BRANCH "release-2020")
+set(REGRESSIONTEST_BRANCH "master")
 # Run the regressiontests packaging job with the correct pakage
 # version string, and the release box checked, in order to have it
 # build the regressiontests tarball with all the right naming. The
 # naming affects the md5sum that has to go here, and if it isn't right
 # release workflow will report a failure.
-set(REGRESSIONTEST_MD5SUM "5965bd26a96f1b5916af0484aa1594e2" CACHE INTERNAL "MD5 sum of the regressiontests tarball for this GROMACS version")
+set(REGRESSIONTEST_MD5SUM "b75c3b1bac0e4114e236f049fc7f1f1c" CACHE INTERNAL "MD5 sum of the regressiontests tarball for this GROMACS version")
 
 math(EXPR GMX_VERSION_NUMERIC
      "${GMX_VERSION_MAJOR}*10000 + ${GMX_VERSION_PATCH}")
@@ -335,9 +337,6 @@ set(SET_OF_DIRECTORIES_TO_CHECKSUM  "src")
 list(APPEND SET_OF_DIRECTORIES_TO_CHECKSUM "python_packaging")
 # Due to the limitations for passing a list as arguments, we make the directories a string here
 string(REPLACE ";" ":" DIRECTORIES_TO_CHECKSUM_STRING "${SET_OF_DIRECTORIES_TO_CHECKSUM}")
-# Try to find python for the checksumming script
-set(PythonInterp_FIND_QUIETLY ON)
-find_package(PythonInterp 3.5)
 
 # Rules to create the VersionInfo.cmake file.
 # For git info, the sequence is:
index 52af462e040cabbc9ecf9b6e5eddb5e411452089..4db45f30e0642a1491eaabdd309b7826f636393b 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -47,9 +48,8 @@
 # of configure time, because 1) some of the version variables are only
 # available during build time, and 2) we don't want to do all the Sphinx setup
 # during configuration to save some time when not building the content.
-# All the generated values get put into conf-vars.py (generated from
-# conf-vars.py.cmakein), which in turn is included by the Sphinx configuration
-# file conf.py.
+# All of the generated values get put into conf.py (generated from
+# conf.cmakein.py).
 
 set(SOURCE_MD5SUM "unknown" CACHE STRING
     "MD5 sum of the source tarball, normally used only for the pre-release webpage build")
@@ -327,19 +327,21 @@ if (SPHINX_FOUND)
     set(SPHINX_SOURCE_FILES
         index.rst
         download.rst
-        conf.py
         links.dat
         dev-manual/build-system.rst
         dev-manual/change-management.rst
         dev-manual/commitstyle.rst
+        dev-manual/containers.rst
         dev-manual/documentation-generation.rst
         dev-manual/contribute.rst
         dev-manual/doxygen.rst
         dev-manual/error-handling.rst
         dev-manual/formatting.rst
+        dev-manual/gitlab.rst
         dev-manual/gmxtree.rst
         dev-manual/includestyle.rst
         dev-manual/index.rst
+        dev-manual/infrastructure.rst
         dev-manual/jenkins.rst
         dev-manual/known-issues.rst
         dev-manual/language-features.rst
@@ -361,6 +363,15 @@ if (SPHINX_FOUND)
         how-to/visualize.rst
         install-guide/index.rst
         release-notes/index.rst
+        release-notes/2021/major/highlights.rst
+        release-notes/2021/major/features.rst
+        release-notes/2021/major/performance.rst
+        release-notes/2021/major/tools.rst
+        release-notes/2021/major/bugs-fixed.rst
+        release-notes/2021/major/removed-functionality.rst
+        release-notes/2021/major/deprecated-functionality.rst
+        release-notes/2021/major/portability.rst
+        release-notes/2021/major/miscellaneous.rst
         release-notes/2020/2020.1.rst
         release-notes/2020/2020.2.rst
         release-notes/2020/2020.3.rst
@@ -475,7 +486,7 @@ if (SPHINX_FOUND)
         set(IMAGE_CONVERT_STRING "impossible")
     endif()
 
-    set(SPHINX_CONFIG_VARS_FILE ${SPHINX_INPUT_DIR}/conf-vars.py)
+    set(SPHINX_CONFIG_FILE ${SPHINX_INPUT_DIR}/conf.py)
     if (GMX_PYTHON_PACKAGE)
         set(GMXAPI_PYTHON_STAGING_DIR ${CMAKE_BINARY_DIR}/python_packaging/src/gmxapi_staging)
         # TODO: Resolve circular reference. We would like to get the CMake build-time directory for
@@ -485,23 +496,29 @@ if (SPHINX_FOUND)
         # in this context?
     endif ()
 
-    gmx_configure_version_file(conf-vars.py.cmakein ${SPHINX_CONFIG_VARS_FILE}
+    gmx_configure_version_file(
+        conf.cmakein.py ${SPHINX_CONFIG_FILE}
         EXTRA_VARS
-            SPHINX_EXTENSION_PATH RELENG_PATH
-            IMAGE_CONVERT_STRING
+            CMAKE_MINIMUM_REQUIRED_VERSION
             EXPECTED_DOXYGEN_VERSION
             EXPECTED_SPHINX_VERSION
-            CMAKE_MINIMUM_REQUIRED_VERSION REQUIRED_CUDA_VERSION
-            REQUIRED_OPENCL_MIN_VERSION
-            REQUIRED_CUDA_COMPUTE_CAPABILITY REGRESSIONTEST_VERSION
-            SOURCE_MD5SUM REGRESSIONTEST_MD5SUM_STRING
-            GMX_TNG_MINIMUM_REQUIRED_VERSION
+            GMX_ADMIN_DIR
             GMX_LMFIT_REQUIRED_VERSION
             GMX_MANUAL_DOI_STRING
+            GMX_TNG_MINIMUM_REQUIRED_VERSION
             GMX_SOURCE_DOI_STRING
             GMXAPI_PYTHON_STAGING_DIR
+            IMAGE_CONVERT_STRING
+            REGRESSIONTEST_VERSION
+            REQUIRED_CUDA_COMPUTE_CAPABILITY
+            REQUIRED_CUDA_VERSION
+            REQUIRED_OPENCL_MIN_VERSION
+            REGRESSIONTEST_MD5SUM_STRING
+            RELENG_PATH
+            SOURCE_MD5SUM
+            SPHINX_EXTENSION_PATH
         COMMENT "Configuring Sphinx configuration file")
-    gmx_add_sphinx_input_file(${SPHINX_CONFIG_VARS_FILE})
+    gmx_add_sphinx_input_file(${SPHINX_CONFIG_FILE})
     gmx_add_sphinx_source_files(FILES ${SPHINX_SOURCE_FILES})
     if (EXISTS ${RELENG_PATH}/docs/FileList.cmake)
         include(${RELENG_PATH}/docs/FileList.cmake)
@@ -535,6 +552,12 @@ if (SPHINX_FOUND)
             )
     endif ()
 
+    gmx_add_sphinx_source_files(
+            FILES
+            nblib/index.rst
+            nblib/guide-to-writing-MD-programs.rst
+    )
+
     gmx_add_sphinx_source_files(
         FILES
         ${REFERENCEMANUAL_SPHINX_FILES_GENERAL})
@@ -601,6 +624,10 @@ if (SPHINX_FOUND)
 
     # Sphinx cache with pickled ReST documents
     set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees")
+    set(SPHINX_CONFIG_OVERRIDES "")
+    if (GMX_DEVELOPER_BUILD)
+        set(SPHINX_CONFIG_OVERRIDES "-Dtodo_include_todos=1")
+    endif()
     add_custom_target(webpage-sphinx
         DEPENDS sphinx-programs
         DEPENDS sphinx-input
@@ -613,6 +640,7 @@ if (SPHINX_FOUND)
             -q -b html
             -w sphinx-html.log
             -d "${SPHINX_CACHE_DIR}"
+            ${SPHINX_CONFIG_OVERRIDES}
             "${SPHINX_INPUT_DIR}"
             "${HTML_OUTPUT_DIR}"
         WORKING_DIRECTORY
@@ -685,7 +713,7 @@ set(HTML_BUILD_NOT_POSSIBLE_REASON)
 set(HTML_BUILD_WARNINGS)
 
 # Next, turn it off if any of the preconditions are unsatisified
-if (NOT PythonInterp_FOUND)
+if (NOT Python3_Interpreter_FOUND)
     set(HTML_BUILD_IS_POSSIBLE OFF)
     set(HTML_BUILD_NOT_POSSIBLE_REASON "Python is required")
 elseif (NOT SPHINX_FOUND)
index 34b71b439b990ba6d9383ba9172ea7f5cd66b6da..2864e911a1ce1367b28b7f4334eee85131ae7943 100644 (file)
@@ -32,8 +32,6 @@ TABLE OF CONTENTS
 
 - Quite a few error conditions are unhandled, noted with TODOs in several files
 
-- gmx_device_info_t needs struct field documentation
-
 3. ENHANCEMENTS
    ============
 - Implement OpenCL kernels for Intel GPUs
diff --git a/docs/conf-vars.py.cmakein b/docs/conf-vars.py.cmakein
deleted file mode 100644 (file)
index a13a82f..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-gmx_sphinx_extension_path = '@SPHINX_EXTENSION_PATH@'
-gmxapi_staging_path = '@GMXAPI_PYTHON_STAGING_DIR@'
-releng_path = '@RELENG_PATH@'
-gmx_version_string = '@GMX_VERSION_STRING@'
-gmx_version_string_full = '@GMX_VERSION_STRING_FULL@'
-regressiontest_version = '@REGRESSIONTEST_VERSION@'
-gmx_min_sphinx = '@EXPECTED_SPHINX_VERSION@'
-gmx_image_convert = '@IMAGE_CONVERT_STRING@'
-variables = [
-        ('EXPECTED_DOXYGEN_VERSION', '@EXPECTED_DOXYGEN_VERSION@'),
-        ('EXPECTED_SPHINX_VERSION', '@EXPECTED_SPHINX_VERSION@'),
-        ('CMAKE_MINIMUM_REQUIRED_VERSION', '@CMAKE_MINIMUM_REQUIRED_VERSION@'),
-        ('REQUIRED_CUDA_VERSION', '@REQUIRED_CUDA_VERSION@'),
-        ('REQUIRED_CUDA_COMPUTE_CAPABILITY', '@REQUIRED_CUDA_COMPUTE_CAPABILITY@'),
-        ('REQUIRED_OPENCL_MIN_VERSION', '@REQUIRED_OPENCL_MIN_VERSION@'),
-        ('SOURCE_MD5SUM', '@SOURCE_MD5SUM@'),
-        ('REGRESSIONTEST_MD5SUM', '@REGRESSIONTEST_MD5SUM_STRING@'),
-        ('GMX_TNG_MINIMUM_REQUIRED_VERSION', '@GMX_TNG_MINIMUM_REQUIRED_VERSION@'),
-        ('GMX_LMFIT_REQUIRED_VERSION', '@GMX_LMFIT_REQUIRED_VERSION@'),
-        ('GMX_MANUAL_DOI_STRING', '@GMX_MANUAL_DOI_STRING@'),
-        ('GMX_SOURCE_DOI_STRING', '@GMX_SOURCE_DOI_STRING@')
-    ]
similarity index 85%
rename from docs/conf.py
rename to docs/conf.cmakein.py
index 929a34c0512087ddb528c3d35b455348b771ca86..ca737a50d11ea2e2b8c4f2995bb5250b31aa6a2c 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2015,2016,2017,2018,2019 by the GROMACS development team.
+# Copyright (c) 2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -50,7 +51,33 @@ import datetime
 import os
 import sys
 
-exec(open('conf-vars.py').read())
+# The following definitions are completed via CMake machinery.
+gmx_containers_path = '@GMX_ADMIN_DIR@/containers'
+gmx_sphinx_extension_path = '@SPHINX_EXTENSION_PATH@'
+gmxapi_staging_path = '@GMXAPI_PYTHON_STAGING_DIR@'
+releng_path = '@RELENG_PATH@'
+gmx_version_string = '@GMX_VERSION_STRING@'
+gmx_version_string_full = '@GMX_VERSION_STRING_FULL@'
+regressiontest_version = '@REGRESSIONTEST_VERSION@'
+gmx_min_sphinx = '@EXPECTED_SPHINX_VERSION@'
+gmx_image_convert = '@IMAGE_CONVERT_STRING@'
+variables = [
+    ('EXPECTED_DOXYGEN_VERSION', '@EXPECTED_DOXYGEN_VERSION@'),
+    ('EXPECTED_SPHINX_VERSION', '@EXPECTED_SPHINX_VERSION@'),
+    ('CMAKE_MINIMUM_REQUIRED_VERSION', '@CMAKE_MINIMUM_REQUIRED_VERSION@'),
+    ('REQUIRED_CUDA_VERSION', '@REQUIRED_CUDA_VERSION@'),
+    ('REQUIRED_CUDA_COMPUTE_CAPABILITY', '@REQUIRED_CUDA_COMPUTE_CAPABILITY@'),
+    ('REQUIRED_OPENCL_MIN_VERSION', '@REQUIRED_OPENCL_MIN_VERSION@'),
+    ('SOURCE_MD5SUM', '@SOURCE_MD5SUM@'),
+    ('REGRESSIONTEST_MD5SUM', '@REGRESSIONTEST_MD5SUM_STRING@'),
+    ('GMX_TNG_MINIMUM_REQUIRED_VERSION', '@GMX_TNG_MINIMUM_REQUIRED_VERSION@'),
+    ('GMX_LMFIT_REQUIRED_VERSION', '@GMX_LMFIT_REQUIRED_VERSION@'),
+    ('GMX_MANUAL_DOI_STRING', '@GMX_MANUAL_DOI_STRING@'),
+    ('GMX_SOURCE_DOI_STRING', '@GMX_SOURCE_DOI_STRING@')
+]
+# End of build-time substitutions.
+
+sys.path.append(gmx_containers_path)
 sys.path.append(gmx_sphinx_extension_path)
 if releng_path and os.path.isdir(releng_path):
     sys.path.append(releng_path)
@@ -83,9 +110,17 @@ extensions = [
     'sphinx.ext.mathjax',
     'sphinx.ext.napoleon',
     'sphinx.ext.todo',
+    # The viewcode extension requires extra configuration or consideration to
+    # avoid collecting sources for external packages (through intersphninx) or
+    # generating broken links for compiled extension modules that do not have
+    # inspectable 'code' member data.
+    # Ref: https://www.sphinx-doc.org/en/master/usage/extensions/viewcode.html
+    # Please run linkchecker and inspect the generated
+    # docs/html/_modules/index.html page before committing a change that enables
+    # 'sphinx.ext.viewcode',
     'gmxsphinx'
 ]
-extlinks = {'issue': ('https://redmine.gromacs.org/issues/%s',
+extlinks = {'issue': ('https://gitlab.com/gromacs/gromacs/-/issues/%s',
                       'Issue ')}
 
 # Add any paths that contain templates here, relative to this directory.
@@ -193,6 +228,9 @@ rst_epilog += """
 .. _webpage: http://www.gromacs.org
 .. _ftp site: ftp://ftp.gromacs.org/pub/gromacs/
 .. _tutorials: http://www.gromacs.org/Documentation/Tutorials
+.. _issue tracker: https://gitlab.com/gromacs/gromacs/-/issues/
+.. _issues: https://gitlab.com/gromacs/gromacs/-/issues/
+.. _gitlab: https://gitlab.com/gromacs/gromacs/
 .. _redmine: http://redmine.gromacs.org
 .. _gerrit: http://gerrit.gromacs.org
 .. _download: ../download.html
@@ -384,6 +422,11 @@ texinfo_documents = [
 # Make it possible to use numbered labels for figures and tables
 numfig = True
 
+# -- Options for autodoc extension ----------------------------------------
+
+# http://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc-mock-imports
+autodoc_mock_imports = ['hpccm', 'hpccm.config']
+
 # -- Options for intersphinx extension ------------------------------------
 
 intersphinx_mapping = {'python': ('https://docs.python.org/3', None)}
index f7a3f0ab400ef653e9bc95913007e260046d4d58..a35b57f78427902aba124c80fa84cdb690a8038e 100644 (file)
@@ -111,10 +111,14 @@ This section provides a (currently incomplete) list of cache variables that
 developers or advanced users can set to affect what CMake generates and/or what
 will get built.
 
-.. TODO: Figure out where to document basic variables intended for user
+.. todo::
+
+   Figure out where to document basic variables intended for user
    consumption, and how does it relate to documentation here.
 
-.. TODO: Document the remaining variables below, and identify any variables
+.. todo::
+
+   Document the remaining variables below, and identify any variables
    missing from the list.
 
 Compiler flags
@@ -170,7 +174,7 @@ Variables affecting compilation/linking
    Defaults to ``OFF``, and there should not be any need to change this in a
    manual build.
 
-   .. TODO: This could likely be replaced by a (yet another) build type.
+   .. todo:: This could likely be replaced by a (yet another) build type.
 
 .. cmake:: GMX_BUILD_MDRUN_ONLY
 
@@ -348,13 +352,13 @@ Variables affecting the ``all`` target
 
 .. cmake:: GMX_CLANG_TIDY
 
-  `clang-tidy <http://releases.llvm.org/8.0.0/tools/clang/tools/extra/docs/clang-tidy/index.html>`_
+  `clang-tidy <http://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/index.html>`_
   is used for static code analysis and (some) automated fixing of issues detected. clang-tidy is easy to install. It is contained in
-  the llvm binary `package <http://releases.llvm.org/download.html#8.0.0>`_. Only
-  version 8.0.* with libstdc++<7 or libc++ is supported. Others might miss tests or give false positives.
-  It is run automatically on Jenkins for each commit. Many checks have fixes which can automatically be
+  the llvm binary `package <http://releases.llvm.org/download.html#9.0.0>`_. Only
+  version 9.0.* with libstdc++<7 or libc++ is supported. Others might miss tests or give false positives.
+  It is run automatically in gitlab CI for each commit. Many checks have fixes which can automatically be
   applied. To run it, the build has to be configured with
-  ``cmake -DGMX_CLANG_TIDY=ON -DGMX_OPENMP=no -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=on``.
+  ``cmake -DGMX_CLANG_TIDY=ON -DCMAKE_BUILD_TYPE=Debug``.
   Any ``CMAKE_BUILD_TYPE`` which enables asserts (e.g. ASAN) works. Such a configured build will
   run both the compiler as well as clang-tidy when building. The name of the clang-tidy executable is set with
   ``-DCLANG_TIDY=...``, and the full path to it can be set with ``-DCLANG_TIDY_EXE=...``.
@@ -388,7 +392,7 @@ Variables affecting special targets
    If ``OFF`` (the default), all detection is skipped and the manual cannot be
    built.
 
-   .. TODO: Consider if this is really necessary, or if we could just use
+   .. todo:: Consider if this is really necessary, or if we could just use
       GMX_DEVELOPER_BUILD.
 
 .. cmake:: GMX_BUILD_TARBALL
@@ -442,7 +446,9 @@ Variables affecting special targets
 External libraries
 ------------------
 
-.. TODO: List external libraries used (either from src/external/, or from the
+.. todo::
+
+   List external libraries used (either from src/external/, or from the
    system), whether they are required or optional, what functionality they
    provide for Gromacs, and how to control their use.
 
index 9e281654f5a9baf4eb537182a69d37d0f79e9828..a5cf8d824fa9c3217b0cd4fc57b4acef1689662c 100644 (file)
@@ -1,8 +1,6 @@
-.. _gmx-gerrit:
-
-=========================
-GROMACS change management
-=========================
+=================
+Change Management
+=================
 
 This documentation assumes the reader is already familiary with using ``git``
 for managing file revisions.
@@ -13,489 +11,188 @@ for managing file revisions.
 Getting started
 ===============
 
-#.  Go to https://gerrit.gromacs.org
-#.  Click Register (you can choose any OpenID provider including any
-    existing Google/Yahoo account. If you manually enter the URL make sure
-    to start with ``http(s)://``)
-#.  Choose a username and add an ssh key
-
-See `here <https://gerrit.gromacs.org/Documentation/intro-quick.html>`_ for
-a quick intro into Gerrit.
-
-Creating the SSH key for Gerrit
--------------------------------
-
-In order to push your commits to gerrit server, you must have an SSH key
-in your computer which matches with the one registered in your Gerrit
-user account. To do so, you first need to create this unique SSH
-key. You will be asked to enter a passphrase. *This is
-optional with respect to Gerrit, but it is a good security practice to have
-it.*
-
-To proceed with the creation of the SSH key, type the following commands
-from your terminal window:
-
-::
-
-    $ cd ~/.ssh
-
-    $ ssh-keygen -t rsa -C "your.email@address.com"
-
-Please substitute the email string in the command above
-with the same email address which you used to register the account in
-Gerrit.
-
-Now you have created your public SSH key, which you need to copy/paste
-into your Gerrit profile. First, open it with the following command:
-
-::
-
-    $ cat id_rsa.pub
-
-Copy all the contents of the file id_rsa.pub in your clipboard, and
-switch to your favorite web browser where you logged in to Gerrit
-GROMACS page. Click on your username at the top right corner of the
-Gerrit webpage and select "Settings". You should now be in your Gerrit
-profile settings page, where you should see a vertical menu.
-
-From this vertical menu, select "SSH Public Keys", then click the button
-"Add Key ..." and an edit box will appear below the button. Here you
-need to paste the contents of id_rsa.pub file, which you previously
-copied to your clipboard.
-
-Now you are ready to operate!
+GROMACS development happens on gitlab at https://gitlab.com/gromacs/gromacs.
+Create a user account at https://gitlab.com/users/sign_in#register-pane or use
+an exisiting account at gitlab.com. For more information on how to use gitlab have 
+a look at their extensive user documentation at https://docs.gitlab.com/ee/user/index.html.
+We follow the workflow described in https://docs.gitlab.com/ee/topics/gitlab_flow.html. 
 
-Setting up a local repository to work with gerrit
--------------------------------------------------
+If you do not already have a GROMACS repository set up, user 
+``git clone git@gitlab.com:gromacs/gromacs.git`` to obtain the current GROMACS
+repository from gitlab. Otherwise use 
+``git remote add gitlab git@gitlab.com:gromacs/gromacs.git``. 
 
-Either clone using::
+Using gitlab, new code enters GROMACS by merging git development branches into
+the master branch. 
 
-    $ git clone ssh://USER@gerrit.gromacs.org/gromacs.git
+To automatically detect issues in new code, it is tested within continuous
+integration (CI) with a large combination of settings. 
 
-(replace **USER** \ with your username)
-
-or change the remote url using:
-
-::
-
-    $ git remote set-url origin ssh://USER@gerrit.gromacs.org/gromacs.git
-
-(change **USER** with the username you've registered)
-
-Or add a new remote url using:
-
-::
-
-    $ git remote add upload ssh://USER@gerrit.gromacs.org/gromacs.git
-
-If you are working with a GROMACS repository other than the source code,
-then you should substitute e.g. regressiontests.git or releng.git
-instead of gromacs.git above.
-
-Be sure to configure your user name and e-mail to match those registered to Gerrit::
-
-       git config [--global] user.name "Your Name"
-       git config [--global] user.email "your.name@domain.org"
+Setting up login credentials with gitlab
+----------------------------------------
 
-It is optional if you want to set those settings for git on a global
-level, or just for the current repository.
+You will need a public ssh key. If you were using Gerrit, you probably 
+already have one and you can ignore the first line::
 
-If necessary, register the e-mail address you want to use
-with Gerrit.
+    ssh-keygen -t rsa -C "your.email@address.com"
+    cat ~/.ssh/id_rsa.pub
 
-Install the commit hook
------------------------
+Copy the output of the last command, got to gitlab.com, find you user in the
+right top corner and select settings.
 
-Differently from a simple usage of git, with Gerrit a Change-ID is
-needed at the end of each commit message. Gerrit uses Change-IDs to
-understand whether your new commit is patching a previous commit or it
-should be regarded as a separate, different patch, uncorrelated with
-your previously pushed commits.
+Chose SSH keys in the menu on the left and past your key in the text field.
 
-To allow git to append such Change-IDs automatically after each commit,
-type the following command:
+Creating issues
+---------------
 
-::
+The meta-level code design and discussions is organised in issues and visible at
+https://gitlab.com/gromacs/gromacs/-/issues. Please check if if your issue or a
+similar issue already exists before creating a new one.
 
-    $ scp -p USER@gerrit.gromacs.org:hooks/commit-msg .git/hooks/
+Note that all Redmine issues have been transferred to gitlab with the same issue
+numbers as used in gitlab. However, comments and discussion are now represented
+by gitlab user @acmnpv - the original authors are found inline at the bottom of
+the comments. 
 
-(change **USER** with the username you've registered in Gerrit)
+Uploading code for review - creating a merge request
+----------------------------------------------------
 
-.. Note::
+Issues are addressed with new code via "merge requests" (MR). Find the current
+MRs at https://gitlab.com/gromacs/gromacs/-/merge_requests. 
+There are two ways of creating a merge request - either via the gitlab graphical
+user interface or via the command line. 
 
-   This commit hook needs to be added to the repo where the
-   commit will occur, not the repo where the push to upstream will occur
-   (should they be different).
+To use the GUI, find the relevant issue or open a new one, then find the 
+"create merge request" button to create a merge request related to that issue in gitlab.
+The default selection is to mark this a work in progress (WIP) merge-request.
+We recommend keeping this setting until you are completely satisfied with the 
+code yourself and all tests are passed.
 
-Uploading a commit for review
------------------------------
+Select milestone and assignees to make tracking of the progress easier. 
+Keep the requirements for merging as they are set by default.
 
-Make sure your HEAD is up to date (use ``git pull --rebase origin`` if
-someone else has committed since you last pulled), check that your commit
-message follows the :doc:`commitstyle`, make your commit and then use
+You can also use ``git push`` on the command line directly and create a merge request 
+following the link that is output on the command line.
 
-::
+Your repository should be in sync with the GROMACS repository. To ensure this,
+use ``git fetch`` to obtain the newest branches, then merge the master branch
+into your branch with ``git merge master`` while on your branch.
 
-    $ git push origin HEAD:refs/for/BRANCH
-
-Replace ``BRANCH`` with the branch it should be committed to.
-Master has a number of sub branches that can be used to show
-what the patch is relevant to such as OpenCL and tools-cleanup.
-These can be pushed to by specifying them after the branch,
-for example ``BRANCH/domdec-cleanup``.
-
-When updating/replacing an existing change, make sure the commit message
-has the same Change-ID. Please see the section `Ammending a change <gmx-ammend-change>`
-below.
-
-Uploading a Work-In-Progress (WIP) or Private commit for review
----------------------------------------------------------------
-
-You can use the WIP or Private workflow on Gerrit to upload changes
-that might not be ready yet for public review and merging.
-Those changes will only be visible to people explicitly added as reviewers,
-and will not automatically trigger Jenkins if the reviewer "Jenkins Buildbot"
-is not added manually to them.
-
-For uploading a new private change, push to refs/for/master%private
-(substituting master with the branch you want to push to). To remove the private
-flag when uploading a new patch set, use refs/for/master%remove-private.
-To mark change as Work-In-Progress, push to refs/for/master%wip,
-to unmark push to refs/for/master%ready.
-You can also mark and unmark changes as Private or WIP in the Gerrit web-interface.
-
-To manually trigger Jenkins on a WIP or Private change, you need to log in
-to Jenkis after adding the "Jenkins Buildbot" reviewer. In Jenkins, navigate to
-http://jenkins.gromacs.org/gerrit_manual_trigger/ and tell it to
-search for the commit for which you want to trigger the build agents.
-For example, https://gerrit.gromacs.org/#/c/1238/ is 1238 (but maybe
-SHA or ChangeID will work, too).
-Any change made to the commit after "Jenkins Buildbot" was added to the
-list of reviewers will also trigger Jenkins.
-
-After uploading a commit
-------------------------
+Naming branches
+---------------
 
-Use
-
-::
-
-    $ git reset --keep HEAD^
-
-to reset your branch to the HEAD before the commit you just uploaded.
-This allows you to keep your repo in sync with what every other repo
-thinks is the HEAD. In particular, if you have another patch to upload
-(or worse, have to pull in other people's patches, and then have a new
-patch), you probably do not want to have the second patch depend on the
-first one. If the first one is rejected, you have made extra work for
-yourself sorting out the mess. Your repo still knows about the commit,
-and you can cherry-pick it to somewhere if you want to use it.
+Good names: documentation_UpdateDevelopersDocsTOGitLab, nbnxm_MakeNbnxmGPUIntoClass, pme_FEPPMEGPU. 
+Bad names: branch1234, mybranch, test, etc
 
 Code Review
 ===========
 
-Reviewing someone else's uploaded commit
-----------------------------------------
+Reviewing someone else's uploaded code
+--------------------------------------
 
 The reviewing workflow is the following:
 
-#. https://gerrit.gromacs.org/#q/status:open shows all open changes
-#. A change needs a +2 and usually +1 review, as well as a +2 verified
-   to be allowed to be merged.
+#. https://gitlab.com/gromacs/gromacs/-/issues shows all open changes
+#. A change needs two approvals to go in, of which one approval has to come from
+   a member of either GMX Core or GMX Developers.
 #. Usually a patch goes through several cycles of voting, commenting and
    updating before it becomes merged, with votes from the developers indicating
    if they think that change hat progressed enough to be included.
 #. A change is submitted for merging and post-submit testing
-   by clicking "Submit" by one of the main developers. This should be done by
-   the reviewer after voting +2. After a patch is submitted it is
-   replicated to the main git server.
+   by clicking "Merge".
 
 Do not review your own code. The point of the policy is that at least
-two non-authors have voted +1, and that the issues are resolved in the
-opinion of the person who applies a +2 before a merge. If you have
+two non-authors have approved, and that the issues are resolved in the
+opinion of the person who applies an approval before a merge. If you have
 uploaded a minor fix to someone else's patch, use your judgement in
-whether to vote on the patch +1.
+whether to approve yourself.
 
 Guide for reviewing
 -------------------
 
 -  First and foremost, check correctness to the extent possible;
--  As portability and performance are the most important things (after
-   correctness) do check for potential issues;
--  Check adherence to the :ref:`GROMACS coding
-   standards <style-guidelines>`;
+-  As portability and performance are the next most important things do check 
+   for potential issues;
+-  Check adherence to the :ref:`GROMACS coding standards <style-guidelines>`;
 -  We should try to ensure that commits that implement bugfixes (as
-   well as important features and tasks) get a `Redmine`_ entry created
-   and linked. The linking is done **automatically** by
-   `Redmine`_ **if the commit message contains** keyword
-   "#issueID", the valid syntax is explained below.
+   well as important features and tasks) get an `issue tracker`_ entry created
+   and linked. The linking is done **automatically** through
+   `special syntax <https://gitlab.com/help/user/markdown#special-gitlab-references>`__
 -  If the commit is a **bugfix**\ :
 
-   -  if present in Redmine it has to contain a valid reference to the
+   -  if present in the `issue tracker`_, it has to contain a valid reference to the
       issue;
-   -  if it's a **major bug**, there has to be a bug report filed in
-      `Redmine`_  (with urgent or
+   -  if it's a **major bug**, there has to be a bug report filed in the
+      `issue tracker`_  (with urgent or
       immediate priority) and referenced appropriately.
 
 -  If the commit is a **feature/task** implementation:
 
-   -  if it's present in `Redmine`_ it
+   -  if it's present in the `issue tracker`_ it
       has to contain a valid reference to the issue;
    -  If no current issue is currently present and the change
       would benefit of one for future explanation on why it was
-      added, a new redmine issue should be created.
-
-Use of Verify
--------------
-
-Jenkins has been installed for automated build testing. So it isn't
-required to vote "verify +2" anymore. As the testing is not always
-perfect, and because test coverage can be spotty, developers can still
-manually vote to indicate that a change performs as intended. Please note
-that this should not be abused to bypass Jenkins testing. The vote from
-the test suite should only be discarded if failures are caused by unrelated
-issues.
-
-Further information
--------------------
-
-Currently it is possible to review your own code. It is undesirable to
-review your own code, because that defeats the point. It will be
-deactivated if it is being abused and those responsible may lose
-their voting rights.
-
-For further documentation:
-
--  |Gromacs| `specific manual <https://gerrit.gromacs.org/Documentation/index.html>`__
--  `General tutorials <https://gerrit-documentation.storage.googleapis.com/Documentation/2.15.3/index.html#_tutorials>`__
-
-FAQs
-====
-
-How do I access gerrit behind a proxy?
---------------------------------------
-
-If you are behind a firewall blocking port 22, you can use socat to
-overcome this problem by adding the following block to your
-``~/.ssh/config``
-
-::
-
-    Host gerrit.gromacs.org
-           User USER
-           Hostname gerrit.gromacs.org
-           ProxyCommand socat - PROXY:YOURPROXY:gerrit.gromacs.org,proxyport=PORT
-
-Replace ``YOURPROXY``, ``PORT`` and ``USER``, (but not ``PROXY``!) with your own
-settings.
-
-How do I link fixes with Redmine issues?
-----------------------------------------
-
-The linking of commits that relate to an existing issue is
-done automatically by `Redmine`_ if
-the git commit message contains a reference to the Redmine entry
-through the issueID, the numeric ID of the respective issue (bug,
-feature, task). The general syntax of a git comit reference is [keyword]
-#issueID.
-
-The following two types of refereces are possible:
-
--  For bugfix commits the issueID should be preceeded by
-   the "Fixes" keyword;
--  For commits related to a general issue (e.g. partial implementation of
-   feature or partial fix), the issueID should be preceeded by the "Refs" keyword;
-
-An example commit message header::
-
-    This commit refs #1, #2 and fixes #3
-
-How can I submit conflicting changes?
--------------------------------------
-
-When there are several, mutually conflicting changes in gerrit pending
-for review, the submission of the 2nd and subsequent ones will fail.
-Those need to be resolved locally and updated by
-
-::
+      added, a new issue should be created.
 
-    $ git pull --rebase
+Moving code from gerrit to gitlab
+=================================
 
-Then fix the conflicts and use
+Create a local repository that is connected to both Gerrit and Gitlab::
 
-::
-
-    $ git push
-
-Please add a comment (review without voting) saying that it was rebased
-with/without conflicts, to help the reviewer.
+    git clone git@gitlab.com:gromacs/gromacs.git -o gitlab gromacs-migrate
+    cd gromacs-migrate/
+    git remote add gerrit ssh://<gerrit-username>@gerrit.gromacs.org/gromacs.git
+    git fetch --all
+Checkout the current gitlab master::
 
+    git checkout gitlab/master
 
-.. _gmx-ammend-change:
+Go to your commit on https://gerrit.gromacs.org/ , select Download->Cherry-Pick
 
-How do I upload an update to a pending change?
-----------------------------------------------
+``git fetch "https://gerrit.gromacs.org/gromacs" refs/changes/XX/YYYY/ZZ && git cherry-pick FETCH_HEAD``
 
-First, obtain the code you want to update. If you haven't changed your
-local repository, then you already have it. Maybe you can check out the
-branch again, or consult your git reflog. Otherwise, you should go to
-gerrit, select the latest patch set (remembering that others may have
-contributed to your work), and use the "Download" link to give you a
-"Checkout" command that you can run, e.g.
-
-::
-
-    $ git fetch ssh://USER@gerrit.gromacs.org/gromacs refs/changes/?/?/? && git checkout FETCH_HEAD
-
-Make your changes, then add them to the index, and use
-
-::
+Resolve conflicts, if any. If you need to do further changes to your patch, 
+feel free to ammend them at this point. Remove the Gerrit commit-id line from
+the bottom of the commit message, but keep the issue (ex. redmine) references - 
+they match the gitlab issues. 
 
-    $ git commit --amend
-    $ git push origin HEAD:refs/for/BRANCH
+Do not forget to run clang-format script (``admin/clang-format.sh update -f --rev=HEAD^``)
+and copyright script (``admin/copyright.sh update -f --rev=HEAD^``). 
 
-When amending the previous commit message, leave the "Change-Id" intact
-so that gerrit can recognize this is an update and not open a new issue.
-
-DO NOT rebase your patch set and update it in one step. If both are done
-in one step, the diff between patch set versions has both kinds of
-changes. This makes it difficult for the reviewer, because it is not
-clear what parts have to be re-reviewed. If you need to update and
-rebase your change please do it in two steps (order doesn't matter).
-gerrit has a feature that allows you to rebase within gerrit, which
-creates the desired independent patch for that rebase (if the rebase is
-clean).
-
-How do I get a copy of my commit for which someone else has uploaded a patch?
------------------------------------------------------------------------------
-
-Gerrit makes this easy. You can download the updated commit in various
-ways, and even copy a magic git command to your clipboard to use in your
-shell.
-
-You can select the kind of git operation you want to do (cherry-pick is
-best if you are currently in the commit that was the parent, checkout is
-best if you just want to get the commit and not worry about the current
-state of your checked out git branch) and how you want to get it. The
-icon on the far right will paste the magic shell command into your
-clipboard, for you to paste into a terminal to use.
-
-How do I submit lots of independent commits (e.g. bug fixes)?
--------------------------------------------------------------
-
-Simply pushing a whole commit tree of unrelated fixes creates
-dependencies between them that make for trouble when one of them needs
-to be changed. Instead, from an up-to-date repo, create and commit the
-first change (or git cherry-pick it from an existing other branch).
-Upload it to gerrit. Then do
-
-::
+When ready, move the patch to a new branch::
 
-    $ git reset --keep HEAD^
-
-This will revert to the old HEAD, and allow you to work on a new commit
-that will be independent of the one you've already uploaded. The one
-you've uploaded won't appear in the commit history until it's been
-reviewed and accepted on gerrit and you've pulled from the main repo,
-however the version of it you uploaded still exists in your repo. You
-can see it with git show or git checkout using its hash - which you can
-get from the gerrit server or by digging in the internals of your repo.
-
-How can I avoid needing to remember all these arcane git commands?
-------------------------------------------------------------------
-
-In your ``.gitconfig``, having set the git remote for the gerrit repo to
-upload, use something like the following to make life easier:
-
-::
-
-    [alias]
-            upload-r2018  = push origin HEAD:refs/for/release-2018
-            upload-r2016  = push origin HEAD:refs/for/release-2016
-            upload-master = push origin HEAD:refs/for/master
-            upload-reset  = reset --keep HEAD^
-
-
-How can I get my patch in gerrit to have a different parent?
-------------------------------------------------------------
-
-Sometimes, some other patch under review is a relevant point from which
-to start work. For simple changes without conflicts to the previous
-work, you can use the Gerrit web UI to either rebase or cherry-pick
-the change you are working on.
-
-If this is not possible, you can still use
-the canned gerrit checkouts to (say) checkout out patch 2117 and start work:
-
-::
-
-    git fetch https://gerrit.gromacs.org/gromacs refs/changes/17/2117/2 && git checkout FETCH_HEAD
-
-Other times you might have already uploaded a patch (e.g. patch 1 of
-2145), but now see that some concurrent work makes more sense as a
-parent commit (e.g. patch 2 of 2117), so check it out as above, and then
-use the canned gerrit **cherry-pick**:
-
-::
+    git branch <branch-name>
 
-    git fetch https://gerrit.gromacs.org/gromacs refs/changes/45/2145/1 && git cherry-pick FETCH_HEAD
+Make sure to select a unique branch name that it is easy for you to connect to
+a specific patch. You will need it later to make changes to your merge request. 
+Keep in mind that your branch name is going to be exposed to everyone while 
+your patch is under review. Push the branch to GitLab::
 
-Resolve any merge commits, check things look OK, and then upload.
-Because the ChangeId of 2145 hasn't changed, and nothing about 2117 has
-changed, the second patch set of 2145 will reflect the state of 2145 now
-having 2117 as a parent.
+    git push gitlab <branch-name>
 
-This can also be useful for constructing a short development branch
-where the commits are somehow dependent, but should be separated for
-review purposes. This technique is useful when constructing a series of
-commits that will contribute to a release.
+Go to https://gitlab.com/gromacs/gromacs and create a merge request.
+Copy-paste your commit message from Gerrit into the merge request description 
+text box, use the first line as a title. If your branch has only one commit,
+this will be done automatically. Add "From: https://gerrit.gromacs.org/#/c/gromacs/+/XXXXX/"
+to the end of your commit message.
+Select "Delete source branch when merge request is accepted." check-box.
+Select "Squash commits when merge request is accepted" check-box.
+Check and that squash commit message is correct. If necessary, update it.
 
-How can I revert a change back to an old patchset?
---------------------------------------------------
+If your change in Gerrit depends on another Gerrit change:
 
-If a change accidentally gets updated or when a patchset is incorrect,
-you might want to revert to an older patchset. This can be done by
-fetching an old patchset, running git commit --amend to update the time
-stamp in the commit and pushing the commit back up to gerrit. Note that
-without the amending you will get an error from the remote telling you
-that there are no new changes.
+Make sure that you transfer the parent change to GitLab first.
+When transferring the child change, specify the parent in the "Merge request dependencies" text field.
+In GitLab menu, go to Repository -> Compare. Select the branch that correspond 
+to the child change as a Source in the drop-down menu, choose parent change as
+the Target. Click Compare button and copy the link from the browser address bar.
+Add "Compare to the parent: https://gitlab.com/gromacs/gromacs/-/compare/PARENT_BRANCH...CHILD_BRANCH"
+to the description of the merge request. You will have to keep this dependency
+up to date for the link to work properly. For example, if you update the parent,
+you will need to merge its branch to the child branch right away.
+Otherwise your recent updates will show up in comparison.
 
-How do I handle common errors
------------------------------
-
-.. rubric:: error: server certificate verification failed. CAfile...
-
-If you try to cherry-pick a change from the server, you'll probably get
-the error:
-
-::
-
-    $ git fetch https://gerrit.gromacs.org/p/gromacs refs/changes/09/109/1 && git cherry-pick FETCH_HEAD
-    error: server certificate verification failed.
-    CAfile: /etc/ssl/certs/ca-certificates.crt
-    CRLfile: none while accessing https://gerrit.gromacs.org/p/gromacs/info/refs
-
-    fatal: HTTP request failed
-
-As explained
-`here <http://code.google.com/p/chromium-os/issues/detail?id=13402>`__,
-the problem is with git not trusting the certificate and as a workaround
-one can set globally
-
-::
-
-    $ git config --global --add http.sslVerify false
-
-or prepend GIT_SSL_NO_VERIFY=1 to the command
-
-::
-
-    $ GIT_SSL_NO_VERIFY=1  git fetch https://gerrit.gromacs.org/p/gromacs refs/changes/09/109/1 \
-     && git cherry-pick FETCH_HEAD
-
-.. rubric:: Various error messages and their meanings
-
-http://review.coreboot.org/Documentation/error-messages.html
 
 More git tips
 =============
@@ -619,7 +316,7 @@ If the testing fails, you can
 amend your existing commit with
 ``git commit --amend``. After you are
 satisfied, you can push the
-commit into gerrit for review. If
+commit for review. If
 you stashed away your changes and
 you want the next change to be
 reviewed independently, do
@@ -630,7 +327,7 @@ reviewed independently, do
     git stash pop
 
 (only do this if you pushed the
-previous change to gerrit,
+previous change upstream,
 otherwise it is difficult to get
 the old changes back!) and repeat
 until each independent change is
@@ -799,59 +496,3 @@ both at the same time using
 ::
 
     git rebase -i master
-
-.. rubric:: Interacting with Gerrit
-   :name: interacting-with-gerrit
-   :class: editable
-
-This section is intended for
-using git to interact with
-gerrit; interacting with the web
-UI may be better dealt with on a
-separate page.
-
-.. rubric:: Q: How do I move a change from a branch to another?
-
-A: Moving one or a few changes is
-most easily done using ``git
-cherry-pick``. To move a single
-change, first do
-
-::
-
-    git checkout <target-branch>
-
-Then, open the change/patch set
-in Gerrit that you want to move,
-select "cherry-pick" in the
-Download section for that patch
-set, and copy/paste the given
-command:
-
-::
-
-    git fetch ... refs/changes/... && git cherry-pick FETCH_HEAD
-
-Resolve any conflicts and do
-
-::
-
-    git commit [-a]
-
-You can also cherry-pick multiple
-changes this way to move a small
-topic branch. Before pushing the
-change to Gerrit, remove the
-lines about conflicts from the
-commit message, as they don't
-serve any useful purpose in the
-history. You can type that
-information into the change as a
-Gerrit comment if it helps the
-review process. Note that Gerrit
-creates a new change for the
-target branch, even if Change-Ids
-are same in the commits. You need
-to manually abandon the change in
-the wrong branch.
-
index 4b26db088c8e2aed423138d9c0ab37fd9ac75c61..963389b3ce5c2eae7d1ee170dc772a5b05b26471 100644 (file)
@@ -6,52 +6,17 @@ Automatic source code formatting
 .. highlight:: bash
 
 The source code can be automatically formatted using clang-format
-(GROMACS 2020 and later)
-or uncrustify (GROMACS 2019 and earlier).
+since GROMACS 2020.
 Both are formatting tools that apply the guidelines in :doc:`formatting`.
 Additionally, other Python scripts are used for a few other automatic
 formatting/checking tasks.  The overview tools page contains a list of these
 tools: :ref:`dev-formatting-tools`.
-This page provides more details for clang-format, uncrustify and copyright scripts.
+This page provides more details for clang-format, clang-tidy and copyright scripts.
 
-Jenkins uses these same scripts (in particular, ``clang-format.sh``,
-``copyright.sh`` and the ``check-source`` target) to enforce that
+Our CI uses these same scripts (in particular, ``clang-format.sh``,
+``copyright.sh``, ``clang-tidy.sh`` and the ``check-source`` target) to enforce that
 the code stays invariant under such formatting.
 
-.. _gmx-uncrustify:
-
-Setting up uncrustify
----------------------
-
-A patched version of uncrustify is used for |Gromacs|.  To set this up, you need
-to do these (once):
-
-1. Change to a directory under which you want to build uncrustify and run::
-
-     git clone -b gromacs git://github.com/rolandschulz/uncrustify.git
-     cd uncrustify
-     ./configure
-     make
-
-2. Copy the binary ``src/uncrustify`` into a directory of your choice
-   (``/path/to/uncrustify`` below).
-
-Alternatively, if you are running Linux, you can try whether the binary from
-http://redmine.gromacs.org/issues/845 works for you.
-
-In order to use the binary for ``uncrustify.sh`` and for the pre-commit hook, you
-also need to run this in each of your |Gromacs| repositories::
-
-  git config hooks.uncrustifypath /path/to/uncrustify
-
-Alternatively, if you just want to use ``uncrustify.sh``, you can set the
-``UNCRUSTIFY`` environment variable to ``/path/to/uncrustify``.
-
-Using the pre-commit hook or git filters needs additional setup; see the
-respective sections below.
-
-Note that Jenkins now only allows formatting using ``clang-format``.
-
 .. _gmx-clang-format:
 
 Setting up clang-format
@@ -97,7 +62,8 @@ and for the pre-commit hook, you also need to run this in each of your |Gromacs|
 Alternatively, if you just want to use ``clang-format.sh``, you can set the
 ``CLANG_FORMAT`` environment variable to ``/path/to/clang-format``.
 
-As above, see the sections below for using the pre-commit hook or git filters.
+Using the pre-commit hook or git filters needs additional setup; see the
+respective sections below.
 
 clang-format discovers which formatting rules to apply from the
 :file:`.clang-format` configuration file(s) in project directories,
@@ -114,14 +80,63 @@ git filters, specified in ``.gitattributes`` files.  Only files that have the
 attribute ``filter`` set to one of the below values are processed:
 
 - ``filter=complete_formatting``: Performs all formatting. Uses clang-format for code formatting.
-- ``filter=uncrustify``: uncrustify is run. Deprecated and here for historical reasons.
-- ``filter=clangformat``: clang-format is run.
+                                  Files included here are also passed to the clang-tidy code checker.
+- ``filter=clangformat``: clang-format is run. Again also runs clang-tidy.
 - ``filter=includesort``: include order is enforced and copyright headers are checked.
 - ``filter=copyright``: only copyright headers are checked.
 
-Other files are ignored by ``uncrustify.sh``, ``clang-format.sh``,
+Other files are ignored by ``clang-tidy.sh``, ``clang-format.sh``,
 ``copyright.sh`` and ``reformat_all.sh`` scripts (see below).
 
+.. _gmx-clang-tidy:
+
+Setting up clang-tidy
+---------------------
+
+|Gromacs| source code tidiness checking is enforced with clang-tidy provided
+alongside *clang* compiler version 9.
+:command:`clang-tidy` is one of the core *clang* tools.
+It may be included in a *clang* or *llvm* package from your favorite packaging
+system or you may find a standalone *clang-tidy* or *clang-tools* package,
+but you should confirm that the provided command is version 9.
+Example::
+
+    $ clang-tidy --version
+      LLVM (http://llvm.org/):
+        LLVM version 9.0.0
+
+If you use a different version of clang-tidy,
+you will likely get different checking results than
+the |Gromacs| continuous integration testing system,
+and the commits that you push will fail the automated tests.
+
+.. note::
+
+    Refer to `LLVM <http://releases.llvm.org/download.html#9.0.1>`__ for
+    source and binary downloads.
+    If downloading sources, note that you will need to download both the
+    *LLVM source code* and the *Clang source code*.
+    As per the clang
+    `INSTALL.txt <https://github.com/llvm/llvm-project/blob/release/9.x/clang/INSTALL.txt>`__,
+    place the expanded clang source into a :file:`tools/clang` subdirectory within
+    the expanded llvm archive, then run CMake against the llvm source directory.
+
+In order to use the installed version of clang-tidy for ``clang-tidy.sh``
+and for the pre-commit hook, you also need to run this in each of your |Gromacs| repositories::
+
+  git config hooks.runclangtidypath /path/to/run-clang-tidy.py
+
+Alternatively, if you just want to use ``clang-tidy.sh``, you can set the
+``RUN_CLANG_TIDY`` environment variable to ``/path/to/run-clang-tidy.py``.
+
+As above, see the sections below for using the pre-commit hook or git filters.
+
+clang-tidy discovers which formatting rules to apply from the
+:file:`.clang-tidy` configuration file(s) in project directories,
+which will be automatically updated (if necessary) when you :command:`git pull`
+from the |Gromacs| repository.
+For more about the tool and the :file:`.clang-tidy` configuration file,
+visit http://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/index.html
 
 Scripts
 -------
@@ -142,45 +157,11 @@ directly, but instead the bash scripts below use it internally.  You can run
 the script with ``--help`` option if you want to see what all options it provides
 if you need to do some maintenance on the copyright headers themselves.
 
-``uncrustify.sh``
-^^^^^^^^^^^^^^^^^
-
-The information for ``uncrustify`` is mainly provided for historical reasons,
-as the actual code formatting is now done using ``clang-format``.
-
-This script runs ``uncrustify`` on modified files and reports/applies the results.
-By default, the current HEAD commit is compared to the work tree,
-and files that
-
-1. are different between these two trees and
-2. change under uncrustify
-
-are reported.  This behavior can be changed by
-
-1. Specifying an ``--rev=REV`` argument, which uses ``REV`` instead of HEAD as
-   the base of the comparison.  A typical use case is to specify ``--rev=HEAD^``
-   to check the HEAD commit.
-2. Specifying an action:
-
-   - ``check-*``:   reports the files that uncrustify changes
-   - ``diff-*``:    prints the actual diff of what would change
-   - ``update-*``:  applies the changes to the repository
-   - ``*-workdir``: operates on the working directory (files on disk)
-   - ``*-index``:   operates on the index of the repository
-
-   For convenience, if you omit the workdir/index suffix, workdir is assumed
-   (i.e., ``diff`` equals ``diff-workdir``).
-3. Specifying ``--uncrustify=off``, which does not run uncrustify.
-
-By default, ``update-*`` refuses to update dirty files (i.e., that differ
-between the disk and the index) to make it easy to revert the changes.
-This can be overridden by adding a ``-f``/``--force`` option.
-
 ``copyright.sh``
 ^^^^^^^^^^^^^^^^
 
 This script runs ``copyright.py`` on modified files and reports/applies the results.
-By default, the current HEAD commit is compared to the work tree,
+By default, the current HEAD commit on the source branch is compared to the work tree,
 and files that
 
 1. are different between these two trees and
@@ -217,7 +198,7 @@ This can be overridden by adding a ``-f``/``--force`` option.
 ^^^^^^^^^^^^^^^^^^^
 
 This script runs ``clang-format`` on modified files and reports/applies the results.
-By default, the current HEAD commit is compared to the work tree,
+By default, the current HEAD commit on the source branch is compared to the work tree,
 and files that
 
 1. are different between these two trees and
@@ -244,36 +225,70 @@ By default, ``update-*`` refuses to update dirty files (i.e., that differ
 between the disk and the index) to make it easy to revert the changes.
 This can be overridden by adding a ``-f``/``--force`` option.
 
+``clang-tidy.sh``
+^^^^^^^^^^^^^^^^^
+
+This script runs the ``clang-tidy`` source code checker on modified files
+and either reports or applies resulting changes. By default, the current
+HEAD commit on the source branch is compared to the work tree,
+and files that
+
+1. are different between these two trees and
+2. change when applying clang-tidy
+
+are reported. This behavior can be changed by
+
+1. Specifying an ``--rev=REV`` argument, which uses ``REV`` instead of HEAD as
+   the base of the comparison.  A typical use case is to specify ``--rev=HEAD^``
+   to check the HEAD commit.
+2. Specifying an action:
+
+   - ``check-*``:   reports the files that clang-format changes
+   - ``diff-*``:    prints the actual diff of what would change
+   - ``update-*``:  applies the changes to the repository
+   - ``*-workdir``: operates on the working directory (files on disk)
+   - ``*-index``:   operates on the index of the repository
+
+   For convenience, if you omit the workdir/index suffix, workdir is assumed
+   (i.e., ``diff`` equals ``diff-workdir``).
+3. Specifying ``--tidy=off``, which does not run clang-tidy.
+
+By default, ``update-*`` refuses to update dirty files (i.e., that differ
+between the disk and the index) to make it easy to revert the changes.
+This can be overridden by adding a ``-f``/``--force`` option.
+
+
 git pre-commit hook
 ^^^^^^^^^^^^^^^^^^^
 
-If you want to run ``uncrustify.sh``, ``copyright.sh`` and/or
+If you want to run ``copyright.sh``, ``clang-tidy.sh`` and/or
 ``clang-format.sh`` automatically for changes you make, you can
 configure a pre-commit hook using ``admin/git-pre-commit``:
 
 1. Copy the ``git-pre-commit`` script to .git/hooks/pre-commit.
 
-2. Specify the paths to ``uncrustify`` and ``clang-format`` for the hook if you have not already done
+2. Specify the paths to ``run-clang-tidy`` and ``clang-format`` for the hook if you have not already done
    so::
 
-     git config hooks.uncrustifypath /path/to/uncrustify
+     git config hooks.runclangtidypath /path/to/run-clang-tidy.py
      git config hooks.clangformatpath /path/to/clang-format
 
 3. Set the operation modes for the hook::
 
-     git config hooks.uncrustifymode check
+     git config hooks.clangtidymode check
      git config hooks.clangformatmode check
      git config hooks.copyrightmode  update
 
 With this configuration, all source files modified in the commit are run
-through the respective code formatting tool and checked for correct copyright headers.
-If any file would be changed by ``uncrustify.sh``, ``clang-format.sh`` or ``copyright.sh``,
+through the code formatting tool, are checked with clang-tidy
+and also checked for correct copyright headers.
+If any file would be changed by ``clang-tidy.sh``, ``clang-format.sh`` or ``copyright.sh``,
 the names of those files are reported and the commit is prevented.
 The issues can be fixed by running the scripts manually.
 
 To disable the hook without removing the ``pre-commit`` file, you can set ::
 
-  git config hooks.uncrustifymode off
+  git config hooks.clangtidymode off
   git config hooks.copyrightmode off
   git config hooks.clangformatmode off
 
@@ -289,15 +304,15 @@ Note that when you run ``git commit --amend``, the hook is only run for the
 changes that are getting amended, not for the whole commit.  During a rebase,
 the hook is not run.
 
-The actual work is done by the ``admin/uncrustify.sh``, ``admin/clang-format.sh``
+The actual work is done by the ``admin/clang-tidy.sh``, ``admin/clang-format.sh``
 and ``admin/copyright.sh`` scripts, which get run with the ``check-index`` action,
-and with ``--uncrustify``, ``--copyright`` and ``--format`` getting set according
+and with ``--copyright`` and ``--format`` getting set according
 to the ``git config`` settings.
 
 ``reformat_all.sh``
 ^^^^^^^^^^^^^^^^^^^
 
-This script runs uncrustify, clang-format, ``copyright.py``, or the include sorter for all
+This script runs clang-format, ``copyright.py``, or the include sorter for all
 applicable files in the source tree.  See ``reformat_all.sh -h`` for the
 invocation.
 
@@ -323,8 +338,6 @@ An alternative to using a pre-commit hook to automatically apply uncrustify or
 clang-format on changes is to use a git filter (does not require either of the scripts,
 only the ``.gitattributes`` file).  You can run ::
 
-  git config filter.complete_formatting.clean \
-      "/path/to/uncrustify -c admin/uncrustify.cfg -q -l cpp"
   git config filter.clangformat.clean \
       "/path/to/clang-format -i"
 
index 6945b32d06567a85250fa94d557353a5b948d4c8..59f2e74863c8e8151271ea1f31fad630f8f5bd3c 100644 (file)
@@ -23,8 +23,8 @@ Guidelines for git commit messages
 Commit messages should contain a quick explanation in verb form on what has been
 changed or what has been the purpose of the change. If available, the final
 part of the message before the ChangeId should be a short section like
-**Fixes #redmine-id** to link the change to a possibly previously 
-posted issue, or **Refs #redmine-id** if the present patch is somehow
+**Fixes #issue-id** to link the change to a possibly previously
+posted issue, or **Refs #issue-id** if the present patch is somehow
 related to that work without necessarily fixing the whole issue.
 
 Concerning inline code comments
index 0bc378d6542ba65bdfbfa8497ef13b90a03e9968..22db6df9f58f0e51a7aa78316cf2282281891be5 100644 (file)
@@ -40,7 +40,7 @@ please make sure that you have checked all the points on this list:
   widespread adoption of the method.
 
 * *Advance discussion*: Please communicate with the other developers,
-  e.g.  on the `developer mailing list`_ mailing list, or `redmine`_ to let them know of the general
+  e.g.  on the `developer mailing list`_ mailing list, or `issue tracker`_ to let them know of the general
   nature of your plans. This will prevent duplicate or wasted
   effort. It is also a good idea to search those resources as well as
   the literature and WWW for other projects that may be relevant.
@@ -124,13 +124,13 @@ please make sure that you have checked all the points on this list:
 Preparing code for submission
 -----------------------------
 
-|Gromacs| revision control uses a ``git`` repository managed by :ref:`Gerrit <gmx-gerrit>`.
+|Gromacs| uses ``git`` for :doc:`change-management`.
 Instead of accepting "pull requests", |Gromacs| changes are submitted as individual
-commits on the tip of the ``master`` branch hosted at https://gerrit.gromacs.org.
+commits on the tip of the ``master`` branch hosted at `gitlab`_.
 Preparing, submitting, and managing patches for a change requires a little bit
 of set-up. Refer to :doc:`change-management` for information about
 
-* accessing the |Gromacs| Gerrit server
+* accessing the |Gromacs| *git* repository
 * structure of the repository
 * source control without merge commits
 * ``git`` usage that may be less common in other development work flows
index 77a1e1bce54e8a572c4cafebdfca558a4430049d..e3b4663d8201a9a53a93b96a0430b1124f53cc66 100644 (file)
@@ -113,7 +113,8 @@ LaTeX
   Also requires ImageMagick for converting graphics file formats.
 
 linkchecker
-  The linkchecker program is used together with the linkcheckerrc file to ensure
+  `linkchecker <http://wummel.github.io/linkchecker/>`__ is used together with the
+  :file:`docs/linkcheckerrc` file to ensure
   that all the links in the documentation can be resolved correctly.
 
 documentation exported from source files
diff --git a/docs/dev-manual/gitlab.rst b/docs/dev-manual/gitlab.rst
new file mode 100644 (file)
index 0000000..a7d5254
--- /dev/null
@@ -0,0 +1,265 @@
+GitLab
+======
+
+The repository contains DockerFiles and GitLab Runner configuration
+files to support automated testing and documentation builds.
+General information on configuring GitLab CI pipelines can be found
+in the official `Gitlab documentation <https://docs.gitlab.com/ee/ci/yaml/>`_.
+
+The GitLab CI configuration entry point is the :file:`.gitlab-ci.yml` file
+at the root of the source tree.
+Configuration templates are found in the files in the
+:file:`admin/ci-templates/` directory.
+
+Docker images used by GitLab Runner are available on `Docker Hub <https://hub.docker.com/u/gromacs>`__.
+Images are (re)built manually using details in :file:`admin/containers`.
+
+This documentation is incomplete, pending resolution of :issue:`3275`.
+
+..  todo:: Expand this documentation to resolve :issue:`3275`
+
+Pipeline execution
+------------------
+
+.. todo:: Discuss the distinct characteristics of |Gromacs| CI pipelines to relevant to job configuration.
+
+.. todo:: Comment on the number of pipelines that can be or which are likely to be running at the same time.
+
+.. note::
+
+    Full automated testing is only available for merge requests originating from
+    branches of the main https://gitlab.com/gromacs/gromacs repository.
+    GitLab CI pipelines created for forked repositories will include fewer jobs
+    in the testing pipeline. Non-trivial merge requests may need to be issued
+    from a branch in the ``gromacs`` project namespace in order to receive
+    sufficient testing before acceptance.
+
+Configuration files
+~~~~~~~~~~~~~~~~~~~
+
+At the root of the repository, :file:`.gitlab-ci.yml` defines the stages and
+some default parameters, then includes files from :file:`admin/gitlab-ci/` to
+define jobs to be executed in the pipelines.
+
+Note that job names beginning with a period (``.``) are
+`"hidden" <https://docs.gitlab.com/ee/ci/yaml/#hidden-keys-jobs>`_.
+Such jobs are not directly eligible to run, but may be used as templates
+via the `*extends* job property <https://docs.gitlab.com/ee/ci/yaml/#extends>`_.
+
+Job parameters
+~~~~~~~~~~~~~~
+
+Refer to https://docs.gitlab.com/ee/ci/yaml for complete documentation on
+GitLab CI job parameters, but note the following GROMACS-specific conventions.
+
+.. glossary::
+
+    before_script
+        Used by several of our templates to prepend shell commands to
+        a job *script* parameter.
+        Avoid using *before-script* directly, and be cautious
+        about nested *extends* overriding multiple *before_script* definitions.
+
+    cache
+        There is no global default, but jobs that build software will likely
+        set *cache*. To explicitly unset *cache* directives, specify a job
+        parameter of ``cache: {}``.
+        Refer to `GitLab docs <https://docs.gitlab.com/ee/ci/yaml/#cache>`__
+        for details. In particular, note the details of cache identity according
+        to `cache:key <https://docs.gitlab.com/ee/ci/yaml/#cachekey>`__
+
+    image
+        Part of the tool chain configuration. Instead of setting *image*
+        directly, *extend* a *.use_<toolchain>* template from
+        :file:`admin/gitlab-ci/global.gitlab-ci.yml`
+
+    rules
+    only
+    except
+    when
+        *Job* parameters for controlling the circumstances under which jobs run.
+        (Some key words may have different meanings when occurring as elements
+        of other parameters, such as *archive:when*, to which this note is not
+        intended to apply.)
+        Instead of setting any of these directly in a job definition, try to use
+        one of the pre-defined behaviors (defined as ``.rules:<something>`` in
+        :file:`admin/gitlab-ci/rules.gitlab-ci.yml`).
+        Errors or unexpected behavior will occur if you specify more than one
+        *.rules:...* template, or if you use these parameters in combination
+        with a *.rules...* template.
+        To reduce errors and unexpected behavior, restrict usage of these controls
+        to regular job definitions (don't use in "hidden" or parent jobs).
+        Note that *rules* is not compatible with the older *only* and *except*
+        parameters. We have standardized on the (newer) *rules* mechanism.
+
+    tags
+        Jobs that can only run in the |Gromacs| GitLab CI Runner infrastructure
+        should require the ``k8s-scilifelab`` tag.
+        These include jobs that specify Kubernetes configuration variables or
+        require special facilities, such as GPUs or MPI.
+        Note that the *tag* controls which Runners are eligible to take a job.
+        It does not affect whether the job is eligible for addition to a particular pipeline.
+        Additional *rules* logic should be used to make sure that jobs with the
+        ``k8s-scilifelab`` do not become eligible for pipelines launched outside
+        of the |Gromacs| project environment.
+        See, for instance, :term:`CI_PROJECT_NAMESPACE`
+
+    variables
+        Many job definitions will add or override keys in *variables*.
+        Refer to `GitLab <https://docs.gitlab.com/ee/ci/yaml/#variables>`__
+        for details of the merging behavior. Refer to :ref:`variables` for local usage.
+
+Schedules and triggers
+~~~~~~~~~~~~~~~~~~~~~~
+
+Pipeline `schedules <https://gitlab.com/help/ci/pipelines/schedules>`__ are
+configured through the GitLab web interface.
+Scheduled pipelines may provide different variable definitions through the
+environment to jobs that run under the ``schedules``
+`condition <https://gitlab.com/help/ci/pipelines/schedules#using-only-and-except>`__.
+
+Nightly scheduled pipelines run against ``master`` and *release* branches in
+the GROMACS repository.
+
+Running post-merge-acceptance pipelines
+"""""""""""""""""""""""""""""""""""""""
+
+The Gitlab CI for |Gromacs| runs a set of jobs by default only after a MR has been
+accepted and the resulting commit is included in the target branch if it is ``master``
+or one of the *release* branches. Those jobs can be triggered manually using the
+``POST_MERGE_ACCEPTANCE`` input variable documented below when executing a new pipeline
+through the Gitlab web interface.
+
+Global templates
+~~~~~~~~~~~~~~~~
+
+In addition to the templates in the main job definition files,
+common "mix-in" functionality and behavioral templates are defined in
+:file:`admin/gitlab-ci/global.gitlab-ci.yml`.
+For readability, some parameters may be separated into their own files, named
+according to the parameter (e.g. :file:`rules.gitlab-ci.yml`).
+
+Jobs beginning with ``.use-`` provide mix-in behavior, such as boilerplate for
+jobs using a particular tool chain.
+
+Jobs beginning with a `parameter <https://docs.gitlab.com/ee/ci/yaml>`__
+name allow parameters to be set in a single place for common job characteristics.
+If providing more than a default parameter value, the job name should be suffixed
+by a meaningful descriptor and documented within
+:file:`admin/gitlab-ci/global.gitlab-ci.yml`
+
+Job names
+~~~~~~~~~
+
+Job names should
+
+1. Indicate the purpose of the job.
+2. Indicate relationships between multi-stage tasks.
+3. Distinguish jobs in the same stage.
+4. Distinguish job definitions throughout the configuration.
+
+Jobs may be reassigned to different stages over time, so including the stage
+name in the job name is not helpful, generally. If tags like "pre" and "post,"
+or "build" and "test" are necessary to distinguish phases of, say, "webpage,"
+then such tags can be buried at the end of the job name.
+
+Stylistically, it is helpful to use delimiters like ``:`` to distinguish the
+basic job name from qualifiers or details. Also consider
+`grouping jobs <https://docs.gitlab.com/ee/ci/pipelines/index.html#grouping-jobs>`__
+
+.. _variables:
+
+Updating regression tests
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Changes in |Gromacs| that require changes in regression-tests are notoriously hard,
+because a merge request that tests against the non-updated version of the
+regression tests will necessarily fail, while updating regression tests while
+the current change is not integrated into master, might cause other
+merge request pipelines to fail.
+
+The solution is a new regression-test branch or commit, uploaded to gitlab.
+Then set that regression test branch with REGRESSIONTESTBRANCH or
+the specific commit with REGRESSIONTESTCOMMIT when
+running the specific pipeline that requires the regressiontest-update. 
+See below on how to set variables for specific pipelines.
+
+Variables
+~~~~~~~~~
+
+The GitLab CI framework, GitLab Runner, plugins, and our own scripts set and
+use several `variables <https://docs.gitlab.com/ee/ci/variables/README.html>`__.
+
+Default values are available from the ``.variables:default`` definition in
+:file:`admin/gitlab-ci/global.gitlab-ci.yml`.
+Many of the mix-in / template jobs provide additional or overriding definitions.
+Other variables may be set when making final job definitions.
+
+Variables may control the behvior of GitLab-CI (those beginning with ``CI_``),
+GitLab Runner and supporting infrastructure, or may be used by job definitions,
+or passed along to the environment of executed commands.
+
+*variables* keys beginning with ``KUBERNETES_`` relate to the GitLab Runner
+`Kubernets executor <https://docs.gitlab.com/runner/executors/kubernetes.html#the-kubernetes-executor>`__
+
+Other important variable keys are as follows.
+
+.. glossary::
+    CI_PROJECT_NAMESPACE
+        Distinguishes pipelines created for repositories in the ``gromacs``
+        GitLab project space. May be used to pre-screen jobs to determine
+        whether |Gromacs| GitLab infrastructure is available to the pipeline
+        before the job is created.
+
+    COMPILER_MAJOR_VERSION
+        Integer version number provided by toolchain mix-in for convenience and
+        internal use.
+
+    CMAKE_COMPILER_SCRIPT
+        CMake command line options for a tool chain. A definition is provided by
+        the mix-in toolchain definitions (e.g. ``.use-gcc8``) to be appended to
+        :command:`cmake` calls in a job's *script*.
+
+    CMAKE_MPI_OPTIONS
+        Provide CMake command line arguments to define GROMACS MPI build options.
+
+    GROMACS_RELEASE
+        Read-only environment variable that can be checked to see if a job is
+        executing in a pipeline for preparing a tagged release.
+        Can be set when launching pipelines via the GitLab web interface.
+        For example, see *rules* mix-ins in :file:`admin/gitlab-ci/global.gitlab-ci.yml`.
+
+    EXTRA_INSTALLS
+        List additional OS package requirements. Used in *before_script* for some
+        mix-in job definitions to install additional software dependencies. If
+        using such a job with *extends*, override this variable key with a
+        space-delimited list of packages (default: ``""``). Consider proposing a
+        patch to the base Docker images to include the dependency to reduce
+        pipeline execution time.
+
+    REGRESSIONTESTBRANCH
+        Use this branch of the regressiontests rather than master to allow for 
+        merge requests that require updated regression tests with valid CI tests.
+
+    REGRESSIONTESTCOMMIT
+        Use this commit to the regressiontests rather than the head on master to 
+        allow for merge requests that require updated regression tests with 
+        valid CI tests.
+
+    POST_MERGE_ACCEPTANCE
+        Read-only environment variable that indicates that only jobs scheduled to
+        run after a commit has been merged into its target branch should be executed.
+        Can be set to run pipelines through the web interface or as schedules.
+        For use please see the *rules* mix-ins in :file:`admin/gitlab-ci/global.gitlab-ci.yml`.
+
+
+.. todo:: Define common variables.
+    ``BUILD_DIR``, ``INSTALL_DIR``, ``CACHE_FALLBACK_KEY``, ...
+
+Setting variables
+~~~~~~~~~~~~~~~~~
+
+Variables for individual piplelines are set in the gitlab interface under 
+``CI/CD``; ``Pipelines``. Then chose in the top right corner ``Run Piplelines``.
+Under ``Run for``, the desired branch may be selected, and variables may be set
+in the fields below.
diff --git a/docs/dev-manual/infrastructure.rst b/docs/dev-manual/infrastructure.rst
new file mode 100644 (file)
index 0000000..535a688
--- /dev/null
@@ -0,0 +1,15 @@
+=============================
+Automation and Infrastructure
+=============================
+
+Through the 2020 release, automated testing and documentation builds are
+performed by a Jenkins installation. With the resolution of :issue:`3272`,
+|Gromacs| is transitioning to GitLab and GitLab Runner.
+
+..  toctree::
+    :maxdepth: 2
+
+    jenkins
+    gitlab
+    containers
+
index 65f2943e039e4eb8a84c1e7389c836c32b2c0e64..af7db95be2181ab5727fc3962c8c7d71e30e2d0e 100644 (file)
@@ -1,8 +1,9 @@
 Understanding Jenkins builds
 ============================
 
-This page documents what different Jenkins builds actually run from the
-|Gromacs| source tree.  The purpose is two-fold:
+This page documents what different Jenkins builds actually run at
+http://jenkins.gromacs.org/ from the |Gromacs| source tree.
+The purpose is two-fold:
 
 * Provide information on how to interpret Jenkins failures and how to run the
   same tasks locally to diagnose issues (in most cases, referring to the
index 87fec394b4a66cd82bdd879ac8a4c5a1be5b9945..ab1486c39e438f688d367807a84a056e0885a0f1 100644 (file)
@@ -7,19 +7,19 @@ reason for deviating from them.
 Portability considerations
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Most |Gromacs| files compile as C++14, but some files remain that compile as C99.
+Most |Gromacs| files compile as C++17, but some files remain that compile as C99.
 C++ has a lot of features, but to keep the source code maintainable and easy to read, 
 we will avoid using some of them in |Gromacs| code. The basic principle is to keep things 
 as simple as possible.
 
 * MSVC supports only a subset of C99 and work-arounds are required in those cases.
-* We should be able to use virtually all C++14 features outside of OpenCL kernels
+* We should be able to use virtually all C++17 features outside of OpenCL kernels
   (which compile as C), and for consistency also in CUDA kernels.
 
 C++ Standard Library
 --------------------
 
-|Gromacs| code must support the lowest common denominator of C++14 standard library
+|Gromacs| code must support the lowest common denominator of C++17 standard library
 features available on supported platforms.
 Some modern features are useful enough to warrant back-porting.
 Consistent and forward-compatible headers are provided in ``src/gromacs/compat/``
@@ -34,7 +34,7 @@ C++ compilers, and because we want to increase readability. However, |Gromacs| i
 advanced projects in constant development, and as our needs evolve we will both
 relax and tighten many of these points. Some of these changes happen naturally as
 part of agreements in code review, while major parts where we don't agree should be
-pushed to a redmine thread. Large changes should be suggested early in the development
+pushed to a `issue tracker`_ thread. Large changes should be suggested early in the development
 cycle for each release so we avoid being hit by last-minute compiler bugs just before
 a release.
 
@@ -64,12 +64,19 @@ a release.
 * Use ``optional<T>`` types in situations where there is exactly one,
   reason (that is clear to all parties) for having no value of type T,
   and where the lack of value is as natural as having any regular
-  value of T. Good examples include the return type of a function that
-  parses an integer value from a string, searching for a matching
+  value of T, see |linkoptionalboost|. Good examples include the return type of a
+  function that parses an integer value from a string, searching for a matching
   element in a range, or providing an optional name for a residue
-  type. Prefer some other construct when the logic requires an
-  explanation of the reason why no regular value for T exists, ie.  do
-  not use ``optional<T>`` for error handling.
+  type. Do use optional for lazy loading of resources, e.g., objects that have
+  no default constructor and are hard to construct.
+  Prefer other constructs when the logic requires an explanation of the
+  reason why no regular value for T exists, e.g.,  do not use ``optional<T>``
+  for error handling. 
+  ``optional<T>`` "models an object, not a pointer, even though operator*() and
+  operator->() are defined" (|linkoptionalcppref|). No dynamic memory allocation
+  ever takes place and forward declaration of objects stored in ``optional<T>``
+  does not work. Thus refrain from optional when passing handles; in contrast to
+  unique_ptr, optional has value semantics, not reference semantics.
 * Don't use C-style casts; use ``const_cast``, ``static_cast`` or
   ``reinterpret_cast as appropriate``. See the point on RTTI for
   ``dynamic_cast``. For emphasizing type (e.g. intentional integer division)
@@ -165,8 +172,9 @@ a release.
 .. |linkrefnotnull1| replace:: `here <http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Ri-nullptr>`__
 .. |linkrefnotnull2| replace:: `here <http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-nullptr>`__
 .. |linkrefstringview| replace:: `here <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html#Rstr-view>`__
-
-
+.. |linkoptionalboost| replace:: `here <https://www.boost.org/doc/libs/release/libs/optional>`__
+.. |linkoptionalbartek| replace:: `here <https://www.bfilipek.com/2018/05/using-optional.html>`__
+.. |linkoptionalcppref| replace:: `cppreference <https://en.cppreference.com/w/cpp/utility/optional>`__
 
 .. _implementing exceptions:
 
index 4a63fd4cf0242953855e2fccf522e0167cc46086..e9bf4c8baa7d5a67cd370c51718ec9191ca3953a 100644 (file)
@@ -274,7 +274,7 @@ Doxygen documentation
   See :doc:`doxygen` for details of how the Doxygen documentation is built and
   organized.
 
-.. TODO: Create a separate page (at the front of the developer guide, and/or at
+.. todo:: Create a separate page (at the front of the developer guide, and/or at
    the main index.rst) that describes the documentation from readers'
    perspective, and move relevant content there.  This should contain just an
    overview of how the documentation is organized in the source tree.
index ac1ef91828bbb13c244c819bde9561e4568e0912..74272b9ab7414af2538cf3ebf7d88a1b9bd243a2 100644 (file)
@@ -10,15 +10,13 @@ Release engineering with Gitlab
    :hidden:
 
 We are currently switching our build and testing system to use Gitlab
-and the integrated CI system, with information for the general system found
-in the official `Gitlab documentation <https://docs.gitlab.com/ee/ci/yaml/>`_.
-The new configuration for the builds and tests can be found in the file
-``.gitlab-ci.yml``, with the templates for configuring is found in the files in the
-``admin/ci-templates/`` directory. This section is going to be extended
+CI pipelines run on GitLab Runner. This section is going to be extended
 with individual build information as it comes available. For now we are
 using a combination of building with the previous system on Jenkins
 and post-submit verification on Gitlab.
 
+.. seealso:: :doc:`../infrastructure`
+
 .. _releng-triggering-builds:
 
 Triggering builds on Gitlab
index 2d9109b99347bebb9e24f7f35d3b06c9154cc41c..93e58492fe599fd6a66100fd257da1a7315b9ede 100644 (file)
@@ -1,11 +1,9 @@
-Guidelines for creating meaningful redmine issue reports
-========================================================
+Guidelines for creating meaningful issue reports
+================================================
 This section gives some started on how to generate useful issues on the
-|Gromacs| `redmine issue tracker`_. The information here comes to a large extent
+|Gromacs| `issue tracker`_. The information here comes to a large extent
 directly from there, to help you in preparing your reports.
 
-.. _redmine issue tracker: https://redmine.gromacs.org
-
 What to report
 ^^^^^^^^^^^^^^
 Please only report issues you have confirmed to be caused by |Gromacs| behaving in an
@@ -36,7 +34,7 @@ does not result in any warnings or errors in itself. If your example generates e
 not be considered as *real*, or at the minimum it will be much harder to analyse to find the actual issue.
 
 
-If your inputs are sensitive, then it is possible to create private Redmine issues so that the
+If your inputs are sensitive, then it is possible to create private `issues`_ so that the
 developer team can have access to solve the problem, while preventing widespread
 visibility on the internet.
 
@@ -61,7 +59,7 @@ General issue workflow
 The general issue workflow is shown in the figure below:
 
 .. image:: redmine-states.png
-   :alt:  Sample procedure pathway for issues reported in redmine.
+   :alt:  Sample procedure pathway for reported issues.
 
 
 .. Text below is stolen from the old Gromacs web page
index 6ef21feadb473f0511d2c432037eceab93a2ce3c..70841bb1574c99e5108536f4b49b39e1ead27cdd 100644 (file)
@@ -31,8 +31,8 @@ this page.
   Guidelines for using Doxygen to document the source code are currently in a
   section on the page on general Doxygen usage.
 :doc:`reportstyle`
-  Guidelines for preparing and formatting bug reports on redmine.
+  Guidelines for preparing and formatting bug reports.
 :doc:`commitstyle`
   Guidelines for formatting git commits when sending in proposed fixes for code review.
 
-.. TODO: Add more guidelines
+.. todo:: Add more guidelines
index 9dce91439cabebf59f68bab07cbaf4eab6daedac..fd90412c285334743a55380d9308631d3bc7deae 100644 (file)
@@ -8,18 +8,22 @@ Several tools have their own individual pages and are listed below.
 
    doxygen
    change-management
-   jenkins
+   infrastructure
    releng/index
    gmxtree
    code-formatting
    testutils
    physical_validation
 
-.. TODO: Consider what is the most reasonable structure; currently, this list
+.. todo:: :issue:`3032`
+
+   Consider what is the most reasonable structure; currently, this list
    here does not make much sense in the overall organization and creates a
    confusing TOC for the developer guide.
 
-.. TODO: Add details for most of the tools, either in the form of links to wiki,
+.. todo:: :issue:`3267`
+
+   Add details for most of the tools, either in the form of links to wiki,
    or to a separate page that explains more details.
 
 Change management
@@ -35,30 +39,26 @@ git
 
   Other basic tutorial material for ``git`` can be found on the `web <https://git-scm.com/doc/ext>`__.
 
-Gerrit
-  All code changes go through a code review system at
-  http://gerrit.gromacs.org.
-
-Jenkins
-  All changes pushed to Gerrit are automatically compiled and otherwise
-  checked on various platforms using a continuous integration system at
-  http://jenkins.gromacs.org.
-  :doc:`jenkins` documents how Jenkins interacts with the build system,
-  providing information on how to replicate the builds Jenkins does (e.g., to
+GitLab
+  Bugs and issues, as well as some random features and discussions,
+  are tracked, and all code changes go through a code review system at
+  https://gitlab.com/gromacs/gromacs.
+
+Build testing
+  All changes pushed to GitLab are automatically compiled and otherwise
+  checked on various platforms.
+  :doc:`infrastructure` documents how builds are automated,
+  providing information on how to replicate the builds (e.g., to
   diagnose issues).
   :doc:`releng/index` provides more information on the technical implementation
   of the builds.
 
-Redmine
-  Bugs and issues, as well as some random features and discussions,
-  are tracked at http://redmine.gromacs.org.
-
 .. _Git Tips & Tricks: http://www.gromacs.org/index.php?title=Developer_Zone/Git/Git_Tips_%26_Tricks
 
 Build system
 ------------
 
-.. TODO: details, ASAN, others?
+.. todo:: details, ASAN, others?
 
 CMake
   Main tool used in the build system.
@@ -86,49 +86,46 @@ The tools and scripts listed below are used to automatically check/apply
 formatting that follows |Gromacs| style guidelines described on a separate page:
 :doc:`style`.
 
-uncrustify
-  `uncrustify <http://uncrustify.sourceforge.net>`_ is used for automatic
-  indentation and other formatting of the source code to follow
-  :doc:`formatting`.  All code must remain invariant under uncrustify
-  with the config at ``admin/uncrustify.cfg``.  A patched version of uncrustify is
-  used.  See :ref:`gmx-uncrustify` for details.
-
 clang-format
   We use clang-format to enforce a consistent coding style, with the
   settings recorded in ``.clang-format`` in the main tree.
   See :ref:`gmx-clang-format` for details.
 
+clang-tidy
+  The source code linter clang-tidy is used to enforce common restrictions to the
+  code, with the checks collected under ``.clang-tidy`` at the top of the main tree.
+  See :ref:`gmx-clang-tidy` for details.
+
 ``admin/copyright.py``
   This Python script adds and formats copyright headers in source files.
   ``copyright.sh`` (see below) uses the script to check/update copyright years on
   changed files automatically.
 
-``admin/uncrustify.sh``
-  This ``bash`` script runs uncrustify for all
-  files that have local changes and checks that they conform to the prescribed
-  style.  Optionally, the script can also apply changes to make the files
-  conform. It is included only for historical reasons.
-  See :doc:`formatting` for details.
-
 ``admin/copyright.sh``
   This ``bash`` script runs the ``copyright.py`` python script to enforce
   correct copyright information in all files that have local changes
   and checks that they conform to the prescribed
   style.  Optionally, the script can also apply changes to make the files
   conform.
-  This script is automatically run by Jenkins to ensure that all commits adhere
+  This script is automatically run by the CI to ensure that all commits adhere
   to :doc:`formatting`.  If the copyright job does not succeed, it
   means that this script has something to complain.
   See :doc:`code-formatting` for details.
 
 ``admin/clang-format.sh``
   This script enforces coding style using clang-format.
-  This script is automatically run by Jenkins to ensure that all commits adhere
+  This script is automatically run by our CI to ensure that all commits adhere
   to :doc:`formatting`.
 
+``admin/clang-tidy.sh``
+  The clang-tidy code correctness restrictions are enforced by this script.
+  The script is also used by the CI to verify the code, in addition to nightly
+  compilations using clang-tidy on the whole tree.
+
 ``admin/git-pre-commit``
   This sample git pre-commit hook can be used if one wants to apply
-  ``uncrustify.sh`` and ``clang-format.sh`` automatically before every commit to check for formatting
+  ``clang-tidy.sh``, ``copyright.sh`` and ``clang-format.sh`` automatically
+  before every commit to check for formatting
   issues.  See :doc:`code-formatting` for details.
 
 ``docs/doxygen/includesorter.py``
@@ -142,12 +139,12 @@ include directive checker
   applied in the formatting script.  To check for issues, it is instead integrated into
   a ``check-source`` build target.  When this target is built, it also checks for
   include formatting issues.  Internally, it uses the sorter script.  This check
-  is run in Jenkins as part of the Documentation job.
+  is run in the CI as part of the Documentation job.
   Details for the checking mechanism are on a separate page (common for several
   checkers): :doc:`gmxtree`.
 
 ``admin/reformat_all.sh``
-  This ``bash`` script runs uncrustify/clang-format/``copyright.py``/include sorter
+  This ``bash`` script runs clang-format/``copyright.py``/include sorter
   on all relevant files in the source tree (or in a particular directory).
   The script can also produce the list of files where these scripts are applied,
   for use with other scripts.  See :doc:`code-formatting` for details.
@@ -160,4 +157,3 @@ git attributes
   checking/formatting to apply.  Custom attributes are used for specifying some
   build system dependencies for easier processing in CMake.
 
-include-what-you-use
index 64a5cf4747e7fe14d9b67efb2fa5145f5697e79d..99ab6d665c61ccfb2dad7b3e35502006df5ee3fb 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -57,7 +58,7 @@ gmx_dependent_option(
 mark_as_advanced(GMX_COMPACT_DOXYGEN)
 
 set(USE_PYTHON_SCRIPTS OFF)
-if (PYTHONINTERP_FOUND)
+if (Python3_Interpreter_FOUND)
     set(USE_PYTHON_SCRIPTS ON)
 endif()
 
@@ -164,7 +165,7 @@ if (DOXYGEN_FOUND)
         # dependency on doxygen-xml takes care of that transitively.
         gmx_add_custom_output_target(dep-graphs-dot ADD_FAST_TARGET
             OUTPUT STAMP
-            COMMAND ${PYTHON_EXECUTABLE}
+            COMMAND ${Python3_EXECUTABLE}
                 ${CMAKE_CURRENT_SOURCE_DIR}/graphbuilder.py
                 -S ${CMAKE_SOURCE_DIR} -B ${CMAKE_BINARY_DIR}
                 --ignore-cycles ${CMAKE_CURRENT_SOURCE_DIR}/cycle-suppressions.txt
@@ -211,7 +212,7 @@ if (DOXYGEN_FOUND)
         # The output .log file currently needs to be here, since Jenkins
         # expects that.
         set(check_source_command
-            ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/check-source.py
+            ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/check-source.py
             -S ${CMAKE_SOURCE_DIR} -B ${CMAKE_BINARY_DIR}
             -l ${CMAKE_CURRENT_BINARY_DIR}/check-source.log
             --ignore ${CMAKE_CURRENT_SOURCE_DIR}/suppressions.txt
index 19e120d9b46e00028e0460413cebff694d2d09f8..d713ea9b564995041de8233d033bf44b584f67fd 100644 (file)
@@ -51,6 +51,8 @@ PREDEFINED            += gmx_inline=inline
 # and that does not have unnecessary function declarations.
 PREDEFINED            += __STDC__ YYMALLOC=malloc YYFREE=free
 
+EXTRA_PACKAGES        += amssymb
+
 JAVADOC_AUTOBRIEF      = YES
 BUILTIN_STL_SUPPORT    = YES
 INLINE_INHERITED_MEMB  = YES
index d49152a172b9f04049128182eee6b24a4a71a7e2..ad53e6cb201dafc4f4d59b59b8d05d9ab4fd1acf 100644 (file)
@@ -2,11 +2,6 @@
 # "moduleA -> moduleB" means that moduleA should not depend on moduleB, and is
 # a problem to be addressed at some point.
 
-# Compat wants assertions and string code from utility, and other string code
-# in utility wants to use string_view from compat. If we fix this, it might be
-# best to implement the compat namespace within the utility module.
-compat -> utility
-
 domdec -> imd
 domdec -> ewald
 domdec -> mdlib
@@ -16,7 +11,6 @@ mdlib -> essentialdynamics
 mdlib -> imd
 mdlib -> ewald
 mdlib -> pulling
-mdlib -> awh
 nbnxm -> domdec
 nbnxm -> mdlib
 simd -> hardware
@@ -29,4 +23,7 @@ modularsimulator -> mdrun
 
 # Cycle counters in timing use comrec for the set up, which is in the mdtypes. This introduces
 # cyclic dependencies if the cycle counting is used anywhere in mdtypes.
-timing -> mdtypes
\ No newline at end of file
+timing -> mdtypes
+
+# awh has dependencies in mdlib and pulling that need resolving.
+mdlib -> applied_forces
index 067d84af326e4b04c3edc8a6e0ecae410444d86d..2d72eee2fa1dcfd868f6288f9ab470873804593d 100755 (executable)
@@ -2,7 +2,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -400,15 +400,15 @@ class Member(Entity):
         """Add a compound that contains this member."""
         self._parents.add(compound)
         if isinstance(compound, Class):
-            assert self._class is None
+            assert self._class is None, 'Class \"{0}\" was already added. Maybe you have two entities with the same name.'.format(self._class)
             self._class = compound
         elif isinstance(compound, Namespace):
-            assert self._namespace is None
+            assert self._namespace is None, 'Namespace \"{0}\" was already added. Maybe you have two entities with the same name.'.format(self._namespace)
             self._namespace = compound
         elif isinstance(compound, File):
             self._files.add(compound)
         elif isinstance(compound, Group):
-            assert self._group is None
+            assert self._group is None, 'Group \"{0}\" was already added.'.format(self._group)
             self._group = compound
         else:
             assert False
index 6103243bd1416c7f65ffab60ae722f467c7cd61d..f03763a95499e543e5918683ccf06656022e4ca6 100644 (file)
@@ -2,7 +2,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -940,10 +941,13 @@ class GromacsTree(object):
     def load_git_attributes(self):
         """Load git attribute information for files."""
         args = ['git', 'check-attr', '--stdin', 'filter']
-        git_check_attr = subprocess.Popen(args, stdin=subprocess.PIPE,
-                stdout=subprocess.PIPE, cwd=self._source_root)
-        filelist = '\n'.join(map(File.get_relpath, self._files.values()))
-        filters = git_check_attr.communicate(filelist.encode())[0].decode()
+        filelist = '\n'.join(map(File.get_relpath, self._files.values())) + '\n'
+        git_check_attr = subprocess.run(args,
+                                        input=filelist,
+                                        stdout=subprocess.PIPE,
+                                        universal_newlines=True,
+                                        cwd=self._source_root)
+        filters = git_check_attr.stdout
         for fileinfo in filters.splitlines():
             path, dummy, value = fileinfo.split(': ')
             fileobj = self._files.get(path)
@@ -963,8 +967,11 @@ class GromacsTree(object):
                 args.extend(['-e', define])
             args.extend(['--', '*.cpp', '*.c', '*.cu', '*.h', '*.cuh'])
             define_re = r'\b(?:' + '|'.join(all_defines)+ r')\b'
-            output = subprocess.check_output(args, cwd=self._source_root).decode()
-            for line in output.splitlines():
+            completed_process = subprocess.run(args,
+                                               cwd=self._source_root,
+                                               universal_newlines=True,
+                                               stdout=subprocess.PIPE)
+            for line in completed_process.stdout.splitlines():
                 (filename, text) = line.split('\0')
                 fileobj = self._files.get(filename)
                 if fileobj is not None and fileobj not in excluded_files:
index 763eb255b0dc292f41a4bf9869604d33c556b8af..4e974a73add4b3b1cf0b24f3a1c7a0e693a5cfa4 100755 (executable)
@@ -2,7 +2,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index a9ce6014882cc95d8be7c05f5c60f4cccf60950d..1003caebec4dfb4d5ca9cbe58f16526345f7d01d 100755 (executable)
@@ -2,7 +2,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -112,14 +113,68 @@ class GroupedSorter(object):
             'stddef.h', 'stdint.h', 'stdio.h', 'stdlib.h', 'string.h',
             'time.h']
     _std_c_cpp_headers = ['c' + x[:-2] for x in _std_c_headers]
-    _std_cpp_headers = ['algorithm', 'array', 'chrono', 'deque', 'exception', 'fstream',
-            'functional', 'initializer_list', 'iomanip', 'ios', 'iosfwd',
-            'iostream', 'istream', 'iterator',
-            'limits', 'list', 'map', 'memory', 'mutex',
-            'new', 'numeric', 'ostream', 'random',
-            'regex', 'set', 'sstream', 'stdexcept', 'streambuf', 'string', 'strstream',
-            'thread', 'tuple', 'type_traits', 'typeindex', 'typeinfo', 'vector',
-            'unordered_map', 'utility']
+    _std_cpp_headers = ['algorithm',
+                        'any',
+                        'array',
+                        'atomic',
+                        'charconv',
+                        'chrono',
+                        'cinttypes',
+                        'cfenv',
+                        'complex',
+                        'cstdint',
+                        'deque',
+                        'exception',
+                        'execution',
+                        'filesystem',
+                        'forward_list',
+                        'fstream',
+                        'functional',
+                        'future',
+                        'initializer_list',
+                        'iomanip',
+                        'ios',
+                        'iosfwd',
+                        'iostream',
+                        'istream',
+                        'iterator',
+                        'limits',
+                        'list',
+                        'locale',
+                        'map',
+                        'memory',
+                        'memory_resource',
+                        'mutex',
+                        'new',
+                        'numeric',
+                        'optional',
+                        'ostream',
+                        'queue',
+                        'random',
+                        'ratio',
+                        'regex',
+                        'scoped_allocator',
+                        'set',
+                        'sstream',
+                        'stack',
+                        'stdexcept',
+                        'streambuf',
+                        'string',
+                        'string_view',
+                        'strstream',
+                        'system_error',
+                        'thread',
+                        'tuple',
+                        'type_traits',
+                        'typeindex',
+                        'typeinfo',
+                        'unordered_map',
+                        'unordered_set',
+                        'valarray',
+                        'variant',
+                        'vector',
+                        'unordered_map',
+                        'utility']
 
     def __init__(self, style='pub-priv', absolute=False):
         """Initialize a sorted with the given style."""
index 400655f7d0bea68b578677b6937467852ea7b845..c5f73730fa03dc6a74ae37feff3e7456e3cff5cb 100644 (file)
@@ -12,10 +12,12 @@ data, most of which is owned by the runner object.
 GROMACS will automatically use the modular simulator for the velocity
 verlet integrator (`integrator = md-vv`), if the functionality chosen
 in the other input parameters is implemented in the new framework.
-Currently, this includes NVE simulations, NVT simulations (
-`tcoupl = v-rescale` only), NPH simulation (`pcoupl = parrinello-rahman` 
-only), and NPT simulations (`tcoupl = v-rescale` and 
-`pcoupl = parrinello-rahman` only), with or without free energy perturbation.
+Currently, this includes NVE, NVT, NPH, and NPT simulations,
+with or without free energy perturbation, using thermodynamic
+boundary conditions
+
+* `tcoupl`: `no`, `v-rescale`, or `berendsen`
+* `pcoupl`: `no` or `parrinello-rahman`
 
 To disable the modular simulator for cases defaulting to the new framework,
 the environment variable `GMX_DISABLE_MODULAR_SIMULATOR=ON` can be set. To
@@ -81,52 +83,6 @@ Periodically during the simulation, the scheduler builds a
 order. Over time, with data dependencies clearly defined, this
 approach can be modified to have independent tasks run in parallel.
 
-The approach is most easily displayed using some pseudo code:
-
-    class ModularSimulator : public ISimulator
-    {
-        public:
-            //! Run the simulator
-            void run() override;
-        private:
-            std::vector<ISignaller*> signallers_;
-            std::vector<ISimulatorElement*> elements_;
-            std::queue<SimulatorRunFunction*> taskQueue_;
-    }
-
-    void ModularSimulator::run()
-    {
-        constructElementsAndSignallers();
-        setupAllElements();
-        while (not lastStep)
-        {
-            // Fill the task queue with new tasks (can be precomputed for many steps)
-            populateTaskQueue();
-            // Now simply loop through the queue and run one task after the next
-            for (auto task : taskQueue)
-            {
-                (*task)();  // run task
-            }
-        }
-    }
-
-This allows for an important division of tasks.
-
-* `constructElementsAndSignallers()` is responsible to **store the
-  elements in the right order**. This includes the different order of
-  element in different algorithms (e.g. leap-frog vs. velocity
-  verlet), but also logical dependencies (energy output after compute
-  globals).
-* `populateTaskQueue()` is responsible to **decide if elements need to
-  run at a specific time step**. The elements get called in order, and
-  decide whether they need to run at a specific step. This can be
-  pre-computed for multiple steps. In the current implementation, the
-  tasks are pre-computed for the entire life-time of the neighbor
-  list.
-* **Running the actual simulation tasks** is done after the task queue
-  was filled.  This is achieved by simply looping over the task list,
-  no conditionals or branching needed.
-
 ### Simulator elements
 
 The task scheduler holds a list of *simulator elements*, defined by
@@ -139,7 +95,27 @@ computation will not be carried out immediately, but that it will be
 called later during the actual (partial) simulation run. From the
 point of view of the builder of the task scheduler, it is important to
 note that the order of the elements determines the order in which
-computation is performed. The task scheduler periodically loops over
+computation is performed.
+
+    class ISimulatorElement
+    {
+    public:
+        /*! \\brief Query whether element wants to run at step / time
+         *
+         * Element can register one or more functions to be run at that step through
+         * the registration pointer.
+         */
+        virtual void scheduleTask(Step, Time, const RegisterRunFunction&) = 0;
+        //! Method guaranteed to be called after construction, before simulator run
+        virtual void elementSetup() = 0;
+        //! Method guaranteed to be called after simulator run, before deconstruction
+        virtual void elementTeardown() = 0;
+        //! Standard virtual destructor
+        virtual ~ISimulatorElement() = default;
+    }; 
+
+
+The task scheduler periodically loops over
 its list of elements, builds a queue of function pointers to run, and
 returns this list of tasks. As an example, a possible application
 would be to build a new queue after each domain-decomposition (DD) /
@@ -170,6 +146,164 @@ to trajectory at the end of this step. The energy element can then
 register an energy calculation during that step, being ready to write
 to trajectory when requested.
 
+    class ISignaller
+    {
+    public:
+        //! Function run before every step of scheduling
+        virtual void signal(Step, Time) = 0;
+        //! Method guaranteed to be called after construction, before simulator run
+        virtual void setup() = 0;
+    };
+    
+### The modular simulator
+
+The approach is most easily displayed using some simplified (pseudo) code.
+    
+The simulator itself is responsible to **store the elements in the 
+right order** (in `addIntegrationElements`) This includes the different 
+order of elements in different algorithms (e.g. leap-frog vs. velocity
+verlet), but also logical dependencies (energy output after compute
+globals). Once the algorithm has been built, the simulator simply
+executes one task after the next, until the end of the simulation is
+reached.
+
+    class ModularSimulator : public ISimulator
+    {
+        public:
+            //! Run the simulator
+            void run() override;
+    }
+
+    void ModularSimulator::run()
+    {
+
+        ModularSimulatorAlgorithmBuilder algorithmBuilder();
+        addIntegrationElements(&algorithmBuilder);
+        auto algorithm = algorithmBuilder.build();
+    
+        while (const auto* task = algorithm.getNextTask())
+        {
+            // execute task
+            (*task)();
+        }
+    }
+    
+The following snippet illustrates building a leap-frog integration
+algorithm. The algorithm builder allows for a concise description of 
+the simulator algorithm. 
+    
+    void ModularSimulator::addIntegrationElements(ModularSimulatorAlgorithmBuilder* builder)
+    {
+        if (legacySimulatorData_->inputrec->eI == eiMD)
+        {
+            // The leap frog integration algorithm
+            builder->add<ForceElement>();
+             // We have a full state here (positions(t), velocities(t-dt/2), forces(t)
+            builder->add<StatePropagatorData::Element>();
+            if (legacySimulatorData_->inputrec->etc == etcVRESCALE)
+            {
+                builder->add<VRescaleThermostat>(-1, VRescaleThermostatUseFullStepKE::No);
+            }
+            builder->add<Propagator<IntegrationStep::LeapFrog>>(legacySimulatorData_->inputrec->delta_t,
+                                                                RegisterWithThermostat::True,
+                                                                RegisterWithBarostat::True);
+            if (legacySimulatorData_->constr)
+            {
+                builder->add<ConstraintsElement<ConstraintVariable::Positions>>();
+            }
+            builder->add<ComputeGlobalsElement<ComputeGlobalsAlgorithm::LeapFrog>>();
+            // We have the energies at time t here
+            builder->add<EnergyData::Element>();
+            if (legacySimulatorData_->inputrec->epc == epcPARRINELLORAHMAN)
+            {
+                builder->add<ParrinelloRahmanBarostat>(-1);
+            }
+        }
+    }
+    
+### The simulator algorithm
+    
+The simulator algorithm is responsible to **decide if elements need to
+run at a specific time step**. The elements get called in order, and
+decide whether they need to run at a specific step. This can be
+pre-computed for multiple steps. In the current implementation, the
+tasks are pre-computed for the entire life-time of the neighbor
+list.
+
+The simulator algorithm offers functionality to get the next task
+from the queue. It owns all elements involved in the simulation
+and is hence controlling their lifetime. This ensures that pointers and
+callbacks exchanged between elements remain valid throughout the duration
+of the simulation run. It also maintains the list of tasks,
+and updates it when needed.
+    
+    class ModularSimulatorAlgorithm
+    {
+    public:
+        //! Get next task in queue
+        [[nodiscard]] const SimulatorRunFunction* getNextTask();
+    private:
+        //! List of signalers
+        std::vector<std::unique_ptr<ISignaller>> signallerList_;
+        //! List of elements
+        std::vector<std::unique_ptr<ISimulatorElement>> elementsList_;
+
+        //! The run queue
+        std::vector<SimulatorRunFunction> taskQueue_;
+        //! The task iterator
+        std::vector<SimulatorRunFunction>::const_iterator taskIterator_;
+
+        //! Update task queue
+        void updateTaskQueue();
+    }
+    
+The `getNextTask()` function is returning the next task in the task
+queue. It rebuilds the task list when needed.
+    
+    const SimulatorRunFunction* ModularSimulatorAlgorithm::getNextTask()
+    {
+        if (!taskQueue_.empty())
+        {
+            taskIterator_++;
+        }
+        if (taskIterator_ == taskQueue_.end())
+        {
+            if (runFinished_)
+            {
+                return nullptr;
+            }
+            updateTaskQueue();
+            taskIterator_ = taskQueue_.begin();
+        }
+        return &*taskIterator_;
+    }
+    
+Updating the task queue involves calling all signallers and
+elements for every step of the scheduling period. This refills
+the task queue. It is important to keep in mind that the *scheduling step* is not
+necessarily identical to the *current step* of the simulation. Most of
+the time, the scheduling step is ahead, as we are pre-scheduling steps.
+    
+    void ModularSimulatorAlgorithm::updateTaskQueue()
+    {
+        for (Step schedulingStep = currentStep; 
+             schedulingStep < currentStep + schedulingPeriod;
+             schedulingStep++)
+        {
+            Time time = getTime(schedulingStep);
+            // Have signallers signal any special treatment of scheduling step
+            for (const auto& signaller : signallerList)
+            {
+                signaller.signal(schedulingStep, time);
+            }
+            // Query all elements whether they need to run at scheduling step
+            for (const auto& element : signallerList)
+            {
+                element.schedule(schedulingStep, time, registerRunFunction_);
+            }
+        }
+    }
+
 ### Sequence diagrams
 
 #### Pre-loop
@@ -180,14 +314,15 @@ perform any setup operations needed.
 \msc
 hscale="2";
 
-ModularSimulator,
-Signallers [label="ModularSimulator::\nSignallers"],
-Elements [label="ModularSimulator::\nElements"],
-TaskQueue [label="ModularSimulator::\nTaskQueue"];
+ModularSimulatorBuilder [label="ModularSimulatorAlgorithmBuilder"],
+ModularSimulator [label="ModularSimulatorAlgorithm"],
+Signallers [label="ModularSimulatorAlgorithm::\nSignallers"],
+Elements [label="ModularSimulatorAlgorithm::\nElements"],
+TaskQueue [label="ModularSimulatorAlgorithm::\nTaskQueue"];
 
 --- [ label = "constructElementsAndSignallers()" ];
-    ModularSimulator => Signallers [ label = "Create signallers\nand order them" ];
-    ModularSimulator => Elements [ label = "Create elements\nand order them" ];
+    ModularSimulatorBuilder => Signallers [ label = "Create signallers\nand order them" ];
+    ModularSimulatorBuilder => Elements [ label = "Create elements\nand order them" ];
 --- [ label = "constructElementsAndSignallers()" ];
 |||;
 |||;
@@ -214,10 +349,10 @@ branching.
 \msc
 hscale="2";
 
-ModularSimulator,
-Signallers [label="ModularSimulator::\nSignallers"],
-Elements [label="ModularSimulator::\nElements"],
-TaskQueue [label="ModularSimulator::\nTaskQueue"];
+ModularSimulator [label="ModularSimulatorAlgorithm"],
+Signallers [label="ModularSimulatorAlgorithm::\nSignallers"],
+Elements [label="ModularSimulatorAlgorithm::\nElements"],
+TaskQueue [label="ModularSimulatorAlgorithm::\nTaskQueue"];
 
 ModularSimulator box TaskQueue [ label = "loop: while(not lastStep)" ];
 ModularSimulator note TaskQueue [ label = "The task queue is empty. The simulation state is at step N.", textbgcolor="yellow" ];
@@ -251,10 +386,10 @@ called in order, allowing the elements to register their respective tasks.
 \msc
 hscale="2";
 
-ModularSimulator,
-Signallers [label="ModularSimulator::\nSignallers"],
-Elements [label="ModularSimulator::\nElements"],
-TaskQueue [label="ModularSimulator::\nTaskQueue"];
+ModularSimulator [label="ModularSimulatorAlgorithm"],
+Signallers [label="ModularSimulatorAlgorithm::\nSignallers"],
+Elements [label="ModularSimulatorAlgorithm::\nElements"],
+TaskQueue [label="ModularSimulatorAlgorithm::\nTaskQueue"];
 
 --- [ label = "populateTaskQueue()" ];
     ModularSimulator box ModularSimulator [ label = "doDomainDecomposition()\ndoPmeLoadBalancing()" ];
@@ -285,10 +420,10 @@ ModularSimulator note ModularSimulator [ label = "schedulingStep == N + nstlist\
 
 ## Acceptance tests and further plans
 
-In January 2019, we defined acceptance tests which need to be 
+Acceptance tests which need to be 
 fulfilled to make the modular simulator the default code path:
 * End-to-end tests pass on both `do_md` and the new loop in
-  Jenkins pre- and post-submit matrices
+  Gitlab pre- and post-submit pipelines
 * Physical validation cases pass on the new loop
 * Performance on different sized benchmark cases, x86 CPU-only
   and NVIDIA GPU are at most 1% slower -
@@ -312,7 +447,7 @@ We will also explore optimization opportunities, including
 * simultaneous execution of independent tasks
 
 We will probably not prioritize support for (and might consider
-deprecating from do_md for GROMACS 2020)
+deprecating from do_md in a future GROMACS version)
 * Simulated annealing
 * REMD
 * Simulated tempering
@@ -328,7 +463,7 @@ deprecating from do_md for GROMACS 2020)
 * Time-averaged restraints
 * Freeze, deform, cos-acceleration
 
-## Signallers and elements
+## Signaller and element details
 
 The current implementation of the modular simulator consists of
 the following signallers and elements:
@@ -348,51 +483,20 @@ signaller is communicating.
 * `EnergySignaller`: Informs its clients about energy related
   special steps, namely energy calculation steps, virial
   calculation steps, and free energy calculation steps.
-* `TrajectoryElement`: Informs its clients if writing to
+* `TrajectorySignaller`: Informs its clients if writing to
   trajectory (state [x/v/f] and/or energy) is planned for the
-  current step. Note that the `TrajectoryElement` is not a
-  pure signaller, but also implements the `ISimulatorElement`
-  interface (see section "Simulator Elements" below).
+  current step.
 
 ### Simulator Elements
 
 #### `TrajectoryElement`
-The `TrajectoryElement` is a special element, as it
-is both implementing the `ISimulatorElement` and the `ISignaller`
-interfaces. During the signaller phase, it is signalling its
-_signaller clients_ that the trajectory will be written at the
-end of the current step. During the simulator run phase, it is
-calling its _trajectory clients_ (which do not necessarily need
-to be identical with the signaller clients), passing them a valid
+The `TrajectoryElement` is calling its trajectory clients, passing them a valid
 output pointer and letting them write to trajectory. Unlike the
 legacy implementation, the trajectory element itself knows nothing
 about the data that is written to file - it is only responsible
 to inform clients about trajectory steps, and providing a valid
 file pointer to the objects that need to write to trajectory.
 
-#### `StatePropagatorData`
-The `StatePropagatorData` takes part in the simulator run, as it might
-have to save a valid state at the right moment during the
-integration. Placing the StatePropagatorData correctly is for now the
-duty of the simulator builder - this might be automated later
-if we have enough meta-data of the variables (i.e., if
-`StatePropagatorData` knows at which time the variables currently are,
-and can decide when a valid state (full-time step of all
-variables) is reached. The `StatePropagatorData` is also a client of
-both the trajectory signaller and writer - it will save a
-state for later writeout during the simulator step if it
-knows that trajectory writing will occur later in the step,
-and it knows how to write to file given a file pointer by
-the `TrajectoryElement`.
-
-#### `EnergyElement`
-The `EnergyElement` takes part in the simulator run, as it
-does either add data (at energy calculation steps), or
-record a non-calculation step (all other steps). It is the
-responsibility of the simulator builder to ensure that the
-`EnergyElement` is called at a point of the simulator run
-at which it has access to a valid energy state.
-
 #### `ComputeGlobalsElement`
 The `ComputeGlobalsElement` encapsulates the legacy calls to
 `compute_globals`. While a new approach to the global reduction
@@ -412,9 +516,9 @@ The `ForceElement` and the `ShellFCElement` encapsulate the legacy
 calls to `do_force` and `do_shellfc`, respectively. It is the
 responsibility of the simulator builder to place them at the right
 place of the integration algorithm. Moving forward, a version of these
-elements which would allow to only calculate forces of subsets of
-degrees of freedom would be desirable to pave the way towards multiple
-time step integrators, allowing to integrate slower degrees of freedom
+elements which would allow calling of do_force with subsets of the topology
+would be desirable to pave the way towards multiple time step integrators
+within modular simulator, allowing to integrate slower degrees of freedom
 at a different frequency than faster degrees of freedom.
 
 #### `ConstraintElement`
@@ -459,13 +563,41 @@ It integrates the Parrinello-Rahman box velocity equations, takes a
 callback to the propagator to update the velocity scaling factor, and
 scales the box and the positions of the system.
 
-#### `FreeEnergyPerturbationElement`
-The `FreeEnergyPerturbationElement` holds the lambda vector and the
-current FEP state, offering access to its values via getter
-functions. The FreeEnergyPerturbationElement does update the lambda
+#### `StatePropagatorData::Element`
+The `StatePropagatorData::Element` takes part in the simulator run, as it might
+have to save a valid state at the right moment during the
+integration. Placing the StatePropagatorData correctly is for now the
+duty of the simulator builder - this might be automated later
+if we have enough meta-data of the variables (i.e., if
+`StatePropagatorData` knows at which time the variables currently are,
+and can decide when a valid state (full-time step of all
+variables) is reached. The `StatePropagatorData::Element` is also a client of
+both the trajectory signaller and writer - it will save a
+state for later writeout during the simulator step if it
+knows that trajectory writing will occur later in the step,
+and it knows how to write to file given a file pointer by
+the `TrajectoryElement`.
+
+#### `EnergyData::Element`
+The `EnergyData::Element` takes part in the simulator run, 
+either adding data (at energy calculation steps), or
+recording a non-calculation step (all other steps). It is the
+responsibility of the simulator builder to ensure that the
+`EnergyData::Element` is called at a point of the simulator run
+at which it has access to a valid energy state.
+
+It subscribes to the trajectory signaller, the energy signaller,
+and the logging signaller to know when an energy calculation is
+needed and when a non-recording step is enough. The EnergyData
+element is also a subscriber to the trajectory writer element, 
+as it is responsible to write energy data to trajectory.
+
+#### `FreeEnergyPerturbationData::Element`
+The `FreeEnergyPerturbationData::Element` is a member class of
+`FreeEnergyPerturbationData` that updates the lambda
 values during the simulation run if lambda is non-static. It
-implements the checkpointing client interface to save its current
-state for restart.
+implements the checkpointing client interface to save the current
+state of `FreeEnergyPerturbationData` for restart.
 
 ## Data structures
 
@@ -485,41 +617,51 @@ between elements.
 Note that the `StatePropagatorData` can be converted to and from the
 legacy `t_state` object. This is useful when dealing with
 functionality which has not yet been adapted to use the new
-data approach - of the elements currently implemented, only
+data approach. Of the elements currently implemented, only
 domain decomposition, PME load balancing, and the initial
 constraining are using this.
 
-### `EnergyElement`
-The EnergyElement owns the EnergyObject, and is hence responsible
-for saving energy data and writing it to trajectory. It also owns
-the tensors for the different virials and the pressure as well as
-the total dipole vector.
-
-It subscribes to the trajectory signaller, the energy signaller,
-and the logging signaller to know when an energy calculation is
-needed and when a non-recording step is enough. The simulator
-builder is responsible to place the element in a location at
-which a valid energy state is available. The EnergyElement is
-also a subscriber to the trajectory writer element, as it is
-responsible to write energy data to trajectory.
-
-The EnergyElement offers an interface to add virial contributions,
+### `EnergyData`
+The EnergyData owns the EnergyObject, and is hence responsible
+for saving energy data and writing it to trajectory.
+The EnergyData offers an interface to add virial contributions,
 but also allows access to the raw pointers to tensor data, the
 dipole vector, and the legacy energy data structures.
 
-### `TopologyHolder`
-The topology object owns the local topology and holds a constant reference
-to the global topology owned by the ISimulator.
-
-The local topology is only infrequently changed if domain decomposition is
-on, and never otherwise. The topology holder therefore offers elements to register
-as ITopologyHolderClients. If they do so, they get a handle to the updated local 
-topology whenever it is changed, and can rely that their handle is valid 
-until the next update. The domain decomposition element is defined as friend 
-class to be able to update the local topology when needed.
-
-The topology holder is not a `ISimulatorElement`, i.e. it does not take part in the
-simulator loop.
+### `FreeEnergyPerturbationData`
+The `FreeEnergyPerturbationData` holds the lambda vector and the
+current FEP state, offering access to its values via getter
+functions.
+
+## Simulator algorithm builder
+Elements that define the integration algorithm (i.e. which are
+added using the templated `ModularSimulatorAlgorithmBuilder::add`
+method) need to implement a `getElementPointerImpl` factory function.
+This gives them access to the data structures and some other
+infrastructure, but also allows elements to accept additional
+arguments (e.g frequency, offset, ...).
+
+    template<typename Element, typename... Args>
+    void ModularSimulatorAlgorithmBuilder::add(Args&&... args)
+    {
+        // Get element from factory method
+        auto* element = static_cast<Element*>(getElementPointer<Element>(
+                legacySimulatorData_, &elementAdditionHelper_, statePropagatorData_.get(),
+                energyData_.get(), freeEnergyPerturbationData_.get(), &globalCommunicationHelper_,
+                std::forward<Args>(args)...));
+
+        // Make sure returned element pointer is owned by *this
+        // Ensuring this makes sure we can control the life time
+        if (!elementExists(element))
+        {
+            throw ElementNotFoundError("Tried to append non-existing element to call list.");
+        }
+    
+        // Register element with infrastructure
+    }
+    
+Note that `getElementPointer<Element>` will call `Element::getElementPointerImpl`,
+which needs to be implemented by the different elements.
 
 ## Infrastructure
 ### `DomDecHelper` and `PmeLoadBalanceHelper`
@@ -533,10 +675,9 @@ the Simulator is calling them explicitly between task queue population
 steps. This allows elements to receive the new topology / state before
 deciding what functionality they need to run.
 
-### `Checkpointing`
-The `CheckpointHelper` is responsible to write checkpoints. In the
-longer term, it will also be responsible to read checkpoints, but this
-is not yet implemented.
+### Checkpointing
+The `CheckpointHelper` is responsible to write checkpoints, and to offer
+its clients access to the data read from checkpoint.
 
 Writing checkpoints is done just before neighbor-searching (NS) steps,
 or before the last step. Checkpointing occurs periodically (by default,
@@ -545,17 +686,160 @@ NS step, the checkpoint helper on master rank signals to all other ranks
 that checkpointing is about to occur. At the next NS step, the checkpoint
 is written. On the last step, checkpointing happens immediately before the
 step (no signalling). To be able to react to last step being signalled,
-the CheckpointHelper does also implement the `ISimulatorElement` interface,
-but does only register a function if the last step has been called.
+the CheckpointHelper also implements the `ISimulatorElement` interface,
+but only registers a function if the last step has been called.
 
 Checkpointing happens at the top of a simulation step, which gives a
 straightforward re-entry point at the top of the simulator loop.
 
-In the current implementation, the clients of CheckpointHelper fill a
-legacy t_state object (passed via pointer) with whatever data they need
-to store. The CheckpointHelper then writes the t_state object to file.
-This is an intermediate state of the code, as the long-term plan is for
-modules to read and write from a checkpoint file directly, without the
-need for a central object. The current implementation allows, however,
-to define clearly which modules take part in checkpointing, while using
-the current infrastructure for reading and writing to checkpoint.
+#### Implementation details
+##### Other (older) approaches
+**Legacy checkpointing approach:** All data to be checkpointed needs to be
+stored in one of the following data structures:
+* `t_state`, which also holds pointers to
+  - `history_t` (history for restraints)
+  - `df_history_t` (history for free energy)
+  - `ekinstate`
+  - `AwhHistory`
+* `ObservableHistory`, consisting of
+  - `energyhistory_t`
+  - `PullHistory`
+  - `edsamhistory_t`
+  - `swaphistory_t`
+* Checkpoint further saves details about the output files being used
+
+These data structures are then serialized by a function having knowledge of
+their implementation details. One possibility to add data to the checkpoint
+is to expand one of the objects that is currently being checkpointed, and
+edit the respective `do_cpt_XXX` function in `checkpoint.cpp` which interacts
+with the XDR library. The alternative would be to write an entirely new data
+structure, changing the function signature of all checkpoint-related functions,
+and write a corresponding low-level routine interacting with the XDR library.
+
+**The MdModule approach:** To allow for modules to write checkpoints, the legacy
+checkpoint was extended by a KVTree. When writing to checkpoint, this tree gets
+filled (via callbacks) by the single modules, and then serialized. When reading,
+the KVTree gets deserialized, and then distributed to the modules which can read
+back the data previously stored.
+
+##### Modular simulator design
+
+The MdModule checks off almost all requirements to a modularized checkpointing format.
+The proposed design is therefore an evolved form of this approach. Notably, two
+improvements include
+* Hide the implementation details of the data structure holding the data (currently,
+  a KV-Tree) from the clients. This allows to change the implementation details of
+  reading / writing checkpoints without touching client code.
+* Offer a unified way to read and write to data, allowing clients to write one
+  (templated) function to read to and write from checkpoint. This allows to
+  eliminate code duplication and the danger of having read and write functions
+  getting out of sync.
+
+The modular simulator checkpointing does not currently change the way that the
+legacy simulator is checkpointing. Some data structures involved in the legacy
+checkpoint did, however, get an implementation of the new approach. This is
+needed for ModularSimulator checkpointing, but also gives a glimpse of how
+implementing this for legacy data structures would look like.
+
+The most important design part is the `CheckpointData` class. It exposes methods
+to read and write scalar values, ArrayRefs, and tensors. It also allows to create
+a "sub-object" of the same type `CheckpointData` which allows to have more complex
+members implement their own checkpointing routines (without having to be aware that
+they are a member). All methods are templated on the chosen operation,
+`CheckpointDataOperation::Read` or `CheckpointDataOperation::Write`, allowing clients
+to use the same code to read and write to checkpoint. Type traits and constness are
+used to catch as many errors as possible at compile time. `CheckpointData` uses a
+KV-tree to store the data internally. This is however never exposed to the client.
+Having this abstraction layer gives freedom to change the internal implementation
+in the future.
+
+All `CheckpointData` objects are owned by a `ReadCheckpointDataHolder` or
+`WriteCheckpointDataHolder`. These holder classes own the internal KV-tree, and offer
+`deserialize(ISerializer*)` and `serialize(ISerializer*)` functions, respectively,
+which allow to read from / write to file. This separation clearly defines ownership
+and separates the interface aimed at file IO from the interface aimed at objects
+reading / writing checkpoints.
+
+Checkpointing for modular simulator is tied in the general checkpoint facility by
+passing a `ReadCheckpointDataHolder` or `WriteCheckpointDataHolder` object to the
+legacy checkpoint read and write operations.
+
+##### Notes about the modular simulator checkpointing design
+
+**Distinction of data between clients:** The design requires that separate
+clients have independent sub-`CheckpointData` objects identified by a unique key.
+This key is the only thing that needs to be unique between clients, i.e. clients are
+free to use any key _within_ their sub-`CheckpointData` without danger to overwrite
+data used by other clients.
+
+**Versioning:** The design relies on clients keeping their own versioning system
+within their sub-`CheckpointData` object. As the data stored by clients is opaque
+to the top level checkpointing facility, it has no way to know when the internals
+change. Only fundamental changes to the checkpointing architecture can still be
+tracked by a top-level checkpoint version.
+
+**Key existence:** The currently uploaded design does not allow to check whether
+a key is present in `CheckpointData`. This could be introduced if needed - however,
+if clients write self-consistent read and write code, this should never be needed.
+Checking for key existence seems rather to be a lazy way to circumvent versioning,
+and is therefore discouraged.
+
+**Callback method:** The modular simulator and MdModules don't use the exact same
+way of communicating with clients. The two methods could be unified if needed.
+The only _fundamental_ difference is that modular simulator clients need to identify
+with a unique key to receive their dedicated sub-data, while MdModules all read from
+and write to the same KV-tree. MdModules could be adapted to that by either requiring
+a unique key from the modules, or by using the same `CheckpointData` for all modules
+and using a single unique key (something like "MdModules") to register that object
+with the global checkpoint.
+
+**Performance:** One of the major differences between the new approach and the legacy
+checkpointing is that data gets _copied_ into `CheckpointData`, while the legacy
+approach only took a pointer to the data and serialized it. This slightly reduces
+performance. Some thoughts on that:
+* By default, checkpointing happens at the start of the simulation (only if reading
+from checkpoint), every 15 minutes during simulation runs, and at the end of the
+simulation run. This makes it a low priority target for optimization. Consequently,
+not much thoughts have been put in the optimization, but there's certainly some way
+to improve things here and there if we consider it necessary.
+* The copying will only have measurable effect when large data gets checkpointed -
+likely only for saving the positions / velocities of the entire state, so that
+should be the first target for optimization if needed.
+* Copying data could have advantages moving forward - we could continue the
+simulation as soon as the data is written to the `CheckpointData` object, and don't
+necessarily need to wait for writing to the physical medium to happen. It also
+simplifies moving the point at which checkpointing is performed within the
+integrator. One could envision clients storing their data any time during the
+integration step, and serializing the resulting `CheckpointData` after the step.
+This avoids the need to find a single point within the loop at which all clients
+need to be in a state suitable for checkpointing.
+* If, however, we wanted to use the same approach for other, more frequent
+(and hence more perfomance critical) operations such as saving/restoring states
+for MC type algorithms or swapping of states between running simulations in
+multi-sim type settings, performance would become more of an issue.
+
+**ISerializer vs KV-tree:** The new approach uses a KV tree internally. The
+KV tree is well suited to represent the design philosophy of the approach:
+Checkpointing offers a functionality which allows clients to write/read any data
+they want. This data remains opaque to the checkpointing element. Clients can
+read or write in any order, and in the future, maybe even simultaneously. Data
+written by any element should be protected from access from other elements to
+avoid bugs. The downside of the KV tree is that all data gets copied before
+getting written to file (see above).
+
+Replacing a KV tree by a single ISerializer object which gets passed to clients
+would require elements to read and write sequentially in a prescribed order. With
+the help of InMemorySerializer, a KV-Tree could likely be emulated (with sub-objects
+that serialize to memory, and then a parent object that serializes this memory to
+file), but that doesn't present a clear advantage anymore.
+
+### `TopologyHolder`
+The topology object owns the local topology and holds a constant reference
+to the global topology owned by the ISimulator.
+
+The local topology is only infrequently changed if domain decomposition is
+on, and never otherwise. The topology holder therefore offers elements to register
+as ITopologyHolderClients. If they do so, they get a handle to the updated local 
+topology whenever it is changed, and can rely that their handle is valid 
+until the next update. The domain decomposition element is defined as friend 
+class to be able to update the local topology when needed.
index 533e028090419083d30375477e9919af0d08a2a5..6a976c4971d17e308bdc4b0ed62a0ba137829ec0 100644 (file)
@@ -34,6 +34,9 @@ src/gromacs/nbnxm/pairlist_simd_4xm.h: warning: should include "simd.h"
 src/gromacs/nbnxm/kernels_simd_2xmm/kernel_common.h: warning: should include "nbnxm_simd.h"
 src/gromacs/nbnxm/kernels_simd_4xm/kernel_common.h: warning: should include "nbnxm_simd.h"
 
+# This seems to be a false positive
+src/gromacs/nbnxm/cuda/nbnxm_cuda_types.h: error: NbnxmGpu: is in internal file(s), but appears in public documentation
+
 # Temporary while we change the SIMD implementation
 src/gromacs/simd/impl_sparc64_hpc_ace/impl_sparc64_hpc_ace_common.h: warning: should include "simd.h"
 
@@ -46,7 +49,7 @@ src/gromacs/tables/quadraticsplinetable.h: warning: includes "simd.h" unnecessar
 # These are specific to Folding@Home, and easiest to suppress here
 src/gmxpre.h: warning: includes non-local file as "swindirect.h"
 
-# New external API (see https://redmine.gromacs.org/issues/2586) has some unresolved
+# New external API (see https://gitlab.com/gromacs/gromacs/-/issues/2586) has some unresolved
 # conflicts with previous definitions of public API, installed API, and other things
 # described or implemented in check-source.py, gmxtree.py, gmxtree.rst, and others
 # TODO: resolve definitions, update testing heuristics, and activate policy checks
index 14bd9ca100134d07b3f66f6b00bf92e0e379accd..2241e680efe3c0692f2071fb80c713fff8a46059 100644 (file)
@@ -173,6 +173,9 @@ compile the \Gromacs headers.</dd>
 <dd>Required compiler flags.</dd>
 </dl>
 
+Additionally an imported target named `Gromacs::libgromacs` is provided and can
+be used with `target_link_libraries(foo PRIVATE Gromacs::libgromacs)`.
+
 Declared macros/functions that can be used for checking for correctness of some
 settings:
 
index 83844e0202ec5955852e5cef02c6071e7872066f..8519d445c34ee2f0d6ac24c6be96cfb1bf386844 100644 (file)
@@ -16,7 +16,7 @@ Proxies and managed resources
 Operations, factories, and data flow: declaration, definition, and initialization
 =================================================================================
 
-..  todo:: Reference https://redmine.gromacs.org/issues/2993
+..  todo:: Reference https://gitlab.com/gromacs/gromacs/-/issues/2993
 
 Expressing inputs and outputs
 -----------------------------
index f4fb5844ea0f9aca27f94817d9a80c7c3e83cdae..65d80d88186fed2d510dc680aabc4cf649662d8e 100644 (file)
@@ -11,7 +11,7 @@ Command line examples assume the `bash <https://www.gnu.org/software/bash/>`_ sh
 
 .. note:: Regarding multiple GROMACS installations:
     Many GROMACS users switch between multiple GROMACS installations on the same
-    computer using an HPC module system and/or a GMXRC configuration script.
+    computer using an HPC module system and/or a :ref:`GMXRC <getting access to |Gromacs|>` configuration script.
     For the equivalent sort of environment switching with the :py:mod:`gmxapi` Python package,
     we recommend installing it in a different
     `Python virtual environment <https://www.google.com/search?q=python+virtual+environment>`_
@@ -25,9 +25,9 @@ Command line examples assume the `bash <https://www.gnu.org/software/bash/>`_ sh
 
 .. note::
 
-    The following documentation contains frequent references to the :command:`pip` tool
+    The following documentation contains frequent references to the pip_ tool
     for installing Python packages. In some cases, an unprivileged user should
-    use the ``--user`` command line flag to tell :py:mod:`pip` to install packages
+    use the ``--user`` command line flag to tell pip_ to install packages
     into the user site-packages directory rather than the default site-packages
     directory for the Python installation. This flag is not appropriate when
     running :command:`pip` in a virtual environment (as recommended) and is omitted in
@@ -86,7 +86,7 @@ Background
 *gmxapi* comes in three parts:
 
 * GROMACS gmxapi library for C++.
-* This Python package, supporting Python 3.5 and higher
+* This Python package, supporting Python 3.6 and higher
 * MD restraint plugins and sample gmxapi client code
 
 GROMACS requirements
@@ -104,14 +104,15 @@ before proceeding.
     for your GROMACS installation, you will see CMake errors when trying to build
     and install the gmxapi Python package or other client software.
 
-Then, "source" the :file:`GMXRC` file from the GROMACS installation as you normally would
+Then, "source" the :file:`GMXRC` file from the GROMACS installation
+:ref:`as you normally would <getting access to |Gromacs|>`
 before using GROMACS, or note its installation location so that you can pass it
 to the build configuration.
 
 Build system requirements
 -------------------------
 
-gmxapi can be built for Python 3.5 and higher.
+gmxapi can be built for Python 3.6 and higher.
 
 You will need a C++ 14 compatible compiler and a reasonably up-to-date version
 of CMake.
@@ -143,7 +144,7 @@ that works.
 Python environment requirements
 -------------------------------
 
-gmxapi requires Python 3.5 or higher. Check your version with
+gmxapi requires Python 3.6 or higher. Check your version with
 :command:`python3 --version` or :command:`python --version`.
 
 ..  note::
@@ -154,19 +155,20 @@ gmxapi requires Python 3.5 or higher. Check your version with
     instead of :command:`python` and :command:`pip`. You can check the version with
     :command:`python3 --version` or :command:`python --version` and :command:`pip --version`.
 
-To build and install, you also need the packages :py:mod:`cmake`,
-:py:mod:`setuptools`, :py:mod:`networkx`, and :py:mod:`scikit-build`.
+To build and install, you need the Python packages for
+cmake_, networkx_, scikit-build_, and setuptools_
+(all available from `PyPI with pip <https://pip.pypa.io/en/stable/>`_).
 
-For full functionality, you should also have :py:mod:`mpi4py` and :py:mod:`numpy`.
+For full functionality, you should also have mpi4py_ and numpy_.
 These requirements and version numbers are listed in :file:`requirements.txt`.
 
 The easiest way to make sure you have the requirements installed, first update
-:py:mod:`pip`, then use the :file:`requirements.txt` file provided with the repository.
+pip_, then use the :file:`requirements.txt` file provided with the repository.
 File paths in this section are relative to the root directory of your local copy
 of the GROMACS source.
 
-Confirm that :py:mod:`pip` is available, install :py:mod:`pip` if it is missing, or get
-instructions on how to install :py:mod:`pip`::
+Confirm that pip_ is available, install pip_ if it is missing, or get
+instructions on how to install pip_::
 
     python -m ensurepip --default-pip
 
@@ -200,13 +202,13 @@ Testing requirements
 --------------------
 
 Testing is performed with `pytest <https://docs.pytest.org/en/latest/>`_.
-Tests also require :py:mod:`numpy`.
-You can probably install both with :command:`pip`::
+Tests also require numpy_.
+You can probably install both with pip_::
 
     pip install pytest numpy
 
 To test the full functionality also requires an MPI parallel environment.
-You will need the :py:mod:`mpi4py` Python package and an MPI launcher
+You will need the mpi4py_ Python package and an MPI launcher
 (such as :command:`mpiexec`, :command:`mpirun`, a launcher provided by your HPC queuing system,
 or whatever is provided by your favorite MPI package for your operating system).
 
@@ -231,18 +233,18 @@ so you may have to specify compilers on the command line for consistency.
 It may be necessary to require that GROMACS, gmxapi,
 and the sample code are built with the same compiler(s).
 
-Note that strange errors have been known to occur when :py:mod:`mpi4py` is built with
+Note that strange errors have been known to occur when mpi4py_ is built with
 different a different tool set than has been used to build Python and gmxapi.
 If the default compilers on your system are not sufficient for GROMACS or gmxapi,
-you may need to build, e.g., OpenMPI or MPICH, and/or build :py:mod:`mpi4py` with a
+you may need to build, e.g., OpenMPI or MPICH, and/or build mpi4py_ with a
 specific MPI compiler wrapper. This can complicate building in environments such
 as Conda_.
 
 Set the MPICC environment variable to the MPI compiler wrapper and forcibly
-reinstall :py:mod:`mpi4py`::
+reinstall mpi4py_::
 
     export MPICC=`which mpicc`
-    pip install --no-cache-dir --upgrade --no-binary \":all:\" --force-reinstall mpi4py
+    pip install --no-cache-dir --upgrade --no-binary ":all:" --force-reinstall mpi4py
 
 If you have a different MPI C compiler wrapper, substitute it for :command:`mpicc` above.
 
@@ -273,7 +275,7 @@ Locate or install GROMACS
 You need a GROMACS installation that includes the gmxapi headers and library.
 If GROMACS 2020 or higher is already installed,
 *and* was configured with ``GMXAPI=ON`` at build time,
-you can just source the GMXRC
+you can just source the :ref:`GMXRC <getting access to |Gromacs|>`
 (so that the Python package knows where to find GROMACS)
 and skip to the next section.
 
@@ -333,7 +335,7 @@ the remaining examples assume the virtual environment is still active.
 Install dependencies
 ^^^^^^^^^^^^^^^^^^^^
 
-It is always a good idea to update :py:mod:`pip` and :py:mod:`setuptools` before installing
+It is always a good idea to update pip_ and setuptools_ before installing
 new Python packages::
 
     pip install --upgrade pip setuptools
@@ -345,7 +347,7 @@ they are installed and up to date before proceeding.
 
     pip install --upgrade cmake scikit-build
 
-For MPI, we use :py:mod:`mpi4py`.
+For MPI, we use mpi4py_.
 Make sure it is using the same MPI installation that we are building
 GROMACS against and building with compatible compilers.
 
@@ -359,10 +361,18 @@ GROMACS against and building with compatible compilers.
 Install the latest version of gmxapi
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Fetch and install the latest version of gmxapi from the Python Packaging Index::
+Fetch and install the latest official version of gmxapi from the Python Packaging Index::
 
+    # Get the latest official release.
     pip install gmxapi
 
+The `PyPI repository <https://pypi.org/project/gmxapi/#history>`_
+may include pre-release versions,
+but :command:`pip` will ignore them unless you use the ``--pre`` flag::
+
+    # Get the latest version, including pre-release versions.
+    pip install --pre gmxapi
+
 If :command:`pip` does not find your GROMACS installation, use one of the following
 environment variables to provide a hint.
 
@@ -399,7 +409,7 @@ Install from source
 You can also install the :py:mod:`gmxapi` Python package from within a local copy of
 the GROMACS source repository. Assuming you have already obtained the GROMACS
 source code and you are in the root directory of the source tree, you will find
-the :py:mod`gmxapi` Python package sources in the :file:`python_packaging/src` directory.
+the :py:mod:`gmxapi` Python package sources in the :file:`python_packaging/src` directory.
 
 ::
 
@@ -419,7 +429,7 @@ For example, the last line of the previous example could be replaced with::
 
     pip install --no-cache-dir --no-deps --no-index --no-build-isolation .
 
-Refer to :py:mod:`pip` documentation for descriptions of these options.
+Refer to pip_ documentation for descriptions of these options.
 
 If you have built or downloaded a source distribution archive, you can provide
 the archive file to :command:`pip` instead of the ``.`` argument::
@@ -483,9 +493,14 @@ extract Python docstrings.
 
 Sometimes the build environment can choose a different Python interpreter than
 the one you intended.
-You can set the ``PYTHON_EXECUTABLE`` CMake variable to explicitly choose the
-Python interpreter for your chosen installation.
-For example: ``-DPYTHON_EXECUTABLE=\`which python\```
+You can set the ``Python3_ROOT`` or ``CMAKE_PREFIX_PATH`` CMake variable to
+explicitly choose the Python installation or *venv* directory.
+
+If you use pyenv or pyenv-virtualenv to dynamically manage your Python version,
+you can help identify a particular version with ``pyenv version-name`` and the
+directory with ``pyenv prefix {version}``. For example::
+
+    -DPython3_ROOT=$(pyenv prefix $(pyenv version-name))
 
 Docker web server
 -----------------
@@ -497,15 +512,30 @@ https://hub.docker.com/r/gmxapi/docs for more information.
 .. todo::
 
     Document sample_restraint package. Reference issue
-    `3027 <https://redmine.gromacs.org/issues/3027>`_
+    `3027 <https://gitlab.com/gromacs/gromacs/-/issues/3027>`_
 
 .. _gmxapi install troubleshooting:
 
 Troubleshooting
 ===============
 
+AttributeError: module 'enum' has no attribute 'IntFlag'
+--------------------------------------------------------
+
+If you had older versions of some of the dependencies installed,
+you might have picked up a transitive dependency on the ``enum34`` package.
+Try::
+
+    pip uninstall -y enum34
+
+and see if that fixes the problem. If not, try a fresh virtual environment
+(see above) to help narrow down the problem before you
+`open an issue <https://gitlab.com/gromacs/gromacs/-/issues/>`_.
+
 Couldn't find the ``gmxapi`` support library?
-If you don't want to "source" your ``GMXRC`` file, you
+---------------------------------------------
+
+If you don't want to "source" your :ref:`GMXRC <getting access to |Gromacs|>` file, you
 can tell the package where to find a gmxapi compatible GROMACS installation with
 ``gmxapi_DIR``. E.g. ``gmxapi_DIR=/path/to/gromacs pip install .``
 
@@ -544,7 +574,7 @@ Two of the easiest problems to run into are incompatible compilers and
 incompatible Python. Try to make sure that you use the same C and C++
 compilers for GROMACS, for the Python package, and for the sample
 plugin. These compilers should also correspond to the :command:`mpicc` compiler
-wrapper used to compile :py:mod:`mpi4py`. In order to build the Python
+wrapper used to compile mpi4py_. In order to build the Python
 package, you will need the Python headers or development installation,
 which might not already be installed on the machine you are using. (If
 not, then you will get an error about missing :file:`Python.h` at some
@@ -605,4 +635,18 @@ be back on track.
 If you are developing code for gmxapi, this should be an indication to
 rebase your feature branches for the new development cycle.
 
+.. _cmake: https://pypi.org/project/cmake/
+
 .. _Conda: https://docs.conda.io/en/latest/
+
+.. _mpi4py: https://pypi.org/project/mpi4py/
+
+.. _networkx: https://pypi.org/project/networkx/
+
+.. _numpy: https://www.numpy.org/
+
+.. _pip: https://pip.pypa.io/en/stable/
+
+.. _scikit-build: https://pypi.org/project/scikit-build/
+
+.. _setuptools: https://pypi.org/project/setuptools/
index c1100b70c7e8bad8ad844ff9ac6e39af14fd3c89..cc9afd0cc5acd1b0f9dc323ec2bb8a3c7a9256d4 100644 (file)
@@ -42,7 +42,7 @@ a *gmxapi* script.
 
     :py:class:`gmxapi.commandline_operation` relies on the environment :envvar:`PATH`
     to locate executables, including the :command:`gmx` wrapper binary.
-    Relates to `#2961 <https://redmine.gromacs.org/issues/2961>`__.
+    Relates to `#2961 <https://gitlab.com/gromacs/gromacs/-/issues/2961>`__.
 
 .. _parallelism:
 
@@ -351,7 +351,7 @@ More
 Refer to the :doc:`pythonreference` for complete and granular documentation.
 
 For more information on writing or using pluggable simulation extension code,
-refer to https://redmine.gromacs.org/issues/3133.
+refer to https://gitlab.com/gromacs/gromacs/-/issues/3133.
 (For gmxapi 0.0.7 and GROMACS 2019, see https://github.com/kassonlab/sample_restraint)
 
 .. todo:: :issue:`3133`: Replace these links as resources for pluggable extension code become available.
index 0c502d389aac32de01ef2754d323554ffe09b0be..de3aa4d110027505c0799894200a09d80452bb56 100644 (file)
@@ -13,4 +13,4 @@ After installing GROMACS and the gmxapi Python package, use ``pydoc gmxapi``
 from the command line or ``import gmxapi; help(gmxapi)`` within Python for
 package and module documentation.
 
-..  todo:: Example scripts. See issue `3014 <https://redmine.gromacs.org/issues/3014>`_
+..  todo:: Example scripts. See issue `3014 <https://gitlab.com/gromacs/gromacs/-/issues/3014>`_
index 65bd93cfbabaca3995d16cd67ad7c01b73d99c60..354ef9e572e69ae1a1632d497a7fefa4955745e0 100644 (file)
@@ -2,7 +2,9 @@
 Welcome to the |Gromacs| documentation!
 =======================================
 
-..  TODO : consolidate at least some of the material in the
+..  todo::
+
+    Consolidate at least some of the material in the
     Documentation links below into the new user guide, along with all
     of http://www.gromacs.org/Documentation/Cut-off_schemes,
     http://www.gromacs.org/Documentation/Acceleration_and_parallelization
@@ -23,6 +25,7 @@ Welcome to the |Gromacs| documentation!
            how-to/index
            reference-manual/index
            gmxapi/index
+          nblib/index
            dev-manual/index
         
         ==================
@@ -44,6 +47,7 @@ Welcome to the |Gromacs| documentation!
             how-to/index
             reference-manual/index
             gmxapi/index
+           nblib/index
             dev-manual/index
 
 
index c0e7238c99b5e059bb5068b2c4a1f6871cf810d9..438802188eb78613ac41e85463fb4d712cd0bb6c 100644 (file)
@@ -72,8 +72,8 @@ appropriate value instead of ``xxx`` :
 * ``-DCMAKE_C_COMPILER=xxx`` equal to the name of the C99 `Compiler`_ you wish to use (or the environment variable ``CC``)
 * ``-DCMAKE_CXX_COMPILER=xxx`` equal to the name of the C++98 `compiler`_ you wish to use (or the environment variable ``CXX``)
 * ``-DGMX_MPI=on`` to build using `MPI support`_ (generally good to combine with `building only mdrun`_)
-* ``-DGMX_GPU=on`` to build using nvcc to run using NVIDIA `CUDA GPU acceleration`_ or an OpenCL_ GPU
-* ``-DGMX_USE_OPENCL=on`` to build with OpenCL_ support enabled. ``GMX_GPU`` must also be set.
+* ``-DGMX_GPU=CUDA`` to build with NVIDIA CUDA support enabled.
+* ``-DGMX_GPU=OpenCL`` to build with OpenCL_ support enabled.
 * ``-DGMX_SIMD=xxx`` to specify the level of `SIMD support`_ of the node on which |Gromacs| will run
 * ``-DGMX_BUILD_MDRUN_ONLY=on`` for `building only mdrun`_, e.g. for compute cluster back-end nodes
 * ``-DGMX_DOUBLE=on`` to build |Gromacs| in double precision (slower, and not normally useful)
@@ -99,25 +99,25 @@ Platform
 |Gromacs| can be compiled for many operating systems and
 architectures.  These include any distribution of Linux, Mac OS X or
 Windows, and architectures including x86, AMD64/x86-64, several
-PowerPC including POWER8, ARM v7, ARM v8, and SPARC VIII.
+PowerPC including POWER8, ARM v8, and SPARC VIII.
 
 Compiler
 ^^^^^^^^
 
-|Gromacs| can be compiled on any platform with ANSI C99 and C++14
+|Gromacs| can be compiled on any platform with ANSI C99 and C++17
 compilers, and their respective standard C/C++ libraries. Good
 performance on an OS and architecture requires choosing a good
 compiler. We recommend gcc, because it is free, widely available and
 frequently provides the best performance.
 
 You should strive to use the most recent version of your
-compiler. Since we require full C++14 support the minimum supported
+compiler. Since we require full C++17 support the minimum supported
 compiler versions are
 
-* GNU (gcc) 5.1
-* Intel (icc) 17.0.1
-* LLVM (clang) 3.6
-* Microsoft (MSVC) 2017
+* GNU (gcc/libstdc++) 7
+* Intel (icc) 19.1
+* LLVM (clang/libc++) 5
+* Microsoft (MSVC) 2017 15.7
 
 Other compilers may work (Cray, Pathscale, older clang) but do
 not offer competitive performance. We recommend against PGI because
@@ -131,7 +131,7 @@ You may also need the most recent version of other compiler toolchain
 components beside the compiler itself (e.g. assembler or linker);
 these are often shipped by your OS distribution's binutils package.
 
-C++14 support requires adequate support in both the compiler and the
+C++17 support requires adequate support in both the compiler and the
 C++ library. The gcc and MSVC compilers include their own standard
 libraries and require no further configuration. If your vendor's
 compiler also manages the standard library library via compiler flags,
@@ -139,12 +139,12 @@ these will be honored. For configuration of other compilers, read on.
 
 On Linux, both the Intel and clang compiler use the libstdc++ which
 comes with gcc as the default C++ library. For |Gromacs|, we require
-the compiler to support libstc++ version 5.1 or higher. To select a
+the compiler to support libstc++ version 7.1 or higher. To select a
 particular libstdc++ library, provide the path to g++ with
 ``-DGMX_GPLUSPLUS_PATH=/path/to/g++``.
 
 On Windows with the Intel compiler, the MSVC standard library is used,
-and at least MSVC 2017 is required. Load the enviroment variables with
+and at least MSVC 2017 15.7 is required. Load the enviroment variables with
 vcvarsall.bat.
 
 To build with clang and llvm's libcxx standard library, use
@@ -301,7 +301,7 @@ should also add ``--enable-avx2`` also. On Intel processors supporting
 512-wide AVX, including KNL, add ``--enable-avx512`` also.
 FFTW will create a fat library with codelets for all different instruction sets,
 and pick the fastest supported one at runtime.
-On ARM architectures with NEON SIMD support and IBM Power8 and later, you
+On ARM architectures with SIMD support and IBM Power8 and later, you
 definitely want version 3.3.5 or later,
 and to compile it with ``--enable-neon`` and ``--enable-vsx``, respectively, for
 SIMD support. If you are using a Cray, there is a special modified
@@ -348,7 +348,9 @@ Other optional build components
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 * Run-time detection of hardware capabilities can be improved by
-  linking with hwloc, which is automatically enabled if detected.
+  linking with hwloc. By default this is turned off since it might
+  not be supported everywhere, but if you have hwloc installed it
+  should work by just setting ``-DGMX_HWLOC=ON``
 * Hardware-optimized BLAS and LAPACK libraries are useful
   for a few of the |Gromacs| utilities focused on normal modes and
   matrix manipulation, but they do not provide any benefits for normal
@@ -371,7 +373,7 @@ Other optional build components
   ``-DGMX_USE_LMFIT=none``.
 * zlib is used by TNG for compressing some kinds of trajectory data
 * Building the |Gromacs| documentation is optional, and requires
-  ImageMagick, pdflatex, bibtex, doxygen, python 3.5, sphinx
+  ImageMagick, pdflatex, bibtex, doxygen, python 3.6, sphinx
   |EXPECTED_SPHINX_VERSION|, and pygments.
 * The |Gromacs| utility programs often write data files in formats
   suitable for the Grace plotting tool, but it is straightforward to
@@ -500,7 +502,7 @@ For example, the following command line
 
 ::
 
-    cmake .. -DGMX_GPU=ON -DGMX_MPI=ON -DCMAKE_INSTALL_PREFIX=/home/marydoe/programs
+    cmake .. -DGMX_GPU=CUDA -DGMX_MPI=ON -DCMAKE_INSTALL_PREFIX=/home/marydoe/programs
 
 can be used to build with CUDA GPUs, MPI and install in a custom
 location. You can even save that in a shell script to make it even
@@ -573,6 +575,10 @@ lead to performance loss, e.g. on Intel Skylake-X/SP and AMD Zen.
 12. ``IBM_VSX`` Power7, Power8, Power9 and later have this.
 13. ``ARM_NEON`` 32-bit ARMv7 with NEON support.
 14. ``ARM_NEON_ASIMD`` 64-bit ARMv8 and later.
+15. ``ARM_SVE`` 64-bit ARMv8 and later with the Scalable Vector Extensions (SVE).
+    The SVE vector length is fixed at CMake configure time. The default vector
+    length is 512 bits, and this can be changed via the ``GMX_SIMD_ARM_SVE_LENGTH``
+    CMake variable.
 
 The CMake configure system will check that the compiler you have
 chosen can target the architecture you have chosen. mdrun will check
@@ -648,7 +654,7 @@ If you have the CUDA_ Toolkit installed, you can use ``cmake`` with:
 
 ::
 
-    cmake .. -DGMX_GPU=ON -DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda
+    cmake .. -DGMX_GPU=CUDA -DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda
 
 (or whichever path has your installation). In some cases, you might
 need to specify manually which of your C++ compilers should be used,
@@ -669,7 +675,7 @@ manual.
 
 The GPU acceleration has been tested on AMD64/x86-64 platforms with
 Linux, Mac OS X and Windows operating systems, but Linux is the
-best-tested and supported of these. Linux running on POWER 8, ARM v7 and v8
+best-tested and supported of these. Linux running on POWER 8 and ARM v8
 CPUs also works well.
 
 Experimental support is available for compiling CUDA code, both for host and
@@ -723,7 +729,7 @@ To trigger an OpenCL_ build the following CMake flags must be set
 
 ::
 
-    cmake .. -DGMX_GPU=ON -DGMX_USE_OPENCL=ON
+    cmake .. -DGMX_GPU=OpenCL
 
 To build with support for Intel integrated GPUs, it is required
 to add ``-DGMX_OPENCL_NB_CLUSTER_SIZE=4`` to the cmake command line,
@@ -741,7 +747,7 @@ external library, use
 
 ::
 
-    cmake .. -DGMX_GPU=ON -DGMX_USE_OPENCL=ON -DclFFT_ROOT_DIR=/path/to/your/clFFT -DGMX_EXTERNAL_CLFFT=TRUE
+    cmake .. -DGMX_GPU=OpenCL -DclFFT_ROOT_DIR=/path/to/your/clFFT -DGMX_EXTERNAL_CLFFT=TRUE
 
 Static linking
 ~~~~~~~~~~~~~~
@@ -802,8 +808,9 @@ of the build host machine or otherwise specified to ``cmake`` during
 configuration.
 
 Often it is possible to ensure portability by choosing the least
-common denominator of SIMD support, e.g. SSE2 for x86, and ensuring
-the you use ``cmake -DGMX_USE_RDTSCP=off`` if any of the target CPU
+common denominator of SIMD support, e.g. SSE2 for x86. In rare cases
+of very old x86 machines, ensure that
+you use ``cmake -DGMX_USE_RDTSCP=off`` if any of the target CPU
 architectures does not support the ``RDTSCP`` instruction.  However, we
 discourage attempts to use a single |Gromacs| installation when the
 execution environment is heterogeneous, such as a mix of AVX and
@@ -1071,7 +1078,7 @@ directory:
     cd ..
     mkdir build-mdrun-only
     cd build-mdrun-only
-    cmake .. -DGMX_MPI=ON -DGMX_GPU=ON -DGMX_BUILD_MDRUN_ONLY=ON -DCMAKE_INSTALL_PREFIX=/your/installation/prefix/here
+    cmake .. -DGMX_MPI=ON -DGMX_GPU=CUDA -DGMX_BUILD_MDRUN_ONLY=ON -DCMAKE_INSTALL_PREFIX=/your/installation/prefix/here
     make -j 4
     make install
     cd /to/your/unpacked/regressiontests
@@ -1289,19 +1296,20 @@ Tested platforms
 
 While it is our best belief that |Gromacs| will build and run pretty
 much everywhere, it is important that we tell you where we really know
-it works because we have tested it. We do test on Linux, Windows, and
-Mac with a range of compilers and libraries for a range of our
-configuration options. Every commit in our git source code repository
-is currently tested on x86 with a number of gcc versions ranging from 5.1
-through 9.1, version 19 of the Intel compiler, and Clang
-versions 3.6 through 8. For this, we use a variety of GNU/Linux
-flavors and versions as well as Windows (where we test only MSVC 2017).
+it works because we have tested it.
+Every commit in our git source code repository
+is currently tested with a range of configuration options on x86 with
+gcc versions 7 and 8,
+clang versions 8 and 9,
+and
+a beta version of oneAPI containing Intel's compiler.
+For this testing, we use Ubuntu 18.04 or 20.04 operating system.
 Other compiler, library, and OS versions are tested less frequently.
 For details, you can
 have a look at the `continuous integration server used by GROMACS`_,
-which runs Jenkins_.
+which uses GitLab runner on a local k8s x86 cluster with NVIDIA and
+AMD GPU support.
 
-We test irregularly on ARM v7, ARM v8, Cray, Fujitsu
-PRIMEHPC, Power8, Power9,
+We test irregularly on ARM v8, Cray, Power8, Power9,
 Google Native Client and other environments, and
 with other compilers and compiler versions, too.
index 6bcecd540a8f6fb83412c4672d7244d7cb53ba17..8f3e5d8d4407e3e67dca06607301f6abcb701cf6 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 85e7a691f4a936f658c945b92b9059adfefaabd3..c7587e78e0744230dfc0067a687f00570843f61f 100755 (executable)
@@ -583,7 +583,7 @@ MACRO(PARSE_ADD_LATEX_ARGUMENTS command)
   ENDIF (LATEX_DEFAULT_ARGS)
 
   IF (LATEX_DEFAULT_ARGS)
-    LATEX_USAGE(${command} "Invalid or depricated arguments: ${LATEX_DEFAULT_ARGS}")
+    LATEX_USAGE(${command} "Invalid or deprecated arguments: ${LATEX_DEFAULT_ARGS}")
   ENDIF (LATEX_DEFAULT_ARGS)
 
   # Backward compatibility between 1.6.0 and 1.6.1.
diff --git a/docs/nblib/guide-to-writing-MD-programs.rst b/docs/nblib/guide-to-writing-MD-programs.rst
new file mode 100644 (file)
index 0000000..2d6fd6a
--- /dev/null
@@ -0,0 +1,378 @@
+Guide to Writing MD Programs
+============================
+
+The goal of NB-LIB’s is to enable researchers to programmatically define molecular simulations.
+Traditionally these have been performed using a collection of executables and a manual workflow followed by a “black-box” simulation engine.
+NB-LIB allows users to script a variety of novel simulation and analysis workflows at a more granular level.
+
+Many possible use cases are facilitated by the flexibility that NB-LIB allows.
+These include customized update rules, defining custom forces, or orchestrating swarms of simulations.
+NB-LIB also allows for writing conventional MD simulations and analysis.
+
+This document goes over the steps to write MD programs using the API in NB-LIB that exposes features that are a part of the GROMACS package.
+
+Global Definitions
+------------------
+
+NB-LIB programs are written in C++ so its headers for I/O or advanced tasks must be included.
+In addition, one must include the headers for various capabilities and abstractions NB-LIB exposes as well.
+This can be directly copied from here.
+Finally, we use the namespace ``nblib`` for the data structures defined in the library.
+The last line in the block allows one to skip this specifier each time a function or a data structure is used.
+
+.. code:: cpp
+
+   #include <cstdio>
+
+   #include "nblib/box.h"
+   #include "nblib/forcecalculator.h"
+   #include "nblib/integrator.h"
+   #include "nblib/molecules.h"
+   #include "nblib/nbkerneloptions.h"
+   #include "nblib/particletype.h"
+   #include "nblib/simulationstate.h"
+   #include "nblib/topology.h"
+
+   using namespace nblib;
+
+Define Particle Data
+--------------------
+
+.. code:: cpp
+
+   // Parameters from a GROMOS compatible force-field 2016H66
+
+   struct OWaterAtom
+   {
+       ParticleName         name = "Ow";
+       Mass                 mass = 15.999;
+       C6                   c6   = 0.0026173456;
+       C12                  c12  = 2.634129e-06;
+   };
+
+   struct HwAtom
+   {
+       ParticleName         name = "Hw";
+       Mass                 mass = 1.00784;
+       C6                   c6   = 0.0;
+       C12                  c12  = 0.0;  
+   };
+
+   struct CMethAtom
+   {
+       ParticleName         name = "Cm";
+       Mass                 mass = 12.0107;
+       C6                   c6   = 0.01317904;
+       C12                  c12  = 34.363044e-06;
+   };
+
+   struct HcAtom
+   {
+       ParticleName         name = "Hc";
+       Mass                 mass = 1.00784;
+       C6                   c6   = 8.464e-05;
+       C12                  c12  = 15.129e-09;  
+   };
+
+There can be as many structs of this kind as there are particle types in the system.
+Organizing the data like this is not strictly necessary, but is shown for the purpose of clarity.
+As shown here, there can be multiple particles that correspond to a single element as atomic mass can vary by molecular context.
+For example, the carbon atom in a carboxyl group would have different parameters from one in the methyl group.
+We can obtain the parameter set from any standard force-field, or generate new parameters to study new compounds or force fields.
+This example comes from the `2016H66 Parameter Set <https://pubs.acs.org/doi/10.1021/acs.jctc.6b00187>`__.
+
+Defining Coordinates, Velocities and Force Buffers
+--------------------------------------------------
+
+.. code:: cpp
+
+   std::vector<gmx::RVec> coordinates = {
+       { 0.794, 1.439, 0.610 }, { 1.397, 0.673, 1.916 }, { 0.659, 1.080, 0.573 },
+       { 1.105, 0.090, 3.431 }, { 1.741, 1.291, 3.432 }, { 1.936, 1.441, 5.873 },
+       { 0.960, 2.246, 1.659 }, { 0.382, 3.023, 2.793 }, { 0.053, 4.857, 4.242 },
+       { 2.655, 5.057, 2.211 }, { 4.114, 0.737, 0.614 }, { 5.977, 5.104, 5.217 },
+   };
+
+   std::vector<gmx::RVec> velocities = {
+       { 0.0055, -0.1400, 0.2127 }, { 0.0930, -0.0160, -0.0086 }, { 0.1678, 0.2476, -0.0660 },
+       { 0.1591, -0.0934, -0.0835 }, { -0.0317, 0.0573, 0.1453 }, { 0.0597, 0.0013, -0.0462 },
+       { 0.0484, -0.0357, 0.0168 }, { 0.0530, 0.0295, -0.2694 }, { -0.0550, -0.0896, 0.0494 },
+       { -0.0799, -0.2534, -0.0079 }, { 0.0436, -0.1557, 0.1849 }, { -0.0214, 0.0446, 0.0758},
+   };
+
+   std::vector<gmx::RVec> forces = {
+       { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+       { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+       { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+       { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 },
+   };
+
+We can initialize coordinates for our particles using ``std::vector`` of ``gmx::RVec`` which is a specific data type for holding 3D vector quantities. `Doxygen page on RVec here`__.
+
+  __ doxygen-ref-rvec_
+
+Writing the MD Program
+----------------------
+
+As with as any basic C++ program, there needs to be a ``main()`` function.
+
+
+Define ParticleTypes
+~~~~~~~~~~~~~~~~~~~~
+
+.. code:: cpp
+
+   int main()
+   {
+       // Bring the parameter structs to scope
+       OwAtom      owAtom;
+       HwAtom      hwAtom;
+       CMethAtom   cmethAtom;
+       HcAtom      hcAtom;
+     
+       // Create the particles
+       ParticleType Ow(owAtom.name, owAtom.mass);
+       ParticleType Hw(hwAtom.name, hwAtom.mass);
+       ParticleType Cm(cmethAtom.name, cmethAtom.mass);
+       ParticleType Hc(hcAtom.name, hcAtom.mass);
+
+As before, the helper struct to define ``ParticleType`` data is not strictly needed, but is shown for clarity.
+The line ``ParticleType CMethAtom(ParticleName("Cm"), Mass(12.0107));`` would be sufficient.
+
+Define Non-Bonded Interactions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: cpp
+
+   ParticleTypeInteractions interactions(CombinationRule::Geometric);
+
+   // add non-bonded interactions for the particle types
+   interactions.add(owAtom.name, owAtom.c6, owAtom.c12);
+   interactions.add(hwAtom.name, hwAtom.c6, hwAtom.c12);
+   interactions.add(cmethAtom.name, cmethAtom.c6, cmethAtom.c12);
+   interactions.add(hcAtom.name, hcAtom.c6, hcAtom.c12);
+
+For the Lennard-Jones interactions, we define a ``ParticleTypeInteractions`` object.
+Each particle of the ``ParticleType`` interacts with each other based on the ``C6`` and ``C12`` parameters.
+These parameters of the two different particles are averaged using ``Geometric`` or ``LorentzBerthelot`` ``CombinationRule``.
+More details `here <http://manual.gromacs.org/documentation/2019/reference-manual/functions/nonbonded-interactions.html#the-lennard-jones-interaction>`__.
+By default ``CombinationRule::Geometric`` is selected.
+
+We add the interaction parameters of each of the particle types into the ``ParticleTypeInteractions`` object.
+The result is a table that has interactions specified for all ``ParticleType`` pairs.
+The following matrix describes the pair-wise C6 parameter created using ``CombinationRule::Geometric``.
+
+== ====== === ======= =======
+#  Ow     Hw  Cm      Hc
+== ====== === ======= =======
+Ow 0.0026 0.0 0.42    4.7e-4
+Hw 0.0    0.0 0.0     0.0
+Cm 0.42   0.0 0.013   1.05e-3
+Hc 4.7e-4 0.0 1.05e-3 8.5e-5
+== ====== === ======= =======
+
+For a particular interaction pair, the user can also override the specified ``CombinationRule`` with custom parameters.
+The following overload would replace the parameters computed from a ``CombinationRule``  between ``Ow`` and ``Cm`` particle types.
+
+.. code:: cpp
+
+   interactions.add("Ow", "Cm", 0.42, 42e-6);
+
+To facilitate modular, reusable code, it is possible to combine multiple ``ParticleTypeInteractions`` objects.
+Assuming ``otherInteractions`` is defined, this can be done with ``interactions.merge(otherInteractions)``
+
+Define Molecules
+~~~~~~~~~~~~~~~~
+
+.. code:: cpp
+
+   Molecule water("Water");
+   Molecule methane("Methane");
+
+   water.addParticle(ParticleName("O"), Ow);
+   water.addParticle(ParticleName("H1"), Hw);
+   water.addParticle(ParticleName("H2"), Hw);
+
+   water.addExclusion("H1", "O");
+   water.addExclusion("H2", "O");
+
+   methane.addParticle(ParticleName("C"), Cm);
+   methane.addParticle(ParticleName("H1"), Hc);
+   methane.addParticle(ParticleName("H2"), Hc);
+   methane.addParticle(ParticleName("H3"), Hc);
+   methane.addParticle(ParticleName("H4"), Hc);
+
+   methane.addExclusion("H1", "C");
+   methane.addExclusion("H2", "C");
+   methane.addExclusion("H3", "C");
+   methane.addExclusion("H4", "C");
+
+We begin declaring molecules with their constituent particles.
+A string identifier must uniquely identify a specific particle within the molecule.
+It is also possible to define partial charges on each particle for the computation of Coulomb interactions.
+``water.addParticle(ParticleName("O"), Charge(-0.04), Ow);``
+
+Adding exclusions ensures that non-bonded interactions are only computed when necessary.
+For example, if two  particles share a bond, the potential energy of the bond makes the non-bonded term negligible.
+Particle self-exclusions are enabled by default.
+We use the unique identifiers specified during ``addParticle()`` for this and the listed interactions later.
+
+Define Listed Interactions
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Within a molecule, one can define interactions such as bonds, angles and dihedrals between the constituent particles.
+NB-LIB provides concrete implementations of several commonly used 2, 3 and 4 center interactions.
+
+.. code:: cpp
+
+   HarmonicBondType ohHarmonicBond(1, 1);
+   HarmonicBondType hcHarmonicBond(2, 1);
+
+   DefaultAngle hohAngle(Degrees(120), 1);
+   DefaultAngle hchAngle(Degrees(109.5), 1);
+
+   //add harmonic bonds for water
+   water.addInteraction("O", "H1", ohHarmonicBond);
+   water.addInteraction("O", "H2", ohHarmonicBond);
+
+   // add the angle for water
+   water.addInteraction("H1", "O", "H2", hohAngle);
+
+   // add harmonic bonds for methane
+   methane.addInteraction("H1", "C", hcHarmonicBond);
+   methane.addInteraction("H2", "C", hcHarmonicBond);
+   methane.addInteraction("H3", "C", hcHarmonicBond);
+   methane.addInteraction("H4", "C", hhcHarmonicBondc);
+
+   // add the angles for methane
+   methane.addInteraction("H1", "C", "H2", hchAngle);
+   methane.addInteraction("H1", "C", "H3", hchAngle);
+   methane.addInteraction("H1", "C", "H4", hchAngle);
+   methane.addInteraction("H2", "C", "H3", hchAngle);
+   methane.addInteraction("H2", "C", "H4", hchAngle);
+   methane.addInteraction("H3", "C", "H4", hchAngle);
+
+Define Options for the Simulation and Non-Bonded Calculations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: cpp
+
+   // Define a box for the simulation
+   Box box(6.05449);
+
+   // Define options for the non-bonded kernels
+   NBKernelOptions options;
+
+One can define the bounding box either with a single argument for a cube and 3 arguments to specify length, breadth and height separately.
+
+``NBKernelOptions`` contains a set of flags and configuration options for both hardware context and the relevant calculations for the simulation.
+The following table describes the possible options that can be set.
+
++----------------------+------+---------------------------------------+
+| Flag or Config       | Type | Implications                          |
+| Option               |      |                                       |
++======================+======+=======================================+
+| ``useGpu``           | Bool | Use GPU for non-bonded computations   |
+|                      | ean  |                                       |
++----------------------+------+---------------------------------------+
+| ``numThreads``       | Inte | Number of CPU threads to use          |
+|                      | ger  |                                       |
++----------------------+------+---------------------------------------+
+| ``nbnxmSimd``        | Enum | Kernel SIMD type                      |
+|                      |      | (``SimdAuto``/``SimdNo``/``Simd4XM``/ |
+|                      |      | ``Simd2XMM``)                         |
++----------------------+------+---------------------------------------+
+| ``ljCombination      | Enum | Lennard-Jones combination rule        |
+| Rule``               |      | (``Geometric``/``LorentzBerthelot``)  |
++----------------------+------+---------------------------------------+
+| ``useHalfLJOptimizat | Bool | Enable i-cluster half-LJ optimization |
+| ion``                | ean  |                                       |
++----------------------+------+---------------------------------------+
+| ``pairlistCutoff``   | Real | Specify pairlist and interaction      |
+|                      |      | cut-off                               |
++----------------------+------+---------------------------------------+
+| ``computeVirialAndEn | Bool | Enable energy computations            |
+| ergy``               | ean  |                                       |
++----------------------+------+---------------------------------------+
+| ``coulombType``      | Enum | Coulomb interaction function          |
+|                      |      | (``Pme``/``Cutoff``/``ReactionField`` |
+|                      |      | )                                     |
++----------------------+------+---------------------------------------+
+| ``useTabulatedEwaldC | Bool | Use tabulated PME grid correction     |
+| orr``                | ean  | instead of analytical                 |
++----------------------+------+---------------------------------------+
+| ``numIterations``    | Inte | Specify number of iterations for each |
+|                      | ger  | kernel                                |
++----------------------+------+---------------------------------------+
+| ``cyclesPerPair``    | Bool | Enable printing cycles/pair instead   |
+|                      | ean  | of pairs/cycle                        |
++----------------------+------+---------------------------------------+
+| ``timestep``         | Real | Specify the time step                 |
++----------------------+------+---------------------------------------+
+
+Define Topology and Simulation State
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We build the system topology using the ``TopologyBuilder`` class.
+We add the ``Molecule`` objects that we defined previously along with the ``ParticleTypesInteractions`` using its public functions.
+We get the actual ``Topology`` object complete with all exclusions, interaction maps and listed interaction data constructed based on the defined entities using the ``buildTopology()``\ function.
+
+.. code:: cpp
+
+   TopologyBuilder topologyBuilder;
+
+   // add molecules
+   topologyBuilder.addMolecule(water, 10);
+   topologyBuilder.addMolecule(methane, 10);
+
+   // add non-bonded interaction map
+   topologyBuilder.addParticleTypesInteractions(interactions);
+
+   Topology topology = topologyBuilder.buildTopology();
+
+We now have all we need to fully describe our system using the ``SimulationState`` object.
+This is built using the topology, the box, and the particle coordinates and velocities.
+This object serves as a snapshot of the system that can be used for analysis or to start simulations from known states.
+
+.. code:: cpp
+
+   SimulationState simulationState(coordinates, velocities, forces, box, topology);
+
+
+
+Writing the MD Loop
+~~~~~~~~~~~~~~~~~~~
+
+Now that we have fully described our system and the problem, we need two entities to write an MD loop.
+The first is the ``ForceCalculator`` and the second is an Integrator.
+NB-LIB comes with a ``LeapFrog`` integrator but it is also possible for users to write custom integrators.
+
+.. code:: cpp
+
+   // The force calculator contains all the data needed to compute forces
+   ForceCalculator forceCalculator(simulationState, options);
+
+   // Integration requires masses, positions, and forces
+   LeapFrog integrator(simulationState);
+
+   // Allocate a force buffer
+   gmx::ArrayRef<gmx::RVec> userForces(topology.numParticles());
+
+   // MD Loop
+   int numSteps = 100;
+
+   for (i = 0; i < numSteps; i++)
+   {
+     userForces = forceCalculator.compute();
+
+     // The forces are not automatically updated in case the user wants to add their own
+     std::copy(userForces.begin(), userForces.end(), begin(simulationState.forces()));
+
+     // Integrate with a time step of 1 fs
+     integrator.integrate(1.0);
+   }
+
+   return 0;
+   } // main
+
+.. _doxygen-ref-rvec: ../doxygen/html-lib/namespacegmx.xhtml#a139c1919a9680de4ad1450f42e37d33b
\ No newline at end of file
diff --git a/docs/nblib/index.rst b/docs/nblib/index.rst
new file mode 100644 (file)
index 0000000..c90fe05
--- /dev/null
@@ -0,0 +1,15 @@
+.. _nblib:
+
+=========
+NBLIB API
+=========
+
+This documentation is part of the `GROMACS manual <http://manual.gromacs.org/current/>`_
+and describes the *nblib* API.
+
+
+..  toctree::
+    :maxdepth: 1
+    :caption: Documentation sections
+
+    guide-to-writing-MD-programs.rst
index baa12db8311e9cc421e4e9d6a312b32ce49e069f..cf5f70f94ecfebf8e9de05a487224d685bf8fdac 100644 (file)
@@ -60,7 +60,17 @@ SETTLE
 For the special case of rigid
 water molecules, that often make up more than 80% of the simulation
 system we have implemented the SETTLE algorithm \ :ref:`47 <refMiyamoto92>`
-(sec. :ref:`constraintalg`).
+(sec. :ref:`constraintalg`). The implementation of SETTLE in |Gromacs|
+is a slight modification of the original algorithm, in that it completely
+avoids the calculation of the center of mass of the water molecule.
+Apart from saving a few operations, the main gain of this is a reduction
+in rouding errors. For large coordinates, the floating pointing precision of constrained
+distances is reduced, which leads to an energy drift which usually depends
+quadratically on the coordinate. For SETTLE this dependence is now linear, which enables
+accurate integration of systems in single precision up to 1000 nm in size.
+But note that the drift due to SHAKE and LINCS still has a quadratic
+dependence, which limits the size of systems with normal constraints
+in single precision to 100 to 200 nm.
 
 For velocity Verlet, an additional round of constraining must be done,
 to constrain the velocities of the second velocity half step, removing
index 7bc72dc8c4680f4095040b80b26e64b380fb47f3..193f1842040c8ae7a57a99dabb6a87c07d4fb5a5 100644 (file)
@@ -1255,6 +1255,57 @@ Berendsen pressure control algorithm yields a simulation with the
 correct average pressure, it does not yield the exact NPT ensemble, and
 it is not yet clear exactly what errors this approximation may yield.
 
+Stochastic cell rescaling
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The stochastic cell rescaling algorithm is a variant of the Berendsen
+algorithm that allows correct fluctuations to be sampled. Similarly
+to the Berendsen algorithm, it rescales the coordinates and box vectors
+every step, or every :math:`n_\mathrm{PC}` steps
+with the effect of a first-order kinetic relaxation of the
+pressure towards a given reference pressure :math:`P_0`.
+At variance with the Berendsen algorithm, the rescaling matrix is calculated
+including a stochastic term that makes volume fluctuations correct.
+
+The isotropic version can be easily written in term of the strain
+:math:`\epsilon=\log(V/V_0)` that is evolved according to the following equation
+of motion
+
+.. math:: \mbox{d}\epsilon=-\frac{\beta}{\tau_p}(P_0-P)\mbox{d}t + \sqrt{\frac{2k_BT\beta}{V\tau_p}}\mbox{d}W
+          :label: eqnstochasticcellrescaling
+
+
+Here, :math:`\beta` is the isothermal compressibility of the system.
+It suffices to take a rough estimate because the value of :math:`\beta` only influences
+the non-critical time constant of the pressure relaxation without affecting
+the volume distribution itself. For water at 1 atm and 300 K
+:math:`\beta = 4.6 \times 10^{-10}`
+Pa\ :math:`^{-1} = 4.6 \times 10^{-5}` bar\ :math:`^{-1}`, which is
+:math:`7.6 \times 10^{-4}` MD units (see chapter :ref:`defunits`). Most
+other liquids have similar values.
+
+Another difference with respect to the Berendsen algorithm is that
+velocities are scaled with a factor that is the reciprocal of the
+scaling factor for positions.
+
+A semi-isotropic implementation is also provided. By defining the variables
+:math:`\epsilon_{xy}=\log(A/A_0)` and :math:`\epsilon_z=\log(L/L_0)`,
+where :math:`A` and :math:`L` are the area of the simulation box in the
+:math:`xy` plane and its height, respectively, the following equations
+can be obtained:
+
+.. math:: \mbox{d}\epsilon_{xy}=-\frac{2\beta}{3\tau_p}(P_0-\frac{\gamma}{L}-\frac{P_{xx}+P_{yy}}{2})\mbox{d}t+\sqrt{\frac{4k_BT\beta}{3V\tau_p}}\mbox{d}W_{xy}
+          :label: eqnstochasticcellrescalingxy
+
+.. math:: \mbox{d}\epsilon_z=-\frac{\beta}{3\tau_p}(P_0-P_{zz})\mbox{d}t + \sqrt{\frac{2k_BT\beta}{3V\tau_p}}\mbox{d}W_z
+          :label: eqnstochasticcellrescalingz
+
+Here :math:`\gamma` is the external surface tension and :math:`P_{xx}`,
+:math:`P_{yy}`, and :math:`P_{zz}` the components of the internal pressure.
+
+More detailed explanations can be found in the original reference \ :ref:`184 <refBernetti2020>`.
+
+
 Parrinello-Rahman pressure coupling
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
index 5567d3a7e45828950bedc61b6c43e27f16dab7fd..28ecdc9347c324306c6c2ae97c6cb8bc62239ac4 100644 (file)
@@ -1,7 +1,7 @@
 File formats
 ============
 
-.. TODO in future patch: update for accuracy, organize better, improve formatting
+.. todo:: in future patch: update for accuracy, organize better, improve formatting
 
 Summary of file formats
 ^^^^^^^^^^^^^^^^^^^^^^^
index 25820ca0d1395f21cf015efdf859c50191915122..4d88abfb0722eaaa8827f6e5ea96c9f5a8f01104 100644 (file)
@@ -197,6 +197,20 @@ below. Constructing atoms in virtual sites can be virtual sites
 themselves, but only if they are higher in the list, i.e. virtual sites
 can be constructed from “particles” that are simpler virtual sites.
 
+-  On top of an atom. This allows giving an atom multiple atom types and
+   with that also assigned multiple, different bonded interactions. This
+   can espically be of use in free-energy calculations.
+
+-  The coordinates of the virtual site equal that of the constructing atom:
+
+   .. math:: \mathbf{r}_s ~=~ \mathbf{r}_i
+             :label: eqnvsite1
+
+-  The force is moved to the constructing atom:
+
+   .. math:: \mathbf{F}_i ~=~ \mathbf{F}_{s}
+             :label: eqnvsite1force
+
 -  As a linear combination of two atoms
    (:numref:`Fig. %s <fig-vsites>` 2):
 
index 44ac3bbf1c583576c11176af31593b5011b7af9b..6ef673093a782b74f98ea619d3b3d34ec5211c88 100644 (file)
@@ -6,7 +6,7 @@ Reference Manual
 
 .. highlight:: bash
 
-.. TODO this needs to be carefully checked that I didn't mess anything up too bad
+.. todo:: this needs to be carefully checked that I didn't mess anything up too bad
 
 .. ifconfig:: gmx_image_convert == 'possible'
 
index 66dd6b5982eb44ebc99d3a59ed5acbd16b50ebca..1fc4bcc6e126d92086585ef35f95670c46f27c9c 100644 (file)
@@ -38,7 +38,7 @@ which in some cases might mean the information is not entirely correct.
 Comments on form and content are welcome, please send them to one of
 the mailing lists (see our `webpage`_ or this section on
 how to :ref:`contribute <gmx-contribute>`), or open an issue
-on our `redmine`_. Corrections can also be made in the |Gromacs| git
+on our `issue tracker`_. Corrections can also be made in the |Gromacs| git
 source repository and uploaded to the |Gromacs| `gerrit`_.
 
 We release an updated version of the manual whenever
@@ -49,7 +49,7 @@ minor release number as your |Gromacs| installation.
 Citation information
 --------------------
 
-.. TODO needs link to ref list
+.. todo:: needs link to ref list
 
 |GMX_MANUAL_DOI_STRING|
 
index b7b4593499fda09a5a7a23a2fb9c981731346719..eb9099d164c147512c505e9aa96788c945ee2474 100644 (file)
@@ -2590,6 +2590,19 @@ structures into cryoelectron microscopy maps using biased molecular dynamics sim
 :sup:`183` Igaev, M., Kutzner, C., Bock, L. V., Vaiana, A. C., & Grubmüller, H.,
 "Automated cryo-EM structure refinement using correlation-driven molecular dynamics", *eLife*, **8**, e43542 (2019).
 
+.. raw:: html
+
+   </div>
+
+.. raw:: html
+
+   <div id="ref-Bernetti2020">
+
+.. _refBernetti2020:
+
+:sup:`184` Bernetti, M. and Bussi, G.,
+"Pressure control using stochastic cell rescaling", *J. Chem. Phys.*, **153**, 114107 (2020).
+
 .. raw:: html
 
    </div>
index 1a76aa321b750875d94565947171613a4c7854b1..faa9f4197900011b55c3b95d91407b26efdaea0c 100644 (file)
@@ -20,22 +20,9 @@ Basics of the method
 ^^^^^^^^^^^^^^^^^^^^
 
 Rather than biasing the reaction coordinate :math:`\xi(x)` directly, AWH
-acts on a *reference coordinate* :math:`\lambda`. The reaction
-coordinate :math:`\xi(x)` is coupled to :math:`\lambda` with a harmonic
-potential
-
-.. math:: Q(\xi,\lambda) = \frac{1}{2} \beta k (\xi - \lambda)^2,
-          :label: eqnawhbasic
-
-so that for large force constants :math:`k`,
-:math:`\xi \approx \lambda`. Note the use of dimensionless energies for
-compatibility with previously published work. Units of energy are
-obtained by multiplication with :math:`k_BT=1/\beta`. In the simulation,
-:math:`\lambda` samples the user-defined sampling interval :math:`I`.
-For a multidimensional reaction coordinate :math:`\xi`, the sampling
-interval is the Cartesian product :math:`I=\Pi_{\mu} I_{\mu}` (a rectangular
-domain). The connection between atom coordinates and :math:`\lambda` is
-established through the extended ensemble \ :ref:`68 <refLyubartsev1992>`,
+acts on a *reference coordinate* :math:`\lambda`. The fundamentals of the
+method is based on the connection between atom coordinates and :math:`\lambda` and
+is established through the extended ensemble \ :ref:`68 <refLyubartsev1992>`,
 
 .. math:: P(x,\lambda) = \frac{1}{\mathcal{Z}}e^{g(\lambda) - Q(\xi(x),\lambda) - V(x)},
           :label: eqawhpxlambda
@@ -56,6 +43,18 @@ where :math:`F(\lambda)` is the free energy
 .. math:: F(\lambda) = -\ln \int e^{- Q(\xi(x),\lambda) - V(x)}  dx.
           :label: eqawhflambda
 
+The reaction coordinate :math:`\xi(x)` is commonly coupled to
+:math:`\lambda` with a harmonic potential
+
+.. math:: Q(\xi,\lambda) = \frac{1}{2} \beta k (\xi - \lambda)^2,
+          :label: eqnawhbasic
+
+so that for large force constants :math:`k`,
+:math:`\xi \approx \lambda`. Note the use of dimensionless energies for
+compatibility with previously published work. Units of energy are
+obtained by multiplication with :math:`k_BT=1/\beta`. In the simulation,
+:math:`\lambda` samples the user-defined sampling interval :math:`I`.
+
 Being the convolution of the PMF with the Gaussian defined by the
 harmonic potential, :math:`F(\lambda)` is a smoothened version of the
 PMF. :eq:`Eq. %s <eqawhplambda>` shows that in order to obtain
@@ -64,6 +63,17 @@ determined accurately. Thus, AWH adaptively calculates
 :math:`F(\lambda)` and simultaneously converges :math:`P(\lambda)`
 toward :math:`\rho(\lambda)`.
 
+It is also possible to directly control the :math:`\lambda` state
+of, e.g., alchemical free energy perturbations. In that case there is no harmonic
+potential and :math:`\lambda` changes in discrete steps along the reaction coordinate
+depending on the biased free energy difference between the :math:`\lambda` states.
+N.b., it is not yet possible to use AWH in combination with perturbed masses or
+constraints.
+
+For a multidimensional reaction coordinate :math:`\xi`, the sampling
+interval is the Cartesian product :math:`I=\Pi_{\mu} I_{\mu}` (a rectangular
+domain).
+
 The free energy update
 ^^^^^^^^^^^^^^^^^^^^^^
 
@@ -126,15 +136,23 @@ implies :math:`\Delta g_n(\lambda) < 0` (assuming
 Secondly, the normalization of the histogram
 :math:`N_n=\sum_\lambda W_n(\lambda)`, determines the update size
 :math:`| \Delta F(\lambda) |`. For instance, for a single sample
-:math:`\omega(\lambda|x)`, the shape of the update is approximately a
+:math:`\omega(\lambda|x)`, and using a harmonic potential
+(:see :eq:`Eq. %s <eqnawhbasic>`),
+the shape of the update is approximately a
 Gaussian function of width :math:`\sigma=1/\sqrt{\beta k}` and height
 :math:`\propto 1/N_n` :ref:`137 <reflindahl2014accelerated>`,
 
 .. math:: | \Delta F_n(\lambda) | \propto \frac{1}{N_n} e^{-\frac{1}{2} \beta k (\xi(x) - \lambda)^2}.
           :label: eqawhdfsize
 
-Therefore, as samples accumulate in :math:`W(\lambda)` and :math:`N_n`
-grows, the updates get smaller, allowing for the free energy to
+When directly controlling the lambda state of the system, the shape of
+the update is instead
+
+.. math:: | \Delta F_n(\lambda) | \propto \frac{1}{N_n} P_n(\lambda | x).
+          :label: eqawhdfsizelambda
+
+Therefore, in both cases, as samples accumulate in :math:`W(\lambda)` and
+:math:`N_n` grows, the updates get smaller, allowing for the free energy to
 converge.
 
 Note that quantity of interest to the user is not :math:`F(\lambda)` but
@@ -165,6 +183,13 @@ different in the two cases. This choice does not affect the internals of
 the AWH algorithm, only what force and potential AWH returns to the MD
 engine.
 
+Along a bias dimension directly controlling the
+:math:`\lambda` state of the system, such as when controlling free energy
+perturbations, the Monte-Carlo sampling alternative is always used, even if
+a convolved bias potential is chosen to be used along the other dimensions
+(if there are more than one).
+
+
 .. _fig-awhbiasevolution1:
 
 .. figure:: plots/awh-traj.*
index e442dfad00bf89bf3211d02ac1cfc8b0e8c53955..ac58be33a0a0386b69c32318379ca308f86d25a8 100644 (file)
@@ -202,6 +202,26 @@ the force constant is increased by multiplying by :math:`1+2\delta t_{\mathrm{de
 Note that adaptive force scaling does not conserve energy and will ultimately lead to very high
 forces when similarity cannot be increased further.
 
+Mapping input structure to density data with affine transformations
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+To align input structure and density data, a transformation matrix
+:math:`\mathbf{A}` and shift vector :math:`\mathbf{v_{\mathrm{shift}}}` may be
+defined that transform the input structure atom coordinates before evaluating
+density-guided-simulation energies and forces, so that 
+
+.. math:: U = U_{\mathrm{forcefield}}(\mathbf{r}) - k S[\rho^{\mathrm{ref}},\rho^{\mathrm{sim}}\!(\mathbf{A} \mathbf{r}+\mathbf{v}_{\mathrm{shift}})]\,\mathrm{.}
+          :label: eqndensnine
+
+.. math:: \mathbf{F}_{\mathrm{density}} = k \nabla_{\mathbf{r}} S[\rho^{\mathrm{ref}},\rho^{\mathrm{sim}}\!(\mathbf{A} \mathbf{r}+\mathbf{v}_{\mathrm{shift}})]\,\mathrm{.}
+          :label: eqndensten
+
+Affine transformations may be used, amongst other things, to perform
+
+ * rotations, e.g., around the z-axis by an angle :math:`\theta` by using :math:`A=\begin{pmatrix} \cos \theta & -\sin \theta & 0 \\ \sin \theta & \cos \theta & 0 \\ 0 & 0 & 1 \end{pmatrix}`.
+ * projection, e.g., onto the z-axis by using :math:`A=\begin{pmatrix} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 1 \end{pmatrix}`. This allows density-guided simulations to be steered by a density-profile along this axis.
+ * scaling the structure against the density by a factor :math:`s` by using :math:`A=\begin{pmatrix} s & 0 & 0 \\ 0 & s & 0 \\ 0 & 0 & s \end{pmatrix}`. This proves useful when, e.g., voxel-sizes in cryo-EM densities have to be adjusted.
+ * and arbitrary combinations of these by matrix multiplications (note that matrix multiplications are not commutative).
+
 Future developments
 ^^^^^^^^^^^^^^^^^^^
 
index 9b322bb5b8da1056944909c0e0b5b79784ca09b8..a7f8a2a28f9087e809b1926314febe038b3f3e7f 100644 (file)
@@ -87,157 +87,4 @@ subsystems are supported in this version:
 Usage
 ^^^^^
 
-To make use of the QM/MM functionality in |Gromacs|, one needs to:
-
-#. introduce link atoms at the QM/MM boundary, if needed;
-
-#. specify which atoms are to be treated at a QM level;
-
-#. specify the QM level, basis set, type of QM/MM interface and so on.
-
-Adding link atoms
-^^^^^^^^^^^^^^^^^
-
-At the bond that connects the QM and MM subsystems, a link atoms is
-introduced. In |Gromacs| the link atom has special atomtype, called LA.
-This atomtype is treated as a hydrogen atom in the QM calculation, and
-as a virtual site in the force-field calculation. The link atoms, if
-any, are part of the system, but have no interaction with any other
-atom, except that the QM force working on it is distributed over the two
-atoms of the bond. In the topology, the link atom (LA), therefore, is
-defined as a virtual site atom:
-
-::
-
-    [ virtual_sites2 ]
-    LA QMatom MMatom 1 0.65
-
-See sec. :ref:`vsitetop` for more details on how virtual sites are
-treated. The link atom is replaced at every step of the simulation.
-
-In addition, the bond itself is replaced by a constraint:
-
-::
-
-    [ constraints ]
-    QMatom MMatom 2 0.153
-
-**Note** that, because in our system the QM/MM bond is a carbon-carbon
-bond (0.153 nm), we use a constraint length of 0.153 nm, and dummy
-position of 0.65. The latter is the ratio between the ideal C-H bond
-length and the ideal C-C bond length. With this ratio, the link atom is
-always 0.1 nm away from the ``QMatom``, consistent with the carbon-hydrogen
-bond length. If the QM and MM subsystems are connected by a different
-kind of bond, a different constraint and a different dummy position,
-appropriate for that bond type, are required.
-
-Specifying the QM atoms
-^^^^^^^^^^^^^^^^^^^^^^^
-
-Atoms that should be treated at a QM level of theory, including the link
-atoms, are added to the index file. In addition, the chemical bonds
-between the atoms in the QM region are to be defined as connect bonds
-(bond type 5) in the topology file:
-
-::
-
-    [ bonds ]
-    QMatom1 QMatom2 5
-    QMatom2 QMatom3 5
-
-Specifying the QM/MM simulation parameters
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-In the :ref:`mdp` file, the following parameters control a
-QM/MM simulation.
-
-``QMMM = no``
-    | If this is set to ``yes``, a QM/MM simulation is
-      requested. Several groups of atoms can be described at different
-      QM levels separately. These are specified in the QMMM-grps field
-      separated by spaces. The level of *ab initio* theory at which the
-      groups are described is specified by ``QMmethod`` and
-      ``QMbasis`` Fields. Describing the groups at different
-      levels of theory is only possible with the ONIOM QM/MM scheme,
-      specified by ``QMMMscheme``.
-
-``QMMM-grps =``
-    | groups to be described at the QM level
-
-``QMMMscheme = normal``
-    | Options are ``normal`` and ``ONIOM``. This
-      selects the QM/MM interface. ``normal`` implies that
-      the QM subsystem is electronically embedded in the MM subsystem.
-      There can only be one ``QMMM-grps`` that is modeled at
-      the ``QMmethod`` and ``QMbasis`` level of
-      * ab initio* theory. The rest of the system is described at the MM
-      level. The QM and MM subsystems interact as follows: MM point
-      charges are included in the QM one-electron Hamiltonian and all
-      Lennard-Jones interactions are described at the MM level. If
-      ``ONIOM`` is selected, the interaction between the
-      subsystem is described using the ONIOM method by Morokuma and
-      co-workers. There can be more than one QMMM-grps each modeled at a
-      different level of QM theory (QMmethod and QMbasis).
-
-``QMmethod =``
-    | Method used to compute the energy and gradients on the QM atoms.
-      Available methods are AM1, PM3, RHF, UHF, DFT, B3LYP, MP2, CASSCF,
-      MMVB and CPMD. For CASSCF, the number of electrons and orbitals
-      included in the active space is specified by
-      ``CASelectrons`` and ``CASorbitals``. For
-      CPMD, the plane-wave cut-off is specified by the
-      ``planewavecutoff`` keyword.
-
-``QMbasis =``
-    | Gaussian basis set used to expand the electronic wave-function.
-      Only Gaussian basis sets are currently available, i.e. STO-3G,
-      3-21G, 3-21G\*, 3-21+G\*, 6-21G, 6-31G, 6-31G\*, 6-31+G\*, and
-      6-311G. For CPMD, which uses plane wave expansion rather than
-      atom-centered basis functions, the ``planewavecutoff``
-      keyword controls the plane wave expansion.
-
-``QMcharge =``
-    | The total charge in *e* of the ``QMMM-grps``. In case
-      there are more than one ``QMMM-grps``, the total
-      charge of each ONIOM layer needs to be specified separately.
-
-``QMmult =``
-    | The multiplicity of the ``QMMM-grps``. In case there
-      are more than one ``QMMM-grps``, the multiplicity of
-      each ONIOM layer needs to be specified separately.
-
-``CASorbitals =``
-    | The number of orbitals to be included in the active space when
-      doing a CASSCF computation.
-
-``CASelectrons =``
-    | The number of electrons to be included in the active space when
-      doing a CASSCF computation.
-
-``SH = no``
-    | If this is set to yes, a QM/MM MD simulation on the excited
-      state-potential energy surface and enforce a diabatic hop to the
-      ground-state when the system hits the conical intersection
-      hyperline in the course the simulation. This option only works in
-      combination with the CASSCF method.
-
-Output
-^^^^^^
-
-The energies and gradients computed in the QM calculation are added to
-those computed by |Gromacs|. In the :ref:`edr` file there is a
-section for the total QM energy.
-
-Future developments
-^^^^^^^^^^^^^^^^^^^
-
-Several features are currently under development to increase the
-accuracy of the QM/MM interface. One useful feature is the use of
-delocalized MM charges in the QM computations. The most important
-benefit of using such smeared-out charges is that the Coulombic
-potential has a finite value at interatomic distances. In the point
-charge representation, the partially-charged MM atoms close to the QM
-region tend to “over-polarize” the QM system, which leads to artifacts
-in the calculation.
-
-What is needed as well is a transition state optimizer.
+QMMM is currently not supported in GROMACS. 
index 64e252520968ddb67b7d2e1ba48a4a5ed4885351..d79603c6708459dc16da65e7cecbc1d851ca7399 100644 (file)
@@ -160,7 +160,7 @@ These document fixes for issues that were identified as having been
 introduced into the release-2016 branch since it diverged from
 release-5-1. These will not appear in the final release notes, because
 no formal release is thought to have had the problem. Of course, the
-Redmine issues remain available should further discussion arise.
+tracked `issues`_ remain available should further discussion arise.
 
 Fixed bug in v-rescale thermostat & replica exchange
 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
index fb2383bdc3e6397e37d898eeae05790d5aca61e7..ab3a576ffff4465928f4310e2fc990e2c7887862 100644 (file)
@@ -47,7 +47,7 @@ the reference to the three term dihedral to an older paper.
 Updated many aspects of the documentation
 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 Imported and updated more material from the wiki. Incorporated
-suggestions arising from many Redmine issues. Updated user guide,
+suggestions arising from many tracked issues. Updated user guide,
 developer guide, install guide, and reference manual.
 
 Updated mdrun signal help text
index 0f867ce1c061ca3a00ef45876e5ae26780416084..aeb6a5515b01f765221e9adcb304932ef323ba3a 100644 (file)
@@ -10,7 +10,7 @@ in the :ref:`release-notes`.
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Fixes where mdrun could behave incorrectly
index 80f501cb4da12cb1fc7f5be07f77511217b231d7..8338de59b997794f0721944c3922d28c1d01816e 100644 (file)
@@ -10,7 +10,7 @@ in the :ref:`release-notes`.
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Fixes where mdrun could behave incorrectly
index 195be46ebcd45f3c144c797215cec4d649b08028..aa7922b03d027ee9dcd3b12a843f722200774639 100644 (file)
@@ -10,7 +10,7 @@ in the :ref:`release-notes`.
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Fixes where mdrun could behave incorrectly
index f526dc3ee1ecd0aad6c83978865e9c0eb196fa4b..7fd579048d736ae51cc7d293a8830618d45ea39e 100644 (file)
@@ -10,7 +10,7 @@ in the :ref:`release-notes`.
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Fixes where mdrun could behave incorrectly
index bf676ce57fabaa607f3c24289694f4cc0f4d05a6..14e2d7eaade73bd84e7d98d6bced9b7c87d061c2 100644 (file)
@@ -10,7 +10,7 @@ in the :ref:`release-notes`.
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Fixes where mdrun could behave incorrectly
index 534d10a0b2c2d385d0ecd466c18cf0aa8be10677..a29b34aba8e2d0247dff6c6210c92d5acf429ea7 100644 (file)
@@ -10,7 +10,7 @@ in the :ref:`release-notes`.
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Fixes where mdrun could behave incorrectly
index 7120dbaa462804dfaa3c09ebd8f976cc04e0ee02..0ef68802565ab2d303fca4810ccad4a8fe553b1e 100644 (file)
@@ -10,7 +10,7 @@ in the :ref:`release-notes`.
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Fixes where mdrun could behave incorrectly
index c9ccef9586dc0f4c8d71008fc4de4a69dd5c3803..b4895cd207a1999ace7aa57cd287b93138f69d61 100644 (file)
@@ -4,7 +4,7 @@ Bugs fixed
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 gmx mdrun -append now requires that a checkpoint is found
index d0e8b3ac726f90507366e892679366329c15a2a3..7ffeaea3abd1e4a0c9ca158eda2f31e6715caed7 100644 (file)
@@ -3,7 +3,7 @@
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Changes anticipated to |Gromacs| 2020 functionality
index 67f24732857b54140142b0a2ade0aecb6cb919ef..81257d8fc4ce75bbfb1e7299d3e42b2ac8e5aa83 100644 (file)
@@ -4,7 +4,7 @@ New and improved features
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Density-guided simulations
index 316064f872b8ab870459f95df457a620fecc858b..757e9314d9269a56ba3d2f4d5ba17ee0efd910ac 100644 (file)
@@ -26,5 +26,5 @@ simulations and hardware. The new features are:
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
index ace0f701ed0969d932b89ac0e01a191631565152..3768cd75858a1512922d524173a8c97d11be25cf 100644 (file)
@@ -4,7 +4,7 @@ Miscellaneous
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 grompp now warns if macros in mdp "define" field are unused in topology
index 426cc2b25e8381399ae2c3ae56ee3e114c662657..4418db84070f78513c72172e4e5f63b6806aa3c9 100644 (file)
@@ -4,7 +4,7 @@ Performance improvements
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Up to a factor 2.5 speed-up of the non-bonded free-energy kernel
index 4186794e8f08888e329ccba8a78265652658c8ed..36577cbd830c01a85ba757bf5c6acc965dcb79f5 100644 (file)
@@ -4,7 +4,7 @@ Portability
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Added support for Hygon Dhyana CPU architecture
index a3326ad559312197af73e3d61a7ec189f20f1e47..eb955bacb215b9931eb9b58dec60a830710ce398 100644 (file)
@@ -4,7 +4,7 @@ Removed functionality
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Group cut-off scheme
index c28942396d8345c579ff978f6ae44a1aa8681c59..6290478b280b83cb94176b9fa0ba9d37b7574e80 100644 (file)
@@ -4,7 +4,7 @@ Improvements to |Gromacs| tools
 .. Note to developers!
    Please use """"""" to underline the individual entries for fixed issues in the subfolders,
    otherwise the formatting on the webpage is messed up.
-   Also, please use the syntax :issue:`number` to reference issues on redmine, without the
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
    a space between the colon and number!
 
 Fixed bug in gmx order -calcdist
diff --git a/docs/release-notes/2021/major/bugs-fixed.rst b/docs/release-notes/2021/major/bugs-fixed.rst
new file mode 100644 (file)
index 0000000..0828fe1
--- /dev/null
@@ -0,0 +1,17 @@
+Bugs fixed
+^^^^^^^^^^
+
+.. Note to developers!
+   Please use """"""" to underline the individual entries for fixed issues in the subfolders,
+   otherwise the formatting on the webpage is messed up.
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
+   a space between the colon and number!
+
+Fixed exported libgromacs CMake target
+""""""""""""""""""""""""""""""""""""""
+
+Update the exported libgromacs CMake target to not depend on non-
+existing include paths and add GMX_DOUBLE define to interface
+definitions. The target now gets exported into the Gromacs namespace.
+
+:issue:`3468`
diff --git a/docs/release-notes/2021/major/deprecated-functionality.rst b/docs/release-notes/2021/major/deprecated-functionality.rst
new file mode 100644 (file)
index 0000000..bad4889
--- /dev/null
@@ -0,0 +1,14 @@
+.. _anticipated-changes:
+
+.. Note to developers!
+   Please use """"""" to underline the individual entries for fixed issues in the subfolders,
+   otherwise the formatting on the webpage is messed up.
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
+   a space between the colon and number!
+
+Changes anticipated to |Gromacs| 2021 functionality
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Functionality deprecated in |Gromacs| 2021
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
diff --git a/docs/release-notes/2021/major/features.rst b/docs/release-notes/2021/major/features.rst
new file mode 100644 (file)
index 0000000..abb2bff
--- /dev/null
@@ -0,0 +1,54 @@
+New and improved features
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. Note to developers!
+   Please use """"""" to underline the individual entries for fixed issues in the subfolders,
+   otherwise the formatting on the webpage is messed up.
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
+   a space between the colon and number!
+
+Virtual site with single constructing atom
+""""""""""""""""""""""""""""""""""""""""""
+
+Added a virtual site that is constructed on top if its single constructing
+atom. This can be useful for free-energy calculations.
+
+Density-guided simulations can apply matrix multiplication and shift vector to structures
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+The new mdp option "density-guided-simulation-shift-vector" defines a
+shift vector that shifts the density-guided simulation group before the 
+density forces are evaluated. With a known shift vector that aligns structure
+and input density, this feature enables structure refinement to non-aligned
+densities without the need to manipulate the input density data or structure.
+The mdp option "density-guided-simulation-transformation-matrix" allows to 
+define a matrix with which to multiply the structure coordinates, before the shift
+vector is applied. This allows arbitrary rotation, skewing and scaling of input
+structures with respect to the input densities.
+A typical use case are membrane-embedded proteins which cannot easily be
+shifted and rotated within membranes.
+
+Lower energy drift due to SETTLE
+""""""""""""""""""""""""""""""""
+
+|Gromacs| already applied an improvement to the center of mass calculation in
+SETTLE to reduce energy drift in single precision. Now the center of mass
+calculation is completely avoided, which significantly reduces the energy
+drift when large coordinate values are present. This allows for accurate
+simulations of systems with SETTLE up to 1000 nm in size (but note that
+constraining with LINCS and SHAKE still introduces significant drift,
+which limits the system size to 100 to 200 nm).
+
+FEP using AWH
+"""""""""""""
+
+It is now possible to control the lambda state of a free energy perturbation
+simulation using the Accelerated Weight Histogram method. This can be used
+as one of multiple AWH dimensions, where the other(s) are coupled to pull
+coordinates.
+
+Stochastic cell rescaling barostat
+""""""""""""""""""""""""""""""""""
+
+Implementation of the stochastic cell rescaling barostat. This is a first-order,
+stochastic barostat, that can be used both for equilibration and production.
diff --git a/docs/release-notes/2021/major/highlights.rst b/docs/release-notes/2021/major/highlights.rst
new file mode 100644 (file)
index 0000000..45231d9
--- /dev/null
@@ -0,0 +1,22 @@
+Highlights
+^^^^^^^^^^
+
+|Gromacs| 2021 was released on INSERT DATE HERE. Patch releases may
+have been made since then, please use the updated versions!  Here are
+some highlights of what you can expect, along with more detail in the
+links below!
+
+As always, we've got several useful performance improvements, with or
+without GPUs, all enabled and automated by default. In addition,
+several new features are available for running simulations. We are extremely
+interested in your feedback on how well the new release works on your
+simulations and hardware. The new features are:
+
+* Cool quote autogenerator
+
+
+.. Note to developers!
+   Please use """"""" to underline the individual entries for fixed issues in the subfolders,
+   otherwise the formatting on the webpage is messed up.
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
+   a space between the colon and number!
diff --git a/docs/release-notes/2021/major/miscellaneous.rst b/docs/release-notes/2021/major/miscellaneous.rst
new file mode 100644 (file)
index 0000000..77cd830
--- /dev/null
@@ -0,0 +1,50 @@
+Miscellaneous
+^^^^^^^^^^^^^
+
+.. Note to developers!
+   Please use """"""" to underline the individual entries for fixed issues in the subfolders,
+   otherwise the formatting on the webpage is messed up.
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
+   a space between the colon and number!
+
+Default values for temperature and pressure coupling intervals are now 10
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+With the default mdp input value of -1 for nsttcouple and nstpcouple, grompp would
+set these values to nstlist. Now these are set to 10 and thus independent of nstlist
+(note that grompp may choose smaller values when needed for accurate integration).
+
+Uniform and manual CMake GPU-support configuration
+""""""""""""""""""""""""""""""""""""""""""""""""""
+The GPU accelerations setup has been changed to be uniform for CUDA and OpenCL. Either
+option is now enabled by setting GMX_GPU to CUDA or OpenCL in the CMake configuration.
+To simplify the CMake code, we have also moved away from automated option selection
+based on the build host. In particular, this means that CUDA will not be enabled unless
+the GMX_GPU option is explicitly enabled, and CMake will no longer perform the extra
+steps of trying to detect hardware and propose to install CUDA if hardware is available.
+Apart from the simplification, this should also make it easier to handle multiple
+different accelerator APIs targeting e.g. NVIDIA hardware.
+
+Configuration-time trivalue options changed from autodetection to boolean on/off
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+To simplify the CMake configuration and avoid having multiple settings that
+change outside of the users direct control we have removed the support for
+automatically setting booleans. GMX_BUILD_HELP and GMX_HWLOC are now
+disabled by default, while GMX_LOAD_PLUGINS is enabled by default.
+
+gmxapi C++ interface
+""""""""""""""""""""
+
+``gmxapi::Context`` is now created with ``gmxapi::createContext()``, which allows
+the client to provide an MPI communicator for the library to use instead of its default
+(e.g MPI_COMM_WORLD). MPI-enabled clients may use the :file:`gmxapi/mpi/gmxapi_mpi.h`
+template header and the ``assignResource()`` helper to generate the argument to
+``createContext``.
+
+Unification of several CUDA and OpenCL environment variables
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+The environment variables that had exactly the same meaning in OpenCL and CUDA were unified:
+
+* GMX_CUDA_NB_ANA_EWALD and GMX_OCL_NB_ANA_EWALD into GMX_GPU_NB_ANA_EWALD
+* GMX_CUDA_NB_TAB_EWALD and GMX_OCL_NB_TAB_EWALD into GMX_GPU_NB_TAB_EWALD
+* GMX_CUDA_NB_EWALD_TWINCUT and GMX_OCL_NB_EWALD_TWINCUT into GMX_GPU_NB_EWALD_TWINCUT
diff --git a/docs/release-notes/2021/major/performance.rst b/docs/release-notes/2021/major/performance.rst
new file mode 100644 (file)
index 0000000..b94c016
--- /dev/null
@@ -0,0 +1,34 @@
+Performance improvements
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. Note to developers!
+   Please use """"""" to underline the individual entries for fixed issues in the subfolders,
+   otherwise the formatting on the webpage is messed up.
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
+   a space between the colon and number!
+
+Extend supported use-cases for GPU version of update and constraints
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+GPU version of update and constraints can now be used for FEP, except mass and constraints
+free-energy perturbation.
+       
+Reduce time spent in grompp with large numbers of distance restraints
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+The time `gmx grompp` spent processing distance restraint has been
+changed from quadratic in the number of restraints to linear.
+       
+:issue:`3457`
+
+Support for offloading PME to GPU when doing Coulomb FEP
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+PME calculations can be offloaded to GPU when doing Coulomb free-energy perturbations.
+
+CPU SIMD accelerated implementation of harmonic bonds
+"""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+SIMD acceleration for bonds slightly improves performance for systems
+with H-bonds only constrained or no constraints. This gives a significant
+improvement with multiple time stepping.
diff --git a/docs/release-notes/2021/major/portability.rst b/docs/release-notes/2021/major/portability.rst
new file mode 100644 (file)
index 0000000..612e9a4
--- /dev/null
@@ -0,0 +1,72 @@
+Portability
+^^^^^^^^^^^
+
+Python environment
+""""""""""""""""""
+
+Where Python is required,
+`CPython <https://www.python.org>`__ versions 3.6 to 3.8 are supported.
+
+CMake now detects Python using
+`FindPython3 <https://cmake.org/cmake/help/v3.13/module/FindPython3.html>`__.
+If you previously used ``PYTHON_EXECUTABLE`` to hint the location of the Python
+interpreter, you should instead specify the Python "root" or "prefix" path
+(the directory containing ``./bin/python3``) with CMake variable
+``Python3_ROOT`` or ``CMAKE_PREFIX_PATH``. As other infrastructure evolves,
+``PYTHON_EXECUTABLE`` may cease to have the desired effect without warning.
+
+.. Note to developers!
+   Please use """"""" to underline the individual entries for fixed issues in the subfolders,
+   otherwise the formatting on the webpage is messed up.
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
+   a space between the colon and number!
+
+CMake
+"""""
+
+Updated required CMake version to 3.13.
+
+C++ standard
+""""""""""""
+
+|Gromacs| has updated the required C++ standards compliance from C++14 to C++17,
+and requires 2017 standard library features. See the install guide for details.
+
+Cygwin
+""""""
+
+|Gromacs| now builds on Cygwin with both gcc and clang compilers.
+
+Windows
+"""""""
+
+|Gromacs| now builds correctly on Windows with MSVC even when the path
+to the source or build directory has a space in it.
+
+RDTSCP usage and reporting
+""""""""""""""""""""""""""
+
+|Gromacs| now defaults always on x86 to use the RDTSCP machine
+instruction for lower latency timing. Very old machines might need to
+configure with ``GMX_USE_RDTSCP=off``. Non-x86 platforms are
+unaffected, except that they will no longer report that RDTSCP is
+disabled (because that is self-evident).
+
+Bundle muparser
+"""""""""""""""
+
+|Gromacs| now bundles MuParser version 2.3. It is also possible
+to link to an external provided library.
+
+armv8+sve support (ARM_SVE)
+"""""""""""""""""""""""""""
+Support for ARM Scalable Vector Extensions (SVE) has been added.
+|Gromacs| supports SVE vector length fixed at CMake configure time
+(typically via the -msve-vector-bits=<len> compiler option),
+which is at the time of the release supported in GNU GCC 10 and later,
+and will supported soon by LLVM 12 and compilers based on this.
+The default vector length is 512 bits, and that can be changed at
+CMake configure time with ``GMX_SIMD_ARM_SVE_LENGTH=<bits>`` option.
+Supported values are 128, 256, 512 and 1024. Note that the nonbonded
+kernels have not been optimized for ARM_SVE as of yet.
+ARM_SVE support is contributed by the Research Organization for Science Information and Technology (RIST)
diff --git a/docs/release-notes/2021/major/removed-functionality.rst b/docs/release-notes/2021/major/removed-functionality.rst
new file mode 100644 (file)
index 0000000..d631d1b
--- /dev/null
@@ -0,0 +1,13 @@
+Removed functionality
+^^^^^^^^^^^^^^^^^^^^^
+
+.. Note to developers!
+   Please use """"""" to underline the individual entries for fixed issues in the subfolders,
+   otherwise the formatting on the webpage is messed up.
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
+   a space between the colon and number!
+
+Removed GMX_SCSIGMA_MIN environment variable
+""""""""""""""""""""""""""""""""""""""""""""
+
+This was used to reproduce free-energy soft-core behavior of GROMACS versions before 4.5.
diff --git a/docs/release-notes/2021/major/tools.rst b/docs/release-notes/2021/major/tools.rst
new file mode 100644 (file)
index 0000000..32a3588
--- /dev/null
@@ -0,0 +1,9 @@
+Improvements to |Gromacs| tools
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. Note to developers!
+   Please use """"""" to underline the individual entries for fixed issues in the subfolders,
+   otherwise the formatting on the webpage is messed up.
+   Also, please use the syntax :issue:`number` to reference issues on GitLab, without the
+   a space between the colon and number!
+
index 93af555709b6f6d9aa3796fc3e9043a8b2091efa..dfb12c7e5a2ae8c581cdd8be8d075811cf75af58 100644 (file)
@@ -8,17 +8,39 @@ releases of |Gromacs|. Major releases contain changes to the
 functionality supported, whereas patch releases contain only fixes for
 issues identified in the corresponding major releases.
 
-Two versions of |Gromacs| are under active maintenance, the 2020
-series and the 2019 series. In the latter, only highly conservative
+Two versions of |Gromacs| are under active maintenance, the 2021
+series and the 2020 series. In the latter, only highly conservative
 fixes will be made, and only to address issues that affect scientific
 correctness. Naturally, some of those releases will be made after the
-year 2019 ends, but we keep 2019 in the name so users understand how
+year 2020 ends, but we keep 2019 in the name so users understand how
 up to date their version is. Such fixes will also be incorporated into
-the 2020 release series, as appropriate. Around the time the 2021
-release is made, the 2019 series will no longer be maintained.
+the 2021 release series, as appropriate. Around the time the 2022
+release is made, the 2020 series will no longer be maintained.
 
 Where issue numbers are reported in these release notes, more details
-can be found at https://redmine.gromacs.org at that issue number.
+can be found at https://gitlab.com/gromacs/gromacs/-/issues at that issue number.
+
+|Gromacs| 2021 series
+---------------------
+
+.. todolist::
+
+Major release
+^^^^^^^^^^^^^
+
+.. toctree::
+   :maxdepth: 1
+
+   2021/major/highlights
+   2021/major/features
+   2021/major/performance
+   2021/major/tools
+   2021/major/bugs-fixed
+   2021/major/deprecated-functionality
+   2021/major/removed-functionality
+   2021/major/portability
+   2021/major/miscellaneous
+
 
 |Gromacs| 2020 series
 ---------------------
index cc5ae564c7f27f0514c36c97e2085d5e795cc2ba..33ae308c6300de92a5a58c08c11f5e77f67090cf 100644 (file)
@@ -11,7 +11,7 @@ changes to the functionality supported, whereas patch releases contain
 only fixes for issues identified in the corresponding major releases.
 
 Where issue numbers are reported in these release notes, more details
-can be found at https://redmine.gromacs.org at that issue number.
+can be found at https://gitlab.com/gromacs/gromacs/-/issues at that issue number.
 
 |Gromacs| 5.1 series
 -----------------------------------
index e50ca665e867f43ee9a2136d581bf861795896b1..28400dd64ccaf3319de07b639b0f345d99139880 100644 (file)
@@ -14,3 +14,12 @@ external tool providers to prepare for such changes, and contact the
 |Gromacs| developers to see how they might be affected and how best to
 adapt. There is a :ref:`current list <deprecated-functionality>`
 of deprecated functionality.
+
+When environment variables are deprecated, it is up to the user to make
+sure that their scripts are updated accordingly for the new release. In
+cases where it is sensible, the development team should do the effort to
+keep the old environment variables working for one extra release cycle,
+before fully removing them. The user should be informed about this future
+deprecation with a warning. If keeping the old environment variable is
+not possible or highly problematic, setting the removed environment
+variable should be triggering a warning during one release cycle.
index 6e4da6382e34fe9af7deda832712bf24aae52be2..8d288eed43bda79949bcc8e2ed8e42662884a4f7 100644 (file)
@@ -4,8 +4,6 @@
 .. Another useful one-liner to find undocumentedvariables:
 ..  ( export INPUT_FILE=docs/user-guide/environment-variables.rst; GIT_PAGER="cat ";   for ss in `for s in $(git grep getenv |  sed 's/.*getenv("\(.*\)".*/\1/' | sort -u  | grep '^[A-Z]'); do [ $(grep $s $INPUT_FILE -c) -eq 0 ] && echo $s; done `; do git grep $ss ; done )
 
-.. TODO: still undocumented GMX_QM_GAUSSIAN_NCPUS
-
 Environment Variables
 =====================
 
@@ -127,6 +125,12 @@ Debugging
         arrive first. Setting this variable switches to the generic path with fixed waiting
         order.
 
+``GMX_TEST_REQUIRED_NUMBER_OF_DEVICES``
+        sets the number of GPUs required by the test suite. By default, the test suite would
+        fall-back to using CPU if GPUs could not be detected. Set it to a positive integer value
+        to ensure that at least this at least this number of usable GPUs are detected. Default:
+        0 (not testing GPU availability).
+
 There are a number of extra environment variables like these
 that are used in debugging - check the code!
 
@@ -143,15 +147,15 @@ Performance and Run Control
         to localized bonded interaction distribution; optimal value dependent on
         system and hardware, default value is 4.
 
-``GMX_CUDA_NB_EWALD_TWINCUT``
+``GMX_GPU_NB_EWALD_TWINCUT``
         force the use of twin-range cutoff kernel even if :mdp:`rvdw` equals
         :mdp:`rcoulomb` after PP-PME load balancing. The switch to twin-range kernels is automated,
         so this variable should be used only for benchmarking.
 
-``GMX_CUDA_NB_ANA_EWALD``
+``GMX_GPU_NB_ANA_EWALD``
         force the use of analytical Ewald kernels. Should be used only for benchmarking.
 
-``GMX_CUDA_NB_TAB_EWALD``
+``GMX_GPU_NB_TAB_EWALD``
         force the use of tabulated Ewald kernels. Should be used only for benchmarking.
 
 ``GMX_DISABLE_CUDA_TIMING``
@@ -249,6 +253,10 @@ Performance and Run Control
         runtime permits this variable to be different for different ranks. Cannot be used
         in conjunction with ``mdrun -gputasks``. Has all the same requirements as ``mdrun -gputasks``.
 
+``GMX_GPU_DISABLE_COMPATIBILITY_CHECK``
+        Disables the hardware compatibility check in OpenCL and SYCL. Useful for developers
+        and allows testing the OpenCL/SYCL kernels on non-supported platforms without source code modification.
+
 ``GMX_IGNORE_FSYNC_FAILURE_ENV``
         allow :ref:`gmx mdrun` to continue even if
         a file is missing.
@@ -354,11 +362,6 @@ Performance and Run Control
         require the use of tabulated Coulombic
         and van der Waals interactions.
 
-``GMX_SCSIGMA_MIN``
-        the minimum value for soft-core sigma. **Note** that this value is set
-        using the :mdp:`sc-sigma` keyword in the :ref:`mdp` file, but this environment variable can be used
-        to reproduce pre-4.5 behavior with respect to this parameter.
-
 ``GMX_TPIC_MASSES``
         should contain multiple masses used for test particle insertion into a cavity.
         The center of mass of the last atoms is used for insertion into the cavity.
@@ -470,59 +473,23 @@ compilation of OpenCL kernels, but they are also used in device selection.
         Enables i-atom data (type or LJ parameter) prefetch allowing
         testing on platforms where this behavior is not default.
 
-``GMX_OCL_NB_ANA_EWALD``
-        Forces the use of analytical Ewald kernels. Equivalent of
-        CUDA environment variable ``GMX_CUDA_NB_ANA_EWALD``
-
-``GMX_OCL_NB_TAB_EWALD``
-        Forces the use of tabulated Ewald kernel. Equivalent
-        of CUDA environment variable ``GMX_OCL_NB_TAB_EWALD``
-
-``GMX_OCL_NB_EWALD_TWINCUT``
-        Forces the use of twin-range cutoff kernel. Equivalent of
-        CUDA environment variable ``GMX_CUDA_NB_EWALD_TWINCUT``
-
 ``GMX_OCL_FILE_PATH``
         Use this parameter to force |Gromacs| to load the OpenCL
         kernels from a custom location. Use it only if you want to
         override |Gromacs| default behavior, or if you want to test
         your own kernels.
 
-``GMX_OCL_DISABLE_COMPATIBILITY_CHECK``
-        Disables the hardware compatibility check. Useful for developers
-        and allows testing the OpenCL kernels on non-supported platforms
-        (like Intel iGPUs) without source code modification.
-
 ``GMX_OCL_SHOW_DIAGNOSTICS``
         Use Intel OpenCL extension to show additional runtime performance
         diagnostics.
 
 Analysis and Core Functions
 ---------------------------
-``GMX_QM_ACCURACY``
-        accuracy in Gaussian L510 (MC-SCF) component program.
-
-``GMX_QM_ORCA_BASENAME``
-        prefix of :ref:`tpr` files, used in Orca calculations
-        for input and output file names.
-
-``GMX_QM_CPMCSCF``
-        when set to a nonzero value, Gaussian QM calculations will
-        iteratively solve the CP-MCSCF equations.
-
-``GMX_QM_MODIFIED_LINKS_DIR``
-        location of modified links in Gaussian.
 
 ``DSSP``
         used by :ref:`gmx do_dssp` to point to the ``dssp``
         executable (not just its path).
 
-``GMX_QM_GAUSS_DIR``
-        directory where Gaussian is installed.
-
-``GMX_QM_GAUSS_EXE``
-        name of the Gaussian executable.
-
 ``GMX_DIPOLE_SPACING``
         spacing used by :ref:`gmx dipoles`.
 
@@ -545,9 +512,6 @@ Analysis and Core Functions
         the time unit used in output files, can be
         anything in fs, ps, ns, us, ms, s, m or h.
 
-``GMX_QM_GAUSSIAN_MEMORY``
-        memory used for Gaussian QM calculation.
-
 ``MULTIPROT``
         name of the ``multiprot`` executable, used by the
         contributed program ``do_multiprot``.
@@ -555,15 +519,6 @@ Analysis and Core Functions
 ``NCPUS``
         number of CPUs to be used for Gaussian QM calculation
 
-``GMX_ORCA_PATH``
-        directory where Orca is installed.
-
-``GMX_QM_SA_STEP``
-        simulated annealing step size for Gaussian QM calculation.
-
-``GMX_QM_GROUND_STATE``
-        defines state for Gaussian surface hopping calculation.
-
 ``GMX_TOTAL``
         name of the ``total`` executable used by the contributed
         ``do_shift`` program.
index 352681430aa077e109c5801e1d6e27280f004bb9..2c45196c27e0495bc95bb988acaa547a86e5bf4b 100644 (file)
@@ -111,7 +111,9 @@ Questions regarding simulation methodology
 
     You can choose different values for :mdp:`tinit` and :mdp:`init-step`.
 
-    .. TODO make links work :ref:`Continuing simulations <gmx-cont-simulation>`.
+    .. todo:: Add "Continuing simulations" content (label: gmx-cont-simulation) and link.
+
+        e.g. ``:ref:`Continuing simulations <gmx-cont-simulation>`.``
 
 #.  Why can't I do conjugate gradient minimization with constraints?
 
@@ -132,16 +134,24 @@ Questions regarding simulation methodology
     You can either prepare a new :ref:`mdp` file, or extend the simulation time
     in the original :ref:`tpr` file using :ref:`convert-tpr <gmx convert-tpr>`.
 
-    .. TODO #.  How do I complete a crashed simulation?
+    .. todo:: #.  How do I complete a crashed simulation?
+
+       Need gmx-cont-crash doc target.
+
+       .. code-block:: none
+
+           This can be easily achieved using the checkpoint reading
+           :ref:`available <gmx-cont-crash>` in |Gromacs| versions newer than 4.
 
-    .. This can be easily achieved using the checkpoint reading
-       :ref:`available <gmx-cont-crash>` in |Gromacs| versions newer than 4.
+    .. todo:: #.  How can I do a simulation at constant pH?
 
-    .. TODO #.  How can I do a simulation at constant pH?
+       Need gmx-howto-cph doc target.
 
-    .. This is a rather large topic, and you should at least read the short
-       :ref:`Constant pH How-To <gmx-howto-cph>` and all of the literature
-       included there to get an overview over the topic.
+        .. code-block:: none
+
+           This is a rather large topic, and you should at least read the short
+           :ref:`Constant pH How-To <gmx-howto-cph>` and all of the literature
+           included there to get an overview over the topic.
 
 #.  How should I compute a single-point energy?
 
@@ -176,9 +186,13 @@ Parameterization and Force Fields
 Analysis and Visualization
 --------------------------
 
-    .. TODO #.  How do I visualize a trajectory?
+.. todo:: #.  How do I visualize a trajectory?
+
+   gmx-howto-visualize doc target:
+
+   .. code-block:: none
 
-    .. Use one of the number of different programs that can visualize
+       Use one of the number of different programs that can visualize
        coordinate :ref:`files and trajectories <gmx-howto-visualize>`.
 
 #.  Why am I seeing bonds being created when I watch the trajectory?
index 334fd1f1b92bec894170d60dfec61764ff039ac7..d01f83f2f9ced31532170041a16b0e1569af9ad9 100644 (file)
@@ -105,7 +105,7 @@ for 43a1, 43a2, 45a3, 53a5, 53a6 and 54a7. The GROMOS force fields are
 * GROMOS 43a1p - 43a1 modified to contain SEP (phosphoserine), TPO (phosphothreonine),
   and PTR (phosphotyrosine) (all PO42- forms), and SEPH, TPOH, PTRH (PO4H- forms).
 
-.. TODO Add new force fields to the list
+.. todo:: Add new force fields to the list
 
 .. _GROMOS: http://www.igc.ethz.ch/gromos/
 .. _reference manual: gmx-manual-parent-dir_
index f70b08ad1d1d3d1c36aa824416865af2fd6d903d..34c6749ee1ff775ab36a2f341f06d941e2154d76 100644 (file)
@@ -20,7 +20,9 @@ For background on algorithms and implementations, see the
 
 |GMX_SOURCE_DOI_STRING|
 
-.. TODO This is going to require more organization now that
+.. todo::
+
+   This is going to require more organization now that
    we are getting more content available.
 
 .. toctree::
index d68588f6cfb8a266175f6b7401ee53e4061ebd69..d6f9d3c63f2c3bec1a06ff2595079ec9a95ef14a 100644 (file)
@@ -2,8 +2,9 @@
    See the "run control" section for a working example of the
    syntax to use when making .mdp entries, with and without detailed
    documentation for values those entries might take. Everything can
-   be cross-referenced, see the examples there. TODO Make more
-   cross-references.
+   be cross-referenced, see the examples there.
+
+.. todo:: Make more cross-references.
 
 Molecular dynamics parameters (.mdp options)
 ============================================
@@ -230,6 +231,45 @@ Run control
          same simulation. This option is generally useful to set only
          when coping with a crashed simulation where files were lost.
 
+.. mdp:: mts
+
+   .. mdp-value:: no
+
+      Evaluate all forces at every integration step.
+
+   .. mdp-value:: yes
+
+      Use a multiple timing-stepping integrator to evaluate some forces, as specified
+      by :mdp:`mts-level2-forces` every :mdp:`mts-level2-factor` integration
+      steps. All other forces are evaluated at every step.
+
+.. mdp:: mts-levels
+
+        (2)
+       The number of levels for the multiple time-stepping scheme.
+       Currently only 2 is supported.
+
+.. mdp:: mts-level2-forces
+
+   (longrange-nonbonded nonbonded pair dihedral)
+   A list of force groups that will be evaluated only every
+   :mdp:`mts-level2-factor` steps. Supported entries are:
+   ``longrange-nonbonded``, ``nonbonded``, ``pair``, ``dihedral`` and
+   ``angle``. With ``pair`` the listed pair forces (such as 1-4) are
+   selected. With ``dihedral`` all dihedrals are selected, including cmap.
+   All other forces, including all restraints, are evaluated and
+   integrated every step. When PME or Ewald is used for electrostatics
+   and/or LJ interactions, ``longrange-nonbonded`` has to be entered here.
+   The default value should work well for most standard atomistic simulations
+   and in particular for replacing virtual site treatment for increasing
+   the time step.
+
+.. mdp:: mts-level2-factor
+
+      (2) [steps]
+      Interval for computing the forces in level 2 of the multiple time-stepping
+      scheme
+
 .. mdp:: comm-mode
 
    .. mdp-value:: Linear
@@ -975,8 +1015,10 @@ Temperature coupling
 
    (-1)
    The frequency for coupling the temperature. The default value of -1
-   sets :mdp:`nsttcouple` equal to :mdp:`nstlist`, unless
-   :mdp:`nstlist` <=0, then a value of 10 is used. For velocity
+   sets :mdp:`nsttcouple` equal to 10, or fewer steps if required
+   for accurate integration. Note that the default value is not 1
+   because additional computation and communication is required for
+   obtaining the kinetic energy. For velocity
    Verlet integrators :mdp:`nsttcouple` is set to 1.
 
 .. mdp:: nh-chain-length
@@ -1033,6 +1075,13 @@ Pressure coupling
       ensemble, but it is the most efficient way to scale a box at the
       beginning of a run.
 
+   .. mdp-value:: C-rescale
+
+      Exponential relaxation pressure coupling with time constant
+      :mdp:`tau-p`, including a stochastic term to enforce correct
+      volume fluctuations.  The box is scaled every :mdp:`nstpcouple`
+      steps. It can be used for both equilibration and production.
+
    .. mdp-value:: Parrinello-Rahman
 
       Extended-ensemble pressure coupling where the box vectors are
@@ -1109,8 +1158,10 @@ Pressure coupling
 
    (-1)
    The frequency for coupling the pressure. The default value of -1
-   sets :mdp:`nstpcouple` equal to :mdp:`nstlist`, unless
-   :mdp:`nstlist` <=0, then a value of 10 is used. For velocity
+   sets :mdp:`nstpcouple` equal to 10, or fewer steps if required
+   for accurate integration. Note that the default value is not 1
+   because additional computation and communication is required for
+   obtaining the virial. For velocity
    Verlet integrators :mdp:`nstpcouple` is set to 1.
 
 .. mdp:: tau-p
@@ -1637,7 +1688,8 @@ pull-coord2-vec, pull-coord2-k, and so on.
       Center of mass pulling using a constraint between the reference
       group and one or more groups. The setup is identical to the
       option umbrella, except for the fact that a rigid constraint is
-      applied instead of a harmonic potential.
+      applied instead of a harmonic potential. Note that this type is
+      not supported in combination with multiple time stepping.
 
    .. mdp-value:: constant-force
 
@@ -2005,7 +2057,7 @@ AWH adaptive biasing
       The file name can be changed with the ``-awh`` option.
       The first :mdp:`awh1-ndim` columns of
       each input file should contain the coordinate values, such that each row defines a point in
-      coordinate space. Column :mdp:`awh1-ndim` + 1 should contain the PMF value for each point.
+      coordinate space. Column :mdp:`awh1-ndim` + 1 should contain the PMF value (in kT) for each point.
       The target distribution column can either follow the PMF (column  :mdp:`awh1-ndim` + 2) or
       be in the same column as written by :ref:`gmx awh`.
 
@@ -2039,8 +2091,14 @@ AWH adaptive biasing
 
    .. mdp-value:: pull
 
-      The module providing the reaction coordinate for this dimension.
-      Currently AWH can only act on pull coordinates.
+      The pull module is providing the reaction coordinate for this dimension.
+
+   .. mdp-value:: fep-lambda
+
+      The free energy lambda state is the reaction coordinate for this dimension.
+      The lambda states to use are specified by :mdp:`fep-lambdas`, :mdp:`vdw-lambdas`,
+      :mdp:`coul-lambdas` etc. This is not compatible with delta-lambda. It also requires
+      calc-lambda-neighbors to be -1.
 
 .. mdp:: awh1-dim1-coord-index
 
@@ -2072,7 +2130,7 @@ AWH adaptive biasing
 
 .. mdp:: awh1-dim1-diffusion
 
-   (10\ :sup:`-5`) [nm\ :sup:`2`/ps] or [rad\ :sup:`2`/ps]
+   (10\ :sup:`-5`) [nm\ :sup:`2`/ps], [rad\ :sup:`2`/ps] or [ps\ :sup:`-1`]
    Estimated diffusion constant for this coordinate dimension determining the initial
    biasing rate. This needs only be a rough estimate and should not critically
    affect the results unless it is set to something very low, leading to slow convergence,
@@ -2456,10 +2514,6 @@ Free energy calculations
    (6)
    power 6 for the radial term in the soft-core equation.
 
-   (48)
-   (deprecated) power 48 for the radial term in the soft-core equation. 
-   Note that sc-alpha should generally be much lower (between 0.001 and 0.003).
-
 .. mdp:: sc-coul
 
    (no)
@@ -2986,101 +3040,15 @@ Electric fields
 Mixed quantum/classical molecular dynamics
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-.. MDP:: QMMM
-
-   .. mdp-value:: no
-
-      No QM/MM.
-
-   .. mdp-value:: yes
-
-      Do a QM/MM simulation. Several groups can be described at
-      different QM levels separately. These are specified in the
-      :mdp:`QMMM-grps` field separated by spaces. The level of *ab
-      initio* theory at which the groups are described is specified by
-      :mdp:`QMmethod` and :mdp:`QMbasis` Fields. Describing the
-      groups at different levels of theory is only possible with the
-      ONIOM QM/MM scheme, specified by :mdp:`QMMMscheme`.
-
 .. mdp:: QMMM-grps
 
-   groups to be descibed at the QM level (works also in case of MiMiC QM/MM)
+   groups to be descibed at the QM level for MiMiC QM/MM
 
-.. mdp:: QMMMscheme
-
-   .. mdp-value:: normal
-
-      normal QM/MM. There can only be one :mdp:`QMMM-grps` that is
-      modelled at the :mdp:`QMmethod` and :mdp:`QMbasis` level of
-      *ab initio* theory. The rest of the system is described at the
-      MM level. The QM and MM subsystems interact as follows: MM point
-      charges are included in the QM one-electron hamiltonian and all
-      Lennard-Jones interactions are described at the MM level.
-
-   .. mdp-value:: ONIOM
-
-      The interaction between the subsystem is described using the
-      ONIOM method by Morokuma and co-workers. There can be more than
-      one :mdp:`QMMM-grps` each modeled at a different level of QM
-      theory (:mdp:`QMmethod` and :mdp:`QMbasis`).
-
-.. mdp:: QMmethod
-
-   (RHF)
-   Method used to compute the energy and gradients on the QM
-   atoms. Available methods are AM1, PM3, RHF, UHF, DFT, B3LYP, MP2,
-   CASSCF, and MMVB. For CASSCF, the number of electrons and orbitals
-   included in the active space is specified by :mdp:`CASelectrons`
-   and :mdp:`CASorbitals`.
-
-.. mdp:: QMbasis
-
-   (STO-3G)
-   Basis set used to expand the electronic wavefuntion. Only Gaussian
-   basis sets are currently available, *i.e.* ``STO-3G, 3-21G, 3-21G*,
-   3-21+G*, 6-21G, 6-31G, 6-31G*, 6-31+G*,`` and ``6-311G``.
-
-.. mdp:: QMcharge
-
-   (0) [integer]
-   The total charge in ``e`` of the :mdp:`QMMM-grps`. In case there are
-   more than one :mdp:`QMMM-grps`, the total charge of each ONIOM
-   layer needs to be specified separately.
-
-.. mdp:: QMmult
-
-   (1) [integer]
-   The multiplicity of the :mdp:`QMMM-grps`. In case there are more
-   than one :mdp:`QMMM-grps`, the multiplicity of each ONIOM layer
-   needs to be specified separately.
-
-.. mdp:: CASorbitals
-
-   (0) [integer]
-   The number of orbitals to be included in the active space when
-   doing a CASSCF computation.
-
-.. mdp:: CASelectrons
-
-   (0) [integer]
-   The number of electrons to be included in the active space when
-   doing a CASSCF computation.
-
-.. MDP:: SH
+.. MDP:: QMMM
 
    .. mdp-value:: no
 
-      No surface hopping. The system is always in the electronic
-      ground-state.
-
-   .. mdp-value:: yes
-
-      Do a QM/MM MD simulation on the excited state-potential energy
-      surface and enforce a *diabatic* hop to the ground-state when
-      the system hits the conical intersection hyperline in the course
-      the simulation. This option only works in combination with the
-      CASSCF method.
-
+      QM/MM is no longer supported via these .mdp options. For MiMic, use no here.
 
 Computational Electrophysiology
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -3311,6 +3279,25 @@ electron-microscopy experiments. (See the `reference manual`_ for details)
    (4) [ps] Couple force constant to increase in similarity with reference density
    with this time constant. Larger times result in looser coupling.
 
+.. mdp:: density-guided-simulation-shift-vector
+
+   (0,0,0) [nm] Add this vector to all atoms in the 
+   density-guided-simulation-group before calculating forces and energies for
+   density-guided-simulations. Affects only the density-guided-simulation forces
+   and energies. Corresponds to a shift of the input density in the opposite
+   direction by (-1) * density-guided-simulation-shift-vector.
+
+.. mdp:: density-guided-simulation-transformation-matrix
+
+   (1,0,0,0,1,0,0,0,1) Multiply all atoms with this matrix in the 
+   density-guided-simulation-group before calculating forces and energies for
+   density-guided-simulations. Affects only the density-guided-simulation forces
+   and energies. Corresponds to a transformation of the input density by the
+   inverse of this matrix. The matrix is given in row-major order.
+   This option allows, e.g., rotation of the density-guided atom group around the
+   z-axis by :math:`\theta` degress by using following input:
+   :math:`(\cos \theta , -\sin \theta , 0 , \sin \theta , \cos \theta , 0 , 0 , 0 , 1)` .
+
 User defined thingies
 ^^^^^^^^^^^^^^^^^^^^^
 
index 322eb94be50d2733830145ecde729bb939070bfa..a394f2e54aa4b2708286fd9092488f0ec7627b65 100644 (file)
@@ -213,6 +213,18 @@ of the better clock frequency available. Consider building :ref:`mdrun <gmx mdru
 configured with ``GMX_SIMD=AVX2_256`` instead of ``GMX_SIMD=AVX512`` for better
 performance in GPU accelerated or highly parallel MPI runs.
 
+Some of the latest ARM based CPU, such as the Fujitsu A64fx, support the Scalable Vector Extensions (SVE).
+Though SVE can be used to generate fairly efficient Vector Length Agnostic (VLA) code,
+this is not a good fit for |Gromacs| (as the SIMD vector length assumed to be known at
+CMake time). Consequently, the SVE vector length must be fixed at CMake time. The default
+value is 512 bits, and this can be changed with ``GMX_SIMD_ARM_SVE_LENGTH=<len>``.
+The supported vector lengths are 128, 256, 512 and 1024. Since the SIMD short-range non-bonded kernels
+only support up to 16 floating point numbers per SIMD vector, 1024 bits vector length is only
+valid in double precision (e.g. ``-DGMX_DOUBLE=on``).
+Note that even if `mdrun` does check the SIMD vector length at runtime, running with a different
+vector length than the one used at CMake time is undefined behavior, and `mdrun` might crash before reaching
+the check (that would abort with a user-friendly error message).
+
 Process(-or) level parallelization via OpenMP
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -531,9 +543,10 @@ behavior.
     Setting "gpu" requires that a compatible CUDA GPU is available,
     the simulation uses a single rank.
     Update and constraints on a GPU is currently not supported
-    with domain decomposition, free-energy, virtual sites,
-    Ewald surface correction, replica exchange, constraint pulling,
-    orientation restraints and computational electrophysiology.
+    with mass and constraints free-energy perturbation, domain
+    decomposition, virtual sites, Ewald surface correction,
+    replica exchange, constraint pulling, orientation restraints
+    and computational electrophysiology.
 
 ``-gpu_id``
     A string that specifies the ID numbers of the GPUs that
@@ -998,9 +1011,11 @@ An additional set of subcounters can offer more fine-grained inspection of perfo
 Subcounters are geared toward developers and have to be enabled during compilation. See
 :doc:`/dev-manual/build-system` for more information.
 
-.. TODO In future patch:
-   - red flags in log files, how to interpret wallcycle output
-   - hints to devs how to extend wallcycles
+..  todo::
+
+    In future patch:
+    - red flags in log files, how to interpret wallcycle output
+    - hints to devs how to extend wallcycles
 
 .. _gmx-mdrun-on-gpu:
 
@@ -1057,7 +1072,7 @@ compatibility (please see the :ref:`section below <gmx-pme-gpu-limitations>`).
 GPU computation of short range nonbonded interactions
 .....................................................
 
-.. TODO make this more elaborate and include figures
+.. todo:: make this more elaborate and include figures
 
 Using the GPU for the short-ranged nonbonded interactions provides
 the majority of the available speed-up compared to run using only the CPU.
@@ -1069,7 +1084,7 @@ this problem and thus reduce the calculation time.
 GPU accelerated calculation of PME
 ..................................
 
-.. TODO again, extend this and add some actual useful information concerning performance etc...
+.. todo:: again, extend this and add some actual useful information concerning performance etc...
 
 |Gromacs| now allows the offloading of the PME calculation
 to the GPU, to further reduce the load on the CPU and improve usage overlap between
@@ -1090,9 +1105,6 @@ Known limitations
 
 - Only single precision is supported.
 
-- Free energy calculations where charges are perturbed are not supported,
-  because only single PME grids can be calculated.
-
 - Only dynamical integrators are supported (ie. leap-frog, Velocity Verlet,
   stochastic dynamics)
 
@@ -1103,7 +1115,7 @@ Known limitations
 GPU accelerated calculation of bonded interactions (CUDA only)
 ..............................................................
 
-.. TODO again, extend this and add some actual useful information concerning performance etc...
+.. todo:: again, extend this and add some actual useful information concerning performance etc...
 
 |Gromacs| now allows the offloading of the bonded part of the PP
 workload to a CUDA-compatible GPU. This is treated as part of the PP
@@ -1211,9 +1223,9 @@ Performance considerations for GPU tasks
 #) The only way to know for sure what alternative is best for
    your machine is to test and check performance.
 
-.. TODO: we need to be more concrete here, i.e. what machine/software aspects to take into consideration, when will default run mode be using PME-GPU and when will it not, when/how should the user reason about testing different settings than the default.
+.. todo:: we need to be more concrete here, i.e. what machine/software aspects to take into consideration, when will default run mode be using PME-GPU and when will it not, when/how should the user reason about testing different settings than the default.
 
-.. TODO someone who knows about the mixed mode should comment further.
+.. todo:: someone who knows about the mixed mode should comment further.
 
 Reducing overheads in GPU accelerated runs
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1264,7 +1276,7 @@ Note that assigning fewer resources to :ref:`gmx mdrun` CPU computation
 involves a tradeoff which may outweigh the benefits of reduced GPU driver overhead,
 in particular without HyperThreading and with few CPU cores.
 
-.. TODO In future patch: any tips not covered above
+.. todo:: In future patch: any tips not covered above
 
 Running the OpenCL version of mdrun
 -----------------------------------
@@ -1284,7 +1296,8 @@ required as the open source nouveau driver (available in Mesa) does not
 provide the OpenCL support.
 For Intel integrated GPUs, the `Neo driver <https://github.com/intel/compute-runtime/releases>`_ is
 recommended.
-TODO: add more Intel driver recommendations
+.. seealso:: :issue:`3268` add more Intel driver recommendations
+
 The minimum OpenCL version required is |REQUIRED_OPENCL_MIN_VERSION|. See
 also the :ref:`known limitations <opencl-known-limitations>`.
 
@@ -1355,7 +1368,7 @@ of 2. So it can be useful go through the checklist.
 * Make sure your compiler supports OpenMP (some versions of Clang don't).
 * If you have GPUs that support either CUDA or OpenCL, use them.
 
-  * Configure with ``-DGMX_GPU=ON`` (add ``-DGMX_USE_OPENCL=ON`` for OpenCL).
+  * Configure with ``-DGMX_GPU=CUDA `` or ``-DGMX_GPU=OpenCL``.
   * For CUDA, use the newest CUDA available for your GPU to take advantage of the
     latest performance enhancements.
   * Use a recent GPU driver.
index b47083a4a72028f27c671ad9dca98358492392f8..440e566daff7f943c25e638179834fbf031d7cab 100644 (file)
@@ -85,8 +85,14 @@ the options for obtaining the force field parameters are:
 * search the primary literature for publications for parameters for the
   residue that are consistent with the force field that is being used.
 
-.. TODO Once you have determined the parameters and topology for your residue, see
-   :ref:`adding a residue to a force field <gmx-add-new-residue>` for instructions on how to proceed.
+..  todo:: gmx-add-new-residue doc target
+
+    Need gmx-add-new-residue doc target.
+
+    .. code-block:: none
+
+        Once you have determined the parameters and topology for your residue, see
+        :ref:`adding a residue to a force field <gmx-add-new-residue>` for instructions on how to proceed.
 
 Long bonds and/or missing atoms
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 8bf657337407ce1a640f447a74cbf3e6cef7160e..6d0e7212e9119385ce0dd00863f48cb43fdf10d1 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # (system or user) GROMACS installation and (user) Python environment. For a
 # system-wide Python environment, the package needs to be built and installed
 # (to the ``site-packages`` directory) for each supported Python interpreter.
-# ``setup.py`` can just be invoked with different Python interpreters. We can
-# add CMake infrastructure to allow multiple/repeated PYTHON_EXECUTABLE
-# specification at the GROMACS project level if such a use case is important to
-# HPC site administrators or Linux distribution packagers.
+# ``setup.py`` can just be invoked with different Python interpreters.
 #
 # To drive the packaging of a Python package distribution archive
 # by a higher-level CMake configuration, a CMakeLists.txt file at this level would
index 2f0d6362646ea94ca92eea815c9f53e00c2e08ce..faa48c6a165ee33aa48809accd37b07ad768b597 100644 (file)
@@ -1,7 +1,7 @@
 # Python package sources
 
 This directory exists as a staging area supporting GROMACS enhancement
-[#2045](https://redmine.gromacs.org/issues/2045),
+[#2045](https://gitlab.com/gromacs/gromacs/-/issues/2045),
 which attempts to update the gmxapi efforts from GROMACS 2019,
 merge external repositories from
 https://github.com/kassonlab/gmxapi
@@ -42,7 +42,7 @@ Use `pytest` to run unit tests and integration tests.
     pytest test
 
 For additional discussion on packaging and distribution, see
-https://redmine.gromacs.org/issues/2896
+https://gitlab.com/gromacs/gromacs/-/issues/2896
 
 ## Sample MD extension code
 
@@ -107,7 +107,7 @@ Additional information in `python_packaging/docker/README.md`.
 
 Hint: the fork point from `master` and the current git ref can be set as environment variables:
 
-    FORKPOINT=$(git show -s --pretty=format:"%h" `git merge-base gerrit_master HEAD`)
+    FORKPOINT=$(git show -s --pretty=format:"%h" `git merge-base master HEAD`)
     REF=`git show -s --pretty=format:"%h"`
 
 ## External project code
@@ -124,7 +124,7 @@ Scikit-build is installed with Python packaging tools automatically with
 `pip install -r requirements.txt`, as above.
 
 Note: scikit-build is only required for convenient management of the Python
-build environment and packaging. See https://redmine.gromacs.org/issues/2896
+build environment and packaging. See https://gitlab.com/gromacs/gromacs/-/issues/2896
 
 # pybind11
 
index e22ba4dbcefdbbb0bcd158b67870975a017b1d46..b149784a69fc24409e7c86da278c7b1e37cfd8fd 100644 (file)
@@ -7,9 +7,7 @@ by future Jenkins infrastructure.
 Assume you have already checked out the commit you want to build for.
 Assume the following definitions.
 
-    git fetch https://github.com/gromacs/gromacs.git master
-    git branch gerrit_master FETCH_HEAD
-    FORKPOINT=$(git show -s --pretty=format:"%h" `git merge-base gerrit_master HEAD`)
+    FORKPOINT=$(git show -s --pretty=format:"%h" `git merge-base master HEAD`)
     TAG="fr1" # for functional requirement 1
 
 ## Building
index 442f9687fa6f66195f35e0f8d855517abb6c75c8..42b0d7f0f84ad9cef64ebf0c9e318649db40f3e4 100644 (file)
@@ -12,7 +12,7 @@
 # Build from the GROMACS image at the current fork point. Tag with the feature
 # name or the current revision.
 #
-#    FORKPOINT=$(git show -s --pretty=format:"%h" `git merge-base gerrit_master HEAD`)
+#    FORKPOINT=$(git show -s --pretty=format:"%h" `git merge-base master HEAD`)
 #    REF=`git show -s --pretty=format:"%h"`
 #    # or
 #    REF="fr1"
index 860b6c58af7c5198b2bdaa10f2dc7296a8914317..7f3ca34cb804df6e30465ca076e6a99e69f6df18 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2015,2016,2017,2018,2019 by the GROMACS development team.
+# Copyright (c) 2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 4ec29f6f94074c764f985554a05227ac2e2d8b9d..ef4c6ae0a9f3fd174e4a6c76d64131604018c20e 100644 (file)
@@ -1,12 +1,13 @@
 # Python package requirements for complete build, installation, and testing of
 # gmxapi functionality.
-cmake>=3.9.6
+cmake>=3.13
+flake8>=3.7.7
 networkx>=2.0
-pip>=10.1
-setuptools>=28.0.0
-scikit-build>=0.7
-# The following packages are not strictly necessary, but allow full documentation
-# builds and testing.
-mpi4py>=2
 numpy>=1
+pip>=10.1
+scikit-build>=0.10
+setuptools>=42
+wheel
+# The following packages are not strictly necessary, but allow full testing.
+mpi4py>=3.0.3
 pytest>=3.9
index 57487268347be3b9fa1c10404d14e152824dcdb9..a95b4862f5b18c3d3793d43f14232e9a59174a40 100644 (file)
@@ -4,7 +4,7 @@ project(pybind-download NONE)
 include(ExternalProject)
 ExternalProject_Add(pybind11
                     GIT_REPOSITORY    https://github.com/pybind/pybind11.git
-                    GIT_TAG           v2.2
+                    GIT_TAG           v2.5
                     SOURCE_DIR        "${CMAKE_CURRENT_BINARY_DIR}/pybind-src"
                     BINARY_DIR        "${CMAKE_CURRENT_BINARY_DIR}/pybind-build"
                     CONFIGURE_COMMAND ""
index b6d6fd9699c67d9d3c53bda4361a615f4fdb6587..fee19bfcd2edfb9c2c8e299446d30f53185bbf2c 100644 (file)
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.9.6)
+cmake_minimum_required(VERSION 3.13.0)
 # If you are using this repository as a template, you should probably change the
 # project name and adopt your own versioning scheme.
 project(sample_restraint VERSION 0.0.8)
@@ -21,7 +21,7 @@ endif()
 # the paackage that should be installed with GROMACS, but the pybind sources
 # would only be available to this project with some additional management by
 # the parent project CMake configuration.
-# Also reference https://redmine.gromacs.org/issues/2896
+# Also reference https://gitlab.com/gromacs/gromacs/-/issues/2896
 if(GMXAPI_EXTENSION_MASTER_PROJECT)
     # TODO: (Issue #3027) Handle locally available sources.
     set(GMXAPI_EXTENSION_USE_BUNDLED_PYBIND OFF CACHE BOOL
@@ -62,7 +62,7 @@ endif()
 # versions of GROMACS. If building from the command line, you can specify a Python executable with the PYTHON_EXECUTABLE
 # variable. For instance, to make sure you are building for your default Python, cmake -DPYTHON_EXECUTABLE=`which python`.
 
-set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_VISIBILITY_PRESET hidden)
 
 # CMake modules are in a subdirectory to keep this file cleaner
index aa1ee524b5cef3af1de692a1b93b2a21c99d5050..e347ba3f2277706af7874aa42544a3df1fd07584 100644 (file)
@@ -48,15 +48,15 @@ package.
     the Pybind project (ref <https://github.com/pybind/pybind11> ). It
     is used to wrap the C++ restraint code and give it a Python
     interface.</strike> Note: pybind is currently retrieved while configuring
-    with CMake. Ref redmine issues [3027](https://redmine.gromacs.org/issues/3027)
-    and [3033](https://redmine.gromacs.org/issues/3033)
+    with CMake. Ref issues [3027](https://gitlab.com/gromacs/gromacs/-/issues/3027)
+    and [3033](https://gitlab.com/gromacs/gromacs/-/issues/3033)
 -   `tests/` contains C++ and Python tests for the provided code. Update
     `CMakeLists.txt` to add your own, based on these examples. C++ unit
     tests use [googletest](https://github.com/google/googletest). Python
     tests use the [pytest](https://docs.pytest.org/en/latest/). Refer to
     those respective projects for more about how they make test-writing
     easier. Note: googletest is currently downloaded while configuring with
-    CMake. Ref [3033](https://redmine.gromacs.org/issues/3033)
+    CMake. Ref [3033](https://gitlab.com/gromacs/gromacs/-/issues/3033)
 -   `examples` contains a sample SLURM job script and
     `restrained-ensemble.py` gmxapi script that have been used to do
     restrained ensemble simulations. `example.py` and `example.ipynb`
index eec2648b28ee36f4c17d57e447e83e67afd18d1f..29ac4a3112c5c7820faf52abd2e338f75d45e1b6 100644 (file)
@@ -27,6 +27,7 @@
 #include "gmxapi/md/mdmodule.h"
 
 #include "gromacs/restraint/restraintpotential.h"
+#include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
 #include "sessionresources.h"
index 24446d74132c155ed71a92d2862d2b874952fbce..3cc6a2dbad0e3835225fe998aca8c9001fbb56fd 100644 (file)
@@ -53,7 +53,7 @@ if (GMXAPI_EXTENSION_MASTER_PROJECT)
     # We also build a test file in the spc_water_box pytest test fixture, but we can
     # presumably extract both of those from the GROMACS installation, or at least
     # through the gmxapi Python package resources.
-    # Ref: https://redmine.gromacs.org/issues/2961
+    # Ref: https://gitlab.com/gromacs/gromacs/-/issues/2961
     file(DOWNLOAD
          https://github.com/kassonlab/sample_restraint/raw/master/tests/data/topol.tpr
          ${CMAKE_CURRENT_BINARY_DIR}/topol.tpr
index db5da48a4397a0a124740d355ac25bce60a3c17e..37ef0767824a937fcd2a92c7ce5d3ff8526e0a8b 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 import json
 import logging
 import os
-import shutil
-import tempfile
-import warnings
-from contextlib import contextmanager
 
 import pytest
 
-
-def pytest_addoption(parser):
-    """Add a command-line user option for the pytest invocation."""
-    parser.addoption(
-        '--rm',
-        action='store',
-        default='always',
-        choices=['always', 'never', 'success'],
-        help='Remove temporary directories "always", "never", or on "success".'
-    )
-
-
-@pytest.fixture(scope='session')
-def remove_tempdir(request):
-    """pytest fixture to get access to the --rm CLI option."""
-    return request.config.getoption('--rm')
-
-
-@contextmanager
-def scoped_chdir(dir):
-    oldpath = os.getcwd()
-    os.chdir(dir)
-    try:
-        yield dir
-        # If the `with` block using scoped_chdir produces an exception, it will
-        # be raised at this point in this function. We want the exception to
-        # propagate out of the `with` block, but first we want to restore the
-        # original working directory, so we skip `except` but provide a `finally`.
-    finally:
-        os.chdir(oldpath)
-
-
-@contextmanager
-def _cleandir(remove_tempdir):
-    """Context manager for a clean temporary working directory.
-
-    Arguments:
-        remove_tempdir (str): whether to remove temporary directory "always",
-                              "never", or on "success"
-
-    The context manager will issue a warning for each temporary directory that
-    is not removed.
-    """
-
-    newpath = tempfile.mkdtemp()
-
-    def remove():
-        shutil.rmtree(newpath)
-
-    def warn():
-        warnings.warn('Temporary directory not removed: {}'.format(newpath))
-
-    if remove_tempdir == 'always':
-        callback = remove
-    else:
-        callback = warn
-    try:
-        with scoped_chdir(newpath):
-            yield newpath
-        # If we get to this line, the `with` block using _cleandir did not throw.
-        # Clean up the temporary directory unless the user specified `--rm never`.
-        # I.e. If the user specified `--rm success`, then we need to toggle from `warn` to `remove`.
-        if remove_tempdir != 'never':
-            callback = remove
-    finally:
-        callback()
-
-
-@pytest.fixture
-def cleandir(remove_tempdir):
-    """Provide a clean temporary working directory for a test.
-
-    Example usage:
-
-        import os
-        import pytest
-
-        @pytest.mark.usefixtures("cleandir")
-        def test_cwd_starts_empty():
-            assert os.listdir(os.getcwd()) == []
-            with open("myfile", "w") as f:
-                f.write("hello")
-
-        def test_cwd_also_starts_empty(cleandir):
-            assert os.listdir(os.getcwd()) == []
-            assert os.path.abspath(os.getcwd()) == os.path.abspath(cleandir)
-            with open("myfile", "w") as f:
-                f.write("hello")
-
-        @pytest.mark.usefixtures("cleandir")
-        class TestDirectoryInit(object):
-            def test_cwd_starts_empty(self):
-                assert os.listdir(os.getcwd()) == []
-                with open("myfile", "w") as f:
-                    f.write("hello")
-
-            def test_cwd_also_starts_empty(self):
-                assert os.listdir(os.getcwd()) == []
-                with open("myfile", "w") as f:
-                    f.write("hello")
-
-    Ref: https://docs.pytest.org/en/latest/fixture.html#using-fixtures-from-classes-modules-or-projects
-    """
-    with _cleandir(remove_tempdir) as newdir:
-        yield newdir
-
-
-@pytest.fixture(scope='session')
-def gmxcli():
-    # TODO: (#2896) Find a more canonical way to identify the GROMACS commandline wrapper binary.
-    #  We should be able to get the GMXRC contents and related hints from a gmxapi
-    #  package resource or from module attributes of a ``gromacs`` stub package.
-    allowed_command_names = ['gmx', 'gmx_mpi']
-    command = None
-    for command_name in allowed_command_names:
-        if command is not None:
-            break
-        command = shutil.which(command_name)
-        if command is None:
-            gmxbindir = os.getenv('GMXBIN')
-            if gmxbindir is None:
-                gromacsdir = os.getenv('GROMACS_DIR')
-                if gromacsdir is not None and gromacsdir != '':
-                    gmxbindir = os.path.join(gromacsdir, 'bin')
-            if gmxbindir is None:
-                gmxapidir = os.getenv('gmxapi_DIR')
-                if gmxapidir is not None and gmxapidir != '':
-                    gmxbindir = os.path.join(gmxapidir, 'bin')
-            if gmxbindir is not None:
-                gmxbindir = os.path.abspath(gmxbindir)
-                command = shutil.which(command_name, path=gmxbindir)
-    if command is None:
-        message = "Tests need 'gmx' command line tool, but could not find it on the path."
-        raise RuntimeError(message)
-    try:
-        assert os.access(command, os.X_OK)
-    except Exception as E:
-        raise RuntimeError('"{}" is not an executable gmx wrapper program'.format(command)) from E
-    yield command
+pytest_plugins = ('gmxapi.testsupport',)
 
 
 @pytest.fixture(scope='class')
@@ -192,6 +50,8 @@ def spc_water_box(gmxcli, remove_tempdir):
     Prepare the MD input in a freshly created working directory.
     """
     import gmxapi as gmx
+    # TODO: Remove this import when the the spc_water_box fixture is migrated to gmxapi.testsupport
+    from gmxapi.testsupport import _cleandir
 
     # TODO: (#2896) Fetch MD input from package / library data.
     # Example:
index 205dd9169bf618e74645ca490bc5b38cfc81dd3f..a483fd799634236353c2342c567842de8afca07b 100644 (file)
@@ -14,7 +14,6 @@ from gmxapi.simulation.context import Context
 from gmxapi.simulation.workflow import WorkElement, from_tpr
 from gmxapi import version as gmx_version
 import pytest
-from gmxapi.testsupport import withmpi_only
 
 # create console handler
 ch = logging.StreamHandler()
@@ -36,14 +35,14 @@ def test_import():
 
 
 @pytest.mark.usefixtures("cleandir")
-def test_ensemble_potential_nompi(spc_water_box):
+def test_ensemble_potential_nompi(spc_water_box, mdrun_kwargs):
     """Test ensemble potential without an ensemble.
     """
     tpr_filename = spc_water_box
     print("Testing plugin potential with input file {}".format(os.path.abspath(tpr_filename)))
 
     assert gmx.version.api_is_at_least(0, 0, 5)
-    md = from_tpr([tpr_filename], append_output=False)
+    md = from_tpr([tpr_filename], append_output=False, **mdrun_kwargs)
 
     # Create a WorkElement for the potential
     params = {'sites': [1, 4],
@@ -72,15 +71,15 @@ def test_ensemble_potential_nompi(spc_water_box):
         session.run()
 
 
-@withmpi_only
+@pytest.mark.withmpi_only
 @pytest.mark.usefixtures("cleandir")
-def test_ensemble_potential_withmpi(spc_water_box):
+def test_ensemble_potential_withmpi(spc_water_box, mdrun_kwargs):
     tpr_filename = spc_water_box
 
     logger.info("Testing plugin potential with input file {}".format(os.path.abspath(tpr_filename)))
 
     assert gmx_version.api_is_at_least(0, 0, 5)
-    md = from_tpr([tpr_filename, tpr_filename], append_output=False)
+    md = from_tpr([tpr_filename, tpr_filename], append_output=False, **mdrun_kwargs)
 
     # Create a WorkElement for the potential
     params = {'sites': [1, 4],
diff --git a/python_packaging/scripts/run_flake8 b/python_packaging/scripts/run_flake8
new file mode 100755 (executable)
index 0000000..3e5ec34
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/bash -x
+
+# Note: E501 specifies a line length limit of 80 characters, but GROMACS allows more.
+#  Ref: http://manual.gromacs.org/current/dev-manual/formatting.html
+# Note: --max-complexity can be used to check McCabe complexity. Compliance
+#       could be a future goal, but is not yet a GROMACS priority.
+# W503 is ignored in observation of current PEP-8 conventions, but must be
+# explicitly disabled in at least some versions.
+# http://pycodestyle.pycqa.org/en/latest/intro.html#error-codes
+# https://www.python.org/dev/peps/pep-0008/#should-a-line-break-before-or-after-a-binary-operator
+
+# Check gmxapi package sources.
+python -m flake8 --ignore W503 --max-line-length 119 ${HOME}/gmxapi/src/gmxapi
+
+# Check gmxapi unit tests.
+python -m flake8 --ignore W503 --max-line-length 119 ${HOME}/gmxapi/src/test
+
+# Check gmxapi acceptance tests.
+python -m flake8 --ignore W503 --max-line-length 119 ${HOME}/gmxapi/test
index f850545c00a59a530750afe07cec4b4f5f3ec373..bdf9f17c52e7c4f3cdc7510e9034bb38bf90c0cd 100755 (executable)
@@ -3,9 +3,7 @@
 # Additional arguments are passed along to pytest.
 # See README.md and the gmxapi/ci-<option> Docker images.
 
-# TODO: (#2756) Centrally manage dependencies on the GROMACS installation.
-#  The gmxapi package, virtualenv, or test  infrastructure should have an
-#  internal mechanism for tracking the correct GROMACS installation.
+# TODO: GMXRC should not be necessary with either Py 3.7+ or importlib_resources package.
 source /usr/local/gromacs/bin/GMXRC
 
 pytest $HOME/gmxapi/test $@
index 18dc260df19f9c29d061c5dcc1e1ccbf7c9ea777..a0e04c002d5ee1eb06a4094a08254355768bcd1a 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # simplest use case is to allow the `setup.py` file to invoke skbuild to
 # configure and run CMake. CMake could be invoked directly by the user or a
 # parent package, but the Python distribution would not be packaged automatically.
-# Reference https://redmine.gromacs.org/issues/2896 for additional discussion.
-cmake_minimum_required(VERSION 3.9.6)
+# Reference https://gitlab.com/gromacs/gromacs/-/issues/2896 for additional discussion.
+cmake_minimum_required(VERSION 3.13.0)
 
 # This needs to be set before project() in order to pick up toolchain files
 #list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake)
 
-# OS X deployment target should be >=10.9 for modern C++ compatibility.
+# OS X deployment target should be >=10.14 for modern C++ compatibility.
 # Reference https://scikit-build.readthedocs.io/en/latest/generators.html#macosx
 # and https://github.com/MacPython/wiki/wiki/Spinning-wheels
-set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9 CACHE STRING
-    "OS X deployment target below 10.9 does not use modern standard library"
-    FORCE)
+set(CMAKE_OSX_DEPLOYMENT_TARGET 10.14 CACHE STRING
+    "OS X deployment target below 10.14 does not use modern standard library")
 set(CMAKE_OSX_ARCHITECTURES x86_64 CACHE STRING
     "OS X should build Python package for 64-bit architecture"
     FORCE)
@@ -61,7 +60,7 @@ if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
     set(GMXAPI_MASTER_PROJECT ON)
 endif()
 
-set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 # Only interpret if() arguments as variables or keywords when unquoted.
@@ -112,7 +111,7 @@ option(GMXAPI_USE_BUNDLED_PYBIND
 if(GMXAPI_USE_BUNDLED_PYBIND)
     add_subdirectory(external/pybind)
 else()
-    # Reference https://redmine.gromacs.org/issues/2896
+    # Reference https://gitlab.com/gromacs/gromacs/-/issues/2896
     find_package(pybind11 2.2 REQUIRED)
 endif()
 
@@ -147,6 +146,50 @@ set(GMXAPI_PYTHON_STAGING_DIR ${CMAKE_CURRENT_BINARY_DIR}/gmxapi_staging)
 set_target_properties(_gmxapi PROPERTIES
                       LIBRARY_OUTPUT_DIRECTORY ${GMXAPI_PYTHON_STAGING_DIR}/gmxapi)
 
+if(GMXAPI_MASTER_PROJECT)
+    find_package(GROMACS 2021 REQUIRED
+                 HINTS "$ENV{GROMACS_DIR}"
+                 )
+endif()
+
+# Get details of GROMACS installation needed by the Python package at run time.
+
+# Get the MPI capability.
+get_target_property(_gmx_mpi Gromacs::gmxapi MPI)
+if (${_gmx_mpi} STREQUAL "library")
+    set(_gmx_mpi_type "\"library\"")
+elseif(${_gmx_mpi} STREQUAL "tmpi")
+    set(_gmx_mpi_type "\"tmpi\"")
+elseif(${_gmx_mpi} STREQUAL "none")
+    set(_gmx_mpi_type "null")
+else()
+    message(FATAL_ERROR "Unrecognized gmxapi MPI value: ${_gmx_mpi}")
+endif ()
+unset(_gmx_mpi)
+# Get the path of the command line entry point and binary install directory.
+if (NOT TARGET Gromacs::gmx)
+    message(FATAL_ERROR "GROMACS command line tool not found.")
+endif ()
+get_target_property(_gmx_executable_imported Gromacs::gmx IMPORTED)
+if (_gmx_executable_imported)
+    get_target_property(_gmx_executable Gromacs::gmx LOCATION)
+    get_filename_component(_gmx_bindir ${_gmx_executable} DIRECTORY)
+    message(STATUS "Imported ${_gmx_bindir} executable.")
+    unset(_gmx_executable_imported)
+else()
+    get_target_property(_gmx_bindir Gromacs::gmx RUNTIME_OUTPUT_DIRECTORY)
+    get_target_property(_gmx_executable Gromacs::gmx OUTPUT_NAME)
+    set(_gmx_executable "${_gmx_bindir}/${_gmx_executable}")
+    message(STATUS "Using ${_gmx_executable} from build tree.")
+endif ()
+if (NOT _gmx_bindir OR NOT _gmx_executable)
+    message(FATAL_ERROR "Could not get path for gmx wrapper binary.")
+endif ()
+configure_file(gmxapi/gmxconfig.json.in ${GMXAPI_PYTHON_STAGING_DIR}/gmxapi/gmxconfig.json)
+unset(_gmx_executable)
+unset(_gmx_bindir)
+unset(_gmx_mpi_type)
+
 # scikit-build sets SKBUILD when running Python packaging tools through setup.py
 # (e.g. with pip)
 if(SKBUILD)
@@ -157,6 +200,7 @@ if(SKBUILD)
     # By default, scikit-build expects the library to be installed into a directory
     # named for the Python package as in setup.py.
     install(TARGETS _gmxapi LIBRARY DESTINATION gmxapi)
+    install(FILES ${GMXAPI_PYTHON_STAGING_DIR}/gmxapi/gmxconfig.json DESTINATION gmxapi)
 else()
     # The Python module is being built against GROMACS in its build tree.
     # Note: we do not have plans to install the staged package when SKBUILD != TRUE
@@ -164,7 +208,7 @@ else()
     target_link_libraries(_gmxapi PRIVATE Gromacs::gmxapi)
 
     # TODO: Determine packaging and installation cases and implementation.
-    # Reference https://redmine.gromacs.org/issues/2896 for additional discussion.
+    # Reference https://gitlab.com/gromacs/gromacs/-/issues/2896 for additional discussion.
     # Currently, CMake should be run by scikit-build through setup.py for proper Python packaging.
     # We don't want to install by default in the outer scope of the GROMACS
     # CMake procedure because we could end up trying to install to a system directory
@@ -176,24 +220,17 @@ else()
     # source distribution archive to be installed in the GROMACS installation
     # destination. We can use the build directory as the working directory for
     # easier clean-up, as well.
-    # TODO: (ref issue #2896) Build and install 'sdist' with GROMACS.
+    # TODO: (ref Issue #2896) Build and install 'sdist' with GROMACS.
 
     # However, we can still produce an importable package for documentation builds and
     # basic testing in ${CMAKE_CURRENT_BINARY_DIR}/gmxapi_staging
-    if(CMAKE_VERSION VERSION_LESS 3.12)
-        file(GLOB_RECURSE _py_sources
-             ${CMAKE_CURRENT_SOURCE_DIR}/gmxapi/*.py)
-    else()
-        # CONFIGURE_DEPENDS appears in CMake 3.12 and can help to more robustly detect
-        # the need to update anything depending on the staged package.
-        file(GLOB_RECURSE _py_sources
-             CONFIGURE_DEPENDS
-             ${CMAKE_CURRENT_SOURCE_DIR}/gmxapi/*.py)
-    endif()
+    file(GLOB_RECURSE _py_sources
+         CONFIGURE_DEPENDS
+         ${CMAKE_CURRENT_SOURCE_DIR}/gmxapi/*.py)
     foreach(_package_file IN LISTS _py_sources)
         get_filename_component(_absolute_dir ${_package_file} DIRECTORY)
         file(RELATIVE_PATH _relative_dir ${CMAKE_CURRENT_SOURCE_DIR} ${_absolute_dir})
-        file(COPY ${_package_file} DESTINATION gmxapi_staging/${_relative_dir})
+        file(COPY ${_package_file} DESTINATION ${GMXAPI_PYTHON_STAGING_DIR}/${_relative_dir})
     endforeach()
     file(COPY setup.py CMakeLists.txt DESTINATION ${GMXAPI_PYTHON_STAGING_DIR})
     # Set CMake variable pybind11_DIR to ${CMAKE_CURRENT_SOURCE_DIR}/external/pybind/tools
index 69890eb5e6eeb57e0604ac49d48fa390690bb19f..6d0c010e1c61a3ccd8af5be6be21e5579e172a20 100644 (file)
@@ -1,11 +1,11 @@
 ## pybind11
 
 For simplicity, the pybind headers were retrieved from 
-https://github.com/pybind/pybind11/archive/v2.4.3.tar.gz
+https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz
 
     git rm -rf pybind
     mkdir -p pybind
-    wget https://github.com/pybind/pybind11/archive/v2.4.3.tar.gz
+    wget https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz
     tar xvf *.tar.gz
     mv pybind11*/include pybind/
     mv pybind11*/tools pybind/
index 9f072fa738793cd4720504b71037df58e2a93179..1f4115a1fa312e9ad451180e5ae2794453505829 100644 (file)
@@ -21,14 +21,15 @@ struct buffer_info {
     std::string format;           // For homogeneous buffers, this should be set to format_descriptor<T>::format()
     ssize_t ndim = 0;             // Number of dimensions
     std::vector<ssize_t> shape;   // Shape of the tensor (1 entry per dimension)
-    std::vector<ssize_t> strides; // Number of entries between adjacent entries (for each per dimension)
+    std::vector<ssize_t> strides; // Number of bytes between adjacent entries (for each per dimension)
+    bool readonly = false;        // flag to indicate if the underlying storage may be written to
 
     buffer_info() { }
 
     buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
-                detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in)
+                detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
     : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
-      shape(std::move(shape_in)), strides(std::move(strides_in)) {
+      shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
         if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size())
             pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
         for (size_t i = 0; i < (size_t) ndim; ++i)
@@ -36,19 +37,23 @@ struct buffer_info {
     }
 
     template <typename T>
-    buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in)
-    : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor<T>::format(), static_cast<ssize_t>(shape_in->size()), std::move(shape_in), std::move(strides_in)) { }
+    buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
+    : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor<T>::format(), static_cast<ssize_t>(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { }
 
-    buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size)
-    : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { }
+    buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false)
+    : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { }
 
     template <typename T>
-    buffer_info(T *ptr, ssize_t size)
-    : buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size) { }
+    buffer_info(T *ptr, ssize_t size, bool readonly=false)
+    : buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) { }
+
+    template <typename T>
+    buffer_info(const T *ptr, ssize_t size, bool readonly=true)
+    : buffer_info(const_cast<T*>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) { }
 
     explicit buffer_info(Py_buffer *view, bool ownview = true)
     : buffer_info(view->buf, view->itemsize, view->format, view->ndim,
-            {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) {
+            {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}, view->readonly) {
         this->view = view;
         this->ownview = ownview;
     }
@@ -70,6 +75,7 @@ struct buffer_info {
         strides = std::move(rhs.strides);
         std::swap(view, rhs.view);
         std::swap(ownview, rhs.ownview);
+        readonly = rhs.readonly;
         return *this;
     }
 
@@ -81,8 +87,8 @@ private:
     struct private_ctr_tag { };
 
     buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
-                detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in)
-    : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { }
+                detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in, bool readonly)
+    : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { }
 
     Py_buffer *view = nullptr;
     bool ownview = false;
index 605acb36685f944ef4bd67dd99c638477f87cfd1..a0b4d1ba9e5b33d3b3b8b20fb66ad3b3187d6deb 100644 (file)
 #include <string_view>
 #endif
 
+#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L
+#  define PYBIND11_HAS_U8STRING
+#endif
+
 NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
 NAMESPACE_BEGIN(detail)
 
@@ -533,9 +537,17 @@ public:
             case return_value_policy::copy:
                 if (copy_constructor)
                     valueptr = copy_constructor(src);
-                else
-                    throw cast_error("return_value_policy = copy, but the "
-                                     "object is non-copyable!");
+                else {
+#if defined(NDEBUG)
+                    throw cast_error("return_value_policy = copy, but type is "
+                                     "non-copyable! (compile in debug mode for details)");
+#else
+                    std::string type_name(tinfo->cpptype->name());
+                    detail::clean_type_id(type_name);
+                    throw cast_error("return_value_policy = copy, but type " +
+                                     type_name + " is non-copyable!");
+#endif
+                }
                 wrapper->owned = true;
                 break;
 
@@ -544,9 +556,18 @@ public:
                     valueptr = move_constructor(src);
                 else if (copy_constructor)
                     valueptr = copy_constructor(src);
-                else
-                    throw cast_error("return_value_policy = move, but the "
-                                     "object is neither movable nor copyable!");
+                else {
+#if defined(NDEBUG)
+                    throw cast_error("return_value_policy = move, but type is neither "
+                                     "movable nor copyable! "
+                                     "(compile in debug mode for details)");
+#else
+                    std::string type_name(tinfo->cpptype->name());
+                    detail::clean_type_id(type_name);
+                    throw cast_error("return_value_policy = move, but type " +
+                                     type_name + " is neither movable nor copyable!");
+#endif
+                }
                 wrapper->owned = true;
                 break;
 
@@ -574,10 +595,10 @@ public:
             if (type->operator_new) {
                 vptr = type->operator_new(type->type_size);
             } else {
-                #if defined(PYBIND11_CPP17)
+                #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
                     if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
                         vptr = ::operator new(type->type_size,
-                                              (std::align_val_t) type->type_align);
+                                              std::align_val_t(type->type_align));
                     else
                 #endif
                 vptr = ::operator new(type->type_size);
@@ -780,12 +801,20 @@ template <typename Container> struct is_copy_constructible<Container, enable_if_
         negation<std::is_same<Container, typename Container::value_type>>
     >::value>> : is_copy_constructible<typename Container::value_type> {};
 
-#if !defined(PYBIND11_CPP17)
-// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the
-// two types aren't themselves copy constructible).
+// Likewise for std::pair
+// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves
+// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers).
 template <typename T1, typename T2> struct is_copy_constructible<std::pair<T1, T2>>
     : all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
-#endif
+
+// The same problems arise with std::is_copy_assignable, so we use the same workaround.
+template <typename T, typename SFINAE = void> struct is_copy_assignable : std::is_copy_assignable<T> {};
+template <typename Container> struct is_copy_assignable<Container, enable_if_t<all_of<
+        std::is_copy_assignable<Container>,
+        std::is_same<typename Container::value_type &, typename Container::reference>
+    >::value>> : is_copy_assignable<typename Container::value_type> {};
+template <typename T1, typename T2> struct is_copy_assignable<std::pair<T1, T2>>
+    : all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {};
 
 NAMESPACE_END(detail)
 
@@ -963,6 +992,9 @@ public:
 
 template <typename CharT> using is_std_char_type = any_of<
     std::is_same<CharT, char>, /* std::string */
+#if defined(PYBIND11_HAS_U8STRING)
+    std::is_same<CharT, char8_t>, /* std::u8string */
+#endif
     std::is_same<CharT, char16_t>, /* std::u16string */
     std::is_same<CharT, char32_t>, /* std::u32string */
     std::is_same<CharT, wchar_t> /* std::wstring */
@@ -1147,6 +1179,8 @@ public:
             if (res == 0 || res == 1) {
                 value = (bool) res;
                 return true;
+            } else {
+                PyErr_Clear();
             }
         }
         return false;
@@ -1164,6 +1198,9 @@ template <typename StringType, bool IsView = false> struct string_caster {
     // Simplify life by being able to assume standard char sizes (the standard only guarantees
     // minimums, but Python requires exact sizes)
     static_assert(!std::is_same<CharT, char>::value || sizeof(CharT) == 1, "Unsupported char size != 1");
+#if defined(PYBIND11_HAS_U8STRING)
+    static_assert(!std::is_same<CharT, char8_t>::value || sizeof(CharT) == 1, "Unsupported char8_t size != 1");
+#endif
     static_assert(!std::is_same<CharT, char16_t>::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2");
     static_assert(!std::is_same<CharT, char32_t>::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4");
     // wchar_t can be either 16 bits (Windows) or 32 (everywhere else)
@@ -1182,7 +1219,7 @@ template <typename StringType, bool IsView = false> struct string_caster {
 #if PY_MAJOR_VERSION >= 3
             return load_bytes(load_src);
 #else
-            if (sizeof(CharT) == 1) {
+            if (std::is_same<CharT, char>::value) {
                 return load_bytes(load_src);
             }
 
@@ -1242,7 +1279,7 @@ private:
     // without any encoding/decoding attempt).  For other C++ char sizes this is a no-op.
     // which supports loading a unicode from a str, doesn't take this path.
     template <typename C = CharT>
-    bool load_bytes(enable_if_t<sizeof(C) == 1, handle> src) {
+    bool load_bytes(enable_if_t<std::is_same<C, char>::value, handle> src) {
         if (PYBIND11_BYTES_CHECK(src.ptr())) {
             // We were passed a Python 3 raw bytes; accept it into a std::string or char*
             // without any encoding attempt.
@@ -1257,7 +1294,7 @@ private:
     }
 
     template <typename C = CharT>
-    bool load_bytes(enable_if_t<sizeof(C) != 1, handle>) { return false; }
+    bool load_bytes(enable_if_t<!std::is_same<C, char>::value, handle>) { return false; }
 };
 
 template <typename CharT, class Traits, class Allocator>
@@ -1395,9 +1432,14 @@ protected:
 
     template <size_t... Is>
     bool load_impl(const sequence &seq, bool convert, index_sequence<Is...>) {
+#ifdef __cpp_fold_expressions
+        if ((... || !std::get<Is>(subcasters).load(seq[Is], convert)))
+            return false;
+#else
         for (bool r : {std::get<Is>(subcasters).load(seq[Is], convert)...})
             if (!r)
                 return false;
+#endif
         return true;
     }
 
@@ -1924,14 +1966,19 @@ private:
 
     template <size_t... Is>
     bool load_impl_sequence(function_call &call, index_sequence<Is...>) {
+#ifdef __cpp_fold_expressions
+        if ((... || !std::get<Is>(argcasters).load(call.args[Is], call.args_convert[Is])))
+            return false;
+#else
         for (bool r : {std::get<Is>(argcasters).load(call.args[Is], call.args_convert[Is])...})
             if (!r)
                 return false;
+#endif
         return true;
     }
 
     template <typename Return, typename Func, size_t... Is, typename Guard>
-    Return call_impl(Func &&f, index_sequence<Is...>, Guard &&) {
+    Return call_impl(Func &&f, index_sequence<Is...>, Guard &&) && {
         return std::forward<Func>(f)(cast_op<Args>(std::move(std::get<Is>(argcasters)))...);
     }
 
index 230ae81ae8bfbaeddf6360543216a49202e9c920..edfa7de68c0177c37c9eb958bf473398d37c1d8f 100644 (file)
@@ -491,6 +491,13 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
     view->len = view->itemsize;
     for (auto s : info->shape)
         view->len *= s;
+    view->readonly = info->readonly;
+    if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
+        if (view)
+            view->obj = nullptr;
+        PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage");
+        return -1;
+    }
     if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
         view->format = const_cast<char *>(info->format.c_str());
     if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
index 579e2a5fa3e6ed9d3c198666544bfb7a4acc72e2..e53f502d6860d5e0dd4acbcc4fd7d61acb1a36be 100644 (file)
@@ -93,8 +93,8 @@
 #endif
 
 #define PYBIND11_VERSION_MAJOR 2
-#define PYBIND11_VERSION_MINOR 4
-#define PYBIND11_VERSION_PATCH 3
+#define PYBIND11_VERSION_MINOR 5
+#define PYBIND11_VERSION_PATCH 0
 
 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
 #if defined(_MSC_VER)
 #  endif
 #  pragma warning(push)
 #  pragma warning(disable: 4510 4610 4512 4005)
-#  if defined(_DEBUG)
+#  if defined(_DEBUG) && !defined(Py_DEBUG)
 #    define PYBIND11_DEBUG_MARKER
 #    undef _DEBUG
 #  endif
 #include <frameobject.h>
 #include <pythread.h>
 
+/* Python #defines overrides on all sorts of core functions, which
+   tends to weak havok in C++ codebases that expect these to work
+   like regular functions (potentially with several overloads) */
 #if defined(isalnum)
 #  undef isalnum
 #  undef isalpha
 #  undef toupper
 #endif
 
+#if defined(copysign)
+#  undef copysign
+#endif
+
 #if defined(_MSC_VER)
 #  if defined(PYBIND11_DEBUG_MARKER)
 #    define _DEBUG
@@ -211,6 +218,8 @@ extern "C" {
 #define PYBIND11_STRINGIFY(x) #x
 #define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x)
 #define PYBIND11_CONCAT(first, second) first##second
+#define PYBIND11_ENSURE_INTERNALS_READY \
+    pybind11::detail::get_internals();
 
 #define PYBIND11_CHECK_PYTHON_VERSION \
     {                                                                          \
@@ -257,6 +266,7 @@ extern "C" {
     static PyObject *pybind11_init();                                          \
     PYBIND11_PLUGIN_IMPL(name) {                                               \
         PYBIND11_CHECK_PYTHON_VERSION                                          \
+        PYBIND11_ENSURE_INTERNALS_READY                                        \
         try {                                                                  \
             return pybind11_init();                                            \
         } PYBIND11_CATCH_INIT_EXCEPTIONS                                       \
@@ -284,6 +294,7 @@ extern "C" {
     static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &);     \
     PYBIND11_PLUGIN_IMPL(name) {                                               \
         PYBIND11_CHECK_PYTHON_VERSION                                          \
+        PYBIND11_ENSURE_INTERNALS_READY                                        \
         auto m = pybind11::module(PYBIND11_TOSTRING(name));                    \
         try {                                                                  \
             PYBIND11_CONCAT(pybind11_init_, name)(m);                          \
@@ -341,7 +352,7 @@ enum class return_value_policy : uint8_t {
         object without taking ownership similar to the above
         return_value_policy::reference policy. In contrast to that policy, the
         function or property’s implicit this argument (called the parent) is
-        considered to be the owner of the return value (the child).
+        considered to be the the owner of the return value (the child).
         pybind11 then couples the lifetime of the parent to the child via a
         reference relationship that ensures that the parent cannot be garbage
         collected while Python is still using the child. More advanced
@@ -674,6 +685,7 @@ PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError)
 PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError)
 PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError)
 PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError)
+PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError)
 PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error
 PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally
 
index 067780c2604805b81a95bfae49b632911c39ed6c..6224dfb22632033a67c33ffaa33e09a1d4c842ba 100644 (file)
@@ -25,6 +25,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
 #    define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
 #    define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
 #    define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
+#    define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
 #else
     // Usually an int but a long on Cygwin64 with Python 3.x
 #    define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0
@@ -43,6 +44,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
 #        define PYBIND11_TLS_REPLACE_VALUE(key, value)                       \
              PyThread_set_key_value((key), (value))
 #    endif
+#    define PYBIND11_TLS_FREE(key) (void)key
 #endif
 
 // Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
@@ -108,6 +110,16 @@ struct internals {
 #if defined(WITH_THREAD)
     PYBIND11_TLS_KEY_INIT(tstate);
     PyInterpreterState *istate = nullptr;
+    ~internals() {
+        // This destructor is called *after* Py_Finalize() in finalize_interpreter().
+        // That *SHOULD BE* fine. The following details what happens whe PyThread_tss_free is called.
+        // PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing.
+        // PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree.
+        // PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither
+        // of those have anything to do with CPython internals.
+        // PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator.
+        PYBIND11_TLS_FREE(tstate);
+    }
 #endif
 };
 
@@ -138,7 +150,7 @@ struct type_info {
 };
 
 /// Tracks the `internals` and `type_info` ABI version independent of the main library version
-#define PYBIND11_INTERNALS_VERSION 3
+#define PYBIND11_INTERNALS_VERSION 4
 
 /// On MSVC, debug and release builds are not ABI-compatible!
 #if defined(_MSC_VER) && defined(_DEBUG)
@@ -211,6 +223,7 @@ inline void translate_exception(std::exception_ptr p) {
     } catch (const std::length_error &e)     { PyErr_SetString(PyExc_ValueError,    e.what()); return;
     } catch (const std::out_of_range &e)     { PyErr_SetString(PyExc_IndexError,    e.what()); return;
     } catch (const std::range_error &e)      { PyErr_SetString(PyExc_ValueError,    e.what()); return;
+    } catch (const std::overflow_error &e)   { PyErr_SetString(PyExc_OverflowError, e.what()); return;
     } catch (const std::exception &e)        { PyErr_SetString(PyExc_RuntimeError,  e.what()); return;
     } catch (...) {
         PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
index 72655885ebb3e496fb9887e3ef20c551dc939862..f814c783e7b072d93e6d9d098c61e8d7ecc75af8 100644 (file)
 
 #if PY_MAJOR_VERSION >= 3
 #  define PYBIND11_EMBEDDED_MODULE_IMPL(name)            \
+      extern "C" PyObject *pybind11_init_impl_##name();  \
       extern "C" PyObject *pybind11_init_impl_##name() { \
           return pybind11_init_wrapper_##name();         \
       }
 #else
 #  define PYBIND11_EMBEDDED_MODULE_IMPL(name)            \
+      extern "C" void pybind11_init_impl_##name();       \
       extern "C" void pybind11_init_impl_##name() {      \
           pybind11_init_wrapper_##name();                \
       }
index c6237056baa24166eee572744ed5b0c22f48c946..d95d61f7bb8c240e463cf3c6f4c673288039e3be 100644 (file)
@@ -1003,14 +1003,21 @@ void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); }
 
 inline void call_operator_delete(void *p, size_t s, size_t a) {
     (void)s; (void)a;
-#if defined(PYBIND11_CPP17)
-    if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
-        ::operator delete(p, s, std::align_val_t(a));
-    else
+    #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
+        if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+            #ifdef __cpp_sized_deallocation
+                ::operator delete(p, s, std::align_val_t(a));
+            #else
+                ::operator delete(p, std::align_val_t(a));
+            #endif
+            return;
+        }
+    #endif
+    #ifdef __cpp_sized_deallocation
         ::operator delete(p, s);
-#else
-    ::operator delete(p);
-#endif
+    #else
+        ::operator delete(p);
+    #endif
 }
 
 NAMESPACE_END(detail)
index 96eab9662c9681959e51887b215756f0ec569169..4003d6918492b82543e7a58a44594eea2f3b60dd 100644 (file)
@@ -1345,7 +1345,7 @@ public:
         buf.strides = py_strides.data();
         buf.shape = py_shape.data();
         buf.suboffsets = nullptr;
-        buf.readonly = false;
+        buf.readonly = info.readonly;
         buf.internal = nullptr;
 
         m_ptr = PyMemoryView_FromBuffer(&buf);
index d3adaed3a296077c26ee97435b1440b97dab6eb7..da233eca99ab10a223a0e526a55175bbe966ab6a 100644 (file)
@@ -136,6 +136,13 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
         return v.release();
     }));
 
+    cl.def("clear",
+        [](Vector &v) {
+            v.clear();
+        },
+        "Clear the contents"
+    );
+
     cl.def("extend",
        [](Vector &v, const Vector &src) {
            v.insert(v.end(), src.begin(), src.end());
@@ -512,7 +519,7 @@ template <typename, typename, typename... Args> void map_assignment(const Args &
 
 // Map assignment when copy-assignable: just copy the value
 template <typename Map, typename Class_>
-void map_assignment(enable_if_t<std::is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
+void map_assignment(enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
     using KeyType = typename Map::key_type;
     using MappedType = typename Map::mapped_type;
 
@@ -528,7 +535,7 @@ void map_assignment(enable_if_t<std::is_copy_assignable<typename Map::mapped_typ
 // Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting
 template<typename Map, typename Class_>
 void map_assignment(enable_if_t<
-        !std::is_copy_assignable<typename Map::mapped_type>::value &&
+        !is_copy_assignable<typename Map::mapped_type>::value &&
         is_copy_constructible<typename Map::mapped_type>::value,
         Class_> &cl) {
     using KeyType = typename Map::key_type;
index e660c5f3eb5e9cfda60e26b4a4efe06597532272..9ea6036e33d1606482cd500efc3c8d3dc5eecf92 100644 (file)
@@ -140,11 +140,11 @@ list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR)
 list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH)
 
 # Make sure all directory separators are '/'
-string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX ${PYTHON_PREFIX})
-string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR ${PYTHON_INCLUDE_DIR})
-string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES ${PYTHON_SITE_PACKAGES})
+string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX "${PYTHON_PREFIX}")
+string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
+string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES "${PYTHON_SITE_PACKAGES}")
 
-if(CMAKE_HOST_WIN32 AND NOT (MSYS OR MINGW))
+if(CMAKE_HOST_WIN32 AND NOT (MINGW AND DEFINED ENV{MSYSTEM}))
     set(PYTHON_LIBRARY
         "${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib")
 
index c7156c020c383b2c393f3a16168e45fb9fd1a5db..508e47429e3ef6c5f4419c2d1906ea0de706bbf1 100644 (file)
@@ -12,7 +12,7 @@ if(NOT PYBIND11_PYTHON_VERSION)
   set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules")
 endif()
 
-set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5 3.4)
+set(Python_ADDITIONAL_VERSIONS 3.9 3.8 3.7 3.6 3.5 3.4)
 find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED)
 
 include(CheckCXXCompilerFlag)
diff --git a/python_packaging/src/gmxapi/.gitignore b/python_packaging/src/gmxapi/.gitignore
new file mode 100644 (file)
index 0000000..fa2b630
--- /dev/null
@@ -0,0 +1 @@
+gmxconfig.json
index 890f225d22153b2f3e672a9a62b78222674691aa..35dd69c709ac24cd98882ad898f6b10b768578d5 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 
 """Python logging facilities use the built-in logging module.
 
-Upon import, the gmxapi package configures the root Python logger with a
-placeholder "NullHandler" to reduce default output. If logging has already been
-imported when gmxapi is imported, this has no effect. However, we set the root
-log level to DEBUG, which could increase the output from other modules.
+Upon import, the gmxapi package sets a placeholder "NullHandler" to block
+propagation of log messages to the root logger (and sys.stderr, if not handled).
+
+If you want to see gmxapi logging output on sys.stderr, attach a
+logging.StreamHandler to the 'gmxapi' logger.
+
+Example::
+
+    ch = logging.StreamHandler()
+    # Optional: Set log level.
+    ch.setLevel(logging.DEBUG)
+    # Optional: create formatter and add to character stream handler
+    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+    ch.setFormatter(formatter)
+    # add handler to logger
+    logging.getLogger('gmxapi').addHandler(ch)
+
+To handle log messages that are issued while importing :py:mod:`gmxapi` and its submodules,
+attach the handler before importing :py:mod:`gmxapi`
 
 Each module in the gmxapi package uses its own hierarchical logger to allow
 granular control of log handling (e.g. ``logging.getLogger('gmxapi.operation')``).
@@ -51,9 +66,8 @@ __all__ = ['logger']
 from logging import getLogger, DEBUG, NullHandler
 
 # Define `logger` attribute that is used by submodules to create sub-loggers.
-getLogger().addHandler(NullHandler(level=DEBUG))
-getLogger().setLevel(DEBUG)
-getLogger().info("Setting up logging for gmxapi package.")
 logger = getLogger('gmxapi')
-logger.setLevel(DEBUG)
+# Prevent gmxapi logs from propagating to the root logger (and to sys.stderr)
+# if the user does not take action to handle logging.
+logger.addHandler(NullHandler(level=DEBUG))
 logger.info("Importing gmxapi.")
index b881e67b7fb321a3323de20821b9df0bf156f068..0166dcebe13d56e1413aefabea6a3158c377f076 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 #
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
+
 """
 Provide command line operation.
 """
 
 __all__ = ['commandline_operation']
 
+import os
 import shutil
 import subprocess
 
@@ -69,7 +71,7 @@ logger.info('Importing {}'.format(__name__))
 # TODO: Operation returns the output object when called with the shorter signature.
 #
 @gmx.function_wrapper(output={'erroroutput': str, 'returncode': int})
-def cli(command: NDArray, shell: bool, output: OutputCollectionDescription):
+def cli(command: NDArray, shell: bool, output: OutputCollectionDescription, stdin: str = ''):
     """Execute a command line program in a subprocess.
 
     Configure an executable in a subprocess. Executes when run in an execution
@@ -87,6 +89,16 @@ def cli(command: NDArray, shell: bool, output: OutputCollectionDescription):
          command: a tuple (or list) to be the subprocess arguments, including `executable`
          output: mapping of command line flags to output filename arguments
          shell: unused (provides forward-compatibility)
+         stdin (str): String input to send to STDIN (terminal input) of the executable.
+
+    Multi-line text sent to *stdin* should be joined into a single string
+    (e.g. ``'\n'.join(list_of_strings) + '\n'``).
+    If multiple strings are provided to *stdin*, gmxapi will assume an ensemble,
+    and will run one operation for each provided string.
+
+    Only string input (:py:func:str) to *stdin* is currently supported.
+    If you have a use case that requires streaming input or binary input,
+    please open an issue or contact the author(s).
 
     Arguments are iteratively added to the command line with standard Python
     iteration, so you should use a tuple or list even if you have only one parameter.
@@ -121,22 +133,23 @@ def cli(command: NDArray, shell: bool, output: OutputCollectionDescription):
     # * STDOUT is available if a consuming operation is bound to `output.stdout`.
     # * STDERR is available if a consuming operation is bound to `output.stderr`.
     # * Otherwise, STDOUT and/or STDERR is(are) closed when command is called.
-    #
-    # Warning:
-    #     Commands relying on STDIN cannot be used and is closed when command is called.
 
     # In the operation implementation, we expect the `shell` parameter to be intercepted by the
     # wrapper and set to False.
     if shell:
         raise exceptions.UsageError("Operation does not support shell processing.")
 
+    if stdin == '':
+        stdin = None
+
     if isinstance(command, (str, bytes)):
         command = [command]
     command = list([arg for arg in command])
-    try:
-        command[0] = shutil.which(command[0])
-    except Exception:
-        raise exceptions.ValueError('command argument could not be resolved to an executable file path.')
+
+    executable = shutil.which(command[0])
+    if executable is None:
+        raise exceptions.ValueError('"{}" is not found or not executable.'.format(command[0]))
+    command[0] = executable
 
     # TODO: (FR9) Can OS input/output filehandles be a responsibility of
     #  the code providing 'resources'?
@@ -144,31 +157,26 @@ def cli(command: NDArray, shell: bool, output: OutputCollectionDescription):
     erroroutput = ''
     logger.debug('executing subprocess')
     try:
-        # TODO: If Python >=3.5 is required, switch to subprocess.run()
-        command_output = subprocess.check_output(command,
-                                                 shell=shell,
-                                                 stdin=subprocess.DEVNULL,
-                                                 stderr=subprocess.STDOUT,
-                                                 )
-        returncode = 0
+        completed_process = subprocess.run(command,
+                                           shell=shell,
+                                           input=stdin,
+                                           check=True,
+                                           stdout=subprocess.PIPE,
+                                           stderr=subprocess.STDOUT,
+                                           encoding='utf-8',
+                                           universal_newlines=True
+                                           )
+        returncode = completed_process.returncode
         # TODO: Resource management code should manage a safe data object for `output`.
-        # WARNING: We have no reason to assume the output is utf-8 encoded text!!!
-        for line in command_output.decode('utf-8').split('\n'):
+        for line in completed_process.stdout.split('\n'):
             logger.debug(line)
     except subprocess.CalledProcessError as e:
         logger.info("commandline operation had non-zero return status when calling {}".format(e.cmd))
-        erroroutput = e.output.decode('utf-8')
+        erroroutput = e.output
         returncode = e.returncode
-    # resources.output.erroroutput.publish(erroroutput)
-    # resources.output.returncode.publish(returncode)
-    # `publish` is descriptive, but redundant. Access to the output data handler is
-    # assumed to coincide with publishing, and we assume data is published when the
-    # handler is released. A class with a single `publish` method is overly complex
-    # since we can just use the assignment operator.
+    # Publish outputs.
     output.erroroutput = erroroutput
     output.returncode = returncode
-    # TODO: Handle the file output at the higher level wrapper.
-    # output.file = None
 
 
 # TODO: (FR4) Make this a formal operation to properly handle gmxapi data dependencies.
@@ -214,6 +222,7 @@ def commandline_operation(executable=None,
                           arguments=(),
                           input_files: dict = None,
                           output_files: dict = None,
+                          stdin: str = None,
                           **kwargs):
     """Helper function to define a new operation that executes a subprocess in gmxapi data flow.
 
@@ -226,9 +235,23 @@ def commandline_operation(executable=None,
         arguments: list of positional arguments to insert at ``argv[1]``
         input_files: mapping of command-line flags to input file names
         output_files: mapping of command-line flags to output file names
+        stdin (str): String input to send to STDIN (terminal input) of the executable (optional).
+
+    Multi-line text sent to *stdin* should be joined into a single string.
+    E.g.::
+
+        commandline_operation(..., stdin='\\n'.join(list_of_strings) + '\\n')
+
+    If multiple strings are provided to *stdin*, gmxapi will assume an ensemble,
+    and will run one operation for each provided string.
+
+    Only string input (:py:func:`str`) to *stdin* is currently supported.
+    If you have a use case that requires streaming input or binary input,
+    please open an issue or contact the author(s).
 
     Output:
         The output node of the resulting operation handle contains
+
         * ``file``: the mapping of CLI flags to filename strings resulting from the ``output_files`` kwarg
         * ``erroroutput``: A string of error output (if any) if the process failed.
         * ``returncode``: return code of the subprocess.
@@ -268,9 +291,12 @@ def commandline_operation(executable=None,
         assert returncode is not None
         assert file is not None
         assert output is not None
-        output.file = file
         output.returncode = returncode
         output.erroroutput = erroroutput
+        if returncode == 0:
+            output.file = file
+        else:
+            output.file = {}
 
     ##
     # 2. Prepare data flow.
@@ -289,6 +315,8 @@ def commandline_operation(executable=None,
     cli_args = {'command': command,
                 'shell': shell}
     cli_args.update(**kwargs)
+    if stdin is not None:
+        cli_args['stdin'] = str(stdin)
 
     ##
     # 3. Merge operations
index ee308f90b89bbcd0faf1a137e82197563d466577..c9af75092cfd2dacaa09260044e23431f866d4c0 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index b991398ea7dac4ae286367ea77fdbe46fb922c67..f618f052b7892a6de044ab9025401ac85069d658 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -65,7 +65,7 @@ namespace py = pybind11;
  * Note that the current library infrastructure does not provide a way for the
  * simulation machinery to express human-readable parameter names with rich
  * descriptions, so a few of the most necessary mdrun command line parameters
- * are hard coded here. Ref. https://redmine.gromacs.org/issues/2877
+ * are hard coded here. Ref. https://gitlab.com/gromacs/gromacs/-/issues/2877
  *
  * For reference and default values, see
  * http://manual.gromacs.org/current/onlinehelp/gmx-mdrun.html#options
index 6be962bfaaf4ff97ab571b72fd1c2e0dd0de4612..9a80297e459928f0db126b11b671282e37c6fd65 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -84,7 +84,7 @@ void detail::export_tprfile(pybind11::module& module)
                              // Less trivial types (strings, maps, arrays) warrant additional
                              // design discussion before being exposed through an interface
                              // like this one.
-                             // Also reference https://redmine.gromacs.org/issues/2993
+                             // Also reference https://gitlab.com/gromacs/gromacs/-/issues/2993
 
                              // We can use templates and/or tag dispatch in a more complete
                              // future implementation.
diff --git a/python_packaging/src/gmxapi/gmxconfig.json.in b/python_packaging/src/gmxapi/gmxconfig.json.in
new file mode 100644 (file)
index 0000000..e56f0de
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "gmx_executable": "@_gmx_executable@",
+    "gmx_bindir": "@_gmx_bindir@",
+    "gmx_mpi_type": @_gmx_mpi_type@
+}
index 4ed554bf020b9098d48e17ba1214ca6feb1678c3..c80b017592252b0e1ff40b2f623694855a9818be 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -1792,18 +1792,21 @@ class ResourceManager(SourceResource[_OutputDataProxyType, _PublishingDataProxyT
         # gets used correctly.
 
         # ref: https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager
+        if self._done[ensemble_member]:
+            raise exceptions.ProtocolError('Attempting to publish {}[{}] more than once.'.format(self.operation_id, ensemble_member))
+
         try:
-            if not self._done[ensemble_member]:
-                resource = self.__publishing_data_proxy(instance=weakref.proxy(self),
-                                                        client_id=ensemble_member)
-                yield resource
+            resource = self.__publishing_data_proxy(instance=weakref.proxy(self),
+                                                    client_id=ensemble_member)
         except Exception as e:
-            message = 'Uncaught {} while providing output-publishing resources for {}.'
-            message.format(repr(e), self.operation_id)
-            raise exceptions.ApiError(message) from e
-        finally:
-            logger.debug('Published output for {} member {}'.format(self.operation_id, ensemble_member))
-            self._done[ensemble_member] = True
+            logger.debug('Publishing context could not be created due to {}'.format(e))
+            raise e
+
+        yield resource
+        # Note: The remaining lines are skipped if an exception occurs in the `with` block
+        # for the contextmanager suite, which effectively raises at the line after 'yield'.
+        logger.debug('Published output for {} member {}'.format(self.operation_id, ensemble_member))
+        self._done[ensemble_member] = True
 
     def __init__(self, *,
                  source: DataEdge,
@@ -1920,6 +1923,11 @@ class ResourceManager(SourceResource[_OutputDataProxyType, _PublishingDataProxyT
         Used internally to implement Futures for the local operation
         associated with this resource manager.
 
+        Raises:
+            exceptions.ApiError if operation runner fails to publish output.
+
+        TODO: More comprehensive error handling for operations that fail to execute.
+
         TODO: We need a different implementation for an operation whose output
          is served by multiple resource managers. E.g. an operation whose output
          is available across the ensemble, but which should only be executed on
@@ -1974,11 +1982,23 @@ class ResourceManager(SourceResource[_OutputDataProxyType, _PublishingDataProxyT
                             # option 1: Make the input and output resources with separate factories and add_resource on
                             # the runner builder.
                             # option 2: Pass resource_builder to input_director and then output_director.
-                            resources = self._resource_factory(input=input, output=output)
+                            error_message = 'Got {} while executing {} for operation {}.'
+                            try:
+                                resources = self._resource_factory(input=input, output=output)
+                            except exceptions.TypeError as e:
+                                message = error_message.format(e, self._resource_factory, self.operation_id)
+                                raise exceptions.ApiError(message) from e
+
                             runner = self._runner_director(resources)
-                            runner()
+                            try:
+                                runner()
+                            except Exception as e:
+                                message = error_message.format(e, runner, self.operation_id)
+                                raise exceptions.ApiError(message) from e
             if not self.done():
-                raise exceptions.ApiError('update_output implementation failed to update all outputs.')
+                message = 'update_output implementation failed to update all outputs for {}.'
+                message = message.format(self.operation_id)
+                raise exceptions.ApiError(message)
 
     def future(self, name: str, description: ResultDescription):
         """Retrieve a Future for a named output.
@@ -2797,6 +2817,9 @@ def function_wrapper(output: dict = None):
 
                 For the Python Context, the protocol is for the Context to call the
                 resource_director instance method, passing input and output containers.
+
+                Raises:
+                    exceptions.TypeError if provided resource type does not match input signature.
                 """
                 resources = PyFunctionRunnerResources()
                 resources.update(input.kwargs)
@@ -2813,7 +2836,11 @@ def function_wrapper(output: dict = None):
                         expected = cls.signature()[name]
                         got = type(value)
                         if got != expected:
-                            raise exceptions.TypeError('Expected {} but got {}.'.format(expected, got))
+                            raise exceptions.TypeError(
+                                'Expected {} but got {} for {} resource {}.'.format(expected,
+                                                                                    got,
+                                                                                    cls.__basename,
+                                                                                    name))
                 return resources
 
         # TODO: (FR4) Update annotations with gmxapi data types. E.g. return -> Future.
index 797c7a2c95256967d610fbc0691aba6e8e8be690..5b9878efbb3d6d7ea8064cfbf3d6750a43d6cf60 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -74,7 +74,7 @@ std::shared_ptr<gmxapi::Context> PyContext::get() const
 }
 
 PyContext::PyContext() :
-    context_{ std::make_shared<gmxapi::Context>() },
+    context_{ std::make_shared<gmxapi::Context>(gmxapi::createContext()) },
     workNodes_{ std::make_shared<gmxapi::MDWorkSpec>() }
 {
     assert(context_);
index 066d40c4c09744f90b244b2226576446d700335c..ed4d1229d6e950d1d91731974518148c12a4ef02 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -261,8 +261,11 @@ class LegacyImplementationSubscription(object):
                                                                       ensemble_rank,
                                                                       self.workdir
                                                                       ))
-                    # TODO: We have not exposed the ability to pass any run time parameters to mdrun.
-                    work = workflow.from_tpr(tpr_filenames)
+                    # TODO: (#3718) Normalize the way we pass run time parameters to mdrun.
+                    kwargs = getattr(resource_manager, 'mdrun_kwargs', {})
+                    for key, value in kwargs.items():
+                        logger.debug('Adding mdrun run time argument: {}'.format(key + '=' + str(value)))
+                    work = workflow.from_tpr(tpr_filenames, **kwargs)
                     self.workspec = work.workspec
                     context = LegacyContext(work=self.workspec, workdir_list=workdir_list, communicator=ensemble_comm)
                     self.simulation_module_context = context
index c610453a68ab70fceb6fcc546b5abacbb808d910..0ffdf02435a072d40ab230577a18cc289205fb94 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 
 """Reusable definitions for test modules.
 
-Define the ``withmpi_only`` test decorator.
+Provides utilities and pytest fixtures for gmxapi and GROMACS tests.
+
+To load these facilities in a pytest environment, set a `pytest_plugins`
+variable in a conftest.py
+(Reference https://docs.pytest.org/en/latest/writing_plugins.html#requiring-loading-plugins-in-a-test-module-or-conftest-file)
+
+    pytest_plugins = "gmxapi.testsupport"
+
+.. seealso:: https://docs.pytest.org/en/latest/plugins.html#findpluginname
 
 .. todo:: Consider moving this to a separate optional package.
 """
 
+import json
+import logging
+import os
+import shutil
+import tempfile
+import warnings
+from contextlib import contextmanager
+from enum import Enum
+from typing import Union
+
 import pytest
 
-mpi_requirement = 'Test requires mpi4py managing 2 MPI ranks.'
+mpi_status = 'Test requires mpi4py managing 2 MPI ranks.'
+skip_mpi = False
 try:
     from mpi4py import MPI
 
     if not MPI.Is_initialized():
         skip_mpi = True
-        reason = mpi_requirement + ' MPI is not initialized'
+        mpi_status += ' MPI is not initialized'
     elif MPI.COMM_WORLD.Get_size() < 2:
         skip_mpi = True
-        reason = mpi_requirement + ' MPI context is too small.'
-    else:
-        skip_mpi = False
-        reason = ''
-    withmpi_only = pytest.mark.skipif(skip_mpi, reason=reason)
+        mpi_status += ' MPI context is too small.'
 except ImportError:
-    withmpi_only = pytest.mark.skip(
-        reason=mpi_requirement + ' mpi4py is not available.')
+    skip_mpi = True
+    mpi_status += ' mpi4py is not available.'
+
+
+def pytest_configure(config):
+    config.addinivalue_line("markers", "withmpi_only: test requires mpi4py managing 2 MPI ranks.")
+
+
+def pytest_runtest_setup(item):
+    # Handle the withmpi_only marker.
+    for _ in item.iter_markers(name='withmpi_only'):
+        if skip_mpi:
+            pytest.skip(mpi_status)
+        # The API uses iteration because markers may be duplicated, but we only
+        # care about whether 'withmpi_only' occurs at all.
+        break
+
+
+def pytest_addoption(parser):
+    """Add command-line user options for the pytest invocation."""
+    parser.addoption(
+        '--rm',
+        action='store',
+        default='always',
+        choices=['always', 'never', 'success'],
+        help='Remove temporary directories "always", "never", or on "success".'
+    )
+    parser.addoption(
+        '--threads',
+        type=int,
+        help='Maximum number of threads per process per gmxapi session.'
+    )
+
+
+class RmOption(Enum):
+    """Enumerate allowable values of the --rm option."""
+    always = 'always'
+    never = 'never'
+    success = 'success'
+
+
+@pytest.fixture(scope='session')
+def remove_tempdir(request) -> RmOption:
+    """pytest fixture to get access to the --rm CLI option."""
+    arg = request.config.getoption('--rm')
+    return RmOption(arg)
+
+@pytest.fixture(scope='session')
+def gmxconfig():
+    try:
+        from importlib.resources import open_text
+        with open_text('gmxapi', 'gmxconfig.json') as textfile:
+            config = json.load(textfile)
+    except ImportError:
+        # TODO: Remove this when we require Python 3.7
+        try:
+            # A backport of importlib.resources is available as importlib_resources
+            # with a somewhat different interface.
+            from importlib_resources import files, as_file
+
+            source = files('gmxapi').joinpath('gmxconfig.json')
+            with as_file(source) as gmxconfig:
+                with open(gmxconfig, 'r') as fp:
+                    config = json.load(fp)
+        except ImportError:
+            config = None
+    yield config
+
+@pytest.fixture(scope='session')
+def mdrun_kwargs(request, gmxconfig):
+    """pytest fixture to provide a mdrun_kwargs dictionary for the mdrun ResourceManager.
+    """
+    from gmxapi.simulation.mdrun import ResourceManager as _ResourceManager
+    if gmxconfig is None:
+        raise RuntimeError('--threads argument requires a usable gmxconfig.json')
+    arg = request.config.getoption('--threads')
+    if arg is None:
+        return {}
+    mpi_type = gmxconfig['gmx_mpi_type']
+    if mpi_type is not None and mpi_type == "tmpi":
+        kwargs = {'threads': int(arg)}
+    else:
+        kwargs = {}
+    # TODO: (#3718) Normalize the handling of run-time arguments.
+    _ResourceManager.mdrun_kwargs = dict(**kwargs)
+    return kwargs
+
+
+@contextmanager
+def scoped_chdir(dir):
+    oldpath = os.getcwd()
+    os.chdir(dir)
+    try:
+        yield dir
+        # If the `with` block using scoped_chdir produces an exception, it will
+        # be raised at this point in this function. We want the exception to
+        # propagate out of the `with` block, but first we want to restore the
+        # original working directory, so we skip `except` but provide a `finally`.
+    finally:
+        os.chdir(oldpath)
+
+
+@contextmanager
+def _cleandir(remove_tempdir: Union[str, RmOption]):
+    """Context manager for a clean temporary working directory.
+
+    Arguments:
+        remove_tempdir (RmOption): whether to remove temporary directory "always",
+                                   "never", or on "success"
+
+    Raises:
+        ValueError: if remove_tempdir value is not valid.
+
+    The context manager will issue a warning for each temporary directory that
+    is not removed.
+    """
+    if not isinstance(remove_tempdir, RmOption):
+        remove_tempdir = RmOption(remove_tempdir)
+
+    newpath = tempfile.mkdtemp()
+
+    def remove():
+        shutil.rmtree(newpath)
+
+    def warn():
+        warnings.warn('Temporary directory not removed: {}'.format(newpath))
+
+    # Initialize callback function reference
+    if remove_tempdir == RmOption.always:
+        callback = remove
+    else:
+        callback = warn
+
+    try:
+        with scoped_chdir(newpath):
+            yield newpath
+        # If we get to this line, the `with` block using _cleandir did not throw.
+        # Clean up the temporary directory unless the user specified `--rm never`.
+        # I.e. If the user specified `--rm success`, then we need to toggle from `warn` to `remove`.
+        if remove_tempdir != RmOption.never:
+            callback = remove
+    finally:
+        callback()
+
+
+@pytest.fixture
+def cleandir(remove_tempdir: RmOption):
+    """Provide a clean temporary working directory for a test.
+
+    Example usage:
+
+        import os
+        import pytest
+
+        @pytest.mark.usefixtures("cleandir")
+        def test_cwd_starts_empty():
+            assert os.listdir(os.getcwd()) == []
+            with open("myfile", "w") as f:
+                f.write("hello")
+
+        def test_cwd_also_starts_empty(cleandir):
+            assert os.listdir(os.getcwd()) == []
+            assert os.path.abspath(os.getcwd()) == os.path.abspath(cleandir)
+            with open("myfile", "w") as f:
+                f.write("hello")
+
+        @pytest.mark.usefixtures("cleandir")
+        class TestDirectoryInit(object):
+            def test_cwd_starts_empty(self):
+                assert os.listdir(os.getcwd()) == []
+                with open("myfile", "w") as f:
+                    f.write("hello")
+
+            def test_cwd_also_starts_empty(self):
+                assert os.listdir(os.getcwd()) == []
+                with open("myfile", "w") as f:
+                    f.write("hello")
+
+    Ref: https://docs.pytest.org/en/latest/fixture.html#using-fixtures-from-classes-modules-or-projects
+    """
+    with _cleandir(remove_tempdir) as newdir:
+        yield newdir
+
+
+class GmxBin:
+    """Represent the detected GROMACS installation."""
+    def __init__(self, gmxconfig):
+        # Try to use package resources to locate the "gmx" binary wrapper.
+        if gmxconfig is not None:
+            gmxbindir = gmxconfig.get('gmx_bindir', None)
+            command = gmxconfig.get('gmx_executable', None)
+        else:
+            gmxbindir = None
+            command = None
+
+        # TODO: Remove fall-back when we can rely on gmxconfig.json via importlib.resources in Py 3.7+.
+        allowed_command_names = ['gmx', 'gmx_mpi']
+        for command_name in allowed_command_names:
+            if command is not None:
+                break
+            command = shutil.which(command_name)
+            if command is None:
+                gmxbindir = os.getenv('GMXBIN')
+                if gmxbindir is None:
+                    gromacsdir = os.getenv('GROMACS_DIR')
+                    if gromacsdir is not None and gromacsdir != '':
+                        gmxbindir = os.path.join(gromacsdir, 'bin')
+                if gmxbindir is None:
+                    gmxapidir = os.getenv('gmxapi_DIR')
+                    if gmxapidir is not None and gmxapidir != '':
+                        gmxbindir = os.path.join(gmxapidir, 'bin')
+                if gmxbindir is not None:
+                    gmxbindir = os.path.abspath(gmxbindir)
+                    command = shutil.which(command_name, path=gmxbindir)
+
+        self._command = command
+        self._bindir = gmxbindir
+
+    def command(self):
+        return self._command
+
+    def bindir(self):
+        return self._bindir
+
+
+@pytest.fixture(scope='session')
+def gmxcli(gmxconfig):
+    command = GmxBin(gmxconfig).command()
+    if command is None:
+        message = "Tests need 'gmx' command line tool, but could not find it on the path."
+        raise RuntimeError(message)
+    try:
+        assert os.access(command, os.X_OK)
+    except Exception as E:
+        raise RuntimeError('"{}" is not an executable gmx wrapper program'.format(command)) from E
+    yield command
index 5a4e929b66fefee6c693bb4fee239a6c30b4abc3..3d2465d0272dcfd6afb95d65ed7873509157e53e 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 """
-Provide version and release information.
+gmxapi version and release information.
 
-Attributes:
-    major (int): gmxapi major version number.
-    minor (int): gmxapi minor version number.
-    patch (int): gmxapi patch level number.
-    release (bool): True if imported gmx module is an officially tagged release, else False.
+The ``gmxapi.__version__`` attribute contains a :pep:`version string <440>`.
+The more general way to access the package version is with the
+:py:mod:`pkg_resources <https://setuptools.readthedocs.io/en/latest/pkg_resources.html>` module::
 
-"""
-import warnings
+    pkg_resources.get_distribution('gmxapi').version
+
+`gmxapi.version` module functions `api_is_at_least()` and `has_feature()`
+support additional convenience and introspection.
+
+.. versionchanged:: 0.2
+
+    This module no longer provides public data attributes.
+    Instead, use the module functions or
+    :py:mod:`packaging.version <https://packaging.pypa.io/en/latest/version/>`.
 
-__version__ = "0.1.0"
+.. seealso::
 
-# TODO: (pending infrastructure and further discussion) Configure with CMake.
-# __version__ = "@PROJECT_VERSION@"
-# major = @PROJECT_VERSION_MAJOR@
-# minor = @PROJECT_VERSION_MINOR@
-# patch = @PROJECT_VERSION_PATCH@
+    Consider https://packaging.pypa.io/en/latest/version/ for programmatic
+    handling of the version string. For example::
+
+        from packaging.version import parse
+        gmxapi_version = pkg_resources.get_distribution('gmxapi').version
+        if parse(gmxapi_version).is_prerelease:
+            print('The early bird gets the worm.')
+
+.. todo:: Use pkg_resources.get_distribution('gmxapi').version and
+          "development installations" instead of relying on or publicizing
+          a __version__ attribute.
+"""
+import warnings
 
-from gmxapi.exceptions import FeatureNotAvailableError
+from .exceptions import FeatureNotAvailableError
 
-major = 0
-minor = 1
-patch = 0
+# TODO: Version management policy and procedures.
+_major = 0
+_minor = 2
+_micro = 0
+_suffix = 'b1'
 
-# Note: this is not automatically updated. See RELEASE.txt and https://github.com/kassonlab/gmxapi/issues/152
-release = True
+# Reference https://www.python.org/dev/peps/pep-0440/
+# and https://packaging.pypa.io/en/latest/version/
+__version__ = '{major}.{minor}.{micro}{suffix}'.format(major=_major,
+                                                       minor=_minor,
+                                                       micro=_micro,
+                                                       suffix=_suffix)
 
 # Features added since the initial gmxapi prototype, targeted for version 0.1.
 _named_features_0_0 = ['fr1', 'fr3', 'fr7', 'fr15']
@@ -77,7 +97,7 @@ _named_features_0_1 = []
 # Bugs and bug fixes may be indicated with names consisting of tracked issue URLs.
 #
 # Features consisting of 'fr' and a numeric suffix are the functional requirements
-# described in roadmap.rst, as described at https://redmine.gromacs.org/issues/2893
+# described in roadmap.rst, as described at https://gitlab.com/gromacs/gromacs/-/issues/2893
 #
 # fr1: wrap importable Python code.
 # fr2: output proxy establishes execution dependency (superseded by fr3)
@@ -119,11 +139,11 @@ def api_is_at_least(major_version, minor_version=0, patch_version=0):
     """
     if not isinstance(major_version, int) or not isinstance(minor_version, int) or not isinstance(patch_version, int):
         raise TypeError('Version levels must be provided as integers.')
-    if major > major_version:
+    if _major > major_version:
         return True
-    elif major == major_version and minor >= minor_version:
+    elif _major == major_version and _minor >= minor_version:
         return True
-    elif major == major_version and minor == minor_version and patch >= patch_version:
+    elif _major == major_version and _minor == minor_version and _micro >= patch_version:
         return True
     else:
         return False
@@ -172,7 +192,8 @@ def has_feature(name='', enable_exception=False) -> bool:
         if name in _named_features_0_0:
             warnings.warn(
                 'Old feature name. Use `api_is_at_least(0, 1)` instead of `has_feature({})`.'.format(name),
-                category=DeprecationWarning
+                category=DeprecationWarning,
+                stacklevel=2
             )
 
     # Check whether the feature is listed in the API specification amendments.
diff --git a/python_packaging/src/pyproject.toml b/python_packaging/src/pyproject.toml
new file mode 100644 (file)
index 0000000..b0e0693
--- /dev/null
@@ -0,0 +1,6 @@
+[build-system]
+requires = ['cmake>=3.13',
+            'scikit-build>=0.10',
+            'setuptools>=42',
+            'wheel']
+build-backend = 'setuptools.build_meta:__legacy__'
index 9358e62014065417282940fc19708112eb71cf49..d75b37054d2482c2b3fd86f0841be9d640c2a376 100644 (file)
@@ -1,6 +1,17 @@
-cmake>=3.12
-pip>=10.1
+# Requirements for build, installation and testing are provided for convenient
+# use of `pip install -r requirements.txt`. To get a complete list of required
+# packages, such as for completely offline installation, install these pacakges
+# in an empty Python virtual environment and use `pip list` or `pip freeze` to
+# see the actual packages installed, including dependencies, with version info.
+#
+# Note that, ordinarily, build requirements are discovered from pyproject.toml,
+# and run time package dependencies (to be checked during installation) are
+# resolved by setuptools from the `install_requires` argument to setup() in
+# setup.py.
+cmake>=3.13
 networkx>=2.0
 numpy>=1
-setuptools>=28.0.0
-scikit-build>=0.7
+pip>=10.1
+scikit-build>=0.10
+setuptools>=42
+wheel
index c4a2c487484b28413c307158af673640fa7a72d0..60002a49aa7b87eefe3541bde9594a4f71c914b8 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 
 import os
 
-from skbuild import setup
+# Allow setup.py to be run when scikit-build is not installed, such as to
+# produce source distribution archives with `python setup.py sdist`
+try:
+    from skbuild import setup
+except ImportError:
+    from distutils.core import setup
 
 usage = """
 The `gmxapi` package requires an existing GROMACS installation, version 2020 or higher.
@@ -149,14 +154,14 @@ cmake_args = [cmake_platform_hints, cmake_gmxapi_hint]
 setup(
     name='gmxapi',
 
-    # TODO: (pending infrastructure and further discussion) Replace with CMake variables from GMXAPI version.
-    version='0.1.0.1',
-    python_requires='>=3.5, <3.9',
-    setup_requires=['cmake>=3.12',
-                    'setuptools>=28',
-                    'scikit-build>=0.7'],
+    # TODO: single-source version information (currently repeated in gmxapi/version.py)
+    version='0.2.0b1',
+    python_requires='>=3.6',
+    install_requires=['networkx>=2.0',
+                      'numpy>=1'],
 
     packages=['gmxapi', 'gmxapi.simulation'],
+    package_data={'gmxapi': ['gmxconfig.json']},
 
     cmake_args=cmake_args,
 
index db5da48a4397a0a124740d355ac25bce60a3c17e..aa9d69a2b0ecfd277c288873b86fd667f735447f 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 import json
 import logging
 import os
-import shutil
-import tempfile
-import warnings
-from contextlib import contextmanager
-
 import pytest
 
-
-def pytest_addoption(parser):
-    """Add a command-line user option for the pytest invocation."""
-    parser.addoption(
-        '--rm',
-        action='store',
-        default='always',
-        choices=['always', 'never', 'success'],
-        help='Remove temporary directories "always", "never", or on "success".'
-    )
-
-
-@pytest.fixture(scope='session')
-def remove_tempdir(request):
-    """pytest fixture to get access to the --rm CLI option."""
-    return request.config.getoption('--rm')
-
-
-@contextmanager
-def scoped_chdir(dir):
-    oldpath = os.getcwd()
-    os.chdir(dir)
-    try:
-        yield dir
-        # If the `with` block using scoped_chdir produces an exception, it will
-        # be raised at this point in this function. We want the exception to
-        # propagate out of the `with` block, but first we want to restore the
-        # original working directory, so we skip `except` but provide a `finally`.
-    finally:
-        os.chdir(oldpath)
-
-
-@contextmanager
-def _cleandir(remove_tempdir):
-    """Context manager for a clean temporary working directory.
-
-    Arguments:
-        remove_tempdir (str): whether to remove temporary directory "always",
-                              "never", or on "success"
-
-    The context manager will issue a warning for each temporary directory that
-    is not removed.
-    """
-
-    newpath = tempfile.mkdtemp()
-
-    def remove():
-        shutil.rmtree(newpath)
-
-    def warn():
-        warnings.warn('Temporary directory not removed: {}'.format(newpath))
-
-    if remove_tempdir == 'always':
-        callback = remove
-    else:
-        callback = warn
-    try:
-        with scoped_chdir(newpath):
-            yield newpath
-        # If we get to this line, the `with` block using _cleandir did not throw.
-        # Clean up the temporary directory unless the user specified `--rm never`.
-        # I.e. If the user specified `--rm success`, then we need to toggle from `warn` to `remove`.
-        if remove_tempdir != 'never':
-            callback = remove
-    finally:
-        callback()
-
-
-@pytest.fixture
-def cleandir(remove_tempdir):
-    """Provide a clean temporary working directory for a test.
-
-    Example usage:
-
-        import os
-        import pytest
-
-        @pytest.mark.usefixtures("cleandir")
-        def test_cwd_starts_empty():
-            assert os.listdir(os.getcwd()) == []
-            with open("myfile", "w") as f:
-                f.write("hello")
-
-        def test_cwd_also_starts_empty(cleandir):
-            assert os.listdir(os.getcwd()) == []
-            assert os.path.abspath(os.getcwd()) == os.path.abspath(cleandir)
-            with open("myfile", "w") as f:
-                f.write("hello")
-
-        @pytest.mark.usefixtures("cleandir")
-        class TestDirectoryInit(object):
-            def test_cwd_starts_empty(self):
-                assert os.listdir(os.getcwd()) == []
-                with open("myfile", "w") as f:
-                    f.write("hello")
-
-            def test_cwd_also_starts_empty(self):
-                assert os.listdir(os.getcwd()) == []
-                with open("myfile", "w") as f:
-                    f.write("hello")
-
-    Ref: https://docs.pytest.org/en/latest/fixture.html#using-fixtures-from-classes-modules-or-projects
-    """
-    with _cleandir(remove_tempdir) as newdir:
-        yield newdir
-
-
-@pytest.fixture(scope='session')
-def gmxcli():
-    # TODO: (#2896) Find a more canonical way to identify the GROMACS commandline wrapper binary.
-    #  We should be able to get the GMXRC contents and related hints from a gmxapi
-    #  package resource or from module attributes of a ``gromacs`` stub package.
-    allowed_command_names = ['gmx', 'gmx_mpi']
-    command = None
-    for command_name in allowed_command_names:
-        if command is not None:
-            break
-        command = shutil.which(command_name)
-        if command is None:
-            gmxbindir = os.getenv('GMXBIN')
-            if gmxbindir is None:
-                gromacsdir = os.getenv('GROMACS_DIR')
-                if gromacsdir is not None and gromacsdir != '':
-                    gmxbindir = os.path.join(gromacsdir, 'bin')
-            if gmxbindir is None:
-                gmxapidir = os.getenv('gmxapi_DIR')
-                if gmxapidir is not None and gmxapidir != '':
-                    gmxbindir = os.path.join(gmxapidir, 'bin')
-            if gmxbindir is not None:
-                gmxbindir = os.path.abspath(gmxbindir)
-                command = shutil.which(command_name, path=gmxbindir)
-    if command is None:
-        message = "Tests need 'gmx' command line tool, but could not find it on the path."
-        raise RuntimeError(message)
-    try:
-        assert os.access(command, os.X_OK)
-    except Exception as E:
-        raise RuntimeError('"{}" is not an executable gmx wrapper program'.format(command)) from E
-    yield command
-
+pytest_plugins = ('gmxapi.testsupport',)
 
 @pytest.fixture(scope='class')
 def spc_water_box(gmxcli, remove_tempdir):
@@ -192,6 +48,8 @@ def spc_water_box(gmxcli, remove_tempdir):
     Prepare the MD input in a freshly created working directory.
     """
     import gmxapi as gmx
+    # TODO: Remove this import when the the spc_water_box fixture is migrated to gmxapi.testsupport
+    from gmxapi.testsupport import _cleandir
 
     # TODO: (#2896) Fetch MD input from package / library data.
     # Example:
diff --git a/python_packaging/src/test/pytest.ini b/python_packaging/src/test/pytest.ini
new file mode 100644 (file)
index 0000000..56711bb
--- /dev/null
@@ -0,0 +1,2 @@
+[pytest]
+junit_family=xunit1
index 8023112ce55713add9658ab9bb79812e6fb31b4c..9aa5dbd4b4eb8d08d318596f18092c7262597892 100644 (file)
@@ -2,7 +2,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -42,9 +42,13 @@ tests of the operation-building utilities in the modules depended on by
 commandline.py.
 """
 
+import os
 import shutil
+import stat
 import unittest
 
+import gmxapi as gmx
+import pytest
 from gmxapi import commandline
 
 
@@ -91,6 +95,23 @@ class SimpleCliTestCase(unittest.TestCase):
         operation = commandline.cli(command=[shutil.which('echo'), 'hi', 'there'], shell=False)
         assert operation.output.returncode.result() == 0
 
+    def test_command_with_stdin(self):
+        """Test that cli() can handle string input."""
+        stdin = 'hi\nthere\n'
+        subcommand = '{wc} -l | {grep} -q 2'.format(wc=shutil.which('wc'), grep=shutil.which('grep'))
+
+        operation = commandline.cli(command=['/bin/sh', '-c', subcommand], shell=False, stdin=stdin)
+        assert operation.output.returncode.result() == 0
+        operation = commandline.commandline_operation('/bin/sh', ['-c', subcommand], stdin=stdin)
+        assert operation.output.returncode.result() == 0
+
+        subcommand = '{wc} -l | {grep} -q 1'.format(wc=shutil.which('wc'), grep=shutil.which('grep'))
+
+        operation = commandline.cli(command=['/bin/sh', '-c', subcommand], shell=False, stdin=stdin)
+        assert operation.output.returncode.result() != 0
+        operation = commandline.commandline_operation('/bin/sh', ['-c', subcommand], stdin=stdin)
+        assert operation.output.returncode.result() != 0
+
 
 class CommandLineOperationSimpleTestCase(unittest.TestCase):
     """Test the command line wrapper operation factory."""
@@ -119,5 +140,75 @@ class CommandLineOperationSimpleTestCase(unittest.TestCase):
         assert operation.output.returncode.result() == 0
 
 
+def test_file_dependency_chain(cleandir):
+    """Test the command line wrapper input/output file handling.
+
+    Operation output can be used as operation input.
+    """
+    file1 = os.path.join(cleandir, 'input')
+    file2 = os.path.join(cleandir, 'output')
+
+    # Make a shell script that acts like the type of tool we are wrapping.
+    scriptname = os.path.join(cleandir, 'clicommand.sh')
+    with open(scriptname, 'w') as fh:
+        fh.write('\n'.join(['#!' + shutil.which('bash'),
+                            '# Concatenate an input file and a string argument to an output file.',
+                            '# Mock a utility with the tested syntax.',
+                            '#     clicommand.sh "some words" -i inputfile -o outputfile',
+                            'echo $1 | cat $3 - > $5\n']))
+    os.chmod(scriptname, stat.S_IRWXU)
+
+    line1 = 'first line'
+    filewriter1 = gmx.commandline_operation(scriptname,
+                                            arguments=[line1],
+                                            input_files={'-i': os.devnull},
+                                            output_files={'-o': file1})
+
+    line2 = 'second line'
+    filewriter2 = gmx.commandline_operation(scriptname,
+                                            arguments=[line2],
+                                            input_files={'-i': filewriter1.output.file['-o']},
+                                            output_files={'-o': file2})
+
+    filewriter2.run()
+    # Check that the files have the expected lines
+    with open(file1, 'r') as fh:
+        lines = [text.rstrip() for text in fh]
+    assert len(lines) == 1
+    assert lines[0] == line1
+    with open(file2, 'r') as fh:
+        lines = [text.rstrip() for text in fh]
+    assert len(lines) == 2
+    assert lines[0] == line1
+    assert lines[1] == line2
+
+def test_failure(cleandir):
+    """The operation should not deliver file output if the subprocess fails."""
+    file1 = os.path.join(cleandir, 'input')
+    file2 = os.path.join(cleandir, 'output')
+
+    # Make a shell script that acts like the type of tool we are wrapping.
+    scriptname = os.path.join(cleandir, 'clicommand.sh')
+    with open(scriptname, 'w') as fh:
+        fh.write('\n'.join(['#!' + shutil.which('bash'),
+                            '# Concatenate an input file and a string argument to an output file.',
+                            '# Mock a utility with the tested syntax.',
+                            '#     clicommand.sh "some words" -i inputfile -o outputfile',
+                            'exit 1\n']))
+    os.chmod(scriptname, stat.S_IRWXU)
+
+    filewriter1 = gmx.commandline_operation(scriptname,
+                                            input_files={'-i': os.devnull},
+                                            output_files={'-o': file1})
+
+    filewriter2 = gmx.commandline_operation(scriptname,
+                                            input_files={'-i': filewriter1.output.file['-o']},
+                                            output_files={'-o': file2})
+
+    # filewriter1 has a non-zero exit code and should have no output files available.
+    with pytest.raises(KeyError):
+        filewriter2.run()
+
+
 if __name__ == '__main__':
     unittest.main()
index 3c764e07c98c95a7ea9b3104afaa83afa6d69176..48d0808f8cc78eae34448e24f4fde7493937e288 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -47,7 +47,7 @@ import os
 import pytest
 
 import gmxapi as gmx
-from gmxapi.testsupport import withmpi_only
+
 
 # Configure the `logging` module before proceeding any further.
 gmx.logger.setLevel(logging.WARNING)
@@ -73,7 +73,7 @@ formatter = logging.Formatter(rank_tag + '%(name)s:%(levelname)s: %(message)s')
 
 
 @pytest.mark.usefixtures('cleandir')
-def test_run_from_tpr(spc_water_box):
+def test_run_from_tpr(spc_water_box, mdrun_kwargs):
     assert os.path.exists(spc_water_box)
 
     md = gmx.mdrun(spc_water_box)
@@ -81,9 +81,9 @@ def test_run_from_tpr(spc_water_box):
     # TODO: better handling of output on unused MPI ranks.
 
 
-@withmpi_only
+@pytest.mark.withmpi_only
 @pytest.mark.usefixtures('cleandir')
-def test_run_trivial_ensemble(spc_water_box, caplog):
+def test_run_trivial_ensemble(spc_water_box, caplog, mdrun_kwargs):
     from mpi4py import MPI
     current_rank = MPI.COMM_WORLD.Get_rank()
     with caplog.at_level(logging.DEBUG):
@@ -119,7 +119,7 @@ def test_run_trivial_ensemble(spc_water_box, caplog):
 
 
 @pytest.mark.usefixtures('cleandir')
-def test_run_from_read_tpr_op(spc_water_box, caplog):
+def test_run_from_read_tpr_op(spc_water_box, caplog, mdrun_kwargs):
     with caplog.at_level(logging.DEBUG):
         caplog.handler.setFormatter(formatter)
         with caplog.at_level(logging.DEBUG, 'gmxapi'):
@@ -132,7 +132,7 @@ def test_run_from_read_tpr_op(spc_water_box, caplog):
 
 
 @pytest.mark.usefixtures('cleandir')
-def test_run_from_modify_input_op(spc_water_box, caplog):
+def test_run_from_modify_input_op(spc_water_box, caplog, mdrun_kwargs):
     with caplog.at_level(logging.DEBUG):
 
         simulation_input = gmx.read_tpr(spc_water_box)
index 2066f8892e3810e11f323acfe6100636d201aa6c..413faf8b3bad729477bd0def1d2e2959ca7561b5 100644 (file)
@@ -36,3 +36,10 @@ where `$LOCAL_REPO_DIR` is the path to the local copy of the GROMACS source repo
 For multi-process tests, run with an MPI execution wrapper and the `mpi4py` module.
 
 `mpiexec -n 2 python -m mpi4py -m pytest $LOCAL_REPO_DIR/python_packaging/test`
+
+## Controlling output
+
+Refer to pytest documentation for command line options to control the type and detail of output.
+Some high level overview and basic tasks are online at https://docs.pytest.org/en/3.9.3/usage.html
+but you should run `pytest -h` in a terminal to get the complete set of available options
+(in particular, note *log_cli* and *log_level*).
index db5da48a4397a0a124740d355ac25bce60a3c17e..37ef0767824a937fcd2a92c7ce5d3ff8526e0a8b 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 import json
 import logging
 import os
-import shutil
-import tempfile
-import warnings
-from contextlib import contextmanager
 
 import pytest
 
-
-def pytest_addoption(parser):
-    """Add a command-line user option for the pytest invocation."""
-    parser.addoption(
-        '--rm',
-        action='store',
-        default='always',
-        choices=['always', 'never', 'success'],
-        help='Remove temporary directories "always", "never", or on "success".'
-    )
-
-
-@pytest.fixture(scope='session')
-def remove_tempdir(request):
-    """pytest fixture to get access to the --rm CLI option."""
-    return request.config.getoption('--rm')
-
-
-@contextmanager
-def scoped_chdir(dir):
-    oldpath = os.getcwd()
-    os.chdir(dir)
-    try:
-        yield dir
-        # If the `with` block using scoped_chdir produces an exception, it will
-        # be raised at this point in this function. We want the exception to
-        # propagate out of the `with` block, but first we want to restore the
-        # original working directory, so we skip `except` but provide a `finally`.
-    finally:
-        os.chdir(oldpath)
-
-
-@contextmanager
-def _cleandir(remove_tempdir):
-    """Context manager for a clean temporary working directory.
-
-    Arguments:
-        remove_tempdir (str): whether to remove temporary directory "always",
-                              "never", or on "success"
-
-    The context manager will issue a warning for each temporary directory that
-    is not removed.
-    """
-
-    newpath = tempfile.mkdtemp()
-
-    def remove():
-        shutil.rmtree(newpath)
-
-    def warn():
-        warnings.warn('Temporary directory not removed: {}'.format(newpath))
-
-    if remove_tempdir == 'always':
-        callback = remove
-    else:
-        callback = warn
-    try:
-        with scoped_chdir(newpath):
-            yield newpath
-        # If we get to this line, the `with` block using _cleandir did not throw.
-        # Clean up the temporary directory unless the user specified `--rm never`.
-        # I.e. If the user specified `--rm success`, then we need to toggle from `warn` to `remove`.
-        if remove_tempdir != 'never':
-            callback = remove
-    finally:
-        callback()
-
-
-@pytest.fixture
-def cleandir(remove_tempdir):
-    """Provide a clean temporary working directory for a test.
-
-    Example usage:
-
-        import os
-        import pytest
-
-        @pytest.mark.usefixtures("cleandir")
-        def test_cwd_starts_empty():
-            assert os.listdir(os.getcwd()) == []
-            with open("myfile", "w") as f:
-                f.write("hello")
-
-        def test_cwd_also_starts_empty(cleandir):
-            assert os.listdir(os.getcwd()) == []
-            assert os.path.abspath(os.getcwd()) == os.path.abspath(cleandir)
-            with open("myfile", "w") as f:
-                f.write("hello")
-
-        @pytest.mark.usefixtures("cleandir")
-        class TestDirectoryInit(object):
-            def test_cwd_starts_empty(self):
-                assert os.listdir(os.getcwd()) == []
-                with open("myfile", "w") as f:
-                    f.write("hello")
-
-            def test_cwd_also_starts_empty(self):
-                assert os.listdir(os.getcwd()) == []
-                with open("myfile", "w") as f:
-                    f.write("hello")
-
-    Ref: https://docs.pytest.org/en/latest/fixture.html#using-fixtures-from-classes-modules-or-projects
-    """
-    with _cleandir(remove_tempdir) as newdir:
-        yield newdir
-
-
-@pytest.fixture(scope='session')
-def gmxcli():
-    # TODO: (#2896) Find a more canonical way to identify the GROMACS commandline wrapper binary.
-    #  We should be able to get the GMXRC contents and related hints from a gmxapi
-    #  package resource or from module attributes of a ``gromacs`` stub package.
-    allowed_command_names = ['gmx', 'gmx_mpi']
-    command = None
-    for command_name in allowed_command_names:
-        if command is not None:
-            break
-        command = shutil.which(command_name)
-        if command is None:
-            gmxbindir = os.getenv('GMXBIN')
-            if gmxbindir is None:
-                gromacsdir = os.getenv('GROMACS_DIR')
-                if gromacsdir is not None and gromacsdir != '':
-                    gmxbindir = os.path.join(gromacsdir, 'bin')
-            if gmxbindir is None:
-                gmxapidir = os.getenv('gmxapi_DIR')
-                if gmxapidir is not None and gmxapidir != '':
-                    gmxbindir = os.path.join(gmxapidir, 'bin')
-            if gmxbindir is not None:
-                gmxbindir = os.path.abspath(gmxbindir)
-                command = shutil.which(command_name, path=gmxbindir)
-    if command is None:
-        message = "Tests need 'gmx' command line tool, but could not find it on the path."
-        raise RuntimeError(message)
-    try:
-        assert os.access(command, os.X_OK)
-    except Exception as E:
-        raise RuntimeError('"{}" is not an executable gmx wrapper program'.format(command)) from E
-    yield command
+pytest_plugins = ('gmxapi.testsupport',)
 
 
 @pytest.fixture(scope='class')
@@ -192,6 +50,8 @@ def spc_water_box(gmxcli, remove_tempdir):
     Prepare the MD input in a freshly created working directory.
     """
     import gmxapi as gmx
+    # TODO: Remove this import when the the spc_water_box fixture is migrated to gmxapi.testsupport
+    from gmxapi.testsupport import _cleandir
 
     # TODO: (#2896) Fetch MD input from package / library data.
     # Example:
index c08c5e8e7a7687ba6a41f3115da772e687d5ac10..56711bbf764025d8d7bac7663a6614f1cb70b32e 100644 (file)
@@ -1,3 +1,2 @@
 [pytest]
-log_cli=true
-log_level=DEBUG
+junit_family=xunit1
index 60ae6e034e25a664743b267fb030e36dd870dcd5..4c171d49438ac31324ce2a3771fb1674463fcdd3 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -39,6 +39,10 @@ Define the ``withmpi_only`` test decorator.
 
 import pytest
 
+# TODO: (#3718) Normalize the handling of run-time arguments.
+from gmxapi.simulation.mdrun import ResourceManager as _ResourceManager
+_ResourceManager.mdrun_kwargs = {'threads': 2}
+
 withmpi_only = None
 
 try:
index 375f082cbaaeb3c2480d95bc5d77f5835bece63e..51fdcfc13ade1a150d9dade65a522391654328c3 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 """Test gmxapi functionality described in roadmap.rst."""
 
 import gmxapi as gmx
-import pytest
-from gmxapi.version import has_feature
 
 
-@pytest.mark.skipif(not has_feature('fr1'),
-                    reason="Feature level not met.")
 def test_fr1():
     """FR1: Wrap importable Python code.
 
diff --git a/python_packaging/test/test_fr02.py b/python_packaging/test/test_fr02.py
deleted file mode 100644 (file)
index bd217da..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#
-# This file is part of the GROMACS molecular simulation package.
-#
-# Copyright (c) 2019, by the GROMACS development team, led by
-# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
-# and including many others, as listed in the AUTHORS file in the
-# top-level source directory and at http://www.gromacs.org.
-#
-# GROMACS is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public License
-# as published by the Free Software Foundation; either version 2.1
-# of the License, or (at your option) any later version.
-#
-# GROMACS is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with GROMACS; if not, see
-# http://www.gnu.org/licenses, or write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
-#
-# If you want to redistribute modifications to GROMACS, please
-# consider that scientific software is very special. Version
-# control is crucial - bugs must be traceable. We will be happy to
-# consider code for inclusion in the official distribution, but
-# derived work must not be called official GROMACS. Details are found
-# in the README & COPYING files - if they are missing, get the
-# official version at http://www.gromacs.org.
-#
-# To help us fund GROMACS development, we humbly ask that you cite
-# the research papers on the package. Check out http://www.gromacs.org.
-
-"""Test gmxapi functionality described in roadmap.rst."""
-
-import os
-import tempfile
-
-import gmxapi as gmx
-import pytest
-from gmxapi.version import has_feature
-
-
-@pytest.mark.skipif(not has_feature('fr2') or has_feature('fr4'),
-                    reason="Feature level not met or is superseded.")
-def test_fr2():
-    """FR2: Output proxy establishes execution dependency.
-
-    Confirm that dependent operations are only executed after their dependencies.
-
-    In a sequence of two operations, write a two-line file one line at a time.
-    Use a user-provided filename as a parameter to each operation.
-    """
-    with tempfile.TemporaryDirectory() as directory:
-        fh, filename = tempfile.mkstemp(dir=directory)
-        os.close(fh)
-
-        line1 = 'first line'
-        subcommand = ' '.join(['echo', '"{}"'.format(line1), '>>', filename])
-        commandline = ['-c', subcommand]
-        filewriter1 = gmx.commandline_operation('bash', arguments=commandline)
-
-        line2 = 'second line'
-        subcommand = ' '.join(['echo', '"{}"'.format(line2), '>>', filename])
-        commandline = ['-c', subcommand]
-        filewriter2 = gmx.commandline_operation('bash', arguments=commandline, input=filewriter1)
-
-        filewriter2.run()
-        # Check that the file has the two expected lines
-        with open(filename, 'r') as fh:
-            lines = [text.rstrip() for text in fh]
-        assert len(lines) == 2
-        assert lines[0] == line1
-        assert lines[1] == line2
index b7f16f29bcf14d07792368797fbd3a2c22c8bef8..1ce96fa32959be76dcea7fc82befa744df16d457 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -40,12 +40,8 @@ import stat
 import tempfile
 
 import gmxapi as gmx
-import pytest
-from gmxapi.version import has_feature
 
 
-@pytest.mark.skipif(not has_feature('fr3'),
-                    reason="Feature level not met.")
 def test_fr3():
     """FR3: Output proxy can be used as input."""
     with tempfile.TemporaryDirectory() as directory:
index 1131fb711ecbff6f3dfed9acbd5b463d8b6dc6cf..27354b7db0ce55f75b36faf7dcea365963dbf132 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 import pytest
 
 import gmxapi as gmx
-from gmxapi.version import has_feature
 
-@pytest.mark.skipif(not has_feature('fr7'),
-                   reason="Feature level not met.")
+
 @pytest.mark.usefixtures('cleandir')
 def test_fr7(spc_water_box):
     """FR7: Python bindings for launching simulations.
index 0d5f7c8463f296c3d104951037f517cd1cbb6ed2..de7a33f8f782d4adee9005264757038ed67e11cd 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -39,7 +39,7 @@ import pytest
 import gmxapi as gmx
 from gmxapi.version import has_feature
 
-# Ref https://redmine.gromacs.org/issues/3192
+# Ref https://gitlab.com/gromacs/gromacs/-/issues/3192
 @pytest.mark.skipif(not has_feature('fr8'),
                    reason="Feature level not met.")
 def test_fr8(spc_water_box):
index 6ae3b5bcaa24464862bb4c998b54801b11037dd2..d59d3f639736038689744419e455c8f5bdf6828a 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -39,7 +39,6 @@ import logging
 import pytest
 
 import gmxapi as gmx
-from gmxapi.version import has_feature
 
 # Configure the `logging` module before proceeding any further.
 gmx.logger.setLevel(logging.WARNING)
@@ -58,8 +57,6 @@ else:
 formatter = logging.Formatter(rank_tag + '%(name)s:%(levelname)s: %(message)s')
 
 @pytest.mark.usefixtures('cleandir')
-@pytest.mark.skipif(not has_feature('fr15'),
-                   reason="Feature level not met.")
 def test_fr15(spc_water_box, caplog):
     """FR15: Simulation input modification.
 
index bb4f99a62ab80e7424b7a32d9d84b23bd57504f8..b536986cae5ff4e3f0caee1b914ba6cd50d38e40 100644 (file)
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
+if(APPLE)
+  set(LD_LIBRARY_PATH "DYLD_LIBRARY_PATH")
+else()
+  set(LD_LIBRARY_PATH "LD_LIBRARY_PATH")
+endif()
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/GMXRC.cmakein      ${CMAKE_CURRENT_BINARY_DIR}/GMXRC @ONLY)
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/GMXRC.bash.cmakein ${CMAKE_CURRENT_BINARY_DIR}/GMXRC.bash @ONLY)
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/GMXRC.csh.cmakein  ${CMAKE_CURRENT_BINARY_DIR}/GMXRC.csh @ONLY)
index 02decc02842f0d6a76b51b0048d0abf2a83da53a..a6fdbe7865ca857a03ca6e7551f1e73b401cce33 100644 (file)
@@ -11,12 +11,12 @@ IFS=":"
 
 # First remove gromacs part of ld_library_path
 tmppath=""
-for i in $LD_LIBRARY_PATH; do
+for i in $@LD_LIBRARY_PATH@; do
   if test "$i" != "$GMXLDLIB"; then
     tmppath="${tmppath}${tmppath:+:}${i}"
   fi
 done
-LD_LIBRARY_PATH=$tmppath
+@LD_LIBRARY_PATH@=$tmppath
 
 # remove gromacs part of PKG_CONFIG_PATH
 tmppath=""
@@ -58,14 +58,14 @@ GMXDATA=${GMXPREFIX}/@GMX_INSTALL_GMXDATADIR@
 GMXTOOLCHAINDIR=${GMXPREFIX}/@GMX_INSTALL_CMAKEDIR@
 GROMACS_DIR=${GMXPREFIX}
 
-LD_LIBRARY_PATH=${GMXLDLIB}${LD_LIBRARY_PATH:+:}${LD_LIBRARY_PATH}
+@LD_LIBRARY_PATH@=${GMXLDLIB}${@LD_LIBRARY_PATH@:+:}${@LD_LIBRARY_PATH@}
 PKG_CONFIG_PATH=${GMXLDLIB}/pkgconfig${PKG_CONFIG_PATH:+:}${PKG_CONFIG_PATH}
 PATH=${GMXBIN}${PATH:+:}${PATH}
 #debian/ubuntu needs a : at the end
 MANPATH=${GMXMAN}:${MANPATH}
 
 # export should be separate, so /bin/sh understands it
-export GMXBIN GMXLDLIB GMXMAN GMXDATA LD_LIBRARY_PATH PATH MANPATH
+export GMXBIN GMXLDLIB GMXMAN GMXDATA @LD_LIBRARY_PATH@ PATH MANPATH
 export PKG_CONFIG_PATH GROMACS_DIR
 
 IFS="$old_IFS"
index ccb1461e35c970a0cb5ea5e99076f31a2de957e1..7e9045ad761f834033e5c788fc5c730023b6464a 100644 (file)
@@ -5,7 +5,7 @@
 # repeatedly switch between gmx versions in a shell.
 
 # zero possibly unset vars to avoid warnings
-if (! $?LD_LIBRARY_PATH) setenv LD_LIBRARY_PATH ""
+if (! $?@LD_LIBRARY_PATH@) setenv @LD_LIBRARY_PATH@ ""
 if (! $?PKG_CONFIG_PATH) setenv PKG_CONFIG_PATH ""
 if (! $?PATH) setenv PATH ""
 if (! $?MANPATH) setenv MANPATH ""
@@ -15,7 +15,7 @@ if (! $?GMXMAN) setenv GMXMAN ""
 
 # remove previous gromacs part from ld_library_path
 set tmppath = ""
-foreach i ( `echo $LD_LIBRARY_PATH | sed "s/:/ /g"` )
+foreach i ( `echo $@LD_LIBRARY_PATH@ | sed "s/:/ /g"` )
   if ( "$i" != "$GMXLDLIB" ) then
     if ("${tmppath}" == "") then
       set tmppath = "$i"
@@ -24,7 +24,7 @@ foreach i ( `echo $LD_LIBRARY_PATH | sed "s/:/ /g"` )
     endif
   endif
 end
-setenv LD_LIBRARY_PATH $tmppath
+setenv @LD_LIBRARY_PATH@ $tmppath
 
 # remove previous gromacs part from PKG_CONFIG_PATH
 set tmppath = ""
@@ -79,12 +79,12 @@ setenv GMXTOOLCHAINDIR ${GMXPREFIX}/@GMX_INSTALL_CMAKEDIR@
 setenv GROMACS_DIR ${GMXPREFIX}
 
 #make them begin with :
-if ($?LD_LIBRARY_PATH) setenv LD_LIBRARY_PATH ":${LD_LIBRARY_PATH}"
+if ($?@LD_LIBRARY_PATH@) setenv @LD_LIBRARY_PATH@ ":${@LD_LIBRARY_PATH@}"
 if ($?PKG_CONFIG_PATH) setenv PKG_CONFIG_PATH ":${PKG_CONFIG_PATH}"
 
 #path is never empty
 setenv PATH ${GMXBIN}:${PATH}
-setenv LD_LIBRARY_PATH ${GMXLDLIB}${LD_LIBRARY_PATH}
+setenv @LD_LIBRARY_PATH@ ${GMXLDLIB}${@LD_LIBRARY_PATH@}
 setenv PKG_CONFIG_PATH ${GMXLDLIB}/pkgconfig${PKG_CONFIG_PATH}
 #debian/ubuntu needs a : at the end
 setenv MANPATH ${GMXMAN}:${MANPATH}
index 256da1d15a0c3f8688d90be881cedfa8db1114b7..d10807073e1d0d7b54adcc983a8f6f2a0e9de2f4 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2009,2010,2011,2012,2013,2014,2018,2020, by the GROMACS development team, led by
+# Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+# Copyright (c) 2014,2018,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 330cbbd460739e2bf14991f7068869445bcf2746..47b14d0ef62d6e2187695b6b7f01a4a563803390 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2011,2012,2014,2016,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2011,2012,2014,2016,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -37,6 +38,9 @@ if (WIN32)
     gmx_target_warning_suppression(template /wd4244 HAS_NO_MSVC_LOSSY_CONVERSION_DOUBLE_TO_REAL)
     gmx_target_warning_suppression(template /wd4267 HAS_NO_MSVC_LOSSY_CONVERSION_SIZE_T_TO_INT)
 endif()
+# This should be removable once object libraries can directly use target_link_libraries
+# with CMake 3.12, #3290
+target_include_directories(template SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/src/external)
 target_link_libraries(template libgromacs ${GMX_EXE_LINKER_FLAGS})
 
 set(DOCUMENTATION_HTTP_URL_BASE
index e2111c53db9b81d6712b627ed82af2ec85c0af78..f1dadb766db241a994e6980e874b12c11f2c6dc0 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 007b57acda9154e630a58e5e43178ddf7fee7b40..484858175f16538f9f9c2a0c51d5837d069d0626 100644 (file)
@@ -1,7 +1,7 @@
 ; original reference: [M. W. Mahoney and W. L. Jorgensen, J. Chem. Phys. 112 , 2000]
 ;
 ; Note that there are various issues with tip5p and the different forcefields.
-; Discussion is here: http://redmine.gromacs.org/issues/1348
+; Discussion is here: https://gitlab.com/gromacs/gromacs/-/issues/1348
 
 [ moleculetype ]
 ; molname      nrexcl
index 7e4956a7e9a47dd4fafbf390fb65446eade24058..f2199278ecffd9621614f71a4814f3a89e40b82d 100644 (file)
@@ -1,6 +1,6 @@
 tip3p    TIP3P     TIP 3-point, recommended
 tip4p    TIP4P     TIP 4-point
 tip4pew  TIP4P-Ew  TIP 4-point optimized with Ewald
-tip5p    TIP5P     TIP 5-point (see http://redmine.gromacs.org/issues/1348 for issues)
+tip5p    TIP5P     TIP 5-point (see https://gitlab.com/gromacs/gromacs/-/issues/1348 for issues)
 spc      SPC       simple point charge
 spce     SPC/E     extended simple point charge
index 007b57acda9154e630a58e5e43178ddf7fee7b40..484858175f16538f9f9c2a0c51d5837d069d0626 100644 (file)
@@ -1,7 +1,7 @@
 ; original reference: [M. W. Mahoney and W. L. Jorgensen, J. Chem. Phys. 112 , 2000]
 ;
 ; Note that there are various issues with tip5p and the different forcefields.
-; Discussion is here: http://redmine.gromacs.org/issues/1348
+; Discussion is here: https://gitlab.com/gromacs/gromacs/-/issues/1348
 
 [ moleculetype ]
 ; molname      nrexcl
index 7e4956a7e9a47dd4fafbf390fb65446eade24058..f2199278ecffd9621614f71a4814f3a89e40b82d 100644 (file)
@@ -1,6 +1,6 @@
 tip3p    TIP3P     TIP 3-point, recommended
 tip4p    TIP4P     TIP 4-point
 tip4pew  TIP4P-Ew  TIP 4-point optimized with Ewald
-tip5p    TIP5P     TIP 5-point (see http://redmine.gromacs.org/issues/1348 for issues)
+tip5p    TIP5P     TIP 5-point (see https://gitlab.com/gromacs/gromacs/-/issues/1348 for issues)
 spc      SPC       simple point charge
 spce     SPC/E     extended simple point charge
index 007b57acda9154e630a58e5e43178ddf7fee7b40..484858175f16538f9f9c2a0c51d5837d069d0626 100644 (file)
@@ -1,7 +1,7 @@
 ; original reference: [M. W. Mahoney and W. L. Jorgensen, J. Chem. Phys. 112 , 2000]
 ;
 ; Note that there are various issues with tip5p and the different forcefields.
-; Discussion is here: http://redmine.gromacs.org/issues/1348
+; Discussion is here: https://gitlab.com/gromacs/gromacs/-/issues/1348
 
 [ moleculetype ]
 ; molname      nrexcl
index 7e4956a7e9a47dd4fafbf390fb65446eade24058..f2199278ecffd9621614f71a4814f3a89e40b82d 100644 (file)
@@ -1,6 +1,6 @@
 tip3p    TIP3P     TIP 3-point, recommended
 tip4p    TIP4P     TIP 4-point
 tip4pew  TIP4P-Ew  TIP 4-point optimized with Ewald
-tip5p    TIP5P     TIP 5-point (see http://redmine.gromacs.org/issues/1348 for issues)
+tip5p    TIP5P     TIP 5-point (see https://gitlab.com/gromacs/gromacs/-/issues/1348 for issues)
 spc      SPC       simple point charge
 spce     SPC/E     extended simple point charge
index 007b57acda9154e630a58e5e43178ddf7fee7b40..484858175f16538f9f9c2a0c51d5837d069d0626 100644 (file)
@@ -1,7 +1,7 @@
 ; original reference: [M. W. Mahoney and W. L. Jorgensen, J. Chem. Phys. 112 , 2000]
 ;
 ; Note that there are various issues with tip5p and the different forcefields.
-; Discussion is here: http://redmine.gromacs.org/issues/1348
+; Discussion is here: https://gitlab.com/gromacs/gromacs/-/issues/1348
 
 [ moleculetype ]
 ; molname      nrexcl
index 7e4956a7e9a47dd4fafbf390fb65446eade24058..f2199278ecffd9621614f71a4814f3a89e40b82d 100644 (file)
@@ -1,6 +1,6 @@
 tip3p    TIP3P     TIP 3-point, recommended
 tip4p    TIP4P     TIP 4-point
 tip4pew  TIP4P-Ew  TIP 4-point optimized with Ewald
-tip5p    TIP5P     TIP 5-point (see http://redmine.gromacs.org/issues/1348 for issues)
+tip5p    TIP5P     TIP 5-point (see https://gitlab.com/gromacs/gromacs/-/issues/1348 for issues)
 spc      SPC       simple point charge
 spce     SPC/E     extended simple point charge
index 007b57acda9154e630a58e5e43178ddf7fee7b40..484858175f16538f9f9c2a0c51d5837d069d0626 100644 (file)
@@ -1,7 +1,7 @@
 ; original reference: [M. W. Mahoney and W. L. Jorgensen, J. Chem. Phys. 112 , 2000]
 ;
 ; Note that there are various issues with tip5p and the different forcefields.
-; Discussion is here: http://redmine.gromacs.org/issues/1348
+; Discussion is here: https://gitlab.com/gromacs/gromacs/-/issues/1348
 
 [ moleculetype ]
 ; molname      nrexcl
index 7e4956a7e9a47dd4fafbf390fb65446eade24058..f2199278ecffd9621614f71a4814f3a89e40b82d 100644 (file)
@@ -1,6 +1,6 @@
 tip3p    TIP3P     TIP 3-point, recommended
 tip4p    TIP4P     TIP 4-point
 tip4pew  TIP4P-Ew  TIP 4-point optimized with Ewald
-tip5p    TIP5P     TIP 5-point (see http://redmine.gromacs.org/issues/1348 for issues)
+tip5p    TIP5P     TIP 5-point (see https://gitlab.com/gromacs/gromacs/-/issues/1348 for issues)
 spc      SPC       simple point charge
 spce     SPC/E     extended simple point charge
index 007b57acda9154e630a58e5e43178ddf7fee7b40..484858175f16538f9f9c2a0c51d5837d069d0626 100644 (file)
@@ -1,7 +1,7 @@
 ; original reference: [M. W. Mahoney and W. L. Jorgensen, J. Chem. Phys. 112 , 2000]
 ;
 ; Note that there are various issues with tip5p and the different forcefields.
-; Discussion is here: http://redmine.gromacs.org/issues/1348
+; Discussion is here: https://gitlab.com/gromacs/gromacs/-/issues/1348
 
 [ moleculetype ]
 ; molname      nrexcl
index fa5b9442f647acf64c00e4bdcfdbe5a8062f0920..fa85cbaa8bea82fd64ae900b99675ddb57bc4e78 100644 (file)
@@ -1,6 +1,6 @@
 tip3p    TIP3P     TIP 3-point, recommended
 tip4p    TIP4P     TIP 4-point
 tip4pew  TIP4P-Ew  TIP 4-point optimized with Ewald, recommended 
-tip5p    TIP5P     TIP 5-point (see http://redmine.gromacs.org/issues/1348 for issues)
+tip5p    TIP5P     TIP 5-point (see https://gitlab.com/gromacs/gromacs/-/issues/1348 for issues)
 spc      SPC       simple point charge
 spce     SPC/E     extended simple point charge
index 007b57acda9154e630a58e5e43178ddf7fee7b40..484858175f16538f9f9c2a0c51d5837d069d0626 100644 (file)
@@ -1,7 +1,7 @@
 ; original reference: [M. W. Mahoney and W. L. Jorgensen, J. Chem. Phys. 112 , 2000]
 ;
 ; Note that there are various issues with tip5p and the different forcefields.
-; Discussion is here: http://redmine.gromacs.org/issues/1348
+; Discussion is here: https://gitlab.com/gromacs/gromacs/-/issues/1348
 
 [ moleculetype ]
 ; molname      nrexcl
index 7e4956a7e9a47dd4fafbf390fb65446eade24058..f2199278ecffd9621614f71a4814f3a89e40b82d 100644 (file)
@@ -1,6 +1,6 @@
 tip3p    TIP3P     TIP 3-point, recommended
 tip4p    TIP4P     TIP 4-point
 tip4pew  TIP4P-Ew  TIP 4-point optimized with Ewald
-tip5p    TIP5P     TIP 5-point (see http://redmine.gromacs.org/issues/1348 for issues)
+tip5p    TIP5P     TIP 5-point (see https://gitlab.com/gromacs/gromacs/-/issues/1348 for issues)
 spc      SPC       simple point charge
 spce     SPC/E     extended simple point charge
index d13477c90f5eeac1c0efa5061a58f832d2827c48..478a7e9ae1574620452ab02b2ebfa89a7d2b889b 100644 (file)
@@ -1,7 +1,7 @@
 ; original reference: [M. W. Mahoney and W. L. Jorgensen, J. Chem. Phys. 112 , 2000]
 ;
 ; Note that there are various issues with tip5p and the different forcefields.
-; Discussion is here: http://redmine.gromacs.org/issues/1348
+; Discussion is here: https://gitlab.com/gromacs/gromacs/-/issues/1348
 
 [ moleculetype ]
 ; molname      nrexcl
index f05e8ecf8c913428d5972fd09e99fa1a94db8085..6a58a6dff12968d28de62f52edf208441c46fa3a 100644 (file)
@@ -1,6 +1,6 @@
 tip3p   TIP3P   TIP 3-point, recommended
 tip4p   TIP4P   TIP 4-point
 tips3p  TIPS3P  CHARMM TIP 3-point with LJ on H's
-tip5p   TIP5P   TIP 5-point (see http://redmine.gromacs.org/issues/1348 for issues)
+tip5p   TIP5P   TIP 5-point (see https://gitlab.com/gromacs/gromacs/-/issues/1348 for issues)
 spc     SPC     simple point charge
 spce    SPC/E   extended simple point charge
index 22418b5f6239700d09341a9e36c86493c1569aad..e59bf5691a0a21bcbce5b359f9a876a6541c5aa8 100644 (file)
@@ -1,7 +1,7 @@
 ; original reference: [M. W. Mahoney and W. L. Jorgensen, J. Chem. Phys. 112 , 2000]
 ;
 ; Note that there are various issues with tip5p and the different forcefields.
-; Discussion is here: http://redmine.gromacs.org/issues/1348
+; Discussion is here: https://gitlab.com/gromacs/gromacs/-/issues/1348
 
 [ moleculetype ]
 ; molname       nrexcl
index a4764133340099a992a301ca7e9fbffa07761f44..3972fa2a4bd3c5b6f2c54c66274b9029ea66bb13 100644 (file)
@@ -1,7 +1,7 @@
 tip4p   TIP4P  TIP 4-point, recommended
 tip4pew TIP4PEW TIP 4-point with Ewald
 tip3p   TIP3P  TIP 3-point
-tip5p   TIP5P  TIP 5-point (see http://redmine.gromacs.org/issues/1348 for issues)
+tip5p   TIP5P  TIP 5-point (see https://gitlab.com/gromacs/gromacs/-/issues/1348 for issues)
 tip5pe  TIP5P  TIP 5-point improved for Ewald sums
 spc     SPC    simple point charge
 spce    SPC/E  extended simple point charge
index 6efdf2d87cbff903b029ec53f16cfcdc61c5c64e..6a7556b89ebe66054b1ed70087d62439792c00b6 100644 (file)
 # We have many cases where int is converted to float and we don't care
 # enough about such potential loss of precision to use explicit casts
 # in large numbers of places.
-
+#
+#         -google-readability-avoid-underscore-in-googletest-name
+# We need to use underscores for readability for our legacy types
+# and command-line parameter names
+#
 Checks:  clang-diagnostic-*,-clang-analyzer-*,-clang-analyzer-security.insecureAPI.strcpy,
          bugprone-*,misc-*,readability-*,performance-*,mpi-*,
          -readability-inconsistent-declaration-parameter-name,
@@ -51,7 +55,8 @@ Checks:  clang-diagnostic-*,-clang-analyzer-*,-clang-analyzer-security.insecureA
          -readability-magic-numbers,
          -cppcoreguidelines-macro-usage,
          -cppcoreguidelines-narrowing-conversions,
-         -bugprone-narrowing-conversions
+         -bugprone-narrowing-conversions,
+         -google-readability-avoid-underscore-in-googletest-name
 HeaderFilterRegex: .*
 CheckOptions:
   - key:           cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
index 51c7c037667ee3c82681a5e988514bb2e908b3b1..b987bdb5e56b62b382ae91e729601ca99a94bdd8 100644 (file)
@@ -1,7 +1,9 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+# Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -38,7 +40,7 @@
 include(GetCompilerInfo.cmake)
 get_compiler_info(C BUILD_C_COMPILER)
 get_compiler_info(CXX BUILD_CXX_COMPILER)
-if(GMX_USE_CUDA)
+if(GMX_GPU_CUDA)
     if(NOT GMX_CLANG_CUDA)
         GMX_SET_CUDA_NVCC_FLAGS()
     endif()
@@ -57,26 +59,19 @@ file(GENERATE
     )
 
 ####
-set(IGNORED_CLANG_ALL_WARNINGS
-    "-Wno-c++98-compat -Wno-c++98-compat-pedantic" #No intention of C++98 compability
+list(APPEND IGNORED_CLANG_ALL_WARNINGS
+    "-Wno-c++98-compat" "-Wno-c++98-compat-pedantic" #No intention of C++98 compability
     "-Wno-source-uses-openmp" #Don't warn for no-omp build
     "-Wno-c++17-extensions"   #Allowed in attributes (compilers are required to ignore unknown attributes)
     "-Wno-documentation-unknown-command" #Custom commands are used
     "-Wno-covered-switch-default" #GCC gives maybe-uninitialized without default label and checks for illegal enum values.
     "-Wno-switch-enum" # default statement for enum is OK
 
-    # TODO uncomment the next few ignore lines when we upgrade to test with clang 8 in Jenkins
-
-    # The barriers we use for tMPI and Nbnxm are sufficient, but it's
-    # not known whether they're excessive. We assume they not
-    # excessive.
-    # "-Wno-atomic-implicit-seq-cst"
-
     # We need to use macros like
     # GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR. Those will look strange
     # if they don't have a semicolon after them, and might confuse
     # tools like IDEs also.
-    "-Wno-extra-semi-stmt"
+    "-Wno-extra-semi-stmt"
 
     #Following ones are undecided/TODO
     "-Wno-disabled-macro-expansion"
@@ -97,17 +92,27 @@ set(IGNORED_CLANG_ALL_WARNINGS
     "-Wno-old-style-cast"
     "-Wno-conversion"
     "-Wno-double-promotion")
-string(REPLACE " " ";" IGNORED_CLANG_ALL_WARNINGS "${IGNORED_CLANG_ALL_WARNINGS}")
 
+option(GMX_CLANG_TIDY "Use clang-tidy" OFF)
 if (GMX_CLANG_TIDY)
+   if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
+   elseif("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithAssert")
+   elseif("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
+   elseif("${CMAKE_BUILD_TYPE}" STREQUAL "ASAN")
+   else()
+       message(FATAL_ERROR "Can only use clang-tidy with build type containing asserts: Debug, RelWithAssert, RelWithDebInfo, ASAN.")
+   endif()
+   set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+   mark_as_advanced(CMAKE_EXPORT_COMPILE_COMMANDS)
    set(CLANG_TIDY "clang-tidy" CACHE STRING "Name of clang-tidy executable")
    find_program(CLANG_TIDY_EXE NAMES "${CLANG_TIDY}"
        DOC "Path to clang-tidy executable")
    if(NOT CLANG_TIDY_EXE)
        message(FATAL_ERROR "clang-tidy not found.")
    endif()
+   mark_as_advanced(CLANG_TIDY)
+   mark_as_advanced(CLANG_TIDY_EXE)
 endif()
-#####
 
 add_subdirectory(external)
 
@@ -121,30 +126,7 @@ endif()
 
 add_subdirectory(gromacs)
 add_subdirectory(programs)
-
-# Activate targets for new C++ API components and docs.
-if(GMX_NATIVE_WINDOWS OR GMX_BUILD_MDRUN_ONLY OR NOT BUILD_SHARED_LIBS)
-    # GMXAPI has not been tested in Microsoft environments.
-    # GMXAPI relies on libgromacs and is incompatible with an `mdrun`-only build.
-    # GMXAPI requires position-independent code
-    set(_GMXAPI_DEFAULT OFF)
-else()
-    set(_GMXAPI_DEFAULT ON)
-endif()
-option(GMXAPI "Experimental external interfaces" ${_GMXAPI_DEFAULT})
-if (GMXAPI)
-   if (GMX_BUILD_MDRUN_ONLY)
-       message(FATAL_ERROR "GMXAPI relies on libgromacs and is incompatible with GMX_BUILD_MDRUN_ONLY.")
-   endif()
-   if(NOT ${BUILD_SHARED_LIBS})
-       # Note: this conditional should check for the existence of a libgromacs target supporting PIC
-       # using the POSITION_INDEPENDENT_CODE property, but for now the only facility we have is the global
-       # variable, BUILD_SHARED_LIBS.
-       # TODO: gmxapi should gracefully build for dynamic linking or static linking for PIC or without.
-       message(FATAL_ERROR "GMXAPI requires position-independent code. Set -DGMXAPI=OFF or -DBUILD_SHARED_LIBS=ON.")
-   endif()
-   add_subdirectory(api)
-endif()
+add_subdirectory(api)
 
 # Configure header files with configuration-specific values. This step
 # should follow all introspection e.g. looking for headers and
index 124b8dbc9d7e96f4c68b9d11fa476f3efe309e47..0f62185f143abe8b94d62b9ffe298bf215408c49 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-# Note: GROMACS releases have a single-integer monotonic version in GMX_API_VERSION
-# and LIBRARY_VERSION annotates the shared object for libgromacs. GMXAPI versioning
-# is not synchronized to releases and may increment faster or slower.
-#
-# Prior to 0.1, GMXAPI patch levels are used to mark short term development cycles
-# and allow compatibility checks for client software of the early releases.
-#
-# gmxapi 0.2 will be the first release candidate for gmxapi 1.0 and will attempt
-# to establish compatibility guarantees consistent with semantic versioning.
-# (https://semver.org). When the API is deemed suitably stable, gmxapi 1.0 should
-# be tagged. Official GROMACS releases should be mappable to a distinct gmxapi
-# release string. For roadmap details, see https://redmine.gromacs.org/issues/2585
-set(GMXAPI_MAJOR 0)
-set(GMXAPI_MINOR 1)
-set(GMXAPI_PATCH 0)
-set(GMXAPI_RELEASE ${GMXAPI_MAJOR}.${GMXAPI_MINOR}.${GMXAPI_PATCH})
-
-###############################
-# New public C++ API components
-###############################
-
-# Define a list of the public headers to be installed and documented. Use
-# absolute paths in the build tree. Path component before `gmxapi` can be
-# stripped as appropriate by consumers of this list.
-set(GMXAPI_PUBLIC_HEADERS
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/context.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/exceptions.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/gmxapi.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/gmxapicompat.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/gromacsfwd.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/md.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/session.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/status.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/system.h
-    ${CMAKE_CURRENT_BINARY_DIR}/cpp/include/gmxapi/version.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/compat/mdparams.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/compat/tpr.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/gmxapi/md/mdmodule.h
-    )
-
-add_subdirectory(cpp)
+# Activate targets for new C++ API components and docs.
+if (GMXAPI)
+    if (GMX_BUILD_MDRUN_ONLY)
+        message(FATAL_ERROR "GMXAPI relies on libgromacs and is incompatible with GMX_BUILD_MDRUN_ONLY.")
+    endif()
+    if(NOT ${BUILD_SHARED_LIBS})
+        # Note: this conditional should check for the existence of a libgromacs target supporting PIC
+        # using the POSITION_INDEPENDENT_CODE property, but for now the only facility we have is the global
+        # variable, BUILD_SHARED_LIBS.
+        # TODO: gmxapi should gracefully build for dynamic linking or static linking for PIC or without.
+        message(FATAL_ERROR "GMXAPI requires position-independent code. Set -DGMXAPI=OFF or -DBUILD_SHARED_LIBS=ON.")
+    endif()
+    add_subdirectory(cpp)
+endif()
 
-add_subdirectory(docs)
index ffbf907db67aabab7b5d3b47e31136db0abfe642..fbeb3057d8bc1090384d09147936cbf760fa4b6d 100644 (file)
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-# This list file provides the Gromacs::gmxapi cmake module.
-
-##########################
-# Set up public interface.
-#
-# The parent (src/api/) CMake scope provides a variable named
-# GMXAPI_PUBLIC_HEADERS containing the full paths to the public
-# headers that are being installed. The headers are segregated into a
-# subdirectory here so that their build-time include directory path
-# does not expose lower level headers.
-
-add_subdirectory(include)
-
-# The include directory should be mostly empty so that we can use it internally as
-# the public interface include directory during build and testing.
-configure_file(include/version.h.in include/gmxapi/version.h)
-
-add_library(gmxapi SHARED
-            context.cpp
-            exceptions.cpp
-            gmxapi.cpp
-            md.cpp
-            mdmodule.cpp
-            mdsignals.cpp
-            session.cpp
-            status.cpp
-            system.cpp
-            version.cpp
-            workflow.cpp
-            tpr.cpp
-            )
-gmx_target_compile_options(gmxapi)
-target_compile_definitions(gmxapi PRIVATE HAVE_CONFIG_H)
-target_include_directories(gmxapi SYSTEM BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include)
-
-# Define public interface. Make sure targets linking against `gmxapi` in the build
-# system don't accidentally have the implementation headers (this directory))
-# in a default include path.
-target_include_directories(gmxapi PUBLIC
-                           $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
-                           $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
-                           $<INSTALL_INTERFACE:include>
-                           )
-# Define implementation interface
-target_include_directories(gmxapi PRIVATE
-                           ${CMAKE_CURRENT_SOURCE_DIR}
-                           )
-
-###############################
-# Install the public interface.
-#
-# If any item begins in a generator expression it must evaluate to a full path,
-# so we can't just use something like $<TARGET_PROPERTIES:gmxapiPublicHeaders,SOURCES>.
-# Instead, we use a canonical list defined in the parent scope.
-install(DIRECTORY include/gmxapi
-        DESTINATION include)
-install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/gmxapi/version.h
-        DESTINATION include/gmxapi)
-
-if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
-    # Instruct a linking client to use its RPATH to resolve the libgmxapi location.
-    #
-    # Explicitly specify library "install name" so that the correct loading
-    # instruction is produced in client code. Client code should be able to find the
-    # library relative to the client code RPATH. Without explicitly specifying,
-    # INSTALL_NAME_DIR is inherited from the global CMAKE_INSTALL_NAME_DIR, which is
-    # not appropriate for libgmxapi if it uses an install name relative to the
-    # executable_path or loader_path.
-    set_target_properties(gmxapi PROPERTIES INSTALL_NAME_DIR "@rpath")
-endif()
-
-set_target_properties(gmxapi PROPERTIES
-                      OUTPUT_NAME "gmxapi${GMX_LIBS_SUFFIX}"
-                      SOVERSION ${GMXAPI_MAJOR}
-                      VERSION ${GMXAPI_RELEASE}
-                      )
-
-target_link_libraries(gmxapi PRIVATE libgromacs)
-
-
-################################################
-# Install and export gmxapi and Gromacs::gmxapi.
-#
-# Install the gmxapi target and simultaneously define the export target for
-# which CMake will create a helper file. Specify the directory for clients to
-# add to their include path to be able to `#include "gmxapi/some_header.h"`
-install(TARGETS gmxapi
-        EXPORT gmxapi
-        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-        INCLUDES DESTINATION include
-        )
-
-# Create the CMake exports file to help other projects build against libgmxapi
-# as a CMake import target Gromacs::gmxapi.
-install(EXPORT gmxapi
-        NAMESPACE Gromacs::
-        DESTINATION share/cmake/gmxapi/
-        )
-add_library(Gromacs::gmxapi ALIAS gmxapi )
-
-include(CMakePackageConfigHelpers)
-
-configure_package_config_file(
-    cmake/gmxapi-config.cmake.in
-    "${CMAKE_CURRENT_BINARY_DIR}/cmake/gmxapi-config.cmake"
-    INSTALL_DESTINATION share/cmake/gmxapi/
-)
-write_basic_package_version_file(
-    ${CMAKE_CURRENT_BINARY_DIR}/cmake/gmxapi-config-version.cmake
-    VERSION ${GMXAPI_RELEASE}
-    COMPATIBILITY SameMajorVersion
-)
-
-install(
-    FILES
-    ${CMAKE_CURRENT_BINARY_DIR}/cmake/gmxapi-config-version.cmake
-    ${CMAKE_CURRENT_BINARY_DIR}/cmake/gmxapi-config.cmake
-    DESTINATION share/cmake/gmxapi/
-)
-
-# We need a CMake target to provide the internal interface(s) of the gmxapi
-# library implementation.
-add_library(gmxapi-detail INTERFACE)
-target_include_directories(gmxapi-detail
-                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
-
 if(BUILD_TESTING)
     add_subdirectory(tests)
     add_subdirectory(workflow/tests)
diff --git a/src/api/cpp/context_impl.h b/src/api/cpp/context_impl.h
deleted file mode 100644 (file)
index faaf0e5..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#ifndef GMXAPI_CONTEXT_IMPL_H
-#define GMXAPI_CONTEXT_IMPL_H
-/*! \file
- * \brief Declare gmxapi::ContextImpl
- *
- * \author M. Eric Irrgang <ericirrgang@gmail.com>
- * \ingroup gmxapi
- */
-
-#include <memory>
-#include <string>
-
-#include "gromacs/mdrun/legacymdrunoptions.h"
-#include "gromacs/mdtypes/mdrunoptions.h"
-
-#include "gmxapi/context.h"
-#include "gmxapi/session.h"
-
-namespace gmxapi
-{
-
-/*!
- * \brief Context implementation base class.
- *
- * Execution contexts have a uniform interface specified by the API. Implementations for
- * particular execution environments can specialize / derive from this base.
- *
- * \todo Separate interface and implementation.
- * \ingroup gmxapi
- */
-class ContextImpl final : public std::enable_shared_from_this<ContextImpl>
-{
-public:
-    /*!
-     * \brief Default constructor.
-     *
-     * Don't use this. Use create() to get a shared pointer right away.
-     * Otherwise, shared_from_this() is potentially dangerous.
-     *
-     * \todo Make default constructor private or otherwise reduce brittleness of construction.
-     */
-    ContextImpl();
-
-    /*!
-     * \brief Factory function
-     *
-     * Since this class provides `shared_from_this`, we need to make sure
-     * that it never exists without a shared_ptr owning it.
-     *
-     * If we can confirm `shared_from_this` is no longer necessary, implementation may change.
-     *
-     * \return ownership of a new object
-     */
-    static std::shared_ptr<gmxapi::ContextImpl> create();
-
-    /*!
-     * \brief Copy disallowed because Session state would become ambiguous.
-     *
-     * The API implementation needs to unambiguously determine
-     * which Sessions and Contexts are associated with each other.
-     * \{
-     */
-    ContextImpl(const ContextImpl&) = delete;
-    ContextImpl& operator=(const ContextImpl&) = delete;
-    //! \}
-
-    /*!
-     * \brief Objects are not trivial to move.
-     *
-     * \todo Implement move semantics.
-     * \{
-     */
-    ContextImpl(ContextImpl&&) = delete;
-    ContextImpl& operator=(ContextImpl&&) = delete;
-    //! \}
-
-    /*!
-     * \brief Translate the workflow to the execution context and launch.
-     *
-     * \param work workflow graph
-     * \return ownership of a new session
-     *
-     * \todo This probably makes more sense as a free function, but we need to determine access policies.
-     *
-     * Session is returned with shared_ptr ownership so that Context
-     * can hold a weak_ptr and because Session Resources handles
-     * are still evolving.
-     * \todo Hide lifetime management and ownership from handle object.
-     * We can achieve the necessary aspects of this shared_ptr at a lower level of implementation.
-     */
-    std::shared_ptr<Session> launch(const Workflow& work);
-
-    /*!
-     * \brief Retain the ability to find a launched session while it exists.
-     *
-     * The client owns the Session launched by a Context, but it is helpful
-     * for the Context to know if it has an active Session associated with it.
-     */
-    std::weak_ptr<Session> session_;
-
-    /*!
-     * \brief mdrun command line arguments.
-     *
-     * Store arguments provided by the client and pass them when launching
-     * a simulation runner. This allows client code to access the same
-     * options as are available to mdrun on the command line while the API
-     * evolves.
-     */
-    MDArgs mdArgs_;
-
-    /*!
-     * \brief Legacy option-handling and set up for mdrun.
-     *
-     * This object should not exist, but is necessary now to introduce
-     * the API in a way that means CLI and API work similarly and do not
-     * duplicate definitions e.g. of command-line options.
-     */
-    gmx::LegacyMdrunOptions options_;
-};
-
-} // end namespace gmxapi
-#endif // GMXAPI_CONTEXT_IMPL_H
diff --git a/src/api/cpp/include/version.h.in b/src/api/cpp/include/version.h.in
deleted file mode 100644 (file)
index e586c69..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2018, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#ifndef GMXAPI_VERSION_H
-#define GMXAPI_VERSION_H
-/*! \file
- * \brief Implement versioning API for C++ external GROMACS interface.
- *
- *  Defines a class Version in the gmxapi namespace providing static methods
- *  for use by API client code at compile time and run time.
- *
- *  External interface uses semantic versioning scheme in which the major version
- *  specifies the API compatibility level, and minor version indicates additional
- *  features that may or may not be ABI compatible. Feature availability may be
- *  implied by API level, but the library can be queried for availability of
- *  explicitly named features. This is essential to allow compatibility and
- *  flexibility during development.
- *
- *  \ingroup gmxapi
- *
- *  \internal
- *   The version numbers for gmxapi are encoded in the repository solely in the `src/api/CMakeLists.txt` file.
- * During CMake configuration, the `src/api/cpp/include/gmxapi/version.h` file is created so that the built library can
- * report this version through the `gmxapi::Version` interface. Client code should include the installed
- * `gmxapi/version.h` header in order to embed the constants `gmxapi::c_majorVersion`, `gmxapi::c_minorVersion`,
- * and `gmxapi::c_patchVersion` so that compatibility checks can be performed at runtime.
- * Additional CMake `check` utilities will probably be necessary to allow GROMACS
- * installations to be forward compatible with client code developed against the
- * GROMACS master branch.
- *
- * When a new software release is tagged, the next commit on the development branch should increment the patch level
- * to distinguish development builds from the tagged release. As incompatibilities are introduced
- * in feature branches, minor or major version number should be incremented as appropriate.
- * At this time, client code has no indication of whether the version presented in a development build of gmxapi is an
- * officially specified API revision or is subject to change. Developers coding against development branches
- * should keep this in mind. If this becomes problematic, please offer your suggestions or propose a revision
- * to the `gmxapi::Version` API.
- *
- *
- */
-
-#include <string>
-
-namespace gmxapi
-{
-
-/*!
-* \brief Type alias for version data type.
-*
-* \ingroup gmxapi
-*/
-using version_t = int;
-
-//! Major version number of gmxapi API support.
-static constexpr const version_t c_majorVersion     = @GMXAPI_MAJOR@;
-//! Minor version number of gmxapi API support.
-static constexpr const version_t c_minorVersion     = @GMXAPI_MINOR@;
-//! Patch level of gmxapi API support.
-static constexpr const version_t c_patchVersion     = @GMXAPI_PATCH@;
-//! C string representation of gmxapi release.
-static const char                c_release[] = "@GMXAPI_RELEASE@";
-
-/*!
- * \brief Provide API library version information for client code.
- *
- * Allow client code to query the currently loaded gmxapi library object to find the built version. Provide helpers
- * to compare against the features for which the client was written and the headers against which it was compiled.
- *
- * \ingroup gmxapi
- */
-class Version
-{
-    public:
-        /*!
-         * \brief Query gmxapi major version.
-         *
-         * \returns major version number
-         */
-        static version_t majorVersion();
-
-        /*! \brief Query gmxapi minor version.
-         *
-         * \returns minor version number
-         */
-        static version_t minorVersion();
-
-        /*! \brief Query gmxapi patch level.
-         *
-         * \returns patch level number
-         */
-        static version_t patchVersion();
-
-        /*! \brief Get formatted release string.
-         *
-         * Format is major.minor.patch
-         * \returns release as string
-         */
-        static std::string release();
-
-        /*! \brief Check features availability
-         *
-         * Features may be named in the documentation
-         * to improve readability of client code and to simplify development. Prefer
-         * this mechanism when checking for features still under development or to
-         * distinguish between interface levels of a specific feature.
-         * \param featurename Feature name described in the feature's documentation.
-         * \returns `true` if the named feature is available.
-         */
-        static bool hasFeature(const std::string &featurename);
-
-        /*! \brief Check for sufficiently high API version number.
-         *
-         *  \returns `true` if gmxapi library version is the same or greater than the argument(s).
-         *  \param major gmxapi major version number.
-         *  \param minor gmxapi minor version number (optional).
-         *  \param patch patch level of the api library (optional).
-         */
-        static bool isAtLeast(version_t major,
-                              version_t minor = 0,
-                              version_t patch = 0);
-};
-
-}      // namespace gmxapi
-
-#endif // version.h include guard
index f0ea2ce29b6a5a13917cb68bea7b90478cd9cc5d..66380fd929fb2cb3d19111f32fcc0d7b19e87823 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # Test public interface.
 #
 
-gmx_add_gtest_executable(
-    gmxapi-test
-    restraint.cpp
-    status.cpp
-    system.cpp
-    version.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
-    )
-if(NOT GMX_USE_OPENCL)
-    # GPU resources may not be properly reinitialized between simulations in
-    # the same process.
-    # TODO: include this with the other test sources once the issue is resolved
-    # Ref https://redmine.gromacs.org/issues/2689
-    target_sources(gmxapi-test PRIVATE runner.cpp stopsignaler.cpp)
-endif()
+add_library(gmxapi-testsupport INTERFACE)
+target_include_directories(gmxapi-testsupport INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
 
-target_include_directories(gmxapi-test PRIVATE
-                           ${CMAKE_CURRENT_SOURCE_DIR})
+# TODO: Test tMPI build in a MPI-enabled client context.
+gmx_add_gtest_executable(gmxapi-test
+    CPP_SOURCE_FILES
+        restraint.cpp
+        runner.cpp
+        status.cpp
+        stopsignaler.cpp
+        system.cpp
+        version.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
+        )
 
 # Link against the gmxapi libraries and get access to its public (installed) headers.
-target_link_libraries(gmxapi-test PRIVATE Gromacs::gmxapi mdrun_test_infrastructure)
+target_link_libraries(gmxapi-test PRIVATE Gromacs::gmxapi gmxapi-testsupport mdrun_test_infrastructure)
 
-gmx_register_gtest_test(GmxapiExternalInterfaceTests gmxapi-test OPENMP_THREADS 2 INTEGRATION_TEST)
+gmx_register_gtest_test(GmxapiExternalInterfaceTests gmxapi-test OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
 
 set_tests_properties(GmxapiExternalInterfaceTests PROPERTIES
                      WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
@@ -79,26 +75,33 @@ set_tests_properties(GmxapiExternalInterfaceTests PROPERTIES
 #
 # Test public interface with MPI.
 #
+# gmxapi is expected to work whether or not GROMACS was built with MPI (or tMPI)
+# but we don't assume that MPI is available to the build tree or testing tools
+# unless CMake was configured with MPI.
+#
+# For MPI-enabled testing of gmxapi clients using non-MPI-enabled GROMACS, we
+# defer testing to the Python gmxapi client package tests.
+#
+if (GMX_MPI)
+    gmx_add_gtest_executable(gmxapi-mpi-test MPI
+                             CPP_SOURCE_FILES
+                             context.cpp
+                             restraint.cpp
+                             runner.cpp
+                             status.cpp
+                             stopsignaler.cpp
+                             system.cpp
+                             version.cpp
+                             # pseudo-library for code for mdrun
+                             $<TARGET_OBJECTS:mdrun_objlib>
+                             )
 
-gmx_add_gtest_executable(
-    gmxapi-mpi-test MPI
-    restraint.cpp
-    status.cpp
-    system.cpp
-    version.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
-    )
-if(NOT GMX_USE_OPENCL)
-    # GPU resources may not be properly reinitialized between simulations in
-    # the same process.
-    # TODO: include this with the other test sources once the issue is resolved
-    # Ref https://redmine.gromacs.org/issues/2689
-    target_sources(gmxapi-mpi-test PRIVATE runner.cpp stopsignaler.cpp)
-endif()
+    target_include_directories(gmxapi-mpi-test PRIVATE
+                               ${CMAKE_CURRENT_SOURCE_DIR})
+    target_link_libraries(gmxapi-mpi-test PRIVATE Gromacs::gmxapi gmxapi-testsupport mdrun_test_infrastructure)
 
-target_include_directories(gmxapi-mpi-test PRIVATE
-                           ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(gmxapi-mpi-test PRIVATE Gromacs::gmxapi mdrun_test_infrastructure)
+    gmx_register_gtest_test(GmxapiMpiTests gmxapi-mpi-test MPI_RANKS 2 OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
 
-gmx_register_gtest_test(GmxapiMpiTests gmxapi-mpi-test MPI_RANKS 2 OPENMP_THREADS 2 INTEGRATION_TEST)
+    set_tests_properties(GmxapiMpiTests PROPERTIES
+                         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+endif ()
diff --git a/src/api/cpp/tests/context.cpp b/src/api/cpp/tests/context.cpp
new file mode 100644 (file)
index 0000000..e873871
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \brief Test gmxapi::Context
+ *
+ * Provides additional test coverage of template headers only used by client code.
+ */
+
+#include "config.h"
+
+#include <gtest/gtest.h>
+
+#include "gromacs/utility/gmxmpi.h"
+
+#include "gmxapi/context.h"
+#include "gmxapi/mpi/gmxapi_mpi.h"
+
+
+namespace gmxapi
+{
+
+namespace testing
+{
+
+namespace
+{
+
+TEST(GmxApiMpiTest, AllContext)
+{
+    // Default Implicit COMM_WORLD for MPI builds.
+    auto context = createContext();
+}
+
+#if GMX_LIB_MPI
+TEST(GmxApiMpiTest, NullContext)
+{
+    // Explicit COMM_NULL is not supported.
+    EXPECT_ANY_THROW(assignResource(MPI_COMM_NULL));
+}
+
+TEST(GmxApiMpiTest, MpiWorldContext)
+{
+    // Note that this test is only compiled when GMX_MPI is enabled for the
+    // build tree, so we cannot unit test the behavior of non-MPI GROMACS
+    // provided with MPI-enabled Context. For that, we defer to the Python
+    // package testing.
+    // Note also that the code should look the same for tMPI or regular MPI.
+
+    // Explicit COMM_WORLD.
+    auto resources = assignResource(MPI_COMM_WORLD);
+    EXPECT_TRUE(resources->size() != 0);
+
+    // Store the rank for debugging convenience.
+    [[maybe_unused]] auto rank = resources->rank();
+
+    auto context = createContext(*resources);
+}
+
+TEST(GmxApiMpiTest, MpiSplitContext)
+{
+    // Explicit sub-communicator.
+    MPI_Comm communicator = MPI_COMM_NULL;
+    int      rank{ 0 };
+    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+    // Run each rank as a separate ensemble member.
+    MPI_Comm_split(MPI_COMM_WORLD, rank, rank, &communicator);
+    auto context = createContext(*assignResource(communicator));
+}
+#endif
+
+} // end anonymous namespace
+
+} // end namespace testing
+
+} // end namespace gmxapi
index a992527aef73f8c723dcec42279f0f8d360fae1c..11d001db648d6bcf36769e8b4bf9adecbaaacbeb 100644 (file)
@@ -123,7 +123,7 @@ TEST_F(GmxApiTest, ApiRunnerRestrainedMD)
     auto system = gmxapi::fromTprFile(runner_.tprFileName_);
 
     {
-        auto           context = std::make_shared<gmxapi::Context>();
+        auto           context = std::make_shared<gmxapi::Context>(gmxapi::createContext());
         gmxapi::MDArgs args    = makeMdArgs();
 
         context->setMDArgs(args);
index b03903b65c4221d33fe9d2d185f664e4e60bdbd4..d96760836ee2d1906f67d89bc365d8201213f4e7 100644 (file)
@@ -61,7 +61,7 @@ TEST_F(GmxApiTest, RunnerBasicMD)
     auto system = gmxapi::fromTprFile(runner_.tprFileName_);
 
     {
-        auto           context = std::make_shared<gmxapi::Context>();
+        auto           context = std::make_shared<gmxapi::Context>(gmxapi::createContext());
         gmxapi::MDArgs args    = makeMdArgs();
         // TODO the command line arguments should be set through the
         // usual command line options settings for the tests
@@ -82,7 +82,7 @@ TEST_F(GmxApiTest, RunnerBasicMD)
  */
 TEST_F(GmxApiTest, RunnerReinitialize)
 {
-    auto           context = std::make_shared<gmxapi::Context>();
+    auto           context = std::make_shared<gmxapi::Context>(gmxapi::createContext());
     gmxapi::MDArgs args    = makeMdArgs();
 
     makeTprFile(20);
@@ -140,7 +140,7 @@ TEST_F(GmxApiTest, RunnerContinuedMD)
     auto system = gmxapi::fromTprFile(runner_.tprFileName_);
 
     {
-        auto context = std::make_shared<gmxapi::Context>();
+        auto context = std::make_shared<gmxapi::Context>(gmxapi::createContext());
 
         {
             EXPECT_TRUE(context != nullptr);
index 53961ecd69071dd1c2929e27600d3ce377e36cf5..5cfcf21d224ded061e01dbbf4982bddc6c9b4df4 100644 (file)
@@ -33,8 +33,8 @@
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 #include <memory>
+#include <optional>
 
-#include "gromacs/compat/optional.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/restraint/restraintpotential.h"
@@ -113,7 +113,7 @@ public:
     /*!
      * \brief Record the simulation time at the last step active.
      */
-    gmx::compat::optional<double> lastSimulationTime_;
+    std::optional<double> lastSimulationTime_;
 
     /*!
      * \brief Whether restraint was ever used
@@ -180,7 +180,7 @@ TEST_F(GmxApiTest, ApiRunnerStopSignalClient)
     const int nsteps = 1;
     makeTprFile(nsteps);
     auto system  = gmxapi::fromTprFile(runner_.tprFileName_);
-    auto context = std::make_shared<gmxapi::Context>();
+    auto context = std::make_shared<gmxapi::Context>(gmxapi::createContext());
 
     // Check assumptions about basic simulation behavior.
     {
index 2d4cf147f013d884bf1f21b690682fb55e955db4..e7180f8984b17b08518a092a7755d7d49375eda7 100644 (file)
@@ -36,7 +36,7 @@
 #ifndef GROMACS_TESTINGCONFIGURATION_H
 #define GROMACS_TESTINGCONFIGURATION_H
 
-#include <gtest/gtest.h>
+#include "config.h"
 
 #include <string>
 #include <vector>
@@ -48,9 +48,9 @@
 #include "gromacs/utility/stringutil.h"
 #include "gromacs/utility/textwriter.h"
 
+#include "programs/mdrun/tests/moduletest.h"
 #include "testutils/cmdlinetest.h"
 #include "testutils/testfilemanager.h"
-#include "programs/mdrun/tests/moduletest.h"
 
 namespace gmxapi
 {
index 9ac87cb47db3ab5933a571a86f771d42c229a553..251a7ab49293caf1c8f5f50aabc7438f506b0f12 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 2018,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -35,6 +35,8 @@
 
 #include <climits>
 
+#include <gtest/gtest.h>
+
 #include "gmxapi/version.h"
 #include "testingconfiguration.h"
 
index e999f58437e9a21c9d4c47e2952a6a74d576dfba..65074a7f149f5032faeca7d4a0c29d04b364f5bb 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-#
 # Test private interface.
 #
 
-gmx_add_gtest_executable(
-    workflow-details-test
-    workflow.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
+gmx_add_gtest_executable(workflow-details-test
+    CPP_SOURCE_FILES
+        workflow.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
 )
 
-target_link_libraries(workflow-details-test PRIVATE Gromacs::gmxapi gmxapi-detail mdrun_test_infrastructure)
+target_link_libraries(workflow-details-test
+                      PRIVATE
+                        Gromacs::gmxapi
+                        gmxapi-detail
+                        gmxapi-testsupport
+                        mdrun_test_infrastructure)
 
-gmx_register_gtest_test(GmxapiInternalInterfaceTests workflow-details-test OPENMP_THREADS 2 INTEGRATION_TEST)
+gmx_register_gtest_test(GmxapiInternalInterfaceTests workflow-details-test OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
 
 set_tests_properties(GmxapiInternalInterfaceTests PROPERTIES
                      WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
@@ -53,14 +57,30 @@ set_tests_properties(GmxapiInternalInterfaceTests PROPERTIES
 #
 # Test with MPI.
 #
+# gmxapi is expected to work whether or not GROMACS was built with MPI (or tMPI)
+# but we don't assume that MPI is available to the build tree or testing tools
+# unless CMake was configured with MPI.
+#
+# For MPI-enabled testing of gmxapi clients using non-MPI-enabled GROMACS, we
+# defer testing to the Python gmxapi client package tests.
+#
+if (GMX_MPI)
+    gmx_add_gtest_executable(workflow-details-mpi-test MPI
+                             CPP_SOURCE_FILES
+                             workflow.cpp
+                             # pseudo-library for code for mdrun
+                             $<TARGET_OBJECTS:mdrun_objlib>
+                             )
 
-gmx_add_gtest_executable(
-    workflow-details-mpi-test MPI
-    workflow.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
-)
+    target_link_libraries(workflow-details-mpi-test
+                          PRIVATE
+                          Gromacs::gmxapi
+                          gmxapi-detail
+                          gmxapi-testsupport
+                          mdrun_test_infrastructure)
 
-target_link_libraries(workflow-details-mpi-test PRIVATE Gromacs::gmxapi gmxapi-detail mdrun_test_infrastructure)
+    gmx_register_gtest_test(GmxapiInternalsMpiTests workflow-details-mpi-test MPI_RANKS 2 OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
 
-gmx_register_gtest_test(GmxapiInternalsMpiTests workflow-details-mpi-test MPI_RANKS 2 OPENMP_THREADS 2 INTEGRATION_TEST)
+    set_tests_properties(GmxapiInternalsMpiTests PROPERTIES
+                         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+endif ()
index 15aa305226b9947fbe511318d9407287aa798392..f3210b2d509869a2d1fde5e441c92d7338491fff 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <memory>
 
-#include "api/cpp/workflow.h"
-#include "api/cpp/workflow_impl.h"
-#include "api/cpp/include/gmxapi/context.h"
-#include "api/cpp/include/gmxapi/status.h"
-#include "api/cpp/include/gmxapi/system.h"
-
-#include "gromacs/utility/arrayref.h"
-
-#include "api/cpp/tests/testingconfiguration.h"
+#include "workflow.h"
+#include "workflow_impl.h"
+#include "testingconfiguration.h"
 
 namespace gmxapi
 {
index 8a7a641e9b6b24ff484dca8ca4c7864bc088c474..ba5f3658b67f8352f588e1a463d504499e4754a6 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e8ab5c617f77703872d63ba9db38bf58bb47dfca..0343a50cd406e7355956c19f166b1ee3dada40ee 100644 (file)
@@ -1,8 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014, The GROMACS development team.
- * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 /* ARM (AArch64) NEON Advanced SIMD instruction set level was selected */
 #cmakedefine01 GMX_SIMD_ARM_NEON_ASIMD
 
+/* ARM (SVE) Scalable Vector extensions */
+#cmakedefine01 GMX_SIMD_ARM_SVE
+
+/* SVE vector length */
+#define GMX_SIMD_ARM_SVE_LENGTH @GMX_SIMD_ARM_SVE_LENGTH@
+
 /* IBM VMX was selected as SIMD instructions (Power 6 and later) */
 #cmakedefine01 GMX_SIMD_IBM_VMX
 
 /* Enable code that requires AVX-512 instruction support, without GMX_SIMD=AVX_512 */
 #cmakedefine01 SIMD_AVX_512_CXX_SUPPORTED
 
+/* Whether NBNXM and other SIMD kernels should be compiled */
+#cmakedefine01 GMX_USE_SIMD_KERNELS
+
 /* Whether a double-precision configuration may target accuracy equivalent to single precision */
 #cmakedefine01 GMX_RELAXED_DOUBLE_PRECISION
 
 /* Use if we cannot rename checkpoints */
 #cmakedefine01 GMX_NO_RENAME
 
-/* Use (modified) Gamess-UK for QM-MM calculations */
-#cmakedefine01 GMX_QMMM_GAMESS
-
-/* Use (modified) Gaussian0x for QM-MM calculations */
-#cmakedefine01 GMX_QMMM_GAUSSIAN
-
-/* Use (modified) Mopac 7 for QM-MM calculations */
-#cmakedefine01 GMX_QMMM_MOPAC
-
-/* Use ORCA for QM-MM calculations */
-#cmakedefine01 GMX_QMMM_ORCA
-
-/* Use cycle counters */
-#cmakedefine01 GMX_CYCLECOUNTERS
-
 /* Use sub-counters */
 #cmakedefine01 GMX_CYCLE_SUBCOUNTERS
 
 /* Enable x86 gcc inline assembly */
 #cmakedefine01 GMX_X86_GCC_INLINE_ASM
 
-/* Define constants useful for handling GPU support */
-#define GMX_GPU_NONE   0
-#define GMX_GPU_CUDA   1
-#define GMX_GPU_OPENCL 2
-/* Which kind of GPU support is configured */
-#define GMX_GPU @GMX_GPU_ACCELERATION_FRAMEWORK@
+/* Define if any type of GPU acceleration is compiled */
+#cmakedefine01 GMX_GPU
+
+/* Define if CUDA GPU acceleration is compiled */
+#cmakedefine01 GMX_GPU_CUDA
+
+/* Define if OpenCL GPU acceleration is compiled */
+#cmakedefine01 GMX_GPU_OPENCL
+
+/* Define if SYCL GPU acceleration is compiled */
+#cmakedefine01 GMX_GPU_SYCL
 
 /* Use a single compilation unit when compiling the CUDA (non-bonded) kernels.  */
 #cmakedefine01 GMX_CUDA_NB_SINGLE_COMPILATION_UNIT
 #define CMAKE_BUILD_TYPE_TSAN 8
 #define CMAKE_BUILD_TYPE_ASAN 9
 #define CMAKE_BUILD_TYPE_MSAN 10
+#define CMAKE_BUILD_TYPE_UBSAN 11
 #cmakedefine CMAKE_BUILD_TYPE CMAKE_BUILD_TYPE_@CMAKE_BUILD_TYPE_UPPER@
 
 /* Define relative path to OpenCL kernels */
 /* Define to 1 if you have the gettimeofday() function. */
 #cmakedefine01 HAVE_GETTIMEOFDAY
 
-/* Define to 1 if you have the rdtscp instruction. */
-#cmakedefine01 HAVE_RDTSCP
+/* Define to 1 if you have the rdtscp instruction (ie essentially all x86 still in use. */
+#cmakedefine01 GMX_USE_RDTSCP
 
 /* Define to 1 if you have the fsync() function. */
 #cmakedefine01 HAVE_FSYNC
 /* Define if we have lmfit support */
 #cmakedefine01 HAVE_LMFIT
 
+/* Define if we have muparser support */
+#cmakedefine01 HAVE_MUPARSER
+
 /* Build using clang analyzer */
 #cmakedefine01 GMX_CLANG_ANALYZER
 
index 6c7e8344ab2af1b509b0422334705f4a6b45507b..e850736ec45f0a4c378bb7cea7d87b0b29b8c2da 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2011,2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+# Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+# Copyright (c) 2016,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -51,15 +52,20 @@ if(BUILD_TESTING AND GMX_BUILD_UNITTESTS)
     endif()
 
     add_subdirectory(googletest EXCLUDE_FROM_ALL)
-    target_compile_definitions(gmock PRIVATE
-                               _GNU_SOURCE=1
-                               GTEST_CAN_STREAM_RESULTS=0)
-    if (WIN32)
-        target_compile_definitions(gmock PRIVATE _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
-        target_compile_definitions(gtest PRIVATE _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
-        target_compile_definitions(gtest_main PRIVATE _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
+    target_compile_definitions(gmock
+        PRIVATE
+            _GNU_SOURCE=1
+            GTEST_CAN_STREAM_RESULTS=0
+        PUBLIC
+            GTEST_LANG_CXX11
+            )
+    target_compile_definitions(gtest PUBLIC GTEST_LANG_CXX11)
+    if (CYGWIN)
+        # Ensure GoogleTest can find POSIX things it needs
+        target_compile_definitions(gtest PRIVATE _POSIX_C_SOURCE=200809L)
     endif()
     gmx_target_warning_suppression(gmock -Wno-deprecated-copy HAVE_NO_DEPRECATED_COPY)
 
     set(GTEST_IS_THREADSAFE "${GTEST_IS_THREADSAFE}" PARENT_SCOPE)
 endif()
+add_subdirectory(boost)
similarity index 82%
rename from admin/builds/get-version-info.py
rename to src/external/boost/CMakeLists.txt
index 29af7691b37801dc48c23d363fefbb7c2853b1f4..6f0dc691392ce48b0804f8b87c4c76b1f3588969 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2016, by the GROMACS development team, led by
+# Copyright (c) 2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-import json
-
-def do_build(context):
-    cmd = [context.env.cmake_command, '-P', 'cmake/gmxVersionInfo.cmake']
-    info_json = context.run_cmd(cmd, use_output=True)
-    values = json.loads(info_json)
-    context.set_version_info(values['version'], values['regressiontest-md5sum'])
+if(GMX_INSTALL_LEGACY_API)
+  install(FILES
+          stl_interfaces/fwd.hpp  stl_interfaces/iterator_interface.hpp
+          DESTINATION include/gromacs/external/boost/stl_interfaces)
+endif()
diff --git a/src/external/boost/README b/src/external/boost/README
new file mode 100644 (file)
index 0000000..98420c4
--- /dev/null
@@ -0,0 +1,3 @@
+stl_interfaces
+==============
+6486db0a5543cea2901ed380fff010ae9e6acf39 from https://github.com/boostorg/stl_interfaces
diff --git a/src/external/boost/stl_interfaces/fwd.hpp b/src/external/boost/stl_interfaces/fwd.hpp
new file mode 100644 (file)
index 0000000..6c41833
--- /dev/null
@@ -0,0 +1,95 @@
+// Copyright (C) 2019 T. Zachary Laine
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_STL_INTERFACES_FWD_HPP
+#define BOOST_STL_INTERFACES_FWD_HPP
+
+#include <iterator>
+
+#ifndef BOOST_STL_INTERFACES_DOXYGEN
+
+#if defined(_MSC_VER) || defined(__GNUC__) && __GNUC__ < 8
+#define BOOST_STL_INTERFACES_NO_HIDDEN_FRIEND_CONSTEXPR
+#define BOOST_STL_INTERFACES_HIDDEN_FRIEND_CONSTEXPR
+#else
+#define BOOST_STL_INTERFACES_HIDDEN_FRIEND_CONSTEXPR constexpr
+#endif
+
+#if defined(__GNUC__) && __GNUC__ < 9
+#define BOOST_STL_INTERFACES_CONCEPT concept bool
+#else
+#define BOOST_STL_INTERFACES_CONCEPT concept
+#endif
+
+#endif
+
+
+namespace boost { namespace stl_interfaces {
+    inline namespace v1 {
+
+        /** An enumeration used to indicate whether the underlying data have a
+            contiguous or discontiguous layout when instantiating
+            `view_interface` and `sequence_container_interface`. */
+        enum class element_layout : bool {
+            discontiguous = false,
+            contiguous = true
+        };
+
+        namespace v1_dtl {
+            template<typename... T>
+            using void_t = void;
+
+            template<typename Iter>
+            using iter_difference_t =
+                typename std::iterator_traits<Iter>::difference_type;
+
+            template<typename Range, typename = void>
+            struct iterator;
+            template<typename Range>
+            struct iterator<
+                Range,
+                void_t<decltype(std::declval<Range &>().begin())>>
+            {
+                using type = decltype(std::declval<Range &>().begin());
+            };
+            template<typename Range>
+            using iterator_t = typename iterator<Range>::type;
+
+            template<typename Range, typename = void>
+            struct sentinel;
+            template<typename Range>
+            struct sentinel<
+                Range,
+                void_t<decltype(std::declval<Range &>().end())>>
+            {
+                using type = decltype(std::declval<Range &>().end());
+            };
+            template<typename Range>
+            using sentinel_t = typename sentinel<Range>::type;
+
+            template<typename Range>
+            using range_difference_t = iter_difference_t<iterator_t<Range>>;
+
+            template<typename Range>
+            using common_range =
+                std::is_same<iterator_t<Range>, sentinel_t<Range>>;
+
+            template<typename Range, typename = void>
+            struct decrementable_sentinel : std::false_type
+            {
+            };
+            template<typename Range>
+            struct decrementable_sentinel<
+                Range,
+                void_t<decltype(--std::declval<sentinel_t<Range> &>())>>
+                : std::true_type
+            {
+            };
+        }
+
+    }
+}}
+
+#endif
diff --git a/src/external/boost/stl_interfaces/iterator_interface.hpp b/src/external/boost/stl_interfaces/iterator_interface.hpp
new file mode 100644 (file)
index 0000000..5f39619
--- /dev/null
@@ -0,0 +1,623 @@
+// Copyright (C) 2019 T. Zachary Laine
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_STL_INTERFACES_ITERATOR_INTERFACE_HPP
+#define BOOST_STL_INTERFACES_ITERATOR_INTERFACE_HPP
+
+#include "./fwd.hpp"
+
+#include <utility>
+#include <type_traits>
+#if defined(__cpp_lib_three_way_comparison)
+#include <compare>
+#endif
+
+
+namespace boost { namespace stl_interfaces {
+
+    /** A type for granting access to the private members of an iterator
+        derived from `iterator_interface`. */
+    struct access
+    {
+#ifndef BOOST_STL_INTERFACES_DOXYGEN
+
+        template<typename D>
+        static constexpr auto base(D & d) noexcept
+            -> decltype(d.base_reference())
+        {
+            return d.base_reference();
+        }
+        template<typename D>
+        static constexpr auto base(D const & d) noexcept
+            -> decltype(d.base_reference())
+        {
+            return d.base_reference();
+        }
+
+#endif
+    };
+
+    /** The return type of `operator->()` in a proxy iterator.
+
+        This template is used as the default `Pointer` template parameter in
+        the `proxy_iterator_interface` template alias.  Note that the use of
+        this template implies a copy or move of the underlying object of type
+        `T`. */
+    template<typename T>
+    struct proxy_arrow_result
+    {
+        constexpr proxy_arrow_result(T const & value) noexcept(
+            noexcept(T(value))) :
+            value_(value)
+        {}
+        constexpr proxy_arrow_result(T && value) noexcept(
+            noexcept(T(std::move(value)))) :
+            value_(std::move(value))
+        {}
+
+        constexpr T const * operator->() const noexcept { return &value_; }
+        constexpr T * operator->() noexcept { return &value_; }
+
+    private:
+        T value_;
+    };
+
+    namespace detail {
+        template<typename Pointer, typename T>
+        auto make_pointer(
+            T && value,
+            std::enable_if_t<std::is_pointer<Pointer>::value, int> = 0)
+            -> decltype(std::addressof(value))
+        {
+            return std::addressof(value);
+        }
+
+        template<typename Pointer, typename T>
+        auto make_pointer(
+            T && value,
+            std::enable_if_t<!std::is_pointer<Pointer>::value, int> = 0)
+        {
+            return Pointer(std::forward<T>(value));
+        }
+
+        template<typename IteratorConcept>
+        struct concept_category
+        {
+            using type = IteratorConcept;
+        };
+        template<typename IteratorConcept>
+        using concept_category_t =
+            typename concept_category<IteratorConcept>::type;
+
+        template<typename Pointer, typename IteratorConcept>
+        struct pointer
+        {
+            using type = Pointer;
+        };
+        template<typename Pointer>
+        struct pointer<Pointer, std::output_iterator_tag>
+        {
+            using type = void;
+        };
+        template<typename Pointer, typename IteratorConcept>
+        using pointer_t = typename pointer<Pointer, IteratorConcept>::type;
+
+        template<typename T, typename U>
+        using interoperable = std::integral_constant<
+            bool,
+            (std::is_convertible<T, U>::value ||
+             std::is_convertible<U, T>::value)>;
+
+        template<typename T, typename U>
+        using common_t =
+            std::conditional_t<std::is_convertible<T, U>::value, U, T>;
+
+        template<typename T>
+        using use_base = decltype(access::base(std::declval<T &>()));
+
+        template<typename... T>
+        using void_t = void;
+
+        template<
+            typename AlwaysVoid,
+            template<class...> class Template,
+            typename... Args>
+        struct detector : std::false_type
+        {
+        };
+
+        template<template<class...> class Template, typename... Args>
+        struct detector<void_t<Template<Args...>>, Template, Args...>
+            : std::true_type
+        {
+        };
+
+        template<
+            typename T,
+            typename U,
+            bool UseBase = detector<void, use_base, T>::value>
+        struct common_eq
+        {
+            static constexpr auto call(T lhs, U rhs)
+            {
+                return static_cast<common_t<T, U>>(lhs).derived() ==
+                       static_cast<common_t<T, U>>(rhs).derived();
+            }
+        };
+        template<typename T, typename U>
+        struct common_eq<T, U, true>
+        {
+            static constexpr auto call(T lhs, U rhs)
+            {
+                return access::base(lhs) == access::base(rhs);
+            }
+        };
+
+        template<typename T, typename U>
+        constexpr auto common_diff(T lhs, U rhs) noexcept(noexcept(
+            static_cast<common_t<T, U>>(lhs) -
+            static_cast<common_t<T, U>>(rhs)))
+            -> decltype(
+                static_cast<common_t<T, U>>(lhs) -
+                static_cast<common_t<T, U>>(rhs))
+        {
+            return static_cast<common_t<T, U>>(lhs) -
+                   static_cast<common_t<T, U>>(rhs);
+        }
+    }
+
+}}
+
+namespace boost { namespace stl_interfaces { inline namespace v1 {
+
+    /** A CRTP template that one may derive from to make defining iterators
+        easier.
+
+        The template parameter `D` for `iterator_interface` may be an
+        incomplete type.  Before any member of the resulting specialization of
+        `iterator_interface` other than special member functions is
+        referenced, `D` shall be complete, and model
+        `std::derived_from<iterator_interface<D>>`. */
+    template<
+        typename Derived,
+        typename IteratorConcept,
+        typename ValueType,
+        typename Reference = ValueType &,
+        typename Pointer = ValueType *,
+        typename DifferenceType = std::ptrdiff_t
+#ifndef BOOST_STL_INTERFACES_DOXYGEN
+        ,
+        typename E = std::enable_if_t<
+            std::is_class<Derived>::value &&
+            std::is_same<Derived, std::remove_cv_t<Derived>>::value>
+#endif
+        >
+    struct iterator_interface;
+
+    namespace v1_dtl {
+        template<typename Iterator, typename = void>
+        struct ra_iter : std::false_type
+        {
+        };
+        template<typename Iterator>
+        struct ra_iter<Iterator, void_t<typename Iterator::iterator_concept>>
+            : std::integral_constant<
+                  bool,
+                  std::is_base_of<
+                      std::random_access_iterator_tag,
+                      typename Iterator::iterator_concept>::value>
+        {
+        };
+
+        template<typename Iterator, typename DifferenceType, typename = void>
+        struct plus_eq : std::false_type
+        {
+        };
+        template<typename Iterator, typename DifferenceType>
+        struct plus_eq<
+            Iterator,
+            DifferenceType,
+            void_t<decltype(
+                std::declval<Iterator &>() += std::declval<DifferenceType>())>>
+            : std::true_type
+        {
+        };
+
+        template<
+            typename D,
+            typename IteratorConcept,
+            typename ValueType,
+            typename Reference,
+            typename Pointer,
+            typename DifferenceType>
+        void derived_iterator(iterator_interface<
+                              D,
+                              IteratorConcept,
+                              ValueType,
+                              Reference,
+                              Pointer,
+                              DifferenceType> const &);
+    }
+
+    template<
+        typename Derived,
+        typename IteratorConcept,
+        typename ValueType,
+        typename Reference,
+        typename Pointer,
+        typename DifferenceType
+#ifndef BOOST_STL_INTERFACES_DOXYGEN
+        ,
+        typename E
+#endif
+        >
+    struct iterator_interface
+    {
+#ifndef BOOST_STL_INTERFACES_DOXYGEN
+    private:
+        constexpr Derived & derived() noexcept
+        {
+            return static_cast<Derived &>(*this);
+        }
+        constexpr Derived const & derived() const noexcept
+        {
+            return static_cast<Derived const &>(*this);
+        }
+
+        template<typename T, typename U, bool UseBase>
+        friend struct detail::common_eq;
+#endif
+
+    public:
+        using iterator_concept = IteratorConcept;
+        using iterator_category = detail::concept_category_t<iterator_concept>;
+        using value_type = std::remove_const_t<ValueType>;
+        using reference = Reference;
+        using pointer = detail::pointer_t<Pointer, iterator_concept>;
+        using difference_type = DifferenceType;
+
+        template<typename D = Derived>
+        constexpr auto operator*() const
+            noexcept(noexcept(*access::base(std::declval<D const &>())))
+                -> decltype(*access::base(std::declval<D const &>()))
+        {
+            return *access::base(derived());
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator-> () const noexcept(
+            noexcept(detail::make_pointer<pointer>(*std::declval<D const &>())))
+            -> decltype(
+                detail::make_pointer<pointer>(*std::declval<D const &>()))
+        {
+            return detail::make_pointer<pointer>(*derived());
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator[](difference_type i) const noexcept(noexcept(
+            D(std::declval<D const &>()),
+            std::declval<D &>() += i,
+            *std::declval<D &>()))
+            -> decltype(std::declval<D &>() += i, *std::declval<D &>())
+        {
+            D retval = derived();
+            retval += i;
+            return *retval;
+        }
+
+        template<
+            typename D = Derived,
+            typename Enable =
+                std::enable_if_t<!v1_dtl::plus_eq<D, difference_type>::value>>
+        constexpr auto
+        operator++() noexcept(noexcept(++access::base(std::declval<D &>())))
+            -> decltype(++access::base(std::declval<D &>()))
+        {
+            return ++access::base(derived());
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator++() noexcept(
+            noexcept(std::declval<D &>() += difference_type(1)))
+            -> decltype(
+                std::declval<D &>() += difference_type(1), std::declval<D &>())
+        {
+            derived() += difference_type(1);
+            return derived();
+        }
+        template<typename D = Derived>
+        constexpr auto operator++(int)noexcept(
+            noexcept(D(std::declval<D &>()), ++std::declval<D &>()))
+            -> std::remove_reference_t<decltype(
+                D(std::declval<D &>()),
+                ++std::declval<D &>(),
+                std::declval<D &>())>
+        {
+            D retval = derived();
+            ++derived();
+            return retval;
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator+=(difference_type n) noexcept(
+            noexcept(access::base(std::declval<D &>()) += n))
+            -> decltype(access::base(std::declval<D &>()) += n)
+        {
+            return access::base(derived()) += n;
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator+(difference_type i) const
+            noexcept(noexcept(D(std::declval<D &>()), std::declval<D &>() += i))
+                -> std::remove_reference_t<decltype(
+                    D(std::declval<D &>()),
+                    std::declval<D &>() += i,
+                    std::declval<D &>())>
+        {
+            D retval = derived();
+            retval += i;
+            return retval;
+        }
+        friend BOOST_STL_INTERFACES_HIDDEN_FRIEND_CONSTEXPR Derived
+        operator+(difference_type i, Derived it) noexcept
+        {
+            return it + i;
+        }
+
+        template<
+            typename D = Derived,
+            typename Enable =
+                std::enable_if_t<!v1_dtl::plus_eq<D, difference_type>::value>>
+        constexpr auto
+        operator--() noexcept(noexcept(--access::base(std::declval<D &>())))
+            -> decltype(--access::base(std::declval<D &>()))
+        {
+            return --access::base(derived());
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator--() noexcept(noexcept(
+            D(std::declval<D &>()), std::declval<D &>() += -difference_type(1)))
+            -> decltype(
+                std::declval<D &>() += -difference_type(1), std::declval<D &>())
+        {
+            derived() += -difference_type(1);
+            return derived();
+        }
+        template<typename D = Derived>
+        constexpr auto operator--(int)noexcept(
+            noexcept(D(std::declval<D &>()), --std::declval<D &>()))
+            -> std::remove_reference_t<decltype(
+                D(std::declval<D &>()),
+                --std::declval<D &>(),
+                std::declval<D &>())>
+        {
+            D retval = derived();
+            --derived();
+            return retval;
+        }
+
+        template<typename D = Derived>
+        constexpr D & operator-=(difference_type i) noexcept
+        {
+            derived() += -i;
+            return derived();
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator-(D other) const noexcept(noexcept(
+            access::base(std::declval<D const &>()) - access::base(other)))
+            -> decltype(
+                access::base(std::declval<D const &>()) - access::base(other))
+        {
+            return access::base(derived()) - access::base(other);
+        }
+
+        friend BOOST_STL_INTERFACES_HIDDEN_FRIEND_CONSTEXPR Derived
+        operator-(Derived it, difference_type i) noexcept
+        {
+            Derived retval = it;
+            retval += -i;
+            return retval;
+        }
+    };
+
+    /** Implementation of `operator==()`, implemented in terms of the iterator
+        underlying IteratorInterface, for all iterators derived from
+        `iterator_interface`, except those with an iterator category derived
+        from `std::random_access_iterator_tag`.  */
+    template<
+        typename IteratorInterface1,
+        typename IteratorInterface2,
+        typename Enable =
+            std::enable_if_t<!v1_dtl::ra_iter<IteratorInterface1>::value>>
+    constexpr auto
+    operator==(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept
+        -> decltype(
+            access::base(std::declval<IteratorInterface1 &>()) ==
+            access::base(std::declval<IteratorInterface2 &>()))
+    {
+        return access::base(lhs) == access::base(rhs);
+    }
+
+    /** Implementation of `operator==()` for all iterators derived from
+        `iterator_interface` that have an iterator category derived from
+        `std::random_access_iterator_tag`.  */
+    template<
+        typename IteratorInterface1,
+        typename IteratorInterface2,
+        typename Enable =
+            std::enable_if_t<v1_dtl::ra_iter<IteratorInterface1>::value>>
+    constexpr auto
+    operator==(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept(
+        noexcept(detail::common_diff(lhs, rhs)))
+        -> decltype(
+            v1_dtl::derived_iterator(lhs), detail::common_diff(lhs, rhs) == 0)
+    {
+        return detail::common_diff(lhs, rhs) == 0;
+    }
+
+    /** Implementation of `operator!=()` for all iterators derived from
+        `iterator_interface`.  */
+    template<typename IteratorInterface1, typename IteratorInterface2>
+    constexpr auto operator!=(
+        IteratorInterface1 lhs,
+        IteratorInterface2 rhs) noexcept(noexcept(!(lhs == rhs)))
+        -> decltype(v1_dtl::derived_iterator(lhs), !(lhs == rhs))
+    {
+        return !(lhs == rhs);
+    }
+
+    /** Implementation of `operator<()` for all iterators derived from
+        `iterator_interface` that have an iterator category derived from
+        `std::random_access_iterator_tag`.  */
+    template<typename IteratorInterface1, typename IteratorInterface2>
+    constexpr auto
+    operator<(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept(
+        noexcept(detail::common_diff(lhs, rhs)))
+        -> decltype(
+            v1_dtl::derived_iterator(lhs), detail::common_diff(lhs, rhs) < 0)
+    {
+        return detail::common_diff(lhs, rhs) < 0;
+    }
+
+    /** Implementation of `operator<=()` for all iterators derived from
+        `iterator_interface` that have an iterator category derived from
+        `std::random_access_iterator_tag`.  */
+    template<typename IteratorInterface1, typename IteratorInterface2>
+    constexpr auto
+    operator<=(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept(
+        noexcept(detail::common_diff(lhs, rhs)))
+        -> decltype(
+            v1_dtl::derived_iterator(lhs), detail::common_diff(lhs, rhs) <= 0)
+    {
+        return detail::common_diff(lhs, rhs) <= 0;
+    }
+
+    /** Implementation of `operator>()` for all iterators derived from
+        `iterator_interface` that have an iterator category derived from
+        `std::random_access_iterator_tag`.  */
+    template<typename IteratorInterface1, typename IteratorInterface2>
+    constexpr auto
+    operator>(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept(
+        noexcept(detail::common_diff(lhs, rhs)))
+        -> decltype(
+            v1_dtl::derived_iterator(lhs), detail::common_diff(lhs, rhs) > 0)
+    {
+        return detail::common_diff(lhs, rhs) > 0;
+    }
+
+    /** Implementation of `operator>=()` for all iterators derived from
+        `iterator_interface` that have an iterator category derived from
+        `std::random_access_iterator_tag`.  */
+    template<typename IteratorInterface1, typename IteratorInterface2>
+    constexpr auto
+    operator>=(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept(
+        noexcept(detail::common_diff(lhs, rhs)))
+        -> decltype(
+            v1_dtl::derived_iterator(lhs), detail::common_diff(lhs, rhs) >= 0)
+    {
+        return detail::common_diff(lhs, rhs) >= 0;
+    }
+
+
+    /** A template alias useful for defining proxy iterators.  \see
+        `iterator_interface`. */
+    template<
+        typename Derived,
+        typename IteratorConcept,
+        typename ValueType,
+        typename Reference = ValueType,
+        typename DifferenceType = std::ptrdiff_t>
+    using proxy_iterator_interface = iterator_interface<
+        Derived,
+        IteratorConcept,
+        ValueType,
+        Reference,
+        proxy_arrow_result<Reference>,
+        DifferenceType>;
+
+}}}
+
+#ifdef BOOST_STL_INTERFACES_DOXYGEN
+
+/** `static_asserts` that type `type` models concept `concept_name`.  This is
+    useful for checking that an iterator, view, etc. that you write using one
+    of the *`_interface` templates models the right C++ concept.
+
+    For example: `BOOST_STL_INTERFACES_STATIC_ASSERT_CONCEPT(my_iter,
+    std::input_iterator)`.
+
+    \note This macro expands to nothing when `__cpp_lib_concepts` is not
+    defined. */
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_CONCEPT(type, concept_name)
+
+/** `static_asserts` that the types of all typedefs in
+    `std::iterator_traits<iter>` match the remaining macro parameters.  This
+    is useful for checking that an iterator you write using
+    `iterator_interface` has the correct iterator traits.
+
+    For example: `BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS(my_iter,
+    std::input_iterator_tag, std::input_iterator_tag, int, int &, int *, std::ptrdiff_t)`.
+
+    \note This macro ignores the `concept` parameter when `__cpp_lib_concepts`
+    is not defined. */
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS(                    \
+    iter, category, concept, value_type, reference, pointer, difference_type)
+
+#else
+
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_CONCEPT_IMPL(              \
+    type, concept_name)                                                        \
+    static_assert(concept_name<type>, "");
+
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_CONCEPT(iter, concept_name)
+
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS_IMPL(               \
+    iter, category, value_t, ref, ptr, diff_t)                                 \
+    static_assert(                                                             \
+        std::is_same<                                                          \
+            typename std::iterator_traits<iter>::iterator_category,            \
+            category>::value,                                                  \
+        "");                                                                   \
+    static_assert(                                                             \
+        std::is_same<                                                          \
+            typename std::iterator_traits<iter>::value_type,                   \
+            value_t>::value,                                                   \
+        "");                                                                   \
+    static_assert(                                                             \
+        std::is_same<typename std::iterator_traits<iter>::reference, ref>::    \
+            value,                                                             \
+        "");                                                                   \
+    static_assert(                                                             \
+        std::is_same<typename std::iterator_traits<iter>::pointer, ptr>::      \
+            value,                                                             \
+        "");                                                                   \
+    static_assert(                                                             \
+        std::is_same<                                                          \
+            typename std::iterator_traits<iter>::difference_type,              \
+            diff_t>::value,                                                    \
+        "");
+
+#if 201703L < __cplusplus && defined(__cpp_lib_ranges)
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS(                    \
+    iter, category, concept, value_type, reference, pointer, difference_type)  \
+    static_assert(                                                             \
+        std::is_same<                                                          \
+            typename std::iterator_traits<iter>::iterator_concept,             \
+            concept>::value,                                                   \
+        "");                                                                   \
+    BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS_IMPL(                   \
+        iter, category, value_type, reference, pointer, difference_type)
+#else
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS(                    \
+    iter, category, concept, value_type, reference, pointer, difference_type)  \
+    BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS_IMPL(                   \
+        iter, category, value_type, reference, pointer, difference_type)
+#endif
+
+#endif
+
+#endif
index a0a1379a0124f0e41e0f42a6bb10c4f95e7b0fe9..8cfcd361ed9ac1b39b9f2a63b7f81ad5e4c2341f 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index f15657b9807bca373c02a0f1e98987fcadf6d5c4..6063cb3573d84cf77425d3d482920a1e5391d5d8 100644 (file)
@@ -30,3 +30,7 @@ substituting the version number placeholders with version 2.14.0
 
 To compile everything as C++ the src/library/md5sum.c is renamed to cpp and
 the extern "C" is removed from src/library/fft_binary_lookup.cpp.
+
+To be compatible with ISO C++17, we have removed the "register" storage
+class in src/statTimer/statisticalTimer.CPU.cpp and
+src/statTimer/statisticalTimer.GPU.cpp
index b5bcdd87b5f679a1f4e6f3c340f326c0a5dbc1e0..0a0d57dce6cce1fd5439d258807a5f595aaceee0 100644 (file)
@@ -18,8 +18,8 @@ endif()
 # Windows doesn't need anything special to load the dynamic libraries
 # that the AMD clFFT implementation uses, but *nix and BSD do.
 if (NOT WIN32)
-    include(CheckCXXSymbolExists)
-    check_cxx_symbol_exists(dlopen dlfcn.h HAVE_DLOPEN)
+    include(gmxTestdlopen)
+    gmx_test_dlopen(HAVE_DLOPEN)
     if (NOT HAVE_DLOPEN)
         message(FATAL_ERROR "Cannot use clFFT for OpenCL support unless dlopen is available")
     endif()
index 4e3c8c2edeeea9d558e2af5161335839fadeafeb..a5a9ed72a4b4a2d23c7ba923ac8d9a859f148e02 100644 (file)
@@ -41,7 +41,7 @@ std::basic_string<TCHAR> commatize (T number)
 {
        static TCHAR scratch [8*sizeof(T)];
 
-       register TCHAR * ptr = scratch + countOf( scratch );
+       TCHAR * ptr = scratch + countOf( scratch );
        *(--ptr) = 0;
 
        for (int digits = 3; ; )
index 403178ef358dc50e6320e94bc4ccf027874336b0..822cd401828bd9a282bf43b0d14bf0b11b3ddc92 100644 (file)
@@ -33,7 +33,7 @@ std::basic_string<TCHAR> commatize (T number)
 {
        static TCHAR scratch [8*sizeof(T)];
 
-       register TCHAR * ptr = scratch + countOf( scratch );
+       TCHAR * ptr = scratch + countOf( scratch );
        *(--ptr) = 0;
 
        for (int digits = 3; ; )
index 52065ff152b0a77f7890107afdb1a97a38415cc3..6867471d4840c88855d549fe6723afb922c7bfd2 100644 (file)
@@ -1,8 +1,8 @@
 This directory contains source code for Google C++ Testing and Mocking
 Frameworks.
 
-The code has been copied nearly verbatim from the GoogleTest 1.8.0
-tarball by copying the following files/directories recursively:
+The code has been copied nearly verbatim from the GoogleTest 1.8.1
+git tag by copying the following files/directories recursively:
 
 README.md
 googletest
@@ -12,20 +12,15 @@ However, the following files/subdirectories were excluded because they
 are unneeded for GROMACS:
 
 *.pump
-travis.h
-appveyor.yml
-build-aux
-codegear
-configure.ac
-Makefile.am
-msvc
-m4
-xcode
-test
-scripts
-googletest/docs
+googletest/configure.ac
+googletest/Makefile.am
+googletest/codegear
+googletest/msvc
+googletest/m4
+googletest/xcode
+googletest/test
+googletest/scripts
 googletest/samples
-googlemock/docs
 
 This README.Gromacs file is new, of course.
 
@@ -58,3 +53,8 @@ use this feature, we'd just want to turn it off, but there's no
 interface for that. So, GTEST_CAN_STREAM_RESULTS is added in
 googletest/include/gtest/internal/gtest-port.h to allow the default
 behaviour to be applied only if the user hasn't gotten involved.
+
+Some compiler warnings cannot be suppressed through CMake machinery,
+such as for the clang static analyzer, and some lines are removed
+from consideration by wrapping with `#ifndef __clang_analyzer__`
+... `#endif`.
index 076484e4fad92229bd15f620972ae66f384d1145..fe5694148640480c55d583f505a2608f93a8e894 100644 (file)
@@ -2,7 +2,13 @@
 # Google Test #
 
 [![Build Status](https://travis-ci.org/google/googletest.svg?branch=master)](https://travis-ci.org/google/googletest)
-[![Build status](https://ci.appveyor.com/api/projects/status/4o38plt0xbo1ubc8/branch/master?svg=true)](https://ci.appveyor.com/project/BillyDonahue/googletest/branch/master)
+[![Build status](https://ci.appveyor.com/api/projects/status/4o38plt0xbo1ubc8/branch/master?svg=true)](https://ci.appveyor.com/project/GoogleTestAppVeyor/googletest/branch/master)
+
+**Future Plans**:
+* 1.8.x Release - the 1.8.x will be the last release that works with pre-C++11 compilers. The 1.8.x will not accept any requests for any new features and any bugfix requests will only be accepted if proven "critical"
+* Post 1.8.x - work to improve/cleanup/pay technical debt. When this work is completed there will be a 1.9.x tagged release
+* Post 1.9.x googletest will follow [Abseil Live at Head philosophy](https://abseil.io/about/philosophy)
+
 
 Welcome to **Google Test**, Google's C++ test framework!
 
@@ -12,11 +18,11 @@ maintain and release them together.
 
 Please see the project page above for more information as well as the
 mailing list for questions, discussions, and development.  There is
-also an IRC channel on OFTC (irc.oftc.net) #gtest available.  Please
+also an IRC channel on [OFTC](https://webchat.oftc.net/) (irc.oftc.net) #gtest available.  Please
 join us!
 
-Getting started information for **Google Test** is available in the 
-[Google Test Primer](googletest/docs/Primer.md) documentation.
+Getting started information for **Google Test** is available in the
+[Google Test Primer](googletest/docs/primer.md) documentation.
 
 **Google Mock** is an extension to Google Test for writing and using C++ mock
 classes.  See the separate [Google Mock documentation](googlemock/README.md).
@@ -26,7 +32,7 @@ in its interior [googletest/README.md](googletest/README.md) file.
 
 ## Features ##
 
-  * An [XUnit](https://en.wikipedia.org/wiki/XUnit) test framework.
+  * An [xUnit](https://en.wikipedia.org/wiki/XUnit) test framework.
   * Test discovery.
   * A rich set of assertions.
   * User-defined assertions.
@@ -60,9 +66,12 @@ the following notable projects:
   * [Protocol Buffers](https://github.com/google/protobuf), Google's data
     interchange format.
   * The [OpenCV](http://opencv.org/) computer vision library.
+  * [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn): header only, dependency-free deep learning framework in C++11.
 
 ## Related Open Source Projects ##
 
+[GTest Runner](https://github.com/nholthaus/gtest-runner) is a Qt5 based automated test-runner and Graphical User Interface with powerful features for Windows and Linux platforms.
+
 [Google Test UI](https://github.com/ospector/gtest-gbar) is test runner that runs
 your test binary, allows you to track its progress via a progress bar, and
 displays a list of test failures. Clicking on one shows failure text. Google
@@ -73,6 +82,11 @@ listener for Google Test that implements the
 [TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test
 result output. If your test runner understands TAP, you may find it useful.
 
+[gtest-parallel](https://github.com/google/gtest-parallel) is a test runner that
+runs tests from your binary in parallel to provide significant speed-up.
+
+[GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter) is a VS Code extension allowing to view Google Tests in a tree view, and run/debug your tests.
+
 ## Requirements ##
 
 Google Test is designed to have fairly minimal requirements to build
@@ -82,7 +96,7 @@ effort to support other platforms (e.g. Solaris, AIX, and z/OS).
 However, since core members of the Google Test project have no access
 to these platforms, Google Test may have outstanding issues there.  If
 you notice any problems on your platform, please notify
-<googletestframework@googlegroups.com>. Patches for fixing them are
+[googletestframework@googlegroups.com](https://groups.google.com/forum/#!forum/googletestframework). Patches for fixing them are
 even more welcome!
 
 ### Linux Requirements ###
@@ -97,7 +111,7 @@ package (as described below):
 
 ### Windows Requirements ###
 
-  * Microsoft Visual C++ v7.1 or newer
+  * Microsoft Visual C++ 2015 or newer
 
 ### Cygwin Requirements ###
 
@@ -108,35 +122,9 @@ package (as described below):
   * Mac OS X v10.4 Tiger or newer
   * Xcode Developer Tools
 
-### Requirements for Contributors ###
-
-We welcome patches.  If you plan to contribute a patch, you need to
-build Google Test and its own tests from a git checkout (described
-below), which has further requirements:
-
-  * [Python](https://www.python.org/) v2.3 or newer (for running some of
-    the tests and re-generating certain source files from templates)
-  * [CMake](https://cmake.org/) v2.6.4 or newer
-
-## Regenerating Source Files ##
-
-Some of Google Test's source files are generated from templates (not
-in the C++ sense) using a script.
-For example, the
-file include/gtest/internal/gtest-type-util.h.pump is used to generate
-gtest-type-util.h in the same directory.
-
-You don't need to worry about regenerating the source files
-unless you need to modify them.  You would then modify the
-corresponding `.pump` files and run the '[pump.py](googletest/scripts/pump.py)'
-generator script.  See the [Pump Manual](googletest/docs/PumpManual.md).
-
-### Contributing Code ###
+## Contributing change
 
-We welcome patches.  Please read the
-[Developer's Guide](googletest/docs/DevGuide.md)
-for how you can contribute. In particular, make sure you have signed
-the Contributor License Agreement, or we won't be able to accept the
-patch.
+Please read the [`CONTRIBUTING.md`](CONTRIBUTING.md) for details on
+how to contribute to this project.
 
 Happy testing!
index 681cccf33d2f3ee5cbace264e46333040cfbc3e8..ceece30fe108dae5cd482a7ecf267b5cfae8bbcf 100644 (file)
@@ -85,7 +85,7 @@ cxx_library(gmock
             "${cxx_strict}"
 #            "${gtest_dir}/src/gtest-all.cc"
             src/gmock-all.cc)
-target_link_libraries(gmock gtest)
+target_link_libraries(gmock PUBLIC gtest)
 
 #cxx_library(gmock_main
 #            "${cxx_strict}"
index ef4451b878654f31de1428fcf00e55b8aa20cdd4..d54dd16ae24d9a7d19403e782b256826f2822abb 100644 (file)
@@ -65,7 +65,7 @@ can specify it by appending `_WITH_CALLTYPE` to any of the macros
 described in the previous two sections and supplying the calling
 convention as the first argument to the macro. For example,
 ```
-  MOCK_METHOD_1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int n));
+  MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int n));
   MOCK_CONST_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, Bar, int(double x, double y));
 ```
 where `STDMETHODCALLTYPE` is defined by `<objbase.h>` on Windows.
@@ -178,6 +178,8 @@ divided into several categories:
 |`Ne(value)`           |`argument != value`|
 |`IsNull()`            |`argument` is a `NULL` pointer (raw or smart).|
 |`NotNull()`           |`argument` is a non-null pointer (raw or smart).|
+|`VariantWith<T>(m)`   |`argument` is `variant<>` that holds the alternative of
+type T with a value matching `m`.|
 |`Ref(variable)`       |`argument` is a reference to `variable`.|
 |`TypedEq<type>(value)`|`argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded.|
 
@@ -227,7 +229,7 @@ The `argument` can be either a C string or a C++ string object:
 
 `ContainsRegex()` and `MatchesRegex()` use the regular expression
 syntax defined
-[here](../../googletest/docs/AdvancedGuide.md#regular-expression-syntax).
+[here](../../googletest/docs/advanced.md#regular-expression-syntax).
 `StrCaseEq()`, `StrCaseNe()`, `StrEq()`, and `StrNe()` work for wide
 strings as well.
 
@@ -249,7 +251,7 @@ match them more flexibly, or get more informative messages, you can use:
 | `SizeIs(m)`              | `argument` is a container whose size matches `m`. E.g. `SizeIs(2)` or `SizeIs(Lt(2))`.                                           |
 | `UnorderedElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, and under some permutation each element matches an `ei` (for a different `i`), which can be a value or a matcher. 0 to 10 arguments are allowed. |
 | `UnorderedElementsAreArray({ e0, e1, ..., en })`, `UnorderedElementsAreArray(array)`, or `UnorderedElementsAreArray(array, count)` | The same as `UnorderedElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, or C-style array. |
-| `WhenSorted(m)`          | When `argument` is sorted using the `<` operator, it matches container matcher `m`. E.g. `WhenSorted(UnorderedElementsAre(1, 2, 3))` verifies that `argument` contains elements `1`, `2`, and `3`, ignoring order. |
+| `WhenSorted(m)`          | When `argument` is sorted using the `<` operator, it matches container matcher `m`. E.g. `WhenSorted(ElementsAre(1, 2, 3))` verifies that `argument` contains elements `1`, `2`, and `3`, ignoring order. |
 | `WhenSortedBy(comparator, m)` | The same as `WhenSorted(m)`, except that the given comparator instead of `<` is used to sort `argument`. E.g. `WhenSortedBy(std::greater<int>(), ElementsAre(3, 2, 1))`. |
 
 Notes:
@@ -347,7 +349,7 @@ You can make a matcher from one or more other matchers:
 
 ## Matchers as Test Assertions ##
 
-|`ASSERT_THAT(expression, m)`|Generates a [fatal failure](../../googletest/docs/Primer.md#assertions) if the value of `expression` doesn't match matcher `m`.|
+|`ASSERT_THAT(expression, m)`|Generates a [fatal failure](../../googletest/docs/primer.md#assertions) if the value of `expression` doesn't match matcher `m`.|
 |:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------|
 |`EXPECT_THAT(expression, m)`|Generates a non-fatal failure if the value of `expression` doesn't match matcher `m`.                                                          |
 
index c52f1009d1df685a856c16e0f1985ad27de86f2b..8809b0e7e48074913ac3106e4e2c1af74f58375e 100644 (file)
@@ -18,8 +18,9 @@ You must always put a mock method definition (`MOCK_METHOD*`) in a
 `public:` section of the mock class, regardless of the method being
 mocked being `public`, `protected`, or `private` in the base class.
 This allows `ON_CALL` and `EXPECT_CALL` to reference the mock function
-from outside of the mock class.  (Yes, C++ allows a subclass to change
-the access level of a virtual function in the base class.)  Example:
+from outside of the mock class.  (Yes, C++ allows a subclass to specify
+a different access level than the base class on a virtual function.)
+Example:
 
 ```
 class Foo {
@@ -147,7 +148,7 @@ Note that the mock class doesn't define `AppendPacket()`, unlike the
 real class. That's fine as long as the test doesn't need to call it.
 
 Next, you need a way to say that you want to use
-`ConcretePacketStream` in production code, and use `MockPacketStream`
+`ConcretePacketStream` in production code and to use `MockPacketStream`
 in tests.  Since the functions are not virtual and the two classes are
 unrelated, you must specify your choice at _compile time_ (as opposed
 to run time).
@@ -218,7 +219,7 @@ per-function syntactic overhead will be much lower.
 
 If you are concerned about the performance overhead incurred by
 virtual functions, and profiling confirms your concern, you can
-combine this with the recipe for [mocking non-virtual methods](#Mocking_Nonvirtual_Methods.md).
+combine this with the recipe for [mocking non-virtual methods](#mocking-nonvirtual-methods).
 
 ## The Nice, the Strict, and the Naggy ##
 
@@ -226,7 +227,7 @@ If a mock method has no `EXPECT_CALL` spec but is called, Google Mock
 will print a warning about the "uninteresting call". The rationale is:
 
   * New methods may be added to an interface after a test is written. We shouldn't fail a test just because a method it doesn't know about is called.
-  * However, this may also mean there's a bug in the test, so Google Mock shouldn't be silent either. If the user believes these calls are harmless, he can add an `EXPECT_CALL()` to suppress the warning.
+  * However, this may also mean there's a bug in the test, so Google Mock shouldn't be silent either. If the user believes these calls are harmless, they can add an `EXPECT_CALL()` to suppress the warning.
 
 However, sometimes you may want to suppress all "uninteresting call"
 warnings, while sometimes you may want the opposite, i.e. to treat all
@@ -294,7 +295,7 @@ There are some caveats though (I don't like them just as much as the
 next guy, but sadly they are side effects of C++'s limitations):
 
   1. `NiceMock<MockFoo>` and `StrictMock<MockFoo>` only work for mock methods defined using the `MOCK_METHOD*` family of macros **directly** in the `MockFoo` class. If a mock method is defined in a **base class** of `MockFoo`, the "nice" or "strict" modifier may not affect it, depending on the compiler. In particular, nesting `NiceMock` and `StrictMock` (e.g. `NiceMock<StrictMock<MockFoo> >`) is **not** supported.
-  1. The constructors of the base mock (`MockFoo`) cannot have arguments passed by non-const reference, which happens to be banned by the [Google C++ style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml).
+  1. The constructors of the base mock (`MockFoo`) cannot have arguments passed by non-const reference, which happens to be banned by the [Google C++ style guide](https://google.github.io/styleguide/cppguide.html).
   1. During the constructor or destructor of `MockFoo`, the mock object is _not_ nice or strict.  This may cause surprises if the constructor or destructor calls a mock method on `this` object. (This behavior, however, is consistent with C++'s general rule: if a constructor or destructor calls a virtual method of `this` object, that method is treated as non-virtual.  In other words, to the base class's constructor or destructor, `this` object behaves like an instance of the base class, not the derived class.  This rule is required for safety.  Otherwise a base constructor may use members of a derived class before they are initialized, or a base destructor may use members of a derived class after they have been destroyed.)
 
 Finally, you should be **very cautious** about when to use naggy or strict mocks, as they tend to make tests more brittle and harder to maintain. When you refactor your code without changing its externally visible behavior, ideally you should't need to update any tests. If your code interacts with a naggy mock, however, you may start to get spammed with warnings as the result of your change. Worse, if your code interacts with a strict mock, your tests may start to fail and you'll be forced to fix them. Our general recommendation is to use nice mocks (not yet the default) most of the time, use naggy mocks (the current default) when developing or debugging tests, and use strict mocks only as the last resort.
@@ -705,7 +706,7 @@ type `m` accepts):
   1. When both `T` and `U` are built-in arithmetic types (`bool`, integers, and floating-point numbers), the conversion from `T` to `U` is not lossy (in other words, any value representable by `T` can also be represented by `U`); and
   1. When `U` is a reference, `T` must also be a reference (as the underlying matcher may be interested in the address of the `U` value).
 
-The code won't compile if any of these conditions isn't met.
+The code won't compile if any of these conditions aren't met.
 
 Here's one example:
 
@@ -1029,9 +1030,10 @@ a value that satisfies matcher `m`.
 
 For example:
 
-> | `Field(&Foo::number, Ge(3))` | Matches `x` where `x.number >= 3`. |
+| Expression                   | Description                        |
 |:-----------------------------|:-----------------------------------|
-> | `Property(&Foo::name, StartsWith("John "))` | Matches `x` where `x.name()` starts with `"John "`. |
+| `Field(&Foo::number, Ge(3))` | Matches `x` where `x.number >= 3`. |
+| `Property(&Foo::name, StartsWith("John "))` | Matches `x` where `x.name()` starts with `"John "`. |
 
 Note that in `Property(&Foo::baz, ...)`, method `baz()` must take no
 argument and be declared as `const`.
@@ -1229,7 +1231,7 @@ that references the implementation object dies, the implementation
 object will be deleted.
 
 Therefore, if you have some complex matcher that you want to use again
-and again, there is no need to build it everytime. Just assign it to a
+and again, there is no need to build it every time. Just assign it to a
 matcher variable and use that variable repeatedly! For example,
 
 ```
@@ -1401,7 +1403,7 @@ edge from node A to node B wherever A must occur before B, we can get
 a DAG. We use the term "sequence" to mean a directed path in this
 DAG. Now, if we decompose the DAG into sequences, we just need to know
 which sequences each `EXPECT_CALL()` belongs to in order to be able to
-reconstruct the orginal DAG.
+reconstruct the original DAG.
 
 So, to specify the partial order on the expectations we need to do two
 things: first to define some `Sequence` objects, and then for each
@@ -1680,7 +1682,7 @@ This also works when the argument is an output iterator:
 
 ```
 using ::testing::_;
-using ::testing::SeArrayArgument;
+using ::testing::SetArrayArgument;
 
 class MockRolodex : public Rolodex {
  public:
@@ -1919,9 +1921,9 @@ using ::testing::_;
   // second argument DoThis() receives.
 ```
 
-Arghh, you need to refer to a mock function argument but C++ has no
-lambda (yet), so you have to define your own action. :-( Or do you
-really?
+Arghh, you need to refer to a mock function argument but your version
+of C++ has no lambdas, so you have to define your own action. :-(
+Or do you really?
 
 Well, Google Mock has an action to solve _exactly_ this problem:
 
@@ -2180,7 +2182,7 @@ the implementation object dies, the implementation object will be
 deleted.
 
 If you have some complex action that you want to use again and again,
-you may not have to build it from scratch everytime. If the action
+you may not have to build it from scratch every time. If the action
 doesn't have an internal state (i.e. if it always does the same thing
 no matter how many times it has been called), you can assign it to an
 action variable and use that variable repeatedly. For example:
@@ -2227,77 +2229,71 @@ versus
 
 ## Mocking Methods That Use Move-Only Types ##
 
-C++11 introduced <em>move-only types</em>.  A move-only-typed value can be moved from one object to another, but cannot be copied.  `std::unique_ptr<T>` is probably the most commonly used move-only type.
+C++11 introduced *move-only types*. A move-only-typed value can be moved from
+one object to another, but cannot be copied. `std::unique_ptr<T>` is
+probably the most commonly used move-only type.
 
-Mocking a method that takes and/or returns move-only types presents some challenges, but nothing insurmountable.  This recipe shows you how you can do it.
+Mocking a method that takes and/or returns move-only types presents some
+challenges, but nothing insurmountable. This recipe shows you how you can do it.
+Note that the support for move-only method arguments was only introduced to
+gMock in April 2017; in older code, you may find more complex
+[workarounds](#LegacyMoveOnly) for lack of this feature.
 
-Let’s say we are working on a fictional project that lets one post and share snippets called “buzzes”.  Your code uses these types:
+Let’s say we are working on a fictional project that lets one post and share
+snippets called “buzzes”. Your code uses these types:
 
-```
+```cpp
 enum class AccessLevel { kInternal, kPublic };
 
 class Buzz {
  public:
-  explicit Buzz(AccessLevel access) {  }
+  explicit Buzz(AccessLevel access) { ... }
   ...
 };
 
 class Buzzer {
  public:
   virtual ~Buzzer() {}
-  virtual std::unique_ptr<Buzz> MakeBuzz(const std::string& text) = 0;
-  virtual bool ShareBuzz(std::unique_ptr<Buzz> buzz, Time timestamp) = 0;
+  virtual std::unique_ptr<Buzz> MakeBuzz(StringPiece text) = 0;
+  virtual bool ShareBuzz(std::unique_ptr<Buzz> buzz, int64_t timestamp) = 0;
   ...
 };
 ```
 
-A `Buzz` object represents a snippet being posted.  A class that implements the `Buzzer` interface is capable of creating and sharing `Buzz`.  Methods in `Buzzer` may return a `unique_ptr<Buzz>` or take a `unique_ptr<Buzz>`.  Now we need to mock `Buzzer` in our tests.
-
-To mock a method that returns a move-only type, you just use the familiar `MOCK_METHOD` syntax as usual:
-
-```
-class MockBuzzer : public Buzzer {
- public:
-  MOCK_METHOD1(MakeBuzz, std::unique_ptr<Buzz>(const std::string& text));
-  …
-};
-```
-
-However, if you attempt to use the same `MOCK_METHOD` pattern to mock a method that takes a move-only parameter, you’ll get a compiler error currently:
-
-```
-  // Does NOT compile!
-  MOCK_METHOD2(ShareBuzz, bool(std::unique_ptr<Buzz> buzz, Time timestamp));
-```
-
-While it’s highly desirable to make this syntax just work, it’s not trivial and the work hasn’t been done yet.  Fortunately, there is a trick you can apply today to get something that works nearly as well as this.
+A `Buzz` object represents a snippet being posted. A class that implements the
+`Buzzer` interface is capable of creating and sharing `Buzz`es. Methods in
+`Buzzer` may return a `unique_ptr<Buzz>` or take a
+`unique_ptr<Buzz>`. Now we need to mock `Buzzer` in our tests.
 
-The trick, is to delegate the `ShareBuzz()` method to a mock method (let’s call it `DoShareBuzz()`) that does not take move-only parameters:
+To mock a method that accepts or returns move-only types, you just use the
+familiar `MOCK_METHOD` syntax as usual:
 
-```
+```cpp
 class MockBuzzer : public Buzzer {
  public:
-  MOCK_METHOD1(MakeBuzz, std::unique_ptr<Buzz>(const std::string& text));
-  MOCK_METHOD2(DoShareBuzz, bool(Buzz* buzz, Time timestamp));
-  bool ShareBuzz(std::unique_ptr<Buzz> buzz, Time timestamp) {
-    return DoShareBuzz(buzz.get(), timestamp);
-  }
+  MOCK_METHOD1(MakeBuzz, std::unique_ptr<Buzz>(StringPiece text));
+  MOCK_METHOD2(ShareBuzz, bool(std::unique_ptr<Buzz> buzz, int64_t timestamp));
 };
 ```
 
-Note that there's no need to define or declare `DoShareBuzz()` in a base class.  You only need to define it as a `MOCK_METHOD` in the mock class.
-
-Now that we have the mock class defined, we can use it in tests.  In the following code examples, we assume that we have defined a `MockBuzzer` object named `mock_buzzer_`:
+Now that we have the mock class defined, we can use it in tests. In the
+following code examples, we assume that we have defined a `MockBuzzer` object
+named `mock_buzzer_`:
 
-```
+```cpp
   MockBuzzer mock_buzzer_;
 ```
 
-First let’s see how we can set expectations on the `MakeBuzz()` method, which returns a `unique_ptr<Buzz>`.
+First let’s see how we can set expectations on the `MakeBuzz()` method, which
+returns a `unique_ptr<Buzz>`.
 
-As usual, if you set an expectation without an action (i.e. the `.WillOnce()` or `.WillRepeated()` clause), when that expectation fires, the default action for that method will be taken.  Since `unique_ptr<>` has a default constructor that returns a null `unique_ptr`, that’s what you’ll get if you don’t specify an action:
+As usual, if you set an expectation without an action (i.e. the `.WillOnce()` or
+`.WillRepeated()` clause), when that expectation fires, the default action for
+that method will be taken. Since `unique_ptr<>` has a default constructor
+that returns a null `unique_ptr`, that’s what you’ll get if you don’t specify an
+action:
 
-```
+```cpp
   // Use the default action.
   EXPECT_CALL(mock_buzzer_, MakeBuzz("hello"));
 
@@ -2305,32 +2301,13 @@ As usual, if you set an expectation without an action (i.e. the `.WillOnce()` or
   EXPECT_EQ(nullptr, mock_buzzer_.MakeBuzz("hello"));
 ```
 
-If you are not happy with the default action, you can tweak it.  Depending on what you need, you may either tweak the default action for a specific (mock object, mock method) combination using `ON_CALL()`, or you may tweak the default action for all mock methods that return a specific type.  The usage of `ON_CALL()` is similar to `EXPECT_CALL()`, so we’ll skip it and just explain how to do the latter (tweaking the default action for a specific return type).  You do this via the `DefaultValue<>::SetFactory()` and `DefaultValue<>::Clear()` API:
-
-```
-  // Sets the default action for return type std::unique_ptr<Buzz> to
-  // creating a new Buzz every time.
-  DefaultValue<std::unique_ptr<Buzz>>::SetFactory(
-      [] { return MakeUnique<Buzz>(AccessLevel::kInternal); });
-
-  // When this fires, the default action of MakeBuzz() will run, which
-  // will return a new Buzz object.
-  EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")).Times(AnyNumber());
+If you are not happy with the default action, you can tweak it as usual; see
+[Setting Default Actions](#OnCall).
 
-  auto buzz1 = mock_buzzer_.MakeBuzz("hello");
-  auto buzz2 = mock_buzzer_.MakeBuzz("hello");
-  EXPECT_NE(nullptr, buzz1);
-  EXPECT_NE(nullptr, buzz2);
-  EXPECT_NE(buzz1, buzz2);
+If you just need to return a pre-defined move-only value, you can use the
+`Return(ByMove(...))` action:
 
-  // Resets the default action for return type std::unique_ptr<Buzz>,
-  // to avoid interfere with other tests.
-  DefaultValue<std::unique_ptr<Buzz>>::Clear();
-```
-
-What if you want the method to do something other than the default action?  If you just need to return a pre-defined move-only value, you can use the `Return(ByMove(...))` action:
-
-```
+```cpp
   // When this fires, the unique_ptr<> specified by ByMove(...) will
   // be returned.
   EXPECT_CALL(mock_buzzer_, MakeBuzz("world"))
@@ -2341,81 +2318,87 @@ What if you want the method to do something other than the default action?  If y
 
 Note that `ByMove()` is essential here - if you drop it, the code won’t compile.
 
-Quiz time!  What do you think will happen if a `Return(ByMove(...))` action is performed more than once (e.g. you write `….WillRepeatedly(Return(ByMove(...)));`)?  Come think of it, after the first time the action runs, the source value will be consumed (since it’s a move-only value), so the next time around, there’s no value to move from -- you’ll get a run-time error that `Return(ByMove(...))` can only be run once.
+Quiz time! What do you think will happen if a `Return(ByMove(...))` action is
+performed more than once (e.g. you write
+`.WillRepeatedly(Return(ByMove(...)));`)? Come think of it, after the first
+time the action runs, the source value will be consumed (since it’s a move-only
+value), so the next time around, there’s no value to move from -- you’ll get a
+run-time error that `Return(ByMove(...))` can only be run once.
 
-If you need your mock method to do more than just moving a pre-defined value, remember that you can always use `Invoke()` to call a lambda or a callable object, which can do pretty much anything you want:
+If you need your mock method to do more than just moving a pre-defined value,
+remember that you can always use a lambda or a callable object, which can do
+pretty much anything you want:
 
-```
+```cpp
   EXPECT_CALL(mock_buzzer_, MakeBuzz("x"))
-      .WillRepeatedly(Invoke([](const std::string& text) {
-        return std::make_unique<Buzz>(AccessLevel::kInternal);
-      }));
+      .WillRepeatedly([](StringPiece text) {
+        return MakeUnique<Buzz>(AccessLevel::kInternal);
+      });
 
   EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x"));
   EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x"));
 ```
 
-Every time this `EXPECT_CALL` fires, a new `unique_ptr<Buzz>` will be created and returned.  You cannot do this with `Return(ByMove(...))`.
+Every time this `EXPECT_CALL` fires, a new `unique_ptr<Buzz>` will be
+created and returned. You cannot do this with `Return(ByMove(...))`.
 
-Now there’s one topic we haven’t covered: how do you set expectations on `ShareBuzz()`, which takes a move-only-typed parameter?  The answer is you don’t.  Instead, you set expectations on the `DoShareBuzz()` mock method (remember that we defined a `MOCK_METHOD` for `DoShareBuzz()`, not `ShareBuzz()`):
+That covers returning move-only values; but how do we work with methods
+accepting move-only arguments? The answer is that they work normally, although
+some actions will not compile when any of method's arguments are move-only. You
+can always use `Return`, or a [lambda or functor](#FunctionsAsActions):
 
-```
-  EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _));
+```cpp
+  using ::testing::Unused;
 
-  // When one calls ShareBuzz() on the MockBuzzer like this, the call is
-  // forwarded to DoShareBuzz(), which is mocked.  Therefore this statement
-  // will trigger the above EXPECT_CALL.
-  mock_buzzer_.ShareBuzz(MakeUnique&lt;Buzz&gt;(AccessLevel::kInternal),
-                         ::base::Now());
+  EXPECT_CALL(mock_buzzer_, ShareBuzz(NotNull(), _)) .WillOnce(Return(true));
+  EXPECT_TRUE(mock_buzzer_.ShareBuzz(MakeUnique<Buzz>(AccessLevel::kInternal)),
+              0);
+
+  EXPECT_CALL(mock_buzzer_, ShareBuzz(_, _)) .WillOnce(
+      [](std::unique_ptr<Buzz> buzz, Unused) { return buzz != nullptr; });
+  EXPECT_FALSE(mock_buzzer_.ShareBuzz(nullptr, 0));
 ```
 
-Some of you may have spotted one problem with this approach: the `DoShareBuzz()` mock method differs from the real `ShareBuzz()` method in that it cannot take ownership of the buzz parameter - `ShareBuzz()` will always delete buzz after `DoShareBuzz()` returns.  What if you need to save the buzz object somewhere for later use when `ShareBuzz()` is called?  Indeed, you'd be stuck.
+Many built-in actions (`WithArgs`, `WithoutArgs`,`DeleteArg`, `SaveArg`, ...)
+could in principle support move-only arguments, but the support for this is not
+implemented yet. If this is blocking you, please file a bug.
 
-Another problem with the `DoShareBuzz()` we had is that it can surprise people reading or maintaining the test, as one would expect that `DoShareBuzz()` has (logically) the same contract as `ShareBuzz()`.
+A few actions (e.g. `DoAll`) copy their arguments internally, so they can never
+work with non-copyable objects; you'll have to use functors instead.
 
-Fortunately, these problems can be fixed with a bit more code.  Let's try to get it right this time:
+##### Legacy workarounds for move-only types {#LegacyMoveOnly}
 
-```
+Support for move-only function arguments was only introduced to gMock in April
+2017. In older code, you may encounter the following workaround for the lack of
+this feature (it is no longer necessary - we're including it just for
+reference):
+
+```cpp
 class MockBuzzer : public Buzzer {
  public:
-  MockBuzzer() {
-    // Since DoShareBuzz(buzz, time) is supposed to take ownership of
-    // buzz, define a default behavior for DoShareBuzz(buzz, time) to
-    // delete buzz.
-    ON_CALL(*this, DoShareBuzz(_, _))
-        .WillByDefault(Invoke([](Buzz* buzz, Time timestamp) {
-          delete buzz;
-          return true;
-        }));
-  }
-
-  MOCK_METHOD1(MakeBuzz, std::unique_ptr<Buzz>(const std::string& text));
-
-  // Takes ownership of buzz.
   MOCK_METHOD2(DoShareBuzz, bool(Buzz* buzz, Time timestamp));
-  bool ShareBuzz(std::unique_ptr<Buzz> buzz, Time timestamp) {
-    return DoShareBuzz(buzz.release(), timestamp);
+  bool ShareBuzz(std::unique_ptr<Buzz> buzz, Time timestamp) override {
+    return DoShareBuzz(buzz.get(), timestamp);
   }
 };
 ```
 
-Now, the mock `DoShareBuzz()` method is free to save the buzz argument for later use if this is what you want:
+The trick is to delegate the `ShareBuzz()` method to a mock method (let’s call
+it `DoShareBuzz()`) that does not take move-only parameters. Then, instead of
+setting expectations on `ShareBuzz()`, you set them on the `DoShareBuzz()` mock
+method:
 
-```
-  std::unique_ptr<Buzz> intercepted_buzz;
-  EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _))
-      .WillOnce(Invoke([&amp;intercepted_buzz](Buzz* buzz, Time timestamp) {
-        // Save buzz in intercepted_buzz for analysis later.
-        intercepted_buzz.reset(buzz);
-        return false;
-      }));
+```cpp
+  MockBuzzer mock_buzzer_;
+  EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _));
 
-  mock_buzzer_.ShareBuzz(std::make_unique<Buzz>(AccessLevel::kInternal),
-                         Now());
-  EXPECT_NE(nullptr, intercepted_buzz);
+  // When one calls ShareBuzz() on the MockBuzzer like this, the call is
+  // forwarded to DoShareBuzz(), which is mocked.  Therefore this statement
+  // will trigger the above EXPECT_CALL.
+  mock_buzzer_.ShareBuzz(MakeUnique<Buzz>(AccessLevel::kInternal), 0);
 ```
 
-Using the tricks covered in this recipe, you are now able to mock methods that take and/or return move-only types.  Put your newly-acquired power to good use - when you design a new API, you can now feel comfortable using `unique_ptrs` as appropriate, without fearing that doing so will compromise your tests.
+
 
 ## Making the Compilation Faster ##
 
@@ -2482,12 +2465,12 @@ MockFoo::~MockFoo() {}
 
 ## Forcing a Verification ##
 
-When it's being destoyed, your friendly mock object will automatically
+When it's being destroyed, your friendly mock object will automatically
 verify that all expectations on it have been satisfied, and will
 generate [Google Test](../../googletest/) failures
 if not. This is convenient as it leaves you with one less thing to
 worry about. That is, unless you are not sure if your mock object will
-be destoyed.
+be destroyed.
 
 How could it be that your mock object won't eventually be destroyed?
 Well, it might be created on the heap and owned by the code you are
@@ -3347,6 +3330,7 @@ For example, when using an `ACTION` as a stub action for mock function:
 int DoSomething(bool flag, int* ptr);
 ```
 we have:
+
 | **Pre-defined Symbol** | **Is Bound To** |
 |:-----------------------|:----------------|
 | `arg0`                 | the value of `flag` |
@@ -3508,6 +3492,7 @@ is asked to infer the type of `x`?
 If you are writing a function that returns an `ACTION` object, you'll
 need to know its type.  The type depends on the macro used to define
 the action and the parameter types.  The rule is relatively simple:
+
 | **Given Definition** | **Expression** | **Has Type** |
 |:---------------------|:---------------|:-------------|
 | `ACTION(Foo)`        | `Foo()`        | `FooAction`  |
@@ -3515,7 +3500,7 @@ the action and the parameter types.  The rule is relatively simple:
 | `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP<int>` |
 | `ACTION_TEMPLATE(Bar, HAS_m_TEMPLATE_PARAMS(...), AND_1_VALUE_PARAMS(p1))` | `Bar<t1, ..., t_m>(int_value)` | `FooActionP<t1, ..., t_m, int>` |
 | `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2<bool, int>` |
-| `ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))` | `Baz<t1, ..., t_m>(bool_value, int_value)` | `FooActionP2<t1, ..., t_m, bool, int>` |
+| `ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))`| `Baz<t1, ..., t_m>(bool_value, int_value)` | `FooActionP2<t1, ..., t_m, bool, int>` |
 | ...                  | ...            | ...          |
 
 Note that we have to pick different suffixes (`Action`, `ActionP`,
@@ -3670,6 +3655,6 @@ This printer knows how to print built-in C++ types, native arrays, STL
 containers, and any type that supports the `<<` operator.  For other
 types, it prints the raw bytes in the value and hopes that you the
 user can figure it out.
-[Google Test's advanced guide](../../googletest/docs/AdvancedGuide.md#teaching-google-test-how-to-print-your-values)
+[Google Test's advanced guide](../../googletest/docs/advanced.md#teaching-google-test-how-to-print-your-values)
 explains how to extend the printer to do a better job at
 printing your particular type than to dump the bytes.
diff --git a/src/external/googletest/googlemock/docs/DevGuide.md b/src/external/googletest/googlemock/docs/DevGuide.md
deleted file mode 100644 (file)
index f4bab75..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
-If you are interested in understanding the internals of Google Mock,
-building from source, or contributing ideas or modifications to the
-project, then this document is for you.
-
-# Introduction #
-
-First, let's give you some background of the project.
-
-## Licensing ##
-
-All Google Mock source and pre-built packages are provided under the [New BSD License](http://www.opensource.org/licenses/bsd-license.php).
-
-## The Google Mock Community ##
-
-The Google Mock community exists primarily through the [discussion group](http://groups.google.com/group/googlemock), the
-[issue tracker](https://github.com/google/googletest/issues) and, to a lesser extent, the [source control repository](../). You are definitely encouraged to contribute to the
-discussion and you can also help us to keep the effectiveness of the
-group high by following and promoting the guidelines listed here.
-
-### Please Be Friendly ###
-
-Showing courtesy and respect to others is a vital part of the Google
-culture, and we strongly encourage everyone participating in Google
-Mock development to join us in accepting nothing less. Of course,
-being courteous is not the same as failing to constructively disagree
-with each other, but it does mean that we should be respectful of each
-other when enumerating the 42 technical reasons that a particular
-proposal may not be the best choice. There's never a reason to be
-antagonistic or dismissive toward anyone who is sincerely trying to
-contribute to a discussion.
-
-Sure, C++ testing is serious business and all that, but it's also
-a lot of fun. Let's keep it that way. Let's strive to be one of the
-friendliest communities in all of open source.
-
-### Where to Discuss Google Mock ###
-
-As always, discuss Google Mock in the official [Google C++ Mocking Framework discussion group](http://groups.google.com/group/googlemock).  You don't have to actually submit
-code in order to sign up. Your participation itself is a valuable
-contribution.
-
-# Working with the Code #
-
-If you want to get your hands dirty with the code inside Google Mock,
-this is the section for you.
-
-## Checking Out the Source from Subversion ##
-
-Checking out the Google Mock source is most useful if you plan to
-tweak it yourself.  You check out the source for Google Mock using a
-[Subversion](http://subversion.tigris.org/) client as you would for any
-other project hosted on Google Code.  Please see the instruction on
-the [source code access page](../) for how to do it.
-
-## Compiling from Source ##
-
-Once you check out the code, you can find instructions on how to
-compile it in the [README](../README.md) file.
-
-## Testing ##
-
-A mocking framework is of no good if itself is not thoroughly tested.
-Tests should be written for any new code, and changes should be
-verified to not break existing tests before they are submitted for
-review. To perform the tests, follow the instructions in [README](http://code.google.com/p/googlemock/source/browse/trunk/README) and
-verify that there are no failures.
-
-# Contributing Code #
-
-We are excited that Google Mock is now open source, and hope to get
-great patches from the community. Before you fire up your favorite IDE
-and begin hammering away at that new feature, though, please take the
-time to read this section and understand the process. While it seems
-rigorous, we want to keep a high standard of quality in the code
-base.
-
-## Contributor License Agreements ##
-
-You must sign a Contributor License Agreement (CLA) before we can
-accept any code.  The CLA protects you and us.
-
-  * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html).
-  * If you work for a company that wants to allow you to contribute your work to Google Mock, then you'll need to sign a [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html).
-
-Follow either of the two links above to access the appropriate CLA and
-instructions for how to sign and return it.
-
-## Coding Style ##
-
-To keep the source consistent, readable, diffable and easy to merge,
-we use a fairly rigid coding style, as defined by the [google-styleguide](https://github.com/google/styleguide) project.  All patches will be expected
-to conform to the style outlined [here](https://github.com/google/styleguide/blob/gh-pages/cppguide.xml).
-
-## Submitting Patches ##
-
-Please do submit code. Here's what you need to do:
-
-  1. Normally you should make your change against the SVN trunk instead of a branch or a tag, as the latter two are for release control and should be treated mostly as read-only.
-  1. Decide which code you want to submit. A submission should be a set of changes that addresses one issue in the [Google Mock issue tracker](http://code.google.com/p/googlemock/issues/list). Please don't mix more than one logical change per submittal, because it makes the history hard to follow. If you want to make a change that doesn't have a corresponding issue in the issue tracker, please create one.
-  1. Also, coordinate with team members that are listed on the issue in question. This ensures that work isn't being duplicated and communicating your plan early also generally leads to better patches.
-  1. Ensure that your code adheres to the [Google Mock source code style](#Coding_Style.md).
-  1. Ensure that there are unit tests for your code.
-  1. Sign a Contributor License Agreement.
-  1. Create a patch file using `svn diff`.
-  1. We use [Rietveld](http://codereview.appspot.com/) to do web-based code reviews.  You can read about the tool [here](https://github.com/rietveld-codereview/rietveld/wiki).  When you are ready, upload your patch via Rietveld and notify `googlemock@googlegroups.com` to review it.  There are several ways to upload the patch.  We recommend using the [upload\_gmock.py](../scripts/upload_gmock.py) script, which you can find in the `scripts/` folder in the SVN trunk.
-
-## Google Mock Committers ##
-
-The current members of the Google Mock engineering team are the only
-committers at present. In the great tradition of eating one's own
-dogfood, we will be requiring each new Google Mock engineering team
-member to earn the right to become a committer by following the
-procedures in this document, writing consistently great code, and
-demonstrating repeatedly that he or she truly gets the zen of Google
-Mock.
-
-# Release Process #
-
-We follow the typical release process for Subversion-based projects:
-
-  1. A release branch named `release-X.Y` is created.
-  1. Bugs are fixed and features are added in trunk; those individual patches are merged into the release branch until it's stable.
-  1. An individual point release (the `Z` in `X.Y.Z`) is made by creating a tag from the branch.
-  1. Repeat steps 2 and 3 throughout one release cycle (as determined by features or time).
-  1. Go back to step 1 to create another release branch and so on.
-
-
----
-
-This page is based on the [Making GWT Better](http://code.google.com/webtoolkit/makinggwtbetter.html) guide from the [Google Web Toolkit](http://code.google.com/webtoolkit/) project.  Except as otherwise [noted](http://code.google.com/policies.html#restrictions), the content of this page is licensed under the [Creative Commons Attribution 2.5 License](http://creativecommons.org/licenses/by/2.5/).
index 444151ee9ed7ec43bd114bd1c9cd0d349dbc431e..16083e7047a8f57ba8af7778bcaee52ee70e7728 100644 (file)
@@ -1,5 +1,8 @@
-This page lists all documentation wiki pages for Google Mock **(the SVN trunk version)**
-- **if you use a released version of Google Mock, please read the documentation for that specific version instead.**
+This page lists all documentation markdown files for Google Mock **(the
+current git version)**
+-- **if you use a former version of Google Mock, please read the
+documentation for that specific version instead (e.g. by checking out
+the respective git branch/tag).**
 
   * [ForDummies](ForDummies.md) -- start here if you are new to Google Mock.
   * [CheatSheet](CheatSheet.md) -- a quick reference.
@@ -8,5 +11,5 @@ This page lists all documentation wiki pages for Google Mock **(the SVN trunk ve
 
 To contribute code to Google Mock, read:
 
-  * [DevGuide](DevGuide.md) -- read this _before_ writing your first patch.
-  * [Pump Manual](../googletest/docs/PumpManual.md) -- how we generate some of Google Mock's source files.
+  * [CONTRIBUTING](../CONTRIBUTING.md) -- read this _before_ writing your first patch.
+  * [Pump Manual](../../googletest/docs/PumpManual.md) -- how we generate some of Google Mock's source files.
index 0da4cbe27b8d141aa308334ba6532d53182e74b1..566a34e5b940be77180a9c711bc6f43a69381a15 100644 (file)
@@ -23,8 +23,8 @@ Using Google Mock involves three basic steps:
 # Why Google Mock? #
 While mock objects help you remove unnecessary dependencies in tests and make them fast and reliable, using mocks manually in C++ is _hard_:
 
-  * Someone has to implement the mocks. The job is usually tedious and error-prone. No wonder people go great distance to avoid it.
-  * The quality of those manually written mocks is a bit, uh, unpredictable. You may see some really polished ones, but you may also see some that were hacked up in a hurry and have all sorts of ad hoc restrictions.
+  * Someone has to implement the mocks. The job is usually tedious and error-prone. No wonder people go great distances to avoid it.
+  * The quality of those manually written mocks is a bit, uh, unpredictable. You may see some really polished ones, but you may also see some that were hacked up in a hurry and have all sorts of ad-hoc restrictions.
   * The knowledge you gained from using one mock doesn't transfer to the next.
 
 In contrast, Java and Python programmers have some fine mock frameworks, which automate the creation of mocks. As a result, mocking is a proven effective technique and widely adopted practice in those communities. Having the right tool absolutely makes the difference.
@@ -170,7 +170,7 @@ Admittedly, this test is contrived and doesn't do much. You can easily achieve t
 
 ## Using Google Mock with Any Testing Framework ##
 If you want to use something other than Google Test (e.g. [CppUnit](http://sourceforge.net/projects/cppunit/) or
-[CxxTest](http://cxxtest.tigris.org/)) as your testing framework, just change the `main()` function in the previous section to:
+[CxxTest](https://cxxtest.com/)) as your testing framework, just change the `main()` function in the previous section to:
 ```
 int main(int argc, char** argv) {
   // The following line causes Google Mock to throw an exception on failure,
@@ -187,7 +187,7 @@ sometimes causes the test program to crash.  You'll still be able to
 notice that the test has failed, but it's not a graceful failure.
 
 A better solution is to use Google Test's
-[event listener API](../../googletest/docs/AdvancedGuide.md#extending-google-test-by-handling-test-events)
+[event listener API](../../googletest/docs/advanced.md#extending-google-test-by-handling-test-events)
 to report a test failure to your testing framework properly.  You'll need to
 implement the `OnTestPartResult()` method of the event listener interface, but it
 should be straightforward.
@@ -217,7 +217,8 @@ The macro can be followed by some optional _clauses_ that provide more informati
 This syntax is designed to make an expectation read like English. For example, you can probably guess that
 
 ```
-using ::testing::Return;...
+using ::testing::Return;
+...
 EXPECT_CALL(turtle, GetX())
     .Times(5)
     .WillOnce(Return(100))
@@ -251,7 +252,8 @@ EXPECT_CALL(turtle, Forward(_));
 A list of built-in matchers can be found in the [CheatSheet](CheatSheet.md). For example, here's the `Ge` (greater than or equal) matcher:
 
 ```
-using ::testing::Ge;...
+using ::testing::Ge;
+...
 EXPECT_CALL(turtle, Forward(Ge(100)));
 ```
 
@@ -280,7 +282,8 @@ First, if the return type of a mock function is a built-in type or a pointer, th
 Second, if a mock function doesn't have a default action, or the default action doesn't suit you, you can specify the action to be taken each time the expectation matches using a series of `WillOnce()` clauses followed by an optional `WillRepeatedly()`. For example,
 
 ```
-using ::testing::Return;...
+using ::testing::Return;
+...
 EXPECT_CALL(turtle, GetX())
     .WillOnce(Return(100))
     .WillOnce(Return(200))
@@ -290,7 +293,8 @@ EXPECT_CALL(turtle, GetX())
 This says that `turtle.GetX()` will be called _exactly three times_ (Google Mock inferred this from how many `WillOnce()` clauses we've written, since we didn't explicitly write `Times()`), and will return 100, 200, and 300 respectively.
 
 ```
-using ::testing::Return;...
+using ::testing::Return;
+...
 EXPECT_CALL(turtle, GetY())
     .WillOnce(Return(100))
     .WillOnce(Return(200))
@@ -317,7 +321,8 @@ Instead of returning 100, 101, 102, ..., consecutively, this mock function will
 Time for another quiz! What do you think the following means?
 
 ```
-using ::testing::Return;...
+using ::testing::Return;
+...
 EXPECT_CALL(turtle, GetY())
 .Times(4)
 .WillOnce(Return(100));
@@ -331,7 +336,8 @@ So far we've only shown examples where you have a single expectation. More reali
 By default, when a mock method is invoked, Google Mock will search the expectations in the **reverse order** they are defined, and stop when an active expectation that matches the arguments is found (you can think of it as "newer rules override older ones."). If the matching expectation cannot take any more calls, you will get an upper-bound-violated failure. Here's an example:
 
 ```
-using ::testing::_;...
+using ::testing::_;
+...
 EXPECT_CALL(turtle, Forward(_));  // #1
 EXPECT_CALL(turtle, Forward(10))  // #2
     .Times(2);
@@ -347,7 +353,8 @@ By default, an expectation can match a call even though an earlier expectation h
 Sometimes, you may want all the expected calls to occur in a strict order. To say this in Google Mock is easy:
 
 ```
-using ::testing::InSequence;...
+using ::testing::InSequence;
+...
 TEST(FooTest, DrawsLineSegment) {
   ...
   {
@@ -365,7 +372,7 @@ By creating an object of type `InSequence`, all expectations in its scope are pu
 
 In this example, we test that `Foo()` calls the three expected functions in the order as written. If a call is made out-of-order, it will be an error.
 
-(What if you care about the relative order of some of the calls, but not all of them? Can you specify an arbitrary partial order? The answer is ... yes! If you are impatient, the details can be found in the [CookBook](CookBook#Expecting_Partially_Ordered_Calls.md).)
+(What if you care about the relative order of some of the calls, but not all of them? Can you specify an arbitrary partial order? The answer is ... yes! If you are impatient, the details can be found in the [CookBook](CookBook.md#expecting-partially-ordered-calls).)
 
 ## All Expectations Are Sticky (Unless Said Otherwise) ##
 Now let's do a quick quiz to see how well you can use this mock stuff already. How would you test that the turtle is asked to go to the origin _exactly twice_ (you want to ignore any other instructions it receives)?
@@ -373,7 +380,8 @@ Now let's do a quick quiz to see how well you can use this mock stuff already. H
 After you've come up with your answer, take a look at ours and compare notes (solve it yourself first - don't cheat!):
 
 ```
-using ::testing::_;...
+using ::testing::_;
+...
 EXPECT_CALL(turtle, GoTo(_, _))  // #1
     .Times(AnyNumber());
 EXPECT_CALL(turtle, GoTo(0, 0))  // #2
index 5eac83f4b9800bc2790f3742b624a661b1cb70fb..9008c63718f9ed14409dcc68e8a8f5b113eb89fe 100644 (file)
@@ -240,7 +240,7 @@ You cannot mock a variadic function (i.e. a function taking ellipsis
 The problem is that in general, there is _no way_ for a mock object to
 know how many arguments are passed to the variadic method, and what
 the arguments' types are.  Only the _author of the base class_ knows
-the protocol, and we cannot look into his head.
+the protocol, and we cannot look into their head.
 
 Therefore, to mock such a function, the _user_ must teach the mock
 object how to figure out the number of arguments and their types.  One
@@ -528,7 +528,7 @@ interface, which then can be easily mocked.  It's a bit of work
 initially, but usually pays for itself quickly.
 
 This Google Testing Blog
-[post](http://googletesting.blogspot.com/2008/06/defeat-static-cling.html)
+[post](https://testing.googleblog.com/2008/06/defeat-static-cling.html)
 says it excellently.  Check it out.
 
 ## My mock object needs to do complex stuff.  It's a lot of pain to specify the actions.  Google Mock sucks! ##
@@ -607,7 +607,6 @@ See this [recipe](CookBook.md#mocking_side_effects) for more details and an exam
 If you cannot find the answer to your question in this FAQ, there are
 some other resources you can use:
 
-  1. read other [documentation](Documentation.md),
   1. search the mailing list [archive](http://groups.google.com/group/googlemock/topics),
   1. ask it on [googlemock@googlegroups.com](mailto:googlemock@googlegroups.com) and someone will answer it (to prevent spam, we require you to join the [discussion group](http://groups.google.com/group/googlemock) before you can post.).
 
index 0985379c72991147b7ca4f9e904c0bfcaf4e98ff..10ed2bf30842c0dcb61554329e8efd59a479d019 100644 (file)
@@ -1616,7 +1616,11 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
       implicit_sequence->AddExpectation(Expectation(untyped_expectation));
     }
 
+#ifndef __clang_analyzer__
     return *expectation;
+#else
+    return nullptr;
+#endif
   }
 
   // The current spec (either default action spec or expectation spec)
index df0faefd468ac689cc0e58c7d84e50c5339485db..c35931f22c30de599b294f697a521b78fe75c1f5 100644 (file)
@@ -5,10 +5,6 @@
 # ctest.  You can select which tests to run using 'ctest -R regex'.
 # For more options, run 'ctest --help'.
 
-# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to
-# make it prominent in the GUI.
-option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF)
-
 # When other libraries are using a shared version of runtime libraries,
 # Google Test also has to use one.
 option(
@@ -44,13 +40,41 @@ endif()
 # as ${gtest_SOURCE_DIR} and to the root binary directory as
 # ${gtest_BINARY_DIR}.
 # Language "C" is required for find_package(Threads).
-project(gtest CXX C)
-cmake_minimum_required(VERSION 2.6.2)
+if (CMAKE_VERSION VERSION_LESS 3.0)
+  project(gtest CXX C)
+else()
+  cmake_policy(SET CMP0048 NEW)
+  project(gtest VERSION 1.8.1 LANGUAGES CXX C)
+endif()
+cmake_minimum_required(VERSION 2.6.4)
+
+if (POLICY CMP0063) # Visibility
+  cmake_policy(SET CMP0063 NEW)
+endif (POLICY CMP0063)
 
 if (COMMAND set_up_hermetic_build)
   set_up_hermetic_build()
 endif()
 
+# These commands only run if this is the main project
+if(CMAKE_PROJECT_NAME STREQUAL "gtest" OR CMAKE_PROJECT_NAME STREQUAL "googletest-distribution")
+
+  # BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to
+  # make it prominent in the GUI.
+  option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF)
+
+else()
+
+  mark_as_advanced(
+    gtest_force_shared_crt
+    gtest_build_tests
+    gtest_build_samples
+    gtest_disable_pthreads
+    gtest_hide_internal_symbols)
+
+endif()
+
+
 if (gtest_hide_internal_symbols)
   set(CMAKE_CXX_VISIBILITY_PRESET hidden)
   set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
@@ -61,10 +85,30 @@ include(cmake/internal_utils.cmake)
 
 config_compiler_and_linker()  # Defined in internal_utils.cmake.
 
+# Create the CMake package file descriptors.
+if (INSTALL_GTEST)
+  include(CMakePackageConfigHelpers)
+  set(cmake_package_name GTest)
+  set(targets_export_name ${cmake_package_name}Targets CACHE INTERNAL "")
+  set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated" CACHE INTERNAL "")
+  set(cmake_files_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${cmake_package_name}")
+  set(version_file "${generated_dir}/${cmake_package_name}ConfigVersion.cmake")
+  write_basic_package_version_file(${version_file} COMPATIBILITY AnyNewerVersion)
+  install(EXPORT ${targets_export_name}
+    NAMESPACE ${cmake_package_name}::
+    DESTINATION ${cmake_files_install_dir})
+  set(config_file "${generated_dir}/${cmake_package_name}Config.cmake")
+  configure_package_config_file("${gtest_SOURCE_DIR}/cmake/Config.cmake.in"
+    "${config_file}" INSTALL_DESTINATION ${cmake_files_install_dir})
+  install(FILES ${version_file} ${config_file}
+    DESTINATION ${cmake_files_install_dir})
+endif()
+
 # Where Google Test's .h files can be found.
-include_directories(
-  ${gtest_SOURCE_DIR}/include
-  ${gtest_SOURCE_DIR})
+set(gtest_build_include_dirs
+  "${gtest_SOURCE_DIR}/include"
+  "${gtest_SOURCE_DIR}")
+include_directories(${gtest_build_include_dirs})
 
 # Summary of tuple support for Microsoft Visual Studio:
 # Compiler    version(MS)  version(cmake)  Support
@@ -72,6 +116,8 @@ include_directories(
 # <= VS 2010  <= 10        <= 1600         Use Google Tests's own tuple.
 # VS 2012     11           1700            std::tr1::tuple + _VARIADIC_MAX=10
 # VS 2013     12           1800            std::tr1::tuple
+# VS 2015     14           1900            std::tuple
+# VS 2017     15           >= 1910         std::tuple
 if (MSVC AND MSVC_VERSION EQUAL 1700)
   add_definitions(/D _VARIADIC_MAX=10)
 endif()
@@ -86,23 +132,23 @@ endif()
 # aggressive about warnings.
 cxx_library(gtest "${cxx_strict}" src/gtest-all.cc)
 cxx_library(gtest_main "${cxx_strict}" src/gtest_main.cc)
-target_link_libraries(gtest_main gtest)
-
 # If the CMake version supports it, attach header directory information
 # to the targets for when we are part of a parent build (ie being pulled
 # in via add_subdirectory() rather than being a standalone build).
 if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")
-  target_include_directories(gtest      SYSTEM INTERFACE "${gtest_SOURCE_DIR}/include")
-  target_include_directories(gtest_main SYSTEM INTERFACE "${gtest_SOURCE_DIR}/include")
+  target_include_directories(gtest SYSTEM INTERFACE
+    "$<BUILD_INTERFACE:${gtest_build_include_dirs}>"
+    "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
+  target_include_directories(gtest_main SYSTEM INTERFACE
+    "$<BUILD_INTERFACE:${gtest_build_include_dirs}>"
+    "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
 endif()
+target_link_libraries(gtest_main PUBLIC gtest)
 
 ########################################################################
 #
 # Install rules
-#install(TARGETS gtest gtest_main
-#  DESTINATION lib)
-install(DIRECTORY ${gtest_SOURCE_DIR}/include/gtest
-  DESTINATION include)
+install_project(gtest gtest_main)
 
 ########################################################################
 #
@@ -144,28 +190,28 @@ if (gtest_build_tests)
   ############################################################
   # C++ tests built with standard compiler flags.
 
-  cxx_test(gtest-death-test_test gtest_main)
+  cxx_test(googletest-death-test-test gtest_main)
   cxx_test(gtest_environment_test gtest)
-  cxx_test(gtest-filepath_test gtest_main)
-  cxx_test(gtest-linked_ptr_test gtest_main)
-  cxx_test(gtest-listener_test gtest_main)
+  cxx_test(googletest-filepath-test gtest_main)
+  cxx_test(googletest-linked-ptr-test gtest_main)
+  cxx_test(googletest-listener-test gtest_main)
   cxx_test(gtest_main_unittest gtest_main)
-  cxx_test(gtest-message_test gtest_main)
+  cxx_test(googletest-message-test gtest_main)
   cxx_test(gtest_no_test_unittest gtest)
-  cxx_test(gtest-options_test gtest_main)
-  cxx_test(gtest-param-test_test gtest
-    test/gtest-param-test2_test.cc)
-  cxx_test(gtest-port_test gtest_main)
+  cxx_test(googletest-options-test gtest_main)
+  cxx_test(googletest-param-test-test gtest
+    test/googletest-param-test2-test.cc)
+  cxx_test(googletest-port-test gtest_main)
   cxx_test(gtest_pred_impl_unittest gtest_main)
   cxx_test(gtest_premature_exit_test gtest
     test/gtest_premature_exit_test.cc)
-  cxx_test(gtest-printers_test gtest_main)
+  cxx_test(googletest-printers-test gtest_main)
   cxx_test(gtest_prod_test gtest_main
     test/production.cc)
   cxx_test(gtest_repeat_test gtest)
   cxx_test(gtest_sole_header_test gtest_main)
   cxx_test(gtest_stress_test gtest)
-  cxx_test(gtest-test-part_test gtest_main)
+  cxx_test(googletest-test-part-test gtest_main)
   cxx_test(gtest_throw_on_failure_ex_test gtest)
   cxx_test(gtest-typed-test_test gtest_main
     test/gtest-typed-test2_test.cc)
@@ -187,10 +233,10 @@ if (gtest_build_tests)
 
   cxx_test_with_flags(gtest-death-test_ex_nocatch_test
     "${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=0"
-    gtest test/gtest-death-test_ex_test.cc)
+    gtest test/googletest-death-test_ex_test.cc)
   cxx_test_with_flags(gtest-death-test_ex_catch_test
     "${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=1"
-    gtest test/gtest-death-test_ex_test.cc)
+    gtest test/googletest-death-test_ex_test.cc)
 
   cxx_test_with_flags(gtest_no_rtti_unittest "${cxx_no_rtti}"
     gtest_main_no_rtti test/gtest_unittest.cc)
@@ -211,73 +257,75 @@ if (gtest_build_tests)
     cxx_library(gtest_main_use_own_tuple "${cxx_use_own_tuple}"
       src/gtest-all.cc src/gtest_main.cc)
 
-    cxx_test_with_flags(gtest-tuple_test "${cxx_use_own_tuple}"
-      gtest_main_use_own_tuple test/gtest-tuple_test.cc)
+    cxx_test_with_flags(googletest-tuple-test "${cxx_use_own_tuple}"
+      gtest_main_use_own_tuple test/googletest-tuple-test.cc)
 
     cxx_test_with_flags(gtest_use_own_tuple_test "${cxx_use_own_tuple}"
       gtest_main_use_own_tuple
-      test/gtest-param-test_test.cc test/gtest-param-test2_test.cc)
+      test/googletest-param-test-test.cc test/googletest-param-test2-test.cc)
   endif()
 
   ############################################################
   # Python tests.
 
-  cxx_executable(gtest_break_on_failure_unittest_ test gtest)
-  py_test(gtest_break_on_failure_unittest)
+  cxx_executable(googletest-break-on-failure-unittest_ test gtest)
+  py_test(googletest-break-on-failure-unittest)
 
   # Visual Studio .NET 2003 does not support STL with exceptions disabled.
   if (NOT MSVC OR MSVC_VERSION GREATER 1310)  # 1310 is Visual Studio .NET 2003
     cxx_executable_with_flags(
-      gtest_catch_exceptions_no_ex_test_
+      googletest-catch-exceptions-no-ex-test_
       "${cxx_no_exception}"
       gtest_main_no_exception
-      test/gtest_catch_exceptions_test_.cc)
+      test/googletest-catch-exceptions-test_.cc)
   endif()
 
   cxx_executable_with_flags(
-    gtest_catch_exceptions_ex_test_
+    googletest-catch-exceptions-ex-test_
     "${cxx_exception}"
     gtest_main
-    test/gtest_catch_exceptions_test_.cc)
-  py_test(gtest_catch_exceptions_test)
+    test/googletest-catch-exceptions-test_.cc)
+  py_test(googletest-catch-exceptions-test)
 
-  cxx_executable(gtest_color_test_ test gtest)
-  py_test(gtest_color_test)
+  cxx_executable(googletest-color-test_ test gtest)
+  py_test(googletest-color-test)
 
-  cxx_executable(gtest_env_var_test_ test gtest)
-  py_test(gtest_env_var_test)
+  cxx_executable(googletest-env-var-test_ test gtest)
+  py_test(googletest-env-var-test)
 
-  cxx_executable(gtest_filter_unittest_ test gtest)
-  py_test(gtest_filter_unittest)
+  cxx_executable(googletest-filter-unittest_ test gtest)
+  py_test(googletest-filter-unittest)
 
   cxx_executable(gtest_help_test_ test gtest_main)
   py_test(gtest_help_test)
 
-  cxx_executable(gtest_list_tests_unittest_ test gtest)
-  py_test(gtest_list_tests_unittest)
+  cxx_executable(googletest-list-tests-unittest_ test gtest)
+  py_test(googletest-list-tests-unittest)
 
-  cxx_executable(gtest_output_test_ test gtest)
-  py_test(gtest_output_test)
+  cxx_executable(googletest-output-test_ test gtest)
+  py_test(googletest-output-test --no_stacktrace_support)
 
-  cxx_executable(gtest_shuffle_test_ test gtest)
-  py_test(gtest_shuffle_test)
+  cxx_executable(googletest-shuffle-test_ test gtest)
+  py_test(googletest-shuffle-test)
 
   # MSVC 7.1 does not support STL with exceptions disabled.
   if (NOT MSVC OR MSVC_VERSION GREATER 1310)
-    cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception)
-    set_target_properties(gtest_throw_on_failure_test_
+    cxx_executable(googletest-throw-on-failure-test_ test gtest_no_exception)
+    set_target_properties(googletest-throw-on-failure-test_
       PROPERTIES
       COMPILE_FLAGS "${cxx_no_exception}")
-    py_test(gtest_throw_on_failure_test)
+    py_test(googletest-throw-on-failure-test)
   endif()
 
-  cxx_executable(gtest_uninitialized_test_ test gtest)
-  py_test(gtest_uninitialized_test)
+  cxx_executable(googletest-uninitialized-test_ test gtest)
+  py_test(googletest-uninitialized-test)
 
   cxx_executable(gtest_xml_outfile1_test_ test gtest_main)
   cxx_executable(gtest_xml_outfile2_test_ test gtest_main)
   py_test(gtest_xml_outfiles_test)
+  py_test(googletest-json-outfiles-test)
 
   cxx_executable(gtest_xml_output_unittest_ test gtest)
-  py_test(gtest_xml_output_unittest)
+  py_test(gtest_xml_output_unittest --no_stacktrace_support)
+  py_test(googletest-json-output-unittest --no_stacktrace_support)
 endif()
index edd4408054b32496aa0b437c02645232457590e6..e30fe8047127bee16d2c71f14b509f37571add41 100644 (file)
@@ -1,23 +1,21 @@
+### Generic Build Instructions
 
-### Generic Build Instructions ###
+#### Setup
 
-#### Setup ####
+To build Google Test and your tests that use it, you need to tell your build
+system where to find its headers and source files. The exact way to do it
+depends on which build system you use, and is usually straightforward.
 
-To build Google Test and your tests that use it, you need to tell your
-build system where to find its headers and source files.  The exact
-way to do it depends on which build system you use, and is usually
-straightforward.
+#### Build
 
-#### Build ####
-
-Suppose you put Google Test in directory `${GTEST_DIR}`.  To build it,
-create a library build target (or a project as called by Visual Studio
-and Xcode) to compile
+Suppose you put Google Test in directory `${GTEST_DIR}`. To build it, create a
+library build target (or a project as called by Visual Studio and Xcode) to
+compile
 
     ${GTEST_DIR}/src/gtest-all.cc
 
 with `${GTEST_DIR}/include` in the system header search path and `${GTEST_DIR}`
-in the normal header search path.  Assuming a Linux-like system and gcc,
+in the normal header search path. Assuming a Linux-like system and gcc,
 something like the following will do:
 
     g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \
@@ -26,136 +24,239 @@ something like the following will do:
 
 (We need `-pthread` as Google Test uses threads.)
 
-Next, you should compile your test source file with
-`${GTEST_DIR}/include` in the system header search path, and link it
-with gtest and any other necessary libraries:
+Next, you should compile your test source file with `${GTEST_DIR}/include` in
+the system header search path, and link it with gtest and any other necessary
+libraries:
 
     g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \
         -o your_test
 
-As an example, the make/ directory contains a Makefile that you can
-use to build Google Test on systems where GNU make is available
-(e.g. Linux, Mac OS X, and Cygwin).  It doesn't try to build Google
-Test's own tests.  Instead, it just builds the Google Test library and
-a sample test.  You can use it as a starting point for your own build
-script.
+As an example, the make/ directory contains a Makefile that you can use to build
+Google Test on systems where GNU make is available (e.g. Linux, Mac OS X, and
+Cygwin). It doesn't try to build Google Test's own tests. Instead, it just
+builds the Google Test library and a sample test. You can use it as a starting
+point for your own build script.
 
-If the default settings are correct for your environment, the
-following commands should succeed:
+If the default settings are correct for your environment, the following commands
+should succeed:
 
     cd ${GTEST_DIR}/make
     make
     ./sample1_unittest
 
-If you see errors, try to tweak the contents of `make/Makefile` to make
-them go away.  There are instructions in `make/Makefile` on how to do
-it.
+If you see errors, try to tweak the contents of `make/Makefile` to make them go
+away. There are instructions in `make/Makefile` on how to do it.
 
-### Using CMake ###
+### Using CMake
 
 Google Test comes with a CMake build script (
-[CMakeLists.txt](CMakeLists.txt)) that can be used on a wide range of platforms ("C" stands for
-cross-platform.). If you don't have CMake installed already, you can
-download it for free from <http://www.cmake.org/>.
+[CMakeLists.txt](https://github.com/google/googletest/blob/master/CMakeLists.txt))
+that can be used on a wide range of platforms ("C" stands for cross-platform.).
+If you don't have CMake installed already, you can download it for free from
+<http://www.cmake.org/>.
+
+CMake works by generating native makefiles or build projects that can be used in
+the compiler environment of your choice. You can either build Google Test as a
+standalone project or it can be incorporated into an existing CMake build for
+another project.
 
-CMake works by generating native makefiles or build projects that can
-be used in the compiler environment of your choice.  The typical
-workflow starts with:
+#### Standalone CMake Project
+
+When building Google Test as a standalone project, the typical workflow starts
+with:
 
     mkdir mybuild       # Create a directory to hold the build output.
     cd mybuild
     cmake ${GTEST_DIR}  # Generate native build scripts.
 
-If you want to build Google Test's samples, you should replace the
-last command with
+If you want to build Google Test's samples, you should replace the last command
+with
 
     cmake -Dgtest_build_samples=ON ${GTEST_DIR}
 
-If you are on a \*nix system, you should now see a Makefile in the
-current directory.  Just type 'make' to build gtest.
+If you are on a \*nix system, you should now see a Makefile in the current
+directory. Just type 'make' to build gtest.
 
-If you use Windows and have Visual Studio installed, a `gtest.sln` file
-and several `.vcproj` files will be created.  You can then build them
-using Visual Studio.
+If you use Windows and have Visual Studio installed, a `gtest.sln` file and
+several `.vcproj` files will be created. You can then build them using Visual
+Studio.
 
 On Mac OS X with Xcode installed, a `.xcodeproj` file will be generated.
 
-### Legacy Build Scripts ###
+#### Incorporating Into An Existing CMake Project
+
+If you want to use gtest in a project which already uses CMake, then a more
+robust and flexible approach is to build gtest as part of that project directly.
+This is done by making the GoogleTest source code available to the main build
+and adding it using CMake's `add_subdirectory()` command. This has the
+significant advantage that the same compiler and linker settings are used
+between gtest and the rest of your project, so issues associated with using
+incompatible libraries (eg debug/release), etc. are avoided. This is
+particularly useful on Windows. Making GoogleTest's source code available to the
+main build can be done a few different ways:
+
+*   Download the GoogleTest source code manually and place it at a known
+    location. This is the least flexible approach and can make it more difficult
+    to use with continuous integration systems, etc.
+*   Embed the GoogleTest source code as a direct copy in the main project's
+    source tree. This is often the simplest approach, but is also the hardest to
+    keep up to date. Some organizations may not permit this method.
+*   Add GoogleTest as a git submodule or equivalent. This may not always be
+    possible or appropriate. Git submodules, for example, have their own set of
+    advantages and drawbacks.
+*   Use CMake to download GoogleTest as part of the build's configure step. This
+    is just a little more complex, but doesn't have the limitations of the other
+    methods.
+
+The last of the above methods is implemented with a small piece of CMake code in
+a separate file (e.g. `CMakeLists.txt.in`) which is copied to the build area and
+then invoked as a sub-build _during the CMake stage_. That directory is then
+pulled into the main build with `add_subdirectory()`. For example:
+
+New file `CMakeLists.txt.in`:
+
+    cmake_minimum_required(VERSION 2.8.2)
+
+    project(googletest-download NONE)
+
+    include(ExternalProject)
+    ExternalProject_Add(googletest
+      GIT_REPOSITORY    https://github.com/google/googletest.git
+      GIT_TAG           master
+      SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"
+      BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"
+      CONFIGURE_COMMAND ""
+      BUILD_COMMAND     ""
+      INSTALL_COMMAND   ""
+      TEST_COMMAND      ""
+    )
+
+Existing build's `CMakeLists.txt`:
+
+    # Download and unpack googletest at configure time
+    configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
+    execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
+      RESULT_VARIABLE result
+      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
+    if(result)
+      message(FATAL_ERROR "CMake step for googletest failed: ${result}")
+    endif()
+    execute_process(COMMAND ${CMAKE_COMMAND} --build .
+      RESULT_VARIABLE result
+      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
+    if(result)
+      message(FATAL_ERROR "Build step for googletest failed: ${result}")
+    endif()
+
+    # Prevent overriding the parent project's compiler/linker
+    # settings on Windows
+    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+
+    # Add googletest directly to our build. This defines
+    # the gtest and gtest_main targets.
+    add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
+                     ${CMAKE_BINARY_DIR}/googletest-build
+                     EXCLUDE_FROM_ALL)
+
+    # The gtest/gtest_main targets carry header search path
+    # dependencies automatically when using CMake 2.8.11 or
+    # later. Otherwise we have to add them here ourselves.
+    if (CMAKE_VERSION VERSION_LESS 2.8.11)
+      include_directories("${gtest_SOURCE_DIR}/include")
+    endif()
+
+    # Now simply link against gtest or gtest_main as needed. Eg
+    add_executable(example example.cpp)
+    target_link_libraries(example gtest_main)
+    add_test(NAME example_test COMMAND example)
+
+Note that this approach requires CMake 2.8.2 or later due to its use of the
+`ExternalProject_Add()` command. The above technique is discussed in more detail
+in [this separate article](http://crascit.com/2015/07/25/cmake-gtest/) which
+also contains a link to a fully generalized implementation of the technique.
+
+##### Visual Studio Dynamic vs Static Runtimes
+
+By default, new Visual Studio projects link the C runtimes dynamically but
+Google Test links them statically. This will generate an error that looks
+something like the following: gtest.lib(gtest-all.obj) : error LNK2038: mismatch
+detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value
+'MDd_DynamicDebug' in main.obj
+
+Google Test already has a CMake option for this: `gtest_force_shared_crt`
+
+Enabling this option will make gtest link the runtimes dynamically too, and
+match the project in which it is included.
+
+### Legacy Build Scripts
 
 Before settling on CMake, we have been providing hand-maintained build
-projects/scripts for Visual Studio, Xcode, and Autotools.  While we
-continue to provide them for convenience, they are not actively
-maintained any more.  We highly recommend that you follow the
-instructions in the previous two sections to integrate Google Test
-with your existing build system.
+projects/scripts for Visual Studio, Xcode, and Autotools. While we continue to
+provide them for convenience, they are not actively maintained any more. We
+highly recommend that you follow the instructions in the above sections to
+integrate Google Test with your existing build system.
 
 If you still need to use the legacy build scripts, here's how:
 
-The msvc\ folder contains two solutions with Visual C++ projects.
-Open the `gtest.sln` or `gtest-md.sln` file using Visual Studio, and you
-are ready to build Google Test the same way you build any Visual
-Studio project.  Files that have names ending with -md use DLL
-versions of Microsoft runtime libraries (the /MD or the /MDd compiler
-option).  Files without that suffix use static versions of the runtime
-libraries (the /MT or the /MTd option).  Please note that one must use
-the same option to compile both gtest and the test code.  If you use
-Visual Studio 2005 or above, we recommend the -md version as /MD is
-the default for new projects in these versions of Visual Studio.
-
-On Mac OS X, open the `gtest.xcodeproj` in the `xcode/` folder using
-Xcode.  Build the "gtest" target.  The universal binary framework will
-end up in your selected build directory (selected in the Xcode
-"Preferences..." -> "Building" pane and defaults to xcode/build).
-Alternatively, at the command line, enter:
+The msvc\ folder contains two solutions with Visual C++ projects. Open the
+`gtest.sln` or `gtest-md.sln` file using Visual Studio, and you are ready to
+build Google Test the same way you build any Visual Studio project. Files that
+have names ending with -md use DLL versions of Microsoft runtime libraries (the
+/MD or the /MDd compiler option). Files without that suffix use static versions
+of the runtime libraries (the /MT or the /MTd option). Please note that one must
+use the same option to compile both gtest and the test code. If you use Visual
+Studio 2005 or above, we recommend the -md version as /MD is the default for new
+projects in these versions of Visual Studio.
+
+On Mac OS X, open the `gtest.xcodeproj` in the `xcode/` folder using Xcode.
+Build the "gtest" target. The universal binary framework will end up in your
+selected build directory (selected in the Xcode "Preferences..." -> "Building"
+pane and defaults to xcode/build). Alternatively, at the command line, enter:
 
     xcodebuild
 
-This will build the "Release" configuration of gtest.framework in your
-default build location.  See the "xcodebuild" man page for more
-information about building different configurations and building in
-different locations.
+This will build the "Release" configuration of gtest.framework in your default
+build location. See the "xcodebuild" man page for more information about
+building different configurations and building in different locations.
 
-If you wish to use the Google Test Xcode project with Xcode 4.x and
-above, you need to either:
+If you wish to use the Google Test Xcode project with Xcode 4.x and above, you
+need to either:
 
- * update the SDK configuration options in xcode/Config/General.xconfig.
-   Comment options `SDKROOT`, `MACOS_DEPLOYMENT_TARGET`, and `GCC_VERSION`. If
-   you choose this route you lose the ability to target earlier versions
-   of MacOS X.
- * Install an SDK for an earlier version. This doesn't appear to be
-   supported by Apple, but has been reported to work
-   (http://stackoverflow.com/questions/5378518).
+*   update the SDK configuration options in xcode/Config/General.xconfig.
+    Comment options `SDKROOT`, `MACOS_DEPLOYMENT_TARGET`, and `GCC_VERSION`. If
+    you choose this route you lose the ability to target earlier versions of
+    MacOS X.
+*   Install an SDK for an earlier version. This doesn't appear to be supported
+    by Apple, but has been reported to work
+    (http://stackoverflow.com/questions/5378518).
 
-### Tweaking Google Test ###
+### Tweaking Google Test
 
-Google Test can be used in diverse environments.  The default
-configuration may not work (or may not work well) out of the box in
-some environments.  However, you can easily tweak Google Test by
-defining control macros on the compiler command line.  Generally,
-these macros are named like `GTEST_XYZ` and you define them to either 1
-or 0 to enable or disable a certain feature.
+Google Test can be used in diverse environments. The default configuration may
+not work (or may not work well) out of the box in some environments. However,
+you can easily tweak Google Test by defining control macros on the compiler
+command line. Generally, these macros are named like `GTEST_XYZ` and you define
+them to either 1 or 0 to enable or disable a certain feature.
 
-We list the most frequently used macros below.  For a complete list,
-see file [include/gtest/internal/gtest-port.h](include/gtest/internal/gtest-port.h).
+We list the most frequently used macros below. For a complete list, see file
+[include/gtest/internal/gtest-port.h](https://github.com/google/googletest/blob/master/include/gtest/internal/gtest-port.h).
 
-### Choosing a TR1 Tuple Library ###
+### Choosing a TR1 Tuple Library
 
-Some Google Test features require the C++ Technical Report 1 (TR1)
-tuple library, which is not yet available with all compilers.  The
-good news is that Google Test implements a subset of TR1 tuple that's
-enough for its own need, and will automatically use this when the
-compiler doesn't provide TR1 tuple.
+Some Google Test features require the C++ Technical Report 1 (TR1) tuple
+library, which is not yet available with all compilers. The good news is that
+Google Test implements a subset of TR1 tuple that's enough for its own need, and
+will automatically use this when the compiler doesn't provide TR1 tuple.
 
-Usually you don't need to care about which tuple library Google Test
-uses.  However, if your project already uses TR1 tuple, you need to
-tell Google Test to use the same TR1 tuple library the rest of your
-project uses, or the two tuple implementations will clash.  To do
-that, add
+Usually you don't need to care about which tuple library Google Test uses.
+However, if your project already uses TR1 tuple, you need to tell Google Test to
+use the same TR1 tuple library the rest of your project uses, or the two tuple
+implementations will clash. To do that, add
 
     -DGTEST_USE_OWN_TR1_TUPLE=0
 
-to the compiler flags while compiling Google Test and your tests.  If
-you want to force Google Test to use its own tuple library, just add
+to the compiler flags while compiling Google Test and your tests. If you want to
+force Google Test to use its own tuple library, just add
 
     -DGTEST_USE_OWN_TR1_TUPLE=1
 
@@ -167,15 +268,15 @@ If you don't want Google Test to use tuple at all, add
 
 and all features using tuple will be disabled.
 
-### Multi-threaded Tests ###
+### Multi-threaded Tests
 
-Google Test is thread-safe where the pthread library is available.
-After `#include "gtest/gtest.h"`, you can check the `GTEST_IS_THREADSAFE`
-macro to see whether this is the case (yes if the macro is `#defined` to
-1, no if it's undefined.).
+Google Test is thread-safe where the pthread library is available. After
+`#include "gtest/gtest.h"`, you can check the `GTEST_IS_THREADSAFE` macro to see
+whether this is the case (yes if the macro is `#defined` to 1, no if it's
+undefined.).
 
-If Google Test doesn't correctly detect whether pthread is available
-in your environment, you can force it with
+If Google Test doesn't correctly detect whether pthread is available in your
+environment, you can force it with
 
     -DGTEST_HAS_PTHREAD=1
 
@@ -183,26 +284,24 @@ or
 
     -DGTEST_HAS_PTHREAD=0
 
-When Google Test uses pthread, you may need to add flags to your
-compiler and/or linker to select the pthread library, or you'll get
-link errors.  If you use the CMake script or the deprecated Autotools
-script, this is taken care of for you.  If you use your own build
-script, you'll need to read your compiler and linker's manual to
-figure out what flags to add.
+When Google Test uses pthread, you may need to add flags to your compiler and/or
+linker to select the pthread library, or you'll get link errors. If you use the
+CMake script or the deprecated Autotools script, this is taken care of for you.
+If you use your own build script, you'll need to read your compiler and linker's
+manual to figure out what flags to add.
 
-### As a Shared Library (DLL) ###
+### As a Shared Library (DLL)
 
-Google Test is compact, so most users can build and link it as a
-static library for the simplicity.  You can choose to use Google Test
-as a shared library (known as a DLL on Windows) if you prefer.
+Google Test is compact, so most users can build and link it as a static library
+for the simplicity. You can choose to use Google Test as a shared library (known
+as a DLL on Windows) if you prefer.
 
 To compile *gtest* as a shared library, add
 
     -DGTEST_CREATE_SHARED_LIBRARY=1
 
-to the compiler flags.  You'll also need to tell the linker to produce
-a shared library instead - consult your linker's manual for how to do
-it.
+to the compiler flags. You'll also need to tell the linker to produce a shared
+library instead - consult your linker's manual for how to do it.
 
 To compile your *tests* that use the gtest shared library, add
 
@@ -210,31 +309,28 @@ To compile your *tests* that use the gtest shared library, add
 
 to the compiler flags.
 
-Note: while the above steps aren't technically necessary today when
-using some compilers (e.g. GCC), they may become necessary in the
-future, if we decide to improve the speed of loading the library (see
-<http://gcc.gnu.org/wiki/Visibility> for details).  Therefore you are
-recommended to always add the above flags when using Google Test as a
-shared library.  Otherwise a future release of Google Test may break
-your build script.
+Note: while the above steps aren't technically necessary today when using some
+compilers (e.g. GCC), they may become necessary in the future, if we decide to
+improve the speed of loading the library (see
+<http://gcc.gnu.org/wiki/Visibility> for details). Therefore you are recommended
+to always add the above flags when using Google Test as a shared library.
+Otherwise a future release of Google Test may break your build script.
 
-### Avoiding Macro Name Clashes ###
+### Avoiding Macro Name Clashes
 
-In C++, macros don't obey namespaces.  Therefore two libraries that
-both define a macro of the same name will clash if you `#include` both
-definitions.  In case a Google Test macro clashes with another
-library, you can force Google Test to rename its macro to avoid the
-conflict.
+In C++, macros don't obey namespaces. Therefore two libraries that both define a
+macro of the same name will clash if you `#include` both definitions. In case a
+Google Test macro clashes with another library, you can force Google Test to
+rename its macro to avoid the conflict.
 
-Specifically, if both Google Test and some other code define macro
-FOO, you can add
+Specifically, if both Google Test and some other code define macro FOO, you can
+add
 
     -DGTEST_DONT_DEFINE_FOO=1
 
-to the compiler flags to tell Google Test to change the macro's name
-from `FOO` to `GTEST_FOO`.  Currently `FOO` can be `FAIL`, `SUCCEED`,
-or `TEST`.  For example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll
-need to write
+to the compiler flags to tell Google Test to change the macro's name from `FOO`
+to `GTEST_FOO`. Currently `FOO` can be `FAIL`, `SUCCEED`, or `TEST`. For
+example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll need to write
 
     GTEST_TEST(SomeTest, DoesThis) { ... }
 
@@ -243,38 +339,3 @@ instead of
     TEST(SomeTest, DoesThis) { ... }
 
 in order to define a test.
-
-## Developing Google Test ##
-
-This section discusses how to make your own changes to Google Test.
-
-### Testing Google Test Itself ###
-
-To make sure your changes work as intended and don't break existing
-functionality, you'll want to compile and run Google Test's own tests.
-For that you can use CMake:
-
-    mkdir mybuild
-    cd mybuild
-    cmake -Dgtest_build_tests=ON ${GTEST_DIR}
-
-Make sure you have Python installed, as some of Google Test's tests
-are written in Python.  If the cmake command complains about not being
-able to find Python (`Could NOT find PythonInterp (missing:
-PYTHON_EXECUTABLE)`), try telling it explicitly where your Python
-executable can be found:
-
-    cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR}
-
-Next, you can build Google Test and all of its own tests.  On \*nix,
-this is usually done by 'make'.  To run the tests, do
-
-    make test
-
-All tests should pass.
-
-Normally you don't need to worry about regenerating the source files,
-unless you need to modify them.  In that case, you should modify the
-corresponding .pump files instead and run the pump.py Python script to
-regenerate them.  You can find pump.py in the [scripts/](scripts/) directory.
-Read the [Pump manual](docs/PumpManual.md) for how to use it.
diff --git a/src/external/googletest/googletest/cmake/Config.cmake.in b/src/external/googletest/googletest/cmake/Config.cmake.in
new file mode 100644 (file)
index 0000000..12be449
--- /dev/null
@@ -0,0 +1,9 @@
+@PACKAGE_INIT@
+include(CMakeFindDependencyMacro)
+if (@GTEST_HAS_PTHREAD@)
+  set(THREADS_PREFER_PTHREAD_FLAG @THREADS_PREFER_PTHREAD_FLAG@)
+  find_dependency(Threads)
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
+check_required_components("@project_name@")
diff --git a/src/external/googletest/googletest/cmake/gtest.pc.in b/src/external/googletest/googletest/cmake/gtest.pc.in
new file mode 100644 (file)
index 0000000..e7967ad
--- /dev/null
@@ -0,0 +1,9 @@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+
+Name: gtest
+Description: GoogleTest (without main() function)
+Version: @PROJECT_VERSION@
+URL: https://github.com/google/googletest
+Libs: -L${libdir} -lgtest @CMAKE_THREAD_LIBS_INIT@
+Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@ @CMAKE_THREAD_LIBS_INIT@
diff --git a/src/external/googletest/googletest/cmake/gtest_main.pc.in b/src/external/googletest/googletest/cmake/gtest_main.pc.in
new file mode 100644 (file)
index 0000000..fe25d9c
--- /dev/null
@@ -0,0 +1,10 @@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+
+Name: gtest_main
+Description: GoogleTest (with main() function)
+Version: @PROJECT_VERSION@
+URL: https://github.com/google/googletest
+Requires: gtest
+Libs: -L${libdir} -lgtest_main @CMAKE_THREAD_LIBS_INIT@
+Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@ @CMAKE_THREAD_LIBS_INIT@
index 777b91ed4b8caa226ea55cfa61fe7ec433ffb452..8c1f9ba99cf50574f669dd7deba324dce40595f3 100644 (file)
@@ -20,7 +20,7 @@ macro(fix_default_compiler_settings_)
   if (MSVC)
     # For MSVC, CMake sets certain flags to defaults we want to override.
     # This replacement code is taken from sample in the CMake Wiki at
-    # http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace.
+    # https://gitlab.kitware.com/cmake/community/wikis/FAQ#dynamic-replace.
     foreach (flag_var
              CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
              CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
@@ -38,6 +38,11 @@ macro(fix_default_compiler_settings_)
       # We prefer more strict warning checking for building Google Test.
       # Replaces /W3 with /W4 in defaults.
       string(REPLACE "/W3" "/W4" ${flag_var} "${${flag_var}}")
+
+      # Prevent D9025 warning for targets that have exception handling
+      # turned off (/EHs-c- flag). Where required, exceptions are explicitly
+      # re-enabled using the cxx_exception_flags variable.
+      string(REPLACE "/EHsc" "" ${flag_var} "${${flag_var}}")
     endforeach()
   endif()
 endmacro()
@@ -46,9 +51,16 @@ endmacro()
 # Google Mock.  You can tweak these definitions to suit your need.  A
 # variable's value is empty before it's explicitly assigned to.
 macro(config_compiler_and_linker)
-  if (NOT gtest_disable_pthreads)
+  # Note: pthreads on MinGW is not supported, even if available
+  # instead, we use windows threading primitives
+  unset(GTEST_HAS_PTHREAD)
+  if (NOT gtest_disable_pthreads AND NOT MINGW)
     # Defines CMAKE_USE_PTHREADS_INIT and CMAKE_THREAD_LIBS_INIT.
+    set(THREADS_PREFER_PTHREAD_FLAG ON)
     find_package(Threads)
+    if (CMAKE_USE_PTHREADS_INIT)
+      set(GTEST_HAS_PTHREAD ON)
+    endif()
   endif()
 
   fix_default_compiler_settings_()
@@ -80,18 +92,17 @@ macro(config_compiler_and_linker)
       # http://stackoverflow.com/questions/3232669 explains the issue.
       set(cxx_base_flags "${cxx_base_flags} -wd4702")
     endif()
-    if (NOT (MSVC_VERSION GREATER 1900))  # 1900 is Visual Studio 2015
-      # BigObj required for tests.
-      set(cxx_base_flags "${cxx_base_flags} -bigobj")
-    endif()
 
     set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32")
     set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN")
     set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1")
-    set(cxx_no_exception_flags "-D_HAS_EXCEPTIONS=0")
+    set(cxx_no_exception_flags "-EHs-c- -D_HAS_EXCEPTIONS=0")
     set(cxx_no_rtti_flags "-GR-")
   elseif (CMAKE_COMPILER_IS_GNUCXX)
-    set(cxx_base_flags "-Wall -Wshadow")
+    set(cxx_base_flags "-Wall -Wshadow -Werror")
+    if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0)
+      set(cxx_base_flags "${cxx_base_flags} -Wno-error=dangling-else")
+    endif()
     set(cxx_exception_flags "-fexceptions")
     set(cxx_no_exception_flags "-fno-exceptions")
     # Until version 4.3.2, GCC doesn't define a macro to indicate
@@ -123,14 +134,16 @@ macro(config_compiler_and_linker)
     set(cxx_no_rtti_flags "")
   endif()
 
-  if (CMAKE_USE_PTHREADS_INIT)  # The pthreads library is available and allowed.
-    set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=1")
+  # The pthreads library is available and allowed?
+  if (DEFINED GTEST_HAS_PTHREAD)
+    set(GTEST_HAS_PTHREAD_MACRO "-DGTEST_HAS_PTHREAD=1")
   else()
-    set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=0")
+    set(GTEST_HAS_PTHREAD_MACRO "-DGTEST_HAS_PTHREAD=0")
   endif()
+  set(cxx_base_flags "${cxx_base_flags} ${GTEST_HAS_PTHREAD_MACRO}")
 
   # For building gtest's own tests and samples.
-  set(cxx_exception "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_exception_flags}")
+  set(cxx_exception "${cxx_base_flags} ${cxx_exception_flags}")
   set(cxx_no_exception
     "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_no_exception_flags}")
   set(cxx_default "${cxx_exception}")
@@ -150,13 +163,26 @@ function(cxx_library_with_type name type cxx_flags)
   set_target_properties(${name}
     PROPERTIES
     COMPILE_FLAGS "${cxx_flags}")
+  # Generate debug library name with a postfix.
+  set_target_properties(${name}
+    PROPERTIES
+    DEBUG_POSTFIX "d")
   if (BUILD_SHARED_LIBS OR type STREQUAL "SHARED")
     set_target_properties(${name}
       PROPERTIES
       COMPILE_DEFINITIONS "GTEST_CREATE_SHARED_LIBRARY=1")
+    if (NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")
+      target_compile_definitions(${name} INTERFACE
+        $<INSTALL_INTERFACE:GTEST_LINKED_AS_SHARED_LIBRARY=1>)
+    endif()
   endif()
-  if (CMAKE_USE_PTHREADS_INIT)
-    target_link_libraries(${name} ${CMAKE_THREAD_LIBS_INIT})
+  if (DEFINED GTEST_HAS_PTHREAD)
+    if ("${CMAKE_VERSION}" VERSION_LESS "3.1.0")
+      set(threads_spec ${CMAKE_THREAD_LIBS_INIT})
+    else()
+      set(threads_spec Threads::Threads)
+    endif()
+    target_link_libraries(${name} PUBLIC ${threads_spec})
   endif()
 endfunction()
 
@@ -178,6 +204,10 @@ endfunction()
 # is built from the given source files with the given compiler flags.
 function(cxx_executable_with_flags name cxx_flags libs)
   add_executable(${name} ${ARGN})
+  if (MSVC AND (NOT (MSVC_VERSION LESS 1700)))  # 1700 is Visual Studio 2012.
+    # BigObj required for tests.
+    set(cxx_flags "${cxx_flags} -bigobj")
+  endif()
   if (cxx_flags)
     set_target_properties(${name}
       PROPERTIES
@@ -214,7 +244,7 @@ find_package(PythonInterp)
 # from the given source files with the given compiler flags.
 function(cxx_test_with_flags name cxx_flags libs)
   cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN})
-  add_test(${name} ${name})
+  add_test(NAME ${name} COMMAND ${name})
 endfunction()
 
 # cxx_test(name libs srcs...)
@@ -232,23 +262,57 @@ endfunction()
 # creates a Python test with the given name whose main module is in
 # test/name.py.  It does nothing if Python is not installed.
 function(py_test name)
-  # We are not supporting Python tests on Linux yet as they consider
-  # all Linux environments to be google3 and try to use google3 features.
   if (PYTHONINTERP_FOUND)
-    # ${CMAKE_BINARY_DIR} is known at configuration time, so we can
-    # directly bind it from cmake. ${CTEST_CONFIGURATION_TYPE} is known
-    # only at ctest runtime (by calling ctest -c <Configuration>), so
-    # we have to escape $ to delay variable substitution here.
     if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1)
-      add_test(
-        NAME ${name}
-        COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
-            --build_dir=${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>)
+      if (CMAKE_CONFIGURATION_TYPES)
+       # Multi-configuration build generators as for Visual Studio save
+       # output in a subdirectory of CMAKE_CURRENT_BINARY_DIR (Debug,
+       # Release etc.), so we have to provide it here.
+        add_test(
+          NAME ${name}
+          COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
+              --build_dir=${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG> ${ARGN})
+      else (CMAKE_CONFIGURATION_TYPES)
+       # Single-configuration build generators like Makefile generators
+       # don't have subdirs below CMAKE_CURRENT_BINARY_DIR.
+        add_test(
+          NAME ${name}
+          COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
+              --build_dir=${CMAKE_CURRENT_BINARY_DIR} ${ARGN})
+      endif (CMAKE_CONFIGURATION_TYPES)
     else (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1)
+      # ${CMAKE_CURRENT_BINARY_DIR} is known at configuration time, so we can
+      # directly bind it from cmake. ${CTEST_CONFIGURATION_TYPE} is known
+      # only at ctest runtime (by calling ctest -c <Configuration>), so
+      # we have to escape $ to delay variable substitution here.
       add_test(
         ${name}
         ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
-          --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE})
+          --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE} ${ARGN})
     endif (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1)
+  endif(PYTHONINTERP_FOUND)
+endfunction()
+
+# install_project(targets...)
+#
+# Installs the specified targets and configures the associated pkgconfig files.
+function(install_project)
+  if(INSTALL_GTEST)
+    install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/"
+      DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
+    # Install the project targets.
+    install(TARGETS ${ARGN}
+      EXPORT ${targets_export_name}
+      RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+      ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+      LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+    # Configure and install pkgconfig files.
+    foreach(t ${ARGN})
+      set(configured_pc "${generated_dir}/${t}.pc")
+      configure_file("${PROJECT_SOURCE_DIR}/cmake/${t}.pc.in"
+        "${configured_pc}" @ONLY)
+      install(FILES "${configured_pc}"
+        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+    endforeach()
   endif()
 endfunction()
diff --git a/src/external/googletest/googletest/docs/AdvancedGuide.md b/src/external/googletest/googletest/docs/AdvancedGuide.md
deleted file mode 100644 (file)
index 93a6520..0000000
+++ /dev/null
@@ -1,2182 +0,0 @@
-
-
-Now that you have read [Primer](Primer.md) and learned how to write tests
-using Google Test, it's time to learn some new tricks. This document
-will show you more assertions as well as how to construct complex
-failure messages, propagate fatal failures, reuse and speed up your
-test fixtures, and use various flags with your tests.
-
-# More Assertions #
-
-This section covers some less frequently used, but still significant,
-assertions.
-
-## Explicit Success and Failure ##
-
-These three assertions do not actually test a value or expression. Instead,
-they generate a success or failure directly. Like the macros that actually
-perform a test, you may stream a custom failure message into the them.
-
-| `SUCCEED();` |
-|:-------------|
-
-Generates a success. This does NOT make the overall test succeed. A test is
-considered successful only if none of its assertions fail during its execution.
-
-Note: `SUCCEED()` is purely documentary and currently doesn't generate any
-user-visible output. However, we may add `SUCCEED()` messages to Google Test's
-output in the future.
-
-| `FAIL();`  | `ADD_FAILURE();` | `ADD_FAILURE_AT("`_file\_path_`", `_line\_number_`);` |
-|:-----------|:-----------------|:------------------------------------------------------|
-
-`FAIL()` generates a fatal failure, while `ADD_FAILURE()` and `ADD_FAILURE_AT()` generate a nonfatal
-failure. These are useful when control flow, rather than a Boolean expression,
-deteremines the test's success or failure. For example, you might want to write
-something like:
-
-```
-switch(expression) {
-  case 1: ... some checks ...
-  case 2: ... some other checks
-  ...
-  default: FAIL() << "We shouldn't get here.";
-}
-```
-
-Note: you can only use `FAIL()` in functions that return `void`. See the [Assertion Placement section](#assertion-placement) for more information.
-
-_Availability_: Linux, Windows, Mac.
-
-## Exception Assertions ##
-
-These are for verifying that a piece of code throws (or does not
-throw) an exception of the given type:
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-| `ASSERT_THROW(`_statement_, _exception\_type_`);`  | `EXPECT_THROW(`_statement_, _exception\_type_`);`  | _statement_ throws an exception of the given type  |
-| `ASSERT_ANY_THROW(`_statement_`);`                | `EXPECT_ANY_THROW(`_statement_`);`                | _statement_ throws an exception of any type        |
-| `ASSERT_NO_THROW(`_statement_`);`                 | `EXPECT_NO_THROW(`_statement_`);`                 | _statement_ doesn't throw any exception            |
-
-Examples:
-
-```
-ASSERT_THROW(Foo(5), bar_exception);
-
-EXPECT_NO_THROW({
-  int n = 5;
-  Bar(&n);
-});
-```
-
-_Availability_: Linux, Windows, Mac; since version 1.1.0.
-
-## Predicate Assertions for Better Error Messages ##
-
-Even though Google Test has a rich set of assertions, they can never be
-complete, as it's impossible (nor a good idea) to anticipate all the scenarios
-a user might run into. Therefore, sometimes a user has to use `EXPECT_TRUE()`
-to check a complex expression, for lack of a better macro. This has the problem
-of not showing you the values of the parts of the expression, making it hard to
-understand what went wrong. As a workaround, some users choose to construct the
-failure message by themselves, streaming it into `EXPECT_TRUE()`. However, this
-is awkward especially when the expression has side-effects or is expensive to
-evaluate.
-
-Google Test gives you three different options to solve this problem:
-
-### Using an Existing Boolean Function ###
-
-If you already have a function or a functor that returns `bool` (or a type
-that can be implicitly converted to `bool`), you can use it in a _predicate
-assertion_ to get the function arguments printed for free:
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-| `ASSERT_PRED1(`_pred1, val1_`);`       | `EXPECT_PRED1(`_pred1, val1_`);` | _pred1(val1)_ returns true |
-| `ASSERT_PRED2(`_pred2, val1, val2_`);` | `EXPECT_PRED2(`_pred2, val1, val2_`);` |  _pred2(val1, val2)_ returns true |
-|  ...                | ...                    | ...          |
-
-In the above, _predn_ is an _n_-ary predicate function or functor, where
-_val1_, _val2_, ..., and _valn_ are its arguments. The assertion succeeds
-if the predicate returns `true` when applied to the given arguments, and fails
-otherwise. When the assertion fails, it prints the value of each argument. In
-either case, the arguments are evaluated exactly once.
-
-Here's an example. Given
-
-```
-// Returns true iff m and n have no common divisors except 1.
-bool MutuallyPrime(int m, int n) { ... }
-const int a = 3;
-const int b = 4;
-const int c = 10;
-```
-
-the assertion `EXPECT_PRED2(MutuallyPrime, a, b);` will succeed, while the
-assertion `EXPECT_PRED2(MutuallyPrime, b, c);` will fail with the message
-
-<pre>
-!MutuallyPrime(b, c) is false, where<br>
-b is 4<br>
-c is 10<br>
-</pre>
-
-**Notes:**
-
-  1. If you see a compiler error "no matching function to call" when using `ASSERT_PRED*` or `EXPECT_PRED*`, please see [this FAQ](FAQ.md#the-compiler-complains-no-matching-function-to-call-when-i-use-assert_predn-how-do-i-fix-it) for how to resolve it.
-  1. Currently we only provide predicate assertions of arity <= 5. If you need a higher-arity assertion, let us know.
-
-_Availability_: Linux, Windows, Mac
-
-### Using a Function That Returns an AssertionResult ###
-
-While `EXPECT_PRED*()` and friends are handy for a quick job, the
-syntax is not satisfactory: you have to use different macros for
-different arities, and it feels more like Lisp than C++.  The
-`::testing::AssertionResult` class solves this problem.
-
-An `AssertionResult` object represents the result of an assertion
-(whether it's a success or a failure, and an associated message).  You
-can create an `AssertionResult` using one of these factory
-functions:
-
-```
-namespace testing {
-
-// Returns an AssertionResult object to indicate that an assertion has
-// succeeded.
-AssertionResult AssertionSuccess();
-
-// Returns an AssertionResult object to indicate that an assertion has
-// failed.
-AssertionResult AssertionFailure();
-
-}
-```
-
-You can then use the `<<` operator to stream messages to the
-`AssertionResult` object.
-
-To provide more readable messages in Boolean assertions
-(e.g. `EXPECT_TRUE()`), write a predicate function that returns
-`AssertionResult` instead of `bool`. For example, if you define
-`IsEven()` as:
-
-```
-::testing::AssertionResult IsEven(int n) {
-  if ((n % 2) == 0)
-    return ::testing::AssertionSuccess();
-  else
-    return ::testing::AssertionFailure() << n << " is odd";
-}
-```
-
-instead of:
-
-```
-bool IsEven(int n) {
-  return (n % 2) == 0;
-}
-```
-
-the failed assertion `EXPECT_TRUE(IsEven(Fib(4)))` will print:
-
-<pre>
-Value of: IsEven(Fib(4))<br>
-Actual: false (*3 is odd*)<br>
-Expected: true<br>
-</pre>
-
-instead of a more opaque
-
-<pre>
-Value of: IsEven(Fib(4))<br>
-Actual: false<br>
-Expected: true<br>
-</pre>
-
-If you want informative messages in `EXPECT_FALSE` and `ASSERT_FALSE`
-as well, and are fine with making the predicate slower in the success
-case, you can supply a success message:
-
-```
-::testing::AssertionResult IsEven(int n) {
-  if ((n % 2) == 0)
-    return ::testing::AssertionSuccess() << n << " is even";
-  else
-    return ::testing::AssertionFailure() << n << " is odd";
-}
-```
-
-Then the statement `EXPECT_FALSE(IsEven(Fib(6)))` will print
-
-<pre>
-Value of: IsEven(Fib(6))<br>
-Actual: true (8 is even)<br>
-Expected: false<br>
-</pre>
-
-_Availability_: Linux, Windows, Mac; since version 1.4.1.
-
-### Using a Predicate-Formatter ###
-
-If you find the default message generated by `(ASSERT|EXPECT)_PRED*` and
-`(ASSERT|EXPECT)_(TRUE|FALSE)` unsatisfactory, or some arguments to your
-predicate do not support streaming to `ostream`, you can instead use the
-following _predicate-formatter assertions_ to _fully_ customize how the
-message is formatted:
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-| `ASSERT_PRED_FORMAT1(`_pred\_format1, val1_`);`        | `EXPECT_PRED_FORMAT1(`_pred\_format1, val1_`);` | _pred\_format1(val1)_ is successful |
-| `ASSERT_PRED_FORMAT2(`_pred\_format2, val1, val2_`);` | `EXPECT_PRED_FORMAT2(`_pred\_format2, val1, val2_`);` | _pred\_format2(val1, val2)_ is successful |
-| `...`               | `...`                  | `...`        |
-
-The difference between this and the previous two groups of macros is that instead of
-a predicate, `(ASSERT|EXPECT)_PRED_FORMAT*` take a _predicate-formatter_
-(_pred\_formatn_), which is a function or functor with the signature:
-
-`::testing::AssertionResult PredicateFormattern(const char* `_expr1_`, const char* `_expr2_`, ... const char* `_exprn_`, T1 `_val1_`, T2 `_val2_`, ... Tn `_valn_`);`
-
-where _val1_, _val2_, ..., and _valn_ are the values of the predicate
-arguments, and _expr1_, _expr2_, ..., and _exprn_ are the corresponding
-expressions as they appear in the source code. The types `T1`, `T2`, ..., and
-`Tn` can be either value types or reference types. For example, if an
-argument has type `Foo`, you can declare it as either `Foo` or `const Foo&`,
-whichever is appropriate.
-
-A predicate-formatter returns a `::testing::AssertionResult` object to indicate
-whether the assertion has succeeded or not. The only way to create such an
-object is to call one of these factory functions:
-
-As an example, let's improve the failure message in the previous example, which uses `EXPECT_PRED2()`:
-
-```
-// Returns the smallest prime common divisor of m and n,
-// or 1 when m and n are mutually prime.
-int SmallestPrimeCommonDivisor(int m, int n) { ... }
-
-// A predicate-formatter for asserting that two integers are mutually prime.
-::testing::AssertionResult AssertMutuallyPrime(const char* m_expr,
-                                               const char* n_expr,
-                                               int m,
-                                               int n) {
-  if (MutuallyPrime(m, n))
-    return ::testing::AssertionSuccess();
-
-  return ::testing::AssertionFailure()
-      << m_expr << " and " << n_expr << " (" << m << " and " << n
-      << ") are not mutually prime, " << "as they have a common divisor "
-      << SmallestPrimeCommonDivisor(m, n);
-}
-```
-
-With this predicate-formatter, we can use
-
-```
-EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c);
-```
-
-to generate the message
-
-<pre>
-b and c (4 and 10) are not mutually prime, as they have a common divisor 2.<br>
-</pre>
-
-As you may have realized, many of the assertions we introduced earlier are
-special cases of `(EXPECT|ASSERT)_PRED_FORMAT*`. In fact, most of them are
-indeed defined using `(EXPECT|ASSERT)_PRED_FORMAT*`.
-
-_Availability_: Linux, Windows, Mac.
-
-
-## Floating-Point Comparison ##
-
-Comparing floating-point numbers is tricky. Due to round-off errors, it is
-very unlikely that two floating-points will match exactly. Therefore,
-`ASSERT_EQ` 's naive comparison usually doesn't work. And since floating-points
-can have a wide value range, no single fixed error bound works. It's better to
-compare by a fixed relative error bound, except for values close to 0 due to
-the loss of precision there.
-
-In general, for floating-point comparison to make sense, the user needs to
-carefully choose the error bound. If they don't want or care to, comparing in
-terms of Units in the Last Place (ULPs) is a good default, and Google Test
-provides assertions to do this. Full details about ULPs are quite long; if you
-want to learn more, see
-[this article on float comparison](http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm).
-
-### Floating-Point Macros ###
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-| `ASSERT_FLOAT_EQ(`_val1, val2_`);`  | `EXPECT_FLOAT_EQ(`_val1, val2_`);` | the two `float` values are almost equal |
-| `ASSERT_DOUBLE_EQ(`_val1, val2_`);` | `EXPECT_DOUBLE_EQ(`_val1, val2_`);` | the two `double` values are almost equal |
-
-By "almost equal", we mean the two values are within 4 ULP's from each
-other.
-
-The following assertions allow you to choose the acceptable error bound:
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-| `ASSERT_NEAR(`_val1, val2, abs\_error_`);` | `EXPECT_NEAR`_(val1, val2, abs\_error_`);` | the difference between _val1_ and _val2_ doesn't exceed the given absolute error |
-
-_Availability_: Linux, Windows, Mac.
-
-### Floating-Point Predicate-Format Functions ###
-
-Some floating-point operations are useful, but not that often used. In order
-to avoid an explosion of new macros, we provide them as predicate-format
-functions that can be used in predicate assertion macros (e.g.
-`EXPECT_PRED_FORMAT2`, etc).
-
-```
-EXPECT_PRED_FORMAT2(::testing::FloatLE, val1, val2);
-EXPECT_PRED_FORMAT2(::testing::DoubleLE, val1, val2);
-```
-
-Verifies that _val1_ is less than, or almost equal to, _val2_. You can
-replace `EXPECT_PRED_FORMAT2` in the above table with `ASSERT_PRED_FORMAT2`.
-
-_Availability_: Linux, Windows, Mac.
-
-## Windows HRESULT assertions ##
-
-These assertions test for `HRESULT` success or failure.
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-| `ASSERT_HRESULT_SUCCEEDED(`_expression_`);` | `EXPECT_HRESULT_SUCCEEDED(`_expression_`);` | _expression_ is a success `HRESULT` |
-| `ASSERT_HRESULT_FAILED(`_expression_`);`    | `EXPECT_HRESULT_FAILED(`_expression_`);`    | _expression_ is a failure `HRESULT` |
-
-The generated output contains the human-readable error message
-associated with the `HRESULT` code returned by _expression_.
-
-You might use them like this:
-
-```
-CComPtr shell;
-ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));
-CComVariant empty;
-ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));
-```
-
-_Availability_: Windows.
-
-## Type Assertions ##
-
-You can call the function
-```
-::testing::StaticAssertTypeEq<T1, T2>();
-```
-to assert that types `T1` and `T2` are the same.  The function does
-nothing if the assertion is satisfied.  If the types are different,
-the function call will fail to compile, and the compiler error message
-will likely (depending on the compiler) show you the actual values of
-`T1` and `T2`.  This is mainly useful inside template code.
-
-_Caveat:_ When used inside a member function of a class template or a
-function template, `StaticAssertTypeEq<T1, T2>()` is effective _only if_
-the function is instantiated.  For example, given:
-```
-template <typename T> class Foo {
- public:
-  void Bar() { ::testing::StaticAssertTypeEq<int, T>(); }
-};
-```
-the code:
-```
-void Test1() { Foo<bool> foo; }
-```
-will _not_ generate a compiler error, as `Foo<bool>::Bar()` is never
-actually instantiated.  Instead, you need:
-```
-void Test2() { Foo<bool> foo; foo.Bar(); }
-```
-to cause a compiler error.
-
-_Availability:_ Linux, Windows, Mac; since version 1.3.0.
-
-## Assertion Placement ##
-
-You can use assertions in any C++ function. In particular, it doesn't
-have to be a method of the test fixture class. The one constraint is
-that assertions that generate a fatal failure (`FAIL*` and `ASSERT_*`)
-can only be used in void-returning functions. This is a consequence of
-Google Test not using exceptions. By placing it in a non-void function
-you'll get a confusing compile error like
-`"error: void value not ignored as it ought to be"`.
-
-If you need to use assertions in a function that returns non-void, one option
-is to make the function return the value in an out parameter instead. For
-example, you can rewrite `T2 Foo(T1 x)` to `void Foo(T1 x, T2* result)`. You
-need to make sure that `*result` contains some sensible value even when the
-function returns prematurely. As the function now returns `void`, you can use
-any assertion inside of it.
-
-If changing the function's type is not an option, you should just use
-assertions that generate non-fatal failures, such as `ADD_FAILURE*` and
-`EXPECT_*`.
-
-_Note_: Constructors and destructors are not considered void-returning
-functions, according to the C++ language specification, and so you may not use
-fatal assertions in them. You'll get a compilation error if you try. A simple
-workaround is to transfer the entire body of the constructor or destructor to a
-private void-returning method. However, you should be aware that a fatal
-assertion failure in a constructor does not terminate the current test, as your
-intuition might suggest; it merely returns from the constructor early, possibly
-leaving your object in a partially-constructed state. Likewise, a fatal
-assertion failure in a destructor may leave your object in a
-partially-destructed state. Use assertions carefully in these situations!
-
-# Teaching Google Test How to Print Your Values #
-
-When a test assertion such as `EXPECT_EQ` fails, Google Test prints the
-argument values to help you debug.  It does this using a
-user-extensible value printer.
-
-This printer knows how to print built-in C++ types, native arrays, STL
-containers, and any type that supports the `<<` operator.  For other
-types, it prints the raw bytes in the value and hopes that you the
-user can figure it out.
-
-As mentioned earlier, the printer is _extensible_.  That means
-you can teach it to do a better job at printing your particular type
-than to dump the bytes.  To do that, define `<<` for your type:
-
-```
-#include <iostream>
-
-namespace foo {
-
-class Bar { ... };  // We want Google Test to be able to print instances of this.
-
-// It's important that the << operator is defined in the SAME
-// namespace that defines Bar.  C++'s look-up rules rely on that.
-::std::ostream& operator<<(::std::ostream& os, const Bar& bar) {
-  return os << bar.DebugString();  // whatever needed to print bar to os
-}
-
-}  // namespace foo
-```
-
-Sometimes, this might not be an option: your team may consider it bad
-style to have a `<<` operator for `Bar`, or `Bar` may already have a
-`<<` operator that doesn't do what you want (and you cannot change
-it).  If so, you can instead define a `PrintTo()` function like this:
-
-```
-#include <iostream>
-
-namespace foo {
-
-class Bar { ... };
-
-// It's important that PrintTo() is defined in the SAME
-// namespace that defines Bar.  C++'s look-up rules rely on that.
-void PrintTo(const Bar& bar, ::std::ostream* os) {
-  *os << bar.DebugString();  // whatever needed to print bar to os
-}
-
-}  // namespace foo
-```
-
-If you have defined both `<<` and `PrintTo()`, the latter will be used
-when Google Test is concerned.  This allows you to customize how the value
-appears in Google Test's output without affecting code that relies on the
-behavior of its `<<` operator.
-
-If you want to print a value `x` using Google Test's value printer
-yourself, just call `::testing::PrintToString(`_x_`)`, which
-returns an `std::string`:
-
-```
-vector<pair<Bar, int> > bar_ints = GetBarIntVector();
-
-EXPECT_TRUE(IsCorrectBarIntVector(bar_ints))
-    << "bar_ints = " << ::testing::PrintToString(bar_ints);
-```
-
-# Death Tests #
-
-In many applications, there are assertions that can cause application failure
-if a condition is not met. These sanity checks, which ensure that the program
-is in a known good state, are there to fail at the earliest possible time after
-some program state is corrupted. If the assertion checks the wrong condition,
-then the program may proceed in an erroneous state, which could lead to memory
-corruption, security holes, or worse. Hence it is vitally important to test
-that such assertion statements work as expected.
-
-Since these precondition checks cause the processes to die, we call such tests
-_death tests_. More generally, any test that checks that a program terminates
-(except by throwing an exception) in an expected fashion is also a death test.
-
-Note that if a piece of code throws an exception, we don't consider it "death"
-for the purpose of death tests, as the caller of the code could catch the exception
-and avoid the crash. If you want to verify exceptions thrown by your code,
-see [Exception Assertions](#exception-assertions).
-
-If you want to test `EXPECT_*()/ASSERT_*()` failures in your test code, see [Catching Failures](#catching-failures).
-
-## How to Write a Death Test ##
-
-Google Test has the following macros to support death tests:
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-| `ASSERT_DEATH(`_statement, regex_`);` | `EXPECT_DEATH(`_statement, regex_`);` | _statement_ crashes with the given error |
-| `ASSERT_DEATH_IF_SUPPORTED(`_statement, regex_`);` | `EXPECT_DEATH_IF_SUPPORTED(`_statement, regex_`);` | if death tests are supported, verifies that _statement_ crashes with the given error; otherwise verifies nothing |
-| `ASSERT_EXIT(`_statement, predicate, regex_`);` | `EXPECT_EXIT(`_statement, predicate, regex_`);` |_statement_ exits with the given error and its exit code matches _predicate_ |
-
-where _statement_ is a statement that is expected to cause the process to
-die, _predicate_ is a function or function object that evaluates an integer
-exit status, and _regex_ is a regular expression that the stderr output of
-_statement_ is expected to match. Note that _statement_ can be _any valid
-statement_ (including _compound statement_) and doesn't have to be an
-expression.
-
-As usual, the `ASSERT` variants abort the current test function, while the
-`EXPECT` variants do not.
-
-**Note:** We use the word "crash" here to mean that the process
-terminates with a _non-zero_ exit status code.  There are two
-possibilities: either the process has called `exit()` or `_exit()`
-with a non-zero value, or it may be killed by a signal.
-
-This means that if _statement_ terminates the process with a 0 exit
-code, it is _not_ considered a crash by `EXPECT_DEATH`.  Use
-`EXPECT_EXIT` instead if this is the case, or if you want to restrict
-the exit code more precisely.
-
-A predicate here must accept an `int` and return a `bool`. The death test
-succeeds only if the predicate returns `true`. Google Test defines a few
-predicates that handle the most common cases:
-
-```
-::testing::ExitedWithCode(exit_code)
-```
-
-This expression is `true` if the program exited normally with the given exit
-code.
-
-```
-::testing::KilledBySignal(signal_number)  // Not available on Windows.
-```
-
-This expression is `true` if the program was killed by the given signal.
-
-The `*_DEATH` macros are convenient wrappers for `*_EXIT` that use a predicate
-that verifies the process' exit code is non-zero.
-
-Note that a death test only cares about three things:
-
-  1. does _statement_ abort or exit the process?
-  1. (in the case of `ASSERT_EXIT` and `EXPECT_EXIT`) does the exit status satisfy _predicate_?  Or (in the case of `ASSERT_DEATH` and `EXPECT_DEATH`) is the exit status non-zero?  And
-  1. does the stderr output match _regex_?
-
-In particular, if _statement_ generates an `ASSERT_*` or `EXPECT_*` failure, it will **not** cause the death test to fail, as Google Test assertions don't abort the process.
-
-To write a death test, simply use one of the above macros inside your test
-function. For example,
-
-```
-TEST(MyDeathTest, Foo) {
-  // This death test uses a compound statement.
-  ASSERT_DEATH({ int n = 5; Foo(&n); }, "Error on line .* of Foo()");
-}
-TEST(MyDeathTest, NormalExit) {
-  EXPECT_EXIT(NormalExit(), ::testing::ExitedWithCode(0), "Success");
-}
-TEST(MyDeathTest, KillMyself) {
-  EXPECT_EXIT(KillMyself(), ::testing::KilledBySignal(SIGKILL), "Sending myself unblockable signal");
-}
-```
-
-verifies that:
-
-  * calling `Foo(5)` causes the process to die with the given error message,
-  * calling `NormalExit()` causes the process to print `"Success"` to stderr and exit with exit code 0, and
-  * calling `KillMyself()` kills the process with signal `SIGKILL`.
-
-The test function body may contain other assertions and statements as well, if
-necessary.
-
-_Important:_ We strongly recommend you to follow the convention of naming your
-test case (not test) `*DeathTest` when it contains a death test, as
-demonstrated in the above example. The `Death Tests And Threads` section below
-explains why.
-
-If a test fixture class is shared by normal tests and death tests, you
-can use typedef to introduce an alias for the fixture class and avoid
-duplicating its code:
-```
-class FooTest : public ::testing::Test { ... };
-
-typedef FooTest FooDeathTest;
-
-TEST_F(FooTest, DoesThis) {
-  // normal test
-}
-
-TEST_F(FooDeathTest, DoesThat) {
-  // death test
-}
-```
-
-_Availability:_ Linux, Windows (requires MSVC 8.0 or above), Cygwin, and Mac (the latter three are supported since v1.3.0).  `(ASSERT|EXPECT)_DEATH_IF_SUPPORTED` are new in v1.4.0.
-
-## Regular Expression Syntax ##
-
-On POSIX systems (e.g. Linux, Cygwin, and Mac), Google Test uses the
-[POSIX extended regular expression](http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04)
-syntax in death tests. To learn about this syntax, you may want to read this [Wikipedia entry](http://en.wikipedia.org/wiki/Regular_expression#POSIX_Extended_Regular_Expressions).
-
-On Windows, Google Test uses its own simple regular expression
-implementation. It lacks many features you can find in POSIX extended
-regular expressions.  For example, we don't support union (`"x|y"`),
-grouping (`"(xy)"`), brackets (`"[xy]"`), and repetition count
-(`"x{5,7}"`), among others. Below is what we do support (Letter `A` denotes a
-literal character, period (`.`), or a single `\\` escape sequence; `x`
-and `y` denote regular expressions.):
-
-| `c` | matches any literal character `c` |
-|:----|:----------------------------------|
-| `\\d` | matches any decimal digit         |
-| `\\D` | matches any character that's not a decimal digit |
-| `\\f` | matches `\f`                      |
-| `\\n` | matches `\n`                      |
-| `\\r` | matches `\r`                      |
-| `\\s` | matches any ASCII whitespace, including `\n` |
-| `\\S` | matches any character that's not a whitespace |
-| `\\t` | matches `\t`                      |
-| `\\v` | matches `\v`                      |
-| `\\w` | matches any letter, `_`, or decimal digit |
-| `\\W` | matches any character that `\\w` doesn't match |
-| `\\c` | matches any literal character `c`, which must be a punctuation |
-| `\\.` | matches the `.` character         |
-| `.` | matches any single character except `\n` |
-| `A?` | matches 0 or 1 occurrences of `A` |
-| `A*` | matches 0 or many occurrences of `A` |
-| `A+` | matches 1 or many occurrences of `A` |
-| `^` | matches the beginning of a string (not that of each line) |
-| `$` | matches the end of a string (not that of each line) |
-| `xy` | matches `x` followed by `y`       |
-
-To help you determine which capability is available on your system,
-Google Test defines macro `GTEST_USES_POSIX_RE=1` when it uses POSIX
-extended regular expressions, or `GTEST_USES_SIMPLE_RE=1` when it uses
-the simple version.  If you want your death tests to work in both
-cases, you can either `#if` on these macros or use the more limited
-syntax only.
-
-## How It Works ##
-
-Under the hood, `ASSERT_EXIT()` spawns a new process and executes the
-death test statement in that process. The details of of how precisely
-that happens depend on the platform and the variable
-`::testing::GTEST_FLAG(death_test_style)` (which is initialized from the
-command-line flag `--gtest_death_test_style`).
-
-  * On POSIX systems, `fork()` (or `clone()` on Linux) is used to spawn the child, after which:
-    * If the variable's value is `"fast"`, the death test statement is immediately executed.
-    * If the variable's value is `"threadsafe"`, the child process re-executes the unit test binary just as it was originally invoked, but with some extra flags to cause just the single death test under consideration to be run.
-  * On Windows, the child is spawned using the `CreateProcess()` API, and re-executes the binary to cause just the single death test under consideration to be run - much like the `threadsafe` mode on POSIX.
-
-Other values for the variable are illegal and will cause the death test to
-fail. Currently, the flag's default value is `"fast"`. However, we reserve the
-right to change it in the future. Therefore, your tests should not depend on
-this.
-
-In either case, the parent process waits for the child process to complete, and checks that
-
-  1. the child's exit status satisfies the predicate, and
-  1. the child's stderr matches the regular expression.
-
-If the death test statement runs to completion without dying, the child
-process will nonetheless terminate, and the assertion fails.
-
-## Death Tests And Threads ##
-
-The reason for the two death test styles has to do with thread safety. Due to
-well-known problems with forking in the presence of threads, death tests should
-be run in a single-threaded context. Sometimes, however, it isn't feasible to
-arrange that kind of environment. For example, statically-initialized modules
-may start threads before main is ever reached. Once threads have been created,
-it may be difficult or impossible to clean them up.
-
-Google Test has three features intended to raise awareness of threading issues.
-
-  1. A warning is emitted if multiple threads are running when a death test is encountered.
-  1. Test cases with a name ending in "DeathTest" are run before all other tests.
-  1. It uses `clone()` instead of `fork()` to spawn the child process on Linux (`clone()` is not available on Cygwin and Mac), as `fork()` is more likely to cause the child to hang when the parent process has multiple threads.
-
-It's perfectly fine to create threads inside a death test statement; they are
-executed in a separate process and cannot affect the parent.
-
-## Death Test Styles ##
-
-The "threadsafe" death test style was introduced in order to help mitigate the
-risks of testing in a possibly multithreaded environment. It trades increased
-test execution time (potentially dramatically so) for improved thread safety.
-We suggest using the faster, default "fast" style unless your test has specific
-problems with it.
-
-You can choose a particular style of death tests by setting the flag
-programmatically:
-
-```
-::testing::FLAGS_gtest_death_test_style = "threadsafe";
-```
-
-You can do this in `main()` to set the style for all death tests in the
-binary, or in individual tests. Recall that flags are saved before running each
-test and restored afterwards, so you need not do that yourself. For example:
-
-```
-TEST(MyDeathTest, TestOne) {
-  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
-  // This test is run in the "threadsafe" style:
-  ASSERT_DEATH(ThisShouldDie(), "");
-}
-
-TEST(MyDeathTest, TestTwo) {
-  // This test is run in the "fast" style:
-  ASSERT_DEATH(ThisShouldDie(), "");
-}
-
-int main(int argc, char** argv) {
-  ::testing::InitGoogleTest(&argc, argv);
-  ::testing::FLAGS_gtest_death_test_style = "fast";
-  return RUN_ALL_TESTS();
-}
-```
-
-## Caveats ##
-
-The _statement_ argument of `ASSERT_EXIT()` can be any valid C++ statement.
-If it leaves the current function via a `return` statement or by throwing an exception,
-the death test is considered to have failed.  Some Google Test macros may return
-from the current function (e.g. `ASSERT_TRUE()`), so be sure to avoid them in _statement_.
-
-Since _statement_ runs in the child process, any in-memory side effect (e.g.
-modifying a variable, releasing memory, etc) it causes will _not_ be observable
-in the parent process. In particular, if you release memory in a death test,
-your program will fail the heap check as the parent process will never see the
-memory reclaimed. To solve this problem, you can
-
-  1. try not to free memory in a death test;
-  1. free the memory again in the parent process; or
-  1. do not use the heap checker in your program.
-
-Due to an implementation detail, you cannot place multiple death test
-assertions on the same line; otherwise, compilation will fail with an unobvious
-error message.
-
-Despite the improved thread safety afforded by the "threadsafe" style of death
-test, thread problems such as deadlock are still possible in the presence of
-handlers registered with `pthread_atfork(3)`.
-
-# Using Assertions in Sub-routines #
-
-## Adding Traces to Assertions ##
-
-If a test sub-routine is called from several places, when an assertion
-inside it fails, it can be hard to tell which invocation of the
-sub-routine the failure is from.  You can alleviate this problem using
-extra logging or custom failure messages, but that usually clutters up
-your tests. A better solution is to use the `SCOPED_TRACE` macro:
-
-| `SCOPED_TRACE(`_message_`);` |
-|:-----------------------------|
-
-where _message_ can be anything streamable to `std::ostream`. This
-macro will cause the current file name, line number, and the given
-message to be added in every failure message. The effect will be
-undone when the control leaves the current lexical scope.
-
-For example,
-
-```
-10: void Sub1(int n) {
-11:   EXPECT_EQ(1, Bar(n));
-12:   EXPECT_EQ(2, Bar(n + 1));
-13: }
-14:
-15: TEST(FooTest, Bar) {
-16:   {
-17:     SCOPED_TRACE("A");  // This trace point will be included in
-18:                         // every failure in this scope.
-19:     Sub1(1);
-20:   }
-21:   // Now it won't.
-22:   Sub1(9);
-23: }
-```
-
-could result in messages like these:
-
-```
-path/to/foo_test.cc:11: Failure
-Value of: Bar(n)
-Expected: 1
-  Actual: 2
-   Trace:
-path/to/foo_test.cc:17: A
-
-path/to/foo_test.cc:12: Failure
-Value of: Bar(n + 1)
-Expected: 2
-  Actual: 3
-```
-
-Without the trace, it would've been difficult to know which invocation
-of `Sub1()` the two failures come from respectively. (You could add an
-extra message to each assertion in `Sub1()` to indicate the value of
-`n`, but that's tedious.)
-
-Some tips on using `SCOPED_TRACE`:
-
-  1. With a suitable message, it's often enough to use `SCOPED_TRACE` at the beginning of a sub-routine, instead of at each call site.
-  1. When calling sub-routines inside a loop, make the loop iterator part of the message in `SCOPED_TRACE` such that you can know which iteration the failure is from.
-  1. Sometimes the line number of the trace point is enough for identifying the particular invocation of a sub-routine. In this case, you don't have to choose a unique message for `SCOPED_TRACE`. You can simply use `""`.
-  1. You can use `SCOPED_TRACE` in an inner scope when there is one in the outer scope. In this case, all active trace points will be included in the failure messages, in reverse order they are encountered.
-  1. The trace dump is clickable in Emacs' compilation buffer - hit return on a line number and you'll be taken to that line in the source file!
-
-_Availability:_ Linux, Windows, Mac.
-
-## Propagating Fatal Failures ##
-
-A common pitfall when using `ASSERT_*` and `FAIL*` is not understanding that
-when they fail they only abort the _current function_, not the entire test. For
-example, the following test will segfault:
-```
-void Subroutine() {
-  // Generates a fatal failure and aborts the current function.
-  ASSERT_EQ(1, 2);
-  // The following won't be executed.
-  ...
-}
-
-TEST(FooTest, Bar) {
-  Subroutine();
-  // The intended behavior is for the fatal failure
-  // in Subroutine() to abort the entire test.
-  // The actual behavior: the function goes on after Subroutine() returns.
-  int* p = NULL;
-  *p = 3; // Segfault!
-}
-```
-
-Since we don't use exceptions, it is technically impossible to
-implement the intended behavior here.  To alleviate this, Google Test
-provides two solutions.  You could use either the
-`(ASSERT|EXPECT)_NO_FATAL_FAILURE` assertions or the
-`HasFatalFailure()` function.  They are described in the following two
-subsections.
-
-### Asserting on Subroutines ###
-
-As shown above, if your test calls a subroutine that has an `ASSERT_*`
-failure in it, the test will continue after the subroutine
-returns. This may not be what you want.
-
-Often people want fatal failures to propagate like exceptions.  For
-that Google Test offers the following macros:
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-| `ASSERT_NO_FATAL_FAILURE(`_statement_`);` | `EXPECT_NO_FATAL_FAILURE(`_statement_`);` | _statement_ doesn't generate any new fatal failures in the current thread. |
-
-Only failures in the thread that executes the assertion are checked to
-determine the result of this type of assertions.  If _statement_
-creates new threads, failures in these threads are ignored.
-
-Examples:
-
-```
-ASSERT_NO_FATAL_FAILURE(Foo());
-
-int i;
-EXPECT_NO_FATAL_FAILURE({
-  i = Bar();
-});
-```
-
-_Availability:_ Linux, Windows, Mac. Assertions from multiple threads
-are currently not supported.
-
-### Checking for Failures in the Current Test ###
-
-`HasFatalFailure()` in the `::testing::Test` class returns `true` if an
-assertion in the current test has suffered a fatal failure. This
-allows functions to catch fatal failures in a sub-routine and return
-early.
-
-```
-class Test {
- public:
-  ...
-  static bool HasFatalFailure();
-};
-```
-
-The typical usage, which basically simulates the behavior of a thrown
-exception, is:
-
-```
-TEST(FooTest, Bar) {
-  Subroutine();
-  // Aborts if Subroutine() had a fatal failure.
-  if (HasFatalFailure())
-    return;
-  // The following won't be executed.
-  ...
-}
-```
-
-If `HasFatalFailure()` is used outside of `TEST()` , `TEST_F()` , or a test
-fixture, you must add the `::testing::Test::` prefix, as in:
-
-```
-if (::testing::Test::HasFatalFailure())
-  return;
-```
-
-Similarly, `HasNonfatalFailure()` returns `true` if the current test
-has at least one non-fatal failure, and `HasFailure()` returns `true`
-if the current test has at least one failure of either kind.
-
-_Availability:_ Linux, Windows, Mac.  `HasNonfatalFailure()` and
-`HasFailure()` are available since version 1.4.0.
-
-# Logging Additional Information #
-
-In your test code, you can call `RecordProperty("key", value)` to log
-additional information, where `value` can be either a string or an `int`. The _last_ value recorded for a key will be emitted to the XML output
-if you specify one. For example, the test
-
-```
-TEST_F(WidgetUsageTest, MinAndMaxWidgets) {
-  RecordProperty("MaximumWidgets", ComputeMaxUsage());
-  RecordProperty("MinimumWidgets", ComputeMinUsage());
-}
-```
-
-will output XML like this:
-
-```
-...
-  <testcase name="MinAndMaxWidgets" status="run" time="6" classname="WidgetUsageTest"
-            MaximumWidgets="12"
-            MinimumWidgets="9" />
-...
-```
-
-_Note_:
-  * `RecordProperty()` is a static member of the `Test` class. Therefore it needs to be prefixed with `::testing::Test::` if used outside of the `TEST` body and the test fixture class.
-  * `key` must be a valid XML attribute name, and cannot conflict with the ones already used by Google Test (`name`, `status`, `time`, `classname`, `type_param`, and `value_param`).
-  * Calling `RecordProperty()` outside of the lifespan of a test is allowed. If it's called outside of a test but between a test case's `SetUpTestCase()` and `TearDownTestCase()` methods, it will be attributed to the XML element for the test case. If it's called outside of all test cases (e.g. in a test environment), it will be attributed to the top-level XML element.
-
-_Availability_: Linux, Windows, Mac.
-
-# Sharing Resources Between Tests in the Same Test Case #
-
-
-
-Google Test creates a new test fixture object for each test in order to make
-tests independent and easier to debug. However, sometimes tests use resources
-that are expensive to set up, making the one-copy-per-test model prohibitively
-expensive.
-
-If the tests don't change the resource, there's no harm in them sharing a
-single resource copy. So, in addition to per-test set-up/tear-down, Google Test
-also supports per-test-case set-up/tear-down. To use it:
-
-  1. In your test fixture class (say `FooTest` ), define as `static` some member variables to hold the shared resources.
-  1. In the same test fixture class, define a `static void SetUpTestCase()` function (remember not to spell it as **`SetupTestCase`** with a small `u`!) to set up the shared resources and a `static void TearDownTestCase()` function to tear them down.
-
-That's it! Google Test automatically calls `SetUpTestCase()` before running the
-_first test_ in the `FooTest` test case (i.e. before creating the first
-`FooTest` object), and calls `TearDownTestCase()` after running the _last test_
-in it (i.e. after deleting the last `FooTest` object). In between, the tests
-can use the shared resources.
-
-Remember that the test order is undefined, so your code can't depend on a test
-preceding or following another. Also, the tests must either not modify the
-state of any shared resource, or, if they do modify the state, they must
-restore the state to its original value before passing control to the next
-test.
-
-Here's an example of per-test-case set-up and tear-down:
-```
-class FooTest : public ::testing::Test {
- protected:
-  // Per-test-case set-up.
-  // Called before the first test in this test case.
-  // Can be omitted if not needed.
-  static void SetUpTestCase() {
-    shared_resource_ = new ...;
-  }
-
-  // Per-test-case tear-down.
-  // Called after the last test in this test case.
-  // Can be omitted if not needed.
-  static void TearDownTestCase() {
-    delete shared_resource_;
-    shared_resource_ = NULL;
-  }
-
-  // You can define per-test set-up and tear-down logic as usual.
-  virtual void SetUp() { ... }
-  virtual void TearDown() { ... }
-
-  // Some expensive resource shared by all tests.
-  static T* shared_resource_;
-};
-
-T* FooTest::shared_resource_ = NULL;
-
-TEST_F(FooTest, Test1) {
-  ... you can refer to shared_resource here ...
-}
-TEST_F(FooTest, Test2) {
-  ... you can refer to shared_resource here ...
-}
-```
-
-_Availability:_ Linux, Windows, Mac.
-
-# Global Set-Up and Tear-Down #
-
-Just as you can do set-up and tear-down at the test level and the test case
-level, you can also do it at the test program level. Here's how.
-
-First, you subclass the `::testing::Environment` class to define a test
-environment, which knows how to set-up and tear-down:
-
-```
-class Environment {
- public:
-  virtual ~Environment() {}
-  // Override this to define how to set up the environment.
-  virtual void SetUp() {}
-  // Override this to define how to tear down the environment.
-  virtual void TearDown() {}
-};
-```
-
-Then, you register an instance of your environment class with Google Test by
-calling the `::testing::AddGlobalTestEnvironment()` function:
-
-```
-Environment* AddGlobalTestEnvironment(Environment* env);
-```
-
-Now, when `RUN_ALL_TESTS()` is called, it first calls the `SetUp()` method of
-the environment object, then runs the tests if there was no fatal failures, and
-finally calls `TearDown()` of the environment object.
-
-It's OK to register multiple environment objects. In this case, their `SetUp()`
-will be called in the order they are registered, and their `TearDown()` will be
-called in the reverse order.
-
-Note that Google Test takes ownership of the registered environment objects.
-Therefore **do not delete them** by yourself.
-
-You should call `AddGlobalTestEnvironment()` before `RUN_ALL_TESTS()` is
-called, probably in `main()`. If you use `gtest_main`, you need to      call
-this before `main()` starts for it to take effect. One way to do this is to
-define a global variable like this:
-
-```
-::testing::Environment* const foo_env = ::testing::AddGlobalTestEnvironment(new FooEnvironment);
-```
-
-However, we strongly recommend you to write your own `main()` and call
-`AddGlobalTestEnvironment()` there, as relying on initialization of global
-variables makes the code harder to read and may cause problems when you
-register multiple environments from different translation units and the
-environments have dependencies among them (remember that the compiler doesn't
-guarantee the order in which global variables from different translation units
-are initialized).
-
-_Availability:_ Linux, Windows, Mac.
-
-
-# Value Parameterized Tests #
-
-_Value-parameterized tests_ allow you to test your code with different
-parameters without writing multiple copies of the same test.
-
-Suppose you write a test for your code and then realize that your code is affected by a presence of a Boolean command line flag.
-
-```
-TEST(MyCodeTest, TestFoo) {
-  // A code to test foo().
-}
-```
-
-Usually people factor their test code into a function with a Boolean parameter in such situations. The function sets the flag, then executes the testing code.
-
-```
-void TestFooHelper(bool flag_value) {
-  flag = flag_value;
-  // A code to test foo().
-}
-
-TEST(MyCodeTest, TestFoo) {
-  TestFooHelper(false);
-  TestFooHelper(true);
-}
-```
-
-But this setup has serious drawbacks. First, when a test assertion fails in your tests, it becomes unclear what value of the parameter caused it to fail. You can stream a clarifying message into your `EXPECT`/`ASSERT` statements, but it you'll have to do it with all of them. Second, you have to add one such helper function per test. What if you have ten tests? Twenty? A hundred?
-
-Value-parameterized tests will let you write your test only once and then easily instantiate and run it with an arbitrary number of parameter values.
-
-Here are some other situations when value-parameterized tests come handy:
-
-  * You want to test different implementations of an OO interface.
-  * You want to test your code over various inputs (a.k.a. data-driven testing). This feature is easy to abuse, so please exercise your good sense when doing it!
-
-## How to Write Value-Parameterized Tests ##
-
-To write value-parameterized tests, first you should define a fixture
-class.  It must be derived from both `::testing::Test` and
-`::testing::WithParamInterface<T>` (the latter is a pure interface),
-where `T` is the type of your parameter values.  For convenience, you
-can just derive the fixture class from `::testing::TestWithParam<T>`,
-which itself is derived from both `::testing::Test` and
-`::testing::WithParamInterface<T>`. `T` can be any copyable type. If
-it's a raw pointer, you are responsible for managing the lifespan of
-the pointed values.
-
-```
-class FooTest : public ::testing::TestWithParam<const char*> {
-  // You can implement all the usual fixture class members here.
-  // To access the test parameter, call GetParam() from class
-  // TestWithParam<T>.
-};
-
-// Or, when you want to add parameters to a pre-existing fixture class:
-class BaseTest : public ::testing::Test {
-  ...
-};
-class BarTest : public BaseTest,
-                public ::testing::WithParamInterface<const char*> {
-  ...
-};
-```
-
-Then, use the `TEST_P` macro to define as many test patterns using
-this fixture as you want.  The `_P` suffix is for "parameterized" or
-"pattern", whichever you prefer to think.
-
-```
-TEST_P(FooTest, DoesBlah) {
-  // Inside a test, access the test parameter with the GetParam() method
-  // of the TestWithParam<T> class:
-  EXPECT_TRUE(foo.Blah(GetParam()));
-  ...
-}
-
-TEST_P(FooTest, HasBlahBlah) {
-  ...
-}
-```
-
-Finally, you can use `INSTANTIATE_TEST_CASE_P` to instantiate the test
-case with any set of parameters you want. Google Test defines a number of
-functions for generating test parameters. They return what we call
-(surprise!) _parameter generators_. Here is a summary of them,
-which are all in the `testing` namespace:
-
-| `Range(begin, end[, step])` | Yields values `{begin, begin+step, begin+step+step, ...}`. The values do not include `end`. `step` defaults to 1. |
-|:----------------------------|:------------------------------------------------------------------------------------------------------------------|
-| `Values(v1, v2, ..., vN)`   | Yields values `{v1, v2, ..., vN}`.                                                                                |
-| `ValuesIn(container)` and `ValuesIn(begin, end)` | Yields values from a C-style array, an STL-style container, or an iterator range `[begin, end)`. `container`, `begin`, and `end` can be expressions whose values are determined at run time.  |
-| `Bool()`                    | Yields sequence `{false, true}`.                                                                                  |
-| `Combine(g1, g2, ..., gN)`  | Yields all combinations (the Cartesian product for the math savvy) of the values generated by the `N` generators. This is only available if your system provides the `<tr1/tuple>` header. If you are sure your system does, and Google Test disagrees, you can override it by defining `GTEST_HAS_TR1_TUPLE=1`. See comments in [include/gtest/internal/gtest-port.h](../include/gtest/internal/gtest-port.h) for more information. |
-
-For more details, see the comments at the definitions of these functions in the [source code](../include/gtest/gtest-param-test.h).
-
-The following statement will instantiate tests from the `FooTest` test case
-each with parameter values `"meeny"`, `"miny"`, and `"moe"`.
-
-```
-INSTANTIATE_TEST_CASE_P(InstantiationName,
-                        FooTest,
-                        ::testing::Values("meeny", "miny", "moe"));
-```
-
-To distinguish different instances of the pattern (yes, you can
-instantiate it more than once), the first argument to
-`INSTANTIATE_TEST_CASE_P` is a prefix that will be added to the actual
-test case name. Remember to pick unique prefixes for different
-instantiations. The tests from the instantiation above will have these
-names:
-
-  * `InstantiationName/FooTest.DoesBlah/0` for `"meeny"`
-  * `InstantiationName/FooTest.DoesBlah/1` for `"miny"`
-  * `InstantiationName/FooTest.DoesBlah/2` for `"moe"`
-  * `InstantiationName/FooTest.HasBlahBlah/0` for `"meeny"`
-  * `InstantiationName/FooTest.HasBlahBlah/1` for `"miny"`
-  * `InstantiationName/FooTest.HasBlahBlah/2` for `"moe"`
-
-You can use these names in [--gtest\_filter](#running-a-subset-of-the-tests).
-
-This statement will instantiate all tests from `FooTest` again, each
-with parameter values `"cat"` and `"dog"`:
-
-```
-const char* pets[] = {"cat", "dog"};
-INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest,
-                        ::testing::ValuesIn(pets));
-```
-
-The tests from the instantiation above will have these names:
-
-  * `AnotherInstantiationName/FooTest.DoesBlah/0` for `"cat"`
-  * `AnotherInstantiationName/FooTest.DoesBlah/1` for `"dog"`
-  * `AnotherInstantiationName/FooTest.HasBlahBlah/0` for `"cat"`
-  * `AnotherInstantiationName/FooTest.HasBlahBlah/1` for `"dog"`
-
-Please note that `INSTANTIATE_TEST_CASE_P` will instantiate _all_
-tests in the given test case, whether their definitions come before or
-_after_ the `INSTANTIATE_TEST_CASE_P` statement.
-
-You can see
-[these](../samples/sample7_unittest.cc)
-[files](../samples/sample8_unittest.cc) for more examples.
-
-_Availability_: Linux, Windows (requires MSVC 8.0 or above), Mac; since version 1.2.0.
-
-## Creating Value-Parameterized Abstract Tests ##
-
-In the above, we define and instantiate `FooTest` in the same source
-file. Sometimes you may want to define value-parameterized tests in a
-library and let other people instantiate them later. This pattern is
-known as <i>abstract tests</i>. As an example of its application, when you
-are designing an interface you can write a standard suite of abstract
-tests (perhaps using a factory function as the test parameter) that
-all implementations of the interface are expected to pass. When
-someone implements the interface, he can instantiate your suite to get
-all the interface-conformance tests for free.
-
-To define abstract tests, you should organize your code like this:
-
-  1. Put the definition of the parameterized test fixture class (e.g. `FooTest`) in a header file, say `foo_param_test.h`. Think of this as _declaring_ your abstract tests.
-  1. Put the `TEST_P` definitions in `foo_param_test.cc`, which includes `foo_param_test.h`. Think of this as _implementing_ your abstract tests.
-
-Once they are defined, you can instantiate them by including
-`foo_param_test.h`, invoking `INSTANTIATE_TEST_CASE_P()`, and linking
-with `foo_param_test.cc`. You can instantiate the same abstract test
-case multiple times, possibly in different source files.
-
-# Typed Tests #
-
-Suppose you have multiple implementations of the same interface and
-want to make sure that all of them satisfy some common requirements.
-Or, you may have defined several types that are supposed to conform to
-the same "concept" and you want to verify it.  In both cases, you want
-the same test logic repeated for different types.
-
-While you can write one `TEST` or `TEST_F` for each type you want to
-test (and you may even factor the test logic into a function template
-that you invoke from the `TEST`), it's tedious and doesn't scale:
-if you want _m_ tests over _n_ types, you'll end up writing _m\*n_
-`TEST`s.
-
-_Typed tests_ allow you to repeat the same test logic over a list of
-types.  You only need to write the test logic once, although you must
-know the type list when writing typed tests.  Here's how you do it:
-
-First, define a fixture class template.  It should be parameterized
-by a type.  Remember to derive it from `::testing::Test`:
-
-```
-template <typename T>
-class FooTest : public ::testing::Test {
- public:
-  ...
-  typedef std::list<T> List;
-  static T shared_;
-  T value_;
-};
-```
-
-Next, associate a list of types with the test case, which will be
-repeated for each type in the list:
-
-```
-typedef ::testing::Types<char, int, unsigned int> MyTypes;
-TYPED_TEST_CASE(FooTest, MyTypes);
-```
-
-The `typedef` is necessary for the `TYPED_TEST_CASE` macro to parse
-correctly.  Otherwise the compiler will think that each comma in the
-type list introduces a new macro argument.
-
-Then, use `TYPED_TEST()` instead of `TEST_F()` to define a typed test
-for this test case.  You can repeat this as many times as you want:
-
-```
-TYPED_TEST(FooTest, DoesBlah) {
-  // Inside a test, refer to the special name TypeParam to get the type
-  // parameter.  Since we are inside a derived class template, C++ requires
-  // us to visit the members of FooTest via 'this'.
-  TypeParam n = this->value_;
-
-  // To visit static members of the fixture, add the 'TestFixture::'
-  // prefix.
-  n += TestFixture::shared_;
-
-  // To refer to typedefs in the fixture, add the 'typename TestFixture::'
-  // prefix.  The 'typename' is required to satisfy the compiler.
-  typename TestFixture::List values;
-  values.push_back(n);
-  ...
-}
-
-TYPED_TEST(FooTest, HasPropertyA) { ... }
-```
-
-You can see `samples/sample6_unittest.cc` for a complete example.
-
-_Availability:_ Linux, Windows (requires MSVC 8.0 or above), Mac;
-since version 1.1.0.
-
-# Type-Parameterized Tests #
-
-_Type-parameterized tests_ are like typed tests, except that they
-don't require you to know the list of types ahead of time.  Instead,
-you can define the test logic first and instantiate it with different
-type lists later.  You can even instantiate it more than once in the
-same program.
-
-If you are designing an interface or concept, you can define a suite
-of type-parameterized tests to verify properties that any valid
-implementation of the interface/concept should have.  Then, the author
-of each implementation can just instantiate the test suite with his
-type to verify that it conforms to the requirements, without having to
-write similar tests repeatedly.  Here's an example:
-
-First, define a fixture class template, as we did with typed tests:
-
-```
-template <typename T>
-class FooTest : public ::testing::Test {
-  ...
-};
-```
-
-Next, declare that you will define a type-parameterized test case:
-
-```
-TYPED_TEST_CASE_P(FooTest);
-```
-
-The `_P` suffix is for "parameterized" or "pattern", whichever you
-prefer to think.
-
-Then, use `TYPED_TEST_P()` to define a type-parameterized test.  You
-can repeat this as many times as you want:
-
-```
-TYPED_TEST_P(FooTest, DoesBlah) {
-  // Inside a test, refer to TypeParam to get the type parameter.
-  TypeParam n = 0;
-  ...
-}
-
-TYPED_TEST_P(FooTest, HasPropertyA) { ... }
-```
-
-Now the tricky part: you need to register all test patterns using the
-`REGISTER_TYPED_TEST_CASE_P` macro before you can instantiate them.
-The first argument of the macro is the test case name; the rest are
-the names of the tests in this test case:
-
-```
-REGISTER_TYPED_TEST_CASE_P(FooTest,
-                           DoesBlah, HasPropertyA);
-```
-
-Finally, you are free to instantiate the pattern with the types you
-want.  If you put the above code in a header file, you can `#include`
-it in multiple C++ source files and instantiate it multiple times.
-
-```
-typedef ::testing::Types<char, int, unsigned int> MyTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
-```
-
-To distinguish different instances of the pattern, the first argument
-to the `INSTANTIATE_TYPED_TEST_CASE_P` macro is a prefix that will be
-added to the actual test case name.  Remember to pick unique prefixes
-for different instances.
-
-In the special case where the type list contains only one type, you
-can write that type directly without `::testing::Types<...>`, like this:
-
-```
-INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
-```
-
-You can see `samples/sample6_unittest.cc` for a complete example.
-
-_Availability:_ Linux, Windows (requires MSVC 8.0 or above), Mac;
-since version 1.1.0.
-
-# Testing Private Code #
-
-If you change your software's internal implementation, your tests should not
-break as long as the change is not observable by users. Therefore, per the
-_black-box testing principle_, most of the time you should test your code
-through its public interfaces.
-
-If you still find yourself needing to test internal implementation code,
-consider if there's a better design that wouldn't require you to do so. If you
-absolutely have to test non-public interface code though, you can. There are
-two cases to consider:
-
-  * Static functions (_not_ the same as static member functions!) or unnamed namespaces, and
-  * Private or protected class members
-
-## Static Functions ##
-
-Both static functions and definitions/declarations in an unnamed namespace are
-only visible within the same translation unit. To test them, you can `#include`
-the entire `.cc` file being tested in your `*_test.cc` file. (`#include`ing `.cc`
-files is not a good way to reuse code - you should not do this in production
-code!)
-
-However, a better approach is to move the private code into the
-`foo::internal` namespace, where `foo` is the namespace your project normally
-uses, and put the private declarations in a `*-internal.h` file. Your
-production `.cc` files and your tests are allowed to include this internal
-header, but your clients are not. This way, you can fully test your internal
-implementation without leaking it to your clients.
-
-## Private Class Members ##
-
-Private class members are only accessible from within the class or by friends.
-To access a class' private members, you can declare your test fixture as a
-friend to the class and define accessors in your fixture. Tests using the
-fixture can then access the private members of your production class via the
-accessors in the fixture. Note that even though your fixture is a friend to
-your production class, your tests are not automatically friends to it, as they
-are technically defined in sub-classes of the fixture.
-
-Another way to test private members is to refactor them into an implementation
-class, which is then declared in a `*-internal.h` file. Your clients aren't
-allowed to include this header but your tests can. Such is called the Pimpl
-(Private Implementation) idiom.
-
-Or, you can declare an individual test as a friend of your class by adding this
-line in the class body:
-
-```
-FRIEND_TEST(TestCaseName, TestName);
-```
-
-For example,
-```
-// foo.h
-#include "gtest/gtest_prod.h"
-
-// Defines FRIEND_TEST.
-class Foo {
-  ...
- private:
-  FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
-  int Bar(void* x);
-};
-
-// foo_test.cc
-...
-TEST(FooTest, BarReturnsZeroOnNull) {
-  Foo foo;
-  EXPECT_EQ(0, foo.Bar(NULL));
-  // Uses Foo's private member Bar().
-}
-```
-
-Pay special attention when your class is defined in a namespace, as you should
-define your test fixtures and tests in the same namespace if you want them to
-be friends of your class. For example, if the code to be tested looks like:
-
-```
-namespace my_namespace {
-
-class Foo {
-  friend class FooTest;
-  FRIEND_TEST(FooTest, Bar);
-  FRIEND_TEST(FooTest, Baz);
-  ...
-  definition of the class Foo
-  ...
-};
-
-}  // namespace my_namespace
-```
-
-Your test code should be something like:
-
-```
-namespace my_namespace {
-class FooTest : public ::testing::Test {
- protected:
-  ...
-};
-
-TEST_F(FooTest, Bar) { ... }
-TEST_F(FooTest, Baz) { ... }
-
-}  // namespace my_namespace
-```
-
-# Catching Failures #
-
-If you are building a testing utility on top of Google Test, you'll
-want to test your utility.  What framework would you use to test it?
-Google Test, of course.
-
-The challenge is to verify that your testing utility reports failures
-correctly.  In frameworks that report a failure by throwing an
-exception, you could catch the exception and assert on it.  But Google
-Test doesn't use exceptions, so how do we test that a piece of code
-generates an expected failure?
-
-`"gtest/gtest-spi.h"` contains some constructs to do this.  After 
-`#include`ing this header, you can use
-
-| `EXPECT_FATAL_FAILURE(`_statement, substring_`);` |
-|:--------------------------------------------------|
-
-to assert that _statement_ generates a fatal (e.g. `ASSERT_*`) failure
-whose message contains the given _substring_, or use
-
-| `EXPECT_NONFATAL_FAILURE(`_statement, substring_`);` |
-|:-----------------------------------------------------|
-
-if you are expecting a non-fatal (e.g. `EXPECT_*`) failure.
-
-For technical reasons, there are some caveats:
-
-  1. You cannot stream a failure message to either macro.
-  1. _statement_ in `EXPECT_FATAL_FAILURE()` cannot reference local non-static variables or non-static members of `this` object.
-  1. _statement_ in `EXPECT_FATAL_FAILURE()` cannot return a value.
-
-_Note:_ Google Test is designed with threads in mind. Once the
-synchronization primitives in `"gtest/internal/gtest-port.h"` have
-been implemented, Google Test will become thread-safe, meaning that
-you can then use assertions in multiple threads concurrently. Before
-that, however, Google Test only supports single-threaded usage. Once
-thread-safe, `EXPECT_FATAL_FAILURE()` and `EXPECT_NONFATAL_FAILURE()`
-will capture failures in the current thread only. If _statement_
-creates new threads, failures in these threads will be ignored. If
-you want to capture failures from all threads instead, you should use
-the following macros:
-
-| `EXPECT_FATAL_FAILURE_ON_ALL_THREADS(`_statement, substring_`);` |
-|:-----------------------------------------------------------------|
-| `EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(`_statement, substring_`);` |
-
-# Getting the Current Test's Name #
-
-Sometimes a function may need to know the name of the currently running test.
-For example, you may be using the `SetUp()` method of your test fixture to set
-the golden file name based on which test is running. The `::testing::TestInfo`
-class has this information:
-
-```
-namespace testing {
-
-class TestInfo {
- public:
-  // Returns the test case name and the test name, respectively.
-  //
-  // Do NOT delete or free the return value - it's managed by the
-  // TestInfo class.
-  const char* test_case_name() const;
-  const char* name() const;
-};
-
-}  // namespace testing
-```
-
-
-> To obtain a `TestInfo` object for the currently running test, call
-`current_test_info()` on the `UnitTest` singleton object:
-
-```
-// Gets information about the currently running test.
-// Do NOT delete the returned object - it's managed by the UnitTest class.
-const ::testing::TestInfo* const test_info =
-  ::testing::UnitTest::GetInstance()->current_test_info();
-printf("We are in test %s of test case %s.\n",
-       test_info->name(), test_info->test_case_name());
-```
-
-`current_test_info()` returns a null pointer if no test is running. In
-particular, you cannot find the test case name in `TestCaseSetUp()`,
-`TestCaseTearDown()` (where you know the test case name implicitly), or
-functions called from them.
-
-_Availability:_ Linux, Windows, Mac.
-
-# Extending Google Test by Handling Test Events #
-
-Google Test provides an <b>event listener API</b> to let you receive
-notifications about the progress of a test program and test
-failures. The events you can listen to include the start and end of
-the test program, a test case, or a test method, among others. You may
-use this API to augment or replace the standard console output,
-replace the XML output, or provide a completely different form of
-output, such as a GUI or a database. You can also use test events as
-checkpoints to implement a resource leak checker, for example.
-
-_Availability:_ Linux, Windows, Mac; since v1.4.0.
-
-## Defining Event Listeners ##
-
-To define a event listener, you subclass either
-[testing::TestEventListener](../include/gtest/gtest.h#L991)
-or [testing::EmptyTestEventListener](../include/gtest/gtest.h#L1044).
-The former is an (abstract) interface, where <i>each pure virtual method<br>
-can be overridden to handle a test event</i> (For example, when a test
-starts, the `OnTestStart()` method will be called.). The latter provides
-an empty implementation of all methods in the interface, such that a
-subclass only needs to override the methods it cares about.
-
-When an event is fired, its context is passed to the handler function
-as an argument. The following argument types are used:
-  * [UnitTest](../include/gtest/gtest.h#L1151) reflects the state of the entire test program,
-  * [TestCase](../include/gtest/gtest.h#L778) has information about a test case, which can contain one or more tests,
-  * [TestInfo](../include/gtest/gtest.h#L644) contains the state of a test, and
-  * [TestPartResult](../include/gtest/gtest-test-part.h#L47) represents the result of a test assertion.
-
-An event handler function can examine the argument it receives to find
-out interesting information about the event and the test program's
-state.  Here's an example:
-
-```
-  class MinimalistPrinter : public ::testing::EmptyTestEventListener {
-    // Called before a test starts.
-    virtual void OnTestStart(const ::testing::TestInfo& test_info) {
-      printf("*** Test %s.%s starting.\n",
-             test_info.test_case_name(), test_info.name());
-    }
-
-    // Called after a failed assertion or a SUCCEED() invocation.
-    virtual void OnTestPartResult(
-        const ::testing::TestPartResult& test_part_result) {
-      printf("%s in %s:%d\n%s\n",
-             test_part_result.failed() ? "*** Failure" : "Success",
-             test_part_result.file_name(),
-             test_part_result.line_number(),
-             test_part_result.summary());
-    }
-
-    // Called after a test ends.
-    virtual void OnTestEnd(const ::testing::TestInfo& test_info) {
-      printf("*** Test %s.%s ending.\n",
-             test_info.test_case_name(), test_info.name());
-    }
-  };
-```
-
-## Using Event Listeners ##
-
-To use the event listener you have defined, add an instance of it to
-the Google Test event listener list (represented by class
-[TestEventListeners](../include/gtest/gtest.h#L1064)
-- note the "s" at the end of the name) in your
-`main()` function, before calling `RUN_ALL_TESTS()`:
-```
-int main(int argc, char** argv) {
-  ::testing::InitGoogleTest(&argc, argv);
-  // Gets hold of the event listener list.
-  ::testing::TestEventListeners& listeners =
-      ::testing::UnitTest::GetInstance()->listeners();
-  // Adds a listener to the end.  Google Test takes the ownership.
-  listeners.Append(new MinimalistPrinter);
-  return RUN_ALL_TESTS();
-}
-```
-
-There's only one problem: the default test result printer is still in
-effect, so its output will mingle with the output from your minimalist
-printer. To suppress the default printer, just release it from the
-event listener list and delete it. You can do so by adding one line:
-```
-  ...
-  delete listeners.Release(listeners.default_result_printer());
-  listeners.Append(new MinimalistPrinter);
-  return RUN_ALL_TESTS();
-```
-
-Now, sit back and enjoy a completely different output from your
-tests. For more details, you can read this
-[sample](../samples/sample9_unittest.cc).
-
-You may append more than one listener to the list. When an `On*Start()`
-or `OnTestPartResult()` event is fired, the listeners will receive it in
-the order they appear in the list (since new listeners are added to
-the end of the list, the default text printer and the default XML
-generator will receive the event first). An `On*End()` event will be
-received by the listeners in the _reverse_ order. This allows output by
-listeners added later to be framed by output from listeners added
-earlier.
-
-## Generating Failures in Listeners ##
-
-You may use failure-raising macros (`EXPECT_*()`, `ASSERT_*()`,
-`FAIL()`, etc) when processing an event. There are some restrictions:
-
-  1. You cannot generate any failure in `OnTestPartResult()` (otherwise it will cause `OnTestPartResult()` to be called recursively).
-  1. A listener that handles `OnTestPartResult()` is not allowed to generate any failure.
-
-When you add listeners to the listener list, you should put listeners
-that handle `OnTestPartResult()` _before_ listeners that can generate
-failures. This ensures that failures generated by the latter are
-attributed to the right test by the former.
-
-We have a sample of failure-raising listener
-[here](../samples/sample10_unittest.cc).
-
-# Running Test Programs: Advanced Options #
-
-Google Test test programs are ordinary executables. Once built, you can run
-them directly and affect their behavior via the following environment variables
-and/or command line flags. For the flags to work, your programs must call
-`::testing::InitGoogleTest()` before calling `RUN_ALL_TESTS()`.
-
-To see a list of supported flags and their usage, please run your test
-program with the `--help` flag.  You can also use `-h`, `-?`, or `/?`
-for short.  This feature is added in version 1.3.0.
-
-If an option is specified both by an environment variable and by a
-flag, the latter takes precedence.  Most of the options can also be
-set/read in code: to access the value of command line flag
-`--gtest_foo`, write `::testing::GTEST_FLAG(foo)`.  A common pattern is
-to set the value of a flag before calling `::testing::InitGoogleTest()`
-to change the default value of the flag:
-```
-int main(int argc, char** argv) {
-  // Disables elapsed time by default.
-  ::testing::GTEST_FLAG(print_time) = false;
-
-  // This allows the user to override the flag on the command line.
-  ::testing::InitGoogleTest(&argc, argv);
-
-  return RUN_ALL_TESTS();
-}
-```
-
-## Selecting Tests ##
-
-This section shows various options for choosing which tests to run.
-
-### Listing Test Names ###
-
-Sometimes it is necessary to list the available tests in a program before
-running them so that a filter may be applied if needed. Including the flag
-`--gtest_list_tests` overrides all other flags and lists tests in the following
-format:
-```
-TestCase1.
-  TestName1
-  TestName2
-TestCase2.
-  TestName
-```
-
-None of the tests listed are actually run if the flag is provided. There is no
-corresponding environment variable for this flag.
-
-_Availability:_ Linux, Windows, Mac.
-
-### Running a Subset of the Tests ###
-
-By default, a Google Test program runs all tests the user has defined.
-Sometimes, you want to run only a subset of the tests (e.g. for debugging or
-quickly verifying a change). If you set the `GTEST_FILTER` environment variable
-or the `--gtest_filter` flag to a filter string, Google Test will only run the
-tests whose full names (in the form of `TestCaseName.TestName`) match the
-filter.
-
-The format of a filter is a '`:`'-separated list of wildcard patterns (called
-the positive patterns) optionally followed by a '`-`' and another
-'`:`'-separated pattern list (called the negative patterns). A test matches the
-filter if and only if it matches any of the positive patterns but does not
-match any of the negative patterns.
-
-A pattern may contain `'*'` (matches any string) or `'?'` (matches any single
-character). For convenience, the filter `'*-NegativePatterns'` can be also
-written as `'-NegativePatterns'`.
-
-For example:
-
-  * `./foo_test` Has no flag, and thus runs all its tests.
-  * `./foo_test --gtest_filter=*` Also runs everything, due to the single match-everything `*` value.
-  * `./foo_test --gtest_filter=FooTest.*` Runs everything in test case `FooTest`.
-  * `./foo_test --gtest_filter=*Null*:*Constructor*` Runs any test whose full name contains either `"Null"` or `"Constructor"`.
-  * `./foo_test --gtest_filter=-*DeathTest.*` Runs all non-death tests.
-  * `./foo_test --gtest_filter=FooTest.*-FooTest.Bar` Runs everything in test case `FooTest` except `FooTest.Bar`.
-
-_Availability:_ Linux, Windows, Mac.
-
-### Temporarily Disabling Tests ###
-
-If you have a broken test that you cannot fix right away, you can add the
-`DISABLED_` prefix to its name. This will exclude it from execution. This is
-better than commenting out the code or using `#if 0`, as disabled tests are
-still compiled (and thus won't rot).
-
-If you need to disable all tests in a test case, you can either add `DISABLED_`
-to the front of the name of each test, or alternatively add it to the front of
-the test case name.
-
-For example, the following tests won't be run by Google Test, even though they
-will still be compiled:
-
-```
-// Tests that Foo does Abc.
-TEST(FooTest, DISABLED_DoesAbc) { ... }
-
-class DISABLED_BarTest : public ::testing::Test { ... };
-
-// Tests that Bar does Xyz.
-TEST_F(DISABLED_BarTest, DoesXyz) { ... }
-```
-
-_Note:_ This feature should only be used for temporary pain-relief. You still
-have to fix the disabled tests at a later date. As a reminder, Google Test will
-print a banner warning you if a test program contains any disabled tests.
-
-_Tip:_ You can easily count the number of disabled tests you have
-using `grep`. This number can be used as a metric for improving your
-test quality.
-
-_Availability:_ Linux, Windows, Mac.
-
-### Temporarily Enabling Disabled Tests ###
-
-To include [disabled tests](#temporarily-disabling-tests) in test
-execution, just invoke the test program with the
-`--gtest_also_run_disabled_tests` flag or set the
-`GTEST_ALSO_RUN_DISABLED_TESTS` environment variable to a value other
-than `0`.  You can combine this with the
-[--gtest\_filter](#running-a-subset-of-the-tests) flag to further select
-which disabled tests to run.
-
-_Availability:_ Linux, Windows, Mac; since version 1.3.0.
-
-## Repeating the Tests ##
-
-Once in a while you'll run into a test whose result is hit-or-miss. Perhaps it
-will fail only 1% of the time, making it rather hard to reproduce the bug under
-a debugger. This can be a major source of frustration.
-
-The `--gtest_repeat` flag allows you to repeat all (or selected) test methods
-in a program many times. Hopefully, a flaky test will eventually fail and give
-you a chance to debug. Here's how to use it:
-
-| `$ foo_test --gtest_repeat=1000` | Repeat foo\_test 1000 times and don't stop at failures. |
-|:---------------------------------|:--------------------------------------------------------|
-| `$ foo_test --gtest_repeat=-1`   | A negative count means repeating forever.               |
-| `$ foo_test --gtest_repeat=1000 --gtest_break_on_failure` | Repeat foo\_test 1000 times, stopping at the first failure. This is especially useful when running under a debugger: when the testfails, it will drop into the debugger and you can then inspect variables and stacks. |
-| `$ foo_test --gtest_repeat=1000 --gtest_filter=FooBar` | Repeat the tests whose name matches the filter 1000 times. |
-
-If your test program contains global set-up/tear-down code registered
-using `AddGlobalTestEnvironment()`, it will be repeated in each
-iteration as well, as the flakiness may be in it. You can also specify
-the repeat count by setting the `GTEST_REPEAT` environment variable.
-
-_Availability:_ Linux, Windows, Mac.
-
-## Shuffling the Tests ##
-
-You can specify the `--gtest_shuffle` flag (or set the `GTEST_SHUFFLE`
-environment variable to `1`) to run the tests in a program in a random
-order. This helps to reveal bad dependencies between tests.
-
-By default, Google Test uses a random seed calculated from the current
-time. Therefore you'll get a different order every time. The console
-output includes the random seed value, such that you can reproduce an
-order-related test failure later. To specify the random seed
-explicitly, use the `--gtest_random_seed=SEED` flag (or set the
-`GTEST_RANDOM_SEED` environment variable), where `SEED` is an integer
-between 0 and 99999. The seed value 0 is special: it tells Google Test
-to do the default behavior of calculating the seed from the current
-time.
-
-If you combine this with `--gtest_repeat=N`, Google Test will pick a
-different random seed and re-shuffle the tests in each iteration.
-
-_Availability:_ Linux, Windows, Mac; since v1.4.0.
-
-## Controlling Test Output ##
-
-This section teaches how to tweak the way test results are reported.
-
-### Colored Terminal Output ###
-
-Google Test can use colors in its terminal output to make it easier to spot
-the separation between tests, and whether tests passed.
-
-You can set the GTEST\_COLOR environment variable or set the `--gtest_color`
-command line flag to `yes`, `no`, or `auto` (the default) to enable colors,
-disable colors, or let Google Test decide. When the value is `auto`, Google
-Test will use colors if and only if the output goes to a terminal and (on
-non-Windows platforms) the `TERM` environment variable is set to `xterm` or
-`xterm-color`.
-
-_Availability:_ Linux, Windows, Mac.
-
-### Suppressing the Elapsed Time ###
-
-By default, Google Test prints the time it takes to run each test.  To
-suppress that, run the test program with the `--gtest_print_time=0`
-command line flag.  Setting the `GTEST_PRINT_TIME` environment
-variable to `0` has the same effect.
-
-_Availability:_ Linux, Windows, Mac.  (In Google Test 1.3.0 and lower,
-the default behavior is that the elapsed time is **not** printed.)
-
-### Generating an XML Report ###
-
-Google Test can emit a detailed XML report to a file in addition to its normal
-textual output. The report contains the duration of each test, and thus can
-help you identify slow tests.
-
-To generate the XML report, set the `GTEST_OUTPUT` environment variable or the
-`--gtest_output` flag to the string `"xml:_path_to_output_file_"`, which will
-create the file at the given location. You can also just use the string
-`"xml"`, in which case the output can be found in the `test_detail.xml` file in
-the current directory.
-
-If you specify a directory (for example, `"xml:output/directory/"` on Linux or
-`"xml:output\directory\"` on Windows), Google Test will create the XML file in
-that directory, named after the test executable (e.g. `foo_test.xml` for test
-program `foo_test` or `foo_test.exe`). If the file already exists (perhaps left
-over from a previous run), Google Test will pick a different name (e.g.
-`foo_test_1.xml`) to avoid overwriting it.
-
-The report uses the format described here.  It is based on the
-`junitreport` Ant task and can be parsed by popular continuous build
-systems like [Hudson](https://hudson.dev.java.net/). Since that format
-was originally intended for Java, a little interpretation is required
-to make it apply to Google Test tests, as shown here:
-
-```
-<testsuites name="AllTests" ...>
-  <testsuite name="test_case_name" ...>
-    <testcase name="test_name" ...>
-      <failure message="..."/>
-      <failure message="..."/>
-      <failure message="..."/>
-    </testcase>
-  </testsuite>
-</testsuites>
-```
-
-  * The root `<testsuites>` element corresponds to the entire test program.
-  * `<testsuite>` elements correspond to Google Test test cases.
-  * `<testcase>` elements correspond to Google Test test functions.
-
-For instance, the following program
-
-```
-TEST(MathTest, Addition) { ... }
-TEST(MathTest, Subtraction) { ... }
-TEST(LogicTest, NonContradiction) { ... }
-```
-
-could generate this report:
-
-```
-<?xml version="1.0" encoding="UTF-8"?>
-<testsuites tests="3" failures="1" errors="0" time="35" name="AllTests">
-  <testsuite name="MathTest" tests="2" failures="1" errors="0" time="15">
-    <testcase name="Addition" status="run" time="7" classname="">
-      <failure message="Value of: add(1, 1)&#x0A; Actual: 3&#x0A;Expected: 2" type=""/>
-      <failure message="Value of: add(1, -1)&#x0A; Actual: 1&#x0A;Expected: 0" type=""/>
-    </testcase>
-    <testcase name="Subtraction" status="run" time="5" classname="">
-    </testcase>
-  </testsuite>
-  <testsuite name="LogicTest" tests="1" failures="0" errors="0" time="5">
-    <testcase name="NonContradiction" status="run" time="5" classname="">
-    </testcase>
-  </testsuite>
-</testsuites>
-```
-
-Things to note:
-
-  * The `tests` attribute of a `<testsuites>` or `<testsuite>` element tells how many test functions the Google Test program or test case contains, while the `failures` attribute tells how many of them failed.
-  * The `time` attribute expresses the duration of the test, test case, or entire test program in milliseconds.
-  * Each `<failure>` element corresponds to a single failed Google Test assertion.
-  * Some JUnit concepts don't apply to Google Test, yet we have to conform to the DTD. Therefore you'll see some dummy elements and attributes in the report. You can safely ignore these parts.
-
-_Availability:_ Linux, Windows, Mac.
-
-## Controlling How Failures Are Reported ##
-
-### Turning Assertion Failures into Break-Points ###
-
-When running test programs under a debugger, it's very convenient if the
-debugger can catch an assertion failure and automatically drop into interactive
-mode. Google Test's _break-on-failure_ mode supports this behavior.
-
-To enable it, set the `GTEST_BREAK_ON_FAILURE` environment variable to a value
-other than `0` . Alternatively, you can use the `--gtest_break_on_failure`
-command line flag.
-
-_Availability:_ Linux, Windows, Mac.
-
-### Disabling Catching Test-Thrown Exceptions ###
-
-Google Test can be used either with or without exceptions enabled.  If
-a test throws a C++ exception or (on Windows) a structured exception
-(SEH), by default Google Test catches it, reports it as a test
-failure, and continues with the next test method.  This maximizes the
-coverage of a test run.  Also, on Windows an uncaught exception will
-cause a pop-up window, so catching the exceptions allows you to run
-the tests automatically.
-
-When debugging the test failures, however, you may instead want the
-exceptions to be handled by the debugger, such that you can examine
-the call stack when an exception is thrown.  To achieve that, set the
-`GTEST_CATCH_EXCEPTIONS` environment variable to `0`, or use the
-`--gtest_catch_exceptions=0` flag when running the tests.
-
-**Availability**: Linux, Windows, Mac.
-
-### Letting Another Testing Framework Drive ###
-
-If you work on a project that has already been using another testing
-framework and is not ready to completely switch to Google Test yet,
-you can get much of Google Test's benefit by using its assertions in
-your existing tests.  Just change your `main()` function to look
-like:
-
-```
-#include "gtest/gtest.h"
-
-int main(int argc, char** argv) {
-  ::testing::GTEST_FLAG(throw_on_failure) = true;
-  // Important: Google Test must be initialized.
-  ::testing::InitGoogleTest(&argc, argv);
-
-  ... whatever your existing testing framework requires ...
-}
-```
-
-With that, you can use Google Test assertions in addition to the
-native assertions your testing framework provides, for example:
-
-```
-void TestFooDoesBar() {
-  Foo foo;
-  EXPECT_LE(foo.Bar(1), 100);     // A Google Test assertion.
-  CPPUNIT_ASSERT(foo.IsEmpty());  // A native assertion.
-}
-```
-
-If a Google Test assertion fails, it will print an error message and
-throw an exception, which will be treated as a failure by your host
-testing framework.  If you compile your code with exceptions disabled,
-a failed Google Test assertion will instead exit your program with a
-non-zero code, which will also signal a test failure to your test
-runner.
-
-If you don't write `::testing::GTEST_FLAG(throw_on_failure) = true;` in
-your `main()`, you can alternatively enable this feature by specifying
-the `--gtest_throw_on_failure` flag on the command-line or setting the
-`GTEST_THROW_ON_FAILURE` environment variable to a non-zero value.
-
-Death tests are _not_ supported when other test framework is used to organize tests.
-
-_Availability:_ Linux, Windows, Mac; since v1.3.0.
-
-## Distributing Test Functions to Multiple Machines ##
-
-If you have more than one machine you can use to run a test program,
-you might want to run the test functions in parallel and get the
-result faster.  We call this technique _sharding_, where each machine
-is called a _shard_.
-
-Google Test is compatible with test sharding.  To take advantage of
-this feature, your test runner (not part of Google Test) needs to do
-the following:
-
-  1. Allocate a number of machines (shards) to run the tests.
-  1. On each shard, set the `GTEST_TOTAL_SHARDS` environment variable to the total number of shards.  It must be the same for all shards.
-  1. On each shard, set the `GTEST_SHARD_INDEX` environment variable to the index of the shard.  Different shards must be assigned different indices, which must be in the range `[0, GTEST_TOTAL_SHARDS - 1]`.
-  1. Run the same test program on all shards.  When Google Test sees the above two environment variables, it will select a subset of the test functions to run.  Across all shards, each test function in the program will be run exactly once.
-  1. Wait for all shards to finish, then collect and report the results.
-
-Your project may have tests that were written without Google Test and
-thus don't understand this protocol.  In order for your test runner to
-figure out which test supports sharding, it can set the environment
-variable `GTEST_SHARD_STATUS_FILE` to a non-existent file path.  If a
-test program supports sharding, it will create this file to
-acknowledge the fact (the actual contents of the file are not
-important at this time; although we may stick some useful information
-in it in the future.); otherwise it will not create it.
-
-Here's an example to make it clear.  Suppose you have a test program
-`foo_test` that contains the following 5 test functions:
-```
-TEST(A, V)
-TEST(A, W)
-TEST(B, X)
-TEST(B, Y)
-TEST(B, Z)
-```
-and you have 3 machines at your disposal.  To run the test functions in
-parallel, you would set `GTEST_TOTAL_SHARDS` to 3 on all machines, and
-set `GTEST_SHARD_INDEX` to 0, 1, and 2 on the machines respectively.
-Then you would run the same `foo_test` on each machine.
-
-Google Test reserves the right to change how the work is distributed
-across the shards, but here's one possible scenario:
-
-  * Machine #0 runs `A.V` and `B.X`.
-  * Machine #1 runs `A.W` and `B.Y`.
-  * Machine #2 runs `B.Z`.
-
-_Availability:_ Linux, Windows, Mac; since version 1.3.0.
-
-# Fusing Google Test Source Files #
-
-Google Test's implementation consists of ~30 files (excluding its own
-tests).  Sometimes you may want them to be packaged up in two files (a
-`.h` and a `.cc`) instead, such that you can easily copy them to a new
-machine and start hacking there.  For this we provide an experimental
-Python script `fuse_gtest_files.py` in the `scripts/` directory (since release 1.3.0).
-Assuming you have Python 2.4 or above installed on your machine, just
-go to that directory and run
-```
-python fuse_gtest_files.py OUTPUT_DIR
-```
-
-and you should see an `OUTPUT_DIR` directory being created with files
-`gtest/gtest.h` and `gtest/gtest-all.cc` in it.  These files contain
-everything you need to use Google Test.  Just copy them to anywhere
-you want and you are ready to write tests.  You can use the
-[scripts/test/Makefile](../scripts/test/Makefile)
-file as an example on how to compile your tests against them.
-
-# Where to Go from Here #
-
-Congratulations! You've now learned more advanced Google Test tools and are
-ready to tackle more complex testing tasks. If you want to dive even deeper, you
-can read the [Frequently-Asked Questions](FAQ.md).
diff --git a/src/external/googletest/googletest/docs/DevGuide.md b/src/external/googletest/googletest/docs/DevGuide.md
deleted file mode 100644 (file)
index 06467a3..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-If you are interested in understanding the internals of Google Test,
-building from source, or contributing ideas or modifications to the
-project, then this document is for you.
-
-# Introduction #
-
-First, let's give you some background of the project.
-
-## Licensing ##
-
-All Google Test source and pre-built packages are provided under the [New BSD License](http://www.opensource.org/licenses/bsd-license.php).
-
-## The Google Test Community ##
-
-The Google Test community exists primarily through the [discussion group](http://groups.google.com/group/googletestframework) and the GitHub repository.
-You are definitely encouraged to contribute to the
-discussion and you can also help us to keep the effectiveness of the
-group high by following and promoting the guidelines listed here.
-
-### Please Be Friendly ###
-
-Showing courtesy and respect to others is a vital part of the Google
-culture, and we strongly encourage everyone participating in Google
-Test development to join us in accepting nothing less. Of course,
-being courteous is not the same as failing to constructively disagree
-with each other, but it does mean that we should be respectful of each
-other when enumerating the 42 technical reasons that a particular
-proposal may not be the best choice. There's never a reason to be
-antagonistic or dismissive toward anyone who is sincerely trying to
-contribute to a discussion.
-
-Sure, C++ testing is serious business and all that, but it's also
-a lot of fun. Let's keep it that way. Let's strive to be one of the
-friendliest communities in all of open source.
-
-As always, discuss Google Test in the official GoogleTest discussion group.
-You don't have to actually submit code in order to sign up. Your participation
-itself is a valuable contribution.
-
-# Working with the Code #
-
-If you want to get your hands dirty with the code inside Google Test,
-this is the section for you.
-
-## Compiling from Source ##
-
-Once you check out the code, you can find instructions on how to
-compile it in the [README](../README.md) file.
-
-## Testing ##
-
-A testing framework is of no good if itself is not thoroughly tested.
-Tests should be written for any new code, and changes should be
-verified to not break existing tests before they are submitted for
-review. To perform the tests, follow the instructions in
-[README](../README.md) and verify that there are no failures.
-
-# Contributing Code #
-
-We are excited that Google Test is now open source, and hope to get
-great patches from the community. Before you fire up your favorite IDE
-and begin hammering away at that new feature, though, please take the
-time to read this section and understand the process. While it seems
-rigorous, we want to keep a high standard of quality in the code
-base.
-
-## Contributor License Agreements ##
-
-You must sign a Contributor License Agreement (CLA) before we can
-accept any code.  The CLA protects you and us.
-
-  * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html).
-  * If you work for a company that wants to allow you to contribute your work to Google Test, then you'll need to sign a [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html).
-
-Follow either of the two links above to access the appropriate CLA and
-instructions for how to sign and return it.
-
-## Coding Style ##
-
-To keep the source consistent, readable, diffable and easy to merge,
-we use a fairly rigid coding style, as defined by the [google-styleguide](http://code.google.com/p/google-styleguide/) project.  All patches will be expected
-to conform to the style outlined [here](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml).
-
-## Updating Generated Code ##
-
-Some of Google Test's source files are generated by the Pump tool (a
-Python script).  If you need to update such files, please modify the
-source (`foo.h.pump`) and re-generate the C++ file using Pump.  You
-can read the PumpManual for details.
-
-## Submitting Patches ##
-
-Please do submit code. Here's what you need to do:
-
-  1. A submission should be a set of changes that addresses one issue in the [issue tracker](https://github.com/google/googletest/issues). Please don't mix more than one logical change per submittal, because it makes the history hard to follow. If you want to make a change that doesn't have a corresponding issue in the issue tracker, please create one.
-  1. Also, coordinate with team members that are listed on the issue in question. This ensures that work isn't being duplicated and communicating your plan early also generally leads to better patches.
-  1. Ensure that your code adheres to the [Google Test source code style](#Coding_Style.md).
-  1. Ensure that there are unit tests for your code.
-  1. Sign a Contributor License Agreement.
-  1. Create a Pull Request in the usual way.
-
-## Google Test Committers ##
-
-The current members of the Google Test engineering team are the only
-committers at present. In the great tradition of eating one's own
-dogfood, we will be requiring each new Google Test engineering team
-member to earn the right to become a committer by following the
-procedures in this document, writing consistently great code, and
-demonstrating repeatedly that he or she truly gets the zen of Google
-Test.
-
-# Release Process #
-
-We follow a typical release process:
-
-  1. A release branch named `release-X.Y` is created.
-  1. Bugs are fixed and features are added in trunk; those individual patches are merged into the release branch until it's stable.
-  1. An individual point release (the `Z` in `X.Y.Z`) is made by creating a tag from the branch.
-  1. Repeat steps 2 and 3 throughout one release cycle (as determined by features or time).
-  1. Go back to step 1 to create another release branch and so on.
-
----
-
-This page is based on the [Making GWT Better](http://code.google.com/webtoolkit/makinggwtbetter.html) guide from the [Google Web Toolkit](http://code.google.com/webtoolkit/) project.  Except as otherwise [noted](http://code.google.com/policies.html#restrictions), the content of this page is licensed under the [Creative Commons Attribution 2.5 License](http://creativecommons.org/licenses/by/2.5/).
diff --git a/src/external/googletest/googletest/docs/Documentation.md b/src/external/googletest/googletest/docs/Documentation.md
deleted file mode 100644 (file)
index 8ca1aac..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-This page lists all documentation wiki pages for Google Test **(the SVN trunk version)**
--- **if you use a released version of Google Test, please read the
-documentation for that specific version instead.**
-
-  * [Primer](Primer.md) -- start here if you are new to Google Test.
-  * [Samples](Samples.md) -- learn from examples.
-  * [AdvancedGuide](AdvancedGuide.md) -- learn more about Google Test.
-  * [XcodeGuide](XcodeGuide.md) -- how to use Google Test in Xcode on Mac.
-  * [Frequently-Asked Questions](FAQ.md) -- check here before asking a question on the mailing list.
-
-To contribute code to Google Test, read:
-
-  * [DevGuide](DevGuide.md) -- read this _before_ writing your first patch.
-  * [PumpManual](PumpManual.md) -- how we generate some of Google Test's source files.
\ No newline at end of file
diff --git a/src/external/googletest/googletest/docs/FAQ.md b/src/external/googletest/googletest/docs/FAQ.md
deleted file mode 100644 (file)
index 5fd6cb7..0000000
+++ /dev/null
@@ -1,1087 +0,0 @@
-
-
-If you cannot find the answer to your question here, and you have read
-[Primer](Primer.md) and [AdvancedGuide](AdvancedGuide.md), send it to
-googletestframework@googlegroups.com.
-
-## Why should I use Google Test instead of my favorite C++ testing framework? ##
-
-First, let us say clearly that we don't want to get into the debate of
-which C++ testing framework is **the best**.  There exist many fine
-frameworks for writing C++ tests, and we have tremendous respect for
-the developers and users of them.  We don't think there is (or will
-be) a single best framework - you have to pick the right tool for the
-particular task you are tackling.
-
-We created Google Test because we couldn't find the right combination
-of features and conveniences in an existing framework to satisfy _our_
-needs.  The following is a list of things that _we_ like about Google
-Test.  We don't claim them to be unique to Google Test - rather, the
-combination of them makes Google Test the choice for us.  We hope this
-list can help you decide whether it is for you too.
-
-  * Google Test is designed to be portable: it doesn't require exceptions or RTTI; it works around various bugs in various compilers and environments; etc.  As a result, it works on Linux, Mac OS X, Windows and several embedded operating systems.
-  * Nonfatal assertions (`EXPECT_*`) have proven to be great time savers, as they allow a test to report multiple failures in a single edit-compile-test cycle.
-  * It's easy to write assertions that generate informative messages: you just use the stream syntax to append any additional information, e.g. `ASSERT_EQ(5, Foo(i)) << " where i = " << i;`.  It doesn't require a new set of macros or special functions.
-  * Google Test automatically detects your tests and doesn't require you to enumerate them in order to run them.
-  * Death tests are pretty handy for ensuring that your asserts in production code are triggered by the right conditions.
-  * `SCOPED_TRACE` helps you understand the context of an assertion failure when it comes from inside a sub-routine or loop.
-  * You can decide which tests to run using name patterns.  This saves time when you want to quickly reproduce a test failure.
-  * Google Test can generate XML test result reports that can be parsed by popular continuous build system like Hudson.
-  * Simple things are easy in Google Test, while hard things are possible: in addition to advanced features like [global test environments](AdvancedGuide.md#global-set-up-and-tear-down) and tests parameterized by [values](AdvancedGuide.md#value-parameterized-tests) or [types](docs/AdvancedGuide.md#typed-tests), Google Test supports various ways for the user to extend the framework -- if Google Test doesn't do something out of the box, chances are that a user can implement the feature using Google Test's public API, without changing Google Test itself.  In particular, you can:
-    * expand your testing vocabulary by defining [custom predicates](AdvancedGuide.md#predicate-assertions-for-better-error-messages),
-    * teach Google Test how to [print your types](AdvancedGuide.md#teaching-google-test-how-to-print-your-values),
-    * define your own testing macros or utilities and verify them using Google Test's [Service Provider Interface](AdvancedGuide.md#catching-failures), and
-    * reflect on the test cases or change the test output format by intercepting the [test events](AdvancedGuide.md#extending-google-test-by-handling-test-events).
-
-## I'm getting warnings when compiling Google Test.  Would you fix them? ##
-
-We strive to minimize compiler warnings Google Test generates.  Before releasing a new version, we test to make sure that it doesn't generate warnings when compiled using its CMake script on Windows, Linux, and Mac OS.
-
-Unfortunately, this doesn't mean you are guaranteed to see no warnings when compiling Google Test in your environment:
-
-  * You may be using a different compiler as we use, or a different version of the same compiler.  We cannot possibly test for all compilers.
-  * You may be compiling on a different platform as we do.
-  * Your project may be using different compiler flags as we do.
-
-It is not always possible to make Google Test warning-free for everyone.  Or, it may not be desirable if the warning is rarely enabled and fixing the violations makes the code more complex.
-
-If you see warnings when compiling Google Test, we suggest that you use the `-isystem` flag (assuming your are using GCC) to mark Google Test headers as system headers.  That'll suppress warnings from Google Test headers.
-
-## Why should not test case names and test names contain underscore? ##
-
-Underscore (`_`) is special, as C++ reserves the following to be used by
-the compiler and the standard library:
-
-  1. any identifier that starts with an `_` followed by an upper-case letter, and
-  1. any identifier that containers two consecutive underscores (i.e. `__`) _anywhere_ in its name.
-
-User code is _prohibited_ from using such identifiers.
-
-Now let's look at what this means for `TEST` and `TEST_F`.
-
-Currently `TEST(TestCaseName, TestName)` generates a class named
-`TestCaseName_TestName_Test`.  What happens if `TestCaseName` or `TestName`
-contains `_`?
-
-  1. If `TestCaseName` starts with an `_` followed by an upper-case letter (say, `_Foo`), we end up with `_Foo_TestName_Test`, which is reserved and thus invalid.
-  1. If `TestCaseName` ends with an `_` (say, `Foo_`), we get `Foo__TestName_Test`, which is invalid.
-  1. If `TestName` starts with an `_` (say, `_Bar`), we get `TestCaseName__Bar_Test`, which is invalid.
-  1. If `TestName` ends with an `_` (say, `Bar_`), we get `TestCaseName_Bar__Test`, which is invalid.
-
-So clearly `TestCaseName` and `TestName` cannot start or end with `_`
-(Actually, `TestCaseName` can start with `_` -- as long as the `_` isn't
-followed by an upper-case letter.  But that's getting complicated.  So
-for simplicity we just say that it cannot start with `_`.).
-
-It may seem fine for `TestCaseName` and `TestName` to contain `_` in the
-middle.  However, consider this:
-``` cpp
-TEST(Time, Flies_Like_An_Arrow) { ... }
-TEST(Time_Flies, Like_An_Arrow) { ... }
-```
-
-Now, the two `TEST`s will both generate the same class
-(`Time_Files_Like_An_Arrow_Test`).  That's not good.
-
-So for simplicity, we just ask the users to avoid `_` in `TestCaseName`
-and `TestName`.  The rule is more constraining than necessary, but it's
-simple and easy to remember.  It also gives Google Test some wiggle
-room in case its implementation needs to change in the future.
-
-If you violate the rule, there may not be immediately consequences,
-but your test may (just may) break with a new compiler (or a new
-version of the compiler you are using) or with a new version of Google
-Test.  Therefore it's best to follow the rule.
-
-## Why is it not recommended to install a pre-compiled copy of Google Test (for example, into /usr/local)? ##
-
-In the early days, we said that you could install
-compiled Google Test libraries on `*`nix systems using `make install`.
-Then every user of your machine can write tests without
-recompiling Google Test.
-
-This seemed like a good idea, but it has a
-got-cha: every user needs to compile his tests using the _same_ compiler
-flags used to compile the installed Google Test libraries; otherwise
-he may run into undefined behaviors (i.e. the tests can behave
-strangely and may even crash for no obvious reasons).
-
-Why?  Because C++ has this thing called the One-Definition Rule: if
-two C++ source files contain different definitions of the same
-class/function/variable, and you link them together, you violate the
-rule.  The linker may or may not catch the error (in many cases it's
-not required by the C++ standard to catch the violation).  If it
-doesn't, you get strange run-time behaviors that are unexpected and
-hard to debug.
-
-If you compile Google Test and your test code using different compiler
-flags, they may see different definitions of the same
-class/function/variable (e.g. due to the use of `#if` in Google Test).
-Therefore, for your sanity, we recommend to avoid installing pre-compiled
-Google Test libraries.  Instead, each project should compile
-Google Test itself such that it can be sure that the same flags are
-used for both Google Test and the tests.
-
-## How do I generate 64-bit binaries on Windows (using Visual Studio 2008)? ##
-
-(Answered by Trevor Robinson)
-
-Load the supplied Visual Studio solution file, either `msvc\gtest-md.sln` or
-`msvc\gtest.sln`. Go through the migration wizard to migrate the
-solution and project files to Visual Studio 2008. Select
-`Configuration Manager...` from the `Build` menu. Select `<New...>` from
-the `Active solution platform` dropdown.  Select `x64` from the new
-platform dropdown, leave `Copy settings from` set to `Win32` and
-`Create new project platforms` checked, then click `OK`. You now have
-`Win32` and `x64` platform configurations, selectable from the
-`Standard` toolbar, which allow you to toggle between building 32-bit or
-64-bit binaries (or both at once using Batch Build).
-
-In order to prevent build output files from overwriting one another,
-you'll need to change the `Intermediate Directory` settings for the
-newly created platform configuration across all the projects. To do
-this, multi-select (e.g. using shift-click) all projects (but not the
-solution) in the `Solution Explorer`. Right-click one of them and
-select `Properties`. In the left pane, select `Configuration Properties`,
-and from the `Configuration` dropdown, select `All Configurations`.
-Make sure the selected platform is `x64`. For the
-`Intermediate Directory` setting, change the value from
-`$(PlatformName)\$(ConfigurationName)` to
-`$(OutDir)\$(ProjectName)`. Click `OK` and then build the
-solution. When the build is complete, the 64-bit binaries will be in
-the `msvc\x64\Debug` directory.
-
-## Can I use Google Test on MinGW? ##
-
-We haven't tested this ourselves, but Per Abrahamsen reported that he
-was able to compile and install Google Test successfully when using
-MinGW from Cygwin.  You'll need to configure it with:
-
-`PATH/TO/configure CC="gcc -mno-cygwin" CXX="g++ -mno-cygwin"`
-
-You should be able to replace the `-mno-cygwin` option with direct links
-to the real MinGW binaries, but we haven't tried that.
-
-Caveats:
-
-  * There are many warnings when compiling.
-  * `make check` will produce some errors as not all tests for Google Test itself are compatible with MinGW.
-
-We also have reports on successful cross compilation of Google Test
-MinGW binaries on Linux using
-[these instructions](http://wiki.wxwidgets.org/Cross-Compiling_Under_Linux#Cross-compiling_under_Linux_for_MS_Windows)
-on the WxWidgets site.
-
-Please contact `googletestframework@googlegroups.com` if you are
-interested in improving the support for MinGW.
-
-## Why does Google Test support EXPECT\_EQ(NULL, ptr) and ASSERT\_EQ(NULL, ptr) but not EXPECT\_NE(NULL, ptr) and ASSERT\_NE(NULL, ptr)? ##
-
-Due to some peculiarity of C++, it requires some non-trivial template
-meta programming tricks to support using `NULL` as an argument of the
-`EXPECT_XX()` and `ASSERT_XX()` macros. Therefore we only do it where
-it's most needed (otherwise we make the implementation of Google Test
-harder to maintain and more error-prone than necessary).
-
-The `EXPECT_EQ()` macro takes the _expected_ value as its first
-argument and the _actual_ value as the second. It's reasonable that
-someone wants to write `EXPECT_EQ(NULL, some_expression)`, and this
-indeed was requested several times. Therefore we implemented it.
-
-The need for `EXPECT_NE(NULL, ptr)` isn't nearly as strong. When the
-assertion fails, you already know that `ptr` must be `NULL`, so it
-doesn't add any information to print ptr in this case. That means
-`EXPECT_TRUE(ptr != NULL)` works just as well.
-
-If we were to support `EXPECT_NE(NULL, ptr)`, for consistency we'll
-have to support `EXPECT_NE(ptr, NULL)` as well, as unlike `EXPECT_EQ`,
-we don't have a convention on the order of the two arguments for
-`EXPECT_NE`. This means using the template meta programming tricks
-twice in the implementation, making it even harder to understand and
-maintain. We believe the benefit doesn't justify the cost.
-
-Finally, with the growth of Google Mock's [matcher](../../googlemock/docs/CookBook.md#using-matchers-in-google-test-assertions) library, we are
-encouraging people to use the unified `EXPECT_THAT(value, matcher)`
-syntax more often in tests. One significant advantage of the matcher
-approach is that matchers can be easily combined to form new matchers,
-while the `EXPECT_NE`, etc, macros cannot be easily
-combined. Therefore we want to invest more in the matchers than in the
-`EXPECT_XX()` macros.
-
-## Does Google Test support running tests in parallel? ##
-
-Test runners tend to be tightly coupled with the build/test
-environment, and Google Test doesn't try to solve the problem of
-running tests in parallel.  Instead, we tried to make Google Test work
-nicely with test runners.  For example, Google Test's XML report
-contains the time spent on each test, and its `gtest_list_tests` and
-`gtest_filter` flags can be used for splitting the execution of test
-methods into multiple processes.  These functionalities can help the
-test runner run the tests in parallel.
-
-## Why don't Google Test run the tests in different threads to speed things up? ##
-
-It's difficult to write thread-safe code.  Most tests are not written
-with thread-safety in mind, and thus may not work correctly in a
-multi-threaded setting.
-
-If you think about it, it's already hard to make your code work when
-you know what other threads are doing.  It's much harder, and
-sometimes even impossible, to make your code work when you don't know
-what other threads are doing (remember that test methods can be added,
-deleted, or modified after your test was written).  If you want to run
-the tests in parallel, you'd better run them in different processes.
-
-## Why aren't Google Test assertions implemented using exceptions? ##
-
-Our original motivation was to be able to use Google Test in projects
-that disable exceptions.  Later we realized some additional benefits
-of this approach:
-
-  1. Throwing in a destructor is undefined behavior in C++.  Not using exceptions means Google Test's assertions are safe to use in destructors.
-  1. The `EXPECT_*` family of macros will continue even after a failure, allowing multiple failures in a `TEST` to be reported in a single run. This is a popular feature, as in C++ the edit-compile-test cycle is usually quite long and being able to fixing more than one thing at a time is a blessing.
-  1. If assertions are implemented using exceptions, a test may falsely ignore a failure if it's caught by user code:
-``` cpp
-try { ... ASSERT_TRUE(...) ... }
-catch (...) { ... }
-```
-The above code will pass even if the `ASSERT_TRUE` throws.  While it's unlikely for someone to write this in a test, it's possible to run into this pattern when you write assertions in callbacks that are called by the code under test.
-
-The downside of not using exceptions is that `ASSERT_*` (implemented
-using `return`) will only abort the current function, not the current
-`TEST`.
-
-## Why do we use two different macros for tests with and without fixtures? ##
-
-Unfortunately, C++'s macro system doesn't allow us to use the same
-macro for both cases.  One possibility is to provide only one macro
-for tests with fixtures, and require the user to define an empty
-fixture sometimes:
-
-``` cpp
-class FooTest : public ::testing::Test {};
-
-TEST_F(FooTest, DoesThis) { ... }
-```
-or
-``` cpp
-typedef ::testing::Test FooTest;
-
-TEST_F(FooTest, DoesThat) { ... }
-```
-
-Yet, many people think this is one line too many. :-) Our goal was to
-make it really easy to write tests, so we tried to make simple tests
-trivial to create.  That means using a separate macro for such tests.
-
-We think neither approach is ideal, yet either of them is reasonable.
-In the end, it probably doesn't matter much either way.
-
-## Why don't we use structs as test fixtures? ##
-
-We like to use structs only when representing passive data.  This
-distinction between structs and classes is good for documenting the
-intent of the code's author.  Since test fixtures have logic like
-`SetUp()` and `TearDown()`, they are better defined as classes.
-
-## Why are death tests implemented as assertions instead of using a test runner? ##
-
-Our goal was to make death tests as convenient for a user as C++
-possibly allows.  In particular:
-
-  * The runner-style requires to split the information into two pieces: the definition of the death test itself, and the specification for the runner on how to run the death test and what to expect.  The death test would be written in C++, while the runner spec may or may not be.  A user needs to carefully keep the two in sync. `ASSERT_DEATH(statement, expected_message)` specifies all necessary information in one place, in one language, without boilerplate code. It is very declarative.
-  * `ASSERT_DEATH` has a similar syntax and error-reporting semantics as other Google Test assertions, and thus is easy to learn.
-  * `ASSERT_DEATH` can be mixed with other assertions and other logic at your will.  You are not limited to one death test per test method. For example, you can write something like:
-``` cpp
-    if (FooCondition()) {
-      ASSERT_DEATH(Bar(), "blah");
-    } else {
-      ASSERT_EQ(5, Bar());
-    }
-```
-If you prefer one death test per test method, you can write your tests in that style too, but we don't want to impose that on the users.  The fewer artificial limitations the better.
-  * `ASSERT_DEATH` can reference local variables in the current function, and you can decide how many death tests you want based on run-time information.  For example,
-``` cpp
-    const int count = GetCount();  // Only known at run time.
-    for (int i = 1; i <= count; i++) {
-      ASSERT_DEATH({
-        double* buffer = new double[i];
-        ... initializes buffer ...
-        Foo(buffer, i)
-      }, "blah blah");
-    }
-```
-The runner-based approach tends to be more static and less flexible, or requires more user effort to get this kind of flexibility.
-
-Another interesting thing about `ASSERT_DEATH` is that it calls `fork()`
-to create a child process to run the death test.  This is lightening
-fast, as `fork()` uses copy-on-write pages and incurs almost zero
-overhead, and the child process starts from the user-supplied
-statement directly, skipping all global and local initialization and
-any code leading to the given statement.  If you launch the child
-process from scratch, it can take seconds just to load everything and
-start running if the test links to many libraries dynamically.
-
-## My death test modifies some state, but the change seems lost after the death test finishes. Why? ##
-
-Death tests (`EXPECT_DEATH`, etc) are executed in a sub-process s.t. the
-expected crash won't kill the test program (i.e. the parent process). As a
-result, any in-memory side effects they incur are observable in their
-respective sub-processes, but not in the parent process. You can think of them
-as running in a parallel universe, more or less.
-
-## The compiler complains about "undefined references" to some static const member variables, but I did define them in the class body. What's wrong? ##
-
-If your class has a static data member:
-
-``` cpp
-// foo.h
-class Foo {
-  ...
-  static const int kBar = 100;
-};
-```
-
-You also need to define it _outside_ of the class body in `foo.cc`:
-
-``` cpp
-const int Foo::kBar;  // No initializer here.
-```
-
-Otherwise your code is **invalid C++**, and may break in unexpected ways. In
-particular, using it in Google Test comparison assertions (`EXPECT_EQ`, etc)
-will generate an "undefined reference" linker error.
-
-## I have an interface that has several implementations. Can I write a set of tests once and repeat them over all the implementations? ##
-
-Google Test doesn't yet have good support for this kind of tests, or
-data-driven tests in general. We hope to be able to make improvements in this
-area soon.
-
-## Can I derive a test fixture from another? ##
-
-Yes.
-
-Each test fixture has a corresponding and same named test case. This means only
-one test case can use a particular fixture. Sometimes, however, multiple test
-cases may want to use the same or slightly different fixtures. For example, you
-may want to make sure that all of a GUI library's test cases don't leak
-important system resources like fonts and brushes.
-
-In Google Test, you share a fixture among test cases by putting the shared
-logic in a base test fixture, then deriving from that base a separate fixture
-for each test case that wants to use this common logic. You then use `TEST_F()`
-to write tests using each derived fixture.
-
-Typically, your code looks like this:
-
-``` cpp
-// Defines a base test fixture.
-class BaseTest : public ::testing::Test {
-  protected:
-   ...
-};
-
-// Derives a fixture FooTest from BaseTest.
-class FooTest : public BaseTest {
-  protected:
-    virtual void SetUp() {
-      BaseTest::SetUp();  // Sets up the base fixture first.
-      ... additional set-up work ...
-    }
-    virtual void TearDown() {
-      ... clean-up work for FooTest ...
-      BaseTest::TearDown();  // Remember to tear down the base fixture
-                             // after cleaning up FooTest!
-    }
-    ... functions and variables for FooTest ...
-};
-
-// Tests that use the fixture FooTest.
-TEST_F(FooTest, Bar) { ... }
-TEST_F(FooTest, Baz) { ... }
-
-... additional fixtures derived from BaseTest ...
-```
-
-If necessary, you can continue to derive test fixtures from a derived fixture.
-Google Test has no limit on how deep the hierarchy can be.
-
-For a complete example using derived test fixtures, see
-[sample5](../samples/sample5_unittest.cc).
-
-## My compiler complains "void value not ignored as it ought to be." What does this mean? ##
-
-You're probably using an `ASSERT_*()` in a function that doesn't return `void`.
-`ASSERT_*()` can only be used in `void` functions.
-
-## My death test hangs (or seg-faults). How do I fix it? ##
-
-In Google Test, death tests are run in a child process and the way they work is
-delicate. To write death tests you really need to understand how they work.
-Please make sure you have read this.
-
-In particular, death tests don't like having multiple threads in the parent
-process. So the first thing you can try is to eliminate creating threads
-outside of `EXPECT_DEATH()`.
-
-Sometimes this is impossible as some library you must use may be creating
-threads before `main()` is even reached. In this case, you can try to minimize
-the chance of conflicts by either moving as many activities as possible inside
-`EXPECT_DEATH()` (in the extreme case, you want to move everything inside), or
-leaving as few things as possible in it. Also, you can try to set the death
-test style to `"threadsafe"`, which is safer but slower, and see if it helps.
-
-If you go with thread-safe death tests, remember that they rerun the test
-program from the beginning in the child process. Therefore make sure your
-program can run side-by-side with itself and is deterministic.
-
-In the end, this boils down to good concurrent programming. You have to make
-sure that there is no race conditions or dead locks in your program. No silver
-bullet - sorry!
-
-## Should I use the constructor/destructor of the test fixture or the set-up/tear-down function? ##
-
-The first thing to remember is that Google Test does not reuse the
-same test fixture object across multiple tests. For each `TEST_F`,
-Google Test will create a fresh test fixture object, _immediately_
-call `SetUp()`, run the test body, call `TearDown()`, and then
-_immediately_ delete the test fixture object.
-
-When you need to write per-test set-up and tear-down logic, you have
-the choice between using the test fixture constructor/destructor or
-`SetUp()/TearDown()`. The former is usually preferred, as it has the
-following benefits:
-
-  * By initializing a member variable in the constructor, we have the option to make it `const`, which helps prevent accidental changes to its value and makes the tests more obviously correct.
-  * In case we need to subclass the test fixture class, the subclass' constructor is guaranteed to call the base class' constructor first, and the subclass' destructor is guaranteed to call the base class' destructor afterward. With `SetUp()/TearDown()`, a subclass may make the mistake of forgetting to call the base class' `SetUp()/TearDown()` or call them at the wrong moment.
-
-You may still want to use `SetUp()/TearDown()` in the following rare cases:
-  * If the tear-down operation could throw an exception, you must use `TearDown()` as opposed to the destructor, as throwing in a destructor leads to undefined behavior and usually will kill your program right away. Note that many standard libraries (like STL) may throw when exceptions are enabled in the compiler. Therefore you should prefer `TearDown()` if you want to write portable tests that work with or without exceptions.
-  * The assertion macros throw an exception when flag `--gtest_throw_on_failure` is specified. Therefore, you shouldn't use Google Test assertions in a destructor if you plan to run your tests with this flag.
-  * In a constructor or destructor, you cannot make a virtual function call on this object. (You can call a method declared as virtual, but it will be statically bound.) Therefore, if you need to call a method that will be overriden in a derived class, you have to use `SetUp()/TearDown()`.
-
-## The compiler complains "no matching function to call" when I use ASSERT\_PREDn. How do I fix it? ##
-
-If the predicate function you use in `ASSERT_PRED*` or `EXPECT_PRED*` is
-overloaded or a template, the compiler will have trouble figuring out which
-overloaded version it should use. `ASSERT_PRED_FORMAT*` and
-`EXPECT_PRED_FORMAT*` don't have this problem.
-
-If you see this error, you might want to switch to
-`(ASSERT|EXPECT)_PRED_FORMAT*`, which will also give you a better failure
-message. If, however, that is not an option, you can resolve the problem by
-explicitly telling the compiler which version to pick.
-
-For example, suppose you have
-
-``` cpp
-bool IsPositive(int n) {
-  return n > 0;
-}
-bool IsPositive(double x) {
-  return x > 0;
-}
-```
-
-you will get a compiler error if you write
-
-``` cpp
-EXPECT_PRED1(IsPositive, 5);
-```
-
-However, this will work:
-
-``` cpp
-EXPECT_PRED1(*static_cast<bool (*)(int)>*(IsPositive), 5);
-```
-
-(The stuff inside the angled brackets for the `static_cast` operator is the
-type of the function pointer for the `int`-version of `IsPositive()`.)
-
-As another example, when you have a template function
-
-``` cpp
-template <typename T>
-bool IsNegative(T x) {
-  return x < 0;
-}
-```
-
-you can use it in a predicate assertion like this:
-
-``` cpp
-ASSERT_PRED1(IsNegative*<int>*, -5);
-```
-
-Things are more interesting if your template has more than one parameters. The
-following won't compile:
-
-``` cpp
-ASSERT_PRED2(*GreaterThan<int, int>*, 5, 0);
-```
-
-
-as the C++ pre-processor thinks you are giving `ASSERT_PRED2` 4 arguments,
-which is one more than expected. The workaround is to wrap the predicate
-function in parentheses:
-
-``` cpp
-ASSERT_PRED2(*(GreaterThan<int, int>)*, 5, 0);
-```
-
-
-## My compiler complains about "ignoring return value" when I call RUN\_ALL\_TESTS(). Why? ##
-
-Some people had been ignoring the return value of `RUN_ALL_TESTS()`. That is,
-instead of
-
-``` cpp
-return RUN_ALL_TESTS();
-```
-
-they write
-
-``` cpp
-RUN_ALL_TESTS();
-```
-
-This is wrong and dangerous. A test runner needs to see the return value of
-`RUN_ALL_TESTS()` in order to determine if a test has passed. If your `main()`
-function ignores it, your test will be considered successful even if it has a
-Google Test assertion failure. Very bad.
-
-To help the users avoid this dangerous bug, the implementation of
-`RUN_ALL_TESTS()` causes gcc to raise this warning, when the return value is
-ignored. If you see this warning, the fix is simple: just make sure its value
-is used as the return value of `main()`.
-
-## My compiler complains that a constructor (or destructor) cannot return a value. What's going on? ##
-
-Due to a peculiarity of C++, in order to support the syntax for streaming
-messages to an `ASSERT_*`, e.g.
-
-``` cpp
-ASSERT_EQ(1, Foo()) << "blah blah" << foo;
-```
-
-we had to give up using `ASSERT*` and `FAIL*` (but not `EXPECT*` and
-`ADD_FAILURE*`) in constructors and destructors. The workaround is to move the
-content of your constructor/destructor to a private void member function, or
-switch to `EXPECT_*()` if that works. This section in the user's guide explains
-it.
-
-## My set-up function is not called. Why? ##
-
-C++ is case-sensitive. It should be spelled as `SetUp()`.  Did you
-spell it as `Setup()`?
-
-Similarly, sometimes people spell `SetUpTestCase()` as `SetupTestCase()` and
-wonder why it's never called.
-
-## How do I jump to the line of a failure in Emacs directly? ##
-
-Google Test's failure message format is understood by Emacs and many other
-IDEs, like acme and XCode. If a Google Test message is in a compilation buffer
-in Emacs, then it's clickable. You can now hit `enter` on a message to jump to
-the corresponding source code, or use `C-x `` to jump to the next failure.
-
-## I have several test cases which share the same test fixture logic, do I have to define a new test fixture class for each of them? This seems pretty tedious. ##
-
-You don't have to. Instead of
-
-``` cpp
-class FooTest : public BaseTest {};
-
-TEST_F(FooTest, Abc) { ... }
-TEST_F(FooTest, Def) { ... }
-
-class BarTest : public BaseTest {};
-
-TEST_F(BarTest, Abc) { ... }
-TEST_F(BarTest, Def) { ... }
-```
-
-you can simply `typedef` the test fixtures:
-``` cpp
-typedef BaseTest FooTest;
-
-TEST_F(FooTest, Abc) { ... }
-TEST_F(FooTest, Def) { ... }
-
-typedef BaseTest BarTest;
-
-TEST_F(BarTest, Abc) { ... }
-TEST_F(BarTest, Def) { ... }
-```
-
-## The Google Test output is buried in a whole bunch of log messages. What do I do? ##
-
-The Google Test output is meant to be a concise and human-friendly report. If
-your test generates textual output itself, it will mix with the Google Test
-output, making it hard to read. However, there is an easy solution to this
-problem.
-
-Since most log messages go to stderr, we decided to let Google Test output go
-to stdout. This way, you can easily separate the two using redirection. For
-example:
-```
-./my_test > googletest_output.txt
-```
-
-## Why should I prefer test fixtures over global variables? ##
-
-There are several good reasons:
-  1. It's likely your test needs to change the states of its global variables. This makes it difficult to keep side effects from escaping one test and contaminating others, making debugging difficult. By using fixtures, each test has a fresh set of variables that's different (but with the same names). Thus, tests are kept independent of each other.
-  1. Global variables pollute the global namespace.
-  1. Test fixtures can be reused via subclassing, which cannot be done easily with global variables. This is useful if many test cases have something in common.
-
-## How do I test private class members without writing FRIEND\_TEST()s? ##
-
-You should try to write testable code, which means classes should be easily
-tested from their public interface. One way to achieve this is the Pimpl idiom:
-you move all private members of a class into a helper class, and make all
-members of the helper class public.
-
-You have several other options that don't require using `FRIEND_TEST`:
-  * Write the tests as members of the fixture class:
-``` cpp
-class Foo {
-  friend class FooTest;
-  ...
-};
-
-class FooTest : public ::testing::Test {
- protected:
-  ...
-  void Test1() {...} // This accesses private members of class Foo.
-  void Test2() {...} // So does this one.
-};
-
-TEST_F(FooTest, Test1) {
-  Test1();
-}
-
-TEST_F(FooTest, Test2) {
-  Test2();
-}
-```
-  * In the fixture class, write accessors for the tested class' private members, then use the accessors in your tests:
-``` cpp
-class Foo {
-  friend class FooTest;
-  ...
-};
-
-class FooTest : public ::testing::Test {
- protected:
-  ...
-  T1 get_private_member1(Foo* obj) {
-    return obj->private_member1_;
-  }
-};
-
-TEST_F(FooTest, Test1) {
-  ...
-  get_private_member1(x)
-  ...
-}
-```
-  * If the methods are declared **protected**, you can change their access level in a test-only subclass:
-``` cpp
-class YourClass {
-  ...
- protected: // protected access for testability.
-  int DoSomethingReturningInt();
-  ...
-};
-
-// in the your_class_test.cc file:
-class TestableYourClass : public YourClass {
-  ...
- public: using YourClass::DoSomethingReturningInt; // changes access rights
-  ...
-};
-
-TEST_F(YourClassTest, DoSomethingTest) {
-  TestableYourClass obj;
-  assertEquals(expected_value, obj.DoSomethingReturningInt());
-}
-```
-
-## How do I test private class static members without writing FRIEND\_TEST()s? ##
-
-We find private static methods clutter the header file.  They are
-implementation details and ideally should be kept out of a .h. So often I make
-them free functions instead.
-
-Instead of:
-``` cpp
-// foo.h
-class Foo {
-  ...
- private:
-  static bool Func(int n);
-};
-
-// foo.cc
-bool Foo::Func(int n) { ... }
-
-// foo_test.cc
-EXPECT_TRUE(Foo::Func(12345));
-```
-
-You probably should better write:
-``` cpp
-// foo.h
-class Foo {
-  ...
-};
-
-// foo.cc
-namespace internal {
-  bool Func(int n) { ... }
-}
-
-// foo_test.cc
-namespace internal {
-  bool Func(int n);
-}
-
-EXPECT_TRUE(internal::Func(12345));
-```
-
-## I would like to run a test several times with different parameters. Do I need to write several similar copies of it? ##
-
-No. You can use a feature called [value-parameterized tests](AdvancedGuide.md#Value_Parameterized_Tests) which
-lets you repeat your tests with different parameters, without defining it more than once.
-
-## How do I test a file that defines main()? ##
-
-To test a `foo.cc` file, you need to compile and link it into your unit test
-program. However, when the file contains a definition for the `main()`
-function, it will clash with the `main()` of your unit test, and will result in
-a build error.
-
-The right solution is to split it into three files:
-  1. `foo.h` which contains the declarations,
-  1. `foo.cc` which contains the definitions except `main()`, and
-  1. `foo_main.cc` which contains nothing but the definition of `main()`.
-
-Then `foo.cc` can be easily tested.
-
-If you are adding tests to an existing file and don't want an intrusive change
-like this, there is a hack: just include the entire `foo.cc` file in your unit
-test. For example:
-``` cpp
-// File foo_unittest.cc
-
-// The headers section
-...
-
-// Renames main() in foo.cc to make room for the unit test main()
-#define main FooMain
-
-#include "a/b/foo.cc"
-
-// The tests start here.
-...
-```
-
-
-However, please remember this is a hack and should only be used as the last
-resort.
-
-## What can the statement argument in ASSERT\_DEATH() be? ##
-
-`ASSERT_DEATH(_statement_, _regex_)` (or any death assertion macro) can be used
-wherever `_statement_` is valid. So basically `_statement_` can be any C++
-statement that makes sense in the current context. In particular, it can
-reference global and/or local variables, and can be:
-  * a simple function call (often the case),
-  * a complex expression, or
-  * a compound statement.
-
-Some examples are shown here:
-
-``` cpp
-// A death test can be a simple function call.
-TEST(MyDeathTest, FunctionCall) {
-  ASSERT_DEATH(Xyz(5), "Xyz failed");
-}
-
-// Or a complex expression that references variables and functions.
-TEST(MyDeathTest, ComplexExpression) {
-  const bool c = Condition();
-  ASSERT_DEATH((c ? Func1(0) : object2.Method("test")),
-               "(Func1|Method) failed");
-}
-
-// Death assertions can be used any where in a function. In
-// particular, they can be inside a loop.
-TEST(MyDeathTest, InsideLoop) {
-  // Verifies that Foo(0), Foo(1), ..., and Foo(4) all die.
-  for (int i = 0; i < 5; i++) {
-    EXPECT_DEATH_M(Foo(i), "Foo has \\d+ errors",
-                   ::testing::Message() << "where i is " << i);
-  }
-}
-
-// A death assertion can contain a compound statement.
-TEST(MyDeathTest, CompoundStatement) {
-  // Verifies that at lease one of Bar(0), Bar(1), ..., and
-  // Bar(4) dies.
-  ASSERT_DEATH({
-    for (int i = 0; i < 5; i++) {
-      Bar(i);
-    }
-  },
-  "Bar has \\d+ errors");}
-```
-
-`googletest_unittest.cc` contains more examples if you are interested.
-
-## What syntax does the regular expression in ASSERT\_DEATH use? ##
-
-On POSIX systems, Google Test uses the POSIX Extended regular
-expression syntax
-(http://en.wikipedia.org/wiki/Regular_expression#POSIX_Extended_Regular_Expressions).
-On Windows, it uses a limited variant of regular expression
-syntax. For more details, see the
-[regular expression syntax](AdvancedGuide.md#Regular_Expression_Syntax).
-
-## I have a fixture class Foo, but TEST\_F(Foo, Bar) gives me error "no matching function for call to Foo::Foo()". Why? ##
-
-Google Test needs to be able to create objects of your test fixture class, so
-it must have a default constructor. Normally the compiler will define one for
-you. However, there are cases where you have to define your own:
-  * If you explicitly declare a non-default constructor for class `Foo`, then you need to define a default constructor, even if it would be empty.
-  * If `Foo` has a const non-static data member, then you have to define the default constructor _and_ initialize the const member in the initializer list of the constructor. (Early versions of `gcc` doesn't force you to initialize the const member. It's a bug that has been fixed in `gcc 4`.)
-
-## Why does ASSERT\_DEATH complain about previous threads that were already joined? ##
-
-With the Linux pthread library, there is no turning back once you cross the
-line from single thread to multiple threads. The first time you create a
-thread, a manager thread is created in addition, so you get 3, not 2, threads.
-Later when the thread you create joins the main thread, the thread count
-decrements by 1, but the manager thread will never be killed, so you still have
-2 threads, which means you cannot safely run a death test.
-
-The new NPTL thread library doesn't suffer from this problem, as it doesn't
-create a manager thread. However, if you don't control which machine your test
-runs on, you shouldn't depend on this.
-
-## Why does Google Test require the entire test case, instead of individual tests, to be named FOODeathTest when it uses ASSERT\_DEATH? ##
-
-Google Test does not interleave tests from different test cases. That is, it
-runs all tests in one test case first, and then runs all tests in the next test
-case, and so on. Google Test does this because it needs to set up a test case
-before the first test in it is run, and tear it down afterwords. Splitting up
-the test case would require multiple set-up and tear-down processes, which is
-inefficient and makes the semantics unclean.
-
-If we were to determine the order of tests based on test name instead of test
-case name, then we would have a problem with the following situation:
-
-``` cpp
-TEST_F(FooTest, AbcDeathTest) { ... }
-TEST_F(FooTest, Uvw) { ... }
-
-TEST_F(BarTest, DefDeathTest) { ... }
-TEST_F(BarTest, Xyz) { ... }
-```
-
-Since `FooTest.AbcDeathTest` needs to run before `BarTest.Xyz`, and we don't
-interleave tests from different test cases, we need to run all tests in the
-`FooTest` case before running any test in the `BarTest` case. This contradicts
-with the requirement to run `BarTest.DefDeathTest` before `FooTest.Uvw`.
-
-## But I don't like calling my entire test case FOODeathTest when it contains both death tests and non-death tests. What do I do? ##
-
-You don't have to, but if you like, you may split up the test case into
-`FooTest` and `FooDeathTest`, where the names make it clear that they are
-related:
-
-``` cpp
-class FooTest : public ::testing::Test { ... };
-
-TEST_F(FooTest, Abc) { ... }
-TEST_F(FooTest, Def) { ... }
-
-typedef FooTest FooDeathTest;
-
-TEST_F(FooDeathTest, Uvw) { ... EXPECT_DEATH(...) ... }
-TEST_F(FooDeathTest, Xyz) { ... ASSERT_DEATH(...) ... }
-```
-
-## The compiler complains about "no match for 'operator<<'" when I use an assertion. What gives? ##
-
-If you use a user-defined type `FooType` in an assertion, you must make sure
-there is an `std::ostream& operator<<(std::ostream&, const FooType&)` function
-defined such that we can print a value of `FooType`.
-
-In addition, if `FooType` is declared in a name space, the `<<` operator also
-needs to be defined in the _same_ name space.
-
-## How do I suppress the memory leak messages on Windows? ##
-
-Since the statically initialized Google Test singleton requires allocations on
-the heap, the Visual C++ memory leak detector will report memory leaks at the
-end of the program run. The easiest way to avoid this is to use the
-`_CrtMemCheckpoint` and `_CrtMemDumpAllObjectsSince` calls to not report any
-statically initialized heap objects. See MSDN for more details and additional
-heap check/debug routines.
-
-## I am building my project with Google Test in Visual Studio and all I'm getting is a bunch of linker errors (or warnings). Help! ##
-
-You may get a number of the following linker error or warnings if you
-attempt to link your test project with the Google Test library when
-your project and the are not built using the same compiler settings.
-
-  * LNK2005: symbol already defined in object
-  * LNK4217: locally defined symbol 'symbol' imported in function 'function'
-  * LNK4049: locally defined symbol 'symbol' imported
-
-The Google Test project (gtest.vcproj) has the Runtime Library option
-set to /MT (use multi-threaded static libraries, /MTd for debug). If
-your project uses something else, for example /MD (use multi-threaded
-DLLs, /MDd for debug), you need to change the setting in the Google
-Test project to match your project's.
-
-To update this setting open the project properties in the Visual
-Studio IDE then select the branch Configuration Properties | C/C++ |
-Code Generation and change the option "Runtime Library".  You may also try
-using gtest-md.vcproj instead of gtest.vcproj.
-
-## I put my tests in a library and Google Test doesn't run them. What's happening? ##
-Have you read a
-[warning](Primer.md#important-note-for-visual-c-users) on
-the Google Test Primer page?
-
-## I want to use Google Test with Visual Studio but don't know where to start. ##
-Many people are in your position and one of the posted his solution to
-our mailing list.
-
-## I am seeing compile errors mentioning std::type\_traits when I try to use Google Test on Solaris. ##
-Google Test uses parts of the standard C++ library that SunStudio does not support.
-Our users reported success using alternative implementations. Try running the build after runing this commad:
-
-`export CC=cc CXX=CC CXXFLAGS='-library=stlport4'`
-
-## How can my code detect if it is running in a test? ##
-
-If you write code that sniffs whether it's running in a test and does
-different things accordingly, you are leaking test-only logic into
-production code and there is no easy way to ensure that the test-only
-code paths aren't run by mistake in production.  Such cleverness also
-leads to
-[Heisenbugs](http://en.wikipedia.org/wiki/Unusual_software_bug#Heisenbug).
-Therefore we strongly advise against the practice, and Google Test doesn't
-provide a way to do it.
-
-In general, the recommended way to cause the code to behave
-differently under test is [dependency injection](http://jamesshore.com/Blog/Dependency-Injection-Demystified.html).
-You can inject different functionality from the test and from the
-production code.  Since your production code doesn't link in the
-for-test logic at all, there is no danger in accidentally running it.
-
-However, if you _really_, _really_, _really_ have no choice, and if
-you follow the rule of ending your test program names with `_test`,
-you can use the _horrible_ hack of sniffing your executable name
-(`argv[0]` in `main()`) to know whether the code is under test.
-
-## Google Test defines a macro that clashes with one defined by another library. How do I deal with that? ##
-
-In C++, macros don't obey namespaces.  Therefore two libraries that
-both define a macro of the same name will clash if you `#include` both
-definitions.  In case a Google Test macro clashes with another
-library, you can force Google Test to rename its macro to avoid the
-conflict.
-
-Specifically, if both Google Test and some other code define macro
-`FOO`, you can add
-```
-  -DGTEST_DONT_DEFINE_FOO=1
-```
-to the compiler flags to tell Google Test to change the macro's name
-from `FOO` to `GTEST_FOO`. For example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll need to write
-``` cpp
-  GTEST_TEST(SomeTest, DoesThis) { ... }
-```
-instead of
-``` cpp
-  TEST(SomeTest, DoesThis) { ... }
-```
-in order to define a test.
-
-Currently, the following `TEST`, `FAIL`, `SUCCEED`, and the basic comparison assertion macros can have alternative names. You can see the full list of covered macros [here](http://www.google.com/codesearch?q=if+!GTEST_DONT_DEFINE_\w%2B+package:http://googletest\.googlecode\.com+file:/include/gtest/gtest.h). More information can be found in the "Avoiding Macro Name Clashes" section of the README file.
-
-
-## Is it OK if I have two separate `TEST(Foo, Bar)` test methods defined in different namespaces? ##
-
-Yes.
-
-The rule is **all test methods in the same test case must use the same fixture class**. This means that the following is **allowed** because both tests use the same fixture class (`::testing::Test`).
-
-``` cpp
-namespace foo {
-TEST(CoolTest, DoSomething) {
-  SUCCEED();
-}
-}  // namespace foo
-
-namespace bar {
-TEST(CoolTest, DoSomething) {
-  SUCCEED();
-}
-}  // namespace foo
-```
-
-However, the following code is **not allowed** and will produce a runtime error from Google Test because the test methods are using different test fixture classes with the same test case name.
-
-``` cpp
-namespace foo {
-class CoolTest : public ::testing::Test {};  // Fixture foo::CoolTest
-TEST_F(CoolTest, DoSomething) {
-  SUCCEED();
-}
-}  // namespace foo
-
-namespace bar {
-class CoolTest : public ::testing::Test {};  // Fixture: bar::CoolTest
-TEST_F(CoolTest, DoSomething) {
-  SUCCEED();
-}
-}  // namespace foo
-```
-
-## How do I build Google Testing Framework with Xcode 4? ##
-
-If you try to build Google Test's Xcode project with Xcode 4.0 or later, you may encounter an error message that looks like
-"Missing SDK in target gtest\_framework: /Developer/SDKs/MacOSX10.4u.sdk". That means that Xcode does not support the SDK the project is targeting. See the Xcode section in the [README](../README.md) file on how to resolve this.
-
-## My question is not covered in your FAQ! ##
-
-If you cannot find the answer to your question in this FAQ, there are
-some other resources you can use:
-
-  1. read other [wiki pages](../docs),
-  1. search the mailing list [archive](https://groups.google.com/forum/#!forum/googletestframework),
-  1. ask it on [googletestframework@googlegroups.com](mailto:googletestframework@googlegroups.com) and someone will answer it (to prevent spam, we require you to join the [discussion group](http://groups.google.com/group/googletestframework) before you can post.).
-
-Please note that creating an issue in the
-[issue tracker](https://github.com/google/googletest/issues) is _not_
-a good way to get your answer, as it is monitored infrequently by a
-very small number of people.
-
-When asking a question, it's helpful to provide as much of the
-following information as possible (people cannot help you if there's
-not enough information in your question):
-
-  * the version (or the commit hash if you check out from Git directly) of Google Test you use (Google Test is under active development, so it's possible that your problem has been solved in a later version),
-  * your operating system,
-  * the name and version of your compiler,
-  * the complete command line flags you give to your compiler,
-  * the complete compiler error messages (if the question is about compilation),
-  * the _actual_ code (ideally, a minimal but complete program) that has the problem you encounter.
diff --git a/src/external/googletest/googletest/docs/Pkgconfig.md b/src/external/googletest/googletest/docs/Pkgconfig.md
new file mode 100644 (file)
index 0000000..9761289
--- /dev/null
@@ -0,0 +1,146 @@
+## Using GoogleTest from various build systems ##
+
+GoogleTest comes with pkg-config files that can be used to determine all
+necessary flags for compiling and linking to GoogleTest (and GoogleMock).
+Pkg-config is a standardised plain-text format containing
+
+  * the includedir (-I) path
+  * necessary macro (-D) definitions
+  * further required flags (-pthread)
+  * the library (-L) path
+  * the library (-l) to link to
+
+All current build systems support pkg-config in one way or another. For
+all examples here we assume you want to compile the sample
+`samples/sample3_unittest.cc`.
+
+
+### CMake ###
+
+Using `pkg-config` in CMake is fairly easy:
+
+```
+cmake_minimum_required(VERSION 3.0)
+
+cmake_policy(SET CMP0048 NEW)
+project(my_gtest_pkgconfig VERSION 0.0.1 LANGUAGES CXX)
+
+find_package(PkgConfig)
+pkg_search_module(GTEST REQUIRED gtest_main)
+
+add_executable(testapp samples/sample3_unittest.cc)
+target_link_libraries(testapp ${GTEST_LDFLAGS})
+target_compile_options(testapp PUBLIC ${GTEST_CFLAGS})
+
+include(CTest)
+add_test(first_and_only_test testapp)
+```
+
+It is generally recommended that you use `target_compile_options` + `_CFLAGS`
+over `target_include_directories` + `_INCLUDE_DIRS` as the former includes not
+just -I flags (GoogleTest might require a macro indicating to internal headers
+that all libraries have been compiled with threading enabled. In addition,
+GoogleTest might also require `-pthread` in the compiling step, and as such
+splitting the pkg-config `Cflags` variable into include dirs and macros for
+`target_compile_definitions()` might still miss this). The same recommendation
+goes for using `_LDFLAGS` over the more commonplace `_LIBRARIES`, which
+happens to discard `-L` flags and `-pthread`.
+
+
+### Autotools ###
+
+Finding GoogleTest in Autoconf and using it from Automake is also fairly easy:
+
+In your `configure.ac`:
+
+```
+AC_PREREQ([2.69])
+AC_INIT([my_gtest_pkgconfig], [0.0.1])
+AC_CONFIG_SRCDIR([samples/sample3_unittest.cc])
+AC_PROG_CXX
+
+PKG_CHECK_MODULES([GTEST], [gtest_main])
+
+AM_INIT_AUTOMAKE([foreign subdir-objects])
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+```
+
+and in your `Makefile.am`:
+
+```
+check_PROGRAMS = testapp
+TESTS = $(check_PROGRAMS)
+
+testapp_SOURCES = samples/sample3_unittest.cc
+testapp_CXXFLAGS = $(GTEST_CFLAGS)
+testapp_LDADD = $(GTEST_LIBS)
+```
+
+
+### Meson ###
+
+Meson natively uses pkgconfig to query dependencies:
+
+```
+project('my_gtest_pkgconfig', 'cpp', version : '0.0.1')
+
+gtest_dep = dependency('gtest_main')
+
+testapp = executable(
+  'testapp',
+  files(['samples/sample3_unittest.cc']),
+  dependencies : gtest_dep,
+  install : false)
+
+test('first_and_only_test', testapp)
+```
+
+
+### Plain Makefiles ###
+
+Since `pkg-config` is a small Unix command-line utility, it can be used
+in handwritten `Makefile`s too:
+
+```
+GTEST_CFLAGS = `pkg-config --cflags gtest_main`
+GTEST_LIBS = `pkg-config --libs gtest_main`
+
+.PHONY: tests all
+
+tests: all
+       ./testapp
+
+all: testapp
+
+testapp: testapp.o
+       $(CXX) $(CXXFLAGS) $(LDFLAGS) $< -o $@ $(GTEST_LIBS)
+
+testapp.o: samples/sample3_unittest.cc
+       $(CXX) $(CPPFLAGS) $(CXXFLAGS) $< -c -o $@ $(GTEST_CFLAGS)
+```
+
+
+### Help! pkg-config can't find GoogleTest! ###
+
+Let's say you have a `CMakeLists.txt` along the lines of the one in this
+tutorial and you try to run `cmake`. It is very possible that you get a
+failure along the lines of:
+
+```
+-- Checking for one of the modules 'gtest_main'
+CMake Error at /usr/share/cmake/Modules/FindPkgConfig.cmake:640 (message):
+  None of the required 'gtest_main' found
+```
+
+These failures are common if you installed GoogleTest yourself and have not
+sourced it from a distro or other package manager. If so, you need to tell
+pkg-config where it can find the `.pc` files containing the information.
+Say you installed GoogleTest to `/usr/local`, then it might be that the
+`.pc` files are installed under `/usr/local/lib64/pkgconfig`. If you set
+
+```
+export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig
+```
+
+pkg-config will also try to look in `PKG_CONFIG_PATH` to find `gtest_main.pc`.
diff --git a/src/external/googletest/googletest/docs/Primer.md b/src/external/googletest/googletest/docs/Primer.md
deleted file mode 100644 (file)
index 474c1d2..0000000
+++ /dev/null
@@ -1,502 +0,0 @@
-
-
-# Introduction: Why Google C++ Testing Framework? #
-
-_Google C++ Testing Framework_ helps you write better C++ tests.
-
-No matter whether you work on Linux, Windows, or a Mac, if you write C++ code,
-Google Test can help you.
-
-So what makes a good test, and how does Google C++ Testing Framework fit in? We believe:
-  1. Tests should be _independent_ and _repeatable_. It's a pain to debug a test that succeeds or fails as a result of other tests.  Google C++ Testing Framework isolates the tests by running each of them on a different object. When a test fails, Google C++ Testing Framework allows you to run it in isolation for quick debugging.
-  1. Tests should be well _organized_ and reflect the structure of the tested code.  Google C++ Testing Framework groups related tests into test cases that can share data and subroutines. This common pattern is easy to recognize and makes tests easy to maintain. Such consistency is especially helpful when people switch projects and start to work on a new code base.
-  1. Tests should be _portable_ and _reusable_. The open-source community has a lot of code that is platform-neutral, its tests should also be platform-neutral.  Google C++ Testing Framework works on different OSes, with different compilers (gcc, MSVC, and others), with or without exceptions, so Google C++ Testing Framework tests can easily work with a variety of configurations.  (Note that the current release only contains build scripts for Linux - we are actively working on scripts for other platforms.)
-  1. When tests fail, they should provide as much _information_ about the problem as possible. Google C++ Testing Framework doesn't stop at the first test failure. Instead, it only stops the current test and continues with the next. You can also set up tests that report non-fatal failures after which the current test continues. Thus, you can detect and fix multiple bugs in a single run-edit-compile cycle.
-  1. The testing framework should liberate test writers from housekeeping chores and let them focus on the test _content_.  Google C++ Testing Framework automatically keeps track of all tests defined, and doesn't require the user to enumerate them in order to run them.
-  1. Tests should be _fast_. With Google C++ Testing Framework, you can reuse shared resources across tests and pay for the set-up/tear-down only once, without making tests depend on each other.
-
-Since Google C++ Testing Framework is based on the popular xUnit
-architecture, you'll feel right at home if you've used JUnit or PyUnit before.
-If not, it will take you about 10 minutes to learn the basics and get started.
-So let's go!
-
-_Note:_ We sometimes refer to Google C++ Testing Framework informally
-as _Google Test_.
-
-# Setting up a New Test Project #
-
-To write a test program using Google Test, you need to compile Google
-Test into a library and link your test with it.  We provide build
-files for some popular build systems: `msvc/` for Visual Studio,
-`xcode/` for Mac Xcode, `make/` for GNU make, `codegear/` for Borland
-C++ Builder, and the autotools script (deprecated) and
-`CMakeLists.txt` for CMake (recommended) in the Google Test root
-directory.  If your build system is not on this list, you can take a
-look at `make/Makefile` to learn how Google Test should be compiled
-(basically you want to compile `src/gtest-all.cc` with `GTEST_ROOT`
-and `GTEST_ROOT/include` in the header search path, where `GTEST_ROOT`
-is the Google Test root directory).
-
-Once you are able to compile the Google Test library, you should
-create a project or build target for your test program.  Make sure you
-have `GTEST_ROOT/include` in the header search path so that the
-compiler can find `"gtest/gtest.h"` when compiling your test.  Set up
-your test project to link with the Google Test library (for example,
-in Visual Studio, this is done by adding a dependency on
-`gtest.vcproj`).
-
-If you still have questions, take a look at how Google Test's own
-tests are built and use them as examples.
-
-# Basic Concepts #
-
-When using Google Test, you start by writing _assertions_, which are statements
-that check whether a condition is true. An assertion's result can be _success_,
-_nonfatal failure_, or _fatal failure_. If a fatal failure occurs, it aborts
-the current function; otherwise the program continues normally.
-
-_Tests_ use assertions to verify the tested code's behavior. If a test crashes
-or has a failed assertion, then it _fails_; otherwise it _succeeds_.
-
-A _test case_ contains one or many tests. You should group your tests into test
-cases that reflect the structure of the tested code. When multiple tests in a
-test case need to share common objects and subroutines, you can put them into a
-_test fixture_ class.
-
-A _test program_ can contain multiple test cases.
-
-We'll now explain how to write a test program, starting at the individual
-assertion level and building up to tests and test cases.
-
-# Assertions #
-
-Google Test assertions are macros that resemble function calls. You test a
-class or function by making assertions about its behavior. When an assertion
-fails, Google Test prints the assertion's source file and line number location,
-along with a failure message. You may also supply a custom failure message
-which will be appended to Google Test's message.
-
-The assertions come in pairs that test the same thing but have different
-effects on the current function. `ASSERT_*` versions generate fatal failures
-when they fail, and **abort the current function**. `EXPECT_*` versions generate
-nonfatal failures, which don't abort the current function. Usually `EXPECT_*`
-are preferred, as they allow more than one failures to be reported in a test.
-However, you should use `ASSERT_*` if it doesn't make sense to continue when
-the assertion in question fails.
-
-Since a failed `ASSERT_*` returns from the current function immediately,
-possibly skipping clean-up code that comes after it, it may cause a space leak.
-Depending on the nature of the leak, it may or may not be worth fixing - so
-keep this in mind if you get a heap checker error in addition to assertion
-errors.
-
-To provide a custom failure message, simply stream it into the macro using the
-`<<` operator, or a sequence of such operators. An example:
-```
-ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
-
-for (int i = 0; i < x.size(); ++i) {
-  EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
-}
-```
-
-Anything that can be streamed to an `ostream` can be streamed to an assertion
-macro--in particular, C strings and `string` objects. If a wide string
-(`wchar_t*`, `TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is
-streamed to an assertion, it will be translated to UTF-8 when printed.
-
-## Basic Assertions ##
-
-These assertions do basic true/false condition testing.
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-| `ASSERT_TRUE(`_condition_`)`;  | `EXPECT_TRUE(`_condition_`)`;   | _condition_ is true |
-| `ASSERT_FALSE(`_condition_`)`; | `EXPECT_FALSE(`_condition_`)`;  | _condition_ is false |
-
-Remember, when they fail, `ASSERT_*` yields a fatal failure and
-returns from the current function, while `EXPECT_*` yields a nonfatal
-failure, allowing the function to continue running. In either case, an
-assertion failure means its containing test fails.
-
-_Availability_: Linux, Windows, Mac.
-
-## Binary Comparison ##
-
-This section describes assertions that compare two values.
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-|`ASSERT_EQ(`_val1_`, `_val2_`);`|`EXPECT_EQ(`_val1_`, `_val2_`);`| _val1_ `==` _val2_ |
-|`ASSERT_NE(`_val1_`, `_val2_`);`|`EXPECT_NE(`_val1_`, `_val2_`);`| _val1_ `!=` _val2_ |
-|`ASSERT_LT(`_val1_`, `_val2_`);`|`EXPECT_LT(`_val1_`, `_val2_`);`| _val1_ `<` _val2_ |
-|`ASSERT_LE(`_val1_`, `_val2_`);`|`EXPECT_LE(`_val1_`, `_val2_`);`| _val1_ `<=` _val2_ |
-|`ASSERT_GT(`_val1_`, `_val2_`);`|`EXPECT_GT(`_val1_`, `_val2_`);`| _val1_ `>` _val2_ |
-|`ASSERT_GE(`_val1_`, `_val2_`);`|`EXPECT_GE(`_val1_`, `_val2_`);`| _val1_ `>=` _val2_ |
-
-In the event of a failure, Google Test prints both _val1_ and _val2_.
-
-Value arguments must be comparable by the assertion's comparison
-operator or you'll get a compiler error.  We used to require the
-arguments to support the `<<` operator for streaming to an `ostream`,
-but it's no longer necessary since v1.6.0 (if `<<` is supported, it
-will be called to print the arguments when the assertion fails;
-otherwise Google Test will attempt to print them in the best way it
-can. For more details and how to customize the printing of the
-arguments, see this Google Mock [recipe](../../googlemock/docs/CookBook.md#teaching-google-mock-how-to-print-your-values).).
-
-These assertions can work with a user-defined type, but only if you define the
-corresponding comparison operator (e.g. `==`, `<`, etc).  If the corresponding
-operator is defined, prefer using the `ASSERT_*()` macros because they will
-print out not only the result of the comparison, but the two operands as well.
-
-Arguments are always evaluated exactly once. Therefore, it's OK for the
-arguments to have side effects. However, as with any ordinary C/C++ function,
-the arguments' evaluation order is undefined (i.e. the compiler is free to
-choose any order) and your code should not depend on any particular argument
-evaluation order.
-
-`ASSERT_EQ()` does pointer equality on pointers. If used on two C strings, it
-tests if they are in the same memory location, not if they have the same value.
-Therefore, if you want to compare C strings (e.g. `const char*`) by value, use
-`ASSERT_STREQ()` , which will be described later on. In particular, to assert
-that a C string is `NULL`, use `ASSERT_STREQ(NULL, c_string)` . However, to
-compare two `string` objects, you should use `ASSERT_EQ`.
-
-Macros in this section work with both narrow and wide string objects (`string`
-and `wstring`).
-
-_Availability_: Linux, Windows, Mac.
-
-_Historical note_: Before February 2016 `*_EQ` had a convention of calling it as
-`ASSERT_EQ(expected, actual)`, so lots of existing code uses this order.
-Now `*_EQ` treats both parameters in the same way.
-
-## String Comparison ##
-
-The assertions in this group compare two **C strings**. If you want to compare
-two `string` objects, use `EXPECT_EQ`, `EXPECT_NE`, and etc instead.
-
-| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
-|:--------------------|:-----------------------|:-------------|
-| `ASSERT_STREQ(`_str1_`, `_str2_`);`    | `EXPECT_STREQ(`_str1_`, `_str_2`);`     | the two C strings have the same content |
-| `ASSERT_STRNE(`_str1_`, `_str2_`);`    | `EXPECT_STRNE(`_str1_`, `_str2_`);`     | the two C strings have different content |
-| `ASSERT_STRCASEEQ(`_str1_`, `_str2_`);`| `EXPECT_STRCASEEQ(`_str1_`, `_str2_`);` | the two C strings have the same content, ignoring case |
-| `ASSERT_STRCASENE(`_str1_`, `_str2_`);`| `EXPECT_STRCASENE(`_str1_`, `_str2_`);` | the two C strings have different content, ignoring case |
-
-Note that "CASE" in an assertion name means that case is ignored.
-
-`*STREQ*` and `*STRNE*` also accept wide C strings (`wchar_t*`). If a
-comparison of two wide strings fails, their values will be printed as UTF-8
-narrow strings.
-
-A `NULL` pointer and an empty string are considered _different_.
-
-_Availability_: Linux, Windows, Mac.
-
-See also: For more string comparison tricks (substring, prefix, suffix, and
-regular expression matching, for example), see the [Advanced Google Test Guide](AdvancedGuide.md).
-
-# Simple Tests #
-
-To create a test:
-  1. Use the `TEST()` macro to define and name a test function, These are ordinary C++ functions that don't return a value.
-  1. In this function, along with any valid C++ statements you want to include, use the various Google Test assertions to check values.
-  1. The test's result is determined by the assertions; if any assertion in the test fails (either fatally or non-fatally), or if the test crashes, the entire test fails. Otherwise, it succeeds.
-
-```
-TEST(test_case_name, test_name) {
- ... test body ...
-}
-```
-
-
-`TEST()` arguments go from general to specific. The _first_ argument is the
-name of the test case, and the _second_ argument is the test's name within the
-test case. Both names must be valid C++ identifiers, and they should not contain underscore (`_`). A test's _full name_ consists of its containing test case and its
-individual name. Tests from different test cases can have the same individual
-name.
-
-For example, let's take a simple integer function:
-```
-int Factorial(int n); // Returns the factorial of n
-```
-
-A test case for this function might look like:
-```
-// Tests factorial of 0.
-TEST(FactorialTest, HandlesZeroInput) {
-  EXPECT_EQ(1, Factorial(0));
-}
-
-// Tests factorial of positive numbers.
-TEST(FactorialTest, HandlesPositiveInput) {
-  EXPECT_EQ(1, Factorial(1));
-  EXPECT_EQ(2, Factorial(2));
-  EXPECT_EQ(6, Factorial(3));
-  EXPECT_EQ(40320, Factorial(8));
-}
-```
-
-Google Test groups the test results by test cases, so logically-related tests
-should be in the same test case; in other words, the first argument to their
-`TEST()` should be the same. In the above example, we have two tests,
-`HandlesZeroInput` and `HandlesPositiveInput`, that belong to the same test
-case `FactorialTest`.
-
-_Availability_: Linux, Windows, Mac.
-
-# Test Fixtures: Using the Same Data Configuration for Multiple Tests #
-
-If you find yourself writing two or more tests that operate on similar data,
-you can use a _test fixture_. It allows you to reuse the same configuration of
-objects for several different tests.
-
-To create a fixture, just:
-  1. Derive a class from `::testing::Test` . Start its body with `protected:` or `public:` as we'll want to access fixture members from sub-classes.
-  1. Inside the class, declare any objects you plan to use.
-  1. If necessary, write a default constructor or `SetUp()` function to prepare the objects for each test. A common mistake is to spell `SetUp()` as `Setup()` with a small `u` - don't let that happen to you.
-  1. If necessary, write a destructor or `TearDown()` function to release any resources you allocated in `SetUp()` . To learn when you should use the constructor/destructor and when you should use `SetUp()/TearDown()`, read this [FAQ entry](FAQ.md#should-i-use-the-constructordestructor-of-the-test-fixture-or-the-set-uptear-down-function).
-  1. If needed, define subroutines for your tests to share.
-
-When using a fixture, use `TEST_F()` instead of `TEST()` as it allows you to
-access objects and subroutines in the test fixture:
-```
-TEST_F(test_case_name, test_name) {
- ... test body ...
-}
-```
-
-Like `TEST()`, the first argument is the test case name, but for `TEST_F()`
-this must be the name of the test fixture class. You've probably guessed: `_F`
-is for fixture.
-
-Unfortunately, the C++ macro system does not allow us to create a single macro
-that can handle both types of tests. Using the wrong macro causes a compiler
-error.
-
-Also, you must first define a test fixture class before using it in a
-`TEST_F()`, or you'll get the compiler error "`virtual outside class
-declaration`".
-
-For each test defined with `TEST_F()`, Google Test will:
-  1. Create a _fresh_ test fixture at runtime
-  1. Immediately initialize it via `SetUp()` ,
-  1. Run the test
-  1. Clean up by calling `TearDown()`
-  1. Delete the test fixture.  Note that different tests in the same test case have different test fixture objects, and Google Test always deletes a test fixture before it creates the next one. Google Test does not reuse the same test fixture for multiple tests. Any changes one test makes to the fixture do not affect other tests.
-
-As an example, let's write tests for a FIFO queue class named `Queue`, which
-has the following interface:
-```
-template <typename E> // E is the element type.
-class Queue {
- public:
-  Queue();
-  void Enqueue(const E& element);
-  E* Dequeue(); // Returns NULL if the queue is empty.
-  size_t size() const;
-  ...
-};
-```
-
-First, define a fixture class. By convention, you should give it the name
-`FooTest` where `Foo` is the class being tested.
-```
-class QueueTest : public ::testing::Test {
- protected:
-  virtual void SetUp() {
-    q1_.Enqueue(1);
-    q2_.Enqueue(2);
-    q2_.Enqueue(3);
-  }
-
-  // virtual void TearDown() {}
-
-  Queue<int> q0_;
-  Queue<int> q1_;
-  Queue<int> q2_;
-};
-```
-
-In this case, `TearDown()` is not needed since we don't have to clean up after
-each test, other than what's already done by the destructor.
-
-Now we'll write tests using `TEST_F()` and this fixture.
-```
-TEST_F(QueueTest, IsEmptyInitially) {
-  EXPECT_EQ(0, q0_.size());
-}
-
-TEST_F(QueueTest, DequeueWorks) {
-  int* n = q0_.Dequeue();
-  EXPECT_EQ(NULL, n);
-
-  n = q1_.Dequeue();
-  ASSERT_TRUE(n != NULL);
-  EXPECT_EQ(1, *n);
-  EXPECT_EQ(0, q1_.size());
-  delete n;
-
-  n = q2_.Dequeue();
-  ASSERT_TRUE(n != NULL);
-  EXPECT_EQ(2, *n);
-  EXPECT_EQ(1, q2_.size());
-  delete n;
-}
-```
-
-The above uses both `ASSERT_*` and `EXPECT_*` assertions. The rule of thumb is
-to use `EXPECT_*` when you want the test to continue to reveal more errors
-after the assertion failure, and use `ASSERT_*` when continuing after failure
-doesn't make sense. For example, the second assertion in the `Dequeue` test is
-`ASSERT_TRUE(n != NULL)`, as we need to dereference the pointer `n` later,
-which would lead to a segfault when `n` is `NULL`.
-
-When these tests run, the following happens:
-  1. Google Test constructs a `QueueTest` object (let's call it `t1` ).
-  1. `t1.SetUp()` initializes `t1` .
-  1. The first test ( `IsEmptyInitially` ) runs on `t1` .
-  1. `t1.TearDown()` cleans up after the test finishes.
-  1. `t1` is destructed.
-  1. The above steps are repeated on another `QueueTest` object, this time running the `DequeueWorks` test.
-
-_Availability_: Linux, Windows, Mac.
-
-_Note_: Google Test automatically saves all _Google Test_ flags when a test
-object is constructed, and restores them when it is destructed.
-
-# Invoking the Tests #
-
-`TEST()` and `TEST_F()` implicitly register their tests with Google Test. So, unlike with many other C++ testing frameworks, you don't have to re-list all your defined tests in order to run them.
-
-After defining your tests, you can run them with `RUN_ALL_TESTS()` , which returns `0` if all the tests are successful, or `1` otherwise. Note that `RUN_ALL_TESTS()` runs _all tests_ in your link unit -- they can be from different test cases, or even different source files.
-
-When invoked, the `RUN_ALL_TESTS()` macro:
-  1. Saves the state of all  Google Test flags.
-  1. Creates a test fixture object for the first test.
-  1. Initializes it via `SetUp()`.
-  1. Runs the test on the fixture object.
-  1. Cleans up the fixture via `TearDown()`.
-  1. Deletes the fixture.
-  1. Restores the state of all Google Test flags.
-  1. Repeats the above steps for the next test, until all tests have run.
-
-In addition, if the text fixture's constructor generates a fatal failure in
-step 2, there is no point for step 3 - 5 and they are thus skipped. Similarly,
-if step 3 generates a fatal failure, step 4 will be skipped.
-
-_Important_: You must not ignore the return value of `RUN_ALL_TESTS()`, or `gcc`
-will give you a compiler error. The rationale for this design is that the
-automated testing service determines whether a test has passed based on its
-exit code, not on its stdout/stderr output; thus your `main()` function must
-return the value of `RUN_ALL_TESTS()`.
-
-Also, you should call `RUN_ALL_TESTS()` only **once**. Calling it more than once
-conflicts with some advanced Google Test features (e.g. thread-safe death
-tests) and thus is not supported.
-
-_Availability_: Linux, Windows, Mac.
-
-# Writing the main() Function #
-
-You can start from this boilerplate:
-```
-#include "this/package/foo.h"
-#include "gtest/gtest.h"
-
-namespace {
-
-// The fixture for testing class Foo.
-class FooTest : public ::testing::Test {
- protected:
-  // You can remove any or all of the following functions if its body
-  // is empty.
-
-  FooTest() {
-    // You can do set-up work for each test here.
-  }
-
-  virtual ~FooTest() {
-    // You can do clean-up work that doesn't throw exceptions here.
-  }
-
-  // If the constructor and destructor are not enough for setting up
-  // and cleaning up each test, you can define the following methods:
-
-  virtual void SetUp() {
-    // Code here will be called immediately after the constructor (right
-    // before each test).
-  }
-
-  virtual void TearDown() {
-    // Code here will be called immediately after each test (right
-    // before the destructor).
-  }
-
-  // Objects declared here can be used by all tests in the test case for Foo.
-};
-
-// Tests that the Foo::Bar() method does Abc.
-TEST_F(FooTest, MethodBarDoesAbc) {
-  const string input_filepath = "this/package/testdata/myinputfile.dat";
-  const string output_filepath = "this/package/testdata/myoutputfile.dat";
-  Foo f;
-  EXPECT_EQ(0, f.Bar(input_filepath, output_filepath));
-}
-
-// Tests that Foo does Xyz.
-TEST_F(FooTest, DoesXyz) {
-  // Exercises the Xyz feature of Foo.
-}
-
-}  // namespace
-
-int main(int argc, char **argv) {
-  ::testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
-```
-
-The `::testing::InitGoogleTest()` function parses the command line for Google
-Test flags, and removes all recognized flags. This allows the user to control a
-test program's behavior via various flags, which we'll cover in [AdvancedGuide](AdvancedGuide.md).
-You must call this function before calling `RUN_ALL_TESTS()`, or the flags
-won't be properly initialized.
-
-On Windows, `InitGoogleTest()` also works with wide strings, so it can be used
-in programs compiled in `UNICODE` mode as well.
-
-But maybe you think that writing all those main() functions is too much work? We agree with you completely and that's why Google Test provides a basic implementation of main(). If it fits your needs, then just link your test with gtest\_main library and you are good to go.
-
-## Important note for Visual C++ users ##
-If you put your tests into a library and your `main()` function is in a different library or in your .exe file, those tests will not run. The reason is a [bug](https://connect.microsoft.com/feedback/viewfeedback.aspx?FeedbackID=244410&siteid=210) in Visual C++. When you define your tests, Google Test creates certain static objects to register them. These objects are not referenced from elsewhere but their constructors are still supposed to run. When Visual C++ linker sees that nothing in the library is referenced from other places it throws the library out. You have to reference your library with tests from your main program to keep the linker from discarding it. Here is how to do it. Somewhere in your library code declare a function:
-```
-__declspec(dllexport) int PullInMyLibrary() { return 0; }
-```
-If you put your tests in a static library (not DLL) then `__declspec(dllexport)` is not required. Now, in your main program, write a code that invokes that function:
-```
-int PullInMyLibrary();
-static int dummy = PullInMyLibrary();
-```
-This will keep your tests referenced and will make them register themselves at startup.
-
-In addition, if you define your tests in a static library, add `/OPT:NOREF` to your main program linker options. If you use MSVC++ IDE, go to your .exe project properties/Configuration Properties/Linker/Optimization and set References setting to `Keep Unreferenced Data (/OPT:NOREF)`. This will keep Visual C++ linker from discarding individual symbols generated by your tests from the final executable.
-
-There is one more pitfall, though. If you use Google Test as a static library (that's how it is defined in gtest.vcproj) your tests must also reside in a static library. If you have to have them in a DLL, you _must_ change Google Test to build into a DLL as well. Otherwise your tests will not run correctly or will not run at all. The general conclusion here is: make your life easier - do not write your tests in libraries!
-
-# Where to Go from Here #
-
-Congratulations! You've learned the Google Test basics. You can start writing
-and running Google Test tests, read some [samples](Samples.md), or continue with
-[AdvancedGuide](AdvancedGuide.md), which describes many more useful Google Test features.
-
-# Known Limitations #
-
-Google Test is designed to be thread-safe.  The implementation is
-thread-safe on systems where the `pthreads` library is available.  It
-is currently _unsafe_ to use Google Test assertions from two threads
-concurrently on other systems (e.g. Windows).  In most tests this is
-not an issue as usually the assertions are done in the main thread. If
-you want to help, you can volunteer to implement the necessary
-synchronization primitives in `gtest-port.h` for your platform.
index 8184f153ca2ecad7c65cb58c27fb31bd72375fee..827bb24b042dc543af4c409ad03e147ea5949b7b 100644 (file)
@@ -40,7 +40,7 @@ maintain.
 ## Highlights ##
 
   * The implementation is in a single Python script and thus ultra portable: no build or installation is needed and it works cross platforms.
-  * Pump tries to be smart with respect to [Google's style guide](http://code.google.com/p/google-styleguide/): it breaks long lines (easy to have when they are generated) at acceptable places to fit within 80 columns and indent the continuation lines correctly.
+  * Pump tries to be smart with respect to [Google's style guide](https://github.com/google/styleguide): it breaks long lines (easy to have when they are generated) at acceptable places to fit within 80 columns and indent the continuation lines correctly.
   * The format is human-readable and more concise than XML.
   * The format works relatively well with Emacs' C++ mode.
 
@@ -169,7 +169,7 @@ improving Pump.
 
 ## Real Examples ##
 
-You can find real-world applications of Pump in [Google Test](http://www.google.com/codesearch?q=file%3A\.pump%24+package%3Ahttp%3A%2F%2Fgoogletest\.googlecode\.com) and [Google Mock](http://www.google.com/codesearch?q=file%3A\.pump%24+package%3Ahttp%3A%2F%2Fgooglemock\.googlecode\.com).  The source file `foo.h.pump` generates `foo.h`.
+You can find real-world applications of Pump in [Google Test](https://github.com/google/googletest/tree/master/googletest) and [Google Mock](https://github.com/google/googletest/tree/master/googlemock). The source file `foo.h.pump` generates `foo.h`.
 
 ## Tips ##
 
diff --git a/src/external/googletest/googletest/docs/Samples.md b/src/external/googletest/googletest/docs/Samples.md
deleted file mode 100644 (file)
index f21d200..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-If you're like us, you'd like to look at some Google Test sample code.  The
-[samples folder](../samples) has a number of well-commented samples showing how to use a
-variety of Google Test features.
-
-  * [Sample #1](../samples/sample1_unittest.cc) shows the basic steps of using Google Test to test C++ functions.
-  * [Sample #2](../samples/sample2_unittest.cc) shows a more complex unit test for a class with multiple member functions.
-  * [Sample #3](../samples/sample3_unittest.cc) uses a test fixture.
-  * [Sample #4](../samples/sample4_unittest.cc) is another basic example of using Google Test.
-  * [Sample #5](../samples/sample5_unittest.cc) teaches how to reuse a test fixture in multiple test cases by deriving sub-fixtures from it.
-  * [Sample #6](../samples/sample6_unittest.cc) demonstrates type-parameterized tests.
-  * [Sample #7](../samples/sample7_unittest.cc) teaches the basics of value-parameterized tests.
-  * [Sample #8](../samples/sample8_unittest.cc) shows using `Combine()` in value-parameterized tests.
-  * [Sample #9](../samples/sample9_unittest.cc) shows use of the listener API to modify Google Test's console output and the use of its reflection API to inspect test results.
-  * [Sample #10](../samples/sample10_unittest.cc) shows use of the listener API to implement a primitive memory leak checker.
index 21d7f5c05f6d5e34d1ca0bf881dd67df3747eac4..1c60a33dad43baf98381f7c9f20f69b558b2aad0 100644 (file)
@@ -6,19 +6,19 @@ This guide will explain how to use the Google Testing Framework in your Xcode pr
 
 Here is the quick guide for using Google Test in your Xcode project.
 
-  1. Download the source from the [website](http://code.google.com/p/googletest) using this command: `svn checkout http://googletest.googlecode.com/svn/trunk/ googletest-read-only`
+  1. Download the source from the [website](https://github.com/google/googletest) using this command: `svn checkout http://googletest.googlecode.com/svn/trunk/ googletest-read-only`.
   1. Open up the `gtest.xcodeproj` in the `googletest-read-only/xcode/` directory and build the gtest.framework.
-  1. Create a new "Shell Tool" target in your Xcode project called something like "UnitTests"
-  1. Add the gtest.framework to your project and add it to the "Link Binary with Libraries" build phase of "UnitTests"
-  1. Add your unit test source code to the "Compile Sources" build phase of "UnitTests"
+  1. Create a new "Shell Tool" target in your Xcode project called something like "UnitTests".
+  1. Add the gtest.framework to your project and add it to the "Link Binary with Libraries" build phase of "UnitTests".
+  1. Add your unit test source code to the "Compile Sources" build phase of "UnitTests".
   1. Edit the "UnitTests" executable and add an environment variable named "DYLD\_FRAMEWORK\_PATH" with a value equal to the path to the framework containing the gtest.framework relative to the compiled executable.
-  1. Build and Go
+  1. Build and Go.
 
 The following sections further explain each of the steps listed above in depth, describing in more detail how to complete it including some variations.
 
 # Get the Source #
 
-Currently, the gtest.framework discussed here isn't available in a tagged release of Google Test, it is only available in the trunk. As explained at the Google Test [site](http://code.google.com/p/googletest/source/checkout">svn), you can get the code from anonymous SVN with this command:
+Currently, the gtest.framework discussed here isn't available in a tagged release of Google Test, it is only available in the trunk. As explained at the Google Test [site](https://github.com/google/googletest), you can get the code from anonymous SVN with this command:
 
 ```
 svn checkout http://googletest.googlecode.com/svn/trunk/ googletest-read-only
@@ -28,7 +28,7 @@ Alternatively, if you are working with Subversion in your own code base, you can
 
 To use `svn:externals`, decide where you would like to have the external source reside. You might choose to put the external source inside the trunk, because you want it to be part of the branch when you make a release. However, keeping it outside the trunk in a version-tagged directory called something like `third-party/googletest/1.0.1`, is another option. Once the location is established, use `svn propedit svn:externals _directory_` to set the svn:externals property on a directory in your repository. This directory won't contain the code, but be its versioned parent directory.
 
-The command `svn propedit` will bring up your Subversion editor, making editing the long, (potentially multi-line) property simpler. This same method can be used to check out a tagged branch, by using the appropriate URL (e.g. `http://googletest.googlecode.com/svn/tags/release-1.0.1`). Additionally, the svn:externals property allows the specification of a particular revision of the trunk with the `-r_##_` option (e.g. `externals/src/googletest -r60 http://googletest.googlecode.com/svn/trunk`).
+The command `svn propedit` will bring up your Subversion editor, making editing the long, (potentially multi-line) property simpler. This same method can be used to check out a tagged branch, by using the appropriate URL (e.g. `https://github.com/google/googletest/releases/tag/release-1.0.1`). Additionally, the svn:externals property allows the specification of a particular revision of the trunk with the `-r_##_` option (e.g. `externals/src/googletest -r60 http://googletest.googlecode.com/svn/trunk`).
 
 Here is an example of using the svn:externals properties on a trunk (read via `svn propget`) of a project. This value checks out a copy of Google Test into the `trunk/externals/src/googletest/` directory.
 
@@ -66,7 +66,7 @@ If you haven't set up the DYLD\_FRAMEWORK\_PATH, correctly, you might get a mess
     Reason: image not found
 ```
 
-To correct this problem, got to the directory containing the executable named in "Referenced from:" value in the error message above. Then, with the terminal in this location, find the relative path to the directory containing the gtest.framework. That is the value you'll need to set as the DYLD\_FRAMEWORK\_PATH.
+To correct this problem, go to to the directory containing the executable named in "Referenced from:" value in the error message above. Then, with the terminal in this location, find the relative path to the directory containing the gtest.framework. That is the value you'll need to set as the DYLD\_FRAMEWORK\_PATH.
 
 # Build and Go #
 
diff --git a/src/external/googletest/googletest/docs/advanced.md b/src/external/googletest/googletest/docs/advanced.md
new file mode 100644 (file)
index 0000000..8065d19
--- /dev/null
@@ -0,0 +1,2520 @@
+# Advanced googletest Topics
+
+
+## Introduction
+
+Now that you have read the [googletest Primer](primer.md) and learned how to write
+tests using googletest, it's time to learn some new tricks. This document will
+show you more assertions as well as how to construct complex failure messages,
+propagate fatal failures, reuse and speed up your test fixtures, and use various
+flags with your tests.
+
+## More Assertions
+
+This section covers some less frequently used, but still significant,
+assertions.
+
+### Explicit Success and Failure
+
+These three assertions do not actually test a value or expression. Instead, they
+generate a success or failure directly. Like the macros that actually perform a
+test, you may stream a custom failure message into them.
+
+```c++
+SUCCEED();
+```
+
+Generates a success. This does **NOT** make the overall test succeed. A test is
+considered successful only if none of its assertions fail during its execution.
+
+NOTE: `SUCCEED()` is purely documentary and currently doesn't generate any
+user-visible output. However, we may add `SUCCEED()` messages to googletest's
+output in the future.
+
+```c++
+FAIL();
+ADD_FAILURE();
+ADD_FAILURE_AT("file_path", line_number);
+```
+
+`FAIL()` generates a fatal failure, while `ADD_FAILURE()` and `ADD_FAILURE_AT()`
+generate a nonfatal failure. These are useful when control flow, rather than a
+Boolean expression, determines the test's success or failure. For example, you
+might want to write something like:
+
+```c++
+switch(expression) {
+  case 1:
+     ... some checks ...
+  case 2:
+     ... some other checks ...
+  default:
+     FAIL() << "We shouldn't get here.";
+}
+```
+
+NOTE: you can only use `FAIL()` in functions that return `void`. See the
+[Assertion Placement section](#assertion-placement) for more information.
+
+**Availability**: Linux, Windows, Mac.
+
+### Exception Assertions
+
+These are for verifying that a piece of code throws (or does not throw) an
+exception of the given type:
+
+Fatal assertion                            | Nonfatal assertion                         | Verifies
+------------------------------------------ | ------------------------------------------ | --------
+`ASSERT_THROW(statement, exception_type);` | `EXPECT_THROW(statement, exception_type);` | `statement` throws an exception of the given type
+`ASSERT_ANY_THROW(statement);`             | `EXPECT_ANY_THROW(statement);`             | `statement` throws an exception of any type
+`ASSERT_NO_THROW(statement);`              | `EXPECT_NO_THROW(statement);`              | `statement` doesn't throw any exception
+
+Examples:
+
+```c++
+ASSERT_THROW(Foo(5), bar_exception);
+
+EXPECT_NO_THROW({
+  int n = 5;
+  Bar(&n);
+});
+```
+
+**Availability**: Linux, Windows, Mac; requires exceptions to be enabled in the
+build environment (note that `google3` **disables** exceptions).
+
+### Predicate Assertions for Better Error Messages
+
+Even though googletest has a rich set of assertions, they can never be complete,
+as it's impossible (nor a good idea) to anticipate all scenarios a user might
+run into. Therefore, sometimes a user has to use `EXPECT_TRUE()` to check a
+complex expression, for lack of a better macro. This has the problem of not
+showing you the values of the parts of the expression, making it hard to
+understand what went wrong. As a workaround, some users choose to construct the
+failure message by themselves, streaming it into `EXPECT_TRUE()`. However, this
+is awkward especially when the expression has side-effects or is expensive to
+evaluate.
+
+googletest gives you three different options to solve this problem:
+
+#### Using an Existing Boolean Function
+
+If you already have a function or functor that returns `bool` (or a type that
+can be implicitly converted to `bool`), you can use it in a *predicate
+assertion* to get the function arguments printed for free:
+
+| Fatal assertion                    | Nonfatal assertion                 | Verifies                    |
+| ---------------------------------- | ---------------------------------- | --------------------------- |
+| `ASSERT_PRED1(pred1, val1);`       | `EXPECT_PRED1(pred1, val1);`       | `pred1(val1)` is true       |
+| `ASSERT_PRED2(pred2, val1, val2);` | `EXPECT_PRED2(pred2, val1, val2);` | `pred2(val1, val2)` is true |
+| `...`                              | `...`                              | ...                         |
+
+In the above, `predn` is an `n`-ary predicate function or functor, where `val1`,
+`val2`, ..., and `valn` are its arguments. The assertion succeeds if the
+predicate returns `true` when applied to the given arguments, and fails
+otherwise. When the assertion fails, it prints the value of each argument. In
+either case, the arguments are evaluated exactly once.
+
+Here's an example. Given
+
+```c++
+// Returns true if m and n have no common divisors except 1.
+bool MutuallyPrime(int m, int n) { ... }
+
+const int a = 3;
+const int b = 4;
+const int c = 10;
+```
+
+the assertion
+
+```c++
+  EXPECT_PRED2(MutuallyPrime, a, b);
+```
+
+will succeed, while the assertion
+
+```c++
+  EXPECT_PRED2(MutuallyPrime, b, c);
+```
+
+will fail with the message
+
+```none
+MutuallyPrime(b, c) is false, where
+b is 4
+c is 10
+```
+
+> NOTE:
+>
+> 1.  If you see a compiler error "no matching function to call" when using
+>     `ASSERT_PRED*` or `EXPECT_PRED*`, please see
+>     [this](faq.md#OverloadedPredicate) for how to resolve it.
+> 1.  Currently we only provide predicate assertions of arity <= 5. If you need
+>     a higher-arity assertion, let [us](https://github.com/google/googletest/issues) know.
+
+**Availability**: Linux, Windows, Mac.
+
+#### Using a Function That Returns an AssertionResult
+
+While `EXPECT_PRED*()` and friends are handy for a quick job, the syntax is not
+satisfactory: you have to use different macros for different arities, and it
+feels more like Lisp than C++. The `::testing::AssertionResult` class solves
+this problem.
+
+An `AssertionResult` object represents the result of an assertion (whether it's
+a success or a failure, and an associated message). You can create an
+`AssertionResult` using one of these factory functions:
+
+```c++
+namespace testing {
+
+// Returns an AssertionResult object to indicate that an assertion has
+// succeeded.
+AssertionResult AssertionSuccess();
+
+// Returns an AssertionResult object to indicate that an assertion has
+// failed.
+AssertionResult AssertionFailure();
+
+}
+```
+
+You can then use the `<<` operator to stream messages to the `AssertionResult`
+object.
+
+To provide more readable messages in Boolean assertions (e.g. `EXPECT_TRUE()`),
+write a predicate function that returns `AssertionResult` instead of `bool`. For
+example, if you define `IsEven()` as:
+
+```c++
+::testing::AssertionResult IsEven(int n) {
+  if ((n % 2) == 0)
+     return ::testing::AssertionSuccess();
+  else
+     return ::testing::AssertionFailure() << n << " is odd";
+}
+```
+
+instead of:
+
+```c++
+bool IsEven(int n) {
+  return (n % 2) == 0;
+}
+```
+
+the failed assertion `EXPECT_TRUE(IsEven(Fib(4)))` will print:
+
+```none
+Value of: IsEven(Fib(4))
+  Actual: false (3 is odd)
+Expected: true
+```
+
+instead of a more opaque
+
+```none
+Value of: IsEven(Fib(4))
+  Actual: false
+Expected: true
+```
+
+If you want informative messages in `EXPECT_FALSE` and `ASSERT_FALSE` as well
+(one third of Boolean assertions in the Google code base are negative ones), and
+are fine with making the predicate slower in the success case, you can supply a
+success message:
+
+```c++
+::testing::AssertionResult IsEven(int n) {
+  if ((n % 2) == 0)
+     return ::testing::AssertionSuccess() << n << " is even";
+  else
+     return ::testing::AssertionFailure() << n << " is odd";
+}
+```
+
+Then the statement `EXPECT_FALSE(IsEven(Fib(6)))` will print
+
+```none
+  Value of: IsEven(Fib(6))
+     Actual: true (8 is even)
+  Expected: false
+```
+
+**Availability**: Linux, Windows, Mac.
+
+#### Using a Predicate-Formatter
+
+If you find the default message generated by `(ASSERT|EXPECT)_PRED*` and
+`(ASSERT|EXPECT)_(TRUE|FALSE)` unsatisfactory, or some arguments to your
+predicate do not support streaming to `ostream`, you can instead use the
+following *predicate-formatter assertions* to *fully* customize how the message
+is formatted:
+
+Fatal assertion                                  | Nonfatal assertion                               | Verifies
+------------------------------------------------ | ------------------------------------------------ | --------
+`ASSERT_PRED_FORMAT1(pred_format1, val1);`       | `EXPECT_PRED_FORMAT1(pred_format1, val1);`       | `pred_format1(val1)` is successful
+`ASSERT_PRED_FORMAT2(pred_format2, val1, val2);` | `EXPECT_PRED_FORMAT2(pred_format2, val1, val2);` | `pred_format2(val1, val2)` is successful
+`...`                                            | `...`                                            | ...
+
+The difference between this and the previous group of macros is that instead of
+a predicate, `(ASSERT|EXPECT)_PRED_FORMAT*` take a *predicate-formatter*
+(`pred_formatn`), which is a function or functor with the signature:
+
+```c++
+::testing::AssertionResult PredicateFormattern(const char* expr1,
+                                               const char* expr2,
+                                               ...
+                                               const char* exprn,
+                                               T1 val1,
+                                               T2 val2,
+                                               ...
+                                               Tn valn);
+```
+
+where `val1`, `val2`, ..., and `valn` are the values of the predicate arguments,
+and `expr1`, `expr2`, ..., and `exprn` are the corresponding expressions as they
+appear in the source code. The types `T1`, `T2`, ..., and `Tn` can be either
+value types or reference types. For example, if an argument has type `Foo`, you
+can declare it as either `Foo` or `const Foo&`, whichever is appropriate.
+
+As an example, let's improve the failure message in `MutuallyPrime()`, which was
+used with `EXPECT_PRED2()`:
+
+```c++
+// Returns the smallest prime common divisor of m and n,
+// or 1 when m and n are mutually prime.
+int SmallestPrimeCommonDivisor(int m, int n) { ... }
+
+// A predicate-formatter for asserting that two integers are mutually prime.
+::testing::AssertionResult AssertMutuallyPrime(const char* m_expr,
+                                               const char* n_expr,
+                                               int m,
+                                               int n) {
+  if (MutuallyPrime(m, n)) return ::testing::AssertionSuccess();
+
+  return ::testing::AssertionFailure() << m_expr << " and " << n_expr
+      << " (" << m << " and " << n << ") are not mutually prime, "
+      << "as they have a common divisor " << SmallestPrimeCommonDivisor(m, n);
+}
+```
+
+With this predicate-formatter, we can use
+
+```c++
+  EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c);
+```
+
+to generate the message
+
+```none
+b and c (4 and 10) are not mutually prime, as they have a common divisor 2.
+```
+
+As you may have realized, many of the built-in assertions we introduced earlier
+are special cases of `(EXPECT|ASSERT)_PRED_FORMAT*`. In fact, most of them are
+indeed defined using `(EXPECT|ASSERT)_PRED_FORMAT*`.
+
+**Availability**: Linux, Windows, Mac.
+
+### Floating-Point Comparison
+
+Comparing floating-point numbers is tricky. Due to round-off errors, it is very
+unlikely that two floating-points will match exactly. Therefore, `ASSERT_EQ` 's
+naive comparison usually doesn't work. And since floating-points can have a wide
+value range, no single fixed error bound works. It's better to compare by a
+fixed relative error bound, except for values close to 0 due to the loss of
+precision there.
+
+In general, for floating-point comparison to make sense, the user needs to
+carefully choose the error bound. If they don't want or care to, comparing in
+terms of Units in the Last Place (ULPs) is a good default, and googletest
+provides assertions to do this. Full details about ULPs are quite long; if you
+want to learn more, see
+[here](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
+
+#### Floating-Point Macros
+
+| Fatal assertion                 | Nonfatal assertion             | Verifies                                 |
+| ------------------------------- | ------------------------------ | ---------------------------------------- |
+| `ASSERT_FLOAT_EQ(val1, val2);`  | `EXPECT_FLOAT_EQ(val1,val2);`  | the two `float` values are almost equal  |
+| `ASSERT_DOUBLE_EQ(val1, val2);` | `EXPECT_DOUBLE_EQ(val1, val2);`| the two `double` values are almost equal |
+
+By "almost equal" we mean the values are within 4 ULP's from each other.
+
+NOTE: `CHECK_DOUBLE_EQ()` in `base/logging.h` uses a fixed absolute error bound,
+so its result may differ from that of the googletest macros. That macro is
+unsafe and has been deprecated. Please don't use it any more.
+
+The following assertions allow you to choose the acceptable error bound:
+
+| Fatal assertion                       | Nonfatal assertion                    | Verifies                  |
+| ------------------------------------- | ------------------------------------- | ------------------------- |
+| `ASSERT_NEAR(val1, val2, abs_error);` | `EXPECT_NEAR(val1, val2, abs_error);` | the difference between `val1` and `val2` doesn't exceed the given absolute error |
+
+**Availability**: Linux, Windows, Mac.
+
+#### Floating-Point Predicate-Format Functions
+
+Some floating-point operations are useful, but not that often used. In order to
+avoid an explosion of new macros, we provide them as predicate-format functions
+that can be used in predicate assertion macros (e.g. `EXPECT_PRED_FORMAT2`,
+etc).
+
+```c++
+EXPECT_PRED_FORMAT2(::testing::FloatLE, val1, val2);
+EXPECT_PRED_FORMAT2(::testing::DoubleLE, val1, val2);
+```
+
+Verifies that `val1` is less than, or almost equal to, `val2`. You can replace
+`EXPECT_PRED_FORMAT2` in the above table with `ASSERT_PRED_FORMAT2`.
+
+**Availability**: Linux, Windows, Mac.
+
+### Asserting Using gMock Matchers
+
+Google-developed C++ mocking framework [gMock](../../googlemock) comes with a
+library of matchers for validating arguments passed to mock objects. A gMock
+*matcher* is basically a predicate that knows how to describe itself. It can be
+used in these assertion macros:
+
+| Fatal assertion                | Nonfatal assertion             | Verifies              |
+| ------------------------------ | ------------------------------ | --------------------- |
+| `ASSERT_THAT(value, matcher);` | `EXPECT_THAT(value, matcher);` | value matches matcher |
+
+For example, `StartsWith(prefix)` is a matcher that matches a string starting
+with `prefix`, and you can write:
+
+```c++
+using ::testing::StartsWith;
+...
+    // Verifies that Foo() returns a string starting with "Hello".
+    EXPECT_THAT(Foo(), StartsWith("Hello"));
+```
+
+Read this [recipe](../../googlemock/docs/CookBook.md#using-matchers-in-google-test-assertions) in
+the gMock Cookbook for more details.
+
+gMock has a rich set of matchers. You can do many things googletest cannot do
+alone with them. For a list of matchers gMock provides, read
+[this](../../googlemock/docs/CookBook.md#using-matchers). Especially useful among them are
+some [protocol buffer matchers](https://github.com/google/nucleus/blob/master/nucleus/testing/protocol-buffer-matchers.h). It's easy to write
+your [own matchers](../../googlemock/docs/CookBook.md#writing-new-matchers-quickly) too.
+
+For example, you can use gMock's
+[EqualsProto](https://github.com/google/nucleus/blob/master/nucleus/testing/protocol-buffer-matchers.h)
+to compare protos in your tests:
+
+```c++
+#include "testing/base/public/gmock.h"
+using ::testing::EqualsProto;
+...
+    EXPECT_THAT(actual_proto, EqualsProto("foo: 123 bar: 'xyz'"));
+    EXPECT_THAT(*actual_proto_ptr, EqualsProto(expected_proto));
+```
+
+gMock is bundled with googletest, so you don't need to add any build dependency
+in order to take advantage of this. Just include `"testing/base/public/gmock.h"`
+and you're ready to go.
+
+**Availability**: Linux, Windows, and Mac.
+
+### More String Assertions
+
+(Please read the [previous](#AssertThat) section first if you haven't.)
+
+You can use the gMock [string matchers](../../googlemock/docs/CheatSheet.md#string-matchers)
+with `EXPECT_THAT()` or `ASSERT_THAT()` to do more string comparison tricks
+(sub-string, prefix, suffix, regular expression, and etc). For example,
+
+```c++
+using ::testing::HasSubstr;
+using ::testing::MatchesRegex;
+...
+  ASSERT_THAT(foo_string, HasSubstr("needle"));
+  EXPECT_THAT(bar_string, MatchesRegex("\\w*\\d+"));
+```
+
+**Availability**: Linux, Windows, Mac.
+
+If the string contains a well-formed HTML or XML document, you can check whether
+its DOM tree matches an [XPath
+expression](http://www.w3.org/TR/xpath/#contents):
+
+```c++
+// Currently still in //template/prototemplate/testing:xpath_matcher
+#include "template/prototemplate/testing/xpath_matcher.h"
+using prototemplate::testing::MatchesXPath;
+EXPECT_THAT(html_string, MatchesXPath("//a[text()='click here']"));
+```
+
+**Availability**: Linux.
+
+### Windows HRESULT assertions
+
+These assertions test for `HRESULT` success or failure.
+
+Fatal assertion                        | Nonfatal assertion                     | Verifies
+-------------------------------------- | -------------------------------------- | --------
+`ASSERT_HRESULT_SUCCEEDED(expression)` | `EXPECT_HRESULT_SUCCEEDED(expression)` | `expression` is a success `HRESULT`
+`ASSERT_HRESULT_FAILED(expression)`    | `EXPECT_HRESULT_FAILED(expression)`    | `expression` is a failure `HRESULT`
+
+The generated output contains the human-readable error message associated with
+the `HRESULT` code returned by `expression`.
+
+You might use them like this:
+
+```c++
+CComPtr<IShellDispatch2> shell;
+ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));
+CComVariant empty;
+ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));
+```
+
+**Availability**: Windows.
+
+### Type Assertions
+
+You can call the function
+
+```c++
+::testing::StaticAssertTypeEq<T1, T2>();
+```
+
+to assert that types `T1` and `T2` are the same. The function does nothing if
+the assertion is satisfied. If the types are different, the function call will
+fail to compile, and the compiler error message will likely (depending on the
+compiler) show you the actual values of `T1` and `T2`. This is mainly useful
+inside template code.
+
+**Caveat**: When used inside a member function of a class template or a function
+template, `StaticAssertTypeEq<T1, T2>()` is effective only if the function is
+instantiated. For example, given:
+
+```c++
+template <typename T> class Foo {
+ public:
+  void Bar() { ::testing::StaticAssertTypeEq<int, T>(); }
+};
+```
+
+the code:
+
+```c++
+void Test1() { Foo<bool> foo; }
+```
+
+will not generate a compiler error, as `Foo<bool>::Bar()` is never actually
+instantiated. Instead, you need:
+
+```c++
+void Test2() { Foo<bool> foo; foo.Bar(); }
+```
+
+to cause a compiler error.
+
+**Availability**: Linux, Windows, Mac.
+
+### Assertion Placement
+
+You can use assertions in any C++ function. In particular, it doesn't have to be
+a method of the test fixture class. The one constraint is that assertions that
+generate a fatal failure (`FAIL*` and `ASSERT_*`) can only be used in
+void-returning functions. This is a consequence of Google's not using
+exceptions. By placing it in a non-void function you'll get a confusing compile
+error like `"error: void value not ignored as it ought to be"` or `"cannot
+initialize return object of type 'bool' with an rvalue of type 'void'"` or
+`"error: no viable conversion from 'void' to 'string'"`.
+
+If you need to use fatal assertions in a function that returns non-void, one
+option is to make the function return the value in an out parameter instead. For
+example, you can rewrite `T2 Foo(T1 x)` to `void Foo(T1 x, T2* result)`. You
+need to make sure that `*result` contains some sensible value even when the
+function returns prematurely. As the function now returns `void`, you can use
+any assertion inside of it.
+
+If changing the function's type is not an option, you should just use assertions
+that generate non-fatal failures, such as `ADD_FAILURE*` and `EXPECT_*`.
+
+NOTE: Constructors and destructors are not considered void-returning functions,
+according to the C++ language specification, and so you may not use fatal
+assertions in them. You'll get a compilation error if you try. A simple
+workaround is to transfer the entire body of the constructor or destructor to a
+private void-returning method. However, you should be aware that a fatal
+assertion failure in a constructor does not terminate the current test, as your
+intuition might suggest; it merely returns from the constructor early, possibly
+leaving your object in a partially-constructed state. Likewise, a fatal
+assertion failure in a destructor may leave your object in a
+partially-destructed state. Use assertions carefully in these situations!
+
+## Teaching googletest How to Print Your Values
+
+When a test assertion such as `EXPECT_EQ` fails, googletest prints the argument
+values to help you debug. It does this using a user-extensible value printer.
+
+This printer knows how to print built-in C++ types, native arrays, STL
+containers, and any type that supports the `<<` operator. For other types, it
+prints the raw bytes in the value and hopes that you the user can figure it out.
+
+As mentioned earlier, the printer is *extensible*. That means you can teach it
+to do a better job at printing your particular type than to dump the bytes. To
+do that, define `<<` for your type:
+
+```c++
+// Streams are allowed only for logging.  Don't include this for
+// any other purpose.
+#include <ostream>
+
+namespace foo {
+
+class Bar {  // We want googletest to be able to print instances of this.
+...
+  // Create a free inline friend function.
+  friend std::ostream& operator<<(std::ostream& os, const Bar& bar) {
+    return os << bar.DebugString();  // whatever needed to print bar to os
+  }
+};
+
+// If you can't declare the function in the class it's important that the
+// << operator is defined in the SAME namespace that defines Bar.  C++'s look-up
+// rules rely on that.
+std::ostream& operator<<(std::ostream& os, const Bar& bar) {
+  return os << bar.DebugString();  // whatever needed to print bar to os
+}
+
+}  // namespace foo
+```
+
+Sometimes, this might not be an option: your team may consider it bad style to
+have a `<<` operator for `Bar`, or `Bar` may already have a `<<` operator that
+doesn't do what you want (and you cannot change it). If so, you can instead
+define a `PrintTo()` function like this:
+
+```c++
+// Streams are allowed only for logging.  Don't include this for
+// any other purpose.
+#include <ostream>
+
+namespace foo {
+
+class Bar {
+  ...
+  friend void PrintTo(const Bar& bar, std::ostream* os) {
+    *os << bar.DebugString();  // whatever needed to print bar to os
+  }
+};
+
+// If you can't declare the function in the class it's important that PrintTo()
+// is defined in the SAME namespace that defines Bar.  C++'s look-up rules rely
+// on that.
+void PrintTo(const Bar& bar, std::ostream* os) {
+  *os << bar.DebugString();  // whatever needed to print bar to os
+}
+
+}  // namespace foo
+```
+
+If you have defined both `<<` and `PrintTo()`, the latter will be used when
+googletest is concerned. This allows you to customize how the value appears in
+googletest's output without affecting code that relies on the behavior of its
+`<<` operator.
+
+If you want to print a value `x` using googletest's value printer yourself, just
+call `::testing::PrintToString(x)`, which returns an `std::string`:
+
+```c++
+vector<pair<Bar, int> > bar_ints = GetBarIntVector();
+
+EXPECT_TRUE(IsCorrectBarIntVector(bar_ints))
+    << "bar_ints = " << ::testing::PrintToString(bar_ints);
+```
+
+## Death Tests
+
+In many applications, there are assertions that can cause application failure if
+a condition is not met. These sanity checks, which ensure that the program is in
+a known good state, are there to fail at the earliest possible time after some
+program state is corrupted. If the assertion checks the wrong condition, then
+the program may proceed in an erroneous state, which could lead to memory
+corruption, security holes, or worse. Hence it is vitally important to test that
+such assertion statements work as expected.
+
+Since these precondition checks cause the processes to die, we call such tests
+_death tests_. More generally, any test that checks that a program terminates
+(except by throwing an exception) in an expected fashion is also a death test.
+
+
+Note that if a piece of code throws an exception, we don't consider it "death"
+for the purpose of death tests, as the caller of the code could catch the
+exception and avoid the crash. If you want to verify exceptions thrown by your
+code, see [Exception Assertions](#exception-assertions).
+
+If you want to test `EXPECT_*()/ASSERT_*()` failures in your test code, see
+Catching Failures
+
+### How to Write a Death Test
+
+googletest has the following macros to support death tests:
+
+Fatal assertion                                | Nonfatal assertion                             | Verifies
+---------------------------------------------- | ---------------------------------------------- | --------
+`ASSERT_DEATH(statement, regex);`              | `EXPECT_DEATH(statement, regex);`              | `statement` crashes with the given error
+`ASSERT_DEATH_IF_SUPPORTED(statement, regex);` | `EXPECT_DEATH_IF_SUPPORTED(statement, regex);` | if death tests are supported, verifies that `statement` crashes with the given error; otherwise verifies nothing
+`ASSERT_EXIT(statement, predicate, regex);`    | `EXPECT_EXIT(statement, predicate, regex);`    | `statement` exits with the given error, and its exit code matches `predicate`
+
+where `statement` is a statement that is expected to cause the process to die,
+`predicate` is a function or function object that evaluates an integer exit
+status, and `regex` is a (Perl) regular expression that the stderr output of
+`statement` is expected to match. Note that `statement` can be *any valid
+statement* (including *compound statement*) and doesn't have to be an
+expression.
+
+
+As usual, the `ASSERT` variants abort the current test function, while the
+`EXPECT` variants do not.
+
+> NOTE: We use the word "crash" here to mean that the process terminates with a
+> *non-zero* exit status code. There are two possibilities: either the process
+> has called `exit()` or `_exit()` with a non-zero value, or it may be killed by
+> a signal.
+>
+> This means that if `*statement*` terminates the process with a 0 exit code, it
+> is *not* considered a crash by `EXPECT_DEATH`. Use `EXPECT_EXIT` instead if
+> this is the case, or if you want to restrict the exit code more precisely.
+
+A predicate here must accept an `int` and return a `bool`. The death test
+succeeds only if the predicate returns `true`. googletest defines a few
+predicates that handle the most common cases:
+
+```c++
+::testing::ExitedWithCode(exit_code)
+```
+
+This expression is `true` if the program exited normally with the given exit
+code.
+
+```c++
+::testing::KilledBySignal(signal_number)  // Not available on Windows.
+```
+
+This expression is `true` if the program was killed by the given signal.
+
+The `*_DEATH` macros are convenient wrappers for `*_EXIT` that use a predicate
+that verifies the process' exit code is non-zero.
+
+Note that a death test only cares about three things:
+
+1.  does `statement` abort or exit the process?
+2.  (in the case of `ASSERT_EXIT` and `EXPECT_EXIT`) does the exit status
+    satisfy `predicate`? Or (in the case of `ASSERT_DEATH` and `EXPECT_DEATH`)
+    is the exit status non-zero? And
+3.  does the stderr output match `regex`?
+
+In particular, if `statement` generates an `ASSERT_*` or `EXPECT_*` failure, it
+will **not** cause the death test to fail, as googletest assertions don't abort
+the process.
+
+To write a death test, simply use one of the above macros inside your test
+function. For example,
+
+```c++
+TEST(MyDeathTest, Foo) {
+  // This death test uses a compound statement.
+  ASSERT_DEATH({
+    int n = 5;
+    Foo(&n);
+  }, "Error on line .* of Foo()");
+}
+
+TEST(MyDeathTest, NormalExit) {
+  EXPECT_EXIT(NormalExit(), ::testing::ExitedWithCode(0), "Success");
+}
+
+TEST(MyDeathTest, KillMyself) {
+  EXPECT_EXIT(KillMyself(), ::testing::KilledBySignal(SIGKILL),
+              "Sending myself unblockable signal");
+}
+```
+
+verifies that:
+
+*   calling `Foo(5)` causes the process to die with the given error message,
+*   calling `NormalExit()` causes the process to print `"Success"` to stderr and
+    exit with exit code 0, and
+*   calling `KillMyself()` kills the process with signal `SIGKILL`.
+
+The test function body may contain other assertions and statements as well, if
+necessary.
+
+### Death Test Naming
+
+IMPORTANT: We strongly recommend you to follow the convention of naming your
+**test case** (not test) `*DeathTest` when it contains a death test, as
+demonstrated in the above example. The [Death Tests And
+Threads](#death-tests-and-threads) section below explains why.
+
+If a test fixture class is shared by normal tests and death tests, you can use
+`using` or `typedef` to introduce an alias for the fixture class and avoid
+duplicating its code:
+
+```c++
+class FooTest : public ::testing::Test { ... };
+
+using FooDeathTest = FooTest;
+
+TEST_F(FooTest, DoesThis) {
+  // normal test
+}
+
+TEST_F(FooDeathTest, DoesThat) {
+  // death test
+}
+```
+
+**Availability**: Linux, Windows (requires MSVC 8.0 or above), Cygwin, and Mac
+
+### Regular Expression Syntax
+
+
+On POSIX systems (e.g. Linux, Cygwin, and Mac), googletest uses the
+[POSIX extended regular expression](http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04)
+syntax. To learn about this syntax, you may want to read this
+[Wikipedia entry](http://en.wikipedia.org/wiki/Regular_expression#POSIX_Extended_Regular_Expressions).
+
+On Windows, googletest uses its own simple regular expression implementation. It
+lacks many features. For example, we don't support union (`"x|y"`), grouping
+(`"(xy)"`), brackets (`"[xy]"`), and repetition count (`"x{5,7}"`), among
+others. Below is what we do support (`A` denotes a literal character, period
+(`.`), or a single `\\ ` escape sequence; `x` and `y` denote regular
+expressions.):
+
+Expression | Meaning
+---------- | --------------------------------------------------------------
+`c`        | matches any literal character `c`
+`\\d`      | matches any decimal digit
+`\\D`      | matches any character that's not a decimal digit
+`\\f`      | matches `\f`
+`\\n`      | matches `\n`
+`\\r`      | matches `\r`
+`\\s`      | matches any ASCII whitespace, including `\n`
+`\\S`      | matches any character that's not a whitespace
+`\\t`      | matches `\t`
+`\\v`      | matches `\v`
+`\\w`      | matches any letter, `_`, or decimal digit
+`\\W`      | matches any character that `\\w` doesn't match
+`\\c`      | matches any literal character `c`, which must be a punctuation
+`.`        | matches any single character except `\n`
+`A?`       | matches 0 or 1 occurrences of `A`
+`A*`       | matches 0 or many occurrences of `A`
+`A+`       | matches 1 or many occurrences of `A`
+`^`        | matches the beginning of a string (not that of each line)
+`$`        | matches the end of a string (not that of each line)
+`xy`       | matches `x` followed by `y`
+
+To help you determine which capability is available on your system, googletest
+defines macros to govern which regular expression it is using. The macros are:
+<!--absl:google3-begin(google3-only)-->`GTEST_USES_PCRE=1`, or
+<!--absl:google3-end--> `GTEST_USES_SIMPLE_RE=1` or `GTEST_USES_POSIX_RE=1`. If
+you want your death tests to work in all cases, you can either `#if` on these
+macros or use the more limited syntax only.
+
+### How It Works
+
+Under the hood, `ASSERT_EXIT()` spawns a new process and executes the death test
+statement in that process. The details of how precisely that happens depend on
+the platform and the variable ::testing::GTEST_FLAG(death_test_style) (which is
+initialized from the command-line flag `--gtest_death_test_style`).
+
+*   On POSIX systems, `fork()` (or `clone()` on Linux) is used to spawn the
+    child, after which:
+    *   If the variable's value is `"fast"`, the death test statement is
+        immediately executed.
+    *   If the variable's value is `"threadsafe"`, the child process re-executes
+        the unit test binary just as it was originally invoked, but with some
+        extra flags to cause just the single death test under consideration to
+        be run.
+*   On Windows, the child is spawned using the `CreateProcess()` API, and
+    re-executes the binary to cause just the single death test under
+    consideration to be run - much like the `threadsafe` mode on POSIX.
+
+Other values for the variable are illegal and will cause the death test to fail.
+Currently, the flag's default value is
+"fast". However, we reserve
+the right to change it in the future. Therefore, your tests should not depend on
+this. In either case, the parent process waits for the child process to
+complete, and checks that
+
+1.  the child's exit status satisfies the predicate, and
+2.  the child's stderr matches the regular expression.
+
+If the death test statement runs to completion without dying, the child process
+will nonetheless terminate, and the assertion fails.
+
+### Death Tests And Threads
+
+The reason for the two death test styles has to do with thread safety. Due to
+well-known problems with forking in the presence of threads, death tests should
+be run in a single-threaded context. Sometimes, however, it isn't feasible to
+arrange that kind of environment. For example, statically-initialized modules
+may start threads before main is ever reached. Once threads have been created,
+it may be difficult or impossible to clean them up.
+
+googletest has three features intended to raise awareness of threading issues.
+
+1.  A warning is emitted if multiple threads are running when a death test is
+    encountered.
+2.  Test cases with a name ending in "DeathTest" are run before all other tests.
+3.  It uses `clone()` instead of `fork()` to spawn the child process on Linux
+    (`clone()` is not available on Cygwin and Mac), as `fork()` is more likely
+    to cause the child to hang when the parent process has multiple threads.
+
+It's perfectly fine to create threads inside a death test statement; they are
+executed in a separate process and cannot affect the parent.
+
+### Death Test Styles
+
+
+The "threadsafe" death test style was introduced in order to help mitigate the
+risks of testing in a possibly multithreaded environment. It trades increased
+test execution time (potentially dramatically so) for improved thread safety.
+
+The automated testing framework does not set the style flag. You can choose a
+particular style of death tests by setting the flag programmatically:
+
+```c++
+testing::FLAGS_gtest_death_test_style="threadsafe"
+```
+
+You can do this in `main()` to set the style for all death tests in the binary,
+or in individual tests. Recall that flags are saved before running each test and
+restored afterwards, so you need not do that yourself. For example:
+
+```c++
+int main(int argc, char** argv) {
+  InitGoogle(argv[0], &argc, &argv, true);
+  ::testing::FLAGS_gtest_death_test_style = "fast";
+  return RUN_ALL_TESTS();
+}
+
+TEST(MyDeathTest, TestOne) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  // This test is run in the "threadsafe" style:
+  ASSERT_DEATH(ThisShouldDie(), "");
+}
+
+TEST(MyDeathTest, TestTwo) {
+  // This test is run in the "fast" style:
+  ASSERT_DEATH(ThisShouldDie(), "");
+}
+```
+
+
+### Caveats
+
+The `statement` argument of `ASSERT_EXIT()` can be any valid C++ statement. If
+it leaves the current function via a `return` statement or by throwing an
+exception, the death test is considered to have failed. Some googletest macros
+may return from the current function (e.g. `ASSERT_TRUE()`), so be sure to avoid
+them in `statement`.
+
+Since `statement` runs in the child process, any in-memory side effect (e.g.
+modifying a variable, releasing memory, etc) it causes will *not* be observable
+in the parent process. In particular, if you release memory in a death test,
+your program will fail the heap check as the parent process will never see the
+memory reclaimed. To solve this problem, you can
+
+1.  try not to free memory in a death test;
+2.  free the memory again in the parent process; or
+3.  do not use the heap checker in your program.
+
+Due to an implementation detail, you cannot place multiple death test assertions
+on the same line; otherwise, compilation will fail with an unobvious error
+message.
+
+Despite the improved thread safety afforded by the "threadsafe" style of death
+test, thread problems such as deadlock are still possible in the presence of
+handlers registered with `pthread_atfork(3)`.
+
+
+## Using Assertions in Sub-routines
+
+### Adding Traces to Assertions
+
+If a test sub-routine is called from several places, when an assertion inside it
+fails, it can be hard to tell which invocation of the sub-routine the failure is
+from. 
+You can alleviate this problem using extra logging or custom failure messages,
+but that usually clutters up your tests. A better solution is to use the
+`SCOPED_TRACE` macro or the `ScopedTrace` utility:
+
+```c++
+SCOPED_TRACE(message);
+ScopedTrace trace("file_path", line_number, message);
+```
+
+where `message` can be anything streamable to `std::ostream`. `SCOPED_TRACE`
+macro will cause the current file name, line number, and the given message to be
+added in every failure message. `ScopedTrace` accepts explicit file name and
+line number in arguments, which is useful for writing test helpers. The effect
+will be undone when the control leaves the current lexical scope.
+
+For example,
+
+```c++
+10: void Sub1(int n) {
+11:   EXPECT_EQ(1, Bar(n));
+12:   EXPECT_EQ(2, Bar(n + 1));
+13: }
+14:
+15: TEST(FooTest, Bar) {
+16:   {
+17:     SCOPED_TRACE("A");  // This trace point will be included in
+18:                         // every failure in this scope.
+19:     Sub1(1);
+20:   }
+21:   // Now it won't.
+22:   Sub1(9);
+23: }
+```
+
+could result in messages like these:
+
+```none
+path/to/foo_test.cc:11: Failure
+Value of: Bar(n)
+Expected: 1
+  Actual: 2
+   Trace:
+path/to/foo_test.cc:17: A
+
+path/to/foo_test.cc:12: Failure
+Value of: Bar(n + 1)
+Expected: 2
+  Actual: 3
+```
+
+Without the trace, it would've been difficult to know which invocation of
+`Sub1()` the two failures come from respectively. (You could add
+
+an extra message to each assertion in `Sub1()` to indicate the value of `n`, but
+that's tedious.)
+
+Some tips on using `SCOPED_TRACE`:
+
+1.  With a suitable message, it's often enough to use `SCOPED_TRACE` at the
+    beginning of a sub-routine, instead of at each call site.
+2.  When calling sub-routines inside a loop, make the loop iterator part of the
+    message in `SCOPED_TRACE` such that you can know which iteration the failure
+    is from.
+3.  Sometimes the line number of the trace point is enough for identifying the
+    particular invocation of a sub-routine. In this case, you don't have to
+    choose a unique message for `SCOPED_TRACE`. You can simply use `""`.
+4.  You can use `SCOPED_TRACE` in an inner scope when there is one in the outer
+    scope. In this case, all active trace points will be included in the failure
+    messages, in reverse order they are encountered.
+5.  The trace dump is clickable in Emacs - hit `return` on a line number and
+    you'll be taken to that line in the source file!
+
+**Availability**: Linux, Windows, Mac.
+
+### Propagating Fatal Failures
+
+A common pitfall when using `ASSERT_*` and `FAIL*` is not understanding that
+when they fail they only abort the _current function_, not the entire test. For
+example, the following test will segfault:
+
+```c++
+void Subroutine() {
+  // Generates a fatal failure and aborts the current function.
+  ASSERT_EQ(1, 2);
+
+  // The following won't be executed.
+  ...
+}
+
+TEST(FooTest, Bar) {
+  Subroutine();  // The intended behavior is for the fatal failure
+                 // in Subroutine() to abort the entire test.
+
+  // The actual behavior: the function goes on after Subroutine() returns.
+  int* p = NULL;
+  *p = 3;  // Segfault!
+}
+```
+
+To alleviate this, googletest provides three different solutions. You could use
+either exceptions, the `(ASSERT|EXPECT)_NO_FATAL_FAILURE` assertions or the
+`HasFatalFailure()` function. They are described in the following two
+subsections.
+
+#### Asserting on Subroutines with an exception
+
+The following code can turn ASSERT-failure into an exception:
+
+```c++
+class ThrowListener : public testing::EmptyTestEventListener {
+  void OnTestPartResult(const testing::TestPartResult& result) override {
+    if (result.type() == testing::TestPartResult::kFatalFailure) {
+      throw testing::AssertionException(result);
+    }
+  }
+};
+int main(int argc, char** argv) {
+  ...
+  testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener);
+  return RUN_ALL_TESTS();
+}
+```
+
+This listener should be added after other listeners if you have any, otherwise
+they won't see failed `OnTestPartResult`.
+
+#### Asserting on Subroutines
+
+As shown above, if your test calls a subroutine that has an `ASSERT_*` failure
+in it, the test will continue after the subroutine returns. This may not be what
+you want.
+
+Often people want fatal failures to propagate like exceptions. For that
+googletest offers the following macros:
+
+Fatal assertion                       | Nonfatal assertion                    | Verifies
+------------------------------------- | ------------------------------------- | --------
+`ASSERT_NO_FATAL_FAILURE(statement);` | `EXPECT_NO_FATAL_FAILURE(statement);` | `statement` doesn't generate any new fatal failures in the current thread.
+
+Only failures in the thread that executes the assertion are checked to determine
+the result of this type of assertions. If `statement` creates new threads,
+failures in these threads are ignored.
+
+Examples:
+
+```c++
+ASSERT_NO_FATAL_FAILURE(Foo());
+
+int i;
+EXPECT_NO_FATAL_FAILURE({
+  i = Bar();
+});
+```
+
+**Availability**: Linux, Windows, Mac. Assertions from multiple threads are
+currently not supported on Windows.
+
+#### Checking for Failures in the Current Test
+
+`HasFatalFailure()` in the `::testing::Test` class returns `true` if an
+assertion in the current test has suffered a fatal failure. This allows
+functions to catch fatal failures in a sub-routine and return early.
+
+```c++
+class Test {
+ public:
+  ...
+  static bool HasFatalFailure();
+};
+```
+
+The typical usage, which basically simulates the behavior of a thrown exception,
+is:
+
+```c++
+TEST(FooTest, Bar) {
+  Subroutine();
+  // Aborts if Subroutine() had a fatal failure.
+  if (HasFatalFailure()) return;
+
+  // The following won't be executed.
+  ...
+}
+```
+
+If `HasFatalFailure()` is used outside of `TEST()` , `TEST_F()` , or a test
+fixture, you must add the `::testing::Test::` prefix, as in:
+
+```c++
+if (::testing::Test::HasFatalFailure()) return;
+```
+
+Similarly, `HasNonfatalFailure()` returns `true` if the current test has at
+least one non-fatal failure, and `HasFailure()` returns `true` if the current
+test has at least one failure of either kind.
+
+**Availability**: Linux, Windows, Mac.
+
+## Logging Additional Information
+
+In your test code, you can call `RecordProperty("key", value)` to log additional
+information, where `value` can be either a string or an `int`. The *last* value
+recorded for a key will be emitted to the [XML output](#generating-an-xml-report) if you
+specify one. For example, the test
+
+```c++
+TEST_F(WidgetUsageTest, MinAndMaxWidgets) {
+  RecordProperty("MaximumWidgets", ComputeMaxUsage());
+  RecordProperty("MinimumWidgets", ComputeMinUsage());
+}
+```
+
+will output XML like this:
+
+```xml
+  ...
+    <testcase name="MinAndMaxWidgets" status="run" time="0.006" classname="WidgetUsageTest" MaximumWidgets="12" MinimumWidgets="9" />
+  ...
+```
+
+> NOTE:
+>
+> *   `RecordProperty()` is a static member of the `Test` class. Therefore it
+>     needs to be prefixed with `::testing::Test::` if used outside of the
+>     `TEST` body and the test fixture class.
+> *   `*key*` must be a valid XML attribute name, and cannot conflict with the
+>     ones already used by googletest (`name`, `status`, `time`, `classname`,
+>     `type_param`, and `value_param`).
+> *   Calling `RecordProperty()` outside of the lifespan of a test is allowed.
+>     If it's called outside of a test but between a test case's
+>     `SetUpTestCase()` and `TearDownTestCase()` methods, it will be attributed
+>     to the XML element for the test case. If it's called outside of all test
+>     cases (e.g. in a test environment), it will be attributed to the top-level
+>     XML element.
+
+**Availability**: Linux, Windows, Mac.
+
+## Sharing Resources Between Tests in the Same Test Case
+
+googletest creates a new test fixture object for each test in order to make
+tests independent and easier to debug. However, sometimes tests use resources
+that are expensive to set up, making the one-copy-per-test model prohibitively
+expensive.
+
+If the tests don't change the resource, there's no harm in their sharing a
+single resource copy. So, in addition to per-test set-up/tear-down, googletest
+also supports per-test-case set-up/tear-down. To use it:
+
+1.  In your test fixture class (say `FooTest` ), declare as `static` some member
+    variables to hold the shared resources.
+1.  Outside your test fixture class (typically just below it), define those
+    member variables, optionally giving them initial values.
+1.  In the same test fixture class, define a `static void SetUpTestCase()`
+    function (remember not to spell it as **`SetupTestCase`** with a small `u`!)
+    to set up the shared resources and a `static void TearDownTestCase()`
+    function to tear them down.
+
+That's it! googletest automatically calls `SetUpTestCase()` before running the
+*first test* in the `FooTest` test case (i.e. before creating the first
+`FooTest` object), and calls `TearDownTestCase()` after running the *last test*
+in it (i.e. after deleting the last `FooTest` object). In between, the tests can
+use the shared resources.
+
+Remember that the test order is undefined, so your code can't depend on a test
+preceding or following another. Also, the tests must either not modify the state
+of any shared resource, or, if they do modify the state, they must restore the
+state to its original value before passing control to the next test.
+
+Here's an example of per-test-case set-up and tear-down:
+
+```c++
+class FooTest : public ::testing::Test {
+ protected:
+  // Per-test-case set-up.
+  // Called before the first test in this test case.
+  // Can be omitted if not needed.
+  static void SetUpTestCase() {
+    shared_resource_ = new ...;
+  }
+
+  // Per-test-case tear-down.
+  // Called after the last test in this test case.
+  // Can be omitted if not needed.
+  static void TearDownTestCase() {
+    delete shared_resource_;
+    shared_resource_ = NULL;
+  }
+
+  // You can define per-test set-up logic as usual.
+  virtual void SetUp() { ... }
+
+  // You can define per-test tear-down logic as usual.
+  virtual void TearDown() { ... }
+
+  // Some expensive resource shared by all tests.
+  static T* shared_resource_;
+};
+
+T* FooTest::shared_resource_ = NULL;
+
+TEST_F(FooTest, Test1) {
+  ... you can refer to shared_resource_ here ...
+}
+
+TEST_F(FooTest, Test2) {
+  ... you can refer to shared_resource_ here ...
+}
+```
+
+NOTE: Though the above code declares `SetUpTestCase()` protected, it may
+sometimes be necessary to declare it public, such as when using it with
+`TEST_P`.
+
+**Availability**: Linux, Windows, Mac.
+
+## Global Set-Up and Tear-Down
+
+Just as you can do set-up and tear-down at the test level and the test case
+level, you can also do it at the test program level. Here's how.
+
+First, you subclass the `::testing::Environment` class to define a test
+environment, which knows how to set-up and tear-down:
+
+```c++
+class Environment {
+ public:
+  virtual ~Environment() {}
+
+  // Override this to define how to set up the environment.
+  virtual void SetUp() {}
+
+  // Override this to define how to tear down the environment.
+  virtual void TearDown() {}
+};
+```
+
+Then, you register an instance of your environment class with googletest by
+calling the `::testing::AddGlobalTestEnvironment()` function:
+
+```c++
+Environment* AddGlobalTestEnvironment(Environment* env);
+```
+
+Now, when `RUN_ALL_TESTS()` is called, it first calls the `SetUp()` method of
+the environment object, then runs the tests if there was no fatal failures, and
+finally calls `TearDown()` of the environment object.
+
+It's OK to register multiple environment objects. In this case, their `SetUp()`
+will be called in the order they are registered, and their `TearDown()` will be
+called in the reverse order.
+
+Note that googletest takes ownership of the registered environment objects.
+Therefore **do not delete them** by yourself.
+
+You should call `AddGlobalTestEnvironment()` before `RUN_ALL_TESTS()` is called,
+probably in `main()`. If you use `gtest_main`, you need to call this before
+`main()` starts for it to take effect. One way to do this is to define a global
+variable like this:
+
+```c++
+::testing::Environment* const foo_env =
+    ::testing::AddGlobalTestEnvironment(new FooEnvironment);
+```
+
+However, we strongly recommend you to write your own `main()` and call
+`AddGlobalTestEnvironment()` there, as relying on initialization of global
+variables makes the code harder to read and may cause problems when you register
+multiple environments from different translation units and the environments have
+dependencies among them (remember that the compiler doesn't guarantee the order
+in which global variables from different translation units are initialized).
+
+## Value-Parameterized Tests
+
+*Value-parameterized tests* allow you to test your code with different
+parameters without writing multiple copies of the same test. This is useful in a
+number of situations, for example:
+
+*   You have a piece of code whose behavior is affected by one or more
+    command-line flags. You want to make sure your code performs correctly for
+    various values of those flags.
+*   You want to test different implementations of an OO interface.
+*   You want to test your code over various inputs (a.k.a. data-driven testing).
+    This feature is easy to abuse, so please exercise your good sense when doing
+    it!
+
+### How to Write Value-Parameterized Tests
+
+To write value-parameterized tests, first you should define a fixture class. It
+must be derived from both `::testing::Test` and
+`::testing::WithParamInterface<T>` (the latter is a pure interface), where `T`
+is the type of your parameter values. For convenience, you can just derive the
+fixture class from `::testing::TestWithParam<T>`, which itself is derived from
+both `::testing::Test` and `::testing::WithParamInterface<T>`. `T` can be any
+copyable type. If it's a raw pointer, you are responsible for managing the
+lifespan of the pointed values.
+
+NOTE: If your test fixture defines `SetUpTestCase()` or `TearDownTestCase()`
+they must be declared **public** rather than **protected** in order to use
+`TEST_P`.
+
+```c++
+class FooTest :
+    public ::testing::TestWithParam<const char*> {
+  // You can implement all the usual fixture class members here.
+  // To access the test parameter, call GetParam() from class
+  // TestWithParam<T>.
+};
+
+// Or, when you want to add parameters to a pre-existing fixture class:
+class BaseTest : public ::testing::Test {
+  ...
+};
+class BarTest : public BaseTest,
+                public ::testing::WithParamInterface<const char*> {
+  ...
+};
+```
+
+Then, use the `TEST_P` macro to define as many test patterns using this fixture
+as you want. The `_P` suffix is for "parameterized" or "pattern", whichever you
+prefer to think.
+
+```c++
+TEST_P(FooTest, DoesBlah) {
+  // Inside a test, access the test parameter with the GetParam() method
+  // of the TestWithParam<T> class:
+  EXPECT_TRUE(foo.Blah(GetParam()));
+  ...
+}
+
+TEST_P(FooTest, HasBlahBlah) {
+  ...
+}
+```
+
+Finally, you can use `INSTANTIATE_TEST_CASE_P` to instantiate the test case with
+any set of parameters you want. googletest defines a number of functions for
+generating test parameters. They return what we call (surprise!) *parameter
+generators*. Here is a summary of them, which are all in the `testing`
+namespace:
+
+| Parameter Generator          | Behavior                                    |
+| ---------------------------- | ------------------------------------------- |
+| `Range(begin, end [, step])` | Yields values `{begin, begin+step, begin+step+step, ...}`. The values do not include `end`. `step` defaults to 1.      |
+| `Values(v1, v2, ..., vN)`    | Yields values `{v1, v2, ..., vN}`.          |
+| `ValuesIn(container)` and `ValuesIn(begin,end)`   | Yields values from a C-style array, an STL-style container, or an iterator range  `[begin, end)`. |
+| `Bool()`                     | Yields sequence `{false, true}`.            |
+| `Combine(g1, g2, ..., gN)`   | Yields all combinations (Cartesian product) as std\:\:tuples of the values generated by the `N` generators.            |
+
+For more details, see the comments at the definitions of these functions.
+
+The following statement will instantiate tests from the `FooTest` test case each
+with parameter values `"meeny"`, `"miny"`, and `"moe"`.
+
+```c++
+INSTANTIATE_TEST_CASE_P(InstantiationName,
+                        FooTest,
+                        ::testing::Values("meeny", "miny", "moe"));
+```
+
+NOTE: The code above must be placed at global or namespace scope, not at
+function scope.
+
+NOTE: Don't forget this step! If you do your test will silently pass, but none
+of its cases will ever run!
+
+To distinguish different instances of the pattern (yes, you can instantiate it
+more than once), the first argument to `INSTANTIATE_TEST_CASE_P` is a prefix
+that will be added to the actual test case name. Remember to pick unique
+prefixes for different instantiations. The tests from the instantiation above
+will have these names:
+
+*   `InstantiationName/FooTest.DoesBlah/0` for `"meeny"`
+*   `InstantiationName/FooTest.DoesBlah/1` for `"miny"`
+*   `InstantiationName/FooTest.DoesBlah/2` for `"moe"`
+*   `InstantiationName/FooTest.HasBlahBlah/0` for `"meeny"`
+*   `InstantiationName/FooTest.HasBlahBlah/1` for `"miny"`
+*   `InstantiationName/FooTest.HasBlahBlah/2` for `"moe"`
+
+You can use these names in [`--gtest_filter`](#running-a-subset-of-the-tests).
+
+This statement will instantiate all tests from `FooTest` again, each with
+parameter values `"cat"` and `"dog"`:
+
+```c++
+const char* pets[] = {"cat", "dog"};
+INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest,
+                        ::testing::ValuesIn(pets));
+```
+
+The tests from the instantiation above will have these names:
+
+*   `AnotherInstantiationName/FooTest.DoesBlah/0` for `"cat"`
+*   `AnotherInstantiationName/FooTest.DoesBlah/1` for `"dog"`
+*   `AnotherInstantiationName/FooTest.HasBlahBlah/0` for `"cat"`
+*   `AnotherInstantiationName/FooTest.HasBlahBlah/1` for `"dog"`
+
+Please note that `INSTANTIATE_TEST_CASE_P` will instantiate *all* tests in the
+given test case, whether their definitions come before or *after* the
+`INSTANTIATE_TEST_CASE_P` statement.
+
+You can see sample7_unittest.cc and sample8_unittest.cc for more examples.
+
+**Availability**: Linux, Windows (requires MSVC 8.0 or above), Mac
+
+### Creating Value-Parameterized Abstract Tests
+
+In the above, we define and instantiate `FooTest` in the *same* source file.
+Sometimes you may want to define value-parameterized tests in a library and let
+other people instantiate them later. This pattern is known as *abstract tests*.
+As an example of its application, when you are designing an interface you can
+write a standard suite of abstract tests (perhaps using a factory function as
+the test parameter) that all implementations of the interface are expected to
+pass. When someone implements the interface, they can instantiate your suite to
+get all the interface-conformance tests for free.
+
+To define abstract tests, you should organize your code like this:
+
+1.  Put the definition of the parameterized test fixture class (e.g. `FooTest`)
+    in a header file, say `foo_param_test.h`. Think of this as *declaring* your
+    abstract tests.
+1.  Put the `TEST_P` definitions in `foo_param_test.cc`, which includes
+    `foo_param_test.h`. Think of this as *implementing* your abstract tests.
+
+Once they are defined, you can instantiate them by including `foo_param_test.h`,
+invoking `INSTANTIATE_TEST_CASE_P()`, and depending on the library target that
+contains `foo_param_test.cc`. You can instantiate the same abstract test case
+multiple times, possibly in different source files.
+
+### Specifying Names for Value-Parameterized Test Parameters
+
+The optional last argument to `INSTANTIATE_TEST_CASE_P()` allows the user to
+specify a function or functor that generates custom test name suffixes based on
+the test parameters. The function should accept one argument of type
+`testing::TestParamInfo<class ParamType>`, and return `std::string`.
+
+`testing::PrintToStringParamName` is a builtin test suffix generator that
+returns the value of `testing::PrintToString(GetParam())`. It does not work for
+`std::string` or C strings.
+
+NOTE: test names must be non-empty, unique, and may only contain ASCII
+alphanumeric characters. In particular, they [should not contain
+underscores](https://g3doc.corp.google.com/third_party/googletest/googletest/g3doc/faq.md#no-underscores).
+
+```c++
+class MyTestCase : public testing::TestWithParam<int> {};
+
+TEST_P(MyTestCase, MyTest)
+{
+  std::cout << "Example Test Param: " << GetParam() << std::endl;
+}
+
+INSTANTIATE_TEST_CASE_P(MyGroup, MyTestCase, testing::Range(0, 10),
+                        testing::PrintToStringParamName());
+```
+
+## Typed Tests</id>
+
+Suppose you have multiple implementations of the same interface and want to make
+sure that all of them satisfy some common requirements. Or, you may have defined
+several types that are supposed to conform to the same "concept" and you want to
+verify it. In both cases, you want the same test logic repeated for different
+types.
+
+While you can write one `TEST` or `TEST_F` for each type you want to test (and
+you may even factor the test logic into a function template that you invoke from
+the `TEST`), it's tedious and doesn't scale: if you want `m` tests over `n`
+types, you'll end up writing `m*n` `TEST`s.
+
+*Typed tests* allow you to repeat the same test logic over a list of types. You
+only need to write the test logic once, although you must know the type list
+when writing typed tests. Here's how you do it:
+
+First, define a fixture class template. It should be parameterized by a type.
+Remember to derive it from `::testing::Test`:
+
+```c++
+template <typename T>
+class FooTest : public ::testing::Test {
+ public:
+  ...
+  typedef std::list<T> List;
+  static T shared_;
+  T value_;
+};
+```
+
+Next, associate a list of types with the test case, which will be repeated for
+each type in the list:
+
+```c++
+using MyTypes = ::testing::Types<char, int, unsigned int>;
+TYPED_TEST_CASE(FooTest, MyTypes);
+```
+
+The type alias (`using` or `typedef`) is necessary for the `TYPED_TEST_CASE`
+macro to parse correctly. Otherwise the compiler will think that each comma in
+the type list introduces a new macro argument.
+
+Then, use `TYPED_TEST()` instead of `TEST_F()` to define a typed test for this
+test case. You can repeat this as many times as you want:
+
+```c++
+TYPED_TEST(FooTest, DoesBlah) {
+  // Inside a test, refer to the special name TypeParam to get the type
+  // parameter.  Since we are inside a derived class template, C++ requires
+  // us to visit the members of FooTest via 'this'.
+  TypeParam n = this->value_;
+
+  // To visit static members of the fixture, add the 'TestFixture::'
+  // prefix.
+  n += TestFixture::shared_;
+
+  // To refer to typedefs in the fixture, add the 'typename TestFixture::'
+  // prefix.  The 'typename' is required to satisfy the compiler.
+  typename TestFixture::List values;
+
+  values.push_back(n);
+  ...
+}
+
+TYPED_TEST(FooTest, HasPropertyA) { ... }
+```
+
+You can see sample6_unittest.cc
+
+**Availability**: Linux, Windows (requires MSVC 8.0 or above), Mac
+
+## Type-Parameterized Tests
+
+*Type-parameterized tests* are like typed tests, except that they don't require
+you to know the list of types ahead of time. Instead, you can define the test
+logic first and instantiate it with different type lists later. You can even
+instantiate it more than once in the same program.
+
+If you are designing an interface or concept, you can define a suite of
+type-parameterized tests to verify properties that any valid implementation of
+the interface/concept should have. Then, the author of each implementation can
+just instantiate the test suite with their type to verify that it conforms to
+the requirements, without having to write similar tests repeatedly. Here's an
+example:
+
+First, define a fixture class template, as we did with typed tests:
+
+```c++
+template <typename T>
+class FooTest : public ::testing::Test {
+  ...
+};
+```
+
+Next, declare that you will define a type-parameterized test case:
+
+```c++
+TYPED_TEST_CASE_P(FooTest);
+```
+
+Then, use `TYPED_TEST_P()` to define a type-parameterized test. You can repeat
+this as many times as you want:
+
+```c++
+TYPED_TEST_P(FooTest, DoesBlah) {
+  // Inside a test, refer to TypeParam to get the type parameter.
+  TypeParam n = 0;
+  ...
+}
+
+TYPED_TEST_P(FooTest, HasPropertyA) { ... }
+```
+
+Now the tricky part: you need to register all test patterns using the
+`REGISTER_TYPED_TEST_CASE_P` macro before you can instantiate them. The first
+argument of the macro is the test case name; the rest are the names of the tests
+in this test case:
+
+```c++
+REGISTER_TYPED_TEST_CASE_P(FooTest,
+                           DoesBlah, HasPropertyA);
+```
+
+Finally, you are free to instantiate the pattern with the types you want. If you
+put the above code in a header file, you can `#include` it in multiple C++
+source files and instantiate it multiple times.
+
+```c++
+typedef ::testing::Types<char, int, unsigned int> MyTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
+```
+
+To distinguish different instances of the pattern, the first argument to the
+`INSTANTIATE_TYPED_TEST_CASE_P` macro is a prefix that will be added to the
+actual test case name. Remember to pick unique prefixes for different instances.
+
+In the special case where the type list contains only one type, you can write
+that type directly without `::testing::Types<...>`, like this:
+
+```c++
+INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
+```
+
+You can see `sample6_unittest.cc` for a complete example.
+
+**Availability**: Linux, Windows (requires MSVC 8.0 or above), Mac
+
+## Testing Private Code
+
+If you change your software's internal implementation, your tests should not
+break as long as the change is not observable by users. Therefore, **per the
+black-box testing principle, most of the time you should test your code through
+its public interfaces.**
+
+**If you still find yourself needing to test internal implementation code,
+consider if there's a better design.** The desire to test internal
+implementation is often a sign that the class is doing too much. Consider
+extracting an implementation class, and testing it. Then use that implementation
+class in the original class.
+
+If you absolutely have to test non-public interface code though, you can. There
+are two cases to consider:
+
+*   Static functions ( *not* the same as static member functions!) or unnamed
+    namespaces, and
+*   Private or protected class members
+
+To test them, we use the following special techniques:
+
+*   Both static functions and definitions/declarations in an unnamed namespace
+    are only visible within the same translation unit. To test them, you can
+    `#include` the entire `.cc` file being tested in your `*_test.cc` file.
+    (including `.cc` files is not a good way to reuse code - you should not do
+    this in production code!)
+
+    However, a better approach is to move the private code into the
+    `foo::internal` namespace, where `foo` is the namespace your project
+    normally uses, and put the private declarations in a `*-internal.h` file.
+    Your production `.cc` files and your tests are allowed to include this
+    internal header, but your clients are not. This way, you can fully test your
+    internal implementation without leaking it to your clients.
+
+*   Private class members are only accessible from within the class or by
+    friends. To access a class' private members, you can declare your test
+    fixture as a friend to the class and define accessors in your fixture. Tests
+    using the fixture can then access the private members of your production
+    class via the accessors in the fixture. Note that even though your fixture
+    is a friend to your production class, your tests are not automatically
+    friends to it, as they are technically defined in sub-classes of the
+    fixture.
+
+    Another way to test private members is to refactor them into an
+    implementation class, which is then declared in a `*-internal.h` file. Your
+    clients aren't allowed to include this header but your tests can. Such is
+    called the
+    [Pimpl](https://www.gamedev.net/articles/programming/general-and-gameplay-programming/the-c-pimpl-r1794/)
+    (Private Implementation) idiom.
+
+    Or, you can declare an individual test as a friend of your class by adding
+    this line in the class body:
+
+    ```c++
+        FRIEND_TEST(TestCaseName, TestName);
+    ```
+
+    For example,
+
+    ```c++
+    // foo.h
+
+    #include "gtest/gtest_prod.h"
+
+    class Foo {
+      ...
+    private:
+      FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
+
+      int Bar(void* x);
+    };
+
+    // foo_test.cc
+    ...
+    TEST(FooTest, BarReturnsZeroOnNull) {
+      Foo foo;
+      EXPECT_EQ(0, foo.Bar(NULL));  // Uses Foo's private member Bar().
+    }
+    ```
+
+    Pay special attention when your class is defined in a namespace, as you
+    should define your test fixtures and tests in the same namespace if you want
+    them to be friends of your class. For example, if the code to be tested
+    looks like:
+
+    ```c++
+    namespace my_namespace {
+
+    class Foo {
+      friend class FooTest;
+      FRIEND_TEST(FooTest, Bar);
+      FRIEND_TEST(FooTest, Baz);
+      ... definition of the class Foo ...
+    };
+
+    }  // namespace my_namespace
+    ```
+
+    Your test code should be something like:
+
+    ```c++
+    namespace my_namespace {
+
+    class FooTest : public ::testing::Test {
+     protected:
+      ...
+    };
+
+    TEST_F(FooTest, Bar) { ... }
+    TEST_F(FooTest, Baz) { ... }
+
+    }  // namespace my_namespace
+    ```
+
+
+## "Catching" Failures
+
+If you are building a testing utility on top of googletest, you'll want to test
+your utility. What framework would you use to test it? googletest, of course.
+
+The challenge is to verify that your testing utility reports failures correctly.
+In frameworks that report a failure by throwing an exception, you could catch
+the exception and assert on it. But googletest doesn't use exceptions, so how do
+we test that a piece of code generates an expected failure?
+
+gunit-spi.h contains some constructs to do this. After #including this header,
+you can use
+
+```c++
+  EXPECT_FATAL_FAILURE(statement, substring);
+```
+
+to assert that `statement` generates a fatal (e.g. `ASSERT_*`) failure in the
+current thread whose message contains the given `substring`, or use
+
+```c++
+  EXPECT_NONFATAL_FAILURE(statement, substring);
+```
+
+if you are expecting a non-fatal (e.g. `EXPECT_*`) failure.
+
+Only failures in the current thread are checked to determine the result of this
+type of expectations. If `statement` creates new threads, failures in these
+threads are also ignored. If you want to catch failures in other threads as
+well, use one of the following macros instead:
+
+```c++
+  EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substring);
+  EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substring);
+```
+
+NOTE: Assertions from multiple threads are currently not supported on Windows.
+
+For technical reasons, there are some caveats:
+
+1.  You cannot stream a failure message to either macro.
+
+1.  `statement` in `EXPECT_FATAL_FAILURE{_ON_ALL_THREADS}()` cannot reference
+    local non-static variables or non-static members of `this` object.
+
+1.  `statement` in `EXPECT_FATAL_FAILURE{_ON_ALL_THREADS}()()` cannot return a
+    value.
+
+
+## Getting the Current Test's Name
+
+Sometimes a function may need to know the name of the currently running test.
+For example, you may be using the `SetUp()` method of your test fixture to set
+the golden file name based on which test is running. The `::testing::TestInfo`
+class has this information:
+
+```c++
+namespace testing {
+
+class TestInfo {
+ public:
+  // Returns the test case name and the test name, respectively.
+  //
+  // Do NOT delete or free the return value - it's managed by the
+  // TestInfo class.
+  const char* test_case_name() const;
+  const char* name() const;
+};
+
+}
+```
+
+To obtain a `TestInfo` object for the currently running test, call
+`current_test_info()` on the `UnitTest` singleton object:
+
+```c++
+  // Gets information about the currently running test.
+  // Do NOT delete the returned object - it's managed by the UnitTest class.
+  const ::testing::TestInfo* const test_info =
+    ::testing::UnitTest::GetInstance()->current_test_info();
+
+
+
+  printf("We are in test %s of test case %s.\n",
+         test_info->name(),
+         test_info->test_case_name());
+```
+
+`current_test_info()` returns a null pointer if no test is running. In
+particular, you cannot find the test case name in `TestCaseSetUp()`,
+`TestCaseTearDown()` (where you know the test case name implicitly), or
+functions called from them.
+
+**Availability**: Linux, Windows, Mac.
+
+## Extending googletest by Handling Test Events
+
+googletest provides an **event listener API** to let you receive notifications
+about the progress of a test program and test failures. The events you can
+listen to include the start and end of the test program, a test case, or a test
+method, among others. You may use this API to augment or replace the standard
+console output, replace the XML output, or provide a completely different form
+of output, such as a GUI or a database. You can also use test events as
+checkpoints to implement a resource leak checker, for example.
+
+**Availability**: Linux, Windows, Mac.
+
+### Defining Event Listeners
+
+To define a event listener, you subclass either testing::TestEventListener or
+testing::EmptyTestEventListener The former is an (abstract) interface, where
+*each pure virtual method can be overridden to handle a test event* (For
+example, when a test starts, the `OnTestStart()` method will be called.). The
+latter provides an empty implementation of all methods in the interface, such
+that a subclass only needs to override the methods it cares about.
+
+When an event is fired, its context is passed to the handler function as an
+argument. The following argument types are used:
+
+*   UnitTest reflects the state of the entire test program,
+*   TestCase has information about a test case, which can contain one or more
+    tests,
+*   TestInfo contains the state of a test, and
+*   TestPartResult represents the result of a test assertion.
+
+An event handler function can examine the argument it receives to find out
+interesting information about the event and the test program's state.
+
+Here's an example:
+
+```c++
+  class MinimalistPrinter : public ::testing::EmptyTestEventListener {
+    // Called before a test starts.
+    virtual void OnTestStart(const ::testing::TestInfo& test_info) {
+      printf("*** Test %s.%s starting.\n",
+             test_info.test_case_name(), test_info.name());
+    }
+
+    // Called after a failed assertion or a SUCCESS().
+    virtual void OnTestPartResult(const ::testing::TestPartResult& test_part_result) {
+      printf("%s in %s:%d\n%s\n",
+             test_part_result.failed() ? "*** Failure" : "Success",
+             test_part_result.file_name(),
+             test_part_result.line_number(),
+             test_part_result.summary());
+    }
+
+    // Called after a test ends.
+    virtual void OnTestEnd(const ::testing::TestInfo& test_info) {
+      printf("*** Test %s.%s ending.\n",
+             test_info.test_case_name(), test_info.name());
+    }
+  };
+```
+
+### Using Event Listeners
+
+To use the event listener you have defined, add an instance of it to the
+googletest event listener list (represented by class TestEventListeners - note
+the "s" at the end of the name) in your `main()` function, before calling
+`RUN_ALL_TESTS()`:
+
+```c++
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  // Gets hold of the event listener list.
+  ::testing::TestEventListeners& listeners =
+        ::testing::UnitTest::GetInstance()->listeners();
+  // Adds a listener to the end.  googletest takes the ownership.
+  listeners.Append(new MinimalistPrinter);
+  return RUN_ALL_TESTS();
+}
+```
+
+There's only one problem: the default test result printer is still in effect, so
+its output will mingle with the output from your minimalist printer. To suppress
+the default printer, just release it from the event listener list and delete it.
+You can do so by adding one line:
+
+```c++
+  ...
+  delete listeners.Release(listeners.default_result_printer());
+  listeners.Append(new MinimalistPrinter);
+  return RUN_ALL_TESTS();
+```
+
+Now, sit back and enjoy a completely different output from your tests. For more
+details, you can read this sample9_unittest.cc
+
+You may append more than one listener to the list. When an `On*Start()` or
+`OnTestPartResult()` event is fired, the listeners will receive it in the order
+they appear in the list (since new listeners are added to the end of the list,
+the default text printer and the default XML generator will receive the event
+first). An `On*End()` event will be received by the listeners in the *reverse*
+order. This allows output by listeners added later to be framed by output from
+listeners added earlier.
+
+### Generating Failures in Listeners
+
+You may use failure-raising macros (`EXPECT_*()`, `ASSERT_*()`, `FAIL()`, etc)
+when processing an event. There are some restrictions:
+
+1.  You cannot generate any failure in `OnTestPartResult()` (otherwise it will
+    cause `OnTestPartResult()` to be called recursively).
+1.  A listener that handles `OnTestPartResult()` is not allowed to generate any
+    failure.
+
+When you add listeners to the listener list, you should put listeners that
+handle `OnTestPartResult()` *before* listeners that can generate failures. This
+ensures that failures generated by the latter are attributed to the right test
+by the former.
+
+We have a sample of failure-raising listener sample10_unittest.cc
+
+## Running Test Programs: Advanced Options
+
+googletest test programs are ordinary executables. Once built, you can run them
+directly and affect their behavior via the following environment variables
+and/or command line flags. For the flags to work, your programs must call
+`::testing::InitGoogleTest()` before calling `RUN_ALL_TESTS()`.
+
+To see a list of supported flags and their usage, please run your test program
+with the `--help` flag. You can also use `-h`, `-?`, or `/?` for short.
+
+If an option is specified both by an environment variable and by a flag, the
+latter takes precedence.
+
+### Selecting Tests
+
+#### Listing Test Names
+
+Sometimes it is necessary to list the available tests in a program before
+running them so that a filter may be applied if needed. Including the flag
+`--gtest_list_tests` overrides all other flags and lists tests in the following
+format:
+
+```none
+TestCase1.
+  TestName1
+  TestName2
+TestCase2.
+  TestName
+```
+
+None of the tests listed are actually run if the flag is provided. There is no
+corresponding environment variable for this flag.
+
+**Availability**: Linux, Windows, Mac.
+
+#### Running a Subset of the Tests
+
+By default, a googletest program runs all tests the user has defined. Sometimes,
+you want to run only a subset of the tests (e.g. for debugging or quickly
+verifying a change). If you set the `GTEST_FILTER` environment variable or the
+`--gtest_filter` flag to a filter string, googletest will only run the tests
+whose full names (in the form of `TestCaseName.TestName`) match the filter.
+
+The format of a filter is a '`:`'-separated list of wildcard patterns (called
+the *positive patterns*) optionally followed by a '`-`' and another
+'`:`'-separated pattern list (called the *negative patterns*). A test matches
+the filter if and only if it matches any of the positive patterns but does not
+match any of the negative patterns.
+
+A pattern may contain `'*'` (matches any string) or `'?'` (matches any single
+character). For convenience, the filter
+
+`'*-NegativePatterns'` can be also written as `'-NegativePatterns'`.
+
+For example:
+
+*   `./foo_test` Has no flag, and thus runs all its tests.
+*   `./foo_test --gtest_filter=*` Also runs everything, due to the single
+    match-everything `*` value.
+*   `./foo_test --gtest_filter=FooTest.*` Runs everything in test case `FooTest`
+    .
+*   `./foo_test --gtest_filter=*Null*:*Constructor*` Runs any test whose full
+    name contains either `"Null"` or `"Constructor"` .
+*   `./foo_test --gtest_filter=-*DeathTest.*` Runs all non-death tests.
+*   `./foo_test --gtest_filter=FooTest.*-FooTest.Bar` Runs everything in test
+    case `FooTest` except `FooTest.Bar`.
+*   `./foo_test --gtest_filter=FooTest.*:BarTest.*-FooTest.Bar:BarTest.Foo` Runs
+    everything in test case `FooTest` except `FooTest.Bar` and everything in
+    test case `BarTest` except `BarTest.Foo`.
+    
+#### Temporarily Disabling Tests
+
+If you have a broken test that you cannot fix right away, you can add the
+`DISABLED_` prefix to its name. This will exclude it from execution. This is
+better than commenting out the code or using `#if 0`, as disabled tests are
+still compiled (and thus won't rot).
+
+If you need to disable all tests in a test case, you can either add `DISABLED_`
+to the front of the name of each test, or alternatively add it to the front of
+the test case name.
+
+For example, the following tests won't be run by googletest, even though they
+will still be compiled:
+
+```c++
+// Tests that Foo does Abc.
+TEST(FooTest, DISABLED_DoesAbc) { ... }
+
+class DISABLED_BarTest : public ::testing::Test { ... };
+
+// Tests that Bar does Xyz.
+TEST_F(DISABLED_BarTest, DoesXyz) { ... }
+```
+
+NOTE: This feature should only be used for temporary pain-relief. You still have
+to fix the disabled tests at a later date. As a reminder, googletest will print
+a banner warning you if a test program contains any disabled tests.
+
+TIP: You can easily count the number of disabled tests you have using `gsearch`
+and/or `grep`. This number can be used as a metric for improving your test
+quality.
+
+**Availability**: Linux, Windows, Mac.
+
+#### Temporarily Enabling Disabled Tests
+
+To include disabled tests in test execution, just invoke the test program with
+the `--gtest_also_run_disabled_tests` flag or set the
+`GTEST_ALSO_RUN_DISABLED_TESTS` environment variable to a value other than `0`.
+You can combine this with the `--gtest_filter` flag to further select which
+disabled tests to run.
+
+**Availability**: Linux, Windows, Mac.
+
+### Repeating the Tests
+
+Once in a while you'll run into a test whose result is hit-or-miss. Perhaps it
+will fail only 1% of the time, making it rather hard to reproduce the bug under
+a debugger. This can be a major source of frustration.
+
+The `--gtest_repeat` flag allows you to repeat all (or selected) test methods in
+a program many times. Hopefully, a flaky test will eventually fail and give you
+a chance to debug. Here's how to use it:
+
+```none
+$ foo_test --gtest_repeat=1000
+Repeat foo_test 1000 times and don't stop at failures.
+
+$ foo_test --gtest_repeat=-1
+A negative count means repeating forever.
+
+$ foo_test --gtest_repeat=1000 --gtest_break_on_failure
+Repeat foo_test 1000 times, stopping at the first failure.  This
+is especially useful when running under a debugger: when the test
+fails, it will drop into the debugger and you can then inspect
+variables and stacks.
+
+$ foo_test --gtest_repeat=1000 --gtest_filter=FooBar.*
+Repeat the tests whose name matches the filter 1000 times.
+```
+
+If your test program contains [global set-up/tear-down](#global-set-up-and-tear-down) code, it
+will be repeated in each iteration as well, as the flakiness may be in it. You
+can also specify the repeat count by setting the `GTEST_REPEAT` environment
+variable.
+
+**Availability**: Linux, Windows, Mac.
+
+### Shuffling the Tests
+
+You can specify the `--gtest_shuffle` flag (or set the `GTEST_SHUFFLE`
+environment variable to `1`) to run the tests in a program in a random order.
+This helps to reveal bad dependencies between tests.
+
+By default, googletest uses a random seed calculated from the current time.
+Therefore you'll get a different order every time. The console output includes
+the random seed value, such that you can reproduce an order-related test failure
+later. To specify the random seed explicitly, use the `--gtest_random_seed=SEED`
+flag (or set the `GTEST_RANDOM_SEED` environment variable), where `SEED` is an
+integer in the range [0, 99999]. The seed value 0 is special: it tells
+googletest to do the default behavior of calculating the seed from the current
+time.
+
+If you combine this with `--gtest_repeat=N`, googletest will pick a different
+random seed and re-shuffle the tests in each iteration.
+
+**Availability**: Linux, Windows, Mac.
+
+### Controlling Test Output
+
+#### Colored Terminal Output
+
+googletest can use colors in its terminal output to make it easier to spot the
+important information:
+
+...<br/>
+<span style="color:green">[----------]<span style="color:black"> 1 test from FooTest<br/>
+<span style="color:green">[ RUN      ]<span style="color:black"> FooTest.DoesAbc<br/>
+<span style="color:green">[       OK ]<span style="color:black"> FooTest.DoesAbc<br/>
+<span style="color:green">[----------]<span style="color:black"> 2 tests from BarTest<br/>
+<span style="color:green">[ RUN      ]<span style="color:black"> BarTest.HasXyzProperty<br/>
+<span style="color:green">[       OK ]<span style="color:black"> BarTest.HasXyzProperty<br/>
+<span style="color:green">[ RUN      ]<span style="color:black"> BarTest.ReturnsTrueOnSuccess<br/>
+... some error messages ...<br/>
+<span   style="color:red">[  FAILED  ] <span style="color:black">BarTest.ReturnsTrueOnSuccess<br/>
+...<br/>
+<span style="color:green">[==========]<span style="color:black"> 30 tests from 14 test cases ran.<br/>
+<span style="color:green">[  PASSED  ]<span style="color:black"> 28 tests.<br/>
+<span style="color:red">[  FAILED  ]<span style="color:black"> 2 tests, listed below:<br/>
+<span style="color:red">[  FAILED  ]<span style="color:black"> BarTest.ReturnsTrueOnSuccess<br/>
+<span style="color:red">[  FAILED  ]<span style="color:black"> AnotherTest.DoesXyz<br/>
+  2 FAILED TESTS
+
+You can set the `GTEST_COLOR` environment variable or the `--gtest_color`
+command line flag to `yes`, `no`, or `auto` (the default) to enable colors,
+disable colors, or let googletest decide. When the value is `auto`, googletest
+will use colors if and only if the output goes to a terminal and (on non-Windows
+platforms) the `TERM` environment variable is set to `xterm` or `xterm-color`.
+
+ **Availability**: Linux, Windows, Mac.
+
+#### Suppressing the Elapsed Time
+
+By default, googletest prints the time it takes to run each test. To disable
+that, run the test program with the `--gtest_print_time=0` command line flag, or
+set the GTEST_PRINT_TIME environment variable to `0`.
+
+**Availability**: Linux, Windows, Mac.
+
+#### Suppressing UTF-8 Text Output
+
+In case of assertion failures, googletest prints expected and actual values of
+type `string` both as hex-encoded strings as well as in readable UTF-8 text if
+they contain valid non-ASCII UTF-8 characters. If you want to suppress the UTF-8
+text because, for example, you don't have an UTF-8 compatible output medium, run
+the test program with `--gtest_print_utf8=0` or set the `GTEST_PRINT_UTF8`
+environment variable to `0`.
+
+**Availability**: Linux, Windows, Mac.
+
+
+#### Generating an XML Report
+
+googletest can emit a detailed XML report to a file in addition to its normal
+textual output. The report contains the duration of each test, and thus can help
+you identify slow tests. The report is also used by the http://unittest
+dashboard to show per-test-method error messages.
+
+To generate the XML report, set the `GTEST_OUTPUT` environment variable or the
+`--gtest_output` flag to the string `"xml:path_to_output_file"`, which will
+create the file at the given location. You can also just use the string `"xml"`,
+in which case the output can be found in the `test_detail.xml` file in the
+current directory.
+
+If you specify a directory (for example, `"xml:output/directory/"` on Linux or
+`"xml:output\directory\"` on Windows), googletest will create the XML file in
+that directory, named after the test executable (e.g. `foo_test.xml` for test
+program `foo_test` or `foo_test.exe`). If the file already exists (perhaps left
+over from a previous run), googletest will pick a different name (e.g.
+`foo_test_1.xml`) to avoid overwriting it.
+
+
+The report is based on the `junitreport` Ant task. Since that format was
+originally intended for Java, a little interpretation is required to make it
+apply to googletest tests, as shown here:
+
+```xml
+<testsuites name="AllTests" ...>
+  <testsuite name="test_case_name" ...>
+    <testcase    name="test_name" ...>
+      <failure message="..."/>
+      <failure message="..."/>
+      <failure message="..."/>
+    </testcase>
+  </testsuite>
+</testsuites>
+```
+
+*   The root `<testsuites>` element corresponds to the entire test program.
+*   `<testsuite>` elements correspond to googletest test cases.
+*   `<testcase>` elements correspond to googletest test functions.
+
+For instance, the following program
+
+```c++
+TEST(MathTest, Addition) { ... }
+TEST(MathTest, Subtraction) { ... }
+TEST(LogicTest, NonContradiction) { ... }
+```
+
+could generate this report:
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<testsuites tests="3" failures="1" errors="0" time="0.035" timestamp="2011-10-31T18:52:42" name="AllTests">
+  <testsuite name="MathTest" tests="2" failures="1" errors="0" time="0.015">
+    <testcase name="Addition" status="run" time="0.007" classname="">
+      <failure message="Value of: add(1, 1)&#x0A;  Actual: 3&#x0A;Expected: 2" type="">...</failure>
+      <failure message="Value of: add(1, -1)&#x0A;  Actual: 1&#x0A;Expected: 0" type="">...</failure>
+    </testcase>
+    <testcase name="Subtraction" status="run" time="0.005" classname="">
+    </testcase>
+  </testsuite>
+  <testsuite name="LogicTest" tests="1" failures="0" errors="0" time="0.005">
+    <testcase name="NonContradiction" status="run" time="0.005" classname="">
+    </testcase>
+  </testsuite>
+</testsuites>
+```
+
+Things to note:
+
+*   The `tests` attribute of a `<testsuites>` or `<testsuite>` element tells how
+    many test functions the googletest program or test case contains, while the
+    `failures` attribute tells how many of them failed.
+
+*   The `time` attribute expresses the duration of the test, test case, or
+    entire test program in seconds.
+
+*   The `timestamp` attribute records the local date and time of the test
+    execution.
+
+*   Each `<failure>` element corresponds to a single failed googletest
+    assertion.
+
+**Availability**: Linux, Windows, Mac.
+
+#### Generating an JSON Report
+
+googletest can also emit a JSON report as an alternative format to XML. To
+generate the JSON report, set the `GTEST_OUTPUT` environment variable or the
+`--gtest_output` flag to the string `"json:path_to_output_file"`, which will
+create the file at the given location. You can also just use the string
+`"json"`, in which case the output can be found in the `test_detail.json` file
+in the current directory.
+
+The report format conforms to the following JSON Schema:
+
+```json
+{
+  "$schema": "http://json-schema.org/schema#",
+  "type": "object",
+  "definitions": {
+    "TestCase": {
+      "type": "object",
+      "properties": {
+        "name": { "type": "string" },
+        "tests": { "type": "integer" },
+        "failures": { "type": "integer" },
+        "disabled": { "type": "integer" },
+        "time": { "type": "string" },
+        "testsuite": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/TestInfo"
+          }
+        }
+      }
+    },
+    "TestInfo": {
+      "type": "object",
+      "properties": {
+        "name": { "type": "string" },
+        "status": {
+          "type": "string",
+          "enum": ["RUN", "NOTRUN"]
+        },
+        "time": { "type": "string" },
+        "classname": { "type": "string" },
+        "failures": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/Failure"
+          }
+        }
+      }
+    },
+    "Failure": {
+      "type": "object",
+      "properties": {
+        "failures": { "type": "string" },
+        "type": { "type": "string" }
+      }
+    }
+  },
+  "properties": {
+    "tests": { "type": "integer" },
+    "failures": { "type": "integer" },
+    "disabled": { "type": "integer" },
+    "errors": { "type": "integer" },
+    "timestamp": {
+      "type": "string",
+      "format": "date-time"
+    },
+    "time": { "type": "string" },
+    "name": { "type": "string" },
+    "testsuites": {
+      "type": "array",
+      "items": {
+        "$ref": "#/definitions/TestCase"
+      }
+    }
+  }
+}
+```
+
+The report uses the format that conforms to the following Proto3 using the [JSON
+encoding](https://developers.google.com/protocol-buffers/docs/proto3#json):
+
+```proto
+syntax = "proto3";
+
+package googletest;
+
+import "google/protobuf/timestamp.proto";
+import "google/protobuf/duration.proto";
+
+message UnitTest {
+  int32 tests = 1;
+  int32 failures = 2;
+  int32 disabled = 3;
+  int32 errors = 4;
+  google.protobuf.Timestamp timestamp = 5;
+  google.protobuf.Duration time = 6;
+  string name = 7;
+  repeated TestCase testsuites = 8;
+}
+
+message TestCase {
+  string name = 1;
+  int32 tests = 2;
+  int32 failures = 3;
+  int32 disabled = 4;
+  int32 errors = 5;
+  google.protobuf.Duration time = 6;
+  repeated TestInfo testsuite = 7;
+}
+
+message TestInfo {
+  string name = 1;
+  enum Status {
+    RUN = 0;
+    NOTRUN = 1;
+  }
+  Status status = 2;
+  google.protobuf.Duration time = 3;
+  string classname = 4;
+  message Failure {
+    string failures = 1;
+    string type = 2;
+  }
+  repeated Failure failures = 5;
+}
+```
+
+For instance, the following program
+
+```c++
+TEST(MathTest, Addition) { ... }
+TEST(MathTest, Subtraction) { ... }
+TEST(LogicTest, NonContradiction) { ... }
+```
+
+could generate this report:
+
+```json
+{
+  "tests": 3,
+  "failures": 1,
+  "errors": 0,
+  "time": "0.035s",
+  "timestamp": "2011-10-31T18:52:42Z"
+  "name": "AllTests",
+  "testsuites": [
+    {
+      "name": "MathTest",
+      "tests": 2,
+      "failures": 1,
+      "errors": 0,
+      "time": "0.015s",
+      "testsuite": [
+        {
+          "name": "Addition",
+          "status": "RUN",
+          "time": "0.007s",
+          "classname": "",
+          "failures": [
+            {
+              "message": "Value of: add(1, 1)\x0A  Actual: 3\x0AExpected: 2",
+              "type": ""
+            },
+            {
+              "message": "Value of: add(1, -1)\x0A  Actual: 1\x0AExpected: 0",
+              "type": ""
+            }
+          ]
+        },
+        {
+          "name": "Subtraction",
+          "status": "RUN",
+          "time": "0.005s",
+          "classname": ""
+        }
+      ]
+    }
+    {
+      "name": "LogicTest",
+      "tests": 1,
+      "failures": 0,
+      "errors": 0,
+      "time": "0.005s",
+      "testsuite": [
+        {
+          "name": "NonContradiction",
+          "status": "RUN",
+          "time": "0.005s",
+          "classname": ""
+        }
+      ]
+    }
+  ]
+}
+```
+
+IMPORTANT: The exact format of the JSON document is subject to change.
+
+**Availability**: Linux, Windows, Mac.
+
+### Controlling How Failures Are Reported
+
+#### Turning Assertion Failures into Break-Points
+
+When running test programs under a debugger, it's very convenient if the
+debugger can catch an assertion failure and automatically drop into interactive
+mode. googletest's *break-on-failure* mode supports this behavior.
+
+To enable it, set the `GTEST_BREAK_ON_FAILURE` environment variable to a value
+other than `0` . Alternatively, you can use the `--gtest_break_on_failure`
+command line flag.
+
+**Availability**: Linux, Windows, Mac.
+
+#### Disabling Catching Test-Thrown Exceptions
+
+googletest can be used either with or without exceptions enabled. If a test
+throws a C++ exception or (on Windows) a structured exception (SEH), by default
+googletest catches it, reports it as a test failure, and continues with the next
+test method. This maximizes the coverage of a test run. Also, on Windows an
+uncaught exception will cause a pop-up window, so catching the exceptions allows
+you to run the tests automatically.
+
+When debugging the test failures, however, you may instead want the exceptions
+to be handled by the debugger, such that you can examine the call stack when an
+exception is thrown. To achieve that, set the `GTEST_CATCH_EXCEPTIONS`
+environment variable to `0`, or use the `--gtest_catch_exceptions=0` flag when
+running the tests.
+
+**Availability**: Linux, Windows, Mac.
+
diff --git a/src/external/googletest/googletest/docs/faq.md b/src/external/googletest/googletest/docs/faq.md
new file mode 100644 (file)
index 0000000..7d42ff7
--- /dev/null
@@ -0,0 +1,770 @@
+# Googletest FAQ
+
+
+## Why should test case names and test names not contain underscore?
+
+Underscore (`_`) is special, as C++ reserves the following to be used by the
+compiler and the standard library:
+
+1.  any identifier that starts with an `_` followed by an upper-case letter, and
+1.  any identifier that contains two consecutive underscores (i.e. `__`)
+    *anywhere* in its name.
+
+User code is *prohibited* from using such identifiers.
+
+Now let's look at what this means for `TEST` and `TEST_F`.
+
+Currently `TEST(TestCaseName, TestName)` generates a class named
+`TestCaseName_TestName_Test`. What happens if `TestCaseName` or `TestName`
+contains `_`?
+
+1.  If `TestCaseName` starts with an `_` followed by an upper-case letter (say,
+    `_Foo`), we end up with `_Foo_TestName_Test`, which is reserved and thus
+    invalid.
+1.  If `TestCaseName` ends with an `_` (say, `Foo_`), we get
+    `Foo__TestName_Test`, which is invalid.
+1.  If `TestName` starts with an `_` (say, `_Bar`), we get
+    `TestCaseName__Bar_Test`, which is invalid.
+1.  If `TestName` ends with an `_` (say, `Bar_`), we get
+    `TestCaseName_Bar__Test`, which is invalid.
+
+So clearly `TestCaseName` and `TestName` cannot start or end with `_` (Actually,
+`TestCaseName` can start with `_` -- as long as the `_` isn't followed by an
+upper-case letter. But that's getting complicated. So for simplicity we just say
+that it cannot start with `_`.).
+
+It may seem fine for `TestCaseName` and `TestName` to contain `_` in the middle.
+However, consider this:
+
+```c++
+TEST(Time, Flies_Like_An_Arrow) { ... }
+TEST(Time_Flies, Like_An_Arrow) { ... }
+```
+
+Now, the two `TEST`s will both generate the same class
+(`Time_Flies_Like_An_Arrow_Test`). That's not good.
+
+So for simplicity, we just ask the users to avoid `_` in `TestCaseName` and
+`TestName`. The rule is more constraining than necessary, but it's simple and
+easy to remember. It also gives googletest some wiggle room in case its
+implementation needs to change in the future.
+
+If you violate the rule, there may not be immediate consequences, but your test
+may (just may) break with a new compiler (or a new version of the compiler you
+are using) or with a new version of googletest. Therefore it's best to follow
+the rule.
+
+## Why does googletest support `EXPECT_EQ(NULL, ptr)` and `ASSERT_EQ(NULL, ptr)` but not `EXPECT_NE(NULL, ptr)` and `ASSERT_NE(NULL, ptr)`?
+
+First of all you can use `EXPECT_NE(nullptr, ptr)` and `ASSERT_NE(nullptr,
+ptr)`. This is the preferred syntax in the style guide because nullptr does not
+have the type problems that NULL does. Which is why NULL does not work.
+
+Due to some peculiarity of C++, it requires some non-trivial template meta
+programming tricks to support using `NULL` as an argument of the `EXPECT_XX()`
+and `ASSERT_XX()` macros. Therefore we only do it where it's most needed
+(otherwise we make the implementation of googletest harder to maintain and more
+error-prone than necessary).
+
+The `EXPECT_EQ()` macro takes the *expected* value as its first argument and the
+*actual* value as the second. It's reasonable that someone wants to write
+`EXPECT_EQ(NULL, some_expression)`, and this indeed was requested several times.
+Therefore we implemented it.
+
+The need for `EXPECT_NE(NULL, ptr)` isn't nearly as strong. When the assertion
+fails, you already know that `ptr` must be `NULL`, so it doesn't add any
+information to print `ptr` in this case. That means `EXPECT_TRUE(ptr != NULL)`
+works just as well.
+
+If we were to support `EXPECT_NE(NULL, ptr)`, for consistency we'll have to
+support `EXPECT_NE(ptr, NULL)` as well, as unlike `EXPECT_EQ`, we don't have a
+convention on the order of the two arguments for `EXPECT_NE`. This means using
+the template meta programming tricks twice in the implementation, making it even
+harder to understand and maintain. We believe the benefit doesn't justify the
+cost.
+
+Finally, with the growth of the gMock matcher library, we are encouraging people
+to use the unified `EXPECT_THAT(value, matcher)` syntax more often in tests. One
+significant advantage of the matcher approach is that matchers can be easily
+combined to form new matchers, while the `EXPECT_NE`, etc, macros cannot be
+easily combined. Therefore we want to invest more in the matchers than in the
+`EXPECT_XX()` macros.
+
+## I need to test that different implementations of an interface satisfy some common requirements. Should I use typed tests or value-parameterized tests?
+
+For testing various implementations of the same interface, either typed tests or
+value-parameterized tests can get it done. It's really up to you the user to
+decide which is more convenient for you, depending on your particular case. Some
+rough guidelines:
+
+*   Typed tests can be easier to write if instances of the different
+    implementations can be created the same way, modulo the type. For example,
+    if all these implementations have a public default constructor (such that
+    you can write `new TypeParam`), or if their factory functions have the same
+    form (e.g. `CreateInstance<TypeParam>()`).
+*   Value-parameterized tests can be easier to write if you need different code
+    patterns to create different implementations' instances, e.g. `new Foo` vs
+    `new Bar(5)`. To accommodate for the differences, you can write factory
+    function wrappers and pass these function pointers to the tests as their
+    parameters.
+*   When a typed test fails, the output includes the name of the type, which can
+    help you quickly identify which implementation is wrong. Value-parameterized
+    tests cannot do this, so there you'll have to look at the iteration number
+    to know which implementation the failure is from, which is less direct.
+*   If you make a mistake writing a typed test, the compiler errors can be
+    harder to digest, as the code is templatized.
+*   When using typed tests, you need to make sure you are testing against the
+    interface type, not the concrete types (in other words, you want to make
+    sure `implicit_cast<MyInterface*>(my_concrete_impl)` works, not just that
+    `my_concrete_impl` works). It's less likely to make mistakes in this area
+    when using value-parameterized tests.
+
+I hope I didn't confuse you more. :-) If you don't mind, I'd suggest you to give
+both approaches a try. Practice is a much better way to grasp the subtle
+differences between the two tools. Once you have some concrete experience, you
+can much more easily decide which one to use the next time.
+
+## My death tests became very slow - what happened?
+
+In August 2008 we had to switch the default death test style from `fast` to
+`threadsafe`, as the former is no longer safe now that threaded logging is the
+default. This caused many death tests to slow down. Unfortunately this change
+was necessary.
+
+Please read [Fixing Failing Death Tests](death_test_styles.md) for what you can
+do.
+
+## I got some run-time errors about invalid proto descriptors when using `ProtocolMessageEquals`. Help!
+
+**Note:** `ProtocolMessageEquals` and `ProtocolMessageEquiv` are *deprecated*
+now. Please use `EqualsProto`, etc instead.
+
+`ProtocolMessageEquals` and `ProtocolMessageEquiv` were redefined recently and
+are now less tolerant on invalid protocol buffer definitions. In particular, if
+you have a `foo.proto` that doesn't fully qualify the type of a protocol message
+it references (e.g. `message<Bar>` where it should be `message<blah.Bar>`), you
+will now get run-time errors like:
+
+```
+... descriptor.cc:...] Invalid proto descriptor for file "path/to/foo.proto":
+... descriptor.cc:...]  blah.MyMessage.my_field: ".Bar" is not defined.
+```
+
+If you see this, your `.proto` file is broken and needs to be fixed by making
+the types fully qualified. The new definition of `ProtocolMessageEquals` and
+`ProtocolMessageEquiv` just happen to reveal your bug.
+
+## My death test modifies some state, but the change seems lost after the death test finishes. Why?
+
+Death tests (`EXPECT_DEATH`, etc) are executed in a sub-process s.t. the
+expected crash won't kill the test program (i.e. the parent process). As a
+result, any in-memory side effects they incur are observable in their respective
+sub-processes, but not in the parent process. You can think of them as running
+in a parallel universe, more or less.
+
+In particular, if you use [gMock](../../googlemock) and the death test statement
+invokes some mock methods, the parent process will think the calls have never
+occurred. Therefore, you may want to move your `EXPECT_CALL` statements inside
+the `EXPECT_DEATH` macro.
+
+## EXPECT_EQ(htonl(blah), blah_blah) generates weird compiler errors in opt mode. Is this a googletest bug?
+
+Actually, the bug is in `htonl()`.
+
+According to `'man htonl'`, `htonl()` is a *function*, which means it's valid to
+use `htonl` as a function pointer. However, in opt mode `htonl()` is defined as
+a *macro*, which breaks this usage.
+
+Worse, the macro definition of `htonl()` uses a `gcc` extension and is *not*
+standard C++. That hacky implementation has some ad hoc limitations. In
+particular, it prevents you from writing `Foo<sizeof(htonl(x))>()`, where `Foo`
+is a template that has an integral argument.
+
+The implementation of `EXPECT_EQ(a, b)` uses `sizeof(... a ...)` inside a
+template argument, and thus doesn't compile in opt mode when `a` contains a call
+to `htonl()`. It is difficult to make `EXPECT_EQ` bypass the `htonl()` bug, as
+the solution must work with different compilers on various platforms.
+
+`htonl()` has some other problems as described in `//util/endian/endian.h`,
+which defines `ghtonl()` to replace it. `ghtonl()` does the same thing `htonl()`
+does, only without its problems. We suggest you to use `ghtonl()` instead of
+`htonl()`, both in your tests and production code.
+
+`//util/endian/endian.h` also defines `ghtons()`, which solves similar problems
+in `htons()`.
+
+Don't forget to add `//util/endian` to the list of dependencies in the `BUILD`
+file wherever `ghtonl()` and `ghtons()` are used. The library consists of a
+single header file and will not bloat your binary.
+
+## The compiler complains about "undefined references" to some static const member variables, but I did define them in the class body. What's wrong?
+
+If your class has a static data member:
+
+```c++
+// foo.h
+class Foo {
+  ...
+  static const int kBar = 100;
+};
+```
+
+You also need to define it *outside* of the class body in `foo.cc`:
+
+```c++
+const int Foo::kBar;  // No initializer here.
+```
+
+Otherwise your code is **invalid C++**, and may break in unexpected ways. In
+particular, using it in googletest comparison assertions (`EXPECT_EQ`, etc) will
+generate an "undefined reference" linker error. The fact that "it used to work"
+doesn't mean it's valid. It just means that you were lucky. :-)
+
+## Can I derive a test fixture from another?
+
+Yes.
+
+Each test fixture has a corresponding and same named test case. This means only
+one test case can use a particular fixture. Sometimes, however, multiple test
+cases may want to use the same or slightly different fixtures. For example, you
+may want to make sure that all of a GUI library's test cases don't leak
+important system resources like fonts and brushes.
+
+In googletest, you share a fixture among test cases by putting the shared logic
+in a base test fixture, then deriving from that base a separate fixture for each
+test case that wants to use this common logic. You then use `TEST_F()` to write
+tests using each derived fixture.
+
+Typically, your code looks like this:
+
+```c++
+// Defines a base test fixture.
+class BaseTest : public ::testing::Test {
+ protected:
+  ...
+};
+
+// Derives a fixture FooTest from BaseTest.
+class FooTest : public BaseTest {
+ protected:
+  void SetUp() override {
+    BaseTest::SetUp();  // Sets up the base fixture first.
+    ... additional set-up work ...
+  }
+
+  void TearDown() override {
+    ... clean-up work for FooTest ...
+    BaseTest::TearDown();  // Remember to tear down the base fixture
+                           // after cleaning up FooTest!
+  }
+
+  ... functions and variables for FooTest ...
+};
+
+// Tests that use the fixture FooTest.
+TEST_F(FooTest, Bar) { ... }
+TEST_F(FooTest, Baz) { ... }
+
+... additional fixtures derived from BaseTest ...
+```
+
+If necessary, you can continue to derive test fixtures from a derived fixture.
+googletest has no limit on how deep the hierarchy can be.
+
+For a complete example using derived test fixtures, see [googletest
+sample](https://github.com/google/googletest/blob/master/googletest/samples/sample5_unittest.cc)
+
+## My compiler complains "void value not ignored as it ought to be." What does this mean?
+
+You're probably using an `ASSERT_*()` in a function that doesn't return `void`.
+`ASSERT_*()` can only be used in `void` functions, due to exceptions being
+disabled by our build system. Please see more details
+[here](advanced.md#assertion-placement).
+
+## My death test hangs (or seg-faults). How do I fix it?
+
+In googletest, death tests are run in a child process and the way they work is
+delicate. To write death tests you really need to understand how they work.
+Please make sure you have read [this](advanced.md#how-it-works).
+
+In particular, death tests don't like having multiple threads in the parent
+process. So the first thing you can try is to eliminate creating threads outside
+of `EXPECT_DEATH()`. For example, you may want to use [mocks](../../googlemock)
+or fake objects instead of real ones in your tests.
+
+Sometimes this is impossible as some library you must use may be creating
+threads before `main()` is even reached. In this case, you can try to minimize
+the chance of conflicts by either moving as many activities as possible inside
+`EXPECT_DEATH()` (in the extreme case, you want to move everything inside), or
+leaving as few things as possible in it. Also, you can try to set the death test
+style to `"threadsafe"`, which is safer but slower, and see if it helps.
+
+If you go with thread-safe death tests, remember that they rerun the test
+program from the beginning in the child process. Therefore make sure your
+program can run side-by-side with itself and is deterministic.
+
+In the end, this boils down to good concurrent programming. You have to make
+sure that there is no race conditions or dead locks in your program. No silver
+bullet - sorry!
+
+## Should I use the constructor/destructor of the test fixture or SetUp()/TearDown()?
+
+The first thing to remember is that googletest does **not** reuse the same test
+fixture object across multiple tests. For each `TEST_F`, googletest will create
+a **fresh** test fixture object, immediately call `SetUp()`, run the test body,
+call `TearDown()`, and then delete the test fixture object.
+
+When you need to write per-test set-up and tear-down logic, you have the choice
+between using the test fixture constructor/destructor or `SetUp()/TearDown()`.
+The former is usually preferred, as it has the following benefits:
+
+*   By initializing a member variable in the constructor, we have the option to
+    make it `const`, which helps prevent accidental changes to its value and
+    makes the tests more obviously correct.
+*   In case we need to subclass the test fixture class, the subclass'
+    constructor is guaranteed to call the base class' constructor *first*, and
+    the subclass' destructor is guaranteed to call the base class' destructor
+    *afterward*. With `SetUp()/TearDown()`, a subclass may make the mistake of
+    forgetting to call the base class' `SetUp()/TearDown()` or call them at the
+    wrong time.
+
+You may still want to use `SetUp()/TearDown()` in the following rare cases:
+
+*   In the body of a constructor (or destructor), it's not possible to use the
+    `ASSERT_xx` macros. Therefore, if the set-up operation could cause a fatal
+    test failure that should prevent the test from running, it's necessary to
+    use a `CHECK` macro or to use `SetUp()` instead of a constructor.
+*   If the tear-down operation could throw an exception, you must use
+    `TearDown()` as opposed to the destructor, as throwing in a destructor leads
+    to undefined behavior and usually will kill your program right away. Note
+    that many standard libraries (like STL) may throw when exceptions are
+    enabled in the compiler. Therefore you should prefer `TearDown()` if you
+    want to write portable tests that work with or without exceptions.
+*   The googletest team is considering making the assertion macros throw on
+    platforms where exceptions are enabled (e.g. Windows, Mac OS, and Linux
+    client-side), which will eliminate the need for the user to propagate
+    failures from a subroutine to its caller. Therefore, you shouldn't use
+    googletest assertions in a destructor if your code could run on such a
+    platform.
+*   In a constructor or destructor, you cannot make a virtual function call on
+    this object. (You can call a method declared as virtual, but it will be
+    statically bound.) Therefore, if you need to call a method that will be
+    overridden in a derived class, you have to use `SetUp()/TearDown()`.
+
+
+## The compiler complains "no matching function to call" when I use ASSERT_PRED*. How do I fix it?
+
+If the predicate function you use in `ASSERT_PRED*` or `EXPECT_PRED*` is
+overloaded or a template, the compiler will have trouble figuring out which
+overloaded version it should use. `ASSERT_PRED_FORMAT*` and
+`EXPECT_PRED_FORMAT*` don't have this problem.
+
+If you see this error, you might want to switch to
+`(ASSERT|EXPECT)_PRED_FORMAT*`, which will also give you a better failure
+message. If, however, that is not an option, you can resolve the problem by
+explicitly telling the compiler which version to pick.
+
+For example, suppose you have
+
+```c++
+bool IsPositive(int n) {
+  return n > 0;
+}
+
+bool IsPositive(double x) {
+  return x > 0;
+}
+```
+
+you will get a compiler error if you write
+
+```c++
+EXPECT_PRED1(IsPositive, 5);
+```
+
+However, this will work:
+
+```c++
+EXPECT_PRED1(static_cast<bool (*)(int)>(IsPositive), 5);
+```
+
+(The stuff inside the angled brackets for the `static_cast` operator is the type
+of the function pointer for the `int`-version of `IsPositive()`.)
+
+As another example, when you have a template function
+
+```c++
+template <typename T>
+bool IsNegative(T x) {
+  return x < 0;
+}
+```
+
+you can use it in a predicate assertion like this:
+
+```c++
+ASSERT_PRED1(IsNegative<int>, -5);
+```
+
+Things are more interesting if your template has more than one parameters. The
+following won't compile:
+
+```c++
+ASSERT_PRED2(GreaterThan<int, int>, 5, 0);
+```
+
+as the C++ pre-processor thinks you are giving `ASSERT_PRED2` 4 arguments, which
+is one more than expected. The workaround is to wrap the predicate function in
+parentheses:
+
+```c++
+ASSERT_PRED2((GreaterThan<int, int>), 5, 0);
+```
+
+
+## My compiler complains about "ignoring return value" when I call RUN_ALL_TESTS(). Why?
+
+Some people had been ignoring the return value of `RUN_ALL_TESTS()`. That is,
+instead of
+
+```c++
+  return RUN_ALL_TESTS();
+```
+
+they write
+
+```c++
+  RUN_ALL_TESTS();
+```
+
+This is **wrong and dangerous**. The testing services needs to see the return
+value of `RUN_ALL_TESTS()` in order to determine if a test has passed. If your
+`main()` function ignores it, your test will be considered successful even if it
+has a googletest assertion failure. Very bad.
+
+We have decided to fix this (thanks to Michael Chastain for the idea). Now, your
+code will no longer be able to ignore `RUN_ALL_TESTS()` when compiled with
+`gcc`. If you do so, you'll get a compiler error.
+
+If you see the compiler complaining about you ignoring the return value of
+`RUN_ALL_TESTS()`, the fix is simple: just make sure its value is used as the
+return value of `main()`.
+
+But how could we introduce a change that breaks existing tests? Well, in this
+case, the code was already broken in the first place, so we didn't break it. :-)
+
+## My compiler complains that a constructor (or destructor) cannot return a value. What's going on?
+
+Due to a peculiarity of C++, in order to support the syntax for streaming
+messages to an `ASSERT_*`, e.g.
+
+```c++
+  ASSERT_EQ(1, Foo()) << "blah blah" << foo;
+```
+
+we had to give up using `ASSERT*` and `FAIL*` (but not `EXPECT*` and
+`ADD_FAILURE*`) in constructors and destructors. The workaround is to move the
+content of your constructor/destructor to a private void member function, or
+switch to `EXPECT_*()` if that works. This
+[section](advanced.md#assertion-placement) in the user's guide explains it.
+
+## My SetUp() function is not called. Why?
+
+C++ is case-sensitive. Did you spell it as `Setup()`?
+
+Similarly, sometimes people spell `SetUpTestCase()` as `SetupTestCase()` and
+wonder why it's never called.
+
+## How do I jump to the line of a failure in Emacs directly?
+
+googletest's failure message format is understood by Emacs and many other IDEs,
+like acme and XCode. If a googletest message is in a compilation buffer in
+Emacs, then it's clickable.
+
+
+## I have several test cases which share the same test fixture logic, do I have to define a new test fixture class for each of them? This seems pretty tedious.
+
+You don't have to. Instead of
+
+```c++
+class FooTest : public BaseTest {};
+
+TEST_F(FooTest, Abc) { ... }
+TEST_F(FooTest, Def) { ... }
+
+class BarTest : public BaseTest {};
+
+TEST_F(BarTest, Abc) { ... }
+TEST_F(BarTest, Def) { ... }
+```
+
+you can simply `typedef` the test fixtures:
+
+```c++
+typedef BaseTest FooTest;
+
+TEST_F(FooTest, Abc) { ... }
+TEST_F(FooTest, Def) { ... }
+
+typedef BaseTest BarTest;
+
+TEST_F(BarTest, Abc) { ... }
+TEST_F(BarTest, Def) { ... }
+```
+
+## googletest output is buried in a whole bunch of LOG messages. What do I do?
+
+The googletest output is meant to be a concise and human-friendly report. If
+your test generates textual output itself, it will mix with the googletest
+output, making it hard to read. However, there is an easy solution to this
+problem.
+
+Since `LOG` messages go to stderr, we decided to let googletest output go to
+stdout. This way, you can easily separate the two using redirection. For
+example:
+
+```shell
+$ ./my_test > gtest_output.txt
+```
+
+
+## Why should I prefer test fixtures over global variables?
+
+There are several good reasons:
+
+1.  It's likely your test needs to change the states of its global variables.
+    This makes it difficult to keep side effects from escaping one test and
+    contaminating others, making debugging difficult. By using fixtures, each
+    test has a fresh set of variables that's different (but with the same
+    names). Thus, tests are kept independent of each other.
+1.  Global variables pollute the global namespace.
+1.  Test fixtures can be reused via subclassing, which cannot be done easily
+    with global variables. This is useful if many test cases have something in
+    common.
+
+
+    ## What can the statement argument in ASSERT_DEATH() be?
+
+`ASSERT_DEATH(*statement*, *regex*)` (or any death assertion macro) can be used
+wherever `*statement*` is valid. So basically `*statement*` can be any C++
+statement that makes sense in the current context. In particular, it can
+reference global and/or local variables, and can be:
+
+*   a simple function call (often the case),
+*   a complex expression, or
+*   a compound statement.
+
+Some examples are shown here:
+
+```c++
+// A death test can be a simple function call.
+TEST(MyDeathTest, FunctionCall) {
+  ASSERT_DEATH(Xyz(5), "Xyz failed");
+}
+
+// Or a complex expression that references variables and functions.
+TEST(MyDeathTest, ComplexExpression) {
+  const bool c = Condition();
+  ASSERT_DEATH((c ? Func1(0) : object2.Method("test")),
+               "(Func1|Method) failed");
+}
+
+// Death assertions can be used any where in a function.  In
+// particular, they can be inside a loop.
+TEST(MyDeathTest, InsideLoop) {
+  // Verifies that Foo(0), Foo(1), ..., and Foo(4) all die.
+  for (int i = 0; i < 5; i++) {
+    EXPECT_DEATH_M(Foo(i), "Foo has \\d+ errors",
+                   ::testing::Message() << "where i is " << i);
+  }
+}
+
+// A death assertion can contain a compound statement.
+TEST(MyDeathTest, CompoundStatement) {
+  // Verifies that at lease one of Bar(0), Bar(1), ..., and
+  // Bar(4) dies.
+  ASSERT_DEATH({
+    for (int i = 0; i < 5; i++) {
+      Bar(i);
+    }
+  },
+  "Bar has \\d+ errors");
+}
+```
+
+gtest-death-test_test.cc contains more examples if you are interested.
+
+## I have a fixture class `FooTest`, but `TEST_F(FooTest, Bar)` gives me error ``"no matching function for call to `FooTest::FooTest()'"``. Why?
+
+Googletest needs to be able to create objects of your test fixture class, so it
+must have a default constructor. Normally the compiler will define one for you.
+However, there are cases where you have to define your own:
+
+*   If you explicitly declare a non-default constructor for class `FooTest`
+    (`DISALLOW_EVIL_CONSTRUCTORS()` does this), then you need to define a
+    default constructor, even if it would be empty.
+*   If `FooTest` has a const non-static data member, then you have to define the
+    default constructor *and* initialize the const member in the initializer
+    list of the constructor. (Early versions of `gcc` doesn't force you to
+    initialize the const member. It's a bug that has been fixed in `gcc 4`.)
+
+## Why does ASSERT_DEATH complain about previous threads that were already joined?
+
+With the Linux pthread library, there is no turning back once you cross the line
+from single thread to multiple threads. The first time you create a thread, a
+manager thread is created in addition, so you get 3, not 2, threads. Later when
+the thread you create joins the main thread, the thread count decrements by 1,
+but the manager thread will never be killed, so you still have 2 threads, which
+means you cannot safely run a death test.
+
+The new NPTL thread library doesn't suffer from this problem, as it doesn't
+create a manager thread. However, if you don't control which machine your test
+runs on, you shouldn't depend on this.
+
+## Why does googletest require the entire test case, instead of individual tests, to be named *DeathTest when it uses ASSERT_DEATH?
+
+googletest does not interleave tests from different test cases. That is, it runs
+all tests in one test case first, and then runs all tests in the next test case,
+and so on. googletest does this because it needs to set up a test case before
+the first test in it is run, and tear it down afterwords. Splitting up the test
+case would require multiple set-up and tear-down processes, which is inefficient
+and makes the semantics unclean.
+
+If we were to determine the order of tests based on test name instead of test
+case name, then we would have a problem with the following situation:
+
+```c++
+TEST_F(FooTest, AbcDeathTest) { ... }
+TEST_F(FooTest, Uvw) { ... }
+
+TEST_F(BarTest, DefDeathTest) { ... }
+TEST_F(BarTest, Xyz) { ... }
+```
+
+Since `FooTest.AbcDeathTest` needs to run before `BarTest.Xyz`, and we don't
+interleave tests from different test cases, we need to run all tests in the
+`FooTest` case before running any test in the `BarTest` case. This contradicts
+with the requirement to run `BarTest.DefDeathTest` before `FooTest.Uvw`.
+
+## But I don't like calling my entire test case \*DeathTest when it contains both death tests and non-death tests. What do I do?
+
+You don't have to, but if you like, you may split up the test case into
+`FooTest` and `FooDeathTest`, where the names make it clear that they are
+related:
+
+```c++
+class FooTest : public ::testing::Test { ... };
+
+TEST_F(FooTest, Abc) { ... }
+TEST_F(FooTest, Def) { ... }
+
+using FooDeathTest = FooTest;
+
+TEST_F(FooDeathTest, Uvw) { ... EXPECT_DEATH(...) ... }
+TEST_F(FooDeathTest, Xyz) { ... ASSERT_DEATH(...) ... }
+```
+
+## googletest prints the LOG messages in a death test's child process only when the test fails. How can I see the LOG messages when the death test succeeds?
+
+Printing the LOG messages generated by the statement inside `EXPECT_DEATH()`
+makes it harder to search for real problems in the parent's log. Therefore,
+googletest only prints them when the death test has failed.
+
+If you really need to see such LOG messages, a workaround is to temporarily
+break the death test (e.g. by changing the regex pattern it is expected to
+match). Admittedly, this is a hack. We'll consider a more permanent solution
+after the fork-and-exec-style death tests are implemented.
+
+## The compiler complains about "no match for 'operator<<'" when I use an assertion. What gives?
+
+If you use a user-defined type `FooType` in an assertion, you must make sure
+there is an `std::ostream& operator<<(std::ostream&, const FooType&)` function
+defined such that we can print a value of `FooType`.
+
+In addition, if `FooType` is declared in a name space, the `<<` operator also
+needs to be defined in the *same* name space. See go/totw/49 for details.
+
+## How do I suppress the memory leak messages on Windows?
+
+Since the statically initialized googletest singleton requires allocations on
+the heap, the Visual C++ memory leak detector will report memory leaks at the
+end of the program run. The easiest way to avoid this is to use the
+`_CrtMemCheckpoint` and `_CrtMemDumpAllObjectsSince` calls to not report any
+statically initialized heap objects. See MSDN for more details and additional
+heap check/debug routines.
+
+
+## How can my code detect if it is running in a test?
+
+If you write code that sniffs whether it's running in a test and does different
+things accordingly, you are leaking test-only logic into production code and
+there is no easy way to ensure that the test-only code paths aren't run by
+mistake in production. Such cleverness also leads to
+[Heisenbugs](https://en.wikipedia.org/wiki/Heisenbug). Therefore we strongly
+advise against the practice, and googletest doesn't provide a way to do it.
+
+In general, the recommended way to cause the code to behave differently under
+test is [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection). You can inject
+different functionality from the test and from the production code. Since your
+production code doesn't link in the for-test logic at all (the
+[`testonly`](https://docs.bazel.build/versions/master/be/common-definitions.html#common.testonly)
+attribute for BUILD targets helps to ensure that), there is no danger in
+accidentally running it.
+
+However, if you *really*, *really*, *really* have no choice, and if you follow
+the rule of ending your test program names with `_test`, you can use the
+*horrible* hack of sniffing your executable name (`argv[0]` in `main()`) to know
+whether the code is under test.
+
+
+## How do I temporarily disable a test?
+
+If you have a broken test that you cannot fix right away, you can add the
+DISABLED_ prefix to its name. This will exclude it from execution. This is
+better than commenting out the code or using #if 0, as disabled tests are still
+compiled (and thus won't rot).
+
+To include disabled tests in test execution, just invoke the test program with
+the --gtest_also_run_disabled_tests flag.
+
+## Is it OK if I have two separate `TEST(Foo, Bar)` test methods defined in different namespaces?
+
+Yes.
+
+The rule is **all test methods in the same test case must use the same fixture
+class.** This means that the following is **allowed** because both tests use the
+same fixture class (`::testing::Test`).
+
+```c++
+namespace foo {
+TEST(CoolTest, DoSomething) {
+  SUCCEED();
+}
+}  // namespace foo
+
+namespace bar {
+TEST(CoolTest, DoSomething) {
+  SUCCEED();
+}
+}  // namespace bar
+```
+
+However, the following code is **not allowed** and will produce a runtime error
+from googletest because the test methods are using different test fixture
+classes with the same test case name.
+
+```c++
+namespace foo {
+class CoolTest : public ::testing::Test {};  // Fixture foo::CoolTest
+TEST_F(CoolTest, DoSomething) {
+  SUCCEED();
+}
+}  // namespace foo
+
+namespace bar {
+class CoolTest : public ::testing::Test {};  // Fixture: bar::CoolTest
+TEST_F(CoolTest, DoSomething) {
+  SUCCEED();
+}
+}  // namespace bar
+```
diff --git a/src/external/googletest/googletest/docs/primer.md b/src/external/googletest/googletest/docs/primer.md
new file mode 100644 (file)
index 0000000..7a8ea8d
--- /dev/null
@@ -0,0 +1,569 @@
+# Googletest Primer
+
+
+## Introduction: Why googletest?
+
+*googletest* helps you write better C++ tests.
+
+googletest is a testing framework developed by the Testing
+Technology team with Google's specific
+requirements and constraints in mind. No matter whether you work on Linux,
+Windows, or a Mac, if you write C++ code, googletest can help you. And it
+supports *any* kind of tests, not just unit tests.
+
+So what makes a good test, and how does googletest fit in? We believe:
+
+1.  Tests should be *independent* and *repeatable*. It's a pain to debug a test
+    that succeeds or fails as a result of other tests. googletest isolates the
+    tests by running each of them on a different object. When a test fails,
+    googletest allows you to run it in isolation for quick debugging.
+1.  Tests should be well *organized* and reflect the structure of the tested
+    code. googletest groups related tests into test cases that can share data
+    and subroutines. This common pattern is easy to recognize and makes tests
+    easy to maintain. Such consistency is especially helpful when people switch
+    projects and start to work on a new code base.
+1.  Tests should be *portable* and *reusable*. Google has a lot of code that is
+    platform-neutral, its tests should also be platform-neutral. googletest
+    works on different OSes, with different compilers (gcc, icc, and MSVC), with
+    or without exceptions, so googletest tests can easily work with a variety of
+    configurations.
+1.  When tests fail, they should provide as much *information* about the problem
+    as possible. googletest doesn't stop at the first test failure. Instead, it
+    only stops the current test and continues with the next. You can also set up
+    tests that report non-fatal failures after which the current test continues.
+    Thus, you can detect and fix multiple bugs in a single run-edit-compile
+    cycle.
+1.  The testing framework should liberate test writers from housekeeping chores
+    and let them focus on the test *content*. googletest automatically keeps
+    track of all tests defined, and doesn't require the user to enumerate them
+    in order to run them.
+1.  Tests should be *fast*. With googletest, you can reuse shared resources
+    across tests and pay for the set-up/tear-down only once, without making
+    tests depend on each other.
+
+Since googletest is based on the popular xUnit architecture, you'll feel right
+at home if you've used JUnit or PyUnit before. If not, it will take you about 10
+minutes to learn the basics and get started. So let's go!
+
+## Beware of the nomenclature
+
+_Note:_ There might be some confusion of idea due to different
+definitions of the terms _Test_, _Test Case_ and _Test Suite_, so beware
+of misunderstanding these.
+
+Historically, googletest started to use the term _Test Case_ for grouping
+related tests, whereas current publications including the International Software
+Testing Qualifications Board ([ISTQB](http://www.istqb.org/)) and various
+textbooks on Software Quality use the term _[Test
+Suite](http://glossary.istqb.org/search/test%20suite)_ for this.
+
+The related term _Test_, as it is used in the googletest, is corresponding to
+the term _[Test Case](http://glossary.istqb.org/search/test%20case)_ of ISTQB
+and others.
+
+The term _Test_ is commonly of broad enough sense, including ISTQB's
+definition of _Test Case_, so it's not much of a problem here. But the
+term _Test Case_ as used in Google Test is of contradictory sense and thus confusing.
+
+Unfortunately replacing the term _Test Case_ by _Test Suite_ throughout the
+googletest is not easy without breaking dependent projects, as `TestCase` is
+part of the public API at various places.
+
+So for the time being, please be aware of the different definitions of
+the terms:
+
+Meaning                                                                              | googletest Term                                                                                            | [ISTQB](http://www.istqb.org/) Term
+:----------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------- | :----------------------------------
+Exercise a particular program path with specific input values and verify the results | [TEST()](#simple-tests)                                                                                    | [Test Case](http://glossary.istqb.org/search/test%20case)
+A set of several tests related to one component                                      | [TestCase](#basic-concepts) | [TestSuite](http://glossary.istqb.org/search/test%20suite)
+
+## Basic Concepts
+
+When using googletest, you start by writing *assertions*, which are statements
+that check whether a condition is true. An assertion's result can be *success*,
+*nonfatal failure*, or *fatal failure*. If a fatal failure occurs, it aborts the
+current function; otherwise the program continues normally.
+
+*Tests* use assertions to verify the tested code's behavior. If a test crashes
+or has a failed assertion, then it *fails*; otherwise it *succeeds*.
+
+A *test case* contains one or many tests. You should group your tests into test
+cases that reflect the structure of the tested code. When multiple tests in a
+test case need to share common objects and subroutines, you can put them into a
+*test fixture* class.
+
+A *test program* can contain multiple test cases.
+
+We'll now explain how to write a test program, starting at the individual
+assertion level and building up to tests and test cases.
+
+## Assertions
+
+googletest assertions are macros that resemble function calls. You test a class
+or function by making assertions about its behavior. When an assertion fails,
+googletest prints the assertion's source file and line number location, along
+with a failure message. You may also supply a custom failure message which will
+be appended to googletest's message.
+
+The assertions come in pairs that test the same thing but have different effects
+on the current function. `ASSERT_*` versions generate fatal failures when they
+fail, and **abort the current function**. `EXPECT_*` versions generate nonfatal
+failures, which don't abort the current function. Usually `EXPECT_*` are
+preferred, as they allow more than one failure to be reported in a test.
+However, you should use `ASSERT_*` if it doesn't make sense to continue when the
+assertion in question fails.
+
+Since a failed `ASSERT_*` returns from the current function immediately,
+possibly skipping clean-up code that comes after it, it may cause a space leak.
+Depending on the nature of the leak, it may or may not be worth fixing - so keep
+this in mind if you get a heap checker error in addition to assertion errors.
+
+To provide a custom failure message, simply stream it into the macro using the
+`<<` operator, or a sequence of such operators. An example:
+
+```c++
+ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
+
+for (int i = 0; i < x.size(); ++i) {
+  EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
+}
+```
+
+Anything that can be streamed to an `ostream` can be streamed to an assertion
+macro--in particular, C strings and `string` objects. If a wide string
+(`wchar_t*`, `TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is
+streamed to an assertion, it will be translated to UTF-8 when printed.
+
+### Basic Assertions
+
+These assertions do basic true/false condition testing.
+
+Fatal assertion            | Nonfatal assertion         | Verifies
+-------------------------- | -------------------------- | --------------------
+`ASSERT_TRUE(condition);`  | `EXPECT_TRUE(condition);`  | `condition` is true
+`ASSERT_FALSE(condition);` | `EXPECT_FALSE(condition);` | `condition` is false
+
+Remember, when they fail, `ASSERT_*` yields a fatal failure and returns from the
+current function, while `EXPECT_*` yields a nonfatal failure, allowing the
+function to continue running. In either case, an assertion failure means its
+containing test fails.
+
+**Availability**: Linux, Windows, Mac.
+
+### Binary Comparison
+
+This section describes assertions that compare two values.
+
+Fatal assertion          | Nonfatal assertion       | Verifies
+------------------------ | ------------------------ | --------------
+`ASSERT_EQ(val1, val2);` | `EXPECT_EQ(val1, val2);` | `val1 == val2`
+`ASSERT_NE(val1, val2);` | `EXPECT_NE(val1, val2);` | `val1 != val2`
+`ASSERT_LT(val1, val2);` | `EXPECT_LT(val1, val2);` | `val1 < val2`
+`ASSERT_LE(val1, val2);` | `EXPECT_LE(val1, val2);` | `val1 <= val2`
+`ASSERT_GT(val1, val2);` | `EXPECT_GT(val1, val2);` | `val1 > val2`
+`ASSERT_GE(val1, val2);` | `EXPECT_GE(val1, val2);` | `val1 >= val2`
+
+Value arguments must be comparable by the assertion's comparison operator or
+you'll get a compiler error. We used to require the arguments to support the
+`<<` operator for streaming to an `ostream`, but it's no longer necessary. If
+`<<` is supported, it will be called to print the arguments when the assertion
+fails; otherwise googletest will attempt to print them in the best way it can.
+For more details and how to customize the printing of the arguments, see
+gMock [recipe](../../googlemock/docs/CookBook.md#teaching-google-mock-how-to-print-your-values).).
+
+These assertions can work with a user-defined type, but only if you define the
+corresponding comparison operator (e.g. `==`, `<`, etc). Since this is
+discouraged by the Google [C++ Style
+Guide](https://google.github.io/styleguide/cppguide.html#Operator_Overloading),
+you may need to use `ASSERT_TRUE()` or `EXPECT_TRUE()` to assert the equality of
+two objects of a user-defined type.
+
+However, when possible, `ASSERT_EQ(actual, expected)` is preferred to
+`ASSERT_TRUE(actual == expected)`, since it tells you `actual` and `expected`'s
+values on failure.
+
+Arguments are always evaluated exactly once. Therefore, it's OK for the
+arguments to have side effects. However, as with any ordinary C/C++ function,
+the arguments' evaluation order is undefined (i.e. the compiler is free to
+choose any order) and your code should not depend on any particular argument
+evaluation order.
+
+`ASSERT_EQ()` does pointer equality on pointers. If used on two C strings, it
+tests if they are in the same memory location, not if they have the same value.
+Therefore, if you want to compare C strings (e.g. `const char*`) by value, use
+`ASSERT_STREQ()`, which will be described later on. In particular, to assert
+that a C string is `NULL`, use `ASSERT_STREQ(c_string, NULL)`. Consider use
+`ASSERT_EQ(c_string, nullptr)` if c++11 is supported. To compare two `string`
+objects, you should use `ASSERT_EQ`.
+
+When doing pointer comparisons use `*_EQ(ptr, nullptr)` and `*_NE(ptr, nullptr)`
+instead of `*_EQ(ptr, NULL)` and `*_NE(ptr, NULL)`. This is because `nullptr` is
+typed while `NULL` is not. See [FAQ](faq.md#why-does-google-test-support-expect_eqnull-ptr-and-assert_eqnull-ptr-but-not-expect_nenull-ptr-and-assert_nenull-ptr)
+for more details.
+
+If you're working with floating point numbers, you may want to use the floating
+point variations of some of these macros in order to avoid problems caused by
+rounding. See [Advanced googletest Topics](advanced.md) for details.
+
+Macros in this section work with both narrow and wide string objects (`string`
+and `wstring`).
+
+**Availability**: Linux, Windows, Mac.
+
+**Historical note**: Before February 2016 `*_EQ` had a convention of calling it
+as `ASSERT_EQ(expected, actual)`, so lots of existing code uses this order. Now
+`*_EQ` treats both parameters in the same way.
+
+### String Comparison
+
+The assertions in this group compare two **C strings**. If you want to compare
+two `string` objects, use `EXPECT_EQ`, `EXPECT_NE`, and etc instead.
+
+| Fatal assertion                 | Nonfatal assertion              | Verifies                                                 |
+| ------------------------------- | ------------------------------- | -------------------------------------------------------- |
+| `ASSERT_STREQ(str1, str2);`     | `EXPECT_STREQ(str1, str2);`     | the two C strings have the same content                  |
+| `ASSERT_STRNE(str1, str2);`     | `EXPECT_STRNE(str1, str2);`     | the two C strings have different contents                |
+| `ASSERT_STRCASEEQ(str1, str2);` | `EXPECT_STRCASEEQ(str1, str2);` | the two C strings have the same content, ignoring case   |
+| `ASSERT_STRCASENE(str1, str2);` | `EXPECT_STRCASENE(str1, str2);` | the two C strings have different contents, ignoring case |
+
+Note that "CASE" in an assertion name means that case is ignored. A `NULL`
+pointer and an empty string are considered *different*.
+
+`*STREQ*` and `*STRNE*` also accept wide C strings (`wchar_t*`). If a comparison
+of two wide strings fails, their values will be printed as UTF-8 narrow strings.
+
+**Availability**: Linux, Windows, Mac.
+
+**See also**: For more string comparison tricks (substring, prefix, suffix, and
+regular expression matching, for example), see
+[this](https://github.com/google/googletest/blob/master/googletest/docs/advanced.md)
+in the Advanced googletest Guide.
+
+## Simple Tests
+
+To create a test:
+
+1.  Use the `TEST()` macro to define and name a test function, These are
+    ordinary C++ functions that don't return a value.
+1.  In this function, along with any valid C++ statements you want to include,
+    use the various googletest assertions to check values.
+1.  The test's result is determined by the assertions; if any assertion in the
+    test fails (either fatally or non-fatally), or if the test crashes, the
+    entire test fails. Otherwise, it succeeds.
+
+```c++
+TEST(TestCaseName, TestName) {
+  ... test body ...
+}
+```
+
+`TEST()` arguments go from general to specific. The *first* argument is the name
+of the test case, and the *second* argument is the test's name within the test
+case. Both names must be valid C++ identifiers, and they should not contain
+underscore (`_`). A test's *full name* consists of its containing test case and
+its individual name. Tests from different test cases can have the same
+individual name.
+
+For example, let's take a simple integer function:
+
+```c++
+int Factorial(int n);  // Returns the factorial of n
+```
+
+A test case for this function might look like:
+
+```c++
+// Tests factorial of 0.
+TEST(FactorialTest, HandlesZeroInput) {
+  EXPECT_EQ(Factorial(0), 1);
+}
+
+// Tests factorial of positive numbers.
+TEST(FactorialTest, HandlesPositiveInput) {
+  EXPECT_EQ(Factorial(1), 1);
+  EXPECT_EQ(Factorial(2), 2);
+  EXPECT_EQ(Factorial(3), 6);
+  EXPECT_EQ(Factorial(8), 40320);
+}
+```
+
+googletest groups the test results by test cases, so logically-related tests
+should be in the same test case; in other words, the first argument to their
+`TEST()` should be the same. In the above example, we have two tests,
+`HandlesZeroInput` and `HandlesPositiveInput`, that belong to the same test case
+`FactorialTest`.
+
+When naming your test cases and tests, you should follow the same convention as
+for [naming functions and
+classes](https://google.github.io/styleguide/cppguide.html#Function_Names).
+
+**Availability**: Linux, Windows, Mac.
+
+## Test Fixtures: Using the Same Data Configuration for Multiple Tests
+
+If you find yourself writing two or more tests that operate on similar data, you
+can use a *test fixture*. It allows you to reuse the same configuration of
+objects for several different tests.
+
+To create a fixture:
+
+1.  Derive a class from `::testing::Test` . Start its body with `protected:` as
+    we'll want to access fixture members from sub-classes.
+1.  Inside the class, declare any objects you plan to use.
+1.  If necessary, write a default constructor or `SetUp()` function to prepare
+    the objects for each test. A common mistake is to spell `SetUp()` as
+    **`Setup()`** with a small `u` - Use `override` in C++11 to make sure you
+    spelled it correctly
+1.  If necessary, write a destructor or `TearDown()` function to release any
+    resources you allocated in `SetUp()` . To learn when you should use the
+    constructor/destructor and when you should use `SetUp()/TearDown()`, read
+    this [FAQ](faq.md#should-i-use-the-constructordestructor-of-the-test-fixture-or-setupteardown) entry.
+1.  If needed, define subroutines for your tests to share.
+
+When using a fixture, use `TEST_F()` instead of `TEST()` as it allows you to
+access objects and subroutines in the test fixture:
+
+```c++
+TEST_F(TestCaseName, TestName) {
+  ... test body ...
+}
+```
+
+Like `TEST()`, the first argument is the test case name, but for `TEST_F()` this
+must be the name of the test fixture class. You've probably guessed: `_F` is for
+fixture.
+
+Unfortunately, the C++ macro system does not allow us to create a single macro
+that can handle both types of tests. Using the wrong macro causes a compiler
+error.
+
+Also, you must first define a test fixture class before using it in a
+`TEST_F()`, or you'll get the compiler error "`virtual outside class
+declaration`".
+
+For each test defined with `TEST_F()` , googletest will create a *fresh* test
+fixture at runtime, immediately initialize it via `SetUp()` , run the test,
+clean up by calling `TearDown()` , and then delete the test fixture. Note that
+different tests in the same test case have different test fixture objects, and
+googletest always deletes a test fixture before it creates the next one.
+googletest does **not** reuse the same test fixture for multiple tests. Any
+changes one test makes to the fixture do not affect other tests.
+
+As an example, let's write tests for a FIFO queue class named `Queue`, which has
+the following interface:
+
+```c++
+template <typename E>  // E is the element type.
+class Queue {
+ public:
+  Queue();
+  void Enqueue(const E& element);
+  E* Dequeue();  // Returns NULL if the queue is empty.
+  size_t size() const;
+  ...
+};
+```
+
+First, define a fixture class. By convention, you should give it the name
+`FooTest` where `Foo` is the class being tested.
+
+```c++
+class QueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+     q1_.Enqueue(1);
+     q2_.Enqueue(2);
+     q2_.Enqueue(3);
+  }
+
+  // void TearDown() override {}
+
+  Queue<int> q0_;
+  Queue<int> q1_;
+  Queue<int> q2_;
+};
+```
+
+In this case, `TearDown()` is not needed since we don't have to clean up after
+each test, other than what's already done by the destructor.
+
+Now we'll write tests using `TEST_F()` and this fixture.
+
+```c++
+TEST_F(QueueTest, IsEmptyInitially) {
+  EXPECT_EQ(q0_.size(), 0);
+}
+
+TEST_F(QueueTest, DequeueWorks) {
+  int* n = q0_.Dequeue();
+  EXPECT_EQ(n, nullptr);
+
+  n = q1_.Dequeue();
+  ASSERT_NE(n, nullptr);
+  EXPECT_EQ(*n, 1);
+  EXPECT_EQ(q1_.size(), 0);
+  delete n;
+
+  n = q2_.Dequeue();
+  ASSERT_NE(n, nullptr);
+  EXPECT_EQ(*n, 2);
+  EXPECT_EQ(q2_.size(), 1);
+  delete n;
+}
+```
+
+The above uses both `ASSERT_*` and `EXPECT_*` assertions. The rule of thumb is
+to use `EXPECT_*` when you want the test to continue to reveal more errors after
+the assertion failure, and use `ASSERT_*` when continuing after failure doesn't
+make sense. For example, the second assertion in the `Dequeue` test is
+=ASSERT_NE(nullptr, n)=, as we need to dereference the pointer `n` later, which
+would lead to a segfault when `n` is `NULL`.
+
+When these tests run, the following happens:
+
+1.  googletest constructs a `QueueTest` object (let's call it `t1` ).
+1.  `t1.SetUp()` initializes `t1` .
+1.  The first test ( `IsEmptyInitially` ) runs on `t1` .
+1.  `t1.TearDown()` cleans up after the test finishes.
+1.  `t1` is destructed.
+1.  The above steps are repeated on another `QueueTest` object, this time
+    running the `DequeueWorks` test.
+
+**Availability**: Linux, Windows, Mac.
+
+
+## Invoking the Tests
+
+`TEST()` and `TEST_F()` implicitly register their tests with googletest. So,
+unlike with many other C++ testing frameworks, you don't have to re-list all
+your defined tests in order to run them.
+
+After defining your tests, you can run them with `RUN_ALL_TESTS()` , which
+returns `0` if all the tests are successful, or `1` otherwise. Note that
+`RUN_ALL_TESTS()` runs *all tests* in your link unit -- they can be from
+different test cases, or even different source files.
+
+When invoked, the `RUN_ALL_TESTS()` macro:
+
+1. Saves the state of all googletest flags
+
+*   Creates a test fixture object for the first test.
+
+*   Initializes it via `SetUp()`.
+
+*   Runs the test on the fixture object.
+
+*   Cleans up the fixture via `TearDown()`.
+
+*   Deletes the fixture.
+
+* Restores the state of all googletest flags
+
+*   Repeats the above steps for the next test, until all tests have run.
+
+If a fatal failure happens the subsequent steps will be skipped.
+
+> IMPORTANT: You must **not** ignore the return value of `RUN_ALL_TESTS()`, or
+> you will get a compiler error. The rationale for this design is that the
+> automated testing service determines whether a test has passed based on its
+> exit code, not on its stdout/stderr output; thus your `main()` function must
+> return the value of `RUN_ALL_TESTS()`.
+>
+> Also, you should call `RUN_ALL_TESTS()` only **once**. Calling it more than
+> once conflicts with some advanced googletest features (e.g. thread-safe [death
+> tests](advanced#death-tests)) and thus is not supported.
+
+**Availability**: Linux, Windows, Mac.
+
+## Writing the main() Function
+
+In `google3`, the simplest approach is to use the default main() function
+provided by linking in `"//testing/base/public:gtest_main"`. If that doesn't
+cover what you need, you should write your own main() function, which should
+return the value of `RUN_ALL_TESTS()`. Link to `"//testing/base/public:gunit"`.
+You can start from this boilerplate:
+
+```c++
+#include "this/package/foo.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+// The fixture for testing class Foo.
+class FooTest : public ::testing::Test {
+ protected:
+  // You can remove any or all of the following functions if its body
+  // is empty.
+
+  FooTest() {
+     // You can do set-up work for each test here.
+  }
+
+  ~FooTest() override {
+     // You can do clean-up work that doesn't throw exceptions here.
+  }
+
+  // If the constructor and destructor are not enough for setting up
+  // and cleaning up each test, you can define the following methods:
+
+  void SetUp() override {
+     // Code here will be called immediately after the constructor (right
+     // before each test).
+  }
+
+  void TearDown() override {
+     // Code here will be called immediately after each test (right
+     // before the destructor).
+  }
+
+  // Objects declared here can be used by all tests in the test case for Foo.
+};
+
+// Tests that the Foo::Bar() method does Abc.
+TEST_F(FooTest, MethodBarDoesAbc) {
+  const std::string input_filepath = "this/package/testdata/myinputfile.dat";
+  const std::string output_filepath = "this/package/testdata/myoutputfile.dat";
+  Foo f;
+  EXPECT_EQ(f.Bar(input_filepath, output_filepath), 0);
+}
+
+// Tests that Foo does Xyz.
+TEST_F(FooTest, DoesXyz) {
+  // Exercises the Xyz feature of Foo.
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
+```
+
+
+The `::testing::InitGoogleTest()` function parses the command line for
+googletest flags, and removes all recognized flags. This allows the user to
+control a test program's behavior via various flags, which we'll cover in
+[AdvancedGuide](advanced.md). You **must** call this function before calling
+`RUN_ALL_TESTS()`, or the flags won't be properly initialized.
+
+On Windows, `InitGoogleTest()` also works with wide strings, so it can be used
+in programs compiled in `UNICODE` mode as well.
+
+But maybe you think that writing all those main() functions is too much work? We
+agree with you completely and that's why Google Test provides a basic
+implementation of main(). If it fits your needs, then just link your test with
+gtest\_main library and you are good to go.
+
+NOTE: `ParseGUnitFlags()` is deprecated in favor of `InitGoogleTest()`.
+
+
+## Known Limitations
+
+*   Google Test is designed to be thread-safe. The implementation is thread-safe
+    on systems where the `pthreads` library is available. It is currently
+    _unsafe_ to use Google Test assertions from two threads concurrently on
+    other systems (e.g. Windows). In most tests this is not an issue as usually
+    the assertions are done in the main thread. If you want to help, you can
+    volunteer to implement the necessary synchronization primitives in
+    `gtest-port.h` for your platform.
diff --git a/src/external/googletest/googletest/docs/samples.md b/src/external/googletest/googletest/docs/samples.md
new file mode 100644 (file)
index 0000000..18dcca3
--- /dev/null
@@ -0,0 +1,22 @@
+# Googletest Samples {#samples}
+
+If you're like us, you'd like to look at [googletest
+samples.](https://github.com/google/googletest/tree/master/googletest/samples)
+The sample directory has a number of well-commented samples showing how to use a
+variety of googletest features.
+
+*   Sample #1 shows the basic steps of using googletest to test C++ functions.
+*   Sample #2 shows a more complex unit test for a class with multiple member
+    functions.
+*   Sample #3 uses a test fixture.
+*   Sample #4 teaches you how to use googletest and `googletest.h` together to
+    get the best of both libraries.
+*   Sample #5 puts shared testing logic in a base test fixture, and reuses it in
+    derived fixtures.
+*   Sample #6 demonstrates type-parameterized tests.
+*   Sample #7 teaches the basics of value-parameterized tests.
+*   Sample #8 shows using `Combine()` in value-parameterized tests.
+*   Sample #9 shows use of the listener API to modify Google Test's console
+    output and the use of its reflection API to inspect test results.
+*   Sample #10 shows use of the listener API to implement a primitive memory
+    leak checker.
index 957a69c6a9e7bed7d57ebd7872fb047f97175fc1..20c54d869519f6930e7ebfd2c0805baca42216ba 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 //
-// Author: wan@google.com (Zhanyong Wan)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // This header file defines the public API for death tests.  It is
 // #included by gtest.h so a user doesn't need to include this
 // directly.
+// GOOGLETEST_CM0001 DO NOT DELETE
 
 #ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
 #define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
@@ -99,10 +99,11 @@ GTEST_API_ bool InDeathTestChild();
 //
 // On the regular expressions used in death tests:
 //
+//   GOOGLETEST_CM0005 DO NOT DELETE
 //   On POSIX-compliant systems (*nix), we use the <regex.h> library,
 //   which uses the POSIX extended regex syntax.
 //
-//   On other platforms (e.g. Windows), we only support a simple regex
+//   On other platforms (e.g. Windows or Mac), we only support a simple regex
 //   syntax implemented as part of Google Test.  This limited
 //   implementation should be enough most of the time when writing
 //   death tests; though it lacks many features you can find in PCRE
@@ -160,7 +161,7 @@ GTEST_API_ bool InDeathTestChild();
 //   is rarely a problem as people usually don't put the test binary
 //   directory in PATH.
 //
-// TODO(wan@google.com): make thread-safe death tests search the PATH.
+// FIXME: make thread-safe death tests search the PATH.
 
 // Asserts that a given statement causes the program to exit, with an
 // integer exit status that satisfies predicate, and emitting error output
@@ -198,9 +199,10 @@ class GTEST_API_ ExitedWithCode {
   const int exit_code_;
 };
 
-# if !GTEST_OS_WINDOWS
+# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
 // Tests that an exit code describes an exit due to termination by a
 // given signal.
+// GOOGLETEST_CM0006 DO NOT DELETE
 class GTEST_API_ KilledBySignal {
  public:
   explicit KilledBySignal(int signum);
@@ -272,6 +274,54 @@ class GTEST_API_ KilledBySignal {
 # endif  // NDEBUG for EXPECT_DEBUG_DEATH
 #endif  // GTEST_HAS_DEATH_TEST
 
+// This macro is used for implementing macros such as
+// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
+// death tests are not supported. Those macros must compile on such systems
+// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on
+// systems that support death tests. This allows one to write such a macro
+// on a system that does not support death tests and be sure that it will
+// compile on a death-test supporting system. It is exposed publicly so that
+// systems that have death-tests with stricter requirements than
+// GTEST_HAS_DEATH_TEST can write their own equivalent of
+// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED.
+//
+// Parameters:
+//   statement -  A statement that a macro such as EXPECT_DEATH would test
+//                for program termination. This macro has to make sure this
+//                statement is compiled but not executed, to ensure that
+//                EXPECT_DEATH_IF_SUPPORTED compiles with a certain
+//                parameter iff EXPECT_DEATH compiles with it.
+//   regex     -  A regex that a macro such as EXPECT_DEATH would use to test
+//                the output of statement.  This parameter has to be
+//                compiled but not evaluated by this macro, to ensure that
+//                this macro only accepts expressions that a macro such as
+//                EXPECT_DEATH would accept.
+//   terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
+//                and a return statement for ASSERT_DEATH_IF_SUPPORTED.
+//                This ensures that ASSERT_DEATH_IF_SUPPORTED will not
+//                compile inside functions where ASSERT_DEATH doesn't
+//                compile.
+//
+//  The branch that has an always false condition is used to ensure that
+//  statement and regex are compiled (and thus syntactically correct) but
+//  never executed. The unreachable code macro protects the terminator
+//  statement from generating an 'unreachable code' warning in case
+//  statement unconditionally returns or throws. The Message constructor at
+//  the end allows the syntax of streaming additional messages into the
+//  macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
+# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \
+    GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+    if (::testing::internal::AlwaysTrue()) { \
+      GTEST_LOG_(WARNING) \
+          << "Death tests are not supported on this platform.\n" \
+          << "Statement '" #statement "' cannot be verified."; \
+    } else if (::testing::internal::AlwaysFalse()) { \
+      ::testing::internal::RE::PartialMatch(".*", (regex)); \
+      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+      terminator; \
+    } else \
+      ::testing::Message()
+
 // EXPECT_DEATH_IF_SUPPORTED(statement, regex) and
 // ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
 // death tests are supported; otherwise they just issue a warning.  This is
@@ -284,9 +334,9 @@ class GTEST_API_ KilledBySignal {
     ASSERT_DEATH(statement, regex)
 #else
 # define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
-    GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
+    GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, )
 # define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
-    GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return)
+    GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return)
 #endif
 
 }  // namespace testing
index fe879bca7927afae2499a6137ab26f4991fbbacf..5ca041614cb355922f355b893f9c84a3b7f52c6f 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 //
-// Author: wan@google.com (Zhanyong Wan)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // This header file defines the Message class.
 //
@@ -43,6 +42,8 @@
 // to CHANGE WITHOUT NOTICE.  Therefore DO NOT DEPEND ON IT in a user
 // program!
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
 #define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
 
@@ -50,6 +51,9 @@
 
 #include "gtest/internal/gtest-port.h"
 
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
 // Ensures that there is at least one operator<< in the global namespace.
 // See Message& operator<<(...) below for why.
 void operator<<(const testing::internal::Secret&, int);
@@ -196,7 +200,6 @@ class GTEST_API_ Message {
   std::string GetString() const;
 
  private:
-
 #if GTEST_OS_SYMBIAN
   // These are needed as the Nokia Symbian Compiler cannot decide between
   // const T& and const T* in a function template. The Nokia compiler _can_
@@ -247,4 +250,6 @@ std::string StreamableToString(const T& streamable) {
 }  // namespace internal
 }  // namespace testing
 
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
 #endif  // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
index 2d668ac188229f12f37f8c269b7cc344e55c72b0..3e95e4390e00e116310a42383d2e30b58eda5f0d 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Authors: vladl@google.com (Vlad Losev)
-//
 // Macros and functions for implementing parameterized tests
-// in Google C++ Testing Framework (Google Test)
+// in Google C++ Testing and Mocking Framework (Google Test)
 //
 // This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
 //
+// GOOGLETEST_CM0001 DO NOT DELETE
 #ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
 #define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
 
@@ -79,7 +78,7 @@ TEST_P(FooTest, HasBlahBlah) {
 // Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test
 // case with any set of parameters you want. Google Test defines a number
 // of functions for generating test parameters. They return what we call
-// (surprise!) parameter generators. Here is a  summary of them, which
+// (surprise!) parameter generators. Here is a summary of them, which
 // are all in the testing namespace:
 //
 //
@@ -185,15 +184,10 @@ TEST_P(DerivedTest, DoesBlah) {
 # include <utility>
 #endif
 
-// scripts/fuse_gtest.py depends on gtest's own header being #included
-// *unconditionally*.  Therefore these #includes cannot be moved
-// inside #if GTEST_HAS_PARAM_TEST.
 #include "gtest/internal/gtest-internal.h"
 #include "gtest/internal/gtest-param-util.h"
 #include "gtest/internal/gtest-param-util-generated.h"
 
-#if GTEST_HAS_PARAM_TEST
-
 namespace testing {
 
 // Functions producing parameter generators.
@@ -273,7 +267,7 @@ internal::ParamGenerator<T> Range(T start, T end) {
 // each with C-string values of "foo", "bar", and "baz":
 //
 // const char* strings[] = {"foo", "bar", "baz"};
-// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings));
+// INSTANTIATE_TEST_CASE_P(StringSequence, StringTest, ValuesIn(strings));
 //
 // This instantiates tests from test case StlStringTest
 // each with STL strings with values "a" and "b":
@@ -1375,8 +1369,6 @@ internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
 }
 # endif  // GTEST_HAS_COMBINE
 
-
-
 # define TEST_P(test_case_name, test_name) \
   class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
       : public test_case_name { \
@@ -1390,8 +1382,8 @@ internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
               #test_case_name, \
               ::testing::internal::CodeLocation(\
                   __FILE__, __LINE__))->AddTestPattern(\
-                      #test_case_name, \
-                      #test_name, \
+                      GTEST_STRINGIFY_(test_case_name), \
+                      GTEST_STRINGIFY_(test_name), \
                       new ::testing::internal::TestMetaFactory< \
                           GTEST_TEST_CLASS_NAME_(\
                               test_case_name, test_name)>()); \
@@ -1412,11 +1404,11 @@ internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
 // type testing::TestParamInfo<class ParamType>, and return std::string.
 //
 // testing::PrintToStringParamName is a builtin test suffix generator that
-// returns the value of testing::PrintToString(GetParam()). It does not work
-// for std::string or C strings.
+// returns the value of testing::PrintToString(GetParam()).
 //
 // Note: test names must be non-empty, unique, and may only contain ASCII
-// alphanumeric characters or underscore.
+// alphanumeric characters or underscore. Because PrintToString adds quotes
+// to std::string and C strings, it won't work for these types.
 
 # define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator, ...) \
   static ::testing::internal::ParamGenerator<test_case_name::ParamType> \
@@ -1426,7 +1418,7 @@ internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
     return ::testing::internal::GetParamNameGen<test_case_name::ParamType> \
         (__VA_ARGS__)(info); \
   } \
-  int gtest_##prefix##test_case_name##_dummy_ GTEST_ATTRIBUTE_UNUSED_ = \
+  static int gtest_##prefix##test_case_name##_dummy_ GTEST_ATTRIBUTE_UNUSED_ = \
       ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
           GetTestCasePatternHolder<test_case_name>(\
               #test_case_name, \
@@ -1439,6 +1431,4 @@ internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
 
 }  // namespace testing
 
-#endif  // GTEST_HAS_PARAM_TEST
-
 #endif  // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
index 8a33164cb38ab505962f75b157654aeee5459f3a..51865f84e6f6fa3e0fce198700b5a5dfe5f6550e 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: wan@google.com (Zhanyong Wan)
 
-// Google Test - The Google C++ Testing Framework
+
+// Google Test - The Google C++ Testing and Mocking Framework
 //
 // This file implements a universal value printer that can print a
 // value of any type T:
 //   2. operator<<(ostream&, const T&) defined in either foo or the
 //      global namespace.
 //
+// However if T is an STL-style container then it is printed element-wise
+// unless foo::PrintTo(const T&, ostream*) is defined. Note that
+// operator<<() is ignored for container types.
+//
 // If none of the above is defined, it will print the debug string of
 // the value if it is a protocol buffer, or print the raw bytes in the
 // value otherwise.
@@ -92,6 +95,8 @@
 // being defined as many user-defined container types don't have
 // value_type.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
 #define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
 
 # include <tuple>
 #endif
 
+#if GTEST_HAS_ABSL
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "absl/types/variant.h"
+#endif  // GTEST_HAS_ABSL
+
 namespace testing {
 
 // Definitions in the 'internal' and 'internal2' name spaces are
@@ -125,7 +136,11 @@ enum TypeKind {
   kProtobuf,              // a protobuf type
   kConvertibleToInteger,  // a type implicitly convertible to BiggestInt
                           // (e.g. a named or unnamed enum type)
-  kOtherType              // anything else
+#if GTEST_HAS_ABSL
+  kConvertibleToStringView,  // a type implicitly convertible to
+                             // absl::string_view
+#endif
+  kOtherType  // anything else
 };
 
 // TypeWithoutFormatter<T, kTypeKind>::PrintValue(value, os) is called
@@ -137,7 +152,8 @@ class TypeWithoutFormatter {
  public:
   // This default version is called when kTypeKind is kOtherType.
   static void PrintValue(const T& value, ::std::ostream* os) {
-    PrintBytesInObjectTo(reinterpret_cast<const unsigned char*>(&value),
+    PrintBytesInObjectTo(static_cast<const unsigned char*>(
+                             reinterpret_cast<const void*>(&value)),
                          sizeof(value), os);
   }
 };
@@ -151,10 +167,10 @@ template <typename T>
 class TypeWithoutFormatter<T, kProtobuf> {
  public:
   static void PrintValue(const T& value, ::std::ostream* os) {
-    const ::testing::internal::string short_str = value.ShortDebugString();
-    const ::testing::internal::string pretty_str =
-        short_str.length() <= kProtobufOneLinerMaxLength ?
-        short_str : ("\n" + value.DebugString());
+    std::string pretty_str = value.ShortDebugString();
+    if (pretty_str.length() > kProtobufOneLinerMaxLength) {
+      pretty_str = "\n" + value.DebugString();
+    }
     *os << ("<" + pretty_str + ">");
   }
 };
@@ -175,6 +191,19 @@ class TypeWithoutFormatter<T, kConvertibleToInteger> {
   }
 };
 
+#if GTEST_HAS_ABSL
+template <typename T>
+class TypeWithoutFormatter<T, kConvertibleToStringView> {
+ public:
+  // Since T has neither operator<< nor PrintTo() but can be implicitly
+  // converted to absl::string_view, we print it as a absl::string_view.
+  //
+  // Note: the implementation is further below, as it depends on
+  // internal::PrintTo symbol which is defined later in the file.
+  static void PrintValue(const T& value, ::std::ostream* os);
+};
+#endif
+
 // Prints the given value to the given ostream.  If the value is a
 // protocol message, its debug string is printed; if it's an enum or
 // of a type implicitly convertible to BiggestInt, it's printed as an
@@ -202,10 +231,19 @@ class TypeWithoutFormatter<T, kConvertibleToInteger> {
 template <typename Char, typename CharTraits, typename T>
 ::std::basic_ostream<Char, CharTraits>& operator<<(
     ::std::basic_ostream<Char, CharTraits>& os, const T& x) {
-  TypeWithoutFormatter<T,
-      (internal::IsAProtocolMessage<T>::value ? kProtobuf :
-       internal::ImplicitlyConvertible<const T&, internal::BiggestInt>::value ?
-       kConvertibleToInteger : kOtherType)>::PrintValue(x, &os);
+  TypeWithoutFormatter<T, (internal::IsAProtocolMessage<T>::value
+                               ? kProtobuf
+                               : internal::ImplicitlyConvertible<
+                                     const T&, internal::BiggestInt>::value
+                                     ? kConvertibleToInteger
+                                     :
+#if GTEST_HAS_ABSL
+                                     internal::ImplicitlyConvertible<
+                                         const T&, absl::string_view>::value
+                                         ? kConvertibleToStringView
+                                         :
+#endif
+                                         kOtherType)>::PrintValue(x, &os);
   return os;
 }
 
@@ -364,11 +402,18 @@ class UniversalPrinter;
 template <typename T>
 void UniversalPrint(const T& value, ::std::ostream* os);
 
+enum DefaultPrinterType {
+  kPrintContainer,
+  kPrintPointer,
+  kPrintFunctionPointer,
+  kPrintOther,
+};
+template <DefaultPrinterType type> struct WrapPrinterType {};
+
 // Used to print an STL-style container when the user doesn't define
 // a PrintTo() for it.
 template <typename C>
-void DefaultPrintTo(IsContainer /* dummy */,
-                    false_type /* is not a pointer */,
+void DefaultPrintTo(WrapPrinterType<kPrintContainer> /* dummy */,
                     const C& container, ::std::ostream* os) {
   const size_t kMaxCount = 32;  // The maximum number of elements to print.
   *os << '{';
@@ -401,40 +446,34 @@ void DefaultPrintTo(IsContainer /* dummy */,
 // implementation-defined.  Therefore they will be printed as raw
 // bytes.)
 template <typename T>
-void DefaultPrintTo(IsNotContainer /* dummy */,
-                    true_type /* is a pointer */,
+void DefaultPrintTo(WrapPrinterType<kPrintPointer> /* dummy */,
                     T* p, ::std::ostream* os) {
   if (p == NULL) {
     *os << "NULL";
   } else {
-    // C++ doesn't allow casting from a function pointer to any object
-    // pointer.
-    //
-    // IsTrue() silences warnings: "Condition is always true",
-    // "unreachable code".
-    if (IsTrue(ImplicitlyConvertible<T*, const void*>::value)) {
-      // T is not a function type.  We just call << to print p,
-      // relying on ADL to pick up user-defined << for their pointer
-      // types, if any.
-      *os << p;
-    } else {
-      // T is a function type, so '*os << p' doesn't do what we want
-      // (it just prints p as bool).  We want to print p as a const
-      // void*.  However, we cannot cast it to const void* directly,
-      // even using reinterpret_cast, as earlier versions of gcc
-      // (e.g. 3.4.5) cannot compile the cast when p is a function
-      // pointer.  Casting to UInt64 first solves the problem.
-      *os << reinterpret_cast<const void*>(
-          reinterpret_cast<internal::UInt64>(p));
-    }
+    // T is not a function type.  We just call << to print p,
+    // relying on ADL to pick up user-defined << for their pointer
+    // types, if any.
+    *os << p;
+  }
+}
+template <typename T>
+void DefaultPrintTo(WrapPrinterType<kPrintFunctionPointer> /* dummy */,
+                    T* p, ::std::ostream* os) {
+  if (p == NULL) {
+    *os << "NULL";
+  } else {
+    // T is a function type, so '*os << p' doesn't do what we want
+    // (it just prints p as bool).  We want to print p as a const
+    // void*.
+    *os << reinterpret_cast<const void*>(p);
   }
 }
 
 // Used to print a non-container, non-pointer value when the user
 // doesn't define PrintTo() for it.
 template <typename T>
-void DefaultPrintTo(IsNotContainer /* dummy */,
-                    false_type /* is not a pointer */,
+void DefaultPrintTo(WrapPrinterType<kPrintOther> /* dummy */,
                     const T& value, ::std::ostream* os) {
   ::testing_internal::DefaultPrintNonContainerTo(value, os);
 }
@@ -452,11 +491,8 @@ void DefaultPrintTo(IsNotContainer /* dummy */,
 // wants).
 template <typename T>
 void PrintTo(const T& value, ::std::ostream* os) {
-  // DefaultPrintTo() is overloaded.  The type of its first two
-  // arguments determine which version will be picked.  If T is an
-  // STL-style container, the version for container will be called; if
-  // T is a pointer, the pointer version will be called; otherwise the
-  // generic version will be called.
+  // DefaultPrintTo() is overloaded.  The type of its first argument
+  // determines which version will be picked.
   //
   // Note that we check for container types here, prior to we check
   // for protocol message types in our operator<<.  The rationale is:
@@ -468,13 +504,27 @@ void PrintTo(const T& value, ::std::ostream* os) {
   // elements; therefore we check for container types here to ensure
   // that our format is used.
   //
-  // The second argument of DefaultPrintTo() is needed to bypass a bug
-  // in Symbian's C++ compiler that prevents it from picking the right
-  // overload between:
-  //
-  //   PrintTo(const T& x, ...);
-  //   PrintTo(T* x, ...);
-  DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os);
+  // Note that MSVC and clang-cl do allow an implicit conversion from
+  // pointer-to-function to pointer-to-object, but clang-cl warns on it.
+  // So don't use ImplicitlyConvertible if it can be helped since it will
+  // cause this warning, and use a separate overload of DefaultPrintTo for
+  // function pointers so that the `*os << p` in the object pointer overload
+  // doesn't cause that warning either.
+  DefaultPrintTo(
+      WrapPrinterType <
+                  (sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) &&
+              !IsRecursiveContainer<T>::value
+          ? kPrintContainer
+          : !is_pointer<T>::value
+                ? kPrintOther
+#if GTEST_LANG_CXX11
+                : std::is_function<typename std::remove_pointer<T>::type>::value
+#else
+                : !internal::ImplicitlyConvertible<T, const void*>::value
+#endif
+                      ? kPrintFunctionPointer
+                      : kPrintPointer > (),
+      value, os);
 }
 
 // The following list of PrintTo() overloads tells
@@ -581,6 +631,17 @@ inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
 }
 #endif  // GTEST_HAS_STD_WSTRING
 
+#if GTEST_HAS_ABSL
+// Overload for absl::string_view.
+inline void PrintTo(absl::string_view sp, ::std::ostream* os) {
+  PrintTo(::std::string(sp), os);
+}
+#endif  // GTEST_HAS_ABSL
+
+#if GTEST_LANG_CXX11
+inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; }
+#endif  // GTEST_LANG_CXX11
+
 #if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
 // Helper function for printing a tuple.  T must be instantiated with
 // a tuple type.
@@ -710,6 +771,48 @@ class UniversalPrinter {
   GTEST_DISABLE_MSC_WARNINGS_POP_()
 };
 
+#if GTEST_HAS_ABSL
+
+// Printer for absl::optional
+
+template <typename T>
+class UniversalPrinter<::absl::optional<T>> {
+ public:
+  static void Print(const ::absl::optional<T>& value, ::std::ostream* os) {
+    *os << '(';
+    if (!value) {
+      *os << "nullopt";
+    } else {
+      UniversalPrint(*value, os);
+    }
+    *os << ')';
+  }
+};
+
+// Printer for absl::variant
+
+template <typename... T>
+class UniversalPrinter<::absl::variant<T...>> {
+ public:
+  static void Print(const ::absl::variant<T...>& value, ::std::ostream* os) {
+    *os << '(';
+    absl::visit(Visitor{os}, value);
+    *os << ')';
+  }
+
+ private:
+  struct Visitor {
+    template <typename U>
+    void operator()(const U& u) const {
+      *os << "'" << GetTypeName<U>() << "' with value ";
+      UniversalPrint(u, os);
+    }
+    ::std::ostream* os;
+  };
+};
+
+#endif  // GTEST_HAS_ABSL
+
 // UniversalPrintArray(begin, len, os) prints an array of 'len'
 // elements, starting at address 'begin'.
 template <typename T>
@@ -723,7 +826,7 @@ void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
     // If the array has more than kThreshold elements, we'll have to
     // omit some details by printing only the first and the last
     // kChunkSize elements.
-    // TODO(wan@google.com): let the user control the threshold using a flag.
+    // FIXME: let the user control the threshold using a flag.
     if (len <= kThreshold) {
       PrintRawArrayTo(begin, len, os);
     } else {
@@ -805,7 +908,7 @@ class UniversalTersePrinter<const char*> {
     if (str == NULL) {
       *os << "NULL";
     } else {
-      UniversalPrint(string(str), os);
+      UniversalPrint(std::string(str), os);
     }
   }
 };
@@ -856,7 +959,7 @@ void UniversalPrint(const T& value, ::std::ostream* os) {
   UniversalPrinter<T1>::Print(value, os);
 }
 
-typedef ::std::vector<string> Strings;
+typedef ::std::vector< ::std::string> Strings;
 
 // TuplePolicy<TupleT> must provide:
 // - tuple_size
@@ -875,12 +978,13 @@ struct TuplePolicy {
   static const size_t tuple_size = ::std::tr1::tuple_size<Tuple>::value;
 
   template <size_t I>
-  struct tuple_element : ::std::tr1::tuple_element<I, Tuple> {};
+  struct tuple_element : ::std::tr1::tuple_element<static_cast<int>(I), Tuple> {
+  };
 
   template <size_t I>
-  static typename AddReference<
-      const typename ::std::tr1::tuple_element<I, Tuple>::type>::type get(
-      const Tuple& tuple) {
+  static typename AddReference<const typename ::std::tr1::tuple_element<
+      static_cast<int>(I), Tuple>::type>::type
+  get(const Tuple& tuple) {
     return ::std::tr1::get<I>(tuple);
   }
 };
@@ -976,6 +1080,16 @@ Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
 
 }  // namespace internal
 
+#if GTEST_HAS_ABSL
+namespace internal2 {
+template <typename T>
+void TypeWithoutFormatter<T, kConvertibleToStringView>::PrintValue(
+    const T& value, ::std::ostream* os) {
+  internal::PrintTo(absl::string_view(value), os);
+}
+}  // namespace internal2
+#endif
+
 template <typename T>
 ::std::string PrintToString(const T& value) {
   ::std::stringstream ss;
index f63fa9a1b2a875081da3606fc4578ee5a94abb8c..1e8983938ea2792c52690ead29b00ab58cdaae9c 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: wan@google.com (Zhanyong Wan)
+
 //
 // Utilities for testing Google Test itself and code that uses Google Test
 // (e.g. frameworks built on top of Google Test).
 
+// GOOGLETEST_CM0004 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_
 #define GTEST_INCLUDE_GTEST_GTEST_SPI_H_
 
 #include "gtest/gtest.h"
 
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
 namespace testing {
 
 // This helper class can be used to mock out Google Test failure reporting
@@ -97,13 +101,12 @@ class GTEST_API_ SingleFailureChecker {
  public:
   // The constructor remembers the arguments.
   SingleFailureChecker(const TestPartResultArray* results,
-                       TestPartResult::Type type,
-                       const string& substr);
+                       TestPartResult::Type type, const std::string& substr);
   ~SingleFailureChecker();
  private:
   const TestPartResultArray* const results_;
   const TestPartResult::Type type_;
-  const string substr_;
+  const std::string substr_;
 
   GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
 };
@@ -112,6 +115,8 @@ class GTEST_API_ SingleFailureChecker {
 
 }  // namespace testing
 
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
 // A set of macros for testing Google Test assertions or code that's expected
 // to generate Google Test fatal failures.  It verifies that the given
 // statement will cause exactly one fatal Google Test failure with 'substr'
index 77eb844839d8927a293d25704df9b1707a4273a5..1c7b89e08796d9730f9f8eeae406227177dba519 100644 (file)
@@ -27,8 +27,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Author: mheule@google.com (Markus Heule)
-//
+// GOOGLETEST_CM0001 DO NOT DELETE
 
 #ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
 #define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
@@ -38,6 +37,9 @@
 #include "gtest/internal/gtest-internal.h"
 #include "gtest/internal/gtest-string.h"
 
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
 namespace testing {
 
 // A copyable object representing the result of a test part (i.e. an
@@ -143,7 +145,7 @@ class GTEST_API_ TestPartResultArray {
 };
 
 // This interface knows how to report a test part result.
-class TestPartResultReporterInterface {
+class GTEST_API_ TestPartResultReporterInterface {
  public:
   virtual ~TestPartResultReporterInterface() {}
 
@@ -176,4 +178,6 @@ class GTEST_API_ HasNewFatalFailureHelper
 
 }  // namespace testing
 
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
 #endif  // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
index 5f69d5678eaf34a600ca45f8cf934083162fafd2..74bce46bdc50f4c8cde2c6d7264911ff4d99cf19 100644 (file)
@@ -26,8 +26,9 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: wan@google.com (Zhanyong Wan)
+
+
+// GOOGLETEST_CM0001 DO NOT DELETE
 
 #ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
 #define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
@@ -82,6 +83,24 @@ TYPED_TEST(FooTest, DoesBlah) {
 
 TYPED_TEST(FooTest, HasPropertyA) { ... }
 
+// TYPED_TEST_CASE takes an optional third argument which allows to specify a
+// class that generates custom test name suffixes based on the type. This should
+// be a class which has a static template function GetName(int index) returning
+// a string for each type. The provided integer index equals the index of the
+// type in the provided type list. In many cases the index can be ignored.
+//
+// For example:
+//   class MyTypeNames {
+//    public:
+//     template <typename T>
+//     static std::string GetName(int) {
+//       if (std::is_same<T, char>()) return "char";
+//       if (std::is_same<T, int>()) return "int";
+//       if (std::is_same<T, unsigned int>()) return "unsignedInt";
+//     }
+//   };
+//   TYPED_TEST_CASE(FooTest, MyTypes, MyTypeNames);
+
 #endif  // 0
 
 // Type-parameterized tests are abstract test patterns parameterized
@@ -143,6 +162,11 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
 // If the type list contains only one type, you can write that type
 // directly without Types<...>:
 //   INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
+//
+// Similar to the optional argument of TYPED_TEST_CASE above,
+// INSTANTIATE_TEST_CASE_P takes an optional fourth argument which allows to
+// generate custom names.
+//   INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes, MyTypeNames);
 
 #endif  // 0
 
@@ -159,32 +183,46 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
 // given test case.
 # define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
 
+// Expands to the name of the typedef for the NameGenerator, responsible for
+// creating the suffixes of the name.
+#define GTEST_NAME_GENERATOR_(TestCaseName) \
+  gtest_type_params_##TestCaseName##_NameGenerator
+
 // The 'Types' template argument below must have spaces around it
 // since some compilers may choke on '>>' when passing a template
 // instance (e.g. Types<int>)
-# define TYPED_TEST_CASE(CaseName, Types) \
-  typedef ::testing::internal::TypeList< Types >::type \
-      GTEST_TYPE_PARAMS_(CaseName)
-
-# define TYPED_TEST(CaseName, TestName) \
-  template <typename gtest_TypeParam_> \
-  class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
-      : public CaseName<gtest_TypeParam_> { \
-   private: \
-    typedef CaseName<gtest_TypeParam_> TestFixture; \
-    typedef gtest_TypeParam_ TypeParam; \
-    virtual void TestBody(); \
-  }; \
-  bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \
-      ::testing::internal::TypeParameterizedTest< \
-          CaseName, \
-          ::testing::internal::TemplateSel< \
-              GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \
-          GTEST_TYPE_PARAMS_(CaseName)>::Register(\
-              "", ::testing::internal::CodeLocation(__FILE__, __LINE__), \
-              #CaseName, #TestName, 0); \
-  template <typename gtest_TypeParam_> \
-  void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody()
+# define TYPED_TEST_CASE(CaseName, Types, ...)                             \
+  typedef ::testing::internal::TypeList< Types >::type GTEST_TYPE_PARAMS_( \
+      CaseName);                                                           \
+  typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type    \
+      GTEST_NAME_GENERATOR_(CaseName)
+
+# define TYPED_TEST(CaseName, TestName)                                       \
+  template <typename gtest_TypeParam_>                                        \
+  class GTEST_TEST_CLASS_NAME_(CaseName, TestName)                            \
+      : public CaseName<gtest_TypeParam_> {                                   \
+   private:                                                                   \
+    typedef CaseName<gtest_TypeParam_> TestFixture;                           \
+    typedef gtest_TypeParam_ TypeParam;                                       \
+    virtual void TestBody();                                                  \
+  };                                                                          \
+  static bool gtest_##CaseName##_##TestName##_registered_                     \
+        GTEST_ATTRIBUTE_UNUSED_ =                                             \
+      ::testing::internal::TypeParameterizedTest<                             \
+          CaseName,                                                           \
+          ::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName,   \
+                                                                  TestName)>, \
+          GTEST_TYPE_PARAMS_(                                                 \
+              CaseName)>::Register("",                                        \
+                                   ::testing::internal::CodeLocation(         \
+                                       __FILE__, __LINE__),                   \
+                                   #CaseName, #TestName, 0,                   \
+                                   ::testing::internal::GenerateNames<        \
+                                       GTEST_NAME_GENERATOR_(CaseName),       \
+                                       GTEST_TYPE_PARAMS_(CaseName)>());      \
+  template <typename gtest_TypeParam_>                                        \
+  void GTEST_TEST_CLASS_NAME_(CaseName,                                       \
+                              TestName)<gtest_TypeParam_>::TestBody()
 
 #endif  // GTEST_HAS_TYPED_TEST
 
@@ -241,22 +279,27 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
   namespace GTEST_CASE_NAMESPACE_(CaseName) { \
   typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \
   } \
-  static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \
-      GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\
-          __FILE__, __LINE__, #__VA_ARGS__)
+  static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) \
+      GTEST_ATTRIBUTE_UNUSED_ = \
+          GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames( \
+              __FILE__, __LINE__, #__VA_ARGS__)
 
 // The 'Types' template argument below must have spaces around it
 // since some compilers may choke on '>>' when passing a template
 // instance (e.g. Types<int>)
-# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \
-  bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \
-      ::testing::internal::TypeParameterizedTestCase<CaseName, \
-          GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \
-          ::testing::internal::TypeList< Types >::type>::Register(\
-              #Prefix, \
-              ::testing::internal::CodeLocation(__FILE__, __LINE__), \
-              &GTEST_TYPED_TEST_CASE_P_STATE_(CaseName), \
-              #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
+# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types, ...)      \
+  static bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ =       \
+      ::testing::internal::TypeParameterizedTestCase<                     \
+          CaseName, GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_,     \
+          ::testing::internal::TypeList< Types >::type>::                 \
+          Register(#Prefix,                                               \
+                   ::testing::internal::CodeLocation(__FILE__, __LINE__), \
+                   &GTEST_TYPED_TEST_CASE_P_STATE_(CaseName), #CaseName,  \
+                   GTEST_REGISTERED_TEST_NAMES_(CaseName),                \
+                   ::testing::internal::GenerateNames<                    \
+                       ::testing::internal::NameGeneratorSelector<        \
+                           __VA_ARGS__>::type,                            \
+                       ::testing::internal::TypeList< Types >::type>())
 
 #endif  // GTEST_HAS_TYPED_TEST_P
 
index f846c5bd66964977fa91eff90597ddd0bdcdce8c..5df4b0a3a7127690455a945cb1528b6bd7449b66 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 //
-// Author: wan@google.com (Zhanyong Wan)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // This header file defines the public API for Google Test.  It should be
 // included by any test program that uses Google Test.
@@ -48,6 +47,8 @@
 // registration from Barthelemy Dagenais' (barthelemy@prologique.com)
 // easyUnit framework.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_GTEST_H_
 #define GTEST_INCLUDE_GTEST_GTEST_H_
 
@@ -65,6 +66,9 @@
 #include "gtest/gtest-test-part.h"
 #include "gtest/gtest-typed-test.h"
 
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
 // Depending on the platform, different string classes are available.
 // On Linux, in addition to ::std::string, Google also makes use of
 // class ::string, which has the same interface as ::std::string, but
 
 namespace testing {
 
+// Silence C4100 (unreferenced formal parameter) and 4805
+// unsafe mix of type 'const int' and type 'const bool'
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4805)
+# pragma warning(disable:4100)
+#endif
+
+
 // Declares the flags.
 
 // This flag temporary enables the disabled tests.
@@ -103,6 +116,10 @@ GTEST_DECLARE_string_(color);
 // the tests to run. If the filter is not given all tests are executed.
 GTEST_DECLARE_string_(filter);
 
+// This flag controls whether Google Test installs a signal handler that dumps
+// debugging information when fatal signals are raised.
+GTEST_DECLARE_bool_(install_failure_signal_handler);
+
 // This flag causes the Google Test to list tests. None of the tests listed
 // are actually run if the flag is provided.
 GTEST_DECLARE_bool_(list_tests);
@@ -115,6 +132,9 @@ GTEST_DECLARE_string_(output);
 // test.
 GTEST_DECLARE_bool_(print_time);
 
+// This flags control whether Google Test prints UTF8 characters as text.
+GTEST_DECLARE_bool_(print_utf8);
+
 // This flag specifies the random number seed.
 GTEST_DECLARE_int32_(random_seed);
 
@@ -135,7 +155,7 @@ GTEST_DECLARE_int32_(stack_trace_depth);
 
 // When this flag is specified, a failed assertion will throw an
 // exception if exceptions are enabled, or exit the program with a
-// non-zero code otherwise.
+// non-zero code otherwise. For use with an external test framework.
 GTEST_DECLARE_bool_(throw_on_failure);
 
 // When this flag is set with a "host:port" string, on supported
@@ -143,6 +163,10 @@ GTEST_DECLARE_bool_(throw_on_failure);
 // the specified host machine.
 GTEST_DECLARE_string_(stream_result_to);
 
+#if GTEST_USE_OWN_FLAGFILE_FLAG_
+GTEST_DECLARE_string_(flagfile);
+#endif  // GTEST_USE_OWN_FLAGFILE_FLAG_
+
 // The upper limit for valid stack trace depths.
 const int kMaxStackTraceDepth = 100;
 
@@ -160,6 +184,7 @@ class TestEventListenersAccessor;
 class TestEventRepeater;
 class UnitTestRecordPropertyTestHelper;
 class WindowsDeathTest;
+class FuchsiaDeathTest;
 class UnitTestImpl* GetUnitTestImpl();
 void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
                                     const std::string& message);
@@ -259,7 +284,9 @@ class GTEST_API_ AssertionResult {
   // Used in EXPECT_TRUE/FALSE(assertion_result).
   AssertionResult(const AssertionResult& other);
 
+#if defined(_MSC_VER) && _MSC_VER < 1910
   GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */)
+#endif
 
   // Used in the EXPECT_TRUE/FALSE(bool_expression).
   //
@@ -276,7 +303,9 @@ class GTEST_API_ AssertionResult {
           /*enabler*/ = NULL)
       : success_(success) {}
 
+#if defined(_MSC_VER) && _MSC_VER < 1910
   GTEST_DISABLE_MSC_WARNINGS_POP_()
+#endif
 
   // Assignment operator.
   AssertionResult& operator=(AssertionResult other) {
@@ -297,7 +326,7 @@ class GTEST_API_ AssertionResult {
   const char* message() const {
     return message_.get() != NULL ?  message_->c_str() : "";
   }
-  // TODO(vladl@google.com): Remove this after making sure no clients use it.
+  // FIXME: Remove this after making sure no clients use it.
   // Deprecated; please use message() instead.
   const char* failure_message() const { return message(); }
 
@@ -345,6 +374,15 @@ GTEST_API_ AssertionResult AssertionFailure();
 // Deprecated; use AssertionFailure() << msg.
 GTEST_API_ AssertionResult AssertionFailure(const Message& msg);
 
+}  // namespace testing
+
+// Includes the auto-generated header that implements a family of generic
+// predicate assertion macros. This include comes late because it relies on
+// APIs declared above.
+#include "gtest/gtest_pred_impl.h"
+
+namespace testing {
+
 // The abstract class that all tests inherit from.
 //
 // In Google Test, a unit test program contains one or many TestCases, and
@@ -355,7 +393,7 @@ GTEST_API_ AssertionResult AssertionFailure(const Message& msg);
 // this for you.
 //
 // The only time you derive from Test is when defining a test fixture
-// to be used a TEST_F.  For example:
+// to be used in a TEST_F.  For example:
 //
 //   class FooTest : public testing::Test {
 //    protected:
@@ -550,9 +588,8 @@ class GTEST_API_ TestResult {
   // Returns the elapsed time, in milliseconds.
   TimeInMillis elapsed_time() const { return elapsed_time_; }
 
-  // Returns the i-th test part result among all the results. i can range
-  // from 0 to test_property_count() - 1. If i is not in that range, aborts
-  // the program.
+  // Returns the i-th test part result among all the results. i can range from 0
+  // to total_part_count() - 1. If i is not in that range, aborts the program.
   const TestPartResult& GetTestPartResult(int i) const;
 
   // Returns the i-th test property. i can range from 0 to
@@ -569,6 +606,7 @@ class GTEST_API_ TestResult {
   friend class internal::TestResultAccessor;
   friend class internal::UnitTestImpl;
   friend class internal::WindowsDeathTest;
+  friend class internal::FuchsiaDeathTest;
 
   // Gets the vector of TestPartResults.
   const std::vector<TestPartResult>& test_part_results() const {
@@ -594,7 +632,7 @@ class GTEST_API_ TestResult {
 
   // Adds a failure if the key is a reserved attribute of Google Test
   // testcase tags.  Returns true if the property is valid.
-  // TODO(russr): Validate attribute names are legal and human readable.
+  // FIXME: Validate attribute names are legal and human readable.
   static bool ValidateTestProperty(const std::string& xml_element,
                                    const TestProperty& test_property);
 
@@ -675,6 +713,9 @@ class GTEST_API_ TestInfo {
   // Returns the line where this test is defined.
   int line() const { return location_.line; }
 
+  // Return true if this test should not be run because it's in another shard.
+  bool is_in_another_shard() const { return is_in_another_shard_; }
+
   // Returns true if this test should run, that is if the test is not
   // disabled (or it is disabled but the also_run_disabled_tests flag has
   // been specified) and its full name matches the user-specified filter.
@@ -695,10 +736,9 @@ class GTEST_API_ TestInfo {
 
   // Returns true iff this test will appear in the XML report.
   bool is_reportable() const {
-    // For now, the XML report includes all tests matching the filter.
-    // In the future, we may trim tests that are excluded because of
-    // sharding.
-    return matches_filter_;
+    // The XML report includes tests matching the filter, excluding those
+    // run in other shards.
+    return matches_filter_ && !is_in_another_shard_;
   }
 
   // Returns the result of the test.
@@ -762,6 +802,7 @@ class GTEST_API_ TestInfo {
   bool is_disabled_;                // True iff this test is disabled
   bool matches_filter_;             // True if this test matches the
                                     // user-specified filter.
+  bool is_in_another_shard_;        // Will be run in another shard.
   internal::TestFactoryBase* const factory_;  // The factory that creates
                                               // the test object
 
@@ -986,6 +1027,18 @@ class Environment {
   virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
 };
 
+#if GTEST_HAS_EXCEPTIONS
+
+// Exception which can be thrown from TestEventListener::OnTestPartResult.
+class GTEST_API_ AssertionException
+    : public internal::GoogleTestFailureException {
+ public:
+  explicit AssertionException(const TestPartResult& result)
+      : GoogleTestFailureException(result) {}
+};
+
+#endif  // GTEST_HAS_EXCEPTIONS
+
 // The interface for tracing execution of tests. The methods are organized in
 // the order the corresponding events are fired.
 class TestEventListener {
@@ -1014,6 +1067,8 @@ class TestEventListener {
   virtual void OnTestStart(const TestInfo& test_info) = 0;
 
   // Fired after a failed assertion or a SUCCEED() invocation.
+  // If you want to throw an exception from this function to skip to the next
+  // TEST, it must be AssertionException defined above, or inherited from it.
   virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0;
 
   // Fired after the test ends.
@@ -1180,14 +1235,12 @@ class GTEST_API_ UnitTest {
   // Returns the random seed used at the start of the current test run.
   int random_seed() const;
 
-#if GTEST_HAS_PARAM_TEST
   // Returns the ParameterizedTestCaseRegistry object used to keep track of
   // value-parameterized tests and instantiate and register them.
   //
   // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
   internal::ParameterizedTestCaseRegistry& parameterized_test_registry()
       GTEST_LOCK_EXCLUDED_(mutex_);
-#endif  // GTEST_HAS_PARAM_TEST
 
   // Gets the number of successful test cases.
   int successful_test_case_count() const;
@@ -1287,11 +1340,11 @@ class GTEST_API_ UnitTest {
   internal::UnitTestImpl* impl() { return impl_; }
   const internal::UnitTestImpl* impl() const { return impl_; }
 
-  // These classes and funcions are friends as they need to access private
+  // These classes and functions are friends as they need to access private
   // members of UnitTest.
+  friend class ScopedTrace;
   friend class Test;
   friend class internal::AssertHelper;
-  friend class internal::ScopedTrace;
   friend class internal::StreamingListenerTest;
   friend class internal::UnitTestRecordPropertyTestHelper;
   friend Environment* AddGlobalTestEnvironment(Environment* env);
@@ -1388,11 +1441,9 @@ AssertionResult CmpHelperEQ(const char* lhs_expression,
                             const char* rhs_expression,
                             const T1& lhs,
                             const T2& rhs) {
-GTEST_DISABLE_MSC_WARNINGS_PUSH_(4389 /* signed/unsigned mismatch */)
   if (lhs == rhs) {
     return AssertionSuccess();
   }
-GTEST_DISABLE_MSC_WARNINGS_POP_()
 
   return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs);
 }
@@ -1706,7 +1757,6 @@ class GTEST_API_ AssertHelper {
 
 }  // namespace internal
 
-#if GTEST_HAS_PARAM_TEST
 // The pure interface class that all value-parameterized tests inherit from.
 // A value-parameterized class must inherit from both ::testing::Test and
 // ::testing::WithParamInterface. In most cases that just means inheriting
@@ -1783,8 +1833,6 @@ template <typename T>
 class TestWithParam : public Test, public WithParamInterface<T> {
 };
 
-#endif  // GTEST_HAS_PARAM_TEST
-
 // Macros for indicating success/failure in test code.
 
 // ADD_FAILURE unconditionally adds a failure to the current test.
@@ -1857,22 +1905,18 @@ class TestWithParam : public Test, public WithParamInterface<T> {
 // AssertionResult. For more information on how to use AssertionResult with
 // these macros see comments on that class.
 #define EXPECT_TRUE(condition) \
-  GTEST_TEST_BOOLEAN_((condition), #condition, false, true, \
+  GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
                       GTEST_NONFATAL_FAILURE_)
 #define EXPECT_FALSE(condition) \
   GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
                       GTEST_NONFATAL_FAILURE_)
 #define ASSERT_TRUE(condition) \
-  GTEST_TEST_BOOLEAN_((condition), #condition, false, true, \
+  GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
                       GTEST_FATAL_FAILURE_)
 #define ASSERT_FALSE(condition) \
   GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
                       GTEST_FATAL_FAILURE_)
 
-// Includes the auto-generated header that implements a family of
-// generic predicate assertion macros.
-#include "gtest/gtest_pred_impl.h"
-
 // Macros for testing equalities and inequalities.
 //
 //    * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2
@@ -1914,8 +1958,8 @@ class TestWithParam : public Test, public WithParamInterface<T> {
 //
 // Examples:
 //
-//   EXPECT_NE(5, Foo());
-//   EXPECT_EQ(NULL, a_pointer);
+//   EXPECT_NE(Foo(), 5);
+//   EXPECT_EQ(a_pointer, NULL);
 //   ASSERT_LT(i, array_size);
 //   ASSERT_GT(records.size(), 0) << "There is no record left.";
 
@@ -2101,6 +2145,57 @@ GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2,
 #define EXPECT_NO_FATAL_FAILURE(statement) \
     GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_)
 
+// Causes a trace (including the given source file path and line number,
+// and the given message) to be included in every test failure message generated
+// by code in the scope of the lifetime of an instance of this class. The effect
+// is undone with the destruction of the instance.
+//
+// The message argument can be anything streamable to std::ostream.
+//
+// Example:
+//   testing::ScopedTrace trace("file.cc", 123, "message");
+//
+class GTEST_API_ ScopedTrace {
+ public:
+  // The c'tor pushes the given source file location and message onto
+  // a trace stack maintained by Google Test.
+
+  // Template version. Uses Message() to convert the values into strings.
+  // Slow, but flexible.
+  template <typename T>
+  ScopedTrace(const char* file, int line, const T& message) {
+    PushTrace(file, line, (Message() << message).GetString());
+  }
+
+  // Optimize for some known types.
+  ScopedTrace(const char* file, int line, const char* message) {
+    PushTrace(file, line, message ? message : "(null)");
+  }
+
+#if GTEST_HAS_GLOBAL_STRING
+  ScopedTrace(const char* file, int line, const ::string& message) {
+    PushTrace(file, line, message);
+  }
+#endif
+
+  ScopedTrace(const char* file, int line, const std::string& message) {
+    PushTrace(file, line, message);
+  }
+
+  // The d'tor pops the info pushed by the c'tor.
+  //
+  // Note that the d'tor is not virtual in order to be efficient.
+  // Don't inherit from ScopedTrace!
+  ~ScopedTrace();
+
+ private:
+  void PushTrace(const char* file, int line, std::string message);
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace);
+} GTEST_ATTRIBUTE_UNUSED_;  // A ScopedTrace object does its job in its
+                            // c'tor and d'tor.  Therefore it doesn't
+                            // need to be used otherwise.
+
 // Causes a trace (including the source file path, the current line
 // number, and the given message) to be included in every test failure
 // message generated by code in the current scope.  The effect is
@@ -2112,9 +2207,14 @@ GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2,
 // of the dummy variable name, thus allowing multiple SCOPED_TRACE()s
 // to appear in the same block - as long as they are on different
 // lines.
+//
+// Assuming that each thread maintains its own stack of traces.
+// Therefore, a SCOPED_TRACE() would (correctly) only affect the
+// assertions in its own thread.
 #define SCOPED_TRACE(message) \
-  ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\
-    __FILE__, __LINE__, ::testing::Message() << (message))
+  ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\
+    __FILE__, __LINE__, (message))
+
 
 // Compile-time assertion for type equality.
 // StaticAssertTypeEq<type1, type2>() compiles iff type1 and type2 are
@@ -2194,7 +2294,7 @@ bool StaticAssertTypeEq() {
 // name of the test within the test case.
 //
 // A test fixture class must be declared earlier.  The user should put
-// his test code between braces after using this macro.  Example:
+// the test code between braces after using this macro.  Example:
 //
 //   class FooTest : public testing::Test {
 //    protected:
@@ -2209,14 +2309,22 @@ bool StaticAssertTypeEq() {
 //   }
 //
 //   TEST_F(FooTest, ReturnsElementCountCorrectly) {
-//     EXPECT_EQ(0, a_.size());
-//     EXPECT_EQ(1, b_.size());
+//     EXPECT_EQ(a_.size(), 0);
+//     EXPECT_EQ(b_.size(), 1);
 //   }
 
 #define TEST_F(test_fixture, test_name)\
   GTEST_TEST_(test_fixture, test_name, test_fixture, \
               ::testing::internal::GetTypeId<test_fixture>())
 
+// Returns a path to temporary directory.
+// Tries to determine an appropriate directory for the platform.
+GTEST_API_ std::string TempDir();
+
+#ifdef _MSC_VER
+#  pragma warning(pop)
+#endif
+
 }  // namespace testing
 
 // Use this function in main() to run all tests.  It returns 0 if all
@@ -2233,4 +2341,6 @@ inline int RUN_ALL_TESTS() {
   return ::testing::UnitTest::GetInstance()->Run();
 }
 
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
 #endif  // GTEST_INCLUDE_GTEST_GTEST_H_
index 30ae712f50ed3d8e8446e4495af9b1abecab2e86..0c1105cb8eb2ef7c2616e67ff5556aab29ff2d1d 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command
+// This file is AUTOMATICALLY GENERATED on 01/02/2018 by command
 // 'gen_gtest_pred_impl.py 5'.  DO NOT EDIT BY HAND!
 //
 // Implements a family of generic predicate assertion macros.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
 #define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
 
-// Makes sure this header is not included before gtest.h.
-#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
-# error Do not include gtest_pred_impl.h directly.  Include gtest.h instead.
-#endif  // GTEST_INCLUDE_GTEST_GTEST_H_
+#include "gtest/gtest.h"
+
+namespace testing {
 
 // This header implements a family of generic predicate assertion
 // macros:
@@ -66,8 +67,6 @@
 // We also define the EXPECT_* variations.
 //
 // For now we only support predicates whose arity is at most 5.
-// Please email googletestframework@googlegroups.com if you need
-// support for higher arities.
 
 // GTEST_ASSERT_ is the basic statement to which all of the assertions
 // in this file reduce.  Don't use this in your code.
@@ -355,4 +354,6 @@ AssertionResult AssertPred5Helper(const char* pred_text,
 
 
 
+}  // namespace testing
+
 #endif  // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
index da80ddc6c70ee2dc773ed6765a4f89dfa8cb38e8..e651671ebde859fc731b5459bdc0661af0757cb6 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 //
-// Author: wan@google.com (Zhanyong Wan)
-//
-// Google C++ Testing Framework definitions useful in production code.
+// Google C++ Testing and Mocking Framework definitions useful in production code.
+// GOOGLETEST_CM0003 DO NOT DELETE
 
 #ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_
 #define GTEST_INCLUDE_GTEST_GTEST_PROD_H_
 //
 // class MyClass {
 //  private:
-//   void MyMethod();
-//   FRIEND_TEST(MyClassTest, MyMethod);
+//   void PrivateMethod();
+//   FRIEND_TEST(MyClassTest, PrivateMethodWorks);
 // };
 //
 // class MyClassTest : public testing::Test {
 //   // ...
 // };
 //
-// TEST_F(MyClassTest, MyMethod) {
-//   // Can call MyClass::MyMethod() here.
+// TEST_F(MyClassTest, PrivateMethodWorks) {
+//   // Can call MyClass::PrivateMethod() here.
 // }
+//
+// Note: The test class must be in the same namespace as the class being tested.
+// For example, putting MyClassTest in an anonymous namespace will not work.
 
 #define FRIEND_TEST(test_case_name, test_name)\
 friend class test_case_name##_##test_name##_Test
diff --git a/src/external/googletest/googletest/include/gtest/internal/custom/README.md b/src/external/googletest/googletest/include/gtest/internal/custom/README.md
new file mode 100644 (file)
index 0000000..ff391fb
--- /dev/null
@@ -0,0 +1,56 @@
+# Customization Points
+
+The custom directory is an injection point for custom user configurations.
+
+## Header `gtest.h`
+
+### The following macros can be defined:
+
+*   `GTEST_OS_STACK_TRACE_GETTER_` - The name of an implementation of
+    `OsStackTraceGetterInterface`.
+*   `GTEST_CUSTOM_TEMPDIR_FUNCTION_` - An override for `testing::TempDir()`. See
+    `testing::TempDir` for semantics and signature.
+
+## Header `gtest-port.h`
+
+The following macros can be defined:
+
+### Flag related macros:
+
+*   `GTEST_FLAG(flag_name)`
+*   `GTEST_USE_OWN_FLAGFILE_FLAG_` - Define to 0 when the system provides its
+    own flagfile flag parsing.
+*   `GTEST_DECLARE_bool_(name)`
+*   `GTEST_DECLARE_int32_(name)`
+*   `GTEST_DECLARE_string_(name)`
+*   `GTEST_DEFINE_bool_(name, default_val, doc)`
+*   `GTEST_DEFINE_int32_(name, default_val, doc)`
+*   `GTEST_DEFINE_string_(name, default_val, doc)`
+
+### Logging:
+
+*   `GTEST_LOG_(severity)`
+*   `GTEST_CHECK_(condition)`
+*   Functions `LogToStderr()` and `FlushInfoLog()` have to be provided too.
+
+### Threading:
+
+*   `GTEST_HAS_NOTIFICATION_` - Enabled if Notification is already provided.
+*   `GTEST_HAS_MUTEX_AND_THREAD_LOCAL_` - Enabled if `Mutex` and `ThreadLocal`
+    are already provided. Must also provide `GTEST_DECLARE_STATIC_MUTEX_(mutex)`
+    and `GTEST_DEFINE_STATIC_MUTEX_(mutex)`
+*   `GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)`
+*   `GTEST_LOCK_EXCLUDED_(locks)`
+
+### Underlying library support features
+
+*   `GTEST_HAS_CXXABI_H_`
+
+### Exporting API symbols:
+
+*   `GTEST_API_` - Specifier for exported symbols.
+
+## Header `gtest-printers.h`
+
+*   See documentation at `gtest/gtest-printers.h` for details on how to define a
+    custom printer.
index 7e744bd3bb38c3a21b752078b7a21eb3d62237f9..cd85d956d2dc17bd537f2142340bdf58f4737118 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Injection point for custom user configurations.
-// The following macros can be defined:
-//
-//   Flag related macros:
-//     GTEST_FLAG(flag_name)
-//     GTEST_USE_OWN_FLAGFILE_FLAG_  - Define to 0 when the system provides its
-//                                     own flagfile flag parsing.
-//     GTEST_DECLARE_bool_(name)
-//     GTEST_DECLARE_int32_(name)
-//     GTEST_DECLARE_string_(name)
-//     GTEST_DEFINE_bool_(name, default_val, doc)
-//     GTEST_DEFINE_int32_(name, default_val, doc)
-//     GTEST_DEFINE_string_(name, default_val, doc)
-//
-//   Test filtering:
-//     GTEST_TEST_FILTER_ENV_VAR_ - The name of an environment variable that
-//                                  will be used if --GTEST_FLAG(test_filter)
-//                                  is not provided.
-//
-//   Logging:
-//     GTEST_LOG_(severity)
-//     GTEST_CHECK_(condition)
-//     Functions LogToStderr() and FlushInfoLog() have to be provided too.
-//
-//   Threading:
-//     GTEST_HAS_NOTIFICATION_ - Enabled if Notification is already provided.
-//     GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ - Enabled if Mutex and ThreadLocal are
-//                                         already provided.
-//     Must also provide GTEST_DECLARE_STATIC_MUTEX_(mutex) and
-//     GTEST_DEFINE_STATIC_MUTEX_(mutex)
-//
-//     GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
-//     GTEST_LOCK_EXCLUDED_(locks)
+// Injection point for custom user configurations. See README for details
 //
 // ** Custom implementation starts here **
 
index 60c1ea050b61e93bfcf146e6111bfa50315c82ee..eb4467abcabea7ff76f9d2d527387c9da810148b 100644 (file)
@@ -31,8 +31,8 @@
 // installation of gTest.
 // It will be included from gtest-printers.h and the overrides in this file
 // will be visible to everyone.
-// See documentation at gtest/gtest-printers.h for details on how to define a
-// custom printer.
+//
+// Injection point for custom user configurations. See README for details
 //
 // ** Custom implementation starts here **
 
index c27412a8981844d92171abe558c1d68989d015fc..4c8e07be23f1fd6159afef2eefd7444034dd0604 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Injection point for custom user configurations.
-// The following macros can be defined:
-//
-// GTEST_OS_STACK_TRACE_GETTER_  - The name of an implementation of
-//                                 OsStackTraceGetterInterface.
+// Injection point for custom user configurations. See README for details
 //
 // ** Custom implementation starts here **
 
index 2b3a78f5bf86328fc1bf58f1682c9ee44a5057c2..0a9b42c8a572a87194c6510903e21ea2260fac5c 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // This header file defines internal utilities needed for implementing
 // death tests.  They are subject to change without notice.
+// GOOGLETEST_CM0001 DO NOT DELETE
 
 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
@@ -53,6 +52,9 @@ const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
 
 #if GTEST_HAS_DEATH_TEST
 
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
 // DeathTest is a class that hides much of the complexity of the
 // GTEST_DEATH_TEST_ macro.  It is abstract; its static Create method
 // returns a concrete class that depends on the prevailing death test
@@ -136,6 +138,8 @@ class GTEST_API_ DeathTest {
   GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest);
 };
 
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
 // Factory interface for death tests.  May be mocked out for testing.
 class DeathTestFactory {
  public:
@@ -218,14 +222,18 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
 // can be streamed.
 
 // This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in
-// NDEBUG mode. In this case we need the statements to be executed, the regex is
-// ignored, and the macro must accept a streamed message even though the message
-// is never printed.
-# define GTEST_EXECUTE_STATEMENT_(statement, regex) \
-  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
-  if (::testing::internal::AlwaysTrue()) { \
-     GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
-  } else \
+// NDEBUG mode. In this case we need the statements to be executed and the macro
+// must accept a streamed message even though the message is never printed.
+// The regex object is not evaluated, but it is used to prevent "unused"
+// warnings and to avoid an expression that doesn't compile in debug mode.
+#define GTEST_EXECUTE_STATEMENT_(statement, regex)             \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                \
+  if (::testing::internal::AlwaysTrue()) {                     \
+    GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+  } else if (!::testing::internal::AlwaysTrue()) {             \
+    const ::testing::internal::RE& gtest_regex = (regex);      \
+    static_cast<void>(gtest_regex);                            \
+  } else                                                       \
     ::testing::Message()
 
 // A class representing the parsed contents of the
@@ -264,53 +272,6 @@ class InternalRunDeathTestFlag {
 // the flag is specified; otherwise returns NULL.
 InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag();
 
-#else  // GTEST_HAS_DEATH_TEST
-
-// This macro is used for implementing macros such as
-// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
-// death tests are not supported. Those macros must compile on such systems
-// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on
-// systems that support death tests. This allows one to write such a macro
-// on a system that does not support death tests and be sure that it will
-// compile on a death-test supporting system.
-//
-// Parameters:
-//   statement -  A statement that a macro such as EXPECT_DEATH would test
-//                for program termination. This macro has to make sure this
-//                statement is compiled but not executed, to ensure that
-//                EXPECT_DEATH_IF_SUPPORTED compiles with a certain
-//                parameter iff EXPECT_DEATH compiles with it.
-//   regex     -  A regex that a macro such as EXPECT_DEATH would use to test
-//                the output of statement.  This parameter has to be
-//                compiled but not evaluated by this macro, to ensure that
-//                this macro only accepts expressions that a macro such as
-//                EXPECT_DEATH would accept.
-//   terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
-//                and a return statement for ASSERT_DEATH_IF_SUPPORTED.
-//                This ensures that ASSERT_DEATH_IF_SUPPORTED will not
-//                compile inside functions where ASSERT_DEATH doesn't
-//                compile.
-//
-//  The branch that has an always false condition is used to ensure that
-//  statement and regex are compiled (and thus syntactically correct) but
-//  never executed. The unreachable code macro protects the terminator
-//  statement from generating an 'unreachable code' warning in case
-//  statement unconditionally returns or throws. The Message constructor at
-//  the end allows the syntax of streaming additional messages into the
-//  macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
-# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \
-    GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
-    if (::testing::internal::AlwaysTrue()) { \
-      GTEST_LOG_(WARNING) \
-          << "Death tests are not supported on this platform.\n" \
-          << "Statement '" #statement "' cannot be verified."; \
-    } else if (::testing::internal::AlwaysFalse()) { \
-      ::testing::internal::RE::PartialMatch(".*", (regex)); \
-      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
-      terminator; \
-    } else \
-      ::testing::Message()
-
 #endif  // GTEST_HAS_DEATH_TEST
 
 }  // namespace internal
index 7a13b4b0de6083bbfe6963f8c0ed16e27de77012..ae38d95bf844fa9dc21739cf821276d5b3417e3a 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Author: keith.ray@gmail.com (Keith Ray)
-//
 // Google Test filepath utilities
 //
 // This header file declares classes and functions used internally by
 // Google Test.  They are subject to change without notice.
 //
-// This file is #included in <gtest/internal/gtest-internal.h>.
+// This file is #included in gtest/internal/gtest-internal.h.
 // Do not include this header file separately!
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
 
 #include "gtest/internal/gtest-string.h"
 
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
 namespace testing {
 namespace internal {
 
@@ -203,4 +206,6 @@ class GTEST_API_ FilePath {
 }  // namespace internal
 }  // namespace testing
 
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
 #endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
index 3f8e2dd9a0c80bab22f6869a13932aa5e641c785..b762f61fc53c3db9cfb753940d91b5d54ab36357 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // This header file declares functions and macros used internally by
 // Google Test.  They are subject to change without notice.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
 
@@ -61,8 +61,8 @@
 #include <vector>
 
 #include "gtest/gtest-message.h"
-#include "gtest/internal/gtest-string.h"
 #include "gtest/internal/gtest-filepath.h"
+#include "gtest/internal/gtest-string.h"
 #include "gtest/internal/gtest-type-util.h"
 
 // Due to C++ preprocessor weirdness, we need double indirection to
@@ -76,6 +76,9 @@
 #define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar)
 #define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar
 
+// Stringifies its argument.
+#define GTEST_STRINGIFY_(name) #name
+
 class ProtocolMessage;
 namespace proto2 { class Message; }
 
@@ -96,7 +99,6 @@ template <typename T>
 namespace internal {
 
 struct TraceInfo;                      // Information about a trace point.
-class ScopedTrace;                     // Implements scoped trace.
 class TestInfoImpl;                    // Opaque implementation of TestInfo
 class UnitTestImpl;                    // Opaque implementation of UnitTest
 
@@ -139,6 +141,9 @@ GTEST_API_ std::string AppendUserMessage(
 
 #if GTEST_HAS_EXCEPTIONS
 
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4275 \
+/* an exported class was derived from a class that was not exported */)
+
 // This exception is thrown by (and only by) a failed Google Test
 // assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions
 // are enabled).  We derive it from std::runtime_error, which is for
@@ -150,32 +155,15 @@ class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error {
   explicit GoogleTestFailureException(const TestPartResult& failure);
 };
 
-#endif  // GTEST_HAS_EXCEPTIONS
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4275
 
-// A helper class for creating scoped traces in user programs.
-class GTEST_API_ ScopedTrace {
- public:
-  // The c'tor pushes the given source file location and message onto
-  // a trace stack maintained by Google Test.
-  ScopedTrace(const char* file, int line, const Message& message);
-
-  // The d'tor pops the info pushed by the c'tor.
-  //
-  // Note that the d'tor is not virtual in order to be efficient.
-  // Don't inherit from ScopedTrace!
-  ~ScopedTrace();
-
- private:
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace);
-} GTEST_ATTRIBUTE_UNUSED_;  // A ScopedTrace object does its job in its
-                            // c'tor and d'tor.  Therefore it doesn't
-                            // need to be used otherwise.
+#endif  // GTEST_HAS_EXCEPTIONS
 
 namespace edit_distance {
 // Returns the optimal edits to go from 'left' to 'right'.
 // All edits cost the same, with replace having lower priority than
 // add/remove.
-// Simple implementation of the WagnerFischer algorithm.
+// Simple implementation of the Wagner-Fischer algorithm.
 // See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm
 enum EditType { kMatch, kAdd, kRemove, kReplace };
 GTEST_API_ std::vector<EditType> CalculateOptimalEdits(
@@ -502,9 +490,10 @@ typedef void (*SetUpTestCaseFunc)();
 typedef void (*TearDownTestCaseFunc)();
 
 struct CodeLocation {
-  CodeLocation(const string& a_file, int a_line) : file(a_file), line(a_line) {}
+  CodeLocation(const std::string& a_file, int a_line)
+      : file(a_file), line(a_line) {}
 
-  string file;
+  std::string file;
   int line;
 };
 
@@ -544,6 +533,9 @@ GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr);
 
 #if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
 
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
 // State of the definition of a type-parameterized test case.
 class GTEST_API_ TypedTestCasePState {
  public:
@@ -589,6 +581,8 @@ class GTEST_API_ TypedTestCasePState {
   RegisteredTestsMap registered_tests_;
 };
 
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
 // Skips to the first non-space char after the first comma in 'str';
 // returns NULL if no comma is found in 'str'.
 inline const char* SkipComma(const char* str) {
@@ -612,6 +606,37 @@ inline std::string GetPrefixUntilComma(const char* str) {
 void SplitString(const ::std::string& str, char delimiter,
                  ::std::vector< ::std::string>* dest);
 
+// The default argument to the template below for the case when the user does
+// not provide a name generator.
+struct DefaultNameGenerator {
+  template <typename T>
+  static std::string GetName(int i) {
+    return StreamableToString(i);
+  }
+};
+
+template <typename Provided = DefaultNameGenerator>
+struct NameGeneratorSelector {
+  typedef Provided type;
+};
+
+template <typename NameGenerator>
+void GenerateNamesRecursively(Types0, std::vector<std::string>*, int) {}
+
+template <typename NameGenerator, typename Types>
+void GenerateNamesRecursively(Types, std::vector<std::string>* result, int i) {
+  result->push_back(NameGenerator::template GetName<typename Types::Head>(i));
+  GenerateNamesRecursively<NameGenerator>(typename Types::Tail(), result,
+                                          i + 1);
+}
+
+template <typename NameGenerator, typename Types>
+std::vector<std::string> GenerateNames() {
+  std::vector<std::string> result;
+  GenerateNamesRecursively<NameGenerator>(Types(), &result, 0);
+  return result;
+}
+
 // TypeParameterizedTest<Fixture, TestSel, Types>::Register()
 // registers a list of type-parameterized tests with Google Test.  The
 // return value is insignificant - we just need to return something
@@ -626,10 +651,10 @@ class TypeParameterizedTest {
   // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase,
   // Types).  Valid values for 'index' are [0, N - 1] where N is the
   // length of Types.
-  static bool Register(const char* prefix,
-                       CodeLocation code_location,
-                       const char* case_name, const char* test_names,
-                       int index) {
+  static bool Register(const char* prefix, const CodeLocation& code_location,
+                       const char* case_name, const char* test_names, int index,
+                       const std::vector<std::string>& type_names =
+                           GenerateNames<DefaultNameGenerator, Types>()) {
     typedef typename Types::Head Type;
     typedef Fixture<Type> FixtureClass;
     typedef typename GTEST_BIND_(TestSel, Type) TestClass;
@@ -637,20 +662,23 @@ class TypeParameterizedTest {
     // First, registers the first type-parameterized test in the type
     // list.
     MakeAndRegisterTestInfo(
-        (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/"
-         + StreamableToString(index)).c_str(),
+        (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name +
+         "/" + type_names[index])
+            .c_str(),
         StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(),
         GetTypeName<Type>().c_str(),
         NULL,  // No value parameter.
-        code_location,
-        GetTypeId<FixtureClass>(),
-        TestClass::SetUpTestCase,
-        TestClass::TearDownTestCase,
-        new TestFactoryImpl<TestClass>);
+        code_location, GetTypeId<FixtureClass>(), TestClass::SetUpTestCase,
+        TestClass::TearDownTestCase, new TestFactoryImpl<TestClass>);
 
     // Next, recurses (at compile time) with the tail of the type list.
-    return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>
-        ::Register(prefix, code_location, case_name, test_names, index + 1);
+    return TypeParameterizedTest<Fixture, TestSel,
+                                 typename Types::Tail>::Register(prefix,
+                                                                 code_location,
+                                                                 case_name,
+                                                                 test_names,
+                                                                 index + 1,
+                                                                 type_names);
   }
 };
 
@@ -658,9 +686,11 @@ class TypeParameterizedTest {
 template <GTEST_TEMPLATE_ Fixture, class TestSel>
 class TypeParameterizedTest<Fixture, TestSel, Types0> {
  public:
-  static bool Register(const char* /*prefix*/, CodeLocation,
+  static bool Register(const char* /*prefix*/, const CodeLocation&,
                        const char* /*case_name*/, const char* /*test_names*/,
-                       int /*index*/) {
+                       int /*index*/,
+                       const std::vector<std::string>& =
+                           std::vector<std::string>() /*type_names*/) {
     return true;
   }
 };
@@ -673,8 +703,10 @@ template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types>
 class TypeParameterizedTestCase {
  public:
   static bool Register(const char* prefix, CodeLocation code_location,
-                       const TypedTestCasePState* state,
-                       const char* case_name, const char* test_names) {
+                       const TypedTestCasePState* state, const char* case_name,
+                       const char* test_names,
+                       const std::vector<std::string>& type_names =
+                           GenerateNames<DefaultNameGenerator, Types>()) {
     std::string test_name = StripTrailingSpaces(
         GetPrefixUntilComma(test_names));
     if (!state->TestExists(test_name)) {
@@ -691,12 +723,14 @@ class TypeParameterizedTestCase {
 
     // First, register the first test in 'Test' for each type in 'Types'.
     TypeParameterizedTest<Fixture, Head, Types>::Register(
-        prefix, test_location, case_name, test_names, 0);
+        prefix, test_location, case_name, test_names, 0, type_names);
 
     // Next, recurses (at compile time) with the tail of the test list.
-    return TypeParameterizedTestCase<Fixture, typename Tests::Tail, Types>
-        ::Register(prefix, code_location, state,
-                   case_name, SkipComma(test_names));
+    return TypeParameterizedTestCase<Fixture, typename Tests::Tail,
+                                     Types>::Register(prefix, code_location,
+                                                      state, case_name,
+                                                      SkipComma(test_names),
+                                                      type_names);
   }
 };
 
@@ -704,9 +738,11 @@ class TypeParameterizedTestCase {
 template <GTEST_TEMPLATE_ Fixture, typename Types>
 class TypeParameterizedTestCase<Fixture, Templates0, Types> {
  public:
-  static bool Register(const char* /*prefix*/, CodeLocation,
+  static bool Register(const char* /*prefix*/, const CodeLocation&,
                        const TypedTestCasePState* /*state*/,
-                       const char* /*case_name*/, const char* /*test_names*/) {
+                       const char* /*case_name*/, const char* /*test_names*/,
+                       const std::vector<std::string>& =
+                           std::vector<std::string>() /*type_names*/) {
     return true;
   }
 };
@@ -823,31 +859,6 @@ struct RemoveConst<T[N]> {
 #define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \
     GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T))
 
-// Adds reference to a type if it is not a reference type,
-// otherwise leaves it unchanged.  This is the same as
-// tr1::add_reference, which is not widely available yet.
-template <typename T>
-struct AddReference { typedef T& type; };  // NOLINT
-template <typename T>
-struct AddReference<T&> { typedef T& type; };  // NOLINT
-
-// A handy wrapper around AddReference that works when the argument T
-// depends on template parameters.
-#define GTEST_ADD_REFERENCE_(T) \
-    typename ::testing::internal::AddReference<T>::type
-
-// Adds a reference to const on top of T as necessary.  For example,
-// it transforms
-//
-//   char         ==> const char&
-//   const char   ==> const char&
-//   char&        ==> const char&
-//   const char&  ==> const char&
-//
-// The argument T must depend on some template parameters.
-#define GTEST_REFERENCE_TO_CONST_(T) \
-    GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T))
-
 // ImplicitlyConvertible<From, To>::value is a compile-time bool
 // constant that's true iff type From can be implicitly converted to
 // type To.
@@ -917,8 +928,11 @@ struct IsAProtocolMessage
 // a container class by checking the type of IsContainerTest<C>(0).
 // The value of the expression is insignificant.
 //
-// Note that we look for both C::iterator and C::const_iterator.  The
-// reason is that C++ injects the name of a class as a member of the
+// In C++11 mode we check the existence of a const_iterator and that an
+// iterator is properly implemented for the container.
+//
+// For pre-C++11 that we look for both C::iterator and C::const_iterator.
+// The reason is that C++ injects the name of a class as a member of the
 // class itself (e.g. you can refer to class iterator as either
 // 'iterator' or 'iterator::iterator').  If we look for C::iterator
 // only, for example, we would mistakenly think that a class named
@@ -928,17 +942,96 @@ struct IsAProtocolMessage
 // IsContainerTest(typename C::const_iterator*) and
 // IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++.
 typedef int IsContainer;
+#if GTEST_LANG_CXX11
+template <class C,
+          class Iterator = decltype(::std::declval<const C&>().begin()),
+          class = decltype(::std::declval<const C&>().end()),
+          class = decltype(++::std::declval<Iterator&>()),
+          class = decltype(*::std::declval<Iterator>()),
+          class = typename C::const_iterator>
+IsContainer IsContainerTest(int /* dummy */) {
+  return 0;
+}
+#else
 template <class C>
 IsContainer IsContainerTest(int /* dummy */,
                             typename C::iterator* /* it */ = NULL,
                             typename C::const_iterator* /* const_it */ = NULL) {
   return 0;
 }
+#endif  // GTEST_LANG_CXX11
 
 typedef char IsNotContainer;
 template <class C>
 IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; }
 
+// Trait to detect whether a type T is a hash table.
+// The heuristic used is that the type contains an inner type `hasher` and does
+// not contain an inner type `reverse_iterator`.
+// If the container is iterable in reverse, then order might actually matter.
+template <typename T>
+struct IsHashTable {
+ private:
+  template <typename U>
+  static char test(typename U::hasher*, typename U::reverse_iterator*);
+  template <typename U>
+  static int test(typename U::hasher*, ...);
+  template <typename U>
+  static char test(...);
+
+ public:
+  static const bool value = sizeof(test<T>(0, 0)) == sizeof(int);
+};
+
+template <typename T>
+const bool IsHashTable<T>::value;
+
+template<typename T>
+struct VoidT {
+    typedef void value_type;
+};
+
+template <typename T, typename = void>
+struct HasValueType : false_type {};
+template <typename T>
+struct HasValueType<T, VoidT<typename T::value_type> > : true_type {
+};
+
+template <typename C,
+          bool = sizeof(IsContainerTest<C>(0)) == sizeof(IsContainer),
+          bool = HasValueType<C>::value>
+struct IsRecursiveContainerImpl;
+
+template <typename C, bool HV>
+struct IsRecursiveContainerImpl<C, false, HV> : public false_type {};
+
+// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to
+// obey the same inconsistencies as the IsContainerTest, namely check if
+// something is a container is relying on only const_iterator in C++11 and
+// is relying on both const_iterator and iterator otherwise
+template <typename C>
+struct IsRecursiveContainerImpl<C, true, false> : public false_type {};
+
+template <typename C>
+struct IsRecursiveContainerImpl<C, true, true> {
+  #if GTEST_LANG_CXX11
+  typedef typename IteratorTraits<typename C::const_iterator>::value_type
+      value_type;
+#else
+  typedef typename IteratorTraits<typename C::iterator>::value_type value_type;
+#endif
+  typedef is_same<value_type, C> type;
+};
+
+// IsRecursiveContainer<Type> is a unary compile-time predicate that
+// evaluates whether C is a recursive container type. A recursive container
+// type is a container type whose value_type is equal to the container type
+// itself. An example for a recursive container type is
+// boost::filesystem::path, whose iterator has a value_type that is equal to
+// boost::filesystem::path.
+template <typename C>
+struct IsRecursiveContainer : public IsRecursiveContainerImpl<C>::type {};
+
 // EnableIf<condition>::type is void when 'Cond' is true, and
 // undefined when 'Cond' is false.  To use SFINAE to make a function
 // overload only apply when a particular expression is true, add
@@ -1070,7 +1163,7 @@ class NativeArray {
  private:
   enum {
     kCheckTypeIsNotConstOrAReference = StaticAssertTypeEqHelper<
-        Element, GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>::value,
+        Element, GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>::value
   };
 
   // Initializes this object with a copy of the input.
@@ -1115,7 +1208,7 @@ class NativeArray {
 #define GTEST_SUCCESS_(message) \
   GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess)
 
-// Suppresses MSVC warnings 4072 (unreachable code) for the code following
+// Suppress MSVC warning 4702 (unreachable code) for the code following
 // statement if it returns or throws (or doesn't return or throw in some
 // situations).
 #define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \
@@ -1230,8 +1323,8 @@ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
         (parent_id), \
         parent_class::SetUpTestCase, \
         parent_class::TearDownTestCase, \
-        new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>); /*NOLINT(cppcoreguidelines-owning-memory)*/ \
- void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
+        new ::testing::internal::TestFactoryImpl<\
+            GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
+void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
 
 #endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
-
index 6716e34516631e275aa6b2da56559aaf5b9c9ef1..082b87289ae17c8aa7b96d8e8c92b96e018c768b 100644 (file)
@@ -27,8 +27,6 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Authors: Dan Egnor (egnor@google.com)
-//
 // A "smart" pointer type with reference tracking.  Every pointer to a
 // particular object is kept on a circular linked list.  When the last pointer
 // to an object is destroyed or reassigned, the object is deleted.
 //       raw pointer (e.g. via get()) concurrently, and
 //     - it's safe to write to two linked_ptrs that point to the same
 //       shared object concurrently.
-// TODO(wan@google.com): rename this to safe_linked_ptr to avoid
+// FIXME: rename this to safe_linked_ptr to avoid
 // confusion with normal linked_ptr.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
 
@@ -150,11 +150,7 @@ class linked_ptr {
   // Take over ownership of a raw pointer.  This should happen as soon as
   // possible after the object is created.
   explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
-  ~linked_ptr() { 
-#ifndef __clang_analyzer__
-    depart(); 
-#endif
-    }
+  ~linked_ptr() { depart(); }
 
   // Copy an existing linked_ptr<>, adding ourselves to the list of references.
   template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
index 4d1d81d20ffca32ba8f9577ff5bd06bfe2de7463..4fac8c02703d591eb2f22ef14841b42556c50408 100644 (file)
@@ -30,8 +30,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: vladl@google.com (Vlad Losev)
+
 
 // Type and function utilities for implementing parameterized tests.
 // This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
 // by the maximum arity of the implementation of tuple which is
 // currently set at 10.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
 
-// scripts/fuse_gtest.py depends on gtest's own header being #included
-// *unconditionally*.  Therefore these #includes cannot be moved
-// inside #if GTEST_HAS_PARAM_TEST.
 #include "gtest/internal/gtest-param-util.h"
 #include "gtest/internal/gtest-port.h"
 
-#if GTEST_HAS_PARAM_TEST
-
 namespace testing {
 
 // Forward declarations of ValuesIn(), which is implemented in
@@ -84,6 +80,8 @@ class ValueArray1 {
     return ValuesIn(array);
   }
 
+  ValueArray1(const ValueArray1& other) : v1_(other.v1_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray1& other);
@@ -102,6 +100,8 @@ class ValueArray2 {
     return ValuesIn(array);
   }
 
+  ValueArray2(const ValueArray2& other) : v1_(other.v1_), v2_(other.v2_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray2& other);
@@ -122,6 +122,9 @@ class ValueArray3 {
     return ValuesIn(array);
   }
 
+  ValueArray3(const ValueArray3& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray3& other);
@@ -144,6 +147,9 @@ class ValueArray4 {
     return ValuesIn(array);
   }
 
+  ValueArray4(const ValueArray4& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray4& other);
@@ -167,6 +173,9 @@ class ValueArray5 {
     return ValuesIn(array);
   }
 
+  ValueArray5(const ValueArray5& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray5& other);
@@ -193,6 +202,9 @@ class ValueArray6 {
     return ValuesIn(array);
   }
 
+  ValueArray6(const ValueArray6& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray6& other);
@@ -220,6 +232,10 @@ class ValueArray7 {
     return ValuesIn(array);
   }
 
+  ValueArray7(const ValueArray7& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray7& other);
@@ -249,6 +265,10 @@ class ValueArray8 {
     return ValuesIn(array);
   }
 
+  ValueArray8(const ValueArray8& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray8& other);
@@ -280,6 +300,10 @@ class ValueArray9 {
     return ValuesIn(array);
   }
 
+  ValueArray9(const ValueArray9& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray9& other);
@@ -312,6 +336,10 @@ class ValueArray10 {
     return ValuesIn(array);
   }
 
+  ValueArray10(const ValueArray10& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray10& other);
@@ -346,6 +374,11 @@ class ValueArray11 {
     return ValuesIn(array);
   }
 
+  ValueArray11(const ValueArray11& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray11& other);
@@ -382,6 +415,11 @@ class ValueArray12 {
     return ValuesIn(array);
   }
 
+  ValueArray12(const ValueArray12& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray12& other);
@@ -420,6 +458,11 @@ class ValueArray13 {
     return ValuesIn(array);
   }
 
+  ValueArray13(const ValueArray13& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray13& other);
@@ -459,6 +502,11 @@ class ValueArray14 {
     return ValuesIn(array);
   }
 
+  ValueArray14(const ValueArray14& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray14& other);
@@ -500,6 +548,12 @@ class ValueArray15 {
     return ValuesIn(array);
   }
 
+  ValueArray15(const ValueArray15& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray15& other);
@@ -544,6 +598,12 @@ class ValueArray16 {
     return ValuesIn(array);
   }
 
+  ValueArray16(const ValueArray16& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray16& other);
@@ -589,6 +649,12 @@ class ValueArray17 {
     return ValuesIn(array);
   }
 
+  ValueArray17(const ValueArray17& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray17& other);
@@ -636,6 +702,12 @@ class ValueArray18 {
     return ValuesIn(array);
   }
 
+  ValueArray18(const ValueArray18& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray18& other);
@@ -684,6 +756,13 @@ class ValueArray19 {
     return ValuesIn(array);
   }
 
+  ValueArray19(const ValueArray19& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray19& other);
@@ -734,6 +813,13 @@ class ValueArray20 {
     return ValuesIn(array);
   }
 
+  ValueArray20(const ValueArray20& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray20& other);
@@ -787,6 +873,13 @@ class ValueArray21 {
     return ValuesIn(array);
   }
 
+  ValueArray21(const ValueArray21& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray21& other);
@@ -841,6 +934,13 @@ class ValueArray22 {
     return ValuesIn(array);
   }
 
+  ValueArray22(const ValueArray22& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray22& other);
@@ -897,6 +997,14 @@ class ValueArray23 {
     return ValuesIn(array);
   }
 
+  ValueArray23(const ValueArray23& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray23& other);
@@ -955,6 +1063,14 @@ class ValueArray24 {
     return ValuesIn(array);
   }
 
+  ValueArray24(const ValueArray24& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray24& other);
@@ -1014,6 +1130,14 @@ class ValueArray25 {
     return ValuesIn(array);
   }
 
+  ValueArray25(const ValueArray25& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray25& other);
@@ -1075,6 +1199,14 @@ class ValueArray26 {
     return ValuesIn(array);
   }
 
+  ValueArray26(const ValueArray26& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray26& other);
@@ -1139,6 +1271,15 @@ class ValueArray27 {
     return ValuesIn(array);
   }
 
+  ValueArray27(const ValueArray27& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray27& other);
@@ -1204,6 +1345,15 @@ class ValueArray28 {
     return ValuesIn(array);
   }
 
+  ValueArray28(const ValueArray28& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray28& other);
@@ -1270,6 +1420,15 @@ class ValueArray29 {
     return ValuesIn(array);
   }
 
+  ValueArray29(const ValueArray29& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray29& other);
@@ -1339,6 +1498,15 @@ class ValueArray30 {
     return ValuesIn(array);
   }
 
+  ValueArray30(const ValueArray30& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray30& other);
@@ -1410,6 +1578,16 @@ class ValueArray31 {
     return ValuesIn(array);
   }
 
+  ValueArray31(const ValueArray31& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray31& other);
@@ -1482,6 +1660,16 @@ class ValueArray32 {
     return ValuesIn(array);
   }
 
+  ValueArray32(const ValueArray32& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray32& other);
@@ -1557,6 +1745,16 @@ class ValueArray33 {
     return ValuesIn(array);
   }
 
+  ValueArray33(const ValueArray33& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray33& other);
@@ -1633,6 +1831,16 @@ class ValueArray34 {
     return ValuesIn(array);
   }
 
+  ValueArray34(const ValueArray34& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray34& other);
@@ -1710,6 +1918,17 @@ class ValueArray35 {
     return ValuesIn(array);
   }
 
+  ValueArray35(const ValueArray35& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray35& other);
@@ -1790,6 +2009,17 @@ class ValueArray36 {
     return ValuesIn(array);
   }
 
+  ValueArray36(const ValueArray36& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray36& other);
@@ -1872,6 +2102,17 @@ class ValueArray37 {
     return ValuesIn(array);
   }
 
+  ValueArray37(const ValueArray37& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray37& other);
@@ -1955,6 +2196,17 @@ class ValueArray38 {
     return ValuesIn(array);
   }
 
+  ValueArray38(const ValueArray38& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray38& other);
@@ -2040,6 +2292,18 @@ class ValueArray39 {
     return ValuesIn(array);
   }
 
+  ValueArray39(const ValueArray39& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray39& other);
@@ -2127,6 +2391,18 @@ class ValueArray40 {
     return ValuesIn(array);
   }
 
+  ValueArray40(const ValueArray40& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray40& other);
@@ -2216,6 +2492,18 @@ class ValueArray41 {
     return ValuesIn(array);
   }
 
+  ValueArray41(const ValueArray41& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_), v41_(other.v41_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray41& other);
@@ -2307,6 +2595,18 @@ class ValueArray42 {
     return ValuesIn(array);
   }
 
+  ValueArray42(const ValueArray42& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray42& other);
@@ -2399,6 +2699,19 @@ class ValueArray43 {
     return ValuesIn(array);
   }
 
+  ValueArray43(const ValueArray43& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_),
+      v43_(other.v43_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray43& other);
@@ -2493,6 +2806,19 @@ class ValueArray44 {
     return ValuesIn(array);
   }
 
+  ValueArray44(const ValueArray44& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_),
+      v43_(other.v43_), v44_(other.v44_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray44& other);
@@ -2589,6 +2915,19 @@ class ValueArray45 {
     return ValuesIn(array);
   }
 
+  ValueArray45(const ValueArray45& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_),
+      v43_(other.v43_), v44_(other.v44_), v45_(other.v45_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray45& other);
@@ -2687,6 +3026,19 @@ class ValueArray46 {
     return ValuesIn(array);
   }
 
+  ValueArray46(const ValueArray46& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_),
+      v43_(other.v43_), v44_(other.v44_), v45_(other.v45_), v46_(other.v46_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray46& other);
@@ -2787,6 +3139,20 @@ class ValueArray47 {
     return ValuesIn(array);
   }
 
+  ValueArray47(const ValueArray47& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_),
+      v43_(other.v43_), v44_(other.v44_), v45_(other.v45_), v46_(other.v46_),
+      v47_(other.v47_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray47& other);
@@ -2889,6 +3255,20 @@ class ValueArray48 {
     return ValuesIn(array);
   }
 
+  ValueArray48(const ValueArray48& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_),
+      v43_(other.v43_), v44_(other.v44_), v45_(other.v45_), v46_(other.v46_),
+      v47_(other.v47_), v48_(other.v48_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray48& other);
@@ -2992,6 +3372,20 @@ class ValueArray49 {
     return ValuesIn(array);
   }
 
+  ValueArray49(const ValueArray49& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_),
+      v43_(other.v43_), v44_(other.v44_), v45_(other.v45_), v46_(other.v46_),
+      v47_(other.v47_), v48_(other.v48_), v49_(other.v49_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray49& other);
@@ -3096,6 +3490,20 @@ class ValueArray50 {
     return ValuesIn(array);
   }
 
+  ValueArray50(const ValueArray50& other) : v1_(other.v1_), v2_(other.v2_),
+      v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_),
+      v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_),
+      v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_),
+      v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_),
+      v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_),
+      v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_),
+      v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_),
+      v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_),
+      v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_),
+      v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_),
+      v43_(other.v43_), v44_(other.v44_), v45_(other.v45_), v46_(other.v46_),
+      v47_(other.v47_), v48_(other.v48_), v49_(other.v49_), v50_(other.v50_) {}
+
  private:
   // No implementation - assignment is unsupported.
   void operator=(const ValueArray50& other);
@@ -3208,7 +3616,7 @@ class CartesianProductGenerator2
     virtual ParamIteratorInterface<ParamType>* Clone() const {
       return new Iterator(*this);
     }
-    virtual const ParamType* Current() const { return &current_value_; }
+    virtual const ParamType* Current() const { return current_value_.get(); }
     virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
       // Having the same base generator guarantees that the other
       // iterator is of the same type and we can downcast.
@@ -3240,7 +3648,7 @@ class CartesianProductGenerator2
 
     void ComputeCurrentValue() {
       if (!AtEnd())
-        current_value_ = ParamType(*current1_, *current2_);
+        current_value_.reset(new ParamType(*current1_, *current2_));
     }
     bool AtEnd() const {
       // We must report iterator past the end of the range when either of the
@@ -3262,7 +3670,7 @@ class CartesianProductGenerator2
     const typename ParamGenerator<T2>::iterator begin2_;
     const typename ParamGenerator<T2>::iterator end2_;
     typename ParamGenerator<T2>::iterator current2_;
-    ParamType current_value_;
+    linked_ptr<ParamType> current_value_;
   };  // class CartesianProductGenerator2::Iterator
 
   // No implementation - assignment is unsupported.
@@ -3331,7 +3739,7 @@ class CartesianProductGenerator3
     virtual ParamIteratorInterface<ParamType>* Clone() const {
       return new Iterator(*this);
     }
-    virtual const ParamType* Current() const { return &current_value_; }
+    virtual const ParamType* Current() const { return current_value_.get(); }
     virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
       // Having the same base generator guarantees that the other
       // iterator is of the same type and we can downcast.
@@ -3367,7 +3775,7 @@ class CartesianProductGenerator3
 
     void ComputeCurrentValue() {
       if (!AtEnd())
-        current_value_ = ParamType(*current1_, *current2_, *current3_);
+        current_value_.reset(new ParamType(*current1_, *current2_, *current3_));
     }
     bool AtEnd() const {
       // We must report iterator past the end of the range when either of the
@@ -3393,7 +3801,7 @@ class CartesianProductGenerator3
     const typename ParamGenerator<T3>::iterator begin3_;
     const typename ParamGenerator<T3>::iterator end3_;
     typename ParamGenerator<T3>::iterator current3_;
-    ParamType current_value_;
+    linked_ptr<ParamType> current_value_;
   };  // class CartesianProductGenerator3::Iterator
 
   // No implementation - assignment is unsupported.
@@ -3472,7 +3880,7 @@ class CartesianProductGenerator4
     virtual ParamIteratorInterface<ParamType>* Clone() const {
       return new Iterator(*this);
     }
-    virtual const ParamType* Current() const { return &current_value_; }
+    virtual const ParamType* Current() const { return current_value_.get(); }
     virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
       // Having the same base generator guarantees that the other
       // iterator is of the same type and we can downcast.
@@ -3512,8 +3920,8 @@ class CartesianProductGenerator4
 
     void ComputeCurrentValue() {
       if (!AtEnd())
-        current_value_ = ParamType(*current1_, *current2_, *current3_,
-            *current4_);
+        current_value_.reset(new ParamType(*current1_, *current2_, *current3_,
+            *current4_));
     }
     bool AtEnd() const {
       // We must report iterator past the end of the range when either of the
@@ -3543,7 +3951,7 @@ class CartesianProductGenerator4
     const typename ParamGenerator<T4>::iterator begin4_;
     const typename ParamGenerator<T4>::iterator end4_;
     typename ParamGenerator<T4>::iterator current4_;
-    ParamType current_value_;
+    linked_ptr<ParamType> current_value_;
   };  // class CartesianProductGenerator4::Iterator
 
   // No implementation - assignment is unsupported.
@@ -3630,7 +4038,7 @@ class CartesianProductGenerator5
     virtual ParamIteratorInterface<ParamType>* Clone() const {
       return new Iterator(*this);
     }
-    virtual const ParamType* Current() const { return &current_value_; }
+    virtual const ParamType* Current() const { return current_value_.get(); }
     virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
       // Having the same base generator guarantees that the other
       // iterator is of the same type and we can downcast.
@@ -3674,8 +4082,8 @@ class CartesianProductGenerator5
 
     void ComputeCurrentValue() {
       if (!AtEnd())
-        current_value_ = ParamType(*current1_, *current2_, *current3_,
-            *current4_, *current5_);
+        current_value_.reset(new ParamType(*current1_, *current2_, *current3_,
+            *current4_, *current5_));
     }
     bool AtEnd() const {
       // We must report iterator past the end of the range when either of the
@@ -3709,7 +4117,7 @@ class CartesianProductGenerator5
     const typename ParamGenerator<T5>::iterator begin5_;
     const typename ParamGenerator<T5>::iterator end5_;
     typename ParamGenerator<T5>::iterator current5_;
-    ParamType current_value_;
+    linked_ptr<ParamType> current_value_;
   };  // class CartesianProductGenerator5::Iterator
 
   // No implementation - assignment is unsupported.
@@ -3807,7 +4215,7 @@ class CartesianProductGenerator6
     virtual ParamIteratorInterface<ParamType>* Clone() const {
       return new Iterator(*this);
     }
-    virtual const ParamType* Current() const { return &current_value_; }
+    virtual const ParamType* Current() const { return current_value_.get(); }
     virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
       // Having the same base generator guarantees that the other
       // iterator is of the same type and we can downcast.
@@ -3855,8 +4263,8 @@ class CartesianProductGenerator6
 
     void ComputeCurrentValue() {
       if (!AtEnd())
-        current_value_ = ParamType(*current1_, *current2_, *current3_,
-            *current4_, *current5_, *current6_);
+        current_value_.reset(new ParamType(*current1_, *current2_, *current3_,
+            *current4_, *current5_, *current6_));
     }
     bool AtEnd() const {
       // We must report iterator past the end of the range when either of the
@@ -3894,7 +4302,7 @@ class CartesianProductGenerator6
     const typename ParamGenerator<T6>::iterator begin6_;
     const typename ParamGenerator<T6>::iterator end6_;
     typename ParamGenerator<T6>::iterator current6_;
-    ParamType current_value_;
+    linked_ptr<ParamType> current_value_;
   };  // class CartesianProductGenerator6::Iterator
 
   // No implementation - assignment is unsupported.
@@ -4001,7 +4409,7 @@ class CartesianProductGenerator7
     virtual ParamIteratorInterface<ParamType>* Clone() const {
       return new Iterator(*this);
     }
-    virtual const ParamType* Current() const { return &current_value_; }
+    virtual const ParamType* Current() const { return current_value_.get(); }
     virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
       // Having the same base generator guarantees that the other
       // iterator is of the same type and we can downcast.
@@ -4053,8 +4461,8 @@ class CartesianProductGenerator7
 
     void ComputeCurrentValue() {
       if (!AtEnd())
-        current_value_ = ParamType(*current1_, *current2_, *current3_,
-            *current4_, *current5_, *current6_, *current7_);
+        current_value_.reset(new ParamType(*current1_, *current2_, *current3_,
+            *current4_, *current5_, *current6_, *current7_));
     }
     bool AtEnd() const {
       // We must report iterator past the end of the range when either of the
@@ -4096,7 +4504,7 @@ class CartesianProductGenerator7
     const typename ParamGenerator<T7>::iterator begin7_;
     const typename ParamGenerator<T7>::iterator end7_;
     typename ParamGenerator<T7>::iterator current7_;
-    ParamType current_value_;
+    linked_ptr<ParamType> current_value_;
   };  // class CartesianProductGenerator7::Iterator
 
   // No implementation - assignment is unsupported.
@@ -4214,7 +4622,7 @@ class CartesianProductGenerator8
     virtual ParamIteratorInterface<ParamType>* Clone() const {
       return new Iterator(*this);
     }
-    virtual const ParamType* Current() const { return &current_value_; }
+    virtual const ParamType* Current() const { return current_value_.get(); }
     virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
       // Having the same base generator guarantees that the other
       // iterator is of the same type and we can downcast.
@@ -4270,8 +4678,8 @@ class CartesianProductGenerator8
 
     void ComputeCurrentValue() {
       if (!AtEnd())
-        current_value_ = ParamType(*current1_, *current2_, *current3_,
-            *current4_, *current5_, *current6_, *current7_, *current8_);
+        current_value_.reset(new ParamType(*current1_, *current2_, *current3_,
+            *current4_, *current5_, *current6_, *current7_, *current8_));
     }
     bool AtEnd() const {
       // We must report iterator past the end of the range when either of the
@@ -4317,7 +4725,7 @@ class CartesianProductGenerator8
     const typename ParamGenerator<T8>::iterator begin8_;
     const typename ParamGenerator<T8>::iterator end8_;
     typename ParamGenerator<T8>::iterator current8_;
-    ParamType current_value_;
+    linked_ptr<ParamType> current_value_;
   };  // class CartesianProductGenerator8::Iterator
 
   // No implementation - assignment is unsupported.
@@ -4443,7 +4851,7 @@ class CartesianProductGenerator9
     virtual ParamIteratorInterface<ParamType>* Clone() const {
       return new Iterator(*this);
     }
-    virtual const ParamType* Current() const { return &current_value_; }
+    virtual const ParamType* Current() const { return current_value_.get(); }
     virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
       // Having the same base generator guarantees that the other
       // iterator is of the same type and we can downcast.
@@ -4503,9 +4911,9 @@ class CartesianProductGenerator9
 
     void ComputeCurrentValue() {
       if (!AtEnd())
-        current_value_ = ParamType(*current1_, *current2_, *current3_,
+        current_value_.reset(new ParamType(*current1_, *current2_, *current3_,
             *current4_, *current5_, *current6_, *current7_, *current8_,
-            *current9_);
+            *current9_));
     }
     bool AtEnd() const {
       // We must report iterator past the end of the range when either of the
@@ -4555,7 +4963,7 @@ class CartesianProductGenerator9
     const typename ParamGenerator<T9>::iterator begin9_;
     const typename ParamGenerator<T9>::iterator end9_;
     typename ParamGenerator<T9>::iterator current9_;
-    ParamType current_value_;
+    linked_ptr<ParamType> current_value_;
   };  // class CartesianProductGenerator9::Iterator
 
   // No implementation - assignment is unsupported.
@@ -4690,7 +5098,7 @@ class CartesianProductGenerator10
     virtual ParamIteratorInterface<ParamType>* Clone() const {
       return new Iterator(*this);
     }
-    virtual const ParamType* Current() const { return &current_value_; }
+    virtual const ParamType* Current() const { return current_value_.get(); }
     virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
       // Having the same base generator guarantees that the other
       // iterator is of the same type and we can downcast.
@@ -4754,9 +5162,9 @@ class CartesianProductGenerator10
 
     void ComputeCurrentValue() {
       if (!AtEnd())
-        current_value_ = ParamType(*current1_, *current2_, *current3_,
+        current_value_.reset(new ParamType(*current1_, *current2_, *current3_,
             *current4_, *current5_, *current6_, *current7_, *current8_,
-            *current9_, *current10_);
+            *current9_, *current10_));
     }
     bool AtEnd() const {
       // We must report iterator past the end of the range when either of the
@@ -4810,7 +5218,7 @@ class CartesianProductGenerator10
     const typename ParamGenerator<T10>::iterator begin10_;
     const typename ParamGenerator<T10>::iterator end10_;
     typename ParamGenerator<T10>::iterator current10_;
-    ParamType current_value_;
+    linked_ptr<ParamType> current_value_;
   };  // class CartesianProductGenerator10::Iterator
 
   // No implementation - assignment is unsupported.
@@ -5141,6 +5549,4 @@ CartesianProductHolder10(const Generator1& g1, const Generator2& g2,
 }  // namespace internal
 }  // namespace testing
 
-#endif  //  GTEST_HAS_PARAM_TEST
-
 #endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
index 82cab9b0201b19b7afba39197f7ea7f8a4a0b170..d64f620c4c67434673c0a61e23960b615c34e784 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: vladl@google.com (Vlad Losev)
+
 
 // Type and function utilities for implementing parameterized tests.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
 
 #include <utility>
 #include <vector>
 
-// scripts/fuse_gtest.py depends on gtest's own header being #included
-// *unconditionally*.  Therefore these #includes cannot be moved
-// inside #if GTEST_HAS_PARAM_TEST.
 #include "gtest/internal/gtest-internal.h"
 #include "gtest/internal/gtest-linked_ptr.h"
 #include "gtest/internal/gtest-port.h"
 #include "gtest/gtest-printers.h"
 
-#if GTEST_HAS_PARAM_TEST
-
 namespace testing {
 
 // Input to a parameterized test name generator, describing a test parameter.
@@ -472,7 +468,7 @@ class ParameterizedTestCaseInfoBase {
   virtual ~ParameterizedTestCaseInfoBase() {}
 
   // Base part of test case name for display purposes.
-  virtual const string& GetTestCaseName() const = 0;
+  virtual const std::string& GetTestCaseName() const = 0;
   // Test case id to verify identity.
   virtual TypeId GetTestCaseTypeId() const = 0;
   // UnitTest class invokes this method to register tests in this
@@ -511,7 +507,7 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
       : test_case_name_(name), code_location_(code_location) {}
 
   // Test case base name for display purposes.
-  virtual const string& GetTestCaseName() const { return test_case_name_; }
+  virtual const std::string& GetTestCaseName() const { return test_case_name_; }
   // Test case id to verify identity.
   virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); }
   // TEST_P macro uses AddTestPattern() to record information
@@ -529,11 +525,10 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
   }
   // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information
   // about a generator.
-  int AddTestCaseInstantiation(const string& instantiation_name,
+  int AddTestCaseInstantiation(const std::string& instantiation_name,
                                GeneratorCreationFunc* func,
                                ParamNameGeneratorFunc* name_func,
-                               const char* file,
-                               int line) {
+                               const char* file, int line) {
     instantiations_.push_back(
         InstantiationInfo(instantiation_name, func, name_func, file, line));
     return 0;  // Return value used only to run this method in namespace scope.
@@ -550,13 +545,13 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
       for (typename InstantiationContainer::iterator gen_it =
                instantiations_.begin(); gen_it != instantiations_.end();
                ++gen_it) {
-        const string& instantiation_name = gen_it->name;
+        const std::string& instantiation_name = gen_it->name;
         ParamGenerator<ParamType> generator((*gen_it->generator)());
         ParamNameGeneratorFunc* name_func = gen_it->name_func;
         const char* file = gen_it->file;
         int line = gen_it->line;
 
-        string test_case_name;
+        std::string test_case_name;
         if ( !instantiation_name.empty() )
           test_case_name = instantiation_name + "/";
         test_case_name += test_info->test_case_base_name;
@@ -609,8 +604,8 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
         test_base_name(a_test_base_name),
         test_meta_factory(a_test_meta_factory) {}
 
-    const string test_case_base_name;
-    const string test_base_name;
+    const std::string test_case_base_name;
+    const std::string test_base_name;
     const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
   };
   typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer;
@@ -651,7 +646,7 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
     return true;
   }
 
-  const string test_case_name_;
+  const std::string test_case_name_;
   CodeLocation code_location_;
   TestInfoContainer tests_;
   InstantiationContainer instantiations_;
@@ -726,6 +721,4 @@ class ParameterizedTestCaseRegistry {
 }  // namespace internal
 }  // namespace testing
 
-#endif  //  GTEST_HAS_PARAM_TEST
-
 #endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
index 74ab949057c734fa49188e8d6bc4e74be910dc0d..f83700e06d98c36f6487291c3f931807af353f74 100644 (file)
@@ -27,7 +27,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // This header file defines the GTEST_OS_* macro.
 // It is separate from gtest-port.h so that custom/gtest-port.h can include it.
@@ -54,6 +54,9 @@
 #   define GTEST_OS_WINDOWS_PHONE 1
 #  elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
 #   define GTEST_OS_WINDOWS_RT 1
+#  elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE)
+#   define GTEST_OS_WINDOWS_PHONE 1
+#   define GTEST_OS_WINDOWS_TV_TITLE 1
 #  else
     // WINAPI_FAMILY defined but no known partition matched.
     // Default to desktop.
@@ -69,6 +72,8 @@
 # endif
 #elif defined __FreeBSD__
 # define GTEST_OS_FREEBSD 1
+#elif defined __Fuchsia__
+# define GTEST_OS_FUCHSIA 1
 #elif defined __linux__
 # define GTEST_OS_LINUX 1
 # if defined __ANDROID__
@@ -84,6 +89,8 @@
 # define GTEST_OS_HPUX 1
 #elif defined __native_client__
 # define GTEST_OS_NACL 1
+#elif defined __NetBSD__
+# define GTEST_OS_NETBSD 1
 #elif defined __OpenBSD__
 # define GTEST_OS_OPENBSD 1
 #elif defined __QNX__
index 0094ed5077e69a59afc3f535c9dcfc403b31fbbc..708cb982eab6ad1519972afdcb5625c20507b625 100644 (file)
@@ -27,8 +27,6 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Authors: wan@google.com (Zhanyong Wan)
-//
 // Low-level types and utilities for porting Google Test to various
 // platforms.  All macros ending with _ and symbols defined in an
 // internal namespace are subject to change without notice.  Code
@@ -40,6 +38,8 @@
 // files are expected to #include this.  Therefore, it cannot #include
 // any other Google Test header.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
 
 //   GTEST_HAS_EXCEPTIONS     - Define it to 1/0 to indicate that exceptions
 //                              are enabled.
 //   GTEST_HAS_GLOBAL_STRING  - Define it to 1/0 to indicate that ::string
-//                              is/isn't available (some systems define
-//                              ::string, which is different to std::string).
-//   GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string
-//                              is/isn't available (some systems define
-//                              ::wstring, which is different to std::wstring).
+//                              is/isn't available
+//   GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::wstring
+//                              is/isn't available
 //   GTEST_HAS_POSIX_RE       - Define it to 1/0 to indicate that POSIX regular
 //                              expressions are/aren't available.
 //   GTEST_HAS_PTHREAD        - Define it to 1/0 to indicate that <pthread.h>
 //   GTEST_CREATE_SHARED_LIBRARY
 //                            - Define to 1 when compiling Google Test itself
 //                              as a shared library.
+//   GTEST_DEFAULT_DEATH_TEST_STYLE
+//                            - The default value of --gtest_death_test_style.
+//                              The legacy default has been "fast" in the open
+//                              source version since 2008. The recommended value
+//                              is "threadsafe", and can be set in
+//                              custom/gtest-port.h.
 
 // Platform-indicating macros
 // --------------------------
 //   GTEST_OS_AIX      - IBM AIX
 //   GTEST_OS_CYGWIN   - Cygwin
 //   GTEST_OS_FREEBSD  - FreeBSD
+//   GTEST_OS_FUCHSIA  - Fuchsia
 //   GTEST_OS_HPUX     - HP-UX
 //   GTEST_OS_LINUX    - Linux
 //     GTEST_OS_LINUX_ANDROID - Google Android
 //   GTEST_OS_MAC      - Mac OS X
 //     GTEST_OS_IOS    - iOS
 //   GTEST_OS_NACL     - Google Native Client (NaCl)
+//   GTEST_OS_NETBSD   - NetBSD
 //   GTEST_OS_OPENBSD  - OpenBSD
 //   GTEST_OS_QNX      - QNX
 //   GTEST_OS_SOLARIS  - Sun Solaris
 //   GTEST_HAS_COMBINE      - the Combine() function (for value-parameterized
 //                            tests)
 //   GTEST_HAS_DEATH_TEST   - death tests
-//   GTEST_HAS_PARAM_TEST   - value-parameterized tests
 //   GTEST_HAS_TYPED_TEST   - typed tests
 //   GTEST_HAS_TYPED_TEST_P - type-parameterized tests
 //   GTEST_IS_THREADSAFE    - Google Test is thread-safe.
+//   GOOGLETEST_CM0007 DO NOT DELETE
 //   GTEST_USES_POSIX_RE    - enhanced POSIX regex is used. Do not confuse with
 //                            GTEST_HAS_POSIX_RE (see above) which users can
 //                            define themselves.
 //   GTEST_USES_SIMPLE_RE   - our own simple regex is used;
-//                            the above two are mutually exclusive.
+//                            the above RE\b(s) are mutually exclusive.
 //   GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ().
 
 // Misc public macros
 //
 // C++11 feature wrappers:
 //
+//   testing::internal::forward - portability wrapper for std::forward.
 //   testing::internal::move  - portability wrapper for std::move.
 //
 // Synchronization:
 //
 // Regular expressions:
 //   RE             - a simple regular expression class using the POSIX
-//                    Extended Regular Expression syntax on UNIX-like
-//                    platforms, or a reduced regular exception syntax on
-//                    other platforms, including Windows.
-//
+//                    Extended Regular Expression syntax on UNIX-like platforms
+//                    GOOGLETEST_CM0008 DO NOT DELETE
+//                    or a reduced regular exception syntax on other
+//                    platforms, including Windows.
 // Logging:
 //   GTEST_LOG_()   - logs messages at the specified severity level.
 //   LogToStderr()  - directs all log messages to stderr.
 # include <TargetConditionals.h>
 #endif
 
+// Brings in the definition of HAS_GLOBAL_STRING.  This must be done
+// BEFORE we test HAS_GLOBAL_STRING.
+#include <string>  // NOLINT
 #include <algorithm>  // NOLINT
 #include <iostream>  // NOLINT
 #include <sstream>  // NOLINT
-#include <string>  // NOLINT
 #include <utility>
 #include <vector>  // NOLINT
 
 //   GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385)
 //   /* code that triggers warnings C4800 and C4385 */
 //   GTEST_DISABLE_MSC_WARNINGS_POP_()
-#if _MSC_VER >= 1500
+#if _MSC_VER >= 1400
 # define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \
     __pragma(warning(push))                        \
     __pragma(warning(disable: warnings))
 # define GTEST_DISABLE_MSC_WARNINGS_POP_()
 #endif
 
+// Clang on Windows does not understand MSVC's pragma warning.
+// We need clang-specific way to disable function deprecation warning.
+#ifdef __clang__
+# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_()                         \
+    _Pragma("clang diagnostic push")                                  \
+    _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \
+    _Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"")
+#define GTEST_DISABLE_MSC_DEPRECATED_POP_() \
+    _Pragma("clang diagnostic pop")
+#else
+# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \
+    GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996)
+# define GTEST_DISABLE_MSC_DEPRECATED_POP_() \
+    GTEST_DISABLE_MSC_WARNINGS_POP_()
+#endif
+
 #ifndef GTEST_LANG_CXX11
 // gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when
 // -std={c,gnu}++{0x,11} is passed.  The C++11 standard specifies a
 // value for __cplusplus, and recent versions of clang, gcc, and
 // probably other compilers set that too in C++11 mode.
-# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L
+# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L || _MSC_VER >= 1900
 // Compiling in at least C++11 mode.
 #  define GTEST_LANG_CXX11 1
 # else
 #if GTEST_STDLIB_CXX11
 # define GTEST_HAS_STD_BEGIN_AND_END_ 1
 # define GTEST_HAS_STD_FORWARD_LIST_ 1
-# define GTEST_HAS_STD_FUNCTION_ 1
+# if !defined(_MSC_VER) || (_MSC_FULL_VER >= 190023824)
+// works only with VS2015U2 and better
+#   define GTEST_HAS_STD_FUNCTION_ 1
+# endif
 # define GTEST_HAS_STD_INITIALIZER_LIST_ 1
 # define GTEST_HAS_STD_MOVE_ 1
-# define GTEST_HAS_STD_SHARED_PTR_ 1
-# define GTEST_HAS_STD_TYPE_TRAITS_ 1
 # define GTEST_HAS_STD_UNIQUE_PTR_ 1
+# define GTEST_HAS_STD_SHARED_PTR_ 1
+# define GTEST_HAS_UNORDERED_MAP_ 1
+# define GTEST_HAS_UNORDERED_SET_ 1
 #endif
 
 // C++11 specifies that <tuple> provides std::tuple.
 #if GTEST_LANG_CXX11
 # define GTEST_HAS_STD_TUPLE_ 1
 # if defined(__clang__)
-// Inspired by http://clang.llvm.org/docs/LanguageExtensions.html#__has_include
+// Inspired by
+// https://clang.llvm.org/docs/LanguageExtensions.html#include-file-checking-macros
 #  if defined(__has_include) && !__has_include(<tuple>)
 #   undef GTEST_HAS_STD_TUPLE_
 #  endif
 # elif defined(__GLIBCXX__)
 // Inspired by boost/config/stdlib/libstdcpp3.hpp,
 // http://gcc.gnu.org/gcc-4.2/changes.html and
-// http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch01.html#manual.intro.status.standard.200x
+// https://web.archive.org/web/20140227044429/gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch01.html#manual.intro.status.standard.200x
 #  if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)
 #   undef GTEST_HAS_STD_TUPLE_
 #  endif
 #  include <io.h>
 # endif
 // In order to avoid having to include <windows.h>, use forward declaration
-// assuming CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION.
+#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR)
+// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two
+// separate (equivalent) structs, instead of using typedef
+typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION;
+#else
+// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION.
 // This assumption is verified by
 // WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION.
-struct _RTL_CRITICAL_SECTION;
+typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
+#endif
 #else
 // This assumes that non-Windows OSes provide unistd.h. For OSes where this
 // is not the case, we need to include headers that provide the functions
@@ -453,8 +489,11 @@ struct _RTL_CRITICAL_SECTION;
 #ifndef GTEST_HAS_EXCEPTIONS
 // The user didn't tell us whether exceptions are enabled, so we need
 // to figure it out.
-# if defined(_MSC_VER) || defined(__BORLANDC__)
-// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS
+# if defined(_MSC_VER) && defined(_CPPUNWIND)
+// MSVC defines _CPPUNWIND to 1 iff exceptions are enabled.
+#  define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__BORLANDC__)
+// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS
 // macro to enable exceptions, so we'll do the same.
 // Assumes that exceptions are enabled by default.
 #  ifndef _HAS_EXCEPTIONS
@@ -498,21 +537,17 @@ struct _RTL_CRITICAL_SECTION;
 # define GTEST_HAS_STD_STRING 1
 #elif !GTEST_HAS_STD_STRING
 // The user told us that ::std::string isn't available.
-# error "Google Test cannot be used where ::std::string isn't available."
+# error "::std::string isn't available."
 #endif  // !defined(GTEST_HAS_STD_STRING)
 
 #ifndef GTEST_HAS_GLOBAL_STRING
-// The user didn't tell us whether ::string is available, so we need
-// to figure it out.
-
 # define GTEST_HAS_GLOBAL_STRING 0
-
 #endif  // GTEST_HAS_GLOBAL_STRING
 
 #ifndef GTEST_HAS_STD_WSTRING
 // The user didn't tell us whether ::std::wstring is available, so we need
 // to figure it out.
-// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring
+// FIXME: uses autoconf to detect whether ::std::wstring
 //   is available.
 
 // Cygwin 1.7 and below doesn't support ::std::wstring.
@@ -600,8 +635,9 @@ struct _RTL_CRITICAL_SECTION;
 //
 // To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0
 // to your compiler flags.
-# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \
-    || GTEST_OS_QNX || GTEST_OS_FREEBSD || GTEST_OS_NACL)
+#define GTEST_HAS_PTHREAD                                             \
+  (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX || \
+   GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA)
 #endif  // GTEST_HAS_PTHREAD
 
 #if GTEST_HAS_PTHREAD
@@ -616,7 +652,7 @@ struct _RTL_CRITICAL_SECTION;
 // Determines if hash_map/hash_set are available.
 // Only used for testing against those containers.
 #if !defined(GTEST_HAS_HASH_MAP_)
-# if _MSC_VER
+# if defined(_MSC_VER) && (_MSC_VER < 1900)
 #  define GTEST_HAS_HASH_MAP_ 1  // Indicates that hash_map is available.
 #  define GTEST_HAS_HASH_SET_ 1  // Indicates that hash_set is available.
 # endif  // _MSC_VER
@@ -629,6 +665,14 @@ struct _RTL_CRITICAL_SECTION;
 # if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR)
 // STLport, provided with the Android NDK, has neither <tr1/tuple> or <tuple>.
 #  define GTEST_HAS_TR1_TUPLE 0
+# elif defined(_MSC_VER) && (_MSC_VER >= 1910)
+// Prevent `warning C4996: 'std::tr1': warning STL4002:
+// The non-Standard std::tr1 namespace and TR1-only machinery
+// are deprecated and will be REMOVED.`
+#  define GTEST_HAS_TR1_TUPLE 0
+# elif GTEST_LANG_CXX11 && defined(_LIBCPP_VERSION)
+// libc++ doesn't support TR1.
+#  define GTEST_HAS_TR1_TUPLE 0
 # else
 // The user didn't tell us not to do it, so we assume it's OK.
 #  define GTEST_HAS_TR1_TUPLE 1
@@ -638,6 +682,10 @@ struct _RTL_CRITICAL_SECTION;
 // Determines whether Google Test's own tr1 tuple implementation
 // should be used.
 #ifndef GTEST_USE_OWN_TR1_TUPLE
+// We use our own tuple implementation on Symbian.
+# if GTEST_OS_SYMBIAN
+#  define GTEST_USE_OWN_TR1_TUPLE 1
+# else
 // The user didn't tell us, so we need to figure it out.
 
 // We use our own TR1 tuple if we aren't sure the user has an
@@ -651,7 +699,8 @@ struct _RTL_CRITICAL_SECTION;
 // support TR1 tuple.  libc++ only provides std::tuple, in C++11 mode,
 // and it can be used with some compilers that define __GNUC__.
 # if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \
-      && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600
+      && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) \
+      || (_MSC_VER >= 1600 && _MSC_VER < 1900)
 #  define GTEST_ENV_HAS_TR1_TUPLE_ 1
 # endif
 
@@ -667,12 +716,11 @@ struct _RTL_CRITICAL_SECTION;
 # else
 #  define GTEST_USE_OWN_TR1_TUPLE 1
 # endif
-
+# endif  // GTEST_OS_SYMBIAN
 #endif  // GTEST_USE_OWN_TR1_TUPLE
 
-// To avoid conditional compilation everywhere, we make it
-// gtest-port.h's responsibility to #include the header implementing
-// tuple.
+// To avoid conditional compilation we make it gtest-port.h's responsibility
+// to #include the header implementing tuple.
 #if GTEST_HAS_STD_TUPLE_
 # include <tuple>  // IWYU pragma: export
 # define GTEST_TUPLE_NAMESPACE_ ::std
@@ -687,22 +735,6 @@ struct _RTL_CRITICAL_SECTION;
 
 # if GTEST_USE_OWN_TR1_TUPLE
 #  include "gtest/internal/gtest-tuple.h"  // IWYU pragma: export  // NOLINT
-# elif GTEST_ENV_HAS_STD_TUPLE_
-#  include <tuple>
-// C++11 puts its tuple into the ::std namespace rather than
-// ::std::tr1.  gtest expects tuple to live in ::std::tr1, so put it there.
-// This causes undefined behavior, but supported compilers react in
-// the way we intend.
-namespace std {
-namespace tr1 {
-using ::std::get;
-using ::std::make_tuple;
-using ::std::tuple;
-using ::std::tuple_element;
-using ::std::tuple_size;
-}
-}
-
 # elif GTEST_OS_SYMBIAN
 
 // On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to
@@ -727,20 +759,22 @@ using ::std::tuple_size;
 // Until version 4.3.2, gcc has a bug that causes <tr1/functional>,
 // which is #included by <tr1/tuple>, to not compile when RTTI is
 // disabled.  _TR1_FUNCTIONAL is the header guard for
-// <tr1/functional>.  Hence the following #define is a hack to prevent
+// <tr1/functional>.  Hence the following #define is used to prevent
 // <tr1/functional> from being included.
 #   define _TR1_FUNCTIONAL 1
 #   include <tr1/tuple>
 #   undef _TR1_FUNCTIONAL  // Allows the user to #include
-                        // <tr1/functional> if he chooses to.
+                        // <tr1/functional> if they choose to.
 #  else
 #   include <tr1/tuple>  // NOLINT
 #  endif  // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
 
-# else
-// If the compiler is not GCC 4.0+, we assume the user is using a
-// spec-conforming TR1 implementation.
+// VS 2010 now has tr1 support.
+# elif _MSC_VER >= 1600
 #  include <tuple>  // IWYU pragma: export  // NOLINT
+
+# else  // GTEST_USE_OWN_TR1_TUPLE
+#  include <tr1/tuple>  // IWYU pragma: export  // NOLINT
 # endif  // GTEST_USE_OWN_TR1_TUPLE
 
 #endif  // GTEST_HAS_TR1_TUPLE
@@ -754,8 +788,12 @@ using ::std::tuple_size;
 
 # if GTEST_OS_LINUX && !defined(__ia64__)
 #  if GTEST_OS_LINUX_ANDROID
-// On Android, clone() is only available on ARM starting with Gingerbread.
-#    if defined(__arm__) && __ANDROID_API__ >= 9
+// On Android, clone() became available at different API levels for each 32-bit
+// architecture.
+#    if defined(__LP64__) || \
+        (defined(__arm__) && __ANDROID_API__ >= 9) || \
+        (defined(__mips__) && __ANDROID_API__ >= 12) || \
+        (defined(__i386__) && __ANDROID_API__ >= 17)
 #     define GTEST_HAS_CLONE 1
 #    else
 #     define GTEST_HAS_CLONE 0
@@ -786,19 +824,15 @@ using ::std::tuple_size;
 // Google Test does not support death tests for VC 7.1 and earlier as
 // abort() in a VC 7.1 application compiled as GUI in debug config
 // pops up a dialog window that cannot be suppressed programmatically.
-#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
-     (GTEST_OS_MAC && !GTEST_OS_IOS) || \
-     (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \
+#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS ||   \
+     (GTEST_OS_MAC && !GTEST_OS_IOS) ||                         \
+     (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) ||          \
      GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \
-     GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD)
+     GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD || \
+     GTEST_OS_NETBSD || GTEST_OS_FUCHSIA)
 # define GTEST_HAS_DEATH_TEST 1
 #endif
 
-// We don't support MSVC 7.1 with exceptions disabled now.  Therefore
-// all the compilers we care about are adequate for supporting
-// value-parameterized tests.
-#define GTEST_HAS_PARAM_TEST 1
-
 // Determines whether to support type-driven tests.
 
 // Typed tests need <typeinfo> and variadic macros, which GCC, VC++ 8.0,
@@ -813,7 +847,7 @@ using ::std::tuple_size;
 // value-parameterized tests are enabled.  The implementation doesn't
 // work on Sun Studio since it doesn't understand templated conversion
 // operators.
-#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC)
+#if (GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_) && !defined(__SUNPRO_CC)
 # define GTEST_HAS_COMBINE 1
 #endif
 
@@ -864,15 +898,39 @@ using ::std::tuple_size;
 # define GTEST_ATTRIBUTE_UNUSED_
 #endif
 
+#if GTEST_LANG_CXX11
+# define GTEST_CXX11_EQUALS_DELETE_ = delete
+#else  // GTEST_LANG_CXX11
+# define GTEST_CXX11_EQUALS_DELETE_
+#endif  // GTEST_LANG_CXX11
+
+// Use this annotation before a function that takes a printf format string.
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(COMPILER_ICC)
+# if defined(__MINGW_PRINTF_FORMAT)
+// MinGW has two different printf implementations. Ensure the format macro
+// matches the selected implementation. See
+// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/.
+#  define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \
+       __attribute__((__format__(__MINGW_PRINTF_FORMAT, string_index, \
+                                 first_to_check)))
+# else
+#  define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \
+       __attribute__((__format__(__printf__, string_index, first_to_check)))
+# endif
+#else
+# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check)
+#endif
+
+
 // A macro to disallow operator=
 // This should be used in the private: declarations for a class.
-#define GTEST_DISALLOW_ASSIGN_(type)\
-  void operator=(type const &)
+#define GTEST_DISALLOW_ASSIGN_(type) \
+  void operator=(type const &) GTEST_CXX11_EQUALS_DELETE_
 
 // A macro to disallow copy constructor and operator=
 // This should be used in the private: declarations for a class.
-#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\
-  type(type const &);\
+#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type) \
+  type(type const &) GTEST_CXX11_EQUALS_DELETE_; \
   GTEST_DISALLOW_ASSIGN_(type)
 
 // Tell the compiler to warn about unused return values for functions declared
@@ -920,6 +978,11 @@ using ::std::tuple_size;
 
 #endif  // GTEST_HAS_SEH
 
+// GTEST_API_ qualifies all symbols that must be exported. The definitions below
+// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in
+// gtest/internal/custom/gtest-port.h
+#ifndef GTEST_API_
+
 #ifdef _MSC_VER
 # if GTEST_LINKED_AS_SHARED_LIBRARY
 #  define GTEST_API_ __declspec(dllimport)
@@ -928,11 +991,17 @@ using ::std::tuple_size;
 # endif
 #elif __GNUC__ >= 4 || defined(__clang__)
 # define GTEST_API_ __attribute__((visibility ("default")))
-#endif // _MSC_VER
+#endif  // _MSC_VER
+
+#endif  // GTEST_API_
 
 #ifndef GTEST_API_
 # define GTEST_API_
-#endif
+#endif  // GTEST_API_
+
+#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE
+# define GTEST_DEFAULT_DEATH_TEST_STYLE  "fast"
+#endif  // GTEST_DEFAULT_DEATH_TEST_STYLE
 
 #ifdef __GNUC__
 // Ask the compiler to never inline a given function.
@@ -942,10 +1011,12 @@ using ::std::tuple_size;
 #endif
 
 // _LIBCPP_VERSION is defined by the libc++ library from the LLVM project.
-#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
-# define GTEST_HAS_CXXABI_H_ 1
-#else
-# define GTEST_HAS_CXXABI_H_ 0
+#if !defined(GTEST_HAS_CXXABI_H_)
+# if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER))
+#  define GTEST_HAS_CXXABI_H_ 1
+# else
+#  define GTEST_HAS_CXXABI_H_ 0
+# endif
 #endif
 
 // A function level attribute to disable checking for use of uninitialized
@@ -1088,6 +1159,16 @@ struct StaticAssertTypeEqHelper<T, T> {
   enum { value = true };
 };
 
+// Same as std::is_same<>.
+template <typename T, typename U>
+struct IsSame {
+  enum { value = false };
+};
+template <typename T>
+struct IsSame<T, T> {
+  enum { value = true };
+};
+
 // Evaluates to the number of elements in 'array'.
 #define GTEST_ARRAY_SIZE_(array) (sizeof(array) / sizeof(array[0]))
 
@@ -1121,8 +1202,13 @@ class scoped_ptr {
 
   T& operator*() const { return *ptr_; }
   T* operator->() const { return ptr_; }
-  T* get() const { return ptr_; }
-
+  T* get() const {
+#ifndef __clang_analyzer__
+      return ptr_;
+#else
+      return nullptr;
+#endif
+  }
   T* release() {
     T* const ptr = ptr_;
     ptr_ = NULL;
@@ -1132,7 +1218,9 @@ class scoped_ptr {
   void reset(T* p = NULL) {
     if (p != ptr_) {
       if (IsTrue(sizeof(T) > 0)) {  // Makes sure T is a complete type.
+#ifndef __clang_analyzer__
         delete ptr_;
+#endif
       }
       ptr_ = p;
     }
@@ -1151,6 +1239,10 @@ class scoped_ptr {
 
 // Defines RE.
 
+#if GTEST_USES_PCRE
+// if used, PCRE is injected by custom/gtest-port.h
+#elif GTEST_USES_POSIX_RE || GTEST_USES_SIMPLE_RE
+
 // A simple C++ wrapper for <regex.h>.  It uses the POSIX Extended
 // Regular Expression syntax.
 class GTEST_API_ RE {
@@ -1162,11 +1254,11 @@ class GTEST_API_ RE {
   // Constructs an RE from a string.
   RE(const ::std::string& regex) { Init(regex.c_str()); }  // NOLINT
 
-#if GTEST_HAS_GLOBAL_STRING
+# if GTEST_HAS_GLOBAL_STRING
 
   RE(const ::string& regex) { Init(regex.c_str()); }  // NOLINT
 
-#endif  // GTEST_HAS_GLOBAL_STRING
+# endif  // GTEST_HAS_GLOBAL_STRING
 
   RE(const char* regex) { Init(regex); }  // NOLINT
   ~RE();
@@ -1179,7 +1271,7 @@ class GTEST_API_ RE {
   // PartialMatch(str, re) returns true iff regular expression re
   // matches a substring of str (including str itself).
   //
-  // TODO(wan@google.com): make FullMatch() and PartialMatch() work
+  // FIXME: make FullMatch() and PartialMatch() work
   // when str contains NUL characters.
   static bool FullMatch(const ::std::string& str, const RE& re) {
     return FullMatch(str.c_str(), re);
@@ -1188,7 +1280,7 @@ class GTEST_API_ RE {
     return PartialMatch(str.c_str(), re);
   }
 
-#if GTEST_HAS_GLOBAL_STRING
+# if GTEST_HAS_GLOBAL_STRING
 
   static bool FullMatch(const ::string& str, const RE& re) {
     return FullMatch(str.c_str(), re);
@@ -1197,7 +1289,7 @@ class GTEST_API_ RE {
     return PartialMatch(str.c_str(), re);
   }
 
-#endif  // GTEST_HAS_GLOBAL_STRING
+# endif  // GTEST_HAS_GLOBAL_STRING
 
   static bool FullMatch(const char* str, const RE& re);
   static bool PartialMatch(const char* str, const RE& re);
@@ -1206,25 +1298,27 @@ class GTEST_API_ RE {
   void Init(const char* regex);
 
   // We use a const char* instead of an std::string, as Google Test used to be
-  // used where std::string is not available.  TODO(wan@google.com): change to
+  // used where std::string is not available.  FIXME: change to
   // std::string.
   const char* pattern_;
   bool is_valid_;
 
-#if GTEST_USES_POSIX_RE
+# if GTEST_USES_POSIX_RE
 
   regex_t full_regex_;     // For FullMatch().
   regex_t partial_regex_;  // For PartialMatch().
 
-#else  // GTEST_USES_SIMPLE_RE
+# else  // GTEST_USES_SIMPLE_RE
 
   const char* full_pattern_;  // For FullMatch();
 
-#endif
+# endif
 
   GTEST_DISALLOW_ASSIGN_(RE);
 };
 
+#endif  // GTEST_USES_PCRE
+
 // Formats a source file path and a line number as they would appear
 // in an error message from the compiler used to compile this code.
 GTEST_API_ ::std::string FormatFileLocation(const char* file, int line);
@@ -1310,13 +1404,59 @@ inline void FlushInfoLog() { fflush(NULL); }
     GTEST_LOG_(FATAL) << #posix_call << "failed with error " \
                       << gtest_error
 
+// Adds reference to a type if it is not a reference type,
+// otherwise leaves it unchanged.  This is the same as
+// tr1::add_reference, which is not widely available yet.
+template <typename T>
+struct AddReference { typedef T& type; };  // NOLINT
+template <typename T>
+struct AddReference<T&> { typedef T& type; };  // NOLINT
+
+// A handy wrapper around AddReference that works when the argument T
+// depends on template parameters.
+#define GTEST_ADD_REFERENCE_(T) \
+    typename ::testing::internal::AddReference<T>::type
+
+// Transforms "T" into "const T&" according to standard reference collapsing
+// rules (this is only needed as a backport for C++98 compilers that do not
+// support reference collapsing). Specifically, it transforms:
+//
+//   char         ==> const char&
+//   const char   ==> const char&
+//   char&        ==> char&
+//   const char&  ==> const char&
+//
+// Note that the non-const reference will not have "const" added. This is
+// standard, and necessary so that "T" can always bind to "const T&".
+template <typename T>
+struct ConstRef { typedef const T& type; };
+template <typename T>
+struct ConstRef<T&> { typedef T& type; };
+
+// The argument T must depend on some template parameters.
+#define GTEST_REFERENCE_TO_CONST_(T) \
+  typename ::testing::internal::ConstRef<T>::type
+
 #if GTEST_HAS_STD_MOVE_
+using std::forward;
 using std::move;
+
+template <typename T>
+struct RvalueRef {
+  typedef T&& type;
+};
 #else  // GTEST_HAS_STD_MOVE_
 template <typename T>
 const T& move(const T& t) {
   return t;
 }
+template <typename T>
+GTEST_ADD_REFERENCE_(T) forward(GTEST_ADD_REFERENCE_(T) t) { return t; }
+
+template <typename T>
+struct RvalueRef {
+  typedef const T& type;
+};
 #endif  // GTEST_HAS_STD_MOVE_
 
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
@@ -1417,10 +1557,6 @@ GTEST_API_ void CaptureStderr();
 GTEST_API_ std::string GetCapturedStderr();
 
 #endif  // GTEST_HAS_STREAM_REDIRECTION
-
-// Returns a path to temporary directory.
-GTEST_API_ std::string TempDir();
-
 // Returns the size (in bytes) of a file.
 GTEST_API_ size_t GetFileSize(FILE* file);
 
@@ -1428,14 +1564,18 @@ GTEST_API_ size_t GetFileSize(FILE* file);
 GTEST_API_ std::string ReadEntireFile(FILE* file);
 
 // All command line arguments.
-GTEST_API_ const ::std::vector<testing::internal::string>& GetArgvs();
+GTEST_API_ std::vector<std::string> GetArgvs();
 
 #if GTEST_HAS_DEATH_TEST
 
-const ::std::vector<testing::internal::string>& GetInjectableArgvs();
-void SetInjectableArgvs(const ::std::vector<testing::internal::string>*
-                             new_argvs);
-
+std::vector<std::string> GetInjectableArgvs();
+// Deprecated: pass the args vector by value instead.
+void SetInjectableArgvs(const std::vector<std::string>* new_argvs);
+void SetInjectableArgvs(const std::vector<std::string>& new_argvs);
+#if GTEST_HAS_GLOBAL_STRING
+void SetInjectableArgvs(const std::vector< ::string>& new_argvs);
+#endif  // GTEST_HAS_GLOBAL_STRING
+void ClearInjectableArgvs();
 
 #endif  // GTEST_HAS_DEATH_TEST
 
@@ -1685,7 +1825,7 @@ class GTEST_API_ Mutex {
   // Initializes owner_thread_id_ and critical_section_ in static mutexes.
   void ThreadSafeLazyInit();
 
-  // Per http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx,
+  // Per https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503,
   // we assume that 0 is an invalid value for thread IDs.
   unsigned int owner_thread_id_;
 
@@ -1693,7 +1833,7 @@ class GTEST_API_ Mutex {
   // by the linker.
   MutexType type_;
   long critical_section_init_phase_;  // NOLINT
-  _RTL_CRITICAL_SECTION* critical_section_;
+  GTEST_CRITICAL_SECTION* critical_section_;
 
   GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
 };
@@ -1969,8 +2109,13 @@ class MutexBase {
      extern ::testing::internal::MutexBase mutex
 
 // Defines and statically (i.e. at link time) initializes a static mutex.
-#  define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
-     ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false, pthread_t() }
+// The initialization list here does not explicitly initialize each field,
+// instead relying on default initialization for the unspecified fields. In
+// particular, the owner_ field (a pthread_t) is not explicitly initialized.
+// This allows initialization to work whether pthread_t is a scalar or struct.
+// The flag -Wmissing-field-initializers must not be specified for this to work.
+#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
+  ::testing::internal::MutexBase mutex = {PTHREAD_MUTEX_INITIALIZER, false, 0}
 
 // The Mutex class can only be used for mutexes created at runtime. It
 // shares its API with MutexBase otherwise.
@@ -2027,7 +2172,7 @@ extern "C" inline void DeleteThreadLocalValue(void* value_holder) {
 
 // Implements thread-local storage on pthreads-based systems.
 template <typename T>
-class ThreadLocal {
+class GTEST_API_ ThreadLocal {
  public:
   ThreadLocal()
       : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {}
@@ -2159,7 +2304,7 @@ class GTestMutexLock {
 typedef GTestMutexLock MutexLock;
 
 template <typename T>
-class ThreadLocal {
+class GTEST_API_ ThreadLocal {
  public:
   ThreadLocal() : value_() {}
   explicit ThreadLocal(const T& value) : value_(value) {}
@@ -2178,12 +2323,13 @@ class ThreadLocal {
 GTEST_API_ size_t GetThreadCount();
 
 // Passing non-POD classes through ellipsis (...) crashes the ARM
-// compiler and generates a warning in Sun Studio The Nokia Symbian
+// compiler and generates a warning in Sun Studio before 12u4. The Nokia Symbian
 // and the IBM XL C/C++ compiler try to instantiate a copy constructor
 // for objects passed through ellipsis (...), failing for uncopyable
 // objects.  We define this to ensure that only POD is passed through
 // ellipsis on these systems.
-#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC)
+#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || \
+     (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x5130)
 // We lose support for NULL detection where the compiler doesn't like
 // passing non-POD classes through ellipsis (...).
 # define GTEST_ELLIPSIS_NEEDS_POD_ 1
@@ -2209,6 +2355,13 @@ template <bool bool_value> const bool bool_constant<bool_value>::value;
 typedef bool_constant<false> false_type;
 typedef bool_constant<true> true_type;
 
+template <typename T, typename U>
+struct is_same : public false_type {};
+
+template <typename T>
+struct is_same<T, T> : public true_type {};
+
+
 template <typename T>
 struct is_pointer : public false_type {};
 
@@ -2220,6 +2373,7 @@ struct IteratorTraits {
   typedef typename Iterator::value_type value_type;
 };
 
+
 template <typename T>
 struct IteratorTraits<T*> {
   typedef T value_type;
@@ -2351,7 +2505,7 @@ inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
 
 // Functions deprecated by MSVC 8.0.
 
-GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996 /* deprecated function */)
+GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
 
 inline const char* StrNCpy(char* dest, const char* src, size_t n) {
   return strncpy(dest, src, n);
@@ -2385,7 +2539,7 @@ inline int Close(int fd) { return close(fd); }
 inline const char* StrError(int errnum) { return strerror(errnum); }
 #endif
 inline const char* GetEnv(const char* name) {
-#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE | GTEST_OS_WINDOWS_RT
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
   // We are on Windows CE, which has no environment variables.
   static_cast<void>(name);  // To prevent 'unused argument' warning.
   return NULL;
@@ -2399,7 +2553,7 @@ inline const char* GetEnv(const char* name) {
 #endif
 }
 
-GTEST_DISABLE_MSC_WARNINGS_POP_()
+GTEST_DISABLE_MSC_DEPRECATED_POP_()
 
 #if GTEST_OS_WINDOWS_MOBILE
 // Windows CE has no C library. The abort() function is used in
@@ -2515,15 +2669,15 @@ typedef TypeWithSize<8>::Int TimeInMillis;  // Represents time in milliseconds.
 # define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name)
 # define GTEST_DECLARE_int32_(name) \
     GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name)
-#define GTEST_DECLARE_string_(name) \
+# define GTEST_DECLARE_string_(name) \
     GTEST_API_ extern ::std::string GTEST_FLAG(name)
 
 // Macros for defining flags.
-#define GTEST_DEFINE_bool_(name, default_val, doc) \
+# define GTEST_DEFINE_bool_(name, default_val, doc) \
     GTEST_API_ bool GTEST_FLAG(name) = (default_val)
-#define GTEST_DEFINE_int32_(name, default_val, doc) \
+# define GTEST_DEFINE_int32_(name, default_val, doc) \
     GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val)
-#define GTEST_DEFINE_string_(name, default_val, doc) \
+# define GTEST_DEFINE_string_(name, default_val, doc) \
     GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val)
 
 #endif  // !defined(GTEST_DECLARE_bool_)
@@ -2537,7 +2691,7 @@ typedef TypeWithSize<8>::Int TimeInMillis;  // Represents time in milliseconds.
 // Parses 'str' for a 32-bit signed integer.  If successful, writes the result
 // to *value and returns true; otherwise leaves *value unchanged and returns
 // false.
-// TODO(chandlerc): Find a better way to refactor flag and environment parsing
+// FIXME: Find a better way to refactor flag and environment parsing
 // out of both gtest-port.cc and gtest.cc to avoid exporting this utility
 // function.
 bool ParseInt32(const Message& src_text, const char* str, Int32* value);
@@ -2546,7 +2700,8 @@ bool ParseInt32(const Message& src_text, const char* str, Int32* value);
 // corresponding to the given Google Test flag.
 bool BoolFromGTestEnv(const char* flag, bool default_val);
 GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val);
-std::string StringFromGTestEnv(const char* flag, const char* default_val);
+std::string OutputFlagAlsoCheckEnvVar();
+const char* StringFromGTestEnv(const char* flag, const char* default_val);
 
 }  // namespace internal
 }  // namespace testing
index 97f1a7fdd2c0cc44c3a148f0f1ecdcbdfe9bac6c..4c9b6262c3c103726aa3cab4c19995baef958b98 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // This header file declares the String class and functions used internally by
 // Google Test.  They are subject to change without notice. They should not used
 // by code external to Google Test.
 //
-// This header file is #included by <gtest/internal/gtest-internal.h>.
+// This header file is #included by gtest-internal.h.
 // It should not be #included by other files.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
 
index e9b405340a855f60258b134026be7b13612ebe1e..78a3a6a01fac75517eab831312427e15d5e4ebb7 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: wan@google.com (Zhanyong Wan)
+
 
 // Implements a subset of TR1 tuple needed by Google Test and Google Mock.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
 
@@ -42,7 +43,7 @@
 
 // The compiler used in Symbian has a bug that prevents us from declaring the
 // tuple template as a friend (it complains that tuple is redefined).  This
-// hack bypasses the bug by declaring the members that should otherwise be
+// bypasses the bug by declaring the members that should otherwise be
 // private as public.
 // Sun Studio versions < 12 also have the above bug.
 #if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590)
index e46f7cfcb483a9551c20ada23b77f9880405f4fe..28e411245361c08f28707e9248e6d03320e68618 100644 (file)
@@ -30,8 +30,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: wan@google.com (Zhanyong Wan)
+
 
 // Type utilities needed for implementing typed and type-parameterized
 // tests.  This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
@@ -41,6 +40,8 @@
 // Please contact googletestframework@googlegroups.com if you need
 // more.
 
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
 #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
 
 namespace testing {
 namespace internal {
 
+// Canonicalizes a given name with respect to the Standard C++ Library.
+// This handles removing the inline namespace within `std` that is
+// used by various standard libraries (e.g., `std::__1`).  Names outside
+// of namespace std are returned unmodified.
+inline std::string CanonicalizeForStdLibVersioning(std::string s) {
+  static const char prefix[] = "std::__";
+  if (s.compare(0, strlen(prefix), prefix) == 0) {
+    std::string::size_type end = s.find("::", strlen(prefix));
+    if (end != s.npos) {
+      // Erase everything between the initial `std` and the second `::`.
+      s.erase(strlen("std"), end - strlen("std"));
+    }
+  }
+  return s;
+}
+
 // GetTypeName<T>() returns a human-readable name of type T.
 // NB: This function is also used in Google Mock, so don't move it inside of
 // the typed-test-only section below.
@@ -75,7 +92,7 @@ std::string GetTypeName() {
   char* const readable_name = __cxa_demangle(name, 0, 0, &status);
   const std::string name_str(status == 0 ? readable_name : name);
   free(readable_name);
-  return name_str;
+  return CanonicalizeForStdLibVersioning(name_str);
 #  else
   return name;
 #  endif  // GTEST_HAS_CXXABI_H_ || __HP_aCC
index 0a9cee52233328b1bafdc4bc402699fa14038f73..b217a18006b0c8bdaf4a9f9b6d06de4fd8ad9eed 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 //
-// Author: mheule@google.com (Markus Heule)
-//
-// Google C++ Testing Framework (Google Test)
+// Google C++ Testing and Mocking Framework (Google Test)
 //
 // Sometimes it's desirable to build Google Test by compiling a single file.
 // This file serves this purpose.
index a01a3698308782821e4f90f41285128b75c8e3ba..09083551612e3c3b9b3bd7d1ee606c18ffb5f73a 100644 (file)
@@ -26,8 +26,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev)
+
 //
 // This file implements death tests.
 
 #  include <spawn.h>
 # endif  // GTEST_OS_QNX
 
+# if GTEST_OS_FUCHSIA
+#  include <lib/fdio/io.h>
+#  include <lib/fdio/spawn.h>
+#  include <zircon/processargs.h>
+#  include <zircon/syscalls.h>
+#  include <zircon/syscalls/port.h>
+# endif  // GTEST_OS_FUCHSIA
+
 #endif  // GTEST_HAS_DEATH_TEST
 
 #include "gtest/gtest-message.h"
 #include "gtest/internal/gtest-string.h"
-
-// Indicates that this translation unit is part of Google Test's
-// implementation.  It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error.  This trick exists to
-// prevent the accidental inclusion of gtest-internal-inl.h in the
-// user's code.
-#define GTEST_IMPLEMENTATION_ 1
 #include "src/gtest-internal-inl.h"
-#undef GTEST_IMPLEMENTATION_
 
 namespace testing {
 
 // Constants.
 
 // The default death test style.
-static const char kDefaultDeathTestStyle[] = "fast";
+//
+// This is defined in internal/gtest-port.h as "fast", but can be overridden by
+// a definition in internal/custom/gtest-port.h. The recommended value, which is
+// used internally at Google, is "threadsafe".
+static const char kDefaultDeathTestStyle[] = GTEST_DEFAULT_DEATH_TEST_STYLE;
 
 GTEST_DEFINE_string_(
     death_test_style,
@@ -121,7 +124,7 @@ namespace internal {
 
 // Valid only for fast death tests. Indicates the code is running in the
 // child process of a fast style death test.
-# if !GTEST_OS_WINDOWS
+# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
 static bool g_in_fast_death_test_child = false;
 # endif
 
@@ -131,10 +134,10 @@ static bool g_in_fast_death_test_child = false;
 // tests.  IMPORTANT: This is an internal utility.  Using it may break the
 // implementation of death tests.  User code MUST NOT use it.
 bool InDeathTestChild() {
-# if GTEST_OS_WINDOWS
+# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
 
-  // On Windows, death tests are thread-safe regardless of the value of the
-  // death_test_style flag.
+  // On Windows and Fuchsia, death tests are thread-safe regardless of the value
+  // of the death_test_style flag.
   return !GTEST_FLAG(internal_run_death_test).empty();
 
 # else
@@ -154,7 +157,7 @@ ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) {
 
 // ExitedWithCode function-call operator.
 bool ExitedWithCode::operator()(int exit_status) const {
-# if GTEST_OS_WINDOWS
+# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
 
   return exit_status == exit_code_;
 
@@ -162,10 +165,10 @@ bool ExitedWithCode::operator()(int exit_status) const {
 
   return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_;
 
-# endif  // GTEST_OS_WINDOWS
+# endif  // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
 }
 
-# if !GTEST_OS_WINDOWS
+# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
 // KilledBySignal constructor.
 KilledBySignal::KilledBySignal(int signum) : signum_(signum) {
 }
@@ -182,7 +185,7 @@ bool KilledBySignal::operator()(int exit_status) const {
 #  endif  // defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_)
   return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_;
 }
-# endif  // !GTEST_OS_WINDOWS
+# endif  // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
 
 namespace internal {
 
@@ -193,7 +196,7 @@ namespace internal {
 static std::string ExitSummary(int exit_code) {
   Message m;
 
-# if GTEST_OS_WINDOWS
+# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
 
   m << "Exited with exit status " << exit_code;
 
@@ -209,7 +212,7 @@ static std::string ExitSummary(int exit_code) {
     m << " (core dumped)";
   }
 #  endif
-# endif  // GTEST_OS_WINDOWS
+# endif  // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
 
   return m.GetString();
 }
@@ -220,7 +223,7 @@ bool ExitedUnsuccessfully(int exit_status) {
   return !ExitedWithCode(0)(exit_status);
 }
 
-# if !GTEST_OS_WINDOWS
+# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
 // Generates a textual failure message when a death test finds more than
 // one thread running, or cannot determine the number of threads, prior
 // to executing the given statement.  It is the responsibility of the
@@ -229,13 +232,19 @@ static std::string DeathTestThreadWarning(size_t thread_count) {
   Message msg;
   msg << "Death tests use fork(), which is unsafe particularly"
       << " in a threaded context. For this test, " << GTEST_NAME_ << " ";
-  if (thread_count == 0)
+  if (thread_count == 0) {
     msg << "couldn't detect the number of threads.";
-  else
+  } else {
     msg << "detected " << thread_count << " threads.";
+  }
+  msg << " See "
+         "https://github.com/google/googletest/blob/master/googletest/docs/"
+         "advanced.md#death-tests-and-threads"
+      << " for more explanation and suggested solutions, especially if"
+      << " this is the last message you see before your test times out.";
   return msg.GetString();
 }
-# endif  // !GTEST_OS_WINDOWS
+# endif  // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
 
 // Flag characters for reporting a death test that did not die.
 static const char kDeathTestLived = 'L';
@@ -243,6 +252,13 @@ static const char kDeathTestReturned = 'R';
 static const char kDeathTestThrew = 'T';
 static const char kDeathTestInternalError = 'I';
 
+#if GTEST_OS_FUCHSIA
+
+// File descriptor used for the pipe in the child process.
+static const int kFuchsiaReadPipeFd = 3;
+
+#endif
+
 // An enumeration describing all of the possible ways that a death test can
 // conclude.  DIED means that the process died while executing the test
 // code; LIVED means that process lived beyond the end of the test code;
@@ -250,7 +266,7 @@ static const char kDeathTestInternalError = 'I';
 // statement, which is not allowed; THREW means that the test statement
 // returned control by throwing an exception.  IN_PROGRESS means the test
 // has not yet concluded.
-// TODO(vladl@google.com): Unify names and possibly values for
+// FIXME: Unify names and possibly values for
 // AbortReason, DeathTestOutcome, and flag characters above.
 enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW };
 
@@ -259,7 +275,7 @@ enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW };
 // message is propagated back to the parent process.  Otherwise, the
 // message is simply printed to stderr.  In either case, the program
 // then exits with status 1.
-void DeathTestAbort(const std::string& message) {
+static void DeathTestAbort(const std::string& message) {
   // On a POSIX system, this function may be called from a threadsafe-style
   // death test child process, which operates on a very small stack.  Use
   // the heap for any additional non-minuscule memory requirements.
@@ -563,7 +579,12 @@ bool DeathTestImpl::Passed(bool status_ok) {
       break;
     case DIED:
       if (status_ok) {
+# if GTEST_USES_PCRE
+        // PCRE regexes support embedded NULs.
+        const bool matched = RE::PartialMatch(error_message, *regex());
+# else
         const bool matched = RE::PartialMatch(error_message.c_str(), *regex());
+# endif  // GTEST_USES_PCRE
         if (matched) {
           success = true;
         } else {
@@ -779,7 +800,200 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() {
   set_spawned(true);
   return OVERSEE_TEST;
 }
-# else  // We are not on Windows.
+
+# elif GTEST_OS_FUCHSIA
+
+class FuchsiaDeathTest : public DeathTestImpl {
+ public:
+  FuchsiaDeathTest(const char* a_statement,
+                   const RE* a_regex,
+                   const char* file,
+                   int line)
+      : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {}
+  virtual ~FuchsiaDeathTest() {
+    zx_status_t status = zx_handle_close(child_process_);
+    GTEST_DEATH_TEST_CHECK_(status == ZX_OK);
+    status = zx_handle_close(port_);
+    GTEST_DEATH_TEST_CHECK_(status == ZX_OK);
+  }
+
+  // All of these virtual functions are inherited from DeathTest.
+  virtual int Wait();
+  virtual TestRole AssumeRole();
+
+ private:
+  // The name of the file in which the death test is located.
+  const char* const file_;
+  // The line number on which the death test is located.
+  const int line_;
+
+  zx_handle_t child_process_ = ZX_HANDLE_INVALID;
+  zx_handle_t port_ = ZX_HANDLE_INVALID;
+};
+
+// Utility class for accumulating command-line arguments.
+class Arguments {
+ public:
+  Arguments() {
+    args_.push_back(NULL);
+  }
+
+  ~Arguments() {
+    for (std::vector<char*>::iterator i = args_.begin(); i != args_.end();
+         ++i) {
+      free(*i);
+    }
+  }
+  void AddArgument(const char* argument) {
+    args_.insert(args_.end() - 1, posix::StrDup(argument));
+  }
+
+  template <typename Str>
+  void AddArguments(const ::std::vector<Str>& arguments) {
+    for (typename ::std::vector<Str>::const_iterator i = arguments.begin();
+         i != arguments.end();
+         ++i) {
+      args_.insert(args_.end() - 1, posix::StrDup(i->c_str()));
+    }
+  }
+  char* const* Argv() {
+    return &args_[0];
+  }
+
+  int size() {
+    return args_.size() - 1;
+  }
+
+ private:
+  std::vector<char*> args_;
+};
+
+// Waits for the child in a death test to exit, returning its exit
+// status, or 0 if no child process exists.  As a side effect, sets the
+// outcome data member.
+int FuchsiaDeathTest::Wait() {
+  if (!spawned())
+    return 0;
+
+  // Register to wait for the child process to terminate.
+  zx_status_t status_zx;
+  status_zx = zx_object_wait_async(child_process_,
+                                   port_,
+                                   0 /* key */,
+                                   ZX_PROCESS_TERMINATED,
+                                   ZX_WAIT_ASYNC_ONCE);
+  GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+
+  // Wait for it to terminate, or an exception to be received.
+  zx_port_packet_t packet;
+  status_zx = zx_port_wait(port_, ZX_TIME_INFINITE, &packet);
+  GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+
+  if (ZX_PKT_IS_EXCEPTION(packet.type)) {
+    // Process encountered an exception. Kill it directly rather than letting
+    // other handlers process the event.
+    status_zx = zx_task_kill(child_process_);
+    GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+
+    // Now wait for |child_process_| to terminate.
+    zx_signals_t signals = 0;
+    status_zx = zx_object_wait_one(
+        child_process_, ZX_PROCESS_TERMINATED, ZX_TIME_INFINITE, &signals);
+    GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+    GTEST_DEATH_TEST_CHECK_(signals & ZX_PROCESS_TERMINATED);
+  } else {
+    // Process terminated.
+    GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_ONE(packet.type));
+    GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_PROCESS_TERMINATED);
+  }
+
+  ReadAndInterpretStatusByte();
+
+  zx_info_process_t buffer;
+  status_zx = zx_object_get_info(
+      child_process_,
+      ZX_INFO_PROCESS,
+      &buffer,
+      sizeof(buffer),
+      nullptr,
+      nullptr);
+  GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+
+  GTEST_DEATH_TEST_CHECK_(buffer.exited);
+  set_status(buffer.return_code);
+  return status();
+}
+
+// The AssumeRole process for a Fuchsia death test.  It creates a child
+// process with the same executable as the current process to run the
+// death test.  The child process is given the --gtest_filter and
+// --gtest_internal_run_death_test flags such that it knows to run the
+// current death test only.
+DeathTest::TestRole FuchsiaDeathTest::AssumeRole() {
+  const UnitTestImpl* const impl = GetUnitTestImpl();
+  const InternalRunDeathTestFlag* const flag =
+      impl->internal_run_death_test_flag();
+  const TestInfo* const info = impl->current_test_info();
+  const int death_test_index = info->result()->death_test_count();
+
+  if (flag != NULL) {
+    // ParseInternalRunDeathTestFlag() has performed all the necessary
+    // processing.
+    set_write_fd(kFuchsiaReadPipeFd);
+    return EXECUTE_TEST;
+  }
+
+  CaptureStderr();
+  // Flush the log buffers since the log streams are shared with the child.
+  FlushInfoLog();
+
+  // Build the child process command line.
+  const std::string filter_flag =
+      std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "="
+      + info->test_case_name() + "." + info->name();
+  const std::string internal_flag =
+      std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "="
+      + file_ + "|"
+      + StreamableToString(line_) + "|"
+      + StreamableToString(death_test_index);
+  Arguments args;
+  args.AddArguments(GetInjectableArgvs());
+  args.AddArgument(filter_flag.c_str());
+  args.AddArgument(internal_flag.c_str());
+
+  // Build the pipe for communication with the child.
+  zx_status_t status;
+  zx_handle_t child_pipe_handle;
+  uint32_t type;
+  status = fdio_pipe_half(&child_pipe_handle, &type);
+  GTEST_DEATH_TEST_CHECK_(status >= 0);
+  set_read_fd(status);
+
+  // Set the pipe handle for the child.
+  fdio_spawn_action_t add_handle_action = {};
+  add_handle_action.action = FDIO_SPAWN_ACTION_ADD_HANDLE;
+  add_handle_action.h.id = PA_HND(type, kFuchsiaReadPipeFd);
+  add_handle_action.h.handle = child_pipe_handle;
+
+  // Spawn the child process.
+  status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
+                          args.Argv()[0], args.Argv(), nullptr, 1,
+                          &add_handle_action, &child_process_, nullptr);
+  GTEST_DEATH_TEST_CHECK_(status == ZX_OK);
+
+  // Create an exception port and attach it to the |child_process_|, to allow
+  // us to suppress the system default exception handler from firing.
+  status = zx_port_create(0, &port_);
+  GTEST_DEATH_TEST_CHECK_(status == ZX_OK);
+  status = zx_task_bind_exception_port(
+      child_process_, port_, 0 /* key */, 0 /*options */);
+  GTEST_DEATH_TEST_CHECK_(status == ZX_OK);
+
+  set_spawned(true);
+  return OVERSEE_TEST;
+}
+
+#else  // We are neither on Windows, nor on Fuchsia.
 
 // ForkingDeathTest provides implementations for most of the abstract
 // methods of the DeathTest interface.  Only the AssumeRole method is
@@ -883,11 +1097,10 @@ class ExecDeathTest : public ForkingDeathTest {
       ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { }
   virtual TestRole AssumeRole();
  private:
-  static ::std::vector<testing::internal::string>
-  GetArgvsForDeathTestChildProcess() {
-    ::std::vector<testing::internal::string> args = GetInjectableArgvs();
+  static ::std::vector<std::string> GetArgvsForDeathTestChildProcess() {
+    ::std::vector<std::string> args = GetInjectableArgvs();
 #  if defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_)
-    ::std::vector<testing::internal::string> extra_args =
+    ::std::vector<std::string> extra_args =
         GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_();
     args.insert(args.end(), extra_args.begin(), extra_args.end());
 #  endif  // defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_)
@@ -986,6 +1199,7 @@ static int ExecDeathTestChildMain(void* child_arg) {
 }
 #  endif  // !GTEST_OS_QNX
 
+#  if GTEST_HAS_CLONE
 // Two utility routines that together determine the direction the stack
 // grows.
 // This could be accomplished more elegantly by a single recursive
@@ -995,20 +1209,22 @@ static int ExecDeathTestChildMain(void* child_arg) {
 // GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining
 // StackLowerThanAddress into StackGrowsDown, which then doesn't give
 // correct answer.
-void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_;
-void StackLowerThanAddress(const void* ptr, bool* result) {
+static void StackLowerThanAddress(const void* ptr,
+                                  bool* result) GTEST_NO_INLINE_;
+static void StackLowerThanAddress(const void* ptr, bool* result) {
   int dummy;
   *result = (&dummy < ptr);
 }
 
 // Make sure AddressSanitizer does not tamper with the stack here.
 GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
-bool StackGrowsDown() {
+static bool StackGrowsDown() {
   int dummy;
   bool result;
   StackLowerThanAddress(&dummy, &result);
   return result;
 }
+#  endif  // GTEST_HAS_CLONE
 
 // Spawns a child process with the same executable as the current process in
 // a thread-safe manner and instructs it to run the death test.  The
@@ -1200,6 +1416,13 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
     *test = new WindowsDeathTest(statement, regex, file, line);
   }
 
+# elif GTEST_OS_FUCHSIA
+
+  if (GTEST_FLAG(death_test_style) == "threadsafe" ||
+      GTEST_FLAG(death_test_style) == "fast") {
+    *test = new FuchsiaDeathTest(statement, regex, file, line);
+  }
+
 # else
 
   if (GTEST_FLAG(death_test_style) == "threadsafe") {
@@ -1224,7 +1447,7 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
 // Recreates the pipe and event handles from the provided parameters,
 // signals the event, and returns a file descriptor wrapped around the pipe
 // handle. This function is called in the child process only.
-int GetStatusFileDescriptor(unsigned int parent_process_id,
+static int GetStatusFileDescriptor(unsigned int parent_process_id,
                             size_t write_handle_as_size_t,
                             size_t event_handle_as_size_t) {
   AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE,
@@ -1235,7 +1458,7 @@ int GetStatusFileDescriptor(unsigned int parent_process_id,
                    StreamableToString(parent_process_id));
   }
 
-  // TODO(vladl@google.com): Replace the following check with a
+  // FIXME: Replace the following check with a
   // compile-time assertion when available.
   GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t));
 
@@ -1243,7 +1466,7 @@ int GetStatusFileDescriptor(unsigned int parent_process_id,
       reinterpret_cast<HANDLE>(write_handle_as_size_t);
   HANDLE dup_write_handle;
 
-  // The newly initialized handle is accessible only in in the parent
+  // The newly initialized handle is accessible only in the parent
   // process. To obtain one accessible within the child, we need to use
   // DuplicateHandle.
   if (!::DuplicateHandle(parent_process_handle.Get(), write_handle,
@@ -1320,6 +1543,16 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
   write_fd = GetStatusFileDescriptor(parent_process_id,
                                      write_handle_as_size_t,
                                      event_handle_as_size_t);
+
+# elif GTEST_OS_FUCHSIA
+
+  if (fields.size() != 3
+      || !ParseNaturalNumber(fields[1], &line)
+      || !ParseNaturalNumber(fields[2], &index)) {
+    DeathTestAbort("Bad --gtest_internal_run_death_test flag: "
+        + GTEST_FLAG(internal_run_death_test));
+  }
+
 # else
 
   if (fields.size() != 4
index 0292dc11957eaa218b358f1f761ca4a5cb2faea7..a7e65c082a75fc6fb038a023d5b1d3e09e479f11 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Authors: keith.ray@gmail.com (Keith Ray)
 
-#include "gtest/gtest-message.h"
 #include "gtest/internal/gtest-filepath.h"
-#include "gtest/internal/gtest-port.h"
 
 #include <stdlib.h>
+#include "gtest/internal/gtest-port.h"
+#include "gtest/gtest-message.h"
 
 #if GTEST_OS_WINDOWS_MOBILE
 # include <windows.h>
@@ -48,6 +46,8 @@
 # include <climits>  // Some Linux distributions define PATH_MAX here.
 #endif  // GTEST_OS_WINDOWS_MOBILE
 
+#include "gtest/internal/gtest-string.h"
+
 #if GTEST_OS_WINDOWS
 # define GTEST_PATH_MAX_ _MAX_PATH
 #elif defined(PATH_MAX)
@@ -58,8 +58,6 @@
 # define GTEST_PATH_MAX_ _POSIX_PATH_MAX
 #endif  // GTEST_OS_WINDOWS
 
-#include "gtest/internal/gtest-string.h"
-
 namespace testing {
 namespace internal {
 
@@ -130,7 +128,7 @@ FilePath FilePath::RemoveExtension(const char* extension) const {
   return *this;
 }
 
-// Returns a pointer to the last occurence of a valid path separator in
+// Returns a pointer to the last occurrence of a valid path separator in
 // the FilePath. On Windows, for example, both '/' and '\' are valid path
 // separators. Returns NULL if no path separator was found.
 const char* FilePath::FindLastPathSeparator() const {
@@ -252,7 +250,7 @@ bool FilePath::DirectoryExists() const {
 // root directory per disk drive.)
 bool FilePath::IsRootDirectory() const {
 #if GTEST_OS_WINDOWS
-  // TODO(wan@google.com): on Windows a network share like
+  // FIXME: on Windows a network share like
   // \\server\share can be a root directory, although it cannot be the
   // current directory.  Handle this properly.
   return pathname_.length() == 3 && IsAbsolutePath();
@@ -352,7 +350,7 @@ FilePath FilePath::RemoveTrailingPathSeparator() const {
 // Removes any redundant separators that might be in the pathname.
 // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
 // redundancies that might be in a pathname involving "." or "..".
-// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share).
+// FIXME: handle Windows network shares (e.g. \\server\share).
 void FilePath::Normalize() {
   if (pathname_.c_str() == NULL) {
     pathname_ = "";
index ed8a682a964f070ca06a9d8e9a77ea568db0fd3a..479004149b48c28564e205e0210c6c4310418b5b 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-// Utility functions and classes used by the Google C++ testing framework.
-//
-// Author: wan@google.com (Zhanyong Wan)
-//
+// Utility functions and classes used by the Google C++ testing framework.//
 // This file contains purely Google Test's internal implementation.  Please
 // DO NOT #INCLUDE IT IN A USER PROGRAM.
 
 #ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_
 #define GTEST_SRC_GTEST_INTERNAL_INL_H_
 
-// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is
-// part of Google Test's implementation; otherwise it's undefined.
-#if !GTEST_IMPLEMENTATION_
-// If this file is included from the user's code, just say no.
-# error "gtest-internal-inl.h is part of Google Test's internal implementation."
-# error "It must not be included except by Google Test itself."
-#endif  // GTEST_IMPLEMENTATION_
-
 #ifndef _WIN32_WCE
 # include <errno.h>
 #endif  // !_WIN32_WCE
 # include <windows.h>  // NOLINT
 #endif  // GTEST_OS_WINDOWS
 
-#include "gtest/gtest.h"  // NOLINT
+#include "gtest/gtest.h"
 #include "gtest/gtest-spi.h"
 
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
 namespace testing {
 
 // Declares the flags.
@@ -94,6 +86,7 @@ const char kFilterFlag[] = "filter";
 const char kListTestsFlag[] = "list_tests";
 const char kOutputFlag[] = "output";
 const char kPrintTimeFlag[] = "print_time";
+const char kPrintUTF8Flag[] = "print_utf8";
 const char kRandomSeedFlag[] = "random_seed";
 const char kRepeatFlag[] = "repeat";
 const char kShuffleFlag[] = "shuffle";
@@ -174,6 +167,7 @@ class GTestFlagSaver {
     list_tests_ = GTEST_FLAG(list_tests);
     output_ = GTEST_FLAG(output);
     print_time_ = GTEST_FLAG(print_time);
+    print_utf8_ = GTEST_FLAG(print_utf8);
     random_seed_ = GTEST_FLAG(random_seed);
     repeat_ = GTEST_FLAG(repeat);
     shuffle_ = GTEST_FLAG(shuffle);
@@ -195,6 +189,7 @@ class GTestFlagSaver {
     GTEST_FLAG(list_tests) = list_tests_;
     GTEST_FLAG(output) = output_;
     GTEST_FLAG(print_time) = print_time_;
+    GTEST_FLAG(print_utf8) = print_utf8_;
     GTEST_FLAG(random_seed) = random_seed_;
     GTEST_FLAG(repeat) = repeat_;
     GTEST_FLAG(shuffle) = shuffle_;
@@ -216,6 +211,7 @@ class GTestFlagSaver {
   bool list_tests_;
   std::string output_;
   bool print_time_;
+  bool print_utf8_;
   internal::Int32 random_seed_;
   internal::Int32 repeat_;
   bool shuffle_;
@@ -426,7 +422,7 @@ class OsStackTraceGetterInterface {
   //                in the trace.
   //   skip_count - the number of top frames to be skipped; doesn't count
   //                against max_depth.
-  virtual string CurrentStackTrace(int max_depth, int skip_count) = 0;
+  virtual std::string CurrentStackTrace(int max_depth, int skip_count) = 0;
 
   // UponLeavingGTest() should be called immediately before Google Test calls
   // user code. It saves some information about the current stack that
@@ -446,10 +442,20 @@ class OsStackTraceGetter : public OsStackTraceGetterInterface {
  public:
   OsStackTraceGetter() {}
 
-  virtual string CurrentStackTrace(int max_depth, int skip_count);
+  virtual std::string CurrentStackTrace(int max_depth, int skip_count);
   virtual void UponLeavingGTest();
 
  private:
+#if GTEST_HAS_ABSL
+  Mutex mutex_;  // Protects all internal state.
+
+  // We save the stack frame below the frame that calls user code.
+  // We do this because the address of the frame immediately below
+  // the user code changes between the call to UponLeavingGTest()
+  // and any calls to the stack trace code from within the user code.
+  void* caller_frame_ = nullptr;
+#endif  // GTEST_HAS_ABSL
+
   GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter);
 };
 
@@ -664,13 +670,11 @@ class GTEST_API_ UnitTestImpl {
                 tear_down_tc)->AddTestInfo(test_info);
   }
 
-#if GTEST_HAS_PARAM_TEST
   // Returns ParameterizedTestCaseRegistry object used to keep track of
   // value-parameterized tests and instantiate and register them.
   internal::ParameterizedTestCaseRegistry& parameterized_test_registry() {
     return parameterized_test_registry_;
   }
-#endif  // GTEST_HAS_PARAM_TEST
 
   // Sets the TestCase object for the test that's currently running.
   void set_current_test_case(TestCase* a_current_test_case) {
@@ -845,14 +849,12 @@ class GTEST_API_ UnitTestImpl {
   // shuffled order.
   std::vector<int> test_case_indices_;
 
-#if GTEST_HAS_PARAM_TEST
   // ParameterizedTestRegistry object used to register value-parameterized
   // tests.
   internal::ParameterizedTestCaseRegistry parameterized_test_registry_;
 
   // Indicates whether RegisterParameterizedTests() has been called already.
   bool parameterized_tests_registered_;
-#endif  // GTEST_HAS_PARAM_TEST
 
   // Index of the last death test case registered.  Initially -1.
   int last_death_test_case_;
@@ -992,7 +994,7 @@ bool ParseNaturalNumber(const ::std::string& str, Integer* number) {
 
   const bool parse_success = *end == '\0' && errno == 0;
 
-  // TODO(vladl@google.com): Convert this to compile time assertion when it is
+  // FIXME: Convert this to compile time assertion when it is
   // available.
   GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed));
 
@@ -1032,7 +1034,7 @@ class TestResultAccessor {
 #if GTEST_CAN_STREAM_RESULTS_
 
 // Streams test results to the given port on the given host machine.
-class GTEST_API_ StreamingListener : public EmptyTestEventListener {
+class StreamingListener : public EmptyTestEventListener {
  public:
   // Abstract base class for writing strings to a socket.
   class AbstractSocketWriter {
@@ -1040,21 +1042,19 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
     virtual ~AbstractSocketWriter() {}
 
     // Sends a string to the socket.
-    virtual void Send(const string& message) = 0;
+    virtual void Send(const std::string& message) = 0;
 
     // Closes the socket.
     virtual void CloseConnection() {}
 
     // Sends a string and a newline to the socket.
-    void SendLn(const string& message) {
-      Send(message + "\n");
-    }
+    void SendLn(const std::string& message) { Send(message + "\n"); }
   };
 
   // Concrete class for actually writing strings to a socket.
   class SocketWriter : public AbstractSocketWriter {
    public:
-    SocketWriter(const string& host, const string& port)
+    SocketWriter(const std::string& host, const std::string& port)
         : sockfd_(-1), host_name_(host), port_num_(port) {
       MakeConnection();
     }
@@ -1065,7 +1065,7 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
     }
 
     // Sends a string to the socket.
-    virtual void Send(const string& message) {
+    virtual void Send(const std::string& message) {
       GTEST_CHECK_(sockfd_ != -1)
           << "Send() can be called only when there is a connection.";
 
@@ -1091,17 +1091,19 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
     }
 
     int sockfd_;  // socket file descriptor
-    const string host_name_;
-    const string port_num_;
+    const std::string host_name_;
+    const std::string port_num_;
 
     GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter);
   };  // class SocketWriter
 
   // Escapes '=', '&', '%', and '\n' characters in str as "%xx".
-  static string UrlEncode(const char* str);
+  static std::string UrlEncode(const char* str);
 
-  StreamingListener(const string& host, const string& port)
-      : socket_writer_(new SocketWriter(host, port)) { Start(); }
+  StreamingListener(const std::string& host, const std::string& port)
+      : socket_writer_(new SocketWriter(host, port)) {
+    Start();
+  }
 
   explicit StreamingListener(AbstractSocketWriter* socket_writer)
       : socket_writer_(socket_writer) { Start(); }
@@ -1162,13 +1164,13 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
 
  private:
   // Sends the given message and a newline to the socket.
-  void SendLn(const string& message) { socket_writer_->SendLn(message); }
+  void SendLn(const std::string& message) { socket_writer_->SendLn(message); }
 
   // Called at the start of streaming to notify the receiver what
   // protocol we are using.
   void Start() { SendLn("gtest_streaming_protocol_version=1.0"); }
 
-  string FormatBool(bool value) { return value ? "1" : "0"; }
+  std::string FormatBool(bool value) { return value ? "1" : "0"; }
 
   const scoped_ptr<AbstractSocketWriter> socket_writer_;
 
@@ -1180,4 +1182,6 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
 }  // namespace internal
 }  // namespace testing
 
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
 #endif  // GTEST_SRC_GTEST_INTERNAL_INL_H_
index e5bf3dd2be4be336176feb5d14d70d9526b28c7d..fecb5d11c21296a9181a11280226067b643edd05 100644 (file)
@@ -26,8 +26,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: wan@google.com (Zhanyong Wan)
+
 
 #include "gtest/internal/gtest-port.h"
 
 # include <sys/types.h>
 #endif  // GTEST_OS_AIX
 
+#if GTEST_OS_FUCHSIA
+# include <zircon/process.h>
+# include <zircon/syscalls.h>
+#endif  // GTEST_OS_FUCHSIA
+
 #include "gtest/gtest-spi.h"
 #include "gtest/gtest-message.h"
 #include "gtest/internal/gtest-internal.h"
 #include "gtest/internal/gtest-string.h"
-
-// Indicates that this translation unit is part of Google Test's
-// implementation.  It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error.  This trick exists to
-// prevent the accidental inclusion of gtest-internal-inl.h in the
-// user's code.
-#define GTEST_IMPLEMENTATION_ 1
 #include "src/gtest-internal-inl.h"
-#undef GTEST_IMPLEMENTATION_
 
 namespace testing {
 namespace internal {
@@ -93,7 +89,7 @@ const int kStdErrFileno = STDERR_FILENO;
 
 namespace {
 template <typename T>
-T ReadProcFileField(const string& filename, int field) {
+T ReadProcFileField(const std::string& filename, int field) {
   std::string dummy;
   std::ifstream file(filename.c_str());
   while (field-- > 0) {
@@ -107,7 +103,7 @@ T ReadProcFileField(const string& filename, int field) {
 
 // Returns the number of active threads, or 0 when there is an error.
 size_t GetThreadCount() {
-  const string filename =
+  const std::string filename =
       (Message() << "/proc/" << getpid() << "/stat").GetString();
   return ReadProcFileField<int>(filename, 19);
 }
@@ -164,6 +160,25 @@ size_t GetThreadCount() {
   }
 }
 
+#elif GTEST_OS_FUCHSIA
+
+size_t GetThreadCount() {
+  int dummy_buffer;
+  size_t avail;
+  zx_status_t status = zx_object_get_info(
+      zx_process_self(),
+      ZX_INFO_PROCESS_THREADS,
+      &dummy_buffer,
+      0,
+      nullptr,
+      &avail);
+  if (status == ZX_OK) {
+    return avail;
+  } else {
+    return 0;
+  }
+}
+
 #else
 
 size_t GetThreadCount() {
@@ -246,9 +261,9 @@ Mutex::Mutex()
 Mutex::~Mutex() {
   // Static mutexes are leaked intentionally. It is not thread-safe to try
   // to clean them up.
-  // TODO(yukawa): Switch to Slim Reader/Writer (SRW) Locks, which requires
+  // FIXME: Switch to Slim Reader/Writer (SRW) Locks, which requires
   // nothing to clean it up but is available only on Vista and later.
-  // http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937.aspx
+  // https://docs.microsoft.com/en-us/windows/desktop/Sync/slim-reader-writer--srw--locks
   if (type_ == kDynamic) {
     ::DeleteCriticalSection(critical_section_);
     delete critical_section_;
@@ -279,6 +294,43 @@ void Mutex::AssertHeld() {
       << "The current thread is not holding the mutex @" << this;
 }
 
+namespace {
+
+// Use the RAII idiom to flag mem allocs that are intentionally never
+// deallocated. The motivation is to silence the false positive mem leaks
+// that are reported by the debug version of MS's CRT which can only detect
+// if an alloc is missing a matching deallocation.
+// Example:
+//    MemoryIsNotDeallocated memory_is_not_deallocated;
+//    critical_section_ = new CRITICAL_SECTION;
+//
+class MemoryIsNotDeallocated
+{
+ public:
+  MemoryIsNotDeallocated() : old_crtdbg_flag_(0) {
+#ifdef _MSC_VER
+    old_crtdbg_flag_ = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+    // Set heap allocation block type to _IGNORE_BLOCK so that MS debug CRT
+    // doesn't report mem leak if there's no matching deallocation.
+    _CrtSetDbgFlag(old_crtdbg_flag_ & ~_CRTDBG_ALLOC_MEM_DF);
+#endif  //  _MSC_VER
+  }
+
+  ~MemoryIsNotDeallocated() {
+#ifdef _MSC_VER
+    // Restore the original _CRTDBG_ALLOC_MEM_DF flag
+    _CrtSetDbgFlag(old_crtdbg_flag_);
+#endif  //  _MSC_VER
+  }
+
+ private:
+  int old_crtdbg_flag_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(MemoryIsNotDeallocated);
+};
+
+}  // namespace
+
 // Initializes owner_thread_id_ and critical_section_ in static mutexes.
 void Mutex::ThreadSafeLazyInit() {
   // Dynamic mutexes are initialized in the constructor.
@@ -289,7 +341,11 @@ void Mutex::ThreadSafeLazyInit() {
         // If critical_section_init_phase_ was 0 before the exchange, we
         // are the first to test it and need to perform the initialization.
         owner_thread_id_ = 0;
-        critical_section_ = new CRITICAL_SECTION;
+        {
+          // Use RAII to flag that following mem alloc is never deallocated.
+          MemoryIsNotDeallocated memory_is_not_deallocated;
+          critical_section_ = new CRITICAL_SECTION;
+        }
         ::InitializeCriticalSection(critical_section_);
         // Updates the critical_section_init_phase_ to 2 to signal
         // initialization complete.
@@ -328,7 +384,7 @@ class ThreadWithParamSupport : public ThreadWithParamBase {
                              Notification* thread_can_start) {
     ThreadMainParam* param = new ThreadMainParam(runnable, thread_can_start);
     DWORD thread_id;
-    // TODO(yukawa): Consider to use _beginthreadex instead.
+    // FIXME: Consider to use _beginthreadex instead.
     HANDLE thread_handle = ::CreateThread(
         NULL,    // Default security.
         0,       // Default stack size.
@@ -496,7 +552,7 @@ class ThreadLocalRegistryImpl {
                                  FALSE,
                                  thread_id);
     GTEST_CHECK_(thread != NULL);
-    // We need to to pass a valid thread ID pointer into CreateThread for it
+    // We need to pass a valid thread ID pointer into CreateThread for it
     // to work correctly under Win98.
     DWORD watcher_thread_id;
     HANDLE watcher_thread = ::CreateThread(
@@ -531,7 +587,8 @@ class ThreadLocalRegistryImpl {
   // Returns map of thread local instances.
   static ThreadIdToThreadLocals* GetThreadLocalsMapLocked() {
     mutex_.AssertHeld();
-    static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals;
+    MemoryIsNotDeallocated memory_is_not_deallocated;
+    static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals();
     return map;
   }
 
@@ -671,7 +728,7 @@ bool AtomMatchesChar(bool escaped, char pattern_char, char ch) {
 }
 
 // Helper function used by ValidateRegex() to format error messages.
-std::string FormatRegexSyntaxError(const char* regex, int index) {
+static std::string FormatRegexSyntaxError(const char* regex, int index) {
   return (Message() << "Syntax error at index " << index
           << " in simple regular expression \"" << regex << "\": ").GetString();
 }
@@ -680,7 +737,7 @@ std::string FormatRegexSyntaxError(const char* regex, int index) {
 // otherwise returns true.
 bool ValidateRegex(const char* regex) {
   if (regex == NULL) {
-    // TODO(wan@google.com): fix the source file location in the
+    // FIXME: fix the source file location in the
     // assertion failures to match where the regex is used in user
     // code.
     ADD_FAILURE() << "NULL is not a valid simple regular expression.";
@@ -923,9 +980,10 @@ GTestLog::~GTestLog() {
     posix::Abort();
   }
 }
+
 // Disable Microsoft deprecation warnings for POSIX functions called from
 // this class (creat, dup, dup2, and close)
-GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996)
+GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
 
 #if GTEST_HAS_STREAM_REDIRECTION
 
@@ -1009,13 +1067,14 @@ class CapturedStream {
   GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
 };
 
-GTEST_DISABLE_MSC_WARNINGS_POP_()
+GTEST_DISABLE_MSC_DEPRECATED_POP_()
 
 static CapturedStream* g_captured_stderr = NULL;
 static CapturedStream* g_captured_stdout = NULL;
 
 // Starts capturing an output stream (stdout/stderr).
-void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) {
+static void CaptureStream(int fd, const char* stream_name,
+                          CapturedStream** stream) {
   if (*stream != NULL) {
     GTEST_LOG_(FATAL) << "Only one " << stream_name
                       << " capturer can exist at a time.";
@@ -1024,7 +1083,7 @@ void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) {
 }
 
 // Stops capturing the output stream and returns the captured string.
-std::string GetCapturedStream(CapturedStream** captured_stream) {
+static std::string GetCapturedStream(CapturedStream** captured_stream) {
   const std::string content = (*captured_stream)->GetCapturedString();
 
   delete *captured_stream;
@@ -1055,23 +1114,9 @@ std::string GetCapturedStderr() {
 
 #endif  // GTEST_HAS_STREAM_REDIRECTION
 
-std::string TempDir() {
-#if GTEST_OS_WINDOWS_MOBILE
-  return "\\temp\\";
-#elif GTEST_OS_WINDOWS
-  const char* temp_dir = posix::GetEnv("TEMP");
-  if (temp_dir == NULL || temp_dir[0] == '\0')
-    return "\\temp\\";
-  else if (temp_dir[strlen(temp_dir) - 1] == '\\')
-    return temp_dir;
-  else
-    return std::string(temp_dir) + "\\";
-#elif GTEST_OS_LINUX_ANDROID
-  return "/sdcard/";
-#else
-  return "/tmp/";
-#endif  // GTEST_OS_WINDOWS_MOBILE
-}
+
+
+
 
 size_t GetFileSize(FILE* file) {
   fseek(file, 0, SEEK_END);
@@ -1101,22 +1146,36 @@ std::string ReadEntireFile(FILE* file) {
 }
 
 #if GTEST_HAS_DEATH_TEST
+static const std::vector<std::string>* g_injected_test_argvs = NULL;  // Owned.
 
-static const ::std::vector<testing::internal::string>* g_injected_test_argvs =
-                                        NULL;  // Owned.
-
-void SetInjectableArgvs(const ::std::vector<testing::internal::string>* argvs) {
-  if (g_injected_test_argvs != argvs)
-    delete g_injected_test_argvs;
-  g_injected_test_argvs = argvs;
-}
-
-const ::std::vector<testing::internal::string>& GetInjectableArgvs() {
+std::vector<std::string> GetInjectableArgvs() {
   if (g_injected_test_argvs != NULL) {
     return *g_injected_test_argvs;
   }
   return GetArgvs();
 }
+
+void SetInjectableArgvs(const std::vector<std::string>* new_argvs) {
+  if (g_injected_test_argvs != new_argvs) delete g_injected_test_argvs;
+  g_injected_test_argvs = new_argvs;
+}
+
+void SetInjectableArgvs(const std::vector<std::string>& new_argvs) {
+  SetInjectableArgvs(
+      new std::vector<std::string>(new_argvs.begin(), new_argvs.end()));
+}
+
+#if GTEST_HAS_GLOBAL_STRING
+void SetInjectableArgvs(const std::vector< ::string>& new_argvs) {
+  SetInjectableArgvs(
+      new std::vector<std::string>(new_argvs.begin(), new_argvs.end()));
+}
+#endif  // GTEST_HAS_GLOBAL_STRING
+
+void ClearInjectableArgvs() {
+  delete g_injected_test_argvs;
+  g_injected_test_argvs = NULL;
+}
 #endif  // GTEST_HAS_DEATH_TEST
 
 #if GTEST_OS_WINDOWS_MOBILE
@@ -1191,11 +1250,12 @@ bool ParseInt32(const Message& src_text, const char* str, Int32* value) {
 bool BoolFromGTestEnv(const char* flag, bool default_value) {
 #if defined(GTEST_GET_BOOL_FROM_ENV_)
   return GTEST_GET_BOOL_FROM_ENV_(flag, default_value);
-#endif  // defined(GTEST_GET_BOOL_FROM_ENV_)
+#else
   const std::string env_var = FlagToEnvVar(flag);
   const char* const string_value = posix::GetEnv(env_var.c_str());
   return string_value == NULL ?
       default_value : strcmp(string_value, "0") != 0;
+#endif  // defined(GTEST_GET_BOOL_FROM_ENV_)
 }
 
 // Reads and returns a 32-bit integer stored in the environment
@@ -1204,7 +1264,7 @@ bool BoolFromGTestEnv(const char* flag, bool default_value) {
 Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
 #if defined(GTEST_GET_INT32_FROM_ENV_)
   return GTEST_GET_INT32_FROM_ENV_(flag, default_value);
-#endif  // defined(GTEST_GET_INT32_FROM_ENV_)
+#else
   const std::string env_var = FlagToEnvVar(flag);
   const char* const string_value = posix::GetEnv(env_var.c_str());
   if (string_value == NULL) {
@@ -1222,37 +1282,36 @@ Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
   }
 
   return result;
+#endif  // defined(GTEST_GET_INT32_FROM_ENV_)
+}
+
+// As a special case for the 'output' flag, if GTEST_OUTPUT is not
+// set, we look for XML_OUTPUT_FILE, which is set by the Bazel build
+// system.  The value of XML_OUTPUT_FILE is a filename without the
+// "xml:" prefix of GTEST_OUTPUT.
+// Note that this is meant to be called at the call site so it does
+// not check that the flag is 'output'
+// In essence this checks an env variable called XML_OUTPUT_FILE
+// and if it is set we prepend "xml:" to its value, if it not set we return ""
+std::string OutputFlagAlsoCheckEnvVar(){
+  std::string default_value_for_output_flag = "";
+  const char* xml_output_file_env = posix::GetEnv("XML_OUTPUT_FILE");
+  if (NULL != xml_output_file_env) {
+    default_value_for_output_flag = std::string("xml:") + xml_output_file_env;
+  }
+  return default_value_for_output_flag;
 }
 
 // Reads and returns the string environment variable corresponding to
 // the given flag; if it's not set, returns default_value.
-std::string StringFromGTestEnv(const char* flag, const char* default_value) {
+const char* StringFromGTestEnv(const char* flag, const char* default_value) {
 #if defined(GTEST_GET_STRING_FROM_ENV_)
   return GTEST_GET_STRING_FROM_ENV_(flag, default_value);
-#endif  // defined(GTEST_GET_STRING_FROM_ENV_)
+#else
   const std::string env_var = FlagToEnvVar(flag);
-  const char* value = posix::GetEnv(env_var.c_str());
-  if (value != NULL) {
-    return value;
-  }
-
-  // As a special case for the 'output' flag, if GTEST_OUTPUT is not
-  // set, we look for XML_OUTPUT_FILE, which is set by the Bazel build
-  // system.  The value of XML_OUTPUT_FILE is a filename without the
-  // "xml:" prefix of GTEST_OUTPUT.
-  //
-  // The net priority order after flag processing is thus:
-  //   --gtest_output command line flag
-  //   GTEST_OUTPUT environment variable
-  //   XML_OUTPUT_FILE environment variable
-  //   'default_value'
-  if (strcmp(flag, "output") == 0) {
-    value = posix::GetEnv("XML_OUTPUT_FILE");
-    if (value != NULL) {
-      return std::string("xml:") + value;
-    }
-  }
-  return default_value;
+  const char* const value = posix::GetEnv(env_var.c_str());
+  return value == NULL ? default_value : value;
+#endif  // defined(GTEST_GET_STRING_FROM_ENV_)
 }
 
 }  // namespace internal
index a2df412f8a20459043cdb70e5b095a72b7a1394a..de4d245e9fc8897af2137679d913d665fd8436e1 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: wan@google.com (Zhanyong Wan)
 
-// Google Test - The Google C++ Testing Framework
+
+// Google Test - The Google C++ Testing and Mocking Framework
 //
 // This file implements a universal value printer that can print a
 // value of any type T:
 // defines Foo.
 
 #include "gtest/gtest-printers.h"
-#include <ctype.h>
 #include <stdio.h>
+#include <cctype>
 #include <cwchar>
 #include <ostream>  // NOLINT
 #include <string>
 #include "gtest/internal/gtest-port.h"
+#include "src/gtest-internal-inl.h"
 
 namespace testing {
 
@@ -89,7 +89,7 @@ void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
   // If the object size is bigger than kThreshold, we'll have to omit
   // some details by printing only the first and the last kChunkSize
   // bytes.
-  // TODO(wan): let the user control the threshold using a flag.
+  // FIXME: let the user control the threshold using a flag.
   if (count < kThreshold) {
     PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
   } else {
@@ -123,7 +123,7 @@ namespace internal {
 // Depending on the value of a char (or wchar_t), we print it in one
 // of three formats:
 //   - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
-//   - as a hexidecimal escape sequence (e.g. '\x7F'), or
+//   - as a hexadecimal escape sequence (e.g. '\x7F'), or
 //   - as a special escape sequence (e.g. '\r', '\n').
 enum CharFormat {
   kAsIs,
@@ -180,7 +180,10 @@ static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
         *os << static_cast<char>(c);
         return kAsIs;
       } else {
-        *os << "\\x" + String::FormatHexInt(static_cast<UnsignedChar>(c));
+        ostream::fmtflags flags = os->flags();
+        *os << "\\x" << std::hex << std::uppercase
+            << static_cast<int>(static_cast<UnsignedChar>(c));
+        os->flags(flags);
         return kHexEscape;
       }
   }
@@ -227,7 +230,7 @@ void PrintCharAndCodeTo(Char c, ostream* os) {
     return;
   *os << " (" << static_cast<int>(c);
 
-  // For more convenience, we print c's code again in hexidecimal,
+  // For more convenience, we print c's code again in hexadecimal,
   // unless c was already printed in the form '\x##' or the code is in
   // [1, 9].
   if (format == kHexEscape || (1 <= c && c <= 9)) {
@@ -259,11 +262,12 @@ template <typename CharType>
 GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
 GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
 GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
-static void PrintCharsAsStringTo(
+static CharFormat PrintCharsAsStringTo(
     const CharType* begin, size_t len, ostream* os) {
   const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\"";
   *os << kQuoteBegin;
   bool is_previous_hex = false;
+  CharFormat print_format = kAsIs;
   for (size_t index = 0; index < len; ++index) {
     const CharType cur = begin[index];
     if (is_previous_hex && IsXDigit(cur)) {
@@ -273,8 +277,13 @@ static void PrintCharsAsStringTo(
       *os << "\" " << kQuoteBegin;
     }
     is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape;
+    // Remember if any characters required hex escaping.
+    if (is_previous_hex) {
+      print_format = kHexEscape;
+    }
   }
   *os << "\"";
+  return print_format;
 }
 
 // Prints a (const) char/wchar_t array of 'len' elements, starting at address
@@ -344,15 +353,90 @@ void PrintTo(const wchar_t* s, ostream* os) {
 }
 #endif  // wchar_t is native
 
+namespace {
+
+bool ContainsUnprintableControlCodes(const char* str, size_t length) {
+  const unsigned char *s = reinterpret_cast<const unsigned char *>(str);
+
+  for (size_t i = 0; i < length; i++) {
+    unsigned char ch = *s++;
+    if (std::iscntrl(ch)) {
+        switch (ch) {
+        case '\t':
+        case '\n':
+        case '\r':
+          break;
+        default:
+          return true;
+        }
+      }
+  }
+  return false;
+}
+
+bool IsUTF8TrailByte(unsigned char t) { return 0x80 <= t && t<= 0xbf; }
+
+bool IsValidUTF8(const char* str, size_t length) {
+  const unsigned char *s = reinterpret_cast<const unsigned char *>(str);
+
+  for (size_t i = 0; i < length;) {
+    unsigned char lead = s[i++];
+
+    if (lead <= 0x7f) {
+      continue;  // single-byte character (ASCII) 0..7F
+    }
+    if (lead < 0xc2) {
+      return false;  // trail byte or non-shortest form
+    } else if (lead <= 0xdf && (i + 1) <= length && IsUTF8TrailByte(s[i])) {
+      ++i;  // 2-byte character
+    } else if (0xe0 <= lead && lead <= 0xef && (i + 2) <= length &&
+               IsUTF8TrailByte(s[i]) &&
+               IsUTF8TrailByte(s[i + 1]) &&
+               // check for non-shortest form and surrogate
+               (lead != 0xe0 || s[i] >= 0xa0) &&
+               (lead != 0xed || s[i] < 0xa0)) {
+      i += 2;  // 3-byte character
+    } else if (0xf0 <= lead && lead <= 0xf4 && (i + 3) <= length &&
+               IsUTF8TrailByte(s[i]) &&
+               IsUTF8TrailByte(s[i + 1]) &&
+               IsUTF8TrailByte(s[i + 2]) &&
+               // check for non-shortest form
+               (lead != 0xf0 || s[i] >= 0x90) &&
+               (lead != 0xf4 || s[i] < 0x90)) {
+      i += 3;  // 4-byte character
+    } else {
+      return false;
+    }
+  }
+  return true;
+}
+
+void ConditionalPrintAsText(const char* str, size_t length, ostream* os) {
+  if (!ContainsUnprintableControlCodes(str, length) &&
+      IsValidUTF8(str, length)) {
+    *os << "\n    As Text: \"" << str << "\"";
+  }
+}
+
+}  // anonymous namespace
+
 // Prints a ::string object.
 #if GTEST_HAS_GLOBAL_STRING
 void PrintStringTo(const ::string& s, ostream* os) {
-  PrintCharsAsStringTo(s.data(), s.size(), os);
+  if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) {
+    if (GTEST_FLAG(print_utf8)) {
+      ConditionalPrintAsText(s.data(), s.size(), os);
+    }
+  }
 }
 #endif  // GTEST_HAS_GLOBAL_STRING
 
 void PrintStringTo(const ::std::string& s, ostream* os) {
-  PrintCharsAsStringTo(s.data(), s.size(), os);
+  if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) {
+    if (GTEST_FLAG(print_utf8)) {
+      ConditionalPrintAsText(s.data(), s.size(), os);
+    }
+  }
 }
 
 // Prints a ::wstring object.
index fb0e35425e1e6f7179d02b93945bdee431b25e4e..c88860d92385619ea67648845839d14583f11146 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 //
-// Author: mheule@google.com (Markus Heule)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 
 #include "gtest/gtest-test-part.h"
-
-// Indicates that this translation unit is part of Google Test's
-// implementation.  It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error.  This trick exists to
-// prevent the accidental inclusion of gtest-internal-inl.h in the
-// user's code.
-#define GTEST_IMPLEMENTATION_ 1
 #include "src/gtest-internal-inl.h"
-#undef GTEST_IMPLEMENTATION_
 
 namespace testing {
 
index df1eef4754efeb6bc066239b1ccc3bfeeb896310..1dc2ad38bab98bc146c16158a47da1e394bf385f 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: wan@google.com (Zhanyong Wan)
+
 
 #include "gtest/gtest-typed-test.h"
+
 #include "gtest/gtest.h"
 
 namespace testing {
index 184ff6f22ffbe804ea0844773e59984537e71a54..96b07c68abb05a2dbe1be09f1c3535bcf79d0030 100644 (file)
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 //
-// Author: wan@google.com (Zhanyong Wan)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 
 #include "gtest/gtest.h"
 #include "gtest/internal/custom/gtest.h"
 #include "gtest/gtest-spi.h"
 
 #include <ctype.h>
+#include <math.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -44,7 +44,6 @@
 #include <wctype.h>
 
 #include <algorithm>
-#include <cmath>
 #include <iomanip>
 #include <limits>
 #include <list>
@@ -55,7 +54,7 @@
 
 #if GTEST_OS_LINUX
 
-// TODO(kenton@google.com): Use autoconf to detect availability of
+// FIXME: Use autoconf to detect availability of
 // gettimeofday().
 # define GTEST_HAS_GETTIMEOFDAY_ 1
 
@@ -94,9 +93,9 @@
 
 # if GTEST_OS_WINDOWS_MINGW
 // MinGW has gettimeofday() but not _ftime64().
-// TODO(kenton@google.com): Use autoconf to detect availability of
+// FIXME: Use autoconf to detect availability of
 //   gettimeofday().
-// TODO(kenton@google.com): There are other ways to get the time on
+// FIXME: There are other ways to get the time on
 //   Windows, like GetTickCount() or GetSystemTimeAsFileTime().  MinGW
 //   supports these.  consider using them instead.
 #  define GTEST_HAS_GETTIMEOFDAY_ 1
 #else
 
 // Assume other platforms have gettimeofday().
-// TODO(kenton@google.com): Use autoconf to detect availability of
+// FIXME: Use autoconf to detect availability of
 //   gettimeofday().
 # define GTEST_HAS_GETTIMEOFDAY_ 1
 
 # include <sys/types.h>  // NOLINT
 #endif
 
-// Indicates that this translation unit is part of Google Test's
-// implementation.  It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error.  This trick is to
-// prevent a user from accidentally including gtest-internal-inl.h in
-// his code.
-#define GTEST_IMPLEMENTATION_ 1
 #include "src/gtest-internal-inl.h"
-#undef GTEST_IMPLEMENTATION_
 
 #if GTEST_OS_WINDOWS
 # define vsnprintf _vsnprintf
 #endif  // GTEST_OS_WINDOWS
 
+#if GTEST_OS_MAC
+#ifndef GTEST_OS_IOS
+#include <crt_externs.h>
+#endif
+#endif
+
+#if GTEST_HAS_ABSL
+#include "absl/debugging/failure_signal_handler.h"
+#include "absl/debugging/stacktrace.h"
+#include "absl/debugging/symbolize.h"
+#include "absl/strings/str_cat.h"
+#endif  // GTEST_HAS_ABSL
+
 namespace testing {
 
 using internal::CountIf;
@@ -167,8 +172,10 @@ static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*";
 // A test filter that matches everything.
 static const char kUniversalFilter[] = "*";
 
-// The default output file for XML output.
-static const char kDefaultOutputFile[] = "test_detail.xml";
+// The default output format.
+static const char kDefaultOutputFormat[] = "xml";
+// The default output file.
+static const char kDefaultOutputFile[] = "test_detail";
 
 // The environment variable name for the test shard index.
 static const char kTestShardIndex[] = "GTEST_SHARD_INDEX";
@@ -187,15 +194,31 @@ const char kStackTraceMarker[] = "\nStack trace:\n";
 // specified on the command line.
 bool g_help_flag = false;
 
+// Utilty function to Open File for Writing
+static FILE* OpenFileForWriting(const std::string& output_file) {
+  FILE* fileout = NULL;
+  FilePath output_file_path(output_file);
+  FilePath output_dir(output_file_path.RemoveFileName());
+
+  if (output_dir.CreateDirectoriesRecursively()) {
+    fileout = posix::FOpen(output_file.c_str(), "w");
+  }
+  if (fileout == NULL) {
+    GTEST_LOG_(FATAL) << "Unable to open file \"" << output_file << "\"";
+  }
+  return fileout;
+}
+
 }  // namespace internal
 
+// Bazel passes in the argument to '--test_filter' via the TESTBRIDGE_TEST_ONLY
+// environment variable.
 static const char* GetDefaultFilter() {
-#ifdef GTEST_TEST_FILTER_ENV_VAR_
-  const char* const testbridge_test_only = getenv(GTEST_TEST_FILTER_ENV_VAR_);
+  const char* const testbridge_test_only =
+      internal::posix::GetEnv("TESTBRIDGE_TEST_ONLY");
   if (testbridge_test_only != NULL) {
     return testbridge_test_only;
   }
-#endif  // GTEST_TEST_FILTER_ENV_VAR_
   return kUniversalFilter;
 }
 
@@ -232,15 +255,28 @@ GTEST_DEFINE_string_(
     "exclude).  A test is run if it matches one of the positive "
     "patterns and does not match any of the negative patterns.");
 
+GTEST_DEFINE_bool_(
+    install_failure_signal_handler,
+    internal::BoolFromGTestEnv("install_failure_signal_handler", false),
+    "If true and supported on the current platform, " GTEST_NAME_ " should "
+    "install a signal handler that dumps debugging information when fatal "
+    "signals are raised.");
+
 GTEST_DEFINE_bool_(list_tests, false,
                    "List all tests without running them.");
 
+// The net priority order after flag processing is thus:
+//   --gtest_output command line flag
+//   GTEST_OUTPUT environment variable
+//   XML_OUTPUT_FILE environment variable
+//   ''
 GTEST_DEFINE_string_(
     output,
-    internal::StringFromGTestEnv("output", ""),
-    "A format (currently must be \"xml\"), optionally followed "
-    "by a colon and an output file name or directory. A directory "
-    "is indicated by a trailing pathname separator. "
+    internal::StringFromGTestEnv("output",
+      internal::OutputFlagAlsoCheckEnvVar().c_str()),
+    "A format (defaults to \"xml\" but can be specified to be \"json\"), "
+    "optionally followed by a colon and an output file name or directory. "
+    "A directory is indicated by a trailing pathname separator. "
     "Examples: \"xml:filename.xml\", \"xml::directoryname/\". "
     "If a directory is specified, output files will be created "
     "within that directory, with file-names based on the test "
@@ -253,6 +289,12 @@ GTEST_DEFINE_bool_(
     "True iff " GTEST_NAME_
     " should display elapsed time in text output.");
 
+GTEST_DEFINE_bool_(
+    print_utf8,
+    internal::BoolFromGTestEnv("print_utf8", true),
+    "True iff " GTEST_NAME_
+    " prints UTF8 characters as text.");
+
 GTEST_DEFINE_int32_(
     random_seed,
     internal::Int32FromGTestEnv("random_seed", 0),
@@ -294,7 +336,7 @@ GTEST_DEFINE_bool_(
     internal::BoolFromGTestEnv("throw_on_failure", false),
     "When this flag is specified, a failed assertion will throw an exception "
     "if exceptions are enabled or exit the program with a non-zero code "
-    "otherwise.");
+    "otherwise. For use with an external test framework.");
 
 #if GTEST_USE_OWN_FLAGFILE_FLAG_
 GTEST_DEFINE_string_(
@@ -310,7 +352,8 @@ namespace internal {
 // than kMaxRange.
 UInt32 Random::Generate(UInt32 range) {
   // These constants are the same as are used in glibc's rand(3).
-  state_ = (1103515245U*state_ + 12345U) % kMaxRange;
+  // Use wider types than necessary to prevent unsigned overflow diagnostics.
+  state_ = static_cast<UInt32>(1103515245ULL*state_ + 12345U) % kMaxRange;
 
   GTEST_CHECK_(range > 0)
       << "Cannot generate a number in the range [0, 0).";
@@ -384,12 +427,15 @@ void AssertHelper::operator=(const Message& message) const {
 GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex);
 
 // A copy of all command line arguments.  Set by InitGoogleTest().
-::std::vector<testing::internal::string> g_argvs;
+static ::std::vector<std::string> g_argvs;
 
-const ::std::vector<testing::internal::string>& GetArgvs() {
+::std::vector<std::string> GetArgvs() {
 #if defined(GTEST_CUSTOM_GET_ARGVS_)
-  return GTEST_CUSTOM_GET_ARGVS_();
-#else  // defined(GTEST_CUSTOM_GET_ARGVS_)
+  // GTEST_CUSTOM_GET_ARGVS_() may return a container of std::string or
+  // ::string. This code converts it to the appropriate type.
+  const auto& custom = GTEST_CUSTOM_GET_ARGVS_();
+  return ::std::vector<std::string>(custom.begin(), custom.end());
+#else   // defined(GTEST_CUSTOM_GET_ARGVS_)
   return g_argvs;
 #endif  // defined(GTEST_CUSTOM_GET_ARGVS_)
 }
@@ -413,8 +459,6 @@ FilePath GetCurrentExecutableName() {
 // Returns the output format, or "" for normal printed output.
 std::string UnitTestOptions::GetOutputFormat() {
   const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
-  if (gtest_output_flag == NULL) return std::string("");
-
   const char* const colon = strchr(gtest_output_flag, ':');
   return (colon == NULL) ?
       std::string(gtest_output_flag) :
@@ -425,19 +469,22 @@ std::string UnitTestOptions::GetOutputFormat() {
 // was explicitly specified.
 std::string UnitTestOptions::GetAbsolutePathToOutputFile() {
   const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
-  if (gtest_output_flag == NULL)
-    return "";
+
+  std::string format = GetOutputFormat();
+  if (format.empty())
+    format = std::string(kDefaultOutputFormat);
 
   const char* const colon = strchr(gtest_output_flag, ':');
   if (colon == NULL)
-    return internal::FilePath::ConcatPaths(
+    return internal::FilePath::MakeFileName(
         internal::FilePath(
             UnitTest::GetInstance()->original_working_dir()),
-        internal::FilePath(kDefaultOutputFile)).string();
+        internal::FilePath(kDefaultOutputFile), 0,
+        format.c_str()).string();
 
   internal::FilePath output_name(colon + 1);
   if (!output_name.IsAbsolutePath())
-    // TODO(wan@google.com): on Windows \some\path is not an absolute
+    // FIXME: on Windows \some\path is not an absolute
     // path (as its meaning depends on the current drive), yet the
     // following logic for turning it into an absolute path is wrong.
     // Fix it.
@@ -628,12 +675,12 @@ extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId();
 // This predicate-formatter checks that 'results' contains a test part
 // failure of the given type and that the failure message contains the
 // given substring.
-AssertionResult HasOneFailure(const char* /* results_expr */,
-                              const char* /* type_expr */,
-                              const char* /* substr_expr */,
-                              const TestPartResultArray& results,
-                              TestPartResult::Type type,
-                              const string& substr) {
+static AssertionResult HasOneFailure(const char* /* results_expr */,
+                                     const char* /* type_expr */,
+                                     const char* /* substr_expr */,
+                                     const TestPartResultArray& results,
+                                     TestPartResult::Type type,
+                                     const std::string& substr) {
   const std::string expected(type == TestPartResult::kFatalFailure ?
                         "1 fatal failure" :
                         "1 non-fatal failure");
@@ -667,13 +714,10 @@ AssertionResult HasOneFailure(const char* /* results_expr */,
 // The constructor of SingleFailureChecker remembers where to look up
 // test part results, what type of failure we expect, and what
 // substring the failure message should contain.
-SingleFailureChecker:: SingleFailureChecker(
-    const TestPartResultArray* results,
-    TestPartResult::Type type,
-    const string& substr)
-    : results_(results),
-      type_(type),
-      substr_(substr) {}
+SingleFailureChecker::SingleFailureChecker(const TestPartResultArray* results,
+                                           TestPartResult::Type type,
+                                           const std::string& substr)
+    : results_(results), type_(type), substr_(substr) {}
 
 // The destructor of SingleFailureChecker verifies that the given
 // TestPartResultArray contains exactly one failure that has the given
@@ -814,7 +858,7 @@ TimeInMillis GetTimeInMillis() {
   SYSTEMTIME now_systime;
   FILETIME now_filetime;
   ULARGE_INTEGER now_int64;
-  // TODO(kenton@google.com): Shouldn't this just use
+  // FIXME: Shouldn't this just use
   //   GetSystemTimeAsFileTime()?
   GetSystemTime(&now_systime);
   if (SystemTimeToFileTime(&now_systime, &now_filetime)) {
@@ -830,11 +874,11 @@ TimeInMillis GetTimeInMillis() {
 
   // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996
   // (deprecated function) there.
-  // TODO(kenton@google.com): Use GetTickCount()?  Or use
+  // FIXME: Use GetTickCount()?  Or use
   //   SystemTimeToFileTime()
-  GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996)
+  GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
   _ftime64(&now);
-  GTEST_DISABLE_MSC_WARNINGS_POP_()
+  GTEST_DISABLE_MSC_DEPRECATED_POP_()
 
   return static_cast<TimeInMillis>(now.time) * 1000 + now.millitm;
 #elif GTEST_HAS_GETTIMEOFDAY_
@@ -1171,7 +1215,7 @@ class Hunk {
   // Print a unified diff header for one hunk.
   // The format is
   //   "@@ -<left_start>,<left_length> +<right_start>,<right_length> @@"
-  // where the left/right parts are ommitted if unnecessary.
+  // where the left/right parts are omitted if unnecessary.
   void PrintHeader(std::ostream* ss) const {
     *ss << "@@ ";
     if (removes_) {
@@ -1315,13 +1359,14 @@ AssertionResult EqFailure(const char* lhs_expression,
                           const std::string& rhs_value,
                           bool ignoring_case) {
   Message msg;
-  msg << "      Expected: " << lhs_expression;
+  msg << "Expected equality of these values:";
+  msg << "\n  " << lhs_expression;
   if (lhs_value != lhs_expression) {
-    msg << "\n      Which is: " << lhs_value;
+    msg << "\n    Which is: " << lhs_value;
   }
-  msg << "\nTo be equal to: " << rhs_expression;
+  msg << "\n  " << rhs_expression;
   if (rhs_value != rhs_expression) {
-    msg << "\n      Which is: " << rhs_value;
+    msg << "\n    Which is: " << rhs_value;
   }
 
   if (ignoring_case) {
@@ -1368,7 +1413,7 @@ AssertionResult DoubleNearPredFormat(const char* expr1,
   const double diff = fabs(val1 - val2);
   if (diff <= abs_error) return AssertionSuccess();
 
-  // TODO(wan): do not print the value of an expression if it's
+  // FIXME: do not print the value of an expression if it's
   // already a literal.
   return AssertionFailure()
       << "The difference between " << expr1 << " and " << expr2
@@ -1663,7 +1708,7 @@ namespace {
 AssertionResult HRESULTFailureHelper(const char* expr,
                                      const char* expected,
                                      long hr) {  // NOLINT
-# if GTEST_OS_WINDOWS_MOBILE
+# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_TV_TITLE
 
   // Windows CE doesn't support FormatMessage.
   const char error_text[] = "";
@@ -1720,7 +1765,7 @@ AssertionResult IsHRESULTFailure(const char* expr, long hr) {  // NOLINT
 // Utility functions for encoding Unicode text (wide strings) in
 // UTF-8.
 
-// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8
+// A Unicode code-point can have up to 21 bits, and is encoded in UTF-8
 // like this:
 //
 // Code-point length   Encoding
@@ -1784,7 +1829,7 @@ std::string CodePointToUtf8(UInt32 code_point) {
   return str;
 }
 
-// The following two functions only make sense if the the system
+// The following two functions only make sense if the system
 // uses UTF-16 for wide string encoding. All supported systems
 // with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16.
 
@@ -2096,13 +2141,8 @@ static const char* const kReservedTestSuiteAttributes[] = {
 
 // The list of reserved attributes used in the <testcase> element of XML output.
 static const char* const kReservedTestCaseAttributes[] = {
-  "classname",
-  "name",
-  "status",
-  "time",
-  "type_param",
-  "value_param"
-};
+    "classname",  "name",        "status", "time",
+    "type_param", "value_param", "file",   "line"};
 
 template <int kSize>
 std::vector<std::string> ArrayAsVector(const char* const (&array)[kSize]) {
@@ -2138,8 +2178,9 @@ static std::string FormatWordList(const std::vector<std::string>& words) {
   return word_list.GetString();
 }
 
-bool ValidateTestPropertyName(const std::string& property_name,
-                              const std::vector<std::string>& reserved_names) {
+static bool ValidateTestPropertyName(
+    const std::string& property_name,
+    const std::vector<std::string>& reserved_names) {
   if (std::find(reserved_names.begin(), reserved_names.end(), property_name) !=
           reserved_names.end()) {
     ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name
@@ -2436,6 +2477,8 @@ Result HandleExceptionsInMethodIfSupported(
 #if GTEST_HAS_EXCEPTIONS
     try {
       return HandleSehExceptionsInMethodIfSupported(object, method, location);
+    } catch (const AssertionException&) {  // NOLINT
+      // This failure was reported already.
     } catch (const internal::GoogleTestFailureException&) {  // NOLINT
       // This exception type can only be thrown by a failed Google
       // Test assertion with the intention of letting another testing
@@ -2557,7 +2600,6 @@ TestInfo* MakeAndRegisterTestInfo(
   return test_info;
 }
 
-#if GTEST_HAS_PARAM_TEST
 void ReportInvalidTestCaseType(const char* test_case_name,
                                CodeLocation code_location) {
   Message errors;
@@ -2571,13 +2613,10 @@ void ReportInvalidTestCaseType(const char* test_case_name,
       << "probably rename one of the classes to put the tests into different\n"
       << "test cases.";
 
-  fprintf(stderr, "%s %s",
-          FormatFileLocation(code_location.file.c_str(),
-                             code_location.line).c_str(),
-          errors.GetString().c_str());
+  GTEST_LOG_(ERROR) << FormatFileLocation(code_location.file.c_str(),
+                                          code_location.line)
+                    << " " << errors.GetString();
 }
-#endif  // GTEST_HAS_PARAM_TEST
-
 }  // namespace internal
 
 namespace {
@@ -2615,12 +2654,10 @@ namespace internal {
 // and INSTANTIATE_TEST_CASE_P into regular tests and registers those.
 // This will be done just once during the program runtime.
 void UnitTestImpl::RegisterParameterizedTests() {
-#if GTEST_HAS_PARAM_TEST
   if (!parameterized_tests_registered_) {
     parameterized_test_registry_.RegisterTests();
     parameterized_tests_registered_ = true;
   }
-#endif
 }
 
 }  // namespace internal
@@ -2648,18 +2685,18 @@ void TestInfo::Run() {
       factory_, &internal::TestFactoryBase::CreateTest,
       "the test fixture's constructor");
 
-  // Runs the test only if the test object was created and its
-  // constructor didn't generate a fatal failure.
-  if ((test != NULL) && !Test::HasFatalFailure()) {
+  // Runs the test if the constructor didn't generate a fatal failure.
+  // Note that the object will not be null
+  if (!Test::HasFatalFailure()) {
     // This doesn't throw as all user code that can throw are wrapped into
     // exception handling code.
     test->Run();
   }
 
-  // Deletes the test object.
-  impl->os_stack_trace_getter()->UponLeavingGTest();
-  internal::HandleExceptionsInMethodIfSupported(
-      test, &Test::DeleteSelf_, "the test fixture's destructor");
+    // Deletes the test object.
+    impl->os_stack_trace_getter()->UponLeavingGTest();
+    internal::HandleExceptionsInMethodIfSupported(
+        test, &Test::DeleteSelf_, "the test fixture's destructor");
 
   result_.set_elapsed_time(internal::GetTimeInMillis() - start);
 
@@ -2885,10 +2922,10 @@ enum GTestColor {
 };
 
 #if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \
-    !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
+    !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW
 
 // Returns the character attribute for the given color.
-WORD GetColorAttribute(GTestColor color) {
+static WORD GetColorAttribute(GTestColor color) {
   switch (color) {
     case COLOR_RED:    return FOREGROUND_RED;
     case COLOR_GREEN:  return FOREGROUND_GREEN;
@@ -2897,11 +2934,42 @@ WORD GetColorAttribute(GTestColor color) {
   }
 }
 
+static int GetBitOffset(WORD color_mask) {
+  if (color_mask == 0) return 0;
+
+  int bitOffset = 0;
+  while ((color_mask & 1) == 0) {
+    color_mask >>= 1;
+    ++bitOffset;
+  }
+  return bitOffset;
+}
+
+static WORD GetNewColor(GTestColor color, WORD old_color_attrs) {
+  // Let's reuse the BG
+  static const WORD background_mask = BACKGROUND_BLUE | BACKGROUND_GREEN |
+                                      BACKGROUND_RED | BACKGROUND_INTENSITY;
+  static const WORD foreground_mask = FOREGROUND_BLUE | FOREGROUND_GREEN |
+                                      FOREGROUND_RED | FOREGROUND_INTENSITY;
+  const WORD existing_bg = old_color_attrs & background_mask;
+
+  WORD new_color =
+      GetColorAttribute(color) | existing_bg | FOREGROUND_INTENSITY;
+  static const int bg_bitOffset = GetBitOffset(background_mask);
+  static const int fg_bitOffset = GetBitOffset(foreground_mask);
+
+  if (((new_color & background_mask) >> bg_bitOffset) ==
+      ((new_color & foreground_mask) >> fg_bitOffset)) {
+    new_color ^= FOREGROUND_INTENSITY;  // invert intensity
+  }
+  return new_color;
+}
+
 #else
 
 // Returns the ANSI color code for the given color.  COLOR_DEFAULT is
 // an invalid input.
-const char* GetAnsiColorCode(GTestColor color) {
+static const char* GetAnsiColorCode(GTestColor color) {
   switch (color) {
     case COLOR_RED:     return "1";
     case COLOR_GREEN:   return "2";
@@ -2917,7 +2985,7 @@ bool ShouldUseColor(bool stdout_is_tty) {
   const char* const gtest_color = GTEST_FLAG(color).c_str();
 
   if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) {
-#if GTEST_OS_WINDOWS
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
     // On Windows the TERM variable is usually not set, but the
     // console there does support colors.
     return stdout_is_tty;
@@ -2953,7 +3021,7 @@ bool ShouldUseColor(bool stdout_is_tty) {
 // cannot simply emit special characters and have the terminal change colors.
 // This routine must actually emit the characters rather than return a string
 // that would be colored when printed, as can be done on Linux.
-void ColoredPrintf(GTestColor color, const char* fmt, ...) {
+static void ColoredPrintf(GTestColor color, const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
 
@@ -2974,20 +3042,21 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) {
   }
 
 #if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \
-    !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
+    !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW
   const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
 
   // Gets the current text color.
   CONSOLE_SCREEN_BUFFER_INFO buffer_info;
   GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
   const WORD old_color_attrs = buffer_info.wAttributes;
+  const WORD new_color = GetNewColor(color, old_color_attrs);
 
   // We need to flush the stream buffers into the console before each
   // SetConsoleTextAttribute call lest it affect the text that is already
   // printed but has not yet reached the console.
   fflush(stdout);
-  SetConsoleTextAttribute(stdout_handle,
-                          GetColorAttribute(color) | FOREGROUND_INTENSITY);
+  SetConsoleTextAttribute(stdout_handle, new_color);
+
   vprintf(fmt, args);
 
   fflush(stdout);
@@ -3001,12 +3070,12 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) {
   va_end(args);
 }
 
-// Text printed in Google Test's text output and --gunit_list_tests
+// Text printed in Google Test's text output and --gtest_list_tests
 // output to label the type parameter and value parameter for a test.
 static const char kTypeParamLabel[] = "TypeParam";
 static const char kValueParamLabel[] = "GetParam()";
 
-void PrintFullTestCommentIfPresent(const TestInfo& test_info) {
+static void PrintFullTestCommentIfPresent(const TestInfo& test_info) {
   const char* const type_param = test_info.type_param();
   const char* const value_param = test_info.value_param();
 
@@ -3277,7 +3346,7 @@ void TestEventRepeater::Append(TestEventListener *listener) {
   listeners_.push_back(listener);
 }
 
-// TODO(vladl@google.com): Factor the search functionality into Vector::Find.
+// FIXME: Factor the search functionality into Vector::Find.
 TestEventListener* TestEventRepeater::Release(TestEventListener *listener) {
   for (size_t i = 0; i < listeners_.size(); ++i) {
     if (listeners_[i] == listener) {
@@ -3351,6 +3420,11 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
   explicit XmlUnitTestResultPrinter(const char* output_file);
 
   virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+  void ListTestsMatchingFilter(const std::vector<TestCase*>& test_cases);
+
+  // Prints an XML summary of all unit tests.
+  static void PrintXmlTestsList(std::ostream* stream,
+                                const std::vector<TestCase*>& test_cases);
 
  private:
   // Is c a whitespace character that is normalized to a space character
@@ -3412,6 +3486,11 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
   // to delimit this attribute from prior attributes.
   static std::string TestPropertiesAsXmlAttributes(const TestResult& result);
 
+  // Streams an XML representation of the test properties of a TestResult
+  // object.
+  static void OutputXmlTestProperties(std::ostream* stream,
+                                      const TestResult& result);
+
   // The output file.
   const std::string output_file_;
 
@@ -3421,46 +3500,30 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
 // Creates a new XmlUnitTestResultPrinter.
 XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file)
     : output_file_(output_file) {
-  if (output_file_.c_str() == NULL || output_file_.empty()) {
-    fprintf(stderr, "XML output file may not be null\n");
-    fflush(stderr);
-    exit(EXIT_FAILURE);
+  if (output_file_.empty()) {
+    GTEST_LOG_(FATAL) << "XML output file may not be null";
   }
 }
 
 // Called after the unit test ends.
 void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
                                                   int /*iteration*/) {
-  FILE* xmlout = NULL;
-  FilePath output_file(output_file_);
-  FilePath output_dir(output_file.RemoveFileName());
-
-  if (output_dir.CreateDirectoriesRecursively()) {
-    xmlout = posix::FOpen(output_file_.c_str(), "w");
-  }
-  if (xmlout == NULL) {
-    // TODO(wan): report the reason of the failure.
-    //
-    // We don't do it for now as:
-    //
-    //   1. There is no urgent need for it.
-    //   2. It's a bit involved to make the errno variable thread-safe on
-    //      all three operating systems (Linux, Windows, and Mac OS).
-    //   3. To interpret the meaning of errno in a thread-safe way,
-    //      we need the strerror_r() function, which is not available on
-    //      Windows.
-    fprintf(stderr,
-            "Unable to open file \"%s\"\n",
-            output_file_.c_str());
-    fflush(stderr);
-    exit(EXIT_FAILURE);
-  }
+  FILE* xmlout = OpenFileForWriting(output_file_);
   std::stringstream stream;
   PrintXmlUnitTest(&stream, unit_test);
   fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
   fclose(xmlout);
 }
 
+void XmlUnitTestResultPrinter::ListTestsMatchingFilter(
+    const std::vector<TestCase*>& test_cases) {
+  FILE* xmlout = OpenFileForWriting(output_file_);
+  std::stringstream stream;
+  PrintXmlTestsList(&stream, test_cases);
+  fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
+  fclose(xmlout);
+}
+
 // Returns an XML-escaped copy of the input string str.  If is_attribute
 // is true, the text is meant to appear as an attribute value, and
 // normalizable whitespace is preserved by replacing it with character
@@ -3471,7 +3534,7 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
 // module will consist of ordinary English text.
 // If this module is ever modified to produce version 1.1 XML output,
 // most invalid characters can be retained using character references.
-// TODO(wan): It might be nice to have a minimally invasive, human-readable
+// FIXME: It might be nice to have a minimally invasive, human-readable
 // escaping scheme for invalid characters, rather than dropping them.
 std::string XmlUnitTestResultPrinter::EscapeXml(
     const std::string& str, bool is_attribute) {
@@ -3532,6 +3595,7 @@ std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(
 
 // The following routines generate an XML representation of a UnitTest
 // object.
+// GOOGLETEST_CM0009 DO NOT DELETE
 //
 // This is how Google Test concepts map to the DTD:
 //
@@ -3621,13 +3685,17 @@ void XmlUnitTestResultPrinter::OutputXmlAttribute(
 }
 
 // Prints an XML representation of a TestInfo object.
-// TODO(wan): There is also value in printing properties with the plain printer.
+// FIXME: There is also value in printing properties with the plain printer.
 void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
                                                  const char* test_case_name,
                                                  const TestInfo& test_info) {
   const TestResult& result = *test_info.result();
   const std::string kTestcase = "testcase";
 
+  if (test_info.is_in_another_shard()) {
+    return;
+  }
+
   *stream << "    <testcase";
   OutputXmlAttribute(stream, kTestcase, "name", test_info.name());
 
@@ -3638,13 +3706,19 @@ void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
   if (test_info.type_param() != NULL) {
     OutputXmlAttribute(stream, kTestcase, "type_param", test_info.type_param());
   }
+  if (GTEST_FLAG(list_tests)) {
+    OutputXmlAttribute(stream, kTestcase, "file", test_info.file());
+    OutputXmlAttribute(stream, kTestcase, "line",
+                       StreamableToString(test_info.line()));
+    *stream << " />\n";
+    return;
+  }
 
   OutputXmlAttribute(stream, kTestcase, "status",
                      test_info.should_run() ? "run" : "notrun");
   OutputXmlAttribute(stream, kTestcase, "time",
                      FormatTimeInMillisAsSeconds(result.elapsed_time()));
   OutputXmlAttribute(stream, kTestcase, "classname", test_case_name);
-  *stream << TestPropertiesAsXmlAttributes(result);
 
   int failures = 0;
   for (int i = 0; i < result.total_part_count(); ++i) {
@@ -3653,22 +3727,28 @@ void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
       if (++failures == 1) {
         *stream << ">\n";
       }
-      const string location = internal::FormatCompilerIndependentFileLocation(
-          part.file_name(), part.line_number());
-      const string summary = location + "\n" + part.summary();
+      const std::string location =
+          internal::FormatCompilerIndependentFileLocation(part.file_name(),
+                                                          part.line_number());
+      const std::string summary = location + "\n" + part.summary();
       *stream << "      <failure message=\""
               << EscapeXmlAttribute(summary.c_str())
               << "\" type=\"\">";
-      const string detail = location + "\n" + part.message();
+      const std::string detail = location + "\n" + part.message();
       OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str());
       *stream << "</failure>\n";
     }
   }
 
-  if (failures == 0)
+  if (failures == 0 && result.test_property_count() == 0) {
     *stream << " />\n";
-  else
+  } else {
+    if (failures == 0) {
+      *stream << ">\n";
+    }
+    OutputXmlTestProperties(stream, result);
     *stream << "    </testcase>\n";
+  }
 }
 
 // Prints an XML representation of a TestCase object
@@ -3679,17 +3759,18 @@ void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream,
   OutputXmlAttribute(stream, kTestsuite, "name", test_case.name());
   OutputXmlAttribute(stream, kTestsuite, "tests",
                      StreamableToString(test_case.reportable_test_count()));
-  OutputXmlAttribute(stream, kTestsuite, "failures",
-                     StreamableToString(test_case.failed_test_count()));
-  OutputXmlAttribute(
-      stream, kTestsuite, "disabled",
-      StreamableToString(test_case.reportable_disabled_test_count()));
-  OutputXmlAttribute(stream, kTestsuite, "errors", "0");
-  OutputXmlAttribute(stream, kTestsuite, "time",
-                     FormatTimeInMillisAsSeconds(test_case.elapsed_time()));
-  *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result())
-          << ">\n";
-
+  if (!GTEST_FLAG(list_tests)) {
+    OutputXmlAttribute(stream, kTestsuite, "failures",
+                       StreamableToString(test_case.failed_test_count()));
+    OutputXmlAttribute(
+        stream, kTestsuite, "disabled",
+        StreamableToString(test_case.reportable_disabled_test_count()));
+    OutputXmlAttribute(stream, kTestsuite, "errors", "0");
+    OutputXmlAttribute(stream, kTestsuite, "time",
+                       FormatTimeInMillisAsSeconds(test_case.elapsed_time()));
+    *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result());
+  }
+  *stream << ">\n";
   for (int i = 0; i < test_case.total_test_count(); ++i) {
     if (test_case.GetTestInfo(i)->is_reportable())
       OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i));
@@ -3723,7 +3804,6 @@ void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream,
     OutputXmlAttribute(stream, kTestsuites, "random_seed",
                        StreamableToString(unit_test.random_seed()));
   }
-
   *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result());
 
   OutputXmlAttribute(stream, kTestsuites, "name", "AllTests");
@@ -3736,6 +3816,28 @@ void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream,
   *stream << "</" << kTestsuites << ">\n";
 }
 
+void XmlUnitTestResultPrinter::PrintXmlTestsList(
+    std::ostream* stream, const std::vector<TestCase*>& test_cases) {
+  const std::string kTestsuites = "testsuites";
+
+  *stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+  *stream << "<" << kTestsuites;
+
+  int total_tests = 0;
+  for (size_t i = 0; i < test_cases.size(); ++i) {
+    total_tests += test_cases[i]->total_test_count();
+  }
+  OutputXmlAttribute(stream, kTestsuites, "tests",
+                     StreamableToString(total_tests));
+  OutputXmlAttribute(stream, kTestsuites, "name", "AllTests");
+  *stream << ">\n";
+
+  for (size_t i = 0; i < test_cases.size(); ++i) {
+    PrintXmlTestCase(stream, *test_cases[i]);
+  }
+  *stream << "</" << kTestsuites << ">\n";
+}
+
 // Produces a string representing the test properties in a result as space
 // delimited XML attributes based on the property key="value" pairs.
 std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes(
@@ -3749,8 +3851,390 @@ std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes(
   return attributes.GetString();
 }
 
+void XmlUnitTestResultPrinter::OutputXmlTestProperties(
+    std::ostream* stream, const TestResult& result) {
+  const std::string kProperties = "properties";
+  const std::string kProperty = "property";
+
+  if (result.test_property_count() <= 0) {
+    return;
+  }
+
+  *stream << "<" << kProperties << ">\n";
+  for (int i = 0; i < result.test_property_count(); ++i) {
+    const TestProperty& property = result.GetTestProperty(i);
+    *stream << "<" << kProperty;
+    *stream << " name=\"" << EscapeXmlAttribute(property.key()) << "\"";
+    *stream << " value=\"" << EscapeXmlAttribute(property.value()) << "\"";
+    *stream << "/>\n";
+  }
+  *stream << "</" << kProperties << ">\n";
+}
+
 // End XmlUnitTestResultPrinter
 
+// This class generates an JSON output file.
+class JsonUnitTestResultPrinter : public EmptyTestEventListener {
+ public:
+  explicit JsonUnitTestResultPrinter(const char* output_file);
+
+  virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+
+  // Prints an JSON summary of all unit tests.
+  static void PrintJsonTestList(::std::ostream* stream,
+                                const std::vector<TestCase*>& test_cases);
+
+ private:
+  // Returns an JSON-escaped copy of the input string str.
+  static std::string EscapeJson(const std::string& str);
+
+  //// Verifies that the given attribute belongs to the given element and
+  //// streams the attribute as JSON.
+  static void OutputJsonKey(std::ostream* stream,
+                            const std::string& element_name,
+                            const std::string& name,
+                            const std::string& value,
+                            const std::string& indent,
+                            bool comma = true);
+  static void OutputJsonKey(std::ostream* stream,
+                            const std::string& element_name,
+                            const std::string& name,
+                            int value,
+                            const std::string& indent,
+                            bool comma = true);
+
+  // Streams a JSON representation of a TestInfo object.
+  static void OutputJsonTestInfo(::std::ostream* stream,
+                                 const char* test_case_name,
+                                 const TestInfo& test_info);
+
+  // Prints a JSON representation of a TestCase object
+  static void PrintJsonTestCase(::std::ostream* stream,
+                                const TestCase& test_case);
+
+  // Prints a JSON summary of unit_test to output stream out.
+  static void PrintJsonUnitTest(::std::ostream* stream,
+                                const UnitTest& unit_test);
+
+  // Produces a string representing the test properties in a result as
+  // a JSON dictionary.
+  static std::string TestPropertiesAsJson(const TestResult& result,
+                                          const std::string& indent);
+
+  // The output file.
+  const std::string output_file_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(JsonUnitTestResultPrinter);
+};
+
+// Creates a new JsonUnitTestResultPrinter.
+JsonUnitTestResultPrinter::JsonUnitTestResultPrinter(const char* output_file)
+    : output_file_(output_file) {
+  if (output_file_.empty()) {
+    GTEST_LOG_(FATAL) << "JSON output file may not be null";
+  }
+}
+
+void JsonUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
+                                                  int /*iteration*/) {
+  FILE* jsonout = OpenFileForWriting(output_file_);
+  std::stringstream stream;
+  PrintJsonUnitTest(&stream, unit_test);
+  fprintf(jsonout, "%s", StringStreamToString(&stream).c_str());
+  fclose(jsonout);
+}
+
+// Returns an JSON-escaped copy of the input string str.
+std::string JsonUnitTestResultPrinter::EscapeJson(const std::string& str) {
+  Message m;
+
+  for (size_t i = 0; i < str.size(); ++i) {
+    const char ch = str[i];
+    switch (ch) {
+      case '\\':
+      case '"':
+      case '/':
+        m << '\\' << ch;
+        break;
+      case '\b':
+        m << "\\b";
+        break;
+      case '\t':
+        m << "\\t";
+        break;
+      case '\n':
+        m << "\\n";
+        break;
+      case '\f':
+        m << "\\f";
+        break;
+      case '\r':
+        m << "\\r";
+        break;
+      default:
+        if (ch < ' ') {
+          m << "\\u00" << String::FormatByte(static_cast<unsigned char>(ch));
+        } else {
+          m << ch;
+        }
+        break;
+    }
+  }
+
+  return m.GetString();
+}
+
+// The following routines generate an JSON representation of a UnitTest
+// object.
+
+// Formats the given time in milliseconds as seconds.
+static std::string FormatTimeInMillisAsDuration(TimeInMillis ms) {
+  ::std::stringstream ss;
+  ss << (static_cast<double>(ms) * 1e-3) << "s";
+  return ss.str();
+}
+
+// Converts the given epoch time in milliseconds to a date string in the
+// RFC3339 format, without the timezone information.
+static std::string FormatEpochTimeInMillisAsRFC3339(TimeInMillis ms) {
+  struct tm time_struct;
+  if (!PortableLocaltime(static_cast<time_t>(ms / 1000), &time_struct))
+    return "";
+  // YYYY-MM-DDThh:mm:ss
+  return StreamableToString(time_struct.tm_year + 1900) + "-" +
+      String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" +
+      String::FormatIntWidth2(time_struct.tm_mday) + "T" +
+      String::FormatIntWidth2(time_struct.tm_hour) + ":" +
+      String::FormatIntWidth2(time_struct.tm_min) + ":" +
+      String::FormatIntWidth2(time_struct.tm_sec) + "Z";
+}
+
+static inline std::string Indent(int width) {
+  return std::string(width, ' ');
+}
+
+void JsonUnitTestResultPrinter::OutputJsonKey(
+    std::ostream* stream,
+    const std::string& element_name,
+    const std::string& name,
+    const std::string& value,
+    const std::string& indent,
+    bool comma) {
+  const std::vector<std::string>& allowed_names =
+      GetReservedAttributesForElement(element_name);
+
+  GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) !=
+                   allowed_names.end())
+      << "Key \"" << name << "\" is not allowed for value \"" << element_name
+      << "\".";
+
+  *stream << indent << "\"" << name << "\": \"" << EscapeJson(value) << "\"";
+  if (comma)
+    *stream << ",\n";
+}
+
+void JsonUnitTestResultPrinter::OutputJsonKey(
+    std::ostream* stream,
+    const std::string& element_name,
+    const std::string& name,
+    int value,
+    const std::string& indent,
+    bool comma) {
+  const std::vector<std::string>& allowed_names =
+      GetReservedAttributesForElement(element_name);
+
+  GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) !=
+                   allowed_names.end())
+      << "Key \"" << name << "\" is not allowed for value \"" << element_name
+      << "\".";
+
+  *stream << indent << "\"" << name << "\": " << StreamableToString(value);
+  if (comma)
+    *stream << ",\n";
+}
+
+// Prints a JSON representation of a TestInfo object.
+void JsonUnitTestResultPrinter::OutputJsonTestInfo(::std::ostream* stream,
+                                                   const char* test_case_name,
+                                                   const TestInfo& test_info) {
+  const TestResult& result = *test_info.result();
+  const std::string kTestcase = "testcase";
+  const std::string kIndent = Indent(10);
+
+  *stream << Indent(8) << "{\n";
+  OutputJsonKey(stream, kTestcase, "name", test_info.name(), kIndent);
+
+  if (test_info.value_param() != NULL) {
+    OutputJsonKey(stream, kTestcase, "value_param",
+                  test_info.value_param(), kIndent);
+  }
+  if (test_info.type_param() != NULL) {
+    OutputJsonKey(stream, kTestcase, "type_param", test_info.type_param(),
+                  kIndent);
+  }
+  if (GTEST_FLAG(list_tests)) {
+    OutputJsonKey(stream, kTestcase, "file", test_info.file(), kIndent);
+    OutputJsonKey(stream, kTestcase, "line", test_info.line(), kIndent, false);
+    *stream << "\n" << Indent(8) << "}";
+    return;
+  }
+
+  OutputJsonKey(stream, kTestcase, "status",
+                test_info.should_run() ? "RUN" : "NOTRUN", kIndent);
+  OutputJsonKey(stream, kTestcase, "time",
+                FormatTimeInMillisAsDuration(result.elapsed_time()), kIndent);
+  OutputJsonKey(stream, kTestcase, "classname", test_case_name, kIndent, false);
+  *stream << TestPropertiesAsJson(result, kIndent);
+
+  int failures = 0;
+  for (int i = 0; i < result.total_part_count(); ++i) {
+    const TestPartResult& part = result.GetTestPartResult(i);
+    if (part.failed()) {
+      *stream << ",\n";
+      if (++failures == 1) {
+        *stream << kIndent << "\"" << "failures" << "\": [\n";
+      }
+      const std::string location =
+          internal::FormatCompilerIndependentFileLocation(part.file_name(),
+                                                          part.line_number());
+      const std::string message = EscapeJson(location + "\n" + part.message());
+      *stream << kIndent << "  {\n"
+              << kIndent << "    \"failure\": \"" << message << "\",\n"
+              << kIndent << "    \"type\": \"\"\n"
+              << kIndent << "  }";
+    }
+  }
+
+  if (failures > 0)
+    *stream << "\n" << kIndent << "]";
+  *stream << "\n" << Indent(8) << "}";
+}
+
+// Prints an JSON representation of a TestCase object
+void JsonUnitTestResultPrinter::PrintJsonTestCase(std::ostream* stream,
+                                                  const TestCase& test_case) {
+  const std::string kTestsuite = "testsuite";
+  const std::string kIndent = Indent(6);
+
+  *stream << Indent(4) << "{\n";
+  OutputJsonKey(stream, kTestsuite, "name", test_case.name(), kIndent);
+  OutputJsonKey(stream, kTestsuite, "tests", test_case.reportable_test_count(),
+                kIndent);
+  if (!GTEST_FLAG(list_tests)) {
+    OutputJsonKey(stream, kTestsuite, "failures", test_case.failed_test_count(),
+                  kIndent);
+    OutputJsonKey(stream, kTestsuite, "disabled",
+                  test_case.reportable_disabled_test_count(), kIndent);
+    OutputJsonKey(stream, kTestsuite, "errors", 0, kIndent);
+    OutputJsonKey(stream, kTestsuite, "time",
+                  FormatTimeInMillisAsDuration(test_case.elapsed_time()),
+                  kIndent, false);
+    *stream << TestPropertiesAsJson(test_case.ad_hoc_test_result(), kIndent)
+            << ",\n";
+  }
+
+  *stream << kIndent << "\"" << kTestsuite << "\": [\n";
+
+  bool comma = false;
+  for (int i = 0; i < test_case.total_test_count(); ++i) {
+    if (test_case.GetTestInfo(i)->is_reportable()) {
+      if (comma) {
+        *stream << ",\n";
+      } else {
+        comma = true;
+      }
+      OutputJsonTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i));
+    }
+  }
+  *stream << "\n" << kIndent << "]\n" << Indent(4) << "}";
+}
+
+// Prints a JSON summary of unit_test to output stream out.
+void JsonUnitTestResultPrinter::PrintJsonUnitTest(std::ostream* stream,
+                                                  const UnitTest& unit_test) {
+  const std::string kTestsuites = "testsuites";
+  const std::string kIndent = Indent(2);
+  *stream << "{\n";
+
+  OutputJsonKey(stream, kTestsuites, "tests", unit_test.reportable_test_count(),
+                kIndent);
+  OutputJsonKey(stream, kTestsuites, "failures", unit_test.failed_test_count(),
+                kIndent);
+  OutputJsonKey(stream, kTestsuites, "disabled",
+                unit_test.reportable_disabled_test_count(), kIndent);
+  OutputJsonKey(stream, kTestsuites, "errors", 0, kIndent);
+  if (GTEST_FLAG(shuffle)) {
+    OutputJsonKey(stream, kTestsuites, "random_seed", unit_test.random_seed(),
+                  kIndent);
+  }
+  OutputJsonKey(stream, kTestsuites, "timestamp",
+                FormatEpochTimeInMillisAsRFC3339(unit_test.start_timestamp()),
+                kIndent);
+  OutputJsonKey(stream, kTestsuites, "time",
+                FormatTimeInMillisAsDuration(unit_test.elapsed_time()), kIndent,
+                false);
+
+  *stream << TestPropertiesAsJson(unit_test.ad_hoc_test_result(), kIndent)
+          << ",\n";
+
+  OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent);
+  *stream << kIndent << "\"" << kTestsuites << "\": [\n";
+
+  bool comma = false;
+  for (int i = 0; i < unit_test.total_test_case_count(); ++i) {
+    if (unit_test.GetTestCase(i)->reportable_test_count() > 0) {
+      if (comma) {
+        *stream << ",\n";
+      } else {
+        comma = true;
+      }
+      PrintJsonTestCase(stream, *unit_test.GetTestCase(i));
+    }
+  }
+
+  *stream << "\n" << kIndent << "]\n" << "}\n";
+}
+
+void JsonUnitTestResultPrinter::PrintJsonTestList(
+    std::ostream* stream, const std::vector<TestCase*>& test_cases) {
+  const std::string kTestsuites = "testsuites";
+  const std::string kIndent = Indent(2);
+  *stream << "{\n";
+  int total_tests = 0;
+  for (size_t i = 0; i < test_cases.size(); ++i) {
+    total_tests += test_cases[i]->total_test_count();
+  }
+  OutputJsonKey(stream, kTestsuites, "tests", total_tests, kIndent);
+
+  OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent);
+  *stream << kIndent << "\"" << kTestsuites << "\": [\n";
+
+  for (size_t i = 0; i < test_cases.size(); ++i) {
+    if (i != 0) {
+      *stream << ",\n";
+    }
+    PrintJsonTestCase(stream, *test_cases[i]);
+  }
+
+  *stream << "\n"
+          << kIndent << "]\n"
+          << "}\n";
+}
+// Produces a string representing the test properties in a result as
+// a JSON dictionary.
+std::string JsonUnitTestResultPrinter::TestPropertiesAsJson(
+    const TestResult& result, const std::string& indent) {
+  Message attributes;
+  for (int i = 0; i < result.test_property_count(); ++i) {
+    const TestProperty& property = result.GetTestProperty(i);
+    attributes << ",\n" << indent << "\"" << property.key() << "\": "
+               << "\"" << EscapeJson(property.value()) << "\"";
+  }
+  return attributes.GetString();
+}
+
+// End JsonUnitTestResultPrinter
+
 #if GTEST_CAN_STREAM_RESULTS_
 
 // Checks if str contains '=', '&', '%' or '\n' characters. If yes,
@@ -3758,8 +4242,8 @@ std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes(
 // example, replaces "=" with "%3D".  This algorithm is O(strlen(str))
 // in both time and space -- important as the input str may contain an
 // arbitrarily long test failure message and stack trace.
-string StreamingListener::UrlEncode(const char* str) {
-  string result;
+std::string StreamingListener::UrlEncode(const char* str) {
+  std::string result;
   result.reserve(strlen(str) + 1);
   for (char ch = *str; ch != '\0'; ch = *++str) {
     switch (ch) {
@@ -3821,47 +4305,82 @@ void StreamingListener::SocketWriter::MakeConnection() {
 // End of class Streaming Listener
 #endif  // GTEST_CAN_STREAM_RESULTS__
 
-// Class ScopedTrace
+// class OsStackTraceGetter
 
-// Pushes the given source file location and message onto a per-thread
-// trace stack maintained by Google Test.
-ScopedTrace::ScopedTrace(const char* file, int line, const Message& message)
-    GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) {
-  TraceInfo trace;
-  trace.file = file;
-  trace.line = line;
-  trace.message = message.GetString();
+const char* const OsStackTraceGetterInterface::kElidedFramesMarker =
+    "... " GTEST_NAME_ " internal frames ...";
 
-  UnitTest::GetInstance()->PushGTestTrace(trace);
-}
+std::string OsStackTraceGetter::CurrentStackTrace(int max_depth, int skip_count)
+    GTEST_LOCK_EXCLUDED_(mutex_) {
+#if GTEST_HAS_ABSL
+  std::string result;
 
-// Pops the info pushed by the c'tor.
-ScopedTrace::~ScopedTrace()
-    GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) {
-  UnitTest::GetInstance()->PopGTestTrace();
-}
+  if (max_depth <= 0) {
+    return result;
+  }
 
+  max_depth = std::min(max_depth, kMaxStackTraceDepth);
 
-// class OsStackTraceGetter
+  std::vector<void*> raw_stack(max_depth);
+  // Skips the frames requested by the caller, plus this function.
+  const int raw_stack_size =
+      absl::GetStackTrace(&raw_stack[0], max_depth, skip_count + 1);
 
-const char* const OsStackTraceGetterInterface::kElidedFramesMarker =
-    "... " GTEST_NAME_ " internal frames ...";
+  void* caller_frame = nullptr;
+  {
+    MutexLock lock(&mutex_);
+    caller_frame = caller_frame_;
+  }
+
+  for (int i = 0; i < raw_stack_size; ++i) {
+    if (raw_stack[i] == caller_frame &&
+        !GTEST_FLAG(show_internal_stack_frames)) {
+      // Add a marker to the trace and stop adding frames.
+      absl::StrAppend(&result, kElidedFramesMarker, "\n");
+      break;
+    }
 
-string OsStackTraceGetter::CurrentStackTrace(int /*max_depth*/,
-                                             int /*skip_count*/) {
+    char tmp[1024];
+    const char* symbol = "(unknown)";
+    if (absl::Symbolize(raw_stack[i], tmp, sizeof(tmp))) {
+      symbol = tmp;
+    }
+
+    char line[1024];
+    snprintf(line, sizeof(line), "  %p: %s\n", raw_stack[i], symbol);
+    result += line;
+  }
+
+  return result;
+
+#else  // !GTEST_HAS_ABSL
+  static_cast<void>(max_depth);
+  static_cast<void>(skip_count);
   return "";
+#endif  // GTEST_HAS_ABSL
 }
 
-void OsStackTraceGetter::UponLeavingGTest() {}
+void OsStackTraceGetter::UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_) {
+#if GTEST_HAS_ABSL
+  void* caller_frame = nullptr;
+  if (absl::GetStackTrace(&caller_frame, 1, 3) <= 0) {
+    caller_frame = nullptr;
+  }
+
+  MutexLock lock(&mutex_);
+  caller_frame_ = caller_frame;
+#endif  // GTEST_HAS_ABSL
+}
 
 // A helper class that creates the premature-exit file in its
 // constructor and deletes the file in its destructor.
 class ScopedPrematureExitFile {
  public:
   explicit ScopedPrematureExitFile(const char* premature_exit_filepath)
-      : premature_exit_filepath_(premature_exit_filepath) {
+      : premature_exit_filepath_(premature_exit_filepath ?
+                                 premature_exit_filepath : "") {
     // If a path to the premature-exit file is specified...
-    if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') {
+    if (!premature_exit_filepath_.empty()) {
       // create the file with a single "0" character in it.  I/O
       // errors are ignored as there's nothing better we can do and we
       // don't want to fail the test because of this.
@@ -3872,13 +4391,18 @@ class ScopedPrematureExitFile {
   }
 
   ~ScopedPrematureExitFile() {
-    if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') {
-      remove(premature_exit_filepath_);
+    if (!premature_exit_filepath_.empty()) {
+      int retval = remove(premature_exit_filepath_.c_str());
+      if (retval) {
+        GTEST_LOG_(ERROR) << "Failed to remove premature exit filepath \""
+                          << premature_exit_filepath_ << "\" with error "
+                          << retval;
+      }
     }
   }
 
  private:
-  const char* const premature_exit_filepath_;
+  const std::string premature_exit_filepath_;
 
   GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile);
 };
@@ -4148,6 +4672,11 @@ void UnitTest::AddTestPartResult(
       // when a failure happens and both the --gtest_break_on_failure and
       // the --gtest_catch_exceptions flags are specified.
       DebugBreak();
+#elif (!defined(__native_client__)) &&            \
+    ((defined(__clang__) || defined(__GNUC__)) && \
+     (defined(__x86_64__) || defined(__i386__)))
+      // with clang/gcc we can achieve the same effect on x86 by invoking int3
+      asm("int3");
 #else
       // Dereference NULL through a volatile pointer to prevent the compiler
       // from removing. We use this rather than abort() or __builtin_trap() for
@@ -4215,7 +4744,7 @@ int UnitTest::Run() {
   // used for the duration of the program.
   impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
 
-#if GTEST_HAS_SEH
+#if GTEST_OS_WINDOWS
   // Either the user wants Google Test to catch exceptions thrown by the
   // tests or this is executing in the context of death test child
   // process. In either case the user does not want to see pop-up dialogs
@@ -4244,7 +4773,7 @@ int UnitTest::Run() {
     // VC++ doesn't define _set_abort_behavior() prior to the version 8.0.
     // Users of prior VC versions shall suffer the agony and pain of
     // clicking through the countless debug dialogs.
-    // TODO(vladl@google.com): find a way to suppress the abort dialog() in the
+    // FIXME: find a way to suppress the abort dialog() in the
     // debug mode when compiled with VC 7.1 or lower.
     if (!GTEST_FLAG(break_on_failure))
       _set_abort_behavior(
@@ -4252,7 +4781,7 @@ int UnitTest::Run() {
           _WRITE_ABORT_MSG | _CALL_REPORTFAULT);  // pop-up window, core dump.
 # endif
   }
-#endif  // GTEST_HAS_SEH
+#endif  // GTEST_OS_WINDOWS
 
   return internal::HandleExceptionsInMethodIfSupported(
       impl(),
@@ -4285,7 +4814,6 @@ const TestInfo* UnitTest::current_test_info() const
 // Returns the random seed used at the start of the current test run.
 int UnitTest::random_seed() const { return impl_->random_seed(); }
 
-#if GTEST_HAS_PARAM_TEST
 // Returns ParameterizedTestCaseRegistry object used to keep track of
 // value-parameterized tests and instantiate and register them.
 internal::ParameterizedTestCaseRegistry&
@@ -4293,7 +4821,6 @@ internal::ParameterizedTestCaseRegistry&
         GTEST_LOCK_EXCLUDED_(mutex_) {
   return impl_->parameterized_test_registry();
 }
-#endif  // GTEST_HAS_PARAM_TEST
 
 // Creates an empty UnitTest.
 UnitTest::UnitTest() {
@@ -4332,10 +4859,8 @@ UnitTestImpl::UnitTestImpl(UnitTest* parent)
           &default_global_test_part_result_reporter_),
       per_thread_test_part_result_reporter_(
           &default_per_thread_test_part_result_reporter_),
-#if GTEST_HAS_PARAM_TEST
       parameterized_test_registry_(),
       parameterized_tests_registered_(false),
-#endif  // GTEST_HAS_PARAM_TEST
       last_death_test_case_(-1),
       current_test_case_(NULL),
       current_test_info_(NULL),
@@ -4402,10 +4927,12 @@ void UnitTestImpl::ConfigureXmlOutput() {
   if (output_format == "xml") {
     listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter(
         UnitTestOptions::GetAbsolutePathToOutputFile().c_str()));
+  } else if (output_format == "json") {
+    listeners()->SetDefaultXmlGenerator(new JsonUnitTestResultPrinter(
+        UnitTestOptions::GetAbsolutePathToOutputFile().c_str()));
   } else if (output_format != "") {
-    printf("WARNING: unrecognized output format \"%s\" ignored.\n",
-           output_format.c_str());
-    fflush(stdout);
+    GTEST_LOG_(WARNING) << "WARNING: unrecognized output format \""
+                        << output_format << "\" ignored.";
   }
 }
 
@@ -4420,9 +4947,8 @@ void UnitTestImpl::ConfigureStreamingOutput() {
       listeners()->Append(new StreamingListener(target.substr(0, pos),
                                                 target.substr(pos+1)));
     } else {
-      printf("WARNING: unrecognized streaming target \"%s\" ignored.\n",
-             target.c_str());
-      fflush(stdout);
+      GTEST_LOG_(WARNING) << "unrecognized streaming target \"" << target
+                          << "\" ignored.";
     }
   }
 }
@@ -4461,6 +4987,13 @@ void UnitTestImpl::PostFlagParsingInit() {
     // Configures listeners for streaming test results to the specified server.
     ConfigureStreamingOutput();
 #endif  // GTEST_CAN_STREAM_RESULTS_
+
+#if GTEST_HAS_ABSL
+    if (GTEST_FLAG(install_failure_signal_handler)) {
+      absl::FailureSignalHandlerOptions options;
+      absl::InstallFailureSignalHandler(options);
+    }
+#endif  // GTEST_HAS_ABSL
   }
 }
 
@@ -4504,11 +5037,11 @@ TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,
                                     Test::SetUpTestCaseFunc set_up_tc,
                                     Test::TearDownTestCaseFunc tear_down_tc) {
   // Can we find a TestCase with the given name?
-  const std::vector<TestCase*>::const_iterator test_case =
-      std::find_if(test_cases_.begin(), test_cases_.end(),
+  const std::vector<TestCase*>::const_reverse_iterator test_case =
+      std::find_if(test_cases_.rbegin(), test_cases_.rend(),
                    TestCaseNameIs(test_case_name));
 
-  if (test_case != test_cases_.end())
+  if (test_case != test_cases_.rend())
     return *test_case;
 
   // No.  Let's create one.
@@ -4549,13 +5082,8 @@ static void TearDownEnvironment(Environment* env) { env->TearDown(); }
 // All other functions called from RunAllTests() may safely assume that
 // parameterized tests are ready to be counted and run.
 bool UnitTestImpl::RunAllTests() {
-  // Makes sure InitGoogleTest() was called.
-  if (!GTestIsInitialized()) {
-    printf("%s",
-           "\nThis test program did NOT call ::testing::InitGoogleTest "
-           "before calling RUN_ALL_TESTS().  Please fix it.\n");
-    return false;
-  }
+  // True iff Google Test is initialized before RUN_ALL_TESTS() is called.
+  const bool gtest_is_initialized_before_run_all_tests = GTestIsInitialized();
 
   // Do not run any test if the --help flag was specified.
   if (g_help_flag)
@@ -4683,6 +5211,20 @@ bool UnitTestImpl::RunAllTests() {
 
   repeater->OnTestProgramEnd(*parent_);
 
+  if (!gtest_is_initialized_before_run_all_tests) {
+    ColoredPrintf(
+        COLOR_RED,
+        "\nIMPORTANT NOTICE - DO NOT IGNORE:\n"
+        "This test program did NOT call " GTEST_INIT_GOOGLE_TEST_NAME_
+        "() before calling RUN_ALL_TESTS(). This is INVALID. Soon " GTEST_NAME_
+        " will start to enforce the valid usage. "
+        "Please fix it ASAP, or IT WILL START TO FAIL.\n");  // NOLINT
+#if GTEST_FOR_GOOGLE_
+    ColoredPrintf(COLOR_RED,
+                  "For more details, see http://wiki/Main/ValidGUnitMain.\n");
+#endif  // GTEST_FOR_GOOGLE_
+  }
+
   return !failed;
 }
 
@@ -4784,8 +5326,8 @@ bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) {
 // each TestCase and TestInfo object.
 // If shard_tests == true, further filters tests based on sharding
 // variables in the environment - see
-// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide.
-// Returns the number of tests that should run.
+// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md
+// Returns the number of tests that should run.
 int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
   const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ?
       Int32FromEnvOrDie(kTestTotalShards, -1) : -1;
@@ -4824,10 +5366,11 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
           (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) &&
           matches_filter;
 
-      const bool is_selected = is_runnable &&
-          (shard_tests == IGNORE_SHARDING_PROTOCOL ||
-           ShouldRunTestOnShard(total_shards, shard_index,
-                                num_runnable_tests));
+      const bool is_in_another_shard =
+          shard_tests != IGNORE_SHARDING_PROTOCOL &&
+          !ShouldRunTestOnShard(total_shards, shard_index, num_runnable_tests);
+      test_info->is_in_another_shard_ = is_in_another_shard;
+      const bool is_selected = is_runnable && !is_in_another_shard;
 
       num_runnable_tests += is_runnable;
       num_selected_tests += is_selected;
@@ -4897,6 +5440,23 @@ void UnitTestImpl::ListTestsMatchingFilter() {
     }
   }
   fflush(stdout);
+  const std::string& output_format = UnitTestOptions::GetOutputFormat();
+  if (output_format == "xml" || output_format == "json") {
+    FILE* fileout = OpenFileForWriting(
+        UnitTestOptions::GetAbsolutePathToOutputFile().c_str());
+    std::stringstream stream;
+    if (output_format == "xml") {
+      XmlUnitTestResultPrinter(
+          UnitTestOptions::GetAbsolutePathToOutputFile().c_str())
+          .PrintXmlTestsList(&stream, test_cases_);
+    } else if (output_format == "json") {
+      JsonUnitTestResultPrinter(
+          UnitTestOptions::GetAbsolutePathToOutputFile().c_str())
+          .PrintJsonTestList(&stream, test_cases_);
+    }
+    fprintf(fileout, "%s", StringStreamToString(&stream).c_str());
+    fclose(fileout);
+  }
 }
 
 // Sets the OS stack trace getter.
@@ -4927,11 +5487,15 @@ OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() {
   return os_stack_trace_getter_;
 }
 
-// Returns the TestResult for the test that's currently running, or
-// the TestResult for the ad hoc test if no test is running.
+// Returns the most specific TestResult currently running.
 TestResult* UnitTestImpl::current_test_result() {
-  return current_test_info_ ?
-      &(current_test_info_->result_) : &ad_hoc_test_result_;
+  if (current_test_info_ != NULL) {
+    return &current_test_info_->result_;
+  }
+  if (current_test_case_ != NULL) {
+    return &current_test_case_->ad_hoc_test_result_;
+  }
+  return &ad_hoc_test_result_;
 }
 
 // Shuffles all test cases, and the tests within each test case,
@@ -5012,9 +5576,8 @@ bool SkipPrefix(const char* prefix, const char** pstr) {
 // part can be omitted.
 //
 // Returns the value of the flag, or NULL if the parsing failed.
-const char* ParseFlagValue(const char* str,
-                           const char* flag,
-                           bool def_optional) {
+static const char* ParseFlagValue(const char* str, const char* flag,
+                                  bool def_optional) {
   // str and flag must not be NULL.
   if (str == NULL || flag == NULL) return NULL;
 
@@ -5050,7 +5613,7 @@ const char* ParseFlagValue(const char* str,
 //
 // On success, stores the value of the flag in *value, and returns
 // true.  On failure, returns false without changing *value.
-bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
+static bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
   // Gets the value of the flag as a string.
   const char* const value_str = ParseFlagValue(str, flag, true);
 
@@ -5084,7 +5647,8 @@ bool ParseInt32Flag(const char* str, const char* flag, Int32* value) {
 //
 // On success, stores the value of the flag in *value, and returns
 // true.  On failure, returns false without changing *value.
-bool ParseStringFlag(const char* str, const char* flag, std::string* value) {
+template <typename String>
+static bool ParseStringFlag(const char* str, const char* flag, String* value) {
   // Gets the value of the flag as a string.
   const char* const value_str = ParseFlagValue(str, flag, false);
 
@@ -5120,7 +5684,7 @@ static bool HasGoogleTestFlagPrefix(const char* str) {
 //   @Y    changes the color to yellow.
 //   @D    changes to the default terminal text color.
 //
-// TODO(wan@google.com): Write tests for this once we add stdout
+// FIXME: Write tests for this once we add stdout
 // capturing to Google Test.
 static void PrintColorEncoded(const char* str) {
   GTestColor color = COLOR_DEFAULT;  // The current color.
@@ -5186,24 +5750,25 @@ static const char kColorEncodedHelpMessage[] =
 "      Enable/disable colored output. The default is @Gauto@D.\n"
 "  -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n"
 "      Don't print the elapsed time of each test.\n"
-"  @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G"
+"  @G--" GTEST_FLAG_PREFIX_ "output=@Y(@Gjson@Y|@Gxml@Y)[@G:@YDIRECTORY_PATH@G"
     GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n"
-"      Generate an XML report in the given directory or with the given file\n"
-"      name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n"
-#if GTEST_CAN_STREAM_RESULTS_
+"      Generate a JSON or XML report in the given directory or with the given\n"
+"      file name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n"
+# if GTEST_CAN_STREAM_RESULTS_
 "  @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n"
 "      Stream test results to the given server.\n"
-#endif  // GTEST_CAN_STREAM_RESULTS_
+# endif  // GTEST_CAN_STREAM_RESULTS_
 "\n"
 "Assertion Behavior:\n"
-#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
+# if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
 "  @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n"
 "      Set the default death test style.\n"
-#endif  // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
+# endif  // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
 "  @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n"
 "      Turn assertion failures into debugger break-points.\n"
 "  @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n"
-"      Turn assertion failures into C++ exceptions.\n"
+"      Turn assertion failures into C++ exceptions for use by an external\n"
+"      test framework.\n"
 "  @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n"
 "      Do not report exceptions as test failures. Instead, allow them\n"
 "      to crash the program or throw a pop-up (on Windows).\n"
@@ -5220,7 +5785,7 @@ static const char kColorEncodedHelpMessage[] =
 "(not one in your own code or tests), please report it to\n"
 "@G<" GTEST_DEV_EMAIL_ ">@D.\n";
 
-bool ParseGoogleTestFlag(const char* const arg) {
+static bool ParseGoogleTestFlag(const char* const arg) {
   return ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag,
                        &GTEST_FLAG(also_run_disabled_tests)) ||
       ParseBoolFlag(arg, kBreakOnFailureFlag,
@@ -5238,6 +5803,7 @@ bool ParseGoogleTestFlag(const char* const arg) {
       ParseBoolFlag(arg, kListTestsFlag, &GTEST_FLAG(list_tests)) ||
       ParseStringFlag(arg, kOutputFlag, &GTEST_FLAG(output)) ||
       ParseBoolFlag(arg, kPrintTimeFlag, &GTEST_FLAG(print_time)) ||
+      ParseBoolFlag(arg, kPrintUTF8Flag, &GTEST_FLAG(print_utf8)) ||
       ParseInt32Flag(arg, kRandomSeedFlag, &GTEST_FLAG(random_seed)) ||
       ParseInt32Flag(arg, kRepeatFlag, &GTEST_FLAG(repeat)) ||
       ParseBoolFlag(arg, kShuffleFlag, &GTEST_FLAG(shuffle)) ||
@@ -5250,14 +5816,11 @@ bool ParseGoogleTestFlag(const char* const arg) {
 }
 
 #if GTEST_USE_OWN_FLAGFILE_FLAG_
-void LoadFlagsFromFile(const std::string& path) {
+static void LoadFlagsFromFile(const std::string& path) {
   FILE* flagfile = posix::FOpen(path.c_str(), "r");
   if (!flagfile) {
-    fprintf(stderr,
-            "Unable to open file \"%s\"\n",
-            GTEST_FLAG(flagfile).c_str());
-    fflush(stderr);
-    exit(EXIT_FAILURE);
+    GTEST_LOG_(FATAL) << "Unable to open file \"" << GTEST_FLAG(flagfile)
+                      << "\"";
   }
   std::string contents(ReadEntireFile(flagfile));
   posix::FClose(flagfile);
@@ -5331,6 +5894,17 @@ void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) {
 // other parts of Google Test.
 void ParseGoogleTestFlagsOnly(int* argc, char** argv) {
   ParseGoogleTestFlagsOnlyImpl(argc, argv);
+
+  // Fix the value of *_NSGetArgc() on macOS, but iff
+  // *_NSGetArgv() == argv
+  // Only applicable to char** version of argv
+#if GTEST_OS_MAC
+#ifndef GTEST_OS_IOS
+  if (*_NSGetArgv() == argv) {
+    *_NSGetArgc() = *argc;
+  }
+#endif
+#endif
 }
 void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) {
   ParseGoogleTestFlagsOnlyImpl(argc, argv);
@@ -5352,6 +5926,10 @@ void InitGoogleTestImpl(int* argc, CharType** argv) {
     g_argvs.push_back(StreamableToString(argv[i]));
   }
 
+#if GTEST_HAS_ABSL
+  absl::InitializeSymbolizer(g_argvs[0].c_str());
+#endif  // GTEST_HAS_ABSL
+
   ParseGoogleTestFlagsOnly(argc, argv);
   GetUnitTestImpl()->PostFlagParsingInit();
 }
@@ -5385,4 +5963,45 @@ void InitGoogleTest(int* argc, wchar_t** argv) {
 #endif  // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
 }
 
+std::string TempDir() {
+#if defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_)
+  return GTEST_CUSTOM_TEMPDIR_FUNCTION_();
+#endif
+
+#if GTEST_OS_WINDOWS_MOBILE
+  return "\\temp\\";
+#elif GTEST_OS_WINDOWS
+  const char* temp_dir = internal::posix::GetEnv("TEMP");
+  if (temp_dir == NULL || temp_dir[0] == '\0')
+    return "\\temp\\";
+  else if (temp_dir[strlen(temp_dir) - 1] == '\\')
+    return temp_dir;
+  else
+    return std::string(temp_dir) + "\\";
+#elif GTEST_OS_LINUX_ANDROID
+  return "/sdcard/";
+#else
+  return "/tmp/";
+#endif  // GTEST_OS_WINDOWS_MOBILE
+}
+
+// Class ScopedTrace
+
+// Pushes the given source file location and message onto a per-thread
+// trace stack maintained by Google Test.
+void ScopedTrace::PushTrace(const char* file, int line, std::string message) {
+  internal::TraceInfo trace;
+  trace.file = file;
+  trace.line = line;
+  trace.message.swap(message);
+
+  UnitTest::GetInstance()->PushGTestTrace(trace);
+}
+
+// Pops the info pushed by the c'tor.
+ScopedTrace::~ScopedTrace()
+    GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) {
+  UnitTest::GetInstance()->PopGTestTrace();
+}
+
 }  // namespace testing
index f3028225523306eb9648f94bcbe9e3a78016bb7e..2113f621e65430e5570dfb2015884a2d4f4cf0b3 100644 (file)
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <stdio.h>
-
 #include "gtest/gtest.h"
 
 GTEST_API_ int main(int argc, char **argv) {
-  printf("Running main() from gtest_main.cc\n");
+  printf("Running main() from %s\n", __FILE__);
   testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
 }
diff --git a/src/external/muparser/README b/src/external/muparser/README
new file mode 100644 (file)
index 0000000..62aaa83
--- /dev/null
@@ -0,0 +1,3 @@
+muparser files
+==============
+Used from https://github.com/beltoforion/muparser/releases/tag/v2.3.2.
diff --git a/src/external/muparser/muParser.cpp b/src/external/muparser/muParser.cpp
new file mode 100644 (file)
index 0000000..d134015
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "muParser.h"
+#include "muParserTemplateMagic.h"
+
+//--- Standard includes ------------------------------------------------------------------------
+#include <cmath>
+#include <algorithm>
+#include <numeric>
+
+using namespace std;
+
+/** \file
+       \brief Implementation of the standard floating point parser.
+*/
+
+
+
+/** \brief Namespace for mathematical applications. */
+namespace mu
+{
+       //---------------------------------------------------------------------------
+       /** \brief Default value recognition callback.
+               \param [in] a_szExpr Pointer to the expression
+               \param [in, out] a_iPos Pointer to an index storing the current position within the expression
+               \param [out] a_fVal Pointer where the value should be stored in case one is found.
+               \return 1 if a value was found 0 otherwise.
+       */
+       int Parser::IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal)
+       {
+               value_type fVal(0);
+
+               stringstream_type stream(a_szExpr);
+               stream.seekg(0);        // todo:  check if this really is necessary
+               stream.imbue(Parser::s_locale);
+               stream >> fVal;
+               stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading
+
+               if (iEnd == (stringstream_type::pos_type) - 1)
+                       return 0;
+
+               *a_iPos += (int)iEnd;
+               *a_fVal = fVal;
+               return 1;
+       }
+
+
+       //---------------------------------------------------------------------------
+       /** \brief Constructor.
+
+         Call ParserBase class constructor and trigger Function, Operator and Constant initialization.
+       */
+       Parser::Parser()
+               :ParserBase()
+       {
+               AddValIdent(IsVal);
+
+               InitCharSets();
+               InitFun();
+               InitConst();
+               InitOprt();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Define the character sets.
+               \sa DefineNameChars, DefineOprtChars, DefineInfixOprtChars
+
+         This function is used for initializing the default character sets that define
+         the characters to be useable in function and variable names and operators.
+       */
+       void Parser::InitCharSets()
+       {
+               DefineNameChars(_T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+               DefineOprtChars(_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}"));
+               DefineInfixOprtChars(_T("/+-*^?<>=#!$%&|~'_"));
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Initialize the default functions. */
+       void Parser::InitFun()
+       {
+               if (mu::TypeInfo<mu::value_type>::IsInteger())
+               {
+                       // When setting MUP_BASETYPE to an integer type
+                       // Place functions for dealing with integer values here
+                       // ...
+                       // ...
+                       // ...
+               }
+               else
+               {
+                       // trigonometric functions
+                       DefineFun(_T("sin"), MathImpl<value_type>::Sin);
+                       DefineFun(_T("cos"), MathImpl<value_type>::Cos);
+                       DefineFun(_T("tan"), MathImpl<value_type>::Tan);
+                       // arcus functions
+                       DefineFun(_T("asin"), MathImpl<value_type>::ASin);
+                       DefineFun(_T("acos"), MathImpl<value_type>::ACos);
+                       DefineFun(_T("atan"), MathImpl<value_type>::ATan);
+                       DefineFun(_T("atan2"), MathImpl<value_type>::ATan2);
+                       // hyperbolic functions
+                       DefineFun(_T("sinh"), MathImpl<value_type>::Sinh);
+                       DefineFun(_T("cosh"), MathImpl<value_type>::Cosh);
+                       DefineFun(_T("tanh"), MathImpl<value_type>::Tanh);
+                       // arcus hyperbolic functions
+                       DefineFun(_T("asinh"), MathImpl<value_type>::ASinh);
+                       DefineFun(_T("acosh"), MathImpl<value_type>::ACosh);
+                       DefineFun(_T("atanh"), MathImpl<value_type>::ATanh);
+                       // Logarithm functions
+                       DefineFun(_T("log2"), MathImpl<value_type>::Log2);
+                       DefineFun(_T("log10"), MathImpl<value_type>::Log10);
+                       DefineFun(_T("log"), MathImpl<value_type>::Log);
+                       DefineFun(_T("ln"), MathImpl<value_type>::Log);
+                       // misc
+                       DefineFun(_T("exp"), MathImpl<value_type>::Exp);
+                       DefineFun(_T("sqrt"), MathImpl<value_type>::Sqrt);
+                       DefineFun(_T("sign"), MathImpl<value_type>::Sign);
+                       DefineFun(_T("rint"), MathImpl<value_type>::Rint);
+                       DefineFun(_T("abs"), MathImpl<value_type>::Abs);
+                       // Functions with variable number of arguments
+                       DefineFun(_T("sum"), MathImpl<value_type>::Sum);
+                       DefineFun(_T("avg"), MathImpl<value_type>::Avg);
+                       DefineFun(_T("min"), MathImpl<value_type>::Min);
+                       DefineFun(_T("max"), MathImpl<value_type>::Max);
+               }
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Initialize constants.
+
+         By default the parser recognizes two constants. Pi ("pi") and the Eulerian
+         number ("_e").
+       */
+       void Parser::InitConst()
+       {
+               DefineConst(_T("_pi"), MathImpl<value_type>::CONST_PI);
+               DefineConst(_T("_e"), MathImpl<value_type>::CONST_E);
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Initialize operators.
+
+         By default only the unary minus operator is added.
+       */
+       void Parser::InitOprt()
+       {
+               DefineInfixOprt(_T("-"), MathImpl<value_type>::UnaryMinus);
+               DefineInfixOprt(_T("+"), MathImpl<value_type>::UnaryPlus);
+       }
+
+       //---------------------------------------------------------------------------
+       void Parser::OnDetectVar(string_type* /*pExpr*/, int& /*nStart*/, int& /*nEnd*/)
+       {
+               // this is just sample code to illustrate modifying variable names on the fly.
+               // I'm not sure anyone really needs such a feature...
+               /*
+
+
+               string sVar(pExpr->begin()+nStart, pExpr->begin()+nEnd);
+               string sRepl = std::string("_") + sVar + "_";
+
+               int nOrigVarEnd = nEnd;
+               cout << "variable detected!\n";
+               cout << "  Expr: " << *pExpr << "\n";
+               cout << "  Start: " << nStart << "\n";
+               cout << "  End: " << nEnd << "\n";
+               cout << "  Var: \"" << sVar << "\"\n";
+               cout << "  Repl: \"" << sRepl << "\"\n";
+               nEnd = nStart + sRepl.length();
+               cout << "  End: " << nEnd << "\n";
+               pExpr->replace(pExpr->begin()+nStart, pExpr->begin()+nOrigVarEnd, sRepl);
+               cout << "  New expr: " << *pExpr << "\n";
+               */
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Numerically differentiate with regard to a variable.
+               \param [in] a_Var Pointer to the differentiation variable.
+               \param [in] a_fPos Position at which the differentiation should take place.
+               \param [in] a_fEpsilon Epsilon used for the numerical differentiation.
+
+               Numerical differentiation uses a 5 point operator yielding a 4th order
+               formula. The default value for epsilon is 0.00074 which is
+               numeric_limits<double>::epsilon() ^ (1/5).
+       */
+       value_type Parser::Diff(value_type* a_Var, value_type  a_fPos, value_type  a_fEpsilon) const
+       {
+               value_type fRes(0);
+               value_type fBuf(*a_Var);
+               value_type f[4] = { 0,0,0,0 };
+               value_type fEpsilon(a_fEpsilon);
+
+               // Backwards compatible calculation of epsilon inc case the user doesn't provide
+               // his own epsilon
+               if (fEpsilon == 0)
+                       fEpsilon = (a_fPos == 0) ? (value_type)1e-10 : (value_type)1e-7 * a_fPos;
+
+               *a_Var = a_fPos + 2 * fEpsilon;  f[0] = Eval();
+               *a_Var = a_fPos + 1 * fEpsilon;  f[1] = Eval();
+               *a_Var = a_fPos - 1 * fEpsilon;  f[2] = Eval();
+               *a_Var = a_fPos - 2 * fEpsilon;  f[3] = Eval();
+               *a_Var = fBuf; // restore variable
+
+               fRes = (-f[0] + 8 * f[1] - 8 * f[2] + f[3]) / (12 * fEpsilon);
+               return fRes;
+       }
+} // namespace mu
diff --git a/src/external/muparser/muParser.h b/src/external/muparser/muParser.h
new file mode 100644 (file)
index 0000000..321fba9
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_H
+#define MU_PARSER_H
+
+//--- Standard includes ------------------------------------------------------------------------
+#include <vector>
+
+//--- Parser includes --------------------------------------------------------------------------
+#include "muParserBase.h"
+#include "muParserTemplateMagic.h"
+
+/** \file
+       \brief Definition of the standard floating point parser.
+*/
+
+namespace mu
+{
+       /** \brief Mathematical expressions parser.
+
+         Standard implementation of the mathematical expressions parser.
+         Can be used as a reference implementation for subclassing the parser.
+       */
+       class API_EXPORT_CXX Parser : public ParserBase
+       {
+       public:
+
+               Parser();
+
+               virtual void InitCharSets();
+               virtual void InitFun();
+               virtual void InitConst();
+               virtual void InitOprt();
+               virtual void OnDetectVar(string_type* pExpr, int& nStart, int& nEnd);
+
+               value_type Diff(value_type* a_Var, value_type a_fPos, value_type a_fEpsilon = 0) const;
+
+       protected:
+
+               static int IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal);
+       };
+} // namespace mu
+
+#endif
+
diff --git a/src/external/muparser/muParserBase.cpp b/src/external/muparser/muParserBase.cpp
new file mode 100644 (file)
index 0000000..4d94ebe
--- /dev/null
@@ -0,0 +1,1896 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "muParserBase.h"
+#include "muParserTemplateMagic.h"
+
+//--- Standard includes ------------------------------------------------------------------------
+#include <algorithm>
+#include <cmath>
+#include <memory>
+#include <vector>
+#include <deque>
+#include <sstream>
+#include <locale>
+#include <cassert>
+#include <cctype>
+
+#ifdef MUP_USE_OPENMP
+#include <omp.h>
+#endif
+
+#if defined(_MSC_VER)
+       #pragma warning(push)
+       #pragma warning(disable : 26812) 
+#endif
+
+using namespace std;
+
+/** \file
+       \brief This file contains the basic implementation of the muParser engine.
+*/
+
+namespace mu
+{
+       std::locale ParserBase::s_locale = std::locale(std::locale::classic(), new change_dec_sep<char_type>('.'));
+
+       bool ParserBase::g_DbgDumpCmdCode = false;
+       bool ParserBase::g_DbgDumpStack = false;
+
+       //------------------------------------------------------------------------------
+       /** \brief Identifiers for built in binary operators.
+
+               When defining custom binary operators with #AddOprt(...) make sure not to choose
+               names conflicting with these definitions.
+       */
+       const char_type* ParserBase::c_DefaultOprt[] =
+       {
+         _T("<="), _T(">="),  _T("!="),
+         _T("=="), _T("<"),   _T(">"),
+         _T("+"),  _T("-"),   _T("*"),
+         _T("/"),  _T("^"),   _T("&&"),
+         _T("||"), _T("="),   _T("("),
+         _T(")"),   _T("?"),  _T(":"), 0
+       };
+
+       const int ParserBase::s_MaxNumOpenMPThreads = 16;
+
+       //------------------------------------------------------------------------------
+       /** \brief Constructor.
+               \param a_szFormula the formula to interpret.
+               \throw ParserException if a_szFormula is nullptr.
+       */
+       ParserBase::ParserBase()
+               : m_pParseFormula(&ParserBase::ParseString)
+               , m_vRPN()
+               , m_vStringBuf()
+               , m_pTokenReader()
+               , m_FunDef()
+               , m_PostOprtDef()
+               , m_InfixOprtDef()
+               , m_OprtDef()
+               , m_ConstDef()
+               , m_StrVarDef()
+               , m_VarDef()
+               , m_bBuiltInOp(true)
+               , m_sNameChars()
+               , m_sOprtChars()
+               , m_sInfixOprtChars()
+               , m_vStackBuffer()
+               , m_nFinalResultIdx(0)
+       {
+               InitTokenReader();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Copy constructor.
+
+         The parser can be safely copy constructed but the bytecode is reset during
+         copy construction.
+       */
+       ParserBase::ParserBase(const ParserBase& a_Parser)
+               : m_pParseFormula(&ParserBase::ParseString)
+               , m_vRPN()
+               , m_vStringBuf()
+               , m_pTokenReader()
+               , m_FunDef()
+               , m_PostOprtDef()
+               , m_InfixOprtDef()
+               , m_OprtDef()
+               , m_ConstDef()
+               , m_StrVarDef()
+               , m_VarDef()
+               , m_bBuiltInOp(true)
+               , m_sNameChars()
+               , m_sOprtChars()
+               , m_sInfixOprtChars()
+       {
+               m_pTokenReader.reset(new token_reader_type(this));
+               Assign(a_Parser);
+       }
+
+       //---------------------------------------------------------------------------
+       ParserBase::~ParserBase()
+       {}
+
+       //---------------------------------------------------------------------------
+       /** \brief Assignment operator.
+
+         Implemented by calling Assign(a_Parser). Self assignment is suppressed.
+         \param a_Parser Object to copy to this.
+         \return *this
+         \throw nothrow
+       */
+       ParserBase& ParserBase::operator=(const ParserBase& a_Parser)
+       {
+               Assign(a_Parser);
+               return *this;
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Copy state of a parser object to this.
+
+         Clears Variables and Functions of this parser.
+         Copies the states of all internal variables.
+         Resets parse function to string parse mode.
+
+         \param a_Parser the source object.
+       */
+       void ParserBase::Assign(const ParserBase& a_Parser)
+       {
+               if (&a_Parser == this)
+                       return;
+
+               // Don't copy bytecode instead cause the parser to create new bytecode
+               // by resetting the parse function.
+               ReInit();
+
+               m_ConstDef = a_Parser.m_ConstDef;         // Copy user define constants
+               m_VarDef = a_Parser.m_VarDef;           // Copy user defined variables
+               m_bBuiltInOp = a_Parser.m_bBuiltInOp;
+               m_vStringBuf = a_Parser.m_vStringBuf;
+               m_vStackBuffer = a_Parser.m_vStackBuffer;
+               m_nFinalResultIdx = a_Parser.m_nFinalResultIdx;
+               m_StrVarDef = a_Parser.m_StrVarDef;
+               m_vStringVarBuf = a_Parser.m_vStringVarBuf;
+               m_pTokenReader.reset(a_Parser.m_pTokenReader->Clone(this));
+
+               // Copy function and operator callbacks
+               m_FunDef = a_Parser.m_FunDef;             // Copy function definitions
+               m_PostOprtDef = a_Parser.m_PostOprtDef;   // post value unary operators
+               m_InfixOprtDef = a_Parser.m_InfixOprtDef; // unary operators for infix notation
+               m_OprtDef = a_Parser.m_OprtDef;           // binary operators
+
+               m_sNameChars = a_Parser.m_sNameChars;
+               m_sOprtChars = a_Parser.m_sOprtChars;
+               m_sInfixOprtChars = a_Parser.m_sInfixOprtChars;
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Set the decimal separator.
+               \param cDecSep Decimal separator as a character value.
+               \sa SetThousandsSep
+
+               By default muParser uses the "C" locale. The decimal separator of this
+               locale is overwritten by the one provided here.
+       */
+       void ParserBase::SetDecSep(char_type cDecSep)
+       {
+               char_type cThousandsSep = std::use_facet< change_dec_sep<char_type> >(s_locale).thousands_sep();
+               s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>(cDecSep, cThousandsSep));
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Sets the thousands operator.
+               \param cThousandsSep The thousands separator as a character
+               \sa SetDecSep
+
+               By default muParser uses the "C" locale. The thousands separator of this
+               locale is overwritten by the one provided here.
+       */
+       void ParserBase::SetThousandsSep(char_type cThousandsSep)
+       {
+               char_type cDecSep = std::use_facet< change_dec_sep<char_type> >(s_locale).decimal_point();
+               s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>(cDecSep, cThousandsSep));
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Resets the locale.
+
+         The default locale used "." as decimal separator, no thousands separator and
+         "," as function argument separator.
+       */
+       void ParserBase::ResetLocale()
+       {
+               s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>('.'));
+               SetArgSep(',');
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Initialize the token reader.
+
+         Create new token reader object and submit pointers to function, operator,
+         constant and variable definitions.
+
+         \post m_pTokenReader.get()!=0
+         \throw nothrow
+       */
+       void ParserBase::InitTokenReader()
+       {
+               m_pTokenReader.reset(new token_reader_type(this));
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Reset parser to string parsing mode and clear internal buffers.
+
+               Clear bytecode, reset the token reader.
+               \throw nothrow
+       */
+       void ParserBase::ReInit() const
+       {
+               m_pParseFormula = &ParserBase::ParseString;
+               m_vStringBuf.clear();
+               m_vRPN.clear();
+               m_pTokenReader->ReInit();
+       }
+
+       //---------------------------------------------------------------------------
+       void ParserBase::OnDetectVar(string_type* /*pExpr*/, int& /*nStart*/, int& /*nEnd*/)
+       {}
+
+       //---------------------------------------------------------------------------
+       /** \brief Returns the version of muParser.
+               \param eInfo A flag indicating whether the full version info should be
+                                        returned or not.
+
+         Format is as follows: "MAJOR.MINOR (COMPILER_FLAGS)" The COMPILER_FLAGS
+         are returned only if eInfo==pviFULL.
+       */
+       string_type ParserBase::GetVersion(EParserVersionInfo eInfo) const
+       {
+               stringstream_type ss;
+
+               ss << ParserVersion;
+
+               if (eInfo == pviFULL)
+               {
+                       ss << _T(" (") << ParserVersionDate;
+                       ss << std::dec << _T("; ") << sizeof(void*) * 8 << _T("BIT");
+
+#ifdef _DEBUG
+                       ss << _T("; DEBUG");
+#else 
+                       ss << _T("; RELEASE");
+#endif
+
+#ifdef _UNICODE
+                       ss << _T("; UNICODE");
+#else
+#ifdef _MBCS
+                       ss << _T("; MBCS");
+#else
+                       ss << _T("; ASCII");
+#endif
+#endif
+
+#ifdef MUP_USE_OPENMP
+                       ss << _T("; OPENMP");
+#endif
+
+                       ss << _T(")");
+               }
+
+               return ss.str();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Add a value parsing function.
+
+               When parsing an expression muParser tries to detect values in the expression
+               string using different valident callbacks. Thus it's possible to parse
+               for hex values, binary values and floating point values.
+       */
+       void ParserBase::AddValIdent(identfun_type a_pCallback)
+       {
+               m_pTokenReader->AddValIdent(a_pCallback);
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Set a function that can create variable pointer for unknown expression variables.
+               \param a_pFactory A pointer to the variable factory.
+               \param pUserData A user defined context pointer.
+       */
+       void ParserBase::SetVarFactory(facfun_type a_pFactory, void* pUserData)
+       {
+               m_pTokenReader->SetVarCreator(a_pFactory, pUserData);
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Add a function or operator callback to the parser. */
+       void ParserBase::AddCallback(const string_type& a_strName,
+               const ParserCallback& a_Callback,
+               funmap_type& a_Storage,
+               const char_type* a_szCharSet)
+       {
+               if (a_Callback.GetAddr() == 0)
+                       Error(ecINVALID_FUN_PTR);
+
+               const funmap_type* pFunMap = &a_Storage;
+
+               // Check for conflicting operator or function names
+               if (pFunMap != &m_FunDef && m_FunDef.find(a_strName) != m_FunDef.end())
+                       Error(ecNAME_CONFLICT, -1, a_strName);
+
+               if (pFunMap != &m_PostOprtDef && m_PostOprtDef.find(a_strName) != m_PostOprtDef.end())
+                       Error(ecNAME_CONFLICT, -1, a_strName);
+
+               if (pFunMap != &m_InfixOprtDef && pFunMap != &m_OprtDef && m_InfixOprtDef.find(a_strName) != m_InfixOprtDef.end())
+                       Error(ecNAME_CONFLICT, -1, a_strName);
+
+               if (pFunMap != &m_InfixOprtDef && pFunMap != &m_OprtDef && m_OprtDef.find(a_strName) != m_OprtDef.end())
+                       Error(ecNAME_CONFLICT, -1, a_strName);
+
+               CheckOprt(a_strName, a_Callback, a_szCharSet);
+               a_Storage[a_strName] = a_Callback;
+               ReInit();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Check if a name contains invalid characters.
+
+               \throw ParserException if the name contains invalid characters.
+       */
+       void ParserBase::CheckOprt(const string_type& a_sName,
+               const ParserCallback& a_Callback,
+               const string_type& a_szCharSet) const
+       {
+               if (!a_sName.length() ||
+                       (a_sName.find_first_not_of(a_szCharSet) != string_type::npos) ||
+                       (a_sName[0] >= '0' && a_sName[0] <= '9'))
+               {
+                       switch (a_Callback.GetCode())
+                       {
+                       case cmOPRT_POSTFIX: Error(ecINVALID_POSTFIX_IDENT, -1, a_sName); break;
+                       case cmOPRT_INFIX:   Error(ecINVALID_INFIX_IDENT, -1, a_sName); break;
+                       default:             Error(ecINVALID_NAME, -1, a_sName);
+                       }
+               }
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Check if a name contains invalid characters.
+
+               \throw ParserException if the name contains invalid characters.
+       */
+       void ParserBase::CheckName(const string_type& a_sName,
+               const string_type& a_szCharSet) const
+       {
+               if (!a_sName.length() ||
+                       (a_sName.find_first_not_of(a_szCharSet) != string_type::npos) ||
+                       (a_sName[0] >= '0' && a_sName[0] <= '9'))
+               {
+                       Error(ecINVALID_NAME);
+               }
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Set the formula.
+               \param a_strFormula Formula as string_type
+               \throw ParserException in case of syntax errors.
+
+               Triggers first time calculation thus the creation of the bytecode and
+               scanning of used variables.
+       */
+       void ParserBase::SetExpr(const string_type& a_sExpr)
+       {
+               if (std::all_of(a_sExpr.begin(), a_sExpr.end(), [](char c) { return !std::isgraph(c); }))
+               {
+                       Error(ecINVALID_CHARACTERS_FOUND);
+               }
+
+               // Check locale compatibility
+               if (m_pTokenReader->GetArgSep() == std::use_facet<numpunct<char_type> >(s_locale).decimal_point())
+                       Error(ecLOCALE);
+
+               // Check maximum allowed expression length. An arbitrary value small enough so i can debug expressions sent to me
+               if (a_sExpr.length() >= MaxLenExpression)
+                       Error(ecEXPRESSION_TOO_LONG, 0, a_sExpr);
+
+               m_pTokenReader->SetFormula(a_sExpr + _T(" "));
+               ReInit();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Get the default symbols used for the built in operators.
+               \sa c_DefaultOprt
+       */
+       const char_type** ParserBase::GetOprtDef() const
+       {
+               return (const char_type**)(&c_DefaultOprt[0]);
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Define the set of valid characters to be used in names of
+                          functions, variables, constants.
+       */
+       void ParserBase::DefineNameChars(const char_type* a_szCharset)
+       {
+               m_sNameChars = a_szCharset;
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Define the set of valid characters to be used in names of
+                          binary operators and postfix operators.
+       */
+       void ParserBase::DefineOprtChars(const char_type* a_szCharset)
+       {
+               m_sOprtChars = a_szCharset;
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Define the set of valid characters to be used in names of
+                          infix operators.
+       */
+       void ParserBase::DefineInfixOprtChars(const char_type* a_szCharset)
+       {
+               m_sInfixOprtChars = a_szCharset;
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Virtual function that defines the characters allowed in name identifiers.
+               \sa #ValidOprtChars, #ValidPrefixOprtChars
+       */
+       const char_type* ParserBase::ValidNameChars() const
+       {
+               MUP_ASSERT(m_sNameChars.size());
+               return m_sNameChars.c_str();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Virtual function that defines the characters allowed in operator definitions.
+               \sa #ValidNameChars, #ValidPrefixOprtChars
+       */
+       const char_type* ParserBase::ValidOprtChars() const
+       {
+               MUP_ASSERT(m_sOprtChars.size());
+               return m_sOprtChars.c_str();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Virtual function that defines the characters allowed in infix operator definitions.
+               \sa #ValidNameChars, #ValidOprtChars
+       */
+       const char_type* ParserBase::ValidInfixOprtChars() const
+       {
+               MUP_ASSERT(m_sInfixOprtChars.size());
+               return m_sInfixOprtChars.c_str();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Add a user defined operator.
+               \post Will reset the Parser to string parsing mode.
+       */
+       void ParserBase::DefinePostfixOprt(const string_type& a_sName, fun_type1 a_pFun, bool a_bAllowOpt)
+       {
+               if (a_sName.length() > MaxLenIdentifier)
+                       Error(ecIDENTIFIER_TOO_LONG);
+
+               AddCallback(a_sName, ParserCallback(a_pFun, a_bAllowOpt, prPOSTFIX, cmOPRT_POSTFIX), m_PostOprtDef, ValidOprtChars());
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Initialize user defined functions.
+
+         Calls the virtual functions InitFun(), InitConst() and InitOprt().
+       */
+       void ParserBase::Init()
+       {
+               InitCharSets();
+               InitFun();
+               InitConst();
+               InitOprt();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Add a user defined operator.
+               \post Will reset the Parser to string parsing mode.
+               \param [in] a_sName  operator Identifier
+               \param [in] a_pFun  Operator callback function
+               \param [in] a_iPrec  Operator Precedence (default=prSIGN)
+               \param [in] a_bAllowOpt  True if operator is volatile (default=false)
+               \sa EPrec
+       */
+       void ParserBase::DefineInfixOprt(const string_type& a_sName, fun_type1 a_pFun, int a_iPrec, bool a_bAllowOpt)
+       {
+               if (a_sName.length() > MaxLenIdentifier)
+                       Error(ecIDENTIFIER_TOO_LONG);
+
+               AddCallback(a_sName, ParserCallback(a_pFun, a_bAllowOpt, a_iPrec, cmOPRT_INFIX), m_InfixOprtDef, ValidInfixOprtChars());
+       }
+
+
+       //---------------------------------------------------------------------------
+       /** \brief Define a binary operator.
+               \param [in] a_sName The identifier of the operator.
+               \param [in] a_pFun Pointer to the callback function.
+               \param [in] a_iPrec Precedence of the operator.
+               \param [in] a_eAssociativity The associativity of the operator.
+               \param [in] a_bAllowOpt If this is true the operator may be optimized away.
+
+               Adds a new Binary operator the the parser instance.
+       */
+       void ParserBase::DefineOprt(const string_type& a_sName, fun_type2 a_pFun, unsigned a_iPrec, EOprtAssociativity a_eAssociativity, bool a_bAllowOpt)
+       {
+               if (a_sName.length() > MaxLenIdentifier)
+                       Error(ecIDENTIFIER_TOO_LONG);
+
+               // Check for conflicts with built in operator names
+               for (int i = 0; m_bBuiltInOp && i < cmENDIF; ++i)
+               {
+                       if (a_sName == string_type(c_DefaultOprt[i]))
+                       {
+                               Error(ecBUILTIN_OVERLOAD, -1, a_sName);
+                       }
+               }
+
+               AddCallback(a_sName, ParserCallback(a_pFun, a_bAllowOpt, a_iPrec, a_eAssociativity), m_OprtDef, ValidOprtChars());
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Define a new string constant.
+               \param [in] a_strName The name of the constant.
+               \param [in] a_strVal the value of the constant.
+       */
+       void ParserBase::DefineStrConst(const string_type& a_strName, const string_type& a_strVal)
+       {
+               // Test if a constant with that names already exists
+               if (m_StrVarDef.find(a_strName) != m_StrVarDef.end())
+                       Error(ecNAME_CONFLICT);
+
+               CheckName(a_strName, ValidNameChars());
+
+               m_vStringVarBuf.push_back(a_strVal);                // Store variable string in internal buffer
+               m_StrVarDef[a_strName] = m_vStringVarBuf.size() - 1;  // bind buffer index to variable name
+
+               ReInit();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Add a user defined variable.
+               \param [in] a_sName the variable name
+               \param [in] a_pVar A pointer to the variable value.
+               \post Will reset the Parser to string parsing mode.
+               \throw ParserException in case the name contains invalid signs or a_pVar is nullptr.
+       */
+       void ParserBase::DefineVar(const string_type& a_sName, value_type* a_pVar)
+       {
+               if (a_pVar == 0)
+                       Error(ecINVALID_VAR_PTR);
+
+               if (a_sName.length() > MaxLenIdentifier)
+                       Error(ecIDENTIFIER_TOO_LONG);
+
+               // Test if a constant with that names already exists
+               if (m_ConstDef.find(a_sName) != m_ConstDef.end())
+                       Error(ecNAME_CONFLICT);
+
+               CheckName(a_sName, ValidNameChars());
+               m_VarDef[a_sName] = a_pVar;
+               ReInit();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Add a user defined constant.
+               \param [in] a_sName The name of the constant.
+               \param [in] a_fVal the value of the constant.
+               \post Will reset the Parser to string parsing mode.
+               \throw ParserException in case the name contains invalid signs.
+       */
+       void ParserBase::DefineConst(const string_type& a_sName, value_type a_fVal)
+       {
+               if (a_sName.length() > MaxLenIdentifier)
+                       Error(ecIDENTIFIER_TOO_LONG);
+
+               CheckName(a_sName, ValidNameChars());
+               m_ConstDef[a_sName] = a_fVal;
+               ReInit();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Get operator priority.
+               \throw ParserException if a_Oprt is no operator code
+       */
+       int ParserBase::GetOprtPrecedence(const token_type& a_Tok) const
+       {
+               switch (a_Tok.GetCode())
+               {
+                       // built in operators
+               case cmEND:      return -5;
+               case cmARG_SEP:  return -4;
+               case cmASSIGN:   return -1;
+               case cmELSE:
+               case cmIF:       return  0;
+               case cmLAND:     return  prLAND;
+               case cmLOR:      return  prLOR;
+               case cmLT:
+               case cmGT:
+               case cmLE:
+               case cmGE:
+               case cmNEQ:
+               case cmEQ:       return  prCMP;
+               case cmADD:
+               case cmSUB:      return  prADD_SUB;
+               case cmMUL:
+               case cmDIV:      return  prMUL_DIV;
+               case cmPOW:      return  prPOW;
+
+                       // user defined binary operators
+               case cmOPRT_INFIX:
+               case cmOPRT_BIN: return a_Tok.GetPri();
+               default:  Error(ecINTERNAL_ERROR, 5);
+                       return 999;
+               }
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Get operator priority.
+               \throw ParserException if a_Oprt is no operator code
+       */
+       EOprtAssociativity ParserBase::GetOprtAssociativity(const token_type& a_Tok) const
+       {
+               switch (a_Tok.GetCode())
+               {
+               case cmASSIGN:
+               case cmLAND:
+               case cmLOR:
+               case cmLT:
+               case cmGT:
+               case cmLE:
+               case cmGE:
+               case cmNEQ:
+               case cmEQ:
+               case cmADD:
+               case cmSUB:
+               case cmMUL:
+               case cmDIV:      return oaLEFT;
+               case cmPOW:      return oaRIGHT;
+               case cmOPRT_BIN: return a_Tok.GetAssociativity();
+               default:         return oaNONE;
+               }
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Return a map containing the used variables only. */
+       const varmap_type& ParserBase::GetUsedVar() const
+       {
+               try
+               {
+                       m_pTokenReader->IgnoreUndefVar(true);
+                       CreateRPN(); // try to create bytecode, but don't use it for any further calculations since it
+                                                // may contain references to nonexisting variables.
+                       m_pParseFormula = &ParserBase::ParseString;
+                       m_pTokenReader->IgnoreUndefVar(false);
+               }
+               catch (exception_type& /*e*/)
+               {
+                       // Make sure to stay in string parse mode, don't call ReInit()
+                       // because it deletes the array with the used variables
+                       m_pParseFormula = &ParserBase::ParseString;
+                       m_pTokenReader->IgnoreUndefVar(false);
+                       throw;
+               }
+
+               return m_pTokenReader->GetUsedVar();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Return a map containing the used variables only. */
+       const varmap_type& ParserBase::GetVar() const
+       {
+               return m_VarDef;
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Return a map containing all parser constants. */
+       const valmap_type& ParserBase::GetConst() const
+       {
+               return m_ConstDef;
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Return prototypes of all parser functions.
+               \return #m_FunDef
+               \sa FunProt
+               \throw nothrow
+
+               The return type is a map of the public type #funmap_type containing the prototype
+               definitions for all numerical parser functions. String functions are not part of
+               this map. The Prototype definition is encapsulated in objects of the class FunProt
+               one per parser function each associated with function names via a map construct.
+       */
+       const funmap_type& ParserBase::GetFunDef() const
+       {
+               return m_FunDef;
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Retrieve the formula. */
+       const string_type& ParserBase::GetExpr() const
+       {
+               return m_pTokenReader->GetExpr();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Execute a function that takes a single string argument.
+               \param a_FunTok Function token.
+               \throw exception_type If the function token is not a string function
+       */
+       ParserBase::token_type ParserBase::ApplyStrFunc(const token_type& a_FunTok,
+               const std::vector<token_type>& a_vArg) const
+       {
+               if (a_vArg.back().GetCode() != cmSTRING)
+                       Error(ecSTRING_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString());
+
+               token_type  valTok;
+               generic_fun_type pFunc = a_FunTok.GetFuncAddr();
+               MUP_ASSERT(pFunc);
+
+               try
+               {
+                       // Check function arguments; write dummy value into valtok to represent the result
+                       switch (a_FunTok.GetArgCount())
+                       {
+                       case 0: valTok.SetVal(1); a_vArg[0].GetAsString();  break;
+                       case 1: valTok.SetVal(1); a_vArg[1].GetAsString();  a_vArg[0].GetVal();  break;
+                       case 2: valTok.SetVal(1); a_vArg[2].GetAsString();  a_vArg[1].GetVal();  a_vArg[0].GetVal();  break;
+                       case 3: valTok.SetVal(1); a_vArg[3].GetAsString();  a_vArg[2].GetVal();  a_vArg[1].GetVal();  a_vArg[0].GetVal();  break;
+                       case 4: valTok.SetVal(1); a_vArg[4].GetAsString();  a_vArg[3].GetVal();  a_vArg[2].GetVal();  a_vArg[1].GetVal();  a_vArg[0].GetVal();  break;
+                       default: Error(ecINTERNAL_ERROR);
+                       }
+               }
+               catch (ParserError&)
+               {
+                       Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString());
+               }
+
+               // string functions won't be optimized
+               m_vRPN.AddStrFun(pFunc, a_FunTok.GetArgCount(), a_vArg.back().GetIdx());
+
+               // Push dummy value representing the function result to the stack
+               return valTok;
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Apply a function token.
+               \param iArgCount Number of Arguments actually gathered used only for multiarg functions.
+               \post The result is pushed to the value stack
+               \post The function token is removed from the stack
+               \throw exception_type if Argument count does not match function requirements.
+       */
+       void ParserBase::ApplyFunc(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal, int a_iArgCount) const
+       {
+               MUP_ASSERT(m_pTokenReader.get());
+
+               // Operator stack empty or does not contain tokens with callback functions
+               if (a_stOpt.empty() || a_stOpt.top().GetFuncAddr() == 0)
+                       return;
+
+               token_type funTok = a_stOpt.top();
+               a_stOpt.pop();
+               MUP_ASSERT(funTok.GetFuncAddr() != nullptr);
+
+               // Binary operators must rely on their internal operator number
+               // since counting of operators relies on commas for function arguments
+               // binary operators do not have commas in their expression
+               int iArgCount = (funTok.GetCode() == cmOPRT_BIN) ? funTok.GetArgCount() : a_iArgCount;
+
+               // determine how many parameters the function needs. To remember iArgCount includes the 
+               // string parameter whilst GetArgCount() counts only numeric parameters.
+               int iArgRequired = funTok.GetArgCount() + ((funTok.GetType() == tpSTR) ? 1 : 0);
+
+               // That's the number of numerical parameters
+               int iArgNumerical = iArgCount - ((funTok.GetType() == tpSTR) ? 1 : 0);
+
+               if (funTok.GetCode() == cmFUNC_STR && iArgCount - iArgNumerical > 1)
+                       Error(ecINTERNAL_ERROR);
+
+               if (funTok.GetArgCount() >= 0 && iArgCount > iArgRequired)
+                       Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos() - 1, funTok.GetAsString());
+
+               if (funTok.GetCode() != cmOPRT_BIN && iArgCount < iArgRequired)
+                       Error(ecTOO_FEW_PARAMS, m_pTokenReader->GetPos() - 1, funTok.GetAsString());
+
+               if (funTok.GetCode() == cmFUNC_STR && iArgCount > iArgRequired)
+                       Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos() - 1, funTok.GetAsString());
+
+               // Collect the numeric function arguments from the value stack and store them
+               // in a vector
+               std::vector<token_type> stArg;
+               for (int i = 0; i < iArgNumerical; ++i)
+               {
+                       if (a_stVal.empty())
+                               Error(ecINTERNAL_ERROR, m_pTokenReader->GetPos(), funTok.GetAsString());
+
+                       stArg.push_back(a_stVal.top());
+                       a_stVal.pop();
+
+                       if (stArg.back().GetType() == tpSTR && funTok.GetType() != tpSTR)
+                               Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString());
+               }
+
+               switch (funTok.GetCode())
+               {
+               case  cmFUNC_STR:
+                       if (a_stVal.empty())
+                               Error(ecINTERNAL_ERROR, m_pTokenReader->GetPos(), funTok.GetAsString());
+
+                       stArg.push_back(a_stVal.top());
+                       a_stVal.pop();
+
+                       if (stArg.back().GetType() == tpSTR && funTok.GetType() != tpSTR)
+                               Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString());
+
+                       ApplyStrFunc(funTok, stArg);
+                       break;
+
+               case  cmFUNC_BULK:
+                       m_vRPN.AddBulkFun(funTok.GetFuncAddr(), (int)stArg.size());
+                       break;
+
+               case  cmOPRT_BIN:
+               case  cmOPRT_POSTFIX:
+               case  cmOPRT_INFIX:
+               case  cmFUNC:
+                       if (funTok.GetArgCount() == -1 && iArgCount == 0)
+                               Error(ecTOO_FEW_PARAMS, m_pTokenReader->GetPos(), funTok.GetAsString());
+
+                       m_vRPN.AddFun(funTok.GetFuncAddr(), (funTok.GetArgCount() == -1) ? -iArgNumerical : iArgNumerical);
+                       break;
+               default:
+                       break;
+               }
+
+               // Push dummy value representing the function result to the stack
+               token_type token;
+               token.SetVal(1);
+               a_stVal.push(token);
+       }
+
+       //---------------------------------------------------------------------------
+       void ParserBase::ApplyIfElse(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const
+       {
+               // Check if there is an if Else clause to be calculated
+               while (a_stOpt.size() && a_stOpt.top().GetCode() == cmELSE)
+               {
+                       MUP_ASSERT(!a_stOpt.empty())
+                               token_type opElse = a_stOpt.top();
+                       a_stOpt.pop();
+
+                       // Take the value associated with the else branch from the value stack
+                       MUP_ASSERT(!a_stVal.empty());
+                       token_type vVal2 = a_stVal.top();
+                       a_stVal.pop();
+
+                       // it then else is a ternary operator Pop all three values from the value s
+                       // tack and just return the right value
+                       MUP_ASSERT(!a_stVal.empty());
+                       token_type vVal1 = a_stVal.top();
+                       a_stVal.pop();
+
+                       MUP_ASSERT(!a_stVal.empty());
+                       token_type vExpr = a_stVal.top();
+                       a_stVal.pop();
+
+                       a_stVal.push((vExpr.GetVal() != 0) ? vVal1 : vVal2);
+
+                       token_type opIf = a_stOpt.top();
+                       a_stOpt.pop();
+
+                       MUP_ASSERT(opElse.GetCode() == cmELSE);
+
+                       if (opIf.GetCode() != cmIF)
+                               Error(ecMISPLACED_COLON, m_pTokenReader->GetPos());
+
+                       m_vRPN.AddIfElse(cmENDIF);
+               } // while pending if-else-clause found
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Performs the necessary steps to write code for
+                          the execution of binary operators into the bytecode.
+       */
+       void ParserBase::ApplyBinOprt(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const
+       {
+               // is it a user defined binary operator?
+               if (a_stOpt.top().GetCode() == cmOPRT_BIN)
+               {
+                       ApplyFunc(a_stOpt, a_stVal, 2);
+               }
+               else
+               {
+                       if (a_stVal.size() < 2)
+                               Error(ecINTERNAL_ERROR, m_pTokenReader->GetPos(), _T("ApplyBinOprt: not enough values in value stack!"));
+
+                       token_type valTok1 = a_stVal.top();
+                       a_stVal.pop();
+
+                       token_type valTok2 = a_stVal.top();
+                       a_stVal.pop();
+
+                       token_type optTok = a_stOpt.top();
+                       a_stOpt.pop();
+
+                       token_type resTok;
+
+                       if (valTok1.GetType() != valTok2.GetType() ||
+                               (valTok1.GetType() == tpSTR && valTok2.GetType() == tpSTR))
+                               Error(ecOPRT_TYPE_CONFLICT, m_pTokenReader->GetPos(), optTok.GetAsString());
+
+                       if (optTok.GetCode() == cmASSIGN)
+                       {
+                               if (valTok2.GetCode() != cmVAR)
+                                       Error(ecUNEXPECTED_OPERATOR, -1, _T("="));
+
+                               m_vRPN.AddAssignOp(valTok2.GetVar());
+                       }
+                       else
+                               m_vRPN.AddOp(optTok.GetCode());
+
+                       resTok.SetVal(1);
+                       a_stVal.push(resTok);
+               }
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Apply a binary operator.
+               \param a_stOpt The operator stack
+               \param a_stVal The value stack
+       */
+       void ParserBase::ApplyRemainingOprt(std::stack<token_type>& stOpt, std::stack<token_type>& stVal) const
+       {
+               while (stOpt.size() &&
+                       stOpt.top().GetCode() != cmBO &&
+                       stOpt.top().GetCode() != cmIF)
+               {
+                       token_type tok = stOpt.top();
+                       switch (tok.GetCode())
+                       {
+                       case cmOPRT_INFIX:
+                       case cmOPRT_BIN:
+                       case cmLE:
+                       case cmGE:
+                       case cmNEQ:
+                       case cmEQ:
+                       case cmLT:
+                       case cmGT:
+                       case cmADD:
+                       case cmSUB:
+                       case cmMUL:
+                       case cmDIV:
+                       case cmPOW:
+                       case cmLAND:
+                       case cmLOR:
+                       case cmASSIGN:
+                               if (stOpt.top().GetCode() == cmOPRT_INFIX)
+                                       ApplyFunc(stOpt, stVal, 1);
+                               else
+                                       ApplyBinOprt(stOpt, stVal);
+                               break;
+
+                       case cmELSE:
+                               ApplyIfElse(stOpt, stVal);
+                               break;
+
+                       default:
+                               Error(ecINTERNAL_ERROR);
+                       }
+               }
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Parse the command code.
+               \sa ParseString(...)
+
+               Command code contains precalculated stack positions of the values and the
+               associated operators. The Stack is filled beginning from index one the
+               value at index zero is not used at all.
+       */
+       value_type ParserBase::ParseCmdCode() const
+       {
+               return ParseCmdCodeBulk(0, 0);
+       }
+
+       value_type ParserBase::ParseCmdCodeShort() const
+       {
+               const SToken *const tok = m_vRPN.GetBase();
+               value_type buf;
+
+               switch (tok->Cmd)
+               {
+               case cmVAL:             
+                       return tok->Val.data2;
+
+               case cmVAR:             
+                       return *tok->Val.ptr;
+
+               case cmVARMUL:  
+                       return *tok->Val.ptr * tok->Val.data + tok->Val.data2;
+
+               case cmVARPOW2: 
+                       buf = *(tok->Val.ptr);
+                       return buf * buf;
+
+               case  cmVARPOW3:                                
+                       buf = *(tok->Val.ptr);
+                       return buf * buf * buf;
+
+               case  cmVARPOW4:                                
+                       buf = *(tok->Val.ptr);
+                       return buf * buf * buf * buf;
+
+               // numerical function without any argument
+               case cmFUNC:
+                       return (*(fun_type0)tok->Fun.ptr)();
+
+               // String function without a numerical argument
+               case cmFUNC_STR:
+                       return (*(strfun_type1)tok->Fun.ptr)(m_vStringBuf[0].c_str());
+
+               default:
+                       throw ParserError(ecINTERNAL_ERROR);
+               }
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Evaluate the RPN.
+               \param nOffset The offset added to variable addresses (for bulk mode)
+               \param nThreadID OpenMP Thread id of the calling thread
+       */
+       value_type ParserBase::ParseCmdCodeBulk(int nOffset, int nThreadID) const
+       {
+               assert(nThreadID <= s_MaxNumOpenMPThreads);
+
+               // Note: The check for nOffset==0 and nThreadID here is not necessary but 
+               //       brings a minor performance gain when not in bulk mode.
+               value_type* Stack = ((nOffset == 0) && (nThreadID == 0)) ? &m_vStackBuffer[0] : &m_vStackBuffer[nThreadID * (m_vStackBuffer.size() / s_MaxNumOpenMPThreads)];
+               value_type buf;
+               int sidx(0);
+               for (const SToken* pTok = m_vRPN.GetBase(); pTok->Cmd != cmEND; ++pTok)
+               {
+                       switch (pTok->Cmd)
+                       {
+                       // built in binary operators
+                       case  cmLE:   --sidx; Stack[sidx] = Stack[sidx] <= Stack[sidx + 1]; continue;
+                       case  cmGE:   --sidx; Stack[sidx] = Stack[sidx] >= Stack[sidx + 1]; continue;
+                       case  cmNEQ:  --sidx; Stack[sidx] = Stack[sidx] != Stack[sidx + 1]; continue;
+                       case  cmEQ:   --sidx; Stack[sidx] = Stack[sidx] == Stack[sidx + 1]; continue;
+                       case  cmLT:   --sidx; Stack[sidx] = Stack[sidx] < Stack[sidx + 1];  continue;
+                       case  cmGT:   --sidx; Stack[sidx] = Stack[sidx] > Stack[sidx + 1];  continue;
+                       case  cmADD:  --sidx; Stack[sidx] += Stack[1 + sidx]; continue;
+                       case  cmSUB:  --sidx; Stack[sidx] -= Stack[1 + sidx]; continue;
+                       case  cmMUL:  --sidx; Stack[sidx] *= Stack[1 + sidx]; continue;
+                       case  cmDIV:  --sidx;
+                               Stack[sidx] /= Stack[1 + sidx];
+                               continue;
+
+                       case  cmPOW:
+                               --sidx; Stack[sidx] = MathImpl<value_type>::Pow(Stack[sidx], Stack[1 + sidx]);
+                               continue;
+
+                       case  cmLAND: --sidx; Stack[sidx] = Stack[sidx] && Stack[sidx + 1]; continue;
+                       case  cmLOR:  --sidx; Stack[sidx] = Stack[sidx] || Stack[sidx + 1]; continue;
+
+                       case  cmASSIGN:
+                               // Bugfix for Bulkmode:
+                               // for details see:
+                               //    https://groups.google.com/forum/embed/?place=forum/muparser-dev&showsearch=true&showpopout=true&showtabs=false&parenturl=http://muparser.beltoforion.de/mup_forum.html&afterlogin&pli=1#!topic/muparser-dev/szgatgoHTws
+                               --sidx; Stack[sidx] = *(pTok->Oprt.ptr + nOffset) = Stack[sidx + 1]; continue;
+                               // original code:
+                               //--sidx; Stack[sidx] = *pTok->Oprt.ptr = Stack[sidx+1]; continue;
+
+                       case  cmIF:
+                               if (Stack[sidx--] == 0)
+                               {
+                                       MUP_ASSERT(sidx >= 0);
+                                       pTok += pTok->Oprt.offset;
+                               }
+                               continue;
+
+                       case  cmELSE:
+                               pTok += pTok->Oprt.offset;
+                               continue;
+
+                       case  cmENDIF:
+                               continue;
+
+                               // value and variable tokens
+                       case  cmVAR:    Stack[++sidx] = *(pTok->Val.ptr + nOffset);  continue;
+                       case  cmVAL:    Stack[++sidx] = pTok->Val.data2;  continue;
+
+                       case  cmVARPOW2: buf = *(pTok->Val.ptr + nOffset);
+                               Stack[++sidx] = buf * buf;
+                               continue;
+
+                       case  cmVARPOW3: buf = *(pTok->Val.ptr + nOffset);
+                               Stack[++sidx] = buf * buf * buf;
+                               continue;
+
+                       case  cmVARPOW4: buf = *(pTok->Val.ptr + nOffset);
+                               Stack[++sidx] = buf * buf * buf * buf;
+                               continue;
+
+                       case  cmVARMUL:  
+                               Stack[++sidx] = *(pTok->Val.ptr + nOffset) * pTok->Val.data + pTok->Val.data2;
+                               continue;
+
+                               // Next is treatment of numeric functions
+                       case  cmFUNC:
+                       {
+                               int iArgCount = pTok->Fun.argc;
+
+                               // switch according to argument count
+                               switch (iArgCount)
+                               {
+                               case 0: sidx += 1; Stack[sidx] = (*(fun_type0)pTok->Fun.ptr)(); continue;
+                               case 1:            Stack[sidx] = (*(fun_type1)pTok->Fun.ptr)(Stack[sidx]);   continue;
+                               case 2: sidx -= 1; Stack[sidx] = (*(fun_type2)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1]); continue;
+                               case 3: sidx -= 2; Stack[sidx] = (*(fun_type3)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1], Stack[sidx + 2]); continue;
+                               case 4: sidx -= 3; Stack[sidx] = (*(fun_type4)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3]); continue;
+                               case 5: sidx -= 4; Stack[sidx] = (*(fun_type5)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4]); continue;
+                               case 6: sidx -= 5; Stack[sidx] = (*(fun_type6)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4], Stack[sidx + 5]); continue;
+                               case 7: sidx -= 6; Stack[sidx] = (*(fun_type7)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4], Stack[sidx + 5], Stack[sidx + 6]); continue;
+                               case 8: sidx -= 7; Stack[sidx] = (*(fun_type8)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4], Stack[sidx + 5], Stack[sidx + 6], Stack[sidx + 7]); continue;
+                               case 9: sidx -= 8; Stack[sidx] = (*(fun_type9)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4], Stack[sidx + 5], Stack[sidx + 6], Stack[sidx + 7], Stack[sidx + 8]); continue;
+                               case 10:sidx -= 9; Stack[sidx] = (*(fun_type10)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4], Stack[sidx + 5], Stack[sidx + 6], Stack[sidx + 7], Stack[sidx + 8], Stack[sidx + 9]); continue;
+                               default:
+                                       // function with variable arguments store the number as a negative value
+                                       if (iArgCount > 0)
+                                               Error(ecINTERNAL_ERROR, -1);
+
+                                       sidx -= -iArgCount - 1;
+
+                                       // <ibg 2020-06-08/> From oss-fuzz. Happend when Multiarg functions and if-then-else are used incorrectly "sum(0?1,2,3,4,5:6)"
+                                       // The final result normally lieas at position 1. If sixd is smaller there is something wrong.
+                                       if (sidx <= 0)
+                                               Error(ecINTERNAL_ERROR, -1);
+
+                                       Stack[sidx] = (*(multfun_type)pTok->Fun.ptr)(&Stack[sidx], -iArgCount);
+                                       continue;
+                               }
+                       }
+
+                       // Next is treatment of string functions
+                       case  cmFUNC_STR:
+                       {
+                               sidx -= pTok->Fun.argc - 1;
+
+                               // The index of the string argument in the string table
+                               int iIdxStack = pTok->Fun.idx;
+                               if (iIdxStack < 0 || iIdxStack >= (int)m_vStringBuf.size())
+                                       Error(ecINTERNAL_ERROR, m_pTokenReader->GetPos());
+
+                               switch (pTok->Fun.argc)  // switch according to argument count
+                               {
+                               case 0: Stack[sidx] = (*(strfun_type1)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str()); continue;
+                               case 1: Stack[sidx] = (*(strfun_type2)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx]); continue;
+                               case 2: Stack[sidx] = (*(strfun_type3)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx], Stack[sidx + 1]); continue;
+                               case 3: Stack[sidx] = (*(strfun_type4)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx], Stack[sidx + 1], Stack[sidx + 2]); continue;
+                               case 4: Stack[sidx] = (*(strfun_type5)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3]); continue;
+                               }
+
+                               continue;
+                       }
+
+                       case  cmFUNC_BULK:
+                       {
+                               int iArgCount = pTok->Fun.argc;
+
+                               // switch according to argument count
+                               switch (iArgCount)
+                               {
+                               case 0: sidx += 1; Stack[sidx] = (*(bulkfun_type0)pTok->Fun.ptr)(nOffset, nThreadID); continue;
+                               case 1:            Stack[sidx] = (*(bulkfun_type1)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx]); continue;
+                               case 2: sidx -= 1; Stack[sidx] = (*(bulkfun_type2)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx + 1]); continue;
+                               case 3: sidx -= 2; Stack[sidx] = (*(bulkfun_type3)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx + 1], Stack[sidx + 2]); continue;
+                               case 4: sidx -= 3; Stack[sidx] = (*(bulkfun_type4)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3]); continue;
+                               case 5: sidx -= 4; Stack[sidx] = (*(bulkfun_type5)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4]); continue;
+                               case 6: sidx -= 5; Stack[sidx] = (*(bulkfun_type6)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4], Stack[sidx + 5]); continue;
+                               case 7: sidx -= 6; Stack[sidx] = (*(bulkfun_type7)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4], Stack[sidx + 5], Stack[sidx + 6]); continue;
+                               case 8: sidx -= 7; Stack[sidx] = (*(bulkfun_type8)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4], Stack[sidx + 5], Stack[sidx + 6], Stack[sidx + 7]); continue;
+                               case 9: sidx -= 8; Stack[sidx] = (*(bulkfun_type9)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4], Stack[sidx + 5], Stack[sidx + 6], Stack[sidx + 7], Stack[sidx + 8]); continue;
+                               case 10:sidx -= 9; Stack[sidx] = (*(bulkfun_type10)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4], Stack[sidx + 5], Stack[sidx + 6], Stack[sidx + 7], Stack[sidx + 8], Stack[sidx + 9]); continue;
+                               default:
+                                       Error(ecINTERNAL_ERROR, 2);
+                                       continue;
+                               }
+                       }
+
+                       default:
+                               Error(ecINTERNAL_ERROR, 3);
+                               return 0;
+                       } // switch CmdCode
+               } // for all bytecode tokens
+
+               return Stack[m_nFinalResultIdx];
+       }
+
+       //---------------------------------------------------------------------------
+       void ParserBase::CreateRPN() const
+       {
+               if (!m_pTokenReader->GetExpr().length())
+                       Error(ecUNEXPECTED_EOF, 0);
+
+               std::stack<token_type> stOpt, stVal;
+               std::stack<int> stArgCount;
+               token_type opta, opt;  // for storing operators
+               token_type val, tval;  // for storing value
+               int ifElseCounter = 0;
+
+               ReInit();
+
+               // The outermost counter counts the number of separated items
+               // such as in "a=10,b=20,c=c+a"
+               stArgCount.push(1);
+
+               for (;;)
+               {
+                       opt = m_pTokenReader->ReadNextToken();
+
+                       switch (opt.GetCode())
+                       {
+                       //
+                       // Next three are different kind of value entries
+                       //
+                       case cmSTRING:
+                               if (stOpt.empty())
+                                       Error(ecSTR_RESULT, m_pTokenReader->GetPos(), opt.GetAsString());
+
+                               opt.SetIdx((int)m_vStringBuf.size());      // Assign buffer index to token 
+                               stVal.push(opt);
+                               m_vStringBuf.push_back(opt.GetAsString()); // Store string in internal buffer
+                               break;
+
+                       case cmVAR:
+                               stVal.push(opt);
+                               m_vRPN.AddVar(static_cast<value_type*>(opt.GetVar()));
+                               break;
+
+                       case cmVAL:
+                               stVal.push(opt);
+                               m_vRPN.AddVal(opt.GetVal());
+                               break;
+
+                       case cmELSE:
+                               if (stArgCount.empty())
+                                       Error(ecMISPLACED_COLON, m_pTokenReader->GetPos());
+
+                               if (stArgCount.top() > 1)
+                                       Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos());
+
+                               stArgCount.pop();
+
+                               ifElseCounter--;
+                               if (ifElseCounter < 0)
+                                       Error(ecMISPLACED_COLON, m_pTokenReader->GetPos());
+
+                               ApplyRemainingOprt(stOpt, stVal);
+                               m_vRPN.AddIfElse(cmELSE);
+                               stOpt.push(opt);
+                               break;
+
+                       case cmARG_SEP:
+                               if (!stOpt.empty() && stOpt.top().GetCode() == cmIF)
+                                       Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos());
+
+                               if (stArgCount.empty())
+                                       Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos());
+
+                               ++stArgCount.top();
+                               // Falls through.
+                               // intentional (no break!)
+
+                       case cmEND:
+                               ApplyRemainingOprt(stOpt, stVal);
+                               break;
+
+                       case cmBC:
+                       {
+                               // The argument count for parameterless functions is zero
+                               // by default an opening bracket sets parameter count to 1
+                               // in preparation of arguments to come. If the last token
+                               // was an opening bracket we know better...
+                               if (opta.GetCode() == cmBO)
+                                       --stArgCount.top();
+
+                               ApplyRemainingOprt(stOpt, stVal);
+
+                               // Check if the bracket content has been evaluated completely
+                               if (stOpt.size() && stOpt.top().GetCode() == cmBO)
+                               {
+                                       // if opt is ")" and opta is "(" the bracket has been evaluated, now its time to check
+                                       // if there is either a function or a sign pending
+                                       // neither the opening nor the closing bracket will be pushed back to
+                                       // the operator stack
+                                       // Check if a function is standing in front of the opening bracket, 
+                                       // if yes evaluate it afterwards check for infix operators
+                                       MUP_ASSERT(stArgCount.size());
+                                       int iArgCount = stArgCount.top();
+                                       stArgCount.pop();
+
+                                       stOpt.pop(); // Take opening bracket from stack
+
+                                       if (iArgCount > 1 && (stOpt.size() == 0 ||
+                                               (stOpt.top().GetCode() != cmFUNC &&
+                                                       stOpt.top().GetCode() != cmFUNC_BULK &&
+                                                       stOpt.top().GetCode() != cmFUNC_STR)))
+                                               Error(ecUNEXPECTED_ARG, m_pTokenReader->GetPos());
+
+                                       // The opening bracket was popped from the stack now check if there
+                                       // was a function before this bracket
+                                       if (stOpt.size() &&
+                                               stOpt.top().GetCode() != cmOPRT_INFIX &&
+                                               stOpt.top().GetCode() != cmOPRT_BIN &&
+                                               stOpt.top().GetFuncAddr() != 0)
+                                       {
+                                               ApplyFunc(stOpt, stVal, iArgCount);
+                                       }
+                               }
+                       } // if bracket content is evaluated
+                       break;
+
+                       //
+                       // Next are the binary operator entries
+                       //
+                       case cmIF:
+                               ifElseCounter++;
+                               stArgCount.push(1);
+                               // Falls through.
+                               // intentional (no break!)
+
+                       case cmLAND:
+                       case cmLOR:
+                       case cmLT:
+                       case cmGT:
+                       case cmLE:
+                       case cmGE:
+                       case cmNEQ:
+                       case cmEQ:
+                       case cmADD:
+                       case cmSUB:
+                       case cmMUL:
+                       case cmDIV:
+                       case cmPOW:
+                       case cmASSIGN:
+                       case cmOPRT_BIN:
+
+                               // A binary operator (user defined or built in) has been found. 
+                               while (
+                                       stOpt.size() &&
+                                       stOpt.top().GetCode() != cmBO &&
+                                       stOpt.top().GetCode() != cmELSE &&
+                                       stOpt.top().GetCode() != cmIF)
+                               {
+                                       int nPrec1 = GetOprtPrecedence(stOpt.top()),
+                                               nPrec2 = GetOprtPrecedence(opt);
+
+                                       if (stOpt.top().GetCode() == opt.GetCode())
+                                       {
+
+                                               // Deal with operator associativity
+                                               EOprtAssociativity eOprtAsct = GetOprtAssociativity(opt);
+                                               if ((eOprtAsct == oaRIGHT && (nPrec1 <= nPrec2)) ||
+                                                       (eOprtAsct == oaLEFT && (nPrec1 < nPrec2)))
+                                               {
+                                                       break;
+                                               }
+                                       }
+                                       else if (nPrec1 < nPrec2)
+                                       {
+                                               // In case the operators are not equal the precedence decides alone...
+                                               break;
+                                       }
+
+                                       if (stOpt.top().GetCode() == cmOPRT_INFIX)
+                                               ApplyFunc(stOpt, stVal, 1);
+                                       else
+                                               ApplyBinOprt(stOpt, stVal);
+                               } // while ( ... )
+
+                               if (opt.GetCode() == cmIF)
+                                       m_vRPN.AddIfElse(opt.GetCode());
+
+                               // The operator can't be evaluated right now, push back to the operator stack
+                               stOpt.push(opt);
+                               break;
+
+                               //
+                               // Last section contains functions and operators implicitly mapped to functions
+                               //
+                       case cmBO:
+                               stArgCount.push(1);
+                               stOpt.push(opt);
+                               break;
+
+                       case cmOPRT_INFIX:
+                       case cmFUNC:
+                       case cmFUNC_BULK:
+                       case cmFUNC_STR:
+                               stOpt.push(opt);
+                               break;
+
+                       case cmOPRT_POSTFIX:
+                               stOpt.push(opt);
+                               ApplyFunc(stOpt, stVal, 1);  // this is the postfix operator
+                               break;
+
+                       default:  Error(ecINTERNAL_ERROR, 3);
+                       } // end of switch operator-token
+
+                       opta = opt;
+
+                       if (opt.GetCode() == cmEND)
+                       {
+                               m_vRPN.Finalize();
+                               break;
+                       }
+
+                       if (ParserBase::g_DbgDumpStack)
+                       {
+                               StackDump(stVal, stOpt);
+                               m_vRPN.AsciiDump();
+                       }
+
+//                     if (ParserBase::g_DbgDumpCmdCode)
+                               //m_vRPN.AsciiDump();
+               } // while (true)
+
+               if (ParserBase::g_DbgDumpCmdCode)
+                       m_vRPN.AsciiDump();
+
+               if (ifElseCounter > 0)
+                       Error(ecMISSING_ELSE_CLAUSE);
+
+               // get the last value (= final result) from the stack
+               MUP_ASSERT(stArgCount.size() == 1);
+               m_nFinalResultIdx = stArgCount.top();
+               if (m_nFinalResultIdx == 0)
+                       Error(ecINTERNAL_ERROR, 9);
+
+               if (stVal.size() == 0)
+                       Error(ecEMPTY_EXPRESSION);
+
+               if (stVal.top().GetType() != tpDBL)
+                       Error(ecSTR_RESULT);
+
+               m_vStackBuffer.resize(m_vRPN.GetMaxStackSize() * s_MaxNumOpenMPThreads);
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief One of the two main parse functions.
+               \sa ParseCmdCode(...)
+
+         Parse expression from input string. Perform syntax checking and create
+         bytecode. After parsing the string and creating the bytecode the function
+         pointer #m_pParseFormula will be changed to the second parse routine the
+         uses bytecode instead of string parsing.
+       */
+       value_type ParserBase::ParseString() const
+       {
+               try
+               {
+                       CreateRPN();
+
+                       if (m_vRPN.GetSize() == 2)
+                       {
+                               m_pParseFormula = &ParserBase::ParseCmdCodeShort;
+                       }
+                       else
+                       {
+                               m_pParseFormula = &ParserBase::ParseCmdCode;
+                       }
+                       
+                       return (this->*m_pParseFormula)();
+               }
+               catch (ParserError& exc)
+               {
+                       exc.SetFormula(m_pTokenReader->GetExpr());
+                       throw;
+               }
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Create an error containing the parse error position.
+
+         This function will create an Parser Exception object containing the error text and
+         its position.
+
+         \param a_iErrc [in] The error code of type #EErrorCodes.
+         \param a_iPos [in] The position where the error was detected.
+         \param a_strTok [in] The token string representation associated with the error.
+         \throw ParserException always throws that's the only purpose of this function.
+       */
+       void  ParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const string_type& a_sTok) const
+       {
+               throw exception_type(a_iErrc, a_sTok, m_pTokenReader->GetExpr(), a_iPos);
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Clear all user defined variables.
+               \throw nothrow
+
+               Resets the parser to string parsing mode by calling #ReInit.
+       */
+       void ParserBase::ClearVar()
+       {
+               m_VarDef.clear();
+               ReInit();
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Remove a variable from internal storage.
+               \throw nothrow
+
+               Removes a variable if it exists. If the Variable does not exist nothing will be done.
+       */
+       void ParserBase::RemoveVar(const string_type& a_strVarName)
+       {
+               varmap_type::iterator item = m_VarDef.find(a_strVarName);
+               if (item != m_VarDef.end())
+               {
+                       m_VarDef.erase(item);
+                       ReInit();
+               }
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Clear all functions.
+               \post Resets the parser to string parsing mode.
+               \throw nothrow
+       */
+       void ParserBase::ClearFun()
+       {
+               m_FunDef.clear();
+               ReInit();
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Clear all user defined constants.
+
+               Both numeric and string constants will be removed from the internal storage.
+               \post Resets the parser to string parsing mode.
+               \throw nothrow
+       */
+       void ParserBase::ClearConst()
+       {
+               m_ConstDef.clear();
+               m_StrVarDef.clear();
+               ReInit();
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Clear all user defined postfix operators.
+               \post Resets the parser to string parsing mode.
+               \throw nothrow
+       */
+       void ParserBase::ClearPostfixOprt()
+       {
+               m_PostOprtDef.clear();
+               ReInit();
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Clear all user defined binary operators.
+               \post Resets the parser to string parsing mode.
+               \throw nothrow
+       */
+       void ParserBase::ClearOprt()
+       {
+               m_OprtDef.clear();
+               ReInit();
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Clear the user defined Prefix operators.
+               \post Resets the parser to string parser mode.
+               \throw nothrow
+       */
+       void ParserBase::ClearInfixOprt()
+       {
+               m_InfixOprtDef.clear();
+               ReInit();
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Enable or disable the formula optimization feature.
+               \post Resets the parser to string parser mode.
+               \throw nothrow
+       */
+       void ParserBase::EnableOptimizer(bool a_bIsOn)
+       {
+               m_vRPN.EnableOptimizer(a_bIsOn);
+               ReInit();
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Enable the dumping of bytecode and stack content on the console.
+               \param bDumpCmd Flag to enable dumping of the current bytecode to the console.
+               \param bDumpStack Flag to enable dumping of the stack content is written to the console.
+
+          This function is for debug purposes only!
+       */
+       void ParserBase::EnableDebugDump(bool bDumpCmd, bool bDumpStack)
+       {
+               ParserBase::g_DbgDumpCmdCode = bDumpCmd;
+               ParserBase::g_DbgDumpStack = bDumpStack;
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Enable or disable the built in binary operators.
+               \throw nothrow
+               \sa m_bBuiltInOp, ReInit()
+
+         If you disable the built in binary operators there will be no binary operators
+         defined. Thus you must add them manually one by one. It is not possible to
+         disable built in operators selectively. This function will Reinitialize the
+         parser by calling ReInit().
+       */
+       void ParserBase::EnableBuiltInOprt(bool a_bIsOn)
+       {
+               m_bBuiltInOp = a_bIsOn;
+               ReInit();
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Query status of built in variables.
+               \return #m_bBuiltInOp; true if built in operators are enabled.
+               \throw nothrow
+       */
+       bool ParserBase::HasBuiltInOprt() const
+       {
+               return m_bBuiltInOp;
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Get the argument separator character.
+       */
+       char_type ParserBase::GetArgSep() const
+       {
+               return m_pTokenReader->GetArgSep();
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Set argument separator.
+               \param cArgSep the argument separator character.
+       */
+       void ParserBase::SetArgSep(char_type cArgSep)
+       {
+               m_pTokenReader->SetArgSep(cArgSep);
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Dump stack content.
+
+               This function is used for debugging only.
+       */
+       void ParserBase::StackDump(const std::stack<token_type>& a_stVal, const std::stack<token_type>& a_stOprt) const
+       {
+               std::stack<token_type> stOprt(a_stOprt);
+               std::stack<token_type> stVal(a_stVal);
+
+               mu::console() << _T("\nValue stack:\n");
+               while (!stVal.empty())
+               {
+                       token_type val = stVal.top();
+                       stVal.pop();
+
+                       if (val.GetType() == tpSTR)
+                               mu::console() << _T(" \"") << val.GetAsString() << _T("\" ");
+                       else
+                               mu::console() << _T(" ") << val.GetVal() << _T(" ");
+               }
+               mu::console() << "\nOperator stack:\n";
+
+               while (!stOprt.empty())
+               {
+                       if (stOprt.top().GetCode() <= cmASSIGN)
+                       {
+                               mu::console() << _T("OPRT_INTRNL \"")
+                                       << ParserBase::c_DefaultOprt[stOprt.top().GetCode()]
+                                       << _T("\" \n");
+                       }
+                       else
+                       {
+                               switch (stOprt.top().GetCode())
+                               {
+                               case cmVAR:   mu::console() << _T("VAR\n");  break;
+                               case cmVAL:   mu::console() << _T("VAL\n");  break;
+                               case cmFUNC:
+                                       mu::console()
+                                               << _T("FUNC \"")
+                                               << stOprt.top().GetAsString()
+                                               << _T("\"\n");
+                                       break;
+
+                               case cmFUNC_BULK:
+                                       mu::console()
+                                               << _T("FUNC_BULK \"")
+                                               << stOprt.top().GetAsString()
+                                               << _T("\"\n");
+                                       break;
+
+                               case cmOPRT_INFIX:
+                                       mu::console() << _T("OPRT_INFIX \"")
+                                               << stOprt.top().GetAsString()
+                                               << _T("\"\n");
+                                       break;
+
+                               case cmOPRT_BIN:
+                                       mu::console() << _T("OPRT_BIN \"")
+                                               << stOprt.top().GetAsString()
+                                               << _T("\"\n");
+                                       break;
+
+                               case cmFUNC_STR: mu::console() << _T("FUNC_STR\n");       break;
+                               case cmEND:      mu::console() << _T("END\n");            break;
+                               case cmUNKNOWN:  mu::console() << _T("UNKNOWN\n");        break;
+                               case cmBO:       mu::console() << _T("BRACKET \"(\"\n");  break;
+                               case cmBC:       mu::console() << _T("BRACKET \")\"\n");  break;
+                               case cmIF:       mu::console() << _T("IF\n");  break;
+                               case cmELSE:     mu::console() << _T("ELSE\n");  break;
+                               case cmENDIF:    mu::console() << _T("ENDIF\n");  break;
+                               default:         mu::console() << stOprt.top().GetCode() << _T(" ");  break;
+                               }
+                       }
+                       stOprt.pop();
+               }
+
+               mu::console() << dec << endl;
+       }
+
+       /** \brief Calculate the result.
+
+         A note on const correctness:
+         I consider it important that Calc is a const function.
+         Due to caching operations Calc changes only the state of internal variables with one exception
+         m_UsedVar this is reset during string parsing and accessible from the outside. Instead of making
+         Calc non const GetUsedVar is non const because it explicitly calls Eval() forcing this update.
+
+         \pre A formula must be set.
+         \pre Variables must have been set (if needed)
+
+         \sa #m_pParseFormula
+         \return The evaluation result
+         \throw ParseException if no Formula is set or in case of any other error related to the formula.
+       */
+       value_type ParserBase::Eval() const
+       {
+               return (this->*m_pParseFormula)();
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Evaluate an expression containing comma separated subexpressions
+               \param [out] nStackSize The total number of results available
+               \return Pointer to the array containing all expression results
+
+               This member function can be used to retrieve all results of an expression
+               made up of multiple comma separated subexpressions (i.e. "x+y,sin(x),cos(y)")
+       */
+       value_type* ParserBase::Eval(int& nStackSize) const
+       {
+               if (m_vRPN.GetSize() > 0)
+               {
+                       ParseCmdCode();
+               }
+               else
+               {
+                       ParseString();
+               }
+
+               nStackSize = m_nFinalResultIdx;
+
+               // (for historic reasons the stack starts at position 1)
+               return &m_vStackBuffer[1];
+       }
+
+       //---------------------------------------------------------------------------
+       /** \brief Return the number of results on the calculation stack.
+
+         If the expression contains comma separated subexpressions (i.e. "sin(y), x+y").
+         There may be more than one return value. This function returns the number of
+         available results.
+       */
+       int ParserBase::GetNumResults() const
+       {
+               return m_nFinalResultIdx;
+       }
+
+       //---------------------------------------------------------------------------
+       void ParserBase::Eval(value_type* results, int nBulkSize)
+       {
+               CreateRPN();
+
+               int i = 0;
+
+#ifdef MUP_USE_OPENMP
+               //#define DEBUG_OMP_STUFF
+#ifdef DEBUG_OMP_STUFF
+               int* pThread = new int[nBulkSize];
+               int* pIdx = new int[nBulkSize];
+#endif
+
+               int nMaxThreads = std::min(omp_get_max_threads(), s_MaxNumOpenMPThreads);
+               int nThreadID = 0;
+
+#ifdef DEBUG_OMP_STUFF
+               int ct = 0;
+#endif
+               omp_set_num_threads(nMaxThreads);
+
+#pragma omp parallel for schedule(static, std::max(nBulkSize/nMaxThreads, 1)) private(nThreadID)
+               for (i = 0; i < nBulkSize; ++i)
+               {
+                       nThreadID = omp_get_thread_num();
+                       results[i] = ParseCmdCodeBulk(i, nThreadID);
+
+#ifdef DEBUG_OMP_STUFF
+#pragma omp critical
+                       {
+                               pThread[ct] = nThreadID;
+                               pIdx[ct] = i;
+                               ct++;
+                       }
+#endif
+               }
+
+#ifdef DEBUG_OMP_STUFF
+               FILE* pFile = fopen("bulk_dbg.txt", "w");
+               for (i = 0; i < nBulkSize; ++i)
+               {
+                       fprintf(pFile, "idx: %d  thread: %d \n", pIdx[i], pThread[i]);
+               }
+
+               delete[] pIdx;
+               delete[] pThread;
+
+               fclose(pFile);
+#endif
+
+#else
+               for (i = 0; i < nBulkSize; ++i)
+               {
+                       results[i] = ParseCmdCodeBulk(i, 0);
+               }
+#endif
+
+       }
+} // namespace mu
+
+#if defined(_MSC_VER)
+       #pragma warning(pop)
+#endif
+
diff --git a/src/external/muparser/muParserBase.h b/src/external/muparser/muParserBase.h
new file mode 100644 (file)
index 0000000..b7f87b4
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_BASE_H
+#define MU_PARSER_BASE_H
+
+//--- Standard includes ------------------------------------------------------------------------
+#include <cmath>
+#include <string>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <locale>
+#include <limits.h>
+
+//--- Parser includes --------------------------------------------------------------------------
+#include "muParserDef.h"
+#include "muParserTokenReader.h"
+#include "muParserBytecode.h"
+#include "muParserError.h"
+
+
+namespace mu
+{
+       /** \file
+               \brief This file contains the class definition of the muParser engine.
+       */
+
+       /** \brief Mathematical expressions parser (base parser engine).
+
+               This is the implementation of a bytecode based mathematical expressions parser.
+               The formula will be parsed from string and converted into a bytecode.
+               Future calculations will be done with the bytecode instead the formula string
+               resulting in a significant performance increase.
+               Complementary to a set of internally implemented functions the parser is able to handle
+               user defined functions and variables.
+       */
+       class API_EXPORT_CXX ParserBase
+       {
+               friend class ParserTokenReader;
+
+       private:
+
+               /** \brief Typedef for the parse functions.
+
+                 The parse function do the actual work. The parser exchanges
+                 the function pointer to the parser function depending on
+                 which state it is in. (i.e. bytecode parser vs. string parser)
+               */
+               typedef value_type(ParserBase::* ParseFunction)() const;
+
+               /** \brief Type used for storing an array of values. */
+               typedef std::vector<value_type> valbuf_type;
+
+               /** \brief Type for a vector of strings. */
+               typedef std::vector<string_type> stringbuf_type;
+
+               /** \brief Typedef for the token reader. */
+               typedef ParserTokenReader token_reader_type;
+
+               /** \brief Type used for parser tokens. */
+               typedef ParserToken<value_type, string_type> token_type;
+
+               /** \brief Maximum number of threads spawned by OpenMP when using the bulk mode. */
+               static const int s_MaxNumOpenMPThreads;
+
+       public:
+
+               /** \brief Type of the error class.
+
+                 Included for backwards compatibility.
+               */
+               typedef ParserError exception_type;
+
+               static void EnableDebugDump(bool bDumpCmd, bool bDumpStack);
+
+               ParserBase();
+               ParserBase(const ParserBase& a_Parser);
+               ParserBase& operator=(const ParserBase& a_Parser);
+
+               virtual ~ParserBase();
+
+               value_type Eval() const;
+               value_type* Eval(int& nStackSize) const;
+               void Eval(value_type* results, int nBulkSize);
+
+               int GetNumResults() const;
+
+               void SetExpr(const string_type& a_sExpr);
+               void SetVarFactory(facfun_type a_pFactory, void* pUserData = nullptr);
+
+               void SetDecSep(char_type cDecSep);
+               void SetThousandsSep(char_type cThousandsSep = 0);
+               void ResetLocale();
+
+               void EnableOptimizer(bool a_bIsOn = true);
+               void EnableBuiltInOprt(bool a_bIsOn = true);
+
+               bool HasBuiltInOprt() const;
+               void AddValIdent(identfun_type a_pCallback);
+
+               /** \fn void mu::ParserBase::DefineFun(const string_type &a_strName, fun_type0 a_pFun, bool a_bAllowOpt = true)
+                       \brief Define a parser function without arguments.
+                       \param a_strName Name of the function
+                       \param a_pFun Pointer to the callback function
+                       \param a_bAllowOpt A flag indicating this function may be optimized
+               */
+               template<typename T>
+               void DefineFun(const string_type& a_strName, T a_pFun, bool a_bAllowOpt = true)
+               {
+                       AddCallback(a_strName, ParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars());
+               }
+
+               void DefineOprt(const string_type& a_strName, fun_type2 a_pFun, unsigned a_iPri = 0, EOprtAssociativity a_eAssociativity = oaLEFT, bool a_bAllowOpt = false);
+               void DefineConst(const string_type& a_sName, value_type a_fVal);
+               void DefineStrConst(const string_type& a_sName, const string_type& a_strVal);
+               void DefineVar(const string_type& a_sName, value_type* a_fVar);
+               void DefinePostfixOprt(const string_type& a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt = true);
+               void DefineInfixOprt(const string_type& a_strName, fun_type1 a_pOprt, int a_iPrec = prINFIX, bool a_bAllowOpt = true);
+
+               // Clear user defined variables, constants or functions
+               void ClearVar();
+               void ClearFun();
+               void ClearConst();
+               void ClearInfixOprt();
+               void ClearPostfixOprt();
+               void ClearOprt();
+
+               void RemoveVar(const string_type& a_strVarName);
+               const varmap_type& GetUsedVar() const;
+               const varmap_type& GetVar() const;
+               const valmap_type& GetConst() const;
+               const string_type& GetExpr() const;
+               const funmap_type& GetFunDef() const;
+               string_type GetVersion(EParserVersionInfo eInfo = pviFULL) const;
+
+               const char_type** GetOprtDef() const;
+               void DefineNameChars(const char_type* a_szCharset);
+               void DefineOprtChars(const char_type* a_szCharset);
+               void DefineInfixOprtChars(const char_type* a_szCharset);
+
+               const char_type* ValidNameChars() const;
+               const char_type* ValidOprtChars() const;
+               const char_type* ValidInfixOprtChars() const;
+
+               void SetArgSep(char_type cArgSep);
+               char_type GetArgSep() const;
+
+       protected:
+
+               void Init();
+               void Error(EErrorCodes a_iErrc, int a_iPos = (int)mu::string_type::npos, const string_type& a_strTok = string_type()) const;
+
+               virtual void InitCharSets() = 0;
+               virtual void InitFun() = 0;
+               virtual void InitConst() = 0;
+               virtual void InitOprt() = 0;
+
+               virtual void OnDetectVar(string_type* pExpr, int& nStart, int& nEnd);
+
+               static const char_type* c_DefaultOprt[];
+               static std::locale s_locale;  ///< The locale used by the parser
+               static bool g_DbgDumpCmdCode;
+               static bool g_DbgDumpStack;
+
+               /** \brief A facet class used to change decimal and thousands separator. */
+               template<class TChar>
+               class change_dec_sep : public std::numpunct<TChar>
+               {
+               public:
+
+                       explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3)
+                               :std::numpunct<TChar>()
+                               , m_nGroup(nGroup)
+                               , m_cDecPoint(cDecSep)
+                               , m_cThousandsSep(cThousandsSep)
+                       {}
+
+               protected:
+
+                       virtual char_type do_decimal_point() const
+                       {
+                               return m_cDecPoint;
+                       }
+
+                       virtual char_type do_thousands_sep() const
+                       {
+                               return m_cThousandsSep;
+                       }
+
+                       virtual std::string do_grouping() const
+                       {
+                               // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4
+                               // courtesy of Jens Bartsch
+                               // original code:
+                               //        return std::string(1, (char)m_nGroup); 
+                               // new code:
+                               return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX));
+                       }
+
+               private:
+
+                       int m_nGroup;
+                       char_type m_cDecPoint;
+                       char_type m_cThousandsSep;
+               };
+
+       private:
+
+               void Assign(const ParserBase& a_Parser);
+               void InitTokenReader();
+               void ReInit() const;
+
+               void AddCallback(const string_type& a_strName, const ParserCallback& a_Callback, funmap_type& a_Storage, const char_type* a_szCharSet);
+               void ApplyRemainingOprt(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const;
+               void ApplyBinOprt(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const;
+               void ApplyIfElse(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const;
+               void ApplyFunc(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal, int iArgCount) const;
+
+               token_type ApplyStrFunc(const token_type& a_FunTok, const std::vector<token_type>& a_vArg) const;
+
+               int GetOprtPrecedence(const token_type& a_Tok) const;
+               EOprtAssociativity GetOprtAssociativity(const token_type& a_Tok) const;
+
+               void CreateRPN() const;
+
+               value_type ParseString() const;
+               value_type ParseCmdCode() const;
+               value_type ParseCmdCodeShort() const;
+               value_type ParseCmdCodeBulk(int nOffset, int nThreadID) const;
+
+               void  CheckName(const string_type& a_strName, const string_type& a_CharSet) const;
+               void  CheckOprt(const string_type& a_sName, const ParserCallback& a_Callback, const string_type& a_szCharSet) const;
+
+               void StackDump(const std::stack<token_type >& a_stVal, const std::stack<token_type >& a_stOprt) const;
+
+               /** \brief Pointer to the parser function.
+
+                 Eval() calls the function whose address is stored there.
+               */
+               mutable ParseFunction  m_pParseFormula;
+               mutable ParserByteCode m_vRPN;        ///< The Bytecode class.
+               mutable stringbuf_type  m_vStringBuf; ///< String buffer, used for storing string function arguments
+               stringbuf_type  m_vStringVarBuf;
+
+               std::unique_ptr<token_reader_type> m_pTokenReader; ///< Managed pointer to the token reader object.
+
+               funmap_type  m_FunDef;         ///< Map of function names and pointers.
+               funmap_type  m_PostOprtDef;    ///< Postfix operator callbacks
+               funmap_type  m_InfixOprtDef;   ///< unary infix operator.
+               funmap_type  m_OprtDef;        ///< Binary operator callbacks
+               valmap_type  m_ConstDef;       ///< user constants.
+               strmap_type  m_StrVarDef;      ///< user defined string constants
+               varmap_type  m_VarDef;         ///< user defind variables.
+
+               bool m_bBuiltInOp;             ///< Flag that can be used for switching built in operators on and off
+
+               string_type m_sNameChars;      ///< Charset for names
+               string_type m_sOprtChars;      ///< Charset for postfix/ binary operator tokens
+               string_type m_sInfixOprtChars; ///< Charset for infix operator tokens
+
+               // items merely used for caching state information
+               mutable valbuf_type m_vStackBuffer; ///< This is merely a buffer used for the stack in the cmd parsing routine
+               mutable int m_nFinalResultIdx;
+       };
+
+} // namespace mu
+
+#endif
diff --git a/src/external/muparser/muParserBytecode.cpp b/src/external/muparser/muParserBytecode.cpp
new file mode 100644 (file)
index 0000000..ebc603a
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "muParserBytecode.h"
+
+#include <algorithm>
+#include <string>
+#include <stack>
+#include <vector>
+#include <iostream>
+
+#include "muParserDef.h"
+#include "muParserError.h"
+#include "muParserToken.h"
+#include "muParserTemplateMagic.h"
+
+
+namespace mu
+{
+       /** \brief Bytecode default constructor. */
+       ParserByteCode::ParserByteCode()
+               :m_iStackPos(0)
+               , m_iMaxStackSize(0)
+               , m_vRPN()
+               , m_bEnableOptimizer(true)
+       {
+               m_vRPN.reserve(50);
+       }
+
+
+       /** \brief Copy constructor.
+
+               Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)
+       */
+       ParserByteCode::ParserByteCode(const ParserByteCode& a_ByteCode)
+       {
+               Assign(a_ByteCode);
+       }
+
+
+       /** \brief Assignment operator.
+
+               Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)
+       */
+       ParserByteCode& ParserByteCode::operator=(const ParserByteCode& a_ByteCode)
+       {
+               Assign(a_ByteCode);
+               return *this;
+       }
+
+
+       void ParserByteCode::EnableOptimizer(bool bStat)
+       {
+               m_bEnableOptimizer = bStat;
+       }
+
+
+       /** \brief Copy state of another object to this.
+
+               \throw nowthrow
+       */
+       void ParserByteCode::Assign(const ParserByteCode& a_ByteCode)
+       {
+               if (this == &a_ByteCode)
+                       return;
+
+               m_iStackPos = a_ByteCode.m_iStackPos;
+               m_vRPN = a_ByteCode.m_vRPN;
+               m_iMaxStackSize = a_ByteCode.m_iMaxStackSize;
+               m_bEnableOptimizer = a_ByteCode.m_bEnableOptimizer;
+       }
+
+
+       /** \brief Add a Variable pointer to bytecode.
+               \param a_pVar Pointer to be added.
+               \throw nothrow
+       */
+       void ParserByteCode::AddVar(value_type* a_pVar)
+       {
+               ++m_iStackPos;
+               m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
+
+               // optimization does not apply
+               SToken tok;
+               tok.Cmd = cmVAR;
+               tok.Val.ptr = a_pVar;
+               tok.Val.data = 1;
+               tok.Val.data2 = 0;
+               m_vRPN.push_back(tok);
+       }
+
+
+       /** \brief Add a Variable pointer to bytecode.
+
+               Value entries in byte code consist of:
+               <ul>
+                 <li>value array position of the value</li>
+                 <li>the operator code according to ParserToken::cmVAL</li>
+                 <li>the value stored in #mc_iSizeVal number of bytecode entries.</li>
+               </ul>
+
+               \param a_pVal Value to be added.
+               \throw nothrow
+       */
+       void ParserByteCode::AddVal(value_type a_fVal)
+       {
+               ++m_iStackPos;
+               m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
+
+               // If optimization does not apply
+               SToken tok;
+               tok.Cmd = cmVAL;
+               tok.Val.ptr = nullptr;
+               tok.Val.data = 0;
+               tok.Val.data2 = a_fVal;
+               m_vRPN.push_back(tok);
+       }
+
+
+       void ParserByteCode::ConstantFolding(ECmdCode a_Oprt)
+       {
+               std::size_t sz = m_vRPN.size();
+               value_type& x = m_vRPN[sz - 2].Val.data2;
+               value_type& y = m_vRPN[sz - 1].Val.data2;
+
+               switch (a_Oprt)
+               {
+               case cmLAND: x = (int)x && (int)y; m_vRPN.pop_back(); break;
+               case cmLOR:  x = (int)x || (int)y; m_vRPN.pop_back(); break;
+               case cmLT:   x = x < y;  m_vRPN.pop_back();  break;
+               case cmGT:   x = x > y;  m_vRPN.pop_back();  break;
+               case cmLE:   x = x <= y; m_vRPN.pop_back();  break;
+               case cmGE:   x = x >= y; m_vRPN.pop_back();  break;
+               case cmNEQ:  x = x != y; m_vRPN.pop_back();  break;
+               case cmEQ:   x = x == y; m_vRPN.pop_back();  break;
+               case cmADD:  x = x + y;  m_vRPN.pop_back();  break;
+               case cmSUB:  x = x - y;  m_vRPN.pop_back();  break;
+               case cmMUL:  x = x * y;  m_vRPN.pop_back();  break;
+               case cmDIV:
+                       x = x / y;
+                       m_vRPN.pop_back();
+                       break;
+
+               case cmPOW: x = MathImpl<value_type>::Pow(x, y);
+                       m_vRPN.pop_back();
+                       break;
+
+               default:
+                       break;
+               } // switch opcode
+       }
+
+
+       /** \brief Add an operator identifier to bytecode.
+
+               Operator entries in byte code consist of:
+               <ul>
+                 <li>value array position of the result</li>
+                 <li>the operator code according to ParserToken::ECmdCode</li>
+               </ul>
+
+               \sa  ParserToken::ECmdCode
+       */
+       void ParserByteCode::AddOp(ECmdCode a_Oprt)
+       {
+               bool bOptimized = false;
+
+               if (m_bEnableOptimizer)
+               {
+                       std::size_t sz = m_vRPN.size();
+
+                       // Check for foldable constants like:
+                       //   cmVAL cmVAL cmADD 
+                       // where cmADD can stand fopr any binary operator applied to
+                       // two constant values.
+                       if (sz >= 2 && m_vRPN[sz - 2].Cmd == cmVAL && m_vRPN[sz - 1].Cmd == cmVAL)
+                       {
+                               ConstantFolding(a_Oprt);
+                               bOptimized = true;
+                       }
+                       else
+                       {
+                               switch (a_Oprt)
+                               {
+                               case  cmPOW:
+                                       // Optimization for polynomials of low order
+                                       if (m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 1].Cmd == cmVAL)
+                                       {
+                                               if (m_vRPN[sz - 1].Val.data2 == 0)
+                                               {
+                                                       m_vRPN[sz - 2].Cmd = cmVAL;
+                                                       m_vRPN[sz - 2].Val.ptr = nullptr;
+                                                       m_vRPN[sz - 2].Val.data = 0;
+                                                       m_vRPN[sz - 2].Val.data2 = 1;
+                                               }
+                                               else if (m_vRPN[sz - 1].Val.data2 == 1)
+                                                       m_vRPN[sz - 2].Cmd = cmVAR;
+                                               else if (m_vRPN[sz - 1].Val.data2 == 2)
+                                                       m_vRPN[sz - 2].Cmd = cmVARPOW2;
+                                               else if (m_vRPN[sz - 1].Val.data2 == 3)
+                                                       m_vRPN[sz - 2].Cmd = cmVARPOW3;
+                                               else if (m_vRPN[sz - 1].Val.data2 == 4)
+                                                       m_vRPN[sz - 2].Cmd = cmVARPOW4;
+                                               else
+                                                       break;
+
+                                               m_vRPN.pop_back();
+                                               bOptimized = true;
+                                       }
+                                       break;
+
+                               case  cmSUB:
+                               case  cmADD:
+                                       // Simple optimization based on pattern recognition for a shitload of different
+                                       // bytecode combinations of addition/subtraction
+                                       if ((m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAL) ||
+                                               (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVAR) ||
+                                               (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL) ||
+                                               (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAL) ||
+                                               (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
+                                               (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
+                                               (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
+                                               (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr))
+                                       {
+                                               MUP_ASSERT(
+                                                       (m_vRPN[sz - 2].Val.ptr == nullptr && m_vRPN[sz - 1].Val.ptr != nullptr) ||
+                                                       (m_vRPN[sz - 2].Val.ptr != nullptr && m_vRPN[sz - 1].Val.ptr == nullptr) ||
+                                                       (m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr));
+
+                                               m_vRPN[sz - 2].Cmd = cmVARMUL;
+                                               m_vRPN[sz - 2].Val.ptr = (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | (long long)(m_vRPN[sz - 1].Val.ptr));    // variable
+                                               m_vRPN[sz - 2].Val.data2 += ((a_Oprt == cmSUB) ? -1 : 1) * m_vRPN[sz - 1].Val.data2;  // offset
+                                               m_vRPN[sz - 2].Val.data += ((a_Oprt == cmSUB) ? -1 : 1) * m_vRPN[sz - 1].Val.data;   // multiplicand
+                                               m_vRPN.pop_back();
+                                               bOptimized = true;
+                                       }
+                                       break;
+
+                               case  cmMUL:
+                                       if ((m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAL) ||
+                                               (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVAR))
+                                       {
+                                               m_vRPN[sz - 2].Cmd = cmVARMUL;
+                                               m_vRPN[sz - 2].Val.ptr = (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | (long long)(m_vRPN[sz - 1].Val.ptr));
+                                               m_vRPN[sz - 2].Val.data = m_vRPN[sz - 2].Val.data2 + m_vRPN[sz - 1].Val.data2;
+                                               m_vRPN[sz - 2].Val.data2 = 0;
+                                               m_vRPN.pop_back();
+                                               bOptimized = true;
+                                       }
+                                       else if (
+                                               (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL) ||
+                                               (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAL))
+                                       {
+                                               // Optimization: 2*(3*b+1) or (3*b+1)*2 -> 6*b+2
+                                               m_vRPN[sz - 2].Cmd = cmVARMUL;
+                                               m_vRPN[sz - 2].Val.ptr = (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | (long long)(m_vRPN[sz - 1].Val.ptr));
+                                               if (m_vRPN[sz - 1].Cmd == cmVAL)
+                                               {
+                                                       m_vRPN[sz - 2].Val.data *= m_vRPN[sz - 1].Val.data2;
+                                                       m_vRPN[sz - 2].Val.data2 *= m_vRPN[sz - 1].Val.data2;
+                                               }
+                                               else
+                                               {
+                                                       m_vRPN[sz - 2].Val.data = m_vRPN[sz - 1].Val.data * m_vRPN[sz - 2].Val.data2;
+                                                       m_vRPN[sz - 2].Val.data2 = m_vRPN[sz - 1].Val.data2 * m_vRPN[sz - 2].Val.data2;
+                                               }
+                                               m_vRPN.pop_back();
+                                               bOptimized = true;
+                                       }
+                                       else if (
+                                               m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAR &&
+                                               m_vRPN[sz - 1].Val.ptr == m_vRPN[sz - 2].Val.ptr)
+                                       {
+                                               // Optimization: a*a -> a^2
+                                               m_vRPN[sz - 2].Cmd = cmVARPOW2;
+                                               m_vRPN.pop_back();
+                                               bOptimized = true;
+                                       }
+                                       break;
+
+                               case cmDIV:
+                                       if (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 1].Val.data2 != 0)
+                                       {
+                                               // Optimization: 4*a/2 -> 2*a
+                                               m_vRPN[sz - 2].Val.data /= m_vRPN[sz - 1].Val.data2;
+                                               m_vRPN[sz - 2].Val.data2 /= m_vRPN[sz - 1].Val.data2;
+                                               m_vRPN.pop_back();
+                                               bOptimized = true;
+                                       }
+                                       break;
+
+                                       // no optimization for other opcodes
+                               default:
+                                       break;
+                               } // switch a_Oprt
+                       }
+               }
+
+               // If optimization can't be applied just write the value
+               if (!bOptimized)
+               {
+                       --m_iStackPos;
+                       SToken tok;
+                       tok.Cmd = a_Oprt;
+                       m_vRPN.push_back(tok);
+               }
+       }
+
+
+       void ParserByteCode::AddIfElse(ECmdCode a_Oprt)
+       {
+               SToken tok;
+               tok.Cmd = a_Oprt;
+               m_vRPN.push_back(tok);
+       }
+
+
+       /** \brief Add an assignment operator
+
+               Operator entries in byte code consist of:
+               <ul>
+                 <li>cmASSIGN code</li>
+                 <li>the pointer of the destination variable</li>
+               </ul>
+
+               \sa  ParserToken::ECmdCode
+       */
+       void ParserByteCode::AddAssignOp(value_type* a_pVar)
+       {
+               --m_iStackPos;
+
+               SToken tok;
+               tok.Cmd = cmASSIGN;
+               tok.Oprt.ptr = a_pVar;
+               m_vRPN.push_back(tok);
+       }
+
+
+       /** \brief Add function to bytecode.
+
+               \param a_iArgc Number of arguments, negative numbers indicate multiarg functions.
+               \param a_pFun Pointer to function callback.
+       */
+       void ParserByteCode::AddFun(generic_fun_type a_pFun, int a_iArgc)
+       {
+               std::size_t sz = m_vRPN.size();
+               bool optimize = false;
+
+               // only optimize functions with fixed number of more than a single arguments
+               if (m_bEnableOptimizer && a_iArgc > 0)
+               {
+                       // <ibg 2020-06-10/> Unary Plus is a no-op
+                       if ((void*)a_pFun == (void*)&MathImpl<value_type>::UnaryPlus)
+                               return;
+
+                       optimize = true;
+
+                       for (int i = 0; i < std::abs(a_iArgc); ++i)
+                       {
+                               if (m_vRPN[sz - i - 1].Cmd != cmVAL)
+                               {
+                                       optimize = false;
+                                       break;
+                               }
+                       }
+               }
+
+               if (optimize)
+               {
+                       value_type val = 0;
+                       switch (a_iArgc)
+                       {
+                       case 1:  val = (*reinterpret_cast<fun_type1>(a_pFun))(m_vRPN[sz - 1].Val.data2);   break;
+                       case 2:  val = (*reinterpret_cast<fun_type2>(a_pFun))(m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
+                       case 3:  val = (*reinterpret_cast<fun_type3>(a_pFun))(m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
+                       case 4:  val = (*reinterpret_cast<fun_type4>(a_pFun))(m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
+                       case 5:  val = (*reinterpret_cast<fun_type5>(a_pFun))(m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
+                       case 6:  val = (*reinterpret_cast<fun_type6>(a_pFun))(m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
+                       case 7:  val = (*reinterpret_cast<fun_type7>(a_pFun))(m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
+                       case 8:  val = (*reinterpret_cast<fun_type8>(a_pFun))(m_vRPN[sz - 8].Val.data2, m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
+                       case 9:  val = (*reinterpret_cast<fun_type9>(a_pFun))(m_vRPN[sz - 9].Val.data2, m_vRPN[sz - 8].Val.data2, m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
+                       case 10: val = (*reinterpret_cast<fun_type10>(a_pFun))(m_vRPN[sz - 10].Val.data2, m_vRPN[sz - 9].Val.data2, m_vRPN[sz - 8].Val.data2, m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
+                       default:
+                               // For now functions with unlimited number of arguments are not optimized
+                               throw ParserError(ecINTERNAL_ERROR);
+                       }
+
+                       // remove the folded values
+                       m_vRPN.erase(m_vRPN.end() - a_iArgc, m_vRPN.end());
+
+                       SToken tok;
+                       tok.Cmd = cmVAL;
+                       tok.Val.data = 0;
+                       tok.Val.data2 = val;
+                       tok.Val.ptr = nullptr;
+                       m_vRPN.push_back(tok);
+               }
+               else
+               {
+                       SToken tok;
+                       tok.Cmd = cmFUNC;
+                       tok.Fun.argc = a_iArgc;
+                       tok.Fun.ptr = a_pFun;
+                       m_vRPN.push_back(tok);
+               }
+
+               m_iStackPos = m_iStackPos - std::abs(a_iArgc) + 1;
+               m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
+
+       }
+
+
+       /** \brief Add a bulk function to bytecode.
+
+               \param a_iArgc Number of arguments, negative numbers indicate multiarg functions.
+               \param a_pFun Pointer to function callback.
+       */
+       void ParserByteCode::AddBulkFun(generic_fun_type a_pFun, int a_iArgc)
+       {
+               m_iStackPos = m_iStackPos - a_iArgc + 1;
+               m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
+
+               SToken tok;
+               tok.Cmd = cmFUNC_BULK;
+               tok.Fun.argc = a_iArgc;
+               tok.Fun.ptr = a_pFun;
+               m_vRPN.push_back(tok);
+       }
+
+
+       /** \brief Add Strung function entry to the parser bytecode.
+               \throw nothrow
+
+               A string function entry consists of the stack position of the return value,
+               followed by a cmSTRFUNC code, the function pointer and an index into the
+               string buffer maintained by the parser.
+       */
+       void ParserByteCode::AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx)
+       {
+               m_iStackPos = m_iStackPos - a_iArgc + 1;
+
+               SToken tok;
+               tok.Cmd = cmFUNC_STR;
+               tok.Fun.argc = a_iArgc;
+               tok.Fun.idx = a_iIdx;
+               tok.Fun.ptr = a_pFun;
+               m_vRPN.push_back(tok);
+
+               m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
+       }
+
+
+       /** \brief Add end marker to bytecode.
+
+               \throw nothrow
+       */
+       void ParserByteCode::Finalize()
+       {
+               SToken tok;
+               tok.Cmd = cmEND;
+               m_vRPN.push_back(tok);
+               rpn_type(m_vRPN).swap(m_vRPN);     // shrink bytecode vector to fit
+
+               // Determine the if-then-else jump offsets
+               std::stack<int> stIf, stElse;
+               int idx;
+               for (int i = 0; i < (int)m_vRPN.size(); ++i)
+               {
+                       switch (m_vRPN[i].Cmd)
+                       {
+                       case cmIF:
+                               stIf.push(i);
+                               break;
+
+                       case cmELSE:
+                               stElse.push(i);
+                               idx = stIf.top();
+                               stIf.pop();
+                               m_vRPN[idx].Oprt.offset = i - idx;
+                               break;
+
+                       case cmENDIF:
+                               idx = stElse.top();
+                               stElse.pop();
+                               m_vRPN[idx].Oprt.offset = i - idx;
+                               break;
+
+                       default:
+                               break;
+                       }
+               }
+       }
+
+
+       std::size_t ParserByteCode::GetMaxStackSize() const
+       {
+               return m_iMaxStackSize + 1;
+       }
+
+
+       /** \brief Delete the bytecode.
+
+               \throw nothrow
+
+               The name of this function is a violation of my own coding guidelines
+               but this way it's more in line with the STL functions thus more
+               intuitive.
+       */
+       void ParserByteCode::clear()
+       {
+               m_vRPN.clear();
+               m_iStackPos = 0;
+               m_iMaxStackSize = 0;
+       }
+
+
+       /** \brief Dump bytecode (for debugging only!). */
+       void ParserByteCode::AsciiDump()
+       {
+               if (!m_vRPN.size())
+               {
+                       mu::console() << _T("No bytecode available\n");
+                       return;
+               }
+
+               mu::console() << _T("Number of RPN tokens:") << (int)m_vRPN.size() << _T("\n");
+               for (std::size_t i = 0; i < m_vRPN.size() && m_vRPN[i].Cmd != cmEND; ++i)
+               {
+                       mu::console() << std::dec << i << _T(" : \t");
+                       switch (m_vRPN[i].Cmd)
+                       {
+                       case cmVAL:   mu::console() << _T("VAL \t");
+                               mu::console() << _T("[") << m_vRPN[i].Val.data2 << _T("]\n");
+                               break;
+
+                       case cmVAR:   mu::console() << _T("VAR \t");
+                               mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
+                               break;
+
+                       case cmVARPOW2: mu::console() << _T("VARPOW2 \t");
+                               mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
+                               break;
+
+                       case cmVARPOW3: mu::console() << _T("VARPOW3 \t");
+                               mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
+                               break;
+
+                       case cmVARPOW4: mu::console() << _T("VARPOW4 \t");
+                               mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
+                               break;
+
+                       case cmVARMUL:  mu::console() << _T("VARMUL \t");
+                               mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]");
+                               mu::console() << _T(" * [") << m_vRPN[i].Val.data << _T("]");
+                               mu::console() << _T(" + [") << m_vRPN[i].Val.data2 << _T("]\n");
+                               break;
+
+                       case cmFUNC:  mu::console() << _T("CALL\t");
+                               mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].Fun.argc << _T("]");
+                               mu::console() << _T("[ADDR: 0x") << std::hex << reinterpret_cast<void*>(m_vRPN[i].Fun.ptr) << _T("]");
+                               mu::console() << _T("\n");
+                               break;
+
+                       case cmFUNC_STR:
+                               mu::console() << _T("CALL STRFUNC\t");
+                               mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].Fun.argc << _T("]");
+                               mu::console() << _T("[IDX:") << std::dec << m_vRPN[i].Fun.idx << _T("]");
+                               mu::console() << _T("[ADDR: 0x") << reinterpret_cast<void*>(m_vRPN[i].Fun.ptr) << _T("]\n");
+                               break;
+
+                       case cmLT:    mu::console() << _T("LT\n");  break;
+                       case cmGT:    mu::console() << _T("GT\n");  break;
+                       case cmLE:    mu::console() << _T("LE\n");  break;
+                       case cmGE:    mu::console() << _T("GE\n");  break;
+                       case cmEQ:    mu::console() << _T("EQ\n");  break;
+                       case cmNEQ:   mu::console() << _T("NEQ\n"); break;
+                       case cmADD:   mu::console() << _T("ADD\n"); break;
+                       case cmLAND:  mu::console() << _T("&&\n"); break;
+                       case cmLOR:   mu::console() << _T("||\n"); break;
+                       case cmSUB:   mu::console() << _T("SUB\n"); break;
+                       case cmMUL:   mu::console() << _T("MUL\n"); break;
+                       case cmDIV:   mu::console() << _T("DIV\n"); break;
+                       case cmPOW:   mu::console() << _T("POW\n"); break;
+
+                       case cmIF:    mu::console() << _T("IF\t");
+                               mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n");
+                               break;
+
+                       case cmELSE:  mu::console() << _T("ELSE\t");
+                               mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n");
+                               break;
+
+                       case cmENDIF: mu::console() << _T("ENDIF\n"); break;
+
+                       case cmASSIGN:
+                               mu::console() << _T("ASSIGN\t");
+                               mu::console() << _T("[ADDR: 0x") << m_vRPN[i].Oprt.ptr << _T("]\n");
+                               break;
+
+                       default:      mu::console() << _T("(unknown code: ") << m_vRPN[i].Cmd << _T(")\n");
+                               break;
+                       } // switch cmdCode
+               } // while bytecode
+
+               mu::console() << _T("END") << std::endl;
+       }
+} // namespace mu
diff --git a/src/external/muparser/muParserBytecode.h b/src/external/muparser/muParserBytecode.h
new file mode 100644 (file)
index 0000000..5074e23
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_BYTECODE_H
+#define MU_PARSER_BYTECODE_H
+
+#include <string>
+#include <stack>
+#include <vector>
+
+#include "muParserDef.h"
+#include "muParserError.h"
+#include "muParserToken.h"
+
+/** \file
+       \brief Definition of the parser bytecode class.
+*/
+
+
+namespace mu
+{
+       struct SToken
+       {
+               ECmdCode Cmd;
+
+               union
+               {
+                       struct //SValData
+                       {
+                               value_type* ptr;
+                               value_type  data;
+                               value_type  data2;
+                       } Val;
+
+                       struct //SFunData
+                       {
+                               // Note: generic_fun_type is merely a placeholder. The real type could be 
+                               //       anything between gun_type1 and fun_type9. I can't use a void
+                               //       pointer due to constraints in the ANSI standard which allows
+                               //       data pointers and function pointers to differ in size.
+                               generic_fun_type ptr;
+                               int   argc;
+                               int   idx;
+                       } Fun;
+
+                       struct //SOprtData
+                       {
+                               value_type* ptr;
+                               int offset;
+                       } Oprt;
+               };
+       };
+
+
+       /** \brief Bytecode implementation of the Math Parser.
+
+               The bytecode contains the formula converted to revers polish notation stored in a continious
+               memory area. Associated with this data are operator codes, variable pointers, constant
+               values and function pointers. Those are necessary in order to calculate the result.
+               All those data items will be casted to the underlying datatype of the bytecode.
+       */
+       class ParserByteCode final
+       {
+       private:
+
+               /** \brief Token type for internal use only. */
+               typedef ParserToken<value_type, string_type> token_type;
+
+               /** \brief Token vector for storing the RPN. */
+               typedef std::vector<SToken> rpn_type;
+
+               /** \brief Position in the Calculation array. */
+               unsigned m_iStackPos;
+
+               /** \brief Maximum size needed for the stack. */
+               std::size_t m_iMaxStackSize;
+
+               /** \brief The actual rpn storage. */
+               rpn_type  m_vRPN;
+
+               bool m_bEnableOptimizer;
+
+               void ConstantFolding(ECmdCode a_Oprt);
+
+       public:
+
+               ParserByteCode();
+               ParserByteCode(const ParserByteCode& a_ByteCode);
+               ParserByteCode& operator=(const ParserByteCode& a_ByteCode);
+               void Assign(const ParserByteCode& a_ByteCode);
+
+               void AddVar(value_type* a_pVar);
+               void AddVal(value_type a_fVal);
+               void AddOp(ECmdCode a_Oprt);
+               void AddIfElse(ECmdCode a_Oprt);
+               void AddAssignOp(value_type* a_pVar);
+               void AddFun(generic_fun_type a_pFun, int a_iArgc);
+               void AddBulkFun(generic_fun_type a_pFun, int a_iArgc);
+               void AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx);
+
+               void EnableOptimizer(bool bStat);
+
+               void Finalize();
+               void clear();
+               std::size_t GetMaxStackSize() const;
+
+               std::size_t GetSize() const
+               {
+                       return m_vRPN.size();
+               }
+
+               inline const SToken* GetBase() const
+               {
+                       if (m_vRPN.size() == 0)
+                               throw ParserError(ecINTERNAL_ERROR);
+                       else
+                               return &m_vRPN[0];
+               }
+
+               void AsciiDump();
+       };
+
+} // namespace mu
+
+#endif
+
+
diff --git a/src/external/muparser/muParserCallback.cpp b/src/external/muparser/muParserCallback.cpp
new file mode 100644 (file)
index 0000000..1f68a65
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "muParserCallback.h"
+
+#if defined(_MSC_VER)
+       #pragma warning(push)
+       #pragma warning(disable : 26812) 
+#endif
+
+/** \file
+       \brief Implementation of the parser callback class.
+*/
+
+
+namespace mu
+{
+       ParserCallback::ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(0)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec, ECmdCode a_iCode)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(1)
+               , m_iPri(a_iPrec)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(a_iCode)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       
+       /** \brief Constructor for constructing function callbacks taking two arguments.
+               \throw nothrow
+       */
+       ParserCallback::ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(2)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       /** \brief Constructor for constructing binary operator callbacks.
+               \param a_pFun Pointer to a static function taking two arguments
+               \param a_bAllowOpti A flag indicating this function can be optimized
+               \param a_iPrec The operator precedence
+               \param a_eOprtAsct The operators associativity
+               \throw nothrow
+       */
+       ParserCallback::ParserCallback(fun_type2 a_pFun,
+               bool a_bAllowOpti,
+               int a_iPrec,
+               EOprtAssociativity a_eOprtAsct)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(2)
+               , m_iPri(a_iPrec)
+               , m_eOprtAsct(a_eOprtAsct)
+               , m_iCode(cmOPRT_BIN)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(3)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(4)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(5)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(6)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(7)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(8)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(9)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(10)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(0)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(1)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       /** \brief Constructor for constructing function callbacks taking two arguments.
+               \throw nothrow
+       */
+       ParserCallback::ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(2)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(3)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(4)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(5)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(6)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(7)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(8)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(9)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(10)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_BULK)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(multfun_type a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(-1)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC)
+               , m_iType(tpDBL)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(0)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_STR)
+               , m_iType(tpSTR)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(1)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_STR)
+               , m_iType(tpSTR)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(2)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_STR)
+               , m_iType(tpSTR)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(strfun_type4 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(3)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_STR)
+               , m_iType(tpSTR)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       ParserCallback::ParserCallback(strfun_type5 a_pFun, bool a_bAllowOpti)
+               :m_pFun((void*)a_pFun)
+               , m_iArgc(4)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmFUNC_STR)
+               , m_iType(tpSTR)
+               , m_bAllowOpti(a_bAllowOpti)
+       {}
+
+
+       /** \brief Default constructor.
+               \throw nothrow
+       */
+       ParserCallback::ParserCallback()
+               :m_pFun(0)
+               , m_iArgc(0)
+               , m_iPri(-1)
+               , m_eOprtAsct(oaNONE)
+               , m_iCode(cmUNKNOWN)
+               , m_iType(tpVOID)
+               , m_bAllowOpti(0)
+       {}
+
+
+       /** \brief Copy constructor.
+               \throw nothrow
+       */
+       ParserCallback::ParserCallback(const ParserCallback& ref)
+       {
+               m_pFun = ref.m_pFun;
+               m_iArgc = ref.m_iArgc;
+               m_bAllowOpti = ref.m_bAllowOpti;
+               m_iCode = ref.m_iCode;
+               m_iType = ref.m_iType;
+               m_iPri = ref.m_iPri;
+               m_eOprtAsct = ref.m_eOprtAsct;
+       }
+
+  ParserCallback & ParserCallback::operator=(const ParserCallback & ref)
+  {
+    if (this != &ref)
+    {
+      m_pFun = ref.m_pFun;
+      m_iArgc = ref.m_iArgc;
+      m_bAllowOpti = ref.m_bAllowOpti;
+      m_iCode = ref.m_iCode;
+      m_iType = ref.m_iType;
+      m_iPri = ref.m_iPri;
+      m_eOprtAsct = ref.m_eOprtAsct;
+    }
+    return *this;
+  }
+
+       /** \brief Clone this instance and return a pointer to the new instance. */
+       ParserCallback* ParserCallback::Clone() const
+       {
+               return new ParserCallback(*this);
+       }
+
+
+       /** \brief Return tru if the function is conservative.
+
+               Conservative functions return always the same result for the same argument.
+               \throw nothrow
+       */
+       bool ParserCallback::IsOptimizable() const
+       {
+               return m_bAllowOpti;
+       }
+
+
+       /** \brief Get the callback address for the parser function.
+
+               The type of the address is void. It needs to be recasted according to the
+               argument number to the right type.
+
+               \throw nothrow
+               \return #pFun
+       */
+       void* ParserCallback::GetAddr() const
+       {
+               return m_pFun;
+       }
+
+
+       /** \brief Return the callback code. */
+       ECmdCode  ParserCallback::GetCode() const
+       {
+               return m_iCode;
+       }
+
+
+       ETypeCode ParserCallback::GetType() const
+       {
+               return m_iType;
+       }
+
+
+       /** \brief Return the operator precedence.
+               \throw nothrown
+
+          Only valid if the callback token is an operator token (binary or infix).
+       */
+       int ParserCallback::GetPri()  const
+       {
+               return m_iPri;
+       }
+
+
+       /** \brief Return the operators associativity.
+               \throw nothrown
+
+          Only valid if the callback token is a binary operator token.
+       */
+       EOprtAssociativity ParserCallback::GetAssociativity() const
+       {
+               return m_eOprtAsct;
+       }
+
+
+       /** \brief Returns the number of function Arguments. */
+       int ParserCallback::GetArgc() const
+       {
+               return m_iArgc;
+       }
+} // namespace mu
+
+#if defined(_MSC_VER)
+       #pragma warning(pop)
+#endif
diff --git a/src/external/muparser/muParserCallback.h b/src/external/muparser/muParserCallback.h
new file mode 100644 (file)
index 0000000..1a8d060
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef MU_PARSER_CALLBACK_H
+#define MU_PARSER_CALLBACK_H
+
+#include "muParserDef.h"
+
+/** \file
+       \brief Definition of the parser callback class.
+*/
+
+namespace mu
+{
+
+       /** \brief Encapsulation of prototypes for a numerical parser function.
+
+               Encapsulates the prototyp for numerical parser functions. The class
+               stores the number of arguments for parser functions as well
+               as additional flags indication the function is non optimizeable.
+               The pointer to the callback function pointer is stored as void*
+               and needs to be casted according to the argument count.
+               Negative argument counts indicate a parser function with a variable number
+               of arguments.
+       */
+       class API_EXPORT_CXX ParserCallback final
+       {
+       public:
+               ParserCallback(fun_type0  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type1  a_pFun, bool a_bAllowOpti, int a_iPrec = -1, ECmdCode a_iCode = cmFUNC);
+               ParserCallback(fun_type2  a_pFun, bool a_bAllowOpti, int a_iPrec, EOprtAssociativity a_eAssociativity);
+               ParserCallback(fun_type2  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type3  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type4  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type5  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type6  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type7  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type8  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type9  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti);
+
+               ParserCallback(bulkfun_type0  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type1  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type2  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type3  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type4  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type5  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type6  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type7  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type8  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type9  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti);
+
+               ParserCallback(multfun_type a_pFun, bool a_bAllowOpti);
+               ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti);
+               ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti);
+               ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti);
+               ParserCallback(strfun_type4 a_pFun, bool a_bAllowOpti);
+               ParserCallback(strfun_type5 a_pFun, bool a_bAllowOpti);
+               ParserCallback();
+               ParserCallback(const ParserCallback& a_Fun);
+    ParserCallback & operator=(const ParserCallback& a_Fun);
+
+               ParserCallback* Clone() const;
+
+               bool  IsOptimizable() const;
+               void* GetAddr() const;
+               ECmdCode  GetCode() const;
+               ETypeCode GetType() const;
+               int GetPri()  const;
+               EOprtAssociativity GetAssociativity() const;
+               int GetArgc() const;
+
+       private:
+               void* m_pFun;                   ///< Pointer to the callback function, casted to void
+
+               /** \brief Number of numeric function arguments
+
+                       This number is negative for functions with variable number of arguments. in this cases
+                       they represent the actual number of arguments found.
+               */
+               int   m_iArgc;
+               int   m_iPri;                   ///< Valid only for binary and infix operators; Operator precedence.
+               EOprtAssociativity m_eOprtAsct; ///< Operator associativity; Valid only for binary operators 
+               ECmdCode  m_iCode;
+               ETypeCode m_iType;
+               bool  m_bAllowOpti;             ///< Flag indication optimizeability 
+       };
+
+
+       /** \brief Container for Callback objects. */
+       typedef std::map<string_type, ParserCallback> funmap_type;
+
+} // namespace mu
+
+#endif
+
diff --git a/src/external/muparser/muParserDLL.cpp b/src/external/muparser/muParserDLL.cpp
new file mode 100644 (file)
index 0000000..b62deb6
--- /dev/null
@@ -0,0 +1,992 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#if defined(MUPARSER_DLL) 
+
+#if defined(_WIN32)
+       #define WIN32_LEAN_AND_MEAN
+       #define _CRT_SECURE_NO_WARNINGS
+       #define _CRT_SECURE_NO_DEPRECATE
+
+       #include <windows.h>
+#endif
+
+#include <cassert>
+
+#include "muParserDLL.h"
+#include "muParser.h"
+#include "muParserInt.h"
+#include "muParserError.h"
+
+#if defined(_MSC_VER)
+       #pragma warning(push)
+       #pragma warning(disable : 26812) 
+#endif
+
+#define MU_TRY  \
+               try             \
+        {
+
+#define MU_CATCH                                                                              \
+        }                                                                                         \
+        catch (muError_t &e)                                          \
+        {                                                                                 \
+               ParserTag *pTag = static_cast<ParserTag*>(a_hParser);  \
+                       pTag->exc = e;                                         \
+                       pTag->bError = true;                                   \
+                       if (pTag->errHandler)                                  \
+                               (pTag->errHandler)(a_hParser);                     \
+        }                                                                             \
+        catch (...)                                                       \
+        {                                                                 \
+               ParserTag *pTag = static_cast<ParserTag*>(a_hParser);  \
+               pTag->exc = muError_t(mu::ecINTERNAL_ERROR);           \
+               pTag->bError = true;                                   \
+                       if (pTag->errHandler)                                  \
+                               (pTag->errHandler)(a_hParser);                     \
+        }
+
+/** \file
+       \brief This file contains the implementation of the DLL interface of muParser.
+*/
+
+typedef mu::ParserBase::exception_type muError_t;
+typedef mu::ParserBase muParser_t;
+int g_nBulkSize;
+
+
+class ParserTag
+{
+public:
+       ParserTag(int nType)
+               : pParser((nType == muBASETYPE_FLOAT)
+                       ? (mu::ParserBase*)new mu::Parser()
+                       : (nType == muBASETYPE_INT) ? (mu::ParserBase*)new mu::ParserInt() : nullptr)
+               , exc()
+               , errHandler(nullptr)
+               , bError(false)
+               , m_nParserType(nType)
+       {}
+
+       ~ParserTag()
+       {
+               delete pParser;
+       }
+
+       mu::ParserBase* pParser;
+       mu::ParserBase::exception_type exc;
+       muErrorHandler_t errHandler;
+       bool bError;
+
+private:
+       ParserTag(const ParserTag& ref);
+       ParserTag& operator=(const ParserTag& ref);
+
+       int m_nParserType;
+};
+
+static muChar_t s_tmpOutBuf[2048];
+
+//---------------------------------------------------------------------------
+//
+//
+//  unexported functions
+//
+//
+//---------------------------------------------------------------------------
+
+
+inline muParser_t* AsParser(muParserHandle_t a_hParser)
+{
+       return static_cast<ParserTag*>(a_hParser)->pParser;
+}
+
+
+inline ParserTag* AsParserTag(muParserHandle_t a_hParser)
+{
+       return static_cast<ParserTag*>(a_hParser);
+}
+
+
+#if defined(_WIN32)
+
+BOOL APIENTRY DllMain(HANDLE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
+{
+       switch (ul_reason_for_call)
+       {
+       case  DLL_PROCESS_ATTACH:
+               break;
+
+       case  DLL_THREAD_ATTACH:
+       case  DLL_THREAD_DETACH:
+       case  DLL_PROCESS_DETACH:
+               break;
+       }
+
+       return TRUE;
+}
+
+#endif
+
+//---------------------------------------------------------------------------
+//
+//
+//  exported functions
+//
+//
+//---------------------------------------------------------------------------
+
+API_EXPORT(void) mupSetVarFactory(muParserHandle_t a_hParser, muFacFun_t a_pFactory, void* pUserData)
+{
+       MU_TRY
+               muParser_t* p(AsParser(a_hParser));
+               p->SetVarFactory(a_pFactory, pUserData);
+       MU_CATCH
+}
+
+
+/** \brief Create a new Parser instance and return its handle. */
+API_EXPORT(muParserHandle_t) mupCreate(int nBaseType)
+{
+       switch (nBaseType)
+       {
+       case  muBASETYPE_FLOAT:   return (void*)(new ParserTag(muBASETYPE_FLOAT));
+       case  muBASETYPE_INT:     return (void*)(new ParserTag(muBASETYPE_INT));
+       default:                  return nullptr;
+       }
+}
+
+
+/** \brief Release the parser instance related with a parser handle. */
+API_EXPORT(void) mupRelease(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               ParserTag* p = static_cast<ParserTag*>(a_hParser);
+               delete p;
+       MU_CATCH
+}
+
+
+API_EXPORT(const muChar_t*) mupGetVersion(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+
+#ifndef _UNICODE
+               sprintf(s_tmpOutBuf, "%s", p->GetVersion().c_str());
+#else
+               wsprintf(s_tmpOutBuf, _T("%s"), p->GetVersion().c_str());
+#endif
+
+               return s_tmpOutBuf;
+       MU_CATCH
+       return _T("");
+}
+
+
+/** \brief Evaluate the expression. */
+API_EXPORT(muFloat_t) mupEval(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               return p->Eval();
+       MU_CATCH
+       return 0;
+}
+
+
+API_EXPORT(muFloat_t*) mupEvalMulti(muParserHandle_t a_hParser, int* nNum)
+{
+       MU_TRY
+               if (nNum == nullptr)
+                       throw std::runtime_error("Argument is null!"); 
+
+               muParser_t* const p(AsParser(a_hParser));
+               return p->Eval(*nNum);
+       MU_CATCH
+       return 0;
+}
+
+
+API_EXPORT(void) mupEvalBulk(muParserHandle_t a_hParser, muFloat_t* a_res, int nSize)
+{
+       MU_TRY
+               muParser_t* p(AsParser(a_hParser));
+               p->Eval(a_res, nSize);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupSetExpr(muParserHandle_t a_hParser, const muChar_t* a_szExpr)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->SetExpr(a_szExpr);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupRemoveVar(muParserHandle_t a_hParser, const muChar_t* a_szName)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->RemoveVar(a_szName);
+       MU_CATCH
+}
+
+
+/** \brief Release all parser variables.
+       \param a_hParser Handle to the parser instance.
+*/
+API_EXPORT(void) mupClearVar(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->ClearVar();
+       MU_CATCH
+}
+
+
+/** \brief Release all parser variables.
+       \param a_hParser Handle to the parser instance.
+*/
+API_EXPORT(void) mupClearConst(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->ClearConst();
+       MU_CATCH
+}
+
+
+/** \brief Clear all user defined operators.
+       \param a_hParser Handle to the parser instance.
+*/
+API_EXPORT(void) mupClearOprt(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->ClearOprt();
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupClearFun(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->ClearFun();
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun0(muParserHandle_t a_hParser,
+       const muChar_t* a_szName,
+       muFun0_t a_pFun,
+       muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun1(muParserHandle_t a_hParser,     const muChar_t* a_szName, muFun1_t a_pFun, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun2_t a_pFun, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun3_t a_pFun, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun4(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun4_t a_pFun, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun5(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun5_t a_pFun, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun6(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun6_t a_pFun, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun7(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun7_t a_pFun, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun8(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun8_t a_pFun, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun9(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun9_t a_pFun, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineFun10(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun10_t a_pFun, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun0(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun0_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun1_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun2_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun3_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun4(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun4_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun5(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun5_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun6(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun6_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun7(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun7_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun8(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun8_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun9(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun9_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkFun10(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun10_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineStrFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun1_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineStrFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun2_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineStrFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun3_t a_pFun)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, false);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineMultFun(muParserHandle_t a_hParser, const muChar_t* a_szName, muMultFun_t a_pFun,    muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineOprt(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun2_t a_pFun, muInt_t a_nPrec, muInt_t a_nOprtAsct, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineOprt(a_szName, a_pFun, a_nPrec, (mu::EOprtAssociativity)a_nOprtAsct, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineVar(muParserHandle_t a_hParser, const muChar_t* a_szName,    muFloat_t* a_pVar)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineVar(a_szName, a_pVar);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineBulkVar(muParserHandle_t a_hParser, const muChar_t* a_szName,        muFloat_t* a_pVar)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineVar(a_szName, a_pVar);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineConst(muParserHandle_t a_hParser,    const muChar_t* a_szName, muFloat_t a_fVal)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineConst(a_szName, a_fVal);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineStrConst(muParserHandle_t a_hParser, const muChar_t* a_szName, const muChar_t* a_szVal)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineStrConst(a_szName, a_szVal);
+       MU_CATCH
+}
+
+
+API_EXPORT(const muChar_t*) mupGetExpr(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+
+               // C# explodes when pMsg is returned directly. For some reason it can't access
+               // the memory where the message lies directly.
+#ifndef _UNICODE
+               sprintf(s_tmpOutBuf, "%s", p->GetExpr().c_str());
+#else
+               wsprintf(s_tmpOutBuf, _T("%s"), p->GetExpr().c_str());
+#endif
+
+               return s_tmpOutBuf;
+       MU_CATCH
+
+       return _T("");
+}
+
+
+API_EXPORT(void) mupDefinePostfixOprt(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun1_t a_pOprt, muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefinePostfixOprt(a_szName, a_pOprt, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupDefineInfixOprt(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun1_t a_pOprt,    muBool_t a_bAllowOpt)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->DefineInfixOprt(a_szName, a_pOprt, a_bAllowOpt != 0);
+       MU_CATCH
+}
+
+// Define character sets for identifiers
+API_EXPORT(void) mupDefineNameChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset)
+{
+       muParser_t* const p(AsParser(a_hParser));
+       p->DefineNameChars(a_szCharset);
+}
+
+
+API_EXPORT(void) mupDefineOprtChars(muParserHandle_t a_hParser,        const muChar_t* a_szCharset)
+{
+       muParser_t* const p(AsParser(a_hParser));
+       p->DefineOprtChars(a_szCharset);
+}
+
+
+API_EXPORT(void) mupDefineInfixOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset)
+{
+       muParser_t* const p(AsParser(a_hParser));
+       p->DefineInfixOprtChars(a_szCharset);
+}
+
+
+/** \brief Get the number of variables defined in the parser.
+       \param a_hParser [in] Must be a valid parser handle.
+       \return The number of used variables.
+       \sa mupGetExprVar
+*/
+API_EXPORT(int) mupGetVarNum(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               const mu::varmap_type VarMap = p->GetVar();
+               return (int)VarMap.size();
+       MU_CATCH
+
+       return 0; // never reached
+}
+
+
+/** \brief Return a variable that is used in an expression.
+       \param a_hParser [in] A valid parser handle.
+       \param a_iVar [in] The index of the variable to return.
+       \param a_szName [out] Pointer to the variable name.
+       \param a_pVar [out] Pointer to the variable.
+       \throw nothrow
+
+       Prior to calling this function call mupGetExprVarNum in order to get the
+       number of variables in the expression. If the parameter a_iVar is greater
+       than the number of variables both a_szName and a_pVar will be set to zero.
+       As a side effect this function will trigger an internal calculation of the
+       expression undefined variables will be set to zero during this calculation.
+       During the calculation user defined callback functions present in the expression
+       will be called, this is unavoidable.
+*/
+API_EXPORT(void) mupGetVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_szName, muFloat_t** a_pVar)
+{
+       // A static buffer is needed for the name since i can't return the
+       // pointer from the map.
+       static muChar_t  szName[1024];
+
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               const mu::varmap_type VarMap = p->GetVar();
+
+               if (a_iVar >= VarMap.size())
+               {
+                       *a_szName = 0;
+                       *a_pVar = 0;
+                       return;
+               }
+               mu::varmap_type::const_iterator item;
+
+               item = VarMap.begin();
+               for (unsigned i = 0; i < a_iVar; ++i)
+                       ++item;
+
+#ifndef _UNICODE
+               strncpy(szName, item->first.c_str(), sizeof(szName));
+#else
+               wcsncpy(szName, item->first.c_str(), sizeof(szName));
+#endif
+
+               szName[sizeof(szName) - 1] = 0;
+
+               *a_szName = &szName[0];
+               *a_pVar = item->second;
+               return;
+       MU_CATCH
+
+       * a_szName = 0;
+       *a_pVar = 0;
+}
+
+
+/** \brief Get the number of variables used in the expression currently set in the parser.
+       \param a_hParser [in] Must be a valid parser handle.
+       \return The number of used variables.
+       \sa mupGetExprVar
+       */
+API_EXPORT(int) mupGetExprVarNum(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               const mu::varmap_type VarMap = p->GetUsedVar();
+               return (int)VarMap.size();
+       MU_CATCH
+
+       return 0; // never reached
+}
+
+
+/** \brief Return a variable that is used in an expression.
+
+       Prior to calling this function call mupGetExprVarNum in order to get the
+       number of variables in the expression. If the parameter a_iVar is greater
+       than the number of variables both a_szName and a_pVar will be set to zero.
+       As a side effect this function will trigger an internal calculation of the
+       expression undefined variables will be set to zero during this calculation.
+       During the calculation user defined callback functions present in the expression
+       will be called, this is unavoidable.
+
+       \param a_hParser [in] A valid parser handle.
+       \param a_iVar [in] The index of the variable to return.
+       \param a_szName [out] Pointer to the variable name.
+       \param a_pVar [out] Pointer to the variable.
+       \throw nothrow
+*/
+API_EXPORT(void) mupGetExprVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_szName, muFloat_t** a_pVar)
+{
+       // A static buffer is needed for the name since i can't return the
+       // pointer from the map.
+       static muChar_t  szName[1024];
+
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               const mu::varmap_type VarMap = p->GetUsedVar();
+
+               if (a_iVar >= VarMap.size())
+               {
+                       *a_szName = 0;
+                       *a_pVar = 0;
+                       return;
+               }
+               mu::varmap_type::const_iterator item;
+
+               item = VarMap.begin();
+               for (unsigned i = 0; i < a_iVar; ++i)
+                       ++item;
+
+#ifndef _UNICODE
+               strncpy(szName, item->first.c_str(), sizeof(szName));
+#else
+               wcsncpy(szName, item->first.c_str(), sizeof(szName));
+#endif
+
+               szName[sizeof(szName) - 1] = 0;
+
+               *a_szName = &szName[0];
+               *a_pVar = item->second;
+               return;
+       MU_CATCH
+
+       * a_szName = 0;
+       *a_pVar = 0;
+}
+
+
+/** \brief Return the number of constants defined in a parser. */
+API_EXPORT(int) mupGetConstNum(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               const mu::valmap_type ValMap = p->GetConst();
+               return (int)ValMap.size();
+       MU_CATCH
+
+       return 0; // never reached
+}
+
+
+API_EXPORT(void) mupSetArgSep(muParserHandle_t a_hParser, const muChar_t cArgSep)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->SetArgSep(cArgSep);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupResetLocale(muParserHandle_t a_hParser)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->ResetLocale();
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupSetDecSep(muParserHandle_t a_hParser, const muChar_t cDecSep)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+       p->SetDecSep(cDecSep);
+       MU_CATCH
+}
+
+
+API_EXPORT(void) mupSetThousandsSep(muParserHandle_t a_hParser, const muChar_t cThousandsSep)
+{
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               p->SetThousandsSep(cThousandsSep);
+       MU_CATCH
+}
+
+//---------------------------------------------------------------------------
+/** \brief Retrieve name and value of a single parser constant.
+       \param a_hParser [in] a valid parser handle
+       \param a_iVar [in] Index of the constant to query
+       \param a_pszName [out] pointer to a null terminated string with the constant name
+       \param [out] The constant value
+       */
+API_EXPORT(void) mupGetConst(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t* a_fVal)
+{
+       // A static buffer is needed for the name since i can't return the
+       // pointer from the map.
+       static muChar_t szName[1024];
+
+       MU_TRY
+               muParser_t* const p(AsParser(a_hParser));
+               const mu::valmap_type ValMap = p->GetConst();
+
+               if (a_iVar >= ValMap.size())
+               {
+                       *a_pszName = 0;
+                       *a_fVal = 0;
+                       return;
+               }
+
+               mu::valmap_type::const_iterator item;
+               item = ValMap.begin();
+               for (unsigned i = 0; i < a_iVar; ++i)
+                       ++item;
+
+#ifndef _UNICODE
+               strncpy(szName, item->first.c_str(), sizeof(szName));
+#else
+               wcsncpy(szName, item->first.c_str(), sizeof(szName));
+#endif
+
+               szName[sizeof(szName) - 1] = 0;
+
+               *a_pszName = &szName[0];
+               *a_fVal = item->second;
+               return;
+
+       MU_CATCH
+
+       * a_pszName = 0;
+       *a_fVal = 0;
+}
+
+
+/** \brief Add a custom value recognition function. */
+API_EXPORT(void) mupAddValIdent(muParserHandle_t a_hParser,    muIdentFun_t a_pFun)
+{
+       MU_TRY
+               muParser_t* p(AsParser(a_hParser));
+               p->AddValIdent(a_pFun);
+       MU_CATCH
+}
+
+
+/** \brief Query if an error occurred.
+
+       After querying the internal error bit will be reset. So a consecutive call
+       will return false.
+*/
+API_EXPORT(muBool_t) mupError(muParserHandle_t a_hParser)
+{
+       bool bError(AsParserTag(a_hParser)->bError);
+       AsParserTag(a_hParser)->bError = false;
+       return bError;
+}
+
+
+/** \brief Reset the internal error flag. */
+API_EXPORT(void) mupErrorReset(muParserHandle_t a_hParser)
+{
+       AsParserTag(a_hParser)->bError = false;
+}
+
+
+API_EXPORT(void) mupSetErrorHandler(muParserHandle_t a_hParser, muErrorHandler_t a_pHandler)
+{
+       AsParserTag(a_hParser)->errHandler = a_pHandler;
+}
+
+
+/** \brief Return the message associated with the last error. */
+API_EXPORT(const muChar_t*) mupGetErrorMsg(muParserHandle_t a_hParser)
+{
+       ParserTag* const p(AsParserTag(a_hParser));
+       const muChar_t* pMsg = p->exc.GetMsg().c_str();
+
+       // C# explodes when pMsg is returned directly. For some reason it can't access
+       // the memory where the message lies directly.
+#ifndef _UNICODE
+       sprintf(s_tmpOutBuf, "%s", pMsg);
+#else
+       wsprintf(s_tmpOutBuf, _T("%s"), pMsg);
+#endif
+
+       return s_tmpOutBuf;
+}
+
+
+/** \brief Return the message associated with the last error. */
+API_EXPORT(const muChar_t*) mupGetErrorToken(muParserHandle_t a_hParser)
+{
+       ParserTag* const p(AsParserTag(a_hParser));
+       const muChar_t* pToken = p->exc.GetToken().c_str();
+
+       // C# explodes when pMsg is returned directly. For some reason it can't access
+       // the memory where the message lies directly.
+#ifndef _UNICODE
+       sprintf(s_tmpOutBuf, "%s", pToken);
+#else
+       wsprintf(s_tmpOutBuf, _T("%s"), pToken);
+#endif
+
+       return s_tmpOutBuf;
+}
+
+
+/** \brief Return the code associated with the last error.
+*/
+API_EXPORT(int) mupGetErrorCode(muParserHandle_t a_hParser)
+{
+       return AsParserTag(a_hParser)->exc.GetCode();
+}
+
+
+/** \brief Return the position associated with the last error. */
+API_EXPORT(int) mupGetErrorPos(muParserHandle_t a_hParser)
+{
+       return (int)AsParserTag(a_hParser)->exc.GetPos();
+}
+
+
+API_EXPORT(muFloat_t*) mupCreateVar()
+{
+       return new muFloat_t(0);
+}
+
+
+API_EXPORT(void) mupReleaseVar(muFloat_t* ptr)
+{
+       delete ptr;
+}
+
+#if defined(_MSC_VER)
+       #pragma warning(pop)
+#endif
+
+#endif      // MUPARSER_DLL
diff --git a/src/external/muparser/muParserDLL.h b/src/external/muparser/muParserDLL.h
new file mode 100644 (file)
index 0000000..253fefc
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_DLL_H
+#define MU_PARSER_DLL_H
+
+#include "muParserFixes.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+       /** \file
+               \brief This file contains the DLL interface of muParser.
+       */
+
+       // Basic types
+       typedef void* muParserHandle_t;    // parser handle
+
+#ifndef _UNICODE
+       typedef char   muChar_t;            // character type
+#else
+       typedef wchar_t   muChar_t;            // character type
+#endif
+
+       typedef int    muBool_t;            // boolean type
+       typedef int    muInt_t;             // integer type 
+       typedef double muFloat_t;           // floating point type
+
+       // function types for calculation
+       typedef muFloat_t(*muFun0_t)(void);
+       typedef muFloat_t(*muFun1_t)(muFloat_t);
+       typedef muFloat_t(*muFun2_t)(muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun3_t)(muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun4_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun5_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun6_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun7_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun8_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun9_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun10_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+
+       // Function prototypes for bulkmode functions
+       typedef muFloat_t(*muBulkFun0_t)(int, int);
+       typedef muFloat_t(*muBulkFun1_t)(int, int, muFloat_t);
+       typedef muFloat_t(*muBulkFun2_t)(int, int, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun3_t)(int, int, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun4_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun5_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun6_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun7_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun8_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun9_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun10_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+
+       typedef muFloat_t(*muMultFun_t)(const muFloat_t*, muInt_t);
+       typedef muFloat_t(*muStrFun1_t)(const muChar_t*);
+       typedef muFloat_t(*muStrFun2_t)(const muChar_t*, muFloat_t);
+       typedef muFloat_t(*muStrFun3_t)(const muChar_t*, muFloat_t, muFloat_t);
+
+       // Functions for parser management
+       typedef void (*muErrorHandler_t)(muParserHandle_t a_hParser);           // [optional] callback to an error handler
+       typedef muFloat_t* (*muFacFun_t)(const muChar_t*, void*);               // [optional] callback for creating new variables
+       typedef muInt_t(*muIdentFun_t)(const muChar_t*, muInt_t*, muFloat_t*); // [optional] value identification callbacks
+
+       //-----------------------------------------------------------------------------------------------------
+       // Constants
+       static const int muOPRT_ASCT_LEFT = 0;
+       static const int muOPRT_ASCT_RIGHT = 1;
+
+       static const int muBASETYPE_FLOAT = 0;
+       static const int muBASETYPE_INT = 1;
+
+       //-----------------------------------------------------------------------------------------------------
+       //
+       //
+       // muParser C compatible bindings
+       //
+       //
+       //-----------------------------------------------------------------------------------------------------
+
+
+       // Basic operations / initialization  
+       API_EXPORT(muParserHandle_t) mupCreate(int nBaseType);
+       API_EXPORT(void) mupRelease(muParserHandle_t a_hParser);
+       API_EXPORT(const muChar_t*) mupGetExpr(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupSetExpr(muParserHandle_t a_hParser, const muChar_t* a_szExpr);
+       API_EXPORT(void) mupSetVarFactory(muParserHandle_t a_hParser, muFacFun_t a_pFactory, void* pUserData);
+       API_EXPORT(const muChar_t*) mupGetVersion(muParserHandle_t a_hParser);
+       API_EXPORT(muFloat_t) mupEval(muParserHandle_t a_hParser);
+       API_EXPORT(muFloat_t*) mupEvalMulti(muParserHandle_t a_hParser, int* nNum);
+       API_EXPORT(void) mupEvalBulk(muParserHandle_t a_hParser, muFloat_t* a_fResult, int nSize);
+
+       // Defining callbacks / variables / constants
+       API_EXPORT(void) mupDefineFun0(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun0_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun1_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun2_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun3_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun4(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun4_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun5(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun5_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun6(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun6_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun7(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun7_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun8(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun8_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun9(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun9_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun10(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun10_t a_pFun, muBool_t a_bOptimize);
+
+       // Defining bulkmode functions
+       API_EXPORT(void) mupDefineBulkFun0(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun0_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun1_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun2_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun3_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun4(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun4_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun5(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun5_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun6(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun6_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun7(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun7_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun8(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun8_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun9(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun9_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun10(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun10_t a_pFun);
+
+       // string functions
+       API_EXPORT(void) mupDefineStrFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun1_t a_pFun);
+       API_EXPORT(void) mupDefineStrFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun2_t a_pFun);
+       API_EXPORT(void) mupDefineStrFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun3_t a_pFun);
+
+       API_EXPORT(void) mupDefineMultFun(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muMultFun_t a_pFun,
+               muBool_t a_bOptimize);
+
+       API_EXPORT(void) mupDefineOprt(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFun2_t a_pFun,
+               muInt_t a_nPrec,
+               muInt_t a_nOprtAsct,
+               muBool_t a_bOptimize);
+
+       API_EXPORT(void) mupDefineConst(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFloat_t a_fVal);
+
+       API_EXPORT(void) mupDefineStrConst(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               const muChar_t* a_sVal);
+
+       API_EXPORT(void) mupDefineVar(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFloat_t* a_fVar);
+
+       API_EXPORT(void) mupDefineBulkVar(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFloat_t* a_fVar);
+
+       API_EXPORT(void) mupDefinePostfixOprt(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFun1_t a_pOprt,
+               muBool_t a_bOptimize);
+
+
+       API_EXPORT(void) mupDefineInfixOprt(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFun1_t a_pOprt,
+               muBool_t a_bOptimize);
+
+       // Define character sets for identifiers
+       API_EXPORT(void) mupDefineNameChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset);
+       API_EXPORT(void) mupDefineOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset);
+       API_EXPORT(void) mupDefineInfixOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset);
+
+       // Remove all / single variables
+       API_EXPORT(void) mupRemoveVar(muParserHandle_t a_hParser, const muChar_t* a_szName);
+       API_EXPORT(void) mupClearVar(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupClearConst(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupClearOprt(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupClearFun(muParserHandle_t a_hParser);
+
+       // Querying variables / expression variables / constants
+       API_EXPORT(int) mupGetExprVarNum(muParserHandle_t a_hParser);
+       API_EXPORT(int) mupGetVarNum(muParserHandle_t a_hParser);
+       API_EXPORT(int) mupGetConstNum(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupGetExprVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t** a_pVar);
+       API_EXPORT(void) mupGetVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t** a_pVar);
+       API_EXPORT(void) mupGetConst(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t* a_pVar);
+       API_EXPORT(void) mupSetArgSep(muParserHandle_t a_hParser, const muChar_t cArgSep);
+       API_EXPORT(void) mupSetDecSep(muParserHandle_t a_hParser, const muChar_t cArgSep);
+       API_EXPORT(void) mupSetThousandsSep(muParserHandle_t a_hParser, const muChar_t cArgSep);
+       API_EXPORT(void) mupResetLocale(muParserHandle_t a_hParser);
+
+       // Add value recognition callbacks
+       API_EXPORT(void) mupAddValIdent(muParserHandle_t a_hParser, muIdentFun_t);
+
+       // Error handling
+       API_EXPORT(muBool_t) mupError(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupErrorReset(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupSetErrorHandler(muParserHandle_t a_hParser, muErrorHandler_t a_pErrHandler);
+       API_EXPORT(const muChar_t*) mupGetErrorMsg(muParserHandle_t a_hParser);
+       API_EXPORT(muInt_t) mupGetErrorCode(muParserHandle_t a_hParser);
+       API_EXPORT(muInt_t) mupGetErrorPos(muParserHandle_t a_hParser);
+       API_EXPORT(const muChar_t*) mupGetErrorToken(muParserHandle_t a_hParser);
+       //API_EXPORT(const muChar_t*) mupGetErrorExpr(muParserHandle_t a_hParser);
+
+       // This is used for .NET only. It creates a new variable allowing the dll to
+       // manage the variable rather than the .NET garbage collector.
+       API_EXPORT(muFloat_t*) mupCreateVar(void);
+       API_EXPORT(void) mupReleaseVar(muFloat_t*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // include guard
diff --git a/src/external/muparser/muParserDef.h b/src/external/muparser/muParserDef.h
new file mode 100644 (file)
index 0000000..7ce24f2
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MUP_DEF_H
+#define MUP_DEF_H
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <map>
+
+#include "muParserFixes.h"
+
+/** \file
+       \brief This file contains standard definitions used by the parser.
+*/
+
+/** \brief Define the base datatype for values.
+
+  This datatype must be a built in value type. You can not use custom classes.
+  It should be working with all types except "int"!
+*/
+#define MUP_BASETYPE double
+
+/** \brief Activate this option in order to compile with OpenMP support.
+
+  OpenMP is used only in the bulk mode it may increase the performance a bit.
+
+  !!! DO NOT ACTIVATE THIS MACRO HERE IF YOU USE CMAKE FOR BUILDING !!!
+
+  use the cmake option instead!
+*/
+//#define MUP_USE_OPENMP
+
+#if defined(_UNICODE)
+       /** \brief Definition of the basic parser string type. */
+       #define MUP_STRING_TYPE std::wstring
+
+       #if !defined(_T)
+               #define _T(x) L##x
+       #endif // not defined _T
+#else
+       #ifndef _T
+               #define _T(x) x
+       #endif
+
+       /** \brief Definition of the basic parser string type. */
+       #define MUP_STRING_TYPE std::string
+#endif
+
+/** \brief An assertion that does not kill the program. */
+#define MUP_ASSERT(COND)                                                                                       \
+            if (!(COND))                                                                                       \
+            {                                                                                                          \
+              stringstream_type ss;                                                                    \
+              ss << _T("Assertion \"") _T(#COND) _T("\" failed: ")     \
+                 << __FILE__ << _T(" line ")                                           \
+                 << __LINE__ << _T(".");                                                       \
+              throw ParserError( ecINTERNAL_ERROR, -1, ss.str());   \
+            }
+
+#if defined(_MSC_VER)
+       #pragma warning(push)
+       #pragma warning(disable : 26812) 
+#endif
+
+
+namespace mu
+{
+#if defined(_UNICODE)
+
+       /** \brief Encapsulate wcout. */
+       inline std::wostream& console()
+       {
+               return std::wcout;
+       }
+
+       /** \brief Encapsulate cin. */
+       inline std::wistream& console_in()
+       {
+               return std::wcin;
+       }
+
+#else
+
+       /** \brief Encapsulate cout.
+
+         Used for supporting UNICODE more easily.
+       */
+       inline std::ostream& console()
+       {
+               return std::cout;
+       }
+
+       /** \brief Encapsulate cin.
+
+         Used for supporting UNICODE more easily.
+       */
+       inline std::istream& console_in()
+       {
+               return std::cin;
+       }
+
+#endif
+
+       /** \brief Bytecode values.
+
+               \attention The order of the operator entries must match the order in ParserBase::c_DefaultOprt!
+       */
+       enum ECmdCode
+       {
+               // The following are codes for built in binary operators
+               // apart from built in operators the user has the opportunity to
+               // add user defined operators.
+               cmLE = 0,                       ///< Operator item:  less or equal
+               cmGE = 1,                       ///< Operator item:  greater or equal
+               cmNEQ = 2,                      ///< Operator item:  not equal
+               cmEQ = 3,                       ///< Operator item:  equals
+               cmLT = 4,                       ///< Operator item:  less than
+               cmGT = 5,                       ///< Operator item:  greater than
+               cmADD = 6,                      ///< Operator item:  add
+               cmSUB = 7,                      ///< Operator item:  subtract
+               cmMUL = 8,                      ///< Operator item:  multiply
+               cmDIV = 9,                      ///< Operator item:  division
+               cmPOW = 10,                     ///< Operator item:  y to the power of ...
+               cmLAND = 11,
+               cmLOR = 12,
+               cmASSIGN = 13,          ///< Operator item:  Assignment operator
+               cmBO = 14,                      ///< Operator item:  opening bracket
+               cmBC = 15,                      ///< Operator item:  closing bracket
+               cmIF = 16,                      ///< For use in the ternary if-then-else operator
+               cmELSE = 17,            ///< For use in the ternary if-then-else operator
+               cmENDIF = 18,           ///< For use in the ternary if-then-else operator
+               cmARG_SEP = 19,         ///< function argument separator
+               cmVAR = 20,                     ///< variable item
+               cmVAL = 21,                     ///< value item
+
+               // For optimization purposes
+               cmVARPOW2 = 22,
+               cmVARPOW3 = 23,
+               cmVARPOW4 = 24,
+               cmVARMUL = 25,
+
+               // operators and functions
+               cmFUNC = 26,            ///< Code for a generic function item
+               cmFUNC_STR,                     ///< Code for a function with a string parameter
+               cmFUNC_BULK,            ///< Special callbacks for Bulk mode with an additional parameter for the bulk index 
+               cmSTRING,                       ///< Code for a string token
+               cmOPRT_BIN,                     ///< user defined binary operator
+               cmOPRT_POSTFIX,         ///< code for postfix operators
+               cmOPRT_INFIX,           ///< code for infix operators
+               cmEND,                          ///< end of formula
+               cmUNKNOWN                       ///< uninitialized item
+       };
+
+       /** \brief Types internally used by the parser.
+       */
+       enum ETypeCode
+       {
+               tpSTR = 0,     ///< String type (Function arguments and constants only, no string variables)
+               tpDBL = 1,     ///< Floating point variables
+               tpVOID = 2      ///< Undefined type.
+       };
+
+
+       enum EParserVersionInfo
+       {
+               pviBRIEF,
+               pviFULL
+       };
+
+
+       /** \brief Parser operator precedence values. */
+       enum EOprtAssociativity
+       {
+               oaLEFT = 0,
+               oaRIGHT = 1,
+               oaNONE = 2
+       };
+
+
+       /** \brief Parser operator precedence values. */
+       enum EOprtPrecedence
+       {
+               // binary operators
+               prLOR = 1,
+               prLAND = 2,
+               prLOGIC = 3,    ///< logic operators
+               prCMP = 4,              ///< comparsion operators
+               prADD_SUB = 5,  ///< addition
+               prMUL_DIV = 6,  ///< multiplication/division
+               prPOW = 7,              ///< power operator priority (highest)
+
+               // infix operators
+               prINFIX = 6,    ///< Signs have a higher priority than ADD_SUB, but lower than power operator
+               prPOSTFIX = 6   ///< Postfix operator priority (currently unused)
+       };
+
+
+       /** \brief Error codes. */
+       enum EErrorCodes
+       {
+               // Formula syntax errors
+               ecUNEXPECTED_OPERATOR = 0,      ///< Unexpected binary operator found
+               ecUNASSIGNABLE_TOKEN = 1,       ///< Token can't be identified.
+               ecUNEXPECTED_EOF = 2,           ///< Unexpected end of formula. (Example: "2+sin(")
+               ecUNEXPECTED_ARG_SEP = 3,       ///< An unexpected comma has been found. (Example: "1,23")
+               ecUNEXPECTED_ARG = 4,           ///< An unexpected argument has been found
+               ecUNEXPECTED_VAL = 5,           ///< An unexpected value token has been found
+               ecUNEXPECTED_VAR = 6,           ///< An unexpected variable token has been found
+               ecUNEXPECTED_PARENS = 7,        ///< Unexpected Parenthesis, opening or closing
+               ecUNEXPECTED_STR = 8,           ///< A string has been found at an inapropriate position
+               ecSTRING_EXPECTED = 9,          ///< A string function has been called with a different type of argument
+               ecVAL_EXPECTED = 10,            ///< A numerical function has been called with a non value type of argument
+               ecMISSING_PARENS = 11,          ///< Missing parens. (Example: "3*sin(3")
+               ecUNEXPECTED_FUN = 12,          ///< Unexpected function found. (Example: "sin(8)cos(9)")
+               ecUNTERMINATED_STRING = 13,     ///< unterminated string constant. (Example: "3*valueof("hello)")
+               ecTOO_MANY_PARAMS = 14,         ///< Too many function parameters
+               ecTOO_FEW_PARAMS = 15,          ///< Too few function parameters. (Example: "ite(1<2,2)")
+               ecOPRT_TYPE_CONFLICT = 16,      ///< binary operators may only be applied to value items of the same type
+               ecSTR_RESULT = 17,                      ///< result is a string
+
+               // Invalid Parser input Parameters
+               ecINVALID_NAME = 18,                    ///< Invalid function, variable or constant name.
+               ecINVALID_BINOP_IDENT = 19,             ///< Invalid binary operator identifier
+               ecINVALID_INFIX_IDENT = 20,             ///< Invalid function, variable or constant name.
+               ecINVALID_POSTFIX_IDENT = 21,   ///< Invalid function, variable or constant name.
+
+               ecBUILTIN_OVERLOAD = 22, ///< Trying to overload builtin operator
+               ecINVALID_FUN_PTR = 23, ///< Invalid callback function pointer 
+               ecINVALID_VAR_PTR = 24, ///< Invalid variable pointer 
+               ecEMPTY_EXPRESSION = 25, ///< The Expression is empty
+               ecNAME_CONFLICT = 26, ///< Name conflict
+               ecOPT_PRI = 27, ///< Invalid operator priority
+               // 
+               ecDOMAIN_ERROR = 28, ///< catch division by zero, sqrt(-1), log(0) (currently unused)
+               ecDIV_BY_ZERO = 29, ///< Division by zero (currently unused)
+               ecGENERIC = 30, ///< Generic error
+               ecLOCALE = 31, ///< Conflict with current locale
+
+               ecUNEXPECTED_CONDITIONAL = 32,
+               ecMISSING_ELSE_CLAUSE = 33,
+               ecMISPLACED_COLON = 34,
+
+               ecUNREASONABLE_NUMBER_OF_COMPUTATIONS = 35,
+
+               ecIDENTIFIER_TOO_LONG = 36, ///< Thrown when an identifier with more then 255 characters is used.
+
+               ecEXPRESSION_TOO_LONG = 37, ///< Throw an exception if the expression has more than 10000 characters. (an arbitrary limit)
+
+               ecINVALID_CHARACTERS_FOUND = 38,///< The expression or identifier contains invalid non printable characters
+
+               // internal errors
+               ecINTERNAL_ERROR = 39,    ///< Internal error of any kind.
+
+               // The last two are special entries 
+               ecCOUNT,                      ///< This is no error code, It just stores just the total number of error codes
+               ecUNDEFINED = -1  ///< Undefined message, placeholder to detect unassigned error messages
+       };
+
+       //------------------------------------------------------------------------------
+       // Basic Types
+       //------------------------------------------------------------------------------
+
+       /** \brief The numeric datatype used by the parser.
+
+         Normally this is a floating point type either single or double precision.
+       */
+       typedef MUP_BASETYPE value_type;
+
+       /** \brief The stringtype used by the parser.
+
+         Depends on whether UNICODE is used or not.
+       */
+       typedef MUP_STRING_TYPE string_type;
+
+       /** \brief The character type used by the parser.
+
+         Depends on whether UNICODE is used or not.
+       */
+       typedef string_type::value_type char_type;
+
+       /** \brief Typedef for easily using stringstream that respect the parser stringtype. */
+       typedef std::basic_stringstream<char_type, std::char_traits<char_type>, std::allocator<char_type> > stringstream_type;
+
+       // Data container types
+
+       /** \brief Type used for storing variables. */
+       typedef std::map<string_type, value_type*> varmap_type;
+
+       /** \brief Type used for storing constants. */
+       typedef std::map<string_type, value_type> valmap_type;
+
+       /** \brief Type for assigning a string name to an index in the internal string table. */
+       typedef std::map<string_type, std::size_t> strmap_type;
+
+       // Parser callbacks
+
+       /** \brief Callback type used for functions without arguments. */
+       typedef value_type(*generic_fun_type)();
+
+       /** \brief Callback type used for functions without arguments. */
+       typedef value_type(*fun_type0)();
+
+       /** \brief Callback type used for functions with a single arguments. */
+       typedef value_type(*fun_type1)(value_type);
+
+       /** \brief Callback type used for functions with two arguments. */
+       typedef value_type(*fun_type2)(value_type, value_type);
+
+       /** \brief Callback type used for functions with three arguments. */
+       typedef value_type(*fun_type3)(value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with four arguments. */
+       typedef value_type(*fun_type4)(value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with five arguments. */
+       typedef value_type(*fun_type5)(value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with six arguments. */
+       typedef value_type(*fun_type6)(value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with seven arguments. */
+       typedef value_type(*fun_type7)(value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with eight arguments. */
+       typedef value_type(*fun_type8)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with nine arguments. */
+       typedef value_type(*fun_type9)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with ten arguments. */
+       typedef value_type(*fun_type10)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions without arguments. */
+       typedef value_type(*bulkfun_type0)(int, int);
+
+       /** \brief Callback type used for functions with a single arguments. */
+       typedef value_type(*bulkfun_type1)(int, int, value_type);
+
+       /** \brief Callback type used for functions with two arguments. */
+       typedef value_type(*bulkfun_type2)(int, int, value_type, value_type);
+
+       /** \brief Callback type used for functions with three arguments. */
+       typedef value_type(*bulkfun_type3)(int, int, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with four arguments. */
+       typedef value_type(*bulkfun_type4)(int, int, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with five arguments. */
+       typedef value_type(*bulkfun_type5)(int, int, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with six arguments. */
+       typedef value_type(*bulkfun_type6)(int, int, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with seven arguments. */
+       typedef value_type(*bulkfun_type7)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with eight arguments. */
+       typedef value_type(*bulkfun_type8)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with nine arguments. */
+       typedef value_type(*bulkfun_type9)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with ten arguments. */
+       typedef value_type(*bulkfun_type10)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with a variable argument list. */
+       typedef value_type(*multfun_type)(const value_type*, int);
+
+       /** \brief Callback type used for functions taking a string as an argument. */
+       typedef value_type(*strfun_type1)(const char_type*);
+
+       /** \brief Callback type used for functions taking a string and a value as arguments. */
+       typedef value_type(*strfun_type2)(const char_type*, value_type);
+
+       /** \brief Callback type used for functions taking a string and two values as arguments. */
+       typedef value_type(*strfun_type3)(const char_type*, value_type, value_type);
+
+       /** \brief Callback type used for functions taking a string and a value as arguments. */
+       typedef value_type(*strfun_type4)(const char_type*, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions taking a string and two values as arguments. */
+       typedef value_type(*strfun_type5)(const char_type*, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback used for functions that identify values in a string. */
+       typedef int (*identfun_type)(const char_type* sExpr, int* nPos, value_type* fVal);
+
+       /** \brief Callback used for variable creation factory functions. */
+       typedef value_type* (*facfun_type)(const char_type*, void*);
+
+       static const int MaxLenExpression = 5000;
+       static const int MaxLenIdentifier = 100;
+       static const string_type ParserVersion = string_type(_T("2.3.2"));
+       static const string_type ParserVersionDate = string_type(_T("20200617"));
+} // end of namespace
+
+#if defined(_MSC_VER)
+       #pragma warning(pop)
+#endif
+
+#endif
+
diff --git a/src/external/muparser/muParserError.cpp b/src/external/muparser/muParserError.cpp
new file mode 100644 (file)
index 0000000..2a192f8
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "muParserError.h"
+#include <exception>
+
+#if defined(_MSC_VER)
+       #pragma warning(push)
+       #pragma warning(disable : 26812) // What the fuck is the point of this warning?
+#endif
+
+namespace mu
+{
+       //------------------------------------------------------------------------------
+       const ParserErrorMsg& ParserErrorMsg::Instance()
+       {
+               static const ParserErrorMsg instance;
+               return instance;
+       }
+
+       //------------------------------------------------------------------------------
+       string_type ParserErrorMsg::operator[](unsigned a_iIdx) const
+       {
+               return (a_iIdx < m_vErrMsg.size()) ? m_vErrMsg[a_iIdx] : string_type();
+       }
+
+       //---------------------------------------------------------------------------
+       ParserErrorMsg::ParserErrorMsg()
+               :m_vErrMsg(0)
+       {
+               m_vErrMsg.resize(ecCOUNT);
+
+               m_vErrMsg[ecUNASSIGNABLE_TOKEN] = _T("Unexpected token \"$TOK$\" found at position $POS$.");
+               m_vErrMsg[ecINTERNAL_ERROR] = _T("Internal error");
+               m_vErrMsg[ecINVALID_NAME] = _T("Invalid function-, variable- or constant name: \"$TOK$\".");
+               m_vErrMsg[ecINVALID_BINOP_IDENT] = _T("Invalid binary operator identifier: \"$TOK$\".");
+               m_vErrMsg[ecINVALID_INFIX_IDENT] = _T("Invalid infix operator identifier: \"$TOK$\".");
+               m_vErrMsg[ecINVALID_POSTFIX_IDENT] = _T("Invalid postfix operator identifier: \"$TOK$\".");
+               m_vErrMsg[ecINVALID_FUN_PTR] = _T("Invalid pointer to callback function.");
+               m_vErrMsg[ecEMPTY_EXPRESSION] = _T("Expression is empty.");
+               m_vErrMsg[ecINVALID_VAR_PTR] = _T("Invalid pointer to variable.");
+               m_vErrMsg[ecUNEXPECTED_OPERATOR] = _T("Unexpected operator \"$TOK$\" found at position $POS$");
+               m_vErrMsg[ecUNEXPECTED_EOF] = _T("Unexpected end of expression at position $POS$");
+               m_vErrMsg[ecUNEXPECTED_ARG_SEP] = _T("Unexpected argument separator at position $POS$");
+               m_vErrMsg[ecUNEXPECTED_PARENS] = _T("Unexpected parenthesis \"$TOK$\" at position $POS$");
+               m_vErrMsg[ecUNEXPECTED_FUN] = _T("Unexpected function \"$TOK$\" at position $POS$");
+               m_vErrMsg[ecUNEXPECTED_VAL] = _T("Unexpected value \"$TOK$\" found at position $POS$");
+               m_vErrMsg[ecUNEXPECTED_VAR] = _T("Unexpected variable \"$TOK$\" found at position $POS$");
+               m_vErrMsg[ecUNEXPECTED_ARG] = _T("Function arguments used without a function (position: $POS$)");
+               m_vErrMsg[ecMISSING_PARENS] = _T("Missing parenthesis");
+               m_vErrMsg[ecTOO_MANY_PARAMS] = _T("Too many parameters for function \"$TOK$\" at expression position $POS$");
+               m_vErrMsg[ecTOO_FEW_PARAMS] = _T("Too few parameters for function \"$TOK$\" at expression position $POS$");
+               m_vErrMsg[ecDIV_BY_ZERO] = _T("Divide by zero");
+               m_vErrMsg[ecDOMAIN_ERROR] = _T("Domain error");
+               m_vErrMsg[ecNAME_CONFLICT] = _T("Name conflict");
+               m_vErrMsg[ecOPT_PRI] = _T("Invalid value for operator priority (must be greater or equal to zero).");
+               m_vErrMsg[ecBUILTIN_OVERLOAD] = _T("user defined binary operator \"$TOK$\" conflicts with a built in operator.");
+               m_vErrMsg[ecUNEXPECTED_STR] = _T("Unexpected string token found at position $POS$.");
+               m_vErrMsg[ecUNTERMINATED_STRING] = _T("Unterminated string starting at position $POS$.");
+               m_vErrMsg[ecSTRING_EXPECTED] = _T("String function called with a non string type of argument.");
+               m_vErrMsg[ecVAL_EXPECTED] = _T("String value used where a numerical argument is expected.");
+               m_vErrMsg[ecOPRT_TYPE_CONFLICT] = _T("No suitable overload for operator \"$TOK$\" at position $POS$.");
+               m_vErrMsg[ecSTR_RESULT] = _T("Strings must only be used as function arguments!");
+               m_vErrMsg[ecGENERIC] = _T("Parser error.");
+               m_vErrMsg[ecLOCALE] = _T("Decimal separator is identic to function argument separator.");
+               m_vErrMsg[ecUNEXPECTED_CONDITIONAL] = _T("The \"$TOK$\" operator must be preceded by a closing bracket.");
+               m_vErrMsg[ecMISSING_ELSE_CLAUSE] = _T("If-then-else operator is missing an else clause");
+               m_vErrMsg[ecMISPLACED_COLON] = _T("Misplaced colon at position $POS$");
+               m_vErrMsg[ecUNREASONABLE_NUMBER_OF_COMPUTATIONS] = _T("Number of computations to small for bulk mode. (Vectorisation overhead too costly)");
+               m_vErrMsg[ecIDENTIFIER_TOO_LONG] = _T("Identifier too long.");
+               m_vErrMsg[ecEXPRESSION_TOO_LONG] = _T("Expression too long.");
+               m_vErrMsg[ecINVALID_CHARACTERS_FOUND] = _T("Invalid non printable characters found in expression/identifer!");
+
+               for (int i = 0; i < ecCOUNT; ++i)
+               {
+                       if (!m_vErrMsg[i].length())
+                               throw std::runtime_error("Error definitions are incomplete!");
+               }
+       }
+
+       //---------------------------------------------------------------------------
+       //
+       //  ParserError class
+       //
+       //---------------------------------------------------------------------------
+
+       /** \brief Default constructor. */
+       ParserError::ParserError()
+               :m_strMsg()
+               , m_strFormula()
+               , m_strTok()
+               , m_iPos(-1)
+               , m_iErrc(ecUNDEFINED)
+               , m_ErrMsg(ParserErrorMsg::Instance())
+       {
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief This Constructor is used for internal exceptions only.
+
+         It does not contain any information but the error code.
+       */
+       ParserError::ParserError(EErrorCodes a_iErrc)
+               :m_strMsg()
+               , m_strFormula()
+               , m_strTok()
+               , m_iPos(-1)
+               , m_iErrc(a_iErrc)
+               , m_ErrMsg(ParserErrorMsg::Instance())
+       {
+               m_strMsg = m_ErrMsg[m_iErrc];
+               stringstream_type stream;
+               stream << (int)m_iPos;
+               ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());
+               ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Construct an error from a message text. */
+       ParserError::ParserError(const string_type& sMsg)
+               :m_ErrMsg(ParserErrorMsg::Instance())
+       {
+               Reset();
+               m_strMsg = sMsg;
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Construct an error object.
+               \param [in] a_iErrc the error code.
+               \param [in] sTok The token string related to this error.
+               \param [in] sExpr The expression related to the error.
+               \param [in] a_iPos the position in the expression where the error occurred.
+       */
+       ParserError::ParserError(EErrorCodes iErrc,
+               const string_type& sTok,
+               const string_type& sExpr,
+               int iPos)
+               :m_strMsg()
+               , m_strFormula(sExpr)
+               , m_strTok(sTok)
+               , m_iPos(iPos)
+               , m_iErrc(iErrc)
+               , m_ErrMsg(ParserErrorMsg::Instance())
+       {
+               m_strMsg = m_ErrMsg[m_iErrc];
+               stringstream_type stream;
+               stream << (int)m_iPos;
+               ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());
+               ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Construct an error object.
+               \param [in] iErrc the error code.
+               \param [in] iPos the position in the expression where the error occurred.
+               \param [in] sTok The token string related to this error.
+       */
+       ParserError::ParserError(EErrorCodes iErrc, int iPos, const string_type& sTok)
+               :m_strMsg()
+               , m_strFormula()
+               , m_strTok(sTok)
+               , m_iPos(iPos)
+               , m_iErrc(iErrc)
+               , m_ErrMsg(ParserErrorMsg::Instance())
+       {
+               m_strMsg = m_ErrMsg[m_iErrc];
+               stringstream_type stream;
+               stream << (int)m_iPos;
+               ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());
+               ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Construct an error object.
+               \param [in] szMsg The error message text.
+               \param [in] iPos the position related to the error.
+               \param [in] sTok The token string related to this error.
+       */
+       ParserError::ParserError(const char_type* szMsg, int iPos, const string_type& sTok)
+               :m_strMsg(szMsg)
+               , m_strFormula()
+               , m_strTok(sTok)
+               , m_iPos(iPos)
+               , m_iErrc(ecGENERIC)
+               , m_ErrMsg(ParserErrorMsg::Instance())
+       {
+               stringstream_type stream;
+               stream << (int)m_iPos;
+               ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());
+               ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Copy constructor. */
+       ParserError::ParserError(const ParserError& a_Obj)
+               :m_strMsg(a_Obj.m_strMsg)
+               , m_strFormula(a_Obj.m_strFormula)
+               , m_strTok(a_Obj.m_strTok)
+               , m_iPos(a_Obj.m_iPos)
+               , m_iErrc(a_Obj.m_iErrc)
+               , m_ErrMsg(ParserErrorMsg::Instance())
+       {
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Assignment operator. */
+       ParserError& ParserError::operator=(const ParserError& a_Obj)
+       {
+               if (this == &a_Obj)
+                       return *this;
+
+               m_strMsg = a_Obj.m_strMsg;
+               m_strFormula = a_Obj.m_strFormula;
+               m_strTok = a_Obj.m_strTok;
+               m_iPos = a_Obj.m_iPos;
+               m_iErrc = a_Obj.m_iErrc;
+               return *this;
+       }
+
+       //------------------------------------------------------------------------------
+       ParserError::~ParserError()
+       {}
+
+       //------------------------------------------------------------------------------
+       /** \brief Replace all occurrences of a substring with another string.
+               \param strFind The string that shall be replaced.
+               \param strReplaceWith The string that should be inserted instead of strFind
+       */
+       void ParserError::ReplaceSubString(string_type& strSource,
+               const string_type& strFind,
+               const string_type& strReplaceWith)
+       {
+               string_type strResult;
+               string_type::size_type iPos(0), iNext(0);
+
+               for (;;)
+               {
+                       iNext = strSource.find(strFind, iPos);
+                       strResult.append(strSource, iPos, iNext - iPos);
+
+                       if (iNext == string_type::npos)
+                               break;
+
+                       strResult.append(strReplaceWith);
+                       iPos = iNext + strFind.length();
+               }
+
+               strSource.swap(strResult);
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Reset the error object. */
+       void ParserError::Reset()
+       {
+               m_strMsg = _T("");
+               m_strFormula = _T("");
+               m_strTok = _T("");
+               m_iPos = -1;
+               m_iErrc = ecUNDEFINED;
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Set the expression related to this error. */
+       void ParserError::SetFormula(const string_type& a_strFormula)
+       {
+               m_strFormula = a_strFormula;
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief gets the expression related tp this error.*/
+       const string_type& ParserError::GetExpr() const
+       {
+               return m_strFormula;
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Returns the message string for this error. */
+       const string_type& ParserError::GetMsg() const
+       {
+               return m_strMsg;
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Return the formula position related to the error.
+
+         If the error is not related to a distinct position this will return -1
+       */
+       int ParserError::GetPos() const
+       {
+               return m_iPos;
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Return string related with this token (if available). */
+       const string_type& ParserError::GetToken() const
+       {
+               return m_strTok;
+       }
+
+       //------------------------------------------------------------------------------
+       /** \brief Return the error code. */
+       EErrorCodes ParserError::GetCode() const
+       {
+               return m_iErrc;
+       }
+} // namespace mu
+
+#if defined(_MSC_VER)
+       #pragma warning(pop)
+#endif
diff --git a/src/external/muparser/muParserError.h b/src/external/muparser/muParserError.h
new file mode 100644 (file)
index 0000000..438b8b6
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_ERROR_H
+#define MU_PARSER_ERROR_H
+
+#include <stdexcept>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <memory>
+
+#include "muParserDef.h"
+
+/** \file
+       \brief This file defines the error class used by the parser.
+*/
+
+namespace mu
+{
+       /** \brief A class that handles the error messages.     */
+       class ParserErrorMsg final
+       {
+       public:
+               static const ParserErrorMsg& Instance();
+               string_type operator[](unsigned a_iIdx) const;
+
+       private:
+               ParserErrorMsg& operator=(const ParserErrorMsg&) = delete;
+               ParserErrorMsg(const ParserErrorMsg&) = delete;
+               ParserErrorMsg();
+
+               ~ParserErrorMsg() = default;
+
+               std::vector<string_type>  m_vErrMsg;  ///< A vector with the predefined error messages
+       };
+
+
+       /** \brief Error class of the parser.
+
+         Part of the math parser package.
+       */
+       class API_EXPORT_CXX ParserError
+       {
+       private:
+
+               /** \brief Replace all ocuurences of a substring with another string. */
+               void ReplaceSubString(string_type& strSource, const string_type& strFind, const string_type& strReplaceWith);
+               void Reset();
+
+       public:
+
+               ParserError();
+               explicit ParserError(EErrorCodes a_iErrc);
+               explicit ParserError(const string_type& sMsg);
+               ParserError(EErrorCodes a_iErrc, const string_type& sTok, const string_type& sFormula = string_type(), int a_iPos = -1);
+               ParserError(EErrorCodes a_iErrc, int a_iPos, const string_type& sTok);
+               ParserError(const char_type* a_szMsg, int a_iPos = -1, const string_type& sTok = string_type());
+               ParserError(const ParserError& a_Obj);
+
+               ParserError& operator=(const ParserError& a_Obj);
+               ~ParserError();
+
+               void SetFormula(const string_type& a_strFormula);
+               const string_type& GetExpr() const;
+               const string_type& GetMsg() const;
+               int GetPos() const;
+               const string_type& GetToken() const;
+               EErrorCodes GetCode() const;
+
+       private:
+               string_type m_strMsg;     ///< The message string
+               string_type m_strFormula; ///< Formula string
+               string_type m_strTok;     ///< Token related with the error
+               int m_iPos;               ///< Formula position related to the error 
+               EErrorCodes m_iErrc;      ///< Error code
+               const ParserErrorMsg& m_ErrMsg;
+       };
+
+} // namespace mu
+
+#endif
+
diff --git a/src/external/muparser/muParserFixes.h b/src/external/muparser/muParserFixes.h
new file mode 100644 (file)
index 0000000..59aecad
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_FIXES_H
+#define MU_PARSER_FIXES_H
+
+/** \file
+       \brief This file contains compatibility fixes for some platforms.
+*/
+
+//
+// Compatibility fixes
+//
+
+/* From http://gcc.gnu.org/wiki/Visibility */
+/* Generic helper definitions for shared library support */
+#if defined _WIN32 || defined __CYGWIN__
+       #define MUPARSER_HELPER_DLL_IMPORT __declspec(dllimport)
+       #define MUPARSER_HELPER_DLL_EXPORT __declspec(dllexport)
+       #define MUPARSER_HELPER_DLL_LOCAL
+#else
+       #if __GNUC__ >= 4
+               #define MUPARSER_HELPER_DLL_IMPORT __attribute__ ((visibility ("default")))
+               #define MUPARSER_HELPER_DLL_EXPORT __attribute__ ((visibility ("default")))
+               #define MUPARSER_HELPER_DLL_LOCAL  __attribute__ ((visibility ("hidden")))
+       #else
+               #define MUPARSER_HELPER_DLL_IMPORT
+               #define MUPARSER_HELPER_DLL_EXPORT
+               #define MUPARSER_HELPER_DLL_LOCAL
+       #endif
+#endif
+
+/* 
+       Now we use the generic helper definitions above to define API_EXPORT_CXX and MUPARSER_LOCAL.
+       API_EXPORT_CXX is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build)
+       MUPARSER_LOCAL is used for non-api symbols.
+*/
+
+#ifndef MUPARSER_STATIC /* defined if muParser is compiled as a DLL */
+
+       #ifdef MUPARSERLIB_EXPORTS /* defined if we are building the muParser DLL (instead of using it) */
+               #define API_EXPORT_CXX MUPARSER_HELPER_DLL_EXPORT
+       #else
+               #define API_EXPORT_CXX MUPARSER_HELPER_DLL_IMPORT
+       #endif /* MUPARSER_DLL_EXPORTS */
+       #define MUPARSER_LOCAL MUPARSER_HELPER_DLL_LOCAL
+
+#else /* MUPARSER_STATIC is defined: this means muParser is a static lib. */
+
+       #define API_EXPORT_CXX
+       #define MUPARSER_LOCAL
+
+#endif /* !MUPARSER_STATIC */
+
+
+#ifdef _WIN32
+       #define API_EXPORT(TYPE) API_EXPORT_CXX TYPE __cdecl
+#else
+       #define API_EXPORT(TYPE) TYPE
+#endif
+
+
+#endif // include guard
+
+
diff --git a/src/external/muparser/muParserInt.cpp b/src/external/muparser/muParserInt.cpp
new file mode 100644 (file)
index 0000000..f79ba1a
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "muParserInt.h"
+
+#include <cmath>
+#include <algorithm>
+#include <numeric>
+
+using namespace std;
+
+/** \file
+       \brief Implementation of a parser using integer value.
+*/
+
+/** \brief Namespace for mathematical applications. */
+namespace mu
+{
+       value_type ParserInt::Abs(value_type v) { return (value_type)Round(fabs((double)v)); }
+       value_type ParserInt::Sign(value_type v) { return (Round(v) < 0) ? -1 : (Round(v) > 0) ? 1 : 0; }
+       value_type ParserInt::Ite(value_type v1, value_type v2, value_type v3) { return (Round(v1) == 1) ? Round(v2) : Round(v3); }
+       value_type ParserInt::Add(value_type v1, value_type v2) { return Round(v1) + Round(v2); }
+       value_type ParserInt::Sub(value_type v1, value_type v2) { return Round(v1) - Round(v2); }
+       value_type ParserInt::Mul(value_type v1, value_type v2) { return Round(v1) * Round(v2); }
+       value_type ParserInt::Div(value_type v1, value_type v2) { return Round(v1) / Round(v2); }
+       value_type ParserInt::Mod(value_type v1, value_type v2) { return Round(v1) % Round(v2); }
+       value_type ParserInt::Shr(value_type v1, value_type v2) { return Round(v1) >> Round(v2); }
+       value_type ParserInt::Shl(value_type v1, value_type v2) { return Round(v1) << Round(v2); }
+       value_type ParserInt::LogAnd(value_type v1, value_type v2) { return Round(v1) & Round(v2); }
+       value_type ParserInt::LogOr(value_type v1, value_type v2) { return Round(v1) | Round(v2); }
+       value_type ParserInt::And(value_type v1, value_type v2) { return Round(v1) && Round(v2); }
+       value_type ParserInt::Or(value_type v1, value_type v2) { return Round(v1) || Round(v2); }
+       value_type ParserInt::Less(value_type v1, value_type v2) { return Round(v1) < Round(v2); }
+       value_type ParserInt::Greater(value_type v1, value_type v2) { return Round(v1) > Round(v2); }
+       value_type ParserInt::LessEq(value_type v1, value_type v2) { return Round(v1) <= Round(v2); }
+       value_type ParserInt::GreaterEq(value_type v1, value_type v2) { return Round(v1) >= Round(v2); }
+       value_type ParserInt::Equal(value_type v1, value_type v2) { return Round(v1) == Round(v2); }
+       value_type ParserInt::NotEqual(value_type v1, value_type v2) { return Round(v1) != Round(v2); }
+       value_type ParserInt::Not(value_type v) { return !Round(v); }
+
+       value_type ParserInt::Pow(value_type v1, value_type v2)
+       {
+               return std::pow((double)Round(v1), (double)Round(v2));
+       }
+
+
+       value_type ParserInt::UnaryMinus(value_type v)
+       {
+               return -Round(v);
+       }
+
+
+       value_type ParserInt::Sum(const value_type* a_afArg, int a_iArgc)
+       {
+               if (!a_iArgc)
+                       throw ParserError(_T("too few arguments for function sum."));
+
+               value_type fRes = 0;
+               for (int i = 0; i < a_iArgc; ++i)
+                       fRes += a_afArg[i];
+
+               return fRes;
+       }
+
+
+       value_type ParserInt::Min(const value_type* a_afArg, int a_iArgc)
+       {
+               if (!a_iArgc)
+                       throw ParserError(_T("too few arguments for function min."));
+
+               value_type fRes = a_afArg[0];
+               for (int i = 0; i < a_iArgc; ++i)
+                       fRes = std::min(fRes, a_afArg[i]);
+
+               return fRes;
+       }
+
+
+       value_type ParserInt::Max(const value_type* a_afArg, int a_iArgc)
+       {
+               if (!a_iArgc)
+                       throw ParserError(_T("too few arguments for function min."));
+
+               value_type fRes = a_afArg[0];
+               for (int i = 0; i < a_iArgc; ++i)
+                       fRes = std::max(fRes, a_afArg[i]);
+
+               return fRes;
+       }
+
+
+       int ParserInt::IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal)
+       {
+               string_type buf(a_szExpr);
+               std::size_t pos = buf.find_first_not_of(_T("0123456789"));
+
+               if (pos == std::string::npos)
+                       return 0;
+
+               stringstream_type stream(buf.substr(0, pos));
+               int iVal(0);
+
+               stream >> iVal;
+               if (stream.fail())
+                       return 0;
+
+               stringstream_type::pos_type iEnd = stream.tellg();   // Position after reading
+               if (stream.fail())
+                       iEnd = stream.str().length();
+
+               if (iEnd == (stringstream_type::pos_type) - 1)
+                       return 0;
+
+               *a_iPos += (int)iEnd;
+               *a_fVal = (value_type)iVal;
+               return 1;
+       }
+
+
+       /** \brief Check a given position in the expression for the presence of
+                          a hex value.
+               \param a_szExpr Pointer to the expression string
+               \param [in/out] a_iPos Pointer to an integer value holding the current parsing
+                          position in the expression.
+               \param [out] a_fVal Pointer to the position where the detected value shall be stored.
+
+         Hey values must be prefixed with "0x" in order to be detected properly.
+       */
+       int ParserInt::IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal)
+       {
+               if (a_szExpr[1] == 0 || (a_szExpr[0] != '0' || a_szExpr[1] != 'x'))
+                       return 0;
+
+               unsigned iVal(0);
+
+               // New code based on streams for UNICODE compliance:
+               stringstream_type::pos_type nPos(0);
+               stringstream_type ss(a_szExpr + 2);
+               ss >> std::hex >> iVal;
+               nPos = ss.tellg();
+
+               if (nPos == (stringstream_type::pos_type)0)
+                       return 1;
+
+               *a_iPos += (int)(2 + nPos);
+               *a_fVal = (value_type)iVal;
+               return 1;
+       }
+
+
+       int ParserInt::IsBinVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal)
+       {
+               if (a_szExpr[0] != '#')
+                       return 0;
+
+               unsigned iVal(0),
+                       iBits(sizeof(iVal) * 8),
+                       i(0);
+
+               for (i = 0; (a_szExpr[i + 1] == '0' || a_szExpr[i + 1] == '1') && i < iBits; ++i)
+                       iVal |= (int)(a_szExpr[i + 1] == '1') << ((iBits - 1) - i);
+
+               if (i == 0)
+                       return 0;
+
+               if (i == iBits)
+                       throw exception_type(_T("Binary to integer conversion error (overflow)."));
+
+               *a_fVal = (unsigned)(iVal >> (iBits - i));
+               *a_iPos += i + 1;
+
+               return 1;
+       }
+
+
+       /** \brief Constructor.
+
+               Call ParserBase class constructor and trigger Function, Operator and Constant initialization.
+       */
+       ParserInt::ParserInt()
+               :ParserBase()
+       {
+               AddValIdent(IsVal);    // lowest priority
+               AddValIdent(IsBinVal);
+               AddValIdent(IsHexVal); // highest priority
+
+               InitCharSets();
+               InitFun();
+               InitOprt();
+       }
+
+
+       void ParserInt::InitConst()
+       {
+       }
+
+
+       void ParserInt::InitCharSets()
+       {
+               DefineNameChars(_T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+               DefineOprtChars(_T("+-*^/?<>=!%&|~'_"));
+               DefineInfixOprtChars(_T("/+-*^?<>=!%&|~'_"));
+       }
+
+
+       /** \brief Initialize the default functions. */
+       void ParserInt::InitFun()
+       {
+               DefineFun(_T("sign"), Sign);
+               DefineFun(_T("abs"), Abs);
+               DefineFun(_T("if"), Ite);
+               DefineFun(_T("sum"), Sum);
+               DefineFun(_T("min"), Min);
+               DefineFun(_T("max"), Max);
+       }
+
+
+       /** \brief Initialize operators. */
+       void ParserInt::InitOprt()
+       {
+               // disable all built in operators, not all of them useful for integer numbers
+               // (they don't do rounding of values)
+               EnableBuiltInOprt(false);
+
+               // Disable all built in operators, they won't work with integer numbers
+               // since they are designed for floating point numbers
+               DefineInfixOprt(_T("-"), UnaryMinus);
+               DefineInfixOprt(_T("!"), Not);
+
+               DefineOprt(_T("&"), LogAnd, prLOGIC);
+               DefineOprt(_T("|"), LogOr, prLOGIC);
+               DefineOprt(_T("&&"), And, prLOGIC);
+               DefineOprt(_T("||"), Or, prLOGIC);
+
+               DefineOprt(_T("<"), Less, prCMP);
+               DefineOprt(_T(">"), Greater, prCMP);
+               DefineOprt(_T("<="), LessEq, prCMP);
+               DefineOprt(_T(">="), GreaterEq, prCMP);
+               DefineOprt(_T("=="), Equal, prCMP);
+               DefineOprt(_T("!="), NotEqual, prCMP);
+
+               DefineOprt(_T("+"), Add, prADD_SUB);
+               DefineOprt(_T("-"), Sub, prADD_SUB);
+
+               DefineOprt(_T("*"), Mul, prMUL_DIV);
+               DefineOprt(_T("/"), Div, prMUL_DIV);
+               DefineOprt(_T("%"), Mod, prMUL_DIV);
+
+               DefineOprt(_T("^"), Pow, prPOW, oaRIGHT);
+               DefineOprt(_T(">>"), Shr, prMUL_DIV + 1);
+               DefineOprt(_T("<<"), Shl, prMUL_DIV + 1);
+       }
+
+} // namespace mu
diff --git a/src/external/muparser/muParserInt.h b/src/external/muparser/muParserInt.h
new file mode 100644 (file)
index 0000000..dd772cf
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_INT_H
+#define MU_PARSER_INT_H
+
+#include "muParserBase.h"
+#include <vector>
+
+
+/** \file
+       \brief Definition of a parser using integer value.
+*/
+
+
+namespace mu
+{
+
+       /** \brief Mathematical expressions parser.
+
+         This version of the parser handles only integer numbers. It disables the built in operators thus it is
+         slower than muParser. Integer values are stored in the double value_type and converted if needed.
+       */
+       class ParserInt : public ParserBase
+       {
+       private:
+               static int  Round(value_type v) { return (int)(v + ((v >= 0) ? 0.5 : -0.5)); };
+
+               static value_type  Abs(value_type);
+               static value_type  Sign(value_type);
+               static value_type  Ite(value_type, value_type, value_type);
+               // !! The unary Minus is a MUST, otherwise you can't use negative signs !!
+               static value_type  UnaryMinus(value_type);
+               // Functions with variable number of arguments
+               static value_type  Sum(const value_type* a_afArg, int a_iArgc);  // sum
+               static value_type  Min(const value_type* a_afArg, int a_iArgc);  // minimum
+               static value_type  Max(const value_type* a_afArg, int a_iArgc);  // maximum
+               // binary operator callbacks
+               static value_type  Add(value_type v1, value_type v2);
+               static value_type  Sub(value_type v1, value_type v2);
+               static value_type  Mul(value_type v1, value_type v2);
+               static value_type  Div(value_type v1, value_type v2);
+               static value_type  Mod(value_type v1, value_type v2);
+               static value_type  Pow(value_type v1, value_type v2);
+               static value_type  Shr(value_type v1, value_type v2);
+               static value_type  Shl(value_type v1, value_type v2);
+               static value_type  LogAnd(value_type v1, value_type v2);
+               static value_type  LogOr(value_type v1, value_type v2);
+               static value_type  And(value_type v1, value_type v2);
+               static value_type  Or(value_type v1, value_type v2);
+               static value_type  Xor(value_type v1, value_type v2);
+               static value_type  Less(value_type v1, value_type v2);
+               static value_type  Greater(value_type v1, value_type v2);
+               static value_type  LessEq(value_type v1, value_type v2);
+               static value_type  GreaterEq(value_type v1, value_type v2);
+               static value_type  Equal(value_type v1, value_type v2);
+               static value_type  NotEqual(value_type v1, value_type v2);
+               static value_type  Not(value_type v1);
+
+               static int IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal);
+               static int IsBinVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal);
+               static int IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal);
+
+               /** \brief A facet class used to change decimal and thousands separator. */
+               template<class TChar>
+               class change_dec_sep : public std::numpunct<TChar>
+               {
+               public:
+
+                       explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3)
+                               :std::numpunct<TChar>()
+                               , m_cDecPoint(cDecSep)
+                               , m_cThousandsSep(cThousandsSep)
+                               , m_nGroup(nGroup)
+                       {}
+
+               protected:
+
+                       virtual char_type do_decimal_point() const
+                       {
+                               return m_cDecPoint;
+                       }
+
+                       virtual char_type do_thousands_sep() const
+                       {
+                               return m_cThousandsSep;
+                       }
+
+                       virtual std::string do_grouping() const
+                       {
+                               // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4
+                               // courtesy of Jens Bartsch
+                               // original code:
+                               //        return std::string(1, (char)m_nGroup); 
+                               // new code:
+                               return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX));
+                       }
+
+               private:
+
+                       int m_nGroup;
+                       char_type m_cDecPoint;
+                       char_type m_cThousandsSep;
+               };
+
+       public:
+               ParserInt();
+
+               virtual void InitFun();
+               virtual void InitOprt();
+               virtual void InitConst();
+               virtual void InitCharSets();
+       };
+
+} // namespace mu
+
+#endif
+
diff --git a/src/external/muparser/muParserTemplateMagic.h b/src/external/muparser/muParserTemplateMagic.h
new file mode 100644 (file)
index 0000000..1b26d00
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_TEMPLATE_MAGIC_H
+#define MU_PARSER_TEMPLATE_MAGIC_H
+
+#include <cmath>
+#include "muParserError.h"
+
+
+namespace mu
+{
+       //-----------------------------------------------------------------------------------------------
+       //
+       // Compile time type detection
+       //
+       //-----------------------------------------------------------------------------------------------
+
+       /** \brief A class singling out integer types at compile time using
+                          template meta programming.
+       */
+       template<typename T>
+       struct TypeInfo
+       {
+               static bool IsInteger() { return false; }
+       };
+
+       template<>
+       struct TypeInfo<char>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<short>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<int>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<long>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<unsigned char>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<unsigned short>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<unsigned int>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<unsigned long>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+
+       //-----------------------------------------------------------------------------------------------
+       //
+       // Standard math functions with dummy overload for integer types
+       //
+       //-----------------------------------------------------------------------------------------------
+
+       /** \brief A template class for providing wrappers for essential math functions.
+
+         This template is spezialized for several types in order to provide a unified interface
+         for parser internal math function calls regardless of the data type.
+       */
+       template<typename T>
+       struct MathImpl
+       {
+               static T Sin(T v) { return sin(v); }
+               static T Cos(T v) { return cos(v); }
+               static T Tan(T v) { return tan(v); }
+               static T ASin(T v) { return asin(v); }
+               static T ACos(T v) { return acos(v); }
+               static T ATan(T v) { return atan(v); }
+               static T ATan2(T v1, T v2) { return atan2(v1, v2); }
+               static T Sinh(T v) { return sinh(v); }
+               static T Cosh(T v) { return cosh(v); }
+               static T Tanh(T v) { return tanh(v); }
+               static T ASinh(T v) { return log(v + sqrt(v * v + 1)); }
+               static T ACosh(T v) { return log(v + sqrt(v * v - 1)); }
+               static T ATanh(T v) { return ((T)0.5 * log((1 + v) / (1 - v))); }
+               static T Log(T v) { return log(v); }
+               static T Log2(T v) { return log(v) / log((T)2); } // Logarithm base 2
+               static T Log10(T v) { return log10(v); }         // Logarithm base 10
+               static T Exp(T v) { return exp(v); }
+               static T Abs(T v) { return (v >= 0) ? v : -v; }
+               static T Sqrt(T v) { return sqrt(v); }
+               static T Rint(T v) { return floor(v + (T)0.5); }
+               static T Sign(T v) { return (T)((v < 0) ? -1 : (v > 0) ? 1 : 0); }
+               static T Pow(T v1, T v2) { return std::pow(v1, v2); }
+
+               static T UnaryMinus(T v) { return -v; }
+               static T UnaryPlus(T v) { return v; }
+
+               static T Sum(const T *a_afArg, int a_iArgc)
+               {
+                       if (!a_iArgc)
+                               throw ParserError(_T("too few arguments for function sum."));
+
+                       T fRes = 0;
+                       for (int i = 0; i < a_iArgc; ++i) fRes += a_afArg[i];
+                       return fRes;
+               }
+
+               static T Avg(const T *a_afArg, int a_iArgc)
+               {
+                       if (!a_iArgc)
+                               throw ParserError(_T("too few arguments for function sum."));
+
+                       T fRes = 0;
+                       for (int i = 0; i < a_iArgc; ++i) fRes += a_afArg[i];
+                       return fRes / (T)a_iArgc;
+               }
+
+               static T Min(const T *a_afArg, int a_iArgc)
+               {
+                       if (!a_iArgc)
+                               throw ParserError(_T("too few arguments for function min."));
+
+                       T fRes = a_afArg[0];
+                       for (int i = 0; i < a_iArgc; ++i)
+                               fRes = std::min(fRes, a_afArg[i]);
+
+                       return fRes;
+               }
+
+               static T Max(const T *a_afArg, int a_iArgc)
+               {
+                       if (!a_iArgc)
+                               throw ParserError(_T("too few arguments for function min."));
+
+                       T fRes = a_afArg[0];
+                       for (int i = 0; i < a_iArgc; ++i) fRes = std::max(fRes, a_afArg[i]);
+
+                       return fRes;
+               }
+
+
+#if defined (__GNUG__)
+               // Bei zu genauer definition von pi kann die Berechnung von
+               // sin(pi*a) mit a=1 10 x langsamer sein! 
+               static constexpr T CONST_PI = (T)3.141592653589;
+#else
+               static constexpr T CONST_PI = (T)3.141592653589793238462643;
+#endif
+
+               static constexpr T CONST_E = (T)2.718281828459045235360287;
+       };
+}
+
+#endif
diff --git a/src/external/muparser/muParserTest.cpp b/src/external/muparser/muParserTest.cpp
new file mode 100644 (file)
index 0000000..75fbdf5
--- /dev/null
@@ -0,0 +1,1597 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "muParserTest.h"
+
+#include <cstdio>
+#include <cmath>
+#include <iostream>
+#include <limits>
+
+using namespace std;
+
+/** \file
+       \brief This file contains the implementation of parser test cases.
+*/
+
+namespace mu
+{
+       namespace Test
+       {
+               int ParserTester::c_iCount = 0;
+
+               //---------------------------------------------------------------------------------------------
+               ParserTester::ParserTester()
+                       :m_vTestFun()
+               {
+                       AddTest(&ParserTester::TestNames);
+                       AddTest(&ParserTester::TestSyntax);
+                       AddTest(&ParserTester::TestPostFix);
+                       AddTest(&ParserTester::TestInfixOprt);
+                       AddTest(&ParserTester::TestVarConst);
+                       AddTest(&ParserTester::TestMultiArg);
+                       AddTest(&ParserTester::TestExpression);
+                       AddTest(&ParserTester::TestIfThenElse);
+                       AddTest(&ParserTester::TestInterface);
+                       AddTest(&ParserTester::TestBinOprt);
+                       AddTest(&ParserTester::TestException);
+                       AddTest(&ParserTester::TestStrArg);
+                       AddTest(&ParserTester::TestBulkMode);
+
+                       ParserTester::c_iCount = 0;
+               }
+
+               //---------------------------------------------------------------------------------------------
+               int ParserTester::IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal)
+               {
+                       if (a_szExpr[1] == 0 || (a_szExpr[0] != '0' || a_szExpr[1] != 'x'))
+                               return 0;
+
+                       unsigned iVal(0);
+
+                       // New code based on streams for UNICODE compliance:
+                       stringstream_type::pos_type nPos(0);
+                       stringstream_type ss(a_szExpr + 2);
+                       ss >> std::hex >> iVal;
+                       nPos = ss.tellg();
+
+                       if (nPos == (stringstream_type::pos_type)0)
+                               return 1;
+
+                       *a_iPos += (int)(2 + nPos);
+                       *a_fVal = (value_type)iVal;
+                       return 1;
+               }
+
+               //---------------------------------------------------------------------------------------------
+               int ParserTester::TestInterface()
+               {
+                       int iStat = 0;
+                       mu::console() << _T("testing member functions...");
+
+                       // Test RemoveVar
+                       value_type afVal[3] = { 1,2,3 };
+                       Parser p;
+
+                       try
+                       {
+                               p.DefineVar(_T("a"), &afVal[0]);
+                               p.DefineVar(_T("b"), &afVal[1]);
+                               p.DefineVar(_T("c"), &afVal[2]);
+                               p.SetExpr(_T("a+b+c"));
+                               p.Eval();
+                       }
+                       catch (...)
+                       {
+                               iStat += 1;  // this is not supposed to happen 
+                       }
+
+                       try
+                       {
+                               p.RemoveVar(_T("c"));
+                               p.Eval();
+                               iStat += 1;  // not supposed to reach this, nonexisting variable "c" deleted...
+                       }
+                       catch (...)
+                       {
+                               // failure is expected...
+                       }
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+               //---------------------------------------------------------------------------------------------
+               int ParserTester::TestStrArg()
+               {
+                       int iStat = 0;
+                       mu::console() << _T("testing string arguments...");
+
+
+                       // from oss-fuzz: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23410
+                       iStat += ThrowTest(_T(R"(6 - 6 ? 4 : "", ? 4 : "", ? 4 : "")"), ecUNEXPECTED_STR,  true);
+                       // variations:
+                       iStat += ThrowTest(_T(R"(1 ? 4 : "")"), ecUNEXPECTED_STR, true);
+                       iStat += ThrowTest(_T(R"(1 ? "" : 4)"), ecUNEXPECTED_STR, true);
+                       iStat += ThrowTest(_T(R"(1 ? "" : "")"), ecUNEXPECTED_STR, true);
+
+                       // from oss-fuzz: https://oss-fuzz.com/testcase-detail/5106868061208576
+                       iStat += ThrowTest(_T(R"("","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",8)"), ecSTR_RESULT);
+                       // variations:
+                       iStat += ThrowTest(_T(R"("","",9)"), ecSTR_RESULT);
+
+                       iStat += EqnTest(_T("valueof(\"\")"), 123, true);   // empty string arguments caused a crash
+                       iStat += EqnTest(_T("valueof(\"aaa\")+valueof(\"bbb\")  "), 246, true);
+                       iStat += EqnTest(_T("2*(valueof(\"aaa\")-23)+valueof(\"bbb\")"), 323, true);
+
+                       // use in expressions with variables
+                       iStat += EqnTest(_T("a*(atof(\"10\")-b)"), 8, true);
+                       iStat += EqnTest(_T("a-(atof(\"10\")*b)"), -19, true);
+
+                       // string + numeric arguments
+                       iStat += EqnTest(_T("strfun1(\"100\")"), 100, true);
+                       iStat += EqnTest(_T("strfun2(\"100\",1)"), 101, true);
+                       iStat += EqnTest(_T("strfun3(\"99\",1,2)"), 102, true);
+                       iStat += EqnTest(_T("strfun4(\"99\",1,2,3)"), 105, true);
+                       iStat += EqnTest(_T("strfun5(\"99\",1,2,3,4)"), 109, true);
+
+                       // string constants
+                       iStat += EqnTest(_T("atof(str1)+atof(str2)"), 3.33, true);
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+               //---------------------------------------------------------------------------------------------
+               int ParserTester::TestBulkMode()
+               {
+                       int iStat = 0;
+                       mu::console() << _T("testing bulkmode...");
+
+#define EQN_TEST_BULK(EXPR, R1, R2, R3, R4, PASS) \
+                       { \
+                         double res[] = { R1, R2, R3, R4 }; \
+                         iStat += EqnTestBulk(_T(EXPR), res, (PASS)); \
+                       }
+
+                       // Bulk Variables for the test:
+                       // a: 1,2,3,4
+                       // b: 2,2,2,2
+                       // c: 3,3,3,3
+                       // d: 5,4,3,2
+                       EQN_TEST_BULK("a", 1, 1, 1, 1, false)
+                       EQN_TEST_BULK("a", 1, 2, 3, 4, true)
+                       EQN_TEST_BULK("b=a", 1, 2, 3, 4, true)
+                       EQN_TEST_BULK("b=a, b*10", 10, 20, 30, 40, true)
+                       EQN_TEST_BULK("b=a, b*10, a", 1, 2, 3, 4, true)
+                       EQN_TEST_BULK("a+b", 3, 4, 5, 6, true)
+                       EQN_TEST_BULK("c*(a+b)", 9, 12, 15, 18, true)
+#undef EQN_TEST_BULK
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+               //---------------------------------------------------------------------------------------------
+               int ParserTester::TestBinOprt()
+               {
+                       int iStat = 0;
+                       mu::console() << _T("testing binary operators...");
+
+                       // built in operators
+                       // xor operator
+
+                       iStat += EqnTest(_T("a++b"), 3, true);
+                       iStat += EqnTest(_T("a ++ b"), 3, true);
+                       iStat += EqnTest(_T("1++2"), 3, true);
+                       iStat += EqnTest(_T("1 ++ 2"), 3, true);
+                       iStat += EqnTest(_T("a add b"), 3, true);
+                       iStat += EqnTest(_T("1 add 2"), 3, true);
+                       iStat += EqnTest(_T("a<b"), 1, true);
+                       iStat += EqnTest(_T("b>a"), 1, true);
+                       iStat += EqnTest(_T("a>a"), 0, true);
+                       iStat += EqnTest(_T("a<a"), 0, true);
+                       iStat += EqnTest(_T("a>a"), 0, true);
+                       iStat += EqnTest(_T("a<=a"), 1, true);
+                       iStat += EqnTest(_T("a<=b"), 1, true);
+                       iStat += EqnTest(_T("b<=a"), 0, true);
+                       iStat += EqnTest(_T("a>=a"), 1, true);
+                       iStat += EqnTest(_T("b>=a"), 1, true);
+                       iStat += EqnTest(_T("a>=b"), 0, true);
+
+                       // Test logical operators, especially if user defined "&" and the internal "&&" collide
+                       iStat += EqnTest(_T("1 && 1"), 1, true);
+                       iStat += EqnTest(_T("1 && 0"), 0, true);
+                       iStat += EqnTest(_T("(a<b) && (b>a)"), 1, true);
+                       iStat += EqnTest(_T("(a<b) && (a>b)"), 0, true);
+                       //iStat += EqnTest(_T("12 and 255"), 12, true); 
+                       //iStat += EqnTest(_T("12 and 0"), 0, true); 
+                       iStat += EqnTest(_T("12 & 255"), 12, true);
+                       iStat += EqnTest(_T("12 & 0"), 0, true);
+                       iStat += EqnTest(_T("12&255"), 12, true);
+                       iStat += EqnTest(_T("12&0"), 0, true);
+
+                       // Assignment operator
+                       iStat += EqnTest(_T("a = b"), 2, true);
+                       iStat += EqnTest(_T("a = sin(b)"), 0.909297, true);
+                       iStat += EqnTest(_T("a = 1+sin(b)"), 1.909297, true);
+                       iStat += EqnTest(_T("(a=b)*2"), 4, true);
+                       iStat += EqnTest(_T("2*(a=b)"), 4, true);
+                       iStat += EqnTest(_T("2*(a=b+1)"), 6, true);
+                       iStat += EqnTest(_T("(a=b+1)*2"), 6, true);
+                       iStat += EqnTest(_T("a=c, a*10"), 30, true);
+
+                       iStat += EqnTest(_T("2^2^3"), 256, true);
+                       iStat += EqnTest(_T("1/2/3"), 1.0 / 6.0, true);
+
+                       // reference: http://www.wolframalpha.com/input/?i=3%2B4*2%2F%281-5%29^2^3
+                       iStat += EqnTest(_T("3+4*2/(1-5)^2^3"), 3.0001220703125, true);
+
+                       // Test user defined binary operators
+                       iStat += EqnTestInt(_T("1 | 2"), 3, true);
+                       iStat += EqnTestInt(_T("1 || 2"), 1, true);
+                       iStat += EqnTestInt(_T("123 & 456"), 72, true);
+                       iStat += EqnTestInt(_T("(123 & 456) % 10"), 2, true);
+                       iStat += EqnTestInt(_T("1 && 0"), 0, true);
+                       iStat += EqnTestInt(_T("123 && 456"), 1, true);
+                       iStat += EqnTestInt(_T("1 << 3"), 8, true);
+                       iStat += EqnTestInt(_T("8 >> 3"), 1, true);
+                       iStat += EqnTestInt(_T("9 / 4"), 2, true);
+                       iStat += EqnTestInt(_T("9 % 4"), 1, true);
+                       iStat += EqnTestInt(_T("if(5%2,1,0)"), 1, true);
+                       iStat += EqnTestInt(_T("if(4%2,1,0)"), 0, true);
+                       iStat += EqnTestInt(_T("-10+1"), -9, true);
+                       iStat += EqnTestInt(_T("1+2*3"), 7, true);
+                       iStat += EqnTestInt(_T("const1 != const2"), 1, true);
+                       iStat += EqnTestInt(_T("const1 != const2"), 0, false);
+                       iStat += EqnTestInt(_T("const1 == const2"), 0, true);
+                       iStat += EqnTestInt(_T("const1 == 1"), 1, true);
+                       iStat += EqnTestInt(_T("10*(const1 == 1)"), 10, true);
+                       iStat += EqnTestInt(_T("2*(const1 | const2)"), 6, true);
+                       iStat += EqnTestInt(_T("2*(const1 | const2)"), 7, false);
+                       iStat += EqnTestInt(_T("const1 < const2"), 1, true);
+                       iStat += EqnTestInt(_T("const2 > const1"), 1, true);
+                       iStat += EqnTestInt(_T("const1 <= 1"), 1, true);
+                       iStat += EqnTestInt(_T("const2 >= 2"), 1, true);
+                       iStat += EqnTestInt(_T("2*(const1 + const2)"), 6, true);
+                       iStat += EqnTestInt(_T("2*(const1 - const2)"), -2, true);
+                       iStat += EqnTestInt(_T("a != b"), 1, true);
+                       iStat += EqnTestInt(_T("a != b"), 0, false);
+                       iStat += EqnTestInt(_T("a == b"), 0, true);
+                       iStat += EqnTestInt(_T("a == 1"), 1, true);
+                       iStat += EqnTestInt(_T("10*(a == 1)"), 10, true);
+                       iStat += EqnTestInt(_T("2*(a | b)"), 6, true);
+                       iStat += EqnTestInt(_T("2*(a | b)"), 7, false);
+                       iStat += EqnTestInt(_T("a < b"), 1, true);
+                       iStat += EqnTestInt(_T("b > a"), 1, true);
+                       iStat += EqnTestInt(_T("a <= 1"), 1, true);
+                       iStat += EqnTestInt(_T("b >= 2"), 1, true);
+                       iStat += EqnTestInt(_T("2*(a + b)"), 6, true);
+                       iStat += EqnTestInt(_T("2*(a - b)"), -2, true);
+                       iStat += EqnTestInt(_T("a + (a << b)"), 5, true);
+                       iStat += EqnTestInt(_T("-2^2"), -4, true);
+                       iStat += EqnTestInt(_T("3--a"), 4, true);
+                       iStat += EqnTestInt(_T("3+-3^2"), -6, true);
+
+                       // Test reading of hex values:
+                       iStat += EqnTestInt(_T("0xff"), 255, true);
+                       iStat += EqnTestInt(_T("10+0xff"), 265, true);
+                       iStat += EqnTestInt(_T("0xff+10"), 265, true);
+                       iStat += EqnTestInt(_T("10*0xff"), 2550, true);
+                       iStat += EqnTestInt(_T("0xff*10"), 2550, true);
+                       iStat += EqnTestInt(_T("10+0xff+1"), 266, true);
+                       iStat += EqnTestInt(_T("1+0xff+10"), 266, true);
+
+                       // incorrect: '^' is yor here, not power
+                       //    iStat += EqnTestInt("-(1+2)^2", -9, true);
+                       //    iStat += EqnTestInt("-1^3", -1, true);          
+
+                                 // Test precedence
+                                 // a=1, b=2, c=3
+                       iStat += EqnTestInt(_T("a + b * c"), 7, true);
+                       iStat += EqnTestInt(_T("a * b + c"), 5, true);
+                       iStat += EqnTestInt(_T("a<b && b>10"), 0, true);
+                       iStat += EqnTestInt(_T("a<b && b<10"), 1, true);
+
+                       iStat += EqnTestInt(_T("a + b << c"), 17, true);
+                       iStat += EqnTestInt(_T("a << b + c"), 7, true);
+                       iStat += EqnTestInt(_T("c * b < a"), 0, true);
+                       iStat += EqnTestInt(_T("c * b == 6 * a"), 1, true);
+                       iStat += EqnTestInt(_T("2^2^3"), 256, true);
+
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+               //---------------------------------------------------------------------------------------------
+               /** \brief Check muParser name restriction enforcement. */
+               int ParserTester::TestNames()
+               {
+                       int  iStat = 0,
+                               iErr = 0;
+
+                       mu::console() << "testing name restriction enforcement...";
+
+                       Parser p;
+
+#define PARSER_THROWCHECK(DOMAIN, FAIL, EXPR, ARG)     \
+                       iErr = 0;                                                                               \
+                       ParserTester::c_iCount++;                                               \
+                       try                                                                                             \
+                       {                                                                                               \
+                               p.Define##DOMAIN(EXPR, ARG);                \
+                               iErr = (FAIL) ? 0 : 1;                                          \
+                       }                                                                                               \
+                       catch(...)                                                                              \
+                       {                                                                                               \
+                               iErr = (!FAIL) ? 0 : 1;                                         \
+                       }                                                                                               \
+                       iStat += iErr;      
+
+                       // constant names
+                       PARSER_THROWCHECK(Const, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1)
+                       PARSER_THROWCHECK(Const, false, _T("0a"), 1)
+                       PARSER_THROWCHECK(Const, false, _T("9a"), 1)
+                       PARSER_THROWCHECK(Const, false, _T("+a"), 1)
+                       PARSER_THROWCHECK(Const, false, _T("-a"), 1)
+                       PARSER_THROWCHECK(Const, false, _T("a-"), 1)
+                       PARSER_THROWCHECK(Const, false, _T("a*"), 1)
+                       PARSER_THROWCHECK(Const, false, _T("a?"), 1)
+                       PARSER_THROWCHECK(Const, true, _T("a"), 1)
+                       PARSER_THROWCHECK(Const, true, _T("a_min"), 1)
+                       PARSER_THROWCHECK(Const, true, _T("a_min0"), 1)
+                       PARSER_THROWCHECK(Const, true, _T("a_min9"), 1)
+
+                       // variable names
+                       value_type a;
+                       p.ClearConst();
+                       PARSER_THROWCHECK(Var, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), &a);
+                       PARSER_THROWCHECK(Var, false, _T("123abc"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("9a"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("0a"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("+a"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("-a"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("?a"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("!a"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("a+"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("a-"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("a*"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("a?"), &a)
+                       PARSER_THROWCHECK(Var, true, _T("a"), &a)
+                       PARSER_THROWCHECK(Var, true, _T("a_min"), &a)
+                       PARSER_THROWCHECK(Var, true, _T("a_min0"), &a)
+                       PARSER_THROWCHECK(Var, true, _T("a_min9"), &a)
+                       PARSER_THROWCHECK(Var, false, _T("a_min9"), 0)
+
+                       // Postfix operators
+                       // fail
+                       PARSER_THROWCHECK(PostfixOprt, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), f1of1);
+                       PARSER_THROWCHECK(PostfixOprt, false, _T("(k"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, false, _T("9+"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, false, _T("+"), 0)
+                       // pass
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("-a"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("?a"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("_"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("#"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("&&"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("||"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("&"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("|"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("++"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("--"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("?>"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("?<"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("**"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("xor"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("and"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("or"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("not"), f1of1)
+                       PARSER_THROWCHECK(PostfixOprt, true, _T("!"), f1of1)
+
+                       // Binary operator
+                       // The following must fail with builtin operators activated
+                       // p.EnableBuiltInOp(true); -> this is the default
+                       p.ClearPostfixOprt();
+                       PARSER_THROWCHECK(Oprt, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), f1of2);
+                       PARSER_THROWCHECK(Oprt, false, _T("+"), f1of2)
+                       PARSER_THROWCHECK(Oprt, false, _T("-"), f1of2)
+                       PARSER_THROWCHECK(Oprt, false, _T("*"), f1of2)
+                       PARSER_THROWCHECK(Oprt, false, _T("/"), f1of2)
+                       PARSER_THROWCHECK(Oprt, false, _T("^"), f1of2)
+                       PARSER_THROWCHECK(Oprt, false, _T("&&"), f1of2)
+                       PARSER_THROWCHECK(Oprt, false, _T("||"), f1of2)
+
+                       // without activated built in operators it should work
+                       p.EnableBuiltInOprt(false);
+                       PARSER_THROWCHECK(Oprt, true, _T("+"), f1of2)
+                       PARSER_THROWCHECK(Oprt, true, _T("-"), f1of2)
+                       PARSER_THROWCHECK(Oprt, true, _T("*"), f1of2)
+                       PARSER_THROWCHECK(Oprt, true, _T("/"), f1of2)
+                       PARSER_THROWCHECK(Oprt, true, _T("^"), f1of2)
+                       PARSER_THROWCHECK(Oprt, true, _T("&&"), f1of2)
+                       PARSER_THROWCHECK(Oprt, true, _T("||"), f1of2)
+#undef PARSER_THROWCHECK
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+               //---------------------------------------------------------------------------
+               int ParserTester::TestSyntax()
+               {
+                       int iStat = 0;
+                       mu::console() << _T("testing syntax engine...");
+
+                       iStat += ThrowTest(_T("1,"), ecUNEXPECTED_EOF);  // incomplete hex definition
+                       iStat += ThrowTest(_T("a,"), ecUNEXPECTED_EOF);  // incomplete hex definition
+                       iStat += ThrowTest(_T("sin(8),"), ecUNEXPECTED_EOF);  // incomplete hex definition
+                       iStat += ThrowTest(_T("(sin(8)),"), ecUNEXPECTED_EOF);  // incomplete hex definition
+                       iStat += ThrowTest(_T("a{m},"), ecUNEXPECTED_EOF);  // incomplete hex definition
+
+                       iStat += EqnTest(_T("(1+ 2*a)"), 3, true);   // Spaces within formula
+                       iStat += EqnTest(_T("sqrt((4))"), 2, true);  // Multiple brackets
+                       iStat += EqnTest(_T("sqrt((2)+2)"), 2, true);// Multiple brackets
+                       iStat += EqnTest(_T("sqrt(2+(2))"), 2, true);// Multiple brackets
+                       iStat += EqnTest(_T("sqrt(a+(3))"), 2, true);// Multiple brackets
+                       iStat += EqnTest(_T("sqrt((3)+a)"), 2, true);// Multiple brackets
+                       iStat += EqnTest(_T("order(1,2)"), 1, true); // May not cause name collision with operator "or"
+                       iStat += EqnTest(_T("(2+"), 0, false);       // missing closing bracket 
+                       iStat += EqnTest(_T("2++4"), 0, false);      // unexpected operator
+                       iStat += EqnTest(_T("2+-4"), 0, false);      // unexpected operator
+                       iStat += EqnTest(_T("(2+)"), 0, false);      // unexpected closing bracket
+                       iStat += EqnTest(_T("--2"), 0, false);       // double sign
+                       iStat += EqnTest(_T("ksdfj"), 0, false);     // unknown token
+                       iStat += EqnTest(_T("()"), 0, false);        // empty bracket without a function
+                       iStat += EqnTest(_T("5+()"), 0, false);      // empty bracket without a function
+                       iStat += EqnTest(_T("sin(cos)"), 0, false);  // unexpected function
+                       iStat += EqnTest(_T("5t6"), 0, false);       // unknown token
+                       iStat += EqnTest(_T("5 t 6"), 0, false);     // unknown token
+                       iStat += EqnTest(_T("8*"), 0, false);        // unexpected end of formula
+                       iStat += EqnTest(_T(",3"), 0, false);        // unexpected comma
+                       iStat += EqnTest(_T("3,5"), 0, false);       // unexpected comma
+                       iStat += EqnTest(_T("sin(8,8)"), 0, false);  // too many function args
+                       iStat += EqnTest(_T("(7,8)"), 0, false);     // too many function args
+                       iStat += EqnTest(_T("sin)"), 0, false);      // unexpected closing bracket
+                       iStat += EqnTest(_T("a)"), 0, false);        // unexpected closing bracket
+                       iStat += EqnTest(_T("pi)"), 0, false);       // unexpected closing bracket
+                       iStat += EqnTest(_T("sin(())"), 0, false);   // unexpected closing bracket
+                       iStat += EqnTest(_T("sin()"), 0, false);     // unexpected closing bracket
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+               //---------------------------------------------------------------------------
+               int ParserTester::TestVarConst()
+               {
+                       int iStat = 0;
+                       mu::console() << _T("testing variable/constant detection...");
+
+                       // Test if the result changes when a variable changes
+                       iStat += EqnTestWithVarChange(_T("a"), 1, 1, 2, 2);
+                       iStat += EqnTestWithVarChange(_T("2*a"), 2, 4, 3, 6);
+
+                       // distinguish constants with same basename
+                       iStat += EqnTest(_T("const"), 1, true);
+                       iStat += EqnTest(_T("const1"), 2, true);
+                       iStat += EqnTest(_T("const2"), 3, true);
+                       iStat += EqnTest(_T("2*const"), 2, true);
+                       iStat += EqnTest(_T("2*const1"), 4, true);
+                       iStat += EqnTest(_T("2*const2"), 6, true);
+                       iStat += EqnTest(_T("2*const+1"), 3, true);
+                       iStat += EqnTest(_T("2*const1+1"), 5, true);
+                       iStat += EqnTest(_T("2*const2+1"), 7, true);
+                       iStat += EqnTest(_T("const"), 0, false);
+                       iStat += EqnTest(_T("const1"), 0, false);
+                       iStat += EqnTest(_T("const2"), 0, false);
+
+                       // distinguish variables with same basename
+                       iStat += EqnTest(_T("a"), 1, true);
+                       iStat += EqnTest(_T("aa"), 2, true);
+                       iStat += EqnTest(_T("2*a"), 2, true);
+                       iStat += EqnTest(_T("2*aa"), 4, true);
+                       iStat += EqnTest(_T("2*a-1"), 1, true);
+                       iStat += EqnTest(_T("2*aa-1"), 3, true);
+
+                       // custom value recognition
+                       iStat += EqnTest(_T("0xff"), 255, true);
+                       iStat += EqnTest(_T("0x97 + 0xff"), 406, true);
+
+                       // Finally test querying of used variables
+                       try
+                       {
+                               int idx;
+                               mu::Parser p;
+                               mu::value_type vVarVal[] = { 1, 2, 3, 4, 5 };
+                               p.DefineVar(_T("a"), &vVarVal[0]);
+                               p.DefineVar(_T("b"), &vVarVal[1]);
+                               p.DefineVar(_T("c"), &vVarVal[2]);
+                               p.DefineVar(_T("d"), &vVarVal[3]);
+                               p.DefineVar(_T("e"), &vVarVal[4]);
+
+                               // Test lookup of defined variables
+                               // 4 used variables
+                               p.SetExpr(_T("a+b+c+d"));
+                               mu::varmap_type UsedVar = p.GetUsedVar();
+                               int iCount = (int)UsedVar.size();
+                               if (iCount != 4)
+                                       throw false;
+
+                               // the next check will fail if the parser 
+                               // erroneously creates new variables internally
+                               if (p.GetVar().size() != 5)
+                                       throw false;
+
+                               mu::varmap_type::const_iterator item = UsedVar.begin();
+                               for (idx = 0; item != UsedVar.end(); ++item)
+                               {
+                                       if (&vVarVal[idx++] != item->second)
+                                               throw false;
+                               }
+
+                               // Test lookup of undefined variables
+                               p.SetExpr(_T("undef1+undef2+undef3"));
+                               UsedVar = p.GetUsedVar();
+                               iCount = (int)UsedVar.size();
+                               if (iCount != 3)
+                                       throw false;
+
+                               // the next check will fail if the parser 
+                               // erroneously creates new variables internally
+                               if (p.GetVar().size() != 5)
+                                       throw false;
+
+                               for (item = UsedVar.begin(); item != UsedVar.end(); ++item)
+                               {
+                                       if (item->second != 0)
+                                               throw false; // all pointers to undefined variables must be null
+                               }
+
+                               // 1 used variables
+                               p.SetExpr(_T("a+b"));
+                               UsedVar = p.GetUsedVar();
+                               iCount = (int)UsedVar.size();
+                               if (iCount != 2) throw false;
+                               item = UsedVar.begin();
+                               for (idx = 0; item != UsedVar.end(); ++item)
+                                       if (&vVarVal[idx++] != item->second) throw false;
+
+                       }
+                       catch (...)
+                       {
+                               iStat += 1;
+                       }
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+               //---------------------------------------------------------------------------
+               int ParserTester::TestMultiArg()
+               {
+                       int iStat = 0;
+                       mu::console() << _T("testing multiarg functions...");
+
+                       // from oss-fzz.com: UNKNOWN READ; https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23330#c1
+                       iStat += ThrowTest(_T("6, +, +, +, +, +, +, +, +, +, +, +, +, +, +, 1, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +"), ecUNEXPECTED_ARG_SEP, true);
+                       
+                       // Compound expressions
+                       iStat += EqnTest(_T("1,2,3"), 3, true);
+                       iStat += EqnTest(_T("a,b,c"), 3, true);
+                       iStat += EqnTest(_T("a=10,b=20,c=a*b"), 200, true);
+                       iStat += EqnTest(_T("1,\n2,\n3"), 3, true);
+                       iStat += EqnTest(_T("a,\nb,\nc"), 3, true);
+                       iStat += EqnTest(_T("a=10,\nb=20,\nc=a*b"), 200, true);
+                       iStat += EqnTest(_T("1,\r\n2,\r\n3"), 3, true);
+                       iStat += EqnTest(_T("a,\r\nb,\r\nc"), 3, true);
+                       iStat += EqnTest(_T("a=10,\r\nb=20,\r\nc=a*b"), 200, true);
+
+                       // picking the right argument
+                       iStat += EqnTest(_T("f1of1(1)"), 1, true);
+                       iStat += EqnTest(_T("f1of2(1, 2)"), 1, true);
+                       iStat += EqnTest(_T("f2of2(1, 2)"), 2, true);
+                       iStat += EqnTest(_T("f1of3(1, 2, 3)"), 1, true);
+                       iStat += EqnTest(_T("f2of3(1, 2, 3)"), 2, true);
+                       iStat += EqnTest(_T("f3of3(1, 2, 3)"), 3, true);
+                       iStat += EqnTest(_T("f1of4(1, 2, 3, 4)"), 1, true);
+                       iStat += EqnTest(_T("f2of4(1, 2, 3, 4)"), 2, true);
+                       iStat += EqnTest(_T("f3of4(1, 2, 3, 4)"), 3, true);
+                       iStat += EqnTest(_T("f4of4(1, 2, 3, 4)"), 4, true);
+                       iStat += EqnTest(_T("f1of5(1, 2, 3, 4, 5)"), 1, true);
+                       iStat += EqnTest(_T("f2of5(1, 2, 3, 4, 5)"), 2, true);
+                       iStat += EqnTest(_T("f3of5(1, 2, 3, 4, 5)"), 3, true);
+                       iStat += EqnTest(_T("f4of5(1, 2, 3, 4, 5)"), 4, true);
+                       iStat += EqnTest(_T("f5of5(1, 2, 3, 4, 5)"), 5, true);
+                       // Too few arguments / Too many arguments
+                       iStat += EqnTest(_T("1+ping()"), 11, true);
+                       iStat += EqnTest(_T("ping()+1"), 11, true);
+                       iStat += EqnTest(_T("2*ping()"), 20, true);
+                       iStat += EqnTest(_T("ping()*2"), 20, true);
+                       iStat += EqnTest(_T("ping(1,2)"), 0, false);
+                       iStat += EqnTest(_T("1+ping(1,2)"), 0, false);
+                       iStat += EqnTest(_T("f1of1(1,2)"), 0, false);
+                       iStat += EqnTest(_T("f1of1()"), 0, false);
+                       iStat += EqnTest(_T("f1of2(1, 2, 3)"), 0, false);
+                       iStat += EqnTest(_T("f1of2(1)"), 0, false);
+                       iStat += EqnTest(_T("f1of3(1, 2, 3, 4)"), 0, false);
+                       iStat += EqnTest(_T("f1of3(1)"), 0, false);
+                       iStat += EqnTest(_T("f1of4(1, 2, 3, 4, 5)"), 0, false);
+                       iStat += EqnTest(_T("f1of4(1)"), 0, false);
+                       iStat += EqnTest(_T("(1,2,3)"), 0, false);
+                       iStat += EqnTest(_T("1,2,3"), 0, false);
+                       iStat += EqnTest(_T("(1*a,2,3)"), 0, false);
+                       iStat += EqnTest(_T("1,2*a,3"), 0, false);
+
+                       // correct calculation of arguments
+                       iStat += EqnTest(_T("min(a, 1)"), 1, true);
+                       iStat += EqnTest(_T("min(3*2, 1)"), 1, true);
+                       iStat += EqnTest(_T("min(3*2, 1)"), 6, false);
+                       iStat += EqnTest(_T("firstArg(2,3,4)"), 2, true);
+                       iStat += EqnTest(_T("lastArg(2,3,4)"), 4, true);
+                       iStat += EqnTest(_T("min(3*a+1, 1)"), 1, true);
+                       iStat += EqnTest(_T("max(3*a+1, 1)"), 4, true);
+                       iStat += EqnTest(_T("max(3*a+1, 1)*2"), 8, true);
+                       iStat += EqnTest(_T("2*max(3*a+1, 1)+2"), 10, true);
+
+                       // functions with Variable argument count
+                       iStat += EqnTest(_T("sum(a)"), 1, true);
+                       iStat += EqnTest(_T("sum(1,2,3)"), 6, true);
+                       iStat += EqnTest(_T("sum(a,b,c)"), 6, true);
+                       iStat += EqnTest(_T("sum(1,-max(1,2),3)*2"), 4, true);
+                       iStat += EqnTest(_T("2*sum(1,2,3)"), 12, true);
+                       iStat += EqnTest(_T("2*sum(1,2,3)+2"), 14, true);
+                       iStat += EqnTest(_T("2*sum(-1,2,3)+2"), 10, true);
+                       iStat += EqnTest(_T("2*sum(-1,2,-(-a))+2"), 6, true);
+                       iStat += EqnTest(_T("2*sum(-1,10,-a)+2"), 18, true);
+                       iStat += EqnTest(_T("2*sum(1,2,3)*2"), 24, true);
+                       iStat += EqnTest(_T("sum(1,-max(1,2),3)*2"), 4, true);
+                       iStat += EqnTest(_T("sum(1*3, 4, a+2)"), 10, true);
+                       iStat += EqnTest(_T("sum(1*3, 2*sum(1,2,2), a+2)"), 16, true);
+                       iStat += EqnTest(_T("sum(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2)"), 24, true);
+
+                       // some failures
+                       iStat += EqnTest(_T("sum()"), 0, false);
+                       iStat += EqnTest(_T("sum(,)"), 0, false);
+                       iStat += EqnTest(_T("sum(1,2,)"), 0, false);
+                       iStat += EqnTest(_T("sum(,1,2)"), 0, false);
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+
+               //---------------------------------------------------------------------------
+               int ParserTester::TestInfixOprt()
+               {
+                       int iStat(0);
+                       mu::console() << "testing infix operators...";
+
+                       iStat += EqnTest(_T("+1"), +1, true);
+                       iStat += EqnTest(_T("-(+1)"), -1, true);
+                       iStat += EqnTest(_T("-(+1)*2"), -2, true);
+                       iStat += EqnTest(_T("-(+2)*sqrt(4)"), -4, true);
+                       iStat += EqnTest(_T("3-+a"), 2, true);
+                       iStat += EqnTest(_T("+1*3"), 3, true);
+
+                       iStat += EqnTest(_T("-1"), -1, true);
+                       iStat += EqnTest(_T("-(-1)"), 1, true);
+                       iStat += EqnTest(_T("-(-1)*2"), 2, true);
+                       iStat += EqnTest(_T("-(-2)*sqrt(4)"), 4, true);
+                       iStat += EqnTest(_T("-_pi"), -MathImpl<double>::CONST_PI, true);
+                       iStat += EqnTest(_T("-a"), -1, true);
+                       iStat += EqnTest(_T("-(a)"), -1, true);
+                       iStat += EqnTest(_T("-(-a)"), 1, true);
+                       iStat += EqnTest(_T("-(-a)*2"), 2, true);
+                       iStat += EqnTest(_T("-(8)"), -8, true);
+                       iStat += EqnTest(_T("-8"), -8, true);
+                       iStat += EqnTest(_T("-(2+1)"), -3, true);
+                       iStat += EqnTest(_T("-(f1of1(1+2*3)+1*2)"), -9, true);
+                       iStat += EqnTest(_T("-(-f1of1(1+2*3)+1*2)"), 5, true);
+                       iStat += EqnTest(_T("-sin(8)"), -0.989358, true);
+                       iStat += EqnTest(_T("3-(-a)"), 4, true);
+                       iStat += EqnTest(_T("3--a"), 4, true);
+                       iStat += EqnTest(_T("-1*3"), -3, true);
+
+                       // Postfix / infix priorities
+                       iStat += EqnTest(_T("~2#"), 8, true);
+                       iStat += EqnTest(_T("~f1of1(2)#"), 8, true);
+                       iStat += EqnTest(_T("~(b)#"), 8, true);
+                       iStat += EqnTest(_T("(~b)#"), 12, true);
+                       iStat += EqnTest(_T("~(2#)"), 8, true);
+                       iStat += EqnTest(_T("~(f1of1(2)#)"), 8, true);
+                       //
+                       iStat += EqnTest(_T("-2^2"), -4, true);
+                       iStat += EqnTest(_T("-(a+b)^2"), -9, true);
+                       iStat += EqnTest(_T("(-3)^2"), 9, true);
+                       iStat += EqnTest(_T("-(-2^2)"), 4, true);
+                       iStat += EqnTest(_T("3+-3^2"), -6, true);
+                       // The following assumes use of sqr as postfix operator together
+                       // with a sign operator of low priority:
+                       iStat += EqnTest(_T("-2'"), -4, true);
+                       iStat += EqnTest(_T("-(1+1)'"), -4, true);
+                       iStat += EqnTest(_T("2+-(1+1)'"), -2, true);
+                       iStat += EqnTest(_T("2+-2'"), -2, true);
+                       // This is the classic behaviour of the infix sign operator (here: "$") which is
+                       // now deprecated:
+                       iStat += EqnTest(_T("$2^2"), 4, true);
+                       iStat += EqnTest(_T("$(a+b)^2"), 9, true);
+                       iStat += EqnTest(_T("($3)^2"), 9, true);
+                       iStat += EqnTest(_T("$($2^2)"), -4, true);
+                       iStat += EqnTest(_T("3+$3^2"), 12, true);
+
+                       // infix operators sharing the first few characters
+                       iStat += EqnTest(_T("~ 123"), (value_type)123.0 + 2, true);
+                       iStat += EqnTest(_T("~~ 123"), (value_type)123.0 + 2, true);
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+
+               //---------------------------------------------------------------------------
+               int ParserTester::TestPostFix()
+               {
+                       int iStat = 0;
+                       mu::console() << _T("testing postfix operators...");
+
+                       // application
+                       iStat += EqnTest(_T("3{m}+5"), 5.003, true);
+                       iStat += EqnTest(_T("1000{m}"), 1, true);
+                       iStat += EqnTest(_T("1000 {m}"), 1, true);
+                       iStat += EqnTest(_T("(a){m}"), 1e-3, true);
+                       iStat += EqnTest(_T("a{m}"), 1e-3, true);
+                       iStat += EqnTest(_T("a {m}"), 1e-3, true);
+                       iStat += EqnTest(_T("-(a){m}"), -1e-3, true);
+                       iStat += EqnTest(_T("-2{m}"), -2e-3, true);
+                       iStat += EqnTest(_T("-2 {m}"), -2e-3, true);
+                       iStat += EqnTest(_T("f1of1(1000){m}"), 1, true);
+                       iStat += EqnTest(_T("-f1of1(1000){m}"), -1, true);
+                       iStat += EqnTest(_T("-f1of1(-1000){m}"), 1, true);
+                       iStat += EqnTest(_T("f4of4(0,0,0,1000){m}"), 1, true);
+                       iStat += EqnTest(_T("2+(a*1000){m}"), 3, true);
+
+                       // can postfix operators "m" und "meg" be told apart properly?
+                       iStat += EqnTest(_T("2*3000meg+2"), 2 * 3e9 + 2, true);
+
+                       // some incorrect results
+                       iStat += EqnTest(_T("1000{m}"), 0.1, false);
+                       iStat += EqnTest(_T("(a){m}"), 2, false);
+                       // failure due to syntax checking
+                       iStat += ThrowTest(_T("0x"), ecUNASSIGNABLE_TOKEN);  // incomplete hex definition
+                       iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF);
+                       iStat += ThrowTest(_T("4 + {m}"), ecUNASSIGNABLE_TOKEN);
+                       iStat += ThrowTest(_T("{m}4"), ecUNASSIGNABLE_TOKEN);
+                       iStat += ThrowTest(_T("sin({m})"), ecUNASSIGNABLE_TOKEN);
+                       iStat += ThrowTest(_T("{m} {m}"), ecUNASSIGNABLE_TOKEN);
+                       iStat += ThrowTest(_T("{m}(8)"), ecUNASSIGNABLE_TOKEN);
+                       iStat += ThrowTest(_T("4,{m}"), ecUNASSIGNABLE_TOKEN);
+                       iStat += ThrowTest(_T("-{m}"), ecUNASSIGNABLE_TOKEN);
+                       iStat += ThrowTest(_T("2(-{m})"), ecUNEXPECTED_PARENS);
+                       iStat += ThrowTest(_T("2({m})"), ecUNEXPECTED_PARENS);
+
+                       iStat += ThrowTest(_T("multi*1.0"), ecUNASSIGNABLE_TOKEN);
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+               //---------------------------------------------------------------------------
+               int ParserTester::TestExpression()
+               {
+                       int iStat = 0;
+                       mu::console() << _T("testing expression samples...");
+
+                       value_type b = 2;
+
+                       iStat += EqnTest(_T("f0()"), 42, true);
+                       iStat += EqnTest(_T("b^2"), 4, true);
+                       iStat += EqnTest(_T("b^1"), 2, true);
+                       iStat += EqnTest(_T("b^0"), 1, true);
+                       iStat += EqnTest(_T("b^-1"), 0.5, true);
+
+                       // Optimization
+                       iStat += EqnTest(_T("2*b*5"), 20, true);
+                       iStat += EqnTest(_T("2*b*5 + 4*b"), 28, true);
+                       iStat += EqnTest(_T("2*a/3"), 2.0 / 3.0, true);
+
+                       // Addition auf cmVARMUL 
+                       iStat += EqnTest(_T("3+b"), b + 3, true);
+                       iStat += EqnTest(_T("b+3"), b + 3, true);
+                       iStat += EqnTest(_T("b*3+2"), b * 3 + 2, true);
+                       iStat += EqnTest(_T("3*b+2"), b * 3 + 2, true);
+                       iStat += EqnTest(_T("2+b*3"), b * 3 + 2, true);
+                       iStat += EqnTest(_T("2+3*b"), b * 3 + 2, true);
+                       iStat += EqnTest(_T("b+3*b"), b + 3 * b, true);
+                       iStat += EqnTest(_T("3*b+b"), b + 3 * b, true);
+
+                       iStat += EqnTest(_T("2+b*3+b"), 2 + b * 3 + b, true);
+                       iStat += EqnTest(_T("b+2+b*3"), b + 2 + b * 3, true);
+
+                       iStat += EqnTest(_T("(2*b+1)*4"), (2 * b + 1) * 4, true);
+                       iStat += EqnTest(_T("4*(2*b+1)"), (2 * b + 1) * 4, true);
+
+                       // operator precedences
+                       iStat += EqnTest(_T("1+2-3*4/5^6"), 2.99923, true);
+                       iStat += EqnTest(_T("1^2/3*4-5+6"), 2.33333333, true);
+                       iStat += EqnTest(_T("1+2*3"), 7, true);
+                       iStat += EqnTest(_T("1+2*3"), 7, true);
+                       iStat += EqnTest(_T("(1+2)*3"), 9, true);
+                       iStat += EqnTest(_T("(1+2)*(-3)"), -9, true);
+                       iStat += EqnTest(_T("2/4"), 0.5, true);
+
+                       iStat += EqnTest(_T("exp(ln(7))"), 7, true);
+                       iStat += EqnTest(_T("e^ln(7)"), 7, true);
+                       iStat += EqnTest(_T("e^(ln(7))"), 7, true);
+                       iStat += EqnTest(_T("(e^(ln(7)))"), 7, true);
+                       iStat += EqnTest(_T("1-(e^(ln(7)))"), -6, true);
+                       iStat += EqnTest(_T("2*(e^(ln(7)))"), 14, true);
+                       iStat += EqnTest(_T("10^log(5)"), pow(10.0, log(5.0)), true);
+                       iStat += EqnTest(_T("10^log10(5)"), 5, true);
+                       iStat += EqnTest(_T("2^log2(4)"), 4, true);
+                       iStat += EqnTest(_T("-(sin(0)+1)"), -1, true);
+                       iStat += EqnTest(_T("-(2^1.1)"), -2.14354692, true);
+
+                       iStat += EqnTest(_T("(cos(2.41)/b)"), -0.372056, true);
+                       iStat += EqnTest(_T("(1*(2*(3*(4*(5*(6*(a+b)))))))"), 2160, true);
+                       iStat += EqnTest(_T("(1*(2*(3*(4*(5*(6*(7*(a+b))))))))"), 15120, true);
+                       iStat += EqnTest(_T("(a/((((b+(((e*(((((pi*((((3.45*((pi+a)+pi))+b)+b)*a))+0.68)+e)+a)/a))+a)+b))+b)*a)-pi))"), 0.00377999, true);
+
+                       // long formula (Reference: Matlab)
+                       iStat += EqnTest(
+                               _T("(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))")
+                               _T("/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/")
+                               _T("((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-")
+                               _T("e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6")
+                               _T("+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e")
+                               _T("*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)"), -12.23016549, true);
+
+                       // long formula (Reference: Matlab)
+                       iStat += EqnTest(
+                               _T("(atan(sin((((((((((((((((pi/cos((a/((((0.53-b)-pi)*e)/b))))+2.51)+a)-0.54)/0.98)+b)*b)+e)/a)+b)+a)+b)+pi)/e")
+                               _T(")+a)))*2.77)"), -2.16995656, true);
+
+                       // long formula (Reference: Matlab)
+                       iStat += EqnTest(_T("1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12"), -7995810.09926, true);
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+
+
+               //---------------------------------------------------------------------------
+               int ParserTester::TestIfThenElse()
+               {
+                       int iStat = 0;
+                       mu::console() << _T("testing if-then-else operator...");
+
+                       // from oss-fuzz.com: https://oss-fuzz.com/testcase-detail/4777121158529024
+                       iStat += ThrowTest(_T("3!=min(0?2>2,2>5,1:6)"), ecUNEXPECTED_ARG_SEP);
+
+                       // Test error detection
+                       iStat += ThrowTest(_T(":3"), ecUNEXPECTED_CONDITIONAL);
+                       iStat += ThrowTest(_T("? 1 : 2"), ecUNEXPECTED_CONDITIONAL);
+                       iStat += ThrowTest(_T("(a<b) ? (b<c) ? 1 : 2"), ecMISSING_ELSE_CLAUSE);
+                       iStat += ThrowTest(_T("(a<b) ? 1"), ecMISSING_ELSE_CLAUSE);
+                       iStat += ThrowTest(_T("(a<b) ? a"), ecMISSING_ELSE_CLAUSE);
+                       iStat += ThrowTest(_T("(a<b) ? a+b"), ecMISSING_ELSE_CLAUSE);
+                       iStat += ThrowTest(_T("a : b"), ecMISPLACED_COLON);
+                       iStat += ThrowTest(_T("1 : 2"), ecMISPLACED_COLON);
+                       iStat += ThrowTest(_T("(1) ? 1 : 2 : 3"), ecMISPLACED_COLON);
+                       iStat += ThrowTest(_T("(true) ? 1 : 2 : 3"), ecUNASSIGNABLE_TOKEN);
+                       
+                       // from oss-fzz.com: UNKNOWN READ; https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22922#c1
+                       iStat += ThrowTest(_T("1?2:0?(7:1)"), ecMISPLACED_COLON);
+
+                       // from oss-fuzz.com: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22938
+                       iStat += ThrowTest(_T("sum(0?1,0,0:3)"), ecUNEXPECTED_ARG_SEP);
+                       iStat += ThrowTest(_T("sum(0?(1,0,0):3)"), ecUNEXPECTED_ARG);
+                       iStat += ThrowTest(_T("sum(2>3?2,4,2:4)"), ecUNEXPECTED_ARG_SEP);
+                       iStat += ThrowTest(_T("sum(2>3?2,4,sin(2):4)"), ecUNEXPECTED_ARG_SEP);
+                       iStat += ThrowTest(_T("sum(2>3?sin(2),4,2:4)"), ecUNEXPECTED_ARG_SEP);
+                       iStat += ThrowTest(_T("sum(2>3?sin(a),4,2:4)"), ecUNEXPECTED_ARG_SEP);
+                       iStat += ThrowTest(_T("sum(2>3?sin(2),4,2:4)"), ecUNEXPECTED_ARG_SEP);
+
+                       iStat += EqnTest(_T("1 ? 128 : 255"), 128, true);
+                       iStat += EqnTest(_T("1<2 ? 128 : 255"), 128, true);
+                       iStat += EqnTest(_T("a<b ? 128 : 255"), 128, true);
+                       iStat += EqnTest(_T("(a<b) ? 128 : 255"), 128, true);
+                       iStat += EqnTest(_T("(1) ? 10 : 11"), 10, true);
+                       iStat += EqnTest(_T("(0) ? 10 : 11"), 11, true);
+                       iStat += EqnTest(_T("(1) ? a+b : c+d"), 3, true);
+                       iStat += EqnTest(_T("(0) ? a+b : c+d"), 1, true);
+                       iStat += EqnTest(_T("(1) ? 0 : 1"), 0, true);
+                       iStat += EqnTest(_T("(0) ? 0 : 1"), 1, true);
+                       iStat += EqnTest(_T("(a<b) ? 10 : 11"), 10, true);
+                       iStat += EqnTest(_T("(a>b) ? 10 : 11"), 11, true);
+                       iStat += EqnTest(_T("(a<b) ? c : d"), 3, true);
+                       iStat += EqnTest(_T("(a>b) ? c : d"), -2, true);
+
+                       iStat += EqnTest(_T("(a>b) ? 1 : 0"), 0, true);
+                       iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : 2"), 2, true);
+                       iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : sum((a>b) ? 1 : 2)"), 2, true);
+                       iStat += EqnTest(_T("((a>b) ? 0 : 1) ? 1 : sum((a>b) ? 1 : 2)"), 1, true);
+
+                       iStat += EqnTest(_T("sum((a>b) ? 1 : 2)"), 2, true);
+                       iStat += EqnTest(_T("sum((1) ? 1 : 2)"), 1, true);
+                       iStat += EqnTest(_T("sum((a>b) ? 1 : 2, 100)"), 102, true);
+                       iStat += EqnTest(_T("sum((1) ? 1 : 2, 100)"), 101, true);
+                       iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)"), 13, true);
+                       iStat += EqnTest(_T("sum(3, (a<b) ? 3 : 10)"), 6, true);
+                       iStat += EqnTest(_T("10*sum(3, (a>b) ? 3 : 10)"), 130, true);
+                       iStat += EqnTest(_T("10*sum(3, (a<b) ? 3 : 10)"), 60, true);
+                       iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)*10"), 130, true);
+                       iStat += EqnTest(_T("sum(3, (a<b) ? 3 : 10)*10"), 60, true);
+                       iStat += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 60, true);
+                       iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 99, true);
+                       iStat += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 360, true);
+                       iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 99, true);
+                       iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : sum(3, (a<b) ? 3 : 10)*10"), 60, true);
+
+                       // todo: also add for muParserX!
+                       iStat += EqnTest(_T("(a<b)&&(a<b) ? 128 : 255"), 128, true);
+                       iStat += EqnTest(_T("(a>b)&&(a<b) ? 128 : 255"), 255, true);
+                       iStat += EqnTest(_T("(1<2)&&(1<2) ? 128 : 255"), 128, true);
+                       iStat += EqnTest(_T("(1>2)&&(1<2) ? 128 : 255"), 255, true);
+                       iStat += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128, true);
+                       iStat += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255, true);
+                       iStat += EqnTest(_T("((a<b)&&(a<b)) ? 128 : 255"), 128, true);
+                       iStat += EqnTest(_T("((a>b)&&(a<b)) ? 128 : 255"), 255, true);
+
+                       iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 255, true);
+                       iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)"), 255, true);
+                       iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 128, true);
+                       iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)"), 128, true);
+                       iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 32, true);
+                       iStat += EqnTest(_T("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 64, true);
+                       iStat += EqnTest(_T("1>0 ? 50 :  1>0 ? 128 : 255"), 50, true);
+                       iStat += EqnTest(_T("1>0 ? 50 : (1>0 ? 128 : 255)"), 50, true);
+                       iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 50"), 128, true);
+                       iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16"), 32, true);
+                       iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)"), 32, true);
+                       iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 :  1>0 ? 32 :1>2 ? 64 : 16"), 255, true);
+                       iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)"), 255, true);
+                       iStat += EqnTest(_T("1 ? 0 ? 128 : 255 : 1 ? 32 : 64"), 255, true);
+
+                       // assignment operators
+                       iStat += EqnTest(_T("a= 0 ? 128 : 255, a"), 255, true);
+                       iStat += EqnTest(_T("a=((a>b)&&(a<b)) ? 128 : 255, a"), 255, true);
+                       iStat += EqnTest(_T("c=(a<b)&&(a<b) ? 128 : 255, c"), 128, true);
+                       iStat += EqnTest(_T("0 ? a=a+1 : 666, a"), 1, true);
+                       iStat += EqnTest(_T("1?a=10:a=20, a"), 10, true);
+                       iStat += EqnTest(_T("0?a=10:a=20, a"), 20, true);
+                       iStat += EqnTest(_T("0?a=sum(3,4):10, a"), 1, true);  // a should not change its value due to lazy calculation
+
+                       iStat += EqnTest(_T("a=1?b=1?3:4:5, a"), 3, true);
+                       iStat += EqnTest(_T("a=1?b=1?3:4:5, b"), 3, true);
+                       iStat += EqnTest(_T("a=0?b=1?3:4:5, a"), 5, true);
+                       iStat += EqnTest(_T("a=0?b=1?3:4:5, b"), 2, true);
+
+                       iStat += EqnTest(_T("a=1?5:b=1?3:4, a"), 5, true);
+                       iStat += EqnTest(_T("a=1?5:b=1?3:4, b"), 2, true);
+                       iStat += EqnTest(_T("a=0?5:b=1?3:4, a"), 3, true);
+                       iStat += EqnTest(_T("a=0?5:b=1?3:4, b"), 3, true);
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+               //---------------------------------------------------------------------------
+               int ParserTester::TestException()
+               {
+                       int  iStat = 0;
+                       mu::console() << _T("testing error codes...");
+
+                       iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF);
+                       iStat += ThrowTest(_T("3+)"), ecUNEXPECTED_PARENS);
+                       iStat += ThrowTest(_T("()"), ecUNEXPECTED_PARENS);
+                       iStat += ThrowTest(_T("3+()"), ecUNEXPECTED_PARENS);
+                       iStat += ThrowTest(_T("sin(3,4)"), ecTOO_MANY_PARAMS);
+                       iStat += ThrowTest(_T("sin()"), ecTOO_FEW_PARAMS);
+                       iStat += ThrowTest(_T("(1+2"), ecMISSING_PARENS);
+                       iStat += ThrowTest(_T("sin(3)3"), ecUNEXPECTED_VAL);
+                       iStat += ThrowTest(_T("sin(3)xyz"), ecUNASSIGNABLE_TOKEN);
+                       iStat += ThrowTest(_T("sin(3)cos(3)"), ecUNEXPECTED_FUN);
+                       iStat += ThrowTest(_T("a+b+c=10"), ecUNEXPECTED_OPERATOR);
+                       iStat += ThrowTest(_T("a=b=3"), ecUNEXPECTED_OPERATOR);
+
+                       // functions without parameter
+                       iStat += ThrowTest(_T("3+ping(2)"), ecTOO_MANY_PARAMS);
+                       iStat += ThrowTest(_T("3+ping(a+2)"), ecTOO_MANY_PARAMS);
+                       iStat += ThrowTest(_T("3+ping(sin(a)+2)"), ecTOO_MANY_PARAMS);
+                       iStat += ThrowTest(_T("3+ping(1+sin(a))"), ecTOO_MANY_PARAMS);
+
+                       // String function related
+                       iStat += ThrowTest(_T("valueof(\"xxx\")"), 999, false);
+                       iStat += ThrowTest(_T("valueof()"), ecUNEXPECTED_PARENS);
+                       iStat += ThrowTest(_T("1+valueof(\"abc\""), ecMISSING_PARENS);
+                       iStat += ThrowTest(_T("valueof(\"abc\""), ecMISSING_PARENS);
+                       iStat += ThrowTest(_T("valueof(\"abc"), ecUNTERMINATED_STRING);
+                       iStat += ThrowTest(_T("valueof(\"abc\",3)"), ecTOO_MANY_PARAMS);
+                       iStat += ThrowTest(_T("valueof(3)"), ecSTRING_EXPECTED);
+                       iStat += ThrowTest(_T("sin(\"abc\")"), ecVAL_EXPECTED);
+                       iStat += ThrowTest(_T("valueof(\"\\\"abc\\\"\")"), 999, false);
+                       iStat += ThrowTest(_T("\"hello world\""), ecSTR_RESULT);
+                       iStat += ThrowTest(_T("(\"hello world\")"), ecSTR_RESULT);
+                       iStat += ThrowTest(_T("\"abcd\"+100"), ecSTR_RESULT);
+                       iStat += ThrowTest(_T("\"a\"+\"b\""), ecSTR_RESULT);
+                       iStat += ThrowTest(_T("strfun1(\"100\",3)"), ecTOO_MANY_PARAMS);
+                       iStat += ThrowTest(_T("strfun2(\"100\",3,5)"), ecTOO_MANY_PARAMS);
+                       iStat += ThrowTest(_T("strfun3(\"100\",3,5,6)"), ecTOO_MANY_PARAMS);
+                       iStat += ThrowTest(_T("strfun2(\"100\")"), ecTOO_FEW_PARAMS);
+                       iStat += ThrowTest(_T("strfun3(\"100\",6)"), ecTOO_FEW_PARAMS);
+                       iStat += ThrowTest(_T("strfun2(1,1)"), ecSTRING_EXPECTED);
+                       iStat += ThrowTest(_T("strfun2(a,1)"), ecSTRING_EXPECTED);
+                       iStat += ThrowTest(_T("strfun2(1,1,1)"), ecTOO_MANY_PARAMS);
+                       iStat += ThrowTest(_T("strfun2(a,1,1)"), ecTOO_MANY_PARAMS);
+                       iStat += ThrowTest(_T("strfun3(1,2,3)"), ecSTRING_EXPECTED);
+                       iStat += ThrowTest(_T("strfun3(1, \"100\",3)"), ecSTRING_EXPECTED);
+                       iStat += ThrowTest(_T("strfun3(\"1\", \"100\",3)"), ecVAL_EXPECTED);
+                       iStat += ThrowTest(_T("strfun3(\"1\", 3, \"100\")"), ecVAL_EXPECTED);
+                       iStat += ThrowTest(_T("strfun3(\"1\", \"100\", \"100\", \"100\")"), ecTOO_MANY_PARAMS);
+
+                       // assignment operator
+                       iStat += ThrowTest(_T("3=4"), ecUNEXPECTED_OPERATOR);
+                       iStat += ThrowTest(_T("sin(8)=4"), ecUNEXPECTED_OPERATOR);
+                       iStat += ThrowTest(_T("\"test\"=a"), ecSTR_RESULT);
+
+                       // <ibg 20090529>
+                       // this is now legal, for reference see:
+                       // https://sourceforge.net/forum/message.php?msg_id=7411373
+                       //      iStat += ThrowTest( _T("sin=9"), ecUNEXPECTED_OPERATOR);    
+                       // </ibg>
+
+                       iStat += ThrowTest(_T("(8)=5"), ecUNEXPECTED_OPERATOR);
+                       iStat += ThrowTest(_T("(a)=5"), ecUNEXPECTED_OPERATOR);
+                       iStat += ThrowTest(_T("a=\"tttt\""), ecOPRT_TYPE_CONFLICT);
+
+                       if (iStat == 0)
+                               mu::console() << _T("passed") << endl;
+                       else
+                               mu::console() << _T("\n  failed with ") << iStat << _T(" errors") << endl;
+
+                       return iStat;
+               }
+
+
+               //---------------------------------------------------------------------------
+               void ParserTester::AddTest(testfun_type a_pFun)
+               {
+                       m_vTestFun.push_back(a_pFun);
+               }
+
+               //---------------------------------------------------------------------------
+               int ParserTester::Run()
+               {
+                       int iStat = 0;
+                       try
+                       {
+                               for (int i = 0; i < (int)m_vTestFun.size(); ++i)
+                                       iStat += (this->*m_vTestFun[i])();
+                       }
+                       catch (Parser::exception_type& e)
+                       {
+                               mu::console() << "\n" << e.GetMsg() << endl;
+                               mu::console() << e.GetToken() << endl;
+                               Abort();
+                       }
+                       catch (std::exception& e)
+                       {
+                               mu::console() << e.what() << endl;
+                               Abort();
+                       }
+                       catch (...)
+                       {
+                               mu::console() << "Internal error";
+                               Abort();
+                       }
+
+                       if (iStat == 0)
+                       {
+                               mu::console() << "Test passed (" << ParserTester::c_iCount << " expressions)" << endl;
+                       }
+                       else
+                       {
+                               mu::console() << "Test failed with " << iStat
+                                       << " errors (" << ParserTester::c_iCount
+                                       << " expressions)" << endl;
+                       }
+                       ParserTester::c_iCount = 0;
+                       return iStat;
+               }
+
+
+               //---------------------------------------------------------------------------
+               int ParserTester::ThrowTest(const string_type& a_str, int a_iErrc, bool a_expectedToFail)
+               {
+                       ParserTester::c_iCount++;
+
+                       try
+                       {
+                               value_type fVal[] = { 1,1,1 };
+                               Parser p;
+
+                               p.DefineVar(_T("a"), &fVal[0]);
+                               p.DefineVar(_T("b"), &fVal[1]);
+                               p.DefineVar(_T("c"), &fVal[2]);
+                               p.DefinePostfixOprt(_T("{m}"), Milli);
+                               p.DefinePostfixOprt(_T("m"), Milli);
+                               p.DefineFun(_T("ping"), Ping);
+                               p.DefineFun(_T("valueof"), ValueOf);
+                               p.DefineFun(_T("strfun1"), StrFun1);
+                               p.DefineFun(_T("strfun2"), StrFun2);
+                               p.DefineFun(_T("strfun3"), StrFun3);
+                               p.DefineFun(_T("strfun4"), StrFun4);
+                               p.DefineFun(_T("strfun5"), StrFun5);
+                               p.SetExpr(a_str);
+//                             p.EnableDebugDump(1, 0);
+                               p.Eval();
+                       }
+                       catch (ParserError& e)
+                       {
+                               // output the formula in case of an failed test
+                               if (a_expectedToFail == false || (a_expectedToFail == true && a_iErrc != e.GetCode()))
+                               {
+                                       mu::console() << _T("\n  ")
+                                               << _T("Expression: ") << a_str
+                                               << _T("  Code:") << e.GetCode() << _T("(") << e.GetMsg() << _T(")")
+                                               << _T("  Expected:") << a_iErrc;
+                               }
+
+                               return (a_iErrc == e.GetCode()) ? 0 : 1;
+                       }
+
+                       // if a_expectedToFail == false no exception is expected
+                       bool bRet((a_expectedToFail == false) ? 0 : 1);
+                       if (bRet == 1)
+                       {
+                               mu::console() << _T("\n  ")
+                                       << _T("Expression: ") << a_str
+                                       << _T("  did evaluate; Expected error:") << a_iErrc;
+                       }
+
+                       return bRet;
+               }
+
+               //---------------------------------------------------------------------------
+               /** \brief Evaluate a tet expression.
+
+                       \return 1 in case of a failure, 0 otherwise.
+               */
+               int ParserTester::EqnTestWithVarChange(const string_type& a_str,
+                       double a_fVar1,
+                       double a_fRes1,
+                       double a_fVar2,
+                       double a_fRes2)
+               {
+                       ParserTester::c_iCount++;
+
+                       try
+                       {
+                               value_type fVal[2] = { -999, -999 }; // should be equal
+
+                               Parser  p;
+                               value_type var = 0;
+
+                               // variable
+                               p.DefineVar(_T("a"), &var);
+                               p.SetExpr(a_str);
+
+                               var = a_fVar1;
+                               fVal[0] = p.Eval();
+
+                               var = a_fVar2;
+                               fVal[1] = p.Eval();
+
+                               if (fabs(a_fRes1 - fVal[0]) > 0.0000000001)
+                                       throw std::runtime_error("incorrect result (first pass)");
+
+                               if (fabs(a_fRes2 - fVal[1]) > 0.0000000001)
+                                       throw std::runtime_error("incorrect result (second pass)");
+                       }
+                       catch (Parser::exception_type& e)
+                       {
+                               mu::console() << _T("\n  fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")");
+                               return 1;
+                       }
+                       catch (std::exception& e)
+                       {
+                               mu::console() << _T("\n  fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")");
+                               return 1;  // always return a failure since this exception is not expected
+                       }
+                       catch (...)
+                       {
+                               mu::console() << _T("\n  fail: ") << a_str.c_str() << _T(" (unexpected exception)");
+                               return 1;  // exceptions other than ParserException are not allowed
+                       }
+
+                       return 0;
+               }
+
+               //---------------------------------------------------------------------------
+               /** \brief Evaluate a tet expression.
+
+                       \return 1 in case of a failure, 0 otherwise.
+               */
+               int ParserTester::EqnTest(const string_type& a_str, double a_fRes, bool a_fPass)
+               {
+                       ParserTester::c_iCount++;
+                       int iRet(0);
+                       value_type fVal[5] = { -999, -998, -997, -996, -995 }; // initially should be different
+
+                       try
+                       {
+                               std::unique_ptr<Parser> p1;
+                               Parser  p2, p3;   // three parser objects
+                                                                 // they will be used for testing copy and assignment operators
+                               // p1 is a pointer since i'm going to delete it in order to test if
+                               // parsers after copy construction still refer to members of it.
+                               // !! If this is the case this function will crash !!
+
+                               p1.reset(new mu::Parser());
+                               // Add constants
+                               p1->DefineConst(_T("pi"), MathImpl<value_type>::CONST_PI);
+                               p1->DefineConst(_T("e"), MathImpl<value_type>::CONST_E);
+                               p1->DefineConst(_T("const"), 1);
+                               p1->DefineConst(_T("const1"), 2);
+                               p1->DefineConst(_T("const2"), 3);
+                               // string constants
+                               p1->DefineStrConst(_T("str1"), _T("1.11"));
+                               p1->DefineStrConst(_T("str2"), _T("2.22"));
+                               // variables
+                               value_type vVarVal[] = { 1, 2, 3, -2 };
+                               p1->DefineVar(_T("a"), &vVarVal[0]);
+                               p1->DefineVar(_T("aa"), &vVarVal[1]);
+                               p1->DefineVar(_T("b"), &vVarVal[1]);
+                               p1->DefineVar(_T("c"), &vVarVal[2]);
+                               p1->DefineVar(_T("d"), &vVarVal[3]);
+
+                               // custom value ident functions
+                               p1->AddValIdent(&ParserTester::IsHexVal);
+
+                               // functions
+                               p1->DefineFun(_T("ping"), Ping);
+                               p1->DefineFun(_T("f0"), f0);        // no parameter
+                               p1->DefineFun(_T("f1of1"), f1of1);  // one parameter
+                               p1->DefineFun(_T("f1of2"), f1of2);  // two parameter
+                               p1->DefineFun(_T("f2of2"), f2of2);
+                               p1->DefineFun(_T("f1of3"), f1of3);  // three parameter
+                               p1->DefineFun(_T("f2of3"), f2of3);
+                               p1->DefineFun(_T("f3of3"), f3of3);
+                               p1->DefineFun(_T("f1of4"), f1of4);  // four parameter
+                               p1->DefineFun(_T("f2of4"), f2of4);
+                               p1->DefineFun(_T("f3of4"), f3of4);
+                               p1->DefineFun(_T("f4of4"), f4of4);
+                               p1->DefineFun(_T("f1of5"), f1of5);  // five parameter
+                               p1->DefineFun(_T("f2of5"), f2of5);
+                               p1->DefineFun(_T("f3of5"), f3of5);
+                               p1->DefineFun(_T("f4of5"), f4of5);
+                               p1->DefineFun(_T("f5of5"), f5of5);
+
+                               // binary operators
+                               p1->DefineOprt(_T("add"), add, 0);
+                               p1->DefineOprt(_T("++"), add, 0);
+                               p1->DefineOprt(_T("&"), land, prLAND);
+
+                               // sample functions
+                               p1->DefineFun(_T("min"), Min);
+                               p1->DefineFun(_T("max"), Max);
+                               p1->DefineFun(_T("sum"), Sum);
+                               p1->DefineFun(_T("valueof"), ValueOf);
+                               p1->DefineFun(_T("atof"), StrToFloat);
+                               p1->DefineFun(_T("strfun1"), StrFun1);
+                               p1->DefineFun(_T("strfun2"), StrFun2);
+                               p1->DefineFun(_T("strfun3"), StrFun3);
+                               p1->DefineFun(_T("strfun4"), StrFun4);
+                               p1->DefineFun(_T("strfun5"), StrFun5);
+                               p1->DefineFun(_T("lastArg"), LastArg);
+                               p1->DefineFun(_T("firstArg"), FirstArg);
+                               p1->DefineFun(_T("order"), FirstArg);
+
+                               // infix / postfix operator
+                               // Note: Identifiers used here do not have any meaning 
+                               //       they are mere placeholders to test certain features.
+                               p1->DefineInfixOprt(_T("$"), sign, prPOW + 1);  // sign with high priority
+                               p1->DefineInfixOprt(_T("~"), plus2);          // high priority
+                               p1->DefineInfixOprt(_T("~~"), plus2);
+                               p1->DefinePostfixOprt(_T("{m}"), Milli);
+                               p1->DefinePostfixOprt(_T("{M}"), Mega);
+                               p1->DefinePostfixOprt(_T("m"), Milli);
+                               p1->DefinePostfixOprt(_T("meg"), Mega);
+                               p1->DefinePostfixOprt(_T("#"), times3);
+                               p1->DefinePostfixOprt(_T("'"), sqr);
+                               p1->SetExpr(a_str);
+
+                               // Test bytecode integrity
+                               // String parsing and bytecode parsing must yield the same result
+                               fVal[0] = p1->Eval(); // result from stringparsing
+                               fVal[1] = p1->Eval(); // result from bytecode
+                               if (fVal[0] != fVal[1])
+                                       throw Parser::exception_type(_T("Bytecode / string parsing mismatch."));
+
+                               // Test copy and assignment operators
+                               try
+                               {
+                                       // Test copy constructor
+                                       std::vector<mu::Parser> vParser;
+                                       vParser.push_back(*(p1.get()));
+                                       mu::Parser p4 = vParser[0];   // take parser from vector
+
+                                       // destroy the originals from p2
+                                       vParser.clear();              // delete the vector
+                                       p1.reset(0);
+
+                                       fVal[2] = p4.Eval();
+
+                                       // Test assignment operator
+                                       // additionally  disable Optimizer this time
+                                       mu::Parser p5;
+                                       p5 = p4;
+                                       p5.EnableOptimizer(false);
+                                       fVal[3] = p5.Eval();
+
+                                       // Test Eval function for multiple return values
+                                       // use p2 since it has the optimizer enabled!
+                                       int nNum;
+                                       value_type* v = p4.Eval(nNum);
+                                       fVal[4] = v[nNum - 1];
+                               }
+                               catch (std::exception& e)
+                               {
+                                       mu::console() << _T("\n  ") << e.what() << _T("\n");
+                               }
+
+                               // limited floating point accuracy requires the following test
+                               bool bCloseEnough(true);
+                               for (unsigned i = 0; i < sizeof(fVal) / sizeof(value_type); ++i)
+                               {
+                                       bCloseEnough &= (fabs(a_fRes - fVal[i]) <= fabs(fVal[i] * 0.00001));
+
+                                       // The tests equations never result in infinity, if they do thats a bug.
+                                       // reference:
+                                       // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/5037825
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4127)
+#endif
+                                       if (std::numeric_limits<value_type>::has_infinity)
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+                                       {
+                                               bCloseEnough &= (fabs(fVal[i]) != numeric_limits<value_type>::infinity());
+                                       }
+                               }
+
+                               iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1;
+
+
+                               if (iRet == 1)
+                               {
+                                       mu::console() << _T("\n  fail: ") << a_str.c_str()
+                                               << _T(" (incorrect result; expected: ") << a_fRes
+                                               << _T(" ;calculated: ") << fVal[0] << _T(",")
+                                               << fVal[1] << _T(",")
+                                               << fVal[2] << _T(",")
+                                               << fVal[3] << _T(",")
+                                               << fVal[4] << _T(").");
+                               }
+                       }
+                       catch (Parser::exception_type& e)
+                       {
+                               if (a_fPass)
+                               {
+                                       if (fVal[0] != fVal[2] && fVal[0] != -999 && fVal[1] != -998)
+                                               mu::console() << _T("\n  fail: ") << a_str.c_str() << _T(" (copy construction)");
+                                       else
+                                               mu::console() << _T("\n  fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")");
+                                       return 1;
+                               }
+                       }
+                       catch (std::exception& e)
+                       {
+                               mu::console() << _T("\n  fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")");
+                               return 1;  // always return a failure since this exception is not expected
+                       }
+                       catch (...)
+                       {
+                               mu::console() << _T("\n  fail: ") << a_str.c_str() << _T(" (unexpected exception)");
+                               return 1;  // exceptions other than ParserException are not allowed
+                       }
+
+                       return iRet;
+               }
+
+               //---------------------------------------------------------------------------
+               int ParserTester::EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass)
+               {
+                       ParserTester::c_iCount++;
+
+                       value_type vVarVal[] = { 1, 2, 3 };   // variable values
+                       int iRet(0);
+
+                       try
+                       {
+                               value_type fVal[2] = { -99, -999 };   // results: initially should be different
+                               ParserInt p;
+                               p.DefineConst(_T("const1"), 1);
+                               p.DefineConst(_T("const2"), 2);
+                               p.DefineVar(_T("a"), &vVarVal[0]);
+                               p.DefineVar(_T("b"), &vVarVal[1]);
+                               p.DefineVar(_T("c"), &vVarVal[2]);
+
+                               p.SetExpr(a_str);
+                               fVal[0] = p.Eval(); // result from stringparsing
+                               fVal[1] = p.Eval(); // result from bytecode
+
+                               if (fVal[0] != fVal[1])
+                                       throw Parser::exception_type(_T("Bytecode corrupt."));
+
+                               iRet = ((a_fRes == fVal[0] && a_fPass) ||
+                                       (a_fRes != fVal[0] && !a_fPass)) ? 0 : 1;
+                               if (iRet == 1)
+                               {
+                                       mu::console() << _T("\n  fail: ") << a_str.c_str()
+                                               << _T(" (incorrect result; expected: ") << a_fRes
+                                               << _T(" ;calculated: ") << fVal[0] << _T(").");
+                               }
+                       }
+                       catch (Parser::exception_type& e)
+                       {
+                               if (a_fPass)
+                               {
+                                       mu::console() << _T("\n  fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg();
+                                       iRet = 1;
+                               }
+                       }
+                       catch (...)
+                       {
+                               mu::console() << _T("\n  fail: ") << a_str.c_str() << _T(" (unexpected exception)");
+                               iRet = 1;  // exceptions other than ParserException are not allowed
+                       }
+
+                       return iRet;
+               }
+
+               //---------------------------------------------------------------------------
+               /** \brief Test an expression in Bulk Mode. */
+               int ParserTester::EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass)
+               {
+                       ParserTester::c_iCount++;
+
+                       // Define Bulk Variables
+                       int nBulkSize = 4;
+                       value_type vVariableA[] = { 1, 2, 3, 4 };   // variable values
+                       value_type vVariableB[] = { 2, 2, 2, 2 };   // variable values
+                       value_type vVariableC[] = { 3, 3, 3, 3 };   // variable values
+                       value_type vResults[] = { 0, 0, 0, 0 };   // variable values
+                       int iRet(0);
+
+                       try
+                       {
+                               Parser p;
+                               p.DefineConst(_T("const1"), 1);
+                               p.DefineConst(_T("const2"), 2);
+                               p.DefineVar(_T("a"), vVariableA);
+                               p.DefineVar(_T("b"), vVariableB);
+                               p.DefineVar(_T("c"), vVariableC);
+
+                               p.SetExpr(a_str);
+                               p.Eval(vResults, nBulkSize);
+
+                               bool bCloseEnough(true);
+                               for (int i = 0; i < nBulkSize; ++i)
+                               {
+                                       bCloseEnough &= (fabs(a_fRes[i] - vResults[i]) <= fabs(a_fRes[i] * 0.00001));
+                               }
+
+                               iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1;
+                               if (iRet == 1)
+                               {
+                                       mu::console() << _T("\n  fail: ") << a_str.c_str()
+                                               << _T(" (incorrect result; expected: {") << a_fRes[0] << _T(",") << a_fRes[1] << _T(",") << a_fRes[2] << _T(",") << a_fRes[3] << _T("}")
+                                               << _T(" ;calculated: ") << vResults[0] << _T(",") << vResults[1] << _T(",") << vResults[2] << _T(",") << vResults[3] << _T("}");
+                               }
+                       }
+                       catch (Parser::exception_type& e)
+                       {
+                               if (a_fPass)
+                               {
+                                       mu::console() << _T("\n  fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg();
+                                       iRet = 1;
+                               }
+                       }
+                       catch (...)
+                       {
+                               mu::console() << _T("\n  fail: ") << a_str.c_str() << _T(" (unexpected exception)");
+                               iRet = 1;  // exceptions other than ParserException are not allowed
+                       }
+
+                       return iRet;
+               }
+
+               //---------------------------------------------------------------------------
+               /** \brief Internal error in test class Test is going to be aborted. */
+               void ParserTester::Abort() const
+               {
+                       mu::console() << _T("Test failed (internal error in test class)") << endl;
+                       while (!getchar());
+                       exit(-1);
+               }
+       } // namespace test
+} // namespace mu
diff --git a/src/external/muparser/muParserTest.h b/src/external/muparser/muParserTest.h
new file mode 100644 (file)
index 0000000..9f4be55
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_TEST_H
+#define MU_PARSER_TEST_H
+
+#include <string>
+#include <cstdlib>
+#include <numeric> // for accumulate
+#include "muParser.h"
+#include "muParserInt.h"
+
+/** \file
+       \brief This file contains the parser test class.
+*/
+
+namespace mu
+{
+       /** \brief Namespace for test cases. */
+       namespace Test
+       {
+               /** \brief Test cases for unit testing. */
+               class API_EXPORT_CXX ParserTester final
+               {
+               private:
+                       static int c_iCount;
+
+                       static value_type f0() { return 42; };
+
+                       // Multiarg callbacks
+                       static value_type f1of1(value_type v) { return v; };
+
+                       static value_type f1of2(value_type v, value_type) { return v; };
+                       static value_type f2of2(value_type, value_type v) { return v; };
+
+                       static value_type f1of3(value_type v, value_type, value_type) { return v; };
+                       static value_type f2of3(value_type, value_type v, value_type) { return v; };
+                       static value_type f3of3(value_type, value_type, value_type v) { return v; };
+
+                       static value_type f1of4(value_type v, value_type, value_type, value_type) { return v; }
+                       static value_type f2of4(value_type, value_type v, value_type, value_type) { return v; }
+                       static value_type f3of4(value_type, value_type, value_type v, value_type) { return v; }
+                       static value_type f4of4(value_type, value_type, value_type, value_type v) { return v; }
+
+                       static value_type f1of5(value_type v, value_type, value_type, value_type, value_type) { return v; }
+                       static value_type f2of5(value_type, value_type v, value_type, value_type, value_type) { return v; }
+                       static value_type f3of5(value_type, value_type, value_type v, value_type, value_type) { return v; }
+                       static value_type f4of5(value_type, value_type, value_type, value_type v, value_type) { return v; }
+                       static value_type f5of5(value_type, value_type, value_type, value_type, value_type v) { return v; }
+
+                       static value_type Min(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1 < a_fVal2) ? a_fVal1 : a_fVal2; }
+                       static value_type Max(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1 > a_fVal2) ? a_fVal1 : a_fVal2; }
+
+                       static value_type plus2(value_type v1) { return v1 + 2; }
+                       static value_type times3(value_type v1) { return v1 * 3; }
+                       static value_type sqr(value_type v1) { return v1 * v1; }
+                       static value_type sign(value_type v) { return -v; }
+                       static value_type add(value_type v1, value_type v2) { return v1 + v2; }
+                       static value_type land(value_type v1, value_type v2) { return (int)v1 & (int)v2; }
+
+
+                       static value_type FirstArg(const value_type* a_afArg, int a_iArgc)
+                       {
+                               if (!a_iArgc)
+                                       throw mu::Parser::exception_type(_T("too few arguments for function FirstArg."));
+
+                               return  a_afArg[0];
+                       }
+
+                       static value_type LastArg(const value_type* a_afArg, int a_iArgc)
+                       {
+                               if (!a_iArgc)
+                                       throw mu::Parser::exception_type(_T("too few arguments for function LastArg."));
+
+                               return  a_afArg[a_iArgc - 1];
+                       }
+
+                       static value_type Sum(const value_type* a_afArg, int a_iArgc)
+                       {
+                               if (!a_iArgc)
+                                       throw mu::Parser::exception_type(_T("too few arguments for function sum."));
+
+                               value_type fRes = 0;
+                               for (int i = 0; i < a_iArgc; ++i) fRes += a_afArg[i];
+                               return fRes;
+                       }
+
+                       static value_type Rnd(value_type v)
+                       {
+                               return (value_type)(1 + (v * std::rand() / (RAND_MAX + 1.0)));
+                       }
+
+                       static value_type RndWithString(const char_type*)
+                       {
+                               return (value_type)(1.0 + (1000.0 * std::rand() / (RAND_MAX + 1.0)));
+                       }
+
+                       static value_type Ping()
+                       {
+                               return 10;
+                       }
+
+                       static value_type ValueOf(const char_type*)
+                       {
+                               return 123;
+                       }
+
+                       static value_type StrFun1(const char_type* v1)
+                       {
+                               int val(0);
+                               stringstream_type(v1) >> val;
+                               return (value_type)val;
+                       }
+
+                       static value_type StrFun2(const char_type* v1, value_type v2)
+                       {
+                               int val(0);
+                               stringstream_type(v1) >> val;
+                               return (value_type)(val + v2);
+                       }
+
+                       static value_type StrFun3(const char_type* v1, value_type v2, value_type v3)
+                       {
+                               int val(0);
+                               stringstream_type(v1) >> val;
+                               return val + v2 + v3;
+                       }
+
+                       static value_type StrFun4(const char_type* v1, value_type v2, value_type v3, value_type v4)
+                       {
+                               int val(0);
+                               stringstream_type(v1) >> val;
+                               return val + v2 + v3 + v4;
+                       }
+
+                       static value_type StrFun5(const char_type* v1, value_type v2, value_type v3, value_type v4, value_type v5)
+                       {
+                               int val(0);
+                               stringstream_type(v1) >> val;
+                               return val + v2 + v3 + v4 + v5;
+                       }
+
+                       static value_type StrToFloat(const char_type* a_szMsg)
+                       {
+                               value_type val(0);
+                               stringstream_type(a_szMsg) >> val;
+                               return val;
+                       }
+
+                       // postfix operator callback
+                       static value_type Mega(value_type a_fVal) { return a_fVal * (value_type)1e6; }
+                       static value_type Micro(value_type a_fVal) { return a_fVal * (value_type)1e-6; }
+                       static value_type Milli(value_type a_fVal) { return a_fVal / (value_type)1e3; }
+
+                       // Custom value recognition
+                       static int IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal);
+
+                       int TestNames();
+                       int TestSyntax();
+                       int TestMultiArg();
+                       int TestPostFix();
+                       int TestExpression();
+                       int TestInfixOprt();
+                       int TestBinOprt();
+                       int TestVarConst();
+                       int TestInterface();
+                       int TestException();
+                       int TestStrArg();
+                       int TestIfThenElse();
+                       int TestBulkMode();
+                       int TestOssFuzzTestCases();
+
+                       void Abort() const;
+
+               public:
+                       typedef int (ParserTester::* testfun_type)();
+
+                       ParserTester();
+                       int Run();
+
+               private:
+                       std::vector<testfun_type> m_vTestFun;
+                       void AddTest(testfun_type a_pFun);
+
+                       // Test Double Parser
+                       int EqnTest(const string_type& a_str, double a_fRes, bool a_fPass);
+                       int EqnTestWithVarChange(const string_type& a_str, double a_fRes1, double a_fVar1,      double a_fRes2, double a_fVar2);
+                       int ThrowTest(const string_type& a_str, int a_iErrc, bool a_bFail = true);
+
+                       // Test Int Parser
+                       int EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass);
+
+                       // Test Bulkmode
+                       int EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass);
+
+               };
+       } // namespace Test
+} // namespace mu
+
+#endif
+
diff --git a/src/external/muparser/muParserToken.h b/src/external/muparser/muParserToken.h
new file mode 100644 (file)
index 0000000..f12a4e8
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_TOKEN_H
+#define MU_PARSER_TOKEN_H
+
+#include <string>
+#include <stack>
+#include <vector>
+#include <memory>
+
+#if defined(_MSC_VER)
+       #pragma warning(push)
+       #pragma warning(disable : 26812) 
+#endif
+
+#include "muParserError.h"
+#include "muParserCallback.h"
+
+/** \file
+       \brief This file contains the parser token definition.
+*/
+
+namespace mu
+{
+       /** \brief Encapsulation of the data for a single formula token.
+
+               Formula token implementation. Part of the Math Parser Package.
+               Formula tokens can be either one of the following:
+               <ul>
+                       <li>value</li>
+                       <li>variable</li>
+                       <li>function with numerical arguments</li>
+                       <li>functions with a string as argument</li>
+                       <li>prefix operators</li>
+                       <li>infix operators</li>
+                       <li>binary operator</li>
+               </ul>
+       */
+       template<typename TBase, typename TString>
+       class ParserToken final
+       {
+       private:
+
+               ECmdCode  m_iCode;  ///< Type of the token; The token type is a constant of type #ECmdCode.
+               ETypeCode m_iType;
+               void* m_pTok;           ///< Stores Token pointer; not applicable for all tokens
+               int  m_iIdx;            ///< An otional index to an external buffer storing the token data
+               TString m_strTok;   ///< Token string
+               TString m_strVal;   ///< Value for string variables
+               value_type m_fVal;  ///< the value 
+               std::unique_ptr<ParserCallback> m_pCallback;
+
+       public:
+
+               /** \brief Constructor (default).
+
+                       Sets token to an neutral state of type cmUNKNOWN.
+                       \throw nothrow
+                       \sa ECmdCode
+               */
+               ParserToken()
+                       :m_iCode(cmUNKNOWN)
+                       , m_iType(tpVOID)
+                       , m_pTok(0)
+                       , m_iIdx(-1)
+                       , m_strTok()
+                       , m_strVal()
+                       , m_fVal(0)
+                       , m_pCallback()
+               {}
+
+               //------------------------------------------------------------------------------
+               /** \brief Create token from another one.
+
+                       Implemented by calling Assign(...)
+                       \throw nothrow
+                       \post m_iType==cmUNKNOWN
+                       \sa #Assign
+               */
+               ParserToken(const ParserToken& a_Tok)
+               {
+                       Assign(a_Tok);
+               }
+
+               
+               /** \brief Assignment operator.
+
+                       Copy token state from another token and return this.
+                       Implemented by calling Assign(...).
+                       \throw nothrow
+               */
+               ParserToken& operator=(const ParserToken& a_Tok)
+               {
+                       Assign(a_Tok);
+                       return *this;
+               }
+
+
+               /** \brief Copy token information from argument.
+
+                       \throw nothrow
+               */
+               void Assign(const ParserToken& a_Tok)
+               {
+                       m_iCode = a_Tok.m_iCode;
+                       m_pTok = a_Tok.m_pTok;
+                       m_strTok = a_Tok.m_strTok;
+                       m_iIdx = a_Tok.m_iIdx;
+                       m_strVal = a_Tok.m_strVal;
+                       m_iType = a_Tok.m_iType;
+                       m_fVal = a_Tok.m_fVal;
+                       // create new callback object if a_Tok has one 
+                       m_pCallback.reset(a_Tok.m_pCallback.get() ? a_Tok.m_pCallback->Clone() : 0);
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Assign a token type.
+
+                 Token may not be of type value, variable or function. Those have separate set functions.
+
+                 \pre [assert] a_iType!=cmVAR
+                 \pre [assert] a_iType!=cmVAL
+                 \pre [assert] a_iType!=cmFUNC
+                 \post m_fVal = 0
+                 \post m_pTok = 0
+               */
+               ParserToken& Set(ECmdCode a_iType, const TString& a_strTok = TString())
+               {
+                       // The following types can't be set this way, they have special Set functions
+                       MUP_ASSERT(a_iType != cmVAR);
+                       MUP_ASSERT(a_iType != cmVAL);
+                       MUP_ASSERT(a_iType != cmFUNC);
+
+                       m_iCode = a_iType;
+                       m_iType = tpVOID;
+                       m_pTok = 0;
+                       m_strTok = a_strTok;
+                       m_iIdx = -1;
+
+                       return *this;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Set Callback type. */
+               ParserToken& Set(const ParserCallback& a_pCallback, const TString& a_sTok)
+               {
+                       MUP_ASSERT(a_pCallback.GetAddr());
+
+                       m_iCode = a_pCallback.GetCode();
+                       m_iType = tpVOID;
+                       m_strTok = a_sTok;
+                       m_pCallback.reset(new ParserCallback(a_pCallback));
+
+                       m_pTok = 0;
+                       m_iIdx = -1;
+
+                       return *this;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Make this token a value token.
+
+                       Member variables not necessary for value tokens will be invalidated.
+                       \throw nothrow
+               */
+               ParserToken& SetVal(TBase a_fVal, const TString& a_strTok = TString())
+               {
+                       m_iCode = cmVAL;
+                       m_iType = tpDBL;
+                       m_fVal = a_fVal;
+                       m_strTok = a_strTok;
+                       m_iIdx = -1;
+
+                       m_pTok = 0;
+                       m_pCallback.reset(0);
+
+                       return *this;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief make this token a variable token.
+
+                       Member variables not necessary for variable tokens will be invalidated.
+                       \throw nothrow
+               */
+               ParserToken& SetVar(TBase* a_pVar, const TString& a_strTok)
+               {
+                       m_iCode = cmVAR;
+                       m_iType = tpDBL;
+                       m_strTok = a_strTok;
+                       m_iIdx = -1;
+                       m_pTok = (void*)a_pVar;
+                       m_pCallback.reset(0);
+                       return *this;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Make this token a variable token.
+
+                       Member variables not necessary for variable tokens will be invalidated.
+                       \throw nothrow
+               */
+               ParserToken& SetString(const TString& a_strTok, std::size_t a_iSize)
+               {
+                       m_iCode = cmSTRING;
+                       m_iType = tpSTR;
+                       m_strTok = a_strTok;
+                       m_iIdx = static_cast<int>(a_iSize);
+
+                       m_pTok = 0;
+                       m_pCallback.reset(0);
+                       return *this;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Set an index associated with the token related data.
+
+                       In cmSTRFUNC - This is the index to a string table in the main parser.
+                       \param a_iIdx The index the string function result will take in the bytecode parser.
+                       \throw exception_type if #a_iIdx<0 or #m_iType!=cmSTRING
+               */
+               void SetIdx(int a_iIdx)
+               {
+                       if (m_iCode != cmSTRING || a_iIdx < 0)
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       m_iIdx = a_iIdx;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Return Index associated with the token related data.
+
+                       In cmSTRFUNC - This is the index to a string table in the main parser.
+
+                       \throw exception_type if #m_iIdx<0 or #m_iType!=cmSTRING
+                       \return The index the result will take in the Bytecode calculatin array (#m_iIdx).
+               */
+               int GetIdx() const
+               {
+                       if (m_iIdx < 0 || m_iCode != cmSTRING)
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       return m_iIdx;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Return the token type.
+
+                       \return #m_iType
+                       \throw nothrow
+               */
+               ECmdCode GetCode() const
+               {
+                       if (m_pCallback.get())
+                       {
+                               return m_pCallback->GetCode();
+                       }
+                       else
+                       {
+                               return m_iCode;
+                       }
+               }
+
+               //------------------------------------------------------------------------------
+               ETypeCode GetType() const
+               {
+                       if (m_pCallback.get())
+                       {
+                               return m_pCallback->GetType();
+                       }
+                       else
+                       {
+                               return m_iType;
+                       }
+               }
+
+               //------------------------------------------------------------------------------
+               int GetPri() const
+               {
+                       if (!m_pCallback.get())
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       if (m_pCallback->GetCode() != cmOPRT_BIN && m_pCallback->GetCode() != cmOPRT_INFIX)
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       return m_pCallback->GetPri();
+               }
+
+               //------------------------------------------------------------------------------
+               EOprtAssociativity GetAssociativity() const
+               {
+                       if (m_pCallback.get() == nullptr || m_pCallback->GetCode() != cmOPRT_BIN)
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       return m_pCallback->GetAssociativity();
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Return the address of the callback function assoziated with
+                                  function and operator tokens.
+
+                       \return The pointer stored in #m_pTok.
+                       \throw exception_type if token type is non of:
+                                  <ul>
+                                        <li>cmFUNC</li>
+                                        <li>cmSTRFUNC</li>
+                                        <li>cmPOSTOP</li>
+                                        <li>cmINFIXOP</li>
+                                        <li>cmOPRT_BIN</li>
+                                  </ul>
+                       \sa ECmdCode
+               */
+               generic_fun_type GetFuncAddr() const
+               {
+                       return (m_pCallback.get()) ? (generic_fun_type)m_pCallback->GetAddr() : 0;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \biref Get value of the token.
+
+                       Only applicable to variable and value tokens.
+                       \throw exception_type if token is no value/variable token.
+               */
+               TBase GetVal() const
+               {
+                       switch (m_iCode)
+                       {
+                       case cmVAL:  return m_fVal;
+                       case cmVAR:  return *((TBase*)m_pTok);
+                       default:     throw ParserError(ecVAL_EXPECTED);
+                       }
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Get address of a variable token.
+
+                 Valid only if m_iType==CmdVar.
+                 \throw exception_type if token is no variable token.
+               */
+               TBase* GetVar() const
+               {
+                       if (m_iCode != cmVAR)
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       return (TBase*)m_pTok;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Return the number of function arguments.
+
+                 Valid only if m_iType==CmdFUNC.
+               */
+               int GetArgCount() const
+               {
+                       MUP_ASSERT(m_pCallback.get());
+
+                       if (!m_pCallback->GetAddr())
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       return m_pCallback->GetArgc();
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Return the token identifier.
+
+                       If #m_iType is cmSTRING the token identifier is the value of the string argument
+                       for a string function.
+                       \return #m_strTok
+                       \throw nothrow
+                       \sa m_strTok
+               */
+               const TString& GetAsString() const
+               {
+                       return m_strTok;
+               }
+       };
+} // namespace mu
+
+#if defined(_MSC_VER)
+       #pragma warning(pop)
+#endif
+
+#endif
diff --git a/src/external/muparser/muParserTokenReader.cpp b/src/external/muparser/muParserTokenReader.cpp
new file mode 100644 (file)
index 0000000..f478d6a
--- /dev/null
@@ -0,0 +1,993 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <cstdio>
+#include <cstring>
+#include <map>
+#include <stack>
+#include <string>
+
+#include "muParserTokenReader.h"
+#include "muParserBase.h"
+
+#if defined(_MSC_VER)
+       #pragma warning(push)
+       #pragma warning(disable : 26812) 
+#endif
+
+/** \file
+       \brief This file contains the parser token reader implementation.
+*/
+
+
+namespace mu
+{
+
+       // Forward declaration
+       class ParserBase;
+
+       /** \brief Copy constructor.
+
+               \sa Assign
+               \throw nothrow
+       */
+       ParserTokenReader::ParserTokenReader(const ParserTokenReader& a_Reader)
+       {
+               Assign(a_Reader);
+       }
+
+
+       /** \brief Assignment operator.
+
+               Self assignment will be suppressed otherwise #Assign is called.
+
+               \param a_Reader Object to copy to this token reader.
+               \throw nothrow
+       */
+       ParserTokenReader& ParserTokenReader::operator=(const ParserTokenReader& a_Reader)
+       {
+               if (&a_Reader != this)
+                       Assign(a_Reader);
+
+               return *this;
+       }
+
+
+       /** \brief Assign state of a token reader to this token reader.
+
+               \param a_Reader Object from which the state should be copied.
+               \throw nothrow
+       */
+       void ParserTokenReader::Assign(const ParserTokenReader& a_Reader)
+       {
+               m_pParser = a_Reader.m_pParser;
+               m_strFormula = a_Reader.m_strFormula;
+               m_iPos = a_Reader.m_iPos;
+               m_iSynFlags = a_Reader.m_iSynFlags;
+
+               m_UsedVar = a_Reader.m_UsedVar;
+               m_pFunDef = a_Reader.m_pFunDef;
+               m_pConstDef = a_Reader.m_pConstDef;
+               m_pVarDef = a_Reader.m_pVarDef;
+               m_pStrVarDef = a_Reader.m_pStrVarDef;
+               m_pPostOprtDef = a_Reader.m_pPostOprtDef;
+               m_pInfixOprtDef = a_Reader.m_pInfixOprtDef;
+               m_pOprtDef = a_Reader.m_pOprtDef;
+               m_bIgnoreUndefVar = a_Reader.m_bIgnoreUndefVar;
+               m_vIdentFun = a_Reader.m_vIdentFun;
+               m_pFactory = a_Reader.m_pFactory;
+               m_pFactoryData = a_Reader.m_pFactoryData;
+               m_bracketStack = a_Reader.m_bracketStack;
+               m_cArgSep = a_Reader.m_cArgSep;
+               m_fZero = a_Reader.m_fZero;
+               m_lastTok = a_Reader.m_lastTok;
+       }
+
+
+       /** \brief Constructor.
+
+               Create a Token reader and bind it to a parser object.
+
+               \pre [assert] a_pParser may not be NULL
+               \post #m_pParser==a_pParser
+               \param a_pParent Parent parser object of the token reader.
+       */
+       ParserTokenReader::ParserTokenReader(ParserBase* a_pParent)
+               :m_pParser(a_pParent)
+               , m_strFormula()
+               , m_iPos(0)
+               , m_iSynFlags(0)
+               , m_bIgnoreUndefVar(false)
+               , m_pFunDef(nullptr)
+               , m_pPostOprtDef(nullptr)
+               , m_pInfixOprtDef(nullptr)
+               , m_pOprtDef(nullptr)
+               , m_pConstDef(nullptr)
+               , m_pStrVarDef(nullptr)
+               , m_pVarDef(nullptr)
+               , m_pFactory(nullptr)
+               , m_pFactoryData(nullptr)
+               , m_vIdentFun()
+               , m_UsedVar()
+               , m_fZero(0)
+               , m_bracketStack()
+               , m_lastTok()
+               , m_cArgSep(',')
+       {
+               MUP_ASSERT(m_pParser != nullptr);
+               SetParent(m_pParser);
+       }
+
+
+       /** \brief Create instance of a ParserTokenReader identical with this
+                               and return its pointer.
+
+               This is a factory method the calling function must take care of the object destruction.
+
+               \return A new ParserTokenReader object.
+               \throw nothrow
+       */
+       ParserTokenReader* ParserTokenReader::Clone(ParserBase* a_pParent) const
+       {
+               std::unique_ptr<ParserTokenReader> ptr(new ParserTokenReader(*this));
+               ptr->SetParent(a_pParent);
+               return ptr.release();
+       }
+
+
+       ParserTokenReader::token_type& ParserTokenReader::SaveBeforeReturn(const token_type& tok)
+       {
+               m_lastTok = tok;
+               return m_lastTok;
+       }
+
+
+       void ParserTokenReader::AddValIdent(identfun_type a_pCallback)
+       {
+               // Use push_front is used to give user defined callbacks a higher priority than
+               // the built in ones. Otherwise reading hex numbers would not work
+               // since the "0" in "0xff" would always be read first making parsing of 
+               // the rest impossible.
+               // reference:
+               // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/4824956
+               m_vIdentFun.push_front(a_pCallback);
+       }
+
+
+       void ParserTokenReader::SetVarCreator(facfun_type a_pFactory, void* pUserData)
+       {
+               m_pFactory = a_pFactory;
+               m_pFactoryData = pUserData;
+       }
+
+
+       /** \brief Return the current position of the token reader in the formula string.
+
+               \return #m_iPos
+               \throw nothrow
+       */
+       int ParserTokenReader::GetPos() const
+       {
+               return m_iPos;
+       }
+
+
+       /** \brief Return a reference to the formula.
+
+               \return #m_strFormula
+               \throw nothrow
+       */
+       const string_type& ParserTokenReader::GetExpr() const
+       {
+               return m_strFormula;
+       }
+
+
+       /** \brief Return a map containing the used variables only. */
+       varmap_type& ParserTokenReader::GetUsedVar()
+       {
+               return m_UsedVar;
+       }
+
+
+       /** \brief Initialize the token Reader.
+
+               Sets the formula position index to zero and set Syntax flags to default for initial formula parsing.
+               \pre [assert] triggered if a_szFormula==0
+       */
+       void ParserTokenReader::SetFormula(const string_type& a_strFormula)
+       {
+               m_strFormula = a_strFormula;
+               ReInit();
+       }
+
+
+       /** \brief Set Flag that controls behaviour in case of undefined variables being found.
+
+         If true, the parser does not throw an exception if an undefined variable is found.
+         otherwise it does. This variable is used internally only!
+         It suppresses a "undefined variable" exception in GetUsedVar().
+         Those function should return a complete list of variables including
+         those the are not defined by the time of it's call.
+       */
+       void ParserTokenReader::IgnoreUndefVar(bool bIgnore)
+       {
+               m_bIgnoreUndefVar = bIgnore;
+       }
+
+
+       /** \brief Reset the token reader to the start of the formula.
+
+               The syntax flags will be reset to a value appropriate for the
+               start of a formula.
+               \post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR
+               \throw nothrow
+               \sa ESynCodes
+       */
+       void ParserTokenReader::ReInit()
+       {
+               m_iPos = 0;
+               m_iSynFlags = sfSTART_OF_LINE;
+               m_bracketStack = std::stack<int>();
+               m_UsedVar.clear();
+               m_lastTok = token_type();
+       }
+
+
+       /** \brief Read the next token from the string. */
+       ParserTokenReader::token_type ParserTokenReader::ReadNextToken()
+       {
+               MUP_ASSERT(m_pParser != nullptr);
+
+               const char_type* szFormula = m_strFormula.c_str();
+               token_type tok;
+
+               // Ignore all non printable characters when reading the expression
+               while (szFormula[m_iPos] > 0 && szFormula[m_iPos] <= 0x20)
+                       ++m_iPos;
+
+               // Check for end of formula
+               if (IsEOF(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check for user defined binary operator
+               if (IsOprt(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check for function token
+               if (IsFunTok(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check built in operators / tokens
+               if (IsBuiltIn(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check for function argument separators
+               if (IsArgSep(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check for values / constant tokens
+               if (IsValTok(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check for variable tokens
+               if (IsVarTok(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check for string variables
+               if (IsStrVarTok(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check for String tokens
+               if (IsString(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check for unary operators
+               if (IsInfixOpTok(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check for unary operators
+               if (IsPostOpTok(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check String for undefined variable token. Done only if a 
+               // flag is set indicating to ignore undefined variables.
+               // This is a way to conditionally avoid an error if 
+               // undefined variables occur. 
+               // (The GetUsedVar function must suppress the error for
+               // undefined variables in order to collect all variable 
+               // names including the undefined ones.)
+               if ((m_bIgnoreUndefVar || m_pFactory) && IsUndefVarTok(tok))
+                       return SaveBeforeReturn(tok);
+
+               // Check for unknown token
+               // 
+               // !!! From this point on there is no exit without an exception possible...
+               // 
+               string_type strTok;
+               auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos);
+               if (iEnd != m_iPos)
+                       Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok);
+
+               Error(ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.substr(m_iPos));
+               return token_type(); // never reached
+       }
+
+
+       void ParserTokenReader::SetParent(ParserBase* a_pParent)
+       {
+               m_pParser = a_pParent;
+               m_pFunDef = &a_pParent->m_FunDef;
+               m_pOprtDef = &a_pParent->m_OprtDef;
+               m_pInfixOprtDef = &a_pParent->m_InfixOprtDef;
+               m_pPostOprtDef = &a_pParent->m_PostOprtDef;
+               m_pVarDef = &a_pParent->m_VarDef;
+               m_pStrVarDef = &a_pParent->m_StrVarDef;
+               m_pConstDef = &a_pParent->m_ConstDef;
+       }
+
+
+       /** \brief Extract all characters that belong to a certain charset.
+
+               \param a_szCharSet [in] Const char array of the characters allowed in the token.
+               \param a_strTok [out]  The string that consists entirely of characters listed in a_szCharSet.
+               \param a_iPos [in] Position in the string from where to start reading.
+               \return The Position of the first character not listed in a_szCharSet.
+               \throw nothrow
+       */
+       int ParserTokenReader::ExtractToken(const char_type* a_szCharSet, string_type& a_sTok, std::size_t a_iPos) const
+       {
+               auto iEnd = m_strFormula.find_first_not_of(a_szCharSet, a_iPos);
+
+               if (iEnd == string_type::npos)
+                       iEnd = m_strFormula.length();
+
+               // Assign token string if there was something found
+               if (a_iPos != iEnd)
+                       a_sTok = string_type(m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd);
+
+               return iEnd;
+       }
+
+
+       /** \brief Check Expression for the presence of a binary operator token.
+
+         Userdefined binary operator "++" gives inconsistent parsing result for
+         the equations "a++b" and "a ++ b" if alphabetic characters are allowed
+         in operator tokens. To avoid this this function checks specifically
+         for operator tokens.
+       */
+       int ParserTokenReader::ExtractOperatorToken(string_type& a_sTok, std::size_t a_iPos) const
+       {
+               // Changed as per Issue 6: https://code.google.com/p/muparser/issues/detail?id=6
+               auto iEnd = m_strFormula.find_first_not_of(m_pParser->ValidOprtChars(), a_iPos);
+               if (iEnd == string_type::npos)
+                       iEnd = m_strFormula.length();
+
+               // Assign token string if there was something found
+               if (a_iPos != iEnd)
+               {
+                       a_sTok = string_type(m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd);
+                       return iEnd;
+               }
+               else
+               {
+                       // There is still the chance of having to deal with an operator consisting exclusively
+                       // of alphabetic characters.
+                       return ExtractToken("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", a_sTok, (std::size_t)a_iPos);
+               }
+       }
+
+
+       /** \brief Check if a built in operator or other token can be found
+               \param a_Tok  [out] Operator token if one is found. This can either be a binary operator or an infix operator token.
+               \return true if an operator token has been found.
+       */
+       bool ParserTokenReader::IsBuiltIn(token_type& a_Tok)
+       {
+               const char_type** const pOprtDef = m_pParser->GetOprtDef(),
+                       * const szFormula = m_strFormula.c_str();
+
+               // Compare token with function and operator strings
+               // check string for operator/function
+               for (int i = 0; pOprtDef[i]; i++)
+               {
+                       std::size_t len(std::char_traits<char_type>::length(pOprtDef[i]));
+                       if (string_type(pOprtDef[i]) == string_type(szFormula + m_iPos, szFormula + m_iPos + len))
+                       {
+                               switch (i)
+                               {
+                               case cmLAND:
+                               case cmLOR:
+                               case cmLT:
+                               case cmGT:
+                               case cmLE:
+                               case cmGE:
+                               case cmNEQ:
+                               case cmEQ:
+                               case cmADD:
+                               case cmSUB:
+                               case cmMUL:
+                               case cmDIV:
+                               case cmPOW:
+                               case cmASSIGN:
+                                       //if (len!=sTok.length())
+                                       //  continue;
+
+                                       // The assignment operator need special treatment
+                                       if (i == cmASSIGN && m_iSynFlags & noASSIGN)
+                                               Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);
+
+                                       if (!m_pParser->HasBuiltInOprt()) continue;
+                                       if (m_iSynFlags & noOPT)
+                                       {
+                                               // Maybe its an infix operator not an operator
+                                               // Both operator types can share characters in 
+                                               // their identifiers
+                                               if (IsInfixOpTok(a_Tok))
+                                                       return true;
+
+                                               Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);
+                                       }
+
+                                       m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE | noEND;
+                                       break;
+
+                               case cmBO:
+                                       if (m_iSynFlags & noBO)
+                                               Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
+
+                                       if (m_lastTok.GetCode() == cmFUNC)
+                                               m_iSynFlags = noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE;
+                                       else
+                                               m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE;
+
+                                       m_bracketStack.push(cmBO);
+                                       break;
+
+                               case cmBC:
+                                       if (m_iSynFlags & noBC)
+                                               Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
+
+                                       m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN;
+
+                                       if (!m_bracketStack.empty())
+                                               m_bracketStack.pop();
+                                       else
+                                               Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
+                                       break;
+
+                               case cmELSE:
+                                       if (m_iSynFlags & noELSE)
+                                               Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);
+
+                                       m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE | noSTR;
+                                       break;
+
+                               case cmIF:
+                                       if (m_iSynFlags & noIF)
+                                               Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);
+
+                                       m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE | noSTR;
+                                       break;
+
+                               default:      // The operator is listed in c_DefaultOprt, but not here. This is a bad thing...
+                                       Error(ecINTERNAL_ERROR);
+                               } // switch operator id
+
+                               m_iPos += (int)len;
+                               a_Tok.Set((ECmdCode)i, pOprtDef[i]);
+                               return true;
+                       } // if operator string found
+               } // end of for all operator strings
+
+               return false;
+       }
+
+
+       bool ParserTokenReader::IsArgSep(token_type& a_Tok)
+       {
+               const char_type* szFormula = m_strFormula.c_str();
+
+               if (szFormula[m_iPos] == m_cArgSep)
+               {
+                       // copy the separator into null terminated string
+                       char_type szSep[2];
+                       szSep[0] = m_cArgSep;
+                       szSep[1] = 0;
+
+                       if (m_iSynFlags & noARG_SEP)
+                               Error(ecUNEXPECTED_ARG_SEP, m_iPos, szSep);
+
+                       m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN;
+                       m_iPos++;
+                       a_Tok.Set(cmARG_SEP, szSep);
+                       return true;
+               }
+
+               return false;
+       }
+
+
+       /** \brief Check for End of Formula.
+
+               \return true if an end of formula is found false otherwise.
+               \param a_Tok [out] If an eof is found the corresponding token will be stored there.
+               \throw nothrow
+               \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok
+       */
+       bool ParserTokenReader::IsEOF(token_type& a_Tok)
+       {
+               const char_type* szFormula = m_strFormula.c_str();
+
+               // check for EOF
+               if (!szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/)
+               {
+                       if (m_iSynFlags & noEND)
+                               Error(ecUNEXPECTED_EOF, m_iPos);
+
+                       if (!m_bracketStack.empty())
+                               Error(ecMISSING_PARENS, m_iPos, _T(")"));
+
+                       m_iSynFlags = 0;
+                       a_Tok.Set(cmEND);
+                       return true;
+               }
+
+               return false;
+       }
+
+
+       /** \brief Check if a string position contains a unary infix operator.
+               \return true if a function token has been found false otherwise.
+       */
+       bool ParserTokenReader::IsInfixOpTok(token_type& a_Tok)
+       {
+               string_type sTok;
+               auto iEnd = ExtractToken(m_pParser->ValidInfixOprtChars(), sTok, (std::size_t)m_iPos);
+               if (iEnd == m_iPos)
+                       return false;
+
+               // iterate over all postfix operator strings
+               funmap_type::const_reverse_iterator it = m_pInfixOprtDef->rbegin();
+               for (; it != m_pInfixOprtDef->rend(); ++it)
+               {
+                       if (sTok.find(it->first) != 0)
+                               continue;
+
+                       a_Tok.Set(it->second, it->first);
+                       m_iPos += (int)it->first.length();
+
+                       if (m_iSynFlags & noINFIXOP)
+                               Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
+
+                       m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN | noARG_SEP;
+                       return true;
+               }
+
+               return false;
+
+               /*
+                       a_Tok.Set(item->second, sTok);
+                       m_iPos = (int)iEnd;
+
+                       if (m_iSynFlags & noINFIXOP)
+                         Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
+
+                       m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN;
+                       return true;
+               */
+       }
+
+
+       /** \brief Check whether the token at a given position is a function token.
+               \param a_Tok [out] If a value token is found it will be placed here.
+               \throw ParserException if Syntaxflags do not allow a function at a_iPos
+               \return true if a function token has been found false otherwise.
+               \pre [assert] m_pParser!=0
+       */
+       bool ParserTokenReader::IsFunTok(token_type& a_Tok)
+       {
+               string_type strTok;
+               auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos);
+               if (iEnd == m_iPos)
+                       return false;
+
+               funmap_type::const_iterator item = m_pFunDef->find(strTok);
+               if (item == m_pFunDef->end())
+                       return false;
+
+               // Check if the next sign is an opening bracket
+               const char_type* szFormula = m_strFormula.c_str();
+               if (szFormula[iEnd] != '(')
+                       return false;
+
+               a_Tok.Set(item->second, strTok);
+
+               m_iPos = (int)iEnd;
+               if (m_iSynFlags & noFUN)
+                       Error(ecUNEXPECTED_FUN, m_iPos - (int)a_Tok.GetAsString().length(), a_Tok.GetAsString());
+
+               m_iSynFlags = noANY ^ noBO;
+               return true;
+       }
+
+
+       /** \brief Check if a string position contains a binary operator.
+               \param a_Tok  [out] Operator token if one is found. This can either be a binary operator or an infix operator token.
+               \return true if an operator token has been found.
+       */
+       bool ParserTokenReader::IsOprt(token_type& a_Tok)
+       {
+               const char_type* const szExpr = m_strFormula.c_str();
+               string_type strTok;
+
+               auto iEnd = ExtractOperatorToken(strTok, (std::size_t)m_iPos);
+               if (iEnd == m_iPos)
+                       return false;
+
+               // Check if the operator is a built in operator, if so ignore it here
+               const char_type** const pOprtDef = m_pParser->GetOprtDef();
+               for (int i = 0; m_pParser->HasBuiltInOprt() && pOprtDef[i]; ++i)
+               {
+                       if (string_type(pOprtDef[i]) == strTok)
+                               return false;
+               }
+
+               // Note:
+               // All tokens in oprt_bin_maptype are have been sorted by their length
+               // Long operators must come first! Otherwise short names (like: "add") that
+               // are part of long token names (like: "add123") will be found instead 
+               // of the long ones.
+               // Length sorting is done with ascending length so we use a reverse iterator here.
+               funmap_type::const_reverse_iterator it = m_pOprtDef->rbegin();
+               for (; it != m_pOprtDef->rend(); ++it)
+               {
+                       const string_type& sID = it->first;
+                       if (sID == string_type(szExpr + m_iPos, szExpr + m_iPos + sID.length()))
+                       {
+                               a_Tok.Set(it->second, strTok);
+
+                               // operator was found
+                               if (m_iSynFlags & noOPT)
+                               {
+                                       // An operator was found but is not expected to occur at
+                                       // this position of the formula, maybe it is an infix 
+                                       // operator, not a binary operator. Both operator types
+                                       // can share characters in their identifiers.
+                                       if (IsInfixOpTok(a_Tok))
+                                               return true;
+                                       else
+                                       {
+                                               // nope, no infix operator
+                                               return false;
+                                               //Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); 
+                                       }
+
+                               }
+
+                               m_iPos += (int)sID.length();
+                               m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noEND | noASSIGN;
+                               return true;
+                       }
+               }
+
+               return false;
+       }
+
+
+       /** \brief Check if a string position contains a unary post value operator. */
+       bool ParserTokenReader::IsPostOpTok(token_type& a_Tok)
+       {
+               // <ibg 20110629> Do not check for postfix operators if they are not allowed at
+               //                the current expression index.
+               //
+               //  This will fix the bug reported here:  
+               //
+               //  http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979
+               //
+               if (m_iSynFlags & noPOSTOP)
+                       return false;
+               // </ibg>
+
+               // Tricky problem with equations like "3m+5":
+               //     m is a postfix operator, + is a valid sign for postfix operators and 
+               //     for binary operators parser detects "m+" as operator string and 
+               //     finds no matching postfix operator.
+               // 
+               // This is a special case so this routine slightly differs from the other
+               // token readers.
+
+               // Test if there could be a postfix operator
+               string_type sTok;
+               auto iEnd = ExtractToken(m_pParser->ValidOprtChars(), sTok, (std::size_t)m_iPos);
+               if (iEnd == m_iPos)
+                       return false;
+
+               // iterate over all postfix operator strings
+               funmap_type::const_reverse_iterator it = m_pPostOprtDef->rbegin();
+               for (; it != m_pPostOprtDef->rend(); ++it)
+               {
+                       if (sTok.find(it->first) != 0)
+                               continue;
+
+                       a_Tok.Set(it->second, sTok);
+                       m_iPos += (int)it->first.length();
+
+                       m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noSTR | noASSIGN;
+                       return true;
+               }
+
+               return false;
+       }
+
+
+       /** \brief Check whether the token at a given position is a value token.
+
+               Value tokens are either values or constants.
+
+               \param a_Tok [out] If a value token is found it will be placed here.
+               \return true if a value token has been found.
+       */
+       bool ParserTokenReader::IsValTok(token_type& a_Tok)
+       {
+               MUP_ASSERT(m_pConstDef != nullptr);
+               MUP_ASSERT(m_pParser != nullptr);
+
+               string_type strTok;
+               value_type fVal(0);
+
+               // 2.) Check for user defined constant
+               // Read everything that could be a constant name
+               auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos);
+               if (iEnd != m_iPos)
+               {
+                       valmap_type::const_iterator item = m_pConstDef->find(strTok);
+                       if (item != m_pConstDef->end())
+                       {
+                               m_iPos = iEnd;
+                               a_Tok.SetVal(item->second, strTok);
+
+                               if (m_iSynFlags & noVAL)
+                                       Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);
+
+                               m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;
+                               return true;
+                       }
+               }
+
+               // 3.call the value recognition functions provided by the user
+               // Call user defined value recognition functions
+               std::list<identfun_type>::const_iterator item = m_vIdentFun.begin();
+               for (item = m_vIdentFun.begin(); item != m_vIdentFun.end(); ++item)
+               {
+                       int iStart = m_iPos;
+                       if ((*item)(m_strFormula.c_str() + m_iPos, &m_iPos, &fVal) == 1)
+                       {
+                               // 2013-11-27 Issue 2:  https://code.google.com/p/muparser/issues/detail?id=2
+                               strTok.assign(m_strFormula.c_str(), iStart, (std::size_t)m_iPos - iStart);
+
+                               if (m_iSynFlags & noVAL)
+                                       Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);
+
+                               a_Tok.SetVal(fVal, strTok);
+                               m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;
+                               return true;
+                       }
+               }
+
+               return false;
+       }
+
+
+       /** \brief Check wheter a token at a given position is a variable token.
+               \param a_Tok [out] If a variable token has been found it will be placed here.
+                 \return true if a variable token has been found.
+       */
+       bool ParserTokenReader::IsVarTok(token_type& a_Tok)
+       {
+               if (m_pVarDef->empty())
+                       return false;
+
+               string_type strTok;
+               auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos);
+               if (iEnd == m_iPos)
+                       return false;
+
+               varmap_type::const_iterator item = m_pVarDef->find(strTok);
+               if (item == m_pVarDef->end())
+                       return false;
+
+               if (m_iSynFlags & noVAR)
+                       Error(ecUNEXPECTED_VAR, m_iPos, strTok);
+
+               m_pParser->OnDetectVar(&m_strFormula, m_iPos, iEnd);
+
+               m_iPos = iEnd;
+               a_Tok.SetVar(item->second, strTok);
+               m_UsedVar[item->first] = item->second;  // Add variable to used-var-list
+
+               m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR;
+
+               //  Zur Info hier die SynFlags von IsVal():
+               //    m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; 
+               return true;
+       }
+
+
+       bool ParserTokenReader::IsStrVarTok(token_type& a_Tok)
+       {
+               if (!m_pStrVarDef || m_pStrVarDef->empty())
+                       return false;
+
+               string_type strTok;
+               auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos);
+               if (iEnd == m_iPos)
+                       return false;
+
+               strmap_type::const_iterator item = m_pStrVarDef->find(strTok);
+               if (item == m_pStrVarDef->end())
+                       return false;
+
+               if (m_iSynFlags & noSTR)
+                       Error(ecUNEXPECTED_VAR, m_iPos, strTok);
+
+               m_iPos = iEnd;
+               if (!m_pParser->m_vStringVarBuf.size())
+                       Error(ecINTERNAL_ERROR);
+
+               a_Tok.SetString(m_pParser->m_vStringVarBuf[item->second], m_pParser->m_vStringVarBuf.size());
+
+               m_iSynFlags = noANY ^ (noBC | noOPT | noEND | noARG_SEP);
+               return true;
+       }
+
+
+
+       /** \brief Check wheter a token at a given position is an undefined variable.
+
+               \param a_Tok [out] If a variable tom_pParser->m_vStringBufken has been found it will be placed here.
+                 \return true if a variable token has been found.
+               \throw nothrow
+       */
+       bool ParserTokenReader::IsUndefVarTok(token_type& a_Tok)
+       {
+               string_type strTok;
+               auto iEnd(ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos));
+               if (iEnd == m_iPos)
+                       return false;
+
+               if (m_iSynFlags & noVAR)
+               {
+                       // <ibg/> 20061021 added token string strTok instead of a_Tok.GetAsString() as the 
+                       //                 token identifier. 
+                       // related bug report:
+                       // http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979
+                       Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok);
+               }
+
+               // If a factory is available implicitely create new variables
+               if (m_pFactory)
+               {
+                       value_type* fVar = m_pFactory(strTok.c_str(), m_pFactoryData);
+                       a_Tok.SetVar(fVar, strTok);
+
+                       // Do not use m_pParser->DefineVar( strTok, fVar );
+                       // in order to define the new variable, it will clear the
+                       // m_UsedVar array which will kill previously defined variables
+                       // from the list
+                       // This is safe because the new variable can never override an existing one
+                       // because they are checked first!
+                       (*m_pVarDef)[strTok] = fVar;
+                       m_UsedVar[strTok] = fVar;  // Add variable to used-var-list
+               }
+               else
+               {
+                       a_Tok.SetVar((value_type*)&m_fZero, strTok);
+                       m_UsedVar[strTok] = 0;  // Add variable to used-var-list
+               }
+
+               m_iPos = iEnd;
+
+               // Call the variable factory in order to let it define a new parser variable
+               m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noINFIXOP | noSTR;
+               return true;
+       }
+
+
+
+       /** \brief Check wheter a token at a given position is a string.
+               \param a_Tok [out] If a variable token has been found it will be placed here.
+               \return true if a string token has been found.
+               \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok
+               \throw nothrow
+       */
+       bool ParserTokenReader::IsString(token_type& a_Tok)
+       {
+               if (m_strFormula[m_iPos] != '"')
+                       return false;
+
+               string_type strBuf(&m_strFormula[(std::size_t)m_iPos + 1]);
+               std::size_t iEnd(0), iSkip(0);
+
+               // parser over escaped '\"' end replace them with '"'
+               for (iEnd = (int)strBuf.find(_T('\"')); iEnd != 0 && iEnd != string_type::npos; iEnd = (int)strBuf.find(_T('\"'), iEnd))
+               {
+                       if (strBuf[iEnd - 1] != '\\') break;
+                       strBuf.replace(iEnd - 1, 2, _T("\""));
+                       iSkip++;
+               }
+
+               if (iEnd == string_type::npos)
+                       Error(ecUNTERMINATED_STRING, m_iPos, _T("\""));
+
+               string_type strTok(strBuf.begin(), strBuf.begin() + iEnd);
+
+               if (m_iSynFlags & noSTR)
+                       Error(ecUNEXPECTED_STR, m_iPos, strTok);
+
+               m_pParser->m_vStringBuf.push_back(strTok); // Store string in internal buffer
+               a_Tok.SetString(strTok, m_pParser->m_vStringBuf.size());
+
+               m_iPos += (int)strTok.length() + 2 + (int)iSkip;  // +2 for quotes; +iSkip for escape characters 
+               m_iSynFlags = noANY ^ (noARG_SEP | noBC | noOPT | noEND);
+
+               return true;
+       }
+
+
+       /** \brief Create an error containing the parse error position.
+
+         This function will create an Parser Exception object containing the error text and its position.
+
+         \param a_iErrc [in] The error code of type #EErrorCodes.
+         \param a_iPos [in] The position where the error was detected.
+         \param a_strTok [in] The token string representation associated with the error.
+         \throw ParserException always throws thats the only purpose of this function.
+       */
+       void  ParserTokenReader::Error(EErrorCodes a_iErrc,     int a_iPos,     const string_type& a_sTok) const
+       {
+               m_pParser->Error(a_iErrc, a_iPos, a_sTok);
+       }
+
+
+       void ParserTokenReader::SetArgSep(char_type cArgSep)
+       {
+               m_cArgSep = cArgSep;
+       }
+
+
+       char_type ParserTokenReader::GetArgSep() const
+       {
+               return m_cArgSep;
+       }
+} // namespace mu
+
+#if defined(_MSC_VER)
+       #pragma warning(pop)
+#endif
diff --git a/src/external/muparser/muParserTokenReader.h b/src/external/muparser/muParserTokenReader.h
new file mode 100644 (file)
index 0000000..caeb920
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_TOKEN_READER_H
+#define MU_PARSER_TOKEN_READER_H
+
+#include <cstdio>
+#include <cstring>
+#include <list>
+#include <map>
+#include <memory>
+#include <stack>
+#include <string>
+
+#include "muParserDef.h"
+#include "muParserToken.h"
+
+/** \file
+       \brief This file contains the parser token reader definition.
+*/
+
+
+namespace mu
+{
+       // Forward declaration
+       class ParserBase;
+
+       /** \brief Token reader for the ParserBase class. */
+       class ParserTokenReader final
+       {
+       private:
+
+               typedef ParserToken<value_type, string_type> token_type;
+
+       public:
+
+               ParserTokenReader(ParserBase* a_pParent);
+               ParserTokenReader* Clone(ParserBase* a_pParent) const;
+
+               void AddValIdent(identfun_type a_pCallback);
+               void SetVarCreator(facfun_type a_pFactory, void* pUserData);
+               void SetFormula(const string_type& a_strFormula);
+               void SetArgSep(char_type cArgSep);
+
+               int GetPos() const;
+               const string_type& GetExpr() const;
+               varmap_type& GetUsedVar();
+               char_type GetArgSep() const;
+
+               void IgnoreUndefVar(bool bIgnore);
+               void ReInit();
+               token_type ReadNextToken();
+
+       private:
+
+               /** \brief Syntax codes.
+
+                       The syntax codes control the syntax check done during the first time parsing of
+                       the expression string. They are flags that indicate which tokens are allowed next
+                       if certain tokens are identified.
+               */
+               enum ESynCodes
+               {
+                       noBO = 1 << 0,                  ///< to avoid i.e. "cos(7)(" 
+                       noBC = 1 << 1,                  ///< to avoid i.e. "sin)" or "()"
+                       noVAL = 1 << 2,                 ///< to avoid i.e. "tan 2" or "sin(8)3.14"
+                       noVAR = 1 << 3,                 ///< to avoid i.e. "sin a" or "sin(8)a"
+                       noARG_SEP = 1 << 4,             ///< to avoid i.e. ",," or "+," ...
+                       noFUN = 1 << 5,                 ///< to avoid i.e. "sqrt cos" or "(1)sin"       
+                       noOPT = 1 << 6,                 ///< to avoid i.e. "(+)"
+                       noPOSTOP = 1 << 7,              ///< to avoid i.e. "(5!!)" "sin!"
+                       noINFIXOP = 1 << 8,             ///< to avoid i.e. "++4" "!!4"
+                       noEND = 1 << 9,                 ///< to avoid unexpected end of formula
+                       noSTR = 1 << 10,                ///< to block numeric arguments on string functions
+                       noASSIGN = 1 << 11,             ///< to block assignment to constant i.e. "4=7"
+                       noIF = 1 << 12,
+                       noELSE = 1 << 13,
+                       sfSTART_OF_LINE = noOPT | noBC | noPOSTOP | noASSIGN | noIF | noELSE | noARG_SEP,
+                       noANY = ~0                              ///< All of he above flags set
+               };
+
+               ParserTokenReader(const ParserTokenReader& a_Reader);
+               ParserTokenReader& operator=(const ParserTokenReader& a_Reader);
+               void Assign(const ParserTokenReader& a_Reader);
+
+               void SetParent(ParserBase* a_pParent);
+               int ExtractToken(const char_type* a_szCharSet, string_type& a_strTok, std::size_t a_iPos) const;
+               int ExtractOperatorToken(string_type& a_sTok, std::size_t a_iPos) const;
+
+               bool IsBuiltIn(token_type& a_Tok);
+               bool IsArgSep(token_type& a_Tok);
+               bool IsEOF(token_type& a_Tok);
+               bool IsInfixOpTok(token_type& a_Tok);
+               bool IsFunTok(token_type& a_Tok);
+               bool IsPostOpTok(token_type& a_Tok);
+               bool IsOprt(token_type& a_Tok);
+               bool IsValTok(token_type& a_Tok);
+               bool IsVarTok(token_type& a_Tok);
+               bool IsStrVarTok(token_type& a_Tok);
+               bool IsUndefVarTok(token_type& a_Tok);
+               bool IsString(token_type& a_Tok);
+               void Error(EErrorCodes a_iErrc, int a_iPos = -1, const string_type& a_sTok = string_type()) const;
+
+               token_type& SaveBeforeReturn(const token_type& tok);
+
+               ParserBase* m_pParser;
+               string_type m_strFormula;
+               int  m_iPos;
+               int  m_iSynFlags;
+               bool m_bIgnoreUndefVar;
+
+               const funmap_type* m_pFunDef;
+               const funmap_type* m_pPostOprtDef;
+               const funmap_type* m_pInfixOprtDef;
+               const funmap_type* m_pOprtDef;
+               const valmap_type* m_pConstDef;
+               const strmap_type* m_pStrVarDef;
+
+               varmap_type* m_pVarDef;  ///< The only non const pointer to parser internals
+               facfun_type m_pFactory;
+               void* m_pFactoryData;
+               std::list<identfun_type> m_vIdentFun; ///< Value token identification function
+               varmap_type m_UsedVar;
+               value_type m_fZero;      ///< Dummy value of zero, referenced by undefined variables
+               
+               std::stack<int> m_bracketStack;
+
+               token_type m_lastTok;
+               char_type m_cArgSep;     ///< The character used for separating function arguments
+       };
+} // namespace mu
+
+#endif
+
+
diff --git a/src/external/nonstd/README b/src/external/nonstd/README
deleted file mode 100644 (file)
index cbd2133..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-The header file here provides a C++14-compatible implementation of
-std::optional as nonstd::optional.
-
-This implementation is nearly identical to the reference
-implementation at commit a08fda6c674ae4c9f of
-https://github.com/martinmoene/optional-lite.git.
-
-There is no Doxygen for this code, but it is intended to conform to
-that of std::optional, so look in the usual C++17 documentation for
-std::optional for that.
-
-This will be removed when GROMACS requires C++17, which has a
-standardized version of std::optional.
-
-Some minor fixes were required for portability, which are described in
-comments containing "GMX".
diff --git a/src/external/nonstd/optional.hpp b/src/external/nonstd/optional.hpp
deleted file mode 100644 (file)
index 4a5945c..0000000
+++ /dev/null
@@ -1,1700 +0,0 @@
-//
-// Copyright (c) 2014-2018 Martin Moene
-//
-// https://github.com/martinmoene/optional-lite
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-// GMX see README for modification notes
-
-#pragma once
-
-#ifndef NONSTD_OPTIONAL_LITE_HPP
-#define NONSTD_OPTIONAL_LITE_HPP
-
-#define optional_lite_MAJOR  3
-#define optional_lite_MINOR  1
-#define optional_lite_PATCH  1
-
-#define optional_lite_VERSION  optional_STRINGIFY(optional_lite_MAJOR) "." optional_STRINGIFY(optional_lite_MINOR) "." optional_STRINGIFY(optional_lite_PATCH)
-
-#define optional_STRINGIFY(  x )  optional_STRINGIFY_( x )
-#define optional_STRINGIFY_( x )  #x
-
-// optional-lite configuration:
-
-#define optional_OPTIONAL_DEFAULT  0
-#define optional_OPTIONAL_NONSTD   1
-#define optional_OPTIONAL_STD      2
-
-#if !defined( optional_CONFIG_SELECT_OPTIONAL )
-# define optional_CONFIG_SELECT_OPTIONAL  ( optional_HAVE_STD_OPTIONAL ? optional_OPTIONAL_STD : optional_OPTIONAL_NONSTD )
-#endif
-
-// Control presence of exception handling (try and auto discover):
-
-#ifndef optional_CONFIG_NO_EXCEPTIONS
-# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
-#  define optional_CONFIG_NO_EXCEPTIONS  0
-# else
-#  define optional_CONFIG_NO_EXCEPTIONS  1
-# endif
-#endif
-
-// C++ language version detection (C++20 is speculative):
-// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
-
-#ifndef   optional_CPLUSPLUS
-# if defined(_MSVC_LANG ) && !defined(__clang__)
-#  define optional_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
-# else
-#  define optional_CPLUSPLUS  __cplusplus
-# endif
-#endif
-
-#define optional_CPP98_OR_GREATER  ( optional_CPLUSPLUS >= 199711L )
-#define optional_CPP11_OR_GREATER  ( optional_CPLUSPLUS >= 201103L )
-#define optional_CPP11_OR_GREATER_ ( optional_CPLUSPLUS >= 201103L )
-#define optional_CPP14_OR_GREATER  ( optional_CPLUSPLUS >= 201402L )
-#define optional_CPP17_OR_GREATER  ( optional_CPLUSPLUS >= 201703L )
-#define optional_CPP20_OR_GREATER  ( optional_CPLUSPLUS >= 202000L )
-
-// C++ language version (represent 98 as 3):
-
-#define optional_CPLUSPLUS_V  ( optional_CPLUSPLUS / 100 - (optional_CPLUSPLUS > 200000 ? 2000 : 1994) )
-
-// Use C++17 std::optional if available and requested:
-
-#if optional_CPP17_OR_GREATER && defined(__has_include )
-# if __has_include( <optional> )
-#  define optional_HAVE_STD_OPTIONAL  1
-# else
-#  define optional_HAVE_STD_OPTIONAL  0
-# endif
-#else
-# define  optional_HAVE_STD_OPTIONAL  0
-#endif
-
-#define optional_USES_STD_OPTIONAL  ( (optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_STD) || ((optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_DEFAULT) && optional_HAVE_STD_OPTIONAL) )
-
-//
-// in_place: code duplicated in any-lite, expected-lite, optional-lite, value-ptr-lite, variant-lite:
-//
-
-#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES
-#define nonstd_lite_HAVE_IN_PLACE_TYPES  1
-
-// C++17 std::in_place in <utility>:
-
-#if optional_CPP17_OR_GREATER
-
-#include <utility>
-
-namespace nonstd {
-
-using std::in_place;
-using std::in_place_type;
-using std::in_place_index;
-using std::in_place_t;
-using std::in_place_type_t;
-using std::in_place_index_t;
-
-#define nonstd_lite_in_place_t(      T)  std::in_place_t
-#define nonstd_lite_in_place_type_t( T)  std::in_place_type_t<T>
-#define nonstd_lite_in_place_index_t(K)  std::in_place_index_t<K>
-
-#define nonstd_lite_in_place(      T)    std::in_place_t{}
-#define nonstd_lite_in_place_type( T)    std::in_place_type_t<T>{}
-#define nonstd_lite_in_place_index(K)    std::in_place_index_t<K>{}
-
-} // namespace nonstd
-
-#else // optional_CPP17_OR_GREATER
-
-#include <cstddef>
-
-namespace nonstd {
-namespace detail {
-
-template< class T >
-struct in_place_type_tag {};
-
-template< std::size_t K >
-struct in_place_index_tag {};
-
-} // namespace detail
-
-struct in_place_t {};
-
-template< class T >
-inline in_place_t in_place( detail::in_place_type_tag<T> /*unused*/ = detail::in_place_type_tag<T>() )
-{
-    return in_place_t();
-}
-
-template< std::size_t K >
-inline in_place_t in_place( detail::in_place_index_tag<K> /*unused*/ = detail::in_place_index_tag<K>() )
-{
-    return in_place_t();
-}
-
-template< class T >
-inline in_place_t in_place_type( detail::in_place_type_tag<T> /*unused*/ = detail::in_place_type_tag<T>() )
-{
-    return in_place_t();
-}
-
-template< std::size_t K >
-inline in_place_t in_place_index( detail::in_place_index_tag<K> /*unused*/ = detail::in_place_index_tag<K>() )
-{
-    return in_place_t();
-}
-
-// mimic templated typedef:
-
-#define nonstd_lite_in_place_t(      T)  nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T>  )
-#define nonstd_lite_in_place_type_t( T)  nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T>  )
-#define nonstd_lite_in_place_index_t(K)  nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag<K> )
-
-#define nonstd_lite_in_place(      T)    nonstd::in_place_type<T>
-#define nonstd_lite_in_place_type( T)    nonstd::in_place_type<T>
-#define nonstd_lite_in_place_index(K)    nonstd::in_place_index<K>
-
-} // namespace nonstd
-
-#endif // optional_CPP17_OR_GREATER
-#endif // nonstd_lite_HAVE_IN_PLACE_TYPES
-
-//
-// Using std::optional:
-//
-
-#if optional_USES_STD_OPTIONAL
-
-#include <optional>
-
-namespace nonstd {
-
-    using std::optional;
-    using std::bad_optional_access;
-    using std::hash;
-
-    using std::nullopt;
-    using std::nullopt_t;
-
-    using std::operator==;
-    using std::operator!=;
-    using std::operator<;
-    using std::operator<=;
-    using std::operator>;
-    using std::operator>=;
-    using std::make_optional;
-    using std::swap;
-}
-
-#else // optional_USES_STD_OPTIONAL
-
-#include <cassert>
-#include <utility>
-
-// optional-lite alignment configuration:
-
-#ifndef  optional_CONFIG_MAX_ALIGN_HACK
-# define optional_CONFIG_MAX_ALIGN_HACK  0
-#endif
-
-#ifndef  optional_CONFIG_ALIGN_AS
-// no default, used in #if defined()
-#endif
-
-#ifndef  optional_CONFIG_ALIGN_AS_FALLBACK
-# define optional_CONFIG_ALIGN_AS_FALLBACK  double
-#endif
-
-// Compiler warning suppression:
-
-#if defined(__clang__)
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wundef"
-#elif defined(__GNUC__)
-# pragma GCC   diagnostic push
-# pragma GCC   diagnostic ignored "-Wundef"
-#elif defined(_MSC_VER )
-# pragma warning( push )
-#endif
-
-// half-open range [lo..hi):
-#define optional_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
-
-// Compiler versions:
-//
-// MSVC++ 6.0  _MSC_VER == 1200 (Visual Studio 6.0)
-// MSVC++ 7.0  _MSC_VER == 1300 (Visual Studio .NET 2002)
-// MSVC++ 7.1  _MSC_VER == 1310 (Visual Studio .NET 2003)
-// MSVC++ 8.0  _MSC_VER == 1400 (Visual Studio 2005)
-// MSVC++ 9.0  _MSC_VER == 1500 (Visual Studio 2008)
-// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
-// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
-// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
-// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
-// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
-
-#if defined(_MSC_VER ) && !defined(__clang__)
-# define optional_COMPILER_MSVC_VER      (_MSC_VER )
-# define optional_COMPILER_MSVC_VERSION  (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
-#else
-# define optional_COMPILER_MSVC_VER      0
-# define optional_COMPILER_MSVC_VERSION  0
-#endif
-
-#define optional_COMPILER_VERSION( major, minor, patch )  ( 10 * (10 * (major) + (minor) ) + (patch) )
-
-#if defined(__GNUC__) && !defined(__clang__)
-# define optional_COMPILER_GNUC_VERSION   optional_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
-#else
-# define optional_COMPILER_GNUC_VERSION   0
-#endif
-
-#if defined(__clang__)
-# define optional_COMPILER_CLANG_VERSION  optional_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
-#else
-# define optional_COMPILER_CLANG_VERSION  0
-#endif
-
-#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 140 )
-# pragma warning( disable: 4345 )   // initialization behavior changed
-#endif
-
-#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 150 )
-# pragma warning( disable: 4814 )   // in C++14 'constexpr' will not imply 'const'
-#endif
-
-// Presence of language and library features:
-
-#define optional_HAVE(FEATURE) ( optional_HAVE_##FEATURE )
-
-#ifdef _HAS_CPP0X
-# define optional_HAS_CPP0X  _HAS_CPP0X
-#else
-# define optional_HAS_CPP0X  0
-#endif
-
-// Unless defined otherwise below, consider VC14 as C++11 for optional-lite:
-
-#if optional_COMPILER_MSVC_VER >= 1900
-# undef  optional_CPP11_OR_GREATER
-# define optional_CPP11_OR_GREATER  1
-#endif
-
-#define optional_CPP11_90   (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1500)
-#define optional_CPP11_100  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1600)
-#define optional_CPP11_110  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1700)
-#define optional_CPP11_120  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1800)
-#define optional_CPP11_140  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1900)
-#define optional_CPP11_141  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1910)
-
-#define optional_CPP14_000  (optional_CPP14_OR_GREATER)
-#define optional_CPP17_000  (optional_CPP17_OR_GREATER)
-
-// Presence of C++11 language features:
-
-#define optional_HAVE_CONSTEXPR_11      optional_CPP11_140
-#define optional_HAVE_IS_DEFAULT        optional_CPP11_140
-#define optional_HAVE_NOEXCEPT          optional_CPP11_140
-#define optional_HAVE_NULLPTR           optional_CPP11_100
-#define optional_HAVE_REF_QUALIFIER     optional_CPP11_140
-
-// Presence of C++14 language features:
-
-#define optional_HAVE_CONSTEXPR_14      optional_CPP14_000
-
-// Presence of C++17 language features:
-
-#define optional_HAVE_NODISCARD         optional_CPP17_000
-
-// Presence of C++ library features:
-
-#define optional_HAVE_CONDITIONAL       optional_CPP11_120
-#define optional_HAVE_REMOVE_CV         optional_CPP11_120
-#define optional_HAVE_TYPE_TRAITS       optional_CPP11_90
-
-#define optional_HAVE_TR1_TYPE_TRAITS   (!! optional_COMPILER_GNUC_VERSION )
-#define optional_HAVE_TR1_ADD_POINTER   (!! optional_COMPILER_GNUC_VERSION )
-
-// C++ feature usage:
-
-#if optional_HAVE( CONSTEXPR_11 )
-# define optional_constexpr  constexpr
-#else
-# define optional_constexpr  /*constexpr*/
-#endif
-
-#if optional_HAVE( IS_DEFAULT )
-# define optional_is_default  = default;
-#else
-# define optional_is_default  {}
-#endif
-
-#if optional_HAVE( CONSTEXPR_14 )
-# define optional_constexpr14  constexpr
-#else
-# define optional_constexpr14  /*constexpr*/
-#endif
-
-#if optional_HAVE( NODISCARD )
-# define optional_nodiscard  [[nodiscard]]
-#else
-# define optional_nodiscard  /*[[nodiscard]]*/
-#endif
-
-#if optional_HAVE( NOEXCEPT )
-# define optional_noexcept  noexcept
-#else
-# define optional_noexcept  /*noexcept*/
-#endif
-
-#if optional_HAVE( NULLPTR )
-# define optional_nullptr  nullptr
-#else
-# define optional_nullptr  NULL
-#endif
-
-#if optional_HAVE( REF_QUALIFIER )
-// NOLINTNEXTLINE( bugprone-macro-parentheses )
-# define optional_ref_qual  &
-# define optional_refref_qual  &&
-#else
-# define optional_ref_qual  /*&*/
-# define optional_refref_qual  /*&&*/
-#endif
-
-// additional includes:
-
-#if optional_CONFIG_NO_EXCEPTIONS
-// already included: <cassert>
-#else
-# include <stdexcept>
-#endif
-
-#if optional_CPP11_OR_GREATER
-# include <functional>
-#endif
-
-#if optional_HAVE( INITIALIZER_LIST )
-# include <initializer_list>
-#endif
-
-#if optional_HAVE( TYPE_TRAITS )
-# include <type_traits>
-#elif optional_HAVE( TR1_TYPE_TRAITS )
-# include <tr1/type_traits>
-#endif
-
-// Method enabling
-
-#if optional_CPP11_OR_GREATER
-
-#define optional_REQUIRES_0(...) \
-    template< bool B = (__VA_ARGS__), typename std::enable_if<B, int>::type = 0 >
-
-#define optional_REQUIRES_T(...) \
-    , typename = typename std::enable_if< (__VA_ARGS__), nonstd::optional_lite::detail::enabler >::type
-
-#define optional_REQUIRES_R(R, ...) \
-    typename std::enable_if< (__VA_ARGS__), R>::type
-
-#define optional_REQUIRES_A(...) \
-    , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr
-
-#endif
-
-//
-// optional:
-//
-
-namespace nonstd { namespace optional_lite {
-
-namespace std11 {
-
-#if optional_CPP11_OR_GREATER
-    using std::move;
-#else
-    template< typename T > T & move( T & t ) { return t; }
-#endif
-
-#if optional_HAVE( CONDITIONAL )
-    using std::conditional;
-#else
-    template< bool B, typename T, typename F > struct conditional              { typedef T type; };
-    template<         typename T, typename F > struct conditional<false, T, F> { typedef F type; };
-#endif // optional_HAVE_CONDITIONAL
-
-} // namespace std11
-
-#if optional_CPP11_OR_GREATER
-
-/// type traits C++17:
-
-namespace std17 {
-
-#if optional_CPP17_OR_GREATER
-
-using std::is_swappable;
-using std::is_nothrow_swappable;
-
-#elif optional_CPP11_OR_GREATER
-
-namespace detail {
-
-using std::swap;
-
-struct is_swappable
-{
-    template< typename T, typename = decltype( swap( std::declval<T&>(), std::declval<T&>() ) ) >
-    static std::true_type test( int /*unused*/ );
-
-    template< typename >
-    static std::false_type test(...);
-};
-
-struct is_nothrow_swappable
-{
-    // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015):
-
-    template< typename T >
-    static constexpr bool satisfies()
-    {
-        return noexcept( swap( std::declval<T&>(), std::declval<T&>() ) );
-    }
-
-    template< typename T >
-    static auto test( int /*unused*/ ) -> std::integral_constant<bool, satisfies<T>()>{}
-
-    template< typename >
-    static auto test(...) -> std::false_type;
-};
-
-} // namespace detail
-
-// is [nothow] swappable:
-
-template< typename T >
-struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
-
-template< typename T >
-struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test<T>(0) ){};
-
-#endif // optional_CPP17_OR_GREATER
-
-} // namespace std17
-
-/// type traits C++20:
-
-namespace std20 {
-
-template< typename T >
-struct remove_cvref
-{
-    typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type;
-};
-
-} // namespace std20
-
-#endif // optional_CPP11_OR_GREATER
-
-/// class optional
-
-template< typename T >
-class optional;
-
-namespace detail {
-
-// for optional_REQUIRES_T
-
-#if optional_CPP11_OR_GREATER
-enum class enabler{};
-#endif
-
-// C++11 emulation:
-
-struct nulltype{};
-
-template< typename Head, typename Tail >
-struct typelist
-{
-    typedef Head head;
-    typedef Tail tail;
-};
-
-#if optional_CONFIG_MAX_ALIGN_HACK
-
-// Max align, use most restricted type for alignment:
-
-#define optional_UNIQUE(  name )       optional_UNIQUE2( name, __LINE__ )
-#define optional_UNIQUE2( name, line ) optional_UNIQUE3( name, line )
-#define optional_UNIQUE3( name, line ) name ## line
-
-#define optional_ALIGN_TYPE( type ) \
-    type optional_UNIQUE( _t ); struct_t< type > optional_UNIQUE( _st )
-
-template< typename T >
-struct struct_t { T _; };
-
-union max_align_t
-{
-    optional_ALIGN_TYPE( char );
-    optional_ALIGN_TYPE( short int );
-    optional_ALIGN_TYPE( int );
-    optional_ALIGN_TYPE( long int  );
-    optional_ALIGN_TYPE( float  );
-    optional_ALIGN_TYPE( double );
-    optional_ALIGN_TYPE( long double );
-    optional_ALIGN_TYPE( char * );
-    optional_ALIGN_TYPE( short int * );
-    optional_ALIGN_TYPE( int *  );
-    optional_ALIGN_TYPE( long int * );
-    optional_ALIGN_TYPE( float * );
-    optional_ALIGN_TYPE( double * );
-    optional_ALIGN_TYPE( long double * );
-    optional_ALIGN_TYPE( void * );
-
-#ifdef HAVE_LONG_LONG
-    optional_ALIGN_TYPE( long long );
-#endif
-
-    struct Unknown;
-
-    Unknown ( * optional_UNIQUE(_) )( Unknown );
-    Unknown * Unknown::* optional_UNIQUE(_);
-    Unknown ( Unknown::* optional_UNIQUE(_) )( Unknown );
-
-    struct_t< Unknown ( * )( Unknown)         > optional_UNIQUE(_);
-    struct_t< Unknown * Unknown::*            > optional_UNIQUE(_);
-    struct_t< Unknown ( Unknown::* )(Unknown) > optional_UNIQUE(_);
-};
-
-#undef optional_UNIQUE
-#undef optional_UNIQUE2
-#undef optional_UNIQUE3
-
-#undef optional_ALIGN_TYPE
-
-#elif defined( optional_CONFIG_ALIGN_AS ) // optional_CONFIG_MAX_ALIGN_HACK
-
-// Use user-specified type for alignment:
-
-#define optional_ALIGN_AS( unused ) \
-    optional_CONFIG_ALIGN_AS
-
-#else // optional_CONFIG_MAX_ALIGN_HACK
-
-// Determine POD type to use for alignment:
-
-#define optional_ALIGN_AS( to_align ) \
-    typename type_of_size< alignment_types, alignment_of< to_align >::value >::type
-
-template< typename T >
-struct alignment_of;
-
-template< typename T >
-struct alignment_of_hack
-{
-    char c;
-    T t;
-    alignment_of_hack();
-};
-
-template< size_t A, size_t S >
-struct alignment_logic
-{
-    enum { value = A < S ? A : S };
-};
-
-template< typename T >
-struct alignment_of
-{
-    enum { value = alignment_logic<
-        sizeof( alignment_of_hack<T> ) - sizeof(T), sizeof(T) >::value };
-};
-
-template< typename List, size_t N >
-struct type_of_size
-{
-    typedef typename std11::conditional<
-        N == sizeof( typename List::head ),
-            typename List::head,
-            typename type_of_size<typename List::tail, N >::type >::type type;
-};
-
-template< size_t N >
-struct type_of_size< nulltype, N >
-{
-    typedef optional_CONFIG_ALIGN_AS_FALLBACK type;
-};
-
-template< typename T>
-struct struct_t { T _; };
-
-#define optional_ALIGN_TYPE( type ) \
-    typelist< type , typelist< struct_t< type >
-
-struct Unknown;
-
-typedef
-    optional_ALIGN_TYPE( char ),
-    optional_ALIGN_TYPE( short ),
-    optional_ALIGN_TYPE( int ),
-    optional_ALIGN_TYPE( long ),
-    optional_ALIGN_TYPE( float ),
-    optional_ALIGN_TYPE( double ),
-    optional_ALIGN_TYPE( long double ),
-
-    optional_ALIGN_TYPE( char *),
-    optional_ALIGN_TYPE( short * ),
-    optional_ALIGN_TYPE( int * ),
-    optional_ALIGN_TYPE( long * ),
-    optional_ALIGN_TYPE( float * ),
-    optional_ALIGN_TYPE( double * ),
-    optional_ALIGN_TYPE( long double * ),
-
-    optional_ALIGN_TYPE( Unknown ( * )( Unknown ) ),
-    optional_ALIGN_TYPE( Unknown * Unknown::*     ),
-    optional_ALIGN_TYPE( Unknown ( Unknown::* )( Unknown ) ),
-
-    nulltype
-    > > > > > > >    > > > > > > >
-    > > > > > > >    > > > > > > >
-    > > > > > >
-    alignment_types;
-
-#undef optional_ALIGN_TYPE
-
-#endif // optional_CONFIG_MAX_ALIGN_HACK
-
-/// C++03 constructed union to hold value.
-
-template< typename T >
-union storage_t
-{
-//private:
-//    template< typename > friend class optional;
-
-    typedef T value_type;
-
-    storage_t() optional_is_default
-
-    explicit storage_t( value_type const & v )
-    {
-        construct_value( v );
-    }
-
-    void construct_value( value_type const & v )
-    {
-        ::new( value_ptr() ) value_type( v );
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    explicit storage_t( value_type && v )
-    {
-        construct_value( std::move( v ) );
-    }
-
-    void construct_value( value_type && v )
-    {
-        ::new( value_ptr() ) value_type( std::move( v ) );
-    }
-
-    template< class... Args >
-    void emplace( Args&&... args )
-    {
-        ::new( value_ptr() ) value_type( std::forward<Args>(args)... );
-    }
-
-    template< class U, class... Args >
-    void emplace( std::initializer_list<U> il, Args&&... args )
-    {
-        ::new( value_ptr() ) value_type( il, std::forward<Args>(args)... );
-    }
-
-#endif
-
-    void destruct_value()
-    {
-        value_ptr()->~T();
-    }
-
-    optional_nodiscard value_type const * value_ptr() const
-    {
-        return as<value_type>();
-    }
-
-    value_type * value_ptr()
-    {
-        return as<value_type>();
-    }
-
-    optional_nodiscard value_type const & value() const optional_ref_qual
-    {
-        return * value_ptr();
-    }
-
-    value_type & value() optional_ref_qual
-    {
-        return * value_ptr();
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    optional_nodiscard value_type const && value() const optional_refref_qual
-    {
-        return std::move( value() );
-    }
-
-    value_type && value() optional_refref_qual
-    {
-        return std::move( value() );
-    }
-
-#endif
-
-#if optional_CPP11_OR_GREATER
-
-    using aligned_storage_t = typename std::aligned_storage< sizeof(value_type), alignof(value_type) >::type;
-    aligned_storage_t data;
-
-#elif optional_CONFIG_MAX_ALIGN_HACK
-
-    typedef struct { unsigned char data[ sizeof(value_type) ]; } aligned_storage_t;
-
-    max_align_t hack;
-    aligned_storage_t data;
-
-#else
-    typedef optional_ALIGN_AS(value_type) align_as_type;
-
-    typedef struct { align_as_type data[ 1 + ( sizeof(value_type) - 1 ) / sizeof(align_as_type) ]; } aligned_storage_t;
-    aligned_storage_t data;
-
-#   undef optional_ALIGN_AS
-
-#endif // optional_CONFIG_MAX_ALIGN_HACK
-
-    optional_nodiscard void * ptr() optional_noexcept
-    {
-        return &data;
-    }
-
-    optional_nodiscard void const * ptr() const optional_noexcept
-    {
-        return &data;
-    }
-
-    template <typename U>
-    optional_nodiscard U * as()
-    {
-        return reinterpret_cast<U*>( ptr() );
-    }
-
-    template <typename U>
-    optional_nodiscard U const * as() const
-    {
-        return reinterpret_cast<U const *>( ptr() );
-    }
-};
-
-} // namespace detail
-
-/// disengaged state tag
-
-struct nullopt_t
-{
-    struct init{};
-    explicit optional_constexpr nullopt_t( init /*unused*/ ) optional_noexcept {}
-};
-
-#if optional_HAVE( CONSTEXPR_11 )
-constexpr nullopt_t nullopt{ nullopt_t::init{} };
-#else
-// extra parenthesis to prevent the most vexing parse:
-const nullopt_t nullopt(( nullopt_t::init() ));
-#endif
-
-/// optional access error
-
-#if ! optional_CONFIG_NO_EXCEPTIONS
-
-class bad_optional_access : public std::logic_error
-{
-public:
-  explicit bad_optional_access()
-  : logic_error( "bad optional access" ) {}
-};
-
-#endif //optional_CONFIG_NO_EXCEPTIONS
-
-/// optional
-
-template< typename T>
-class optional
-{
-private:
-    template< typename > friend class optional;
-
-    typedef void (optional::*safe_bool)() const;
-
-public:
-    typedef T value_type;
-
-    // x.x.3.1, constructors
-
-    // 1a - default construct
-    optional_constexpr optional() optional_noexcept
-    : has_value_( false )
-    , contained()
-    {}
-
-    // 1b - construct explicitly empty
-    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
-    optional_constexpr optional( nullopt_t /*unused*/ ) optional_noexcept
-    : has_value_( false )
-    , contained()
-    {}
-
-    // 2 - copy-construct
-    optional_constexpr14 optional( optional const & other
-#if optional_CPP11_OR_GREATER
-        optional_REQUIRES_A(
-            true || std::is_copy_constructible<T>::value
-        )
-#endif
-    )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( other.contained.value() );
-        }
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    // 3 (C++11) - move-construct from optional
-    optional_constexpr14 optional( optional && other
-        optional_REQUIRES_A(
-            true || std::is_move_constructible<T>::value
-        )
-        // NOLINTNEXTLINE( performance-noexcept-move-constructor )
-    ) noexcept( std::is_nothrow_move_constructible<T>::value )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( std::move( other.contained.value() ) );
-        }
-    }
-
-    // 4a (C++11) - explicit converting copy-construct from optional
-    template< typename U >
-    explicit optional( optional<U> const & other
-        optional_REQUIRES_A(
-            std::is_constructible<T, U const &>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            && !std::is_convertible<               U const & , T>::value /*=> explicit */
-        )
-    )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( T{ other.contained.value() } );
-        }
-    }
-#endif // optional_CPP11_OR_GREATER
-
-    // 4b (C++98 and later) - non-explicit converting copy-construct from optional
-    template< typename U >
-    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
-    optional( optional<U> const & other
-#if optional_CPP11_OR_GREATER
-        optional_REQUIRES_A(
-            std::is_constructible<T, U const &>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            &&  std::is_convertible<               U const & , T>::value /*=> non-explicit */
-        )
-#endif // optional_CPP11_OR_GREATER
-    )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( other.contained.value() );
-        }
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    // 5a (C++11) - explicit converting move-construct from optional
-    template< typename U >
-    explicit optional( optional<U> && other
-        optional_REQUIRES_A(
-            std::is_constructible<T, U &&>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            && !std::is_convertible<                     U &&, T>::value /*=> explicit */
-        )
-    )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( T{ std::move( other.contained.value() ) } );
-        }
-    }
-
-    // 5a (C++11) - non-explicit converting move-construct from optional
-    template< typename U >
-    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
-    optional( optional<U> && other
-        optional_REQUIRES_A(
-            std::is_constructible<T, U &&>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            &&  std::is_convertible<                     U &&, T>::value /*=> non-explicit */
-        )
-    )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( std::move( other.contained.value() ) );
-        }
-    }
-
-    // 6 (C++11) - in-place construct
-    template< typename... Args
-        optional_REQUIRES_T(
-            std::is_constructible<T, Args&&...>::value
-        )
-    >
-    optional_constexpr explicit optional( nonstd_lite_in_place_t(T), Args&&... args )
-    : has_value_( true )
-    , contained( T( std::forward<Args>(args)...) )
-    {}
-
-    // 7 (C++11) - in-place construct,  initializer-list
-    template< typename U, typename... Args
-        optional_REQUIRES_T(
-            std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value
-        )
-    >
-    optional_constexpr explicit optional( nonstd_lite_in_place_t(T), std::initializer_list<U> il, Args&&... args )
-    : has_value_( true )
-    , contained( T( il, std::forward<Args>(args)...) )
-    {}
-
-    // 8a (C++11) - explicit move construct from value
-    template< typename U = value_type >
-    optional_constexpr explicit optional( U && value
-        optional_REQUIRES_A(
-            std::is_constructible<T, U&&>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
-            && !std::is_convertible<U&&, T>::value /*=> explicit */
-        )
-    )
-    : has_value_( true )
-    , contained( T{ std::forward<U>( value ) } )
-    {}
-
-    // 8b (C++11) - non-explicit move construct from value
-    template< typename U = value_type >
-    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
-    optional_constexpr optional( U && value
-        optional_REQUIRES_A(
-            std::is_constructible<T, U&&>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
-            && std::is_convertible<U&&, T>::value /*=> non-explicit */
-        )
-    )
-    : has_value_( true )
-    , contained( std::forward<U>( value ) )
-    {}
-
-#else // optional_CPP11_OR_GREATER
-
-    // 8 (C++98)
-    optional( value_type const & value )
-    : has_value_( true )
-    , contained( value )
-    {}
-
-#endif // optional_CPP11_OR_GREATER
-
-    // x.x.3.2, destructor
-
-    ~optional()
-    {
-        if ( has_value() )
-        {
-            contained.destruct_value();
-        }
-    }
-
-    // x.x.3.3, assignment
-
-    // 1 (C++98and later) -  assign explicitly empty
-    optional & operator=( nullopt_t /*unused*/) optional_noexcept
-    {
-        reset();
-        return *this;
-    }
-
-    // 2 (C++98and later) - copy-assign from optional
-#if optional_CPP11_OR_GREATER
-    // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
-    optional_REQUIRES_R(
-        optional &,
-        true
-//      std::is_copy_constructible<T>::value
-//      && std::is_copy_assignable<T>::value
-    )
-    operator=( optional const & other )
-        noexcept(
-            std::is_nothrow_move_assignable<T>::value
-            && std::is_nothrow_move_constructible<T>::value
-        )
-#else
-    optional & operator=( optional const & other )
-#endif
-    {
-        if      ( (has_value() == true ) && (other.has_value() == false) ) { reset(); }
-        else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( *other ); }
-        else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = *other; }
-        return *this;
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    // 3 (C++11) - move-assign from optional
-    // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
-    optional_REQUIRES_R(
-        optional &,
-        true
-//      std::is_move_constructible<T>::value
-//      && std::is_move_assignable<T>::value
-    )
-    operator=( optional && other ) noexcept
-    {
-        if      ( (has_value() == true ) && (other.has_value() == false) ) { reset(); }
-        else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std::move( *other ) ); }
-        else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = std::move( *other ); }
-        return *this;
-    }
-
-    // 4 (C++11) - move-assign from value
-    template< typename U = T >
-        // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
-        optional_REQUIRES_R(
-            optional &,
-            std::is_constructible<T , U>::value
-            && std::is_assignable<T&, U>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
-            && !(std::is_scalar<T>::value && std::is_same<T, typename std::decay<U>::type>::value)
-        )
-    operator=( U && value )
-    {
-        if ( has_value() )
-        {
-            contained.value() = std::forward<U>( value );
-        }
-        else
-        {
-            initialize( T( std::forward<U>( value ) ) );
-        }
-        return *this;
-    }
-
-#else // optional_CPP11_OR_GREATER
-
-    // 4 (C++98) - copy-assign from value
-    template< typename U /*= T*/ >
-    optional & operator=( U const & value )
-    {
-        if ( has_value() ) contained.value() = value;
-        else               initialize( T( value ) );
-        return *this;
-    }
-
-#endif // optional_CPP11_OR_GREATER
-
-    // 5 (C++98 and later) - converting copy-assign from optional
-    template< typename U >
-#if optional_CPP11_OR_GREATER
-        // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
-        optional_REQUIRES_R(
-            optional&,
-            std::is_constructible<  T , U const &>::value
-            &&  std::is_assignable< T&, U const &>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            && !std::is_assignable<  T&, optional<U> &          >::value
-            && !std::is_assignable<  T&, optional<U> &&         >::value
-            && !std::is_assignable<  T&, optional<U> const &    >::value
-            && !std::is_assignable<  T&, optional<U> const &&   >::value
-        )
-#else
-    optional&
-#endif // optional_CPP11_OR_GREATER
-    operator=( optional<U> const & other )
-    {
-        return *this = optional( other );
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    // 6 (C++11) -  converting move-assign from optional
-    template< typename U >
-        // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
-        optional_REQUIRES_R(
-            optional&,
-            std::is_constructible<  T , U>::value
-            &&  std::is_assignable< T&, U>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            && !std::is_assignable<  T&, optional<U> &          >::value
-            && !std::is_assignable<  T&, optional<U> &&         >::value
-            && !std::is_assignable<  T&, optional<U> const &    >::value
-            && !std::is_assignable<  T&, optional<U> const &&   >::value
-        )
-    operator=( optional<U> && other )
-    {
-        return *this = optional( std::move( other ) );
-    }
-
-    // 7 (C++11) - emplace
-    template< typename... Args
-        optional_REQUIRES_T(
-            std::is_constructible<T, Args&&...>::value
-        )
-    >
-    T& emplace( Args&&... args )
-    {
-        *this = nullopt;
-        contained.emplace( std::forward<Args>(args)...  );
-        has_value_ = true;
-        return contained.value();
-    }
-
-    // 8 (C++11) - emplace, initializer-list
-    template< typename U, typename... Args
-        optional_REQUIRES_T(
-            std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value
-        )
-    >
-    T& emplace( std::initializer_list<U> il, Args&&... args )
-    {
-        *this = nullopt;
-        contained.emplace( il, std::forward<Args>(args)...  );
-        has_value_ = true;
-        return contained.value();
-    }
-
-#endif // optional_CPP11_OR_GREATER
-
-    // x.x.3.4, swap
-
-    void swap( optional & other )
-#if optional_CPP11_OR_GREATER
-        noexcept(
-            std::is_nothrow_move_constructible<T>::value
-            && std17::is_nothrow_swappable<T>::value
-        )
-#endif
-    {
-        using std::swap;
-        if      ( (has_value() == true ) && (other.has_value() == true ) ) { swap( **this, *other ); }
-        else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std11::move(*other) ); other.reset(); }
-        else if ( (has_value() == true ) && (other.has_value() == false) ) { other.initialize( std11::move(**this) ); reset(); }
-    }
-
-    // x.x.3.5, observers
-
-    optional_constexpr value_type const * operator ->() const
-    {
-        return assert( has_value() ),
-            contained.value_ptr();
-    }
-
-    optional_constexpr14 value_type * operator ->()
-    {
-        return assert( has_value() ),
-            contained.value_ptr();
-    }
-
-    optional_constexpr value_type const & operator *() const optional_ref_qual
-    {
-        return assert( has_value() ),
-            contained.value();
-    }
-
-    optional_constexpr14 value_type & operator *() optional_ref_qual
-    {
-        return assert( has_value() ),
-            contained.value();
-    }
-
-#if optional_HAVE( REF_QUALIFIER )  &&  ( !optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490 )
-
-    optional_constexpr value_type const && operator *() const optional_refref_qual
-    {
-        return std::move( **this );
-    }
-
-    optional_constexpr14 value_type && operator *() optional_refref_qual
-    {
-        return std::move( **this );
-    }
-
-#endif
-
-#if optional_CPP11_OR_GREATER
-    optional_constexpr explicit operator bool() const optional_noexcept
-    {
-        return has_value();
-    }
-#else
-    optional_constexpr operator safe_bool() const optional_noexcept
-    {
-        return has_value() ? &optional::this_type_does_not_support_comparisons : 0;
-    }
-#endif
-
-    // NOLINTNEXTLINE( modernize-use-nodiscard )
-    /*optional_nodiscard*/ optional_constexpr bool has_value() const optional_noexcept
-    {
-        return has_value_;
-    }
-
-    // NOLINTNEXTLINE( modernize-use-nodiscard )
-    /*optional_nodiscard*/ optional_constexpr14 value_type const & value() const optional_ref_qual
-    {
-#if optional_CONFIG_NO_EXCEPTIONS
-        assert( has_value() );
-#else
-        if ( ! has_value() )
-        {
-            throw bad_optional_access();
-        }
-#endif
-        return contained.value();
-    }
-
-    optional_constexpr14 value_type & value() optional_ref_qual
-    {
-#if optional_CONFIG_NO_EXCEPTIONS
-        assert( has_value() );
-#else
-        if ( ! has_value() )
-        {
-            throw bad_optional_access();
-        }
-#endif
-        return contained.value();
-    }
-
-#if optional_HAVE( REF_QUALIFIER )  &&  ( !optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490 )
-
-    // NOLINTNEXTLINE( modernize-use-nodiscard )
-    /*optional_nodiscard*/ optional_constexpr value_type const && value() const optional_refref_qual
-    {
-        return std::move( value() );
-    }
-
-    optional_constexpr14 value_type && value() optional_refref_qual
-    {
-        return std::move( value() );
-    }
-
-#endif
-
-#if optional_CPP11_OR_GREATER
-
-    template< typename U >
-    optional_constexpr value_type value_or( U && v ) const optional_ref_qual
-    {
-        return has_value() ? contained.value() : static_cast<T>(std::forward<U>( v ) );
-    }
-
-    template< typename U >
-    optional_constexpr14 value_type value_or( U && v ) optional_refref_qual
-    {
-        return has_value() ? std::move( contained.value() ) : static_cast<T>(std::forward<U>( v ) );
-    }
-
-#else
-
-    template< typename U >
-    optional_constexpr value_type value_or( U const & v ) const
-    {
-        return has_value() ? contained.value() : static_cast<value_type>( v );
-    }
-
-#endif // optional_CPP11_OR_GREATER
-
-    // x.x.3.6, modifiers
-
-    void reset() optional_noexcept
-    {
-        if ( has_value() )
-        {
-            contained.destruct_value();
-        }
-
-        has_value_ = false;
-    }
-
-private:
-    void this_type_does_not_support_comparisons() const {}
-
-    template< typename V >
-    void initialize( V const & value )
-    {
-        assert( ! has_value()  );
-        contained.construct_value( value );
-        has_value_ = true;
-    }
-
-#if optional_CPP11_OR_GREATER
-    template< typename V >
-    void initialize( V && value )
-    {
-        assert( ! has_value()  );
-        contained.construct_value( std::move( value ) );
-        has_value_ = true;
-    }
-
-#endif
-
-private:
-    bool has_value_;
-    detail::storage_t< value_type > contained;
-
-};
-
-// Relational operators
-
-template< typename T, typename U >
-inline optional_constexpr bool operator==( optional<T> const & x, optional<U> const & y )
-{
-    return bool(x) != bool(y) ? false : !bool( x ) ? true : *x == *y;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator!=( optional<T> const & x, optional<U> const & y )
-{
-    return !(x == y);
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator<( optional<T> const & x, optional<U> const & y )
-{
-    return (!y) ? false : (!x) ? true : *x < *y;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator>( optional<T> const & x, optional<U> const & y )
-{
-    return (y < x);
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator<=( optional<T> const & x, optional<U> const & y )
-{
-    return !(y < x);
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator>=( optional<T> const & x, optional<U> const & y )
-{
-    return !(x < y);
-}
-
-// Comparison with nullopt
-
-template< typename T >
-inline optional_constexpr bool operator==( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return (!x);
-}
-
-template< typename T >
-inline optional_constexpr bool operator==( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
-{
-    return (!x);
-}
-
-template< typename T >
-inline optional_constexpr bool operator!=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return bool(x);
-}
-
-template< typename T >
-inline optional_constexpr bool operator!=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
-{
-    return bool(x);
-}
-
-template< typename T >
-inline optional_constexpr bool operator<( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return false;
-}
-
-template< typename T >
-inline optional_constexpr bool operator<( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
-{
-    return bool(x);
-}
-
-template< typename T >
-inline optional_constexpr bool operator<=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return (!x);
-}
-
-template< typename T >
-inline optional_constexpr bool operator<=( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
-{
-    return true;
-}
-
-template< typename T >
-inline optional_constexpr bool operator>( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return bool(x);
-}
-
-template< typename T >
-inline optional_constexpr bool operator>( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
-{
-    return false;
-}
-
-template< typename T >
-inline optional_constexpr bool operator>=( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return true;
-}
-
-template< typename T >
-inline optional_constexpr bool operator>=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
-{
-    return (!x);
-}
-
-// Comparison with T
-
-template< typename T, typename U >
-inline optional_constexpr bool operator==( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x == v : false;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator==( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v == *x : false;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator!=( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x != v : true;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator!=( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v != *x : true;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator<( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x < v : true;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator<( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v < *x : false;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator<=( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x <= v : true;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator<=( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v <= *x : false;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator>( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x > v : false;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator>( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v > *x : true;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator>=( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x >= v : false;
-}
-
-template< typename T, typename U >
-inline optional_constexpr bool operator>=( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v >= *x : true;
-}
-
-// Specialized algorithms
-
-template< typename T
-#if optional_CPP11_OR_GREATER
-    optional_REQUIRES_T(
-        std::is_move_constructible<T>::value
-        && std17::is_swappable<T>::value )
-#endif
->
-void swap( optional<T> & x, optional<T> & y )
-#if optional_CPP11_OR_GREATER
-    noexcept( noexcept( x.swap(y) ) )
-#endif
-{
-    x.swap( y );
-}
-
-#if optional_CPP11_OR_GREATER
-
-template< typename T >
-optional_constexpr optional< typename std::decay<T>::type > make_optional( T && value )
-{
-    return optional< typename std::decay<T>::type >( std::forward<T>( value ) );
-}
-
-template< typename T, typename...Args >
-optional_constexpr optional<T> make_optional( Args&&... args )
-{
-    return optional<T>( nonstd_lite_in_place(T), std::forward<Args>(args)...);
-}
-
-template< typename T, typename U, typename... Args >
-optional_constexpr optional<T> make_optional( std::initializer_list<U> il, Args&&... args )
-{
-    return optional<T>( nonstd_lite_in_place(T), il, std::forward<Args>(args)...);
-}
-
-#else
-
-template< typename T >
-optional<T> make_optional( T const & value )
-{
-    return optional<T>( value );
-}
-
-#endif // optional_CPP11_OR_GREATER
-
-} // namespace optional_lite
-
-using optional_lite::optional;
-using optional_lite::nullopt_t;
-using optional_lite::nullopt;
-using optional_lite::bad_optional_access;
-
-using optional_lite::make_optional;
-
-} // namespace nonstd
-
-#if optional_CPP11_OR_GREATER
-
-// specialize the std::hash algorithm:
-
-namespace std {
-
-template< class T >
-struct hash< nonstd::optional<T> >
-{
-public:
-    std::size_t operator()( nonstd::optional<T> const & v ) const optional_noexcept
-    {
-        return bool( v ) ? std::hash<T>{}( *v ) : 0;
-    }
-};
-
-} //namespace std
-
-#endif // optional_CPP11_OR_GREATER
-
-#if defined(__clang__)
-# pragma clang diagnostic pop
-#elif defined(__GNUC__)
-# pragma GCC   diagnostic pop
-#elif defined(_MSC_VER )
-# pragma warning( pop )
-#endif
-
-#endif // optional_USES_STD_OPTIONAL
-
-#endif // NONSTD_OPTIONAL_LITE_HPP
index a4c7f2787da24f60980f9f6471852e3d3f0a1325..203077c624401e99f61c313632bb8de21753ca74 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * define HAVE_RDTSCP to use the serializing rdtscp instruction instead of rdtsc.
+ * define GMX_USE_RDTSCP to use the serializing rdtscp instruction instead of rdtsc.
  * This is only supported on newer Intel/AMD hardware, but provides better accuracy.
  */
 
@@ -15,7 +15,7 @@ static __inline__ tMPI_Cycles_t tMPI_Cycles_read(void)
     tMPI_Cycles_t cycle;
     unsigned      low, high;
 
-#ifdef HAVE_RDTSCP
+#if GMX_USE_RDTSCP
     __asm__ __volatile__("rdtscp" : "=a" (low), "=d" (high) :: "ecx" );
 #else
     __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high));
@@ -48,7 +48,7 @@ static __inline__ tMPI_Cycles_t tMPI_Cycles_read(void)
 typedef __int64 tMPI_Cycles_t;
 static __inline tMPI_Cycles_t tMPI_Cycles_read(void)
 {
-#ifdef HAVE_RDTSCP
+#ifdef GMX_USE_RDTSCP
     unsigned int ui;
     return __rdtscp(&ui);
 #else
index 9249a7a08f03f0da7717b7172b9cbaf3a93fcba6..207a7062d82340af4f11fc8cec24895f4a956836 100644 (file)
@@ -1,8 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2011,2012,2013,2014,2015, The GROMACS development team.
-# Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+# Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -60,11 +60,13 @@ function (gmx_add_libgromacs_sources)
     _gmx_add_files_to_property(GMX_LIBGROMACS_SOURCES ${ARGN})
 endfunction ()
 
-# TODO Reconsider this, as the CUDA driver API is probably a simpler
-# approach, at least for the build system. See Redmine #2530
-function (gmx_compile_cpp_as_cuda)
-    _gmx_add_files_to_property(GMX_LIBGROMACS_GPU_IMPL_SOURCES ${ARGN})
-endfunction ()
+# Permit the configuration to disable compiling the many nbnxm kernels
+# and others involved in force calculations. Currently only
+# short-ranged and bonded kernels are disabled this way, but in future
+# others may be appropriate. Thus the cmake option is not specific to
+# nbnxm module.
+option(GMX_USE_SIMD_KERNELS "Whether to compile NBNXM and other SIMD kernels" ON)
+mark_as_advanced(GMX_USE_SIMD_KERNELS)
 
 # Add these contents first because linking their tests can take a lot
 # of time, so we want lots of parallel work still available after
@@ -101,7 +103,6 @@ add_subdirectory(trajectory)
 add_subdirectory(swap)
 add_subdirectory(essentialdynamics)
 add_subdirectory(pulling)
-add_subdirectory(awh)
 add_subdirectory(simd)
 add_subdirectory(imd)
 add_subdirectory(compat)
@@ -131,6 +132,10 @@ list(APPEND LIBGROMACS_SOURCES ${GMXLIB_SOURCES} ${MDLIB_SOURCES} ${PROPERTY_SOU
 tmpi_get_source_list(THREAD_MPI_SOURCES ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/src)
 add_library(thread_mpi OBJECT ${THREAD_MPI_SOURCES})
 target_compile_definitions(thread_mpi PRIVATE HAVE_CONFIG_H)
+if(CYGWIN)
+    # Needs POSIX-isms for strdup, not just std-isms
+    target_compile_definitions(thread_mpi PRIVATE _POSIX_C_SOURCE=200809L)
+endif()
 gmx_target_compile_options(thread_mpi)
 if (WIN32)
     gmx_target_warning_suppression(thread_mpi /wd4996 HAS_NO_MSVC_UNSAFE_FUNCTION)
@@ -165,7 +170,7 @@ gmx_configure_version_file(
 list(APPEND LIBGROMACS_SOURCES ${GENERATED_VERSION_FILE})
 
 # Mark some shared GPU implementation files to compile with CUDA if needed
-if (GMX_USE_CUDA)
+if (GMX_GPU_CUDA)
     get_property(LIBGROMACS_GPU_IMPL_SOURCES GLOBAL PROPERTY GMX_LIBGROMACS_GPU_IMPL_SOURCES)
     set_source_files_properties(${LIBGROMACS_GPU_IMPL_SOURCES} PROPERTIES CUDA_SOURCE_PROPERTY_FORMAT OBJ)
 endif()
@@ -181,7 +186,7 @@ if (GMX_CLANG_CUDA)
     endforeach()
 endif()
 
-if (GMX_USE_CUDA)
+if (GMX_GPU_CUDA)
     # Work around FindCUDA that prevents using target_link_libraries()
     # with keywords otherwise...
     set(CUDA_LIBRARIES PRIVATE ${CUDA_LIBRARIES})
@@ -220,7 +225,7 @@ gmx_target_compile_options(libgromacs)
 target_compile_definitions(libgromacs PRIVATE HAVE_CONFIG_H)
 target_include_directories(libgromacs SYSTEM BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include)
 
-if (GMX_USE_OPENCL)
+if (GMX_GPU_OPENCL)
     option(GMX_EXTERNAL_CLFFT "True if an external clFFT is required to be used" FALSE)
     mark_as_advanced(GMX_EXTERNAL_CLFFT)
 
@@ -277,8 +282,32 @@ if(SIMD_AVX_512_CXX_SUPPORTED AND NOT ("${GMX_SIMD_ACTIVE}" STREQUAL "AVX_512_KN
     set_source_files_properties(hardware/identifyavx512fmaunits.cpp PROPERTIES COMPILE_FLAGS "${SIMD_AVX_512_CXX_FLAGS} ${CXX_NO_UNUSED_OPTION_WARNING_FLAGS}")
 endif()
 
+# Do any special handling needed for .cpp files that use
+# CUDA runtime headers
+if (GMX_GPU_CUDA AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    # CUDA header cuda_runtime_api.h in at least CUDA 10.1 uses 0
+    # where nullptr would be preferable. GROMACS can't fix these, so
+    # must suppress them.
+    GMX_TEST_CXXFLAG(CXXFLAGS_NO_ZERO_AS_NULL_POINTER_CONSTANT "-Wno-zero-as-null-pointer-constant" NVCC_CLANG_SUPPRESSIONS_CXXFLAGS)
+
+    get_property(CUDA_SOURCES GLOBAL PROPERTY CUDA_SOURCES)
+    foreach(_compile_flag ${NVCC_CLANG_SUPPRESSIONS_CXXFLAGS})
+        set_source_files_properties(${CUDA_SOURCES} PROPERTIES COMPILE_FLAGS ${_compile_flag})
+    endforeach()
+endif()
+
+# Only add the -fsycl flag to sources that really need it
+if (GMX_GPU_SYCL)
+    get_property(SYCL_SOURCES GLOBAL PROPERTY SYCL_SOURCES)
+    set_source_files_properties(${SYCL_SOURCES} PROPERTIES COMPILE_FLAGS "${SYCL_CXX_FLAGS}")
+endif()
+
 gmx_setup_tng_for_libgromacs()
 
+# We apply the SYCL flag explicitly just for libgromacs, since bugs in the beta versions of
+# icpx/dpcpp leads to crashes if we try to link an library without any SYCL code with the
+# -fsycl flag enabled. Once that bug is fixed, we should change it to simply add
+# SYCL_CXX_FLAGS to GMX_SHARED_LINKER_FLAGS.
 target_link_libraries(libgromacs
                       PRIVATE
                       ${EXTRAE_LIBRARIES}
@@ -286,6 +315,7 @@ target_link_libraries(libgromacs
                       ${GMX_COMMON_LIBRARIES}
                       ${FFT_LIBRARIES} ${LINEAR_ALGEBRA_LIBRARIES}
                       ${THREAD_LIB} ${GMX_SHARED_LINKER_FLAGS}
+                      ${SYCL_CXX_FLAGS}
                       ${OpenCL_LIBRARIES}
                       $<$<PLATFORM_ID:SunOS>:socket>
                       PUBLIC
@@ -302,11 +332,11 @@ set_target_properties(libgromacs PROPERTIES
 
 gmx_manage_lmfit()
 target_link_libraries(libgromacs PRIVATE lmfit)
+gmx_manage_muparser()
+target_link_libraries(libgromacs PRIVATE muparser)
 
-# Fix everything found by the latest version of clang that we use in
-# Jenkins testing. This should be updated when we update the latest
-# tested version of clang.
-if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION MATCHES "^7\.0")
+# Make sure we fix "everything" found by more recent versions of clang.
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7")
    target_compile_options(libgromacs PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Weverything ${IGNORED_CLANG_ALL_WARNINGS}>)
 endif()
 if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
@@ -365,6 +395,12 @@ if (NOT GMX_BUILD_MDRUN_ONLY OR BUILD_SHARED_LIBS)
                 DESTINATION ${CMAKE_INSTALL_LIBDIR}
                 COMPONENT libraries
             INCLUDES DESTINATION include)
+    target_compile_definitions(libgromacs PUBLIC $<INSTALL_INTERFACE:GMX_DOUBLE=${GMX_DOUBLE_VALUE}>)
+    # legacy headers use c++17 features, so consumer codes need to use that standard, too
+    if(GMX_INSTALL_LEGACY_API)
+        target_compile_features(libgromacs INTERFACE cxx_std_${CMAKE_CXX_STANDARD})
+    endif()
+    add_library(Gromacs::libgromacs ALIAS libgromacs)
 endif()
 
 if (NOT GMX_BUILD_MDRUN_ONLY)
@@ -375,7 +411,7 @@ endif()
 # using the CUDA runtime, but currently there's no reason to want to
 # do that.
 if (INSTALL_CUDART_LIB) #can be set manual by user
-    if (GMX_USE_CUDA)
+    if (GMX_GPU_CUDA)
         foreach(CUDA_LIB ${CUDA_LIBRARIES})
             string(REGEX MATCH "cudart" IS_CUDART ${CUDA_LIB})
             if(IS_CUDART) #libcuda should not be installed
@@ -390,7 +426,7 @@ if (INSTALL_CUDART_LIB) #can be set manual by user
     endif()
 endif()
 
-if(GMX_USE_OPENCL)
+if(GMX_GPU_OPENCL)
     # Install the utility headers
     file(GLOB OPENCL_INSTALLED_FILES
         gpu_utils/vectype_ops.clh
@@ -432,7 +468,7 @@ if(GMX_USE_OPENCL)
         ewald/pme_spread.clh
         ewald/pme_solve.clh
         ewald/pme_gather.clh
-        ewald/pme_gpu_utils.clh
+        ewald/pme_gpu_calculate_splines.clh
         ewald/pme_program.cl
         ewald/pme_gpu_types.h
         )
index 67a4b5729eda46bf1b06a8c6401d1921a9329d9c..80289a4dd0780ff8dffd941adba0e60944970662 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2016,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2016,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -70,6 +70,7 @@ function (do_cmake_config)
     endif()
     install(EXPORT libgromacs
             FILE ${EXPORT_FILE_NAME}
+            NAMESPACE Gromacs::
             DESTINATION ${GMX_INSTALL_CMAKEPKGDIR}
             COMPONENT libraries)
 
index e2dc9683894b8fdae03f8610f2716287ed50c9f3..43b71d60ec8419f9b124bdf42e7f9211c16de451 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2011,2012,2013,2014,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 6822878a9882ea48b5c2b348b3a534700fd28942..31b4fe2cf4a5bbd1c02b98f57893a42fe0906795 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 3b3767e7a1941d218bcb95fffcfd33432f6c4a7b..ca83202be56efe06ea5581d11099b88379b03cef 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 691a63a2a75f6d430271488cd361773ccb9b9ac9..8dfa05a5722630b2b99eeba5b9ced9b47d90528e 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7b1e72d16577404469c6de1361ff060215076faf..71de8903cb0508d2156380be1bc7ac5245ef7e82 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 5a5164d291c60cdb5ad981c092a09b05205a7458..c034fd8ed27a030d85e12cc7e7afe5c74f13f566 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -153,7 +153,7 @@ public:
 
 private:
     //! Possible flags for \a flags_.
-    enum Flag
+    enum Flag : uint64_t
     {
         efSet      = 1 << 0, //!< Value has been set.
         efErrorSet = 1 << 1, //!< Error estimate has been set.
index 6183cbc1771771c31f0277ac55d09f9829e1db91..220be7d5595661dadfb5ac9720065204e809a135 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -212,7 +213,7 @@ public:
      * This method is called after frameFinished(), but with an additional
      * constraint that it is always called in serial and with an increasing
      * \p frameIndex.  Parallel data modules need this to serialize their
-     * data for downsteam serial modules; AnalysisDataModuleSerial provides
+     * data for downstream serial modules; AnalysisDataModuleSerial provides
      * an empty implementation, as there frameFinished() can be used for
      * the same purpose.
      */
index a68bb85459ab48eac00383976731434b5291a36e..bd51891b02bc4aa9d2285bc43255f7c2bec169ff 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -107,7 +107,7 @@ public:
      * \param[in] bSet     Value of the property to check against.
      * \throws    APIError if \p module is not compatible with the data.
      */
-    void checkModuleProperty(const IAnalysisDataModule& module, DataProperty property, bool bSet) const;
+    static void checkModuleProperty(const IAnalysisDataModule& module, DataProperty property, bool bSet);
     /*! \brief
      * Checks whether a module is compatible with the data properties.
      *
@@ -175,7 +175,7 @@ AnalysisDataModuleManager::Impl::Impl() :
 
 void AnalysisDataModuleManager::Impl::checkModuleProperty(const IAnalysisDataModule& module,
                                                           DataProperty               property,
-                                                          bool                       bSet) const
+                                                          bool                       bSet)
 {
     bool      bOk   = true;
     const int flags = module.flags();
index 09f40e219b5a8946c1c8225ad010c6f8ec6a2242..55f15ac8579ebaf5c8827cfe28077e1838e62183 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 6417272fc47e0816c50c59c6d2f0fa116455b3d8..24460bd78eaadd23e32434f156a0f0b15e0bd53f 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b10535a9cdc3f7c5fd41973de9656c01d924f512..7c672e02e0632b96009d22ff944d610cd8a29757 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e5b9302340ae5bdd22d71046467d8a85683408be..78f379e8693e753006591fdc3b43c703d8f16fe8 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 432926cbc89151b032e7b50746575cf09b653221..33d9e977ed0f93c93adc7778fa28350c1dcfb9c9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <vector>
 
 #include "gromacs/analysisdata/paralleloptions.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/gmxassert.h"
 
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
+
 //! \addtogroup module_analysisdata
 //! \{
 
index ea5c25601c48d07b2a2bca616f25cfef37709c38..38b20682014b296cd3e5afa605133c3aee0ec6b0 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7b14c449602e5232269b29a670da0eced758df1a..52527ccb7d5f9b6e3283314e521a3aa31e448ae6 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 3c6cdc467c49e6f05df9746245e0bff12cfae569..9cad05c3ca6122e7d46b3c3f0d5921c3d595fc62 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 1dbecbf2ee0695060c049b3500e220e8055359a1..72fb6e5e752e9cba1493568cf07a66fab0664637 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7a2f8c5ec92fe486969a7cbe9a5949a6f5078cf4..c15844c6c3438d9de25bb5ac68294770ee18b860 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 5c9727ce7953553d75e9b8e9f55753f8fcb4c65d..f791cabd27dcfd1c4b59da5e6dc3ab86de8c9d64 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 573dbd47043e9556739c781fdf9dd8106f3401dc..0ea52a0b8d054a779253b09c46c96b608c29a7f8 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/stringutil.h"
 #include "gromacs/utility/unique_cptr.h"
 
-namespace
-{
-
-//! Enum values for plot formats.
-const char* const g_plotFormats[] = { "none", "xmgrace", "xmgr" };
-
-} // namespace
-
 namespace gmx
 {
 
@@ -82,8 +74,8 @@ namespace gmx
 
 AnalysisDataPlotSettings::AnalysisDataPlotSettings() :
     selections_(nullptr),
-    timeUnit_(TimeUnit_Default),
-    plotFormat_(1)
+    timeUnit_(TimeUnit::Default),
+    plotFormat_(XvgFormat::Xmgrace)
 {
 }
 
@@ -92,11 +84,18 @@ void AnalysisDataPlotSettings::setSelectionCollection(const SelectionCollection*
     selections_ = selections;
 }
 
+/*! \brief Names for XvgFormat
+ *
+ * Technically this duplicates a definition in pargs.cpp for legacy
+ * support code, but as the latter will go away and the alternatives
+ * are ugly, the duplication is acceptable. */
+const gmx::EnumerationArray<XvgFormat, const char*> c_xvgFormatNames = { { "xmgrace", "xmgr",
+                                                                           "none" } };
 
 void AnalysisDataPlotSettings::initOptions(IOptionsContainer* options)
 {
     options->addOption(
-            EnumIntOption("xvg").enumValue(g_plotFormats).store(&plotFormat_).description("Plot formatting"));
+            EnumOption<XvgFormat>("xvg").enumValue(c_xvgFormatNames).store(&plotFormat_).description("Plot formatting"));
 }
 
 
@@ -308,18 +307,15 @@ void AbstractPlotModule::dataStarted(AbstractAnalysisData* /* data */)
         }
         else
         {
-            // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)
-            time_unit_t       time_unit = static_cast<time_unit_t>(impl_->settings_.timeUnit() + 1);
-            xvg_format_t      xvg_format = (impl_->settings_.plotFormat() > 0
-                                               ? static_cast<xvg_format_t>(impl_->settings_.plotFormat())
-                                               : exvgNONE);
+            const TimeUnit    timeUnit  = impl_->settings_.timeUnit();
+            const XvgFormat   xvgFormat = impl_->settings_.plotFormat();
             gmx_output_env_t* oenv;
-            output_env_init(&oenv, getProgramContext(), time_unit, FALSE, xvg_format, 0);
+            output_env_init(&oenv, getProgramContext(), timeUnit, FALSE, xvgFormat, 0);
             const unique_cptr<gmx_output_env_t, output_env_done> oenvGuard(oenv);
             impl_->fp_ = xvgropen(impl_->filename_.c_str(), impl_->title_.c_str(), impl_->xlabel_,
                                   impl_->ylabel_, oenv);
             const SelectionCollection* selections = impl_->settings_.selectionCollection();
-            if (selections != nullptr && output_env_get_xvg_format(oenv) != exvgNONE)
+            if (selections != nullptr && output_env_get_xvg_format(oenv) != XvgFormat::None)
             {
                 selections->printXvgrInfo(impl_->fp_);
             }
index aa64b57279462185e1a20d20b850ea19b99a0929..7064a49c22567d8f26d717a42d1422ecf29ea109 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,6 +51,8 @@
 #include "gromacs/options/timeunitmanager.h"
 #include "gromacs/utility/classhelpers.h"
 
+enum class XvgFormat : int;
+
 namespace gmx
 {
 
@@ -75,10 +78,8 @@ public:
     TimeUnit timeUnit() const { return timeUnit_; }
     /*! \brief
      * Returns the plot format.
-     *
-     * \todo Use a proper enum.
      */
-    int plotFormat() const { return plotFormat_; }
+    XvgFormat plotFormat() const { return plotFormat_; }
 
     /*! \brief
      * Set selection collection to print as comments into the output.
@@ -110,7 +111,7 @@ public:
 private:
     const SelectionCollection* selections_;
     TimeUnit                   timeUnit_;
-    int                        plotFormat_;
+    XvgFormat                  plotFormat_;
 };
 
 /*! \brief
index e0fc5e1595f7f43e1508dc16b054a1380311388b..bcecb7340e7f0ca17cbf22adf1a96d2eb4d4371f 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2011,2012,2013,2014,2019, by the GROMACS development team, led by
+# Copyright (c) 2011,2012,2013,2014,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -37,9 +37,11 @@ gmx_add_unit_test_library(analysisdata-test-shared
                           mock_datamodule.cpp)
 
 gmx_add_unit_test(AnalysisDataUnitTests analysisdata-test
-                  analysisdata.cpp
-                  arraydata.cpp
-                  average.cpp
-                  histogram.cpp
-                  lifetime.cpp)
+    CPP_SOURCE_FILES
+        analysisdata.cpp
+        arraydata.cpp
+        average.cpp
+        histogram.cpp
+        lifetime.cpp
+        )
 target_link_libraries(analysisdata-test PRIVATE analysisdata-test-shared)
index 30dba86a34908d3ff97b6daea707eb4671df86db..38d7d295258967549607b937c83134e8dc3e5644 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 18b0b8addaa6d8267a1b3811ccaf745575da7fe5..e8009f40a129cf1ce97c879ef4d9d2fda0edd4eb 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -206,7 +207,7 @@ void AnalysisDataTestFixture::presentAllData(const AnalysisDataTestInput& input,
 void AnalysisDataTestFixture::presentDataFrame(const AnalysisDataTestInput& input, int row, AnalysisDataHandle handle)
 {
     const AnalysisDataTestInputFrame& frame = input.frame(row);
-    handle.startFrame(row, frame.x(), frame.dx());
+    handle.startFrame(row, frame.x(), AnalysisDataTestInputFrame::dx());
     for (int i = 0; i < frame.pointSetCount(); ++i)
     {
         const AnalysisDataTestInputPointSet& points = frame.pointSet(i);
@@ -215,11 +216,13 @@ void AnalysisDataTestFixture::presentDataFrame(const AnalysisDataTestInput& inpu
         {
             if (points.hasError(j))
             {
-                handle.setPoint(j + points.firstColumn(), points.y(j), points.error(j), points.present(j));
+                handle.setPoint(j + points.firstColumn(), points.y(j), points.error(j),
+                                AnalysisDataTestInputPointSet::present(j));
             }
             else
             {
-                handle.setPoint(j + points.firstColumn(), points.y(j), points.present(j));
+                handle.setPoint(j + points.firstColumn(), points.y(j),
+                                AnalysisDataTestInputPointSet::present(j));
             }
         }
         if (input.isMultipoint())
index 6f1b57ee446e9694d5fb990897485031cad33f17..1e1766b58b3d06815e77c2472a32c692a143c3be 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -101,7 +102,7 @@ public:
     //! Returns the error in column \p i.
     real error(int i) const { return values_[i].error; }
     //! Returns whether the value in column \p i is present.
-    bool present(int /*i*/) const { return true; }
+    static bool present(int /*i*/) { return true; }
     //! Returns an AnalysisDataValue for column \p i.
     AnalysisDataValue value(int i) const
     {
@@ -157,7 +158,7 @@ public:
     //! Returns x coordinate for the frame.
     real x() const { return x_; }
     //! Returns error in the x coordinate for the frame.
-    real dx() const { return 0.0; }
+    static real dx() { return 0.0; }
 
     //! Number of individual point sets in the frame.
     int pointSetCount() const { return pointSets_.size(); }
index 05046f5d40e4a5e4edfa9176a69877411b729229..e0ccf94153c50985002e66628481ea912fa8882d 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 24226093a6035a2a1490b756242b0a4944e9e675..65eaf2b06a54a1af618c372a4afd9bbdbd33bb7b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d7ea182db697e511d50b1b3f1210eaeeaa8edaa2..498d44a220d3a56134a412990a8954b135cb83aa 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2016,2019, by the GROMACS development team, led by
+# Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_libgromacs_sources(
-    densityfitting.cpp
-    densityfittingamplitudelookup.cpp
-    densityfittingforceprovider.cpp
-    densityfittingoptions.cpp
-    densityfittingoutputprovider.cpp
-    densityfittingparameters.cpp
     electricfield.cpp
     )
 
+add_subdirectory(awh)
+add_subdirectory(densityfitting)
+
 if (BUILD_TESTING)
     add_subdirectory(tests)
 endif()
diff --git a/src/gromacs/applied_forces/awh/CMakeLists.txt b/src/gromacs/applied_forces/awh/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0917be1
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2014,2015,2016,2017,2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+gmx_add_libgromacs_sources(awh.cpp
+                           bias.cpp
+                           biasgrid.cpp
+                           biasparams.cpp
+                           biassharing.cpp
+                           biasstate.cpp
+                           biaswriter.cpp
+                           coordstate.cpp
+                           correlationgrid.cpp
+                           correlationhistory.cpp
+                           correlationtensor.cpp
+                           histogramsize.cpp
+                           pointstate.cpp
+                           read_params.cpp)
+
+if (BUILD_TESTING)
+    add_subdirectory(tests)
+endif()
similarity index 64%
rename from src/gromacs/awh/awh.cpp
rename to src/gromacs/applied_forces/awh/awh.cpp
index 76046ff09bca60337681b77b5aa22ea9642a829b..0f3c8c28b4c55c6e5436eaebb0bde15c06e469b5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -39,6 +39,7 @@
  *
  * \author Viveca Lindahl
  * \author Berk Hess <hess@kth.se>
+ * \author Magnus Lundborg
  * \ingroup module_awh
  */
 
@@ -69,6 +70,7 @@
 #include "gromacs/pulling/pull.h"
 #include "gromacs/timing/wallcycle.h"
 #include "gromacs/trajectory/energyframe.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/pleasecite.h"
@@ -105,13 +107,67 @@ struct BiasCoupledToSystem
     /* Here AWH can be extended to work on other coordinates than pull. */
 };
 
+/*! \brief Checks whether any dimension uses pulling as a coordinate provider.
+ *
+ * \param[in] awhBiasParams The bias params to check.
+ * \returns true if any dimension of the bias is linked to pulling.
+ */
+static bool anyDimUsesPull(const AwhBiasParams& awhBiasParams)
+{
+    for (int d = 0; d < awhBiasParams.ndim; d++)
+    {
+        const AwhDimParams& awhDimParams = awhBiasParams.dimParams[d];
+        if (awhDimParams.eCoordProvider == eawhcoordproviderPULL)
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*! \brief Checks whether any dimension uses pulling as a coordinate provider.
+ *
+ * \param[in] awhParams The AWH params to check.
+ * \returns true if any dimension of awh is linked to pulling.
+ */
+static bool anyDimUsesPull(const AwhParams& awhParams)
+{
+    for (int k = 0; k < awhParams.numBias; k++)
+    {
+        const AwhBiasParams& awhBiasParams = awhParams.awhBiasParams[k];
+        if (anyDimUsesPull(awhBiasParams))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*! \brief Checks whether any dimension uses pulling as a coordinate provider.
+ *
+ * \param[in] biasCoupledToSystem The AWH biases to check.
+ * \returns true if any dimension of the provided biases is linked to pulling.
+ */
+static bool anyDimUsesPull(const ArrayRef<BiasCoupledToSystem> biasCoupledToSystem)
+{
+    for (const auto& biasCts : biasCoupledToSystem)
+    {
+        if (!biasCts.pullCoordIndex_.empty())
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
 BiasCoupledToSystem::BiasCoupledToSystem(Bias bias, const std::vector<int>& pullCoordIndex) :
     bias_(std::move(bias)),
     pullCoordIndex_(pullCoordIndex)
 {
     /* We already checked for this in grompp, but check again here. */
-    GMX_RELEASE_ASSERT(static_cast<size_t>(bias_.ndim()) == pullCoordIndex_.size(),
-                       "The bias dimensionality should match the number of pull coordinates.");
+    GMX_RELEASE_ASSERT(
+            static_cast<size_t>(bias_.ndim()) == pullCoordIndex_.size() + bias_.hasFepLambdaDimension() ? 1 : 0,
+            "The bias dimensionality should match the number of pull and lambda coordinates.");
 }
 
 Awh::Awh(FILE*                 fplog,
@@ -120,18 +176,24 @@ Awh::Awh(FILE*                 fplog,
          const gmx_multisim_t* multiSimRecord,
          const AwhParams&      awhParams,
          const std::string&    biasInitFilename,
-         pull_t*               pull_work) :
+         pull_t*               pull_work,
+         int                   numFepLambdaStates,
+         int                   fepLambdaState) :
     seed_(awhParams.seed),
     nstout_(awhParams.nstOut),
     commRecord_(commRecord),
     multiSimRecord_(multiSimRecord),
     pull_(pull_work),
-    potentialOffset_(0)
+    potentialOffset_(0),
+    numFepLambdaStates_(numFepLambdaStates),
+    fepLambdaState_(fepLambdaState)
 {
-    /* We already checked for this in grompp, but check again here. */
-    GMX_RELEASE_ASSERT(inputRecord.pull != nullptr, "With AWH we should have pull parameters");
-    GMX_RELEASE_ASSERT(pull_work != nullptr,
-                       "With AWH pull should be initialized before initializing AWH");
+    if (anyDimUsesPull(awhParams))
+    {
+        GMX_RELEASE_ASSERT(inputRecord.pull != nullptr, "With AWH we should have pull parameters");
+        GMX_RELEASE_ASSERT(pull_work != nullptr,
+                           "With AWH pull should be initialized before initializing AWH");
+    }
 
     if (fplog != nullptr)
     {
@@ -149,7 +211,7 @@ Awh::Awh(FILE*                 fplog,
     int numSharingSimulations = 1;
     if (awhParams.shareBiasMultisim && isMultiSim(multiSimRecord_))
     {
-        numSharingSimulations = multiSimRecord_->nsim;
+        numSharingSimulations = multiSimRecord_->numSimulations_;
     }
 
     /* Initialize all the biases */
@@ -163,15 +225,30 @@ Awh::Awh(FILE*                 fplog,
         for (int d = 0; d < awhBiasParams.ndim; d++)
         {
             const AwhDimParams& awhDimParams = awhBiasParams.dimParams[d];
-            GMX_RELEASE_ASSERT(awhDimParams.eCoordProvider == eawhcoordproviderPULL,
-                               "Currently only the pull code is supported as coordinate provider");
-            const t_pull_coord& pullCoord = inputRecord.pull->coord[awhDimParams.coordIndex];
-            GMX_RELEASE_ASSERT(pullCoord.eGeom != epullgDIRPBC,
-                               "Pull geometry 'direction-periodic' is not supported by AWH");
-            double conversionFactor = pull_coordinate_is_angletype(&pullCoord) ? DEG2RAD : 1;
-            dimParams.emplace_back(conversionFactor, awhDimParams.forceConstant, beta);
-
-            pullCoordIndex.push_back(awhDimParams.coordIndex);
+            if (awhDimParams.eCoordProvider != eawhcoordproviderPULL
+                && awhDimParams.eCoordProvider != eawhcoordproviderFREE_ENERGY_LAMBDA)
+            {
+                GMX_THROW(
+                        InvalidInputError("Currently only the pull code and lambda are supported "
+                                          "as coordinate providers"));
+            }
+            if (awhDimParams.eCoordProvider == eawhcoordproviderPULL)
+            {
+                const t_pull_coord& pullCoord = inputRecord.pull->coord[awhDimParams.coordIndex];
+                if (pullCoord.eGeom == epullgDIRPBC)
+                {
+                    GMX_THROW(InvalidInputError(
+                            "Pull geometry 'direction-periodic' is not supported by AWH"));
+                }
+                double conversionFactor = pull_coordinate_is_angletype(&pullCoord) ? DEG2RAD : 1;
+                pullCoordIndex.push_back(awhDimParams.coordIndex);
+                dimParams.push_back(DimParams::pullDimParams(conversionFactor,
+                                                             awhDimParams.forceConstant, beta));
+            }
+            else
+            {
+                dimParams.push_back(DimParams::fepLambdaDimParams(numFepLambdaStates_, beta));
+            }
         }
 
         /* Construct the bias and couple it to the system. */
@@ -207,21 +284,26 @@ bool Awh::isOutputStep(int64_t step) const
     return (nstout_ > 0 && step % nstout_ == 0);
 }
 
-real Awh::applyBiasForcesAndUpdateBias(int                   ePBC,
-                                       const t_mdatoms&      mdatoms,
-                                       const matrix          box,
-                                       gmx::ForceWithVirial* forceWithVirial,
-                                       double                t,
-                                       int64_t               step,
-                                       gmx_wallcycle*        wallcycle,
-                                       FILE*                 fplog)
+real Awh::applyBiasForcesAndUpdateBias(PbcType                pbcType,
+                                       const real*            masses,
+                                       ArrayRef<const double> neighborLambdaEnergies,
+                                       ArrayRef<const double> neighborLambdaDhdl,
+                                       const matrix           box,
+                                       gmx::ForceWithVirial*  forceWithVirial,
+                                       double                 t,
+                                       int64_t                step,
+                                       gmx_wallcycle*         wallcycle,
+                                       FILE*                  fplog)
 {
-    GMX_ASSERT(forceWithVirial, "Need a valid ForceWithVirial object");
+    if (anyDimUsesPull(biasCoupledToSystem_))
+    {
+        GMX_ASSERT(forceWithVirial, "Need a valid ForceWithVirial object");
+    }
 
     wallcycle_start(wallcycle, ewcAWH);
 
     t_pbc pbc;
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
 
     /* During the AWH update the potential can instantaneously jump due to either
        an bias update or moving the umbrella. The jumps are kept track of and
@@ -233,10 +315,20 @@ real Awh::applyBiasForcesAndUpdateBias(int                   ePBC,
         /* Update the AWH coordinate values with those of the corresponding
          * pull coordinates.
          */
-        awh_dvec coordValue = { 0, 0, 0, 0 };
+        awh_dvec coordValue           = { 0, 0, 0, 0 };
+        int      numLambdaDimsCounted = 0;
         for (int d = 0; d < biasCts.bias_.ndim(); d++)
         {
-            coordValue[d] = get_pull_coord_value(pull_, biasCts.pullCoordIndex_[d], &pbc);
+            if (biasCts.bias_.dimParams()[d].isPullDimension())
+            {
+                coordValue[d] = get_pull_coord_value(
+                        pull_, biasCts.pullCoordIndex_[d - numLambdaDimsCounted], &pbc);
+            }
+            else
+            {
+                coordValue[d] = fepLambdaState_;
+                numLambdaDimsCounted += 1;
+            }
         }
 
         /* Perform an AWH biasing step: this means, at regular intervals,
@@ -249,8 +341,8 @@ real Awh::applyBiasForcesAndUpdateBias(int                   ePBC,
          *       to supports bias sharing within a single simulation.
          */
         gmx::ArrayRef<const double> biasForce = biasCts.bias_.calcForceAndUpdateBias(
-                coordValue, &biasPotential, &biasPotentialJump, commRecord_, multiSimRecord_, t,
-                step, seed_, fplog);
+                coordValue, neighborLambdaEnergies, neighborLambdaDhdl, &biasPotential,
+                &biasPotentialJump, commRecord_, multiSimRecord_, t, step, seed_, fplog);
 
         awhPotential += biasPotential;
 
@@ -261,10 +353,20 @@ real Awh::applyBiasForcesAndUpdateBias(int                   ePBC,
          * The bias potential is returned at the end of this function,
          * so that it can be added externally to the correct energy data block.
          */
+        numLambdaDimsCounted = 0;
         for (int d = 0; d < biasCts.bias_.ndim(); d++)
         {
-            apply_external_pull_coord_force(pull_, biasCts.pullCoordIndex_[d], biasForce[d],
-                                            &mdatoms, forceWithVirial);
+            if (biasCts.bias_.dimParams()[d].isPullDimension())
+            {
+                apply_external_pull_coord_force(pull_, biasCts.pullCoordIndex_[d - numLambdaDimsCounted],
+                                                biasForce[d], masses, forceWithVirial);
+            }
+            else
+            {
+                int umbrellaGridpointIndex = biasCts.bias_.state().coordState().umbrellaGridpoint();
+                fepLambdaState_ = biasCts.bias_.getGridCoordValue(umbrellaGridpointIndex)[d];
+                numLambdaDimsCounted += 1;
+            }
         }
 
         if (isOutputStep(step))
@@ -323,7 +425,7 @@ void Awh::restoreStateFromHistory(const AwhHistory* awhHistory)
     }
     if (PAR(commRecord_))
     {
-        gmx_bcast(sizeof(potentialOffset_), &potentialOffset_, commRecord_);
+        gmx_bcast(sizeof(potentialOffset_), &potentialOffset_, commRecord_->mpi_comm_mygroup);
     }
 
     for (size_t k = 0; k < biasCoupledToSystem_.size(); k++)
@@ -359,7 +461,7 @@ const char* Awh::externalPotentialString()
 
 void Awh::registerAwhWithPull(const AwhParams& awhParams, pull_t* pull_work)
 {
-    GMX_RELEASE_ASSERT(pull_work, "Need a valid pull object");
+    GMX_RELEASE_ASSERT(!anyDimUsesPull(awhParams) || pull_work, "Need a valid pull object");
 
     for (int k = 0; k < awhParams.numBias; k++)
     {
@@ -367,8 +469,11 @@ void Awh::registerAwhWithPull(const AwhParams& awhParams, pull_t* pull_work)
 
         for (int d = 0; d < biasParams.ndim; d++)
         {
-            register_external_pull_potential(pull_work, biasParams.dimParams[d].coordIndex,
-                                             Awh::externalPotentialString());
+            if (biasParams.dimParams[d].eCoordProvider == eawhcoordproviderPULL)
+            {
+                register_external_pull_potential(pull_work, biasParams.dimParams[d].coordIndex,
+                                                 Awh::externalPotentialString());
+            }
         }
     }
 }
@@ -412,6 +517,38 @@ void Awh::writeToEnergyFrame(int64_t step, t_enxframe* frame) const
     }
 }
 
+bool Awh::hasFepLambdaDimension() const
+{
+    return std::any_of(
+            std::begin(biasCoupledToSystem_), std::end(biasCoupledToSystem_),
+            [](const auto& coupledBias) { return coupledBias.bias_.hasFepLambdaDimension(); });
+}
+
+bool Awh::needForeignEnergyDifferences(const int64_t step) const
+{
+    /* If there is no FEP lambda dimension at all in any bias there will be no need for foreign
+     * energy differences */
+    if (!hasFepLambdaDimension())
+    {
+        return false;
+    }
+    if (step == 0)
+    {
+        return true;
+    }
+    /* Check whether the bias(es) that has/have a FEP lambda dimension should sample coordinates
+     * this step. Since the biases may have different sampleCoordStep it is necessary to check
+     * this combination. */
+    for (const auto& biasCts : biasCoupledToSystem_)
+    {
+        if (biasCts.bias_.hasFepLambdaDimension() && biasCts.bias_.isSampleCoordStep(step))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
 std::unique_ptr<Awh> prepareAwhModule(FILE*                 fplog,
                                       const t_inputrec&     inputRecord,
                                       t_state*              stateGlobal,
@@ -431,8 +568,9 @@ std::unique_ptr<Awh> prepareAwhModule(FILE*                 fplog,
         GMX_THROW(InvalidInputError("AWH biasing does not support shell particles."));
     }
 
-    auto awh = std::make_unique<Awh>(fplog, inputRecord, commRecord, multiSimRecord,
-                                     *inputRecord.awhParams, biasInitFilename, pull_work);
+    auto awh = std::make_unique<Awh>(
+            fplog, inputRecord, commRecord, multiSimRecord, *inputRecord.awhParams, biasInitFilename,
+            pull_work, inputRecord.fepvals->n_lambda, inputRecord.fepvals->init_fep_state);
 
     if (startingFromCheckpoint)
     {
similarity index 73%
rename from src/gromacs/awh/awh.h
rename to src/gromacs/applied_forces/awh/awh.h
index 5c57dfabe137574859c1e32cdeaa1b7a23a9c97f..30b0a25b1a3506b9ecf778f7f6d603a8900df57e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -35,7 +35,7 @@
 
 /*! \libinternal
  * \defgroup module_awh Accelerated weight histogram (AWH) method
- * \ingroup group_mdrun
+ * \ingroup module_applied_forces
  * \brief
  * Implements the "accelerated weight histogram" sampling method.
  *
@@ -46,6 +46,7 @@
  *
  * \author Viveca Lindahl
  * \author Berk Hess <hess@kth.se>
+ * \author Magnus Lundborg
  */
 
 /*! \libinternal \file
@@ -79,11 +80,13 @@ class t_state;
 struct t_commrec;
 struct t_enxframe;
 struct t_inputrec;
-struct t_mdatoms;
+enum class PbcType : int;
 
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
 struct AwhHistory;
 struct AwhParams;
 class Bias;
@@ -115,13 +118,19 @@ public:
      * in the user input. This allows AWH to later apply the bias force to
      * these coordinate in \ref Awh::applyBiasForcesAndUpdateBias.
      *
-     * \param[in,out] fplog             General output file, normally md.log, can be nullptr.
-     * \param[in]     inputRecord       General input parameters (as set up by grompp).
-     * \param[in]     commRecord        Struct for communication, can be nullptr.
-     * \param[in]     multiSimRecord    Multi-sim handler
-     * \param[in]     awhParams         AWH input parameters, consistent with the relevant parts of \p inputRecord (as set up by grompp).
-     * \param[in]     biasInitFilename  Name of file to read PMF and target from.
-     * \param[in,out] pull_work         Pointer to a pull struct which AWH will couple to, has to be initialized, is assumed not to change during the lifetime of the Awh object.
+     * \param[in,out] fplog              General output file, normally md.log, can be nullptr.
+     * \param[in]     inputRecord        General input parameters (as set up by grompp).
+     * \param[in]     commRecord         Struct for communication, can be nullptr.
+     * \param[in]     multiSimRecord     Multi-sim handler
+     * \param[in]     awhParams          AWH input parameters, consistent with the relevant
+     * parts of \p inputRecord (as set up by grompp).
+     * \param[in]     biasInitFilename   Name of file to read PMF and target from.
+     * \param[in,out] pull_work          Pointer to a pull struct which AWH will
+     * couple to, has to be initialized, is assumed not to change during the
+     * lifetime of the Awh object.
+     * \param[in] numLambdaStates        The number of free energy lambda states.
+     * \param[in] lambdaState            The initial free energy lambda state of the system.
+     * \throws    InvalidInputError      If there is user input (or combinations thereof) that is not allowed.
      */
     Awh(FILE*                 fplog,
         const t_inputrec&     inputRecord,
@@ -129,7 +138,9 @@ public:
         const gmx_multisim_t* multiSimRecord,
         const AwhParams&      awhParams,
         const std::string&    biasInitFilename,
-        pull_t*               pull_work);
+        pull_t*               pull_work,
+        int                   numLambdaStates,
+        int                   lambdaState);
 
     ~Awh();
 
@@ -152,8 +163,20 @@ public:
      * since AWH needs the current coordinate values (the pull code checks
      * for this).
      *
-     * \param[in]     mdatoms          Atom properties.
-     * \param[in]     ePBC             Type of periodic boundary conditions.
+     * \param[in]     pbcType          Type of periodic boundary conditions.
+     * \param[in]     masses           Atoms masses.
+     * \param[in]     neighborLambdaEnergies An array containing the energy of the system
+     * in neighboring lambdas. The array is of length numLambdas+1, where numLambdas is
+     * the number of free energy lambda states. Element 0 in the array is the energy
+     * of the current state and elements 1..numLambdas contain the energy of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[in]     neighborLambdaDhdl     An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
      * \param[in]     box              Box vectors.
      * \param[in,out] forceWithVirial  Force and virial buffers, should cover at least the local atoms.
      * \param[in]     t                Time.
@@ -162,14 +185,16 @@ public:
      * \param[in,out] fplog            General output file, normally md.log, can be nullptr.
      * \returns the potential energy for the bias.
      */
-    real applyBiasForcesAndUpdateBias(int                   ePBC,
-                                      const t_mdatoms&      mdatoms,
-                                      const matrix          box,
-                                      gmx::ForceWithVirial* forceWithVirial,
-                                      double                t,
-                                      int64_t               step,
-                                      gmx_wallcycle*        wallcycle,
-                                      FILE*                 fplog);
+    real applyBiasForcesAndUpdateBias(PbcType                pbcType,
+                                      const real*            masses,
+                                      ArrayRef<const double> neighborLambdaEnergies,
+                                      ArrayRef<const double> neighborLambdaDhdl,
+                                      const matrix           box,
+                                      gmx::ForceWithVirial*  forceWithVirial,
+                                      double                 t,
+                                      int64_t                step,
+                                      gmx_wallcycle*         wallcycle,
+                                      FILE*                  fplog);
 
     /*! \brief
      * Update the AWH history in preparation for writing to checkpoint file.
@@ -229,6 +254,20 @@ public:
      */
     static void registerAwhWithPull(const AwhParams& awhParams, pull_t* pull_work);
 
+    /*! \brief Get the current free energy lambda state.
+     * \returns The value of lambdaState_.
+     */
+    int fepLambdaState() const { return fepLambdaState_; }
+
+    /*! \brief Returns if foreign energy differences are required during this step.
+     * \param[in]     step             The current MD step.
+     */
+    bool needForeignEnergyDifferences(int64_t step) const;
+
+    /*! \brief Returns true if AWH has a bias with a free energy lambda state dimension at all.
+     */
+    bool hasFepLambdaDimension() const;
+
 private:
     /*! \brief Returns whether we need to write output at the current step.
      *
@@ -244,6 +283,8 @@ private:
     const gmx_multisim_t* multiSimRecord_; /**< Handler for multi-simulations. */
     pull_t*               pull_;           /**< Pointer to the pull working data. */
     double                potentialOffset_; /**< The offset of the bias potential which changes due to bias updates. */
+    const int numFepLambdaStates_; /**< The number of free energy lambda states of the system. */
+    int       fepLambdaState_;     /**< The current free energy lambda state. */
 };
 
 /*! \brief Makes an Awh and prepares to use it if the user input
similarity index 83%
rename from src/gromacs/awh/bias.cpp
rename to src/gromacs/applied_forces/awh/bias.cpp
index 006dee4e87bb188d7eef2a8f0290ed1066dbe23a..5abd53402bb187a393970121244fb053d532b1ba 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -102,15 +102,17 @@ void Bias::doSkippedUpdatesForAllPoints()
     }
 }
 
-gmx::ArrayRef<const double> Bias::calcForceAndUpdateBias(const awh_dvec        coordValue,
-                                                         double*               awhPotential,
-                                                         double*               potentialJump,
-                                                         const t_commrec*      commRecord,
-                                                         const gmx_multisim_t* ms,
-                                                         double                t,
-                                                         int64_t               step,
-                                                         int64_t               seed,
-                                                         FILE*                 fplog)
+gmx::ArrayRef<const double> Bias::calcForceAndUpdateBias(const awh_dvec         coordValue,
+                                                         ArrayRef<const double> neighborLambdaEnergies,
+                                                         ArrayRef<const double> neighborLambdaDhdl,
+                                                         double*                awhPotential,
+                                                         double*                potentialJump,
+                                                         const t_commrec*       commRecord,
+                                                         const gmx_multisim_t*  ms,
+                                                         double                 t,
+                                                         int64_t                step,
+                                                         int64_t                seed,
+                                                         FILE*                  fplog)
 {
     if (step < 0)
     {
@@ -126,29 +128,35 @@ gmx::ArrayRef<const double> Bias::calcForceAndUpdateBias(const awh_dvec        c
      * the bias in the current neighborhood needs to be up-to-date
      * and the probablity weights need to be calculated.
      */
-    const bool sampleCoord   = params_.isSampleCoordStep(step);
-    const bool moveUmbrella  = (sampleCoord || step == 0);
-    double     convolvedBias = 0;
-    if (params_.convolveForce || moveUmbrella || sampleCoord)
+    const bool        isSampleCoordStep = params_.isSampleCoordStep(step);
+    const bool        moveUmbrella      = (isSampleCoordStep || step == 0);
+    double            convolvedBias     = 0;
+    const CoordState& coordState        = state_.coordState();
+
+    if (params_.convolveForce || moveUmbrella || isSampleCoordStep)
     {
         if (params_.skipUpdates())
         {
             state_.doSkippedUpdatesInNeighborhood(params_, grid_);
         }
+        convolvedBias = state_.updateProbabilityWeightsAndConvolvedBias(
+                dimParams_, grid_, moveUmbrella ? neighborLambdaEnergies : ArrayRef<const double>{},
+                &probWeightNeighbor);
 
-        convolvedBias =
-                state_.updateProbabilityWeightsAndConvolvedBias(dimParams_, grid_, &probWeightNeighbor);
-
-        if (sampleCoord)
+        if (isSampleCoordStep)
         {
-            updateForceCorrelationGrid(probWeightNeighbor, t);
+            updateForceCorrelationGrid(probWeightNeighbor, neighborLambdaDhdl, t);
 
-            state_.sampleCoordAndPmf(grid_, probWeightNeighbor, convolvedBias);
+            state_.sampleCoordAndPmf(dimParams_, grid_, probWeightNeighbor, convolvedBias);
+        }
+        /* Set the umbrella grid point (for the lambda axis) to the
+         * current grid point. */
+        if (params_.convolveForce && grid_.hasLambdaAxis())
+        {
+            state_.setUmbrellaGridpointToGridpoint();
         }
     }
 
-    const CoordState& coordState = state_.coordState();
-
     /* Set the bias force and get the potential contribution from this bias.
      * The potential jump occurs at different times depending on how
      * the force is applied (and how the potential is normalized).
@@ -159,7 +167,9 @@ gmx::ArrayRef<const double> Bias::calcForceAndUpdateBias(const awh_dvec        c
     double potential;
     if (params_.convolveForce)
     {
-        state_.calcConvolvedForce(dimParams_, grid_, probWeightNeighbor, tempForce_, biasForce_);
+        state_.calcConvolvedForce(dimParams_, grid_, probWeightNeighbor,
+                                  moveUmbrella ? neighborLambdaDhdl : ArrayRef<const double>{},
+                                  tempForce_, biasForce_);
 
         potential = -convolvedBias * params_.invBeta;
     }
@@ -170,7 +180,8 @@ gmx::ArrayRef<const double> Bias::calcForceAndUpdateBias(const awh_dvec        c
                            "AWH bias grid point for the umbrella reference value is outside of the "
                            "target region.");
         potential = state_.calcUmbrellaForceAndPotential(
-                dimParams_, grid_, coordState.umbrellaGridpoint(), biasForce_);
+                dimParams_, grid_, coordState.umbrellaGridpoint(),
+                moveUmbrella ? neighborLambdaDhdl : ArrayRef<const double>{}, biasForce_);
 
         /* Moving the umbrella results in a force correction and
          * a new potential. The umbrella center is sampled as often as
@@ -179,9 +190,11 @@ gmx::ArrayRef<const double> Bias::calcForceAndUpdateBias(const awh_dvec        c
          */
         if (moveUmbrella)
         {
-            double newPotential = state_.moveUmbrella(dimParams_, grid_, probWeightNeighbor,
-                                                      biasForce_, step, seed, params_.biasIndex);
-            *potentialJump      = newPotential - potential;
+            const bool onlySampleUmbrellaGridpoint = false;
+            double     newPotential = state_.moveUmbrella(dimParams_, grid_, probWeightNeighbor,
+                                                      neighborLambdaDhdl, biasForce_, step, seed,
+                                                      params_.biasIndex, onlySampleUmbrellaGridpoint);
+            *potentialJump          = newPotential - potential;
         }
     }
 
@@ -198,6 +211,14 @@ gmx::ArrayRef<const double> Bias::calcForceAndUpdateBias(const awh_dvec        c
             *potentialJump      = newPotential - potential;
         }
     }
+    /* If there is a lambda axis it is still controlled using an umbrella even if the force
+     * is convolved in the other dimensions. */
+    if (moveUmbrella && params_.convolveForce && grid_.hasLambdaAxis())
+    {
+        const bool onlySampleUmbrellaGridpoint = true;
+        state_.moveUmbrella(dimParams_, grid_, probWeightNeighbor, neighborLambdaDhdl, biasForce_,
+                            step, seed, params_.biasIndex, onlySampleUmbrellaGridpoint);
+    }
 
     /* Return the potential. */
     *awhPotential = potential;
@@ -383,7 +404,9 @@ void Bias::printInitializationToLog(FILE* fplog) const
     }
 }
 
-void Bias::updateForceCorrelationGrid(gmx::ArrayRef<const double> probWeightNeighbor, double t)
+void Bias::updateForceCorrelationGrid(gmx::ArrayRef<const double> probWeightNeighbor,
+                                      ArrayRef<const double>      neighborLambdaDhdl,
+                                      double                      t)
 {
     if (forceCorrelationGrid_ == nullptr)
     {
@@ -403,7 +426,8 @@ void Bias::updateForceCorrelationGrid(gmx::ArrayRef<const double> probWeightNeig
            We actually add the force normalized by beta which has the units of 1/length. This means that the
            resulting correlation time integral is directly in units of friction time/length^2 which is really what
            we're interested in. */
-        state_.calcUmbrellaForceAndPotential(dimParams_, grid_, indexNeighbor, forceFromNeighbor);
+        state_.calcUmbrellaForceAndPotential(dimParams_, grid_, indexNeighbor, neighborLambdaDhdl,
+                                             forceFromNeighbor);
 
         /* Note: we might want to give a whole list of data to add instead and have this loop in the data adding function */
         forceCorrelationGrid_->addData(indexNeighbor, weightNeighbor, forceFromNeighbor, t);
@@ -426,4 +450,10 @@ int Bias::writeToEnergySubblocks(t_enxsubblock* subblock) const
     return writer_->writeToEnergySubblocks(*this, subblock);
 }
 
+bool Bias::isSampleCoordStep(const int64_t step) const
+{
+    return params_.isSampleCoordStep(step);
+}
+
+
 } // namespace gmx
similarity index 83%
rename from src/gromacs/awh/bias.h
rename to src/gromacs/applied_forces/awh/bias.h
index 65943d56d76f110fad7318443397cfa84027ce30..895bfe456aef5d1950291e564b5b1e15024c38ce 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/gmxassert.h"
 
+#include "biasgrid.h"
 #include "biasparams.h"
 #include "biasstate.h"
 #include "biaswriter.h"
 #include "dimparams.h"
-#include "grid.h"
 
 struct gmx_multisim_t;
 struct t_commrec;
@@ -78,7 +78,7 @@ struct AwhHistory;
 struct AwhParams;
 struct AwhPointStateHistory;
 class CorrelationGrid;
-class Grid;
+class BiasGrid;
 class GridAxis;
 class PointState;
 
@@ -159,7 +159,8 @@ public:
      * \param[in] mdTimeStep             The MD time step.
      * \param[in] numSharingSimulations  The number of simulations to share the bias across.
      * \param[in] biasInitFilename       Name of file to read PMF and target from.
-     * \param[in] thisRankWillDoIO       Tells whether this MPI rank will do I/O (checkpointing, AWH output), normally (only) the master rank does I/O.
+     * \param[in] thisRankWillDoIO       Tells whether this MPI rank will do I/O (checkpointing, AWH output),
+     * normally (only) the master rank does I/O.
      * \param[in] disableUpdateSkips     If to disable update skips, useful for testing.
      */
     Bias(int                            biasIndexInCollection,
@@ -192,6 +193,18 @@ public:
      * - reweight samples to extract the PMF.
      *
      * \param[in]     coordValue     The current coordinate value(s).
+     * \param[in]     neighborLambdaEnergies An array containing the energy of the system
+     * in neighboring lambdas. The array is of length numLambdas+1, where numLambdas is
+     * the number of free energy lambda states. Element 0 in the array is the energy
+     * of the current state and elements 1..numLambdas contain the energy of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[in]     neighborLambdaDhdl     An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
      * \param[out]    awhPotential   Bias potential.
      * \param[out]    potentialJump  Change in bias potential for this bias.
      * \param[in]     commRecord     Struct for intra-simulation communication.
@@ -202,15 +215,17 @@ public:
      * \param[in,out] fplog          Log file.
      * \returns a reference to the bias force, size \ref ndim(), valid until the next call of this method or destruction of Bias, whichever comes first.
      */
-    gmx::ArrayRef<const double> calcForceAndUpdateBias(const awh_dvec        coordValue,
-                                                       double*               awhPotential,
-                                                       double*               potentialJump,
-                                                       const t_commrec*      commRecord,
-                                                       const gmx_multisim_t* ms,
-                                                       double                t,
-                                                       int64_t               step,
-                                                       int64_t               seed,
-                                                       FILE*                 fplog);
+    gmx::ArrayRef<const double> calcForceAndUpdateBias(const awh_dvec         coordValue,
+                                                       ArrayRef<const double> neighborLambdaEnergies,
+                                                       ArrayRef<const double> neighborLambdaDhdl,
+                                                       double*                awhPotential,
+                                                       double*                potentialJump,
+                                                       const t_commrec*       commRecord,
+                                                       const gmx_multisim_t*  ms,
+                                                       double                 t,
+                                                       int64_t                step,
+                                                       int64_t                seed,
+                                                       FILE*                  fplog);
 
     /*! \brief
      * Calculates the convolved bias for a given coordinate value.
@@ -301,9 +316,17 @@ private:
      * Collect samples for the force correlation analysis on the grid.
      *
      * \param[in] probWeightNeighbor  Probability weight of the neighboring points.
+     * \param[in] neighborLambdaDhdl  An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
      * \param[in] t                   The time.
      */
-    void updateForceCorrelationGrid(gmx::ArrayRef<const double> probWeightNeighbor, double t);
+    void updateForceCorrelationGrid(gmx::ArrayRef<const double> probWeightNeighbor,
+                                    ArrayRef<const double>      neighborLambdaDhdl,
+                                    double                      t);
 
 public:
     /*! \brief Return a const reference to the force correlation grid.
@@ -328,10 +351,25 @@ public:
      */
     int writeToEnergySubblocks(t_enxsubblock* subblock) const;
 
+    /*! \brief Returns true if the bias has a free energy lambda state dimension at all.
+     */
+    bool hasFepLambdaDimension() const
+    {
+        return std::any_of(std::begin(dimParams_), std::end(dimParams_),
+                           [](const auto& dimParam) { return dimParam.isFepLambdaDimension(); });
+    }
+
+    /*! \brief
+     * Returns whether we should sample the coordinate.
+     *
+     * \param[in] step  The MD step number.
+     */
+    bool isSampleCoordStep(int64_t step) const;
+
     /* Data members. */
 private:
     const std::vector<DimParams> dimParams_; /**< Parameters for each dimension. */
-    const Grid grid_; /**< The multidimensional grid organizing the coordinate point locations. */
+    const BiasGrid grid_; /**< The multidimensional grid organizing the coordinate point locations. */
 
     const BiasParams params_; /**< Constant parameters for the method. */
 
similarity index 78%
rename from src/gromacs/awh/grid.cpp
rename to src/gromacs/applied_forces/awh/biasgrid.cpp
index 7e73cb812024e1b601c9ccb41fcd58015401b225..0db229cd865976a08fee9c177a3b168c057f7053 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gmxpre.h"
 
-#include "grid.h"
+#include "biasgrid.h"
 
 #include <cassert>
 #include <cmath>
 #include <cstring>
 
 #include <algorithm>
+#include <optional>
 
 #include "gromacs/math/functions.h"
 #include "gromacs/math/utilities.h"
@@ -187,13 +188,76 @@ double getDeviationPeriodic(double x, double x0, double period)
 
 } // namespace
 
-double getDeviationFromPointAlongGridAxis(const Grid& grid, int dimIndex, int pointIndex, double value)
+double getDeviationFromPointAlongGridAxis(const BiasGrid& grid, int dimIndex, int pointIndex, double value)
 {
     double coordValue = grid.point(pointIndex).coordValue[dimIndex];
 
     return getDeviationPeriodic(value, coordValue, grid.axis(dimIndex).period());
 }
 
+double getDeviationFromPointAlongGridAxis(const BiasGrid& grid, int dimIndex, int pointIndex1, int pointIndex2)
+{
+    double coordValue1 = grid.point(pointIndex1).coordValue[dimIndex];
+    double coordValue2 = grid.point(pointIndex2).coordValue[dimIndex];
+
+    return getDeviationPeriodic(coordValue1, coordValue2, grid.axis(dimIndex).period());
+}
+
+bool pointsAlongLambdaAxis(const BiasGrid& grid, int pointIndex1, int pointIndex2)
+{
+    if (!grid.hasLambdaAxis())
+    {
+        return false;
+    }
+    if (pointIndex1 == pointIndex2)
+    {
+        return true;
+    }
+    const int numDimensions = grid.numDimensions();
+    for (int d = 0; d < numDimensions; d++)
+    {
+        if (grid.axis(d).isFepLambdaAxis())
+        {
+            if (getDeviationFromPointAlongGridAxis(grid, d, pointIndex1, pointIndex2) == 0)
+            {
+                return false;
+            }
+        }
+        else
+        {
+            if (getDeviationFromPointAlongGridAxis(grid, d, pointIndex1, pointIndex2) != 0)
+            {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool pointsHaveDifferentLambda(const BiasGrid& grid, int pointIndex1, int pointIndex2)
+{
+    if (!grid.hasLambdaAxis())
+    {
+        return false;
+    }
+    if (pointIndex1 == pointIndex2)
+    {
+        return false;
+    }
+    const int numDimensions = grid.numDimensions();
+    for (int d = 0; d < numDimensions; d++)
+    {
+        if (grid.axis(d).isFepLambdaAxis())
+        {
+            if (getDeviationFromPointAlongGridAxis(grid, d, pointIndex1, pointIndex2) != 0)
+            {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 void linearArrayIndexToMultiDim(int indexLinear, int numDimensions, const awh_ivec numPointsDim, awh_ivec indexMulti)
 {
     for (int d = 0; d < numDimensions; d++)
@@ -210,7 +274,7 @@ void linearArrayIndexToMultiDim(int indexLinear, int numDimensions, const awh_iv
     }
 }
 
-void linearGridindexToMultiDim(const Grid& grid, int indexLinear, awh_ivec indexMulti)
+void linearGridindexToMultiDim(const BiasGrid& grid, int indexLinear, awh_ivec indexMulti)
 {
     awh_ivec  numPointsDim;
     const int numDimensions = grid.numDimensions();
@@ -259,7 +323,7 @@ int multiDimGridIndexToLinear(const std::vector<GridAxis>& axis, const awh_ivec
 
 } // namespace
 
-int multiDimGridIndexToLinear(const Grid& grid, const awh_ivec indexMulti)
+int multiDimGridIndexToLinear(const BiasGrid& grid, const awh_ivec indexMulti)
 {
     return multiDimGridIndexToLinear(grid.axis(), indexMulti);
 }
@@ -323,14 +387,14 @@ bool stepInMultiDimArray(int numDim, const awh_ivec numPoints, awh_ivec indexDim
  * \param[in]     grid            The grid.
  * \param[in]     subgridOrigin   Vector locating the subgrid origin relative to the grid origin.
  * \param[in]     subgridNpoints  The number of subgrid points in each dimension.
- * \param[in]     point           Grid point to get subgrid index for.
+ * \param[in]     point           BiasGrid point to get subgrid index for.
  * \param[in,out] subgridIndex    Subgrid multidimensional index.
  */
-void gridToSubgridIndex(const Grid&    grid,
-                        const awh_ivec subgridOrigin,
-                        const awh_ivec subgridNpoints,
-                        int            point,
-                        awh_ivec       subgridIndex)
+void gridToSubgridIndex(const BiasGrid& grid,
+                        const awh_ivec  subgridOrigin,
+                        const awh_ivec  subgridNpoints,
+                        int             point,
+                        awh_ivec        subgridIndex)
 {
     /* Get the subgrid index of the given grid point, for each dimension. */
     for (int d = 0; d < grid.numDimensions(); d++)
@@ -356,10 +420,10 @@ void gridToSubgridIndex(const Grid&    grid,
  * \param[in]     grid           The grid.
  * \param[in]     subgridOrigin  Vector locating the subgrid origin relative to the grid origin.
  * \param[in]     subgridIndex   Subgrid multidimensional index to get grid point index for.
- * \param[in,out] gridIndex      Grid point index.
+ * \param[in,out] gridIndex      BiasGrid point index.
  * \returns true if the transformation was successful.
  */
-bool subgridToGridIndex(const Grid& grid, const awh_ivec subgridOrigin, const awh_ivec subgridIndex, int* gridIndex)
+bool subgridToGridIndex(const BiasGrid& grid, const awh_ivec subgridOrigin, const awh_ivec subgridIndex, int* gridIndex)
 {
     awh_ivec globalIndexDim;
 
@@ -417,10 +481,10 @@ bool subgridToGridIndex(const Grid& grid, const awh_ivec subgridOrigin, const aw
 
 } // namespace
 
-bool advancePointInSubgrid(const Grid&    grid,
-                           const awh_ivec subgridOrigin,
-                           const awh_ivec subgridNumPoints,
-                           int*           gridPointIndex)
+bool advancePointInSubgrid(const BiasGrid& grid,
+                           const awh_ivec  subgridOrigin,
+                           const awh_ivec  subgridNumPoints,
+                           int*            gridPointIndex)
 {
     /* Initialize the subgrid index to the subgrid origin. */
     awh_ivec subgridIndex = { 0 };
@@ -461,7 +525,7 @@ bool advancePointInSubgrid(const Grid&    grid,
  * number of points in the axis. For a periodic axis, the distance is chosen
  * to be in [0, period), i.e. always positive but not the shortest one.
  *
- * \param[in]  axis   Grid axis.
+ * \param[in]  axis   BiasGrid axis.
  * \param[in]  x      From value.
  * \param[in]  x0     To value.
  * \returns (x - x0) in number of points.
@@ -510,11 +574,35 @@ static bool valueIsInGrid(const awh_dvec value, const std::vector<GridAxis>& axi
     return true;
 }
 
-bool Grid::covers(const awh_dvec value) const
+bool BiasGrid::covers(const awh_dvec value) const
 {
     return valueIsInGrid(value, axis());
 }
 
+std::optional<int> BiasGrid::lambdaAxisIndex() const
+{
+    for (size_t i = 0; i < axis_.size(); i++)
+    {
+        if (axis_[i].isFepLambdaAxis())
+        {
+            return i;
+        }
+    }
+    return {};
+}
+
+int BiasGrid::numFepLambdaStates() const
+{
+    for (size_t i = 0; i < axis_.size(); i++)
+    {
+        if (axis_[i].isFepLambdaAxis())
+        {
+            return axis_[i].numPoints();
+        }
+    }
+    return 0;
+}
+
 int GridAxis::nearestIndex(double value) const
 {
     /* Get the point distance to the origin. This may by an out of index range for the axis. */
@@ -559,7 +647,7 @@ static int getNearestIndexInGrid(const awh_dvec value, const std::vector<GridAxi
     return multiDimGridIndexToLinear(axis, indexMulti);
 }
 
-int Grid::nearestIndex(const awh_dvec value) const
+int BiasGrid::nearestIndex(const awh_dvec value) const
 {
     return getNearestIndexInGrid(value, axis());
 }
@@ -573,25 +661,34 @@ namespace
  * The search space for neighbors is a subgrid with size set by a scope cutoff.
  * In general not all point within scope will be valid grid points.
  *
- * \param[in]     pointIndex           Grid point index.
+ * \param[in]     pointIndex           BiasGrid point index.
  * \param[in]     grid                 The grid.
  * \param[in,out] neighborIndexArray   Array to fill with neighbor indices.
  */
-void setNeighborsOfGridPoint(int pointIndex, const Grid& grid, std::vector<int>* neighborIndexArray)
+void setNeighborsOfGridPoint(int pointIndex, const BiasGrid& grid, std::vector<int>* neighborIndexArray)
 {
     const int c_maxNeighborsAlongAxis =
-            1 + 2 * static_cast<int>(Grid::c_numPointsPerSigma * Grid::c_scopeCutoff);
+            1 + 2 * static_cast<int>(BiasGrid::c_numPointsPerSigma * BiasGrid::c_scopeCutoff);
 
     awh_ivec numCandidates = { 0 };
     awh_ivec subgridOrigin = { 0 };
     for (int d = 0; d < grid.numDimensions(); d++)
     {
-        /* The number of candidate points along this dimension is given by the scope cutoff. */
-        numCandidates[d] = std::min(c_maxNeighborsAlongAxis, grid.axis(d).numPoints());
+        if (grid.axis(d).isFepLambdaAxis())
+        {
+            /* Use all points along an axis linked to FEP */
+            numCandidates[d] = grid.axis(d).numPoints();
+            subgridOrigin[d] = 0;
+        }
+        else
+        {
+            /* The number of candidate points along this dimension is given by the scope cutoff. */
+            numCandidates[d] = std::min(c_maxNeighborsAlongAxis, grid.axis(d).numPoints());
 
-        /* The origin of the subgrid to search */
-        int centerIndex  = grid.point(pointIndex).index[d];
-        subgridOrigin[d] = centerIndex - numCandidates[d] / 2;
+            /* The origin of the subgrid to search */
+            int centerIndex  = grid.point(pointIndex).index[d];
+            subgridOrigin[d] = centerIndex - numCandidates[d] / 2;
+        }
     }
 
     /* Find and set the neighbors */
@@ -613,7 +710,7 @@ void setNeighborsOfGridPoint(int pointIndex, const Grid& grid, std::vector<int>*
 
 } // namespace
 
-void Grid::initPoints()
+void BiasGrid::initPoints()
 {
     awh_ivec numPointsDimWork = { 0 };
     awh_ivec indexWork        = { 0 };
@@ -628,7 +725,14 @@ void Grid::initPoints()
     {
         for (size_t d = 0; d < axis_.size(); d++)
         {
-            point.coordValue[d] = axis_[d].origin() + indexWork[d] * axis_[d].spacing();
+            if (axis_[d].isFepLambdaAxis())
+            {
+                point.coordValue[d] = indexWork[d];
+            }
+            else
+            {
+                point.coordValue[d] = axis_[d].origin() + indexWork[d] * axis_[d].spacing();
+            }
 
             if (axis_[d].period() > 0)
             {
@@ -645,7 +749,8 @@ void Grid::initPoints()
 
 GridAxis::GridAxis(double origin, double end, double period, double pointDensity) :
     origin_(origin),
-    period_(period)
+    period_(period),
+    isFepLambdaAxis_(false)
 {
     length_ = getIntervalLengthPeriodic(origin_, end, period_);
 
@@ -686,17 +791,27 @@ GridAxis::GridAxis(double origin, double end, double period, double pointDensity
     }
 }
 
-GridAxis::GridAxis(double origin, double end, double period, int numPoints) :
+GridAxis::GridAxis(double origin, double end, double period, int numPoints, bool isFepLambdaAxis) :
     origin_(origin),
     period_(period),
-    numPoints_(numPoints)
+    numPoints_(numPoints),
+    isFepLambdaAxis_(isFepLambdaAxis)
 {
-    length_            = getIntervalLengthPeriodic(origin_, end, period_);
-    spacing_           = numPoints_ > 1 ? length_ / (numPoints_ - 1) : period_;
-    numPointsInPeriod_ = static_cast<int>(std::round(period_ / spacing_));
+    if (isFepLambdaAxis)
+    {
+        length_            = end - origin_;
+        spacing_           = 1;
+        numPointsInPeriod_ = numPoints;
+    }
+    else
+    {
+        length_            = getIntervalLengthPeriodic(origin_, end, period_);
+        spacing_           = numPoints_ > 1 ? length_ / (numPoints_ - 1) : period_;
+        numPointsInPeriod_ = static_cast<int>(std::round(period_ / spacing_));
+    }
 }
 
-Grid::Grid(const std::vector<DimParams>& dimParams, const AwhDimParams* awhDimParams)
+BiasGrid::BiasGrid(const std::vector<DimParams>& dimParams, const AwhDimParams* awhDimParams)
 {
     /* Define the discretization along each dimension */
     awh_dvec period;
@@ -705,12 +820,20 @@ Grid::Grid(const std::vector<DimParams>& dimParams, const AwhDimParams* awhDimPa
     {
         double origin = dimParams[d].scaleUserInputToInternal(awhDimParams[d].origin);
         double end    = dimParams[d].scaleUserInputToInternal(awhDimParams[d].end);
-        period[d]     = dimParams[d].scaleUserInputToInternal(awhDimParams[d].period);
-        static_assert(c_numPointsPerSigma >= 1.0,
-                      "The number of points per sigma should be at least 1.0 to get a uniformly "
-                      "covering the reaction using Gaussians");
-        double pointDensity = std::sqrt(dimParams[d].betak) * c_numPointsPerSigma;
-        axis_.emplace_back(origin, end, period[d], pointDensity);
+        if (awhDimParams[d].eCoordProvider == eawhcoordproviderPULL)
+        {
+            period[d] = dimParams[d].scaleUserInputToInternal(awhDimParams[d].period);
+            static_assert(
+                    c_numPointsPerSigma >= 1.0,
+                    "The number of points per sigma should be at least 1.0 to get a uniformly "
+                    "covering the reaction using Gaussians");
+            double pointDensity = std::sqrt(dimParams[d].pullDimParams().betak) * c_numPointsPerSigma;
+            axis_.emplace_back(origin, end, period[d], pointDensity);
+        }
+        else
+        {
+            axis_.emplace_back(origin, end, 0, dimParams[d].fepDimParams().numFepLambdaStates, true);
+        }
         numPoints *= axis_[d].numPoints();
     }
 
@@ -735,7 +858,7 @@ void mapGridToDataGrid(std::vector<int>*    gridpointToDatapoint,
                        const double* const* data,
                        int                  numDataPoints,
                        const std::string&   dataFilename,
-                       const Grid&          grid,
+                       const BiasGrid&      grid,
                        const std::string&   correctFormatMessage)
 {
     /* Transform the data into a grid in order to map each grid point to a data point
@@ -743,9 +866,10 @@ void mapGridToDataGrid(std::vector<int>*    gridpointToDatapoint,
 
     /* Count the number of points for each dimension. Each dimension
        has its own stride. */
-    int              stride           = 1;
-    int              numPointsCounted = 0;
-    std::vector<int> numPoints(grid.numDimensions());
+    int               stride           = 1;
+    int               numPointsCounted = 0;
+    std::vector<int>  numPoints(grid.numDimensions());
+    std::vector<bool> isFepLambdaAxis(grid.numDimensions());
     for (int d = grid.numDimensions() - 1; d >= 0; d--)
     {
         int    numPointsInDim = 0;
@@ -764,7 +888,8 @@ void mapGridToDataGrid(std::vector<int>*    gridpointToDatapoint,
 
         numPointsCounted = (numPointsCounted == 0) ? numPointsInDim : numPointsCounted * numPointsInDim;
 
-        numPoints[d] = numPointsInDim;
+        numPoints[d]       = numPointsInDim;
+        isFepLambdaAxis[d] = grid.axis(d).isFepLambdaAxis();
     }
 
     if (numPointsCounted != numDataPoints)
@@ -781,7 +906,15 @@ void mapGridToDataGrid(std::vector<int>*    gridpointToDatapoint,
     /* The data grid has the data that was read and the properties of the AWH grid */
     for (int d = 0; d < grid.numDimensions(); d++)
     {
-        axis_.emplace_back(data[d][0], data[d][numDataPoints - 1], grid.axis(d).period(), numPoints[d]);
+        if (isFepLambdaAxis[d])
+        {
+            axis_.emplace_back(data[d][0], data[d][numDataPoints - 1], 0, numPoints[d], true);
+        }
+        else
+        {
+            axis_.emplace_back(data[d][0], data[d][numDataPoints - 1], grid.axis(d).period(),
+                               numPoints[d], false);
+        }
     }
 
     /* Map each grid point to a data point. No interpolation, just pick the nearest one.
similarity index 77%
rename from src/gromacs/awh/grid.h
rename to src/gromacs/applied_forces/awh/biasgrid.h
index 70e58d18cdc9600b3e20db3d0e465a0b10668f66..9fd994daa7d23c5d1416ffa9663a4378ffbc161f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * \ingroup module_awh
  */
 
-#ifndef GMX_AWH_GRID_H
-#define GMX_AWH_GRID_H
+#ifndef GMX_AWH_BIASGRID_H
+#define GMX_AWH_BIASGRID_H
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "dimparams.h" /* This is needed for awh_dvec */
@@ -90,10 +91,11 @@ public:
      * \param[in] end              End value.
      * \param[in] period           Period, pass 0 if not periodic.
      * \param[in] numPoints        The number of points.
+     * \param[in] isFepLambdaAxis     If this axis is controlling lambda.
      */
-    GridAxis(double origin, double end, double period, int numPoints);
+    GridAxis(double origin, double end, double period, int numPoints, bool isFepLambdaAxis);
 
-    /*! \brief Returns if the axis has periodic boundaries.
+    /*! \brief Returns whether the axis has periodic boundaries.
      */
     bool isPeriodic() const { return period_ > 0; }
 
@@ -135,6 +137,10 @@ public:
      */
     int nearestIndex(double value) const;
 
+    /*! \brief Returns whether this axis is coupled to the free energy lambda state.
+     */
+    bool isFepLambdaAxis() const { return isFepLambdaAxis_; }
+
 private:
     double origin_;            /**< Interval start value */
     double length_;            /**< Interval length */
@@ -142,6 +148,7 @@ private:
     double spacing_;           /**< Point spacing */
     int    numPoints_;         /**< Number of points in the interval */
     int    numPointsInPeriod_; /**< Number of points in a period (0 if no periodicity) */
+    bool isFepLambdaAxis_; /**< If this axis is coupled to the system's free energy lambda state */
 };
 
 /*! \internal
@@ -159,13 +166,13 @@ struct GridPoint
 };
 
 /*! \internal
- * \brief The grid, generally multidimensional and periodic.
+ * \brief The grid for a single bias, generally multidimensional and periodic.
  *
  * The grid discretizes a multidimensional space with some given resolution.
  * Each dimension is represented by an axis which sets the spatial extent,
  * point spacing and periodicity of the grid in that direction.
  */
-class Grid
+class BiasGrid
 {
 private:
     /*! \brief Initializes the grid points.
@@ -186,10 +193,11 @@ public:
 
     /*! \brief Construct a grid using AWH input parameters.
      *
-     * \param[in] dimParams     Dimension parameters including the expected inverse variance of the coordinate living on the grid (determines the grid spacing).
+     * \param[in] dimParams     Dimension parameters including the expected inverse variance of the
+     * coordinate living on the grid (determines the grid spacing).
      * \param[in] awhDimParams  Dimension params from inputrec.
      */
-    Grid(const std::vector<DimParams>& dimParams, const AwhDimParams* awhDimParams);
+    BiasGrid(const std::vector<DimParams>& dimParams, const AwhDimParams* awhDimParams);
 
     /*! \brief Returns the number of points in the grid.
      *
@@ -237,6 +245,27 @@ public:
      */
     bool covers(const awh_dvec value) const;
 
+    /*! \brief Returns true if the grid has a free energy lambda state axis at all.
+     */
+    bool hasLambdaAxis() const
+    {
+        return std::any_of(std::begin(axis_), std::end(axis_),
+                           [](const auto& axis) { return axis.isFepLambdaAxis(); });
+    }
+
+    /*! \brief
+     * Returns the index of a free energy lambda state axis (there can be
+     * no more than one) if there is one.
+     */
+    std::optional<int> lambdaAxisIndex() const;
+
+    /*! \brief
+     * Returns the number of free energy lambda states in the grid (the number
+     * of points along a free energy lambda state axis) or 0 if there are no free energy
+     * lambda state axes.
+     */
+    int numFepLambdaStates() const;
+
 private:
     std::vector<GridPoint> point_; /**< Points on the grid */
     std::vector<GridAxis>  axis_;  /**< Axes, one for each dimension. */
@@ -250,7 +279,7 @@ private:
  * \param[in] indexMulti  Multidimensional grid point index to convert to a linear one.
  * \returns the linear index.
  */
-int multiDimGridIndexToLinear(const Grid& grid, const awh_ivec indexMulti);
+int multiDimGridIndexToLinear(const BiasGrid& grid, const awh_ivec indexMulti);
 
 /*! \brief Convert multidimensional array index to a linear one.
  *
@@ -268,7 +297,7 @@ int multiDimArrayIndexToLinear(const awh_ivec indexMulti, int numDim, const awh_
  * \param[in]  indexLinear  Linear grid point index to convert to a multidimensional one.
  * \param[out] indexMulti   The multidimensional index.
  */
-void linearGridindexToMultiDim(const Grid& grid, int indexLinear, awh_ivec indexMulti);
+void linearGridindexToMultiDim(const BiasGrid& grid, int indexLinear, awh_ivec indexMulti);
 
 /*! \brief Convert a linear array index to a multidimensional one.
  *
@@ -298,10 +327,10 @@ void linearArrayIndexToMultiDim(int indexLinear, int ndim, const awh_ivec numPoi
  * \param[in,out] gridPointIndex  Pointer to the starting/next grid point index.
  * \returns true if the grid point was updated.
  */
-bool advancePointInSubgrid(const Grid&    grid,
-                           const awh_ivec subgridOrigin,
-                           const awh_ivec subgridNpoints,
-                           int*           gridPointIndex);
+bool advancePointInSubgrid(const BiasGrid& grid,
+                           const awh_ivec  subgridOrigin,
+                           const awh_ivec  subgridNpoints,
+                           int*            gridPointIndex);
 
 /*! \brief Maps each point in the grid to a point in the data grid.
  *
@@ -321,7 +350,7 @@ void mapGridToDataGrid(std::vector<int>*    gridpointToDatapoint,
                        const double* const* data,
                        int                  numDataPoints,
                        const std::string&   dataFilename,
-                       const Grid&          grid,
+                       const BiasGrid&      grid,
                        const std::string&   correctFormatMessage);
 
 /*! \brief
@@ -329,11 +358,43 @@ void mapGridToDataGrid(std::vector<int>*    gridpointToDatapoint,
  *
  * \param[in] grid        The grid.
  * \param[in] dimIndex    Dimensional index in [0, ndim -1].
- * \param[in] pointIndex  Grid point index.
+ * \param[in] pointIndex  BiasGrid point index.
  * \param[in] value       Value along the given dimension.
  * \returns the deviation of the given value to the given point.
  */
-double getDeviationFromPointAlongGridAxis(const Grid& grid, int dimIndex, int pointIndex, double value);
+double getDeviationFromPointAlongGridAxis(const BiasGrid& grid, int dimIndex, int pointIndex, double value);
+
+/*! \brief
+ * Get the deviation from one point to another along one dimension in the grid.
+ *
+ * \param[in] grid        The grid.
+ * \param[in] dimIndex    Dimensional index in [0, ndim -1].
+ * \param[in] pointIndex1 Grid point index of the first point.
+ * \param[in] pointIndex2 Grid point index of the second point.
+ * \returns the deviation of the two points along the given axis.
+ */
+double getDeviationFromPointAlongGridAxis(const BiasGrid& grid, int dimIndex, int pointIndex1, int pointIndex2);
+
+/*! \brief
+ * Checks whether two points are along a free energy lambda state axis.
+ *
+ * \param[in] grid        The grid.
+ * \param[in] pointIndex1 Grid point index of the first point.
+ * \param[in] pointIndex2 Grid point index of the second point.
+ * \returns true if the two points are along a free energy lambda state axis.
+ */
+bool pointsAlongLambdaAxis(const BiasGrid& grid, int pointIndex1, int pointIndex2);
+
+/*! \brief
+ * Checks whether two points are different in the free energy lambda state
+ * dimension (if any).
+ *
+ * \param[in] grid        The grid.
+ * \param[in] pointIndex1 Grid point index of the first point.
+ * \param[in] pointIndex2 Grid point index of the second point.
+ * \returns true if the two points have different lambda values.
+ */
+bool pointsHaveDifferentLambda(const BiasGrid& grid, int pointIndex1, int pointIndex2);
 
 } // namespace gmx
 
similarity index 87%
rename from src/gromacs/awh/biasparams.cpp
rename to src/gromacs/applied_forces/awh/biasparams.cpp
index 6f9b0d387d5965661680db0af49b403ca69bc6a4..96f1b5e67cdbb72420e3d347d5c7c5e0082e0fe4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -56,7 +56,7 @@
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
 
-#include "grid.h"
+#include "biasgrid.h"
 
 namespace gmx
 {
@@ -112,7 +112,7 @@ int64_t calcTargetUpdateInterval(const AwhParams& awhParams, const AwhBiasParams
  *
  * \param[in] awhParams      AWH parameters.
  * \param[in] dimParams         Parameters for the dimensions of the coordinate.
- * \param[in] gridAxis          The Grid axes.
+ * \param[in] gridAxis          The BiasGrid axes.
  * \returns the check interval in steps.
  */
 int64_t calcCheckCoveringInterval(const AwhParams&              awhParams,
@@ -124,20 +124,28 @@ int64_t calcCheckCoveringInterval(const AwhParams&              awhParams,
     int minNumSamplesCover = 0;
     for (size_t d = 0; d < gridAxis.size(); d++)
     {
-        GMX_RELEASE_ASSERT(dimParams[d].betak > 0,
-                           "Inverse temperature (beta) and force constant (k) should be positive.");
-        double sigma = 1.0 / std::sqrt(dimParams[d].betak);
-
-        /* The additional sample is here because to cover a discretized
-           axis of length sigma one needs two samples, one for each
-           end point. */
-        GMX_RELEASE_ASSERT(gridAxis[d].length() / sigma < std::numeric_limits<int>::max(),
-                           "The axis length in units of sigma should fit in an int");
-        int numSamplesCover = static_cast<int>(std::ceil(gridAxis[d].length() / sigma)) + 1;
-
+        int numSamplesCover;
+        if (dimParams[d].isPullDimension())
+        {
+            GMX_RELEASE_ASSERT(
+                    dimParams[d].pullDimParams().betak > 0,
+                    "Inverse temperature (beta) and force constant (k) should be positive.");
+            double sigma = 1.0 / std::sqrt(dimParams[d].pullDimParams().betak);
+
+            /* The additional sample is here because to cover a discretized
+            axis of length sigma one needs two samples, one for each
+            end point. */
+            GMX_RELEASE_ASSERT(gridAxis[d].length() / sigma < std::numeric_limits<int>::max(),
+                               "The axis length in units of sigma should fit in an int");
+            numSamplesCover = static_cast<int>(std::ceil(gridAxis[d].length() / sigma)) + 1;
+        }
+        else
+        {
+            numSamplesCover = dimParams[d].fepDimParams().numFepLambdaStates;
+        }
         /* The minimum number of samples needed for simultaneously
-           covering all axes is limited by the axis requiring most
-           samples. */
+        covering all axes is limited by the axis requiring most
+        samples. */
         minNumSamplesCover = std::max(minNumSamplesCover, numSamplesCover);
     }
 
@@ -184,12 +192,13 @@ double gaussianGeometryFactor(gmx::ArrayRef<const double> xArray)
         zetaTable = zetaTable1d;
     }
     else if (xArray.size() == 2)
-    {
+    { // NOLINT bugprone-branch-clone
         zetaTable = zetaTable2d;
     }
     else
     {
-        /* TODO... but this is anyway a rough estimate and > 2 dimensions is not so popular. */
+        /* TODO... but this is anyway a rough estimate and > 2 dimensions is not so popular.
+         * Remove the above NOLINT when addressing this */
         zetaTable = zetaTable2d;
     }
 
@@ -238,7 +247,7 @@ double gaussianGeometryFactor(gmx::ArrayRef<const double> xArray)
  *
  * \param[in] dimParams         Parameters for the dimensions of the coordinate.
  * \param[in] awhBiasParams     Bias parameters.
- * \param[in] gridAxis          The Grid axes.
+ * \param[in] gridAxis          The BiasGrid axes.
  * \param[in] beta              1/(k_B T).
  * \param[in] samplingTimestep  Sampling frequency of probability weights.
  * \returns estimate of initial histogram size.
@@ -254,12 +263,18 @@ double getInitialHistogramSizeEstimate(const std::vector<DimParams>& dimParams,
     std::vector<double> x;
     for (size_t d = 0; d < gridAxis.size(); d++)
     {
-        double axisLength = gridAxis[d].length();
+        double axisLength = gridAxis[d].isFepLambdaAxis() ? 1.0 : gridAxis[d].length();
         if (axisLength > 0)
         {
             crossingTime += awhBiasParams.dimParams[d].diffusion / (axisLength * axisLength);
             /* The sigma of the Gaussian distribution in the umbrella */
-            double sigma = 1. / std::sqrt(dimParams[d].betak);
+            double sigma = 1.;
+            if (dimParams[d].isPullDimension())
+            {
+                GMX_RELEASE_ASSERT(dimParams[d].pullDimParams().betak != 0,
+                                   "beta*k cannot be zero");
+                sigma /= std::sqrt(dimParams[d].pullDimParams().betak);
+            }
             x.push_back(sigma / axisLength);
         }
     }
@@ -333,6 +348,8 @@ BiasParams::BiasParams(const AwhParams&              awhParams,
 
     for (int d = 0; d < awhBiasParams.ndim; d++)
     {
+        /* The spacing in FEP dimensions is 1. The calculated coverRadius will be in lambda states
+         * (cf points in other dimensions). */
         double coverRadiusInNm = 0.5 * awhBiasParams.dimParams[d].coverDiameter;
         double spacing         = gridAxis[d].spacing();
         coverRadius_[d] = spacing > 0 ? static_cast<int>(std::round(coverRadiusInNm / spacing)) : 0;
similarity index 99%
rename from src/gromacs/awh/biasparams.h
rename to src/gromacs/applied_forces/awh/biasparams.h
index 812963c27a508386a9cc4dd1713d223adcd4c0ac..6b24e2bd094444fcab17b68bb444b8ac262f88a7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 92%
rename from src/gromacs/awh/biassharing.cpp
rename to src/gromacs/applied_forces/awh/biassharing.cpp
index 52d5a89b4e9c0e516603dbd6cecd69f656bafab6..b377931fa04bf2c538f774a93ae990abddf43ee0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -83,7 +83,7 @@ void biasesAreCompatibleForSharingBetweenSimulations(const AwhParams&
                                                      const std::vector<size_t>& pointSize,
                                                      const gmx_multisim_t*      multiSimComm)
 {
-    const int numSim = multiSimComm->nsim;
+    const int numSim = multiSimComm->numSimulations_;
 
     /* We currently enforce subsequent shared biases to have consecutive
      * share-group values starting at 1. This means we can reduce shared
@@ -105,7 +105,7 @@ void biasesAreCompatibleForSharingBetweenSimulations(const AwhParams&
         }
     }
     std::vector<int> numShareAll(numSim);
-    numShareAll[multiSimComm->sim] = numShare;
+    numShareAll[multiSimComm->simulationIndex_] = numShare;
     gmx_sumi_sim(numShareAll.size(), numShareAll.data(), multiSimComm);
     for (int sim = 1; sim < numSim; sim++)
     {
@@ -117,8 +117,8 @@ void biasesAreCompatibleForSharingBetweenSimulations(const AwhParams&
     }
 
     std::vector<int> intervals(numSim * 2);
-    intervals[numSim * 0 + multiSimComm->sim] = awhParams.nstSampleCoord;
-    intervals[numSim * 1 + multiSimComm->sim] = awhParams.numSamplesUpdateFreeEnergy;
+    intervals[numSim * 0 + multiSimComm->simulationIndex_] = awhParams.nstSampleCoord;
+    intervals[numSim * 1 + multiSimComm->simulationIndex_] = awhParams.numSamplesUpdateFreeEnergy;
     gmx_sumi_sim(intervals.size(), intervals.data(), multiSimComm);
     for (int sim = 1; sim < numSim; sim++)
     {
@@ -142,7 +142,7 @@ void biasesAreCompatibleForSharingBetweenSimulations(const AwhParams&
         if (awhParams.awhBiasParams[b].shareGroup > 0)
         {
             std::vector<int64_t> pointSizes(numSim);
-            pointSizes[multiSimComm->sim] = pointSize[b];
+            pointSizes[multiSimComm->simulationIndex_] = pointSize[b];
             gmx_sumli_sim(pointSizes.size(), pointSizes.data(), multiSimComm);
             for (int sim = 1; sim < numSim; sim++)
             {
similarity index 97%
rename from src/gromacs/awh/biassharing.h
rename to src/gromacs/applied_forces/awh/biassharing.h
index b98a4344a8bbf39a9d9e2a2fdd9dbbea31ded369..751fe2f9a5f9b7c8bbce89d6684013023dcdc437 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 84%
rename from src/gromacs/awh/biasstate.cpp
rename to src/gromacs/applied_forces/awh/biasstate.cpp
index 081fb7f13f0a0e40b394ceb917d33b97c45d6cc9..a64e8e6038d9cfc1dd383c41634c8a42c1aa9491 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,6 +53,7 @@
 #include <cstring>
 
 #include <algorithm>
+#include <optional>
 
 #include "gromacs/fileio/gmxfio.h"
 #include "gromacs/fileio/xvgr.h"
@@ -70,7 +71,7 @@
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/stringutil.h"
 
-#include "grid.h"
+#include "biasgrid.h"
 #include "pointstate.h"
 
 namespace gmx
@@ -132,7 +133,7 @@ void sumOverSimulations(gmx::ArrayRef<T> arrayRef, const t_commrec* commRecord,
     }
     if (commRecord->nnodes > 1)
     {
-        gmx_bcast(arrayRef.size() * sizeof(T), arrayRef.data(), commRecord);
+        gmx_bcast(arrayRef.size() * sizeof(T), arrayRef.data(), commRecord->mpi_comm_mygroup);
     }
 }
 
@@ -153,9 +154,9 @@ void sumPmf(gmx::ArrayRef<PointState> pointState,
     {
         return;
     }
-    GMX_ASSERT(multiSimComm != nullptr && numSharedUpdate % multiSimComm->nsim == 0,
-               "numSharedUpdate should be a multiple of multiSimComm->nsim");
-    GMX_ASSERT(numSharedUpdate == multiSimComm->nsim,
+    GMX_ASSERT(multiSimComm != nullptr && numSharedUpdate % multiSimComm->numSimulations_ == 0,
+               "numSharedUpdate should be a multiple of multiSimComm->numSimulations_");
+    GMX_ASSERT(numSharedUpdate == multiSimComm->numSimulations_,
                "Sharing within a simulation is not implemented (yet)");
 
     std::vector<double> buffer(pointState.size());
@@ -207,24 +208,29 @@ double freeEnergyMinimumValue(gmx::ArrayRef<const PointState> pointState)
  * w(point|value) = exp(bias(point) - U(value,point)),
  * where U is a harmonic umbrella potential.
  *
- * \param[in] dimParams     The bias dimensions parameters
- * \param[in] points        The point state.
- * \param[in] grid          The grid.
- * \param[in] pointIndex    Point to evaluate probability weight for.
- * \param[in] pointBias     Bias for the point (as a log weight).
- * \param[in] value         Coordinate value.
+ * \param[in] dimParams              The bias dimensions parameters
+ * \param[in] points                 The point state.
+ * \param[in] grid                   The grid.
+ * \param[in] pointIndex             Point to evaluate probability weight for.
+ * \param[in] pointBias              Bias for the point (as a log weight).
+ * \param[in] value                  Coordinate value.
+ * \param[in] neighborLambdaEnergies The energy of the system in neighboring lambdas states. Can be
+ * empty when there are no free energy lambda state dimensions.
+ * \param[in] gridpointIndex         The index of the current grid point.
  * \returns the log of the biased probability weight.
  */
 double biasedLogWeightFromPoint(const std::vector<DimParams>&  dimParams,
                                 const std::vector<PointState>& points,
-                                const Grid&                    grid,
+                                const BiasGrid&                grid,
                                 int                            pointIndex,
                                 double                         pointBias,
-                                const awh_dvec                 value)
+                                const awh_dvec                 value,
+                                gmx::ArrayRef<const double>    neighborLambdaEnergies,
+                                int                            gridpointIndex)
 {
     double logWeight = detail::c_largeNegativeExponent;
 
-    /* Only points in the target reigon have non-zero weight */
+    /* Only points in the target region have non-zero weight */
     if (points[pointIndex].inTargetRegion())
     {
         logWeight = pointBias;
@@ -232,18 +238,66 @@ double biasedLogWeightFromPoint(const std::vector<DimParams>&  dimParams,
         /* Add potential for all parameter dimensions */
         for (size_t d = 0; d < dimParams.size(); d++)
         {
-            double dev = getDeviationFromPointAlongGridAxis(grid, d, pointIndex, value[d]);
-            logWeight -= 0.5 * dimParams[d].betak * dev * dev;
+            if (dimParams[d].isFepLambdaDimension())
+            {
+                /* If this is not a sampling step or if this function is called from
+                 * calcConvolvedBias(), when writing energy subblocks, neighborLambdaEnergies will
+                 * be empty. No convolution is required along the lambda dimension. */
+                if (!neighborLambdaEnergies.empty())
+                {
+                    const int pointLambdaIndex     = grid.point(pointIndex).coordValue[d];
+                    const int gridpointLambdaIndex = grid.point(gridpointIndex).coordValue[d];
+                    logWeight -= dimParams[d].fepDimParams().beta
+                                 * (neighborLambdaEnergies[pointLambdaIndex]
+                                    - neighborLambdaEnergies[gridpointLambdaIndex]);
+                }
+            }
+            else
+            {
+                double dev = getDeviationFromPointAlongGridAxis(grid, d, pointIndex, value[d]);
+                logWeight -= 0.5 * dimParams[d].pullDimParams().betak * dev * dev;
+            }
         }
     }
-
     return logWeight;
 }
 
+/*! \brief
+ * Calculates the marginal distribution (marginal probability) for each value along
+ * a free energy lambda axis.
+ * The marginal distribution of one coordinate dimension value is the sum of the probability
+ * distribution of all values (herein all neighbor values) with the same value in the dimension
+ * of interest.
+ * \param[in] grid               The bias grid.
+ * \param[in] neighbors          The points to use for the calculation of the marginal distribution.
+ * \param[in] probWeightNeighbor Probability weights of the neighbors.
+ * \returns The calculated marginal distribution in a 1D array with
+ * as many elements as there are points along the axis of interest.
+ */
+std::vector<double> calculateFELambdaMarginalDistribution(const BiasGrid&          grid,
+                                                          gmx::ArrayRef<const int> neighbors,
+                                                          gmx::ArrayRef<const double> probWeightNeighbor)
+{
+    const std::optional<int> lambdaAxisIndex = grid.lambdaAxisIndex();
+    GMX_RELEASE_ASSERT(lambdaAxisIndex,
+                       "There must be a free energy lambda axis in order to calculate the free "
+                       "energy lambda marginal distribution.");
+    const int           numFepLambdaStates = grid.numFepLambdaStates();
+    std::vector<double> lambdaMarginalDistribution(numFepLambdaStates, 0);
+
+    for (size_t i = 0; i < neighbors.size(); i++)
+    {
+        const int neighbor    = neighbors[i];
+        const int lambdaState = grid.point(neighbor).coordValue[lambdaAxisIndex.value()];
+        lambdaMarginalDistribution[lambdaState] += probWeightNeighbor[i];
+    }
+    return lambdaMarginalDistribution;
+}
+
 } // namespace
 
 void BiasState::calcConvolvedPmf(const std::vector<DimParams>& dimParams,
-                                 const Grid&                   grid,
+                                 const BiasGrid&               grid,
                                  std::vector<float>*           convolvedPmf) const
 {
     size_t numPoints = grid.numPoints();
@@ -267,7 +321,7 @@ void BiasState::calcConvolvedPmf(const std::vector<DimParams>& dimParams,
                Note that this function only adds point within the target > 0 region.
                Sum weights, take the logarithm last to get the free energy. */
             double logWeight = biasedLogWeightFromPoint(dimParams, points_, grid, neighbor,
-                                                        biasNeighbor, point.coordValue);
+                                                        biasNeighbor, point.coordValue, {}, m);
             freeEnergyWeights += std::exp(logWeight);
         }
 
@@ -316,10 +370,10 @@ void updateTargetDistribution(gmx::ArrayRef<PointState> pointState, const BiasPa
  * Puts together a string describing a grid point.
  *
  * \param[in] grid         The grid.
- * \param[in] point        Grid point index.
+ * \param[in] point        BiasGrid point index.
  * \returns a string for the point.
  */
-std::string gridPointValueString(const Grid& grid, int point)
+std::string gridPointValueString(const BiasGrid& grid, int point)
 {
     std::string pointString;
 
@@ -343,7 +397,7 @@ std::string gridPointValueString(const Grid& grid, int point)
 
 } // namespace
 
-int BiasState::warnForHistogramAnomalies(const Grid& grid, int biasIndex, double t, FILE* fplog, int maxNumWarnings) const
+int BiasState::warnForHistogramAnomalies(const BiasGrid& grid, int biasIndex, double t, FILE* fplog, int maxNumWarnings) const
 {
     GMX_ASSERT(fplog != nullptr, "Warnings can only be issued if there is log file.");
     const double maxHistogramRatio = 0.5; /* Tolerance for printing a warning about the histogram ratios */
@@ -414,29 +468,42 @@ int BiasState::warnForHistogramAnomalies(const Grid& grid, int biasIndex, double
 }
 
 double BiasState::calcUmbrellaForceAndPotential(const std::vector<DimParams>& dimParams,
-                                                const Grid&                   grid,
+                                                const BiasGrid&               grid,
                                                 int                           point,
+                                                ArrayRef<const double>        neighborLambdaDhdl,
                                                 gmx::ArrayRef<double>         force) const
 {
     double potential = 0;
     for (size_t d = 0; d < dimParams.size(); d++)
     {
-        double deviation =
-                getDeviationFromPointAlongGridAxis(grid, d, point, coordState_.coordValue()[d]);
-
-        double k = dimParams[d].k;
+        if (dimParams[d].isFepLambdaDimension())
+        {
+            if (!neighborLambdaDhdl.empty())
+            {
+                const int coordpointLambdaIndex = grid.point(point).coordValue[d];
+                force[d]                        = neighborLambdaDhdl[coordpointLambdaIndex];
+                /* The potential should not be affected by the lambda dimension. */
+            }
+        }
+        else
+        {
+            double deviation =
+                    getDeviationFromPointAlongGridAxis(grid, d, point, coordState_.coordValue()[d]);
+            double k = dimParams[d].pullDimParams().k;
 
-        /* Force from harmonic potential 0.5*k*dev^2 */
-        force[d] = -k * deviation;
-        potential += 0.5 * k * deviation * deviation;
+            /* Force from harmonic potential 0.5*k*dev^2 */
+            force[d] = -k * deviation;
+            potential += 0.5 * k * deviation * deviation;
+        }
     }
 
     return potential;
 }
 
 void BiasState::calcConvolvedForce(const std::vector<DimParams>& dimParams,
-                                   const Grid&                   grid,
+                                   const BiasGrid&               grid,
                                    gmx::ArrayRef<const double>   probWeightNeighbor,
+                                   ArrayRef<const double>        neighborLambdaDhdl,
                                    gmx::ArrayRef<double>         forceWorkBuffer,
                                    gmx::ArrayRef<double>         force) const
 {
@@ -454,7 +521,7 @@ void BiasState::calcConvolvedForce(const std::vector<DimParams>& dimParams,
         int    indexNeighbor  = neighbor[n];
 
         /* Get the umbrella force from this point. The returned potential is ignored here. */
-        calcUmbrellaForceAndPotential(dimParams, grid, indexNeighbor, forceFromNeighbor);
+        calcUmbrellaForceAndPotential(dimParams, grid, indexNeighbor, neighborLambdaDhdl, forceFromNeighbor);
 
         /* Add the weighted umbrella force to the convolved force. */
         for (size_t d = 0; d < dimParams.size(); d++)
@@ -465,20 +532,27 @@ void BiasState::calcConvolvedForce(const std::vector<DimParams>& dimParams,
 }
 
 double BiasState::moveUmbrella(const std::vector<DimParams>& dimParams,
-                               const Grid&                   grid,
+                               const BiasGrid&               grid,
                                gmx::ArrayRef<const double>   probWeightNeighbor,
+                               ArrayRef<const double>        neighborLambdaDhdl,
                                gmx::ArrayRef<double>         biasForce,
                                int64_t                       step,
                                int64_t                       seed,
-                               int                           indexSeed)
+                               int                           indexSeed,
+                               bool                          onlySampleUmbrellaGridpoint)
 {
     /* Generate and set a new coordinate reference value */
     coordState_.sampleUmbrellaGridpoint(grid, coordState_.gridpointIndex(), probWeightNeighbor,
                                         step, seed, indexSeed);
 
+    if (onlySampleUmbrellaGridpoint)
+    {
+        return 0;
+    }
+
     std::vector<double> newForce(dimParams.size());
-    double              newPotential =
-            calcUmbrellaForceAndPotential(dimParams, grid, coordState_.umbrellaGridpoint(), newForce);
+    double              newPotential = calcUmbrellaForceAndPotential(
+            dimParams, grid, coordState_.umbrellaGridpoint(), neighborLambdaDhdl, newForce);
 
     /*  A modification of the reference value at time t will lead to a different
         force over t-dt/2 to t and over t to t+dt/2. For high switching rates
@@ -585,7 +659,7 @@ void BiasState::doSkippedUpdatesForAllPoints(const BiasParams& params)
     }
 }
 
-void BiasState::doSkippedUpdatesInNeighborhood(const BiasParams& params, const Grid& grid)
+void BiasState::doSkippedUpdatesInNeighborhood(const BiasParams& params, const BiasGrid& grid)
 {
     double weightHistScaling;
     double logPmfsumScaling;
@@ -655,7 +729,7 @@ void mergeSharedUpdateLists(std::vector<int>*     updateList,
  * last update. \param[in] endUpdatelist     The end of the rectangular that has been sampled since
  * last update. \param[in,out] updateList    Local update list to set (assumed >= npoints long).
  */
-void makeLocalUpdateList(const Grid&                    grid,
+void makeLocalUpdateList(const BiasGrid&                grid,
                          const std::vector<PointState>& points,
                          const awh_ivec                 originUpdatelist,
                          const awh_ivec                 endUpdatelist,
@@ -693,7 +767,7 @@ void makeLocalUpdateList(const Grid&                    grid,
 
 } // namespace
 
-void BiasState::resetLocalUpdateRange(const Grid& grid)
+void BiasState::resetLocalUpdateRange(const BiasGrid& grid)
 {
     const int gridpointIndex = coordState_.gridpointIndex();
     for (int d = 0; d < grid.numDimensions(); d++)
@@ -734,7 +808,7 @@ void sumHistograms(gmx::ArrayRef<PointState> pointState,
     /* Sum histograms over multiple simulations if needed. */
     if (numSharedUpdate > 1)
     {
-        GMX_ASSERT(numSharedUpdate == multiSimComm->nsim,
+        GMX_ASSERT(numSharedUpdate == multiSimComm->numSimulations_,
                    "Sharing within a simulation is not implemented (yet)");
 
         /* Collect the weights and counts in linear arrays to be able to use gmx_sumd_sim. */
@@ -881,7 +955,7 @@ void labelCoveredPoints(const std::vector<bool>& visited,
 
 bool BiasState::isSamplingRegionCovered(const BiasParams&             params,
                                         const std::vector<DimParams>& dimParams,
-                                        const Grid&                   grid,
+                                        const BiasGrid&               grid,
                                         const t_commrec*              commRecord,
                                         const gmx_multisim_t*         multiSimComm) const
 {
@@ -923,7 +997,18 @@ bool BiasState::isSamplingRegionCovered(const BiasParams&             params,
     double weightThreshold = 1;
     for (int d = 0; d < grid.numDimensions(); d++)
     {
-        weightThreshold *= grid.axis(d).spacing() * std::sqrt(dimParams[d].betak * 0.5 * M_1_PI);
+        if (grid.axis(d).isFepLambdaAxis())
+        {
+            /* TODO: Verify that a threshold of 1.0 is OK. With a very high sample weight 1.0 can be
+             * reached quickly even in regions with low probability. Should the sample weight be
+             * taken into account here? */
+            weightThreshold *= 1.0;
+        }
+        else
+        {
+            weightThreshold *= grid.axis(d).spacing()
+                               * std::sqrt(dimParams[d].pullDimParams().betak * 0.5 * M_1_PI);
+        }
     }
 
     /* Project the sampling weights onto each dimension */
@@ -1006,7 +1091,7 @@ static void normalizeFreeEnergyAndPmfSum(std::vector<PointState>* pointState)
 }
 
 void BiasState::updateFreeEnergyAndAddSamplesToHistogram(const std::vector<DimParams>& dimParams,
-                                                         const Grid&                   grid,
+                                                         const BiasGrid&               grid,
                                                          const BiasParams&             params,
                                                          const t_commrec*              commRecord,
                                                          const gmx_multisim_t*         multiSimComm,
@@ -1153,7 +1238,8 @@ void BiasState::updateFreeEnergyAndAddSamplesToHistogram(const std::vector<DimPa
 }
 
 double BiasState::updateProbabilityWeightsAndConvolvedBias(const std::vector<DimParams>& dimParams,
-                                                           const Grid&                   grid,
+                                                           const BiasGrid&               grid,
+                                                           gmx::ArrayRef<const double> neighborLambdaEnergies,
                                                            std::vector<double, AlignedAllocator<double>>* weight) const
 {
     /* Only neighbors of the current coordinate value will have a non-negligible chance of getting sampled */
@@ -1179,9 +1265,9 @@ double BiasState::updateProbabilityWeightsAndConvolvedBias(const std::vector<Dim
             if (n < neighbors.size())
             {
                 const int neighbor = neighbors[n];
-                (*weight)[n] =
-                        biasedLogWeightFromPoint(dimParams, points_, grid, neighbor,
-                                                 points_[neighbor].bias(), coordState_.coordValue());
+                (*weight)[n]       = biasedLogWeightFromPoint(
+                        dimParams, points_, grid, neighbor, points_[neighbor].bias(),
+                        coordState_.coordValue(), neighborLambdaEnergies, coordState_.gridpointIndex());
             }
             else
             {
@@ -1201,6 +1287,30 @@ double BiasState::updateProbabilityWeightsAndConvolvedBias(const std::vector<Dim
 
     /* Normalize probabilities to sum to 1 */
     double invWeightSum = 1 / weightSum;
+
+    /* When there is a free energy lambda state axis remove the convolved contributions along that
+     * axis from the total bias. This must be done after calculating invWeightSum (since weightSum
+     * will be modified), but before normalizing the weights (below). */
+    if (grid.hasLambdaAxis())
+    {
+        /* If there is only one axis the bias will not be convolved in any dimension. */
+        if (grid.axis().size() == 1)
+        {
+            weightSum = gmx::exp(points_[coordState_.gridpointIndex()].bias());
+        }
+        else
+        {
+            for (size_t i = 0; i < neighbors.size(); i++)
+            {
+                const int neighbor = neighbors[i];
+                if (pointsHaveDifferentLambda(grid, coordState_.gridpointIndex(), neighbor))
+                {
+                    weightSum -= weightData[i];
+                }
+            }
+        }
+    }
+
     for (double& w : *weight)
     {
         w *= invWeightSum;
@@ -1211,7 +1321,7 @@ double BiasState::updateProbabilityWeightsAndConvolvedBias(const std::vector<Dim
 }
 
 double BiasState::calcConvolvedBias(const std::vector<DimParams>& dimParams,
-                                    const Grid&                   grid,
+                                    const BiasGrid&               grid,
                                     const awh_dvec&               coordValue) const
 {
     int              point     = grid.nearestIndex(coordValue);
@@ -1221,8 +1331,13 @@ double BiasState::calcConvolvedBias(const std::vector<DimParams>& dimParams,
     double weightSum = 0;
     for (int neighbor : gridPoint.neighbor)
     {
+        /* No convolution is required along the lambda dimension. */
+        if (pointsHaveDifferentLambda(grid, point, neighbor))
+        {
+            continue;
+        }
         double logWeight = biasedLogWeightFromPoint(dimParams, points_, grid, neighbor,
-                                                    points_[neighbor].bias(), coordValue);
+                                                    points_[neighbor].bias(), coordValue, {}, point);
         weightSum += std::exp(logWeight);
     }
 
@@ -1230,7 +1345,7 @@ double BiasState::calcConvolvedBias(const std::vector<DimParams>& dimParams,
     return (weightSum > 0) ? std::log(weightSum) : -GMX_FLOAT_MAX;
 }
 
-void BiasState::sampleProbabilityWeights(const Grid& grid, gmx::ArrayRef<const double> probWeightNeighbor)
+void BiasState::sampleProbabilityWeights(const BiasGrid& grid, gmx::ArrayRef<const double> probWeightNeighbor)
 {
     const std::vector<int>& neighbor = grid.point(coordState_.gridpointIndex()).neighbor;
 
@@ -1284,7 +1399,10 @@ void BiasState::sampleProbabilityWeights(const Grid& grid, gmx::ArrayRef<const d
     }
 }
 
-void BiasState::sampleCoordAndPmf(const Grid& grid, gmx::ArrayRef<const double> probWeightNeighbor, double convolvedBias)
+void BiasState::sampleCoordAndPmf(const std::vector<DimParams>& dimParams,
+                                  const BiasGrid&               grid,
+                                  gmx::ArrayRef<const double>   probWeightNeighbor,
+                                  double                        convolvedBias)
 {
     /* Sampling-based deconvolution extracting the PMF.
      * Update the PMF histogram with the current coordinate value.
@@ -1299,13 +1417,60 @@ void BiasState::sampleCoordAndPmf(const Grid& grid, gmx::ArrayRef<const double>
      * it works (mainly because how the PMF histogram is rescaled).
      */
 
-    /* Only save coordinate data that is in range (the given index is always
-     * in range even if the coordinate value is not).
-     */
-    if (grid.covers(coordState_.coordValue()))
+    const int                gridPointIndex  = coordState_.gridpointIndex();
+    const std::optional<int> lambdaAxisIndex = grid.lambdaAxisIndex();
+
+    /* Update the PMF of points along a lambda axis with their bias. */
+    if (lambdaAxisIndex)
     {
-        /* Save PMF sum and keep a histogram of the sampled coordinate values */
-        points_[coordState_.gridpointIndex()].samplePmf(convolvedBias);
+        const std::vector<int>& neighbors = grid.point(gridPointIndex).neighbor;
+
+        std::vector<double> lambdaMarginalDistribution =
+                calculateFELambdaMarginalDistribution(grid, neighbors, probWeightNeighbor);
+
+        awh_dvec coordValueAlongLambda = { coordState_.coordValue()[0], coordState_.coordValue()[1],
+                                           coordState_.coordValue()[2], coordState_.coordValue()[3] };
+        for (size_t i = 0; i < neighbors.size(); i++)
+        {
+            const int neighbor = neighbors[i];
+            double    bias;
+            if (pointsAlongLambdaAxis(grid, gridPointIndex, neighbor))
+            {
+                const double neighborLambda = grid.point(neighbor).coordValue[lambdaAxisIndex.value()];
+                if (neighbor == gridPointIndex)
+                {
+                    bias = convolvedBias;
+                }
+                else
+                {
+                    coordValueAlongLambda[lambdaAxisIndex.value()] = neighborLambda;
+                    bias = calcConvolvedBias(dimParams, grid, coordValueAlongLambda);
+                }
+
+                const double probWeight = lambdaMarginalDistribution[neighborLambda];
+                const double weightedBias = bias - std::log(std::max(probWeight, GMX_DOUBLE_MIN)); // avoid log(0)
+
+                if (neighbor == gridPointIndex && grid.covers(coordState_.coordValue()))
+                {
+                    points_[neighbor].samplePmf(weightedBias);
+                }
+                else
+                {
+                    points_[neighbor].updatePmfUnvisited(weightedBias);
+                }
+            }
+        }
+    }
+    else
+    {
+        /* Only save coordinate data that is in range (the given index is always
+         * in range even if the coordinate value is not).
+         */
+        if (grid.covers(coordState_.coordValue()))
+        {
+            /* Save PMF sum and keep a histogram of the sampled coordinate values */
+            points_[gridPointIndex].samplePmf(convolvedBias);
+        }
     }
 
     /* Save probability weights for the update */
@@ -1317,7 +1482,7 @@ void BiasState::initHistoryFromState(AwhBiasHistory* biasHistory) const
     biasHistory->pointState.resize(points_.size());
 }
 
-void BiasState::updateHistory(AwhBiasHistory* biasHistory, const Grid& grid) const
+void BiasState::updateHistory(AwhBiasHistory* biasHistory, const BiasGrid& grid) const
 {
     GMX_RELEASE_ASSERT(biasHistory->pointState.size() == points_.size(),
                        "The AWH history setup does not match the AWH state.");
@@ -1340,7 +1505,7 @@ void BiasState::updateHistory(AwhBiasHistory* biasHistory, const Grid& grid) con
     stateHistory->end_index_updatelist    = multiDimGridIndexToLinear(grid, endUpdatelist_);
 }
 
-void BiasState::restoreFromHistory(const AwhBiasHistory& biasHistory, const Grid& grid)
+void BiasState::restoreFromHistory(const AwhBiasHistory& biasHistory, const BiasGrid& grid)
 {
     const AwhBiasStateHistory& stateHistory = biasHistory.state;
 
@@ -1370,16 +1535,17 @@ void BiasState::restoreFromHistory(const AwhBiasHistory& biasHistory, const Grid
 
 void BiasState::broadcast(const t_commrec* commRecord)
 {
-    gmx_bcast(sizeof(coordState_), &coordState_, commRecord);
+    gmx_bcast(sizeof(coordState_), &coordState_, commRecord->mpi_comm_mygroup);
 
-    gmx_bcast(points_.size() * sizeof(PointState), points_.data(), commRecord);
+    gmx_bcast(points_.size() * sizeof(PointState), points_.data(), commRecord->mpi_comm_mygroup);
 
-    gmx_bcast(weightSumCovering_.size() * sizeof(double), weightSumCovering_.data(), commRecord);
+    gmx_bcast(weightSumCovering_.size() * sizeof(double), weightSumCovering_.data(),
+              commRecord->mpi_comm_mygroup);
 
-    gmx_bcast(sizeof(histogramSize_), &histogramSize_, commRecord);
+    gmx_bcast(sizeof(histogramSize_), &histogramSize_, commRecord->mpi_comm_mygroup);
 }
 
-void BiasState::setFreeEnergyToConvolvedPmf(const std::vector<DimParams>& dimParams, const Grid& grid)
+void BiasState::setFreeEnergyToConvolvedPmf(const std::vector<DimParams>& dimParams, const BiasGrid& grid)
 {
     std::vector<float> convolvedPmf;
 
@@ -1440,7 +1606,7 @@ static int countTrailingZeroRows(const double* const* data, int numRows, int num
  * \param[in,out] pointState  The state of the points in this bias.
  */
 static void readUserPmfAndTargetDistribution(const std::vector<DimParams>& dimParams,
-                                             const Grid&                   grid,
+                                             const BiasGrid&               grid,
                                              const std::string&            filename,
                                              int                           numBias,
                                              int                           biasIndex,
@@ -1479,7 +1645,7 @@ static void readUserPmfAndTargetDistribution(const std::vector<DimParams>& dimPa
     int      numColumns;
     int      numRows = read_xvg(filenameModified.c_str(), &data, &numColumns);
 
-    /* Check basic data properties here. Grid takes care of more complicated things. */
+    /* Check basic data properties here. BiasGrid takes care of more complicated things. */
 
     if (numRows <= 0)
     {
@@ -1629,7 +1795,7 @@ void BiasState::normalizePmf(int numSharingSims)
 
 void BiasState::initGridPointState(const AwhBiasParams&          awhBiasParams,
                                    const std::vector<DimParams>& dimParams,
-                                   const Grid&                   grid,
+                                   const BiasGrid&               grid,
                                    const BiasParams&             params,
                                    const std::string&            filename,
                                    int                           numBias)
@@ -1678,7 +1844,7 @@ void BiasState::initGridPointState(const AwhBiasParams&          awhBiasParams,
 BiasState::BiasState(const AwhBiasParams&          awhBiasParams,
                      double                        histogramSizeInitial,
                      const std::vector<DimParams>& dimParams,
-                     const Grid&                   grid) :
+                     const BiasGrid&               grid) :
     coordState_(awhBiasParams, dimParams, grid),
     points_(grid.numPoints()),
     weightSumCovering_(grid.numPoints()),
similarity index 79%
rename from src/gromacs/awh/biasstate.h
rename to src/gromacs/applied_forces/awh/biasstate.h
index 66b3373b151deb364e78bda7aca7785482d3e453..622f96f12c464a194402d5409cddf3c0743f24b8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,7 +40,7 @@
  *
  * The data members of this class are the state variables of the bias.
  * All interaction from the outside happens through the Bias class, which
- * holds important helper classes such as DimParams and Grid.
+ * holds important helper classes such as DimParams and BiasGrid.
  * This class holds many methods, but more are const methods that compute
  * properties of the state.
  *
 
 #include <cstdio>
 
+#include <string>
 #include <vector>
 
 #include "gromacs/math/vectypes.h"
 #include "gromacs/utility/alignedallocator.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/gmxassert.h"
 
@@ -72,10 +72,12 @@ struct t_commrec;
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
 struct AwhBiasHistory;
 struct AwhBiasParams;
 class BiasParams;
-class Grid;
+class BiasGrid;
 class GridAxis;
 class PointState;
 
@@ -108,7 +110,7 @@ public:
     BiasState(const AwhBiasParams&          awhBiasParams,
               double                        histogramSizeInitial,
               const std::vector<DimParams>& dimParams,
-              const Grid&                   grid);
+              const BiasGrid&               grid);
 
     /*! \brief
      * Restore the bias state from history.
@@ -116,7 +118,7 @@ public:
      * \param[in] biasHistory  Bias history struct.
      * \param[in] grid         The bias grid.
      */
-    void restoreFromHistory(const AwhBiasHistory& biasHistory, const Grid& grid);
+    void restoreFromHistory(const AwhBiasHistory& biasHistory, const BiasGrid& grid);
 
     /*! \brief
      * Broadcast the bias state over the MPI ranks in this simulation.
@@ -143,7 +145,7 @@ public:
      * \param[out] biasHistory  Bias history struct.
      * \param[in]  grid         The bias grid.
      */
-    void updateHistory(AwhBiasHistory* biasHistory, const Grid& grid) const;
+    void updateHistory(AwhBiasHistory* biasHistory, const BiasGrid& grid) const;
 
 private:
     /*! \brief Convolves the given PMF using the given AWH bias.
@@ -156,7 +158,7 @@ private:
      * \param[in,out] convolvedPmf  Array returned will be of the same length as the AWH grid to store the convolved PMF in.
      */
     void calcConvolvedPmf(const std::vector<DimParams>& dimParams,
-                          const Grid&                   grid,
+                          const BiasGrid&               grid,
                           std::vector<float>*           convolvedPmf) const;
 
     /*! \brief
@@ -165,7 +167,7 @@ private:
      * \param[in] dimParams  The bias dimensions parameters
      * \param[in] grid       The bias grid.
      */
-    void setFreeEnergyToConvolvedPmf(const std::vector<DimParams>& dimParams, const Grid& grid);
+    void setFreeEnergyToConvolvedPmf(const std::vector<DimParams>& dimParams, const BiasGrid& grid);
 
     /*! \brief
      * Normalize the PMF histogram.
@@ -187,7 +189,7 @@ public:
      */
     void initGridPointState(const AwhBiasParams&          awhBiasParams,
                             const std::vector<DimParams>& dimParams,
-                            const Grid&                   grid,
+                            const BiasGrid&               grid,
                             const BiasParams&             params,
                             const std::string&            filename,
                             int                           numBias);
@@ -202,7 +204,7 @@ public:
      * \param[in]     maxNumWarnings  Don't issue more than this number of warnings.
      * \returns the number of warnings issued.
      */
-    int warnForHistogramAnomalies(const Grid& grid, int biasIndex, double t, FILE* fplog, int maxNumWarnings) const;
+    int warnForHistogramAnomalies(const BiasGrid& grid, int biasIndex, double t, FILE* fplog, int maxNumWarnings) const;
 
     /*! \brief
      * Calculates and sets the force the coordinate experiences from an umbrella centered at the given point.
@@ -213,12 +215,19 @@ public:
      * \param[in]     dimParams  The bias dimensions parameters.
      * \param[in]     grid       The grid.
      * \param[in]     point      Point for umbrella center.
+     * \param[in]     neighborLambdaDhdl     An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
      * \param[in,out] force      Force vector to set.
      * Returns the umbrella potential.
      */
     double calcUmbrellaForceAndPotential(const std::vector<DimParams>& dimParams,
-                                         const Grid&                   grid,
+                                         const BiasGrid&               grid,
                                          int                           point,
+                                         ArrayRef<const double>        neighborLambdaDhdl,
                                          gmx::ArrayRef<double>         force) const;
 
     /*! \brief
@@ -230,12 +239,19 @@ public:
      * \param[in]     dimParams           The bias dimensions parameters.
      * \param[in]     grid                The grid.
      * \param[in]     probWeightNeighbor  Probability weights of the neighbors.
+     * \param[in]     neighborLambdaDhdl     An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
      * \param[in]     forceWorkBuffer     Force work buffer, values only used internally.
      * \param[in,out] force               Bias force vector to set.
      */
     void calcConvolvedForce(const std::vector<DimParams>& dimParams,
-                            const Grid&                   grid,
+                            const BiasGrid&               grid,
                             gmx::ArrayRef<const double>   probWeightNeighbor,
+                            ArrayRef<const double>        neighborLambdaDhdl,
                             gmx::ArrayRef<double>         forceWorkBuffer,
                             gmx::ArrayRef<double>         force) const;
 
@@ -248,22 +264,32 @@ public:
      * This function should only be called when the bias force is not being convolved.
      * It is assumed that the probability distribution has been updated.
      *
-     * \param[in] dimParams           Bias dimension parameters.
-     * \param[in] grid                The grid.
-     * \param[in] probWeightNeighbor  Probability weights of the neighbors.
-     * \param[in,out] biasForce       The AWH bias force.
-     * \param[in] step                Step number, needed for the random number generator.
-     * \param[in] seed                Random seed.
-     * \param[in] indexSeed           Second random seed, should be the bias Index.
+     * \param[in] dimParams                   Bias dimension parameters.
+     * \param[in] grid                        The grid.
+     * \param[in] probWeightNeighbor          Probability weights of the neighbors.
+     * \param[in] neighborLambdaDhdl          An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[in,out] biasForce               The AWH bias force.
+     * \param[in] step                        Step number, needed for the random number generator.
+     * \param[in] seed                        Random seed.
+     * \param[in] indexSeed                   Second random seed, should be the bias Index.
+     * \param[in] onlySampleUmbrellaGridpoint Only sample the umbrella gridpoint without calculating
+     * force and potential.
      * \returns the new potential value.
      */
     double moveUmbrella(const std::vector<DimParams>& dimParams,
-                        const Grid&                   grid,
+                        const BiasGrid&               grid,
                         gmx::ArrayRef<const double>   probWeightNeighbor,
+                        ArrayRef<const double>        neighborLambdaDhdl,
                         gmx::ArrayRef<double>         biasForce,
                         int64_t                       step,
                         int64_t                       seed,
-                        int                           indexSeed);
+                        int                           indexSeed,
+                        bool                          onlySampleUmbrellaGridpoint);
 
 private:
     /*! \brief
@@ -292,7 +318,7 @@ public:
      * \param[in] params  The bias parameters.
      * \param[in] grid    The grid.
      */
-    void doSkippedUpdatesInNeighborhood(const BiasParams& params, const Grid& grid);
+    void doSkippedUpdatesInNeighborhood(const BiasParams& params, const BiasGrid& grid);
 
 private:
     /*! \brief
@@ -300,7 +326,7 @@ private:
      *
      * \param[in] grid  The grid.
      */
-    void resetLocalUpdateRange(const Grid& grid);
+    void resetLocalUpdateRange(const BiasGrid& grid);
 
     /*! \brief
      * Returns the new size of the reference weight histogram in the initial stage.
@@ -337,7 +363,7 @@ private:
      */
     bool isSamplingRegionCovered(const BiasParams&             params,
                                  const std::vector<DimParams>& dimParams,
-                                 const Grid&                   grid,
+                                 const BiasGrid&               grid,
                                  const t_commrec*              commRecord,
                                  const gmx_multisim_t*         multiSimComm) const;
 
@@ -361,7 +387,7 @@ public:
      * \param[in] grid        The bias grid.
      * \param[in] coordValue  The current reaction coordinate value (there are no limits on allowed values).
      */
-    void setCoordValue(const Grid& grid, const awh_dvec coordValue)
+    void setCoordValue(const BiasGrid& grid, const awh_dvec coordValue)
     {
         coordState_.setCoordValue(grid, coordValue);
     }
@@ -391,7 +417,7 @@ public:
      * \param[in,out] updateList  Work space to store a temporary list.
      */
     void updateFreeEnergyAndAddSamplesToHistogram(const std::vector<DimParams>& dimParams,
-                                                  const Grid&                   grid,
+                                                  const BiasGrid&               grid,
                                                   const BiasParams&             params,
                                                   const t_commrec*              commRecord,
                                                   const gmx_multisim_t*         ms,
@@ -411,14 +437,21 @@ public:
      * it here since this saves us from doing extra exponential function evaluations
      * later on.
      *
-     * \param[in]  dimParams  The bias dimensions parameters
-     * \param[in]  grid       The grid.
-     * \param[out] weight     Probability weights of the neighbors, SIMD aligned.
+     * \param[in]  dimParams              The bias dimensions parameters
+     * \param[in]  grid                   The grid.
+     * \param[in]  neighborLambdaEnergies An array containing the energy of the system
+     * in neighboring lambdas. The array is of length numLambdas+1, where numLambdas is
+     * the number of free energy lambda states. Element 0 in the array is the energy
+     * of the current state and elements 1..numLambdas contain the energy of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[out] weight                 Probability weights of the neighbors, SIMD aligned.
      * \returns the convolved bias.
      */
 
     double updateProbabilityWeightsAndConvolvedBias(const std::vector<DimParams>& dimParams,
-                                                    const Grid&                   grid,
+                                                    const BiasGrid&               grid,
+                                                    ArrayRef<const double> neighborLambdaEnergies,
                                                     std::vector<double, AlignedAllocator<double>>* weight) const;
 
     /*! \brief
@@ -431,7 +464,7 @@ public:
      * \param[in] grid                The grid.
      * \param[in] probWeightNeighbor  Probability weights of the neighbors.
      */
-    void sampleProbabilityWeights(const Grid& grid, gmx::ArrayRef<const double> probWeightNeighbor);
+    void sampleProbabilityWeights(const BiasGrid& grid, gmx::ArrayRef<const double> probWeightNeighbor);
 
     /*! \brief
      * Sample the reaction coordinate and PMF for future updates or analysis.
@@ -439,11 +472,15 @@ public:
      * These samples do not affect the (future) sampling and are thus
      * pure observables. Statisics of these are stored in the energy file.
      *
+     * \param[in] dimParams           The bias dimensions parameters
      * \param[in] grid                The grid.
      * \param[in] probWeightNeighbor  Probability weights of the neighbors.
      * \param[in] convolvedBias       The convolved bias.
      */
-    void sampleCoordAndPmf(const Grid& grid, gmx::ArrayRef<const double> probWeightNeighbor, double convolvedBias);
+    void sampleCoordAndPmf(const std::vector<DimParams>& dimParams,
+                           const BiasGrid&               grid,
+                           gmx::ArrayRef<const double>   probWeightNeighbor,
+                           double                        convolvedBias);
     /*! \brief
      * Calculates the convolved bias for a given coordinate value.
      *
@@ -460,7 +497,7 @@ public:
      * \returns the convolved bias >= -GMX_FLOAT_MAX.
      */
     double calcConvolvedBias(const std::vector<DimParams>& dimParams,
-                             const Grid&                   grid,
+                             const BiasGrid&               grid,
                              const awh_dvec&               coordValue) const;
 
     /*! \brief
@@ -490,6 +527,10 @@ public:
      */
     inline HistogramSize histogramSize() const { return histogramSize_; }
 
+    /*! \brief Sets the umbrella grid point to the current grid point
+     */
+    void setUmbrellaGridpointToGridpoint() { coordState_.setUmbrellaGridpointToGridpoint(); }
+
     /* Data members */
 private:
     CoordState coordState_; /**< The Current coordinate state */
similarity index 98%
rename from src/gromacs/awh/biaswriter.cpp
rename to src/gromacs/applied_forces/awh/biaswriter.cpp
index 9230302eacc1b3f014f391ad7dae6e75997b3c12..8fd09eedbca37be6fa5be9238867923a958b1b37 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,7 +40,7 @@
 #include <cassert>
 #include <cmath>
 
-#include "gromacs/awh/awh.h"
+#include "gromacs/applied_forces/awh/awh.h"
 #include "gromacs/mdtypes/awh_params.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/trajectory/energyframe.h"
@@ -48,8 +48,8 @@
 #include "gromacs/utility/smalloc.h"
 
 #include "bias.h"
+#include "biasgrid.h"
 #include "correlationgrid.h"
-#include "grid.h"
 #include "pointstate.h"
 
 namespace gmx
similarity index 98%
rename from src/gromacs/awh/biaswriter.h
rename to src/gromacs/applied_forces/awh/biaswriter.h
index 14625b54dc542145a735b4ef2eac88d3fb810ccf..420a26ff630de93f7d2bef46b9cecebb423e0549 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 94%
rename from src/gromacs/awh/coordstate.cpp
rename to src/gromacs/applied_forces/awh/coordstate.cpp
index bd07a19044e9c1662b2dcb5f1a6d64092ff5a95f..6de883221315b2ab4cbd70adfc0653bffbc34a88 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
 
-#include "grid.h"
+#include "biasgrid.h"
 
 namespace gmx
 {
 
 CoordState::CoordState(const AwhBiasParams&          awhBiasParams,
                        const std::vector<DimParams>& dimParams,
-                       const Grid&                   grid)
+                       const BiasGrid&               grid)
 {
     for (size_t d = 0; d < dimParams.size(); d++)
     {
@@ -126,7 +126,7 @@ int getSampleFromDistribution(ArrayRef<const double> distr, int64_t seed, int64_
 
 } // namespace
 
-void CoordState::sampleUmbrellaGridpoint(const Grid&                 grid,
+void CoordState::sampleUmbrellaGridpoint(const BiasGrid&             grid,
                                          int                         gridpointIndex,
                                          gmx::ArrayRef<const double> probWeightNeighbor,
                                          int64_t                     step,
@@ -145,7 +145,7 @@ void CoordState::sampleUmbrellaGridpoint(const Grid&                 grid,
     umbrellaGridpoint_ = neighbor[localIndex];
 }
 
-void CoordState::setCoordValue(const Grid& grid, const awh_dvec coordValue)
+void CoordState::setCoordValue(const BiasGrid& grid, const awh_dvec coordValue)
 {
     /* We need to check for valid (probable) coordinate values, to give
      * a clear error message instead of a low-level assertion failure.
@@ -165,7 +165,7 @@ void CoordState::setCoordValue(const Grid& grid, const awh_dvec coordValue)
          */
         if (!axis.isPeriodic())
         {
-            const double margin = axis.spacing() * c_marginInSigma / Grid::c_numPointsPerSigma;
+            const double margin = axis.spacing() * c_marginInSigma / BiasGrid::c_numPointsPerSigma;
             if (coordValue[dim] < axis.origin() - margin
                 || coordValue[dim] > axis.origin() + axis.length() + margin)
             {
@@ -194,4 +194,9 @@ void CoordState::restoreFromHistory(const AwhBiasStateHistory& stateHistory)
     umbrellaGridpoint_ = stateHistory.umbrellaGridpoint;
 }
 
+void CoordState::setUmbrellaGridpointToGridpoint()
+{
+    umbrellaGridpoint_ = gridpointIndex_;
+}
+
 } // namespace gmx
similarity index 89%
rename from src/gromacs/awh/coordstate.h
rename to src/gromacs/applied_forces/awh/coordstate.h
index 05e88c54d5bd9e582d40776c1b3ae9af814b1bfe..f38ce86de98e2c27f0216138d849397f47654859 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <vector>
 
-#include "gromacs/utility/arrayref.h"
-
 #include "dimparams.h"
 
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
 struct AwhBiasParams;
 struct AwhBiasStateHistory;
 class BiasParams;
-class Grid;
+class BiasGrid;
 
 /*! \internal \brief Keeps track of the current coordinate value, grid index and umbrella location.
  */
@@ -77,7 +77,9 @@ public:
      * \param[in] dimParams      The dimension Parameters.
      * \param[in] grid           The grid.
      */
-    CoordState(const AwhBiasParams& awhBiasParams, const std::vector<DimParams>& dimParams, const Grid& grid);
+    CoordState(const AwhBiasParams&          awhBiasParams,
+               const std::vector<DimParams>& dimParams,
+               const BiasGrid&               grid);
 
     /*! \brief
      * Sample a new umbrella reference point given the current coordinate value.
@@ -92,7 +94,7 @@ public:
      * \param[in] indexSeed           Second random seed, should be the bias Index.
      * \returns the index of the sampled point.
      */
-    void sampleUmbrellaGridpoint(const Grid&                 grid,
+    void sampleUmbrellaGridpoint(const BiasGrid&             grid,
                                  int                         gridpointIndex,
                                  gmx::ArrayRef<const double> probWeightNeighbor,
                                  int64_t                     step,
@@ -104,7 +106,7 @@ public:
      * \param[in] grid        The grid.
      * \param[in] coordValue  The new coordinate value.
      */
-    void setCoordValue(const Grid& grid, const awh_dvec coordValue);
+    void setCoordValue(const BiasGrid& grid, const awh_dvec coordValue);
 
     /*! \brief Restores the coordinate state from history.
      *
@@ -124,6 +126,10 @@ public:
      */
     int umbrellaGridpoint() const { return umbrellaGridpoint_; }
 
+    /*! \brief Sets the umbrella grid point to the current grid point
+     */
+    void setUmbrellaGridpointToGridpoint();
+
 private:
     awh_dvec coordValue_;        /**< Current coordinate value in (nm or rad) */
     int      gridpointIndex_;    /**< The grid point index for the current coordinate value */
similarity index 98%
rename from src/gromacs/awh/correlationgrid.cpp
rename to src/gromacs/applied_forces/awh/correlationgrid.cpp
index ac1b48edb47de453a66500edfe6520ec8fc35769..1181dd1999c14718fe9180ccf466921eae66601e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 97%
rename from src/gromacs/awh/correlationgrid.h
rename to src/gromacs/applied_forces/awh/correlationgrid.h
index 022b51a4e92d0afe171f5c5f2e2372de12b1030d..a1c4338d039954adee0fdc36736efb97153f8e26 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,7 +50,6 @@
 
 #include <vector>
 
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/gmxassert.h"
 
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
 struct CorrelationGridHistory;
 
 /*! \internal
- * \brief Grid of local correlation tensors.
+ * \brief BiasGrid of local correlation tensors.
  *
  * This class provides the means for a bias to interaction with the grid
  * of correlation tensors. The grid should have the same number of points
similarity index 99%
rename from src/gromacs/awh/correlationhistory.cpp
rename to src/gromacs/applied_forces/awh/correlationhistory.cpp
index 3dac473fe0f3f41a0ee1591d45ec59dca71fb6a6..f77ba399387046ca91da0ce91c72505193043181 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 97%
rename from src/gromacs/awh/correlationhistory.h
rename to src/gromacs/applied_forces/awh/correlationhistory.h
index 61e70d95070b9b0ecc7a65a621984e4422de7735..05ac52845f7da0db3c0461be460097e92b4c22b1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 99%
rename from src/gromacs/awh/correlationtensor.cpp
rename to src/gromacs/applied_forces/awh/correlationtensor.cpp
index ebcd1aa5900a09cf1d047d0b58d88c38b7508954..b2a42d905c7216b4778904bf0ee77f95f8a66af1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 99%
rename from src/gromacs/awh/correlationtensor.h
rename to src/gromacs/applied_forces/awh/correlationtensor.h
index e9fe9537dadd1bba13f92c10984d7213e1a0fd85..9da3928dc9ef0fcfaa985c6e445393017cadf1c9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 54%
rename from src/gromacs/awh/dimparams.h
rename to src/gromacs/applied_forces/awh/dimparams.h
index 8478d19ec793e0f3fec8f2ee11574015af69bc51..2b3ac017205328a12e114f0093ac8dcd1be5560e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,6 +49,7 @@
 #ifndef GMX_AWH_DIMPARAMS_H
 #define GMX_AWH_DIMPARAMS_H
 
+#include <variant>
 #include <vector>
 
 #include "gromacs/math/vectypes.h"
@@ -70,20 +71,74 @@ typedef int awh_ivec[c_biasMaxNumDim];
  */
 struct DimParams
 {
+    /*! \internal \brief Type for storing dimension parameters for pull type dimensions
+     */
+    struct PullDimParams
+    {
+        const double k;     /**< Force constant (kJ/mol/nm^2) for each coordinate dimension. */
+        const double betak; /**< Inverse variance (1/nm^2) for each coordinate dimension. */
+        const double userCoordUnitsToInternal; /**< Conversion factor coordinate units. */
+    };
+
+    /*! \internal \brief Type for storing dimension parameters for free-energy lamdba type dimensions
+     */
+    struct FepDimParams
+    {
+        const double beta;               /**< 1/(k_B T). */
+        const int    numFepLambdaStates; /**< Number of lambda points in this dimension. */
+    };
+
+private:
     /*! \brief
-     * Constructor.
+     * Private constructor called by public builder functions for PullDimParams and FepLambdaDimParams.
+     */
+    DimParams(double conversionFactor, std::variant<PullDimParams, FepDimParams> dimParams) :
+        dimParams(std::move(dimParams)),
+        userCoordUnitsToInternal(conversionFactor)
+    {
+    }
+
+public:
+    /*! \brief
+     * Builder function for pull dimension parameters.
      *
-     * \param[in] conversionFactor  Conversion factor from user coordinate units to bias internal units (=DEG2RAD for angles).
+     * \param[in] conversionFactor  Conversion factor from user coordinate units to bias internal
+     * units (=DEG2RAD for angles).
      * \param[in] forceConstant     The harmonic force constant.
      * \param[in] beta              1/(k_B T).
      */
-    DimParams(double conversionFactor, double forceConstant, double beta) :
-        k(forceConstant),
-        betak(beta * forceConstant),
-        userCoordUnitsToInternal(conversionFactor)
+    static DimParams pullDimParams(double conversionFactor, double forceConstant, double beta)
     {
+        PullDimParams pullDimParams = { forceConstant, forceConstant * beta };
+
+        return DimParams(conversionFactor, pullDimParams);
+    }
+
+    /*! \brief
+     * Builder function for FEP lambda dimension parameters.
+     *
+     * \param[in] numFepLambdaStates  Number of lambda states in the system.
+     * \param[in] beta                1/(k_B T).
+     */
+    static DimParams fepLambdaDimParams(int numFepLambdaStates, double beta)
+    {
+        FepDimParams fepDimParams = { beta, numFepLambdaStates };
+
+        return DimParams(1.0, fepDimParams);
     }
 
+    //! Returns whether this dimension is coupled to a pull coordinate.
+    bool isPullDimension() const { return std::holds_alternative<PullDimParams>(dimParams); }
+
+    //! Returns whether this dimension has lambda states and thereby is a dimension coupled to lambda.
+    bool isFepLambdaDimension() const { return std::holds_alternative<FepDimParams>(dimParams); }
+
+    //! Returns pull dimension parameters, only call for pull dimensions
+    const PullDimParams& pullDimParams() const { return std::get<PullDimParams>(dimParams); }
+
+    //! Returns FEP dimension parameters, only call for FEP dimensions
+    const FepDimParams& fepDimParams() const { return std::get<FepDimParams>(dimParams); }
+
     /*! \brief Convert internal coordinate units to external, user coordinate units.
      *
      * \param[in] value               Value to convert.
@@ -98,9 +153,10 @@ struct DimParams
      */
     double scaleUserInputToInternal(double value) const { return value * userCoordUnitsToInternal; }
 
-    const double k;     /**< Force constant (kJ/mol/nm^2) for each coordinate dimension. */
-    const double betak; /**< Inverse variance (1/nm^2) for each coordinate dimension. */
-    const double userCoordUnitsToInternal; /**< Conversion factor coordinate units. */
+    //! Parameters for pull dimensions, either type pull or free-energy lambda
+    const std::variant<PullDimParams, FepDimParams> dimParams;
+    //! Conversion factor for ordinate units
+    const double userCoordUnitsToInternal;
 };
 
 } // namespace gmx
similarity index 98%
rename from src/gromacs/awh/histogramsize.cpp
rename to src/gromacs/applied_forces/awh/histogramsize.cpp
index f405685d747556a41aebc61714a0ab478130aa04..fcfd452d4a2e441553f9f6ab983c484566305ce3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -54,6 +54,7 @@
 
 #include "gromacs/mdtypes/awh_history.h"
 #include "gromacs/mdtypes/awh_params.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
 
similarity index 98%
rename from src/gromacs/awh/histogramsize.h
rename to src/gromacs/applied_forces/awh/histogramsize.h
index 7fb3a21ce9c6b663c9b7dd81ca4425930c217472..7d9313549cd8443ec0e042e722e1785c25b9c94f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <vector>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
 struct AwhBiasStateHistory;
 struct AwhBiasParams;
 class BiasParams;
similarity index 91%
rename from src/gromacs/awh/pointstate.cpp
rename to src/gromacs/applied_forces/awh/pointstate.cpp
index 5878860cb85cec96d983f9472d7ee709e7c4cd5a..2266fdcf209d90b5942e4eb9ef4ca863bc9b1a67 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -74,4 +74,12 @@ void PointState::samplePmf(double convolvedBias)
     }
 }
 
+void PointState::updatePmfUnvisited(double bias)
+{
+    if (inTargetRegion())
+    {
+        logPmfSum_ = expSum(logPmfSum_, -bias);
+    }
+}
+
 } // namespace gmx
similarity index 98%
rename from src/gromacs/awh/pointstate.h
rename to src/gromacs/applied_forces/awh/pointstate.h
index a66a6371c242e520cb119564e35efb8b99218bba..a893140b6b90249bf24bdf0c9525de2c7b5cf52e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -322,6 +322,13 @@ public:
      */
     void samplePmf(double convolvedBias);
 
+    /*! \brief Update the PMF histogram of unvisited coordinate values
+     * (along a lambda axis)
+     *
+     * \param[in] bias  The bias to update with.
+     */
+    void updatePmfUnvisited(double bias);
+
 private:
     /*! \brief Update the free energy estimate of a point.
      *
similarity index 67%
rename from src/gromacs/awh/read_params.cpp
rename to src/gromacs/applied_forces/awh/read_params.cpp
index 2b277177316ea97859389c93c38f3608f505a144..d301bd83d9240375263af5bed02b49b3c8978407 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,7 +39,7 @@
 
 #include "read_params.h"
 
-#include "gromacs/awh/awh.h"
+#include "gromacs/applied_forces/awh/awh.h"
 #include "gromacs/fileio/readinp.h"
 #include "gromacs/fileio/warninp.h"
 #include "gromacs/math/units.h"
@@ -51,6 +52,7 @@
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pulling/pull.h"
 #include "gromacs/random/seed.h"
+#include "gromacs/topology/mtop_util.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
@@ -69,88 +71,45 @@ const char* eawhgrowth_names[eawhgrowthNR + 1] = { "exp-linear", "linear", nullp
 
 const char* eawhpotential_names[eawhpotentialNR + 1] = { "convolved", "umbrella", nullptr };
 
-const char* eawhcoordprovider_names[eawhcoordproviderNR + 1] = { "pull", nullptr };
+const char* eawhcoordprovider_names[eawhcoordproviderNR + 1] = { "pull", "fep-lambda", nullptr };
 
+namespace
+{
 /*! \brief
- * Read parameters of an AWH bias dimension.
+ * Check the parameters of an AWH bias pull dimension.
  *
- * \param[in,out] inp        Input file entries.
  * \param[in] prefix         Prefix for dimension parameters.
  * \param[in,out] dimParams  AWH dimensional parameters.
  * \param[in] pull_params    Pull parameters.
  * \param[in,out] wi         Struct for bookeeping warnings.
- * \param[in] bComment       True if comments should be printed.
  */
-static void readDimParams(std::vector<t_inpfile>* inp,
-                          const std::string&      prefix,
-                          AwhDimParams*           dimParams,
-                          const pull_params_t*    pull_params,
-                          warninp_t               wi,
-                          bool                    bComment)
+void checkPullDimParams(const std::string&   prefix,
+                        AwhDimParams*        dimParams,
+                        const pull_params_t* pull_params,
+                        warninp_t            wi)
 {
-    std::string opt;
-    if (bComment)
-    {
-        printStringNoNewline(
-                inp, "The provider of the reaction coordinate, currently only pull is supported");
-    }
-
-    opt                       = prefix + "-coord-provider";
-    dimParams->eCoordProvider = get_eeenum(inp, opt, eawhcoordprovider_names, wi);
-
-    if (bComment)
-    {
-        printStringNoNewline(inp, "The coordinate index for this dimension");
-    }
-    opt = prefix + "-coord-index";
-    int coordIndexInput;
-    coordIndexInput = get_eint(inp, opt, 1, wi);
-    if (coordIndexInput < 1)
+    if (dimParams->coordIndex < 0)
     {
         gmx_fatal(FARGS,
-                  "Failed to read a valid coordinate index for %s. "
+                  "Failed to read a valid coordinate index for %s-coord-index. "
                   "Note that the pull coordinate indexing starts at 1.",
-                  opt.c_str());
+                  prefix.c_str());
     }
-
-    /* The pull coordinate indices start at 1 in the input file, at 0 internally */
-    dimParams->coordIndex = coordIndexInput - 1;
-
-    /* The pull settings need to be consistent with the AWH settings */
-    if (!(pull_params->coord[dimParams->coordIndex].eType == epullEXTERNAL))
-    {
-        gmx_fatal(FARGS, "AWH biasing can only be  applied to pull type %s", EPULLTYPE(epullEXTERNAL));
-    }
-
     if (dimParams->coordIndex >= pull_params->ncoord)
     {
         gmx_fatal(FARGS,
                   "The given AWH coordinate index (%d) is larger than the number of pull "
                   "coordinates (%d)",
-                  coordIndexInput, pull_params->ncoord);
+                  dimParams->coordIndex + 1, pull_params->ncoord);
     }
     if (pull_params->coord[dimParams->coordIndex].rate != 0)
     {
         auto message = formatString(
                 "Setting pull-coord%d-rate (%g) is incompatible with AWH biasing this coordinate",
-                coordIndexInput, pull_params->coord[dimParams->coordIndex].rate);
+                dimParams->coordIndex + 1, pull_params->coord[dimParams->coordIndex].rate);
         warning_error(wi, message);
     }
 
-    /* Grid params for each axis */
-    int eGeom = pull_params->coord[dimParams->coordIndex].eGeom;
-
-    if (bComment)
-    {
-        printStringNoNewline(inp, "Start and end values for each coordinate dimension");
-    }
-
-    opt               = prefix + "-start";
-    dimParams->origin = get_ereal(inp, opt, 0., wi);
-
-    opt            = prefix + "-end";
-    dimParams->end = get_ereal(inp, opt, 0., wi);
-
     if (gmx_within_tol(dimParams->end - dimParams->origin, 0, GMX_REAL_EPS))
     {
         auto message = formatString(
@@ -159,14 +118,23 @@ static void readDimParams(std::vector<t_inpfile>* inp,
                 prefix.c_str(), dimParams->origin, prefix.c_str(), dimParams->end);
         warning(wi, message);
     }
+
+    if (dimParams->forceConstant <= 0)
+    {
+        warning_error(wi, "The force AWH bias force constant should be > 0");
+    }
+
+    /* Grid params for each axis */
+    int eGeom = pull_params->coord[dimParams->coordIndex].eGeom;
+
     /* Check that the requested interval is in allowed range */
     if (eGeom == epullgDIST)
     {
         if (dimParams->origin < 0 || dimParams->end < 0)
         {
             gmx_fatal(FARGS,
-                      "%s-start (%g) or %s-end (%g) set to a negative value. With pull geometry "
-                      "distance coordinate values are non-negative. "
+                      "%s-start (%g) or %s-end (%g) set to a negative value. With pull "
+                      "geometry distance coordinate values are non-negative. "
                       "Perhaps you want to use geometry %s instead?",
                       prefix.c_str(), dimParams->origin, prefix.c_str(), dimParams->end,
                       EPULLGEOM(epullgDIR));
@@ -177,8 +145,8 @@ static void readDimParams(std::vector<t_inpfile>* inp,
         if (dimParams->origin < 0 || dimParams->end > 180)
         {
             gmx_fatal(FARGS,
-                      "%s-start (%g) and %s-end (%g) are outside of the allowed range 0 to 180 deg "
-                      "for pull geometries %s and %s ",
+                      "%s-start (%g) and %s-end (%g) are outside of the allowed range "
+                      "0 to 180 deg for pull geometries %s and %s ",
                       prefix.c_str(), dimParams->origin, prefix.c_str(), dimParams->end,
                       EPULLGEOM(epullgANGLE), EPULLGEOM(epullgANGLEAXIS));
         }
@@ -188,56 +156,226 @@ static void readDimParams(std::vector<t_inpfile>* inp,
         if (dimParams->origin < -180 || dimParams->end > 180)
         {
             gmx_fatal(FARGS,
-                      "%s-start (%g) and %s-end (%g) are outside of the allowed range -180 to 180 "
-                      "deg for pull geometry %s. ",
+                      "%s-start (%g) and %s-end (%g) are outside of the allowed range "
+                      "-180 to 180 deg for pull geometry %s. ",
                       prefix.c_str(), dimParams->origin, prefix.c_str(), dimParams->end,
                       EPULLGEOM(epullgDIHEDRAL));
         }
     }
+}
+
+/*! \brief
+ * Check parameters of an AWH bias in a free energy lambda state dimension.
+ *
+ * \param[in] prefix         Prefix for dimension parameters.
+ * \param[in,out] dimParams  AWH dimensional parameters.
+ * \param[in] lambdaParams   The free energy lambda related parameters.
+ * \param[in] efep           This is the type of FEP calculation (efep enumerator).
+ * \param[in,out] wi         Struct for bookeeping warnings.
+ */
+void checkFepLambdaDimParams(const std::string&  prefix,
+                             const AwhDimParams* dimParams,
+                             const t_lambda*     lambdaParams,
+                             const int           efep,
+                             warninp_t           wi)
+{
+    std::string opt;
+
+    if (!lambdaParams)
+    {
+        gmx_fatal(FARGS,
+                  "There must be free energy input if using AWH to steer the free energy lambda "
+                  "state.");
+    }
+
+    if (lambdaParams->lambda_neighbors != -1)
+    {
+        gmx_fatal(FARGS,
+                  "When running AWH coupled to the free energy lambda state all lambda states "
+                  "should be used as neighbors in order to get correct probabilities, i.e. "
+                  "calc-lambda-neighbors (%d) must be %d.",
+                  lambdaParams->lambda_neighbors, -1);
+    }
+
+    if (efep == efepSLOWGROWTH || lambdaParams->delta_lambda != 0)
+    {
+        gmx_fatal(FARGS,
+                  "AWH coupled to the free energy lambda state is not compatible with slow-growth "
+                  "and delta-lambda must be 0.");
+    }
+
+    if (efep == efepEXPANDED)
+    {
+        gmx_fatal(FARGS,
+                  "AWH is not treated like other expanded ensemble methods. Do not use expanded.");
+    }
+
+    if (dimParams->origin < 0)
+    {
+        opt = prefix + "-start";
+        gmx_fatal(FARGS,
+                  "When running AWH coupled to the free energy lambda state the lower lambda state "
+                  "for AWH, %s (%.0f), must be >= 0.",
+                  opt.c_str(), dimParams->origin);
+    }
+    if (dimParams->end >= lambdaParams->n_lambda)
+    {
+        opt = prefix + "-end";
+        gmx_fatal(FARGS,
+                  "When running AWH coupled to the free energy lambda state the upper lambda state "
+                  "for AWH, %s (%.0f), must be < n_lambda (%d).",
+                  opt.c_str(), dimParams->origin, lambdaParams->n_lambda);
+    }
+    if (gmx_within_tol(dimParams->end - dimParams->origin, 0, GMX_REAL_EPS))
+    {
+        auto message = formatString(
+                "The given interval length given by %s-start (%g) and %s-end (%g) is zero. "
+                "This will result in only one lambda point along this free energy lambda state "
+                "axis in the coordinate value grid.",
+                prefix.c_str(), dimParams->origin, prefix.c_str(), dimParams->end);
+        warning(wi, message);
+    }
+
+    if (dimParams->forceConstant != 0)
+    {
+        warning_error(
+                wi,
+                "The force AWH bias force constant is not used with free energy lambda state as "
+                "coordinate provider.");
+    }
+}
+
+/*! \brief
+ * Check that AWH FEP is not combined with incompatible decoupling.
+ *
+ * \param[in] mtop      System topology.
+ * \param[in,out] wi    Struct for bookeeping warnings.
+ */
+void checkFepLambdaDimDecouplingConsistency(const gmx_mtop_t& mtop, warninp_t wi)
+{
+    if (haveFepPerturbedMasses(mtop))
+    {
+        warning(wi,
+                "Masses may not be perturbed when using the free energy lambda state as AWH "
+                "coordinate provider. If you are using fep-lambdas to specify lambda states "
+                "make sure that you also specify mass-lambdas without perturbation.");
+    }
+    if (havePerturbedConstraints(mtop))
+    {
+        warning(wi,
+                "Constraints may not be perturbed when using the free energy lambda state as AWH "
+                "coordinate provider. If you are using fep-lambdas to specify lambda states "
+                "make sure that you also specify mass-lambdas without perturbation.");
+    }
+}
 
+/*! \brief
+ * Read parameters of an AWH bias dimension.
+ *
+ * \param[in,out] inp        Input file entries.
+ * \param[in] prefix         Prefix for dimension parameters.
+ * \param[in,out] dimParams  AWH dimensional parameters.
+ * \param[in,out] wi         Struct for bookeeping warnings.
+ * \param[in] bComment       True if comments should be printed.
+ */
+void readDimParams(std::vector<t_inpfile>* inp,
+                   const std::string&      prefix,
+                   AwhDimParams*           dimParams,
+                   warninp_t               wi,
+                   bool                    bComment)
+{
+    std::string opt;
     if (bComment)
     {
         printStringNoNewline(
-                inp, "The force constant for this coordinate (kJ/mol/nm^2 or kJ/mol/rad^2)");
+                inp,
+                "The provider of the reaction coordinate, "
+                "currently only 'pull' and 'fep-lambda' (free energy lambda state) is supported");
     }
-    opt                      = prefix + "-force-constant";
-    dimParams->forceConstant = get_ereal(inp, opt, 0, wi);
-    if (dimParams->forceConstant <= 0)
+    opt                       = prefix + "-coord-provider";
+    dimParams->eCoordProvider = get_eeenum(inp, opt, eawhcoordprovider_names, wi);
+
+    if (bComment)
     {
-        warning_error(wi, "The force AWH bias force constant should be > 0");
+        printStringNoNewline(inp, "The coordinate index for this dimension");
     }
+    opt = prefix + "-coord-index";
+    int coordIndexInput;
+    coordIndexInput = get_eint(inp, opt, 1, wi);
+
+    /* The pull coordinate indices start at 1 in the input file, at 0 internally */
+    dimParams->coordIndex = coordIndexInput - 1;
 
     if (bComment)
     {
-        printStringNoNewline(inp, "Estimated diffusion constant (nm^2/ps or rad^2/ps)");
+        printStringNoNewline(inp, "Start and end values for each coordinate dimension");
     }
-    opt                  = prefix + "-diffusion";
-    dimParams->diffusion = get_ereal(inp, opt, 0, wi);
+    opt               = prefix + "-start";
+    dimParams->origin = get_ereal(inp, opt, 0., wi);
+    opt               = prefix + "-end";
+    dimParams->end    = get_ereal(inp, opt, 0., wi);
 
-    if (dimParams->diffusion <= 0)
+    if (bComment)
     {
-        const double diffusion_default = 1e-5;
-        auto         message           = formatString(
-                "%s not explicitly set by user. You can choose to use a default "
-                "value (%g nm^2/ps or rad^2/ps) but this may very well be "
-                "non-optimal for your system!",
-                opt.c_str(), diffusion_default);
-        warning(wi, message);
-        dimParams->diffusion = diffusion_default;
+        printStringNoNewline(
+                inp, "The force constant for this coordinate (kJ/mol/nm^2 or kJ/mol/rad^2)");
+    }
+    opt                      = prefix + "-force-constant";
+    dimParams->forceConstant = get_ereal(inp, opt, 0, wi);
+
+    if (bComment)
+    {
+        printStringNoNewline(inp, "Estimated diffusion constant (nm^2/ps, rad^2/ps or ps^-1)");
     }
+    opt                  = prefix + "-diffusion";
+    dimParams->diffusion = get_ereal(inp, opt, 0, wi);
 
     if (bComment)
     {
         printStringNoNewline(inp,
                              "Diameter that needs to be sampled around a point before it is "
-                             "considered covered.");
+                             "considered covered. In FEP dimensions the cover diameter is "
+                             "specified in lambda states.");
     }
     opt                      = prefix + "-cover-diameter";
     dimParams->coverDiameter = get_ereal(inp, opt, 0, wi);
+}
 
-    if (dimParams->coverDiameter < 0)
+/*! \brief
+ * Check the parameters of an AWH bias dimension.
+ *
+ * \param[in] prefix         Prefix for dimension parameters.
+ * \param[in,out] dimParams  AWH dimensional parameters.
+ * \param[in] ir             Input parameter struct.
+ * \param[in,out] wi         Struct for bookeeping warnings.
+ */
+void checkDimParams(const std::string& prefix, AwhDimParams* dimParams, const t_inputrec* ir, warninp_t wi)
+{
+    if (dimParams->eCoordProvider == eawhcoordproviderPULL)
+    {
+        if (!ir->bPull)
+        {
+            gmx_fatal(FARGS,
+                      "AWH biasing along a pull dimension is only compatible with COM pulling "
+                      "turned on");
+        }
+        checkPullDimParams(prefix, dimParams, ir->pull, wi);
+    }
+    else if (dimParams->eCoordProvider == eawhcoordproviderFREE_ENERGY_LAMBDA)
     {
-        gmx_fatal(FARGS, "%s (%g) cannot be negative.", opt.c_str(), dimParams->coverDiameter);
+        if (ir->efep == efepNO)
+        {
+            gmx_fatal(FARGS,
+                      "AWH biasing along a free energy lambda state dimension is only compatible "
+                      "with free energy turned on");
+        }
+        checkFepLambdaDimParams(prefix, dimParams, ir->fepvals, ir->efep, wi);
+    }
+    else
+    {
+        gmx_fatal(FARGS,
+                  "AWH biasing can only be  applied to pull and free energy lambda state "
+                  "coordinates");
     }
 }
 
@@ -247,7 +385,7 @@ static void readDimParams(std::vector<t_inpfile>* inp,
  * \param[in]     awhBiasParams  AWH bias parameters.
  * \param[in,out] wi             Struct for bookkeeping warnings.
  */
-static void checkInputConsistencyAwhBias(const AwhBiasParams& awhBiasParams, warninp_t wi)
+void checkInputConsistencyAwhBias(const AwhBiasParams& awhBiasParams, warninp_t wi)
 {
     /* Covering diameter and sharing warning. */
     for (int d = 0; d < awhBiasParams.ndim; d++)
@@ -267,29 +405,22 @@ static void checkInputConsistencyAwhBias(const AwhBiasParams& awhBiasParams, war
  * \param[in,out] inp            Input file entries.
  * \param[in,out] awhBiasParams  AWH dimensional parameters.
  * \param[in]     prefix         Prefix for bias parameters.
- * \param[in]     ir             Input parameter struct.
  * \param[in,out] wi             Struct for bookeeping warnings.
  * \param[in]     bComment       True if comments should be printed.
  */
-static void read_bias_params(std::vector<t_inpfile>* inp,
-                             AwhBiasParams*          awhBiasParams,
-                             const std::string&      prefix,
-                             const t_inputrec*       ir,
-                             warninp_t               wi,
-                             bool                    bComment)
+void readBiasParams(std::vector<t_inpfile>* inp,
+                    AwhBiasParams*          awhBiasParams,
+                    const std::string&      prefix,
+                    warninp_t               wi,
+                    bool                    bComment)
 {
     if (bComment)
     {
         printStringNoNewline(inp, "Estimated initial PMF error (kJ/mol)");
     }
 
-    std::string opt = prefix + "-error-init";
-    /* We allow using a default value here without warning (but warn the user if the diffusion constant is not set). */
+    std::string opt             = prefix + "-error-init";
     awhBiasParams->errorInitial = get_ereal(inp, opt, 10, wi);
-    if (awhBiasParams->errorInitial <= 0)
-    {
-        gmx_fatal(FARGS, "%s needs to be > 0.", opt.c_str());
-    }
 
     if (bComment)
     {
@@ -308,13 +439,6 @@ static void read_bias_params(std::vector<t_inpfile>* inp,
     }
     opt                                 = prefix + "-equilibrate-histogram";
     awhBiasParams->equilibrateHistogram = (get_eeenum(inp, opt, yesno_names, wi) != 0);
-    if (awhBiasParams->equilibrateHistogram && awhBiasParams->eGrowth != eawhgrowthEXP_LINEAR)
-    {
-        auto message =
-                formatString("Option %s will only have an effect for histogram growth type '%s'.",
-                             opt.c_str(), EAWHGROWTH(eawhgrowthEXP_LINEAR));
-        warning(wi, message);
-    }
 
     if (bComment)
     {
@@ -324,6 +448,83 @@ static void read_bias_params(std::vector<t_inpfile>* inp,
     opt                    = prefix + "-target";
     awhBiasParams->eTarget = get_eeenum(inp, opt, eawhtarget_names, wi);
 
+    if (bComment)
+    {
+        printStringNoNewline(inp,
+                             "Boltzmann beta scaling factor for target distribution types "
+                             "'boltzmann' and 'boltzmann-local'");
+    }
+    opt                              = prefix + "-target-beta-scaling";
+    awhBiasParams->targetBetaScaling = get_ereal(inp, opt, 0, wi);
+
+    if (bComment)
+    {
+        printStringNoNewline(inp, "Free energy cutoff value for target distribution type 'cutoff'");
+    }
+    opt                         = prefix + "-target-cutoff";
+    awhBiasParams->targetCutoff = get_ereal(inp, opt, 0, wi);
+
+    if (bComment)
+    {
+        printStringNoNewline(inp, "Initialize PMF and target with user data: no or yes");
+    }
+    opt                      = prefix + "-user-data";
+    awhBiasParams->bUserData = get_eeenum(inp, opt, yesno_names, wi);
+
+    if (bComment)
+    {
+        printStringNoNewline(inp, "Group index to share the bias with, 0 means not shared");
+    }
+    opt                       = prefix + "-share-group";
+    awhBiasParams->shareGroup = get_eint(inp, opt, 0, wi);
+
+    if (bComment)
+    {
+        printStringNoNewline(inp, "Dimensionality of the coordinate");
+    }
+    opt                 = prefix + "-ndim";
+    awhBiasParams->ndim = get_eint(inp, opt, 0, wi);
+
+    /* Check this before starting to read the AWH dimension parameters. */
+    if (awhBiasParams->ndim <= 0 || awhBiasParams->ndim > c_biasMaxNumDim)
+    {
+        gmx_fatal(FARGS, "%s (%d) needs to be > 0 and at most %d\n", opt.c_str(),
+                  awhBiasParams->ndim, c_biasMaxNumDim);
+    }
+    snew(awhBiasParams->dimParams, awhBiasParams->ndim);
+    for (int d = 0; d < awhBiasParams->ndim; d++)
+    {
+        bComment              = bComment && d == 0;
+        std::string prefixdim = prefix + formatString("-dim%d", d + 1);
+        readDimParams(inp, prefixdim, &awhBiasParams->dimParams[d], wi, bComment);
+    }
+}
+
+/*! \brief
+ * Check the parameters of an AWH bias.
+ *
+ * \param[in]     awhBiasParams  AWH dimensional parameters.
+ * \param[in]     prefix         Prefix for bias parameters.
+ * \param[in]     ir             Input parameter struct.
+ * \param[in,out] wi             Struct for bookeeping warnings.
+ */
+void checkBiasParams(const AwhBiasParams* awhBiasParams, const std::string& prefix, const t_inputrec* ir, warninp_t wi)
+{
+    std::string opt = prefix + "-error-init";
+    if (awhBiasParams->errorInitial <= 0)
+    {
+        gmx_fatal(FARGS, "%s needs to be > 0.", opt.c_str());
+    }
+
+    opt = prefix + "-equilibrate-histogram";
+    if (awhBiasParams->equilibrateHistogram && awhBiasParams->eGrowth != eawhgrowthEXP_LINEAR)
+    {
+        auto message =
+                formatString("Option %s will only have an effect for histogram growth type '%s'.",
+                             opt.c_str(), EAWHGROWTH(eawhgrowthEXP_LINEAR));
+        warning(wi, message);
+    }
+
     if ((awhBiasParams->eTarget == eawhtargetLOCALBOLTZMANN)
         && (awhBiasParams->eGrowth == eawhgrowthEXP_LINEAR))
     {
@@ -336,15 +537,7 @@ static void read_bias_params(std::vector<t_inpfile>* inp,
         warning(wi, message);
     }
 
-    if (bComment)
-    {
-        printStringNoNewline(inp,
-                             "Boltzmann beta scaling factor for target distribution types "
-                             "'boltzmann' and 'boltzmann-local'");
-    }
-    opt                              = prefix + "-target-beta-scaling";
-    awhBiasParams->targetBetaScaling = get_ereal(inp, opt, 0, wi);
-
+    opt = prefix + "-target-beta-scaling";
     switch (awhBiasParams->eTarget)
     {
         case eawhtargetBOLTZMANN:
@@ -367,13 +560,7 @@ static void read_bias_params(std::vector<t_inpfile>* inp,
             break;
     }
 
-    if (bComment)
-    {
-        printStringNoNewline(inp, "Free energy cutoff value for target distribution type 'cutoff'");
-    }
-    opt                         = prefix + "-target-cutoff";
-    awhBiasParams->targetCutoff = get_ereal(inp, opt, 0, wi);
-
+    opt = prefix + "-target-cutoff";
     switch (awhBiasParams->eTarget)
     {
         case eawhtargetCUTOFF:
@@ -394,31 +581,13 @@ static void read_bias_params(std::vector<t_inpfile>* inp,
             break;
     }
 
-    if (bComment)
-    {
-        printStringNoNewline(inp, "Initialize PMF and target with user data: no or yes");
-    }
-    opt                      = prefix + "-user-data";
-    awhBiasParams->bUserData = get_eeenum(inp, opt, yesno_names, wi);
-
-    if (bComment)
-    {
-        printStringNoNewline(inp, "Group index to share the bias with, 0 means not shared");
-    }
-    opt                       = prefix + "-share-group";
-    awhBiasParams->shareGroup = get_eint(inp, opt, 0, wi);
+    opt = prefix + "-share-group";
     if (awhBiasParams->shareGroup < 0)
     {
         warning_error(wi, "AWH bias share-group should be >= 0");
     }
 
-    if (bComment)
-    {
-        printStringNoNewline(inp, "Dimensionality of the coordinate");
-    }
-    opt                 = prefix + "-ndim";
-    awhBiasParams->ndim = get_eint(inp, opt, 0, wi);
-
+    opt = prefix + "-ndim";
     if (awhBiasParams->ndim <= 0 || awhBiasParams->ndim > c_biasMaxNumDim)
     {
         gmx_fatal(FARGS, "%s (%d) needs to be > 0 and at most %d\n", opt.c_str(),
@@ -431,12 +600,10 @@ static void read_bias_params(std::vector<t_inpfile>* inp,
                      "currently only a rough guideline."
                      " You should verify its usefulness for your system before production runs!");
     }
-    snew(awhBiasParams->dimParams, awhBiasParams->ndim);
     for (int d = 0; d < awhBiasParams->ndim; d++)
     {
-        bComment              = bComment && d == 0;
         std::string prefixdim = prefix + formatString("-dim%d", d + 1);
-        readDimParams(inp, prefixdim, &awhBiasParams->dimParams[d], ir->pull, wi, bComment);
+        checkDimParams(prefixdim, &awhBiasParams->dimParams[d], ir, wi);
     }
 
     /* Check consistencies here that cannot be checked at read time at a lower level. */
@@ -449,7 +616,7 @@ static void read_bias_params(std::vector<t_inpfile>* inp,
  * \param[in]     awhParams  AWH parameters.
  * \param[in,out] wi         Struct for bookkeeping warnings.
  */
-static void checkInputConsistencyAwh(const AwhParams& awhParams, warninp_t wi)
+void checkInputConsistencyAwh(const AwhParams& awhParams, warninp_t wi)
 {
     /* Each pull coord can map to at most 1 AWH coord.
      * Check that we have a shared bias when requesting multisim sharing.
@@ -469,11 +636,19 @@ static void checkInputConsistencyAwh(const AwhParams& awhParams, warninp_t wi)
         {
             for (int d1 = 0; d1 < awhBiasParams1.ndim; d1++)
             {
+                if (awhBiasParams1.dimParams[d1].eCoordProvider == eawhcoordproviderFREE_ENERGY_LAMBDA)
+                {
+                    continue;
+                }
                 const AwhBiasParams& awhBiasParams2 = awhParams.awhBiasParams[k2];
 
                 /* d1 is the reference dimension of the reference AWH. d2 is the dim index of the AWH to compare with. */
                 for (int d2 = 0; d2 < awhBiasParams2.ndim; d2++)
                 {
+                    if (awhBiasParams2.dimParams[d2].eCoordProvider == eawhcoordproviderFREE_ENERGY_LAMBDA)
+                    {
+                        continue;
+                    }
                     /* Give an error if (d1, k1) is different from (d2, k2) but the pull coordinate is the same */
                     if ((d1 != d2 || k1 != k2)
                         && (awhBiasParams1.dimParams[d1].coordIndex == awhBiasParams2.dimParams[d2].coordIndex))
@@ -508,8 +683,9 @@ static void checkInputConsistencyAwh(const AwhParams& awhParams, warninp_t wi)
                 "this (yet)");
     }
 }
+} // namespace
 
-AwhParams* readAndCheckAwhParams(std::vector<t_inpfile>* inp, const t_inputrec* ir, warninp_t wi)
+AwhParams* readAwhParams(std::vector<t_inpfile>* inp, warninp_t wi)
 {
     AwhParams* awhParams;
     snew(awhParams, 1);
@@ -535,19 +711,6 @@ AwhParams* readAndCheckAwhParams(std::vector<t_inpfile>* inp, const t_inputrec*
     printStringNoNewline(inp, "Data output interval in number of steps");
     opt               = "awh-nstout";
     awhParams->nstOut = get_eint(inp, opt, 100000, wi);
-    if (awhParams->nstOut <= 0)
-    {
-        auto message = formatString("Not writing AWH output with AWH (%s = %d) does not make sense",
-                                    opt.c_str(), awhParams->nstOut);
-        warning_error(wi, message);
-    }
-    /* This restriction can be removed by changing a flag of print_ebin() */
-    if (ir->nstenergy == 0 || awhParams->nstOut % ir->nstenergy != 0)
-    {
-        auto message = formatString("%s (%d) should be a multiple of nstenergy (%d)", opt.c_str(),
-                                    awhParams->nstOut, ir->nstenergy);
-        warning_error(wi, message);
-    }
 
     printStringNoNewline(inp, "Coordinate sampling interval in number of steps");
     opt                       = "awh-nstsample";
@@ -556,10 +719,6 @@ AwhParams* readAndCheckAwhParams(std::vector<t_inpfile>* inp, const t_inputrec*
     printStringNoNewline(inp, "Free energy and bias update interval in number of samples");
     opt                                   = "awh-nsamples-update";
     awhParams->numSamplesUpdateFreeEnergy = get_eint(inp, opt, 10, wi);
-    if (awhParams->numSamplesUpdateFreeEnergy <= 0)
-    {
-        warning_error(wi, opt + " needs to be an integer > 0");
-    }
 
     printStringNoNewline(
             inp, "When true, biases with share-group>0 are shared between multiple simulations");
@@ -569,6 +728,7 @@ AwhParams* readAndCheckAwhParams(std::vector<t_inpfile>* inp, const t_inputrec*
     printStringNoNewline(inp, "The number of independent AWH biases");
     opt                = "awh-nbias";
     awhParams->numBias = get_eint(inp, opt, 1, wi);
+    /* Check this before starting to read the AWH biases. */
     if (awhParams->numBias <= 0)
     {
         gmx_fatal(FARGS, "%s needs to be an integer > 0", opt.c_str());
@@ -581,7 +741,41 @@ AwhParams* readAndCheckAwhParams(std::vector<t_inpfile>* inp, const t_inputrec*
     {
         bool        bComment  = (k == 0);
         std::string prefixawh = formatString("awh%d", k + 1);
-        read_bias_params(inp, &awhParams->awhBiasParams[k], prefixawh, ir, wi, bComment);
+        readBiasParams(inp, &awhParams->awhBiasParams[k], prefixawh, wi, bComment);
+    }
+
+    return awhParams;
+}
+
+void checkAwhParams(const AwhParams* awhParams, const t_inputrec* ir, warninp_t wi)
+{
+    std::string opt;
+
+    opt = "awh-nstout";
+    if (awhParams->nstOut <= 0)
+    {
+        auto message = formatString("Not writing AWH output with AWH (%s = %d) does not make sense",
+                                    opt.c_str(), awhParams->nstOut);
+        warning_error(wi, message);
+    }
+    /* This restriction can be removed by changing a flag of print_ebin() */
+    if (ir->nstenergy == 0 || awhParams->nstOut % ir->nstenergy != 0)
+    {
+        auto message = formatString("%s (%d) should be a multiple of nstenergy (%d)", opt.c_str(),
+                                    awhParams->nstOut, ir->nstenergy);
+        warning_error(wi, message);
+    }
+
+    opt = "awh-nsamples-update";
+    if (awhParams->numSamplesUpdateFreeEnergy <= 0)
+    {
+        warning_error(wi, opt + " needs to be an integer > 0");
+    }
+
+    for (int k = 0; k < awhParams->numBias; k++)
+    {
+        std::string prefixawh = formatString("awh%d", k + 1);
+        checkBiasParams(&awhParams->awhBiasParams[k], prefixawh, ir, wi);
     }
 
     /* Do a final consistency check before returning */
@@ -591,8 +785,6 @@ AwhParams* readAndCheckAwhParams(std::vector<t_inpfile>* inp, const t_inputrec*
     {
         warning_error(wi, "With AWH init-step should be 0");
     }
-
-    return awhParams;
 }
 
 /*! \brief
@@ -755,13 +947,80 @@ static void checkInputConsistencyInterval(const AwhParams* awhParams, warninp_t
     }
 }
 
+/*! \brief
+ * Sets AWH parameters, for one AWH pull dimension.
+ *
+ * \param[in,out] dimParams          AWH dimension parameters.
+ * \param[in]     biasIndex             The index of the bias containing this AWH pull dimension.
+ * \param[in]     dimIndex              The index of this AWH pull dimension.
+ * \param[in]     pull_params           Pull parameters.
+ * \param[in,out] pull_work             Pull working struct to register AWH bias in.
+ * \param[in]     pbc                   A pbc information structure.
+ * \param[in]     compressibility       Compressibility matrix for pressure coupling,
+ * pass all 0 without pressure coupling.
+ * \param[in,out] wi                    Struct for bookeeping warnings.
+ *
+ */
+static void setStateDependentAwhPullDimParams(AwhDimParams*        dimParams,
+                                              const int            biasIndex,
+                                              const int            dimIndex,
+                                              const pull_params_t* pull_params,
+                                              pull_t*              pull_work,
+                                              const t_pbc&         pbc,
+                                              const tensor&        compressibility,
+                                              warninp_t            wi)
+{
+    const t_pull_coord& pullCoordParams = pull_params->coord[dimParams->coordIndex];
+
+    if (pullCoordParams.eGeom == epullgDIRPBC)
+    {
+        gmx_fatal(FARGS,
+                  "AWH does not support pull geometry '%s'. "
+                  "If the maximum distance between the groups is always "
+                  "less than half the box size, "
+                  "you can use geometry '%s' instead.",
+                  EPULLGEOM(epullgDIRPBC), EPULLGEOM(epullgDIR));
+    }
+
+    dimParams->period = get_pull_coord_period(pullCoordParams, pbc, dimParams->end - dimParams->origin);
+    // We would like to check for scaling, but we don't have the full inputrec available here
+    if (dimParams->period > 0
+        && !(pullCoordParams.eGeom == epullgANGLE || pullCoordParams.eGeom == epullgDIHEDRAL))
+    {
+        bool coordIsScaled = false;
+        for (int d2 = 0; d2 < DIM; d2++)
+        {
+            if (pullCoordParams.vec[d2] != 0 && norm2(compressibility[d2]) != 0)
+            {
+                coordIsScaled = true;
+            }
+        }
+        if (coordIsScaled)
+        {
+            std::string mesg = gmx::formatString(
+                    "AWH dimension %d of bias %d is periodic with pull geometry '%s', "
+                    "while you should are applying pressure scaling to the "
+                    "corresponding box vector, this is not supported.",
+                    dimIndex + 1, biasIndex + 1, EPULLGEOM(pullCoordParams.eGeom));
+            warning(wi, mesg.c_str());
+        }
+    }
+
+    /* The initial coordinate value, converted to external user units. */
+    dimParams->coordValueInit = get_pull_coord_value(pull_work, dimParams->coordIndex, &pbc);
+
+    dimParams->coordValueInit *= pull_conversion_factor_internal2userinput(&pullCoordParams);
+}
+
 void setStateDependentAwhParams(AwhParams*           awhParams,
                                 const pull_params_t* pull_params,
                                 pull_t*              pull_work,
                                 const matrix         box,
-                                int                  ePBC,
+                                PbcType              pbcType,
                                 const tensor&        compressibility,
                                 const t_grpopts*     inputrecGroupOptions,
+                                const real           initLambda,
+                                const gmx_mtop_t&    mtop,
                                 warninp_t            wi)
 {
     /* The temperature is not really state depenendent but is not known
@@ -783,60 +1042,30 @@ void setStateDependentAwhParams(AwhParams*           awhParams,
     }
 
     t_pbc pbc;
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
 
     for (int k = 0; k < awhParams->numBias; k++)
     {
         AwhBiasParams* awhBiasParams = &awhParams->awhBiasParams[k];
         for (int d = 0; d < awhBiasParams->ndim; d++)
         {
-            AwhDimParams*       dimParams       = &awhBiasParams->dimParams[d];
-            const t_pull_coord& pullCoordParams = pull_params->coord[dimParams->coordIndex];
-
-            if (pullCoordParams.eGeom == epullgDIRPBC)
+            AwhDimParams* dimParams = &awhBiasParams->dimParams[d];
+            if (dimParams->eCoordProvider == eawhcoordproviderPULL)
             {
-                gmx_fatal(FARGS,
-                          "AWH does not support pull geometry '%s'. "
-                          "If the maximum distance between the groups is always less than half the "
-                          "box size, "
-                          "you can use geometry '%s' instead.",
-                          EPULLGEOM(epullgDIRPBC), EPULLGEOM(epullgDIR));
+                setStateDependentAwhPullDimParams(dimParams, k, d, pull_params, pull_work, pbc,
+                                                  compressibility, wi);
             }
-
-            dimParams->period =
-                    get_pull_coord_period(pullCoordParams, pbc, dimParams->end - dimParams->origin);
-            // We would like to check for scaling, but we don't have the full inputrec available here
-            if (dimParams->period > 0
-                && !(pullCoordParams.eGeom == epullgANGLE || pullCoordParams.eGeom == epullgDIHEDRAL))
+            else
             {
-                bool coordIsScaled = false;
-                for (int d2 = 0; d2 < DIM; d2++)
-                {
-                    if (pullCoordParams.vec[d2] != 0 && norm2(compressibility[d2]) != 0)
-                    {
-                        coordIsScaled = true;
-                    }
-                }
-                if (coordIsScaled)
-                {
-                    std::string mesg = gmx::formatString(
-                            "AWH dimension %d of bias %d is periodic with pull geometry '%s', "
-                            "while you should are applying pressure scaling to the corresponding "
-                            "box vector, this is not supported.",
-                            d + 1, k + 1, EPULLGEOM(pullCoordParams.eGeom));
-                    warning(wi, mesg.c_str());
-                }
+                dimParams->coordValueInit = initLambda;
+                checkFepLambdaDimDecouplingConsistency(mtop, wi);
             }
-
-            /* The initial coordinate value, converted to external user units. */
-            dimParams->coordValueInit = get_pull_coord_value(pull_work, dimParams->coordIndex, &pbc);
-
-            dimParams->coordValueInit *= pull_conversion_factor_internal2userinput(&pullCoordParams);
         }
     }
     checkInputConsistencyInterval(awhParams, wi);
 
-    /* Register AWH as external potential with pull to check consistency. */
+    /* Register AWH as external potential with pull (for AWH dimensions that use pull as
+     * reaction coordinate provider) to check consistency. */
     Awh::registerAwhWithPull(*awhParams, pull_work);
 }
 
similarity index 76%
rename from src/gromacs/awh/read_params.h
rename to src/gromacs/applied_forces/awh/read_params.h
index 61098890d1c3ce28563aa901ad42e679f3bd6966..7da77f1fdd49758cd9a96684933666ba0a269b91 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 struct t_grpopts;
 struct t_inputrec;
+struct gmx_mtop_t;
 struct pull_params_t;
 struct pull_t;
+enum class PbcType : int;
 
 namespace gmx
 {
 struct AwhParams;
 
-/*! \brief Allocate, initialize and check the AWH parameters with values from the input file.
+/*! \brief Allocate and initialize the AWH parameters with values from the input file.
  *
  * \param[in,out] inp          Input file entries.
- * \param[in]     inputrec     Input parameter struct.
  * \param[in,out] wi           Struct for bookeeping warnings.
  * \returns AWH parameters.
  */
-AwhParams* readAndCheckAwhParams(std::vector<t_inpfile>* inp, const t_inputrec* inputrec, warninp_t wi);
+AwhParams* readAwhParams(std::vector<t_inpfile>* inp, warninp_t wi);
+
+/*! \brief Check the AWH parameters.
+ *
+ * \param[in,out] awhParams    The AWH parameters.
+ * \param[in]     inputrec     Input parameter struct.
+ * \param[in,out] wi           Struct for bookeeping warnings.
+ */
+void checkAwhParams(const AwhParams* awhParams, const t_inputrec* inputrec, warninp_t wi);
 
 
 /*! \brief
@@ -75,9 +85,12 @@ AwhParams* readAndCheckAwhParams(std::vector<t_inpfile>* inp, const t_inputrec*
  * \param[in]     pull_params           Pull parameters.
  * \param[in,out] pull_work             Pull working struct to register AWH bias in.
  * \param[in]     box                   Box vectors.
- * \param[in]     ePBC                  Periodic boundary conditions enum.
+ * \param[in]     pbcType               Periodic boundary conditions enum.
  * \param[in]     compressibility       Compressibility matrix for pressure coupling, pass all 0 without pressure coupling
  * \param[in]     inputrecGroupOptions  Parameters for atom groups.
+ * \param[in]     initLambda            The starting lambda, to allow using free energy lambda as reaction coordinate
+ * provider in any dimension.
+ * \param[in]     mtop                  The system topology.
  * \param[in,out] wi                    Struct for bookeeping warnings.
  *
  * \note This function currently relies on the function set_pull_init to have been called.
@@ -86,9 +99,11 @@ void setStateDependentAwhParams(AwhParams*           awhParams,
                                 const pull_params_t* pull_params,
                                 pull_t*              pull_work,
                                 const matrix         box,
-                                int                  ePBC,
+                                PbcType              pbcType,
                                 const tensor&        compressibility,
                                 const t_grpopts*     inputrecGroupOptions,
+                                real                 initLambda,
+                                const gmx_mtop_t&    mtop,
                                 warninp_t            wi);
 
 } // namespace gmx
similarity index 89%
rename from src/gromacs/awh/tests/CMakeLists.txt
rename to src/gromacs/applied_forces/awh/tests/CMakeLists.txt
index 691be579a475da6030887ea6d7e145f21a22f896..bbce544ace5c1b3c58847fedf4216b5b49245b88 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2017, by the GROMACS development team, led by
+# Copyright (c) 2017,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,4 +33,9 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(AwhTest awh-test
-                  bias.cpp biasstate.cpp grid.cpp)
+    CPP_SOURCE_FILES
+        bias.cpp
+       biasgrid.cpp
+        biasstate.cpp
+        bias_fep_lambda_state.cpp
+        )
similarity index 93%
rename from src/gromacs/awh/tests/bias.cpp
rename to src/gromacs/applied_forces/awh/tests/bias.cpp
index 4d5b54a8ee7f54568fed5766513f042bc24f76e6..c7b0cedadd6e0d7c277ced3e7f044566be455095 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -34,7 +34,7 @@
  */
 #include "gmxpre.h"
 
-#include "gromacs/awh/bias.h"
+#include "gromacs/applied_forces/awh/bias.h"
 
 #include <cmath>
 
@@ -45,8 +45,8 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "gromacs/awh/correlationgrid.h"
-#include "gromacs/awh/pointstate.h"
+#include "gromacs/applied_forces/awh/correlationgrid.h"
+#include "gromacs/applied_forces/awh/pointstate.h"
 #include "gromacs/mdtypes/awh_params.h"
 #include "gromacs/utility/stringutil.h"
 
@@ -100,6 +100,7 @@ static AwhTestParameters getAwhTestParameters(int eawhgrowth, int eawhpotential)
     awhDimParams.end            = 1.5;
     awhDimParams.coordValueInit = awhDimParams.origin;
     awhDimParams.coverDiameter  = 0;
+    awhDimParams.eCoordProvider = eawhcoordproviderPULL;
 
     AwhBiasParams& awhBiasParams = params.awhBiasParams;
 
@@ -118,7 +119,7 @@ static AwhTestParameters getAwhTestParameters(int eawhgrowth, int eawhpotential)
     double  k          = 1000;
     int64_t seed       = 93471803;
 
-    params.dimParams.emplace_back(convFactor, k, params.beta);
+    params.dimParams.push_back(DimParams::pullDimParams(convFactor, k, params.beta));
 
     AwhParams& awhParams = params.awhParams;
 
@@ -228,8 +229,9 @@ TEST_P(BiasTest, ForcesBiasPmf)
 
         awh_dvec                    coordValue = { coord, 0, 0, 0 };
         double                      potential  = 0;
-        gmx::ArrayRef<const double> biasForce  = bias.calcForceAndUpdateBias(
-                coordValue, &potential, &potentialJump, nullptr, nullptr, step, step, seed_, nullptr);
+        gmx::ArrayRef<const double> biasForce =
+                bias.calcForceAndUpdateBias(coordValue, {}, {}, &potential, &potentialJump, nullptr,
+                                            nullptr, step, step, seed_, nullptr);
 
         force.push_back(biasForce[0]);
         pot.push_back(potential);
@@ -257,7 +259,7 @@ TEST_P(BiasTest, ForcesBiasPmf)
      * In taking this deviation we lose a lot of precision, so we should
      * compare against k*max(coord) instead of the instantaneous force.
      */
-    const double kCoordMax = bias.dimParams()[0].k * coordMaxValue;
+    const double kCoordMax = bias.dimParams()[0].pullDimParams().k * coordMaxValue;
 
     constexpr int ulpTol = 10;
 
@@ -313,8 +315,8 @@ TEST(BiasTest, DetectsCovering)
         awh_dvec coordValue    = { coord, 0, 0, 0 };
         double   potential     = 0;
         double   potentialJump = 0;
-        bias.calcForceAndUpdateBias(coordValue, &potential, &potentialJump, nullptr, nullptr, step,
-                                    step, params.awhParams.seed, nullptr);
+        bias.calcForceAndUpdateBias(coordValue, {}, {}, &potential, &potentialJump, nullptr,
+                                    nullptr, step, step, params.awhParams.seed, nullptr);
 
         inInitialStage = bias.state().inInitialStage();
         if (!inInitialStage)
diff --git a/src/gromacs/applied_forces/awh/tests/bias_fep_lambda_state.cpp b/src/gromacs/applied_forces/awh/tests/bias_fep_lambda_state.cpp
new file mode 100644 (file)
index 0000000..4c03ef5
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#include "gmxpre.h"
+
+#include <cmath>
+
+#include <memory>
+#include <random>
+#include <tuple>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "gromacs/applied_forces/awh/bias.h"
+#include "gromacs/applied_forces/awh/correlationgrid.h"
+#include "gromacs/applied_forces/awh/pointstate.h"
+#include "gromacs/mdtypes/awh_params.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "testutils/refdata.h"
+#include "testutils/testasserts.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+//! The number of lambda states to use in the tests.
+const int numLambdaStates = 16;
+
+/*! \internal \brief
+ * Struct that gathers all input for setting up and using a Bias
+ */
+struct AwhFepLambdaStateTestParameters
+{
+    AwhFepLambdaStateTestParameters() = default;
+    //! Move constructor
+    AwhFepLambdaStateTestParameters(AwhFepLambdaStateTestParameters&& o) noexcept :
+        beta(o.beta),
+        awhDimParams(o.awhDimParams),
+        awhBiasParams(o.awhBiasParams),
+        awhParams(o.awhParams),
+        dimParams(std::move(o.dimParams))
+    {
+        awhBiasParams.dimParams = &awhDimParams;
+        awhParams.awhBiasParams = &awhBiasParams;
+    }
+    double beta; //!< 1/(kB*T)
+
+    AwhDimParams  awhDimParams;  //!< Dimension parameters pointed to by \p awhBiasParams
+    AwhBiasParams awhBiasParams; //!< Bias parameters pointed to by \[ awhParams
+    AwhParams     awhParams;     //!< AWH parameters, this is the struct to actually use
+
+    std::vector<DimParams> dimParams; //!< Dimension parameters for setting up Bias
+};
+
+/*! \internal \brief Helper function to fill an array with random values (between lowerBound and
+ * upperBound) from randomEngine.
+ */
+static void randomArrayFill(ArrayRef<double>           array,
+                            std::default_random_engine randomEngine,
+                            double                     lowerBound,
+                            double                     upperBound)
+{
+    std::uniform_real_distribution<double> unif(lowerBound, upperBound);
+    for (size_t i = 0; i < array.size(); i++)
+    {
+        array[i] = unif(randomEngine);
+    }
+}
+
+//! Helper function to set up the C-style AWH parameters for the test
+static AwhFepLambdaStateTestParameters getAwhFepLambdaTestParameters(int eawhgrowth, int eawhpotential)
+{
+    AwhFepLambdaStateTestParameters params;
+
+    params.beta = 0.4;
+
+    AwhDimParams& awhDimParams = params.awhDimParams;
+
+    awhDimParams.period         = 0;
+    awhDimParams.diffusion      = 1e-4;
+    awhDimParams.origin         = 0;
+    awhDimParams.end            = numLambdaStates - 1;
+    awhDimParams.coordValueInit = awhDimParams.origin;
+    awhDimParams.coverDiameter  = 0;
+    awhDimParams.eCoordProvider = eawhcoordproviderFREE_ENERGY_LAMBDA;
+
+    AwhBiasParams& awhBiasParams = params.awhBiasParams;
+
+    awhBiasParams.ndim                 = 1;
+    awhBiasParams.dimParams            = &awhDimParams;
+    awhBiasParams.eTarget              = eawhtargetCONSTANT;
+    awhBiasParams.targetBetaScaling    = 0;
+    awhBiasParams.targetCutoff         = 0;
+    awhBiasParams.eGrowth              = eawhgrowth;
+    awhBiasParams.bUserData            = FALSE;
+    awhBiasParams.errorInitial         = 1.0 / params.beta;
+    awhBiasParams.shareGroup           = 0;
+    awhBiasParams.equilibrateHistogram = FALSE;
+
+    int64_t seed = 93471803;
+
+    params.dimParams.push_back(DimParams::fepLambdaDimParams(numLambdaStates, params.beta));
+
+    AwhParams& awhParams = params.awhParams;
+
+    awhParams.numBias                    = 1;
+    awhParams.awhBiasParams              = &awhBiasParams;
+    awhParams.seed                       = seed;
+    awhParams.nstOut                     = 0;
+    awhParams.nstSampleCoord             = 1;
+    awhParams.numSamplesUpdateFreeEnergy = 10;
+    awhParams.ePotential                 = eawhpotential;
+    awhParams.shareBiasMultisim          = FALSE;
+
+    return params;
+}
+
+//! Convenience typedef: growth type enum, potential type enum, disable update skips
+typedef std::tuple<int, int, BiasParams::DisableUpdateSkips> BiasTestParameters;
+
+/*! \brief Test fixture for testing Bias updates
+ */
+class BiasFepLambdaStateTest : public ::testing::TestWithParam<BiasTestParameters>
+{
+public:
+    //! Random seed for AWH MC sampling
+    int64_t seed_;
+
+    //! The awh Bias
+    std::unique_ptr<Bias> bias_;
+
+    BiasFepLambdaStateTest()
+    {
+        /* We test all combinations of:
+         *   eawhgrowth:
+         *     eawhgrowthLINEAR:     final, normal update phase
+         *     ewahgrowthEXP_LINEAR: intial phase, updated size is constant
+         *   eawhpotential (test both, but for the FEP lambda state dimension MC will in practice be used,
+         *                  except that eawhpotentialCONVOLVED also gives a potential output):
+         *     eawhpotentialUMBRELLA:  MC on lambda state
+         *     eawhpotentialCONVOLVED: MD on a convolved potential landscape (falling back to MC on lambda state)
+         *   disableUpdateSkips (should not affect the results):
+         *     BiasParams::DisableUpdateSkips::yes: update the point state for every sample
+         *     BiasParams::DisableUpdateSkips::no:  update the point state at an interval > 1 sample
+         *
+         * Note: It would be nice to explicitly check that eawhpotential
+         *       and disableUpdateSkips do not affect the point state.
+         *       But the reference data will also ensure this.
+         */
+        int                            eawhgrowth;
+        int                            eawhpotential;
+        BiasParams::DisableUpdateSkips disableUpdateSkips;
+        std::tie(eawhgrowth, eawhpotential, disableUpdateSkips) = GetParam();
+
+        /* Set up a basic AWH setup with a single, 1D bias with parameters
+         * such that we can measure the effects of different parameters.
+         */
+        const AwhFepLambdaStateTestParameters params =
+                getAwhFepLambdaTestParameters(eawhgrowth, eawhpotential);
+
+        seed_ = params.awhParams.seed;
+
+        double mdTimeStep = 0.1;
+
+        bias_ = std::make_unique<Bias>(-1, params.awhParams, params.awhBiasParams, params.dimParams,
+                                       params.beta, mdTimeStep, 1, "", Bias::ThisRankWillDoIO::No,
+                                       disableUpdateSkips);
+    }
+};
+
+TEST_P(BiasFepLambdaStateTest, ForcesBiasPmf)
+{
+    gmx::test::TestReferenceData    data;
+    gmx::test::TestReferenceChecker checker(data.rootChecker());
+
+    Bias& bias = *bias_;
+
+    /* Make strings with the properties we expect to be different in the tests.
+     * These also helps to interpret the reference data.
+     */
+    std::vector<std::string> props;
+    props.push_back(formatString("stage:           %s", bias.state().inInitialStage() ? "initial" : "final"));
+    props.push_back(formatString("convolve forces: %s", bias.params().convolveForce ? "yes" : "no"));
+    props.push_back(formatString("skip updates:    %s", bias.params().skipUpdates() ? "yes" : "no"));
+
+    SCOPED_TRACE(gmx::formatString("%s, %s, %s", props[0].c_str(), props[1].c_str(), props[2].c_str()));
+
+    std::vector<double> force, pot;
+
+    double                     potentialJump        = 0;
+    double                     mdTimeStep           = 0.1;
+    double                     energyNoiseMagnitude = 1.0;
+    double                     dhdlNoiseMagnitude   = 1.5;
+    int                        nSteps               = 501;
+    std::default_random_engine randomEngine;
+    randomEngine.seed(1234);
+
+    /* Some energies to use as base values (to which some noise is added later on). */
+    std::vector<double> lambdaEnergyBase(numLambdaStates);
+    std::vector<double> lambdaDhdlBase(numLambdaStates);
+    const double        magnitude = 12.0;
+    for (int i = 0; i < numLambdaStates; i++)
+    {
+        lambdaEnergyBase[i] = magnitude * std::sin(i * 0.1);
+        lambdaDhdlBase[i]   = magnitude * std::cos(i * 0.1);
+    }
+
+    for (int step = 0; step < nSteps; step++)
+    {
+        /* Create some noise and add it to the base values */
+        std::vector<double> neighborLambdaEnergyNoise(numLambdaStates);
+        std::vector<double> neighborLambdaDhdlNoise(numLambdaStates);
+        randomArrayFill(neighborLambdaEnergyNoise, randomEngine, -energyNoiseMagnitude, energyNoiseMagnitude);
+        randomArrayFill(neighborLambdaDhdlNoise, randomEngine, -dhdlNoiseMagnitude, dhdlNoiseMagnitude);
+        std::vector<double> neighborLambdaEnergies(numLambdaStates);
+        std::vector<double> neighborLambdaDhdl(numLambdaStates);
+        for (int i = 0; i < numLambdaStates; i++)
+        {
+            neighborLambdaEnergies[i] = lambdaEnergyBase[i] + neighborLambdaEnergyNoise[i];
+            neighborLambdaDhdl[i]     = lambdaDhdlBase[i] + neighborLambdaDhdlNoise[i];
+        }
+
+        int      umbrellaGridpointIndex = bias.state().coordState().umbrellaGridpoint();
+        awh_dvec coordValue = { bias.getGridCoordValue(umbrellaGridpointIndex)[0], 0, 0, 0 };
+        double   potential  = 0;
+        gmx::ArrayRef<const double> biasForce = bias.calcForceAndUpdateBias(
+                coordValue, neighborLambdaEnergies, neighborLambdaDhdl, &potential, &potentialJump,
+                nullptr, nullptr, step * mdTimeStep, step, seed_, nullptr);
+
+        force.push_back(biasForce[0]);
+        pot.push_back(potential);
+    }
+
+    /* When skipping updates, ensure all skipped updates are performed here.
+     * This should result in the same bias state as at output in a normal run.
+     */
+    if (bias.params().skipUpdates())
+    {
+        bias.doSkippedUpdatesForAllPoints();
+    }
+
+    std::vector<double> pointBias, logPmfsum;
+    for (auto& point : bias.state().points())
+    {
+        pointBias.push_back(point.bias());
+        logPmfsum.push_back(point.logPmfSum());
+    }
+
+    constexpr int ulpTol = 10;
+
+    checker.checkSequence(props.begin(), props.end(), "Properties");
+    checker.setDefaultTolerance(absoluteTolerance(magnitude * GMX_DOUBLE_EPS * ulpTol));
+    checker.checkSequence(force.begin(), force.end(), "Force");
+    checker.checkSequence(pot.begin(), pot.end(), "Potential");
+    checker.setDefaultTolerance(relativeToleranceAsUlp(1.0, ulpTol));
+    checker.checkSequence(pointBias.begin(), pointBias.end(), "PointBias");
+    checker.checkSequence(logPmfsum.begin(), logPmfsum.end(), "PointLogPmfsum");
+}
+
+/* Scan initial/final phase, MC/convolved force and update skip (not) allowed
+ * Both the convolving and skipping should not affect the bias and PMF.
+ * It would be nice if the test would explicitly check for this.
+ * Currently this is tested through identical reference data.
+ */
+INSTANTIATE_TEST_CASE_P(WithParameters,
+                        BiasFepLambdaStateTest,
+                        ::testing::Combine(::testing::Values(eawhgrowthLINEAR, eawhgrowthEXP_LINEAR),
+                                           ::testing::Values(eawhpotentialUMBRELLA, eawhpotentialCONVOLVED),
+                                           ::testing::Values(BiasParams::DisableUpdateSkips::yes,
+                                                             BiasParams::DisableUpdateSkips::no)));
+
+// Test that we detect coverings and exit the initial stage at the correct step
+TEST(BiasFepLambdaStateTest, DetectsCovering)
+{
+    const AwhFepLambdaStateTestParameters params =
+            getAwhFepLambdaTestParameters(eawhgrowthEXP_LINEAR, eawhpotentialCONVOLVED);
+
+    const double mdTimeStep = 0.1;
+
+    Bias bias(-1, params.awhParams, params.awhBiasParams, params.dimParams, params.beta, mdTimeStep,
+              1, "", Bias::ThisRankWillDoIO::No);
+
+    const int64_t exitStepRef = 380;
+
+    bool inInitialStage = bias.state().inInitialStage();
+
+    double                     energyNoiseMagnitude = 1.0;
+    double                     dhdlNoiseMagnitude   = 1.5;
+    std::default_random_engine randomEngine;
+    randomEngine.seed(1234);
+
+    /* Some energies to use as base values (to which some noise is added later on). */
+    std::vector<double> lambdaEnergyBase(numLambdaStates);
+    std::vector<double> lambdaDhdlBase(numLambdaStates);
+    const double        magnitude = 12.0;
+    for (int i = 0; i < numLambdaStates; i++)
+    {
+        lambdaEnergyBase[i] = magnitude * std::sin(i * 0.1);
+        lambdaDhdlBase[i]   = magnitude * std::cos(i * 0.1);
+    }
+
+    int64_t step;
+    /* Normally this loop exits at exitStepRef, but we extend with failure */
+    for (step = 0; step <= 2 * exitStepRef; step++)
+    {
+        /* Create some noise and add it to the base values */
+        std::vector<double> neighborLambdaEnergyNoise(numLambdaStates);
+        std::vector<double> neighborLambdaDhdlNoise(numLambdaStates);
+        randomArrayFill(neighborLambdaEnergyNoise, randomEngine, -energyNoiseMagnitude, energyNoiseMagnitude);
+        randomArrayFill(neighborLambdaDhdlNoise, randomEngine, -dhdlNoiseMagnitude, dhdlNoiseMagnitude);
+        std::vector<double> neighborLambdaEnergies(numLambdaStates);
+        std::vector<double> neighborLambdaDhdl(numLambdaStates);
+        for (int i = 0; i < numLambdaStates; i++)
+        {
+            neighborLambdaEnergies[i] = lambdaEnergyBase[i] + neighborLambdaEnergyNoise[i];
+            neighborLambdaDhdl[i]     = lambdaDhdlBase[i] + neighborLambdaDhdlNoise[i];
+        }
+
+        int      umbrellaGridpointIndex = bias.state().coordState().umbrellaGridpoint();
+        awh_dvec coordValue = { bias.getGridCoordValue(umbrellaGridpointIndex)[0], 0, 0, 0 };
+
+        double potential     = 0;
+        double potentialJump = 0;
+        bias.calcForceAndUpdateBias(coordValue, neighborLambdaEnergies, neighborLambdaDhdl,
+                                    &potential, &potentialJump, nullptr, nullptr, step, step,
+                                    params.awhParams.seed, nullptr);
+
+        inInitialStage = bias.state().inInitialStage();
+        if (!inInitialStage)
+        {
+            break;
+        }
+    }
+
+    EXPECT_EQ(false, inInitialStage);
+    if (!inInitialStage)
+    {
+        EXPECT_EQ(exitStepRef, step);
+    }
+}
+
+} // namespace test
+} // namespace gmx
similarity index 92%
rename from src/gromacs/awh/tests/grid.cpp
rename to src/gromacs/applied_forces/awh/tests/biasgrid.cpp
index 0e1f41723905978a790443213dc40fad3cd7e56c..9337d0632de0579ffe42d0fa2961e1c2eeca76f0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -34,7 +34,7 @@
  */
 #include "gmxpre.h"
 
-#include "gromacs/awh/grid.h"
+#include "gromacs/applied_forces/awh/biasgrid.h"
 
 #include <cmath>
 
@@ -55,9 +55,9 @@ namespace gmx
 namespace test
 {
 
-TEST(gridTest, neighborhood)
+TEST(biasGridTest, neighborhood)
 {
-    constexpr double pointsPerScope = Grid::c_scopeCutoff * Grid::c_numPointsPerSigma;
+    constexpr double pointsPerScope = BiasGrid::c_scopeCutoff * BiasGrid::c_numPointsPerSigma;
     GMX_RELEASE_ASSERT(std::abs(pointsPerScope - std::round(pointsPerScope)) > 1e-4,
                        "If the scope is close to an integer number of points, this test can be "
                        "unstable due to rounding issues");
@@ -86,10 +86,10 @@ TEST(gridTest, neighborhood)
 
     /* Set up dimParams to get about 15 points along each dimension */
     std::vector<DimParams> dimParams;
-    dimParams.emplace_back(conversionFactor, 1 / (beta * 0.7 * 0.7), beta);
-    dimParams.emplace_back(conversionFactor, 1 / (beta * 0.1 * 0.1), beta);
+    dimParams.push_back(DimParams::pullDimParams(conversionFactor, 1 / (beta * 0.7 * 0.7), beta));
+    dimParams.push_back(DimParams::pullDimParams(conversionFactor, 1 / (beta * 0.1 * 0.1), beta));
 
-    Grid grid(dimParams, awhDimParams.data());
+    BiasGrid grid(dimParams, awhDimParams.data());
 
     const int numPoints = grid.numPoints();
 
similarity index 91%
rename from src/gromacs/awh/tests/biasstate.cpp
rename to src/gromacs/applied_forces/awh/tests/biasstate.cpp
index ba2620ac0610ae5b815e035d6ea8964d8c3e74f3..a1e68b6460b6cb5c287c2a326b3c55304de49806 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -34,7 +34,7 @@
  */
 #include "gmxpre.h"
 
-#include "gromacs/awh/biasstate.h"
+#include "gromacs/applied_forces/awh/biasstate.h"
 
 #include <cmath>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "gromacs/awh/grid.h"
-#include "gromacs/awh/pointstate.h"
+#include "gromacs/applied_forces/awh/biasgrid.h"
+#include "gromacs/applied_forces/awh/pointstate.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/mdtypes/awh_params.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/smalloc.h"
 
 #include "testutils/testasserts.h"
@@ -91,6 +92,7 @@ static AwhTestParameters getAwhTestParameters()
     awhDimParams0.end            = 1.5;
     awhDimParams0.coordValueInit = awhDimParams0.origin;
     awhDimParams0.coverDiameter  = 0;
+    awhDimParams0.eCoordProvider = eawhcoordproviderPULL;
 
     AwhDimParams& awhDimParams1 = awhBiasParams.dimParams[1];
 
@@ -100,6 +102,7 @@ static AwhTestParameters getAwhTestParameters()
     awhDimParams1.end            = 1.3;
     awhDimParams1.coordValueInit = awhDimParams1.origin;
     awhDimParams1.coverDiameter  = 0;
+    awhDimParams1.eCoordProvider = eawhcoordproviderPULL;
 
     awhBiasParams.ndim                 = 2;
     awhBiasParams.eTarget              = eawhtargetCONSTANT;
@@ -135,9 +138,9 @@ public:
         const AwhParams&       awhParams     = params.awhParams;
         const AwhBiasParams&   awhBiasParams = awhParams.awhBiasParams[0];
         std::vector<DimParams> dimParams;
-        dimParams.emplace_back(1.0, 15.0, params.beta);
-        dimParams.emplace_back(1.0, 15.0, params.beta);
-        Grid       grid(dimParams, awhBiasParams.dimParams);
+        dimParams.push_back(DimParams::pullDimParams(1.0, 15.0, params.beta));
+        dimParams.push_back(DimParams::pullDimParams(1.0, 15.0, params.beta));
+        BiasGrid   grid(dimParams, awhBiasParams.dimParams);
         BiasParams biasParams(awhParams, awhBiasParams, dimParams, 1.0, 1.0,
                               BiasParams::DisableUpdateSkips::no, 1, grid.axis(), 0);
         biasState_ = std::make_unique<BiasState>(awhBiasParams, 1.0, dimParams, grid);
diff --git a/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_0.xml b/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_0.xml
new file mode 100644 (file)
index 0000000..b5f24a1
--- /dev/null
@@ -0,0 +1,1056 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Properties">
+    <Int Name="Length">3</Int>
+    <String>stage:           final</String>
+    <String>convolve forces: no</String>
+    <String>skip updates:    no</String>
+  </Sequence>
+  <Sequence Name="Force">
+    <Int Name="Length">501</Int>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.618843635924877</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.150299212615359</Real>
+    <Real>7.9854380397522462</Real>
+    <Real>7.9854380397522462</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.636619251512844</Real>
+    <Real>11.681645921797619</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>7.6024448199752941</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.876409458086407</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.495530169773147</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.681645921797619</Real>
+    <Real>12.636619251512844</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>8.8909476519676431</Real>
+    <Real>8.8909476519676431</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.876409458086407</Real>
+    <Real>8.4507330206875295</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>12.105272542330583</Real>
+    <Real>9.889523596492225</Real>
+    <Real>8.6319577743306972</Real>
+    <Real>12.105272542330583</Real>
+    <Real>13.362838364492113</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.785832700507417</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>7.8299236962943137</Real>
+    <Real>11.787055710383672</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.453526208106549</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.453526208106549</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>7.6024448199752941</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.207027337780797</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>7.6333818298061127</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.01580992711963</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.636619251512844</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.681645921797619</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>9.9060287852367068</Real>
+    <Real>9.3745732085462272</Real>
+    <Real>10.587906626094059</Real>
+    <Real>11.348116419520498</Real>
+    <Real>8.1647285389883741</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.785832700507417</Real>
+    <Real>8.6010207644998786</Real>
+    <Real>9.889523596492225</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.01580992711963</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.121219715705681</Real>
+    <Real>9.268714272099011</Real>
+    <Real>8.6010207644998786</Real>
+    <Real>11.348116419520498</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.847706720169054</Real>
+    <Real>7.6333818298061127</Real>
+    <Real>8.1647285389883741</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.876409458086407</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.847706720169054</Real>
+    <Real>8.6319577743306972</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.618843635924877</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.785832700507417</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>7.1844138499758952</Real>
+    <Real>5.895911017983547</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.121219715705681</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.587906626094059</Real>
+    <Real>8.4030946900865189</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.379053429351316</Real>
+    <Real>12.01580992711963</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.742029040098897</Real>
+    <Real>9.7652513140669175</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>8.4507330206875295</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.1284948162986037</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>9.889523596492225</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>6.5636045255826794</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.816769710338235</Real>
+    <Real>12.074335532499765</Real>
+    <Real>10.861002114951933</Real>
+    <Real>9.9060287852367068</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.074335532499765</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>6.4000390266268736</Real>
+    <Real>7.3550123563420993</Real>
+    <Real>13.362838364492113</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>8.4030946900865189</Real>
+    <Real>9.268714272099011</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.484463217937368</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.379053429351316</Real>
+    <Real>12.01580992711963</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.540556840057921</Real>
+    <Real>8.5187524437411657</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>11.452891705061662</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.1284948162986037</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>9.5724992829595852</Real>
+    <Real>10.861002114951933</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.150299212615359</Real>
+    <Real>9.4310873087449067</Real>
+  </Sequence>
+  <Sequence Name="Potential">
+    <Int Name="Length">501</Int>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+  </Sequence>
+  <Sequence Name="PointBias">
+    <Int Name="Length">16</Int>
+    <Real>-2.9778075646516879</Real>
+    <Real>-2.8306911400082684</Real>
+    <Real>-2.8452452501030732</Real>
+    <Real>-2.7818431701140449</Real>
+    <Real>-2.773287416778349</Real>
+    <Real>-2.7509964948610919</Real>
+    <Real>-2.7484461855866056</Real>
+    <Real>-2.7441738774779143</Real>
+    <Real>-2.742217227445416</Real>
+    <Real>-2.7402383329083655</Real>
+    <Real>-2.7388974973295919</Real>
+    <Real>-2.7389676556834073</Real>
+    <Real>-2.7380013180793359</Real>
+    <Real>-2.7362840769649939</Real>
+    <Real>-2.7371621526557868</Real>
+    <Real>-2.7365384787787761</Real>
+  </Sequence>
+  <Sequence Name="PointLogPmfsum">
+    <Int Name="Length">16</Int>
+    <Real>9.7102598263208186</Real>
+    <Real>9.5631434016773866</Real>
+    <Real>9.5776975117721843</Real>
+    <Real>9.5142954317831574</Real>
+    <Real>9.5057396784474673</Real>
+    <Real>9.4834487565302208</Real>
+    <Real>9.4808984472557309</Real>
+    <Real>9.4766261391470401</Real>
+    <Real>9.4746694891145395</Real>
+    <Real>9.4726905945774931</Real>
+    <Real>9.4713497589987163</Real>
+    <Real>9.4714199173525184</Real>
+    <Real>9.4704535797484493</Real>
+    <Real>9.4687363386341357</Real>
+    <Real>9.4696144143248997</Real>
+    <Real>9.4689907404478859</Real>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_1.xml b/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_1.xml
new file mode 100644 (file)
index 0000000..6e66f40
--- /dev/null
@@ -0,0 +1,1056 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Properties">
+    <Int Name="Length">3</Int>
+    <String>stage:           final</String>
+    <String>convolve forces: no</String>
+    <String>skip updates:    yes</String>
+  </Sequence>
+  <Sequence Name="Force">
+    <Int Name="Length">501</Int>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.618843635924877</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.150299212615359</Real>
+    <Real>7.9854380397522462</Real>
+    <Real>7.9854380397522462</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.636619251512844</Real>
+    <Real>11.681645921797619</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>7.6024448199752941</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.876409458086407</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.495530169773147</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.681645921797619</Real>
+    <Real>12.636619251512844</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>8.8909476519676431</Real>
+    <Real>8.8909476519676431</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.876409458086407</Real>
+    <Real>8.4507330206875295</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>12.105272542330583</Real>
+    <Real>9.889523596492225</Real>
+    <Real>8.6319577743306972</Real>
+    <Real>12.105272542330583</Real>
+    <Real>13.362838364492113</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.785832700507417</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>7.8299236962943137</Real>
+    <Real>11.787055710383672</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.453526208106549</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.453526208106549</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>7.6024448199752941</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.207027337780797</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>7.6333818298061127</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.01580992711963</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.636619251512844</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.681645921797619</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>9.9060287852367068</Real>
+    <Real>9.3745732085462272</Real>
+    <Real>10.587906626094059</Real>
+    <Real>11.348116419520498</Real>
+    <Real>8.1647285389883741</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.785832700507417</Real>
+    <Real>8.6010207644998786</Real>
+    <Real>9.889523596492225</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.01580992711963</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.121219715705681</Real>
+    <Real>9.268714272099011</Real>
+    <Real>8.6010207644998786</Real>
+    <Real>11.348116419520498</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.847706720169054</Real>
+    <Real>7.6333818298061127</Real>
+    <Real>8.1647285389883741</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.876409458086407</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.847706720169054</Real>
+    <Real>8.6319577743306972</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.618843635924877</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.785832700507417</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>7.1844138499758952</Real>
+    <Real>5.895911017983547</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.121219715705681</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.587906626094059</Real>
+    <Real>8.4030946900865189</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.379053429351316</Real>
+    <Real>12.01580992711963</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.742029040098897</Real>
+    <Real>9.7652513140669175</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>8.4507330206875295</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.1284948162986037</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>9.889523596492225</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>6.5636045255826794</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.816769710338235</Real>
+    <Real>12.074335532499765</Real>
+    <Real>10.861002114951933</Real>
+    <Real>9.9060287852367068</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.074335532499765</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>6.4000390266268736</Real>
+    <Real>7.3550123563420993</Real>
+    <Real>13.362838364492113</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>8.4030946900865189</Real>
+    <Real>9.268714272099011</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.484463217937368</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.379053429351316</Real>
+    <Real>12.01580992711963</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.540556840057921</Real>
+    <Real>8.5187524437411657</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>11.452891705061662</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.1284948162986037</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>9.5724992829595852</Real>
+    <Real>10.861002114951933</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.150299212615359</Real>
+    <Real>9.4310873087449067</Real>
+  </Sequence>
+  <Sequence Name="Potential">
+    <Int Name="Length">501</Int>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+  </Sequence>
+  <Sequence Name="PointBias">
+    <Int Name="Length">16</Int>
+    <Real>-2.9778075646516879</Real>
+    <Real>-2.8306911400082684</Real>
+    <Real>-2.8452452501030732</Real>
+    <Real>-2.7818431701140449</Real>
+    <Real>-2.773287416778349</Real>
+    <Real>-2.7509964948610919</Real>
+    <Real>-2.7484461855866056</Real>
+    <Real>-2.7441738774779143</Real>
+    <Real>-2.742217227445416</Real>
+    <Real>-2.7402383329083655</Real>
+    <Real>-2.7388974973295919</Real>
+    <Real>-2.7389676556834073</Real>
+    <Real>-2.7380013180793359</Real>
+    <Real>-2.7362840769649939</Real>
+    <Real>-2.7371621526557868</Real>
+    <Real>-2.7365384787787761</Real>
+  </Sequence>
+  <Sequence Name="PointLogPmfsum">
+    <Int Name="Length">16</Int>
+    <Real>9.7102598263208186</Real>
+    <Real>9.5631434016773866</Real>
+    <Real>9.5776975117721843</Real>
+    <Real>9.5142954317831574</Real>
+    <Real>9.5057396784474673</Real>
+    <Real>9.4834487565302208</Real>
+    <Real>9.4808984472557309</Real>
+    <Real>9.4766261391470401</Real>
+    <Real>9.4746694891145395</Real>
+    <Real>9.4726905945774931</Real>
+    <Real>9.4713497589987163</Real>
+    <Real>9.4714199173525184</Real>
+    <Real>9.4704535797484493</Real>
+    <Real>9.4687363386341357</Real>
+    <Real>9.4696144143248997</Real>
+    <Real>9.4689907404478859</Real>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_2.xml b/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_2.xml
new file mode 100644 (file)
index 0000000..b719c2a
--- /dev/null
@@ -0,0 +1,1056 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Properties">
+    <Int Name="Length">3</Int>
+    <String>stage:           final</String>
+    <String>convolve forces: yes</String>
+    <String>skip updates:    no</String>
+  </Sequence>
+  <Sequence Name="Force">
+    <Int Name="Length">501</Int>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331023233258199</Real>
+    <Real>11.331023233258199</Real>
+    <Real>11.331023233258197</Real>
+    <Real>11.331023233258197</Real>
+    <Real>11.331023233258199</Real>
+    <Real>11.331023233258197</Real>
+    <Real>11.331023233258197</Real>
+    <Real>11.331023233258199</Real>
+    <Real>11.331023233258199</Real>
+    <Real>11.331023233258197</Real>
+    <Real>11.330344329698057</Real>
+    <Real>11.330344329698057</Real>
+    <Real>11.330344329698063</Real>
+    <Real>11.330344329698057</Real>
+    <Real>11.330344329698057</Real>
+    <Real>11.330344329698063</Real>
+    <Real>11.330344329698061</Real>
+    <Real>11.330344329698063</Real>
+    <Real>11.330344329698057</Real>
+    <Real>11.330344329698061</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.32966409364419</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.32966409364419</Real>
+    <Real>11.32966409364419</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568165</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568171</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568165</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328299653923288</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923288</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923287</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.327615465144348</Real>
+    <Real>11.327615465144348</Real>
+    <Real>11.327615465144348</Real>
+    <Real>11.327615465144351</Real>
+    <Real>11.327615465144351</Real>
+    <Real>11.327615465144349</Real>
+    <Real>11.327615465144348</Real>
+    <Real>11.327615465144351</Real>
+    <Real>11.327615465144351</Real>
+    <Real>11.327615465144349</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647488</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829933</Real>
+    <Real>11.325555112069884</Real>
+    <Real>11.32555511206988</Real>
+    <Real>11.325555112069882</Real>
+    <Real>11.32555511206988</Real>
+    <Real>11.32555511206988</Real>
+    <Real>11.325555112069884</Real>
+    <Real>11.325555112069882</Real>
+    <Real>11.32555511206988</Real>
+    <Real>11.325555112069882</Real>
+    <Real>11.32555511206988</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726256</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726256</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726261</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138595</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.323483233626808</Real>
+    <Real>11.323483233626812</Real>
+    <Real>11.323483233626806</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.323483233626808</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322790080491069</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322790080491069</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322790080491067</Real>
+    <Real>11.322790080491069</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322095676011624</Real>
+    <Real>11.322095676011628</Real>
+    <Real>11.322095676011628</Real>
+    <Real>11.322095676011626</Real>
+    <Real>11.322095676011628</Real>
+    <Real>11.322095676011626</Real>
+    <Real>11.322095676011624</Real>
+    <Real>11.322095676011628</Real>
+    <Real>11.322095676011628</Real>
+    <Real>11.322095676011626</Real>
+    <Real>11.321400027448652</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.321400027448648</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.321400027448648</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.321400027448652</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.32070314204206</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.32070314204206</Real>
+    <Real>11.32070314204206</Real>
+    <Real>11.32070314204206</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011433</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011433</Real>
+    <Real>11.320005027011433</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.319305689555794</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.319305689555794</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.319305689555794</Real>
+    <Real>11.319305689555794</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.318605136853487</Real>
+    <Real>11.318605136853488</Real>
+    <Real>11.318605136853485</Real>
+    <Real>11.318605136853485</Real>
+    <Real>11.318605136853483</Real>
+    <Real>11.318605136853485</Real>
+    <Real>11.318605136853485</Real>
+    <Real>11.318605136853485</Real>
+    <Real>11.318605136853483</Real>
+    <Real>11.318605136853488</Real>
+    <Real>11.317903376062089</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062088</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062089</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318233</Real>
+    <Real>11.317200414318233</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318233</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318233</Real>
+    <Real>11.31649625873747</Real>
+    <Real>11.316496258737471</Real>
+    <Real>11.316496258737471</Real>
+    <Real>11.316496258737471</Real>
+    <Real>11.31649625873747</Real>
+    <Real>11.316496258737468</Real>
+    <Real>11.316496258737468</Real>
+    <Real>11.31649625873747</Real>
+    <Real>11.316496258737471</Real>
+    <Real>11.31649625873747</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315790916414207</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315790916414203</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315790916414207</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315790916414203</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421493</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.31437669981101</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.31437669981101</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.31437669981101</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.314376699811008</Real>
+    <Real>11.313667839612867</Real>
+    <Real>11.313667839612869</Real>
+    <Real>11.313667839612867</Real>
+    <Real>11.313667839612869</Real>
+    <Real>11.313667839612867</Real>
+    <Real>11.313667839612869</Real>
+    <Real>11.313667839612869</Real>
+    <Real>11.313667839612867</Real>
+    <Real>11.313667839612869</Real>
+    <Real>11.313667839612867</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312957820835571</Real>
+    <Real>11.312957820835567</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312957820835567</Real>
+    <Real>11.312957820835567</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465836</Real>
+    <Real>11.312246650465834</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.311534335468568</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.311534335468574</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.311534335468574</Real>
+    <Real>11.311534335468574</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786716</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786713</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341193</Real>
+    <Real>11.310106299341193</Real>
+    <Real>11.310106299341193</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.309390592030777</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.309390592030777</Real>
+    <Real>11.309390592030777</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30867376773204</Real>
+    <Real>11.308673767732042</Real>
+    <Real>11.308673767732042</Real>
+    <Real>11.308673767732039</Real>
+    <Real>11.30867376773204</Real>
+    <Real>11.308673767732039</Real>
+    <Real>11.30867376773204</Real>
+    <Real>11.308673767732039</Real>
+    <Real>11.308673767732039</Real>
+    <Real>11.308673767732042</Real>
+    <Real>11.307955833299259</Real>
+    <Real>11.307955833299259</Real>
+    <Real>11.307955833299257</Real>
+    <Real>11.307955833299259</Real>
+    <Real>11.307955833299257</Real>
+    <Real>11.30795583329926</Real>
+    <Real>11.307955833299257</Real>
+    <Real>11.307955833299259</Real>
+    <Real>11.307955833299259</Real>
+    <Real>11.307955833299257</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.307236795564311</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.307236795564311</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.307236795564311</Real>
+    <Real>11.307236795564311</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336639</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336639</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336639</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403141</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403143</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403143</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305073130528124</Real>
+    <Real>11.305073130528122</Real>
+    <Real>11.305073130528122</Real>
+    <Real>11.305073130528124</Real>
+    <Real>11.305073130528124</Real>
+    <Real>11.305073130528124</Real>
+    <Real>11.305073130528122</Real>
+    <Real>11.305073130528122</Real>
+    <Real>11.305073130528122</Real>
+    <Real>11.305073130528124</Real>
+    <Real>11.304349747453212</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453212</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453212</Real>
+    <Real>11.303625294897312</Real>
+    <Real>11.303625294897312</Real>
+    <Real>11.303625294897314</Real>
+    <Real>11.303625294897314</Real>
+    <Real>11.303625294897312</Real>
+    <Real>11.303625294897314</Real>
+    <Real>11.303625294897316</Real>
+    <Real>11.303625294897312</Real>
+    <Real>11.303625294897314</Real>
+    <Real>11.303625294897312</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556515</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556514</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.30217320810408</Real>
+    <Real>11.302173208104078</Real>
+    <Real>11.302173208104078</Real>
+    <Real>11.302173208104078</Real>
+    <Real>11.302173208104083</Real>
+    <Real>11.30217320810408</Real>
+    <Real>11.30217320810408</Real>
+    <Real>11.30217320810408</Real>
+    <Real>11.302173208104078</Real>
+    <Real>11.30217320810408</Real>
+    <Real>11.301445587190321</Real>
+    <Real>11.301445587190321</Real>
+    <Real>11.301445587190322</Real>
+    <Real>11.301445587190322</Real>
+    <Real>11.301445587190321</Real>
+    <Real>11.301445587190322</Real>
+    <Real>11.301445587190321</Real>
+    <Real>11.301445587190321</Real>
+    <Real>11.301445587190322</Real>
+    <Real>11.301445587190322</Real>
+    <Real>11.300716923442614</Real>
+    <Real>11.300716923442616</Real>
+    <Real>11.300716923442616</Real>
+    <Real>11.300716923442614</Real>
+    <Real>11.300716923442618</Real>
+    <Real>11.300716923442616</Real>
+    <Real>11.300716923442618</Real>
+    <Real>11.300716923442618</Real>
+    <Real>11.300716923442616</Real>
+    <Real>11.300716923442616</Real>
+    <Real>11.299987223465321</Real>
+    <Real>11.299987223465321</Real>
+    <Real>11.299987223465319</Real>
+    <Real>11.299987223465317</Real>
+    <Real>11.299987223465317</Real>
+    <Real>11.299987223465321</Real>
+    <Real>11.299987223465319</Real>
+    <Real>11.299987223465317</Real>
+    <Real>11.299987223465317</Real>
+    <Real>11.299987223465321</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123982</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123982</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853153</Real>
+    <Real>11.297791971853153</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853153</Real>
+    <Real>11.297791971853155</Real>
+  </Sequence>
+  <Sequence Name="Potential">
+    <Int Name="Length">501</Int>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9350826496477405</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.9350826496477405</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.938695180114502</Real>
+    <Real>6.938695180114502</Real>
+    <Real>6.9536280756541462</Real>
+    <Real>6.9536280756541462</Real>
+    <Real>6.938695180114502</Real>
+    <Real>6.9536280756541462</Real>
+    <Real>6.9536280756541462</Real>
+    <Real>6.9321955109035134</Real>
+    <Real>6.938695180114502</Real>
+    <Real>6.9536280756541462</Real>
+    <Real>6.9259116037118416</Real>
+    <Real>6.9646510320192867</Real>
+    <Real>6.9400207820232298</Real>
+    <Real>6.9280287976266317</Real>
+    <Real>6.9646510320192867</Real>
+    <Real>6.9400207820232298</Real>
+    <Real>6.9423093422450712</Real>
+    <Real>6.9312930026052877</Real>
+    <Real>6.9646510320192867</Real>
+    <Real>6.9423093422450712</Real>
+    <Real>6.9312419690440228</Real>
+    <Real>6.9756374517531592</Real>
+    <Real>6.9245740692373019</Real>
+    <Real>6.9312419690440228</Real>
+    <Real>6.9756374517531592</Real>
+    <Real>6.9756374517531592</Real>
+    <Real>6.9428765484924853</Real>
+    <Real>6.9756374517531592</Real>
+    <Real>6.9428765484924853</Real>
+    <Real>6.9263945791974448</Real>
+    <Real>6.986587520325271</Real>
+    <Real>6.9333150117622999</Real>
+    <Real>6.986587520325271</Real>
+    <Real>6.986587520325271</Real>
+    <Real>6.9495423449290836</Real>
+    <Real>6.945735286797925</Real>
+    <Real>6.986587520325271</Real>
+    <Real>6.9333150117622999</Real>
+    <Real>6.9495423449290836</Real>
+    <Real>6.9495423449290836</Real>
+    <Real>6.9311526469894602</Real>
+    <Real>6.9975014221008935</Real>
+    <Real>6.9226367029620848</Real>
+    <Real>6.9485969482946075</Real>
+    <Real>6.9975014221008935</Real>
+    <Real>6.9311526469894602</Real>
+    <Real>6.9975014221008935</Real>
+    <Real>6.9246127721517139</Real>
+    <Real>6.9485969482946075</Real>
+    <Real>6.9975014221008935</Real>
+    <Real>6.9514614847028646</Real>
+    <Real>6.9194210525355793</Real>
+    <Real>6.9514614847028646</Real>
+    <Real>7.008379340346317</Real>
+    <Real>7.008379340346317</Real>
+    <Real>6.9226159532699425</Real>
+    <Real>6.9514614847028646</Real>
+    <Real>6.919096801568168</Real>
+    <Real>7.008379340346317</Real>
+    <Real>6.956781229419633</Real>
+    <Real>6.9344747059632796</Real>
+    <Real>7.0192214572341385</Real>
+    <Real>6.9604027456134556</Real>
+    <Real>6.9604027456134556</Real>
+    <Real>6.9543288481064538</Real>
+    <Real>6.9176802841515483</Real>
+    <Real>6.9604027456134556</Real>
+    <Real>6.9543288481064538</Real>
+    <Real>6.9543288481064538</Real>
+    <Real>6.9543288481064538</Real>
+    <Real>6.9348700988278047</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>6.9310502634259841</Real>
+    <Real>6.9310244923635214</Real>
+    <Real>7.0407990101908151</Real>
+    <Real>6.9676496666088266</Real>
+    <Real>7.0407990101908151</Real>
+    <Real>7.0407990101908151</Real>
+    <Real>6.9138206706558911</Real>
+    <Real>6.9352698690036307</Real>
+    <Real>7.0407990101908151</Real>
+    <Real>6.9188622504434081</Real>
+    <Real>7.0407990101908151</Real>
+    <Real>6.9712749691089035</Real>
+    <Real>7.0515348051843443</Real>
+    <Real>7.0515348051843443</Real>
+    <Real>7.0515348051843443</Real>
+    <Real>6.9356739945682975</Real>
+    <Real>7.0515348051843443</Real>
+    <Real>6.9356739945682975</Real>
+    <Real>6.9712749691089035</Real>
+    <Real>6.9629474265393707</Real>
+    <Real>7.0515348051843443</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>6.9658256259655857</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>6.974901432188064</Real>
+    <Real>6.974901432188064</Real>
+    <Real>6.974901432188064</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>6.9785290058586202</Real>
+    <Real>6.9309720073590908</Real>
+    <Real>6.9364952246064915</Real>
+    <Real>6.9687064181923724</Real>
+    <Real>6.9785290058586202</Real>
+    <Real>6.9785290058586202</Real>
+    <Real>7.0729013214631653</Real>
+    <Real>6.9151333048789461</Real>
+    <Real>7.0729013214631653</Real>
+    <Real>6.9785290058586202</Real>
+    <Real>7.0835323952555651</Real>
+    <Real>6.9715897574449635</Real>
+    <Real>6.930962731158397</Real>
+    <Real>6.9369122856668062</Real>
+    <Real>6.9074644308484405</Real>
+    <Real>6.930962731158397</Real>
+    <Real>7.0835323952555651</Real>
+    <Real>6.9109914543830664</Real>
+    <Real>6.930962731158397</Real>
+    <Real>6.9715897574449635</Real>
+    <Real>6.9309575358793563</Real>
+    <Real>7.0941289127243268</Real>
+    <Real>6.9744755982989295</Real>
+    <Real>6.9857872873182316</Real>
+    <Real>6.9744755982989295</Real>
+    <Real>6.9857872873182316</Real>
+    <Real>6.9309575358793563</Real>
+    <Real>7.0941289127243268</Real>
+    <Real>6.9057639612350705</Real>
+    <Real>6.9857872873182316</Real>
+    <Real>6.9309564045253316</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>6.9773638956782769</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>6.9773638956782769</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>6.937759191981959</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>6.9930494227231401</Real>
+    <Real>7.1152189721104566</Real>
+    <Real>6.9930494227231401</Real>
+    <Real>6.9802546048535508</Real>
+    <Real>6.9930494227231401</Real>
+    <Real>7.1152189721104566</Real>
+    <Real>7.1152189721104566</Real>
+    <Real>7.1152189721104566</Real>
+    <Real>6.9930494227231401</Real>
+    <Real>6.9802546048535508</Real>
+    <Real>7.1257128581302176</Real>
+    <Real>6.9309662659068927</Real>
+    <Real>7.1257128581302176</Real>
+    <Real>7.1257128581302176</Real>
+    <Real>6.9966818155141111</Real>
+    <Real>6.9386230010395833</Real>
+    <Real>6.9112146562423593</Real>
+    <Real>7.1257128581302176</Real>
+    <Real>7.1257128581302176</Real>
+    <Real>6.983147681439938</Real>
+    <Real>6.9101171134988002</Real>
+    <Real>7.0003150285142848</Real>
+    <Real>6.9101171134988002</Real>
+    <Real>7.1361728760420888</Real>
+    <Real>6.8967423378901351</Real>
+    <Real>7.1361728760420888</Real>
+    <Real>6.9019810915888744</Real>
+    <Real>6.9077490290560517</Real>
+    <Real>6.9309772249499888</Real>
+    <Real>6.9101171134988002</Real>
+    <Real>6.8972899766738553</Real>
+    <Real>6.939503542615304</Real>
+    <Real>6.9309921805328853</Real>
+    <Real>6.9309921805328853</Real>
+    <Real>6.8972217991863474</Real>
+    <Real>6.9889407610185392</Real>
+    <Real>6.9889407610185392</Real>
+    <Real>7.1465991953145771</Real>
+    <Real>7.0039490148884296</Real>
+    <Real>6.939503542615304</Real>
+    <Real>6.9079306780776282</Real>
+    <Real>7.1569919843930165</Real>
+    <Real>7.1569919843930165</Real>
+    <Real>6.9918406769471355</Real>
+    <Real>6.9918406769471355</Real>
+    <Real>7.0075837282393323</Real>
+    <Real>6.9053092391503039</Real>
+    <Real>7.1569919843930165</Real>
+    <Real>7.1569919843930165</Real>
+    <Real>6.9399500352747578</Real>
+    <Real>6.9947427861557863</Real>
+    <Real>7.1673514107050718</Real>
+    <Real>6.9947427861557863</Real>
+    <Real>7.1673514107050718</Real>
+    <Real>7.0112191226047136</Real>
+    <Real>7.0112191226047136</Real>
+    <Real>7.1673514107050718</Real>
+    <Real>7.0112191226047136</Real>
+    <Real>7.1673514107050718</Real>
+    <Real>6.9404006478461442</Real>
+    <Real>7.1776776406662579</Real>
+    <Real>6.9408553594332991</Real>
+    <Real>6.9408553594332991</Real>
+    <Real>6.8921495253010248</Real>
+    <Real>6.9310608598530274</Real>
+    <Real>6.9057557304547013</Real>
+    <Real>7.0148551524541727</Real>
+    <Real>7.1776776406662579</Real>
+    <Real>6.9408553594332991</Real>
+    <Real>7.1776776406662579</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>7.0005534139852905</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>6.9413141492243646</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>7.0184917726861276</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>6.9413141492243646</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>7.0221289386247605</Real>
+    <Real>7.0221289386247605</Real>
+    <Real>7.0221289386247605</Real>
+    <Real>6.8888629740277239</Real>
+    <Real>6.9035922161528598</Real>
+    <Real>7.198231172170475</Real>
+    <Real>7.198231172170475</Real>
+    <Real>7.0034618482231439</Real>
+    <Real>7.0034618482231439</Real>
+    <Real>7.0221289386247605</Real>
+    <Real>7.0063723069712101</Real>
+    <Real>6.8992566403317666</Real>
+    <Real>7.0257666060169823</Real>
+    <Real>7.0257666060169823</Real>
+    <Real>6.8870940204278019</Real>
+    <Real>7.2084588015335029</Real>
+    <Real>6.9422438805910947</Real>
+    <Real>7.2084588015335029</Real>
+    <Real>7.2084588015335029</Real>
+    <Real>6.931164910528075</Real>
+    <Real>7.2186538901967126</Real>
+    <Real>6.9312073777179641</Real>
+    <Real>7.2186538901967126</Real>
+    <Real>7.0294047310293815</Real>
+    <Real>7.2186538901967126</Real>
+    <Real>6.9312073777179641</Real>
+    <Real>7.0092847488603303</Real>
+    <Real>7.2186538901967126</Real>
+    <Real>6.9312073777179641</Real>
+    <Real>7.2186538901967126</Real>
+    <Real>7.2288165995977565</Real>
+    <Real>7.2288165995977565</Real>
+    <Real>7.2288165995977565</Real>
+    <Real>7.0121991328468214</Real>
+    <Real>7.0330432702452139</Real>
+    <Real>7.2288165995977565</Real>
+    <Real>7.0330432702452139</Real>
+    <Real>7.0330432702452139</Real>
+    <Real>7.2288165995977565</Real>
+    <Real>6.9431896771297357</Real>
+    <Real>7.2389470901952997</Real>
+    <Real>6.9436685486986995</Real>
+    <Real>7.2389470901952997</Real>
+    <Real>7.2389470901952997</Real>
+    <Real>7.2389470901952997</Real>
+    <Real>7.0366821806613693</Real>
+    <Real>6.9313038897664061</Real>
+    <Real>7.0151154182105593</Real>
+    <Real>6.9436685486986995</Real>
+    <Real>7.2389470901952997</Real>
+    <Real>7.0180335645530558</Real>
+    <Real>7.2490455214745539</Real>
+    <Real>7.2490455214745539</Real>
+    <Real>6.8944623882789013</Real>
+    <Real>7.0403214196853634</Real>
+    <Real>7.2490455214745539</Real>
+    <Real>7.2490455214745539</Real>
+    <Real>7.2490455214745539</Real>
+    <Real>7.0403214196853634</Real>
+    <Real>7.0403214196853634</Real>
+    <Real>7.2591120519527959</Real>
+    <Real>6.9314157308460365</Real>
+    <Real>7.2591120519527959</Real>
+    <Real>7.2591120519527959</Real>
+    <Real>7.2591120519527959</Real>
+    <Real>6.9446381368835839</Real>
+    <Real>6.9314157308460365</Real>
+    <Real>6.9314157308460365</Real>
+    <Real>7.2591120519527959</Real>
+    <Real>7.0209535317955414</Real>
+    <Real>7.0238752801770463</Real>
+    <Real>7.2691468391848968</Real>
+    <Real>7.2691468391848968</Real>
+    <Real>7.2691468391848968</Real>
+    <Real>7.0238752801770463</Real>
+    <Real>6.8733235143785443</Real>
+    <Real>6.9314773595440453</Real>
+    <Real>7.0476007152220781</Real>
+    <Real>6.9451288131268747</Real>
+    <Real>6.9451288131268747</Real>
+    <Real>6.945623384026403</Real>
+    <Real>6.9315427723421088</Real>
+    <Real>6.8908943536503395</Real>
+    <Real>6.8753625856386886</Real>
+    <Real>7.2791500397688358</Real>
+    <Real>6.945623384026403</Real>
+    <Real>6.945623384026403</Real>
+    <Real>7.2791500397688358</Real>
+    <Real>7.2791500397688358</Real>
+    <Real>7.0267987702524826</Real>
+    <Real>7.0297239628907269</Real>
+    <Real>7.2891218093512187</Real>
+    <Real>7.2891218093512187</Real>
+    <Real>7.0548808242142069</Real>
+    <Real>6.8939964438791499</Real>
+    <Real>6.9461218296016414</Real>
+    <Real>7.0297239628907269</Real>
+    <Real>6.9461218296016414</Real>
+    <Real>6.8759399951857345</Real>
+    <Real>7.2891218093512187</Real>
+    <Real>6.8743414980824458</Real>
+    <Real>7.2990623026327821</Real>
+    <Real>7.0585210815525166</Real>
+    <Real>7.2990623026327821</Real>
+    <Real>6.8704810616007777</Real>
+    <Real>7.0326508192727122</Real>
+    <Real>6.946624129953971</Real>
+    <Real>7.2990623026327821</Real>
+    <Real>7.2990623026327821</Real>
+    <Real>7.0585210815525166</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>7.0621614203995051</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>7.0621614203995051</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>6.8727449480133256</Real>
+    <Real>7.0621614203995051</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>7.0385093695404244</Real>
+    <Real>6.868699289983712</Real>
+    <Real>7.3188500744000429</Real>
+    <Real>6.8643891419737848</Real>
+    <Real>6.947640215803526</Real>
+    <Real>7.0658018009535715</Real>
+    <Real>7.0658018009535715</Real>
+    <Real>6.9318419467710788</Real>
+    <Real>7.0385093695404244</Real>
+    <Real>6.8747693804070318</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>7.0414409873310913</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>6.9481539619109114</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>6.9319260422510389</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>6.9320138271064335</Real>
+    <Real>7.0443741166715634</Real>
+    <Real>7.0443741166715634</Real>
+    <Real>6.9486714840152324</Real>
+    <Real>7.3385145739679896</Real>
+    <Real>6.8620298140636971</Real>
+    <Real>7.0443741166715634</Real>
+    <Real>7.0443741166715634</Real>
+    <Real>7.0443741166715634</Real>
+    <Real>6.9486714840152324</Real>
+    <Real>7.0767228006280654</Real>
+    <Real>7.3483009735357276</Real>
+    <Real>7.3483009735357276</Real>
+    <Real>7.3483009735357276</Real>
+    <Real>7.3483009735357276</Real>
+    <Real>6.8826601818817075</Real>
+    <Real>6.8637209427570944</Real>
+    <Real>6.9491927626238406</Real>
+    <Real>7.3483009735357276</Real>
+    <Real>7.0767228006280654</Real>
+    <Real>7.3580570054513208</Real>
+    <Real>7.3580570054513208</Real>
+    <Real>7.0803629576885125</Real>
+    <Real>7.0803629576885125</Real>
+    <Real>6.9497177783245618</Real>
+    <Real>7.0803629576885125</Real>
+    <Real>6.932200402653228</Real>
+    <Real>7.3580570054513208</Real>
+    <Real>7.0803629576885125</Real>
+    <Real>7.3580570054513208</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>7.053182202618852</Real>
+    <Real>6.9322991623415735</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>7.0840029631931802</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>6.871414378143732</Real>
+    <Real>7.3774785583568416</Real>
+    <Real>6.9507789437545586</Real>
+    <Real>7.0876427796182568</Real>
+    <Real>6.9507789437545586</Real>
+    <Real>6.8589087640607822</Real>
+    <Real>7.3774785583568416</Real>
+    <Real>6.8700340918947287</Real>
+    <Real>7.3774785583568416</Real>
+    <Real>7.0876427796182568</Real>
+    <Real>6.8846262490558905</Real>
+    <Real>6.9513150550596485</Real>
+    <Real>6.9513150550596485</Real>
+    <Real>7.387144373112303</Real>
+    <Real>7.387144373112303</Real>
+    <Real>7.0912823698094938</Real>
+    <Real>7.387144373112303</Real>
+    <Real>7.0912823698094938</Real>
+    <Real>7.0912823698094938</Real>
+    <Real>7.387144373112303</Real>
+    <Real>6.8686560501175533</Real>
+    <Real>6.8584630660708683</Real>
+    <Real>7.3967804077575208</Real>
+    <Real>7.3967804077575208</Real>
+    <Real>6.8584630660708683</Real>
+    <Real>7.0949216969793474</Real>
+    <Real>7.3967804077575208</Real>
+    <Real>7.0949216969793474</Real>
+    <Real>7.0949216969793474</Real>
+    <Real>7.3967804077575208</Real>
+    <Real>7.3967804077575208</Real>
+    <Real>7.0985607247041518</Real>
+    <Real>7.0985607247041518</Real>
+    <Real>7.0649452509469137</Real>
+    <Real>7.4063868069497563</Real>
+    <Real>6.93273032146449</Real>
+    <Real>6.8614213685008387</Real>
+    <Real>7.0649452509469137</Real>
+    <Real>7.4063868069497563</Real>
+    <Real>6.93273032146449</Real>
+    <Real>7.0985607247041518</Real>
+    <Real>7.4159637144657733</Real>
+    <Real>7.1021994169212981</Real>
+    <Real>7.4159637144657733</Real>
+    <Real>7.4159637144657733</Real>
+    <Real>7.0678891553533436</Real>
+    <Real>7.0678891553533436</Real>
+    <Real>7.4159637144657733</Real>
+    <Real>7.0678891553533436</Real>
+    <Real>7.4159637144657733</Real>
+    <Real>7.1021994169212981</Real>
+    <Real>7.105837737926425</Real>
+    <Real>7.4255112732072002</Real>
+    <Real>7.0708342463868723</Real>
+    <Real>7.4255112732072002</Real>
+    <Real>6.8631662420165505</Real>
+    <Real>7.4255112732072002</Real>
+    <Real>7.4255112732072002</Real>
+    <Real>7.0708342463868723</Real>
+    <Real>7.0708342463868723</Real>
+    <Real>6.9534959129721798</Real>
+    <Real>7.4350296252058818</Real>
+    <Real>7.4350296252058818</Real>
+    <Real>7.1094756523706302</Real>
+    <Real>7.1094756523706302</Real>
+    <Real>7.4350296252058818</Real>
+    <Real>7.4350296252058818</Real>
+    <Real>7.4350296252058818</Real>
+    <Real>6.9330911902514938</Real>
+    <Real>7.1094756523706302</Real>
+    <Real>7.4350296252058818</Real>
+  </Sequence>
+  <Sequence Name="PointBias">
+    <Int Name="Length">16</Int>
+    <Real>-2.9778075646516879</Real>
+    <Real>-2.8306911400082684</Real>
+    <Real>-2.8452452501030732</Real>
+    <Real>-2.7818431701140449</Real>
+    <Real>-2.773287416778349</Real>
+    <Real>-2.7509964948610919</Real>
+    <Real>-2.7484461855866056</Real>
+    <Real>-2.7441738774779143</Real>
+    <Real>-2.742217227445416</Real>
+    <Real>-2.7402383329083655</Real>
+    <Real>-2.7388974973295919</Real>
+    <Real>-2.7389676556834073</Real>
+    <Real>-2.7380013180793359</Real>
+    <Real>-2.7362840769649939</Real>
+    <Real>-2.7371621526557868</Real>
+    <Real>-2.7365384787787761</Real>
+  </Sequence>
+  <Sequence Name="PointLogPmfsum">
+    <Int Name="Length">16</Int>
+    <Real>9.7102598263208186</Real>
+    <Real>9.5631434016773866</Real>
+    <Real>9.5776975117721843</Real>
+    <Real>9.5142954317831574</Real>
+    <Real>9.5057396784474673</Real>
+    <Real>9.4834487565302208</Real>
+    <Real>9.4808984472557309</Real>
+    <Real>9.4766261391470401</Real>
+    <Real>9.4746694891145395</Real>
+    <Real>9.4726905945774931</Real>
+    <Real>9.4713497589987163</Real>
+    <Real>9.4714199173525184</Real>
+    <Real>9.4704535797484493</Real>
+    <Real>9.4687363386341357</Real>
+    <Real>9.4696144143248997</Real>
+    <Real>9.4689907404478859</Real>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_3.xml b/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_3.xml
new file mode 100644 (file)
index 0000000..e7b7c5a
--- /dev/null
@@ -0,0 +1,1056 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Properties">
+    <Int Name="Length">3</Int>
+    <String>stage:           final</String>
+    <String>convolve forces: yes</String>
+    <String>skip updates:    yes</String>
+  </Sequence>
+  <Sequence Name="Force">
+    <Int Name="Length">501</Int>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331023233258199</Real>
+    <Real>11.331023233258199</Real>
+    <Real>11.331023233258197</Real>
+    <Real>11.331023233258197</Real>
+    <Real>11.331023233258199</Real>
+    <Real>11.331023233258197</Real>
+    <Real>11.331023233258197</Real>
+    <Real>11.331023233258199</Real>
+    <Real>11.331023233258199</Real>
+    <Real>11.331023233258197</Real>
+    <Real>11.330344329698057</Real>
+    <Real>11.330344329698057</Real>
+    <Real>11.330344329698063</Real>
+    <Real>11.330344329698057</Real>
+    <Real>11.330344329698057</Real>
+    <Real>11.330344329698063</Real>
+    <Real>11.330344329698061</Real>
+    <Real>11.330344329698063</Real>
+    <Real>11.330344329698057</Real>
+    <Real>11.330344329698061</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.32966409364419</Real>
+    <Real>11.329664093644189</Real>
+    <Real>11.32966409364419</Real>
+    <Real>11.32966409364419</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568165</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568171</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568165</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328982532568167</Real>
+    <Real>11.328299653923288</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923288</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923287</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.328299653923285</Real>
+    <Real>11.327615465144348</Real>
+    <Real>11.327615465144348</Real>
+    <Real>11.327615465144348</Real>
+    <Real>11.327615465144351</Real>
+    <Real>11.327615465144351</Real>
+    <Real>11.327615465144349</Real>
+    <Real>11.327615465144348</Real>
+    <Real>11.327615465144351</Real>
+    <Real>11.327615465144351</Real>
+    <Real>11.327615465144349</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647488</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326929973647486</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829936</Real>
+    <Real>11.326243186829933</Real>
+    <Real>11.325555112069884</Real>
+    <Real>11.32555511206988</Real>
+    <Real>11.325555112069882</Real>
+    <Real>11.32555511206988</Real>
+    <Real>11.32555511206988</Real>
+    <Real>11.325555112069884</Real>
+    <Real>11.325555112069882</Real>
+    <Real>11.32555511206988</Real>
+    <Real>11.325555112069882</Real>
+    <Real>11.32555511206988</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726256</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726256</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324865756726261</Real>
+    <Real>11.324865756726254</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138595</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.324175128138593</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.323483233626808</Real>
+    <Real>11.323483233626812</Real>
+    <Real>11.323483233626806</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.323483233626808</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.32348323362681</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322790080491069</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322790080491069</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322790080491067</Real>
+    <Real>11.322790080491069</Real>
+    <Real>11.322790080491071</Real>
+    <Real>11.322095676011624</Real>
+    <Real>11.322095676011628</Real>
+    <Real>11.322095676011628</Real>
+    <Real>11.322095676011626</Real>
+    <Real>11.322095676011628</Real>
+    <Real>11.322095676011626</Real>
+    <Real>11.322095676011624</Real>
+    <Real>11.322095676011628</Real>
+    <Real>11.322095676011628</Real>
+    <Real>11.322095676011626</Real>
+    <Real>11.321400027448652</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.321400027448648</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.321400027448648</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.321400027448652</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.32140002744865</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.32070314204206</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.32070314204206</Real>
+    <Real>11.32070314204206</Real>
+    <Real>11.32070314204206</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.320703142042062</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011433</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011433</Real>
+    <Real>11.320005027011433</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.320005027011435</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.319305689555794</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.319305689555794</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.319305689555794</Real>
+    <Real>11.319305689555794</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.31930568955579</Real>
+    <Real>11.318605136853487</Real>
+    <Real>11.318605136853488</Real>
+    <Real>11.318605136853485</Real>
+    <Real>11.318605136853485</Real>
+    <Real>11.318605136853483</Real>
+    <Real>11.318605136853485</Real>
+    <Real>11.318605136853485</Real>
+    <Real>11.318605136853485</Real>
+    <Real>11.318605136853483</Real>
+    <Real>11.318605136853488</Real>
+    <Real>11.317903376062089</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062088</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062091</Real>
+    <Real>11.317903376062089</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318233</Real>
+    <Real>11.317200414318233</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318233</Real>
+    <Real>11.317200414318231</Real>
+    <Real>11.317200414318233</Real>
+    <Real>11.31649625873747</Real>
+    <Real>11.316496258737471</Real>
+    <Real>11.316496258737471</Real>
+    <Real>11.316496258737471</Real>
+    <Real>11.31649625873747</Real>
+    <Real>11.316496258737468</Real>
+    <Real>11.316496258737468</Real>
+    <Real>11.31649625873747</Real>
+    <Real>11.316496258737471</Real>
+    <Real>11.31649625873747</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315790916414207</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315790916414203</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315790916414207</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315790916414203</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315790916414205</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421493</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.315084394421495</Real>
+    <Real>11.31437669981101</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.31437669981101</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.31437669981101</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.314376699811007</Real>
+    <Real>11.314376699811008</Real>
+    <Real>11.313667839612867</Real>
+    <Real>11.313667839612869</Real>
+    <Real>11.313667839612867</Real>
+    <Real>11.313667839612869</Real>
+    <Real>11.313667839612867</Real>
+    <Real>11.313667839612869</Real>
+    <Real>11.313667839612869</Real>
+    <Real>11.313667839612867</Real>
+    <Real>11.313667839612869</Real>
+    <Real>11.313667839612867</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312957820835571</Real>
+    <Real>11.312957820835567</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312957820835567</Real>
+    <Real>11.312957820835567</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312957820835569</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465836</Real>
+    <Real>11.312246650465834</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.312246650465838</Real>
+    <Real>11.311534335468568</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.311534335468574</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.31153433546857</Real>
+    <Real>11.311534335468574</Real>
+    <Real>11.311534335468574</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786716</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786714</Real>
+    <Real>11.310820882786713</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341193</Real>
+    <Real>11.310106299341193</Real>
+    <Real>11.310106299341193</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.310106299341191</Real>
+    <Real>11.309390592030777</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.309390592030777</Real>
+    <Real>11.309390592030777</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30939059203078</Real>
+    <Real>11.30867376773204</Real>
+    <Real>11.308673767732042</Real>
+    <Real>11.308673767732042</Real>
+    <Real>11.308673767732039</Real>
+    <Real>11.30867376773204</Real>
+    <Real>11.308673767732039</Real>
+    <Real>11.30867376773204</Real>
+    <Real>11.308673767732039</Real>
+    <Real>11.308673767732039</Real>
+    <Real>11.308673767732042</Real>
+    <Real>11.307955833299259</Real>
+    <Real>11.307955833299259</Real>
+    <Real>11.307955833299257</Real>
+    <Real>11.307955833299259</Real>
+    <Real>11.307955833299257</Real>
+    <Real>11.30795583329926</Real>
+    <Real>11.307955833299257</Real>
+    <Real>11.307955833299259</Real>
+    <Real>11.307955833299259</Real>
+    <Real>11.307955833299257</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.307236795564311</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.307236795564311</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.307236795564311</Real>
+    <Real>11.307236795564311</Real>
+    <Real>11.307236795564313</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336639</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336639</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336641</Real>
+    <Real>11.306516661336639</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403141</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403143</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403143</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305795437403145</Real>
+    <Real>11.305073130528124</Real>
+    <Real>11.305073130528122</Real>
+    <Real>11.305073130528122</Real>
+    <Real>11.305073130528124</Real>
+    <Real>11.305073130528124</Real>
+    <Real>11.305073130528124</Real>
+    <Real>11.305073130528122</Real>
+    <Real>11.305073130528122</Real>
+    <Real>11.305073130528122</Real>
+    <Real>11.305073130528124</Real>
+    <Real>11.304349747453212</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453212</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453214</Real>
+    <Real>11.304349747453212</Real>
+    <Real>11.303625294897312</Real>
+    <Real>11.303625294897312</Real>
+    <Real>11.303625294897314</Real>
+    <Real>11.303625294897314</Real>
+    <Real>11.303625294897312</Real>
+    <Real>11.303625294897314</Real>
+    <Real>11.303625294897316</Real>
+    <Real>11.303625294897312</Real>
+    <Real>11.303625294897314</Real>
+    <Real>11.303625294897312</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556515</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556514</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.302899779556517</Real>
+    <Real>11.30217320810408</Real>
+    <Real>11.302173208104078</Real>
+    <Real>11.302173208104078</Real>
+    <Real>11.302173208104078</Real>
+    <Real>11.302173208104083</Real>
+    <Real>11.30217320810408</Real>
+    <Real>11.30217320810408</Real>
+    <Real>11.30217320810408</Real>
+    <Real>11.302173208104078</Real>
+    <Real>11.30217320810408</Real>
+    <Real>11.301445587190321</Real>
+    <Real>11.301445587190321</Real>
+    <Real>11.301445587190322</Real>
+    <Real>11.301445587190322</Real>
+    <Real>11.301445587190321</Real>
+    <Real>11.301445587190322</Real>
+    <Real>11.301445587190321</Real>
+    <Real>11.301445587190321</Real>
+    <Real>11.301445587190322</Real>
+    <Real>11.301445587190322</Real>
+    <Real>11.300716923442614</Real>
+    <Real>11.300716923442616</Real>
+    <Real>11.300716923442616</Real>
+    <Real>11.300716923442614</Real>
+    <Real>11.300716923442618</Real>
+    <Real>11.300716923442616</Real>
+    <Real>11.300716923442618</Real>
+    <Real>11.300716923442618</Real>
+    <Real>11.300716923442616</Real>
+    <Real>11.300716923442616</Real>
+    <Real>11.299987223465321</Real>
+    <Real>11.299987223465321</Real>
+    <Real>11.299987223465319</Real>
+    <Real>11.299987223465317</Real>
+    <Real>11.299987223465317</Real>
+    <Real>11.299987223465321</Real>
+    <Real>11.299987223465319</Real>
+    <Real>11.299987223465317</Real>
+    <Real>11.299987223465317</Real>
+    <Real>11.299987223465321</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.299256493839719</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123982</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123984</Real>
+    <Real>11.298524741123982</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853153</Real>
+    <Real>11.297791971853153</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853155</Real>
+    <Real>11.297791971853153</Real>
+    <Real>11.297791971853155</Real>
+  </Sequence>
+  <Sequence Name="Potential">
+    <Int Name="Length">501</Int>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9350826496477405</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.9350826496477405</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.938695180114502</Real>
+    <Real>6.938695180114502</Real>
+    <Real>6.9536280756541462</Real>
+    <Real>6.9536280756541462</Real>
+    <Real>6.938695180114502</Real>
+    <Real>6.9536280756541462</Real>
+    <Real>6.9536280756541462</Real>
+    <Real>6.9321955109035134</Real>
+    <Real>6.938695180114502</Real>
+    <Real>6.9536280756541462</Real>
+    <Real>6.9259116037118416</Real>
+    <Real>6.9646510320192867</Real>
+    <Real>6.9400207820232298</Real>
+    <Real>6.9280287976266317</Real>
+    <Real>6.9646510320192867</Real>
+    <Real>6.9400207820232298</Real>
+    <Real>6.9423093422450712</Real>
+    <Real>6.9312930026052877</Real>
+    <Real>6.9646510320192867</Real>
+    <Real>6.9423093422450712</Real>
+    <Real>6.9312419690440228</Real>
+    <Real>6.9756374517531592</Real>
+    <Real>6.9245740692373019</Real>
+    <Real>6.9312419690440228</Real>
+    <Real>6.9756374517531592</Real>
+    <Real>6.9756374517531592</Real>
+    <Real>6.9428765484924853</Real>
+    <Real>6.9756374517531592</Real>
+    <Real>6.9428765484924853</Real>
+    <Real>6.9263945791974448</Real>
+    <Real>6.986587520325271</Real>
+    <Real>6.9333150117622999</Real>
+    <Real>6.986587520325271</Real>
+    <Real>6.986587520325271</Real>
+    <Real>6.9495423449290836</Real>
+    <Real>6.945735286797925</Real>
+    <Real>6.986587520325271</Real>
+    <Real>6.9333150117622999</Real>
+    <Real>6.9495423449290836</Real>
+    <Real>6.9495423449290836</Real>
+    <Real>6.9311526469894602</Real>
+    <Real>6.9975014221008935</Real>
+    <Real>6.9226367029620848</Real>
+    <Real>6.9485969482946075</Real>
+    <Real>6.9975014221008935</Real>
+    <Real>6.9311526469894602</Real>
+    <Real>6.9975014221008935</Real>
+    <Real>6.9246127721517139</Real>
+    <Real>6.9485969482946075</Real>
+    <Real>6.9975014221008935</Real>
+    <Real>6.9514614847028646</Real>
+    <Real>6.9194210525355793</Real>
+    <Real>6.9514614847028646</Real>
+    <Real>7.008379340346317</Real>
+    <Real>7.008379340346317</Real>
+    <Real>6.9226159532699425</Real>
+    <Real>6.9514614847028646</Real>
+    <Real>6.919096801568168</Real>
+    <Real>7.008379340346317</Real>
+    <Real>6.956781229419633</Real>
+    <Real>6.9344747059632796</Real>
+    <Real>7.0192214572341385</Real>
+    <Real>6.9604027456134556</Real>
+    <Real>6.9604027456134556</Real>
+    <Real>6.9543288481064538</Real>
+    <Real>6.9176802841515483</Real>
+    <Real>6.9604027456134556</Real>
+    <Real>6.9543288481064538</Real>
+    <Real>6.9543288481064538</Real>
+    <Real>6.9543288481064538</Real>
+    <Real>6.9348700988278047</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>7.0300279538485793</Real>
+    <Real>6.9310502634259841</Real>
+    <Real>6.9310244923635214</Real>
+    <Real>7.0407990101908151</Real>
+    <Real>6.9676496666088266</Real>
+    <Real>7.0407990101908151</Real>
+    <Real>7.0407990101908151</Real>
+    <Real>6.9138206706558911</Real>
+    <Real>6.9352698690036307</Real>
+    <Real>7.0407990101908151</Real>
+    <Real>6.9188622504434081</Real>
+    <Real>7.0407990101908151</Real>
+    <Real>6.9712749691089035</Real>
+    <Real>7.0515348051843443</Real>
+    <Real>7.0515348051843443</Real>
+    <Real>7.0515348051843443</Real>
+    <Real>6.9356739945682975</Real>
+    <Real>7.0515348051843443</Real>
+    <Real>6.9356739945682975</Real>
+    <Real>6.9712749691089035</Real>
+    <Real>6.9629474265393707</Real>
+    <Real>7.0515348051843443</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>6.9658256259655857</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>6.974901432188064</Real>
+    <Real>6.974901432188064</Real>
+    <Real>6.974901432188064</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>7.0622355166803619</Real>
+    <Real>6.9785290058586202</Real>
+    <Real>6.9309720073590908</Real>
+    <Real>6.9364952246064915</Real>
+    <Real>6.9687064181923724</Real>
+    <Real>6.9785290058586202</Real>
+    <Real>6.9785290058586202</Real>
+    <Real>7.0729013214631653</Real>
+    <Real>6.9151333048789461</Real>
+    <Real>7.0729013214631653</Real>
+    <Real>6.9785290058586202</Real>
+    <Real>7.0835323952555651</Real>
+    <Real>6.9715897574449635</Real>
+    <Real>6.930962731158397</Real>
+    <Real>6.9369122856668062</Real>
+    <Real>6.9074644308484405</Real>
+    <Real>6.930962731158397</Real>
+    <Real>7.0835323952555651</Real>
+    <Real>6.9109914543830664</Real>
+    <Real>6.930962731158397</Real>
+    <Real>6.9715897574449635</Real>
+    <Real>6.9309575358793563</Real>
+    <Real>7.0941289127243268</Real>
+    <Real>6.9744755982989295</Real>
+    <Real>6.9857872873182316</Real>
+    <Real>6.9744755982989295</Real>
+    <Real>6.9857872873182316</Real>
+    <Real>6.9309575358793563</Real>
+    <Real>7.0941289127243268</Real>
+    <Real>6.9057639612350705</Real>
+    <Real>6.9857872873182316</Real>
+    <Real>6.9309564045253316</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>6.9773638956782769</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>6.9773638956782769</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>6.937759191981959</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>7.1046910474856144</Real>
+    <Real>6.9930494227231401</Real>
+    <Real>7.1152189721104566</Real>
+    <Real>6.9930494227231401</Real>
+    <Real>6.9802546048535508</Real>
+    <Real>6.9930494227231401</Real>
+    <Real>7.1152189721104566</Real>
+    <Real>7.1152189721104566</Real>
+    <Real>7.1152189721104566</Real>
+    <Real>6.9930494227231401</Real>
+    <Real>6.9802546048535508</Real>
+    <Real>7.1257128581302176</Real>
+    <Real>6.9309662659068927</Real>
+    <Real>7.1257128581302176</Real>
+    <Real>7.1257128581302176</Real>
+    <Real>6.9966818155141111</Real>
+    <Real>6.9386230010395833</Real>
+    <Real>6.9112146562423593</Real>
+    <Real>7.1257128581302176</Real>
+    <Real>7.1257128581302176</Real>
+    <Real>6.983147681439938</Real>
+    <Real>6.9101171134988002</Real>
+    <Real>7.0003150285142848</Real>
+    <Real>6.9101171134988002</Real>
+    <Real>7.1361728760420888</Real>
+    <Real>6.8967423378901351</Real>
+    <Real>7.1361728760420888</Real>
+    <Real>6.9019810915888744</Real>
+    <Real>6.9077490290560517</Real>
+    <Real>6.9309772249499888</Real>
+    <Real>6.9101171134988002</Real>
+    <Real>6.8972899766738553</Real>
+    <Real>6.939503542615304</Real>
+    <Real>6.9309921805328853</Real>
+    <Real>6.9309921805328853</Real>
+    <Real>6.8972217991863474</Real>
+    <Real>6.9889407610185392</Real>
+    <Real>6.9889407610185392</Real>
+    <Real>7.1465991953145771</Real>
+    <Real>7.0039490148884296</Real>
+    <Real>6.939503542615304</Real>
+    <Real>6.9079306780776282</Real>
+    <Real>7.1569919843930165</Real>
+    <Real>7.1569919843930165</Real>
+    <Real>6.9918406769471355</Real>
+    <Real>6.9918406769471355</Real>
+    <Real>7.0075837282393323</Real>
+    <Real>6.9053092391503039</Real>
+    <Real>7.1569919843930165</Real>
+    <Real>7.1569919843930165</Real>
+    <Real>6.9399500352747578</Real>
+    <Real>6.9947427861557863</Real>
+    <Real>7.1673514107050718</Real>
+    <Real>6.9947427861557863</Real>
+    <Real>7.1673514107050718</Real>
+    <Real>7.0112191226047136</Real>
+    <Real>7.0112191226047136</Real>
+    <Real>7.1673514107050718</Real>
+    <Real>7.0112191226047136</Real>
+    <Real>7.1673514107050718</Real>
+    <Real>6.9404006478461442</Real>
+    <Real>7.1776776406662579</Real>
+    <Real>6.9408553594332991</Real>
+    <Real>6.9408553594332991</Real>
+    <Real>6.8921495253010248</Real>
+    <Real>6.9310608598530274</Real>
+    <Real>6.9057557304547013</Real>
+    <Real>7.0148551524541727</Real>
+    <Real>7.1776776406662579</Real>
+    <Real>6.9408553594332991</Real>
+    <Real>7.1776776406662579</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>7.0005534139852905</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>6.9413141492243646</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>7.0184917726861276</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>6.9413141492243646</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>7.1879708396854634</Real>
+    <Real>7.0221289386247605</Real>
+    <Real>7.0221289386247605</Real>
+    <Real>7.0221289386247605</Real>
+    <Real>6.8888629740277239</Real>
+    <Real>6.9035922161528598</Real>
+    <Real>7.198231172170475</Real>
+    <Real>7.198231172170475</Real>
+    <Real>7.0034618482231439</Real>
+    <Real>7.0034618482231439</Real>
+    <Real>7.0221289386247605</Real>
+    <Real>7.0063723069712101</Real>
+    <Real>6.8992566403317666</Real>
+    <Real>7.0257666060169823</Real>
+    <Real>7.0257666060169823</Real>
+    <Real>6.8870940204278019</Real>
+    <Real>7.2084588015335029</Real>
+    <Real>6.9422438805910947</Real>
+    <Real>7.2084588015335029</Real>
+    <Real>7.2084588015335029</Real>
+    <Real>6.931164910528075</Real>
+    <Real>7.2186538901967126</Real>
+    <Real>6.9312073777179641</Real>
+    <Real>7.2186538901967126</Real>
+    <Real>7.0294047310293815</Real>
+    <Real>7.2186538901967126</Real>
+    <Real>6.9312073777179641</Real>
+    <Real>7.0092847488603303</Real>
+    <Real>7.2186538901967126</Real>
+    <Real>6.9312073777179641</Real>
+    <Real>7.2186538901967126</Real>
+    <Real>7.2288165995977565</Real>
+    <Real>7.2288165995977565</Real>
+    <Real>7.2288165995977565</Real>
+    <Real>7.0121991328468214</Real>
+    <Real>7.0330432702452139</Real>
+    <Real>7.2288165995977565</Real>
+    <Real>7.0330432702452139</Real>
+    <Real>7.0330432702452139</Real>
+    <Real>7.2288165995977565</Real>
+    <Real>6.9431896771297357</Real>
+    <Real>7.2389470901952997</Real>
+    <Real>6.9436685486986995</Real>
+    <Real>7.2389470901952997</Real>
+    <Real>7.2389470901952997</Real>
+    <Real>7.2389470901952997</Real>
+    <Real>7.0366821806613693</Real>
+    <Real>6.9313038897664061</Real>
+    <Real>7.0151154182105593</Real>
+    <Real>6.9436685486986995</Real>
+    <Real>7.2389470901952997</Real>
+    <Real>7.0180335645530558</Real>
+    <Real>7.2490455214745539</Real>
+    <Real>7.2490455214745539</Real>
+    <Real>6.8944623882789013</Real>
+    <Real>7.0403214196853634</Real>
+    <Real>7.2490455214745539</Real>
+    <Real>7.2490455214745539</Real>
+    <Real>7.2490455214745539</Real>
+    <Real>7.0403214196853634</Real>
+    <Real>7.0403214196853634</Real>
+    <Real>7.2591120519527959</Real>
+    <Real>6.9314157308460365</Real>
+    <Real>7.2591120519527959</Real>
+    <Real>7.2591120519527959</Real>
+    <Real>7.2591120519527959</Real>
+    <Real>6.9446381368835839</Real>
+    <Real>6.9314157308460365</Real>
+    <Real>6.9314157308460365</Real>
+    <Real>7.2591120519527959</Real>
+    <Real>7.0209535317955414</Real>
+    <Real>7.0238752801770463</Real>
+    <Real>7.2691468391848968</Real>
+    <Real>7.2691468391848968</Real>
+    <Real>7.2691468391848968</Real>
+    <Real>7.0238752801770463</Real>
+    <Real>6.8733235143785443</Real>
+    <Real>6.9314773595440453</Real>
+    <Real>7.0476007152220781</Real>
+    <Real>6.9451288131268747</Real>
+    <Real>6.9451288131268747</Real>
+    <Real>6.945623384026403</Real>
+    <Real>6.9315427723421088</Real>
+    <Real>6.8908943536503395</Real>
+    <Real>6.8753625856386886</Real>
+    <Real>7.2791500397688358</Real>
+    <Real>6.945623384026403</Real>
+    <Real>6.945623384026403</Real>
+    <Real>7.2791500397688358</Real>
+    <Real>7.2791500397688358</Real>
+    <Real>7.0267987702524826</Real>
+    <Real>7.0297239628907269</Real>
+    <Real>7.2891218093512187</Real>
+    <Real>7.2891218093512187</Real>
+    <Real>7.0548808242142069</Real>
+    <Real>6.8939964438791499</Real>
+    <Real>6.9461218296016414</Real>
+    <Real>7.0297239628907269</Real>
+    <Real>6.9461218296016414</Real>
+    <Real>6.8759399951857345</Real>
+    <Real>7.2891218093512187</Real>
+    <Real>6.8743414980824458</Real>
+    <Real>7.2990623026327821</Real>
+    <Real>7.0585210815525166</Real>
+    <Real>7.2990623026327821</Real>
+    <Real>6.8704810616007777</Real>
+    <Real>7.0326508192727122</Real>
+    <Real>6.946624129953971</Real>
+    <Real>7.2990623026327821</Real>
+    <Real>7.2990623026327821</Real>
+    <Real>7.0585210815525166</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>7.0621614203995051</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>7.0621614203995051</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>6.8727449480133256</Real>
+    <Real>7.0621614203995051</Real>
+    <Real>7.3089716733738905</Real>
+    <Real>7.0385093695404244</Real>
+    <Real>6.868699289983712</Real>
+    <Real>7.3188500744000429</Real>
+    <Real>6.8643891419737848</Real>
+    <Real>6.947640215803526</Real>
+    <Real>7.0658018009535715</Real>
+    <Real>7.0658018009535715</Real>
+    <Real>6.9318419467710788</Real>
+    <Real>7.0385093695404244</Real>
+    <Real>6.8747693804070318</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>7.0414409873310913</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>6.9481539619109114</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>6.9319260422510389</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>7.3286976576073446</Real>
+    <Real>6.9320138271064335</Real>
+    <Real>7.0443741166715634</Real>
+    <Real>7.0443741166715634</Real>
+    <Real>6.9486714840152324</Real>
+    <Real>7.3385145739679896</Real>
+    <Real>6.8620298140636971</Real>
+    <Real>7.0443741166715634</Real>
+    <Real>7.0443741166715634</Real>
+    <Real>7.0443741166715634</Real>
+    <Real>6.9486714840152324</Real>
+    <Real>7.0767228006280654</Real>
+    <Real>7.3483009735357276</Real>
+    <Real>7.3483009735357276</Real>
+    <Real>7.3483009735357276</Real>
+    <Real>7.3483009735357276</Real>
+    <Real>6.8826601818817075</Real>
+    <Real>6.8637209427570944</Real>
+    <Real>6.9491927626238406</Real>
+    <Real>7.3483009735357276</Real>
+    <Real>7.0767228006280654</Real>
+    <Real>7.3580570054513208</Real>
+    <Real>7.3580570054513208</Real>
+    <Real>7.0803629576885125</Real>
+    <Real>7.0803629576885125</Real>
+    <Real>6.9497177783245618</Real>
+    <Real>7.0803629576885125</Real>
+    <Real>6.932200402653228</Real>
+    <Real>7.3580570054513208</Real>
+    <Real>7.0803629576885125</Real>
+    <Real>7.3580570054513208</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>7.053182202618852</Real>
+    <Real>6.9322991623415735</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>7.0840029631931802</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>7.3677828179479885</Real>
+    <Real>6.871414378143732</Real>
+    <Real>7.3774785583568416</Real>
+    <Real>6.9507789437545586</Real>
+    <Real>7.0876427796182568</Real>
+    <Real>6.9507789437545586</Real>
+    <Real>6.8589087640607822</Real>
+    <Real>7.3774785583568416</Real>
+    <Real>6.8700340918947287</Real>
+    <Real>7.3774785583568416</Real>
+    <Real>7.0876427796182568</Real>
+    <Real>6.8846262490558905</Real>
+    <Real>6.9513150550596485</Real>
+    <Real>6.9513150550596485</Real>
+    <Real>7.387144373112303</Real>
+    <Real>7.387144373112303</Real>
+    <Real>7.0912823698094938</Real>
+    <Real>7.387144373112303</Real>
+    <Real>7.0912823698094938</Real>
+    <Real>7.0912823698094938</Real>
+    <Real>7.387144373112303</Real>
+    <Real>6.8686560501175533</Real>
+    <Real>6.8584630660708683</Real>
+    <Real>7.3967804077575208</Real>
+    <Real>7.3967804077575208</Real>
+    <Real>6.8584630660708683</Real>
+    <Real>7.0949216969793474</Real>
+    <Real>7.3967804077575208</Real>
+    <Real>7.0949216969793474</Real>
+    <Real>7.0949216969793474</Real>
+    <Real>7.3967804077575208</Real>
+    <Real>7.3967804077575208</Real>
+    <Real>7.0985607247041518</Real>
+    <Real>7.0985607247041518</Real>
+    <Real>7.0649452509469137</Real>
+    <Real>7.4063868069497563</Real>
+    <Real>6.93273032146449</Real>
+    <Real>6.8614213685008387</Real>
+    <Real>7.0649452509469137</Real>
+    <Real>7.4063868069497563</Real>
+    <Real>6.93273032146449</Real>
+    <Real>7.0985607247041518</Real>
+    <Real>7.4159637144657733</Real>
+    <Real>7.1021994169212981</Real>
+    <Real>7.4159637144657733</Real>
+    <Real>7.4159637144657733</Real>
+    <Real>7.0678891553533436</Real>
+    <Real>7.0678891553533436</Real>
+    <Real>7.4159637144657733</Real>
+    <Real>7.0678891553533436</Real>
+    <Real>7.4159637144657733</Real>
+    <Real>7.1021994169212981</Real>
+    <Real>7.105837737926425</Real>
+    <Real>7.4255112732072002</Real>
+    <Real>7.0708342463868723</Real>
+    <Real>7.4255112732072002</Real>
+    <Real>6.8631662420165505</Real>
+    <Real>7.4255112732072002</Real>
+    <Real>7.4255112732072002</Real>
+    <Real>7.0708342463868723</Real>
+    <Real>7.0708342463868723</Real>
+    <Real>6.9534959129721798</Real>
+    <Real>7.4350296252058818</Real>
+    <Real>7.4350296252058818</Real>
+    <Real>7.1094756523706302</Real>
+    <Real>7.1094756523706302</Real>
+    <Real>7.4350296252058818</Real>
+    <Real>7.4350296252058818</Real>
+    <Real>7.4350296252058818</Real>
+    <Real>6.9330911902514938</Real>
+    <Real>7.1094756523706302</Real>
+    <Real>7.4350296252058818</Real>
+  </Sequence>
+  <Sequence Name="PointBias">
+    <Int Name="Length">16</Int>
+    <Real>-2.9778075646516879</Real>
+    <Real>-2.8306911400082684</Real>
+    <Real>-2.8452452501030732</Real>
+    <Real>-2.7818431701140449</Real>
+    <Real>-2.773287416778349</Real>
+    <Real>-2.7509964948610919</Real>
+    <Real>-2.7484461855866056</Real>
+    <Real>-2.7441738774779143</Real>
+    <Real>-2.742217227445416</Real>
+    <Real>-2.7402383329083655</Real>
+    <Real>-2.7388974973295919</Real>
+    <Real>-2.7389676556834073</Real>
+    <Real>-2.7380013180793359</Real>
+    <Real>-2.7362840769649939</Real>
+    <Real>-2.7371621526557868</Real>
+    <Real>-2.7365384787787761</Real>
+  </Sequence>
+  <Sequence Name="PointLogPmfsum">
+    <Int Name="Length">16</Int>
+    <Real>9.7102598263208186</Real>
+    <Real>9.5631434016773866</Real>
+    <Real>9.5776975117721843</Real>
+    <Real>9.5142954317831574</Real>
+    <Real>9.5057396784474673</Real>
+    <Real>9.4834487565302208</Real>
+    <Real>9.4808984472557309</Real>
+    <Real>9.4766261391470401</Real>
+    <Real>9.4746694891145395</Real>
+    <Real>9.4726905945774931</Real>
+    <Real>9.4713497589987163</Real>
+    <Real>9.4714199173525184</Real>
+    <Real>9.4704535797484493</Real>
+    <Real>9.4687363386341357</Real>
+    <Real>9.4696144143248997</Real>
+    <Real>9.4689907404478859</Real>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_4.xml b/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_4.xml
new file mode 100644 (file)
index 0000000..af1d64b
--- /dev/null
@@ -0,0 +1,1056 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Properties">
+    <Int Name="Length">3</Int>
+    <String>stage:           initial</String>
+    <String>convolve forces: no</String>
+    <String>skip updates:    no</String>
+  </Sequence>
+  <Sequence Name="Force">
+    <Int Name="Length">501</Int>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.618843635924877</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.150299212615359</Real>
+    <Real>7.9854380397522462</Real>
+    <Real>7.9854380397522462</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.636619251512844</Real>
+    <Real>11.681645921797619</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>7.6024448199752941</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.876409458086407</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.495530169773147</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.681645921797619</Real>
+    <Real>12.636619251512844</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>8.8909476519676431</Real>
+    <Real>8.8909476519676431</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.876409458086407</Real>
+    <Real>8.4507330206875295</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>12.105272542330583</Real>
+    <Real>9.889523596492225</Real>
+    <Real>8.6319577743306972</Real>
+    <Real>12.105272542330583</Real>
+    <Real>13.362838364492113</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.785832700507417</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>7.8299236962943137</Real>
+    <Real>11.787055710383672</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.453526208106549</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.453526208106549</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>7.6024448199752941</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.207027337780797</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>7.6333818298061127</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.01580992711963</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.636619251512844</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.681645921797619</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>9.9060287852367068</Real>
+    <Real>9.3745732085462272</Real>
+    <Real>10.587906626094059</Real>
+    <Real>11.348116419520498</Real>
+    <Real>8.1647285389883741</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.785832700507417</Real>
+    <Real>8.6010207644998786</Real>
+    <Real>9.889523596492225</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.01580992711963</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.121219715705681</Real>
+    <Real>9.268714272099011</Real>
+    <Real>8.6010207644998786</Real>
+    <Real>11.348116419520498</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.847706720169054</Real>
+    <Real>7.6333818298061127</Real>
+    <Real>8.1647285389883741</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.876409458086407</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.847706720169054</Real>
+    <Real>8.6319577743306972</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.618843635924877</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.785832700507417</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>7.1844138499758952</Real>
+    <Real>5.895911017983547</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.121219715705681</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.587906626094059</Real>
+    <Real>8.4030946900865189</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.379053429351316</Real>
+    <Real>12.01580992711963</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.742029040098897</Real>
+    <Real>9.7652513140669175</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>8.4507330206875295</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.1284948162986037</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>9.889523596492225</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>6.5636045255826794</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.816769710338235</Real>
+    <Real>12.074335532499765</Real>
+    <Real>10.861002114951933</Real>
+    <Real>9.9060287852367068</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.074335532499765</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>6.4000390266268736</Real>
+    <Real>7.3550123563420993</Real>
+    <Real>13.362838364492113</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>8.4030946900865189</Real>
+    <Real>9.268714272099011</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.484463217937368</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.379053429351316</Real>
+    <Real>12.01580992711963</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.540556840057921</Real>
+    <Real>8.5187524437411657</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>11.452891705061662</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.1284948162986037</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>9.5724992829595852</Real>
+    <Real>10.861002114951933</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.150299212615359</Real>
+    <Real>9.4310873087449067</Real>
+  </Sequence>
+  <Sequence Name="Potential">
+    <Int Name="Length">501</Int>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+  </Sequence>
+  <Sequence Name="PointBias">
+    <Int Name="Length">16</Int>
+    <Real>-2.9812350510475558</Real>
+    <Real>-2.8317587119323302</Real>
+    <Real>-2.8465623256890895</Real>
+    <Real>-2.7820458005979609</Real>
+    <Real>-2.7733340764443373</Real>
+    <Real>-2.7506303517910191</Real>
+    <Real>-2.748032213357384</Real>
+    <Real>-2.7436795022898046</Real>
+    <Real>-2.7416859119553316</Real>
+    <Real>-2.7396695821731636</Real>
+    <Real>-2.7383033387374733</Real>
+    <Real>-2.7383748274017918</Real>
+    <Real>-2.7373901581787741</Real>
+    <Real>-2.7356402960808746</Real>
+    <Real>-2.7365350589678044</Real>
+    <Real>-2.7358995341543086</Real>
+  </Sequence>
+  <Sequence Name="PointLogPmfsum">
+    <Int Name="Length">16</Int>
+    <Real>9.684978457071038</Real>
+    <Real>9.5355021179558346</Real>
+    <Real>9.550305731712589</Real>
+    <Real>9.48578920662146</Real>
+    <Real>9.4770774824678394</Real>
+    <Real>9.4543737578145386</Real>
+    <Real>9.4517756193808804</Real>
+    <Real>9.4474229083133299</Real>
+    <Real>9.4454293179788316</Real>
+    <Real>9.4434129881966662</Real>
+    <Real>9.4420467447610044</Real>
+    <Real>9.4421182334253082</Real>
+    <Real>9.4411335642022838</Real>
+    <Real>9.4393837021043776</Real>
+    <Real>9.4402784649912928</Real>
+    <Real>9.4396429401778015</Real>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_5.xml b/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_5.xml
new file mode 100644 (file)
index 0000000..b9d1af2
--- /dev/null
@@ -0,0 +1,1056 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Properties">
+    <Int Name="Length">3</Int>
+    <String>stage:           initial</String>
+    <String>convolve forces: no</String>
+    <String>skip updates:    yes</String>
+  </Sequence>
+  <Sequence Name="Force">
+    <Int Name="Length">501</Int>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.618843635924877</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.150299212615359</Real>
+    <Real>7.9854380397522462</Real>
+    <Real>7.9854380397522462</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.636619251512844</Real>
+    <Real>11.681645921797619</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>7.6024448199752941</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.876409458086407</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.495530169773147</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.681645921797619</Real>
+    <Real>12.636619251512844</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>8.8909476519676431</Real>
+    <Real>8.8909476519676431</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.876409458086407</Real>
+    <Real>8.4507330206875295</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>12.105272542330583</Real>
+    <Real>9.889523596492225</Real>
+    <Real>8.6319577743306972</Real>
+    <Real>12.105272542330583</Real>
+    <Real>13.362838364492113</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.785832700507417</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>7.8299236962943137</Real>
+    <Real>11.787055710383672</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.453526208106549</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.453526208106549</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>7.6024448199752941</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.207027337780797</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>7.6333818298061127</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.01580992711963</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.636619251512844</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.681645921797619</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>9.9060287852367068</Real>
+    <Real>9.3745732085462272</Real>
+    <Real>10.587906626094059</Real>
+    <Real>11.348116419520498</Real>
+    <Real>8.1647285389883741</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.785832700507417</Real>
+    <Real>8.6010207644998786</Real>
+    <Real>9.889523596492225</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.01580992711963</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.105272542330583</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.92143612837118</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.121219715705681</Real>
+    <Real>9.268714272099011</Real>
+    <Real>8.6010207644998786</Real>
+    <Real>11.348116419520498</Real>
+    <Real>11.379053429351316</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.847706720169054</Real>
+    <Real>7.6333818298061127</Real>
+    <Real>8.1647285389883741</Real>
+    <Real>11.681645921797619</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.876409458086407</Real>
+    <Real>10.618843635924877</Real>
+    <Real>10.847706720169054</Real>
+    <Real>8.6319577743306972</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.105272542330583</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.816769710338235</Real>
+    <Real>12.074335532499765</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>10.618843635924877</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.785832700507417</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>7.1844138499758952</Real>
+    <Real>5.895911017983547</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.484463217937368</Real>
+    <Real>12.121219715705681</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.453526208106549</Real>
+    <Real>10.587906626094059</Real>
+    <Real>8.4030946900865189</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>11.787055710383672</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.379053429351316</Real>
+    <Real>12.01580992711963</Real>
+    <Real>12.742029040098897</Real>
+    <Real>12.742029040098897</Real>
+    <Real>9.7652513140669175</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>7.4957596909723048</Real>
+    <Real>8.4507330206875295</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.1284948162986037</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>9.889523596492225</Real>
+    <Real>8.9345502667770003</Real>
+    <Real>6.2294405202606695</Real>
+    <Real>6.5636045255826794</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.847706720169054</Real>
+    <Real>10.816769710338235</Real>
+    <Real>12.074335532499765</Real>
+    <Real>10.861002114951933</Real>
+    <Real>9.9060287852367068</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.11936220278454</Real>
+    <Real>12.074335532499765</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>6.4000390266268736</Real>
+    <Real>7.3550123563420993</Real>
+    <Real>13.362838364492113</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.92143612837118</Real>
+    <Real>8.4030946900865189</Real>
+    <Real>9.268714272099011</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.484463217937368</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.074335532499765</Real>
+    <Real>11.11936220278454</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.484463217937368</Real>
+    <Real>11.484463217937368</Real>
+    <Real>8.2701383275744256</Real>
+    <Real>7.9359743222524166</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.379053429351316</Real>
+    <Real>12.01580992711963</Real>
+    <Real>12.121219715705681</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.540556840057921</Real>
+    <Real>8.5187524437411657</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>11.452891705061662</Real>
+    <Real>9.4310873087449067</Real>
+    <Real>9.1284948162986037</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>12.105272542330583</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>9.5724992829595852</Real>
+    <Real>10.861002114951933</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.150299212615359</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>10.540556840057921</Real>
+    <Real>10.540556840057921</Real>
+    <Real>11.452891705061662</Real>
+    <Real>12.407865034776886</Real>
+    <Real>13.362838364492113</Real>
+    <Real>12.742029040098897</Real>
+    <Real>11.787055710383672</Real>
+    <Real>11.452891705061662</Real>
+    <Real>11.150299212615359</Real>
+    <Real>10.847706720169054</Real>
+    <Real>11.150299212615359</Real>
+    <Real>12.407865034776886</Real>
+    <Real>12.407865034776886</Real>
+    <Real>11.11936220278454</Real>
+    <Real>10.816769710338235</Real>
+    <Real>11.150299212615359</Real>
+    <Real>9.4310873087449067</Real>
+  </Sequence>
+  <Sequence Name="Potential">
+    <Int Name="Length">501</Int>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+    <Real>0</Real>
+  </Sequence>
+  <Sequence Name="PointBias">
+    <Int Name="Length">16</Int>
+    <Real>-2.9812350510475558</Real>
+    <Real>-2.8317587119323302</Real>
+    <Real>-2.8465623256890895</Real>
+    <Real>-2.7820458005979609</Real>
+    <Real>-2.7733340764443373</Real>
+    <Real>-2.7506303517910191</Real>
+    <Real>-2.748032213357384</Real>
+    <Real>-2.7436795022898046</Real>
+    <Real>-2.7416859119553316</Real>
+    <Real>-2.7396695821731636</Real>
+    <Real>-2.7383033387374733</Real>
+    <Real>-2.7383748274017918</Real>
+    <Real>-2.7373901581787741</Real>
+    <Real>-2.7356402960808746</Real>
+    <Real>-2.7365350589678044</Real>
+    <Real>-2.7358995341543086</Real>
+  </Sequence>
+  <Sequence Name="PointLogPmfsum">
+    <Int Name="Length">16</Int>
+    <Real>9.684978457071038</Real>
+    <Real>9.5355021179558346</Real>
+    <Real>9.550305731712589</Real>
+    <Real>9.48578920662146</Real>
+    <Real>9.4770774824678394</Real>
+    <Real>9.4543737578145386</Real>
+    <Real>9.4517756193808804</Real>
+    <Real>9.4474229083133299</Real>
+    <Real>9.4454293179788316</Real>
+    <Real>9.4434129881966662</Real>
+    <Real>9.4420467447610044</Real>
+    <Real>9.4421182334253082</Real>
+    <Real>9.4411335642022838</Real>
+    <Real>9.4393837021043776</Real>
+    <Real>9.4402784649912928</Real>
+    <Real>9.4396429401778015</Real>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_6.xml b/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_6.xml
new file mode 100644 (file)
index 0000000..ff53a21
--- /dev/null
@@ -0,0 +1,1056 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Properties">
+    <Int Name="Length">3</Int>
+    <String>stage:           initial</String>
+    <String>convolve forces: yes</String>
+    <String>skip updates:    no</String>
+  </Sequence>
+  <Sequence Name="Force">
+    <Int Name="Length">501</Int>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331022709191608</Real>
+    <Real>11.331022709191608</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.331022709191608</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.331022709191608</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.330342753988663</Real>
+    <Real>11.330342753988663</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.330342753988663</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.330342753988663</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235024</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235024</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235024</Real>
+    <Real>11.329660935235022</Real>
+    <Real>11.328977256950315</Real>
+    <Real>11.328977256950319</Real>
+    <Real>11.328977256950315</Real>
+    <Real>11.328977256950315</Real>
+    <Real>11.328977256950319</Real>
+    <Real>11.328977256950321</Real>
+    <Real>11.328977256950315</Real>
+    <Real>11.328977256950319</Real>
+    <Real>11.328977256950319</Real>
+    <Real>11.328977256950319</Real>
+    <Real>11.328291723164707</Real>
+    <Real>11.328291723164703</Real>
+    <Real>11.328291723164709</Real>
+    <Real>11.328291723164709</Real>
+    <Real>11.328291723164703</Real>
+    <Real>11.328291723164707</Real>
+    <Real>11.328291723164703</Real>
+    <Real>11.328291723164707</Real>
+    <Real>11.328291723164709</Real>
+    <Real>11.328291723164703</Real>
+    <Real>11.327604337918583</Real>
+    <Real>11.327604337918581</Real>
+    <Real>11.327604337918583</Real>
+    <Real>11.327604337918585</Real>
+    <Real>11.327604337918585</Real>
+    <Real>11.327604337918583</Real>
+    <Real>11.327604337918583</Real>
+    <Real>11.327604337918583</Real>
+    <Real>11.327604337918585</Real>
+    <Real>11.327604337918585</Real>
+    <Real>11.326915105262364</Real>
+    <Real>11.326915105262362</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.326915105262364</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.326224029256217</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.325531113969872</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.325531113969872</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.325531113969873</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.325531113969872</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.324836363482333</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482333</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.324139781881673</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.324139781881668</Real>
+    <Real>11.324139781881668</Real>
+    <Real>11.324139781881668</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.32344137326479</Real>
+    <Real>11.323441373264787</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.32344137326479</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.322741141737211</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322741141737211</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322741141737211</Real>
+    <Real>11.322741141737211</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322039091412815</Real>
+    <Real>11.322039091412812</Real>
+    <Real>11.322039091412815</Real>
+    <Real>11.322039091412814</Real>
+    <Real>11.322039091412815</Real>
+    <Real>11.322039091412814</Real>
+    <Real>11.322039091412815</Real>
+    <Real>11.322039091412812</Real>
+    <Real>11.322039091412815</Real>
+    <Real>11.322039091412814</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.321335226413646</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.321335226413646</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.32133522641365</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.320629550869695</Real>
+    <Real>11.3206295508697</Real>
+    <Real>11.320629550869695</Real>
+    <Real>11.320629550869693</Real>
+    <Real>11.320629550869695</Real>
+    <Real>11.3206295508697</Real>
+    <Real>11.3206295508697</Real>
+    <Real>11.3206295508697</Real>
+    <Real>11.320629550869695</Real>
+    <Real>11.320629550869693</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918663</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918663</Real>
+    <Real>11.31921278470573</Real>
+    <Real>11.31921278470573</Real>
+    <Real>11.31921278470573</Real>
+    <Real>11.319212784705732</Real>
+    <Real>11.319212784705732</Real>
+    <Real>11.319212784705732</Real>
+    <Real>11.31921278470573</Real>
+    <Real>11.319212784705732</Real>
+    <Real>11.319212784705732</Real>
+    <Real>11.31921278470573</Real>
+    <Real>11.318501702383397</Real>
+    <Real>11.318501702383397</Real>
+    <Real>11.318501702383395</Real>
+    <Real>11.318501702383395</Real>
+    <Real>11.318501702383395</Real>
+    <Real>11.318501702383394</Real>
+    <Real>11.318501702383394</Real>
+    <Real>11.318501702383392</Real>
+    <Real>11.318501702383395</Real>
+    <Real>11.318501702383397</Real>
+    <Real>11.317788826111206</Real>
+    <Real>11.317788826111208</Real>
+    <Real>11.317788826111208</Real>
+    <Real>11.31778882611121</Real>
+    <Real>11.31778882611121</Real>
+    <Real>11.317788826111212</Real>
+    <Real>11.317788826111206</Real>
+    <Real>11.317788826111208</Real>
+    <Real>11.317788826111208</Real>
+    <Real>11.317788826111205</Real>
+    <Real>11.317074160055597</Real>
+    <Real>11.317074160055595</Real>
+    <Real>11.317074160055597</Real>
+    <Real>11.317074160055595</Real>
+    <Real>11.317074160055599</Real>
+    <Real>11.317074160055599</Real>
+    <Real>11.317074160055595</Real>
+    <Real>11.317074160055599</Real>
+    <Real>11.317074160055595</Real>
+    <Real>11.317074160055597</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.316357708389637</Real>
+    <Real>11.31635770838964</Real>
+    <Real>11.31635770838964</Real>
+    <Real>11.316357708389637</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292871</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951071</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951071</Real>
+    <Real>11.314919464951071</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556083</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.31419768155609</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.312748812402932</Real>
+    <Real>11.312748812402932</Real>
+    <Real>11.312748812402932</Real>
+    <Real>11.312748812402933</Real>
+    <Real>11.312748812402933</Real>
+    <Real>11.312748812402932</Real>
+    <Real>11.312748812402933</Real>
+    <Real>11.312748812402933</Real>
+    <Real>11.312748812402932</Real>
+    <Real>11.312748812402935</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.312021735056918</Real>
+    <Real>11.312021735056916</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31129290148162</Real>
+    <Real>11.311292901481616</Real>
+    <Real>11.311292901481616</Real>
+    <Real>11.31129290148162</Real>
+    <Real>11.31129290148162</Real>
+    <Real>11.311292901481616</Real>
+    <Real>11.311292901481616</Real>
+    <Real>11.311292901481616</Real>
+    <Real>11.31129290148162</Real>
+    <Real>11.31129290148162</Real>
+    <Real>11.31056231589619</Real>
+    <Real>11.310562315896188</Real>
+    <Real>11.31056231589619</Real>
+    <Real>11.31056231589619</Real>
+    <Real>11.31056231589619</Real>
+    <Real>11.310562315896192</Real>
+    <Real>11.310562315896188</Real>
+    <Real>11.310562315896188</Real>
+    <Real>11.31056231589619</Real>
+    <Real>11.310562315896192</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524677</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524681</Real>
+    <Real>11.309829982524681</Real>
+    <Real>11.30909590559585</Real>
+    <Real>11.309095905595852</Real>
+    <Real>11.30909590559585</Real>
+    <Real>11.30909590559585</Real>
+    <Real>11.309095905595854</Real>
+    <Real>11.30909590559585</Real>
+    <Real>11.30909590559585</Real>
+    <Real>11.309095905595854</Real>
+    <Real>11.309095905595854</Real>
+    <Real>11.309095905595852</Real>
+    <Real>11.308360089342983</Real>
+    <Real>11.308360089342981</Real>
+    <Real>11.308360089342981</Real>
+    <Real>11.30836008934298</Real>
+    <Real>11.30836008934298</Real>
+    <Real>11.308360089342981</Real>
+    <Real>11.308360089342983</Real>
+    <Real>11.308360089342981</Real>
+    <Real>11.308360089342983</Real>
+    <Real>11.308360089342981</Real>
+    <Real>11.307622538003702</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003701</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003701</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003701</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819795</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819795</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819795</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306142247037041</Real>
+    <Real>11.306142247037043</Real>
+    <Real>11.306142247037045</Real>
+    <Real>11.306142247037045</Real>
+    <Real>11.306142247037043</Real>
+    <Real>11.306142247037043</Real>
+    <Real>11.306142247037043</Real>
+    <Real>11.306142247037043</Real>
+    <Real>11.306142247037041</Real>
+    <Real>11.306142247037046</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905034</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905034</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.304655066677002</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677004</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677004</Real>
+    <Real>11.303909480230603</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230603</Real>
+    <Real>11.30316276388869</Real>
+    <Real>11.30316276388869</Real>
+    <Real>11.303162763888688</Real>
+    <Real>11.303162763888688</Real>
+    <Real>11.30316276388869</Real>
+    <Real>11.303162763888688</Real>
+    <Real>11.303162763888688</Real>
+    <Real>11.30316276388869</Real>
+    <Real>11.303162763888688</Real>
+    <Real>11.30316276388869</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.302414924948431</Real>
+    <Real>11.302414924948431</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.30241492494843</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.302414924948428</Real>
+    <Real>11.30166597068127</Real>
+    <Real>11.30166597068127</Real>
+    <Real>11.301665970681269</Real>
+    <Real>11.30166597068127</Real>
+    <Real>11.301665970681269</Real>
+    <Real>11.30166597068127</Real>
+    <Real>11.301665970681269</Real>
+    <Real>11.30166597068127</Real>
+    <Real>11.301665970681269</Real>
+    <Real>11.301665970681269</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300164745122878</Real>
+    <Real>11.300164745122879</Real>
+    <Real>11.300164745122879</Real>
+    <Real>11.300164745122878</Real>
+    <Real>11.300164745122878</Real>
+    <Real>11.300164745122879</Real>
+    <Real>11.300164745122878</Real>
+    <Real>11.300164745122878</Real>
+    <Real>11.300164745122879</Real>
+    <Real>11.300164745122879</Real>
+    <Real>11.299412488245258</Real>
+    <Real>11.299412488245258</Real>
+    <Real>11.29941248824526</Real>
+    <Real>11.29941248824526</Real>
+    <Real>11.299412488245261</Real>
+    <Real>11.299412488245261</Real>
+    <Real>11.29941248824526</Real>
+    <Real>11.29941248824526</Real>
+    <Real>11.299412488245261</Real>
+    <Real>11.299412488245258</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867841</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867841</Real>
+    <Real>11.297904722132458</Real>
+    <Real>11.29790472213246</Real>
+    <Real>11.297904722132461</Real>
+    <Real>11.29790472213246</Real>
+    <Real>11.297904722132458</Real>
+    <Real>11.29790472213246</Real>
+    <Real>11.29790472213246</Real>
+    <Real>11.297904722132461</Real>
+    <Real>11.297904722132461</Real>
+    <Real>11.29790472213246</Real>
+    <Real>11.297149227154899</Real>
+    <Real>11.297149227154899</Real>
+    <Real>11.2971492271549</Real>
+    <Real>11.2971492271549</Real>
+    <Real>11.297149227154899</Real>
+    <Real>11.297149227154899</Real>
+    <Real>11.297149227154899</Real>
+    <Real>11.297149227154895</Real>
+    <Real>11.2971492271549</Real>
+    <Real>11.297149227154899</Real>
+  </Sequence>
+  <Sequence Name="Potential">
+    <Int Name="Length">501</Int>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9350826496477405</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.9350826496477405</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.9386979704459701</Real>
+    <Real>6.9386979704459701</Real>
+    <Real>6.9536366054726368</Real>
+    <Real>6.9536366054726368</Real>
+    <Real>6.9386979704459701</Real>
+    <Real>6.9536366054726368</Real>
+    <Real>6.9536366054726368</Real>
+    <Real>6.9321957923421671</Real>
+    <Real>6.9386979704459701</Real>
+    <Real>6.9536366054726368</Real>
+    <Real>6.9259073070485453</Real>
+    <Real>6.9646765431469584</Real>
+    <Real>6.9400273951284479</Real>
+    <Real>6.9280261419925129</Real>
+    <Real>6.9646765431469584</Real>
+    <Real>6.9400273951284479</Real>
+    <Real>6.9423177191538201</Real>
+    <Real>6.9312928743207793</Real>
+    <Real>6.9646765431469584</Real>
+    <Real>6.9423177191538201</Real>
+    <Real>6.9312417322333157</Real>
+    <Real>6.9756883180679168</Real>
+    <Real>6.9245660783053209</Real>
+    <Real>6.9312417322333157</Real>
+    <Real>6.9756883180679168</Real>
+    <Real>6.9756883180679168</Real>
+    <Real>6.9428897920854915</Real>
+    <Real>6.9756883180679168</Real>
+    <Real>6.9428897920854915</Real>
+    <Real>6.9263887073973791</Real>
+    <Real>6.9866720388012302</Real>
+    <Real>6.9333179329873298</Real>
+    <Real>6.9866720388012302</Real>
+    <Real>6.9866720388012302</Real>
+    <Real>6.9495703061753886</Real>
+    <Real>6.9457573880680421</Real>
+    <Real>6.9866720388012302</Real>
+    <Real>6.9333179329873298</Real>
+    <Real>6.9495703061753886</Real>
+    <Real>6.9495703061753886</Real>
+    <Real>6.931152153272798</Real>
+    <Real>6.9976278135113565</Real>
+    <Real>6.9226196777098403</Real>
+    <Real>6.9486301425918864</Real>
+    <Real>6.9976278135113565</Real>
+    <Real>6.931152153272798</Real>
+    <Real>6.9976278135113565</Real>
+    <Real>6.9245995879630362</Real>
+    <Real>6.9486301425918864</Real>
+    <Real>6.9976278135113565</Real>
+    <Real>6.9515080153296847</Real>
+    <Real>6.9193931567838955</Real>
+    <Real>6.9515080153296847</Real>
+    <Real>7.008555749960772</Real>
+    <Real>7.008555749960772</Real>
+    <Real>6.9225955219071134</Real>
+    <Real>6.9515080153296847</Real>
+    <Real>6.9190681477199316</Real>
+    <Real>7.008555749960772</Real>
+    <Real>6.9568400249413251</Real>
+    <Real>6.9344831809704353</Real>
+    <Real>7.0194559555093008</Real>
+    <Real>6.9604811892568632</Real>
+    <Real>6.9604811892568632</Real>
+    <Real>6.954390966111256</Real>
+    <Real>6.9176430483896869</Real>
+    <Real>6.9604811892568632</Real>
+    <Real>6.954390966111256</Real>
+    <Real>6.954390966111256</Real>
+    <Real>6.954390966111256</Real>
+    <Real>6.9348811209275407</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>6.9310494295467437</Real>
+    <Real>6.9310235953272556</Real>
+    <Real>7.0411736013261095</Real>
+    <Real>6.9677758909708061</Real>
+    <Real>7.0411736013261095</Real>
+    <Real>7.0411736013261095</Real>
+    <Real>6.9137594131403439</Real>
+    <Real>6.9352838030748671</Real>
+    <Real>7.0411736013261095</Real>
+    <Real>6.9188187247640043</Real>
+    <Real>7.0411736013261095</Real>
+    <Real>6.9714293343960936</Real>
+    <Real>7.0519912542955066</Real>
+    <Real>7.0519912542955066</Real>
+    <Real>7.0519912542955066</Real>
+    <Real>6.9356912155432591</Real>
+    <Real>7.0519912542955066</Real>
+    <Real>6.9356912155432591</Real>
+    <Real>6.9714293343960936</Real>
+    <Real>6.963069887379234</Real>
+    <Real>7.0519912542955066</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>6.9659727517856016</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>6.9750867772952461</Real>
+    <Real>6.9750867772952461</Real>
+    <Real>6.9750867772952461</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>6.9787481732968883</Real>
+    <Real>6.9309712031874424</Real>
+    <Real>6.9365201839868726</Real>
+    <Real>6.9688804957503789</Real>
+    <Real>6.9787481732968883</Real>
+    <Real>6.9787481732968883</Real>
+    <Real>7.0735447490737871</Real>
+    <Real>6.9150583017149172</Real>
+    <Real>7.0735447490737871</Real>
+    <Real>6.9787481732968883</Real>
+    <Real>7.0842808011538159</Real>
+    <Real>6.9717930800510182</Real>
+    <Real>6.9309620829817327</Real>
+    <Real>6.9369417162447009</Real>
+    <Real>6.9073442782021139</Real>
+    <Real>6.9309620829817327</Real>
+    <Real>7.0842808011538159</Real>
+    <Real>6.9108893110848522</Real>
+    <Real>6.9309620829817327</Real>
+    <Real>6.9717930800510182</Real>
+    <Real>6.9309571215270811</Real>
+    <Real>7.0949898625322412</Real>
+    <Real>6.9747104656235486</Real>
+    <Real>6.9860826403494887</Real>
+    <Real>6.9747104656235486</Real>
+    <Real>6.9860826403494887</Real>
+    <Real>6.9309571215270811</Real>
+    <Real>7.0949898625322412</Real>
+    <Real>6.9056254431707025</Real>
+    <Real>6.9860826403494887</Real>
+    <Real>6.9309563109201182</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>6.9776326135627009</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>6.9776326135627009</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>6.9377988175735386</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>6.9934323694876399</Real>
+    <Real>7.1163274292606893</Real>
+    <Real>6.9934323694876399</Real>
+    <Real>6.9805594851220363</Real>
+    <Real>6.9934323694876399</Real>
+    <Real>7.1163274292606893</Real>
+    <Real>7.1163274292606893</Real>
+    <Real>7.1163274292606893</Real>
+    <Real>6.9934323694876399</Real>
+    <Real>6.9805594851220363</Real>
+    <Real>7.1269561416327241</Real>
+    <Real>6.9309671106310722</Real>
+    <Real>7.1269561416327241</Real>
+    <Real>7.1269561416327241</Real>
+    <Real>6.9971128440130927</Real>
+    <Real>6.9386745556992144</Real>
+    <Real>6.9110839635292551</Real>
+    <Real>7.1269561416327241</Real>
+    <Real>7.1269561416327241</Real>
+    <Real>6.9834910417140499</Real>
+    <Real>6.9099713939209808</Real>
+    <Real>7.000796998557016</Real>
+    <Real>6.9099713939209808</Real>
+    <Real>7.1375582773468595</Real>
+    <Real>6.8965015860864218</Real>
+    <Real>7.1375582773468595</Real>
+    <Real>6.9017776238831763</Real>
+    <Real>6.9075865207323739</Real>
+    <Real>6.9309787051379557</Real>
+    <Real>6.9099713939209808</Real>
+    <Real>6.8970404033706458</Real>
+    <Real>6.9395688359512278</Real>
+    <Real>6.9309944188707115</Real>
+    <Real>6.9309944188707115</Real>
+    <Real>6.8969717130094956</Real>
+    <Real>6.9893680564412763</Real>
+    <Real>6.9893680564412763</Real>
+    <Real>7.1481339388972636</Real>
+    <Real>7.0044847884593198</Real>
+    <Real>6.9395688359512278</Real>
+    <Real>6.9077525786136995</Real>
+    <Real>7.1586832283712232</Real>
+    <Real>7.1586832283712232</Real>
+    <Real>6.9923134381968879</Real>
+    <Real>6.9923134381968879</Real>
+    <Real>7.0081761693027911</Real>
+    <Real>6.9051104493248925</Real>
+    <Real>7.1586832283712232</Real>
+    <Real>7.1586832283712232</Real>
+    <Real>6.9400228998146911</Real>
+    <Real>6.9952633522260736</Real>
+    <Real>7.1692062474491376</Real>
+    <Real>6.9952633522260736</Real>
+    <Real>7.1692062474491376</Real>
+    <Real>7.0118710969126532</Real>
+    <Real>7.0118710969126532</Real>
+    <Real>7.1692062474491376</Real>
+    <Real>7.0118710969126532</Real>
+    <Real>7.1692062474491376</Real>
+    <Real>6.9404815637496924</Real>
+    <Real>7.1797030974045386</Real>
+    <Real>6.9409448159488916</Real>
+    <Real>6.9409448159488916</Real>
+    <Real>6.8918173690300897</Real>
+    <Real>6.9310661963538811</Real>
+    <Real>6.9055421888808972</Real>
+    <Real>7.0155695273560994</Real>
+    <Real>7.1797030974045386</Real>
+    <Real>6.9409448159488916</Real>
+    <Real>7.1797030974045386</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>7.0011766260973385</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>6.9414126446113498</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>7.0192714169418169</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>6.9414126446113498</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>7.022976722219493</Real>
+    <Real>7.022976722219493</Real>
+    <Real>7.022976722219493</Real>
+    <Real>6.8884706040410029</Real>
+    <Real>6.9033402171470044</Real>
+    <Real>7.2006186930080212</Real>
+    <Real>7.2006186930080212</Real>
+    <Real>7.0041399108336542</Real>
+    <Real>7.0041399108336542</Real>
+    <Real>7.022976722219493</Real>
+    <Real>7.0071075776320226</Real>
+    <Real>6.8989519979377159</Real>
+    <Real>7.0266853999793044</Real>
+    <Real>7.0266853999793044</Real>
+    <Real>6.8866684297824943</Real>
+    <Real>7.2110376391696063</Real>
+    <Real>6.9423619841553466</Real>
+    <Real>7.2110376391696063</Real>
+    <Real>7.2110376391696063</Real>
+    <Real>6.9311747622091424</Real>
+    <Real>7.2214308172360075</Real>
+    <Real>6.931219089120475</Real>
+    <Real>7.2214308172360075</Real>
+    <Real>7.0303974072513995</Real>
+    <Real>7.2214308172360075</Real>
+    <Real>6.931219089120475</Real>
+    <Real>7.0100795893376899</Real>
+    <Real>7.2214308172360075</Real>
+    <Real>6.931219089120475</Real>
+    <Real>7.2214308172360075</Real>
+    <Real>7.2317983264481489</Real>
+    <Real>7.2317983264481489</Real>
+    <Real>7.2317983264481489</Real>
+    <Real>7.0130559089551099</Real>
+    <Real>7.0341127013053608</Real>
+    <Real>7.2317983264481489</Real>
+    <Real>7.0341127013053608</Real>
+    <Real>7.0341127013053608</Real>
+    <Real>7.2317983264481489</Real>
+    <Real>6.9433294881087928</Real>
+    <Real>7.242140265641039</Real>
+    <Real>6.9438200223096196</Real>
+    <Real>7.242140265641039</Real>
+    <Real>7.242140265641039</Real>
+    <Real>7.242140265641039</Real>
+    <Real>7.0378312396496536</Real>
+    <Real>6.9313199029419401</Real>
+    <Real>7.0160364996478988</Real>
+    <Real>6.9438200223096196</Real>
+    <Real>7.242140265641039</Real>
+    <Real>7.0190213247387714</Real>
+    <Real>7.2524567332440473</Real>
+    <Real>7.2524567332440473</Real>
+    <Real>6.8940578353227702</Real>
+    <Real>7.0415529800310681</Real>
+    <Real>7.2524567332440473</Real>
+    <Real>7.2524567332440473</Real>
+    <Real>7.2524567332440473</Real>
+    <Real>7.0415529800310681</Real>
+    <Real>7.0415529800310681</Real>
+    <Real>7.2627478272812258</Real>
+    <Real>6.9314368773899329</Real>
+    <Real>7.2627478272812258</Real>
+    <Real>7.2627478272812258</Real>
+    <Real>7.2627478272812258</Real>
+    <Real>6.9448145963648225</Real>
+    <Real>6.9314368773899329</Real>
+    <Real>6.9314368773899329</Real>
+    <Real>7.2627478272812258</Real>
+    <Real>7.0220103477094762</Real>
+    <Real>7.0250035322007172</Real>
+    <Real>7.2730136453716572</Real>
+    <Real>7.2730136453716572</Real>
+    <Real>7.2730136453716572</Real>
+    <Real>7.0250035322007172</Real>
+    <Real>6.8726311089004852</Real>
+    <Real>6.9315014050869541</Real>
+    <Real>7.0490058990805595</Real>
+    <Real>6.9453186127241029</Real>
+    <Real>6.9453186127241029</Real>
+    <Real>6.9458270996541103</Real>
+    <Real>6.9315699492279279</Real>
+    <Real>6.8904062067753022</Real>
+    <Real>6.8746752819676953</Real>
+    <Real>7.2832542847298329</Real>
+    <Real>6.9458270996541103</Real>
+    <Real>6.9458270996541103</Real>
+    <Real>7.2832542847298329</Real>
+    <Real>7.2832542847298329</Real>
+    <Real>7.0280008420120543</Real>
+    <Real>7.0310022411018025</Real>
+    <Real>7.2934698421660702</Real>
+    <Real>7.2934698421660702</Real>
+    <Real>7.0564711251724077</Real>
+    <Real>6.8935353134190835</Real>
+    <Real>6.9463400454270214</Real>
+    <Real>7.0310022411018025</Real>
+    <Real>6.9463400454270214</Real>
+    <Real>6.8752401699043295</Real>
+    <Real>7.2934698421660702</Real>
+    <Real>6.8735999832595294</Real>
+    <Real>7.3036604140869557</Real>
+    <Real>7.060208250241554</Real>
+    <Real>7.3036604140869557</Real>
+    <Real>6.8696866759399997</Real>
+    <Real>7.0340076935869185</Real>
+    <Real>6.9468574383231658</Real>
+    <Real>7.3036604140869557</Real>
+    <Real>7.3036604140869557</Real>
+    <Real>7.060208250241554</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>7.0639483288002323</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>7.0639483288002323</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>6.871960580494938</Real>
+    <Real>7.0639483288002323</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>7.0400306160035218</Real>
+    <Real>6.8678352996434171</Real>
+    <Real>7.3239669849932216</Real>
+    <Real>6.863462445023468</Real>
+    <Real>6.947905518648076</Real>
+    <Real>7.0676913202466949</Real>
+    <Real>7.0676913202466949</Real>
+    <Real>6.9318841322005076</Real>
+    <Real>7.0400306160035218</Real>
+    <Real>6.8739935155751084</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>7.0430480149609416</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>6.9484361826795134</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>6.9319726400483379</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>6.932065116943634</Real>
+    <Real>7.0460693253652966</Real>
+    <Real>7.0460693253652966</Real>
+    <Real>6.9489712470397969</Real>
+    <Real>7.3441747606454086</Real>
+    <Real>6.861018632371005</Real>
+    <Real>7.0460693253652966</Real>
+    <Real>7.0460693253652966</Real>
+    <Real>7.0460693253652966</Real>
+    <Real>6.9489712470397969</Real>
+    <Real>7.07893447404388</Real>
+    <Real>7.3542340771552039</Real>
+    <Real>7.3542340771552039</Real>
+    <Real>7.3542340771552039</Real>
+    <Real>7.3542340771552039</Real>
+    <Real>6.8819498936358814</Real>
+    <Real>6.8627129619752321</Real>
+    <Real>6.949510283442252</Real>
+    <Real>7.3542340771552039</Real>
+    <Real>7.07893447404388</Real>
+    <Real>7.3642612865721055</Real>
+    <Real>7.3642612865721055</Real>
+    <Real>7.0826829231018982</Real>
+    <Real>7.0826829231018982</Real>
+    <Real>6.9500532707385059</Real>
+    <Real>7.0826829231018982</Real>
+    <Real>6.9322617138170619</Real>
+    <Real>7.3642612865721055</Real>
+    <Real>7.0826829231018982</Real>
+    <Real>7.3642612865721055</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>7.0551423434156755</Real>
+    <Real>6.9323658000115751</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>7.0864311866007901</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>6.8704915143895828</Real>
+    <Real>7.3842200281110317</Real>
+    <Real>6.9511510138689303</Real>
+    <Real>7.0901792238218286</Real>
+    <Real>6.9511510138689303</Real>
+    <Real>6.8577589690608765</Real>
+    <Real>7.3842200281110317</Real>
+    <Real>6.8690716322147747</Real>
+    <Real>7.3842200281110317</Real>
+    <Real>7.0901792238218286</Real>
+    <Real>6.8839082198277417</Real>
+    <Real>6.9517057278554963</Real>
+    <Real>6.9517057278554963</Real>
+    <Real>7.3941518797052996</Real>
+    <Real>7.3941518797052996</Real>
+    <Real>7.0939269944597738</Real>
+    <Real>7.3941518797052996</Real>
+    <Real>7.0939269944597738</Real>
+    <Real>7.0939269944597738</Real>
+    <Real>7.3941518797052996</Real>
+    <Real>6.8676541271811873</Real>
+    <Real>6.8572676743088508</Real>
+    <Real>7.4040522631636785</Real>
+    <Real>7.4040522631636785</Real>
+    <Real>6.8572676743088508</Real>
+    <Real>7.0976744586195748</Real>
+    <Real>7.4040522631636785</Real>
+    <Real>7.0976744586195748</Real>
+    <Real>7.0976744586195748</Real>
+    <Real>7.4040522631636785</Real>
+    <Real>7.4040522631636785</Real>
+    <Real>7.1014215768131113</Real>
+    <Real>7.1014215768131113</Real>
+    <Real>7.067259424326835</Real>
+    <Real>7.4139213357307598</Real>
+    <Real>6.9328203386614984</Real>
+    <Real>6.8602612055427272</Real>
+    <Real>7.067259424326835</Real>
+    <Real>7.4139213357307598</Real>
+    <Real>6.9328203386614984</Real>
+    <Real>7.1014215768131113</Real>
+    <Real>7.423759253665736</Real>
+    <Real>7.105168309955932</Real>
+    <Real>7.423759253665736</Real>
+    <Real>7.423759253665736</Real>
+    <Real>7.0702919574250069</Real>
+    <Real>7.0702919574250069</Real>
+    <Real>7.423759253665736</Real>
+    <Real>7.0702919574250069</Real>
+    <Real>7.423759253665736</Real>
+    <Real>7.105168309955932</Real>
+    <Real>7.1089146193640129</Real>
+    <Real>7.4335661722485895</Real>
+    <Real>7.0733257193787722</Real>
+    <Real>7.4335661722485895</Real>
+    <Real>6.8620077850371297</Real>
+    <Real>7.4335661722485895</Real>
+    <Real>7.4335661722485895</Real>
+    <Real>7.0733257193787722</Real>
+    <Real>7.0733257193787722</Real>
+    <Real>6.9539630492151128</Real>
+    <Real>7.4433422457862761</Real>
+    <Real>7.4433422457862761</Real>
+    <Real>7.1126604667505466</Real>
+    <Real>7.1126604667505466</Real>
+    <Real>7.4433422457862761</Real>
+    <Real>7.0763606726032302</Real>
+    <Real>7.4433422457862761</Real>
+    <Real>6.9332008812119295</Real>
+    <Real>7.1126604667505466</Real>
+    <Real>7.4433422457862761</Real>
+  </Sequence>
+  <Sequence Name="PointBias">
+    <Int Name="Length">16</Int>
+    <Real>-2.9812350510475558</Real>
+    <Real>-2.8317587119323302</Real>
+    <Real>-2.8465623256890895</Real>
+    <Real>-2.7820458005979609</Real>
+    <Real>-2.7733340764443373</Real>
+    <Real>-2.7506303517910191</Real>
+    <Real>-2.748032213357384</Real>
+    <Real>-2.7436795022898046</Real>
+    <Real>-2.7416859119553316</Real>
+    <Real>-2.7396695821731636</Real>
+    <Real>-2.7383033387374733</Real>
+    <Real>-2.7383748274017918</Real>
+    <Real>-2.7373901581787741</Real>
+    <Real>-2.7356402960808746</Real>
+    <Real>-2.7365350589678044</Real>
+    <Real>-2.7358995341543086</Real>
+  </Sequence>
+  <Sequence Name="PointLogPmfsum">
+    <Int Name="Length">16</Int>
+    <Real>9.684978457071038</Real>
+    <Real>9.5355021179558346</Real>
+    <Real>9.550305731712589</Real>
+    <Real>9.48578920662146</Real>
+    <Real>9.4770774824678394</Real>
+    <Real>9.4543737578145386</Real>
+    <Real>9.4517756193808804</Real>
+    <Real>9.4474229083133299</Real>
+    <Real>9.4454293179788316</Real>
+    <Real>9.4434129881966662</Real>
+    <Real>9.4420467447610044</Real>
+    <Real>9.4421182334253082</Real>
+    <Real>9.4411335642022838</Real>
+    <Real>9.4393837021043776</Real>
+    <Real>9.4402784649912928</Real>
+    <Real>9.4396429401778015</Real>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_7.xml b/src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasFepLambdaStateTest_ForcesBiasPmf_7.xml
new file mode 100644 (file)
index 0000000..160034e
--- /dev/null
@@ -0,0 +1,1056 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Properties">
+    <Int Name="Length">3</Int>
+    <String>stage:           initial</String>
+    <String>convolve forces: yes</String>
+    <String>skip updates:    yes</String>
+  </Sequence>
+  <Sequence Name="Force">
+    <Int Name="Length">501</Int>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.332377012920768</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331700796834943</Real>
+    <Real>11.331700796834944</Real>
+    <Real>11.331022709191608</Real>
+    <Real>11.331022709191608</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.331022709191608</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.331022709191608</Real>
+    <Real>11.331022709191609</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.330342753988663</Real>
+    <Real>11.330342753988663</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.330342753988663</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.330342753988663</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.330342753988665</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235024</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235024</Real>
+    <Real>11.329660935235021</Real>
+    <Real>11.329660935235024</Real>
+    <Real>11.329660935235022</Real>
+    <Real>11.328977256950315</Real>
+    <Real>11.328977256950319</Real>
+    <Real>11.328977256950315</Real>
+    <Real>11.328977256950315</Real>
+    <Real>11.328977256950319</Real>
+    <Real>11.328977256950321</Real>
+    <Real>11.328977256950315</Real>
+    <Real>11.328977256950319</Real>
+    <Real>11.328977256950319</Real>
+    <Real>11.328977256950319</Real>
+    <Real>11.328291723164707</Real>
+    <Real>11.328291723164703</Real>
+    <Real>11.328291723164709</Real>
+    <Real>11.328291723164709</Real>
+    <Real>11.328291723164703</Real>
+    <Real>11.328291723164707</Real>
+    <Real>11.328291723164703</Real>
+    <Real>11.328291723164707</Real>
+    <Real>11.328291723164709</Real>
+    <Real>11.328291723164703</Real>
+    <Real>11.327604337918583</Real>
+    <Real>11.327604337918581</Real>
+    <Real>11.327604337918583</Real>
+    <Real>11.327604337918585</Real>
+    <Real>11.327604337918585</Real>
+    <Real>11.327604337918583</Real>
+    <Real>11.327604337918583</Real>
+    <Real>11.327604337918583</Real>
+    <Real>11.327604337918585</Real>
+    <Real>11.327604337918585</Real>
+    <Real>11.326915105262364</Real>
+    <Real>11.326915105262362</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.326915105262364</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.32691510526236</Real>
+    <Real>11.326224029256217</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.326224029256219</Real>
+    <Real>11.325531113969872</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.325531113969872</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.325531113969873</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.325531113969872</Real>
+    <Real>11.325531113969875</Real>
+    <Real>11.324836363482333</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482333</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324836363482337</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.324139781881673</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.324139781881668</Real>
+    <Real>11.324139781881668</Real>
+    <Real>11.324139781881668</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.324139781881671</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.32344137326479</Real>
+    <Real>11.323441373264787</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.32344137326479</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.323441373264792</Real>
+    <Real>11.322741141737211</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322741141737211</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322741141737211</Real>
+    <Real>11.322741141737211</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322741141737213</Real>
+    <Real>11.322039091412815</Real>
+    <Real>11.322039091412812</Real>
+    <Real>11.322039091412815</Real>
+    <Real>11.322039091412814</Real>
+    <Real>11.322039091412815</Real>
+    <Real>11.322039091412814</Real>
+    <Real>11.322039091412815</Real>
+    <Real>11.322039091412812</Real>
+    <Real>11.322039091412815</Real>
+    <Real>11.322039091412814</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.321335226413646</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.321335226413646</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.32133522641365</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.321335226413648</Real>
+    <Real>11.320629550869695</Real>
+    <Real>11.3206295508697</Real>
+    <Real>11.320629550869695</Real>
+    <Real>11.320629550869693</Real>
+    <Real>11.320629550869695</Real>
+    <Real>11.3206295508697</Real>
+    <Real>11.3206295508697</Real>
+    <Real>11.3206295508697</Real>
+    <Real>11.320629550869695</Real>
+    <Real>11.320629550869693</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918663</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918661</Real>
+    <Real>11.319922068918663</Real>
+    <Real>11.31921278470573</Real>
+    <Real>11.31921278470573</Real>
+    <Real>11.31921278470573</Real>
+    <Real>11.319212784705732</Real>
+    <Real>11.319212784705732</Real>
+    <Real>11.319212784705732</Real>
+    <Real>11.31921278470573</Real>
+    <Real>11.319212784705732</Real>
+    <Real>11.319212784705732</Real>
+    <Real>11.31921278470573</Real>
+    <Real>11.318501702383397</Real>
+    <Real>11.318501702383397</Real>
+    <Real>11.318501702383395</Real>
+    <Real>11.318501702383395</Real>
+    <Real>11.318501702383395</Real>
+    <Real>11.318501702383394</Real>
+    <Real>11.318501702383394</Real>
+    <Real>11.318501702383392</Real>
+    <Real>11.318501702383395</Real>
+    <Real>11.318501702383397</Real>
+    <Real>11.317788826111206</Real>
+    <Real>11.317788826111208</Real>
+    <Real>11.317788826111208</Real>
+    <Real>11.31778882611121</Real>
+    <Real>11.31778882611121</Real>
+    <Real>11.317788826111212</Real>
+    <Real>11.317788826111206</Real>
+    <Real>11.317788826111208</Real>
+    <Real>11.317788826111208</Real>
+    <Real>11.317788826111205</Real>
+    <Real>11.317074160055597</Real>
+    <Real>11.317074160055595</Real>
+    <Real>11.317074160055597</Real>
+    <Real>11.317074160055595</Real>
+    <Real>11.317074160055599</Real>
+    <Real>11.317074160055599</Real>
+    <Real>11.317074160055595</Real>
+    <Real>11.317074160055599</Real>
+    <Real>11.317074160055595</Real>
+    <Real>11.317074160055597</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.316357708389637</Real>
+    <Real>11.31635770838964</Real>
+    <Real>11.31635770838964</Real>
+    <Real>11.316357708389637</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.316357708389639</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292871</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.315639475292869</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951071</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951071</Real>
+    <Real>11.314919464951071</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314919464951073</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556083</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.31419768155609</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.314197681556088</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.31347412930559</Real>
+    <Real>11.312748812402932</Real>
+    <Real>11.312748812402932</Real>
+    <Real>11.312748812402932</Real>
+    <Real>11.312748812402933</Real>
+    <Real>11.312748812402933</Real>
+    <Real>11.312748812402932</Real>
+    <Real>11.312748812402933</Real>
+    <Real>11.312748812402933</Real>
+    <Real>11.312748812402932</Real>
+    <Real>11.312748812402935</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.312021735056918</Real>
+    <Real>11.312021735056916</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31202173505692</Real>
+    <Real>11.31129290148162</Real>
+    <Real>11.311292901481616</Real>
+    <Real>11.311292901481616</Real>
+    <Real>11.31129290148162</Real>
+    <Real>11.31129290148162</Real>
+    <Real>11.311292901481616</Real>
+    <Real>11.311292901481616</Real>
+    <Real>11.311292901481616</Real>
+    <Real>11.31129290148162</Real>
+    <Real>11.31129290148162</Real>
+    <Real>11.31056231589619</Real>
+    <Real>11.310562315896188</Real>
+    <Real>11.31056231589619</Real>
+    <Real>11.31056231589619</Real>
+    <Real>11.31056231589619</Real>
+    <Real>11.310562315896192</Real>
+    <Real>11.310562315896188</Real>
+    <Real>11.310562315896188</Real>
+    <Real>11.31056231589619</Real>
+    <Real>11.310562315896192</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524677</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524679</Real>
+    <Real>11.309829982524681</Real>
+    <Real>11.309829982524681</Real>
+    <Real>11.30909590559585</Real>
+    <Real>11.309095905595852</Real>
+    <Real>11.30909590559585</Real>
+    <Real>11.30909590559585</Real>
+    <Real>11.309095905595854</Real>
+    <Real>11.30909590559585</Real>
+    <Real>11.30909590559585</Real>
+    <Real>11.309095905595854</Real>
+    <Real>11.309095905595854</Real>
+    <Real>11.309095905595852</Real>
+    <Real>11.308360089342983</Real>
+    <Real>11.308360089342981</Real>
+    <Real>11.308360089342981</Real>
+    <Real>11.30836008934298</Real>
+    <Real>11.30836008934298</Real>
+    <Real>11.308360089342981</Real>
+    <Real>11.308360089342983</Real>
+    <Real>11.308360089342981</Real>
+    <Real>11.308360089342983</Real>
+    <Real>11.308360089342981</Real>
+    <Real>11.307622538003702</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003701</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003701</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003699</Real>
+    <Real>11.307622538003701</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819795</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819795</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306883255819795</Real>
+    <Real>11.306883255819793</Real>
+    <Real>11.306142247037041</Real>
+    <Real>11.306142247037043</Real>
+    <Real>11.306142247037045</Real>
+    <Real>11.306142247037045</Real>
+    <Real>11.306142247037043</Real>
+    <Real>11.306142247037043</Real>
+    <Real>11.306142247037043</Real>
+    <Real>11.306142247037043</Real>
+    <Real>11.306142247037041</Real>
+    <Real>11.306142247037046</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905034</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905034</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.305399515905036</Real>
+    <Real>11.304655066677002</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677004</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677006</Real>
+    <Real>11.304655066677004</Real>
+    <Real>11.303909480230603</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230601</Real>
+    <Real>11.303909480230603</Real>
+    <Real>11.30316276388869</Real>
+    <Real>11.30316276388869</Real>
+    <Real>11.303162763888688</Real>
+    <Real>11.303162763888688</Real>
+    <Real>11.30316276388869</Real>
+    <Real>11.303162763888688</Real>
+    <Real>11.303162763888688</Real>
+    <Real>11.30316276388869</Real>
+    <Real>11.303162763888688</Real>
+    <Real>11.30316276388869</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.302414924948431</Real>
+    <Real>11.302414924948431</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.30241492494843</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.302414924948433</Real>
+    <Real>11.302414924948428</Real>
+    <Real>11.30166597068127</Real>
+    <Real>11.30166597068127</Real>
+    <Real>11.301665970681269</Real>
+    <Real>11.30166597068127</Real>
+    <Real>11.301665970681269</Real>
+    <Real>11.30166597068127</Real>
+    <Real>11.301665970681269</Real>
+    <Real>11.30166597068127</Real>
+    <Real>11.301665970681269</Real>
+    <Real>11.301665970681269</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300915908332831</Real>
+    <Real>11.300164745122878</Real>
+    <Real>11.300164745122879</Real>
+    <Real>11.300164745122879</Real>
+    <Real>11.300164745122878</Real>
+    <Real>11.300164745122878</Real>
+    <Real>11.300164745122879</Real>
+    <Real>11.300164745122878</Real>
+    <Real>11.300164745122878</Real>
+    <Real>11.300164745122879</Real>
+    <Real>11.300164745122879</Real>
+    <Real>11.299412488245258</Real>
+    <Real>11.299412488245258</Real>
+    <Real>11.29941248824526</Real>
+    <Real>11.29941248824526</Real>
+    <Real>11.299412488245261</Real>
+    <Real>11.299412488245261</Real>
+    <Real>11.29941248824526</Real>
+    <Real>11.29941248824526</Real>
+    <Real>11.299412488245261</Real>
+    <Real>11.299412488245258</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867841</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867839</Real>
+    <Real>11.298659144867841</Real>
+    <Real>11.297904722132458</Real>
+    <Real>11.29790472213246</Real>
+    <Real>11.297904722132461</Real>
+    <Real>11.29790472213246</Real>
+    <Real>11.297904722132458</Real>
+    <Real>11.29790472213246</Real>
+    <Real>11.29790472213246</Real>
+    <Real>11.297904722132461</Real>
+    <Real>11.297904722132461</Real>
+    <Real>11.29790472213246</Real>
+    <Real>11.297149227154899</Real>
+    <Real>11.297149227154899</Real>
+    <Real>11.2971492271549</Real>
+    <Real>11.2971492271549</Real>
+    <Real>11.297149227154899</Real>
+    <Real>11.297149227154899</Real>
+    <Real>11.297149227154899</Real>
+    <Real>11.297149227154895</Real>
+    <Real>11.2971492271549</Real>
+    <Real>11.297149227154899</Real>
+  </Sequence>
+  <Sequence Name="Potential">
+    <Int Name="Length">501</Int>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9314718055994531</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9350826496477405</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.9350826496477405</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.934318361010372</Real>
+    <Real>6.9425683960787516</Real>
+    <Real>6.9386979704459701</Real>
+    <Real>6.9386979704459701</Real>
+    <Real>6.9536366054726368</Real>
+    <Real>6.9536366054726368</Real>
+    <Real>6.9386979704459701</Real>
+    <Real>6.9536366054726368</Real>
+    <Real>6.9536366054726368</Real>
+    <Real>6.9321957923421671</Real>
+    <Real>6.9386979704459701</Real>
+    <Real>6.9536366054726368</Real>
+    <Real>6.9259073070485453</Real>
+    <Real>6.9646765431469584</Real>
+    <Real>6.9400273951284479</Real>
+    <Real>6.9280261419925129</Real>
+    <Real>6.9646765431469584</Real>
+    <Real>6.9400273951284479</Real>
+    <Real>6.9423177191538201</Real>
+    <Real>6.9312928743207793</Real>
+    <Real>6.9646765431469584</Real>
+    <Real>6.9423177191538201</Real>
+    <Real>6.9312417322333157</Real>
+    <Real>6.9756883180679168</Real>
+    <Real>6.9245660783053209</Real>
+    <Real>6.9312417322333157</Real>
+    <Real>6.9756883180679168</Real>
+    <Real>6.9756883180679168</Real>
+    <Real>6.9428897920854915</Real>
+    <Real>6.9756883180679168</Real>
+    <Real>6.9428897920854915</Real>
+    <Real>6.9263887073973791</Real>
+    <Real>6.9866720388012302</Real>
+    <Real>6.9333179329873298</Real>
+    <Real>6.9866720388012302</Real>
+    <Real>6.9866720388012302</Real>
+    <Real>6.9495703061753886</Real>
+    <Real>6.9457573880680421</Real>
+    <Real>6.9866720388012302</Real>
+    <Real>6.9333179329873298</Real>
+    <Real>6.9495703061753886</Real>
+    <Real>6.9495703061753886</Real>
+    <Real>6.931152153272798</Real>
+    <Real>6.9976278135113565</Real>
+    <Real>6.9226196777098403</Real>
+    <Real>6.9486301425918864</Real>
+    <Real>6.9976278135113565</Real>
+    <Real>6.931152153272798</Real>
+    <Real>6.9976278135113565</Real>
+    <Real>6.9245995879630362</Real>
+    <Real>6.9486301425918864</Real>
+    <Real>6.9976278135113565</Real>
+    <Real>6.9515080153296847</Real>
+    <Real>6.9193931567838955</Real>
+    <Real>6.9515080153296847</Real>
+    <Real>7.008555749960772</Real>
+    <Real>7.008555749960772</Real>
+    <Real>6.9225955219071134</Real>
+    <Real>6.9515080153296847</Real>
+    <Real>6.9190681477199316</Real>
+    <Real>7.008555749960772</Real>
+    <Real>6.9568400249413251</Real>
+    <Real>6.9344831809704353</Real>
+    <Real>7.0194559555093008</Real>
+    <Real>6.9604811892568632</Real>
+    <Real>6.9604811892568632</Real>
+    <Real>6.954390966111256</Real>
+    <Real>6.9176430483896869</Real>
+    <Real>6.9604811892568632</Real>
+    <Real>6.954390966111256</Real>
+    <Real>6.954390966111256</Real>
+    <Real>6.954390966111256</Real>
+    <Real>6.9348811209275407</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>7.0303285371135038</Real>
+    <Real>6.9310494295467437</Real>
+    <Real>6.9310235953272556</Real>
+    <Real>7.0411736013261095</Real>
+    <Real>6.9677758909708061</Real>
+    <Real>7.0411736013261095</Real>
+    <Real>7.0411736013261095</Real>
+    <Real>6.9137594131403439</Real>
+    <Real>6.9352838030748671</Real>
+    <Real>7.0411736013261095</Real>
+    <Real>6.9188187247640043</Real>
+    <Real>7.0411736013261095</Real>
+    <Real>6.9714293343960936</Real>
+    <Real>7.0519912542955066</Real>
+    <Real>7.0519912542955066</Real>
+    <Real>7.0519912542955066</Real>
+    <Real>6.9356912155432591</Real>
+    <Real>7.0519912542955066</Real>
+    <Real>6.9356912155432591</Real>
+    <Real>6.9714293343960936</Real>
+    <Real>6.963069887379234</Real>
+    <Real>7.0519912542955066</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>6.9659727517856016</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>6.9750867772952461</Real>
+    <Real>6.9750867772952461</Real>
+    <Real>6.9750867772952461</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>7.0627816017652769</Real>
+    <Real>6.9787481732968883</Real>
+    <Real>6.9309712031874424</Real>
+    <Real>6.9365201839868726</Real>
+    <Real>6.9688804957503789</Real>
+    <Real>6.9787481732968883</Real>
+    <Real>6.9787481732968883</Real>
+    <Real>7.0735447490737871</Real>
+    <Real>6.9150583017149172</Real>
+    <Real>7.0735447490737871</Real>
+    <Real>6.9787481732968883</Real>
+    <Real>7.0842808011538159</Real>
+    <Real>6.9717930800510182</Real>
+    <Real>6.9309620829817327</Real>
+    <Real>6.9369417162447009</Real>
+    <Real>6.9073442782021139</Real>
+    <Real>6.9309620829817327</Real>
+    <Real>7.0842808011538159</Real>
+    <Real>6.9108893110848522</Real>
+    <Real>6.9309620829817327</Real>
+    <Real>6.9717930800510182</Real>
+    <Real>6.9309571215270811</Real>
+    <Real>7.0949898625322412</Real>
+    <Real>6.9747104656235486</Real>
+    <Real>6.9860826403494887</Real>
+    <Real>6.9747104656235486</Real>
+    <Real>6.9860826403494887</Real>
+    <Real>6.9309571215270811</Real>
+    <Real>7.0949898625322412</Real>
+    <Real>6.9056254431707025</Real>
+    <Real>6.9860826403494887</Real>
+    <Real>6.9309563109201182</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>6.9776326135627009</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>6.9776326135627009</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>6.9377988175735386</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>7.1056720373297662</Real>
+    <Real>6.9934323694876399</Real>
+    <Real>7.1163274292606893</Real>
+    <Real>6.9934323694876399</Real>
+    <Real>6.9805594851220363</Real>
+    <Real>6.9934323694876399</Real>
+    <Real>7.1163274292606893</Real>
+    <Real>7.1163274292606893</Real>
+    <Real>7.1163274292606893</Real>
+    <Real>6.9934323694876399</Real>
+    <Real>6.9805594851220363</Real>
+    <Real>7.1269561416327241</Real>
+    <Real>6.9309671106310722</Real>
+    <Real>7.1269561416327241</Real>
+    <Real>7.1269561416327241</Real>
+    <Real>6.9971128440130927</Real>
+    <Real>6.9386745556992144</Real>
+    <Real>6.9110839635292551</Real>
+    <Real>7.1269561416327241</Real>
+    <Real>7.1269561416327241</Real>
+    <Real>6.9834910417140499</Real>
+    <Real>6.9099713939209808</Real>
+    <Real>7.000796998557016</Real>
+    <Real>6.9099713939209808</Real>
+    <Real>7.1375582773468595</Real>
+    <Real>6.8965015860864218</Real>
+    <Real>7.1375582773468595</Real>
+    <Real>6.9017776238831763</Real>
+    <Real>6.9075865207323739</Real>
+    <Real>6.9309787051379557</Real>
+    <Real>6.9099713939209808</Real>
+    <Real>6.8970404033706458</Real>
+    <Real>6.9395688359512278</Real>
+    <Real>6.9309944188707115</Real>
+    <Real>6.9309944188707115</Real>
+    <Real>6.8969717130094956</Real>
+    <Real>6.9893680564412763</Real>
+    <Real>6.9893680564412763</Real>
+    <Real>7.1481339388972636</Real>
+    <Real>7.0044847884593198</Real>
+    <Real>6.9395688359512278</Real>
+    <Real>6.9077525786136995</Real>
+    <Real>7.1586832283712232</Real>
+    <Real>7.1586832283712232</Real>
+    <Real>6.9923134381968879</Real>
+    <Real>6.9923134381968879</Real>
+    <Real>7.0081761693027911</Real>
+    <Real>6.9051104493248925</Real>
+    <Real>7.1586832283712232</Real>
+    <Real>7.1586832283712232</Real>
+    <Real>6.9400228998146911</Real>
+    <Real>6.9952633522260736</Real>
+    <Real>7.1692062474491376</Real>
+    <Real>6.9952633522260736</Real>
+    <Real>7.1692062474491376</Real>
+    <Real>7.0118710969126532</Real>
+    <Real>7.0118710969126532</Real>
+    <Real>7.1692062474491376</Real>
+    <Real>7.0118710969126532</Real>
+    <Real>7.1692062474491376</Real>
+    <Real>6.9404815637496924</Real>
+    <Real>7.1797030974045386</Real>
+    <Real>6.9409448159488916</Real>
+    <Real>6.9409448159488916</Real>
+    <Real>6.8918173690300897</Real>
+    <Real>6.9310661963538811</Real>
+    <Real>6.9055421888808972</Real>
+    <Real>7.0155695273560994</Real>
+    <Real>7.1797030974045386</Real>
+    <Real>6.9409448159488916</Real>
+    <Real>7.1797030974045386</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>7.0011766260973385</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>6.9414126446113498</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>7.0192714169418169</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>6.9414126446113498</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>7.1901738791041563</Real>
+    <Real>7.022976722219493</Real>
+    <Real>7.022976722219493</Real>
+    <Real>7.022976722219493</Real>
+    <Real>6.8884706040410029</Real>
+    <Real>6.9033402171470044</Real>
+    <Real>7.2006186930080212</Real>
+    <Real>7.2006186930080212</Real>
+    <Real>7.0041399108336542</Real>
+    <Real>7.0041399108336542</Real>
+    <Real>7.022976722219493</Real>
+    <Real>7.0071075776320226</Real>
+    <Real>6.8989519979377159</Real>
+    <Real>7.0266853999793044</Real>
+    <Real>7.0266853999793044</Real>
+    <Real>6.8866684297824943</Real>
+    <Real>7.2110376391696063</Real>
+    <Real>6.9423619841553466</Real>
+    <Real>7.2110376391696063</Real>
+    <Real>7.2110376391696063</Real>
+    <Real>6.9311747622091424</Real>
+    <Real>7.2214308172360075</Real>
+    <Real>6.931219089120475</Real>
+    <Real>7.2214308172360075</Real>
+    <Real>7.0303974072513995</Real>
+    <Real>7.2214308172360075</Real>
+    <Real>6.931219089120475</Real>
+    <Real>7.0100795893376899</Real>
+    <Real>7.2214308172360075</Real>
+    <Real>6.931219089120475</Real>
+    <Real>7.2214308172360075</Real>
+    <Real>7.2317983264481489</Real>
+    <Real>7.2317983264481489</Real>
+    <Real>7.2317983264481489</Real>
+    <Real>7.0130559089551099</Real>
+    <Real>7.0341127013053608</Real>
+    <Real>7.2317983264481489</Real>
+    <Real>7.0341127013053608</Real>
+    <Real>7.0341127013053608</Real>
+    <Real>7.2317983264481489</Real>
+    <Real>6.9433294881087928</Real>
+    <Real>7.242140265641039</Real>
+    <Real>6.9438200223096196</Real>
+    <Real>7.242140265641039</Real>
+    <Real>7.242140265641039</Real>
+    <Real>7.242140265641039</Real>
+    <Real>7.0378312396496536</Real>
+    <Real>6.9313199029419401</Real>
+    <Real>7.0160364996478988</Real>
+    <Real>6.9438200223096196</Real>
+    <Real>7.242140265641039</Real>
+    <Real>7.0190213247387714</Real>
+    <Real>7.2524567332440473</Real>
+    <Real>7.2524567332440473</Real>
+    <Real>6.8940578353227702</Real>
+    <Real>7.0415529800310681</Real>
+    <Real>7.2524567332440473</Real>
+    <Real>7.2524567332440473</Real>
+    <Real>7.2524567332440473</Real>
+    <Real>7.0415529800310681</Real>
+    <Real>7.0415529800310681</Real>
+    <Real>7.2627478272812258</Real>
+    <Real>6.9314368773899329</Real>
+    <Real>7.2627478272812258</Real>
+    <Real>7.2627478272812258</Real>
+    <Real>7.2627478272812258</Real>
+    <Real>6.9448145963648225</Real>
+    <Real>6.9314368773899329</Real>
+    <Real>6.9314368773899329</Real>
+    <Real>7.2627478272812258</Real>
+    <Real>7.0220103477094762</Real>
+    <Real>7.0250035322007172</Real>
+    <Real>7.2730136453716572</Real>
+    <Real>7.2730136453716572</Real>
+    <Real>7.2730136453716572</Real>
+    <Real>7.0250035322007172</Real>
+    <Real>6.8726311089004852</Real>
+    <Real>6.9315014050869541</Real>
+    <Real>7.0490058990805595</Real>
+    <Real>6.9453186127241029</Real>
+    <Real>6.9453186127241029</Real>
+    <Real>6.9458270996541103</Real>
+    <Real>6.9315699492279279</Real>
+    <Real>6.8904062067753022</Real>
+    <Real>6.8746752819676953</Real>
+    <Real>7.2832542847298329</Real>
+    <Real>6.9458270996541103</Real>
+    <Real>6.9458270996541103</Real>
+    <Real>7.2832542847298329</Real>
+    <Real>7.2832542847298329</Real>
+    <Real>7.0280008420120543</Real>
+    <Real>7.0310022411018025</Real>
+    <Real>7.2934698421660702</Real>
+    <Real>7.2934698421660702</Real>
+    <Real>7.0564711251724077</Real>
+    <Real>6.8935353134190835</Real>
+    <Real>6.9463400454270214</Real>
+    <Real>7.0310022411018025</Real>
+    <Real>6.9463400454270214</Real>
+    <Real>6.8752401699043295</Real>
+    <Real>7.2934698421660702</Real>
+    <Real>6.8735999832595294</Real>
+    <Real>7.3036604140869557</Real>
+    <Real>7.060208250241554</Real>
+    <Real>7.3036604140869557</Real>
+    <Real>6.8696866759399997</Real>
+    <Real>7.0340076935869185</Real>
+    <Real>6.9468574383231658</Real>
+    <Real>7.3036604140869557</Real>
+    <Real>7.3036604140869557</Real>
+    <Real>7.060208250241554</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>7.0639483288002323</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>7.0639483288002323</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>6.871960580494938</Real>
+    <Real>7.0639483288002323</Real>
+    <Real>7.3138260964958155</Real>
+    <Real>7.0400306160035218</Real>
+    <Real>6.8678352996434171</Real>
+    <Real>7.3239669849932216</Real>
+    <Real>6.863462445023468</Real>
+    <Real>6.947905518648076</Real>
+    <Real>7.0676913202466949</Real>
+    <Real>7.0676913202466949</Real>
+    <Real>6.9318841322005076</Real>
+    <Real>7.0400306160035218</Real>
+    <Real>6.8739935155751084</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>7.0430480149609416</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>6.9484361826795134</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>6.9319726400483379</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>7.3340831747775237</Real>
+    <Real>6.932065116943634</Real>
+    <Real>7.0460693253652966</Real>
+    <Real>7.0460693253652966</Real>
+    <Real>6.9489712470397969</Real>
+    <Real>7.3441747606454086</Real>
+    <Real>6.861018632371005</Real>
+    <Real>7.0460693253652966</Real>
+    <Real>7.0460693253652966</Real>
+    <Real>7.0460693253652966</Real>
+    <Real>6.9489712470397969</Real>
+    <Real>7.07893447404388</Real>
+    <Real>7.3542340771552039</Real>
+    <Real>7.3542340771552039</Real>
+    <Real>7.3542340771552039</Real>
+    <Real>7.3542340771552039</Real>
+    <Real>6.8819498936358814</Real>
+    <Real>6.8627129619752321</Real>
+    <Real>6.949510283442252</Real>
+    <Real>7.3542340771552039</Real>
+    <Real>7.07893447404388</Real>
+    <Real>7.3642612865721055</Real>
+    <Real>7.3642612865721055</Real>
+    <Real>7.0826829231018982</Real>
+    <Real>7.0826829231018982</Real>
+    <Real>6.9500532707385059</Real>
+    <Real>7.0826829231018982</Real>
+    <Real>6.9322617138170619</Real>
+    <Real>7.3642612865721055</Real>
+    <Real>7.0826829231018982</Real>
+    <Real>7.3642612865721055</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>7.0551423434156755</Real>
+    <Real>6.9323658000115751</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>7.0864311866007901</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>7.3742565501446569</Real>
+    <Real>6.8704915143895828</Real>
+    <Real>7.3842200281110317</Real>
+    <Real>6.9511510138689303</Real>
+    <Real>7.0901792238218286</Real>
+    <Real>6.9511510138689303</Real>
+    <Real>6.8577589690608765</Real>
+    <Real>7.3842200281110317</Real>
+    <Real>6.8690716322147747</Real>
+    <Real>7.3842200281110317</Real>
+    <Real>7.0901792238218286</Real>
+    <Real>6.8839082198277417</Real>
+    <Real>6.9517057278554963</Real>
+    <Real>6.9517057278554963</Real>
+    <Real>7.3941518797052996</Real>
+    <Real>7.3941518797052996</Real>
+    <Real>7.0939269944597738</Real>
+    <Real>7.3941518797052996</Real>
+    <Real>7.0939269944597738</Real>
+    <Real>7.0939269944597738</Real>
+    <Real>7.3941518797052996</Real>
+    <Real>6.8676541271811873</Real>
+    <Real>6.8572676743088508</Real>
+    <Real>7.4040522631636785</Real>
+    <Real>7.4040522631636785</Real>
+    <Real>6.8572676743088508</Real>
+    <Real>7.0976744586195748</Real>
+    <Real>7.4040522631636785</Real>
+    <Real>7.0976744586195748</Real>
+    <Real>7.0976744586195748</Real>
+    <Real>7.4040522631636785</Real>
+    <Real>7.4040522631636785</Real>
+    <Real>7.1014215768131113</Real>
+    <Real>7.1014215768131113</Real>
+    <Real>7.067259424326835</Real>
+    <Real>7.4139213357307598</Real>
+    <Real>6.9328203386614984</Real>
+    <Real>6.8602612055427272</Real>
+    <Real>7.067259424326835</Real>
+    <Real>7.4139213357307598</Real>
+    <Real>6.9328203386614984</Real>
+    <Real>7.1014215768131113</Real>
+    <Real>7.423759253665736</Real>
+    <Real>7.105168309955932</Real>
+    <Real>7.423759253665736</Real>
+    <Real>7.423759253665736</Real>
+    <Real>7.0702919574250069</Real>
+    <Real>7.0702919574250069</Real>
+    <Real>7.423759253665736</Real>
+    <Real>7.0702919574250069</Real>
+    <Real>7.423759253665736</Real>
+    <Real>7.105168309955932</Real>
+    <Real>7.1089146193640129</Real>
+    <Real>7.4335661722485895</Real>
+    <Real>7.0733257193787722</Real>
+    <Real>7.4335661722485895</Real>
+    <Real>6.8620077850371297</Real>
+    <Real>7.4335661722485895</Real>
+    <Real>7.4335661722485895</Real>
+    <Real>7.0733257193787722</Real>
+    <Real>7.0733257193787722</Real>
+    <Real>6.9539630492151128</Real>
+    <Real>7.4433422457862761</Real>
+    <Real>7.4433422457862761</Real>
+    <Real>7.1126604667505466</Real>
+    <Real>7.1126604667505466</Real>
+    <Real>7.4433422457862761</Real>
+    <Real>7.0763606726032302</Real>
+    <Real>7.4433422457862761</Real>
+    <Real>6.9332008812119295</Real>
+    <Real>7.1126604667505466</Real>
+    <Real>7.4433422457862761</Real>
+  </Sequence>
+  <Sequence Name="PointBias">
+    <Int Name="Length">16</Int>
+    <Real>-2.9812350510475558</Real>
+    <Real>-2.8317587119323302</Real>
+    <Real>-2.8465623256890895</Real>
+    <Real>-2.7820458005979609</Real>
+    <Real>-2.7733340764443373</Real>
+    <Real>-2.7506303517910191</Real>
+    <Real>-2.748032213357384</Real>
+    <Real>-2.7436795022898046</Real>
+    <Real>-2.7416859119553316</Real>
+    <Real>-2.7396695821731636</Real>
+    <Real>-2.7383033387374733</Real>
+    <Real>-2.7383748274017918</Real>
+    <Real>-2.7373901581787741</Real>
+    <Real>-2.7356402960808746</Real>
+    <Real>-2.7365350589678044</Real>
+    <Real>-2.7358995341543086</Real>
+  </Sequence>
+  <Sequence Name="PointLogPmfsum">
+    <Int Name="Length">16</Int>
+    <Real>9.684978457071038</Real>
+    <Real>9.5355021179558346</Real>
+    <Real>9.550305731712589</Real>
+    <Real>9.48578920662146</Real>
+    <Real>9.4770774824678394</Real>
+    <Real>9.4543737578145386</Real>
+    <Real>9.4517756193808804</Real>
+    <Real>9.4474229083133299</Real>
+    <Real>9.4454293179788316</Real>
+    <Real>9.4434129881966662</Real>
+    <Real>9.4420467447610044</Real>
+    <Real>9.4421182334253082</Real>
+    <Real>9.4411335642022838</Real>
+    <Real>9.4393837021043776</Real>
+    <Real>9.4402784649912928</Real>
+    <Real>9.4396429401778015</Real>
+  </Sequence>
+</ReferenceData>
similarity index 81%
rename from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_0.xml
rename to src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_0.xml
index 953a44d524a2fa61dd94040339c56a1ca23dd0d5..16d4ac61223cd1a6f1d75c7022731b56a93d66b3 100644 (file)
     <Real>-70.000000000000014</Real>
     <Real>-49.999999999999986</Real>
     <Real>-5.0000000000000604</Real>
-    <Real>-49.999999999999993</Real>
-    <Real>-5</Real>
+    <Real>-50.000000000000043</Real>
+    <Real>-5.0000000000000568</Real>
     <Real>80.000000000000014</Real>
     <Real>-184.99999999999989</Real>
     <Real>-64.999999999999829</Real>
-    <Real>75.000000000000071</Real>
+    <Real>75.000000000000128</Real>
     <Real>10.000000000000011</Real>
-    <Real>-59.999999999999943</Real>
-    <Real>-9.999999999999897</Real>
-    <Real>-4.9999999999999485</Real>
+    <Real>-60.000000000000057</Real>
+    <Real>-10.000000000000009</Real>
+    <Real>-5.0000000000000053</Real>
     <Real>60</Real>
     <Real>0</Real>
-    <Real>-44.999999999999929</Real>
-    <Real>60.000000000000057</Real>
+    <Real>-44.999999999999986</Real>
+    <Real>60</Real>
     <Real>5.3290705182007514e-14</Real>
     <Real>-30.000000000000028</Real>
     <Real>-59.999999999999943</Real>
     <Real>4.9999999999999973</Real>
     <Real>0.19999999999999815</Real>
     <Real>11.250000000000004</Real>
-    <Real>3.1999999999999966</Real>
+    <Real>3.2000000000000055</Real>
     <Real>8.4500000000000011</Real>
     <Real>33.799999999999969</Real>
     <Real>4.0499999999999874</Real>
     <Real>11.250000000000021</Real>
-    <Real>1.7999999999999965</Real>
-    <Real>1.7999999999999965</Real>
-    <Real>0.049999999999998976</Real>
-    <Real>0.44999999999999746</Real>
+    <Real>1.8000000000000034</Real>
+    <Real>1.8000000000000034</Real>
+    <Real>0.050000000000000086</Real>
+    <Real>0.45000000000000084</Real>
     <Real>6.049999999999998</Real>
     <Real>1.2500000000000022</Real>
     <Real>7.1999999999999993</Real>
-    <Real>6.0500000000000105</Real>
+    <Real>6.049999999999998</Real>
     <Real>1.2500000000000022</Real>
     <Real>0.45000000000000084</Real>
     <Real>1.7999999999999965</Real>
     <Real>-6.9999999999999991</Real>
     <Real>-4.9999999999999973</Real>
     <Real>0.25000000000000266</Real>
-    <Real>-10.000000000000002</Real>
-    <Real>-0.75</Real>
+    <Real>-10.000000000000007</Real>
+    <Real>-0.75000000000000888</Real>
     <Real>-8</Real>
     <Real>-27.749999999999982</Real>
     <Real>-3.2499999999999947</Real>
     <Real>-11.250000000000021</Real>
-    <Real>-0.99999999999999956</Real>
+    <Real>-1.000000000000002</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>-0.24999999999999711</Real>
+    <Real>-0.2500000000000005</Real>
     <Real>-5.9999999999999982</Real>
     <Real>0</Real>
-    <Real>-6.7499999999999956</Real>
-    <Real>-6.0000000000000107</Real>
+    <Real>-6.7499999999999982</Real>
+    <Real>-5.9999999999999982</Real>
     <Real>-5.5511151231257827e-15</Real>
     <Real>0</Real>
     <Real>0</Real>
     <Real>-3.4502423109495881</Real>
     <Real>-3.469814270443778</Real>
     <Real>-3.4405375988254123</Real>
-    <Real>-3.2226649751609364</Real>
+    <Real>-3.222664975160936</Real>
     <Real>-2.9351106219650442</Real>
     <Real>-2.8182713948302314</Real>
     <Real>-2.9109204815675662</Real>
     <Real>-2.9974201933988711</Real>
-    <Real>-2.909045620263937</Real>
-    <Real>-2.7875440262964757</Real>
+    <Real>-2.9090456202639374</Real>
+    <Real>-2.7875440262964752</Real>
     <Real>-2.7504540277720304</Real>
     <Real>-2.7468371894369819</Real>
     <Real>-2.7467110222070819</Real>
similarity index 81%
rename from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_1.xml
rename to src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_1.xml
index 0e77b34d81aa74cb2ea9792fc886fbd88630f255..e633780fd14046e1a9de082943b1244df4b236f5 100644 (file)
     <Real>-70.000000000000014</Real>
     <Real>-49.999999999999986</Real>
     <Real>-5.0000000000000604</Real>
-    <Real>-49.999999999999993</Real>
-    <Real>-5</Real>
+    <Real>-50.000000000000043</Real>
+    <Real>-5.0000000000000568</Real>
     <Real>80.000000000000014</Real>
     <Real>-184.99999999999989</Real>
     <Real>-64.999999999999829</Real>
-    <Real>75.000000000000071</Real>
+    <Real>75.000000000000128</Real>
     <Real>10.000000000000011</Real>
-    <Real>-59.999999999999943</Real>
-    <Real>-9.999999999999897</Real>
-    <Real>-4.9999999999999485</Real>
+    <Real>-60.000000000000057</Real>
+    <Real>-10.000000000000009</Real>
+    <Real>-5.0000000000000053</Real>
     <Real>60</Real>
     <Real>0</Real>
-    <Real>-44.999999999999929</Real>
-    <Real>60.000000000000057</Real>
+    <Real>-44.999999999999986</Real>
+    <Real>60</Real>
     <Real>5.3290705182007514e-14</Real>
     <Real>-30.000000000000028</Real>
     <Real>-59.999999999999943</Real>
     <Real>4.9999999999999973</Real>
     <Real>0.19999999999999815</Real>
     <Real>11.250000000000004</Real>
-    <Real>3.1999999999999966</Real>
+    <Real>3.2000000000000055</Real>
     <Real>8.4500000000000011</Real>
     <Real>33.799999999999969</Real>
     <Real>4.0499999999999874</Real>
     <Real>11.250000000000021</Real>
-    <Real>1.7999999999999965</Real>
-    <Real>1.7999999999999965</Real>
-    <Real>0.049999999999998976</Real>
-    <Real>0.44999999999999746</Real>
+    <Real>1.8000000000000034</Real>
+    <Real>1.8000000000000034</Real>
+    <Real>0.050000000000000086</Real>
+    <Real>0.45000000000000084</Real>
     <Real>6.049999999999998</Real>
     <Real>1.2500000000000022</Real>
     <Real>7.1999999999999993</Real>
-    <Real>6.0500000000000105</Real>
+    <Real>6.049999999999998</Real>
     <Real>1.2500000000000022</Real>
     <Real>0.45000000000000084</Real>
     <Real>1.7999999999999965</Real>
     <Real>-6.9999999999999991</Real>
     <Real>-4.9999999999999973</Real>
     <Real>0.25000000000000266</Real>
-    <Real>-10.000000000000002</Real>
-    <Real>-0.75</Real>
+    <Real>-10.000000000000007</Real>
+    <Real>-0.75000000000000888</Real>
     <Real>-8</Real>
     <Real>-27.749999999999982</Real>
     <Real>-3.2499999999999947</Real>
     <Real>-11.250000000000021</Real>
-    <Real>-0.99999999999999956</Real>
+    <Real>-1.000000000000002</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>-0.24999999999999711</Real>
+    <Real>-0.2500000000000005</Real>
     <Real>-5.9999999999999982</Real>
     <Real>0</Real>
-    <Real>-6.7499999999999956</Real>
-    <Real>-6.0000000000000107</Real>
+    <Real>-6.7499999999999982</Real>
+    <Real>-5.9999999999999982</Real>
     <Real>-5.5511151231257827e-15</Real>
     <Real>0</Real>
     <Real>0</Real>
     <Real>-3.4502423109495881</Real>
     <Real>-3.469814270443778</Real>
     <Real>-3.4405375988254123</Real>
-    <Real>-3.2226649751609364</Real>
+    <Real>-3.222664975160936</Real>
     <Real>-2.9351106219650442</Real>
     <Real>-2.8182713948302314</Real>
     <Real>-2.9109204815675662</Real>
     <Real>-2.9974201933988711</Real>
-    <Real>-2.909045620263937</Real>
-    <Real>-2.7875440262964757</Real>
+    <Real>-2.9090456202639374</Real>
+    <Real>-2.7875440262964752</Real>
     <Real>-2.7504540277720304</Real>
     <Real>-2.7468371894369819</Real>
     <Real>-2.7467110222070819</Real>
similarity index 66%
rename from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_2.xml
rename to src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_2.xml
index 533383afa7ce85fcd7485f8560deb9b07a91f49b..07158824432d57f6cc9914879c765bee6ae8224d 100644 (file)
@@ -9,51 +9,51 @@
   </Sequence>
   <Sequence Name="Force">
     <Int Name="Length">21</Int>
-    <Real>0.21526724902654029</Real>
-    <Real>0.00037168043378189554</Real>
-    <Real>0.0023506021278633634</Real>
-    <Real>1.8322908294787776e-14</Real>
-    <Real>1.8182159860257569e-05</Real>
-    <Real>-1.8182159861071263e-05</Real>
-    <Real>-6.7883759693111072e-06</Real>
-    <Real>6.7883758926473613e-06</Real>
-    <Real>-4.5547116425681544e-14</Real>
-    <Real>6.7883759521357456e-06</Real>
-    <Real>-6.7883759527878661e-06</Real>
-    <Real>-3.5538537690811558</Real>
-    <Real>-2.0147107048387825</Real>
-    <Real>-2.7052521678814725</Real>
-    <Real>-1.0406813794780787</Real>
-    <Real>-3.967066260592369</Real>
-    <Real>-0.87907598448922164</Real>
-    <Real>-1.6612940249811217</Real>
-    <Real>-2.6184399315060456</Real>
-    <Real>-1.2743003165852991</Real>
-    <Real>-0.86472575028406273</Real>
+    <Real>0.21526724902654093</Real>
+    <Real>0.0003716804337800827</Real>
+    <Real>0.0023506021278663566</Real>
+    <Real>1.8012407615664505e-14</Real>
+    <Real>1.818215991704466e-05</Real>
+    <Real>-1.8182159915952154e-05</Real>
+    <Real>-6.788375906408334e-06</Real>
+    <Real>6.7883759915004787e-06</Real>
+    <Real>1.6429796433937993e-14</Real>
+    <Real>6.7883759305581166e-06</Real>
+    <Real>-6.7883759311136313e-06</Real>
+    <Real>-3.5538537690812251</Real>
+    <Real>-2.0147107048388269</Real>
+    <Real>-2.7052521678814729</Real>
+    <Real>-1.0406813794780656</Real>
+    <Real>-3.967066260592401</Real>
+    <Real>-0.8790759844891981</Real>
+    <Real>-1.6612940249811192</Real>
+    <Real>-2.6184399315060443</Real>
+    <Real>-1.2743003165852878</Real>
+    <Real>-0.86472575028404164</Real>
   </Sequence>
   <Sequence Name="Potential">
     <Int Name="Length">21</Int>
     <Real>5.3171052747656216</Real>
     <Real>5.3139634951471235</Real>
     <Real>5.3139852805648378</Real>
-    <Real>5.3139597783455503</Real>
-    <Real>5.313959928304433</Real>
-    <Real>5.313959928304433</Real>
-    <Real>5.3139598111771384</Real>
-    <Real>5.3139598111771384</Real>
-    <Real>5.3139597783455486</Real>
+    <Real>5.3139597783455494</Real>
+    <Real>5.3139599283044348</Real>
+    <Real>5.3139599283044348</Real>
     <Real>5.3139598111771358</Real>
-    <Real>5.3139598111771358</Real>
-    <Real>5.8523574407739236</Real>
-    <Real>5.909517708141518</Real>
-    <Real>5.5891055083437386</Real>
+    <Real>5.3139598111771367</Real>
+    <Real>5.3139597783455494</Real>
+    <Real>5.3139598111771393</Real>
+    <Real>5.3139598111771393</Real>
+    <Real>5.8523574407739245</Real>
+    <Real>5.9095177081415207</Real>
+    <Real>5.5891055083437378</Real>
     <Real>5.5179043538034485</Real>
-    <Real>5.6909400832286146</Real>
+    <Real>5.6909400832286137</Real>
     <Real>5.5084236091309347</Real>
     <Real>5.4647638463620405</Real>
     <Real>5.4221743541770948</Real>
     <Real>5.4793615522962513</Real>
-    <Real>5.4998305387193387</Real>
+    <Real>5.4998305387193378</Real>
   </Sequence>
   <Sequence Name="PotentialJump">
     <Int Name="Length">21</Int>
@@ -67,7 +67,7 @@
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>0.60502446654887354</Real>
+    <Real>0.60502446654887332</Real>
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
@@ -77,7 +77,7 @@
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>0.83485389150670564</Real>
+    <Real>0.8348538915067063</Real>
   </Sequence>
   <Sequence Name="PointBias">
     <Int Name="Length">21</Int>
     <Real>-3.4502423109495881</Real>
     <Real>-3.469814270443778</Real>
     <Real>-3.4405375988254123</Real>
-    <Real>-3.2226649751609364</Real>
+    <Real>-3.222664975160936</Real>
     <Real>-2.9351106219650442</Real>
     <Real>-2.8182713948302314</Real>
     <Real>-2.9109204815675662</Real>
     <Real>-2.9974201933988711</Real>
-    <Real>-2.909045620263937</Real>
-    <Real>-2.7875440262964757</Real>
+    <Real>-2.9090456202639374</Real>
+    <Real>-2.7875440262964752</Real>
     <Real>-2.7504540277720304</Real>
     <Real>-2.7468371894369819</Real>
     <Real>-2.7467110222070819</Real>
similarity index 66%
rename from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_3.xml
rename to src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_3.xml
index dc189ee2665d6133963f3ec2a285dc7c5821cbeb..89d0b3483678d50b7d1dbc34a243159094ca5a6c 100644 (file)
@@ -9,51 +9,51 @@
   </Sequence>
   <Sequence Name="Force">
     <Int Name="Length">21</Int>
-    <Real>0.21526724902654029</Real>
-    <Real>0.00037168043378189554</Real>
-    <Real>0.0023506021278633634</Real>
-    <Real>1.8322908294787776e-14</Real>
-    <Real>1.8182159860257569e-05</Real>
-    <Real>-1.8182159861071263e-05</Real>
-    <Real>-6.7883759693111072e-06</Real>
-    <Real>6.7883758926473613e-06</Real>
-    <Real>-4.5547116425681544e-14</Real>
-    <Real>6.7883759521357456e-06</Real>
-    <Real>-6.7883759527878661e-06</Real>
-    <Real>-3.5538537690811558</Real>
-    <Real>-2.0147107048387825</Real>
-    <Real>-2.7052521678814725</Real>
-    <Real>-1.0406813794780787</Real>
-    <Real>-3.967066260592369</Real>
-    <Real>-0.87907598448922164</Real>
-    <Real>-1.6612940249811217</Real>
-    <Real>-2.6184399315060456</Real>
-    <Real>-1.2743003165852991</Real>
-    <Real>-0.86472575028406273</Real>
+    <Real>0.21526724902654093</Real>
+    <Real>0.0003716804337800827</Real>
+    <Real>0.0023506021278663566</Real>
+    <Real>1.8012407615664505e-14</Real>
+    <Real>1.818215991704466e-05</Real>
+    <Real>-1.8182159915952154e-05</Real>
+    <Real>-6.788375906408334e-06</Real>
+    <Real>6.7883759915004787e-06</Real>
+    <Real>1.6429796433937993e-14</Real>
+    <Real>6.7883759305581166e-06</Real>
+    <Real>-6.7883759311136313e-06</Real>
+    <Real>-3.5538537690812251</Real>
+    <Real>-2.0147107048388269</Real>
+    <Real>-2.7052521678814729</Real>
+    <Real>-1.0406813794780656</Real>
+    <Real>-3.967066260592401</Real>
+    <Real>-0.8790759844891981</Real>
+    <Real>-1.6612940249811192</Real>
+    <Real>-2.6184399315060443</Real>
+    <Real>-1.2743003165852878</Real>
+    <Real>-0.86472575028404164</Real>
   </Sequence>
   <Sequence Name="Potential">
     <Int Name="Length">21</Int>
     <Real>5.3171052747656216</Real>
     <Real>5.3139634951471235</Real>
     <Real>5.3139852805648378</Real>
-    <Real>5.3139597783455503</Real>
-    <Real>5.313959928304433</Real>
-    <Real>5.313959928304433</Real>
-    <Real>5.3139598111771384</Real>
-    <Real>5.3139598111771384</Real>
-    <Real>5.3139597783455486</Real>
+    <Real>5.3139597783455494</Real>
+    <Real>5.3139599283044348</Real>
+    <Real>5.3139599283044348</Real>
     <Real>5.3139598111771358</Real>
-    <Real>5.3139598111771358</Real>
-    <Real>5.8523574407739236</Real>
-    <Real>5.909517708141518</Real>
-    <Real>5.5891055083437386</Real>
+    <Real>5.3139598111771367</Real>
+    <Real>5.3139597783455494</Real>
+    <Real>5.3139598111771393</Real>
+    <Real>5.3139598111771393</Real>
+    <Real>5.8523574407739245</Real>
+    <Real>5.9095177081415207</Real>
+    <Real>5.5891055083437378</Real>
     <Real>5.5179043538034485</Real>
-    <Real>5.6909400832286146</Real>
+    <Real>5.6909400832286137</Real>
     <Real>5.5084236091309347</Real>
     <Real>5.4647638463620405</Real>
     <Real>5.4221743541770948</Real>
     <Real>5.4793615522962513</Real>
-    <Real>5.4998305387193387</Real>
+    <Real>5.4998305387193378</Real>
   </Sequence>
   <Sequence Name="PotentialJump">
     <Int Name="Length">21</Int>
@@ -67,7 +67,7 @@
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>0.60502446654887354</Real>
+    <Real>0.60502446654887332</Real>
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
@@ -77,7 +77,7 @@
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>0.83485389150670564</Real>
+    <Real>0.8348538915067063</Real>
   </Sequence>
   <Sequence Name="PointBias">
     <Int Name="Length">21</Int>
     <Real>-3.4502423109495881</Real>
     <Real>-3.469814270443778</Real>
     <Real>-3.4405375988254123</Real>
-    <Real>-3.2226649751609364</Real>
+    <Real>-3.222664975160936</Real>
     <Real>-2.9351106219650442</Real>
     <Real>-2.8182713948302314</Real>
     <Real>-2.9109204815675662</Real>
     <Real>-2.9974201933988711</Real>
-    <Real>-2.909045620263937</Real>
-    <Real>-2.7875440262964757</Real>
+    <Real>-2.9090456202639374</Real>
+    <Real>-2.7875440262964752</Real>
     <Real>-2.7504540277720304</Real>
     <Real>-2.7468371894369819</Real>
     <Real>-2.7467110222070819</Real>
similarity index 81%
rename from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_4.xml
rename to src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_4.xml
index c57f9877723dc21f52dd7da577009ea3bf80a6e5..3639d33008ffc9f46cd498d78fd61b8f14e45ab0 100644 (file)
     <Real>-70.000000000000014</Real>
     <Real>-49.999999999999986</Real>
     <Real>-5.0000000000000604</Real>
-    <Real>-49.999999999999993</Real>
-    <Real>-5</Real>
+    <Real>-50.000000000000043</Real>
+    <Real>-5.0000000000000568</Real>
     <Real>80.000000000000014</Real>
     <Real>-184.99999999999989</Real>
     <Real>-64.999999999999829</Real>
-    <Real>75.000000000000071</Real>
+    <Real>75.000000000000128</Real>
     <Real>10.000000000000011</Real>
-    <Real>-59.999999999999943</Real>
-    <Real>-9.999999999999897</Real>
-    <Real>-4.9999999999999485</Real>
+    <Real>-60.000000000000057</Real>
+    <Real>-10.000000000000009</Real>
+    <Real>-5.0000000000000053</Real>
     <Real>60</Real>
     <Real>0</Real>
-    <Real>-44.999999999999929</Real>
-    <Real>60.000000000000057</Real>
+    <Real>-44.999999999999986</Real>
+    <Real>60</Real>
     <Real>5.3290705182007514e-14</Real>
     <Real>-30.000000000000028</Real>
     <Real>-59.999999999999943</Real>
     <Real>4.9999999999999973</Real>
     <Real>0.19999999999999815</Real>
     <Real>11.250000000000004</Real>
-    <Real>3.1999999999999966</Real>
+    <Real>3.2000000000000055</Real>
     <Real>8.4500000000000011</Real>
     <Real>33.799999999999969</Real>
     <Real>4.0499999999999874</Real>
     <Real>11.250000000000021</Real>
-    <Real>1.7999999999999965</Real>
-    <Real>1.7999999999999965</Real>
-    <Real>0.049999999999998976</Real>
-    <Real>0.44999999999999746</Real>
+    <Real>1.8000000000000034</Real>
+    <Real>1.8000000000000034</Real>
+    <Real>0.050000000000000086</Real>
+    <Real>0.45000000000000084</Real>
     <Real>6.049999999999998</Real>
     <Real>1.2500000000000022</Real>
     <Real>7.1999999999999993</Real>
-    <Real>6.0500000000000105</Real>
+    <Real>6.049999999999998</Real>
     <Real>1.2500000000000022</Real>
     <Real>0.45000000000000084</Real>
     <Real>1.7999999999999965</Real>
     <Real>-6.9999999999999991</Real>
     <Real>-4.9999999999999973</Real>
     <Real>0.25000000000000266</Real>
-    <Real>-10.000000000000002</Real>
-    <Real>-0.75</Real>
+    <Real>-10.000000000000007</Real>
+    <Real>-0.75000000000000888</Real>
     <Real>-8</Real>
     <Real>-27.749999999999982</Real>
     <Real>-3.2499999999999947</Real>
     <Real>-11.250000000000021</Real>
-    <Real>-0.99999999999999956</Real>
+    <Real>-1.000000000000002</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>-0.24999999999999711</Real>
+    <Real>-0.2500000000000005</Real>
     <Real>-5.9999999999999982</Real>
     <Real>0</Real>
-    <Real>-6.7499999999999956</Real>
-    <Real>-6.0000000000000107</Real>
+    <Real>-6.7499999999999982</Real>
+    <Real>-5.9999999999999982</Real>
     <Real>-5.5511151231257827e-15</Real>
     <Real>0</Real>
     <Real>0</Real>
     <Real>-3.5460149559446359</Real>
     <Real>-3.5687884755542472</Real>
     <Real>-3.4901303714495153</Real>
-    <Real>-3.4970015262603624</Real>
+    <Real>-3.4970015262603629</Real>
     <Real>-3.4496621504416853</Real>
     <Real>-3.2125376886451624</Real>
     <Real>-2.91505863813991</Real>
     <Real>-2.7962936950351538</Real>
     <Real>-2.8888307484050659</Real>
     <Real>-2.9753285114361296</Real>
-    <Real>-2.8869539267116591</Real>
+    <Real>-2.8869539267116595</Real>
     <Real>-2.7654523327441973</Real>
     <Real>-2.7283623342197525</Real>
     <Real>-2.7247454958847039</Real>
     <Real>4.1747174038020365</Real>
     <Real>4.1623670231442622</Real>
     <Real>4.0396595028897746</Real>
-    <Real>4.1446557041188061</Real>
+    <Real>4.144655704118807</Real>
     <Real>3.9896509587172271</Real>
     <Real>3.7345175271931224</Real>
     <Real>3.7345175271931224</Real>
similarity index 81%
rename from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_5.xml
rename to src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_5.xml
index 7309c59486b9b4fe336695b678decd4d76a80e00..902717c221ad994ef3aac79809d72b3ca54916bc 100644 (file)
     <Real>-70.000000000000014</Real>
     <Real>-49.999999999999986</Real>
     <Real>-5.0000000000000604</Real>
-    <Real>-49.999999999999993</Real>
-    <Real>-5</Real>
+    <Real>-50.000000000000043</Real>
+    <Real>-5.0000000000000568</Real>
     <Real>80.000000000000014</Real>
     <Real>-184.99999999999989</Real>
     <Real>-64.999999999999829</Real>
-    <Real>75.000000000000071</Real>
+    <Real>75.000000000000128</Real>
     <Real>10.000000000000011</Real>
-    <Real>-59.999999999999943</Real>
-    <Real>-9.999999999999897</Real>
-    <Real>-4.9999999999999485</Real>
+    <Real>-60.000000000000057</Real>
+    <Real>-10.000000000000009</Real>
+    <Real>-5.0000000000000053</Real>
     <Real>60</Real>
     <Real>0</Real>
-    <Real>-44.999999999999929</Real>
-    <Real>60.000000000000057</Real>
+    <Real>-44.999999999999986</Real>
+    <Real>60</Real>
     <Real>5.3290705182007514e-14</Real>
     <Real>-30.000000000000028</Real>
     <Real>-59.999999999999943</Real>
     <Real>4.9999999999999973</Real>
     <Real>0.19999999999999815</Real>
     <Real>11.250000000000004</Real>
-    <Real>3.1999999999999966</Real>
+    <Real>3.2000000000000055</Real>
     <Real>8.4500000000000011</Real>
     <Real>33.799999999999969</Real>
     <Real>4.0499999999999874</Real>
     <Real>11.250000000000021</Real>
-    <Real>1.7999999999999965</Real>
-    <Real>1.7999999999999965</Real>
-    <Real>0.049999999999998976</Real>
-    <Real>0.44999999999999746</Real>
+    <Real>1.8000000000000034</Real>
+    <Real>1.8000000000000034</Real>
+    <Real>0.050000000000000086</Real>
+    <Real>0.45000000000000084</Real>
     <Real>6.049999999999998</Real>
     <Real>1.2500000000000022</Real>
     <Real>7.1999999999999993</Real>
-    <Real>6.0500000000000105</Real>
+    <Real>6.049999999999998</Real>
     <Real>1.2500000000000022</Real>
     <Real>0.45000000000000084</Real>
     <Real>1.7999999999999965</Real>
     <Real>-6.9999999999999991</Real>
     <Real>-4.9999999999999973</Real>
     <Real>0.25000000000000266</Real>
-    <Real>-10.000000000000002</Real>
-    <Real>-0.75</Real>
+    <Real>-10.000000000000007</Real>
+    <Real>-0.75000000000000888</Real>
     <Real>-8</Real>
     <Real>-27.749999999999982</Real>
     <Real>-3.2499999999999947</Real>
     <Real>-11.250000000000021</Real>
-    <Real>-0.99999999999999956</Real>
+    <Real>-1.000000000000002</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>-0.24999999999999711</Real>
+    <Real>-0.2500000000000005</Real>
     <Real>-5.9999999999999982</Real>
     <Real>0</Real>
-    <Real>-6.7499999999999956</Real>
-    <Real>-6.0000000000000107</Real>
+    <Real>-6.7499999999999982</Real>
+    <Real>-5.9999999999999982</Real>
     <Real>-5.5511151231257827e-15</Real>
     <Real>0</Real>
     <Real>0</Real>
     <Real>-3.5460149559446359</Real>
     <Real>-3.5687884755542472</Real>
     <Real>-3.4901303714495153</Real>
-    <Real>-3.4970015262603624</Real>
+    <Real>-3.4970015262603629</Real>
     <Real>-3.4496621504416853</Real>
     <Real>-3.2125376886451624</Real>
     <Real>-2.91505863813991</Real>
     <Real>-2.7962936950351538</Real>
     <Real>-2.8888307484050659</Real>
     <Real>-2.9753285114361296</Real>
-    <Real>-2.8869539267116591</Real>
+    <Real>-2.8869539267116595</Real>
     <Real>-2.7654523327441973</Real>
     <Real>-2.7283623342197525</Real>
     <Real>-2.7247454958847039</Real>
     <Real>4.1747174038020365</Real>
     <Real>4.1623670231442622</Real>
     <Real>4.0396595028897746</Real>
-    <Real>4.1446557041188061</Real>
+    <Real>4.144655704118807</Real>
     <Real>3.9896509587172271</Real>
     <Real>3.7345175271931224</Real>
     <Real>3.7345175271931224</Real>
similarity index 66%
rename from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_6.xml
rename to src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_6.xml
index 9ede0c3dcbbaba3060532f31565735bce05b2f8d..defaed77f33e7b4376d9fefb19027b257ea1c38a 100644 (file)
@@ -9,51 +9,51 @@
   </Sequence>
   <Sequence Name="Force">
     <Int Name="Length">21</Int>
-    <Real>0.21526724902654029</Real>
-    <Real>0.00037168043378189554</Real>
-    <Real>0.0023506021278633634</Real>
-    <Real>1.8322908294787776e-14</Real>
-    <Real>1.8182159860257569e-05</Real>
-    <Real>-1.8182159861071263e-05</Real>
-    <Real>-6.7883759693111072e-06</Real>
-    <Real>6.7883758926473613e-06</Real>
-    <Real>-4.5547116425681544e-14</Real>
-    <Real>6.7883759521357456e-06</Real>
-    <Real>-6.7883759527878661e-06</Real>
-    <Real>-3.5538537690811558</Real>
-    <Real>-2.0147107048387825</Real>
-    <Real>-2.7052521678814725</Real>
-    <Real>-1.0406813794780787</Real>
-    <Real>-3.967066260592369</Real>
-    <Real>-0.87907598448922164</Real>
-    <Real>-1.6612940249811217</Real>
-    <Real>-2.6184399315060456</Real>
-    <Real>-1.2743003165852991</Real>
-    <Real>-0.86472575028406273</Real>
+    <Real>0.21526724902654093</Real>
+    <Real>0.0003716804337800827</Real>
+    <Real>0.0023506021278663566</Real>
+    <Real>1.8012407615664505e-14</Real>
+    <Real>1.818215991704466e-05</Real>
+    <Real>-1.8182159915952154e-05</Real>
+    <Real>-6.788375906408334e-06</Real>
+    <Real>6.7883759915004787e-06</Real>
+    <Real>1.6429796433937993e-14</Real>
+    <Real>6.7883759305581166e-06</Real>
+    <Real>-6.7883759311136313e-06</Real>
+    <Real>-3.5538537690812251</Real>
+    <Real>-2.0147107048388269</Real>
+    <Real>-2.7052521678814729</Real>
+    <Real>-1.0406813794780656</Real>
+    <Real>-3.967066260592401</Real>
+    <Real>-0.8790759844891981</Real>
+    <Real>-1.6612940249811192</Real>
+    <Real>-2.6184399315060443</Real>
+    <Real>-1.2743003165852878</Real>
+    <Real>-0.86472575028404164</Real>
   </Sequence>
   <Sequence Name="Potential">
     <Int Name="Length">21</Int>
     <Real>5.3171052747656216</Real>
     <Real>5.3139634951471235</Real>
     <Real>5.3139852805648378</Real>
-    <Real>5.3139597783455503</Real>
-    <Real>5.313959928304433</Real>
-    <Real>5.313959928304433</Real>
-    <Real>5.3139598111771384</Real>
-    <Real>5.3139598111771384</Real>
-    <Real>5.3139597783455486</Real>
+    <Real>5.3139597783455494</Real>
+    <Real>5.3139599283044348</Real>
+    <Real>5.3139599283044348</Real>
     <Real>5.3139598111771358</Real>
-    <Real>5.3139598111771358</Real>
-    <Real>5.8523574407739236</Real>
-    <Real>5.909517708141518</Real>
-    <Real>5.5891055083437386</Real>
+    <Real>5.3139598111771367</Real>
+    <Real>5.3139597783455494</Real>
+    <Real>5.3139598111771393</Real>
+    <Real>5.3139598111771393</Real>
+    <Real>5.8523574407739245</Real>
+    <Real>5.9095177081415207</Real>
+    <Real>5.5891055083437378</Real>
     <Real>5.5179043538034485</Real>
-    <Real>5.6909400832286146</Real>
+    <Real>5.6909400832286137</Real>
     <Real>5.5084236091309347</Real>
     <Real>5.4647638463620405</Real>
     <Real>5.4221743541770948</Real>
     <Real>5.4793615522962513</Real>
-    <Real>5.4998305387193387</Real>
+    <Real>5.4998305387193378</Real>
   </Sequence>
   <Sequence Name="PotentialJump">
     <Int Name="Length">21</Int>
@@ -67,7 +67,7 @@
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>0.60502446654887354</Real>
+    <Real>0.60502446654887332</Real>
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
@@ -77,7 +77,7 @@
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>0.93416481023846032</Real>
+    <Real>0.93416481023846054</Real>
   </Sequence>
   <Sequence Name="PointBias">
     <Int Name="Length">21</Int>
     <Real>-3.5460149559446359</Real>
     <Real>-3.5687884755542472</Real>
     <Real>-3.4901303714495153</Real>
-    <Real>-3.4970015262603624</Real>
+    <Real>-3.4970015262603629</Real>
     <Real>-3.4496621504416853</Real>
     <Real>-3.2125376886451624</Real>
     <Real>-2.91505863813991</Real>
     <Real>-2.7962936950351538</Real>
     <Real>-2.8888307484050659</Real>
     <Real>-2.9753285114361296</Real>
-    <Real>-2.8869539267116591</Real>
+    <Real>-2.8869539267116595</Real>
     <Real>-2.7654523327441973</Real>
     <Real>-2.7283623342197525</Real>
     <Real>-2.7247454958847039</Real>
     <Real>4.1747174038020365</Real>
     <Real>4.1623670231442622</Real>
     <Real>4.0396595028897746</Real>
-    <Real>4.1446557041188061</Real>
+    <Real>4.144655704118807</Real>
     <Real>3.9896509587172271</Real>
     <Real>3.7345175271931224</Real>
     <Real>3.7345175271931224</Real>
similarity index 66%
rename from src/gromacs/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_7.xml
rename to src/gromacs/applied_forces/awh/tests/refdata/WithParameters_BiasTest_ForcesBiasPmf_7.xml
index 35b41a85496247c16f53e4fdf0c7aeb8ca428d73..b5362d7f324360f06e221f84dc11a48a71c15714 100644 (file)
@@ -9,51 +9,51 @@
   </Sequence>
   <Sequence Name="Force">
     <Int Name="Length">21</Int>
-    <Real>0.21526724902654029</Real>
-    <Real>0.00037168043378189554</Real>
-    <Real>0.0023506021278633634</Real>
-    <Real>1.8322908294787776e-14</Real>
-    <Real>1.8182159860257569e-05</Real>
-    <Real>-1.8182159861071263e-05</Real>
-    <Real>-6.7883759693111072e-06</Real>
-    <Real>6.7883758926473613e-06</Real>
-    <Real>-4.5547116425681544e-14</Real>
-    <Real>6.7883759521357456e-06</Real>
-    <Real>-6.7883759527878661e-06</Real>
-    <Real>-3.5538537690811558</Real>
-    <Real>-2.0147107048387825</Real>
-    <Real>-2.7052521678814725</Real>
-    <Real>-1.0406813794780787</Real>
-    <Real>-3.967066260592369</Real>
-    <Real>-0.87907598448922164</Real>
-    <Real>-1.6612940249811217</Real>
-    <Real>-2.6184399315060456</Real>
-    <Real>-1.2743003165852991</Real>
-    <Real>-0.86472575028406273</Real>
+    <Real>0.21526724902654093</Real>
+    <Real>0.0003716804337800827</Real>
+    <Real>0.0023506021278663566</Real>
+    <Real>1.8012407615664505e-14</Real>
+    <Real>1.818215991704466e-05</Real>
+    <Real>-1.8182159915952154e-05</Real>
+    <Real>-6.788375906408334e-06</Real>
+    <Real>6.7883759915004787e-06</Real>
+    <Real>1.6429796433937993e-14</Real>
+    <Real>6.7883759305581166e-06</Real>
+    <Real>-6.7883759311136313e-06</Real>
+    <Real>-3.5538537690812251</Real>
+    <Real>-2.0147107048388269</Real>
+    <Real>-2.7052521678814729</Real>
+    <Real>-1.0406813794780656</Real>
+    <Real>-3.967066260592401</Real>
+    <Real>-0.8790759844891981</Real>
+    <Real>-1.6612940249811192</Real>
+    <Real>-2.6184399315060443</Real>
+    <Real>-1.2743003165852878</Real>
+    <Real>-0.86472575028404164</Real>
   </Sequence>
   <Sequence Name="Potential">
     <Int Name="Length">21</Int>
     <Real>5.3171052747656216</Real>
     <Real>5.3139634951471235</Real>
     <Real>5.3139852805648378</Real>
-    <Real>5.3139597783455503</Real>
-    <Real>5.313959928304433</Real>
-    <Real>5.313959928304433</Real>
-    <Real>5.3139598111771384</Real>
-    <Real>5.3139598111771384</Real>
-    <Real>5.3139597783455486</Real>
+    <Real>5.3139597783455494</Real>
+    <Real>5.3139599283044348</Real>
+    <Real>5.3139599283044348</Real>
     <Real>5.3139598111771358</Real>
-    <Real>5.3139598111771358</Real>
-    <Real>5.8523574407739236</Real>
-    <Real>5.909517708141518</Real>
-    <Real>5.5891055083437386</Real>
+    <Real>5.3139598111771367</Real>
+    <Real>5.3139597783455494</Real>
+    <Real>5.3139598111771393</Real>
+    <Real>5.3139598111771393</Real>
+    <Real>5.8523574407739245</Real>
+    <Real>5.9095177081415207</Real>
+    <Real>5.5891055083437378</Real>
     <Real>5.5179043538034485</Real>
-    <Real>5.6909400832286146</Real>
+    <Real>5.6909400832286137</Real>
     <Real>5.5084236091309347</Real>
     <Real>5.4647638463620405</Real>
     <Real>5.4221743541770948</Real>
     <Real>5.4793615522962513</Real>
-    <Real>5.4998305387193387</Real>
+    <Real>5.4998305387193378</Real>
   </Sequence>
   <Sequence Name="PotentialJump">
     <Int Name="Length">21</Int>
@@ -67,7 +67,7 @@
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>0.60502446654887354</Real>
+    <Real>0.60502446654887332</Real>
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
@@ -77,7 +77,7 @@
     <Real>0</Real>
     <Real>0</Real>
     <Real>0</Real>
-    <Real>0.93416481023846032</Real>
+    <Real>0.93416481023846054</Real>
   </Sequence>
   <Sequence Name="PointBias">
     <Int Name="Length">21</Int>
     <Real>-3.5460149559446359</Real>
     <Real>-3.5687884755542472</Real>
     <Real>-3.4901303714495153</Real>
-    <Real>-3.4970015262603624</Real>
+    <Real>-3.4970015262603629</Real>
     <Real>-3.4496621504416853</Real>
     <Real>-3.2125376886451624</Real>
     <Real>-2.91505863813991</Real>
     <Real>-2.7962936950351538</Real>
     <Real>-2.8888307484050659</Real>
     <Real>-2.9753285114361296</Real>
-    <Real>-2.8869539267116591</Real>
+    <Real>-2.8869539267116595</Real>
     <Real>-2.7654523327441973</Real>
     <Real>-2.7283623342197525</Real>
     <Real>-2.7247454958847039</Real>
     <Real>4.1747174038020365</Real>
     <Real>4.1623670231442622</Real>
     <Real>4.0396595028897746</Real>
-    <Real>4.1446557041188061</Real>
+    <Real>4.144655704118807</Real>
     <Real>3.9896509587172271</Real>
     <Real>3.7345175271931224</Real>
     <Real>3.7345175271931224</Real>
similarity index 78%
rename from src/api/cpp/include/CMakeLists.txt
rename to src/gromacs/applied_forces/densityfitting/CMakeLists.txt
index bc5bb2e9a48f78b35e67a6c7be9a9e7f73e97f5f..b9f779ed714ece17af6464a000a093e41aeb9107 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2018, by the GROMACS development team, led by
+# Copyright (c) 2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-# The `src/api/cpp/include` directory should be mostly empty so that we can use it internally as
-# the public include directory during build and testing. No targets are defined.
-# Linking targets in the build tree should use the gmxapi INTERFACE. Scripts
-# Needing access to a list of pre-installed public headers can look at the
-# GMXAPI_PUBLIC_HEADERS variable in the src/api scope.
+gmx_add_libgromacs_sources(
+    densityfitting.cpp
+    densityfittingamplitudelookup.cpp
+    densityfittingforceprovider.cpp
+    densityfittingoptions.cpp
+    densityfittingoutputprovider.cpp
+    densityfittingparameters.cpp
+    )
+
+if (BUILD_TESTING)
+    add_subdirectory(tests)
+endif()
similarity index 70%
rename from src/gromacs/applied_forces/densityfitting.cpp
rename to src/gromacs/applied_forces/densityfitting/densityfitting.cpp
index 28ead0fcfa1b5398876e0b3766a5f9f373683d18..819903868fb6a1f19ac12b1ee2d9502ad547eace 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <memory>
 #include <numeric>
 
+#include "gromacs/domdec/localatomset.h"
 #include "gromacs/domdec/localatomsetmanager.h"
 #include "gromacs/fileio/checkpoint.h"
 #include "gromacs/fileio/mrcdensitymap.h"
+#include "gromacs/math/coordinatetransformation.h"
 #include "gromacs/math/multidimarray.h"
-#include "gromacs/mdlib/broadcaststructs.h"
-#include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/imdmodule.h"
 #include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/exceptions.h"
@@ -176,16 +176,15 @@ public:
      * allow the MdModuleNotifier to statically distinguish the callback
      * function type from other 'int' function callbacks.
      *
-     * \param[in] pbc MdModuleNotification class that contains a variable
-     *                that enumerates the periodic boundary condition.
+     * \param[in] pbcType enumerates the periodic boundary condition.
      */
-    void setPeriodicBoundaryConditionType(PeriodicBoundaryConditionType pbc)
+    void setPeriodicBoundaryConditionType(const PbcType& pbcType)
     {
-        pbcType_ = std::make_unique<int>(pbc.pbcType);
+        pbcType_ = std::make_unique<PbcType>(pbcType);
     }
 
     //! Get the periodic boundary conditions
-    int periodicBoundaryConditionType()
+    PbcType periodicBoundaryConditionType()
     {
         if (pbcType_ == nullptr)
         {
@@ -209,7 +208,7 @@ private:
     //! The local atom set to act on
     std::unique_ptr<LocalAtomSet> localAtomSet_;
     //! The type of periodic boundary conditions in the simulation
-    std::unique_ptr<int> pbcType_;
+    std::unique_ptr<PbcType> pbcType_;
     //! The simulation time step
     double simulationTimeStep_ = 1;
 
@@ -224,103 +223,124 @@ private:
  */
 class DensityFitting final : public IMDModule
 {
+
 public:
-    /*! \brief Construct the density fitting module.
+    //! Construct the density fitting module.
+    explicit DensityFitting() = default;
+
+    /*! \brief Request to be notified during pre-processing.
      *
      * \param[in] notifier allows the module to subscribe to notifications from MdModules.
      *
      * The density fitting code subscribes to these notifications:
-     *   - setting atom group indices in the densityFittingOptions_ by
-     *     taking a parmeter const IndexGroupsAndNames &
+     *   - setting atom group indices in the densityFittingOptions_ from an
+     *     index group string by taking a parmeter const IndexGroupsAndNames &
      *   - storing its internal parameters in a tpr file by writing to a
      *     key-value-tree during pre-processing by a function taking a
      *     KeyValueTreeObjectBuilder as parameter
-     *   - reading its internal parameters from a key-value-tree during
-     *     simulation setup by taking a const KeyValueTreeObject & parameter
-     *   - constructing local atom sets in the simulation parameter setup
-     *     by taking a LocalAtomSetManager * as parameter
-     *   - the type of periodic boundary conditions that are used
-     *     by taking a PeriodicBoundaryConditionType as parameter
-     *   - the writing of checkpoint data
-     *     by taking a MdModulesWriteCheckpointData as parameter
-     *   - the reading of checkpoint data
-     *     by taking a MdModulesCheckpointReadingDataOnMaster as parameter
-     *   - the broadcasting of checkpoint data
-     *     by taking MdModulesCheckpointReadingBroadcast as parameter
      */
-    explicit DensityFitting(MdModulesNotifier* notifier)
+    void subscribeToPreProcessingNotifications(MdModulesNotifier* notifier) override
     {
         // Callbacks for several kinds of MdModuleNotification are created
         // and subscribed, and will be dispatched correctly at run time
         // based on the type of the parameter required by the lambda.
 
-        // Setting atom group indices
+        if (!densityFittingOptions_.active())
+        {
+            return;
+        }
+
+        // Setting the atom group indices from index group string
         const auto setFitGroupIndicesFunction = [this](const IndexGroupsAndNames& indexGroupsAndNames) {
             densityFittingOptions_.setFitGroupIndices(indexGroupsAndNames);
         };
-        notifier->notifier_.subscribe(setFitGroupIndicesFunction);
+        notifier->preProcessingNotifications_.subscribe(setFitGroupIndicesFunction);
 
         // Writing internal parameters during pre-processing
         const auto writeInternalParametersFunction = [this](KeyValueTreeObjectBuilder treeBuilder) {
             densityFittingOptions_.writeInternalParametersToKvt(treeBuilder);
         };
-        notifier->notifier_.subscribe(writeInternalParametersFunction);
-
-        // Reading internal parameters during simulation setup
-        const auto readInternalParametersFunction = [this](const KeyValueTreeObject& tree) {
-            densityFittingOptions_.readInternalParametersFromKvt(tree);
-        };
-        notifier->notifier_.subscribe(readInternalParametersFunction);
+        notifier->preProcessingNotifications_.subscribe(writeInternalParametersFunction);
 
         // Checking for consistency with all .mdp options
         const auto checkEnergyCaluclationFrequencyFunction =
                 [this](EnergyCalculationFrequencyErrors* energyCalculationFrequencyErrors) {
                     densityFittingOptions_.checkEnergyCaluclationFrequency(energyCalculationFrequencyErrors);
                 };
-        notifier->notifier_.subscribe(checkEnergyCaluclationFrequencyFunction);
+        notifier->preProcessingNotifications_.subscribe(checkEnergyCaluclationFrequencyFunction);
+    }
+
+    /*! \brief Request to be notified.
+     * The density fitting code subscribes to these notifications:
+     *   - reading its internal parameters from a key-value-tree during
+     *     simulation setup by taking a const KeyValueTreeObject & parameter
+     *   - constructing local atom sets in the simulation parameter setup
+     *     by taking a LocalAtomSetManager * as parameter
+     *   - the type of periodic boundary conditions that are used
+     *     by taking a PeriodicBoundaryConditionType as parameter
+     *   - the writing of checkpoint data
+     *     by taking a MdModulesWriteCheckpointData as parameter
+     *   - the reading of checkpoint data
+     *     by taking a MdModulesCheckpointReadingDataOnMaster as parameter
+     *   - the broadcasting of checkpoint data
+     *     by taking MdModulesCheckpointReadingBroadcast as parameter
+     */
+    void subscribeToSimulationSetupNotifications(MdModulesNotifier* notifier) override
+    {
+        if (!densityFittingOptions_.active())
+        {
+            return;
+        }
+
+        // Reading internal parameters during simulation setup
+        const auto readInternalParametersFunction = [this](const KeyValueTreeObject& tree) {
+            densityFittingOptions_.readInternalParametersFromKvt(tree);
+        };
+        notifier->simulationSetupNotifications_.subscribe(readInternalParametersFunction);
 
         // constructing local atom sets during simulation setup
         const auto setLocalAtomSetFunction = [this](LocalAtomSetManager* localAtomSetManager) {
             this->constructLocalAtomSet(localAtomSetManager);
         };
-        notifier->notifier_.subscribe(setLocalAtomSetFunction);
+        notifier->simulationSetupNotifications_.subscribe(setLocalAtomSetFunction);
 
         // constructing local atom sets during simulation setup
-        const auto setPeriodicBoundaryContionsFunction = [this](PeriodicBoundaryConditionType pbc) {
+        const auto setPeriodicBoundaryContionsFunction = [this](const PbcType& pbc) {
             this->densityFittingSimulationParameters_.setPeriodicBoundaryConditionType(pbc);
         };
-        notifier->notifier_.subscribe(setPeriodicBoundaryContionsFunction);
+        notifier->simulationSetupNotifications_.subscribe(setPeriodicBoundaryContionsFunction);
 
         // setting the simulation time step
         const auto setSimulationTimeStepFunction = [this](const SimulationTimeStep& simulationTimeStep) {
             this->densityFittingSimulationParameters_.setSimulationTimeStep(simulationTimeStep.delta_t);
         };
-        notifier->notifier_.subscribe(setSimulationTimeStepFunction);
+        notifier->simulationSetupNotifications_.subscribe(setSimulationTimeStepFunction);
 
         // adding output to energy file
         const auto requestEnergyOutput =
                 [this](MdModulesEnergyOutputToDensityFittingRequestChecker* energyOutputRequest) {
                     this->setEnergyOutputRequest(energyOutputRequest);
                 };
-        notifier->notifier_.subscribe(requestEnergyOutput);
+        notifier->simulationSetupNotifications_.subscribe(requestEnergyOutput);
 
         // writing checkpoint data
         const auto checkpointDataWriting = [this](MdModulesWriteCheckpointData checkpointData) {
-            this->writeCheckpointData(checkpointData);
+            forceProvider_->writeCheckpointData(checkpointData, DensityFittingModuleInfo::name_);
         };
-        notifier->notifier_.subscribe(checkpointDataWriting);
+        notifier->checkpointingNotifications_.subscribe(checkpointDataWriting);
 
         // reading checkpoint data
         const auto checkpointDataReading = [this](MdModulesCheckpointReadingDataOnMaster checkpointData) {
-            this->readCheckpointDataOnMaster(checkpointData);
+            densityFittingState_.readState(checkpointData.checkpointedData_,
+                                           DensityFittingModuleInfo::name_);
         };
-        notifier->notifier_.subscribe(checkpointDataReading);
+        notifier->checkpointingNotifications_.subscribe(checkpointDataReading);
 
         // broadcasting checkpoint data
         const auto checkpointDataBroadcast = [this](MdModulesCheckpointReadingBroadcast checkpointData) {
-            this->broadcastCheckpointData(checkpointData);
+            densityFittingState_.broadcastState(checkpointData.communicator_, checkpointData.isParallelRun_);
         };
-        notifier->notifier_.subscribe(checkpointDataBroadcast);
+        notifier->checkpointingNotifications_.subscribe(checkpointDataBroadcast);
     }
 
     //! From IMDModule
@@ -360,12 +380,8 @@ public:
      */
     void constructLocalAtomSet(LocalAtomSetManager* localAtomSetManager)
     {
-        if (densityFittingOptions_.active())
-        {
-            LocalAtomSet atomSet =
-                    localAtomSetManager->add(densityFittingOptions_.buildParameters().indices_);
-            densityFittingSimulationParameters_.setLocalAtomSet(atomSet);
-        }
+        LocalAtomSet atomSet = localAtomSetManager->add(densityFittingOptions_.buildParameters().indices_);
+        densityFittingSimulationParameters_.setLocalAtomSet(atomSet);
     }
 
     /*! \brief Request energy output to energy file during simulation.
@@ -375,86 +391,6 @@ public:
         energyOutputRequest->energyOutputToDensityFitting_ = densityFittingOptions_.active();
     }
 
-    /*! \brief Write internal density fitting data to checkpoint file.
-     * \param[in] checkpointWriting enables writing to the Key-Value-Tree
-     *                              that is used for storing the checkpoint
-     *                              information
-     *
-     * \note The provided state to checkpoint has to change if checkpointing
-     *       is moved before the force provider call in the MD-loop.
-     */
-    void writeCheckpointData(MdModulesWriteCheckpointData checkpointWriting)
-    {
-        if (densityFittingOptions_.active())
-        {
-            const DensityFittingForceProviderState& state = forceProvider_->stateToCheckpoint();
-            checkpointWriting.builder_.addValue<std::int64_t>(
-                    DensityFittingModuleInfo::name_ + "-stepsSinceLastCalculation",
-                    state.stepsSinceLastCalculation_);
-            checkpointWriting.builder_.addValue<real>(
-                    DensityFittingModuleInfo::name_ + "-adaptiveForceConstantScale",
-                    state.adaptiveForceConstantScale_);
-            KeyValueTreeObjectBuilder exponentialMovingAverageKvtEntry =
-                    checkpointWriting.builder_.addObject(DensityFittingModuleInfo::name_
-                                                         + "-exponentialMovingAverageState");
-            exponentialMovingAverageStateAsKeyValueTree(exponentialMovingAverageKvtEntry,
-                                                        state.exponentialMovingAverageState_);
-        }
-    }
-
-    /*! \brief Read the internal parameters from the checkpoint file on master
-     * \param[in] checkpointReading holding the checkpoint information
-     */
-    void readCheckpointDataOnMaster(MdModulesCheckpointReadingDataOnMaster checkpointReading)
-    {
-        if (densityFittingOptions_.active())
-        {
-            if (checkpointReading.checkpointedData_.keyExists(DensityFittingModuleInfo::name_
-                                                              + "-stepsSinceLastCalculation"))
-            {
-                densityFittingState_.stepsSinceLastCalculation_ =
-                        checkpointReading
-                                .checkpointedData_[DensityFittingModuleInfo::name_
-                                                   + "-stepsSinceLastCalculation"]
-                                .cast<std::int64_t>();
-            }
-            if (checkpointReading.checkpointedData_.keyExists(DensityFittingModuleInfo::name_
-                                                              + "-adaptiveForceConstantScale"))
-            {
-                densityFittingState_.adaptiveForceConstantScale_ =
-                        checkpointReading
-                                .checkpointedData_[DensityFittingModuleInfo::name_
-                                                   + "-adaptiveForceConstantScale"]
-                                .cast<real>();
-            }
-            if (checkpointReading.checkpointedData_.keyExists(DensityFittingModuleInfo::name_
-                                                              + "-exponentialMovingAverageState"))
-            {
-                densityFittingState_.exponentialMovingAverageState_ = exponentialMovingAverageStateFromKeyValueTree(
-                        checkpointReading
-                                .checkpointedData_[DensityFittingModuleInfo::name_ + "-exponentialMovingAverageState"]
-                                .asObject());
-            }
-        }
-    }
-
-    /*! \brief Broadcast the internal parameters.
-     * \param[in] checkpointBroadcast containing the communication record to
-     *                                broadcast the checkpoint information
-     */
-    void broadcastCheckpointData(MdModulesCheckpointReadingBroadcast checkpointBroadcast)
-    {
-        if (densityFittingOptions_.active())
-        {
-            if (PAR(&(checkpointBroadcast.cr_)))
-            {
-                block_bc(&(checkpointBroadcast.cr_), densityFittingState_.stepsSinceLastCalculation_);
-                block_bc(&(checkpointBroadcast.cr_), densityFittingState_.adaptiveForceConstantScale_);
-                block_bc(&(checkpointBroadcast.cr_), densityFittingState_.exponentialMovingAverageState_);
-            }
-        }
-    }
-
 private:
     //! The output provider
     DensityFittingOutputProvider densityFittingOutputProvider_;
@@ -474,9 +410,9 @@ private:
 
 } // namespace
 
-std::unique_ptr<IMDModule> DensityFittingModuleInfo::create(MdModulesNotifier* notifier)
+std::unique_ptr<IMDModule> DensityFittingModuleInfo::create()
 {
-    return std::make_unique<DensityFitting>(notifier);
+    return std::make_unique<DensityFitting>();
 }
 
 const std::string DensityFittingModuleInfo::name_ = "density-guided-simulation";
similarity index 93%
rename from src/gromacs/applied_forces/densityfitting.h
rename to src/gromacs/applied_forces/densityfitting/densityfitting.h
index c453027137292abefd7bdbda08b62d9a4176002f..a5d09de17d01567211d342c0867f8da93efa8e8b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -42,7 +42,6 @@ namespace gmx
 {
 
 class IMDModule;
-struct MdModulesNotifier;
 
 /*! \libinternal \brief Information about the density fitting module.
  *
@@ -56,7 +55,7 @@ struct DensityFittingModuleInfo
      * Fitting an all-atom structure into an experimental cryo-EM density map is a
      * typical application.
      */
-    static std::unique_ptr<IMDModule> create(MdModulesNotifier* notifier);
+    static std::unique_ptr<IMDModule> create();
     //! The name of the module
     static const std::string name_;
 };
similarity index 98%
rename from src/gromacs/applied_forces/densityfittingamplitudelookup.cpp
rename to src/gromacs/applied_forces/densityfitting/densityfittingamplitudelookup.cpp
index 3b7343b717bace01e34a57a0fe5a39aceb94efae..27be15efb38e7f7ea9c0517d5a30add61bed8372 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,7 @@
 #include <vector>
 
 #include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/utility/arrayref.h"
 
 namespace gmx
 {
similarity index 90%
rename from src/gromacs/applied_forces/densityfittingamplitudelookup.h
rename to src/gromacs/applied_forces/densityfitting/densityfittingamplitudelookup.h
index e3f7f28ffb315f2244d541a0e3d59cdff9234b5a..86675bcec649c882f9021e06867e2d569bcd3561 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,8 +44,8 @@
 
 #include <map>
 #include <memory>
+#include <vector>
 
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/real.h"
 
@@ -54,10 +54,13 @@ struct t_mdatoms;
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
+
 /*! \brief
  * The methods that determine how amplitudes are spread on a grid in density guided simulations.
  */
-enum class DensityFittingAmplitudeMethod
+enum class DensityFittingAmplitudeMethod : int
 {
     Unity,  //!< same spread amplitude, unity, for all atoms
     Mass,   //!< atom mass is the spread amplitude
@@ -65,11 +68,6 @@ enum class DensityFittingAmplitudeMethod
     Count
 };
 
-//! The names of the methods to determine the amplitude of the atoms to be spread on a grid
-const EnumerationArray<DensityFittingAmplitudeMethod, const char* const> c_densityFittingAmplitudeMethodNames = {
-    { "unity", "mass", "charge" }
-};
-
 class DensityFittingAmplitudeLookupImpl;
 
 /*! \internal \brief Class that translates atom properties into amplitudes.
similarity index 72%
rename from src/gromacs/applied_forces/densityfittingforceprovider.cpp
rename to src/gromacs/applied_forces/densityfitting/densityfittingforceprovider.cpp
index 535b418a61b0b2caa448e6f101e20186184331f8..c0dab954369b5faa312a254d21b821e5588a7e9e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "densityfittingforceprovider.h"
 
 #include <numeric>
+#include <optional>
 
-#include "gromacs/compat/optional.h"
+#include "gromacs/domdec/localatomset.h"
 #include "gromacs/gmxlib/network.h"
+#include "gromacs/math/coordinatetransformation.h"
 #include "gromacs/math/densityfit.h"
 #include "gromacs/math/densityfittingforce.h"
-#include "gromacs/math/exponentialmovingaverage.h"
 #include "gromacs/math/gausstransform.h"
+#include "gromacs/mdlib/broadcaststructs.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/forceoutput.h"
-#include "gromacs/mdtypes/iforceprovider.h"
 #include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/strconvert.h"
 
 #include "densityfittingamplitudelookup.h"
 #include "densityfittingparameters.h"
@@ -76,7 +78,7 @@ namespace
 GaussianSpreadKernelParameters::Shape makeSpreadKernel(real sigma, real nSigma, const ScaleCoordinates& scaleToLattice)
 {
     RVec sigmaInLatticeCoordinates{ sigma, sigma, sigma };
-    scaleToLattice({ &sigmaInLatticeCoordinates, &sigmaInLatticeCoordinates + 1 });
+    scaleToLattice(&sigmaInLatticeCoordinates);
     return { DVec{ sigmaInLatticeCoordinates[XX], sigmaInLatticeCoordinates[YY],
                    sigmaInLatticeCoordinates[ZZ] },
              nSigma };
@@ -84,6 +86,59 @@ GaussianSpreadKernelParameters::Shape makeSpreadKernel(real sigma, real nSigma,
 
 } // namespace
 
+/********************************************************************
+ * DensityFittingForceProviderState
+ */
+
+const std::string DensityFittingForceProviderState::adaptiveForceConstantScaleName_ =
+        "adaptiveForceConstantScale";
+
+const std::string DensityFittingForceProviderState::exponentialMovingAverageStateName_ =
+        "exponentialMovingAverageState";
+
+const std::string DensityFittingForceProviderState::stepsSinceLastCalculationName_ =
+        "stepsSinceLastCalculation";
+
+void DensityFittingForceProviderState::writeState(KeyValueTreeObjectBuilder kvtBuilder,
+                                                  const std::string&        identifier) const
+{
+    writeKvtCheckpointValue(stepsSinceLastCalculation_, stepsSinceLastCalculationName_, identifier,
+                            kvtBuilder);
+    writeKvtCheckpointValue(adaptiveForceConstantScale_, adaptiveForceConstantScaleName_,
+                            identifier, kvtBuilder);
+
+    KeyValueTreeObjectBuilder exponentialMovingAverageKvtEntry =
+            kvtBuilder.addObject(identifier + "-" + exponentialMovingAverageStateName_);
+    exponentialMovingAverageStateAsKeyValueTree(exponentialMovingAverageKvtEntry,
+                                                exponentialMovingAverageState_);
+}
+
+void DensityFittingForceProviderState::readState(const KeyValueTreeObject& kvtData,
+                                                 const std::string&        identifier)
+{
+    readKvtCheckpointValue(compat::make_not_null(&stepsSinceLastCalculation_),
+                           stepsSinceLastCalculationName_, identifier, kvtData);
+    readKvtCheckpointValue(compat::make_not_null(&adaptiveForceConstantScale_),
+                           adaptiveForceConstantScaleName_, identifier, kvtData);
+
+    if (kvtData.keyExists(identifier + "-" + exponentialMovingAverageStateName_))
+    {
+        exponentialMovingAverageState_ = exponentialMovingAverageStateFromKeyValueTree(
+                kvtData[identifier + "-" + exponentialMovingAverageStateName_].asObject());
+    }
+}
+
+void DensityFittingForceProviderState::broadcastState(MPI_Comm communicator, bool isParallelRun)
+{
+    if (isParallelRun)
+    {
+        block_bc(communicator, stepsSinceLastCalculation_);
+        block_bc(communicator, adaptiveForceConstantScale_);
+        block_bc(communicator, exponentialMovingAverageState_);
+    }
+}
+
+
 /********************************************************************
  * DensityFittingForceProvider::Impl
  */
@@ -96,7 +151,7 @@ public:
          basic_mdspan<const float, dynamicExtents3D> referenceDensity,
          const TranslateAndScale&                    transformationToDensityLattice,
          const LocalAtomSet&                         localAtomSet,
-         int                                         pbcType,
+         PbcType                                     pbcType,
          double                                      simulationTimeStep,
          const DensityFittingForceProviderState&     state);
     ~Impl();
@@ -121,10 +176,13 @@ private:
     DensityFittingAmplitudeLookup amplitudeLookup_;
     TranslateAndScale             transformationToDensityLattice_;
     RVec                          referenceDensityCenter_;
-    int                           pbcType_;
+    PbcType                       pbcType_;
 
     //! Optionally scale the force according to a moving average of the similarity
-    compat::optional<ExponentialMovingAverage> expAverageSimilarity_;
+    std::optional<ExponentialMovingAverage> expAverageSimilarity_;
+
+    //! Optionally translate the structure
+    std::optional<AffineTransformation> affineTransformation_;
 };
 
 DensityFittingForceProvider::Impl::~Impl() = default;
@@ -133,7 +191,7 @@ DensityFittingForceProvider::Impl::Impl(const DensityFittingParameters&
                                         basic_mdspan<const float, dynamicExtents3D> referenceDensity,
                                         const TranslateAndScale& transformationToDensityLattice,
                                         const LocalAtomSet&      localAtomSet,
-                                        int                      pbcType,
+                                        PbcType                  pbcType,
                                         double                   simulationTimeStep,
                                         const DensityFittingForceProviderState& state) :
     parameters_(parameters),
@@ -149,7 +207,7 @@ DensityFittingForceProvider::Impl::Impl(const DensityFittingParameters&
     amplitudeLookup_(parameters_.amplitudeLookupMethod_),
     transformationToDensityLattice_(transformationToDensityLattice),
     pbcType_(pbcType),
-    expAverageSimilarity_(compat::nullopt)
+    expAverageSimilarity_(std::nullopt)
 {
     if (parameters_.adaptiveForceScaling_)
     {
@@ -160,17 +218,35 @@ DensityFittingForceProvider::Impl::Impl(const DensityFittingParameters&
                         / (simulationTimeStep * parameters_.calculationIntervalInSteps_),
                 state.exponentialMovingAverageState_));
     }
+
+    // set up optional coordinate translation if the translation string contains a vector
+    const std::optional<std::array<real, 3>> translationParametersAsArray =
+            parsedArrayFromInputString<real, 3>(parameters_.translationString_);
+    // set up optional coordinate transformation if the transformation string contains data
+    const std::optional<std::array<real, 9>> transformationMatrixParametersAsArray =
+            parsedArrayFromInputString<real, 9>(parameters_.transformationMatrixString_);
+    if (translationParametersAsArray || transformationMatrixParametersAsArray)
+    {
+        Matrix3x3 translationMatrix = transformationMatrixParametersAsArray.has_value()
+                                              ? *transformationMatrixParametersAsArray
+                                              : identityMatrix<real, 3>();
+        RVec translationVector = translationParametersAsArray.has_value()
+                                         ? RVec((*translationParametersAsArray)[XX],
+                                                (*translationParametersAsArray)[YY],
+                                                (*translationParametersAsArray)[ZZ])
+                                         : RVec(0, 0, 0);
+        affineTransformation_.emplace(translationMatrix.asConstView(), translationVector);
+    }
+
     referenceDensityCenter_ = { real(referenceDensity.extent(XX)) / 2,
                                 real(referenceDensity.extent(YY)) / 2,
                                 real(referenceDensity.extent(ZZ)) / 2 };
-    transformationToDensityLattice_.scaleOperationOnly().inverseIgnoringZeroScale(
-            { &referenceDensityCenter_, &referenceDensityCenter_ + 1 });
+    transformationToDensityLattice_.scaleOperationOnly().inverseIgnoringZeroScale(&referenceDensityCenter_);
     // correct the reference density center for a shift
     // if the reference density does not have its origin at (0,0,0)
     RVec referenceDensityOriginShift(0, 0, 0);
-    transformationToDensityLattice_({ &referenceDensityOriginShift, &referenceDensityOriginShift + 1 });
-    transformationToDensityLattice_.scaleOperationOnly().inverseIgnoringZeroScale(
-            { &referenceDensityOriginShift, &referenceDensityOriginShift + 1 });
+    transformationToDensityLattice_(&referenceDensityOriginShift);
+    transformationToDensityLattice_.scaleOperationOnly().inverseIgnoringZeroScale(&referenceDensityOriginShift);
     referenceDensityCenter_ -= referenceDensityOriginShift;
 }
 
@@ -194,6 +270,12 @@ void DensityFittingForceProvider::Impl::calculateForces(const ForceProviderInput
                    std::begin(transformedCoordinates_),
                    [&forceProviderInput](int index) { return forceProviderInput.x_[index]; });
 
+    // apply additional structure transformations
+    if (affineTransformation_)
+    {
+        (*affineTransformation_)(transformedCoordinates_);
+    }
+
     // pick periodic image that is closest to the center of the reference density
     {
         t_pbc pbc;
@@ -314,7 +396,7 @@ DensityFittingForceProvider::DensityFittingForceProvider(const DensityFittingPar
                                                          basic_mdspan<const float, dynamicExtents3D> referenceDensity,
                                                          const TranslateAndScale& transformationToDensityLattice,
                                                          const LocalAtomSet& localAtomSet,
-                                                         int                 pbcType,
+                                                         PbcType             pbcType,
                                                          double              simulationTimeStep,
                                                          const DensityFittingForceProviderState& state) :
     impl_(new Impl(parameters, referenceDensity, transformationToDensityLattice, localAtomSet, pbcType, simulationTimeStep, state))
@@ -327,9 +409,10 @@ void DensityFittingForceProvider::calculateForces(const ForceProviderInput& forc
     impl_->calculateForces(forceProviderInput, forceProviderOutput);
 }
 
-const DensityFittingForceProviderState& DensityFittingForceProvider::stateToCheckpoint()
+void DensityFittingForceProvider::writeCheckpointData(MdModulesWriteCheckpointData checkpointWriting,
+                                                      const std::string&           moduleName)
 {
-    return impl_->stateToCheckpoint();
+    impl_->stateToCheckpoint().writeState(checkpointWriting.builder_, moduleName);
 }
 
 } // namespace gmx
similarity index 59%
rename from src/gromacs/applied_forces/densityfittingforceprovider.h
rename to src/gromacs/applied_forces/densityfitting/densityfittingforceprovider.h
index 16a121ba3432d21ee32dc30e9393bac563b80678..98616ebd97c2d7b13f4b8e715de07fec3b984c78 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <memory>
 
-#include "gromacs/domdec/localatomset.h"
-#include "gromacs/math/coordinatetransformation.h"
+#include "gromacs/fileio/checkpoint.h"
 #include "gromacs/math/exponentialmovingaverage.h"
 #include "gromacs/mdspan/extensions.h"
 #include "gromacs/mdtypes/iforceprovider.h"
 #include "gromacs/utility/classhelpers.h"
 
+enum class PbcType : int;
+
 namespace gmx
 {
 
+class LocalAtomSet;
+class TranslateAndScale;
 struct DensityFittingParameters;
 
 /*! \internal
@@ -65,10 +68,48 @@ struct DensityFittingForceProviderState
      *  Used if density fitting is to be calculated every N steps.
      */
     std::int64_t stepsSinceLastCalculation_ = 0;
+    /*! \brief String naming variable holding the steps since last calculation.
+     * \note Changing this name will break backwards compability for checkpoint file writing.
+     */
+    static const std::string stepsSinceLastCalculationName_;
     //! The state of the exponential moving average of the similarity measure
     ExponentialMovingAverageState exponentialMovingAverageState_ = {};
+    /*! \brief String naming variable holding the exponential moving average.
+     * \note Changing this name will break backwards compability for checkpoint file writing.
+     */
+    static const std::string exponentialMovingAverageStateName_;
+
     //! An additional factor scaling the force for adaptive force scaling
     real adaptiveForceConstantScale_ = 1.0_real;
+    /*! \brief String naming variable holding the adaptive force constant scale.
+     * \note Changing this name will break backwards compability for checkpoint file writing.
+     */
+    static const std::string adaptiveForceConstantScaleName_;
+
+    /*! \brief Write internal density fitting data into a key value tree.
+     * The entries to the kvt are identified with identifier, so that a variable
+     * is indentified with the key "identifier-variablename"
+     *
+     * \param[in] kvtBuilder enables writing to the Key-Value-Tree
+     *                              the state is written to
+     *
+     * \param[in] identifier denotes the module that is checkpointing the data
+     */
+    void writeState(KeyValueTreeObjectBuilder kvtBuilder, const std::string& identifier) const;
+
+    /*! \brief Read the internal parameters from the checkpoint file on master
+     * \param[in] kvtData holding the checkpoint information
+     * \param[in] identifier identifies the data in a key-value-tree
+     */
+    void readState(const KeyValueTreeObject& kvtData, const std::string& identifier);
+
+    /*! \brief Broadcast the internal parameters.
+     *
+     * \param[in] communicator to broadcast the state information
+     * \param[in] isParallelRun to determine if anything has to be broadcast at all
+     *
+     */
+    void broadcastState(MPI_Comm communicator, bool isParallelRun);
 };
 
 /*! \internal \brief
@@ -82,7 +123,7 @@ public:
                                 basic_mdspan<const float, dynamicExtents3D> referenceDensity,
                                 const TranslateAndScale& transformationToDensityLattice,
                                 const LocalAtomSet&      localAtomSet,
-                                int                      pbcType,
+                                PbcType                  pbcType,
                                 double                   simulationTimeStep,
                                 const DensityFittingForceProviderState& state);
     ~DensityFittingForceProvider();
@@ -93,11 +134,16 @@ public:
     void calculateForces(const ForceProviderInput& forceProviderInput,
                          ForceProviderOutput*      forceProviderOutput) override;
 
-    /*! \brief Return the state of the forceprovider to be checkpointed
-     * TODO update this routine if checkpointing is moved to the beginning of
-     *      the md loop
+    /*! \brief Write internal density fitting data to checkpoint file.
+     * \param[in] checkpointWriting enables writing to the Key-Value-Tree
+     *                              that is used for storing the checkpoint
+     *                              information
+     * \param[in] moduleName names the module that is checkpointing this force-provider
+     *
+     * \note The provided state to checkpoint has to change if checkpointing
+     *       is moved before the force provider call in the MD-loop.
      */
-    const DensityFittingForceProviderState& stateToCheckpoint();
+    void writeCheckpointData(MdModulesWriteCheckpointData checkpointWriting, const std::string& moduleName);
 
 private:
     class Impl;
similarity index 88%
rename from src/gromacs/applied_forces/densityfittingoptions.cpp
rename to src/gromacs/applied_forces/densityfitting/densityfittingoptions.cpp
index 5a1a01d1beb67d9336228d67106ce3f20f629a6b..a3cb45794be772bde3c13ef916b7c9f60d2238cc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "densityfittingoptions.h"
 
-#include "gromacs/applied_forces/densityfitting.h"
+#include "gromacs/applied_forces/densityfitting/densityfitting.h"
 #include "gromacs/math/densityfit.h"
 #include "gromacs/options/basicoptions.h"
 #include "gromacs/options/optionsection.h"
 #include "gromacs/selection/indexutil.h"
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/keyvaluetreebuilder.h"
 #include "gromacs/utility/keyvaluetreetransform.h"
@@ -145,8 +146,32 @@ void DensityFittingOptions::initMdpTransform(IKeyValueTreeTransformRules* rules)
     densityfittingMdpTransformFromString<bool>(rules, &fromStdString<bool>, c_adaptiveForceScalingTag_);
     densityfittingMdpTransformFromString<real>(rules, &fromStdString<real>,
                                                c_adaptiveForceScalingTimeConstantTag_);
+    const auto& stringRVecToStringRVecWithCheck = [](const std::string& str) {
+        return stringIdentityTransformWithArrayCheck<real, 3>(
+                str, "Reading three real values as vector while parsing the .mdp input failed in "
+                             + DensityFittingModuleInfo::name_ + ".");
+    };
+    densityfittingMdpTransformFromString<std::string>(rules, stringRVecToStringRVecWithCheck,
+                                                      c_translationTag_);
+
+    const auto& stringMatrixToStringMatrixWithCheck = [](const std::string& str) {
+        return stringIdentityTransformWithArrayCheck<real, 9>(
+                str, "Reading nine real values as vector while parsing the .mdp input failed in "
+                             + DensityFittingModuleInfo::name_ + ".");
+    };
+    densityfittingMdpTransformFromString<std::string>(rules, stringMatrixToStringMatrixWithCheck,
+                                                      c_transformationMatrixTag_);
 }
 
+//! Name the methods that may be used to evaluate similarity between densities
+static const EnumerationArray<DensitySimilarityMeasureMethod, const char*> c_densitySimilarityMeasureMethodNames = {
+    { "inner-product", "relative-entropy", "cross-correlation" }
+};
+//! The names of the methods to determine the amplitude of the atoms to be spread on a grid
+static const EnumerationArray<DensityFittingAmplitudeMethod, const char*> c_densityFittingAmplitudeMethodNames = {
+    { "unity", "mass", "charge" }
+};
+
 void DensityFittingOptions::buildMdpOutput(KeyValueTreeObjectBuilder* builder) const
 {
 
@@ -208,11 +233,11 @@ void DensityFittingOptions::initMdpOptions(IOptionsContainerWithSections* option
     section.addOption(StringOption(c_groupTag_.c_str()).store(&groupString_));
 
     section.addOption(EnumOption<DensitySimilarityMeasureMethod>(c_similarityMeasureTag_.c_str())
-                              .enumValue(c_densitySimilarityMeasureMethodNames.m_elements)
+                              .enumValue(c_densitySimilarityMeasureMethodNames)
                               .store(&parameters_.similarityMeasureMethod_));
 
     section.addOption(EnumOption<DensityFittingAmplitudeMethod>(c_amplitudeMethodTag_.c_str())
-                              .enumValue(c_densityFittingAmplitudeMethodNames.m_elements)
+                              .enumValue(c_densityFittingAmplitudeMethodNames)
                               .store(&parameters_.amplitudeLookupMethod_));
 
     section.addOption(RealOption(c_forceConstantTag_.c_str()).store(&parameters_.forceConstant_));
@@ -227,6 +252,9 @@ void DensityFittingOptions::initMdpOptions(IOptionsContainerWithSections* option
             BooleanOption(c_adaptiveForceScalingTag_.c_str()).store(&parameters_.adaptiveForceScaling_));
     section.addOption(RealOption(c_adaptiveForceScalingTimeConstantTag_.c_str())
                               .store(&parameters_.adaptiveForceScalingTimeConstant_));
+    section.addOption(StringOption(c_translationTag_.c_str()).store(&parameters_.translationString_));
+    section.addOption(
+            StringOption(c_transformationMatrixTag_.c_str()).store(&parameters_.transformationMatrixString_));
 }
 
 bool DensityFittingOptions::active() const
similarity index 96%
rename from src/gromacs/applied_forces/densityfittingoptions.h
rename to src/gromacs/applied_forces/densityfitting/densityfittingoptions.h
index 993a94b00e7702ff638fd6a684a44c8a85fb7d6a..bbdf3176157d8a9403ec59427f6138ff68ea1374 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -136,6 +136,9 @@ private:
     const std::string c_adaptiveForceScalingTimeConstantTag_ =
             "adaptive-force-scaling-time-constant";
 
+    const std::string c_translationTag_ = "shift-vector";
+
+    const std::string c_transformationMatrixTag_ = "transformation-matrix";
 
     DensityFittingParameters parameters_;
 };
similarity index 97%
rename from src/gromacs/applied_forces/densityfittingoutputprovider.cpp
rename to src/gromacs/applied_forces/densityfitting/densityfittingoutputprovider.cpp
index 8b4aebf8f5a1f0247181cfa442763ab823869d20..b7168c79bcb6eae0b0a74d814dbd458719cc512f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 97%
rename from src/gromacs/applied_forces/densityfittingoutputprovider.h
rename to src/gromacs/applied_forces/densityfitting/densityfittingoutputprovider.h
index 6958f4e2fcd3788024002bda58141cdb31f9ab07..9bd165def2268ba5ef618be7b2a9764a30069ebf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 97%
rename from src/gromacs/applied_forces/densityfittingparameters.cpp
rename to src/gromacs/applied_forces/densityfitting/densityfittingparameters.cpp
index ee7a725e4795140b707b98bb810081b0a17be261..906dc25b6edbe26bc0e25a2bbfdebfc689ac7af3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 92%
rename from src/gromacs/applied_forces/densityfittingparameters.h
rename to src/gromacs/applied_forces/densityfitting/densityfittingparameters.h
index 96ec5900b2c8534198ba34c80714488ec41e98f7..0e123ef5bc4efc1a4b5df76871b1e2f886c83016 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -42,6 +42,7 @@
 #ifndef GMX_APPLIED_FORCES_DENSITYFITTINGPARAMETERS_H
 #define GMX_APPLIED_FORCES_DENSITYFITTINGPARAMETERS_H
 
+#include <string>
 #include <vector>
 
 #include "gromacs/math/densityfit.h"
@@ -81,6 +82,10 @@ struct DensityFittingParameters
     bool adaptiveForceScaling_ = false;
     //! The time constant for the adaptive force scaling in ps
     real adaptiveForceScalingTimeConstant_ = 4;
+    //! Translation of the structure, so that the coordinates that are fitted are x+translation
+    std::string translationString_ = "";
+    //! Linear transformation of the structure, so that the coordinates that are fitted are Matrix * x
+    std::string transformationMatrixString_ = "";
 };
 
 /*!\brief Check if two structs holding density fitting parameters are equal.
diff --git a/src/gromacs/applied_forces/densityfitting/tests/CMakeLists.txt b/src/gromacs/applied_forces/densityfitting/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fd53edd
--- /dev/null
@@ -0,0 +1,41 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+gmx_add_unit_test(DensityFittingAppliedForcesUnitTest density_fitting_applied_forces-test
+    CPP_SOURCE_FILES
+        densityfitting.cpp
+        densityfittingamplitudelookup.cpp
+        densityfittingforceprovider.cpp
+        densityfittingoptions.cpp
+        )
similarity index 96%
rename from src/gromacs/applied_forces/tests/densityfitting.cpp
rename to src/gromacs/applied_forces/densityfitting/tests/densityfitting.cpp
index 38f3cb0d4c2c4e379fced7ced966b5130abb4fda..f9b09904e207956a373a2d65b69b05c1208151c5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -41,7 +41,7 @@
  */
 #include "gmxpre.h"
 
-#include "gromacs/applied_forces/densityfitting.h"
+#include "gromacs/applied_forces/densityfitting/densityfitting.h"
 
 #include <gtest/gtest.h>
 
@@ -94,7 +94,7 @@ public:
     {
         KeyValueTreeObject mdpOptionsTree = mdpValueBuilder_.build();
 
-        densityFittingModule_ = DensityFittingModuleInfo::create(&notifier_);
+        densityFittingModule_ = DensityFittingModuleInfo::create();
 
         // set up options
         Options densityFittingModuleOptions;
@@ -117,7 +117,6 @@ public:
 
 protected:
     KeyValueTreeBuilder        mdpValueBuilder_;
-    MdModulesNotifier          notifier_;
     ForceProviders             densityFittingForces_;
     std::unique_ptr<IMDModule> densityFittingModule_;
 };
similarity index 96%
rename from src/gromacs/applied_forces/tests/densityfittingamplitudelookup.cpp
rename to src/gromacs/applied_forces/densityfitting/tests/densityfittingamplitudelookup.cpp
index 531052885e601b47ad30d8e31a8c3c7239396d6f..d0eeb3a9e3df47523225265a2395f97fc85d05e5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  */
 #include "gmxpre.h"
 
-#include "gromacs/applied_forces/densityfittingamplitudelookup.h"
+#include "gromacs/applied_forces/densityfitting/densityfittingamplitudelookup.h"
 
 #include <vector>
 
 #include <gtest/gtest.h>
 
 #include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/utility/arrayref.h"
 
 namespace gmx
 {
diff --git a/src/gromacs/applied_forces/densityfitting/tests/densityfittingforceprovider.cpp b/src/gromacs/applied_forces/densityfitting/tests/densityfittingforceprovider.cpp
new file mode 100644 (file)
index 0000000..45722d2
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests amplitude lookup for density fitting
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_applied_forces
+ */
+#include "gmxpre.h"
+
+#include "gromacs/applied_forces/densityfitting/densityfittingforceprovider.h"
+
+#include <gtest/gtest.h>
+
+namespace gmx
+{
+
+TEST(DensityFittingForceProviderState, RoundTripSaving)
+{
+    DensityFittingForceProviderState state;
+    // set-up state
+    state.adaptiveForceConstantScale_                   = 1.0;
+    state.stepsSinceLastCalculation_                    = 0;
+    state.exponentialMovingAverageState_.increasing_    = false;
+    state.exponentialMovingAverageState_.weightedCount_ = 0;
+    state.exponentialMovingAverageState_.weightedSum_   = 0;
+
+    KeyValueTreeBuilder kvtBuilder;
+    const std::string   identifier = "test-module";
+    state.writeState(kvtBuilder.rootObject(), identifier);
+    KeyValueTreeObject stateStoredInKvt = kvtBuilder.build();
+
+    // invalidate state
+    state.adaptiveForceConstantScale_                   = -1;
+    state.stepsSinceLastCalculation_                    = -1;
+    state.exponentialMovingAverageState_.increasing_    = true;
+    state.exponentialMovingAverageState_.weightedCount_ = -1;
+    state.exponentialMovingAverageState_.weightedSum_   = -1;
+
+    // read back the original state
+    state.readState(stateStoredInKvt, identifier);
+
+    EXPECT_EQ(state.adaptiveForceConstantScale_, 1.0);
+    EXPECT_EQ(state.stepsSinceLastCalculation_, 0);
+
+    EXPECT_EQ(state.exponentialMovingAverageState_.increasing_, false);
+    EXPECT_EQ(state.exponentialMovingAverageState_.weightedCount_, 0);
+    EXPECT_EQ(state.exponentialMovingAverageState_.weightedSum_, 0);
+}
+
+
+} // namespace gmx
similarity index 98%
rename from src/gromacs/applied_forces/tests/densityfittingoptions.cpp
rename to src/gromacs/applied_forces/densityfitting/tests/densityfittingoptions.cpp
index 4dcc3c5873a399ae2e1883a3d169c362e941194e..54600746e0efd28520721b49e99640d47f9db5e1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -41,7 +41,7 @@
  */
 #include "gmxpre.h"
 
-#include "gromacs/applied_forces/densityfittingoptions.h"
+#include "gromacs/applied_forces/densityfitting/densityfittingoptions.h"
 
 #include <string>
 #include <vector>
@@ -91,7 +91,7 @@ public:
         assignOptionsFromKeyValueTree(&densityFittingModuleOptions, transformedMdpValues.object(), nullptr);
     }
 
-    KeyValueTreeObject densityFittingSetActiveAsMdpValues()
+    static KeyValueTreeObject densityFittingSetActiveAsMdpValues()
     {
         // Prepare MDP inputs
         KeyValueTreeBuilder mdpValueBuilder;
index 5bc278142d330498555d45d9a2f2e26e8992a80d..08c414f5d42c973e06f32387c75079250ef0e397 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -171,6 +171,9 @@ public:
     void calculateForces(const ForceProviderInput& forceProviderInput,
                          ForceProviderOutput*      forceProviderOutput) override;
 
+    void subscribeToSimulationSetupNotifications(MdModulesNotifier* /* notifier */) override {}
+    void subscribeToPreProcessingNotifications(MdModulesNotifier* /* notifier */) override {}
+
 private:
     //! Return whether or not to apply a field
     bool isActive() const;
index 87c0275638426bf54e56c120ab766c3afe312af6..48ff8a85ad5dda7a3d28a1a7b6e390fd7f3d6d7f 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2016,2019, by the GROMACS development team, led by
+# Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,8 +33,6 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(AppliedForcesUnitTest applied_forces-test
-                  densityfitting.cpp
-                  densityfittingoptions.cpp
-                  densityfittingamplitudelookup.cpp
-                  electricfield.cpp
-                )
+    CPP_SOURCE_FILES
+        electricfield.cpp
+        )
index bef644480f61fe678d0b5e565622ddcce8fdeacc..8e19955ae37654f42471b1793933e171251ec962 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,6 +48,7 @@
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/math/paddedvector.h"
 #include "gromacs/mdlib/forcerec.h"
+#include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/forceoutput.h"
 #include "gromacs/mdtypes/iforceprovider.h"
@@ -81,7 +82,7 @@ namespace
 class ElectricFieldTest : public ::testing::Test
 {
 public:
-    void test(int dim, real E0, real omega, real t0, real sigma, real expectedValue)
+    static void test(int dim, real E0, real omega, real t0, real sigma, real expectedValue)
     {
         // Make the electric field module
         auto module = createElectricFieldModule();
@@ -107,11 +108,11 @@ public:
         // Prepare a ForceProviderInput
         t_mdatoms         md;
         std::vector<real> chargeA{ 1 };
-        md.homenr                   = ssize(chargeA);
-        md.chargeA                  = chargeA.data();
-        CommrecHandle      cr       = init_commrec(MPI_COMM_WORLD, nullptr);
+        md.homenr  = ssize(chargeA);
+        md.chargeA = chargeA.data();
+        t_commrec          cr;
         matrix             boxDummy = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
-        ForceProviderInput forceProviderInput({}, md, 0.0, boxDummy, *cr);
+        ForceProviderInput forceProviderInput({}, md, 0.0, boxDummy, cr);
 
         // Prepare a ForceProviderOutput
         PaddedVector<RVec>  f = { { 0, 0, 0 } };
index 5858579b1faebdf10ab379c9caa70112662f6542..aa7216cb5bf10127cafeab529ef461125fe702e1 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index cbcb84fefa6adec1882d750045289b2f6dcd9bd5..95b4acfa61fe15c5591075a849b0f77e7338b12c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 02b07170359ba2ee86fd28616d9be55ce3e4f4dc..aebdf285f8297123f21896651c49550ea5743655 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 588b95f7a49d86c7c78501501187f02e63ed00f2..051790201dff7b8d65f2eb3ebb9685e4d6c0f3a4 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 81870708ab18ac512ff579db333e048a1c662178..f94da3846deef62dc8fe60e05ad25db17b920c1b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7cf4f002d757eca5df0dc5ed580bfcc5d5a298e0..066495cd67405afb42cf59e44c136fb4661a2268 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -276,7 +277,7 @@ public:
                                              char***                         argv);
 
     //! Prints the footer at the end of execution.
-    void printThanks(FILE* fp);
+    static void printThanks(FILE* fp);
 
     /*! \brief
      * Maps module names to module objects.
index f850bcedcd04ecaf68153ee567d063e0bc915877..9575de0c4d525dcdeda6583c1ee2a98a5c171b37 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f49763e0755790a9bc6ceec81377463e2faa000a..53f2f92636489d3697a66a2d263f74f772becd7e 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 8f5b50e7989ae4f7d317ce9937ab48135c48e536..5f33ed552faba4ce7864812aaf0b11031daa241d 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -79,7 +79,7 @@ public:
      * \returns The beginning of the option name in \p arg, or NULL if
      *     \p arg does not look like an option.
      */
-    const char* toOptionName(const char* arg) const;
+    static const char* toOptionName(const char* arg);
 
     //! Helper object for assigning the options.
     OptionsAssigner assigner_;
@@ -100,7 +100,7 @@ CommandLineParser::Impl::Impl(Options* options) :
     assigner_.setAcceptBooleanNoPrefix(true);
 }
 
-const char* CommandLineParser::Impl::toOptionName(const char* arg) const
+const char* CommandLineParser::Impl::toOptionName(const char* arg)
 {
     // Lone '-' or '--' is not an option.
     if (arg[0] != '-' || arg[1] == '\0' || (arg[1] == '-' && arg[2] == '\0'))
index 171e2f2b49065a9b4387898c2ad309890d22b1a1..b2ecfe491f27dc2a6a7f927811c6698017557275 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 14a70beda13d69d82310783c7bf517366dec9fd6..dd573cc1abe4ffbae0bc606aff98f9a68e46c4ce 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e42f3ecaec45f3eb8692307eab023632ccd20790..00a3699ddf820f95c7faefa333f9511196ee8f0e 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b1044cf33082a4af87b2a8ae3a7def78e5b6fa7e..ec798075f3f6056d8ad6241d3712141569df5fad 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <cstdio>
 #include <cstring>
 
-#include "gromacs/compat/string_view.h"
+#include <string_view>
+
 #include "gromacs/fileio/filetypes.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/gmxassert.h"
@@ -246,7 +249,7 @@ namespace
 
 /*! \brief Return the first position within \c filename of the ".partNNNN"
  * interior sequence produced by mdrun -noappend, or npos if not found. */
-size_t findSuffixFromNoAppendPosition(const gmx::compat::string_view filename)
+size_t findSuffixFromNoAppendPosition(const std::string_view filename)
 {
     size_t partPosition = filename.find(".part");
     if ((partPosition != decltype(filename)::npos) && (filename.length() - partPosition >= 10)
@@ -261,7 +264,7 @@ size_t findSuffixFromNoAppendPosition(const gmx::compat::string_view filename)
 
 } // namespace
 
-bool hasSuffixFromNoAppend(const gmx::compat::string_view filename)
+bool hasSuffixFromNoAppend(const std::string_view filename)
 {
     return (findSuffixFromNoAppendPosition(filename) != decltype(filename)::npos);
 }
index a188ebe9e2fab391caf36255a88c7458141d4d47..5cc0e620a15b1781a7f0962311abea25de300f70 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_COMMANDLINE_FILENM_H
 
 #include <string>
+#include <string_view>
 #include <vector>
 
-#include "gromacs/compat/string_view.h"
 #include "gromacs/fileio/filetypes.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 
 
 //! \addtogroup module_commandline
 //! \{
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
 /*! \brief
  * File name option definition for C code.
  *
@@ -176,7 +182,7 @@ gmx_bool is_set(const t_filenm* fnm);
  *
  * If so, it must match "prefix.partNNNN.extension", for four decimal
  * digits N and non-empty prefix and extension. */
-bool hasSuffixFromNoAppend(gmx::compat::string_view filename);
+bool hasSuffixFromNoAppend(std::string_view filename);
 
 /*! \brief
  * When we do checkpointing, this routine is called to check for previous
index 31c10f4eda56939d6f82bddac674ff47f2d0c6c7..1b4f6a8c523a994490fca7a4e8179a6369419bf8 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -58,6 +59,7 @@
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basenetwork.h"
 #include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
@@ -186,29 +188,30 @@ namespace gmx
 namespace
 {
 
-/*! \brief
- * Returns the index of the default xvg format.
+//! Names for XvgFormat
+const gmx::EnumerationArray<XvgFormat, const char*> c_xvgFormatNames = { { "xmgrace", "xmgr",
+                                                                           "none" } };
+
+/*! \brief Returns the default xvg format, as modified by GMX_VIEW_XVG
+ * if that environment variable is set.
  *
  * \ingroup module_commandline
  */
-int getDefaultXvgFormat(gmx::ArrayRef<const char* const> xvgFormats)
+XvgFormat getDefaultXvgFormat()
 {
     const char* const select = getenv("GMX_VIEW_XVG");
     if (select != nullptr)
     {
-        ArrayRef<const char* const>::const_iterator i =
-                std::find(xvgFormats.begin(), xvgFormats.end(), std::string(select));
-        if (i != xvgFormats.end())
-        {
-            return std::distance(xvgFormats.begin(), i);
-        }
-        else
+        for (XvgFormat c : keysOf(c_xvgFormatNames))
         {
-            return exvgNONE - 1;
+            if (std::strcmp(select, c_xvgFormatNames[c]) == 0)
+            {
+                return c;
+            }
         }
+        return XvgFormat::None;
     }
-    /* The default is the first option */
-    return 0;
+    return XvgFormat::Xmgrace;
 }
 
 /*! \brief
@@ -386,8 +389,13 @@ void OptionsAdapter::pargsToOptions(Options* options, t_pargs* pa)
             return;
         case etENUM:
         {
+            // TODO This is the only use of LegacyEnumOption. It
+            // exists to support dozens of analysis tools use that
+            // don't make sense to fix without either test coverage or
+            // automated refactoring. No new uses of LegacyEnumOption
+            // should be made.
             const int defaultIndex = (pa->u.c[0] != nullptr ? nenum(pa->u.c) - 1 : 0);
-            data.optionInfo        = options->addOption(EnumIntOption(name)
+            data.optionInfo        = options->addOption(LegacyEnumOption<int>(name)
                                                          .store(&data.enumIndex)
                                                          .defaultValue(defaultIndex)
                                                          .enumValueFromNullTerminatedArray(pa->u.c + 1)
@@ -454,9 +462,6 @@ gmx_bool parse_common_args(int*               argc,
                            const char**       bugs,
                            gmx_output_env_t** oenv)
 {
-    /* This array should match the order of the enum in oenv.h */
-    const char* const xvg_formats[] = { "xmgrace", "xmgr", "none" };
-
     // Lambda function to test the (local) Flags parameter against a bit mask.
     auto isFlagSet = [Flags](unsigned long bits) { return (Flags & bits) == bits; };
 
@@ -464,8 +469,7 @@ gmx_bool parse_common_args(int*               argc,
     {
         double                         tbegin = 0.0, tend = 0.0, tdelta = 0.0;
         bool                           bBeginTimeSet = false, bEndTimeSet = false, bDtSet = false;
-        bool                           bView     = false;
-        int                            xvgFormat = 0;
+        bool                           bView = false;
         gmx::OptionsAdapter            adapter(*argc, argv);
         gmx::Options                   options;
         gmx::OptionsBehaviorCollection behaviors(&options);
@@ -493,7 +497,7 @@ gmx_bool parse_common_args(int*               argc,
             options.addOption(gmx::DoubleOption("dt").store(&tdelta).storeIsSet(&bDtSet).timeValue().description(
                     "Only use frame when t MOD dt = first time (default unit %t)"));
         }
-        gmx::TimeUnit timeUnit = gmx::TimeUnit_Default;
+        gmx::TimeUnit timeUnit = gmx::TimeUnit::Default;
         if (isFlagSet(PCA_TIME_UNIT))
         {
             std::shared_ptr<gmx::TimeUnitBehavior> timeUnitBehavior(new gmx::TimeUnitBehavior());
@@ -514,11 +518,13 @@ gmx_bool parse_common_args(int*               argc,
         {
             bXvgr = bXvgr || (fnm[i].ftp == efXVG);
         }
-        xvgFormat = gmx::getDefaultXvgFormat(xvg_formats);
+        XvgFormat xvgFormat = gmx::getDefaultXvgFormat();
         if (bXvgr)
         {
-            options.addOption(
-                    gmx::EnumIntOption("xvg").enumValue(xvg_formats).store(&xvgFormat).description("xvg plot formatting"));
+            options.addOption(gmx::EnumOption<XvgFormat>("xvg")
+                                      .enumValue(gmx::c_xvgFormatNames)
+                                      .store(&xvgFormat)
+                                      .description("xvg plot formatting"));
         }
 
         /* Now append the program specific arguments */
@@ -554,8 +560,7 @@ gmx_bool parse_common_args(int*               argc,
 
         /* set program name, command line, and default values for output options */
         // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)
-        output_env_init(oenv, gmx::getProgramContext(), static_cast<time_unit_t>(timeUnit + 1),
-                        bView, static_cast<xvg_format_t>(xvgFormat + 1), 0);
+        output_env_init(oenv, gmx::getProgramContext(), timeUnit, bView, xvgFormat, 0);
 
         /* Extract Time info from arguments */
         if (bBeginTimeSet)
index 2c2efd60fa162d772bdb8ceb3a06fcc40208fa91..77b1603d2a1d4fcebd7fbe3c19a6144c608ce5f8 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d2b82561643660463d7ff00fc3fcf8e837ce594c..ee92e41d5b56b22cebb7898b389939e7233c3134 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 5ff5ff97cf6a87540a43877abf0063f4148d0138..ac4a392c58a74d66f8d6ded1b7f43c87380d74da 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -52,12 +53,14 @@ if (UNIX)
 endif()
 
 gmx_add_unit_test(CommandLineUnitTests commandline-test
-                  cmdlinehelpmodule.cpp
-                  cmdlinehelpwriter.cpp
-                  cmdlinemodulemanager.cpp
-                  cmdlinemodulemanagertest.cpp
-                  cmdlineparser.cpp
-                  cmdlineprogramcontext.cpp
-                  filenm.cpp
-                  pargs.cpp)
+    CPP_SOURCE_FILES
+        cmdlinehelpmodule.cpp
+        cmdlinehelpwriter.cpp
+        cmdlinemodulemanager.cpp
+        cmdlinemodulemanagertest.cpp
+        cmdlineparser.cpp
+        cmdlineprogramcontext.cpp
+        filenm.cpp
+        pargs.cpp
+        )
 target_link_libraries(commandline-test PRIVATE onlinehelp-test-shared)
index 3630ed1ced836eabd0c04c1c999c1d635ed36236..c8dd3e9f28191149d126b22d041ccd7ff19581d7 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -57,6 +58,7 @@
 #include "gromacs/options/filenameoption.h"
 #include "gromacs/options/options.h"
 #include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/stringstream.h"
 #include "gromacs/utility/textwriter.h"
 
@@ -113,10 +115,19 @@ TEST_F(CommandLineHelpWriterTest, HandlesOptionTypes)
     options.addOption(DoubleOption("time").description("Time option (%t)").timeValue().defaultValue(10.0));
     options.addOption(StringOption("string").description("String option").defaultValue("test"));
     const char* const enumValues[] = { "no", "opt1", "opt2" };
+    enum class TestEnum : int
+    {
+        No,
+        Opt1,
+        Opt2,
+        Count
+    };
+    const gmx::EnumerationArray<TestEnum, const char*> testEnumNames = { { "no", "opt1", "opt2" } };
+
     options.addOption(
             StringOption("enum").description("Enum option").enumValue(enumValues).defaultEnumIndex(0));
     options.addOption(
-            EnumIntOption("ienum").description("Enum option").enumValue(enumValues).defaultValue(1));
+            EnumOption<TestEnum>("ienum").description("Enum option").enumValue(testEnumNames).defaultValue(TestEnum::Opt1));
 
     std::string filename;
     options.addOption(FileNameOption("f")
@@ -152,10 +163,11 @@ TEST_F(CommandLineHelpWriterTest, HandlesOptionTypes)
 }
 
 //! Enum value for testing.
-enum TestEnum
+enum class TestEnum : int
 {
-    eFoo,
-    eBar
+    Foo,
+    Bar,
+    Count
 };
 
 /*
@@ -182,8 +194,8 @@ TEST_F(CommandLineHelpWriterTest, HandlesDefaultValuesFromVariables)
     svalues.emplace_back("foo");
     options.addOption(StringOption("str").description("String option").storeVector(&svalues).multiValue());
 
-    TestEnum          evalue    = eBar;
-    const char* const allowed[] = { "foo", "bar" };
+    TestEnum                                           evalue  = TestEnum::Bar;
+    const gmx::EnumerationArray<TestEnum, const char*> allowed = { { "foo", "bar" } };
     options.addOption(
             EnumOption<TestEnum>("enum").description("Enum option").enumValue(allowed).store(&evalue));
 
index 8c495a69406411be73b0f4f08218a62b919dcf2c..c8b01f2a3a30b959f25e89883f58e0dbfcb8618c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 3647a32ee9191ff7b2afdfeaf7535569e77e8236..493d1c0b33896dd933babb24283736e3f41dce21 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 52af43774f870e54b1db786f3aeb57f6aca13866..5c12b2bdf4cebf473afa90a24d4172483141bfbc 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 0485ff4ada9e76b4b28a05fc781e5dc081e467e6..c5049530320c1822027e0cf3751ae773924c2a8b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 9a42421e1578a9207b123acbe7f3cbf58d54c82d..a2b1ae28259aedf459f333431f01ef47aa6d89c0 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index ac3099207e486b253fadf44905dd1118a6b10956..f10760581fec26b052fe82d55306c35bfe8083a5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -89,24 +89,24 @@ template<class T>
 class not_null
 {
 public:
-    static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
+    static_assert(std::is_assignable_v<T&, std::nullptr_t>, "T cannot be assigned nullptr.");
 
     //! Move constructor. Asserts in debug mode if \c is nullptr.
-    template<typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
+    template<typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
     constexpr explicit not_null(U&& u) : ptr_(std::forward<U>(u))
     {
         Expects(ptr_ != nullptr);
     }
 
     //! Simple constructor. Asserts in debug mode if \c u is nullptr.
-    template<typename = typename std::enable_if<!std::is_same<std::nullptr_t, T>::value>::type>
+    template<typename = std::enable_if_t<!std::is_same_v<std::nullptr_t, T>>>
     constexpr explicit not_null(T u) : ptr_(u)
     {
         Expects(ptr_ != nullptr);
     }
 
     //! Copy constructor.
-    template<typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
+    template<typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
     constexpr not_null(const not_null<U>& other) : not_null(other.get())
     {
     }
@@ -155,16 +155,14 @@ private:
 template<class T>
 not_null<T> make_not_null(T&& t)
 {
-    return not_null<typename std::remove_cv<typename std::remove_reference<T>::type>::type>{
-        std::forward<T>(t)
-    };
+    return not_null<std::remove_cv_t<std::remove_reference_t<T>>>{ std::forward<T>(t) };
 }
 
 //! Convenience function for making not_null pointers from smart pointers.
 template<class T>
 not_null<typename T::pointer> make_not_null(T& t)
 {
-    return not_null<typename std::remove_reference<T>::type::pointer>{ t.get() };
+    return not_null<typename std::remove_reference_t<T>::pointer>{ t.get() };
 }
 
 //! Operators to compare not_null pointers.
diff --git a/src/gromacs/compat/string_view.h b/src/gromacs/compat/string_view.h
deleted file mode 100644 (file)
index 07396d3..0000000
+++ /dev/null
@@ -1,1412 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-// Copyright 2017-2019 by Martin Moene
-//
-// string-view lite, a C++17-like string_view for C++98 and later.
-// For more information see https://github.com/martinmoene/string-view-lite
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-/*! \file
- * \brief Provides C++14-compatible implementation of std::string_view.
- *
- * This implementation is nearly identical to the reference
- * implementation at commit bf5824916b6895ccab0dbc2431520ee3b6d4f27f of
- * https://github.com/martinmoene/string_view-lite.git. The code has not
- * been linted with uncrustify so that any future updates to this
- * active repo can be incorporated easily, and //NOLINT comments and
- * braces around if-expressions added to suppress clang-tidy
- * warnings. Comments referring to GMX note those places where a
- * change has been made. The form of those changes have been made to
- * simplify the contents, while making it easy to import any bug fixes
- * that may appear in the source repository.
- *
- * There is no Doxygen for this code, but it is intended to conform to
- * that of std::string_view, so look in the usual C++17 documentation for
- * std::string_view for that.
- *
- * \todo Remove when requiring C++17, which has a standardized version
- * of std::string_view.
- *
- * \author Paul Bauer <paul.bauer.q@gmail.com>
- * \ingroup module_compat
- * \inpublicapi
- */
-// GMX Make include guard conform to GROMACS standards.
-#ifndef GMX_COMPAT_STRINGVIEW_H
-#define GMX_COMPAT_STRINGVIEW_H
-
-// GMX modification to suppress Doxygen checking
-#ifndef DOXYGEN
-
-#define string_view_lite_MAJOR  1
-#define string_view_lite_MINOR  1
-#define string_view_lite_PATCH  0
-
-#define string_view_lite_VERSION  nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH)
-
-#define nssv_STRINGIFY(  x )  nssv_STRINGIFY_( x )
-#define nssv_STRINGIFY_( x )  #x
-
-// string-view lite configuration:
-
-#define nssv_STRING_VIEW_DEFAULT  0
-#define nssv_STRING_VIEW_NONSTD   1
-#define nssv_STRING_VIEW_STD      2
-
-#if !defined( nssv_CONFIG_SELECT_STRING_VIEW )
-# define nssv_CONFIG_SELECT_STRING_VIEW  ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD )
-#endif
-
-#if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW )
-# error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_...
-#endif
-
-#ifndef  nssv_CONFIG_STD_SV_OPERATOR
-# define nssv_CONFIG_STD_SV_OPERATOR  0
-#endif
-
-#ifndef  nssv_CONFIG_USR_SV_OPERATOR
-# define nssv_CONFIG_USR_SV_OPERATOR  1
-#endif
-
-#ifdef   nssv_CONFIG_CONVERSION_STD_STRING
-# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS   nssv_CONFIG_CONVERSION_STD_STRING
-# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS  nssv_CONFIG_CONVERSION_STD_STRING
-#endif
-
-#ifndef  nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS  1
-#endif
-
-#ifndef  nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS  1
-#endif
-
-// Control presence of exception handling (try and auto discover):
-
-#ifndef nssv_CONFIG_NO_EXCEPTIONS
-# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
-#  define nssv_CONFIG_NO_EXCEPTIONS  0
-# else
-#  define nssv_CONFIG_NO_EXCEPTIONS  1
-# endif
-#endif
-
-// C++ language version detection (C++20 is speculative):
-// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
-
-#ifndef   nssv_CPLUSPLUS
-# if defined(_MSVC_LANG ) && !defined(__clang__)
-#  define nssv_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
-# else
-#  define nssv_CPLUSPLUS  __cplusplus
-# endif
-#endif
-
-#define nssv_CPP98_OR_GREATER  ( nssv_CPLUSPLUS >= 199711L )
-#define nssv_CPP11_OR_GREATER  ( nssv_CPLUSPLUS >= 201103L )
-#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L )
-#define nssv_CPP14_OR_GREATER  ( nssv_CPLUSPLUS >= 201402L )
-#define nssv_CPP17_OR_GREATER  ( nssv_CPLUSPLUS >= 201703L )
-#define nssv_CPP20_OR_GREATER  ( nssv_CPLUSPLUS >= 202000L )
-
-// use C++17 std::string_view if available and requested:
-
-#if nssv_CPP17_OR_GREATER && defined(__has_include )
-# if __has_include( <string_view> )
-#  define nssv_HAVE_STD_STRING_VIEW  1
-# else
-#  define nssv_HAVE_STD_STRING_VIEW  0
-# endif
-#else
-# define  nssv_HAVE_STD_STRING_VIEW  0
-#endif
-
-#define  nssv_USES_STD_STRING_VIEW  ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) )
-
-#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW )
-#define nssv_HAVE_ENDS_WITH     nssv_HAVE_STARTS_WITH
-
-//
-// Use C++17 std::string_view:
-//
-
-#if nssv_USES_STD_STRING_VIEW
-
-#include <string_view>
-
-// Extensions for std::string:
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-
-template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
-std::basic_string<CharT, Traits, Allocator>
-to_string( std::basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
-{
-    return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
-}
-
-template< class CharT, class Traits, class Allocator >
-std::basic_string_view<CharT, Traits>
-to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
-{
-    return std::basic_string_view<CharT, Traits>( s.data(), s.size() );
-}
-
-// Literal operators sv and _sv:
-
-#if nssv_CONFIG_STD_SV_OPERATOR
-
-using namespace std::literals::string_view_literals;
-
-#endif
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-
-inline namespace literals {
-inline namespace string_view_literals {
-
-
-constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept  // (1)
-{
-    return std::string_view{ str, len };
-}
-
-constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept  // (2)
-{
-    return std::u16string_view{ str, len };
-}
-
-constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept  // (3)
-{
-    return std::u32string_view{ str, len };
-}
-
-constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept  // (4)
-{
-    return std::wstring_view{ str, len };
-}
-
-}} // namespace literals::string_view_literals
-
-#endif // nssv_CONFIG_USR_SV_OPERATOR
-
-} // namespace nonstd
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-
-using std::string_view;
-using std::wstring_view;
-using std::u16string_view;
-using std::u32string_view;
-using std::basic_string_view;
-
-// literal "sv" and "_sv", see above
-
-using std::operator==;
-using std::operator!=;
-using std::operator<;
-using std::operator<=;
-using std::operator>;
-using std::operator>=;
-
-using std::operator<<;
-
-} // namespace nonstd
-
-#else // nssv_HAVE_STD_STRING_VIEW
-
-//
-// Before C++17: use string_view lite:
-//
-
-// Compiler versions:
-//
-// MSVC++ 6.0  _MSC_VER == 1200 (Visual Studio 6.0)
-// MSVC++ 7.0  _MSC_VER == 1300 (Visual Studio .NET 2002)
-// MSVC++ 7.1  _MSC_VER == 1310 (Visual Studio .NET 2003)
-// MSVC++ 8.0  _MSC_VER == 1400 (Visual Studio 2005)
-// MSVC++ 9.0  _MSC_VER == 1500 (Visual Studio 2008)
-// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
-// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
-// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
-// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
-// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
-
-#if defined(_MSC_VER ) && !defined(__clang__)
-# define nssv_COMPILER_MSVC_VER      (_MSC_VER )
-# define nssv_COMPILER_MSVC_VERSION  (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
-#else
-# define nssv_COMPILER_MSVC_VER      0
-# define nssv_COMPILER_MSVC_VERSION  0
-#endif
-
-#define nssv_COMPILER_VERSION( major, minor, patch )  (10 * ( 10 * (major) + (minor)) + (patch))
-
-#if defined(__clang__)
-# define nssv_COMPILER_CLANG_VERSION  nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
-#else
-# define nssv_COMPILER_CLANG_VERSION    0
-#endif
-
-#if defined(__GNUC__) && !defined(__clang__)
-# define nssv_COMPILER_GNUC_VERSION  nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
-#else
-# define nssv_COMPILER_GNUC_VERSION    0
-#endif
-
-// half-open range [lo..hi):
-#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
-
-// Presence of language and library features:
-
-#ifdef _HAS_CPP0X
-# define nssv_HAS_CPP0X  _HAS_CPP0X
-#else
-# define nssv_HAS_CPP0X  0
-#endif
-
-// Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
-
-#if nssv_COMPILER_MSVC_VER >= 1900
-# undef  nssv_CPP11_OR_GREATER
-# define nssv_CPP11_OR_GREATER  1
-#endif
-
-#define nssv_CPP11_90   (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
-#define nssv_CPP11_100  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
-#define nssv_CPP11_110  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
-#define nssv_CPP11_120  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
-#define nssv_CPP11_140  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
-#define nssv_CPP11_141  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
-
-#define nssv_CPP14_000  (nssv_CPP14_OR_GREATER)
-#define nssv_CPP17_000  (nssv_CPP17_OR_GREATER)
-
-// Presence of C++11 language features:
-
-#define nssv_HAVE_CONSTEXPR_11          nssv_CPP11_140
-#define nssv_HAVE_EXPLICIT_CONVERSION   nssv_CPP11_140
-#define nssv_HAVE_INLINE_NAMESPACE      nssv_CPP11_140
-#define nssv_HAVE_NOEXCEPT              nssv_CPP11_140
-#define nssv_HAVE_NULLPTR               nssv_CPP11_100
-#define nssv_HAVE_REF_QUALIFIER         nssv_CPP11_140
-#define nssv_HAVE_UNICODE_LITERALS      nssv_CPP11_140
-#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
-#define nssv_HAVE_WCHAR16_T             nssv_CPP11_100
-#define nssv_HAVE_WCHAR32_T             nssv_CPP11_100
-
-// GMX fixed definitions
-#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
-# define nssv_HAVE_STD_DEFINED_LITERALS  nssv_CPP11_140
-#endif
-
-// Presence of C++14 language features:
-
-#define nssv_HAVE_CONSTEXPR_14          nssv_CPP14_000
-
-// Presence of C++17 language features:
-
-#define nssv_HAVE_NODISCARD             nssv_CPP17_000
-
-// Presence of C++ library features:
-
-#define nssv_HAVE_STD_HASH              nssv_CPP11_120
-
-// C++ feature usage:
-
-#if nssv_HAVE_CONSTEXPR_11
-# define nssv_constexpr  constexpr
-#else
-# define nssv_constexpr  /*constexpr*/
-#endif
-
-#if  nssv_HAVE_CONSTEXPR_14
-# define nssv_constexpr14  constexpr
-#else
-# define nssv_constexpr14  /*constexpr*/
-#endif
-
-#if nssv_HAVE_EXPLICIT_CONVERSION
-# define nssv_explicit  explicit
-#else
-# define nssv_explicit  /*explicit*/
-#endif
-
-#if nssv_HAVE_INLINE_NAMESPACE
-# define nssv_inline_ns  inline
-#else
-# define nssv_inline_ns  /*inline*/
-#endif
-
-#if nssv_HAVE_NOEXCEPT
-# define nssv_noexcept  noexcept
-#else
-# define nssv_noexcept  /*noexcept*/
-#endif
-
-//#if nssv_HAVE_REF_QUALIFIER
-//# define nssv_ref_qual  &
-//# define nssv_refref_qual  &&
-//#else
-//# define nssv_ref_qual  /*&*/
-//# define nssv_refref_qual  /*&&*/
-//#endif
-
-#if nssv_HAVE_NULLPTR
-# define nssv_nullptr  nullptr
-#else
-# define nssv_nullptr  NULL
-#endif
-
-#if nssv_HAVE_NODISCARD
-# define nssv_nodiscard  [[nodiscard]]
-#else
-# define nssv_nodiscard  /*[[nodiscard]]*/
-#endif
-
-// Additional includes:
-
-#include <algorithm>
-#include <cassert>
-#include <iterator>
-#include <limits>
-#include <ostream>
-#include <string>   // std::char_traits<>
-
-#if ! nssv_CONFIG_NO_EXCEPTIONS
-# include <stdexcept>
-#endif
-
-#if nssv_CPP11_OR_GREATER
-# include <type_traits>
-#endif
-
-// Clang, GNUC, MSVC warning suppression macros:
-
-#if defined(__clang__)
-# pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wuser-defined-literals"
-#elif defined(__GNUC__)
-# pragma  GCC  diagnostic push
-# pragma  GCC  diagnostic ignored "-Wliteral-suffix"
-#endif // __clang__
-
-#if nssv_COMPILER_MSVC_VERSION >= 140
-# define nssv_SUPPRESS_MSGSL_WARNING(expr)        [[gsl::suppress(expr)]]
-# define nssv_SUPPRESS_MSVC_WARNING(code, descr)  __pragma(warning(suppress: code) )
-# define nssv_DISABLE_MSVC_WARNINGS(codes)        __pragma(warning(push))  __pragma(warning(disable: codes))
-#else
-# define nssv_SUPPRESS_MSGSL_WARNING(expr)
-# define nssv_SUPPRESS_MSVC_WARNING(code, descr)
-# define nssv_DISABLE_MSVC_WARNINGS(codes)
-#endif
-
-#if defined(__clang__)
-# define nssv_RESTORE_WARNINGS()  _Pragma("clang diagnostic pop")
-#elif defined(__GNUC__)
-# define nssv_RESTORE_WARNINGS()  _Pragma("GCC diagnostic pop")
-#elif nssv_COMPILER_MSVC_VERSION >= 140
-# define nssv_RESTORE_WARNINGS()  __pragma(warning(pop ))
-#else
-# define nssv_RESTORE_WARNINGS()
-#endif
-
-// Suppress the following MSVC (GSL) warnings:
-// - C4455, non-gsl   : 'operator ""sv': literal suffix identifiers that do not
-//                      start with an underscore are reserved
-// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
-//                      use brace initialization, gsl::narrow_cast or gsl::narow
-// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
-
-nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 )
-//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
-//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
-
-namespace nonstd { namespace sv_lite {
-
-template
-<
-    class CharT,
-    class Traits = std::char_traits<CharT>
->
-class basic_string_view;
-
-//
-// basic_string_view:
-//
-
-template
-<
-    class CharT,
-    class Traits /* = std::char_traits<CharT> */
->
-class basic_string_view
-{
-public:
-    // Member types:
-
-    typedef Traits traits_type;
-    typedef CharT  value_type;
-
-    typedef CharT       * pointer;
-    typedef CharT const * const_pointer;
-    typedef CharT       & reference;
-    typedef CharT const & const_reference;
-
-    typedef const_pointer iterator;
-    typedef const_pointer const_iterator;
-    typedef std::reverse_iterator< const_iterator > reverse_iterator;
-    typedef    std::reverse_iterator< const_iterator > const_reverse_iterator;
-
-    typedef std::size_t     size_type;
-    typedef std::ptrdiff_t  difference_type;
-
-    // 24.4.2.1 Construction and assignment:
-
-    nssv_constexpr basic_string_view() nssv_noexcept
-        : data_( nssv_nullptr )
-        , size_( 0 )
-    {}
-
-#if nssv_CPP11_OR_GREATER
-    nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default;
-#else
-    nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept
-        : data_( other.data_)
-        , size_( other.size_)
-    {}
-#endif
-
-    nssv_constexpr basic_string_view( CharT const * s, size_type count )
-        : data_( s )
-        , size_( count )
-    {}
-
-    nssv_constexpr basic_string_view( CharT const * s)
-        : data_( s )
-        , size_( Traits::length(s) )
-    {}
-
-    // Assignment:
-
-#if nssv_CPP11_OR_GREATER
-    nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default;
-#else
-    nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept
-    {
-        data_ = other.data_;
-        size_ = other.size_;
-        return *this;
-    }
-#endif
-
-    // 24.4.2.2 Iterator support:
-
-    nssv_constexpr const_iterator begin()  const nssv_noexcept { return data_;         }
-    nssv_constexpr const_iterator end()    const nssv_noexcept { return data_ + size_; }
-
-    nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
-    nssv_constexpr const_iterator cend()   const nssv_noexcept { return end();   }
-
-    nssv_constexpr const_reverse_iterator rbegin()  const nssv_noexcept { return const_reverse_iterator( end() );   }
-    nssv_constexpr const_reverse_iterator rend()    const nssv_noexcept { return const_reverse_iterator( begin() ); }
-
-    nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
-    nssv_constexpr const_reverse_iterator crend()   const nssv_noexcept { return rend();   }
-
-    // 24.4.2.3 Capacity:
-
-    nssv_constexpr size_type size()     const nssv_noexcept { return size_; }
-    nssv_constexpr size_type length()   const nssv_noexcept { return size_; }
-    nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); }
-
-    // since C++20
-    nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept
-    {
-        return 0 == size_;
-    }
-
-    // 24.4.2.4 Element access:
-
-    nssv_constexpr const_reference operator[]( size_type pos ) const
-    {
-        return data_at( pos );
-    }
-
-    nssv_constexpr14 const_reference at( size_type pos ) const
-    {
-#if nssv_CONFIG_NO_EXCEPTIONS
-        assert( pos < size() );
-#else
-        if ( pos >= size() )
-        {
-            throw std::out_of_range("nonst::string_view::at()");
-        }
-#endif
-        return data_at( pos );
-    }
-
-    nssv_constexpr const_reference front() const { return data_at( 0 );          }
-    nssv_constexpr const_reference back()  const { return data_at( size() - 1 ); }
-
-    nssv_constexpr const_pointer   data()  const nssv_noexcept { return data_; }
-
-    // 24.4.2.5 Modifiers:
-
-    nssv_constexpr14 void remove_prefix( size_type n )
-    {
-        assert( n <= size() );
-        data_ += n;
-        size_ -= n;
-    }
-
-    nssv_constexpr14 void remove_suffix( size_type n )
-    {
-        assert( n <= size() );
-        size_ -= n;
-    }
-
-    nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept
-    {
-        using std::swap;
-        swap( data_, other.data_ );
-        swap( size_, other.size_ );
-    }
-
-    // 24.4.2.6 String operations:
-
-    size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const
-    {
-#if nssv_CONFIG_NO_EXCEPTIONS
-        assert( pos <= size() );
-#else
-        if ( pos > size() )
-        {
-            throw std::out_of_range("nonst::string_view::copy()");
-        }
-#endif
-        const size_type rlen = (std::min)( n, size() - pos );
-
-        (void) Traits::copy( dest, data() + pos, rlen );
-
-        return rlen;
-    }
-
-    nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const
-    {
-#if nssv_CONFIG_NO_EXCEPTIONS
-        assert( pos <= size() );
-#else
-        if ( pos > size() )
-        {
-            throw std::out_of_range("nonst::string_view::substr()");
-        }
-#endif
-        return basic_string_view( data() + pos, (std::min)( n, size() - pos ) );
-    }
-
-    // compare(), 6x:
-
-    nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1)
-    {
-        if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
-        {
-            return result;
-        }
-
-        return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
-    }
-
-    nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2)
-    {
-        return substr( pos1, n1 ).compare( other );
-    }
-
-    nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3)
-    {
-        return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) );
-    }
-
-    nssv_constexpr int compare( CharT const * s ) const // (4)
-    {
-        return compare( basic_string_view( s ) );
-    }
-
-    nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5)
-    {
-        return substr( pos1, n1 ).compare( basic_string_view( s ) );
-    }
-
-    nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6)
-    {
-        return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) );
-    }
-
-    // 24.4.2.7 Searching:
-
-    // starts_with(), 3x, since C++20:
-
-    nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept  // (1)
-    {
-        return size() >= v.size() && compare( 0, v.size(), v ) == 0;
-    }
-
-    nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept  // (2)
-    {
-        return starts_with( basic_string_view( &c, 1 ) );
-    }
-
-    nssv_constexpr bool starts_with( CharT const * s ) const  // (3)
-    {
-        return starts_with( basic_string_view( s ) );
-    }
-
-    // ends_with(), 3x, since C++20:
-
-    nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept  // (1)
-    {
-        return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0;
-    }
-
-    nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept  // (2)
-    {
-        return ends_with( basic_string_view( &c, 1 ) );
-    }
-
-    nssv_constexpr bool ends_with( CharT const * s ) const  // (3)
-    {
-        return ends_with( basic_string_view( s ) );
-    }
-
-    // find(), 4x:
-
-    nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept  // (1)
-    {
-        return assert( v.size() == 0 || v.data() != nssv_nullptr )
-            , pos >= size()
-            ? npos
-            : to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
-    }
-
-    nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept  // (2)
-    {
-        return find( basic_string_view( &c, 1 ), pos );
-    }
-
-    nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const  // (3)
-    {
-        return find( basic_string_view( s, n ), pos );
-    }
-
-    nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const  // (4)
-    {
-        return find( basic_string_view( s ), pos );
-    }
-
-    // rfind(), 4x:
-
-    nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
-    {
-        if ( size() < v.size() )
-        {
-            return npos;
-        }
-
-        if ( v.empty() )
-        {
-            return (std::min)( size(), pos );
-        }
-
-        const_iterator last   = cbegin() + (std::min)( size() - v.size(), pos ) + v.size();
-        const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq );
-
-        return result != last ? size_type( result - cbegin() ) : npos;
-    }
-
-    nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept  // (2)
-    {
-        return rfind( basic_string_view( &c, 1 ), pos );
-    }
-
-    nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const  // (3)
-    {
-        return rfind( basic_string_view( s, n ), pos );
-    }
-
-    nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const  // (4)
-    {
-        return rfind( basic_string_view( s ), pos );
-    }
-
-    // find_first_of(), 4x:
-
-    nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept  // (1)
-    {
-        return pos >= size()
-            ? npos
-            : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
-    }
-
-    nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept  // (2)
-    {
-        return find_first_of( basic_string_view( &c, 1 ), pos );
-    }
-
-    nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const  // (3)
-    {
-        return find_first_of( basic_string_view( s, n ), pos );
-    }
-
-    nssv_constexpr size_type find_first_of(  CharT const * s, size_type pos = 0 ) const  // (4)
-    {
-        return find_first_of( basic_string_view( s ), pos );
-    }
-
-    // find_last_of(), 4x:
-
-    nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
-    {
-        return empty()
-            ? npos
-            : pos >= size()
-            ? find_last_of( v, size() - 1 )
-            : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) );
-    }
-
-    nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept  // (2)
-    {
-        return find_last_of( basic_string_view( &c, 1 ), pos );
-    }
-
-    nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const  // (3)
-    {
-        return find_last_of( basic_string_view( s, count ), pos );
-    }
-
-    nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const  // (4)
-    {
-        return find_last_of( basic_string_view( s ), pos );
-    }
-
-    // find_first_not_of(), 4x:
-
-    nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept  // (1)
-    {
-        return pos >= size()
-            ? npos
-            : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) );
-    }
-
-    nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept  // (2)
-    {
-        return find_first_not_of( basic_string_view( &c, 1 ), pos );
-    }
-
-    nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const  // (3)
-    {
-        return find_first_not_of( basic_string_view( s, count ), pos );
-    }
-
-    nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const  // (4)
-    {
-        return find_first_not_of( basic_string_view( s ), pos );
-    }
-
-    // find_last_not_of(), 4x:
-
-    nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
-    {
-        return empty()
-            ? npos
-            : pos >= size()
-            ? find_last_not_of( v, size() - 1 )
-            : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) );
-    }
-
-    nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept  // (2)
-    {
-        return find_last_not_of( basic_string_view( &c, 1 ), pos );
-    }
-
-    nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const  // (3)
-    {
-        return find_last_not_of( basic_string_view( s, count ), pos );
-    }
-
-    nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const  // (4)
-    {
-        return find_last_not_of( basic_string_view( s ), pos );
-    }
-
-    // Constants:
-
-#if nssv_CPP17_OR_GREATER
-    static nssv_constexpr size_type npos = size_type(-1);
-#elif nssv_CPP11_OR_GREATER
-    enum : size_type { npos = size_type(-1) };
-#else
-    enum { npos = size_type(-1) };
-#endif
-
-private:
-    struct not_in_view
-    {
-        const basic_string_view v;
-
-        nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {}
-
-        nssv_constexpr bool operator()( CharT c ) const
-        {
-            return npos == v.find_first_of( c );
-        }
-    };
-
-    nssv_constexpr size_type to_pos( const_iterator it ) const
-    {
-        return it == cend() ? npos : size_type( it - cbegin() );
-    }
-
-    nssv_constexpr size_type to_pos( const_reverse_iterator it ) const
-    {
-        return it == crend() ? npos : size_type( crend() - it - 1 );
-    }
-
-    nssv_constexpr const_reference data_at( size_type pos ) const
-    {
-#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 )
-        return data_[pos];
-#else
-        return assert( pos < size() ), data_[pos];
-#endif
-    }
-
-private:
-    const_pointer data_;
-    size_type     size_;
-
-public:
-#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-
-    template< class Allocator >
-    basic_string_view( std::basic_string<CharT, Traits, Allocator> const & s ) nssv_noexcept
-        : data_( s.data() )
-        , size_( s.size() )
-    {}
-
-#if nssv_HAVE_EXPLICIT_CONVERSION
-
-    template< class Allocator >
-    explicit operator std::basic_string<CharT, Traits, Allocator>() const
-    {
-        return to_string( Allocator() );
-    }
-
-#endif // nssv_HAVE_EXPLICIT_CONVERSION
-
-#if nssv_CPP11_OR_GREATER
-
-    template< class Allocator = std::allocator<CharT> >
-    std::basic_string<CharT, Traits, Allocator>
-    to_string( Allocator const & a = Allocator() ) const
-    {
-        return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
-    }
-
-#else
-
-    std::basic_string<CharT, Traits>
-    to_string() const
-    {
-        return std::basic_string<CharT, Traits>( begin(), end() );
-    }
-
-    template< class Allocator >
-    std::basic_string<CharT, Traits, Allocator>
-    to_string( Allocator const & a ) const
-    {
-        return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
-    }
-
-#endif // nssv_CPP11_OR_GREATER
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-};
-
-//
-// Non-member functions:
-//
-
-// 24.4.3 Non-member comparison functions:
-// lexicographically compare two string views (function template):
-
-template< class CharT, class Traits >
-nssv_constexpr bool operator== (
-    basic_string_view <CharT, Traits> lhs,
-    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) == 0 ; }
-
-template< class CharT, class Traits >
-nssv_constexpr bool operator!= (
-    basic_string_view <CharT, Traits> lhs,
-    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) != 0 ; }
-
-template< class CharT, class Traits >
-nssv_constexpr bool operator< (
-    basic_string_view <CharT, Traits> lhs,
-    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) < 0 ; }
-
-template< class CharT, class Traits >
-nssv_constexpr bool operator<= (
-    basic_string_view <CharT, Traits> lhs,
-    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) <= 0 ; }
-
-template< class CharT, class Traits >
-nssv_constexpr bool operator> (
-    basic_string_view <CharT, Traits> lhs,
-    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) > 0 ; }
-
-template< class CharT, class Traits >
-nssv_constexpr bool operator>= (
-    basic_string_view <CharT, Traits> lhs,
-    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) >= 0 ; }
-
-// Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
-// Implementations shall provide sufficient additional overloads marked
-// constexpr and noexcept so that an object t with an implicit conversion
-// to S can be compared according to Table 67.
-
-#if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
-
-#define nssv_BASIC_STRING_VIEW_I(T,U)  typename std::decay< basic_string_view<T,U> >::type
-
-#if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 )
-# define nssv_MSVC_ORDER(x)  , int=x
-#else
-# define nssv_MSVC_ORDER(x)  /*, int=x*/
-#endif
-
-// ==
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
-nssv_constexpr bool operator==(
-         basic_string_view  <CharT, Traits> lhs,
-    nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) == 0; }
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
-nssv_constexpr bool operator==(
-    nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
-         basic_string_view  <CharT, Traits> rhs ) nssv_noexcept
-{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
-
-// !=
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
-nssv_constexpr bool operator!= (
-         basic_string_view  < CharT, Traits > lhs,
-    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
-{ return lhs.size() != rhs.size() || lhs.compare( rhs ) != 0 ; }
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
-nssv_constexpr bool operator!= (
-    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
-         basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) != 0 ; }
-
-// <
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
-nssv_constexpr bool operator< (
-         basic_string_view  < CharT, Traits > lhs,
-    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) < 0 ; }
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
-nssv_constexpr bool operator< (
-    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
-         basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) < 0 ; }
-
-// <=
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
-nssv_constexpr bool operator<= (
-         basic_string_view  < CharT, Traits > lhs,
-    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) <= 0 ; }
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
-nssv_constexpr bool operator<= (
-    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
-         basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) <= 0 ; }
-
-// >
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
-nssv_constexpr bool operator> (
-         basic_string_view  < CharT, Traits > lhs,
-    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) > 0 ; }
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
-nssv_constexpr bool operator> (
-    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
-         basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) > 0 ; }
-
-// >=
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
-nssv_constexpr bool operator>= (
-         basic_string_view  < CharT, Traits > lhs,
-    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) >= 0 ; }
-
-template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
-nssv_constexpr bool operator>= (
-    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
-         basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
-{ return lhs.compare( rhs ) >= 0 ; }
-
-#undef nssv_MSVC_ORDER
-#undef nssv_BASIC_STRING_VIEW_I
-
-#endif // nssv_CPP11_OR_GREATER
-
-// 24.4.4 Inserters and extractors:
-
-namespace detail {
-
-template< class Stream >
-void write_padding( Stream & os, std::streamsize n )
-{
-    for ( std::streamsize i = 0; i < n; ++i )
-    {
-        os.rdbuf()->sputc( os.fill() );
-    }
-}
-
-template< class Stream, class View >
-Stream & write_to_stream( Stream & os, View const & sv )
-{
-    typename Stream::sentry sentry( os );
-
-    if ( !os )
-    {
-        return os;
-    }
-
-    const std::streamsize length = static_cast<std::streamsize>( sv.length() );
-
-    // Whether, and how, to pad:
-    const bool      pad = ( length < os.width() );
-    const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right;
-
-    if ( left_pad )
-    {
-        write_padding( os, os.width() - length );
-    }
-
-    // Write span characters:
-    os.rdbuf()->sputn( sv.begin(), length );
-
-    if ( pad && !left_pad )
-    {
-        write_padding( os, os.width() - length );
-    }
-
-    // Reset output stream width:
-    os.width( 0 );
-
-    return os;
-}
-
-} // namespace detail
-
-template< class CharT, class Traits >
-std::basic_ostream<CharT, Traits> &
-operator<<(
-    std::basic_ostream<CharT, Traits>& os,
-    basic_string_view <CharT, Traits> sv )
-{
-    return detail::write_to_stream( os, sv );
-}
-
-// Several typedefs for common character types are provided:
-
-typedef basic_string_view<char>      string_view;
-typedef basic_string_view<wchar_t>   wstring_view;
-#if nssv_HAVE_WCHAR16_T
-typedef basic_string_view<char16_t>  u16string_view;
-typedef basic_string_view<char32_t>  u32string_view;
-#endif
-
-} // namespace sv_lite
-} // namespace nonstd
-
-//
-// 24.4.6 Suffix for basic_string_view literals:
-//
-
-#if nssv_HAVE_USER_DEFINED_LITERALS
-
-namespace nonstd {
-nssv_inline_ns namespace literals {
-nssv_inline_ns namespace string_view_literals {
-
-#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
-
-nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept  // (1)
-{
-    return nonstd::sv_lite::string_view{ str, len };
-}
-
-nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept  // (2)
-{
-    return nonstd::sv_lite::u16string_view{ str, len };
-}
-
-nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept  // (3)
-{
-    return nonstd::sv_lite::u32string_view{ str, len };
-}
-
-nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept  // (4)
-{
-    return nonstd::sv_lite::wstring_view{ str, len };
-}
-
-#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-
-nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept  // (1)
-{
-    return nonstd::sv_lite::string_view{ str, len };
-}
-
-nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept  // (2)
-{
-    return nonstd::sv_lite::u16string_view{ str, len };
-}
-
-nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept  // (3)
-{
-    return nonstd::sv_lite::u32string_view{ str, len };
-}
-
-nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept  // (4)
-{
-    return nonstd::sv_lite::wstring_view{ str, len };
-}
-
-#endif // nssv_CONFIG_USR_SV_OPERATOR
-
-} // namespace string_view_literals
-} // namespace literals
-} // namespace nonstd
-
-#endif
-
-//
-// Extensions for std::string:
-//
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-namespace sv_lite {
-
-// Exclude MSVC 14 (19.00): it yields ambiguous to_string():
-
-#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
-
-template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
-std::basic_string<CharT, Traits, Allocator>
-to_string( basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
-{
-    return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
-}
-
-#else
-
-template< class CharT, class Traits >
-std::basic_string<CharT, Traits>
-to_string( basic_string_view<CharT, Traits> v )
-{
-    return std::basic_string<CharT, Traits>( v.begin(), v.end() );
-}
-
-template< class CharT, class Traits, class Allocator >
-std::basic_string<CharT, Traits, Allocator>
-to_string( basic_string_view<CharT, Traits> v, Allocator const & a )
-{
-    return std::basic_string<CharT, Traits, Allocator>( v.begin(), v.end(), a );
-}
-
-#endif // nssv_CPP11_OR_GREATER
-
-template< class CharT, class Traits, class Allocator >
-basic_string_view<CharT, Traits>
-to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
-{
-    return basic_string_view<CharT, Traits>( s.data(), s.size() );
-}
-
-} // namespace sv_lite
-} // namespace nonstd
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-//
-// make types and algorithms available in namespace nonstd:
-//
-
-namespace nonstd {
-
-using sv_lite::basic_string_view;
-using sv_lite::string_view;
-using sv_lite::wstring_view;
-
-#if nssv_HAVE_WCHAR16_T
-using sv_lite::u16string_view;
-#endif
-#if nssv_HAVE_WCHAR32_T
-using sv_lite::u32string_view;
-#endif
-
-// literal "sv"
-
-using sv_lite::operator==;
-using sv_lite::operator!=;
-using sv_lite::operator<;
-using sv_lite::operator<=;
-using sv_lite::operator>;
-using sv_lite::operator>=;
-
-using sv_lite::operator<<;
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-using sv_lite::to_string;
-using sv_lite::to_string_view;
-#endif
-
-} // namespace nonstd
-
-// 24.4.5 Hash support (C++11):
-
-// Note: The hash value of a string view object is equal to the hash value of
-// the corresponding string object.
-
-#if nssv_HAVE_STD_HASH
-
-#include <functional>
-
-namespace std {
-
-template<>
-struct hash< nonstd::string_view >
-{
-public:
-    std::size_t operator()( nonstd::string_view v ) const nssv_noexcept
-    {
-        return std::hash<std::string>()( std::string( v.data(), v.size() ) );
-    }
-};
-
-template<>
-struct hash< nonstd::wstring_view >
-{
-public:
-    std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept
-    {
-        return std::hash<std::wstring>()( std::wstring( v.data(), v.size() ) );
-    }
-};
-
-template<>
-struct hash< nonstd::u16string_view >
-{
-public:
-    std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept
-    {
-        return std::hash<std::u16string>()( std::u16string( v.data(), v.size() ) );
-    }
-};
-
-template<>
-struct hash< nonstd::u32string_view >
-{
-public:
-    std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept
-    {
-        return std::hash<std::u32string>()( std::u32string( v.data(), v.size() ) );
-    }
-};
-
-} // namespace std
-
-#endif // nssv_HAVE_STD_HASH
-
-nssv_RESTORE_WARNINGS()
-
-#endif // nssv_HAVE_STD_STRING_VIEW
-
-namespace gmx
-{
-namespace compat
-{
-    using namespace nonstd;
-} // namespace compat
-} // namespace gmx
-
-// GMX modification to suppress Doxygen checking
-#endif // DOXYGEN
-
-#endif
index dc9be01d2bd4f653043f4f567461ebc2d9843507..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,2 +0,0 @@
-string_view.cpp             filter=copyright
-optional.cpp             filter=copyright
index 123728bed32cf3dba6d346f609d4e503d4258779..67f5ad9a7f5f1f8db94dc8a195c1ac5a95a3344e 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 
 if (GMX_BUILD_UNITTESTS)
     gmx_add_unit_test(CompatibilityHelpersTests compat-test
-                     optional.cpp
-                     pointers.cpp
-                     string_view.cpp
-                     )
-    check_cxx_compiler_flag("-Wno-unused-member-function" HAS_NO_UNUSED_MEMBER_FUNCTION)
-    if (HAS_NO_UNUSED_MEMBER_FUNCTION)
-        set_source_files_properties(optional.cpp PROPERTIES COMPILE_FLAGS -Wno-unused-member-function)
-    endif()
+        CPP_SOURCE_FILES
+            pointers.cpp
+            )
+    # Maintainer note: The files here may be borrowed from other projects, and
+    # can require additional handling for compiler flags, warning suppression,
+    # etcetera. Rather than documenting best practices and tips here, we refer
+    # the reader to the `git` repository history for examples of how integration
+    # caveats have been handled in the past (for borrowed code that has since
+    # been removed when it became unnecessary).
 endif()
diff --git a/src/gromacs/compat/tests/optional.cpp b/src/gromacs/compat/tests/optional.cpp
deleted file mode 100644 (file)
index 672cb1d..0000000
+++ /dev/null
@@ -1,1474 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-//
-// Copyright 2014-2018 by Martin Moene
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// optional lite is inspired on std::optional by Fernando Cacciola and Andrzej Krzemienski
-// and on expected lite by Martin Moene.
-
-/*! \internal \file
- * \brief Tests for C++14-compatible implementation of std::optional.
- *
- * These tests are similer to those used in commit 7f4c6d4b4c8 of
- * https://github.com/martinmoene/optional-lite.git. The code has not
- * been linted with uncrustify so that any future updates to this
- * active repo can be incorporated easily, and //NOLINT comments added
- * to suppress clang-tidy warnings. The form of those
- * changes have been made to simplify the contents, while making it
- * easy to import any bug fixes that may appear in the source
- * repository.
- *
- * \todo Remove when requiring C++17, which has a standardized version
- * of std::optional.
- *
- * \author Mark Abraham <mark.j.abraham@gmail.com>
- * \ingroup module_compat
- */
-#include "gmxpre.h"
-
-#include "gromacs/compat/optional.h"
-
-#include <initializer_list>
-#include <iostream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include "gromacs/utility/strconvert.h"
-
-// GMX modification to suppress Doxygen checking
-#ifndef DOXYGEN
-
-namespace gmx
-{
-
-using namespace compat;
-
-#if optional_USES_STD_OPTIONAL && defined(__APPLE__)
-# define opt_value( o ) *o
-#else
-# define opt_value( o )  o.value()
-#endif
-
-namespace {
-
-struct nonpod { nonpod(){} };
-
-struct Implicit { int x;          Implicit(int v) : x(v) {} };
-struct Explicit { int x; explicit Explicit(int v) : x(v) {} };
-
-bool operator==( Implicit a, Implicit b ) { return a.x == b.x; }
-bool operator==( Explicit a, Explicit b ) { return a.x == b.x; }
-
-std::ostream & operator<<( std::ostream & os, Implicit i ) { return os << "Implicit:" << i.x; }
-std::ostream & operator<<( std::ostream & os, Explicit e ) { return os << "Explicit:" << e.x; }
-
-// ensure comparison of pointers for lest:
-
-// const void * lest_nullptr = 0;
-
-// The following tracer code originates as Oracle from Optional by
-// Andrzej Krzemienski, https://github.com/akrzemi1/Optional.
-
-enum State
-{
-    /* 0 */ default_constructed,
-    /* 1 */ value_copy_constructed,
-    /* 2 */ value_move_constructed,
-    /* 3 */ copy_constructed,
-    /* 4 */ move_constructed,
-    /* 5 */ move_assigned,
-    /* 6 */ copy_assigned,
-    /* 7 */ value_copy_assigned,
-    /* 8 */ value_move_assigned,
-    /* 9 */ moved_from,
-    /*10 */ value_constructed
-};
-
-struct V
-{
-    State state;
-    int   value;
-
-    V(       ) : state( default_constructed ), value( deflt() ) {}
-    V( int v ) : state( value_constructed   ), value( v       ) {}
-
-    bool operator==( V const & rhs ) const { return state == rhs.state && value == rhs.value; }
-    bool operator==( int       val ) const { return value == val; }
-
-    static int deflt() { return 42; }
-};
-
-struct S
-{
-    State state;
-    V     value;
-
-    S(             ) : state( default_constructed    ) {}
-    S( V const & v ) : state( value_copy_constructed ), value( v ) {}
-    S( S const & s ) : state( copy_constructed       ), value( s.value        ) {}
-
-    S & operator=( V const & v ) { state = value_copy_assigned; value = v; return *this; }
-    S & operator=( const S & s ) { state = copy_assigned      ; value = s.value; return *this; }
-
-#if optional_CPP11_OR_GREATER
-    S(             V && v ) : state(  value_move_constructed ), value(  std::move( v       ) ) { v.state = moved_from; } //NOLINT(performance-move-const-arg) 
-    S(             S && s ) : state(  move_constructed       ), value(  std::move( s.value ) ) { s.state = moved_from; } //NOLINT(performance-noexcept-move-constructor,performance-move-const-arg)
-
-    S & operator=( V && v ) { state = value_move_assigned     ; value = std::move( v       ); v.state = moved_from; return *this; } //NOLINT(performance-noexcept-move-constructor,performance-move-const-arg,bugprone-use-after-move)
-    S & operator=( S && s ) { state = move_assigned           ; value = std::move( s.value ); s.state = moved_from; return *this; } //NOLINT(performance-noexcept-move-constructor,performance-move-const-arg)
-#endif
-
-    bool operator==( S const & rhs ) const { return state == rhs.state && value == rhs.value; }
-};
-
-inline std::ostream & operator<<( std::ostream & os, V const & v )
-{
-    return os << "[V:" << toString( v.value ) << "]";
-}
-
-struct NoDefault
-{
-    NoDefault( NoDefault const & ) {} //NOLINT(readability-named-parameter)
-    NoDefault & operator=( NoDefault const & ) { return *this; } //NOLINT(readability-named-parameter)
-
-#if optional_CPP11_OR_GREATER
-    NoDefault( NoDefault && ) = default;
-    NoDefault & operator=( NoDefault && ) = default;
-#endif
-
-private:
-    NoDefault();
-};
-
-struct CopyOnly
-{
-    CopyOnly( CopyOnly const & ) {} //NOLINT(readability-named-parameter)
-    CopyOnly & operator=( CopyOnly const & ) { return *this; } //NOLINT(readability-named-parameter)
-
-private:
-    CopyOnly();
-#if optional_CPP11_OR_GREATER
-    CopyOnly( CopyOnly && ) = delete;
-    CopyOnly & operator=( CopyOnly && ) = delete;
-#endif
-};
-
-struct MoveOnly
-{
-#if optional_CPP11_OR_GREATER
-    MoveOnly( MoveOnly && ) = default;
-    MoveOnly & operator=( MoveOnly && ) = default;
-#endif
-
-private:
-    MoveOnly();
-    MoveOnly( MoveOnly const & );
-    MoveOnly & operator=( MoveOnly const & );
-};
-
-struct NoDefaultCopyMove
-{
-    std::string text;
-    NoDefaultCopyMove( std::string txt ) : text( txt ) {} //NOLINT(readability-named-parameter,performance-unnecessary-value-param)
-
-private:
-    NoDefaultCopyMove();
-    NoDefaultCopyMove( NoDefaultCopyMove const & );
-    NoDefaultCopyMove & operator=( NoDefaultCopyMove const & );
-#if optional_CPP11_OR_GREATER
-    NoDefaultCopyMove( NoDefaultCopyMove && ) = delete;
-    NoDefaultCopyMove & operator=( NoDefaultCopyMove && ) = delete;
-#endif
-};
-
-#if optional_CPP11_OR_GREATER
-struct InitList
-{
-    std::vector<int> vec;
-    char c;
-    S s;
-
-    InitList( std::initializer_list<int> il, char k, S const & t )
-    : vec( il ), c( k ), s( t ) {}
-
-    InitList( std::initializer_list<int> il, char k, S && t )
-    : vec( il ), c( k ), s( std::move( t ) ) {}
-};
-#endif
-
-} // anonymous namespace
-
-//
-// test specification:
-//
-
-TEST(OptionalTest, UnionCanContainNonPodTypes)
-{
-    SCOPED_TRACE("union: A C++03 union can only contain POD types");
-    union U
-    {
-        char c;
-#if optional_CPP11_OR_GREATER
-        nonpod np;
-#endif
-    };
-}
-
-//
-// optional member operations:
-//
-
-// construction:
-
-TEST(OptionalTest, CanDefaultConstructEmpty)
-{
-    SCOPED_TRACE("optional: Allows to default construct an empty optional (1a)");
-    optional<int> a;
-
-    EXPECT_TRUE( !a );
-}
-
-TEST(OptionalTest, CanConstructFromNullopt)
-{
-    SCOPED_TRACE("optional: Allows to explicitly construct a disengaged, empty optional via nullopt (1b)");
-    optional<int> a( nullopt );
-
-    EXPECT_TRUE( !a );
-}
-
-TEST(OptionalTest, CanDefaultConstructUsingNonDefaultConstructibleType)
-{
-    SCOPED_TRACE("optional: Allows to default construct an empty optional with a non-default-constructible (1a)");
-//  FAILS: NoDefault nd;
-//  FAILS: NoDefaultCopyMove ndcm;
-    optional<NoDefault> ond;
-    optional<CopyOnly> oco;
-    optional<MoveOnly> omo;
-    optional<NoDefaultCopyMove> ondcm;
-
-    EXPECT_TRUE( !ond );
-    EXPECT_TRUE( !oco );
-    EXPECT_TRUE( !omo );
-    EXPECT_TRUE( !ondcm );
-}
-
-TEST(OptionalTest, CanCopyConstructFromEmptyOptional)
-{
-    SCOPED_TRACE("optional: Allows to copy-construct from empty optional (2)");
-    optional<int> a;
-
-    optional<int> b( a ); //NOLINT(performance-unnecessary-copy-initialization)
-
-    EXPECT_TRUE( !b );
-}
-
-TEST(OptionalTest, CanMoveConstructFromEmptyOptional)
-{
-    SCOPED_TRACE("optional: Allows to move-construct from empty optional (C++11, 3)");
-#if optional_CPP11_OR_GREATER
-    optional<int> a;
-
-    optional<int> b( std::move( a ) );
-
-    EXPECT_TRUE( !b );
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanCopyConstructFromEmptyOptionalWithExplicitConversion)
-{
-    SCOPED_TRACE("optional: Allows to copy-construct from empty optional, explicit converting (C++11, 4a)");
-#if optional_CPP11_OR_GREATER
-    optional<int> a;
-
-    optional<Explicit> b{ a };
-
-    EXPECT_TRUE( !b );
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanCopyConstructFromEmptyOptionalNonExplicitConverting)
-{
-    SCOPED_TRACE("optional: Allows to copy-construct from empty optional, non-explicit converting (4b)");
-    optional<int> a;
-
-    optional<Implicit> b( a );
-
-    EXPECT_TRUE( !b );
-}
-
-TEST(OptionalTest, CanMoveConstructFromEmptyOptionalExplicitConverting)
-{
-    SCOPED_TRACE("optional: Allows to move-construct from empty optional, explicit converting (C++11, 5a)");
-#if optional_CPP11_OR_GREATER
-    optional<int> a;
-
-    optional<Explicit> b{ std::move( a ) };
-
-    EXPECT_TRUE( !b );
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanMoveConstructFromEmptyOptionalNonExplicitConverting)
-{
-    SCOPED_TRACE("optional: Allows to move-construct from empty optional, non-explicit converting (C++11, 5a)");
-#if optional_CPP11_OR_GREATER
-    optional<int> a;
-
-    optional<Implicit> b( std::move( a ) );
-
-    EXPECT_TRUE( !b );
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanCopyConstructFromNonEmptyOptional)
-{
-    SCOPED_TRACE("optional: Allows to copy-construct from non-empty optional (2)");
-    optional<int> a( 7 );
-
-    optional<int> b( a );
-
-    EXPECT_TRUE(  b    );
-    EXPECT_EQ  ( *b, 7 );
-}
-
-TEST(OptionalTest, CanCopyConstructFromNonEmptyOptionalExplicitConverting)
-{
-    SCOPED_TRACE("optional: Allows to copy-construct from non-empty optional, explicit converting (C++11, 4a)");
-#if optional_CPP11_OR_GREATER
-    optional<int> a( 7 );
-
-    optional<Explicit> b{ a };
-
-    EXPECT_TRUE(  b                );
-    EXPECT_EQ( *b, Explicit{7} );
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanCopyConstructFromNonEmptyOptionalNonExplicitConverting)
-{
-    SCOPED_TRACE("optional: Allows to copy-construct from non-empty optional, non-explicit converting (4b)");
-    optional<int> a( 7 );
-
-    optional<Implicit> b( a );
-
-    EXPECT_TRUE(  b                );
-    EXPECT_EQ( *b, Implicit(7) );
-}
-
-TEST(OptionalTest, CanMoveConstructFromNonEmptyOptional)
-{
-    SCOPED_TRACE("optional: Allows to move-construct from non-empty optional (C++11, 3)");
-#if optional_CPP11_OR_GREATER
-    optional<int> a( 7 );
-
-    optional<int> b( std::move( a ) );
-
-    EXPECT_TRUE(  b      );
-    EXPECT_EQ( *b, 7 );
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanMoveConstructFromNonEmptyOptionalExplicitConverting)
-{
-    SCOPED_TRACE("optional: Allows to move-construct from non-empty optional, explicit converting (C++11, 5a)");
-#if optional_CPP11_OR_GREATER
-    optional<int> a( 7 );
-
-    optional<Explicit> b{ std::move( a ) };
-
-    EXPECT_TRUE(  b                );
-    EXPECT_EQ( *b, Explicit{7} );
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanMoveConstructFromNonEmptyOptionalNonExplicitConverting)
-{
-    SCOPED_TRACE("optional: Allows to move-construct from non-empty optional, non-explicit converting (C++11, 5b)");
-#if optional_CPP11_OR_GREATER
-    optional<int> a( 7 );
-
-    optional<Implicit> b( std::move( a ) );
-
-    EXPECT_TRUE(  b                );
-    EXPECT_EQ( *b, Implicit(7) );
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-namespace {
-
-#if optional_CPP11_OR_GREATER
-    void use_optional( nonstd::optional<Implicit> ) {} //NOLINT(readability-named-parameter,performance-unnecessary-value-param)
-#else
-    template< typename T >
-    void use_optional( T ) {}
-#endif
-
-}
-
-TEST(OptionalTest, CanCopyConstructFromLiteral)
-{
-    SCOPED_TRACE("optional: Allows to copy-construct from literal value (8)");
-    use_optional( 7 );
-    optional<int> a = 7;
-
-    EXPECT_TRUE(  a      );
-    EXPECT_EQ( *a, 7 );
-}
-
-TEST(OptionalTest, CanCopyConstructFromLiteralConverting)
-{
-    SCOPED_TRACE("optional: Allows to copy-construct from literal value, converting (8)");
-    use_optional( '7' );
-    optional<int> a = '7';
-
-    EXPECT_TRUE(  a        );
-    EXPECT_EQ( *a, '7' );
-}
-
-TEST(OptionalTest, CanCopyConstructFromValue)
-{
-    SCOPED_TRACE("optional: Allows to copy-construct from value (8)");
-    const int i = 7;
-
-    use_optional( i );
-    optional<int> a( i );
-
-    EXPECT_TRUE(  a      );
-    EXPECT_EQ( *a, 7 );
-}
-
-TEST(OptionalTest, CanCopyConstructFromValueConverting)
-{
-    SCOPED_TRACE("optional: Allows to copy-construct from value, converting (8)");
-    const char c = '7';
-
-    use_optional( c );
-    optional<int> a( c );
-
-    EXPECT_TRUE(  a        );
-    EXPECT_EQ( *a, '7' );
-}
-
-TEST(OptionalTest, CanMoveConstructFromValue)
-{
-    SCOPED_TRACE("optional: Allows to move-construct from value (C++11, 8b)");
-#if optional_CPP11_OR_GREATER
-    S s( 7 );
-
-    optional<S> a( std::move( s ) );
-
-    EXPECT_EQ( a->value, 7                );
-    EXPECT_EQ( a->state, move_constructed );
-    EXPECT_EQ(  s.state, moved_from       ); //NOLINT(bugprone-use-after-move)
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanMoveConstructFromValueExplicitConverting)
-{
-    SCOPED_TRACE("optional: Allows to move-construct from value, explicit converting (C++11, 8a)");
-#if optional_CPP11_OR_GREATER
-    int seven = 7;
-
-    optional<Explicit> a{ std::move( seven ) }; //NOLINT(performance-move-const-arg)
-
-    EXPECT_TRUE(  a                );
-    EXPECT_EQ( *a, Explicit{7} );
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanMoveConstructFromValueNonExplicitConverting)
-{
-    SCOPED_TRACE("optional: Allows to move-construct from value, non-explicit converting (C++11, 8b)");
-#if optional_CPP11_OR_GREATER
-    int seven = 7;
-    optional<Implicit> a( std::move( seven ) ); //NOLINT(performance-move-const-arg)
-
-    EXPECT_TRUE(  a                );
-    EXPECT_EQ( *a, Implicit(7) );
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanInPlaceConstructFromLiteral)
-{
-    SCOPED_TRACE("optional: Allows to in-place construct from literal value (C++11, 6)");
-#if optional_CPP11_OR_GREATER
-    using pair_t = std::pair<char, int>;
-
-    optional<pair_t> a( in_place, 'a', 7 );
-
-    EXPECT_EQ( a->first , 'a' );
-    EXPECT_EQ( a->second,  7  );
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanInPlaceCopyConstructFromValue)
-{
-    SCOPED_TRACE("optional: Allows to in-place copy-construct from value (C++11, 6)");
-#if optional_CPP11_OR_GREATER
-    char c = 'a'; S s( 7 );
-    using pair_t = std::pair<char, S>;
-
-    optional<pair_t> a( in_place, c, s );
-
-    EXPECT_EQ( a->first       , 'a' );
-    EXPECT_EQ( a->second.value,  7  );
-#if optional_USES_STD_OPTIONAL
-    EXPECT_EQ( a->second.state, copy_constructed );
-#else
-    EXPECT_EQ( a->second.state, move_constructed );
-#endif
-    EXPECT_NE(         s.state , moved_from       );
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanInPlaceMoveConstructFromValue)
-{
-    SCOPED_TRACE("optional: Allows to in-place move-construct from value (C++11, 6)");
-#if optional_CPP11_OR_GREATER
-    char c = 'a'; S s( 7 );
-    using pair_t = std::pair<char, S>;
-
-    optional<pair_t> a( in_place, c, std::move( s ) );
-
-    EXPECT_EQ( a->first       , 'a' );
-    EXPECT_EQ( a->second.value,  7  );
-    EXPECT_EQ( a->second.state, move_constructed );
-    EXPECT_EQ(         s.state, moved_from       ); //NOLINT(bugprone-use-after-move)
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanInPlaceCopyConstructFromInitializerList)
-{
-    SCOPED_TRACE("optional: Allows to in-place copy-construct from initializer-list (C++11, 7)");
-#if optional_CPP11_OR_GREATER
-    S s( 7 );
-    optional<InitList> a( in_place, { 7, 8, 9, }, 'a', s );
-
-    EXPECT_EQ( a->vec[0] ,  7 );
-    EXPECT_EQ( a->vec[1] ,  8 );
-    EXPECT_EQ( a->vec[2] ,  9 );
-    EXPECT_EQ( a->c      , 'a');
-    EXPECT_EQ( a->s.value,  7 );
-#if optional_USES_STD_OPTIONAL
-    EXPECT_EQ( a->s.state , copy_constructed );
-#else
-    EXPECT_EQ( a->s.state , move_constructed );
-#endif
-    EXPECT_NE(    s.state , moved_from       );
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanInPlaceMoveConstructFromInitializerList)
-{
-    SCOPED_TRACE("optional: Allows to in-place move-construct from initializer-list (C++11, 7)");
-#if optional_CPP11_OR_GREATER
-    S s( 7 );
-    optional<InitList> a( in_place, { 7, 8, 9, }, 'a', std::move( s ) );
-
-    EXPECT_EQ( a->vec[0]  ,  7  );
-    EXPECT_EQ( a->vec[1]  ,  8  );
-    EXPECT_EQ( a->vec[2]  ,  9  );
-    EXPECT_EQ( a->c       , 'a' );
-    EXPECT_EQ( a->s.value ,  7  );
-    EXPECT_EQ( a->s.state , move_constructed );
-    EXPECT_EQ(    s.state , moved_from       ); //NOLINT(bugprone-use-after-move)
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-// assignment:
-
-TEST(OptionalTest, CanAssignNulloptToDisengage)
-{
-    SCOPED_TRACE("optional: Allows to assign nullopt to disengage (1)");
-    optional<int>  a( 7 );
-
-    a = nullopt;
-
-    EXPECT_TRUE( !a );
-}
-
-TEST(OptionalTest, CanCopyAssignBetweenEngagedAndDisengagedOptionals)
-{
-    SCOPED_TRACE("optional: Allows to copy-assign from/to engaged and disengaged optionals (2)");
-        optional<int> d1;
-        optional<int> d2;
-        optional<int> e1( 123 );
-        optional<int> e2( 987 );
-
-    {
-        SCOPED_TRACE("a disengaged optional assigned nullopt remains empty");
-        d1 = nullopt;
-        EXPECT_TRUE( !d1 );
-    }
-    {
-        SCOPED_TRACE("a disengaged optional assigned an engaged optional obtains its value");
-        d1 = e1;
-        EXPECT_TRUE(  d1 );
-        EXPECT_EQ( *d1 , 123 );
-    }
-    {
-        SCOPED_TRACE("an engaged optional assigned an engaged optional obtains its value");
-        e1 = e2;
-        EXPECT_TRUE(  e1 );
-        EXPECT_EQ( *e1 , 987 );
-    }
-    {
-        SCOPED_TRACE("an engaged optional assigned nullopt becomes empty");
-        e1 = nullopt;
-        EXPECT_TRUE( !e1 );
-    }
-    {
-        SCOPED_TRACE("a disengaged optional assigned a disengaged optional remains empty");
-        d1 = d2;
-        EXPECT_TRUE( !d1 );
-    }
-}
-
-TEST(MakeOptionalTest, CanMoveAssignBetweenEngagedAndDisengagedOptionals)
-{
-    SCOPED_TRACE("optional: Allows to move-assign from/to engaged and disengaged optionals (C++11, 3)");
-#if optional_CPP11_OR_GREATER
-        optional<int> d1;
-        optional<int> d2;
-        optional<int> e1( 123 );
-        optional<int> e2( 987 );
-
-    {
-        SCOPED_TRACE("a disengaged optional assigned nullopt remains empty");
-        d1 = std::move( nullopt ); //NOLINT(performance-move-const-arg)
-        EXPECT_TRUE( !d1 );
-    }
-    {
-        SCOPED_TRACE("a disengaged optional assigned an engaged optional obtains its value");
-        d1 = std::move( e1);
-        EXPECT_TRUE(  d1 );
-        EXPECT_EQ( *d1 , 123 );
-    }
-    {
-        SCOPED_TRACE("an engaged optional assigned an engaged optional obtains its value");
-        e1 = std::move( e2 );
-        EXPECT_TRUE(  e1 );
-        EXPECT_EQ( *e1 , 987 );
-    }
-    {
-        SCOPED_TRACE("an engaged optional assigned nullopt becomes empty");
-        e1 = std::move( nullopt ); //NOLINT(performance-move-const-arg)
-        EXPECT_TRUE( !e1 );
-    }
-    {
-        SCOPED_TRACE("a disengaged optional assigned a disengaged optional remains empty");
-        d1 = std::move( d2);
-        EXPECT_TRUE( !d1 );
-    }
-#else
-    EXPECT_TRUE( !!"optional: move-assignment is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanCopyAssignBetweenEngagedAndDisengagedOptionalsConverting)
-{
-    SCOPED_TRACE("optional: Allows to copy-assign from/to engaged and disengaged optionals, converting, (5)");
-        optional<int>  d1;
-        optional<char> d2;
-        optional<int>  e1( 123 );
-        optional<char> e2( '7' );
-
-    {
-        SCOPED_TRACE("a disengaged optional assigned an engaged optional obtains its value, converting");
-        d1 = e2;
-        EXPECT_TRUE(  d1 );
-        EXPECT_EQ( *d1 , '7' );
-    }
-    {
-        SCOPED_TRACE("an engaged optional assigned an engaged optional obtains its value, converting");
-        e1 = e2;
-        EXPECT_TRUE(  e1 );
-        EXPECT_EQ( *e1 , '7' );
-    }
-    {
-        SCOPED_TRACE("an engaged optional assigned a disengaged optional becomes empty, converting");
-        e1 = d2;
-        EXPECT_TRUE(  !e1 );
-    }
-    {
-        SCOPED_TRACE("a disengaged optional assigned a disengaged optional remains empty, converting");
-        d1 = d2;
-        EXPECT_TRUE( !d1 );
-    }
-}
-
-TEST(OptionalTest, CanMoveAssignBetweenEngagedAndDisengagedOptionalsConverting)
-{
-    SCOPED_TRACE("optional: Allows to move-assign from/to engaged and disengaged optionals, converting (C++11, 6)");
-#if optional_CPP11_OR_GREATER
-        optional<int>  d1;
-        optional<char> d2;
-        optional<int>  e1( 123 );
-        optional<char> e2( '7' );
-
-    {
-        SCOPED_TRACE("a disengaged optional assigned an engaged optional obtains its value, converting");
-        d1 = std::move( e2 );
-        EXPECT_TRUE(  d1 );
-        EXPECT_EQ( *d1 , '7' );
-    }
-    {
-        SCOPED_TRACE("an engaged optional assigned an engaged optional obtains its value, converting");
-        e1 = std::move( e2 ); //NOLINT(bugprone-use-after-move)
-        EXPECT_TRUE(  e1 );
-        EXPECT_EQ( *e1 , '7' );
-    }
-    {
-        SCOPED_TRACE("an engaged optional assigned a disengaged optional becomes empty, converting");
-        e1 = std::move( d2 );
-        EXPECT_TRUE(  !e1 );
-    }
-    {
-        SCOPED_TRACE("a disengaged optional assigned a disengaged optional remains empty, converting");
-        d1 = std::move( d2 ); //NOLINT(bugprone-use-after-move)
-        EXPECT_TRUE( !d1 );
-    }
-#else
-    EXPECT_TRUE( !!"optional: move-assignment is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanCopyAssignFromLiteral)
-{
-    SCOPED_TRACE("optional: Allows to copy-assign from literal value (4)");
-    optional<int> a;
-
-    a = 7;
-
-    EXPECT_EQ( *a , 7 );
-}
-
-TEST(OptionalTest, CanCopyAssignFromValue)
-{
-    SCOPED_TRACE("optional: Allows to copy-assign from value (4)");
-    const int i = 7;
-    optional<int> a;
-
-    a = i;
-
-    EXPECT_EQ( *a , i );
-}
-
-TEST(OptionalTest, CanMoveAssignFromValue)
-{
-    SCOPED_TRACE("optional: Allows to move-assign from value (C++11, 4)");
-#if optional_CPP11_OR_GREATER
-    S s( 7 );
-    optional<S> a;
-
-    a = std::move( s );
-
-    EXPECT_EQ( a->value , 7 );
-    EXPECT_EQ( a->state , move_constructed );
-    EXPECT_EQ(  s.state , moved_from       ); //NOLINT(bugprone-use-after-move)
-#else
-    EXPECT_TRUE( !!"optional: move-assign is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanCopyEmplaceFromArguments)
-{
-    SCOPED_TRACE("optional: Allows to copy-emplace content from arguments (C++11, 7)");
-#if optional_CPP11_OR_GREATER
-    using pair_t = std::pair<char, S>;
-    S s( 7 );
-    optional<pair_t> a;
-
-    a.emplace( 'a', s );
-
-    EXPECT_EQ( a->first        , 'a' );
-    EXPECT_EQ( a->second.value ,  7  );
-    EXPECT_EQ( a->second.state , copy_constructed );
-    EXPECT_NE(         s.state , moved_from       );
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanMoveEmplaceFromArguments)
-{
-    SCOPED_TRACE("optional: Allows to move-emplace content from arguments (C++11, 7)");
-#if optional_CPP11_OR_GREATER
-    using pair_t = std::pair<char, S>;
-    S s( 7 );
-    optional<pair_t> a;
-
-    a.emplace( 'a', std::move( s ) );
-
-    EXPECT_EQ( a->first        , 'a' );
-    EXPECT_EQ( a->second.value ,  7  );
-    EXPECT_EQ( a->second.state , move_constructed );
-    EXPECT_EQ(         s.state , moved_from       ); //NOLINT(bugprone-use-after-move)
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanCopyEmplaceFromInitializerListAndArguments)
-{
-    SCOPED_TRACE("optional: Allows to copy-emplace content from intializer-list and arguments (C++11, 8)");
-#if optional_CPP11_OR_GREATER
-    S s( 7 );
-    optional<InitList> a;
-
-    a.emplace( { 7, 8, 9, }, 'a', s );
-
-    EXPECT_EQ( a->vec[0]  ,  7  );
-    EXPECT_EQ( a->vec[1]  ,  8  );
-    EXPECT_EQ( a->vec[2]  ,  9  );
-    EXPECT_EQ( a->c       , 'a' );
-    EXPECT_EQ( a->s.value ,  7  );
-    EXPECT_EQ( a->s.state , copy_constructed );
-    EXPECT_NE(    s.state , moved_from       );
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanMoveEmplaceFromInitializerListAndArguments)
-{
-    SCOPED_TRACE("optional: Allows to move-emplace content from intializer-list and arguments (C++11, 8)");
-#if optional_CPP11_OR_GREATER
-    S s( 7 );
-    optional<InitList> a;
-
-    a.emplace( { 7, 8, 9, }, 'a', std::move( s ) );
-
-    EXPECT_EQ( a->vec[0]  ,  7  );
-    EXPECT_EQ( a->vec[1]  ,  8  );
-    EXPECT_EQ( a->vec[2]  ,  9  );
-    EXPECT_EQ( a->c       , 'a' );
-    EXPECT_EQ( a->s.value ,  7               );
-    EXPECT_EQ( a->s.state , move_constructed );
-    EXPECT_EQ(    s.state , moved_from       ); //NOLINT(bugprone-use-after-move)
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-// swap:
-
-class OptionalMemberSwapTest : public ::testing::Test
-{
-    public:
-        optional<int> d1;
-        optional<int> d2;
-        optional<int> e1{ 42 };
-        optional<int> e2{ 7 };
-};
-
-TEST_F(OptionalMemberSwapTest, CanSwapDisengagedWithDisengaged)
-    {
-        SCOPED_TRACE("swap disengaged with disengaged optional");
-        d1.swap( d2 );
-        EXPECT_TRUE( !d1 );
-    }
-
-TEST_F(OptionalMemberSwapTest, CanSwapEngagedWithDisengaged)
-    {
-        SCOPED_TRACE("swap engaged with engaged optional");
-        e1.swap( e2 );
-        EXPECT_TRUE(  e1  );
-        EXPECT_TRUE(  e2 );
-        EXPECT_EQ( *e1 , 7 );
-        EXPECT_EQ( *e2 , 42 );
-    }
-TEST_F(OptionalMemberSwapTest, CanSwapDisengagedWithEngaged)
-    {
-        SCOPED_TRACE("swap disengaged with engaged optional");
-        d1.swap( e1 );
-        EXPECT_TRUE(  d1 );
-        EXPECT_TRUE( !e1 );
-        EXPECT_EQ( *d1 , 42 );
-    }
-TEST_F(OptionalMemberSwapTest, CanSwapEngagedWithEngaged)
-    {
-        SCOPED_TRACE("swap engaged with disengaged optional");
-        e1.swap( d1 );
-        EXPECT_TRUE(  d1 );
-        EXPECT_TRUE( !e1 );
-        EXPECT_EQ( *d1 , 42 );
-    }
-
-// observers:
-
-class OptionalImplicitValueTest : public ::testing::Test
-{
-    public:
-        optional<Implicit>        e{ Implicit( 42 ) };
-        optional<Implicit> const ce{ Implicit( 42 ) };
-};
-
-TEST_F(OptionalImplicitValueTest, CanObtainValueConst)
-    {
-        SCOPED_TRACE("operator->() yields pointer to value (const)");
-        EXPECT_EQ(  ce->x , 42 );
-    }
-TEST_F(OptionalImplicitValueTest, CanObtainValueNonConst)
-    {
-        SCOPED_TRACE("operator->() yields pointer to value (non-const)");
-        e->x = 7;
-        EXPECT_EQ(  e->x , 7 );
-    }
-
-TEST_F(OptionalImplicitValueTest, CanObtainMovedFromValueConst)
-    {
-#if optional_CPP11_OR_GREATER
-        SCOPED_TRACE("operator->() yields pointer to value (const)");
-        EXPECT_EQ(  std::move( ce )->x , 42 ); //NOLINT(performance-move-const-arg)
-#else
-        EXPECT_TRUE( !!"optional: move-semantics are not available (no C++11)" );
-#endif
-    }
-TEST_F(OptionalImplicitValueTest, CanObtainMovedFromValueNonConst)
-    {
-#if optional_CPP11_OR_GREATER
-        SCOPED_TRACE("operator->() yields pointer to value (non-const)");
-        e->x = 7;
-        EXPECT_EQ(  std::move( e )->x , 7 );
-#else
-        EXPECT_TRUE( !!"optional: move-semantics are not available (no C++11)" );
-#endif
-    }
-
-class OptionalIntValueTest : public ::testing::Test
-{
-    public:
-        optional<int> a;
-        optional<int> b{ 7 };
-        optional<int> d;
-        optional<int> const cd;
-        optional<int>        e{ 42 };
-        optional<int> const ce{ 42 };
-        optional<int> d1;
-        optional<int> d2;
-        optional<int> e1{ 42 };
-        optional<int> e2{ 7 };
-};
-
-TEST_F(OptionalIntValueTest, CanObtainValueFromDereferenceOperatorConst)
-    {
-        SCOPED_TRACE("operator*() yields value (const)");
-        EXPECT_EQ( *ce , 42 );
-    }
-TEST_F(OptionalIntValueTest, CanObtainValueFromDereferenceOperatorNonConst)
-    {
-        SCOPED_TRACE("operator*() yields value (non-const)");
-        *e = 7;
-        EXPECT_EQ( *e , 7 );
-    }
-
-TEST_F(OptionalIntValueTest, CanObtainMovedValueFromDereferenceOperatorConst)
-    {
-#if optional_CPP11_OR_GREATER
-        SCOPED_TRACE("operator*() yields value (const)");
-        EXPECT_EQ( *(std::move( ce )) , 42 ); //NOLINT(performance-move-const-arg)
-#else
-        EXPECT_TRUE( !!"optional: move-semantics are not available (no C++11)" );
-#endif
-    }
-TEST_F(OptionalIntValueTest, CanObtainMovedValueFromDereferenceOperatorNonConst)
-    {
-#if optional_CPP11_OR_GREATER
-        SCOPED_TRACE("operator*() yields value (non-const)");
-        *e = 7;
-        EXPECT_EQ( *(std::move( e )) , 7 );
-#else
-        EXPECT_TRUE( !!"optional: move-semantics are not available (no C++11)" );
-#endif
-    }
-
-TEST_F(OptionalIntValueTest, CanObtainHasValueViaOperatorBool)
-{
-    SCOPED_TRACE("optional: Allows to obtain has_value() via operator bool()");
-
-    EXPECT_FALSE( a );
-    EXPECT_TRUE(     b );
-}
-
-TEST_F(OptionalIntValueTest, CanObtainValueViaValueMethodConst)
-    {
-        SCOPED_TRACE("value() yields value (const)");
-        EXPECT_EQ( opt_value( ce ) , 42 );
-    }
-TEST_F(OptionalIntValueTest, CanObtainValueViaValueMethodNonConst)
-    {
-        SCOPED_TRACE("value() yields value (non-const)");
-        EXPECT_EQ( opt_value( e ) , 42 );
-    }
-
-TEST_F(OptionalIntValueTest, CanObtainMovedValueViaValueMethodConst)
-    {
-#if optional_CPP11_OR_GREATER
-        SCOPED_TRACE("value() yields value (const)");
-        EXPECT_EQ( opt_value( std::move( ce ) ) , 42 ); //NOLINT(performance-move-const-arg)
-#else
-        EXPECT_TRUE( !!"optional: move-semantics are not available (no C++11)" );
-#endif
-    }
-TEST_F(OptionalIntValueTest, CanObtainMovedValueViaValueMethodNonConst)
-    {
-#if optional_CPP11_OR_GREATER
-        SCOPED_TRACE("value() yields value (non-const)");
-        EXPECT_EQ( opt_value( std::move( e ) ) , 42 );
-#else
-        EXPECT_TRUE( !!"optional: move-semantics are not available (no C++11)" );
-#endif
-    }
-
-TEST_F(OptionalIntValueTest, CanObtainValueFromNonEmptyOptionalViaValueOrMethod)
-    {
-        SCOPED_TRACE("value_or( 7 ) yields value for non-empty optional");
-        EXPECT_EQ( e.value_or( 7 ) , 42 );
-    }
-TEST_F(OptionalIntValueTest, CanObtainDefaultFromEmptyOptionalViaValueOrMethod)
-    {
-        SCOPED_TRACE("value_or( 7 ) yields default for empty optional");
-        EXPECT_EQ( d.value_or( 7 ) , 7 );
-    }
-
-TEST_F(OptionalIntValueTest, CanObtainMovedFromValueForLValuesViaValueOrMethod)
-    {
-#if optional_CPP11_OR_GREATER
-        SCOPED_TRACE("for l-values");
-        EXPECT_EQ( d.value_or( 7 ) ,  7 );
-        EXPECT_EQ( e.value_or( 7 ) , 42 );
-#else
-        EXPECT_TRUE( !!"optional: move-semantics are not available (no C++11)" );
-#endif
-    }
-TEST_F(OptionalIntValueTest, CanObtainMovedFromValueForRValuesViaValueOrMethod)
-    {
-#if optional_CPP11_OR_GREATER
-        SCOPED_TRACE("for r-values");
-        EXPECT_EQ( std::move( d ).value_or( 7 ) ,  7 );
-        EXPECT_EQ( std::move( e ).value_or( 7 ) , 42 );
-#else
-        EXPECT_TRUE( !!"optional: move-semantics are not available (no C++11)" );
-#endif
-    }
-
-TEST_F(OptionalIntValueTest, ThrowsBadOptionalAccessAtDisengagedAccessForLValues)
-{
-    SCOPED_TRACE("optional: Throws bad_optional_access at disengaged access");
-#if optional_USES_STD_OPTIONAL && defined(__APPLE__)
-    EXPECT_TRUE( true );
-#else
-
-    {
-        SCOPED_TRACE("for l-values");
-        EXPECT_THROW(  d.value(), bad_optional_access );
-        EXPECT_THROW( cd.value(), bad_optional_access );
-    }
-#endif
-}
-TEST_F(OptionalIntValueTest, ThrowsBadOptionalAccessAtDisengagedAccessForRValues)
-{
-    SCOPED_TRACE("optional: Throws bad_optional_access at disengaged access");
-#if optional_USES_STD_OPTIONAL && defined(__APPLE__)
-    EXPECT_TRUE( true );
-#else
-
-# if optional_CPP11_OR_GREATER
-    {
-        SCOPED_TRACE("for r-values");
-        EXPECT_THROW( std::move(  d ).value(), bad_optional_access );
-        EXPECT_THROW( std::move( cd ).value(), bad_optional_access ); //NOLINT(performance-move-const-arg)
-    }
-# endif
-#endif
-}
-
-TEST_F(OptionalIntValueTest, ThrowsBadOptionalAccessWithNonEmptyWhatMethod)
-{
-    SCOPED_TRACE("optional: Throws bad_optional_access with non-empty what()");
-    try
-    {
-        (void) d.value();
-    }
-    catch( bad_optional_access const & e )
-    {
-        EXPECT_TRUE( ! std::string( e.what() ).empty() );
-    }
-}
-
-// modifiers:
-
-TEST(OptionalTest, CanResetContent)
-{
-    SCOPED_TRACE("optional: Allows to reset content");
-    optional<int> a = 7;
-
-    a.reset();
-
-    EXPECT_FALSE( a.has_value() );
-}
-
-//
-// optional non-member functions:
-//
-
-TEST_F(OptionalIntValueTest, CanNonMemberSwapDisengagnedWithDisengaged)
-    {
-        SCOPED_TRACE("swap disengaged with disengaged optional");
-        swap( d1, d2 );
-        EXPECT_TRUE( !d1 );
-    }
-TEST_F(OptionalIntValueTest, CanNonMemberSwapEngagnedWithEngaged)
-    {
-        SCOPED_TRACE("swap engaged with engaged optional");
-        swap( e1, e2 );
-        EXPECT_TRUE(  e1  );
-        EXPECT_TRUE(  e2 );
-        EXPECT_EQ( *e1 , 7 );
-        EXPECT_EQ( *e2 , 42 );
-    }
-TEST_F(OptionalIntValueTest, CanNonMemberSwapDisengagnedWithEngaged)
-    {
-        SCOPED_TRACE("swap disengaged with engaged optional");
-        swap( d1, e1 );
-        EXPECT_TRUE(  d1 );
-        EXPECT_TRUE( !e1 );
-        EXPECT_EQ( *d1 , 42 );
-    }
-TEST_F(OptionalIntValueTest, CanNonMemberSwapEngagnedWithDisengaged)
-    {
-        SCOPED_TRACE("swap engaged with disengaged optional");
-        swap( e1, d1 );
-        EXPECT_TRUE(  d1 );
-        EXPECT_TRUE( !e1 );
-        EXPECT_EQ( *d1 , 42 );
-    }
-
-template< typename R, typename S, typename T >
-void relop()
-{
-        optional<R> d;
-        optional<S> e1( 6 );
-        optional<T> e2( 7 );
-
-    { SCOPED_TRACE( "engaged    == engaged"    ); EXPECT_TRUE(   e1 == e1  ); }
-    { SCOPED_TRACE( "engaged    == disengaged" ); EXPECT_TRUE( !(e1 == d ) ); }
-    { SCOPED_TRACE( "disengaged == engaged"    ); EXPECT_TRUE( !(d  == e1) ); }
-
-    { SCOPED_TRACE( "engaged    != engaged"    ); EXPECT_TRUE(   e1 != e2  ); }
-    { SCOPED_TRACE( "engaged    != disengaged" ); EXPECT_TRUE(   e1 != d   ); }
-    { SCOPED_TRACE( "disengaged != engaged"    ); EXPECT_TRUE(   d  != e2  ); }
-
-    { SCOPED_TRACE( "engaged    <  engaged"    ); EXPECT_TRUE(   e1 <  e2  ); }
-    { SCOPED_TRACE( "engaged    <  disengaged" ); EXPECT_TRUE( !(e1 <  d ) ); }
-    { SCOPED_TRACE( "disengaged <  engaged"    ); EXPECT_TRUE(   d  <  e2  ); }
-
-    { SCOPED_TRACE( "engaged    <= engaged"    ); EXPECT_TRUE(   e1 <= e1  ); }
-    { SCOPED_TRACE( "engaged    <= engaged"    ); EXPECT_TRUE(   e1 <= e2  ); }
-    { SCOPED_TRACE( "engaged    <= disengaged" ); EXPECT_TRUE( !(e1 <= d ) ); }
-    { SCOPED_TRACE( "disengaged <= engaged"    ); EXPECT_TRUE(   d  <= e2  ); }
-
-    { SCOPED_TRACE( "engaged    >  engaged"    ); EXPECT_TRUE(   e2 >  e1  ); }
-    { SCOPED_TRACE( "engaged    >  disengaged" ); EXPECT_TRUE(   e2 >  d   ); }
-    { SCOPED_TRACE( "disengaged >  engaged"    ); EXPECT_TRUE( !(d  >  e1) ); }
-
-    { SCOPED_TRACE( "engaged    >= engaged"    ); EXPECT_TRUE(   e1 >= e1  ); }
-    { SCOPED_TRACE( "engaged    >= engaged"    ); EXPECT_TRUE(   e2 >= e1  ); }
-    { SCOPED_TRACE( "engaged    >= disengaged" ); EXPECT_TRUE(   e2 >= d   ); }
-    { SCOPED_TRACE( "disengaged >= engaged"    ); EXPECT_TRUE( !(d  >= e1) ); }
-
-    { SCOPED_TRACE( "disengaged == nullopt"    ); EXPECT_TRUE(  (d       == nullopt) ); }
-    { SCOPED_TRACE( "nullopt    == disengaged" ); EXPECT_TRUE(  (nullopt == d      ) ); }
-    { SCOPED_TRACE( "engaged    == nullopt"    ); EXPECT_TRUE(  (e1      != nullopt) ); }
-    { SCOPED_TRACE( "nullopt    == engaged"    ); EXPECT_TRUE(  (nullopt != e1     ) ); }
-    { SCOPED_TRACE( "disengaged == nullopt"    ); EXPECT_TRUE( !(d       <  nullopt) ); }
-    { SCOPED_TRACE( "nullopt    == disengaged" ); EXPECT_TRUE( !(nullopt <  d      ) ); }
-    { SCOPED_TRACE( "disengaged == nullopt"    ); EXPECT_TRUE(  (d       <= nullopt) ); }
-    { SCOPED_TRACE( "nullopt    == disengaged" ); EXPECT_TRUE(  (nullopt <= d      ) ); }
-    { SCOPED_TRACE( "disengaged == nullopt"    ); EXPECT_TRUE( !(d       >  nullopt) ); }
-    { SCOPED_TRACE( "nullopt    == disengaged" ); EXPECT_TRUE( !(nullopt >  d      ) ); }
-    { SCOPED_TRACE( "disengaged == nullopt"    ); EXPECT_TRUE(  (d       >= nullopt) ); }
-    { SCOPED_TRACE( "nullopt    == disengaged" ); EXPECT_TRUE(  (nullopt >= d      ) ); }
-
-    { SCOPED_TRACE( "engaged    == value"      ); EXPECT_TRUE( e1 == 6  ); }
-    { SCOPED_TRACE( "value     == engaged"     ); EXPECT_TRUE(  6 == e1 ); }
-    { SCOPED_TRACE( "engaged   != value"       ); EXPECT_TRUE( e1 != 7  ); }
-    { SCOPED_TRACE( "value     != engaged"     ); EXPECT_TRUE(  6 != e2 ); }
-    { SCOPED_TRACE( "engaged   <  value"       ); EXPECT_TRUE( e1 <  7  ); }
-    { SCOPED_TRACE( "value     <  engaged"     ); EXPECT_TRUE(  6 <  e2 ); }
-    { SCOPED_TRACE( "engaged   <= value"       ); EXPECT_TRUE( e1 <= 7  ); }
-    { SCOPED_TRACE( "value     <= engaged"     ); EXPECT_TRUE(  6 <= e2 ); }
-    { SCOPED_TRACE( "engaged   >  value"       ); EXPECT_TRUE( e2 >  6  ); }
-    { SCOPED_TRACE( "value     >  engaged"     ); EXPECT_TRUE(  7 >  e1 ); }
-    { SCOPED_TRACE( "engaged   >= value"       ); EXPECT_TRUE( e2 >= 6  ); }
-    { SCOPED_TRACE( "value     >= engaged"     ); EXPECT_TRUE(  7 >= e1 ); }
-}
-
-TEST(OptionalTest, ProvidesRelationalOperators)
-{
-    SCOPED_TRACE("optional: Provides relational operators");
-    relop<int, int, int>();
-}
-
-TEST(OptionalTest, ProvidesMixedTypeRelationalOperators)
-{
-    SCOPED_TRACE("optional: Provides mixed-type relational operators");
-    relop<char, int, long>();
-}
-
-TEST(MakeOptionalTest, CanCopyConstruct)
-{
-    SCOPED_TRACE("make_optional: Allows to copy-construct optional");
-    S s( 7 );
-
-    EXPECT_EQ( make_optional( s )->value , 7          );
-    EXPECT_NE(                   s.state , moved_from );
-}
-
-TEST(MakeOptionalTest, CanMoveConstruct)
-{
-    SCOPED_TRACE("make_optional: Allows to move-construct optional (C++11)");
-#if optional_CPP11_OR_GREATER
-    S s( 7 );
-
-    EXPECT_EQ( make_optional( std::move( s ) )->value , 7          );
-    EXPECT_EQ(                                s.state , moved_from ); //NOLINT(bugprone-use-after-move)
-#else
-    EXPECT_TRUE( !!"optional: move-construction is not available (no C++11)" );
-#endif
-}
-
-TEST(MakeOptionalTest, CanInPlaceCopyConstructFromArguments)
-{
-    SCOPED_TRACE("make_optional: Allows to in-place copy-construct optional from arguments (C++11)");
-#if optional_CPP11_OR_GREATER
-    using pair_t = std::pair<char, S>;
-
-    S s( 7);
-    auto a = make_optional<pair_t>( 'a', s );
-
-    EXPECT_EQ( a->first        , 'a' );
-    EXPECT_EQ( a->second.value ,  7  );
-#if optional_USES_STD_OPTIONAL
-    EXPECT_EQ( a->second.state , copy_constructed );
-#else
-    EXPECT_EQ( a->second.state , move_constructed );
-#endif
-    EXPECT_NE(         s.state , moved_from       );
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(MakeOptionalTest, CanInPlaceMoveConstructFromArguments)
-{
-    SCOPED_TRACE("make_optional: Allows to in-place move-construct optional from arguments (C++11)");
-#if optional_CPP11_OR_GREATER
-    using pair_t = std::pair<char, S>;
-
-    S s( 7 );
-    auto a = make_optional<pair_t>( 'a', std::move( s ) );
-
-    EXPECT_EQ( a->first        , 'a' );
-    EXPECT_EQ( a->second.value ,  7  );
-    EXPECT_EQ( a->second.state , move_constructed );
-    EXPECT_EQ(         s.state , moved_from       ); //NOLINT(bugprone-use-after-move)
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(MakeOptionalTest, CanInPlaceCopyConstructFromInitializerListAndArguments)
-{
-    SCOPED_TRACE("make_optional: Allows to in-place copy-construct optional from initializer-list and arguments (C++11)");
-#if optional_CPP11_OR_GREATER
-    S s( 7 );
-    auto a = make_optional<InitList>( { 7, 8, 9, }, 'a', s );
-
-    EXPECT_EQ( a->vec[0]  ,  7  );
-    EXPECT_EQ( a->vec[1]  ,  8  );
-    EXPECT_EQ( a->vec[2]  ,  9  );
-    EXPECT_EQ( a->c       , 'a' );
-    EXPECT_EQ( a->s.value ,  7  );
-#if optional_USES_STD_OPTIONAL
-    EXPECT_EQ( a->s.state , copy_constructed );
-#else
-    EXPECT_EQ( a->s.state , move_constructed );
-#endif
-    EXPECT_NE(    s.state , moved_from       );
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(MakeOptionalTest, CanInPlaceMoveConstructFromInitializerListAndArguments)
-{
-    SCOPED_TRACE("make_optional: Allows to in-place move-construct optional from initializer-list and arguments (C++11)");
-#if optional_CPP11_OR_GREATER
-    S s( 7 );
-    auto a = make_optional<InitList>( { 7, 8, 9, }, 'a', std::move( s ) );
-
-    EXPECT_EQ( a->vec[0]  ,  7  );
-    EXPECT_EQ( a->vec[1]  ,  8  );
-    EXPECT_EQ( a->vec[2]  ,  9  );
-    EXPECT_EQ( a->c       , 'a' );
-    EXPECT_EQ( a->s.value ,  7  );
-    EXPECT_EQ( a->s.state , move_constructed );
-    EXPECT_EQ(    s.state , moved_from       ); //NOLINT(bugprone-use-after-move)
-#else
-    EXPECT_TRUE( !!"optional: in-place construction is not available (no C++11)" );
-#endif
-}
-
-TEST(OptionalTest, CanProduceHash)
-{
-    SCOPED_TRACE("std::hash<>: Allows to obtain hash (C++11)");
-#if optional_CPP11_OR_GREATER
-    const auto a = optional<int>( 7 );
-    const auto b = optional<int>( 7 );
-
-    EXPECT_EQ( std::hash<optional<int>>{}( a ) , std::hash<optional<int>>{}( b ) );
-#else
-    EXPECT_GT( !!"std::hash<>: std::hash<, is not available (no C++11)" );
-#endif
-}
-
-
-//
-// Negative tests:
-//
-
-//
-// Tests that print information:
-//
-
-struct Struct{ Struct(){} };
-
-#if !defined(optional_FEATURE_MAX_ALIGN_HACK) || !optional_FEATURE_MAX_ALIGN_HACK
-
-#define optional_OUTPUT_ALIGNMENT_OF( type ) \
-    "alignment_of<" #type ">: " <<  \
-     alignment_of<type>::value  << "\n" <<
-
-TEST(OptionalTest, ShowAlignmentDependingOnBaseType)
-{
-    SCOPED_TRACE("alignment_of: Show alignment of various types");
-
-#if optional_CPP11_OR_GREATER
-    using std::alignment_of;
-#else
-    using ::nonstd::optional_lite::detail::alignment_of;
-#endif
-
-    std::cout <<
-        optional_OUTPUT_ALIGNMENT_OF( char )
-        optional_OUTPUT_ALIGNMENT_OF( short )
-        optional_OUTPUT_ALIGNMENT_OF( int )
-        optional_OUTPUT_ALIGNMENT_OF( long )
-        optional_OUTPUT_ALIGNMENT_OF( float )
-        optional_OUTPUT_ALIGNMENT_OF( double )
-        optional_OUTPUT_ALIGNMENT_OF( long double )
-        optional_OUTPUT_ALIGNMENT_OF( Struct )
-         "";
-}
-#undef optional_OUTPUT_ALIGNMENT_OF
-#endif
-
-#define optional_OUTPUT_SIZEOF( type ) \
-    "sizeof( optional<" #type "> ): " << \
-     sizeof( optional<   type>   )    << " (" << sizeof(type) << ")\n" <<
-
-TEST(OptionalTest, ShowSizeDependingOnBaseType)
-{
-    std::cout <<
-#if !optional_USES_STD_OPTIONAL
-        "sizeof( nonstd::optional_lite::detail::storage_t<char> ): " <<
-         sizeof( nonstd::optional_lite::detail::storage_t<char> )    << "\n" <<
-#endif
-         optional_OUTPUT_SIZEOF( char )
-         optional_OUTPUT_SIZEOF( short )
-         optional_OUTPUT_SIZEOF( int )
-         optional_OUTPUT_SIZEOF( long )
-         optional_OUTPUT_SIZEOF( float )
-         optional_OUTPUT_SIZEOF( double )
-         optional_OUTPUT_SIZEOF( long double )
-         optional_OUTPUT_SIZEOF( Struct )
-         "";
-}
-#undef optional_OUTPUT_SIZEOF
-
-// GMX modification to suppress Doxygen checking
-#endif // DOXYGEN
-
-} // namespace gmx
diff --git a/src/gromacs/compat/tests/string_view.cpp b/src/gromacs/compat/tests/string_view.cpp
deleted file mode 100644 (file)
index 5f5f1b9..0000000
+++ /dev/null
@@ -1,1469 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-// Copyright 2017-2019 Martin Moene
-//
-// https://github.com/martinmoene/string-view-lite
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-/*! \internal \file
- * \brief Tests for C++14-compatible implementation of std::string_view.
- *
- * These tests are similer to those used in commit bf5824916b6895ccab0dbc2431520ee3b6d4f27f of
- * https://github.com/martinmoene/string_view-lite.git. The code has not
- * been linted with uncrustify so that any future updates to this
- * active repo can be incorporated easily, and //NOLINT comments added
- * to suppress clang-tidy warnings. The form of those
- * changes have been made to simplify the contents, while making it
- * easy to import any bug fixes that may appear in the source
- * repository.
- *
- * \todo Remove when requiring C++17, which has a standardized version
- * of std::string_view.
- *
- * \author Paul Bauer <paul.bauer.q@gmail.com>
- * \ingroup module_compat
- */
-#include "gmxpre.h"
-
-#include "gromacs/compat/string_view.h"
-
-#include <initializer_list>
-#include <iostream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include "gromacs/utility/strconvert.h"
-
-// GMX modification to suppress Doxygen checking
-#ifndef DOXYGEN
-
-namespace gmx
-{
-
-using namespace compat;
-
-namespace {
-
-template< class T >
-T * data( std::vector<T> & v )
-{
-#if nssv_CPP11_OR_GREATER
-    return v.data();
-#else
-    return &v[0];
-#endif
-}
-
-#define nssv_STD_SV_OR( expr )  ( nssv_USES_STD_STRING_VIEW || (expr) )
-
-typedef string_view::size_type size_type;
-
-// 24.4.2.1 Construction and assignment:
-
-TEST(StringViewTest, CanDefaultConstructEmptyView)
-{
-    SCOPED_TRACE( "string_view: Allows to default construct an empty string_view" );
-
-    string_view sv;
-
-    // use parenthesis with data() to prevent lest from using sv.data() as C-string:
-
-    EXPECT_TRUE(  sv.size() == size_type( 0 ) );
-    EXPECT_TRUE( (sv.data() == nssv_nullptr)  );
-}
-
-TEST(StringViewTest, CanConstructFromPointerAndSize)
-{
-    SCOPED_TRACE( "string_view: Allows to construct from pointer and size" );
-
-    string_view sv( "hello world", 5 );
-
-    EXPECT_TRUE(   sv.size() == size_type( 5 ) );
-    EXPECT_TRUE( *(sv.data() + 0) == 'h'       );
-    EXPECT_TRUE( *(sv.data() + 4) == 'o'       );
-}
-
-TEST(StringViewTest, CanConstructFromCString)
-{
-    SCOPED_TRACE( "string_view: Allows to construct from C-string" );
-
-    string_view sv( "hello world" );
-
-    EXPECT_TRUE(   sv.size() == size_type( 11 ) );
-    EXPECT_TRUE( *(sv.data() +  0) == 'h'       );
-    EXPECT_TRUE( *(sv.data() + 10) == 'd'       );
-}
-
-TEST(StringViewTest, CanCopyConstructFromEmptyView)
-{
-    SCOPED_TRACE( "string_view: Allows to copy-construct from empty string_view" );
-
-    string_view sv1;
-
-    string_view sv2( sv1 );
-
-    // use parenthesis with data() to prevent lest from using sv.data() as C-string:
-
-    EXPECT_TRUE(  sv2.size() == size_type( 0 ) );
-    EXPECT_TRUE( (sv2.data() == nssv_nullptr)  );
-}
-
-TEST(StringViewTest, CanCopyConstructFromNonEmptyView)
-{
-    SCOPED_TRACE( "string_view: Allows to copy-construct from non-empty string_view" );
-
-    string_view sv1( "hello world", 5 );
-
-    string_view sv2( sv1 );
-
-    EXPECT_TRUE(   sv2.size()      == sv1.size()  );
-    EXPECT_TRUE(  (sv2.data()      == sv1.data()) );
-    EXPECT_TRUE( *(sv2.data() + 0) == 'h'         );
-    EXPECT_TRUE( *(sv2.data() + 4) == 'o'         );
-}
-
-// Assignment:
-
-TEST(StringViewTest, CanCopyAssingFromEmptyView)
-{
-    SCOPED_TRACE( "string_view: Allows to copy-assign from empty string_view" );
-
-    string_view sv1;
-    string_view sv2;
-
-    sv2 = sv1;
-
-    // use parenthesis with data() to prevent lest from using sv.data() as C-string:
-
-    EXPECT_TRUE(  sv2.size() == size_type( 0 ) );
-    EXPECT_TRUE( (sv2.data() == nssv_nullptr) );
-}
-
-TEST(StringViewTest, CanCopyAssingFromNonEmptyView)
-{
-    SCOPED_TRACE( "string_view: Allows to copy-assign from non-empty string_view" );
-
-    string_view sv1( "hello world", 5 );
-    string_view sv2;
-
-    sv2 = sv1;
-
-    // use parenthesis with data() to prevent lest from using sv.data() as C-string:
-
-    EXPECT_TRUE(   sv2.size()      == sv1.size()  );
-    EXPECT_TRUE(  (sv2.data()      == sv1.data()) );
-    EXPECT_TRUE( *(sv2.data() + 0) == 'h'         );
-    EXPECT_TRUE( *(sv2.data() + 4) == 'o'         );
-}
-
-// 24.4.2.2 Iterator support:
-
-TEST(StringViewTest, AllowForwardIteration)
-{
-    SCOPED_TRACE( "string_view: Allows forward iteration" );
-
-    char hello[] = "hello";
-    string_view sv( hello );
-
-    for ( string_view::iterator pos = sv.begin(); pos != sv.end(); ++pos )
-    {
-        typedef std::iterator_traits< string_view::iterator >::difference_type difference_type;
-
-        difference_type i = std::distance( sv.begin(), pos );
-        EXPECT_TRUE( *pos == *(sv.data() + i) );
-    }
-}
-
-TEST(StringViewTest, AllowConstForwardIteration)
-{
-    SCOPED_TRACE( "string_view: Allows const forward iteration" );
-
-    char hello[] = "hello";
-    string_view sv( hello );
-
-    for ( string_view::const_iterator pos = sv.begin(); pos != sv.end(); ++pos )
-    {
-        typedef std::iterator_traits< string_view::const_iterator >::difference_type difference_type;
-
-        difference_type i = std::distance( sv.cbegin(), pos );
-        EXPECT_TRUE( *pos == *(sv.data() + i) );
-    }
-}
-
-TEST(StringViewTest, AllowReverseIteration)
-{
-    SCOPED_TRACE( "string_view: Allows reverse iteration" );
-
-    char hello[] = "hello";
-    string_view sv( hello );
-
-    for ( string_view::reverse_iterator pos = sv.rbegin(); pos != sv.rend(); ++pos )
-    {
-        typedef std::iterator_traits< string_view::reverse_iterator >::difference_type difference_type;
-
-        difference_type dist = std::distance( sv.rbegin(), pos );
-        EXPECT_TRUE( *pos == *(sv.data() + sv.size() - 1 - dist) );
-    }
-}
-
-TEST(StringViewTest, AllowConstReverseIteration)
-{
-    SCOPED_TRACE( "string_view: Allows const reverse iteration" );
-
-    char hello[] = "hello";
-    string_view sv( hello );
-
-    for ( string_view::const_reverse_iterator pos = sv.crbegin(); pos != sv.crend(); ++pos )
-    {
-        typedef std::iterator_traits< string_view::const_reverse_iterator >::difference_type difference_type;
-
-        difference_type  dist = std::distance( sv.crbegin(), pos );
-        EXPECT_TRUE( *pos == *(sv.data() + sv.size() - 1 - dist) );
-    }
-}
-
-// 24.4.2.3 Capacity:
-
-TEST(StringViewTest, CanObtainSizeFromViewViaSize)
-{
-    SCOPED_TRACE( "string_view: Allows to obtain the size of the view via size()" );
-
-    char hello[] = "hello";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.size() == std::char_traits<char>::length(hello) );
-}
-
-TEST(StringViewTest, CanObtainSizeFromViewViaLength)
-{
-    SCOPED_TRACE( "string_view: Allows to obtain the size of the view via length()" );
-
-    char hello[] = "hello";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.length() == std::char_traits<char>::length(hello) );
-}
-
-TEST(StringViewTest, CanObtainMaxSizeViaMaxSize)
-{
-    SCOPED_TRACE( "string_view: Allows to obtain the maximum size a view can be via max_size()" );
-
-    // "large"
-    EXPECT_TRUE( string_view().max_size() >= (std::numeric_limits< string_view::size_type >::max)() / 10 );
-}
-
-TEST(StringViewTest, CanCheckForEmptyStringWithEmpty)
-{
-    SCOPED_TRACE( "string_view: Allows to check for an empty string_view via empty()" );
-
-    string_view sve;
-    string_view svne("hello");
-
-    EXPECT_TRUE(      sve.size() == size_type( 0 ) );
-    EXPECT_TRUE(      sve.empty() );
-    EXPECT_FALSE( svne.empty() );
-}
-
-// 24.4.2.4 Element access:
-
-TEST(StringViewTest, CanAccessElementViaArrayIndex)
-{
-    SCOPED_TRACE( "string_view: Allows to observe an element via array indexing" );
-
-    // Requires: index < sv.size()
-
-    char hello[] = "hello";
-    string_view sv( hello );
-
-    for ( size_type i = 0; i < sv.size(); ++i )
-    {
-        EXPECT_TRUE( sv[i] == hello[i] );
-    }
-}
-
-TEST(StringViewTest, CanAccessElementViaAt)
-{
-    SCOPED_TRACE( "string_view: Allows to observe an element via at()" );
-
-    char hello[] = "hello";
-    string_view sv( hello );
-
-    for ( size_type i = 0; i < sv.size(); ++i )
-    {
-        EXPECT_TRUE( sv.at(i) == hello[i] );
-    }
-}
-
-TEST(StringViewTest, ThrowsOnOutOfBoundsAccess)
-{
-    SCOPED_TRACE( "string_view: Throws at observing an element via at() with an index of size() or larger" );
-
-    string_view sv("hello");
-
-    EXPECT_ANY_THROW(   sv.at( sv.size()     ) );
-    EXPECT_NO_THROW( sv.at( sv.size() - 1 ) );
-}
-
-TEST(StringViewTest, CanAccessAllElementsViaData)
-{
-    SCOPED_TRACE( "string_view: Allows to observe elements via data()" );
-
-    char hello[] = "hello";
-    string_view sv( hello );
-
-    EXPECT_TRUE( *sv.data() == *sv.begin() );
-
-    for ( size_type i = 0; i < sv.size(); ++i )
-    {
-        EXPECT_TRUE( sv.data()[i] == hello[i] );
-    }
-}
-
-TEST(StringViewTest, DataFromEmptyStringIsNullptr)
-{
-    SCOPED_TRACE( "string_view: Yields nullptr (or NULL) with data() for an empty string_view" );
-
-    string_view sv;
-
-    // use parenthesis with data() to prevent lest from using sv.data() as C-string:
-
-    EXPECT_TRUE( (sv.data() == nssv_nullptr) );
-}
-
-// 24.4.2.5 Modifiers:
-
-TEST(StringViewTest, CanRemovePrefix)
-{
-    SCOPED_TRACE( "string_view: Allows to remove a prefix of n elements" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    sv.remove_prefix( 6 );
-
-    EXPECT_TRUE( sv.size() == size_type( 5 ) );
-    EXPECT_TRUE( std::equal( sv.begin(), sv.end(), hello + 6) );
-}
-
-TEST(StringViewTest, CanRemoveSuffix)
-{
-    SCOPED_TRACE( "string_view: Allows to remove a suffix of n elements" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    sv.remove_suffix( 6 );
-
-    EXPECT_TRUE( sv.size() == size_type( 5 ) );
-    EXPECT_TRUE( std::equal( sv.begin(), sv.end(), hello ) );
-}
-
-TEST(StringViewTest, CanSwapWithOtherView)
-{
-    SCOPED_TRACE( "string_view: Allows to swap with other string_view" );
-
-    char hello[]  = "hello";
-    char world[]  = "world";
-    string_view sv1( hello );
-    string_view sv2( world );
-
-    sv1.swap( sv2 );
-
-    EXPECT_TRUE( std::equal( sv1.begin(), sv1.end(), world )  );
-    EXPECT_TRUE( std::equal( sv2.begin(), sv2.end(), hello )  );
-}
-
-// 24.4.2.6 String operations:
-
-TEST(StringViewTest, CanCopySubstringWithCopy)
-{
-    SCOPED_TRACE( "string_view: Allows to copy a substring of length n, starting at position pos (default: 0) via copy()" );
-
-    char hello[]  = "hello world";
-    string_view sv( hello );
-
-    {
-        std::vector<string_view::value_type> vec( sv.size() );
-
-        sv.copy( data(vec), vec.size() );
-
-        EXPECT_TRUE( std::equal( vec.begin(), vec.end(), hello )  );
-    }{
-        std::size_t offset = 3U; std::size_t length = 4U;
-        std::vector<string_view::value_type> vec( length );
-
-        sv.copy( data(vec), length, offset );
-
-        EXPECT_TRUE( std::equal( vec.begin(), vec.end(), hello + offset )  );
-    }
-}
-
-TEST(StringViewTest, ThrowsOnOutOfBoundsCopy)
-{
-    SCOPED_TRACE( "string_view: Throws if requested position of copy() exceeds string_view's size()" );
-
-    string_view sv("hello world");
-    std::vector<string_view::value_type> vec( sv.size() );
-
-    EXPECT_ANY_THROW(   sv.copy( data(vec), sv.size() - 4, sv.size() + 1 ) );
-    EXPECT_NO_THROW( sv.copy( data(vec), sv.size() - 4, sv.size() + 0 ) );
-}
-
-TEST(StringViewTest, CanObtainSubstringWithSubstr)
-{
-    SCOPED_TRACE( "string_view: Allow to obtain a sub string, starting at position pos (default: 0) and of length n (default full), via substr()" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    {
-#if nssv_USES_STD_STRING_VIEW && defined(__GNUC__)
-        EXPECT_TRUE( !!"std::string_view::substr(), with default pos, count is not available with GNU C" );
-#else
-        EXPECT_TRUE( std::equal( sv.begin(), sv.end(), sv.substr().begin() ) );
-#endif
-    }{
-        string_view subv = sv.substr( 6 );
-
-        EXPECT_TRUE( std::equal( subv.begin(), subv.end(), hello + 6 ) );
-    }{
-        string_view subv = sv.substr( 3, 4 );
-
-        EXPECT_TRUE( std::equal( subv.begin(), subv.end(), hello + 3 ) );
-    }
-}
-
-TEST(StringViewTest, ThrowsOnOutOfBoundsSubstr)
-{
-    SCOPED_TRACE( "string_view: Throws if requested position of substr() exceeds string_view's size()" );
-
-    string_view sv("hello world");
-
-    EXPECT_ANY_THROW(   sv.substr( sv.size() + 1 ) );
-    EXPECT_NO_THROW( sv.substr( sv.size() + 0 ) );
-}
-
-TEST(StringViewTest, CanLexicallyCompareViewWithCompare)
-{
-    SCOPED_TRACE( "string_view: Allows to lexically compare to another string_view via compare(), (1)" );
-
-    char hello[] = "hello";
-    char world[] = "world";
-
-    EXPECT_TRUE( string_view( hello ).compare( string_view( hello ) ) == 0 );
-    EXPECT_TRUE( string_view( hello ).compare( string_view( world ) ) <  0 );
-    EXPECT_TRUE( string_view( world ).compare( string_view( hello ) ) >  0 );
-
-    char hello_sp[] = "hello ";
-
-    EXPECT_TRUE( string_view( hello    ).compare( string_view( hello_sp ) ) < 0 );
-    EXPECT_TRUE( string_view( hello_sp ).compare( string_view( hello    ) ) > 0 );
-}
-
-TEST(StringViewTest, CanCompareEmptyViewsWIthCompare)
-{
-    SCOPED_TRACE( "string_view: Allows to compare empty string_views as equal via compare(), (1)" );
-
-    EXPECT_TRUE( string_view().compare( string_view() ) == 0 );
-}
-
-TEST(StringViewTest, CanCompareSubStringWithViewViaCompare)
-{
-    SCOPED_TRACE( "string_view: Allows to compare a sub string to another string_view via compare(), (2)" );
-
-    string_view sv1("hello world");
-    string_view sv2("world");
-
-    EXPECT_TRUE( sv1.compare ( 0, sv1.length(), sv1 ) == 0 );
-    EXPECT_TRUE( sv1.compare ( 6, 5, sv2 ) == 0 );
-    EXPECT_TRUE( sv1.compare ( 0, 5, sv2 ) <  0 );
-    EXPECT_TRUE( sv2.compare ( 0, 5, sv1 ) >  0 );
-}
-
-TEST(StringViewTest, CanCompareSubStringWithSubStringViewViaCompare)
-{
-    SCOPED_TRACE( "string_view: Allows to compare a sub string to another string_view sub string via compare(), (3)" );
-
-    string_view sv1("hello world");
-
-    EXPECT_TRUE( sv1.compare ( 0, sv1.length(), sv1 ) == 0 );
-    EXPECT_TRUE( sv1.compare ( 6, 5, sv1, 6, 5 ) ==  0 );
-    EXPECT_TRUE( sv1.compare ( 0, 5, sv1, 6, 5 )  <  0 );
-    EXPECT_TRUE( sv1.compare ( 6, 5, sv1, 0, 5 )  >  0 );
-}
-
-TEST(StringViewTest, CanCompareToCStringViaCompare)
-{
-    SCOPED_TRACE( "string_view: Allows to compare to a C-string via compare(), (4)" );
-
-    char hello[] = "hello";
-    char world[] = "world";
-
-    EXPECT_TRUE( string_view( hello ).compare( hello ) == 0 );
-    EXPECT_TRUE( string_view( hello ).compare( world ) <  0 );
-    EXPECT_TRUE( string_view( world ).compare( hello ) >  0 );
-}
-
-TEST(StringViewTest, CanCompareSubStringToCStringViaCompare)
-{
-    SCOPED_TRACE( "string_view: Allows to compare a sub string to a C-string via compare(), (5)" );
-
-    char hello[] = "hello world";
-    char world[] = "world";
-
-    EXPECT_TRUE( string_view( hello ).compare( 6, 5, world ) == 0 );
-    EXPECT_TRUE( string_view( hello ).compare( world ) <  0 );
-    EXPECT_TRUE( string_view( world ).compare( hello ) >  0 );
-}
-
-TEST(StringViewTest, CanCompareSubStringToCStringPrefixViaCompare)
-{
-    SCOPED_TRACE( "string_view: Allows to compare a sub string to a C-string prefix via compare(), (6)" );
-
-    char hello[] = "hello world";
-    char world[] = "world";
-
-    EXPECT_TRUE( string_view( hello ).compare( 6, 5, world, 5 ) == 0 );
-    EXPECT_TRUE( string_view( hello ).compare( 0, 5, world, 5 ) <  0 );
-    EXPECT_TRUE( string_view( hello ).compare( 6, 5, hello, 5 ) >  0 );
-}
-
-// 24.4.2.7 Searching:
-
-#if nssv_HAVE_STARTS_WITH
-
-TEST(StringViewTest, CanCheckForPrefixViewViaStartsWith)
-{
-    SCOPED_TRACE( "string_view: Allows to check for a prefix string_view via starts_with(), (1)" );
-
-    char hello[] = "hello world";
-
-    EXPECT_TRUE(     string_view( hello ).starts_with( string_view( hello ) ) );
-    EXPECT_TRUE(     string_view( hello ).starts_with( string_view("hello") ) );
-    EXPECT_FALSE( string_view( hello ).starts_with( string_view("world") ) );
-}
-
-TEST(StringViewTest, CanCheckForPrefixCharacterViaStartsWith)
-{
-    SCOPED_TRACE( "string_view: Allows to check for a prefix character via starts_with(), (2)" );
-
-    char hello[] = "hello world";
-
-    EXPECT_TRUE(     string_view( hello ).starts_with( 'h' ) );
-    EXPECT_FALSE( string_view( hello ).starts_with( 'e' ) );
-}
-
-TEST(StringViewTest, CanCheckForPrefixCStringViaStartsWith)
-{
-    SCOPED_TRACE( "string_view: Allows to check for a prefix C-string via starts_with(), (3)" );
-
-    char hello[] = "hello world";
-
-    EXPECT_TRUE(     string_view( hello ).starts_with( hello ) );
-    EXPECT_TRUE(     string_view( hello ).starts_with("hello") );
-    EXPECT_FALSE( string_view( hello ).starts_with("world") );
-}
-
-#endif // nssv_HAVE_STARTS_WITH
-
-#if nssv_HAVE_ENDS_WITH
-
-TEST(StringViewTest, CanCheckForSuffixViewViaEndsWith)
-{
-    SCOPED_TRACE( "string_view: Allows to check for a suffix string_view via ends_with(), (1)" );
-
-    char hello[] = "hello world";
-
-    EXPECT_TRUE(     string_view( hello ).ends_with( string_view( hello ) ) );
-    EXPECT_TRUE(     string_view( hello ).ends_with( string_view("world") ) );
-    EXPECT_FALSE( string_view( hello ).ends_with( string_view("hello") ) );
-}
-
-TEST(StringViewTest, CanCheckForSuffixCharacterViaEndsWith)
-{
-    SCOPED_TRACE( "string_view: Allows to check for a suffix character via ends_with(), (2)" );
-
-    char hello[] = "hello world";
-
-    EXPECT_TRUE(     string_view( hello ).ends_with( 'd' ) );
-    EXPECT_FALSE( string_view( hello ).ends_with( 'l' ) );
-}
-
-TEST(StringViewTest, CanCheckForSuffixCStringViaEndsWith)
-{
-    SCOPED_TRACE( "string_view: Allows to check for a suffix C-string via ends_with(), (3)" );
-
-    char hello[] = "hello world";
-
-    EXPECT_TRUE(     string_view( hello ).ends_with( hello ) );
-    EXPECT_TRUE(     string_view( hello ).ends_with("world") );
-    EXPECT_FALSE( string_view( hello ).ends_with("hello") );
-}
-
-#endif // nssv_HAVE_ENDS_WITH
-
-TEST(StringViewTest, CanSearchForViewSubstrViaFind)
-{
-    SCOPED_TRACE( "string_view: Allows to search for a string_view substring, starting at position pos (default: 0) via find(), (1)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find( sv    ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.find( sv, 1 ) == string_view::npos );
-    EXPECT_TRUE( sv.find( string_view("world" )    ) == size_type( 6 ) );
-    EXPECT_TRUE( sv.find( string_view("world" ), 6 ) == size_type( 6 ) );
-    EXPECT_TRUE( sv.find( string_view("world" ), 7 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanSearchForCharacterViaFind)
-{
-    SCOPED_TRACE( "string_view: Allows to search for a character, starting at position pos (default: 0) via find(), (2)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find('h'    ) == size_type( 0 )    );
-    EXPECT_TRUE( sv.find('h', 1 ) == string_view::npos );
-    EXPECT_TRUE( sv.find('w'    ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find('w', 6 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find('w', 7 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanSearchForCStringSubstringViaFind)
-{
-    SCOPED_TRACE( "string_view: Allows to search for a C-string substring, starting at position pos and of length n via find(), (3)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find( hello , 0, sv.size() ) == size_type( 0 )    );
-    EXPECT_TRUE( sv.find( hello , 1, sv.size() ) == string_view::npos );
-    EXPECT_TRUE( sv.find("world", 0, 5 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find("world", 6, 5 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find("world", 7, 4 ) == string_view::npos );
-    EXPECT_TRUE( sv.find("world", 3, 0 ) == size_type( 3 )    );
-}
-
-TEST(StringViewTest, CanSearchForCStringSubstringViaFindWithDefaultPos)
-{
-    SCOPED_TRACE( "string_view: Allows to search for a C-string substring, starting at position pos (default: 0) via find(), (4)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find( hello     ) == size_type( 0 )    );
-    EXPECT_TRUE( sv.find( hello , 1 ) == string_view::npos );
-    EXPECT_TRUE( sv.find("world"    ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find("world", 6 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find("world", 7 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanBackwardsSearchForViewSubstrViaFind)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for a string_view substring, starting at position pos (default: npos) via rfind(), (1)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.rfind( sv    ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.rfind( sv, 3 ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.rfind( string_view(        )    ) == size_type(11 ) );
-    EXPECT_TRUE( sv.rfind( string_view("world" )    ) == size_type( 6 ) );
-    EXPECT_TRUE( sv.rfind( string_view("world" ), 6 ) == size_type( 6 ) );
-    EXPECT_TRUE( sv.rfind( string_view("world" ), 5 ) == string_view::npos );
-    EXPECT_TRUE( sv.rfind( string_view("hello world, a longer text" ) ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanBackwardsSearchForCharacterViaFind)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for a character, starting at position pos (default: npos) via rfind(), (2)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.rfind('h'    ) == size_type( 0 )    );
-    EXPECT_TRUE( sv.rfind('e'    ) == size_type( 1 )    );
-    EXPECT_TRUE( sv.rfind('e', 0 ) == string_view::npos );
-    EXPECT_TRUE( sv.rfind('w'    ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.rfind('w', 6 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.rfind('w', 5 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanBackwardsSearchForCStringSubstringViaFind)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for a C-string substring, starting at position pos and of length n via rfind(), (3)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.rfind( hello         ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.rfind( hello ,  0, 5 ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.rfind( hello ,  1, 5 ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.rfind("world", 10, 5 ) == size_type( 6 ) );
-    EXPECT_TRUE( sv.rfind("world",  6, 5 ) == size_type( 6 ) );
-    EXPECT_TRUE( sv.rfind("world",  5, 5 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanBackwardsSearchForCStringSubstringViaFindWithDefaultPos)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for a C-string substring, starting at position pos (default: 0) via rfind(), (4)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.rfind( hello     ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.rfind( hello , 3 ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.rfind("world"    ) == size_type( 6 ) );
-    EXPECT_TRUE( sv.rfind("world", 6 ) == size_type( 6 ) );
-    EXPECT_TRUE( sv.rfind("world", 5 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanSearchForFirstOccurenceOfAnyCharacterInView)
-{
-    SCOPED_TRACE( "string_view: Allows to search for the first occurrence of any of the characters specified in a string view, starting at position pos (default: 0) via find_first_of(), (1)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_first_of( sv    ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.find_first_of( sv, 3 ) == size_type( 3 ) );
-    EXPECT_TRUE( sv.find_first_of( string_view("xwo" )    ) == size_type( 4 ) );
-    EXPECT_TRUE( sv.find_first_of( string_view("wdx" ), 6 ) == size_type( 6 ) );
-    EXPECT_TRUE( sv.find_first_of( string_view("wxy" ), 7 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanSearchForFirstOccurenceOfCharacter)
-{
-    SCOPED_TRACE( "string_view: Allows to search for a character, starting at position pos (default: 0) via find_first_of(), (2)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_first_of('h'    ) == size_type( 0 )    );
-    EXPECT_TRUE( sv.find_first_of('h', 1 ) == string_view::npos );
-    EXPECT_TRUE( sv.find_first_of('w'    ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_first_of('w', 6 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_first_of('w', 7 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanSearchForFirstOccurenceOfCharactersInCStringInLenght)
-{
-    SCOPED_TRACE( "string_view: Allows to search for the first occurrence of any of the characters specified in a C-string, starting at position pos and of length n via find_first_of(), (3)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_first_of( hello , 0, sv.size() ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.find_first_of( hello , 1, sv.size() ) == size_type( 1 ) );
-    EXPECT_TRUE( sv.find_first_of(  "xwy", 0, 3 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_first_of(  "xwy", 6, 3 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_first_of(  "xwy", 7, 3 ) == string_view::npos );
-    EXPECT_TRUE( sv.find_first_of(  "xyw", 0, 2 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanSearchForFirstOccurenceOfCharactersInCString)
-{
-    SCOPED_TRACE( "string_view: Allows to search for the first occurrence of any of the characters specified in a C-string, starting at position pos via find_first_of(), (4)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_first_of( hello , 0 ) == size_type( 0 )    );
-    EXPECT_TRUE( sv.find_first_of( hello , 1 ) == size_type( 1 )    );
-    EXPECT_TRUE( sv.find_first_of(  "xwy", 0 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_first_of(  "xwy", 6 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_first_of(  "xwy", 7 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanBackwardsSearchForLastOccurenceOfAnyCharacterInView)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for the last occurrence of any of the characters specified in a string view, starting at position pos (default: npos) via find_last_of(), (1)" );
-
-    char hello[] = "hello world";
-    char empty[] = "";
-    string_view sv( hello );
-    string_view sve( empty );
-
-    EXPECT_TRUE( sv.find_last_of( sv    ) == size_type( 10 ) );
-    EXPECT_TRUE( sv.find_last_of( sv, 3 ) == size_type(  3 ) );
-    EXPECT_TRUE( sv.find_last_of( string_view("xwo" )    ) == size_type( 7 ) );
-    EXPECT_TRUE( sv.find_last_of( string_view("wdx" ), 6 ) == size_type( 6 ) );
-    EXPECT_TRUE( sv.find_last_of( string_view("wxy" ), 7 ) == size_type( 6 ) );
-
-    EXPECT_TRUE( sve.find_last_of( string_view("x") ) == string_view::npos );    // issue 20 (endless loop)
-}
-
-TEST(StringViewTest, CanBackwardsSearchForLastOccurenceOfCharacter)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for a character, starting at position pos (default: 0) via find_last_of(), (2)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_last_of('h'    ) == size_type( 0 )    );
-    EXPECT_TRUE( sv.find_last_of('l', 1 ) == string_view::npos );
-    EXPECT_TRUE( sv.find_last_of('w'    ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_last_of('w', 6 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_last_of('w', 5 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanBackwardsSearchForLastOccurenceOfCharactersInCStringInLenght)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for the first occurrence of any of the characters specified in a C-string, starting at position pos and of length n via find_last_of(), (3)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_last_of( hello , 0, sv.size() ) == size_type( 0 ) );
-    EXPECT_TRUE( sv.find_last_of( hello , 1, sv.size() ) == size_type( 1 ) );
-    EXPECT_TRUE( sv.find_last_of("xwy", 10, 3 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_last_of("xwy",  6, 3 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_last_of("xwy",  5, 3 ) == string_view::npos );
-    EXPECT_TRUE( sv.find_last_of("xyw", 10, 2 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanBackwardsSearchForLastOccurenceOfCharactersInCString)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for the first occurrence of any of the characters specified in a C-string, starting at position pos via find_last_of(), (4)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_last_of( hello ,  0 ) == size_type( 0 )    );
-    EXPECT_TRUE( sv.find_last_of( hello ,  1 ) == size_type( 1 )    );
-    EXPECT_TRUE( sv.find_last_of(  "xwy", 10 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_last_of(  "xwy",  6 ) == size_type( 6 )    );
-    EXPECT_TRUE( sv.find_last_of(  "xwy",  5 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanSearchForFirstNotFoundCharacter)
-{
-    SCOPED_TRACE( "string_view: Allows to search for the first character not specified in a string view, starting at position pos (default: 0) via find_first_not_of(), (1)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_first_not_of( sv    ) == string_view::npos );
-    EXPECT_TRUE( sv.find_first_not_of( sv, 3 ) == string_view::npos );
-    EXPECT_TRUE( sv.find_first_not_of( string_view("helo "   )    ) == size_type(  6 ) );
-    EXPECT_TRUE( sv.find_first_not_of( string_view("helo "   ), 6 ) == size_type(  6 ) );
-    EXPECT_TRUE( sv.find_first_not_of( string_view("helo "   ), 7 ) == size_type(  8 ) );
-    EXPECT_TRUE( sv.find_first_not_of( string_view("helo wr" )    ) == size_type( 10 ) );
-}
-
-TEST(StringViewTest, CanSearchForFirstNonMatchingCharacter)
-{
-    SCOPED_TRACE( "string_view: Allows to search for the first character not equal to the specified character, starting at position pos (default: 0) via find_first_not_of(), (2)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_first_not_of('h'     ) == size_type( 1 )    );
-    EXPECT_TRUE( sv.find_first_not_of('h',  1 ) == size_type( 1 )    );
-    EXPECT_TRUE( sv.find_first_not_of('w'     ) == size_type( 0 )    );
-    EXPECT_TRUE( sv.find_first_not_of('w',  6 ) == size_type( 7 )    );
-    EXPECT_TRUE( sv.find_first_not_of('d', 10 ) == string_view::npos );
-}
-
-TEST(StringViewTest, CanSearchForFirstNonEqualToAnyCharacterInCStringInLength)
-{
-    SCOPED_TRACE( "string_view: Allows to search for  the first character not equal to any of the characters specified in a C-string, starting at position pos and of length n via find_first_not_of(), (3)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_first_not_of( hello, 0, sv.size() ) == string_view::npos );
-    EXPECT_TRUE( sv.find_first_not_of( hello, 3, sv.size() ) == string_view::npos );
-    EXPECT_TRUE( sv.find_first_not_of( "helo "  , 0, 5     ) == size_type(  6 ) );
-    EXPECT_TRUE( sv.find_first_not_of( "helo "  , 6, 5     ) == size_type(  6 ) );
-    EXPECT_TRUE( sv.find_first_not_of( "helo "  , 7, 5     ) == size_type(  8 ) );
-    EXPECT_TRUE( sv.find_first_not_of( "helo wr", 0, 7     ) == size_type( 10 ) );
-    EXPECT_TRUE( sv.find_first_not_of( "he"     , 0, 1     ) == size_type(  1 ) );
-}
-
-TEST(StringViewTest, CanSearchForFirstNonEqualToAnyCharacterInCString)
-{
-    SCOPED_TRACE( "string_view: Allows to search for  the first character not equal to any of the characters specified in a C-string, starting at position pos via find_first_not_of(), (4)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_first_not_of( hello    , 0 ) == string_view::npos );
-    EXPECT_TRUE( sv.find_first_not_of( hello    , 3 ) == string_view::npos );
-    EXPECT_TRUE( sv.find_first_not_of( "helo "  , 0 ) == size_type(  6 ) );
-    EXPECT_TRUE( sv.find_first_not_of( "helo "  , 6 ) == size_type(  6 ) );
-    EXPECT_TRUE( sv.find_first_not_of( "helo "  , 7 ) == size_type(  8 ) );
-    EXPECT_TRUE( sv.find_first_not_of( "helo wr", 0 ) == size_type( 10 ) );
-}
-
-TEST(StringViewTest, CanBackwardsSearchForForstNonFoundCharacterInView)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for the first character not specified in a string view, starting at position pos (default: npos) via find_last_not_of(), (1)" );
-
-    char hello[] = "hello world";
-    char empty[] = "";
-    string_view sv( hello );
-    string_view sve( empty );
-
-    EXPECT_TRUE( sv.find_last_not_of( sv    ) == string_view::npos );
-    EXPECT_TRUE( sv.find_last_not_of( sv, 3 ) == string_view::npos );
-    EXPECT_TRUE( sv.find_last_not_of( string_view("world " )    ) == size_type(  1 ) );
-    EXPECT_TRUE( sv.find_last_not_of( string_view("heo "   ), 4 ) == size_type(  3 ) );
-    EXPECT_TRUE( sv.find_last_not_of( string_view("heo "   ), 3 ) == size_type(  3 ) );
-    EXPECT_TRUE( sv.find_last_not_of( string_view("heo "   ), 2 ) == size_type(  2 ) );
-    EXPECT_TRUE( sv.find_last_not_of( string_view("x"      )    ) == size_type( 10 ) );
-
-    EXPECT_TRUE( sve.find_last_not_of( string_view("x") ) == string_view::npos );    // issue 20 (endless loop)
-}
-
-TEST(StringViewTest, CanBackwardsSearchForFirstNonMatchingCharacter)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for the first character not equal to the specified character, starting at position pos (default: npos) via find_last_not_of(), (2)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_last_not_of('d'     ) == size_type( 9 ) );
-    EXPECT_TRUE( sv.find_last_not_of('d', 10 ) == size_type( 9 ) );
-    EXPECT_TRUE( sv.find_last_not_of('d',  9 ) == size_type( 9 ) );
-    EXPECT_TRUE( sv.find_last_not_of('d',  8 ) == size_type( 8 ) );
-    EXPECT_TRUE( sv.find_last_not_of('d',  0 ) == size_type( 0 ) );
-}
-
-TEST(StringViewTest, CanBackwardsSearchForFirstNonEqualToAnyCharacterInCStringInLength)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for  the first character not equal to any of the characters specified in a C-string, starting at position pos and of length n via find_last_not_of(), (3)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_last_not_of( hello, 0, sv.size() ) == string_view::npos );
-    EXPECT_TRUE( sv.find_last_not_of( hello, 3, sv.size() ) == string_view::npos );
-    EXPECT_TRUE( sv.find_last_not_of( "world ", 10, 6 ) == size_type(  1 ) );
-    EXPECT_TRUE( sv.find_last_not_of( "heo "  ,  4, 4 ) == size_type(  3 ) );
-    EXPECT_TRUE( sv.find_last_not_of( "heo "  ,  3, 4 ) == size_type(  3 ) );
-    EXPECT_TRUE( sv.find_last_not_of( "heo "  ,  2, 4 ) == size_type(  2 ) );
-    EXPECT_TRUE( sv.find_last_not_of( "x"             ) == size_type( 10 ) );
-}
-
-TEST(StringViewTest, CanBackwardsSearchForFirstNonEqualToAnyCharacterInCString)
-{
-    SCOPED_TRACE( "string_view: Allows to search backwards for  the first character not equal to any of the characters specified in a C-string, starting at position pos via find_last_not_of(), (4)" );
-
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.find_last_not_of( hello    , 0 ) == string_view::npos );
-    EXPECT_TRUE( sv.find_last_not_of( hello    , 3 ) == string_view::npos );
-    EXPECT_TRUE( sv.find_last_not_of( "world ", 10 ) == size_type(  1 ) );
-    EXPECT_TRUE( sv.find_last_not_of( "heo "  ,  4 ) == size_type(  3 ) );
-    EXPECT_TRUE( sv.find_last_not_of( "heo "  ,  3 ) == size_type(  3 ) );
-    EXPECT_TRUE( sv.find_last_not_of( "heo "  ,  2 ) == size_type(  2 ) );
-    EXPECT_TRUE( sv.find_last_not_of( "x"          ) == size_type( 10 ) );
-}
-
-TEST(StringViewTest, CanCreateViewWithLiteralSV)
-{
-    SCOPED_TRACE( "string_view: Allows to create a string_view, wstring_view, u16string_view, u32string_view via literal \"sv\"" );
-
-#if nssv_CONFIG_STD_SV_OPERATOR
-#if nssv_STD_SV_OR( nssv_HAVE_STD_DEFINED_LITERALS )
-    using namespace nonstd::literals::string_view_literals;
-
-    string_view sv1 =  "abc"sv;
-    wstring_view sv2 = L"abc"sv;
-
-    EXPECT_TRUE( sv1.size() == size_type( 3 ) );
-    EXPECT_TRUE( sv2.size() == size_type( 3 ) );
-
-#if nssv_HAVE_WCHAR16_T
-    u16string_view sv3 = u"abc"sv;
-    EXPECT_TRUE( sv3.size() == size_type( 3 ) );
-#endif
-#if nssv_HAVE_WCHAR32_T
-    u32string_view sv4 = U"abc"sv;
-    EXPECT_TRUE( sv4.size() == size_type( 3 ) );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator 'sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator 'sv' for string_view not available (nssv_CONFIG_STD_SV_OPERATOR=0)." );
-#endif
-}
-
-TEST(StringViewTest, CanCreateViewWithLiteralSVInLiteralsStringViewLiteralsNamespace)
-{
-    SCOPED_TRACE( "string_view: Allows to create a string_view via literal \"sv\", using namespace gmx::compat::literals::string_view_literals" );
-
-#if nssv_CONFIG_STD_SV_OPERATOR
-#if nssv_STD_SV_OR( nssv_HAVE_STD_DEFINED_LITERALS )
-    using namespace gmx::compat::literals::string_view_literals;
-
-    string_view sv1 = "abc\0\0def";
-    string_view sv2 = "abc\0\0def"sv;
-
-    EXPECT_TRUE( sv1.size() == size_type( 3 ) );
-    EXPECT_TRUE( sv2.size() == size_type( 8 ) );
-#else
-    EXPECT_TRUE( !!"Literal operator 'sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator 'sv' for string_view not available (nssv_CONFIG_STD_SV_OPERATOR=0)." );
-#endif
-}
-
-TEST(StringViewTest, CanCreateViewWithLiteralSVInStringViewLiteralsNamespace)
-{
-    SCOPED_TRACE( "string_view: Allows to create a string_view via literal \"sv\", using namespace gmx::compat::string_view_literals" );
-
-#if nssv_CONFIG_STD_SV_OPERATOR
-#if nssv_STD_SV_OR( nssv_HAVE_STD_DEFINED_LITERALS )
-#if nssv_STD_SV_OR( nssv_HAVE_INLINE_NAMESPACE )
-    using namespace gmx::compat::string_view_literals;
-
-    string_view sv1 = "abc\0\0def";
-    string_view sv2 = "abc\0\0def"sv;
-
-    EXPECT_TRUE( sv1.size() == size_type( 3 ) );
-    EXPECT_TRUE( sv2.size() == size_type( 8 ) );
-#else
-    EXPECT_TRUE( !!"Inline namespaces for literal operator 'sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator 'sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator 'sv' for string_view not available (nssv_CONFIG_STD_SV_OPERATOR=0)." );
-#endif
-}
-
-TEST(StringViewTest, CanCreateViewWithLiteralSVInLiteralsNamespace)
-{
-    SCOPED_TRACE( "string_view: Allows to create a string_view via literal \"sv\", using namespace gmx::compat::literals" );
-
-#if nssv_CONFIG_STD_SV_OPERATOR
-#if nssv_STD_SV_OR( nssv_HAVE_STD_DEFINED_LITERALS )
-#if nssv_STD_SV_OR( nssv_HAVE_INLINE_NAMESPACE )
-    using namespace gmx::compat::literals;
-
-    string_view sv1 = "abc\0\0def";
-    string_view sv2 = "abc\0\0def"sv;
-
-    EXPECT_TRUE( sv1.size() == size_type( 3 ) );
-    EXPECT_TRUE( sv2.size() == size_type( 8 ) );
-#else
-    EXPECT_TRUE( !!"Inline namespaces for literal operator 'sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator 'sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator 'sv' for string_view not available (nssv_CONFIG_STD_SV_OPERATOR=0)." );
-#endif
-}
-
-TEST(StringViewTest, CanCreateViewWithLiteral_SV)
-{
-    SCOPED_TRACE( "string_view: Allows to create a string_view, wstring_view, u16string_view, u32string_view via literal \"_sv\"" );
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-#if nssv_STD_SV_OR( nssv_HAVE_USER_DEFINED_LITERALS )
-    using namespace gmx::compat::literals::string_view_literals;
-
-    string_view sv1 =  "abc"_sv;
-    wstring_view sv2 = L"abc"_sv;
-
-    EXPECT_TRUE( sv1.size() == size_type( 3 ) );
-    EXPECT_TRUE( sv2.size() == size_type( 3 ) );
-
-#if nssv_HAVE_WCHAR16_T
-    u16string_view sv3 = u"abc"_sv;
-    EXPECT_TRUE( sv3.size() == size_type( 3 ) );
-#endif
-#if nssv_HAVE_WCHAR32_T
-    u32string_view sv4 = U"abc"_sv;
-    EXPECT_TRUE( sv4.size() == size_type( 3 ) );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator '_sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator '_sv' for string_view not available (nssv_CONFIG_USR_SV_OPERATOR=0)." );
-#endif
-}
-
-TEST(StringViewTest, CanCreateViewWithLiteral_SVInLiteralsStringViewLiteralsNamespace)
-{
-    SCOPED_TRACE( "string_view: Allows to create a string_view via literal \"_sv\", using namespace gmx::compat::literals::string_view_literals" );
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-#if nssv_STD_SV_OR( nssv_HAVE_USER_DEFINED_LITERALS )
-    using namespace gmx::compat::literals::string_view_literals;
-
-    string_view sv1 = "abc\0\0def";
-    string_view sv2 = "abc\0\0def"_sv;
-
-    EXPECT_TRUE( sv1.size() == size_type( 3 ) );
-    EXPECT_TRUE( sv2.size() == size_type( 8 ) );
-#else
-    EXPECT_TRUE( !!"Literal operator '_sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator '_sv' for string_view not available (nssv_CONFIG_USR_SV_OPERATOR=0)." );
-#endif
-}
-
-TEST(StringViewTest, CanCreateViewWithLiteral_SVInStringViewLiteralsNamespace)
-{
-    SCOPED_TRACE( "string_view: Allows to create a string_view via literal \"_sv\", using namespace gmx::compat::string_view_literals" );
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-#if nssv_STD_SV_OR( nssv_HAVE_USER_DEFINED_LITERALS )
-#if nssv_STD_SV_OR( nssv_HAVE_INLINE_NAMESPACE )
-    using namespace gmx::compat::string_view_literals;
-
-    string_view sv1 = "abc\0\0def";
-    string_view sv2 = "abc\0\0def"_sv;
-
-    EXPECT_TRUE( sv1.size() == size_type( 3 ) );
-    EXPECT_TRUE( sv2.size() == size_type( 8 ) );
-#else
-    EXPECT_TRUE( !!"Inline namespaces for literal operator '_sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator '_sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator '_sv' for string_view not available (nssv_CONFIG_USR_SV_OPERATOR=0)." );
-#endif
-}
-
-TEST(StringViewTest, CanCreateViewWithLiteral_SVInLiteralsNamespace)
-{
-    SCOPED_TRACE( "string_view: Allows to create a string_view via literal \"_sv\", using namespace gmx::compat::literals" );
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-#if nssv_STD_SV_OR( nssv_HAVE_USER_DEFINED_LITERALS )
-#if nssv_STD_SV_OR( nssv_HAVE_INLINE_NAMESPACE )
-    using namespace gmx::compat::literals;
-
-    string_view sv1 = "abc\0\0def";
-    string_view sv2 = "abc\0\0def"_sv;
-
-    EXPECT_TRUE( sv1.size() == size_type( 3 ) );
-    EXPECT_TRUE( sv2.size() == size_type( 8 ) );
-#else
-    EXPECT_TRUE( !!"Inline namespaces for literal operator '_sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator '_sv' for string_view not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Literal operator '_sv' for string_view not available (nssv_CONFIG_USR_SV_OPERATOR=0)." );
-#endif
-}
-
-// 24.4.3 Non-member comparison functions:
-
-TEST(StringViewTest, CanCompareToViews)
-{
-    SCOPED_TRACE( "string_view: Allows to compare a string_view with another string_view" );
-
-    char s[] = "hello";
-    char t[] = "world";
-    string_view sv( s );
-    string_view tv( t );
-
-    EXPECT_TRUE( sv.length() == size_type( 5 ) );
-    EXPECT_TRUE( tv.length() == size_type( 5 ) );
-
-    EXPECT_TRUE( sv == sv );
-    EXPECT_TRUE( sv != tv );
-    EXPECT_TRUE( sv <= sv );
-    EXPECT_TRUE( sv <= tv );
-    EXPECT_TRUE( sv <  tv );
-    EXPECT_TRUE( tv >= tv );
-    EXPECT_TRUE( tv >= sv );
-    EXPECT_TRUE( tv >  sv );
-}
-
-TEST(StringViewTest, CanCompareViewToImplicitlyConvertedView)
-{
-    SCOPED_TRACE( "string_view: Allows to compare a string_view with an object with implicit conversion to string_view" );
-
-#if nssv_CPP11_OR_GREATER
-#if defined(_MSC_VER) && _MSC_VER != 1900
-    char s[] = "hello";
-    string_view sv( s );
-
-    EXPECT_TRUE( sv == "hello"       );
-    EXPECT_TRUE(       "hello" == sv );
-
-    EXPECT_TRUE( sv != "world"       );
-    EXPECT_TRUE(       "world" != sv );
-
-    EXPECT_TRUE( sv <= "hello"       );
-    EXPECT_TRUE(       "hello" <= sv );
-    EXPECT_TRUE( sv <= "world"       );
-    EXPECT_TRUE(       "aloha" <= sv );
-
-    EXPECT_TRUE( sv <  "world"       );
-    EXPECT_TRUE(       "aloha" <  sv );
-
-    EXPECT_TRUE( sv >= "hello"       );
-    EXPECT_TRUE(       "hello" >= sv );
-    EXPECT_TRUE( sv >= "aloha"       );
-    EXPECT_TRUE(       "world" >= sv );
-
-    EXPECT_TRUE( sv >  "aloha"       );
-    EXPECT_TRUE(       "world"  >  sv );
-#else
-    EXPECT_TRUE( !!"Comparison for types with implicit conversion to string_view not available (insufficient C++11 support of MSVC)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Comparison for types with implicit conversion to string_view not available (no C++11)." );
-#endif
-}
-
-TEST(StringViewTest, EmptyViewsCompareAsEqual)
-{
-    SCOPED_TRACE( "string_view: Allows to compare empty string_view-s as equal" );
-
-    string_view a, b;
-
-    EXPECT_TRUE( a == b );
-}
-
-// 24.4.4 Inserters and extractors:
-
-TEST(StringViewTest, CanPrintViewToPutputStream)
-{
-    SCOPED_TRACE ( "operator<<: Allows printing a string_view to an output stream" );
-
-    std::ostringstream oss;
-    char s[] = "hello";
-    string_view sv( s );
-
-    oss << sv << '\n'
-        << std::right << std::setw(10) << sv << '\n'
-        << sv << '\n'
-        << std::setfill('.') << std::left << std::setw(10) << sv;
-
-    EXPECT_TRUE( oss.str() == "hello\n     hello\nhello\nhello....." );
-}
-
-// 24.4.5 Hash support (C++11):
-
-TEST(StringViewTest, HashOfViewIsEqualToHashOfString)
-{
-    SCOPED_TRACE ( "std::hash<>: Hash value of string_view equals hash value of corresponding string object" );
-
-#if nssv_HAVE_STD_HASH
-    EXPECT_TRUE( std::hash<string_view>()( "Hello, world!" ) == std::hash<std::string>()( "Hello, world!" ) );
-#else
-    EXPECT_TRUE( !!"std::hash is not available (no C++11)" );
-#endif
-}
-
-TEST(StringViewTest, HashOfWStringViewIsEqualToHashOfString)
-{
-    SCOPED_TRACE ( "std::hash<>: Hash value of wstring_view equals hash value of corresponding string object" );
-
-#if nssv_HAVE_STD_HASH
-    EXPECT_TRUE( std::hash<wstring_view>()( L"Hello, world!" ) == std::hash<std::wstring>()( L"Hello, world!" ) );
-#else
-    EXPECT_TRUE( !!"std::hash is not available (no C++11)" );
-#endif
-}
-
-TEST(StringViewTest, HashOfU16StringViewIsEqualToHashOfString)
-{
-    SCOPED_TRACE ( "std::hash<>: Hash value of u16string_view equals hash value of corresponding string object" );
-
-#if nssv_HAVE_STD_HASH
-#if nssv_HAVE_WCHAR16_T
-#if nssv_HAVE_UNICODE_LITERALS
-    EXPECT_TRUE( std::hash<u16string_view>()( u"Hello, world!" ) == std::hash<std::u16string>()( u"Hello, world!" ) );
-#else
-    EXPECT_TRUE( !!"Unicode literal u\"...\" is not available (no C++11)" );
-#endif
-#else
-    EXPECT_TRUE( !!"std::u16string is not available (no C++11)" );
-#endif
-#else
-    EXPECT_TRUE( !!"std::hash is not available (no C++11)" );
-#endif
-}
-
-TEST(StringViewTest, HashOfU32StringViewIsEqualToHashOfString)
-{
-    SCOPED_TRACE ( "std::hash<>: Hash value of u32string_view equals hash value of corresponding string object" );
-
-#if nssv_HAVE_STD_HASH
-#if nssv_HAVE_WCHAR16_T
-#if nssv_HAVE_UNICODE_LITERALS
-    EXPECT_TRUE( std::hash<u32string_view>()( U"Hello, world!" ) == std::hash<std::u32string>()( U"Hello, world!" ) );
-#else
-    EXPECT_TRUE( !!"Unicode literal U\"...\" is not available (no C++11)" );
-#endif
-#else
-    EXPECT_TRUE( !!"std::u32string is not available (no C++11)" );
-#endif
-#else
-    EXPECT_TRUE( !!"std::hash is not available (no C++11)" );
-#endif
-}
-
-// nonstd extension: conversions from and to std::basic_string
-
-TEST(StringViewExtensionTest, CanConstructViewFromString)
-{
-    SCOPED_TRACE( "string_view: construct from std::string " "[extension]" );
-
-#if nssv_USES_STD_STRING_VIEW
-    EXPECT_TRUE( !!"Conversion to/from std::string is not available (nssv_USES_STD_STRING_VIEW=1)." );
-#elif nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-    char hello[]  = "hello world";
-    std::string s =  hello;
-
-    string_view sv( hello );
-
-    EXPECT_TRUE( sv.size() == s.size() );
-    EXPECT_TRUE( sv.compare( s ) == 0  );
-#else
-    EXPECT_TRUE( !!"Conversion to/from std::string is not available (nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS=0)." );
-#endif
-}
-
-TEST(StringViewExtensionTest, CanConvertViewToStringViaExplicitOperator)
-{
-    SCOPED_TRACE( "string_view: convert to std::string via explicit operator " "[extension]" );
-
-#if nssv_USES_STD_STRING_VIEW
-    EXPECT_TRUE( !!"Conversion to/from std::string is not available (nssv_USES_STD_STRING_VIEW=1)." );
-#elif nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-#if nssv_HAVE_EXPLICIT_CONVERSION
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    std::string s( sv );
-//  std::string t{ sv };
-
-    EXPECT_TRUE( sv.size() == s.size() );
-    EXPECT_TRUE( sv.compare( s ) == 0  );
-#else
-    EXPECT_TRUE( !!"explicit conversion is not available (no C++11)." );
-#endif
-#else
-    EXPECT_TRUE( !!"Conversion to/from std::string is not available (nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS=0)." );
-#endif
-}
-
-TEST(StringViewExtensionTest, CanConvertViewToStringViaToString)
-{
-    SCOPED_TRACE( "string_view: convert to std::string via to_string() " "[extension]" );
-
-#if nssv_USES_STD_STRING_VIEW
-    EXPECT_TRUE( !!"Conversion to/from std::string is not available (nssv_USES_STD_STRING_VIEW=1)." );
-#elif nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    std::string s1 = sv.to_string();
-
-    EXPECT_TRUE( sv.size() == s1.size() );
-    EXPECT_TRUE( sv.compare( s1 ) == 0  );
-
-    std::string s2 = sv.to_string( std::string::allocator_type() );
-
-    EXPECT_TRUE( sv.size() == s2.size() );
-    EXPECT_TRUE( sv.compare( s2 ) == 0  );
-#else
-    EXPECT_TRUE( !!"Conversion to/from std::string is not available (nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS=0)." );
-#endif
-}
-
-TEST(StringViewExtensionTest, CanConvertViewToStringViaToStringFreeFunction)
-{
-    SCOPED_TRACE( "to_string(): convert to std::string via to_string() " "[extension]" );
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-    char hello[] = "hello world";
-    string_view sv( hello );
-
-    std::string s1 = to_string( sv );
-
-    EXPECT_TRUE( sv.size() == s1.size() );
-    EXPECT_TRUE( sv.compare( s1 ) == 0  );
-
-    std::string s2 = to_string( sv, std::string::allocator_type() );
-
-    EXPECT_TRUE( sv.size() == s2.size() );
-    EXPECT_TRUE( sv.compare( s2 ) == 0  );
-
-#else
-    EXPECT_TRUE( !!"Conversion to/from std::string is not available (nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS=0)." );
-#endif
-}
-
-TEST(StringViewExtensionTest, CanConvertViewToStringViewViaToStringView)
-{
-    SCOPED_TRACE( "to_string_view(): convert from std::string via to_string_view() " "[extension]" );
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-    char hello[] = "hello world";
-    std::string s = hello;
-
-    string_view sv = to_string_view( s );
-
-    EXPECT_TRUE( sv.size() == s.size() );
-    EXPECT_TRUE( sv.compare( s ) == 0  );
-#else
-    EXPECT_TRUE( !!"Conversion to/from std::string is not available (nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS=0)." );
-#endif
-}
-
-} // anonymous namespace
-
-} // namespace gmx
-
-// GMX modification to suppress Doxygen checking
-#endif // DOXYGEN
index c3fcdc6c5f8c6c0284f6f880853cd976ec5e9308..705b6a73852d61da21501753d282d42e00d61929 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -36,6 +36,8 @@
 /*! \libinternal \file
  * \brief Provides backported functions/classes from utility
  *
+ * \todo Remove when CUDA 11 is a requirement.
+ *
  * \author Roland Schulz <roland.schulz@intel.com>
  * \ingroup module_compat
  * \inlibraryapi
index 4c245b12725c4d706422dbb3681d65ec01be4fc8..09fc9733c10142e4c815ea33e1c8aafccfce7842 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * \ingroup module_coordinateio
  */
 
-
 #include "gmxpre.h"
 
 #include "coordinatefile.h"
 
 #include <algorithm>
 
+#include "gromacs/options.h"
 #include "gromacs/coordinateio/outputadapters.h"
 #include "gromacs/coordinateio/requirements.h"
+#include "gromacs/fileio/trxio.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/topology/mtop_util.h"
 #include "gromacs/trajectory/trajectoryframe.h"
 #include "gromacs/utility/exceptions.h"
 
@@ -322,8 +324,8 @@ deepCopy_t_trxframe(const t_trxframe& input, t_trxframe* copy, RVec* xvec, RVec*
         }
     }
     copy_mat(input.box, copy->box);
-    copy->bPBC = input.bPBC;
-    copy->ePBC = input.ePBC;
+    copy->bPBC    = input.bPBC;
+    copy->pbcType = input.pbcType;
 }
 
 /*! \brief
index dfd822757383f65e560152299ba479bca17bce53..86dcc9b4ab542d61c078fdc63921373c036783a5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_COORDINATEIO_COORDINATEFILE_H
 #define GMX_COORDINATEIO_COORDINATEFILE_H
 
-#include <algorithm>
+#include <string>
 #include <utility>
 
 #include "gromacs/coordinateio/ioutputadapter.h"
 #include "gromacs/coordinateio/outputadaptercontainer.h"
-#include "gromacs/fileio/filetypes.h"
-#include "gromacs/fileio/trxio.h"
-#include "gromacs/selection/selection.h"
-#include "gromacs/topology/mtop_util.h"
-#include "gromacs/topology/topology.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/atoms.h"
+
+struct gmx_mtop_t;
+struct t_trxstatus;
 
 namespace gmx
 {
 
+class Selection;
 class TrajectoryFrameWriter;
 struct OutputRequirements;
 
index ecd2d4652ca6cf84047176841c589e493bbff845..b1b10033b34263e71d7728c7642b8a48ff17925b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -138,14 +138,13 @@ inline unsigned long convertFlag(CoordinateFileFlags flag)
 }
 
 //! Enum class for setting basic flags in a t_trxframe
-enum class ChangeSettingType
+enum class ChangeSettingType : int
 {
     PreservedIfPresent,
     Always,
-    Never
+    Never,
+    Count
 };
-//! Mapping for enums from \ref ChangeSettingType.
-const char* const cChangeSettingTypeEnum[] = { "preserved-if-present", "always", "never" };
 
 //! Enum class for t_atoms settings
 enum class ChangeAtomsType
@@ -153,21 +152,17 @@ enum class ChangeAtomsType
     PreservedIfPresent,
     AlwaysFromStructure,
     Never,
-    Always
+    Always,
+    Count
 };
 
-//! Mapping for enums from \ref ChangeAtomsType.
-const char* const cChangeAtomsTypeEnum[] = { "preserved-if-present", "always-from-structure",
-                                             "never", "always" };
-
 //! Enum class for setting fields new or not.
 enum class ChangeFrameInfoType
 {
     PreservedIfPresent,
-    Always
+    Always,
+    Count
 };
-//! Mapping for enums from \ref ChangeFrameInfoType.
-const char* const cChangeFrameInfoTypeEnum[] = { "preserved-if-present", "always" };
 
 //! Enum class for setting frame time from user input.
 enum class ChangeFrameTimeType
@@ -175,14 +170,10 @@ enum class ChangeFrameTimeType
     PreservedIfPresent,
     StartTime,
     TimeStep,
-    Both
+    Both,
+    Count
 };
 
-//! Mapping for values from \ref ChangeFrameTimeType.
-const char* const cChangeFrameTimeTypeEnum[] = { "preserved-if-present", "starttime", "timestep",
-                                                 "both" };
-
-
 } // namespace gmx
 
 #endif
index a450cf637975178462c9a37c004ec231ff579eae..5a65e621c7f6a3f21d976e6749bf461dd50af516 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -100,7 +100,7 @@ void SetAtoms::processFrame(const int /*framenumber*/, t_trxframe* input)
     }
 }
 
-bool SetAtoms::haveFrameAtoms(const t_trxframe& input) const
+bool SetAtoms::haveFrameAtoms(const t_trxframe& input)
 {
     return input.bAtoms;
 }
index 58e8a7870d89b184154e09e3bc4e88850d32c353..00d7d1af377934d99493f576aad6433c0e0a77b5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -125,7 +125,7 @@ private:
      *
      *  \param[in] input t_trxframe before we start modifying it.
      */
-    bool haveFrameAtoms(const t_trxframe& input) const;
+    static bool haveFrameAtoms(const t_trxframe& input);
     //! Test if the atoms data is available for writing.
     bool haveAtoms(const t_trxframe& input) const
     {
index 71757b51d1bf2c0f53c1a7372015e5e822a05fb8..dc3f8a830a33d2da0a13900923f0837cffdfdf68 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/options/basicoptions.h"
 #include "gromacs/options/filenameoption.h"
 #include "gromacs/options/ioptionscontainer.h"
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/exceptions.h"
 
 namespace gmx
 {
 
+//! Mapping for enums from \ref ChangeSettingType.
+static const EnumerationArray<ChangeSettingType, const char*> c_changeSettingTypeNames = {
+    { "preserved-if-present", "always", "never" }
+};
+//! Mapping for enums from \ref ChangeAtomsType.
+static const EnumerationArray<ChangeAtomsType, const char*> c_changeAtomsTypeNames = {
+    { "preserved-if-present", "always-from-structure", "never", "always" }
+};
+/* Currently unused
+//! Mapping for enums from \ref ChangeFrameInfoType.
+static const EnumerationArray<ChangeFrameInfoType, const char*> c_changeFrameInfoTypeNames = { {
+"preserved-if-present", "always" } };
+//! Mapping for values from \ref ChangeFrameTimeType.
+static const EnumerationArray<ChangeFrameTimeType, const char*> c_changeFrameTimeTypeNames = { {
+"preserved-if-present", "starttime", "timestep", "both" } };
+*/
+
 void OutputRequirementOptionDirector::initOptions(IOptionsContainer* options)
 {
     options->addOption(EnumOption<ChangeSettingType>("vel")
-                               .enumValue(cChangeSettingTypeEnum)
+                               .enumValue(c_changeSettingTypeNames)
                                .store(&velocity_)
                                .description("Save velocities from frame if possible"));
     options->addOption(EnumOption<ChangeSettingType>("force")
-                               .enumValue(cChangeSettingTypeEnum)
+                               .enumValue(c_changeSettingTypeNames)
                                .store(&force_)
                                .description("Save forces from frame if possible"));
     options->addOption(
-            EnumOption<ChangeAtomsType>("atoms").enumValue(cChangeAtomsTypeEnum).store(&atoms_).description("Decide on providing new atom information from topology or using current frame atom information"));
+            EnumOption<ChangeAtomsType>("atoms").enumValue(c_changeAtomsTypeNames).store(&atoms_).description("Decide on providing new atom information from topology or using current frame atom information"));
     options->addOption(IntegerOption("precision")
                                .store(&prec_)
                                .defaultValue(prec_)
index 7fe3add7c7dedda9f9e3a05cc5cedeb847790a30..611fbb21f88b98aea624b40eafe702053b406b8e 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-set(test_sources
-    builder.cpp
-    outputadaptercontainer.cpp
-    outputadapters.cpp
-    requirements.cpp
-    setatoms.cpp
-    setbothtime.cpp
-    setstarttime.cpp
-    settimestep.cpp
-    testmodule.cpp
-    )
-gmx_add_unit_test(CoordinateIOTests coordinateio-test ${test_sources})
+gmx_add_unit_test(CoordinateIOTests coordinateio-test
+    CPP_SOURCE_FILES
+        builder.cpp
+        outputadaptercontainer.cpp
+        outputadapters.cpp
+        requirements.cpp
+        setatoms.cpp
+        setbothtime.cpp
+        setstarttime.cpp
+        settimestep.cpp
+        testmodule.cpp
+        )
+
index da4dd48b157667a0c4b3ebf84ca4924c2589a7d9..8a91f71262eea677e90be24eeb09b799a88db565 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,7 +52,6 @@
 #include "gromacs/coordinateio/requirements.h"
 #include "gromacs/options/options.h"
 #include "gromacs/utility/any.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/stringutil.h"
 
 #include "gromacs/coordinateio/tests/coordinate_test.h"
@@ -60,6 +59,9 @@
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
+
 namespace test
 {
 
@@ -126,7 +128,10 @@ public:
      * \param[in] options      Container for options.
      * \param[in] type         Need to know type of entries.
      */
-    void setModuleFlag(const std::string& optionName, const std::string& optionValues, Options* options, TestEnums type)
+    static void setModuleFlag(const std::string& optionName,
+                              const std::string& optionValues,
+                              Options*           options,
+                              TestEnums          type)
     {
         OptionsAssigner assigner(options);
         assigner.start();
index 79ac2ad7b27fb501cf3d562f9274f7854ca8cbaa..eff560c9d5400bc58a9694b3d629e48aec052a88 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,7 @@
 #include <utility>
 
 #include "gromacs/coordinateio/outputadapters/setatoms.h"
+#include "gromacs/fileio/trxio.h"
 #include "gromacs/trajectory/trajectoryframe.h"
 #include "gromacs/trajectoryanalysis/topologyinformation.h"
 
index b015ef7a6bc3bae7cc9167a388200bc1f389acbc..3463cc9465fcc3eed7034dd99ee9ccc72846a70c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,6 +48,7 @@
 
 #include "gromacs/coordinateio/outputadapters/setstarttime.h"
 #include "gromacs/coordinateio/outputadapters/settimestep.h"
+#include "gromacs/fileio/trxio.h"
 #include "gromacs/trajectory/trajectoryframe.h"
 
 #include "gromacs/coordinateio/tests/coordinate_test.h"
index 5c22753c4daa61c9b94ba23453c442b40a7436f7..8559c767de57a576261099172e3f7b1e9f8a9627 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,7 @@
 #include <memory>
 
 #include "gromacs/coordinateio/outputadapters/setstarttime.h"
+#include "gromacs/fileio/trxio.h"
 #include "gromacs/trajectory/trajectoryframe.h"
 
 #include "gromacs/coordinateio/tests/coordinate_test.h"
index cf2a9924ea17d4b5cd4a53a296be5ac107f4e1ee..ed24859c9bbf8ee6a5e43e8b3b2bd9327113689d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,7 @@
 #include <memory>
 
 #include "gromacs/coordinateio/outputadapters/settimestep.h"
+#include "gromacs/fileio/trxio.h"
 #include "gromacs/trajectory/trajectoryframe.h"
 
 #include "gromacs/coordinateio/tests/coordinate_test.h"
index 1f4f5c7a3b0186e914a372187111c4d7e1c08cf7..3c6b4b7018dd1c796695ba993535452e19da5115 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 840f8166754de073b1a1439b01ac348f6dc79c4f..b72a7ca743ab0a609e329df9ab62711d19664539 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 1bcb355f5cab07323a4114ff9c84c9e1169bd1ea..77acdcc2403db65f5072d2766f29c75250e9f391 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2016, by the GROMACS development team, led by
+# Copyright (c) 2014,2016,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(CorrelationsTest  correlations-test
-  autocorr.cpp
-  manyautocorrelation.cpp
-  correlationdataset.cpp
-  expfit.cpp)
+    CPP_SOURCE_FILES
+        autocorr.cpp
+        correlationdataset.cpp
+        expfit.cpp
+        manyautocorrelation.cpp
+        )
 
index 9c1dd7ebc3adc9d39a180228817da5d41ce3fdb6..eb6e17e1c15ab72ce148b351c5351b4c2b2371de 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -136,18 +137,20 @@ protected:
         checker_.checkReal(testResult, "Integral");
     }
 
-    int getDim(unsigned long type)
+    static int getDim(unsigned long type)
     {
         switch (type)
         {
             case eacNormal: return 1;
             case eacVector: return 3;
-            case eacCos: return 1;
-            case eacRcross: return 3;
-            case eacP0: return 3;
-            case eacP1: return 3;
-            case eacP2: return 3;
-            case eacP3: return 3;
+            case eacCos:
+                return 1;
+                // Several intended fall-throughs follow
+            case eacRcross:
+            case eacP0:
+            case eacP1:
+            case eacP2:
+            case eacP3:
             case eacP4: return 3;
             case eacIden: return 1;
             default: GMX_RELEASE_ASSERT(false, "Invalid auto correlation option"); return -1;
index c4fca1e308a2c540ee356a939067a407ad91d719..d57300371103bb1f17b690a93ac1716f31560432 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -34,7 +34,7 @@
 
 file(GLOB DOMDEC_SOURCES *.cpp)
 
-if(GMX_USE_CUDA)
+if(GMX_GPU_CUDA)
   file(GLOB DOMDEC_CUDA_SOURCES gpuhaloexchange_impl.cu)
 endif()
 
index 0e9f4e611bc9258494c3d6ba3be60f0d7d8bf1d4..8c342f84380cac78f50f71b896a70352cf4f466a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -288,8 +289,8 @@ void set_ddbox(const gmx_domdec_t&            dd,
         gmx::ArrayRef<const gmx::RVec> xRef = constArrayRefFromArray(
                 x.data(), masterRankHasTheSystemState ? x.size() : dd.comm->atomRanges.numHomeAtoms());
 
-        low_set_ddbox(dd.unitCellInfo.npbcdim, dd.unitCellInfo.numBoundedDimensions, &dd.nc, box,
-                      calculateUnboundedSize, xRef,
+        low_set_ddbox(dd.unitCellInfo.npbcdim, dd.unitCellInfo.numBoundedDimensions, &dd.numCells,
+                      box, calculateUnboundedSize, xRef,
                       needToReduceCoordinateData ? &dd.mpi_comm_all : nullptr, ddbox);
     }
 
@@ -299,17 +300,19 @@ void set_ddbox(const gmx_domdec_t&            dd,
     }
 }
 
-void set_ddbox_cr(const t_commrec&               cr,
+void set_ddbox_cr(DDRole                         ddRole,
+                  MPI_Comm                       communicator,
                   const ivec*                    dd_nc,
                   const t_inputrec&              ir,
                   const matrix                   box,
                   gmx::ArrayRef<const gmx::RVec> x,
                   gmx_ddbox_t*                   ddbox)
 {
-    if (MASTER(&cr))
+    if (ddRole == DDRole::Master)
     {
-        low_set_ddbox(ePBC2npbcdim(ir.ePBC), inputrec2nboundeddim(&ir), dd_nc, box, true, x, nullptr, ddbox);
+        low_set_ddbox(numPbcDimensions(ir.pbcType), inputrec2nboundeddim(&ir), dd_nc, box, true, x,
+                      nullptr, ddbox);
     }
 
-    gmx_bcast(sizeof(gmx_ddbox_t), ddbox, &cr);
+    gmx_bcast(sizeof(gmx_ddbox_t), ddbox, communicator);
 }
index d02c34b9d802799ea750f17f943b89ce4bf2ad87..7974b801c1e4bc3fcf8bc5a56c17dad9a560e284 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <vector>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxmpi.h"
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
 struct gmx_ddbox_t;
 struct gmx_domdec_t;
 struct t_commrec;
 struct t_inputrec;
+enum class DDRole;
 
 /*! \brief Set the box and PBC data in \p ddbox */
 void set_ddbox(const gmx_domdec_t&            dd,
@@ -63,7 +69,8 @@ void set_ddbox(const gmx_domdec_t&            dd,
                gmx_ddbox_t*                   ddbox);
 
 /*! \brief Set the box and PBC data in \p ddbox */
-void set_ddbox_cr(const t_commrec&               cr,
+void set_ddbox_cr(DDRole                         ddRole,
+                  MPI_Comm                       communicator,
                   const ivec*                    dd_nc,
                   const t_inputrec&              ir,
                   const matrix                   box,
index 067524997d0b398e3ff046db79af66de3081c09b..01239c8058a5c9c5b2048de02acd7c2aea310f41 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -79,7 +79,6 @@ public:
                                t_commrec*           cr,
                                const DomdecOptions& options,
                                const MdrunOptions&  mdrunOptions,
-                               bool                 prefer1DAnd1Pulse,
                                const gmx_mtop_t&    mtop,
                                const t_inputrec&    ir,
                                const matrix         box,
index 28078ffc54bfd45b2c663529cf18a3aad64b527e..4867f64330aebaac43ddc9c040398bae851ae8f0 100644 (file)
@@ -70,7 +70,7 @@ static void set_pme_maxshift(gmx_domdec_t*      dd,
     int                sh;
 
     comm = dd->comm;
-    nc   = dd->nc[ddpme->dim];
+    nc   = dd->numCells[ddpme->dim];
     ns   = ddpme->nslab;
 
     if (!ddpme->dim_match)
@@ -137,13 +137,13 @@ static void check_box_size(const gmx_domdec_t* dd, const gmx_ddbox_t* ddbox)
         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)
+                       < dd->numCells[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],
+                    dim2char(dim), ddbox->box_size[dim], ddbox->skew_fac[dim], dd->numCells[dim],
                     dd->comm->cellsize_limit);
         }
     }
@@ -221,14 +221,14 @@ set_dd_cell_sizes_slb(gmx_domdec_t* dd, const gmx_ddbox_t* ddbox, int setmode, i
     {
         cellsize_min[d] = ddbox->box_size[d] * ddbox->skew_fac[d];
         npulse[d]       = 1;
-        if (dd->nc[d] == 1 || comm->slb_frac[d] == nullptr)
+        if (dd->numCells[d] == 1 || comm->slb_frac[d] == nullptr)
         {
             /* Uniform grid */
-            real cell_dx = ddbox->box_size[d] / dd->nc[d];
+            real cell_dx = ddbox->box_size[d] / dd->numCells[d];
             switch (setmode)
             {
                 case setcellsizeslbMASTER:
-                    for (int j = 0; j < dd->nc[d] + 1; j++)
+                    for (int j = 0; j < dd->numCells[d] + 1; j++)
                     {
                         cell_x_master[d][j] = ddbox->box0[d] + j * cell_dx;
                     }
@@ -261,16 +261,16 @@ set_dd_cell_sizes_slb(gmx_domdec_t* dd, const gmx_ddbox_t* ddbox, int setmode, i
             }
             else
             {
-                cell_x_buffer.resize(dd->nc[d] + 1);
+                cell_x_buffer.resize(dd->numCells[d] + 1);
                 cell_x = cell_x_buffer;
             }
             cell_x[0] = ddbox->box0[d];
-            for (int j = 0; j < dd->nc[d]; j++)
+            for (int j = 0; j < dd->numCells[d]; j++)
             {
                 real cell_dx  = ddbox->box_size[d] * comm->slb_frac[d][j];
                 cell_x[j + 1] = cell_x[j] + cell_dx;
                 real cellsize = cell_dx * ddbox->skew_fac[d];
-                while (cellsize * npulse[d] < comm->systemInfo.cutoff && npulse[d] < dd->nc[d] - 1)
+                while (cellsize * npulse[d] < comm->systemInfo.cutoff && npulse[d] < dd->numCells[d] - 1)
                 {
                     npulse[d]++;
                 }
@@ -286,7 +286,7 @@ set_dd_cell_sizes_slb(gmx_domdec_t* dd, const gmx_ddbox_t* ddbox, int setmode, i
          * 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])
+        if (d < ddbox->npbcdim && dd->numCells[d] > 1 && npulse[d] >= dd->numCells[d])
         {
             char error_string[STRLEN];
 
@@ -295,7 +295,7 @@ set_dd_cell_sizes_slb(gmx_domdec_t* dd, const gmx_ddbox_t* ddbox, int setmode, i
                     "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->systemInfo.cutoff,
-                    dd->nc[d], dd->nc[d], dd->nnodes > dd->nc[d] ? "cells" : "ranks");
+                    dd->numCells[d], dd->numCells[d], dd->nnodes > dd->numCells[d] ? "cells" : "ranks");
 
             if (setmode == setcellsizeslbLOCAL)
             {
@@ -346,7 +346,7 @@ static void dd_cell_sizes_dlb_root_enforce_limits(gmx_domdec_t*      dd,
 
     comm = dd->comm;
 
-    const int ncd = dd->nc[dim];
+    const int ncd = dd->numCells[dim];
 
     const bool dimHasPbc = (dim < ddbox->npbcdim);
 
@@ -391,7 +391,7 @@ static void dd_cell_sizes_dlb_root_enforce_limits(gmx_domdec_t*      dd,
             if (!rowMaster->isCellMin[i])
             {
                 cell_size[i] *= fac;
-                if (!dimHasPbc && (i == 0 || i == dd->nc[dim] - 1))
+                if (!dimHasPbc && (i == 0 || i == dd->numCells[dim] - 1))
                 {
                     cellsize_limit_f_i = 0;
                 }
@@ -572,7 +572,7 @@ static void set_dd_cell_sizes_dlb_root(gmx_domdec_t*      dd,
     /* Convert the maximum change from the input percentage to a fraction */
     const real change_limit = comm->ddSettings.dlb_scale_lim * 0.01;
 
-    const int ncd = dd->nc[dim];
+    const int ncd = dd->numCells[dim];
 
     const bool bPBC = (dim < ddbox->npbcdim);
 
@@ -688,7 +688,7 @@ static void set_dd_cell_sizes_dlb_root(gmx_domdec_t*      dd,
                     rowMaster->cellFrac[i], rowMaster->cellFrac[i + 1]);
         }
 
-        if ((bPBC || (i != 0 && i != dd->nc[dim] - 1))
+        if ((bPBC || (i != 0 && i != dd->numCells[dim] - 1))
             && rowMaster->cellFrac[i + 1] - rowMaster->cellFrac[i] < cellsize_limit_f / DD_CELL_MARGIN)
         {
             char buf[22];
@@ -757,7 +757,7 @@ static void distribute_dd_cell_sizes_dlb(gmx_domdec_t*       dd,
     comm.cellsizesWithDlb[d].fracLower = cellFracRow[dd->ci[dim]];
     comm.cellsizesWithDlb[d].fracUpper = cellFracRow[dd->ci[dim] + 1];
     /* The whole array was communicated, so set the buffer position */
-    int pos = dd->nc[dim] + 1;
+    int pos = dd->numCells[dim] + 1;
     for (int d1 = 0; d1 <= d; d1++)
     {
         if (d1 < d)
@@ -860,7 +860,7 @@ static void set_dd_cell_sizes_dlb(gmx_domdec_t*      dd,
     /* Set the dimensions for which no DD is used */
     for (dim = 0; dim < DIM; dim++)
     {
-        if (dd->nc[dim] == 1)
+        if (dd->numCells[dim] == 1)
         {
             comm->cell_x0[dim] = 0;
             comm->cell_x1[dim] = ddbox->box_size[dim];
index d35da8357f9421f2fabf4e16415db47ea5a070ed..cf07585e89c62133cae9a41c677865a55608d826 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/math/vectypes.h"
 #include "gromacs/timing/wallcycle.h"
-#include "gromacs/utility/arrayref.h"
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
 struct gmx_ddbox_t;
 struct gmx_domdec_comm_t;
 struct gmx_domdec_t;
index 431fe7f9fa71f73c6fbf3db46a3fdb2a56b5c7c9..0924461f7889ff28364c06e9e66241240df54235 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "distribute.h"
 #include "domdec_internal.h"
 
-static void dd_collect_cg(gmx_domdec_t* dd, const t_state* state_local)
+static void dd_collect_cg(gmx_domdec_t*            dd,
+                          const int                ddpCount,
+                          const int                ddpCountCgGl,
+                          gmx::ArrayRef<const int> localCGNumbers)
 {
-    if (state_local->ddp_count == dd->comm->master_cg_ddp_count)
+    if (ddpCount == dd->comm->master_cg_ddp_count)
     {
         /* The master has the correct distribution */
         return;
@@ -66,18 +69,18 @@ static void dd_collect_cg(gmx_domdec_t* dd, const t_state* state_local)
     gmx::ArrayRef<const int> atomGroups;
     int                      nat_home = 0;
 
-    if (state_local->ddp_count == dd->ddp_count)
+    if (ddpCount == dd->ddp_count)
     {
         /* The local state and DD are in sync, use the DD indices */
         atomGroups = gmx::constArrayRefFromArray(dd->globalAtomGroupIndices.data(), dd->ncg_home);
         nat_home   = dd->comm->atomRanges.numHomeAtoms();
     }
-    else if (state_local->ddp_count_cg_gl == state_local->ddp_count)
+    else if (ddpCountCgGl == ddpCount)
     {
         /* The DD is out of sync with the local state, but we have stored
          * the cg indices with the local state, so we can use those.
          */
-        atomGroups = state_local->cg_gl;
+        atomGroups = localCGNumbers;
         nat_home   = atomGroups.size();
     }
     else
@@ -136,7 +139,7 @@ static void dd_collect_cg(gmx_domdec_t* dd, const t_state* state_local)
                DDMASTER(dd) ? ma->intBuffer.data() + dd->nnodes : nullptr,
                DDMASTER(dd) ? ma->atomGroups.data() : nullptr);
 
-    dd->comm->master_cg_ddp_count = state_local->ddp_count;
+    dd->comm->master_cg_ddp_count = ddpCount;
 }
 
 static void dd_collect_vec_sendrecv(gmx_domdec_t*                  dd,
@@ -226,19 +229,21 @@ static void dd_collect_vec_gatherv(gmx_domdec_t*                  dd,
 }
 
 void dd_collect_vec(gmx_domdec_t*                  dd,
-                    const t_state*                 state_local,
-                    gmx::ArrayRef<const gmx::RVec> lv,
-                    gmx::ArrayRef<gmx::RVec>       v)
+                    const int                      ddpCount,
+                    const int                      ddpCountCgGl,
+                    gmx::ArrayRef<const int>       localCGNumbers,
+                    gmx::ArrayRef<const gmx::RVec> localVector,
+                    gmx::ArrayRef<gmx::RVec>       globalVector)
 {
-    dd_collect_cg(dd, state_local);
+    dd_collect_cg(dd, ddpCount, ddpCountCgGl, localCGNumbers);
 
     if (dd->nnodes <= c_maxNumRanksUseSendRecvForScatterAndGather)
     {
-        dd_collect_vec_sendrecv(dd, lv, v);
+        dd_collect_vec_sendrecv(dd, localVector, globalVector);
     }
     else
     {
-        dd_collect_vec_gatherv(dd, lv, v);
+        dd_collect_vec_gatherv(dd, localVector, globalVector);
     }
 }
 
@@ -288,16 +293,19 @@ void dd_collect_state(gmx_domdec_t* dd, const t_state* state_local, t_state* sta
     if (state_local->flags & (1 << estX))
     {
         auto globalXRef = state ? state->x : gmx::ArrayRef<gmx::RVec>();
-        dd_collect_vec(dd, state_local, state_local->x, globalXRef);
+        dd_collect_vec(dd, state_local->ddp_count, state_local->ddp_count_cg_gl, state_local->cg_gl,
+                       state_local->x, globalXRef);
     }
     if (state_local->flags & (1 << estV))
     {
         auto globalVRef = state ? state->v : gmx::ArrayRef<gmx::RVec>();
-        dd_collect_vec(dd, state_local, state_local->v, globalVRef);
+        dd_collect_vec(dd, state_local->ddp_count, state_local->ddp_count_cg_gl, state_local->cg_gl,
+                       state_local->v, globalVRef);
     }
     if (state_local->flags & (1 << estCGP))
     {
         auto globalCgpRef = state ? state->cg_p : gmx::ArrayRef<gmx::RVec>();
-        dd_collect_vec(dd, state_local, state_local->cg_p, globalCgpRef);
+        dd_collect_vec(dd, state_local->ddp_count, state_local->ddp_count_cg_gl, state_local->cg_gl,
+                       state_local->cg_p, globalCgpRef);
     }
 }
index 4d4b3b766f912f1b4331973f6ff42f0e59d8ba8f..6ac84f628e23dcc911c928ab9214b12a8f6026a6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_DOMDEC_COLLECT_H
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
 struct gmx_domdec_t;
 class t_state;
 
 /*! \brief Gathers rvec arrays \p localVector to \p globalVector on the master rank */
 void dd_collect_vec(gmx_domdec_t*                  dd,
-                    const t_state*                 localState,
+                    int                            ddpCount,
+                    int                            ddpCountCgGl,
+                    gmx::ArrayRef<const int>       localCGNumbers,
                     gmx::ArrayRef<const gmx::RVec> localVector,
                     gmx::ArrayRef<gmx::RVec>       globalVector);
 
index 50b17507ce8bdb23d9f3db9b18e816b4665c2cea..062acc6b570c712254536c922220ad6dcb1a1adc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -187,10 +187,7 @@ static void dd_distribute_dfhist(gmx_domdec_t* dd, df_history_t* dfhist)
     }
 }
 
-static void dd_distribute_state(gmx_domdec_t*                dd,
-                                const t_state*               state,
-                                t_state*                     state_local,
-                                PaddedHostVector<gmx::RVec>* f)
+static void dd_distribute_state(gmx_domdec_t* dd, const t_state* state, t_state* state_local)
 {
     int nh = state_local->nhchainlength;
 
@@ -252,7 +249,7 @@ static void dd_distribute_state(gmx_domdec_t*                dd,
     /* communicate df_history -- required for restarting from checkpoint */
     dd_distribute_dfhist(dd, state_local->dfhist);
 
-    dd_resize_state(state_local, f, dd->comm->atomRanges.numHomeAtoms());
+    state_change_natoms(state_local, dd->comm->atomRanges.numHomeAtoms());
 
     if (state_local->flags & (1 << estX))
     {
@@ -310,7 +307,7 @@ static inline int computeAtomGroupDomainIndex(const gmx_domdec_t& dd,
         if (d < dd.unitCellInfo.npbcdim)
         {
             bool bScrew = (dd.unitCellInfo.haveScrewPBC && d == XX);
-            if (ddbox.tric_dir[d] && dd.nc[d] > 1)
+            if (ddbox.tric_dir[d] && dd.numCells[d] > 1)
             {
                 /* Use triclinic coordinates for this dimension */
                 for (int j = d + 1; j < DIM; j++)
@@ -359,13 +356,13 @@ static inline int computeAtomGroupDomainIndex(const gmx_domdec_t& dd,
         }
         /* This could be done more efficiently */
         ind[d] = 0;
-        while (ind[d] + 1 < dd.nc[d] && pos_d >= cellBoundaries[d][ind[d] + 1])
+        while (ind[d] + 1 < dd.numCells[d] && pos_d >= cellBoundaries[d][ind[d] + 1])
         {
             ind[d]++;
         }
     }
 
-    return dd_index(dd.nc, ind);
+    return dd_index(dd.numCells, ind);
 }
 
 
@@ -552,17 +549,16 @@ static void distributeAtomGroups(const gmx::MDLogger& mdlog,
     }
 }
 
-void distributeState(const gmx::MDLogger&         mdlog,
-                     gmx_domdec_t*                dd,
-                     const gmx_mtop_t&            mtop,
-                     t_state*                     state_global,
-                     const gmx_ddbox_t&           ddbox,
-                     t_state*                     state_local,
-                     PaddedHostVector<gmx::RVec>* f)
+void distributeState(const gmx::MDLogger& mdlog,
+                     gmx_domdec_t*        dd,
+                     const gmx_mtop_t&    mtop,
+                     t_state*             state_global,
+                     const gmx_ddbox_t&   ddbox,
+                     t_state*             state_local)
 {
     rvec* xGlobal = (DDMASTER(dd) ? state_global->x.rvec_array() : nullptr);
 
     distributeAtomGroups(mdlog, dd, mtop, DDMASTER(dd) ? state_global->box : nullptr, &ddbox, xGlobal);
 
-    dd_distribute_state(dd, state_global, state_local, f);
+    dd_distribute_state(dd, state_global, state_local);
 }
index 7dc1a7b53d3b6d29a6018eb6e3791ac2999898ae..7012f5bd600020e9543ea75b37864809c45e2b3d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -42,7 +42,6 @@
 #ifndef GMX_DOMDEC_DOMDEC_DISTRIBUTE_H
 #define GMX_DOMDEC_DOMDEC_DISTRIBUTE_H
 
-#include "gromacs/gpu_utils/hostallocator.h"
 #include "gromacs/utility/basedefinitions.h"
 
 struct gmx_ddbox_t;
@@ -57,12 +56,11 @@ class MDLogger;
 }
 
 /*! \brief Distributes the state from the master rank to all DD ranks */
-void distributeState(const gmx::MDLogger&              mdlog,
-                     gmx_domdec_t*                     dd,
-                     const gmx_mtop_t&                 mtop,
-                     t_state*                          state_global,
-                     const gmx_ddbox_t&                ddbox,
-                     t_state*                          state_local,
-                     gmx::PaddedHostVector<gmx::RVec>* f);
+void distributeState(const gmx::MDLogger& mdlog,
+                     gmx_domdec_t*        dd,
+                     const gmx_mtop_t&    mtop,
+                     t_state*             state_global,
+                     const gmx_ddbox_t&   ddbox,
+                     t_state*             state_local);
 
 #endif
index 542275d91681b6b421ecdbce51c13e29d8dd55ab..d3ba5aba08ef2f8e997bb988ab5baab865303824 100644 (file)
@@ -64,9 +64,9 @@
 #include "gromacs/domdec/partition.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
+#include "gromacs/gpu_utils/device_stream_manager.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/hardware/hw_info.h"
-#include "gromacs/listed_forces/manage_threading.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdlib/calc_verletbuf.h"
@@ -145,18 +145,6 @@ static const int ddNonbondedZonePairRanges[DD_MAXIZONE][3] = { { 0, 0, 8 },
                                                                { 2, 5, 6 },
                                                                { 3, 5, 7 } };
 
-
-/*
-   #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]);
-   }
- */
-
 static void ddindex2xyz(const ivec nc, int ind, ivec xyz)
 {
     xyz[XX] = ind / (nc[YY] * nc[ZZ]);
@@ -169,7 +157,7 @@ static int ddcoord2ddnodeid(gmx_domdec_t* dd, ivec c)
     int ddnodeid = -1;
 
     const CartesianRankSetup& cartSetup = dd->comm->cartesianRankSetup;
-    const int                 ddindex   = dd_index(dd->nc, c);
+    const int                 ddindex   = dd_index(dd->numCells, c);
     if (cartSetup.bCartesianPP_PME)
     {
         ddnodeid = cartSetup.ddindex2ddnodeid[ddindex];
@@ -703,24 +691,11 @@ static int gmx_ddcoord2pmeindex(const t_commrec* cr, int x, int y, int z)
     ivec          coords;
     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;
-       }
-     */
+    dd         = cr->dd;
     coords[XX] = x;
     coords[YY] = y;
     coords[ZZ] = z;
-    slab       = ddindex2pmeindex(dd->comm->ddRankSetup, dd_index(dd->nc, coords));
+    slab       = ddindex2pmeindex(dd->comm->ddRankSetup, dd_index(dd->numCells, coords));
 
     return slab;
 }
@@ -739,7 +714,7 @@ static int ddcoord2simnodeid(const t_commrec* cr, int x, int y, int z)
     }
     else
     {
-        int ddindex = dd_index(cr->dd->nc, coords);
+        int ddindex = dd_index(cr->dd->numCells, coords);
         if (cartSetup.bCartesianPP)
         {
             nodeid = cartSetup.ddindex2simnodeid[ddindex];
@@ -886,7 +861,7 @@ static gmx_bool receive_vir_ener(const gmx_domdec_t* dd, gmx::ArrayRef<const int
             ivec coords;
             MPI_Cart_coords(cr->mpi_comm_mysim, cr->sim_nodeid, DIM, coords);
             coords[cartSetup.cartpmedim]++;
-            if (coords[cartSetup.cartpmedim] < dd->nc[cartSetup.cartpmedim])
+            if (coords[cartSetup.cartpmedim] < dd->numCells[cartSetup.cartpmedim])
             {
                 int rank;
                 MPI_Cart_rank(cr->mpi_comm_mysim, coords, &rank);
@@ -924,9 +899,9 @@ static void set_slb_pme_dim_f(gmx_domdec_t* dd, int dim, real** dim_f)
 
     comm = dd->comm;
 
-    snew(*dim_f, dd->nc[dim] + 1);
+    snew(*dim_f, dd->numCells[dim] + 1);
     (*dim_f)[0] = 0;
-    for (i = 1; i < dd->nc[dim]; i++)
+    for (i = 1; i < dd->numCells[dim]; i++)
     {
         if (comm->slb_frac[dim])
         {
@@ -934,10 +909,10 @@ static void set_slb_pme_dim_f(gmx_domdec_t* dd, int dim, real** dim_f)
         }
         else
         {
-            (*dim_f)[i] = static_cast<real>(i) / static_cast<real>(dd->nc[dim]);
+            (*dim_f)[i] = static_cast<real>(i) / static_cast<real>(dd->numCells[dim]);
         }
     }
-    (*dim_f)[dd->nc[dim]] = 1;
+    (*dim_f)[dd->numCells[dim]] = 1;
 }
 
 static void init_ddpme(gmx_domdec_t* dd, gmx_ddpme_t* ddpme, int dimind)
@@ -967,13 +942,13 @@ static void init_ddpme(gmx_domdec_t* dd, gmx_ddpme_t* ddpme, int dimind)
     snew(ddpme->pp_max, ddpme->nslab);
     for (int slab = 0; slab < ddpme->nslab; slab++)
     {
-        ddpme->pp_min[slab] = dd->nc[dd->dim[dimind]] - 1;
+        ddpme->pp_min[slab] = dd->numCells[dd->dim[dimind]] - 1;
         ddpme->pp_max[slab] = 0;
     }
     for (int i = 0; i < dd->nnodes; i++)
     {
         ivec xyz;
-        ddindex2xyz(dd->nc, i, xyz);
+        ddindex2xyz(dd->numCells, i, xyz);
         /* For y only use our y/z slab.
          * This assumes that the PME x grid size matches the DD grid size.
          */
@@ -1069,10 +1044,10 @@ static void make_load_communicator(gmx_domdec_t* dd, int dim_ind, ivec loc)
 
     dim = dd->dim[dim_ind];
     copy_ivec(loc, loc_c);
-    for (i = 0; i < dd->nc[dim]; i++)
+    for (i = 0; i < dd->numCells[dim]; i++)
     {
         loc_c[dim] = i;
-        rank       = dd_index(dd->nc, loc_c);
+        rank       = dd_index(dd->numCells, loc_c);
         if (rank == dd->rank)
         {
             /* This process is part of the group */
@@ -1094,13 +1069,13 @@ static void make_load_communicator(gmx_domdec_t* dd, int dim_ind, ivec loc)
 
                 RowMaster& rowMaster = *cellsizes.rowMaster;
                 rowMaster.cellFrac.resize(ddCellFractionBufferSize(dd, dim_ind));
-                rowMaster.oldCellFrac.resize(dd->nc[dim] + 1);
-                rowMaster.isCellMin.resize(dd->nc[dim]);
+                rowMaster.oldCellFrac.resize(dd->numCells[dim] + 1);
+                rowMaster.isCellMin.resize(dd->numCells[dim]);
                 if (dim_ind > 0)
                 {
-                    rowMaster.bounds.resize(dd->nc[dim]);
+                    rowMaster.bounds.resize(dd->numCells[dim]);
                 }
-                rowMaster.buf_ncd.resize(dd->nc[dim]);
+                rowMaster.buf_ncd.resize(dd->numCells[dim]);
             }
             else
             {
@@ -1110,7 +1085,7 @@ static void make_load_communicator(gmx_domdec_t* dd, int dim_ind, ivec loc)
         }
         if (dd->ci[dim] == dd->master_ci[dim])
         {
-            snew(dd->comm->load[dim_ind].load, dd->nc[dim] * DD_NLOAD_MAX);
+            snew(dd->comm->load[dim_ind].load, dd->numCells[dim] * DD_NLOAD_MAX);
         }
     }
 }
@@ -1193,7 +1168,7 @@ static void make_load_communicators(gmx_domdec_t gmx_unused* dd)
     if (dd->ndim > 1)
     {
         dim0 = dd->dim[0];
-        for (i = 0; i < dd->nc[dim0]; i++)
+        for (i = 0; i < dd->numCells[dim0]; i++)
         {
             loc[dim0] = i;
             make_load_communicator(dd, 1, loc);
@@ -1202,11 +1177,11 @@ static void make_load_communicators(gmx_domdec_t gmx_unused* dd)
     if (dd->ndim > 2)
     {
         dim0 = dd->dim[0];
-        for (i = 0; i < dd->nc[dim0]; i++)
+        for (i = 0; i < dd->numCells[dim0]; i++)
         {
             loc[dim0] = i;
             dim1      = dd->dim[1];
-            for (j = 0; j < dd->nc[dim1]; j++)
+            for (j = 0; j < dd->numCells[dim1]; j++)
             {
                 loc[dim1] = j;
                 make_load_communicator(dd, 2, loc);
@@ -1233,10 +1208,10 @@ static void setup_neighbor_relations(gmx_domdec_t* dd)
     {
         dim = dd->dim[d];
         copy_ivec(dd->ci, tmp);
-        tmp[dim]           = (tmp[dim] + 1) % dd->nc[dim];
+        tmp[dim]           = (tmp[dim] + 1) % dd->numCells[dim];
         dd->neighbor[d][0] = ddcoord2ddnodeid(dd, tmp);
         copy_ivec(dd->ci, tmp);
-        tmp[dim]           = (tmp[dim] - 1 + dd->nc[dim]) % dd->nc[dim];
+        tmp[dim]           = (tmp[dim] - 1 + dd->numCells[dim]) % dd->numCells[dim];
         dd->neighbor[d][1] = ddcoord2ddnodeid(dd, tmp);
         if (debug)
         {
@@ -1269,11 +1244,11 @@ static void setup_neighbor_relations(gmx_domdec_t* dd)
             s[d] = dd->ci[d] - zones->shift[i][d];
             if (s[d] < 0)
             {
-                s[d] += dd->nc[d];
+                s[d] += dd->numCells[d];
             }
-            else if (s[d] >= dd->nc[d])
+            else if (s[d] >= dd->numCells[d])
             {
-                s[d] -= dd->nc[d];
+                s[d] -= dd->numCells[d];
             }
         }
     }
@@ -1292,7 +1267,7 @@ static void setup_neighbor_relations(gmx_domdec_t* dd)
                                            std::min(ddNonbondedZonePairRanges[iZoneIndex][2], nzone));
         for (dim = 0; dim < DIM; dim++)
         {
-            if (dd->nc[dim] == 1)
+            if (dd->numCells[dim] == 1)
             {
                 /* All shifts should be allowed */
                 iZone.shift0[dim] = -1;
@@ -1345,8 +1320,8 @@ static void make_pp_communicator(const gmx::MDLogger& mdlog,
     {
         /* Set up cartesian communication for the particle-particle part */
         GMX_LOG(mdlog.info)
-                .appendTextFormatted("Will use a Cartesian communicator: %d x %d x %d", dd->nc[XX],
-                                     dd->nc[YY], dd->nc[ZZ]);
+                .appendTextFormatted("Will use a Cartesian communicator: %d x %d x %d",
+                                     dd->numCells[XX], dd->numCells[YY], dd->numCells[ZZ]);
 
         ivec periods;
         for (int i = 0; i < DIM; i++)
@@ -1354,7 +1329,8 @@ static void make_pp_communicator(const gmx::MDLogger& mdlog,
             periods[i] = TRUE;
         }
         MPI_Comm comm_cart;
-        MPI_Cart_create(cr->mpi_comm_mygroup, DIM, dd->nc, periods, static_cast<int>(reorder), &comm_cart);
+        MPI_Cart_create(cr->mpi_comm_mygroup, DIM, dd->numCells, periods, static_cast<int>(reorder),
+                        &comm_cart);
         /* We overwrite the old communicator with the new cartesian one */
         cr->mpi_comm_mygroup = comm_cart;
     }
@@ -1368,7 +1344,7 @@ static void make_pp_communicator(const gmx::MDLogger& mdlog,
          * and not the one after split, we need to make an index.
          */
         cartSetup.ddindex2ddnodeid.resize(dd->nnodes);
-        cartSetup.ddindex2ddnodeid[dd_index(dd->nc, dd->ci)] = dd->rank;
+        cartSetup.ddindex2ddnodeid[dd_index(dd->numCells, dd->ci)] = dd->rank;
         gmx_sumi(dd->nnodes, cartSetup.ddindex2ddnodeid.data(), cr);
         /* Get the rank of the DD master,
          * above we made sure that the master node is a PP node.
@@ -1404,7 +1380,7 @@ static void make_pp_communicator(const gmx::MDLogger& mdlog,
         std::vector<int> buf(dd->nnodes);
         if (thisRankHasDuty(cr, DUTY_PP))
         {
-            buf[dd_index(dd->nc, dd->ci)] = cr->sim_nodeid;
+            buf[dd_index(dd->numCells, dd->ci)] = cr->sim_nodeid;
         }
         /* Communicate the ddindex to simulation nodeid index */
         MPI_Allreduce(buf.data(), cartSetup.ddindex2simnodeid.data(), dd->nnodes, MPI_INT, MPI_SUM,
@@ -1417,7 +1393,7 @@ static void make_pp_communicator(const gmx::MDLogger& mdlog,
         {
             if (cartSetup.ddindex2simnodeid[i] == 0)
             {
-                ddindex2xyz(dd->nc, i, dd->master_ci);
+                ddindex2xyz(dd->numCells, i, dd->master_ci);
                 MPI_Cart_rank(dd->mpi_comm_all, dd->master_ci, &dd->masterrank);
             }
         }
@@ -1430,7 +1406,7 @@ static void make_pp_communicator(const gmx::MDLogger& mdlog,
     {
         /* No Cartesian communicators */
         /* We use the rank in dd->comm->all as DD index */
-        ddindex2xyz(dd->nc, dd->rank, dd->ci);
+        ddindex2xyz(dd->numCells, 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);
@@ -1458,7 +1434,7 @@ static void receive_ddindex2simnodeid(gmx_domdec_t* dd, t_commrec* cr)
         std::vector<int> buf(dd->nnodes);
         if (thisRankHasDuty(cr, DUTY_PP))
         {
-            buf[dd_index(dd->nc, dd->ci)] = cr->sim_nodeid;
+            buf[dd_index(dd->numCells, dd->ci)] = cr->sim_nodeid;
         }
         /* Communicate the ddindex to simulation nodeid index */
         MPI_Allreduce(buf.data(), cartSetup.ddindex2simnodeid.data(), dd->nnodes, MPI_INT, MPI_SUM,
@@ -1637,6 +1613,13 @@ static CartesianRankSetup makeGroupCommunicators(const gmx::MDLogger& mdlog,
 {
     CartesianRankSetup cartSetup;
 
+    // As a default, both group and sim communicators are equal to the default communicator
+    cr->mpi_comm_mygroup = cr->mpiDefaultCommunicator;
+    cr->mpi_comm_mysim   = cr->mpiDefaultCommunicator;
+    cr->nnodes           = cr->sizeOfDefaultCommunicator;
+    cr->nodeid           = cr->rankInDefaultCommunicator;
+    cr->sim_nodeid       = cr->rankInDefaultCommunicator;
+
     if (ddRankSetup.usePmeOnlyRanks)
     {
         /* Split the communicator into a PP and PME part */
@@ -1647,8 +1630,6 @@ static CartesianRankSetup makeGroupCommunicators(const gmx::MDLogger& mdlog,
     {
         /* All nodes do PP and PME */
         /* We do not require separate communicators */
-        cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
-
         cartSetup.bCartesianPP     = false;
         cartSetup.bCartesianPP_PME = false;
     }
@@ -1706,7 +1687,7 @@ static void setupGroupCommunication(const gmx::MDLogger&     mdlog,
     /* We can not use DDMASTER(dd), because dd->masterrank is set later */
     if (MASTER(cr))
     {
-        dd->ma = std::make_unique<AtomDistribution>(dd->nc, numAtomsInSystem, numAtomsInSystem);
+        dd->ma = std::make_unique<AtomDistribution>(dd->numCells, numAtomsInSystem, numAtomsInSystem);
     }
 }
 
@@ -1789,10 +1770,11 @@ static int dd_getenv(const gmx::MDLogger& mdlog, const char* env_var, int def)
 
 static void check_dd_restrictions(const gmx_domdec_t* dd, const t_inputrec* ir, const gmx::MDLogger& mdlog)
 {
-    if (ir->ePBC == epbcSCREW && (dd->nc[XX] == 1 || dd->nc[YY] > 1 || dd->nc[ZZ] > 1))
+    if (ir->pbcType == PbcType::Screw
+        && (dd->numCells[XX] == 1 || dd->numCells[YY] > 1 || dd->numCells[ZZ] > 1))
     {
         gmx_fatal(FARGS, "With pbc=%s can only do domain decomposition in the x-direction",
-                  epbc_names[ir->ePBC]);
+                  c_pbcTypeNames[ir->pbcType].c_str());
     }
 
     if (ir->nstlist == 0)
@@ -1800,7 +1782,7 @@ static void check_dd_restrictions(const gmx_domdec_t* dd, const t_inputrec* ir,
         gmx_fatal(FARGS, "Domain decomposition does not work with nstlist=0");
     }
 
-    if (ir->comm_mode == ecmANGULAR && ir->ePBC != epbcNONE)
+    if (ir->comm_mode == ecmANGULAR && ir->pbcType != PbcType::No)
     {
         GMX_LOG(mdlog.warning)
                 .appendText(
@@ -2027,10 +2009,10 @@ static void setupUpdateGroups(const gmx::MDLogger& mdlog,
 }
 
 UnitCellInfo::UnitCellInfo(const t_inputrec& ir) :
-    npbcdim(ePBC2npbcdim(ir.ePBC)),
+    npbcdim(numPbcDimensions(ir.pbcType)),
     numBoundedDimensions(inputrec2nboundeddim(&ir)),
     ddBoxIsDynamic(numBoundedDimensions < DIM || inputrecDynamicBox(&ir)),
-    haveScrewPBC(ir.ePBC == epbcSCREW)
+    haveScrewPBC(ir.pbcType == PbcType::Screw)
 {
 }
 
@@ -2067,7 +2049,8 @@ static bool moleculesAreAlwaysWhole(const gmx_mtop_t&
 
 /*! \brief Generate the simulation system information */
 static DDSystemInfo getSystemInfo(const gmx::MDLogger&           mdlog,
-                                  const t_commrec*               cr,
+                                  DDRole                         ddRole,
+                                  MPI_Comm                       communicator,
                                   const DomdecOptions&           options,
                                   const gmx_mtop_t&              mtop,
                                   const t_inputrec&              ir,
@@ -2082,7 +2065,7 @@ static DDSystemInfo getSystemInfo(const gmx::MDLogger&           mdlog,
     systemInfo.useUpdateGroups = false;
     if (ir.cutoff_scheme == ecutsVERLET)
     {
-        real cutoffMargin = std::sqrt(max_cutoff2(ir.ePBC, box)) - ir.rlist;
+        real cutoffMargin = std::sqrt(max_cutoff2(ir.pbcType, box)) - ir.rlist;
         setupUpdateGroups(mdlog, mtop, ir, cutoffMargin, &systemInfo);
     }
 
@@ -2178,13 +2161,13 @@ static DDSystemInfo getSystemInfo(const gmx::MDLogger&           mdlog,
         {
             real r_2b, r_mb;
 
-            if (MASTER(cr))
+            if (ddRole == DDRole::Master)
             {
-                dd_bonded_cg_distance(mdlog, &mtop, &ir, as_rvec_array(xGlobal.data()), box,
+                dd_bonded_cg_distance(mdlog, &mtop, &ir, xGlobal, box,
                                       options.checkBondedInteractions, &r_2b, &r_mb);
             }
-            gmx_bcast(sizeof(r_2b), &r_2b, cr);
-            gmx_bcast(sizeof(r_mb), &r_mb, cr);
+            gmx_bcast(sizeof(r_2b), &r_2b, communicator);
+            gmx_bcast(sizeof(r_mb), &r_mb, communicator);
 
             /* We use an initial margin of 10% for the minimum cell size,
              * except when we are just below the non-bonded cut-off.
@@ -2258,7 +2241,9 @@ static DDSystemInfo getSystemInfo(const gmx::MDLogger&           mdlog,
 /*! \brief Exit with a fatal error if the DDGridSetup cannot be
  * implemented. */
 static void checkDDGridSetup(const DDGridSetup&   ddGridSetup,
-                             const t_commrec*     cr,
+                             DDRole               ddRole,
+                             MPI_Comm             communicator,
+                             int                  numNodes,
                              const DomdecOptions& options,
                              const DDSettings&    ddSettings,
                              const DDSystemInfo&  systemInfo,
@@ -2274,12 +2259,12 @@ static void checkDDGridSetup(const DDGridSetup&   ddGridSetup,
                 ddSettings.initialDlbState != DlbState::offUser ? " or -dds" : "",
                 bC ? " or your LINCS settings" : "");
 
-        gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
+        gmx_fatal_collective(FARGS, communicator, ddRole == DDRole::Master,
                              "There is no domain decomposition for %d ranks 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 - ddGridSetup.numPmeOnlyRanks, cellsizeLimit, buf);
+                             numNodes - ddGridSetup.numPmeOnlyRanks, cellsizeLimit, buf);
     }
 
     const real acs = average_cellsize_min(ddbox, ddGridSetup.numDomains);
@@ -2294,7 +2279,7 @@ static void checkDDGridSetup(const DDGridSetup&   ddGridSetup,
         else
         {
             gmx_fatal_collective(
-                    FARGS, cr->mpi_comm_mysim, MASTER(cr),
+                    FARGS, communicator, ddRole == DDRole::Master,
                     "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, cellsizeLimit);
@@ -2303,16 +2288,16 @@ static void checkDDGridSetup(const DDGridSetup&   ddGridSetup,
 
     const int numPPRanks =
             ddGridSetup.numDomains[XX] * ddGridSetup.numDomains[YY] * ddGridSetup.numDomains[ZZ];
-    if (cr->nnodes - numPPRanks != ddGridSetup.numPmeOnlyRanks)
+    if (numNodes - numPPRanks != ddGridSetup.numPmeOnlyRanks)
     {
-        gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
+        gmx_fatal_collective(FARGS, communicator, ddRole == DDRole::Master,
                              "The size of the domain decomposition grid (%d) does not match the "
                              "number of PP ranks (%d). The total number of ranks is %d",
-                             numPPRanks, cr->nnodes - ddGridSetup.numPmeOnlyRanks, cr->nnodes);
+                             numPPRanks, numNodes - ddGridSetup.numPmeOnlyRanks, numNodes);
     }
     if (ddGridSetup.numPmeOnlyRanks > numPPRanks)
     {
-        gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
+        gmx_fatal_collective(FARGS, communicator, ddRole == DDRole::Master,
                              "The number of separate PME ranks (%d) is larger than the number of "
                              "PP ranks (%d), this is not supported.",
                              ddGridSetup.numPmeOnlyRanks, numPPRanks);
@@ -2321,7 +2306,7 @@ static void checkDDGridSetup(const DDGridSetup&   ddGridSetup,
 
 /*! \brief Set the cell size and interaction limits, as well as the DD grid */
 static DDRankSetup getDDRankSetup(const gmx::MDLogger& mdlog,
-                                  t_commrec*           cr,
+                                  int                  numNodes,
                                   const DDGridSetup&   ddGridSetup,
                                   const t_inputrec&    ir)
 {
@@ -2332,7 +2317,7 @@ static DDRankSetup getDDRankSetup(const gmx::MDLogger& mdlog,
 
     DDRankSetup ddRankSetup;
 
-    ddRankSetup.numPPRanks = cr->nnodes - ddGridSetup.numPmeOnlyRanks;
+    ddRankSetup.numPPRanks = numNodes - ddGridSetup.numPmeOnlyRanks;
     copy_ivec(ddGridSetup.numDomains, ddRankSetup.numPPCells);
 
     ddRankSetup.usePmeOnlyRanks = (ddGridSetup.numPmeOnlyRanks > 0);
@@ -2399,7 +2384,7 @@ static DDRankSetup getDDRankSetup(const gmx::MDLogger& mdlog,
 
 /*! \brief Set the cell size and interaction limits */
 static void set_dd_limits(const gmx::MDLogger& mdlog,
-                          t_commrec*           cr,
+                          DDRole               ddRole,
                           gmx_domdec_t*        dd,
                           const DomdecOptions& options,
                           const DDSettings&    ddSettings,
@@ -2443,18 +2428,18 @@ static void set_dd_limits(const gmx::MDLogger& mdlog,
     }
 
     /* Set the DD setup given by ddGridSetup */
-    copy_ivec(ddGridSetup.numDomains, dd->nc);
+    copy_ivec(ddGridSetup.numDomains, dd->numCells);
     dd->ndim = ddGridSetup.numDDDimensions;
     copy_ivec(ddGridSetup.ddDimensions, dd->dim);
 
-    dd->nnodes = dd->nc[XX] * dd->nc[YY] * dd->nc[ZZ];
+    dd->nnodes = dd->numCells[XX] * dd->numCells[YY] * dd->numCells[ZZ];
 
     snew(comm->slb_frac, DIM);
     if (isDlbDisabled(comm))
     {
-        comm->slb_frac[XX] = get_slb_frac(mdlog, "x", dd->nc[XX], options.cellSizeX);
-        comm->slb_frac[YY] = get_slb_frac(mdlog, "y", dd->nc[YY], options.cellSizeY);
-        comm->slb_frac[ZZ] = get_slb_frac(mdlog, "z", dd->nc[ZZ], options.cellSizeZ);
+        comm->slb_frac[XX] = get_slb_frac(mdlog, "x", dd->numCells[XX], options.cellSizeX);
+        comm->slb_frac[YY] = get_slb_frac(mdlog, "y", dd->numCells[YY], options.cellSizeY);
+        comm->slb_frac[ZZ] = get_slb_frac(mdlog, "z", dd->numCells[ZZ], options.cellSizeZ);
     }
 
     /* Set the multi-body cut-off and cellsize limit for DLB */
@@ -2468,7 +2453,7 @@ static void set_dd_limits(const gmx::MDLogger& mdlog,
              * the minimum and the maximum,
              * since the extra communication cost is nearly zero.
              */
-            real acs           = average_cellsize_min(ddbox, dd->nc);
+            real acs           = average_cellsize_min(ddbox, dd->numCells);
             comm->cutoff_mbody = 0.5 * (systemInfo.minCutoffForMultiBody + acs);
             if (!isDlbDisabled(comm))
             {
@@ -2508,23 +2493,23 @@ static void set_dd_limits(const gmx::MDLogger& mdlog,
                 gmx::boolToString(systemInfo.filterBondedCommunication), comm->cellsize_limit);
     }
 
-    if (MASTER(cr))
+    if (ddRole == DDRole::Master)
     {
         check_dd_restrictions(dd, ir, mdlog);
     }
 }
 
-void dd_init_bondeds(FILE*              fplog,
-                     gmx_domdec_t*      dd,
-                     const gmx_mtop_t*  mtop,
-                     const gmx_vsite_t* vsite,
-                     const t_inputrec*  ir,
-                     gmx_bool           bBCheck,
-                     cginfo_mb_t*       cginfo_mb)
+void dd_init_bondeds(FILE*                           fplog,
+                     gmx_domdec_t*                   dd,
+                     const gmx_mtop_t&               mtop,
+                     const gmx::VirtualSitesHandler* vsite,
+                     const t_inputrec*               ir,
+                     gmx_bool                        bBCheck,
+                     gmx::ArrayRef<cginfo_mb_t>      cginfo_mb)
 {
     gmx_domdec_comm_t* comm;
 
-    dd_make_reverse_top(fplog, dd, mtop, vsite, ir, bBCheck);
+    dd_make_reverse_top(fplog, dd, &mtop, vsite, ir, bBCheck);
 
     comm = dd->comm;
 
@@ -2569,16 +2554,16 @@ static void writeSettings(gmx::TextWriter*   log,
         log->writeString("The allowed shrink of domain decomposition cells is:");
         for (d = 0; d < DIM; d++)
         {
-            if (dd->nc[d] > 1)
+            if (dd->numCells[d] > 1)
             {
-                if (d >= ddbox->npbcdim && dd->nc[d] == 2)
+                if (d >= ddbox->npbcdim && dd->numCells[d] == 2)
                 {
                     shrink = 0;
                 }
                 else
                 {
                     shrink = comm->cellsize_min_dlb[d]
-                             / (ddbox->box_size[d] * ddbox->skew_fac[d] / dd->nc[d]);
+                             / (ddbox->box_size[d] * ddbox->skew_fac[d] / dd->numCells[d]);
                 }
                 log->writeStringFormatted(" %c %.2f", dim2char(d), shrink);
             }
@@ -2597,7 +2582,7 @@ static void writeSettings(gmx::TextWriter*   log,
         log->writeString("The initial domain decomposition cell size is:");
         for (d = 0; d < DIM; d++)
         {
-            if (dd->nc[d] > 1)
+            if (dd->numCells[d] > 1)
             {
                 log->writeStringFormatted(" %c %.2f nm", dim2char(d), dd->comm->cellsize_min[d]);
             }
@@ -2729,7 +2714,7 @@ static void set_cell_limits_dlb(const gmx::MDLogger& mdlog,
     else
     {
         /* There is no cell size limit */
-        npulse = std::max(dd->nc[XX] - 1, std::max(dd->nc[YY] - 1, dd->nc[ZZ] - 1));
+        npulse = std::max(dd->numCells[XX] - 1, std::max(dd->numCells[YY] - 1, dd->numCells[ZZ] - 1));
     }
 
     if (!bNoCutOff && npulse > 1)
@@ -2741,7 +2726,7 @@ static void set_cell_limits_dlb(const gmx::MDLogger& mdlog,
             dim      = dd->dim[d];
             npulse_d = static_cast<int>(
                     1
-                    + dd->nc[dim] * comm->systemInfo.cutoff
+                    + dd->numCells[dim] * comm->systemInfo.cutoff
                               / (ddbox->box_size[dim] * ddbox->skew_fac[dim] * dlb_scale));
             npulse_d_max = std::max(npulse_d_max, npulse_d);
         }
@@ -2756,19 +2741,12 @@ static void set_cell_limits_dlb(const gmx::MDLogger& mdlog,
     }
 
     comm->maxpulse       = 1;
-    comm->bVacDLBNoLimit = (ir->ePBC == epbcNONE);
+    comm->bVacDLBNoLimit = (ir->pbcType == PbcType::No);
     for (d = 0; d < dd->ndim; d++)
     {
-        if (comm->ddSettings.request1DAnd1Pulse)
-        {
-            comm->cd[d].np_dlb = 1;
-        }
-        else
-        {
-            comm->cd[d].np_dlb = std::min(npulse, dd->nc[dd->dim[d]] - 1);
-            comm->maxpulse     = std::max(comm->maxpulse, comm->cd[d].np_dlb);
-        }
-        if (comm->cd[d].np_dlb < dd->nc[dd->dim[d]] - 1)
+        comm->cd[d].np_dlb = std::min(npulse, dd->numCells[dd->dim[d]] - 1);
+        comm->maxpulse     = std::max(comm->maxpulse, comm->cd[d].np_dlb);
+        if (comm->cd[d].np_dlb < dd->numCells[dd->dim[d]] - 1)
         {
             comm->bVacDLBNoLimit = FALSE;
         }
@@ -2807,14 +2785,15 @@ bool dd_moleculesAreAlwaysWhole(const gmx_domdec_t& dd)
     return dd.comm->systemInfo.moleculesAreAlwaysWhole;
 }
 
-gmx_bool dd_bonded_molpbc(const gmx_domdec_t* dd, int ePBC)
+gmx_bool dd_bonded_molpbc(const gmx_domdec_t* dd, PbcType pbcType)
 {
     /* 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.
      */
-    return (ePBC != epbcNONE && dd->comm->systemInfo.haveInterDomainBondeds
-            && !(dd->nc[XX] > 1 && dd->nc[YY] > 1 && (dd->nc[ZZ] > 1 || ePBC == epbcXY)));
+    return (pbcType != PbcType::No && dd->comm->systemInfo.haveInterDomainBondeds
+            && !(dd->numCells[XX] > 1 && dd->numCells[YY] > 1
+                 && (dd->numCells[ZZ] > 1 || pbcType == PbcType::XY)));
 }
 
 /*! \brief Sets grid size limits and PP-PME setup, prints settings to log */
@@ -2858,13 +2837,13 @@ static void set_ddgrid_parameters(const gmx::MDLogger& mdlog,
     logSettings(mdlog, dd, mtop, ir, dlb_scale, ddbox);
 
     real vol_frac;
-    if (ir->ePBC == epbcNONE)
+    if (ir->pbcType == PbcType::No)
     {
         vol_frac = 1 - 1 / static_cast<double>(dd->nnodes);
     }
     else
     {
-        vol_frac = (1 + comm_box_frac(dd->nc, comm->systemInfo.cutoff, *ddbox))
+        vol_frac = (1 + comm_box_frac(dd->numCells, comm->systemInfo.cutoff, *ddbox))
                    / static_cast<double>(dd->nnodes);
     }
     if (debug)
@@ -2886,7 +2865,6 @@ static DDSettings getDDSettings(const gmx::MDLogger&     mdlog,
 
     ddSettings.useSendRecv2        = (dd_getenv(mdlog, "GMX_DD_USE_SENDRECV2", 0) != 0);
     ddSettings.dlb_scale_lim       = dd_getenv(mdlog, "GMX_DLB_MAX_BOX_SCALING", 10);
-    ddSettings.request1DAnd1Pulse  = bool(dd_getenv(mdlog, "GMX_DD_1D_1PULSE", 0));
     ddSettings.useDDOrderZYX       = bool(dd_getenv(mdlog, "GMX_DD_ORDER_ZYX", 0));
     ddSettings.useCartesianReorder = bool(dd_getenv(mdlog, "GMX_NO_CART_REORDER", 1));
     ddSettings.eFlop               = dd_getenv(mdlog, "GMX_DLB_BASED_ON_FLOPS", 0);
@@ -2925,65 +2903,6 @@ static DDSettings getDDSettings(const gmx::MDLogger&     mdlog,
 
 gmx_domdec_t::gmx_domdec_t(const t_inputrec& ir) : unitCellInfo(ir) {}
 
-/*! \brief Return whether the simulation described can run a 1D single-pulse DD.
- *
- * The GPU halo exchange code requires a 1D single-pulse DD. Such a DD
- * generally requires a larger box than other possible decompositions
- * with the same rank count, so the calling code might need to decide
- * what is the most appropriate way to run the simulation based on
- * whether such a DD is possible.
- *
- * This function works like init_domain_decomposition(), but will not
- * give a fatal error, and only uses \c cr for communicating between
- * ranks.
- *
- * It is safe to call before thread-MPI spawns ranks, so that
- * thread-MPI can decide whether and how to trigger the GPU halo
- * exchange code path. The number of PME ranks, if any, should be set
- * in \c options.numPmeRanks.
- */
-static bool canMake1DAnd1PulseDomainDecomposition(const DDSettings&              ddSettingsOriginal,
-                                                  const t_commrec*               cr,
-                                                  const int                      numRanksRequested,
-                                                  const DomdecOptions&           options,
-                                                  const gmx_mtop_t&              mtop,
-                                                  const t_inputrec&              ir,
-                                                  const matrix                   box,
-                                                  gmx::ArrayRef<const gmx::RVec> xGlobal)
-{
-    // Ensure we don't write any output from this checking routine
-    gmx::MDLogger dummyLogger;
-
-    DDSystemInfo systemInfo = getSystemInfo(dummyLogger, cr, options, mtop, ir, box, xGlobal);
-
-    DDSettings ddSettings             = ddSettingsOriginal;
-    ddSettings.request1DAnd1Pulse     = true;
-    const real gridSetupCellsizeLimit = getDDGridSetupCellSizeLimit(
-            dummyLogger, ddSettings.request1DAnd1Pulse, !isDlbDisabled(ddSettings.initialDlbState),
-            options.dlbScaling, ir, systemInfo.cellsizeLimit);
-    gmx_ddbox_t ddbox = { 0 };
-    DDGridSetup ddGridSetup =
-            getDDGridSetup(dummyLogger, cr, numRanksRequested, options, ddSettings, systemInfo,
-                           gridSetupCellsizeLimit, mtop, ir, box, xGlobal, &ddbox);
-
-    const bool canMakeDDWith1DAnd1Pulse = (ddGridSetup.numDomains[XX] != 0);
-
-    return canMakeDDWith1DAnd1Pulse;
-}
-
-bool is1DAnd1PulseDD(const gmx_domdec_t& dd)
-{
-    const int  maxDimensionSize             = std::max(dd.nc[XX], std::max(dd.nc[YY], dd.nc[ZZ]));
-    const int  productOfDimensionSizes      = dd.nc[XX] * dd.nc[YY] * dd.nc[ZZ];
-    const bool decompositionHasOneDimension = (maxDimensionSize == productOfDimensionSizes);
-
-    const bool hasMax1Pulse =
-            ((isDlbDisabled(dd.comm) && dd.comm->cellsize_limit >= dd.comm->systemInfo.cutoff)
-             || (!isDlbDisabled(dd.comm) && dd.comm->maxpulse == 1));
-
-    return decompositionHasOneDimension && hasMax1Pulse;
-}
-
 namespace gmx
 {
 
@@ -2998,7 +2917,6 @@ public:
          t_commrec*           cr,
          const DomdecOptions& options,
          const MdrunOptions&  mdrunOptions,
-         bool                 prefer1DAnd1Pulse,
          const gmx_mtop_t&    mtop,
          const t_inputrec&    ir,
          const matrix         box,
@@ -3046,7 +2964,6 @@ DomainDecompositionBuilder::Impl::Impl(const MDLogger&      mdlog,
                                        t_commrec*           cr,
                                        const DomdecOptions& options,
                                        const MdrunOptions&  mdrunOptions,
-                                       const bool           prefer1DAnd1Pulse,
                                        const gmx_mtop_t&    mtop,
                                        const t_inputrec&    ir,
                                        const matrix         box,
@@ -3057,26 +2974,20 @@ DomainDecompositionBuilder::Impl::Impl(const MDLogger&      mdlog,
     mtop_(mtop),
     ir_(ir)
 {
-    GMX_LOG(mdlog_.info).appendTextFormatted("\nInitializing Domain Decomposition on %d ranks", cr_->nnodes);
+    GMX_LOG(mdlog_.info).appendTextFormatted("\nInitializing Domain Decomposition on %d ranks", cr_->sizeOfDefaultCommunicator);
 
     ddSettings_ = getDDSettings(mdlog_, options_, mdrunOptions, ir_);
 
-    if (prefer1DAnd1Pulse
-        && canMake1DAnd1PulseDomainDecomposition(ddSettings_, cr_, cr_->nnodes, options_, mtop_,
-                                                 ir_, box, xGlobal))
-    {
-        ddSettings_.request1DAnd1Pulse = true;
-    }
-
     if (ddSettings_.eFlop > 1)
     {
         /* Ensure that we have different random flop counts on different ranks */
-        srand(1 + cr_->nodeid);
+        srand(1 + cr_->rankInDefaultCommunicator);
     }
 
-    systemInfo_ = getSystemInfo(mdlog_, cr_, options_, mtop_, ir_, box, xGlobal);
+    systemInfo_ = getSystemInfo(mdlog_, MASTER(cr_) ? DDRole::Master : DDRole::Agent,
+                                cr->mpiDefaultCommunicator, options_, mtop_, ir_, box, xGlobal);
 
-    const int  numRanksRequested         = cr_->nnodes;
+    const int  numRanksRequested         = cr_->sizeOfDefaultCommunicator;
     const bool checkForLargePrimeFactors = (options_.numCells[0] <= 0);
     checkForValidRankCountRequests(numRanksRequested, EEL_PME(ir_.coulombtype),
                                    options_.numPmeRanks, checkForLargePrimeFactors);
@@ -3084,16 +2995,20 @@ DomainDecompositionBuilder::Impl::Impl(const MDLogger&      mdlog,
     // DD grid setup uses a more different cell size limit for
     // automated setup than the one in systemInfo_. The latter is used
     // in set_dd_limits() to configure DLB, for example.
-    const real gridSetupCellsizeLimit = getDDGridSetupCellSizeLimit(
-            mdlog_, ddSettings_.request1DAnd1Pulse, !isDlbDisabled(ddSettings_.initialDlbState),
-            options_.dlbScaling, ir_, systemInfo_.cellsizeLimit);
-    ddGridSetup_ = getDDGridSetup(mdlog_, cr_, numRanksRequested, options_, ddSettings_, systemInfo_,
-                                  gridSetupCellsizeLimit, mtop_, ir_, box, xGlobal, &ddbox_);
-    checkDDGridSetup(ddGridSetup_, cr_, options_, ddSettings_, systemInfo_, gridSetupCellsizeLimit, ddbox_);
+    const real gridSetupCellsizeLimit =
+            getDDGridSetupCellSizeLimit(mdlog_, !isDlbDisabled(ddSettings_.initialDlbState),
+                                        options_.dlbScaling, ir_, systemInfo_.cellsizeLimit);
+    ddGridSetup_ =
+            getDDGridSetup(mdlog_, MASTER(cr_) ? DDRole::Master : DDRole::Agent,
+                           cr->mpiDefaultCommunicator, numRanksRequested, options_, ddSettings_,
+                           systemInfo_, gridSetupCellsizeLimit, mtop_, ir_, box, xGlobal, &ddbox_);
+    checkDDGridSetup(ddGridSetup_, MASTER(cr_) ? DDRole::Master : DDRole::Agent,
+                     cr->mpiDefaultCommunicator, cr->sizeOfDefaultCommunicator, options_,
+                     ddSettings_, systemInfo_, gridSetupCellsizeLimit, ddbox_);
 
     cr_->npmenodes = ddGridSetup_.numPmeOnlyRanks;
 
-    ddRankSetup_ = getDDRankSetup(mdlog_, cr_, ddGridSetup_, ir_);
+    ddRankSetup_ = getDDRankSetup(mdlog_, cr_->sizeOfDefaultCommunicator, ddGridSetup_, ir_);
 
     /* Generate the group communicator, also decides the duty of each rank */
     cartSetup_ = makeGroupCommunicators(mdlog_, ddSettings_, options_.rankOrder, ddRankSetup_, cr_,
@@ -3111,8 +3026,8 @@ gmx_domdec_t* DomainDecompositionBuilder::Impl::build(LocalAtomSetManager* atomS
     dd->comm->ddRankSetup        = ddRankSetup_;
     dd->comm->cartesianRankSetup = cartSetup_;
 
-    set_dd_limits(mdlog_, cr_, dd, options_, ddSettings_, systemInfo_, ddGridSetup_,
-                  ddRankSetup_.numPPRanks, &mtop_, &ir_, ddbox_);
+    set_dd_limits(mdlog_, MASTER(cr_) ? DDRole::Master : DDRole::Agent, dd, options_, ddSettings_,
+                  systemInfo_, ddGridSetup_, ddRankSetup_.numPPRanks, &mtop_, &ir_, ddbox_);
 
     setupGroupCommunication(mdlog_, ddSettings_, pmeRanks_, cr_, mtop_.natoms, dd);
 
@@ -3135,12 +3050,11 @@ DomainDecompositionBuilder::DomainDecompositionBuilder(const MDLogger&      mdlo
                                                        t_commrec*           cr,
                                                        const DomdecOptions& options,
                                                        const MdrunOptions&  mdrunOptions,
-                                                       const bool           prefer1DAnd1Pulse,
                                                        const gmx_mtop_t&    mtop,
                                                        const t_inputrec&    ir,
                                                        const matrix         box,
                                                        ArrayRef<const RVec> xGlobal) :
-    impl_(new Impl(mdlog, cr, options, mdrunOptions, prefer1DAnd1Pulse, mtop, ir, box, xGlobal))
+    impl_(new Impl(mdlog, cr, options, mdrunOptions, mtop, ir, box, xGlobal))
 {
 }
 
@@ -3153,15 +3067,14 @@ DomainDecompositionBuilder::~DomainDecompositionBuilder() = default;
 
 } // namespace gmx
 
-static gmx_bool test_dd_cutoff(t_commrec* cr, const matrix box, gmx::ArrayRef<const gmx::RVec> x, real cutoffRequested)
+static gmx_bool test_dd_cutoff(const t_commrec* cr, const matrix box, gmx::ArrayRef<const gmx::RVec> x, real cutoffRequested)
 {
-    gmx_domdec_t* dd;
-    gmx_ddbox_t   ddbox;
-    int           d, dim, np;
-    real          inv_cell_size;
-    int           LocallyLimited;
+    gmx_ddbox_t ddbox;
+    int         d, dim, np;
+    real        inv_cell_size;
+    int         LocallyLimited;
 
-    dd = cr->dd;
+    const auto* dd = cr->dd;
 
     set_ddbox(*dd, false, box, true, x, &ddbox);
 
@@ -3171,7 +3084,7 @@ static gmx_bool test_dd_cutoff(t_commrec* cr, const matrix box, gmx::ArrayRef<co
     {
         dim = dd->dim[d];
 
-        inv_cell_size = DD_CELL_MARGIN * dd->nc[dim] / ddbox.box_size[dim];
+        inv_cell_size = DD_CELL_MARGIN * dd->numCells[dim] / ddbox.box_size[dim];
         if (dd->unitCellInfo.ddBoxIsDynamic)
         {
             inv_cell_size *= DD_PRES_SCALE_MARGIN;
@@ -3179,11 +3092,6 @@ static gmx_bool test_dd_cutoff(t_commrec* cr, const matrix box, gmx::ArrayRef<co
 
         np = 1 + static_cast<int>(cutoffRequested * inv_cell_size * ddbox.skew_fac[dim]);
 
-        if (dd->comm->ddSettings.request1DAnd1Pulse && np > 1)
-        {
-            return FALSE;
-        }
-
         if (!isDlbDisabled(dd->comm) && (dim < ddbox.npbcdim) && (dd->comm->cd[d].np_dlb > 0))
         {
             if (np > dd->comm->cd[d].np_dlb)
@@ -3238,3 +3146,74 @@ gmx_bool change_dd_cutoff(t_commrec* cr, const matrix box, gmx::ArrayRef<const g
 
     return bCutoffAllowed;
 }
+
+void constructGpuHaloExchange(const gmx::MDLogger&            mdlog,
+                              const t_commrec&                cr,
+                              const gmx::DeviceStreamManager& deviceStreamManager,
+                              gmx_wallcycle*                  wcycle)
+{
+    GMX_RELEASE_ASSERT(deviceStreamManager.streamIsValid(gmx::DeviceStreamType::NonBondedLocal),
+                       "Local non-bonded stream should be valid when using"
+                       "GPU halo exchange.");
+    GMX_RELEASE_ASSERT(deviceStreamManager.streamIsValid(gmx::DeviceStreamType::NonBondedNonLocal),
+                       "Non-local non-bonded stream should be valid when using "
+                       "GPU halo exchange.");
+
+    if (cr.dd->gpuHaloExchange[0].empty())
+    {
+        GMX_LOG(mdlog.warning)
+                .asParagraph()
+                .appendTextFormatted(
+                        "NOTE: Activating the 'GPU halo exchange' feature, enabled "
+                        "by the "
+                        "GMX_GPU_DD_COMMS environment variable.");
+    }
+
+    for (int d = 0; d < cr.dd->ndim; d++)
+    {
+        for (int pulse = cr.dd->gpuHaloExchange[d].size(); pulse < cr.dd->comm->cd[d].numPulses(); pulse++)
+        {
+            cr.dd->gpuHaloExchange[d].push_back(std::make_unique<gmx::GpuHaloExchange>(
+                    cr.dd, d, cr.mpi_comm_mysim, deviceStreamManager.context(),
+                    deviceStreamManager.stream(gmx::DeviceStreamType::NonBondedLocal),
+                    deviceStreamManager.stream(gmx::DeviceStreamType::NonBondedNonLocal), pulse, wcycle));
+        }
+    }
+}
+
+void reinitGpuHaloExchange(const t_commrec&              cr,
+                           const DeviceBuffer<gmx::RVec> d_coordinatesBuffer,
+                           const DeviceBuffer<gmx::RVec> d_forcesBuffer)
+{
+    for (int d = 0; d < cr.dd->ndim; d++)
+    {
+        for (int pulse = 0; pulse < cr.dd->comm->cd[d].numPulses(); pulse++)
+        {
+            cr.dd->gpuHaloExchange[d][pulse]->reinitHalo(d_coordinatesBuffer, d_forcesBuffer);
+        }
+    }
+}
+
+void communicateGpuHaloCoordinates(const t_commrec&      cr,
+                                   const matrix          box,
+                                   GpuEventSynchronizer* coordinatesReadyOnDeviceEvent)
+{
+    for (int d = 0; d < cr.dd->ndim; d++)
+    {
+        for (int pulse = 0; pulse < cr.dd->comm->cd[d].numPulses(); pulse++)
+        {
+            cr.dd->gpuHaloExchange[d][pulse]->communicateHaloCoordinates(box, coordinatesReadyOnDeviceEvent);
+        }
+    }
+}
+
+void communicateGpuHaloForces(const t_commrec& cr, bool accumulateForces)
+{
+    for (int d = cr.dd->ndim - 1; d >= 0; d--)
+    {
+        for (int pulse = cr.dd->comm->cd[d].numPulses() - 1; pulse >= 0; pulse--)
+        {
+            cr.dd->gpuHaloExchange[d][pulse]->communicateHaloForces(accumulateForces);
+        }
+    }
+}
index 9fb46bc0608c722f5b136c8462a7c108beb2a900..2b65e1989b822eb638f3375b86f29fe0d1f2c681 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2005 - 2014, The GROMACS development team.
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019 by the GROMACS development team.
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -61,6 +62,7 @@
 
 #include <vector>
 
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
@@ -72,7 +74,6 @@ struct gmx_ddbox_t;
 struct gmx_domdec_zones_t;
 struct gmx_localtop_t;
 struct gmx_mtop_t;
-struct gmx_vsite_t;
 struct t_block;
 struct t_blocka;
 struct t_commrec;
@@ -81,13 +82,18 @@ struct t_inputrec;
 struct t_mdatoms;
 struct t_nrnb;
 struct gmx_wallcycle;
+enum class PbcType : int;
 class t_state;
+class DeviceContext;
+class GpuEventSynchronizer;
 
 namespace gmx
 {
+class DeviceStreamManager;
 class ForceWithShiftForces;
 class MDLogger;
 class RangePartitioning;
+class VirtualSitesHandler;
 } // namespace gmx
 
 /*! \brief Returns the global topology atom number belonging to local atom index i.
@@ -150,27 +156,20 @@ bool ddHaveSplitConstraints(const gmx_domdec_t& dd);
 /*! \brief Return whether update groups are used */
 bool ddUsesUpdateGroups(const gmx_domdec_t& dd);
 
-/*! \brief Return whether the DD has a single dimension with a single pulse
- *
- * The GPU halo exchange code requires a 1D single-pulse DD, and its
- * setup code can use the returned value to understand what it should
- * do. */
-bool is1DAnd1PulseDD(const gmx_domdec_t& dd);
-
 /*! \brief Initialize data structures for bonded interactions */
-void dd_init_bondeds(FILE*              fplog,
-                     gmx_domdec_t*      dd,
-                     const gmx_mtop_t*  mtop,
-                     const gmx_vsite_t* vsite,
-                     const t_inputrec*  ir,
-                     gmx_bool           bBCheck,
-                     cginfo_mb_t*       cginfo_mb);
+void dd_init_bondeds(FILE*                           fplog,
+                     gmx_domdec_t*                   dd,
+                     const gmx_mtop_t&               mtop,
+                     const gmx::VirtualSitesHandler* vsite,
+                     const t_inputrec*               ir,
+                     gmx_bool                        bBCheck,
+                     gmx::ArrayRef<cginfo_mb_t>      cginfo_mb);
 
 /*! \brief Returns whether molecules are always whole, i.e. not broken by PBC */
 bool dd_moleculesAreAlwaysWhole(const gmx_domdec_t& dd);
 
 /*! \brief Returns if we need to do pbc for calculating bonded interactions */
-gmx_bool dd_bonded_molpbc(const gmx_domdec_t* dd, int ePBC);
+gmx_bool dd_bonded_molpbc(const gmx_domdec_t* dd, PbcType pbcType);
 
 /*! \brief Change the DD non-bonded communication cut-off.
  *
@@ -229,16 +228,20 @@ void reset_dd_statistics_counters(struct gmx_domdec_t* dd);
 /* In domdec_con.c */
 
 /*! \brief Communicates the virtual site forces, reduces the shift forces when \p fshift != NULL */
-void dd_move_f_vsites(struct gmx_domdec_t* dd, rvec* f, rvec* fshift);
+void dd_move_f_vsites(const gmx_domdec_t& dd, gmx::ArrayRef<gmx::RVec> f, gmx::ArrayRef<gmx::RVec> fshift);
 
 /*! \brief Clears the forces for virtual sites */
-void dd_clear_f_vsites(struct gmx_domdec_t* dd, rvec* f);
+void dd_clear_f_vsites(const gmx_domdec_t& dd, gmx::ArrayRef<gmx::RVec> f);
 
 /*! \brief Move x0 and also x1 if x1!=NULL. bX1IsCoord tells if to do PBC on x1 */
-void dd_move_x_constraints(struct gmx_domdec_t* dd, const matrix box, rvec* x0, rvec* x1, gmx_bool bX1IsCoord);
+void dd_move_x_constraints(struct gmx_domdec_t*     dd,
+                           const matrix             box,
+                           gmx::ArrayRef<gmx::RVec> x0,
+                           gmx::ArrayRef<gmx::RVec> x1,
+                           gmx_bool                 bX1IsCoord);
 
 /*! \brief Communicates the coordinates involved in virtual sites */
-void dd_move_x_vsites(struct gmx_domdec_t* dd, const matrix box, rvec* x);
+void dd_move_x_vsites(const gmx_domdec_t& dd, const matrix box, rvec* x);
 
 /*! \brief Returns the local atom count array for all constraints
  *
@@ -253,21 +256,21 @@ gmx::ArrayRef<const int> dd_constraints_nlocalatoms(const gmx_domdec_t* dd);
 /* In domdec_top.c */
 
 /*! \brief Print error output when interactions are missing */
-[[noreturn]] void dd_print_missing_interactions(const gmx::MDLogger&  mdlog,
-                                                t_commrec*            cr,
-                                                int                   local_count,
-                                                const gmx_mtop_t*     top_global,
-                                                const gmx_localtop_t* top_local,
-                                                const rvec*           x,
-                                                const matrix          box);
+[[noreturn]] void dd_print_missing_interactions(const gmx::MDLogger&           mdlog,
+                                                t_commrec*                     cr,
+                                                int                            local_count,
+                                                const gmx_mtop_t*              top_global,
+                                                const gmx_localtop_t*          top_local,
+                                                gmx::ArrayRef<const gmx::RVec> x,
+                                                const matrix                   box);
 
 /*! \brief Generate and store the reverse topology */
-void dd_make_reverse_top(FILE*              fplog,
-                         gmx_domdec_t*      dd,
-                         const gmx_mtop_t*  mtop,
-                         const gmx_vsite_t* vsite,
-                         const t_inputrec*  ir,
-                         gmx_bool           bBCheck);
+void dd_make_reverse_top(FILE*                           fplog,
+                         gmx_domdec_t*                   dd,
+                         const gmx_mtop_t*               mtop,
+                         const gmx::VirtualSitesHandler* vsite,
+                         const t_inputrec*               ir,
+                         gmx_bool                        bBCheck);
 
 /*! \brief Generate the local topology and virtual site data */
 void dd_make_local_top(struct gmx_domdec_t*       dd,
@@ -284,13 +287,6 @@ void dd_make_local_top(struct gmx_domdec_t*       dd,
 /*! \brief Sort ltop->ilist when we are doing free energy. */
 void dd_sort_local_top(gmx_domdec_t* dd, const t_mdatoms* mdatoms, gmx_localtop_t* ltop);
 
-/*! \brief Initialize local topology
- *
- * \param[in] top_global Reference to global topology.
- * \param[in,out] top Pointer to new local topology
- */
-void dd_init_local_top(const gmx_mtop_t& top_global, gmx_localtop_t* top);
-
 /*! \brief Construct local state */
 void dd_init_local_state(struct gmx_domdec_t* dd, const t_state* state_global, t_state* local_state);
 
@@ -298,16 +294,55 @@ void dd_init_local_state(struct gmx_domdec_t* dd, const t_state* state_global, t
  *
  * Also stores whether atoms are linked in \p cginfo_mb.
  */
-t_blocka* makeBondedLinks(const gmx_mtop_t* mtop, cginfo_mb_t* cginfo_mb);
+t_blocka* makeBondedLinks(const gmx_mtop_t& mtop, gmx::ArrayRef<cginfo_mb_t> cginfo_mb);
 
 /*! \brief Calculate the maximum distance involved in 2-body and multi-body bonded interactions */
-void dd_bonded_cg_distance(const gmx::MDLogger& mdlog,
-                           const gmx_mtop_t*    mtop,
-                           const t_inputrec*    ir,
-                           const rvec*          x,
-                           const matrix         box,
-                           gmx_bool             bBCheck,
-                           real*                r_2b,
-                           real*                r_mb);
+void dd_bonded_cg_distance(const gmx::MDLogger&           mdlog,
+                           const gmx_mtop_t*              mtop,
+                           const t_inputrec*              ir,
+                           gmx::ArrayRef<const gmx::RVec> x,
+                           const matrix                   box,
+                           gmx_bool                       bBCheck,
+                           real*                          r_2b,
+                           real*                          r_mb);
+
+/*! \brief Construct the GPU halo exchange object(s).
+ *
+ * \param[in] mdlog               The logger object.
+ * \param[in] cr                  The commrec object.
+ * \param[in] deviceStreamManager Manager of the GPU context and streams.
+ * \param[in] wcycle              The wallclock counter.
+ */
+void constructGpuHaloExchange(const gmx::MDLogger&            mdlog,
+                              const t_commrec&                cr,
+                              const gmx::DeviceStreamManager& deviceStreamManager,
+                              gmx_wallcycle*                  wcycle);
+
+/*! \brief
+ * (Re-) Initialization for GPU halo exchange
+ * \param [in] cr                   The commrec object
+ * \param [in] d_coordinatesBuffer  pointer to coordinates buffer in GPU memory
+ * \param [in] d_forcesBuffer       pointer to forces buffer in GPU memory
+ */
+void reinitGpuHaloExchange(const t_commrec&        cr,
+                           DeviceBuffer<gmx::RVec> d_coordinatesBuffer,
+                           DeviceBuffer<gmx::RVec> d_forcesBuffer);
+
+
+/*! \brief GPU halo exchange of coordinates buffer.
+ * \param [in] cr                             The commrec object
+ * \param [in] box                            Coordinate box (from which shifts will be constructed)
+ * \param [in] coordinatesReadyOnDeviceEvent  event recorded when coordinates have been copied to device
+ */
+void communicateGpuHaloCoordinates(const t_commrec&      cr,
+                                   const matrix          box,
+                                   GpuEventSynchronizer* coordinatesReadyOnDeviceEvent);
+
+
+/*! \brief GPU halo exchange of force buffer.
+ * \param [in] cr                The commrec object
+ * \param [in] accumulateForces  True if forces should accumulate, otherwise they are set
+ */
+void communicateGpuHaloForces(const t_commrec& cr, bool accumulateForces);
 
 #endif
index 0a62ae980ee58c162770ab360bfb74fd28e6e3cb..b11ef6bdb335f738add0841c2b5991bd159ace8f 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2006,2007,2008,2009,2010,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2006,2007,2008,2009,2010 by the GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
-#include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/listoflists.h"
 
 #include "domdec_internal.h"
 #include "domdec_specatomcomm.h"
 
+using gmx::ListOfLists;
+
 /*! \brief Struct used during constraint setup with domain decomposition */
 struct gmx_domdec_constraints_t
 {
@@ -90,8 +94,8 @@ struct gmx_domdec_constraints_t
     std::unique_ptr<gmx::HashedMap<int>> ga2la; /**< Global to local communicated constraint atom only index */
 
     /* Multi-threading stuff */
-    int                  nthread; /**< Number of threads used for DD constraint setup */
-    std::vector<t_ilist> ils;     /**< Constraint ilist working arrays, size \p nthread */
+    int                          nthread; /**< Number of threads used for DD constraint setup */
+    std::vector<InteractionList> ils;     /**< Constraint ilist working arrays, size \p nthread */
 
     /* Buffers for requesting atoms */
     std::vector<std::vector<int>> requestedGlobalAtomIndices; /**< Buffers for requesting global atom indices, one per thread */
@@ -99,11 +103,16 @@ struct gmx_domdec_constraints_t
     //! @endcond
 };
 
-void dd_move_x_constraints(gmx_domdec_t* dd, const matrix box, rvec* x0, rvec* x1, gmx_bool bX1IsCoord)
+void dd_move_x_constraints(gmx_domdec_t*            dd,
+                           const matrix             box,
+                           gmx::ArrayRef<gmx::RVec> x0,
+                           gmx::ArrayRef<gmx::RVec> x1,
+                           gmx_bool                 bX1IsCoord)
 {
     if (dd->constraint_comm)
     {
-        dd_move_x_specat(dd, dd->constraint_comm, box, x0, x1, bX1IsCoord);
+        dd_move_x_specat(dd, dd->constraint_comm, box, as_rvec_array(x0.data()),
+                         as_rvec_array(x1.data()), bX1IsCoord);
 
         ddReopenBalanceRegionCpu(dd);
     }
@@ -141,51 +150,45 @@ static void walk_out(int                       con,
                      int                       nrec,
                      gmx::ArrayRef<const int>  ia1,
                      gmx::ArrayRef<const int>  ia2,
-                     const t_blocka*           at2con,
+                     const ListOfLists<int>&   at2con,
                      const gmx_ga2la_t&        ga2la,
                      gmx_bool                  bHomeConnect,
                      gmx_domdec_constraints_t* dc,
                      gmx_domdec_specat_comm_t* dcc,
-                     t_ilist*                  il_local,
+                     InteractionList*          il_local,
                      std::vector<int>*         ireq)
 {
-    int            a1_gl, a2_gl, i, coni, b;
-    const t_iatom* iap;
-
     if (!dc->gc_req[con_offset + con])
     {
         /* Add this non-home constraint to the list */
         dc->con_gl.push_back(con_offset + con);
         dc->con_nlocat.push_back(bHomeConnect ? 1 : 0);
-        dc->gc_req[con_offset + con] = true;
-        if (il_local->nr + 3 > il_local->nalloc)
-        {
-            il_local->nalloc = over_alloc_dd(il_local->nr + 3);
-            srenew(il_local->iatoms, il_local->nalloc);
-        }
-        iap                              = constr_iatomptr(ia1, ia2, con);
-        il_local->iatoms[il_local->nr++] = iap[0];
-        a1_gl                            = offset + iap[1];
-        a2_gl                            = offset + iap[2];
+        dc->gc_req[con_offset + con]     = true;
+        const int*         iap           = constr_iatomptr(ia1, ia2, con);
+        const int          parameterType = iap[0];
+        const int          a1_gl         = offset + iap[1];
+        const int          a2_gl         = offset + iap[2];
+        std::array<int, 2> atoms;
         /* The following indexing code can probably be optizimed */
         if (const int* a_loc = ga2la.findHome(a1_gl))
         {
-            il_local->iatoms[il_local->nr++] = *a_loc;
+            atoms[0] = *a_loc;
         }
         else
         {
             /* We set this index later */
-            il_local->iatoms[il_local->nr++] = -a1_gl - 1;
+            atoms[0] = -a1_gl - 1;
         }
         if (const int* a_loc = ga2la.findHome(a2_gl))
         {
-            il_local->iatoms[il_local->nr++] = *a_loc;
+            atoms[1] = *a_loc;
         }
         else
         {
             /* We set this index later */
-            il_local->iatoms[il_local->nr++] = -a2_gl - 1;
+            atoms[1] = -a2_gl - 1;
         }
+        il_local->push_back(parameterType, atoms);
         dc->ncon++;
     }
     /* Check to not ask for the same atom more than once */
@@ -200,13 +203,14 @@ static void walk_out(int                       con,
 
     if (nrec > 0)
     {
-        for (i = at2con->index[a]; i < at2con->index[a + 1]; i++)
+        /* Loop over the constraint connected to atom a */
+        for (const int coni : at2con[a])
         {
-            coni = at2con->a[i];
             if (coni != con)
             {
                 /* Walk further */
-                iap = constr_iatomptr(ia1, ia2, coni);
+                const int* iap = constr_iatomptr(ia1, ia2, coni);
+                int        b;
                 if (a == iap[1])
                 {
                     b = iap[2];
@@ -232,7 +236,7 @@ static void atoms_to_settles(gmx_domdec_t*                         dd,
                              gmx::ArrayRef<const std::vector<int>> at2settle_mt,
                              int                                   cg_start,
                              int                                   cg_end,
-                             t_ilist*                              ils_local,
+                             InteractionList*                      ils_local,
                              std::vector<int>*                     ireq)
 {
     const gmx_ga2la_t& ga2la = *dd->ga2la;
@@ -275,23 +279,17 @@ static void atoms_to_settles(gmx_domdec_t*                         dd,
 
                 if (bAssign)
                 {
-                    if (ils_local->nr + 1 + nral > ils_local->nalloc)
-                    {
-                        ils_local->nalloc = over_alloc_dd(ils_local->nr + 1 + nral);
-                        srenew(ils_local->iatoms, ils_local->nalloc);
-                    }
-
-                    ils_local->iatoms[ils_local->nr++] = ia1[settle * 4];
-
+                    const int          parameterType = ia1[settle * 4];
+                    std::array<int, 3> atoms;
                     for (int sa = 0; sa < nral; sa++)
                     {
                         if (const int* a_loc = ga2la.findHome(a_gls[sa]))
                         {
-                            ils_local->iatoms[ils_local->nr++] = *a_loc;
+                            atoms[sa] = *a_loc;
                         }
                         else
                         {
-                            ils_local->iatoms[ils_local->nr++] = -a_gls[sa] - 1;
+                            atoms[sa] = -a_gls[sa] - 1;
                             /* Add this non-home atom to the list */
                             ireq->push_back(a_gls[sa]);
                             /* A check on double atom requests is
@@ -299,6 +297,7 @@ static void atoms_to_settles(gmx_domdec_t*                         dd,
                              */
                         }
                     }
+                    ils_local->push_back(parameterType, atoms);
                 }
             }
         }
@@ -306,17 +305,14 @@ static void atoms_to_settles(gmx_domdec_t*                         dd,
 }
 
 /*! \brief Looks up constraint for the local atoms */
-static void atoms_to_constraints(gmx_domdec_t*                 dd,
-                                 const gmx_mtop_t*             mtop,
-                                 const int*                    cginfo,
-                                 gmx::ArrayRef<const t_blocka> at2con_mt,
-                                 int                           nrec,
-                                 t_ilist*                      ilc_local,
-                                 std::vector<int>*             ireq)
+static void atoms_to_constraints(gmx_domdec_t*                         dd,
+                                 const gmx_mtop_t*                     mtop,
+                                 const int*                            cginfo,
+                                 gmx::ArrayRef<const ListOfLists<int>> at2con_mt,
+                                 int                                   nrec,
+                                 InteractionList*                      ilc_local,
+                                 std::vector<int>*                     ireq)
 {
-    const t_blocka* at2con;
-    int             b_lo, offset, b_mol, i, con, con_offset;
-
     gmx_domdec_constraints_t* dc  = dd->constraints;
     gmx_domdec_specat_comm_t* dcc = dd->constraint_comm;
 
@@ -344,15 +340,16 @@ static void atoms_to_constraints(gmx_domdec_t*                 dd,
              * This is only required for the global index to make sure
              * that we use each constraint only once.
              */
-            con_offset = dc->molb_con_offset[mb] + molnr * dc->molb_ncon_mol[mb];
+            const int con_offset = dc->molb_con_offset[mb] + molnr * dc->molb_ncon_mol[mb];
 
             /* The global atom number offset for this molecule */
-            offset = a_gl - a_mol;
-            at2con = &at2con_mt[molb.type];
-            for (i = at2con->index[a_mol]; i < at2con->index[a_mol + 1]; i++)
+            const int offset = a_gl - a_mol;
+            /* Loop over the constraints connected to atom a_mol in the molecule */
+            const auto& at2con = at2con_mt[molb.type];
+            for (const int con : at2con[a_mol])
             {
-                con            = at2con->a[i];
                 const int* iap = constr_iatomptr(ia1, ia2, con);
+                int        b_mol;
                 if (a_mol == iap[1])
                 {
                     b_mol = iap[2];
@@ -368,15 +365,12 @@ static void atoms_to_constraints(gmx_domdec_t*                 dd,
                     {
                         dc->con_gl.push_back(con_offset + con);
                         dc->con_nlocat.push_back(2);
-                        if (ilc_local->nr + 3 > ilc_local->nalloc)
-                        {
-                            ilc_local->nalloc = over_alloc_dd(ilc_local->nr + 3);
-                            srenew(ilc_local->iatoms, ilc_local->nalloc);
-                        }
-                        b_lo                               = *a_loc;
-                        ilc_local->iatoms[ilc_local->nr++] = iap[0];
-                        ilc_local->iatoms[ilc_local->nr++] = (a_gl == iap[1] ? a : b_lo);
-                        ilc_local->iatoms[ilc_local->nr++] = (a_gl == iap[1] ? b_lo : a);
+                        const int          b_lo          = *a_loc;
+                        const int          parameterType = iap[0];
+                        std::array<int, 2> atoms;
+                        atoms[0] = (a_gl == iap[1] ? a : b_lo);
+                        atoms[1] = (a_gl == iap[1] ? b_lo : a);
+                        ilc_local->push_back(parameterType, atoms);
                         dc->ncon++;
                         nhome++;
                     }
@@ -408,21 +402,18 @@ static void atoms_to_constraints(gmx_domdec_t*                 dd,
     }
 }
 
-int dd_make_local_constraints(gmx_domdec_t*            dd,
-                              int                      at_start,
-                              const struct gmx_mtop_t* mtop,
-                              const int*               cginfo,
-                              gmx::Constraints*        constr,
-                              int                      nrec,
-                              t_ilist*                 il_local)
+int dd_make_local_constraints(gmx_domdec_t*                  dd,
+                              int                            at_start,
+                              const struct gmx_mtop_t*       mtop,
+                              const int*                     cginfo,
+                              gmx::Constraints*              constr,
+                              int                            nrec,
+                              gmx::ArrayRef<InteractionList> il_local)
 {
-    gmx_domdec_constraints_t*     dc;
-    t_ilist *                     ilc_local, *ils_local;
-    std::vector<int>*             ireq;
-    gmx::ArrayRef<const t_blocka> at2con_mt;
-    gmx::HashedMap<int>*          ga2la_specat;
-    int                           at_end, i, j;
-    t_iatom*                      iap;
+    gmx_domdec_constraints_t* dc;
+    InteractionList *         ilc_local, *ils_local;
+    gmx::HashedMap<int>*      ga2la_specat;
+    int                       at_end, i, j;
 
     // This code should not be called unless this condition is true,
     // because that's the only time init_domdec_constraints is
@@ -444,8 +435,10 @@ int dd_make_local_constraints(gmx_domdec_t*            dd,
     ilc_local = &il_local[F_CONSTR];
     ils_local = &il_local[F_SETTLE];
 
-    dc->ncon      = 0;
-    ilc_local->nr = 0;
+    dc->ncon = 0;
+    gmx::ArrayRef<const ListOfLists<int>> at2con_mt;
+    std::vector<int>*                     ireq = nullptr;
+    ilc_local->clear();
     if (dd->constraint_comm)
     {
         // TODO Perhaps gmx_domdec_constraints_t should keep a valid constr?
@@ -454,12 +447,6 @@ int dd_make_local_constraints(gmx_domdec_t*            dd,
         ireq      = &dc->requestedGlobalAtomIndices[0];
         ireq->clear();
     }
-    else
-    {
-        // Currently unreachable
-        at2con_mt = {};
-        ireq      = nullptr;
-    }
 
     gmx::ArrayRef<const std::vector<int>> at2settle_mt;
     /* When settle works inside charge groups, we assigned them already */
@@ -467,8 +454,8 @@ int dd_make_local_constraints(gmx_domdec_t*            dd,
     {
         // TODO Perhaps gmx_domdec_constraints_t should keep a valid constr?
         GMX_RELEASE_ASSERT(constr != nullptr, "Must have valid constraints object");
-        at2settle_mt  = constr->atom2settle_moltype();
-        ils_local->nr = 0;
+        at2settle_mt = constr->atom2settle_moltype();
+        ils_local->clear();
     }
 
     if (at2settle_mt.empty())
@@ -496,8 +483,8 @@ int dd_make_local_constraints(gmx_domdec_t*            dd,
 
                 if (thread >= t0_set)
                 {
-                    int      cg0, cg1;
-                    t_ilist* ilst;
+                    int              cg0, cg1;
+                    InteractionList* ilst;
 
                     /* Distribute the settle check+assignments over
                      * dc->nthread or dc->nthread-1 threads.
@@ -513,7 +500,7 @@ int dd_make_local_constraints(gmx_domdec_t*            dd,
                     {
                         ilst = &dc->ils[thread];
                     }
-                    ilst->nr = 0;
+                    ilst->clear();
 
                     std::vector<int>& ireqt = dc->requestedGlobalAtomIndices[thread];
                     if (thread > 0)
@@ -530,22 +517,9 @@ int dd_make_local_constraints(gmx_domdec_t*            dd,
         /* Combine the generate settles and requested indices */
         for (int thread = 1; thread < dc->nthread; thread++)
         {
-            t_ilist* ilst;
-            int      ia;
-
             if (thread > t0_set)
             {
-                ilst = &dc->ils[thread];
-                if (ils_local->nr + ilst->nr > ils_local->nalloc)
-                {
-                    ils_local->nalloc = over_alloc_large(ils_local->nr + ilst->nr);
-                    srenew(ils_local->iatoms, ils_local->nalloc);
-                }
-                for (ia = 0; ia < ilst->nr; ia++)
-                {
-                    ils_local->iatoms[ils_local->nr + ia] = ilst->iatoms[ia];
-                }
-                ils_local->nr += ilst->nr;
+                ils_local->append(dc->ils[thread]);
             }
 
             const std::vector<int>& ireqt = dc->requestedGlobalAtomIndices[thread];
@@ -554,7 +528,7 @@ int dd_make_local_constraints(gmx_domdec_t*            dd,
 
         if (debug)
         {
-            fprintf(debug, "Settles: total %3d\n", ils_local->nr / 4);
+            fprintf(debug, "Settles: total %3d\n", ils_local->size() / 4);
         }
     }
 
@@ -569,9 +543,9 @@ int dd_make_local_constraints(gmx_domdec_t*            dd,
         ga2la_specat = dd->constraints->ga2la.get();
 
         nral1 = 1 + NRAL(F_CONSTR);
-        for (i = 0; i < ilc_local->nr; i += nral1)
+        for (i = 0; i < ilc_local->size(); i += nral1)
         {
-            iap = ilc_local->iatoms + i;
+            int* iap = ilc_local->iatoms.data() + i;
             for (j = 1; j < nral1; j++)
             {
                 if (iap[j] < 0)
@@ -584,9 +558,9 @@ int dd_make_local_constraints(gmx_domdec_t*            dd,
         }
 
         nral1 = 1 + NRAL(F_SETTLE);
-        for (i = 0; i < ils_local->nr; i += nral1)
+        for (i = 0; i < ils_local->size(); i += nral1)
         {
-            iap = ils_local->iatoms + i;
+            int* iap = ils_local->iatoms.data() + i;
             for (j = 1; j < nral1; j++)
             {
                 if (iap[j] < 0)
index 1ca1028e16bff68da0b908787b7aac0acf5bb8bc..de25e404a02433974fedda35e998023310ebac2e 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2005,2006,2007,2008,2009,2010,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2005,2006,2007,2008,2009 by the GROMACS development team.
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,6 +46,8 @@
 #ifndef GMX_DOMDEC_DOMDEC_CONSTRAINTS_H
 #define GMX_DOMDEC_DOMDEC_CONSTRAINTS_H
 
+#include "gromacs/utility/arrayref.h"
+
 namespace gmx
 {
 class Constraints;
@@ -51,19 +55,19 @@ class Constraints;
 
 struct gmx_domdec_t;
 struct gmx_mtop_t;
-struct t_ilist;
+struct InteractionList;
 
 /*! \brief Clears the local indices for the constraint communication setup */
 void dd_clear_local_constraint_indices(gmx_domdec_t* dd);
 
 /*! \brief Sets up communication and atom indices for all local+connected constraints */
-int dd_make_local_constraints(struct gmx_domdec_t*     dd,
-                              int                      at_start,
-                              const struct gmx_mtop_t* mtop,
-                              const int*               cginfo,
-                              gmx::Constraints*        constr,
-                              int                      nrec,
-                              struct t_ilist*          il_local);
+int dd_make_local_constraints(struct gmx_domdec_t*           dd,
+                              int                            at_start,
+                              const struct gmx_mtop_t*       mtop,
+                              const int*                     cginfo,
+                              gmx::Constraints*              constr,
+                              int                            nrec,
+                              gmx::ArrayRef<InteractionList> il_local);
 
 /*! \brief Initializes the data structures for constraint communication */
 void init_domdec_constraints(gmx_domdec_t* dd, const gmx_mtop_t* mtop);
index 1b62f894b093964c6f0d804a07d28e78f700f8b6..691b0b87aa4cef053579f538791b82c868beb2d7 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -144,7 +145,7 @@ struct DDCellsizesWithDlb
  * Here floats are accurate enough, since these variables
  * only influence the load balancing, not the actual MD results.
  */
-typedef struct
+typedef struct domdec_load
 {
     /**< The number of load recordings */
     int nload = 0;
@@ -167,7 +168,7 @@ typedef struct
 } domdec_load_t;
 
 /*! \brief Data needed to sort an atom to the desired location in the local state */
-typedef struct
+typedef struct gmx_cgsort
 {
     /**< Neighborsearch grid cell index */
     int nsc = 0;
@@ -178,7 +179,7 @@ typedef struct
 } gmx_cgsort_t;
 
 /*! \brief Temporary buffers for sorting atoms */
-typedef struct
+typedef struct gmx_domdec_sort
 {
     /**< Sorted array of indices */
     std::vector<gmx_cgsort_t> sorted;
@@ -283,7 +284,7 @@ enum class DlbState
 };
 
 /*! \brief The PME domain decomposition for one dimension */
-typedef struct
+typedef struct gmx_ddpme
 {
     /**< The dimension */
     int dim = 0;
@@ -472,9 +473,6 @@ struct DDSettings
     //! Flop counter (0=no,1=yes,2=with (eFlop-1)*5% noise
     int eFlop = 0;
 
-    //! Request 1D domain decomposition with maximum one communication pulse
-    bool request1DAnd1Pulse;
-
     //! Whether to order the DD dimensions from z to x
     bool useDDOrderZYX = false;
 
@@ -778,7 +776,7 @@ static inline int dd_index(const ivec numDomains, const ivec domainCoordinates)
 /*! Returns the size of the buffer to hold fractional cell boundaries for DD dimension index dimIndex */
 static inline int ddCellFractionBufferSize(const gmx_domdec_t* dd, int dimIndex)
 {
-    return dd->nc[dd->dim[dimIndex]] + 1 + dimIndex * 2 + 1 + dimIndex;
+    return dd->numCells[dd->dim[dimIndex]] + 1 + dimIndex * 2 + 1 + dimIndex;
 }
 
 /*! \brief Maximum number of ranks for using send/recv for state scattering and gathering
index 902dbb768ad1a7505812554bc733f516e5af8d14..48d8702bcd5b444b3ad7f66f13571c5818ef0676 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2008-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_DOMDEC_DOMDEC_NETWORK_H
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 
 struct gmx_domdec_t;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
 /* \brief */
 enum
 {
index 17b0d63c6cc051fd8e5a550ba3dffbf58764d504..1b058becc5b7c0168faa3609c8a6783e9ed8e2e1 100644 (file)
@@ -314,7 +314,7 @@ real comm_box_frac(const gmx::IVec& dd_nc, real cutoff, const gmx_ddbox_t& ddbox
 /*! \brief Return whether the DD inhomogeneous in the z direction */
 static gmx_bool inhomogeneous_z(const t_inputrec& ir)
 {
-    return ((EEL_PME(ir.coulombtype) || ir.coulombtype == eelEWALD) && ir.ePBC == epbcXYZ
+    return ((EEL_PME(ir.coulombtype) || ir.coulombtype == eelEWALD) && ir.pbcType == PbcType::Xyz
             && ir.ewald_geometry == eewg3DC);
 }
 
@@ -364,8 +364,8 @@ static float comm_cost_est(real               limit,
     float temp;
 
     /* Check the DD algorithm restrictions */
-    if ((ir.ePBC == epbcXY && ir.nwall < 2 && nc[ZZ] > 1)
-        || (ir.ePBC == epbcSCREW && (nc[XX] == 1 || nc[YY] > 1 || nc[ZZ] > 1)))
+    if ((ir.pbcType == PbcType::XY && ir.nwall < 2 && nc[ZZ] > 1)
+        || (ir.pbcType == PbcType::Screw && (nc[XX] == 1 || nc[YY] > 1 || nc[ZZ] > 1)))
     {
         return -1;
     }
@@ -528,7 +528,7 @@ static float comm_cost_est(real               limit,
 
     /* Add cost of pbc_dx for bondeds */
     cost_pbcdx = 0;
-    if ((nc[XX] == 1 || nc[YY] == 1) || (nc[ZZ] == 1 && ir.ePBC != epbcXY))
+    if ((nc[XX] == 1 || nc[YY] == 1) || (nc[ZZ] == 1 && ir.pbcType != PbcType::XY))
     {
         if ((ddbox.tric_dir[XX] && nc[XX] == 1) || (ddbox.tric_dir[YY] && nc[YY] == 1))
         {
@@ -553,7 +553,6 @@ static float comm_cost_est(real               limit,
 /*! \brief Assign penalty factors to possible domain decompositions,
  * based on the estimated communication costs. */
 static void assign_factors(const real         limit,
-                           const bool         request1D,
                            const real         cutoff,
                            const matrix       box,
                            const gmx_ddbox_t& ddbox,
@@ -573,14 +572,6 @@ static void assign_factors(const real         limit,
 
     if (ndiv == 0)
     {
-        const int  maxDimensionSize        = std::max(ir_try[XX], std::max(ir_try[YY], ir_try[ZZ]));
-        const int  productOfDimensionSizes = ir_try[XX] * ir_try[YY] * ir_try[ZZ];
-        const bool decompositionHasOneDimension = (maxDimensionSize == productOfDimensionSizes);
-        if (request1D && !decompositionHasOneDimension)
-        {
-            /* We requested 1D DD, but got multiple dimensions */
-            return;
-        }
 
         ce = comm_cost_est(limit, cutoff, box, ddbox, natoms, ir, pbcdxr, npme, ir_try);
         if (ce >= 0
@@ -611,8 +602,8 @@ static void assign_factors(const real         limit,
             }
 
             /* recurse */
-            assign_factors(limit, request1D, cutoff, box, ddbox, natoms, ir, pbcdxr, npme, ndiv - 1,
-                           div + 1, mdiv + 1, irTryPtr, opt);
+            assign_factors(limit, cutoff, box, ddbox, natoms, ir, pbcdxr, npme, ndiv - 1, div + 1,
+                           mdiv + 1, irTryPtr, opt);
 
             for (i = 0; i < mdiv[0] - x - y; i++)
             {
@@ -639,7 +630,6 @@ static gmx::IVec optimizeDDCells(const gmx::MDLogger& mdlog,
                                  const int            numRanksRequested,
                                  const int            numPmeOnlyRanks,
                                  const real           cellSizeLimit,
-                                 const bool           request1DAnd1Pulse,
                                  const gmx_mtop_t&    mtop,
                                  const matrix         box,
                                  const gmx_ddbox_t&   ddbox,
@@ -716,24 +706,19 @@ static gmx::IVec optimizeDDCells(const gmx::MDLogger& mdlog,
 
     gmx::IVec itry       = { 1, 1, 1 };
     gmx::IVec numDomains = { 0, 0, 0 };
-    assign_factors(cellSizeLimit, request1DAnd1Pulse, systemInfo.cutoff, box, ddbox, mtop.natoms, ir,
-                   pbcdxr, numRanksDoingPmeWork, div.size(), div.data(), mdiv.data(), &itry, &numDomains);
+    assign_factors(cellSizeLimit, systemInfo.cutoff, box, ddbox, mtop.natoms, ir, pbcdxr,
+                   numRanksDoingPmeWork, div.size(), div.data(), mdiv.data(), &itry, &numDomains);
 
     return numDomains;
 }
 
 real getDDGridSetupCellSizeLimit(const gmx::MDLogger& mdlog,
-                                 const bool           request1DAnd1Pulse,
                                  const bool           bDynLoadBal,
                                  const real           dlb_scale,
                                  const t_inputrec&    ir,
-                                 const real           systemInfoCellSizeLimit)
+                                 real                 systemInfoCellSizeLimit)
 {
     real cellSizeLimit = systemInfoCellSizeLimit;
-    if (request1DAnd1Pulse)
-    {
-        cellSizeLimit = std::max(cellSizeLimit, ir.rlist);
-    }
 
     /* Add a margin for DLB and/or pressure scaling */
     if (bDynLoadBal)
@@ -904,7 +889,8 @@ static int set_dd_dim(const gmx::IVec& numDDCells, const DDSettings& ddSettings,
 }
 
 DDGridSetup getDDGridSetup(const gmx::MDLogger&           mdlog,
-                           const t_commrec*               cr,
+                           DDRole                         ddRole,
+                           MPI_Comm                       communicator,
                            const int                      numRanksRequested,
                            const DomdecOptions&           options,
                            const DDSettings&              ddSettings,
@@ -918,37 +904,29 @@ DDGridSetup getDDGridSetup(const gmx::MDLogger&           mdlog,
 {
     int numPmeOnlyRanks = getNumPmeOnlyRanksToUse(mdlog, options, mtop, ir, box, numRanksRequested);
 
-    if (ddSettings.request1DAnd1Pulse && (numRanksRequested - numPmeOnlyRanks == 1))
-    {
-        // With only one PP rank, there will not be a need for
-        // GPU-based halo exchange that wants to request that any DD
-        // has only 1 dimension and 1 pulse.
-        return DDGridSetup{};
-    }
-
     gmx::IVec numDomains;
     if (options.numCells[XX] > 0)
     {
         numDomains                      = gmx::IVec(options.numCells);
         const ivec numDomainsLegacyIvec = { numDomains[XX], numDomains[YY], numDomains[ZZ] };
-        set_ddbox_cr(*cr, &numDomainsLegacyIvec, ir, box, xGlobal, ddbox);
+        set_ddbox_cr(ddRole, communicator, &numDomainsLegacyIvec, ir, box, xGlobal, ddbox);
     }
     else
     {
-        set_ddbox_cr(*cr, nullptr, ir, box, xGlobal, ddbox);
+        set_ddbox_cr(ddRole, communicator, nullptr, ir, box, xGlobal, ddbox);
 
-        if (MASTER(cr))
+        if (ddRole == DDRole::Master)
         {
             numDomains = optimizeDDCells(mdlog, numRanksRequested, numPmeOnlyRanks, cellSizeLimit,
-                                         ddSettings.request1DAnd1Pulse, mtop, box, *ddbox, ir, systemInfo);
+                                         mtop, box, *ddbox, ir, systemInfo);
         }
     }
 
     /* Communicate the information set by the master to all ranks */
-    gmx_bcast(sizeof(numDomains), numDomains, cr);
+    gmx_bcast(sizeof(numDomains), numDomains, communicator);
     if (EEL_PME(ir.coulombtype))
     {
-        gmx_bcast(sizeof(numPmeOnlyRanks), &numPmeOnlyRanks, cr);
+        gmx_bcast(sizeof(numPmeOnlyRanks), &numPmeOnlyRanks, communicator);
     }
 
     DDGridSetup ddGridSetup;
index 210fe44a5ca527afac5c2687e5f229bcaf00607d..d982ed328a5d20b7e98e92a7fa51faecbeaae8fc 100644 (file)
@@ -43,7 +43,9 @@
 #define GMX_DOMDEC_DOMDEC_SETUP_H
 
 #include "gromacs/math/vec.h"
+#include "gromacs/utility/gmxmpi.h"
 
+enum class DDRole;
 struct DDSettings;
 struct DDSystemInfo;
 struct gmx_ddbox_t;
@@ -92,7 +94,6 @@ void checkForValidRankCountRequests(int  numRanksRequested,
 
 /*! \brief Return the minimum cell size (in nm) required for DD */
 real getDDGridSetupCellSizeLimit(const gmx::MDLogger& mdlog,
-                                 bool                 request1DAnd1Pulse,
                                  bool                 bDynLoadBal,
                                  real                 dlb_scale,
                                  const t_inputrec&    ir,
@@ -105,7 +106,8 @@ real getDDGridSetupCellSizeLimit(const gmx::MDLogger& mdlog,
  * cell setup, DD cell size limits, and the initial ddbox.
  */
 DDGridSetup getDDGridSetup(const gmx::MDLogger&           mdlog,
-                           const t_commrec*               cr,
+                           DDRole                         ddRole,
+                           MPI_Comm                       communicator,
                            int                            numRanksRequested,
                            const gmx::DomdecOptions&      options,
                            const DDSettings&              ddSettings,
index 04faddad435aa3791c6a9662dc3e28ec5b3ec5e4..2c543a034369a565317b012d29d2e262067c4926 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2006,2007,2008,2009,2010,2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2006,2007,2008,2009,2010 by the GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -63,7 +65,7 @@
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
 
-void dd_move_f_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, rvec* f, rvec* fshift)
+void dd_move_f_specat(const gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, rvec* f, rvec* fshift)
 {
     gmx_specatsend_t* spas;
     rvec*             vbuf;
@@ -76,7 +78,7 @@ void dd_move_f_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, rvec* f,
     for (int d = dd->ndim - 1; d >= 0; d--)
     {
         dim = dd->dim[d];
-        if (dd->nc[dim] > 2)
+        if (dd->numCells[dim] > 2)
         {
             /* Pulse the grid forward and backward */
             spas = spac->spas[d];
@@ -89,7 +91,8 @@ void dd_move_f_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, rvec* f,
                               vbuf + spas[0].a.size(), spas[1].a.size());
             for (dir = 0; dir < 2; dir++)
             {
-                bPBC = ((dir == 0 && dd->ci[dim] == 0) || (dir == 1 && dd->ci[dim] == dd->nc[dim] - 1));
+                bPBC   = ((dir == 0 && dd->ci[dim] == 0)
+                        || (dir == 1 && dd->ci[dim] == dd->numCells[dim] - 1));
                 bScrew = (bPBC && dd->unitCellInfo.haveScrewPBC && dim == XX);
 
                 spas = &spac->spas[d][dir];
@@ -145,7 +148,7 @@ void dd_move_f_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, rvec* f,
                        spas->a.size());
             /* Sum the buffer into the required forces */
             if (dd->unitCellInfo.haveScrewPBC && dim == XX
-                && (dd->ci[dim] == 0 || dd->ci[dim] == dd->nc[dim] - 1))
+                && (dd->ci[dim] == 0 || dd->ci[dim] == dd->numCells[dim] - 1))
             {
                 int i = 0;
                 for (int a : spas->a)
@@ -170,7 +173,12 @@ void dd_move_f_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, rvec* f,
     }
 }
 
-void dd_move_x_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, const matrix box, rvec* x0, rvec* x1, gmx_bool bX1IsCoord)
+void dd_move_x_specat(const gmx_domdec_t*       dd,
+                      gmx_domdec_specat_comm_t* spac,
+                      const matrix              box,
+                      rvec*                     x0,
+                      rvec*                     x1,
+                      gmx_bool                  bX1IsCoord)
 {
     gmx_specatsend_t* spas;
     int               nvec, v, n, nn, ns0, ns1, nr0, nr1, nr, d, dim, dir, i;
@@ -187,7 +195,7 @@ void dd_move_x_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, const ma
     for (d = 0; d < dd->ndim; d++)
     {
         dim = dd->dim[d];
-        if (dd->nc[dim] > 2)
+        if (dd->numCells[dim] > 2)
         {
             /* Pulse the grid forward and backward */
             rvec* vbuf = as_rvec_array(spac->vbuf.data());
@@ -199,7 +207,7 @@ void dd_move_x_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, const ma
                     bScrew = (dd->unitCellInfo.haveScrewPBC && dim == XX);
                     copy_rvec(box[dim], shift);
                 }
-                else if (dir == 1 && dd->ci[dim] == dd->nc[dim] - 1)
+                else if (dir == 1 && dd->ci[dim] == dd->numCells[dim] - 1)
                 {
                     bPBC   = TRUE;
                     bScrew = (dd->unitCellInfo.haveScrewPBC && dim == XX);
@@ -295,7 +303,7 @@ void dd_move_x_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, const ma
             {
                 rvec* x = (v == 0 ? x0 : x1);
                 if (dd->unitCellInfo.haveScrewPBC && dim == XX
-                    && (dd->ci[XX] == 0 || dd->ci[XX] == dd->nc[XX] - 1))
+                    && (dd->ci[XX] == 0 || dd->ci[XX] == dd->numCells[XX] - 1))
                 {
                     /* Here we only perform the rotation, the rest of the pbc
                      * is handled in the constraint or viste routines.
@@ -379,7 +387,7 @@ int setup_specat_communication(gmx_domdec_t*             dd,
         /* Pulse the grid forward and backward */
         dim  = dd->dim[d];
         bPBC = (dim < dd->unitCellInfo.npbcdim);
-        if (dd->nc[dim] == 2)
+        if (dd->numCells[dim] == 2)
         {
             /* Only 2 cells, so we only need to communicate once */
             ndir = 1;
@@ -390,8 +398,8 @@ int setup_specat_communication(gmx_domdec_t*             dd,
         }
         for (int dir = 0; dir < ndir; dir++)
         {
-            if (!bPBC && dd->nc[dim] > 2
-                && ((dir == 0 && dd->ci[dim] == dd->nc[dim] - 1) || (dir == 1 && dd->ci[dim] == 0)))
+            if (!bPBC && dd->numCells[dim] > 2
+                && ((dir == 0 && dd->ci[dim] == dd->numCells[dim] - 1) || (dir == 1 && dd->ci[dim] == 0)))
             {
                 /* No pbc: the fist/last cell should not request atoms */
                 nsend_ptr = nsend_zero;
@@ -423,7 +431,7 @@ int setup_specat_communication(gmx_domdec_t*             dd,
     for (int d = 0; d < dd->ndim; d++)
     {
         /* Pulse the grid forward and backward */
-        if (dd->dim[d] >= dd->unitCellInfo.npbcdim || dd->nc[dd->dim[d]] > 2)
+        if (dd->dim[d] >= dd->unitCellInfo.npbcdim || dd->numCells[dd->dim[d]] > 2)
         {
             ndir = 2;
         }
index a051190d754c7f3065934c066d18e63bd8bb5e27..ab7d723c819ed7e8de4a4fc3d28f3162bf36be54 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2005,2006,2007,2008,2009,2010,2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2005,2006,2007,2008,2009 by the GROMACS development team.
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -84,7 +86,7 @@ struct gmx_domdec_specat_comm_t
 };
 
 /*! \brief Communicates the force for special atoms, the shift forces are reduced with \p fshift != NULL */
-void dd_move_f_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, rvec* f, rvec* fshift);
+void dd_move_f_specat(const gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, rvec* f, rvec* fshift);
 
 /*! \brief Communicates the coordinates for special atoms
  *
@@ -95,7 +97,7 @@ void dd_move_f_specat(gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, rvec* f,
  * \param[in,out] x1         Vector to communicate, when != NULL
  * \param[in]     bX1IsCoord Tells is \p x1 is a coordinate vector (needs pbc)
  */
-void dd_move_x_specat(gmx_domdec_t*             dd,
+void dd_move_x_specat(const gmx_domdec_t*       dd,
                       gmx_domdec_specat_comm_t* spac,
                       const matrix              box,
                       rvec*                     x0,
index 55a6a3e9690b253df20089df981867b88cbb3808..db67680fb1465e67889d9f6ce471a3dba34668af 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -94,21 +94,21 @@ struct DDPairInteractionRanges
     //! The j-atom range
     gmx::Range<int> jAtomRange;
     //! Minimum shifts to consider
-    ivec shift0 = {};
+    gmx::IVec shift0 = { 0, 0, 0 };
     //! Maximum shifts to consider
-    ivec shift1 = {};
+    gmx::IVec shift1 = { 0, 0, 0 };
 };
 
-typedef struct
+typedef struct gmx_domdec_zone_size
 {
     /* Zone lower corner in triclinic coordinates         */
-    rvec x0 = {};
+    gmx::RVec x0 = { 0, 0, 0 };
     /* Zone upper corner in triclinic coordinates         */
-    rvec x1 = {};
+    gmx::RVec x1 = { 0, 0, 0 };
     /* Zone bounding box lower corner in Cartesian coords */
-    rvec bb_x0 = {};
+    gmx::RVec bb_x0 = { 0, 0, 0 };
     /* Zone bounding box upper corner in Cartesian coords */
-    rvec bb_x1 = {};
+    gmx::RVec bb_x1 = { 0, 0, 0 };
 } gmx_domdec_zone_size_t;
 
 struct gmx_domdec_zones_t
@@ -129,13 +129,13 @@ struct gmx_domdec_zones_t
 
 struct gmx_ddbox_t
 {
-    int  npbcdim;
-    int  nboundeddim;
-    rvec box0;
-    rvec box_size;
+    int       npbcdim;
+    int       nboundeddim;
+    gmx::RVec box0     = { 0, 0, 0 };
+    gmx::RVec box_size = { 0, 0, 0 };
     /* Tells if the box is skewed for each of the three cartesian directions */
-    ivec tric_dir;
-    rvec skew_fac;
+    gmx::IVec tric_dir = { 0, 0, 0 };
+    gmx::RVec skew_fac = { 0, 0, 0 };
     /* Orthogonal vectors for triclinic cells, Cartesian index */
     rvec v[DIM][DIM];
     /* Normal vectors for the cells walls */
@@ -170,10 +170,10 @@ struct gmx_domdec_t
     int      nnodes       = 0;
     MPI_Comm mpi_comm_all = MPI_COMM_NULL;
     /* The local DD cell index and rank */
-    ivec ci         = { 0, 0, 0 };
-    int  rank       = 0;
-    ivec master_ci  = { 0, 0, 0 };
-    int  masterrank = 0;
+    gmx::IVec ci         = { 0, 0, 0 };
+    int       rank       = 0;
+    gmx::IVec master_ci  = { 0, 0, 0 };
+    int       masterrank = 0;
     /* Communication with the PME only nodes */
     int                   pme_nodeid           = 0;
     gmx_bool              pme_receive_vir_ener = false;
@@ -185,9 +185,10 @@ struct gmx_domdec_t
     UnitCellInfo unitCellInfo;
 
     /* The communication setup, identical for each cell, cartesian index */
-    ivec nc   = { 0, 0, 0 };
-    int  ndim = 0;
-    ivec dim  = { 0, 0, 0 }; /* indexed by 0 to ndim */
+    //! Todo: refactor nbnxm to not rely on this sometimes being a nullptr so this can be IVec
+    ivec      numCells = { 0, 0, 0 };
+    int       ndim     = 0;
+    gmx::IVec dim      = { 0, 0, 0 }; /* indexed by 0 to ndim */
 
     /* Forward and backward neighboring cells, indexed by 0 to ndim */
     int neighbor[DIM][2] = { { 0, 0 }, { 0, 0 }, { 0, 0 } };
@@ -235,8 +236,8 @@ struct gmx_domdec_t
     /* gmx_pme_recv_f buffer */
     std::vector<gmx::RVec> pmeForceReceiveBuffer;
 
-    /* GPU halo exchange object */
-    std::unique_ptr<gmx::GpuHaloExchange> gpuHaloExchange;
+    /* GPU halo exchange objects: this structure supports a vector of pulses for each dimension */
+    std::vector<std::unique_ptr<gmx::GpuHaloExchange>> gpuHaloExchange[DIM];
 };
 
 //! Are we the master node for domain decomposition
index f2f85df0a341bb0b52f20ea505e2f4aa933ac622..59815003f13adb6376e5c5adac4cb2fe9f0863b1 100644 (file)
@@ -61,7 +61,9 @@
 #include "gromacs/math/vec.h"
 #include "gromacs/mdlib/forcerec.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
+#include "gromacs/mdlib/vsite.h"
 #include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/mdatom.h"
@@ -75,6 +77,7 @@
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/logger.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/strconvert.h"
 #include "domdec_vsite.h"
 #include "dump.h"
 
+using gmx::ArrayRef;
+using gmx::ListOfLists;
+using gmx::RVec;
+
 /*! \brief The number of integer item in the local state, used for broadcasting of the state */
 #define NITEM_DD_INIT_LOCAL_STATE 5
 
@@ -108,19 +115,23 @@ struct MolblockIndices
 /*! \brief Struct for thread local work data for local topology generation */
 struct thread_work_t
 {
-    t_idef                    idef;       /**< Partial local topology */
-    std::unique_ptr<VsitePbc> vsitePbc;   /**< vsite PBC structure */
-    int                       nbonded;    /**< The number of bondeds in this struct */
-    t_blocka                  excl;       /**< List of exclusions */
-    int                       excl_count; /**< The total exclusion count for \p excl */
+    /*! \brief Constructor
+     *
+     * \param[in] ffparams  The interaction parameters, the lifetime of the created object should not exceed the lifetime of the passed parameters
+     */
+    thread_work_t(const gmx_ffparams_t& ffparams) : idef(ffparams) {}
+
+    InteractionDefinitions         idef;               /**< Partial local topology */
+    std::unique_ptr<gmx::VsitePbc> vsitePbc = nullptr; /**< vsite PBC structure */
+    int                            nbonded  = 0;       /**< The number of bondeds in this struct */
+    ListOfLists<int>               excl;               /**< List of exclusions */
+    int                            excl_count = 0;     /**< The total exclusion count for \p excl */
 };
 
 /*! \brief Struct for the reverse topology: links bonded interactions to atomsx */
 struct gmx_reverse_top_t
 {
     //! @cond Doxygen_Suppress
-    //! \brief The maximum number of exclusions one atom can have
-    int n_excl_at_max = 0;
     //! \brief Are there constraints in this revserse top?
     bool bConstr = false;
     //! \brief Are there settles in this revserse top?
@@ -175,95 +186,115 @@ static gmx_bool dd_check_ftype(int ftype, gmx_bool bBCheck, gmx_bool bConstr, gm
             || (bConstr && (ftype == F_CONSTR || ftype == F_CONSTRNC)) || (bSettle && ftype == F_SETTLE));
 }
 
-/*! \brief Help print error output when interactions are missing
+/*! \brief Checks whether interactions have been assigned for one function type
  *
- * \note This function needs to be called on all ranks (contains a global summation)
+ * Loops over a list of interactions in the local topology of one function type
+ * and flags each of the interactions as assigned in the global \p isAssigned list.
+ * Exits with an inconsistency error when an interaction is assigned more than once.
  */
-static std::string print_missing_interactions_mb(t_commrec*               cr,
-                                                 const gmx_reverse_top_t* rt,
-                                                 const char*              moltypename,
-                                                 const reverse_ilist_t*   ril,
-                                                 int                      a_start,
-                                                 int                      a_end,
-                                                 int                      nat_mol,
-                                                 int                      nmol,
-                                                 const t_idef*            idef)
+static void flagInteractionsForType(const int                ftype,
+                                    const InteractionList&   il,
+                                    const reverse_ilist_t&   ril,
+                                    const gmx::Range<int>&   atomRange,
+                                    const int                numAtomsPerMolecule,
+                                    gmx::ArrayRef<const int> globalAtomIndices,
+                                    gmx::ArrayRef<int>       isAssigned)
 {
-    int* assigned;
-    int  nril_mol = ril->index[nat_mol];
-    snew(assigned, nmol * nril_mol);
-    gmx::StringOutputStream stream;
-    gmx::TextWriter         log(&stream);
+    const int nril_mol = ril.index[numAtomsPerMolecule];
+    const int nral     = NRAL(ftype);
 
-    gmx::ArrayRef<const int> gatindex = cr->dd->globalAtomIndices;
-    for (int ftype = 0; ftype < F_NRE; ftype++)
+    for (int i = 0; i < il.size(); i += 1 + nral)
     {
-        if (dd_check_ftype(ftype, rt->bBCheck, rt->bConstr, rt->bSettle))
+        // ia[0] is the interaction type, ia[1, ...] the atom indices
+        const int* ia = il.iatoms.data() + i;
+        // Extract the global atom index of the first atom in this interaction
+        const int a0 = globalAtomIndices[ia[1]];
+        /* Check if this interaction is in
+         * the currently checked molblock.
+         */
+        if (atomRange.isInRange(a0))
         {
-            int            nral = NRAL(ftype);
-            const t_ilist* il   = &idef->il[ftype];
-            const t_iatom* ia   = il->iatoms;
-            for (int i = 0; i < il->nr; i += 1 + nral)
+            // The molecule index in the list of this molecule type
+            const int moleculeIndex = (a0 - atomRange.begin()) / numAtomsPerMolecule;
+            const int atomOffset = (a0 - atomRange.begin()) - moleculeIndex * numAtomsPerMolecule;
+            const int globalAtomStartInMolecule = atomRange.begin() + moleculeIndex * numAtomsPerMolecule;
+            int       j_mol                     = ril.index[atomOffset];
+            bool found                          = false;
+            while (j_mol < ril.index[atomOffset + 1] && !found)
             {
-                int a0 = gatindex[ia[1]];
-                /* Check if this interaction is in
-                 * the currently checked molblock.
+                const int j       = moleculeIndex * nril_mol + j_mol;
+                const int ftype_j = ril.il[j_mol];
+                /* Here we need to check if this interaction has
+                 * not already been assigned, since we could have
+                 * multiply defined interactions.
                  */
-                if (a0 >= a_start && a0 < a_end)
+                if (ftype == ftype_j && ia[0] == ril.il[j_mol + 1] && isAssigned[j] == 0)
                 {
-                    int  mol    = (a0 - a_start) / nat_mol;
-                    int  a0_mol = (a0 - a_start) - mol * nat_mol;
-                    int  j_mol  = ril->index[a0_mol];
-                    bool found  = false;
-                    while (j_mol < ril->index[a0_mol + 1] && !found)
+                    /* Check the atoms */
+                    found = true;
+                    for (int a = 0; a < nral; a++)
                     {
-                        int j       = mol * nril_mol + j_mol;
-                        int ftype_j = ril->il[j_mol];
-                        /* Here we need to check if this interaction has
-                         * not already been assigned, since we could have
-                         * multiply defined interactions.
-                         */
-                        if (ftype == ftype_j && ia[0] == ril->il[j_mol + 1] && assigned[j] == 0)
+                        if (globalAtomIndices[ia[1 + a]]
+                            != globalAtomStartInMolecule + ril.il[j_mol + 2 + a])
                         {
-                            /* Check the atoms */
-                            found = true;
-                            for (int a = 0; a < nral; a++)
-                            {
-                                if (gatindex[ia[1 + a]] != a_start + mol * nat_mol + ril->il[j_mol + 2 + a])
-                                {
-                                    found = false;
-                                }
-                            }
-                            if (found)
-                            {
-                                assigned[j] = 1;
-                            }
+                            found = false;
                         }
-                        j_mol += 2 + nral_rt(ftype_j);
                     }
-                    if (!found)
+                    if (found)
                     {
-                        gmx_incons("Some interactions seem to be assigned multiple times");
+                        isAssigned[j] = 1;
                     }
                 }
-                ia += 1 + nral;
+                j_mol += 2 + nral_rt(ftype_j);
+            }
+            if (!found)
+            {
+                gmx_incons("Some interactions seem to be assigned multiple times");
             }
         }
     }
+}
+
+/*! \brief Help print error output when interactions are missing in a molblock
+ *
+ * \note This function needs to be called on all ranks (contains a global summation)
+ */
+static std::string printMissingInteractionsMolblock(t_commrec*               cr,
+                                                    const gmx_reverse_top_t& rt,
+                                                    const char*              moltypename,
+                                                    const reverse_ilist_t&   ril,
+                                                    const gmx::Range<int>&   atomRange,
+                                                    const int                numAtomsPerMolecule,
+                                                    const int                numMolecules,
+                                                    const InteractionDefinitions& idef)
+{
+    const int               nril_mol = ril.index[numAtomsPerMolecule];
+    std::vector<int>        isAssigned(numMolecules * nril_mol, 0);
+    gmx::StringOutputStream stream;
+    gmx::TextWriter         log(&stream);
+
+    for (int ftype = 0; ftype < F_NRE; ftype++)
+    {
+        if (dd_check_ftype(ftype, rt.bBCheck, rt.bConstr, rt.bSettle))
+        {
+            flagInteractionsForType(ftype, idef.il[ftype], ril, atomRange, numAtomsPerMolecule,
+                                    cr->dd->globalAtomIndices, isAssigned);
+        }
+    }
 
-    gmx_sumi(nmol * nril_mol, assigned, cr);
+    gmx_sumi(isAssigned.size(), isAssigned.data(), cr);
 
-    int nprint = 10;
-    int i      = 0;
-    for (int mol = 0; mol < nmol; mol++)
+    const int numMissingToPrint = 10;
+    int       i                 = 0;
+    for (int mol = 0; mol < numMolecules; mol++)
     {
         int j_mol = 0;
         while (j_mol < nril_mol)
         {
-            int ftype = ril->il[j_mol];
+            int ftype = ril.il[j_mol];
             int nral  = NRAL(ftype);
             int j     = mol * nril_mol + j_mol;
-            if (assigned[j] == 0 && !(interaction_function[ftype].flags & IF_VSITE))
+            if (isAssigned[j] == 0 && !(interaction_function[ftype].flags & IF_VSITE))
             {
                 if (DDMASTER(cr->dd))
                 {
@@ -271,13 +302,14 @@ static std::string print_missing_interactions_mb(t_commrec*               cr,
                     {
                         log.writeLineFormatted("Molecule type '%s'", moltypename);
                         log.writeLineFormatted(
-                                "the first %d missing interactions, except for exclusions:", nprint);
+                                "the first %d missing interactions, except for exclusions:",
+                                numMissingToPrint);
                     }
                     log.writeStringFormatted("%20s atoms", interaction_function[ftype].longname);
                     int a;
                     for (a = 0; a < nral; a++)
                     {
-                        log.writeStringFormatted("%5d", ril->il[j_mol + 2 + a] + 1);
+                        log.writeStringFormatted("%5d", ril.il[j_mol + 2 + a] + 1);
                     }
                     while (a < 4)
                     {
@@ -287,13 +319,13 @@ static std::string print_missing_interactions_mb(t_commrec*               cr,
                     log.writeString(" global");
                     for (a = 0; a < nral; a++)
                     {
-                        log.writeStringFormatted(
-                                "%6d", a_start + mol * nat_mol + ril->il[j_mol + 2 + a] + 1);
+                        log.writeStringFormatted("%6d", atomRange.begin() + mol * numAtomsPerMolecule
+                                                                + ril.il[j_mol + 2 + a] + 1);
                     }
                     log.ensureLineBreak();
                 }
                 i++;
-                if (i >= nprint)
+                if (i >= numMissingToPrint)
                 {
                     break;
                 }
@@ -302,40 +334,40 @@ static std::string print_missing_interactions_mb(t_commrec*               cr,
         }
     }
 
-    sfree(assigned);
     return stream.toString();
 }
 
 /*! \brief Help print error output when interactions are missing */
-static void print_missing_interactions_atoms(const gmx::MDLogger& mdlog,
-                                             t_commrec*           cr,
-                                             const gmx_mtop_t*    mtop,
-                                             const t_idef*        idef)
+static void printMissingInteractionsAtoms(const gmx::MDLogger&          mdlog,
+                                          t_commrec*                    cr,
+                                          const gmx_mtop_t&             mtop,
+                                          const InteractionDefinitions& idef)
 {
-    const gmx_reverse_top_t* rt = cr->dd->reverse_top;
+    const gmx_reverse_top_t& rt = *cr->dd->reverse_top;
 
     /* Print the atoms in the missing interactions per molblock */
     int a_end = 0;
-    for (const gmx_molblock_t& molb : mtop->molblock)
+    for (const gmx_molblock_t& molb : mtop.molblock)
     {
-        const gmx_moltype_t& moltype = mtop->moltype[molb.type];
-        int                  a_start = a_end;
+        const gmx_moltype_t& moltype = mtop.moltype[molb.type];
+        const int            a_start = a_end;
         a_end                        = a_start + molb.nmol * moltype.atoms.nr;
+        const gmx::Range<int> atomRange(a_start, a_end);
 
-        auto warning = print_missing_interactions_mb(cr, rt, *(moltype.name), &rt->ril_mt[molb.type],
-                                                     a_start, a_end, moltype.atoms.nr, molb.nmol, idef);
+        auto warning = printMissingInteractionsMolblock(cr, rt, *(moltype.name), rt.ril_mt[molb.type],
+                                                        atomRange, moltype.atoms.nr, molb.nmol, idef);
 
         GMX_LOG(mdlog.warning).appendText(warning);
     }
 }
 
-void dd_print_missing_interactions(const gmx::MDLogger&  mdlog,
-                                   t_commrec*            cr,
-                                   int                   local_count,
-                                   const gmx_mtop_t*     top_global,
-                                   const gmx_localtop_t* top_local,
-                                   const rvec*           x,
-                                   const matrix          box)
+void dd_print_missing_interactions(const gmx::MDLogger&           mdlog,
+                                   t_commrec*                     cr,
+                                   int                            local_count,
+                                   const gmx_mtop_t*              top_global,
+                                   const gmx_localtop_t*          top_local,
+                                   gmx::ArrayRef<const gmx::RVec> x,
+                                   const matrix                   box)
 {
     int           ndiff_tot, cl[F_NRE], n, ndiff, rest_global, rest_local;
     int           ftype, nral;
@@ -353,7 +385,7 @@ void dd_print_missing_interactions(const gmx::MDLogger&  mdlog,
     for (ftype = 0; ftype < F_NRE; ftype++)
     {
         nral      = NRAL(ftype);
-        cl[ftype] = top_local->idef.il[ftype].nr / (1 + nral);
+        cl[ftype] = top_local->idef.il[ftype].size() / (1 + nral);
     }
 
     gmx_sumi(F_NRE, cl, cr);
@@ -398,8 +430,8 @@ void dd_print_missing_interactions(const gmx::MDLogger&  mdlog,
         }
     }
 
-    print_missing_interactions_atoms(mdlog, cr, top_global, &top_local->idef);
-    write_dd_pdb("dd_dump_err", 0, "dump", top_global, cr, -1, x, box);
+    printMissingInteractionsAtoms(mdlog, cr, *top_global, top_local->idef);
+    write_dd_pdb("dd_dump_err", 0, "dump", top_global, cr, -1, as_rvec_array(x.data()), box);
 
     std::string errorMessage;
 
@@ -456,14 +488,15 @@ static void global_atomnr_to_moltype_ind(const gmx_reverse_top_t* rt, int i_gl,
 }
 
 /*! \brief Returns the maximum number of exclusions per atom */
-static int getMaxNumExclusionsPerAtom(const t_blocka& excls)
+static int getMaxNumExclusionsPerAtom(const ListOfLists<int>& excls)
 {
     int maxNumExcls = 0;
-    for (int at = 0; at < excls.nr; at++)
+    for (gmx::index at = 0; at < excls.ssize(); at++)
     {
-        const int numExcls = excls.index[at + 1] - excls.index[at];
+        const auto list     = excls[at];
+        const int  numExcls = list.ssize();
 
-        GMX_RELEASE_ASSERT(numExcls != 1 || excls.a[excls.index[at]] == at,
+        GMX_RELEASE_ASSERT(numExcls != 1 || list[0] == at,
                            "With 1 exclusion we expect a self-exclusion");
 
         maxNumExcls = std::max(maxNumExcls, numExcls);
@@ -692,17 +725,20 @@ static gmx_reverse_top_t make_reverse_top(const gmx_mtop_t* mtop,
         rt.mbi.push_back(mbi);
     }
 
-    rt.th_work.resize(gmx_omp_nthreads_get(emntDomdec));
+    for (int th = 0; th < gmx_omp_nthreads_get(emntDomdec); th++)
+    {
+        rt.th_work.emplace_back(mtop->ffparams);
+    }
 
     return rt;
 }
 
-void dd_make_reverse_top(FILE*              fplog,
-                         gmx_domdec_t*      dd,
-                         const gmx_mtop_t*  mtop,
-                         const gmx_vsite_t* vsite,
-                         const t_inputrec*  ir,
-                         gmx_bool           bBCheck)
+void dd_make_reverse_top(FILE*                           fplog,
+                         gmx_domdec_t*                   dd,
+                         const gmx_mtop_t*               mtop,
+                         const gmx::VirtualSitesHandler* vsite,
+                         const t_inputrec*               ir,
+                         gmx_bool                        bBCheck)
 {
     if (fplog)
     {
@@ -720,10 +756,7 @@ void dd_make_reverse_top(FILE*              fplog,
             make_reverse_top(mtop, ir->efep != efepNO, !dd->comm->systemInfo.haveSplitConstraints,
                              !dd->comm->systemInfo.haveSplitSettles, bBCheck, &dd->nbonded_global);
 
-    gmx_reverse_top_t* rt = dd->reverse_top;
-
     dd->haveExclusions = false;
-    rt->n_excl_at_max  = 0;
     for (const gmx_molblock_t& molb : mtop->molblock)
     {
         const int maxNumExclusionsPerAtom = getMaxNumExclusionsPerAtom(mtop->moltype[molb.type].excls);
@@ -732,19 +765,18 @@ void dd_make_reverse_top(FILE*              fplog,
         {
             dd->haveExclusions = true;
         }
-        rt->n_excl_at_max = std::max(rt->n_excl_at_max, maxNumExclusionsPerAtom);
     }
 
-    if (vsite && vsite->numInterUpdategroupVsites > 0)
+    if (vsite && vsite->numInterUpdategroupVirtualSites() > 0)
     {
         if (fplog)
         {
             fprintf(fplog,
                     "There are %d inter update-group virtual sites,\n"
                     "will an extra communication step for selected coordinates and forces\n",
-                    vsite->numInterUpdategroupVsites);
+                    vsite->numInterUpdategroupVirtualSites());
         }
-        init_domdec_vsites(dd, vsite->numInterUpdategroupVsites);
+        init_domdec_vsites(dd, vsite->numInterUpdategroupVirtualSites());
     }
 
     if (dd->comm->systemInfo.haveSplitConstraints || dd->comm->systemInfo.haveSplitSettles)
@@ -773,18 +805,8 @@ static inline void add_ifunc_for_vsites(t_iatom*           tiatoms,
                                         int                a_gl,
                                         int                a_mol,
                                         const t_iatom*     iatoms,
-                                        t_ilist*           il)
+                                        InteractionList*   il)
 {
-    t_iatom* liatoms;
-
-    if (il->nr + 1 + nral > il->nalloc)
-    {
-        il->nalloc = over_alloc_large(il->nr + 1 + nral);
-        srenew(il->iatoms, il->nalloc);
-    }
-    liatoms = il->iatoms + il->nr;
-    il->nr += 1 + nral;
-
     /* Copy the type */
     tiatoms[0] = iatoms[0];
 
@@ -799,6 +821,7 @@ static inline void add_ifunc_for_vsites(t_iatom*           tiatoms,
         tiatoms[1] = -a_gl - 1;
     }
 
+    GMX_ASSERT(nral >= 2 && nral <= 5, "Invalid nral for vsites");
     for (int k = 2; k < 1 + nral; k++)
     {
         int ak_gl = a_gl + iatoms[k] - a_mol;
@@ -814,117 +837,85 @@ static inline void add_ifunc_for_vsites(t_iatom*           tiatoms,
         // Note that ga2la_get_home always sets the third parameter if
         // it returns TRUE
     }
-    for (int k = 0; k < 1 + nral; k++)
-    {
-        liatoms[k] = tiatoms[k];
-    }
-}
-
-/*! \brief Store a bonded interaction at the end of \p il */
-static inline void add_ifunc(int nral, const t_iatom* tiatoms, t_ilist* il)
-{
-    t_iatom* liatoms;
-    int      k;
-
-    if (il->nr + 1 + nral > il->nalloc)
-    {
-        il->nalloc = over_alloc_large(il->nr + 1 + nral);
-        srenew(il->iatoms, il->nalloc);
-    }
-    liatoms = il->iatoms + il->nr;
-    for (k = 0; k <= nral; k++)
-    {
-        liatoms[k] = tiatoms[k];
-    }
-    il->nr += 1 + nral;
+    il->push_back(tiatoms[0], nral, tiatoms + 1);
 }
 
 /*! \brief Store a position restraint in idef and iatoms, complex because the parameters are different for each entry */
-static void add_posres(int                   mol,
-                       int                   a_mol,
-                       int                   numAtomsInMolecule,
-                       const gmx_molblock_t* molb,
-                       t_iatom*              iatoms,
-                       const t_iparams*      ip_in,
-                       t_idef*               idef)
+static void add_posres(int                     mol,
+                       int                     a_mol,
+                       int                     numAtomsInMolecule,
+                       const gmx_molblock_t*   molb,
+                       t_iatom*                iatoms,
+                       const t_iparams*        ip_in,
+                       InteractionDefinitions* idef)
 {
-    int        n, a_molb;
-    t_iparams* ip;
-
     /* This position restraint has not been added yet,
      * so it's index is the current number of position restraints.
      */
-    n = idef->il[F_POSRES].nr / 2;
-    if (n + 1 > idef->iparams_posres_nalloc)
-    {
-        idef->iparams_posres_nalloc = over_alloc_dd(n + 1);
-        srenew(idef->iparams_posres, idef->iparams_posres_nalloc);
-    }
-    ip = &idef->iparams_posres[n];
-    /* Copy the force constants */
-    *ip = ip_in[iatoms[0]];
+    const int n = idef->il[F_POSRES].size() / 2;
 
     /* Get the position restraint coordinates from the molblock */
-    a_molb = mol * numAtomsInMolecule + a_mol;
+    const int a_molb = mol * numAtomsInMolecule + a_mol;
     GMX_ASSERT(a_molb < ssize(molb->posres_xA),
                "We need a sufficient number of position restraint coordinates");
-    ip->posres.pos0A[XX] = molb->posres_xA[a_molb][XX];
-    ip->posres.pos0A[YY] = molb->posres_xA[a_molb][YY];
-    ip->posres.pos0A[ZZ] = molb->posres_xA[a_molb][ZZ];
+    /* Copy the force constants */
+    t_iparams ip        = ip_in[iatoms[0]];
+    ip.posres.pos0A[XX] = molb->posres_xA[a_molb][XX];
+    ip.posres.pos0A[YY] = molb->posres_xA[a_molb][YY];
+    ip.posres.pos0A[ZZ] = molb->posres_xA[a_molb][ZZ];
     if (!molb->posres_xB.empty())
     {
-        ip->posres.pos0B[XX] = molb->posres_xB[a_molb][XX];
-        ip->posres.pos0B[YY] = molb->posres_xB[a_molb][YY];
-        ip->posres.pos0B[ZZ] = molb->posres_xB[a_molb][ZZ];
+        ip.posres.pos0B[XX] = molb->posres_xB[a_molb][XX];
+        ip.posres.pos0B[YY] = molb->posres_xB[a_molb][YY];
+        ip.posres.pos0B[ZZ] = molb->posres_xB[a_molb][ZZ];
     }
     else
     {
-        ip->posres.pos0B[XX] = ip->posres.pos0A[XX];
-        ip->posres.pos0B[YY] = ip->posres.pos0A[YY];
-        ip->posres.pos0B[ZZ] = ip->posres.pos0A[ZZ];
+        ip.posres.pos0B[XX] = ip.posres.pos0A[XX];
+        ip.posres.pos0B[YY] = ip.posres.pos0A[YY];
+        ip.posres.pos0B[ZZ] = ip.posres.pos0A[ZZ];
     }
-    /* Set the parameter index for idef->iparams_posre */
+    /* Set the parameter index for idef->iparams_posres */
     iatoms[0] = n;
+    idef->iparams_posres.push_back(ip);
+
+    GMX_ASSERT(int(idef->iparams_posres.size()) == n + 1,
+               "The index of the parameter type should match n");
 }
 
 /*! \brief Store a flat-bottomed position restraint in idef and iatoms, complex because the parameters are different for each entry */
-static void add_fbposres(int                   mol,
-                         int                   a_mol,
-                         int                   numAtomsInMolecule,
-                         const gmx_molblock_t* molb,
-                         t_iatom*              iatoms,
-                         const t_iparams*      ip_in,
-                         t_idef*               idef)
+static void add_fbposres(int                     mol,
+                         int                     a_mol,
+                         int                     numAtomsInMolecule,
+                         const gmx_molblock_t*   molb,
+                         t_iatom*                iatoms,
+                         const t_iparams*        ip_in,
+                         InteractionDefinitions* idef)
 {
-    int        n, a_molb;
-    t_iparams* ip;
-
     /* This flat-bottom position restraint has not been added yet,
      * so it's index is the current number of position restraints.
      */
-    n = idef->il[F_FBPOSRES].nr / 2;
-    if (n + 1 > idef->iparams_fbposres_nalloc)
-    {
-        idef->iparams_fbposres_nalloc = over_alloc_dd(n + 1);
-        srenew(idef->iparams_fbposres, idef->iparams_fbposres_nalloc);
-    }
-    ip = &idef->iparams_fbposres[n];
-    /* Copy the force constants */
-    *ip = ip_in[iatoms[0]];
+    const int n = idef->il[F_FBPOSRES].size() / 2;
 
     /* Get the position restraint coordinats from the molblock */
-    a_molb = mol * numAtomsInMolecule + a_mol;
+    const int a_molb = mol * numAtomsInMolecule + a_mol;
     GMX_ASSERT(a_molb < ssize(molb->posres_xA),
                "We need a sufficient number of position restraint coordinates");
+    /* Copy the force constants */
+    t_iparams ip = ip_in[iatoms[0]];
     /* Take reference positions from A position of normal posres */
-    ip->fbposres.pos0[XX] = molb->posres_xA[a_molb][XX];
-    ip->fbposres.pos0[YY] = molb->posres_xA[a_molb][YY];
-    ip->fbposres.pos0[ZZ] = molb->posres_xA[a_molb][ZZ];
+    ip.fbposres.pos0[XX] = molb->posres_xA[a_molb][XX];
+    ip.fbposres.pos0[YY] = molb->posres_xA[a_molb][YY];
+    ip.fbposres.pos0[ZZ] = molb->posres_xA[a_molb][ZZ];
 
     /* Note: no B-type for flat-bottom posres */
 
-    /* Set the parameter index for idef->iparams_posre */
+    /* Set the parameter index for idef->iparams_fbposres */
     iatoms[0] = n;
+    idef->iparams_fbposres.push_back(ip);
+
+    GMX_ASSERT(int(idef->iparams_fbposres.size()) == n + 1,
+               "The index of the parameter type should match n");
 }
 
 /*! \brief Store a virtual site interaction, complex because of PBC and recursion */
@@ -938,7 +929,7 @@ static void add_vsite(const gmx_ga2la_t&       ga2la,
                       int                      a_gl,
                       int                      a_mol,
                       const t_iatom*           iatoms,
-                      t_idef*                  idef)
+                      InteractionDefinitions*  idef)
 {
     int     k;
     t_iatom tiatoms[1 + MAXATOMLIST];
@@ -998,42 +989,8 @@ static real dd_dist2(t_pbc* pbc_null, const rvec* x, const int i, int j)
     return norm2(dx);
 }
 
-/*! \brief Append t_blocka block structures 1 to nsrc in src to *dest */
-static void combine_blocka(t_blocka* dest, gmx::ArrayRef<const thread_work_t> src)
-{
-    int ni = src.back().excl.nr;
-    int na = 0;
-    for (const thread_work_t& th_work : src)
-    {
-        na += th_work.excl.nra;
-    }
-    if (ni + 1 > dest->nalloc_index)
-    {
-        dest->nalloc_index = over_alloc_large(ni + 1);
-        srenew(dest->index, dest->nalloc_index);
-    }
-    if (dest->nra + na > dest->nalloc_a)
-    {
-        dest->nalloc_a = over_alloc_large(dest->nra + na);
-        srenew(dest->a, dest->nalloc_a);
-    }
-    for (gmx::index s = 1; s < src.ssize(); s++)
-    {
-        for (int i = dest->nr + 1; i < src[s].excl.nr + 1; i++)
-        {
-            dest->index[i] = dest->nra + src[s].excl.index[i];
-        }
-        for (int i = 0; i < src[s].excl.nra; i++)
-        {
-            dest->a[dest->nra + i] = src[s].excl.a[i];
-        }
-        dest->nr = src[s].excl.nr;
-        dest->nra += src[s].excl.nra;
-    }
-}
-
 /*! \brief Append t_idef structures 1 to nsrc in src to *dest */
-static void combine_idef(t_idef* dest, gmx::ArrayRef<const thread_work_t> src)
+static void combine_idef(InteractionDefinitions* dest, gmx::ArrayRef<const thread_work_t> src)
 {
     int ftype;
 
@@ -1042,67 +999,45 @@ static void combine_idef(t_idef* dest, gmx::ArrayRef<const thread_work_t> src)
         int n = 0;
         for (gmx::index s = 1; s < src.ssize(); s++)
         {
-            n += src[s].idef.il[ftype].nr;
+            n += src[s].idef.il[ftype].size();
         }
         if (n > 0)
         {
-            t_ilist* ild;
-
-            ild = &dest->il[ftype];
-
-            if (ild->nr + n > ild->nalloc)
-            {
-                ild->nalloc = over_alloc_large(ild->nr + n);
-                srenew(ild->iatoms, ild->nalloc);
-            }
-
             for (gmx::index s = 1; s < src.ssize(); s++)
             {
-                const t_ilist& ils = src[s].idef.il[ftype];
-
-                for (int i = 0; i < ils.nr; i++)
-                {
-                    ild->iatoms[ild->nr + i] = ils.iatoms[i];
-                }
-
-                ild->nr += ils.nr;
+                dest->il[ftype].append(src[s].idef.il[ftype]);
             }
 
             /* Position restraints need an additional treatment */
             if (ftype == F_POSRES || ftype == F_FBPOSRES)
             {
-                int nposres = dest->il[ftype].nr / 2;
-                // TODO: Simplify this code using std::vector
-                t_iparams*& iparams_dest =
+                int                     nposres = dest->il[ftype].size() / 2;
+                std::vector<t_iparams>& iparams_dest =
                         (ftype == F_POSRES ? dest->iparams_posres : dest->iparams_fbposres);
-                int& posres_nalloc = (ftype == F_POSRES ? dest->iparams_posres_nalloc
-                                                        : dest->iparams_fbposres_nalloc);
-                if (nposres > posres_nalloc)
-                {
-                    posres_nalloc = over_alloc_large(nposres);
-                    srenew(iparams_dest, posres_nalloc);
-                }
 
                 /* Set nposres to the number of original position restraints in dest */
                 for (gmx::index s = 1; s < src.ssize(); s++)
                 {
-                    nposres -= src[s].idef.il[ftype].nr / 2;
+                    nposres -= src[s].idef.il[ftype].size() / 2;
                 }
 
                 for (gmx::index s = 1; s < src.ssize(); s++)
                 {
-                    const t_iparams* iparams_src = (ftype == F_POSRES ? src[s].idef.iparams_posres
-                                                                      : src[s].idef.iparams_fbposres);
+                    const std::vector<t_iparams>& iparams_src =
+                            (ftype == F_POSRES ? src[s].idef.iparams_posres : src[s].idef.iparams_fbposres);
+                    iparams_dest.insert(iparams_dest.end(), iparams_src.begin(), iparams_src.end());
 
-                    for (int i = 0; i < src[s].idef.il[ftype].nr / 2; i++)
+                    /* Correct the indices into iparams_posres */
+                    for (int i = 0; i < src[s].idef.il[ftype].size() / 2; i++)
                     {
                         /* Correct the index into iparams_posres */
                         dest->il[ftype].iatoms[nposres * 2] = nposres;
-                        /* Copy the position restraint force parameters */
-                        iparams_dest[nposres] = iparams_src[i];
                         nposres++;
                     }
                 }
+                GMX_RELEASE_ASSERT(
+                        int(iparams_dest.size()) == nposres,
+                        "The number of parameters should match the number of restraints");
             }
         }
     }
@@ -1130,7 +1065,7 @@ static inline void check_assign_interactions_atom(int                       i,
                                                   t_pbc*                    pbc_null,
                                                   rvec*                     cg_cm,
                                                   const t_iparams*          ip_in,
-                                                  t_idef*                   idef,
+                                                  InteractionDefinitions*   idef,
                                                   int                       iz,
                                                   gmx_bool                  bBCheck,
                                                   int*                      nbonded_local)
@@ -1303,7 +1238,7 @@ static inline void check_assign_interactions_atom(int                       i,
             if (bUse)
             {
                 /* Add this interaction to the local topology */
-                add_ifunc(nral, tiatoms, &idef->il[ftype]);
+                idef->il[ftype].push_back(tiatoms[0], nral, tiatoms + 1);
                 /* Sum so we can check in global_stat
                  * if we have everything.
                  */
@@ -1332,7 +1267,7 @@ static int make_bondeds_zone(gmx_domdec_t*                      dd,
                              t_pbc*                             pbc_null,
                              rvec*                              cg_cm,
                              const t_iparams*                   ip_in,
-                             t_idef*                            idef,
+                             InteractionDefinitions*            idef,
                              int                                izone,
                              const gmx::Range<int>&             atomRange)
 {
@@ -1378,66 +1313,38 @@ static int make_bondeds_zone(gmx_domdec_t*                      dd,
     return nbonded_local;
 }
 
-/*! \brief Set the exclusion data for i-zone \p iz for the case of no exclusions */
-static void set_no_exclusions_zone(const gmx_domdec_zones_t* zones, int iz, t_blocka* lexcls)
-{
-    for (int a = zones->cg_range[iz]; a < zones->cg_range[iz + 1]; a++)
-    {
-        lexcls->index[a + 1] = lexcls->nra;
-    }
-}
-
 /*! \brief Set the exclusion data for i-zone \p iz */
 static void make_exclusions_zone(gmx_domdec_t*                     dd,
                                  gmx_domdec_zones_t*               zones,
                                  const std::vector<gmx_moltype_t>& moltype,
                                  const int*                        cginfo,
-                                 t_blocka*                         lexcls,
+                                 ListOfLists<int>*                 lexcls,
                                  int                               iz,
                                  int                               at_start,
                                  int                               at_end,
                                  const gmx::ArrayRef<const int>    intermolecularExclusionGroup)
 {
-    int n_excl_at_max, n, at;
-
     const gmx_ga2la_t& ga2la = *dd->ga2la;
 
     const auto& jAtomRange = zones->iZones[iz].jAtomRange;
 
-    n_excl_at_max = dd->reverse_top->n_excl_at_max;
-
-    /* We set the end index, but note that we might not start at zero here */
-    lexcls->nr = at_end;
+    const gmx::index oldNumLists = lexcls->ssize();
 
-    n = lexcls->nra;
-    for (at = at_start; at < at_end; at++)
+    std::vector<int> exclusionsForAtom;
+    for (int at = at_start; at < at_end; at++)
     {
-        if (n + 1000 > lexcls->nalloc_a)
-        {
-            lexcls->nalloc_a = over_alloc_large(n + 1000);
-            srenew(lexcls->a, lexcls->nalloc_a);
-        }
+        exclusionsForAtom.clear();
 
         if (GET_CGINFO_EXCL_INTER(cginfo[at]))
         {
-            int             a_gl, mb, mt, mol, a_mol, j;
-            const t_blocka* excls;
-
-            if (n + n_excl_at_max > lexcls->nalloc_a)
-            {
-                lexcls->nalloc_a = over_alloc_large(n + n_excl_at_max);
-                srenew(lexcls->a, lexcls->nalloc_a);
-            }
+            int a_gl, mb, mt, mol, a_mol;
 
             /* Copy the exclusions from the global top */
-            lexcls->index[at] = n;
-            a_gl              = dd->globalAtomIndices[at];
+            a_gl = dd->globalAtomIndices[at];
             global_atomnr_to_moltype_ind(dd->reverse_top, a_gl, &mb, &mt, &mol, &a_mol);
-            excls = &moltype[mt].excls;
-            for (j = excls->index[a_mol]; j < excls->index[a_mol + 1]; j++)
+            const auto excls = moltype[mt].excls[a_mol];
+            for (const int aj_mol : excls)
             {
-                const int aj_mol = excls->a[j];
-
                 if (const auto* jEntry = ga2la.find(a_gl + aj_mol - a_mol))
                 {
                     /* This check is not necessary, but it can reduce
@@ -1446,16 +1353,11 @@ static void make_exclusions_zone(gmx_domdec_t*                     dd,
                      */
                     if (jAtomRange.isInRange(jEntry->la))
                     {
-                        lexcls->a[n++] = jEntry->la;
+                        exclusionsForAtom.push_back(jEntry->la);
                     }
                 }
             }
         }
-        else
-        {
-            /* We don't need exclusions for this atom */
-            lexcls->index[at] = n;
-        }
 
         bool isExcludedAtom = !intermolecularExclusionGroup.empty()
                               && std::find(intermolecularExclusionGroup.begin(),
@@ -1464,104 +1366,40 @@ static void make_exclusions_zone(gmx_domdec_t*                     dd,
 
         if (isExcludedAtom)
         {
-            if (n + intermolecularExclusionGroup.ssize() > lexcls->nalloc_a)
-            {
-                lexcls->nalloc_a = over_alloc_large(n + intermolecularExclusionGroup.size());
-                srenew(lexcls->a, lexcls->nalloc_a);
-            }
             for (int qmAtomGlobalIndex : intermolecularExclusionGroup)
             {
                 if (const auto* entry = dd->ga2la->find(qmAtomGlobalIndex))
                 {
-                    lexcls->a[n++] = entry->la;
+                    exclusionsForAtom.push_back(entry->la);
                 }
             }
         }
-    }
-
-    lexcls->index[lexcls->nr] = n;
-    lexcls->nra               = n;
-}
-
 
-/*! \brief Ensure we have enough space in \p ba for \p nindex_max indices */
-static void check_alloc_index(t_blocka* ba, int nindex_max)
-{
-    if (nindex_max + 1 > ba->nalloc_index)
-    {
-        ba->nalloc_index = over_alloc_dd(nindex_max + 1);
-        srenew(ba->index, ba->nalloc_index);
+        /* Append the exclusions for this atom to the topology */
+        lexcls->pushBack(exclusionsForAtom);
     }
-}
-
-/*! \brief Ensure that we have enough space for exclusion storate in \p lexcls */
-static void check_exclusions_alloc(const gmx_domdec_t* dd, const gmx_domdec_zones_t* zones, t_blocka* lexcls)
-{
-    const int nr = zones->iZones.back().iAtomRange.end();
-
-    check_alloc_index(lexcls, nr);
 
-    for (size_t thread = 1; thread < dd->reverse_top->th_work.size(); thread++)
-    {
-        check_alloc_index(&dd->reverse_top->th_work[thread].excl, nr);
-    }
-}
-
-/*! \brief Set the total count indexes for the local exclusions, needed by several functions */
-static void finish_local_exclusions(gmx_domdec_t* dd, gmx_domdec_zones_t* zones, t_blocka* lexcls)
-{
-    const gmx::Range<int> nonhomeIzonesAtomRange(zones->iZones[0].iAtomRange.end(),
-                                                 zones->iZones.back().iAtomRange.end());
-
-    if (!dd->haveExclusions)
-    {
-        /* There are no exclusions involving non-home charge groups,
-         * but we need to set the indices for neighborsearching.
-         */
-        for (int la : nonhomeIzonesAtomRange)
-        {
-            lexcls->index[la] = lexcls->nra;
-        }
-
-        /* nr is only used to loop over the exclusions for Ewald and RF,
-         * so we can set it to the number of home atoms for efficiency.
-         */
-        lexcls->nr = nonhomeIzonesAtomRange.begin();
-    }
-    else
-    {
-        lexcls->nr = nonhomeIzonesAtomRange.end();
-    }
-}
-
-/*! \brief Clear a t_idef struct */
-static void clear_idef(t_idef* idef)
-{
-    int ftype;
-
-    /* Clear the counts */
-    for (ftype = 0; ftype < F_NRE; ftype++)
-    {
-        idef->il[ftype].nr = 0;
-    }
+    GMX_RELEASE_ASSERT(
+            lexcls->ssize() - oldNumLists == at_end - at_start,
+            "The number of exclusion list should match the number of atoms in the range");
 }
 
 /*! \brief Generate and store all required local bonded interactions in \p idef and local exclusions in \p lexcls */
-static int make_local_bondeds_excls(gmx_domdec_t*       dd,
-                                    gmx_domdec_zones_t* zones,
-                                    const gmx_mtop_t*   mtop,
-                                    const int*          cginfo,
-                                    gmx_bool            bRCheckMB,
-                                    ivec                rcheck,
-                                    gmx_bool            bRCheck2B,
-                                    real                rc,
-                                    t_pbc*              pbc_null,
-                                    rvec*               cg_cm,
-                                    t_idef*             idef,
-                                    t_blocka*           lexcls,
-                                    int*                excl_count)
+static int make_local_bondeds_excls(gmx_domdec_t*           dd,
+                                    gmx_domdec_zones_t*     zones,
+                                    const gmx_mtop_t*       mtop,
+                                    const int*              cginfo,
+                                    gmx_bool                bRCheckMB,
+                                    ivec                    rcheck,
+                                    gmx_bool                bRCheck2B,
+                                    real                    rc,
+                                    t_pbc*                  pbc_null,
+                                    rvec*                   cg_cm,
+                                    InteractionDefinitions* idef,
+                                    ListOfLists<int>*       lexcls,
+                                    int*                    excl_count)
 {
-    int                nzone_bondeds, nzone_excl;
+    int                nzone_bondeds;
     int                cg0, cg1;
     real               rc2;
     int                nbonded_local;
@@ -1579,29 +1417,18 @@ static int make_local_bondeds_excls(gmx_domdec_t*       dd,
         nzone_bondeds = 1;
     }
 
-    if (dd->haveExclusions)
-    {
-        /* We only use exclusions from i-zones to i- and j-zones */
-        nzone_excl = zones->iZones.size();
-    }
-    else
-    {
-        /* There are no exclusions and only zone 0 sees itself */
-        nzone_excl = 1;
-    }
-
-    check_exclusions_alloc(dd, zones, lexcls);
+    /* We only use exclusions from i-zones to i- and j-zones */
+    const int numIZonesForExclusions = (dd->haveExclusions ? zones->iZones.size() : 0);
 
     rt = dd->reverse_top;
 
     rc2 = rc * rc;
 
     /* Clear the counts */
-    clear_idef(idef);
+    idef->clear();
     nbonded_local = 0;
 
-    lexcls->nr  = 0;
-    lexcls->nra = 0;
+    lexcls->clear();
     *excl_count = 0;
 
     for (int izone = 0; izone < nzone_bondeds; izone++)
@@ -1615,9 +1442,8 @@ static int make_local_bondeds_excls(gmx_domdec_t*       dd,
         {
             try
             {
-                int       cg0t, cg1t;
-                t_idef*   idef_t;
-                t_blocka* excl_t;
+                int                     cg0t, cg1t;
+                InteractionDefinitions* idef_t;
 
                 cg0t = cg0 + ((cg1 - cg0) * thread) / numThreads;
                 cg1t = cg0 + ((cg1 - cg0) * (thread + 1)) / numThreads;
@@ -1629,24 +1455,26 @@ static int make_local_bondeds_excls(gmx_domdec_t*       dd,
                 else
                 {
                     idef_t = &rt->th_work[thread].idef;
-                    clear_idef(idef_t);
+                    idef_t->clear();
                 }
 
                 rt->th_work[thread].nbonded = make_bondeds_zone(
                         dd, zones, mtop->molblock, bRCheckMB, rcheck, bRCheck2B, rc2, pbc_null,
-                        cg_cm, idef->iparams, idef_t, izone, gmx::Range<int>(cg0t, cg1t));
+                        cg_cm, idef->iparams.data(), idef_t, izone, gmx::Range<int>(cg0t, cg1t));
 
-                if (izone < nzone_excl)
+                if (izone < numIZonesForExclusions)
                 {
+                    ListOfLists<int>* excl_t;
                     if (thread == 0)
                     {
+                        // Thread 0 stores exclusions directly in the final storage
                         excl_t = lexcls;
                     }
                     else
                     {
-                        excl_t      = &rt->th_work[thread].excl;
-                        excl_t->nr  = 0;
-                        excl_t->nra = 0;
+                        // Threads > 0 store in temporary storage, starting at list index 0
+                        excl_t = &rt->th_work[thread].excl;
+                        excl_t->clear();
                     }
 
                     /* No charge groups and no distance check required */
@@ -1667,13 +1495,12 @@ static int make_local_bondeds_excls(gmx_domdec_t*       dd,
             nbonded_local += th_work.nbonded;
         }
 
-        if (izone < nzone_excl)
+        if (izone < numIZonesForExclusions)
         {
-            if (rt->th_work.size() > 1)
+            for (std::size_t th = 1; th < rt->th_work.size(); th++)
             {
-                combine_blocka(lexcls, rt->th_work);
+                lexcls->appendListOfLists(rt->th_work[th].excl);
             }
-
             for (const thread_work_t& th_work : rt->th_work)
             {
                 *excl_count += th_work.excl_count;
@@ -1681,18 +1508,9 @@ static int make_local_bondeds_excls(gmx_domdec_t*       dd,
         }
     }
 
-    /* Some zones might not have exclusions, but some code still needs to
-     * loop over the index, so we set the indices here.
-     */
-    for (size_t iZone = nzone_excl; iZone < zones->iZones.size(); iZone++)
-    {
-        set_no_exclusions_zone(zones, iZone, lexcls);
-    }
-
-    finish_local_exclusions(dd, zones, lexcls);
     if (debug)
     {
-        fprintf(debug, "We have %d exclusions, check count %d\n", lexcls->nra, *excl_count);
+        fprintf(debug, "We have %d exclusions, check count %d\n", lexcls->numElements(), *excl_count);
     }
 
     return nbonded_local;
@@ -1739,9 +1557,10 @@ void dd_make_local_top(gmx_domdec_t*       dd,
             /* Only need to check for dimensions where the part of the box
              * that is not communicated is smaller than the cut-off.
              */
-            if (d < npbcdim && dd->nc[d] > 1 && (dd->nc[d] - npulse[d]) * cellsize_min[d] < 2 * rc)
+            if (d < npbcdim && dd->numCells[d] > 1
+                && (dd->numCells[d] - npulse[d]) * cellsize_min[d] < 2 * rc)
             {
-                if (dd->nc[d] == 2)
+                if (dd->numCells[d] == 2)
                 {
                     rcheck[d] = TRUE;
                     bRCheckMB = TRUE;
@@ -1762,7 +1581,7 @@ void dd_make_local_top(gmx_domdec_t*       dd,
         {
             if (fr->bMolPBC)
             {
-                pbc_null = set_pbc_dd(&pbc, fr->ePBC, dd->nc, TRUE, box);
+                pbc_null = set_pbc_dd(&pbc, fr->pbcType, dd->numCells, TRUE, box);
             }
             else
             {
@@ -1779,8 +1598,6 @@ void dd_make_local_top(gmx_domdec_t*       dd,
      * we can only do this when we have the charge arrays.
      */
     ltop->idef.ilsort = ilsortUNKNOWN;
-
-    ltop->atomtypes = mtop.atomtypes;
 }
 
 void dd_sort_local_top(gmx_domdec_t* dd, const t_mdatoms* mdatoms, gmx_localtop_t* ltop)
@@ -1795,21 +1612,6 @@ void dd_sort_local_top(gmx_domdec_t* dd, const t_mdatoms* mdatoms, gmx_localtop_
     }
 }
 
-void dd_init_local_top(const gmx_mtop_t& top_global, gmx_localtop_t* top)
-{
-    /* TODO: Get rid of the const casts below, e.g. by using a reference */
-    top->idef.ntypes     = top_global.ffparams.numTypes();
-    top->idef.atnr       = top_global.ffparams.atnr;
-    top->idef.functype   = const_cast<t_functype*>(top_global.ffparams.functype.data());
-    top->idef.iparams    = const_cast<t_iparams*>(top_global.ffparams.iparams.data());
-    top->idef.fudgeQQ    = top_global.ffparams.fudgeQQ;
-    top->idef.cmap_grid  = new gmx_cmap_t;
-    *top->idef.cmap_grid = top_global.ffparams.cmap_grid;
-
-    top->idef.ilsort        = ilsortUNKNOWN;
-    top->useInDomainDecomp_ = true;
-}
-
 void dd_init_local_state(gmx_domdec_t* dd, const t_state* state_global, t_state* state_local)
 {
     int buf[NITEM_DD_INIT_LOCAL_STATE];
@@ -1859,7 +1661,7 @@ static void check_link(t_blocka* link, int cg_gl, int cg_gl_j)
     }
 }
 
-t_blocka* makeBondedLinks(const gmx_mtop_t* mtop, cginfo_mb_t* cginfo_mb)
+t_blocka* makeBondedLinks(const gmx_mtop_t& mtop, gmx::ArrayRef<cginfo_mb_t> cginfo_mb)
 {
     t_blocka*    link;
     cginfo_mb_t* cgi_mb;
@@ -1870,35 +1672,35 @@ t_blocka* makeBondedLinks(const gmx_mtop_t* mtop, cginfo_mb_t* cginfo_mb)
      */
 
     reverse_ilist_t ril_intermol;
-    if (mtop->bIntermolecularInteractions)
+    if (mtop.bIntermolecularInteractions)
     {
         t_atoms atoms;
 
-        atoms.nr   = mtop->natoms;
+        atoms.nr   = mtop.natoms;
         atoms.atom = nullptr;
 
-        GMX_RELEASE_ASSERT(mtop->intermolecular_ilist,
+        GMX_RELEASE_ASSERT(mtop.intermolecular_ilist,
                            "We should have an ilist when intermolecular interactions are on");
 
-        make_reverse_ilist(*mtop->intermolecular_ilist, &atoms, FALSE, FALSE, FALSE, TRUE, &ril_intermol);
+        make_reverse_ilist(*mtop.intermolecular_ilist, &atoms, FALSE, FALSE, FALSE, TRUE, &ril_intermol);
     }
 
     snew(link, 1);
-    snew(link->index, mtop->natoms + 1);
+    snew(link->index, mtop.natoms + 1);
     link->nalloc_a = 0;
     link->a        = nullptr;
 
     link->index[0] = 0;
     int cg_offset  = 0;
     int ncgi       = 0;
-    for (size_t mb = 0; mb < mtop->molblock.size(); mb++)
+    for (size_t mb = 0; mb < mtop.molblock.size(); mb++)
     {
-        const gmx_molblock_t& molb = mtop->molblock[mb];
+        const gmx_molblock_t& molb = mtop.molblock[mb];
         if (molb.nmol == 0)
         {
             continue;
         }
-        const gmx_moltype_t& molt = mtop->moltype[molb.type];
+        const gmx_moltype_t& molt = mtop.moltype[molb.type];
         /* Make a reverse ilist in which the interactions are linked
          * to all atoms, not only the first atom as in gmx_reverse_top.
          * The constraints are discarded here.
@@ -1909,7 +1711,7 @@ t_blocka* makeBondedLinks(const gmx_mtop_t* mtop, cginfo_mb_t* cginfo_mb)
         cgi_mb = &cginfo_mb[mb];
 
         int mol;
-        for (mol = 0; mol < (mtop->bIntermolecularInteractions ? molb.nmol : 1); mol++)
+        for (mol = 0; mol < (mtop.bIntermolecularInteractions ? molb.nmol : 1); mol++)
         {
             for (int a = 0; a < molt.atoms.nr; a++)
             {
@@ -1933,7 +1735,7 @@ t_blocka* makeBondedLinks(const gmx_mtop_t* mtop, cginfo_mb_t* cginfo_mb)
                     i += nral_rt(ftype);
                 }
 
-                if (mtop->bIntermolecularInteractions)
+                if (mtop.bIntermolecularInteractions)
                 {
                     int i = ril_intermol.index[cg_gl];
                     while (i < ril_intermol.index[cg_gl + 1])
@@ -1999,7 +1801,7 @@ t_blocka* makeBondedLinks(const gmx_mtop_t* mtop, cginfo_mb_t* cginfo_mb)
 
     if (debug)
     {
-        fprintf(debug, "Of the %d atoms %d are linked via bonded interactions\n", mtop->natoms, ncgi);
+        fprintf(debug, "Of the %d atoms %d are linked via bonded interactions\n", mtop.natoms, ncgi);
     }
 
     return link;
@@ -2025,11 +1827,11 @@ static void update_max_bonded_distance(real r2, int ftype, int a1, int a2, bonde
     }
 }
 
-/*! \brief Set the distance, function type and atom indices for the longest distance between charge-groups of molecule type \p molt for two-body and multi-body bonded interactions */
+/*! \brief Set the distance, function type and atom indices for the longest distance between atoms of molecule type \p molt for two-body and multi-body bonded interactions */
 static void bonded_cg_distance_mol(const gmx_moltype_t* molt,
                                    gmx_bool             bBCheck,
                                    gmx_bool             bExcl,
-                                   rvec*                cg_cm,
+                                   ArrayRef<const RVec> x,
                                    bonded_distance_t*   bd_2b,
                                    bonded_distance_t*   bd_mb)
 {
@@ -2051,7 +1853,7 @@ static void bonded_cg_distance_mol(const gmx_moltype_t* molt,
                             int atomJ = il.iatoms[i + 1 + aj];
                             if (atomI != atomJ)
                             {
-                                real rij2 = distance2(cg_cm[atomI], cg_cm[atomJ]);
+                                real rij2 = distance2(x[atomI], x[atomJ]);
 
                                 update_max_bonded_distance(rij2, ftype, atomI, atomJ,
                                                            (nral == 2) ? bd_2b : bd_mb);
@@ -2064,15 +1866,14 @@ static void bonded_cg_distance_mol(const gmx_moltype_t* molt,
     }
     if (bExcl)
     {
-        const t_blocka* excls = &molt->excls;
-        for (int ai = 0; ai < excls->nr; ai++)
+        const auto& excls = molt->excls;
+        for (gmx::index ai = 0; ai < excls.ssize(); ai++)
         {
-            for (int j = excls->index[ai]; j < excls->index[ai + 1]; j++)
+            for (const int aj : excls[ai])
             {
-                int aj = excls->a[j];
                 if (ai != aj)
                 {
-                    real rij2 = distance2(cg_cm[ai], cg_cm[aj]);
+                    real rij2 = distance2(x[ai], x[aj]);
 
                     /* There is no function type for exclusions, use -1 */
                     update_max_bonded_distance(rij2, -1, ai, aj, bd_2b);
@@ -2085,15 +1886,15 @@ static void bonded_cg_distance_mol(const gmx_moltype_t* molt,
 /*! \brief Set the distance, function type and atom indices for the longest atom distance involved in intermolecular interactions for two-body and multi-body bonded interactions */
 static void bonded_distance_intermol(const InteractionLists& ilists_intermol,
                                      gmx_bool                bBCheck,
-                                     const rvec*             x,
-                                     int                     ePBC,
+                                     ArrayRef<const RVec>    x,
+                                     PbcType                 pbcType,
                                      const matrix            box,
                                      bonded_distance_t*      bd_2b,
                                      bonded_distance_t*      bd_mb)
 {
     t_pbc pbc;
 
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
 
     for (int ftype = 0; ftype < F_NRE; ftype++)
     {
@@ -2136,7 +1937,7 @@ static bool moltypeHasVsite(const gmx_moltype_t& molt)
     bool hasVsite = false;
     for (int i = 0; i < F_NRE; i++)
     {
-        if ((interaction_function[i].flags & IF_VSITE) && molt.ilist[i].size() > 0)
+        if ((interaction_function[i].flags & IF_VSITE) && !molt.ilist[i].empty())
         {
             hasVsite = true;
         }
@@ -2148,25 +1949,25 @@ static bool moltypeHasVsite(const gmx_moltype_t& molt)
 //! Returns coordinates not broken over PBC for a molecule
 static void getWholeMoleculeCoordinates(const gmx_moltype_t*  molt,
                                         const gmx_ffparams_t* ffparams,
-                                        int                   ePBC,
+                                        PbcType               pbcType,
                                         t_graph*              graph,
                                         const matrix          box,
-                                        const rvec*           x,
-                                        rvec*                 xs)
+                                        ArrayRef<const RVec>  x,
+                                        ArrayRef<RVec>        xs)
 {
     int n, i;
 
-    if (ePBC != epbcNONE)
+    if (pbcType != PbcType::No)
     {
-        mk_mshift(nullptr, graph, ePBC, box, x);
+        mk_mshift(nullptr, graph, pbcType, box, as_rvec_array(x.data()));
 
-        shift_x(graph, box, x, xs);
+        shift_x(graph, box, as_rvec_array(x.data()), as_rvec_array(xs.data()));
         /* 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(nullptr, graph, ePBC, box, xs);
+        mk_mshift(nullptr, graph, pbcType, box, as_rvec_array(xs.data()));
     }
     else
     {
@@ -2183,26 +1984,14 @@ static void getWholeMoleculeCoordinates(const gmx_moltype_t*  molt,
 
     if (moltypeHasVsite(*molt))
     {
-        /* Convert to old, deprecated format */
-        t_ilist ilist[F_NRE];
-        for (int ftype = 0; ftype < F_NRE; ftype++)
-        {
-            if (interaction_function[ftype].flags & IF_VSITE)
-            {
-                ilist[ftype].nr     = molt->ilist[ftype].size();
-                ilist[ftype].iatoms = const_cast<int*>(molt->ilist[ftype].iatoms.data());
-            }
-        }
-
-        construct_vsites(nullptr, xs, 0.0, nullptr, ffparams->iparams.data(), ilist, epbcNONE, TRUE,
-                         nullptr, nullptr);
+        gmx::constructVirtualSites(xs, ffparams->iparams, molt->ilist);
     }
 }
 
 void dd_bonded_cg_distance(const gmx::MDLogger& mdlog,
                            const gmx_mtop_t*    mtop,
                            const t_inputrec*    ir,
-                           const rvec*          x,
+                           ArrayRef<const RVec> x,
                            const matrix         box,
                            gmx_bool             bBCheck,
                            real*                r_2b,
@@ -2210,8 +1999,6 @@ void dd_bonded_cg_distance(const gmx::MDLogger& mdlog,
 {
     gmx_bool          bExclRequired;
     int               at_offset;
-    t_graph           graph;
-    rvec*             xs;
     bonded_distance_t bd_2b = { 0, -1, -1, -1 };
     bonded_distance_t bd_mb = { 0, -1, -1, -1 };
 
@@ -2229,16 +2016,17 @@ void dd_bonded_cg_distance(const gmx::MDLogger& mdlog,
         }
         else
         {
-            if (ir->ePBC != epbcNONE)
+            t_graph graph;
+            if (ir->pbcType != PbcType::No)
             {
-                mk_graph_moltype(molt, &graph);
+                graph = mk_graph_moltype(molt);
             }
 
-            snew(xs, molt.atoms.nr);
+            std::vector<RVec> xs(molt.atoms.nr);
             for (int mol = 0; mol < molb.nmol; mol++)
             {
-                getWholeMoleculeCoordinates(&molt, &mtop->ffparams, ir->ePBC, &graph, box,
-                                            x + at_offset, xs);
+                getWholeMoleculeCoordinates(&molt, &mtop->ffparams, ir->pbcType, &graph, box,
+                                            x.subArray(at_offset, molt.atoms.nr), xs);
 
                 bonded_distance_t bd_mol_2b = { 0, -1, -1, -1 };
                 bonded_distance_t bd_mol_mb = { 0, -1, -1, -1 };
@@ -2253,11 +2041,6 @@ void dd_bonded_cg_distance(const gmx::MDLogger& mdlog,
 
                 at_offset += molt.atoms.nr;
             }
-            sfree(xs);
-            if (ir->ePBC != epbcNONE)
-            {
-                done_graph(&graph);
-            }
         }
     }
 
@@ -2266,7 +2049,7 @@ void dd_bonded_cg_distance(const gmx::MDLogger& mdlog,
         GMX_RELEASE_ASSERT(mtop->intermolecular_ilist,
                            "We should have an ilist when intermolecular interactions are on");
 
-        bonded_distance_intermol(*mtop->intermolecular_ilist, bBCheck, x, ir->ePBC, box, &bd_2b, &bd_mb);
+        bonded_distance_intermol(*mtop->intermolecular_ilist, bBCheck, x, ir->pbcType, box, &bd_2b, &bd_mb);
     }
 
     *r_2b = sqrt(bd_2b.r2);
index 5d2001dbc05bf0a023bfe7bacfe858227d0e9000..0fe374ee45223796bb03b70b152009a7550efa7f 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2006,2007,2008,2009,2010,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2006,2007,2008,2009,2010 by the GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "domdec_specatomcomm.h"
 
-void dd_move_f_vsites(gmx_domdec_t* dd, rvec* f, rvec* fshift)
+void dd_move_f_vsites(const gmx_domdec_t& dd, gmx::ArrayRef<gmx::RVec> f, gmx::ArrayRef<gmx::RVec> fshift)
 {
-    if (dd->vsite_comm)
+    if (dd.vsite_comm)
     {
-        dd_move_f_specat(dd, dd->vsite_comm, f, fshift);
+        dd_move_f_specat(&dd, dd.vsite_comm, as_rvec_array(f.data()), as_rvec_array(fshift.data()));
     }
 }
 
-void dd_clear_f_vsites(gmx_domdec_t* dd, rvec* f)
+void dd_clear_f_vsites(const gmx_domdec_t& dd, gmx::ArrayRef<gmx::RVec> f)
 {
-    int i;
-
-    if (dd->vsite_comm)
+    if (dd.vsite_comm)
     {
-        for (i = dd->vsite_comm->at_start; i < dd->vsite_comm->at_end; i++)
+        for (int i = dd.vsite_comm->at_start; i < dd.vsite_comm->at_end; i++)
         {
             clear_rvec(f[i]);
         }
     }
 }
 
-void dd_move_x_vsites(gmx_domdec_t* dd, const matrix box, rvec* x)
+void dd_move_x_vsites(const gmx_domdec_t& dd, const matrix box, rvec* x)
 {
-    if (dd->vsite_comm)
+    if (dd.vsite_comm)
     {
-        dd_move_x_specat(dd, dd->vsite_comm, box, x, nullptr, FALSE);
+        dd_move_x_specat(&dd, dd.vsite_comm, box, x, nullptr, FALSE);
     }
 }
 
@@ -101,7 +101,7 @@ void dd_clear_local_vsite_indices(gmx_domdec_t* dd)
     }
 }
 
-int dd_make_local_vsites(gmx_domdec_t* dd, int at_start, t_ilist* lil)
+int dd_make_local_vsites(gmx_domdec_t* dd, int at_start, gmx::ArrayRef<InteractionList> lil)
 {
     std::vector<int>&    ireq         = dd->vsite_requestedGlobalAtomIndices;
     gmx::HashedMap<int>* ga2la_specat = dd->ga2la_vsite;
@@ -112,11 +112,11 @@ int dd_make_local_vsites(gmx_domdec_t* dd, int at_start, t_ilist* lil)
     {
         if (interaction_function[ftype].flags & IF_VSITE)
         {
-            const int      nral = NRAL(ftype);
-            const t_ilist& lilf = lil[ftype];
-            for (int i = 0; i < lilf.nr; i += 1 + nral)
+            const int              nral = NRAL(ftype);
+            const InteractionList& lilf = lil[ftype];
+            for (int i = 0; i < lilf.size(); i += 1 + nral)
             {
-                const t_iatom* iatoms = lilf.iatoms + i;
+                const int* iatoms = lilf.iatoms.data() + i;
                 /* Check if we have the other atoms */
                 for (int j = 1; j < 1 + nral; j++)
                 {
@@ -150,11 +150,11 @@ int dd_make_local_vsites(gmx_domdec_t* dd, int at_start, t_ilist* lil)
     {
         if (interaction_function[ftype].flags & IF_VSITE)
         {
-            const int nral = NRAL(ftype);
-            t_ilist&  lilf = lil[ftype];
-            for (int i = 0; i < lilf.nr; i += 1 + nral)
+            const int        nral = NRAL(ftype);
+            InteractionList& lilf = lil[ftype];
+            for (int i = 0; i < lilf.size(); i += 1 + nral)
             {
-                t_iatom* iatoms = lilf.iatoms + i;
+                t_iatom* iatoms = lilf.iatoms.data() + i;
                 for (int j = 1; j < 1 + nral; j++)
                 {
                     if (iatoms[j] < 0)
index 0ce2a4d22bbca84aea65f1109ae3a61bd1e2bd2f..3ee206f2838049549ae08dda902b5eed8bd44756 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2005,2006,2007,2008,2009,2010,2012,2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2005,2006,2007,2008,2009 by the GROMACS development team.
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_DOMDEC_DOMDEC_VSITE_H
 #define GMX_DOMDEC_DOMDEC_VSITE_H
 
-struct t_ilist;
+#include "gromacs/utility/arrayref.h"
+
 struct gmx_domdec_t;
+struct InteractionList;
 
 /*! \brief Clears the local indices for the virtual site communication setup */
 void dd_clear_local_vsite_indices(struct gmx_domdec_t* dd);
 
 /*! \brief Sets up communication and atom indices for all local vsites */
-int dd_make_local_vsites(struct gmx_domdec_t* dd, int at_start, struct t_ilist* lil);
+int dd_make_local_vsites(struct gmx_domdec_t* dd, int at_start, gmx::ArrayRef<InteractionList> lil);
 
 /*! \brief Initializes the data structures for virtual site communication */
 void init_domdec_vsites(struct gmx_domdec_t* dd, int n_intercg_vsite);
index 28db18d212d455e594929bc1b897e8a22b34eaa2..c3f7eaad13a8d541419432038697c88943cb1055 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -87,7 +87,7 @@ void write_dd_grid_pdb(const char* fn, int64_t step, gmx_domdec_t* dd, matrix bo
                 }
                 else
                 {
-                    if (d < ddbox->npbcdim && dd->nc[d] > 1)
+                    if (d < ddbox->npbcdim && dd->numCells[d] > 1)
                     {
                         tric[d][i] = box[i][d] / box[i][i];
                     }
@@ -100,7 +100,7 @@ void write_dd_grid_pdb(const char* fn, int64_t step, gmx_domdec_t* dd, matrix bo
         }
         sprintf(fname, "%s_%s.pdb", fn, gmx_step_str(step, buf));
         out = gmx_fio_fopen(fname, "w");
-        gmx_write_pdb_box(out, dd->unitCellInfo.haveScrewPBC ? epbcSCREW : epbcXYZ, box);
+        gmx_write_pdb_box(out, dd->unitCellInfo.haveScrewPBC ? PbcType::Screw : PbcType::Xyz, box);
         a = 1;
         for (i = 0; i < dd->nnodes; i++)
         {
@@ -169,7 +169,7 @@ void write_dd_pdb(const char*       fn,
     out = gmx_fio_fopen(fname, "w");
 
     fprintf(out, "TITLE     %s\n", title);
-    gmx_write_pdb_box(out, dd->unitCellInfo.haveScrewPBC ? epbcSCREW : epbcXYZ, box);
+    gmx_write_pdb_box(out, dd->unitCellInfo.haveScrewPBC ? PbcType::Screw : PbcType::Xyz, box);
     int molb = 0;
     for (int i = 0; i < natoms; i++)
     {
index 6d1f7b818ccc4d48c973a4b0f8d74a6d2631dd56..e4b972afbfd3f03d8892b76ec0debd43ea5fa083 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b7e6ff54fa70c0aa273aa3300ac177e137384a26..6f4586098e3de1632885f67a88378bd177aaccf9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,6 +49,9 @@
 #include "gromacs/utility/gmxmpi.h"
 
 struct gmx_domdec_t;
+struct gmx_wallcycle;
+class DeviceContext;
+class DeviceStream;
 class GpuEventSynchronizer;
 
 namespace gmx
@@ -79,11 +82,22 @@ public:
      * does not yet support virial steps.
      *
      * \param [inout] dd                       domdec structure
+     * \param [in]    dimIndex                 the dimension index for this instance
      * \param [in]    mpi_comm_mysim           communicator used for simulation
+     * \param [in]    deviceContext            GPU device context
      * \param [in]    streamLocal              local NB CUDA stream.
      * \param [in]    streamNonLocal           non-local NB CUDA stream.
+     * \param [in]    pulse                    the communication pulse for this instance
+     * \param [in]    wcycle                   The wallclock counter
      */
-    GpuHaloExchange(gmx_domdec_t* dd, MPI_Comm mpi_comm_mysim, void* streamLocal, void* streamNonLocal);
+    GpuHaloExchange(gmx_domdec_t*        dd,
+                    int                  dimIndex,
+                    MPI_Comm             mpi_comm_mysim,
+                    const DeviceContext& deviceContext,
+                    const DeviceStream&  streamLocal,
+                    const DeviceStream&  streamNonLocal,
+                    int                  pulse,
+                    gmx_wallcycle*       wcycle);
     ~GpuHaloExchange();
 
     /*! \brief
@@ -92,7 +106,7 @@ public:
      * \param [in] d_coordinateBuffer   pointer to coordinates buffer in GPU memory
      * \param [in] d_forcesBuffer   pointer to coordinates buffer in GPU memory
      */
-    void reinitHalo(DeviceBuffer<float> d_coordinateBuffer, DeviceBuffer<float> d_forcesBuffer);
+    void reinitHalo(DeviceBuffer<RVec> d_coordinateBuffer, DeviceBuffer<RVec> d_forcesBuffer);
 
 
     /*! \brief GPU halo exchange of coordinates buffer.
index 2511673218b3eac56b681e808c80da36989b17c6..64221f0257deece465f0dc2f831f14e9c8b696e7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,8 +48,9 @@
 #include "config.h"
 
 #include "gromacs/domdec/gpuhaloexchange.h"
+#include "gromacs/utility/gmxassert.h"
 
-#if GMX_GPU != GMX_GPU_CUDA
+#if !GMX_GPU_CUDA
 
 namespace gmx
 {
@@ -61,9 +62,13 @@ class GpuHaloExchange::Impl
 
 /*!\brief Constructor stub. */
 GpuHaloExchange::GpuHaloExchange(gmx_domdec_t* /* dd */,
+                                 int /* dimIndex */,
                                  MPI_Comm /* mpi_comm_mysim */,
-                                 void* /*streamLocal */,
-                                 void* /*streamNonLocal */) :
+                                 const DeviceContext& /* deviceContext */,
+                                 const DeviceStream& /*streamLocal */,
+                                 const DeviceStream& /*streamNonLocal */,
+                                 int /*pulse */,
+                                 gmx_wallcycle* /*wcycle*/) :
     impl_(nullptr)
 {
     GMX_ASSERT(false,
@@ -73,10 +78,10 @@ GpuHaloExchange::GpuHaloExchange(gmx_domdec_t* /* dd */,
 GpuHaloExchange::~GpuHaloExchange() = default;
 
 /*!\brief init halo exhange stub. */
-void GpuHaloExchange::reinitHalo(DeviceBuffer<float> /* d_coordinatesBuffer */,
-                                 DeviceBuffer<float> /* d_forcesBuffer */)
+void GpuHaloExchange::reinitHalo(DeviceBuffer<RVec> /* d_coordinatesBuffer */,
+                                 DeviceBuffer<RVec> /* d_forcesBuffer */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for GPU Halo Exchange was called insted of the correct implementation.");
 }
 
@@ -84,7 +89,7 @@ void GpuHaloExchange::reinitHalo(DeviceBuffer<float> /* d_coordinatesBuffer */,
 void GpuHaloExchange::communicateHaloCoordinates(const matrix /* box */,
                                                  GpuEventSynchronizer* /*coordinatesOnDeviceEvent*/)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for GPU Halo Exchange exchange was called insted of the correct "
                "implementation.");
 }
@@ -92,18 +97,18 @@ void GpuHaloExchange::communicateHaloCoordinates(const matrix /* box */,
 /*!\brief apply F halo exchange stub. */
 void GpuHaloExchange::communicateHaloForces(bool gmx_unused accumulateForces)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for GPU Halo Exchange was called insted of the correct implementation.");
 }
 
 /*!\brief get forces ready on device event stub. */
 GpuEventSynchronizer* GpuHaloExchange::getForcesReadyOnDeviceEvent()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for GPU Halo Exchange was called insted of the correct implementation.");
     return nullptr;
 }
 
 } // namespace gmx
 
-#endif /* GMX_GPU != GMX_GPU_CUDA */
+#endif // !GMX_GPU_CUDA
index 660566a9dd9117343497c5d6308c8c6e255f840e..d519445f74d48d0dd2fbea7b1f623e3b7c8c5371 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/domdec/domdec_struct.h"
 #include "gromacs/domdec/gpuhaloexchange.h"
 #include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/device_context.h"
 #include "gromacs/gpu_utils/devicebuffer.h"
 #include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
+#include "gromacs/gpu_utils/typecasts.cuh"
 #include "gromacs/gpu_utils/vectype_ops.cuh"
+#include "gromacs/math/vectypes.h"
 #include "gromacs/pbcutil/ishift.h"
+#include "gromacs/timing/wallcycle.h"
 #include "gromacs/utility/gmxmpi.h"
 
 #include "domdec_internal.h"
@@ -132,15 +136,31 @@ void GpuHaloExchange::Impl::reinitHalo(float3* d_coordinatesBuffer, float3* d_fo
     d_x_ = d_coordinatesBuffer;
     d_f_ = d_forcesBuffer;
 
-    cudaStream_t                 stream  = nonLocalStream_;
-    int                          nzone   = 1;
-    const gmx_domdec_comm_t&     comm    = *dd_->comm;
-    const gmx_domdec_comm_dim_t& cd      = comm.cd[0];
-    const gmx_domdec_ind_t&      ind     = cd.ind[0];
-    int                          newSize = ind.nsend[nzone + 1];
+    const gmx_domdec_comm_t&     comm = *dd_->comm;
+    const gmx_domdec_comm_dim_t& cd   = comm.cd[dimIndex_];
+    const gmx_domdec_ind_t&      ind  = cd.ind[pulse_];
+
+    numHomeAtoms_ = comm.atomRanges.numHomeAtoms(); // offset for data recieved by this rank
+
+    // Determine receive offset for the dimension index and pulse of this halo exchange object
+    int numZoneTemp   = 1;
+    int numZone       = 0;
+    int numAtomsTotal = numHomeAtoms_;
+    for (int i = 0; i <= dimIndex_; i++)
+    {
+        int pulseMax = (i == dimIndex_) ? pulse_ : (comm.cd[i].numPulses() - 1);
+        for (int p = 0; p <= pulseMax; p++)
+        {
+            atomOffset_                     = numAtomsTotal;
+            const gmx_domdec_ind_t& indTemp = comm.cd[i].ind[p];
+            numAtomsTotal += indTemp.nrecv[numZoneTemp + 1];
+        }
+        numZone = numZoneTemp;
+        numZoneTemp += numZoneTemp;
+    }
+
+    int newSize = ind.nsend[numZone + 1];
 
-    GMX_RELEASE_ASSERT(cd.numPulses() == 1,
-                       "Multiple pulses are not yet supported in GPU halo exchange");
     GMX_ASSERT(cd.receiveInPlace, "Out-of-place receive is not yet supported in GPU halo exchange");
 
     // reallocates only if needed
@@ -148,9 +168,9 @@ void GpuHaloExchange::Impl::reinitHalo(float3* d_coordinatesBuffer, float3* d_fo
     // reallocate on device only if needed
     if (newSize > maxPackedBufferSize_)
     {
-        reallocateDeviceBuffer(&d_indexMap_, newSize, &indexMapSize_, &indexMapSizeAlloc_, nullptr);
-        reallocateDeviceBuffer(&d_sendBuf_, newSize, &sendBufSize_, &sendBufSizeAlloc_, nullptr);
-        reallocateDeviceBuffer(&d_recvBuf_, newSize, &recvBufSize_, &recvBufSizeAlloc_, nullptr);
+        reallocateDeviceBuffer(&d_indexMap_, newSize, &indexMapSize_, &indexMapSizeAlloc_, deviceContext_);
+        reallocateDeviceBuffer(&d_sendBuf_, newSize, &sendBufSize_, &sendBufSizeAlloc_, deviceContext_);
+        reallocateDeviceBuffer(&d_recvBuf_, newSize, &recvBufSize_, &recvBufSizeAlloc_, deviceContext_);
         maxPackedBufferSize_ = newSize;
     }
 
@@ -162,22 +182,24 @@ void GpuHaloExchange::Impl::reinitHalo(float3* d_coordinatesBuffer, float3* d_fo
     fSendSize_ = xRecvSize_;
     fRecvSize_ = xSendSize_;
 
-    numHomeAtoms_ = comm.atomRanges.numHomeAtoms(); // offset for data recieved by this rank
-
-    GMX_ASSERT(ind.index.size() == h_indexMap_.size(), "Size mismatch");
-    std::copy(ind.index.begin(), ind.index.end(), h_indexMap_.begin());
-
-    copyToDeviceBuffer(&d_indexMap_, h_indexMap_.data(), 0, newSize, stream,
-                       GpuApiCallBehavior::Async, nullptr);
+    if (newSize > 0)
+    {
+        GMX_ASSERT(ind.index.size() == h_indexMap_.size(),
+                   "Size mismatch between domain decomposition communication index array and GPU "
+                   "halo exchange index mapping array");
+        std::copy(ind.index.begin(), ind.index.end(), h_indexMap_.begin());
 
+        copyToDeviceBuffer(&d_indexMap_, h_indexMap_.data(), 0, newSize, nonLocalStream_,
+                           GpuApiCallBehavior::Async, nullptr);
+    }
     // This rank will push data to its neighbor, so needs to know
     // the remote receive address and similarly send its receive
     // address to other neighbour. We can do this here in reinit fn
     // since the pointers will not change until the next NS step.
 
     // Coordinates buffer:
+    void* recvPtr = static_cast<void*>(&d_x_[atomOffset_]);
 #if GMX_MPI
-    void* recvPtr = static_cast<void*>(&d_coordinatesBuffer[numHomeAtoms_]);
     MPI_Sendrecv(&recvPtr, sizeof(void*), MPI_BYTE, recvRankX_, 0, &remoteXPtr_, sizeof(void*),
                  MPI_BYTE, sendRankX_, 0, mpi_comm_mysim_, MPI_STATUS_IGNORE);
 
@@ -187,7 +209,6 @@ void GpuHaloExchange::Impl::reinitHalo(float3* d_coordinatesBuffer, float3* d_fo
                  MPI_BYTE, sendRankF_, 0, mpi_comm_mysim_, MPI_STATUS_IGNORE);
 #endif
 
-
     return;
 }
 
@@ -195,8 +216,14 @@ void GpuHaloExchange::Impl::communicateHaloCoordinates(const matrix          box
                                                        GpuEventSynchronizer* coordinatesReadyOnDeviceEvent)
 {
 
-    // ensure stream waits until coordinate data is available on device
-    coordinatesReadyOnDeviceEvent->enqueueWaitEvent(nonLocalStream_);
+    if (pulse_ == 0)
+    {
+        // ensure stream waits until coordinate data is available on device
+        coordinatesReadyOnDeviceEvent->enqueueWaitEvent(nonLocalStream_);
+    }
+
+    wallcycle_start(wcycle_, ewcLAUNCH_GPU);
+    wallcycle_sub_start(wcycle_, ewcsLAUNCH_GPU_MOVEX);
 
     // launch kernel to pack send buffer
     KernelLaunchConfig config;
@@ -207,7 +234,6 @@ void GpuHaloExchange::Impl::communicateHaloCoordinates(const matrix          box
     config.gridSize[1]      = 1;
     config.gridSize[2]      = 1;
     config.sharedMemorySize = 0;
-    config.stream           = nonLocalStream_;
 
     const float3* sendBuf  = d_sendBuf_;
     const float3* d_x      = d_x_;
@@ -218,12 +244,9 @@ void GpuHaloExchange::Impl::communicateHaloCoordinates(const matrix          box
     // performing pressure coupling. So, for simplicity, the box
     // is used every step to pass the shift vector as an argument of
     // the packing kernel.
-    //
-    // Because only one-dimensional DD is supported, the coordinate
-    // shift only needs to handle that dimension.
-    const int    dimensionIndex = dd_->dim[0];
-    const float3 coordinateShift{ box[dimensionIndex][XX], box[dimensionIndex][YY],
-                                  box[dimensionIndex][ZZ] };
+    const int    boxDimensionIndex = dd_->dim[dimIndex_];
+    const float3 coordinateShift{ box[boxDimensionIndex][XX], box[boxDimensionIndex][YY],
+                                  box[boxDimensionIndex][ZZ] };
 
     // Avoid launching kernel when there is no work to do
     if (size > 0)
@@ -233,11 +256,21 @@ void GpuHaloExchange::Impl::communicateHaloCoordinates(const matrix          box
         const auto kernelArgs = prepareGpuKernelArguments(kernelFn, config, &sendBuf, &d_x,
                                                           &indexMap, &size, &coordinateShift);
 
-        launchGpuKernel(kernelFn, config, nullptr, "Domdec GPU Apply X Halo Exchange", kernelArgs);
+        launchGpuKernel(kernelFn, config, nonLocalStream_, nullptr,
+                        "Domdec GPU Apply X Halo Exchange", kernelArgs);
     }
 
+    wallcycle_sub_stop(wcycle_, ewcsLAUNCH_GPU_MOVEX);
+    wallcycle_stop(wcycle_, ewcLAUNCH_GPU);
+
+    // Consider time spent in communicateHaloData as Comm.X counter
+    // ToDo: We need further refinement here as communicateHaloData includes launch time for cudamemcpyasync
+    wallcycle_start(wcycle_, ewcMOVEX);
+
     communicateHaloData(d_x_, HaloQuantity::HaloCoordinates, coordinatesReadyOnDeviceEvent);
 
+    wallcycle_stop(wcycle_, ewcMOVEX);
+
     return;
 }
 
@@ -245,25 +278,39 @@ void GpuHaloExchange::Impl::communicateHaloCoordinates(const matrix          box
 // and before the local buffer operations. It operates in the non-local stream.
 void GpuHaloExchange::Impl::communicateHaloForces(bool accumulateForces)
 {
+    // Consider time spent in communicateHaloData as Comm.F counter
+    // ToDo: We need further refinement here as communicateHaloData includes launch time for cudamemcpyasync
+    wallcycle_start(wcycle_, ewcMOVEF);
 
     // Communicate halo data (in non-local stream)
     communicateHaloData(d_f_, HaloQuantity::HaloForces, nullptr);
 
-    float3* d_f = d_f_;
+    wallcycle_stop(wcycle_, ewcMOVEF);
+
+    wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
+    wallcycle_sub_start(wcycle_, ewcsLAUNCH_GPU_MOVEF);
 
-    if (!accumulateForces)
+    float3* d_f = d_f_;
+    // If this is the last pulse and index (noting the force halo
+    // exchanges across multiple pulses and indices are called in
+    // reverse order) then perform the following preparation
+    // activities
+    if ((pulse_ == (dd_->comm->cd[dimIndex_].numPulses() - 1)) && (dimIndex_ == (dd_->ndim - 1)))
     {
-        // Clear local portion of force array (in local stream)
-        cudaMemsetAsync(d_f, 0, numHomeAtoms_ * sizeof(rvec), localStream_);
-    }
+        if (!accumulateForces)
+        {
+            // Clear local portion of force array (in local stream)
+            cudaMemsetAsync(d_f, 0, numHomeAtoms_ * sizeof(rvec), localStream_.stream());
+        }
 
-    // ensure non-local stream waits for local stream, due to dependence on
-    // the previous H2D copy of CPU forces (if accumulateForces is true)
-    // or the above clearing.
-    // TODO remove this dependency on localStream - edmine issue #3093
-    GpuEventSynchronizer eventLocal;
-    eventLocal.markEvent(localStream_);
-    eventLocal.enqueueWaitEvent(nonLocalStream_);
+        // ensure non-local stream waits for local stream, due to dependence on
+        // the previous H2D copy of CPU forces (if accumulateForces is true)
+        // or the above clearing.
+        // TODO remove this dependency on localStream - edmine Issue #3093
+        GpuEventSynchronizer eventLocal;
+        eventLocal.markEvent(localStream_);
+        eventLocal.enqueueWaitEvent(nonLocalStream_);
+    }
 
     // Unpack halo buffer into force array
 
@@ -275,12 +322,19 @@ void GpuHaloExchange::Impl::communicateHaloForces(bool accumulateForces)
     config.gridSize[1]      = 1;
     config.gridSize[2]      = 1;
     config.sharedMemorySize = 0;
-    config.stream           = nonLocalStream_;
 
     const float3* recvBuf  = d_recvBuf_;
     const int*    indexMap = d_indexMap_;
     const int     size     = fRecvSize_;
 
+    if (pulse_ > 0 || dd_->ndim > 1)
+    {
+        // We need to accumulate rather than set, since it is possible
+        // that, in this pulse/dim, a value could be written to a location
+        // corresponding to the halo region of a following pulse/dim.
+        accumulateForces = true;
+    }
+
     if (size > 0)
     {
         auto kernelFn = accumulateForces ? unpackRecvBufKernel<true> : unpackRecvBufKernel<false>;
@@ -288,9 +342,17 @@ void GpuHaloExchange::Impl::communicateHaloForces(bool accumulateForces)
         const auto kernelArgs =
                 prepareGpuKernelArguments(kernelFn, config, &d_f, &recvBuf, &indexMap, &size);
 
-        launchGpuKernel(kernelFn, config, nullptr, "Domdec GPU Apply F Halo Exchange", kernelArgs);
+        launchGpuKernel(kernelFn, config, nonLocalStream_, nullptr,
+                        "Domdec GPU Apply F Halo Exchange", kernelArgs);
     }
-    fReadyOnDevice_.markEvent(nonLocalStream_);
+
+    if (pulse_ == 0)
+    {
+        fReadyOnDevice_.markEvent(nonLocalStream_);
+    }
+
+    wallcycle_sub_stop(wcycle_, ewcsLAUNCH_GPU_MOVEF);
+    wallcycle_stop(wcycle_, ewcLAUNCH_GPU);
 }
 
 
@@ -328,7 +390,7 @@ void GpuHaloExchange::Impl::communicateHaloData(float3*               d_ptr,
     }
     else
     {
-        sendPtr   = static_cast<void*>(&(d_ptr[numHomeAtoms_]));
+        sendPtr   = static_cast<void*>(&(d_ptr[atomOffset_]));
         sendSize  = fSendSize_;
         remotePtr = remoteFPtr_;
         sendRank  = sendRankF_;
@@ -345,8 +407,7 @@ void GpuHaloExchange::Impl::communicateHaloDataWithCudaDirect(void* sendPtr,
                                                               int   recvRank)
 {
 
-    cudaError_t  stat;
-    cudaStream_t stream = nonLocalStream_;
+    cudaError_t stat;
 
     // We asynchronously push data to remote rank. The remote
     // destination pointer has already been set in the init fn.  We
@@ -358,7 +419,8 @@ void GpuHaloExchange::Impl::communicateHaloDataWithCudaDirect(void* sendPtr,
     if (sendSize > 0)
     {
         stat = cudaMemcpyAsync(remotePtr, sendPtr, sendSize * DIM * sizeof(float),
-                               cudaMemcpyDeviceToDevice, stream);
+                               cudaMemcpyDeviceToDevice, nonLocalStream_.stream());
+
         CU_RET_ERR(stat, "cudaMemcpyAsync on GPU Domdec CUDA direct data transfer failed");
     }
 
@@ -369,13 +431,13 @@ void GpuHaloExchange::Impl::communicateHaloDataWithCudaDirect(void* sendPtr,
     // to its stream.
     GpuEventSynchronizer* haloDataTransferRemote;
 
-    haloDataTransferLaunched_->markEvent(stream);
+    haloDataTransferLaunched_->markEvent(nonLocalStream_);
 
     MPI_Sendrecv(&haloDataTransferLaunched_, sizeof(GpuEventSynchronizer*), MPI_BYTE, sendRank, 0,
                  &haloDataTransferRemote, sizeof(GpuEventSynchronizer*), MPI_BYTE, recvRank, 0,
                  mpi_comm_mysim_, MPI_STATUS_IGNORE);
 
-    haloDataTransferRemote->enqueueWaitEvent(stream);
+    haloDataTransferRemote->enqueueWaitEvent(nonLocalStream_);
 #else
     GMX_UNUSED_VALUE(sendRank);
     GMX_UNUSED_VALUE(recvRank);
@@ -388,27 +450,33 @@ GpuEventSynchronizer* GpuHaloExchange::Impl::getForcesReadyOnDeviceEvent()
 }
 
 /*! \brief Create Domdec GPU object */
-GpuHaloExchange::Impl::Impl(gmx_domdec_t* dd, MPI_Comm mpi_comm_mysim, void* localStream, void* nonLocalStream) :
+GpuHaloExchange::Impl::Impl(gmx_domdec_t*        dd,
+                            int                  dimIndex,
+                            MPI_Comm             mpi_comm_mysim,
+                            const DeviceContext& deviceContext,
+                            const DeviceStream&  localStream,
+                            const DeviceStream&  nonLocalStream,
+                            int                  pulse,
+                            gmx_wallcycle*       wcycle) :
     dd_(dd),
-    sendRankX_(dd->neighbor[0][1]),
-    recvRankX_(dd->neighbor[0][0]),
-    sendRankF_(dd->neighbor[0][0]),
-    recvRankF_(dd->neighbor[0][1]),
-    usePBC_(dd->ci[dd->dim[0]] == 0),
+    dimIndex_(dimIndex),
+    sendRankX_(dd->neighbor[dimIndex][1]),
+    recvRankX_(dd->neighbor[dimIndex][0]),
+    sendRankF_(dd->neighbor[dimIndex][0]),
+    recvRankF_(dd->neighbor[dimIndex][1]),
+    usePBC_(dd->ci[dd->dim[dimIndex]] == 0),
     haloDataTransferLaunched_(new GpuEventSynchronizer()),
     mpi_comm_mysim_(mpi_comm_mysim),
-    localStream_(*static_cast<cudaStream_t*>(localStream)),
-    nonLocalStream_(*static_cast<cudaStream_t*>(nonLocalStream))
+    deviceContext_(deviceContext),
+    localStream_(localStream),
+    nonLocalStream_(nonLocalStream),
+    pulse_(pulse),
+    wcycle_(wcycle)
 {
 
     GMX_RELEASE_ASSERT(GMX_THREAD_MPI,
                        "GPU Halo exchange is currently only supported with thread-MPI enabled");
 
-    if (dd->ndim > 1)
-    {
-        gmx_fatal(FARGS, "Error: dd->ndim > 1 is not yet supported in GPU halo exchange");
-    }
-
     if (usePBC_ && dd->unitCellInfo.haveScrewPBC)
     {
         gmx_fatal(FARGS, "Error: screw is not yet supported in GPU halo exchange\n");
@@ -416,7 +484,7 @@ GpuHaloExchange::Impl::Impl(gmx_domdec_t* dd, MPI_Comm mpi_comm_mysim, void* loc
 
     changePinningPolicy(&h_indexMap_, gmx::PinningPolicy::PinnedIfSupported);
 
-    allocateDeviceBuffer(&d_fShift_, 1, nullptr);
+    allocateDeviceBuffer(&d_fShift_, 1, deviceContext_);
 }
 
 GpuHaloExchange::Impl::~Impl()
@@ -428,17 +496,23 @@ GpuHaloExchange::Impl::~Impl()
     delete haloDataTransferLaunched_;
 }
 
-GpuHaloExchange::GpuHaloExchange(gmx_domdec_t* dd, MPI_Comm mpi_comm_mysim, void* localStream, void* nonLocalStream) :
-    impl_(new Impl(dd, mpi_comm_mysim, localStream, nonLocalStream))
+GpuHaloExchange::GpuHaloExchange(gmx_domdec_t*        dd,
+                                 int                  dimIndex,
+                                 MPI_Comm             mpi_comm_mysim,
+                                 const DeviceContext& deviceContext,
+                                 const DeviceStream&  localStream,
+                                 const DeviceStream&  nonLocalStream,
+                                 int                  pulse,
+                                 gmx_wallcycle*       wcycle) :
+    impl_(new Impl(dd, dimIndex, mpi_comm_mysim, deviceContext, localStream, nonLocalStream, pulse, wcycle))
 {
 }
 
 GpuHaloExchange::~GpuHaloExchange() = default;
 
-void GpuHaloExchange::reinitHalo(DeviceBuffer<float> d_coordinatesBuffer, DeviceBuffer<float> d_forcesBuffer)
+void GpuHaloExchange::reinitHalo(DeviceBuffer<RVec> d_coordinatesBuffer, DeviceBuffer<RVec> d_forcesBuffer)
 {
-    impl_->reinitHalo(reinterpret_cast<float3*>(d_coordinatesBuffer),
-                      reinterpret_cast<float3*>(d_forcesBuffer));
+    impl_->reinitHalo(asFloat3(d_coordinatesBuffer), asFloat3(d_forcesBuffer));
 }
 
 void GpuHaloExchange::communicateHaloCoordinates(const matrix          box,
index 017cb191869e40bc9d5b46cb1c9f164a0767e6a5..761938a0133c3c5f5645d17e2121d818356ca9a7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_DOMDEC_GPUHALOEXCHANGE_IMPL_H
 
 #include "gromacs/domdec/gpuhaloexchange.h"
+#include "gromacs/gpu_utils/device_context.h"
 #include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
 #include "gromacs/gpu_utils/hostallocator.h"
 #include "gromacs/utility/gmxmpi.h"
 
+struct gmx_wallcycle;
+
 namespace gmx
 {
 
@@ -69,11 +72,22 @@ public:
     /*! \brief Creates GPU Halo Exchange object.
      *
      * \param [inout] dd                       domdec structure
+     * \param [in]    dimIndex                 the dimension index for this instance
      * \param [in]    mpi_comm_mysim           communicator used for simulation
+     * \param [in]    deviceContext            GPU device context
      * \param [in]    localStream              local NB CUDA stream
      * \param [in]    nonLocalStream           non-local NB CUDA stream
+     * \param [in]    pulse                    the communication pulse for this instance
+     * \param [in]    wcycle                   The wallclock counter
      */
-    Impl(gmx_domdec_t* dd, MPI_Comm mpi_comm_mysim, void* localStream, void* nonLocalStream);
+    Impl(gmx_domdec_t*        dd,
+         int                  dimIndex,
+         MPI_Comm             mpi_comm_mysim,
+         const DeviceContext& deviceContext,
+         const DeviceStream&  localStream,
+         const DeviceStream&  nonLocalStream,
+         int                  pulse,
+         gmx_wallcycle*       wcycle);
     ~Impl();
 
     /*! \brief
@@ -174,16 +188,28 @@ private:
     GpuEventSynchronizer* haloDataTransferLaunched_ = nullptr;
     //! MPI communicator used for simulation
     MPI_Comm mpi_comm_mysim_;
+    //! GPU context object
+    const DeviceContext& deviceContext_;
     //! CUDA stream for local non-bonded calculations
-    cudaStream_t localStream_ = nullptr;
+    const DeviceStream& localStream_;
     //! CUDA stream for non-local non-bonded calculations
-    cudaStream_t nonLocalStream_ = nullptr;
+    const DeviceStream& nonLocalStream_;
     //! full coordinates buffer in GPU memory
     float3* d_x_ = nullptr;
     //! full forces buffer in GPU memory
     float3* d_f_ = nullptr;
     //! An event recorded once the exchanged forces are ready on the GPU
     GpuEventSynchronizer fReadyOnDevice_;
+    //! The dimension index corresponding to this halo exchange instance
+    int dimIndex_ = 0;
+    //! The pulse corresponding to this halo exchange instance
+    int pulse_ = 0;
+    //! Number of zones. Always 1 for 1-D case.
+    const int nzone_ = 1;
+    //! The wallclock counter
+    gmx_wallcycle* wcycle_ = nullptr;
+    //! The atom offset for receive (x) or send (f) for dimension index and pulse corresponding to this halo exchange instance
+    int atomOffset_ = 0;
 };
 
 } // namespace gmx
index 32bc3ceb25d7edd85958510a0eb373d7e52050bf..00f37fc81a5d674e023a49d499476a602f70f7aa 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,6 +50,7 @@
 #include <climits>
 
 #include <algorithm>
+#include <utility>
 #include <vector>
 
 #include "gromacs/compat/utility.h"
@@ -256,6 +257,8 @@ public:
     }
 
     /*! \brief Returns a pointer to the value for the given key or nullptr when not present
+     *
+     * \todo Use std::as_const when CUDA 11 is a requirement.
      *
      * \param[in] key  The key
      * \return a pointer to value for the given key or nullptr when not present
index 594c18b5c2169f1028c704d4d728d096ac9e686d..41599d37b21790c550095888e307a09a1acfa4ed 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_DOMDEC_LOCALATOMSET_H
 #define GMX_DOMDEC_LOCALATOMSET_H
 
-#include "gromacs/utility/arrayref.h"
+#include <cstddef>
 
 namespace gmx
 {
-
+template<typename>
+class ArrayRef;
 namespace internal
 {
 class LocalAtomSetData;
index c6dd2cb85e68da8e9794a44860ad34317af22cc7..a41c6dceef75035687afdc9bb6abe9a46964c6ef 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -75,7 +75,7 @@ public:
      *
      * \param[in] globalAtomIndex Indices of the atoms to be managed
      */
-    template<typename T = void, typename U = std::enable_if_t<!std::is_same<int, index>::value, T>>
+    template<typename T = void, typename U = std::enable_if_t<!std::is_same_v<int, index>, T>>
     explicit LocalAtomSetData(ArrayRef<const int> globalAtomIndex) :
         globalIndex_(globalAtomIndex.begin(), globalAtomIndex.end()),
         localIndex_(globalAtomIndex.begin(), globalAtomIndex.end())
@@ -106,7 +106,7 @@ public:
     /*! \brief Global indices of the atoms in this set. */
     const std::vector<int> globalIndex_;
     /*! \brief Maps indices on this rank [0..num_atoms_local_) to global atom indicices,
-     * so that localIndex[i] = globalIndex[collectiveIndex[i]].
+     * so that localIndex[i] identifies the same atom as globalIndex[collectiveIndex[i]].
      *
      * This translation of locally dense atom data to global representation,
      * allows to adresses per-atom properties, e.g., scattering factors,
index 2c7bafed200b71ff6b00770d4248946162829db9..bff851974512d261e04e2f0107a088587e47906f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,7 +46,6 @@
 
 #include <memory>
 
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/classhelpers.h"
 
@@ -54,7 +53,8 @@ class gmx_ga2la_t;
 
 namespace gmx
 {
-
+template<typename>
+class ArrayRef;
 class LocalAtomSet;
 
 /*! \libinternal \brief
@@ -84,7 +84,7 @@ public:
      * \param[in] globalAtomIndex Indices of the atoms to be managed
      * \returns Handle to LocalAtomSet.
      */
-    template<typename T = void, typename U = std::enable_if_t<!std::is_same<int, index>::value, T>>
+    template<typename T = void, typename U = std::enable_if_t<!std::is_same_v<int, index>, T>>
     LocalAtomSet add(ArrayRef<const int> globalAtomIndex);
 #endif
     /*! \brief Add a new atom set to be managed and give back a handle.
index 5ba8445574178006543567d247fee4c76597d196..90f28d18a77bc64e8ea5fcc0c6f6c39caf8e47a0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/domdec/domdec.h"
 #include "gromacs/domdec/domdec_struct.h"
 #include "gromacs/ewald/pme.h"
-#include "gromacs/listed_forces/manage_threading.h"
+#include "gromacs/listed_forces/listed_forces.h"
 #include "gromacs/mdlib/constr.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/vsite.h"
 #include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/forcebuffers.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/pbcutil/mshift.h"
+#include "gromacs/mdtypes/interaction_const.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/smalloc.h"
 
+namespace gmx
+{
+
 /* TODO: Add a routine that collects the initial setup of the algorithms.
  *
  * The final solution should be an MD algorithm base class with methods
  * for initialization and atom-data setup.
  */
-void mdAlgorithmsSetupAtomData(const t_commrec*  cr,
-                               const t_inputrec* ir,
-                               const gmx_mtop_t& top_global,
-                               gmx_localtop_t*   top,
-                               t_forcerec*       fr,
-                               t_graph**         graph,
-                               gmx::MDAtoms*     mdAtoms,
-                               gmx::Constraints* constr,
-                               gmx_vsite_t*      vsite,
-                               gmx_shellfc_t*    shellfc)
+void mdAlgorithmsSetupAtomData(const t_commrec*     cr,
+                               const t_inputrec*    ir,
+                               const gmx_mtop_t&    top_global,
+                               gmx_localtop_t*      top,
+                               t_forcerec*          fr,
+                               ForceBuffers*        force,
+                               MDAtoms*             mdAtoms,
+                               Constraints*         constr,
+                               VirtualSitesHandler* vsite,
+                               gmx_shellfc_t*       shellfc)
 {
     bool usingDomDec = DOMAINDECOMP(cr);
 
-    int  numAtomIndex, numHomeAtoms;
-    int* atomIndex;
+    int numAtomIndex;
+    int numHomeAtoms;
+    int numTotalAtoms;
 
     if (usingDomDec)
     {
-        numAtomIndex = dd_natoms_mdatoms(cr->dd);
-        atomIndex    = cr->dd->globalAtomIndices.data();
-        numHomeAtoms = dd_numHomeAtoms(*cr->dd);
+        numAtomIndex  = dd_natoms_mdatoms(cr->dd);
+        numHomeAtoms  = dd_numHomeAtoms(*cr->dd);
+        numTotalAtoms = dd_natoms_mdatoms(cr->dd);
     }
     else
     {
-        numAtomIndex = -1;
-        atomIndex    = nullptr;
-        numHomeAtoms = top_global.natoms;
+        numAtomIndex  = -1;
+        numHomeAtoms  = top_global.natoms;
+        numTotalAtoms = top_global.natoms;
+    }
+
+    if (force != nullptr)
+    {
+        force->resize(numTotalAtoms);
     }
-    atoms2md(&top_global, ir, numAtomIndex, atomIndex, numHomeAtoms, mdAtoms);
+
+    atoms2md(&top_global, ir, numAtomIndex,
+             usingDomDec ? cr->dd->globalAtomIndices : std::vector<int>(), numHomeAtoms, mdAtoms);
 
     auto mdatoms = mdAtoms->mdatoms();
     if (usingDomDec)
@@ -100,28 +113,7 @@ void mdAlgorithmsSetupAtomData(const t_commrec*  cr,
 
     if (vsite)
     {
-        if (usingDomDec)
-        {
-            /* The vsites were already assigned by the domdec topology code.
-             * We only need to do the thread division here.
-             */
-            split_vsites_over_threads(top->idef.il, top->idef.iparams, mdatoms, vsite);
-        }
-        else
-        {
-            set_vsite_top(vsite, top, mdatoms);
-        }
-    }
-
-    if (!usingDomDec && ir->ePBC != epbcNONE && !fr->bMolPBC)
-    {
-        GMX_ASSERT(graph != nullptr, "We use a graph with PBC (no periodic mols) and without DD");
-
-        *graph = mk_graph(nullptr, &(top->idef), 0, top_global.natoms, FALSE, FALSE);
-    }
-    else if (graph != nullptr)
-    {
-        *graph = nullptr;
+        vsite->setVirtualSites(top->idef.il, *mdatoms);
     }
 
     /* Note that with DD only flexible constraints, not shells, are supported
@@ -135,7 +127,10 @@ void mdAlgorithmsSetupAtomData(const t_commrec*  cr,
         make_local_shells(cr, mdatoms, shellfc);
     }
 
-    setup_bonded_threading(fr->bondedThreading, fr->natoms_force, fr->gpuBonded != nullptr, top->idef);
+    for (auto& listedForces : fr->listedForces)
+    {
+        listedForces.setup(top->idef, fr->natoms_force, fr->gpuBonded != nullptr);
+    }
 
     if (EEL_PME(fr->ic->eeltype) && (cr->duty & DUTY_PME))
     {
@@ -143,11 +138,14 @@ void mdAlgorithmsSetupAtomData(const t_commrec*  cr,
          * For PME-only ranks, gmx_pmeonly() has its own call to gmx_pme_reinit_atoms().
          */
         const int numPmeAtoms = numHomeAtoms - fr->n_tpi;
-        gmx_pme_reinit_atoms(fr->pmedata, numPmeAtoms, mdatoms->chargeA);
+        gmx_pme_reinit_atoms(fr->pmedata, numPmeAtoms, mdatoms->chargeA, mdatoms->chargeB);
     }
 
     if (constr)
     {
-        constr->setConstraints(*top, *mdatoms);
+        constr->setConstraints(top, mdatoms->nr, mdatoms->homenr, mdatoms->massT, mdatoms->invmass,
+                               mdatoms->nMassPerturbed != 0, mdatoms->lambda, mdatoms->cFREEZE);
     }
 }
+
+} // namespace gmx
index 3752118133d7be328119aa5711b64868719ff36c..ab0da8fcf16f398beeace4c119e42ea610c462a9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_DOMDEC_MDSETUP_H
 #define GMX_DOMDEC_MDSETUP_H
 
+#include "gromacs/math/vectypes.h"
+
 struct bonded_threading_t;
 struct gmx_localtop_t;
 struct gmx_mtop_t;
 struct gmx_shellfc_t;
-struct gmx_vsite_t;
 struct t_commrec;
 struct t_forcerec;
-struct t_graph;
 struct t_inputrec;
 struct t_mdatoms;
 
 namespace gmx
 {
 class Constraints;
+class ForceBuffers;
 class MDAtoms;
-} // namespace gmx
+class VirtualSitesHandler;
 
 /*! \brief Gets the local shell with domain decomposition
  *
@@ -80,21 +81,23 @@ void make_local_shells(const t_commrec* cr, const t_mdatoms* md, gmx_shellfc_t*
  * \param[in]     top_global The global topology
  * \param[in,out] top        The local topology
  * \param[in,out] fr         The force calculation parameter/data record
- * \param[out]    graph      The molecular graph, can be NULL
+ * \param[out]    force      The force buffer
  * \param[out]    mdAtoms    The MD atom data
  * \param[in,out] constr     The constraints handler, can be NULL
  * \param[in,out] vsite      The virtual site data, can be NULL
  * \param[in,out] shellfc    The shell/flexible-constraint data, can be NULL
  */
-void mdAlgorithmsSetupAtomData(const t_commrec*  cr,
-                               const t_inputrec* ir,
-                               const gmx_mtop_t& top_global,
-                               gmx_localtop_t*   top,
-                               t_forcerec*       fr,
-                               t_graph**         graph,
-                               gmx::MDAtoms*     mdAtoms,
-                               gmx::Constraints* constr,
-                               gmx_vsite_t*      vsite,
-                               gmx_shellfc_t*    shellfc);
+void mdAlgorithmsSetupAtomData(const t_commrec*     cr,
+                               const t_inputrec*    ir,
+                               const gmx_mtop_t&    top_global,
+                               gmx_localtop_t*      top,
+                               t_forcerec*          fr,
+                               ForceBuffers*        force,
+                               MDAtoms*             mdAtoms,
+                               Constraints*         constr,
+                               VirtualSitesHandler* vsite,
+                               gmx_shellfc_t*       shellfc);
+
+} // namespace gmx
 
 #endif
index b00ceec520596409c50bc96b027913a0dc0e7749..d1488c855dec91163532b7ddfe33e0a8130c05bb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -60,7 +60,7 @@
 #include "gromacs/domdec/ga2la.h"
 #include "gromacs/domdec/localatomsetmanager.h"
 #include "gromacs/domdec/mdsetup.h"
-#include "gromacs/ewald/pme.h"
+#include "gromacs/ewald/pme_pp.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/imd/imd.h"
@@ -75,6 +75,7 @@
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/nblist.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/nbnxm/nbnxm.h"
@@ -209,7 +210,7 @@ static void dd_move_cellx(gmx_domdec_t* dd, const gmx_ddbox_t* ddbox, rvec cell_
         if (applyPbc)
         {
             /* Take the minimum to avoid double communication */
-            numPulsesMin = std::min(numPulses, dd->nc[dim] - 1 - numPulses);
+            numPulsesMin = std::min(numPulses, dd->numCells[dim] - 1 - numPulses);
         }
         else
         {
@@ -243,7 +244,7 @@ static void dd_move_cellx(gmx_domdec_t* dd, const gmx_ddbox_t* ddbox, rvec cell_
         for (int pulse = 0; pulse < numPulses; pulse++)
         {
             /* Communicate all the zone information backward */
-            bool receiveValidData = (applyPbc || dd->ci[dim] < dd->nc[dim] - 1);
+            bool receiveValidData = (applyPbc || dd->ci[dim] < dd->numCells[dim] - 1);
 
             static_assert(
                     sizeof(gmx_ddzone_t) == c_ddzoneNumReals * sizeof(real),
@@ -325,8 +326,8 @@ static void dd_move_cellx(gmx_domdec_t* dd, const gmx_ddbox_t* ddbox, rvec cell_
                  */
                 buf_s[i] = buf_r[i];
             }
-            if (((applyPbc || dd->ci[dim] + numPulses < dd->nc[dim]) && pulse == numPulses - 1)
-                || (!applyPbc && dd->ci[dim] + 1 + pulse == dd->nc[dim] - 1))
+            if (((applyPbc || dd->ci[dim] + numPulses < dd->numCells[dim]) && pulse == numPulses - 1)
+                || (!applyPbc && dd->ci[dim] + 1 + pulse == dd->numCells[dim] - 1))
             {
                 /* Store the extremes */
                 int pos = 0;
@@ -462,8 +463,8 @@ static void dd_set_cginfo(gmx::ArrayRef<const int> index_gl, int cg0, int cg1, t
 {
     if (fr != nullptr)
     {
-        const cginfo_mb_t* cginfo_mb = fr->cginfo_mb;
-        gmx::ArrayRef<int> cginfo    = fr->cginfo;
+        gmx::ArrayRef<cginfo_mb_t> cginfo_mb = fr->cginfo_mb;
+        gmx::ArrayRef<int>         cginfo    = fr->cginfo;
 
         for (int cg = cg0; cg < cg1; cg++)
         {
@@ -732,7 +733,7 @@ static void comm_dd_ns_cell_sizes(gmx_domdec_t* dd, gmx_ddbox_t* ddbox, rvec cel
         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))
+        if (!(dim >= ddbox->npbcdim && (dd->ci[dim] == 0 || dd->ci[dim] == dd->numCells[dim] - 1))
             && isDlbOn(comm)
             && (comm->cell_x1[dim] - comm->cell_x0[dim]) * ddbox->skew_fac[dim] < comm->cellsize_min[dim])
         {
@@ -863,7 +864,7 @@ static void get_load_distribution(gmx_domdec_t* dd, gmx_wallcycle_t wcycle)
                 load->mdf      = 0;
                 load->pme      = 0;
                 int pos        = 0;
-                for (int i = 0; i < dd->nc[dim]; i++)
+                for (int i = 0; i < dd->numCells[dim]; i++)
                 {
                     load->sum += load->load[pos++];
                     load->max = std::max(load->max, load->load[pos]);
@@ -904,7 +905,7 @@ static void get_load_distribution(gmx_domdec_t* dd, gmx_wallcycle_t wcycle)
                 }
                 if (isDlbOn(comm) && rowMaster->dlbIsLimited)
                 {
-                    load->sum_m *= dd->nc[dim];
+                    load->sum_m *= dd->numCells[dim];
                     load->flags |= (1 << d);
                 }
             }
@@ -1258,7 +1259,7 @@ static void turn_on_dlb(const gmx::MDLogger& mdlog, gmx_domdec_t* dd, int64_t st
         {
             comm->load[d].sum_m = comm->load[d].sum;
 
-            int nc = dd->nc[dd->dim[d]];
+            int nc = dd->numCells[dd->dim[d]];
             for (int i = 0; i < nc; i++)
             {
                 rowMaster->cellFrac[i] = i / static_cast<real>(nc);
@@ -1327,7 +1328,7 @@ static void merge_cg_buffers(int                            ncell,
                              const int*                     recv_i,
                              gmx::ArrayRef<gmx::RVec>       x,
                              gmx::ArrayRef<const gmx::RVec> recv_vr,
-                             cginfo_mb_t*                   cginfo_mb,
+                             gmx::ArrayRef<cginfo_mb_t>     cginfo_mb,
                              gmx::ArrayRef<int>             cginfo)
 {
     gmx_domdec_ind_t *ind, *ind_p;
@@ -1806,12 +1807,7 @@ static void clearCommSetupData(dd_comm_setup_work_t* work)
 }
 
 //! Prepare DD communication.
-static void setup_dd_communication(gmx_domdec_t*                dd,
-                                   matrix                       box,
-                                   gmx_ddbox_t*                 ddbox,
-                                   t_forcerec*                  fr,
-                                   t_state*                     state,
-                                   PaddedHostVector<gmx::RVec>* f)
+static void setup_dd_communication(gmx_domdec_t* dd, matrix box, gmx_ddbox_t* ddbox, t_forcerec* fr, t_state* state)
 {
     int                    dim_ind, dim, dim0, dim1, dim2, dimd, nat_tot;
     int                    nzone, nzone_send, zone, zonei, cg0, cg1;
@@ -1820,7 +1816,6 @@ static void setup_dd_communication(gmx_domdec_t*                dd,
     gmx_domdec_comm_t*     comm;
     gmx_domdec_zones_t*    zones;
     gmx_domdec_comm_dim_t* cd;
-    cginfo_mb_t*           cginfo_mb;
     gmx_bool               bBondComm, bDist2B, bDistMB, bDistBonded;
     dd_corners_t           corners;
     rvec *                 normal, *v_d, *v_0 = nullptr, *v_1 = nullptr;
@@ -1894,8 +1889,8 @@ static void setup_dd_communication(gmx_domdec_t*                dd,
         v_1 = ddbox->v[dim1];
     }
 
-    zone_cg_range = zones->cg_range;
-    cginfo_mb     = fr->cginfo_mb;
+    zone_cg_range                        = zones->cg_range;
+    gmx::ArrayRef<cginfo_mb_t> cginfo_mb = fr->cginfo_mb;
 
     zone_cg_range[0]   = 0;
     zone_cg_range[1]   = dd->ncg_home;
@@ -2089,7 +2084,7 @@ static void setup_dd_communication(gmx_domdec_t*                dd,
             ddSendrecv<int>(dd, dim_ind, dddirBackward, work.atomGroupBuffer, integerBufferRef);
 
             /* Make space for cg_cm */
-            dd_check_alloc_ncg(fr, state, f, pos_cg + ind->nrecv[nzone]);
+            dd_resize_atominfo_and_state(fr, state, pos_cg + ind->nrecv[nzone]);
 
             /* Communicate the coordinates */
             gmx::ArrayRef<gmx::RVec> rvecBufferRef;
@@ -2629,27 +2624,27 @@ void print_dd_statistics(const t_commrec* cr, const t_inputrec* ir, FILE* fplog)
 }
 
 //!\brief TODO Remove fplog when group scheme and charge groups are gone
-void dd_partition_system(FILE*                        fplog,
-                         const gmx::MDLogger&         mdlog,
-                         int64_t                      step,
-                         const t_commrec*             cr,
-                         gmx_bool                     bMasterState,
-                         int                          nstglobalcomm,
-                         t_state*                     state_global,
-                         const gmx_mtop_t&            top_global,
-                         const t_inputrec*            ir,
-                         gmx::ImdSession*             imdSession,
-                         pull_t*                      pull_work,
-                         t_state*                     state_local,
-                         PaddedHostVector<gmx::RVec>* f,
-                         gmx::MDAtoms*                mdAtoms,
-                         gmx_localtop_t*              top_local,
-                         t_forcerec*                  fr,
-                         gmx_vsite_t*                 vsite,
-                         gmx::Constraints*            constr,
-                         t_nrnb*                      nrnb,
-                         gmx_wallcycle*               wcycle,
-                         gmx_bool                     bVerbose)
+void dd_partition_system(FILE*                     fplog,
+                         const gmx::MDLogger&      mdlog,
+                         int64_t                   step,
+                         const t_commrec*          cr,
+                         gmx_bool                  bMasterState,
+                         int                       nstglobalcomm,
+                         t_state*                  state_global,
+                         const gmx_mtop_t&         top_global,
+                         const t_inputrec*         ir,
+                         gmx::ImdSession*          imdSession,
+                         pull_t*                   pull_work,
+                         t_state*                  state_local,
+                         gmx::ForceBuffers*        f,
+                         gmx::MDAtoms*             mdAtoms,
+                         gmx_localtop_t*           top_local,
+                         t_forcerec*               fr,
+                         gmx::VirtualSitesHandler* vsite,
+                         gmx::Constraints*         constr,
+                         t_nrnb*                   nrnb,
+                         gmx_wallcycle*            wcycle,
+                         gmx_bool                  bVerbose)
 {
     gmx_domdec_t*      dd;
     gmx_domdec_comm_t* comm;
@@ -2865,10 +2860,10 @@ void dd_partition_system(FILE*                        fplog,
 
         set_ddbox(*dd, true, DDMASTER(dd) ? state_global->box : nullptr, true, xGlobal, &ddbox);
 
-        distributeState(mdlog, dd, top_global, state_global, ddbox, state_local, f);
+        distributeState(mdlog, dd, top_global, state_global, ddbox, state_local);
 
         /* Ensure that we have space for the new distribution */
-        dd_check_alloc_ncg(fr, state_local, f, dd->ncg_home);
+        dd_resize_atominfo_and_state(fr, state_local, dd->ncg_home);
 
         inc_nrnb(nrnb, eNR_CGCM, comm->atomRanges.numHomeAtoms());
 
@@ -2961,7 +2956,7 @@ void dd_partition_system(FILE*                        fplog,
         wallcycle_sub_start(wcycle, ewcsDD_REDIST);
 
         ncgindex_set = dd->ncg_home;
-        dd_redistribute_cg(fplog, step, dd, ddbox.tric_dir, state_local, f, fr, nrnb, &ncg_moved);
+        dd_redistribute_cg(fplog, step, dd, ddbox.tric_dir, state_local, fr, nrnb, &ncg_moved);
 
         GMX_RELEASE_ASSERT(bSortCG, "Sorting is required after redistribution");
 
@@ -3015,7 +3010,7 @@ void dd_partition_system(FILE*                        fplog,
         dd_sort_state(dd, fr, state_local);
 
         /* After sorting and compacting we set the correct size */
-        dd_resize_state(state_local, f, comm->atomRanges.numHomeAtoms());
+        state_change_natoms(state_local, comm->atomRanges.numHomeAtoms());
 
         /* Rebuild all the indices */
         dd->ga2la->clear();
@@ -3049,7 +3044,7 @@ void dd_partition_system(FILE*                        fplog,
     make_dd_indices(dd, ncgindex_set);
 
     /* Setup up the communication and communicate the coordinates */
-    setup_dd_communication(dd, state_local->box, &ddbox, fr, state_local, f);
+    setup_dd_communication(dd, state_local->box, &ddbox, fr, state_local);
 
     /* Set the indices for the halo atoms */
     make_dd_indices(dd, dd->ncg_home);
@@ -3057,11 +3052,8 @@ void dd_partition_system(FILE*                        fplog,
     /* Set the charge group boundaries for neighbor searching */
     set_cg_boundaries(&comm->zones);
 
-    if (fr->cutoff_scheme == ecutsVERLET)
-    {
-        /* When bSortCG=true, we have already set the size for zone 0 */
-        set_zones_size(dd, state_local->box, &ddbox, bSortCG ? 1 : 0, comm->zones.n, 0);
-    }
+    /* When bSortCG=true, we have already set the size for zone 0 */
+    set_zones_size(dd, state_local->box, &ddbox, bSortCG ? 1 : 0, comm->zones.n, 0);
 
     wallcycle_sub_stop(wcycle, ewcsDD_SETUPCOMM);
 
@@ -3093,7 +3085,7 @@ void dd_partition_system(FILE*                        fplog,
         switch (range)
         {
             case DDAtomRanges::Type::Vsites:
-                if (vsite && vsite->numInterUpdategroupVsites)
+                if (vsite && vsite->numInterUpdategroupVirtualSites())
                 {
                     n = dd_make_local_vsites(dd, n, top_local->idef.il);
                 }
@@ -3120,30 +3112,23 @@ void dd_partition_system(FILE*                        fplog,
      */
     state_local->natoms = comm->atomRanges.numAtomsTotal();
 
-    dd_resize_state(state_local, f, state_local->natoms);
+    state_change_natoms(state_local, state_local->natoms);
 
-    if (fr->haveDirectVirialContributions)
+    if (vsite && vsite->numInterUpdategroupVirtualSites())
     {
-        if (vsite && vsite->numInterUpdategroupVsites)
+        nat_f_novirsum = comm->atomRanges.end(DDAtomRanges::Type::Vsites);
+    }
+    else
+    {
+        if (EEL_FULL(ir->coulombtype) && dd->haveExclusions)
         {
-            nat_f_novirsum = comm->atomRanges.end(DDAtomRanges::Type::Vsites);
+            nat_f_novirsum = comm->atomRanges.end(DDAtomRanges::Type::Zones);
         }
         else
         {
-            if (EEL_FULL(ir->coulombtype) && dd->haveExclusions)
-            {
-                nat_f_novirsum = comm->atomRanges.end(DDAtomRanges::Type::Zones);
-            }
-            else
-            {
-                nat_f_novirsum = comm->atomRanges.numHomeAtoms();
-            }
+            nat_f_novirsum = comm->atomRanges.numHomeAtoms();
         }
     }
-    else
-    {
-        nat_f_novirsum = 0;
-    }
 
     /* Set the number of atoms required for the force calculation.
      * Forces need to be constrained when doing energy
@@ -3155,7 +3140,7 @@ void dd_partition_system(FILE*                        fplog,
                         comm->atomRanges.end(DDAtomRanges::Type::Constraints), nat_f_novirsum);
 
     /* Update atom data for mdatoms and several algorithms */
-    mdAlgorithmsSetupAtomData(cr, ir, top_global, top_local, fr, nullptr, mdAtoms, constr, vsite, nullptr);
+    mdAlgorithmsSetupAtomData(cr, ir, top_global, top_local, fr, f, mdAtoms, constr, vsite, nullptr);
 
     auto mdatoms = mdAtoms->mdatoms();
     if (!thisRankHasDuty(cr, DUTY_PME))
@@ -3191,7 +3176,7 @@ void dd_partition_system(FILE*                        fplog,
      * 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.rvec_array());
+    dd_move_x_vsites(*dd, state_local->box, state_local->x.rvec_array());
 
     wallcycle_sub_stop(wcycle, ewcsDD_TOPOTHER);
 
@@ -3227,14 +3212,14 @@ void dd_partition_system(FILE*                        fplog,
 }
 
 /*! \brief Check whether bonded interactions are missing, if appropriate */
-void checkNumberOfBondedInteractions(const gmx::MDLogger&  mdlog,
-                                     t_commrec*            cr,
-                                     int                   totalNumberOfBondedInteractions,
-                                     const gmx_mtop_t*     top_global,
-                                     const gmx_localtop_t* top_local,
-                                     const rvec*           x,
-                                     const matrix          box,
-                                     bool*                 shouldCheckNumberOfBondedInteractions)
+void checkNumberOfBondedInteractions(const gmx::MDLogger&           mdlog,
+                                     t_commrec*                     cr,
+                                     int                            totalNumberOfBondedInteractions,
+                                     const gmx_mtop_t*              top_global,
+                                     const gmx_localtop_t*          top_local,
+                                     gmx::ArrayRef<const gmx::RVec> x,
+                                     const matrix                   box,
+                                     bool* shouldCheckNumberOfBondedInteractions)
 {
     if (*shouldCheckNumberOfBondedInteractions)
     {
index fc07a745b195cb5d80062d9f90da75b37cfdbc43..de05c4a73847b7cebcb1f9cd78892d2074085e0d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,7 +45,8 @@
 #ifndef GMX_DOMDEC_PARTITION_H
 #define GMX_DOMDEC_PARTITION_H
 
-#include "gromacs/gpu_utils/hostallocator.h"
+#include <cstdio>
+
 #include "gromacs/math/vectypes.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
@@ -54,7 +55,6 @@ struct gmx_ddbox_t;
 struct gmx_domdec_t;
 struct gmx_localtop_t;
 struct gmx_mtop_t;
-struct gmx_vsite_t;
 struct gmx_wallcycle;
 struct pull_t;
 struct t_commrec;
@@ -65,10 +65,14 @@ class t_state;
 
 namespace gmx
 {
+template<typename>
+class ArrayRef;
 class Constraints;
+class ForceBuffers;
 class ImdSession;
 class MDAtoms;
 class MDLogger;
+class VirtualSitesHandler;
 } // namespace gmx
 
 //! Check whether the DD grid has moved too far for correctness.
@@ -97,36 +101,36 @@ void print_dd_statistics(const t_commrec* cr, const t_inputrec* ir, FILE* fplog)
  * \param[in] pull_work     Pulling data
  * \param[in] state_local   Local state
  * \param[in] f             Force buffer
- * \param[in] mdatoms       MD atoms
+ * \param[in] mdAtoms       MD atoms
  * \param[in] top_local     Local topology
  * \param[in] fr            Force record
- * \param[in] vsite         Virtual sites
+ * \param[in] vsite         Virtual sites handler
  * \param[in] constr        Constraints
  * \param[in] nrnb          Cycle counters
  * \param[in] wcycle        Timers
  * \param[in] bVerbose      Be verbose
  */
-void dd_partition_system(FILE*                             fplog,
-                         const gmx::MDLogger&              mdlog,
-                         int64_t                           step,
-                         const t_commrec*                  cr,
-                         gmx_bool                          bMasterState,
-                         int                               nstglobalcomm,
-                         t_state*                          state_global,
-                         const gmx_mtop_t&                 top_global,
-                         const t_inputrec*                 ir,
-                         gmx::ImdSession*                  imdSession,
-                         pull_t*                           pull_work,
-                         t_state*                          state_local,
-                         gmx::PaddedHostVector<gmx::RVec>* f,
-                         gmx::MDAtoms*                     mdatoms,
-                         gmx_localtop_t*                   top_local,
-                         t_forcerec*                       fr,
-                         gmx_vsite_t*                      vsite,
-                         gmx::Constraints*                 constr,
-                         t_nrnb*                           nrnb,
-                         gmx_wallcycle*                    wcycle,
-                         gmx_bool                          bVerbose);
+void dd_partition_system(FILE*                     fplog,
+                         const gmx::MDLogger&      mdlog,
+                         int64_t                   step,
+                         const t_commrec*          cr,
+                         gmx_bool                  bMasterState,
+                         int                       nstglobalcomm,
+                         t_state*                  state_global,
+                         const gmx_mtop_t&         top_global,
+                         const t_inputrec*         ir,
+                         gmx::ImdSession*          imdSession,
+                         pull_t*                   pull_work,
+                         t_state*                  state_local,
+                         gmx::ForceBuffers*        f,
+                         gmx::MDAtoms*             mdAtoms,
+                         gmx_localtop_t*           top_local,
+                         t_forcerec*               fr,
+                         gmx::VirtualSitesHandler* vsite,
+                         gmx::Constraints*         constr,
+                         t_nrnb*                   nrnb,
+                         gmx_wallcycle*            wcycle,
+                         gmx_bool                  bVerbose);
 
 /*! \brief Check whether bonded interactions are missing, if appropriate
  *
@@ -139,13 +143,13 @@ void dd_partition_system(FILE*                             fplog,
  * \param[in]    box                                    Box matrix for the error message
  * \param[in,out] shouldCheckNumberOfBondedInteractions Whether we should do the check. Always set to false.
  */
-void checkNumberOfBondedInteractions(const gmx::MDLogger&  mdlog,
-                                     t_commrec*            cr,
-                                     int                   totalNumberOfBondedInteractions,
-                                     const gmx_mtop_t*     top_global,
-                                     const gmx_localtop_t* top_local,
-                                     const rvec*           x,
-                                     const matrix          box,
-                                     bool*                 shouldCheckNumberOfBondedInteractions);
+void checkNumberOfBondedInteractions(const gmx::MDLogger&           mdlog,
+                                     t_commrec*                     cr,
+                                     int                            totalNumberOfBondedInteractions,
+                                     const gmx_mtop_t*              top_global,
+                                     const gmx_localtop_t*          top_local,
+                                     gmx::ArrayRef<const gmx::RVec> x,
+                                     const matrix                   box,
+                                     bool* shouldCheckNumberOfBondedInteractions);
 
 #endif
index eb130157c64b42808990291014d794c0e671674c..6b985c1345aa6d6a24407d1664cbab611a82282e 100644 (file)
@@ -1,8 +1,10 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2005,2006,2007,2008,2009 by the GROMACS development team.
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2017,2018,2019 by the GROMACS development team.
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -299,7 +301,7 @@ static int computeMoveFlag(const gmx_domdec_t& dd, const ivec& dev)
             flag |= DD_FLAG_BW(d);
             if (firstMoveDimValue == -1)
             {
-                if (dd.nc[dim] > 2)
+                if (dd.numCells[dim] > 2)
                 {
                     firstMoveDimValue = d * 2 + 1;
                 }
@@ -344,7 +346,7 @@ static void calc_cg_move(FILE*              fplog,
         /* Do pbc and check DD cell boundary crossings */
         for (int d = DIM - 1; d >= 0; d--)
         {
-            if (dd->nc[d] > 1)
+            if (dd->numCells[d] > 1)
             {
                 bool bScrew = (dd->unitCellInfo.haveScrewPBC && d == XX);
                 /* Determine the location of this cg in lattice coordinates */
@@ -365,7 +367,7 @@ static void calc_cg_move(FILE*              fplog,
                                       cm_new, cm_new, pos_d);
                     }
                     dev[d] = 1;
-                    if (dd->ci[d] == dd->nc[d] - 1)
+                    if (dd->ci[d] == dd->numCells[d] - 1)
                     {
                         rvec_dec(cm_new, state->box[d]);
                         if (bScrew)
@@ -468,7 +470,7 @@ static void calcGroupMove(FILE*                     fplog,
         /* Do pbc and check DD cell boundary crossings */
         for (int d = DIM - 1; d >= 0; d--)
         {
-            if (dd->nc[d] > 1)
+            if (dd->numCells[d] > 1)
             {
                 /* Determine the location of this COG in lattice coordinates */
                 real pos_d = cog[d];
@@ -488,7 +490,7 @@ static void calcGroupMove(FILE*                     fplog,
                                       cogOld, cog, pos_d);
                     }
                     dev[d] = 1;
-                    if (dd->ci[d] == dd->nc[d] - 1)
+                    if (dd->ci[d] == dd->numCells[d] - 1)
                     {
                         rvec_dec(cog, state->box[d]);
                     }
@@ -545,15 +547,14 @@ static void applyPbcAndSetMoveFlags(const gmx::UpdateGroupsCog&     updateGroups
     }
 }
 
-void dd_redistribute_cg(FILE*                        fplog,
-                        int64_t                      step,
-                        gmx_domdec_t*                dd,
-                        ivec                         tric_dir,
-                        t_state*                     state,
-                        PaddedHostVector<gmx::RVec>* f,
-                        t_forcerec*                  fr,
-                        t_nrnb*                      nrnb,
-                        int*                         ncg_moved)
+void dd_redistribute_cg(FILE*         fplog,
+                        int64_t       step,
+                        gmx_domdec_t* dd,
+                        ivec          tric_dir,
+                        t_state*      state,
+                        t_forcerec*   fr,
+                        t_nrnb*       nrnb,
+                        int*          ncg_moved)
 {
     gmx_domdec_comm_t* comm = dd->comm;
 
@@ -584,7 +585,7 @@ void dd_redistribute_cg(FILE*                        fplog,
         {
             cell_x0[d] = comm->cell_x0[d];
         }
-        if (d >= npbcdim && dd->ci[d] == dd->nc[d] - 1)
+        if (d >= npbcdim && dd->ci[d] == dd->numCells[d] - 1)
         {
             cell_x1[d] = GMX_FLOAT_MAX;
         }
@@ -736,7 +737,7 @@ void dd_redistribute_cg(FILE*                        fplog,
     /* We reuse the intBuffer without reacquiring since we are in the same scope */
     DDBufferAccess<int>& flagBuffer = moveBuffer;
 
-    const cginfo_mb_t* cginfo_mb = fr->cginfo_mb;
+    gmx::ArrayRef<const cginfo_mb_t> cginfo_mb = fr->cginfo_mb;
 
     /* Temporarily store atoms passed to our rank at the end of the range */
     int home_pos_cg = dd->ncg_home;
@@ -748,7 +749,7 @@ void dd_redistribute_cg(FILE*                        fplog,
         const int dim      = dd->dim[d];
         int       ncg_recv = 0;
         int       nvr      = 0;
-        for (int dir = 0; dir < (dd->nc[dim] == 2 ? 1 : 2); dir++)
+        for (int dir = 0; dir < (dd->numCells[dim] == 2 ? 1 : 2); dir++)
         {
             const int cdd = d * 2 + dir;
             /* Communicate the cg and atom counts */
@@ -777,7 +778,7 @@ void dd_redistribute_cg(FILE*                        fplog,
             nvr += i;
         }
 
-        dd_check_alloc_ncg(fr, state, f, home_pos_cg + ncg_recv);
+        dd_resize_atominfo_and_state(fr, state, home_pos_cg + ncg_recv);
 
         /* Process the received charge or update groups */
         int buf_pos = 0;
@@ -787,7 +788,7 @@ void dd_redistribute_cg(FILE*                        fplog,
             int              flag = flagBuffer.buffer[cg * DD_CGIBS + 1];
             const gmx::RVec& cog  = rvecBuffer.buffer[buf_pos];
 
-            if (dim >= npbcdim && dd->nc[dim] > 2)
+            if (dim >= npbcdim && dd->numCells[dim] > 2)
             {
                 /* No pbc in this dim and more than one domain boundary.
                  * We do a separate check if a charge group didn't move too far.
@@ -819,7 +820,7 @@ void dd_redistribute_cg(FILE*                        fplog,
                          * so we do not need to handle boundary crossings.
                          * This also means we do not have to handle PBC here.
                          */
-                        if (!((dd->ci[dim2] == dd->nc[dim2] - 1 && (flag & DD_FLAG_FW(d2)))
+                        if (!((dd->ci[dim2] == dd->numCells[dim2] - 1 && (flag & DD_FLAG_FW(d2)))
                               || (dd->ci[dim2] == 0 && (flag & DD_FLAG_BW(d2)))))
                         {
                             /* Clear the two flags for this dimension */
@@ -842,7 +843,7 @@ void dd_redistribute_cg(FILE*                        fplog,
                              * to an adjacent cell because of the
                              * staggering.
                              */
-                            if (pos_d >= cell_x1[dim2] && dd->ci[dim2] != dd->nc[dim2] - 1)
+                            if (pos_d >= cell_x1[dim2] && dd->ci[dim2] != dd->numCells[dim2] - 1)
                             {
                                 flag |= DD_FLAG_FW(d2);
                             }
@@ -861,7 +862,7 @@ void dd_redistribute_cg(FILE*                        fplog,
                     }
                     else if (flag & DD_FLAG_BW(d2))
                     {
-                        if (dd->nc[dd->dim[d2]] > 2)
+                        if (dd->numCells[dd->dim[d2]] > 2)
                         {
                             mc = d2 * 2 + 1;
                         }
index 732094f4d7319800e259af9e790dbe689f10ec45..68d8bd9c6ec924821cf42829fa57458efced6718 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,7 +44,7 @@
 
 #include <cstdio>
 
-#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/vectypes.h"
 #include "gromacs/utility/basedefinitions.h"
 
 struct gmx_domdec_t;
@@ -53,14 +53,13 @@ struct t_nrnb;
 class t_state;
 
 /*! \brief Redistribute the atoms to their, new, local domains */
-void dd_redistribute_cg(FILE*                             fplog,
-                        int64_t                           step,
-                        gmx_domdec_t*                     dd,
-                        ivec                              tric_dir,
-                        t_state*                          state,
-                        gmx::PaddedHostVector<gmx::RVec>* f,
-                        t_forcerec*                       fr,
-                        t_nrnb*                           nrnb,
-                        int*                              ncg_moved);
+void dd_redistribute_cg(FILE*         fplog,
+                        int64_t       step,
+                        gmx_domdec_t* dd,
+                        ivec          tric_dir,
+                        t_state*      state,
+                        t_forcerec*   fr,
+                        t_nrnb*       nrnb,
+                        int*          ncg_moved);
 
 #endif
index 478f578e355e54fff9ff0439f4ba8d5e48aac5bd..04beb7bb56740539c535dc35054766acaa47ac47 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2018, by the GROMACS development team, led by
+# Copyright (c) 2014,2018,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,5 +33,7 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(DomDecTests domdec-test
-            hashedmap.cpp
-            localatomsetmanager.cpp)
+    CPP_SOURCE_FILES
+        hashedmap.cpp
+        localatomsetmanager.cpp
+        )
index 282ad6824e957d54ba0b130270de25a336da423b..6feb1b03d3dbb94e0b28f6a8f42a039cbafbe0e6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -51,6 +51,7 @@
 #include <gtest/gtest.h>
 
 #include "gromacs/domdec/localatomset.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
 
 #include "testutils/testasserts.h"
index 01db3ead594b9480910a770afecaaf3b63f27a82..db2833c0c3990a5628b76fc180d7375ac413279c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -102,34 +102,11 @@ void check_screw_box(const matrix box)
         gmx_fatal(FARGS, "pbc=screw with non-zero box_zy is not supported");
     }
 }
-/*! \brief Resize the state and f*/
-void dd_resize_state(t_state* state, PaddedHostVector<gmx::RVec>* f, int natoms)
-{
-    if (debug)
-    {
-        fprintf(debug, "Resizing state: currently %d, required %d\n", state->natoms, natoms);
-    }
-
-    state_change_natoms(state, natoms);
-
-    if (f != nullptr)
-    {
-        /* We need to allocate one element extra, since we might use
-         * (unaligned) 4-wide SIMD loads to access rvec entries.
-         */
-        f->resizeWithPadding(natoms);
-    }
-}
 
-/*! \brief Ensure fr, state and f, if != nullptr, can hold numChargeGroups
- *         atoms for the Verlet scheme and charge groups for the group scheme.
- *
- * todo refactor this now that group scheme is removed
- */
-void dd_check_alloc_ncg(t_forcerec* fr, t_state* state, PaddedHostVector<gmx::RVec>* f, int numChargeGroups)
+void dd_resize_atominfo_and_state(t_forcerec* fr, t_state* state, const int numAtoms)
 {
-    fr->cginfo.resize(numChargeGroups);
+    fr->cginfo.resize(numAtoms);
 
     /* We use x during the setup of the atom communication */
-    dd_resize_state(state, f, numChargeGroups);
+    state_change_natoms(state, numAtoms);
 }
index 9d7db1f11235161c117f31deb98b49f368b36ea8..e3fea6e1e7de42a3a8ac38732011c4b80fa77800 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_DOMDEC_DOMDEC_UTILITY_H
 #define GMX_DOMDEC_DOMDEC_UTILITY_H
 
-#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/vectypes.h"
 #include "gromacs/mdtypes/forcerec.h"
 
 #include "domdec_internal.h"
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /*! \brief Returns true if the DLB state indicates that the balancer is on. */
 static inline bool isDlbOn(const gmx_domdec_comm_t* comm)
 {
@@ -77,14 +83,16 @@ void make_tric_corr_matrix(int npbcdim, const matrix box, matrix tcm);
 void check_screw_box(const matrix box);
 
 /*! \brief Return the charge group information flags for charge group cg */
-static inline int ddcginfo(const cginfo_mb_t* cginfo_mb, int cg)
+static inline int ddcginfo(gmx::ArrayRef<const cginfo_mb_t> cginfo_mb, int cg)
 {
-    while (cg >= cginfo_mb->cg_end)
+    size_t index = 0;
+    while (cg >= cginfo_mb[index].cg_end)
     {
-        cginfo_mb++;
+        index++;
     }
+    const cginfo_mb_t& cgimb = cginfo_mb[index];
 
-    return cginfo_mb->cginfo[(cg - cginfo_mb->cg_start) % cginfo_mb->cg_mod];
+    return cgimb.cginfo[(cg - cgimb.cg_start) % cgimb.cg_mod];
 };
 
 /*! \brief Returns the number of MD steps for which load has been recorded */
@@ -93,22 +101,13 @@ static inline int dd_load_count(const gmx_domdec_comm_t* comm)
     return (comm->ddSettings.eFlop ? comm->flop_n : comm->cycl_n[ddCyclF]);
 }
 
-/*! \brief Resize the state and f, if !=nullptr, to natoms
- *
- * \param[in]  state   Current state
- * \param[in]  f       The vector of forces to be resized
- * \param[out] natoms  New number of atoms to accommodate
- */
-void dd_resize_state(t_state* state, gmx::PaddedHostVector<gmx::RVec>* f, int natoms);
-
-/*! \brief Enrsure fr, state and f, if != nullptr, can hold numChargeGroups atoms for the Verlet scheme and charge groups for the group scheme
+/*! \brief Ensure fr and state can hold numAtoms atoms
  *
- * \param[in]  fr               Force record
- * \param[in]  state            Current state
- * \param[in]  f                The force buffer
- * \param[out] numChargeGroups  Number of charged groups
+ * \param[in]  fr        Force record
+ * \param[in]  state     Current state
+ * \param[out] numAtoms  Number of atoms
  */
-void dd_check_alloc_ncg(t_forcerec* fr, t_state* state, gmx::PaddedHostVector<gmx::RVec>* f, int numChargeGroups);
+void dd_resize_atominfo_and_state(t_forcerec* fr, t_state* state, int numAtoms);
 
 /*! \brief Returns a domain-to-domain cutoff distance given an atom-to-atom cutoff */
 static inline real atomToAtomIntoDomainToDomainCutoff(const DDSystemInfo& systemInfo, real cutoff)
index 5665a35b952afaec2454728d1f0fa97c49720440..ce8484aa16d3246a9603632288884314661b6fac 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2017,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,5 +33,6 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(EnergyAnalysisUnitTests energyanalysis-test
+    CPP_SOURCE_FILES
         legacyenergy.cpp
-)
+        )
index 70a2a6b86a93cdcaf449eab760530c0169077793..aebd672980e33fa4eadd1843f523a45086755c60 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -1183,10 +1184,10 @@ static std::unique_ptr<gmx::EssentialDynamics> ed_open(int
 /* Broadcasts the structure data */
 static void bc_ed_positions(const t_commrec* cr, struct gmx_edx* s, EssentialDynamicsStructure stype)
 {
-    snew_bc(cr, s->anrs, s->nr); /* Index numbers     */
-    snew_bc(cr, s->x, s->nr);    /* Positions         */
-    nblock_bc(cr, s->nr, s->anrs);
-    nblock_bc(cr, s->nr, s->x);
+    snew_bc(MASTER(cr), s->anrs, s->nr); /* Index numbers     */
+    snew_bc(MASTER(cr), s->x, s->nr);    /* Positions         */
+    nblock_bc(cr->mpi_comm_mygroup, s->nr, s->anrs);
+    nblock_bc(cr->mpi_comm_mygroup, s->nr, s->x);
 
     /* For the average & reference structures we need an array for the collective indices,
      * and we need to broadcast the masses as well */
@@ -1196,25 +1197,26 @@ static void bc_ed_positions(const t_commrec* cr, struct gmx_edx* s, EssentialDyn
         snew(s->c_ind, s->nr); /* Collective indices */
         /* Local atom indices get assigned in dd_make_local_group_indices.
          * There, also memory is allocated */
-        s->nalloc_loc = 0;            /* allocation size of s->anrs_loc */
-        snew_bc(cr, s->x_old, s->nr); /* To be able to always make the ED molecule whole, ... */
-        nblock_bc(cr, s->nr, s->x_old); /* ... keep track of shift changes with the help of old coords */
+        s->nalloc_loc = 0;                    /* allocation size of s->anrs_loc */
+        snew_bc(MASTER(cr), s->x_old, s->nr); /* To be able to always make the ED molecule whole, ... */
+        nblock_bc(cr->mpi_comm_mygroup, s->nr,
+                  s->x_old); /* ... keep track of shift changes with the help of old coords */
     }
 
     /* broadcast masses for the reference structure (for mass-weighted fitting) */
     if (stype == EssentialDynamicsStructure::Reference)
     {
-        snew_bc(cr, s->m, s->nr);
-        nblock_bc(cr, s->nr, s->m);
+        snew_bc(MASTER(cr), s->m, s->nr);
+        nblock_bc(cr->mpi_comm_mygroup, s->nr, s->m);
     }
 
     /* For the average structure we might need the masses for mass-weighting */
     if (stype == EssentialDynamicsStructure::Average)
     {
-        snew_bc(cr, s->sqrtm, s->nr);
-        nblock_bc(cr, s->nr, s->sqrtm);
-        snew_bc(cr, s->m, s->nr);
-        nblock_bc(cr, s->nr, s->m);
+        snew_bc(MASTER(cr), s->sqrtm, s->nr);
+        nblock_bc(cr->mpi_comm_mygroup, s->nr, s->sqrtm);
+        snew_bc(MASTER(cr), s->m, s->nr);
+        nblock_bc(cr->mpi_comm_mygroup, s->nr, s->m);
     }
 }
 
@@ -1224,23 +1226,23 @@ static void bc_ed_vecs(const t_commrec* cr, t_eigvec* ev, int length)
 {
     int i;
 
-    snew_bc(cr, ev->ieig, ev->neig);    /* index numbers of eigenvector  */
-    snew_bc(cr, ev->stpsz, ev->neig);   /* stepsizes per eigenvector     */
-    snew_bc(cr, ev->xproj, ev->neig);   /* instantaneous x projection    */
-    snew_bc(cr, ev->fproj, ev->neig);   /* instantaneous f projection    */
-    snew_bc(cr, ev->refproj, ev->neig); /* starting or target projection */
+    snew_bc(MASTER(cr), ev->ieig, ev->neig);    /* index numbers of eigenvector  */
+    snew_bc(MASTER(cr), ev->stpsz, ev->neig);   /* stepsizes per eigenvector     */
+    snew_bc(MASTER(cr), ev->xproj, ev->neig);   /* instantaneous x projection    */
+    snew_bc(MASTER(cr), ev->fproj, ev->neig);   /* instantaneous f projection    */
+    snew_bc(MASTER(cr), ev->refproj, ev->neig); /* starting or target projection */
 
-    nblock_bc(cr, ev->neig, ev->ieig);
-    nblock_bc(cr, ev->neig, ev->stpsz);
-    nblock_bc(cr, ev->neig, ev->xproj);
-    nblock_bc(cr, ev->neig, ev->fproj);
-    nblock_bc(cr, ev->neig, ev->refproj);
+    nblock_bc(cr->mpi_comm_mygroup, ev->neig, ev->ieig);
+    nblock_bc(cr->mpi_comm_mygroup, ev->neig, ev->stpsz);
+    nblock_bc(cr->mpi_comm_mygroup, ev->neig, ev->xproj);
+    nblock_bc(cr->mpi_comm_mygroup, ev->neig, ev->fproj);
+    nblock_bc(cr->mpi_comm_mygroup, ev->neig, ev->refproj);
 
-    snew_bc(cr, ev->vec, ev->neig); /* Eigenvector components        */
+    snew_bc(MASTER(cr), ev->vec, ev->neig); /* Eigenvector components        */
     for (i = 0; i < ev->neig; i++)
     {
-        snew_bc(cr, ev->vec[i], length);
-        nblock_bc(cr, length, ev->vec[i]);
+        snew_bc(MASTER(cr), ev->vec[i], length);
+        nblock_bc(cr->mpi_comm_mygroup, length, ev->vec[i]);
     }
 }
 
@@ -1250,17 +1252,17 @@ static void bc_ed_vecs(const t_commrec* cr, t_eigvec* ev, int length)
 static void broadcast_ed_data(const t_commrec* cr, gmx_edsam* ed)
 {
     /* Master lets the other nodes know if its ED only or also flooding */
-    gmx_bcast(sizeof(ed->eEDtype), &(ed->eEDtype), cr);
+    gmx_bcast(sizeof(ed->eEDtype), &(ed->eEDtype), cr->mpi_comm_mygroup);
 
     int numedis = ed->edpar.size();
     /* First let everybody know how many ED data sets to expect */
-    gmx_bcast(sizeof(numedis), &numedis, cr);
-    nblock_abc(cr, numedis, &(ed->edpar));
+    gmx_bcast(sizeof(numedis), &numedis, cr->mpi_comm_mygroup);
+    nblock_abc(MASTER(cr), cr->mpi_comm_mygroup, numedis, &(ed->edpar));
     /* Now transfer the ED data set(s) */
     for (auto& edi : ed->edpar)
     {
         /* Broadcast a single ED data set */
-        block_bc(cr, edi);
+        block_bc(cr->mpi_comm_mygroup, edi);
 
         /* Broadcast positions */
         bc_ed_positions(cr, &(edi.sref),
@@ -1283,10 +1285,10 @@ static void broadcast_ed_data(const t_commrec* cr, gmx_edsam* ed)
         /* For harmonic restraints the reference projections can change with time */
         if (edi.flood.bHarmonic)
         {
-            snew_bc(cr, edi.flood.initialReferenceProjection, edi.flood.vecs.neig);
-            snew_bc(cr, edi.flood.referenceProjectionSlope, edi.flood.vecs.neig);
-            nblock_bc(cr, edi.flood.vecs.neig, edi.flood.initialReferenceProjection);
-            nblock_bc(cr, edi.flood.vecs.neig, edi.flood.referenceProjectionSlope);
+            snew_bc(MASTER(cr), edi.flood.initialReferenceProjection, edi.flood.vecs.neig);
+            snew_bc(MASTER(cr), edi.flood.referenceProjectionSlope, edi.flood.vecs.neig);
+            nblock_bc(cr->mpi_comm_mygroup, edi.flood.vecs.neig, edi.flood.initialReferenceProjection);
+            nblock_bc(cr->mpi_comm_mygroup, edi.flood.vecs.neig, edi.flood.referenceProjectionSlope);
         }
     }
 }
@@ -2773,7 +2775,7 @@ std::unique_ptr<gmx::EssentialDynamics> init_edsam(const gmx::MDLogger&        m
             /* Remove PBC, make molecule(s) subject to ED whole. */
             snew(x_pbc, mtop->natoms);
             copy_rvecn(globalState->x.rvec_array(), x_pbc, 0, mtop->natoms);
-            do_pbc_first_mtop(nullptr, ir->ePBC, globalState->box, mtop, x_pbc);
+            do_pbc_first_mtop(nullptr, ir->pbcType, globalState->box, mtop, x_pbc);
         }
         /* Reset pointer to first ED data set which contains the actual ED data */
         auto edi = ed->edpar.begin();
@@ -3028,7 +3030,7 @@ std::unique_ptr<gmx::EssentialDynamics> init_edsam(const gmx::MDLogger&        m
     for (auto edi = ed->edpar.begin(); edi != ed->edpar.end(); ++edi)
     {
         /* Allocate space for ED buffer variables */
-        snew_bc(cr, edi->buf, 1); /* MASTER has already allocated edi->buf in init_edi() */
+        snew_bc(MASTER(cr), edi->buf, 1); /* MASTER has already allocated edi->buf in init_edi() */
         snew(edi->buf->do_edsam, 1);
 
         /* Space for collective ED buffer variables */
index 7ec53375d7e0255b4112085183e21084d99045f7..502abde6beae2c787b6ad4a7a9df21b41560f1db 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 13377b7b3e64b6f8d060cb0c08ab016dea23caf2..ba9faa378e93ad6155e43b81cd1681610c33c8c6 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -53,7 +54,7 @@ gmx_add_libgromacs_sources(
     pme_coordinate_receiver_gpu_impl.cpp
     pme_force_sender_gpu_impl.cpp
     )
-if (GMX_USE_CUDA)
+if (GMX_GPU_CUDA)
     gmx_add_libgromacs_sources(
         # CUDA-specific sources
         pme_gather.cu
@@ -69,12 +70,12 @@ if (GMX_USE_CUDA)
         pme_gpu_internal.cpp
         pme_gpu_timings.cpp
         )
-    gmx_compile_cpp_as_cuda(
+    _gmx_add_files_to_property(CUDA_SOURCES
+        # Must add these files so they can include device_information.h
         pme_gpu_internal.cpp
-        pme_gpu_program.cpp
         pme_gpu_timings.cpp
-        )
-elseif (GMX_USE_OPENCL)
+       )
+elseif (GMX_GPU_OPENCL)
     gmx_add_libgromacs_sources(
         # OpenCL-specific sources
         pme_gpu_3dfft_ocl.cpp
@@ -84,6 +85,15 @@ elseif (GMX_USE_OPENCL)
         pme_gpu_internal.cpp
         pme_gpu_timings.cpp
         )
+elseif (GMX_GPU_SYCL)
+    # SYCL-TODO: proper implementation
+    gmx_add_libgromacs_sources(
+        pme_gpu_program_impl.cpp
+        )
+    _gmx_add_files_to_property(SYCL_SOURCES
+        pme_gpu_program_impl.cpp
+        pme_gpu_program.cpp
+        )
 else()
     gmx_add_libgromacs_sources(
         # Files that implement stubs
@@ -94,3 +104,54 @@ endif()
 if (BUILD_TESTING)
     add_subdirectory(tests)
 endif()
+
+
+set(PME_OCL_KERNEL_SOURCES
+    "${CMAKE_CURRENT_SOURCE_DIR}/pme_gpu_calculate_splines.clh"
+    "${CMAKE_CURRENT_SOURCE_DIR}/pme_solve.clh"
+    "${CMAKE_CURRENT_SOURCE_DIR}/pme_gather.clh"
+    "${CMAKE_CURRENT_SOURCE_DIR}/pme_spread.clh")
+
+if(CLANG_TIDY_EXE)
+   set(OCL_COMPILER "${CLANG_TIDY_EXE}")
+   set(CLANG_TIDY_ARGS "-quiet;-checks=*,-readability-implicit-bool-conversion,-llvm-header-guard,-hicpp-signed-bitwise,-clang-analyzer-deadcode.DeadStores,-google-readability-todo,-clang-diagnostic-padded,-fcomment-block-commands=internal;--;${CMAKE_C_COMPILER}")
+else()
+   set(OCL_COMPILER "${CMAKE_C_COMPILER}")
+endif()
+
+# TODO: test all warp sizes on all vendor targets?
+foreach(VENDOR AMD NVIDIA INTEL)
+    foreach(WARPSIZE 16 32 64)
+        math(EXPR SPREAD_WG_SIZE "8*${WARPSIZE}")
+        math(EXPR SOLVE_WG_SIZE "8*${WARPSIZE}")
+        math(EXPR GATHER_WG_SIZE "4*${WARPSIZE}")
+        set(OBJ_FILE pme_ocl_kernel_warpSize${WARPSIZE}_${VENDOR}.o)
+        add_custom_command(OUTPUT ${OBJ_FILE} COMMAND ${OCL_COMPILER}
+        ${CMAKE_CURRENT_SOURCE_DIR}/pme_program.cl ${CLANG_TIDY_ARGS}
+        -Xclang -finclude-default-header  -D_${VENDOR}_SOURCE_
+        -Dwarp_size=${WARPSIZE}
+        -Dorder=4
+        -DthreadsPerAtom=16
+        -Dc_pmeMaxUnitcellShift=2
+        -Dc_skipNeutralAtoms=false
+        -Dc_virialAndEnergyCount=7
+        -Dc_spreadWorkGroupSize=${SPREAD_WG_SIZE}
+        -Dc_solveMaxWorkGroupSize=${SOLVE_WG_SIZE}
+        -Dc_gatherWorkGroupSize=${GATHER_WG_SIZE}
+        -DDIM=3 -DXX=0 -DYY=1 -DZZ=2
+        -DwrapX=true -DwrapY=true
+        -c -I ${CMAKE_SOURCE_DIR}/src -std=cl1.2
+        -Weverything  -Wno-conversion -Wno-missing-variable-declarations -Wno-used-but-marked-unused
+        -Wno-cast-align -Wno-incompatible-pointer-types
+        # to avoid  "warning: unknown command tag name" for \internal
+        -Wno-documentation-unknown-command
+        # to avoid pme_gpu_types.h:100:52: warning: padding struct 'struct PmeGpuConstParams' with 4 bytes to align 'd_virialAndEnergy'
+        -Wno-padded
+        -o${OBJ_FILE}
+        )
+        list(APPEND PME_OCL_KERNELS ${OBJ_FILE})
+    endforeach()
+endforeach()
+
+add_custom_target(ocl_pme_kernels DEPENDS ${PME_OCL_KERNELS} )
+gmx_set_custom_target_output(ocl_pme_kernels ${PME_OCL_KERNELS})
index 3471ac0a7ac0eb4507a47fb21d46d5415bc10325..09dc3998fb67f01018a761d46d0a24e4a835a2bd 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,8 +49,6 @@
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/smalloc.h"
 
-#include "pme_internal.h"
-
 static void make_dft_mod(real* mod, const double* data, int splineOrder, int ndata)
 {
     for (int i = 0; i < ndata; i++)
index 24c8fef6dfeaadc3d1fd9a371b16f95df4869d9d..abdf4223f3e648933da128f0d4506ebc18ef43fb 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -37,7 +37,7 @@
 #ifndef GMX_EWALD_CALCULATE_SPLINE_MODULI_H
 #define GMX_EWALD_CALCULATE_SPLINE_MODULI_H
 
-#include "pme_internal.h"
+#include "spline_vectors.h"
 
 /* Calulate plain SPME B-spline interpolation */
 void make_bspline_moduli(splinevec bsp_mod, int nx, int ny, int nz, int order);
index a114edbf505e86cc396e8a175b2521fb7e822e6e..6e089c2b99c511867ac4f7fa33c608667d4e5c00 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -65,6 +66,7 @@
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
index 61669276407f61e036a5b7cc39e0b54b46479dda..77fc79b3fa16b066e4ffd752bb59f0acfe4253bd 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 16733a15f716e28bb4b59939280cf12a02cf7c7f..b62a25e58ccb22529fffbab3f8846b1212730747 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
 
-#include "pme_internal.h"
-
 /* There's nothing special to do here if just masses are perturbed,
  * but if either charge or type is perturbed then the implementation
  * requires that B states are defined for both charge and type, and
index 786318dda77d56f536de256b730981f5ec78dce0..df0a7405695f847ce1e8d2b8c9440a8fb978300c 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index a30d67b3a2e63ef58fb8e62ac624d66daaa91787..912216964c66a664b8de8c460a5629e43cb3aa2f 100644 (file)
@@ -99,6 +99,7 @@
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/timing/cyclecounter.h"
 #include "gromacs/timing/wallcycle.h"
@@ -158,7 +159,7 @@ bool pme_gpu_supports_build(std::string* error)
     {
         errorReasons.emplace_back("a double-precision build");
     }
-    if (GMX_GPU == GMX_GPU_NONE)
+    if (!GMX_GPU)
     {
         errorReasons.emplace_back("a non-GPU build");
     }
@@ -169,7 +170,7 @@ bool pme_gpu_supports_hardware(const gmx_hw_info_t gmx_unused& hwinfo, std::stri
 {
     std::list<std::string> errorReasons;
 
-    if (GMX_GPU == GMX_GPU_OPENCL)
+    if (GMX_GPU_OPENCL)
     {
 #ifdef __APPLE__
         errorReasons.emplace_back("Apple OS X operating system");
@@ -178,7 +179,7 @@ bool pme_gpu_supports_hardware(const gmx_hw_info_t gmx_unused& hwinfo, std::stri
     return addMessageIfNotSupported(errorReasons, error);
 }
 
-bool pme_gpu_supports_input(const t_inputrec& ir, const gmx_mtop_t& mtop, std::string* error)
+bool pme_gpu_supports_input(const t_inputrec& ir, std::string* error)
 {
     std::list<std::string> errorReasons;
     if (!EEL_PME(ir.coulombtype))
@@ -189,14 +190,6 @@ bool pme_gpu_supports_input(const t_inputrec& ir, const gmx_mtop_t& mtop, std::s
     {
         errorReasons.emplace_back("interpolation orders other than 4");
     }
-    if (ir.efep != efepNO)
-    {
-        if (gmx_mtop_has_perturbed_charges(mtop))
-        {
-            errorReasons.emplace_back(
-                    "free energy calculations with perturbed charges (multiple grids)");
-        }
-    }
     if (EVDW_PME(ir.vdwtype))
     {
         errorReasons.emplace_back("Lennard-Jones PME");
@@ -230,10 +223,6 @@ static bool pme_gpu_check_restrictions(const gmx_pme_t* pme, std::string* error)
     {
         errorReasons.emplace_back("interpolation orders other than 4");
     }
-    if (pme->bFEP)
-    {
-        errorReasons.emplace_back("free energy calculations (multiple grids)");
-    }
     if (pme->doLJ)
     {
         errorReasons.emplace_back("Lennard-Jones PME");
@@ -242,7 +231,7 @@ static bool pme_gpu_check_restrictions(const gmx_pme_t* pme, std::string* error)
     {
         errorReasons.emplace_back("double precision");
     }
-    if (GMX_GPU == GMX_GPU_NONE)
+    if (!GMX_GPU)
     {
         errorReasons.emplace_back("non-GPU build of GROMACS");
     }
@@ -561,20 +550,21 @@ static int div_round_up(int enumerator, int denominator)
     return (enumerator + denominator - 1) / denominator;
 }
 
-gmx_pme_t* gmx_pme_init(const t_commrec*         cr,
-                        const NumPmeDomains&     numPmeDomains,
-                        const t_inputrec*        ir,
-                        gmx_bool                 bFreeEnergy_q,
-                        gmx_bool                 bFreeEnergy_lj,
-                        gmx_bool                 bReproducible,
-                        real                     ewaldcoeff_q,
-                        real                     ewaldcoeff_lj,
-                        int                      nthread,
-                        PmeRunMode               runMode,
-                        PmeGpu*                  pmeGpu,
-                        const gmx_device_info_t* gpuInfo,
-                        PmeGpuProgramHandle      pmeGpuProgram,
-                        const gmx::MDLogger&     mdlog)
+gmx_pme_t* gmx_pme_init(const t_commrec*     cr,
+                        const NumPmeDomains& numPmeDomains,
+                        const t_inputrec*    ir,
+                        gmx_bool             bFreeEnergy_q,
+                        gmx_bool             bFreeEnergy_lj,
+                        gmx_bool             bReproducible,
+                        real                 ewaldcoeff_q,
+                        real                 ewaldcoeff_lj,
+                        int                  nthread,
+                        PmeRunMode           runMode,
+                        PmeGpu*              pmeGpu,
+                        const DeviceContext* deviceContext,
+                        const DeviceStream*  deviceStream,
+                        const PmeGpuProgram* pmeGpuProgram,
+                        const gmx::MDLogger& mdlog)
 {
     int  use_threads, sum_use_threads, i;
     ivec ndata;
@@ -684,7 +674,7 @@ gmx_pme_t* gmx_pme_init(const t_commrec*         cr,
     }
     pme->bUseThreads = (sum_use_threads > 0);
 
-    if (ir->ePBC == epbcSCREW)
+    if (ir->pbcType == PbcType::Screw)
     {
         gmx_fatal(FARGS, "pme does not (yet) work with pbc = screw");
     }
@@ -877,22 +867,23 @@ gmx_pme_t* gmx_pme_init(const t_commrec*         cr,
         pme->atc.emplace_back(pme->mpi_comm_d[1], pme->nthread, pme->pme_order, secondDimIndex, doSpread);
     }
 
-    if (pme_gpu_active(pme.get()))
+    // Initial check of validity of the input for running on the GPU
+    if (pme->runMode != PmeRunMode::CPU)
     {
-        if (!pme->gpu)
+        std::string errorString;
+        bool        canRunOnGpu = pme_gpu_check_restrictions(pme.get(), &errorString);
+        if (!canRunOnGpu)
         {
-            // Initial check of validity of the data
-            std::string errorString;
-            bool        canRunOnGpu = pme_gpu_check_restrictions(pme.get(), &errorString);
-            if (!canRunOnGpu)
-            {
-                GMX_THROW(gmx::NotImplementedError(errorString));
-            }
+            GMX_THROW(gmx::NotImplementedError(errorString));
         }
-
-        pme_gpu_reinit(pme.get(), gpuInfo, pmeGpuProgram);
+        pme_gpu_reinit(pme.get(), deviceContext, deviceStream, pmeGpuProgram);
+    }
+    else
+    {
+        GMX_ASSERT(pme->gpu == nullptr, "Should not have PME GPU object when PME is on a CPU.");
     }
 
+
     pme_init_all_work(&pme->solve_work, pme->nthread, pme->nkx);
 
     // no exception was thrown during the init, so we hand over the PME structure handle
@@ -911,7 +902,7 @@ void gmx_pme_reinit(struct gmx_pme_t** pmedata,
     // TODO: This would be better as just copying a sub-structure that contains
     // all the PME parameters and nothing else.
     t_inputrec irc;
-    irc.ePBC                   = ir->ePBC;
+    irc.pbcType                = ir->pbcType;
     irc.coulombtype            = ir->coulombtype;
     irc.vdwtype                = ir->vdwtype;
     irc.efep                   = ir->efep;
@@ -932,13 +923,13 @@ void gmx_pme_reinit(struct gmx_pme_t** pmedata,
         NumPmeDomains numPmeDomains = { pme_src->nnodes_major, pme_src->nnodes_minor };
         *pmedata = gmx_pme_init(cr, numPmeDomains, &irc, pme_src->bFEP_q, pme_src->bFEP_lj, FALSE,
                                 ewaldcoeff_q, ewaldcoeff_lj, pme_src->nthread, pme_src->runMode,
-                                pme_src->gpu, nullptr, nullptr, dummyLogger);
+                                pme_src->gpu, nullptr, nullptr, nullptr, dummyLogger);
         /* When running PME on the CPU not using domain decomposition,
          * the atom data is allocated once only in gmx_pme_(re)init().
          */
         if (!pme_src->gpu && pme_src->nnodes == 1)
         {
-            gmx_pme_reinit_atoms(*pmedata, pme_src->atc[0].numAtoms(), nullptr);
+            gmx_pme_reinit_atoms(*pmedata, pme_src->atc[0].numAtoms(), nullptr, nullptr);
         }
         // TODO this is mostly passing around current values
     }
@@ -1024,7 +1015,7 @@ int gmx_pme_do(struct gmx_pme_t*              pme,
                real                           lambda_lj,
                real*                          dvdlambda_q,
                real*                          dvdlambda_lj,
-               int                            flags)
+               const gmx::StepWorkload&       stepWork)
 {
     GMX_ASSERT(pme->runMode == PmeRunMode::CPU,
                "gmx_pme_do should not be called on the GPU PME run.");
@@ -1044,9 +1035,8 @@ int gmx_pme_do(struct gmx_pme_t*              pme,
     gmx_bool             bFirst, bDoSplines;
     int                  fep_state;
     int                  fep_states_lj = pme->bFEP_lj ? 2 : 1;
-    const gmx_bool       bCalcEnerVir  = (flags & GMX_PME_CALC_ENER_VIR) != 0;
-    const gmx_bool       bBackFFT      = (flags & (GMX_PME_CALC_F | GMX_PME_CALC_POT)) != 0;
-    const gmx_bool       bCalcF        = (flags & GMX_PME_CALC_F) != 0;
+    // There's no support for computing energy without virial, or vice versa
+    const bool computeEnergyAndVirial = (stepWork.computeEnergy || stepWork.computeVirial);
 
     /* We could be passing lambda!=0 while no q or LJ is actually perturbed */
     if (!pme->bFEP_q)
@@ -1161,41 +1151,37 @@ int gmx_pme_do(struct gmx_pme_t*              pme,
             fprintf(debug, "Rank= %6d, pme local particles=%6d\n", cr->nodeid, atc.numAtoms());
         }
 
-        if (flags & GMX_PME_SPREAD)
+        wallcycle_start(wcycle, ewcPME_SPREAD);
+
+        /* Spread the coefficients on a grid */
+        spread_on_grid(pme, &atc, pmegrid, bFirst, TRUE, fftgrid, bDoSplines, grid_index);
+
+        if (bFirst)
         {
-            wallcycle_start(wcycle, ewcPME_SPREAD);
+            inc_nrnb(nrnb, eNR_WEIGHTS, DIM * atc.numAtoms());
+        }
+        inc_nrnb(nrnb, eNR_SPREADBSP, pme->pme_order * pme->pme_order * pme->pme_order * atc.numAtoms());
 
-            /* Spread the coefficients on a grid */
-            spread_on_grid(pme, &atc, pmegrid, bFirst, TRUE, fftgrid, bDoSplines, grid_index);
+        if (!pme->bUseThreads)
+        {
+            wrap_periodic_pmegrid(pme, grid);
 
-            if (bFirst)
+            /* sum contributions to local grid from other nodes */
+            if (pme->nnodes > 1)
             {
-                inc_nrnb(nrnb, eNR_WEIGHTS, DIM * atc.numAtoms());
+                gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_FORWARD);
             }
-            inc_nrnb(nrnb, eNR_SPREADBSP,
-                     pme->pme_order * pme->pme_order * pme->pme_order * atc.numAtoms());
-
-            if (!pme->bUseThreads)
-            {
-                wrap_periodic_pmegrid(pme, grid);
-
-                /* sum contributions to local grid from other nodes */
-                if (pme->nnodes > 1)
-                {
-                    gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_FORWARD);
-                }
 
-                copy_pmegrid_to_fftgrid(pme, grid, fftgrid, grid_index);
-            }
+            copy_pmegrid_to_fftgrid(pme, grid, fftgrid, grid_index);
+        }
 
-            wallcycle_stop(wcycle, ewcPME_SPREAD);
+        wallcycle_stop(wcycle, ewcPME_SPREAD);
 
-            /* TODO If the OpenMP and single-threaded implementations
-               converge, then spread_on_grid() and
-               copy_pmegrid_to_fftgrid() will perhaps live in the same
-               source file.
-             */
-        }
+        /* TODO If the OpenMP and single-threaded implementations
+           converge, then spread_on_grid() and
+           copy_pmegrid_to_fftgrid() will perhaps live in the same
+           source file.
+        */
 
         /* Here we start a large thread parallel region */
 #pragma omp parallel num_threads(pme->nthread) private(thread)
@@ -1203,75 +1189,69 @@ int gmx_pme_do(struct gmx_pme_t*              pme,
             try
             {
                 thread = gmx_omp_get_thread_num();
-                if (flags & GMX_PME_SOLVE)
-                {
-                    int loop_count;
-
-                    /* do 3d-fft */
-                    if (thread == 0)
-                    {
-                        wallcycle_start(wcycle, ewcPME_FFT);
-                    }
-                    gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_REAL_TO_COMPLEX, thread, wcycle);
-                    if (thread == 0)
-                    {
-                        wallcycle_stop(wcycle, ewcPME_FFT);
-                    }
+                int loop_count;
 
-                    /* solve in k-space for our local cells */
-                    if (thread == 0)
-                    {
-                        wallcycle_start(wcycle, (grid_index < DO_Q ? ewcPME_SOLVE : ewcLJPME));
-                    }
-                    if (grid_index < DO_Q)
-                    {
-                        loop_count = solve_pme_yzx(
-                                pme, cfftgrid, scaledBox[XX][XX] * scaledBox[YY][YY] * scaledBox[ZZ][ZZ],
-                                bCalcEnerVir, pme->nthread, thread);
-                    }
-                    else
-                    {
-                        loop_count = solve_pme_lj_yzx(
-                                pme, &cfftgrid, FALSE,
-                                scaledBox[XX][XX] * scaledBox[YY][YY] * scaledBox[ZZ][ZZ],
-                                bCalcEnerVir, pme->nthread, thread);
-                    }
+                /* do 3d-fft */
+                if (thread == 0)
+                {
+                    wallcycle_start(wcycle, ewcPME_FFT);
+                }
+                gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_REAL_TO_COMPLEX, thread, wcycle);
+                if (thread == 0)
+                {
+                    wallcycle_stop(wcycle, ewcPME_FFT);
+                }
 
-                    if (thread == 0)
-                    {
-                        wallcycle_stop(wcycle, (grid_index < DO_Q ? ewcPME_SOLVE : ewcLJPME));
-                        inc_nrnb(nrnb, eNR_SOLVEPME, loop_count);
-                    }
+                /* solve in k-space for our local cells */
+                if (thread == 0)
+                {
+                    wallcycle_start(wcycle, (grid_index < DO_Q ? ewcPME_SOLVE : ewcLJPME));
+                }
+                if (grid_index < DO_Q)
+                {
+                    loop_count = solve_pme_yzx(
+                            pme, cfftgrid, scaledBox[XX][XX] * scaledBox[YY][YY] * scaledBox[ZZ][ZZ],
+                            computeEnergyAndVirial, pme->nthread, thread);
+                }
+                else
+                {
+                    loop_count =
+                            solve_pme_lj_yzx(pme, &cfftgrid, FALSE,
+                                             scaledBox[XX][XX] * scaledBox[YY][YY] * scaledBox[ZZ][ZZ],
+                                             computeEnergyAndVirial, pme->nthread, thread);
                 }
 
-                if (bBackFFT)
+                if (thread == 0)
                 {
-                    /* do 3d-invfft */
-                    if (thread == 0)
-                    {
-                        wallcycle_start(wcycle, ewcPME_FFT);
-                    }
-                    gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_COMPLEX_TO_REAL, thread, wcycle);
-                    if (thread == 0)
-                    {
-                        wallcycle_stop(wcycle, ewcPME_FFT);
+                    wallcycle_stop(wcycle, (grid_index < DO_Q ? ewcPME_SOLVE : ewcLJPME));
+                    inc_nrnb(nrnb, eNR_SOLVEPME, loop_count);
+                }
 
+                /* do 3d-invfft */
+                if (thread == 0)
+                {
+                    wallcycle_start(wcycle, ewcPME_FFT);
+                }
+                gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_COMPLEX_TO_REAL, thread, wcycle);
+                if (thread == 0)
+                {
+                    wallcycle_stop(wcycle, ewcPME_FFT);
 
-                        if (pme->nodeid == 0)
-                        {
-                            real ntot = pme->nkx * pme->nky * pme->nkz;
-                            npme      = static_cast<int>(ntot * std::log(ntot) / std::log(2.0));
-                            inc_nrnb(nrnb, eNR_FFT, 2 * npme);
-                        }
 
-                        /* Note: this wallcycle region is closed below
-                           outside an OpenMP region, so take care if
-                           refactoring code here. */
-                        wallcycle_start(wcycle, ewcPME_GATHER);
+                    if (pme->nodeid == 0)
+                    {
+                        real ntot = pme->nkx * pme->nky * pme->nkz;
+                        npme      = static_cast<int>(ntot * std::log(ntot) / std::log(2.0));
+                        inc_nrnb(nrnb, eNR_FFT, 2 * npme);
                     }
 
-                    copy_fftgrid_to_pmegrid(pme, fftgrid, grid, grid_index, pme->nthread, thread);
+                    /* Note: this wallcycle region is closed below
+                       outside an OpenMP region, so take care if
+                       refactoring code here. */
+                    wallcycle_start(wcycle, ewcPME_GATHER);
                 }
+
+                copy_fftgrid_to_pmegrid(pme, fftgrid, grid, grid_index, pme->nthread, thread);
             }
             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
         }
@@ -1279,18 +1259,15 @@ int gmx_pme_do(struct gmx_pme_t*              pme,
          * With MPI we have to synchronize here before gmx_sum_qgrid_dd.
          */
 
-        if (bBackFFT)
+        /* distribute local grid to all nodes */
+        if (pme->nnodes > 1)
         {
-            /* distribute local grid to all nodes */
-            if (pme->nnodes > 1)
-            {
-                gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_BACKWARD);
-            }
-
-            unwrap_periodic_pmegrid(pme, grid);
+            gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_BACKWARD);
         }
 
-        if (bCalcF)
+        unwrap_periodic_pmegrid(pme, grid);
+
+        if (stepWork.computeForces)
         {
             /* interpolate forces for our local atoms */
 
@@ -1320,7 +1297,7 @@ int gmx_pme_do(struct gmx_pme_t*              pme,
             wallcycle_stop(wcycle, ewcPME_GATHER);
         }
 
-        if (bCalcEnerVir)
+        if (computeEnergyAndVirial)
         {
             /* This should only be called on the master thread
              * and after the threads have synchronized.
@@ -1412,85 +1389,77 @@ int gmx_pme_do(struct gmx_pme_t*              pme,
                 calc_next_lb_coeffs(coefficientBuffer, local_sigma);
                 grid = pmegrid->grid.grid;
 
-                if (flags & GMX_PME_SPREAD)
-                {
-                    wallcycle_start(wcycle, ewcPME_SPREAD);
-                    /* Spread the c6 on a grid */
-                    spread_on_grid(pme, &atc, pmegrid, bFirst, TRUE, fftgrid, bDoSplines, grid_index);
+                wallcycle_start(wcycle, ewcPME_SPREAD);
+                /* Spread the c6 on a grid */
+                spread_on_grid(pme, &atc, pmegrid, bFirst, TRUE, fftgrid, bDoSplines, grid_index);
 
-                    if (bFirst)
-                    {
-                        inc_nrnb(nrnb, eNR_WEIGHTS, DIM * atc.numAtoms());
-                    }
+                if (bFirst)
+                {
+                    inc_nrnb(nrnb, eNR_WEIGHTS, DIM * atc.numAtoms());
+                }
 
-                    inc_nrnb(nrnb, eNR_SPREADBSP,
-                             pme->pme_order * pme->pme_order * pme->pme_order * atc.numAtoms());
-                    if (pme->nthread == 1)
+                inc_nrnb(nrnb, eNR_SPREADBSP,
+                         pme->pme_order * pme->pme_order * pme->pme_order * atc.numAtoms());
+                if (pme->nthread == 1)
+                {
+                    wrap_periodic_pmegrid(pme, grid);
+                    /* sum contributions to local grid from other nodes */
+                    if (pme->nnodes > 1)
                     {
-                        wrap_periodic_pmegrid(pme, grid);
-                        /* sum contributions to local grid from other nodes */
-                        if (pme->nnodes > 1)
-                        {
-                            gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_FORWARD);
-                        }
-                        copy_pmegrid_to_fftgrid(pme, grid, fftgrid, grid_index);
+                        gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_FORWARD);
                     }
-                    wallcycle_stop(wcycle, ewcPME_SPREAD);
+                    copy_pmegrid_to_fftgrid(pme, grid, fftgrid, grid_index);
                 }
+                wallcycle_stop(wcycle, ewcPME_SPREAD);
+
                 /*Here we start a large thread parallel region*/
 #pragma omp parallel num_threads(pme->nthread) private(thread)
                 {
                     try
                     {
                         thread = gmx_omp_get_thread_num();
-                        if (flags & GMX_PME_SOLVE)
+                        /* do 3d-fft */
+                        if (thread == 0)
                         {
-                            /* do 3d-fft */
-                            if (thread == 0)
-                            {
-                                wallcycle_start(wcycle, ewcPME_FFT);
-                            }
+                            wallcycle_start(wcycle, ewcPME_FFT);
+                        }
 
-                            gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_REAL_TO_COMPLEX, thread, wcycle);
-                            if (thread == 0)
-                            {
-                                wallcycle_stop(wcycle, ewcPME_FFT);
-                            }
+                        gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_REAL_TO_COMPLEX, thread, wcycle);
+                        if (thread == 0)
+                        {
+                            wallcycle_stop(wcycle, ewcPME_FFT);
                         }
                     }
                     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
                 }
                 bFirst = FALSE;
             }
-            if (flags & GMX_PME_SOLVE)
-            {
-                /* solve in k-space for our local cells */
+            /* solve in k-space for our local cells */
 #pragma omp parallel num_threads(pme->nthread) private(thread)
+            {
+                try
                 {
-                    try
+                    int loop_count;
+                    thread = gmx_omp_get_thread_num();
+                    if (thread == 0)
                     {
-                        int loop_count;
-                        thread = gmx_omp_get_thread_num();
-                        if (thread == 0)
-                        {
-                            wallcycle_start(wcycle, ewcLJPME);
-                        }
+                        wallcycle_start(wcycle, ewcLJPME);
+                    }
 
-                        loop_count = solve_pme_lj_yzx(
-                                pme, &pme->cfftgrid[2], TRUE,
-                                scaledBox[XX][XX] * scaledBox[YY][YY] * scaledBox[ZZ][ZZ],
-                                bCalcEnerVir, pme->nthread, thread);
-                        if (thread == 0)
-                        {
-                            wallcycle_stop(wcycle, ewcLJPME);
-                            inc_nrnb(nrnb, eNR_SOLVEPME, loop_count);
-                        }
+                    loop_count =
+                            solve_pme_lj_yzx(pme, &pme->cfftgrid[2], TRUE,
+                                             scaledBox[XX][XX] * scaledBox[YY][YY] * scaledBox[ZZ][ZZ],
+                                             computeEnergyAndVirial, pme->nthread, thread);
+                    if (thread == 0)
+                    {
+                        wallcycle_stop(wcycle, ewcLJPME);
+                        inc_nrnb(nrnb, eNR_SOLVEPME, loop_count);
                     }
-                    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
                 }
+                GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
             }
 
-            if (bCalcEnerVir)
+            if (computeEnergyAndVirial)
             {
                 /* This should only be called on the master thread and
                  * after the threads have synchronized.
@@ -1498,88 +1467,85 @@ int gmx_pme_do(struct gmx_pme_t*              pme,
                 get_pme_ener_vir_lj(pme->solve_work, pme->nthread, &output[fep_state]);
             }
 
-            if (bBackFFT)
+            bFirst = !pme->doCoulomb;
+            calc_initial_lb_coeffs(coefficientBuffer, local_c6, local_sigma);
+            for (grid_index = 8; grid_index >= 2; --grid_index)
             {
-                bFirst = !pme->doCoulomb;
-                calc_initial_lb_coeffs(coefficientBuffer, local_c6, local_sigma);
-                for (grid_index = 8; grid_index >= 2; --grid_index)
-                {
-                    /* Unpack structure */
-                    pmegrid    = &pme->pmegrid[grid_index];
-                    fftgrid    = pme->fftgrid[grid_index];
-                    pfft_setup = pme->pfft_setup[grid_index];
-                    grid       = pmegrid->grid.grid;
-                    calc_next_lb_coeffs(coefficientBuffer, local_sigma);
+                /* Unpack structure */
+                pmegrid    = &pme->pmegrid[grid_index];
+                fftgrid    = pme->fftgrid[grid_index];
+                pfft_setup = pme->pfft_setup[grid_index];
+                grid       = pmegrid->grid.grid;
+                calc_next_lb_coeffs(coefficientBuffer, local_sigma);
 #pragma omp parallel num_threads(pme->nthread) private(thread)
+                {
+                    try
                     {
-                        try
+                        thread = gmx_omp_get_thread_num();
+                        /* do 3d-invfft */
+                        if (thread == 0)
                         {
-                            thread = gmx_omp_get_thread_num();
-                            /* do 3d-invfft */
-                            if (thread == 0)
-                            {
-                                wallcycle_start(wcycle, ewcPME_FFT);
-                            }
+                            wallcycle_start(wcycle, ewcPME_FFT);
+                        }
 
-                            gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_COMPLEX_TO_REAL, thread, wcycle);
-                            if (thread == 0)
-                            {
-                                wallcycle_stop(wcycle, ewcPME_FFT);
+                        gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_COMPLEX_TO_REAL, thread, wcycle);
+                        if (thread == 0)
+                        {
+                            wallcycle_stop(wcycle, ewcPME_FFT);
 
 
-                                if (pme->nodeid == 0)
-                                {
-                                    real ntot = pme->nkx * pme->nky * pme->nkz;
-                                    npme = static_cast<int>(ntot * std::log(ntot) / std::log(2.0));
-                                    inc_nrnb(nrnb, eNR_FFT, 2 * npme);
-                                }
-                                wallcycle_start(wcycle, ewcPME_GATHER);
+                            if (pme->nodeid == 0)
+                            {
+                                real ntot = pme->nkx * pme->nky * pme->nkz;
+                                npme      = static_cast<int>(ntot * std::log(ntot) / std::log(2.0));
+                                inc_nrnb(nrnb, eNR_FFT, 2 * npme);
                             }
-
-                            copy_fftgrid_to_pmegrid(pme, fftgrid, grid, grid_index, pme->nthread, thread);
+                            wallcycle_start(wcycle, ewcPME_GATHER);
                         }
-                        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
-                    } /*#pragma omp parallel*/
 
-                    /* distribute local grid to all nodes */
-                    if (pme->nnodes > 1)
-                    {
-                        gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_BACKWARD);
+                        copy_fftgrid_to_pmegrid(pme, fftgrid, grid, grid_index, pme->nthread, thread);
                     }
+                    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
+                } /*#pragma omp parallel*/
 
-                    unwrap_periodic_pmegrid(pme, grid);
+                /* distribute local grid to all nodes */
+                if (pme->nnodes > 1)
+                {
+                    gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_BACKWARD);
+                }
 
-                    if (bCalcF)
-                    {
-                        /* interpolate forces for our local atoms */
-                        bClearF = (bFirst && PAR(cr));
-                        scale   = pme->bFEP ? (fep_state < 1 ? 1.0 - lambda_lj : lambda_lj) : 1.0;
-                        scale *= lb_scale_factor[grid_index - 2];
+                unwrap_periodic_pmegrid(pme, grid);
+
+                if (stepWork.computeForces)
+                {
+                    /* interpolate forces for our local atoms */
+                    bClearF = (bFirst && PAR(cr));
+                    scale   = pme->bFEP ? (fep_state < 1 ? 1.0 - lambda_lj : lambda_lj) : 1.0;
+                    scale *= lb_scale_factor[grid_index - 2];
 
 #pragma omp parallel for num_threads(pme->nthread) schedule(static)
-                        for (thread = 0; thread < pme->nthread; thread++)
+                    for (thread = 0; thread < pme->nthread; thread++)
+                    {
+                        try
                         {
-                            try
-                            {
-                                gather_f_bsplines(pme, grid, bClearF, &pme->atc[0],
-                                                  &pme->atc[0].spline[thread], scale);
-                            }
-                            GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
+                            gather_f_bsplines(pme, grid, bClearF, &pme->atc[0],
+                                              &pme->atc[0].spline[thread], scale);
                         }
+                        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
+                    }
 
 
-                        inc_nrnb(nrnb, eNR_GATHERFBSP,
-                                 pme->pme_order * pme->pme_order * pme->pme_order * pme->atc[0].numAtoms());
-                    }
-                    wallcycle_stop(wcycle, ewcPME_GATHER);
+                    inc_nrnb(nrnb, eNR_GATHERFBSP,
+                             pme->pme_order * pme->pme_order * pme->pme_order * pme->atc[0].numAtoms());
+                }
+                wallcycle_stop(wcycle, ewcPME_GATHER);
 
-                    bFirst = FALSE;
-                } /* for (grid_index = 8; grid_index >= 2; --grid_index) */
-            }     /* if (bCalcF) */
-        }         /* for (fep_state = 0; fep_state < fep_states_lj; ++fep_state) */
-    }             /* if ((flags & GMX_PME_DO_LJ) && pme->ljpme_combination_rule == eljpmeLB) */
+                bFirst = FALSE;
+            } /* for (grid_index = 8; grid_index >= 2; --grid_index) */
+        }     /* for (fep_state = 0; fep_state < fep_states_lj; ++fep_state) */
+    }         /* if (pme->doLJ && pme->ljpme_combination_rule == eljpmeLB) */
 
-    if (bCalcF && pme->nnodes > 1)
+    if (stepWork.computeForces && pme->nnodes > 1)
     {
         wallcycle_start(wcycle, ewcPME_REDISTXF);
         for (d = 0; d < pme->ndecompdim; d++)
@@ -1604,7 +1570,7 @@ int gmx_pme_do(struct gmx_pme_t*              pme,
         wallcycle_stop(wcycle, ewcPME_REDISTXF);
     }
 
-    if (bCalcEnerVir)
+    if (computeEnergyAndVirial)
     {
         if (pme->doCoulomb)
         {
@@ -1719,7 +1685,7 @@ void gmx_pme_destroy(gmx_pme_t* pme)
 
     destroy_pme_spline_work(pme->spline_work);
 
-    if (pme_gpu_active(pme) && pme->gpu)
+    if (pme->gpu != nullptr)
     {
         pme_gpu_destroy(pme->gpu);
     }
@@ -1727,11 +1693,13 @@ void gmx_pme_destroy(gmx_pme_t* pme)
     delete pme;
 }
 
-void gmx_pme_reinit_atoms(gmx_pme_t* pme, const int numAtoms, const real* charges)
+void gmx_pme_reinit_atoms(gmx_pme_t* pme, const int numAtoms, const real* chargesA, const real* chargesB)
 {
-    if (pme_gpu_active(pme))
+    if (pme->gpu != nullptr)
     {
-        pme_gpu_reinit_atoms(pme->gpu, numAtoms, charges);
+        GMX_ASSERT(!(pme->bFEP_q && chargesB == nullptr),
+                   "B state charges must be specified if running Coulomb FEP on the GPU");
+        pme_gpu_reinit_atoms(pme->gpu, numAtoms, chargesA, pme->bFEP_q ? chargesB : nullptr);
     }
     else
     {
@@ -1739,3 +1707,8 @@ void gmx_pme_reinit_atoms(gmx_pme_t* pme, const int numAtoms, const real* charge
         // TODO: set the charges here as well
     }
 }
+
+bool gmx_pme_grid_matches(const gmx_pme_t& pme, const ivec grid_size)
+{
+    return (pme.nkx == grid_size[XX] && pme.nky == grid_size[YY] && pme.nkz == grid_size[ZZ]);
+}
index f30a975a229a9964b399a2178087ea9fe1b6af53..af0e258ae98fff341a239bfa03446f9a44f67ff5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "pme_gpu_types.h"
 
 /*! \brief \internal
- * A single structure encompassing all the PME data used in CUDA kernels.
- * This inherits from PmeGpuKernelParamsBase and adds a couple cudaTextureObject_t handles,
- * which we would like to avoid in plain C++.
+ * An alias for PME parameters in CUDA.
+ * \todo Remove if we decide to unify CUDA and OpenCL
  */
 struct PmeGpuCudaKernelParams : PmeGpuKernelParamsBase
 {
-    /* These are CUDA texture objects, related to the grid size. */
-    /*! \brief CUDA texture object for accessing grid.d_fractShiftsTable */
-    cudaTextureObject_t fractShiftsTableTexture;
-    /*! \brief CUDA texture object for accessing grid.d_gridlineIndicesTable */
-    cudaTextureObject_t gridlineIndicesTableTexture;
+    // Place CUDA-specific stuff here
 };
 
 #endif
index c7efd52f9911829d9485660c79a7661452de53c1..0408b87f77be914e4029659fca3845ace8fd446e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/gpu_utils/devicebuffer_datatype.h"
 #include "gromacs/gpu_utils/gpu_macros.h"
 #include "gromacs/math/vectypes.h"
-#include "gromacs/timing/walltime_accounting.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
 struct gmx_hw_info_t;
-struct interaction_const_t;
 struct t_commrec;
-struct t_forcerec;
 struct t_inputrec;
 struct t_nrnb;
 struct PmeGpu;
 struct gmx_wallclock_gpu_pme_t;
-struct gmx_device_info_t;
 struct gmx_enerdata_t;
 struct gmx_mtop_t;
 struct gmx_pme_t;
 struct gmx_wallcycle;
 struct NumPmeDomains;
 
+class DeviceContext;
+class DeviceStream;
 enum class GpuTaskCompletion;
 class PmeGpuProgram;
 class GpuEventSynchronizer;
-//! Convenience name.
-using PmeGpuProgramHandle = const PmeGpuProgram*;
 
 namespace gmx
 {
-class PmePpCommGpu;
+template<typename>
+class ArrayRef;
 class ForceWithVirial;
 class MDLogger;
 enum class PinningPolicy : int;
+class StepWorkload;
 } // namespace gmx
 
 enum
@@ -104,17 +102,12 @@ enum class PmeRunMode
     Mixed, //!< Mixed mode: only spread and gather run on GPU; FFT and solving are done on CPU.
 };
 
-//! PME gathering output forces treatment
-enum class PmeForceOutputHandling
-{
-    Set,             /**< Gather simply writes into provided force buffer */
-    ReduceWithInput, /**< Gather adds its output to the buffer.
-                        On GPU, that means additional H2D copy before the kernel launch. */
-};
-
 /*! \brief Return the smallest allowed PME grid size for \p pmeOrder */
 int minimalPmeGridSize(int pmeOrder);
 
+//! Return whether the grid of \c pme is identical to \c grid_size.
+bool gmx_pme_grid_matches(const gmx_pme_t& pme, const ivec grid_size);
+
 /*! \brief Check restrictions on pme_order and the PME grid nkx,nky,nkz.
  *
  * With errorsAreFatal=true, an exception or fatal error is generated
@@ -140,43 +133,40 @@ bool gmx_pme_check_restrictions(int  pme_order,
  * \returns  Pointer to newly allocated and initialized PME data.
  *
  * \todo We should evolve something like a \c GpuManager that holds \c
- * gmx_device_info_t * and \c PmeGpuProgramHandle and perhaps other
+ * DeviceInformation* and \c PmeGpuProgram* and perhaps other
  * related things whose lifetime can/should exceed that of a task (or
- * perhaps task manager). See Redmine #2522.
+ * perhaps task manager). See Issue #2522.
  */
-gmx_pme_t* gmx_pme_init(const t_commrec*         cr,
-                        const NumPmeDomains&     numPmeDomains,
-                        const t_inputrec*        ir,
-                        gmx_bool                 bFreeEnergy_q,
-                        gmx_bool                 bFreeEnergy_lj,
-                        gmx_bool                 bReproducible,
-                        real                     ewaldcoeff_q,
-                        real                     ewaldcoeff_lj,
-                        int                      nthread,
-                        PmeRunMode               runMode,
-                        PmeGpu*                  pmeGpu,
-                        const gmx_device_info_t* gpuInfo,
-                        PmeGpuProgramHandle      pmeGpuProgram,
-                        const gmx::MDLogger&     mdlog);
+gmx_pme_t* gmx_pme_init(const t_commrec*     cr,
+                        const NumPmeDomains& numPmeDomains,
+                        const t_inputrec*    ir,
+                        gmx_bool             bFreeEnergy_q,
+                        gmx_bool             bFreeEnergy_lj,
+                        gmx_bool             bReproducible,
+                        real                 ewaldcoeff_q,
+                        real                 ewaldcoeff_lj,
+                        int                  nthread,
+                        PmeRunMode           runMode,
+                        PmeGpu*              pmeGpu,
+                        const DeviceContext* deviceContext,
+                        const DeviceStream*  deviceStream,
+                        const PmeGpuProgram* pmeGpuProgram,
+                        const gmx::MDLogger& mdlog);
+
+/*! \brief As gmx_pme_init, but takes most settings, except the grid/Ewald coefficients, from
+ * pme_src. This is only called when the PME cut-off/grid size changes.
+ */
+void gmx_pme_reinit(gmx_pme_t**       pmedata,
+                    const t_commrec*  cr,
+                    gmx_pme_t*        pme_src,
+                    const t_inputrec* ir,
+                    const ivec        grid_size,
+                    real              ewaldcoeff_q,
+                    real              ewaldcoeff_lj);
 
 /*! \brief Destroys the PME data structure.*/
 void gmx_pme_destroy(gmx_pme_t* pme);
 
-//@{
-/*! \brief Flag values that control what gmx_pme_do() will calculate
- *
- * These can be combined with bitwise-OR if more than one thing is required.
- */
-#define GMX_PME_SPREAD (1 << 0)
-#define GMX_PME_SOLVE (1 << 1)
-#define GMX_PME_CALC_F (1 << 2)
-#define GMX_PME_CALC_ENER_VIR (1 << 3)
-/* This forces the grid to be backtransformed even without GMX_PME_CALC_F */
-#define GMX_PME_CALC_POT (1 << 4)
-
-#define GMX_PME_DO_ALL_F (GMX_PME_SPREAD | GMX_PME_SOLVE | GMX_PME_CALC_F)
-//@}
-
 /*! \brief Do a PME calculation on a CPU for the long range electrostatics and/or LJ.
  *
  * Computes the PME forces and the energy and viral, when requested,
@@ -211,74 +201,17 @@ int gmx_pme_do(struct gmx_pme_t*              pme,
                real                           lambda_lj,
                real*                          dvdlambda_q,
                real*                          dvdlambda_lj,
-               int                            flags);
-
-/*! \brief Called on the nodes that do PME exclusively */
-int gmx_pmeonly(struct gmx_pme_t*         pme,
-                const t_commrec*          cr,
-                t_nrnb*                   mynrnb,
-                gmx_wallcycle*            wcycle,
-                gmx_walltime_accounting_t walltime_accounting,
-                t_inputrec*               ir,
-                PmeRunMode                runMode);
+               const gmx::StepWorkload&       stepWork);
 
 /*! \brief Calculate the PME grid energy V for n charges.
  *
  * The potential (found in \p pme) must have been found already with a
- * call to gmx_pme_do() with at least GMX_PME_SPREAD and GMX_PME_SOLVE
- * specified. Note that the charges are not spread on the grid in the
+ * call to gmx_pme_do(). Note that the charges are not spread on the grid in the
  * pme struct. Currently does not work in parallel or with free
  * energy.
  */
 void gmx_pme_calc_energy(gmx_pme_t* pme, gmx::ArrayRef<const gmx::RVec> x, gmx::ArrayRef<const real> q, real* V);
 
-/*! \brief Send the charges and maxshift to out PME-only node. */
-void gmx_pme_send_parameters(const t_commrec*           cr,
-                             const interaction_const_t* ic,
-                             gmx_bool                   bFreeEnergy_q,
-                             gmx_bool                   bFreeEnergy_lj,
-                             real*                      chargeA,
-                             real*                      chargeB,
-                             real*                      sqrt_c6A,
-                             real*                      sqrt_c6B,
-                             real*                      sigmaA,
-                             real*                      sigmaB,
-                             int                        maxshift_x,
-                             int                        maxshift_y);
-
-/*! \brief Send the coordinates to our PME-only node and request a PME calculation */
-void gmx_pme_send_coordinates(t_forcerec*           fr,
-                              const t_commrec*      cr,
-                              const matrix          box,
-                              const rvec*           x,
-                              real                  lambda_q,
-                              real                  lambda_lj,
-                              gmx_bool              bEnerVir,
-                              int64_t               step,
-                              bool                  useGpuPmePpComms,
-                              bool                  reinitGpuPmePpComms,
-                              bool                  sendCoordinatesFromGpu,
-                              GpuEventSynchronizer* coordinatesReadyOnDeviceEvent,
-                              gmx_wallcycle*        wcycle);
-
-/*! \brief Tell our PME-only node to finish */
-void gmx_pme_send_finish(const t_commrec* cr);
-
-/*! \brief Tell our PME-only node to reset all cycle and flop counters */
-void gmx_pme_send_resetcounters(const t_commrec* cr, int64_t step);
-
-/*! \brief PP nodes receive the long range forces from the PME nodes */
-void gmx_pme_receive_f(gmx::PmePpCommGpu*    pmePpCommGpu,
-                       const t_commrec*      cr,
-                       gmx::ForceWithVirial* forceWithVirial,
-                       real*                 energy_q,
-                       real*                 energy_lj,
-                       real*                 dvdlambda_q,
-                       real*                 dvdlambda_lj,
-                       bool                  useGpuPmePpComms,
-                       bool                  receivePmeForceToGpu,
-                       float*                pme_cycles);
-
 /*! \brief
  * This function updates the local atom data on GPU after DD (charges, coordinates, etc.).
  * TODO: it should update the PME CPU atom data as well.
@@ -286,9 +219,12 @@ void gmx_pme_receive_f(gmx::PmePpCommGpu*    pmePpCommGpu,
  *
  * \param[in,out] pme        The PME structure.
  * \param[in]     numAtoms   The number of particles.
- * \param[in]     charges    The pointer to the array of particle charges.
+ * \param[in]     chargesA   The pointer to the array of particle charges in the normal state or FEP
+ * state A. Can be nullptr if PME is not performed on the GPU.
+ * \param[in]     chargesB   The pointer to the array of particle charges in state B. Only used if
+ * charges are perturbed and can otherwise be nullptr.
  */
-void gmx_pme_reinit_atoms(gmx_pme_t* pme, int numAtoms, const real* charges);
+void gmx_pme_reinit_atoms(gmx_pme_t* pme, int numAtoms, const real* chargesA, const real* chargesB);
 
 /* A block of PME GPU functions */
 
@@ -318,12 +254,11 @@ bool pme_gpu_supports_hardware(const gmx_hw_info_t& hwinfo, std::string* error);
  * formed gmx_pme_t structure. Should that one go away/work with inputrec?
  *
  * \param[in]  ir     Input system.
- * \param[in]  mtop   Complete system topology to check if an FE simulation perturbs charges.
  * \param[out] error  If non-null, the error message if the input is not supported on GPU.
  *
  * \returns true if PME can run on GPU with this input, false otherwise.
  */
-bool pme_gpu_supports_input(const t_inputrec& ir, const gmx_mtop_t& mtop, std::string* error);
+bool pme_gpu_supports_input(const t_inputrec& ir, std::string* error);
 
 /*! \brief
  * Returns the active PME codepath (CPU, GPU, mixed).
@@ -353,11 +288,14 @@ inline bool pme_gpu_task_enabled(const gmx_pme_t* pme)
     return (pme != nullptr) && (pme_run_mode(pme) != PmeRunMode::CPU);
 }
 
-/*! \brief Returns the size of the padding needed by GPU version of PME in the coordinates array.
+/*! \brief Returns the block size requirement
+ *
+ * The GPU version of PME requires that the coordinates array have a
+ * size divisible by the returned number.
  *
  * \param[in]  pme  The PME data structure.
  */
-GPU_FUNC_QUALIFIER int pme_gpu_get_padding_size(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme))
+GPU_FUNC_QUALIFIER int pme_gpu_get_block_size(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme))
         GPU_FUNC_TERM_WITH_RETURN(0);
 
 // The following functions are all the PME GPU entry points,
@@ -384,52 +322,52 @@ GPU_FUNC_QUALIFIER void pme_gpu_get_timings(const gmx_pme_t* GPU_FUNC_ARGUMENT(p
 /*! \brief
  * Prepares PME on GPU computation (updating the box if needed)
  * \param[in] pme               The PME data structure.
- * \param[in] needToUpdateBox   Tells if the stored unit cell parameters should be updated from \p box.
  * \param[in] box               The unit cell box.
  * \param[in] wcycle            The wallclock counter.
- * \param[in] flags             The combination of flags to affect this PME computation.
- *                              The flags are the GMX_PME_ flags from pme.h.
- * \param[in]  useGpuForceReduction Whether PME forces are reduced on GPU this step or should be downloaded for CPU reduction
+ * \param[in] stepWork          The required work for this simulation step
  */
-GPU_FUNC_QUALIFIER void pme_gpu_prepare_computation(gmx_pme_t*   GPU_FUNC_ARGUMENT(pme),
-                                                    bool         GPU_FUNC_ARGUMENT(needToUpdateBox),
-                                                    const matrix GPU_FUNC_ARGUMENT(box),
+GPU_FUNC_QUALIFIER void pme_gpu_prepare_computation(gmx_pme_t*     GPU_FUNC_ARGUMENT(pme),
+                                                    const matrix   GPU_FUNC_ARGUMENT(box),
                                                     gmx_wallcycle* GPU_FUNC_ARGUMENT(wcycle),
-                                                    int            GPU_FUNC_ARGUMENT(flags),
-                                                    bool GPU_FUNC_ARGUMENT(useGpuForceReduction)) GPU_FUNC_TERM;
+                                                    const gmx::StepWorkload& GPU_FUNC_ARGUMENT(stepWork)) GPU_FUNC_TERM;
 
 /*! \brief
  * Launches first stage of PME on GPU - spreading kernel.
  *
  * \param[in] pme                The PME data structure.
- * \param[in] xReadyOnDevice     Event synchronizer indicating that the coordinates are ready in the device memory; nullptr allowed only on separate PME ranks.
+ * \param[in] xReadyOnDevice     Event synchronizer indicating that the coordinates
+ * are ready in the device memory; nullptr allowed only on separate PME ranks.
  * \param[in] wcycle             The wallclock counter.
+ * \param[in] lambdaQ            The Coulomb lambda of the current state of the
+ * system. Only used if FEP of Coulomb is active.
  */
 GPU_FUNC_QUALIFIER void pme_gpu_launch_spread(gmx_pme_t*            GPU_FUNC_ARGUMENT(pme),
                                               GpuEventSynchronizer* GPU_FUNC_ARGUMENT(xReadyOnDevice),
-                                              gmx_wallcycle* GPU_FUNC_ARGUMENT(wcycle)) GPU_FUNC_TERM;
+                                              gmx_wallcycle*        GPU_FUNC_ARGUMENT(wcycle),
+                                              const real GPU_FUNC_ARGUMENT(lambdaQ)) GPU_FUNC_TERM;
 
 /*! \brief
  * Launches middle stages of PME (FFT R2C, solving, FFT C2R) either on GPU or on CPU, depending on the run mode.
  *
  * \param[in] pme               The PME data structure.
  * \param[in] wcycle            The wallclock counter.
+ * \param[in] stepWork          The required work for this simulation step
  */
-GPU_FUNC_QUALIFIER void pme_gpu_launch_complex_transforms(gmx_pme_t* GPU_FUNC_ARGUMENT(pme),
-                                                          gmx_wallcycle* GPU_FUNC_ARGUMENT(wcycle)) GPU_FUNC_TERM;
+GPU_FUNC_QUALIFIER void
+pme_gpu_launch_complex_transforms(gmx_pme_t*               GPU_FUNC_ARGUMENT(pme),
+                                  gmx_wallcycle*           GPU_FUNC_ARGUMENT(wcycle),
+                                  const gmx::StepWorkload& GPU_FUNC_ARGUMENT(stepWork)) GPU_FUNC_TERM;
 
 /*! \brief
  * Launches last stage of PME on GPU - force gathering and D2H force transfer.
  *
- * \param[in]  pme               The PME data structure.
- * \param[in]  wcycle            The wallclock counter.
- * \param[in]  forceTreatment    Tells how data should be treated. The gathering kernel either
- * stores the output reciprocal forces into the host array, or copies its contents to the GPU first
- *                               and accumulates. The reduction is non-atomic.
+ * \param[in] pme               The PME data structure.
+ * \param[in] wcycle            The wallclock counter.
+ * \param[in] lambdaQ           The Coulomb lambda to use when calculating the results.
  */
 GPU_FUNC_QUALIFIER void pme_gpu_launch_gather(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme),
                                               gmx_wallcycle*   GPU_FUNC_ARGUMENT(wcycle),
-                                              PmeForceOutputHandling GPU_FUNC_ARGUMENT(forceTreatment)) GPU_FUNC_TERM;
+                                              const real GPU_FUNC_ARGUMENT(lambdaQ)) GPU_FUNC_TERM;
 
 /*! \brief
  * Attempts to complete PME GPU tasks.
@@ -441,22 +379,22 @@ GPU_FUNC_QUALIFIER void pme_gpu_launch_gather(const gmx_pme_t* GPU_FUNC_ARGUMENT
  * by assigning the ArrayRef to the \p forces pointer passed in.
  * Virial/energy are also outputs if they were to be computed.
  *
- * \param[in]  pme            The PME data structure.
- * \param[in]  flags          The combination of flags to affect this PME computation.
- *                            The flags are the GMX_PME_ flags from pme.h.
- * \param[in]  wcycle         The wallclock counter.
+ * \param[in]  pme             The PME data structure.
+ * \param[in]  stepWork        The required work for this simulation step
+ * \param[in]  wcycle          The wallclock counter.
  * \param[out] forceWithVirial The output force and virial
  * \param[out] enerd           The output energies
- * \param[in] flags            The combination of flags to affect this PME computation.
- *                             The flags are the GMX_PME_ flags from pme.h.
+ * \param[in]  lambdaQ         The Coulomb lambda to use when calculating the results.
  * \param[in]  completionKind  Indicates whether PME task completion should only be checked rather
- * than waited for \returns                   True if the PME GPU tasks have completed
+ *                             than waited for
+ * \returns                    True if the PME GPU tasks have completed
  */
-GPU_FUNC_QUALIFIER bool pme_gpu_try_finish_task(gmx_pme_t*            GPU_FUNC_ARGUMENT(pme),
-                                                int                   GPU_FUNC_ARGUMENT(flags),
-                                                gmx_wallcycle*        GPU_FUNC_ARGUMENT(wcycle),
+GPU_FUNC_QUALIFIER bool pme_gpu_try_finish_task(gmx_pme_t*               GPU_FUNC_ARGUMENT(pme),
+                                                const gmx::StepWorkload& GPU_FUNC_ARGUMENT(stepWork),
+                                                gmx_wallcycle*           GPU_FUNC_ARGUMENT(wcycle),
                                                 gmx::ForceWithVirial* GPU_FUNC_ARGUMENT(forceWithVirial),
                                                 gmx_enerdata_t*       GPU_FUNC_ARGUMENT(enerd),
+                                                const real            GPU_FUNC_ARGUMENT(lambdaQ),
                                                 GpuTaskCompletion GPU_FUNC_ARGUMENT(completionKind))
         GPU_FUNC_TERM_WITH_RETURN(false);
 
@@ -465,17 +403,18 @@ GPU_FUNC_QUALIFIER bool pme_gpu_try_finish_task(gmx_pme_t*            GPU_FUNC_A
  * (if they were to be computed).
  *
  * \param[in]  pme             The PME data structure.
- * \param[in]  flags           The combination of flags to affect this PME computation.
- *                             The flags are the GMX_PME_ flags from pme.h.
+ * \param[in]  stepWork        The required work for this simulation step
  * \param[in]  wcycle          The wallclock counter.
  * \param[out] forceWithVirial The output force and virial
  * \param[out] enerd           The output energies
+ * \param[in]  lambdaQ         The Coulomb lambda to use when calculating the results.
  */
-GPU_FUNC_QUALIFIER void pme_gpu_wait_and_reduce(gmx_pme_t*            GPU_FUNC_ARGUMENT(pme),
-                                                int                   GPU_FUNC_ARGUMENT(flags),
-                                                gmx_wallcycle*        GPU_FUNC_ARGUMENT(wcycle),
+GPU_FUNC_QUALIFIER void pme_gpu_wait_and_reduce(gmx_pme_t*               GPU_FUNC_ARGUMENT(pme),
+                                                const gmx::StepWorkload& GPU_FUNC_ARGUMENT(stepWork),
+                                                gmx_wallcycle*           GPU_FUNC_ARGUMENT(wcycle),
                                                 gmx::ForceWithVirial* GPU_FUNC_ARGUMENT(forceWithVirial),
-                                                gmx_enerdata_t* GPU_FUNC_ARGUMENT(enerd)) GPU_FUNC_TERM;
+                                                gmx_enerdata_t*       GPU_FUNC_ARGUMENT(enerd),
+                                                const real GPU_FUNC_ARGUMENT(lambdaQ)) GPU_FUNC_TERM;
 
 /*! \brief
  * The PME GPU reinitialization function that is called both at the end of any PME computation and on any load balancing.
@@ -493,20 +432,12 @@ GPU_FUNC_QUALIFIER void pme_gpu_wait_and_reduce(gmx_pme_t*            GPU_FUNC_A
 GPU_FUNC_QUALIFIER void pme_gpu_reinit_computation(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme),
                                                    gmx_wallcycle* GPU_FUNC_ARGUMENT(wcycle)) GPU_FUNC_TERM;
 
-
-/*! \brief Get pointer to device copy of coordinate data.
- * \param[in] pme            The PME data structure.
- * \returns                  Pointer to coordinate data
- */
-GPU_FUNC_QUALIFIER DeviceBuffer<float> pme_gpu_get_device_x(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme))
-        GPU_FUNC_TERM_WITH_RETURN(DeviceBuffer<float>{});
-
 /*! \brief Set pointer to device copy of coordinate data.
  * \param[in] pme            The PME data structure.
  * \param[in] d_x            The pointer to the positions buffer to be set
  */
-GPU_FUNC_QUALIFIER void pme_gpu_set_device_x(const gmx_pme_t*    GPU_FUNC_ARGUMENT(pme),
-                                             DeviceBuffer<float> GPU_FUNC_ARGUMENT(d_x)) GPU_FUNC_TERM;
+GPU_FUNC_QUALIFIER void pme_gpu_set_device_x(const gmx_pme_t*        GPU_FUNC_ARGUMENT(pme),
+                                             DeviceBuffer<gmx::RVec> GPU_FUNC_ARGUMENT(d_x)) GPU_FUNC_TERM;
 
 /*! \brief Get pointer to device copy of force data.
  * \param[in] pme            The PME data structure.
@@ -515,20 +446,6 @@ GPU_FUNC_QUALIFIER void pme_gpu_set_device_x(const gmx_pme_t*    GPU_FUNC_ARGUME
 GPU_FUNC_QUALIFIER void* pme_gpu_get_device_f(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme))
         GPU_FUNC_TERM_WITH_RETURN(nullptr);
 
-/*! \brief Returns the pointer to the GPU stream.
- *  \param[in] pme            The PME data structure.
- *  \returns                  Pointer to GPU stream object.
- */
-GPU_FUNC_QUALIFIER void* pme_gpu_get_device_stream(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme))
-        GPU_FUNC_TERM_WITH_RETURN(nullptr);
-
-/*! \brief Returns the pointer to the GPU context.
- *  \param[in] pme            The PME data structure.
- *  \returns                  Pointer to GPU context object.
- */
-GPU_FUNC_QUALIFIER void* pme_gpu_get_device_context(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme))
-        GPU_FUNC_TERM_WITH_RETURN(nullptr);
-
 /*! \brief Get pointer to the device synchronizer object that allows syncing on PME force calculation completion
  * \param[in] pme            The PME data structure.
  * \returns                  Pointer to sychronizer
index dfe848159380f441bb2a12be37fb8963773937ef..144bd27fddcf042366ffcfd609277ceb948c859a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_PMECOORDINATERECEIVERGPU_H
 #define GMX_PMECOORDINATERECEIVERGPU_H
 
-#include "gromacs/ewald/pme.h"
-#include "gromacs/ewald/pme_force_sender_gpu.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/math/vectypes.h"
 #include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/gmxmpi.h"
 
+class DeviceStream;
+struct PpRanks;
+
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
+
 class PmeCoordinateReceiverGpu
 {
 
@@ -59,14 +65,14 @@ public:
      * \param[in] comm            Communicator used for simulation
      * \param[in] ppRanks         List of PP ranks
      */
-    PmeCoordinateReceiverGpu(void* pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks);
+    PmeCoordinateReceiverGpu(const DeviceStream& pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks);
     ~PmeCoordinateReceiverGpu();
 
     /*! \brief
      * send coordinates buffer address to PP rank
      * \param[in] d_x   coordinates buffer in GPU memory
      */
-    void sendCoordinateBufferAddressToPpRanks(rvec* d_x);
+    void sendCoordinateBufferAddressToPpRanks(DeviceBuffer<RVec> d_x);
 
 
     /*! \brief
index 35ddc077336c5b1110a43c697fae388cf91fe117..be4cf2a16acb58f9945a184020b31572e6201e92 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "config.h"
 
 #include "gromacs/ewald/pme_coordinate_receiver_gpu.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
 
-#if GMX_GPU != GMX_GPU_CUDA
+#if !GMX_GPU_CUDA
 
 namespace gmx
 {
 
-/*!\brief Impl class stub. */
+/*!\brief \internal Impl class stub. */
 class PmeCoordinateReceiverGpu::Impl
 {
 };
 
 /*!\brief Constructor stub. */
-PmeCoordinateReceiverGpu::PmeCoordinateReceiverGpu(void gmx_unused* pmeStream,
-                                                   MPI_Comm gmx_unused    comm,
-                                                   gmx::ArrayRef<PpRanks> gmx_unused ppRanks) :
+PmeCoordinateReceiverGpu::PmeCoordinateReceiverGpu(const DeviceStream& /* pmeStream */,
+                                                   MPI_Comm /* comm */,
+                                                   gmx::ArrayRef<PpRanks> /* ppRanks */) :
     impl_(nullptr)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication was called instead of the correct "
                "implementation.");
 }
@@ -73,27 +75,27 @@ PmeCoordinateReceiverGpu::PmeCoordinateReceiverGpu(void gmx_unused* pmeStream,
 PmeCoordinateReceiverGpu::~PmeCoordinateReceiverGpu() = default;
 
 /*!\brief init PME-PP GPU communication stub */
-void PmeCoordinateReceiverGpu::sendCoordinateBufferAddressToPpRanks(rvec gmx_unused* d_x)
+void PmeCoordinateReceiverGpu::sendCoordinateBufferAddressToPpRanks(DeviceBuffer<RVec> /* d_x */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication initialization was called instead of the "
                "correct implementation.");
 }
 
-void PmeCoordinateReceiverGpu::launchReceiveCoordinatesFromPpCudaDirect(int gmx_unused ppRank)
+void PmeCoordinateReceiverGpu::launchReceiveCoordinatesFromPpCudaDirect(int /* ppRank */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication was called instead of the correct "
                "implementation.");
 }
 
 void PmeCoordinateReceiverGpu::enqueueWaitReceiveCoordinatesFromPpCudaDirect()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication was called instead of the correct "
                "implementation.");
 }
 
 } // namespace gmx
 
-#endif /* GMX_GPU != GMX_GPU_CUDA */
+#endif // !GMX_GPU_CUDA
index 3ca69e27a882225bba32590da8afb32f0d2f0501..db81fb7b0aad4d80f401cf4a2a4c2ce4cf5124d7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "config.h"
 
-#include <assert.h>
-#include <stdio.h>
-
-#include "gromacs/ewald/pme.h"
+#include "gromacs/ewald/pme_force_sender_gpu.h"
 #include "gromacs/gpu_utils/cudautils.cuh"
 #include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
 #include "gromacs/utility/gmxmpi.h"
 namespace gmx
 {
 
-PmeCoordinateReceiverGpu::Impl::Impl(void* pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks) :
-    pmeStream_(*static_cast<cudaStream_t*>(pmeStream)),
+PmeCoordinateReceiverGpu::Impl::Impl(const DeviceStream&    pmeStream,
+                                     MPI_Comm               comm,
+                                     gmx::ArrayRef<PpRanks> ppRanks) :
+    pmeStream_(pmeStream),
     comm_(comm),
     ppRanks_(ppRanks)
 {
@@ -72,7 +71,7 @@ PmeCoordinateReceiverGpu::Impl::Impl(void* pmeStream, MPI_Comm comm, gmx::ArrayR
 
 PmeCoordinateReceiverGpu::Impl::~Impl() = default;
 
-void PmeCoordinateReceiverGpu::Impl::sendCoordinateBufferAddressToPpRanks(rvec* d_x)
+void PmeCoordinateReceiverGpu::Impl::sendCoordinateBufferAddressToPpRanks(DeviceBuffer<RVec> d_x)
 {
 
     int ind_start = 0;
@@ -125,7 +124,7 @@ void PmeCoordinateReceiverGpu::Impl::enqueueWaitReceiveCoordinatesFromPpCudaDire
     }
 }
 
-PmeCoordinateReceiverGpu::PmeCoordinateReceiverGpu(void*                  pmeStream,
+PmeCoordinateReceiverGpu::PmeCoordinateReceiverGpu(const DeviceStream&    pmeStream,
                                                    MPI_Comm               comm,
                                                    gmx::ArrayRef<PpRanks> ppRanks) :
     impl_(new Impl(pmeStream, comm, ppRanks))
@@ -134,7 +133,7 @@ PmeCoordinateReceiverGpu::PmeCoordinateReceiverGpu(void*                  pmeStr
 
 PmeCoordinateReceiverGpu::~PmeCoordinateReceiverGpu() = default;
 
-void PmeCoordinateReceiverGpu::sendCoordinateBufferAddressToPpRanks(rvec* d_x)
+void PmeCoordinateReceiverGpu::sendCoordinateBufferAddressToPpRanks(DeviceBuffer<RVec> d_x)
 {
     impl_->sendCoordinateBufferAddressToPpRanks(d_x);
 }
index fe689799e1cb3a4b46d9578786fc0b9cac5c1fc7..9094a7c7b3483cdc64744ce13f2e150506b7f073 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_PMECOORDINATERECEIVERGPU_IMPL_H
 #define GMX_PMECOORDINATERECEIVERGPU_IMPL_H
 
+#include <vector>
+
 #include "gromacs/ewald/pme_coordinate_receiver_gpu.h"
-#include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
+#include "gromacs/utility/arrayref.h"
+
+class GpuEventSynchronizer;
 
 namespace gmx
 {
-
 /*! \internal \brief Class with interfaces and data for CUDA version of PME coordinate receiving functionality */
 
 class PmeCoordinateReceiverGpu::Impl
@@ -60,14 +63,14 @@ public:
      * \param[in] comm            Communicator used for simulation
      * \param[in] ppRanks         List of PP ranks
      */
-    Impl(void* pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks);
+    Impl(const DeviceStream& pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks);
     ~Impl();
 
     /*! \brief
      * send coordinates buffer address to PP rank
      * \param[in] d_x   coordinates buffer in GPU memory
      */
-    void sendCoordinateBufferAddressToPpRanks(rvec* d_x);
+    void sendCoordinateBufferAddressToPpRanks(DeviceBuffer<RVec> d_x);
 
     /*! \brief
      * launch receive of coordinate data from PP rank
@@ -82,7 +85,7 @@ public:
 
 private:
     //! CUDA stream for PME operations
-    cudaStream_t pmeStream_ = nullptr;
+    const DeviceStream& pmeStream_;
     //! communicator for simulation
     MPI_Comm comm_;
     //! list of PP ranks
index 479e01ad52a59aa3aff5f4ed645bd51e930f106e..df8e1873f8ecb4e9b36df823778be1fea4c404e3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,6 +46,8 @@
 #include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/gmxmpi.h"
 
+class DeviceStream;
+
 /*! \libinternal
  * \brief Contains information about the PP ranks that partner this PME rank. */
 struct PpRanks
@@ -73,7 +75,7 @@ public:
      * \param[in] comm            Communicator used for simulation
      * \param[in] ppRanks         List of PP ranks
      */
-    PmeForceSenderGpu(void* pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks);
+    PmeForceSenderGpu(const DeviceStream& pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks);
     ~PmeForceSenderGpu();
 
     /*! \brief
index 0877beecf9b9066eb4b718a615a25c4e0941db7b..a30384a8c28135949ca4238a73ad101a5c2eb6aa 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/ewald/pme_force_sender_gpu.h"
 #include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
 
-#if GMX_GPU != GMX_GPU_CUDA
+#if !GMX_GPU_CUDA
 
 namespace gmx
 {
 
-/*!\brief Impl class stub. */
+/*!\brief \internal Impl class stub. */
 class PmeForceSenderGpu::Impl
 {
 };
 
 /*!\brief Constructor stub. */
-PmeForceSenderGpu::PmeForceSenderGpu(void gmx_unused* pmeStream,
-                                     MPI_Comm gmx_unused    comm,
-                                     gmx::ArrayRef<PpRanks> gmx_unused ppRanks) :
+PmeForceSenderGpu::PmeForceSenderGpu(const DeviceStream& /*pmeStream */,
+                                     MPI_Comm /* comm     */,
+                                     gmx::ArrayRef<PpRanks> /* ppRanks */) :
     impl_(nullptr)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication was called instead of the correct "
                "implementation.");
 }
@@ -74,20 +75,20 @@ PmeForceSenderGpu::PmeForceSenderGpu(void gmx_unused* pmeStream,
 PmeForceSenderGpu::~PmeForceSenderGpu() = default;
 
 /*!\brief init PME-PP GPU communication stub */
-void PmeForceSenderGpu::sendForceBufferAddressToPpRanks(rvec gmx_unused* d_f)
+void PmeForceSenderGpu::sendForceBufferAddressToPpRanks(rvec* /* d_f */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication initialization was called instead of the "
                "correct implementation.");
 }
 
-void PmeForceSenderGpu::sendFToPpCudaDirect(int gmx_unused ppRank)
+void PmeForceSenderGpu::sendFToPpCudaDirect(int /* ppRank */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication was called instead of the correct "
                "implementation.");
 }
 
 } // namespace gmx
 
-#endif /* GMX_GPU != GMX_GPU_CUDA */
+#endif // !GMX_GPU_CUDA
index 1268bac7921307034e4164d82c2279b538a8f043..6e6d21eaf2ee7b98f338e51b6877de2bbb4cebfc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -55,8 +55,8 @@ namespace gmx
 {
 
 /*! \brief Create PME-PP GPU communication object */
-PmeForceSenderGpu::Impl::Impl(void* pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks) :
-    pmeStream_(*static_cast<cudaStream_t*>(pmeStream)),
+PmeForceSenderGpu::Impl::Impl(const DeviceStream& pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks) :
+    pmeStream_(pmeStream),
     comm_(comm),
     ppRanks_(ppRanks)
 {
@@ -106,7 +106,9 @@ void PmeForceSenderGpu::Impl::sendFToPpCudaDirect(int ppRank)
 #endif
 }
 
-PmeForceSenderGpu::PmeForceSenderGpu(void* pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks) :
+PmeForceSenderGpu::PmeForceSenderGpu(const DeviceStream&    pmeStream,
+                                     MPI_Comm               comm,
+                                     gmx::ArrayRef<PpRanks> ppRanks) :
     impl_(new Impl(pmeStream, comm, ppRanks))
 {
 }
index 4ba9702fa12d4e9be3e916a88746a5167ba78f22..91fe1c1140d271651bd9f9bf40c248e9f7385b70 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -61,7 +61,7 @@ public:
      * \param[in] comm            Communicator used for simulation
      * \param[in] ppRanks         List of PP ranks
      */
-    Impl(void* pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks);
+    Impl(const DeviceStream& pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks);
     ~Impl();
 
     /*! \brief
@@ -78,7 +78,7 @@ public:
 
 private:
     //! CUDA stream for PME operations
-    cudaStream_t pmeStream_ = nullptr;
+    const DeviceStream& pmeStream_;
     //! Event triggered when to allow remote PP stream to syn with pme stream
     GpuEventSynchronizer pmeSync_;
     //! communicator for simulation
index 3b760e9d02bba97c7b8d47c1749e0075975790b4..8c7106acf9138fce11e7e6d51d45a2263026424e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,8 +43,6 @@
  *
  * - atomsPerBlock which expresses how many atoms are processed by a single work group
  * - order which is a PME interpolation order
- * - overwriteForces must evaluate to either true or false to specify whether the kernel
- * overwrites or reduces into the forces buffer
  * - wrapX and wrapY must evaluate to either true or false to specify whether the grid overlap
  * in dimension X/Y is to be used
  *
@@ -52,7 +50,7 @@
  */
 
 #include "pme_gpu_types.h"
-#include "pme_gpu_utils.clh"
+#include "pme_gpu_calculate_splines.clh"
 
 #ifndef COMPILE_GATHER_HELPERS_ONCE
 #    define COMPILE_GATHER_HELPERS_ONCE
@@ -67,9 +65,10 @@ inline float read_grid_size(const float* realGridSizeFP, const int dimIndex)
         case XX: return realGridSizeFP[XX];
         case YY: return realGridSizeFP[YY];
         case ZZ: return realGridSizeFP[ZZ];
+        default: assert(false); break;
     }
     assert(false);
-    return 0.0f;
+    return 0.0F;
 }
 
 /*! \brief Reduce the partial force contributions.
@@ -185,6 +184,86 @@ inline void reduce_atom_forces(__local float* __restrict__ sm_forces,
     }
 }
 
+/*! \brief Calculate the sum of the force partial components (in X, Y and Z)
+ *
+ * \param[out] fx                 The force partial component in the X dimension.
+ * \param[out] fy                 The force partial component in the Y dimension.
+ * \param[out] fz                 The force partial component in the Z dimension.
+ * \param[in] ixBase              The grid line index base value in the X dimension.
+ * \param[in] nx                  The grid real size in the X dimension.
+ * \param[in] pny                 The padded grid real size in the Y dimension.
+ * \param[in] pnz                 The padded grid real size in the Z dimension.
+ * \param[in] constOffset         The offset to calculate the global grid index.
+ * \param[in] splineIndexBase     The base value of the spline parameter index.
+ * \param[in] tdy                 The theta and dtheta in the Y dimension.
+ * \param[in] tdz                 The theta and dtheta in the Z dimension.
+ * \param[in] sm_splineParams     Shared memory array of spline parameters.
+ * \param[in] gm_grid             Global memory array of the grid to use.
+ */
+inline void sumForceComponents(float*        fx,
+                               float*        fy,
+                               float*        fz,
+                               const int     ixBase,
+                               const int     nx,
+                               const int     pny,
+                               const int     pnz,
+                               const int     constOffset,
+                               const int     splineIndexBase,
+                               const float2  tdy,
+                               const float2  tdz,
+                               __local const float2* __restrict__ sm_splineParams,
+                               __global const float* __restrict__ gm_grid)
+{
+#    pragma unroll order
+    for (int ithx = 0; (ithx < order); ithx++)
+    {
+        int ix = ixBase + ithx;
+        if (wrapX & (ix >= nx))
+        {
+            ix -= nx;
+        }
+        const int gridIndexGlobal = ix * pny * pnz + constOffset;
+        assert(gridIndexGlobal >= 0);
+        const float gridValue = gm_grid[gridIndexGlobal];
+        assert(isfinite(gridValue));
+        const int    splineIndexX = getSplineParamIndex(splineIndexBase, XX, ithx);
+        const float2 tdx          = sm_splineParams[splineIndexX];
+        const float  fxy1         = tdz.x * gridValue;
+        const float  fz1          = tdz.y * gridValue;
+        *fx += tdx.y * tdy.x * fxy1;
+        *fy += tdx.x * tdy.y * fxy1;
+        *fz += tdx.x * tdy.x * fz1;
+    }
+}
+
+/*! \brief Calculate the grid forces and store them in shared memory.
+ *
+ * \param[in,out] sm_forces       Shared memory array with the output forces.
+ * \param[in] forceIndexLocal     The local (per thread) index in the sm_forces array.
+ * \param[in] forceIndexGlobal    The index of the thread in the gm_coefficients array.
+ * \param[in] recipBox            The reciprocal box.
+ * \param[in] scale               The scale to use when calculating the forces (only relevant when
+ * using two grids).
+ * \param[in] gm_coefficients     Global memory array of the coefficients to use.
+ */
+inline void calculateAndStoreGridForces(__local float* __restrict__ sm_forces,
+                                        const int   forceIndexLocal,
+                                        const int   forceIndexGlobal,
+                                        const float recipBox[DIM][DIM],
+                                        const float scale,
+                                        __global const float* __restrict__ gm_coefficients)
+{
+    const float3 atomForces     = vload3(forceIndexLocal, sm_forces);
+    float        negCoefficient = -scale * gm_coefficients[forceIndexGlobal];
+    float3       result;
+    result.x = negCoefficient * recipBox[XX][XX] * atomForces.x;
+    result.y = negCoefficient * (recipBox[XX][YY] * atomForces.x + recipBox[YY][YY] * atomForces.y);
+    result.z = negCoefficient
+               * (recipBox[XX][ZZ] * atomForces.x + recipBox[YY][ZZ] * atomForces.y
+                  + recipBox[ZZ][ZZ] * atomForces.z);
+    vstore3(result, forceIndexLocal, sm_forces);
+}
+
 #endif // COMPILE_GATHER_HELPERS_ONCE
 
 /*! \brief
@@ -193,8 +272,15 @@ inline void reduce_atom_forces(__local float* __restrict__ sm_forces,
  * Please see the file description for additional defines which this kernel expects.
  *
  * \param[in]     kernelParams         All the PME GPU data.
- * \param[in]     gm_coefficients      Atom charges/coefficients.
- * \param[in]     gm_grid              Global 3D grid.
+ * \param[in]     gm_coefficientsA     Atom charges/coefficients in the unperturbed state, or FEP
+ * state A.
+ * \param[in]     gm_coefficientsB     Atom charges/coefficients in FEP state B. Only used
+ * when spreading interpolated coefficients on one grid or spreading two sets of coefficients on two
+ * separate grids.
+ * \param[in]     gm_gridA             Global 3D grid for the unperturbed state, FEP
+ * state A or the single grid used for interpolated coefficients on one grid in FEP A/B.
+ * \param[in]     gm_gridB             Global 3D grid for FEP state B when using dual
+ * grids (when calculating energy and virials).
  * \param[in]     gm_theta             Atom spline parameter values
  * \param[in]     gm_dtheta            Atom spline parameter derivatives
  * \param[in]     gm_gridlineIndices   Atom gridline indices (ivec)
@@ -202,16 +288,20 @@ inline void reduce_atom_forces(__local float* __restrict__ sm_forces,
  */
 __attribute__((reqd_work_group_size(order, order, atomsPerBlock)))
 __kernel void CUSTOMIZED_KERNEL_NAME(pme_gather_kernel)(const struct PmeOpenCLKernelParams kernelParams,
-                                                        __global const float* __restrict__ gm_coefficients,
-                                                        __global const float* __restrict__ gm_grid,
+                                                        __global const float* __restrict__ gm_coefficientsA,
+                                                        __global const float* __restrict__ gm_coefficientsB,
+                                                        __global const float* __restrict__ gm_gridA,
+                                                        __global const float* __restrict__ gm_gridB,
                                                         __global const float* __restrict__ gm_theta,
                                                         __global const float* __restrict__ gm_dtheta,
                                                         __global const int* __restrict__ gm_gridlineIndices,
                                                         __global float* __restrict__ gm_forces)
 {
+    assert(numGrids == 1 || numGrids == 2);
+
     /* These are the atom indices - for the shared and global memory */
     const int atomIndexLocal  = get_local_id(ZZ);
-    const int atomIndexOffset = get_group_id(XX) * atomsPerBlock;
+    const int atomIndexOffset = (int)get_group_id(XX) * atomsPerBlock;
     const int atomIndexGlobal = atomIndexOffset + atomIndexLocal;
 
 /* Some sizes which are defines and not consts because they go into the array size */
@@ -230,31 +320,32 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_gather_kernel)(const struct PmeOpenCLKe
     const int ithy = get_local_id(YY);
     const int ithz = get_local_id(XX);
 
-    const int threadLocalId = (get_local_id(2) * get_local_size(1) + get_local_id(1)) * get_local_size(0)
-                              + get_local_id(0);
+    assert((get_local_id(2) * get_local_size(1) + get_local_id(1)) * get_local_size(0) + get_local_id(0)
+           <= MAX_INT);
+    const int threadLocalId =
+            (int)((get_local_id(2) * get_local_size(1) + get_local_id(1)) * get_local_size(0)
+                  + get_local_id(0));
 
     /* These are the spline contribution indices in shared memory */
-    const int splineIndex = (get_local_id(1) * get_local_size(0)
-                             + get_local_id(0)); /* Relative to the current particle , 0..15 for order 4 */
-    const int lineIndex   = threadLocalId; /* And to all the block's particles */
+    assert((get_local_id(1) * get_local_size(0) + get_local_id(0)) <= MAX_INT);
+    const int splineIndex =
+            (int)(get_local_id(1) * get_local_size(0)
+                  + get_local_id(0));    /* Relative to the current particle , 0..15 for order 4 */
+    const int lineIndex = threadLocalId; /* And to all the block's particles */
 
     /* Staging the atom gridline indices, DIM * atomsPerBlock threads */
     const int localGridlineIndicesIndex = threadLocalId;
     const int globalGridlineIndicesIndex =
-            get_group_id(XX) * gridlineIndicesSize + localGridlineIndicesIndex;
-    const int globalCheckIndices =
-            pme_gpu_check_atom_data_index(globalGridlineIndicesIndex, kernelParams.atoms.nAtoms * DIM);
-    if ((localGridlineIndicesIndex < gridlineIndicesSize) & globalCheckIndices)
+            (int)get_group_id(XX) * gridlineIndicesSize + localGridlineIndicesIndex;
+    if (localGridlineIndicesIndex < gridlineIndicesSize)
     {
         sm_gridlineIndices[localGridlineIndicesIndex] = gm_gridlineIndices[globalGridlineIndicesIndex];
         assert(sm_gridlineIndices[localGridlineIndicesIndex] >= 0);
     }
     /* Staging the spline parameters, DIM * order * atomsPerBlock threads */
-    const int localSplineParamsIndex  = threadLocalId;
-    const int globalSplineParamsIndex = get_group_id(XX) * splineParamsSize + localSplineParamsIndex;
-    const int globalCheckSplineParams = pme_gpu_check_atom_data_index(
-            globalSplineParamsIndex, kernelParams.atoms.nAtoms * DIM * order);
-    if ((localSplineParamsIndex < splineParamsSize) && globalCheckSplineParams)
+    const int localSplineParamsIndex = threadLocalId;
+    const int globalSplineParamsIndex = (int)get_group_id(XX) * splineParamsSize + localSplineParamsIndex;
+    if (localSplineParamsIndex < splineParamsSize)
     {
         sm_splineParams[localSplineParamsIndex].x = gm_theta[globalSplineParamsIndex];
         sm_splineParams[localSplineParamsIndex].y = gm_dtheta[globalSplineParamsIndex];
@@ -263,63 +354,44 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_gather_kernel)(const struct PmeOpenCLKe
     }
     barrier(CLK_LOCAL_MEM_FENCE);
 
-    float fx = 0.0f;
-    float fy = 0.0f;
-    float fz = 0.0f;
+    float fx = 0.0F;
+    float fy = 0.0F;
+    float fz = 0.0F;
+
+    int chargeCheck = pme_gpu_check_atom_charge(gm_coefficientsA[atomIndexGlobal]);
+
+    const int nx  = kernelParams.grid.realGridSize[XX];
+    const int ny  = kernelParams.grid.realGridSize[YY];
+    const int nz  = kernelParams.grid.realGridSize[ZZ];
+    const int pny = kernelParams.grid.realGridSizePadded[YY];
+    const int pnz = kernelParams.grid.realGridSizePadded[ZZ];
+
+    const int atomWarpIndex = atomIndexLocal % atomsPerWarp;
+    const int warpIndex     = atomIndexLocal / atomsPerWarp;
 
-    const int globalCheck = pme_gpu_check_atom_data_index(atomIndexGlobal, kernelParams.atoms.nAtoms);
-    const int chargeCheck = pme_gpu_check_atom_charge(gm_coefficients[atomIndexGlobal]);
+    const int    splineIndexBase = getSplineParamIndexBase(warpIndex, atomWarpIndex);
+    const int    splineIndexY    = getSplineParamIndex(splineIndexBase, YY, ithy);
+    const float2 tdy             = sm_splineParams[splineIndexY];
+    const int    splineIndexZ    = getSplineParamIndex(splineIndexBase, ZZ, ithz);
+    const float2 tdz             = sm_splineParams[splineIndexZ];
 
-    if (chargeCheck & globalCheck)
+    const int ixBase = sm_gridlineIndices[atomIndexLocal * DIM + XX];
+    int       iy     = sm_gridlineIndices[atomIndexLocal * DIM + YY] + ithy;
+    if (wrapY & (iy >= ny))
     {
-        const int nx  = kernelParams.grid.realGridSize[XX];
-        const int ny  = kernelParams.grid.realGridSize[YY];
-        const int nz  = kernelParams.grid.realGridSize[ZZ];
-        const int pny = kernelParams.grid.realGridSizePadded[YY];
-        const int pnz = kernelParams.grid.realGridSizePadded[ZZ];
-
-        const int atomWarpIndex = atomIndexLocal % atomsPerWarp;
-        const int warpIndex     = atomIndexLocal / atomsPerWarp;
-
-        const int    splineIndexBase = getSplineParamIndexBase(warpIndex, atomWarpIndex);
-        const int    splineIndexY    = getSplineParamIndex(splineIndexBase, YY, ithy);
-        const float2 tdy             = sm_splineParams[splineIndexY];
-        const int    splineIndexZ    = getSplineParamIndex(splineIndexBase, ZZ, ithz);
-        const float2 tdz             = sm_splineParams[splineIndexZ];
-
-        const int ixBase = sm_gridlineIndices[atomIndexLocal * DIM + XX];
-        int       iy     = sm_gridlineIndices[atomIndexLocal * DIM + YY] + ithy;
-        if (wrapY & (iy >= ny))
-        {
-            iy -= ny;
-        }
-        int iz = sm_gridlineIndices[atomIndexLocal * DIM + ZZ] + ithz;
-        if (iz >= nz)
-        {
-            iz -= nz;
-        }
-        const int constOffset = iy * pnz + iz;
+        iy -= ny;
+    }
+    int iz = sm_gridlineIndices[atomIndexLocal * DIM + ZZ] + ithz;
+    if (iz >= nz)
+    {
+        iz -= nz;
+    }
+    const int constOffset = iy * pnz + iz;
 
-#pragma unroll order
-        for (int ithx = 0; (ithx < order); ithx++)
-        {
-            int ix = ixBase + ithx;
-            if (wrapX & (ix >= nx))
-            {
-                ix -= nx;
-            }
-            const int gridIndexGlobal = ix * pny * pnz + constOffset;
-            assert(gridIndexGlobal >= 0);
-            const float gridValue = gm_grid[gridIndexGlobal];
-            assert(isfinite(gridValue));
-            const int    splineIndexX = getSplineParamIndex(splineIndexBase, XX, ithx);
-            const float2 tdx          = sm_splineParams[splineIndexX];
-            const float  fxy1         = tdz.x * gridValue;
-            const float  fz1          = tdz.y * gridValue;
-            fx += tdx.y * tdy.x * fxy1;
-            fy += tdx.x * tdy.y * fxy1;
-            fz += tdx.x * tdy.x * fz1;
-        }
+    if (chargeCheck)
+    {
+        sumForceComponents(&fx, &fy, &fz, ixBase, nx, pny, pnz, constOffset, splineIndexBase, tdy,
+                           tdz, sm_splineParams, gm_gridA);
     }
 
     // Reduction of partial force contributions
@@ -333,23 +405,13 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_gather_kernel)(const struct PmeOpenCLKe
     barrier(CLK_LOCAL_MEM_FENCE);
 
     /* Calculating the final forces with no component branching, atomsPerBlock threads */
-    const int forceIndexLocal  = threadLocalId;
-    const int forceIndexGlobal = atomIndexOffset + forceIndexLocal;
-    const int calcIndexCheck = pme_gpu_check_atom_data_index(forceIndexGlobal, kernelParams.atoms.nAtoms);
-    if ((forceIndexLocal < atomsPerBlock) & calcIndexCheck)
+    const int   forceIndexLocal  = threadLocalId;
+    const int   forceIndexGlobal = atomIndexOffset + forceIndexLocal;
+    const float scale            = kernelParams.current.scale;
+    if (forceIndexLocal < atomsPerBlock)
     {
-        const float3 atomForces     = vload3(forceIndexLocal, sm_forces);
-        const float  negCoefficient = -gm_coefficients[forceIndexGlobal];
-        float3       result;
-        result.x = negCoefficient * kernelParams.current.recipBox[XX][XX] * atomForces.x;
-        result.y = negCoefficient
-                   * (kernelParams.current.recipBox[XX][YY] * atomForces.x
-                      + kernelParams.current.recipBox[YY][YY] * atomForces.y);
-        result.z = negCoefficient
-                   * (kernelParams.current.recipBox[XX][ZZ] * atomForces.x
-                      + kernelParams.current.recipBox[YY][ZZ] * atomForces.y
-                      + kernelParams.current.recipBox[ZZ][ZZ] * atomForces.z);
-        vstore3(result, forceIndexLocal, sm_forces);
+        calculateAndStoreGridForces(sm_forces, forceIndexLocal, forceIndexGlobal,
+                                    kernelParams.current.recipBox, scale, gm_coefficientsA);
     }
 
 #if !defined(_AMD_SOURCE_) && !defined(_NVIDIA_SOURCE_)
@@ -371,20 +433,50 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_gather_kernel)(const struct PmeOpenCLKe
         for (int i = 0; i < numIter; i++)
         {
             const int outputIndexLocal  = i * iterThreads + threadLocalId;
-            const int outputIndexGlobal = get_group_id(XX) * blockForcesSize + outputIndexLocal;
-            const int globalOutputCheck =
-                    pme_gpu_check_atom_data_index(outputIndexGlobal, kernelParams.atoms.nAtoms * DIM);
-            if (globalOutputCheck)
+            const int outputIndexGlobal = (int)get_group_id(XX) * blockForcesSize + outputIndexLocal;
+            const float outputForceComponent = sm_forces[outputIndexLocal];
+            gm_forces[outputIndexGlobal]     = outputForceComponent;
+        }
+    }
+
+    if (numGrids == 2)
+    {
+        barrier(CLK_LOCAL_MEM_FENCE);
+        fx          = 0.0f;
+        fy          = 0.0f;
+        fz          = 0.0f;
+        chargeCheck = pme_gpu_check_atom_charge(gm_coefficientsB[atomIndexGlobal]);
+        if (chargeCheck)
+        {
+            sumForceComponents(&fx, &fy, &fz, ixBase, nx, pny, pnz, constOffset, splineIndexBase,
+                               tdy, tdz, sm_splineParams, gm_gridB);
+        }
+        reduce_atom_forces(sm_forces, atomIndexLocal, splineIndex, lineIndex,
+                           kernelParams.grid.realGridSizeFP, fx, fy, fz, sm_forceReduction, sm_forceTemp);
+        barrier(CLK_LOCAL_MEM_FENCE);
+        if (forceIndexLocal < atomsPerBlock)
+        {
+            calculateAndStoreGridForces(sm_forces, forceIndexLocal, forceIndexGlobal,
+                                        kernelParams.current.recipBox, 1.0F - scale, gm_coefficientsB);
+        }
+
+#if !defined(_AMD_SOURCE_) && !defined(_NVIDIA_SOURCE_)
+        /* This is only here for execution of e.g. 32-sized warps on 16-wide hardware; this was
+         * __syncwarp() in CUDA. #2519
+         */
+        barrier(CLK_LOCAL_MEM_FENCE);
+#endif
+
+        /* Writing or adding the final forces component-wise, single warp */
+        if (threadLocalId < iterThreads)
+        {
+#pragma unroll
+            for (int i = 0; i < numIter; i++)
             {
+                const int outputIndexLocal  = i * iterThreads + threadLocalId;
+                const int outputIndexGlobal = get_group_id(XX) * blockForcesSize + outputIndexLocal;
                 const float outputForceComponent = sm_forces[outputIndexLocal];
-                if (overwriteForces)
-                {
-                    gm_forces[outputIndexGlobal] = outputForceComponent;
-                }
-                else
-                {
-                    gm_forces[outputIndexGlobal] += outputForceComponent;
-                }
+                gm_forces[outputIndexGlobal] += outputForceComponent;
             }
         }
     }
index 315cb3aefb12f3ac292dcc2535980df67c38d532..2a7925cfe281335cd195176f2497f87aab10265c 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -92,7 +93,7 @@ struct do_fspline
     template<typename Int>
     RVec operator()(Int order) const
     {
-        static_assert(isIntegralConstant<Int, int>::value || std::is_same<Int, int>::value,
+        static_assert(isIntegralConstant<Int, int>::value || std::is_same_v<Int, int>,
                       "'order' needs to be either of type integral_constant<int,N> or int.");
 
         const int norder = nn * order;
index 8b2ff5f80e7e0a3e1623bc6c10501eb50cfe48a5..52814c4ce44f372c70c1be0366c88e609779966f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <cassert>
 
 #include "gromacs/gpu_utils/cuda_kernel_utils.cuh"
+#include "gromacs/gpu_utils/typecasts.cuh"
 
 #include "pme.cuh"
-#include "pme_calculate_splines.cuh"
-#include "pme_gpu_utils.h"
+#include "pme_gpu_calculate_splines.cuh"
 #include "pme_grid.h"
 
 /*! \brief
@@ -69,14 +69,20 @@ __device__ __forceinline__ float read_grid_size(const float* realGridSizeFP, con
  *
  * \tparam[in] order              The PME order (must be 4).
  * \tparam[in] atomDataSize       The number of partial force contributions for each atom (currently
- * order^2 == 16) \tparam[in] blockSize          The CUDA block size \param[out] sm_forces Shared
- * memory array with the output forces (number of elements is number of atoms per block) \param[in]
- * atomIndexLocal     Local atom index \param[in]  splineIndex        Spline index \param[in]
- * lineIndex          Line index (same as threadLocalId) \param[in]  realGridSizeFP     Local grid
- * size constant \param[in]  fx                 Input force partial component X \param[in]  fy Input
- * force partial component Y \param[in]  fz                 Input force partial component Z
+ *                                order^2 == 16)
+ * \tparam[in] blockSize          The CUDA block size
+ *
+ * \param[out] sm_forces          Shared memory array with the output forces (number of elements
+ *                                is number of atoms per block)
+ * \param[in]  atomIndexLocal     Local atom index
+ * \param[in]  splineIndex        Spline index
+ * \param[in]  lineIndex          Line index (same as threadLocalId)
+ * \param[in]  realGridSizeFP     Local grid size constant
+ * \param[in]  fx                 Input force partial component X
+ * \param[in]  fy                 Input force partial component Y
+ * \param[in]  fz                 Input force partial component Z
  */
-template<const int order, const int atomDataSize, const int blockSize>
+template<int order, int atomDataSize, int blockSize>
 __device__ __forceinline__ void reduce_atom_forces(float3* __restrict__ sm_forces,
                                                    const int    atomIndexLocal,
                                                    const int    splineIndex,
@@ -207,27 +213,144 @@ __device__ __forceinline__ void reduce_atom_forces(float3* __restrict__ sm_force
     }
 }
 
+/*! \brief Calculate the sum of the force partial components (in X, Y and Z)
+ *
+ * \tparam[in] order              The PME order (must be 4).
+ * \tparam[in] atomsPerWarp       The number of atoms per GPU warp.
+ * \tparam[in] wrapX              Tells if the grid is wrapped in the X dimension.
+ * \tparam[in] wrapY              Tells if the grid is wrapped in the Y dimension.
+ * \param[out] fx                 The force partial component in the X dimension.
+ * \param[out] fy                 The force partial component in the Y dimension.
+ * \param[out] fz                 The force partial component in the Z dimension.
+ * \param[in] ithyMin             The thread minimum index in the Y dimension.
+ * \param[in] ithyMax             The thread maximum index in the Y dimension.
+ * \param[in] ixBase              The grid line index base value in the X dimension.
+ * \param[in] iz                  The grid line index in the Z dimension.
+ * \param[in] nx                  The grid real size in the X dimension.
+ * \param[in] ny                  The grid real size in the Y dimension.
+ * \param[in] pny                 The padded grid real size in the Y dimension.
+ * \param[in] pnz                 The padded grid real size in the Z dimension.
+ * \param[in] atomIndexLocal      The atom index for this thread.
+ * \param[in] splineIndexBase     The base value of the spline parameter index.
+ * \param[in] tdz                 The theta and dtheta in the Z dimension.
+ * \param[in] sm_gridlineIndices  Shared memory array of grid line indices.
+ * \param[in] sm_theta            Shared memory array of atom theta values.
+ * \param[in] sm_dtheta           Shared memory array of atom dtheta values.
+ * \param[in] gm_grid             Global memory array of the grid to use.
+ */
+template<int order, int atomsPerWarp, bool wrapX, bool wrapY>
+__device__ __forceinline__ void sumForceComponents(float* __restrict__ fx,
+                                                   float* __restrict__ fy,
+                                                   float* __restrict__ fz,
+                                                   const int    ithyMin,
+                                                   const int    ithyMax,
+                                                   const int    ixBase,
+                                                   const int    iz,
+                                                   const int    nx,
+                                                   const int    ny,
+                                                   const int    pny,
+                                                   const int    pnz,
+                                                   const int    atomIndexLocal,
+                                                   const int    splineIndexBase,
+                                                   const float2 tdz,
+                                                   const int* __restrict__ sm_gridlineIndices,
+                                                   const float* __restrict__ sm_theta,
+                                                   const float* __restrict__ sm_dtheta,
+                                                   const float* __restrict__ gm_grid)
+{
+#pragma unroll
+    for (int ithy = ithyMin; ithy < ithyMax; ithy++)
+    {
+        const int splineIndexY = getSplineParamIndex<order, atomsPerWarp>(splineIndexBase, YY, ithy);
+        const float2 tdy       = make_float2(sm_theta[splineIndexY], sm_dtheta[splineIndexY]);
+
+        int iy = sm_gridlineIndices[atomIndexLocal * DIM + YY] + ithy;
+        if (wrapY & (iy >= ny))
+        {
+            iy -= ny;
+        }
+        const int constOffset = iy * pnz + iz;
+
+#pragma unroll
+        for (int ithx = 0; (ithx < order); ithx++)
+        {
+            int ix = ixBase + ithx;
+            if (wrapX & (ix >= nx))
+            {
+                ix -= nx;
+            }
+            const int gridIndexGlobal = ix * pny * pnz + constOffset;
+            assert(gridIndexGlobal >= 0);
+            const float gridValue = gm_grid[gridIndexGlobal];
+            assert(isfinite(gridValue));
+            const int splineIndexX = getSplineParamIndex<order, atomsPerWarp>(splineIndexBase, XX, ithx);
+            const float2 tdx       = make_float2(sm_theta[splineIndexX], sm_dtheta[splineIndexX]);
+            const float  fxy1      = tdz.x * gridValue;
+            const float  fz1       = tdz.y * gridValue;
+            *fx += tdx.y * tdy.x * fxy1;
+            *fy += tdx.x * tdy.y * fxy1;
+            *fz += tdx.x * tdy.x * fz1;
+        }
+    }
+}
+
+/*! \brief Calculate the grid forces and store them in shared memory.
+ *
+ * \param[in,out] sm_forces       Shared memory array with the output forces.
+ * \param[in] forceIndexLocal     The local (per thread) index in the sm_forces array.
+ * \param[in] forceIndexGlobal    The index of the thread in the gm_coefficients array.
+ * \param[in] recipBox            The reciprocal box.
+ * \param[in] scale               The scale to use when calculating the forces. For gm_coefficientsB
+ * (when using multiple coefficients on a single grid) the scale will be (1.0 - scale).
+ * \param[in] gm_coefficients     Global memory array of the coefficients to use for an unperturbed
+ * or FEP in state A if a single grid is used (\p multiCoefficientsSingleGrid == true).If two
+ * separate grids are used this should be the coefficients of the grid in question.
+ * \param[in] gm_coefficientsB    Global memory array of the coefficients to use for FEP in state B.
+ * Should be nullptr if two separate grids are used.
+ */
+__device__ __forceinline__ void calculateAndStoreGridForces(float3* __restrict__ sm_forces,
+                                                            const int   forceIndexLocal,
+                                                            const int   forceIndexGlobal,
+                                                            const float recipBox[DIM][DIM],
+                                                            const float scale,
+                                                            const float* __restrict__ gm_coefficients)
+{
+    const float3 atomForces     = sm_forces[forceIndexLocal];
+    float        negCoefficient = -scale * gm_coefficients[forceIndexGlobal];
+    float3       result;
+    result.x = negCoefficient * recipBox[XX][XX] * atomForces.x;
+    result.y = negCoefficient * (recipBox[XX][YY] * atomForces.x + recipBox[YY][YY] * atomForces.y);
+    result.z = negCoefficient
+               * (recipBox[XX][ZZ] * atomForces.x + recipBox[YY][ZZ] * atomForces.y
+                  + recipBox[ZZ][ZZ] * atomForces.z);
+    sm_forces[forceIndexLocal] = result;
+}
+
 /*! \brief
  * A CUDA kernel which gathers the atom forces from the grid.
  * The grid is assumed to be wrapped in dimension Z.
  *
  * \tparam[in] order                The PME order (must be 4 currently).
- * \tparam[in] overwriteForces      True: the forces are written to the output buffer;
- *                                  False: the forces are added non-atomically to the output buffer (e.g. to the bonded forces).
  * \tparam[in] wrapX                Tells if the grid is wrapped in the X dimension.
  * \tparam[in] wrapY                Tells if the grid is wrapped in the Y dimension.
+ * \tparam[in] numGrids             The number of grids to use in the kernel. Can be 1 or 2.
  * \tparam[in] readGlobal           Tells if we should read spline values from global memory
- * \tparam[in] useOrderThreads      Tells if we should use order threads per atom (order*order used if false)
+ * \tparam[in] threadsPerAtom       How many threads work on each atom
+ *
  * \param[in]  kernelParams         All the PME GPU data.
  */
-template<const int order, const bool overwriteForces, const bool wrapX, const bool wrapY, const bool readGlobal, const bool useOrderThreads>
+template<int order, bool wrapX, bool wrapY, int numGrids, bool readGlobal, ThreadsPerAtom threadsPerAtom>
 __launch_bounds__(c_gatherMaxThreadsPerBlock, c_gatherMinBlocksPerMP) __global__
         void pme_gather_kernel(const PmeGpuCudaKernelParams kernelParams)
 {
+    assert(numGrids == 1 || numGrids == 2);
+
     /* Global memory pointers */
-    const float* __restrict__ gm_coefficients = kernelParams.atoms.d_coefficients;
-    const float* __restrict__ gm_grid         = kernelParams.grid.d_realGrid;
-    float* __restrict__ gm_forces             = kernelParams.atoms.d_forces;
+    const float* __restrict__ gm_coefficientsA = kernelParams.atoms.d_coefficients[0];
+    const float* __restrict__ gm_coefficientsB = kernelParams.atoms.d_coefficients[1];
+    const float* __restrict__ gm_gridA         = kernelParams.grid.d_realGrid[0];
+    const float* __restrict__ gm_gridB         = kernelParams.grid.d_realGrid[1];
+    float* __restrict__ gm_forces              = kernelParams.atoms.d_forces;
 
     /* Global memory pointers for readGlobal */
     const float* __restrict__ gm_theta         = kernelParams.atoms.d_theta;
@@ -237,17 +360,14 @@ __launch_bounds__(c_gatherMaxThreadsPerBlock, c_gatherMinBlocksPerMP) __global__
     float3 atomX;
     float  atomCharge;
 
-    /* Some sizes */
-    const int atomsPerBlock =
-            useOrderThreads ? (c_gatherMaxThreadsPerBlock / c_pmeSpreadGatherThreadsPerAtom4ThPerAtom)
-                            : (c_gatherMaxThreadsPerBlock / c_pmeSpreadGatherThreadsPerAtom);
     const int blockIndex = blockIdx.y * gridDim.x + blockIdx.x;
 
     /* Number of data components and threads for a single atom */
-    const int atomDataSize = useOrderThreads ? c_pmeSpreadGatherThreadsPerAtom4ThPerAtom
-                                             : c_pmeSpreadGatherThreadsPerAtom;
-    const int atomsPerWarp = useOrderThreads ? c_pmeSpreadGatherAtomsPerWarp4ThPerAtom
-                                             : c_pmeSpreadGatherAtomsPerWarp;
+    const int threadsPerAtomValue = (threadsPerAtom == ThreadsPerAtom::Order) ? order : order * order;
+    const int atomDataSize        = threadsPerAtomValue;
+    const int atomsPerBlock       = c_gatherMaxThreadsPerBlock / atomDataSize;
+    // Number of atoms processed by a single warp in spread and gather
+    const int atomsPerWarp = warp_size / atomDataSize;
 
     const int blockSize = atomsPerBlock * atomDataSize;
     assert(blockSize == blockDim.x * blockDim.y * blockDim.z);
@@ -288,9 +408,7 @@ __launch_bounds__(c_gatherMaxThreadsPerBlock, c_gatherMinBlocksPerMP) __global__
         /* Read splines */
         const int localGridlineIndicesIndex = threadLocalId;
         const int globalGridlineIndicesIndex = blockIndex * gridlineIndicesSize + localGridlineIndicesIndex;
-        const int globalCheckIndices         = pme_gpu_check_atom_data_index(
-                globalGridlineIndicesIndex, kernelParams.atoms.nAtoms * DIM);
-        if ((localGridlineIndicesIndex < gridlineIndicesSize) & globalCheckIndices)
+        if (localGridlineIndicesIndex < gridlineIndicesSize)
         {
             sm_gridlineIndices[localGridlineIndicesIndex] = gm_gridlineIndices[globalGridlineIndicesIndex];
             assert(sm_gridlineIndices[localGridlineIndicesIndex] >= 0);
@@ -299,7 +417,7 @@ __launch_bounds__(c_gatherMaxThreadsPerBlock, c_gatherMinBlocksPerMP) __global__
            with order*order threads per atom, it is only required for each thread to load one data value */
 
         const int iMin = 0;
-        const int iMax = useOrderThreads ? 3 : 1;
+        const int iMax = (threadsPerAtom == ThreadsPerAtom::Order) ? 3 : 1;
 
         for (int i = iMin; i < iMax; i++)
         {
@@ -307,9 +425,7 @@ __launch_bounds__(c_gatherMaxThreadsPerBlock, c_gatherMinBlocksPerMP) __global__
                     threadLocalId
                     + i * threadLocalIdMax; /* i will always be zero for order*order threads per atom */
             int globalSplineParamsIndex = blockIndex * splineParamsSize + localSplineParamsIndex;
-            int globalCheckSplineParams = pme_gpu_check_atom_data_index(
-                    globalSplineParamsIndex, kernelParams.atoms.nAtoms * DIM * order);
-            if ((localSplineParamsIndex < splineParamsSize) && globalCheckSplineParams)
+            if (localSplineParamsIndex < splineParamsSize)
             {
                 sm_theta[localSplineParamsIndex]  = gm_theta[globalSplineParamsIndex];
                 sm_dtheta[localSplineParamsIndex] = gm_dtheta[globalSplineParamsIndex];
@@ -321,32 +437,27 @@ __launch_bounds__(c_gatherMaxThreadsPerBlock, c_gatherMinBlocksPerMP) __global__
     }
     else
     {
+        const float3* __restrict__ gm_coordinates = asFloat3(kernelParams.atoms.d_coordinates);
         /* Recaclulate  Splines  */
         if (c_useAtomDataPrefetch)
         {
             // charges
             __shared__ float sm_coefficients[atomsPerBlock];
             // Coordinates
-            __shared__ float sm_coordinates[DIM * atomsPerBlock];
+            __shared__ float3 sm_coordinates[atomsPerBlock];
             /* Staging coefficients/charges */
-            pme_gpu_stage_atom_data<float, atomsPerBlock, 1>(kernelParams, sm_coefficients,
-                                                             kernelParams.atoms.d_coefficients);
+            pme_gpu_stage_atom_data<float, atomsPerBlock, 1>(sm_coefficients, gm_coefficientsA);
 
             /* Staging coordinates */
-            pme_gpu_stage_atom_data<float, atomsPerBlock, DIM>(kernelParams, sm_coordinates,
-                                                               kernelParams.atoms.d_coordinates);
+            pme_gpu_stage_atom_data<float3, atomsPerBlock, 1>(sm_coordinates, gm_coordinates);
             __syncthreads();
-            atomX.x    = sm_coordinates[atomIndexLocal * DIM + XX];
-            atomX.y    = sm_coordinates[atomIndexLocal * DIM + YY];
-            atomX.z    = sm_coordinates[atomIndexLocal * DIM + ZZ];
+            atomX      = sm_coordinates[atomIndexLocal];
             atomCharge = sm_coefficients[atomIndexLocal];
         }
         else
         {
-            atomCharge = gm_coefficients[atomIndexGlobal];
-            atomX.x    = kernelParams.atoms.d_coordinates[atomIndexGlobal * DIM + XX];
-            atomX.y    = kernelParams.atoms.d_coordinates[atomIndexGlobal * DIM + YY];
-            atomX.z    = kernelParams.atoms.d_coordinates[atomIndexGlobal * DIM + ZZ];
+            atomX      = gm_coordinates[atomIndexGlobal];
+            atomCharge = gm_coefficientsA[atomIndexGlobal];
         }
         calculate_splines<order, atomsPerBlock, atomsPerWarp, true, false>(
                 kernelParams, atomIndexOffset, atomX, atomCharge, sm_theta, sm_dtheta, sm_gridlineIndices);
@@ -356,71 +467,37 @@ __launch_bounds__(c_gatherMaxThreadsPerBlock, c_gatherMinBlocksPerMP) __global__
     float fy = 0.0f;
     float fz = 0.0f;
 
-    const int globalCheck = pme_gpu_check_atom_data_index(atomIndexGlobal, kernelParams.atoms.nAtoms);
-    const int chargeCheck = pme_gpu_check_atom_charge(gm_coefficients[atomIndexGlobal]);
-
-    if (chargeCheck & globalCheck)
-    {
-        const int nx  = kernelParams.grid.realGridSize[XX];
-        const int ny  = kernelParams.grid.realGridSize[YY];
-        const int nz  = kernelParams.grid.realGridSize[ZZ];
-        const int pny = kernelParams.grid.realGridSizePadded[YY];
-        const int pnz = kernelParams.grid.realGridSizePadded[ZZ];
-
-        const int atomWarpIndex = atomIndexLocal % atomsPerWarp;
-        const int warpIndex     = atomIndexLocal / atomsPerWarp;
+    const int chargeCheck = pme_gpu_check_atom_charge(gm_coefficientsA[atomIndexGlobal]);
 
-        const int splineIndexBase = getSplineParamIndexBase<order, atomsPerWarp>(warpIndex, atomWarpIndex);
-        const int splineIndexZ = getSplineParamIndex<order, atomsPerWarp>(splineIndexBase, ZZ, ithz);
-        const float2 tdz       = make_float2(sm_theta[splineIndexZ], sm_dtheta[splineIndexZ]);
+    const int nx  = kernelParams.grid.realGridSize[XX];
+    const int ny  = kernelParams.grid.realGridSize[YY];
+    const int nz  = kernelParams.grid.realGridSize[ZZ];
+    const int pny = kernelParams.grid.realGridSizePadded[YY];
+    const int pnz = kernelParams.grid.realGridSizePadded[ZZ];
 
-        int       iz     = sm_gridlineIndices[atomIndexLocal * DIM + ZZ] + ithz;
-        const int ixBase = sm_gridlineIndices[atomIndexLocal * DIM + XX];
+    const int atomWarpIndex = atomIndexLocal % atomsPerWarp;
+    const int warpIndex     = atomIndexLocal / atomsPerWarp;
 
-        if (iz >= nz)
-        {
-            iz -= nz;
-        }
-        int constOffset, iy;
+    const int splineIndexBase = getSplineParamIndexBase<order, atomsPerWarp>(warpIndex, atomWarpIndex);
+    const int    splineIndexZ = getSplineParamIndex<order, atomsPerWarp>(splineIndexBase, ZZ, ithz);
+    const float2 tdz          = make_float2(sm_theta[splineIndexZ], sm_dtheta[splineIndexZ]);
 
-        const int ithyMin = useOrderThreads ? 0 : threadIdx.y;
-        const int ithyMax = useOrderThreads ? order : threadIdx.y + 1;
-        for (int ithy = ithyMin; ithy < ithyMax; ithy++)
-        {
-            const int splineIndexY = getSplineParamIndex<order, atomsPerWarp>(splineIndexBase, YY, ithy);
-            const float2 tdy       = make_float2(sm_theta[splineIndexY], sm_dtheta[splineIndexY]);
+    int       iz     = sm_gridlineIndices[atomIndexLocal * DIM + ZZ] + ithz;
+    const int ixBase = sm_gridlineIndices[atomIndexLocal * DIM + XX];
 
-            iy = sm_gridlineIndices[atomIndexLocal * DIM + YY] + ithy;
-            if (wrapY & (iy >= ny))
-            {
-                iy -= ny;
-            }
-            constOffset = iy * pnz + iz;
-
-#pragma unroll
-            for (int ithx = 0; (ithx < order); ithx++)
-            {
-                int ix = ixBase + ithx;
-                if (wrapX & (ix >= nx))
-                {
-                    ix -= nx;
-                }
-                const int gridIndexGlobal = ix * pny * pnz + constOffset;
-                assert(gridIndexGlobal >= 0);
-                const float gridValue = gm_grid[gridIndexGlobal];
-                assert(isfinite(gridValue));
-                const int splineIndexX =
-                        getSplineParamIndex<order, atomsPerWarp>(splineIndexBase, XX, ithx);
-                const float2 tdx  = make_float2(sm_theta[splineIndexX], sm_dtheta[splineIndexX]);
-                const float  fxy1 = tdz.x * gridValue;
-                const float  fz1  = tdz.y * gridValue;
-                fx += tdx.y * tdy.x * fxy1;
-                fy += tdx.x * tdy.y * fxy1;
-                fz += tdx.x * tdy.x * fz1;
-            }
-        }
+    if (iz >= nz)
+    {
+        iz -= nz;
     }
 
+    const int ithyMin = (threadsPerAtom == ThreadsPerAtom::Order) ? 0 : threadIdx.y;
+    const int ithyMax = (threadsPerAtom == ThreadsPerAtom::Order) ? order : threadIdx.y + 1;
+    if (chargeCheck)
+    {
+        sumForceComponents<order, atomsPerWarp, wrapX, wrapY>(
+                &fx, &fy, &fz, ithyMin, ithyMax, ixBase, iz, nx, ny, pny, pnz, atomIndexLocal,
+                splineIndexBase, tdz, sm_gridlineIndices, sm_theta, sm_dtheta, gm_gridA);
+    }
     // Reduction of partial force contributions
     __shared__ float3 sm_forces[atomsPerBlock];
     reduce_atom_forces<order, atomDataSize, blockSize>(sm_forces, atomIndexLocal, splineIndex, lineIndex,
@@ -428,23 +505,13 @@ __launch_bounds__(c_gatherMaxThreadsPerBlock, c_gatherMinBlocksPerMP) __global__
     __syncthreads();
 
     /* Calculating the final forces with no component branching, atomsPerBlock threads */
-    const int forceIndexLocal  = threadLocalId;
-    const int forceIndexGlobal = atomIndexOffset + forceIndexLocal;
-    const int calcIndexCheck = pme_gpu_check_atom_data_index(forceIndexGlobal, kernelParams.atoms.nAtoms);
-    if ((forceIndexLocal < atomsPerBlock) & calcIndexCheck)
+    const int   forceIndexLocal  = threadLocalId;
+    const int   forceIndexGlobal = atomIndexOffset + forceIndexLocal;
+    const float scale            = kernelParams.current.scale;
+    if (forceIndexLocal < atomsPerBlock)
     {
-        const float3 atomForces     = sm_forces[forceIndexLocal];
-        const float  negCoefficient = -gm_coefficients[forceIndexGlobal];
-        float3       result;
-        result.x = negCoefficient * kernelParams.current.recipBox[XX][XX] * atomForces.x;
-        result.y = negCoefficient
-                   * (kernelParams.current.recipBox[XX][YY] * atomForces.x
-                      + kernelParams.current.recipBox[YY][YY] * atomForces.y);
-        result.z = negCoefficient
-                   * (kernelParams.current.recipBox[XX][ZZ] * atomForces.x
-                      + kernelParams.current.recipBox[YY][ZZ] * atomForces.y
-                      + kernelParams.current.recipBox[ZZ][ZZ] * atomForces.z);
-        sm_forces[forceIndexLocal] = result;
+        calculateAndStoreGridForces(sm_forces, forceIndexLocal, forceIndexGlobal,
+                                    kernelParams.current.recipBox, scale, gm_coefficientsA);
     }
 
     __syncwarp();
@@ -459,32 +526,65 @@ __launch_bounds__(c_gatherMaxThreadsPerBlock, c_gatherMinBlocksPerMP) __global__
 #pragma unroll
         for (int i = 0; i < numIter; i++)
         {
-            int       outputIndexLocal  = i * iterThreads + threadLocalId;
-            int       outputIndexGlobal = blockIndex * blockForcesSize + outputIndexLocal;
-            const int globalOutputCheck =
-                    pme_gpu_check_atom_data_index(outputIndexGlobal, kernelParams.atoms.nAtoms * DIM);
-            if (globalOutputCheck)
+            int   outputIndexLocal       = i * iterThreads + threadLocalId;
+            int   outputIndexGlobal      = blockIndex * blockForcesSize + outputIndexLocal;
+            float outputForceComponent   = ((float*)sm_forces)[outputIndexLocal];
+            gm_forces[outputIndexGlobal] = outputForceComponent;
+        }
+    }
+
+    if (numGrids == 2)
+    {
+        /* We must sync here since the same shared memory is used as above. */
+        __syncthreads();
+        fx                    = 0.0f;
+        fy                    = 0.0f;
+        fz                    = 0.0f;
+        const int chargeCheck = pme_gpu_check_atom_charge(gm_coefficientsB[atomIndexGlobal]);
+        if (chargeCheck)
+        {
+            sumForceComponents<order, atomsPerWarp, wrapX, wrapY>(
+                    &fx, &fy, &fz, ithyMin, ithyMax, ixBase, iz, nx, ny, pny, pnz, atomIndexLocal,
+                    splineIndexBase, tdz, sm_gridlineIndices, sm_theta, sm_dtheta, gm_gridB);
+        }
+        // Reduction of partial force contributions
+        reduce_atom_forces<order, atomDataSize, blockSize>(sm_forces, atomIndexLocal, splineIndex,
+                                                           lineIndex, kernelParams.grid.realGridSizeFP,
+                                                           fx, fy, fz);
+        __syncthreads();
+
+        /* Calculating the final forces with no component branching, atomsPerBlock threads */
+        if (forceIndexLocal < atomsPerBlock)
+        {
+            calculateAndStoreGridForces(sm_forces, forceIndexLocal, forceIndexGlobal,
+                                        kernelParams.current.recipBox, 1.0F - scale, gm_coefficientsB);
+        }
+
+        __syncwarp();
+
+        /* Writing or adding the final forces component-wise, single warp */
+        if (threadLocalId < iterThreads)
+        {
+#pragma unroll
+            for (int i = 0; i < numIter; i++)
             {
-                const float outputForceComponent = ((float*)sm_forces)[outputIndexLocal];
-                if (overwriteForces)
-                {
-                    gm_forces[outputIndexGlobal] = outputForceComponent;
-                }
-                else
-                {
-                    gm_forces[outputIndexGlobal] += outputForceComponent;
-                }
+                int   outputIndexLocal     = i * iterThreads + threadLocalId;
+                int   outputIndexGlobal    = blockIndex * blockForcesSize + outputIndexLocal;
+                float outputForceComponent = ((float*)sm_forces)[outputIndexLocal];
+                gm_forces[outputIndexGlobal] += outputForceComponent;
             }
         }
     }
 }
 
 //! Kernel instantiations
-template __global__ void pme_gather_kernel<4, true, true, true, true, true>(const PmeGpuCudaKernelParams);
-template __global__ void pme_gather_kernel<4, true, true, true, true, false>(const PmeGpuCudaKernelParams);
-template __global__ void pme_gather_kernel<4, false, true, true, true, true>(const PmeGpuCudaKernelParams);
-template __global__ void pme_gather_kernel<4, false, true, true, true, false>(const PmeGpuCudaKernelParams);
-template __global__ void pme_gather_kernel<4, true, true, true, false, true>(const PmeGpuCudaKernelParams);
-template __global__ void pme_gather_kernel<4, true, true, true, false, false>(const PmeGpuCudaKernelParams);
-template __global__ void pme_gather_kernel<4, false, true, true, false, true>(const PmeGpuCudaKernelParams);
-template __global__ void pme_gather_kernel<4, false, true, true, false, false>(const PmeGpuCudaKernelParams);
+// clang-format off
+template __global__ void pme_gather_kernel<4, true, true, 1, true, ThreadsPerAtom::Order>        (const PmeGpuCudaKernelParams);
+template __global__ void pme_gather_kernel<4, true, true, 1, true, ThreadsPerAtom::OrderSquared> (const PmeGpuCudaKernelParams);
+template __global__ void pme_gather_kernel<4, true, true, 1, false, ThreadsPerAtom::Order>       (const PmeGpuCudaKernelParams);
+template __global__ void pme_gather_kernel<4, true, true, 1, false, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+template __global__ void pme_gather_kernel<4, true, true, 2, true, ThreadsPerAtom::Order>        (const PmeGpuCudaKernelParams);
+template __global__ void pme_gather_kernel<4, true, true, 2, true, ThreadsPerAtom::OrderSquared> (const PmeGpuCudaKernelParams);
+template __global__ void pme_gather_kernel<4, true, true, 2, false, ThreadsPerAtom::Order>       (const PmeGpuCudaKernelParams);
+template __global__ void pme_gather_kernel<4, true, true, 2, false, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+// clang-format on
index 846794c94950d56b91da5da297d89bd13a75df8a..a088f25070d472ce43398959704ce00bcc279cd6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_EWALD_PME_GATHER_H
 #define GMX_EWALD_PME_GATHER_H
 
+#include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
-#include "pme_internal.h"
+class PmeAtomComm;
+struct gmx_pme_t;
+struct splinedata_t;
 
 void gather_f_bsplines(const struct gmx_pme_t* pme,
                        const real*             grid,
index 31467da6da1683e0e655157eed515efc5672f1fa..19215fa90fff848956a4cd0e8c562f43a54bf65b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/forceoutput.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
 
 #include "pme_gpu_internal.h"
+#include "pme_gpu_settings.h"
+#include "pme_gpu_timings.h"
+#include "pme_gpu_types_host.h"
 #include "pme_grid.h"
 #include "pme_internal.h"
 #include "pme_solve.h"
 
+/*! \brief
+ * Finds out if PME is currently running on GPU.
+ *
+ * \todo The GPU module should not be constructed (or at least called)
+ * when it is not active, so there should be no need to check whether
+ * it is active. An assertion that this is true makes sense.
+ *
+ * \param[in] pme  The PME structure.
+ * \returns        True if PME runs on GPU currently, false otherwise.
+ */
+static inline bool pme_gpu_active(const gmx_pme_t* pme)
+{
+    return (pme != nullptr) && (pme->runMode != PmeRunMode::CPU);
+}
+
 void pme_gpu_reset_timings(const gmx_pme_t* pme)
 {
     if (pme_gpu_active(pme))
@@ -80,7 +99,7 @@ void pme_gpu_get_timings(const gmx_pme_t* pme, gmx_wallclock_gpu_pme_t* timings)
     }
 }
 
-int pme_gpu_get_padding_size(const gmx_pme_t* pme)
+int pme_gpu_get_block_size(const gmx_pme_t* pme)
 {
 
     if (!pme || !pme_gpu_active(pme))
@@ -89,7 +108,7 @@ int pme_gpu_get_padding_size(const gmx_pme_t* pme)
     }
     else
     {
-        return pme_gpu_get_atom_data_alignment(pme->gpu);
+        return pme_gpu_get_atom_data_block_size();
     }
 }
 
@@ -106,8 +125,7 @@ void inline parallel_3dfft_execute_gpu_wrapper(gmx_pme_t*             pme,
                                                enum gmx_fft_direction dir,
                                                gmx_wallcycle_t        wcycle)
 {
-    GMX_ASSERT(gridIndex == 0, "Only single grid supported");
-    if (pme_gpu_performs_FFT(pme->gpu))
+    if (pme_gpu_settings(pme->gpu).performGPUFFT)
     {
         wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU);
         wallcycle_sub_start_nocount(wcycle, ewcsLAUNCH_GPU_PME);
@@ -129,21 +147,18 @@ void inline parallel_3dfft_execute_gpu_wrapper(gmx_pme_t*             pme,
 
 /* The PME computation code split into a few separate functions. */
 
-void pme_gpu_prepare_computation(gmx_pme_t*     pme,
-                                 bool           needToUpdateBox,
-                                 const matrix   box,
-                                 gmx_wallcycle* wcycle,
-                                 int            flags,
-                                 bool           useGpuForceReduction)
+void pme_gpu_prepare_computation(gmx_pme_t*               pme,
+                                 const matrix             box,
+                                 gmx_wallcycle*           wcycle,
+                                 const gmx::StepWorkload& stepWork)
 {
     GMX_ASSERT(pme_gpu_active(pme), "This should be a GPU run of PME but it is not enabled.");
     GMX_ASSERT(pme->nnodes > 0, "");
     GMX_ASSERT(pme->nnodes == 1 || pme->ndecompdim > 0, "");
 
-    PmeGpu* pmeGpu                = pme->gpu;
-    pmeGpu->settings.currentFlags = flags;
+    PmeGpu* pmeGpu = pme->gpu;
     // TODO these flags are only here to honor the CPU PME code, and probably should be removed
-    pmeGpu->settings.useGpuForceReduction = useGpuForceReduction;
+    pmeGpu->settings.useGpuForceReduction = stepWork.useGpuPmeFReduction;
 
     bool shouldUpdateBox = false;
     for (int i = 0; i < DIM; ++i)
@@ -155,7 +170,7 @@ void pme_gpu_prepare_computation(gmx_pme_t*     pme,
         }
     }
 
-    if (needToUpdateBox || shouldUpdateBox) // || is to make the first computation always update
+    if (stepWork.haveDynamicBox || shouldUpdateBox) // || is to make the first computation always update
     {
         wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU);
         wallcycle_sub_start_nocount(wcycle, ewcsLAUNCH_GPU_PME);
@@ -163,7 +178,7 @@ void pme_gpu_prepare_computation(gmx_pme_t*     pme,
         wallcycle_sub_stop(wcycle, ewcsLAUNCH_GPU_PME);
         wallcycle_stop(wcycle, ewcLAUNCH_GPU);
 
-        if (!pme_gpu_performs_solve(pmeGpu))
+        if (!pme_gpu_settings(pmeGpu).performGPUSolve)
         {
             // TODO remove code duplication and add test coverage
             matrix scaledBox;
@@ -174,61 +189,68 @@ void pme_gpu_prepare_computation(gmx_pme_t*     pme,
     }
 }
 
-void pme_gpu_launch_spread(gmx_pme_t* pme, GpuEventSynchronizer* xReadyOnDevice, gmx_wallcycle* wcycle)
+void pme_gpu_launch_spread(gmx_pme_t*            pme,
+                           GpuEventSynchronizer* xReadyOnDevice,
+                           gmx_wallcycle*        wcycle,
+                           const real            lambdaQ)
 {
     GMX_ASSERT(pme_gpu_active(pme), "This should be a GPU run of PME but it is not enabled.");
-    GMX_ASSERT(xReadyOnDevice || !pme->bPPnode || (GMX_GPU != GMX_GPU_CUDA),
+    GMX_ASSERT(!GMX_GPU_CUDA || xReadyOnDevice || !pme->bPPnode,
                "Need a valid xReadyOnDevice on PP+PME ranks with CUDA.");
+    GMX_ASSERT(pme->doCoulomb, "Only Coulomb PME can be run on GPU.");
 
     PmeGpu* pmeGpu = pme->gpu;
 
-    const unsigned int gridIndex = 0;
-    real*              fftgrid   = pme->fftgrid[gridIndex];
-    if (pmeGpu->settings.currentFlags & GMX_PME_SPREAD)
-    {
-        /* Spread the coefficients on a grid */
-        const bool computeSplines = true;
-        const bool spreadCharges  = true;
-        wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU);
-        wallcycle_sub_start_nocount(wcycle, ewcsLAUNCH_GPU_PME);
-        pme_gpu_spread(pmeGpu, xReadyOnDevice, gridIndex, fftgrid, computeSplines, spreadCharges);
-        wallcycle_sub_stop(wcycle, ewcsLAUNCH_GPU_PME);
-        wallcycle_stop(wcycle, ewcLAUNCH_GPU);
-    }
+    GMX_ASSERT(pmeGpu->common->ngrids == 1 || (pmeGpu->common->ngrids == 2 && pme->bFEP_q),
+               "If not decoupling Coulomb interactions there should only be one FEP grid. If "
+               "decoupling Coulomb interactions there should be two grids.");
+
+    /* PME on GPU can currently manage two grids:
+     * grid_index=0: Coulomb PME with charges in the normal state or from FEP state A.
+     * grid_index=1: Coulomb PME with charges from FEP state B.
+     */
+    real** fftgrids = pme->fftgrid;
+    /* Spread the coefficients on a grid */
+    const bool computeSplines = true;
+    const bool spreadCharges  = true;
+    wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU);
+    wallcycle_sub_start_nocount(wcycle, ewcsLAUNCH_GPU_PME);
+    pme_gpu_spread(pmeGpu, xReadyOnDevice, fftgrids, computeSplines, spreadCharges, lambdaQ);
+    wallcycle_sub_stop(wcycle, ewcsLAUNCH_GPU_PME);
+    wallcycle_stop(wcycle, ewcLAUNCH_GPU);
 }
 
-void pme_gpu_launch_complex_transforms(gmx_pme_t* pme, gmx_wallcycle* wcycle)
+void pme_gpu_launch_complex_transforms(gmx_pme_t* pme, gmx_wallcycle* wcycle, const gmx::StepWorkload& stepWork)
 {
-    PmeGpu*    pmeGpu                 = pme->gpu;
-    const bool computeEnergyAndVirial = (pmeGpu->settings.currentFlags & GMX_PME_CALC_ENER_VIR) != 0;
-    const bool performBackFFT = (pmeGpu->settings.currentFlags & (GMX_PME_CALC_F | GMX_PME_CALC_POT)) != 0;
-    const unsigned int gridIndex = 0;
-    t_complex*         cfftgrid  = pme->cfftgrid[gridIndex];
-
-    if (pmeGpu->settings.currentFlags & GMX_PME_SPREAD)
+    PmeGpu*     pmeGpu   = pme->gpu;
+    const auto& settings = pmeGpu->settings;
+    // There's no support for computing energy without virial, or vice versa
+    const bool computeEnergyAndVirial = stepWork.computeEnergy || stepWork.computeVirial;
+    if (!settings.performGPUFFT)
     {
-        if (!pme_gpu_performs_FFT(pmeGpu))
-        {
-            wallcycle_start(wcycle, ewcWAIT_GPU_PME_SPREAD);
-            pme_gpu_sync_spread_grid(pme->gpu);
-            wallcycle_stop(wcycle, ewcWAIT_GPU_PME_SPREAD);
-        }
+        wallcycle_start(wcycle, ewcWAIT_GPU_PME_SPREAD);
+        pme_gpu_sync_spread_grid(pme->gpu);
+        wallcycle_stop(wcycle, ewcWAIT_GPU_PME_SPREAD);
     }
 
     try
     {
-        if (pmeGpu->settings.currentFlags & GMX_PME_SOLVE)
+        /* The 3dffts and the solve are done in a loop to simplify things, even if this means that
+         * there will be two kernel launches for solve. */
+        for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
         {
             /* do R2C 3D-FFT */
+            t_complex* cfftgrid = pme->cfftgrid[gridIndex];
             parallel_3dfft_execute_gpu_wrapper(pme, gridIndex, GMX_FFT_REAL_TO_COMPLEX, wcycle);
 
             /* solve in k-space for our local cells */
-            if (pme_gpu_performs_solve(pmeGpu))
+            if (settings.performGPUSolve)
             {
-                const auto gridOrdering = pme_gpu_uses_dd(pmeGpu) ? GridOrdering::YZX : GridOrdering::XYZ;
+                const auto gridOrdering =
+                        settings.useDecomposition ? GridOrdering::YZX : GridOrdering::XYZ;
                 wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU);
                 wallcycle_sub_start_nocount(wcycle, ewcsLAUNCH_GPU_PME);
-                pme_gpu_solve(pmeGpu, cfftgrid, gridOrdering, computeEnergyAndVirial);
+                pme_gpu_solve(pmeGpu, gridIndex, cfftgrid, gridOrdering, computeEnergyAndVirial);
                 wallcycle_sub_stop(wcycle, ewcsLAUNCH_GPU_PME);
                 wallcycle_stop(wcycle, ewcLAUNCH_GPU);
             }
@@ -243,30 +265,27 @@ void pme_gpu_launch_complex_transforms(gmx_pme_t* pme, gmx_wallcycle* wcycle)
                 }
                 wallcycle_stop(wcycle, ewcPME_SOLVE_MIXED_MODE);
             }
-        }
 
-        if (performBackFFT)
-        {
             parallel_3dfft_execute_gpu_wrapper(pme, gridIndex, GMX_FFT_COMPLEX_TO_REAL, wcycle);
         }
     }
     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 }
 
-void pme_gpu_launch_gather(const gmx_pme_t* pme, gmx_wallcycle gmx_unused* wcycle, PmeForceOutputHandling forceTreatment)
+void pme_gpu_launch_gather(const gmx_pme_t* pme, gmx_wallcycle gmx_unused* wcycle, const real lambdaQ)
 {
     GMX_ASSERT(pme_gpu_active(pme), "This should be a GPU run of PME but it is not enabled.");
 
-    if (!pme_gpu_performs_gather(pme->gpu))
+    if (!pme_gpu_settings(pme->gpu).performGPUGather)
     {
         return;
     }
 
     wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU);
     wallcycle_sub_start_nocount(wcycle, ewcsLAUNCH_GPU_PME);
-    const unsigned int gridIndex = 0;
-    real*              fftgrid   = pme->fftgrid[gridIndex];
-    pme_gpu_gather(pme->gpu, forceTreatment, reinterpret_cast<float*>(fftgrid));
+
+    float** fftgrids = pme->fftgrid;
+    pme_gpu_gather(pme->gpu, fftgrids, lambdaQ);
     wallcycle_sub_stop(wcycle, ewcsLAUNCH_GPU_PME);
     wallcycle_stop(wcycle, ewcLAUNCH_GPU);
 }
@@ -285,7 +304,7 @@ static void sum_forces(gmx::ArrayRef<gmx::RVec> f, gmx::ArrayRef<const gmx::RVec
 }
 
 //! Reduce quantities from \c output to \c forceWithVirial and \c enerd.
-static void pme_gpu_reduce_outputs(const int             flags,
+static void pme_gpu_reduce_outputs(const bool            computeEnergyAndVirial,
                                    const PmeOutput&      output,
                                    gmx_wallcycle*        wcycle,
                                    gmx::ForceWithVirial* forceWithVirial,
@@ -294,12 +313,12 @@ static void pme_gpu_reduce_outputs(const int             flags,
     wallcycle_start(wcycle, ewcPME_GPU_F_REDUCTION);
     GMX_ASSERT(forceWithVirial, "Invalid force pointer");
 
-    const bool haveComputedEnergyAndVirial = (flags & GMX_PME_CALC_ENER_VIR) != 0;
-    if (haveComputedEnergyAndVirial)
+    if (computeEnergyAndVirial)
     {
         GMX_ASSERT(enerd, "Invalid energy output manager");
         forceWithVirial->addVirialContribution(output.coulombVirial_);
         enerd->term[F_COUL_RECIP] += output.coulombEnergy_;
+        enerd->dvdl_lin[efptCOUL] += output.coulombDvdl_;
     }
     if (output.haveForceOutput_)
     {
@@ -308,12 +327,13 @@ static void pme_gpu_reduce_outputs(const int             flags,
     wallcycle_stop(wcycle, ewcPME_GPU_F_REDUCTION);
 }
 
-bool pme_gpu_try_finish_task(gmx_pme_t*            pme,
-                             const int             flags,
-                             gmx_wallcycle*        wcycle,
-                             gmx::ForceWithVirial* forceWithVirial,
-                             gmx_enerdata_t*       enerd,
-                             GpuTaskCompletion     completionKind)
+bool pme_gpu_try_finish_task(gmx_pme_t*               pme,
+                             const gmx::StepWorkload& stepWork,
+                             gmx_wallcycle*           wcycle,
+                             gmx::ForceWithVirial*    forceWithVirial,
+                             gmx_enerdata_t*          enerd,
+                             const real               lambdaQ,
+                             GpuTaskCompletion        completionKind)
 {
     GMX_ASSERT(pme_gpu_active(pme), "This should be a GPU run of PME but it is not enabled.");
     GMX_ASSERT(!pme->gpu->settings.useGpuForceReduction,
@@ -324,7 +344,8 @@ bool pme_gpu_try_finish_task(gmx_pme_t*            pme,
     // time needed for that checking, but do not yet record that the
     // gather has occured.
     bool           needToSynchronize      = true;
-    constexpr bool c_streamQuerySupported = (GMX_GPU == GMX_GPU_CUDA);
+    constexpr bool c_streamQuerySupported = bool(GMX_GPU_CUDA);
+
     // TODO: implement c_streamQuerySupported with an additional GpuEventSynchronizer per stream (#2521)
     if ((completionKind == GpuTaskCompletion::Check) && c_streamQuerySupported)
     {
@@ -350,18 +371,24 @@ bool pme_gpu_try_finish_task(gmx_pme_t*            pme,
         pme_gpu_synchronize(pme->gpu);
     }
     pme_gpu_update_timings(pme->gpu);
-    PmeOutput output = pme_gpu_getOutput(*pme, flags);
+    // There's no support for computing energy without virial, or vice versa
+    const bool computeEnergyAndVirial = stepWork.computeEnergy || stepWork.computeVirial;
+    PmeOutput  output                 = pme_gpu_getOutput(*pme, computeEnergyAndVirial,
+                                         pme->gpu->common->ngrids > 1 ? lambdaQ : 1.0);
     wallcycle_stop(wcycle, ewcWAIT_GPU_PME_GATHER);
 
     GMX_ASSERT(pme->gpu->settings.useGpuForceReduction == !output.haveForceOutput_,
                "When forces are reduced on the CPU, there needs to be force output");
-    pme_gpu_reduce_outputs(flags, output, wcycle, forceWithVirial, enerd);
+    pme_gpu_reduce_outputs(computeEnergyAndVirial, output, wcycle, forceWithVirial, enerd);
 
     return true;
 }
 
 // This is used by PME-only ranks
-PmeOutput pme_gpu_wait_finish_task(gmx_pme_t* pme, const int flags, gmx_wallcycle* wcycle)
+PmeOutput pme_gpu_wait_finish_task(gmx_pme_t*     pme,
+                                   const bool     computeEnergyAndVirial,
+                                   const real     lambdaQ,
+                                   gmx_wallcycle* wcycle)
 {
     GMX_ASSERT(pme_gpu_active(pme), "This should be a GPU run of PME but it is not enabled.");
 
@@ -370,28 +397,32 @@ PmeOutput pme_gpu_wait_finish_task(gmx_pme_t* pme, const int flags, gmx_wallcycl
     // Synchronize the whole PME stream at once, including D2H result transfers
     // if there are outputs we need to wait for at this step; we still call getOutputs
     // for uniformity and because it sets the PmeOutput.haveForceOutput_.
-    const bool haveComputedEnergyAndVirial = (flags & GMX_PME_CALC_ENER_VIR) != 0;
-    if (!pme->gpu->settings.useGpuForceReduction || haveComputedEnergyAndVirial)
+    if (!pme->gpu->settings.useGpuForceReduction || computeEnergyAndVirial)
     {
         pme_gpu_synchronize(pme->gpu);
     }
 
-    PmeOutput output = pme_gpu_getOutput(*pme, flags);
+    PmeOutput output = pme_gpu_getOutput(*pme, computeEnergyAndVirial,
+                                         pme->gpu->common->ngrids > 1 ? lambdaQ : 1.0);
     wallcycle_stop(wcycle, ewcWAIT_GPU_PME_GATHER);
     return output;
 }
 
 // This is used when not using the alternate-waiting reduction
-void pme_gpu_wait_and_reduce(gmx_pme_t*            pme,
-                             const int             flags,
-                             gmx_wallcycle*        wcycle,
-                             gmx::ForceWithVirial* forceWithVirial,
-                             gmx_enerdata_t*       enerd)
+void pme_gpu_wait_and_reduce(gmx_pme_t*               pme,
+                             const gmx::StepWorkload& stepWork,
+                             gmx_wallcycle*           wcycle,
+                             gmx::ForceWithVirial*    forceWithVirial,
+                             gmx_enerdata_t*          enerd,
+                             const real               lambdaQ)
 {
-    PmeOutput output = pme_gpu_wait_finish_task(pme, flags, wcycle);
+    // There's no support for computing energy without virial, or vice versa
+    const bool computeEnergyAndVirial = stepWork.computeEnergy || stepWork.computeVirial;
+    PmeOutput  output                 = pme_gpu_wait_finish_task(
+            pme, computeEnergyAndVirial, pme->gpu->common->ngrids > 1 ? lambdaQ : 1.0, wcycle);
     GMX_ASSERT(pme->gpu->settings.useGpuForceReduction == !output.haveForceOutput_,
                "When forces are reduced on the CPU, there needs to be force output");
-    pme_gpu_reduce_outputs(flags, output, wcycle, forceWithVirial, enerd);
+    pme_gpu_reduce_outputs(computeEnergyAndVirial, output, wcycle, forceWithVirial, enerd);
 }
 
 void pme_gpu_reinit_computation(const gmx_pme_t* pme, gmx_wallcycle* wcycle)
@@ -410,13 +441,6 @@ void pme_gpu_reinit_computation(const gmx_pme_t* pme, gmx_wallcycle* wcycle)
     wallcycle_stop(wcycle, ewcLAUNCH_GPU);
 }
 
-DeviceBuffer<float> pme_gpu_get_device_x(const gmx_pme_t* pme)
-{
-    GMX_ASSERT((pme && pme_gpu_active(pme)),
-               "PME GPU coordinates buffer was requested from uninitialized PME module");
-    return pme_gpu_get_kernelparam_coordinates(pme->gpu);
-}
-
 void* pme_gpu_get_device_f(const gmx_pme_t* pme)
 {
     if (!pme || !pme_gpu_active(pme))
@@ -426,7 +450,7 @@ void* pme_gpu_get_device_f(const gmx_pme_t* pme)
     return pme_gpu_get_kernelparam_forces(pme->gpu);
 }
 
-void pme_gpu_set_device_x(const gmx_pme_t* pme, DeviceBuffer<float> d_x)
+void pme_gpu_set_device_x(const gmx_pme_t* pme, DeviceBuffer<gmx::RVec> d_x)
 {
     GMX_ASSERT(pme != nullptr, "Null pointer is passed as a PME to the set coordinates function.");
     GMX_ASSERT(pme_gpu_active(pme), "This should be a GPU run of PME but it is not enabled.");
@@ -434,24 +458,6 @@ void pme_gpu_set_device_x(const gmx_pme_t* pme, DeviceBuffer<float> d_x)
     pme_gpu_set_kernelparam_coordinates(pme->gpu, d_x);
 }
 
-void* pme_gpu_get_device_stream(const gmx_pme_t* pme)
-{
-    if (!pme || !pme_gpu_active(pme))
-    {
-        return nullptr;
-    }
-    return pme_gpu_get_stream(pme->gpu);
-}
-
-void* pme_gpu_get_device_context(const gmx_pme_t* pme)
-{
-    if (!pme || !pme_gpu_active(pme))
-    {
-        return nullptr;
-    }
-    return pme_gpu_get_context(pme->gpu);
-}
-
 GpuEventSynchronizer* pme_gpu_get_f_ready_synchronizer(const gmx_pme_t* pme)
 {
     if (!pme || !pme_gpu_active(pme))
index 16bde23684d9a64d7dc52814bf1b9f1958a39053..ec928f745ab4817b7928098f3d9a1bc14304db15 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -60,7 +60,7 @@ static void handleCufftError(cufftResult_t status, const char* msg)
     }
 }
 
-GpuParallel3dFft::GpuParallel3dFft(const PmeGpu* pmeGpu)
+GpuParallel3dFft::GpuParallel3dFft(const PmeGpu* pmeGpu, const int gridIndex)
 {
     const PmeGpuCudaKernelParams* kernelParamsPtr = pmeGpu->kernelParams.get();
     ivec                          realGridSize, realGridSizePadded, complexGridSizePadded;
@@ -71,16 +71,17 @@ GpuParallel3dFft::GpuParallel3dFft(const PmeGpu* pmeGpu)
         complexGridSizePadded[i] = kernelParamsPtr->grid.complexGridSizePadded[i];
     }
 
-    GMX_RELEASE_ASSERT(!pme_gpu_uses_dd(pmeGpu), "FFT decomposition not implemented");
+    GMX_RELEASE_ASSERT(!pme_gpu_settings(pmeGpu).useDecomposition,
+                       "FFT decomposition not implemented");
 
     const int complexGridSizePaddedTotal =
             complexGridSizePadded[XX] * complexGridSizePadded[YY] * complexGridSizePadded[ZZ];
     const int realGridSizePaddedTotal =
             realGridSizePadded[XX] * realGridSizePadded[YY] * realGridSizePadded[ZZ];
 
-    realGrid_ = (cufftReal*)kernelParamsPtr->grid.d_realGrid;
+    realGrid_ = (cufftReal*)kernelParamsPtr->grid.d_realGrid[gridIndex];
     GMX_RELEASE_ASSERT(realGrid_, "Bad (null) input real-space grid");
-    complexGrid_ = (cufftComplex*)kernelParamsPtr->grid.d_fourierGrid;
+    complexGrid_ = (cufftComplex*)kernelParamsPtr->grid.d_fourierGrid[gridIndex];
     GMX_RELEASE_ASSERT(complexGrid_, "Bad (null) input complex grid");
 
     cufftResult_t result;
@@ -103,7 +104,7 @@ GpuParallel3dFft::GpuParallel3dFft(const PmeGpu* pmeGpu)
                            realGridSizePaddedTotal, CUFFT_C2R, batch);
     handleCufftError(result, "cufftPlanMany C2R plan failure");
 
-    cudaStream_t stream = pmeGpu->archSpecific->pmeStream;
+    cudaStream_t stream = pmeGpu->archSpecific->pmeStream_.stream();
     GMX_RELEASE_ASSERT(stream, "Using the default CUDA stream for PME cuFFT");
 
     result = cufftSetStream(planR2C_, stream);
index 07d3b1af5712227e44354f4b759d0b90af5bc752..c334d013e3abdc513750fadcba3b3e30ff422bcb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <vector>
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 #    include <cufft.h>
 
 #    include "gromacs/gpu_utils/gputraits.cuh"
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
 #    include <clFFT.h>
 
 #    include "gromacs/gpu_utils/gmxopencl.h"
@@ -73,8 +73,9 @@ public:
      * Constructs CUDA/OpenCL FFT plans for performing 3D FFT on a PME grid.
      *
      * \param[in] pmeGpu                  The PME GPU structure.
+     * \param[in] gridIndex               The index of the grid on which to perform the calculations.
      */
-    GpuParallel3dFft(const PmeGpu* pmeGpu);
+    GpuParallel3dFft(const PmeGpu* pmeGpu, const int gridIndex);
     /*! \brief Destroys the FFT plans. */
     ~GpuParallel3dFft();
     /*! \brief Performs the FFT transform in given direction
@@ -85,15 +86,15 @@ public:
     void perform3dFft(gmx_fft_direction dir, CommandEvent* timingEvent);
 
 private:
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
     cufftHandle   planR2C_;
     cufftHandle   planC2R_;
     cufftReal*    realGrid_;
     cufftComplex* complexGrid_;
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
     clfftPlanHandle               planR2C_;
     clfftPlanHandle               planC2R_;
-    std::vector<cl_command_queue> commandStreams_;
+    std::vector<cl_command_queue> deviceStreams_;
     cl_mem                        realGrid_;
     cl_mem                        complexGrid_;
 #endif
index cd0a18e0a5264a4ac38eb118caa509b86fdacffb..4ba6649497494df884b6d86868834edb6b527a37 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -63,12 +63,13 @@ static void handleClfftError(clfftStatus status, const char* msg)
     }
 }
 
-GpuParallel3dFft::GpuParallel3dFft(const PmeGpu* pmeGpu)
+GpuParallel3dFft::GpuParallel3dFft(const PmeGpu* pmeGpu, const int gridIndex)
 {
     // Extracting all the data from PME GPU
     std::array<size_t, DIM> realGridSize, realGridSizePadded, complexGridSizePadded;
 
-    GMX_RELEASE_ASSERT(!pme_gpu_uses_dd(pmeGpu), "FFT decomposition not implemented");
+    GMX_RELEASE_ASSERT(!pme_gpu_settings(pmeGpu).useDecomposition,
+                       "FFT decomposition not implemented");
     PmeGpuKernelParamsBase* kernelParamsPtr = pmeGpu->kernelParams.get();
     for (int i = 0; i < DIM; i++)
     {
@@ -79,10 +80,10 @@ GpuParallel3dFft::GpuParallel3dFft(const PmeGpu* pmeGpu)
                            == kernelParamsPtr->grid.complexGridSize[i],
                    "Complex padding not implemented");
     }
-    cl_context context = pmeGpu->archSpecific->context;
-    commandStreams_.push_back(pmeGpu->archSpecific->pmeStream);
-    realGrid_                       = kernelParamsPtr->grid.d_realGrid;
-    complexGrid_                    = kernelParamsPtr->grid.d_fourierGrid;
+    cl_context context = pmeGpu->archSpecific->deviceContext_.context();
+    deviceStreams_.push_back(pmeGpu->archSpecific->pmeStream_.stream());
+    realGrid_                       = kernelParamsPtr->grid.d_realGrid[gridIndex];
+    complexGrid_                    = kernelParamsPtr->grid.d_fourierGrid[gridIndex];
     const bool performOutOfPlaceFFT = pmeGpu->archSpecific->performOutOfPlaceFFT;
 
 
@@ -123,9 +124,9 @@ GpuParallel3dFft::GpuParallel3dFft(const PmeGpu* pmeGpu)
     handleClfftError(clfftSetPlanOutStride(planC2R_, dims, realGridStrides.data()),
                      "clFFT stride setting failure");
 
-    handleClfftError(clfftBakePlan(planR2C_, commandStreams_.size(), commandStreams_.data(), nullptr, nullptr),
+    handleClfftError(clfftBakePlan(planR2C_, deviceStreams_.size(), deviceStreams_.data(), nullptr, nullptr),
                      "clFFT precompiling failure");
-    handleClfftError(clfftBakePlan(planC2R_, commandStreams_.size(), commandStreams_.data(), nullptr, nullptr),
+    handleClfftError(clfftBakePlan(planC2R_, deviceStreams_.size(), deviceStreams_.data(), nullptr, nullptr),
                      "clFFT precompiling failure");
 
     // TODO: implement solve kernel as R2C FFT callback
@@ -165,8 +166,8 @@ void GpuParallel3dFft::perform3dFft(gmx_fft_direction dir, CommandEvent* timingE
             GMX_THROW(
                     gmx::NotImplementedError("The chosen 3D-FFT case is not implemented on GPUs"));
     }
-    handleClfftError(clfftEnqueueTransform(plan, direction, commandStreams_.size(),
-                                           commandStreams_.data(), waitEvents.size(), waitEvents.data(),
+    handleClfftError(clfftEnqueueTransform(plan, direction, deviceStreams_.size(),
+                                           deviceStreams_.data(), waitEvents.size(), waitEvents.data(),
                                            timingEvent, inputGrids, outputGrids, tempBuffer),
                      "clFFT execution failure");
 }
similarity index 78%
rename from src/gromacs/ewald/pme_gpu_utils.clh
rename to src/gromacs/ewald/pme_gpu_calculate_splines.clh
index 5f611e9510b8546dbff90e79f9c3e6d29863001a..6485a62a43e5f81cd11fdb990bda3aeb0531d689 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-#ifndef GMX_EWALD_PME_GPU_UTILS_CLH
-#define GMX_EWALD_PME_GPU_UTILS_CLH
+#ifndef GMX_EWALD_PME_GPU_CALCULATE_SPLINES_CLH
+#define GMX_EWALD_PME_GPU_CALCULATE_SPLINES_CLH
 
 /*! \internal \file
- * \brief This file defines the small PME OpenCL inline device functions.
- * This closely mirrors pme_gpu_utils.h (which is used in CUDA and unit tests), except with no templates.
+ * \brief This file defines the PME OpenCL inline device functions for computing splines.
+ * This closely mirrors pme_gpu_calculate_splines.cuh (which is used in CUDA kernels), except with no templates.
  * Instead of templated parameters this file expects following defines during compilation:
  * - order - PME interpolation order;
  * - atomsPerWarp - number of atoms processed by a warp (fixed for spread and gather kernels to be the same);
- * - c_usePadding and c_skipNeutralAtoms - same as in pme_gpu_constants.h.
+ * - c_skipNeutralAtoms - same as in pme_gpu_constants.h.
  *
  * \author Aleksei Iupinov <a.yupinov@gmail.com>
  * \ingroup module_ewald
@@ -78,8 +78,6 @@ inline int getSplineParamIndexBase(int warpIndex, int atomWarpIndex)
  * \param[in] paramIndexBase   Must be result of getSplineParamIndexBase().
  * \param[in] dimIndex         Dimension index (from 0 to 2)
  * \param[in] splineIndex      Spline contribution index (from 0 to \p order - 1)
- * \param[in] order            PME order
- * \param[in] atomsPerWarp     Number of atoms processed by a warp
  *
  * \returns Index into theta or dtheta array using GPU layout.
  */
@@ -90,21 +88,6 @@ inline int getSplineParamIndex(int paramIndexBase, int dimIndex, int splineIndex
     return (paramIndexBase + (splineIndex * DIM + dimIndex) * atomsPerWarp);
 }
 
-/*! \brief
- * A function for checking the global atom data indices against the atom data array sizes.
- *
- * \param[in] atomDataIndexGlobal  The atom data index.
- * \param[in] nAtomData            The atom data array element count.
- * \returns                        Non-0 if index is within bounds (or PME data padding is enabled), 0 otherwise.
- *
- * This is called from the spline_and_spread and gather PME kernels.
- * The goal is to isolate the global range checks, and allow avoiding them with c_usePadding being true.
- */
-inline int pme_gpu_check_atom_data_index(const size_t atomDataIndex, const size_t nAtomData)
-{
-    return c_usePadding ? 1 : (atomDataIndex < nAtomData);
-}
-
 /*! \brief
  * A function for optionally skipping neutral charges, depending on c_skipNeutralAtoms.
  *
@@ -113,7 +96,8 @@ inline int pme_gpu_check_atom_data_index(const size_t atomDataIndex, const size_
  */
 inline int pme_gpu_check_atom_charge(const float coefficient)
 {
-    return c_skipNeutralAtoms ? (coefficient != 0.0f) : 1;
+    assert(isfinite(coefficient));
+    return c_skipNeutralAtoms ? (coefficient != 0.0F) : 1;
 }
 
 #endif
similarity index 74%
rename from src/gromacs/ewald/pme_calculate_splines.cuh
rename to src/gromacs/ewald/pme_gpu_calculate_splines.cuh
index f52c81c59925aad2147f5c48a14fd5f21e6101c3..05649b600ad6914930b2f258144fe4e0a4f3e226 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <cassert>
 
 #include "gromacs/gpu_utils/cuda_kernel_utils.cuh"
+#include "gromacs/gpu_utils/vectype_ops.cuh"
 
 #include "pme.cuh"
-#include "pme_gpu_utils.h"
 #include "pme_grid.h"
 
+/*! \internal \brief
+ * Gets a base of the unique index to an element in a spline parameter buffer (theta/dtheta),
+ * which is laid out for GPU spread/gather kernels. The base only corresponds to the atom index within the execution block.
+ * Feed the result into getSplineParamIndex() to get a full index.
+ * TODO: it's likely that both parameters can be just replaced with a single atom index, as they are derived from it.
+ * Do that, verifying that the generated code is not bloated, and/or revise the spline indexing scheme.
+ * Removing warp dependency would also be nice (and would probably coincide with removing c_pmeSpreadGatherAtomsPerWarp).
+ *
+ * \tparam order               PME order
+ * \tparam atomsPerWarp        Number of atoms processed by a warp
+ * \param[in] warpIndex        Warp index wrt the block.
+ * \param[in] atomWarpIndex    Atom index wrt the warp (from 0 to atomsPerWarp - 1).
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+template<int order, int atomsPerWarp>
+int __device__ __forceinline__ getSplineParamIndexBase(int warpIndex, int atomWarpIndex)
+{
+    assert((atomWarpIndex >= 0) && (atomWarpIndex < atomsPerWarp));
+    const int dimIndex    = 0;
+    const int splineIndex = 0;
+    // The zeroes are here to preserve the full index formula for reference
+    return (((splineIndex + order * warpIndex) * DIM + dimIndex) * atomsPerWarp + atomWarpIndex);
+}
+
+/*! \internal \brief
+ * Gets a unique index to an element in a spline parameter buffer (theta/dtheta),
+ * which is laid out for GPU spread/gather kernels. The index is wrt to the execution block,
+ * in range(0, atomsPerBlock * order * DIM).
+ * This function consumes result of getSplineParamIndexBase() and adjusts it for \p dimIndex and \p splineIndex.
+ *
+ * \tparam order               PME order
+ * \tparam atomsPerWarp        Number of atoms processed by a warp
+ * \param[in] paramIndexBase   Must be result of getSplineParamIndexBase().
+ * \param[in] dimIndex         Dimension index (from 0 to 2)
+ * \param[in] splineIndex      Spline contribution index (from 0 to \p order - 1)
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+template<int order, int atomsPerWarp>
+int __device__ __forceinline__ getSplineParamIndex(int paramIndexBase, int dimIndex, int splineIndex)
+{
+    assert((dimIndex >= XX) && (dimIndex < DIM));
+    assert((splineIndex >= 0) && (splineIndex < order));
+    return (paramIndexBase + (splineIndex * DIM + dimIndex) * atomsPerWarp);
+}
+
+/*! \internal \brief
+ * An inline CUDA function for skipping the zero-charge atoms.
+ *
+ * \returns                        Non-0 if atom should be processed, 0 otherwise.
+ * \param[in] coefficient          The atom charge.
+ *
+ * This is called from the spline_and_spread and gather PME kernels.
+ */
+int __device__ __forceinline__ pme_gpu_check_atom_charge(const float coefficient)
+{
+    assert(isfinite(coefficient));
+    return c_skipNeutralAtoms ? (coefficient != 0.0f) : 1;
+}
+
 //! Controls if the atom and charge data is prefeched into shared memory or loaded per thread from global
 static const bool c_useAtomDataPrefetch = true;
 
+/*! \brief Asserts if the argument is finite.
+ *
+ *  The function works for any data type, that can be casted to float. Note that there is also
+ *  a specialized implementation for float3 data type.
+ *
+ * \param[in] arg  Argument to check.
+ */
+template<typename T>
+__device__ inline void assertIsFinite(T arg);
+
+template<>
+__device__ inline void assertIsFinite(float3 arg)
+{
+    assert(isfinite(float(arg.x)));
+    assert(isfinite(float(arg.y)));
+    assert(isfinite(float(arg.z)));
+}
+
+template<typename T>
+__device__ inline void assertIsFinite(T arg)
+{
+    assert(isfinite(float(arg)));
+}
+
 /*! \brief
  * General purpose function for loading atom-related data from global to shared memory.
  *
- * \tparam[in] T                 Data type (float/int/...)
- * \tparam[in] atomsPerBlock     Number of atoms processed by a block - should be accounted for in the size of the shared memory array.
- * \tparam[in] dataCountPerAtom  Number of data elements per single atom (e.g. DIM for an rvec coordinates array).
- * \param[in]  kernelParams      Input PME CUDA data in constant memory.
- * \param[out] sm_destination    Shared memory array for output.
- * \param[in]  gm_source         Global memory array for input.
+ * \tparam[in] T                  Data type (float/int/...)
+ * \tparam[in] atomsPerBlock      Number of atoms processed by a block - should be accounted for in
+ * the size of the shared memory array.
+ * \tparam[in] dataCountPerAtom   Number of data elements per single atom (e.g. DIM for an rvec
+ * coordinates array).
+ * \param[out] sm_destination     Shared memory array for output.
+ * \param[in]  gm_source          Global memory array for input.
  */
-template<typename T, const int atomsPerBlock, const int dataCountPerAtom>
-__device__ __forceinline__ void pme_gpu_stage_atom_data(const PmeGpuCudaKernelParams kernelParams,
-                                                        T* __restrict__ sm_destination,
+template<typename T, int atomsPerBlock, int dataCountPerAtom>
+__device__ __forceinline__ void pme_gpu_stage_atom_data(T* __restrict__ sm_destination,
                                                         const T* __restrict__ gm_source)
 {
-    static_assert(c_usePadding,
-                  "With padding disabled, index checking should be fixed to account for spline "
-                  "theta/dtheta pr-warp alignment");
     const int blockIndex       = blockIdx.y * gridDim.x + blockIdx.x;
     const int threadLocalIndex = ((threadIdx.z * blockDim.y + threadIdx.y) * blockDim.x) + threadIdx.x;
     const int localIndex       = threadLocalIndex;
     const int globalIndexBase = blockIndex * atomsPerBlock * dataCountPerAtom;
     const int globalIndex     = globalIndexBase + localIndex;
-    const int globalCheck =
-            pme_gpu_check_atom_data_index(globalIndex, kernelParams.atoms.nAtoms * dataCountPerAtom);
-    if ((localIndex < atomsPerBlock * dataCountPerAtom) & globalCheck)
+    if (localIndex < atomsPerBlock * dataCountPerAtom)
     {
-        assert(isfinite(float(gm_source[globalIndex])));
+        assertIsFinite(gm_source[globalIndex]);
         sm_destination[localIndex] = gm_source[globalIndex];
     }
 }
@@ -104,7 +184,7 @@ __device__ __forceinline__ void pme_gpu_stage_atom_data(const PmeGpuCudaKernelPa
  * \param[out] sm_gridlineIndices   Atom gridline indices in the shared memory.
  */
 
-template<const int order, const int atomsPerBlock, const int atomsPerWarp, const bool writeSmDtheta, const bool writeGlobal>
+template<int order, int atomsPerBlock, int atomsPerWarp, bool writeSmDtheta, bool writeGlobal>
 __device__ __forceinline__ void calculate_splines(const PmeGpuCudaKernelParams kernelParams,
                                                   const int                    atomIndexOffset,
                                                   const float3                 atomX,
@@ -131,8 +211,6 @@ __device__ __forceinline__ void calculate_splines(const PmeGpuCudaKernelParams k
     /* Atom index w.r.t. block/shared memory */
     const int atomIndexLocal = warpIndex * atomsPerWarp + atomWarpIndex;
 
-    /* Atom index w.r.t. global memory */
-    const int atomIndexGlobal = atomIndexOffset + atomIndexLocal;
     /* Spline contribution index in one dimension */
     const int threadLocalIdXY = (threadIdx.y * blockDim.x) + threadIdx.x;
     const int orderIndex      = threadLocalIdXY / DIM;
@@ -145,10 +223,9 @@ __device__ __forceinline__ void calculate_splines(const PmeGpuCudaKernelParams k
     float splineData[order];
 
     const int localCheck = (dimIndex < DIM) && (orderIndex < 1);
-    const int globalCheck = pme_gpu_check_atom_data_index(atomIndexGlobal, kernelParams.atoms.nAtoms);
 
     /* we have 4 threads per atom, but can only use 3 here for the dimensions */
-    if (localCheck && globalCheck)
+    if (localCheck)
     {
         /* Indices interpolation */
 
similarity index 71%
rename from src/gromacs/ewald/pme_gpu_utils.h
rename to src/gromacs/ewald/pme_gpu_calculate_splines.h
index 9e4ca2a8bc716dd9f1a8a2add27d324e9e374d26..ab87a73c02212f318769874fd11014a56f81091f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * \ingroup module_ewald
  */
 
-#include "config.h"
-
 #include <cassert>
 
-#include "pme_gpu_constants.h"
+#include "gromacs/math/vectypes.h"
 
-//! A macro for inline GPU functions.
-#if GMX_GPU == GMX_GPU_CUDA
-#    define INLINE_EVERYWHERE __host__ __device__ __forceinline__
-#else
-#    define INLINE_EVERYWHERE inline
-#endif
+struct PmeGpu;
 
 /*! \internal \brief
  * Gets a base of the unique index to an element in a spline parameter buffer (theta/dtheta),
@@ -74,7 +67,7 @@
  * \returns Index into theta or dtheta array using GPU layout.
  */
 template<int order, int atomsPerWarp>
-int INLINE_EVERYWHERE getSplineParamIndexBase(int warpIndex, int atomWarpIndex)
+int inline getSplineParamIndexBase(int warpIndex, int atomWarpIndex)
 {
     assert((atomWarpIndex >= 0) && (atomWarpIndex < atomsPerWarp));
     const int dimIndex    = 0;
@@ -98,45 +91,11 @@ int INLINE_EVERYWHERE getSplineParamIndexBase(int warpIndex, int atomWarpIndex)
  * \returns Index into theta or dtheta array using GPU layout.
  */
 template<int order, int atomsPerWarp>
-int INLINE_EVERYWHERE getSplineParamIndex(int paramIndexBase, int dimIndex, int splineIndex)
+int inline getSplineParamIndex(int paramIndexBase, int dimIndex, int splineIndex)
 {
     assert((dimIndex >= XX) && (dimIndex < DIM));
     assert((splineIndex >= 0) && (splineIndex < order));
     return (paramIndexBase + (splineIndex * DIM + dimIndex) * atomsPerWarp);
 }
 
-#if GMX_GPU == GMX_GPU_CUDA
-// CUDA device code helpers below
-
-/*! \internal \brief
- * An inline CUDA function for checking the global atom data indices against the atom data array sizes.
- *
- * \param[in] atomDataIndex        The atom data index.
- * \param[in] nAtomData            The atom data array element count.
- * \returns                        Non-0 if index is within bounds (or PME data padding is enabled), 0 otherwise.
- *
- * This is called from the spline_and_spread and gather PME kernels.
- * The goal is to isolate the global range checks, and allow avoiding them with c_usePadding enabled.
- */
-int __device__ __forceinline__ pme_gpu_check_atom_data_index(const int atomDataIndex, const int nAtomData)
-{
-    return c_usePadding ? 1 : (atomDataIndex < nAtomData);
-}
-
-/*! \internal \brief
- * An inline CUDA function for skipping the zero-charge atoms.
- *
- * \returns                        Non-0 if atom should be processed, 0 otherwise.
- * \param[in] coefficient          The atom charge.
- *
- * This is called from the spline_and_spread and gather PME kernels.
- */
-int __device__ __forceinline__ pme_gpu_check_atom_charge(const float coefficient)
-{
-    assert(isfinite(coefficient));
-    return c_skipNeutralAtoms ? (coefficient != 0.0f) : 1;
-}
-
-#endif
-
 #endif
index 35299ca6b1198e7e17da5e02039663132a1cd4b5..d2503cb948ab84e6e8d13badf25a122544c48797 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -42,7 +42,7 @@
  *
  * \todo The values are currently common to both CUDA and OpenCL
  * implementations, but should be reconsidered when we tune the OpenCL
- * implementation. See Redmine #2528.
+ * implementation. See Issue #2528.
  *
  * \author Aleksei Iupinov <a.yupinov@gmail.com>
  * \ingroup module_ewald
 
 #include "config.h"
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 #    include "gromacs/gpu_utils/cuda_arch_utils.cuh" // for warp_size
 #endif
 
 /* General settings for PME GPU behaviour */
 
-/*! \brief
- * false: The atom data GPU buffers are sized precisely according to the number of atoms.
- *        (Except GPU spline data layout which is regardless intertwined for 2 atoms per warp).
- *        The atom index checks in the spread/gather code potentially hinder the performance.
- * true:  The atom data GPU buffers are padded with zeroes so that the possible number of atoms
- *        fitting in is divisible by c_pmeAtomDataAlignment.
- *        The atom index checks are not performed. There should be a performance win, but how big is it, remains to be seen.
- *        Additional cudaMemsetAsync calls are done occasionally (only charges/coordinates; spline data is always recalculated now).
- * \todo Estimate performance differences
- */
-constexpr bool c_usePadding = true;
-
 /*! \brief
  * false: Atoms with zero charges are processed by PME. Could introduce some overhead.
  * true:  Atoms with zero charges are not processed by PME. Adds branching to the spread/gather.
@@ -120,40 +108,27 @@ constexpr int c_virialAndEnergyCount = 7;
  */
 constexpr int c_pmeGpuOrder = 4;
 
-/*! \brief
- * The number of GPU threads used for computing spread/gather contributions of a single atom as function of the PME order.
- * The assumption is currently that any thread processes only a single atom's contributions.
- * TODO: this assumption leads to minimum execution width of 16. See Redmine #2516
- */
-constexpr int c_pmeSpreadGatherThreadsPerAtom = c_pmeGpuOrder * c_pmeGpuOrder;
-
-//! Number of threads per atom when order threads are used
-constexpr int c_pmeSpreadGatherThreadsPerAtom4ThPerAtom = c_pmeGpuOrder;
-
-/*! \brief Minimum execution width of the PME spread and gather kernels.
+/*! \brief The number of GPU threads used for computing spread/gather
+ * contributions of a single atom, which relates to the PME order.
  *
- * Due to the one thread per atom and order=4 implementation constraints, order^2 threads
- * should execute without synchronization needed. See c_pmeSpreadGatherThreadsPerAtom
- */
-constexpr int c_pmeSpreadGatherMinWarpSize = c_pmeSpreadGatherThreadsPerAtom;
-
-//! Minimum warp size if order threads pera atom are used instead of order^2
-constexpr int c_pmeSpreadGatherMinWarpSize4ThPerAtom = c_pmeSpreadGatherThreadsPerAtom4ThPerAtom;
-
-/*! \brief
- * Atom data alignment (in terms of number of atoms).
- * This is the least common multiple of number of atoms processed by
- * a single block/workgroup of the spread and gather kernels.
- * If the GPU atom data buffers are padded (c_usePadding == true),
- * Then the numbers of atoms which would fit in the padded GPU buffers have to be divisible by this.
- * There are debug asserts for this divisibility in pme_gpu_spread() and pme_gpu_gather().
+ * TODO: this assumption leads to minimum execution width of 16. See Issue #2516
  */
-constexpr int c_pmeAtomDataAlignment = 64;
+enum class ThreadsPerAtom : int
+{
+    /*! \brief Use a number of threads equal to the PME order (ie. 4)
+     *
+     * Only CUDA implements this. See Issue #2516 */
+    Order,
+    //! Use a number of threads equal to the square of the PME order (ie. 16)
+    OrderSquared,
+    //! Size of the enumeration
+    Count
+};
 
 /*
  * The execution widths for PME GPU kernels, used both on host and device for correct scheduling.
  * TODO: those were tuned for CUDA with assumption of warp size 32; specialize those for OpenCL
- * (Redmine #2528).
+ * (Issue #2528).
  * As noted below, these are very approximate maximum sizes; in run time we might have to use
  * smaller block/workgroup sizes, depending on device capabilities.
  */
@@ -168,37 +143,27 @@ constexpr int c_solveMaxWarpsPerBlock = 8;
 //! Gathering max block width in warps - picked empirically among 2, 4, 8, 16 for max. occupancy and min. runtime
 constexpr int c_gatherMaxWarpsPerBlock = 4;
 
-
-#if GMX_GPU == GMX_GPU_CUDA
-
-/* All the guys below are dependent on warp_size and should ideally be removed from the host-side code,
- * as we have to do that for OpenCL already.
- * They also express maximum desired block/workgroup sizes, while both with CUDA and OpenCL we have to treat
- * the device runtime limitations gracefully as well.
- */
-
-/*! \brief
- * The number of atoms processed by a single warp in spread/gather.
- * This macro depends on the templated order parameter (2 atoms per warp for order 4 and warp_size
- * of 32). It is mostly used for spline data layout tweaked for coalesced access.
+#if GMX_GPU_CUDA
+/* All the fields below are dependent on warp_size and should
+ * ideally be removed from the device-side code, as we have to
+ * do that for OpenCL already.
+ *
+ * They also express maximum desired block/workgroup sizes,
+ * while both with CUDA and OpenCL we have to treat the device
+ * runtime limitations gracefully as well.
  */
-constexpr int c_pmeSpreadGatherAtomsPerWarp = (warp_size / c_pmeSpreadGatherThreadsPerAtom);
-
-//! number of atoms per warp when order threads are used per atom
-constexpr int c_pmeSpreadGatherAtomsPerWarp4ThPerAtom =
-        (warp_size / c_pmeSpreadGatherThreadsPerAtom4ThPerAtom);
 
 //! Spreading max block size in threads
-constexpr int c_spreadMaxThreadsPerBlock = c_spreadMaxWarpsPerBlock * warp_size;
+static constexpr int c_spreadMaxThreadsPerBlock = c_spreadMaxWarpsPerBlock * warp_size;
 
 //! Solving kernel max block size in threads
-constexpr int c_solveMaxThreadsPerBlock = (c_solveMaxWarpsPerBlock * warp_size);
+static constexpr int c_solveMaxThreadsPerBlock = c_solveMaxWarpsPerBlock * warp_size;
 
 //! Gathering max block size in threads
-constexpr int c_gatherMaxThreadsPerBlock = c_gatherMaxWarpsPerBlock * warp_size;
+static constexpr int c_gatherMaxThreadsPerBlock = c_gatherMaxWarpsPerBlock * warp_size;
 //! Gathering min blocks per CUDA multiprocessor
-constexpr int c_gatherMinBlocksPerMP = GMX_CUDA_MAX_THREADS_PER_MP / c_gatherMaxThreadsPerBlock;
+static constexpr int c_gatherMinBlocksPerMP = GMX_CUDA_MAX_THREADS_PER_MP / c_gatherMaxThreadsPerBlock;
 
-#endif // GMX_GPU == GMX_GPU_CUDA
+#endif // GMX_GPU_CUDA
 
 #endif
index e2c3059774913e524b85a85625a47245bfd991d8..9575aaf2ff36b6ded5229070a2ef0faa64ce3d58 100644 (file)
 #include <string>
 
 #include "gromacs/ewald/ewald_utils.h"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/hardware/device_information.h"
 #include "gromacs/math/invertmatrix.h"
 #include "gromacs/math/units.h"
 #include "gromacs/timing/gpu_timing.h"
 #include "gromacs/utility/logger.h"
 #include "gromacs/utility/stringutil.h"
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 #    include "gromacs/gpu_utils/pmalloc_cuda.h"
 
 #    include "pme.cuh"
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
 #    include "gromacs/gpu_utils/gmxopencl.h"
 #endif
 
 #include "gromacs/ewald/pme.h"
 
 #include "pme_gpu_3dfft.h"
+#include "pme_gpu_calculate_splines.h"
 #include "pme_gpu_constants.h"
 #include "pme_gpu_program_impl.h"
 #include "pme_gpu_timings.h"
 #include "pme_gpu_types.h"
 #include "pme_gpu_types_host.h"
 #include "pme_gpu_types_host_impl.h"
-#include "pme_gpu_utils.h"
 #include "pme_grid.h"
 #include "pme_internal.h"
 #include "pme_solve.h"
@@ -108,59 +111,69 @@ static PmeGpuKernelParamsBase* pme_gpu_get_kernel_params_base_ptr(const PmeGpu*
     return kernelParamsPtr;
 }
 
-int pme_gpu_get_atom_data_alignment(const PmeGpu* /*unused*/)
-{
-    // TODO: this can be simplified, as c_pmeAtomDataAlignment is now constant
-    if (c_usePadding)
-    {
-        return c_pmeAtomDataAlignment;
-    }
-    else
-    {
-        return 0;
-    }
-}
+/*! \brief
+ * Atom data block size (in terms of number of atoms).
+ * This is the least common multiple of number of atoms processed by
+ * a single block/workgroup of the spread and gather kernels.
+ * The GPU atom data buffers must be padded, which means that
+ * the numbers of atoms used for determining the size of the memory
+ * allocation must be divisible by this.
+ */
+constexpr int c_pmeAtomDataBlockSize = 64;
 
-int pme_gpu_get_atoms_per_warp(const PmeGpu* pmeGpu)
+int pme_gpu_get_atom_data_block_size()
 {
-    if (pmeGpu->settings.useOrderThreadsPerAtom)
-    {
-        return pmeGpu->programHandle_->impl_->warpSize / c_pmeSpreadGatherThreadsPerAtom4ThPerAtom;
-    }
-    else
-    {
-        return pmeGpu->programHandle_->impl_->warpSize / c_pmeSpreadGatherThreadsPerAtom;
-    }
+    return c_pmeAtomDataBlockSize;
 }
 
 void pme_gpu_synchronize(const PmeGpu* pmeGpu)
 {
-    gpuStreamSynchronize(pmeGpu->archSpecific->pmeStream);
+    pmeGpu->archSpecific->pmeStream_.synchronize();
 }
 
 void pme_gpu_alloc_energy_virial(PmeGpu* pmeGpu)
 {
     const size_t energyAndVirialSize = c_virialAndEnergyCount * sizeof(float);
-    allocateDeviceBuffer(&pmeGpu->kernelParams->constants.d_virialAndEnergy, c_virialAndEnergyCount,
-                         pmeGpu->archSpecific->context);
-    pmalloc(reinterpret_cast<void**>(&pmeGpu->staging.h_virialAndEnergy), energyAndVirialSize);
+
+    GMX_ASSERT(
+            pmeGpu->common->ngrids == 1 || pmeGpu->common->ngrids == 2,
+            "Only one (normal Coulomb PME) or two (FEP coulomb PME) PME grids can be used on GPU");
+
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
+    {
+        allocateDeviceBuffer(&pmeGpu->kernelParams->constants.d_virialAndEnergy[gridIndex],
+                             c_virialAndEnergyCount, pmeGpu->archSpecific->deviceContext_);
+        pmalloc(reinterpret_cast<void**>(&pmeGpu->staging.h_virialAndEnergy[gridIndex]), energyAndVirialSize);
+    }
 }
 
 void pme_gpu_free_energy_virial(PmeGpu* pmeGpu)
 {
-    freeDeviceBuffer(&pmeGpu->kernelParams->constants.d_virialAndEnergy);
-    pfree(pmeGpu->staging.h_virialAndEnergy);
-    pmeGpu->staging.h_virialAndEnergy = nullptr;
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
+    {
+        freeDeviceBuffer(&pmeGpu->kernelParams->constants.d_virialAndEnergy[gridIndex]);
+        pfree(pmeGpu->staging.h_virialAndEnergy[gridIndex]);
+        pmeGpu->staging.h_virialAndEnergy[gridIndex] = nullptr;
+    }
 }
 
 void pme_gpu_clear_energy_virial(const PmeGpu* pmeGpu)
 {
-    clearDeviceBufferAsync(&pmeGpu->kernelParams->constants.d_virialAndEnergy, 0,
-                           c_virialAndEnergyCount, pmeGpu->archSpecific->pmeStream);
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
+    {
+        clearDeviceBufferAsync(&pmeGpu->kernelParams->constants.d_virialAndEnergy[gridIndex], 0,
+                               c_virialAndEnergyCount, pmeGpu->archSpecific->pmeStream_);
+    }
 }
 
-void pme_gpu_realloc_and_copy_bspline_values(PmeGpu* pmeGpu)
+void pme_gpu_realloc_and_copy_bspline_values(PmeGpu* pmeGpu, const int gridIndex)
 {
+    GMX_ASSERT(
+            pmeGpu->common->ngrids == 1 || pmeGpu->common->ngrids == 2,
+            "Only one (normal Coulomb PME) or two (FEP coulomb PME) PME grids can be used on GPU");
+    GMX_ASSERT(gridIndex < pmeGpu->common->ngrids,
+               "Invalid combination of gridIndex and number of grids");
+
     const int splineValuesOffset[DIM] = { 0, pmeGpu->kernelParams->grid.realGridSize[XX],
                                           pmeGpu->kernelParams->grid.realGridSize[XX]
                                                   + pmeGpu->kernelParams->grid.realGridSize[YY] };
@@ -169,32 +182,36 @@ void pme_gpu_realloc_and_copy_bspline_values(PmeGpu* pmeGpu)
     const int newSplineValuesSize = pmeGpu->kernelParams->grid.realGridSize[XX]
                                     + pmeGpu->kernelParams->grid.realGridSize[YY]
                                     + pmeGpu->kernelParams->grid.realGridSize[ZZ];
-    const bool shouldRealloc = (newSplineValuesSize > pmeGpu->archSpecific->splineValuesSize);
-    reallocateDeviceBuffer(&pmeGpu->kernelParams->grid.d_splineModuli, newSplineValuesSize,
-                           &pmeGpu->archSpecific->splineValuesSize,
-                           &pmeGpu->archSpecific->splineValuesSizeAlloc, pmeGpu->archSpecific->context);
+    const bool shouldRealloc = (newSplineValuesSize > pmeGpu->archSpecific->splineValuesSize[gridIndex]);
+    reallocateDeviceBuffer(&pmeGpu->kernelParams->grid.d_splineModuli[gridIndex],
+                           newSplineValuesSize, &pmeGpu->archSpecific->splineValuesSize[gridIndex],
+                           &pmeGpu->archSpecific->splineValuesCapacity[gridIndex],
+                           pmeGpu->archSpecific->deviceContext_);
     if (shouldRealloc)
     {
         /* Reallocate the host buffer */
-        pfree(pmeGpu->staging.h_splineModuli);
-        pmalloc(reinterpret_cast<void**>(&pmeGpu->staging.h_splineModuli),
+        pfree(pmeGpu->staging.h_splineModuli[gridIndex]);
+        pmalloc(reinterpret_cast<void**>(&pmeGpu->staging.h_splineModuli[gridIndex]),
                 newSplineValuesSize * sizeof(float));
     }
     for (int i = 0; i < DIM; i++)
     {
-        memcpy(pmeGpu->staging.h_splineModuli + splineValuesOffset[i],
+        memcpy(pmeGpu->staging.h_splineModuli[gridIndex] + splineValuesOffset[i],
                pmeGpu->common->bsp_mod[i].data(), pmeGpu->common->bsp_mod[i].size() * sizeof(float));
     }
     /* TODO: pin original buffer instead! */
-    copyToDeviceBuffer(&pmeGpu->kernelParams->grid.d_splineModuli, pmeGpu->staging.h_splineModuli,
-                       0, newSplineValuesSize, pmeGpu->archSpecific->pmeStream,
-                       pmeGpu->settings.transferKind, nullptr);
+    copyToDeviceBuffer(&pmeGpu->kernelParams->grid.d_splineModuli[gridIndex],
+                       pmeGpu->staging.h_splineModuli[gridIndex], 0, newSplineValuesSize,
+                       pmeGpu->archSpecific->pmeStream_, pmeGpu->settings.transferKind, nullptr);
 }
 
 void pme_gpu_free_bspline_values(const PmeGpu* pmeGpu)
 {
-    pfree(pmeGpu->staging.h_splineModuli);
-    freeDeviceBuffer(&pmeGpu->kernelParams->grid.d_splineModuli);
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
+    {
+        pfree(pmeGpu->staging.h_splineModuli[gridIndex]);
+        freeDeviceBuffer(&pmeGpu->kernelParams->grid.d_splineModuli[gridIndex]);
+    }
 }
 
 void pme_gpu_realloc_forces(PmeGpu* pmeGpu)
@@ -202,8 +219,8 @@ void pme_gpu_realloc_forces(PmeGpu* pmeGpu)
     const size_t newForcesSize = pmeGpu->nAtomsAlloc * DIM;
     GMX_ASSERT(newForcesSize > 0, "Bad number of atoms in PME GPU");
     reallocateDeviceBuffer(&pmeGpu->kernelParams->atoms.d_forces, newForcesSize,
-                           &pmeGpu->archSpecific->forcesSize,
-                           &pmeGpu->archSpecific->forcesSizeAlloc, pmeGpu->archSpecific->context);
+                           &pmeGpu->archSpecific->forcesSize, &pmeGpu->archSpecific->forcesSizeAlloc,
+                           pmeGpu->archSpecific->deviceContext_);
     pmeGpu->staging.h_forces.reserveWithPadding(pmeGpu->nAtomsAlloc);
     pmeGpu->staging.h_forces.resizeWithPadding(pmeGpu->kernelParams->atoms.nAtoms);
 }
@@ -218,7 +235,7 @@ void pme_gpu_copy_input_forces(PmeGpu* pmeGpu)
     GMX_ASSERT(pmeGpu->kernelParams->atoms.nAtoms > 0, "Bad number of atoms in PME GPU");
     float* h_forcesFloat = reinterpret_cast<float*>(pmeGpu->staging.h_forces.data());
     copyToDeviceBuffer(&pmeGpu->kernelParams->atoms.d_forces, h_forcesFloat, 0,
-                       DIM * pmeGpu->kernelParams->atoms.nAtoms, pmeGpu->archSpecific->pmeStream,
+                       DIM * pmeGpu->kernelParams->atoms.nAtoms, pmeGpu->archSpecific->pmeStream_,
                        pmeGpu->settings.transferKind, nullptr);
 }
 
@@ -227,78 +244,56 @@ void pme_gpu_copy_output_forces(PmeGpu* pmeGpu)
     GMX_ASSERT(pmeGpu->kernelParams->atoms.nAtoms > 0, "Bad number of atoms in PME GPU");
     float* h_forcesFloat = reinterpret_cast<float*>(pmeGpu->staging.h_forces.data());
     copyFromDeviceBuffer(h_forcesFloat, &pmeGpu->kernelParams->atoms.d_forces, 0,
-                         DIM * pmeGpu->kernelParams->atoms.nAtoms, pmeGpu->archSpecific->pmeStream,
+                         DIM * pmeGpu->kernelParams->atoms.nAtoms, pmeGpu->archSpecific->pmeStream_,
                          pmeGpu->settings.transferKind, nullptr);
 }
 
-void pme_gpu_realloc_coordinates(const PmeGpu* pmeGpu)
-{
-    const size_t newCoordinatesSize = pmeGpu->nAtomsAlloc * DIM;
-    GMX_ASSERT(newCoordinatesSize > 0, "Bad number of atoms in PME GPU");
-    reallocateDeviceBuffer(&pmeGpu->kernelParams->atoms.d_coordinates, newCoordinatesSize,
-                           &pmeGpu->archSpecific->coordinatesSize,
-                           &pmeGpu->archSpecific->coordinatesSizeAlloc, pmeGpu->archSpecific->context);
-    if (c_usePadding)
-    {
-        const size_t paddingIndex = DIM * pmeGpu->kernelParams->atoms.nAtoms;
-        const size_t paddingCount = DIM * pmeGpu->nAtomsAlloc - paddingIndex;
-        if (paddingCount > 0)
-        {
-            clearDeviceBufferAsync(&pmeGpu->kernelParams->atoms.d_coordinates, paddingIndex,
-                                   paddingCount, pmeGpu->archSpecific->pmeStream);
-        }
-    }
-}
-
-void pme_gpu_free_coordinates(const PmeGpu* pmeGpu)
-{
-    freeDeviceBuffer(&pmeGpu->kernelParams->atoms.d_coordinates);
-}
-
-void pme_gpu_realloc_and_copy_input_coefficients(const PmeGpu* pmeGpu, const float* h_coefficients)
+void pme_gpu_realloc_and_copy_input_coefficients(const PmeGpu* pmeGpu,
+                                                 const float*  h_coefficients,
+                                                 const int     gridIndex)
 {
     GMX_ASSERT(h_coefficients, "Bad host-side charge buffer in PME GPU");
     const size_t newCoefficientsSize = pmeGpu->nAtomsAlloc;
     GMX_ASSERT(newCoefficientsSize > 0, "Bad number of atoms in PME GPU");
-    reallocateDeviceBuffer(&pmeGpu->kernelParams->atoms.d_coefficients, newCoefficientsSize,
-                           &pmeGpu->archSpecific->coefficientsSize,
-                           &pmeGpu->archSpecific->coefficientsSizeAlloc, pmeGpu->archSpecific->context);
-    copyToDeviceBuffer(&pmeGpu->kernelParams->atoms.d_coefficients,
+    reallocateDeviceBuffer(&pmeGpu->kernelParams->atoms.d_coefficients[gridIndex],
+                           newCoefficientsSize, &pmeGpu->archSpecific->coefficientsSize[gridIndex],
+                           &pmeGpu->archSpecific->coefficientsCapacity[gridIndex],
+                           pmeGpu->archSpecific->deviceContext_);
+    copyToDeviceBuffer(&pmeGpu->kernelParams->atoms.d_coefficients[gridIndex],
                        const_cast<float*>(h_coefficients), 0, pmeGpu->kernelParams->atoms.nAtoms,
-                       pmeGpu->archSpecific->pmeStream, pmeGpu->settings.transferKind, nullptr);
-    if (c_usePadding)
+                       pmeGpu->archSpecific->pmeStream_, pmeGpu->settings.transferKind, nullptr);
+
+    const size_t paddingIndex = pmeGpu->kernelParams->atoms.nAtoms;
+    const size_t paddingCount = pmeGpu->nAtomsAlloc - paddingIndex;
+    if (paddingCount > 0)
     {
-        const size_t paddingIndex = pmeGpu->kernelParams->atoms.nAtoms;
-        const size_t paddingCount = pmeGpu->nAtomsAlloc - paddingIndex;
-        if (paddingCount > 0)
-        {
-            clearDeviceBufferAsync(&pmeGpu->kernelParams->atoms.d_coefficients, paddingIndex,
-                                   paddingCount, pmeGpu->archSpecific->pmeStream);
-        }
+        clearDeviceBufferAsync(&pmeGpu->kernelParams->atoms.d_coefficients[gridIndex], paddingIndex,
+                               paddingCount, pmeGpu->archSpecific->pmeStream_);
     }
 }
 
 void pme_gpu_free_coefficients(const PmeGpu* pmeGpu)
 {
-    freeDeviceBuffer(&pmeGpu->kernelParams->atoms.d_coefficients);
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
+    {
+        freeDeviceBuffer(&pmeGpu->kernelParams->atoms.d_coefficients[gridIndex]);
+    }
 }
 
 void pme_gpu_realloc_spline_data(PmeGpu* pmeGpu)
 {
-    const int    order        = pmeGpu->common->pme_order;
-    const int    alignment    = pme_gpu_get_atoms_per_warp(pmeGpu);
-    const size_t nAtomsPadded = ((pmeGpu->nAtomsAlloc + alignment - 1) / alignment) * alignment;
-    const int    newSplineDataSize = DIM * order * nAtomsPadded;
+    const int order             = pmeGpu->common->pme_order;
+    const int newSplineDataSize = DIM * order * pmeGpu->nAtomsAlloc;
     GMX_ASSERT(newSplineDataSize > 0, "Bad number of atoms in PME GPU");
     /* Two arrays of the same size */
     const bool shouldRealloc        = (newSplineDataSize > pmeGpu->archSpecific->splineDataSize);
     int        currentSizeTemp      = pmeGpu->archSpecific->splineDataSize;
     int        currentSizeTempAlloc = pmeGpu->archSpecific->splineDataSizeAlloc;
-    reallocateDeviceBuffer(&pmeGpu->kernelParams->atoms.d_theta, newSplineDataSize,
-                           &currentSizeTemp, &currentSizeTempAlloc, pmeGpu->archSpecific->context);
+    reallocateDeviceBuffer(&pmeGpu->kernelParams->atoms.d_theta, newSplineDataSize, &currentSizeTemp,
+                           &currentSizeTempAlloc, pmeGpu->archSpecific->deviceContext_);
     reallocateDeviceBuffer(&pmeGpu->kernelParams->atoms.d_dtheta, newSplineDataSize,
-                           &pmeGpu->archSpecific->splineDataSize,
-                           &pmeGpu->archSpecific->splineDataSizeAlloc, pmeGpu->archSpecific->context);
+                           &pmeGpu->archSpecific->splineDataSize, &pmeGpu->archSpecific->splineDataSizeAlloc,
+                           pmeGpu->archSpecific->deviceContext_);
     // the host side reallocation
     if (shouldRealloc)
     {
@@ -324,7 +319,8 @@ void pme_gpu_realloc_grid_indices(PmeGpu* pmeGpu)
     GMX_ASSERT(newIndicesSize > 0, "Bad number of atoms in PME GPU");
     reallocateDeviceBuffer(&pmeGpu->kernelParams->atoms.d_gridlineIndices, newIndicesSize,
                            &pmeGpu->archSpecific->gridlineIndicesSize,
-                           &pmeGpu->archSpecific->gridlineIndicesSizeAlloc, pmeGpu->archSpecific->context);
+                           &pmeGpu->archSpecific->gridlineIndicesSizeAlloc,
+                           pmeGpu->archSpecific->deviceContext_);
     pfree(pmeGpu->staging.h_gridlineIndices);
     pmalloc(reinterpret_cast<void**>(&pmeGpu->staging.h_gridlineIndices), newIndicesSize * sizeof(int));
 }
@@ -337,50 +333,65 @@ void pme_gpu_free_grid_indices(const PmeGpu* pmeGpu)
 
 void pme_gpu_realloc_grids(PmeGpu* pmeGpu)
 {
-    auto*     kernelParamsPtr = pmeGpu->kernelParams.get();
+    auto* kernelParamsPtr = pmeGpu->kernelParams.get();
+
     const int newRealGridSize = kernelParamsPtr->grid.realGridSizePadded[XX]
                                 * kernelParamsPtr->grid.realGridSizePadded[YY]
                                 * kernelParamsPtr->grid.realGridSizePadded[ZZ];
     const int newComplexGridSize = kernelParamsPtr->grid.complexGridSizePadded[XX]
                                    * kernelParamsPtr->grid.complexGridSizePadded[YY]
                                    * kernelParamsPtr->grid.complexGridSizePadded[ZZ] * 2;
-    // Multiplied by 2 because we count complex grid size for complex numbers, but all allocations/pointers are float
-    if (pmeGpu->archSpecific->performOutOfPlaceFFT)
-    {
-        /* 2 separate grids */
-        reallocateDeviceBuffer(&kernelParamsPtr->grid.d_fourierGrid, newComplexGridSize,
-                               &pmeGpu->archSpecific->complexGridSize,
-                               &pmeGpu->archSpecific->complexGridSizeAlloc, pmeGpu->archSpecific->context);
-        reallocateDeviceBuffer(&kernelParamsPtr->grid.d_realGrid, newRealGridSize,
-                               &pmeGpu->archSpecific->realGridSize,
-                               &pmeGpu->archSpecific->realGridSizeAlloc, pmeGpu->archSpecific->context);
-    }
-    else
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
     {
-        /* A single buffer so that any grid will fit */
-        const int newGridsSize = std::max(newRealGridSize, newComplexGridSize);
-        reallocateDeviceBuffer(
-                &kernelParamsPtr->grid.d_realGrid, newGridsSize, &pmeGpu->archSpecific->realGridSize,
-                &pmeGpu->archSpecific->realGridSizeAlloc, pmeGpu->archSpecific->context);
-        kernelParamsPtr->grid.d_fourierGrid   = kernelParamsPtr->grid.d_realGrid;
-        pmeGpu->archSpecific->complexGridSize = pmeGpu->archSpecific->realGridSize;
-        // the size might get used later for copying the grid
+        // Multiplied by 2 because we count complex grid size for complex numbers, but all allocations/pointers are float
+        if (pmeGpu->archSpecific->performOutOfPlaceFFT)
+        {
+            /* 2 separate grids */
+            reallocateDeviceBuffer(&kernelParamsPtr->grid.d_fourierGrid[gridIndex], newComplexGridSize,
+                                   &pmeGpu->archSpecific->complexGridSize[gridIndex],
+                                   &pmeGpu->archSpecific->complexGridCapacity[gridIndex],
+                                   pmeGpu->archSpecific->deviceContext_);
+            reallocateDeviceBuffer(&kernelParamsPtr->grid.d_realGrid[gridIndex], newRealGridSize,
+                                   &pmeGpu->archSpecific->realGridSize[gridIndex],
+                                   &pmeGpu->archSpecific->realGridCapacity[gridIndex],
+                                   pmeGpu->archSpecific->deviceContext_);
+        }
+        else
+        {
+            /* A single buffer so that any grid will fit */
+            const int newGridsSize = std::max(newRealGridSize, newComplexGridSize);
+            reallocateDeviceBuffer(&kernelParamsPtr->grid.d_realGrid[gridIndex], newGridsSize,
+                                   &pmeGpu->archSpecific->realGridSize[gridIndex],
+                                   &pmeGpu->archSpecific->realGridCapacity[gridIndex],
+                                   pmeGpu->archSpecific->deviceContext_);
+            kernelParamsPtr->grid.d_fourierGrid[gridIndex] = kernelParamsPtr->grid.d_realGrid[gridIndex];
+            pmeGpu->archSpecific->complexGridSize[gridIndex] =
+                    pmeGpu->archSpecific->realGridSize[gridIndex];
+            // the size might get used later for copying the grid
+        }
     }
 }
 
 void pme_gpu_free_grids(const PmeGpu* pmeGpu)
 {
-    if (pmeGpu->archSpecific->performOutOfPlaceFFT)
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
     {
-        freeDeviceBuffer(&pmeGpu->kernelParams->grid.d_fourierGrid);
+        if (pmeGpu->archSpecific->performOutOfPlaceFFT)
+        {
+            freeDeviceBuffer(&pmeGpu->kernelParams->grid.d_fourierGrid[gridIndex]);
+        }
+        freeDeviceBuffer(&pmeGpu->kernelParams->grid.d_realGrid[gridIndex]);
     }
-    freeDeviceBuffer(&pmeGpu->kernelParams->grid.d_realGrid);
 }
 
 void pme_gpu_clear_grids(const PmeGpu* pmeGpu)
 {
-    clearDeviceBufferAsync(&pmeGpu->kernelParams->grid.d_realGrid, 0,
-                           pmeGpu->archSpecific->realGridSize, pmeGpu->archSpecific->pmeStream);
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
+    {
+        clearDeviceBufferAsync(&pmeGpu->kernelParams->grid.d_realGrid[gridIndex], 0,
+                               pmeGpu->archSpecific->realGridSize[gridIndex],
+                               pmeGpu->archSpecific->pmeStream_);
+    }
 }
 
 void pme_gpu_realloc_and_copy_fract_shifts(PmeGpu* pmeGpu)
@@ -399,37 +410,24 @@ void pme_gpu_realloc_and_copy_fract_shifts(PmeGpu* pmeGpu)
 
     const int newFractShiftsSize = cellCount * (nx + ny + nz);
 
-#if GMX_GPU == GMX_GPU_CUDA
-    initParamLookupTable(kernelParamsPtr->grid.d_fractShiftsTable, kernelParamsPtr->fractShiftsTableTexture,
-                         pmeGpu->common->fsh.data(), newFractShiftsSize);
-
-    initParamLookupTable(kernelParamsPtr->grid.d_gridlineIndicesTable,
-                         kernelParamsPtr->gridlineIndicesTableTexture, pmeGpu->common->nn.data(),
-                         newFractShiftsSize);
-#elif GMX_GPU == GMX_GPU_OPENCL
-    // No dedicated texture routines....
-    allocateDeviceBuffer(&kernelParamsPtr->grid.d_fractShiftsTable, newFractShiftsSize,
-                         pmeGpu->archSpecific->context);
-    allocateDeviceBuffer(&kernelParamsPtr->grid.d_gridlineIndicesTable, newFractShiftsSize,
-                         pmeGpu->archSpecific->context);
-    copyToDeviceBuffer(&kernelParamsPtr->grid.d_fractShiftsTable, pmeGpu->common->fsh.data(), 0,
-                       newFractShiftsSize, pmeGpu->archSpecific->pmeStream,
-                       GpuApiCallBehavior::Async, nullptr);
-    copyToDeviceBuffer(&kernelParamsPtr->grid.d_gridlineIndicesTable, pmeGpu->common->nn.data(), 0,
-                       newFractShiftsSize, pmeGpu->archSpecific->pmeStream,
-                       GpuApiCallBehavior::Async, nullptr);
-#endif
+    initParamLookupTable(&kernelParamsPtr->grid.d_fractShiftsTable,
+                         &kernelParamsPtr->fractShiftsTableTexture, pmeGpu->common->fsh.data(),
+                         newFractShiftsSize, pmeGpu->archSpecific->deviceContext_);
+
+    initParamLookupTable(&kernelParamsPtr->grid.d_gridlineIndicesTable,
+                         &kernelParamsPtr->gridlineIndicesTableTexture, pmeGpu->common->nn.data(),
+                         newFractShiftsSize, pmeGpu->archSpecific->deviceContext_);
 }
 
 void pme_gpu_free_fract_shifts(const PmeGpu* pmeGpu)
 {
     auto* kernelParamsPtr = pmeGpu->kernelParams.get();
-#if GMX_GPU == GMX_GPU_CUDA
-    destroyParamLookupTable(kernelParamsPtr->grid.d_fractShiftsTable,
+#if GMX_GPU_CUDA
+    destroyParamLookupTable(&kernelParamsPtr->grid.d_fractShiftsTable,
                             kernelParamsPtr->fractShiftsTableTexture);
-    destroyParamLookupTable(kernelParamsPtr->grid.d_gridlineIndicesTable,
+    destroyParamLookupTable(&kernelParamsPtr->grid.d_gridlineIndicesTable,
                             kernelParamsPtr->gridlineIndicesTableTexture);
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
     freeDeviceBuffer(&kernelParamsPtr->grid.d_fractShiftsTable);
     freeDeviceBuffer(&kernelParamsPtr->grid.d_gridlineIndicesTable);
 #endif
@@ -437,62 +435,58 @@ void pme_gpu_free_fract_shifts(const PmeGpu* pmeGpu)
 
 bool pme_gpu_stream_query(const PmeGpu* pmeGpu)
 {
-    return haveStreamTasksCompleted(pmeGpu->archSpecific->pmeStream);
+    return haveStreamTasksCompleted(pmeGpu->archSpecific->pmeStream_);
 }
 
-void pme_gpu_copy_input_gather_grid(const PmeGpu* pmeGpu, float* h_grid)
+void pme_gpu_copy_input_gather_grid(const PmeGpu* pmeGpu, const float* h_grid, const int gridIndex)
 {
-    copyToDeviceBuffer(&pmeGpu->kernelParams->grid.d_realGrid, h_grid, 0, pmeGpu->archSpecific->realGridSize,
-                       pmeGpu->archSpecific->pmeStream, pmeGpu->settings.transferKind, nullptr);
+    copyToDeviceBuffer(&pmeGpu->kernelParams->grid.d_realGrid[gridIndex], h_grid, 0,
+                       pmeGpu->archSpecific->realGridSize[gridIndex],
+                       pmeGpu->archSpecific->pmeStream_, pmeGpu->settings.transferKind, nullptr);
 }
 
-void pme_gpu_copy_output_spread_grid(const PmeGpu* pmeGpu, float* h_grid)
+void pme_gpu_copy_output_spread_grid(const PmeGpu* pmeGpu, float* h_grid, const int gridIndex)
 {
-    copyFromDeviceBuffer(h_grid, &pmeGpu->kernelParams->grid.d_realGrid, 0,
-                         pmeGpu->archSpecific->realGridSize, pmeGpu->archSpecific->pmeStream,
-                         pmeGpu->settings.transferKind, nullptr);
-    pmeGpu->archSpecific->syncSpreadGridD2H.markEvent(pmeGpu->archSpecific->pmeStream);
+    copyFromDeviceBuffer(h_grid, &pmeGpu->kernelParams->grid.d_realGrid[gridIndex], 0,
+                         pmeGpu->archSpecific->realGridSize[gridIndex],
+                         pmeGpu->archSpecific->pmeStream_, pmeGpu->settings.transferKind, nullptr);
+    pmeGpu->archSpecific->syncSpreadGridD2H.markEvent(pmeGpu->archSpecific->pmeStream_);
 }
 
 void pme_gpu_copy_output_spread_atom_data(const PmeGpu* pmeGpu)
 {
-    const int    alignment       = pme_gpu_get_atoms_per_warp(pmeGpu);
-    const size_t nAtomsPadded    = ((pmeGpu->nAtomsAlloc + alignment - 1) / alignment) * alignment;
-    const size_t splinesCount    = DIM * nAtomsPadded * pmeGpu->common->pme_order;
+    const size_t splinesCount    = DIM * pmeGpu->nAtomsAlloc * pmeGpu->common->pme_order;
     auto*        kernelParamsPtr = pmeGpu->kernelParams.get();
     copyFromDeviceBuffer(pmeGpu->staging.h_dtheta, &kernelParamsPtr->atoms.d_dtheta, 0, splinesCount,
-                         pmeGpu->archSpecific->pmeStream, pmeGpu->settings.transferKind, nullptr);
+                         pmeGpu->archSpecific->pmeStream_, pmeGpu->settings.transferKind, nullptr);
     copyFromDeviceBuffer(pmeGpu->staging.h_theta, &kernelParamsPtr->atoms.d_theta, 0, splinesCount,
-                         pmeGpu->archSpecific->pmeStream, pmeGpu->settings.transferKind, nullptr);
+                         pmeGpu->archSpecific->pmeStream_, pmeGpu->settings.transferKind, nullptr);
     copyFromDeviceBuffer(pmeGpu->staging.h_gridlineIndices, &kernelParamsPtr->atoms.d_gridlineIndices,
-                         0, kernelParamsPtr->atoms.nAtoms * DIM, pmeGpu->archSpecific->pmeStream,
+                         0, kernelParamsPtr->atoms.nAtoms * DIM, pmeGpu->archSpecific->pmeStream_,
                          pmeGpu->settings.transferKind, nullptr);
 }
 
 void pme_gpu_copy_input_gather_atom_data(const PmeGpu* pmeGpu)
 {
-    const int    alignment       = pme_gpu_get_atoms_per_warp(pmeGpu);
-    const size_t nAtomsPadded    = ((pmeGpu->nAtomsAlloc + alignment - 1) / alignment) * alignment;
-    const size_t splinesCount    = DIM * nAtomsPadded * pmeGpu->common->pme_order;
+    const size_t splinesCount    = DIM * pmeGpu->nAtomsAlloc * pmeGpu->common->pme_order;
     auto*        kernelParamsPtr = pmeGpu->kernelParams.get();
-    if (c_usePadding)
-    {
-        // TODO: could clear only the padding and not the whole thing, but this is a test-exclusive code anyway
-        clearDeviceBufferAsync(&kernelParamsPtr->atoms.d_gridlineIndices, 0,
-                               pmeGpu->nAtomsAlloc * DIM, pmeGpu->archSpecific->pmeStream);
-        clearDeviceBufferAsync(&kernelParamsPtr->atoms.d_dtheta, 0,
-                               pmeGpu->nAtomsAlloc * pmeGpu->common->pme_order * DIM,
-                               pmeGpu->archSpecific->pmeStream);
-        clearDeviceBufferAsync(&kernelParamsPtr->atoms.d_theta, 0,
-                               pmeGpu->nAtomsAlloc * pmeGpu->common->pme_order * DIM,
-                               pmeGpu->archSpecific->pmeStream);
-    }
+
+    // TODO: could clear only the padding and not the whole thing, but this is a test-exclusive code anyway
+    clearDeviceBufferAsync(&kernelParamsPtr->atoms.d_gridlineIndices, 0, pmeGpu->nAtomsAlloc * DIM,
+                           pmeGpu->archSpecific->pmeStream_);
+    clearDeviceBufferAsync(&kernelParamsPtr->atoms.d_dtheta, 0,
+                           pmeGpu->nAtomsAlloc * pmeGpu->common->pme_order * DIM,
+                           pmeGpu->archSpecific->pmeStream_);
+    clearDeviceBufferAsync(&kernelParamsPtr->atoms.d_theta, 0,
+                           pmeGpu->nAtomsAlloc * pmeGpu->common->pme_order * DIM,
+                           pmeGpu->archSpecific->pmeStream_);
+
     copyToDeviceBuffer(&kernelParamsPtr->atoms.d_dtheta, pmeGpu->staging.h_dtheta, 0, splinesCount,
-                       pmeGpu->archSpecific->pmeStream, pmeGpu->settings.transferKind, nullptr);
+                       pmeGpu->archSpecific->pmeStream_, pmeGpu->settings.transferKind, nullptr);
     copyToDeviceBuffer(&kernelParamsPtr->atoms.d_theta, pmeGpu->staging.h_theta, 0, splinesCount,
-                       pmeGpu->archSpecific->pmeStream, pmeGpu->settings.transferKind, nullptr);
+                       pmeGpu->archSpecific->pmeStream_, pmeGpu->settings.transferKind, nullptr);
     copyToDeviceBuffer(&kernelParamsPtr->atoms.d_gridlineIndices, pmeGpu->staging.h_gridlineIndices,
-                       0, kernelParamsPtr->atoms.nAtoms * DIM, pmeGpu->archSpecific->pmeStream,
+                       0, kernelParamsPtr->atoms.nAtoms * DIM, pmeGpu->archSpecific->pmeStream_,
                        pmeGpu->settings.transferKind, nullptr);
 }
 
@@ -501,16 +495,16 @@ void pme_gpu_sync_spread_grid(const PmeGpu* pmeGpu)
     pmeGpu->archSpecific->syncSpreadGridD2H.waitForEvent();
 }
 
-void pme_gpu_init_internal(PmeGpu* pmeGpu)
+/*! \brief Internal GPU initialization for PME.
+ *
+ * \param[in]  pmeGpu         GPU PME data.
+ * \param[in]  deviceContext  GPU context.
+ * \param[in]  deviceStream   GPU stream.
+ */
+static void pme_gpu_init_internal(PmeGpu* pmeGpu, const DeviceContext& deviceContext, const DeviceStream& deviceStream)
 {
-#if GMX_GPU == GMX_GPU_CUDA
-    // Prepare to use the device that this PME task was assigned earlier.
-    // Other entities, such as CUDA timing events, are known to implicitly use the device context.
-    CU_RET_ERR(cudaSetDevice(pmeGpu->deviceInfo->id), "Switching to PME CUDA device");
-#endif
-
     /* Allocate the target-specific structures */
-    pmeGpu->archSpecific.reset(new PmeGpuSpecific());
+    pmeGpu->archSpecific.reset(new PmeGpuSpecific(deviceContext, deviceStream));
     pmeGpu->kernelParams.reset(new PmeGpuKernelParams());
 
     pmeGpu->archSpecific->performOutOfPlaceFFT = true;
@@ -519,80 +513,23 @@ void pme_gpu_init_internal(PmeGpu* pmeGpu)
      * TODO: PME could also try to pick up nice grid sizes (with factors of 2, 3, 5, 7).
      */
 
-    // TODO: this is just a convenient reuse because programHandle_ currently is in charge of creating context
-    pmeGpu->archSpecific->context = pmeGpu->programHandle_->impl_->context;
-
-    // timing enabling - TODO put this in gpu_utils (even though generally this is just option handling?) and reuse in NB
-    if (GMX_GPU == GMX_GPU_CUDA)
-    {
-        /* WARNING: CUDA timings are incorrect with multiple streams.
-         *          This is the main reason why they are disabled by default.
-         */
-        // TODO: Consider turning on by default when we can detect nr of streams.
-        pmeGpu->archSpecific->useTiming = (getenv("GMX_ENABLE_GPU_TIMING") != nullptr);
-    }
-    else if (GMX_GPU == GMX_GPU_OPENCL)
-    {
-        pmeGpu->archSpecific->useTiming = (getenv("GMX_DISABLE_GPU_TIMING") == nullptr);
-    }
-
-#if GMX_GPU == GMX_GPU_CUDA
-    pmeGpu->maxGridWidthX = pmeGpu->deviceInfo->prop.maxGridSize[0];
-#elif GMX_GPU == GMX_GPU_OPENCL
-    pmeGpu->maxGridWidthX = INT32_MAX / 2;
+#if GMX_GPU_CUDA
+    pmeGpu->maxGridWidthX = deviceContext.deviceInfo().prop.maxGridSize[0];
+#else
+    // Use this path for any non-CUDA GPU acceleration
     // TODO: is there no really global work size limit in OpenCL?
-#endif
-
-    /* Creating a PME GPU stream:
-     * - default high priority with CUDA
-     * - no priorities implemented yet with OpenCL; see #2532
-     */
-#if GMX_GPU == GMX_GPU_CUDA
-    cudaError_t stat;
-    int         highest_priority, lowest_priority;
-    stat = cudaDeviceGetStreamPriorityRange(&lowest_priority, &highest_priority);
-    CU_RET_ERR(stat, "PME cudaDeviceGetStreamPriorityRange failed");
-    stat = cudaStreamCreateWithPriority(&pmeGpu->archSpecific->pmeStream,
-                                        cudaStreamDefault, // cudaStreamNonBlocking,
-                                        highest_priority);
-    CU_RET_ERR(stat, "cudaStreamCreateWithPriority on the PME stream failed");
-#elif GMX_GPU == GMX_GPU_OPENCL
-    cl_command_queue_properties queueProperties =
-            pmeGpu->archSpecific->useTiming ? CL_QUEUE_PROFILING_ENABLE : 0;
-    cl_device_id device_id = pmeGpu->deviceInfo->ocl_gpu_id.ocl_device_id;
-    cl_int       clError;
-    pmeGpu->archSpecific->pmeStream =
-            clCreateCommandQueue(pmeGpu->archSpecific->context, device_id, queueProperties, &clError);
-    if (clError != CL_SUCCESS)
-    {
-        GMX_THROW(gmx::InternalError("Failed to create PME command queue"));
-    }
-#endif
-}
-
-void pme_gpu_destroy_specific(const PmeGpu* pmeGpu)
-{
-#if GMX_GPU == GMX_GPU_CUDA
-    /* Destroy the CUDA stream */
-    cudaError_t stat = cudaStreamDestroy(pmeGpu->archSpecific->pmeStream);
-    CU_RET_ERR(stat, "PME cudaStreamDestroy error");
-#elif GMX_GPU == GMX_GPU_OPENCL
-    cl_int clError = clReleaseCommandQueue(pmeGpu->archSpecific->pmeStream);
-    if (clError != CL_SUCCESS)
-    {
-        gmx_warning("Failed to destroy PME command queue");
-    }
+    pmeGpu->maxGridWidthX = INT32_MAX / 2;
 #endif
 }
 
 void pme_gpu_reinit_3dfft(const PmeGpu* pmeGpu)
 {
-    if (pme_gpu_performs_FFT(pmeGpu))
+    if (pme_gpu_settings(pmeGpu).performGPUFFT)
     {
         pmeGpu->archSpecific->fftSetup.resize(0);
-        for (int i = 0; i < pmeGpu->common->ngrids; i++)
+        for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
         {
-            pmeGpu->archSpecific->fftSetup.push_back(std::make_unique<GpuParallel3dFft>(pmeGpu));
+            pmeGpu->archSpecific->fftSetup.push_back(std::make_unique<GpuParallel3dFft>(pmeGpu, gridIndex));
         }
     }
 }
@@ -602,69 +539,62 @@ void pme_gpu_destroy_3dfft(const PmeGpu* pmeGpu)
     pmeGpu->archSpecific->fftSetup.resize(0);
 }
 
-int getSplineParamFullIndex(int order, int splineIndex, int dimIndex, int atomIndex, int atomsPerWarp)
+void pme_gpu_getEnergyAndVirial(const gmx_pme_t& pme, const float lambda, PmeOutput* output)
 {
-    if (order != c_pmeGpuOrder)
+    const PmeGpu* pmeGpu = pme.gpu;
+
+    GMX_ASSERT(lambda == 1.0 || pmeGpu->common->ngrids == 2,
+               "Invalid combination of lambda and number of grids");
+
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
     {
-        throw order;
+        for (int j = 0; j < c_virialAndEnergyCount; j++)
+        {
+            GMX_ASSERT(std::isfinite(pmeGpu->staging.h_virialAndEnergy[gridIndex][j]),
+                       "PME GPU produces incorrect energy/virial.");
+        }
     }
-    constexpr int fixedOrder = c_pmeGpuOrder;
-    GMX_UNUSED_VALUE(fixedOrder);
-
-    const int atomWarpIndex = atomIndex % atomsPerWarp;
-    const int warpIndex     = atomIndex / atomsPerWarp;
-    int       indexBase, result;
-    switch (atomsPerWarp)
+    for (int dim1 = 0; dim1 < DIM; dim1++)
     {
-        case 1:
-            indexBase = getSplineParamIndexBase<fixedOrder, 1>(warpIndex, atomWarpIndex);
-            result    = getSplineParamIndex<fixedOrder, 1>(indexBase, dimIndex, splineIndex);
-            break;
-
-        case 2:
-            indexBase = getSplineParamIndexBase<fixedOrder, 2>(warpIndex, atomWarpIndex);
-            result    = getSplineParamIndex<fixedOrder, 2>(indexBase, dimIndex, splineIndex);
-            break;
-
-        case 4:
-            indexBase = getSplineParamIndexBase<fixedOrder, 4>(warpIndex, atomWarpIndex);
-            result    = getSplineParamIndex<fixedOrder, 4>(indexBase, dimIndex, splineIndex);
-            break;
-
-        case 8:
-            indexBase = getSplineParamIndexBase<fixedOrder, 8>(warpIndex, atomWarpIndex);
-            result    = getSplineParamIndex<fixedOrder, 8>(indexBase, dimIndex, splineIndex);
-            break;
-
-        default:
-            GMX_THROW(gmx::NotImplementedError(
-                    gmx::formatString("Test function call not unrolled for atomsPerWarp = %d in "
-                                      "getSplineParamFullIndex",
-                                      atomsPerWarp)));
+        for (int dim2 = 0; dim2 < DIM; dim2++)
+        {
+            output->coulombVirial_[dim1][dim2] = 0;
+        }
+    }
+    output->coulombEnergy_ = 0;
+    float scale            = 1.0;
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
+    {
+        if (pmeGpu->common->ngrids == 2)
+        {
+            scale = gridIndex == 0 ? (1.0 - lambda) : lambda;
+        }
+        output->coulombVirial_[XX][XX] +=
+                scale * 0.25F * pmeGpu->staging.h_virialAndEnergy[gridIndex][0];
+        output->coulombVirial_[YY][YY] +=
+                scale * 0.25F * pmeGpu->staging.h_virialAndEnergy[gridIndex][1];
+        output->coulombVirial_[ZZ][ZZ] +=
+                scale * 0.25F * pmeGpu->staging.h_virialAndEnergy[gridIndex][2];
+        output->coulombVirial_[XX][YY] +=
+                scale * 0.25F * pmeGpu->staging.h_virialAndEnergy[gridIndex][3];
+        output->coulombVirial_[YY][XX] +=
+                scale * 0.25F * pmeGpu->staging.h_virialAndEnergy[gridIndex][3];
+        output->coulombVirial_[XX][ZZ] +=
+                scale * 0.25F * pmeGpu->staging.h_virialAndEnergy[gridIndex][4];
+        output->coulombVirial_[ZZ][XX] +=
+                scale * 0.25F * pmeGpu->staging.h_virialAndEnergy[gridIndex][4];
+        output->coulombVirial_[YY][ZZ] +=
+                scale * 0.25F * pmeGpu->staging.h_virialAndEnergy[gridIndex][5];
+        output->coulombVirial_[ZZ][YY] +=
+                scale * 0.25F * pmeGpu->staging.h_virialAndEnergy[gridIndex][5];
+        output->coulombEnergy_ += scale * 0.5F * pmeGpu->staging.h_virialAndEnergy[gridIndex][6];
+    }
+    if (pmeGpu->common->ngrids > 1)
+    {
+        output->coulombDvdl_ = 0.5F
+                               * (pmeGpu->staging.h_virialAndEnergy[FEP_STATE_B][6]
+                                  - pmeGpu->staging.h_virialAndEnergy[FEP_STATE_A][6]);
     }
-    return result;
-}
-
-void pme_gpu_getEnergyAndVirial(const gmx_pme_t& pme, PmeOutput* output)
-{
-    const PmeGpu* pmeGpu = pme.gpu;
-    for (int j = 0; j < c_virialAndEnergyCount; j++)
-    {
-        GMX_ASSERT(std::isfinite(pmeGpu->staging.h_virialAndEnergy[j]),
-                   "PME GPU produces incorrect energy/virial.");
-    }
-
-    unsigned int j                 = 0;
-    output->coulombVirial_[XX][XX] = 0.25F * pmeGpu->staging.h_virialAndEnergy[j++];
-    output->coulombVirial_[YY][YY] = 0.25F * pmeGpu->staging.h_virialAndEnergy[j++];
-    output->coulombVirial_[ZZ][ZZ] = 0.25F * pmeGpu->staging.h_virialAndEnergy[j++];
-    output->coulombVirial_[XX][YY] = output->coulombVirial_[YY][XX] =
-            0.25F * pmeGpu->staging.h_virialAndEnergy[j++];
-    output->coulombVirial_[XX][ZZ] = output->coulombVirial_[ZZ][XX] =
-            0.25F * pmeGpu->staging.h_virialAndEnergy[j++];
-    output->coulombVirial_[YY][ZZ] = output->coulombVirial_[ZZ][YY] =
-            0.25F * pmeGpu->staging.h_virialAndEnergy[j++];
-    output->coulombEnergy_ = 0.5F * pmeGpu->staging.h_virialAndEnergy[j++];
 }
 
 /*! \brief Sets the force-related members in \p output
@@ -681,22 +611,19 @@ static void pme_gpu_getForceOutput(PmeGpu* pmeGpu, PmeOutput* output)
     }
 }
 
-PmeOutput pme_gpu_getOutput(const gmx_pme_t& pme, const int flags)
+PmeOutput pme_gpu_getOutput(const gmx_pme_t& pme, const bool computeEnergyAndVirial, const real lambdaQ)
 {
-    PmeGpu*    pmeGpu                      = pme.gpu;
-    const bool haveComputedEnergyAndVirial = (flags & GMX_PME_CALC_ENER_VIR) != 0;
+    PmeGpu* pmeGpu = pme.gpu;
 
     PmeOutput output;
 
     pme_gpu_getForceOutput(pmeGpu, &output);
 
-    // The caller knows from the flags that the energy and the virial are not usable
-    // on the else branch
-    if (haveComputedEnergyAndVirial)
+    if (computeEnergyAndVirial)
     {
-        if (pme_gpu_performs_solve(pmeGpu))
+        if (pme_gpu_settings(pmeGpu).performGPUSolve)
         {
-            pme_gpu_getEnergyAndVirial(pme, &output);
+            pme_gpu_getEnergyAndVirial(pme, lambdaQ, &output);
         }
         else
         {
@@ -738,9 +665,13 @@ void pme_gpu_update_input_box(PmeGpu gmx_unused* pmeGpu, const matrix gmx_unused
 static void pme_gpu_reinit_grids(PmeGpu* pmeGpu)
 {
     auto* kernelParamsPtr = pme_gpu_get_kernel_params_base_ptr(pmeGpu);
+
+    GMX_ASSERT(
+            pmeGpu->common->ngrids == 1 || pmeGpu->common->ngrids == 2,
+            "Only one (normal Coulomb PME) or two (FEP coulomb PME) PME grids can be used on GPU");
+
     kernelParamsPtr->grid.ewaldFactor =
             (M_PI * M_PI) / (pmeGpu->common->ewaldcoeff_q * pmeGpu->common->ewaldcoeff_q);
-
     /* The grid size variants */
     for (int i = 0; i < DIM; i++)
     {
@@ -755,20 +686,23 @@ static void pme_gpu_reinit_grids(PmeGpu* pmeGpu)
         kernelParamsPtr->grid.complexGridSizePadded[i] = kernelParamsPtr->grid.realGridSize[i];
     }
     /* FFT: n real elements correspond to (n / 2 + 1) complex elements in minor dimension */
-    if (!pme_gpu_performs_FFT(pmeGpu))
+    if (!pme_gpu_settings(pmeGpu).performGPUFFT)
     {
         // This allows for GPU spreading grid and CPU fftgrid to have the same layout, so that we can copy the data directly
         kernelParamsPtr->grid.realGridSizePadded[ZZ] =
                 (kernelParamsPtr->grid.realGridSize[ZZ] / 2 + 1) * 2;
     }
-
     /* GPU FFT: n real elements correspond to (n / 2 + 1) complex elements in minor dimension */
     kernelParamsPtr->grid.complexGridSize[ZZ] /= 2;
     kernelParamsPtr->grid.complexGridSize[ZZ]++;
     kernelParamsPtr->grid.complexGridSizePadded[ZZ] = kernelParamsPtr->grid.complexGridSize[ZZ];
 
     pme_gpu_realloc_and_copy_fract_shifts(pmeGpu);
-    pme_gpu_realloc_and_copy_bspline_values(pmeGpu);
+    for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
+    {
+        pme_gpu_realloc_and_copy_bspline_values(pmeGpu, gridIndex);
+    }
+
     pme_gpu_realloc_grids(pmeGpu);
     pme_gpu_reinit_3dfft(pmeGpu);
 }
@@ -789,7 +723,7 @@ static void pme_gpu_copy_common_data_from(const gmx_pme_t* pme)
     /* TODO: Consider refactoring the CPU PME code to use the same structure,
      * so that this function becomes 2 lines */
     PmeGpu* pmeGpu               = pme->gpu;
-    pmeGpu->common->ngrids       = pme->ngrids;
+    pmeGpu->common->ngrids       = pme->bFEP_q ? 2 : 1;
     pmeGpu->common->epsilon_r    = pme->epsilon_r;
     pmeGpu->common->ewaldcoeff_q = pme->ewaldcoeff_q;
     pmeGpu->common->nk[XX]       = pme->nkx;
@@ -825,15 +759,15 @@ static void pme_gpu_copy_common_data_from(const gmx_pme_t* pme)
  */
 static void pme_gpu_select_best_performing_pme_spreadgather_kernels(PmeGpu* pmeGpu)
 {
-    if (pmeGpu->kernelParams->atoms.nAtoms > c_pmeGpuPerformanceAtomLimit && (GMX_GPU == GMX_GPU_CUDA))
+    if (GMX_GPU_CUDA && pmeGpu->kernelParams->atoms.nAtoms > c_pmeGpuPerformanceAtomLimit)
     {
-        pmeGpu->settings.useOrderThreadsPerAtom = true;
-        pmeGpu->settings.recalculateSplines     = true;
+        pmeGpu->settings.threadsPerAtom     = ThreadsPerAtom::Order;
+        pmeGpu->settings.recalculateSplines = true;
     }
     else
     {
-        pmeGpu->settings.useOrderThreadsPerAtom = false;
-        pmeGpu->settings.recalculateSplines     = false;
+        pmeGpu->settings.threadsPerAtom     = ThreadsPerAtom::OrderSquared;
+        pmeGpu->settings.recalculateSplines = false;
     }
 }
 
@@ -843,10 +777,14 @@ static void pme_gpu_select_best_performing_pme_spreadgather_kernels(PmeGpu* pmeG
  * TODO: this should become PmeGpu::PmeGpu()
  *
  * \param[in,out] pme            The PME structure.
- * \param[in,out] gpuInfo        The GPU information structure.
- * \param[in]     pmeGpuProgram  The handle to the program/kernel data created outside (e.g. in unit tests/runner)
+ * \param[in]     deviceContext  The GPU context.
+ * \param[in]     deviceStream   The GPU stream.
+ * \param[in,out] pmeGpuProgram  The handle to the program/kernel data created outside (e.g. in unit tests/runner)
  */
-static void pme_gpu_init(gmx_pme_t* pme, const gmx_device_info_t* gpuInfo, PmeGpuProgramHandle pmeGpuProgram)
+static void pme_gpu_init(gmx_pme_t*           pme,
+                         const DeviceContext& deviceContext,
+                         const DeviceStream&  deviceStream,
+                         const PmeGpuProgram* pmeGpuProgram)
 {
     pme->gpu       = new PmeGpu();
     PmeGpu* pmeGpu = pme->gpu;
@@ -855,7 +793,7 @@ static void pme_gpu_init(gmx_pme_t* pme, const gmx_device_info_t* gpuInfo, PmeGp
 
     /* These settings are set here for the whole run; dynamic ones are set in pme_gpu_reinit() */
     /* A convenience variable. */
-    pmeGpu->settings.useDecomposition = (pme->nnodes == 1);
+    pmeGpu->settings.useDecomposition = (pme->nnodes != 1);
     /* TODO: CPU gather with GPU spread is broken due to different theta/dtheta layout. */
     pmeGpu->settings.performGPUGather = true;
     // By default GPU-side reduction is off (explicitly set here for tests, otherwise reset per-step)
@@ -863,16 +801,15 @@ static void pme_gpu_init(gmx_pme_t* pme, const gmx_device_info_t* gpuInfo, PmeGp
 
     pme_gpu_set_testing(pmeGpu, false);
 
-    pmeGpu->deviceInfo = gpuInfo;
     GMX_ASSERT(pmeGpuProgram != nullptr, "GPU kernels must be already compiled");
     pmeGpu->programHandle_ = pmeGpuProgram;
 
     pmeGpu->initializedClfftLibrary_ = std::make_unique<gmx::ClfftInitializer>();
 
-    pme_gpu_init_internal(pmeGpu);
-    pme_gpu_alloc_energy_virial(pmeGpu);
+    pme_gpu_init_internal(pmeGpu, deviceContext, deviceStream);
 
     pme_gpu_copy_common_data_from(pme);
+    pme_gpu_alloc_energy_virial(pmeGpu);
 
     GMX_ASSERT(pmeGpu->common->epsilon_r != 0.0F, "PME GPU: bad electrostatic coefficient");
 
@@ -880,67 +817,6 @@ static void pme_gpu_init(gmx_pme_t* pme, const gmx_device_info_t* gpuInfo, PmeGp
     kernelParamsPtr->constants.elFactor = ONE_4PI_EPS0 / pmeGpu->common->epsilon_r;
 }
 
-void pme_gpu_transform_spline_atom_data(const PmeGpu*      pmeGpu,
-                                        const PmeAtomComm* atc,
-                                        PmeSplineDataType  type,
-                                        int                dimIndex,
-                                        PmeLayoutTransform transform)
-{
-    // The GPU atom spline data is laid out in a different way currently than the CPU one.
-    // This function converts the data from GPU to CPU layout (in the host memory).
-    // It is only intended for testing purposes so far.
-    // Ideally we should use similar layouts on CPU and GPU if we care about mixed modes and their
-    // performance (e.g. spreading on GPU, gathering on CPU).
-    GMX_RELEASE_ASSERT(atc->nthread == 1, "Only the serial PME data layout is supported");
-    const uintmax_t threadIndex  = 0;
-    const auto      atomCount    = pme_gpu_get_kernel_params_base_ptr(pmeGpu)->atoms.nAtoms;
-    const auto      atomsPerWarp = pme_gpu_get_atoms_per_warp(pmeGpu);
-    const auto      pmeOrder     = pmeGpu->common->pme_order;
-    GMX_ASSERT(pmeOrder == c_pmeGpuOrder, "Only PME order 4 is implemented");
-
-    real*  cpuSplineBuffer;
-    float* h_splineBuffer;
-    switch (type)
-    {
-        case PmeSplineDataType::Values:
-            cpuSplineBuffer = atc->spline[threadIndex].theta.coefficients[dimIndex];
-            h_splineBuffer  = pmeGpu->staging.h_theta;
-            break;
-
-        case PmeSplineDataType::Derivatives:
-            cpuSplineBuffer = atc->spline[threadIndex].dtheta.coefficients[dimIndex];
-            h_splineBuffer  = pmeGpu->staging.h_dtheta;
-            break;
-
-        default: GMX_THROW(gmx::InternalError("Unknown spline data type"));
-    }
-
-    for (auto atomIndex = 0; atomIndex < atomCount; atomIndex++)
-    {
-        for (auto orderIndex = 0; orderIndex < pmeOrder; orderIndex++)
-        {
-            const auto gpuValueIndex =
-                    getSplineParamFullIndex(pmeOrder, orderIndex, dimIndex, atomIndex, atomsPerWarp);
-            const auto cpuValueIndex = atomIndex * pmeOrder + orderIndex;
-            GMX_ASSERT(cpuValueIndex < atomCount * pmeOrder,
-                       "Atom spline data index out of bounds (while transforming GPU data layout "
-                       "for host)");
-            switch (transform)
-            {
-                case PmeLayoutTransform::GpuToHost:
-                    cpuSplineBuffer[cpuValueIndex] = h_splineBuffer[gpuValueIndex];
-                    break;
-
-                case PmeLayoutTransform::HostToGpu:
-                    h_splineBuffer[gpuValueIndex] = cpuSplineBuffer[cpuValueIndex];
-                    break;
-
-                default: GMX_THROW(gmx::InternalError("Unknown layout transform"));
-            }
-        }
-    }
-}
-
 void pme_gpu_get_real_grid_sizes(const PmeGpu* pmeGpu, gmx::IVec* gridSize, gmx::IVec* paddedGridSize)
 {
     GMX_ASSERT(gridSize != nullptr, "");
@@ -954,17 +830,21 @@ void pme_gpu_get_real_grid_sizes(const PmeGpu* pmeGpu, gmx::IVec* gridSize, gmx:
     }
 }
 
-void pme_gpu_reinit(gmx_pme_t* pme, const gmx_device_info_t* gpuInfo, PmeGpuProgramHandle pmeGpuProgram)
+void pme_gpu_reinit(gmx_pme_t*           pme,
+                    const DeviceContext* deviceContext,
+                    const DeviceStream*  deviceStream,
+                    const PmeGpuProgram* pmeGpuProgram)
 {
-    if (!pme_gpu_active(pme))
-    {
-        return;
-    }
+    GMX_ASSERT(pme != nullptr, "Need valid PME object");
 
     if (!pme->gpu)
     {
+        GMX_RELEASE_ASSERT(deviceContext != nullptr,
+                           "Device context can not be nullptr when setting up PME on GPU.");
+        GMX_RELEASE_ASSERT(deviceStream != nullptr,
+                           "Device stream can not be nullptr when setting up PME on GPU.");
         /* First-time initialization */
-        pme_gpu_init(pme, gpuInfo, pmeGpuProgram);
+        pme_gpu_init(pme, *deviceContext, *deviceStream, pmeGpuProgram);
     }
     else
     {
@@ -973,7 +853,7 @@ void pme_gpu_reinit(gmx_pme_t* pme, const gmx_device_info_t* gpuInfo, PmeGpuProg
     }
     /* GPU FFT will only get used for a single rank.*/
     pme->gpu->settings.performGPUFFT =
-            (pme->gpu->common->runMode == PmeRunMode::GPU) && !pme_gpu_uses_dd(pme->gpu);
+            (pme->gpu->common->runMode == PmeRunMode::GPU) && !pme->gpu->settings.useDecomposition;
     pme->gpu->settings.performGPUSolve = (pme->gpu->common->runMode == PmeRunMode::GPU);
 
     /* Reinit active timers */
@@ -1003,29 +883,41 @@ void pme_gpu_destroy(PmeGpu* pmeGpu)
 
     pme_gpu_destroy_3dfft(pmeGpu);
 
-    /* Free the GPU-framework specific data last */
-    pme_gpu_destroy_specific(pmeGpu);
-
     delete pmeGpu;
 }
 
-void pme_gpu_reinit_atoms(PmeGpu* pmeGpu, const int nAtoms, const real* charges)
+void pme_gpu_reinit_atoms(PmeGpu* pmeGpu, const int nAtoms, const real* chargesA, const real* chargesB)
 {
     auto* kernelParamsPtr         = pme_gpu_get_kernel_params_base_ptr(pmeGpu);
     kernelParamsPtr->atoms.nAtoms = nAtoms;
-    const int alignment           = pme_gpu_get_atom_data_alignment(pmeGpu);
-    pmeGpu->nAtomsPadded          = ((nAtoms + alignment - 1) / alignment) * alignment;
-    const int  nAtomsAlloc        = c_usePadding ? pmeGpu->nAtomsPadded : nAtoms;
-    const bool haveToRealloc =
-            (pmeGpu->nAtomsAlloc < nAtomsAlloc); /* This check might be redundant, but is logical */
-    pmeGpu->nAtomsAlloc = nAtomsAlloc;
+    const int  block_size         = pme_gpu_get_atom_data_block_size();
+    const int  nAtomsNewPadded    = ((nAtoms + block_size - 1) / block_size) * block_size;
+    const bool haveToRealloc      = (pmeGpu->nAtomsAlloc < nAtomsNewPadded);
+    pmeGpu->nAtomsAlloc           = nAtomsNewPadded;
 
 #if GMX_DOUBLE
     GMX_RELEASE_ASSERT(false, "Only single precision supported");
     GMX_UNUSED_VALUE(charges);
 #else
-    pme_gpu_realloc_and_copy_input_coefficients(pmeGpu, reinterpret_cast<const float*>(charges));
+    int gridIndex = 0;
     /* Could also be checked for haveToRealloc, but the copy always needs to be performed */
+    pme_gpu_realloc_and_copy_input_coefficients(pmeGpu, reinterpret_cast<const float*>(chargesA), gridIndex);
+    gridIndex++;
+    if (chargesB != nullptr)
+    {
+        pme_gpu_realloc_and_copy_input_coefficients(
+                pmeGpu, reinterpret_cast<const float*>(chargesB), gridIndex);
+    }
+    else
+    {
+        /* Fill the second set of coefficients with chargesA as well to be able to avoid
+         * conditionals in the GPU kernels */
+        /* FIXME: This should be avoided by making a separate templated version of the
+         * relevant kernel(s) (probably only pme_gather_kernel). That would require a
+         * reduction of the current number of templated parameters of that kernel. */
+        pme_gpu_realloc_and_copy_input_coefficients(
+                pmeGpu, reinterpret_cast<const float*>(chargesA), gridIndex);
+    }
 #endif
 
     if (haveToRealloc)
@@ -1037,7 +929,26 @@ void pme_gpu_reinit_atoms(PmeGpu* pmeGpu, const int nAtoms, const real* charges)
     pme_gpu_select_best_performing_pme_spreadgather_kernels(pmeGpu);
 }
 
-void pme_gpu_3dfft(const PmeGpu* pmeGpu, gmx_fft_direction dir, int grid_index)
+/*! \internal \brief
+ * Returns raw timing event from the corresponding GpuRegionTimer (if timings are enabled).
+ * In CUDA result can be nullptr stub, per GpuRegionTimer implementation.
+ *
+ * \param[in] pmeGpu         The PME GPU data structure.
+ * \param[in] PMEStageId     The PME GPU stage gtPME_ index from the enum in src/gromacs/timing/gpu_timing.h
+ */
+static CommandEvent* pme_gpu_fetch_timing_event(const PmeGpu* pmeGpu, size_t PMEStageId)
+{
+    CommandEvent* timingEvent = nullptr;
+    if (pme_gpu_timings_enabled(pmeGpu))
+    {
+        GMX_ASSERT(PMEStageId < pmeGpu->archSpecific->timingEvents.size(),
+                   "Wrong PME GPU timing event index");
+        timingEvent = pmeGpu->archSpecific->timingEvents[PMEStageId].fetchNextEvent();
+    }
+    return timingEvent;
+}
+
+void pme_gpu_3dfft(const PmeGpu* pmeGpu, gmx_fft_direction dir, const int grid_index)
 {
     int timerId = (dir == GMX_FFT_REAL_TO_COMPLEX) ? gtPME_FFT_R2C : gtPME_FFT_C2R;
 
@@ -1067,34 +978,66 @@ std::pair<int, int> inline pmeGpuCreateGrid(const PmeGpu* pmeGpu, int blockCount
  * Returns a pointer to appropriate spline and spread kernel based on the input bool values
  *
  * \param[in]  pmeGpu                   The PME GPU structure.
- * \param[in]  useOrderThreadsPerAtom   bool controlling if we should use order or order*order threads per atom
+ * \param[in]  threadsPerAtom           Controls whether we should use order or order*order threads per atom
  * \param[in]  writeSplinesToGlobal     bool controlling if we should write spline data to global memory
+ * \param[in]  numGrids                 Number of grids to use. numGrids == 2 if Coulomb is perturbed.
  *
  * \return Pointer to CUDA kernel
  */
-static auto selectSplineAndSpreadKernelPtr(const PmeGpu* pmeGpu, bool useOrderThreadsPerAtom, bool writeSplinesToGlobal)
+static auto selectSplineAndSpreadKernelPtr(const PmeGpu*  pmeGpu,
+                                           ThreadsPerAtom threadsPerAtom,
+                                           bool           writeSplinesToGlobal,
+                                           const int      numGrids)
 {
     PmeGpuProgramImpl::PmeKernelHandle kernelPtr = nullptr;
     if (writeSplinesToGlobal)
     {
-        if (useOrderThreadsPerAtom)
+        if (threadsPerAtom == ThreadsPerAtom::Order)
         {
-            kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelWriteSplinesThPerAtom4;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelWriteSplinesThPerAtom4Dual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelWriteSplinesThPerAtom4Single;
+            }
         }
         else
         {
-            kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelWriteSplines;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelWriteSplinesDual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelWriteSplinesSingle;
+            }
         }
     }
     else
     {
-        if (useOrderThreadsPerAtom)
+        if (threadsPerAtom == ThreadsPerAtom::Order)
         {
-            kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelThPerAtom4;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelThPerAtom4Dual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelThPerAtom4Single;
+            }
         }
         else
         {
-            kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernel;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelDual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelSingle;
+            }
         }
     }
 
@@ -1105,25 +1048,43 @@ static auto selectSplineAndSpreadKernelPtr(const PmeGpu* pmeGpu, bool useOrderTh
  * Returns a pointer to appropriate spline kernel based on the input bool values
  *
  * \param[in]  pmeGpu                   The PME GPU structure.
- * \param[in]  useOrderThreadsPerAtom   bool controlling if we should use order or order*order threads per atom
+ * \param[in]  threadsPerAtom           Controls whether we should use order or order*order threads per atom
  * \param[in]  writeSplinesToGlobal     bool controlling if we should write spline data to global memory
+ * \param[in]  numGrids                 Number of grids to use. numGrids == 2 if Coulomb is perturbed.
  *
  * \return Pointer to CUDA kernel
  */
-static auto selectSplineKernelPtr(const PmeGpu* pmeGpu, bool useOrderThreadsPerAtom, bool gmx_unused writeSplinesToGlobal)
+static auto selectSplineKernelPtr(const PmeGpu*  pmeGpu,
+                                  ThreadsPerAtom threadsPerAtom,
+                                  bool gmx_unused writeSplinesToGlobal,
+                                  const int       numGrids)
 {
     PmeGpuProgramImpl::PmeKernelHandle kernelPtr = nullptr;
     GMX_ASSERT(
             writeSplinesToGlobal,
             "Spline data should always be written to global memory when just calculating splines");
 
-    if (useOrderThreadsPerAtom)
+    if (threadsPerAtom == ThreadsPerAtom::Order)
     {
-        kernelPtr = pmeGpu->programHandle_->impl_->splineKernelThPerAtom4;
+        if (numGrids == 2)
+        {
+            kernelPtr = pmeGpu->programHandle_->impl_->splineKernelThPerAtom4Dual;
+        }
+        else
+        {
+            kernelPtr = pmeGpu->programHandle_->impl_->splineKernelThPerAtom4Single;
+        }
     }
     else
     {
-        kernelPtr = pmeGpu->programHandle_->impl_->splineKernel;
+        if (numGrids == 2)
+        {
+            kernelPtr = pmeGpu->programHandle_->impl_->splineKernelDual;
+        }
+        else
+        {
+            kernelPtr = pmeGpu->programHandle_->impl_->splineKernelSingle;
+        }
     }
     return kernelPtr;
 }
@@ -1132,36 +1093,67 @@ static auto selectSplineKernelPtr(const PmeGpu* pmeGpu, bool useOrderThreadsPerA
  * Returns a pointer to appropriate spread kernel based on the input bool values
  *
  * \param[in]  pmeGpu                   The PME GPU structure.
- * \param[in]  useOrderThreadsPerAtom   bool controlling if we should use order or order*order threads per atom
+ * \param[in]  threadsPerAtom           Controls whether we should use order or order*order threads per atom
  * \param[in]  writeSplinesToGlobal     bool controlling if we should write spline data to global memory
+ * \param[in]  numGrids                 Number of grids to use. numGrids == 2 if Coulomb is perturbed.
  *
  * \return Pointer to CUDA kernel
  */
-static auto selectSpreadKernelPtr(const PmeGpu* pmeGpu, bool useOrderThreadsPerAtom, bool writeSplinesToGlobal)
+static auto selectSpreadKernelPtr(const PmeGpu*  pmeGpu,
+                                  ThreadsPerAtom threadsPerAtom,
+                                  bool           writeSplinesToGlobal,
+                                  const int      numGrids)
 {
     PmeGpuProgramImpl::PmeKernelHandle kernelPtr = nullptr;
     if (writeSplinesToGlobal)
     {
-        if (useOrderThreadsPerAtom)
+        if (threadsPerAtom == ThreadsPerAtom::Order)
         {
-            kernelPtr = pmeGpu->programHandle_->impl_->spreadKernelThPerAtom4;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->spreadKernelThPerAtom4Dual;
+            }
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->spreadKernelThPerAtom4Single;
+            }
         }
         else
         {
-            kernelPtr = pmeGpu->programHandle_->impl_->spreadKernel;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->spreadKernelDual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->spreadKernelSingle;
+            }
         }
     }
     else
     {
         /* if we are not saving the spline data we need to recalculate it
            using the spline and spread Kernel */
-        if (useOrderThreadsPerAtom)
+        if (threadsPerAtom == ThreadsPerAtom::Order)
         {
-            kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelThPerAtom4;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelThPerAtom4Dual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelThPerAtom4Single;
+            }
         }
         else
         {
-            kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernel;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelDual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->splineAndSpreadKernelSingle;
+            }
         }
     }
     return kernelPtr;
@@ -1169,29 +1161,35 @@ static auto selectSpreadKernelPtr(const PmeGpu* pmeGpu, bool useOrderThreadsPerA
 
 void pme_gpu_spread(const PmeGpu*         pmeGpu,
                     GpuEventSynchronizer* xReadyOnDevice,
-                    int gmx_unused gridIndex,
-                    real*          h_grid,
-                    bool           computeSplines,
-                    bool           spreadCharges)
+                    real**                h_grids,
+                    bool                  computeSplines,
+                    bool                  spreadCharges,
+                    const real            lambda)
 {
+    GMX_ASSERT(
+            pmeGpu->common->ngrids == 1 || pmeGpu->common->ngrids == 2,
+            "Only one (normal Coulomb PME) or two (FEP coulomb PME) PME grids can be used on GPU");
+
     GMX_ASSERT(computeSplines || spreadCharges,
                "PME spline/spread kernel has invalid input (nothing to do)");
-    const auto* kernelParamsPtr = pmeGpu->kernelParams.get();
+    auto* kernelParamsPtr = pmeGpu->kernelParams.get();
     GMX_ASSERT(kernelParamsPtr->atoms.nAtoms > 0, "No atom data in PME GPU spread");
 
     const size_t blockSize = pmeGpu->programHandle_->impl_->spreadWorkGroupSize;
 
     const int order = pmeGpu->common->pme_order;
     GMX_ASSERT(order == c_pmeGpuOrder, "Only PME order 4 is implemented");
-    const bool writeGlobal            = pmeGpu->settings.copyAllOutputs;
-    const bool useOrderThreadsPerAtom = pmeGpu->settings.useOrderThreadsPerAtom;
-    const bool recalculateSplines     = pmeGpu->settings.recalculateSplines;
-#if GMX_GPU == GMX_GPU_OPENCL
-    GMX_ASSERT(!useOrderThreadsPerAtom, "Only 16 threads per atom supported in OpenCL");
-    GMX_ASSERT(!recalculateSplines, "Recalculating splines not supported in OpenCL");
-#endif
-    const int atomsPerBlock = useOrderThreadsPerAtom ? blockSize / c_pmeSpreadGatherThreadsPerAtom4ThPerAtom
-                                                     : blockSize / c_pmeSpreadGatherThreadsPerAtom;
+    const bool writeGlobal = pmeGpu->settings.copyAllOutputs;
+    const int  threadsPerAtom =
+            (pmeGpu->settings.threadsPerAtom == ThreadsPerAtom::Order ? order : order * order);
+    const bool recalculateSplines = pmeGpu->settings.recalculateSplines;
+
+    GMX_ASSERT(!GMX_GPU_OPENCL || pmeGpu->settings.threadsPerAtom == ThreadsPerAtom::OrderSquared,
+               "Only 16 threads per atom supported in OpenCL");
+    GMX_ASSERT(!GMX_GPU_OPENCL || !recalculateSplines,
+               "Recalculating splines not supported in OpenCL");
+
+    const int atomsPerBlock = blockSize / threadsPerAtom;
 
     // TODO: pick smaller block size in runtime if needed
     // (e.g. on 660 Ti where 50% occupancy is ~25% faster than 100% occupancy with RNAse (~17.8k atoms))
@@ -1199,30 +1197,39 @@ void pme_gpu_spread(const PmeGpu*         pmeGpu,
     // TODO: test varying block sizes on modern arch-s as well
     // TODO: also consider using cudaFuncSetCacheConfig() for preferring shared memory on older architectures
     //(for spline data mostly)
-    GMX_ASSERT(!c_usePadding || !(c_pmeAtomDataAlignment % atomsPerBlock),
+    GMX_ASSERT(!(c_pmeAtomDataBlockSize % atomsPerBlock),
                "inconsistent atom data padding vs. spreading block size");
 
     // Ensure that coordinates are ready on the device before launching spread;
     // only needed with CUDA on PP+PME ranks, not on separate PME ranks, in unit tests
     // nor in OpenCL as these cases use a single stream (hence xReadyOnDevice == nullptr).
-    GMX_ASSERT(xReadyOnDevice != nullptr || (GMX_GPU != GMX_GPU_CUDA)
-                       || pmeGpu->common->isRankPmeOnly || pme_gpu_is_testing(pmeGpu),
+    GMX_ASSERT(!GMX_GPU_CUDA || xReadyOnDevice != nullptr || pmeGpu->common->isRankPmeOnly
+                       || pme_gpu_settings(pmeGpu).copyAllOutputs,
                "Need a valid coordinate synchronizer on PP+PME ranks with CUDA.");
+
     if (xReadyOnDevice)
     {
-        xReadyOnDevice->enqueueWaitEvent(pmeGpu->archSpecific->pmeStream);
+        xReadyOnDevice->enqueueWaitEvent(pmeGpu->archSpecific->pmeStream_);
     }
 
-    const int blockCount = pmeGpu->nAtomsPadded / atomsPerBlock;
+    const int blockCount = pmeGpu->nAtomsAlloc / atomsPerBlock;
     auto      dimGrid    = pmeGpuCreateGrid(pmeGpu, blockCount);
 
+    if (pmeGpu->common->ngrids == 1)
+    {
+        kernelParamsPtr->current.scale = 1.0;
+    }
+    else
+    {
+        kernelParamsPtr->current.scale = 1.0 - lambda;
+    }
+
     KernelLaunchConfig config;
     config.blockSize[0] = order;
-    config.blockSize[1] = useOrderThreadsPerAtom ? 1 : order;
+    config.blockSize[1] = (pmeGpu->settings.threadsPerAtom == ThreadsPerAtom::Order ? 1 : order);
     config.blockSize[2] = atomsPerBlock;
     config.gridSize[0]  = dimGrid.first;
     config.gridSize[1]  = dimGrid.second;
-    config.stream       = pmeGpu->archSpecific->pmeStream;
 
     int                                timingId;
     PmeGpuProgramImpl::PmeKernelHandle kernelPtr = nullptr;
@@ -1231,21 +1238,23 @@ void pme_gpu_spread(const PmeGpu*         pmeGpu,
         if (spreadCharges)
         {
             timingId  = gtPME_SPLINEANDSPREAD;
-            kernelPtr = selectSplineAndSpreadKernelPtr(pmeGpu, useOrderThreadsPerAtom,
-                                                       writeGlobal || (!recalculateSplines));
+            kernelPtr = selectSplineAndSpreadKernelPtr(pmeGpu, pmeGpu->settings.threadsPerAtom,
+                                                       writeGlobal || (!recalculateSplines),
+                                                       pmeGpu->common->ngrids);
         }
         else
         {
             timingId  = gtPME_SPLINE;
-            kernelPtr = selectSplineKernelPtr(pmeGpu, useOrderThreadsPerAtom,
-                                              writeGlobal || (!recalculateSplines));
+            kernelPtr = selectSplineKernelPtr(pmeGpu, pmeGpu->settings.threadsPerAtom,
+                                              writeGlobal || (!recalculateSplines),
+                                              pmeGpu->common->ngrids);
         }
     }
     else
     {
         timingId  = gtPME_SPREAD;
-        kernelPtr = selectSpreadKernelPtr(pmeGpu, useOrderThreadsPerAtom,
-                                          writeGlobal || (!recalculateSplines));
+        kernelPtr = selectSpreadKernelPtr(pmeGpu, pmeGpu->settings.threadsPerAtom,
+                                          writeGlobal || (!recalculateSplines), pmeGpu->common->ngrids);
     }
 
 
@@ -1257,40 +1266,57 @@ void pme_gpu_spread(const PmeGpu*         pmeGpu,
     const auto kernelArgs = prepareGpuKernelArguments(
             kernelPtr, config, kernelParamsPtr, &kernelParamsPtr->atoms.d_theta,
             &kernelParamsPtr->atoms.d_dtheta, &kernelParamsPtr->atoms.d_gridlineIndices,
-            &kernelParamsPtr->grid.d_realGrid, &kernelParamsPtr->grid.d_fractShiftsTable,
-            &kernelParamsPtr->grid.d_gridlineIndicesTable, &kernelParamsPtr->atoms.d_coefficients,
-            &kernelParamsPtr->atoms.d_coordinates);
+            &kernelParamsPtr->grid.d_realGrid[FEP_STATE_A], &kernelParamsPtr->grid.d_realGrid[FEP_STATE_B],
+            &kernelParamsPtr->grid.d_fractShiftsTable, &kernelParamsPtr->grid.d_gridlineIndicesTable,
+            &kernelParamsPtr->atoms.d_coefficients[FEP_STATE_A],
+            &kernelParamsPtr->atoms.d_coefficients[FEP_STATE_B], &kernelParamsPtr->atoms.d_coordinates);
 #endif
 
-    launchGpuKernel(kernelPtr, config, timingEvent, "PME spline/spread", kernelArgs);
+    launchGpuKernel(kernelPtr, config, pmeGpu->archSpecific->pmeStream_, timingEvent,
+                    "PME spline/spread", kernelArgs);
     pme_gpu_stop_timing(pmeGpu, timingId);
 
-    const bool copyBackGrid =
-            spreadCharges && (pme_gpu_is_testing(pmeGpu) || !pme_gpu_performs_FFT(pmeGpu));
+    const auto& settings    = pmeGpu->settings;
+    const bool copyBackGrid = spreadCharges && (!settings.performGPUFFT || settings.copyAllOutputs);
     if (copyBackGrid)
     {
-        pme_gpu_copy_output_spread_grid(pmeGpu, h_grid);
+        for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
+        {
+            float* h_grid = h_grids[gridIndex];
+            pme_gpu_copy_output_spread_grid(pmeGpu, h_grid, gridIndex);
+        }
     }
     const bool copyBackAtomData =
-            computeSplines && (pme_gpu_is_testing(pmeGpu) || !pme_gpu_performs_gather(pmeGpu));
+            computeSplines && (!settings.performGPUGather || settings.copyAllOutputs);
     if (copyBackAtomData)
     {
         pme_gpu_copy_output_spread_atom_data(pmeGpu);
     }
 }
 
-void pme_gpu_solve(const PmeGpu* pmeGpu, t_complex* h_grid, GridOrdering gridOrdering, bool computeEnergyAndVirial)
+void pme_gpu_solve(const PmeGpu* pmeGpu,
+                   const int     gridIndex,
+                   t_complex*    h_grid,
+                   GridOrdering  gridOrdering,
+                   bool          computeEnergyAndVirial)
 {
-    const bool copyInputAndOutputGrid = pme_gpu_is_testing(pmeGpu) || !pme_gpu_performs_FFT(pmeGpu);
+    GMX_ASSERT(
+            pmeGpu->common->ngrids == 1 || pmeGpu->common->ngrids == 2,
+            "Only one (normal Coulomb PME) or two (FEP coulomb PME) PME grids can be used on GPU");
+    GMX_ASSERT(gridIndex < pmeGpu->common->ngrids,
+               "Invalid combination of gridIndex and number of grids");
+
+    const auto& settings               = pmeGpu->settings;
+    const bool  copyInputAndOutputGrid = !settings.performGPUFFT || settings.copyAllOutputs;
 
     auto* kernelParamsPtr = pmeGpu->kernelParams.get();
 
     float* h_gridFloat = reinterpret_cast<float*>(h_grid);
     if (copyInputAndOutputGrid)
     {
-        copyToDeviceBuffer(&kernelParamsPtr->grid.d_fourierGrid, h_gridFloat, 0,
-                           pmeGpu->archSpecific->complexGridSize, pmeGpu->archSpecific->pmeStream,
-                           pmeGpu->settings.transferKind, nullptr);
+        copyToDeviceBuffer(&kernelParamsPtr->grid.d_fourierGrid[gridIndex], h_gridFloat, 0,
+                           pmeGpu->archSpecific->complexGridSize[gridIndex],
+                           pmeGpu->archSpecific->pmeStream_, pmeGpu->settings.transferKind, nullptr);
     }
 
     int majorDim = -1, middleDim = -1, minorDim = -1;
@@ -1325,10 +1351,10 @@ void pme_gpu_solve(const PmeGpu* pmeGpu, t_complex* h_grid, GridOrdering gridOrd
     {
         cellsPerBlock = (gridLineSize + blocksPerGridLine - 1) / blocksPerGridLine;
     }
-    const int warpSize  = pmeGpu->programHandle_->impl_->warpSize;
+    const int warpSize  = pmeGpu->programHandle_->warpSize();
     const int blockSize = (cellsPerBlock + warpSize - 1) / warpSize * warpSize;
 
-    static_assert(GMX_GPU != GMX_GPU_CUDA || c_solveMaxWarpsPerBlock / 2 >= 4,
+    static_assert(!GMX_GPU_CUDA || c_solveMaxWarpsPerBlock / 2 >= 4,
                   "The CUDA solve energy kernels needs at least 4 warps. "
                   "Here we launch at least half of the max warps.");
 
@@ -1339,19 +1365,34 @@ void pme_gpu_solve(const PmeGpu* pmeGpu, t_complex* h_grid, GridOrdering gridOrd
     config.gridSize[1] = (pmeGpu->kernelParams->grid.complexGridSize[middleDim] + gridLinesPerBlock - 1)
                          / gridLinesPerBlock;
     config.gridSize[2] = pmeGpu->kernelParams->grid.complexGridSize[majorDim];
-    config.stream      = pmeGpu->archSpecific->pmeStream;
 
     int                                timingId  = gtPME_SOLVE;
     PmeGpuProgramImpl::PmeKernelHandle kernelPtr = nullptr;
     if (gridOrdering == GridOrdering::YZX)
     {
-        kernelPtr = computeEnergyAndVirial ? pmeGpu->programHandle_->impl_->solveYZXEnergyKernel
-                                           : pmeGpu->programHandle_->impl_->solveYZXKernel;
+        if (gridIndex == 0)
+        {
+            kernelPtr = computeEnergyAndVirial ? pmeGpu->programHandle_->impl_->solveYZXEnergyKernelA
+                                               : pmeGpu->programHandle_->impl_->solveYZXKernelA;
+        }
+        else
+        {
+            kernelPtr = computeEnergyAndVirial ? pmeGpu->programHandle_->impl_->solveYZXEnergyKernelB
+                                               : pmeGpu->programHandle_->impl_->solveYZXKernelB;
+        }
     }
     else if (gridOrdering == GridOrdering::XYZ)
     {
-        kernelPtr = computeEnergyAndVirial ? pmeGpu->programHandle_->impl_->solveXYZEnergyKernel
-                                           : pmeGpu->programHandle_->impl_->solveXYZKernel;
+        if (gridIndex == 0)
+        {
+            kernelPtr = computeEnergyAndVirial ? pmeGpu->programHandle_->impl_->solveXYZEnergyKernelA
+                                               : pmeGpu->programHandle_->impl_->solveXYZKernelA;
+        }
+        else
+        {
+            kernelPtr = computeEnergyAndVirial ? pmeGpu->programHandle_->impl_->solveXYZEnergyKernelB
+                                               : pmeGpu->programHandle_->impl_->solveXYZKernelB;
+        }
     }
 
     pme_gpu_start_timing(pmeGpu, timingId);
@@ -1360,24 +1401,27 @@ void pme_gpu_solve(const PmeGpu* pmeGpu, t_complex* h_grid, GridOrdering gridOrd
     const auto kernelArgs = prepareGpuKernelArguments(kernelPtr, config, kernelParamsPtr);
 #else
     const auto kernelArgs = prepareGpuKernelArguments(
-            kernelPtr, config, kernelParamsPtr, &kernelParamsPtr->grid.d_splineModuli,
-            &kernelParamsPtr->constants.d_virialAndEnergy, &kernelParamsPtr->grid.d_fourierGrid);
+            kernelPtr, config, kernelParamsPtr, &kernelParamsPtr->grid.d_splineModuli[gridIndex],
+            &kernelParamsPtr->constants.d_virialAndEnergy[gridIndex],
+            &kernelParamsPtr->grid.d_fourierGrid[gridIndex]);
 #endif
-    launchGpuKernel(kernelPtr, config, timingEvent, "PME solve", kernelArgs);
+    launchGpuKernel(kernelPtr, config, pmeGpu->archSpecific->pmeStream_, timingEvent, "PME solve",
+                    kernelArgs);
     pme_gpu_stop_timing(pmeGpu, timingId);
 
     if (computeEnergyAndVirial)
     {
-        copyFromDeviceBuffer(pmeGpu->staging.h_virialAndEnergy,
-                             &kernelParamsPtr->constants.d_virialAndEnergy, 0, c_virialAndEnergyCount,
-                             pmeGpu->archSpecific->pmeStream, pmeGpu->settings.transferKind, nullptr);
+        copyFromDeviceBuffer(pmeGpu->staging.h_virialAndEnergy[gridIndex],
+                             &kernelParamsPtr->constants.d_virialAndEnergy[gridIndex], 0,
+                             c_virialAndEnergyCount, pmeGpu->archSpecific->pmeStream_,
+                             pmeGpu->settings.transferKind, nullptr);
     }
 
     if (copyInputAndOutputGrid)
     {
-        copyFromDeviceBuffer(h_gridFloat, &kernelParamsPtr->grid.d_fourierGrid, 0,
-                             pmeGpu->archSpecific->complexGridSize, pmeGpu->archSpecific->pmeStream,
-                             pmeGpu->settings.transferKind, nullptr);
+        copyFromDeviceBuffer(h_gridFloat, &kernelParamsPtr->grid.d_fourierGrid[gridIndex], 0,
+                             pmeGpu->archSpecific->complexGridSize[gridIndex],
+                             pmeGpu->archSpecific->pmeStream_, pmeGpu->settings.transferKind, nullptr);
     }
 }
 
@@ -1385,126 +1429,161 @@ void pme_gpu_solve(const PmeGpu* pmeGpu, t_complex* h_grid, GridOrdering gridOrd
  * Returns a pointer to appropriate gather kernel based on the inputvalues
  *
  * \param[in]  pmeGpu                   The PME GPU structure.
- * \param[in]  useOrderThreadsPerAtom   bool controlling if we should use order or order*order threads per atom
+ * \param[in]  threadsPerAtom           Controls whether we should use order or order*order threads per atom
  * \param[in]  readSplinesFromGlobal    bool controlling if we should write spline data to global memory
- * \param[in]  forceTreatment           Controls if the forces from the gather should increment or replace the input forces.
+ * \param[in]  numGrids                 Number of grids to use. numGrids == 2 if Coulomb is perturbed.
  *
  * \return Pointer to CUDA kernel
  */
-inline auto selectGatherKernelPtr(const PmeGpu*          pmeGpu,
-                                  bool                   useOrderThreadsPerAtom,
-                                  bool                   readSplinesFromGlobal,
-                                  PmeForceOutputHandling forceTreatment)
+inline auto selectGatherKernelPtr(const PmeGpu*  pmeGpu,
+                                  ThreadsPerAtom threadsPerAtom,
+                                  bool           readSplinesFromGlobal,
+                                  const int      numGrids)
 
 {
     PmeGpuProgramImpl::PmeKernelHandle kernelPtr = nullptr;
 
     if (readSplinesFromGlobal)
     {
-        if (useOrderThreadsPerAtom)
+        if (threadsPerAtom == ThreadsPerAtom::Order)
         {
-            kernelPtr = (forceTreatment == PmeForceOutputHandling::Set)
-                                ? pmeGpu->programHandle_->impl_->gatherKernelReadSplinesThPerAtom4
-                                : pmeGpu->programHandle_->impl_->gatherReduceWithInputKernelReadSplinesThPerAtom4;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->gatherKernelReadSplinesThPerAtom4Dual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->gatherKernelReadSplinesThPerAtom4Single;
+            }
         }
         else
         {
-            kernelPtr = (forceTreatment == PmeForceOutputHandling::Set)
-                                ? pmeGpu->programHandle_->impl_->gatherKernelReadSplines
-                                : pmeGpu->programHandle_->impl_->gatherReduceWithInputKernelReadSplines;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->gatherKernelReadSplinesDual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->gatherKernelReadSplinesSingle;
+            }
         }
     }
     else
     {
-        if (useOrderThreadsPerAtom)
+        if (threadsPerAtom == ThreadsPerAtom::Order)
         {
-            kernelPtr = (forceTreatment == PmeForceOutputHandling::Set)
-                                ? pmeGpu->programHandle_->impl_->gatherKernelThPerAtom4
-                                : pmeGpu->programHandle_->impl_->gatherReduceWithInputKernelThPerAtom4;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->gatherKernelThPerAtom4Dual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->gatherKernelThPerAtom4Single;
+            }
         }
         else
         {
-            kernelPtr = (forceTreatment == PmeForceOutputHandling::Set)
-                                ? pmeGpu->programHandle_->impl_->gatherKernel
-                                : pmeGpu->programHandle_->impl_->gatherReduceWithInputKernel;
+            if (numGrids == 2)
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->gatherKernelDual;
+            }
+            else
+            {
+                kernelPtr = pmeGpu->programHandle_->impl_->gatherKernelSingle;
+            }
         }
     }
     return kernelPtr;
 }
 
-
-void pme_gpu_gather(PmeGpu* pmeGpu, PmeForceOutputHandling forceTreatment, const float* h_grid)
+void pme_gpu_gather(PmeGpu* pmeGpu, real** h_grids, const float lambda)
 {
-    /* Copying the input CPU forces for reduction */
-    if (forceTreatment != PmeForceOutputHandling::Set)
-    {
-        pme_gpu_copy_input_forces(pmeGpu);
-    }
+    GMX_ASSERT(
+            pmeGpu->common->ngrids == 1 || pmeGpu->common->ngrids == 2,
+            "Only one (normal Coulomb PME) or two (FEP coulomb PME) PME grids can be used on GPU");
 
-    if (!pme_gpu_performs_FFT(pmeGpu) || pme_gpu_is_testing(pmeGpu))
+    const auto& settings = pmeGpu->settings;
+
+    if (!settings.performGPUFFT || settings.copyAllOutputs)
     {
-        pme_gpu_copy_input_gather_grid(pmeGpu, const_cast<float*>(h_grid));
+        for (int gridIndex = 0; gridIndex < pmeGpu->common->ngrids; gridIndex++)
+        {
+            float* h_grid = const_cast<float*>(h_grids[gridIndex]);
+            pme_gpu_copy_input_gather_grid(pmeGpu, h_grid, gridIndex);
+        }
     }
 
-    if (pme_gpu_is_testing(pmeGpu))
+    if (settings.copyAllOutputs)
     {
         pme_gpu_copy_input_gather_atom_data(pmeGpu);
     }
 
     /* Set if we have unit tests */
-    const bool   readGlobal             = pmeGpu->settings.copyAllOutputs;
-    const size_t blockSize              = pmeGpu->programHandle_->impl_->gatherWorkGroupSize;
-    const bool   useOrderThreadsPerAtom = pmeGpu->settings.useOrderThreadsPerAtom;
-    const bool   recalculateSplines     = pmeGpu->settings.recalculateSplines;
-#if GMX_GPU == GMX_GPU_OPENCL
-    GMX_ASSERT(!useOrderThreadsPerAtom, "Only 16 threads per atom supported in OpenCL");
-    GMX_ASSERT(!recalculateSplines, "Recalculating splines not supported in OpenCL");
-#endif
-    const int atomsPerBlock = useOrderThreadsPerAtom ? blockSize / c_pmeSpreadGatherThreadsPerAtom4ThPerAtom
-                                                     : blockSize / c_pmeSpreadGatherThreadsPerAtom;
+    const bool   readGlobal = pmeGpu->settings.copyAllOutputs;
+    const size_t blockSize  = pmeGpu->programHandle_->impl_->gatherWorkGroupSize;
+    const int    order      = pmeGpu->common->pme_order;
+    GMX_ASSERT(order == c_pmeGpuOrder, "Only PME order 4 is implemented");
+    const int threadsPerAtom =
+            (pmeGpu->settings.threadsPerAtom == ThreadsPerAtom::Order ? order : order * order);
+    const bool recalculateSplines = pmeGpu->settings.recalculateSplines;
+
+    GMX_ASSERT(!GMX_GPU_OPENCL || pmeGpu->settings.threadsPerAtom == ThreadsPerAtom::OrderSquared,
+               "Only 16 threads per atom supported in OpenCL");
+    GMX_ASSERT(!GMX_GPU_OPENCL || !recalculateSplines,
+               "Recalculating splines not supported in OpenCL");
 
-    GMX_ASSERT(!c_usePadding || !(c_pmeAtomDataAlignment % atomsPerBlock),
+    const int atomsPerBlock = blockSize / threadsPerAtom;
+
+    GMX_ASSERT(!(c_pmeAtomDataBlockSize % atomsPerBlock),
                "inconsistent atom data padding vs. gathering block size");
 
-    const int blockCount = pmeGpu->nAtomsPadded / atomsPerBlock;
+    const int blockCount = pmeGpu->nAtomsAlloc / atomsPerBlock;
     auto      dimGrid    = pmeGpuCreateGrid(pmeGpu, blockCount);
 
-    const int order = pmeGpu->common->pme_order;
-    GMX_ASSERT(order == c_pmeGpuOrder, "Only PME order 4 is implemented");
-
     KernelLaunchConfig config;
     config.blockSize[0] = order;
-    config.blockSize[1] = useOrderThreadsPerAtom ? 1 : order;
+    config.blockSize[1] = (pmeGpu->settings.threadsPerAtom == ThreadsPerAtom::Order ? 1 : order);
     config.blockSize[2] = atomsPerBlock;
     config.gridSize[0]  = dimGrid.first;
     config.gridSize[1]  = dimGrid.second;
-    config.stream       = pmeGpu->archSpecific->pmeStream;
 
     // TODO test different cache configs
 
-    int                                timingId  = gtPME_GATHER;
-    PmeGpuProgramImpl::PmeKernelHandle kernelPtr = selectGatherKernelPtr(
-            pmeGpu, useOrderThreadsPerAtom, readGlobal || (!recalculateSplines), forceTreatment);
+    int                                timingId = gtPME_GATHER;
+    PmeGpuProgramImpl::PmeKernelHandle kernelPtr =
+            selectGatherKernelPtr(pmeGpu, pmeGpu->settings.threadsPerAtom,
+                                  readGlobal || (!recalculateSplines), pmeGpu->common->ngrids);
     // TODO design kernel selection getters and make PmeGpu a friend of PmeGpuProgramImpl
 
     pme_gpu_start_timing(pmeGpu, timingId);
-    auto*       timingEvent     = pme_gpu_fetch_timing_event(pmeGpu, timingId);
-    const auto* kernelParamsPtr = pmeGpu->kernelParams.get();
+    auto* timingEvent     = pme_gpu_fetch_timing_event(pmeGpu, timingId);
+    auto* kernelParamsPtr = pmeGpu->kernelParams.get();
+    if (pmeGpu->common->ngrids == 1)
+    {
+        kernelParamsPtr->current.scale = 1.0;
+    }
+    else
+    {
+        kernelParamsPtr->current.scale = 1.0 - lambda;
+    }
+
 #if c_canEmbedBuffers
     const auto kernelArgs = prepareGpuKernelArguments(kernelPtr, config, kernelParamsPtr);
 #else
     const auto kernelArgs = prepareGpuKernelArguments(
-            kernelPtr, config, kernelParamsPtr, &kernelParamsPtr->atoms.d_coefficients,
-            &kernelParamsPtr->grid.d_realGrid, &kernelParamsPtr->atoms.d_theta,
-            &kernelParamsPtr->atoms.d_dtheta, &kernelParamsPtr->atoms.d_gridlineIndices,
-            &kernelParamsPtr->atoms.d_forces);
+            kernelPtr, config, kernelParamsPtr, &kernelParamsPtr->atoms.d_coefficients[FEP_STATE_A],
+            &kernelParamsPtr->atoms.d_coefficients[FEP_STATE_B],
+            &kernelParamsPtr->grid.d_realGrid[FEP_STATE_A], &kernelParamsPtr->grid.d_realGrid[FEP_STATE_B],
+            &kernelParamsPtr->atoms.d_theta, &kernelParamsPtr->atoms.d_dtheta,
+            &kernelParamsPtr->atoms.d_gridlineIndices, &kernelParamsPtr->atoms.d_forces);
 #endif
-    launchGpuKernel(kernelPtr, config, timingEvent, "PME gather", kernelArgs);
+    launchGpuKernel(kernelPtr, config, pmeGpu->archSpecific->pmeStream_, timingEvent, "PME gather",
+                    kernelArgs);
     pme_gpu_stop_timing(pmeGpu, timingId);
 
     if (pmeGpu->settings.useGpuForceReduction)
     {
-        pmeGpu->archSpecific->pmeForcesReady.markEvent(pmeGpu->archSpecific->pmeStream);
+        pmeGpu->archSpecific->pmeForcesReady.markEvent(pmeGpu->archSpecific->pmeStream_);
     }
     else
     {
@@ -1512,15 +1591,6 @@ void pme_gpu_gather(PmeGpu* pmeGpu, PmeForceOutputHandling forceTreatment, const
     }
 }
 
-DeviceBuffer<float> pme_gpu_get_kernelparam_coordinates(const PmeGpu* pmeGpu)
-{
-    GMX_ASSERT(pmeGpu && pmeGpu->kernelParams,
-               "PME GPU device buffer was requested in non-GPU build or before the GPU PME was "
-               "initialized.");
-
-    return pmeGpu->kernelParams->atoms.d_coordinates;
-}
-
 void* pme_gpu_get_kernelparam_forces(const PmeGpu* pmeGpu)
 {
     if (pmeGpu && pmeGpu->kernelParams)
@@ -1533,38 +1603,7 @@ void* pme_gpu_get_kernelparam_forces(const PmeGpu* pmeGpu)
     }
 }
 
-/*! \brief Check the validity of the device buffer.
- *
- * Checks if the buffer is not nullptr and, when possible, if it is big enough.
- *
- * \todo Split and move this function to gpu_utils.
- *
- * \param[in] buffer        Device buffer to be checked.
- * \param[in] requiredSize  Number of elements that the buffer will have to accommodate.
- *
- * \returns If the device buffer can be set.
- */
-template<typename T>
-static bool checkDeviceBuffer(gmx_unused DeviceBuffer<T> buffer, gmx_unused int requiredSize)
-{
-#if GMX_GPU == GMX_GPU_CUDA
-    GMX_ASSERT(buffer != nullptr, "The device pointer is nullptr");
-    return buffer != nullptr;
-#elif GMX_GPU == GMX_GPU_OPENCL
-    size_t size;
-    int    retval = clGetMemObjectInfo(buffer, CL_MEM_SIZE, sizeof(size), &size, nullptr);
-    GMX_ASSERT(retval == CL_SUCCESS,
-               gmx::formatString("clGetMemObjectInfo failed with error code #%d", retval).c_str());
-    GMX_ASSERT(static_cast<int>(size) >= requiredSize,
-               "Number of atoms in device buffer is smaller then required size.");
-    return retval == CL_SUCCESS && static_cast<int>(size) >= requiredSize;
-#elif GMX_GPU == GMX_GPU_NONE
-    GMX_ASSERT(false, "Setter for device-side coordinates was called in non-GPU build.");
-    return false;
-#endif
-}
-
-void pme_gpu_set_kernelparam_coordinates(const PmeGpu* pmeGpu, DeviceBuffer<float> d_x)
+void pme_gpu_set_kernelparam_coordinates(const PmeGpu* pmeGpu, DeviceBuffer<gmx::RVec> d_x)
 {
     GMX_ASSERT(pmeGpu && pmeGpu->kernelParams,
                "PME GPU device buffer can not be set in non-GPU builds or before the GPU PME was "
@@ -1576,30 +1615,6 @@ void pme_gpu_set_kernelparam_coordinates(const PmeGpu* pmeGpu, DeviceBuffer<floa
     pmeGpu->kernelParams->atoms.d_coordinates = d_x;
 }
 
-void* pme_gpu_get_stream(const PmeGpu* pmeGpu)
-{
-    if (pmeGpu)
-    {
-        return static_cast<void*>(&pmeGpu->archSpecific->pmeStream);
-    }
-    else
-    {
-        return nullptr;
-    }
-}
-
-void* pme_gpu_get_context(const PmeGpu* pmeGpu)
-{
-    if (pmeGpu)
-    {
-        return static_cast<void*>(&pmeGpu->archSpecific->context);
-    }
-    else
-    {
-        return nullptr;
-    }
-}
-
 GpuEventSynchronizer* pme_gpu_get_forces_ready_synchronizer(const PmeGpu* pmeGpu)
 {
     if (pmeGpu && pmeGpu->kernelParams)
index 443b97a60e327d042321ab8059b8f7067c3a2d92..1220b139845a87ef2808adf767aed48260da527e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_EWALD_PME_GPU_INTERNAL_H
 #define GMX_EWALD_PME_GPU_INTERNAL_H
 
-#include "gromacs/fft/fft.h"              // for the gmx_fft_direction enum
+#include "gromacs/fft/fft.h" // for the gmx_fft_direction enum
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
 #include "gromacs/gpu_utils/gpu_macros.h" // for the GPU_FUNC_ macros
-#include "gromacs/utility/arrayref.h"
 
-#include "pme_gpu_types_host.h" // for the inline functions accessing PmeGpu members
+#include "pme_gpu_types_host.h"
+#include "pme_output.h"
 
+class DeviceContext;
+struct DeviceInformation;
+class DeviceStream;
+class GpuEventSynchronizer;
 struct gmx_hw_info_t;
 struct gmx_gpu_opt_t;
 struct gmx_pme_t; // only used in pme_gpu_reinit
-struct gmx_wallclock_gpu_pme_t;
+struct gmx_wallcycle;
 class PmeAtomComm;
+enum class PmeForceOutputHandling;
+struct PmeGpu;
+class PmeGpuProgram;
+struct PmeGpuStaging;
+struct PmeGpuSettings;
 struct t_complex;
 
+#ifndef FEP_STATE_A
+//! Grid index of FEP state A (or unperturbed system)
+#    define FEP_STATE_A 0
+#endif
+#ifndef FEP_STATE_B
+//! Grid index of FEP state B
+#    define FEP_STATE_B 1
+#endif
+
 namespace gmx
 {
+template<typename>
+class ArrayRef;
 class MDLogger;
-}
+} // namespace gmx
 
 //! Type of spline data
 enum class PmeSplineDataType
@@ -79,21 +100,15 @@ enum class GridOrdering
 };
 
 /*! \libinternal \brief
- * Returns the number of atoms per chunk in the atom charges/coordinates data layout.
- * Depends on CUDA-specific block sizes, needed for the atom data padding.
+ * Returns the size of the block size requirement
  *
- * \param[in] pmeGpu            The PME GPU structure.
- * \returns   Number of atoms in a single GPU atom data chunk.
- */
-int pme_gpu_get_atom_data_alignment(const PmeGpu* pmeGpu);
-
-/*! \libinternal \brief
- * Returns the number of atoms per chunk in the atom spline theta/dtheta data layout.
+ * The GPU version of PME requires that the coordinates array have a
+ * size divisible by the returned number.
  *
- * \param[in] pmeGpu            The PME GPU structure.
- * \returns   Number of atoms in a single GPU atom spline data chunk.
+ * \returns Number of atoms in a single GPU atom data chunk, which
+ * determines a minimum divisior of the size of the memory allocated.
  */
-int pme_gpu_get_atoms_per_warp(const PmeGpu* pmeGpu);
+int pme_gpu_get_atom_data_block_size();
 
 /*! \libinternal \brief
  * Synchronizes the current computation, waiting for the GPU kernels/transfers to finish.
@@ -128,8 +143,10 @@ void pme_gpu_clear_energy_virial(const PmeGpu* pmeGpu);
  * Reallocates and copies the pre-computed B-spline values to the GPU.
  *
  * \param[in,out] pmeGpu             The PME GPU structure.
+ * \param[in]     gridIndex          The index of the grid to use. 0 is Coulomb in the normal
+ *                                   state or FEP state A and 1 is Coulomb in FEP state B.
  */
-void pme_gpu_realloc_and_copy_bspline_values(PmeGpu* pmeGpu);
+void pme_gpu_realloc_and_copy_bspline_values(PmeGpu* pmeGpu, int gridIndex = 0);
 
 /*! \libinternal \brief
  * Frees the pre-computed B-spline values on the GPU (and the transfer CPU buffers).
@@ -176,33 +193,21 @@ void pme_gpu_copy_output_forces(PmeGpu* pmeGpu);
  */
 bool pme_gpu_stream_query(const PmeGpu* pmeGpu);
 
-/*! \libinternal \brief
- * Reallocates the input coordinates buffer on the GPU (and clears the padded part if needed).
- *
- * \param[in] pmeGpu            The PME GPU structure.
- *
- * Needs to be called on every DD step/in the beginning.
- */
-void pme_gpu_realloc_coordinates(const PmeGpu* pmeGpu);
-
-/*! \libinternal \brief
- * Frees the coordinates on the GPU.
- *
- * \param[in] pmeGpu            The PME GPU structure.
- */
-void pme_gpu_free_coordinates(const PmeGpu* pmeGpu);
-
 /*! \libinternal \brief
  * Reallocates the buffer on the GPU and copies the charges/coefficients from the CPU buffer.
  * Clears the padded part if needed.
  *
  * \param[in] pmeGpu            The PME GPU structure.
  * \param[in] h_coefficients    The input atom charges/coefficients.
+ * \param[in] gridIndex         The index of the grid to use. 0 is Coulomb in the normal
+ *                              state or FEP state A and 1 is Coulomb in FEP state B.
  *
  * Does not need to be done for every PME computation, only whenever the local charges change.
  * (So, in the beginning of the run, or on DD step).
  */
-void pme_gpu_realloc_and_copy_input_coefficients(const PmeGpu* pmeGpu, const float* h_coefficients);
+void pme_gpu_realloc_and_copy_input_coefficients(const PmeGpu* pmeGpu,
+                                                 const float*  h_coefficients,
+                                                 int           gridIndex = 0);
 
 /*! \libinternal \brief
  * Frees the charges/coefficients on the GPU.
@@ -278,30 +283,34 @@ void pme_gpu_free_fract_shifts(const PmeGpu* pmeGpu);
 /*! \libinternal \brief
  * Copies the input real-space grid from the host to the GPU.
  *
- * \param[in] pmeGpu   The PME GPU structure.
- * \param[in] h_grid   The host-side grid buffer.
+ * \param[in] pmeGpu    The PME GPU structure.
+ * \param[in] h_grid    The host-side grid buffer.
+ * \param[in] gridIndex The index of the grid to use. 0 is Coulomb in the normal
+ *                      state or FEP state A and 1 is Coulomb in FEP state B.
  */
-void pme_gpu_copy_input_gather_grid(const PmeGpu* pmeGpu, float* h_grid);
+void pme_gpu_copy_input_gather_grid(const PmeGpu* pmeGpu, const float* h_grid, int gridIndex = 0);
 
 /*! \libinternal \brief
  * Copies the output real-space grid from the GPU to the host.
  *
- * \param[in] pmeGpu   The PME GPU structure.
- * \param[out] h_grid  The host-side grid buffer.
+ * \param[in] pmeGpu    The PME GPU structure.
+ * \param[out] h_grid   The host-side grid buffer.
+ * \param[in] gridIndex The index of the grid to use. 0 is Coulomb in the normal
+ *                      state or FEP state A and 1 is Coulomb in FEP state B.
  */
-void pme_gpu_copy_output_spread_grid(const PmeGpu* pmeGpu, float* h_grid);
+void pme_gpu_copy_output_spread_grid(const PmeGpu* pmeGpu, float* h_grid, int gridIndex = 0);
 
 /*! \libinternal \brief
  * Copies the spread output spline data and gridline indices from the GPU to the host.
  *
- * \param[in] pmeGpu   The PME GPU structure.
+ * \param[in] pmeGpu    The PME GPU structure.
  */
 void pme_gpu_copy_output_spread_atom_data(const PmeGpu* pmeGpu);
 
 /*! \libinternal \brief
  * Copies the gather input spline data and gridline indices from the host to the GPU.
  *
- * \param[in] pmeGpu   The PME GPU structure.
+ * \param[in] pmeGpu    The PME GPU structure.
  */
 void pme_gpu_copy_input_gather_atom_data(const PmeGpu* pmeGpu);
 
@@ -312,22 +321,6 @@ void pme_gpu_copy_input_gather_atom_data(const PmeGpu* pmeGpu);
  */
 void pme_gpu_sync_spread_grid(const PmeGpu* pmeGpu);
 
-/*! \libinternal \brief
- * Does the one-time GPU-framework specific PME initialization.
- * For CUDA, the PME stream is created with the highest priority.
- *
- * \param[in] pmeGpu  The PME GPU structure.
- */
-void pme_gpu_init_internal(PmeGpu* pmeGpu);
-
-/*! \libinternal \brief
- * Destroys the PME GPU-framework specific data.
- * Should be called last in the PME GPU destructor.
- *
- * \param[in] pmeGpu  The PME GPU structure.
- */
-void pme_gpu_destroy_specific(const PmeGpu* pmeGpu);
-
 /*! \libinternal \brief
  * Initializes the CUDA FFT structures.
  *
@@ -342,76 +335,49 @@ void pme_gpu_reinit_3dfft(const PmeGpu* pmeGpu);
  */
 void pme_gpu_destroy_3dfft(const PmeGpu* pmeGpu);
 
-/* Several GPU event-based timing functions that live in pme_gpu_timings.cpp */
-
-/*! \libinternal \brief
- * Finalizes all the active PME GPU stage timings for the current computation. Should be called at the end of every computation.
- *
- * \param[in] pmeGpu         The PME GPU structure.
- */
-void pme_gpu_update_timings(const PmeGpu* pmeGpu);
-
-/*! \libinternal \brief
- * Updates the internal list of active PME GPU stages (if timings are enabled).
- *
- * \param[in] pmeGpu         The PME GPU data structure.
- */
-void pme_gpu_reinit_timings(const PmeGpu* pmeGpu);
-
-/*! \brief
- * Resets the PME GPU timings. To be called at the reset MD step.
- *
- * \param[in] pmeGpu         The PME GPU structure.
- */
-void pme_gpu_reset_timings(const PmeGpu* pmeGpu);
-
-/*! \libinternal \brief
- * Copies the PME GPU timings to the gmx_wallclock_gpu_t structure (for log output). To be called at the run end.
- *
- * \param[in] pmeGpu         The PME GPU structure.
- * \param[in] timings        The gmx_wallclock_gpu_pme_t structure.
- */
-void pme_gpu_get_timings(const PmeGpu* pmeGpu, gmx_wallclock_gpu_pme_t* timings);
-
 /* The PME stages themselves */
 
 /*! \libinternal \brief
  * A GPU spline computation and charge spreading function.
  *
- * \param[in]  pmeGpu          The PME GPU structure.
- * \param[in]  xReadyOnDevice  Event synchronizer indicating that the coordinates are ready in the device memory;
- *                             can be nullptr when invoked on a separate PME rank or from PME tests.
- * \param[in]  gridIndex       Index of the PME grid - unused, assumed to be 0.
- * \param[out] h_grid          The host-side grid buffer (used only if the result of the spread is expected on the host,
- *                             e.g. testing or host-side FFT)
- * \param[in]  computeSplines  Should the computation of spline parameters and gridline indices be performed.
- * \param[in]  spreadCharges   Should the charges/coefficients be spread on the grid.
+ * \param[in]  pmeGpu                 The PME GPU structure.
+ * \param[in]  xReadyOnDevice         Event synchronizer indicating that the coordinates are ready in the device memory;
+ *                                    can be nullptr when invoked on a separate PME rank or from PME tests.
+ * \param[out] h_grids                The host-side grid buffers (used only if the result of the spread is expected on the host,
+ *                                    e.g. testing or host-side FFT)
+ * \param[in]  computeSplines         Should the computation of spline parameters and gridline indices be performed.
+ * \param[in]  spreadCharges          Should the charges/coefficients be spread on the grid.
+ * \param[in]  lambda                 The lambda value of the current system state.
  */
 GPU_FUNC_QUALIFIER void pme_gpu_spread(const PmeGpu*         GPU_FUNC_ARGUMENT(pmeGpu),
                                        GpuEventSynchronizer* GPU_FUNC_ARGUMENT(xReadyOnDevice),
-                                       int                   GPU_FUNC_ARGUMENT(gridIndex),
-                                       real*                 GPU_FUNC_ARGUMENT(h_grid),
+                                       float**               GPU_FUNC_ARGUMENT(h_grids),
                                        bool                  GPU_FUNC_ARGUMENT(computeSplines),
-                                       bool GPU_FUNC_ARGUMENT(spreadCharges)) GPU_FUNC_TERM;
+                                       bool                  GPU_FUNC_ARGUMENT(spreadCharges),
+                                       const real GPU_FUNC_ARGUMENT(lambda)) GPU_FUNC_TERM;
 
 /*! \libinternal \brief
  * 3D FFT R2C/C2R routine.
  *
  * \param[in]  pmeGpu          The PME GPU structure.
  * \param[in]  direction       Transform direction (real-to-complex or complex-to-real)
- * \param[in]  gridIndex       Index of the PME grid - unused, assumed to be 0.
+ * \param[in]  gridIndex       The index of the grid to use. 0 is Coulomb in the normal
+ *                             state or FEP state A and 1 is Coulomb in FEP state B.
  */
-void pme_gpu_3dfft(const PmeGpu* pmeGpu, enum gmx_fft_direction direction, int gridIndex);
+void pme_gpu_3dfft(const PmeGpu* pmeGpu, enum gmx_fft_direction direction, int gridIndex = 0);
 
 /*! \libinternal \brief
  * A GPU Fourier space solving function.
  *
  * \param[in]     pmeGpu                  The PME GPU structure.
+ * \param[in]     gridIndex               The index of the grid to use. 0 is Coulomb in the normal
+ *                                        state or FEP state A and 1 is Coulomb in FEP state B.
  * \param[in,out] h_grid                  The host-side input and output Fourier grid buffer (used only with testing or host-side FFT)
  * \param[in]     gridOrdering            Specifies the dimenion ordering of the complex grid. TODO: store this information?
- * \param[in]     computeEnergyAndVirial  Tells if the energy and virial computation should also be performed.
+ * \param[in]     computeEnergyAndVirial  Tells if the energy and virial computation should be performed.
  */
 GPU_FUNC_QUALIFIER void pme_gpu_solve(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu),
+                                      int           GPU_FUNC_ARGUMENT(gridIndex),
                                       t_complex*    GPU_FUNC_ARGUMENT(h_grid),
                                       GridOrdering  GPU_FUNC_ARGUMENT(gridOrdering),
                                       bool GPU_FUNC_ARGUMENT(computeEnergyAndVirial)) GPU_FUNC_TERM;
@@ -419,28 +385,21 @@ GPU_FUNC_QUALIFIER void pme_gpu_solve(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu),
 /*! \libinternal \brief
  * A GPU force gathering function.
  *
- * \param[in]     pmeGpu           The PME GPU structure.
- * \param[in]     forceTreatment   Tells how data in h_forces should be treated.
- *                                 TODO: determine efficiency/balance of host/device-side
- * reductions. \param[in]     h_grid           The host-side grid buffer (used only in testing mode)
+ * \param[in]     pmeGpu                   The PME GPU structure.
+ * \param[in]     h_grids                  The host-side grid buffer (used only in testing mode).
+ * \param[in]     lambda                   The lambda value to use.
  */
-GPU_FUNC_QUALIFIER void pme_gpu_gather(PmeGpu*                GPU_FUNC_ARGUMENT(pmeGpu),
-                                       PmeForceOutputHandling GPU_FUNC_ARGUMENT(forceTreatment),
-                                       const float* GPU_FUNC_ARGUMENT(h_grid)) GPU_FUNC_TERM;
+GPU_FUNC_QUALIFIER void pme_gpu_gather(PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu),
+                                       float** GPU_FUNC_ARGUMENT(h_grids),
+                                       float   GPU_FUNC_ARGUMENT(lambda)) GPU_FUNC_TERM;
 
-/*! \brief Return pointer to device copy of coordinate data.
- * \param[in] pmeGpu         The PME GPU structure.
- * \returns                  Pointer to coordinate data
- */
-GPU_FUNC_QUALIFIER DeviceBuffer<float> pme_gpu_get_kernelparam_coordinates(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu))
-        GPU_FUNC_TERM_WITH_RETURN(DeviceBuffer<float>{});
 
 /*! \brief Sets the device pointer to coordinate data
  * \param[in] pmeGpu         The PME GPU structure.
  * \param[in] d_x            Pointer to coordinate data
  */
 GPU_FUNC_QUALIFIER void pme_gpu_set_kernelparam_coordinates(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu),
-                                                            DeviceBuffer<float> GPU_FUNC_ARGUMENT(d_x)) GPU_FUNC_TERM;
+                                                            DeviceBuffer<gmx::RVec> GPU_FUNC_ARGUMENT(d_x)) GPU_FUNC_TERM;
 
 /*! \brief Return pointer to device copy of force data.
  * \param[in] pmeGpu         The PME GPU structure.
@@ -449,20 +408,6 @@ GPU_FUNC_QUALIFIER void pme_gpu_set_kernelparam_coordinates(const PmeGpu* GPU_FU
 GPU_FUNC_QUALIFIER void* pme_gpu_get_kernelparam_forces(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu))
         GPU_FUNC_TERM_WITH_RETURN(nullptr);
 
-/*! \brief Return pointer to GPU stream.
- * \param[in] pmeGpu         The PME GPU structure.
- * \returns                  Pointer to stream object.
- */
-GPU_FUNC_QUALIFIER void* pme_gpu_get_stream(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu))
-        GPU_FUNC_TERM_WITH_RETURN(nullptr);
-
-/*! \brief Return pointer to GPU context (for OpenCL builds).
- * \param[in] pmeGpu         The PME GPU structure.
- * \returns                  Pointer to context object.
- */
-GPU_FUNC_QUALIFIER void* pme_gpu_get_context(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu))
-        GPU_FUNC_TERM_WITH_RETURN(nullptr);
-
 /*! \brief Return pointer to the sync object triggered after the PME force calculation completion
  * \param[in] pmeGpu         The PME GPU structure.
  * \returns                  Pointer to sync object
@@ -470,70 +415,33 @@ GPU_FUNC_QUALIFIER void* pme_gpu_get_context(const PmeGpu* GPU_FUNC_ARGUMENT(pme
 GPU_FUNC_QUALIFIER GpuEventSynchronizer* pme_gpu_get_forces_ready_synchronizer(
         const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu)) GPU_FUNC_TERM_WITH_RETURN(nullptr);
 
-/* The inlined convenience PME GPU status getters */
-
 /*! \libinternal \brief
- * Tells if PME runs on multiple GPUs with the decomposition.
+ * Returns the PME GPU settings
  *
  * \param[in] pmeGpu         The PME GPU structure.
- * \returns                  True if PME runs on multiple GPUs, false otherwise.
+ * \returns                  The settings for PME on GPU
  */
-inline bool pme_gpu_uses_dd(const PmeGpu* pmeGpu)
+inline const PmeGpuSettings& pme_gpu_settings(const PmeGpu* pmeGpu)
 {
-    return !pmeGpu->settings.useDecomposition;
+    return pmeGpu->settings;
 }
 
 /*! \libinternal \brief
- * Tells if PME performs the gathering stage on GPU.
+ * Returns the PME GPU staging object
  *
  * \param[in] pmeGpu         The PME GPU structure.
- * \returns                  True if the gathering is performed on GPU, false otherwise.
+ * \returns                  The staging object for PME on GPU
  */
-inline bool pme_gpu_performs_gather(const PmeGpu* pmeGpu)
+inline const PmeGpuStaging& pme_gpu_staging(const PmeGpu* pmeGpu)
 {
-    return pmeGpu->settings.performGPUGather;
+    return pmeGpu->staging;
 }
 
 /*! \libinternal \brief
- * Tells if PME performs the FFT stages on GPU.
+ * Sets whether the PME module is running in testing mode
  *
  * \param[in] pmeGpu         The PME GPU structure.
- * \returns                  True if FFT is performed on GPU, false otherwise.
- */
-inline bool pme_gpu_performs_FFT(const PmeGpu* pmeGpu)
-{
-    return pmeGpu->settings.performGPUFFT;
-}
-
-/*! \libinternal \brief
- * Tells if PME performs the grid (un-)wrapping on GPU.
- *
- * \param[in] pmeGpu         The PME GPU structure.
- * \returns                  True if (un-)wrapping is performed on GPU, false otherwise.
- */
-inline bool pme_gpu_performs_wrapping(const PmeGpu* pmeGpu)
-{
-    return pmeGpu->settings.useDecomposition;
-}
-
-/*! \libinternal \brief
- * Tells if PME performs the grid solving on GPU.
- *
- * \param[in] pmeGpu         The PME GPU structure.
- * \returns                  True if solving is performed on GPU, false otherwise.
- */
-inline bool pme_gpu_performs_solve(const PmeGpu* pmeGpu)
-{
-    return pmeGpu->settings.performGPUSolve;
-}
-
-/*! \libinternal \brief
- * Enables or disables the testing mode.
- * Testing mode only implies copying all the outputs, even the intermediate ones, to the host,
- * and also makes the copies synchronous.
- *
- * \param[in] pmeGpu             The PME GPU structure.
- * \param[in] testing            Should the testing mode be enabled, or disabled.
+ * \param[in] testing        Whether testing mode is on.
  */
 inline void pme_gpu_set_testing(PmeGpu* pmeGpu, bool testing)
 {
@@ -544,17 +452,6 @@ inline void pme_gpu_set_testing(PmeGpu* pmeGpu, bool testing)
     }
 }
 
-/*! \libinternal \brief
- * Tells if PME is in the testing mode.
- *
- * \param[in] pmeGpu             The PME GPU structure.
- * \returns                      true if testing mode is enabled, false otherwise.
- */
-inline bool pme_gpu_is_testing(const PmeGpu* pmeGpu)
-{
-    return pmeGpu->settings.copyAllOutputs;
-}
-
 /* A block of C++ functions that live in pme_gpu_internal.cpp */
 
 /*! \libinternal \brief
@@ -564,21 +461,24 @@ inline bool pme_gpu_is_testing(const PmeGpu* pmeGpu)
  * handled the solve stage.
  *
  * \param[in] pme                The PME structure.
+ * \param[in] lambda             The lambda value to use when calculating the results.
  * \param[out] output            Pointer to output where energy and virial should be stored.
  */
 GPU_FUNC_QUALIFIER void pme_gpu_getEnergyAndVirial(const gmx_pme_t& GPU_FUNC_ARGUMENT(pme),
+                                                   float            GPU_FUNC_ARGUMENT(lambda),
                                                    PmeOutput* GPU_FUNC_ARGUMENT(output)) GPU_FUNC_TERM;
 
 /*! \libinternal \brief
  * Returns the GPU outputs (forces, energy and virial)
  *
- * \param[in] pme                The PME structure.
- * \param[in] flags              The combination of flags that affected this PME computation.
- *                               The flags are the GMX_PME_ flags from pme.h.
- * \returns                      The output object.
+ * \param[in] pme                     The PME structure.
+ * \param[in] computeEnergyAndVirial  Whether the energy and virial are being computed
+ * \param[in] lambdaQ            The Coulomb lambda to use when finalizing the output.
+ * \returns                           The output object.
  */
 GPU_FUNC_QUALIFIER PmeOutput pme_gpu_getOutput(const gmx_pme_t& GPU_FUNC_ARGUMENT(pme),
-                                               int              GPU_FUNC_ARGUMENT(flags))
+                                               bool GPU_FUNC_ARGUMENT(computeEnergyAndVirial),
+                                               real GPU_FUNC_ARGUMENT(lambdaQ))
         GPU_FUNC_TERM_WITH_RETURN(PmeOutput{});
 
 /*! \libinternal \brief
@@ -602,44 +502,6 @@ GPU_FUNC_QUALIFIER void pme_gpu_update_input_box(PmeGpu*      GPU_FUNC_ARGUMENT(
  */
 void pme_gpu_finish_computation(const PmeGpu* pmeGpu);
 
-//! A binary enum for spline data layout transformation
-enum class PmeLayoutTransform
-{
-    GpuToHost,
-    HostToGpu
-};
-
-/*! \libinternal \brief
- * Rearranges the atom spline data between the GPU and host layouts.
- * Only used for test purposes so far, likely to be horribly slow.
- *
- * \param[in]  pmeGpu     The PME GPU structure.
- * \param[out] atc        The PME CPU atom data structure (with a single-threaded layout).
- * \param[in]  type       The spline data type (values or derivatives).
- * \param[in]  dimIndex   Dimension index.
- * \param[in]  transform  Layout transform type
- */
-GPU_FUNC_QUALIFIER void pme_gpu_transform_spline_atom_data(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu),
-                                                           const PmeAtomComm* GPU_FUNC_ARGUMENT(atc),
-                                                           PmeSplineDataType GPU_FUNC_ARGUMENT(type),
-                                                           int GPU_FUNC_ARGUMENT(dimIndex),
-                                                           PmeLayoutTransform GPU_FUNC_ARGUMENT(transform)) GPU_FUNC_TERM;
-
-/*! \libinternal \brief
- * Gets a unique index to an element in a spline parameter buffer (theta/dtheta),
- * which is laid out for GPU spread/gather kernels. The index is wrt the execution block,
- * in range(0, atomsPerBlock * order * DIM).
- * This is a wrapper, only used in unit tests.
- * \param[in] order            PME order
- * \param[in] splineIndex      Spline contribution index (from 0 to \p order - 1)
- * \param[in] dimIndex         Dimension index (from 0 to 2)
- * \param[in] atomIndex        Atom index wrt the block.
- * \param[in] atomsPerWarp     Number of atoms processed by a warp.
- *
- * \returns Index into theta or dtheta array using GPU layout.
- */
-int getSplineParamFullIndex(int order, int splineIndex, int dimIndex, int atomIndex, int atomsPerWarp);
-
 /*! \libinternal \brief
  * Get the normal/padded grid dimensions of the real-space PME grid on GPU. Only used in tests.
  *
@@ -654,14 +516,17 @@ GPU_FUNC_QUALIFIER void pme_gpu_get_real_grid_sizes(const PmeGpu* GPU_FUNC_ARGUM
 /*! \libinternal \brief
  * (Re-)initializes the PME GPU data at the beginning of the run or on DLB.
  *
- * \param[in,out] pme             The PME structure.
- * \param[in]     gpuInfo         The GPU information structure.
- * \param[in]     pmeGpuProgram   The PME GPU program data
+ * \param[in,out] pme            The PME structure.
+ * \param[in]     deviceContext  The GPU context.
+ * \param[in]     deviceStream   The GPU stream.
+ * \param[in,out] pmeGpuProgram  The handle to the program/kernel data created outside (e.g. in unit tests/runner)
+ *
  * \throws gmx::NotImplementedError if this generally valid PME structure is not valid for GPU runs.
  */
-GPU_FUNC_QUALIFIER void pme_gpu_reinit(gmx_pme_t*               GPU_FUNC_ARGUMENT(pme),
-                                       const gmx_device_info_t* GPU_FUNC_ARGUMENT(gpuInfo),
-                                       PmeGpuProgramHandle GPU_FUNC_ARGUMENT(pmeGpuProgram)) GPU_FUNC_TERM;
+GPU_FUNC_QUALIFIER void pme_gpu_reinit(gmx_pme_t*           GPU_FUNC_ARGUMENT(pme),
+                                       const DeviceContext* GPU_FUNC_ARGUMENT(deviceContext),
+                                       const DeviceStream*  GPU_FUNC_ARGUMENT(deviceStream),
+                                       const PmeGpuProgram* GPU_FUNC_ARGUMENT(pmeGpuProgram)) GPU_FUNC_TERM;
 
 /*! \libinternal \brief
  * Destroys the PME GPU data at the end of the run.
@@ -675,14 +540,16 @@ GPU_FUNC_QUALIFIER void pme_gpu_destroy(PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu)) GPU_F
  *
  * \param[in] pmeGpu    The PME GPU structure.
  * \param[in] nAtoms    The number of particles.
- * \param[in] charges   The pointer to the host-side array of particle charges.
+ * \param[in] chargesA  The pointer to the host-side array of particle charges in the unperturbed state or FEP state A.
+ * \param[in] chargesB  The pointer to the host-side array of particle charges in FEP state B.
  *
  * This is a function that should only be called in the beginning of the run and on domain
  * decomposition. Should be called before the pme_gpu_set_io_ranges.
  */
 GPU_FUNC_QUALIFIER void pme_gpu_reinit_atoms(PmeGpu*     GPU_FUNC_ARGUMENT(pmeGpu),
                                              int         GPU_FUNC_ARGUMENT(nAtoms),
-                                             const real* GPU_FUNC_ARGUMENT(charges)) GPU_FUNC_TERM;
+                                             const real* GPU_FUNC_ARGUMENT(chargesA),
+                                             const real* GPU_FUNC_ARGUMENT(chargesB) = nullptr) GPU_FUNC_TERM;
 
 /*! \brief \libinternal
  * The PME GPU reinitialization function that is called both at the end of any PME computation and on any load balancing.
@@ -697,14 +564,15 @@ void pme_gpu_reinit_computation(const PmeGpu* pmeGpu);
  * Blocks until PME GPU tasks are completed, and gets the output forces and virial/energy
  * (if they were to be computed).
  *
- * \param[in]  pme            The PME data structure.
- * \param[in]  flags          The combination of flags to affect this PME computation.
- *                            The flags are the GMX_PME_ flags from pme.h.
- * \param[out] wcycle         The wallclock counter.
- * \return     The output forces, energy and virial
+ * \param[in]  pme                     The PME data structure.
+ * \param[in]  computeEnergyAndVirial  Tells if the energy and virial computation should be performed.
+ * \param[in]  lambdaQ                 The Coulomb lambda to use when calculating the results.
+ * \param[out] wcycle                  The wallclock counter.
+ * \return                             The output forces, energy and virial
  */
-GPU_FUNC_QUALIFIER PmeOutput pme_gpu_wait_finish_task(gmx_pme_t*     GPU_FUNC_ARGUMENT(pme),
-                                                      int            GPU_FUNC_ARGUMENT(flags),
+GPU_FUNC_QUALIFIER PmeOutput pme_gpu_wait_finish_task(gmx_pme_t* GPU_FUNC_ARGUMENT(pme),
+                                                      bool GPU_FUNC_ARGUMENT(computeEnergyAndVirial),
+                                                      real           GPU_FUNC_ARGUMENT(lambdaQ),
                                                       gmx_wallcycle* GPU_FUNC_ARGUMENT(wcycle))
         GPU_FUNC_TERM_WITH_RETURN(PmeOutput{});
 
index 5227eca063f712fce3c52087dd421e03452cce47..efc754530a2cce18e0933480f496516fc9448d47 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <memory>
 
+#include "gromacs/utility/gmxassert.h"
+
 #include "pme_gpu_program_impl.h"
 
-PmeGpuProgram::PmeGpuProgram(const gmx_device_info_t* deviceInfo) :
-    impl_(std::make_unique<PmeGpuProgramImpl>(deviceInfo))
+PmeGpuProgram::PmeGpuProgram(const DeviceContext& deviceContext) :
+    impl_(std::make_unique<PmeGpuProgramImpl>(deviceContext))
 {
 }
 
 PmeGpuProgram::~PmeGpuProgram() = default;
 
-PmeGpuProgramStorage buildPmeGpuProgram(const gmx_device_info_t* deviceInfo)
+int PmeGpuProgram::warpSize() const
+{
+    return impl_->warpSize();
+}
+
+PmeGpuProgramStorage buildPmeGpuProgram(const DeviceContext& deviceContext)
 {
-    if (!deviceInfo)
-    {
-        // This workaround is only needed for CodePath::CPU dummy in testhardwarecontexts.cpp
-        return nullptr;
-    }
-    return std::make_unique<PmeGpuProgram>(deviceInfo);
+    return std::make_unique<PmeGpuProgram>(deviceContext);
 }
index 3045feb97329857a479b127782fde09e6ed06fe3..f73bd4d0dd606b5b140bb7fc22c0e816be99c544 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 /*! \libinternal \file
  * \brief
- * Declares PmeGpuProgram, which wrap arounds PmeGpuProgramImpl
- * to store permanent PME GPU context-derived data,
- * such as (compiled) kernel handles.
+ * Declares PmeGpuProgram
+ * to store data derived from the GPU context or devices for
+ * PME, such as (compiled) kernel handles and the warp sizes
+ * they work with.
  *
  * \author Aleksei Iupinov <a.yupinov@gmail.com>
  * \ingroup module_ewald
 
 #include <memory>
 
+class DeviceContext;
+
 struct PmeGpuProgramImpl;
-struct gmx_device_info_t;
+struct DeviceInformation;
 
+/*! \libinternal
+ * \brief Stores PME data derived from the GPU context or devices.
+ *
+ * This includes compiled kernel handles and the warp sizes they
+ * work with.
+ */
 class PmeGpuProgram
 {
 public:
-    explicit PmeGpuProgram(const gmx_device_info_t* deviceInfo);
+    /*! \brief Construct a PME GPU program.
+     *
+     * \param[in] deviceContext  GPU context.
+     */
+    explicit PmeGpuProgram(const DeviceContext& deviceContext);
+    //! Destructor
     ~PmeGpuProgram();
 
-    // TODO: design getters for information inside, if needed for PME, and make this private?
+    //! Return the warp size for which the kernels were compiled
+    int warpSize() const;
+
+    // TODO: design more getters for information inside, if needed for PME, and make this private?
+    //! Private impl class
     std::unique_ptr<PmeGpuProgramImpl> impl_;
 };
 
@@ -66,14 +84,9 @@ public:
  */
 using PmeGpuProgramStorage = std::unique_ptr<PmeGpuProgram>;
 
-/*! \brief This is a handle for passing references to PME GPU program data.
- * TODO: it should be a const reference, but for that the PmeGpu types need to be C++
- */
-using PmeGpuProgramHandle = const PmeGpuProgram*;
-
 /*! \brief
  * Factory function used to build persistent PME GPU program for the device at once.
  */
-PmeGpuProgramStorage buildPmeGpuProgram(const gmx_device_info_t* /*deviceInfo*/);
+PmeGpuProgramStorage buildPmeGpuProgram(const DeviceContext& /* deviceContext */);
 
 #endif
index 078f97ee4fe258f0f4989885bdbaa1b381987c18..a6ceac16ee7128b6abb996edba3e040dfbf0cfcf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,8 +45,9 @@
 
 #include "pme_gpu_program_impl.h"
 
-PmeGpuProgramImpl::PmeGpuProgramImpl(const gmx_device_info_t* /*unused*/) :
-    warpSize(0),
+PmeGpuProgramImpl::PmeGpuProgramImpl(const DeviceContext& deviceContext) :
+    deviceContext_(deviceContext),
+    warpSize_(0),
     spreadWorkGroupSize(0),
     gatherWorkGroupSize(0),
     solveMaxWorkGroupSize(0)
index f34f7a2741b335568bc62ab8bd82319f9f92c056..13587938074146861d8013619fc9e7a0346c4f20 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -55,95 +55,128 @@ constexpr int c_pmeOrder = 4;
 constexpr bool c_wrapX = true;
 constexpr bool c_wrapY = true;
 
+constexpr int c_stateA = 0;
+constexpr int c_stateB = 1;
+
 //! PME CUDA kernels forward declarations. Kernels are documented in their respective files.
-template<const int order, const bool computeSplines, const bool spreadCharges, const bool wrapX, const bool wrapY, const bool writeGlobal, const bool orderThreads>
+template<int order, bool computeSplines, bool spreadCharges, bool wrapX, bool wrapY, int mode, bool writeGlobal, ThreadsPerAtom threadsPerAtom>
 void pme_spline_and_spread_kernel(const PmeGpuCudaKernelParams kernelParams);
 
 // Add extern declarations to inform that there will be a definition
 // provided in another translation unit.
-extern template void pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, true, true>(
-        const PmeGpuCudaKernelParams);
-extern template void pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, true, false>(
-        const PmeGpuCudaKernelParams);
-extern template void pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, true, true>(
-        const PmeGpuCudaKernelParams);
-extern template void pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, true, false>(
-        const PmeGpuCudaKernelParams);
-extern template void pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, true, true>(
-        const PmeGpuCudaKernelParams);
-extern template void pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, true, false>(
-        const PmeGpuCudaKernelParams);
-extern template void pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, false, true>(
-        const PmeGpuCudaKernelParams);
-extern template void pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, false, false>(
-        const PmeGpuCudaKernelParams);
+// clang-format off
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::Order>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::Order>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::Order>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 1, false, ThreadsPerAtom::Order>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 1, false, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::Order>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::Order>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::Order>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 2, false, ThreadsPerAtom::Order>(const PmeGpuCudaKernelParams);
+extern template void
+pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 2, false, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
 
-template<GridOrdering gridOrdering, bool computeEnergyAndVirial>
+template<GridOrdering gridOrdering, bool computeEnergyAndVirial, const int gridIndex> /* It is significantly slower to pass gridIndex as a kernel parameter */
 void pme_solve_kernel(const PmeGpuCudaKernelParams kernelParams);
 
 // Add extern declarations to inform that there will be a definition
 // provided in another translation unit.
-extern template void pme_solve_kernel<GridOrdering::XYZ, false>(const PmeGpuCudaKernelParams);
-extern template void pme_solve_kernel<GridOrdering::XYZ, true>(const PmeGpuCudaKernelParams);
-extern template void pme_solve_kernel<GridOrdering::YZX, false>(const PmeGpuCudaKernelParams);
-extern template void pme_solve_kernel<GridOrdering::YZX, true>(const PmeGpuCudaKernelParams);
+// clang-format off
+extern template void pme_solve_kernel<GridOrdering::XYZ, false, c_stateA>(const PmeGpuCudaKernelParams);
+extern template void pme_solve_kernel<GridOrdering::XYZ, true, c_stateA>(const PmeGpuCudaKernelParams);
+extern template void pme_solve_kernel<GridOrdering::YZX, false, c_stateA>(const PmeGpuCudaKernelParams);
+extern template void pme_solve_kernel<GridOrdering::YZX, true, c_stateA>(const PmeGpuCudaKernelParams);
+extern template void pme_solve_kernel<GridOrdering::XYZ, false, c_stateB>(const PmeGpuCudaKernelParams);
+extern template void pme_solve_kernel<GridOrdering::XYZ, true, c_stateB>(const PmeGpuCudaKernelParams);
+extern template void pme_solve_kernel<GridOrdering::YZX, false, c_stateB>(const PmeGpuCudaKernelParams);
+extern template void pme_solve_kernel<GridOrdering::YZX, true, c_stateB>(const PmeGpuCudaKernelParams);
+// clang-format on
 
-template<const int order, const bool overwriteForces, const bool wrapX, const bool wrapY, const bool readGlobal, const bool orderThreads>
+template<int order, bool wrapX, bool wrapY, int nGrids, bool readGlobal, ThreadsPerAtom threadsPerAtom>
 void pme_gather_kernel(const PmeGpuCudaKernelParams kernelParams);
 
 // Add extern declarations to inform that there will be a definition
 // provided in another translation unit.
-extern template void pme_gather_kernel<c_pmeOrder, true, c_wrapX, c_wrapY, true, true>(const PmeGpuCudaKernelParams);
-extern template void pme_gather_kernel<c_pmeOrder, true, c_wrapX, c_wrapY, false, true>(const PmeGpuCudaKernelParams);
-extern template void pme_gather_kernel<c_pmeOrder, false, c_wrapX, c_wrapY, true, true>(const PmeGpuCudaKernelParams);
-extern template void pme_gather_kernel<c_pmeOrder, false, c_wrapX, c_wrapY, false, true>(const PmeGpuCudaKernelParams);
-extern template void pme_gather_kernel<c_pmeOrder, true, c_wrapX, c_wrapY, true, false>(const PmeGpuCudaKernelParams);
-extern template void pme_gather_kernel<c_pmeOrder, true, c_wrapX, c_wrapY, false, false>(const PmeGpuCudaKernelParams);
-extern template void pme_gather_kernel<c_pmeOrder, false, c_wrapX, c_wrapY, true, false>(const PmeGpuCudaKernelParams);
-extern template void pme_gather_kernel<c_pmeOrder, false, c_wrapX, c_wrapY, false, false>(const PmeGpuCudaKernelParams);
+// clang-format off
+extern template void pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::Order>        (const PmeGpuCudaKernelParams);
+extern template void pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 1, false, ThreadsPerAtom::Order>       (const PmeGpuCudaKernelParams);
+extern template void pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::OrderSquared> (const PmeGpuCudaKernelParams);
+extern template void pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 1, false, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+extern template void pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::Order>          (const PmeGpuCudaKernelParams);
+extern template void pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 2, false, ThreadsPerAtom::Order>         (const PmeGpuCudaKernelParams);
+extern template void pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::OrderSquared>   (const PmeGpuCudaKernelParams);
+extern template void pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 2, false, ThreadsPerAtom::OrderSquared>  (const PmeGpuCudaKernelParams);
+// clang-format on
 
-PmeGpuProgramImpl::PmeGpuProgramImpl(const gmx_device_info_t*)
+PmeGpuProgramImpl::PmeGpuProgramImpl(const DeviceContext& deviceContext) :
+    deviceContext_(deviceContext)
 {
     // kernel parameters
-    warpSize              = warp_size;
+    warpSize_             = warp_size;
     spreadWorkGroupSize   = c_spreadMaxThreadsPerBlock;
     solveMaxWorkGroupSize = c_solveMaxThreadsPerBlock;
     gatherWorkGroupSize   = c_gatherMaxThreadsPerBlock;
 
-    /*!
-     * Not all combinations of the splineAndSpread, spline and Spread kernels are required
+    /* Not all combinations of the splineAndSpread, spline and Spread kernels are required
      * If only the spline (without the spread) then it does not make sense not to write the data to global memory
      * Similarly the spread kernel (without the spline) implies that we should read the spline data from global memory
      */
-    splineAndSpreadKernel =
-            pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, false, false>;
-    splineAndSpreadKernelThPerAtom4 =
-            pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, false, true>;
-    splineAndSpreadKernelWriteSplines =
-            pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, true, false>;
-    splineAndSpreadKernelWriteSplinesThPerAtom4 =
-            pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, true, true>;
-    splineKernel = pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, true, false>;
-    splineKernelThPerAtom4 =
-            pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, true, true>;
-    spreadKernel = pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, true, false>;
-    spreadKernelThPerAtom4 =
-            pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, true, true>;
-    gatherKernel            = pme_gather_kernel<c_pmeOrder, true, c_wrapX, c_wrapY, false, false>;
-    gatherKernelThPerAtom4  = pme_gather_kernel<c_pmeOrder, true, c_wrapX, c_wrapY, false, true>;
-    gatherKernelReadSplines = pme_gather_kernel<c_pmeOrder, true, c_wrapX, c_wrapY, true, false>;
-    gatherKernelReadSplinesThPerAtom4 = pme_gather_kernel<c_pmeOrder, true, c_wrapX, c_wrapY, true, true>;
-    gatherReduceWithInputKernel = pme_gather_kernel<c_pmeOrder, false, c_wrapX, c_wrapY, false, false>;
-    gatherReduceWithInputKernelThPerAtom4 =
-            pme_gather_kernel<c_pmeOrder, false, c_wrapX, c_wrapY, false, true>;
-    gatherReduceWithInputKernelReadSplines =
-            pme_gather_kernel<c_pmeOrder, false, c_wrapX, c_wrapY, true, false>;
-    gatherReduceWithInputKernelReadSplinesThPerAtom4 =
-            pme_gather_kernel<c_pmeOrder, false, c_wrapX, c_wrapY, true, true>;
-    solveXYZKernel       = pme_solve_kernel<GridOrdering::XYZ, false>;
-    solveXYZEnergyKernel = pme_solve_kernel<GridOrdering::XYZ, true>;
-    solveYZXKernel       = pme_solve_kernel<GridOrdering::YZX, false>;
-    solveYZXEnergyKernel = pme_solve_kernel<GridOrdering::YZX, true>;
+    // clang-format off
+    splineAndSpreadKernelSingle                       = pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 1, false, ThreadsPerAtom::OrderSquared>;
+    splineAndSpreadKernelThPerAtom4Single             = pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 1, false, ThreadsPerAtom::Order>;
+    splineAndSpreadKernelWriteSplinesSingle           = pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::OrderSquared>;
+    splineAndSpreadKernelWriteSplinesThPerAtom4Single = pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::Order>;
+    splineKernelSingle                                = pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::OrderSquared>;
+    splineKernelThPerAtom4Single                      = pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::Order>;
+    spreadKernelSingle                                = pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::OrderSquared>;
+    spreadKernelThPerAtom4Single                      = pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::Order>;
+    splineAndSpreadKernelDual                         = pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 2, false, ThreadsPerAtom::OrderSquared>;
+    splineAndSpreadKernelThPerAtom4Dual               = pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 2, false, ThreadsPerAtom::Order>;
+    splineAndSpreadKernelWriteSplinesDual             = pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::OrderSquared>;
+    splineAndSpreadKernelWriteSplinesThPerAtom4Dual   = pme_spline_and_spread_kernel<c_pmeOrder, true, true, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::Order>;
+    splineKernelDual                                  = pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::OrderSquared>;
+    splineKernelThPerAtom4Dual                        = pme_spline_and_spread_kernel<c_pmeOrder, true, false, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::Order>;
+    spreadKernelDual                                  = pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::OrderSquared>;
+    spreadKernelThPerAtom4Dual                        = pme_spline_and_spread_kernel<c_pmeOrder, false, true, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::Order>;
+    gatherKernelSingle                                = pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 1, false, ThreadsPerAtom::OrderSquared>;
+    gatherKernelThPerAtom4Single                      = pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 1, false, ThreadsPerAtom::Order>;
+    gatherKernelReadSplinesSingle                     = pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::OrderSquared>;
+    gatherKernelReadSplinesThPerAtom4Single           = pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 1, true, ThreadsPerAtom::Order>;
+    gatherKernelDual                                  = pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 2, false, ThreadsPerAtom::OrderSquared>;
+    gatherKernelThPerAtom4Dual                        = pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 2, false, ThreadsPerAtom::Order>;
+    gatherKernelReadSplinesDual                       = pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::OrderSquared>;
+    gatherKernelReadSplinesThPerAtom4Dual             = pme_gather_kernel<c_pmeOrder, c_wrapX, c_wrapY, 2, true, ThreadsPerAtom::Order>;
+    solveXYZKernelA                                   = pme_solve_kernel<GridOrdering::XYZ, false, c_stateA>;
+    solveXYZEnergyKernelA                             = pme_solve_kernel<GridOrdering::XYZ, true, c_stateA>;
+    solveYZXKernelA                                   = pme_solve_kernel<GridOrdering::YZX, false, c_stateA>;
+    solveYZXEnergyKernelA                             = pme_solve_kernel<GridOrdering::YZX, true, c_stateA>;
+    solveXYZKernelB                                   = pme_solve_kernel<GridOrdering::XYZ, false, c_stateB>;
+    solveXYZEnergyKernelB                             = pme_solve_kernel<GridOrdering::XYZ, true, c_stateB>;
+    solveYZXKernelB                                   = pme_solve_kernel<GridOrdering::YZX, false, c_stateB>;
+    solveYZXEnergyKernelB                             = pme_solve_kernel<GridOrdering::YZX, true, c_stateB>;
+    // clang-format on
 }
 
 PmeGpuProgramImpl::~PmeGpuProgramImpl() {}
index 8867ea0bdcacc8c123f5e9cfce79c7f071c735f0..6255e460546fb6965fa94391777c3ba91aee2789 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "config.h"
 
-#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/gpu_utils/device_context.h"
 #include "gromacs/utility/classhelpers.h"
 
-struct gmx_device_info_t;
+class DeviceContext;
+struct DeviceInformation;
 
 /*! \internal
  * \brief
@@ -67,22 +68,20 @@ struct gmx_device_info_t;
  * This also doesn't manage cuFFT/clFFT kernels, which depend on the PME grid dimensions.
  *
  * TODO: pass cl_context to the constructor and not create it inside.
- * See also Redmine #2522.
+ * See also Issue #2522.
  */
 struct PmeGpuProgramImpl
 {
     /*! \brief
      * This is a handle to the GPU context, which is just a dummy in CUDA,
      * but is created/destroyed by this class in OpenCL.
-     * TODO: Later we want to be able to own the context at a higher level and not here,
-     * but this class would still need the non-owning context handle to build the kernels.
      */
-    DeviceContext context;
+    const DeviceContext& deviceContext_;
 
     //! Conveniently all the PME kernels use the same single argument type
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
     using PmeKernelHandle = void (*)(const struct PmeGpuCudaKernelParams);
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
     using PmeKernelHandle = cl_kernel;
 #else
     using PmeKernelHandle = void*;
@@ -94,7 +93,7 @@ struct PmeGpuProgramImpl
      * For CUDA, this is a static value that comes from gromacs/gpu_utils/cuda_arch_utils.cuh;
      * for OpenCL, we have to query it dynamically.
      */
-    size_t warpSize;
+    size_t warpSize_;
 
     //@{
     /**
@@ -105,17 +104,28 @@ struct PmeGpuProgramImpl
      *   or recalculated in the gather.
      * Spreading kernels also have hardcoded X/Y indices wrapping parameters,
      * as a placeholder for implementing 1/2D decomposition.
+     * The kernels are templated separately for spreading on one grid (one or
+     * two sets of coefficients) or on two grids (required for energy and virial
+     * calculations).
      */
     size_t spreadWorkGroupSize;
 
-    PmeKernelHandle splineKernel;
-    PmeKernelHandle splineKernelThPerAtom4;
-    PmeKernelHandle spreadKernel;
-    PmeKernelHandle spreadKernelThPerAtom4;
-    PmeKernelHandle splineAndSpreadKernel;
-    PmeKernelHandle splineAndSpreadKernelThPerAtom4;
-    PmeKernelHandle splineAndSpreadKernelWriteSplines;
-    PmeKernelHandle splineAndSpreadKernelWriteSplinesThPerAtom4;
+    PmeKernelHandle splineKernelSingle;
+    PmeKernelHandle splineKernelThPerAtom4Single;
+    PmeKernelHandle spreadKernelSingle;
+    PmeKernelHandle spreadKernelThPerAtom4Single;
+    PmeKernelHandle splineAndSpreadKernelSingle;
+    PmeKernelHandle splineAndSpreadKernelThPerAtom4Single;
+    PmeKernelHandle splineAndSpreadKernelWriteSplinesSingle;
+    PmeKernelHandle splineAndSpreadKernelWriteSplinesThPerAtom4Single;
+    PmeKernelHandle splineKernelDual;
+    PmeKernelHandle splineKernelThPerAtom4Dual;
+    PmeKernelHandle spreadKernelDual;
+    PmeKernelHandle spreadKernelThPerAtom4Dual;
+    PmeKernelHandle splineAndSpreadKernelDual;
+    PmeKernelHandle splineAndSpreadKernelThPerAtom4Dual;
+    PmeKernelHandle splineAndSpreadKernelWriteSplinesDual;
+    PmeKernelHandle splineAndSpreadKernelWriteSplinesThPerAtom4Dual;
     //@}
 
     //@{
@@ -123,40 +133,50 @@ struct PmeGpuProgramImpl
      * it can either reduce with previous forces in the host buffer, or ignore them.
      * Also similarly to the gather we can use either order(4) or order*order (16) threads per atom
      * and either recalculate the splines or read the ones written by the spread
+     * The kernels are templated separately for using one or two grids (required for
+     * calculating energies and virial).
      */
     size_t gatherWorkGroupSize;
 
-    PmeKernelHandle gatherReduceWithInputKernel;
-    PmeKernelHandle gatherReduceWithInputKernelThPerAtom4;
-    PmeKernelHandle gatherKernel;
-    PmeKernelHandle gatherKernelThPerAtom4;
-    PmeKernelHandle gatherReduceWithInputKernelReadSplines;
-    PmeKernelHandle gatherReduceWithInputKernelReadSplinesThPerAtom4;
-    PmeKernelHandle gatherKernelReadSplines;
-    PmeKernelHandle gatherKernelReadSplinesThPerAtom4;
+    PmeKernelHandle gatherKernelSingle;
+    PmeKernelHandle gatherKernelThPerAtom4Single;
+    PmeKernelHandle gatherKernelReadSplinesSingle;
+    PmeKernelHandle gatherKernelReadSplinesThPerAtom4Single;
+    PmeKernelHandle gatherKernelDual;
+    PmeKernelHandle gatherKernelThPerAtom4Dual;
+    PmeKernelHandle gatherKernelReadSplinesDual;
+    PmeKernelHandle gatherKernelReadSplinesThPerAtom4Dual;
     //@}
 
     //@{
     /** Solve kernel doesn't care about the interpolation order, but can optionally
      * compute energy and virial, and supports XYZ and YZX grid orderings.
+     * The kernels are templated separately for grids in state A and B.
      */
     size_t solveMaxWorkGroupSize;
 
-    PmeKernelHandle solveYZXKernel;
-    PmeKernelHandle solveXYZKernel;
-    PmeKernelHandle solveYZXEnergyKernel;
-    PmeKernelHandle solveXYZEnergyKernel;
+    PmeKernelHandle solveYZXKernelA;
+    PmeKernelHandle solveXYZKernelA;
+    PmeKernelHandle solveYZXEnergyKernelA;
+    PmeKernelHandle solveXYZEnergyKernelA;
+    PmeKernelHandle solveYZXKernelB;
+    PmeKernelHandle solveXYZKernelB;
+    PmeKernelHandle solveYZXEnergyKernelB;
+    PmeKernelHandle solveXYZEnergyKernelB;
     //@}
 
     PmeGpuProgramImpl() = delete;
     //! Constructor for the given device
-    explicit PmeGpuProgramImpl(const gmx_device_info_t* deviceInfo);
+    explicit PmeGpuProgramImpl(const DeviceContext& deviceContext);
     ~PmeGpuProgramImpl();
     GMX_DISALLOW_COPY_AND_ASSIGN(PmeGpuProgramImpl);
 
+    //! Return the warp size for which the kernels were compiled
+    int warpSize() const { return warpSize_; }
+
 private:
     // Compiles kernels, if supported. Called by the constructor.
-    void compileKernels(const gmx_device_info_t* deviceInfo);
+    void compileKernels(const DeviceInformation& deviceInfo);
 };
 
 #endif
index 800f43ea9ec40732c1a8c0bac21b05276fd3b4d8..c88baf40e5fca4782d2b4be594ef934d69afd4fc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "pme_gpu_types_host.h"
 #include "pme_grid.h"
 
-PmeGpuProgramImpl::PmeGpuProgramImpl(const gmx_device_info_t* deviceInfo)
+PmeGpuProgramImpl::PmeGpuProgramImpl(const DeviceContext& deviceContext) :
+    deviceContext_(deviceContext)
 {
-    // Context creation (which should happen outside of this class: #2522)
-    cl_platform_id        platformId = deviceInfo->ocl_gpu_id.ocl_platform_id;
-    cl_device_id          deviceId   = deviceInfo->ocl_gpu_id.ocl_device_id;
-    cl_context_properties contextProperties[3];
-    contextProperties[0] = CL_CONTEXT_PLATFORM;
-    contextProperties[1] = reinterpret_cast<cl_context_properties>(platformId);
-    contextProperties[2] = 0; /* Terminates the list of properties */
-
-    cl_int clError;
-    context = clCreateContext(contextProperties, 1, &deviceId, nullptr, nullptr, &clError);
-    if (clError != CL_SUCCESS)
-    {
-        const std::string errorString = gmx::formatString(
-                "Failed to create context for PME on GPU #%s:\n OpenCL error %d: %s",
-                deviceInfo->device_name, clError, ocl_get_error_string(clError).c_str());
-        GMX_THROW(gmx::InternalError(errorString));
-    }
-
+    const DeviceInformation& deviceInfo = deviceContext.deviceInfo();
     // kernel parameters
-    warpSize = gmx::ocl::getDeviceWarpSize(context, deviceId);
+    warpSize_ = gmx::ocl::getDeviceWarpSize(deviceContext_.context(), deviceInfo.oclDeviceId);
     // TODO: for Intel ideally we'd want to set these based on the compiler warp size
     // but given that we've done no tuning for Intel iGPU, this is as good as anything.
-    spreadWorkGroupSize = std::min(c_spreadMaxWarpsPerBlock * warpSize, deviceInfo->maxWorkGroupSize);
-    solveMaxWorkGroupSize = std::min(c_solveMaxWarpsPerBlock * warpSize, deviceInfo->maxWorkGroupSize);
-    gatherWorkGroupSize = std::min(c_gatherMaxWarpsPerBlock * warpSize, deviceInfo->maxWorkGroupSize);
+    spreadWorkGroupSize = std::min(c_spreadMaxWarpsPerBlock * warpSize_, deviceInfo.maxWorkGroupSize);
+    solveMaxWorkGroupSize = std::min(c_solveMaxWarpsPerBlock * warpSize_, deviceInfo.maxWorkGroupSize);
+    gatherWorkGroupSize = std::min(c_gatherMaxWarpsPerBlock * warpSize_, deviceInfo.maxWorkGroupSize);
 
     compileKernels(deviceInfo);
 }
@@ -88,16 +72,22 @@ PmeGpuProgramImpl::~PmeGpuProgramImpl()
 {
     // TODO: log releasing errors
     cl_int gmx_used_in_debug stat = 0;
-    stat |= clReleaseKernel(splineAndSpreadKernel);
-    stat |= clReleaseKernel(splineKernel);
-    stat |= clReleaseKernel(spreadKernel);
-    stat |= clReleaseKernel(gatherKernel);
-    stat |= clReleaseKernel(gatherReduceWithInputKernel);
-    stat |= clReleaseKernel(solveXYZKernel);
-    stat |= clReleaseKernel(solveXYZEnergyKernel);
-    stat |= clReleaseKernel(solveYZXKernel);
-    stat |= clReleaseKernel(solveYZXEnergyKernel);
-    stat |= clReleaseContext(context);
+    stat |= clReleaseKernel(splineAndSpreadKernelSingle);
+    stat |= clReleaseKernel(splineKernelSingle);
+    stat |= clReleaseKernel(spreadKernelSingle);
+    stat |= clReleaseKernel(splineAndSpreadKernelDual);
+    stat |= clReleaseKernel(splineKernelDual);
+    stat |= clReleaseKernel(spreadKernelDual);
+    stat |= clReleaseKernel(gatherKernelSingle);
+    stat |= clReleaseKernel(gatherKernelDual);
+    stat |= clReleaseKernel(solveXYZKernelA);
+    stat |= clReleaseKernel(solveXYZEnergyKernelA);
+    stat |= clReleaseKernel(solveYZXKernelA);
+    stat |= clReleaseKernel(solveYZXEnergyKernelA);
+    stat |= clReleaseKernel(solveXYZKernelB);
+    stat |= clReleaseKernel(solveXYZEnergyKernelB);
+    stat |= clReleaseKernel(solveYZXKernelB);
+    stat |= clReleaseKernel(solveYZXEnergyKernelB);
     GMX_ASSERT(stat == CL_SUCCESS,
                gmx::formatString("Failed to release PME OpenCL resources %d: %s", stat,
                                  ocl_get_error_string(stat).c_str())
@@ -109,26 +99,30 @@ PmeGpuProgramImpl::~PmeGpuProgramImpl()
  * On Intel the exec width/warp is decided at compile-time and can be
  * smaller than the minimum order^2 required in spread/gather ATM which
  * we need to check for.
+ *
+ * Due to the one thread per atom and order=4 implementation
+ * constraints, order^2 threads should execute without synchronization
+ * needed.
  */
-static void checkRequiredWarpSize(cl_kernel kernel, const char* kernelName, const gmx_device_info_t* deviceInfo)
+static void checkRequiredWarpSize(cl_kernel kernel, const char* kernelName, const DeviceInformation& deviceInfo)
 {
-    if (deviceInfo->vendor_e == OCL_VENDOR_INTEL)
+    if (deviceInfo.deviceVendor == DeviceVendor::Intel)
     {
-        size_t kernelWarpSize = gmx::ocl::getKernelWarpSize(kernel, deviceInfo->ocl_gpu_id.ocl_device_id);
-
-        if (kernelWarpSize < c_pmeSpreadGatherMinWarpSize)
+        int       kernelWarpSize    = gmx::ocl::getKernelWarpSize(kernel, deviceInfo.oclDeviceId);
+        const int minKernelWarpSize = c_pmeGpuOrder * c_pmeGpuOrder;
+        if (kernelWarpSize < minKernelWarpSize)
         {
             const std::string errorString = gmx::formatString(
                     "PME OpenCL kernels require >=%d execution width, but the %s kernel "
-                    "has been compiled for the device %s to a %zu width and therefore it can not "
+                    "has been compiled for the device %s to a %d width and therefore it can not "
                     "execute correctly.",
-                    c_pmeSpreadGatherMinWarpSize, kernelName, deviceInfo->device_name, kernelWarpSize);
+                    minKernelWarpSize, kernelName, deviceInfo.device_name, kernelWarpSize);
             GMX_THROW(gmx::InternalError(errorString));
         }
     }
 }
 
-void PmeGpuProgramImpl::compileKernels(const gmx_device_info_t* deviceInfo)
+void PmeGpuProgramImpl::compileKernels(const DeviceInformation& deviceInfo)
 {
     // We might consider storing program as a member variable if it's needed later
     cl_program program = nullptr;
@@ -142,12 +136,10 @@ void PmeGpuProgramImpl::compileKernels(const gmx_device_info_t* deviceInfo)
         const std::string commonDefines = gmx::formatString(
                 "-Dwarp_size=%zd "
                 "-Dorder=%d "
-                "-DatomsPerWarp=%zd "
                 "-DthreadsPerAtom=%d "
                 // forwarding from pme_grid.h, used for spline computation table sizes only
                 "-Dc_pmeMaxUnitcellShift=%f "
                 // forwarding PME behavior constants from pme_gpu_constants.h
-                "-Dc_usePadding=%d "
                 "-Dc_skipNeutralAtoms=%d "
                 "-Dc_virialAndEnergyCount=%d "
                 // forwarding kernel work sizes
@@ -158,28 +150,28 @@ void PmeGpuProgramImpl::compileKernels(const gmx_device_info_t* deviceInfo)
                 "-DDIM=%d -DXX=%d -DYY=%d -DZZ=%d "
                 // decomposition parameter placeholders
                 "-DwrapX=true -DwrapY=true ",
-                warpSize, c_pmeGpuOrder, warpSize / c_pmeSpreadGatherThreadsPerAtom,
-                c_pmeSpreadGatherThreadsPerAtom, static_cast<float>(c_pmeMaxUnitcellShift),
-                static_cast<int>(c_usePadding), static_cast<int>(c_skipNeutralAtoms), c_virialAndEnergyCount,
-                spreadWorkGroupSize, solveMaxWorkGroupSize, gatherWorkGroupSize, DIM, XX, YY, ZZ);
+                warpSize_, c_pmeGpuOrder, c_pmeGpuOrder * c_pmeGpuOrder,
+                static_cast<float>(c_pmeMaxUnitcellShift), static_cast<int>(c_skipNeutralAtoms),
+                c_virialAndEnergyCount, spreadWorkGroupSize, solveMaxWorkGroupSize,
+                gatherWorkGroupSize, DIM, XX, YY, ZZ);
         try
         {
             /* TODO when we have a proper MPI-aware logging module,
                the log output here should be written there */
-            program = gmx::ocl::compileProgram(stderr, "gromacs/ewald", "pme_program.cl", commonDefines,
-                                               context, deviceInfo->ocl_gpu_id.ocl_device_id,
-                                               deviceInfo->vendor_e);
+            program = gmx::ocl::compileProgram(stderr, "gromacs/ewald", "pme_program.cl",
+                                               commonDefines, deviceContext_.context(),
+                                               deviceInfo.oclDeviceId, deviceInfo.deviceVendor);
         }
         catch (gmx::GromacsException& e)
         {
             e.prependContext(gmx::formatString("Failed to compile PME kernels for GPU #%s\n",
-                                               deviceInfo->device_name));
+                                               deviceInfo.device_name));
             throw;
         }
     }
     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 
-    constexpr cl_uint expectedKernelCount = 9;
+    constexpr cl_uint expectedKernelCount = 18;
     // Has to be equal or larger than the number of kernel instances.
     // If it is not, CL_INVALID_VALUE will be thrown.
     std::vector<cl_kernel> kernels(expectedKernelCount, nullptr);
@@ -189,7 +181,7 @@ void PmeGpuProgramImpl::compileKernels(const gmx_device_info_t* deviceInfo)
     {
         const std::string errorString = gmx::formatString(
                 "Failed to create kernels for PME on GPU #%s:\n OpenCL error %d: %s",
-                deviceInfo->device_name, clError, ocl_get_error_string(clError).c_str());
+                deviceInfo.device_name, clError, ocl_get_error_string(clError).c_str());
         GMX_THROW(gmx::InternalError(errorString));
     }
     kernels.resize(actualKernelCount);
@@ -203,54 +195,85 @@ void PmeGpuProgramImpl::compileKernels(const gmx_device_info_t* deviceInfo)
         {
             const std::string errorString = gmx::formatString(
                     "Failed to parse kernels for PME on GPU #%s:\n OpenCL error %d: %s",
-                    deviceInfo->device_name, clError, ocl_get_error_string(clError).c_str());
+                    deviceInfo.device_name, clError, ocl_get_error_string(clError).c_str());
             GMX_THROW(gmx::InternalError(errorString));
         }
 
         // The names below must correspond to those defined in pme_program.cl
         // TODO use a map with string key instead?
-        if (!strcmp(kernelNamesBuffer.data(), "pmeSplineKernel"))
+        if (!strcmp(kernelNamesBuffer.data(), "pmeSplineKernelSingle"))
+        {
+            splineKernelSingle = kernel;
+        }
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSplineAndSpreadKernelSingle"))
+        {
+            splineAndSpreadKernelSingle             = kernel;
+            splineAndSpreadKernelWriteSplinesSingle = kernel;
+            checkRequiredWarpSize(splineAndSpreadKernelSingle, kernelNamesBuffer.data(), deviceInfo);
+        }
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSpreadKernelSingle"))
+        {
+            spreadKernelSingle = kernel;
+            checkRequiredWarpSize(spreadKernelSingle, kernelNamesBuffer.data(), deviceInfo);
+        }
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSplineKernelDual"))
+        {
+            splineKernelDual = kernel;
+        }
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSplineAndSpreadKernelDual"))
+        {
+            splineAndSpreadKernelDual             = kernel;
+            splineAndSpreadKernelWriteSplinesDual = kernel;
+            checkRequiredWarpSize(splineAndSpreadKernelDual, kernelNamesBuffer.data(), deviceInfo);
+        }
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSpreadKernelDual"))
+        {
+            spreadKernelDual = kernel;
+            checkRequiredWarpSize(spreadKernelDual, kernelNamesBuffer.data(), deviceInfo);
+        }
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeGatherKernelSingle"))
+        {
+            gatherKernelSingle            = kernel;
+            gatherKernelReadSplinesSingle = kernel;
+            checkRequiredWarpSize(gatherKernelSingle, kernelNamesBuffer.data(), deviceInfo);
+        }
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeGatherKernelDual"))
         {
-            splineKernel = kernel;
+            gatherKernelDual            = kernel;
+            gatherKernelReadSplinesDual = kernel;
+            checkRequiredWarpSize(gatherKernelDual, kernelNamesBuffer.data(), deviceInfo);
         }
-        else if (!strcmp(kernelNamesBuffer.data(), "pmeSplineAndSpreadKernel"))
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveYZXKernelA"))
         {
-            splineAndSpreadKernel             = kernel;
-            splineAndSpreadKernelWriteSplines = kernel;
-            checkRequiredWarpSize(splineAndSpreadKernel, kernelNamesBuffer.data(), deviceInfo);
+            solveYZXKernelA = kernel;
         }
-        else if (!strcmp(kernelNamesBuffer.data(), "pmeSpreadKernel"))
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveYZXEnergyKernelA"))
         {
-            spreadKernel = kernel;
-            checkRequiredWarpSize(spreadKernel, kernelNamesBuffer.data(), deviceInfo);
+            solveYZXEnergyKernelA = kernel;
         }
-        else if (!strcmp(kernelNamesBuffer.data(), "pmeGatherKernel"))
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveXYZKernelA"))
         {
-            gatherKernel            = kernel;
-            gatherKernelReadSplines = kernel;
-            checkRequiredWarpSize(gatherKernel, kernelNamesBuffer.data(), deviceInfo);
+            solveXYZKernelA = kernel;
         }
-        else if (!strcmp(kernelNamesBuffer.data(), "pmeGatherReduceWithInputKernel"))
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveXYZEnergyKernelA"))
         {
-            gatherReduceWithInputKernel            = kernel;
-            gatherReduceWithInputKernelReadSplines = kernel;
-            checkRequiredWarpSize(gatherReduceWithInputKernel, kernelNamesBuffer.data(), deviceInfo);
+            solveXYZEnergyKernelA = kernel;
         }
-        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveYZXKernel"))
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveYZXKernelB"))
         {
-            solveYZXKernel = kernel;
+            solveYZXKernelB = kernel;
         }
-        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveYZXEnergyKernel"))
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveYZXEnergyKernelB"))
         {
-            solveYZXEnergyKernel = kernel;
+            solveYZXEnergyKernelB = kernel;
         }
-        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveXYZKernel"))
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveXYZKernelB"))
         {
-            solveXYZKernel = kernel;
+            solveXYZKernelB = kernel;
         }
-        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveXYZEnergyKernel"))
+        else if (!strcmp(kernelNamesBuffer.data(), "pmeSolveXYZEnergyKernelB"))
         {
-            solveXYZEnergyKernel = kernel;
+            solveXYZEnergyKernelB = kernel;
         }
     }
     clReleaseProgram(program);
diff --git a/src/gromacs/ewald/pme_gpu_settings.h b/src/gromacs/ewald/pme_gpu_settings.h
new file mode 100644 (file)
index 0000000..d6fa414
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Defines the PME GPU settings data structures.
+ * \todo Some renaming/refactoring, which does not impair the performance:
+ * -- PmeGpuSettings -> PmeGpuTasks
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_GPU_SETTINGS_H
+#define GMX_EWALD_PME_GPU_SETTINGS_H
+
+#include "gromacs/gpu_utils/gpu_utils.h" // for GpuApiCallBehavior
+
+enum class ThreadsPerAtom : int;
+
+/*! \internal \brief
+ * The PME GPU settings structure, included in the main PME GPU structure by value.
+ */
+struct PmeGpuSettings
+{
+    /* Permanent settings set on initialization */
+    /*! \brief A boolean which tells if the solving is performed on GPU. Currently always true */
+    bool performGPUSolve;
+    /*! \brief A boolean which tells if the gathering is performed on GPU. Currently always true */
+    bool performGPUGather;
+    /*! \brief A boolean which tells if the FFT is performed on GPU. Currently true for a single MPI rank. */
+    bool performGPUFFT;
+    /*! \brief A convenience boolean which tells if PME decomposition is used. */
+    bool useDecomposition;
+    /*! \brief True if PME forces are reduced on-GPU, false if reduction is done on the CPU;
+     *  in the former case transfer does not need to happen.
+     *
+     *  Note that this flag may change per-step.
+     */
+    bool useGpuForceReduction;
+
+    /*! \brief A boolean which tells if any PME GPU stage should copy all of its outputs to the
+     * host. Only intended to be used by the test framework.
+     */
+    bool copyAllOutputs;
+    /*! \brief An enum which tells whether most PME GPU D2H/H2D data transfers should be synchronous. */
+    GpuApiCallBehavior transferKind;
+    /*! \brief
+     *  Controls whether we use order (i.e. 4) threads per atom for the GPU
+     *  or order*order (i.e. 16) threads per atom.
+     *
+     *  Currently ThreadsPerAtom::Order is only supported by CUDA.
+     */
+    ThreadsPerAtom threadsPerAtom;
+    /*! \brief
+     * Currently only supported by CUDA.
+     * Controls if we should recalculate the splines in the gather or
+     * save the values in the spread and reload in the gather.
+     */
+    bool recalculateSplines;
+};
+
+#endif
diff --git a/src/gromacs/ewald/pme_gpu_staging.h b/src/gromacs/ewald/pme_gpu_staging.h
new file mode 100644 (file)
index 0000000..7439027
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Defines the host-side PME GPU data structures.
+ * \todo Some renaming/refactoring, which does not impair the performance:
+ * -- bringing the function names up to guidelines
+ * -- PmeGpuSettings -> PmeGpuTasks
+ * -- refining GPU notation application (#2053)
+ * -- renaming coefficients to charges (?)
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_GPU_STAGING_H
+#define GMX_EWALD_PME_GPU_STAGING_H
+
+#include <vector>
+
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/vectypes.h"
+
+#ifndef NUM_STATES
+//! Number of FEP states.
+#    define NUM_STATES 2
+#endif
+
+/*! \internal \brief
+ * The PME GPU intermediate buffers structure, included in the main PME GPU structure by value.
+ * Buffers are managed by the PME GPU module.
+ */
+struct PmeGpuStaging
+{
+    //! Host-side force buffer
+    gmx::PaddedHostVector<gmx::RVec> h_forces;
+
+    /*! \brief Virial and energy intermediate host-side buffer. Size is PME_GPU_VIRIAL_AND_ENERGY_COUNT. */
+    float* h_virialAndEnergy[NUM_STATES];
+    /*! \brief B-spline values intermediate host-side buffer. */
+    float* h_splineModuli[NUM_STATES];
+
+    /*! \brief Pointer to the host memory with B-spline values. Only used for host-side gather, or unit tests */
+    float* h_theta;
+    /*! \brief Pointer to the host memory with B-spline derivative values. Only used for host-side gather, or unit tests */
+    float* h_dtheta;
+    /*! \brief Pointer to the host memory with ivec atom gridline indices. Only used for host-side gather, or unit tests */
+    int* h_gridlineIndices;
+};
+
+#endif
index d725cd968a3307f7387c29419b1a7adb5a22292e..3a1f45746857d78d5a099b83507c38b47b8c08e7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "pme_gpu_types_host.h"
 #include "pme_gpu_types_host_impl.h"
 
-/*! \brief
- * Tells if CUDA-based performance tracking is enabled for PME.
- *
- * \param[in] pmeGpu         The PME GPU data structure.
- * \returns                  True if timings are enabled, false otherwise.
- */
-inline bool pme_gpu_timings_enabled(const PmeGpu* pmeGpu)
+bool pme_gpu_timings_enabled(const PmeGpu* pmeGpu)
 {
     return pmeGpu->archSpecific->useTiming;
 }
@@ -67,20 +61,8 @@ void pme_gpu_start_timing(const PmeGpu* pmeGpu, size_t PMEStageId)
     {
         GMX_ASSERT(PMEStageId < pmeGpu->archSpecific->timingEvents.size(),
                    "Wrong PME GPU timing event index");
-        pmeGpu->archSpecific->timingEvents[PMEStageId].openTimingRegion(pmeGpu->archSpecific->pmeStream);
-    }
-}
-
-CommandEvent* pme_gpu_fetch_timing_event(const PmeGpu* pmeGpu, size_t PMEStageId)
-{
-    CommandEvent* timingEvent = nullptr;
-    if (pme_gpu_timings_enabled(pmeGpu))
-    {
-        GMX_ASSERT(PMEStageId < pmeGpu->archSpecific->timingEvents.size(),
-                   "Wrong PME GPU timing event index");
-        timingEvent = pmeGpu->archSpecific->timingEvents[PMEStageId].fetchNextEvent();
+        pmeGpu->archSpecific->timingEvents[PMEStageId].openTimingRegion(pmeGpu->archSpecific->pmeStream_);
     }
-    return timingEvent;
 }
 
 void pme_gpu_stop_timing(const PmeGpu* pmeGpu, size_t PMEStageId)
@@ -89,7 +71,7 @@ void pme_gpu_stop_timing(const PmeGpu* pmeGpu, size_t PMEStageId)
     {
         GMX_ASSERT(PMEStageId < pmeGpu->archSpecific->timingEvents.size(),
                    "Wrong PME GPU timing event index");
-        pmeGpu->archSpecific->timingEvents[PMEStageId].closeTimingRegion(pmeGpu->archSpecific->pmeStream);
+        pmeGpu->archSpecific->timingEvents[PMEStageId].closeTimingRegion(pmeGpu->archSpecific->pmeStream_);
     }
 }
 
@@ -123,17 +105,18 @@ void pme_gpu_reinit_timings(const PmeGpu* pmeGpu)
     {
         pmeGpu->archSpecific->activeTimers.clear();
         pmeGpu->archSpecific->activeTimers.insert(gtPME_SPLINEANDSPREAD);
+        const auto& settings = pme_gpu_settings(pmeGpu);
         // TODO: no separate gtPME_SPLINE and gtPME_SPREAD as they are not used currently
-        if (pme_gpu_performs_FFT(pmeGpu))
+        if (settings.performGPUFFT)
         {
             pmeGpu->archSpecific->activeTimers.insert(gtPME_FFT_C2R);
             pmeGpu->archSpecific->activeTimers.insert(gtPME_FFT_R2C);
         }
-        if (pme_gpu_performs_solve(pmeGpu))
+        if (settings.performGPUSolve)
         {
             pmeGpu->archSpecific->activeTimers.insert(gtPME_SOLVE);
         }
-        if (pme_gpu_performs_gather(pmeGpu))
+        if (settings.performGPUGather)
         {
             pmeGpu->archSpecific->activeTimers.insert(gtPME_GATHER);
         }
index b2d09e21f7a56b9b79c84d333442c74a04cd4d52..f7c222b6b2ec7a32e57c13f4fe05522d28cc85e3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_EWALD_PME_GPU_TIMINGS_H
 #define GMX_EWALD_PME_GPU_TIMINGS_H
 
-#include "config.h"
-
-#if GMX_GPU == GMX_GPU_CUDA
-#    include "gromacs/gpu_utils/gputraits.cuh"
-#elif GMX_GPU == GMX_GPU_OPENCL
-#    include "gromacs/gpu_utils/gputraits_ocl.h"
-#endif
+#include <cstddef>
 
+struct gmx_wallclock_gpu_pme_t;
 struct PmeGpu;
 
 /*! \libinternal \brief
@@ -62,20 +57,48 @@ struct PmeGpu;
 void pme_gpu_start_timing(const PmeGpu* pmeGpu, size_t PMEStageId);
 
 /*! \libinternal \brief
- * Returns raw timing event from the corresponding GpuRegionTimer (if timings are enabled).
- * In CUDA result can be nullptr stub, per GpuRegionTimer implementation.
+ * Stops timing the certain PME GPU stage during a single computation (if timings are enabled).
  *
  * \param[in] pmeGpu         The PME GPU data structure.
  * \param[in] PMEStageId     The PME GPU stage gtPME_ index from the enum in src/gromacs/timing/gpu_timing.h
  */
-CommandEvent* pme_gpu_fetch_timing_event(const PmeGpu* pmeGpu, size_t PMEStageId);
+void pme_gpu_stop_timing(const PmeGpu* pmeGpu, size_t PMEStageId);
+
+/*! \brief
+ * Tells if CUDA-based performance tracking is enabled for PME.
+ *
+ * \param[in] pmeGpu         The PME GPU data structure.
+ * \returns                  True if timings are enabled, false otherwise.
+ */
+bool pme_gpu_timings_enabled(const PmeGpu* pmeGpu);
 
 /*! \libinternal \brief
- * Stops timing the certain PME GPU stage during a single computation (if timings are enabled).
+ * Finalizes all the active PME GPU stage timings for the current computation. Should be called at the end of every computation.
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ */
+void pme_gpu_update_timings(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Updates the internal list of active PME GPU stages (if timings are enabled).
  *
  * \param[in] pmeGpu         The PME GPU data structure.
- * \param[in] PMEStageId     The PME GPU stage gtPME_ index from the enum in src/gromacs/timing/gpu_timing.h
  */
-void pme_gpu_stop_timing(const PmeGpu* pmeGpu, size_t PMEStageId);
+void pme_gpu_reinit_timings(const PmeGpu* pmeGpu);
+
+/*! \brief
+ * Resets the PME GPU timings. To be called at the reset MD step.
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ */
+void pme_gpu_reset_timings(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Copies the PME GPU timings to the gmx_wallclock_gpu_t structure (for log output). To be called at the run end.
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ * \param[in] timings        The gmx_wallclock_gpu_pme_t structure.
+ */
+void pme_gpu_get_timings(const PmeGpu* pmeGpu, gmx_wallclock_gpu_pme_t* timings);
 
 #endif
index 3749d5748b91af38514be5d22fd3f7b14592a564..4274f095d99619e642b4e6d423e161af180a51bc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -82,6 +82,11 @@ static_assert(sizeof(DeviceBuffer<int>) == 8,
 #    define HIDE_FROM_OPENCL_COMPILER(x) char8
 #endif
 
+#ifndef NUMFEPSTATES
+//! Number of FEP states.
+#    define NUMFEPSTATES 2
+#endif
+
 /* What follows is all the PME GPU function arguments,
  * sorted into several device-side structures depending on the update rate.
  * This is GPU agnostic (float3 replaced by float[3], etc.).
@@ -97,9 +102,9 @@ struct PmeGpuConstParams
 {
     /*! \brief Electrostatics coefficient = ONE_4PI_EPS0 / pme->epsilon_r */
     float elFactor;
-    /*! \brief Virial and energy GPU array. Size is PME_GPU_ENERGY_AND_VIRIAL_COUNT (7) floats.
+    /*! \brief Virial and energy GPU array. Size is c_virialAndEnergyCount (7) floats.
      * The element order is virxx, viryy, virzz, virxy, virxz, viryz, energy. */
-    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_virialAndEnergy;
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_virialAndEnergy[NUMFEPSTATES];
 };
 
 /*! \internal \brief
@@ -130,14 +135,14 @@ struct PmeGpuGridParams
 
     /* Grid arrays */
     /*! \brief Real space grid. */
-    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_realGrid;
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_realGrid[NUMFEPSTATES];
     /*! \brief Complex grid - used in FFT/solve. If inplace cu/clFFT is used, then it is the same handle as realGrid. */
-    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_fourierGrid;
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_fourierGrid[NUMFEPSTATES];
 
     /*! \brief Grid spline values as in pme->bsp_mod
      * (laid out sequentially (XXX....XYYY......YZZZ.....Z))
      */
-    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_splineModuli;
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_splineModuli[NUMFEPSTATES];
     /*! \brief Fractional shifts lookup table as in pme->fshx/fshy/fshz, laid out sequentially (XXX....XYYY......YZZZ.....Z) */
     HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_fractShiftsTable;
     /*! \brief Gridline indices lookup table
@@ -157,11 +162,11 @@ struct PmeGpuAtomParams
      * The coordinates themselves change and need to be copied to the GPU for every PME computation,
      * but reallocation happens only at DD.
      */
-    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_coordinates;
-    /*! \brief Global GPU memory array handle with input atom charges.
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<gmx::RVec>) d_coordinates;
+    /*! \brief Global GPU memory array handle with input atom charges in states A and B.
      * The charges only need to be reallocated and copied to the GPU at DD step.
      */
-    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_coefficients;
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_coefficients[NUMFEPSTATES];
     /*! \brief Global GPU memory array handle with input/output rvec atom forces.
      * The forces change and need to be copied from (and possibly to) the GPU for every PME
      * computation, but reallocation happens only at DD.
@@ -196,14 +201,15 @@ struct PmeGpuDynamicParams
     float recipBox[DIM][DIM];
     /*! \brief The unit cell volume for solving. */
     float boxVolume;
+
+    /*! \brief The current coefficient scaling value. */
+    float scale;
 };
 
 /*! \internal \brief
- * A single structure encompassing almost all the PME data used in GPU kernels on device.
- * This is inherited by the GPU framework-specific structure
- * (PmeGpuCudaKernelParams in pme.cuh).
- * This way, most code preparing the kernel parameters can be GPU-agnostic by casting
- * the kernel parameter data pointer to PmeGpuKernelParamsBase.
+ * A single structure encompassing all the PME data used in GPU kernels on device.
+ * To extend the list with platform-specific parameters, this can be inherited by the
+ * GPU framework-specific structure.
  */
 struct PmeGpuKernelParamsBase
 {
@@ -218,6 +224,11 @@ struct PmeGpuKernelParamsBase
      * before launching spreading.
      */
     struct PmeGpuDynamicParams current;
+    /* These texture objects are only used in CUDA and are related to the grid size. */
+    /*! \brief Texture object for accessing grid.d_fractShiftsTable */
+    HIDE_FROM_OPENCL_COMPILER(DeviceTexture) fractShiftsTableTexture;
+    /*! \brief Texture object for accessing grid.d_gridlineIndicesTable */
+    HIDE_FROM_OPENCL_COMPILER(DeviceTexture) gridlineIndicesTableTexture;
 };
 
 #endif
index 21c77f94b3f1bd637372bbd9352b1177cd1ca692..26405433f53ab92397a24e24b8fb8a2a6db633be 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/ewald/pme.h"
 #include "gromacs/ewald/pme_gpu_program.h"
 #include "gromacs/gpu_utils/clfftinitializer.h"
-#include "gromacs/gpu_utils/gpu_utils.h" // for GpuApiCallBehavior
 #include "gromacs/gpu_utils/hostallocator.h"
 #include "gromacs/math/vectypes.h"
 
-#if GMX_GPU != GMX_GPU_NONE
+#include "pme_gpu_settings.h"
+#include "pme_gpu_staging.h"
+
+namespace gmx
+{
+class PmeDeviceBuffers;
+} // namespace gmx
+
+#if GMX_GPU
 struct PmeGpuSpecific;
 #else
 /*! \brief A dummy typedef for the GPU host data placeholder on non-GPU builds */
 typedef int PmeGpuSpecific;
 #endif
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 struct PmeGpuCudaKernelParams;
 /*! \brief A typedef for including the GPU kernel arguments data by pointer */
 typedef PmeGpuCudaKernelParams PmeGpuKernelParams;
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
 struct PmeGpuKernelParamsBase;
 /*! \brief A typedef for including the GPU kernel arguments data by pointer */
 typedef PmeGpuKernelParamsBase PmeGpuKernelParams;
@@ -80,87 +87,7 @@ typedef PmeGpuKernelParamsBase PmeGpuKernelParams;
 typedef int PmeGpuKernelParams;
 #endif
 
-struct gmx_device_info_t;
-
-/*! \internal \brief
- * The PME GPU settings structure, included in the main PME GPU structure by value.
- */
-struct PmeGpuSettings
-{
-    /* Permanent settings set on initialization */
-    /*! \brief A boolean which tells if the solving is performed on GPU. Currently always true */
-    bool performGPUSolve;
-    /*! \brief A boolean which tells if the gathering is performed on GPU. Currently always true */
-    bool performGPUGather;
-    /*! \brief A boolean which tells if the FFT is performed on GPU. Currently true for a single MPI rank. */
-    bool performGPUFFT;
-    /*! \brief A convenience boolean which tells if PME decomposition is used. */
-    bool useDecomposition;
-    /*! \brief True if PME forces are reduced on-GPU, false if reduction is done on the CPU;
-     *  in the former case transfer does not need to happen.
-     *
-     *  Note that this flag may change per-step.
-     */
-    bool useGpuForceReduction;
-
-    /*! \brief A boolean which tells if any PME GPU stage should copy all of its outputs to the
-     * host. Only intended to be used by the test framework.
-     */
-    bool copyAllOutputs;
-    /*! \brief An enum which tells whether most PME GPU D2H/H2D data transfers should be synchronous. */
-    GpuApiCallBehavior transferKind;
-    /*! \brief Various flags for the current PME computation, corresponding to the GMX_PME_ flags in pme.h. */
-    int currentFlags;
-    /*! \brief
-     *  Currently only supported by CUDA.
-     *  Controls if we should use order (i.e. 4) threads per atom for the GPU
-     *  or order*order (i.e. 16) threads per atom.
-     */
-    bool useOrderThreadsPerAtom;
-    /*! \brief
-     * Currently only supported by CUDA.
-     * Controls if we should recalculate the splines in the gather or
-     * save the values in the spread and reload in the gather.
-     */
-    bool recalculateSplines;
-};
-
-// TODO There's little value in computing the Coulomb and LJ virial
-// separately, so we should simplify that.
-// TODO The matrices might be best as a view, but not currently
-// possible. Use mdspan?
-struct PmeOutput
-{
-    gmx::ArrayRef<gmx::RVec> forces_; //!< Host staging area for PME forces
-    bool                     haveForceOutput_ =
-            false; //!< True if forces have been staged other false (when forces are reduced on the GPU).
-    real   coulombEnergy_ = 0;         //!< Host staging area for PME coulomb energy
-    matrix coulombVirial_ = { { 0 } }; //!< Host staging area for PME coulomb virial contributions
-    real   lennardJonesEnergy_ = 0;    //!< Host staging area for PME LJ energy
-    matrix lennardJonesVirial_ = { { 0 } }; //!< Host staging area for PME LJ virial contributions
-};
-
-/*! \internal \brief
- * The PME GPU intermediate buffers structure, included in the main PME GPU structure by value.
- * Buffers are managed by the PME GPU module.
- */
-struct PmeGpuStaging
-{
-    //! Host-side force buffer
-    gmx::PaddedHostVector<gmx::RVec> h_forces;
-
-    /*! \brief Virial and energy intermediate host-side buffer. Size is PME_GPU_VIRIAL_AND_ENERGY_COUNT. */
-    float* h_virialAndEnergy;
-    /*! \brief B-spline values intermediate host-side buffer. */
-    float* h_splineModuli;
-
-    /*! \brief Pointer to the host memory with B-spline values. Only used for host-side gather, or unit tests */
-    float* h_theta;
-    /*! \brief Pointer to the host memory with B-spline derivative values. Only used for host-side gather, or unit tests */
-    float* h_dtheta;
-    /*! \brief Pointer to the host memory with ivec atom gridline indices. Only used for host-side gather, or unit tests */
-    int* h_gridlineIndices;
-};
+struct DeviceInformation;
 
 /*! \internal \brief
  * The PME GPU structure for all the data copied directly from the CPU PME structure.
@@ -173,7 +100,7 @@ struct PmeGpuStaging
  */
 struct PmeShared
 {
-    /*! \brief Grid count - currently always 1 on GPU */
+    /*! \brief Grid count */
     int ngrids;
     /*! \brief Grid dimensions - nkx, nky, nkz */
     int nk[DIM];
@@ -211,7 +138,7 @@ struct PmeGpu
     std::shared_ptr<PmeShared> common; // TODO: make the CPU structure use the same type
 
     //! A handle to the program created by buildPmeGpuProgram()
-    PmeGpuProgramHandle programHandle_;
+    const PmeGpuProgram* programHandle_;
 
     //! Handle that ensures the clFFT library has been initialized once per process.
     std::unique_ptr<gmx::ClfftInitializer> initializedClfftLibrary_;
@@ -225,24 +152,16 @@ struct PmeGpu
     PmeGpuStaging staging;
 
     /*! \brief Number of local atoms, padded to be divisible by c_pmeAtomDataAlignment.
-     * Used for kernel scheduling.
-     * kernelParams.atoms.nAtoms is the actual atom count to be used for data copying.
-     * TODO: this and the next member represent a memory allocation/padding properties -
-     * what a container type should do ideally.
-     */
-    int nAtomsPadded;
-    /*! \brief Number of local atoms, padded to be divisible by c_pmeAtomDataAlignment
-     * if c_usePadding is true.
+     *
      * Used only as a basic size for almost all the atom data allocations
      * (spline parameter data is also aligned by PME_SPREADGATHER_PARTICLES_PER_WARP).
-     * This should be the same as (c_usePadding ? nAtomsPadded : kernelParams.atoms.nAtoms).
      * kernelParams.atoms.nAtoms is the actual atom count to be used for most data copying.
+     *
+     * TODO: memory allocation/padding properties should be handled by
+     * something like a container
      */
     int nAtomsAlloc;
 
-    /*! \brief A pointer to the device used during the execution. */
-    const gmx_device_info_t* deviceInfo;
-
     /*! \brief Kernel scheduling grid width limit in X - derived from deviceinfo compute capability in CUDA.
      * Declared as very large int to make it useful in computations with type promotion, to avoid overflows.
      * OpenCL seems to not have readily available global work size limit, so we just assign a large arbitrary constant to this instead.
index be4f986957d84f257fd5fc3658886bcbb6579c82..cf98d701c54fe4f3c6fd9cd2f8eece9b03a0d31a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <set>
 #include <vector>
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 #    include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
 #    include "gromacs/gpu_utils/gpuregiontimer.cuh"
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
 #    include "gromacs/gpu_utils/gpueventsynchronizer_ocl.h"
 #    include "gromacs/gpu_utils/gpuregiontimer_ocl.h"
 #endif
 
 #include "gromacs/timing/gpu_timing.h" // for gtPME_EVENT_COUNT
 
+#include "pme_gpu_3dfft.h"
+
+#ifndef NUMFEPSTATES
+//! Number of FEP states.
+#    define NUMFEPSTATES 2
+#endif
+
 class GpuParallel3dFft;
 
 /*! \internal \brief
@@ -67,8 +74,16 @@ class GpuParallel3dFft;
  */
 struct PmeGpuSpecific
 {
-    /*! \brief The GPU stream where everything related to the PME happens. */
-    CommandStream pmeStream;
+    /*! \brief Constructor
+     *
+     * \param[in] deviceContext  GPU device context
+     * \param[in] pmeStream      GPU pme stream.
+     */
+    PmeGpuSpecific(const DeviceContext& deviceContext, const DeviceStream& pmeStream) :
+        deviceContext_(deviceContext),
+        pmeStream_(pmeStream)
+    {
+    }
 
     /*! \brief
      * A handle to the GPU context.
@@ -76,7 +91,10 @@ struct PmeGpuSpecific
      * but should be a constructor parameter to PmeGpu, as well as PmeGpuProgram,
      * managed by high-level code.
      */
-    DeviceContext context;
+    const DeviceContext& deviceContext_;
+
+    /*! \brief The GPU stream where everything related to the PME happens. */
+    const DeviceStream& pmeStream_;
 
     /* Synchronization events */
     /*! \brief Triggered after the PME Force Calculations have been completed */
@@ -86,13 +104,13 @@ struct PmeGpuSpecific
 
     /* Settings which are set at the start of the run */
     /*! \brief A boolean which tells whether the complex and real grids for cu/clFFT are different or same. Currenty true. */
-    bool performOutOfPlaceFFT;
+    bool performOutOfPlaceFFT = false;
     /*! \brief A boolean which tells if the GPU timing events are enabled.
      *  False by default, can be enabled by setting the environment variable GMX_ENABLE_GPU_TIMING.
      *  Note: will not be reliable when multiple GPU tasks are running concurrently on the same
      * device context, as CUDA events on multiple streams are untrustworthy.
      */
-    bool useTiming;
+    bool useTiming = false;
 
     //! Vector of FFT setups
     std::vector<std::unique_ptr<GpuParallel3dFft>> fftSetup;
@@ -112,37 +130,37 @@ struct PmeGpuSpecific
      * TODO: these should live in a clean buffered container type, and be refactored in the NB/cudautils as well.
      */
     /*! \brief The kernelParams.atoms.coordinates float element count (actual)*/
-    int coordinatesSize;
+    int coordinatesSize = 0;
     /*! \brief The kernelParams.atoms.coordinates float element count (reserved) */
-    int coordinatesSizeAlloc;
+    int coordinatesSizeAlloc = 0;
     /*! \brief The kernelParams.atoms.forces float element count (actual) */
-    int forcesSize;
+    int forcesSize = 0;
     /*! \brief The kernelParams.atoms.forces float element count (reserved) */
-    int forcesSizeAlloc;
+    int forcesSizeAlloc = 0;
     /*! \brief The kernelParams.atoms.gridlineIndices int element count (actual) */
-    int gridlineIndicesSize;
+    int gridlineIndicesSize = 0;
     /*! \brief The kernelParams.atoms.gridlineIndices int element count (reserved) */
-    int gridlineIndicesSizeAlloc;
+    int gridlineIndicesSizeAlloc = 0;
     /*! \brief Both the kernelParams.atoms.theta and kernelParams.atoms.dtheta float element count (actual) */
-    int splineDataSize;
+    int splineDataSize = 0;
     /*! \brief Both the kernelParams.atoms.theta and kernelParams.atoms.dtheta float element count (reserved) */
-    int splineDataSizeAlloc;
+    int splineDataSizeAlloc = 0;
     /*! \brief The kernelParams.atoms.coefficients float element count (actual) */
-    int coefficientsSize;
+    int coefficientsSize[NUMFEPSTATES] = { 0, 0 };
     /*! \brief The kernelParams.atoms.coefficients float element count (reserved) */
-    int coefficientsSizeAlloc;
+    int coefficientsCapacity[NUMFEPSTATES] = { 0, 0 };
     /*! \brief The kernelParams.grid.splineValuesArray float element count (actual) */
-    int splineValuesSize;
+    int splineValuesSize[NUMFEPSTATES] = { 0, 0 };
     /*! \brief The kernelParams.grid.splineValuesArray float element count (reserved) */
-    int splineValuesSizeAlloc;
+    int splineValuesCapacity[NUMFEPSTATES] = { 0, 0 };
     /*! \brief The kernelParams.grid.realGrid float element count (actual) */
-    int realGridSize;
+    int realGridSize[NUMFEPSTATES] = { 0, 0 };
     /*! \brief The kernelParams.grid.realGrid float element count (reserved) */
-    int realGridSizeAlloc;
+    int realGridCapacity[NUMFEPSTATES] = { 0, 0 };
     /*! \brief The kernelParams.grid.fourierGrid float (not float2!) element count (actual) */
-    int complexGridSize;
+    int complexGridSize[NUMFEPSTATES] = { 0, 0 };
     /*! \brief The kernelParams.grid.fourierGrid float (not float2!) element count (reserved) */
-    int complexGridSizeAlloc;
+    int complexGridCapacity[NUMFEPSTATES] = { 0, 0 };
 };
 
 #endif
index ab759f30deda0d41c894f1b2a4c0361d3c8603ea..3d277d314666eb1d123aa07be7455d5a8b69fca0 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 99dca07d43cfdd13aa94b3761630f19aee2bf664..41f14533b078ffb4ab6fe6371558dc4799a6d532 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 028cd90e9918b22cbd6bee2cfccb94c7173c9bef..a4af8154de90709f18f06827d9135ec8c5eb1495 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "config.h"
 
+#include <vector>
+
 #include "gromacs/math/gmxcomplex.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/defaultinitializationallocator.h"
+#include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/gmxmpi.h"
-#include "gromacs/utility/smalloc.h"
 
-#include "pme_gpu_types_host.h"
+#include "spline_vectors.h"
 
 //! A repeat of typedef from parallel_3dfft.h
 typedef struct gmx_parallel_3dfft* gmx_parallel_3dfft_t;
@@ -70,6 +75,8 @@ struct t_commrec;
 struct t_inputrec;
 struct PmeGpu;
 
+enum class PmeRunMode;
+
 //@{
 //! Grid indices for A state for charge and Lennard-Jones C6
 #define PME_GRID_QA 0
@@ -96,17 +103,6 @@ static const real lb_scale_factor_symm[] = { 2.0 / 64, 12.0 / 64, 30.0 / 64, 20.
  */
 #define PME_ORDER_MAX 12
 
-/*! \brief As gmx_pme_init, but takes most settings, except the grid/Ewald coefficients, from
- * pme_src. This is only called when the PME cut-off/grid size changes.
- */
-void gmx_pme_reinit(struct gmx_pme_t** pmedata,
-                    const t_commrec*   cr,
-                    struct gmx_pme_t*  pme_src,
-                    const t_inputrec*  ir,
-                    const ivec         grid_size,
-                    real               ewaldcoeff_q,
-                    real               ewaldcoeff_lj);
-
 
 /* Temporary suppression until these structs become opaque and don't live in
  * a header that is included by other headers. Also, until then I have no
@@ -157,9 +153,6 @@ struct AtomToThreadMap
     FastVector<int> i;
 };
 
-/*! \brief Helper typedef for spline vectors */
-typedef real* splinevec[DIM];
-
 /*! \internal
  * \brief Coefficients for theta or dtheta
  */
@@ -435,19 +428,4 @@ struct gmx_pme_t
 
 //! @endcond
 
-/*! \brief
- * Finds out if PME is currently running on GPU.
- * TODO: should this be removed eventually?
- *
- * \param[in] pme  The PME structure.
- * \returns        True if PME runs on GPU currently, false otherwise.
- */
-inline bool pme_gpu_active(const gmx_pme_t* pme)
-{
-    return (pme != nullptr) && (pme->runMode != PmeRunMode::CPU);
-}
-
-/*! \brief Tell our PME-only node to switch to a new grid size */
-void gmx_pme_send_switchgrid(const t_commrec* cr, ivec grid_size, real ewaldcoeff_q, real ewaldcoeff_lj);
-
 #endif
index 4cd569105dd23918d4d192fa2c173238b9c213f0..73b40cc43aa3471d02e851b59d1b947731378327 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
- * Copyright (c) 2020, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdlib/dispersioncorrection.h"
 #include "gromacs/mdlib/forcerec.h"
 #include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/nbnxm/gpu_data_mgmt.h"
 #include "gromacs/nbnxm/nbnxm.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/timing/wallcycle.h"
+#include "gromacs/timing/walltime_accounting.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
@@ -80,6 +83,7 @@
 #include "gromacs/utility/strconvert.h"
 
 #include "pme_internal.h"
+#include "pme_pp.h"
 
 /*! \brief Parameters and settings for one PP-PME setup */
 struct pme_setup_t
@@ -680,9 +684,10 @@ static void pme_load_balance(pme_load_balancing_t*          pme_lb,
                 pme_lb->elimited = epmelblimMAXSCALING;
             }
 
-            if (OK && ir.ePBC != epbcNONE)
+            if (OK && ir.pbcType != PbcType::No)
             {
-                OK = (gmx::square(pme_lb->setup[pme_lb->cur + 1].rlistOuter) <= max_cutoff2(ir.ePBC, box));
+                OK = (gmx::square(pme_lb->setup[pme_lb->cur + 1].rlistOuter)
+                      <= max_cutoff2(ir.pbcType, box));
                 if (!OK)
                 {
                     pme_lb->elimited = epmelblimBOX;
@@ -828,7 +833,7 @@ static void pme_load_balance(pme_load_balancing_t*          pme_lb,
     }
 
     /* We always re-initialize the tables whether they are used or not */
-    init_interaction_const_tables(nullptr, ic);
+    init_interaction_const_tables(nullptr, ic, ir.tabext);
 
     Nbnxm::gpu_pme_loadbal_update_param(nbv, ic);
 
index 1e6682d46c6b63462557f8571197b5509361d65e..bb98635ca239238b9fd262317641f5508dc15d22 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_EWALD_PME_LOAD_BALANCING_H
 #define GMX_EWALD_PME_LOAD_BALANCING_H
 
-#include "gromacs/mdtypes/forcerec.h"
-#include "gromacs/mdtypes/interaction_const.h"
+#include "gromacs/math/vectypes.h"
 #include "gromacs/timing/wallcycle.h"
 
 struct nonbonded_verlet_t;
 struct t_commrec;
+struct t_forcerec;
 struct t_inputrec;
+struct interaction_const_t;
+struct gmx_pme_t;
 class t_state;
 
 namespace gmx
index 658b7f45343f5662a78c2d30bfa531d18662c3c2..de9386f63979df36dce8bf7f62fe8149c5739180 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -59,6 +60,8 @@
 
 #include "gmxpre.h"
 
+#include "pme_only.h"
+
 #include "config.h"
 
 #include <cassert>
@@ -79,6 +82,7 @@
 #include "gromacs/fileio/pdbio.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
+#include "gromacs/gpu_utils/device_stream_manager.h"
 #include "gromacs/gpu_utils/hostallocator.h"
 #include "gromacs/math/gmxcomplex.h"
 #include "gromacs/math/units.h"
@@ -86,6 +90,7 @@
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/forceoutput.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/mdtypes/state_propagator_data_gpu.h"
 #include "gromacs/timing/cyclecounter.h"
 #include "gromacs/timing/wallcycle.h"
 #include "gromacs/utility/smalloc.h"
 
 #include "pme_gpu_internal.h"
-#include "pme_internal.h"
+#include "pme_output.h"
 #include "pme_pp_communication.h"
 
 /*! \brief environment variable to enable GPU P2P communication */
 static const bool c_enableGpuPmePpComms =
-        (getenv("GMX_GPU_PME_PP_COMMS") != nullptr) && GMX_THREAD_MPI && (GMX_GPU == GMX_GPU_CUDA);
+        GMX_GPU_CUDA && GMX_THREAD_MPI && (getenv("GMX_GPU_PME_PP_COMMS") != nullptr);
 
 /*! \brief Master PP-PME communication data structure */
 struct gmx_pme_pp
@@ -192,7 +197,7 @@ static gmx_pme_t* gmx_pmeonly_switch(std::vector<gmx_pme_t*>* pmedata,
     for (auto& pme : *pmedata)
     {
         GMX_ASSERT(pme, "Bad PME tuning list element pointer");
-        if (pme->nkx == grid_size[XX] && pme->nky == grid_size[YY] && pme->nkz == grid_size[ZZ])
+        if (gmx_pme_grid_matches(*pme, grid_size))
         {
             /* Here we have found an existing PME data structure that suits us.
              * However, in the GPU case, we have to reinitialize it - there's only one GPU structure.
@@ -215,25 +220,28 @@ static gmx_pme_t* gmx_pmeonly_switch(std::vector<gmx_pme_t*>* pmedata,
 
 /*! \brief Called by PME-only ranks to receive coefficients and coordinates
  *
- * \param[in] pme           PME data structure.
- * \param[in,out] pme_pp    PME-PP communication structure.
- * \param[out] natoms       Number of received atoms.
- * \param[out] box        System box, if received.
- * \param[out] maxshift_x        Maximum shift in X direction, if received.
- * \param[out] maxshift_y        Maximum shift in Y direction, if received.
- * \param[out] lambda_q         Free-energy lambda for electrostatics, if received.
- * \param[out] lambda_lj         Free-energy lambda for Lennard-Jones, if received.
- * \param[out] bEnerVir          Set to true if this is an energy/virial calculation step, otherwise
- * set to false. \param[out] step              MD integration step number. \param[out] grid_size PME
- * grid size, if received. \param[out] ewaldcoeff_q         Ewald cut-off parameter for
- * electrostatics, if received. \param[out] ewaldcoeff_lj         Ewald cut-off parameter for
- * Lennard-Jones, if received. \param[in] useGpuForPme      flag on whether PME is on GPU \param[in]
- * stateGpu          GPU state propagator object \param[in] runMode           PME run mode
+ * \param[in] pme                     PME data structure.
+ * \param[in,out] pme_pp              PME-PP communication structure.
+ * \param[out] natoms                 Number of received atoms.
+ * \param[out] box                    System box, if received.
+ * \param[out] maxshift_x             Maximum shift in X direction, if received.
+ * \param[out] maxshift_y             Maximum shift in Y direction, if received.
+ * \param[out] lambda_q               Free-energy lambda for electrostatics, if received.
+ * \param[out] lambda_lj              Free-energy lambda for Lennard-Jones, if received.
+ * \param[out] computeEnergyAndVirial Set to true if this is an energy/virial calculation
+ *                                    step, otherwise set to false.
+ * \param[out] step                   MD integration step number.
+ * \param[out] grid_size              PME grid size, if received.
+ * \param[out] ewaldcoeff_q           Ewald cut-off parameter for electrostatics, if received.
+ * \param[out] ewaldcoeff_lj          Ewald cut-off parameter for Lennard-Jones, if received.
+ * \param[in]  useGpuForPme           Flag on whether PME is on GPU.
+ * \param[in]  stateGpu               GPU state propagator object.
+ * \param[in]  runMode                PME run mode.
  *
- * \retval pmerecvqxX             All parameters were set, chargeA and chargeB can be NULL.
- * \retval pmerecvqxFINISH        No parameters were set.
- * \retval pmerecvqxSWITCHGRID    Only grid_size and *ewaldcoeff were set.
- * \retval pmerecvqxRESETCOUNTERS *step was set.
+ * \retval pmerecvqxX                 All parameters were set, chargeA and chargeB can be NULL.
+ * \retval pmerecvqxFINISH            No parameters were set.
+ * \retval pmerecvqxSWITCHGRID        Only grid_size and *ewaldcoeff were set.
+ * \retval pmerecvqxRESETCOUNTERS     *step was set.
  */
 static int gmx_pme_recv_coeffs_coords(struct gmx_pme_t*            pme,
                                       gmx_pme_pp*                  pme_pp,
@@ -243,7 +251,7 @@ static int gmx_pme_recv_coeffs_coords(struct gmx_pme_t*            pme,
                                       int*                         maxshift_y,
                                       real*                        lambda_q,
                                       real*                        lambda_lj,
-                                      gmx_bool*                    bEnerVir,
+                                      gmx_bool*                    computeEnergyAndVirial,
                                       int64_t*                     step,
                                       ivec*                        grid_size,
                                       real*                        ewaldcoeff_q,
@@ -410,7 +418,7 @@ static int gmx_pme_recv_coeffs_coords(struct gmx_pme_t*            pme,
         {
             if (atomSetChanged)
             {
-                gmx_pme_reinit_atoms(pme, nat, pme_pp->chargeA.data());
+                gmx_pme_reinit_atoms(pme, nat, pme_pp->chargeA.data(), pme_pp->chargeB.data());
                 if (useGpuForPme)
                 {
                     stateGpu->reinit(nat, nat);
@@ -423,14 +431,10 @@ static int gmx_pme_recv_coeffs_coords(struct gmx_pme_t*            pme,
                                "but PME run mode is not PmeRunMode::GPU\n");
 
                     // This rank will have its data accessed directly by PP rank, so needs to send the remote addresses.
-                    rvec* d_x = nullptr;
-                    rvec* d_f = nullptr;
-#    if (GMX_GPU == GMX_GPU_CUDA) // avoid invalid cast for OpenCL
-                    d_x = reinterpret_cast<rvec*>(pme_gpu_get_device_x(pme));
-                    d_f = reinterpret_cast<rvec*>(pme_gpu_get_device_f(pme));
-#    endif
-                    pme_pp->pmeCoordinateReceiverGpu->sendCoordinateBufferAddressToPpRanks(d_x);
-                    pme_pp->pmeForceSenderGpu->sendForceBufferAddressToPpRanks(d_f);
+                    pme_pp->pmeCoordinateReceiverGpu->sendCoordinateBufferAddressToPpRanks(
+                            stateGpu->getCoordinates());
+                    pme_pp->pmeForceSenderGpu->sendForceBufferAddressToPpRanks(
+                            reinterpret_cast<rvec*>(pme_gpu_get_device_f(pme)));
                 }
             }
 
@@ -438,10 +442,10 @@ static int gmx_pme_recv_coeffs_coords(struct gmx_pme_t*            pme,
             /* The box, FE flag and lambda are sent along with the coordinates
              *  */
             copy_mat(cnb.box, box);
-            *lambda_q  = cnb.lambda_q;
-            *lambda_lj = cnb.lambda_lj;
-            *bEnerVir  = ((cnb.flags & PP_PME_ENER_VIR) != 0U);
-            *step      = cnb.step;
+            *lambda_q               = cnb.lambda_q;
+            *lambda_lj              = cnb.lambda_lj;
+            *computeEnergyAndVirial = ((cnb.flags & PP_PME_ENER_VIR) != 0U);
+            *step                   = cnb.step;
 
             /* Receive the coordinates in place */
             nat = 0;
@@ -490,7 +494,7 @@ static int gmx_pme_recv_coeffs_coords(struct gmx_pme_t*            pme,
     GMX_UNUSED_VALUE(maxshift_y);
     GMX_UNUSED_VALUE(lambda_q);
     GMX_UNUSED_VALUE(lambda_lj);
-    GMX_UNUSED_VALUE(bEnerVir);
+    GMX_UNUSED_VALUE(computeEnergyAndVirial);
     GMX_UNUSED_VALUE(step);
     GMX_UNUSED_VALUE(grid_size);
     GMX_UNUSED_VALUE(ewaldcoeff_q);
@@ -594,59 +598,61 @@ static void gmx_pme_send_force_vir_ener(const gmx_pme_t& pme,
 #endif
 }
 
-int gmx_pmeonly(struct gmx_pme_t*         pme,
-                const t_commrec*          cr,
-                t_nrnb*                   mynrnb,
-                gmx_wallcycle*            wcycle,
-                gmx_walltime_accounting_t walltime_accounting,
-                t_inputrec*               ir,
-                PmeRunMode                runMode)
+int gmx_pmeonly(struct gmx_pme_t*               pme,
+                const t_commrec*                cr,
+                t_nrnb*                         mynrnb,
+                gmx_wallcycle*                  wcycle,
+                gmx_walltime_accounting_t       walltime_accounting,
+                t_inputrec*                     ir,
+                PmeRunMode                      runMode,
+                const gmx::DeviceStreamManager* deviceStreamManager)
 {
-    int      ret;
-    int      natoms = 0;
-    matrix   box;
-    real     lambda_q   = 0;
-    real     lambda_lj  = 0;
-    int      maxshift_x = 0, maxshift_y = 0;
-    real     dvdlambda_q, dvdlambda_lj;
-    float    cycles;
-    int      count;
-    gmx_bool bEnerVir = FALSE;
-    int64_t  step;
+    int     ret;
+    int     natoms = 0;
+    matrix  box;
+    real    lambda_q   = 0;
+    real    lambda_lj  = 0;
+    int     maxshift_x = 0, maxshift_y = 0;
+    real    dvdlambda_q, dvdlambda_lj;
+    float   cycles;
+    int     count;
+    bool    computeEnergyAndVirial = false;
+    int64_t step;
 
     /* This data will only use with PME tuning, i.e. switching PME grids */
     std::vector<gmx_pme_t*> pmedata;
     pmedata.push_back(pme);
 
     auto pme_pp = gmx_pme_pp_init(cr);
+
+    std::unique_ptr<gmx::StatePropagatorDataGpu> stateGpu;
     // TODO the variable below should be queried from the task assignment info
-    const bool  useGpuForPme  = (runMode == PmeRunMode::GPU) || (runMode == PmeRunMode::Mixed);
-    const void* commandStream = useGpuForPme ? pme_gpu_get_device_stream(pme) : nullptr;
-    const void* deviceContext = useGpuForPme ? pme_gpu_get_device_context(pme) : nullptr;
-    const int   paddingSize   = pme_gpu_get_padding_size(pme);
+    const bool useGpuForPme = (runMode == PmeRunMode::GPU) || (runMode == PmeRunMode::Mixed);
     if (useGpuForPme)
     {
+        GMX_RELEASE_ASSERT(
+                deviceStreamManager != nullptr,
+                "Device stream manager can not be nullptr when using GPU in PME-only rank.");
+        GMX_RELEASE_ASSERT(deviceStreamManager->streamIsValid(gmx::DeviceStreamType::Pme),
+                           "Device stream can not be nullptr when using GPU in PME-only rank");
         changePinningPolicy(&pme_pp->chargeA, pme_get_pinning_policy());
         changePinningPolicy(&pme_pp->x, pme_get_pinning_policy());
         if (c_enableGpuPmePpComms)
         {
             pme_pp->pmeCoordinateReceiverGpu = std::make_unique<gmx::PmeCoordinateReceiverGpu>(
-                    pme_gpu_get_device_stream(pme), pme_pp->mpi_comm_mysim, pme_pp->ppRanks);
+                    deviceStreamManager->stream(gmx::DeviceStreamType::Pme), pme_pp->mpi_comm_mysim,
+                    pme_pp->ppRanks);
             pme_pp->pmeForceSenderGpu = std::make_unique<gmx::PmeForceSenderGpu>(
-                    pme_gpu_get_device_stream(pme), pme_pp->mpi_comm_mysim, pme_pp->ppRanks);
+                    deviceStreamManager->stream(gmx::DeviceStreamType::Pme), pme_pp->mpi_comm_mysim,
+                    pme_pp->ppRanks);
         }
-    }
-
-    std::unique_ptr<gmx::StatePropagatorDataGpu> stateGpu;
-    if (useGpuForPme)
-    {
         // TODO: Special PME-only constructor is used here. There is no mechanism to prevent from using the other constructor here.
         //       This should be made safer.
         stateGpu = std::make_unique<gmx::StatePropagatorDataGpu>(
-                commandStream, deviceContext, GpuApiCallBehavior::Async, paddingSize, wcycle);
+                &deviceStreamManager->stream(gmx::DeviceStreamType::Pme), deviceStreamManager->context(),
+                GpuApiCallBehavior::Async, pme_gpu_get_block_size(pme), wcycle);
     }
 
-
     clear_nrnb(mynrnb);
 
     count = 0;
@@ -658,8 +664,8 @@ int gmx_pmeonly(struct gmx_pme_t*         pme,
             /* Domain decomposition */
             ivec newGridSize;
             real ewaldcoeff_q = 0, ewaldcoeff_lj = 0;
-            ret = gmx_pme_recv_coeffs_coords(pme, pme_pp.get(), &natoms, box, &maxshift_x,
-                                             &maxshift_y, &lambda_q, &lambda_lj, &bEnerVir, &step,
+            ret = gmx_pme_recv_coeffs_coords(pme, pme_pp.get(), &natoms, box, &maxshift_x, &maxshift_y,
+                                             &lambda_q, &lambda_lj, &computeEnergyAndVirial, &step,
                                              &newGridSize, &ewaldcoeff_q, &ewaldcoeff_lj,
                                              useGpuForPme, stateGpu.get(), runMode);
 
@@ -697,15 +703,18 @@ int gmx_pmeonly(struct gmx_pme_t*         pme,
         // of pme_pp (maybe box, energy and virial, too; and likewise
         // from mdatoms for the other call to gmx_pme_do), so we have
         // fewer lines of code and less parameter passing.
-        const int pmeFlags = GMX_PME_DO_ALL_F | (bEnerVir ? GMX_PME_CALC_ENER_VIR : 0);
-        PmeOutput output;
+        gmx::StepWorkload stepWork;
+        stepWork.computeVirial = computeEnergyAndVirial;
+        stepWork.computeEnergy = computeEnergyAndVirial;
+        stepWork.computeForces = true;
+        PmeOutput output       = { {}, false, 0, { { 0 } }, 0, 0, { { 0 } }, 0 };
         if (useGpuForPme)
         {
-            const bool boxChanged              = false;
-            const bool useGpuPmeForceReduction = pme_pp->useGpuDirectComm;
+            stepWork.haveDynamicBox      = false;
+            stepWork.useGpuPmeFReduction = pme_pp->useGpuDirectComm;
             // TODO this should be set properly by gmx_pme_recv_coeffs_coords,
             // or maybe use inputrecDynamicBox(ir), at the very least - change this when this codepath is tested!
-            pme_gpu_prepare_computation(pme, boxChanged, box, wcycle, pmeFlags, useGpuPmeForceReduction);
+            pme_gpu_prepare_computation(pme, box, wcycle, stepWork);
             if (!pme_pp->useGpuDirectComm)
             {
                 stateGpu->copyCoordinatesToGpu(gmx::ArrayRef<gmx::RVec>(pme_pp->x), gmx::AtomLocality::All);
@@ -714,10 +723,10 @@ int gmx_pmeonly(struct gmx_pme_t*         pme,
             // TODO: with pme on GPU the receive should make a list of synchronizers and pass it here #3157
             auto xReadyOnDevice = nullptr;
 
-            pme_gpu_launch_spread(pme, xReadyOnDevice, wcycle);
-            pme_gpu_launch_complex_transforms(pme, wcycle);
-            pme_gpu_launch_gather(pme, wcycle, PmeForceOutputHandling::Set);
-            output = pme_gpu_wait_finish_task(pme, pmeFlags, wcycle);
+            pme_gpu_launch_spread(pme, xReadyOnDevice, wcycle, lambda_q);
+            pme_gpu_launch_complex_transforms(pme, wcycle, stepWork);
+            pme_gpu_launch_gather(pme, wcycle, lambda_q);
+            output = pme_gpu_wait_finish_task(pme, computeEnergyAndVirial, lambda_q, wcycle);
             pme_gpu_reinit_computation(pme, wcycle);
         }
         else
@@ -730,7 +739,7 @@ int gmx_pmeonly(struct gmx_pme_t*         pme,
                        pme_pp->sigmaB.data(), box, cr, maxshift_x, maxshift_y, mynrnb, wcycle,
                        output.coulombVirial_, output.lennardJonesVirial_, &output.coulombEnergy_,
                        &output.lennardJonesEnergy_, lambda_q, lambda_lj, &dvdlambda_q,
-                       &dvdlambda_lj, pmeFlags);
+                       &dvdlambda_lj, stepWork);
             output.forces_ = pme_pp->f;
         }
 
similarity index 61%
rename from src/gromacs/utility/mpiinplacebuffers.h
rename to src/gromacs/ewald/pme_only.h
index dfbac0de09b5b4a431693fd68624716f382745ca..1a71ea195c1b74ad44d168bd20753cfb0706e4d4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \libinternal \file
- * \brief Declares object that provides buffers for the copies
- * necessary when MPI_IN_PLACE is not supported by the MPI library.
  *
- * The struct is declared in the header so that functionality it
- * supports can be inlined.
+ * \brief This file contains function declarations necessary for
+ * running on an MPI rank doing only PME long-ranged work.
  *
- * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Berk Hess <hess@kth.se>
  * \inlibraryapi
- * \ingroup module_utility
+ * \ingroup module_ewald
  */
-#ifndef GMX_UTILTIY_MPIINPLACEBUFFERS_H
-#define GMX_UTILTIY_MPIINPLACEBUFFERS_H
 
-#include <cstdint>
+#ifndef GMX_EWALD_PME_ONLY_H
+#define GMX_EWALD_PME_ONLY_H
 
-struct mpi_in_place_buf_t
-{
-    /* these buffers are used as destination buffers if MPI_IN_PLACE isn't
-       supported.*/
-    int* ibuf; /* for ints */
-    int  ibuf_alloc;
+#include <string>
 
-    int64_t* libuf;
-    int      libuf_alloc;
+#include "gromacs/timing/walltime_accounting.h"
 
-    float* fbuf; /* for floats */
-    int    fbuf_alloc;
+struct t_commrec;
+struct t_inputrec;
+struct t_nrnb;
+struct gmx_pme_t;
+struct gmx_wallcycle;
 
-    double* dbuf; /* for doubles */
-    int     dbuf_alloc;
-};
+enum class PmeRunMode;
+namespace gmx
+{
+class DeviceStreamManager;
+}
 
-//! Cleans up the buffers
-void done_mpi_in_place_buf(mpi_in_place_buf_t* buf);
+/*! \brief Called on the nodes that do PME exclusively */
+int gmx_pmeonly(gmx_pme_t*                      pme,
+                const t_commrec*                cr,
+                t_nrnb*                         mynrnb,
+                gmx_wallcycle*                  wcycle,
+                gmx_walltime_accounting_t       walltime_accounting,
+                t_inputrec*                     ir,
+                PmeRunMode                      runMode,
+                const gmx::DeviceStreamManager* deviceStreamManager);
 
 #endif
diff --git a/src/gromacs/ewald/pme_output.h b/src/gromacs/ewald/pme_output.h
new file mode 100644 (file)
index 0000000..bc8a501
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Defines a struct useful for transferring the PME output
+ * values
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_OUTPUT_H
+#define GMX_EWALD_PME_OUTPUT_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+
+// TODO There's little value in computing the Coulomb and LJ virial
+// separately, so we should simplify that.
+// TODO The matrices might be best as a view, but not currently
+// possible. Use mdspan?
+struct PmeOutput
+{
+    //!< Host staging area for PME forces
+    gmx::ArrayRef<gmx::RVec> forces_;
+    //!< True if forces have been staged other false (when forces are reduced on the GPU).
+    bool haveForceOutput_ = false;
+    //!< Host staging area for PME coulomb energy
+    real coulombEnergy_ = 0;
+    //!< Host staging area for PME coulomb virial contributions
+    matrix coulombVirial_ = { { 0 } };
+    //!< Host staging area for PME coulomb dVdl.
+    real coulombDvdl_ = 0;
+    //!< Host staging area for PME LJ energy
+    real lennardJonesEnergy_ = 0;
+    //!< Host staging area for PME LJ virial contributions
+    matrix lennardJonesVirial_ = { { 0 } };
+    //!< Host staging area for PME LJ dVdl. (Not used)
+    real lennardJonesDvdl_ = 0;
+};
+
+#endif
index acd0a25ef74b5b7c75d4943c0248988a7965b1bd..f788992e52b594a149386805a3199be3ec1c1201 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,6 +47,8 @@
 
 #include "gmxpre.h"
 
+#include "pme_pp.h"
+
 #include "config.h"
 
 #include <cstdio>
@@ -70,7 +73,6 @@
 #include "gromacs/utility/gmxmpi.h"
 #include "gromacs/utility/smalloc.h"
 
-#include "pme_internal.h"
 #include "pme_pp_communication.h"
 
 /*! \brief Block to wait for communication to PME ranks to complete
@@ -287,7 +289,7 @@ void gmx_pme_send_coordinates(t_forcerec*           fr,
                               const rvec*           x,
                               real                  lambda_q,
                               real                  lambda_lj,
-                              gmx_bool              bEnerVir,
+                              bool                  computeEnergyAndVirial,
                               int64_t               step,
                               bool                  useGpuPmePpComms,
                               bool                  receiveCoordinateAddressFromPme,
@@ -298,7 +300,7 @@ void gmx_pme_send_coordinates(t_forcerec*           fr,
     wallcycle_start(wcycle, ewcPP_PMESENDX);
 
     unsigned int flags = PP_PME_COORD;
-    if (bEnerVir)
+    if (computeEnergyAndVirial)
     {
         flags |= PP_PME_ENER_VIR;
     }
diff --git a/src/gromacs/ewald/pme_pp.h b/src/gromacs/ewald/pme_pp.h
new file mode 100644 (file)
index 0000000..3bdc575
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file contains function declarations necessary for
+ * mananging the PP side of PME-only ranks.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_PP_H
+#define GMX_EWALD_PME_PP_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_wallcycle;
+struct interaction_const_t;
+struct t_commrec;
+struct t_forcerec;
+
+class GpuEventSynchronizer;
+
+namespace gmx
+{
+class ForceWithVirial;
+class PmePpCommGpu;
+} // namespace gmx
+
+/*! \brief Send the charges and maxshift to out PME-only node. */
+void gmx_pme_send_parameters(const t_commrec*           cr,
+                             const interaction_const_t* ic,
+                             gmx_bool                   bFreeEnergy_q,
+                             gmx_bool                   bFreeEnergy_lj,
+                             real*                      chargeA,
+                             real*                      chargeB,
+                             real*                      sqrt_c6A,
+                             real*                      sqrt_c6B,
+                             real*                      sigmaA,
+                             real*                      sigmaB,
+                             int                        maxshift_x,
+                             int                        maxshift_y);
+
+/*! \brief Send the coordinates to our PME-only node and request a PME calculation */
+void gmx_pme_send_coordinates(t_forcerec*           fr,
+                              const t_commrec*      cr,
+                              const matrix          box,
+                              const rvec*           x,
+                              real                  lambda_q,
+                              real                  lambda_lj,
+                              bool                  computeEnergyAndVirial,
+                              int64_t               step,
+                              bool                  useGpuPmePpComms,
+                              bool                  reinitGpuPmePpComms,
+                              bool                  sendCoordinatesFromGpu,
+                              GpuEventSynchronizer* coordinatesReadyOnDeviceEvent,
+                              gmx_wallcycle*        wcycle);
+
+/*! \brief Tell our PME-only node to finish */
+void gmx_pme_send_finish(const t_commrec* cr);
+
+/*! \brief Tell our PME-only node to reset all cycle and flop counters */
+void gmx_pme_send_resetcounters(const t_commrec* cr, int64_t step);
+
+/*! \brief PP nodes receive the long range forces from the PME nodes */
+void gmx_pme_receive_f(gmx::PmePpCommGpu*    pmePpCommGpu,
+                       const t_commrec*      cr,
+                       gmx::ForceWithVirial* forceWithVirial,
+                       real*                 energy_q,
+                       real*                 energy_lj,
+                       real*                 dvdlambda_q,
+                       real*                 dvdlambda_lj,
+                       bool                  useGpuPmePpComms,
+                       bool                  receivePmeForceToGpu,
+                       float*                pme_cycles);
+
+/*! \brief Tell our PME-only node to switch to a new grid size */
+void gmx_pme_send_switchgrid(const t_commrec* cr, ivec grid_size, real ewaldcoeff_q, real ewaldcoeff_lj);
+
+#endif
index e9d8c4ff697c921ba82a704d3171ba9ae71a49c9..a3e0239e589f0f3c294a0f4318334d9a11cae185 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/gmxmpi.h"
 
+class DeviceContext;
+class DeviceStream;
 class GpuEventSynchronizer;
 
 namespace gmx
 {
 
+class DeviceStreamManager;
+
 /*! \libinternal
 
  * \brief Manages communication related to GPU buffers between this
@@ -61,8 +65,10 @@ public:
     /*! \brief Creates PME-PP GPU communication object
      * \param[in] comm            Communicator used for simulation
      * \param[in] pmeRank         Rank of PME task
+     * \param[in] deviceContext   GPU context.
+     * \param[in] deviceStream    GPU stream.
      */
-    PmePpCommGpu(MPI_Comm comm, int pmeRank);
+    PmePpCommGpu(MPI_Comm comm, int pmeRank, const DeviceContext& deviceContext, const DeviceStream& deviceStream);
     ~PmePpCommGpu();
 
     /*! \brief Perform steps required when buffer size changes
@@ -97,7 +103,7 @@ public:
     /*! \brief
      * Return pointer to event recorded when forces are ready
      */
-    void* getForcesReadySynchronizer();
+    GpuEventSynchronizer* getForcesReadySynchronizer();
 
 private:
     class Impl;
index 0b59ff921201b919324f08af032b6306e9b740fd..d19004e7edada326327458cdc937ae8bfdc7b040 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/gmxmpi.h"
 
-#if GMX_GPU != GMX_GPU_CUDA
+#if !GMX_GPU_CUDA
 
 namespace gmx
 {
 
-/*!\brief Impl class stub. */
+/*!\brief \internal Impl class stub. */
 class PmePpCommGpu::Impl
 {
 };
 
 /*!\brief Constructor stub. */
-PmePpCommGpu::PmePpCommGpu(MPI_Comm gmx_unused comm, int gmx_unused pmeRank) : impl_(nullptr)
+PmePpCommGpu::PmePpCommGpu(MPI_Comm /* comm */,
+                           int /* pmeRank */,
+                           const DeviceContext& /* deviceContext */,
+                           const DeviceStream& /* deviceStream */) :
+    impl_(nullptr)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication was called instead of the correct "
                "implementation.");
 }
@@ -72,43 +76,43 @@ PmePpCommGpu::PmePpCommGpu(MPI_Comm gmx_unused comm, int gmx_unused pmeRank) : i
 PmePpCommGpu::~PmePpCommGpu() = default;
 
 /*!\brief init PME-PP GPU communication stub */
-void PmePpCommGpu::reinit(int gmx_unused size)
+void PmePpCommGpu::reinit(int /* size */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication initialization was called instead of the "
                "correct implementation.");
 }
 
-void PmePpCommGpu::receiveForceFromPmeCudaDirect(void gmx_unused* recvPtr,
-                                                 int gmx_unused recvSize,
-                                                 bool gmx_unused receivePmeForceToGpu)
+void PmePpCommGpu::receiveForceFromPmeCudaDirect(void* /* recvPtr */,
+                                                 int /* recvSize */,
+                                                 bool /* receivePmeForceToGpu */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication was called instead of the correct "
                "implementation.");
 }
 
-void PmePpCommGpu::sendCoordinatesToPmeCudaDirect(void gmx_unused* sendPtr,
-                                                  int gmx_unused sendSize,
-                                                  bool gmx_unused sendPmeCoordinatesFromGpu,
-                                                  GpuEventSynchronizer gmx_unused* coordinatesOnDeviceEvent)
+void PmePpCommGpu::sendCoordinatesToPmeCudaDirect(void* /* sendPtr */,
+                                                  int /* sendSize */,
+                                                  bool /* sendPmeCoordinatesFromGpu */,
+                                                  GpuEventSynchronizer* /* coordinatesOnDeviceEvent */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication was called instead of the correct "
                "implementation.");
 }
 
 void* PmePpCommGpu::getGpuForceStagingPtr()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication was called instead of the correct "
                "implementation.");
     return nullptr;
 }
 
-void* PmePpCommGpu::getForcesReadySynchronizer()
+GpuEventSynchronizer* PmePpCommGpu::getForcesReadySynchronizer()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for PME-PP GPU communication was called instead of the correct "
                "implementation.");
     return nullptr;
@@ -116,4 +120,4 @@ void* PmePpCommGpu::getForcesReadySynchronizer()
 
 } // namespace gmx
 
-#endif /* GMX_GPU != GMX_GPU_CUDA */
+#endif // !GMX_GPU_CUDA
index 827a1bab343f956694a8b09435bed71d99d2195c..acb5998b02271170377c8911d0df2e7a75e5c775 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,6 +48,8 @@
 #include "config.h"
 
 #include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/devicebuffer.h"
 #include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
 #include "gromacs/utility/gmxmpi.h"
 namespace gmx
 {
 
-PmePpCommGpu::Impl::Impl(MPI_Comm comm, int pmeRank) : comm_(comm), pmeRank_(pmeRank)
+PmePpCommGpu::Impl::Impl(MPI_Comm             comm,
+                         int                  pmeRank,
+                         const DeviceContext& deviceContext,
+                         const DeviceStream&  deviceStream) :
+    deviceContext_(deviceContext),
+    pmePpCommStream_(deviceStream),
+    comm_(comm),
+    pmeRank_(pmeRank)
 {
     GMX_RELEASE_ASSERT(
             GMX_THREAD_MPI,
             "PME-PP GPU Communication is currently only supported with thread-MPI enabled");
-    cudaStreamCreate(&pmePpCommStream_);
 }
 
 PmePpCommGpu::Impl::~Impl() = default;
@@ -73,7 +81,7 @@ void PmePpCommGpu::Impl::reinit(int size)
     MPI_Recv(&remotePmeFBuffer_, sizeof(void**), MPI_BYTE, pmeRank_, 0, comm_, MPI_STATUS_IGNORE);
 
     // Reallocate buffer used for staging PME force on GPU
-    reallocateDeviceBuffer(&d_pmeForces_, size, &d_pmeForcesSize_, &d_pmeForcesSizeAlloc_, nullptr);
+    reallocateDeviceBuffer(&d_pmeForces_, size, &d_pmeForcesSize_, &d_pmeForcesSizeAlloc_, deviceContext_);
 #else
     GMX_UNUSED_VALUE(size);
 #endif
@@ -94,7 +102,7 @@ void PmePpCommGpu::Impl::receiveForceFromPmeCudaDirect(void* recvPtr, int recvSi
     // Pull force data from remote GPU
     void*       pmeForcePtr = receivePmeForceToGpu ? static_cast<void*>(d_pmeForces_) : recvPtr;
     cudaError_t stat = cudaMemcpyAsync(pmeForcePtr, remotePmeFBuffer_, recvSize * DIM * sizeof(float),
-                                       cudaMemcpyDefault, pmePpCommStream_);
+                                       cudaMemcpyDefault, pmePpCommStream_.stream());
     CU_RET_ERR(stat, "cudaMemcpyAsync on Recv from PME CUDA direct data transfer failed");
 
     if (receivePmeForceToGpu)
@@ -108,7 +116,7 @@ void PmePpCommGpu::Impl::receiveForceFromPmeCudaDirect(void* recvPtr, int recvSi
     {
         // Ensure CPU waits for PME forces to be copied before reducing
         // them with other forces on the CPU
-        cudaStreamSynchronize(pmePpCommStream_);
+        cudaStreamSynchronize(pmePpCommStream_.stream());
     }
 #else
     GMX_UNUSED_VALUE(recvPtr);
@@ -127,7 +135,7 @@ void PmePpCommGpu::Impl::sendCoordinatesToPmeCudaDirect(void* sendPtr,
     coordinatesReadyOnDeviceEvent->enqueueWaitEvent(pmePpCommStream_);
 
     cudaError_t stat = cudaMemcpyAsync(remotePmeXBuffer_, sendPtr, sendSize * DIM * sizeof(float),
-                                       cudaMemcpyDefault, pmePpCommStream_);
+                                       cudaMemcpyDefault, pmePpCommStream_.stream());
     CU_RET_ERR(stat, "cudaMemcpyAsync on Send to PME CUDA direct data transfer failed");
 
     // Record and send event to allow PME task to sync to above transfer before commencing force calculations
@@ -146,12 +154,18 @@ void* PmePpCommGpu::Impl::getGpuForceStagingPtr()
     return static_cast<void*>(d_pmeForces_);
 }
 
-void* PmePpCommGpu::Impl::getForcesReadySynchronizer()
+GpuEventSynchronizer* PmePpCommGpu::Impl::getForcesReadySynchronizer()
 {
-    return static_cast<void*>(&forcesReadySynchronizer_);
+    return &forcesReadySynchronizer_;
 }
 
-PmePpCommGpu::PmePpCommGpu(MPI_Comm comm, int pmeRank) : impl_(new Impl(comm, pmeRank)) {}
+PmePpCommGpu::PmePpCommGpu(MPI_Comm             comm,
+                           int                  pmeRank,
+                           const DeviceContext& deviceContext,
+                           const DeviceStream&  deviceStream) :
+    impl_(new Impl(comm, pmeRank, deviceContext, deviceStream))
+{
+}
 
 PmePpCommGpu::~PmePpCommGpu() = default;
 
@@ -179,7 +193,7 @@ void* PmePpCommGpu::getGpuForceStagingPtr()
     return impl_->getGpuForceStagingPtr();
 }
 
-void* PmePpCommGpu::getForcesReadySynchronizer()
+GpuEventSynchronizer* PmePpCommGpu::getForcesReadySynchronizer()
 {
     return impl_->getForcesReadySynchronizer();
 }
index 5565bea3705d5284499aca13a51abad17709209b..0630084e59b6950b5c09cdd28969cfd7b3ab1522 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -57,10 +57,13 @@ class PmePpCommGpu::Impl
 
 public:
     /*! \brief Creates PME-PP GPU communication object.
+     *
      * \param[in] comm            Communicator used for simulation
      * \param[in] pmeRank         Rank of PME task
+     * \param[in] deviceContext   GPU context.
+     * \param[in] deviceStream    GPU stream.
      */
-    Impl(MPI_Comm comm, int pmeRank);
+    Impl(MPI_Comm comm, int pmeRank, const DeviceContext& deviceContext, const DeviceStream& deviceStream);
     ~Impl();
 
     /*! \brief Perform steps required when buffer size changes
@@ -112,11 +115,13 @@ public:
     /*! \brief
      * Return pointer to event recorded when forces are ready
      */
-    void* getForcesReadySynchronizer();
+    GpuEventSynchronizer* getForcesReadySynchronizer();
 
 private:
-    //! CUDA stream used for the communication operations in this class
-    cudaStream_t pmePpCommStream_ = nullptr;
+    //! GPU context handle (not used in CUDA)
+    const DeviceContext& deviceContext_;
+    //! Handle for CUDA stream used for the communication operations in this class
+    const DeviceStream& pmePpCommStream_;
     //! Remote location of PME coordinate data buffer
     void* remotePmeXBuffer_ = nullptr;
     //! Remote location of PME force data buffer
index 3ba294d17d5cec5742a3f697f3911882e83935f4..329284d4e4404e88feebf06a1d7636d8727c866c 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 493fb5f8f24db27323a46c484effb4f8c3b11d55..354aa12e5873ee9fdd5ae767e14d8123463e33f3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,7 +50,6 @@
  */
 
 // Assert placeholders, to not rip them out from OpenCL implementation - hopefully they come in handy some day with OpenCL 2
-#define static_assert(a, b)
 #define assert(a)
 
 #define PmeOpenCLKernelParams PmeGpuKernelParamsBase
 /* SPREAD/SPLINE */
 
 #define atomsPerBlock (c_spreadWorkGroupSize / threadsPerAtom)
+#define atomsPerWarp (warp_size / threadsPerAtom)
 
-// spline/spread fused
+// spline/spread fused single grid (without FEP or FEP without energy and virial calculation at this step)
 #define computeSplines 1
 #define spreadCharges 1
-#define CUSTOMIZED_KERNEL_NAME(x) pmeSplineAndSpreadKernel
+#define numGrids 1 /* Using define to avoid a conditional inside the function. */
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSplineAndSpreadKernelSingle
 #include "pme_spread.clh"
 #undef computeSplines
 #undef spreadCharges
+#undef numGrids
 #undef CUSTOMIZED_KERNEL_NAME
 
-// spline
+// spline on single grid (without FEP or FEP without energy and virial calculation at this step)
 #define computeSplines 1
 #define spreadCharges 0
-#define CUSTOMIZED_KERNEL_NAME(x) pmeSplineKernel
+#define numGrids 1 /* Using define to avoid a conditional inside the function. */
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSplineKernelSingle
 #include "pme_spread.clh"
 #undef computeSplines
 #undef spreadCharges
+#undef numGrids
 #undef CUSTOMIZED_KERNEL_NAME
 
-// spread
+// spread on single grid (without FEP or FEP without energy and virial calculation at this step)
 #define computeSplines 0
 #define spreadCharges 1
-#define CUSTOMIZED_KERNEL_NAME(x) pmeSpreadKernel
+#define numGrids 1 /* Using define to avoid a conditional inside the function. */
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSpreadKernelSingle
 #include "pme_spread.clh"
 #undef computeSplines
 #undef spreadCharges
+#undef numGrids
+#undef CUSTOMIZED_KERNEL_NAME
+
+// spline/spread fused on two grids (FEP with energy and virial calculation at this step)
+#define computeSplines 1
+#define spreadCharges 1
+#define numGrids 2 /* Using define to avoid a conditional inside the function. */
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSplineAndSpreadKernelDual
+#include "pme_spread.clh"
+#undef computeSplines
+#undef spreadCharges
+#undef numGrids
+#undef CUSTOMIZED_KERNEL_NAME
+
+// spline on two grids (FEP with energy and virial calculation at this step)
+#define computeSplines 1
+#define spreadCharges 0
+#define numGrids 2 /* Using define to avoid a conditional inside the function. */
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSplineKernelDual
+#include "pme_spread.clh"
+#undef computeSplines
+#undef spreadCharges
+#undef numGrids
+#undef CUSTOMIZED_KERNEL_NAME
+
+// spread on two grids (FEP with energy and virial calculation at this step)
+#define computeSplines 0
+#define spreadCharges 1
+#define numGrids 2 /* Using define to avoid a conditional inside the function. */
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSpreadKernelDual
+#include "pme_spread.clh"
+#undef computeSplines
+#undef spreadCharges
+#undef numGrids
 #undef CUSTOMIZED_KERNEL_NAME
 
 
 
 #define atomsPerBlock (c_gatherWorkGroupSize / threadsPerAtom)
 
-// gather
-#define overwriteForces 1
-#define CUSTOMIZED_KERNEL_NAME(x) pmeGatherKernel
+// gather using single grid
+#define numGrids 1 /* Using define to avoid a conditional inside the function. */
+#define CUSTOMIZED_KERNEL_NAME(x) pmeGatherKernelSingle
 #include "pme_gather.clh"
-#undef overwriteForces
+#undef numGrids
 #undef CUSTOMIZED_KERNEL_NAME
 
-// gather with reduction
-#define overwriteForces 0
-#define CUSTOMIZED_KERNEL_NAME(x) pmeGatherReduceWithInputKernel
+// gather using two grids
+#define numGrids 2 /* Using define to avoid a conditional inside the function. */
+#define CUSTOMIZED_KERNEL_NAME(x) pmeGatherKernelDual
 #include "pme_gather.clh"
-#undef overwriteForces
+#undef numGrids
 #undef CUSTOMIZED_KERNEL_NAME
 
-
 #undef atomsPerBlock
 
 /* SOLVE */
 #define YZX 1
 #define XYZ 2
 
-// solve, YZX dimension order
+// solve, YZX dimension order state A
+#define gridOrdering YZX
+#define computeEnergyAndVirial 0
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveYZXKernelA
+#include "pme_solve.clh"
+#undef gridOrdering
+#undef computeEnergyAndVirial
+#undef CUSTOMIZED_KERNEL_NAME
+
+// solve with reduction, YZX dimension order state A
+#define gridOrdering YZX
+#define computeEnergyAndVirial 1
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveYZXEnergyKernelA
+#include "pme_solve.clh"
+#undef gridOrdering
+#undef computeEnergyAndVirial
+#undef CUSTOMIZED_KERNEL_NAME
+
+// solve, XYZ dimension order state A
+#define gridOrdering XYZ
+#define computeEnergyAndVirial 0
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveXYZKernelA
+#include "pme_solve.clh"
+#undef gridOrdering
+#undef computeEnergyAndVirial
+#undef CUSTOMIZED_KERNEL_NAME
+
+// solve with reduction, XYZ dimension order state A
+#define gridOrdering XYZ
+#define computeEnergyAndVirial 1
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveXYZEnergyKernelA
+#include "pme_solve.clh"
+#undef gridOrdering
+#undef computeEnergyAndVirial
+#undef CUSTOMIZED_KERNEL_NAME
+
+// solve, YZX dimension order state B
 #define gridOrdering YZX
 #define computeEnergyAndVirial 0
-#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveYZXKernel
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveYZXKernelB
 #include "pme_solve.clh"
 #undef gridOrdering
 #undef computeEnergyAndVirial
 #undef CUSTOMIZED_KERNEL_NAME
 
-// solve with reduction, YZX dimension order
+// solve with reduction, YZX dimension order state B
 #define gridOrdering YZX
 #define computeEnergyAndVirial 1
-#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveYZXEnergyKernel
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveYZXEnergyKernelB
 #include "pme_solve.clh"
 #undef gridOrdering
 #undef computeEnergyAndVirial
 #undef CUSTOMIZED_KERNEL_NAME
 
-// solve, XYZ dimension order
+// solve, XYZ dimension order state B
 #define gridOrdering XYZ
 #define computeEnergyAndVirial 0
-#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveXYZKernel
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveXYZKernelB
 #include "pme_solve.clh"
 #undef gridOrdering
 #undef computeEnergyAndVirial
 #undef CUSTOMIZED_KERNEL_NAME
 
-// solve with reduction, XYZ dimension order
+// solve with reduction, XYZ dimension order state B
 #define gridOrdering XYZ
 #define computeEnergyAndVirial 1
-#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveXYZEnergyKernel
+#define CUSTOMIZED_KERNEL_NAME(x) pmeSolveXYZEnergyKernelB
 #include "pme_solve.clh"
 #undef gridOrdering
 #undef computeEnergyAndVirial
index 8b948c2fab07fbbb414f02e70fa7e82df64b3688..a2c8e3b24db5f2d6220de037dbae8aaca9e2dc4d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index ac38117d385facc6e7b63d5f3420a11e948fe172..0c21512b6fbb28a3640af598c31f8e8c545a4ced 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  *  \author Aleksei Iupinov <a.yupinov@gmail.com>
  */
 
-#include "pme_gpu_types.h"
 #include "gromacs/gpu_utils/vectype_ops.clh"
 
+#include "pme_gpu_types.h"
+
 /*! \brief
  * PME complex grid solver kernel function.
  * Please see the file description for additional defines which this kernel expects.
@@ -59,7 +60,8 @@
  * \param[in]     kernelParams         Input PME GPU data in constant memory.
  * \param[in]     gm_splineModuli      B-Spline moduli.
  * \param[out]    gm_virialAndEnergy   Reduced virial and enrgy (only with computeEnergyAndVirial ==
- * true) \param[in,out] gm_grid              Fourier grid to transform.
+ * true)
+ * \param[in,out] gm_grid              Fourier grid to transform.
  */
 __attribute__((work_group_size_hint(c_solveMaxWorkGroupSize, 1, 1)))
 __kernel void CUSTOMIZED_KERNEL_NAME(pme_solve_kernel)(const struct PmeOpenCLKernelParams kernelParams,
@@ -68,7 +70,9 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_solve_kernel)(const struct PmeOpenCLKer
                                                        __global float2* __restrict__ gm_grid)
 {
     /* This kernel supports 2 different grid dimension orderings: YZX and XYZ */
-    int majorDim, middleDim, minorDim;
+    int majorDim;
+    int middleDim;
+    int minorDim;
     if (gridOrdering == YZX)
     {
         majorDim  = YY;
@@ -90,17 +94,19 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_solve_kernel)(const struct PmeOpenCLKer
             gm_splineModuli + kernelParams.grid.splineValuesOffset[minorDim];
 
     /* Various grid sizes and indices */
-    const int localOffsetMinor = 0, localOffsetMajor = 0, localOffsetMiddle = 0; // unused
-    const int localSizeMinor   = kernelParams.grid.complexGridSizePadded[minorDim];
-    const int localSizeMiddle  = kernelParams.grid.complexGridSizePadded[middleDim];
-    const int localCountMiddle = kernelParams.grid.complexGridSize[middleDim];
-    const int localCountMinor  = kernelParams.grid.complexGridSize[minorDim];
-    const int nMajor           = kernelParams.grid.realGridSize[majorDim];
-    const int nMiddle          = kernelParams.grid.realGridSize[middleDim];
-    const int nMinor           = kernelParams.grid.realGridSize[minorDim];
-    const int maxkMajor        = (nMajor + 1) / 2;  // X or Y
-    const int maxkMiddle       = (nMiddle + 1) / 2; // Y OR Z => only check for !YZX
-    const int maxkMinor        = (nMinor + 1) / 2;  // Z or X => only check for YZX
+    const int localOffsetMinor  = 0; // unused
+    const int localOffsetMajor  = 0; // unused
+    const int localOffsetMiddle = 0; // unused
+    const int localSizeMinor    = kernelParams.grid.complexGridSizePadded[minorDim];
+    const int localSizeMiddle   = kernelParams.grid.complexGridSizePadded[middleDim];
+    const int localCountMiddle  = kernelParams.grid.complexGridSize[middleDim];
+    const int localCountMinor   = kernelParams.grid.complexGridSize[minorDim];
+    const int nMajor            = kernelParams.grid.realGridSize[majorDim];
+    const int nMiddle           = kernelParams.grid.realGridSize[middleDim];
+    const int nMinor            = kernelParams.grid.realGridSize[minorDim];
+    const int maxkMajor         = (nMajor + 1) / 2;  // X or Y
+    const int maxkMiddle        = (nMiddle + 1) / 2; // Y OR Z => only check for !YZX
+    const int maxkMinor         = (nMinor + 1) / 2;  // Z or X => only check for YZX
 
     /* Each thread works on one cell of the Fourier space complex 3D grid (gm_grid).
      * Each block handles up to c_solveMaxWorkGroupSize cells -
@@ -112,50 +118,54 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_solve_kernel)(const struct PmeOpenCLKer
     const int gridLineIndex     = threadLocalId / gridLineSize;
     const int gridLineCellIndex = threadLocalId - gridLineSize * gridLineIndex;
     const int gridLinesPerBlock = max((int)(get_local_size(XX)) / gridLineSize, 1);
-    const int activeWarps       = (get_local_size(XX) / warp_size);
-    const int indexMinor        = get_group_id(XX) * get_local_size(XX) + gridLineCellIndex;
-    const int indexMiddle       = get_group_id(YY) * gridLinesPerBlock + gridLineIndex;
-    const int indexMajor        = get_group_id(ZZ);
+    const int activeWarps       = ((int)get_local_size(XX) / warp_size);
+    assert((get_group_id(XX) * get_local_size(XX)) < MAX_INT);
+    const int indexMinor  = (int)get_group_id(XX) * (int)get_local_size(XX) + gridLineCellIndex;
+    const int indexMiddle = (int)get_group_id(YY) * gridLinesPerBlock + gridLineIndex;
+    const int indexMajor  = (int)get_group_id(ZZ);
 
     /* Optional outputs */
-    float energy = 0.0f;
-    float virxx  = 0.0f;
-    float virxy  = 0.0f;
-    float virxz  = 0.0f;
-    float viryy  = 0.0f;
-    float viryz  = 0.0f;
-    float virzz  = 0.0f;
+    float energy = 0.0F;
+    float virxx  = 0.0F;
+    float virxy  = 0.0F;
+    float virxz  = 0.0F;
+    float viryy  = 0.0F;
+    float viryz  = 0.0F;
+    float virzz  = 0.0F;
 
     assert(indexMajor < kernelParams.grid.complexGridSize[majorDim]);
     if ((indexMiddle < localCountMiddle) & (indexMinor < localCountMinor)
         & (gridLineIndex < gridLinesPerBlock))
     {
         /* The offset should be equal to the global thread index for coalesced access */
-        const int gridIndex = (indexMajor * localSizeMiddle + indexMiddle) * localSizeMinor + indexMinor;
-        __global float2* __restrict__ gm_gridCell = gm_grid + gridIndex;
+        const int gridThreadIndex =
+                (indexMajor * localSizeMiddle + indexMiddle) * localSizeMinor + indexMinor;
+        __global float2* __restrict__ gm_gridCell = gm_grid + gridThreadIndex;
 
         const int kMajor = indexMajor + localOffsetMajor;
         /* Checking either X in XYZ, or Y in YZX cases */
-        const float mMajor = (kMajor < maxkMajor) ? kMajor : (kMajor - nMajor);
+        const float mMajor = (float)((kMajor < maxkMajor) ? kMajor : (kMajor - nMajor));
 
         const int kMiddle = indexMiddle + localOffsetMiddle;
-        float     mMiddle = kMiddle;
+        float     mMiddle = (float)kMiddle;
         /* Checking Y in XYZ case */
         if (gridOrdering == XYZ)
         {
-            mMiddle = (kMiddle < maxkMiddle) ? kMiddle : (kMiddle - nMiddle);
+            mMiddle = (float)((kMiddle < maxkMiddle) ? kMiddle : (kMiddle - nMiddle));
         }
         const int kMinor = localOffsetMinor + indexMinor;
-        float     mMinor = kMinor;
+        float     mMinor = (float)kMinor;
         /* Checking X in YZX case */
         if (gridOrdering == YZX)
         {
-            mMinor = (kMinor < maxkMinor) ? kMinor : (kMinor - nMinor);
+            mMinor = (float)((kMinor < maxkMinor) ? kMinor : (kMinor - nMinor));
         }
         /* We should skip the k-space point (0,0,0) */
         const bool notZeroPoint = (kMinor > 0) | (kMajor > 0) | (kMiddle > 0);
 
-        float mX, mY, mZ;
+        float mX;
+        float mY;
+        float mZ;
         if (gridOrdering == YZX)
         {
             mX = mMinor;
@@ -170,19 +180,20 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_solve_kernel)(const struct PmeOpenCLKer
         }
 
         /* 0.5 correction factor for the first and last components of a Z dimension */
-        float corner_fac = 1.0f;
+        float       corner_fac   = 1.0F;
+        const float z_corner_fac = 0.5F;
         if (gridOrdering == YZX)
         {
             if ((kMiddle == 0) | (kMiddle == maxkMiddle))
             {
-                corner_fac = 0.5f;
+                corner_fac = z_corner_fac;
             }
         }
         if (gridOrdering == XYZ)
         {
             if ((kMinor == 0) | (kMinor == maxkMinor))
             {
-                corner_fac = 0.5f;
+                corner_fac = z_corner_fac;
             }
         }
 
@@ -196,11 +207,11 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_solve_kernel)(const struct PmeOpenCLKer
                                + mZ * kernelParams.current.recipBox[ZZ][ZZ];
 
             const float m2k = mhxk * mhxk + mhyk * mhyk + mhzk * mhzk;
-            assert(m2k != 0.0f);
+            assert(m2k != 0.0F);
             const float denom = m2k * M_PI_F * kernelParams.current.boxVolume * gm_splineValueMajor[kMajor]
                                 * gm_splineValueMiddle[kMiddle] * gm_splineValueMinor[kMinor];
             assert(isfinite(denom));
-            assert(denom != 0.0f);
+            assert(denom != 0.0F);
             const float tmp1   = exp(-kernelParams.grid.ewaldFactor * m2k);
             const float etermk = kernelParams.constants.elFactor * tmp1 / denom;
 
@@ -214,9 +225,9 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_solve_kernel)(const struct PmeOpenCLKer
             if (computeEnergyAndVirial)
             {
                 const float tmp1k =
-                        2.0f * (gridValue.x * oldGridValue.x + gridValue.y * oldGridValue.y);
+                        2.0F * (gridValue.x * oldGridValue.x + gridValue.y * oldGridValue.y);
 
-                const float vfactor = (kernelParams.grid.ewaldFactor + 1.0f / m2k) * 2.0f;
+                const float vfactor = (kernelParams.grid.ewaldFactor + 1.0F / m2k) * 2.0F;
                 const float ets2    = corner_fac * tmp1k;
                 energy              = ets2;
 
@@ -257,7 +268,9 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_solve_kernel)(const struct PmeOpenCLKer
             sm_virialAndEnergy[2 * warp_size + lane] = virzz;
             sm_virialAndEnergy[3 * warp_size + lane] = virxy;
             sm_virialAndEnergy[4 * warp_size + lane] = virxz;
+            // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
             sm_virialAndEnergy[5 * warp_size + lane] = viryz;
+            // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
             sm_virialAndEnergy[6 * warp_size + lane] = energy;
         }
         barrier(CLK_LOCAL_MEM_FENCE);
@@ -268,7 +281,9 @@ __kernel void CUSTOMIZED_KERNEL_NAME(pme_solve_kernel)(const struct PmeOpenCLKer
             atomicAdd_l_f(sm_virialAndEnergy + 2 * warp_size + lane, virzz);
             atomicAdd_l_f(sm_virialAndEnergy + 3 * warp_size + lane, virxy);
             atomicAdd_l_f(sm_virialAndEnergy + 4 * warp_size + lane, virxz);
+            // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
             atomicAdd_l_f(sm_virialAndEnergy + 5 * warp_size + lane, viryz);
+            // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
             atomicAdd_l_f(sm_virialAndEnergy + 6 * warp_size + lane, energy);
         }
         barrier(CLK_LOCAL_MEM_FENCE);
index 406e701e2d9976bf0664b5064bc93305f6558923..99ffa7f12038ed6b041fdafc8f27119c59f752d4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,6 +53,7 @@
 #include "gromacs/utility/smalloc.h"
 
 #include "pme_internal.h"
+#include "pme_output.h"
 
 #if GMX_SIMD_HAVE_REAL
 /* Turn on arbitrary width SIMD intrinsics for PME solve */
@@ -327,7 +329,7 @@ using PME_T = SimdReal;
 using PME_T = real;
 #endif
 
-int solve_pme_yzx(const gmx_pme_t* pme, t_complex* grid, real vol, gmx_bool bEnerVir, int nthread, int thread)
+int solve_pme_yzx(const gmx_pme_t* pme, t_complex* grid, real vol, bool computeEnergyAndVirial, int nthread, int thread)
 {
     /* do recip sum over local cells in grid */
     /* y major, z middle, x minor or continuous */
@@ -431,7 +433,7 @@ int solve_pme_yzx(const gmx_pme_t* pme, t_complex* grid, real vol, gmx_bool bEne
         }
         kxend = local_offset[XX] + local_ndata[XX];
 
-        if (bEnerVir)
+        if (computeEnergyAndVirial)
         {
             /* More expensive inner loop, especially because of the storage
              * of the mh elements in array's.
@@ -561,7 +563,7 @@ int solve_pme_yzx(const gmx_pme_t* pme, t_complex* grid, real vol, gmx_bool bEne
         }
     }
 
-    if (bEnerVir)
+    if (computeEnergyAndVirial)
     {
         /* Update virial with local values.
          * The virial is symmetric by definition.
@@ -584,7 +586,13 @@ int solve_pme_yzx(const gmx_pme_t* pme, t_complex* grid, real vol, gmx_bool bEne
     return local_ndata[YY] * local_ndata[XX];
 }
 
-int solve_pme_lj_yzx(const gmx_pme_t* pme, t_complex** grid, gmx_bool bLB, real vol, gmx_bool bEnerVir, int nthread, int thread)
+int solve_pme_lj_yzx(const gmx_pme_t* pme,
+                     t_complex**      grid,
+                     gmx_bool         bLB,
+                     real             vol,
+                     bool             computeEnergyAndVirial,
+                     int              nthread,
+                     int              thread)
 {
     /* do recip sum over local cells in grid */
     /* y major, z middle, x minor or continuous */
@@ -667,7 +675,7 @@ int solve_pme_lj_yzx(const gmx_pme_t* pme, t_complex** grid, gmx_bool bLB, real
 
         kxstart = local_offset[XX];
         kxend   = local_offset[XX] + local_ndata[XX];
-        if (bEnerVir)
+        if (computeEnergyAndVirial)
         {
             /* More expensive inner loop, especially because of the
              * storage of the mh elements in array's.  Because x is the
@@ -890,7 +898,7 @@ int solve_pme_lj_yzx(const gmx_pme_t* pme, t_complex** grid, gmx_bool bLB, real
             }
         }
     }
-    if (bEnerVir)
+    if (computeEnergyAndVirial)
     {
         work->vir_lj[XX][XX] = 0.25 * virxx;
         work->vir_lj[YY][YY] = 0.25 * viryy;
index 784de81f96a14d9d2ac725b0eea48001c5225435..3f5d2d06f45b5a0121562c1d69ef6b1d99258535 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * PME complex grid solver kernel function.
  *
  * \tparam[in] gridOrdering             Specifies the dimension ordering of the complex grid.
- * \tparam[in] computeEnergyAndVirial   Tells if the reciprocal energy and virial should be
- * computed. \param[in]  kernelParams             Input PME CUDA data in constant memory.
+ * \tparam[in] computeEnergyAndVirial   Tells if the reciprocal energy and virial should be computed.
+ * \tparam[in] gridIndex                The index of the grid to use in the kernel.
+ * \param[in]  kernelParams             Input PME CUDA data in constant memory.
  */
-template<GridOrdering gridOrdering, bool computeEnergyAndVirial>
+template<GridOrdering gridOrdering, bool computeEnergyAndVirial, const int gridIndex>
 __launch_bounds__(c_solveMaxThreadsPerBlock) CLANG_DISABLE_OPTIMIZATION_ATTRIBUTE __global__
         void pme_solve_kernel(const struct PmeGpuCudaKernelParams kernelParams)
 {
@@ -80,14 +81,14 @@ __launch_bounds__(c_solveMaxThreadsPerBlock) CLANG_DISABLE_OPTIMIZATION_ATTRIBUT
     }
 
     /* Global memory pointers */
-    const float* __restrict__ gm_splineValueMajor =
-            kernelParams.grid.d_splineModuli + kernelParams.grid.splineValuesOffset[majorDim];
-    const float* __restrict__ gm_splineValueMiddle =
-            kernelParams.grid.d_splineModuli + kernelParams.grid.splineValuesOffset[middleDim];
-    const float* __restrict__ gm_splineValueMinor =
-            kernelParams.grid.d_splineModuli + kernelParams.grid.splineValuesOffset[minorDim];
-    float* __restrict__ gm_virialAndEnergy = kernelParams.constants.d_virialAndEnergy;
-    float2* __restrict__ gm_grid           = (float2*)kernelParams.grid.d_fourierGrid;
+    const float* __restrict__ gm_splineValueMajor = kernelParams.grid.d_splineModuli[gridIndex]
+                                                    + kernelParams.grid.splineValuesOffset[majorDim];
+    const float* __restrict__ gm_splineValueMiddle = kernelParams.grid.d_splineModuli[gridIndex]
+                                                     + kernelParams.grid.splineValuesOffset[middleDim];
+    const float* __restrict__ gm_splineValueMinor = kernelParams.grid.d_splineModuli[gridIndex]
+                                                    + kernelParams.grid.splineValuesOffset[minorDim];
+    float* __restrict__ gm_virialAndEnergy = kernelParams.constants.d_virialAndEnergy[gridIndex];
+    float2* __restrict__ gm_grid           = (float2*)kernelParams.grid.d_fourierGrid[gridIndex];
 
     /* Various grid sizes and indices */
     const int localOffsetMinor = 0, localOffsetMajor = 0, localOffsetMiddle = 0; // unused
@@ -131,8 +132,9 @@ __launch_bounds__(c_solveMaxThreadsPerBlock) CLANG_DISABLE_OPTIMIZATION_ATTRIBUT
         & (gridLineIndex < gridLinesPerBlock))
     {
         /* The offset should be equal to the global thread index for coalesced access */
-        const int gridIndex = (indexMajor * localSizeMiddle + indexMiddle) * localSizeMinor + indexMinor;
-        float2* __restrict__ gm_gridCell = gm_grid + gridIndex;
+        const int gridThreadIndex =
+                (indexMajor * localSizeMiddle + indexMiddle) * localSizeMinor + indexMinor;
+        float2* __restrict__ gm_gridCell = gm_grid + gridThreadIndex;
 
         const int kMajor = indexMajor + localOffsetMajor;
         /* Checking either X in XYZ, or Y in YZX cases */
@@ -352,7 +354,11 @@ __launch_bounds__(c_solveMaxThreadsPerBlock) CLANG_DISABLE_OPTIMIZATION_ATTRIBUT
 }
 
 //! Kernel instantiations
-template __global__ void pme_solve_kernel<GridOrdering::YZX, true>(const PmeGpuCudaKernelParams);
-template __global__ void pme_solve_kernel<GridOrdering::YZX, false>(const PmeGpuCudaKernelParams);
-template __global__ void pme_solve_kernel<GridOrdering::XYZ, true>(const PmeGpuCudaKernelParams);
-template __global__ void pme_solve_kernel<GridOrdering::XYZ, false>(const PmeGpuCudaKernelParams);
+template __global__ void pme_solve_kernel<GridOrdering::YZX, true, 0>(const PmeGpuCudaKernelParams);
+template __global__ void pme_solve_kernel<GridOrdering::YZX, false, 0>(const PmeGpuCudaKernelParams);
+template __global__ void pme_solve_kernel<GridOrdering::XYZ, true, 0>(const PmeGpuCudaKernelParams);
+template __global__ void pme_solve_kernel<GridOrdering::XYZ, false, 0>(const PmeGpuCudaKernelParams);
+template __global__ void pme_solve_kernel<GridOrdering::YZX, true, 1>(const PmeGpuCudaKernelParams);
+template __global__ void pme_solve_kernel<GridOrdering::YZX, false, 1>(const PmeGpuCudaKernelParams);
+template __global__ void pme_solve_kernel<GridOrdering::XYZ, true, 1>(const PmeGpuCudaKernelParams);
+template __global__ void pme_solve_kernel<GridOrdering::XYZ, false, 1>(const PmeGpuCudaKernelParams);
index bcf33c740d32ede383f30b3738a74a0158843a39..61da89d991b92c55d6842998fc6bf971ca8db8a1 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -68,13 +69,13 @@ void get_pme_ener_vir_q(pme_solve_work_t* work, int nthread, PmeOutput* output);
  */
 void get_pme_ener_vir_lj(pme_solve_work_t* work, int nthread, PmeOutput* output);
 
-int solve_pme_yzx(const gmx_pme_t* pme, t_complex* grid, real vol, gmx_bool bEnerVir, int nthread, int thread);
+int solve_pme_yzx(const gmx_pme_t* pme, t_complex* grid, real vol, bool computeEnergyAndVirial, int nthread, int thread);
 
 int solve_pme_lj_yzx(const gmx_pme_t* pme,
                      t_complex**      grid,
                      gmx_bool         bLB,
                      real             vol,
-                     gmx_bool         bEnerVir,
+                     bool             computeEnergyAndVirial,
                      int              nthread,
                      int              thread);
 
index 3a7b7fa403cc80245d484d14b51b92c308bd2cc6..cc9307b9bfd9801228a8fbf82bf135c2ef47ef94 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 8215614ef04fe6229034494ff396b31a6564213e..3342120d51d46a67528ffebe718ed22e80f763bf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  *  \author Aleksei Iupinov <a.yupinov@gmail.com>
  */
 
-#include "pme_gpu_types.h"
-#include "pme_gpu_utils.clh"
 #include "gromacs/gpu_utils/vectype_ops.clh"
 
+#include "pme_gpu_types.h"
+#include "pme_gpu_calculate_splines.clh"
+
 /*
  * This define affects the spline calculation behaviour in the kernel.
  * 0: a single GPU thread handles a single dimension of a single particle (calculating and storing
 /*! \brief
  * General purpose function for loading atom-related data from global to shared memory.
  *
- * \param[in]  kernelParams      Input PME GPU data in constant memory.
  * \param[out] sm_destination    Local memory array for output.
  * \param[in]  gm_source         Global memory array for input.
- * \param[in] dataCountPerAtom   Number of data elements per single atom (e.g. DIM for an rvec coordinates array).
+ * \param[in]  dataCountPerAtom  Number of data elements per single
+ * atom (e.g. DIM for an rvec coordinates array).
  *
  */
-inline void pme_gpu_stage_atom_data(const struct PmeOpenCLKernelParams kernelParams,
-                                    __local float* __restrict__ sm_destination,
+inline void pme_gpu_stage_atom_data(__local float* __restrict__ sm_destination,
                                     __global const float* __restrict__ gm_source,
                                     const int dataCountPerAtom)
 {
-    const size_t threadLocalIndex =
-            ((get_local_id(2) * get_local_size(1) + get_local_id(1)) * get_local_size(0) + get_local_id(0));
-    const size_t localIndex      = threadLocalIndex;
-    const size_t globalIndexBase = get_group_id(XX) * atomsPerBlock * dataCountPerAtom;
-    const size_t globalIndex     = globalIndexBase + localIndex;
-    const int    globalCheck =
-            pme_gpu_check_atom_data_index(globalIndex, kernelParams.atoms.nAtoms * dataCountPerAtom);
-    if ((localIndex < atomsPerBlock * dataCountPerAtom) & globalCheck)
+    assert((get_local_id(2) * get_local_size(1) + get_local_id(1)) * get_local_size(0) + get_local_id(0)
+           < MAX_INT);
+    const int threadLocalIndex =
+            (int)((get_local_id(2) * get_local_size(1) + get_local_id(1)) * get_local_size(0)
+                  + get_local_id(0));
+    const int localIndex      = threadLocalIndex;
+    const int globalIndexBase = (int)get_group_id(XX) * atomsPerBlock * dataCountPerAtom;
+    const int globalIndex     = globalIndexBase + localIndex;
+    if (localIndex < atomsPerBlock * dataCountPerAtom)
     {
         assert(isfinite(float(gm_source[globalIndex])));
         sm_destination[localIndex] = gm_source[globalIndex];
@@ -113,24 +114,27 @@ inline void pme_gpu_stage_atom_data(const struct PmeOpenCLKernelParams kernelPar
  * \param[out] gm_dtheta                Atom spline parameter derivatives
  * \param[out] gm_gridlineIndices       Same as \p sm_gridlineIndices but in global memory.
  * \param[in]  gm_fractShiftsTable      Atom fractional coordinates correction table
- * \param[in]  gm_gridlineIndicesTable  Atom fractional coordinates correction table
+ * \param[in]  gm_gridlineIndicesTable  Table with atom gridline indices
  */
-inline void calculate_splines(const struct PmeOpenCLKernelParams kernelParams,
-                              const int                          atomIndexOffset,
-                              __local const float* __restrict__ sm_coordinates,
-                              __local const float* __restrict__ sm_coefficients,
-                              __local float* __restrict__ sm_theta,
-                              __local int* __restrict__ sm_gridlineIndices,
-                              __local float* __restrict__ sm_fractCoords,
-                              __global float* __restrict__ gm_theta,
-                              __global float* __restrict__ gm_dtheta,
-                              __global int* __restrict__ gm_gridlineIndices,
-                              __global const float* __restrict__ gm_fractShiftsTable,
-                              __global const int* __restrict__ gm_gridlineIndicesTable)
+gmx_opencl_inline void calculate_splines(const struct PmeOpenCLKernelParams kernelParams,
+                                         const int                          atomIndexOffset,
+                                         __local const float* __restrict__ sm_coordinates,
+                                         __local const float* __restrict__ sm_coefficients,
+                                         __local float* __restrict__ sm_theta,
+                                         __local int* __restrict__ sm_gridlineIndices,
+                                         __local float* __restrict__ sm_fractCoords,
+                                         __global float* __restrict__ gm_theta,
+                                         __global float* __restrict__ gm_dtheta,
+                                         __global int* __restrict__ gm_gridlineIndices,
+                                         __global const float* __restrict__ gm_fractShiftsTable,
+                                         __global const int* __restrict__ gm_gridlineIndicesTable)
 {
     /* Thread index w.r.t. block */
+    assert((get_local_id(2) * get_local_size(1) + get_local_id(1)) * get_local_size(0) + get_local_id(0)
+           < MAX_INT);
     const int threadLocalIndex =
-            ((get_local_id(2) * get_local_size(1) + get_local_id(1)) * get_local_size(0) + get_local_id(0));
+            (int)((get_local_id(2) * get_local_size(1) + get_local_id(1)) * get_local_size(0)
+                  + get_local_id(0));
     /* Warp index w.r.t. block - could probably be obtained easier? */
     const int warpIndex = threadLocalIndex / warp_size;
     /* Thread index w.r.t. warp */
@@ -140,8 +144,6 @@ inline void calculate_splines(const struct PmeOpenCLKernelParams kernelParams,
     /* Atom index w.r.t. block/shared memory */
     const int atomIndexLocal = warpIndex * atomsPerWarp + atomWarpIndex;
 
-    /* Atom index w.r.t. global memory */
-    const int atomIndexGlobal = atomIndexOffset + atomIndexLocal;
     /* Spline contribution index in one dimension */
     const int orderIndex = threadWarpIndex / (atomsPerWarp * DIM);
     /* Dimension index */
@@ -172,15 +174,16 @@ inline void calculate_splines(const struct PmeOpenCLKernelParams kernelParams,
 #    define SPLINE_DATA(i) (*SPLINE_DATA_PTR(i))
 
     const int localCheck = (dimIndex < DIM) && (orderIndex < (PME_GPU_PARALLEL_SPLINE ? order : 1));
-    const int globalCheck = pme_gpu_check_atom_data_index(atomIndexGlobal, kernelParams.atoms.nAtoms);
 
-    if (localCheck && globalCheck)
+    if (localCheck)
     {
         /* Indices interpolation */
         if (orderIndex == 0)
         {
-            int          tableIndex, tInt;
-            float        n, t;
+            int          tableIndex;
+            int          tInt;
+            float        n;
+            float        t;
             const float3 x = vload3(atomIndexLocal, sm_coordinates);
 
             /* Accessing fields in fshOffset/nXYZ/recipbox/... with dimIndex offset
@@ -213,13 +216,17 @@ inline void calculate_splines(const struct PmeOpenCLKernelParams kernelParams,
                                 .z
                         * kernelParams.current.recipBox[dimIndex][ZZ];
                     break;
+                default:
+                    assert(false);
+                    return;
+                    break;
             }
             const float shift = c_pmeMaxUnitcellShift;
 
             /* Fractional coordinates along box vectors, adding a positive shift to ensure t is positive for triclinic boxes */
             t                                 = (t + shift) * n;
             tInt                              = (int)t;
-            sm_fractCoords[sharedMemoryIndex] = t - tInt;
+            sm_fractCoords[sharedMemoryIndex] = t - (float)tInt;
             tableIndex += tInt;
             assert(tInt >= 0);
             assert(tInt < c_pmeNeighborUnitcellCount * n);
@@ -241,23 +248,24 @@ inline void calculate_splines(const struct PmeOpenCLKernelParams kernelParams,
             assert(isfinite(dr));
 
             /* dr is relative offset from lower cell limit */
-            *SPLINE_DATA_PTR(order - 1) = 0.0f;
+            *SPLINE_DATA_PTR(order - 1) = 0.0F;
             *SPLINE_DATA_PTR(1)         = dr;
-            *SPLINE_DATA_PTR(0)         = 1.0f - dr;
+            *SPLINE_DATA_PTR(0)         = 1.0F - dr;
 
 #    pragma unroll order
             for (int k = 3; k < order; k++)
             {
-                div                     = 1.0f / (k - 1.0f);
+                div                     = 1.0F / ((float)k - 1.0F);
                 *SPLINE_DATA_PTR(k - 1) = div * dr * SPLINE_DATA(k - 2);
 #    pragma unroll
                 for (int l = 1; l < (k - 1); l++)
                 {
-                    *SPLINE_DATA_PTR(k - l - 1) = div
-                                                  * ((dr + l) * SPLINE_DATA(k - l - 2)
-                                                     + (k - l - dr) * SPLINE_DATA(k - l - 1));
+                    *SPLINE_DATA_PTR(k - l - 1) =
+                            div
+                            * ((dr + (float)l) * SPLINE_DATA(k - l - 2)
+                               + ((float)k - (float)l - dr) * SPLINE_DATA(k - l - 1));
                 }
-                *SPLINE_DATA_PTR(0) = div * (1.0f - dr) * SPLINE_DATA(0);
+                *SPLINE_DATA_PTR(0) = div * (1.0F - dr) * SPLINE_DATA(0);
             }
 
             const int thetaIndexBase        = getSplineParamIndexBase(warpIndex, atomWarpIndex);
@@ -274,21 +282,21 @@ inline void calculate_splines(const struct PmeOpenCLKernelParams kernelParams,
                 const int thetaIndex       = getSplineParamIndex(thetaIndexBase, dimIndex, o);
                 const int thetaGlobalIndex = thetaGlobalOffsetBase + thetaIndex;
 
-                const float dtheta = ((o > 0) ? SPLINE_DATA(o - 1) : 0.0f) - SPLINE_DATA(o);
+                const float dtheta = ((o > 0) ? SPLINE_DATA(o - 1) : 0.0F) - SPLINE_DATA(o);
                 assert(isfinite(dtheta));
                 gm_dtheta[thetaGlobalIndex] = dtheta;
             }
 
-            div                         = 1.0f / (order - 1.0f);
+            div                         = 1.0F / (order - 1.0F);
             *SPLINE_DATA_PTR(order - 1) = div * dr * SPLINE_DATA(order - 2);
 #    pragma unroll
             for (int k = 1; k < (order - 1); k++)
             {
                 *SPLINE_DATA_PTR(order - k - 1) = div
-                                                  * ((dr + k) * SPLINE_DATA(order - k - 2)
+                                                  * ((dr + (float)k) * SPLINE_DATA(order - k - 2)
                                                      + (order - k - dr) * SPLINE_DATA(order - k - 1));
             }
-            *SPLINE_DATA_PTR(0) = div * (1.0f - dr) * SPLINE_DATA(0);
+            *SPLINE_DATA_PTR(0) = div * (1.0F - dr) * SPLINE_DATA(0);
 
             /* Storing the spline values (theta) */
 #    if !PME_GPU_PARALLEL_SPLINE
@@ -314,17 +322,16 @@ inline void calculate_splines(const struct PmeOpenCLKernelParams kernelParams,
  * Second stage of the whole kernel.
  *
  * \param[in]  kernelParams         Input PME GPU data in constant memory.
- * \param[in]  atomIndexOffset      Starting atom index for the execution block w.r.t. global
- * memory. \param[in]  sm_coefficients      Atom coefficents/charges in the shared memory. \param[in]
- * sm_gridlineIndices   Atom gridline indices in the shared memory. \param[in]  sm_theta Atom spline
- * values in the shared memory. \param[out] gm_grid              Global 3D grid for spreading.
+ * \param[in]  sm_coefficients      Atom coefficents/charges in the shared memory.
+ * \param[in]  sm_gridlineIndices   Atom gridline indices in the shared memory.
+ * \param[in]  sm_theta             Atom spline values in the shared memory.
+ * \param[out] gm_grid              Global 3D grid for spreading.
  */
-inline void spread_charges(const struct PmeOpenCLKernelParams kernelParams,
-                           int                                atomIndexOffset,
-                           __local const float* __restrict__ sm_coefficients,
-                           __local const int* __restrict__ sm_gridlineIndices,
-                           __local const float* __restrict__ sm_theta,
-                           __global float* __restrict__ gm_grid)
+gmx_opencl_inline void spread_charges(const struct PmeOpenCLKernelParams kernelParams,
+                                      __local const float* __restrict__ sm_coefficients,
+                                      __local const int* __restrict__ sm_gridlineIndices,
+                                      __local const float* __restrict__ sm_theta,
+                                      __global float* __restrict__ gm_grid)
 {
     const int nx  = kernelParams.grid.realGridSize[XX];
     const int ny  = kernelParams.grid.realGridSize[YY];
@@ -332,14 +339,14 @@ inline void spread_charges(const struct PmeOpenCLKernelParams kernelParams,
     const int pny = kernelParams.grid.realGridSizePadded[YY];
     const int pnz = kernelParams.grid.realGridSizePadded[ZZ];
 
-    const int offx = 0, offy = 0, offz = 0; // unused for now
+    const int offx = 0;
+    const int offy = 0;
+    const int offz = 0;
 
-    const int atomIndexLocal  = get_local_id(ZZ);
-    const int atomIndexGlobal = atomIndexOffset + atomIndexLocal;
+    const int atomIndexLocal = get_local_id(ZZ);
 
-    const int globalCheck = pme_gpu_check_atom_data_index(atomIndexGlobal, kernelParams.atoms.nAtoms);
     const int chargeCheck = pme_gpu_check_atom_charge(sm_coefficients[atomIndexLocal]);
-    if (chargeCheck & globalCheck)
+    if (chargeCheck)
     {
         // Spline Y/Z coordinates
         const int ithy   = get_local_id(YY);
@@ -398,10 +405,17 @@ inline void spread_charges(const struct PmeOpenCLKernelParams kernelParams,
  * \param[in,out] gm_theta                 Atom spline parameter values
  * \param[out]    gm_dtheta                Atom spline parameter derivatives
  * \param[in,out] gm_gridlineIndices       Atom gridline indices (ivec)
- * \param[out]    gm_grid                  Global 3D grid for charge spreading.
+ * \param[out]    gm_gridA                 Global 3D grid for charge spreading.
+ * Grid for the unperturbed state, FEP state A or the single grid used for
+ * interpolated coefficients on one grid in FEP A/B.
+ * \param[out]    gm_gridB                 Global 3D grid for charge spreading.
+ * FEP state B when using dual grids (when calculating energy and virials).
  * \param[in]     gm_fractShiftsTable      Atom fractional coordinates correction table
- * \param[in]     gm_gridlineIndicesTable  Atom fractional coordinates correction table
- * \param[in]     gm_coefficients          Atom charges/coefficients.
+ * \param[in]     gm_gridlineIndicesTable  Table with atom gridline indices
+ * \param[in]     gm_coefficientsA         Atom charges/coefficients of the unperturbed state
+ * or FEP state A.
+ * \param[in]     gm_coefficientsB         Atom charges/coefficients in FEP state B. Only
+ * used when spreading interpolated coefficients on one grid.
  * \param[in]     gm_coordinates           Atom coordinates (rvec)
  */
 __attribute__((reqd_work_group_size(order, order, atomsPerBlock))) __kernel void CUSTOMIZED_KERNEL_NAME(
@@ -409,10 +423,12 @@ __attribute__((reqd_work_group_size(order, order, atomsPerBlock))) __kernel void
                                       __global float* __restrict__ gm_theta,
                                       __global float* __restrict__ gm_dtheta,
                                       __global int* __restrict__ gm_gridlineIndices,
-                                      __global float* __restrict__ gm_grid,
+                                      __global float* __restrict__ gm_gridA,
+                                      __global float* __restrict__ gm_gridB,
                                       __global const float* __restrict__ gm_fractShiftsTable,
                                       __global const int* __restrict__ gm_gridlineIndicesTable,
-                                      __global const float* __restrict__ gm_coefficients,
+                                      __global const float* __restrict__ gm_coefficientsA,
+                                      __global const float* __restrict__ gm_coefficientsB,
                                       __global const float* __restrict__ gm_coordinates)
 {
     // Gridline indices, ivec
@@ -426,15 +442,16 @@ __attribute__((reqd_work_group_size(order, order, atomsPerBlock))) __kernel void
     // Staging coordinates - only for spline computation
     __local float sm_coordinates[DIM * atomsPerBlock];
 
-    const int atomIndexOffset = get_group_id(XX) * atomsPerBlock;
+    const int atomIndexOffset = (int)get_group_id(XX) * atomsPerBlock;
+    int       gridIndex       = 0;
 
     /* Staging coefficients/charges for both spline and spread */
-    pme_gpu_stage_atom_data(kernelParams, sm_coefficients, gm_coefficients, 1);
+    pme_gpu_stage_atom_data(sm_coefficients, gm_coefficientsA, 1);
 
     if (computeSplines)
     {
         /* Staging coordinates */
-        pme_gpu_stage_atom_data(kernelParams, sm_coordinates, gm_coordinates, DIM);
+        pme_gpu_stage_atom_data(sm_coordinates, gm_coordinates, DIM);
 
         barrier(CLK_LOCAL_MEM_FENCE);
         calculate_splines(kernelParams, atomIndexOffset, sm_coordinates, sm_coefficients, sm_theta,
@@ -454,9 +471,9 @@ __attribute__((reqd_work_group_size(order, order, atomsPerBlock))) __kernel void
          * as in after running the spline kernel)
          */
         /* Spline data - only thetas (dthetas will only be needed in gather) */
-        pme_gpu_stage_atom_data(kernelParams, sm_theta, gm_theta, DIM * order);
+        pme_gpu_stage_atom_data(sm_theta, gm_theta, DIM * order);
         /* Gridline indices - they're actually int and not float, but C99 is angry about overloads */
-        pme_gpu_stage_atom_data(kernelParams, (__local float*)sm_gridlineIndices,
+        pme_gpu_stage_atom_data((__local float*)sm_gridlineIndices,
                                 (__global const float*)gm_gridlineIndices, DIM);
 
         barrier(CLK_LOCAL_MEM_FENCE);
@@ -464,6 +481,17 @@ __attribute__((reqd_work_group_size(order, order, atomsPerBlock))) __kernel void
     /* Spreading */
     if (spreadCharges)
     {
-        spread_charges(kernelParams, atomIndexOffset, sm_coefficients, sm_gridlineIndices, sm_theta, gm_grid);
+        spread_charges(kernelParams, sm_coefficients, sm_gridlineIndices, sm_theta, gm_gridA);
+    }
+    if (numGrids == 2)
+    {
+        barrier(CLK_LOCAL_MEM_FENCE);
+        gridIndex = 1;
+        pme_gpu_stage_atom_data(sm_coefficients, gm_coefficientsB, 1);
+        barrier(CLK_LOCAL_MEM_FENCE);
+        if (spreadCharges)
+        {
+            spread_charges(kernelParams, sm_coefficients, sm_gridlineIndices, sm_theta, gm_gridB);
+        }
     }
 }
index bc8842be3469cf5c83707f6560e356de87dedd72..c5711797a46c428be93b15126c7c886b4a036b5a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -57,6 +58,7 @@
 #include "pme_internal.h"
 #include "pme_simd.h"
 #include "pme_spline_work.h"
+#include "spline_vectors.h"
 
 /* TODO consider split of pme-spline from this file */
 
index 3d02e43d0d1dc5b3c63452f7821a71b0cd9782bd..3cf3a1f7ca77599297e51adb4a0d57b043f7117c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013-2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013-2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <cassert>
 
 #include "gromacs/gpu_utils/cuda_kernel_utils.cuh"
+#include "gromacs/gpu_utils/typecasts.cuh"
 
 #include "pme.cuh"
-#include "pme_calculate_splines.cuh"
-#include "pme_gpu_utils.h"
+#include "pme_gpu_calculate_splines.cuh"
 #include "pme_grid.h"
 
+/*
+ * This define affects the spline calculation behaviour in the kernel.
+ * 0: a single GPU thread handles a single dimension of a single particle (calculating and storing
+ * (order) spline values and derivatives). 1: (order) threads do redundant work on this same task,
+ * each one stores only a single theta and single dtheta into global arrays. The only efficiency
+ * difference is less global store operations, countered by more redundant spline computation.
+ *
+ * TODO: estimate if this should be a boolean parameter (and add it to the unit test if so).
+ */
+#define PME_GPU_PARALLEL_SPLINE 0
+
 /*! \brief
  * Charge spreading onto the grid.
  * This corresponds to the CPU function spread_coefficients_bsplines_thread().
  * Optional second stage of the spline_and_spread_kernel.
  *
  * \tparam[in] order                PME interpolation order.
- * \tparam[in] wrapX                A boolean which tells if the grid overlap in dimension X should
- * be wrapped. \tparam[in] wrapY                A boolean which tells if the grid overlap in
- * dimension Y should be wrapped. \tparam[in] useOrderThreads      A boolean which Tells if we
- * should use order threads per atom (order*order used if false) \param[in]  kernelParams Input PME
- * CUDA data in constant memory. \param[in]  atomIndexOffset      Starting atom index for the
- * execution block w.r.t. global memory. \param[in]  atomCharge           Atom charge/coefficient of
- * atom processed by thread. \param[in]  sm_gridlineIndices   Atom gridline indices in the shared
- * memory. \param[in]  sm_theta             Atom spline values in the shared memory.
+ * \tparam[in] wrapX                Whether the grid overlap in dimension X should be wrapped.
+ * \tparam[in] wrapY                Whether the grid overlap in dimension Y should be wrapped.
+ * \tparam[in] gridIndex            The index of the grid to use in the kernel.
+ * \tparam[in] threadsPerAtom       How many threads work on each atom
+ *
+ * \param[in]  kernelParams         Input PME CUDA data in constant memory.
+ * \param[in]  atomCharge           Atom charge/coefficient of atom processed by thread.
+ * \param[in]  sm_gridlineIndices   Atom gridline indices in the shared memory.
+ * \param[in]  sm_theta             Atom spline values in the shared memory.
  */
-template<const int order, const bool wrapX, const bool wrapY, const bool useOrderThreads>
+template<int order, bool wrapX, bool wrapY, int gridIndex, ThreadsPerAtom threadsPerAtom>
 __device__ __forceinline__ void spread_charges(const PmeGpuCudaKernelParams kernelParams,
-                                               int                          atomIndexOffset,
                                                const float*                 atomCharge,
                                                const int* __restrict__ sm_gridlineIndices,
                                                const float* __restrict__ sm_theta)
 {
     /* Global memory pointer to the output grid */
-    float* __restrict__ gm_grid = kernelParams.grid.d_realGrid;
+    float* __restrict__ gm_grid = kernelParams.grid.d_realGrid[gridIndex];
 
-
-    const int atomsPerWarp = useOrderThreads ? c_pmeSpreadGatherAtomsPerWarp4ThPerAtom
-                                             : c_pmeSpreadGatherAtomsPerWarp;
+    // Number of atoms processed by a single warp in spread and gather
+    const int threadsPerAtomValue = (threadsPerAtom == ThreadsPerAtom::Order) ? order : order * order;
+    const int atomsPerWarp        = warp_size / threadsPerAtomValue;
 
     const int nx  = kernelParams.grid.realGridSize[XX];
     const int ny  = kernelParams.grid.realGridSize[YY];
@@ -90,12 +101,10 @@ __device__ __forceinline__ void spread_charges(const PmeGpuCudaKernelParams kern
 
     const int offx = 0, offy = 0, offz = 0; // unused for now
 
-    const int atomIndexLocal  = threadIdx.z;
-    const int atomIndexGlobal = atomIndexOffset + atomIndexLocal;
+    const int atomIndexLocal = threadIdx.z;
 
-    const int globalCheck = pme_gpu_check_atom_data_index(atomIndexGlobal, kernelParams.atoms.nAtoms);
     const int chargeCheck = pme_gpu_check_atom_charge(*atomCharge);
-    if (chargeCheck & globalCheck)
+    if (chargeCheck)
     {
         // Spline Z coordinates
         const int ithz = threadIdx.x;
@@ -117,8 +126,8 @@ __device__ __forceinline__ void spread_charges(const PmeGpuCudaKernelParams kern
         const float thetaZ     = sm_theta[splineIndexZ];
 
         /* loop not used if order*order threads per atom */
-        const int ithyMin = useOrderThreads ? 0 : threadIdx.y;
-        const int ithyMax = useOrderThreads ? order : threadIdx.y + 1;
+        const int ithyMin = (threadsPerAtom == ThreadsPerAtom::Order) ? 0 : threadIdx.y;
+        const int ithyMax = (threadsPerAtom == ThreadsPerAtom::Order) ? order : threadIdx.y + 1;
         for (int ithy = ithyMin; ithy < ithyMax; ithy++)
         {
             int iy = iyBase + ithy;
@@ -166,26 +175,27 @@ __device__ __forceinline__ void spread_charges(const PmeGpuCudaKernelParams kern
  * \tparam[in] spreadCharges        A boolean which tells if the charge spreading should be performed.
  * \tparam[in] wrapX                A boolean which tells if the grid overlap in dimension X should be wrapped.
  * \tparam[in] wrapY                A boolean which tells if the grid overlap in dimension Y should be wrapped.
+ * \tparam[in] numGrids             The number of grids to use in the kernel. Can be 1 or 2.
  * \tparam[in] writeGlobal          A boolean which tells if the theta values and gridlines should be written to global memory.
- * \tparam[in] useOrderThreads         A boolean which tells if we should use order threads per atom (order*order used if false).
+ * \tparam[in] threadsPerAtom       How many threads work on each atom
  * \param[in]  kernelParams         Input PME CUDA data in constant memory.
  */
-template<const int order, const bool computeSplines, const bool spreadCharges, const bool wrapX, const bool wrapY, const bool writeGlobal, const bool useOrderThreads>
+template<int order, bool computeSplines, bool spreadCharges, bool wrapX, bool wrapY, int numGrids, bool writeGlobal, ThreadsPerAtom threadsPerAtom>
 __launch_bounds__(c_spreadMaxThreadsPerBlock) CLANG_DISABLE_OPTIMIZATION_ATTRIBUTE __global__
         void pme_spline_and_spread_kernel(const PmeGpuCudaKernelParams kernelParams)
 {
-    const int atomsPerBlock =
-            useOrderThreads ? c_spreadMaxThreadsPerBlock / c_pmeSpreadGatherThreadsPerAtom4ThPerAtom
-                            : c_spreadMaxThreadsPerBlock / c_pmeSpreadGatherThreadsPerAtom;
+    const int threadsPerAtomValue = (threadsPerAtom == ThreadsPerAtom::Order) ? order : order * order;
+    const int atomsPerBlock       = c_spreadMaxThreadsPerBlock / threadsPerAtomValue;
+    // Number of atoms processed by a single warp in spread and gather
+    const int atomsPerWarp = warp_size / threadsPerAtomValue;
     // Gridline indices, ivec
     __shared__ int sm_gridlineIndices[atomsPerBlock * DIM];
+    // Charges
+    __shared__ float sm_coefficients[atomsPerBlock];
     // Spline values
     __shared__ float sm_theta[atomsPerBlock * DIM * order];
     float            dtheta;
 
-    const int atomsPerWarp = useOrderThreads ? c_pmeSpreadGatherAtomsPerWarp4ThPerAtom
-                                             : c_pmeSpreadGatherAtomsPerWarp;
-
     float3 atomX;
     float  atomCharge;
 
@@ -215,37 +225,32 @@ __launch_bounds__(c_spreadMaxThreadsPerBlock) CLANG_DISABLE_OPTIMIZATION_ATTRIBU
     /* Charges, required for both spline and spread */
     if (c_useAtomDataPrefetch)
     {
-        __shared__ float sm_coefficients[atomsPerBlock];
-        pme_gpu_stage_atom_data<float, atomsPerBlock, 1>(kernelParams, sm_coefficients,
-                                                         kernelParams.atoms.d_coefficients);
+        pme_gpu_stage_atom_data<float, atomsPerBlock, 1>(sm_coefficients,
+                                                         kernelParams.atoms.d_coefficients[0]);
         __syncthreads();
         atomCharge = sm_coefficients[atomIndexLocal];
     }
     else
     {
-        atomCharge = kernelParams.atoms.d_coefficients[atomIndexGlobal];
+        atomCharge = kernelParams.atoms.d_coefficients[0][atomIndexGlobal];
     }
 
     if (computeSplines)
     {
+        const float3* __restrict__ gm_coordinates = asFloat3(kernelParams.atoms.d_coordinates);
         if (c_useAtomDataPrefetch)
         {
             // Coordinates
-            __shared__ float sm_coordinates[DIM * atomsPerBlock];
+            __shared__ float3 sm_coordinates[atomsPerBlock];
 
             /* Staging coordinates */
-            pme_gpu_stage_atom_data<float, atomsPerBlock, DIM>(kernelParams, sm_coordinates,
-                                                               kernelParams.atoms.d_coordinates);
+            pme_gpu_stage_atom_data<float3, atomsPerBlock, 1>(sm_coordinates, gm_coordinates);
             __syncthreads();
-            atomX.x = sm_coordinates[atomIndexLocal * DIM + XX];
-            atomX.y = sm_coordinates[atomIndexLocal * DIM + YY];
-            atomX.z = sm_coordinates[atomIndexLocal * DIM + ZZ];
+            atomX = sm_coordinates[atomIndexLocal];
         }
         else
         {
-            atomX.x = kernelParams.atoms.d_coordinates[atomIndexGlobal * DIM + XX];
-            atomX.y = kernelParams.atoms.d_coordinates[atomIndexGlobal * DIM + YY];
-            atomX.z = kernelParams.atoms.d_coordinates[atomIndexGlobal * DIM + ZZ];
+            atomX = gm_coordinates[atomIndexGlobal];
         }
         calculate_splines<order, atomsPerBlock, atomsPerWarp, false, writeGlobal>(
                 kernelParams, atomIndexOffset, atomX, atomCharge, sm_theta, &dtheta, sm_gridlineIndices);
@@ -258,10 +263,9 @@ __launch_bounds__(c_spreadMaxThreadsPerBlock) CLANG_DISABLE_OPTIMIZATION_ATTRIBU
          * as in after running the spline kernel)
          */
         /* Spline data - only thetas (dthetas will only be needed in gather) */
-        pme_gpu_stage_atom_data<float, atomsPerBlock, DIM * order>(kernelParams, sm_theta,
-                                                                   kernelParams.atoms.d_theta);
+        pme_gpu_stage_atom_data<float, atomsPerBlock, DIM * order>(sm_theta, kernelParams.atoms.d_theta);
         /* Gridline indices */
-        pme_gpu_stage_atom_data<int, atomsPerBlock, DIM>(kernelParams, sm_gridlineIndices,
+        pme_gpu_stage_atom_data<int, atomsPerBlock, DIM>(sm_gridlineIndices,
                                                          kernelParams.atoms.d_gridlineIndices);
 
         __syncthreads();
@@ -270,27 +274,47 @@ __launch_bounds__(c_spreadMaxThreadsPerBlock) CLANG_DISABLE_OPTIMIZATION_ATTRIBU
     /* Spreading */
     if (spreadCharges)
     {
-        spread_charges<order, wrapX, wrapY, useOrderThreads>(
-                kernelParams, atomIndexOffset, &atomCharge, sm_gridlineIndices, sm_theta);
+        spread_charges<order, wrapX, wrapY, 0, threadsPerAtom>(kernelParams, &atomCharge,
+                                                               sm_gridlineIndices, sm_theta);
+    }
+    if (numGrids == 2)
+    {
+        __syncthreads();
+        if (c_useAtomDataPrefetch)
+        {
+            pme_gpu_stage_atom_data<float, atomsPerBlock, 1>(sm_coefficients,
+                                                             kernelParams.atoms.d_coefficients[1]);
+            __syncthreads();
+            atomCharge = sm_coefficients[atomIndexLocal];
+        }
+        else
+        {
+            atomCharge = kernelParams.atoms.d_coefficients[1][atomIndexGlobal];
+        }
+        if (spreadCharges)
+        {
+            spread_charges<order, wrapX, wrapY, 1, threadsPerAtom>(kernelParams, &atomCharge,
+                                                                   sm_gridlineIndices, sm_theta);
+        }
     }
 }
 
 //! Kernel instantiations
-template __global__ void pme_spline_and_spread_kernel<4, true, true, true, true, true, true>(const PmeGpuCudaKernelParams);
-template __global__ void
-pme_spline_and_spread_kernel<4, true, false, true, true, true, true>(const PmeGpuCudaKernelParams);
-template __global__ void
-pme_spline_and_spread_kernel<4, false, true, true, true, true, true>(const PmeGpuCudaKernelParams);
-
-template __global__ void
-pme_spline_and_spread_kernel<4, true, true, true, true, false, true>(const PmeGpuCudaKernelParams);
-
-template __global__ void
-pme_spline_and_spread_kernel<4, true, true, true, true, true, false>(const PmeGpuCudaKernelParams);
-template __global__ void
-pme_spline_and_spread_kernel<4, true, false, true, true, true, false>(const PmeGpuCudaKernelParams);
-template __global__ void
-pme_spline_and_spread_kernel<4, false, true, true, true, true, false>(const PmeGpuCudaKernelParams);
-
-template __global__ void
-pme_spline_and_spread_kernel<4, true, true, true, true, false, false>(const PmeGpuCudaKernelParams);
+// clang-format off
+template __global__ void pme_spline_and_spread_kernel<4, true, true, true, true, 1, true, ThreadsPerAtom::Order>        (const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, false, true, true, 1, true, ThreadsPerAtom::Order>       (const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, false, true, true, true, 1, true, ThreadsPerAtom::Order>       (const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, true, true, true, 1, false, ThreadsPerAtom::Order>       (const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, true, true, true, 1, true, ThreadsPerAtom::OrderSquared> (const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, false, true, true, 1, true, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, false, true, true, true, 1, true, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, true, true, true, 1, false, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, true, true, true, 2, true, ThreadsPerAtom::Order>        (const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, false, true, true, 2, true, ThreadsPerAtom::Order>       (const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, false, true, true, true, 2, true, ThreadsPerAtom::Order>       (const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, true, true, true, 2, false, ThreadsPerAtom::Order>       (const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, true, true, true, 2, true, ThreadsPerAtom::OrderSquared> (const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, false, true, true, 2, true, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, false, true, true, true, 2, true, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+template __global__ void pme_spline_and_spread_kernel<4, true, true, true, true, 2, false, ThreadsPerAtom::OrderSquared>(const PmeGpuCudaKernelParams);
+// clang-format on
diff --git a/src/gromacs/ewald/spline_vectors.h b/src/gromacs/ewald/spline_vectors.h
new file mode 100644 (file)
index 0000000..24b0b55
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file declares a type useful for spline vectors
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_SPLINE_VECTORS_H
+#define GMX_EWALD_SPLINE_VECTORS_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+/*! \brief Helper typedef for spline vectors */
+typedef real* splinevec[DIM];
+
+#endif
index 26756429a237008975a770481a48047cf47bde0c..61da015d2dae8308ef455d75098398bcb863373e 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2016,2017, by the GROMACS development team, led by
+# Copyright (c) 2016,2017,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-file(GLOB EWALD_TEST_SOURCES *.cpp)
-if (GMX_USE_CUDA)
-    file(GLOB EWALD_CUDA_SOURCES ../*.cu)
-endif()
-
 gmx_add_unit_test(EwaldUnitTests ewald-test HARDWARE_DETECTION
-                  ${EWALD_TEST_SOURCES} ${EWALD_CUDA_SOURCES})
+    CPP_SOURCE_FILES
+        pmebsplinetest.cpp
+        pmegathertest.cpp
+        pmesolvetest.cpp
+        pmesplinespreadtest.cpp
+        pmetestcommon.cpp
+)
index 19157f758ebffb956034b74359a464a55e6c6933..b6a4e9e35f81b69e24f16180b26b36bfcabcd898 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -124,13 +124,13 @@ public:
 using PmeBSplineModuliFailureTest = PmeBSplineModuliTest;
 TEST_P(PmeBSplineModuliFailureTest, Throws)
 {
-    EXPECT_THROW(runTest(), InconsistentInputError);
+    EXPECT_THROW_GMX(runTest(), InconsistentInputError);
 }
 //! Correctness test alias
 using PmeBSplineModuliCorrectnessTest = PmeBSplineModuliTest;
 TEST_P(PmeBSplineModuliCorrectnessTest, ReproducesValues)
 {
-    EXPECT_NO_THROW(runTest());
+    EXPECT_NO_THROW_GMX(runTest());
 }
 
 /* Invalid input instances */
index 325a0cdc69ef155da3fc55f2f6a10b0b5a36cebc..1190caea1de56244be0bf6bb1da966608fb86ce4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,6 +50,7 @@
 #include "gromacs/utility/stringutil.h"
 
 #include "testutils/refdata.h"
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
 #include "pmetestcommon.h"
@@ -214,7 +215,7 @@ typedef std::map<size_t, AtomSizedData> InputDataByAtomCount;
  * The rest of the atom-related input data - gridline indices, spline theta values, spline dtheta
  * values, atom charges - is looked up in the inputAtomDataSets_ test fixture variable.
  */
-typedef std::tuple<Matrix3x3, int, IVec, SparseRealGridValuesInput, PmeForceOutputHandling, size_t> GatherInputParameters;
+typedef std::tuple<Matrix3x3, int, IVec, SparseRealGridValuesInput, size_t> GatherInputParameters;
 
 //! Test fixture
 class PmeGatherTest : public ::testing::TestWithParam<GatherInputParameters>
@@ -225,7 +226,7 @@ private:
 
 public:
     PmeGatherTest() = default;
-    //! Sets the input atom data references once
+    //! Sets the input atom data references and programs once
     static void SetUpTestCase()
     {
         size_t start = 0;
@@ -253,7 +254,9 @@ public:
             }
             s_inputAtomDataSets_[atomCount] = atomData;
         }
+        s_pmeTestHardwareContexts = createPmeTestHardwareContextList();
     }
+
     //! The test
     void runTest()
     {
@@ -263,8 +266,7 @@ public:
         IVec                      gridSize;
         size_t                    atomCount;
         SparseRealGridValuesInput nonZeroGridValues;
-        PmeForceOutputHandling    inputForceTreatment;
-        std::tie(box, pmeOrder, gridSize, nonZeroGridValues, inputForceTreatment, atomCount) = GetParam();
+        std::tie(box, pmeOrder, gridSize, nonZeroGridValues, atomCount) = GetParam();
         auto inputAtomData       = s_inputAtomDataSets_[atomCount];
         auto inputAtomSplineData = inputAtomData.splineDataByPmeOrder[pmeOrder];
 
@@ -278,32 +280,37 @@ public:
         inputRec.epsilon_r   = 1.0;
 
         TestReferenceData refData;
-        for (const auto& context : getPmeTestEnv()->getHardwareContexts())
+        for (const auto& pmeTestHardwareContext : s_pmeTestHardwareContexts)
         {
-            CodePath   codePath = context->getCodePath();
-            const bool supportedInput =
-                    pmeSupportsInputForMode(*getPmeTestEnv()->hwinfo(), &inputRec, codePath);
+            pmeTestHardwareContext->activate();
+            CodePath   codePath       = pmeTestHardwareContext->codePath();
+            const bool supportedInput = pmeSupportsInputForMode(
+                    *getTestHardwareEnvironment()->hwinfo(), &inputRec, codePath);
             if (!supportedInput)
             {
                 /* Testing the failure for the unsupported input */
-                EXPECT_THROW(pmeInitWrapper(&inputRec, codePath, nullptr, nullptr, box), NotImplementedError);
+                EXPECT_THROW_GMX(pmeInitWrapper(&inputRec, codePath, nullptr, nullptr, nullptr, box),
+                                 NotImplementedError);
                 continue;
             }
 
             /* Describing the test uniquely */
             SCOPED_TRACE(
-                    formatString("Testing force gathering with %s %sfor PME grid size %d %d %d"
-                                 ", order %d, %zu atoms, %s",
-                                 codePathToString(codePath), context->getDescription().c_str(),
-                                 gridSize[XX], gridSize[YY], gridSize[ZZ], pmeOrder, atomCount,
-                                 (inputForceTreatment == PmeForceOutputHandling::ReduceWithInput)
-                                         ? "with reduction"
-                                         : "without reduction"));
-
-            PmeSafePointer pmeSafe = pmeInitWrapper(&inputRec, codePath, context->getDeviceInfo(),
-                                                    context->getPmeGpuProgram(), box);
+                    formatString("Testing force gathering on %s for PME grid size %d %d %d"
+                                 ", order %d, %zu atoms",
+                                 pmeTestHardwareContext->description().c_str(), gridSize[XX],
+                                 gridSize[YY], gridSize[ZZ], pmeOrder, atomCount));
+
+            PmeSafePointer pmeSafe =
+                    pmeInitWrapper(&inputRec, codePath, pmeTestHardwareContext->deviceContext(),
+                                   pmeTestHardwareContext->deviceStream(),
+                                   pmeTestHardwareContext->pmeGpuProgram(), box);
             std::unique_ptr<StatePropagatorDataGpu> stateGpu =
-                    (codePath == CodePath::GPU) ? makeStatePropagatorDataGpu(*pmeSafe.get()) : nullptr;
+                    (codePath == CodePath::GPU)
+                            ? makeStatePropagatorDataGpu(*pmeSafe.get(),
+                                                         pmeTestHardwareContext->deviceContext(),
+                                                         pmeTestHardwareContext->deviceStream())
+                            : nullptr;
 
             pmeInitAtoms(pmeSafe.get(), stateGpu.get(), codePath, inputAtomData.coordinates,
                          inputAtomData.charges);
@@ -325,7 +332,7 @@ public:
             auto forces = ForcesVector(inputForcesFull).subArray(0, atomCount);
 
             /* Running the force gathering itself */
-            pmePerformGather(pmeSafe.get(), codePath, inputForceTreatment, forces);
+            pmePerformGather(pmeSafe.get(), codePath, forces);
             pmeFinalizeTest(pmeSafe.get(), codePath);
 
             /* Check the output forces correctness */
@@ -335,15 +342,19 @@ public:
             forceChecker.checkSequence(forces.begin(), forces.end(), "Forces");
         }
     }
+
+    static std::vector<std::unique_ptr<PmeTestHardwareContext>> s_pmeTestHardwareContexts;
 };
 
+std::vector<std::unique_ptr<PmeTestHardwareContext>> PmeGatherTest::s_pmeTestHardwareContexts;
+
 // An instance of static atom data
 InputDataByAtomCount PmeGatherTest::s_inputAtomDataSets_;
 
 //! Test for PME force gathering
 TEST_P(PmeGatherTest, ReproducesOutputs)
 {
-    EXPECT_NO_THROW(runTest());
+    EXPECT_NO_THROW_GMX(runTest());
 }
 
 //! Instantiation of the PME gathering test
@@ -353,8 +364,6 @@ INSTANTIATE_TEST_CASE_P(SaneInput,
                                            ::testing::ValuesIn(pmeOrders),
                                            ::testing::ValuesIn(c_sampleGridSizes),
                                            ::testing::ValuesIn(c_sampleGrids),
-                                           ::testing::Values(PmeForceOutputHandling::Set,
-                                                             PmeForceOutputHandling::ReduceWithInput),
                                            ::testing::ValuesIn(atomCounts)));
 
 } // namespace
index 99af453394a7d1c95c2786d2e2e38ce1f85ffac6..578fa8caa748b2925eb1977902f6426c9b18d491 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,6 +50,7 @@
 #include "gromacs/utility/stringutil.h"
 
 #include "testutils/refdata.h"
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
 #include "pmetestcommon.h"
@@ -74,6 +75,9 @@ class PmeSolveTest : public ::testing::TestWithParam<SolveInputParameters>
 public:
     PmeSolveTest() = default;
 
+    //! Sets the programs once
+    static void SetUpTestCase() { s_pmeTestHardwareContexts = createPmeTestHardwareContextList(); }
+
     //! The test
     void runTest()
     {
@@ -106,17 +110,18 @@ public:
         }
 
         TestReferenceData refData;
-        for (const auto& context : getPmeTestEnv()->getHardwareContexts())
+        for (const auto& pmeTestHardwareContext : s_pmeTestHardwareContexts)
         {
-            CodePath   codePath = context->getCodePath();
-            const bool supportedInput =
-                    pmeSupportsInputForMode(*getPmeTestEnv()->hwinfo(), &inputRec, codePath);
+            pmeTestHardwareContext->activate();
+            CodePath   codePath       = pmeTestHardwareContext->codePath();
+            const bool supportedInput = pmeSupportsInputForMode(
+                    *getTestHardwareEnvironment()->hwinfo(), &inputRec, codePath);
             if (!supportedInput)
             {
                 /* Testing the failure for the unsupported input */
-                EXPECT_THROW(pmeInitEmpty(&inputRec, codePath, nullptr, nullptr, box, ewaldCoeff_q,
-                                          ewaldCoeff_lj),
-                             NotImplementedError);
+                EXPECT_THROW_GMX(pmeInitWrapper(&inputRec, codePath, nullptr, nullptr, nullptr, box,
+                                                ewaldCoeff_q, ewaldCoeff_lj),
+                                 NotImplementedError);
                 continue;
             }
 
@@ -132,17 +137,18 @@ public:
                 {
                     /* Describing the test*/
                     SCOPED_TRACE(formatString(
-                            "Testing solving (%s, %s, %s energy/virial) with %s %sfor PME grid "
+                            "Testing solving (%s, %s, %s energy/virial) on %s for PME grid "
                             "size %d %d %d, Ewald coefficients %g %g",
                             (method == PmeSolveAlgorithm::LennardJones) ? "Lennard-Jones" : "Coulomb",
                             gridOrdering.second.c_str(), computeEnergyAndVirial ? "with" : "without",
-                            codePathToString(codePath), context->getDescription().c_str(),
-                            gridSize[XX], gridSize[YY], gridSize[ZZ], ewaldCoeff_q, ewaldCoeff_lj));
+                            pmeTestHardwareContext->description().c_str(), gridSize[XX],
+                            gridSize[YY], gridSize[ZZ], ewaldCoeff_q, ewaldCoeff_lj));
 
                     /* Running the test */
-                    PmeSafePointer pmeSafe =
-                            pmeInitEmpty(&inputRec, codePath, context->getDeviceInfo(),
-                                         context->getPmeGpuProgram(), box, ewaldCoeff_q, ewaldCoeff_lj);
+                    PmeSafePointer pmeSafe = pmeInitWrapper(
+                            &inputRec, codePath, pmeTestHardwareContext->deviceContext(),
+                            pmeTestHardwareContext->deviceStream(),
+                            pmeTestHardwareContext->pmeGpuProgram(), box, ewaldCoeff_q, ewaldCoeff_lj);
                     pmeSetComplexGrid(pmeSafe.get(), codePath, gridOrdering.first, nonZeroGridValues);
                     const real cellVolume = box[0] * box[4] * box[8];
                     // FIXME - this is box[XX][XX] * box[YY][YY] * box[ZZ][ZZ], should be stored in the PME structure
@@ -243,12 +249,16 @@ public:
             }
         }
     }
+
+    static std::vector<std::unique_ptr<PmeTestHardwareContext>> s_pmeTestHardwareContexts;
 };
 
+std::vector<std::unique_ptr<PmeTestHardwareContext>> PmeSolveTest::s_pmeTestHardwareContexts;
+
 /*! \brief Test for PME solving */
 TEST_P(PmeSolveTest, ReproducesOutputs)
 {
-    EXPECT_NO_THROW(runTest());
+    EXPECT_NO_THROW_GMX(runTest());
 }
 
 /* Valid input instances */
index 43a459420e10a9666323e5a7e4e26b5d8ff30cb5..4765acbddbd5319308e5d67be406273c26da3313 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,6 +50,7 @@
 #include "gromacs/utility/stringutil.h"
 
 #include "testutils/refdata.h"
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
 #include "pmetestcommon.h"
@@ -82,6 +83,10 @@ class PmeSplineAndSpreadTest : public ::testing::TestWithParam<SplineAndSpreadIn
 {
 public:
     PmeSplineAndSpreadTest() = default;
+
+    //! Sets the programs once
+    static void SetUpTestCase() { s_pmeTestHardwareContexts = createPmeTestHardwareContextList(); }
+
     //! The test
     void runTest()
     {
@@ -104,8 +109,6 @@ public:
         inputRec.coulombtype = eelPME;
         inputRec.epsilon_r   = 1.0;
 
-        TestReferenceData refData;
-
         const std::map<PmeSplineAndSpreadOptions, std::string> optionsToTest = {
             { PmeSplineAndSpreadOptions::SplineAndSpreadUnified,
               "spline computation and charge spreading (fused)" },
@@ -123,15 +126,18 @@ public:
         bool   gridValuesSizeAssigned = false;
         size_t previousGridValuesSize;
 
-        for (const auto& context : getPmeTestEnv()->getHardwareContexts())
+        TestReferenceData refData;
+        for (const auto& pmeTestHardwareContext : s_pmeTestHardwareContexts)
         {
-            CodePath   codePath = context->getCodePath();
-            const bool supportedInput =
-                    pmeSupportsInputForMode(*getPmeTestEnv()->hwinfo(), &inputRec, codePath);
+            pmeTestHardwareContext->activate();
+            CodePath   codePath       = pmeTestHardwareContext->codePath();
+            const bool supportedInput = pmeSupportsInputForMode(
+                    *getTestHardwareEnvironment()->hwinfo(), &inputRec, codePath);
             if (!supportedInput)
             {
                 /* Testing the failure for the unsupported input */
-                EXPECT_THROW(pmeInitWrapper(&inputRec, codePath, nullptr, nullptr, box), NotImplementedError);
+                EXPECT_THROW_GMX(pmeInitWrapper(&inputRec, codePath, nullptr, nullptr, nullptr, box),
+                                 NotImplementedError);
                 continue;
             }
 
@@ -139,19 +145,24 @@ public:
             {
                 /* Describing the test uniquely in case it fails */
 
-                SCOPED_TRACE(
-                        formatString("Testing %s with %s %sfor PME grid size %d %d %d"
-                                     ", order %d, %zu atoms",
-                                     option.second.c_str(), codePathToString(codePath),
-                                     context->getDescription().c_str(), gridSize[XX], gridSize[YY],
-                                     gridSize[ZZ], pmeOrder, atomCount));
+                SCOPED_TRACE(formatString(
+                        "Testing %s on %s for PME grid size %d %d %d"
+                        ", order %d, %zu atoms",
+                        option.second.c_str(), pmeTestHardwareContext->description().c_str(),
+                        gridSize[XX], gridSize[YY], gridSize[ZZ], pmeOrder, atomCount));
 
                 /* Running the test */
 
-                PmeSafePointer pmeSafe = pmeInitWrapper(&inputRec, codePath, context->getDeviceInfo(),
-                                                        context->getPmeGpuProgram(), box);
+                PmeSafePointer pmeSafe =
+                        pmeInitWrapper(&inputRec, codePath, pmeTestHardwareContext->deviceContext(),
+                                       pmeTestHardwareContext->deviceStream(),
+                                       pmeTestHardwareContext->pmeGpuProgram(), box);
                 std::unique_ptr<StatePropagatorDataGpu> stateGpu =
-                        (codePath == CodePath::GPU) ? makeStatePropagatorDataGpu(*pmeSafe.get()) : nullptr;
+                        (codePath == CodePath::GPU)
+                                ? makeStatePropagatorDataGpu(*pmeSafe.get(),
+                                                             pmeTestHardwareContext->deviceContext(),
+                                                             pmeTestHardwareContext->deviceStream())
+                                : nullptr;
 
                 pmeInitAtoms(pmeSafe.get(), stateGpu.get(), codePath, coordinates, charges);
 
@@ -257,13 +268,17 @@ public:
             }
         }
     }
+
+    static std::vector<std::unique_ptr<PmeTestHardwareContext>> s_pmeTestHardwareContexts;
 };
 
+std::vector<std::unique_ptr<PmeTestHardwareContext>> PmeSplineAndSpreadTest::s_pmeTestHardwareContexts;
+
 
 /*! \brief Test for spline parameter computation and charge spreading. */
 TEST_P(PmeSplineAndSpreadTest, ReproducesOutputs)
 {
-    EXPECT_NO_THROW(runTest());
+    EXPECT_NO_THROW_GMX(runTest());
 }
 
 /* Valid input instances */
index fc68e96f4131af69cbc38c607be52f92230f1946..eb1e170411bbf090582852ed283fe65824919143 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/domdec/domdec.h"
 #include "gromacs/ewald/pme_gather.h"
+#include "gromacs/ewald/pme_gpu_calculate_splines.h"
+#include "gromacs/ewald/pme_gpu_constants.h"
 #include "gromacs/ewald/pme_gpu_internal.h"
+#include "gromacs/ewald/pme_gpu_staging.h"
 #include "gromacs/ewald/pme_grid.h"
 #include "gromacs/ewald/pme_internal.h"
 #include "gromacs/ewald/pme_redistribute.h"
@@ -57,6 +60,7 @@
 #include "gromacs/ewald/pme_spread.h"
 #include "gromacs/fft/parallel_3dfft.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/math/invertmatrix.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/utility/logger.h"
 #include "gromacs/utility/stringutil.h"
 
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
+class DeviceContext;
+
 namespace gmx
 {
 namespace test
@@ -75,15 +82,14 @@ namespace test
 
 bool pmeSupportsInputForMode(const gmx_hw_info_t& hwinfo, const t_inputrec* inputRec, CodePath mode)
 {
-    bool       implemented;
-    gmx_mtop_t mtop;
+    bool implemented;
     switch (mode)
     {
         case CodePath::CPU: implemented = true; break;
 
         case CodePath::GPU:
             implemented = (pme_gpu_supports_build(nullptr) && pme_gpu_supports_hardware(hwinfo, nullptr)
-                           && pme_gpu_supports_input(*inputRec, mtop, nullptr));
+                           && pme_gpu_supports_input(*inputRec, nullptr));
             break;
 
         default: GMX_THROW(InternalError("Test not implemented for this mode"));
@@ -101,21 +107,22 @@ uint64_t getSplineModuliDoublePrecisionUlps(int splineOrder)
 }
 
 //! PME initialization
-PmeSafePointer pmeInitWrapper(const t_inputrec*        inputRec,
-                              const CodePath           mode,
-                              const gmx_device_info_t* gpuInfo,
-                              PmeGpuProgramHandle      pmeGpuProgram,
-                              const Matrix3x3&         box,
-                              const real               ewaldCoeff_q,
-                              const real               ewaldCoeff_lj)
+PmeSafePointer pmeInitWrapper(const t_inputrec*    inputRec,
+                              const CodePath       mode,
+                              const DeviceContext* deviceContext,
+                              const DeviceStream*  deviceStream,
+                              const PmeGpuProgram* pmeGpuProgram,
+                              const Matrix3x3&     box,
+                              const real           ewaldCoeff_q,
+                              const real           ewaldCoeff_lj)
 {
     const MDLogger dummyLogger;
     const auto     runMode       = (mode == CodePath::CPU) ? PmeRunMode::CPU : PmeRunMode::Mixed;
     t_commrec      dummyCommrec  = { 0 };
     NumPmeDomains  numPmeDomains = { 1, 1 };
-    gmx_pme_t*     pmeDataRaw =
-            gmx_pme_init(&dummyCommrec, numPmeDomains, inputRec, false, false, true, ewaldCoeff_q,
-                         ewaldCoeff_lj, 1, runMode, nullptr, gpuInfo, pmeGpuProgram, dummyLogger);
+    gmx_pme_t* pmeDataRaw = gmx_pme_init(&dummyCommrec, numPmeDomains, inputRec, false, false, true,
+                                         ewaldCoeff_q, ewaldCoeff_lj, 1, runMode, nullptr,
+                                         deviceContext, deviceStream, pmeGpuProgram, dummyLogger);
     PmeSafePointer pme(pmeDataRaw); // taking ownership
 
     // TODO get rid of this with proper matrix type
@@ -127,7 +134,7 @@ PmeSafePointer pmeInitWrapper(const t_inputrec*        inputRec,
             boxTemp[i][j] = box[i * DIM + j];
         }
     }
-    const char* boxError = check_box(-1, boxTemp);
+    const char* boxError = check_box(PbcType::Unset, boxTemp);
     GMX_RELEASE_ASSERT(boxError == nullptr, boxError);
 
     switch (mode)
@@ -145,28 +152,22 @@ PmeSafePointer pmeInitWrapper(const t_inputrec*        inputRec,
     return pme;
 }
 
-//! Simple PME initialization based on input, no atom data
-PmeSafePointer pmeInitEmpty(const t_inputrec*        inputRec,
-                            const CodePath           mode,
-                            const gmx_device_info_t* gpuInfo,
-                            PmeGpuProgramHandle      pmeGpuProgram,
-                            const Matrix3x3&         box,
-                            real                     ewaldCoeff_q,
-                            real                     ewaldCoeff_lj)
+PmeSafePointer pmeInitEmpty(const t_inputrec* inputRec)
 {
-    return pmeInitWrapper(inputRec, mode, gpuInfo, pmeGpuProgram, box, ewaldCoeff_q, ewaldCoeff_lj);
-    // hiding the fact that PME actually needs to know the number of atoms in advance
+    const Matrix3x3 defaultBox = { { 1.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 1.0F } };
+    return pmeInitWrapper(inputRec, CodePath::CPU, nullptr, nullptr, nullptr, defaultBox, 0.0F, 0.0F);
 }
 
 //! Make a GPU state-propagator manager
-std::unique_ptr<StatePropagatorDataGpu> makeStatePropagatorDataGpu(const gmx_pme_t& pme)
+std::unique_ptr<StatePropagatorDataGpu> makeStatePropagatorDataGpu(const gmx_pme_t&     pme,
+                                                                   const DeviceContext* deviceContext,
+                                                                   const DeviceStream* deviceStream)
 {
     // TODO: Pin the host buffer and use async memory copies
     // TODO: Special constructor for PME-only rank / PME-tests is used here. There should be a mechanism to
     //       restrict one from using other constructor here.
-    return std::make_unique<StatePropagatorDataGpu>(
-            pme_gpu_get_device_stream(&pme), pme_gpu_get_device_context(&pme),
-            GpuApiCallBehavior::Sync, pme_gpu_get_padding_size(&pme), nullptr);
+    return std::make_unique<StatePropagatorDataGpu>(deviceStream, *deviceContext, GpuApiCallBehavior::Sync,
+                                                    pme_gpu_get_block_size(&pme), nullptr);
 }
 
 //! PME initialization with atom data
@@ -186,7 +187,7 @@ void pmeInitAtoms(gmx_pme_t*               pme,
             atc              = &(pme->atc[0]);
             atc->x           = coordinates;
             atc->coefficient = charges;
-            gmx_pme_reinit_atoms(pme, atomCount, charges.data());
+            gmx_pme_reinit_atoms(pme, atomCount, charges.data(), nullptr);
             /* With decomposition there would be more boilerplate atc code here, e.g. do_redist_pos_coeffs */
             break;
 
@@ -195,7 +196,7 @@ void pmeInitAtoms(gmx_pme_t*               pme,
             atc = &(pme->atc[0]);
             // We need to set atc->n for passing the size in the tests
             atc->setNumAtoms(atomCount);
-            gmx_pme_reinit_atoms(pme, atomCount, charges.data());
+            gmx_pme_reinit_atoms(pme, atomCount, charges.data(), nullptr);
 
             stateGpu->reinit(atomCount, atomCount);
             stateGpu->copyCoordinatesToGpu(arrayRefFromArray(coordinates.data(), coordinates.size()),
@@ -298,28 +299,35 @@ void pmePerformSplineAndSpread(gmx_pme_t* pme,
     PmeAtomComm* atc                          = &(pme->atc[0]);
     const size_t gridIndex                    = 0;
     const bool   computeSplinesForZeroCharges = true;
-    real*        fftgrid                      = spreadCharges ? pme->fftgrid[gridIndex] : nullptr;
+    real**       fftgrid                      = spreadCharges ? pme->fftgrid : nullptr;
     real*        pmegrid                      = pme->pmegrid[gridIndex].grid.grid;
 
     switch (mode)
     {
         case CodePath::CPU:
             spread_on_grid(pme, atc, &pme->pmegrid[gridIndex], computeSplines, spreadCharges,
-                           fftgrid, computeSplinesForZeroCharges, gridIndex);
+                           fftgrid != nullptr ? fftgrid[gridIndex] : nullptr,
+                           computeSplinesForZeroCharges, gridIndex);
             if (spreadCharges && !pme->bUseThreads)
             {
                 wrap_periodic_pmegrid(pme, pmegrid);
-                copy_pmegrid_to_fftgrid(pme, pmegrid, fftgrid, gridIndex);
+                copy_pmegrid_to_fftgrid(
+                        pme, pmegrid, fftgrid != nullptr ? fftgrid[gridIndex] : nullptr, gridIndex);
             }
             break;
 
+/* The compiler will complain about passing fftgrid (converting double ** to float **) if using
+ * double precision. GPUs are not used with double precision anyhow. */
+#if !GMX_DOUBLE
         case CodePath::GPU:
         {
+            const real lambdaQ = 1.0;
             // no synchronization needed as x is transferred in the PME stream
             GpuEventSynchronizer* xReadyOnDevice = nullptr;
-            pme_gpu_spread(pme->gpu, xReadyOnDevice, gridIndex, fftgrid, computeSplines, spreadCharges);
+            pme_gpu_spread(pme->gpu, xReadyOnDevice, fftgrid, computeSplines, spreadCharges, lambdaQ);
         }
         break;
+#endif
 
         default: GMX_THROW(InternalError("Test not implemented for this mode"));
     }
@@ -358,6 +366,7 @@ void pmePerformSolve(const gmx_pme_t*  pme,
     t_complex*   h_grid              = pmeGetComplexGridInternal(pme);
     const bool   useLorentzBerthelot = false;
     const size_t threadIndex         = 0;
+    const size_t gridIndex           = 0;
     switch (mode)
     {
         case CodePath::CPU:
@@ -384,7 +393,7 @@ void pmePerformSolve(const gmx_pme_t*  pme,
             switch (method)
             {
                 case PmeSolveAlgorithm::Coulomb:
-                    pme_gpu_solve(pme->gpu, h_grid, gridOrdering, computeEnergyAndVirial);
+                    pme_gpu_solve(pme->gpu, gridIndex, h_grid, gridOrdering, computeEnergyAndVirial);
                     break;
 
                 default: GMX_THROW(InternalError("Test not implemented for this mode"));
@@ -396,17 +405,16 @@ void pmePerformSolve(const gmx_pme_t*  pme,
 }
 
 //! PME force gathering
-void pmePerformGather(gmx_pme_t* pme, CodePath mode, PmeForceOutputHandling inputTreatment, ForcesVector& forces)
+void pmePerformGather(gmx_pme_t* pme, CodePath mode, ForcesVector& forces)
 {
     PmeAtomComm* atc       = &(pme->atc[0]);
     const index  atomCount = atc->numAtoms();
     GMX_RELEASE_ASSERT(forces.ssize() == atomCount, "Invalid force buffer size");
-    const bool forceReductionWithInput = (inputTreatment == PmeForceOutputHandling::ReduceWithInput);
-    const real   scale                 = 1.0;
-    const size_t threadIndex           = 0;
-    const size_t gridIndex             = 0;
-    real*        pmegrid               = pme->pmegrid[gridIndex].grid.grid;
-    real*        fftgrid               = pme->fftgrid[gridIndex];
+    const real   scale       = 1.0;
+    const size_t threadIndex = 0;
+    const size_t gridIndex   = 0;
+    real*        pmegrid     = pme->pmegrid[gridIndex].grid.grid;
+    real**       fftgrid     = pme->fftgrid;
 
     switch (mode)
     {
@@ -417,25 +425,27 @@ void pmePerformGather(gmx_pme_t* pme, CodePath mode, PmeForceOutputHandling inpu
                 // something which is normally done in serial spline computation (make_thread_local_ind())
                 atc->spline[threadIndex].n = atomCount;
             }
-            copy_fftgrid_to_pmegrid(pme, fftgrid, pmegrid, gridIndex, pme->nthread, threadIndex);
+            copy_fftgrid_to_pmegrid(pme, fftgrid[gridIndex], pmegrid, gridIndex, pme->nthread, threadIndex);
             unwrap_periodic_pmegrid(pme, pmegrid);
-            gather_f_bsplines(pme, pmegrid, !forceReductionWithInput, atc, &atc->spline[threadIndex], scale);
+            gather_f_bsplines(pme, pmegrid, true, atc, &atc->spline[threadIndex], scale);
             break;
 
+/* The compiler will complain about passing fftgrid (converting double ** to float **) if using
+ * double precision. GPUs are not used with double precision anyhow. */
+#if !GMX_DOUBLE
         case CodePath::GPU:
         {
             // Variable initialization needs a non-switch scope
-            PmeOutput output = pme_gpu_getOutput(*pme, GMX_PME_CALC_F);
+            const bool computeEnergyAndVirial = false;
+            const real lambdaQ                = 1.0;
+            PmeOutput  output = pme_gpu_getOutput(*pme, computeEnergyAndVirial, lambdaQ);
             GMX_ASSERT(forces.size() == output.forces_.size(),
                        "Size of force buffers did not match");
-            if (forceReductionWithInput)
-            {
-                std::copy(std::begin(forces), std::end(forces), std::begin(output.forces_));
-            }
-            pme_gpu_gather(pme->gpu, inputTreatment, reinterpret_cast<float*>(fftgrid));
+            pme_gpu_gather(pme->gpu, fftgrid, lambdaQ);
             std::copy(std::begin(output.forces_), std::end(output.forces_), std::begin(forces));
         }
         break;
+#endif
 
         default: GMX_THROW(InternalError("Test not implemented for this mode"));
     }
@@ -454,6 +464,150 @@ void pmeFinalizeTest(const gmx_pme_t* pme, CodePath mode)
     }
 }
 
+//! A binary enum for spline data layout transformation
+enum class PmeLayoutTransform
+{
+    GpuToHost,
+    HostToGpu
+};
+
+/*! \brief Gets a unique index to an element in a spline parameter buffer.
+ *
+ * These theta/dtheta buffers are laid out for GPU spread/gather
+ * kernels. The index is wrt the execution block, in range(0,
+ * atomsPerBlock * order * DIM).
+ *
+ * This is a wrapper, only used in unit tests.
+ * \param[in] order            PME order
+ * \param[in] splineIndex      Spline contribution index (from 0 to \p order - 1)
+ * \param[in] dimIndex         Dimension index (from 0 to 2)
+ * \param[in] atomIndex        Atom index wrt the block.
+ * \param[in] atomsPerWarp     Number of atoms processed by a warp.
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+static int getSplineParamFullIndex(int order, int splineIndex, int dimIndex, int atomIndex, int atomsPerWarp)
+{
+    if (order != c_pmeGpuOrder)
+    {
+        throw order;
+    }
+    constexpr int fixedOrder = c_pmeGpuOrder;
+    GMX_UNUSED_VALUE(fixedOrder);
+
+    const int atomWarpIndex = atomIndex % atomsPerWarp;
+    const int warpIndex     = atomIndex / atomsPerWarp;
+    int       indexBase, result;
+    switch (atomsPerWarp)
+    {
+        case 1:
+            indexBase = getSplineParamIndexBase<fixedOrder, 1>(warpIndex, atomWarpIndex);
+            result    = getSplineParamIndex<fixedOrder, 1>(indexBase, dimIndex, splineIndex);
+            break;
+
+        case 2:
+            indexBase = getSplineParamIndexBase<fixedOrder, 2>(warpIndex, atomWarpIndex);
+            result    = getSplineParamIndex<fixedOrder, 2>(indexBase, dimIndex, splineIndex);
+            break;
+
+        case 4:
+            indexBase = getSplineParamIndexBase<fixedOrder, 4>(warpIndex, atomWarpIndex);
+            result    = getSplineParamIndex<fixedOrder, 4>(indexBase, dimIndex, splineIndex);
+            break;
+
+        case 8:
+            indexBase = getSplineParamIndexBase<fixedOrder, 8>(warpIndex, atomWarpIndex);
+            result    = getSplineParamIndex<fixedOrder, 8>(indexBase, dimIndex, splineIndex);
+            break;
+
+        default:
+            GMX_THROW(NotImplementedError(
+                    formatString("Test function call not unrolled for atomsPerWarp = %d in "
+                                 "getSplineParamFullIndex",
+                                 atomsPerWarp)));
+    }
+    return result;
+}
+
+/*!\brief Return the number of atoms per warp */
+static int pme_gpu_get_atoms_per_warp(const PmeGpu* pmeGpu)
+{
+    const int order = pmeGpu->common->pme_order;
+    const int threadsPerAtom =
+            (pmeGpu->settings.threadsPerAtom == ThreadsPerAtom::Order ? order : order * order);
+    return pmeGpu->programHandle_->warpSize() / threadsPerAtom;
+}
+
+/*! \brief Rearranges the atom spline data between the GPU and host layouts.
+ * Only used for test purposes so far, likely to be horribly slow.
+ *
+ * \param[in]  pmeGpu     The PME GPU structure.
+ * \param[out] atc        The PME CPU atom data structure (with a single-threaded layout).
+ * \param[in]  type       The spline data type (values or derivatives).
+ * \param[in]  dimIndex   Dimension index.
+ * \param[in]  transform  Layout transform type
+ */
+static void pme_gpu_transform_spline_atom_data(const PmeGpu*      pmeGpu,
+                                               const PmeAtomComm* atc,
+                                               PmeSplineDataType  type,
+                                               int                dimIndex,
+                                               PmeLayoutTransform transform)
+{
+    // The GPU atom spline data is laid out in a different way currently than the CPU one.
+    // This function converts the data from GPU to CPU layout (in the host memory).
+    // It is only intended for testing purposes so far.
+    // Ideally we should use similar layouts on CPU and GPU if we care about mixed modes and their
+    // performance (e.g. spreading on GPU, gathering on CPU).
+    GMX_RELEASE_ASSERT(atc->nthread == 1, "Only the serial PME data layout is supported");
+    const uintmax_t threadIndex  = 0;
+    const auto      atomCount    = atc->numAtoms();
+    const auto      atomsPerWarp = pme_gpu_get_atoms_per_warp(pmeGpu);
+    const auto      pmeOrder     = pmeGpu->common->pme_order;
+    GMX_ASSERT(pmeOrder == c_pmeGpuOrder, "Only PME order 4 is implemented");
+
+    real*  cpuSplineBuffer;
+    float* h_splineBuffer;
+    switch (type)
+    {
+        case PmeSplineDataType::Values:
+            cpuSplineBuffer = atc->spline[threadIndex].theta.coefficients[dimIndex];
+            h_splineBuffer  = pmeGpu->staging.h_theta;
+            break;
+
+        case PmeSplineDataType::Derivatives:
+            cpuSplineBuffer = atc->spline[threadIndex].dtheta.coefficients[dimIndex];
+            h_splineBuffer  = pmeGpu->staging.h_dtheta;
+            break;
+
+        default: GMX_THROW(InternalError("Unknown spline data type"));
+    }
+
+    for (auto atomIndex = 0; atomIndex < atomCount; atomIndex++)
+    {
+        for (auto orderIndex = 0; orderIndex < pmeOrder; orderIndex++)
+        {
+            const auto gpuValueIndex =
+                    getSplineParamFullIndex(pmeOrder, orderIndex, dimIndex, atomIndex, atomsPerWarp);
+            const auto cpuValueIndex = atomIndex * pmeOrder + orderIndex;
+            GMX_ASSERT(cpuValueIndex < atomCount * pmeOrder,
+                       "Atom spline data index out of bounds (while transforming GPU data layout "
+                       "for host)");
+            switch (transform)
+            {
+                case PmeLayoutTransform::GpuToHost:
+                    cpuSplineBuffer[cpuValueIndex] = h_splineBuffer[gpuValueIndex];
+                    break;
+
+                case PmeLayoutTransform::HostToGpu:
+                    h_splineBuffer[gpuValueIndex] = cpuSplineBuffer[cpuValueIndex];
+                    break;
+
+                default: GMX_THROW(InternalError("Unknown layout transform"));
+            }
+        }
+    }
+}
+
 //! Setting atom spline values/derivatives to be used in spread/gather
 void pmeSetSplineData(const gmx_pme_t*             pme,
                       CodePath                     mode,
@@ -505,7 +659,7 @@ void pmeSetGridLineIndices(gmx_pme_t* pme, CodePath mode, const GridLineIndicesV
     switch (mode)
     {
         case CodePath::GPU:
-            memcpy(pme->gpu->staging.h_gridlineIndices, gridLineIndices.data(),
+            memcpy(pme_gpu_staging(pme->gpu).h_gridlineIndices, gridLineIndices.data(),
                    atomCount * sizeof(gridLineIndices[0]));
             break;
 
@@ -622,7 +776,7 @@ GridLineIndicesVector pmeGetGridlineIndices(const gmx_pme_t* pme, CodePath mode)
     {
         case CodePath::GPU:
             gridLineIndices = arrayRefFromArray(
-                    reinterpret_cast<IVec*>(pme->gpu->staging.h_gridlineIndices), atomCount);
+                    reinterpret_cast<IVec*>(pme_gpu_staging(pme->gpu).h_gridlineIndices), atomCount);
             break;
 
         case CodePath::CPU: gridLineIndices = atc->idx; break;
@@ -687,7 +841,8 @@ SparseComplexGridValuesOutput pmeGetComplexGrid(const gmx_pme_t* pme, CodePath m
 //! Getting the reciprocal energy and virial
 PmeOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t* pme, CodePath mode, PmeSolveAlgorithm method)
 {
-    PmeOutput output;
+    PmeOutput  output;
+    const real lambdaQ = 1.0;
     switch (mode)
     {
         case CodePath::CPU:
@@ -707,7 +862,9 @@ PmeOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t* pme, CodePath mode, P
         case CodePath::GPU:
             switch (method)
             {
-                case PmeSolveAlgorithm::Coulomb: pme_gpu_getEnergyAndVirial(*pme, &output); break;
+                case PmeSolveAlgorithm::Coulomb:
+                    pme_gpu_getEnergyAndVirial(*pme, lambdaQ, &output);
+                    break;
 
                 default: GMX_THROW(InternalError("Test not implemented for this mode"));
             }
@@ -718,5 +875,58 @@ PmeOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t* pme, CodePath mode, P
     return output;
 }
 
+const char* codePathToString(CodePath codePath)
+{
+    switch (codePath)
+    {
+        case CodePath::CPU: return "CPU";
+        case CodePath::GPU: return "GPU";
+        default: GMX_THROW(NotImplementedError("This CodePath should support codePathToString"));
+    }
+}
+
+PmeTestHardwareContext::PmeTestHardwareContext() : codePath_(CodePath::CPU) {}
+
+PmeTestHardwareContext::PmeTestHardwareContext(TestDevice* testDevice) :
+    codePath_(CodePath::CPU),
+    testDevice_(testDevice)
+{
+    setActiveDevice(testDevice_->deviceInfo());
+    pmeGpuProgram_ = buildPmeGpuProgram(testDevice_->deviceContext());
+}
+
+//! Returns a human-readable context description line
+std::string PmeTestHardwareContext::description() const
+{
+    switch (codePath_)
+    {
+        case CodePath::CPU: return "CPU";
+        case CodePath::GPU: return "GPU (" + testDevice_->description() + ")";
+        default: return "Unknown code path.";
+    }
+}
+
+void PmeTestHardwareContext::activate() const
+{
+    if (codePath_ == CodePath::GPU)
+    {
+        setActiveDevice(testDevice_->deviceInfo());
+    }
+}
+
+std::vector<std::unique_ptr<PmeTestHardwareContext>> createPmeTestHardwareContextList()
+{
+    std::vector<std::unique_ptr<PmeTestHardwareContext>> pmeTestHardwareContextList;
+    // Add CPU
+    pmeTestHardwareContextList.emplace_back(std::make_unique<PmeTestHardwareContext>());
+    // Add GPU devices
+    const auto& testDeviceList = getTestHardwareEnvironment()->getTestDeviceList();
+    for (const auto& testDevice : testDeviceList)
+    {
+        pmeTestHardwareContextList.emplace_back(std::make_unique<PmeTestHardwareContext>(testDevice.get()));
+    }
+    return pmeTestHardwareContextList;
+}
+
 } // namespace test
 } // namespace gmx
index 1679dfb6c157da961fa62fff821c856f389a1186..6b1e6d136c492872fdb7dfb4a56a09c78f454620 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <map>
 #include <vector>
 
-#include <gtest/gtest.h>
-
 #include "gromacs/ewald/pme.h"
 #include "gromacs/ewald/pme_gpu_internal.h"
 #include "gromacs/math/gmxcomplex.h"
 #include "gromacs/mdtypes/state_propagator_data_gpu.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/unique_cptr.h"
 
-#include "testhardwarecontexts.h"
+#include "testutils/test_device.h"
 
 namespace gmx
 {
+
+template<typename>
+class ArrayRef;
+
 namespace test
 {
 
+//! Hardware code path being tested
+enum class CodePath : int
+{
+    //! CPU code path
+    CPU,
+    //! GPU code path
+    GPU,
+    //! Total number of code paths
+    Count
+};
+
+//! Return a string useful for human-readable messages describing a \c codePath.
+const char* codePathToString(CodePath codePath);
+
 // Convenience typedefs
 //! A safe pointer type for PME.
 typedef gmx::unique_cptr<gmx_pme_t, gmx_pme_destroy> PmeSafePointer;
@@ -98,10 +113,14 @@ typedef SparseGridValuesOutput<t_complex> SparseComplexGridValuesOutput;
 //! TODO: make proper C++ matrix for the whole Gromacs, get rid of this
 typedef std::array<real, DIM * DIM> Matrix3x3;
 //! PME solver type
-enum class PmeSolveAlgorithm
+enum class PmeSolveAlgorithm : int
 {
+    //! Coulomb electrostatics
     Coulomb,
+    //! Lennard-Jones
     LennardJones,
+    //! Total number of solvers
+    Count
 };
 
 // Misc.
@@ -119,23 +138,22 @@ uint64_t getSplineModuliDoublePrecisionUlps(int splineOrder);
 // PME stages
 
 //! PME initialization
-PmeSafePointer pmeInitWrapper(const t_inputrec*        inputRec,
-                              CodePath                 mode,
-                              const gmx_device_info_t* gpuInfo,
-                              PmeGpuProgramHandle      pmeGpuProgram,
-                              const Matrix3x3&         box,
-                              real                     ewaldCoeff_q  = 1.0F,
-                              real                     ewaldCoeff_lj = 1.0F);
-//! Simple PME initialization (no atom data)
-PmeSafePointer pmeInitEmpty(const t_inputrec*        inputRec,
-                            CodePath                 mode          = CodePath::CPU,
-                            const gmx_device_info_t* gpuInfo       = nullptr,
-                            PmeGpuProgramHandle      pmeGpuProgram = nullptr,
-                            const Matrix3x3& box = { { 1.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 1.0F } },
-                            real             ewaldCoeff_q  = 0.0F,
-                            real             ewaldCoeff_lj = 0.0F);
+PmeSafePointer pmeInitWrapper(const t_inputrec*    inputRec,
+                              CodePath             mode,
+                              const DeviceContext* deviceContext,
+                              const DeviceStream*  deviceStream,
+                              const PmeGpuProgram* pmeGpuProgram,
+                              const Matrix3x3&     box,
+                              real                 ewaldCoeff_q  = 1.0F,
+                              real                 ewaldCoeff_lj = 1.0F);
+
+//! Simple PME initialization based on inputrec only
+PmeSafePointer pmeInitEmpty(const t_inputrec* inputRec);
+
 //! Make a GPU state-propagator manager
-std::unique_ptr<StatePropagatorDataGpu> makeStatePropagatorDataGpu(const gmx_pme_t& pme);
+std::unique_ptr<StatePropagatorDataGpu> makeStatePropagatorDataGpu(const gmx_pme_t&     pme,
+                                                                   const DeviceContext* deviceContext,
+                                                                   const DeviceStream* deviceStream);
 //! PME initialization with atom data and system box
 void pmeInitAtoms(gmx_pme_t*               pme,
                   StatePropagatorDataGpu*  stateGpu,
@@ -152,10 +170,9 @@ void pmePerformSolve(const gmx_pme_t*  pme,
                      GridOrdering      gridOrdering,
                      bool              computeEnergyAndVirial);
 //! PME force gathering
-void pmePerformGather(gmx_pme_t*             pme,
-                      CodePath               mode,
-                      PmeForceOutputHandling inputTreatment,
-                      ForcesVector&          forces); //NOLINT(google-runtime-references)
+void pmePerformGather(gmx_pme_t*    pme,
+                      CodePath      mode,
+                      ForcesVector& forces); //NOLINT(google-runtime-references)
 //! PME test finalization before fetching the outputs
 void pmeFinalizeTest(const gmx_pme_t* pme, CodePath mode);
 
@@ -167,6 +184,7 @@ void pmeSetSplineData(const gmx_pme_t*             pme,
                       const SplineParamsDimVector& splineValues,
                       PmeSplineDataType            type,
                       int                          dimIndex);
+
 //! Setting gridline indices be used in spread/gather
 void pmeSetGridLineIndices(gmx_pme_t* pme, CodePath mode, const GridLineIndicesVector& gridLineIndices);
 //! Setting real grid to be used in gather
@@ -188,7 +206,47 @@ SparseRealGridValuesOutput pmeGetRealGrid(const gmx_pme_t* pme, CodePath mode);
 SparseComplexGridValuesOutput pmeGetComplexGrid(const gmx_pme_t* pme, CodePath mode, GridOrdering gridOrdering);
 //! Getting the reciprocal energy and virial
 PmeOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t* pme, CodePath mode, PmeSolveAlgorithm method);
+
+struct PmeTestHardwareContext
+{
+    //! Hardware path for the code being tested.
+    CodePath codePath_;
+    //! Returns a human-readable context description line
+    std::string description() const;
+    //! Pointer to the global test hardware device (if on GPU)
+    TestDevice* testDevice_ = nullptr;
+    //! PME GPU program if needed
+    PmeGpuProgramStorage pmeGpuProgram_ = nullptr;
+    // Constructor for CPU context
+    PmeTestHardwareContext();
+    // Constructor for GPU context
+    explicit PmeTestHardwareContext(TestDevice* testDevice);
+
+    //! Get the code path
+    CodePath codePath() const { return codePath_; }
+    //! Get the PME GPU program
+    const PmeGpuProgram* pmeGpuProgram() const
+    {
+        return codePath() == CodePath::GPU ? pmeGpuProgram_.get() : nullptr;
+    }
+
+    const DeviceContext* deviceContext() const
+    {
+        return codePath() == CodePath::GPU ? &testDevice_->deviceContext() : nullptr;
+    }
+
+    const DeviceStream* deviceStream() const
+    {
+        return codePath() == CodePath::GPU ? &testDevice_->deviceStream() : nullptr;
+    }
+
+    //! Activate the context (set the device)
+    void activate() const;
+};
+
+std::vector<std::unique_ptr<PmeTestHardwareContext>> createPmeTestHardwareContextList();
+
 } // namespace test
 } // namespace gmx
 
-#endif
+#endif // GMX_EWALD_PME_TEST_COMMON_H
index 46d659b611b56def4007b5cee3cac5ee39c537d1..adf3e3259efdef269a6b90b53adb172d4d21d86a 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-4.2362713136143704</Real>
-      <Real Name="Y">-2.3275379753649541</Real>
-      <Real Name="Z">-11.746263473883745</Real>
+      <Real Name="X">-3.4582204419484599</Real>
+      <Real Name="Y">-3.9969224751666577</Real>
+      <Real Name="Z">-9.9756355772564991</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.57636324498554081</Real>
-      <Real Name="Y">-0.013594713514657464</Real>
-      <Real Name="Z">-6.1436686744664879</Real>
+      <Real Name="X">-0.067954884757908562</Real>
+      <Real Name="Y">-0.85449341275494772</Real>
+      <Real Name="Z">-5.12573966904852</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_100.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_100.xml
deleted file mode 100644 (file)
index 265805b..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">2.6940571045158022</Real>
-      <Real Name="Y">2.1019189996213141</Real>
-      <Real Name="Z">-0.21251856629141863</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_101.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_101.xml
deleted file mode 100644 (file)
index 32fa955..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.19104463336113997</Real>
-      <Real Name="Y">-16.083563532509753</Real>
-      <Real Name="Z">2.8065205292802267</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-2.6398356036393578</Real>
-      <Real Name="Y">-0.91018032292522766</Real>
-      <Real Name="Z">0.93368498675769429</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.27000001072883606</Real>
-      <Real Name="Y">0.98000001907348633</Real>
-      <Real Name="Z">0.82999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_102.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_102.xml
deleted file mode 100644 (file)
index 3a17deb..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-8.0544456082501288</Real>
-      <Real Name="Y">-11.923968234039531</Real>
-      <Real Name="Z">1.9257328983388657</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_103.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_103.xml
deleted file mode 100644 (file)
index 0d9ee08..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-0.59903073971265186</Real>
-      <Real Name="Y">-1.6350310136998707</Real>
-      <Real Name="Z">-2.6446765062295143</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-7.7106240328717144</Real>
-      <Real Name="Y">-1.2779139061856373</Real>
-      <Real Name="Z">2.2200763403785335</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_104.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_104.xml
deleted file mode 100644 (file)
index 45fa915..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.060088412760632121</Real>
-      <Real Name="Y">-1.4519790167692483</Real>
-      <Real Name="Z">0.24378158880830342</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-1.3603736897226484</Real>
-      <Real Name="Y">-1.4452674741283262</Real>
-      <Real Name="Z">-0.088156767496452001</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_105.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_105.xml
deleted file mode 100644 (file)
index 862e11a..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-8.0344456086971636</Real>
-      <Real Name="Y">-11.05396822927116</Real>
-      <Real Name="Z">2.8757328864179366</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_106.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_106.xml
deleted file mode 100644 (file)
index 1c21fd0..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-0.5790307401596867</Real>
-      <Real Name="Y">-0.76503100893149911</Real>
-      <Real Name="Z">-1.6946765181504433</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-7.0506240066456707</Real>
-      <Real Name="Y">-0.60791388949633673</Real>
-      <Real Name="Z">2.6000763356101619</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_107.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_107.xml
deleted file mode 100644 (file)
index 9a18786..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.76991157055006731</Real>
-      <Real Name="Y">-1.1419790143850626</Real>
-      <Real Name="Z">0.9737816078817898</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.70999997854232788</Real>
-      <Real Name="Y">0.059999998658895493</Real>
-      <Real Name="Z">0.34999999403953552</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-1.0903736789938123</Real>
-      <Real Name="Y">-0.46526745505483991</Real>
-      <Real Name="Z">0.7418432158142475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_108.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_108.xml
deleted file mode 100644 (file)
index b8d7398..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-9.4063956103865909</Real>
-      <Real Name="Y">1.0613765111849376</Real>
-      <Real Name="Z">1.4765278904032615</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_109.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_109.xml
deleted file mode 100644 (file)
index 935b88e..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">1.6526713761104288</Real>
-      <Real Name="Y">1.789898728665017</Real>
-      <Real Name="Z">-0.59016766968693601</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
index 0ed491280340e33ca454ecd4b59e26f1d349789d..32a755e11a8905790811d6aa740ce232b355f502 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.82999998331069946</Real>
-      <Real Name="Y">0.31000000238418579</Real>
-      <Real Name="Z">0.73000001907348633</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.70999997854232788</Real>
-      <Real Name="Y">0.059999998658895493</Real>
-      <Real Name="Z">0.34999999403953552</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-3.3126533138132714</Real>
-      <Real Name="Y">-8.6003366303629782</Real>
-      <Real Name="Z">-22.745529254386152</Real>
+      <Real Name="X">-2.9109058261904628</Real>
+      <Real Name="Y">-11.975420811795583</Real>
+      <Real Name="Z">-18.523630115333241</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_110.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_110.xml
deleted file mode 100644 (file)
index 90aa5e9..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.82959875104586944</Real>
-      <Real Name="Y">-20.491954418617421</Real>
-      <Real Name="Z">2.8871516559459058</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-2.7217414105226196</Real>
-      <Real Name="Y">-1.2127254019801541</Real>
-      <Real Name="Z">0.55819396762823659</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_111.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_111.xml
deleted file mode 100644 (file)
index 5bf291e..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-9.3863956108336257</Real>
-      <Real Name="Y">1.9313765159533092</Real>
-      <Real Name="Z">2.4265278784823323</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_112.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_112.xml
deleted file mode 100644 (file)
index 9187b09..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">2.3126714023364725</Real>
-      <Real Name="Y">2.4598987453543177</Real>
-      <Real Name="Z">-0.2101676744553076</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_113.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_113.xml
deleted file mode 100644 (file)
index f2088aa..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.00040123226483002039</Real>
-      <Real Name="Y">-20.181954416233236</Real>
-      <Real Name="Z">3.6171516750193922</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-2.0117414319802918</Real>
-      <Real Name="Y">-1.1527254033212586</Real>
-      <Real Name="Z">0.90819396166777211</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.27000001072883606</Real>
-      <Real Name="Y">0.98000001907348633</Real>
-      <Real Name="Z">0.82999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_114.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_114.xml
deleted file mode 100644 (file)
index b5d4abc..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-6.5442370567032295</Real>
-      <Real Name="Y">-14.904960292549413</Real>
-      <Real Name="Z">2.4825316419739121</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_115.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_115.xml
deleted file mode 100644 (file)
index 028ca5f..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-0.48671247601652962</Real>
-      <Real Name="Y">-2.0437887671248385</Real>
-      <Real Name="Z">-1.9489107480528729</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-6.2648820267082685</Real>
-      <Real Name="Y">-1.5973923827320464</Real>
-      <Real Name="Z">1.9008625299783468</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_116.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_116.xml
deleted file mode 100644 (file)
index 5634d4e..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.0488218353680136</Real>
-      <Real Name="Y">-1.8149737709615605</Real>
-      <Real Name="Z">0.30251804902903451</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-1.1053036228996518</Real>
-      <Real Name="Y">-1.8065843426604078</Real>
-      <Real Name="Z">0.051190455240391619</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_117.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_117.xml
deleted file mode 100644 (file)
index 5520d5d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-6.5242370571502644</Real>
-      <Real Name="Y">-14.034960287781042</Real>
-      <Real Name="Z">3.4325316300529831</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_118.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_118.xml
deleted file mode 100644 (file)
index 09181d2..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-0.46671247646356445</Real>
-      <Real Name="Y">-1.1737887623564669</Real>
-      <Real Name="Z">-0.99891075997380185</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-5.6048820004822248</Real>
-      <Real Name="Y">-0.92739236604274589</Real>
-      <Real Name="Z">2.280862525209975</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_119.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_119.xml
deleted file mode 100644 (file)
index 650d147..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.78117814794268581</Real>
-      <Real Name="Y">-1.5049737685773747</Real>
-      <Real Name="Z">1.0325180681025208</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.70999997854232788</Real>
-      <Real Name="Y">0.059999998658895493</Real>
-      <Real Name="Z">0.34999999403953552</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.83530361217081572</Real>
-      <Real Name="Y">-0.82658432358692147</Real>
-      <Real Name="Z">0.88119043855109114</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
index 8df22aa42b501689e5d5379afe29f7627741831c..32b9f451efd5eca2101ceaded2fc3d5b34f7f479 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">0.55642797645760989</Real>
-      <Real Name="Y">0.3723331315116295</Real>
-      <Real Name="Z">1.6306575619901507</Real>
+      <Real Name="X">-10.129964503493252</Real>
+      <Real Name="Y">1.0239161111946309</Real>
+      <Real Name="Z">-8.1365401298928379</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_120.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_120.xml
deleted file mode 100644 (file)
index d1ecbf9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">3.4320122274828666</Real>
-      <Real Name="Y">-0.81224418229071649</Real>
-      <Real Name="Z">-0.764761636476889</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_121.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_121.xml
deleted file mode 100644 (file)
index b754dce..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">12.870589400646217</Real>
-      <Real Name="Y">2.4002360860135794</Real>
-      <Real Name="Z">-2.7526346195710181</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_122.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_122.xml
deleted file mode 100644 (file)
index c28af07..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">-3.9130491664545959</Real>
-      <Real Name="Y">-0.62204635074971815</Real>
-      <Real Name="Z">1.1633583220151211</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">4.1305407769409488</Real>
-      <Real Name="Y">-0.46469970389626269</Real>
-      <Real Name="Z">-3.6193285397896875</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.1597567999404905</Real>
-      <Real Name="Y">-0.73800016335319685</Real>
-      <Real Name="Z">-0.54897393926970917</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">2.2124223213639174</Real>
-      <Real Name="Y">-16.220145737822616</Real>
-      <Real Name="Z">2.8258755564357965</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_123.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_123.xml
deleted file mode 100644 (file)
index 672c139..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">3.4520122270358318</Real>
-      <Real Name="Y">0.057755822477655094</Real>
-      <Real Name="Z">0.18523835160218205</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_124.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_124.xml
deleted file mode 100644 (file)
index 8d70dc0..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">13.530589426872261</Real>
-      <Real Name="Y">3.0702361027028799</Real>
-      <Real Name="Z">-2.3726346243393897</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_125.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_125.xml
deleted file mode 100644 (file)
index c94d064..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">-3.8930491669016307</Real>
-      <Real Name="Y">0.24795365401865344</Real>
-      <Real Name="Z">2.1133583100941919</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">4.9605407602516483</Real>
-      <Real Name="Y">-0.1546997015120769</Real>
-      <Real Name="Z">-2.8893285207162012</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.55024317860183736</Real>
-      <Real Name="Y">-0.67800016469430135</Real>
-      <Real Name="Z">-0.19897394523017364</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">2.4824223320927534</Real>
-      <Real Name="Y">-15.24014571874913</Real>
-      <Real Name="Z">3.6558755397464959</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_126.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_126.xml
deleted file mode 100644 (file)
index bd4621e..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-16.500695458472016</Real>
-      <Real Name="Y">-15.572010667113076</Real>
-      <Real Name="Z">6.0618364657657224</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_127.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_127.xml
deleted file mode 100644 (file)
index 63f2ba6..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-0.56750283571532234</Real>
-      <Real Name="Y">-0.70603611451836135</Real>
-      <Real Name="Z">-0.73298662849655538</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-7.5662986143550031</Real>
-      <Real Name="Y">-4.9570092424793035</Real>
-      <Real Name="Z">0.22987632186540616</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_128.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_128.xml
deleted file mode 100644 (file)
index d33540d..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">-8.3516260207559565</Real>
-      <Real Name="Y">-0.89155680381272195</Real>
-      <Real Name="Z">1.9626024243687374</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-10.991689448063681</Real>
-      <Real Name="Y">-17.399473852314976</Real>
-      <Real Name="Z">1.9943402280115583</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-1.5304702532285459</Real>
-      <Real Name="Y">-2.0431421404374799</Real>
-      <Real Name="Z">-2.0385186389652117</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-7.0292328592073234</Real>
-      <Real Name="Y">-14.608255429729208</Real>
-      <Real Name="Z">3.679921612389506</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.67546778632879001</Real>
-      <Real Name="Y">-0.07424836221296291</Real>
-      <Real Name="Z">0.089381774901316297</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_129.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_129.xml
deleted file mode 100644 (file)
index 83d377c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-16.480695458919051</Real>
-      <Real Name="Y">-14.702010662344705</Real>
-      <Real Name="Z">7.0118364538447935</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
index f0a9eef513b399272a74fdac8ec5529c46781244..da6834cfde8278093d6ed074bfed0707ec87b0cc 100644 (file)
@@ -9,9 +9,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44047122348806367</Real>
-      <Real Name="Y">0.3871114065335613</Real>
-      <Real Name="Z">2.3221153764227829</Real>
+      <Real Name="X">1.7797999435035388</Real>
+      <Real Name="Y">1.7267257437615944</Real>
+      <Real Name="Z">1.3771557011554771</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_130.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_130.xml
deleted file mode 100644 (file)
index 340efaf..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-0.54750283616235718</Real>
-      <Real Name="Y">0.16396389025001024</Real>
-      <Real Name="Z">0.21701335958251566</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-6.9062985881289594</Real>
-      <Real Name="Y">-4.287009225790003</Real>
-      <Real Name="Z">0.6098763170970346</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_131.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_131.xml
deleted file mode 100644 (file)
index 1970d05..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">-8.3316260212029913</Real>
-      <Real Name="Y">-0.021556799044350372</Real>
-      <Real Name="Z">2.9126024124478085</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-10.161689464752982</Real>
-      <Real Name="Y">-17.089473849930791</Real>
-      <Real Name="Z">2.7243402470850446</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.82047027468621803</Real>
-      <Real Name="Y">-1.9831421417785844</Real>
-      <Real Name="Z">-1.6885186449256762</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-6.7592328484784874</Real>
-      <Real Name="Y">-13.628255410655722</Real>
-      <Real Name="Z">4.5099215957002059</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.38546779467344028</Real>
-      <Real Name="Y">0.02575163927715321</Real>
-      <Real Name="Z">0.76938178205387364</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_132.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_132.xml
deleted file mode 100644 (file)
index 330c2a8..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">2.788509934829829</Real>
-      <Real Name="Y">-1.0153052278633954</Real>
-      <Real Name="Z">-0.5654353231810999</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_133.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_133.xml
deleted file mode 100644 (file)
index dd4f51b..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">10.457353888025052</Real>
-      <Real Name="Y">3.0002951075169739</Real>
-      <Real Name="Z">-2.4443752014337479</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_134.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_134.xml
deleted file mode 100644 (file)
index 775c0fd..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">-3.1793524477443595</Real>
-      <Real Name="Y">-0.77755793843714771</Real>
-      <Real Name="Z">0.99148224011632069</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">3.3560643812645212</Real>
-      <Real Name="Y">-0.58087462987032856</Real>
-      <Real Name="Z">-2.8401295588358915</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.12980239995164852</Real>
-      <Real Name="Y">-0.92250020419149636</Real>
-      <Real Name="Z">-0.37393805370732247</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">1.7975931361081829</Real>
-      <Real Name="Y">-20.275182172278267</Real>
-      <Real Name="Z">3.4378840352447644</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_135.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_135.xml
deleted file mode 100644 (file)
index d4d2726..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">2.8085099343827942</Real>
-      <Real Name="Y">-0.14530522309502381</Real>
-      <Real Name="Z">0.38456466489797114</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_136.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_136.xml
deleted file mode 100644 (file)
index 7d8827f..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">11.117353914251096</Real>
-      <Real Name="Y">3.6702951242062745</Real>
-      <Real Name="Z">-2.0643752062021195</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_137.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_137.xml
deleted file mode 100644 (file)
index dc388f2..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">-3.1593524481913944</Real>
-      <Real Name="Y">0.092442066331223871</Real>
-      <Real Name="Z">1.9414822281953916</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">4.1860643645752207</Real>
-      <Real Name="Y">-0.27087462748614277</Real>
-      <Real Name="Z">-2.1101295397624051</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.58019757859067933</Real>
-      <Real Name="Y">-0.86250020553260087</Real>
-      <Real Name="Z">-0.023938059667786948</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">2.067593146837019</Real>
-      <Real Name="Y">-19.295182153204781</Real>
-      <Real Name="Z">4.2678840185554634</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_138.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_138.xml
deleted file mode 100644 (file)
index 9871d3a..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-13.406815060008514</Real>
-      <Real Name="Y">-19.465013333891342</Real>
-      <Real Name="Z">6.0748932006495915</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_139.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_139.xml
deleted file mode 100644 (file)
index 53b7b71..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-3.4931516109310436</Real>
-      <Real Name="Y">-1.1695586621622649</Real>
-      <Real Name="Z">0.37948711624127879</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-6.1476176241634404</Real>
-      <Real Name="Y">-6.196261553099129</Real>
-      <Real Name="Z">0.61604971101077255</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
index e5582ccc2ea98719234347b4d60270d34d483576..64e514723ced3177cab32b14729030078a3af31a 100644 (file)
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-8.1581138200755436</Real>
-      <Real Name="Y">-14.70340061881376</Real>
-      <Real Name="Z">-8.9453847792568535</Real>
+      <Real Name="X">-0.89341403958785948</Real>
+      <Real Name="Y">-19.76870795422418</Real>
+      <Real Name="Z">-5.5136166998409726</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-8.4309497156824804</Real>
-      <Real Name="Y">-11.184451168201075</Real>
-      <Real Name="Z">-7.7012318674352152</Real>
+      <Real Name="X">-2.9311061344089753</Real>
+      <Real Name="Y">-1.1699232689408006</Real>
+      <Real Name="Z">-3.2719141904858438</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_140.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_140.xml
deleted file mode 100644 (file)
index 5f89638..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">-6.7856961418642161</Real>
-      <Real Name="Y">-1.1144460047659024</Real>
-      <Real Name="Z">1.6740807570530818</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-8.9307476765517411</Real>
-      <Real Name="Y">-21.749342315393726</Real>
-      <Real Name="Z">2.9757623819770487</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-1.2435070807481936</Real>
-      <Real Name="Y">-2.5539276755468499</Real>
-      <Real Name="Z">-1.4344239512261392</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-5.7112516981059507</Real>
-      <Real Name="Y">-18.260319287161511</Real>
-      <Real Name="Z">4.0572521792161034</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.5488175763921419</Real>
-      <Real Name="Y">-0.092810452766203616</Real>
-      <Real Name="Z">0.081070341914709868</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_141.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_141.xml
deleted file mode 100644 (file)
index 26365b6..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-13.386815060455548</Real>
-      <Real Name="Y">-18.59501332912297</Real>
-      <Real Name="Z">7.0248931887286625</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_142.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_142.xml
deleted file mode 100644 (file)
index 145c59c..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-3.4731516113780785</Real>
-      <Real Name="Y">-0.29955865739389331</Real>
-      <Real Name="Z">1.3294871043203498</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-5.4876175979373967</Real>
-      <Real Name="Y">-5.5262615364098284</Real>
-      <Real Name="Z">0.99604970624240097</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_143.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_143.xml
deleted file mode 100644 (file)
index 237ccf4..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">-6.7656961423112509</Real>
-      <Real Name="Y">-0.24444599999753081</Real>
-      <Real Name="Z">2.6240807451321526</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-8.1007476932410416</Real>
-      <Real Name="Y">-21.43934231300954</Real>
-      <Real Name="Z">3.705762401050535</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.53350710220586572</Real>
-      <Real Name="Y">-2.4939276768879544</Real>
-      <Real Name="Z">-1.0844239571866037</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-5.4412516873771146</Real>
-      <Real Name="Y">-17.280319268088025</Real>
-      <Real Name="Z">4.8872521625268028</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.25881758473679217</Real>
-      <Real Name="Y">0.0071895487239125033</Real>
-      <Real Name="Z">0.76107034906726723</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
index dd50b3f12bf28cdb9d43c4db183349c58ed22fbc..f812d5cf0af0f59c3aa2867205fe0421ccf02e76 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">0.57642797601057505</Real>
-      <Real Name="Y">1.242333136280001</Real>
-      <Real Name="Z">2.5806575500692217</Real>
+      <Real Name="X">-7.0476399072188638</Real>
+      <Real Name="Y">-14.378902132683789</Real>
+      <Real Name="Z">-14.272277552262375</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index ee9cd8baa8f54f05bb86a68c88cf93bc8bf4ce07..1dd998ae50c6daabf9793b2024993fc5e4472f0a 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">-0.52415189724857048</Real>
+      <Real Name="Y">-1.9716549447673803</Real>
+      <Real Name="Z">-18.815861243981043</Real>
     </Vector>
     <Vector>
-      <Real Name="X">1.1004712497141074</Real>
-      <Real Name="Y">1.0571114232228618</Real>
-      <Real Name="Z">2.7021153716544113</Real>
+      <Real Name="X">-6.7467960287627502</Real>
+      <Real Name="Y">-1.5410137489785949</Real>
+      <Real Name="Z">-1.2290404991250639</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index f0c2f1dd06b046b83febb0218b205c6867b40598..28f1061a2a48a8dbe9dad6bc9b6631b0a4ac17f7 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-7.3281138367648442</Real>
-      <Real Name="Y">-14.393400616429574</Real>
-      <Real Name="Z">-8.2153847601833672</Real>
+      <Real Name="X">-0.052577361165553116</Real>
+      <Real Name="Y">-1.7509157833241376</Real>
+      <Real Name="Z">-0.070066070618527432</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-7.7209497371401525</Real>
-      <Real Name="Y">-11.12445116954218</Real>
-      <Real Name="Z">-7.3512318733956796</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.27000001072883606</Real>
-      <Real Name="Y">0.98000001907348633</Real>
-      <Real Name="Z">0.82999998331069946</Real>
+      <Real Name="X">-1.1903269785073174</Real>
+      <Real Name="Y">-1.7428224529077028</Real>
+      <Real Name="Z">-4.3636777044640338</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index c634fc3ec6b8fa462e84e0fe1767545a83359aca..771778a51091ffa0e02e0c5dd8abe8c804812b1a 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-16.205517527929572</Real>
-      <Real Name="Y">-7.3188043172213684</Real>
-      <Real Name="Z">-7.9336359387100472</Real>
+      <Real Name="X">-8.2305961590882664</Real>
+      <Real Name="Y">1.2798951389932887</Real>
+      <Real Name="Z">-6.3929958163443716</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index adf3e3259efdef269a6b90b53adb172d4d21d86a..515e4103be24a743a3e9a512890af2bfa8b24317 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-3.4582204419484599</Real>
-      <Real Name="Y">-3.9969224751666577</Real>
-      <Real Name="Z">-9.9756355772564991</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.067954884757908562</Real>
-      <Real Name="Y">-0.85449341275494772</Real>
-      <Real Name="Z">-5.12573966904852</Real>
+      <Real Name="X">1.4460874540966253</Real>
+      <Real Name="Y">2.1584071797019928</Real>
+      <Real Name="Z">1.0820509080507321</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 32a755e11a8905790811d6aa740ce232b355f502..81e684c2abab9f2373cadc942349820b43fe8b8e 100644 (file)
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
+      <Real Name="X">-0.72589890716513583</Real>
+      <Real Name="Y">-24.710884942780222</Real>
+      <Real Name="Z">-4.3321274070179072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.3815237342072924</Real>
+      <Real Name="Y">-1.4624040861760008</Real>
+      <Real Name="Z">-2.5707897210960202</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Y">0</Real>
       <Real Name="Z">0</Real>
     </Vector>
-    <Vector>
-      <Real Name="X">-2.9109058261904628</Real>
-      <Real Name="Y">-11.975420811795583</Real>
-      <Real Name="Z">-18.523630115333241</Real>
-    </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Y">0</Real>
index b98c4bb01a15b87b2a162ffb3b3a80131563e14a..61df0e832a3062a564dce473fd755cae923fdded 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-16.185517528376607</Real>
-      <Real Name="Y">-6.4488043124529968</Real>
-      <Real Name="Z">-6.9836359506309762</Real>
+      <Real Name="X">-5.7262074246153265</Real>
+      <Real Name="Y">-17.973627665854735</Real>
+      <Real Name="Z">-11.213932362491866</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index f2588cbd43b48e330add9e198e50da902dd9b24e..73af10376f844806a48762f4afe970f84772a9a2 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-3.4382204423954947</Real>
-      <Real Name="Y">-3.1269224703982861</Real>
-      <Real Name="Z">-9.025635589177428</Real>
+      <Real Name="X">-0.42587341651446342</Real>
+      <Real Name="Y">-2.4645686809592258</Real>
+      <Real Name="Z">-14.783890977413678</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.59204514146813514</Real>
-      <Real Name="Y">-0.18449339606564719</Real>
-      <Real Name="Z">-4.7457396738168915</Real>
+      <Real Name="X">-5.4817717733697346</Real>
+      <Real Name="Y">-1.9262671862232437</Real>
+      <Real Name="Z">-0.96567467788397876</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 480214237cc074aaefe67497e7992afc18d38385..bc152230623d8443cb8e8f1650a599f595b67377 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.82999998331069946</Real>
-      <Real Name="Y">0.31000000238418579</Real>
-      <Real Name="Z">0.73000001907348633</Real>
+      <Real Name="X">-0.0427191059470119</Real>
+      <Real Name="Y">-2.1886447291551718</Real>
+      <Real Name="Z">-0.055051912628842979</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.70999997854232788</Real>
-      <Real Name="Y">0.059999998658895493</Real>
-      <Real Name="Z">0.34999999403953552</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-2.6409058154616267</Real>
-      <Real Name="Y">-10.995420792722097</Real>
-      <Real Name="Z">-17.693630132022541</Real>
+      <Real Name="X">-0.96714067003719539</Real>
+      <Real Name="Y">-2.1785280661346289</Real>
+      <Real Name="Z">-3.4286039106503119</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 9f65a9d98330f255951fee01a73a5eac7c8d9f18..fb5bbfcd5aa7576675428bc5bb2100902ca3debe 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-10.129964503493252</Real>
-      <Real Name="Y">1.0239161111946307</Real>
-      <Real Name="Z">-8.1365401298928379</Real>
+      <Real Name="X">3.0030106990475081</Real>
+      <Real Name="Y">-0.97947087544725719</Real>
+      <Real Name="Z">0.52873130622863018</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index da6834cfde8278093d6ed074bfed0707ec87b0cc..bde9921db6667d1a046e0785f743a287ac847780 100644 (file)
@@ -9,9 +9,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">1.7797999435035388</Real>
-      <Real Name="Y">1.7267257437615944</Real>
-      <Real Name="Z">1.3771557011554771</Real>
+      <Real Name="X">11.261765725565441</Real>
+      <Real Name="Y">2.8944021905057711</Real>
+      <Real Name="Z">8.1326966202729416</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 64e514723ced3177cab32b14729030078a3af31a..440ae9f6182195b22fbadc13b090216537413386 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
+      <Real Name="X">-3.423918020647772</Real>
+      <Real Name="Y">-0.75011467859246217</Real>
+      <Real Name="Z">-0.37339673869951689</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.89341403958785948</Real>
-      <Real Name="Y">-19.76870795422418</Real>
-      <Real Name="Z">-5.5136166998409726</Real>
+      <Real Name="X">3.6142231798233309</Real>
+      <Real Name="Y">-0.5603731435929743</Real>
+      <Real Name="Z">-15.314157091800631</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-2.9311061344089753</Real>
-      <Real Name="Y">-1.1699232689408006</Real>
-      <Real Name="Z">-3.2719141904858438</Real>
+      <Real Name="X">-0.13978719994792918</Real>
+      <Real Name="Y">-0.88994132779279667</Real>
+      <Real Name="Z">-4.3663155404400467</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
@@ -39,9 +39,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
+      <Real Name="X">1.935869531193428</Real>
+      <Real Name="Y">-19.559586503779869</Real>
+      <Real Name="Z">4.8894339493261105</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
index 79179ca9fcfd7260ea205417256653c68b078e28..015696753483be8306662aa39b6f269122a50cbe 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-10.109964503940287</Real>
-      <Real Name="Y">1.8939161159630022</Real>
-      <Real Name="Z">-7.1865401418137669</Real>
+      <Real Name="X">-14.438108526163012</Real>
+      <Real Name="Y">-18.778011899791231</Real>
+      <Real Name="Z">-7.4710258563699679</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 49d85f3a43d36764f576da1a99dc25d8040fe3d9..75f89f2d3a5d96f0512ff1ff62fe945e3a861a5a 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">-0.49656498125090714</Real>
+      <Real Name="Y">-0.85139644735204068</Real>
+      <Real Name="Z">-6.170384440946111</Real>
     </Vector>
     <Vector>
-      <Real Name="X">2.4397999697295827</Real>
-      <Real Name="Y">2.3967257604508951</Real>
-      <Real Name="Z">1.7571556963871056</Real>
+      <Real Name="X">-6.6205112875606282</Real>
+      <Real Name="Y">-5.9775696621654237</Real>
+      <Real Name="Z">-16.795786276144295</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 271e8e1bcc581f0776eac33cd06596e9032ec5a3..043cabb341e3c3b1556a65f8d8b05918df2bf13b 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">-7.3076727681614635</Real>
+      <Real Name="Y">-1.0751125611988102</Real>
+      <Real Name="Z">-3.5350277386547013</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.063414056277160014</Real>
-      <Real Name="Y">-19.458707951839994</Real>
-      <Real Name="Z">-4.7836166807674863</Real>
+      <Real Name="X">-9.6177282670557229</Real>
+      <Real Name="Y">-20.98171739240464</Real>
+      <Real Name="Z">-24.469455185751023</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-2.2211061558666474</Real>
-      <Real Name="Y">-1.1099232702819051</Real>
-      <Real Name="Z">-2.9219141964463082</Real>
+      <Real Name="X">-1.3391614715749778</Real>
+      <Real Name="Y">-2.4637889252880081</Real>
+      <Real Name="Z">-17.156428586866937</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.27000001072883606</Real>
-      <Real Name="Y">0.98000001907348633</Real>
-      <Real Name="Z">0.82999998331069946</Real>
+      <Real Name="X">-6.1505787518064086</Real>
+      <Real Name="Y">-17.61583652610614</Real>
+      <Real Name="Z">-4.4618914487107055</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
+      <Real Name="X">-0.59103431303769127</Real>
+      <Real Name="Y">-0.089534785133402892</Real>
+      <Real Name="Z">-0.71108816991442636</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 8c011cef78644f5f8a1f9c83ece43b237d38d28d..f9540fbb9376adadc43d20dc13095bd4b4fd2b37 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">0.70483443211617736</Real>
-      <Real Name="Y">1.1678665099776753</Real>
-      <Real Name="Z">3.0253823397028992</Real>
+      <Real Name="X">-19.945252342067167</Real>
+      <Real Name="Y">-5.8550434537770943</Real>
+      <Real Name="Z">-10.097354831085514</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index f812d5cf0af0f59c3aa2867205fe0421ccf02e76..ab73bb477ea42fe5dd0baf8ca35dd7b421c96305 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-7.0476399072188638</Real>
-      <Real Name="Y">-14.378902132683789</Real>
-      <Real Name="Z">-14.272277552262375</Real>
+      <Real Name="X">2.4399461929761004</Real>
+      <Real Name="Y">-1.2243385943090714</Real>
+      <Real Name="Z">0.41543174060820942</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 1dd998ae50c6daabf9793b2024993fc5e4472f0a..2de56c45b427850636c65c9d5c371b22cdd82f88 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-0.52415189724857048</Real>
-      <Real Name="Y">-1.9716549447673803</Real>
-      <Real Name="Z">-18.815861243981043</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-6.7467960287627502</Real>
-      <Real Name="Y">-1.5410137489785949</Real>
-      <Real Name="Z">-1.2290404991250639</Real>
+      <Real Name="X">9.1501846520219203</Real>
+      <Real Name="Y">3.6180027381322137</Real>
+      <Real Name="Z">6.3899759159287406</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 28f1061a2a48a8dbe9dad6bc9b6631b0a4ac17f7..79bbf3b0e2befe55ee9e09dd6dc309c6305f2fd9 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
+      <Real Name="X">-2.7819333917763145</Real>
+      <Real Name="Y">-0.93764334824057782</Real>
+      <Real Name="Z">-0.2933831518353347</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.052577361165553116</Real>
-      <Real Name="Y">-1.7509157833241376</Real>
-      <Real Name="Z">-0.070066070618527432</Real>
+      <Real Name="X">2.9365563336064566</Real>
+      <Real Name="Y">-0.70046642949121796</Real>
+      <Real Name="Z">-12.032552000700495</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
+      <Real Name="X">-0.11357709995769247</Real>
+      <Real Name="Y">-1.112426659740996</Real>
+      <Real Name="Z">-3.4306764960600371</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
@@ -39,9 +39,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-1.1903269785073174</Real>
-      <Real Name="Y">-1.7428224529077028</Real>
-      <Real Name="Z">-4.3636777044640338</Real>
+      <Real Name="X">1.5728939940946602</Real>
+      <Real Name="Y">-24.449483129724836</Real>
+      <Real Name="Z">3.8416981030419435</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
index 2201ef182510e1e1505c33192bb7fcde17ae0d8f..ae13cfb5b2e9947f95f8894dd8b8174d702ec92b 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-7.0276399076658986</Real>
-      <Real Name="Y">-13.508902127915418</Real>
-      <Real Name="Z">-13.322277564183304</Real>
+      <Real Name="X">-11.730963177507448</Real>
+      <Real Name="Y">-23.472514874739037</Real>
+      <Real Name="Z">-5.870091744290689</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 7c8a85aca7e542dbbd94c6755d92e9ea09f5c28c..ce158bbce5efd8ad4811f0b3a5855eee4b09ce83 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-0.50415189769560531</Real>
-      <Real Name="Y">-1.1016549399990088</Real>
-      <Real Name="Z">-17.865861255901972</Real>
+      <Real Name="X">-3.0565076595646632</Real>
+      <Real Name="Y">-1.4103500790664754</Real>
+      <Real Name="Z">-4.9677026084105016</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-6.0867960025367065</Real>
-      <Real Name="Y">-0.87101373228929435</Real>
-      <Real Name="Z">-0.84904050389343544</Real>
+      <Real Name="X">-5.3791654211430107</Real>
+      <Real Name="Y">-7.4719620777067792</Real>
+      <Real Name="Z">-13.196689216970519</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index cf4222c6be1502a433681c448a428848ceae946d..7968b98e6476676df08b5642810f4ebb6f2e3a45 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">-5.9374841241311893</Real>
+      <Real Name="Y">-1.3438907014985126</Real>
+      <Real Name="Z">-2.7775217946572655</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.77742262214514635</Real>
-      <Real Name="Y">-1.4409157809399518</Real>
-      <Real Name="Z">0.6599339484549589</Real>
+      <Real Name="X">-7.8144042169827745</Real>
+      <Real Name="Y">-26.227146740505805</Real>
+      <Real Name="Z">-19.226000503090088</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.70999997854232788</Real>
-      <Real Name="Y">0.059999998658895493</Real>
-      <Real Name="Z">0.34999999403953552</Real>
+      <Real Name="X">-1.0880686956546695</Real>
+      <Real Name="Y">-3.0797361566100099</Real>
+      <Real Name="Z">-13.480051032538309</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.92032696777848133</Real>
-      <Real Name="Y">-0.76282243383421644</Real>
-      <Real Name="Z">-3.5336777211533343</Real>
+      <Real Name="X">-4.9973452358427064</Real>
+      <Real Name="Y">-22.019795657632674</Real>
+      <Real Name="Z">-3.5057718525584116</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
+      <Real Name="X">-0.48021537934312419</Real>
+      <Real Name="Y">-0.1119184814167536</Real>
+      <Real Name="Z">-0.55871213350419213</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 4c5cf09410c13244c561b4627f4dd4689cd9071c..571aef36b81eca980c22d39691891267cf6e9c6a 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-8.2305961590882664</Real>
-      <Real Name="Y">1.2798951389932887</Real>
-      <Real Name="Z">-6.3929958163443734</Real>
+      <Real Name="X">0.78266792292938536</Real>
+      <Real Name="Y">0.24701126089639641</Real>
+      <Real Name="Z">0.075197087339731225</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 515e4103be24a743a3e9a512890af2bfa8b24317..f59a29786c28bfb1efe4950a153b5035036f76a0 100644 (file)
@@ -9,9 +9,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">1.4460874540966253</Real>
-      <Real Name="Y">2.1584071797019928</Real>
-      <Real Name="Z">1.0820509080507321</Real>
+      <Real Name="X">0.61956391875244121</Real>
+      <Real Name="Y">0.25681538531643089</Real>
+      <Real Name="Z">0.264650385589911</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 81e684c2abab9f2373cadc942349820b43fe8b8e..831e52909278452bc85bd4c4c4a79e7f4361321f 100644 (file)
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.72589890716513583</Real>
-      <Real Name="Y">-24.710884942780222</Real>
-      <Real Name="Z">-4.3321274070179072</Real>
+      <Real Name="X">-11.475149109556806</Real>
+      <Real Name="Y">-9.7544516427343577</Real>
+      <Real Name="Z">3.0247422457903341</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-2.3815237342072924</Real>
-      <Real Name="Y">-1.4624040861760008</Real>
-      <Real Name="Z">-2.5707897210960202</Real>
+      <Real Name="X">-11.858918281399532</Real>
+      <Real Name="Y">-7.4199289605932677</Real>
+      <Real Name="Z">3.011716132619819</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
index 4bf64f36c2a15110999e15dce2f51cbb0f9fcacc..2b89260977076cbf0f70a970843737aa25207478 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-8.2105961595353012</Real>
-      <Real Name="Y">2.1498951437616602</Real>
-      <Real Name="Z">-5.4429958282653024</Real>
+      <Real Name="X">-22.79457410521962</Real>
+      <Real Name="Y">-4.8554021376267826</Real>
+      <Real Name="Z">5.6800905790772545</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index f40c696953e9d9f0698dffb7161ca1a580afba6c..a985a7b5bb8adc1518310cc919cc00dea778d376 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">-4.2562713131673355</Real>
+      <Real Name="Y">-3.1975379801333257</Real>
+      <Real Name="Z">-12.696263461962817</Real>
     </Vector>
     <Vector>
-      <Real Name="X">1.2021184551344297</Real>
-      <Real Name="Y">0.97968914191614953</Real>
-      <Real Name="Z">3.3354195652242611</Real>
+      <Real Name="X">-0.083636781240502847</Real>
+      <Real Name="Y">-0.683594730203958</Real>
+      <Real Name="Z">-6.5236686696981163</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index a1b6cd62fc08cb854bb32d3fb6957c147b5196de..7b95007f25a2db9a0bd5fb6e8ce778be6b470dd5 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">-4.8643100721912402</Real>
+      <Real Name="Y">-2.6516169976273649</Real>
+      <Real Name="Z">-0.25116456753339361</Real>
     </Vector>
     <Vector>
-      <Real Name="X">2.1060874803226692</Real>
-      <Real Name="Y">2.8284071963912933</Real>
-      <Real Name="Z">1.4620509032823605</Real>
+      <Real Name="X">-0.09558489284628896</Real>
+      <Real Name="Y">-0.56688346388984168</Real>
+      <Real Name="Z">-0.94910028423615156</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 4a5309be9b9a0d73ca5c30fc89eef73dda81c6a6..b008c5693fb115cef92823f396da5567642c503c 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10410107614556363</Real>
-      <Real Name="Y">-24.400884940396036</Real>
-      <Real Name="Z">-3.6021273879444209</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-1.6715237556649645</Real>
-      <Real Name="Y">-1.4024040875171053</Real>
-      <Real Name="Z">-2.2207897270564847</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.27000001072883606</Real>
-      <Real Name="Y">0.98000001907348633</Real>
-      <Real Name="Z">0.82999998331069946</Real>
+      <Real Name="X">-4.0944609423338374</Real>
+      <Real Name="Y">-7.9446698242436193</Real>
+      <Real Name="Z">-1.3877955568392644</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 61df0e832a3062a564dce473fd755cae923fdded..5f5962404fcb48e2b0c784bf39ef85cc18d92cca 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-5.7262074246153265</Real>
-      <Real Name="Y">-17.973627665854735</Real>
-      <Real Name="Z">-11.213932362491866</Real>
+      <Real Name="X">0.63591768738012566</Real>
+      <Real Name="Y">0.3087640761204955</Real>
+      <Real Name="Z">0.03426844856107035</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 73af10376f844806a48762f4afe970f84772a9a2..ef5c4c2864e3d1661bf539c98d477b45ddb34abc 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-0.42587341651446342</Real>
-      <Real Name="Y">-2.4645686809592258</Real>
-      <Real Name="Z">-14.783890977413678</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-5.4817717733697346</Real>
-      <Real Name="Y">-1.9262671862232437</Real>
-      <Real Name="Z">-0.96567467788397876</Real>
+      <Real Name="X">0.50339568398635848</Real>
+      <Real Name="Y">0.32101923164553864</Real>
+      <Real Name="Z">0.18363175660878781</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index bc152230623d8443cb8e8f1650a599f595b67377..ef02889786b4972c6adee6c8c5ab9f4d6c4659d5 100644 (file)
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.0427191059470119</Real>
-      <Real Name="Y">-2.1886447291551718</Real>
-      <Real Name="Z">-0.055051912628842979</Real>
+      <Real Name="X">-9.3235586515149063</Real>
+      <Real Name="Y">-12.193064553417946</Real>
+      <Real Name="Z">3.2071979869138385</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.6353711036371195</Real>
+      <Real Name="Y">-9.2749112007415846</Real>
+      <Real Name="Z">3.0222260742447977</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Y">0</Real>
       <Real Name="Z">0</Real>
     </Vector>
-    <Vector>
-      <Real Name="X">-0.96714067003719539</Real>
-      <Real Name="Y">-2.1785280661346289</Real>
-      <Real Name="Z">-3.4286039106503119</Real>
-    </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Y">0</Real>
index 7441c778876cc8f1198b818eac254c99af49b4e7..759360dfb5cb219fab7c15fbeec5c50aa8ccdbcc 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-5.7062074250623613</Real>
-      <Real Name="Y">-17.103627661086364</Real>
-      <Real Name="Z">-10.263932374412795</Real>
+      <Real Name="X">-18.520591460490941</Real>
+      <Real Name="Y">-6.0692526720334774</Real>
+      <Real Name="Z">5.0076479125818185</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 5ca4468fd8fa2fb0aeeefba6905e66ab6fdf07a8..e86b6ed4273899b92c556e48d3b52b518be988ce 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-0.40587341696149826</Real>
-      <Real Name="Y">-1.5945686761908542</Real>
-      <Real Name="Z">-13.833890989334607</Real>
+      <Real Name="X">-3.9522519336553823</Real>
+      <Real Name="Y">-3.3145212470342056</Real>
+      <Real Name="Z">0.041856812732197594</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-4.8217717471436909</Real>
-      <Real Name="Y">-1.2562671695339431</Real>
-      <Real Name="Z">-0.58567468265235034</Real>
+      <Real Name="X">-0.077662725437609784</Real>
+      <Real Name="Y">-0.70860432986230215</Real>
+      <Real Name="Z">-0.70184026829559087</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 0d6f646015538967d3076fd376c437e5960eff1d..993c18dbf6c1d08e802b82495896d45db1e69aa4 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.78728087736368757</Real>
-      <Real Name="Y">-1.878644726770986</Real>
-      <Real Name="Z">0.67494810644464331</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.70999997854232788</Real>
-      <Real Name="Y">0.059999998658895493</Real>
-      <Real Name="Z">0.34999999403953552</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.69714065930835933</Real>
-      <Real Name="Y">-1.1985280470611426</Real>
-      <Real Name="Z">-2.5986039273396124</Real>
+      <Real Name="X">-3.326749515646243</Real>
+      <Real Name="Y">-9.9308372803045231</Real>
+      <Real Name="Z">-0.45425921737202102</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index fb5bbfcd5aa7576675428bc5bb2100902ca3debe..b0c8d113577fd57cd8c1695ee12497289df8ee38 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">3.0030106990475081</Real>
-      <Real Name="Y">-0.97947087544725719</Real>
-      <Real Name="Z">0.52873130622863018</Real>
+      <Real Name="X">-11.577102289706572</Real>
+      <Real Name="Y">0.84910120894795016</Real>
+      <Real Name="Z">1.8482439089193423</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index bde9921db6667d1a046e0785f743a287ac847780..7bc4c48eb1679675d8dfd5d409fe60a1817619c3 100644 (file)
@@ -9,9 +9,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">11.261765725565441</Real>
-      <Real Name="Y">2.8944021905057711</Real>
-      <Real Name="Z">8.1326966202729416</Real>
+      <Real Name="X">2.0340570782897585</Real>
+      <Real Name="Y">1.4319189829320136</Real>
+      <Real Name="Z">-0.59251856152304705</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 41a9637cd7d40274abeeef528c09e5f5765d1f37..e537a3868004aec396d670133f38248057360a80 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-9.2107554875515074</Real>
-      <Real Name="Y">-11.452720492666822</Real>
-      <Real Name="Z">-10.655035154526145</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-9.6665535176822637</Real>
-      <Real Name="Y">-8.8875609359019645</Real>
-      <Real Name="Z">-9.4515678372416474</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.27000001072883606</Real>
-      <Real Name="Y">0.98000001907348633</Real>
-      <Real Name="Z">0.82999998331069946</Real>
+      <Real Name="X">-3.5826533245421075</Real>
+      <Real Name="Y">-9.5803366494364646</Real>
+      <Real Name="Z">-23.575529237696852</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 3dce4918c1f07a22d0dfe70e02da8b37bab475be..f60869de13d25ce73bbe6111b28a18a1c455b2cc 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">-3.423918020647772</Real>
-      <Real Name="Y">-0.75011467859246217</Real>
-      <Real Name="Z">-0.37339673869951695</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">3.6142231798233309</Real>
-      <Real Name="Y">-0.5603731435929743</Real>
-      <Real Name="Z">-15.314157091800631</Real>
+      <Real Name="X">-1.0210446166718394</Real>
+      <Real Name="Y">-16.393563534893939</Real>
+      <Real Name="Z">2.0765205102067403</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.13978719994792918</Real>
-      <Real Name="Y">-0.88994132779279667</Real>
-      <Real Name="Z">-4.3663155404400467</Real>
+      <Real Name="X">-3.3498355821816856</Real>
+      <Real Name="Y">-0.97018032158412315</Real>
+      <Real Name="Z">0.58368499271815877</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
@@ -39,9 +39,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">1.935869531193428</Real>
-      <Real Name="Y">-19.559586503779869</Real>
-      <Real Name="Z">4.8894339493261105</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
index 353f2d7b83d9ded6ec95a40d8158511159214da0..3a17debac991a01c6edfd2b4a18942c1ee26ed84 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">3.0230106986004732</Real>
-      <Real Name="Y">-0.10947087067888561</Real>
-      <Real Name="Z">1.4787312943077011</Real>
+      <Real Name="X">-8.0544456082501288</Real>
+      <Real Name="Y">-11.923968234039531</Real>
+      <Real Name="Z">1.9257328983388657</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 748e28d391611a3653d3c6daeafa7796cb988a66..0d9ee088137f930d5e6d637ee20e1adace9106fc 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">-0.59903073971265186</Real>
+      <Real Name="Y">-1.6350310136998707</Real>
+      <Real Name="Z">-2.6446765062295143</Real>
     </Vector>
     <Vector>
-      <Real Name="X">11.921765751791485</Real>
-      <Real Name="Y">3.5644022071950716</Real>
-      <Real Name="Z">8.51269661550457</Real>
+      <Real Name="X">-7.7106240328717144</Real>
+      <Real Name="Y">-1.2779139061856373</Real>
+      <Real Name="Z">2.2200763403785335</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 1ed2e94e1c1b702b82d3edfd8a44637c30738c71..45fa91501e237ce7762549594b43136acd9269ad 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">-3.4039180210948068</Real>
-      <Real Name="Y">0.11988532617590941</Real>
-      <Real Name="Z">0.57660324937955409</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">4.4442231631340299</Real>
-      <Real Name="Y">-0.25037314120878851</Real>
-      <Real Name="Z">-14.584157072727145</Real>
+      <Real Name="X">-0.060088412760632121</Real>
+      <Real Name="Y">-1.4519790167692483</Real>
+      <Real Name="Z">0.24378158880830342</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.57021277859439867</Real>
-      <Real Name="Y">-0.82994132913390117</Real>
-      <Real Name="Z">-4.0163155464005111</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">2.2058695419222643</Real>
-      <Real Name="Y">-18.579586484706383</Real>
-      <Real Name="Z">5.71943393263681</Real>
+      <Real Name="X">-1.3603736897226484</Real>
+      <Real Name="Y">-1.4452674741283262</Real>
+      <Real Name="Z">-0.088156767496452001</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 5e7af781e257ab3f1a3e9edd08db9ddb4e684f80..e8bfc65fd73ecb1e991c02427ab42a914524de26 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-14.438108526163015</Real>
-      <Real Name="Y">-18.778011899791231</Real>
-      <Real Name="Z">-7.4710258563699679</Real>
+      <Real Name="X">-9.4063956103865909</Real>
+      <Real Name="Y">1.0613765111849378</Real>
+      <Real Name="Z">1.4765278904032617</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 75f89f2d3a5d96f0512ff1ff62fe945e3a861a5a..935b88ecfc7890caeef069d935ab7769fb225057 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-0.49656498125090714</Real>
-      <Real Name="Y">-0.85139644735204068</Real>
-      <Real Name="Z">-6.170384440946111</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-6.6205112875606282</Real>
-      <Real Name="Y">-5.9775696621654237</Real>
-      <Real Name="Z">-16.795786276144295</Real>
+      <Real Name="X">1.6526713761104288</Real>
+      <Real Name="Y">1.789898728665017</Real>
+      <Real Name="Z">-0.59016766968693601</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 043cabb341e3c3b1556a65f8d8b05918df2bf13b..90aa5e9aefa98c4a20dd2754236636c3b4c8868f 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">-7.3076727681614635</Real>
-      <Real Name="Y">-1.0751125611988102</Real>
-      <Real Name="Z">-3.5350277386547013</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-9.6177282670557229</Real>
-      <Real Name="Y">-20.98171739240464</Real>
-      <Real Name="Z">-24.469455185751023</Real>
+      <Real Name="X">-0.82959875104586944</Real>
+      <Real Name="Y">-20.491954418617421</Real>
+      <Real Name="Z">2.8871516559459058</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-1.3391614715749778</Real>
-      <Real Name="Y">-2.4637889252880081</Real>
-      <Real Name="Z">-17.156428586866937</Real>
+      <Real Name="X">-2.7217414105226196</Real>
+      <Real Name="Y">-1.2127254019801541</Real>
+      <Real Name="Z">0.55819396762823659</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
@@ -39,9 +39,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-6.1505787518064086</Real>
-      <Real Name="Y">-17.61583652610614</Real>
-      <Real Name="Z">-4.4618914487107055</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
@@ -54,9 +54,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.59103431303769127</Real>
-      <Real Name="Y">-0.089534785133402892</Real>
-      <Real Name="Z">-0.71108816991442636</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
index b86c84ae2dcad29e801fd9c5559b9aea41a5c8e2..b5d4abc796a5d1db28f6837d6c5f09d20ae35aab 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-14.41810852661005</Real>
-      <Real Name="Y">-17.90801189502286</Real>
-      <Real Name="Z">-6.5210258682908968</Real>
+      <Real Name="X">-6.5442370567032295</Real>
+      <Real Name="Y">-14.904960292549413</Real>
+      <Real Name="Z">2.4825316419739121</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 37334d2f560f8f26d76fbbe9719c35f3d2fd0ebd..028ca5fa40385fd72af10c157258daf5fe5b89c6 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-0.47656498169794198</Real>
-      <Real Name="Y">0.018603557416330907</Real>
-      <Real Name="Z">-5.22038445286704</Real>
+      <Real Name="X">-0.48671247601652962</Real>
+      <Real Name="Y">-2.0437887671248385</Real>
+      <Real Name="Z">-1.9489107480528729</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-5.9605112613345845</Real>
-      <Real Name="Y">-5.3075696454761232</Real>
-      <Real Name="Z">-16.415786280912666</Real>
+      <Real Name="X">-6.2648820267082685</Real>
+      <Real Name="Y">-1.5973923827320464</Real>
+      <Real Name="Z">1.9008625299783468</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 0ef2b80acd1bf5c4b2d9d813ef2a5727382f31b5..5634d4e8aa6d1d3066d6ed74c6dde0a259fc75a1 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">-7.2876727686084983</Real>
-      <Real Name="Y">-0.20511255643043858</Real>
-      <Real Name="Z">-2.5850277505756303</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-8.7877282837450235</Real>
-      <Real Name="Y">-20.671717390020454</Real>
-      <Real Name="Z">-23.739455166677537</Real>
+      <Real Name="X">-0.0488218353680136</Real>
+      <Real Name="Y">-1.8149737709615605</Real>
+      <Real Name="Z">0.30251804902903451</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.6291614930326499</Real>
-      <Real Name="Y">-2.4037889266291126</Real>
-      <Real Name="Z">-16.806428592827402</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-5.8805787410775725</Real>
-      <Real Name="Y">-16.635836507032653</Real>
-      <Real Name="Z">-3.6318914654000061</Real>
+      <Real Name="X">-1.1053036228996518</Real>
+      <Real Name="Y">-1.8065843426604078</Real>
+      <Real Name="Z">0.051190455240391619</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.30103432138234154</Real>
-      <Real Name="Y">0.010465216356713228</Real>
-      <Real Name="Z">-0.031088162761868987</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index f9540fbb9376adadc43d20dc13095bd4b4fd2b37..8df22aa42b501689e5d5379afe29f7627741831c 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-19.945252342067167</Real>
-      <Real Name="Y">-5.8550434537770943</Real>
-      <Real Name="Z">-10.097354831085514</Real>
+      <Real Name="X">0.55642797645760989</Real>
+      <Real Name="Y">0.3723331315116295</Real>
+      <Real Name="Z">1.6306575619901507</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index ab73bb477ea42fe5dd0baf8ca35dd7b421c96305..d1ecbf983fbd7034b439cdf6eebfee3d59ea415d 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">2.4399461929761004</Real>
-      <Real Name="Y">-1.2243385943090714</Real>
-      <Real Name="Z">0.41543174060820942</Real>
+      <Real Name="X">3.4320122274828666</Real>
+      <Real Name="Y">-0.81224418229071649</Real>
+      <Real Name="Z">-0.764761636476889</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 2de56c45b427850636c65c9d5c371b22cdd82f88..b754dcee9ef9a8394baccc1c26b2981a27fe12b2 100644 (file)
@@ -9,9 +9,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">9.1501846520219203</Real>
-      <Real Name="Y">3.6180027381322137</Real>
-      <Real Name="Z">6.3899759159287406</Real>
+      <Real Name="X">12.870589400646217</Real>
+      <Real Name="Y">2.4002360860135794</Real>
+      <Real Name="Z">-2.7526346195710181</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index fe0b3dc6c2bf86a7e5c788c028aa5301e87c021d..c28af077ffe9044da7abc27bf380264b718302e7 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">-2.7819333917763145</Real>
-      <Real Name="Y">-0.93764334824057782</Real>
-      <Real Name="Z">-0.29338315183533475</Real>
+      <Real Name="X">-3.9130491664545959</Real>
+      <Real Name="Y">-0.62204635074971815</Real>
+      <Real Name="Z">1.1633583220151211</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">2.9365563336064566</Real>
-      <Real Name="Y">-0.70046642949121796</Real>
-      <Real Name="Z">-12.032552000700495</Real>
+      <Real Name="X">4.1305407769409488</Real>
+      <Real Name="Y">-0.46469970389626269</Real>
+      <Real Name="Z">-3.6193285397896875</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.11357709995769247</Real>
-      <Real Name="Y">-1.112426659740996</Real>
-      <Real Name="Z">-3.4306764960600371</Real>
+      <Real Name="X">-0.1597567999404905</Real>
+      <Real Name="Y">-0.73800016335319685</Real>
+      <Real Name="Z">-0.54897393926970917</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
@@ -39,9 +39,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">1.5728939940946602</Real>
-      <Real Name="Y">-24.449483129724836</Real>
-      <Real Name="Z">3.8416981030419435</Real>
+      <Real Name="X">2.2124223213639174</Real>
+      <Real Name="Y">-16.220145737822616</Real>
+      <Real Name="Z">2.8258755564357965</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
index e5b27b0c595afb309326820692c2aec9651de11d..a5acad9c82bfafb2c57bd13b073447de63f84ac4 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">2.4599461925290655</Real>
-      <Real Name="Y">-0.35433858954069986</Real>
-      <Real Name="Z">1.3654317286872804</Real>
+      <Real Name="X">-16.500695458472013</Real>
+      <Real Name="Y">-15.572010667113076</Real>
+      <Real Name="Z">6.0618364657657224</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index d360799e74feccda40d444df22247a43c3449ca5..63f2ba6eaa596498b4ef9f417375fb3d6631bddb 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
+      <Real Name="X">-0.56750283571532234</Real>
+      <Real Name="Y">-0.70603611451836135</Real>
+      <Real Name="Z">-0.73298662849655538</Real>
     </Vector>
     <Vector>
-      <Real Name="X">9.810184678247964</Real>
-      <Real Name="Y">4.2880027548215143</Real>
-      <Real Name="Z">6.769975911160369</Real>
+      <Real Name="X">-7.5662986143550031</Real>
+      <Real Name="Y">-4.9570092424793035</Real>
+      <Real Name="Z">0.22987632186540616</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 205fd7d23e62574116e8fe95d9db8f23ad624d1b..d33540d324494cea30b5373d7446ef8a35a325be 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">-2.7619333922233493</Real>
-      <Real Name="Y">-0.067643343472206241</Real>
-      <Real Name="Z">0.65661683624373635</Real>
+      <Real Name="X">-8.3516260207559565</Real>
+      <Real Name="Y">-0.89155680381272195</Real>
+      <Real Name="Z">1.9626024243687374</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">3.7665563169171561</Real>
-      <Real Name="Y">-0.39046642710703217</Real>
-      <Real Name="Z">-11.302551981627008</Real>
+      <Real Name="X">-10.991689448063681</Real>
+      <Real Name="Y">-17.399473852314976</Real>
+      <Real Name="Z">1.9943402280115583</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.59642287858463539</Real>
-      <Real Name="Y">-1.0524266610821005</Real>
-      <Real Name="Z">-3.0806765020205016</Real>
+      <Real Name="X">-1.5304702532285459</Real>
+      <Real Name="Y">-2.0431421404374799</Real>
+      <Real Name="Z">-2.0385186389652117</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">1.8428940048234963</Real>
-      <Real Name="Y">-23.46948311065135</Real>
-      <Real Name="Z">4.6716980863526434</Real>
+      <Real Name="X">-7.0292328592073234</Real>
+      <Real Name="Y">-14.608255429729208</Real>
+      <Real Name="Z">3.679921612389506</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
+      <Real Name="X">-0.67546778632879001</Real>
+      <Real Name="Y">-0.07424836221296291</Real>
+      <Real Name="Z">0.089381774901316297</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 86c8e779743edf9bb37c149c832336c94a65fd65..330c2a879fb63ffedc68fc9df95be7b92e155ee7 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-11.73096317750745</Real>
-      <Real Name="Y">-23.472514874739037</Real>
-      <Real Name="Z">-5.870091744290689</Real>
+      <Real Name="X">2.788509934829829</Real>
+      <Real Name="Y">-1.0153052278633954</Real>
+      <Real Name="Z">-0.5654353231810999</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index ce158bbce5efd8ad4811f0b3a5855eee4b09ce83..dd4f51b9d7fcd2ef80157dd7cd337d38cb212bb4 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-3.0565076595646632</Real>
-      <Real Name="Y">-1.4103500790664754</Real>
-      <Real Name="Z">-4.9677026084105016</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-5.3791654211430107</Real>
-      <Real Name="Y">-7.4719620777067792</Real>
-      <Real Name="Z">-13.196689216970519</Real>
+      <Real Name="X">10.457353888025052</Real>
+      <Real Name="Y">3.0002951075169739</Real>
+      <Real Name="Z">-2.4443752014337479</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 7968b98e6476676df08b5642810f4ebb6f2e3a45..775c0fdfec9ab8956653c266896472a62e11e4bd 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">-5.9374841241311893</Real>
-      <Real Name="Y">-1.3438907014985126</Real>
-      <Real Name="Z">-2.7775217946572655</Real>
+      <Real Name="X">-3.1793524477443595</Real>
+      <Real Name="Y">-0.77755793843714771</Real>
+      <Real Name="Z">0.99148224011632069</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-7.8144042169827745</Real>
-      <Real Name="Y">-26.227146740505805</Real>
-      <Real Name="Z">-19.226000503090088</Real>
+      <Real Name="X">3.3560643812645212</Real>
+      <Real Name="Y">-0.58087462987032856</Real>
+      <Real Name="Z">-2.8401295588358915</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-1.0880686956546695</Real>
-      <Real Name="Y">-3.0797361566100099</Real>
-      <Real Name="Z">-13.480051032538309</Real>
+      <Real Name="X">-0.12980239995164852</Real>
+      <Real Name="Y">-0.92250020419149636</Real>
+      <Real Name="Z">-0.37393805370732247</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
@@ -39,9 +39,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-4.9973452358427064</Real>
-      <Real Name="Y">-22.019795657632674</Real>
-      <Real Name="Z">-3.5057718525584116</Real>
+      <Real Name="X">1.7975931361081829</Real>
+      <Real Name="Y">-20.275182172278267</Real>
+      <Real Name="Z">3.4378840352447644</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
@@ -54,9 +54,9 @@
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.48021537934312419</Real>
-      <Real Name="Y">-0.1119184814167536</Real>
-      <Real Name="Z">-0.55871213350419213</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
index 454e02b535b978de8e115855ccf53025b9c36237..354d15b0b32035c2c0292e8bdabe34afd5a639ab 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-11.710963177954484</Real>
-      <Real Name="Y">-22.602514869970666</Real>
-      <Real Name="Z">-4.920091756211618</Real>
+      <Real Name="X">-13.406815060008512</Real>
+      <Real Name="Y">-19.465013333891342</Real>
+      <Real Name="Z">6.0748932006495915</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index a985a7b5bb8adc1518310cc919cc00dea778d376..f0a9eef513b399272a74fdac8ec5529c46781244 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-4.2562713131673355</Real>
-      <Real Name="Y">-3.1975379801333257</Real>
-      <Real Name="Z">-12.696263461962817</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.083636781240502847</Real>
-      <Real Name="Y">-0.683594730203958</Real>
-      <Real Name="Z">-6.5236686696981163</Real>
+      <Real Name="X">0.44047122348806367</Real>
+      <Real Name="Y">0.3871114065335613</Real>
+      <Real Name="Z">2.3221153764227829</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 049d03812df84b871cef242d7137097c8117b307..53b7b71eeebb425ed848c69ff4f9615ce2a4e197 100644 (file)
@@ -4,14 +4,14 @@
   <Sequence Name="Forces">
     <Int Name="Length">2</Int>
     <Vector>
-      <Real Name="X">-3.036507660011698</Real>
-      <Real Name="Y">-0.54035007429810378</Real>
-      <Real Name="Z">-4.0177026203314306</Real>
+      <Real Name="X">-3.4931516109310436</Real>
+      <Real Name="Y">-1.1695586621622649</Real>
+      <Real Name="Z">0.37948711624127879</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-4.719165394916967</Real>
-      <Real Name="Y">-6.8019620610174787</Real>
-      <Real Name="Z">-12.816689221738891</Real>
+      <Real Name="X">-6.1476176241634404</Real>
+      <Real Name="Y">-6.196261553099129</Real>
+      <Real Name="Z">0.61604971101077255</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
index 5b6e408b522401e0df747403f3034b683804e71a..5f89638d2fb16f1cf9459a5c7bcac27af70c4a71 100644 (file)
@@ -4,69 +4,69 @@
   <Sequence Name="Forces">
     <Int Name="Length">13</Int>
     <Vector>
-      <Real Name="X">-5.9174841245782241</Real>
-      <Real Name="Y">-0.47389069673014106</Real>
-      <Real Name="Z">-1.8275218065781944</Real>
+      <Real Name="X">-6.7856961418642161</Real>
+      <Real Name="Y">-1.1144460047659024</Real>
+      <Real Name="Z">1.6740807570530818</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-6.9844042336720751</Real>
-      <Real Name="Y">-25.91714673812162</Real>
-      <Real Name="Z">-18.496000484016601</Real>
+      <Real Name="X">-8.9307476765517411</Real>
+      <Real Name="Y">-21.749342315393726</Real>
+      <Real Name="Z">2.9757623819770487</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.37806871711234158</Real>
-      <Real Name="Y">-3.0197361579511144</Real>
-      <Real Name="Z">-13.130051038498774</Real>
+      <Real Name="X">-1.2435070807481936</Real>
+      <Real Name="Y">-2.5539276755468499</Real>
+      <Real Name="Z">-1.4344239512261392</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-4.7273452251138703</Real>
-      <Real Name="Y">-21.039795638559188</Real>
-      <Real Name="Z">-2.6757718692477122</Real>
+      <Real Name="X">-5.7112516981059507</Real>
+      <Real Name="Y">-18.260319287161511</Real>
+      <Real Name="Z">4.0572521792161034</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">-0.19021538768777446</Real>
-      <Real Name="Y">-0.011918479926637485</Real>
-      <Real Name="Z">0.12128787364836524</Real>
+      <Real Name="X">-0.5488175763921419</Real>
+      <Real Name="Y">-0.092810452766203616</Real>
+      <Real Name="Z">0.081070341914709868</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
+      <Real Name="X">0</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_72.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_72.xml
deleted file mode 100644 (file)
index 571aef3..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">0.78266792292938536</Real>
-      <Real Name="Y">0.24701126089639641</Real>
-      <Real Name="Z">0.075197087339731225</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_73.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_73.xml
deleted file mode 100644 (file)
index f59a297..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.61956391875244121</Real>
-      <Real Name="Y">0.25681538531643089</Real>
-      <Real Name="Z">0.264650385589911</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_74.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_74.xml
deleted file mode 100644 (file)
index 831e529..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-11.475149109556806</Real>
-      <Real Name="Y">-9.7544516427343577</Real>
-      <Real Name="Z">3.0247422457903341</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-11.858918281399532</Real>
-      <Real Name="Y">-7.4199289605932677</Real>
-      <Real Name="Z">3.011716132619819</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_75.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_75.xml
deleted file mode 100644 (file)
index fffc562..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">0.80266792248235053</Real>
-      <Real Name="Y">1.1170112656647679</Real>
-      <Real Name="Z">1.0251970754188022</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_76.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_76.xml
deleted file mode 100644 (file)
index a88b4e1..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">1.2795639449784848</Real>
-      <Real Name="Y">0.92681540200573143</Real>
-      <Real Name="Z">0.64465038082153936</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_77.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_77.xml
deleted file mode 100644 (file)
index f57b0cd..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-10.645149126246107</Real>
-      <Real Name="Y">-9.4444516403501719</Real>
-      <Real Name="Z">3.7547422648638205</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-11.148918302857204</Real>
-      <Real Name="Y">-7.3599289619343722</Real>
-      <Real Name="Z">3.3617161266593545</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.27000001072883606</Real>
-      <Real Name="Y">0.98000001907348633</Real>
-      <Real Name="Z">0.82999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_78.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_78.xml
deleted file mode 100644 (file)
index 2b89260..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-22.79457410521962</Real>
-      <Real Name="Y">-4.8554021376267826</Real>
-      <Real Name="Z">5.6800905790772545</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_79.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_79.xml
deleted file mode 100644 (file)
index 7b95007..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-4.8643100721912402</Real>
-      <Real Name="Y">-2.6516169976273649</Real>
-      <Real Name="Z">-0.25116456753339361</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.09558489284628896</Real>
-      <Real Name="Y">-0.56688346388984168</Real>
-      <Real Name="Z">-0.94910028423615156</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
index e537a3868004aec396d670133f38248057360a80..e5582ccc2ea98719234347b4d60270d34d483576 100644 (file)
       <Real Name="Z">0</Real>
     </Vector>
     <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
+      <Real Name="X">-8.1581138200755436</Real>
+      <Real Name="Y">-14.70340061881376</Real>
+      <Real Name="Z">-8.9453847792568535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-8.4309497156824804</Real>
+      <Real Name="Y">-11.184451168201075</Real>
+      <Real Name="Z">-7.7012318674352152</Real>
     </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Y">0</Real>
       <Real Name="Z">0</Real>
     </Vector>
-    <Vector>
-      <Real Name="X">-3.5826533245421075</Real>
-      <Real Name="Y">-9.5803366494364646</Real>
-      <Real Name="Z">-23.575529237696852</Real>
-    </Vector>
     <Vector>
       <Real Name="X">0</Real>
       <Real Name="Y">0</Real>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_81.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_81.xml
deleted file mode 100644 (file)
index 90ff66e..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-22.774574105666655</Real>
-      <Real Name="Y">-3.9854021328584111</Real>
-      <Real Name="Z">6.6300905671563255</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_82.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_82.xml
deleted file mode 100644 (file)
index 161ac2b..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-4.8443100726382751</Real>
-      <Real Name="Y">-1.7816169928589933</Real>
-      <Real Name="Z">0.69883542054567749</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.56441513337975469</Real>
-      <Real Name="Y">0.10311655279945886</Real>
-      <Real Name="Z">-0.56910028900452314</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_83.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_83.xml
deleted file mode 100644 (file)
index 5b85cce..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.82999998331069946</Real>
-      <Real Name="Y">0.31000000238418579</Real>
-      <Real Name="Z">0.73000001907348633</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.70999997854232788</Real>
-      <Real Name="Y">0.059999998658895493</Real>
-      <Real Name="Z">0.34999999403953552</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-3.8244609316050013</Real>
-      <Real Name="Y">-6.964669805170133</Real>
-      <Real Name="Z">-0.5577955735285649</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_84.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_84.xml
deleted file mode 100644 (file)
index 5f59624..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">0.63591768738012566</Real>
-      <Real Name="Y">0.3087640761204955</Real>
-      <Real Name="Z">0.03426844856107035</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_85.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_85.xml
deleted file mode 100644 (file)
index ef5c4c2..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.50339568398635848</Real>
-      <Real Name="Y">0.32101923164553864</Real>
-      <Real Name="Z">0.18363175660878781</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_86.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_86.xml
deleted file mode 100644 (file)
index ef02889..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-9.3235586515149063</Real>
-      <Real Name="Y">-12.193064553417946</Real>
-      <Real Name="Z">3.2071979869138385</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-9.6353711036371195</Real>
-      <Real Name="Y">-9.2749112007415846</Real>
-      <Real Name="Z">3.0222260742447977</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_87.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_87.xml
deleted file mode 100644 (file)
index 5a48768..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">0.65591768693309083</Real>
-      <Real Name="Y">1.1787640808888671</Real>
-      <Real Name="Z">0.98426843664014141</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_88.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_88.xml
deleted file mode 100644 (file)
index 1cc01a9..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">1.1633957102124022</Real>
-      <Real Name="Y">0.99101924833483923</Real>
-      <Real Name="Z">0.56363175184041625</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_89.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_89.xml
deleted file mode 100644 (file)
index 061bf0f..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-8.4935586682042068</Real>
-      <Real Name="Y">-11.88306455103376</Real>
-      <Real Name="Z">3.9371980059873248</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-8.9253711250947916</Real>
-      <Real Name="Y">-9.2149112020826891</Real>
-      <Real Name="Z">3.3722260682843332</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.27000001072883606</Real>
-      <Real Name="Y">0.98000001907348633</Real>
-      <Real Name="Z">0.82999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
index 7759abfe05ed0acff288eebc51a539959518c710..c634fc3ec6b8fa462e84e0fe1767545a83359aca 100644 (file)
@@ -4,9 +4,9 @@
   <Sequence Name="Forces">
     <Int Name="Length">1</Int>
     <Vector>
-      <Real Name="X">-19.925252342514202</Real>
-      <Real Name="Y">-4.9850434490087228</Real>
-      <Real Name="Z">-9.1473548430064433</Real>
+      <Real Name="X">-16.205517527929572</Real>
+      <Real Name="Y">-7.3188043172213684</Real>
+      <Real Name="Z">-7.9336359387100472</Real>
     </Vector>
   </Sequence>
 </ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_90.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_90.xml
deleted file mode 100644 (file)
index 759360d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-18.520591460490941</Real>
-      <Real Name="Y">-6.0692526720334774</Real>
-      <Real Name="Z">5.0076479125818185</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_91.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_91.xml
deleted file mode 100644 (file)
index e86b6ed..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-3.9522519336553823</Real>
-      <Real Name="Y">-3.3145212470342056</Real>
-      <Real Name="Z">0.041856812732197594</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-0.077662725437609784</Real>
-      <Real Name="Y">-0.70860432986230215</Real>
-      <Real Name="Z">-0.70184026829559087</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_92.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_92.xml
deleted file mode 100644 (file)
index 993c18d..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-3.326749515646243</Real>
-      <Real Name="Y">-9.9308372803045231</Real>
-      <Real Name="Z">-0.45425921737202102</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_93.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_93.xml
deleted file mode 100644 (file)
index cd4597a..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-18.500591460937976</Real>
-      <Real Name="Y">-5.1992526672651058</Real>
-      <Real Name="Z">5.9576479006608896</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_94.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_94.xml
deleted file mode 100644 (file)
index 36ced80..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">-3.9322519341024171</Real>
-      <Real Name="Y">-2.444521242265834</Real>
-      <Real Name="Z">0.99185680081126859</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.58233730078843393</Real>
-      <Real Name="Y">-0.038604313173001614</Real>
-      <Real Name="Z">-0.32184027306396246</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_95.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_95.xml
deleted file mode 100644 (file)
index 59d83c3..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0.019999999552965164</Real>
-      <Real Name="Y">0.87000000476837158</Real>
-      <Real Name="Z">0.94999998807907104</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.6600000262260437</Real>
-      <Real Name="Y">0.67000001668930054</Real>
-      <Real Name="Z">0.37999999523162842</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.44999998807907104</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.93999999761581421</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.54000002145767212</Real>
-      <Real Name="Y">0.75999999046325684</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.82999998331069946</Real>
-      <Real Name="Y">0.31000000238418579</Real>
-      <Real Name="Z">0.73000001907348633</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.70999997854232788</Real>
-      <Real Name="Y">0.059999998658895493</Real>
-      <Real Name="Z">0.34999999403953552</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.31999999284744263</Real>
-      <Real Name="Y">0.34999999403953552</Real>
-      <Real Name="Z">0.61000001430511475</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-3.0567495049174069</Real>
-      <Real Name="Y">-8.9508372612310367</Real>
-      <Real Name="Z">0.37574076593867844</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.10999999940395355</Real>
-      <Real Name="Y">0.30000001192092896</Real>
-      <Real Name="Z">0.41999998688697815</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.94999998807907104</Real>
-      <Real Name="Y">0.68999999761581421</Real>
-      <Real Name="Z">0.57999998331069946</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.28999999165534973</Real>
-      <Real Name="Y">0.10000000149011612</Real>
-      <Real Name="Z">0.68000000715255737</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.93999999761581421</Real>
-      <Real Name="Y">0.62000000476837158</Real>
-      <Real Name="Z">0.50999999046325684</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0.4699999988079071</Real>
-      <Real Name="Y">0.039999999105930328</Real>
-      <Real Name="Z">0.4699999988079071</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_96.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_96.xml
deleted file mode 100644 (file)
index 50244a9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-11.577102289706572</Real>
-      <Real Name="Y">0.84910120894795005</Real>
-      <Real Name="Z">1.8482439089193421</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_97.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_97.xml
deleted file mode 100644 (file)
index 7bc4c48..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">2</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">2.0340570782897585</Real>
-      <Real Name="Y">1.4319189829320136</Real>
-      <Real Name="Z">-0.59251856152304705</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_98.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_98.xml
deleted file mode 100644 (file)
index f60869d..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">13</Int>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-1.0210446166718394</Real>
-      <Real Name="Y">-16.393563534893939</Real>
-      <Real Name="Z">2.0765205102067403</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">-3.3498355821816856</Real>
-      <Real Name="Y">-0.97018032158412315</Real>
-      <Real Name="Z">0.58368499271815877</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-    <Vector>
-      <Real Name="X">0</Real>
-      <Real Name="Y">0</Real>
-      <Real Name="Z">0</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
diff --git a/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_99.xml b/src/gromacs/ewald/tests/refdata/SaneInput_PmeGatherTest_ReproducesOutputs_99.xml
deleted file mode 100644 (file)
index ef42b55..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <Sequence Name="Forces">
-    <Int Name="Length">1</Int>
-    <Vector>
-      <Real Name="X">-11.557102290153606</Real>
-      <Real Name="Y">1.7191012137163217</Real>
-      <Real Name="Z">2.7982438969984131</Real>
-    </Vector>
-  </Sequence>
-</ReferenceData>
index 6817cb0890e2182a501070cc443650dd3dd59549..8d293464b7b069a005aecb843a6689d8fbcfcc4f 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index c1bb1d90a5beb7394870a8f2f2585fe571c6a346..5f20a8dee80ce19e86a7aba2fcf8b7d82951cc19 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 62526196ffce98a67627f4e37d96135b110e6ad4..75b30408b2048d35cbcdae235395d4795460a08d 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 1991-2003 Erik Lindahl, David van der Spoel, University of Groningen.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 29618ee086aadf4bd269dec351bdc0542fa9f892..e01d28027b527bac7492abfb37e25badf4383483 100644 (file)
@@ -431,7 +431,7 @@ fft5d_plan fft5d_plan_3d(int                NG,
     if (!(flags & FFT5D_NOMALLOC))
     {
         // only needed for PME GPU mixed mode
-        if (realGridAllocationPinningPolicy == gmx::PinningPolicy::PinnedIfSupported && GMX_GPU == GMX_GPU_CUDA)
+        if (GMX_GPU_CUDA && realGridAllocationPinningPolicy == gmx::PinningPolicy::PinnedIfSupported)
         {
             const std::size_t numBytes = lsize * sizeof(t_complex);
             lin = static_cast<t_complex*>(gmx::PageAlignedAllocationPolicy::malloc(numBytes));
index 49615f9ec0079175675b28d613f32c9f3f46a092..102b41446cd488ab4d8369445a188e750f3432e8 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 1991-2003 David van der Spoel, Erik Lindahl, University of Groningen.
- * Copyright (c) 2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7dc332f6070ec63e5811f07c3c8bc911ec84ff7e..33c9b8c1841a904ede1190da3a9f2aada1d5a27e 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 1991-2003 David van der Spoel, Erik Lindahl, University of Groningen.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index abf1ae038f3417bc43597abc34f88e12b3f69c85..a64a7574c563af359288cabea32cf1276de8bbd2 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 1991-2003 David van der Spoel, Erik Lindahl, University of Groningen.
- * Copyright (c) 2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -115,14 +115,14 @@ int gmx_fft_init_1d(gmx_fft_t* pfft, int nx, gmx_fft_flag gmx_unused flags)
     int       d;
     int       status;
 
-    if (pfft == NULL)
+    if (pfft == nullptr)
     {
         gmx_fatal(FARGS, "Invalid opaque FFT datatype pointer.");
         return EINVAL;
     }
-    *pfft = NULL;
+    *pfft = nullptr;
 
-    if ((fft = (gmx_fft_t)malloc(sizeof(struct gmx_fft))) == NULL)
+    if ((fft = (gmx_fft_t)malloc(sizeof(struct gmx_fft))) == nullptr)
     {
         return ENOMEM;
     }
@@ -130,9 +130,9 @@ int gmx_fft_init_1d(gmx_fft_t* pfft, int nx, gmx_fft_flag gmx_unused flags)
     /* Mark all handles invalid */
     for (d = 0; d < 3; d++)
     {
-        fft->inplace[d] = fft->ooplace[d] = NULL;
+        fft->inplace[d] = fft->ooplace[d] = nullptr;
     }
-    fft->ooplace[3] = NULL;
+    fft->ooplace[3] = nullptr;
 
 
     status = DftiCreateDescriptor(&fft->inplace[0], GMX_DFTI_PREC, DFTI_COMPLEX, 1, (MKL_LONG)nx);
@@ -167,14 +167,12 @@ int gmx_fft_init_1d(gmx_fft_t* pfft, int nx, gmx_fft_flag gmx_unused flags)
     if (status != 0)
     {
         gmx_fatal(FARGS, "Error initializing Intel MKL FFT; status=%d", status);
-        gmx_fft_destroy(fft);
-        return status;
     }
 
     fft->ndim     = 1;
     fft->nx       = nx;
     fft->real_fft = 0;
-    fft->work     = NULL;
+    fft->work     = nullptr;
 
     *pfft = fft;
     return 0;
@@ -187,14 +185,14 @@ int gmx_fft_init_1d_real(gmx_fft_t* pfft, int nx, gmx_fft_flag gmx_unused flags)
     int       d;
     int       status;
 
-    if (pfft == NULL)
+    if (pfft == nullptr)
     {
         gmx_fatal(FARGS, "Invalid opaque FFT datatype pointer.");
         return EINVAL;
     }
-    *pfft = NULL;
+    *pfft = nullptr;
 
-    if ((fft = (gmx_fft_t)malloc(sizeof(struct gmx_fft))) == NULL)
+    if ((fft = (gmx_fft_t)malloc(sizeof(struct gmx_fft))) == nullptr)
     {
         return ENOMEM;
     }
@@ -202,9 +200,9 @@ int gmx_fft_init_1d_real(gmx_fft_t* pfft, int nx, gmx_fft_flag gmx_unused flags)
     /* Mark all handles invalid */
     for (d = 0; d < 3; d++)
     {
-        fft->inplace[d] = fft->ooplace[d] = NULL;
+        fft->inplace[d] = fft->ooplace[d] = nullptr;
     }
-    fft->ooplace[3] = NULL;
+    fft->ooplace[3] = nullptr;
 
     status = DftiCreateDescriptor(&fft->inplace[0], GMX_DFTI_PREC, DFTI_REAL, 1, (MKL_LONG)nx);
 
@@ -238,22 +236,18 @@ int gmx_fft_init_1d_real(gmx_fft_t* pfft, int nx, gmx_fft_flag gmx_unused flags)
     if (status == DFTI_UNIMPLEMENTED)
     {
         gmx_fatal(FARGS, "The linked Intel MKL version (<6.0?) cannot do real FFTs.");
-        gmx_fft_destroy(fft);
-        return status;
     }
 
 
     if (status != 0)
     {
         gmx_fatal(FARGS, "Error initializing Intel MKL FFT; status=%d", status);
-        gmx_fft_destroy(fft);
-        return status;
     }
 
     fft->ndim     = 1;
     fft->nx       = nx;
     fft->real_fft = 1;
-    fft->work     = NULL;
+    fft->work     = nullptr;
 
     *pfft = fft;
     return 0;
@@ -268,14 +262,14 @@ int gmx_fft_init_2d_real(gmx_fft_t* pfft, int nx, int ny, gmx_fft_flag gmx_unuse
     MKL_LONG  stride[2];
     MKL_LONG  nyc;
 
-    if (pfft == NULL)
+    if (pfft == nullptr)
     {
         gmx_fatal(FARGS, "Invalid opaque FFT datatype pointer.");
         return EINVAL;
     }
-    *pfft = NULL;
+    *pfft = nullptr;
 
-    if ((fft = (gmx_fft_t)malloc(sizeof(struct gmx_fft))) == NULL)
+    if ((fft = (gmx_fft_t)malloc(sizeof(struct gmx_fft))) == nullptr)
     {
         return ENOMEM;
     }
@@ -285,9 +279,9 @@ int gmx_fft_init_2d_real(gmx_fft_t* pfft, int nx, int ny, gmx_fft_flag gmx_unuse
     /* Mark all handles invalid */
     for (d = 0; d < 3; d++)
     {
-        fft->inplace[d] = fft->ooplace[d] = NULL;
+        fft->inplace[d] = fft->ooplace[d] = nullptr;
     }
-    fft->ooplace[3] = NULL;
+    fft->ooplace[3] = nullptr;
 
     /* Roll our own 2D real transform using multiple transforms in MKL,
      * since the current MKL versions does not support our storage format,
@@ -416,8 +410,6 @@ int gmx_fft_init_2d_real(gmx_fft_t* pfft, int nx, int ny, gmx_fft_flag gmx_unuse
     if (status != 0)
     {
         gmx_fatal(FARGS, "Error initializing Intel MKL FFT; status=%d", status);
-        gmx_fft_destroy(fft);
-        return status;
     }
 
     fft->ndim     = 2;
@@ -466,7 +458,6 @@ int gmx_fft_1d(gmx_fft_t fft, enum gmx_fft_direction dir, void* in_data, void* o
     if (status != 0)
     {
         gmx_fatal(FARGS, "Error executing Intel MKL FFT.");
-        status = -1;
     }
 
     return status;
@@ -511,7 +502,6 @@ int gmx_fft_1d_real(gmx_fft_t fft, enum gmx_fft_direction dir, void* in_data, vo
     if (status != 0)
     {
         gmx_fatal(FARGS, "Error executing Intel MKL FFT.");
-        status = -1;
     }
 
     return status;
@@ -527,7 +517,6 @@ int gmx_fft_2d_real(gmx_fft_t fft, enum gmx_fft_direction dir, void* in_data, vo
         || ((dir != GMX_FFT_REAL_TO_COMPLEX) && (dir != GMX_FFT_COMPLEX_TO_REAL)))
     {
         gmx_fatal(FARGS, "FFT plan mismatch - bad plan or direction.");
-        return EINVAL;
     }
 
     if (dir == GMX_FFT_REAL_TO_COMPLEX)
@@ -564,7 +553,6 @@ int gmx_fft_2d_real(gmx_fft_t fft, enum gmx_fft_direction dir, void* in_data, vo
     if (status != 0)
     {
         gmx_fatal(FARGS, "Error executing Intel MKL FFT.");
-        status = -1;
     }
 
     return status;
@@ -574,24 +562,24 @@ void gmx_fft_destroy(gmx_fft_t fft)
 {
     int d;
 
-    if (fft != NULL)
+    if (fft != nullptr)
     {
         for (d = 0; d < 3; d++)
         {
-            if (fft->inplace[d] != NULL)
+            if (fft->inplace[d] != nullptr)
             {
                 DftiFreeDescriptor(&fft->inplace[d]);
             }
-            if (fft->ooplace[d] != NULL)
+            if (fft->ooplace[d] != nullptr)
             {
                 DftiFreeDescriptor(&fft->ooplace[d]);
             }
         }
-        if (fft->ooplace[3] != NULL)
+        if (fft->ooplace[3] != nullptr)
         {
             DftiFreeDescriptor(&fft->ooplace[3]);
         }
-        if (fft->work != NULL)
+        if (fft->work != nullptr)
         {
             free(fft->work);
         }
index c7082cc657116cfe970f56ee740b6bb7bac62343..4a6e1dd848967c4c37003052cc8867eab4ad43a1 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,4 +33,6 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(FFTUnitTests fft-test
-                  fft.cpp)
+    CPP_SOURCE_FILES
+        fft.cpp
+    )
index 4fb26f2532065f72f5b697cb7f111c745fb3ac22..f422241309002388ef9fbf90d4fd41913feef507 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 471da9c5406da577f44536bfc2b8bade49da4770..f0e81bb03cd50c4434b9afc32eaf3138b0fd7053 100644 (file)
@@ -34,8 +34,6 @@
 
 file(GLOB FILEIO_SOURCES *.cpp)
 
-target_sources(libgromacs PRIVATE ${FILEIO_SOURCES})
-
 if(GMX_USE_PLUGINS)
     add_library(vmddlopen OBJECT ${CMAKE_SOURCE_DIR}/src/external/vmd_molfile/vmddlopen.cpp)
     gmx_target_compile_options(vmddlopen)
@@ -44,8 +42,13 @@ if(GMX_USE_PLUGINS)
     endif()
     list(APPEND libgromacs_object_library_dependencies vmddlopen)
     set(libgromacs_object_library_dependencies ${libgromacs_object_library_dependencies} PARENT_SCOPE)
+else()
+    # Remove vmdio.cpp from sources since we do not have plugin support
+    list(FILTER FILEIO_SOURCES EXCLUDE REGEX ".*vmdio.cpp$")
 endif()
 
+target_sources(libgromacs PRIVATE ${FILEIO_SOURCES})
+
 if(GMX_INSTALL_LEGACY_API)
   install(FILES
           oenv.h
index 9c6cfe42135b6bf4a7e8c98af3786d7abeed11ea..abd957f09a5fe1a30a48f7f5f2fb9a3d63784942 100644 (file)
@@ -41,8 +41,6 @@
 
 #include "checkpoint.h"
 
-#include "config.h"
-
 #include <cerrno>
 #include <cstdlib>
 #include <cstring>
@@ -62,6 +60,7 @@
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdtypes/awh_correlation_history.h"
 #include "gromacs/mdtypes/awh_history.h"
+#include "gromacs/mdtypes/checkpointdata.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/df_history.h"
 #include "gromacs/mdtypes/edsamhistory.h"
 #define CPT_MAGIC1 171817
 #define CPT_MAGIC2 171819
 
+namespace gmx
+{
+
+template<typename ValueType>
+void readKvtCheckpointValue(compat::not_null<ValueType*> value,
+                            const std::string&           name,
+                            const std::string&           identifier,
+                            const KeyValueTreeObject&    kvt)
+{
+    const std::string key = identifier + "-" + name;
+    if (!kvt.keyExists(key))
+    {
+        std::string errorMessage = "Cannot read requested checkpoint value " + key + " .";
+        GMX_THROW(InternalError(errorMessage));
+    }
+    *value = kvt[key].cast<ValueType>();
+}
+
+template void readKvtCheckpointValue(compat::not_null<std::int64_t*> value,
+                                     const std::string&              name,
+                                     const std::string&              identifier,
+                                     const KeyValueTreeObject&       kvt);
+template void readKvtCheckpointValue(compat::not_null<real*>   value,
+                                     const std::string&        name,
+                                     const std::string&        identifier,
+                                     const KeyValueTreeObject& kvt);
+
+template<typename ValueType>
+void writeKvtCheckpointValue(const ValueType&          value,
+                             const std::string&        name,
+                             const std::string&        identifier,
+                             KeyValueTreeObjectBuilder kvtBuilder)
+{
+    kvtBuilder.addValue<ValueType>(identifier + "-" + name, value);
+}
+
+template void writeKvtCheckpointValue(const std::int64_t&       value,
+                                      const std::string&        name,
+                                      const std::string&        identifier,
+                                      KeyValueTreeObjectBuilder kvtBuilder);
+template void writeKvtCheckpointValue(const real&               value,
+                                      const std::string&        name,
+                                      const std::string&        identifier,
+                                      KeyValueTreeObjectBuilder kvtBuilder);
+
+
+} // namespace gmx
+
 /*! \brief Enum of values that describe the contents of a cpt file
  * whose format matches a version number
  *
@@ -107,9 +154,10 @@ enum cptv
     cptv_Unknown = 17,                  /**< Version before numbering scheme */
     cptv_RemoveBuildMachineInformation, /**< remove functionality that makes mdrun builds non-reproducible */
     cptv_ComPrevStepAsPullGroupReference, /**< Allow using COM of previous step as pull group PBC reference */
-    cptv_PullAverage, /**< Added possibility to output average pull force and position */
-    cptv_MdModules,   /**< Added checkpointing for MdModules */
-    cptv_Count        /**< the total number of cptv versions */
+    cptv_PullAverage,      /**< Added possibility to output average pull force and position */
+    cptv_MdModules,        /**< Added checkpointing for MdModules */
+    cptv_ModularSimulator, /**< Added checkpointing for modular simulator */
+    cptv_Count             /**< the total number of cptv versions */
 };
 
 /*! \brief Version number of the file format written to checkpoint
@@ -1178,6 +1226,16 @@ static void do_cpt_header(XDR* xd, gmx_bool bRead, FILE* list, CheckpointHeaderC
     {
         contents->flagsPullHistory = 0;
     }
+
+    if (contents->file_version >= cptv_ModularSimulator)
+    {
+        do_cpt_bool_err(xd, "Is modular simulator checkpoint",
+                        &contents->isModularSimulatorCheckpoint, list);
+    }
+    else
+    {
+        contents->isModularSimulatorCheckpoint = false;
+    }
 }
 
 static int do_cpt_footer(XDR* xd, int file_version)
@@ -1268,16 +1326,11 @@ static int do_cpt_state(XDR* xd, int fflags, t_state* state, FILE* list)
                     break;
                 /* The RNG entries are no longer written,
                  * the next 4 lines are only for reading old files.
+                 * It's OK that three case statements fall through.
                  */
                 case estLD_RNG_NOTSUPPORTED:
-                    ret = do_cpte_ints(xd, part, i, sflags, 0, nullptr, list);
-                    break;
                 case estLD_RNGI_NOTSUPPORTED:
-                    ret = do_cpte_ints(xd, part, i, sflags, 0, nullptr, list);
-                    break;
                 case estMC_RNG_NOTSUPPORTED:
-                    ret = do_cpte_ints(xd, part, i, sflags, 0, nullptr, list);
-                    break;
                 case estMC_RNGI_NOTSUPPORTED:
                     ret = do_cpte_ints(xd, part, i, sflags, 0, nullptr, list);
                     break;
@@ -2085,7 +2138,7 @@ static void do_cpt_mdmodules(int                           fileVersion,
         gmx::MdModulesCheckpointReadingDataOnMaster mdModuleCheckpointReadingDataOnMaster = {
             mdModuleCheckpointParameterTree, fileVersion
         };
-        mdModulesNotifier.notifier_.notify(mdModuleCheckpointReadingDataOnMaster);
+        mdModulesNotifier.checkpointingNotifications_.notify(mdModuleCheckpointReadingDataOnMaster);
     }
 }
 
@@ -2173,211 +2226,103 @@ static int do_cpt_files(XDR* xd, gmx_bool bRead, std::vector<gmx_file_position_t
     return 0;
 }
 
-static void mpiBarrierBeforeRename(const bool applyMpiBarrierBeforeRename, MPI_Comm mpiBarrierCommunicator)
-{
-    if (applyMpiBarrierBeforeRename)
-    {
-#if GMX_MPI
-        MPI_Barrier(mpiBarrierCommunicator);
-#else
-        GMX_RELEASE_ASSERT(false, "Should not request a barrier without MPI");
-        GMX_UNUSED_VALUE(mpiBarrierCommunicator);
-#endif
-    }
-}
-
-void write_checkpoint(const char*                   fn,
-                      gmx_bool                      bNumberAndKeep,
-                      FILE*                         fplog,
-                      const t_commrec*              cr,
-                      ivec                          domdecCells,
-                      int                           nppnodes,
-                      int                           eIntegrator,
-                      int                           simulation_part,
-                      gmx_bool                      bExpanded,
-                      int                           elamstats,
-                      int64_t                       step,
-                      double                        t,
-                      t_state*                      state,
-                      ObservablesHistory*           observablesHistory,
-                      const gmx::MdModulesNotifier& mdModulesNotifier,
-                      bool                          applyMpiBarrierBeforeRename,
-                      MPI_Comm                      mpiBarrierCommunicator)
-{
-    t_fileio* fp;
-    char*     fntemp; /* the temporary checkpoint file name */
-    int       npmenodes;
-    char      buf[1024], suffix[5 + STEPSTRSIZE], sbuf[STEPSTRSIZE];
-    t_fileio* ret;
-
-    if (DOMAINDECOMP(cr))
-    {
-        npmenodes = cr->npmenodes;
-    }
-    else
-    {
-        npmenodes = 0;
-    }
-
-#if !GMX_NO_RENAME
-    /* make the new temporary filename */
-    snew(fntemp, std::strlen(fn) + 5 + STEPSTRSIZE);
-    std::strcpy(fntemp, fn);
-    fntemp[std::strlen(fn) - std::strlen(ftp2ext(fn2ftp(fn))) - 1] = '\0';
-    sprintf(suffix, "_%s%s", "step", gmx_step_str(step, sbuf));
-    std::strcat(fntemp, suffix);
-    std::strcat(fntemp, fn + std::strlen(fn) - std::strlen(ftp2ext(fn2ftp(fn))) - 1);
-#else
-    /* if we can't rename, we just overwrite the cpt file.
-     * dangerous if interrupted.
-     */
-    snew(fntemp, std::strlen(fn));
-    std::strcpy(fntemp, fn);
-#endif
-    std::string timebuf = gmx_format_current_time();
-
-    if (fplog)
-    {
-        fprintf(fplog, "Writing checkpoint, step %s at %s\n\n", gmx_step_str(step, buf), timebuf.c_str());
-    }
-
-    /* Get offsets for open files */
-    auto outputfiles = gmx_fio_get_output_file_positions();
-
-    fp = gmx_fio_open(fntemp, "w");
-
-    int flags_eks;
+void write_checkpoint_data(t_fileio*                         fp,
+                           CheckpointHeaderContents          headerContents,
+                           gmx_bool                          bExpanded,
+                           int                               elamstats,
+                           t_state*                          state,
+                           ObservablesHistory*               observablesHistory,
+                           const gmx::MdModulesNotifier&     mdModulesNotifier,
+                           std::vector<gmx_file_position_t>* outputfiles,
+                           gmx::WriteCheckpointDataHolder*   modularSimulatorCheckpointData)
+{
+    headerContents.flags_eks = 0;
     if (state->ekinstate.bUpToDate)
     {
-        flags_eks = ((1 << eeksEKIN_N) | (1 << eeksEKINH) | (1 << eeksEKINF) | (1 << eeksEKINO)
-                     | (1 << eeksEKINSCALEF) | (1 << eeksEKINSCALEH) | (1 << eeksVSCALE)
-                     | (1 << eeksDEKINDL) | (1 << eeksMVCOS));
-    }
-    else
-    {
-        flags_eks = 0;
+        headerContents.flags_eks = ((1 << eeksEKIN_N) | (1 << eeksEKINH) | (1 << eeksEKINF)
+                                    | (1 << eeksEKINO) | (1 << eeksEKINSCALEF) | (1 << eeksEKINSCALEH)
+                                    | (1 << eeksVSCALE) | (1 << eeksDEKINDL) | (1 << eeksMVCOS));
     }
+    headerContents.isModularSimulatorCheckpoint = !modularSimulatorCheckpointData->empty();
 
-    energyhistory_t* enerhist  = observablesHistory->energyHistory.get();
-    int              flags_enh = 0;
+    energyhistory_t* enerhist = observablesHistory->energyHistory.get();
+    headerContents.flags_enh  = 0;
     if (enerhist != nullptr && (enerhist->nsum > 0 || enerhist->nsum_sim > 0))
     {
-        flags_enh |= (1 << eenhENERGY_N) | (1 << eenhENERGY_NSTEPS) | (1 << eenhENERGY_NSTEPS_SIM);
+        headerContents.flags_enh |=
+                (1 << eenhENERGY_N) | (1 << eenhENERGY_NSTEPS) | (1 << eenhENERGY_NSTEPS_SIM);
         if (enerhist->nsum > 0)
         {
-            flags_enh |= ((1 << eenhENERGY_AVER) | (1 << eenhENERGY_SUM) | (1 << eenhENERGY_NSUM));
+            headerContents.flags_enh |=
+                    ((1 << eenhENERGY_AVER) | (1 << eenhENERGY_SUM) | (1 << eenhENERGY_NSUM));
         }
         if (enerhist->nsum_sim > 0)
         {
-            flags_enh |= ((1 << eenhENERGY_SUM_SIM) | (1 << eenhENERGY_NSUM_SIM));
+            headerContents.flags_enh |= ((1 << eenhENERGY_SUM_SIM) | (1 << eenhENERGY_NSUM_SIM));
         }
         if (enerhist->deltaHForeignLambdas != nullptr)
         {
-            flags_enh |= ((1 << eenhENERGY_DELTA_H_NN) | (1 << eenhENERGY_DELTA_H_LIST)
-                          | (1 << eenhENERGY_DELTA_H_STARTTIME) | (1 << eenhENERGY_DELTA_H_STARTLAMBDA));
+            headerContents.flags_enh |=
+                    ((1 << eenhENERGY_DELTA_H_NN) | (1 << eenhENERGY_DELTA_H_LIST)
+                     | (1 << eenhENERGY_DELTA_H_STARTTIME) | (1 << eenhENERGY_DELTA_H_STARTLAMBDA));
         }
     }
 
-    PullHistory* pullHist         = observablesHistory->pullHistory.get();
-    int          flagsPullHistory = 0;
+    PullHistory* pullHist           = observablesHistory->pullHistory.get();
+    headerContents.flagsPullHistory = 0;
     if (pullHist != nullptr && (pullHist->numValuesInXSum > 0 || pullHist->numValuesInFSum > 0))
     {
-        flagsPullHistory |= (1 << epullhPULL_NUMCOORDINATES);
-        flagsPullHistory |= ((1 << epullhPULL_NUMGROUPS) | (1 << epullhPULL_NUMVALUESINXSUM)
-                             | (1 << epullhPULL_NUMVALUESINFSUM));
+        headerContents.flagsPullHistory |= (1 << epullhPULL_NUMCOORDINATES);
+        headerContents.flagsPullHistory |= ((1 << epullhPULL_NUMGROUPS) | (1 << epullhPULL_NUMVALUESINXSUM)
+                                            | (1 << epullhPULL_NUMVALUESINFSUM));
     }
 
-    int flags_dfh;
+    headerContents.flags_dfh = 0;
     if (bExpanded)
     {
-        flags_dfh = ((1 << edfhBEQUIL) | (1 << edfhNATLAMBDA) | (1 << edfhSUMWEIGHTS)
-                     | (1 << edfhSUMDG) | (1 << edfhTIJ) | (1 << edfhTIJEMP));
+        headerContents.flags_dfh = ((1 << edfhBEQUIL) | (1 << edfhNATLAMBDA) | (1 << edfhSUMWEIGHTS)
+                                    | (1 << edfhSUMDG) | (1 << edfhTIJ) | (1 << edfhTIJEMP));
         if (EWL(elamstats))
         {
-            flags_dfh |= ((1 << edfhWLDELTA) | (1 << edfhWLHISTO));
+            headerContents.flags_dfh |= ((1 << edfhWLDELTA) | (1 << edfhWLHISTO));
         }
         if ((elamstats == elamstatsMINVAR) || (elamstats == elamstatsBARKER)
             || (elamstats == elamstatsMETROPOLIS))
         {
-            flags_dfh |= ((1 << edfhACCUMP) | (1 << edfhACCUMM) | (1 << edfhACCUMP2)
-                          | (1 << edfhACCUMM2) | (1 << edfhSUMMINVAR) | (1 << edfhSUMVAR));
+            headerContents.flags_dfh |= ((1 << edfhACCUMP) | (1 << edfhACCUMM) | (1 << edfhACCUMP2)
+                                         | (1 << edfhACCUMM2) | (1 << edfhSUMMINVAR) | (1 << edfhSUMVAR));
         }
     }
-    else
-    {
-        flags_dfh = 0;
-    }
 
-    int flags_awhh = 0;
+    headerContents.flags_awhh = 0;
     if (state->awhHistory != nullptr && !state->awhHistory->bias.empty())
     {
-        flags_awhh |= ((1 << eawhhIN_INITIAL) | (1 << eawhhEQUILIBRATEHISTOGRAM) | (1 << eawhhHISTSIZE)
-                       | (1 << eawhhNPOINTS) | (1 << eawhhCOORDPOINT) | (1 << eawhhUMBRELLAGRIDPOINT)
-                       | (1 << eawhhUPDATELIST) | (1 << eawhhLOGSCALEDSAMPLEWEIGHT)
-                       | (1 << eawhhNUMUPDATES) | (1 << eawhhFORCECORRELATIONGRID));
-    }
-
-    /* We can check many more things now (CPU, acceleration, etc), but
-     * it is highly unlikely to have two separate builds with exactly
-     * the same version, user, time, and build host!
-     */
-
-    int nlambda = (state->dfhist ? state->dfhist->nlambda : 0);
-
-    edsamhistory_t* edsamhist = observablesHistory->edsamHistory.get();
-    int             nED       = (edsamhist ? edsamhist->nED : 0);
-
-    swaphistory_t* swaphist    = observablesHistory->swapHistory.get();
-    int            eSwapCoords = (swaphist ? swaphist->eSwapCoords : eswapNO);
-
-    CheckpointHeaderContents headerContents = { 0,
-                                                { 0 },
-                                                { 0 },
-                                                { 0 },
-                                                { 0 },
-                                                GMX_DOUBLE,
-                                                { 0 },
-                                                { 0 },
-                                                eIntegrator,
-                                                simulation_part,
-                                                step,
-                                                t,
-                                                nppnodes,
-                                                { 0 },
-                                                npmenodes,
-                                                state->natoms,
-                                                state->ngtc,
-                                                state->nnhpres,
-                                                state->nhchainlength,
-                                                nlambda,
-                                                state->flags,
-                                                flags_eks,
-                                                flags_enh,
-                                                flagsPullHistory,
-                                                flags_dfh,
-                                                flags_awhh,
-                                                nED,
-                                                eSwapCoords };
-    std::strcpy(headerContents.version, gmx_version());
-    std::strcpy(headerContents.fprog, gmx::getProgramContext().fullBinaryPath());
-    std::strcpy(headerContents.ftime, timebuf.c_str());
-    if (DOMAINDECOMP(cr))
-    {
-        copy_ivec(domdecCells, headerContents.dd_nc);
+        headerContents.flags_awhh |=
+                ((1 << eawhhIN_INITIAL) | (1 << eawhhEQUILIBRATEHISTOGRAM) | (1 << eawhhHISTSIZE)
+                 | (1 << eawhhNPOINTS) | (1 << eawhhCOORDPOINT) | (1 << eawhhUMBRELLAGRIDPOINT)
+                 | (1 << eawhhUPDATELIST) | (1 << eawhhLOGSCALEDSAMPLEWEIGHT)
+                 | (1 << eawhhNUMUPDATES) | (1 << eawhhFORCECORRELATIONGRID));
     }
 
     do_cpt_header(gmx_fio_getxdr(fp), FALSE, nullptr, &headerContents);
 
     if ((do_cpt_state(gmx_fio_getxdr(fp), state->flags, state, nullptr) < 0)
-        || (do_cpt_ekinstate(gmx_fio_getxdr(fp), flags_eks, &state->ekinstate, nullptr) < 0)
-        || (do_cpt_enerhist(gmx_fio_getxdr(fp), FALSE, flags_enh, enerhist, nullptr) < 0)
-        || (doCptPullHist(gmx_fio_getxdr(fp), FALSE, flagsPullHistory, pullHist, StatePart::pullHistory, nullptr)
+        || (do_cpt_ekinstate(gmx_fio_getxdr(fp), headerContents.flags_eks, &state->ekinstate, nullptr) < 0)
+        || (do_cpt_enerhist(gmx_fio_getxdr(fp), FALSE, headerContents.flags_enh, enerhist, nullptr) < 0)
+        || (doCptPullHist(gmx_fio_getxdr(fp), FALSE, headerContents.flagsPullHistory, pullHist,
+                          StatePart::pullHistory, nullptr)
+            < 0)
+        || (do_cpt_df_hist(gmx_fio_getxdr(fp), headerContents.flags_dfh, headerContents.nlambda,
+                           &state->dfhist, nullptr)
+            < 0)
+        || (do_cpt_EDstate(gmx_fio_getxdr(fp), FALSE, headerContents.nED,
+                           observablesHistory->edsamHistory.get(), nullptr)
+            < 0)
+        || (do_cpt_awh(gmx_fio_getxdr(fp), FALSE, headerContents.flags_awhh, state->awhHistory.get(), nullptr) < 0)
+        || (do_cpt_swapstate(gmx_fio_getxdr(fp), FALSE, headerContents.eSwapCoords,
+                             observablesHistory->swapHistory.get(), nullptr)
             < 0)
-        || (do_cpt_df_hist(gmx_fio_getxdr(fp), flags_dfh, nlambda, &state->dfhist, nullptr) < 0)
-        || (do_cpt_EDstate(gmx_fio_getxdr(fp), FALSE, nED, edsamhist, nullptr) < 0)
-        || (do_cpt_awh(gmx_fio_getxdr(fp), FALSE, flags_awhh, state->awhHistory.get(), nullptr) < 0)
-        || (do_cpt_swapstate(gmx_fio_getxdr(fp), FALSE, eSwapCoords, swaphist, nullptr) < 0)
-        || (do_cpt_files(gmx_fio_getxdr(fp), FALSE, &outputfiles, nullptr, headerContents.file_version) < 0))
+        || (do_cpt_files(gmx_fio_getxdr(fp), FALSE, outputfiles, nullptr, headerContents.file_version) < 0))
     {
         gmx_file("Cannot read/write checkpoint; corrupt file, or maybe you are out of disk space?");
     }
@@ -2387,84 +2332,19 @@ void write_checkpoint(const char*                   fn,
         gmx::KeyValueTreeBuilder          builder;
         gmx::MdModulesWriteCheckpointData mdModulesWriteCheckpoint = { builder.rootObject(),
                                                                        headerContents.file_version };
-        mdModulesNotifier.notifier_.notify(mdModulesWriteCheckpoint);
+        mdModulesNotifier.checkpointingNotifications_.notify(mdModulesWriteCheckpoint);
         auto                     tree = builder.build();
         gmx::FileIOXdrSerializer serializer(fp);
         gmx::serializeKeyValueTree(tree, &serializer);
     }
 
-    do_cpt_footer(gmx_fio_getxdr(fp), headerContents.file_version);
-
-    /* we really, REALLY, want to make sure to physically write the checkpoint,
-       and all the files it depends on, out to disk. Because we've
-       opened the checkpoint with gmx_fio_open(), it's in our list
-       of open files.  */
-    ret = gmx_fio_all_output_fsync();
-
-    if (ret)
+    // Checkpointing modular simulator
     {
-        char buf[STRLEN];
-        sprintf(buf, "Cannot fsync '%s'; maybe you are out of disk space?", gmx_fio_getname(ret));
-
-        if (getenv(GMX_IGNORE_FSYNC_FAILURE_ENV) == nullptr)
-        {
-            gmx_file(buf);
-        }
-        else
-        {
-            gmx_warning("%s", buf);
-        }
-    }
-
-    if (gmx_fio_close(fp) != 0)
-    {
-        gmx_file("Cannot read/write checkpoint; corrupt file, or maybe you are out of disk space?");
-    }
-
-    /* we don't move the checkpoint if the user specified they didn't want it,
-       or if the fsyncs failed */
-#if !GMX_NO_RENAME
-    if (!bNumberAndKeep && !ret)
-    {
-        if (gmx_fexist(fn))
-        {
-            /* Rename the previous checkpoint file */
-            mpiBarrierBeforeRename(applyMpiBarrierBeforeRename, mpiBarrierCommunicator);
-
-            std::strcpy(buf, fn);
-            buf[std::strlen(fn) - std::strlen(ftp2ext(fn2ftp(fn))) - 1] = '\0';
-            std::strcat(buf, "_prev");
-            std::strcat(buf, fn + std::strlen(fn) - std::strlen(ftp2ext(fn2ftp(fn))) - 1);
-            if (!GMX_FAHCORE)
-            {
-                /* we copy here so that if something goes wrong between now and
-                 * the rename below, there's always a state.cpt.
-                 * If renames are atomic (such as in POSIX systems),
-                 * this copying should be unneccesary.
-                 */
-                gmx_file_copy(fn, buf, FALSE);
-                /* We don't really care if this fails:
-                 * there's already a new checkpoint.
-                 */
-            }
-            else
-            {
-                gmx_file_rename(fn, buf);
-            }
-        }
-
-        /* Rename the checkpoint file from the temporary to the final name */
-        mpiBarrierBeforeRename(applyMpiBarrierBeforeRename, mpiBarrierCommunicator);
-
-        if (gmx_file_rename(fntemp, fn) != 0)
-        {
-            gmx_file("Cannot rename checkpoint file; maybe you are out of disk space?");
-        }
+        gmx::FileIOXdrSerializer serializer(fp);
+        modularSimulatorCheckpointData->serialize(&serializer);
     }
-#endif /* GMX_NO_RENAME */
-
-    sfree(fntemp);
 
+    do_cpt_footer(gmx_fio_getxdr(fp), headerContents.file_version);
 #if GMX_FAHCORE
     /* Always FAH checkpoint immediately after a Gromacs checkpoint.
      *
@@ -2553,11 +2433,12 @@ static void check_match(FILE*                           fplog,
         check_int(fplog, "#ranks", cr->nnodes, headerContents.nnodes, &mm);
     }
 
-    if (cr->nnodes > 1 && reproducibilityRequested)
+    if (cr->sizeOfDefaultCommunicator > 1 && reproducibilityRequested)
     {
+        // TODO: These checks are incorrect (see redmine #3309)
         check_int(fplog, "#PME-ranks", cr->npmenodes, headerContents.npme, &mm);
 
-        int npp = cr->nnodes;
+        int npp = cr->sizeOfDefaultCommunicator;
         if (cr->npmenodes >= 0)
         {
             npp -= cr->npmenodes;
@@ -2618,17 +2499,19 @@ static void check_match(FILE*                           fplog,
     }
 }
 
-static void read_checkpoint(const char*                   fn,
-                            t_fileio*                     logfio,
-                            const t_commrec*              cr,
-                            const ivec                    dd_nc,
-                            int                           eIntegrator,
-                            int*                          init_fep_state,
-                            CheckpointHeaderContents*     headerContents,
-                            t_state*                      state,
-                            ObservablesHistory*           observablesHistory,
-                            gmx_bool                      reproducibilityRequested,
-                            const gmx::MdModulesNotifier& mdModulesNotifier)
+static void read_checkpoint(const char*                    fn,
+                            t_fileio*                      logfio,
+                            const t_commrec*               cr,
+                            const ivec                     dd_nc,
+                            int                            eIntegrator,
+                            int*                           init_fep_state,
+                            CheckpointHeaderContents*      headerContents,
+                            t_state*                       state,
+                            ObservablesHistory*            observablesHistory,
+                            gmx_bool                       reproducibilityRequested,
+                            const gmx::MdModulesNotifier&  mdModulesNotifier,
+                            gmx::ReadCheckpointDataHolder* modularSimulatorCheckpointData,
+                            bool                           useModularSimulator)
 {
     t_fileio* fp;
     char      buf[STEPSTRSIZE];
@@ -2699,7 +2582,8 @@ static void read_checkpoint(const char*                   fn,
                   fn);
     }
 
-    if (headerContents->flags_state != state->flags)
+    // For modular simulator, no state object is populated, so we cannot do this check here!
+    if (headerContents->flags_state != state->flags && !useModularSimulator)
     {
         gmx_fatal(FARGS,
                   "Cannot change a simulation algorithm during a checkpoint restart. Perhaps you "
@@ -2707,6 +2591,13 @@ static void read_checkpoint(const char*                   fn,
                   fn);
     }
 
+    GMX_ASSERT(!(headerContents->isModularSimulatorCheckpoint && !useModularSimulator),
+               "Checkpoint file was written by modular simulator, but the current simulation uses "
+               "the legacy simulator.");
+    GMX_ASSERT(!(!headerContents->isModularSimulatorCheckpoint && useModularSimulator),
+               "Checkpoint file was written by legacy simulator, but the current simulation uses "
+               "the modular simulator.");
+
     if (MASTER(cr))
     {
         check_match(fplog, cr, dd_nc, *headerContents, reproducibilityRequested);
@@ -2809,6 +2700,11 @@ static void read_checkpoint(const char*                   fn,
         cp_error();
     }
     do_cpt_mdmodules(headerContents->file_version, fp, mdModulesNotifier);
+    if (headerContents->file_version >= cptv_ModularSimulator)
+    {
+        gmx::FileIOXdrSerializer serializer(fp);
+        modularSimulatorCheckpointData->deserialize(&serializer);
+    }
     ret = do_cpt_footer(gmx_fio_getxdr(fp), headerContents->file_version);
     if (ret)
     {
@@ -2821,28 +2717,33 @@ static void read_checkpoint(const char*                   fn,
 }
 
 
-void load_checkpoint(const char*                   fn,
-                     t_fileio*                     logfio,
-                     const t_commrec*              cr,
-                     const ivec                    dd_nc,
-                     t_inputrec*                   ir,
-                     t_state*                      state,
-                     ObservablesHistory*           observablesHistory,
-                     gmx_bool                      reproducibilityRequested,
-                     const gmx::MdModulesNotifier& mdModulesNotifier)
+void load_checkpoint(const char*                    fn,
+                     t_fileio*                      logfio,
+                     const t_commrec*               cr,
+                     const ivec                     dd_nc,
+                     t_inputrec*                    ir,
+                     t_state*                       state,
+                     ObservablesHistory*            observablesHistory,
+                     gmx_bool                       reproducibilityRequested,
+                     const gmx::MdModulesNotifier&  mdModulesNotifier,
+                     gmx::ReadCheckpointDataHolder* modularSimulatorCheckpointData,
+                     bool                           useModularSimulator)
 {
     CheckpointHeaderContents headerContents;
     if (SIMMASTER(cr))
     {
         /* Read the state from the checkpoint file */
-        read_checkpoint(fn, logfio, cr, dd_nc, ir->eI, &(ir->fepvals->init_fep_state), &headerContents,
-                        state, observablesHistory, reproducibilityRequested, mdModulesNotifier);
+        read_checkpoint(fn, logfio, cr, dd_nc, ir->eI, &(ir->fepvals->init_fep_state),
+                        &headerContents, state, observablesHistory, reproducibilityRequested,
+                        mdModulesNotifier, modularSimulatorCheckpointData, useModularSimulator);
     }
     if (PAR(cr))
     {
-        gmx_bcast(sizeof(headerContents.step), &headerContents.step, cr);
-        gmx::MdModulesCheckpointReadingBroadcast broadcastCheckPointData = { *cr, headerContents.file_version };
-        mdModulesNotifier.notifier_.notify(broadcastCheckPointData);
+        gmx_bcast(sizeof(headerContents.step), &headerContents.step, cr->mpiDefaultCommunicator);
+        gmx::MdModulesCheckpointReadingBroadcast broadcastCheckPointData = {
+            cr->mpiDefaultCommunicator, PAR(cr), headerContents.file_version
+        };
+        mdModulesNotifier.checkpointingNotifications_.notify(broadcastCheckPointData);
     }
     ir->bContinuation = TRUE;
     if (ir->nsteps >= 0)
@@ -2965,6 +2866,14 @@ static CheckpointHeaderContents read_checkpoint_data(t_fileio*
     }
     gmx::MdModulesNotifier mdModuleNotifier;
     do_cpt_mdmodules(headerContents.file_version, fp, mdModuleNotifier);
+    if (headerContents.file_version >= cptv_ModularSimulator)
+    {
+        // In the scope of the current function, we can just throw away the content
+        // of the modular checkpoint, but we need to read it to move the file pointer
+        gmx::FileIOXdrSerializer      serializer(fp);
+        gmx::ReadCheckpointDataHolder modularSimulatorCheckpointData;
+        modularSimulatorCheckpointData.deserialize(&serializer);
+    }
     ret = do_cpt_footer(gmx_fio_getxdr(fp), headerContents.file_version);
     if (ret)
     {
index fb8f7268be8df1ad347308af9d1cde8ce06abaf0..c3dbc3c1077ee06c3eb055250e74c70823d6e0b3 100644 (file)
@@ -43,6 +43,7 @@
 
 #include <vector>
 
+#include "gromacs/compat/pointers.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/gmxmpi.h"
@@ -62,7 +63,67 @@ namespace gmx
 
 struct MdModulesNotifier;
 class KeyValueTreeObject;
+class ReadCheckpointDataHolder;
+class WriteCheckpointDataHolder;
 
+/*! \brief Read to a key-value-tree value used for checkpointing.
+ *
+ * \tparam ValueType
+ *
+ * \param[in] value the value to be checkpointed
+ * \param[in] name name of the value to be checkpointed
+ * \param[in] identifier uniquely identifies the module that is checkpointing
+ *                       typically the module name
+ * \param[in] kvt the key value tree to read from
+ *
+ * \throws InternalError if kvt does not contain requested value.
+ * \note Triggers assertion if value type is not correct.
+ */
+template<typename ValueType>
+void readKvtCheckpointValue(compat::not_null<ValueType*> value,
+                            const std::string&           name,
+                            const std::string&           identifier,
+                            const KeyValueTreeObject&    kvt);
+//! \copydoc readKvtCheckpointValue
+extern template void readKvtCheckpointValue(compat::not_null<std::int64_t*> value,
+                                            const std::string&              name,
+                                            const std::string&              identifier,
+                                            const KeyValueTreeObject&       kvt);
+//! \copydoc readKvtCheckpointValue
+extern template void readKvtCheckpointValue(compat::not_null<real*>   value,
+                                            const std::string&        name,
+                                            const std::string&        identifier,
+                                            const KeyValueTreeObject& kvt);
+
+/*! \brief Write to a key-value-tree used for checkpointing.
+ *
+ * \tparam ValueType
+ *
+ * \param[in] value name of the value to be checkpointed
+ * \param[in] name the value to be checkpointed
+ * \param[in] identifier uniquely identifies the module that is checkpointing
+ *                       typically the module name
+ * \param[in] kvtBuilder the key-value-tree builder used to store the checkpoint values
+ */
+template<typename ValueType>
+void writeKvtCheckpointValue(const ValueType&          value,
+                             const std::string&        name,
+                             const std::string&        identifier,
+                             KeyValueTreeObjectBuilder kvtBuilder);
+//! \copydoc writeKvtCheckpointValue
+extern template void writeKvtCheckpointValue(const std::int64_t&       value,
+                                             const std::string&        name,
+                                             const std::string&        identifier,
+                                             KeyValueTreeObjectBuilder kvtBuilder);
+//! \copydoc writeKvtCheckpointValue
+extern template void writeKvtCheckpointValue(const real&               value,
+                                             const std::string&        name,
+                                             const std::string&        identifier,
+                                             KeyValueTreeObjectBuilder kvtBuilder);
+
+/*! \libinternal
+ * \brief Provides the MdModules with the checkpointed data on the master rank.
+ */
 struct MdModulesCheckpointReadingDataOnMaster
 {
     //! The data of the MdModules that is stored in the checkpoint file
@@ -76,8 +137,10 @@ struct MdModulesCheckpointReadingDataOnMaster
  */
 struct MdModulesCheckpointReadingBroadcast
 {
-    //! The communication record
-    const t_commrec& cr_;
+    //! The communicator
+    MPI_Comm communicator_;
+    //! Whether the run is executed in parallel
+    bool isParallelRun_;
     //! The version of the read file version
     int checkpointFileVersion_;
 };
@@ -175,29 +238,20 @@ struct CheckpointHeaderContents
     int nED;
     //! Enum for coordinate swapping.
     int eSwapCoords;
+    //! Whether the checkpoint was written by modular simulator.
+    bool isModularSimulatorCheckpoint = false;
 };
 
-/* Write a checkpoint to <fn>.cpt
- * Appends the _step<step>.cpt with bNumberAndKeep,
- * otherwise moves the previous <fn>.cpt to <fn>_prev.cpt
- */
-void write_checkpoint(const char*                   fn,
-                      gmx_bool                      bNumberAndKeep,
-                      FILE*                         fplog,
-                      const t_commrec*              cr,
-                      ivec                          domdecCells,
-                      int                           nppnodes,
-                      int                           eIntegrator,
-                      int                           simulation_part,
-                      gmx_bool                      bExpanded,
-                      int                           elamstats,
-                      int64_t                       step,
-                      double                        t,
-                      t_state*                      state,
-                      ObservablesHistory*           observablesHistory,
-                      const gmx::MdModulesNotifier& notifier,
-                      bool                          applyMpiBarrierBeforeRename,
-                      MPI_Comm                      mpiBarrierCommunicator);
+/*! \brief Low-level checkpoint writing function */
+void write_checkpoint_data(t_fileio*                         fp,
+                           CheckpointHeaderContents          headerContents,
+                           gmx_bool                          bExpanded,
+                           int                               elamstats,
+                           t_state*                          state,
+                           ObservablesHistory*               observablesHistory,
+                           const gmx::MdModulesNotifier&     notifier,
+                           std::vector<gmx_file_position_t>* outputfiles,
+                           gmx::WriteCheckpointDataHolder*   modularSimulatorCheckpointData);
 
 /* Loads a checkpoint from fn for run continuation.
  * Generates a fatal error on system size mismatch.
@@ -206,15 +260,17 @@ void write_checkpoint(const char*                   fn,
  * but not the state itself.
  * With reproducibilityRequested warns about version, build, #ranks differences.
  */
-void load_checkpoint(const char*                   fn,
-                     t_fileio*                     logfio,
-                     const t_commrec*              cr,
-                     const ivec                    dd_nc,
-                     t_inputrec*                   ir,
-                     t_state*                      state,
-                     ObservablesHistory*           observablesHistory,
-                     gmx_bool                      reproducibilityRequested,
-                     const gmx::MdModulesNotifier& mdModulesNotifier);
+void load_checkpoint(const char*                    fn,
+                     t_fileio*                      logfio,
+                     const t_commrec*               cr,
+                     const ivec                     dd_nc,
+                     t_inputrec*                    ir,
+                     t_state*                       state,
+                     ObservablesHistory*            observablesHistory,
+                     gmx_bool                       reproducibilityRequested,
+                     const gmx::MdModulesNotifier&  mdModulesNotifier,
+                     gmx::ReadCheckpointDataHolder* modularSimulatorCheckpointData,
+                     bool                           useModularSimulator);
 
 /* Read everything that can be stored in t_trxframe from a checkpoint file */
 void read_checkpoint_trxframe(struct t_fileio* fp, t_trxframe* fr);
index f822d09be58c470fcdef9a95ca0ea52347c1b8c7..7114d54c6d7e56862080fd25fcaaddc440d98e1b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -66,7 +67,7 @@ void write_sto_conf_indexed(const char*    outfile,
                             const t_atoms* atoms,
                             const rvec     x[],
                             const rvec*    v,
-                            int            ePBC,
+                            PbcType        pbcType,
                             const matrix   box,
                             int            nindex,
                             int            index[])
@@ -106,8 +107,8 @@ void write_sto_conf_indexed(const char*    outfile,
         case efENT:
         case efPQR:
             out = gmx_fio_fopen(outfile, "w");
-            write_pdbfile_indexed(out, title, atoms, x, ePBC, box, ' ', -1, nindex, index, nullptr,
-                                  ftp == efPQR);
+            write_pdbfile_indexed(out, title, atoms, x, pbcType, box, ' ', -1, nindex, index,
+                                  nullptr, ftp == efPQR);
             gmx_fio_fclose(out);
             break;
         case efESP:
@@ -125,7 +126,7 @@ void write_sto_conf(const char*    outfile,
                     const t_atoms* atoms,
                     const rvec     x[],
                     const rvec*    v,
-                    int            ePBC,
+                    PbcType        pbcType,
                     const matrix   box)
 {
     FILE*      out;
@@ -158,7 +159,7 @@ void write_sto_conf(const char*    outfile,
         case efBRK:
         case efENT:
             out = gmx_fio_fopen(outfile, "w");
-            write_pdbfile(out, title, atoms, x, ePBC, box, ' ', -1, nullptr);
+            write_pdbfile(out, title, atoms, x, pbcType, box, ' ', -1, nullptr);
             gmx_fio_fclose(out);
             break;
         case efESP:
@@ -176,7 +177,7 @@ void write_sto_conf_mtop(const char*       outfile,
                          const gmx_mtop_t* mtop,
                          const rvec        x[],
                          const rvec*       v,
-                         int               ePBC,
+                         PbcType           pbcType,
                          const matrix      box)
 {
     int     ftp;
@@ -197,7 +198,7 @@ void write_sto_conf_mtop(const char*       outfile,
              */
             atoms = gmx_mtop_global_atoms(mtop);
 
-            write_sto_conf(outfile, title, &atoms, x, v, ePBC, box);
+            write_sto_conf(outfile, title, &atoms, x, v, pbcType, box);
 
             done_atom(&atoms);
             break;
@@ -343,7 +344,7 @@ static void read_stx_conf(const char* infile,
                           t_atoms*    atoms,
                           rvec        x[],
                           rvec*       v,
-                          int*        ePBC,
+                          PbcType*    pbcType,
                           matrix      box)
 {
     FILE*      in;
@@ -360,9 +361,9 @@ static void read_stx_conf(const char* infile,
         gmx_mem("Uninitialized array atom");
     }
 
-    if (ePBC)
+    if (pbcType)
     {
-        *ePBC = -1;
+        *pbcType = PbcType::Unset;
     }
 
     ftp = fn2ftp(infile);
@@ -382,7 +383,7 @@ static void read_stx_conf(const char* infile,
             break;
         case efPDB:
         case efBRK:
-        case efENT: gmx_pdb_read_conf(infile, symtab, name, atoms, x, ePBC, box); break;
+        case efENT: gmx_pdb_read_conf(infile, symtab, name, atoms, x, pbcType, box); break;
         case efESP: gmx_espresso_read_conf(infile, symtab, name, atoms, x, v, box); break;
         default: gmx_incons("Not supported in read_stx_conf");
     }
@@ -392,7 +393,7 @@ void readConfAndAtoms(const char* infile,
                       t_symtab*   symtab,
                       char**      name,
                       t_atoms*    atoms,
-                      int*        ePBC,
+                      PbcType*    pbcType,
                       rvec**      x,
                       rvec**      v,
                       matrix      box)
@@ -403,7 +404,7 @@ void readConfAndAtoms(const char* infile,
     {
         bool       haveTopology;
         gmx_mtop_t mtop;
-        readConfAndTopology(infile, &haveTopology, &mtop, ePBC, x, v, box);
+        readConfAndTopology(infile, &haveTopology, &mtop, pbcType, x, v, box);
         *symtab                          = mtop.symtab;
         *name                            = gmx_strdup(*mtop.name);
         *atoms                           = gmx_mtop_global_atoms(&mtop);
@@ -436,7 +437,7 @@ void readConfAndAtoms(const char* infile,
     {
         snew(*v, natoms);
     }
-    read_stx_conf(infile, symtab, name, atoms, *x, (v == nullptr) ? nullptr : *v, ePBC, box);
+    read_stx_conf(infile, symtab, name, atoms, *x, (v == nullptr) ? nullptr : *v, pbcType, box);
     if (xIsNull)
     {
         sfree(*x);
@@ -444,13 +445,19 @@ void readConfAndAtoms(const char* infile,
     }
 }
 
-void readConfAndTopology(const char* infile, bool* haveTopology, gmx_mtop_t* mtop, int* ePBC, rvec** x, rvec** v, matrix box)
+void readConfAndTopology(const char* infile,
+                         bool*       haveTopology,
+                         gmx_mtop_t* mtop,
+                         PbcType*    pbcType,
+                         rvec**      x,
+                         rvec**      v,
+                         matrix      box)
 {
     GMX_RELEASE_ASSERT(mtop != nullptr, "readConfAndTopology requires mtop!=NULL");
 
-    if (ePBC != nullptr)
+    if (pbcType != nullptr)
     {
-        *ePBC = -1;
+        *pbcType = PbcType::Unset;
     }
 
     *haveTopology = fn2bTPX(infile);
@@ -465,12 +472,12 @@ void readConfAndTopology(const char* infile, bool* haveTopology, gmx_mtop_t* mto
         {
             snew(*v, header.natoms);
         }
-        int natoms;
-        int ePBC_tmp = read_tpx(infile, nullptr, box, &natoms, (x == nullptr) ? nullptr : *x,
-                                (v == nullptr) ? nullptr : *v, mtop);
-        if (ePBC != nullptr)
+        int     natoms;
+        PbcType pbcType_tmp = read_tpx(infile, nullptr, box, &natoms, (x == nullptr) ? nullptr : *x,
+                                       (v == nullptr) ? nullptr : *v, mtop);
+        if (pbcType != nullptr)
         {
-            *ePBC = ePBC_tmp;
+            *pbcType = pbcType_tmp;
         }
     }
     else
@@ -481,19 +488,19 @@ void readConfAndTopology(const char* infile, bool* haveTopology, gmx_mtop_t* mto
 
         open_symtab(&symtab);
 
-        readConfAndAtoms(infile, &symtab, &name, &atoms, ePBC, x, v, box);
+        readConfAndAtoms(infile, &symtab, &name, &atoms, pbcType, x, v, box);
 
         convertAtomsToMtop(&symtab, put_symtab(&symtab, name), &atoms, mtop);
         sfree(name);
     }
 }
 
-gmx_bool read_tps_conf(const char* infile, t_topology* top, int* ePBC, rvec** x, rvec** v, matrix box, gmx_bool requireMasses)
+gmx_bool read_tps_conf(const char* infile, t_topology* top, PbcType* pbcType, rvec** x, rvec** v, matrix box, gmx_bool requireMasses)
 {
     bool       haveTopology;
     gmx_mtop_t mtop;
 
-    readConfAndTopology(infile, &haveTopology, &mtop, ePBC, x, v, box);
+    readConfAndTopology(infile, &haveTopology, &mtop, pbcType, x, v, box);
 
     *top = gmx_mtop_t_to_t_topology(&mtop, true);
 
index 6fa060b6497c1bd081b2896f659807d93bfc9e0f..08af69c205ef465e727c040d8a9b9fa2e8bcaa8c 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,13 +48,14 @@ struct gmx_mtop_t;
 struct t_atoms;
 struct t_symtab;
 struct t_topology;
+enum class PbcType : int;
 
 void write_sto_conf_indexed(const char*    outfile,
                             const char*    title,
                             const t_atoms* atoms,
                             const rvec     x[],
                             const rvec*    v,
-                            int            ePBC,
+                            PbcType        pbcType,
                             const matrix   box,
                             int            nindex,
                             int            index[]);
@@ -64,7 +66,7 @@ void write_sto_conf(const char*    outfile,
                     const t_atoms* atoms,
                     const rvec     x[],
                     const rvec*    v,
-                    int            ePBC,
+                    PbcType        pbcType,
                     const matrix   box);
 /* write atoms, x, v (if .gro and not NULL) and box (if not NULL)
  * to an STO (.gro or .pdb) file */
@@ -74,7 +76,7 @@ void write_sto_conf_mtop(const char*       outfile,
                          const gmx_mtop_t* mtop,
                          const rvec        x[],
                          const rvec*       v,
-                         int               ePBC,
+                         PbcType           pbcType,
                          const matrix      box);
 /* As write_sto_conf, but uses a gmx_mtop_t struct */
 
@@ -86,7 +88,7 @@ void write_sto_conf_mtop(const char*       outfile,
  * \param[in]     infile        Input file name
  * \param[out]    haveTopology  true when a topology was read and stored in mtop
  * \param[out]    mtop          The topology, either complete or only atom data
- * \param[out]    ePBC          Enum reporting the type of PBC
+ * \param[out]    pbcType       Enum reporting the type of PBC
  * \param[in,out] x             Coordinates will be stored when *x!=NULL
  * \param[in,out] v             Velocities will be stored when *v!=NULL
  * \param[out]    box           Box dimensions
@@ -94,7 +96,7 @@ void write_sto_conf_mtop(const char*       outfile,
 void readConfAndTopology(const char* infile,
                          bool*       haveTopology,
                          gmx_mtop_t* mtop,
-                         int*        ePBC,
+                         PbcType*    pbcType,
                          rvec**      x,
                          rvec**      v,
                          matrix      box);
@@ -107,7 +109,7 @@ void readConfAndTopology(const char* infile,
  * \param[out]    symtab        The symbol table
  * \param[out]    name          The title of the molecule, e.g. from pdb TITLE record
  * \param[out]    atoms         The global t_atoms struct
- * \param[out]    ePBC          Enum reporting the type of PBC
+ * \param[out]    pbcType       Enum reporting the type of PBC
  * \param[in,out] x             Coordinates will be stored when *x!=NULL
  * \param[in,out] v             Velocities will be stored when *v!=NULL
  * \param[out]    box           Box dimensions
@@ -116,7 +118,7 @@ void readConfAndAtoms(const char* infile,
                       t_symtab*   symtab,
                       char**      name,
                       t_atoms*    atoms,
-                      int*        ePBC,
+                      PbcType*    pbcType,
                       rvec**      x,
                       rvec**      v,
                       matrix      box);
@@ -133,7 +135,8 @@ void readConfAndAtoms(const char* infile,
  *
  * \param[in]     infile        Input file name
  * \param[out]    top           The topology, either complete or only atom data. Caller is
- * responsible for calling done_top(). \param[out]    ePBC          Enum reporting the type of PBC
+ *                              responsible for calling done_top().
+ * \param[out]    pbcType       Enum reporting the type of PBC
  * \param[in,out] x             Coordinates will be stored when *x!=NULL
  * \param[in,out] v             Velocities will be stored when *v!=NULL
  * \param[out]    box           Box dimensions
@@ -142,7 +145,7 @@ void readConfAndAtoms(const char* infile,
  */
 gmx_bool read_tps_conf(const char*        infile,
                        struct t_topology* top,
-                       int*               ePBC,
+                       PbcType*           pbcType,
                        rvec**             x,
                        rvec**             v,
                        matrix             box,
index 58d8a9622aae49172667cac5e76aba853e18eaf0..f36118b55187d7ffa5a80207c546ceb3b4574e52 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d5d83a5068773395e002260c466035438fea21c7..18c46aa79cc549abe194f7c667d4d5c425a94fd8 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 4d5580b3d782058486d80d18b8b5e44d251324a7..a380ffafc598e31da5d89a0b47a22620eeb5fef0 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2005, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f22fd1c930b170c545e20028249a6eda70346442..d888c3887d32076db985a28b49cd20073f7e444e 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2005, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 380f91248aaa8ded1e4c47c1a1d85b0b99d24688..6729446ecd33dc47f4bdba90956301823a41bda5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 89c3557f51dd1a9e984f4871cf5c7a5ee6d66b68..e046b03d4dd624bc932ca9ec10d1dfd8db82d62d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 11ab938959d829f475bfacddda3b14fa84aef1be..6c421e5e2e2f6c69c1bc227264e5a39a4833a875 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index ab2b1832962759a887b0e5d5d71df7036a976ec9..cad75e64eb44fadf5b60f3d1dabbfaf8732f21e7 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 9c088a794d33908d491f824b963ef4febfb1550d..7df93fe4ef27ce4eb9b6f9578c9a00b239e1f0df 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 90a65cf9ffc7bcb1cf3765bc9039e46709a00f6f..defdcf89252478536f27f56100e42ec0b3da8dec 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 6f3756d30a50313c3d75268f1188a95d4761a899..45527bcee0806d45bc5f7408f934faad6afbee91 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 0c8c8cc51793bfe2f6f18c594c75e852baea5c3d..3361893f9a93a5bf877699cc4a5b85098283f538 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 683860cda3f7b369bac2653b1a6d1af81aa7269c..2e64a58682fefd055129d30dc6fabb02b87d774d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -61,7 +62,14 @@ const char* xdr_datatype_names[] = { "int", "float", "double", "large int", "cha
  | with some routines to assist in this task (those are marked
  | static and cannot be called from user programs)
  */
-#define MAXABS (INT_MAX - 2)
+
+// Integers above 2^24 do not have unique representations in
+// 32-bit floats ie with 24 bits of precision.  We use maxAbsoluteInt
+// to check that float values can be transformed into an in-range
+// 32-bit integer. There is no need to ensure we are within the range
+// of ints with exact floating-point representations. However, we should
+// reject all floats above that which converts to an in-range 32-bit integer.
+const float maxAbsoluteInt = nextafterf(float(INT_MAX), 0.F); // NOLINT(cert-err58-cpp)
 
 #ifndef SQR
 #    define SQR(x) ((x) * (x))
@@ -489,7 +497,7 @@ int xdr3dfcoord(XDR* xdrs, float* fp, int* size, float* precision)
             {
                 lf = *lfp * *precision - 0.5;
             }
-            if (std::fabs(lf) > MAXABS)
+            if (std::fabs(lf) > maxAbsoluteInt)
             {
                 /* scaling would cause overflow */
                 errval = 0;
@@ -513,7 +521,7 @@ int xdr3dfcoord(XDR* xdrs, float* fp, int* size, float* precision)
             {
                 lf = *lfp * *precision - 0.5;
             }
-            if (std::fabs(lf) > MAXABS)
+            if (std::fabs(lf) > maxAbsoluteInt)
             {
                 /* scaling would cause overflow */
                 errval = 0;
@@ -537,7 +545,7 @@ int xdr3dfcoord(XDR* xdrs, float* fp, int* size, float* precision)
             {
                 lf = *lfp * *precision - 0.5;
             }
-            if (std::abs(lf) > MAXABS)
+            if (std::abs(lf) > maxAbsoluteInt)
             {
                 /* scaling would cause overflow */
                 errval = 0;
@@ -574,9 +582,9 @@ int xdr3dfcoord(XDR* xdrs, float* fp, int* size, float* precision)
             return 0;
         }
 
-        if (static_cast<float>(maxint[0]) - static_cast<float>(minint[0]) >= MAXABS
-            || static_cast<float>(maxint[1]) - static_cast<float>(minint[1]) >= MAXABS
-            || static_cast<float>(maxint[2]) - static_cast<float>(minint[2]) >= MAXABS)
+        if (static_cast<float>(maxint[0]) - static_cast<float>(minint[0]) >= maxAbsoluteInt
+            || static_cast<float>(maxint[1]) - static_cast<float>(minint[1]) >= maxAbsoluteInt
+            || static_cast<float>(maxint[2]) - static_cast<float>(minint[2]) >= maxAbsoluteInt)
         {
             /* turning value in unsigned by subtracting minint
              * would cause overflow
index cbb9c96b79614fa2c4363729e82bafdb5f51a758..8e1873867cb62076882fbcc7718957e8f7e4d93b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e39ce730b883ebd4b2c43e65cf5a8280bb81cba0..eb99ff7f38e8f4a1900c43299903ecf141c85604 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index c7e94e1205b8b52479cda57cd3288b1264ad1f92..945b78bbc1802f041c28c8b508f56ec231073385 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2014,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2014,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 067dc33bc7d305d9347157866f65639b6f0a25d0..de5dad28041eb79c895f8d5f712bc2d3bc53ae99 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7f746567306c96948bce6d7509a65de00c8f9815..aec1d970ac94a26375f37e59c28e8f79e4dd6ecc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/math/multidimarray.h"
 #include "gromacs/mdspan/extensions.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/classhelpers.h"
 
 namespace gmx
 {
-
+template<typename>
+class ArrayRef;
 struct MrcDensityMapHeader;
 class ISerializer;
 class TranslateAndScale;
index ac4968cdf2b7b23a081caf0dbbd6fa6c8c225428..96899c0389beeb70c148bcecb4aeb7bccb6b0629 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -77,7 +77,7 @@ enum class MachineStamp : int32_t
  * \param[in,out] valueContainer the array to be serialized
  */
 template<typename ContainerType>
-std::enable_if_t<std::is_same<typename ContainerType::value_type, int32_t>::value, void>
+std::enable_if_t<std::is_same_v<typename ContainerType::value_type, int32_t>, void>
 serialize(ISerializer* serializer, ContainerType* valueContainer)
 {
     for (auto& value : *valueContainer)
@@ -95,7 +95,7 @@ serialize(ISerializer* serializer, ContainerType* valueContainer)
  * \param[in,out] valueContainer the array to be serialized
  */
 template<typename ContainerType>
-std::enable_if_t<std::is_same<typename ContainerType::value_type, float>::value, void>
+std::enable_if_t<std::is_same_v<typename ContainerType::value_type, float>, void>
 serialize(ISerializer* serializer, ContainerType* valueContainer)
 {
     for (auto& value : *valueContainer)
@@ -134,7 +134,7 @@ void serializeIndices(ISerializer* serializer, std::array<int32_t, 3>* valueArra
  * \tparam IntegralType type to be serialized as int32_t
  */
 template<class IntegralType>
-std::enable_if_t<(std::is_integral<IntegralType>::value || std::is_enum<IntegralType>::value), void>
+std::enable_if_t<(std::is_integral_v<IntegralType> || std::is_enum_v<IntegralType>), void>
 serializeAsInt32(ISerializer* serializer, IntegralType* value)
 {
     int32_t serializedValue;
index 3bbe18303d6be9a7bb17a7d6fd52080ab392d0f5..b4b8cb1f30f655c87b9a14c6e2dc8343d421a2eb 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d8e5ec8e1dc80aeb28367c796b6de08d642c952e..0c99fa191a521704831fc02f86b81c540e23b968 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,6 +39,7 @@
 
 #include "oenv.h"
 
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/programcontext.h"
 #include "gromacs/utility/smalloc.h"
@@ -47,9 +49,9 @@ struct gmx_output_env_t
 {
     explicit gmx_output_env_t(const gmx::IProgramContext& context) :
         programContext(context),
-        time_unit(time_ps),
+        timeUnit(gmx::TimeUnit::Picoseconds),
         view(FALSE),
-        xvg_format(exvgNONE),
+        xvgFormat(XvgFormat::None),
         verbosity(0),
         trajectory_io_verbosity(0)
     {
@@ -58,12 +60,12 @@ struct gmx_output_env_t
 
     const gmx::IProgramContext& programContext;
 
-    /* the time unit, enum defined in oenv.h */
-    time_unit_t time_unit;
+    /* the time unit, enum defined in timeunitmanager.h */
+    gmx::TimeUnit timeUnit;
     /* view of file requested */
     gmx_bool view;
     /* xvg output format, enum defined in oenv.h */
-    xvg_format_t xvg_format;
+    XvgFormat xvgFormat;
     /* The level of verbosity for this program */
     int verbosity;
     /* The level of verbosity during trajectory I/O. Default=1, quiet=0. */
@@ -80,31 +82,36 @@ struct gmx_output_env_t
  ******************************************************************/
 
 /* read only time names */
-/* These must correspond to the time units type time_unit_t in oenv.h */
-static const real  timefactors[]     = { real(0),    real(1e3),  real(1),     real(1e-3),
-                                    real(1e-6), real(1e-9), real(1e-12), real(0) };
-static const real  timeinvfactors[]  = { real(0),   real(1e-3), real(1),    real(1e3),
-                                       real(1e6), real(1e9),  real(1e12), real(0) };
-static const char* time_units_str[]  = { nullptr, "fs", "ps", "ns", "us", "ms", "s" };
-static const char* time_units_xvgr[] = { nullptr, "fs", "ps", "ns", "\\mus", "ms", "s", nullptr };
+static const gmx::EnumerationArray<gmx::TimeUnit, real> c_picosecondsInTimeUnit = {
+    { real(1e3), real(1), real(1e-3), real(1e-6), real(1e-9), real(1e-12) }
+};
+static const gmx::EnumerationArray<gmx::TimeUnit, real> c_timeUnitsInPicoseconds = {
+    { real(1e-3), real(1), real(1e3), real(1e6), real(1e9), real(1e12) }
+};
+static const gmx::EnumerationArray<gmx::TimeUnit, const char*> c_timeUnitNames = {
+    { "fs", "ps", "ns", "us", "ms", "s" }
+};
+static const gmx::EnumerationArray<gmx::TimeUnit, const char*> c_timeUnitNamesForXvgr = {
+    { "fs", "ps", "ns", "\\mus", "ms", "s" }
+};
 
 
 /***** OUTPUT_ENV MEMBER FUNCTIONS ******/
 
 void output_env_init(gmx_output_env_t**          oenvp,
                      const gmx::IProgramContext& context,
-                     time_unit_t                 tmu,
+                     gmx::TimeUnit               tmu,
                      gmx_bool                    view,
-                     xvg_format_t                xvg_format,
+                     XvgFormat                   xvgFormat,
                      int                         verbosity)
 {
     try
     {
         gmx_output_env_t* oenv        = new gmx_output_env_t(context);
         *oenvp                        = oenv;
-        oenv->time_unit               = tmu;
+        oenv->timeUnit                = tmu;
         oenv->view                    = view;
-        oenv->xvg_format              = xvg_format;
+        oenv->xvgFormat               = xvgFormat;
         oenv->verbosity               = verbosity;
         const char* env               = getenv("GMX_TRAJECTORY_IO_VERBOSITY");
         oenv->trajectory_io_verbosity = (env != nullptr ? strtol(env, nullptr, 10) : 1);
@@ -140,40 +147,38 @@ int output_env_get_trajectory_io_verbosity(const gmx_output_env_t* oenv)
 
 std::string output_env_get_time_unit(const gmx_output_env_t* oenv)
 {
-    return time_units_str[oenv->time_unit];
+    return c_timeUnitNames[oenv->timeUnit];
 }
 
 std::string output_env_get_time_label(const gmx_output_env_t* oenv)
 {
-    return gmx::formatString(
-            "Time (%s)", time_units_str[oenv->time_unit] ? time_units_str[oenv->time_unit] : "ps");
+    return gmx::formatString("Time (%s)", c_timeUnitNames[oenv->timeUnit]);
 }
 
 std::string output_env_get_xvgr_tlabel(const gmx_output_env_t* oenv)
 {
-    return gmx::formatString(
-            "Time (%s)", time_units_xvgr[oenv->time_unit] ? time_units_xvgr[oenv->time_unit] : "ps");
+    return gmx::formatString("Time (%s)", c_timeUnitNamesForXvgr[oenv->timeUnit]);
 }
 
 real output_env_get_time_factor(const gmx_output_env_t* oenv)
 {
-    return timefactors[oenv->time_unit];
+    return c_picosecondsInTimeUnit[oenv->timeUnit];
 }
 
 real output_env_get_time_invfactor(const gmx_output_env_t* oenv)
 {
-    return timeinvfactors[oenv->time_unit];
+    return c_timeUnitsInPicoseconds[oenv->timeUnit];
 }
 
 real output_env_conv_time(const gmx_output_env_t* oenv, real time)
 {
-    return time * timefactors[oenv->time_unit];
+    return time * c_picosecondsInTimeUnit[oenv->timeUnit];
 }
 
 void output_env_conv_times(const gmx_output_env_t* oenv, int n, real* time)
 {
     int    i;
-    double fact = timefactors[oenv->time_unit];
+    double fact = c_picosecondsInTimeUnit[oenv->timeUnit];
 
     if (fact != 1.)
     {
@@ -189,9 +194,9 @@ gmx_bool output_env_get_view(const gmx_output_env_t* oenv)
     return oenv->view;
 }
 
-xvg_format_t output_env_get_xvg_format(const gmx_output_env_t* oenv)
+XvgFormat output_env_get_xvg_format(const gmx_output_env_t* oenv)
 {
-    return oenv->xvg_format;
+    return oenv->xvgFormat;
 }
 
 const char* output_env_get_program_display_name(const gmx_output_env_t* oenv)
index 0624e1d77ba51d894203224b07108167cb0e2bd5..586a3916b54688ebdd909e55dd571b3e4f8811c9 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,27 +53,37 @@ struct gmx_output_env_t;
    There are still legacy functions for the program name, and the command
    line, but the output_env versions are now preferred.*/
 
-typedef enum
+namespace gmx
 {
-    timeNULL,
-    time_fs,
-    time_ps,
-    time_ns,
-    time_us,
-    time_ms,
-    time_s
-} time_unit_t;
-/* the time units. For the time being, ps means no conversion. */
-
-typedef enum
+
+/*! \brief
+ * Time values for TimeUnitManager and legacy oenv module.
+ *
+ * \inpublicapi
+ */
+enum class TimeUnit : int
 {
-    exvgNULL,
-    exvgXMGRACE,
-    exvgXMGR,
-    exvgNONE
-} xvg_format_t;
-/* the xvg output formattings */
+    Femtoseconds,
+    Picoseconds,
+    Nanoseconds,
+    Microseconds,
+    Milliseconds,
+    Seconds,
+    Count,
+    Default = Picoseconds
+};
+
+} // namespace gmx
 
+//! The xvg output format
+enum class XvgFormat : int
+{
+    // Select,
+    Xmgrace,
+    Xmgr,
+    None,
+    Count
+};
 
 void output_env_init_default(gmx_output_env_t** oenvp);
 /* initialize an output_env structure, with reasonable default settings.
@@ -113,7 +123,7 @@ void output_env_conv_times(const gmx_output_env_t* oenv, int n, real* time);
 gmx_bool output_env_get_view(const gmx_output_env_t* oenv);
 /* Return TRUE when user requested viewing of the file */
 
-xvg_format_t output_env_get_xvg_format(const gmx_output_env_t* oenv);
+XvgFormat output_env_get_xvg_format(const gmx_output_env_t* oenv);
 /* Returns enum (see above) for xvg output formatting */
 
 /*! \brief
@@ -128,9 +138,9 @@ class IProgramContext;
 
 void output_env_init(gmx_output_env_t**          oenvp,
                      const gmx::IProgramContext& context,
-                     time_unit_t                 tmu,
+                     gmx::TimeUnit               tmu,
                      gmx_bool                    view,
-                     xvg_format_t                xvg_format,
+                     XvgFormat                   xvgFormat,
                      int                         verbosity);
 /* initialize an output_env structure, setting the command line,
    the default time value a gmx_boolean view that is set to TRUE when the
index f431614cc8a512e39d767b6fd97c9a9d9726a755..8e170a04df91d7337abe1d07ed88790959beb7a9 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -112,16 +113,16 @@ static std::string xlate_atomname_gmx2pdb(std::string name)
 }
 
 
-void gmx_write_pdb_box(FILE* out, int ePBC, const matrix box)
+void gmx_write_pdb_box(FILE* out, PbcType pbcType, const matrix box)
 {
     real alpha, beta, gamma;
 
-    if (ePBC == -1)
+    if (pbcType == PbcType::Unset)
     {
-        ePBC = guess_ePBC(box);
+        pbcType = guessPbcType(box);
     }
 
-    if (ePBC == epbcNONE)
+    if (pbcType == PbcType::No)
     {
         return;
     }
@@ -151,7 +152,7 @@ void gmx_write_pdb_box(FILE* out, int ePBC, const matrix box)
         gamma = 90;
     }
     fprintf(out, "REMARK    THIS IS A SIMULATION BOX\n");
-    if (ePBC != epbcSCREW)
+    if (pbcType != PbcType::Screw)
     {
         fprintf(out, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n", 10 * norm(box[XX]),
                 10 * norm(box[YY]), 10 * norm(box[ZZ]), alpha, beta, gamma, "P 1", 1);
@@ -164,17 +165,17 @@ void gmx_write_pdb_box(FILE* out, int ePBC, const matrix box)
     }
 }
 
-static void read_cryst1(char* line, int* ePBC, matrix box)
+static void read_cryst1(char* line, PbcType* pbcType, matrix box)
 {
 #define SG_SIZE 11
-    char   sa[12], sb[12], sc[12], sg[SG_SIZE + 1], ident;
-    double fa, fb, fc, alpha, beta, gamma, cosa, cosb, cosg, sing;
-    int    syma, symb, symc;
-    int    ePBC_file;
+    char    sa[12], sb[12], sc[12], sg[SG_SIZE + 1], ident;
+    double  fa, fb, fc, alpha, beta, gamma, cosa, cosb, cosg, sing;
+    int     syma, symb, symc;
+    PbcType pbcTypeFile;
 
     sscanf(line, "%*s%s%s%s%lf%lf%lf", sa, sb, sc, &alpha, &beta, &gamma);
 
-    ePBC_file = -1;
+    pbcTypeFile = PbcType::Unset;
     if (strlen(line) >= 55)
     {
         strncpy(sg, line + 55, SG_SIZE);
@@ -186,17 +187,17 @@ static void read_cryst1(char* line, int* ePBC, matrix box)
         sscanf(sg, "%c %d %d %d", &ident, &syma, &symb, &symc);
         if (ident == 'P' && syma == 1 && symb <= 1 && symc <= 1)
         {
-            fc        = strtod(sc, nullptr) * 0.1;
-            ePBC_file = (fc > 0 ? epbcXYZ : epbcXY);
+            fc          = strtod(sc, nullptr) * 0.1;
+            pbcTypeFile = (fc > 0 ? PbcType::Xyz : PbcType::XY);
         }
         if (ident == 'P' && syma == 21 && symb == 1 && symc == 1)
         {
-            ePBC_file = epbcSCREW;
+            pbcTypeFile = PbcType::Screw;
         }
     }
-    if (ePBC)
+    if (pbcType)
     {
-        *ePBC = ePBC_file;
+        *pbcType = pbcTypeFile;
     }
 
     if (box)
@@ -204,7 +205,7 @@ static void read_cryst1(char* line, int* ePBC, matrix box)
         fa = strtod(sa, nullptr) * 0.1;
         fb = strtod(sb, nullptr) * 0.1;
         fc = strtod(sc, nullptr) * 0.1;
-        if (ePBC_file == epbcSCREW)
+        if (pbcTypeFile == PbcType::Screw)
         {
             fa *= 0.5;
         }
@@ -289,7 +290,7 @@ void write_pdbfile_indexed(FILE*          out,
                            const char*    title,
                            const t_atoms* atoms,
                            const rvec     x[],
-                           int            ePBC,
+                           PbcType        pbcType,
                            const matrix   box,
                            char           chainid,
                            int            model_nr,
@@ -308,7 +309,7 @@ void write_pdbfile_indexed(FILE*          out,
     fprintf(out, "TITLE     %s\n", (title && title[0]) ? title : gmx::bromacs().c_str());
     if (box && ((norm2(box[XX]) != 0.0F) || (norm2(box[YY]) != 0.0F) || (norm2(box[ZZ]) != 0.0F)))
     {
-        gmx_write_pdb_box(out, ePBC, box);
+        gmx_write_pdb_box(out, pbcType, box);
     }
     if (atoms->havePdbInfo)
     {
@@ -414,7 +415,7 @@ void write_pdbfile(FILE*          out,
                    const char*    title,
                    const t_atoms* atoms,
                    const rvec     x[],
-                   int            ePBC,
+                   PbcType        pbcType,
                    const matrix   box,
                    char           chainid,
                    int            model_nr,
@@ -427,7 +428,7 @@ void write_pdbfile(FILE*          out,
     {
         index[i] = i;
     }
-    write_pdbfile_indexed(out, title, atoms, x, ePBC, box, chainid, model_nr, atoms->nr, index,
+    write_pdbfile_indexed(out, title, atoms, x, pbcType, box, chainid, model_nr, atoms->nr, index,
                           conect, false);
     sfree(index);
 }
@@ -729,15 +730,7 @@ gmx_bool is_hydrogen(const char* nm)
     std::strcpy(buf, nm);
     trim(buf);
 
-    if (buf[0] == 'H')
-    {
-        return TRUE;
-    }
-    else if ((std::isdigit(buf[0])) && (buf[1] == 'H'))
-    {
-        return TRUE;
-    }
-    return FALSE;
+    return ((buf[0] == 'H') || ((std::isdigit(buf[0]) != 0) && (buf[1] == 'H')));
 }
 
 gmx_bool is_dummymass(const char* nm)
@@ -838,7 +831,7 @@ int read_pdbfile(FILE*      in,
                  t_atoms*   atoms,
                  t_symtab*  symtab,
                  rvec       x[],
-                 int*       ePBC,
+                 PbcType*   pbcType,
                  matrix     box,
                  gmx_bool   bChange,
                  gmx_conect conect)
@@ -852,10 +845,10 @@ int read_pdbfile(FILE*      in,
     int           natom, chainnum;
     gmx_bool      bStop = FALSE;
 
-    if (ePBC)
+    if (pbcType)
     {
         /* Only assume pbc when there is a CRYST1 entry */
-        *ePBC = epbcNONE;
+        *pbcType = PbcType::No;
     }
     if (box != nullptr)
     {
@@ -890,7 +883,7 @@ int read_pdbfile(FILE*      in,
                 }
                 break;
 
-            case epdbCRYST1: read_cryst1(line, ePBC, box); break;
+            case epdbCRYST1: read_cryst1(line, pbcType, box); break;
 
             case epdbTITLE:
             case epdbHEADER:
@@ -1008,11 +1001,11 @@ void get_pdb_coordnum(FILE* in, int* natoms)
     }
 }
 
-void gmx_pdb_read_conf(const char* infile, t_symtab* symtab, char** name, t_atoms* atoms, rvec x[], int* ePBC, matrix box)
+void gmx_pdb_read_conf(const char* infile, t_symtab* symtab, char** name, t_atoms* atoms, rvec x[], PbcType* pbcType, matrix box)
 {
     FILE* in = gmx_fio_fopen(infile, "r");
     char  title[STRLEN];
-    read_pdbfile(in, title, nullptr, atoms, symtab, x, ePBC, box, TRUE, nullptr);
+    read_pdbfile(in, title, nullptr, atoms, symtab, x, pbcType, box, TRUE, nullptr);
     if (name != nullptr)
     {
         *name = gmx_strdup(title);
index 0d9ea634b668342423d3581121cd329a90f06708..d9e918c27590854c3ef0086c73e44cf8a3059c60 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,6 +50,7 @@ class AtomProperties;
 struct t_atoms;
 struct t_symtab;
 struct t_topology;
+enum class PbcType : int;
 
 typedef struct gmx_conect_t* gmx_conect;
 
@@ -87,9 +89,9 @@ void pdb_use_ter(gmx_bool bSet);
 /* set read_pdbatoms to read upto 'TER' or 'ENDMDL' (default, bSet=FALSE).
    This function is fundamentally broken as far as thread-safety is concerned.*/
 
-void gmx_write_pdb_box(FILE* out, int ePBC, const matrix box);
+void gmx_write_pdb_box(FILE* out, PbcType pbcType, const matrix box);
 /* write the box in the CRYST1 record,
- * with ePBC=-1 the pbc is guessed from the box
+ * with pbcType=PbcType::Unset the pbc is guessed from the box
  * This function is fundamentally broken as far as thread-safety is concerned.
  */
 
@@ -97,7 +99,7 @@ void write_pdbfile_indexed(FILE*          out,
                            const char*    title,
                            const t_atoms* atoms,
                            const rvec     x[],
-                           int            ePBC,
+                           PbcType        pbcType,
                            const matrix   box,
                            char           chain,
                            int            model_nr,
@@ -111,7 +113,7 @@ void write_pdbfile(FILE*          out,
                    const char*    title,
                    const t_atoms* atoms,
                    const rvec     x[],
-                   int            ePBC,
+                   PbcType        pbcType,
                    const matrix   box,
                    char           chain,
                    int            model_nr,
@@ -138,18 +140,24 @@ int read_pdbfile(FILE*            in,
                  struct t_atoms*  atoms,
                  struct t_symtab* symtab,
                  rvec             x[],
-                 int*             ePBC,
+                 PbcType*         pbcType,
                  matrix           box,
                  gmx_bool         bChange,
                  gmx_conect       conect);
 /* Function returns number of atoms found.
- * ePBC and gmx_conect structure may be NULL.
+ * pbcType and gmx_conect structure may be NULL.
  */
 
-void gmx_pdb_read_conf(const char* infile, t_symtab* symtab, char** name, t_atoms* atoms, rvec x[], int* ePBC, matrix box);
+void gmx_pdb_read_conf(const char* infile,
+                       t_symtab*   symtab,
+                       char**      name,
+                       t_atoms*    atoms,
+                       rvec        x[],
+                       PbcType*    pbcType,
+                       matrix      box);
 /* Read a pdb file and extract ATOM and HETATM fields.
  * Read a box from the CRYST1 line, return 0 box when no CRYST1 is found.
- * ePBC may be NULL.
+ * pbcType may be NULL.
  *
  * If name is not nullptr, gmx_strdup the title string into it. */
 
index 6c343b2143397826b300c5f0c0d6f385ac13aecc..5d497b8cd740c9a518dda0eb1eff25bbfb12770e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,6 +45,7 @@
 #include <algorithm>
 
 #include "gromacs/fileio/warninp.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/binaryinformation.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/exceptions.h"
@@ -450,7 +452,6 @@ double get_ereal(std::vector<t_inpfile>* inp, const std::string& name, double de
 const char* get_estr(std::vector<t_inpfile>* inp, const char* name, const char* def)
 {
     std::vector<t_inpfile>& inpRef = *inp;
-    char                    buf[32];
 
     int ii = get_einp(inp, name);
 
@@ -458,8 +459,7 @@ const char* get_estr(std::vector<t_inpfile>* inp, const char* name, const char*
     {
         if (def)
         {
-            sprintf(buf, "%s", def);
-            inpRef.back().value_.assign(buf);
+            inpRef.back().value_.assign(def);
         }
         else
         {
@@ -554,8 +554,11 @@ void printStringNoNewline(std::vector<t_inpfile>* inp, const char* line)
     tmp.append(line);
     get_estr(inp, tmp.c_str(), nullptr);
 }
+
 void setStringEntry(std::vector<t_inpfile>* inp, const char* name, char* newName, const char* def)
 {
+    GMX_RELEASE_ASSERT(newName != nullptr, "Need a valid char buffer");
+
     const char* found = nullptr;
     found             = get_estr(inp, name, def);
     if (found != nullptr)
@@ -563,3 +566,10 @@ void setStringEntry(std::vector<t_inpfile>* inp, const char* name, char* newName
         std::strcpy(newName, found);
     }
 }
+
+std::string setStringEntry(std::vector<t_inpfile>* inp, const std::string& name, const std::string& def)
+{
+    GMX_RELEASE_ASSERT(!name.empty(), "Need a valid string");
+
+    return get_estr(inp, name.c_str(), def.c_str());
+}
index 10d2952d94409c6072261ec4826b5d8936f4c365..37034e7c7dc234a5afe3739cf2091c0ec165a8b7 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,7 +44,6 @@
 #include <utility>
 #include <vector>
 
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 
 struct warninp;
@@ -51,6 +51,8 @@ typedef warninp* warninp_t;
 
 namespace gmx
 {
+template<typename>
+class ArrayRef;
 class KeyValueTreeObject;
 class TextInputStream;
 class TextOutputStream;
@@ -165,4 +167,13 @@ void printStringNoNewline(std::vector<t_inpfile>* inp, const char* line);
 //! Replace for macro STYPE, checks for existing string entry and if possible replaces it
 void setStringEntry(std::vector<t_inpfile>* inp, const char* name, char* newName, const char* def);
 
+/*! \brief
+ * Returns a string value and sets the value in \p inp
+ *
+ * The value is either from \p inp when \p name is found or \p def otherwise.
+ *
+ * \note this is a wrapper function for g_estr()
+ */
+std::string setStringEntry(std::vector<t_inpfile>* inp, const std::string& name, const std::string& def);
+
 #endif
index 2f6e3e5deeb4166d622107702d24bb97a6621514..5017b67f4c53d98e9e17ce20f8ab08888da68dbf 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-set(test_sources
-    confio.cpp
-    filemd5.cpp
-    mrcserializer.cpp
-    mrcdensitymap.cpp
-    mrcdensitymapheader.cpp
-    readinp.cpp
-    fileioxdrserializer.cpp
-    )
 if (GMX_USE_TNG)
-    list(APPEND test_sources tngio.cpp)
+    set(tng_sources tngio.cpp)
 endif()
-gmx_add_unit_test(FileIOTests fileio-test ${test_sources})
+gmx_add_unit_test(FileIOTests fileio-test
+    CPP_SOURCE_FILES
+        confio.cpp
+        filemd5.cpp
+        mrcserializer.cpp
+        mrcdensitymap.cpp
+        mrcdensitymapheader.cpp
+        readinp.cpp
+        fileioxdrserializer.cpp
+        ${tng_sources}
+        xvgio.cpp
+    )
diff --git a/src/gromacs/fileio/tests/checkpoint.cpp b/src/gromacs/fileio/tests/checkpoint.cpp
new file mode 100644 (file)
index 0000000..f368954
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Tests routines in checkpoint.h .
+ *
+ * \author Christian Blau <blau@kth.se>
+ */
+
+#include "gmxpre.h"
+
+#include "gromacs/fileio/checkpoint.h"
+
+#include <gtest/gtest.h>
+
+#include "gromacs/utility/exceptions.h"
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+TEST(Checkpoint, ReadingThrowsWhenValueNotPresent)
+{
+    KeyValueTreeObject kvtObject;
+    std::int64_t       readValue;
+    EXPECT_THROW_GMX(readKvtCheckpointValue(compat::make_not_null(&readValue), "non", "sense", kvtObject),
+                     InternalError);
+}
+
+TEST(Checkpoint, ReadingDoesNotThrowWhenValuePresent)
+{
+    int64_t             value;
+    std::string         name       = "checkpointedInteger";
+    std::string         identifier = "testingmodule";
+    KeyValueTreeBuilder kvtBuilder;
+    writeKvtCheckpointValue(value, name, identifier, kvtBuilder.rootObject());
+    const auto kvtObject = kvtBuilder.build();
+    int64_t    readValue = 0;
+    EXPECT_NO_THROW_GMX(readKvtCheckpointValue(compat::make_not_null(&readValue), "non", "sense", kvtObject),
+                        InternalError);
+}
+
+TEST(Checkpoint, KvtRoundTripInt64)
+{
+    int64_t             value;
+    std::string         name       = "checkpointedInteger";
+    std::string         identifier = "testingmodule";
+    KeyValueTreeBuilder kvtBuilder;
+    writeKvtCheckpointValue(value, name, identifier, kvtBuilder.rootObject());
+    const auto kvtObject = kvtBuilder.build();
+    int64_t    readValue = 0;
+    readKvtCheckpointValue(compat::make_not_null(&readValue), name, identifier, kvtObject);
+    EXPECT_EQ(value, readValue);
+}
+
+TEST(Checkpoint, KvtRoundTripReal)
+{
+    real                value;
+    std::string         name       = "checkpointedInteger";
+    std::string         identifier = "testingmodule";
+    KeyValueTreeBuilder kvtBuilder;
+    writeKvtCheckpointValue(value, name, identifier, kvtBuilder.rootObject());
+    const auto kvtObject = kvtBuilder.build();
+    real       readValue = 0;
+    readKvtCheckpointValue(compat::make_not_null(&readValue), name, identifier, kvtObject);
+    EXPECT_EQ(value, readValue);
+}
+
+
+} // namespace
+} // namespace test
+} // namespace gmx
index e0f6c7231f271214ca7ffcc3b600a1786975b506..53a1216ff3352abfbdd0008996f0ac1f1dd15fc2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,6 +52,7 @@
 #include "gromacs/fileio/filetypes.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/math/vectypes.h"
+#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/atoms.h"
 #include "gromacs/topology/symtab.h"
 #include "gromacs/topology/topology.h"
@@ -103,14 +104,14 @@ public:
     void writeReferenceFile()
     {
         write_sto_conf(referenceFilename_.c_str(), *refTop_->name, &refTop_->atoms,
-                       as_rvec_array(refX_.data()), nullptr, -1, refBox_);
+                       as_rvec_array(refX_.data()), nullptr, PbcType::Unset, refBox_);
     }
 
     void readReferenceFileTps()
     {
         snew(testTop_, 1);
-        int ePBC = -2;
-        read_tps_conf(referenceFilename_.c_str(), testTop_, &ePBC, &testX_, nullptr, testBox_, FALSE);
+        PbcType pbcType = PbcType::Unset;
+        read_tps_conf(referenceFilename_.c_str(), testTop_, &pbcType, &testX_, nullptr, testBox_, FALSE);
     }
 
     void testTopologies()
@@ -121,7 +122,7 @@ public:
     void writeTestFileAndTest()
     {
         write_sto_conf(testFilename_.c_str(), *testTop_->name, &testTop_->atoms, testX_, nullptr,
-                       -1, testBox_);
+                       PbcType::Unset, testBox_);
         testFilesEqual(referenceFilename_, testFilename_);
     }
 
index 1523ab20dbc8e8c38b3c921fc2393615c1e96f1d..2c5d9b0c4309632435361ff75897d569801a74d5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -103,7 +103,7 @@ public:
         std::int64_t   int64Value_         = c_int64Value;
         double         doubleValue_        = c_intAndFloat64.doubleValue_;
         int            intValue_           = integerSizeDependentTestingValue();
-        real           realValue_          = std::is_same<real, double>::value
+        real           realValue_          = std::is_same_v<real, double>
                                   ? static_cast<real>(c_intAndFloat64.doubleValue_)
                                   : static_cast<real>(c_intAndFloat32.floatValue_);
     } defaultValues_;
index e9e17df359a87e4d0f267058456ed815e2964cd8..0cc8580d654377e70ba5772e5bb179715ccae5b4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,6 +50,8 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "gromacs/utility/arrayref.h"
+
 #include "testutils/testasserts.h"
 #include "testutils/testmatchers.h"
 
index b1aa6641828bd1c4328445bd135c2ff4bd188f4d..343a15f19b9b8e6f6bf837fc94ca82c6339cbb46 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -131,5 +131,16 @@ TEST_F(ReadTest, get_ereal_WarnsAboutString)
     ASSERT_TRUE(warning_errors_exist(wi_));
 }
 
+TEST_F(ReadTest, setStringEntry_ReturnsCorrectString)
+{
+    const std::string name        = "name";
+    const std::string definition  = "definition";
+    const std::string returnValue = setStringEntry(&inputField_, name, definition);
+    // The definition should be returned
+    EXPECT_EQ(returnValue, definition);
+    // The name should not be returned
+    EXPECT_NE(returnValue, name);
+}
+
 } // namespace testing
 } // namespace gmx
index 3a56def8058930c393151c8e2b57a77ad1d95deb..7025a94a418a6d99a6b7fb475cd2005b39c56eb7 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
diff --git a/src/gromacs/fileio/tests/xvgio.cpp b/src/gromacs/fileio/tests/xvgio.cpp
new file mode 100644 (file)
index 0000000..20d82b5
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements tests for xvg file operations
+ *
+ * \author Joe Jordan <ejjordan@kth.se>
+ */
+#include "gmxpre.h"
+
+#include <numeric>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/fileio/xvgr.h"
+#include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/textwriter.h"
+
+#include "testutils/testfilemanager.h"
+#include "testutils/testoptions.h"
+
+namespace gmx
+{
+namespace test
+{
+
+class XvgioTest : public ::testing::Test
+{
+public:
+    XvgioTest() { referenceFilename_ = fileManager_.getTemporaryFilePath("ref.xvg"); }
+
+    const std::string& referenceFilename() const { return referenceFilename_; }
+
+    const std::string& referenceContents() const { return referenceContents_; }
+
+    void useStringAsXvgFile(const std::string& xvgString) { referenceContents_ = xvgString; }
+
+    void writeXvgFile()
+    {
+        gmx::TextWriter::writeFileFromString(referenceFilename(), referenceContents());
+    }
+
+    static void compareValues(basic_mdspan<const double, dynamicExtents2D> ref,
+                              basic_mdspan<const double, dynamicExtents2D> test)
+    {
+        // The xvg reading routines use a column-major layout, while we would
+        // like to enforce row major behaviour everywhere else. This requires
+        // this test to swap the orders between reference data and test data.
+        // Hence, we compare extent(0) with extent(1) and [i][j] with [j][i].
+        EXPECT_EQ(ref.extent(0), test.extent(1));
+        EXPECT_EQ(ref.extent(1), test.extent(0));
+
+        for (std::ptrdiff_t i = 0; i < ref.extent(0); i++)
+        {
+            for (std::ptrdiff_t j = 0; j < ref.extent(1); j++)
+            {
+                EXPECT_DOUBLE_EQ(ref[i][j], test[j][i]);
+            }
+        }
+    }
+
+private:
+    gmx::test::TestFileManager fileManager_;
+    std::string                referenceFilename_;
+    std::string                referenceContents_;
+};
+
+TEST_F(XvgioTest, readXvgIntWorks)
+{
+    useStringAsXvgFile(
+            "1 2 3\n"
+            "4 5 6\n");
+    writeXvgFile();
+    MultiDimArray<std::vector<double>, dynamicExtents2D> xvgTestData = readXvgData(referenceFilename());
+
+    const int                                            numRows    = 2;
+    const int                                            numColumns = 3;
+    MultiDimArray<std::vector<double>, dynamicExtents2D> xvgRefData(numRows, numColumns);
+    std::iota(begin(xvgRefData), end(xvgRefData), 1);
+
+    compareValues(xvgRefData.asConstView(), xvgTestData.asConstView());
+}
+
+TEST_F(XvgioTest, readXvgRealWorks)
+{
+    useStringAsXvgFile(
+            "1.1 2.2\n"
+            "3.3 4.4\n"
+            "5.5 6.6\n");
+    writeXvgFile();
+    MultiDimArray<std::vector<double>, dynamicExtents2D> xvgTestData = readXvgData(referenceFilename());
+
+    const int                                            numRows    = 3;
+    const int                                            numColumns = 2;
+    MultiDimArray<std::vector<double>, dynamicExtents2D> xvgRefData(numRows, numColumns);
+    std::generate(begin(xvgRefData), end(xvgRefData), [n = 0.0]() mutable {
+        n += 1.1;
+        return n;
+    });
+    compareValues(xvgRefData.asConstView(), xvgTestData.asConstView());
+}
+
+TEST_F(XvgioTest, readXvgIgnoreCommentLineWorks)
+{
+    useStringAsXvgFile(
+            "1 2 3\n"
+            "#comment\n"
+            "4 5 6\n");
+    writeXvgFile();
+
+    MultiDimArray<std::vector<double>, dynamicExtents2D> xvgTestData = readXvgData(referenceFilename());
+
+    const int                                            numRows    = 2;
+    const int                                            numColumns = 3;
+    MultiDimArray<std::vector<double>, dynamicExtents2D> xvgRefData(numRows, numColumns);
+    std::iota(begin(xvgRefData), end(xvgRefData), 1);
+
+    compareValues(xvgRefData.asConstView(), xvgTestData.asConstView());
+}
+
+// TODO Remove this test once all calls to read_xvg have been ported to readXvgData
+TEST_F(XvgioTest, readXvgDeprecatedWorks)
+{
+    useStringAsXvgFile(
+            "1 2 3\n"
+            "4 5 6\n");
+    writeXvgFile();
+    std::vector<std::vector<double>> xvgData = { { 1, 4 }, { 2, 5 }, { 3, 6 } };
+
+    double** xvgTestData = nullptr;
+    int      testNumColumns;
+    int      testNumRows = read_xvg(referenceFilename().c_str(), &xvgTestData, &testNumColumns);
+
+    double** xvgRefData    = nullptr;
+    int      refNumColumns = 3;
+    int      refNumRows    = 2;
+
+    EXPECT_EQ(refNumColumns, testNumColumns);
+    EXPECT_EQ(refNumRows, testNumRows);
+
+    // Set the reference data
+    snew(xvgRefData, refNumColumns);
+    for (int column = 0; column < refNumColumns; column++)
+    {
+        snew(xvgRefData[column], refNumRows);
+        for (int row = 0; row < refNumRows; row++)
+        {
+            xvgRefData[column][row] = xvgData[column][row];
+        }
+    }
+
+    // Check that the reference and test data match
+    for (int column = 0; column < refNumColumns; column++)
+    {
+        for (int row = 0; row < refNumRows; row++)
+        {
+            EXPECT_EQ(xvgRefData[column][row], xvgTestData[column][row]);
+        }
+    }
+
+    // Free the reference and test data memory
+    for (int column = 0; column < refNumColumns; column++)
+    {
+        sfree(xvgRefData[column]);
+        sfree(xvgTestData[column]);
+    }
+    sfree(xvgRefData);
+    sfree(xvgTestData);
+}
+
+} // namespace test
+} // namespace gmx
index f8a9ea661bb888e83fed65714894f232fe4cbed9..a872a8793b69083d340baaced6b7353ee3be53a6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +43,7 @@
 #include <algorithm>
 #include <iterator>
 #include <memory>
+#include <numeric>
 #include <vector>
 
 #if GMX_USE_TNG
@@ -372,7 +373,7 @@ static int greatest_common_divisor_if_positive(int n1, int n2)
     }
 
     /* We have a non-trivial greatest common divisor to compute. */
-    return gmx_greatest_common_divisor(n1, n2);
+    return std::gcd(n1, n2);
 }
 
 /* By default try to write 100 frames (of actual output) in each frame set.
index d3e2273f0938d07f4eef33b03f55dd319f873e99..6a33e33d9699695c6c65537783220aa19fe5ad38 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -39,7 +40,6 @@
 #include <cstdio>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
@@ -49,6 +49,11 @@ struct gmx_tng_trajectory;
 typedef struct gmx_tng_trajectory* gmx_tng_trajectory_t;
 struct t_trxframe;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
 /*! \brief Open a TNG trajectory file
  *
  * \param filename   Name of file to open
index b5906dca12b0c1140b40971af5cce1c45ffac0ea..9ebd98f4b3e29c87734289ee10b2147f68122351 100644 (file)
@@ -58,6 +58,7 @@
 #include "gromacs/mdtypes/awh_params.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/multipletimestepping.h"
 #include "gromacs/mdtypes/pull_params.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/pbcutil/boxutilities.h"
@@ -130,7 +131,10 @@ enum tpxv
     tpxv_GenericInternalParameters, /**< Added internal parameters for mdrun modules*/
     tpxv_VSite2FD,                  /**< Added 2FD type virtual site */
     tpxv_AddSizeField, /**< Added field with information about the size of the serialized tpr file in bytes, excluding the header */
-    tpxv_Count         /**< the total number of tpxv versions */
+    tpxv_StoreNonBondedInteractionExclusionGroup, /**< Store the non bonded interaction exclusion group in the topology */
+    tpxv_VSite1,                                  /**< Added 1 type virtual site */
+    tpxv_MTS,                                     /**< Added multiple time stepping */
+    tpxv_Count                                    /**< the total number of tpxv versions */
 };
 
 /*! \brief Version number of the file format written to run input
@@ -206,6 +210,7 @@ static const t_ftupd ftupd[] = {
     { 93, F_LJ_RECIP },
     { 76, F_ANHARM_POL },
     { 90, F_FBPOSRES },
+    { tpxv_VSite1, F_VSITE1 },
     { tpxv_VSite2FD, F_VSITE2FD },
     { tpxv_GenericInternalParameters, F_DENSITYFITTING },
     { 69, F_VTEMP_NOLONGERUSED },
@@ -562,6 +567,10 @@ static void do_fepvals(gmx::ISerializer* serializer, t_lambda* fepvals, int file
     {
         fepvals->sc_r_power = 6.0;
     }
+    if (fepvals->sc_r_power != 6.0)
+    {
+        gmx_fatal(FARGS, "sc-r-power=48 is no longer supported");
+    }
     serializer->doReal(&fepvals->sc_sigma);
     if (serializer->reading())
     {
@@ -1075,6 +1084,29 @@ static void do_inputrec(gmx::ISerializer* serializer, t_inputrec* ir, int file_v
 
     serializer->doInt(&ir->simulation_part);
 
+    if (file_version >= tpxv_MTS)
+    {
+        serializer->doBool(&ir->useMts);
+        int numLevels = ir->mtsLevels.size();
+        if (ir->useMts)
+        {
+            serializer->doInt(&numLevels);
+        }
+        ir->mtsLevels.resize(numLevels);
+        for (auto& mtsLevel : ir->mtsLevels)
+        {
+            int forceGroups = mtsLevel.forceGroups.to_ulong();
+            serializer->doInt(&forceGroups);
+            mtsLevel.forceGroups = std::bitset<static_cast<int>(gmx::MtsForceGroups::Count)>(forceGroups);
+            serializer->doInt(&mtsLevel.stepFactor);
+        }
+    }
+    else
+    {
+        ir->useMts = false;
+        ir->mtsLevels.clear();
+    }
+
     if (file_version >= 67)
     {
         serializer->doInt(&ir->nstcalcenergy);
@@ -1151,20 +1183,14 @@ static void do_inputrec(gmx::ISerializer* serializer, t_inputrec* ir, int file_v
             real dummy_rlistlong = -1;
             serializer->doReal(&dummy_rlistlong);
 
-            if (ir->rlist > 0 && (dummy_rlistlong == 0 || dummy_rlistlong > ir->rlist))
-            {
-                // Get mdrun to issue an error (regardless of
-                // ir->cutoff_scheme).
-                ir->useTwinRange = true;
-            }
-            else
-            {
-                // grompp used to set rlistlong actively. Users were
-                // probably also confused and set rlistlong == rlist.
-                // However, in all remaining cases, it is safe to let
-                // mdrun proceed normally.
-                ir->useTwinRange = false;
-            }
+            ir->useTwinRange = (ir->rlist > 0 && (dummy_rlistlong == 0 || dummy_rlistlong > ir->rlist));
+            // When true, this forces mdrun to issue an error (regardless of
+            // ir->cutoff_scheme).
+            //
+            // Otherwise, grompp used to set rlistlong actively. Users
+            // were probably also confused and set rlistlong == rlist.
+            // However, in all remaining cases, it is safe to let
+            // mdrun proceed normally.
         }
     }
     else
@@ -1347,14 +1373,6 @@ static void do_inputrec(gmx::ISerializer* serializer, t_inputrec* ir, int file_v
     if (file_version >= 79)
     {
         serializer->doBool(&ir->bExpanded);
-        if (ir->bExpanded)
-        {
-            ir->bExpanded = TRUE;
-        }
-        else
-        {
-            ir->bExpanded = FALSE;
-        }
     }
     if (ir->bExpanded)
     {
@@ -1620,43 +1638,43 @@ static void do_inputrec(gmx::ISerializer* serializer, t_inputrec* ir, int file_v
         }
     }
 
-    /* QMMM stuff */
+    /* QMMM reading - despite defunct we require reading for backwards
+     * compability and correct serialization
+     */
     {
         serializer->doBool(&ir->bQMMM);
-        serializer->doInt(&ir->QMMMscheme);
-        serializer->doReal(&ir->scalefactor);
+        int qmmmScheme;
+        serializer->doInt(&qmmmScheme);
+        real unusedScalefactor;
+        serializer->doReal(&unusedScalefactor);
+
+        // this is still used in Mimic
         serializer->doInt(&ir->opts.ngQM);
-        if (serializer->reading())
-        {
-            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);
-        }
         if (ir->opts.ngQM > 0 && ir->bQMMM)
         {
-            serializer->doIntArray(ir->opts.QMmethod, ir->opts.ngQM);
-            serializer->doIntArray(ir->opts.QMbasis, ir->opts.ngQM);
-            serializer->doIntArray(ir->opts.QMcharge, ir->opts.ngQM);
-            serializer->doIntArray(ir->opts.QMmult, ir->opts.ngQM);
-            serializer->doBoolArray(ir->opts.bSH, ir->opts.ngQM);
-            serializer->doIntArray(ir->opts.CASorbitals, ir->opts.ngQM);
-            serializer->doIntArray(ir->opts.CASelectrons, ir->opts.ngQM);
-            serializer->doRealArray(ir->opts.SAon, ir->opts.ngQM);
-            serializer->doRealArray(ir->opts.SAoff, ir->opts.ngQM);
-            serializer->doIntArray(ir->opts.SAsteps, ir->opts.ngQM);
             /* We leave in dummy i/o for removed parameters to avoid
-             * changing the tpr format for every QMMM change.
+             * changing the tpr format.
              */
-            std::vector<int> dummy(ir->opts.ngQM, 0);
-            serializer->doIntArray(dummy.data(), ir->opts.ngQM);
-            serializer->doIntArray(dummy.data(), ir->opts.ngQM);
+            std::vector<int> dummyIntVec(4 * ir->opts.ngQM, 0);
+            serializer->doIntArray(dummyIntVec.data(), dummyIntVec.size());
+            dummyIntVec.clear();
+
+            // std::vector<bool> has no data()
+            std::vector<char> dummyBoolVec(ir->opts.ngQM * sizeof(bool) / sizeof(char));
+            serializer->doBoolArray(reinterpret_cast<bool*>(dummyBoolVec.data()), dummyBoolVec.size());
+            dummyBoolVec.clear();
+
+            dummyIntVec.resize(2 * ir->opts.ngQM, 0);
+            serializer->doIntArray(dummyIntVec.data(), dummyIntVec.size());
+            dummyIntVec.clear();
+
+            std::vector<real> dummyRealVec(2 * ir->opts.ngQM, 0);
+            serializer->doRealArray(dummyRealVec.data(), dummyRealVec.size());
+            dummyRealVec.clear();
+
+            dummyIntVec.resize(3 * ir->opts.ngQM, 0);
+            serializer->doIntArray(dummyIntVec.data(), dummyIntVec.size());
+            dummyIntVec.clear();
         }
         /* end of QMMM stuff */
     }
@@ -1932,9 +1950,7 @@ static void do_iparams(gmx::ISerializer* serializer, t_functype ftype, t_iparams
             break;
         case F_CBTDIHS: serializer->doRealArray(iparams->cbtdihs.cbtcA, NR_CBTDIHS); break;
         case F_RBDIHS:
-            serializer->doRealArray(iparams->rbdihs.rbcA, NR_RBDIHS);
-            serializer->doRealArray(iparams->rbdihs.rbcB, NR_RBDIHS);
-            break;
+            // Fall-through intended
         case F_FOURDIHS:
             /* Fourier dihedrals are internally represented
              * as Ryckaert-Bellemans since those are faster to compute.
@@ -1951,6 +1967,7 @@ static void do_iparams(gmx::ISerializer* serializer, t_functype ftype, t_iparams
             serializer->doReal(&iparams->settle.doh);
             serializer->doReal(&iparams->settle.dhh);
             break;
+        case F_VSITE1: break; // VSite1 has 0 parameters
         case F_VSITE2:
         case F_VSITE2FD: serializer->doReal(&iparams->vsite.a); break;
         case F_VSITE3:
@@ -2103,7 +2120,7 @@ static void do_ilists(gmx::ISerializer* serializer, InteractionLists* ilists, in
         else
         {
             do_ilist(serializer, &ilist);
-            if (file_version < 78 && j == F_SETTLE && ilist.size() > 0)
+            if (file_version < 78 && j == F_SETTLE && !ilist.empty())
             {
                 add_settle_atoms(&ilist);
             }
@@ -2126,19 +2143,25 @@ static void do_block(gmx::ISerializer* serializer, t_block* block)
     serializer->doIntArray(block->index, block->nr + 1);
 }
 
-static void do_blocka(gmx::ISerializer* serializer, t_blocka* block)
+static void doListOfLists(gmx::ISerializer* serializer, gmx::ListOfLists<int>* listOfLists)
 {
-    serializer->doInt(&block->nr);
-    serializer->doInt(&block->nra);
+    int numLists = listOfLists->ssize();
+    serializer->doInt(&numLists);
+    int numElements = listOfLists->elementsView().ssize();
+    serializer->doInt(&numElements);
     if (serializer->reading())
     {
-        block->nalloc_index = block->nr + 1;
-        snew(block->index, block->nalloc_index);
-        block->nalloc_a = block->nra;
-        snew(block->a, block->nalloc_a);
+        std::vector<int> listRanges(numLists + 1);
+        serializer->doIntArray(listRanges.data(), numLists + 1);
+        std::vector<int> elements(numElements);
+        serializer->doIntArray(elements.data(), numElements);
+        *listOfLists = gmx::ListOfLists<int>(std::move(listRanges), std::move(elements));
+    }
+    else
+    {
+        serializer->doIntArray(const_cast<int*>(listOfLists->listRangesView().data()), numLists + 1);
+        serializer->doIntArray(const_cast<int*>(listOfLists->elementsView().data()), numElements);
     }
-    serializer->doIntArray(block->index, block->nr + 1);
-    serializer->doIntArray(block->a, block->nra);
 }
 
 /* This is a primitive routine to make it possible to translate atomic numbers
@@ -2451,7 +2474,7 @@ static void do_moltype(gmx::ISerializer* serializer, gmx_moltype_t* molt, t_symt
     sfree(cgs.index);
 
     /* This used to be in the atoms struct */
-    do_blocka(serializer, &molt->excls);
+    doListOfLists(serializer, &molt->excls);
 }
 
 static void do_molblock(gmx::ISerializer* serializer, gmx_molblock_t* molb, int numAtomsPerMolecule)
@@ -2498,7 +2521,7 @@ static void set_disres_npair(gmx_mtop_t* mtop)
     {
         const InteractionList& il = (*ilist)[F_DISRES];
 
-        if (il.size() > 0)
+        if (!il.empty())
         {
             gmx::ArrayRef<const int> a     = il.iatoms;
             int                      npair = 0;
@@ -2580,6 +2603,18 @@ static void do_mtop(gmx::ISerializer* serializer, gmx_mtop_t* mtop, int file_ver
 
     mtop->haveMoleculeIndices = true;
 
+    if (file_version >= tpxv_StoreNonBondedInteractionExclusionGroup)
+    {
+        std::int64_t intermolecularExclusionGroupSize = gmx::ssize(mtop->intermolecularExclusionGroup);
+        serializer->doInt64(&intermolecularExclusionGroupSize);
+        GMX_RELEASE_ASSERT(intermolecularExclusionGroupSize >= 0,
+                           "Number of atoms with excluded intermolecular non-bonded interactions "
+                           "is negative.");
+        mtop->intermolecularExclusionGroup.resize(intermolecularExclusionGroupSize); // no effect when writing
+        serializer->doIntArray(mtop->intermolecularExclusionGroup.data(),
+                               mtop->intermolecularExclusionGroup.size());
+    }
+
     if (serializer->reading())
     {
         close_symtab(&(mtop->symtab));
@@ -2827,7 +2862,7 @@ static void do_tpx_mtop(gmx::ISerializer* serializer, TpxFileHeader* tpx, gmx_mt
         {
             do_mtop(serializer, mtop, tpx->fileVersion);
             set_disres_npair(mtop);
-            gmx_mtop_finalize(mtop);
+            mtop->finalize();
         }
         else
         {
@@ -2933,9 +2968,9 @@ static void do_tpx_state_second(gmx::ISerializer* serializer, TpxFileHeader* tpx
  * \param[in] tpx The file header data.
  * \param[in,out] ir Datastructure with simulation parameters.
  */
-static int do_tpx_ir(gmx::ISerializer* serializer, TpxFileHeader* tpx, t_inputrec* ir)
+static PbcType do_tpx_ir(gmx::ISerializer* serializer, TpxFileHeader* tpx, t_inputrec* ir)
 {
-    int      ePBC;
+    PbcType  pbcType;
     gmx_bool bPeriodicMols;
 
     /* Starting with tpx version 26, we have the inputrec
@@ -2945,7 +2980,7 @@ static int do_tpx_ir(gmx::ISerializer* serializer, TpxFileHeader* tpx, t_inputre
      *
      *
      */
-    ePBC          = -1;
+    pbcType       = PbcType::Unset;
     bPeriodicMols = FALSE;
 
     do_test(serializer, tpx->bIr, ir);
@@ -2956,10 +2991,10 @@ static int do_tpx_ir(gmx::ISerializer* serializer, TpxFileHeader* tpx, t_inputre
             /* Removed the pbc info from do_inputrec, since we always want it */
             if (!serializer->reading())
             {
-                ePBC          = ir->ePBC;
+                pbcType       = ir->pbcType;
                 bPeriodicMols = ir->bPeriodicMols;
             }
-            serializer->doInt(&ePBC);
+            serializer->doInt(reinterpret_cast<int*>(&pbcType));
             serializer->doBool(&bPeriodicMols);
         }
         if (tpx->fileGeneration <= tpx_generation && ir)
@@ -2967,18 +3002,18 @@ static int do_tpx_ir(gmx::ISerializer* serializer, TpxFileHeader* tpx, t_inputre
             do_inputrec(serializer, ir, tpx->fileVersion);
             if (tpx->fileVersion < 53)
             {
-                ePBC          = ir->ePBC;
+                pbcType       = ir->pbcType;
                 bPeriodicMols = ir->bPeriodicMols;
             }
         }
         if (serializer->reading() && ir && tpx->fileVersion >= 53)
         {
             /* We need to do this after do_inputrec, since that initializes ir */
-            ir->ePBC          = ePBC;
+            ir->pbcType       = pbcType;
             ir->bPeriodicMols = bPeriodicMols;
         }
     }
-    return ePBC;
+    return pbcType;
 }
 
 /*! \brief
@@ -3011,7 +3046,7 @@ static void do_tpx_finalize(TpxFileHeader* tpx, t_inputrec* ir, t_state* state,
         {
             if (tpx->fileVersion < 57)
             {
-                if (mtop->moltype[0].ilist[F_DISRES].size() > 0)
+                if (!mtop->moltype[0].ilist[F_DISRES].empty())
                 {
                     ir->eDisre = edrSimple;
                 }
@@ -3044,13 +3079,13 @@ static void do_tpx_finalize(TpxFileHeader* tpx, t_inputrec* ir, t_state* state,
  * \param[in,out] v Individual velocities for processing, deprecated.
  * \param[in,out] mtop Global topology.
  */
-static int do_tpx_body(gmx::ISerializer* serializer,
-                       TpxFileHeader*    tpx,
-                       t_inputrec*       ir,
-                       t_state*          state,
-                       rvec*             x,
-                       rvec*             v,
-                       gmx_mtop_t*       mtop)
+static PbcType do_tpx_body(gmx::ISerializer* serializer,
+                           TpxFileHeader*    tpx,
+                           t_inputrec*       ir,
+                           t_state*          state,
+                           rvec*             x,
+                           rvec*             v,
+                           gmx_mtop_t*       mtop)
 {
     if (state)
     {
@@ -3061,12 +3096,12 @@ static int do_tpx_body(gmx::ISerializer* serializer,
     {
         do_tpx_state_second(serializer, tpx, state, x, v);
     }
-    int ePBC = do_tpx_ir(serializer, tpx, ir);
+    PbcType pbcType = do_tpx_ir(serializer, tpx, ir);
     if (serializer->reading())
     {
         do_tpx_finalize(tpx, ir, state, mtop);
     }
-    return ePBC;
+    return pbcType;
 }
 
 /*! \brief
@@ -3077,7 +3112,7 @@ static int do_tpx_body(gmx::ISerializer* serializer,
  * \param[in,out] ir Datastructures with simulation parameters.
  * \param[in,out] mtop Global topology.
  */
-static int do_tpx_body(gmx::ISerializer* serializer, TpxFileHeader* tpx, t_inputrec* ir, gmx_mtop_t* mtop)
+static PbcType do_tpx_body(gmx::ISerializer* serializer, TpxFileHeader* tpx, t_inputrec* ir, gmx_mtop_t* mtop)
 {
     return do_tpx_body(serializer, tpx, ir, nullptr, nullptr, nullptr, mtop);
 }
@@ -3182,12 +3217,12 @@ static PartialDeserializedTprFile readTpxBody(TpxFileHeader*    tpx,
         partialDeserializedTpr.header = *tpx;
         doTpxBodyBuffer(serializer, partialDeserializedTpr.body);
 
-        partialDeserializedTpr.ePBC =
+        partialDeserializedTpr.pbcType =
                 completeTprDeserialization(&partialDeserializedTpr, ir, state, x, v, mtop);
     }
     else
     {
-        partialDeserializedTpr.ePBC = do_tpx_body(serializer, tpx, ir, state, x, v, mtop);
+        partialDeserializedTpr.pbcType = do_tpx_body(serializer, tpx, ir, state, x, v, mtop);
     }
     // Update header to system info for communication to nodes.
     // As we only need to communicate the inputrec and mtop to other nodes,
@@ -3257,12 +3292,12 @@ void write_tpx_state(const char* fn, const t_inputrec* ir, const t_state* state,
     close_tpx(fio);
 }
 
-int completeTprDeserialization(PartialDeserializedTprFile* partialDeserializedTpr,
-                               t_inputrec*                 ir,
-                               t_state*                    state,
-                               rvec*                       x,
-                               rvec*                       v,
-                               gmx_mtop_t*                 mtop)
+PbcType completeTprDeserialization(PartialDeserializedTprFile* partialDeserializedTpr,
+                                   t_inputrec*                 ir,
+                                   t_state*                    state,
+                                   rvec*                       x,
+                                   rvec*                       v,
+                                   gmx_mtop_t*                 mtop)
 {
     // Long-term we should move to use little endian in files to avoid extra byte swapping,
     // but since we just used the default XDR format (which is big endian) for the TPR
@@ -3275,9 +3310,9 @@ int completeTprDeserialization(PartialDeserializedTprFile* partialDeserializedTp
     return do_tpx_body(&tprBodyDeserializer, &partialDeserializedTpr->header, ir, state, x, v, mtop);
 }
 
-int completeTprDeserialization(PartialDeserializedTprFile* partialDeserializedTpr,
-                               t_inputrec*                 ir,
-                               gmx_mtop_t*                 mtop)
+PbcType completeTprDeserialization(PartialDeserializedTprFile* partialDeserializedTpr,
+                                   t_inputrec*                 ir,
+                                   gmx_mtop_t*                 mtop)
 {
     return completeTprDeserialization(partialDeserializedTpr, ir, nullptr, nullptr, nullptr, mtop);
 }
@@ -3295,7 +3330,7 @@ PartialDeserializedTprFile read_tpx_state(const char* fn, t_inputrec* ir, t_stat
     return partialDeserializedTpr;
 }
 
-int read_tpx(const char* fn, t_inputrec* ir, matrix box, int* natoms, rvec* x, rvec* v, gmx_mtop_t* mtop)
+PbcType read_tpx(const char* fn, t_inputrec* ir, matrix box, int* natoms, rvec* x, rvec* v, gmx_mtop_t* mtop)
 {
     t_fileio* fio;
     t_state   state;
@@ -3315,19 +3350,19 @@ int read_tpx(const char* fn, t_inputrec* ir, matrix box, int* natoms, rvec* x, r
     {
         copy_mat(state.box, box);
     }
-    return partialDeserializedTpr.ePBC;
+    return partialDeserializedTpr.pbcType;
 }
 
-int read_tpx_top(const char* fn, t_inputrec* ir, matrix box, int* natoms, rvec* x, rvec* v, t_topology* top)
+PbcType read_tpx_top(const char* fn, t_inputrec* ir, matrix box, int* natoms, rvec* x, rvec* v, t_topology* top)
 {
     gmx_mtop_t mtop;
-    int        ePBC;
+    PbcType    pbcType;
 
-    ePBC = read_tpx(fn, ir, box, natoms, x, v, &mtop);
+    pbcType = read_tpx(fn, ir, box, natoms, x, v, &mtop);
 
     *top = gmx_mtop_t_to_t_topology(&mtop, true);
 
-    return ePBC;
+    return pbcType;
 }
 
 gmx_bool fn2bTPX(const char* file)
index 2346f0e00c3e6796055025983345caea3a402074..5fc0bea2bce3b675c67b49982c45d42d541fb849 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -42,7 +43,7 @@
 #include <vector>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
+#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
@@ -53,6 +54,11 @@ struct t_inputrec;
 class t_state;
 struct t_topology;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
 /*! \libinternal
  * \brief
  * First part of the TPR file structure containing information about
@@ -109,7 +115,7 @@ struct PartialDeserializedTprFile
     //! The file body.
     std::vector<char> body;
     //! Flag for PBC needed by legacy implementation.
-    int ePBC = -1;
+    PbcType pbcType = PbcType::Unset;
 };
 
 /*
@@ -153,17 +159,17 @@ void write_tpx_state(const char* fn, const t_inputrec* ir, const t_state* state,
  *
  * \returns PBC flag.
  */
-int completeTprDeserialization(PartialDeserializedTprFile* partialDeserializedTpr,
-                               t_inputrec*                 ir,
-                               t_state*                    state,
-                               rvec*                       x,
-                               rvec*                       v,
-                               gmx_mtop_t*                 mtop);
+PbcType completeTprDeserialization(PartialDeserializedTprFile* partialDeserializedTpr,
+                                   t_inputrec*                 ir,
+                                   t_state*                    state,
+                                   rvec*                       x,
+                                   rvec*                       v,
+                                   gmx_mtop_t*                 mtop);
 
 //! Overload for final TPR deserialization when not using state vectors.
-int completeTprDeserialization(PartialDeserializedTprFile* partialDeserializedTpr,
-                               t_inputrec*                 ir,
-                               gmx_mtop_t*                 mtop);
+PbcType completeTprDeserialization(PartialDeserializedTprFile* partialDeserializedTpr,
+                                   t_inputrec*                 ir,
+                                   gmx_mtop_t*                 mtop);
 
 /*! \brief
  * Read a file to set up a simulation and close it after reading.
@@ -202,11 +208,11 @@ PartialDeserializedTprFile read_tpx_state(const char* fn, t_inputrec* ir, t_stat
  * \param[out] x Positions to be filled from file, or nullptr.
  * \param[out] v Velocities to be filled from file, or nullptr.
  * \param[out] mtop Topology to be populated, or nullptr.
- * \returns ir->ePBC if it was read from the file.
+ * \returns ir->pbcType if it was read from the file.
  */
-int read_tpx(const char* fn, t_inputrec* ir, matrix box, int* natoms, rvec* x, rvec* v, gmx_mtop_t* mtop);
+PbcType read_tpx(const char* fn, t_inputrec* ir, matrix box, int* natoms, rvec* x, rvec* v, gmx_mtop_t* mtop);
 
-int read_tpx_top(const char* fn, t_inputrec* ir, matrix box, int* natoms, rvec* x, rvec* v, t_topology* top);
+PbcType read_tpx_top(const char* fn, t_inputrec* ir, matrix box, int* natoms, rvec* x, rvec* v, t_topology* top);
 /* As read_tpx, but for the old t_topology struct */
 
 gmx_bool fn2bTPX(const char* file);
index fbad07788a879965656e06827c129d766bc7f547..e9f227e4caa654c0f30b603de59628505c8e8f95 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 945489eb5d76f4872b6c18ecefabeea34b515755..6a551c0a743d83c1b41cbdbdbda60bb18428cfaa 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 34ee2995c915914ba3159cb2f591dfbf0335bd97..06ff8bcf12af59313c64b003711044a002f27726 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -291,15 +292,15 @@ void clear_trxframe(t_trxframe* fr, gmx_bool bFirst)
         fr->v         = nullptr;
         fr->f         = nullptr;
         clear_mat(fr->box);
-        fr->bPBC = FALSE;
-        fr->ePBC = -1;
+        fr->bPBC    = FALSE;
+        fr->pbcType = PbcType::Unset;
     }
 }
 
-void set_trxframe_ePBC(t_trxframe* fr, int ePBC)
+void setTrxFramePbcType(t_trxframe* fr, PbcType pbcType)
 {
-    fr->bPBC = (ePBC == -1);
-    fr->ePBC = ePBC;
+    fr->bPBC    = (pbcType == PbcType::Unset);
+    fr->pbcType = pbcType;
 }
 
 int write_trxframe_indexed(t_trxstatus* status, const t_trxframe* fr, int nind, const int* ind, gmx_conect gc)
@@ -409,8 +410,8 @@ int write_trxframe_indexed(t_trxstatus* status, const t_trxframe* fr, int nind,
             }
             else
             {
-                write_pdbfile_indexed(gmx_fio_getfp(status->fio), title, fr->atoms, fr->x, -1,
-                                      fr->box, ' ', fr->step, nind, ind, gc, FALSE);
+                write_pdbfile_indexed(gmx_fio_getfp(status->fio), title, fr->atoms, fr->x,
+                                      PbcType::Unset, fr->box, ' ', fr->step, nind, ind, gc, FALSE);
             }
             break;
         case efG96:
@@ -549,7 +550,7 @@ int write_trxframe(t_trxstatus* status, t_trxframe* fr, gmx_conect gc)
             else
             {
                 write_pdbfile(gmx_fio_getfp(status->fio), title, fr->atoms, fr->x,
-                              fr->bPBC ? fr->ePBC : -1, fr->box, ' ', fr->step, gc);
+                              fr->bPBC ? fr->pbcType : PbcType::Unset, fr->box, ' ', fr->step, gc);
             }
             break;
         case efG96: write_g96_conf(gmx_fio_getfp(status->fio), title, fr, -1, nullptr); break;
@@ -696,9 +697,10 @@ static gmx_bool pdb_next_x(t_trxstatus* status, FILE* fp, t_trxframe* fr)
     // Initiate model_nr to -1 rather than NOTSET.
     // It is not worthwhile introducing extra variables in the
     // read_pdbfile call to verify that a model_nr was read.
-    int    ePBC, model_nr = -1, na;
-    char   title[STRLEN], *time, *step;
-    double dbl;
+    PbcType pbcType;
+    int     model_nr = -1, na;
+    char    title[STRLEN], *time, *step;
+    double  dbl;
 
     atoms.nr      = fr->natoms;
     atoms.atom    = nullptr;
@@ -706,10 +708,10 @@ static gmx_bool pdb_next_x(t_trxstatus* status, FILE* fp, t_trxframe* fr)
     /* the other pointers in atoms should not be accessed if these are NULL */
     snew(symtab, 1);
     open_symtab(symtab);
-    na = read_pdbfile(fp, title, &model_nr, &atoms, symtab, fr->x, &ePBC, boxpdb, TRUE, nullptr);
+    na = read_pdbfile(fp, title, &model_nr, &atoms, symtab, fr->x, &pbcType, boxpdb, TRUE, nullptr);
     free_symtab(symtab);
     sfree(symtab);
-    set_trxframe_ePBC(fr, ePBC);
+    setTrxFramePbcType(fr, pbcType);
     if (nframes_read(status) == 0)
     {
         fprintf(stderr, " '%s', %d atoms\n", title, fr->natoms);
@@ -1081,16 +1083,17 @@ void rewind_trj(t_trxstatus* status)
 
 /***** T O P O L O G Y   S T U F F ******/
 
-t_topology* read_top(const char* fn, int* ePBC)
+t_topology* read_top(const char* fn, PbcType* pbcType)
 {
-    int         epbc, natoms;
+    int         natoms;
+    PbcType     pbcTypeFile;
     t_topology* top;
 
     snew(top, 1);
-    epbc = read_tpx_top(fn, nullptr, nullptr, &natoms, nullptr, nullptr, top);
-    if (ePBC)
+    pbcTypeFile = read_tpx_top(fn, nullptr, nullptr, &natoms, nullptr, nullptr, top);
+    if (pbcType)
     {
-        *ePBC = epbc;
+        *pbcType = pbcTypeFile;
     }
 
     return top;
index bc0ee048863f910d558620545df6536f7fefe1a3..ae1660c99ac33d13a573fa019f278b227183fba3 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -39,7 +40,6 @@
 #define GMX_FILEIO_TRXIO_H
 
 #include "gromacs/fileio/pdbio.h"
-#include "gromacs/utility/arrayref.h"
 
 struct gmx_mtop_t;
 struct gmx_output_env_t;
@@ -48,6 +48,11 @@ struct t_fileio;
 struct t_topology;
 struct t_trxframe;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
 /* a dedicated status type contains fp, etc. */
 typedef struct t_trxstatus t_trxstatus;
 
@@ -70,8 +75,8 @@ void clear_trxframe(struct t_trxframe* fr, gmx_bool bFirst);
  *                     and all data to zero.
  */
 
-void set_trxframe_ePBC(struct t_trxframe* fr, int ePBC);
-/* Set the type of periodic boundary conditions, ePBC=-1 is not set */
+void setTrxFramePbcType(struct t_trxframe* fr, PbcType pbcType);
+/* Set the type of periodic boundary conditions, pbcType=PbcType::Unset is not set */
 
 int nframes_read(t_trxstatus* status);
 /* Returns the number of frames read from the trajectory */
@@ -242,9 +247,9 @@ gmx_bool read_next_x(const gmx_output_env_t* oenv, t_trxstatus* status, real* t,
 void rewind_trj(t_trxstatus* status);
 /* Rewind trajectory file as opened with read_first_x */
 
-struct t_topology* read_top(const char* fn, int* ePBC);
+struct t_topology* read_top(const char* fn, PbcType* pbcType);
 /* Extract a topology data structure from a topology file.
- * If ePBC!=NULL *ePBC gives the pbc type.
+ * If pbcType!=NULL *pbcType gives the pbc type.
  */
 
 #endif
index 2e40cfd2dc63e9fa5f0bd528b21a69de08dfefcf..f10712ba8e2e46ac5672e3994e92ce959e2de278 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2012,2013,2014,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d8665b247dadf1588b47f8048dd07b99f312d5ea..f186ff95621756a724375d8a0421a7091a1f014e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index c8ee1eedd456b548212cfcf430eb3e379cf73c59..0b7d03054a337e33a14923e81d6fc940d5314cb2 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 1222bad816b01a8f642cc458a09edcd9c1cb66d6..808bbaf9cc0b85bb186b4f58eaee4bee7fa140bd 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 45175a5d6aeeeb560de6b633c6a87fa16c83a682..d954678b2036b97603c48dcc08ddb0714cd44717 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -114,20 +114,17 @@ int xdr_int64(XDR* xdrs, int64_t* i)
     // requires 64-bit systems.
     static_assert(2 * sizeof(int) >= 8,
                   "XDR handling assumes that an int64_t can be stored in two ints");
-    int imaj, imin;
-    int ret;
 
-    static const int64_t two_p32_m1 = 0xFFFFFFFF;
-    int64_t              imaj64, imin64;
+    static const uint64_t two_p32_m1 = 0xFFFFFFFF;
 
-    imaj64 = ((*i) >> 32) & two_p32_m1;
-    imin64 = (*i) & two_p32_m1;
-    imaj   = static_cast<int>(imaj64);
-    imin   = static_cast<int>(imin64);
-    ret    = xdr_int(xdrs, &imaj);
+    uint64_t imaj64 = ((*i) >> 32) & two_p32_m1;
+    uint64_t imin64 = (*i) & two_p32_m1;
+    int      imaj   = static_cast<int>(imaj64);
+    int      imin   = static_cast<int>(imin64);
+    int      ret    = xdr_int(xdrs, &imaj);
     ret |= xdr_int(xdrs, &imin);
 
-    *i = ((static_cast<int64_t>(imaj) << 32) | (static_cast<int64_t>(imin) & two_p32_m1));
+    *i = ((static_cast<uint64_t>(imaj) << 32) | (static_cast<uint64_t>(imin) & two_p32_m1));
 
     return ret;
 }
index 35c77deba6b0266719caf3c71e263d644d93e719..c5ceb8338d1ff16430859bbe6023a54390d82c95 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index db206a904a8490f1a0c4dd539415ae62ee4b78b0..bb37a8df0dccad3a40383d7669a7e41bb603d79d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 55764b0362ee81e92ed7adbadea0386f13baab2e..4cc0794db209f054572792d7cb3866376d44ffe5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +48,7 @@
 #include "gromacs/fileio/gmxfio.h"
 #include "gromacs/fileio/oenv.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/binaryinformation.h"
 #include "gromacs/utility/coolstuff.h"
 #include "gromacs/utility/cstringutil.h"
 
 gmx_bool output_env_get_print_xvgr_codes(const gmx_output_env_t* oenv)
 {
-    int xvg_format;
+    XvgFormat xvgFormat = output_env_get_xvg_format(oenv);
 
-    xvg_format = output_env_get_xvg_format(oenv);
-
-    return (xvg_format == exvgXMGRACE || xvg_format == exvgXMGR);
+    return (xvgFormat == XvgFormat::Xmgrace || xvgFormat == XvgFormat::Xmgr);
 }
 
 static char* xvgrstr(const std::string& gmx, const gmx_output_env_t* oenv, char* buf, int buflen)
@@ -71,13 +71,12 @@ static char* xvgrstr(const std::string& gmx, const gmx_output_env_t* oenv, char*
     const char* sym[]  = { "beta",  "chi", "delta", "eta", "lambda", "mu",
                           "omega", "phi", "psi",   "rho", "theta",  nullptr };
     const char  symc[] = { 'b', 'c', 'd', 'h', 'l', 'm', 'w', 'f', 'y', 'r', 'q', '\0' };
-    int         xvgf;
     gmx_bool    bXVGR;
     int         g, b, i;
     char        c;
 
-    xvgf  = output_env_get_xvg_format(oenv);
-    bXVGR = (xvgf == exvgXMGRACE || xvgf == exvgXMGR);
+    XvgFormat xvgf = output_env_get_xvg_format(oenv);
+    bXVGR          = (xvgf == XvgFormat::Xmgrace || xvgf == XvgFormat::Xmgr);
 
     g = 0;
     b = 0;
@@ -144,8 +143,8 @@ static char* xvgrstr(const std::string& gmx, const gmx_output_env_t* oenv, char*
                 /* Backward compatibility for xmgr normal font "\4" */
                 switch (xvgf)
                 {
-                    case exvgXMGRACE: sprintf(buf + b, "%s", "\\f{}"); break;
-                    case exvgXMGR: sprintf(buf + b, "%s", "\\4"); break;
+                    case XvgFormat::Xmgrace: sprintf(buf + b, "%s", "\\f{}"); break;
+                    case XvgFormat::Xmgr: sprintf(buf + b, "%s", "\\4"); break;
                     default: buf[b] = '\0'; break;
                 }
                 g++;
@@ -156,8 +155,8 @@ static char* xvgrstr(const std::string& gmx, const gmx_output_env_t* oenv, char*
                 /* Backward compatibility for xmgr symbol font "\8" */
                 switch (xvgf)
                 {
-                    case exvgXMGRACE: sprintf(buf + b, "%s", "\\x"); break;
-                    case exvgXMGR: sprintf(buf + b, "%s", "\\8"); break;
+                    case XvgFormat::Xmgrace: sprintf(buf + b, "%s", "\\x"); break;
+                    case XvgFormat::Xmgr: sprintf(buf + b, "%s", "\\8"); break;
                     default: buf[b] = '\0'; break;
                 }
                 g++;
@@ -180,8 +179,10 @@ static char* xvgrstr(const std::string& gmx, const gmx_output_env_t* oenv, char*
                     }
                     switch (xvgf)
                     {
-                        case exvgXMGRACE: sprintf(buf + b, "%s%c%s", "\\x", c, "\\f{}"); break;
-                        case exvgXMGR: sprintf(buf + b, "%s%c%s", "\\8", c, "\\4"); break;
+                        case XvgFormat::Xmgrace:
+                            sprintf(buf + b, "%s%c%s", "\\x", c, "\\f{}");
+                            break;
+                        case XvgFormat::Xmgr: sprintf(buf + b, "%s%c%s", "\\8", c, "\\4"); break;
                         default:
                             std::strncat(buf + b, &gmx[g], std::strlen(sym[i]));
                             b += std::strlen(sym[i]);
@@ -245,7 +246,7 @@ void xvgr_header(FILE*                   fp,
         switch (exvg_graph_type)
         {
             case exvggtXNY:
-                if (output_env_get_xvg_format(oenv) == exvgXMGR)
+                if (output_env_get_xvg_format(oenv) == XvgFormat::Xmgr)
                 {
                     fprintf(fp, "@TYPE nxy\n");
                 }
@@ -349,7 +350,7 @@ static void xvgr_legend(FILE* out, int nsets, const T* setname, const gmx_output
         {
             if (!stringIsEmpty(setname[i]))
             {
-                if (output_env_get_xvg_format(oenv) == exvgXMGR)
+                if (output_env_get_xvg_format(oenv) == XvgFormat::Xmgr)
                 {
                     fprintf(out, "@ legend string %d \"%s\"\n", i, xvgrstr(setname[i], oenv, buf, STRLEN));
                 }
@@ -383,7 +384,7 @@ void xvgr_new_dataset(FILE* out, int nr_first, int nsets, const char** setname,
         {
             if (setname[i])
             {
-                if (output_env_get_xvg_format(oenv) == exvgXMGR)
+                if (output_env_get_xvg_format(oenv) == XvgFormat::Xmgr)
                 {
                     fprintf(out, "@ legend string %d \"%s\"\n", i + nr_first,
                             xvgrstr(setname[i], oenv, buf, STRLEN));
@@ -511,11 +512,7 @@ static int wordcount(char* ptr)
         for (i = 0; (ptr[i] != '\0'); i++)
         {
             is[cur] = std::isspace(ptr[i]);
-            if ((0 == i) && !is[cur])
-            {
-                n++;
-            }
-            else if ((i > 0) && (!is[cur] && is[prev]))
+            if (((0 == i) && !is[cur]) || ((i > 0) && (!is[cur] && is[prev])))
             {
                 n++;
             }
@@ -711,7 +708,111 @@ int read_xvg_legend(const char* fn, double*** y, int* ny, char** subtitle, char*
 
 int read_xvg(const char* fn, double*** y, int* ny)
 {
-    return read_xvg_legend(fn, y, ny, nullptr, nullptr);
+    gmx::MultiDimArray<std::vector<double>, gmx::dynamicExtents2D> xvgData =
+            readXvgData(std::string(fn));
+
+    int numColumns = xvgData.extent(0);
+    int numRows    = xvgData.extent(1);
+
+    double** yy = nullptr;
+    snew(yy, numColumns);
+    for (int column = 0; column < numColumns; column++)
+    {
+        snew(yy[column], numRows);
+        for (int row = 0; row < numRows; row++)
+        {
+            yy[column][row] = xvgData.asConstView()[column][row];
+        }
+    }
+
+    *y     = yy;
+    *ny    = numColumns;
+    int nx = numRows;
+    return nx;
+}
+
+gmx::MultiDimArray<std::vector<double>, gmx::dynamicExtents2D> readXvgData(const std::string& fn)
+{
+    FILE* fp = gmx_fio_fopen(fn.c_str(), "r");
+    char* ptr;
+    char* base = nullptr;
+    char* fmt  = nullptr;
+    char* tmpbuf;
+    int   len = STRLEN;
+
+    //! This only gets properly set after the first line of data is read
+    int numColumns = 0;
+    int numRows    = 0;
+    snew(tmpbuf, len);
+    std::vector<double> xvgData;
+
+    for (int line = 0; (ptr = fgets3(fp, &tmpbuf, &len, 10 * STRLEN)) != nullptr && ptr[0] != '&'; ++line)
+    {
+        trim(ptr);
+        if (ptr[0] == '@' || ptr[0] == '#')
+        {
+            continue;
+        }
+        ++numRows;
+        if (numColumns == 0)
+        {
+            numColumns = wordcount(ptr);
+            if (numColumns == 0)
+            {
+                return {}; // There are no columns and hence no data to process
+            }
+            snew(fmt, 3 * numColumns + 1);
+            snew(base, 3 * numColumns + 1);
+        }
+        /* Initiate format string */
+        fmt[0]          = '\0';
+        base[0]         = '\0';
+        int columnCount = 0;
+        for (columnCount = 0; (columnCount < numColumns); columnCount++)
+        {
+            double lf;
+            std::strcpy(fmt, base);
+            std::strcat(fmt, "%lf");
+            int rval = sscanf(ptr, fmt, &lf);
+            if ((rval == EOF) || (rval == 0))
+            {
+                break;
+            }
+            xvgData.push_back(lf);
+            srenew(fmt, 3 * (numColumns + 1) + 1);
+            srenew(base, 3 * numColumns + 1);
+            std::strcat(base, "%*s");
+        }
+
+        if (columnCount != numColumns)
+        {
+            fprintf(stderr, "Only %d columns on line %d in file %s\n", columnCount, line, fn.c_str());
+            for (; (columnCount < numColumns); columnCount++)
+            {
+                xvgData.push_back(0.0);
+            }
+        }
+    }
+    gmx_fio_fclose(fp);
+
+    sfree(tmpbuf);
+    sfree(base);
+    sfree(fmt);
+
+    gmx::MultiDimArray<std::vector<double>, gmx::dynamicExtents2D> xvgDataAsArray(numRows, numColumns);
+    std::copy(std::begin(xvgData), std::end(xvgData), begin(xvgDataAsArray.asView()));
+
+    gmx::MultiDimArray<std::vector<double>, gmx::dynamicExtents2D> xvgDataAsArrayTransposed(
+            numColumns, numRows);
+    for (std::ptrdiff_t row = 0; row < numRows; ++row)
+    {
+        for (std::ptrdiff_t column = 0; column < numColumns; ++column)
+        {
+            xvgDataAsArrayTransposed(column, row) = xvgDataAsArray(row, column);
+        }
+    }
+
+    return xvgDataAsArrayTransposed;
 }
 
 void write_xvg(const char* fn, const char* title, int nx, int ny, real** y, const char** leg, const gmx_output_env_t* oenv)
index f443ea14c0a7e424aea040e505c659f3210df591..fc8683c5660892e99c12e1d12dd7f84084175aa7 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -42,6 +43,8 @@
 #include <string>
 #include <vector>
 
+#include "gromacs/math/multidimarray.h"
+#include "gromacs/mdspan/extensions.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
@@ -212,8 +215,27 @@ int read_xvg_legend(const char* fn, double*** y, int* ny, char** subtitle, char*
  * 0 is the first y legend, the legend string will be NULL when not present.
  */
 
+/* \brief Read only the data from an xvg file for post processing.
+ *
+ * Note: this function is deprecated in favor of readXvg, which is
+ *       used under the hood in this function.
+ *
+ * \param[out]    nx Number of rows.
+ * \param[in]     fn Xvg file to read.
+ * \param[in/out] y  Pointer to 2D array (allocated by the routine).
+ * \param[in/out] ny Number of columns.
+ *
+ * Todo: Port all read_xvg calls to use readXvgData
+ */
 int read_xvg(const char* fn, double*** y, int* ny);
-/* As read_xvg_legend, but does not read legends. */
+
+/* \brief Read only the data from an xvg file for post processing.
+ *
+ * \param[out] XvgData Data in row major.
+ * \param[in]  fn      Xvg file to read.
+ */
+gmx::MultiDimArray<std::vector<double>, gmx::dynamicExtents2D> readXvgData(const std::string& fn);
+
 
 void write_xvg(const char*                    fn,
                const char*                    title,
index bfe16bede134eead6646469ce52e79c2d1819237..3a3a952f312d2fe0d68ae31e6e6c7ae50f6c57e8 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -723,11 +724,7 @@ static real calc_fraction(const real angles[], int nangles)
         {
             trans += 1.0;
         }
-        else if (angle > 270 && angle < 330)
-        {
-            gauche += 1.0;
-        }
-        else if (angle < 90 && angle > 30)
+        else if ((angle > 270 && angle < 330) || (angle < 90 && angle > 30))
         {
             gauche += 1.0;
         }
@@ -886,7 +883,7 @@ void read_ang_dih(const char*             trj_fn,
 
         if (pbc)
         {
-            set_pbc(pbc, -1, box);
+            set_pbc(pbc, PbcType::Unset, box);
         }
 
         if (bAngles)
index 3ee9a99542e339aef73bcf8ce03262fd89b3fd00..3bf1507dd74075d11171d64a6d8a1021a8f25a75 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 5c68c4df4e91cfced228b711cfd4034c7acb6ce2..52ab32a3be5252ed88a008ccd382ab095d8aa6ca 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 1acdebe339318eca5adc322eabcbbbf7a1396960..15684e8379c4b63f3d5a8c84bf7e316b5b95ece2 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 07368d9d41b2c5ddff94daa131bd7edcd54e5d96..79bf726642fe8ad061483b4f1cc50b5f74e6eadf 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d1334ac1e9ee8af738f9f27d3947148134485ebf..d781d94235e5c3296c2c37d0cea533ff4288f610 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 78b59aa7f09830db1bd0c9bc7a32d416a4dc3d7e..908f0fa8ac67848dd35d057db46d44d9b479df74 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -126,7 +127,7 @@ t_dlist* mk_dlist(FILE*          log,
                      || (std::strcmp(*(atoms->atomname[i]), "SD") == 0)
                      || (std::strcmp(*(atoms->atomname[i]), "OD1") == 0)
                      || (std::strcmp(*(atoms->atomname[i]), "ND1") == 0))
-            {
+            { // NOLINT bugprone-branch-clone
                 atm.Cn[4] = i;
             }
             /* by grs - split the Cn[4] into 2 bits to check allowing dih to H */
index 3cea7e812a1227272e822d51b101a0cd751d89fa..4433fb463c248804a46d769933ef19d6c7781b01 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b6d0f715628f6ab750d178c2ae6542071b839c17..673315141794c44274580e8b6056498fcdc73b13 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 98526305f73e06555bbc14620f6df0d406f4ec3f..f38d2d77f430fe48bad3d3d967388ff66512f411 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f40e33e2db2231a463e9c4f6423a5d58af3b72a6..7f0623b34d505a525b46d07f8eb1d83774cd8972 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 319d069673e9366a57a68272e331ff708e0fd07d..08a5a7419494cff3ce04fc3fb76be897d91d3ebf 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -110,7 +111,7 @@ static void write_xvgr_graphs(const char*             file,
     real  ymin, ymax, xsp, ysp;
 
     out = gmx_ffopen(file, "w");
-    if (output_env_get_xvg_format(oenv) == exvgXMGRACE)
+    if (output_env_get_xvg_format(oenv) == XvgFormat::Xmgrace)
     {
         fprintf(out, "@ autoscale onread none\n");
     }
@@ -465,7 +466,7 @@ static void overlap(const char*             outfile,
 
 static void project(const char*             trajfile,
                     const t_topology*       top,
-                    int                     ePBC,
+                    PbcType                 pbcType,
                     matrix                  topbox,
                     const char*             projfile,
                     const char*             twodplotfile,
@@ -549,7 +550,7 @@ static void project(const char*             trajfile,
 
         if (top)
         {
-            gpbc = gmx_rmpbc_init(&top->idef, ePBC, nat);
+            gpbc = gmx_rmpbc_init(&top->idef, pbcType, nat);
         }
 
         for (i = 0; i < nat; i++)
@@ -766,7 +767,7 @@ static void project(const char*             trajfile,
         }
         else
         {
-            write_sto_conf(threedplotfile, str, &atoms, x, nullptr, ePBC, box);
+            write_sto_conf(threedplotfile, str, &atoms, x, nullptr, pbcType, box);
         }
         done_atom(&atoms);
     }
@@ -1068,8 +1069,8 @@ int gmx_anaeig(int argc, char* argv[])
 #define NPA asize(pa)
 
     t_topology        top;
-    int               ePBC  = -1;
-    const t_atoms*    atoms = nullptr;
+    PbcType           pbcType = PbcType::Unset;
+    const t_atoms*    atoms   = nullptr;
     rvec *            xtop, *xref1, *xref2, *xrefp = nullptr;
     gmx_bool          bDMR1, bDMA1, bDMR2, bDMA2;
     int               nvec1, nvec2, *eignr1 = nullptr, *eignr2 = nullptr;
@@ -1262,9 +1263,9 @@ int gmx_anaeig(int argc, char* argv[])
     }
     else
     {
-        bTop  = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, topbox, bM);
+        bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, topbox, bM);
         atoms = &top.atoms;
-        gpbc  = gmx_rmpbc_init(&top.idef, ePBC, atoms->nr);
+        gpbc  = gmx_rmpbc_init(&top.idef, pbcType, atoms->nr);
         gmx_rmpbc(gpbc, atoms->nr, topbox, xtop);
         /* Fitting is only required for the projection */
         if (bProj && bFit1)
@@ -1464,7 +1465,7 @@ int gmx_anaeig(int argc, char* argv[])
 
     if (bProj)
     {
-        project(bTraj ? opt2fn("-f", NFILE, fnm) : nullptr, bTop ? &top : nullptr, ePBC, topbox,
+        project(bTraj ? opt2fn("-f", NFILE, fnm) : nullptr, bTop ? &top : nullptr, pbcType, topbox,
                 ProjOnVecFile, TwoDPlotFile, ThreeDPlotFile, FilterFile, skip, ExtremeFile,
                 bFirstLastSet, max, nextr, atoms, natoms, index, bFit1, xrefp, nfit, ifit, w_rls,
                 sqrtm, xav1, eignr1, eigvec1, noutvec, outvec, bSplit, oenv);
index 267a3b0a5071450abe7765ef9e42c35972e2ef47..75888d57fc5093e1170fe6c57adef84baeb375c2 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -630,13 +631,13 @@ static void estimate_error(const char*             eefile,
             tau2 = fitparm[2];
         }
         fprintf(stdout, "Set %3d:  err.est. %g  a %g  tau1 %g  tau2 %g\n", s + 1, ee, a, tau1, tau2);
-        if (output_env_get_xvg_format(oenv) == exvgXMGR)
+        if (output_env_get_xvg_format(oenv) == XvgFormat::Xmgr)
         {
             fprintf(fp, "@ legend string %d \"av %f\"\n", 2 * s, av[s]);
             fprintf(fp, "@ legend string %d \"ee %6g\"\n", 2 * s + 1,
                     optimal_error_estimate(sig[s], fitparm, n * dt));
         }
-        else if (output_env_get_xvg_format(oenv) == exvgXMGRACE)
+        else if (output_env_get_xvg_format(oenv) == XvgFormat::Xmgrace)
         {
             fprintf(fp, "@ s%d legend \"av %f\"\n", 2 * s, av[s]);
             fprintf(fp, "@ s%d legend \"ee %6g\"\n", 2 * s + 1,
index e99bee5628e958d5ed9574e09c120adb03d3d753..70f006512aa2ec3f47278245fd1f64fe615aeb05 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -211,7 +212,7 @@ int gmx_g_angle(int argc, char* argv[])
             mult   = 3;
             maxang = 180.0;
             break;
-        case 'd': break;
+        case 'd': // Intended fall through
         case 'i': break;
         case 'r': bRb = TRUE; break;
     }
index e0d8d94893cb50d5e6c2ca901c3ddb44d5449b57..f68beb07e9f36e335e460503f39f25b2049a7acb 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -339,7 +340,7 @@ void OutputFile::initializeAwhOutputFile(int                  subblockStart,
     int numLegend = numDim_ - 1 + numGraph_;
     legend_       = makeLegend(awhBiasParams, OutputFileType::Awh, numLegend);
     /* We could have both length and angle coordinates in a single bias */
-    xLabel_ = "(nm or deg)";
+    xLabel_ = "(nm, deg or lambda state)";
     yLabel_ = useKTForEnergy_ ? "(k\\sB\\NT)" : "(kJ/mol)";
     if (graphSelection == AwhGraphSelection::All)
     {
@@ -376,14 +377,16 @@ void OutputFile::initializeFrictionOutputFile(int                  subBlockStart
     scaleFactor_.resize(numGraph_, useKTForEnergy_ ? 1 : kTValue);
     int numLegend = numDim_ - 1 + numGraph_;
     legend_       = makeLegend(awhBiasParams, OutputFileType::Friction, numLegend);
-    xLabel_       = "(nm or deg)";
+    xLabel_       = "(nm, deg or lambda state)";
     if (useKTForEnergy_)
     {
-        yLabel_ = "friction/k\\sB\\NT (nm\\S-2\\Nps or rad\\S-2\\Nps)";
+        yLabel_ = "friction/k\\sB\\NT (nm\\S-2\\Nps, rad\\S-2\\Nps or ps)";
     }
     else
     {
-        yLabel_ = "friction (kJ mol\\S-1\\Nnm\\S-2\\Nps or kJ mol\\S-1\\Nrad\\S-2\\Nps)";
+        yLabel_ =
+                "friction (kJ mol\\S-1\\Nnm\\S-2\\Nps, kJ mol\\S-1\\Nrad\\S-2\\Nps or kJ "
+                "mol\\S-1\\Nps)";
     }
 }
 
index 062a3594e8fde464296d3e79b2108c7b9549daba..04c9dda231201530f19d03dc4f78e3730e915000 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -42,6 +42,7 @@
 
 #include <algorithm>
 #include <limits>
+#include <vector>
 
 #include "gromacs/commandline/pargs.h"
 #include "gromacs/commandline/viewit.h"
@@ -816,8 +817,7 @@ static void lambda_data_list_insert_sample(lambda_data_t* head, samples_t* s)
 
 
 /* make a histogram out of a sample collection */
-static void
-sample_coll_make_hist(sample_coll_t* sc, int** bin, int* nbin_alloc, int* nbin, double* dx, double* xmin, int nbin_default)
+static void sample_coll_make_hist(sample_coll_t* sc, std::vector<int>* bin, double* dx, double* xmin, int nbin_default)
 {
     int      i, j, k;
     gmx_bool dx_set   = FALSE;
@@ -906,33 +906,24 @@ sample_coll_make_hist(sample_coll_t* sc, int** bin, int* nbin_alloc, int* nbin,
 
     if (!xmax_set || !xmin_set)
     {
-        *nbin = 0;
+        bin->clear();
         return;
     }
 
 
     if (!dx_set)
     {
-        *nbin = nbin_default;
-        *dx   = (xmax - (*xmin)) / ((*nbin) - 2); /* -2 because we want the last bin to
-                                                     be 0, and we count from 0 */
+        bin->resize(nbin_default);
+        *dx = (xmax - (*xmin)) / ((bin->size()) - 2); /* -2 because we want the last bin to
+                                                   be 0, and we count from 0 */
     }
     else
     {
-        *nbin = static_cast<int>((xmax - (*xmin)) / (*dx));
-    }
-
-    if (*nbin > *nbin_alloc)
-    {
-        *nbin_alloc = *nbin;
-        srenew(*bin, *nbin_alloc);
+        bin->resize(static_cast<int>((xmax - (*xmin)) / (*dx)));
     }
 
     /* reset the histogram */
-    for (i = 0; i < (*nbin); i++)
-    {
-        (*bin)[i] = 0;
-    }
+    std::fill(bin->begin(), bin->end(), 0);
 
     /* now add the actual data */
     for (i = 0; i < sc->nsamples; i++)
@@ -951,9 +942,9 @@ sample_coll_make_hist(sample_coll_t* sc, int** bin, int* nbin_alloc, int* nbin,
                     double x     = hdx * (j + 0.5) + xmin_hist;
                     int    binnr = static_cast<int>((x - (*xmin)) / (*dx));
 
-                    if (binnr >= *nbin || binnr < 0)
+                    if (binnr >= gmx::ssize(*bin) || binnr < 0)
                     {
-                        binnr = (*nbin) - 1;
+                        binnr = (bin->size()) - 1;
                     }
 
                     (*bin)[binnr] += hist->bin[k][j];
@@ -967,9 +958,9 @@ sample_coll_make_hist(sample_coll_t* sc, int** bin, int* nbin_alloc, int* nbin,
             for (j = starti; j < endi; j++)
             {
                 int binnr = static_cast<int>((sc->s[i]->du[j] - (*xmin)) / (*dx));
-                if (binnr >= *nbin || binnr < 0)
+                if (binnr >= gmx::ssize(*bin) || binnr < 0)
                 {
-                    binnr = (*nbin) - 1;
+                    binnr = (bin->size()) - 1;
                 }
 
                 (*bin)[binnr]++;
@@ -991,13 +982,10 @@ static void sim_data_histogram(sim_data_t* sd, const char* filename, int nbin_de
     char**         setnames  = nullptr;
     gmx_bool       first_set = FALSE;
     /* histogram data: */
-    int*           hist       = nullptr;
-    int            nbin       = 0;
-    int            nbin_alloc = 0;
-    double         dx         = 0;
-    double         minval     = 0;
-    int            i;
-    lambda_data_t* bl_head = sd->lb;
+    std::vector<int> hist;
+    double           dx      = 0;
+    double           minval  = 0;
+    lambda_data_t*   bl_head = sd->lb;
 
     printf("\nWriting histogram to %s\n", filename);
     sprintf(label_x, "\\DeltaH (%s)", unit_energy);
@@ -1053,9 +1041,9 @@ static void sim_data_histogram(sim_data_t* sd, const char* filename, int nbin_de
                 xvgr_new_dataset(fp, 0, 0, nullptr, oenv);
             }
 
-            sample_coll_make_hist(sc, &hist, &nbin_alloc, &nbin, &dx, &minval, nbin_default);
+            sample_coll_make_hist(sc, &hist, &dx, &minval, nbin_default);
 
-            for (i = 0; i < nbin; i++)
+            for (gmx::index i = 0; i < gmx::ssize(hist); i++)
             {
                 double xmin = i * dx + minval;
                 double xmax = (i + 1) * dx + minval;
@@ -1070,11 +1058,6 @@ static void sim_data_histogram(sim_data_t* sd, const char* filename, int nbin_de
         bl = bl->next;
     }
 
-    if (hist)
-    {
-        sfree(hist);
-    }
-
     xvgrclose(fp);
 }
 
@@ -3096,7 +3079,7 @@ static void read_barsim_edr(const char* fn, real* temp, sim_data_t* sd)
                 old_start_lambda = fr->block[i].sub[0].dval[3];
                 delta_lambda     = fr->block[i].sub[0].dval[4];
 
-                if (delta_lambda > 0)
+                if (delta_lambda != 0)
                 {
                     gmx_fatal(FARGS, "Lambda values not constant in %s: can't apply BAR method", fn);
                 }
index df5a6c4d3f97120031359c79a10774682f3a6bc1..a55cde5f9596dcfa5865a0457f8f074bdbde9f93 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -227,7 +228,7 @@ int gmx_bundle(int argc, char* argv[])
     t_trxstatus*    status;
     t_trxstatus*    fpdb;
     t_topology      top;
-    int             ePBC;
+    PbcType         pbcType;
     rvec*           xtop;
     matrix          box;
     t_trxframe      fr;
@@ -262,7 +263,7 @@ int gmx_bundle(int argc, char* argv[])
         return 0;
     }
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, box, TRUE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, box, TRUE);
 
     bKink = opt2bSet("-ok", NFILE, fnm) || opt2bSet("-okr", NFILE, fnm) || opt2bSet("-okl", NFILE, fnm);
     if (bKink)
@@ -344,7 +345,7 @@ int gmx_bundle(int argc, char* argv[])
     }
 
     read_first_frame(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &fr, TRX_NEED_X);
-    gpbc = gmx_rmpbc_init(&top.idef, ePBC, fr.natoms);
+    gpbc = gmx_rmpbc_init(&top.idef, pbcType, fr.natoms);
 
     do
     {
index f14961a2b80fa216c9107d2c806b614f8632048b..21be848c9861e8f7a2459cdfdc7aaf2bd43a0f8d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -1071,7 +1072,7 @@ static void order_params(FILE*                   log,
                          real                    bfac_init,
                          t_atoms*                atoms,
                          const rvec              x[],
-                         int                     ePBC,
+                         PbcType                 pbcType,
                          matrix                  box,
                          gmx_bool                bPhi,
                          gmx_bool                bPsi,
@@ -1181,7 +1182,7 @@ static void order_params(FILE*                   log,
         fprintf(fp,
                 "REMARK "
                 "B-factor field contains negative of dihedral order parameters\n");
-        write_pdbfile(fp, nullptr, atoms, x, ePBC, box, ' ', 0, nullptr);
+        write_pdbfile(fp, nullptr, atoms, x, pbcType, box, ' ', 0, nullptr);
         x0 = y0 = z0 = 1000.0;
         for (i = 0; (i < atoms->nr); i++)
         {
@@ -1401,7 +1402,7 @@ int gmx_chi(int argc, char* argv[])
     FILE*             log;
     int               nlist, idum, nbin;
     rvec*             x;
-    int               ePBC;
+    PbcType           pbcType;
     matrix            box;
     char              grpname[256];
     t_dlist*          dlist;
@@ -1487,7 +1488,7 @@ int gmx_chi(int argc, char* argv[])
     /* Find the chi angles using atoms struct and a list of amino acids */
     t_topology* top;
     snew(top, 1);
-    read_tps_conf(ftp2fn(efSTX, NFILE, fnm), top, &ePBC, &x, nullptr, box, FALSE);
+    read_tps_conf(ftp2fn(efSTX, NFILE, fnm), top, &pbcType, &x, nullptr, box, FALSE);
     t_atoms& atoms = top->atoms;
     if (atoms.pdbinfo == nullptr)
     {
@@ -1571,7 +1572,7 @@ int gmx_chi(int argc, char* argv[])
 
     /* Order parameters */
     order_params(log, opt2fn("-o", NFILE, fnm), maxchi, nlist, dlist, ftp2fn_null(efPDB, NFILE, fnm),
-                 bfac_init, &atoms, x, ePBC, box, bPhi, bPsi, bChi, oenv);
+                 bfac_init, &atoms, x, pbcType, box, bPhi, bPsi, bChi, oenv);
 
     /* Print ramachandran maps! */
     if (bRama)
index 3b4d8f0245b62ef82f02da79348558d12f4020bf..751c62f6d0f6515f428c441674a812634f64ffc0 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -1436,7 +1437,7 @@ int gmx_cluster(int argc, char* argv[])
     t_mat *     rms, *orig = nullptr;
     real*       eigenvalues;
     t_topology  top;
-    int         ePBC;
+    PbcType     pbcType;
     t_atoms     useatoms;
     real*       eigenvectors;
 
@@ -1669,10 +1670,10 @@ int gmx_cluster(int argc, char* argv[])
     if (bReadTraj)
     {
         /* don't read mass-database as masses (and top) are not used */
-        read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtps, nullptr, box, TRUE);
+        read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtps, nullptr, box, TRUE);
         if (bPBC)
         {
-            gpbc = gmx_rmpbc_init(&top.idef, ePBC, top.atoms.nr);
+            gpbc = gmx_rmpbc_init(&top.idef, pbcType, top.atoms.nr);
         }
 
         fprintf(stderr, "\nSelect group for least squares fit%s:\n", bReadMat ? "" : " and RMSD calculation");
index 28cc178ca70dbe4d968b959fb36a8566bfa95dbf..fd64ddf7f9e8c59a4d11f76f86d5f25a90ad0051 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2007, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -95,8 +96,8 @@ static void clust_size(const char*             ndx,
     /* Topology stuff */
     t_trxframe    fr;
     TpxFileHeader tpxh;
-    gmx_mtop_t*   mtop = nullptr;
-    int           ePBC = -1;
+    gmx_mtop_t*   mtop    = nullptr;
+    PbcType       pbcType = PbcType::Unset;
     int           ii, jj;
     real          temp, tfac;
     /* Cluster size distribution (matrix) */
@@ -132,7 +133,7 @@ static void clust_size(const char*             ndx,
         {
             gmx_fatal(FARGS, "tpr (%d atoms) and trajectory (%d atoms) do not match!", tpxh.natoms, natoms);
         }
-        ePBC = read_tpx(tpr, nullptr, nullptr, &natoms, nullptr, nullptr, mtop);
+        pbcType = read_tpx(tpr, nullptr, nullptr, &natoms, nullptr, nullptr, mtop);
     }
     if (ndf <= -1)
     {
@@ -187,7 +188,7 @@ static void clust_size(const char*             ndx,
         {
             if (bPBC)
             {
-                set_pbc(&pbc, ePBC, fr.box);
+                set_pbc(&pbc, pbcType, fr.box);
             }
             max_clust_size = 1;
             max_clust_ind  = -1;
index 73d2678989a89728e05d6239607cff7e0e20021f..213aaf11304477045b1329da1721281a2920a46e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -525,7 +526,7 @@ int gmx_confrms(int argc, char* argv[])
     FILE*       fp;
     char *      name1, *name2;
     t_topology *top1, *top2;
-    int         ePBC1, ePBC2;
+    PbcType     pbcType1, pbcType2;
     t_atoms *   atoms1, *atoms2;
     int         warn = 0;
     int         at;
@@ -561,7 +562,7 @@ int gmx_confrms(int argc, char* argv[])
     /* reading reference structure from first structure file */
     fprintf(stderr, "\nReading first structure file\n");
     snew(top1, 1);
-    read_tps_conf(conf1file, top1, &ePBC1, &x1, &v1, box1, TRUE);
+    read_tps_conf(conf1file, top1, &pbcType1, &x1, &v1, box1, TRUE);
     atoms1 = &(top1->atoms);
     fprintf(stderr, "%s\nContaining %d atoms in %d residues\n", *top1->name, atoms1->nr, atoms1->nres);
 
@@ -582,7 +583,7 @@ int gmx_confrms(int argc, char* argv[])
     /* reading second structure file */
     fprintf(stderr, "\nReading second structure file\n");
     snew(top2, 1);
-    read_tps_conf(conf2file, top2, &ePBC2, &x2, &v2, box2, TRUE);
+    read_tps_conf(conf2file, top2, &pbcType2, &x2, &v2, box2, TRUE);
     atoms2 = &(top2->atoms);
     fprintf(stderr, "%s\nContaining %d atoms in %d residues\n", *top2->name, atoms2->nr, atoms2->nres);
 
@@ -792,9 +793,9 @@ int gmx_confrms(int argc, char* argv[])
             fp = gmx_ffopen(outfile, "w");
             if (!bOne)
             {
-                write_pdbfile(fp, *top1->name, atoms1, x1, ePBC1, box1, ' ', 1, nullptr);
+                write_pdbfile(fp, *top1->name, atoms1, x1, pbcType1, box1, ' ', 1, nullptr);
             }
-            write_pdbfile(fp, *top2->name, atoms2, x2, ePBC2, box2, ' ', bOne ? -1 : 2, nullptr);
+            write_pdbfile(fp, *top2->name, atoms2, x2, pbcType2, box2, ' ', bOne ? -1 : 2, nullptr);
             gmx_ffclose(fp);
             break;
         case efGRO:
@@ -821,7 +822,7 @@ int gmx_confrms(int argc, char* argv[])
                 fprintf(stderr, "WARNING: cannot write the reference structure to %s file\n",
                         ftp2ext(fn2ftp(outfile)));
             }
-            write_sto_conf(outfile, *top2->name, atoms2, x2, v2, ePBC2, box2);
+            write_sto_conf(outfile, *top2->name, atoms2, x2, v2, pbcType2, box2);
             break;
     }
 
index 4d8bce1d0a18dc7b5dd895927000d80a3a92ab42..5fa50372bf70a467c0642d32076df33cc5b1f655 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -113,7 +114,7 @@ int gmx_covar(int argc, char* argv[])
     FILE*             out = nullptr; /* initialization makes all compilers happy */
     t_trxstatus*      status;
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     t_atoms*          atoms;
     rvec *            x, *xread, *xref, *xav, *xproj;
     matrix            box, zerobox;
@@ -164,7 +165,7 @@ int gmx_covar(int argc, char* argv[])
     xpmfile    = opt2fn_null("-xpm", NFILE, fnm);
     xpmafile   = opt2fn_null("-xpma", NFILE, fnm);
 
-    read_tps_conf(fitfile, &top, &ePBC, &xref, nullptr, box, TRUE);
+    read_tps_conf(fitfile, &top, &pbcType, &xref, nullptr, box, TRUE);
     atoms = &top.atoms;
 
     if (bFit)
@@ -238,7 +239,7 @@ int gmx_covar(int argc, char* argv[])
     /* Prepare reference frame */
     if (bPBC)
     {
-        gpbc = gmx_rmpbc_init(&top.idef, ePBC, atoms->nr);
+        gpbc = gmx_rmpbc_init(&top.idef, pbcType, atoms->nr);
         gmx_rmpbc(gpbc, atoms->nr, box, xref);
     }
     if (bFit)
@@ -293,7 +294,7 @@ int gmx_covar(int argc, char* argv[])
         }
     }
     write_sto_conf_indexed(opt2fn("-av", NFILE, fnm), "Average structure", atoms, xread, nullptr,
-                           epbcNONE, zerobox, natoms, index);
+                           PbcType::No, zerobox, natoms, index);
     sfree(xread);
 
     fprintf(stderr, "Constructing covariance matrix (%dx%d) ...\n", static_cast<int>(ndim),
index 3e9c1b350a18c01ae111150b3dc6d5fd7f9837de..067f46c797df0162caf343c1733dd9b0304cfa5c 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2008,2009,2010,2011,2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2008,2009,2010,2011,2012 by the GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -187,7 +189,7 @@ static void remove_jump(matrix box, int natoms, rvec xp[], rvec x[])
 }
 
 static void calc_mj(t_topology top,
-                    int        ePBC,
+                    PbcType    pbcType,
                     matrix     box,
                     gmx_bool   bNoJump,
                     int        isize,
@@ -209,7 +211,7 @@ static void calc_mj(t_topology top,
 
     if (!bNoJump)
     {
-        set_pbc(&pbc, ePBC, box);
+        set_pbc(&pbc, pbcType, box);
     }
 
     clear_rvec(tmp);
@@ -353,7 +355,7 @@ static void dielectric(FILE*                   fmj,
                        gmx_bool                bNoJump,
                        gmx_bool                bACF,
                        gmx_bool                bINT,
-                       int                     ePBC,
+                       PbcType                 pbcType,
                        t_topology              top,
                        t_trxframe              fr,
                        real                    temp,
@@ -453,7 +455,7 @@ static void dielectric(FILE*                   fmj,
     clear_rvec(mjd_tmp);
     clear_rvec(mdvec);
     clear_rvec(tmp);
-    gpbc = gmx_rmpbc_init(&top.idef, ePBC, fr.natoms);
+    gpbc = gmx_rmpbc_init(&top.idef, pbcType, fr.natoms);
 
     do
     {
@@ -517,7 +519,7 @@ static void dielectric(FILE*                   fmj,
 
         gmx_rmpbc_trxfr(gpbc, &fr);
 
-        calc_mj(top, ePBC, fr.box, bNoJump, nmols, indexm, fr.x, mtrans[nfr], mass2, qmol);
+        calc_mj(top, pbcType, fr.box, bNoJump, nmols, indexm, fr.x, mtrans[nfr], mass2, qmol);
 
         for (i = 0; i < isize; i++)
         {
@@ -855,7 +857,7 @@ int gmx_current(int argc, char* argv[])
     int               flags = 0;
     gmx_bool          bACF;
     gmx_bool          bINT;
-    int               ePBC = -1;
+    PbcType           pbcType = PbcType::Unset;
     int               nmols;
     int               i;
     real*             qmol;
@@ -941,7 +943,7 @@ int gmx_current(int argc, char* argv[])
     bACF = opt2bSet("-caf", NFILE, fnm);
     bINT = opt2bSet("-mc", NFILE, fnm);
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, nullptr, nullptr, box, TRUE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, nullptr, nullptr, box, TRUE);
 
     indexfn = ftp2fn_null(efNDX, NFILE, fnm);
     snew(grpname, 1);
@@ -1007,7 +1009,7 @@ int gmx_current(int argc, char* argv[])
     /* System information is read and prepared, dielectric() processes the frames
      * and calculates the requested quantities */
 
-    dielectric(fmj, fmd, outf, fcur, mcor, fmjdsp, bNoJump, bACF, bINT, ePBC, top, fr, temp, bfit, efit,
+    dielectric(fmj, fmd, outf, fcur, mcor, fmjdsp, bNoJump, bACF, bINT, pbcType, top, fr, temp, bfit, efit,
                bvit, evit, status, isize, nmols, nshift, index0, indexm, mass2, qmol, eps_rf, oenv);
 
     xvgrclose(fmj);
index 9525795f2a378d6884553d5d3fd423018fdad6d6..1a15db384e0c55c3b19b18d5d0909fc41c072c7d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -173,7 +174,7 @@ static void calc_electron_density(const char*             fn,
                                   double***               slDensity,
                                   int*                    nslices,
                                   t_topology*             top,
-                                  int                     ePBC,
+                                  PbcType                 pbcType,
                                   int                     axis,
                                   int                     nr_grps,
                                   real*                   slWidth,
@@ -224,7 +225,7 @@ static void calc_electron_density(const char*             fn,
         snew((*slDensity)[i], *nslices);
     }
 
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, top->atoms.nr);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, top->atoms.nr);
     /*********** Start processing trajectory ***********/
     do
     {
@@ -337,7 +338,7 @@ static void calc_density(const char*             fn,
                          double***               slDensity,
                          int*                    nslices,
                          t_topology*             top,
-                         int                     ePBC,
+                         PbcType                 pbcType,
                          int                     axis,
                          int                     nr_grps,
                          real*                   slWidth,
@@ -385,7 +386,7 @@ static void calc_density(const char*             fn,
         snew((*slDensity)[i], *nslices);
     }
 
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, top->atoms.nr);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, top->atoms.nr);
     /*********** Start processing trajectory ***********/
 
     snew(den_val, top->atoms.nr);
@@ -713,7 +714,7 @@ int gmx_density(int argc, char* argv[])
     int*        ngx;            /* sizes of groups            */
     t_electron* el_tab;         /* tabel with nr. of electrons*/
     t_topology* top;            /* topology               */
-    int         ePBC;
+    PbcType     pbcType;
     int*        index_center; /* index for centering group  */
     int**       index;        /* indices for all groups     */
 
@@ -744,7 +745,7 @@ int gmx_density(int argc, char* argv[])
     /* Calculate axis */
     axis = toupper(axtitle[0]) - 'X';
 
-    top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC); /* read topology file */
+    top = read_top(ftp2fn(efTPR, NFILE, fnm), &pbcType); /* read topology file */
 
     snew(grpname, ngrps);
     snew(index, ngrps);
@@ -773,13 +774,13 @@ int gmx_density(int argc, char* argv[])
         nr_electrons = get_electrons(&el_tab, ftp2fn(efDAT, NFILE, fnm));
         fprintf(stderr, "Read %d atomtypes from datafile\n", nr_electrons);
 
-        calc_electron_density(ftp2fn(efTRX, NFILE, fnm), index, ngx, &density, &nslices, top, ePBC,
-                              axis, ngrps, &slWidth, el_tab, nr_electrons, bCenter, index_center,
-                              ncenter, bRelative, oenv);
+        calc_electron_density(ftp2fn(efTRX, NFILE, fnm), index, ngx, &density, &nslices, top,
+                              pbcType, axis, ngrps, &slWidth, el_tab, nr_electrons, bCenter,
+                              index_center, ncenter, bRelative, oenv);
     }
     else
     {
-        calc_density(ftp2fn(efTRX, NFILE, fnm), index, ngx, &density, &nslices, top, ePBC, axis,
+        calc_density(ftp2fn(efTRX, NFILE, fnm), index, ngx, &density, &nslices, top, pbcType, axis,
                      ngrps, &slWidth, bCenter, index_center, ncenter, bRelative, oenv, dens_opt);
     }
 
index 1e98fb8f7cc685b62f1b7cde22ce26a4d9b343ce..f9c838b2a723f28db2e0c8245be4be1a8494c358 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -120,7 +121,7 @@ int gmx_densmap(int argc, char* argv[])
     FILE*             fp;
     t_trxstatus*      status;
     t_topology        top;
-    int               ePBC = -1;
+    PbcType           pbcType = PbcType::Unset;
     rvec *            x, xcom[2], direction, center, dx;
     matrix            box;
     real              t, m, mtot;
@@ -183,7 +184,7 @@ int gmx_densmap(int argc, char* argv[])
 
     if (ftp2bSet(efTPS, NFILE, fnm) || !ftp2bSet(efNDX, NFILE, fnm))
     {
-        read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &x, nullptr, box, bRadial);
+        read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &x, nullptr, box, bRadial);
     }
     if (!bRadial)
     {
@@ -315,7 +316,7 @@ int gmx_densmap(int argc, char* argv[])
         }
         else
         {
-            set_pbc(&pbc, ePBC, box);
+            set_pbc(&pbc, pbcType, box);
             for (i = 0; i < 2; i++)
             {
                 if (gnx[i] == 1)
index 194a3b4a3519e3858f0869ccccbadfd2a1983b85..0942cda75c196793ee994a10f7347ede56e58994 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -118,7 +118,7 @@ static void density_in_time(const char*             fn,
                             int*                    zslices,
                             int*                    tblock,
                             const t_topology*       top,
-                            int                     ePBC,
+                            PbcType                 pbcType,
                             int                     axis,
                             gmx_bool                bCenter,
                             gmx_bool                bps1d,
@@ -191,7 +191,7 @@ static void density_in_time(const char*             fn,
     /****Start trajectory processing***/
 
     /*Initialize Densdevel and PBC-remove*/
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, top->atoms.nr);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, top->atoms.nr);
 
     *Densdevel = nullptr;
 
@@ -708,7 +708,8 @@ int gmx_densorder(int argc, char* argv[])
     gmx_output_env_t*  oenv;
     t_topology*        top;
     char**             grpname;
-    int                ePBC, *ngx;
+    PbcType            pbcType;
+    int*               ngx;
     static real        binw      = 0.2;
     static real        binwz     = 0.05;
     static real        dens1     = 0.00;
@@ -789,7 +790,7 @@ int gmx_densorder(int argc, char* argv[])
     bRawOut  = opt2bSet("-or", NFILE, fnm);
     bGraph   = opt2bSet("-og", NFILE, fnm);
     bOut     = opt2bSet("-o", NFILE, fnm);
-    top      = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC);
+    top      = read_top(ftp2fn(efTPR, NFILE, fnm), &pbcType);
     snew(grpname, 1);
     snew(index, 1);
     snew(ngx, 1);
@@ -800,7 +801,7 @@ int gmx_densorder(int argc, char* argv[])
     get_index(&top->atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, ngx, index, grpname);
 
     density_in_time(ftp2fn(efTRX, NFILE, fnm), index, ngx, binw, binwz, nsttblock, &Densmap,
-                    &xslices, &yslices, &zslices, &tblock, top, ePBC, axis, bCenter, b1d, oenv);
+                    &xslices, &yslices, &zslices, &tblock, top, pbcType, axis, bCenter, b1d, oenv);
 
     if (ftorder > 0)
     {
index 5f5e719c24c758e176c9b7bf370280a9c00d80cd..3ab32fd48236b93c0565fd1d5e3e8d45966bb212 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d7ca1c3c1384cd21024cb093a785565dc4db8668..4f5e8f2d001ef1d82abbd07f52557b97f55f59fc 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -185,7 +186,7 @@ static void do_gkr(t_gkrbin*     gb,
                    const int     mindex[],
                    rvec          x[],
                    rvec          mu[],
-                   int           ePBC,
+                   PbcType       pbcType,
                    const matrix  box,
                    const t_atom* atom,
                    const int*    nAtom)
@@ -230,7 +231,7 @@ static void do_gkr(t_gkrbin*     gb,
             }
         }
     }
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
     grp0 = 0;
     grp1 = ncos - 1;
     for (i = 0; i < ngrp[grp0]; i++)
@@ -725,7 +726,7 @@ static void compute_avercos(int n, rvec dip[], real* dd, rvec axis, gmx_bool bPa
 }
 
 static void do_dip(const t_topology*       top,
-                   int                     ePBC,
+                   PbcType                 pbcType,
                    real                    volume,
                    const char*             fn,
                    const char*             out_mtot,
@@ -1019,7 +1020,7 @@ static void do_dip(const t_topology*       top,
 
         gkrbin = mk_gkrbin(rcut, rcmax, bPhi, ndegrees);
     }
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, natom);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, natom);
 
     /* Start while loop over frames */
     t0     = t;
@@ -1213,7 +1214,7 @@ static void do_dip(const t_topology*       top,
 
         if (bGkr)
         {
-            do_gkr(gkrbin, ncos, gnx, molindex, mols->index, x, dipole, ePBC, box, atom, gkatom);
+            do_gkr(gkrbin, ncos, gnx, molindex, mols->index, x, dipole, pbcType, box, atom, gkatom);
         }
 
         if (bTotal)
@@ -1622,7 +1623,7 @@ int gmx_dipoles(int argc, char* argv[])
     int         npargs;
     t_pargs*    ppa;
     t_topology* top;
-    int         ePBC;
+    PbcType     pbcType;
     int         k, natoms;
     matrix      box;
 
@@ -1681,7 +1682,7 @@ int gmx_dipoles(int argc, char* argv[])
     }
 
     snew(top, 1);
-    ePBC = read_tpx_top(ftp2fn(efTPR, NFILE, fnm), nullptr, box, &natoms, nullptr, nullptr, top);
+    pbcType = read_tpx_top(ftp2fn(efTPR, NFILE, fnm), nullptr, box, &natoms, nullptr, nullptr, top);
 
     snew(gnx, ncos);
     snew(grpname, ncos);
@@ -1694,7 +1695,7 @@ int gmx_dipoles(int argc, char* argv[])
     }
     nFF[0] = nFA;
     nFF[1] = nFB;
-    do_dip(top, ePBC, det(box), ftp2fn(efTRX, NFILE, fnm), opt2fn("-o", NFILE, fnm),
+    do_dip(top, pbcType, det(box), ftp2fn(efTRX, NFILE, fnm), opt2fn("-o", NFILE, fnm),
            opt2fn("-eps", NFILE, fnm), opt2fn("-a", NFILE, fnm), opt2fn("-d", NFILE, fnm),
            opt2fn_null("-cos", NFILE, fnm), opt2fn_null("-dip3d", NFILE, fnm),
            opt2fn_null("-adip", NFILE, fnm), bPairs, corrtype[0], opt2fn("-c", NFILE, fnm), bGkr,
index 608677f69fdc572f32c9b7009f0c0c681f5cb47c..045481ac3ec5fb4412e8bf856f3fda804b389090 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/math/vec.h"
 #include "gromacs/mdlib/force.h"
 #include "gromacs/mdlib/mdatoms.h"
+#include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/fcdata.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/ishift.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/rmpbc.h"
 #include "gromacs/topology/index.h"
@@ -145,21 +147,19 @@ static void print5(FILE* fp)
     fprintf(fp, "\n");
 }
 
-static void check_viol(FILE*       log,
-                       t_ilist*    disres,
-                       t_iparams   forceparams[],
-                       rvec        x[],
-                       rvec4       f[],
-                       t_pbc*      pbc,
-                       t_graph*    g,
-                       t_dr_result dr[],
-                       int         clust_id,
-                       int         isize,
-                       const int   index[],
-                       real        vvindex[],
-                       t_fcdata*   fcd)
+static void check_viol(FILE*                          log,
+                       const InteractionList&         disres,
+                       gmx::ArrayRef<const t_iparams> forceparams,
+                       rvec                           x[],
+                       rvec4                          f[],
+                       t_pbc*                         pbc,
+                       t_dr_result                    dr[],
+                       int                            clust_id,
+                       int                            isize,
+                       const int                      index[],
+                       real                           vvindex[],
+                       t_disresdata*                  disresdata)
 {
-    t_iatom*        forceatoms;
     int             i, j, nat, n, type, nviol, ndr, label;
     real            rt, mviol, tviol, viol, lam, dvdl, drt;
     rvec*           fshift;
@@ -175,7 +175,7 @@ static void check_viol(FILE*       log,
     {
         reset5();
     }
-    forceatoms = disres->iatoms;
+    gmx::ArrayRef<const int> forceatoms = disres.iatoms;
     for (j = 0; (j < isize); j++)
     {
         vvindex[j] = 0;
@@ -185,7 +185,7 @@ static void check_viol(FILE*       log,
     // The label for a distance restraint should be at most one larger
     // than the previous label.
     int label_old = forceparams[forceatoms[0]].disres.label;
-    for (i = 0; (i < disres->nr); i += nat)
+    for (i = 0; (i < disres.size()); i += nat)
     {
         type  = forceatoms[i];
         label = forceparams[type].disres.label;
@@ -201,9 +201,14 @@ static void check_viol(FILE*       log,
                       i / nat, label, label_old, label_old + 1);
         }
     }
+
+    // Set up t_fcdata, only needed for calling ta_disres()
+    t_fcdata fcd;
+    fcd.disres = disresdata;
+
     // Get offset for label index
     label_old = forceparams[forceatoms[0]].disres.label;
-    for (i = 0; (i < disres->nr);)
+    for (i = 0; (i < disres.size());)
     {
         type  = forceatoms[i];
         n     = 0;
@@ -215,27 +220,28 @@ static void check_viol(FILE*       log,
         do
         {
             n += nat;
-        } while (((i + n) < disres->nr)
+        } while (((i + n) < disres.size())
                  && (forceparams[forceatoms[i + n]].disres.label == label + label_old));
 
-        calc_disres_R_6(nullptr, nullptr, n, &forceatoms[i], x, pbc, fcd, nullptr);
+        calc_disres_R_6(nullptr, nullptr, n, &forceatoms[i], x, pbc, disresdata, nullptr);
 
-        if (fcd->disres.Rt_6[label] <= 0)
+        if (disresdata->Rt_6[label] <= 0)
         {
-            gmx_fatal(FARGS, "ndr = %d, rt_6 = %f", ndr, fcd->disres.Rt_6[label]);
+            gmx_fatal(FARGS, "ndr = %d, rt_6 = %f", ndr, disresdata->Rt_6[label]);
         }
 
-        rt = gmx::invsixthroot(fcd->disres.Rt_6[label]);
+        rt = gmx::invsixthroot(disresdata->Rt_6[label]);
         dr[clust_id].aver1[ndr] += rt;
         dr[clust_id].aver2[ndr] += gmx::square(rt);
         drt = 1.0 / gmx::power3(rt);
         dr[clust_id].aver_3[ndr] += drt;
-        dr[clust_id].aver_6[ndr] += fcd->disres.Rt_6[label];
+        dr[clust_id].aver_6[ndr] += disresdata->Rt_6[label];
 
         snew(fshift, SHIFTS);
-        ta_disres(n, &forceatoms[i], forceparams, x, f, fshift, pbc, g, lam, &dvdl, nullptr, fcd, nullptr);
+        ta_disres(n, &forceatoms[i], forceparams.data(), x, f, fshift, pbc, lam, &dvdl, nullptr,
+                  &fcd, nullptr);
         sfree(fshift);
-        viol = fcd->disres.sumviol;
+        viol = disresdata->sumviol;
 
         if (viol > 0)
         {
@@ -253,7 +259,7 @@ static void check_viol(FILE*       log,
             {
                 if (index[j] == forceparams[type].disres.label)
                 {
-                    vvindex[j] = gmx::invsixthroot(fcd->disres.Rt_6[label]);
+                    vvindex[j] = gmx::invsixthroot(disresdata->Rt_6[label]);
                 }
             }
         }
@@ -268,7 +274,7 @@ static void check_viol(FILE*       log,
 
     if (bFirst)
     {
-        fprintf(stderr, "\nThere are %d restraints and %d pairs\n", ndr, disres->nr / nat);
+        fprintf(stderr, "\nThere are %d restraints and %d pairs\n", ndr, disres.size() / nat);
         bFirst = FALSE;
     }
     if (ntop)
@@ -369,15 +375,15 @@ static gmx_bool is_core(int i, int isize, const int index[])
     return bIC;
 }
 
-static void dump_stats(FILE*               log,
-                       int                 nsteps,
-                       const t_disresdata& dd,
-                       const t_ilist*      disres,
-                       t_iparams           ip[],
-                       t_dr_result*        dr,
-                       int                 isize,
-                       int                 index[],
-                       t_atoms*            atoms)
+static void dump_stats(FILE*                          log,
+                       int                            nsteps,
+                       const t_disresdata&            dd,
+                       const InteractionList&         disres,
+                       gmx::ArrayRef<const t_iparams> ip,
+                       t_dr_result*                   dr,
+                       int                            isize,
+                       int                            index[],
+                       t_atoms*                       atoms)
 {
     t_dr_stats* drs;
 
@@ -385,15 +391,15 @@ static void dump_stats(FILE*               log,
     fprintf(log, "++++++++++++++ STATISTICS ++++++++++++++++++++++++\n");
     snew(drs, dd.nres);
     const int nra = interaction_function[F_DISRES].nratoms + 1;
-    for (int j = 0; j < disres->nr; j += nra)
+    for (int j = 0; j < disres.size(); j += nra)
     {
         // Note that the restraint i can be used by multiple pairs
-        const int i = disres->iatoms[j] - dd.type_min;
+        const int i = disres.iatoms[j] - dd.type_min;
         GMX_RELEASE_ASSERT(i >= 0 && i < dd.nres, "The restraint index should be in range");
 
-        drs[i].label  = ip[disres->iatoms[j]].disres.label;
+        drs[i].label  = ip[disres.iatoms[j]].disres.label;
         drs[i].bCore  = is_core(drs[i].label, isize, index);
-        drs[i].up1    = ip[disres->iatoms[j]].disres.up1;
+        drs[i].up1    = ip[disres.iatoms[j]].disres.up1;
         drs[i].r      = dr->aver1[i] / nsteps;
         drs[i].rT3    = gmx::invcbrt(dr->aver_3[i] / nsteps);
         drs[i].rT6    = gmx::invsixthroot(dr->aver_6[i] / nsteps);
@@ -402,8 +408,8 @@ static void dump_stats(FILE*               log,
         drs[i].violT6 = std::max(0.0, static_cast<double>(drs[i].rT6 - drs[i].up1));
         if (atoms)
         {
-            int j1 = disres->iatoms[j + 1];
-            int j2 = disres->iatoms[j + 2];
+            int j1 = disres.iatoms[j + 1];
+            int j2 = disres.iatoms[j + 2];
             atoms->pdbinfo[j1].bfac += drs[i].violT3 * 5;
             atoms->pdbinfo[j2].bfac += drs[i].violT3 * 5;
         }
@@ -420,15 +426,15 @@ static void dump_stats(FILE*               log,
     sfree(drs);
 }
 
-static void dump_clust_stats(FILE*               fp,
-                             const t_disresdata& dd,
-                             const t_ilist*      disres,
-                             t_iparams           ip[],
-                             t_blocka*           clust,
-                             t_dr_result         dr[],
-                             char*               clust_name[],
-                             int                 isize,
-                             int                 index[])
+static void dump_clust_stats(FILE*                          fp,
+                             const t_disresdata&            dd,
+                             const InteractionList&         disres,
+                             gmx::ArrayRef<const t_iparams> ip,
+                             t_blocka*                      clust,
+                             t_dr_result                    dr[],
+                             char*                          clust_name[],
+                             int                            isize,
+                             int                            index[])
 {
     int         k, nra, mmm = 0;
     double      sumV, maxV, sumVT3, sumVT6, maxVT3, maxVT6;
@@ -465,16 +471,16 @@ static void dump_clust_stats(FILE*               fp,
         for (int j = 0; j < dd.nres; j += nra)
         {
             // Note that the restraint i can be used by multiple pairs
-            const int i = disres->iatoms[j] - dd.type_min;
+            const int i = disres.iatoms[j] - dd.type_min;
 
             if (restraintHasBeenProcessed[i])
             {
                 continue;
             }
 
-            drs[i].label = ip[disres->iatoms[j]].disres.label;
+            drs[i].label = ip[disres.iatoms[j]].disres.label;
             drs[i].bCore = is_core(drs[i].label, isize, index);
-            drs[i].up1   = ip[disres->iatoms[j]].disres.up1;
+            drs[i].up1   = ip[disres.iatoms[j]].disres.up1;
             drs[i].r     = dr[k].aver1[i] / dr[k].nframes;
             if ((dr[k].aver_3[i] <= 0) || !std::isfinite(dr[k].aver_3[i]))
             {
@@ -519,15 +525,15 @@ static void init_dr_res(t_dr_result* dr, int ndr)
     dr->averv   = 0;
 }
 
-static void dump_disre_matrix(const char*       fn,
-                              t_dr_result*      dr,
-                              int               ndr,
-                              int               nsteps,
-                              t_idef*           idef,
-                              const gmx_mtop_t* mtop,
-                              real              max_dr,
-                              int               nlevels,
-                              gmx_bool          bThird)
+static void dump_disre_matrix(const char*                   fn,
+                              t_dr_result*                  dr,
+                              int                           ndr,
+                              int                           nsteps,
+                              const InteractionDefinitions& idef,
+                              const gmx_mtop_t*             mtop,
+                              real                          max_dr,
+                              int                           nlevels,
+                              gmx_bool                      bThird)
 {
     FILE*  fp;
     int*   resnr;
@@ -570,16 +576,16 @@ static void dump_disre_matrix(const char*       fn,
         snew(matrix[i], n_res);
     }
     nratoms = interaction_function[F_DISRES].nratoms;
-    nra     = (idef->il[F_DISRES].nr / (nratoms + 1));
+    nra     = (idef.il[F_DISRES].size() / (nratoms + 1));
     snew(ptr, nra + 1);
     index  = 0;
     nlabel = 0;
     ptr[0] = 0;
     snew(w_dr, ndr);
-    for (i = 0; (i < idef->il[F_DISRES].nr); i += nratoms + 1)
+    for (i = 0; (i < idef.il[F_DISRES].size()); i += nratoms + 1)
     {
-        tp    = idef->il[F_DISRES].iatoms[i];
-        label = idef->iparams[tp].disres.label;
+        tp    = idef.il[F_DISRES].iatoms[i];
+        label = idef.iparams[tp].disres.label;
 
         if (label != index)
         {
@@ -609,9 +615,9 @@ static void dump_disre_matrix(const char*       fn,
     {
         for (j = ptr[i]; (j < ptr[i + 1]); j += nratoms + 1)
         {
-            tp = idef->il[F_DISRES].iatoms[j];
-            ai = idef->il[F_DISRES].iatoms[j + 1];
-            aj = idef->il[F_DISRES].iatoms[j + 2];
+            tp = idef.il[F_DISRES].iatoms[j];
+            ai = idef.il[F_DISRES].iatoms[j + 1];
+            aj = idef.il[F_DISRES].iatoms[j + 2];
 
             ri = resnr[ai];
             rj = resnr[aj];
@@ -627,7 +633,7 @@ static void dump_disre_matrix(const char*       fn,
             {
                 fprintf(debug, "DR %d, atoms %d, %d, distance %g\n", i, ai, aj, rav);
             }
-            rviol = std::max(0.0_real, rav - idef->iparams[tp].disres.up1);
+            rviol = std::max(0.0_real, rav - idef.iparams[tp].disres.up1);
             matrix[ri][rj] += w_dr[i] * rviol;
             matrix[rj][ri] += w_dr[i] * rviol;
             hi = std::max(hi, matrix[ri][rj]);
@@ -697,20 +703,17 @@ int gmx_disre(int argc, char* argv[])
           "Use inverse third power averaging or linear for matrix output" }
     };
 
-    FILE *out = nullptr, *aver = nullptr, *numv = nullptr, *maxxv = nullptr, *xvg = nullptr;
-    gmx_localtop_t    top;
-    t_fcdata          fcd;
-    t_graph*          g;
-    int               i, j, kkk;
-    t_trxstatus*      status;
-    real              t;
-    rvec *            x, *xav = nullptr;
-    rvec4*            f;
-    matrix            box;
-    gmx_bool          bPDB;
-    int               isize;
-    int *             index = nullptr, *ind_fit = nullptr;
-    char*             grpname;
+    FILE *       out = nullptr, *aver = nullptr, *numv = nullptr, *maxxv = nullptr, *xvg = nullptr;
+    int          i, j, kkk;
+    t_trxstatus* status;
+    real         t;
+    rvec *       x, *xav = nullptr;
+    rvec4*       f;
+    matrix       box;
+    gmx_bool     bPDB;
+    int          isize;
+    int *        index = nullptr, *ind_fit = nullptr;
+    char*        grpname;
     t_cluster_ndx*    clust = nullptr;
     t_dr_result       dr, *dr_clust = nullptr;
     char**            leg;
@@ -770,20 +773,14 @@ int gmx_disre(int argc, char* argv[])
         atoms->havePdbInfo = TRUE;
     }
 
+    gmx_localtop_t top(topInfo.mtop()->ffparams);
     gmx_mtop_generate_local_top(*topInfo.mtop(), &top, ir->efep != efepNO);
+    const InteractionDefinitions& idef = top.idef;
 
-    g        = nullptr;
     pbc_null = nullptr;
-    if (ir->ePBC != epbcNONE)
+    if (ir->pbcType != PbcType::No)
     {
-        if (ir->bPeriodicMols)
-        {
-            pbc_null = &pbc;
-        }
-        else
-        {
-            g = mk_graph(fplog, &top.idef, 0, ntopatoms, FALSE, FALSE);
-        }
+        pbc_null = &pbc;
     }
 
     if (ftp2bSet(efNDX, NFILE, fnm))
@@ -808,19 +805,21 @@ int gmx_disre(int argc, char* argv[])
     }
 
     ir->dr_tau = 0.0;
-    init_disres(fplog, topInfo.mtop(), ir, nullptr, nullptr, &fcd, nullptr, FALSE);
+    t_disresdata disresdata;
+    init_disres(fplog, topInfo.mtop(), ir, DisResRunMode::AnalysisTool, DDRole::Master,
+                NumRanks::Single, MPI_COMM_NULL, nullptr, &disresdata, nullptr, FALSE);
 
     int natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
     snew(f, 5 * natoms);
 
-    init_dr_res(&dr, fcd.disres.nres);
+    init_dr_res(&dr, disresdata.nres);
     if (opt2bSet("-c", NFILE, fnm))
     {
         clust = cluster_index(fplog, opt2fn("-c", NFILE, fnm));
         snew(dr_clust, clust->clust->nr + 1);
         for (i = 0; (i <= clust->clust->nr); i++)
         {
-            init_dr_res(&dr_clust[i], fcd.disres.nres);
+            init_dr_res(&dr_clust[i], disresdata.nres);
         }
     }
     else
@@ -832,21 +831,21 @@ int gmx_disre(int argc, char* argv[])
     }
 
     auto mdAtoms = gmx::makeMDAtoms(fplog, *topInfo.mtop(), *ir, false);
-    atoms2md(topInfo.mtop(), ir, -1, nullptr, ntopatoms, mdAtoms.get());
+    atoms2md(topInfo.mtop(), ir, -1, {}, ntopatoms, mdAtoms.get());
     update_mdatoms(mdAtoms->mdatoms(), ir->fepvals->init_lambda);
-    if (ir->ePBC != epbcNONE)
+    if (ir->pbcType != PbcType::No)
     {
-        gpbc = gmx_rmpbc_init(&top.idef, ir->ePBC, natoms);
+        gpbc = gmx_rmpbc_init(idef, ir->pbcType, natoms);
     }
 
     j = 0;
     do
     {
-        if (ir->ePBC != epbcNONE)
+        if (ir->pbcType != PbcType::No)
         {
             if (ir->bPeriodicMols)
             {
-                set_pbc(&pbc, ir->ePBC, box);
+                set_pbc(&pbc, ir->pbcType, box);
             }
             else
             {
@@ -865,13 +864,13 @@ int gmx_disre(int argc, char* argv[])
             }
             my_clust = clust->inv_clust[j];
             range_check(my_clust, 0, clust->clust->nr);
-            check_viol(fplog, &(top.idef.il[F_DISRES]), top.idef.iparams, x, f, pbc_null, g,
-                       dr_clust, my_clust, isize, index, vvindex, &fcd);
+            check_viol(fplog, idef.il[F_DISRES], idef.iparams, x, f, pbc_null, dr_clust, my_clust,
+                       isize, index, vvindex, &disresdata);
         }
         else
         {
-            check_viol(fplog, &(top.idef.il[F_DISRES]), top.idef.iparams, x, f, pbc_null, g, &dr, 0,
-                       isize, index, vvindex, &fcd);
+            check_viol(fplog, idef.il[F_DISRES], idef.iparams, x, f, pbc_null, &dr, 0, isize, index,
+                       vvindex, &disresdata);
         }
         if (bPDB)
         {
@@ -907,26 +906,26 @@ int gmx_disre(int argc, char* argv[])
         j++;
     } while (read_next_x(oenv, status, &t, x, box));
     close_trx(status);
-    if (ir->ePBC != epbcNONE)
+    if (ir->pbcType != PbcType::No)
     {
         gmx_rmpbc_done(gpbc);
     }
 
     if (clust)
     {
-        dump_clust_stats(fplog, fcd.disres, &(top.idef.il[F_DISRES]), top.idef.iparams,
-                         clust->clust, dr_clust, clust->grpname, isize, index);
+        dump_clust_stats(fplog, disresdata, idef.il[F_DISRES], idef.iparams, clust->clust, dr_clust,
+                         clust->grpname, isize, index);
     }
     else
     {
-        dump_stats(fplog, j, fcd.disres, &(top.idef.il[F_DISRES]), top.idef.iparams, &dr, isize,
-                   index, bPDB ? atoms.get() : nullptr);
+        dump_stats(fplog, j, disresdata, idef.il[F_DISRES], idef.iparams, &dr, isize, index,
+                   bPDB ? atoms.get() : nullptr);
         if (bPDB)
         {
             write_sto_conf(opt2fn("-q", NFILE, fnm), "Coloured by average violation in Angstrom",
-                           atoms.get(), xav, nullptr, ir->ePBC, box);
+                           atoms.get(), xav, nullptr, ir->pbcType, box);
         }
-        dump_disre_matrix(opt2fn_null("-x", NFILE, fnm), &dr, fcd.disres.nres, j, &top.idef,
+        dump_disre_matrix(opt2fn_null("-x", NFILE, fnm), &dr, disresdata.nres, j, idef,
                           topInfo.mtop(), max_dr, nlevels, bThird);
         xvgrclose(out);
         xvgrclose(aver);
index f47b978ed5c1ddf39b5ca2fc7dec6150f83ad540..b1c9aa11c52be1727d7ac054babecdc8d1235ff0 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -266,15 +267,9 @@ static void check_oo(t_atoms* atoms)
 
     for (i = 0; (i < atoms->nr); i++)
     {
-        if (std::strcmp(*(atoms->atomname[i]), "OXT") == 0)
-        {
-            *atoms->atomname[i] = OOO;
-        }
-        else if (std::strcmp(*(atoms->atomname[i]), "O1") == 0)
-        {
-            *atoms->atomname[i] = OOO;
-        }
-        else if (std::strcmp(*(atoms->atomname[i]), "OC1") == 0)
+        if ((std::strcmp(*(atoms->atomname[i]), "OXT") == 0)
+            || (std::strcmp(*(atoms->atomname[i]), "O1") == 0)
+            || (std::strcmp(*(atoms->atomname[i]), "OC1") == 0))
         {
             *atoms->atomname[i] = OOO;
         }
@@ -517,7 +512,7 @@ int gmx_do_dssp(int argc, char* argv[])
     const char *      fnSCount, *fnArea, *fnTArea, *fnAArea;
     const char*       leg[] = { "Phobic", "Phylic" };
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     t_atoms*          atoms;
     t_matrix          mat;
     int               nres, nr0, naccr, nres_plus_separators;
@@ -557,7 +552,7 @@ int gmx_do_dssp(int argc, char* argv[])
     fnAArea    = opt2fn_null("-aa", NFILE, fnm);
     bDoAccSurf = ((fnArea != nullptr) || (fnTArea != nullptr) || (fnAArea != nullptr));
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xp, nullptr, box, FALSE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xp, nullptr, box, FALSE);
     atoms = &(top.atoms);
     check_oo(atoms);
     bPhbres = bPhobics(atoms);
@@ -670,7 +665,7 @@ int gmx_do_dssp(int argc, char* argv[])
     accr  = nullptr;
     naccr = 0;
 
-    gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top.idef, pbcType, natoms);
     do
     {
         t = output_env_conv_time(oenv, t);
@@ -685,7 +680,7 @@ int gmx_do_dssp(int argc, char* argv[])
         }
         gmx_rmpbc(gpbc, natoms, box, x);
         tapein = gmx_ffopen(pdbfile, "w");
-        write_pdbfile_indexed(tapein, nullptr, atoms, x, ePBC, box, ' ', -1, gnx, index, nullptr, FALSE);
+        write_pdbfile_indexed(tapein, nullptr, atoms, x, pbcType, box, ' ', -1, gnx, index, nullptr, FALSE);
         gmx_ffclose(tapein);
         /* strip_dssp returns the number of lines found in the dssp file, i.e.
          * the number of residues plus the separator lines */
index 344ad272ea00bb5650a2dacccd0fe6c8c9e8c5d0..2c1a9717c4e8f6817d4a0b5320c5f81dd951453a 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2011-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -54,6 +54,7 @@
 #include "gromacs/math/units.h"
 #include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/index.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/trajectory/trajectoryframe.h"
@@ -270,7 +271,7 @@ int gmx_dos(int argc, char* argv[])
     };
     FILE *            fp, *fplog;
     t_topology        top;
-    int               ePBC = -1;
+    PbcType           pbcType = PbcType::Unset;
     t_trxframe        fr;
     matrix            box;
     int               gnx;
@@ -347,7 +348,7 @@ int gmx_dos(int argc, char* argv[])
     please_cite(fplog, "Pascal2011a");
     please_cite(fplog, "Caleman2011b");
 
-    read_tps_conf(ftp2fn(efTPR, NFILE, fnm), &top, &ePBC, nullptr, nullptr, box, TRUE);
+    read_tps_conf(ftp2fn(efTPR, NFILE, fnm), &top, &pbcType, nullptr, nullptr, box, TRUE);
 
     /* Handle index groups */
     get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &grpNatoms, &index, &grpname);
index d77819d564112b147f116dfe603bf7c5fab1d18f..301ca77e8ee8da3abecf420d2a35eae848faf289 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -107,7 +108,7 @@ int gmx_dyecoupl(int argc, char* argv[])
     int  natoms;
 
     /*we rely on PBC autodetection (...currently)*/
-    int ePBC = -1;
+    PbcType pbcType = PbcType::Unset;
 
     real *   rvalues = nullptr, *kappa2values = nullptr, *rhist = nullptr, *khist = nullptr;
     t_pbc*   pbc = nullptr;
@@ -284,7 +285,7 @@ int gmx_dyecoupl(int argc, char* argv[])
 
                 if (bPBCdist)
                 {
-                    set_pbc(pbc, ePBC, fr.box);
+                    set_pbc(pbc, pbcType, fr.box);
                     pbc_dx(pbc, donpos, accpos, dist);
                 }
                 else
index 6491e164e25d019eac722ed06eb9b92af58c1ef7..25b5db751300e6417cf675caed0d6de5476311a6 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -537,7 +538,7 @@ int gmx_enemat(int argc, char* argv[])
         if (output_env_get_print_xvgr_codes(oenv))
         {
             char str1[STRLEN], str2[STRLEN];
-            if (output_env_get_xvg_format(oenv) == exvgXMGR)
+            if (output_env_get_xvg_format(oenv) == XvgFormat::Xmgr)
             {
                 sprintf(str1, "@ legend string ");
                 sprintf(str2, " ");
index 7e61ceb4f9ee4c0e3ec15f03e399e84f9a98fae9..25e6a2e9c73f10e1baac5de8b46f5476024df6a2 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 45505439f4efac51fc25319a1e5700fe88e6c137..c9567a806b3c9a39195219fdc9ebe8ee9b432c2a 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,7 @@
 #include "gromacs/math/do_fit.h"
 #include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/rmpbc.h"
 #include "gromacs/topology/index.h"
 #include "gromacs/topology/topology.h"
@@ -94,7 +95,7 @@ int gmx_filter(int argc, char* argv[])
     const char *      topfile, *lowfile, *highfile;
     gmx_bool          bTop = FALSE;
     t_topology        top;
-    int               ePBC = -1;
+    PbcType           pbcType = PbcType::Unset;
     rvec*             xtop;
     matrix            topbox, *box, boxf;
     char*             grpname;
@@ -137,10 +138,10 @@ int gmx_filter(int argc, char* argv[])
     }
     if (topfile)
     {
-        bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, topbox, TRUE);
+        bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, topbox, TRUE);
         if (bTop)
         {
-            gpbc = gmx_rmpbc_init(&top.idef, ePBC, top.atoms.nr);
+            gpbc = gmx_rmpbc_init(&top.idef, pbcType, top.atoms.nr);
             gmx_rmpbc(gpbc, top.atoms.nr, topbox, xtop);
         }
     }
index d6340a56b0923b674ab2ac35bcbfe29a74640cdb..6df19ae6fefeb385c95e1a13455e2c4b519df7d2 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -221,7 +222,7 @@ int gmx_gyrate(int argc, char* argv[])
     FILE*             out;
     t_trxstatus*      status;
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     rvec *            x, *x_s;
     rvec              xcm, gvec, gvec1;
     matrix            box, trans;
@@ -282,7 +283,7 @@ int gmx_gyrate(int argc, char* argv[])
         printf("Will print radius normalised by charge\n");
     }
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &x, nullptr, box, TRUE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &x, nullptr, box, TRUE);
     get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &gnx, &index, &grpname);
 
     if (nmol > gnx || gnx % nmol != 0)
@@ -328,7 +329,7 @@ int gmx_gyrate(int argc, char* argv[])
     }
     if (nz == 0)
     {
-        gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms);
+        gpbc = gmx_rmpbc_init(&top.idef, pbcType, natoms);
     }
     do
     {
index 790aa6f72b9d9408c3aa995aaaa420d2dc987790..1e52fe0b491ac09cc4d5024cae6931318e39455b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -71,7 +72,7 @@ static void calc_h2order(const char*             fn,
                          real*                   slWidth,
                          int*                    nslices,
                          const t_topology*       top,
-                         int                     ePBC,
+                         PbcType                 pbcType,
                          int                     axis,
                          gmx_bool                bMicel,
                          int                     micel[],
@@ -132,7 +133,7 @@ static void calc_h2order(const char*             fn,
 
     teller = 0;
 
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
     /*********** Start processing trajectory ***********/
     do
     {
@@ -300,7 +301,7 @@ int gmx_h2order(int argc, char* argv[])
     int ngx,          /* nr. of atomsin sol group   */
             nmic = 0; /* nr. of atoms in micelle    */
     t_topology* top;  /* topology           */
-    int         ePBC;
+    PbcType     pbcType;
     int *       index, /* indices for solvent group  */
             *micelle = nullptr;
     gmx_bool bMicel  = FALSE; /* think we're a micel        */
@@ -322,7 +323,7 @@ int gmx_h2order(int argc, char* argv[])
     }
     bMicel = opt2bSet("-nm", NFILE, fnm);
 
-    top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC); /* read topology file */
+    top = read_top(ftp2fn(efTPR, NFILE, fnm), &pbcType); /* read topology file */
 
     rd_index(ftp2fn(efNDX, NFILE, fnm), 1, &ngx, &index, &grpname);
 
@@ -332,7 +333,7 @@ int gmx_h2order(int argc, char* argv[])
     }
 
     calc_h2order(ftp2fn(efTRX, NFILE, fnm), index, ngx, &slDipole, &slOrder, &slWidth, &nslices,
-                 top, ePBC, axis, bMicel, micelle, nmic, oenv);
+                 top, pbcType, axis, bMicel, micelle, nmic, oenv);
 
     h2order_plot(slDipole, slOrder, opt2fn("-o", NFILE, fnm), nslices, slWidth, oenv);
 
index 584aef24350ff34ce5d5897effac9fde530961d6..b9bb443893eb3f393389fa65df1e6c6a868e8072 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -2847,7 +2848,7 @@ int gmx_hbond(int argc, char* argv[])
         gmx_fatal(FARGS, "Topology (%d atoms) does not match trajectory (%d atoms)", top.atoms.nr, natoms);
     }
 
-    bBox  = (ir->ePBC != epbcNONE);
+    bBox  = (ir->pbcType != PbcType::No);
     grid  = init_grid(bBox, box, (rcut > r2cut) ? rcut : r2cut, ngrid);
     nabin = static_cast<int>(acut / abin);
     nrbin = static_cast<int>(rcut / rbin);
index 13f5d04874998cd02647af2a0eb6ee81764de5ca..0b9399ae094ec89db859d024e4b06bf4a98b2f3e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -143,7 +144,7 @@ int gmx_helix(int argc, char* argv[])
     int               i, j, nall, nbb, nca, teller;
     int *             bbindex, *caindex, *allindex;
     t_topology*       top;
-    int               ePBC;
+    PbcType           pbcType;
     rvec *            x, *xref;
     real              t;
     real              rms;
@@ -166,7 +167,7 @@ int gmx_helix(int argc, char* argv[])
 
     bRange = (opt2parg_bSet("-ahxstart", asize(pa), pa) && opt2parg_bSet("-ahxend", asize(pa), pa));
 
-    top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC);
+    top = read_top(ftp2fn(efTPR, NFILE, fnm), &pbcType);
 
     natoms = read_first_x(oenv, &status, opt2fn("-f", NFILE, fnm), &t, &x, box);
 
@@ -211,7 +212,7 @@ int gmx_helix(int argc, char* argv[])
         pr_bb(stdout, nres, bb);
     }
 
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
 
     teller = 0;
     do
@@ -237,7 +238,7 @@ int gmx_helix(int argc, char* argv[])
             if (teller == 1)
             {
                 write_sto_conf(opt2fn("-cz", NFILE, fnm), "Helix fitted to Z-Axis", &(top->atoms),
-                               x, nullptr, ePBC, box);
+                               x, nullptr, pbcType, box);
             }
 
             xf[efhRAD].val   = radius(xf[efhRAD].fp2, nca, caindex, x);
index fef5d10ae4b7412eec6c64d30e69095f2c380682..c5651ce84ae7d0e38f686ff007ff2aaf06915d5a 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -126,11 +126,11 @@ int gmx_helixorient(int argc, char* argv[])
     t_pbc  pbc;
     matrix A;
 
-    FILE *fpaxis, *fpcenter, *fptilt, *fprotation;
-    FILE *fpradius, *fprise, *fptwist;
-    FILE *fptheta1, *fptheta2, *fptheta3;
-    FILE* fpbending;
-    int   ePBC;
+    FILE *  fpaxis, *fpcenter, *fptilt, *fprotation;
+    FILE *  fpradius, *fprise, *fptwist;
+    FILE *  fptheta1, *fptheta2, *fptheta3;
+    FILE*   fpbending;
+    PbcType pbcType;
 
     gmx_output_env_t* oenv;
     gmx_rmpbc_t       gpbc = nullptr;
@@ -168,7 +168,7 @@ int gmx_helixorient(int argc, char* argv[])
         return 0;
     }
 
-    top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC);
+    top = read_top(ftp2fn(efTPR, NFILE, fnm), &pbcType);
 
     for (i = 0; i < 3; i++)
     {
@@ -249,12 +249,12 @@ int gmx_helixorient(int argc, char* argv[])
     unitaxes[1][1] = 1;
     unitaxes[2][2] = 1;
 
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
 
     do
     {
         /* initialisation for correct distance calculations */
-        set_pbc(&pbc, ePBC, box);
+        set_pbc(&pbc, pbcType, box);
         /* make molecules whole again */
         gmx_rmpbc(gpbc, natoms, box, x);
 
index c7f81a1ee47eedacd2b344a7260e5bf9ca80b7ac..9e45a5ba7d157842597cdb64ab9d28dde809a6f1 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -61,7 +61,7 @@
 #include "gromacs/utility/smalloc.h"
 
 static void find_tetra_order_grid(t_topology top,
-                                  int        ePBC,
+                                  PbcType    pbcType,
                                   int        natoms,
                                   matrix     box,
                                   rvec       x[],
@@ -115,8 +115,8 @@ static void find_tetra_order_grid(t_topology top,
     snew(skmol, maxidx);
 
     /* Must init pbc every step because of pressure coupling */
-    set_pbc(&pbc, ePBC, box);
-    gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms);
+    set_pbc(&pbc, pbcType, box);
+    gpbc = gmx_rmpbc_init(&top.idef, pbcType, natoms);
     gmx_rmpbc(gpbc, natoms, box, x);
 
     *sgmean = 0.0;
@@ -280,7 +280,7 @@ static void calc_tetra_order_interface(const char*       fnNDX,
 {
     FILE *       fpsg = nullptr, *fpsk = nullptr;
     t_topology   top;
-    int          ePBC;
+    PbcType      pbcType;
     t_trxstatus* status;
     int          natoms;
     real         t;
@@ -300,7 +300,7 @@ static void calc_tetra_order_interface(const char*       fnNDX,
      * i.e 1D Row-major order in (t,x,y) */
 
 
-    read_tps_conf(fnTPS, &top, &ePBC, &xtop, nullptr, box, FALSE);
+    read_tps_conf(fnTPS, &top, &pbcType, &xtop, nullptr, box, FALSE);
 
     *nslicex = static_cast<int>(box[XX][XX] / binw + onehalf); /*Calculate slicenr from binwidth*/
     *nslicey = static_cast<int>(box[YY][YY] / binw + onehalf);
@@ -366,7 +366,7 @@ static void calc_tetra_order_interface(const char*       fnNDX,
             }
         }
 
-        find_tetra_order_grid(top, ePBC, natoms, box, x, isize[0], index[0], &sg, &sk, *nslicex,
+        find_tetra_order_grid(top, pbcType, natoms, box, x, isize[0], index[0], &sg, &sk, *nslicex,
                               *nslicey, nslicez, sg_grid, sk_grid);
         GMX_RELEASE_ASSERT(sk_fravg != nullptr, "Trying to dereference NULL sk_fravg pointer");
         for (i = 0; i < *nslicex; i++)
index fb644192ead08ec07342d11c87340105c9b26c76..7ad065cbf823cae0421a6bad342dfaf13eea5581 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b22d063d2ad17b24637c4485f0e69754f609d2eb..288cff7feddf509f77bce3d1c9f3b1487e841d78 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -900,7 +901,7 @@ int gmx_make_edi(int argc, char* argv[])
 
     /*to read topology file*/
     t_topology top;
-    int        ePBC;
+    PbcType    pbcType;
     matrix     topbox;
     rvec*      xtop;
     gmx_bool   bFit1;
@@ -998,7 +999,7 @@ int gmx_make_edi(int argc, char* argv[])
     read_eigenvectors(EigvecFile, &nav, &bFit1, &xref1, &edi_params.fitmas, &xav1,
                       &edi_params.pcamas, &nvec1, &eignr1, &eigvec1, &eigval1);
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, topbox, false);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, topbox, false);
     atoms = &top.atoms;
 
 
index e3799edfff03c517ebad3ad4afd980114efa8756..a6a9058d7aeb6878ad564bc9b8309adc7fdad4b3 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -114,7 +115,7 @@ static void calc_mat(int        nres,
                      real       trunc,
                      real**     mdmat,
                      int**      nmat,
-                     int        ePBC,
+                     PbcType    pbcType,
                      matrix     box)
 {
     int   i, j, resi, resj;
@@ -122,7 +123,7 @@ static void calc_mat(int        nres,
     t_pbc pbc;
     rvec  ddx;
 
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
     trunc2 = gmx::square(trunc);
     for (resi = 0; (resi < nres); resi++)
     {
@@ -206,7 +207,7 @@ int gmx_mdmat(int argc, char* argv[])
 
     FILE *     out = nullptr, *fp;
     t_topology top;
-    int        ePBC;
+    PbcType    pbcType;
     t_atoms    useatoms;
     int        isize;
     int*       index;
@@ -242,7 +243,7 @@ int gmx_mdmat(int argc, char* argv[])
         fprintf(stderr, "Will calculate number of different contacts\n");
     }
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &x, nullptr, box, FALSE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &x, nullptr, box, FALSE);
 
     fprintf(stderr, "Select group for analysis\n");
     get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
@@ -312,7 +313,7 @@ int gmx_mdmat(int argc, char* argv[])
     rhi.g = 0.0;
     rhi.b = 0.0;
 
-    gpbc = gmx_rmpbc_init(&top.idef, ePBC, trxnat);
+    gpbc = gmx_rmpbc_init(&top.idef, pbcType, trxnat);
 
     if (bFrames)
     {
@@ -322,7 +323,7 @@ int gmx_mdmat(int argc, char* argv[])
     {
         gmx_rmpbc(gpbc, trxnat, box, x);
         nframes++;
-        calc_mat(nres, natoms, rndx, x, index, truncate, mdmat, nmat, ePBC, box);
+        calc_mat(nres, natoms, rndx, x, index, truncate, mdmat, nmat, pbcType, box);
         for (i = 0; (i < nres); i++)
         {
             for (j = 0; (j < natoms); j++)
index fff6505628e7e870e6f0a8eb0aab502e13ef04a8..ed51f851d9a415b1f73a8feec5181a1d4a6896ee 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -63,7 +64,8 @@
 #include "gromacs/utility/smalloc.h"
 
 
-static void periodic_dist(int ePBC, matrix box, rvec x[], int n, const int index[], real* rmin, real* rmax, int* min_ind)
+static void
+periodic_dist(PbcType pbcType, matrix box, rvec x[], int n, const int index[], real* rmin, real* rmax, int* min_ind)
 {
 #define NSHIFT_MAX 26
     int  nsz, nshift, sx, sy, sz, i, j, s;
@@ -71,18 +73,18 @@ static void periodic_dist(int ePBC, matrix box, rvec x[], int n, const int index
     rvec shift[NSHIFT_MAX], d0, d;
 
     sqr_box = std::min(norm2(box[XX]), norm2(box[YY]));
-    if (ePBC == epbcXYZ)
+    if (pbcType == PbcType::Xyz)
     {
         sqr_box = std::min(sqr_box, norm2(box[ZZ]));
         nsz     = 1;
     }
-    else if (ePBC == epbcXY)
+    else if (pbcType == PbcType::XY)
     {
         nsz = 0;
     }
     else
     {
-        gmx_fatal(FARGS, "pbc = %s is not supported by g_mindist", epbc_names[ePBC]);
+        gmx_fatal(FARGS, "pbc = %s is not supported by g_mindist", c_pbcTypeNames[pbcType].c_str());
     }
 
     nshift = 0;
@@ -138,7 +140,7 @@ static void periodic_dist(int ePBC, matrix box, rvec x[], int n, const int index
 static void periodic_mindist_plot(const char*             trxfn,
                                   const char*             outfn,
                                   const t_topology*       top,
-                                  int                     ePBC,
+                                  PbcType                 pbcType,
                                   int                     n,
                                   int                     index[],
                                   gmx_bool                bSplit,
@@ -172,7 +174,7 @@ static void periodic_mindist_plot(const char*             trxfn,
 
     if (nullptr != top)
     {
-        gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
+        gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
     }
 
     bFirst = TRUE;
@@ -183,7 +185,7 @@ static void periodic_mindist_plot(const char*             trxfn,
             gmx_rmpbc(gpbc, natoms, box, x);
         }
 
-        periodic_dist(ePBC, box, x, n, index, &rmin, &rmax, ind_min);
+        periodic_dist(pbcType, box, x, n, index, &rmin, &rmax, ind_min);
         if (rmin < rmint)
         {
             rmint    = rmin;
@@ -216,7 +218,7 @@ static void periodic_mindist_plot(const char*             trxfn,
 
 static void calc_dist(real     rcut,
                       gmx_bool bPBC,
-                      int      ePBC,
+                      PbcType  pbcType,
                       matrix   box,
                       rvec     x[],
                       int      nx1,
@@ -253,7 +255,7 @@ static void calc_dist(real     rcut,
     /* Must init pbc every step because of pressure coupling */
     if (bPBC)
     {
-        set_pbc(&pbc, ePBC, box);
+        set_pbc(&pbc, pbcType, box);
     }
     if (index2)
     {
@@ -355,7 +357,7 @@ static void dist_plot(const char*             fn,
                       int                     nres,
                       int*                    residue,
                       gmx_bool                bPBC,
-                      int                     ePBC,
+                      PbcType                 pbcType,
                       gmx_bool                bGroup,
                       gmx_bool                bEachResEachTime,
                       gmx_bool                bPrintResName,
@@ -496,7 +498,7 @@ static void dist_plot(const char*             fn,
         {
             if (ng == 1)
             {
-                calc_dist(rcut, bPBC, ePBC, box, x0, gnx[0], gnx[0], index[0], index[0], bGroup,
+                calc_dist(rcut, bPBC, pbcType, box, x0, gnx[0], gnx[0], index[0], index[0], bGroup,
                           &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
                 fprintf(dist, "  %12e", bMin ? dmin : dmax);
                 if (num)
@@ -510,7 +512,7 @@ static void dist_plot(const char*             fn,
                 {
                     for (k = i + 1; (k < ng); k++)
                     {
-                        calc_dist(rcut, bPBC, ePBC, box, x0, gnx[i], gnx[k], index[i], index[k],
+                        calc_dist(rcut, bPBC, pbcType, box, x0, gnx[i], gnx[k], index[i], index[k],
                                   bGroup, &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
                         fprintf(dist, "  %12e", bMin ? dmin : dmax);
                         if (num)
@@ -526,7 +528,7 @@ static void dist_plot(const char*             fn,
             GMX_RELEASE_ASSERT(ng > 1, "Must have more than one group when not using -matrix");
             for (i = 1; (i < ng); i++)
             {
-                calc_dist(rcut, bPBC, ePBC, box, x0, gnx[0], gnx[i], index[0], index[i], bGroup,
+                calc_dist(rcut, bPBC, pbcType, box, x0, gnx[0], gnx[i], index[0], index[i], bGroup,
                           &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
                 fprintf(dist, "  %12e", bMin ? dmin : dmax);
                 if (num)
@@ -537,7 +539,7 @@ static void dist_plot(const char*             fn,
                 {
                     for (j = 0; j < nres; j++)
                     {
-                        calc_dist(rcut, bPBC, ePBC, box, x0, residue[j + 1] - residue[j], gnx[i],
+                        calc_dist(rcut, bPBC, pbcType, box, x0, residue[j + 1] - residue[j], gnx[i],
                                   &(index[0][residue[j]]), index[i], bGroup, &dmin, &dmax, &nmin,
                                   &nmax, &min1r, &min2r, &max1r, &max2r);
                         mindres[i - 1][j] = std::min(mindres[i - 1][j], dmin);
@@ -736,9 +738,9 @@ int gmx_mindist(int argc, char* argv[])
         { "-printresname", FALSE, etBOOL, { &bPrintResName }, "Write residue names" }
     };
     gmx_output_env_t* oenv;
-    t_topology*       top  = nullptr;
-    int               ePBC = -1;
-    rvec*             x    = nullptr;
+    t_topology*       top     = nullptr;
+    PbcType           pbcType = PbcType::Unset;
+    rvec*             x       = nullptr;
     matrix            box;
     gmx_bool          bTop = FALSE;
 
@@ -798,7 +800,7 @@ int gmx_mindist(int argc, char* argv[])
     if (tpsfnm || resfnm || !ndxfnm)
     {
         snew(top, 1);
-        bTop = read_tps_conf(tpsfnm, top, &ePBC, &x, nullptr, box, FALSE);
+        bTop = read_tps_conf(tpsfnm, top, &pbcType, &x, nullptr, box, FALSE);
         if (bPI && !bTop)
         {
             printf("\nWARNING: Without a run input file a trajectory with broken molecules will "
@@ -842,13 +844,13 @@ int gmx_mindist(int argc, char* argv[])
 
     if (bPI)
     {
-        periodic_mindist_plot(trxfnm, distfnm, top, ePBC, gnx[0], index[0], bSplit, oenv);
+        periodic_mindist_plot(trxfnm, distfnm, top, pbcType, gnx[0], index[0], bSplit, oenv);
     }
     else
     {
         dist_plot(trxfnm, atmfnm, distfnm, numfnm, resfnm, oxfnm, rcutoff, bMat,
                   top ? &(top->atoms) : nullptr, ng, index, gnx, grpname, bSplit, !bMax, nres,
-                  residues, bPBC, ePBC, bGroup, bEachResEachTime, bPrintResName, oenv);
+                  residues, bPBC, pbcType, bGroup, bEachResEachTime, bPrintResName, oenv);
     }
 
     do_view(oenv, distfnm, "-nxy");
index 39882709e8f8f86cf8c44578253449fbcd254a5b..deb0ac25391c0c9e3a07147c30819bc12cc734c4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -564,7 +565,7 @@ static void printmol(t_corr*                 curr,
                      const int*              molindex,
                      const t_topology*       top,
                      rvec*                   x,
-                     int                     ePBC,
+                     PbcType                 pbcType,
                      matrix                  box,
                      const gmx_output_env_t* oenv)
 {
@@ -646,7 +647,7 @@ static void printmol(t_corr*                 curr,
         {
             pdbinfo[i].bfac *= scale;
         }
-        write_sto_conf(fn_pdb, "molecular MSD", &top->atoms, x, nullptr, ePBC, box);
+        write_sto_conf(fn_pdb, "molecular MSD", &top->atoms, x, nullptr, pbcType, box);
     }
 }
 
@@ -657,7 +658,7 @@ static void printmol(t_corr*                 curr,
 static int corr_loop(t_corr*                  curr,
                      const char*              fn,
                      const t_topology*        top,
-                     int                      ePBC,
+                     PbcType                  pbcType,
                      gmx_bool                 bMol,
                      int                      gnx[],
                      int*                     index[],
@@ -720,7 +721,7 @@ static int corr_loop(t_corr*                  curr,
 
     if (bMol)
     {
-        gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
+        gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
     }
 
     /* the loop over all frames */
@@ -905,7 +906,7 @@ static void do_corr(const char*             trx_file,
                     real                    t_pdb,
                     int                     nrgrp,
                     t_topology*             top,
-                    int                     ePBC,
+                    PbcType                 pbcType,
                     gmx_bool                bTen,
                     gmx_bool                bMW,
                     gmx_bool                bRmCOMM,
@@ -954,7 +955,7 @@ static void do_corr(const char*             trx_file,
     msd = std::make_unique<t_corr>(nrgrp, type, axis, dim_factor, mol_file == nullptr ? 0 : gnx[0],
                                    bTen, bMW, dt, top, beginfit, endfit);
 
-    nat_trx = corr_loop(msd.get(), trx_file, top, ePBC, mol_file ? gnx[0] != 0 : false, gnx.data(),
+    nat_trx = corr_loop(msd.get(), trx_file, top, pbcType, mol_file ? gnx[0] != 0 : false, gnx.data(),
                         index, (mol_file != nullptr) ? calc1_mol : (bMW ? calc1_mw : calc1_norm),
                         bTen, gnx_com, index_com, dt, t_pdb, pdb_file ? &x : nullptr, box, oenv);
 
@@ -986,7 +987,7 @@ static void do_corr(const char*             trx_file,
         {
             snew(top->atoms.pdbinfo, top->atoms.nr);
         }
-        printmol(msd.get(), mol_file, pdb_file, index[0], top, x, ePBC, box, oenv);
+        printmol(msd.get(), mol_file, pdb_file, index[0], top, x, pbcType, box, oenv);
         top->atoms.nr = i;
     }
 
@@ -1139,7 +1140,7 @@ int gmx_msd(int argc, char* argv[])
 #define NFILE asize(fnm)
 
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     matrix            box;
     const char *      trx_file, *tps_file, *ndx_file, *msd_file, *mol_file, *pdb_file;
     rvec*             xdum;
@@ -1212,14 +1213,14 @@ int gmx_msd(int argc, char* argv[])
         gmx_fatal(FARGS, "Can only calculate the full tensor for 3D msd");
     }
 
-    bTop = read_tps_conf(tps_file, &top, &ePBC, &xdum, nullptr, box, bMW || bRmCOMM);
+    bTop = read_tps_conf(tps_file, &top, &pbcType, &xdum, nullptr, box, bMW || bRmCOMM);
     if (mol_file && !bTop)
     {
         gmx_fatal(FARGS, "Could not read a topology from %s. Try a tpr file instead.", tps_file);
     }
 
-    do_corr(trx_file, ndx_file, msd_file, mol_file, pdb_file, t_pdb, ngroup, &top, ePBC, bTen, bMW,
-            bRmCOMM, type, dim_factor, axis, dt, beginfit, endfit, oenv);
+    do_corr(trx_file, ndx_file, msd_file, mol_file, pdb_file, t_pdb, ngroup, &top, pbcType, bTen,
+            bMW, bRmCOMM, type, dim_factor, axis, dt, beginfit, endfit, oenv);
 
     done_top(&top);
     view_all(oenv, NFILE, fnm);
index 24d4f70aa90e5792fd47823da21c11c503be49fc..2df043607d1114c8829400a4519bfae9da0de0cd 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index ea3d6f47d3281ab830b28d0a21b9411accc9e1ca..78859917b490e235375a61b168d584d730a4ebef 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -83,7 +84,7 @@ int gmx_nmens(int argc, char* argv[])
 
     t_trxstatus*        out;
     t_topology          top;
-    int                 ePBC;
+    PbcType             pbcType;
     t_atoms*            atoms;
     rvec *              xtop, *xref, *xav, *xout1, *xout2;
     gmx_bool            bDMR, bDMA, bFit;
@@ -122,7 +123,7 @@ int gmx_nmens(int argc, char* argv[])
     read_eigenvectors(opt2fn("-v", NFILE, fnm), &natoms, &bFit, &xref, &bDMR, &xav, &bDMA, &nvec,
                       &eignr, &eigvec, &eigval);
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, box, bDMA);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, box, bDMA);
     atoms = &top.atoms;
 
     printf("\nSelect an index group of %d elements that corresponds to the eigenvectors\n", natoms);
index 37284b225d23f83e91f2a2c3feeb5e3f9eeb66e2..8d597542cd9af214a854ffd4ee2b8bde7843f4cc 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -197,22 +198,18 @@ static void get_orires_parms(const char* topnm, t_inputrec* ir, int* nor, int* n
     done_top_mtop(&top, &mtop);
 }
 
-static int get_bounds(real** bounds, int** index, int** dr_pair, int* npairs, gmx_localtop_t* top)
+static int get_bounds(real** bounds, int** index, int** dr_pair, int* npairs, const InteractionDefinitions& idef)
 {
-    t_functype* functype;
-    t_iparams*  ip;
-    int         i, j, k, type, ftype, natom;
-    t_ilist*    disres;
-    t_iatom*    iatom;
-    real*       b;
-    int *       ind, *pair;
-    int         nb, label1;
-
-    functype = top->idef.functype;
-    ip       = top->idef.iparams;
+    int   i, j, k, type, ftype, natom;
+    real* b;
+    int * ind, *pair;
+    int   nb, label1;
+
+    gmx::ArrayRef<const t_functype> functype = idef.functype;
+    gmx::ArrayRef<const t_iparams>  iparams  = idef.iparams;
 
     /* Count how many distance restraint there are... */
-    nb = top->idef.il[F_DISRES].nr;
+    nb = idef.il[F_DISRES].size();
     if (nb == 0)
     {
         gmx_fatal(FARGS, "No distance restraints in topology!\n");
@@ -225,14 +222,14 @@ static int get_bounds(real** bounds, int** index, int** dr_pair, int* npairs, gm
 
     /* Fill the bound array */
     nb = 0;
-    for (i = 0; (i < top->idef.ntypes); i++)
+    for (gmx::index i = 0; i < functype.ssize(); i++)
     {
         ftype = functype[i];
         if (ftype == F_DISRES)
         {
 
-            label1  = ip[i].disres.label;
-            b[nb]   = ip[i].disres.up1;
+            label1  = iparams[i].disres.label;
+            b[nb]   = iparams[i].disres.up1;
             ind[nb] = label1;
             nb++;
         }
@@ -240,18 +237,18 @@ static int get_bounds(real** bounds, int** index, int** dr_pair, int* npairs, gm
     *bounds = b;
 
     /* Fill the index array */
-    label1 = -1;
-    disres = &(top->idef.il[F_DISRES]);
-    iatom  = disres->iatoms;
-    for (i = j = k = 0; (i < disres->nr);)
+    label1                          = -1;
+    const InteractionList&   disres = idef.il[F_DISRES];
+    gmx::ArrayRef<const int> iatom  = disres.iatoms;
+    for (i = j = k = 0; (i < disres.size());)
     {
         type  = iatom[i];
-        ftype = top->idef.functype[type];
+        ftype = functype[type];
         natom = interaction_function[ftype].nratoms + 1;
-        if (label1 != top->idef.iparams[type].disres.label)
+        if (label1 != iparams[type].disres.label)
         {
             pair[j] = k;
-            label1  = top->idef.iparams[type].disres.label;
+            label1  = iparams[type].disres.label;
             j++;
         }
         k++;
@@ -410,13 +407,12 @@ int gmx_nmr(int argc, char* argv[])
 
     FILE /* *out     = NULL,*/ *out_disre = nullptr, *fp_pairs = nullptr, *fort = nullptr,
                                *fodt = nullptr, *foten = nullptr;
-    ener_file_t    fp;
-    int            timecheck = 0;
-    gmx_localtop_t top;
-    gmx_enxnm_t*   enm = nullptr;
-    t_enxframe     fr;
-    int            nre, teller, teller_disre;
-    int            nor = 0, nex = 0, norfr = 0, enx_i = 0;
+    ener_file_t  fp;
+    int          timecheck = 0;
+    gmx_enxnm_t* enm       = nullptr;
+    t_enxframe   fr;
+    int          nre, teller, teller_disre;
+    int          nor = 0, nex = 0, norfr = 0, enx_i = 0;
     real *bounds = nullptr, *violaver = nullptr, *oobs = nullptr, *orient = nullptr, *odrms = nullptr;
     int *       index = nullptr, *pair = nullptr, norsel = 0, *orsel = nullptr, *or_label = nullptr;
     int         nbounds = 0, npairs;
@@ -479,7 +475,8 @@ int gmx_nmr(int argc, char* argv[])
     t_inputrec  irInstance;
     t_inputrec* ir = &irInstance;
     init_enxframe(&fr);
-    gmx::TopologyInformation topInfo;
+    gmx::TopologyInformation        topInfo;
+    std::unique_ptr<gmx_localtop_t> top;
     if (!bDisRe)
     {
         if (bORIRE || bOTEN)
@@ -617,9 +614,10 @@ int gmx_nmr(int argc, char* argv[])
     {
         {
             topInfo.fillFromInputFile(ftp2fn(efTPR, NFILE, fnm));
-            gmx_mtop_generate_local_top(*topInfo.mtop(), &top, ir->efep != efepNO);
+            top = std::make_unique<gmx_localtop_t>(topInfo.mtop()->ffparams);
+            gmx_mtop_generate_local_top(*topInfo.mtop(), top.get(), ir->efep != efepNO);
         }
-        nbounds = get_bounds(&bounds, &index, &pair, &npairs, &top);
+        nbounds = get_bounds(&bounds, &index, &pair, &npairs, top->idef);
         snew(violaver, npairs);
         out_disre = xvgropen(opt2fn("-o", NFILE, fnm), "Sum of Violations", "Time (ps)", "nm", oenv);
         xvgr_legend(out_disre, 2, drleg, oenv);
@@ -660,23 +658,21 @@ int gmx_nmr(int argc, char* argv[])
             blk_disre = find_block_id_enxframe(&fr, enxDISRE, nullptr);
             if (bDisRe && bDRAll && !leg && blk_disre)
             {
-                t_iatom*   fa;
-                t_iparams* ip;
-
-                fa = top.idef.il[F_DISRES].iatoms;
-                ip = top.idef.iparams;
+                const InteractionList&   ilist = top->idef.il[F_DISRES];
+                gmx::ArrayRef<const int> fa    = ilist.iatoms;
+                const t_iparams*         ip    = top->idef.iparams.data();
                 if (blk_disre->nsub != 2 || (blk_disre->sub[0].nr != blk_disre->sub[1].nr))
                 {
                     gmx_incons("Number of disre sub-blocks not equal to 2");
                 }
 
                 ndisre = blk_disre->sub[0].nr;
-                if (ndisre != top.idef.il[F_DISRES].nr / 3)
+                if (ndisre != ilist.size() / 3)
                 {
                     gmx_fatal(FARGS,
                               "Number of disre pairs in the energy file (%d) does not match the "
                               "number in the run input file (%d)\n",
-                              ndisre, top.idef.il[F_DISRES].nr / 3);
+                              ndisre, ilist.size() / 3);
                 }
                 snew(pairleg, ndisre);
                 int molb = 0;
index 7c4557cec0c350845ff1b4660143176ab6900a19..c752308e9217b59df0f8758f17a159e64e6db284 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -97,7 +97,7 @@ int gmx_nmtraj(int argc, char* argv[])
 
     t_trxstatus* out;
     t_topology   top;
-    int          ePBC;
+    PbcType      pbcType;
     t_atoms*     atoms;
     rvec *       xtop, *xref, *xav, *xout;
     int          nvec, *eignr = nullptr;
@@ -130,7 +130,7 @@ int gmx_nmtraj(int argc, char* argv[])
     read_eigenvectors(opt2fn("-v", NFILE, fnm), &natoms, &bFit, &xref, &bDMR, &xav, &bDMA, &nvec,
                       &eignr, &eigvec, &eigval);
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, box, bDMA);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, box, bDMA);
 
     /* Find vectors and phases */
 
index b608e85b8350b8a66baa94f29575d3d771937216..aa97af5f97d8b4a63cc17e1f475a8eef32838a6b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -75,7 +76,7 @@
 /* P.J. van Maaren, November 2005     Added tetrahedral stuff               */
 /****************************************************************************/
 
-static void find_nearest_neighbours(int         ePBC,
+static void find_nearest_neighbours(PbcType     pbcType,
                                     int         natoms,
                                     matrix      box,
                                     rvec        x[],
@@ -115,7 +116,7 @@ static void find_nearest_neighbours(int         ePBC,
     snew(skmol, maxidx);
 
     /* Must init pbc every step because of pressure coupling */
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
 
     gmx_rmpbc(gpbc, natoms, box, x);
 
@@ -282,7 +283,7 @@ static void calc_tetra_order_parm(const char*             fnNDX,
 {
     FILE *       fpsg = nullptr, *fpsk = nullptr;
     t_topology   top;
-    int          ePBC;
+    PbcType      pbcType;
     t_trxstatus* status;
     int          natoms;
     real         t;
@@ -296,7 +297,7 @@ static void calc_tetra_order_parm(const char*             fnNDX,
     gmx_rmpbc_t  gpbc = nullptr;
 
 
-    read_tps_conf(fnTPS, &top, &ePBC, &xtop, nullptr, box, FALSE);
+    read_tps_conf(fnTPS, &top, &pbcType, &xtop, nullptr, box, FALSE);
 
     snew(sg_slice, nslice);
     snew(sk_slice, nslice);
@@ -323,11 +324,11 @@ static void calc_tetra_order_parm(const char*             fnNDX,
     fpsk = xvgropen(skfn, "S\\sk\\N Distance Order Parameter", "Time (ps)", "S\\sk\\N", oenv);
 
     /* loop over frames */
-    gpbc    = gmx_rmpbc_init(&top.idef, ePBC, natoms);
+    gpbc    = gmx_rmpbc_init(&top.idef, pbcType, natoms);
     nframes = 0;
     do
     {
-        find_nearest_neighbours(ePBC, natoms, box, x, isize[0], index[0], &sg, &sk, nslice,
+        find_nearest_neighbours(pbcType, natoms, box, x, isize[0], index[0], &sg, &sk, nslice,
                                 slice_dim, sg_slice, sk_slice, gpbc);
         for (i = 0; (i < nslice); i++)
         {
@@ -397,7 +398,7 @@ static void calc_order(const char*             fn,
                        gmx_bool                bSliced,
                        gmx_bool                bUnsat,
                        const t_topology*       top,
-                       int                     ePBC,
+                       PbcType                 pbcType,
                        int                     ngrps,
                        int                     axis,
                        gmx_bool                permolecule,
@@ -514,7 +515,7 @@ static void calc_order(const char*             fn,
 
     teller = 0;
 
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
     /*********** Start processing trajectory ***********/
     do
     {
@@ -524,7 +525,7 @@ static void calc_order(const char*             fn,
         }
         teller++;
 
-        set_pbc(&pbc, ePBC, box);
+        set_pbc(&pbc, pbcType, box);
         gmx_rmpbc_copy(gpbc, natoms, box, x0, x1);
 
         /* Now loop over all groups. There are ngrps groups, the order parameter can
@@ -937,7 +938,7 @@ static void write_bfactors(t_filenm*         fnm,
     }
 
     write_sto_conf(opt2fn("-ob", nfile, fnm), "Order parameters", &useatoms, frout.x, nullptr,
-                   frout.ePBC, frout.box);
+                   frout.pbcType, frout.box);
 
     sfree(frout.x);
     done_atom(&useatoms);
@@ -1007,11 +1008,11 @@ int gmx_order(int argc, char* argv[])
     char** grpname;       /* groupnames                 */
     int    ngrps,         /* nr. of groups              */
             i, axis = 0;  /* normal axis                */
-    t_topology* top;      /* topology         */
-    int         ePBC;
-    int *       index, /* indices for a              */
-            *a;        /* atom numbers in each group */
-    t_blocka* block;   /* data from index file       */
+    t_topology* top;      /* topology                   */
+    PbcType     pbcType;  /* type of periodic boundary conditions */
+    int *       index,    /* indices for a              */
+            *a;           /* atom numbers in each group */
+    t_blocka* block;      /* data from index file       */
     t_filenm  fnm[] = {
         /* files for g_order    */
         { efTRX, "-f", nullptr, ffREAD },     /* trajectory file              */
@@ -1110,7 +1111,7 @@ int gmx_order(int argc, char* argv[])
             fprintf(stderr, "Taking carbons as unsaturated!\n");
         }
 
-        top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC); /* read topology file */
+        top = read_top(ftp2fn(efTPR, NFILE, fnm), &pbcType); /* read topology file */
 
         block = init_index(ftp2fn(efNDX, NFILE, fnm), &grpname);
         index = block->index; /* get indices from t_block block */
@@ -1136,7 +1137,7 @@ int gmx_order(int argc, char* argv[])
         print_types(index, a, ngrps, grpname, top);
 
         calc_order(ftp2fn(efTRX, NFILE, fnm), index, a, &order, &slOrder, &slWidth, nslices,
-                   bSliced, bUnsat, top, ePBC, ngrps, axis, permolecule, radial, distcalc,
+                   bSliced, bUnsat, top, pbcType, ngrps, axis, permolecule, radial, distcalc,
                    opt2fn_null("-nr", NFILE, fnm), &distvals, oenv);
 
         if (radial)
index 00108b546871d0be222d43023b64917013bfd257..ee1cfe1af4e367e400a820bcde332bd361fc6ad9 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -146,7 +147,7 @@ int gmx_polystat(int argc, char* argv[])
 
     t_topology*       top;
     gmx_output_env_t* oenv;
-    int               ePBC;
+    PbcType           pbcType;
     int               isize, *index, nmol, *molind, mol, nat_min = 0, nat_max = 0;
     char*             grpname;
     t_trxstatus*      status;
@@ -176,7 +177,7 @@ int gmx_polystat(int argc, char* argv[])
     }
 
     snew(top, 1);
-    ePBC = read_tpx_top(ftp2fn(efTPR, NFILE, fnm), nullptr, box, &natoms, nullptr, nullptr, top);
+    pbcType = read_tpx_top(ftp2fn(efTPR, NFILE, fnm), nullptr, box, &natoms, nullptr, nullptr, top);
 
     fprintf(stderr, "Select a group of polymer mainchain atoms:\n");
     get_index(&top->atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
@@ -273,7 +274,7 @@ int gmx_polystat(int argc, char* argv[])
     sum_gyro_tot = 0;
     sum_pers_tot = 0;
 
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
 
     do
     {
index 459f99ca5da61cfaff960cf336adbd26c1debb86..fb417b0f8f5815b63625efa4faf85b129eb60c7e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -108,7 +109,7 @@ static void calc_potential(const char*             fn,
                            double***               slField,
                            int*                    nslices,
                            const t_topology*       top,
-                           int                     ePBC,
+                           PbcType                 pbcType,
                            int                     axis,
                            int                     nr_grps,
                            double*                 slWidth,
@@ -171,7 +172,7 @@ static void calc_potential(const char*             fn,
     }
 
 
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
 
     /*********** Start processing trajectory ***********/
     do
@@ -484,7 +485,7 @@ int gmx_potential(int argc, char* argv[])
     char**      grpname; /* groupnames                 */
     int*        ngx;     /* sizes of groups            */
     t_topology* top;     /* topology        */
-    int         ePBC;
+    PbcType     pbcType;
     int**       index; /* indices for all groups     */
     t_filenm    fnm[] = {
         /* files for g_order       */
@@ -507,7 +508,7 @@ int gmx_potential(int argc, char* argv[])
     /* Calculate axis */
     axis = toupper(axtitle[0]) - 'X';
 
-    top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC); /* read topology file */
+    top = read_top(ftp2fn(efTPR, NFILE, fnm), &pbcType); /* read topology file */
 
     snew(grpname, ngrps);
     snew(index, ngrps);
@@ -517,7 +518,7 @@ int gmx_potential(int argc, char* argv[])
 
 
     calc_potential(ftp2fn(efTRX, NFILE, fnm), index, ngx, &potential, &charge, &field, &nslices,
-                   top, ePBC, axis, ngrps, &slWidth, fudge_z, bSpherical, bCorrect, oenv);
+                   top, pbcType, axis, ngrps, &slWidth, fudge_z, bSpherical, bCorrect, oenv);
 
     plot_potential(potential, charge, field, opt2fn("-o", NFILE, fnm), opt2fn("-oc", NFILE, fnm),
                    opt2fn("-of", NFILE, fnm), nslices, ngrps, grpname, slWidth, oenv);
index 53286210ff8922f398c2f130f4760a285e55fee6..40092a218fa8188db069a20b35768dfa40d08d86 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -79,7 +80,7 @@ int gmx_principal(int argc, char* argv[])
     t_pargs pa[] = { { "-foo", FALSE, etBOOL, { &foo }, "Dummy option to avoid empty array" } };
     t_trxstatus* status;
     t_topology   top;
-    int          ePBC;
+    PbcType      pbcType;
     real         t;
     rvec*        x;
 
@@ -142,13 +143,13 @@ int gmx_principal(int argc, char* argv[])
     }
     sfree(legend);
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, nullptr, nullptr, box, TRUE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, nullptr, nullptr, box, TRUE);
 
     get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &gnx, &index, &grpname);
 
     natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
 
-    gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top.idef, pbcType, natoms);
 
     do
     {
index ccc24ff46747b5d80a8629b1281b326714f2e775..43c3419d25c2e3ffa14ec82c3c2fdc63f3f92f8b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -207,7 +208,7 @@ int gmx_rms(int argc, char* argv[])
     gmx_bool   bNorm, bAv, bFreq2, bFile2, bMat, bBond, bDelta, bMirror, bMass;
     gmx_bool   bFit, bReset;
     t_topology top;
-    int        ePBC;
+    PbcType    pbcType;
     t_iatom*   iatom = nullptr;
 
     matrix box = { { 0 } };
@@ -326,7 +327,7 @@ int gmx_rms(int argc, char* argv[])
         }
     }
 
-    bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xp, nullptr, box, TRUE);
+    bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xp, nullptr, box, TRUE);
     snew(w_rls, top.atoms.nr);
     snew(w_rms, top.atoms.nr);
 
@@ -435,7 +436,7 @@ int gmx_rms(int argc, char* argv[])
     /* Prepare reference frame */
     if (bPBC)
     {
-        gpbc = gmx_rmpbc_init(&top.idef, ePBC, top.atoms.nr);
+        gpbc = gmx_rmpbc_init(&top.idef, pbcType, top.atoms.nr);
         gmx_rmpbc(gpbc, top.atoms.nr, box, xp);
     }
     if (bReset)
index 48eb4e8ffdd236658bcdf0b34af7d000b7609bec..af6235491905e7c1e5ea5f31562bc0c976375e1a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/strdb.h"
 
 
-static void calc_dist(int nind, const int index[], const rvec x[], int ePBC, matrix box, real** d)
+static void calc_dist(int nind, const int index[], const rvec x[], PbcType pbcType, matrix box, real** d)
 {
     int   i, j;
     rvec  dx;
     real  temp2;
     t_pbc pbc;
 
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
     for (i = 0; (i < nind - 1); i++)
     {
         const real* xi = x[index[i]];
@@ -85,7 +86,7 @@ static void calc_dist(int nind, const int index[], const rvec x[], int ePBC, mat
 static void calc_dist_tot(int       nind,
                           const int index[],
                           rvec      x[],
-                          int       ePBC,
+                          PbcType   pbcType,
                           matrix    box,
                           real**    d,
                           real**    dtot,
@@ -100,7 +101,7 @@ static void calc_dist_tot(int       nind,
     rvec  dx;
     t_pbc pbc;
 
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
     for (i = 0; (i < nind - 1); i++)
     {
         xi = x[index[i]];
@@ -668,7 +669,7 @@ int gmx_rmsdist(int argc, char* argv[])
     real t;
 
     t_topology top;
-    int        ePBC;
+    PbcType    pbcType;
     t_atoms*   atoms;
     matrix     box;
     rvec*      x;
@@ -741,11 +742,11 @@ int gmx_rmsdist(int argc, char* argv[])
     }
 
     /* get topology and index */
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &x, nullptr, box, FALSE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &x, nullptr, box, FALSE);
 
     if (!bPBC)
     {
-        ePBC = epbcNONE;
+        pbcType = PbcType::No;
     }
     atoms = &(top.atoms);
 
@@ -783,7 +784,7 @@ int gmx_rmsdist(int argc, char* argv[])
     }
 
     /*set box type*/
-    calc_dist(isize, index, x, ePBC, box, d_r);
+    calc_dist(isize, index, x, pbcType, box, d_r);
     sfree(x);
 
     /*open output files*/
@@ -799,7 +800,7 @@ int gmx_rmsdist(int argc, char* argv[])
 
     do
     {
-        calc_dist_tot(isize, index, x, ePBC, box, d, dtot, dtot2, bNMR, dtot1_3, dtot1_6);
+        calc_dist_tot(isize, index, x, pbcType, box, d, dtot, dtot2, bNMR, dtot1_3, dtot1_6);
 
         rmsnow = rms_diff(isize, d, d_r);
         fprintf(fp, "%g  %g\n", t, rmsnow);
index e2fe3c133abd2471172a96cf8077b670f64e6acb..c0c439afd6af2c7174b364df59c64825402f4914 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -239,7 +240,7 @@ int gmx_rmsf(int argc, char* argv[])
     real t, *w_rls;
 
     t_topology top;
-    int        ePBC;
+    PbcType    pbcType;
     t_atoms *  pdbatoms, *refatoms;
 
     matrix       box, pdbbox;
@@ -287,7 +288,7 @@ int gmx_rmsf(int argc, char* argv[])
     devfn    = opt2fn_null("-od", NFILE, fnm);
     dirfn    = opt2fn_null("-dir", NFILE, fnm);
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xref, nullptr, box, TRUE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xref, nullptr, box, TRUE);
     const char* title = *top.name;
     snew(w_rls, top.atoms.nr);
 
@@ -348,7 +349,7 @@ int gmx_rmsf(int argc, char* argv[])
 
     if (bFit)
     {
-        gpbc = gmx_rmpbc_init(&top.idef, ePBC, natom);
+        gpbc = gmx_rmpbc_init(&top.idef, pbcType, natom);
     }
 
     /* Now read the trj again to compute fluctuations */
@@ -554,7 +555,7 @@ int gmx_rmsf(int argc, char* argv[])
         {
             rvec_inc(pdbx[index[i]], xcm);
         }
-        write_sto_conf_indexed(opt2fn("-oq", NFILE, fnm), title, pdbatoms, pdbx, nullptr, ePBC,
+        write_sto_conf_indexed(opt2fn("-oq", NFILE, fnm), title, pdbatoms, pdbx, nullptr, pbcType,
                                pdbbox, isize, index);
     }
     if (opt2bSet("-ox", NFILE, fnm))
@@ -569,8 +570,8 @@ int gmx_rmsf(int argc, char* argv[])
             }
         }
         /* Write a .pdb file with B-factors and optionally anisou records */
-        write_sto_conf_indexed(opt2fn("-ox", NFILE, fnm), title, pdbatoms, bFactorX, nullptr, ePBC,
-                               pdbbox, isize, index);
+        write_sto_conf_indexed(opt2fn("-ox", NFILE, fnm), title, pdbatoms, bFactorX, nullptr,
+                               pbcType, pdbbox, isize, index);
         sfree(bFactorX);
     }
     if (bAniso)
index 63d21d22c70c4ee29f099068143d8e36ccb3cdd6..048c663158d1d9159c38c548c70d82e78d5d2d50 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -98,7 +99,7 @@ int gmx_rotacf(int argc, char* argv[])
     real          t, t0, t1, dt;
     gmx_rmpbc_t   gpbc = nullptr;
     t_topology*   top;
-    int           ePBC;
+    PbcType       pbcType;
     t_filenm      fnm[] = { { efTRX, "-f", nullptr, ffREAD },
                        { efTPR, nullptr, nullptr, ffREAD },
                        { efNDX, nullptr, nullptr, ffREAD },
@@ -143,7 +144,7 @@ int gmx_rotacf(int argc, char* argv[])
                   "these can not be atom doublets\n");
     }
 
-    top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC);
+    top = read_top(ftp2fn(efTPR, NFILE, fnm), &pbcType);
 
     snew(c1, nvec);
     for (i = 0; (i < nvec); i++)
@@ -155,7 +156,7 @@ int gmx_rotacf(int argc, char* argv[])
     natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
     snew(x_s, natoms);
 
-    gpbc = gmx_rmpbc_init(&(top->idef), ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&(top->idef), pbcType, natoms);
 
     /* Start the loop over frames */
     t0     = t;
index 9aa0fd82cae26e4f836794288d922253429ef3f1..6bc93086afa5c94e42dda1c545480e5843f78482 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2009-2017, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -64,7 +64,7 @@ static void get_refx(gmx_output_env_t* oenv,
                      int*              index,
                      gmx_bool          bMW,
                      const t_topology* top,
-                     int               ePBC,
+                     PbcType           pbcType,
                      rvec*             x_ref)
 {
     int          natoms, nfr_all, nfr, i, j, a, r, c, min_fr;
@@ -97,7 +97,7 @@ static void get_refx(gmx_output_env_t* oenv,
         w_rls[a] = (bMW ? top->atoms.atom[index[a]].m : 1.0);
         tot_mass += w_rls[a];
     }
-    gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
 
     do
     {
@@ -227,7 +227,7 @@ int gmx_rotmat(int argc, char* argv[])
     FILE*             out;
     t_trxstatus*      status;
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     rvec *            x_ref, *x;
     matrix            box, R;
     real              t;
@@ -252,9 +252,9 @@ int gmx_rotmat(int argc, char* argv[])
         return 0;
     }
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &x_ref, nullptr, box, bMW);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &x_ref, nullptr, box, bMW);
 
-    gpbc = gmx_rmpbc_init(&top.idef, ePBC, top.atoms.nr);
+    gpbc = gmx_rmpbc_init(&top.idef, pbcType, top.atoms.nr);
 
     gmx_rmpbc(gpbc, top.atoms.nr, box, x_ref);
 
@@ -264,7 +264,7 @@ int gmx_rotmat(int argc, char* argv[])
     if (reffit[0][0] != 'n')
     {
         get_refx(oenv, ftp2fn(efTRX, NFILE, fnm), reffit[0][2] == 'z' ? 3 : 2, skip, gnx, index,
-                 bMW, &top, ePBC, x_ref);
+                 bMW, &top, pbcType, x_ref);
     }
 
     natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
index 225004499a14acce96818e9a8bd3c91677f51892..46faac366874ede6ebfe40754e4f519d5ef9ea7c 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -135,7 +136,7 @@ int gmx_saltbr(int argc, char* argv[])
     int                nset[3]  = { 0, 0, 0 };
 
     t_topology*  top;
-    int          ePBC;
+    PbcType      pbcType;
     char*        buf;
     t_trxstatus* status;
     int          i, j, k, m, nnn, teller, ncg;
@@ -155,7 +156,7 @@ int gmx_saltbr(int argc, char* argv[])
         return 0;
     }
 
-    top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC);
+    top = read_top(ftp2fn(efTPR, NFILE, fnm), &pbcType);
     cg  = mk_charge(&top->atoms, &ncg);
     snew(cgdist, ncg);
     snew(nWithin, ncg);
@@ -174,7 +175,7 @@ int gmx_saltbr(int argc, char* argv[])
         srenew(time, teller + 1);
         time[teller] = t;
 
-        set_pbc(&pbc, ePBC, box);
+        set_pbc(&pbc, pbcType, box);
 
         for (i = 0; (i < ncg); i++)
         {
@@ -254,11 +255,11 @@ int gmx_saltbr(int argc, char* argv[])
                     }
                     else
                     {
-                        if (output_env_get_xvg_format(oenv) == exvgXMGR)
+                        if (output_env_get_xvg_format(oenv) == XvgFormat::Xmgr)
                         {
                             fprintf(out[nnn], "@ legend string %d \"%s\"\n", nset[nnn], buf);
                         }
-                        else if (output_env_get_xvg_format(oenv) == exvgXMGRACE)
+                        else if (output_env_get_xvg_format(oenv) == XvgFormat::Xmgrace)
                         {
                             fprintf(out[nnn], "@ s%d legend \"%s\"\n", nset[nnn], buf);
                         }
index 797ab499ef2b9270b827037ecc7ec1de04038b1d..ab77d08a1cf938135939bbaa950ae93a6eb278b0 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +48,7 @@
 #include "gromacs/gmxana/gstat.h"
 #include "gromacs/gmxana/nsfactor.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/rmpbc.h"
 #include "gromacs/topology/index.h"
 #include "gromacs/topology/topology.h"
@@ -125,8 +127,8 @@ int gmx_sans(int argc, char* argv[])
     t_topology*                          top  = nullptr;
     gmx_rmpbc_t                          gpbc = nullptr;
     gmx_bool                             bFFT = FALSE, bDEBYE = FALSE;
-    gmx_bool                             bMC  = FALSE;
-    int                                  ePBC = -1;
+    gmx_bool                             bMC     = FALSE;
+    PbcType                              pbcType = PbcType::Unset;
     matrix                               box;
     rvec*                                x;
     int                                  natoms;
@@ -218,7 +220,7 @@ int gmx_sans(int argc, char* argv[])
     snew(grpname, 1);
     snew(index, 1);
 
-    read_tps_conf(fnTPX, top, &ePBC, &x, nullptr, box, TRUE);
+    read_tps_conf(fnTPX, top, &pbcType, &x, nullptr, box, TRUE);
 
     printf("\nPlease select group for SANS spectra calculation:\n");
     get_index(&(top->atoms), ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, grpname);
@@ -228,7 +230,7 @@ int gmx_sans(int argc, char* argv[])
     /* Prepare reference frame */
     if (bPBC)
     {
-        gpbc = gmx_rmpbc_init(&top->idef, ePBC, top->atoms.nr);
+        gpbc = gmx_rmpbc_init(&top->idef, pbcType, top->atoms.nr);
         gmx_rmpbc(gpbc, top->atoms.nr, box, x);
     }
 
index afe2bc0f929afecf864985ff58b4a9c8b00a4856..6827fa1c8d448c5352ac29db06b4d44a3f83a0eb 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f367d121e6a0f462315f539f9ed071a4834520f4..cc7d8f9e88bb07e9fc63a1915c86d29d85d3aa80 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 12d7a461a7f4067ebdcdfc58de1a36996d43dd6c..498416abcfd540f71ad938856163f37b6de8e5ad 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 4d661534cd3621eb84d88eedf61ea12c6651265a..4561a28a807b66c01ed2c4d7621823bcb8e310a7 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -114,7 +115,7 @@ static void calc_com_pbc(int nrefat, t_topology* top, rvec x[], t_pbc* pbc, cons
 int gmx_sorient(int argc, char* argv[])
 {
     t_topology   top;
-    int          ePBC = -1;
+    PbcType      pbcType = PbcType::Unset;
     t_trxstatus* status;
     int          natoms;
     real         t;
@@ -203,7 +204,7 @@ int gmx_sorient(int argc, char* argv[])
     bTPS = (opt2bSet("-s", NFILE, fnm) || !opt2bSet("-n", NFILE, fnm) || bCom);
     if (bTPS)
     {
-        read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, box, bCom);
+        read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, box, bCom);
     }
 
     /* get index groups */
@@ -241,7 +242,7 @@ int gmx_sorient(int argc, char* argv[])
 
     rmin2 = gmx::square(rmin);
     rmax2 = gmx::square(rmax);
-    rcut  = 0.99 * std::sqrt(max_cutoff2(guess_ePBC(box), box));
+    rcut  = 0.99 * std::sqrt(max_cutoff2(guessPbcType(box), box));
     if (rcut == 0)
     {
         rcut = 10 * rmax;
@@ -273,7 +274,7 @@ int gmx_sorient(int argc, char* argv[])
     if (bTPS)
     {
         /* make molecules whole again */
-        gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms);
+        gpbc = gmx_rmpbc_init(&top.idef, pbcType, natoms);
     }
     /* start analysis of trajectory */
     do
@@ -284,7 +285,7 @@ int gmx_sorient(int argc, char* argv[])
             gmx_rmpbc(gpbc, natoms, box, x);
         }
 
-        set_pbc(&pbc, ePBC, box);
+        set_pbc(&pbc, pbcType, box);
         n   = 0;
         inp = 0;
         for (p = 0; (p < nrefgrp); p++)
index 000517b6da8f5b917247329593a8ee3ba0018595..0ec43ffafda9f4a68fdc4bbe948b17d00c0755d8 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2007,2008,2009,2010,2011,2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2007,2008,2009,2010,2011 by the GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -156,7 +158,7 @@ int gmx_spatial(int argc, char* argv[])
     double            MINBIN[3];
     double            MAXBIN[3];
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     t_trxframe        fr;
     rvec*             xtop;
     matrix            box, box_pbc;
@@ -194,7 +196,7 @@ int gmx_spatial(int argc, char* argv[])
         return 0;
     }
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, box, TRUE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, box, TRUE);
     sfree(xtop);
 
     atoms = &(top.atoms);
@@ -260,7 +262,7 @@ int gmx_spatial(int argc, char* argv[])
 
     if (bPBC)
     {
-        gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms);
+        gpbc = gmx_rmpbc_init(&top.idef, pbcType, natoms);
     }
     /* This is the main loop over frames */
     do
@@ -271,7 +273,7 @@ int gmx_spatial(int argc, char* argv[])
         if (bPBC)
         {
             gmx_rmpbc_trxfr(gpbc, &fr);
-            set_pbc(&pbc, ePBC, box_pbc);
+            set_pbc(&pbc, pbcType, box_pbc);
         }
 
         for (i = 0; i < nidx; i++)
index 74cc5b6654cc4d0183886afad83c942bed444323..d67045be907debdfe1c247d4f380ea00665b474d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -58,7 +59,7 @@
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
 
-static void calc_com_pbc(int nrefat, const t_topology* top, rvec x[], t_pbc* pbc, const int index[], rvec xref, int ePBC)
+static void calc_com_pbc(int nrefat, const t_topology* top, rvec x[], t_pbc* pbc, const int index[], rvec xref, PbcType pbcType)
 {
     const real tol = 1e-4;
     gmx_bool   bChanged;
@@ -81,7 +82,7 @@ static void calc_com_pbc(int nrefat, const t_topology* top, rvec x[], t_pbc* pbc
     }
     svmul(1 / mtot, xref, xref);
     /* Now check if any atom is more than half the box from the COM */
-    if (ePBC != epbcNONE)
+    if (pbcType != PbcType::No)
     {
         iter = 0;
         do
@@ -216,7 +217,7 @@ int gmx_spol(int argc, char* argv[])
     }
 
     snew(top, 1);
-    // TODO: Only ePBC is used, not the full inputrec.
+    // TODO: Only pbcType is used, not the full inputrec.
     t_inputrec  irInstance;
     t_inputrec* ir = &irInstance;
     read_tpx_top(ftp2fn(efTPR, NFILE, fnm), ir, box, &natoms, nullptr, nullptr, top);
@@ -245,7 +246,7 @@ int gmx_spol(int argc, char* argv[])
     /* initialize reading trajectory:                         */
     natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
 
-    rcut = 0.99 * std::sqrt(max_cutoff2(ir->ePBC, box));
+    rcut = 0.99 * std::sqrt(max_cutoff2(ir->pbcType, box));
     if (rcut == 0)
     {
         rcut = 10 * rmax;
@@ -268,7 +269,7 @@ int gmx_spol(int argc, char* argv[])
     molindex = top->mols.index;
     atom     = top->atoms.atom;
 
-    gpbc = gmx_rmpbc_init(&top->idef, ir->ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top->idef, ir->pbcType, natoms);
 
     /* start analysis of trajectory */
     do
@@ -276,10 +277,10 @@ int gmx_spol(int argc, char* argv[])
         /* make molecules whole again */
         gmx_rmpbc(gpbc, natoms, box, x);
 
-        set_pbc(&pbc, ir->ePBC, box);
+        set_pbc(&pbc, ir->pbcType, box);
         if (bCom)
         {
-            calc_com_pbc(nrefat, top, x, &pbc, index[0], xref, ir->ePBC);
+            calc_com_pbc(nrefat, top, x, &pbc, index[0], xref, ir->pbcType);
         }
 
         for (m = 0; m < isize[1]; m++)
index b4c8e1952f58189913f2fee63aa5f7971c01aa65..03e9f2c6ad4d7f0022bcbbf6bf941b21563e0ad9 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -319,7 +320,7 @@ int gmx_tcaf(int argc, char* argv[])
     };
 
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     t_trxframe        fr;
     matrix            box;
     gmx_bool          bTop;
@@ -356,7 +357,7 @@ int gmx_tcaf(int argc, char* argv[])
         return 0;
     }
 
-    bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, nullptr, nullptr, box, TRUE);
+    bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, nullptr, nullptr, box, TRUE);
     get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &gnx, &index, &grpname);
 
     if (bMol)
index 059081a9d8769219866ea5995177a35f80fb1894..93c811bb5a223ab7d2032910864ba7564630cfb5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -426,7 +427,7 @@ static void write_pdb_bfac(const char*             fname,
                            const char*             xname,
                            const char*             title,
                            t_atoms*                atoms,
-                           int                     ePBC,
+                           PbcType                 pbcType,
                            matrix                  box,
                            int                     isize,
                            int*                    index,
@@ -547,7 +548,7 @@ static void write_pdb_bfac(const char*             fname,
                 atoms->pdbinfo[index[i]].bfac = sum[index[i]][onedim] * scale;
             }
         }
-        write_sto_conf_indexed(fname, title, atoms, x, nullptr, ePBC, box, isize, index);
+        write_sto_conf_indexed(fname, title, atoms, x, nullptr, pbcType, box, isize, index);
     }
 }
 
@@ -670,7 +671,7 @@ int gmx_traj(int argc, char* argv[])
     FILE *       outx = nullptr, *outv = nullptr, *outf = nullptr, *outb = nullptr, *outt = nullptr;
     FILE *       outekt = nullptr, *outekr = nullptr;
     t_topology   top;
-    int          ePBC;
+    PbcType      pbcType;
     real *       mass, time;
     const char*  indexfn;
     t_trxframe   fr;
@@ -750,7 +751,7 @@ int gmx_traj(int argc, char* argv[])
     }
     std::string sffmt6 = gmx::formatString("%s%s%s%s%s%s", sffmt, sffmt, sffmt, sffmt, sffmt, sffmt);
 
-    bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, topbox,
+    bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, topbox,
                          bCom && (bOX || bOXT || bOV || bOT || bEKT || bEKR));
     sfree(xtop);
     if ((bMol || bCV || bCF) && !bTop)
@@ -928,7 +929,7 @@ int gmx_traj(int argc, char* argv[])
 
     if (bCom && bPBC)
     {
-        gpbc = gmx_rmpbc_init(&top.idef, ePBC, fr.natoms);
+        gpbc = gmx_rmpbc_init(&top.idef, pbcType, fr.natoms);
     }
 
     do
@@ -1097,7 +1098,7 @@ int gmx_traj(int argc, char* argv[])
     {
         if (nr_xfr > 1)
         {
-            if (ePBC != epbcNONE && !bNoJump)
+            if (pbcType != PbcType::No && !bNoJump)
             {
                 fprintf(stderr,
                         "\nWARNING: More than one frame was used for option -cv or -cf\n"
@@ -1117,14 +1118,14 @@ int gmx_traj(int argc, char* argv[])
     if (bCV)
     {
         write_pdb_bfac(opt2fn("-cv", NFILE, fnm), opt2fn("-av", NFILE, fnm), "average velocity",
-                       &(top.atoms), ePBC, topbox, isize[0], index[0], nr_xfr, sumx, nr_vfr, sumv,
-                       bDim, scale, oenv);
+                       &(top.atoms), pbcType, topbox, isize[0], index[0], nr_xfr, sumx, nr_vfr,
+                       sumv, bDim, scale, oenv);
     }
     if (bCF)
     {
         write_pdb_bfac(opt2fn("-cf", NFILE, fnm), opt2fn("-af", NFILE, fnm), "average force",
-                       &(top.atoms), ePBC, topbox, isize[0], index[0], nr_xfr, sumx, nr_ffr, sumf,
-                       bDim, scale, oenv);
+                       &(top.atoms), pbcType, topbox, isize[0], index[0], nr_xfr, sumx, nr_ffr,
+                       sumf, bDim, scale, oenv);
     }
 
     /* view it */
index 75cf93dc3987a14e81a108c9b06c0edac5d8beed..96966c9a00ab73368617ba7a440d798eda65cfbf 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -132,7 +133,7 @@ int gmx_trjorder(int argc, char* argv[])
     t_trxstatus*      status;
     gmx_bool          bNShell, bPDBout;
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     rvec *            x, *xsol, xcom, dx;
     matrix            box;
     t_pbc             pbc;
@@ -156,7 +157,7 @@ int gmx_trjorder(int argc, char* argv[])
         return 0;
     }
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &x, nullptr, box, TRUE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &x, nullptr, box, TRUE);
     sfree(x);
 
     /* get index groups */
@@ -239,11 +240,11 @@ int gmx_trjorder(int argc, char* argv[])
         }
         out = open_trx(opt2fn("-o", NFILE, fnm), "w");
     }
-    gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms);
+    gpbc = gmx_rmpbc_init(&top.idef, pbcType, natoms);
     do
     {
         gmx_rmpbc(gpbc, natoms, box, x);
-        set_pbc(&pbc, ePBC, box);
+        set_pbc(&pbc, pbcType, box);
 
         if (ref_a == -1)
         {
index 7dae7ea80b1f45d5041725ebbe5c417cf9b462b9..44edc6539d9f985ecc044f0a04e5909e107305c5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -133,7 +134,7 @@ int gmx_vanhove(int argc, char* argv[])
     gmx_output_env_t* oenv;
     const char *      matfile, *otfile, *orfile;
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     matrix            boxtop, box, *sbox, avbox, corr;
     rvec *            xtop, *x, **sx;
     int               isize, nalloc, nallocn;
@@ -179,7 +180,7 @@ int gmx_vanhove(int argc, char* argv[])
         exit(0);
     }
 
-    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, boxtop, FALSE);
+    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, boxtop, FALSE);
     get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
 
     nalloc = 0;
@@ -294,7 +295,7 @@ int gmx_vanhove(int argc, char* argv[])
             fprintf(stderr, "\rProcessing frame %d", f);
             fflush(stderr);
         }
-        if (ePBC != epbcNONE)
+        if (pbcType != PbcType::No)
         {
             /* Scale all the configuration to the average box */
             gmx::invertBoxMatrix(sbox[f], corr);
index d75f95b501fcd52aaaa284e06c84323716c84d4c..48a0ee567f4001618c9445a0bd8394db0f73fcf5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,6 +54,7 @@
 #include "gromacs/math/units.h"
 #include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/index.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/trajectory/trajectoryframe.h"
@@ -195,7 +197,7 @@ int gmx_velacc(int argc, char* argv[])
     };
 
     t_topology top;
-    int        ePBC = -1;
+    PbcType    pbcType = PbcType::Unset;
     t_trxframe fr;
     matrix     box;
     gmx_bool   bTPS = FALSE, bTop = FALSE;
@@ -241,7 +243,7 @@ int gmx_velacc(int argc, char* argv[])
 
     if (bTPS)
     {
-        bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, nullptr, nullptr, box, TRUE);
+        bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, nullptr, nullptr, box, TRUE);
         get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &gnx, &index, &grpname);
     }
     else
index c865262e1d8369006cd877737d957160aa6244e0..9162bbcd4e261732652a3293f19cd3f69ff4be6f 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -223,7 +224,7 @@ typedef struct
 } t_coordselection;
 
 //! Parameters of WHAM
-typedef struct // NOLINT(clang-analyzer-optin.performance.Padding)
+typedef struct UmbrellaOptions // NOLINT(clang-analyzer-optin.performance.Padding)
 {
     /*!
      * \name Input stuff
index 924eda09b184b05a8a8bb7f822ea7e948d9e5cba..a2a0c38f1ffc76943efc2a827158b1af73e5b6b6 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index af4c8a06dfdd3a28ff8d144f636a2509182242a2..e0e24fea33722508c4cb7b1cb05da1f7d95b2085 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 405228ef80aa87c3775a301ff983ea29f0d0463b..f09bcfc87805e5f943536deb6f3075e422e589cd 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 102141b0061fd5fdf33c6c9c87caac9e368805b8..59206dd2ea61e1f5169738804c72cbcd72d4a1a1 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 6799aa9552309cc5c0db4ce5cf6f61913c44e129..4ec0380083cba0ed72d9a3728aedb8ea5aa5519e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index bcd8b5e5f5dda0241a4bdcf359a7613b28a1d89b..cf7de12ebdb5a79d3928fa3c0d0b410fabc60942 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 14b01053e1af92d517cf58cc5fbb93c0be04e4dc..f1bf5bb89e71b700572f09f3e23a1278da75e964 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -77,7 +78,7 @@ static void calc_dihs(t_xrama* xr)
     t_dih*      dd;
     gmx_rmpbc_t gpbc = nullptr;
 
-    gpbc = gmx_rmpbc_init(xr->idef, xr->ePBC, xr->natoms);
+    gpbc = gmx_rmpbc_init(xr->idef, xr->pbcType, xr->natoms);
     gmx_rmpbc(gpbc, xr->natoms, xr->box, xr->x);
     gmx_rmpbc_done(gpbc);
 
@@ -242,7 +243,7 @@ t_topology* init_rama(gmx_output_env_t* oenv, const char* infile, const char* to
     t_topology* top;
     real        t;
 
-    top = read_top(topfile, &xr->ePBC);
+    top = read_top(topfile, &xr->pbcType);
 
     /*get_dih2(xr,top->idef.functype,&(top->idef.bondeds),&(top->atoms));*/
     get_dih(xr, &(top->atoms));
index 6aee0a3e94c95ba101fa9d62d0044e5b025c471c..71c00fded00fa8081d4c8131b10c96d108c0d089 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -72,7 +72,7 @@ typedef struct
     rvec*             x;
     matrix            box;
     t_idef*           idef;
-    int               ePBC;
+    PbcType           pbcType;
     gmx_output_env_t* oenv;
 } t_xrama;
 
index 3c53901f132c91a27c4a9246220eeaf73c3abc11..0f8171fecc99275171f154943dd7ea446ec90a02 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,6 +46,7 @@
 #include "gromacs/random/threefry.h"
 #include "gromacs/random/uniformintdistribution.h"
 #include "gromacs/topology/topology.h"
+#include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
@@ -208,7 +210,7 @@ gmx_radial_distribution_histogram_t* calc_radial_distribution_histogram(gmx_sans
     int                       nthreads;
     gmx::DefaultRandomEngine* trng = nullptr;
 #endif
-    int64_t                  mc = 0, mc_max;
+    int64_t                  mc_max;
     gmx::DefaultRandomEngine rng(seed);
 
     /* allocate memory for pr */
@@ -249,13 +251,14 @@ gmx_radial_distribution_histogram_t* calc_radial_distribution_histogram(gmx_sans
             snew(tgr[i], pr->grn);
             trng[i].seed(rng());
         }
-#    pragma omp parallel shared(tgr, trng, mc) private(tid, i, j)
+#    pragma omp parallel shared(tgr, trng) private(tid, i, j)
         {
             gmx::UniformIntDistribution<int> tdist(0, isize - 1);
             tid = gmx_omp_get_thread_num();
-/* now starting parallel threads */
+            /* now starting parallel threads */
+            INTEL_DIAGNOSTIC_IGNORE(593)
 #    pragma omp for
-            for (mc = 0; mc < mc_max; mc++)
+            for (int64_t mc = 0; mc < mc_max; mc++)
             {
                 try
                 {
@@ -270,6 +273,7 @@ gmx_radial_distribution_histogram_t* calc_radial_distribution_histogram(gmx_sans
                 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
             }
         }
+        INTEL_DIAGNOSTIC_RESET
         /* collecting data from threads */
         for (i = 0; i < pr->grn; i++)
         {
@@ -287,7 +291,7 @@ gmx_radial_distribution_histogram_t* calc_radial_distribution_histogram(gmx_sans
         delete[] trng;
 #else
         gmx::UniformIntDistribution<int> dist(0, isize - 1);
-        for (mc = 0; mc < mc_max; mc++)
+        for (int64_t mc = 0; mc < mc_max; mc++)
         {
             i = dist(rng); // [0,isize-1]
             j = dist(rng); // [0,isize-1]
index df87a9899819f696a37e87262b98c908e23e02bc..f22010aafe215653f727f8b20b6156728508ec98 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7a7da61e95a6d00c3f53e960d1483ef881ab4b8e..118491ccf12a3c0c876179a98c24c804389b6cc0 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -39,6 +40,7 @@
 
 #include "gromacs/fft/fft.h"
 #include "gromacs/gmxana/interf.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/futil.h"
 #include "gromacs/utility/real.h"
index 79f49620796cc4282729a20f0164ac51ed5c2c45..e37bb57c1738b5de697145ea52446722da65fcf5 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <vector>
 
 #include "gromacs/gmxana/interf.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/real.h"
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 extern void powerspectavg(real*** interface, int t, int xbins, int ybins, gmx::ArrayRef<const std::string> outfiles);
 
 extern void powerspectavg_intf(t_interf***                      if1,
index d27fe4ea3fe29672420decc694b5883fb5464ece..cbb4a0d58a2ba9dcb260bfa992a8432afda264bc 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,6 +46,7 @@
 #include "gromacs/math/units.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/futil.h"
+#include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/pleasecite.h"
 #include "gromacs/utility/smalloc.h"
 
index 330a518fcac70501dd10988e4e9bae532d87d894..8f32afa7b342e5ecc3367c253a3b1417caeae03f 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 021b341c77a2e76389694fff4d2bf45545cc607a..bf24db3ba4d2efbe6067325380ecfe9f7a32c04b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 0f9035a670ad71978698aad6fa7cd18cefac4ca5..87b08d6e84fdac5856b32ba5e8ef67770593968a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -459,7 +460,7 @@ extern int do_scattering_intensity(const char*             fnTPS,
     char**            grpname;
     int**             index;
     t_topology        top;
-    int               ePBC;
+    PbcType           pbcType;
     t_trxframe        fr;
     reduced_atom_t**  red;
     structure_factor* sf;
@@ -483,7 +484,7 @@ extern int do_scattering_intensity(const char*             fnTPS,
     sf->energy = energy;
 
     /* Read the topology informations */
-    read_tps_conf(fnTPS, &top, &ePBC, &xtop, nullptr, box, TRUE);
+    read_tps_conf(fnTPS, &top, &pbcType, &xtop, nullptr, box, TRUE);
     sfree(xtop);
 
     /* groups stuff... */
index 1010accf21a027b702e92998fe7b1a16897fb344..97b1ce1953641161855cbec3ea8703d7c782dec0 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 47dfa35da94160fd7a6148d4688d46ea5a295e4e..381d847f0636230dec31593c1ed53a389c9a7da1 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 set(exename gmxana-test)
-
-gmx_add_gtest_executable(
-    ${exename}
-    entropy.cpp
-    gmx_traj.cpp
-    gmx_mindist.cpp
-    gmx_msd.cpp
-    )
-gmx_register_gtest_test(GmxAnaTest ${exename} INTEGRATION_TEST)
+gmx_add_gtest_executable(${exename}
+    CPP_SOURCE_FILES
+        entropy.cpp
+        gmx_traj.cpp
+        gmx_mindist.cpp
+        gmx_msd.cpp
+        )
+gmx_register_gtest_test(GmxAnaTest ${exename} INTEGRATION_TEST IGNORE_LEAKS)
index ff2ec4de0c3000e19326c06fca59919800a6bd15..2890f084af02254aea7346470b5e5b0fbe1e02c8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -41,6 +41,7 @@
 #include "gmxpre.h"
 
 #include "gromacs/gmxana/thermochemistry.h"
+#include "gromacs/utility/arrayref.h"
 
 #include "testutils/refdata.h"
 #include "testutils/testasserts.h"
index eb43b61b8511fdd4fa478c262255fab390170d82..0af48b1021c25dbd1fa4e1d4b0b201a5c137d94a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b704d0e8d32dd5ddf67cafe5849a7df312532c75..efc50dd8531791a68117b7a8ede5483f28f1dd40 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/math/units.h"
 #include "gromacs/math/vec.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /*! \brief Compute zero point energy from an array of eigenvalues.
  *
  * This routine first converts the eigenvalues from a normal mode
index 50d052de34f4d1ed5416fabea17eec33f48f92fe..2e20112e902b41ba22604617ff0fbbc286cb981e 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2009,2010,2012,2014,2015,2016, by the GROMACS development team, led by
+# Copyright (c) 2009,2010,2012,2014,2015 by the GROMACS development team.
+# Copyright (c) 2016,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index b2de3a17790d922ea334d9ae3209e7e5486f20df..66023edac82d5019ea91da73ac32f9902e91babd 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,7 +47,6 @@
 #include <cstring>
 
 #include "gromacs/commandline/filenm.h"
-#include "gromacs/mdrunutility/multisim.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/utility/basenetwork.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/futil.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/gmxmpi.h"
-#include "gromacs/utility/mpiinplacebuffers.h"
 #include "gromacs/utility/real.h"
 #include "gromacs/utility/smalloc.h"
 
 /* The source code in this file should be thread-safe.
       Please keep it that way. */
 
-CommrecHandle init_commrec(MPI_Comm communicator, const gmx_multisim_t* ms)
+CommrecHandle init_commrec(MPI_Comm communicator)
 {
     CommrecHandle handle;
     t_commrec*    cr;
@@ -82,41 +81,21 @@ CommrecHandle init_commrec(MPI_Comm communicator, const gmx_multisim_t* ms)
     sizeOfCommunicator = 1;
 #endif
 
-    if (ms != nullptr)
-    {
-#if GMX_MPI
-        cr->nnodes = sizeOfCommunicator / ms->nsim;
-        MPI_Comm_split(communicator, ms->sim, rankInCommunicator, &cr->mpi_comm_mysim);
-        cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
-        MPI_Comm_rank(cr->mpi_comm_mysim, &cr->sim_nodeid);
-        MPI_Comm_rank(cr->mpi_comm_mygroup, &cr->nodeid);
-#endif
-    }
-    else
-    {
-        cr->nnodes           = sizeOfCommunicator;
-        cr->nodeid           = rankInCommunicator;
-        cr->sim_nodeid       = cr->nodeid;
-        cr->mpi_comm_mysim   = communicator;
-        cr->mpi_comm_mygroup = communicator;
-    }
+    cr->mpiDefaultCommunicator    = communicator;
+    cr->sizeOfDefaultCommunicator = sizeOfCommunicator;
+    cr->rankInDefaultCommunicator = rankInCommunicator;
+
+    // For now, we want things to go horribly wrong if this is used too early...
+    // TODO: Remove when communicators are removed from commrec (#2395)
+    cr->nnodes           = -1;
+    cr->nodeid           = -1;
+    cr->sim_nodeid       = -1;
+    cr->mpi_comm_mysim   = MPI_COMM_NULL;
+    cr->mpi_comm_mygroup = MPI_COMM_NULL;
 
     // TODO cr->duty should not be initialized here
     cr->duty = (DUTY_PP | DUTY_PME);
 
-#if GMX_MPI && !MPI_IN_PLACE_EXISTS
-    /* initialize the MPI_IN_PLACE replacement buffers */
-    snew(cr->mpb, 1);
-    cr->mpb->ibuf        = nullptr;
-    cr->mpb->libuf       = nullptr;
-    cr->mpb->fbuf        = nullptr;
-    cr->mpb->dbuf        = nullptr;
-    cr->mpb->ibuf_alloc  = 0;
-    cr->mpb->libuf_alloc = 0;
-    cr->mpb->fbuf_alloc  = 0;
-    cr->mpb->dbuf_alloc  = 0;
-#endif
-
     return handle;
 }
 
@@ -129,7 +108,6 @@ void done_commrec(t_commrec* cr)
             // TODO: implement
             // done_domdec(cr->dd);
         }
-        done_mpi_in_place_buf(cr->mpb);
     }
 #if GMX_MPI
     // TODO We need to be able to free communicators, but the
@@ -239,30 +217,21 @@ void gmx_setup_nodecomm(FILE gmx_unused* fplog, t_commrec* cr)
 #endif
 }
 
-void gmx_barrier(const t_commrec gmx_unused* cr)
+void gmx_barrier(MPI_Comm gmx_unused communicator)
 {
 #if !GMX_MPI
     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_barrier");
 #else
-    MPI_Barrier(cr->mpi_comm_mygroup);
+    MPI_Barrier(communicator);
 #endif
 }
 
-void gmx_bcast(int gmx_unused nbytes, void gmx_unused* b, const t_commrec gmx_unused* cr)
+void gmx_bcast(int gmx_unused nbytes, void gmx_unused* b, MPI_Comm gmx_unused communicator)
 {
 #if !GMX_MPI
     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_bcast");
 #else
-    MPI_Bcast(b, nbytes, MPI_BYTE, MASTERRANK(cr), cr->mpi_comm_mygroup);
-#endif
-}
-
-void gmx_bcast_sim(int gmx_unused nbytes, void gmx_unused* b, const t_commrec gmx_unused* cr)
-{
-#if !GMX_MPI
-    GMX_RELEASE_ASSERT(false, "Invalid call to gmx_bcast_sim");
-#else
-    MPI_Bcast(b, nbytes, MPI_BYTE, MASTERRANK(cr), cr->mpi_comm_mysim);
+    MPI_Bcast(b, nbytes, MPI_BYTE, 0, communicator);
 #endif
 }
 
index 99d29bfdb0a857b232b2e6284dbfe39312acae0b..3bc41df9b3d7722db17929ab55904c2429585da3 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,7 +49,6 @@
 #include "gromacs/utility/stringutil.h"
 #include "gromacs/utility/unique_cptr.h"
 
-struct gmx_multisim_t;
 struct t_commrec;
 struct t_filenm;
 
@@ -59,7 +59,7 @@ void done_commrec(t_commrec* cr);
 using CommrecHandle = gmx::unique_cptr<t_commrec, done_commrec>;
 
 //! Allocate, initialize and return the commrec.
-CommrecHandle init_commrec(MPI_Comm communicator, const gmx_multisim_t* ms);
+CommrecHandle init_commrec(MPI_Comm communicator);
 
 struct t_commrec* reinitialize_commrec_for_this_thread(const t_commrec* cro);
 
@@ -75,14 +75,11 @@ void gmx_fill_commrec_from_mpi(t_commrec* cr);
 void gmx_setup_nodecomm(FILE* fplog, struct t_commrec* cr);
 /* Sets up fast global communication for clusters with multi-core nodes */
 
-void gmx_barrier(const struct t_commrec* cr);
-/* Wait till all processes in cr->mpi_comm_mygroup have reached the barrier */
+//! Wait until all processes in communicator have reached the barrier
+void gmx_barrier(MPI_Comm communicator);
 
-void gmx_bcast(int nbytes, void* b, const struct t_commrec* cr);
-/* Broadcast nbytes bytes from the master to cr->mpi_comm_mygroup */
-
-void gmx_bcast_sim(int nbytes, void* b, const struct t_commrec* cr);
-/* Broadcast nbytes bytes from the sim master to cr->mpi_comm_mysim */
+//! Broadcast nbytes bytes from the master to communicator
+void gmx_bcast(int nbytes, void* b, MPI_Comm communicator);
 
 void gmx_sumi(int nr, int r[], const struct t_commrec* cr);
 /* Calculate the global sum of an array of ints */
index bf967aea0019e323ac8852cd22a765edb443fa20..306643b31b522c325ef5d4a718de1220aa394441 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 19e5c660dd26bf9e9eaa0a07b502f15d26b376d0..53f3dbf9d7f90f59b7942dd73300829ab9dcf70a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,6 +39,8 @@
 
 #include "nb_free_energy.h"
 
+#include "config.h"
+
 #include <cmath>
 
 #include <algorithm>
 #include "gromacs/math/vec.h"
 #include "gromacs/mdtypes/forceoutput.h"
 #include "gromacs/mdtypes/forcerec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/simd/simd.h"
 #include "gromacs/utility/fatalerror.h"
 
 
-//! Enum for templating the soft-core treatment in the kernel
-enum class SoftCoreTreatment
-{
-    None,    //!< No soft-core
-    RPower6, //!< Soft-core with r-power = 6
-    RPower48 //!< Soft-core with r-power = 48
-};
-
-//! Most treatments are fine with float in mixed-precision mode.
-template<SoftCoreTreatment softCoreTreatment>
-struct SoftCoreReal
+//! Scalar (non-SIMD) data types.
+struct ScalarDataTypes
 {
-    //! Real type for soft-core calculations
-    using Real = real;
+    using RealType                     = real; //!< The data type to use as real.
+    using IntType                      = int;  //!< The data type to use as int.
+    static constexpr int simdRealWidth = 1;    //!< The width of the RealType.
+    static constexpr int simdIntWidth  = 1;    //!< The width of the IntType.
 };
 
-//! This treatment requires double precision for some computations.
-template<>
-struct SoftCoreReal<SoftCoreTreatment::RPower48>
+#if GMX_SIMD_HAVE_REAL && GMX_SIMD_HAVE_INT32_ARITHMETICS
+//! SIMD data types.
+struct SimdDataTypes
 {
-    //! Real type for soft-core calculations
-    using Real = double;
+    using RealType                     = gmx::SimdReal;         //!< The data type to use as real.
+    using IntType                      = gmx::SimdInt32;        //!< The data type to use as int.
+    static constexpr int simdRealWidth = GMX_SIMD_REAL_WIDTH;   //!< The width of the RealType.
+    static constexpr int simdIntWidth  = GMX_SIMD_FINT32_WIDTH; //!< The width of the IntType.
 };
+#endif
 
 //! Computes r^(1/p) and 1/r^(1/p) for the standard p=6
-template<SoftCoreTreatment softCoreTreatment>
-static inline void pthRoot(const real r, real* pthRoot, real* invPthRoot)
-{
-    *invPthRoot = gmx::invsqrt(std::cbrt(r));
-    *pthRoot    = 1 / (*invPthRoot);
-}
-
-// We need a double version to make the specialization below work
-#if !GMX_DOUBLE
-//! Computes r^(1/p) and 1/r^(1/p) for the standard p=6
-template<SoftCoreTreatment softCoreTreatment>
-static inline void pthRoot(const double r, real* pthRoot, double* invPthRoot)
+template<class RealType>
+static inline void pthRoot(const RealType r, RealType* pthRoot, RealType* invPthRoot)
 {
     *invPthRoot = gmx::invsqrt(std::cbrt(r));
     *pthRoot    = 1 / (*invPthRoot);
 }
-#endif
-
-//! Computes r^(1/p) and 1/r^(1/p) for p=48
-template<>
-inline void pthRoot<SoftCoreTreatment::RPower48>(const double r, real* pthRoot, double* invPthRoot)
-{
-    *pthRoot    = std::pow(r, 1.0 / 48.0);
-    *invPthRoot = 1 / (*pthRoot);
-}
-
-template<SoftCoreTreatment softCoreTreatment>
-static inline real calculateSigmaPow(const real sigma6)
-{
-    if (softCoreTreatment == SoftCoreTreatment::RPower6)
-    {
-        return sigma6;
-    }
-    else
-    {
-        real sigmaPow = sigma6 * sigma6;     /* sigma^12 */
-        sigmaPow      = sigmaPow * sigmaPow; /* sigma^24 */
-        sigmaPow      = sigmaPow * sigmaPow; /* sigma^48 */
-        return (sigmaPow);
-    }
-}
 
-template<SoftCoreTreatment softCoreTreatment, class SCReal>
-static inline real calculateRinv6(const SCReal rinvV)
+template<class RealType>
+static inline RealType calculateRinv6(const RealType rinvV)
 {
-    if (softCoreTreatment == SoftCoreTreatment::RPower6)
-    {
-        return rinvV;
-    }
-    else
-    {
-        real rinv6 = rinvV * rinvV;
-        return (rinv6 * rinv6 * rinv6);
-    }
+    RealType rinv6 = rinvV * rinvV;
+    return (rinv6 * rinv6 * rinv6);
 }
 
-static inline real calculateVdw6(const real c6, const real rinv6)
+template<class RealType>
+static inline RealType calculateVdw6(const RealType c6, const RealType rinv6)
 {
     return (c6 * rinv6);
 }
 
-static inline real calculateVdw12(const real c12, const real rinv6)
+template<class RealType>
+static inline RealType calculateVdw12(const RealType c12, const RealType rinv6)
 {
     return (c12 * rinv6 * rinv6);
 }
 
 /* reaction-field electrostatics */
-template<class SCReal>
-static inline SCReal
-reactionFieldScalarForce(const real qq, const real rinv, const SCReal r, const real krf, const real two)
+template<class RealType>
+static inline RealType reactionFieldScalarForce(const RealType qq,
+                                                const RealType rinv,
+                                                const RealType r,
+                                                const real     krf,
+                                                const real     two)
 {
     return (qq * (rinv - two * krf * r * r));
 }
-template<class SCReal>
-static inline real
-reactionFieldPotential(const real qq, const real rinv, const SCReal r, const real krf, const real potentialShift)
+template<class RealType>
+static inline RealType reactionFieldPotential(const RealType qq,
+                                              const RealType rinv,
+                                              const RealType r,
+                                              const real     krf,
+                                              const real     potentialShift)
 {
     return (qq * (rinv + krf * r * r - potentialShift));
 }
 
 /* Ewald electrostatics */
-static inline real ewaldScalarForce(const real coulomb, const real rinv)
+template<class RealType>
+static inline RealType ewaldScalarForce(const RealType coulomb, const RealType rinv)
 {
     return (coulomb * rinv);
 }
-static inline real ewaldPotential(const real coulomb, const real rinv, const real potentialShift)
+template<class RealType>
+static inline RealType ewaldPotential(const RealType coulomb, const RealType rinv, const real potentialShift)
 {
     return (coulomb * (rinv - potentialShift));
 }
 
 /* cutoff LJ */
-static inline real lennardJonesScalarForce(const real v6, const real v12)
+template<class RealType>
+static inline RealType lennardJonesScalarForce(const RealType v6, const RealType v12)
 {
     return (v12 - v6);
 }
-static inline real lennardJonesPotential(const real v6,
-                                         const real v12,
-                                         const real c6,
-                                         const real c12,
-                                         const real repulsionShift,
-                                         const real dispersionShift,
-                                         const real onesixth,
-                                         const real onetwelfth)
+template<class RealType>
+static inline RealType lennardJonesPotential(const RealType v6,
+                                             const RealType v12,
+                                             const RealType c6,
+                                             const RealType c12,
+                                             const real     repulsionShift,
+                                             const real     dispersionShift,
+                                             const real     onesixth,
+                                             const real     onetwelfth)
 {
     return ((v12 + c12 * repulsionShift) * onetwelfth - (v6 + c6 * dispersionShift) * onesixth);
 }
@@ -192,25 +164,28 @@ static inline real ewaldLennardJonesGridSubtract(const real c6grid, const real p
 }
 
 /* LJ Potential switch */
-template<class SCReal>
-static inline SCReal potSwitchScalarForceMod(const SCReal fScalarInp,
-                                             const real   potential,
-                                             const real   sw,
-                                             const SCReal r,
-                                             const real   rVdw,
-                                             const real   dsw,
-                                             const real   zero)
+template<class RealType>
+static inline RealType potSwitchScalarForceMod(const RealType fScalarInp,
+                                               const RealType potential,
+                                               const RealType sw,
+                                               const RealType r,
+                                               const RealType rVdw,
+                                               const RealType dsw,
+                                               const real     zero)
 {
     if (r < rVdw)
     {
-        SCReal fScalar = fScalarInp * sw - r * potential * dsw;
+        real fScalar = fScalarInp * sw - r * potential * dsw;
         return (fScalar);
     }
     return (zero);
 }
-template<class SCReal>
-static inline real
-potSwitchPotentialMod(const real potentialInp, const real sw, const SCReal r, const real rVdw, const real zero)
+template<class RealType>
+static inline RealType potSwitchPotentialMod(const RealType potentialInp,
+                                             const RealType sw,
+                                             const RealType r,
+                                             const RealType rVdw,
+                                             const real     zero)
 {
     if (r < rVdw)
     {
@@ -222,7 +197,7 @@ potSwitchPotentialMod(const real potentialInp, const real sw, const SCReal r, co
 
 
 //! Templated free-energy non-bonded kernel
-template<SoftCoreTreatment softCoreTreatment, bool scLambdasOrAlphasDiffer, bool vdwInteractionTypeIsEwald, bool elecInteractionTypeIsEwald, bool vdwModifierIsPotSwitch>
+template<typename DataTypes, bool useSoftCore, bool scLambdasOrAlphasDiffer, bool vdwInteractionTypeIsEwald, bool elecInteractionTypeIsEwald, bool vdwModifierIsPotSwitch>
 static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                                   rvec* gmx_restrict         xx,
                                   gmx::ForceWithShiftForces* forceWithShiftForces,
@@ -231,14 +206,14 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                                   nb_kernel_data_t* gmx_restrict kernel_data,
                                   t_nrnb* gmx_restrict nrnb)
 {
-    using SCReal = typename SoftCoreReal<softCoreTreatment>::Real;
-
-    constexpr bool useSoftCore = (softCoreTreatment != SoftCoreTreatment::None);
-
 #define STATE_A 0
 #define STATE_B 1
 #define NSTATES 2
 
+    using RealType = typename DataTypes::RealType;
+    using IntType  = typename DataTypes::IntType;
+
+    /* FIXME: How should these be handled with SIMD? */
     constexpr real onetwelfth = 1.0 / 12.0;
     constexpr real onesixth   = 1.0 / 6.0;
     constexpr real zero       = 0.0;
@@ -265,17 +240,18 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
     const int*  typeA         = mdatoms->typeA;
     const int*  typeB         = mdatoms->typeB;
     const int   ntype         = fr->ntype;
-    const real* nbfp          = fr->nbfp;
+    const real* nbfp          = fr->nbfp.data();
     const real* nbfp_grid     = fr->ljpme_c6grid;
     real*       Vv            = kernel_data->energygrp_vdw;
     const real  lambda_coul   = kernel_data->lambda[efptCOUL];
     const real  lambda_vdw    = kernel_data->lambda[efptVDW];
     real*       dvdl          = kernel_data->dvdl;
-    const real  alpha_coul    = fr->sc_alphacoul;
-    const real  alpha_vdw     = fr->sc_alphavdw;
-    const real  lam_power     = fr->sc_power;
-    const real  sigma6_def    = fr->sc_sigma6_def;
-    const real  sigma6_min    = fr->sc_sigma6_min;
+    const auto& scParams      = *ic->softCoreParameters;
+    const real  alpha_coul    = scParams.alphaCoulomb;
+    const real  alpha_vdw     = scParams.alphaVdw;
+    const real  lam_power     = scParams.lambdaPower;
+    const real  sigma6_def    = scParams.sigma6WithInvalidSigma;
+    const real  sigma6_min    = scParams.sigma6Minimum;
     const bool  doForces      = ((kernel_data->flags & GMX_NONBONDED_DO_FORCE) != 0);
     const bool  doShiftForces = ((kernel_data->flags & GMX_NONBONDED_DO_SHIFTFORCE) != 0);
     const bool  doPotential   = ((kernel_data->flags & GMX_NONBONDED_DO_POTENTIAL) != 0);
@@ -367,8 +343,8 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
     GMX_RELEASE_ASSERT(!(vdwInteractionTypeIsEwald && vdwModifierIsPotSwitch),
                        "Can not apply soft-core to switched Ewald potentials");
 
-    SCReal dvdl_coul = 0; /* Needs double for sc_power==48 */
-    SCReal dvdl_vdw  = 0; /* Needs double for sc_power==48 */
+    real dvdl_coul = 0;
+    real dvdl_vdw  = 0;
 
     /* Lambda factor for state A, 1-lambda*/
     real LFC[NSTATES], LFV[NSTATES];
@@ -385,7 +361,7 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
     DLF[STATE_B] = 1;
 
     real           lfac_coul[NSTATES], dlfac_coul[NSTATES], lfac_vdw[NSTATES], dlfac_vdw[NSTATES];
-    constexpr real sc_r_power = (softCoreTreatment == SoftCoreTreatment::RPower48 ? 48.0_real : 6.0_real);
+    constexpr real sc_r_power = 6.0_real;
     for (int i = 0; i < NSTATES; i++)
     {
         lfac_coul[i]  = (lam_power == 2 ? (1 - LFC[i]) * (1 - LFC[i]) : (1 - LFC[i]));
@@ -426,25 +402,31 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
 
         for (int k = nj0; k < nj1; k++)
         {
-            int        tj[NSTATES];
-            const int  jnr = jjnr[k];
-            const int  j3  = 3 * jnr;
-            real       c6[NSTATES], c12[NSTATES], qq[NSTATES], Vcoul[NSTATES], Vvdw[NSTATES];
-            real       r, rinv, rp, rpm2;
-            real       alpha_vdw_eff, alpha_coul_eff, sigma_pow[NSTATES];
-            const real dx  = ix - x[j3];
-            const real dy  = iy - x[j3 + 1];
-            const real dz  = iz - x[j3 + 2];
-            const real rsq = dx * dx + dy * dy + dz * dz;
-            SCReal     FscalC[NSTATES], FscalV[NSTATES]; /* Needs double for sc_power==48 */
-
-            if (rsq >= rcutoff_max2)
+            int            tj[NSTATES];
+            const int      jnr = jjnr[k];
+            const int      j3  = 3 * jnr;
+            RealType       c6[NSTATES], c12[NSTATES], qq[NSTATES], Vcoul[NSTATES], Vvdw[NSTATES];
+            RealType       r, rinv, rp, rpm2;
+            RealType       alpha_vdw_eff, alpha_coul_eff, sigma6[NSTATES];
+            const RealType dx  = ix - x[j3];
+            const RealType dy  = iy - x[j3 + 1];
+            const RealType dz  = iz - x[j3 + 2];
+            const RealType rsq = dx * dx + dy * dy + dz * dz;
+            RealType       FscalC[NSTATES], FscalV[NSTATES];
+            /* Check if this pair on the exlusions list.*/
+            const bool bPairIncluded = nlist->excl_fep == nullptr || nlist->excl_fep[k];
+
+            if (rsq >= rcutoff_max2 && bPairIncluded)
             {
                 /* We save significant time by skipping all code below.
                  * Note that with soft-core interactions, the actual cut-off
                  * check might be different. But since the soft-core distance
                  * is always larger than r, checking on r here is safe.
+                 * Exclusions outside the cutoff can not be skipped as
+                 * when using Ewald: the reciprocal-space
+                 * Ewald component still needs to be subtracted.
                  */
+
                 continue;
             }
             npair_within_cutoff++;
@@ -470,7 +452,12 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                 r    = 0;
             }
 
-            if (softCoreTreatment == SoftCoreTreatment::None)
+            if (useSoftCore)
+            {
+                rpm2 = rsq * rsq;  /* r4 */
+                rp   = rpm2 * rsq; /* r6 */
+            }
+            else
             {
                 /* The soft-core power p will not affect the results
                  * with not using soft-core, so we use power of 0 which gives
@@ -479,21 +466,8 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                 rpm2 = rinv * rinv;
                 rp   = 1;
             }
-            if (softCoreTreatment == SoftCoreTreatment::RPower6)
-            {
-                rpm2 = rsq * rsq;  /* r4 */
-                rp   = rpm2 * rsq; /* r6 */
-            }
-            if (softCoreTreatment == SoftCoreTreatment::RPower48)
-            {
-                rp   = rsq * rsq * rsq; /* r6 */
-                rp   = rp * rp;         /* r12 */
-                rp   = rp * rp;         /* r24 */
-                rp   = rp * rp;         /* r48 */
-                rpm2 = rp / rsq;        /* r46 */
-            }
 
-            real Fscal = 0;
+            RealType Fscal = 0;
 
             qq[STATE_A] = iqA * chargeA[jnr];
             qq[STATE_B] = iqB * chargeB[jnr];
@@ -501,7 +475,7 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
             tj[STATE_A] = ntiA + 2 * typeA[jnr];
             tj[STATE_B] = ntiB + 2 * typeB[jnr];
 
-            if (nlist->excl_fep == nullptr || nlist->excl_fep[k])
+            if (bPairIncluded)
             {
                 c6[STATE_A] = nbfp[tj[STATE_A]];
                 c6[STATE_B] = nbfp[tj[STATE_B]];
@@ -511,7 +485,6 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                     c12[i] = nbfp[tj[i] + 1];
                     if (useSoftCore)
                     {
-                        real sigma6[NSTATES];
                         if ((c6[i] > 0) && (c12[i] > 0))
                         {
                             /* c12 is stored scaled with 12.0 and c6 is scaled with 6.0 - correct for this */
@@ -525,7 +498,6 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                         {
                             sigma6[i] = sigma6_def;
                         }
-                        sigma_pow[i] = calculateSigmaPow<softCoreTreatment>(sigma6[i]);
                     }
                 }
 
@@ -551,21 +523,20 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                     Vcoul[i]  = 0;
                     Vvdw[i]   = 0;
 
-                    real   rinvC, rinvV;
-                    SCReal rC, rV, rpinvC, rpinvV; /* Needs double for sc_power==48 */
+                    RealType rinvC, rinvV, rC, rV, rpinvC, rpinvV;
 
                     /* Only spend time on A or B state if it is non-zero */
                     if ((qq[i] != 0) || (c6[i] != 0) || (c12[i] != 0))
                     {
-                        /* this section has to be inside the loop because of the dependence on sigma_pow */
+                        /* this section has to be inside the loop because of the dependence on sigma6 */
                         if (useSoftCore)
                         {
-                            rpinvC = one / (alpha_coul_eff * lfac_coul[i] * sigma_pow[i] + rp);
-                            pthRoot<softCoreTreatment>(rpinvC, &rinvC, &rC);
+                            rpinvC = one / (alpha_coul_eff * lfac_coul[i] * sigma6[i] + rp);
+                            pthRoot(rpinvC, &rinvC, &rC);
                             if (scLambdasOrAlphasDiffer)
                             {
-                                rpinvV = one / (alpha_vdw_eff * lfac_vdw[i] * sigma_pow[i] + rp);
-                                pthRoot<softCoreTreatment>(rpinvV, &rinvV, &rV);
+                                rpinvV = one / (alpha_vdw_eff * lfac_vdw[i] * sigma6[i] + rp);
+                                pthRoot(rpinvV, &rinvV, &rV);
                             }
                             else
                             {
@@ -616,17 +587,17 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                                                      || (!vdwInteractionTypeIsEwald && rV < rvdw);
                         if ((c6[i] != 0 || c12[i] != 0) && computeVdwInteraction)
                         {
-                            real rinv6;
-                            if (softCoreTreatment == SoftCoreTreatment::RPower6)
+                            RealType rinv6;
+                            if (useSoftCore)
                             {
-                                rinv6 = calculateRinv6<softCoreTreatment>(rpinvV);
+                                rinv6 = rpinvV;
                             }
                             else
                             {
-                                rinv6 = calculateRinv6<softCoreTreatment>(rinvV);
+                                rinv6 = calculateRinv6(rinvV);
                             }
-                            real Vvdw6  = calculateVdw6(c6[i], rinv6);
-                            real Vvdw12 = calculateVdw12(c12[i], rinv6);
+                            RealType Vvdw6  = calculateVdw6(c6[i], rinv6);
+                            RealType Vvdw12 = calculateVdw12(c12[i], rinv6);
 
                             Vvdw[i] = lennardJonesPotential(Vvdw6, Vvdw12, c6[i], c12[i], repulsionShift,
                                                             dispersionShift, onesixth, onetwelfth);
@@ -641,11 +612,12 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
 
                             if (vdwModifierIsPotSwitch)
                             {
-                                real d        = rV - ic->rvdw_switch;
-                                d             = (d > zero) ? d : zero;
-                                const real d2 = d * d;
-                                const real sw = one + d2 * d * (vdw_swV3 + d * (vdw_swV4 + d * vdw_swV5));
-                                const real dsw = d2 * (vdw_swF2 + d * (vdw_swF3 + d * vdw_swF4));
+                                RealType d        = rV - ic->rvdw_switch;
+                                d                 = (d > zero) ? d : zero;
+                                const RealType d2 = d * d;
+                                const RealType sw =
+                                        one + d2 * d * (vdw_swV3 + d * (vdw_swV4 + d * vdw_swV5));
+                                const RealType dsw = d2 * (vdw_swF2 + d * (vdw_swF3 + d * vdw_swF4));
 
                                 FscalV[i] = potSwitchScalarForceMod(FscalV[i], Vvdw[i], sw, rV,
                                                                     rvdw, dsw, zero);
@@ -661,7 +633,7 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                         FscalC[i] *= rpinvC;
                         FscalV[i] *= rpinvV;
                     }
-                }
+                } // end for (int i = 0; i < NSTATES; i++)
 
                 /* Assemble A and B states */
                 for (int i = 0; i < NSTATES; i++)
@@ -674,11 +646,10 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
 
                     if (useSoftCore)
                     {
-                        dvdl_coul +=
-                                Vcoul[i] * DLF[i]
-                                + LFC[i] * alpha_coul_eff * dlfac_coul[i] * FscalC[i] * sigma_pow[i];
+                        dvdl_coul += Vcoul[i] * DLF[i]
+                                     + LFC[i] * alpha_coul_eff * dlfac_coul[i] * FscalC[i] * sigma6[i];
                         dvdl_vdw += Vvdw[i] * DLF[i]
-                                    + LFV[i] * alpha_vdw_eff * dlfac_vdw[i] * FscalV[i] * sigma_pow[i];
+                                    + LFV[i] * alpha_vdw_eff * dlfac_vdw[i] * FscalV[i] * sigma6[i];
                     }
                     else
                     {
@@ -686,7 +657,7 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                         dvdl_vdw += Vvdw[i] * DLF[i];
                     }
                 }
-            }
+            } // end if (bPairIncluded)
             else if (icoul == GMX_NBKERNEL_ELEC_REACTIONFIELD)
             {
                 /* For excluded pairs, which are only in this pair list when
@@ -694,7 +665,7 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                  * As there is no singularity, there is no need for soft-core.
                  */
                 const real FF = -two * krf;
-                real       VV = krf * rsq - crf;
+                RealType   VV = krf * rsq - crf;
 
                 if (ii == jnr)
                 {
@@ -709,7 +680,7 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                 }
             }
 
-            if (elecInteractionTypeIsEwald && r < rcoulomb)
+            if (elecInteractionTypeIsEwald && (r < rcoulomb || !bPairIncluded))
             {
                 /* See comment in the preamble. When using Ewald interactions
                  * (unless we use a switch modifier) we subtract the reciprocal-space
@@ -721,11 +692,11 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                  */
                 real v_lr, f_lr;
 
-                const real ewrt   = r * coulombTableScale;
-                int        ewitab = static_cast<int>(ewrt);
-                const real eweps  = ewrt - ewitab;
-                ewitab            = 4 * ewitab;
-                f_lr              = ewtab[ewitab] + eweps * ewtab[ewitab + 1];
+                const RealType ewrt   = r * coulombTableScale;
+                IntType        ewitab = static_cast<IntType>(ewrt);
+                const RealType eweps  = ewrt - ewitab;
+                ewitab                = 4 * ewitab;
+                f_lr                  = ewtab[ewitab] + eweps * ewtab[ewitab + 1];
                 v_lr = (ewtab[ewitab + 2] - coulombTableScaleInvHalf * eweps * (ewtab[ewitab] + f_lr));
                 f_lr *= rinv;
 
@@ -766,16 +737,17 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                  * r close to 0 for non-interacting pairs.
                  */
 
-                const real rs   = rsq * rinv * vdwTableScale;
-                const int  ri   = static_cast<int>(rs);
-                const real frac = rs - ri;
-                const real f_lr = (1 - frac) * tab_ewald_F_lj[ri] + frac * tab_ewald_F_lj[ri + 1];
+                const RealType rs   = rsq * rinv * vdwTableScale;
+                const IntType  ri   = static_cast<IntType>(rs);
+                const RealType frac = rs - ri;
+                const RealType f_lr = (1 - frac) * tab_ewald_F_lj[ri] + frac * tab_ewald_F_lj[ri + 1];
                 /* TODO: Currently the Ewald LJ table does not contain
                  * the factor 1/6, we should add this.
                  */
-                const real FF = f_lr * rinv / six;
-                real VV = (tab_ewald_V_lj[ri] - vdwTableScaleInvHalf * frac * (tab_ewald_F_lj[ri] + f_lr))
-                          / six;
+                const RealType FF = f_lr * rinv / six;
+                RealType       VV =
+                        (tab_ewald_V_lj[ri] - vdwTableScaleInvHalf * frac * (tab_ewald_F_lj[ri] + f_lr))
+                        / six;
 
                 if (ii == jnr)
                 {
@@ -818,7 +790,7 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
 #pragma omp atomic
                 f[j3 + 2] -= tz;
             }
-        }
+        } // end for (int k = nj0; k < nj1; k++)
 
         /* The atomics below are expensive with many OpenMP threads.
          * Here unperturbed i-particles will usually only have a few
@@ -854,7 +826,7 @@ static void nb_free_energy_kernel(const t_nblist* gmx_restrict nlist,
                 Vv[ggid] += vvtot;
             }
         }
-    }
+    } // end for (int n = 0; n < nri; n++)
 
 #pragma omp atomic
     dvdl[efptCOUL] += dvdl_coul;
@@ -877,95 +849,114 @@ typedef void (*KernelFunction)(const t_nblist* gmx_restrict nlist,
                                nb_kernel_data_t* gmx_restrict kernel_data,
                                t_nrnb* gmx_restrict nrnb);
 
-template<SoftCoreTreatment softCoreTreatment, bool scLambdasOrAlphasDiffer, bool vdwInteractionTypeIsEwald, bool elecInteractionTypeIsEwald>
-static KernelFunction dispatchKernelOnVdwModifier(const bool vdwModifierIsPotSwitch)
+template<bool useSoftCore, bool scLambdasOrAlphasDiffer, bool vdwInteractionTypeIsEwald, bool elecInteractionTypeIsEwald, bool vdwModifierIsPotSwitch>
+static KernelFunction dispatchKernelOnUseSimd(const bool useSimd)
+{
+    if (useSimd)
+    {
+#if GMX_SIMD_HAVE_REAL && GMX_SIMD_HAVE_INT32_ARITHMETICS && GMX_USE_SIMD_KERNELS
+        /* FIXME: Here SimdDataTypes should be used to enable SIMD. So far, the code in nb_free_energy_kernel is not adapted to SIMD */
+        return (nb_free_energy_kernel<ScalarDataTypes, useSoftCore, scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald,
+                                      elecInteractionTypeIsEwald, vdwModifierIsPotSwitch>);
+#else
+        return (nb_free_energy_kernel<ScalarDataTypes, useSoftCore, scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald,
+                                      elecInteractionTypeIsEwald, vdwModifierIsPotSwitch>);
+#endif
+    }
+    else
+    {
+        return (nb_free_energy_kernel<ScalarDataTypes, useSoftCore, scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald,
+                                      elecInteractionTypeIsEwald, vdwModifierIsPotSwitch>);
+    }
+}
+
+template<bool useSoftCore, bool scLambdasOrAlphasDiffer, bool vdwInteractionTypeIsEwald, bool elecInteractionTypeIsEwald>
+static KernelFunction dispatchKernelOnVdwModifier(const bool vdwModifierIsPotSwitch, const bool useSimd)
 {
     if (vdwModifierIsPotSwitch)
     {
-        return (nb_free_energy_kernel<softCoreTreatment, scLambdasOrAlphasDiffer,
-                                      vdwInteractionTypeIsEwald, elecInteractionTypeIsEwald, true>);
+        return (dispatchKernelOnUseSimd<useSoftCore, scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald,
+                                        elecInteractionTypeIsEwald, true>(useSimd));
     }
     else
     {
-        return (nb_free_energy_kernel<softCoreTreatment, scLambdasOrAlphasDiffer,
-                                      vdwInteractionTypeIsEwald, elecInteractionTypeIsEwald, false>);
+        return (dispatchKernelOnUseSimd<useSoftCore, scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald,
+                                        elecInteractionTypeIsEwald, false>(useSimd));
     }
 }
 
-template<SoftCoreTreatment softCoreTreatment, bool scLambdasOrAlphasDiffer, bool vdwInteractionTypeIsEwald>
+template<bool useSoftCore, bool scLambdasOrAlphasDiffer, bool vdwInteractionTypeIsEwald>
 static KernelFunction dispatchKernelOnElecInteractionType(const bool elecInteractionTypeIsEwald,
-                                                          const bool vdwModifierIsPotSwitch)
+                                                          const bool vdwModifierIsPotSwitch,
+                                                          const bool useSimd)
 {
     if (elecInteractionTypeIsEwald)
     {
-        return (dispatchKernelOnVdwModifier<softCoreTreatment, scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald, true>(
-                vdwModifierIsPotSwitch));
+        return (dispatchKernelOnVdwModifier<useSoftCore, scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald, true>(
+                vdwModifierIsPotSwitch, useSimd));
     }
     else
     {
-        return (dispatchKernelOnVdwModifier<softCoreTreatment, scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald, false>(
-                vdwModifierIsPotSwitch));
+        return (dispatchKernelOnVdwModifier<useSoftCore, scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald, false>(
+                vdwModifierIsPotSwitch, useSimd));
     }
 }
 
-template<SoftCoreTreatment softCoreTreatment, bool scLambdasOrAlphasDiffer>
+template<bool useSoftCore, bool scLambdasOrAlphasDiffer>
 static KernelFunction dispatchKernelOnVdwInteractionType(const bool vdwInteractionTypeIsEwald,
                                                          const bool elecInteractionTypeIsEwald,
-                                                         const bool vdwModifierIsPotSwitch)
+                                                         const bool vdwModifierIsPotSwitch,
+                                                         const bool useSimd)
 {
     if (vdwInteractionTypeIsEwald)
     {
-        return (dispatchKernelOnElecInteractionType<softCoreTreatment, scLambdasOrAlphasDiffer, true>(
-                elecInteractionTypeIsEwald, vdwModifierIsPotSwitch));
+        return (dispatchKernelOnElecInteractionType<useSoftCore, scLambdasOrAlphasDiffer, true>(
+                elecInteractionTypeIsEwald, vdwModifierIsPotSwitch, useSimd));
     }
     else
     {
-        return (dispatchKernelOnElecInteractionType<softCoreTreatment, scLambdasOrAlphasDiffer, false>(
-                elecInteractionTypeIsEwald, vdwModifierIsPotSwitch));
+        return (dispatchKernelOnElecInteractionType<useSoftCore, scLambdasOrAlphasDiffer, false>(
+                elecInteractionTypeIsEwald, vdwModifierIsPotSwitch, useSimd));
     }
 }
 
-template<SoftCoreTreatment softCoreTreatment>
+template<bool useSoftCore>
 static KernelFunction dispatchKernelOnScLambdasOrAlphasDifference(const bool scLambdasOrAlphasDiffer,
                                                                   const bool vdwInteractionTypeIsEwald,
                                                                   const bool elecInteractionTypeIsEwald,
-                                                                  const bool vdwModifierIsPotSwitch)
+                                                                  const bool vdwModifierIsPotSwitch,
+                                                                  const bool useSimd)
 {
     if (scLambdasOrAlphasDiffer)
     {
-        return (dispatchKernelOnVdwInteractionType<softCoreTreatment, true>(
-                vdwInteractionTypeIsEwald, elecInteractionTypeIsEwald, vdwModifierIsPotSwitch));
+        return (dispatchKernelOnVdwInteractionType<useSoftCore, true>(
+                vdwInteractionTypeIsEwald, elecInteractionTypeIsEwald, vdwModifierIsPotSwitch, useSimd));
     }
     else
     {
-        return (dispatchKernelOnVdwInteractionType<softCoreTreatment, false>(
-                vdwInteractionTypeIsEwald, elecInteractionTypeIsEwald, vdwModifierIsPotSwitch));
+        return (dispatchKernelOnVdwInteractionType<useSoftCore, false>(
+                vdwInteractionTypeIsEwald, elecInteractionTypeIsEwald, vdwModifierIsPotSwitch, useSimd));
     }
 }
 
-static KernelFunction dispatchKernel(const bool        scLambdasOrAlphasDiffer,
-                                     const bool        vdwInteractionTypeIsEwald,
-                                     const bool        elecInteractionTypeIsEwald,
-                                     const bool        vdwModifierIsPotSwitch,
-                                     const t_forcerec* fr)
+static KernelFunction dispatchKernel(const bool                 scLambdasOrAlphasDiffer,
+                                     const bool                 vdwInteractionTypeIsEwald,
+                                     const bool                 elecInteractionTypeIsEwald,
+                                     const bool                 vdwModifierIsPotSwitch,
+                                     const bool                 useSimd,
+                                     const interaction_const_t& ic)
 {
-    if (fr->sc_alphacoul == 0 && fr->sc_alphavdw == 0)
+    if (ic.softCoreParameters->alphaCoulomb == 0 && ic.softCoreParameters->alphaVdw == 0)
     {
-        return (dispatchKernelOnScLambdasOrAlphasDifference<SoftCoreTreatment::None>(
+        return (dispatchKernelOnScLambdasOrAlphasDifference<false>(
                 scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald, elecInteractionTypeIsEwald,
-                vdwModifierIsPotSwitch));
-    }
-    else if (fr->sc_r_power == 6.0_real)
-    {
-        return (dispatchKernelOnScLambdasOrAlphasDifference<SoftCoreTreatment::RPower6>(
-                scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald, elecInteractionTypeIsEwald,
-                vdwModifierIsPotSwitch));
+                vdwModifierIsPotSwitch, useSimd));
     }
     else
     {
-        return (dispatchKernelOnScLambdasOrAlphasDifference<SoftCoreTreatment::RPower48>(
+        return (dispatchKernelOnScLambdasOrAlphasDifference<true>(
                 scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald, elecInteractionTypeIsEwald,
-                vdwModifierIsPotSwitch));
+                vdwModifierIsPotSwitch, useSimd));
     }
 }
 
@@ -978,30 +969,33 @@ void gmx_nb_free_energy_kernel(const t_nblist*            nlist,
                                nb_kernel_data_t*          kernel_data,
                                t_nrnb*                    nrnb)
 {
-    GMX_ASSERT(EEL_PME_EWALD(fr->ic->eeltype) || fr->ic->eeltype == eelCUT || EEL_RF(fr->ic->eeltype),
+    const interaction_const_t& ic = *fr->ic;
+    GMX_ASSERT(EEL_PME_EWALD(ic.eeltype) || ic.eeltype == eelCUT || EEL_RF(ic.eeltype),
                "Unsupported eeltype with free energy");
+    GMX_ASSERT(ic.softCoreParameters, "We need soft-core parameters");
 
-    const bool vdwInteractionTypeIsEwald  = (EVDW_PME(fr->ic->vdwtype));
-    const bool elecInteractionTypeIsEwald = (EEL_PME_EWALD(fr->ic->eeltype));
-    const bool vdwModifierIsPotSwitch     = (fr->ic->vdw_modifier == eintmodPOTSWITCH);
-    bool       scLambdasOrAlphasDiffer    = true;
+    const auto& scParams                   = *ic.softCoreParameters;
+    const bool  vdwInteractionTypeIsEwald  = (EVDW_PME(ic.vdwtype));
+    const bool  elecInteractionTypeIsEwald = (EEL_PME_EWALD(ic.eeltype));
+    const bool  vdwModifierIsPotSwitch     = (ic.vdw_modifier == eintmodPOTSWITCH);
+    bool        scLambdasOrAlphasDiffer    = true;
+    const bool  useSimd                    = fr->use_simd_kernels;
 
-    if (fr->sc_alphacoul == 0 && fr->sc_alphavdw == 0)
+    if (scParams.alphaCoulomb == 0 && scParams.alphaVdw == 0)
     {
         scLambdasOrAlphasDiffer = false;
     }
-    else if (fr->sc_r_power == 6.0_real || fr->sc_r_power == 48.0_real)
+    else
     {
-        if (kernel_data->lambda[efptCOUL] == kernel_data->lambda[efptVDW] && fr->sc_alphacoul == fr->sc_alphavdw)
+        if (kernel_data->lambda[efptCOUL] == kernel_data->lambda[efptVDW]
+            && scParams.alphaCoulomb == scParams.alphaVdw)
         {
             scLambdasOrAlphasDiffer = false;
         }
     }
-    else
-    {
-        GMX_RELEASE_ASSERT(false, "Unsupported soft-core r-power");
-    }
-    KernelFunction kernelFunc = dispatchKernel(scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald,
-                                               elecInteractionTypeIsEwald, vdwModifierIsPotSwitch, fr);
+
+    KernelFunction kernelFunc;
+    kernelFunc = dispatchKernel(scLambdasOrAlphasDiffer, vdwInteractionTypeIsEwald,
+                                elecInteractionTypeIsEwald, vdwModifierIsPotSwitch, useSimd, ic);
     kernelFunc(nlist, xx, ff, fr, mdatoms, kernel_data, nrnb);
 }
index 75070022ee4934e86a9aa96dc08dba5045ec83b3..ee78e7fe0241b5cb6aee7c2072402e4f07a84cba 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/gmxlib/nonbonded/nb_kernel.h"
 #include "gromacs/math/vectypes.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/nblist.h"
 
 struct t_forcerec;
+struct t_mdatoms;
 namespace gmx
 {
 class ForceWithShiftForces;
index b90f7404ae61f8123e029601a71bd252fb8a6d5e..ff75e9159a77affa82c9ec88f3e04ab6a52c4c2d 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/math/vectypes.h"
-#include "gromacs/mdtypes/forcerec.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/nblist.h"
 #include "gromacs/utility/real.h"
 
 struct t_blocka;
+struct t_mdatoms;
 
 /* Structure to collect kernel data not available in forcerec or mdatoms structures.
  * This is only used inside the nonbonded module.
@@ -53,7 +53,7 @@ typedef struct
 {
     int                    flags;
     const struct t_blocka* exclusions;
-    real*                  lambda;
+    const real*            lambda;
     real*                  dvdl;
 
     /* pointers to tables */
index 77598f01085acb59cbc6ecba68dceaca8f0dc23c..1366d351e51c93f6abbec57f2234fd6300bea1e5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -170,10 +171,11 @@ static const t_nrnb_data nbdata[eNRNB] = {
     { "Lincs", 60 },
     { "Lincs-Mat", 4 },
     { "Shake", 30 },
-    { "Constraint-V", 8 },
+    { "Constraint-V", 9 },
     { "Shake-Init", 10 },
     { "Constraint-Vir", 24 },
-    { "Settle", 323 },
+    { "Settle", 370 },
+    { "Virtual Site 1", 1 },
     { "Virtual Site 2", 23 },
     { "Virtual Site 2fd", 63 },
     { "Virtual Site 3", 37 },
index 85eff94880685cba5eadb2c9486a4fa043249167..847c6cdb9d345ca3d168c95e7cbf42d3c7cdc0c4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -154,6 +155,7 @@ enum
     eNR_SHAKE_RIJ,
     eNR_CONSTR_VIR,
     eNR_SETTLE,
+    eNR_VSITE1,
     eNR_VSITE2,
     eNR_VSITE2FD,
     eNR_VSITE3,
index 66ee98c31e341ad53ecc557c098dba6949fe29a0..6dec70f062aaae7c5a26d0acdb3bcf9a7728be23 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2016, by the GROMACS development team, led by
+# Copyright (c) 2016,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,4 +33,5 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(GmxlibTests gmxlib-test
+    CPP_SOURCE_FILES
     )
index bffcbdf8755ab2605a18c9a48086f3e944d06bc8..aac7f94e74db9ea46c5e7b706d7b8efadb00de2a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 048d42e799f0a0aae66f49b2a56be9de1a95e659..22d60b8477c9da0832d678f9c193dce7158ae2b0 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GMXPREPROCESS_ADD_PAR_H
 #define GMX_GMXPREPROCESS_ADD_PAR_H
 
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/real.h"
 
 struct InteractionsOfType;
 struct PreprocessResidue;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 void add_param(InteractionsOfType* ps, int ai, int aj, gmx::ArrayRef<const real> c, const char* s);
 
 void add_cmap_param(InteractionsOfType* ps, int ai, int aj, int ak, int al, int am, const char* s);
index b8c6c6635907fd9c2774bb9e08aa5652dda6ed00..225086c52f6dd3aaad4e99bc7615b83666381425 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -401,6 +402,7 @@ static int assign_param(t_functype ftype, t_iparams* newparam, gmx::ArrayRef<con
             newparam->settle.doh = old[0];
             newparam->settle.dhh = old[1];
             break;
+        case F_VSITE1:
         case F_VSITE2:
         case F_VSITE2FD:
         case F_VSITE3:
@@ -449,7 +451,6 @@ static int enter_params(gmx_ffparams_t*           ffparams,
                         bool                      bAppend)
 {
     t_iparams newparam;
-    int       type;
     int       rc;
 
     if ((rc = assign_param(ftype, &newparam, forceparams, comb, reppow)) < 0)
@@ -460,21 +461,33 @@ static int enter_params(gmx_ffparams_t*           ffparams,
 
     if (!bAppend)
     {
-        for (type = start; (type < ffparams->numTypes()); type++)
+        if (ftype != F_DISRES)
         {
-            if (ffparams->functype[type] == ftype)
+            for (int type = start; type < ffparams->numTypes(); type++)
             {
-                if (memcmp(&newparam, &ffparams->iparams[type], static_cast<size_t>(sizeof(newparam))) == 0)
+                // Note that the first condition is always met by starting the loop at start
+                if (ffparams->functype[type] == ftype
+                    && memcmp(&newparam, &ffparams->iparams[type], static_cast<size_t>(sizeof(newparam))) == 0)
                 {
                     return type;
                 }
             }
         }
+        else
+        {
+            // Distance restraints should have unique labels and pairs with the same label
+            // should be consecutive, so we here we only need to check the last type in the list.
+            // This changes the complexity from quadratic to linear in the number of restraints.
+            const int type = ffparams->numTypes() - 1;
+            if (type >= 0 && ffparams->functype[type] == ftype
+                && memcmp(&newparam, &ffparams->iparams[type], static_cast<size_t>(sizeof(newparam))) == 0)
+            {
+                return type;
+            }
+        }
     }
-    else
-    {
-        type = ffparams->numTypes();
-    }
+
+    const int type = ffparams->numTypes();
 
     ffparams->iparams.push_back(newparam);
     ffparams->functype.push_back(ftype);
index 5268cde9fcbe5094e2485e5343d788c98b1cd2f8..a3d65b8e846219e96e7daf97bb782a0a25581823 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GMXPREPROCESS_CONVPARM_H
 #define GMX_GMXPREPROCESS_CONVPARM_H
 
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/real.h"
 
 struct gmx_mtop_t;
 struct MoleculeInformation;
 struct InteractionsOfType;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 void convertInteractionsOfType(int                                      atnr,
                                gmx::ArrayRef<const InteractionsOfType>  nbtypes,
                                gmx::ArrayRef<const MoleculeInformation> mi,
index ee76e72bfe2ddf48c05fbfe423df4c151641d136..45f02a99ccc937e1ed5ef6771b395b8d136fbb77 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -328,7 +329,7 @@ static void pdb_legend(FILE* out, int natoms, int nres, t_atoms* atoms, rvec x[]
     }
 }
 
-static void visualize_images(const char* fn, int ePBC, matrix box)
+static void visualize_images(const char* fn, PbcType pbcType, matrix box)
 {
     t_atoms atoms;
     rvec*   img;
@@ -352,7 +353,7 @@ static void visualize_images(const char* fn, int ePBC, matrix box)
     }
     calc_triclinic_images(box, img + 1);
 
-    write_sto_conf(fn, "Images", &atoms, img, nullptr, ePBC, box);
+    write_sto_conf(fn, "Images", &atoms, img, nullptr, pbcType, box);
 
     done_atom(&atoms);
     sfree(img);
@@ -681,7 +682,7 @@ int gmx_editconf(int argc, char* argv[])
     int               isize, ssize, numAlignmentAtoms;
     int *             index, *sindex, *aindex;
     rvec *            x, *v, gc, rmin, rmax, size;
-    int               ePBC;
+    PbcType           pbcType;
     matrix            box, rotmatrix, trans;
     rvec              princd, tmpvec;
     gmx_bool          bIndex, bSetSize, bSetAng, bDist, bSetCenter, bAlign;
@@ -768,7 +769,7 @@ int gmx_editconf(int argc, char* argv[])
     char*    name;
     t_atoms  atoms;
     open_symtab(&symtab);
-    readConfAndAtoms(infile, &symtab, &name, &atoms, &ePBC, &x, &v, box);
+    readConfAndAtoms(infile, &symtab, &name, &atoms, &pbcType, &x, &v, box);
     natom = atoms.nr;
     if (atoms.pdbinfo == nullptr)
     {
@@ -788,7 +789,7 @@ int gmx_editconf(int argc, char* argv[])
         get_pdb_atomnumber(&atoms, &aps);
     }
 
-    if (ePBC != epbcNONE)
+    if (pbcType != PbcType::No)
     {
         real vol = det(box);
         printf("Volume: %g nm^3, corresponds to roughly %d electrons\n", vol,
@@ -880,7 +881,7 @@ int gmx_editconf(int argc, char* argv[])
     }
     else if (visbox[0] == -1)
     {
-        visualize_images("images.pdb", ePBC, box);
+        visualize_images("images.pdb", pbcType, box);
     }
 
     /* remove pbc */
@@ -1071,7 +1072,7 @@ int gmx_editconf(int argc, char* argv[])
 
     if ((btype[0] != nullptr) && (bSetSize || bDist || (btype[0][0] == 't' && bSetAng)))
     {
-        ePBC = epbcXYZ;
+        pbcType = PbcType::Xyz;
         if (!(bSetSize || bDist))
         {
             for (i = 0; i < DIM; i++)
@@ -1169,12 +1170,12 @@ int gmx_editconf(int argc, char* argv[])
         printf("new box volume  :%7.2f               (nm^3)\n", det(box));
     }
 
-    if (check_box(epbcXYZ, box))
+    if (check_box(PbcType::Xyz, box))
     {
         printf("\nWARNING: %s\n"
                "See the GROMACS manual for a description of the requirements that\n"
                "must be satisfied by descriptions of simulation cells.\n",
-               check_box(epbcXYZ, box));
+               check_box(PbcType::Xyz, box));
     }
 
     if (bDist && btype[0][0] == 't')
@@ -1230,12 +1231,13 @@ int gmx_editconf(int argc, char* argv[])
         if (outftp == efPDB)
         {
             out = gmx_ffopen(outfile, "w");
-            write_pdbfile_indexed(out, name, &atoms, x, ePBC, box, ' ', 1, isize, index, conect, FALSE);
+            write_pdbfile_indexed(out, name, &atoms, x, pbcType, box, ' ', 1, isize, index, conect, FALSE);
             gmx_ffclose(out);
         }
         else
         {
-            write_sto_conf_indexed(outfile, name, &atoms, x, bHaveV ? v : nullptr, ePBC, box, isize, index);
+            write_sto_conf_indexed(outfile, name, &atoms, x, bHaveV ? v : nullptr, pbcType, box,
+                                   isize, index);
         }
         sfree(grpname);
         sfree(index);
@@ -1290,8 +1292,8 @@ int gmx_editconf(int argc, char* argv[])
             {
                 index[i] = i;
             }
-            write_pdbfile_indexed(out, name, &atoms, x, ePBC, box, ' ', -1, atoms.nr, index, conect,
-                                  outftp == efPQR);
+            write_pdbfile_indexed(out, name, &atoms, x, pbcType, box, ' ', -1, atoms.nr, index,
+                                  conect, outftp == efPQR);
             sfree(index);
             if (bLegend)
             {
@@ -1306,7 +1308,7 @@ int gmx_editconf(int argc, char* argv[])
         }
         else
         {
-            write_sto_conf(outfile, name, &atoms, x, bHaveV ? v : nullptr, ePBC, box);
+            write_sto_conf(outfile, name, &atoms, x, bHaveV ? v : nullptr, pbcType, box);
         }
     }
     done_atom(&atoms);
index 89eb9d44a25eda799db654078becf9a831ea2ba9..9942775507f008c588a81bf8201b3d30a847ef75 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 2ccc05234c95c2bf681a507359fa65a34d9b00e8..bdc4bcc8f260633dc4dfecb7b0054fa1192d0e80 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -77,11 +78,7 @@ static bool acomp(const InteractionOfType& a1, const InteractionOfType& a2)
 {
     int ac;
 
-    if ((ac = (a1.aj() - a2.aj())) != 0)
-    {
-        return ac < 0;
-    }
-    else if ((ac = (a1.ai() - a2.ai())) != 0)
+    if (((ac = (a1.aj() - a2.aj())) != 0) || ((ac = (a1.ai() - a2.ai())) != 0))
     {
         return ac < 0;
     }
@@ -110,12 +107,8 @@ static bool dcomp(const InteractionOfType& d1, const InteractionOfType& d2)
     int dc;
 
     /* First sort by J & K (the two central) atoms */
-    if ((dc = (d1.aj() - d2.aj())) != 0)
-    {
-        return dc < 0;
-    }
-    else if ((dc = (d1.ak() - d2.ak())) != 0)
-    {
+    if (((dc = (d1.aj() - d2.aj())) != 0) || ((dc = (d1.ak() - d2.ak())) != 0))
+    { // NOLINT bugprone-branch-clone
         return dc < 0;
     }
     /* Then make sure to put rtp dihedrals before generated ones */
@@ -128,11 +121,7 @@ static bool dcomp(const InteractionOfType& d1, const InteractionOfType& d2)
         return false;
     }
     /* Then sort by I and J (two outer) atoms */
-    else if ((dc = (d1.ai() - d2.ai())) != 0)
-    {
-        return dc < 0;
-    }
-    else if ((dc = (d1.al() - d2.al())) != 0)
+    else if (((dc = (d1.ai() - d2.ai())) != 0) || ((dc = (d1.al() - d2.al())) != 0))
     {
         return dc < 0;
     }
@@ -193,15 +182,7 @@ static bool idcomp(const InteractionOfType& a, const InteractionOfType& b)
 {
     int d;
 
-    if ((d = (a.ai() - b.ai())) != 0)
-    {
-        return d < 0;
-    }
-    else if ((d = (a.al() - b.al())) != 0)
-    {
-        return d < 0;
-    }
-    else if ((d = (a.aj() - b.aj())) != 0)
+    if (((d = (a.ai() - b.ai())) != 0) || ((d = (a.al() - b.al())) != 0) || ((d = (a.aj() - b.aj())) != 0))
     {
         return d < 0;
     }
index f05ffdcf4d440f5d6e7e3d1656a69bf673a69906..e722f6c1eb504cfc4aae633e41060b20a68dc131 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GMXPREPROCESS_GEN_AD_H
 #define GMX_GMXPREPROCESS_GEN_AD_H
 
-#include "gromacs/utility/arrayref.h"
-
 struct t_atoms;
 struct t_excls;
 struct MoleculePatchDatabase;
 struct InteractionsOfType;
 struct PreprocessResidue;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 void gen_pad(t_atoms*                               atoms,
              gmx::ArrayRef<const PreprocessResidue> rtpFFDB,
              gmx::ArrayRef<InteractionsOfType>      plist,
index fbcd56eeb4dca313c17ba18a999440d242c5eeae..41d53a1d3c4d589b2e9d90da34a091f2e8cea082 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/logger.h"
 #include "gromacs/utility/smalloc.h"
 
-static void low_mspeed(real tempi, gmx_mtop_t* mtop, rvec v[], gmx::ThreeFry2x64<>* rng)
+static void low_mspeed(real tempi, gmx_mtop_t* mtop, rvec v[], gmx::ThreeFry2x64<>* rng, const gmx::MDLogger& logger)
 {
     int                                    nrdf;
     real                                   boltz;
@@ -89,7 +91,9 @@ static void low_mspeed(real tempi, gmx_mtop_t* mtop, rvec v[], gmx::ThreeFry2x64
             }
         }
     }
-    fprintf(stderr, "Velocities were taken from a Maxwell distribution at %g K\n", tempi);
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted("Velocities were taken from a Maxwell distribution at %g K", tempi);
     if (debug)
     {
         fprintf(debug,
@@ -99,17 +103,19 @@ static void low_mspeed(real tempi, gmx_mtop_t* mtop, rvec v[], gmx::ThreeFry2x64
     }
 }
 
-void maxwell_speed(real tempi, unsigned int seed, gmx_mtop_t* mtop, rvec v[])
+void maxwell_speed(real tempi, unsigned int seed, gmx_mtop_t* mtop, rvec v[], const gmx::MDLogger& logger)
 {
 
     if (seed == 0)
     {
         seed = static_cast<int>(gmx::makeRandomSeed());
-        fprintf(stderr, "Using random seed %u for generating velocities\n", seed);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Using random seed %u for generating velocities", seed);
     }
     gmx::ThreeFry2x64<> rng(seed, gmx::RandomDomain::MaxwellVelocities);
 
-    low_mspeed(tempi, mtop, v, &rng);
+    low_mspeed(tempi, mtop, v, &rng, logger);
 }
 
 static real calc_cm(int natoms, const real mass[], rvec x[], rvec v[], rvec xcm, rvec vcm, rvec acm, matrix L)
@@ -174,14 +180,14 @@ static real calc_cm(int natoms, const real mass[], rvec x[], rvec v[], rvec xcm,
     return tm;
 }
 
-void stop_cm(FILE gmx_unused* log, int natoms, real mass[], rvec x[], rvec v[])
+void stop_cm(const gmx::MDLogger gmx_unused& logger, int natoms, real mass[], rvec x[], rvec v[])
 {
     rvec   xcm, vcm, acm;
     tensor L;
     int    i, m;
 
 #ifdef DEBUG
-    fprintf(log, "stopping center of mass motion...\n");
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("stopping center of mass motion...");
 #endif
     (void)calc_cm(natoms, mass, x, v, xcm, vcm, acm, L);
 
@@ -193,8 +199,4 @@ void stop_cm(FILE gmx_unused* log, int natoms, real mass[], rvec x[], rvec v[])
             v[i][m] -= vcm[m];
         }
     }
-
-#ifdef DEBUG
-    (void)calc_cm(log, natoms, mass, x, v, xcm, vcm, acm, L);
-#endif
 }
index e339cd797d2e92fec0debbf0194d45402c21aefb..5a31cb4fd4d3ed34052367a15460f6d75f1b07ed 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 struct gmx_mtop_t;
 
+namespace gmx
+{
+class MDLogger;
+}
+
 /*! \brief
  * Generate Maxwellian velocities.
  *
@@ -52,18 +57,19 @@ struct gmx_mtop_t;
  * \param[in] seed  Random number generator seed
  * \param[in] mtop  Molecular Topology
  * \param[out] v    Velocities
+ * \param[in] logger Handle to logging interface.
  */
-void maxwell_speed(real tempi, unsigned int seed, gmx_mtop_t* mtop, rvec v[]);
+void maxwell_speed(real tempi, unsigned int seed, gmx_mtop_t* mtop, rvec v[], const gmx::MDLogger& logger);
 
 /*! \brief
  * Remove the center of mass motion in a set of coordinates.
  *
- * \param[out] log  File for printing debug information
+ * \param[in]  logger Handle to logging interface.
  * \param[in]  natoms Number of atoms
  * \param[in]  mass   Atomic masses
  * \param[in]  x      Coordinates
  * \param[out] v      Velocities
  */
-void stop_cm(FILE* log, int natoms, real mass[], rvec x[], rvec v[]);
+void stop_cm(const gmx::MDLogger& logger, int natoms, real mass[], rvec x[], rvec v[]);
 
 #endif
index c72debf48a92d60dcf7682f57573e58639b6c792..cb0735fce79848b34450c7adf4632a8c07bfc9f4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index a86f4f49267803f80eb88b265c00f09af18fcfb8..2cd20ce9adbfe105ad73e5a5d6de9d450a7c836e 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,8 +38,9 @@
 #ifndef GMX_GMXPREPROCESS_GEN_VSITE_H
 #define GMX_GMXPREPROCESS_GEN_VSITE_H
 
+#include <vector>
+
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/real.h"
 
 class PreprocessingAtomTypes;
@@ -48,6 +49,12 @@ struct InteractionsOfType;
 struct PreprocessResidue;
 struct t_symtab;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /* stuff for pdb2gmx */
 
 void do_vsites(gmx::ArrayRef<const PreprocessResidue> rtpFFDB,
index a2630b7b88a41a77c98c6428ad21f12e62658516..37d6384f6e7847d06df60e54475ec7ae6052da29 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -122,7 +123,7 @@ int gmx_genconf(int argc, char* argv[])
     rvec *            x, *xx, *v; /* coordinates? */
     real              t;
     vec4 *            xrot, *vrot;
-    int               ePBC;
+    PbcType           pbcType;
     matrix            box, boxx; /* box length matrix */
     rvec              shift;
     int               natoms; /* number of atoms in one molecule  */
@@ -177,7 +178,7 @@ int gmx_genconf(int argc, char* argv[])
 
     gmx_mtop_t mtop;
     bool       haveTop = false;
-    readConfAndTopology(opt2fn("-f", NFILE, fnm), &haveTop, &mtop, &ePBC, &x, &v, box);
+    readConfAndTopology(opt2fn("-f", NFILE, fnm), &haveTop, &mtop, &pbcType, &x, &v, box);
     t_atoms atoms = gmx_mtop_global_atoms(&mtop);
     natoms        = atoms.nr;
     nres          = atoms.nres; /* nr of residues in one element? */
@@ -241,7 +242,7 @@ int gmx_genconf(int argc, char* argv[])
                             v[ndx + l][m] = v[l][m];
                         }
                     }
-                    if (ePBC == epbcSCREW && i % 2 == 1)
+                    if (pbcType == PbcType::Screw && i % 2 == 1)
                     {
                         /* Rotate around x axis */
                         for (m = YY; m <= ZZ; m++)
@@ -289,10 +290,10 @@ int gmx_genconf(int argc, char* argv[])
     svmul(nx, box[XX], box[XX]);
     svmul(ny, box[YY], box[YY]);
     svmul(nz, box[ZZ], box[ZZ]);
-    if (ePBC == epbcSCREW && nx % 2 == 0)
+    if (pbcType == PbcType::Screw && nx % 2 == 0)
     {
         /* With an even number of boxes in x we can forgot about the screw */
-        ePBC = epbcXYZ;
+        pbcType = PbcType::Xyz;
     }
 
     /*depending on how you look at it, this is either a nasty hack or the way it should work*/
@@ -304,7 +305,7 @@ int gmx_genconf(int argc, char* argv[])
         }
     }
 
-    write_sto_conf(opt2fn("-o", NFILE, fnm), *mtop.name, &atoms, x, v, ePBC, box);
+    write_sto_conf(opt2fn("-o", NFILE, fnm), *mtop.name, &atoms, x, v, pbcType, box);
 
     sfree(x);
     sfree(v);
index a7c451fb2932deadd07360c5b5c948c9a1044ea1..3193d5709b17c3bbe972f36ea5cf4aff34905e81 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GMXPREPROCESS_GENHYDRO_H
 #define GMX_GMXPREPROCESS_GENHYDRO_H
 
+#include <vector>
+
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 
 struct t_atoms;
 struct t_symtab;
 struct MoleculePatchDatabase;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /*! \brief
  * Generate hydrogen atoms and N and C terminal patches.
  *
index bb472e7dc0e7fedf1b179f1f1ca951b2a0e75390..487ffa0c8feb23ab488125a7ac2e994cf4cf7d60 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,7 +50,6 @@
 #include "gromacs/commandline/pargs.h"
 #include "gromacs/fileio/confio.h"
 #include "gromacs/math/units.h"
-#include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/mdlib/force.h"
 #include "gromacs/pbcutil/pbc.h"
@@ -480,7 +480,8 @@ int gmx_genion(int argc, char* argv[])
     matrix            box;
     t_atoms           atoms;
     t_pbc             pbc;
-    int *             repl, ePBC;
+    int*              repl;
+    PbcType           pbcType;
     int               nw, nsa, nsalt, iqtot;
     gmx_output_env_t* oenv  = nullptr;
     t_filenm          fnm[] = { { efTPR, nullptr, nullptr, ffREAD },
@@ -511,7 +512,7 @@ int gmx_genion(int argc, char* argv[])
     }
 
     /* Read atom positions and charges */
-    read_tps_conf(ftp2fn(efTPR, NFILE, fnm), &top, &ePBC, &x, nullptr, box, FALSE);
+    read_tps_conf(ftp2fn(efTPR, NFILE, fnm), &top, &pbcType, &x, nullptr, box, FALSE);
     atoms = top.atoms;
 
     /* Compute total charge */
@@ -537,7 +538,7 @@ int gmx_genion(int argc, char* argv[])
 
         /* Check if the system is neutralizable
          * is (qdelta == p_q*p_num + n_q*n_num) solvable for p_num and n_num? */
-        int gcd = gmx_greatest_common_divisor(n_q, p_q);
+        int gcd = std::gcd(n_q, p_q);
         if ((qdelta % gcd) != 0)
         {
             gmx_fatal(FARGS,
@@ -620,7 +621,7 @@ int gmx_genion(int argc, char* argv[])
         }
 
         snew(repl, nw);
-        set_pbc(&pbc, ePBC, box);
+        set_pbc(&pbc, pbcType, box);
 
 
         if (seed == 0)
@@ -666,7 +667,7 @@ int gmx_genion(int argc, char* argv[])
 
     sfree(atoms.pdbinfo);
     atoms.pdbinfo = nullptr;
-    write_sto_conf(ftp2fn(efSTO, NFILE, fnm), *top.name, &atoms, x, nullptr, ePBC, box);
+    write_sto_conf(ftp2fn(efSTO, NFILE, fnm), *top.name, &atoms, x, nullptr, pbcType, box);
 
     sfree(pptr);
     sfree(paptr);
index 74515a7638672e02abc486d77f21384390f92b92..7c488ab20e62192e7c0d7bc5c5d6baee6274eaa3 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index eaebc356dd76ff61532369fa0ee0a6b98e04fb72..c359c7a450851cbabcafe7686df08df1b98075ce 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,8 +49,8 @@
 
 #include <algorithm>
 #include <memory>
-
 #include <unordered_set>
+
 #include <sys/types.h>
 
 #include "gromacs/utility/arrayref.h"
index d5b65c5c35b460d33846ab10accbe64acb13482a..46376534a0941c3609fe59e83bfc014311793cd0 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 2ced59ad2358cc6d71a18cd9b7efe7b331650ac8..8b6c7f08de3f3b1cce52c871fd2a102846b56b2c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,7 +48,8 @@
 
 #include <cstdio>
 
-#include "gromacs/utility/arrayref.h"
+#include <string>
+
 #include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/real.h"
 
@@ -59,6 +60,12 @@ class InteractionOfType;
 struct InteractionsOfType;
 struct t_symtab;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /*! \libinternal \brief
  * Storage of all atom types used during preprocessing of a simulation
  * input.
index 4d083506b75aa6834324e5cd542a62843aa6cfcd..395bf76067b574131085025d86cf6ffc7cbb6f8b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 19aa460d27637b7f6bf294e815b7d27a392f8fb9..e4b360bd1f4aadfbfe88a97396d107138540be03 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -172,14 +172,14 @@ void __print_nnb(t_nextnb* nnb, char* s)
 }
 #endif
 
-static void nnb2excl(t_nextnb* nnb, t_blocka* excl)
+static void nnb2excl(t_nextnb* nnb, gmx::ListOfLists<int>* excls)
 {
     int       i, j, j_index;
     int       nre, nrx, nrs, nr_of_sortables;
     sortable* s;
 
-    srenew(excl->index, nnb->nr + 1);
-    excl->index[0] = 0;
+    excls->clear();
+
     for (i = 0; (i < nnb->nr); i++)
     {
         /* calculate the total number of exclusions for atom i */
@@ -230,16 +230,13 @@ static void nnb2excl(t_nextnb* nnb, t_blocka* excl)
         nr_of_sortables = j_index;
         prints("after rm-double", j_index, s);
 
-        /* make space for arrays */
-        srenew(excl->a, excl->nra + nr_of_sortables);
-
         /* put the sorted exclusions in the target list */
+        excls->pushBackListOfSize(nr_of_sortables);
+        gmx::ArrayRef<int> exclusionsForAtom = excls->back();
         for (nrs = 0; (nrs < nr_of_sortables); nrs++)
         {
-            excl->a[excl->nra + nrs] = s[nrs].aj;
+            exclusionsForAtom[nrs] = s[nrs].aj;
         }
-        excl->nra += nr_of_sortables;
-        excl->index[i + 1] = excl->nra;
 
         /* cleanup temporary space */
         sfree(s);
@@ -402,8 +399,10 @@ static void sort_and_purge_nnb(t_nextnb* nnb)
         for (n = 0; (n <= nnb->nrex); n++)
         {
             /* Sort atoms in this list */
-            qsort(nnb->a[i][n], nnb->nrexcl[i][n], sizeof(int), compare_int);
-
+            if (nnb->nrexcl[i][n] > 0)
+            {
+                qsort(nnb->a[i][n], nnb->nrexcl[i][n], sizeof(int), compare_int);
+            }
             cnt  = 0;
             prev = -1;
             for (j = 0; j < nnb->nrexcl[i][n]; j++)
@@ -432,7 +431,7 @@ static void sort_and_purge_nnb(t_nextnb* nnb)
 }
 
 
-void generate_excl(int nrexcl, int nratoms, gmx::ArrayRef<InteractionsOfType> plist, t_blocka* excl)
+void generate_excl(int nrexcl, int nratoms, gmx::ArrayRef<InteractionsOfType> plist, gmx::ListOfLists<int>* excls)
 {
     t_nextnb nnb;
     if (nrexcl < 0)
@@ -441,8 +440,7 @@ void generate_excl(int nrexcl, int nratoms, gmx::ArrayRef<InteractionsOfType> pl
     }
     init_nnb(&nnb, nratoms, nrexcl);
     gen_nnb(&nnb, plist);
-    excl->nr = nratoms;
     sort_and_purge_nnb(&nnb);
-    nnb2excl(&nnb, excl);
+    nnb2excl(&nnb, excls);
     done_nnb(&nnb);
 }
index 4d014cdf97a6afb61f478eb8b7cd902251330212..2788f1853bc40e45eba40b27734d8f34467eca0c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GMXPREPROCESS_GPP_NEXTNB_H
 #define GMX_GMXPREPROCESS_GPP_NEXTNB_H
 
-#include "gromacs/utility/arrayref.h"
-
-struct t_blocka;
 struct InteractionsOfType;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+template<typename>
+class ListOfLists;
+} // namespace gmx
+
 struct t_nextnb
 {
     int nr;   /* nr atoms (0 <= i < nr) (atoms->nr)            */
@@ -75,7 +80,7 @@ void gen_nnb(t_nextnb* nnb, gmx::ArrayRef<InteractionsOfType> plist);
  * initiated using init_nnb.
  */
 
-void generate_excl(int nrexcl, int nratoms, gmx::ArrayRef<InteractionsOfType> plist, t_blocka* excl);
+void generate_excl(int nrexcl, int nratoms, gmx::ArrayRef<InteractionsOfType> plist, gmx::ListOfLists<int>* excls);
 /* Generate an exclusion block from bonds and constraints in
  * plist.
  */
index 0e68f1bd94b6ffca06ccb7b44a0c1761085f9dc8..6a20af125b0a1626c976586d1df61cec36014e69 100644 (file)
@@ -50,7 +50,7 @@
 
 #include <sys/types.h>
 
-#include "gromacs/awh/read_params.h"
+#include "gromacs/applied_forces/awh/read_params.h"
 #include "gromacs/commandline/pargs.h"
 #include "gromacs/ewald/ewald_utils.h"
 #include "gromacs/ewald/pme.h"
@@ -80,7 +80,6 @@
 #include "gromacs/mdlib/compute_io.h"
 #include "gromacs/mdlib/constr.h"
 #include "gromacs/mdlib/perf_est.h"
-#include "gromacs/mdlib/qmmm.h"
 #include "gromacs/mdlib/vsite.h"
 #include "gromacs/mdrun/mdmodules.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/filestream.h"
 #include "gromacs/utility/futil.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/keyvaluetreebuilder.h"
+#include "gromacs/utility/listoflists.h"
+#include "gromacs/utility/logger.h"
+#include "gromacs/utility/loggerbuilder.h"
 #include "gromacs/utility/mdmodulenotification.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/snprintf.h"
@@ -235,7 +238,7 @@ void InteractionOfType::setForceParameter(int pos, real value)
 void MoleculeInformation::initMolInfo()
 {
     init_block(&mols);
-    init_blocka(&excls);
+    excls.clear();
     init_t_atoms(&atoms, 0, FALSE);
 }
 
@@ -262,11 +265,16 @@ static int rm_interactions(int ifunc, gmx::ArrayRef<MoleculeInformation> mols)
     return n;
 }
 
-static int check_atom_names(const char* fn1, const char* fn2, gmx_mtop_t* mtop, const t_atoms* at)
+static int check_atom_names(const char*          fn1,
+                            const char*          fn2,
+                            gmx_mtop_t*          mtop,
+                            const t_atoms*       at,
+                            const gmx::MDLogger& logger)
 {
     int      m, i, j, nmismatch;
     t_atoms* tat;
-#define MAXMISMATCH 20
+
+    constexpr int c_maxNumberOfMismatches = 20;
 
     if (mtop->natoms != at->nr)
     {
@@ -284,15 +292,20 @@ static int check_atom_names(const char* fn1, const char* fn2, gmx_mtop_t* mtop,
             {
                 if (strcmp(*(tat->atomname[j]), *(at->atomname[i])) != 0)
                 {
-                    if (nmismatch < MAXMISMATCH)
+                    if (nmismatch < c_maxNumberOfMismatches)
                     {
-                        fprintf(stderr,
-                                "Warning: atom name %d in %s and %s does not match (%s - %s)\n",
-                                i + 1, fn1, fn2, *(tat->atomname[j]), *(at->atomname[i]));
+                        GMX_LOG(logger.warning)
+                                .asParagraph()
+                                .appendTextFormatted(
+                                        "atom name %d in %s and %s does not match (%s - %s)", i + 1,
+                                        fn1, fn2, *(tat->atomname[j]), *(at->atomname[i]));
                     }
-                    else if (nmismatch == MAXMISMATCH)
+                    else if (nmismatch == c_maxNumberOfMismatches)
                     {
-                        fprintf(stderr, "(more than %d non-matching atom names)\n", MAXMISMATCH);
+                        GMX_LOG(logger.warning)
+                                .asParagraph()
+                                .appendTextFormatted("(more than %d non-matching atom names)",
+                                                     c_maxNumberOfMismatches);
                     }
                     nmismatch++;
                 }
@@ -330,7 +343,6 @@ static void check_bonds_timestep(const gmx_mtop_t* mtop, double dt, warninp* wi)
     int  i, a1, a2, w_a1, w_a2, j;
     real twopi2, limit2, fc, re, m1, m2, period2, w_period2;
     bool bFound, bWater, bWarn;
-    char warn_buf[STRLEN];
 
     /* Get the interaction parameters */
     gmx::ArrayRef<const t_iparams> ip = mtop->ffparams.iparams;
@@ -419,7 +431,7 @@ static void check_bonds_timestep(const gmx_mtop_t* mtop, double dt, warninp* wi)
         bWarn = (w_period2 < gmx::square(min_steps_warn * dt));
         /* A check that would recognize most water models */
         bWater = ((*w_moltype->atoms.atomname[0])[0] == 'O' && w_moltype->atoms.nr <= 5);
-        sprintf(warn_buf,
+        std::string warningMessage = gmx::formatString(
                 "The bond in molecule-type %s between atoms %d %s and %d %s has an estimated "
                 "oscillational period of %.1e ps, which is less than %d times the time step of "
                 "%.1e ps.\n"
@@ -431,11 +443,11 @@ static void check_bonds_timestep(const gmx_mtop_t* mtop, double dt, warninp* wi)
                        : "Maybe you forgot to change the constraints mdp option.");
         if (bWarn)
         {
-            warning(wi, warn_buf);
+            warning(wi, warningMessage.c_str());
         }
         else
         {
-            warning_note(wi, warn_buf);
+            warning_note(wi, warningMessage.c_str());
         }
     }
 }
@@ -455,8 +467,7 @@ static void check_vel(gmx_mtop_t* mtop, rvec v[])
 
 static void check_shells_inputrec(gmx_mtop_t* mtop, t_inputrec* ir, warninp* wi)
 {
-    int  nshells = 0;
-    char warn_buf[STRLEN];
+    int nshells = 0;
 
     for (const AtomProxy atomP : AtomRange(*mtop))
     {
@@ -469,10 +480,10 @@ static void check_shells_inputrec(gmx_mtop_t* mtop, t_inputrec* ir, warninp* wi)
     if ((nshells > 0) && (ir->nstcalcenergy != 1))
     {
         set_warning_line(wi, "unknown", -1);
-        snprintf(warn_buf, STRLEN, "There are %d shells, changing nstcalcenergy from %d to 1",
-                 nshells, ir->nstcalcenergy);
+        std::string warningMessage = gmx::formatString(
+                "There are %d shells, changing nstcalcenergy from %d to 1", nshells, ir->nstcalcenergy);
         ir->nstcalcenergy = 1;
-        warning(wi, warn_buf);
+        warning(wi, warningMessage.c_str());
     }
 }
 
@@ -569,18 +580,17 @@ static void new_status(const char*                           topfile,
                        double*                               reppow,
                        real*                                 fudgeQQ,
                        gmx_bool                              bMorse,
-                       warninp*                              wi)
+                       warninp*                              wi,
+                       const gmx::MDLogger&                  logger)
 {
     std::vector<gmx_molblock_t> molblock;
     int                         i, nmismatch;
     bool                        ffParametrizedWithHBondConstraints;
-    char                        buf[STRLEN];
-    char                        warn_buf[STRLEN];
 
     /* TOPOLOGY processing */
     sys->name = do_top(bVerbose, topfile, topppfile, opts, bZero, &(sys->symtab), interactions,
                        comb, reppow, fudgeQQ, atypes, mi, intermolecular_interactions, ir,
-                       &molblock, &ffParametrizedWithHBondConstraints, wi);
+                       &molblock, &ffParametrizedWithHBondConstraints, wi, logger);
 
     sys->molblock.clear();
 
@@ -617,8 +627,9 @@ static void new_status(const char*                           topfile,
         if (i > 0)
         {
             set_warning_line(wi, "unknown", -1);
-            sprintf(warn_buf, "disre = no, removed %d distance restraints", i);
-            warning_note(wi, warn_buf);
+            std::string warningMessage =
+                    gmx::formatString("disre = no, removed %d distance restraints", i);
+            warning_note(wi, warningMessage.c_str());
         }
     }
     if (!opts->bOrire)
@@ -627,15 +638,16 @@ static void new_status(const char*                           topfile,
         if (i > 0)
         {
             set_warning_line(wi, "unknown", -1);
-            sprintf(warn_buf, "orire = no, removed %d orientation restraints", i);
-            warning_note(wi, warn_buf);
+            std::string warningMessage =
+                    gmx::formatString("orire = no, removed %d orientation restraints", i);
+            warning_note(wi, warningMessage.c_str());
         }
     }
 
     /* Copy structures from msys to sys */
     molinfo2mtop(*mi, sys);
 
-    gmx_mtop_finalize(sys);
+    sys->finalize();
 
     /* Discourage using the, previously common, setup of applying constraints
      * to all bonds with force fields that have been parametrized with H-bond
@@ -655,7 +667,7 @@ static void new_status(const char*                           topfile,
     /* COORDINATE file processing */
     if (bVerbose)
     {
-        fprintf(stderr, "processing coordinates...\n");
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("processing coordinates...");
     }
 
     t_topology* conftop;
@@ -690,24 +702,26 @@ static void new_status(const char*                           topfile,
     /* This call fixes the box shape for runs with pressure scaling */
     set_box_rel(ir, state);
 
-    nmismatch = check_atom_names(topfile, confin, sys, &conftop->atoms);
+    nmismatch = check_atom_names(topfile, confin, sys, &conftop->atoms, logger);
     done_top(conftop);
     sfree(conftop);
 
     if (nmismatch)
     {
-        sprintf(buf,
+        std::string warningMessage = gmx::formatString(
                 "%d non-matching atom name%s\n"
                 "atom names from %s will be used\n"
                 "atom names from %s will be ignored\n",
                 nmismatch, (nmismatch == 1) ? "" : "s", topfile, confin);
-        warning(wi, buf);
+        warning(wi, warningMessage.c_str());
     }
 
     /* Do more checks, mostly related to constraints */
     if (bVerbose)
     {
-        fprintf(stderr, "double-checking input for internal consistency...\n");
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("double-checking input for internal consistency...");
     }
     {
         bool bHasNormalConstraints =
@@ -731,12 +745,12 @@ static void new_status(const char*                           topfile,
         if (opts->seed == -1)
         {
             opts->seed = static_cast<int>(gmx::makeRandomSeed());
-            fprintf(stderr, "Setting gen_seed to %d\n", opts->seed);
+            GMX_LOG(logger.info).asParagraph().appendTextFormatted("Setting gen_seed to %d", opts->seed);
         }
         state->flags |= (1 << estV);
-        maxwell_speed(opts->tempi, opts->seed, sys, state->v.rvec_array());
+        maxwell_speed(opts->tempi, opts->seed, sys, state->v.rvec_array(), logger);
 
-        stop_cm(stdout, state->natoms, mass, state->x.rvec_array(), state->v.rvec_array());
+        stop_cm(logger, state->natoms, mass, state->x.rvec_array(), state->v.rvec_array());
         sfree(mass);
     }
 }
@@ -777,7 +791,8 @@ static void cont_status(const char*             slog,
                         t_inputrec*             ir,
                         t_state*                state,
                         gmx_mtop_t*             sys,
-                        const gmx_output_env_t* oenv)
+                        const gmx_output_env_t* oenv,
+                        const gmx::MDLogger&    logger)
 /* If fr_time == -1 read the last frame available which is complete */
 {
     bool         bReadVel;
@@ -787,23 +802,27 @@ static void cont_status(const char*             slog,
 
     bReadVel = (bNeedVel && !bGenVel);
 
-    fprintf(stderr, "Reading Coordinates%s and Box size from old trajectory\n",
-            bReadVel ? ", Velocities" : "");
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted("Reading Coordinates%s and Box size from old trajectory",
+                                 bReadVel ? ", Velocities" : "");
     if (fr_time == -1)
     {
-        fprintf(stderr, "Will read whole trajectory\n");
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("Will read whole trajectory");
     }
     else
     {
-        fprintf(stderr, "Will read till time %g\n", fr_time);
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("Will read till time %g", fr_time);
     }
     if (!bReadVel)
     {
         if (bGenVel)
         {
-            fprintf(stderr,
-                    "Velocities generated: "
-                    "ignoring velocities in input trajectory\n");
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted(
+                            "Velocities generated: "
+                            "ignoring velocities in input trajectory");
         }
         read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X);
     }
@@ -813,11 +832,12 @@ static void cont_status(const char*             slog,
 
         if (!fr.bV)
         {
-            fprintf(stderr,
-                    "\n"
-                    "WARNING: Did not find a frame with velocities in file %s,\n"
-                    "         all velocities will be set to zero!\n\n",
-                    slog);
+            GMX_LOG(logger.warning)
+                    .asParagraph()
+                    .appendTextFormatted(
+                            "WARNING: Did not find a frame with velocities in file %s,\n"
+                            "         all velocities will be set to zero!",
+                            slog);
             for (auto& vi : makeArrayRef(state->v))
             {
                 vi = { 0, 0, 0 };
@@ -853,8 +873,8 @@ static void cont_status(const char*             slog,
      */
     set_box_rel(ir, state);
 
-    fprintf(stderr, "Using frame at t = %g ps\n", use_time);
-    fprintf(stderr, "Starting time for run is %g ps\n", ir->init_t);
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Using frame at t = %g ps", use_time);
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Starting time for run is %g ps", ir->init_t);
 
     if ((ir->epc != epcNO || ir->etc == etcNOSEHOOVER) && ener)
     {
@@ -868,9 +888,10 @@ static void read_posres(gmx_mtop_t*                              mtop,
                         gmx_bool                                 bTopB,
                         const char*                              fn,
                         int                                      rc_scaling,
-                        int                                      ePBC,
+                        PbcType                                  pbcType,
                         rvec                                     com,
-                        warninp*                                 wi)
+                        warninp*                                 wi,
+                        const gmx::MDLogger&                     logger)
 {
     gmx_bool*   hadAtom;
     rvec *      x, *v;
@@ -879,7 +900,6 @@ static void read_posres(gmx_mtop_t*                              mtop,
     t_topology* top;
     matrix      box, invbox;
     int         natoms, npbcdim = 0;
-    char        warn_buf[STRLEN];
     int         a, nat_molb;
     t_atom*     atom;
 
@@ -890,14 +910,14 @@ static void read_posres(gmx_mtop_t*                              mtop,
     sfree(top);
     if (natoms != mtop->natoms)
     {
-        sprintf(warn_buf,
+        std::string warningMessage = gmx::formatString(
                 "The number of atoms in %s (%d) does not match the number of atoms in the topology "
                 "(%d). Will assume that the first %d atoms in the topology and %s match.",
                 fn, natoms, mtop->natoms, std::min(mtop->natoms, natoms), fn);
-        warning(wi, warn_buf);
+        warning(wi, warningMessage.c_str());
     }
 
-    npbcdim = ePBC2npbcdim(ePBC);
+    npbcdim = numPbcDimensions(pbcType);
     GMX_RELEASE_ASSERT(npbcdim <= DIM, "Invalid npbcdim");
     clear_rvec(com);
     if (rc_scaling != erscNO)
@@ -995,9 +1015,11 @@ static void read_posres(gmx_mtop_t*                              mtop,
         {
             com[j] = sum[j] / totmass;
         }
-        fprintf(stderr,
-                "The center of mass of the position restraint coord's is %6.3f %6.3f %6.3f\n",
-                com[XX], com[YY], com[ZZ]);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted(
+                        "The center of mass of the position restraint coord's is %6.3f %6.3f %6.3f",
+                        com[XX], com[YY], com[ZZ]);
     }
 
     if (rc_scaling != erscNO)
@@ -1057,34 +1079,39 @@ static void gen_posres(gmx_mtop_t*                              mtop,
                        const char*                              fnA,
                        const char*                              fnB,
                        int                                      rc_scaling,
-                       int                                      ePBC,
+                       PbcType                                  pbcType,
                        rvec                                     com,
                        rvec                                     comB,
-                       warninp*                                 wi)
+                       warninp*                                 wi,
+                       const gmx::MDLogger&                     logger)
 {
-    read_posres(mtop, mi, FALSE, fnA, rc_scaling, ePBC, com, wi);
+    read_posres(mtop, mi, FALSE, fnA, rc_scaling, pbcType, com, wi, logger);
     /* It is safer to simply read the b-state posres rather than trying
      * to be smart and copy the positions.
      */
-    read_posres(mtop, mi, TRUE, fnB, rc_scaling, ePBC, comB, wi);
+    read_posres(mtop, mi, TRUE, fnB, rc_scaling, pbcType, comB, wi, logger);
 }
 
-static void set_wall_atomtype(PreprocessingAtomTypes* at, t_gromppopts* opts, t_inputrec* ir, warninp* wi)
+static void set_wall_atomtype(PreprocessingAtomTypes* at,
+                              t_gromppopts*           opts,
+                              t_inputrec*             ir,
+                              warninp*                wi,
+                              const gmx::MDLogger&    logger)
 {
-    int  i;
-    char warn_buf[STRLEN];
+    int i;
 
     if (ir->nwall > 0)
     {
-        fprintf(stderr, "Searching the wall atom type(s)\n");
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("Searching the wall atom type(s)");
     }
     for (i = 0; i < ir->nwall; i++)
     {
         ir->wall_atomtype[i] = at->atomTypeFromName(opts->wall_atomtype[i]);
         if (ir->wall_atomtype[i] == NOTSET)
         {
-            sprintf(warn_buf, "Specified wall atom type %s is not defined", opts->wall_atomtype[i]);
-            warning_error(wi, warn_buf);
+            std::string warningMessage = gmx::formatString(
+                    "Specified wall atom type %s is not defined", opts->wall_atomtype[i]);
+            warning_error(wi, warningMessage.c_str());
         }
     }
 }
@@ -1104,7 +1131,7 @@ static int nrdf_internal(const t_atoms* atoms)
     }
     switch (nmass)
     {
-        case 0: nrdf = 0; break;
+        case 0: // Fall through intended
         case 1: nrdf = 0; break;
         case 2: nrdf = 1; break;
         default: nrdf = nmass * 3 - 6; break;
@@ -1247,8 +1274,7 @@ static void init_cmap_grid(gmx_cmap_t* cmap_grid, int ngrid, int grid_spacing)
 
 static int count_constraints(const gmx_mtop_t* mtop, gmx::ArrayRef<const MoleculeInformation> mi, warninp* wi)
 {
-    int  count, count_mol;
-    char buf[STRLEN];
+    int count, count_mol;
 
     count = 0;
     for (const gmx_molblock_t& molb : mtop->molblock)
@@ -1270,12 +1296,12 @@ static int count_constraints(const gmx_mtop_t* mtop, gmx::ArrayRef<const Molecul
 
         if (count_mol > nrdf_internal(&mi[molb.type].atoms))
         {
-            sprintf(buf,
+            std::string warningMessage = gmx::formatString(
                     "Molecule type '%s' has %d constraints.\n"
                     "For stability and efficiency there should not be more constraints than "
                     "internal number of degrees of freedom: %d.\n",
                     *mi[molb.type].name, count_mol, nrdf_internal(&mi[molb.type].atoms));
-            warning(wi, buf);
+            warning(wi, warningMessage.c_str());
         }
         count += molb.nmol * count_mol;
     }
@@ -1324,14 +1350,12 @@ static real get_max_reference_temp(const t_inputrec* ir, warninp* wi)
 
     if (bNoCoupl)
     {
-        char buf[STRLEN];
-
-        sprintf(buf,
+        std::string warningMessage = gmx::formatString(
                 "Some temperature coupling groups do not use temperature coupling. We will assume "
                 "their temperature is not more than %.3f K. If their temperature is higher, the "
                 "energy error and the Verlet buffer might be underestimated.",
                 ref_t);
-        warning(wi, buf);
+        warning(wi, warningMessage.c_str());
     }
 
     return ref_t;
@@ -1340,7 +1364,7 @@ static real get_max_reference_temp(const t_inputrec* ir, warninp* wi)
 /* Checks if there are unbound atoms in moleculetype molt.
  * Prints a note for each unbound atoms and a warning if any is present.
  */
-static void checkForUnboundAtoms(const gmx_moltype_t* molt, gmx_bool bVerbose, warninp* wi)
+static void checkForUnboundAtoms(const gmx_moltype_t* molt, gmx_bool bVerbose, warninp* wi, const gmx::MDLogger& logger)
 {
     const t_atoms* atoms = &molt->atoms;
 
@@ -1377,10 +1401,12 @@ static void checkForUnboundAtoms(const gmx_moltype_t* molt, gmx_bool bVerbose, w
         {
             if (bVerbose)
             {
-                fprintf(stderr,
-                        "\nAtom %d '%s' in moleculetype '%s' is not bound by a potential or "
-                        "constraint to any other atom in the same moleculetype.\n",
-                        a + 1, *atoms->atomname[a], *molt->name);
+                GMX_LOG(logger.warning)
+                        .asParagraph()
+                        .appendTextFormatted(
+                                "Atom %d '%s' in moleculetype '%s' is not bound by a potential or "
+                                "constraint to any other atom in the same moleculetype.",
+                                a + 1, *atoms->atomname[a], *molt->name);
             }
             numDanglingAtoms++;
         }
@@ -1388,24 +1414,23 @@ static void checkForUnboundAtoms(const gmx_moltype_t* molt, gmx_bool bVerbose, w
 
     if (numDanglingAtoms > 0)
     {
-        char buf[STRLEN];
-        sprintf(buf,
+        std::string warningMessage = gmx::formatString(
                 "In moleculetype '%s' %d atoms are not bound by a potential or constraint to any "
                 "other atom in the same moleculetype. Although technically this might not cause "
                 "issues in a simulation, this often means that the user forgot to add a "
                 "bond/potential/constraint or put multiple molecules in the same moleculetype "
                 "definition by mistake. Run with -v to get information for each atom.",
                 *molt->name, numDanglingAtoms);
-        warning_note(wi, buf);
+        warning_note(wi, warningMessage.c_str());
     }
 }
 
 /* Checks all moleculetypes for unbound atoms */
-static void checkForUnboundAtoms(const gmx_mtop_t* mtop, gmx_bool bVerbose, warninp* wi)
+static void checkForUnboundAtoms(const gmx_mtop_t* mtop, gmx_bool bVerbose, warninp* wi, const gmx::MDLogger& logger)
 {
     for (const gmx_moltype_t& molt : mtop->moltype)
     {
-        checkForUnboundAtoms(&molt, bVerbose, wi);
+        checkForUnboundAtoms(&molt, bVerbose, wi, logger);
     }
 }
 
@@ -1420,14 +1445,14 @@ static bool haveDecoupledModeInMol(const gmx_moltype_t&           molt,
                                    gmx::ArrayRef<const t_iparams> iparams,
                                    real                           massFactorThreshold)
 {
-    if (molt.ilist[F_CONSTR].size() == 0 && molt.ilist[F_CONSTRNC].size() == 0)
+    if (molt.ilist[F_CONSTR].empty() && molt.ilist[F_CONSTRNC].empty())
     {
         return false;
     }
 
     const t_atom* atom = molt.atoms.atom;
 
-    t_blocka atomToConstraints =
+    const auto atomToConstraints =
             gmx::make_at2con(molt, iparams, gmx::FlexibleConstraintTreatment::Exclude);
 
     bool haveDecoupledMode = false;
@@ -1457,23 +1482,21 @@ static bool haveDecoupledModeInMol(const gmx_moltype_t&           molt,
                 int a1 = il.iatoms[1 + i + 1];
                 int a2 = il.iatoms[1 + i + 2];
                 if ((atom[a0].m > atom[a2].m * massFactorThreshold || atom[a2].m > atom[a0].m * massFactorThreshold)
-                    && atomToConstraints.index[a0 + 1] - atomToConstraints.index[a0] == 1
-                    && atomToConstraints.index[a2 + 1] - atomToConstraints.index[a2] == 1
-                    && atomToConstraints.index[a1 + 1] - atomToConstraints.index[a1] >= 3)
+                    && atomToConstraints[a0].ssize() == 1 && atomToConstraints[a2].ssize() == 1
+                    && atomToConstraints[a1].ssize() >= 3)
                 {
-                    int constraint0 = atomToConstraints.a[atomToConstraints.index[a0]];
-                    int constraint2 = atomToConstraints.a[atomToConstraints.index[a2]];
+                    int constraint0 = atomToConstraints[a0][0];
+                    int constraint2 = atomToConstraints[a2][0];
 
                     bool foundAtom0 = false;
                     bool foundAtom2 = false;
-                    for (int conIndex = atomToConstraints.index[a1];
-                         conIndex < atomToConstraints.index[a1 + 1]; conIndex++)
+                    for (const int constraint : atomToConstraints[a1])
                     {
-                        if (atomToConstraints.a[conIndex] == constraint0)
+                        if (constraint == constraint0)
                         {
                             foundAtom0 = true;
                         }
-                        if (atomToConstraints.a[conIndex] == constraint2)
+                        if (constraint == constraint2)
                         {
                             foundAtom2 = true;
                         }
@@ -1487,8 +1510,6 @@ static bool haveDecoupledModeInMol(const gmx_moltype_t&           molt,
         }
     }
 
-    done_blocka(&atomToConstraints);
-
     return haveDecoupledMode;
 }
 
@@ -1577,12 +1598,18 @@ static void checkDecoupledModeAccuracy(const gmx_mtop_t* mtop, const t_inputrec*
     }
 }
 
-static void set_verlet_buffer(const gmx_mtop_t* mtop, t_inputrec* ir, real buffer_temp, matrix box, warninp* wi)
+static void set_verlet_buffer(const gmx_mtop_t*    mtop,
+                              t_inputrec*          ir,
+                              real                 buffer_temp,
+                              matrix               box,
+                              warninp*             wi,
+                              const gmx::MDLogger& logger)
 {
-    char warn_buf[STRLEN];
-
-    printf("Determining Verlet buffer for a tolerance of %g kJ/mol/ps at %g K\n", ir->verletbuf_tol,
-           buffer_temp);
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted(
+                    "Determining Verlet buffer for a tolerance of %g kJ/mol/ps at %g K",
+                    ir->verletbuf_tol, buffer_temp);
 
     /* Calculate the buffer size for simple atom vs atoms list */
     VerletbufListSetup listSetup1x1;
@@ -1596,33 +1623,42 @@ static void set_verlet_buffer(const gmx_mtop_t* mtop, t_inputrec* ir, real buffe
     ir->rlist = calcVerletBufferSize(*mtop, det(box), *ir, ir->nstlist, ir->nstlist - 1,
                                      buffer_temp, listSetup4x4);
 
-    const int n_nonlin_vsite = countNonlinearVsites(*mtop);
+    const int n_nonlin_vsite = gmx::countNonlinearVsites(*mtop);
     if (n_nonlin_vsite > 0)
     {
-        sprintf(warn_buf,
+        std::string warningMessage = gmx::formatString(
                 "There are %d non-linear virtual site constructions. Their contribution to the "
                 "energy error is approximated. In most cases this does not affect the error "
                 "significantly.",
                 n_nonlin_vsite);
-        warning_note(wi, warn_buf);
+        warning_note(wi, warningMessage);
     }
 
-    printf("Calculated rlist for %dx%d atom pair-list as %.3f nm, buffer size %.3f nm\n", 1, 1,
-           rlist_1x1, rlist_1x1 - std::max(ir->rvdw, ir->rcoulomb));
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted(
+                    "Calculated rlist for %dx%d atom pair-list as %.3f nm, buffer size %.3f nm", 1,
+                    1, rlist_1x1, rlist_1x1 - std::max(ir->rvdw, ir->rcoulomb));
 
-    printf("Set rlist, assuming %dx%d atom pair-list, to %.3f nm, buffer size %.3f nm\n",
-           listSetup4x4.cluster_size_i, listSetup4x4.cluster_size_j, ir->rlist,
-           ir->rlist - std::max(ir->rvdw, ir->rcoulomb));
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted(
+                    "Set rlist, assuming %dx%d atom pair-list, to %.3f nm, buffer size %.3f nm",
+                    listSetup4x4.cluster_size_i, listSetup4x4.cluster_size_j, ir->rlist,
+                    ir->rlist - std::max(ir->rvdw, ir->rcoulomb));
 
-    printf("Note that mdrun will redetermine rlist based on the actual pair-list setup\n");
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted(
+                    "Note that mdrun will redetermine rlist based on the actual pair-list setup");
 
-    if (gmx::square(ir->rlist) >= max_cutoff2(ir->ePBC, box))
+    if (gmx::square(ir->rlist) >= max_cutoff2(ir->pbcType, box))
     {
         gmx_fatal(FARGS,
                   "The pair-list cut-off (%g nm) is longer than half the shortest box vector or "
                   "longer than the smallest box diagonal element (%g nm). Increase the box size or "
                   "decrease nstlist or increase verlet-buffer-tolerance.",
-                  ir->rlist, std::sqrt(max_cutoff2(ir->ePBC, box)));
+                  ir->rlist, std::sqrt(max_cutoff2(ir->pbcType, box)));
     }
 }
 
@@ -1726,7 +1762,6 @@ int gmx_grompp(int argc, char* argv[])
         "interpret the output messages before attempting to bypass them with",
         "this option."
     };
-    t_gromppopts*                        opts;
     std::vector<MoleculeInformation>     mi;
     std::unique_ptr<MoleculeInformation> intermolecular_interactions;
     int                                  nvsite, comb;
@@ -1734,11 +1769,9 @@ int gmx_grompp(int argc, char* argv[])
     double                               reppow;
     const char*                          mdparin;
     bool                                 bNeedVel, bGenVel;
-    gmx_bool                             have_atomnumber;
     gmx_output_env_t*                    oenv;
     gmx_bool                             bVerbose = FALSE;
     warninp*                             wi;
-    char                                 warn_buf[STRLEN];
 
     t_filenm fnm[] = { { efMDP, nullptr, nullptr, ffREAD },
                        { efMDP, "-po", "mdout", ffWRITE },
@@ -1753,7 +1786,7 @@ int gmx_grompp(int argc, char* argv[])
                        { efEDR, "-e", nullptr, ffOPTRD },
                        /* This group is needed by the VMD viewer as the start configuration for IMD sessions: */
                        { efGRO, "-imd", "imdgroup", ffOPTWR },
-                       { efTRN, "-ref", "rotref", ffOPTRW } };
+                       { efTRN, "-ref", "rotref", ffOPTRW | ffALLOW_MISSING } };
 #define NFILE asize(fnm)
 
     /* Command line options */
@@ -1798,10 +1831,18 @@ int gmx_grompp(int argc, char* argv[])
     gmx::MDModules mdModules;
     t_inputrec     irInstance;
     t_inputrec*    ir = &irInstance;
-    snew(opts, 1);
+    t_gromppopts   optsInstance;
+    t_gromppopts*  opts = &optsInstance;
     snew(opts->include, STRLEN);
     snew(opts->define, STRLEN);
 
+    gmx::LoggerBuilder builder;
+    builder.addTargetStream(gmx::MDLogger::LogLevel::Info, &gmx::TextOutputFile::standardOutput());
+    builder.addTargetStream(gmx::MDLogger::LogLevel::Warning, &gmx::TextOutputFile::standardError());
+    gmx::LoggerOwner    logOwner(builder.build());
+    const gmx::MDLogger logger(logOwner.logger());
+
+
     wi = init_warning(TRUE, maxwarn);
 
     /* PARAMETER file processing */
@@ -1813,33 +1854,44 @@ int gmx_grompp(int argc, char* argv[])
     }
     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
 
+    // Now that the MdModules have their options assigned from get_ir, subscribe
+    // to eventual notifications during pre-processing their data
+    mdModules.subscribeToPreProcessingNotifications();
+
+
     if (bVerbose)
     {
-        fprintf(stderr, "checking input for internal consistency...\n");
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("checking input for internal consistency...");
     }
     check_ir(mdparin, mdModules.notifier(), ir, opts, wi);
 
     if (ir->ld_seed == -1)
     {
         ir->ld_seed = static_cast<int>(gmx::makeRandomSeed());
-        fprintf(stderr, "Setting the LD random seed to %" PRId64 "\n", ir->ld_seed);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Setting the LD random seed to %" PRId64 "", ir->ld_seed);
     }
 
     if (ir->expandedvals->lmc_seed == -1)
     {
         ir->expandedvals->lmc_seed = static_cast<int>(gmx::makeRandomSeed());
-        fprintf(stderr, "Setting the lambda MC random seed to %d\n", ir->expandedvals->lmc_seed);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Setting the lambda MC random seed to %d", ir->expandedvals->lmc_seed);
     }
 
     bNeedVel = EI_STATE_VELOCITY(ir->eI);
     bGenVel  = (bNeedVel && opts->bGenVel);
     if (bGenVel && ir->bContinuation)
     {
-        sprintf(warn_buf,
+        std::string warningMessage = gmx::formatString(
                 "Generating velocities is inconsistent with attempting "
                 "to continue a previous run. Choose only one of "
                 "gen-vel = yes and continuation = yes.");
-        warning_error(wi, warn_buf);
+        warning_error(wi, warningMessage);
     }
 
     std::array<InteractionsOfType, F_NRE> interactions;
@@ -1859,7 +1911,7 @@ int gmx_grompp(int argc, char* argv[])
     t_state state;
     new_status(fn, opt2fn_null("-pp", NFILE, fnm), opt2fn("-c", NFILE, fnm), opts, ir, bZero,
                bGenVel, bVerbose, &state, &atypes, &sys, &mi, &intermolecular_interactions,
-               interactions, &comb, &reppow, &fudgeQQ, opts->bMorse, wi);
+               interactions, &comb, &reppow, &fudgeQQ, opts->bMorse, wi, logger);
 
     if (debug)
     {
@@ -1870,7 +1922,7 @@ int gmx_grompp(int argc, char* argv[])
     /* set parameters for virtual site construction (not for vsiten) */
     for (size_t mt = 0; mt < sys.moltype.size(); mt++)
     {
-        nvsite += set_vsites(bVerbose, &sys.moltype[mt].atoms, &atypes, mi[mt].interactions);
+        nvsite += set_vsites(bVerbose, &sys.moltype[mt].atoms, &atypes, mi[mt].interactions, logger);
     }
     /* now throw away all obsolete bonds, angles and dihedrals: */
     /* note: constraints are ALWAYS removed */
@@ -1878,7 +1930,7 @@ int gmx_grompp(int argc, char* argv[])
     {
         for (size_t mt = 0; mt < sys.moltype.size(); mt++)
         {
-            clean_vsite_bondeds(mi[mt].interactions, sys.moltype[mt].atoms.nr, bRmVSBds);
+            clean_vsite_bondeds(mi[mt].interactions, sys.moltype[mt].atoms.nr, bRmVSBds, logger);
         }
     }
 
@@ -1886,15 +1938,17 @@ int gmx_grompp(int argc, char* argv[])
     {
         if (ir->eI == eiCG || ir->eI == eiLBFGS)
         {
-            sprintf(warn_buf, "Can not do %s with %s, use %s", EI(ir->eI),
-                    econstr_names[econtSHAKE], econstr_names[econtLINCS]);
-            warning_error(wi, warn_buf);
+            std::string warningMessage =
+                    gmx::formatString("Can not do %s with %s, use %s", EI(ir->eI),
+                                      econstr_names[econtSHAKE], econstr_names[econtLINCS]);
+            warning_error(wi, warningMessage);
         }
         if (ir->bPeriodicMols)
         {
-            sprintf(warn_buf, "Can not do periodic molecules with %s, use %s",
-                    econstr_names[econtSHAKE], econstr_names[econtLINCS]);
-            warning_error(wi, warn_buf);
+            std::string warningMessage =
+                    gmx::formatString("Can not do periodic molecules with %s, use %s",
+                                      econstr_names[econtSHAKE], econstr_names[econtLINCS]);
+            warning_error(wi, warningMessage);
         }
     }
 
@@ -1903,25 +1957,6 @@ int gmx_grompp(int argc, char* argv[])
         warning_note(wi, "Temperature coupling is ignored with SD integrators.");
     }
 
-    /* If we are doing QM/MM, check that we got the atom numbers */
-    have_atomnumber = TRUE;
-    for (gmx::index i = 0; i < gmx::ssize(atypes); i++)
-    {
-        have_atomnumber = have_atomnumber && (atypes.atomNumberFromAtomType(i) >= 0);
-    }
-    if (!have_atomnumber && ir->bQMMM)
-    {
-        warning_error(
-                wi,
-                "\n"
-                "It appears as if you are trying to run a QM/MM calculation, but the force\n"
-                "field you are using does not contain atom numbers fields. This is an\n"
-                "optional field (introduced in GROMACS 3.3) for general runs, but mandatory\n"
-                "for QM/MM. The good news is that it is easy to add - put the atom number as\n"
-                "an integer just before the mass column in ffXXXnb.itp.\n"
-                "NB: United atoms have the same atom numbers as normal ones.\n\n");
-    }
-
     /* Check for errors in the input now, since they might cause problems
      * during processing further down.
      */
@@ -1931,12 +1966,12 @@ int gmx_grompp(int argc, char* argv[])
     {
         if (ir->epc == epcPARRINELLORAHMAN || ir->epc == epcMTTK)
         {
-            sprintf(warn_buf,
+            std::string warningMessage = gmx::formatString(
                     "You are combining position restraints with %s pressure coupling, which can "
                     "lead to instabilities. If you really want to combine position restraints with "
                     "pressure coupling, we suggest to use %s pressure coupling instead.",
                     EPCOUPLTYPE(ir->epc), EPCOUPLTYPE(epcBERENDSEN));
-            warning_note(wi, warn_buf);
+            warning_note(wi, warningMessage);
         }
 
         const char* fn = opt2fn("-r", NFILE, fnm);
@@ -1972,17 +2007,15 @@ int gmx_grompp(int argc, char* argv[])
 
         if (bVerbose)
         {
-            fprintf(stderr, "Reading position restraint coords from %s", fn);
-            if (strcmp(fn, fnB) == 0)
-            {
-                fprintf(stderr, "\n");
-            }
-            else
+            std::string message = gmx::formatString("Reading position restraint coords from %s", fn);
+            if (strcmp(fn, fnB) != 0)
             {
-                fprintf(stderr, " and %s\n", fnB);
+                message += gmx::formatString(" and %s", fnB);
             }
+            GMX_LOG(logger.info).asParagraph().appendText(message);
         }
-        gen_posres(&sys, mi, fn, fnB, ir->refcoord_scaling, ir->ePBC, ir->posres_com, ir->posres_comB, wi);
+        gen_posres(&sys, mi, fn, fnB, ir->refcoord_scaling, ir->pbcType, ir->posres_com,
+                   ir->posres_comB, wi, logger);
     }
 
     /* If we are using CMAP, setup the pre-interpolation grid */
@@ -1994,7 +2027,7 @@ int gmx_grompp(int argc, char* argv[])
                    interactions[F_CMAP].cmap, &sys.ffparams.cmap_grid);
     }
 
-    set_wall_atomtype(&atypes, opts, ir, wi);
+    set_wall_atomtype(&atypes, opts, ir, wi, logger);
     if (bRenum)
     {
         atypes.renumberTypes(interactions, &sys, ir->wall_atomtype, bVerbose);
@@ -2015,7 +2048,7 @@ int gmx_grompp(int argc, char* argv[])
 
     if (bVerbose)
     {
-        fprintf(stderr, "converting bonded parameters...\n");
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("converting bonded parameters...");
     }
 
     const int ntype = atypes.size();
@@ -2030,7 +2063,7 @@ int gmx_grompp(int argc, char* argv[])
     /* set ptype to VSite for virtual sites */
     for (gmx_moltype_t& moltype : sys.moltype)
     {
-        set_vsites_ptype(FALSE, &moltype);
+        set_vsites_ptype(FALSE, &moltype, logger);
     }
     if (debug)
     {
@@ -2048,7 +2081,7 @@ int gmx_grompp(int argc, char* argv[])
     /* check masses */
     check_mol(&sys, wi);
 
-    checkForUnboundAtoms(&sys, bVerbose, wi);
+    checkForUnboundAtoms(&sys, bVerbose, wi, logger);
 
     if (EI_DYNAMICS(ir->eI) && ir->eI != eiBD)
     {
@@ -2071,7 +2104,7 @@ int gmx_grompp(int argc, char* argv[])
 
     if (bVerbose)
     {
-        fprintf(stderr, "initialising group options...\n");
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("initialising group options...");
     }
     do_index(mdparin, ftp2fn_null(efNDX, NFILE, fnm), &sys, bVerbose, mdModules.notifier(), ir, wi);
 
@@ -2093,19 +2126,19 @@ int gmx_grompp(int argc, char* argv[])
                 }
                 if (buffer_temp > 0)
                 {
-                    sprintf(warn_buf,
+                    std::string warningMessage = gmx::formatString(
                             "NVE simulation: will use the initial temperature of %.3f K for "
                             "determining the Verlet buffer size",
                             buffer_temp);
-                    warning_note(wi, warn_buf);
+                    warning_note(wi, warningMessage);
                 }
                 else
                 {
-                    sprintf(warn_buf,
+                    std::string warningMessage = gmx::formatString(
                             "NVE simulation with an initial temperature of zero: will use a Verlet "
                             "buffer of %d%%. Check your energy drift!",
                             gmx::roundToInt(verlet_buffer_ratio_NVE_T0 * 100));
-                    warning_note(wi, warn_buf);
+                    warning_note(wi, warningMessage);
                 }
             }
             else
@@ -2140,7 +2173,7 @@ int gmx_grompp(int argc, char* argv[])
 
                     if (ir->verletbuf_tol > 1.1 * driftTolerance * totalEnergyDriftPerAtomPerPicosecond)
                     {
-                        sprintf(warn_buf,
+                        std::string warningMessage = gmx::formatString(
                                 "You are using a Verlet buffer tolerance of %g kJ/mol/ps for an "
                                 "NVE simulation of length %g ps, which can give a final drift of "
                                 "%d%%. For conserving energy to %d%% when using constraints, you "
@@ -2149,11 +2182,11 @@ int gmx_grompp(int argc, char* argv[])
                                 gmx::roundToInt(ir->verletbuf_tol / totalEnergyDriftPerAtomPerPicosecond * 100),
                                 gmx::roundToInt(100 * driftTolerance),
                                 driftTolerance * totalEnergyDriftPerAtomPerPicosecond);
-                        warning_note(wi, warn_buf);
+                        warning_note(wi, warningMessage);
                     }
                 }
 
-                set_verlet_buffer(&sys, ir, buffer_temp, state.box, wi);
+                set_verlet_buffer(&sys, ir, buffer_temp, state.box, wi, logger);
             }
         }
     }
@@ -2173,33 +2206,24 @@ int gmx_grompp(int argc, char* argv[])
         pr_symtab(debug, 0, "After close", &sys.symtab);
     }
 
-    /* make exclusions between QM atoms and remove charges if needed */
-    if (ir->bQMMM)
-    {
-        generate_qmexcl(&sys, ir, wi, GmxQmmmMode::GMX_QMMM_ORIGINAL);
-        if (ir->QMMMscheme != eQMMMschemeoniom)
-        {
-            std::vector<int> qmmmAtoms = qmmmAtomIndices(*ir, sys);
-            removeQmmmAtomCharges(&sys, qmmmAtoms);
-        }
-    }
-
     if (ir->eI == eiMimic)
     {
-        generate_qmexcl(&sys, ir, wi, GmxQmmmMode::GMX_QMMM_MIMIC);
+        generate_qmexcl(&sys, ir, logger);
     }
 
     if (ftp2bSet(efTRN, NFILE, fnm))
     {
         if (bVerbose)
         {
-            fprintf(stderr, "getting data from old trajectory ...\n");
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted("getting data from old trajectory ...");
         }
         cont_status(ftp2fn(efTRN, NFILE, fnm), ftp2fn_null(efEDR, NFILE, fnm), bNeedVel, bGenVel,
-                    fr_time, ir, &state, &sys, oenv);
+                    fr_time, ir, &state, &sys, oenv, logger);
     }
 
-    if (ir->ePBC == epbcXY && ir->nwall != 2)
+    if (ir->pbcType == PbcType::XY && ir->nwall != 2)
     {
         clear_rvec(state.box[ZZ]);
     }
@@ -2279,8 +2303,10 @@ int gmx_grompp(int argc, char* argv[])
         {
             copy_mat(ir->compress, compressibility);
         }
-        setStateDependentAwhParams(ir->awhParams, ir->pull, pull, state.box, ir->ePBC,
-                                   compressibility, &ir->opts, wi);
+        setStateDependentAwhParams(
+                ir->awhParams, ir->pull, pull, state.box, ir->pbcType, compressibility, &ir->opts,
+                ir->efep != efepNO ? ir->fepvals->all_lambda[efptFEP][ir->fepvals->init_fep_state] : 0,
+                sys, wi);
     }
 
     if (ir->bPull)
@@ -2299,7 +2325,10 @@ int gmx_grompp(int argc, char* argv[])
     if (EEL_PME(ir->coulombtype))
     {
         float ratio = pme_load_estimate(sys, *ir, state.box);
-        fprintf(stderr, "Estimate for the relative computational load of the PME mesh part: %.2f\n", ratio);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted(
+                        "Estimate for the relative computational load of the PME mesh part: %.2f", ratio);
         /* With free energy we might need to do PME both for the A and B state
          * charges. This will double the cost, but the optimal performance will
          * then probably be at a slightly larger cut-off and grid spacing.
@@ -2321,17 +2350,17 @@ int gmx_grompp(int argc, char* argv[])
     }
 
     {
-        char   warn_buf[STRLEN];
-        double cio = compute_io(ir, sys.natoms, sys.groups, F_NRE, 1);
-        sprintf(warn_buf, "This run will generate roughly %.0f Mb of data", cio);
+        double      cio = compute_io(ir, sys.natoms, sys.groups, F_NRE, 1);
+        std::string warningMessage =
+                gmx::formatString("This run will generate roughly %.0f Mb of data", cio);
         if (cio > 2000)
         {
             set_warning_line(wi, mdparin, -1);
-            warning_note(wi, warn_buf);
+            warning_note(wi, warningMessage);
         }
         else
         {
-            printf("%s\n", warn_buf);
+            GMX_LOG(logger.info).asParagraph().appendText(warningMessage);
         }
     }
 
@@ -2340,14 +2369,14 @@ int gmx_grompp(int argc, char* argv[])
 
     {
         gmx::KeyValueTreeBuilder internalParameterBuilder;
-        mdModules.notifier().notifier_.notify(internalParameterBuilder.rootObject());
+        mdModules.notifier().preProcessingNotifications_.notify(internalParameterBuilder.rootObject());
         ir->internalParameters =
                 std::make_unique<gmx::KeyValueTreeObject>(internalParameterBuilder.build());
     }
 
     if (bVerbose)
     {
-        fprintf(stderr, "writing run input file...\n");
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("writing run input file...");
     }
 
     done_warning(wi, FARGS);
@@ -2357,8 +2386,9 @@ int gmx_grompp(int argc, char* argv[])
     gmx::write_IMDgroup_to_file(ir->bIMD, ir, &state, &sys, NFILE, fnm);
 
     sfree(opts->define);
+    sfree(opts->wall_atomtype[0]);
+    sfree(opts->wall_atomtype[1]);
     sfree(opts->include);
-    sfree(opts);
     for (auto& mol : mi)
     {
         // Some of the contents of molinfo have been stolen, so
index 1e06a7d6da5103cef538af118254c62822ce52ca..47ecb013b4c2f99ed0d5a6a796268c7334b695a4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/topology/atoms.h"
 #include "gromacs/topology/block.h"
 #include "gromacs/topology/idef.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/real.h"
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /*! \libinternal \brief
  * Describes an interaction of a given type, plus its parameters.
  */
@@ -176,7 +183,7 @@ struct MoleculeInformation
     //! Molecules separated in datastructure.
     t_block mols;
     //! Exclusions in the molecule.
-    t_blocka excls;
+    gmx::ListOfLists<int> excls;
     //! Interactions of a defined type.
     std::array<InteractionsOfType, F_NRE> interactions;
 
index 1a3e257e2d9f6a239697b5293fbbbe830fe4eb61..9092fc8310f61d1579a6076741dc109644837da4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 56422819e0e25889ba6b70f12d24d1158be52983..bc00ea2c46f1396e4e569fd68738ff2632991791 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,6 +47,7 @@
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/topology/atoms.h"
 #include "gromacs/topology/symtab.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
index 9fa36722eb21fde3dbcf4c3b0f8c14400cbf009e..33613291bb5212fd69233d9c59a45ac9fed21251 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <cstdio>
 
+#include <array>
 #include <string>
 #include <vector>
 
 #include "gromacs/gmxpreprocess/notset.h"
 #include "gromacs/topology/ifunc.h"
-#include "gromacs/utility/arrayref.h"
 
 struct t_atom;
 struct t_symtab;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /*! \brief
  * Used for reading .rtp/.tdb
  * ebtsBONDS must be the first, new types can be added to the end
index 3244b51e2afd6f21e620419950d178090c444069..83ba164b66bac4fca2a4ff4650a6e8e9358d0610 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b7684ba057ff0681eea8b3ce9c8e7a30421c2297..94cde0b968457025ee9637f91b9441495cb9b310 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -72,6 +73,7 @@
 #include "gromacs/topology/topology.h"
 #include "gromacs/trajectory/trajectoryframe.h"
 #include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
 using gmx::RVec;
 
 /* enum for random rotations of inserted solutes */
-enum RotationType
+enum class RotationType : int
 {
-    en_rotXYZ,
-    en_rotZ,
-    en_rotNone
+    XYZ,
+    Z,
+    None,
+    Count
 };
-const char* const cRotationEnum[] = { "xyz", "z", "none" };
+static const gmx::EnumerationArray<RotationType, const char*> c_rotationTypeNames = { { "xyz", "z",
+                                                                                        "none" } };
 
 static void center_molecule(gmx::ArrayRef<RVec> x)
 {
@@ -113,18 +117,19 @@ static void generate_trial_conf(gmx::ArrayRef<RVec>       xin,
     real alfa = 0.0, beta = 0.0, gamma = 0.0;
     switch (enum_rot)
     {
-        case en_rotXYZ:
+        case RotationType::XYZ:
             alfa  = dist(*rng);
             beta  = dist(*rng);
             gamma = dist(*rng);
             break;
-        case en_rotZ:
+        case RotationType::Z:
             alfa = beta = 0.;
             gamma       = dist(*rng);
             break;
-        case en_rotNone: alfa = beta = gamma = 0.; break;
+        case RotationType::None: alfa = beta = gamma = 0.; break;
+        default: GMX_THROW(gmx::InternalError("Invalid RotationType"));
     }
-    if (enum_rot == en_rotXYZ || enum_rot == en_rotZ)
+    if (enum_rot == RotationType::XYZ || enum_rot == RotationType::Z)
     {
         rotate_conf(xout->size(), as_rvec_array(xout->data()), nullptr, alfa, beta, gamma);
     }
@@ -174,7 +179,7 @@ static void insert_mols(int                  nmol_insrt,
                         const std::set<int>& removableAtoms,
                         const t_atoms&       atoms_insrt,
                         gmx::ArrayRef<RVec>  x_insrt,
-                        int                  ePBC,
+                        PbcType              pbcType,
                         matrix               box,
                         const std::string&   posfn,
                         const rvec           deltaR,
@@ -210,7 +215,7 @@ static void insert_mols(int                  nmol_insrt,
     gmx::DefaultRandomEngine rng(seed);
 
     t_pbc pbc;
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
 
     /* With -ip, take nmol_insrt from file posfn */
     double**   rpos              = nullptr;
@@ -331,7 +336,7 @@ public:
         seed_(0),
         defaultDistance_(0.105),
         scaleFactor_(0.57),
-        enumRot_(en_rotXYZ)
+        enumRot_(RotationType::XYZ)
     {
         clear_rvec(newBox_);
         clear_rvec(deltaR_);
@@ -369,7 +374,7 @@ private:
     gmx_mtop_t        top_;
     std::vector<RVec> x_;
     matrix            box_;
-    int               ePBC_;
+    PbcType           pbcType_;
 };
 
 void InsertMolecules::initOptions(IOptionsContainer* options, ICommandLineOptionsModuleSettings* settings)
@@ -472,7 +477,7 @@ void InsertMolecules::initOptions(IOptionsContainer* options, ICommandLineOption
     options->addOption(RealOption("dr").vector().store(deltaR_).description(
             "Allowed displacement in x/y/z from positions in [TT]-ip[tt] file"));
     options->addOption(EnumOption<RotationType>("rot")
-                               .enumValue(cRotationEnum)
+                               .enumValue(c_rotationTypeNames)
                                .store(&enumRot_)
                                .description("Rotate inserted molecules randomly"));
 }
@@ -503,7 +508,7 @@ void InsertMolecules::optionsFinished()
         bool  bTprFileWasRead;
         rvec* temporaryX = nullptr;
         fprintf(stderr, "Reading solute configuration\n");
-        readConfAndTopology(inputConfFile_.c_str(), &bTprFileWasRead, &top_, &ePBC_, &temporaryX,
+        readConfAndTopology(inputConfFile_.c_str(), &bTprFileWasRead, &top_, &pbcType_, &temporaryX,
                             nullptr, box_);
         x_.assign(temporaryX, temporaryX + top_.natoms);
         sfree(temporaryX);
@@ -520,7 +525,7 @@ int InsertMolecules::run()
     if (replaceSel_.isValid())
     {
         t_pbc pbc;
-        set_pbc(&pbc, ePBC_, box_);
+        set_pbc(&pbc, pbcType_, box_);
         t_trxframe* fr;
         snew(fr, 1);
         fr->natoms = x_.size();
@@ -534,10 +539,10 @@ int InsertMolecules::run()
         // individual atoms.
     }
 
-    int ePBCForOutput = ePBC_;
+    PbcType pbcTypeForOutput = pbcType_;
     if (bBox_)
     {
-        ePBCForOutput = epbcXYZ;
+        pbcTypeForOutput = PbcType::Xyz;
         clear_mat(box_);
         box_[XX][XX] = newBox_[XX];
         box_[YY][YY] = newBox_[YY];
@@ -554,11 +559,11 @@ int InsertMolecules::run()
     t_atoms           atomsInserted;
     std::vector<RVec> xInserted;
     {
-        bool   bTprFileWasRead;
-        int    ePBC_dummy;
-        matrix box_dummy;
-        rvec*  temporaryX;
-        readConfAndTopology(insertConfFile_.c_str(), &bTprFileWasRead, &topInserted, &ePBC_dummy,
+        bool    bTprFileWasRead;
+        PbcType pbcType_dummy;
+        matrix  box_dummy;
+        rvec*   temporaryX;
+        readConfAndTopology(insertConfFile_.c_str(), &bTprFileWasRead, &topInserted, &pbcType_dummy,
                             &temporaryX, nullptr, box_dummy);
         xInserted.assign(temporaryX, temporaryX + topInserted.natoms);
         sfree(temporaryX);
@@ -582,13 +587,13 @@ int InsertMolecules::run()
     /* add nmol_ins molecules of atoms_ins
        in random orientation at random place */
     insert_mols(nmolIns_, nmolTry_, seed_, defaultDistance_, scaleFactor_, &atoms, &top_.symtab,
-                &x_, removableAtoms, atomsInserted, xInserted, ePBCForOutput, box_, positionFile_,
-                deltaR_, enumRot_);
+                &x_, removableAtoms, atomsInserted, xInserted, pbcTypeForOutput, box_,
+                positionFile_, deltaR_, enumRot_);
 
     /* write new configuration to file confout */
     fprintf(stderr, "Writing generated configuration to %s\n", outputConfFile_.c_str());
     write_sto_conf(outputConfFile_.c_str(), *top_.name, &atoms, as_rvec_array(x_.data()), nullptr,
-                   ePBCForOutput, box_);
+                   pbcTypeForOutput, box_);
 
     /* print size of generated configuration */
     fprintf(stderr, "\nOutput configuration contains %d atoms in %d residues\n", atoms.nr, atoms.nres);
index c290dd31af6ee80026fc11a9f4b6cf2597986f84..3258fc0591021921b4aa203b05ce9b9a88b0e820 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7c8a9423711a236bee35b0de6ff81da4c1e159be..035daabf2a3806c9c0b9da8f97efb7b43d30788e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -172,7 +173,7 @@ enum
 static int match_str(const char* atom, const char* template_string)
 {
     if (!atom || !template_string)
-    {
+    { // NOLINT bugprone-branch-clone
         return ematchNone;
     }
     else if (gmx_strcasecmp(atom, template_string) == 0)
index 409472415bdf1d56e0c8d7edbad29b28b41b9bfd..d4b8301b5411abc3bcdde68dbc3ebd6a1ecd382e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -79,6 +80,8 @@
 #include "gromacs/utility/dir_separator.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/filestream.h"
+#include "gromacs/utility/loggerbuilder.h"
 #include "gromacs/utility/path.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/strdb.h"
@@ -320,7 +323,8 @@ static void rename_resrtp(t_atoms*                       pdba,
                           gmx::ArrayRef<const int>       r_end,
                           gmx::ArrayRef<const RtpRename> rr,
                           t_symtab*                      symtab,
-                          bool                           bVerbose)
+                          bool                           bVerbose,
+                          const gmx::MDLogger&           logger)
 {
     bool bFFRTPTERRNM = (getenv("GMX_NO_FFRTP_TER_RENAME") == nullptr);
 
@@ -359,8 +363,11 @@ static void rename_resrtp(t_atoms*                       pdba,
         {
             if (bVerbose)
             {
-                printf("Changing rtp entry of residue %d %s to '%s'\n", pdba->resinfo[r].nr,
-                       *pdba->resinfo[r].name, newName.c_str());
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted("Changing rtp entry of residue %d %s to '%s'",
+                                             pdba->resinfo[r].nr, *pdba->resinfo[r].name,
+                                             newName.c_str());
             }
             pdba->resinfo[r].rtp = put_symtab(symtab, newName.c_str());
         }
@@ -438,7 +445,7 @@ static void rename_bbint(t_atoms*                       pdba,
     }
 }
 
-static void check_occupancy(t_atoms* atoms, const char* filename, bool bVerbose)
+static void check_occupancy(t_atoms* atoms, const char* filename, bool bVerbose, const gmx::MDLogger& logger)
 {
     int i, ftp;
     int nzero   = 0;
@@ -447,7 +454,7 @@ static void check_occupancy(t_atoms* atoms, const char* filename, bool bVerbose)
     ftp = fn2ftp(filename);
     if (!atoms->pdbinfo || ((ftp != efPDB) && (ftp != efBRK) && (ftp != efENT)))
     {
-        fprintf(stderr, "No occupancies in %s\n", filename);
+        GMX_LOG(logger.warning).asParagraph().appendTextFormatted("No occupancies in %s", filename);
     }
     else
     {
@@ -457,10 +464,12 @@ static void check_occupancy(t_atoms* atoms, const char* filename, bool bVerbose)
             {
                 if (bVerbose)
                 {
-                    fprintf(stderr, "Occupancy for atom %s%d-%s is %f rather than 1\n",
-                            *atoms->resinfo[atoms->atom[i].resind].name,
-                            atoms->resinfo[atoms->atom[i].resind].nr, *atoms->atomname[i],
-                            atoms->pdbinfo[i].occup);
+                    GMX_LOG(logger.warning)
+                            .asParagraph()
+                            .appendTextFormatted("Occupancy for atom %s%d-%s is %f rather than 1",
+                                                 *atoms->resinfo[atoms->atom[i].resind].name,
+                                                 atoms->resinfo[atoms->atom[i].resind].nr,
+                                                 *atoms->atomname[i], atoms->pdbinfo[i].occup);
                 }
                 if (atoms->pdbinfo[i].occup == 0)
                 {
@@ -474,20 +483,24 @@ static void check_occupancy(t_atoms* atoms, const char* filename, bool bVerbose)
         }
         if (nzero == atoms->nr)
         {
-            fprintf(stderr, "All occupancy fields zero. This is probably not an X-Ray structure\n");
+            GMX_LOG(logger.warning)
+                    .asParagraph()
+                    .appendTextFormatted(
+                            "All occupancy fields zero. This is probably not an X-Ray structure");
         }
         else if ((nzero > 0) || (nnotone > 0))
         {
-            fprintf(stderr,
-                    "\n"
-                    "WARNING: there were %d atoms with zero occupancy and %d atoms with\n"
-                    "         occupancy unequal to one (out of %d atoms). Check your pdb file.\n"
-                    "\n",
-                    nzero, nnotone, atoms->nr);
+            GMX_LOG(logger.warning)
+                    .asParagraph()
+                    .appendTextFormatted(
+                            "there were %d atoms with zero occupancy and %d atoms with "
+                            "         occupancy unequal to one (out of %d atoms). Check your pdb "
+                            "file.",
+                            nzero, nnotone, atoms->nr);
         }
         else
         {
-            fprintf(stderr, "All occupancies are one\n");
+            GMX_LOG(logger.warning).asParagraph().appendTextFormatted("All occupancies are one");
         }
     }
 }
@@ -523,7 +536,7 @@ static int read_pdball(const char*     inf,
                        char**          title,
                        t_atoms*        atoms,
                        rvec**          x,
-                       int*            ePBC,
+                       PbcType*        pbcType,
                        matrix          box,
                        bool            bRemoveH,
                        t_symtab*       symtab,
@@ -537,7 +550,7 @@ static int read_pdball(const char*     inf,
 
     /* READ IT */
     printf("Reading %s...\n", inf);
-    readConfAndAtoms(inf, symtab, title, atoms, ePBC, x, nullptr, box);
+    readConfAndAtoms(inf, symtab, title, atoms, pbcType, x, nullptr, box);
     natom = atoms->nr;
     if (atoms->pdbinfo == nullptr)
     {
@@ -585,7 +598,7 @@ static int read_pdball(const char*     inf,
     }
     if (bOutput)
     {
-        write_sto_conf(outf, *title, atoms, *x, nullptr, *ePBC, box);
+        write_sto_conf(outf, *title, atoms, *x, nullptr, *pbcType, box);
     }
 
     return natom;
@@ -723,9 +736,7 @@ static void sort_pdbatoms(gmx::ArrayRef<const PreprocessResidue> restp_chain,
                              [&atomnm](char** it) { return gmx::equalCaseInsensitive(atomnm, *it); });
         if (found == localPpResidue->atomname.end())
         {
-            char buf[STRLEN];
-
-            sprintf(buf,
+            std::string buf = gmx::formatString(
                     "Atom %s in residue %s %d was not found in rtp entry %s with %d atoms\n"
                     "while sorting atoms.\n%s",
                     atomnm, *pdba->resinfo[pdba->atom[i].resind].name,
@@ -742,7 +753,7 @@ static void sort_pdbatoms(gmx::ArrayRef<const PreprocessResidue> restp_chain,
                               "solve it.\n"
                               "Option -ignh will ignore all hydrogens in the input."
                             : ".");
-            gmx_fatal(FARGS, "%s", buf);
+            gmx_fatal(FARGS, "%s", buf.c_str());
         }
         /* make shadow array to be sorted into indexgroup */
         pdbi[i].resnr  = pdba->atom[i].resind;
@@ -780,12 +791,12 @@ static void sort_pdbatoms(gmx::ArrayRef<const PreprocessResidue> restp_chain,
     sfree(pdbi);
 }
 
-static int remove_duplicate_atoms(t_atoms* pdba, gmx::ArrayRef<gmx::RVec> x, bool bVerbose)
+static int remove_duplicate_atoms(t_atoms* pdba, gmx::ArrayRef<gmx::RVec> x, bool bVerbose, const gmx::MDLogger& logger)
 {
     int        i, j, oldnatoms, ndel;
     t_resinfo* ri;
 
-    printf("Checking for duplicate atoms....\n");
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Checking for duplicate atoms....");
     oldnatoms = pdba->nr;
     ndel      = 0;
     /* NOTE: pdba->nr is modified inside the loop */
@@ -800,8 +811,10 @@ static int remove_duplicate_atoms(t_atoms* pdba, gmx::ArrayRef<gmx::RVec> x, boo
             if (bVerbose)
             {
                 ri = &pdba->resinfo[pdba->atom[i].resind];
-                printf("deleting duplicate atom %4s  %s%4d%c", *pdba->atomname[i], *ri->name,
-                       ri->nr, ri->ic);
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted("deleting duplicate atom %4s  %s%4d%c",
+                                             *pdba->atomname[i], *ri->name, ri->nr, ri->ic);
                 if (ri->chainid && (ri->chainid != ' '))
                 {
                     printf(" ch %c", ri->chainid);
@@ -839,7 +852,9 @@ static int remove_duplicate_atoms(t_atoms* pdba, gmx::ArrayRef<gmx::RVec> x, boo
     }
     if (pdba->nr != oldnatoms)
     {
-        printf("Now there are %d atoms. Deleted %d duplicates.\n", pdba->nr, ndel);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Now there are %d atoms. Deleted %d duplicates.", pdba->nr, ndel);
     }
 
     return pdba->nr;
@@ -913,10 +928,10 @@ static void checkResidueTypeSanity(t_atoms* pdba, int r0, int r1, ResidueType* r
     }
 }
 
-static void find_nc_ter(t_atoms* pdba, int r0, int r1, int* r_start, int* r_end, ResidueType* rt)
+static void find_nc_ter(t_atoms* pdba, int r0, int r1, int* r_start, int* r_end, ResidueType* rt, const gmx::MDLogger& logger)
 {
-    int                                i;
-    gmx::compat::optional<std::string> startrestype;
+    int                        i;
+    std::optional<std::string> startrestype;
 
     *r_start = -1;
     *r_end   = -1;
@@ -951,20 +966,28 @@ static void find_nc_ter(t_atoms* pdba, int r0, int r1, int* r_start, int* r_end,
             || gmx::equalCaseInsensitive(*startrestype, "DNA")
             || gmx::equalCaseInsensitive(*startrestype, "RNA"))
         {
-            printf("Identified residue %s%d as a starting terminus.\n", *pdba->resinfo[i].name,
-                   pdba->resinfo[i].nr);
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted("Identified residue %s%d as a starting terminus.",
+                                         *pdba->resinfo[i].name, pdba->resinfo[i].nr);
             *r_start = i;
         }
         else if (gmx::equalCaseInsensitive(*startrestype, "Ion"))
         {
             if (ionNotes < 5)
             {
-                printf("Residue %s%d has type 'Ion', assuming it is not linked into a chain.\n",
-                       *pdba->resinfo[i].name, pdba->resinfo[i].nr);
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted(
+                                "Residue %s%d has type 'Ion', assuming it is not linked into a "
+                                "chain.",
+                                *pdba->resinfo[i].name, pdba->resinfo[i].nr);
             }
             if (ionNotes == 4)
             {
-                printf("Disabling further notes about ions.\n");
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted("Disabling further notes about ions.");
             }
             ionNotes++;
         }
@@ -975,35 +998,45 @@ static void find_nc_ter(t_atoms* pdba, int r0, int r1, int* r_start, int* r_end,
             {
                 if (chainID == ' ')
                 {
-                    printf("\nWarning: Starting residue %s%d in chain not identified as "
-                           "Protein/RNA/DNA.\n"
-                           "This chain lacks identifiers, which makes it impossible to do strict\n"
-                           "classification of the start/end residues. Here we need to guess this "
-                           "residue\n"
-                           "should not be part of the chain and instead introduce a break, but "
-                           "that will\n"
-                           "be catastrophic if they should in fact be linked. Please check your "
-                           "structure,\n"
-                           "and add %s to residuetypes.dat if this was not correct.\n\n",
-                           *pdba->resinfo[i].name, pdba->resinfo[i].nr, *pdba->resinfo[i].name);
+                    GMX_LOG(logger.warning)
+                            .asParagraph()
+                            .appendTextFormatted(
+                                    "Starting residue %s%d in chain not identified as "
+                                    "Protein/RNA/DNA. "
+                                    "This chain lacks identifiers, which makes it impossible to do "
+                                    "strict "
+                                    "classification of the start/end residues. Here we need to "
+                                    "guess this residue "
+                                    "should not be part of the chain and instead introduce a "
+                                    "break, but that will "
+                                    "be catastrophic if they should in fact be linked. Please "
+                                    "check your structure, "
+                                    "and add %s to residuetypes.dat if this was not correct.",
+                                    *pdba->resinfo[i].name, pdba->resinfo[i].nr, *pdba->resinfo[i].name);
                 }
                 else
                 {
-                    printf("\nWarning: No residues in chain starting at %s%d identified as "
-                           "Protein/RNA/DNA.\n"
-                           "This makes it impossible to link them into a molecule, which could "
-                           "either be\n"
-                           "correct or a catastrophic error. Please check your structure, and add "
-                           "all\n"
-                           "necessary residue names to residuetypes.dat if this was not "
-                           "correct.\n\n",
-                           *pdba->resinfo[i].name, pdba->resinfo[i].nr);
+                    GMX_LOG(logger.warning)
+                            .asParagraph()
+                            .appendTextFormatted(
+                                    "No residues in chain starting at %s%d identified as "
+                                    "Protein/RNA/DNA. "
+                                    "This makes it impossible to link them into a molecule, which "
+                                    "could either be "
+                                    "correct or a catastrophic error. Please check your structure, "
+                                    "and add all "
+                                    "necessary residue names to residuetypes.dat if this was not "
+                                    "correct.",
+                                    *pdba->resinfo[i].name, pdba->resinfo[i].nr);
                 }
             }
             if (startWarnings == 4)
             {
-                printf("Disabling further warnings about unidentified residues at start of "
-                       "chain.\n");
+                GMX_LOG(logger.warning)
+                        .asParagraph()
+                        .appendTextFormatted(
+                                "Disabling further warnings about unidentified residues at start "
+                                "of chain.");
             }
             startWarnings++;
         }
@@ -1014,7 +1047,7 @@ static void find_nc_ter(t_atoms* pdba, int r0, int r1, int* r_start, int* r_end,
         /* Go through the rest of the residues, check that they are the same class, and identify the ending terminus. */
         for (int i = *r_start; i < r1; i++)
         {
-            gmx::compat::optional<std::string> restype =
+            std::optional<std::string> restype =
                     rt->optionalTypeOfNamedDatabaseResidue(*pdba->resinfo[i].name);
             if (!restype)
             {
@@ -1028,12 +1061,18 @@ static void find_nc_ter(t_atoms* pdba, int r0, int r1, int* r_start, int* r_end,
             {
                 if (ionNotes < 5)
                 {
-                    printf("Residue %s%d has type 'Ion', assuming it is not linked into a chain.\n",
-                           *pdba->resinfo[i].name, pdba->resinfo[i].nr);
+                    GMX_LOG(logger.info)
+                            .asParagraph()
+                            .appendTextFormatted(
+                                    "Residue %s%d has type 'Ion', assuming it is not linked into a "
+                                    "chain.",
+                                    *pdba->resinfo[i].name, pdba->resinfo[i].nr);
                 }
                 if (ionNotes == 4)
                 {
-                    printf("Disabling further notes about ions.\n");
+                    GMX_LOG(logger.info)
+                            .asParagraph()
+                            .appendTextFormatted("Disabling further notes about ions.");
                 }
                 ionNotes++;
             }
@@ -1045,24 +1084,32 @@ static void find_nc_ter(t_atoms* pdba, int r0, int r1, int* r_start, int* r_end,
                 // have caught the problem.
                 if (endWarnings < 5)
                 {
-                    printf("\nWarning: Residue %s%d in chain has different type ('%s') from\n"
-                           "residue %s%d ('%s'). This chain lacks identifiers, which makes\n"
-                           "it impossible to do strict classification of the start/end residues. "
-                           "Here we\n"
-                           "need to guess this residue should not be part of the chain and "
-                           "instead\n"
-                           "introduce a break, but that will be catastrophic if they should in "
-                           "fact be\n"
-                           "linked. Please check your structure, and add %s to residuetypes.dat\n"
-                           "if this was not correct.\n\n",
-                           *pdba->resinfo[i].name, pdba->resinfo[i].nr, restype->c_str(),
-                           *pdba->resinfo[*r_start].name, pdba->resinfo[*r_start].nr,
-                           startrestype->c_str(), *pdba->resinfo[i].name);
+                    GMX_LOG(logger.warning)
+                            .asParagraph()
+                            .appendTextFormatted(
+                                    "Residue %s%d in chain has different type ('%s') from "
+                                    "residue %s%d ('%s'). This chain lacks identifiers, which "
+                                    "makes "
+                                    "it impossible to do strict classification of the start/end "
+                                    "residues. Here we "
+                                    "need to guess this residue should not be part of the chain "
+                                    "and instead "
+                                    "introduce a break, but that will be catastrophic if they "
+                                    "should in fact be "
+                                    "linked. Please check your structure, and add %s to "
+                                    "residuetypes.dat "
+                                    "if this was not correct.",
+                                    *pdba->resinfo[i].name, pdba->resinfo[i].nr, restype->c_str(),
+                                    *pdba->resinfo[*r_start].name, pdba->resinfo[*r_start].nr,
+                                    startrestype->c_str(), *pdba->resinfo[i].name);
                 }
                 if (endWarnings == 4)
                 {
-                    printf("Disabling further warnings about unidentified residues at end of "
-                           "chain.\n");
+                    GMX_LOG(logger.warning)
+                            .asParagraph()
+                            .appendTextFormatted(
+                                    "Disabling further warnings about unidentified residues at end "
+                                    "of chain.");
                 }
                 endWarnings++;
             }
@@ -1071,30 +1118,34 @@ static void find_nc_ter(t_atoms* pdba, int r0, int r1, int* r_start, int* r_end,
 
     if (*r_end >= 0)
     {
-        printf("Identified residue %s%d as a ending terminus.\n", *pdba->resinfo[*r_end].name,
-               pdba->resinfo[*r_end].nr);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Identified residue %s%d as a ending terminus.",
+                                     *pdba->resinfo[*r_end].name, pdba->resinfo[*r_end].nr);
     }
 }
 
-/* enum for chain separation */
-enum ChainSepType
+enum class ChainSeparationType : int
 {
-    enChainSep_id_or_ter,
-    enChainSep_id_and_ter,
-    enChainSep_ter,
-    enChainSep_id,
-    enChainSep_interactive
+    IdOrTer,
+    IdAndTer,
+    Ter,
+    Id,
+    Interactive,
+    Count
+};
+static const gmx::EnumerationArray<ChainSeparationType, const char*> c_chainSeparationTypeNames = {
+    { "id_or_ter", "id_and_ter", "ter", "id", "interactive" }
 };
-static const char* ChainSepEnum[]       = { "id_or_ter", "id_and_ter", "ter", "id", "interactive" };
-static const char* ChainSepInfoString[] = {
-    "Splitting chemical chains based on TER records or chain id changing.\n",
-    "Splitting chemical chains based on TER records and chain id changing.\n",
-    "Splitting chemical chains based on TER records only (ignoring chain id).\n",
-    "Splitting chemical chains based on changing chain id only (ignoring TER records).\n",
-    "Splitting chemical chains interactively.\n"
+static const gmx::EnumerationArray<ChainSeparationType, const char*> c_chainSeparationTypeNotificationMessages = {
+    { "Splitting chemical chains based on TER records or chain id changing.\n",
+      "Splitting chemical chains based on TER records and chain id changing.\n",
+      "Splitting chemical chains based on TER records only (ignoring chain id).\n",
+      "Splitting chemical chains based on changing chain id only (ignoring TER records).\n",
+      "Splitting chemical chains interactively.\n" }
 };
 
-static void modify_chain_numbers(t_atoms* pdba, ChainSepType enumChainSep)
+static void modify_chain_numbers(t_atoms* pdba, ChainSeparationType chainSeparation, const gmx::MDLogger& logger)
 {
     int         i;
     char        old_prev_chainid;
@@ -1116,7 +1167,7 @@ static void modify_chain_numbers(t_atoms* pdba, ChainSepType enumChainSep)
     char        this_chainid;
 
     /* The default chain enumeration is based on TER records only */
-    printf("%s", ChainSepInfoString[enumChainSep]);
+    printf("%s", c_chainSeparationTypeNotificationMessages[chainSeparation]);
 
     old_prev_chainid  = '?';
     old_prev_chainnum = -1;
@@ -1146,45 +1197,49 @@ static void modify_chain_numbers(t_atoms* pdba, ChainSepType enumChainSep)
         this_resnum   = ri->nr;
         this_chainid  = ri->chainid;
 
-        switch (enumChainSep)
+        switch (chainSeparation)
         {
-            case enChainSep_id_or_ter:
+            case ChainSeparationType::IdOrTer:
                 if (old_this_chainid != old_prev_chainid || old_this_chainnum != old_prev_chainnum)
                 {
                     new_chainnum++;
                 }
                 break;
 
-            case enChainSep_id_and_ter:
+            case ChainSeparationType::IdAndTer:
                 if (old_this_chainid != old_prev_chainid && old_this_chainnum != old_prev_chainnum)
                 {
                     new_chainnum++;
                 }
                 break;
 
-            case enChainSep_id:
+            case ChainSeparationType::Id:
                 if (old_this_chainid != old_prev_chainid)
                 {
                     new_chainnum++;
                 }
                 break;
 
-            case enChainSep_ter:
+            case ChainSeparationType::Ter:
                 if (old_this_chainnum != old_prev_chainnum)
                 {
                     new_chainnum++;
                 }
                 break;
-            case enChainSep_interactive:
+            case ChainSeparationType::Interactive:
                 if (old_this_chainid != old_prev_chainid || old_this_chainnum != old_prev_chainnum)
                 {
                     if (i > 0)
                     {
-                        printf("Split the chain (and introduce termini) between residue %s%d (chain id '%c', atom %d %s)\
-\n"
-                               "and residue %s%d (chain id '%c', atom %d %s) ? [n/y]\n",
-                               prev_resname, prev_resnum, prev_chainid, prev_atomnum, prev_atomname,
-                               this_resname, this_resnum, this_chainid, this_atomnum, this_atomname);
+                        GMX_LOG(logger.info)
+                                .asParagraph()
+                                .appendTextFormatted(
+                                        "Split the chain (and introduce termini) between residue %s%d (chain id '%c', atom %d %s)\
+"
+                                        "and residue %s%d (chain id '%c', atom %d %s) ? [n/y]",
+                                        prev_resname, prev_resnum, prev_chainid, prev_atomnum,
+                                        prev_atomname, this_resname, this_resnum, this_chainid,
+                                        this_atomnum, this_atomname);
 
                         if (nullptr == fgets(select, STRLEN - 1, stdin))
                         {
@@ -1197,7 +1252,8 @@ static void modify_chain_numbers(t_atoms* pdba, ChainSepType enumChainSep)
                     }
                 }
                 break;
-            default: gmx_fatal(FARGS, "Internal inconsistency - this shouldn't happen...");
+            case ChainSeparationType::Count:
+                gmx_fatal(FARGS, "Internal inconsistency - this shouldn't happen...");
         }
         old_prev_chainid  = old_this_chainid;
         old_prev_chainnum = old_this_chainnum;
@@ -1232,39 +1288,42 @@ struct t_chain
     std::vector<gmx::RVec>              x;
 };
 
-// TODO make all enums into scoped enums
-/* enum for vsites */
-enum VSitesType
+enum class VSitesType : int
 {
-    enVSites_none,
-    enVSites_hydrogens,
-    enVSites_aromatics
+    None,
+    Hydrogens,
+    Aromatics,
+    Count
+};
+static const gmx::EnumerationArray<VSitesType, const char*> c_vsitesTypeNames = {
+    { "none", "hydrogens", "aromatics" }
 };
-static const char* VSitesEnum[] = { "none", "hydrogens", "aromatics" };
 
-/* enum for water model */
-enum WaterType
+enum class WaterType : int
 {
-    enWater_select,
-    enWater_none,
-    enWater_spc,
-    enWater_spce,
-    enWater_tip3p,
-    enWater_tip4p,
-    enWater_tip5p,
-    enWater_tips3p
+    Select,
+    None,
+    Spc,
+    SpcE,
+    Tip3p,
+    Tip4p,
+    Tip5p,
+    Tips3p,
+    Count
+};
+static const gmx::EnumerationArray<WaterType, const char*> c_waterTypeNames = {
+    { "select", "none", "spc", "spce", "tip3p", "tip4p", "tip5p", "tips3p" }
 };
-static const char* WaterEnum[] = { "select", "none",  "spc",   "spce",
-                                   "tip3p",  "tip4p", "tip5p", "tips3p" };
 
-/* enum for merge */
-enum MergeType
+enum class MergeType : int
 {
-    enMerge_no,
-    enMerge_all,
-    enMerge_interactive
+    No,
+    All,
+    Interactive,
+    Count
 };
-static const char* MergeEnum[] = { "no", "all", "interactive" };
+static const gmx::EnumerationArray<MergeType, const char*> c_mergeTypeNames = { { "no", "all",
+                                                                                  "interactive" } };
 
 namespace gmx
 {
@@ -1279,13 +1338,17 @@ public:
         bVsites_(FALSE),
         bPrevWat_(FALSE),
         bVsiteAromatics_(FALSE),
-        enumChainSep_(enChainSep_id_or_ter),
-        enumVSites_(enVSites_none),
-        enumWater_(enWater_select),
-        enumMerge_(enMerge_no),
+        chainSeparation_(ChainSeparationType::IdOrTer),
+        vsitesType_(VSitesType::None),
+        waterType_(WaterType::Select),
+        mergeType_(MergeType::No),
         itp_file_(nullptr),
         mHmult_(0)
     {
+        gmx::LoggerBuilder builder;
+        builder.addTargetStream(gmx::MDLogger::LogLevel::Info, &gmx::TextOutputFile::standardOutput());
+        builder.addTargetStream(gmx::MDLogger::LogLevel::Warning, &gmx::TextOutputFile::standardError());
+        loggerOwner_ = std::make_unique<LoggerOwner>(builder.build());
     }
 
     // From ICommandLineOptionsModule
@@ -1341,19 +1404,20 @@ private:
     std::string outFile_;
     std::string ff_;
 
-    ChainSepType enumChainSep_;
-    VSitesType   enumVSites_;
-    WaterType    enumWater_;
-    MergeType    enumMerge_;
-
-    FILE*                    itp_file_;
-    char                     forcefield_[STRLEN];
-    char                     ffdir_[STRLEN];
-    char*                    ffname_;
-    char*                    watermodel_;
-    std::vector<std::string> incls_;
-    std::vector<t_mols>      mols_;
-    real                     mHmult_;
+    ChainSeparationType chainSeparation_;
+    VSitesType          vsitesType_;
+    WaterType           waterType_;
+    MergeType           mergeType_;
+
+    FILE*                        itp_file_;
+    char                         forcefield_[STRLEN];
+    char                         ffdir_[STRLEN];
+    char*                        ffname_;
+    char*                        watermodel_;
+    std::vector<std::string>     incls_;
+    std::vector<t_mols>          mols_;
+    real                         mHmult_;
+    std::unique_ptr<LoggerOwner> loggerOwner_;
 };
 
 void pdb2gmx::initOptions(IOptionsContainer* options, ICommandLineOptionsModuleSettings* settings)
@@ -1478,16 +1542,19 @@ void pdb2gmx::initOptions(IOptionsContainer* options, ICommandLineOptionsModuleS
             RealOption("lb").store(&long_bond_dist_).defaultValue(0.25).hidden().description("Long bond warning distance"));
     options->addOption(
             RealOption("sb").store(&short_bond_dist_).defaultValue(0.05).hidden().description("Short bond warning distance"));
-    options->addOption(
-            EnumOption<ChainSepType>("chainsep").enumValue(ChainSepEnum).store(&enumChainSep_).description("Condition in PDB files when a new chain should be started (adding termini)"));
+    options->addOption(EnumOption<ChainSeparationType>("chainsep")
+                               .enumValue(c_chainSeparationTypeNames)
+                               .store(&chainSeparation_)
+                               .description("Condition in PDB files when a new chain should be "
+                                            "started (adding termini)"));
     options->addOption(EnumOption<MergeType>("merge")
-                               .enumValue(MergeEnum)
-                               .store(&enumMerge_)
+                               .enumValue(c_mergeTypeNames)
+                               .store(&mergeType_)
                                .description("Merge multiple chains into a single [moleculetype]"));
     options->addOption(StringOption("ff").store(&ff_).defaultValue("select").description(
             "Force field, interactive by default. Use [TT]-h[tt] for information."));
     options->addOption(
-            EnumOption<WaterType>("water").store(&enumWater_).enumValue(WaterEnum).description("Water model to use"));
+            EnumOption<WaterType>("water").store(&waterType_).enumValue(c_waterTypeNames).description("Water model to use"));
     options->addOption(BooleanOption("inter").store(&bInter_).defaultValue(false).description(
             "Set the next 8 options to interactive"));
     options->addOption(BooleanOption("ss").store(&bCysMan_).defaultValue(false).description(
@@ -1528,8 +1595,10 @@ void pdb2gmx::initOptions(IOptionsContainer* options, ICommandLineOptionsModuleS
             BooleanOption("v").store(&bVerbose_).defaultValue(false).description("Be slightly more verbose in messages"));
     options->addOption(
             RealOption("posrefc").store(&posre_fc_).defaultValue(1000).description("Force constant for position restraints"));
-    options->addOption(
-            EnumOption<VSitesType>("vsite").store(&enumVSites_).enumValue(VSitesEnum).description("Convert atoms to virtual sites"));
+    options->addOption(EnumOption<VSitesType>("vsite")
+                               .store(&vsitesType_)
+                               .enumValue(c_vsitesTypeNames)
+                               .description("Convert atoms to virtual sites"));
     options->addOption(BooleanOption("heavyh").store(&bHeavyH_).defaultValue(false).description(
             "Make hydrogen atoms heavy"));
     options->addOption(
@@ -1628,7 +1697,7 @@ void pdb2gmx::optionsFinished()
 
     /* Force field selection, interactive or direct */
     choose_ff(strcmp(ff_.c_str(), "select") == 0 ? nullptr : ff_.c_str(), forcefield_,
-              sizeof(forcefield_), ffdir_, sizeof(ffdir_));
+              sizeof(forcefield_), ffdir_, sizeof(ffdir_), loggerOwner_->logger());
 
     if (strlen(forcefield_) > 0)
     {
@@ -1661,26 +1730,30 @@ int pdb2gmx::run()
     int         this_chainstart;
     int         prev_chainstart;
 
-    printf("\nUsing the %s force field in directory %s\n\n", ffname_, ffdir_);
+    const gmx::MDLogger& logger = loggerOwner_->logger();
 
-    choose_watermodel(WaterEnum[enumWater_], ffdir_, &watermodel_);
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted("Using the %s force field in directory %s", ffname_, ffdir_);
 
-    switch (enumVSites_)
+    choose_watermodel(c_waterTypeNames[waterType_], ffdir_, &watermodel_, logger);
+
+    switch (vsitesType_)
     {
-        case enVSites_none:
+        case VSitesType::None:
             bVsites_         = false;
             bVsiteAromatics_ = false;
             break;
-        case enVSites_hydrogens:
+        case VSitesType::Hydrogens:
             bVsites_         = true;
             bVsiteAromatics_ = false;
             break;
-        case enVSites_aromatics:
+        case VSitesType::Aromatics:
             bVsites_         = true;
             bVsiteAromatics_ = true;
             break;
-        default:
-            gmx_fatal(FARGS, "Internal inconsistency: VSitesEnum='%s'", VSitesEnum[enumVSites_]);
+        case VSitesType::Count:
+            gmx_fatal(FARGS, "Internal inconsistency: VSitesType is out of range.");
     } /* end switch */
 
     /* Open the symbol table */
@@ -1696,7 +1769,7 @@ int pdb2gmx::run()
     std::vector<RtpRename> rtprename;
     for (const auto& filename : rrn)
     {
-        printf("going to rename %s\n", filename.c_str());
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("going to rename %s", filename.c_str());
         FILE* fp = fflib_open(filename);
         read_rtprename(filename.c_str(), fp, &rtprename);
         gmx_ffclose(fp);
@@ -1734,11 +1807,11 @@ int pdb2gmx::run()
 
     AtomProperties aps;
     char*          title = nullptr;
-    int            ePBC;
+    PbcType        pbcType;
     t_atoms        pdba_all;
     rvec*          pdbx;
     int natom = read_pdball(inputConfFile_.c_str(), bOutputSet_, outFile_.c_str(), &title, &pdba_all,
-                            &pdbx, &ePBC, box, bRemoveH_, &symtab, &rt, watres, &aps, bVerbose_);
+                            &pdbx, &pbcType, box, bRemoveH_, &symtab, &rt, watres, &aps, bVerbose_);
 
     if (natom == 0)
     {
@@ -1746,10 +1819,10 @@ int pdb2gmx::run()
         GMX_THROW(InconsistentInputError(message));
     }
 
-    printf("Analyzing pdb file\n");
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Analyzing pdb file");
     int nwaterchain = 0;
 
-    modify_chain_numbers(&pdba_all, enumChainSep_);
+    modify_chain_numbers(&pdba_all, chainSeparation_, logger);
 
     int nchainmerges = 0;
 
@@ -1803,14 +1876,18 @@ int pdb2gmx::run()
             bMerged         = false;
             if (i > 0 && !bWat_)
             {
-                if (!strncmp(MergeEnum[enumMerge_], "int", 3))
+                if (!strncmp(c_mergeTypeNames[mergeType_], "int", 3))
                 {
-                    printf("Merge chain ending with residue %s%d (chain id '%c', atom %d %s) and "
-                           "chain starting with\n"
-                           "residue %s%d (chain id '%c', atom %d %s) into a single moleculetype "
-                           "(keeping termini)? [n/y]\n",
-                           prev_resname, prev_resnum, prev_chainid, prev_atomnum, prev_atomname,
-                           this_resname, this_resnum, this_chainid, this_atomnum, this_atomname);
+                    GMX_LOG(logger.info)
+                            .asParagraph()
+                            .appendTextFormatted(
+                                    "Merge chain ending with residue %s%d (chain id '%c', atom %d "
+                                    "%s) and chain starting with "
+                                    "residue %s%d (chain id '%c', atom %d %s) into a single "
+                                    "moleculetype (keeping termini)? [n/y]",
+                                    prev_resname, prev_resnum, prev_chainid, prev_atomnum,
+                                    prev_atomname, this_resname, this_resnum, this_chainid,
+                                    this_atomnum, this_atomname);
 
                     if (nullptr == fgets(select, STRLEN - 1, stdin))
                     {
@@ -1818,7 +1895,7 @@ int pdb2gmx::run()
                     }
                     bMerged = (select[0] == 'y');
                 }
-                else if (!strncmp(MergeEnum[enumMerge_], "all", 3))
+                else if (!strncmp(c_mergeTypeNames[mergeType_], "all", 3))
                 {
                     bMerged = true;
                 }
@@ -1849,11 +1926,14 @@ int pdb2gmx::run()
                 {
                     if (pdb_ch[j].chainid != ' ' && pdb_ch[j].chainid == ri->chainid)
                     {
-                        printf("WARNING: Chain identifier '%c' is used in two non-sequential "
-                               "blocks.\n"
-                               "They will be treated as separate chains unless you reorder your "
-                               "file.\n",
-                               ri->chainid);
+                        GMX_LOG(logger.warning)
+                                .asParagraph()
+                                .appendTextFormatted(
+                                        "Chain identifier '%c' is used in two non-sequential "
+                                        "blocks. "
+                                        "They will be treated as separate chains unless you "
+                                        "reorder your file.",
+                                        ri->chainid);
                     }
                 }
                 t_pdbchain newChain;
@@ -1901,7 +1981,9 @@ int pdb2gmx::run()
     }
     if (nwaterchain > 1)
     {
-        printf("Moved all the water blocks to the end\n");
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Moved all the water blocks to the end");
     }
 
     t_atoms*             pdba;
@@ -1949,33 +2031,43 @@ int pdb2gmx::run()
 
     if (nchainmerges > 0)
     {
-        printf("\nMerged chains into joint molecule definitions at %d places.\n\n", nchainmerges);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Merged chains into joint molecule definitions at %d places.",
+                                     nchainmerges);
     }
 
-    printf("There are %d chains and %d blocks of water and "
-           "%d residues with %d atoms\n",
-           numChains - nwaterchain, nwaterchain, pdba_all.nres, natom);
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted(
+                    "There are %d chains and %d blocks of water and "
+                    "%d residues with %d atoms",
+                    numChains - nwaterchain, nwaterchain, pdba_all.nres, natom);
 
-    printf("\n  %5s  %4s %6s\n", "chain", "#res", "#atoms");
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted("  %5s  %4s %6s", "chain", "#res", "#atoms");
     for (int i = 0; (i < numChains); i++)
     {
-        printf("  %d '%c' %5d %6d  %s\n", i + 1, chains[i].chainid ? chains[i].chainid : '-',
-               chains[i].pdba->nres, chains[i].pdba->nr, chains[i].bAllWat ? "(only water)" : "");
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("  %d '%c' %5d %6d  %s\n", i + 1,
+                                     chains[i].chainid ? chains[i].chainid : '-', chains[i].pdba->nres,
+                                     chains[i].pdba->nr, chains[i].bAllWat ? "(only water)" : "");
     }
-    printf("\n");
 
-    check_occupancy(&pdba_all, inputConfFile_.c_str(), bVerbose_);
+    check_occupancy(&pdba_all, inputConfFile_.c_str(), bVerbose_, logger);
 
     /* Read atomtypes... */
     PreprocessingAtomTypes atype = read_atype(ffdir_, &symtab);
 
     /* read residue database */
-    printf("Reading residue database... (%s)\n", forcefield_);
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Reading residue database... (%s)", forcefield_);
     std::vector<std::string>       rtpf = fflib_search_file_end(ffdir_, ".rtp", true);
     std::vector<PreprocessResidue> rtpFFDB;
     for (const auto& filename : rtpf)
     {
-        readResidueDatabase(filename, &rtpFFDB, &atype, &symtab, false);
+        readResidueDatabase(filename, &rtpFFDB, &atype, &symtab, logger, false);
     }
     if (bNewRTP_)
     {
@@ -2019,12 +2111,17 @@ int pdb2gmx::run()
 
         if (cc->chainid && (cc->chainid != ' '))
         {
-            printf("Processing chain %d '%c' (%d atoms, %d residues)\n", chain + 1, cc->chainid,
-                   natom, nres);
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted("Processing chain %d '%c' (%d atoms, %d residues)",
+                                         chain + 1, cc->chainid, natom, nres);
         }
         else
         {
-            printf("Processing chain %d (%d atoms, %d residues)\n", chain + 1, natom, nres);
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted("Processing chain %d (%d atoms, %d residues)", chain + 1,
+                                         natom, nres);
         }
 
         process_chain(pdba, x, bUnA_, bUnA_, bUnA_, bLysMan_, bAspMan_, bGluMan_, bHisMan_,
@@ -2035,7 +2132,7 @@ int pdb2gmx::run()
         for (int i = 0; i < cc->nterpairs; i++)
         {
             find_nc_ter(pdba, cc->chainstart[i], cc->chainstart[i + 1], &(cc->r_start[j]),
-                        &(cc->r_end[j]), &rt);
+                        &(cc->r_end[j]), &rt, logger);
 
             if (cc->r_start[j] >= 0 && cc->r_end[j] >= 0)
             {
@@ -2045,9 +2142,13 @@ int pdb2gmx::run()
         cc->nterpairs = j;
         if (cc->nterpairs == 0)
         {
-            printf("Problem with chain definition, or missing terminal residues.\n"
-                   "This chain does not appear to contain a recognized chain molecule.\n"
-                   "If this is incorrect, you can edit residuetypes.dat to modify the behavior.\n");
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted(
+                            "Problem with chain definition, or missing terminal residues. "
+                            "This chain does not appear to contain a recognized chain molecule. "
+                            "If this is incorrect, you can edit residuetypes.dat to modify the "
+                            "behavior.");
         }
 
         /* Check for disulfides and other special bonds */
@@ -2055,7 +2156,7 @@ int pdb2gmx::run()
 
         if (!rtprename.empty())
         {
-            rename_resrtp(pdba, cc->nterpairs, cc->r_start, cc->r_end, rtprename, &symtab, bVerbose_);
+            rename_resrtp(pdba, cc->nterpairs, cc->r_start, cc->r_end, rtprename, &symtab, bVerbose_, logger);
         }
 
         for (int i = 0; i < cc->nterpairs; i++)
@@ -2071,10 +2172,13 @@ int pdb2gmx::run()
                 tdblist = filter_ter(ntdb, *pdba->resinfo[cc->r_start[i]].name);
                 if (tdblist.empty())
                 {
-                    printf("No suitable end (N or 5') terminus found in database - assuming this "
-                           "residue\n"
-                           "is already in a terminus-specific form and skipping terminus "
-                           "selection.\n");
+                    GMX_LOG(logger.info)
+                            .asParagraph()
+                            .appendTextFormatted(
+                                    "No suitable end (N or 5') terminus found in database - "
+                                    "assuming this residue "
+                                    "is already in a terminus-specific form and skipping terminus "
+                                    "selection.");
                     cc->ntdb.push_back(nullptr);
                 }
                 else
@@ -2106,10 +2210,13 @@ int pdb2gmx::run()
                 tdblist = filter_ter(ctdb, *pdba->resinfo[cc->r_end[i]].name);
                 if (tdblist.empty())
                 {
-                    printf("No suitable end (C or 3') terminus found in database - assuming this "
-                           "residue\n"
-                           "is already in a terminus-specific form and skipping terminus "
-                           "selection.\n");
+                    GMX_LOG(logger.info)
+                            .asParagraph()
+                            .appendTextFormatted(
+                                    "No suitable end (C or 3') terminus found in database - "
+                                    "assuming this residue"
+                                    "is already in a terminus-specific form and skipping terminus "
+                                    "selection.");
                     cc->ctdb.push_back(nullptr);
                 }
                 else
@@ -2138,7 +2245,7 @@ int pdb2gmx::run()
         /* lookup hackblocks and rtp for all residues */
         std::vector<PreprocessResidue> restp_chain;
         get_hackblocks_rtp(&hb_chain, &restp_chain, rtpFFDB, pdba->nres, pdba->resinfo, cc->nterpairs,
-                           &symtab, cc->ntdb, cc->ctdb, cc->r_start, cc->r_end, bAllowMissing_);
+                           &symtab, cc->ntdb, cc->ctdb, cc->r_start, cc->r_end, bAllowMissing_, logger);
         /* ideally, now we would not need the rtp itself anymore, but do
            everything using the hb and restp arrays. Unfortunately, that
            requires some re-thinking of code in gen_vsite.c, which I won't
@@ -2146,7 +2253,7 @@ int pdb2gmx::run()
 
         rename_atoms(nullptr, ffdir_, pdba, &symtab, restp_chain, false, &rt, false, bVerbose_);
 
-        match_atomnames_with_rtp(restp_chain, hb_chain, pdba, &symtab, x, bVerbose_);
+        match_atomnames_with_rtp(restp_chain, hb_chain, pdba, &symtab, x, bVerbose_, logger);
 
         if (bSort_)
         {
@@ -2154,16 +2261,18 @@ int pdb2gmx::run()
             t_blocka* block = new_blocka();
             snew(gnames, 1);
             sort_pdbatoms(restp_chain, natom, &pdba, &sortAtoms[chain], &x, block, &gnames);
-            remove_duplicate_atoms(pdba, x, bVerbose_);
+            remove_duplicate_atoms(pdba, x, bVerbose_, logger);
             if (bIndexSet_)
             {
                 if (bRemoveH_)
                 {
-                    fprintf(stderr,
-                            "WARNING: with the -remh option the generated "
-                            "index file (%s) might be useless\n"
-                            "(the index file is generated before hydrogens are added)",
-                            indexOutputFile_.c_str());
+                    GMX_LOG(logger.warning)
+                            .asParagraph()
+                            .appendTextFormatted(
+                                    "With the -remh option the generated "
+                                    "index file (%s) might be useless "
+                                    "(the index file is generated before hydrogens are added)",
+                                    indexOutputFile_.c_str());
                 }
                 write_index(indexOutputFile_.c_str(), block, gnames, false, 0);
             }
@@ -2177,16 +2286,22 @@ int pdb2gmx::run()
         }
         else
         {
-            fprintf(stderr,
-                    "WARNING: "
-                    "without sorting no check for duplicate atoms can be done\n");
+            GMX_LOG(logger.warning)
+                    .asParagraph()
+                    .appendTextFormatted(
+                            "Without sorting no check for duplicate atoms can be done");
         }
 
         /* Generate Hydrogen atoms (and termini) in the sequence */
-        printf("Generating any missing hydrogen atoms and/or adding termini.\n");
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted(
+                        "Generating any missing hydrogen atoms and/or adding termini.");
         add_h(&pdba, &localAtoms[chain], &x, ah, &symtab, cc->nterpairs, cc->ntdb, cc->ctdb,
               cc->r_start, cc->r_end, bAllowMissing_);
-        printf("Now there are %d residues with %d atoms\n", pdba->nres, pdba->nr);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Now there are %d residues with %d atoms", pdba->nres, pdba->nr);
 
         /* make up molecule name(s) */
 
@@ -2299,7 +2414,7 @@ int pdb2gmx::run()
         pdb2top(top_file2, posre_fn.c_str(), molname.c_str(), pdba, &x, &atype, &symtab, rtpFFDB,
                 restp_chain, hb_chain, bAllowMissing_, bVsites_, bVsiteAromatics_, ffdir_, mHmult_,
                 ssbonds, long_bond_dist_, short_bond_dist_, bDeuterate_, bChargeGroups_, bCmap_,
-                bRenumRes_, bRTPresname_);
+                bRenumRes_, bRTPresname_, logger);
 
         if (!cc->bAllWat)
         {
@@ -2371,8 +2486,10 @@ int pdb2gmx::run()
     {
         if (numChains > 1)
         {
-            printf("Including chain %d in system: %d atoms %d residues\n", i + 1,
-                   chains[i].pdba->nr, chains[i].pdba->nres);
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted("Including chain %d in system: %d atoms %d residues",
+                                         i + 1, chains[i].pdba->nr, chains[i].pdba->nres);
         }
         for (int j = 0; (j < chains[i].pdba->nr); j++)
         {
@@ -2397,18 +2514,21 @@ int pdb2gmx::run()
 
     if (numChains > 1)
     {
-        fprintf(stderr, "Now there are %d atoms and %d residues\n", k, l);
-        print_sums(atoms, true);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Now there are %d atoms and %d residues", k, l);
+        print_sums(atoms, true, logger);
     }
 
     rvec box_space;
-    fprintf(stderr, "\nWriting coordinate file...\n");
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Writing coordinate file...");
     clear_rvec(box_space);
     if (box[0][0] == 0)
     {
         make_new_box(atoms->nr, gmx::as_rvec_array(x.data()), box, box_space, false);
     }
-    write_sto_conf(outputConfFile_.c_str(), title, atoms, gmx::as_rvec_array(x.data()), nullptr, ePBC, box);
+    write_sto_conf(outputConfFile_.c_str(), title, atoms, gmx::as_rvec_array(x.data()), nullptr,
+                   pbcType, box);
 
     done_symtab(&symtab);
     done_atom(&pdba_all);
@@ -2423,18 +2543,29 @@ int pdb2gmx::run()
     sfree(atoms);
     sfree(title);
     sfree(pdbx);
-    printf("\t\t--------- PLEASE NOTE ------------\n");
-    printf("You have successfully generated a topology from: %s.\n", inputConfFile_.c_str());
+
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted("\t\t--------- PLEASE NOTE ------------");
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted("You have successfully generated a topology from: %s.",
+                                 inputConfFile_.c_str());
     if (watermodel_ != nullptr)
     {
-        printf("The %s force field and the %s water model are used.\n", ffname_, watermodel_);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("The %s force field and the %s water model are used.", ffname_,
+                                     watermodel_);
         sfree(watermodel_);
     }
     else
     {
-        printf("The %s force field is used.\n", ffname_);
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("The %s force field is used.", ffname_);
     }
-    printf("\t\t--------- ETON ESAELP ------------\n");
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted("\t\t--------- ETON ESAELP ------------");
 
     return 0;
 }
index e333b6ea9f32cf9e73cdc7611581797584ac1d95..d4df1c2718368af5f4fd43332399053fc0fe5ec1 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -70,6 +71,7 @@
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/futil.h"
+#include "gromacs/utility/logger.h"
 #include "gromacs/utility/niceheader.h"
 #include "gromacs/utility/path.h"
 #include "gromacs/utility/programcontext.h"
@@ -85,7 +87,7 @@
 /* this must correspond to enum in pdb2top.h */
 const char* hh[ehisNR] = { "HISD", "HISE", "HISH", "HIS1" };
 
-static int missing_atoms(const PreprocessResidue* rp, int resind, t_atoms* at, int i0, int i)
+static int missing_atoms(const PreprocessResidue* rp, int resind, t_atoms* at, int i0, int i, const gmx::MDLogger& logger)
 {
     int nmiss = 0;
     for (int j = 0; j < rp->natom(); j++)
@@ -99,19 +101,20 @@ static int missing_atoms(const PreprocessResidue* rp, int resind, t_atoms* at, i
         if (!bFound)
         {
             nmiss++;
-            fprintf(stderr,
-                    "\nWARNING: "
-                    "atom %s is missing in residue %s %d in the pdb file\n",
-                    name, *(at->resinfo[resind].name), at->resinfo[resind].nr);
+            GMX_LOG(logger.warning)
+                    .asParagraph()
+                    .appendTextFormatted("atom %s is missing in residue %s %d in the pdb file",
+                                         name, *(at->resinfo[resind].name), at->resinfo[resind].nr);
             if (name[0] == 'H' || name[0] == 'h')
             {
-                fprintf(stderr,
-                        "         You might need to add atom %s to the hydrogen database of "
-                        "building block %s\n"
-                        "         in the file %s.hdb (see the manual)\n",
-                        name, *(at->resinfo[resind].rtp), rp->filebase.c_str());
+                GMX_LOG(logger.warning)
+                        .asParagraph()
+                        .appendTextFormatted(
+                                "You might need to add atom %s to the hydrogen database of "
+                                "building block %s "
+                                "in the file %s.hdb (see the manual)",
+                                name, *(at->resinfo[resind].rtp), rp->filebase.c_str());
             }
-            fprintf(stderr, "\n");
         }
     }
 
@@ -132,7 +135,12 @@ bool is_int(double x)
     return (fabs(x - ix) < tol);
 }
 
-static void choose_ff_impl(const char* ffsel, char* forcefield, int ff_maxlen, char* ffdir, int ffdir_maxlen)
+static void choose_ff_impl(const char*          ffsel,
+                           char*                forcefield,
+                           int                  ff_maxlen,
+                           char*                ffdir,
+                           int                  ffdir_maxlen,
+                           const gmx::MDLogger& logger)
 {
     std::vector<gmx::DataFileInfo> ffdirs = fflib_enumerate_forcefields();
     const int                      nff    = ssize(ffdirs);
@@ -183,11 +191,13 @@ static void choose_ff_impl(const char* ffsel, char* forcefield, int ff_maxlen, c
         {
             if (cwdsel != -1)
             {
-                fprintf(stderr,
-                        "Force field '%s' occurs in %d places. pdb2gmx is using the one in the\n"
-                        "current directory. Use interactive selection (not the -ff option) if\n"
-                        "you would prefer a different one.\n",
-                        ffsel, nfound);
+                GMX_LOG(logger.warning)
+                        .asParagraph()
+                        .appendTextFormatted(
+                                "Force field '%s' occurs in %d places. pdb2gmx is using the one in "
+                                "the current directory. Use interactive selection "
+                                "(not the -ff option) if you would prefer a different one.",
+                                ffsel, nfound);
             }
             else
             {
@@ -252,21 +262,25 @@ static void choose_ff_impl(const char* ffsel, char* forcefield, int ff_maxlen, c
             }
         }
 
-        printf("\nSelect the Force Field:\n");
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("Select the Force Field:");
         for (int i = 0; i < nff; ++i)
         {
             if (i == 0 || ffdirs[i - 1].dir != ffdirs[i].dir)
             {
                 if (ffdirs[i].dir == ".")
                 {
-                    printf("From current directory:\n");
+                    GMX_LOG(logger.info)
+                            .asParagraph()
+                            .appendTextFormatted("From current directory:");
                 }
                 else
                 {
-                    printf("From '%s':\n", ffdirs[i].dir.c_str());
+                    GMX_LOG(logger.info)
+                            .asParagraph()
+                            .appendTextFormatted("From '%s':", ffdirs[i].dir.c_str());
                 }
             }
-            printf("%2d: %s\n", i + 1, desc[i].c_str());
+            GMX_LOG(logger.info).asParagraph().appendTextFormatted("%2d: %s", i + 1, desc[i].c_str());
         }
 
         sel = -1;
@@ -337,16 +351,16 @@ static void choose_ff_impl(const char* ffsel, char* forcefield, int ff_maxlen, c
     strcpy(ffdir, ffpath.c_str());
 }
 
-void choose_ff(const char* ffsel, char* forcefield, int ff_maxlen, char* ffdir, int ffdir_maxlen)
+void choose_ff(const char* ffsel, char* forcefield, int ff_maxlen, char* ffdir, int ffdir_maxlen, const gmx::MDLogger& logger)
 {
     try
     {
-        choose_ff_impl(ffsel, forcefield, ff_maxlen, ffdir, ffdir_maxlen);
+        choose_ff_impl(ffsel, forcefield, ff_maxlen, ffdir, ffdir_maxlen, logger);
     }
     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
 }
 
-void choose_watermodel(const char* wmsel, const char* ffdir, char** watermodel)
+void choose_watermodel(const char* wmsel, const char* ffdir, char** watermodel, const gmx::MDLogger& logger)
 {
     const char* fn_watermodels = "watermodels.dat";
     FILE*       fp;
@@ -371,14 +385,16 @@ void choose_watermodel(const char* wmsel, const char* ffdir, char** watermodel)
     std::string filename = gmx::Path::join(ffdir, fn_watermodels);
     if (!fflib_fexist(filename))
     {
-        fprintf(stderr, "No file '%s' found, will not include a water model\n", fn_watermodels);
+        GMX_LOG(logger.warning)
+                .asParagraph()
+                .appendTextFormatted("No file '%s' found, will not include a water model", fn_watermodels);
         *watermodel = nullptr;
 
         return;
     }
 
     fp = fflib_open(filename);
-    printf("\nSelect the Water Model:\n");
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Select the Water Model:");
     nwm   = 0;
     model = nullptr;
     while (get_a_line(fp, buf, STRLEN))
@@ -389,7 +405,7 @@ void choose_watermodel(const char* wmsel, const char* ffdir, char** watermodel)
         if (i > 0)
         {
             ltrim(buf + i);
-            fprintf(stderr, "%2d: %s\n", nwm + 1, buf + i);
+            GMX_LOG(logger.info).asParagraph().appendTextFormatted("%2d: %s", nwm + 1, buf + i);
             nwm++;
         }
         else
@@ -398,7 +414,7 @@ void choose_watermodel(const char* wmsel, const char* ffdir, char** watermodel)
         }
     }
     gmx_ffclose(fp);
-    fprintf(stderr, "%2d: %s\n", nwm + 1, "None");
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("%2d: %s", nwm + 1, "None");
 
     sel = -1;
     do
@@ -428,7 +444,11 @@ void choose_watermodel(const char* wmsel, const char* ffdir, char** watermodel)
     sfree(model);
 }
 
-static int name2type(t_atoms* at, int** cgnr, gmx::ArrayRef<const PreprocessResidue> usedPpResidues, ResidueType* rt)
+static int name2type(t_atoms*                               at,
+                     int**                                  cgnr,
+                     gmx::ArrayRef<const PreprocessResidue> usedPpResidues,
+                     ResidueType*                           rt,
+                     const gmx::MDLogger&                   logger)
 {
     int    i, j, prevresind, i0, prevcg, cg, curcg;
     char*  name;
@@ -456,7 +476,7 @@ static int name2type(t_atoms* at, int** cgnr, gmx::ArrayRef<const PreprocessResi
             bNterm     = bProt && (resind == 0);
             if (resind > 0)
             {
-                nmissat += missing_atoms(&usedPpResidues[prevresind], prevresind, at, i0, i);
+                nmissat += missing_atoms(&usedPpResidues[prevresind], prevresind, at, i0, i, logger);
             }
             i0 = i;
         }
@@ -493,7 +513,7 @@ static int name2type(t_atoms* at, int** cgnr, gmx::ArrayRef<const PreprocessResi
         at->atom[i].qB    = at->atom[i].q;
         at->atom[i].mB    = at->atom[i].m;
     }
-    nmissat += missing_atoms(&usedPpResidues[resind], resind, at, i0, i);
+    nmissat += missing_atoms(&usedPpResidues[resind], resind, at, i0, i, logger);
 
     return nmissat;
 }
@@ -735,7 +755,8 @@ static void at2bonds(InteractionsOfType*                  psb,
                      t_atoms*                             atoms,
                      gmx::ArrayRef<const gmx::RVec>       x,
                      real                                 long_bond_dist,
-                     real                                 short_bond_dist)
+                     real                                 short_bond_dist,
+                     const gmx::MDLogger&                 logger)
 {
     real        long_bond_dist2, short_bond_dist2;
     const char* ptr;
@@ -752,7 +773,7 @@ static void at2bonds(InteractionsOfType*                  psb,
         ptr = "check";
     }
 
-    fprintf(stderr, "Making bonds...\n");
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Making bonds...");
     int i = 0;
     for (int resind = 0; (resind < atoms->nres) && (i < atoms->nr); resind++)
     {
@@ -771,13 +792,17 @@ static void at2bonds(InteractionsOfType*                  psb,
                 if (dist2 > long_bond_dist2)
 
                 {
-                    fprintf(stderr, "Warning: Long Bond (%d-%d = %g nm)\n", ai + 1, aj + 1,
-                            std::sqrt(dist2));
+                    GMX_LOG(logger.warning)
+                            .asParagraph()
+                            .appendTextFormatted("Long Bond (%d-%d = %g nm)", ai + 1, aj + 1,
+                                                 std::sqrt(dist2));
                 }
                 else if (dist2 < short_bond_dist2)
                 {
-                    fprintf(stderr, "Warning: Short Bond (%d-%d = %g nm)\n", ai + 1, aj + 1,
-                            std::sqrt(dist2));
+                    GMX_LOG(logger.warning)
+                            .asParagraph()
+                            .appendTextFormatted("Short Bond (%d-%d = %g nm)", ai + 1, aj + 1,
+                                                 std::sqrt(dist2));
                 }
                 add_param(psb, ai, aj, {}, patch.s.c_str());
             }
@@ -815,11 +840,7 @@ static bool pcompar(const InteractionOfType& a, const InteractionOfType& b)
 {
     int d;
 
-    if ((d = a.ai() - b.ai()) != 0)
-    {
-        return d < 0;
-    }
-    else if ((d = a.aj() - b.aj()) != 0)
+    if (((d = a.ai() - b.ai()) != 0) || ((d = a.aj() - b.aj()) != 0))
     {
         return d < 0;
     }
@@ -829,7 +850,7 @@ static bool pcompar(const InteractionOfType& a, const InteractionOfType& b)
     }
 }
 
-static void clean_bonds(InteractionsOfType* ps)
+static void clean_bonds(InteractionsOfType* ps, const gmx::MDLogger& logger)
 {
     if (ps->size() > 0)
     {
@@ -854,15 +875,17 @@ static void clean_bonds(InteractionsOfType* ps)
                 ++parm;
             }
         }
-        fprintf(stderr, "Number of bonds was %d, now %zu\n", oldNumber, ps->size());
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Number of bonds was %d, now %zu", oldNumber, ps->size());
     }
     else
     {
-        fprintf(stderr, "No bonds\n");
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("No bonds");
     }
 }
 
-void print_sums(const t_atoms* atoms, bool bSystem)
+void print_sums(const t_atoms* atoms, bool bSystem, const gmx::MDLogger& logger)
 {
     double      m, qtot;
     int         i;
@@ -885,8 +908,8 @@ void print_sums(const t_atoms* atoms, bool bSystem)
         qtot += atoms->atom[i].q;
     }
 
-    fprintf(stderr, "Total mass%s %.3f a.m.u.\n", where, m);
-    fprintf(stderr, "Total charge%s %.3f e\n", where, qtot);
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Total mass%s %.3f a.m.u.", where, m);
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Total charge%s %.3f e", where, qtot);
 }
 
 static void check_restp_type(const char* name, int t1, int t2)
@@ -953,7 +976,8 @@ void get_hackblocks_rtp(std::vector<MoleculePatchDatabase>*    globalPatches,
                         gmx::ArrayRef<MoleculePatchDatabase*>  ctdb,
                         gmx::ArrayRef<const int>               rn,
                         gmx::ArrayRef<const int>               rc,
-                        bool                                   bAllowMissing)
+                        bool                                   bAllowMissing,
+                        const gmx::MDLogger&                   logger)
 {
     char* key;
     bool  bRM;
@@ -985,7 +1009,7 @@ void get_hackblocks_rtp(std::vector<MoleculePatchDatabase>*    globalPatches,
          */
         key = *resinfo[i].rtp;
 
-        resinfo[i].rtp = put_symtab(symtab, searchResidueDatabase(key, rtpFFDB).c_str());
+        resinfo[i].rtp = put_symtab(symtab, searchResidueDatabase(key, rtpFFDB, logger).c_str());
         auto res       = getDatabaseEntry(*resinfo[i].rtp, rtpFFDB);
         usedPpResidues->push_back(PreprocessResidue());
         PreprocessResidue* newentry = &usedPpResidues->back();
@@ -1021,7 +1045,7 @@ void get_hackblocks_rtp(std::vector<MoleculePatchDatabase>*    globalPatches,
                     "terminal database entries (.tdb).";
             if (bAllowMissing)
             {
-                fprintf(stderr, "%s\n", errString);
+                GMX_LOG(logger.warning).asParagraph().appendTextFormatted("%s", errString);
             }
             else
             {
@@ -1036,7 +1060,7 @@ void get_hackblocks_rtp(std::vector<MoleculePatchDatabase>*    globalPatches,
                     "proper existing terminal entry.";
             if (bAllowMissing)
             {
-                fprintf(stderr, "%s\n", errString);
+                GMX_LOG(logger.warning).asParagraph().appendTextFormatted("%s", errString);
             }
             else
             {
@@ -1156,7 +1180,8 @@ static bool match_atomnames_with_rtp_atom(t_atoms*                     pdba,
                                           int                          atind,
                                           PreprocessResidue*           localPpResidue,
                                           const MoleculePatchDatabase& singlePatch,
-                                          bool                         bVerbose)
+                                          bool                         bVerbose,
+                                          const gmx::MDLogger&         logger)
 {
     int   resnr;
     char* oldnm;
@@ -1258,8 +1283,10 @@ static bool match_atomnames_with_rtp_atom(t_atoms*                     pdba,
 
             if (bVerbose)
             {
-                printf("Renaming atom '%s' in residue '%s' %d to '%s'\n", oldnm,
-                       localPpResidue->resname.c_str(), resnr, newnm.c_str());
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted("Renaming atom '%s' in residue '%s' %d to '%s'", oldnm,
+                                             localPpResidue->resname.c_str(), resnr, newnm.c_str());
             }
             /* Rename the atom in pdba */
             pdba->atomname[atind] = put_symtab(symtab, newnm.c_str());
@@ -1280,8 +1307,10 @@ static bool match_atomnames_with_rtp_atom(t_atoms*                     pdba,
                  */
                 if (bVerbose)
                 {
-                    printf("Deleting atom '%s' in residue '%s' %d\n", oldnm,
-                           localPpResidue->resname.c_str(), resnr);
+                    GMX_LOG(logger.info)
+                            .asParagraph()
+                            .appendTextFormatted("Deleting atom '%s' in residue '%s' %d", oldnm,
+                                                 localPpResidue->resname.c_str(), resnr);
                 }
                 /* We should free the atom name,
                  * but it might be used multiple times in the symtab.
@@ -1307,7 +1336,8 @@ void match_atomnames_with_rtp(gmx::ArrayRef<PreprocessResidue>     usedPpResidue
                               t_atoms*                             pdba,
                               t_symtab*                            symtab,
                               gmx::ArrayRef<gmx::RVec>             x,
-                              bool                                 bVerbose)
+                              bool                                 bVerbose,
+                              const gmx::MDLogger&                 logger)
 {
     for (int i = 0; i < pdba->nr; i++)
     {
@@ -1320,7 +1350,7 @@ void match_atomnames_with_rtp(gmx::ArrayRef<PreprocessResidue>     usedPpResidue
         {
             /* Not found yet, check if we have to rename this atom */
             if (match_atomnames_with_rtp_atom(pdba, x, symtab, i, localPpResidue,
-                                              globalPatches[pdba->atom[i].resind], bVerbose))
+                                              globalPatches[pdba->atom[i].resind], bVerbose, logger))
             {
                 /* We deleted this atom, decrease the atom counter by 1. */
                 i--;
@@ -1330,7 +1360,10 @@ void match_atomnames_with_rtp(gmx::ArrayRef<PreprocessResidue>     usedPpResidue
 }
 
 #define NUM_CMAP_ATOMS 5
-static void gen_cmap(InteractionsOfType* psb, gmx::ArrayRef<const PreprocessResidue> usedPpResidues, t_atoms* atoms)
+static void gen_cmap(InteractionsOfType*                    psb,
+                     gmx::ArrayRef<const PreprocessResidue> usedPpResidues,
+                     t_atoms*                               atoms,
+                     const gmx::MDLogger&                   logger)
 {
     int         residx;
     const char* ptr;
@@ -1348,7 +1381,7 @@ static void gen_cmap(InteractionsOfType* psb, gmx::ArrayRef<const PreprocessResi
         ptr = "check";
     }
 
-    fprintf(stderr, "Making cmap torsions...\n");
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Making cmap torsions...");
     int i = 0;
     /* Most cmap entries use the N atom from the next residue, so the last
      * residue should not have its CMAP entry in that case, but for things like
@@ -1465,7 +1498,8 @@ void pdb2top(FILE*                                  top_file,
              bool                                   bChargeGroups,
              bool                                   bCmap,
              bool                                   bRenumRes,
-             bool                                   bRTPresname)
+             bool                                   bRTPresname,
+             const gmx::MDLogger&                   logger)
 {
     std::array<InteractionsOfType, F_NRE> plist;
     t_excls*                              excls;
@@ -1477,17 +1511,19 @@ void pdb2top(FILE*                                  top_file,
     ResidueType rt;
 
     /* Make bonds */
-    at2bonds(&(plist[F_BONDS]), globalPatches, atoms, *x, long_bond_dist, short_bond_dist);
+    at2bonds(&(plist[F_BONDS]), globalPatches, atoms, *x, long_bond_dist, short_bond_dist, logger);
 
     /* specbonds: disulphide bonds & heme-his */
     do_ssbonds(&(plist[F_BONDS]), atoms, ssbonds, bAllowMissing);
 
-    nmissat = name2type(atoms, &cgnr, usedPpResidues, &rt);
+    nmissat = name2type(atoms, &cgnr, usedPpResidues, &rt, logger);
     if (nmissat)
     {
         if (bAllowMissing)
         {
-            fprintf(stderr, "There were %d missing atoms in molecule %s\n", nmissat, molname);
+            GMX_LOG(logger.warning)
+                    .asParagraph()
+                    .appendTextFormatted("There were %d missing atoms in molecule %s", nmissat, molname);
         }
         else
         {
@@ -1499,7 +1535,7 @@ void pdb2top(FILE*                                  top_file,
     }
 
     /* Cleanup bonds (sort and rm doubles) */
-    clean_bonds(&(plist[F_BONDS]));
+    clean_bonds(&(plist[F_BONDS]), logger);
 
     snew(vsite_type, atoms->nr);
     for (i = 0; i < atoms->nr; i++)
@@ -1510,9 +1546,11 @@ void pdb2top(FILE*                                  top_file,
     {
         if (bVsiteAromatics)
         {
-            fprintf(stdout,
-                    "The conversion of aromatic rings into virtual sites is deprecated "
-                    "and may be removed in a future version of GROMACS");
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted(
+                            "The conversion of aromatic rings into virtual sites is deprecated "
+                            "and may be removed in a future version of GROMACS");
         }
         /* determine which atoms will be vsites and add dummy masses
            also renumber atom numbers in plist[0..F_NRE]! */
@@ -1520,17 +1558,21 @@ void pdb2top(FILE*                                  top_file,
     }
 
     /* Make Angles and Dihedrals */
-    fprintf(stderr, "Generating angles, dihedrals and pairs...\n");
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted("Generating angles, dihedrals and pairs...");
     snew(excls, atoms->nr);
     gen_pad(atoms, usedPpResidues, plist, excls, globalPatches, bAllowMissing);
 
     /* Make CMAP */
     if (bCmap)
     {
-        gen_cmap(&(plist[F_CMAP]), usedPpResidues, atoms);
+        gen_cmap(&(plist[F_CMAP]), usedPpResidues, atoms, logger);
         if (plist[F_CMAP].size() > 0)
         {
-            fprintf(stderr, "There are %4zu cmap torsion pairs\n", plist[F_CMAP].size());
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted("There are %4zu cmap torsion pairs", plist[F_CMAP].size());
         }
     }
 
@@ -1544,16 +1586,18 @@ void pdb2top(FILE*                                  top_file,
     /* Cleanup bonds (sort and rm doubles) */
     /* clean_bonds(&(plist[F_BONDS]));*/
 
-    fprintf(stderr,
-            "There are %4zu dihedrals, %4zu impropers, %4zu angles\n"
-            "          %4zu pairs,     %4zu bonds and  %4zu virtual sites\n",
-            plist[F_PDIHS].size(), plist[F_IDIHS].size(), plist[F_ANGLES].size(),
-            plist[F_LJ14].size(), plist[F_BONDS].size(),
-            plist[F_VSITE2].size() + plist[F_VSITE3].size() + plist[F_VSITE3FD].size()
-                    + plist[F_VSITE3FAD].size() + plist[F_VSITE3OUT].size()
-                    + plist[F_VSITE4FD].size() + plist[F_VSITE4FDN].size());
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted(
+                    "There are %4zu dihedrals, %4zu impropers, %4zu angles\n"
+                    "          %4zu pairs,     %4zu bonds and  %4zu virtual sites",
+                    plist[F_PDIHS].size(), plist[F_IDIHS].size(), plist[F_ANGLES].size(),
+                    plist[F_LJ14].size(), plist[F_BONDS].size(),
+                    plist[F_VSITE2].size() + plist[F_VSITE3].size() + plist[F_VSITE3FD].size()
+                            + plist[F_VSITE3FAD].size() + plist[F_VSITE3OUT].size()
+                            + plist[F_VSITE4FD].size() + plist[F_VSITE4FDN].size());
 
-    print_sums(atoms, FALSE);
+    print_sums(atoms, FALSE, logger);
 
     if (!bChargeGroups)
     {
@@ -1571,7 +1615,7 @@ void pdb2top(FILE*                                  top_file,
 
     if (top_file)
     {
-        fprintf(stderr, "Writing topology\n");
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("Writing topology");
         /* We can copy the bonded types from the first restp,
          * since the types have to be identical for all residues in one molecule.
          */
index 85538e3ea7b3d286c488fbbec24838f0e3bba68b..b9dd8f8fa4fc59bea9d9df71d8dbf796ea8a6092 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <cstdio>
 
+#include <string>
 #include <vector>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 
 class PreprocessingAtomTypes;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+} // namespace gmx
+
 struct t_atoms;
 struct t_excls;
 struct MoleculePatchDatabase;
@@ -67,13 +75,18 @@ enum
 };
 extern const char* hh[ehisNR];
 
-void choose_ff(const char* ffsel, char* forcefield, int ff_maxlen, char* ffdir, int ffdir_maxlen);
+void choose_ff(const char*          ffsel,
+               char*                forcefield,
+               int                  ff_maxlen,
+               char*                ffdir,
+               int                  ffdir_maxlen,
+               const gmx::MDLogger& logger);
 /* Find force fields in the current and libdirs and choose an ff.
  * If ffsel!=NULL: search for ffsel.
  * If ffsel==NULL: interactive selection.
  */
 
-void choose_watermodel(const char* wmsel, const char* ffdir, char** watermodel);
+void choose_watermodel(const char* wmsel, const char* ffdir, char** watermodel, const gmx::MDLogger& logger);
 /* Choose, possibly interactively, which water model to include,
  * based on the wmsel command line option choice and watermodels.dat
  * in ffdir.
@@ -90,7 +103,8 @@ void get_hackblocks_rtp(std::vector<MoleculePatchDatabase>*    globalPatches,
                         gmx::ArrayRef<MoleculePatchDatabase*>  ctdb,
                         gmx::ArrayRef<const int>               rn,
                         gmx::ArrayRef<const int>               rc,
-                        bool                                   bAllowMissing);
+                        bool                                   bAllowMissing,
+                        const gmx::MDLogger&                   logger);
 /* Get the database entries for the nres residues in resinfo
  * and store them in restp and hb.
  */
@@ -100,7 +114,8 @@ void match_atomnames_with_rtp(gmx::ArrayRef<PreprocessResidue>     usedPpResidue
                               t_atoms*                             pdba,
                               t_symtab*                            symtab,
                               gmx::ArrayRef<gmx::RVec>             x,
-                              bool                                 bVerbose);
+                              bool                                 bVerbose,
+                              const gmx::MDLogger&                 logger);
 /* Check if atom in pdba need to be deleted of renamed due to tdb or hdb.
  * If renaming involves atoms added wrt to the rtp database,
  * add these atoms to restp.
@@ -152,9 +167,10 @@ void pdb2top(FILE*                                  top_file,
              bool                                   bChargeGroups,
              bool                                   bCmap,
              bool                                   bRenumRes,
-             bool                                   bRTPresname);
+             bool                                   bRTPresname,
+             const gmx::MDLogger&                   logger);
 /* Create a topology ! */
 
-void print_sums(const t_atoms* atoms, bool bSystem);
+void print_sums(const t_atoms* atoms, bool bSystem, const gmx::MDLogger& logger);
 
 #endif
index 1584f9f0bec885fe2756676d7e2ef6af3f7e21e2..cced555d25f0f2e14b653d53f057a5a5203b9970 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 9c6d01670a6b6e3b7678cd6f3c066a202a4fc23b..241ef2d64d7a1274294376bca1508d9736067267 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013-2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017, The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,7 +47,7 @@
 #include <algorithm>
 #include <string>
 
-#include "gromacs/awh/read_params.h"
+#include "gromacs/applied_forces/awh/read_params.h"
 #include "gromacs/fileio/readinp.h"
 #include "gromacs/fileio/warninp.h"
 #include "gromacs/gmxlib/network.h"
@@ -58,6 +59,7 @@
 #include "gromacs/mdrun/mdmodules.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/multipletimestepping.h"
 #include "gromacs/mdtypes/pull_params.h"
 #include "gromacs/options/options.h"
 #include "gromacs/options/treesupport.h"
  * message.
  */
 
-typedef struct t_inputrec_strings
+struct gmx_inputrec_strings
 {
     char tcgrps[STRLEN], tau_t[STRLEN], ref_t[STRLEN], acc[STRLEN], accgrps[STRLEN], freeze[STRLEN],
             frdim[STRLEN], energy[STRLEN], user1[STRLEN], user2[STRLEN], vcm[STRLEN],
             x_compressed_groups[STRLEN], couple_moltype[STRLEN], orirefitgrp[STRLEN],
             egptable[STRLEN], egpexcl[STRLEN], wall_atomtype[STRLEN], wall_density[STRLEN],
             deform[STRLEN], QMMM[STRLEN], imd_grp[STRLEN];
-    char   fep_lambda[efptNR][STRLEN];
-    char   lambda_weights[STRLEN];
-    char** pull_grp;
-    char** rot_grp;
-    char   anneal[STRLEN], anneal_npoints[STRLEN], anneal_time[STRLEN], anneal_temp[STRLEN];
-    char   QMmethod[STRLEN], QMbasis[STRLEN], QMcharge[STRLEN], QMmult[STRLEN], bSH[STRLEN],
+    char                     fep_lambda[efptNR][STRLEN];
+    char                     lambda_weights[STRLEN];
+    std::vector<std::string> pullGroupNames;
+    std::vector<std::string> rotateGroupNames;
+    char anneal[STRLEN], anneal_npoints[STRLEN], anneal_time[STRLEN], anneal_temp[STRLEN];
+    char QMmethod[STRLEN], QMbasis[STRLEN], QMcharge[STRLEN], QMmult[STRLEN], bSH[STRLEN],
             CASorbitals[STRLEN], CASelectrons[STRLEN], SAon[STRLEN], SAoff[STRLEN], SAsteps[STRLEN];
+};
 
-} gmx_inputrec_strings;
-
-static gmx_inputrec_strings* is = nullptr;
+static gmx_inputrec_strings* inputrecStrings = nullptr;
 
 void init_inputrec_strings()
 {
-    if (is)
+    if (inputrecStrings)
     {
         gmx_incons(
                 "Attempted to call init_inputrec_strings before calling done_inputrec_strings. "
                 "Only one inputrec (i.e. .mdp file) can be parsed at a time.");
     }
-    snew(is, 1);
+    inputrecStrings = new gmx_inputrec_strings();
 }
 
 void done_inputrec_strings()
 {
-    sfree(is);
-    is = nullptr;
+    delete inputrecStrings;
+    inputrecStrings = nullptr;
 }
 
 
@@ -232,6 +233,89 @@ static void process_interaction_modifier(int* eintmod)
     }
 }
 
+static void checkMtsRequirement(const t_inputrec& ir, const char* param, const int nstValue, warninp_t wi)
+{
+    GMX_RELEASE_ASSERT(ir.mtsLevels.size() >= 2, "Need at least two levels for MTS");
+    const int mtsFactor = ir.mtsLevels.back().stepFactor;
+    if (nstValue % mtsFactor != 0)
+    {
+        auto message = gmx::formatString(
+                "With MTS, %s = %d should be a multiple of mts-factor = %d", param, nstValue, mtsFactor);
+        warning_error(wi, message.c_str());
+    }
+}
+
+static void setupMtsLevels(gmx::ArrayRef<gmx::MtsLevel> mtsLevels,
+                           const t_inputrec&            ir,
+                           const t_gromppopts&          opts,
+                           warninp_t                    wi)
+{
+    if (!(ir.eI == eiMD || ir.eI == eiSD1))
+    {
+        auto message = gmx::formatString(
+                "Multiple time stepping is only supported with integrators %s and %s",
+                ei_names[eiMD], ei_names[eiSD1]);
+        warning_error(wi, message.c_str());
+    }
+    if (opts.numMtsLevels != 2)
+    {
+        warning_error(wi, "Only mts-levels = 2 is supported");
+    }
+    else
+    {
+        const std::vector<std::string> inputForceGroups = gmx::splitString(opts.mtsLevel2Forces);
+        auto&                          forceGroups      = mtsLevels[1].forceGroups;
+        for (const auto& inputForceGroup : inputForceGroups)
+        {
+            bool found     = false;
+            int  nameIndex = 0;
+            for (const auto& forceGroupName : gmx::mtsForceGroupNames)
+            {
+                if (gmx::equalCaseInsensitive(inputForceGroup, forceGroupName))
+                {
+                    forceGroups.set(nameIndex);
+                    found = true;
+                }
+                nameIndex++;
+            }
+            if (!found)
+            {
+                auto message =
+                        gmx::formatString("Unknown MTS force group '%s'", inputForceGroup.c_str());
+                warning_error(wi, message.c_str());
+            }
+        }
+
+        if (mtsLevels[1].stepFactor <= 1)
+        {
+            gmx_fatal(FARGS, "mts-factor should be larger than 1");
+        }
+
+        // Make the level 0 use the complement of the force groups of group 1
+        mtsLevels[0].forceGroups = ~mtsLevels[1].forceGroups;
+        mtsLevels[0].stepFactor  = 1;
+
+        if ((EEL_FULL(ir.coulombtype) || EVDW_PME(ir.vdwtype))
+            && !mtsLevels[1].forceGroups[static_cast<int>(gmx::MtsForceGroups::LongrangeNonbonded)])
+        {
+            warning_error(wi,
+                          "With long-range electrostatics and/or LJ treatment, the long-range part "
+                          "has to be part of the mts-level2-forces");
+        }
+
+        if (ir.nstcalcenergy > 0)
+        {
+            checkMtsRequirement(ir, "nstcalcenergy", ir.nstcalcenergy, wi);
+        }
+        checkMtsRequirement(ir, "nstenergy", ir.nstenergy, wi);
+        checkMtsRequirement(ir, "nstlog", ir.nstlog, wi);
+        if (ir.efep != efepNO)
+        {
+            checkMtsRequirement(ir, "nstdhdl", ir.fepvals->nstdhdl, wi);
+        }
+    }
+}
+
 void check_ir(const char*                   mdparin,
               const gmx::MdModulesNotifier& mdModulesNotifier,
               t_inputrec*                   ir,
@@ -537,6 +621,12 @@ void check_ir(const char*                   mdparin,
             {
                 ir->nstpcouple = ir_optimal_nstpcouple(ir);
             }
+            if (ir->useMts && ir->nstpcouple % ir->mtsLevels.back().stepFactor != 0)
+            {
+                warning_error(wi,
+                              "With multiple time stepping, nstpcouple should be a mutiple of "
+                              "mts-factor");
+            }
         }
 
         if (ir->nstcalcenergy > 0)
@@ -561,7 +651,7 @@ void check_ir(const char*                   mdparin,
         // Inquire all MdModules, if their parameters match with the energy
         // calculation frequency
         gmx::EnergyCalculationFrequencyErrors energyCalculationFrequencyErrors(ir->nstcalcenergy);
-        mdModulesNotifier.notifier_.notify(&energyCalculationFrequencyErrors);
+        mdModulesNotifier.preProcessingNotifications_.notify(&energyCalculationFrequencyErrors);
 
         // Emit all errors from the energy calculation frequency checks
         for (const std::string& energyFrequencyErrorMessage :
@@ -589,8 +679,8 @@ void check_ir(const char*                   mdparin,
     /* TPI STUFF */
     if (EI_TPI(ir->eI))
     {
-        sprintf(err_buf, "TPI only works with pbc = %s", epbc_names[epbcXYZ]);
-        CHECK(ir->ePBC != epbcXYZ);
+        sprintf(err_buf, "TPI only works with pbc = %s", c_pbcTypeNames[PbcType::Xyz].c_str());
+        CHECK(ir->pbcType != PbcType::Xyz);
         sprintf(err_buf, "with TPI nstlist should be larger than zero");
         CHECK(ir->nstlist <= 0);
         sprintf(err_buf, "TPI does not work with full electrostatics other than PME");
@@ -667,9 +757,11 @@ void check_ir(const char*                   mdparin,
         sprintf(err_buf, "The soft-core power is %d and can only be 1 or 2", fep->sc_power);
         CHECK(fep->sc_alpha != 0 && fep->sc_power != 1 && fep->sc_power != 2);
 
-        sprintf(err_buf, "The soft-core sc-r-power is %d and can only be 6 or 48",
+        sprintf(err_buf,
+                "The soft-core sc-r-power is %d and can only be 6. (sc-r-power 48 is no longer "
+                "supported.)",
                 static_cast<int>(fep->sc_r_power));
-        CHECK(fep->sc_alpha != 0 && fep->sc_r_power != 6.0 && fep->sc_r_power != 48.0);
+        CHECK(fep->sc_alpha != 0 && fep->sc_r_power != 6.0);
 
         sprintf(err_buf,
                 "Can't use positive delta-lambda (%g) if initial state/lambda does not start at "
@@ -910,13 +1002,13 @@ void check_ir(const char*                   mdparin,
     }
 
     /* PBC/WALLS */
-    sprintf(err_buf, "walls only work with pbc=%s", epbc_names[epbcXY]);
-    CHECK(ir->nwall && ir->ePBC != epbcXY);
+    sprintf(err_buf, "walls only work with pbc=%s", c_pbcTypeNames[PbcType::XY].c_str());
+    CHECK(ir->nwall && ir->pbcType != PbcType::XY);
 
     /* VACUUM STUFF */
-    if (ir->ePBC != epbcXYZ && ir->nwall != 2)
+    if (ir->pbcType != PbcType::Xyz && ir->nwall != 2)
     {
-        if (ir->ePBC == epbcNONE)
+        if (ir->pbcType == PbcType::No)
         {
             if (ir->epc != epcNO)
             {
@@ -926,13 +1018,15 @@ void check_ir(const char*                   mdparin,
         }
         else
         {
-            sprintf(err_buf, "Can not have pressure coupling with pbc=%s", epbc_names[ir->ePBC]);
+            sprintf(err_buf, "Can not have pressure coupling with pbc=%s",
+                    c_pbcTypeNames[ir->pbcType].c_str());
             CHECK(ir->epc != epcNO);
         }
-        sprintf(err_buf, "Can not have Ewald with pbc=%s", epbc_names[ir->ePBC]);
+        sprintf(err_buf, "Can not have Ewald with pbc=%s", c_pbcTypeNames[ir->pbcType].c_str());
         CHECK(EEL_FULL(ir->coulombtype));
 
-        sprintf(err_buf, "Can not have dispersion correction with pbc=%s", epbc_names[ir->ePBC]);
+        sprintf(err_buf, "Can not have dispersion correction with pbc=%s",
+                c_pbcTypeNames[ir->pbcType].c_str());
         CHECK(ir->eDispCorr != edispcNO);
     }
 
@@ -943,9 +1037,9 @@ void check_ir(const char*                   mdparin,
                 "with coulombtype = %s or coulombtype = %s\n"
                 "without periodic boundary conditions (pbc = %s) and\n"
                 "rcoulomb and rvdw set to zero",
-                eel_names[eelCUT], eel_names[eelUSER], epbc_names[epbcNONE]);
+                eel_names[eelCUT], eel_names[eelUSER], c_pbcTypeNames[PbcType::No].c_str());
         CHECK(((ir->coulombtype != eelCUT) && (ir->coulombtype != eelUSER))
-              || (ir->ePBC != epbcNONE) || (ir->rcoulomb != 0.0) || (ir->rvdw != 0.0));
+              || (ir->pbcType != PbcType::No) || (ir->rcoulomb != 0.0) || (ir->rvdw != 0.0));
 
         if (ir->nstlist > 0)
         {
@@ -991,7 +1085,7 @@ void check_ir(const char*                   mdparin,
                     "Can not remove the rotation around the center of mass with periodic "
                     "molecules");
             CHECK(ir->bPeriodicMols);
-            if (ir->ePBC != epbcNONE)
+            if (ir->pbcType != PbcType::No)
             {
                 warning(wi,
                         "Removing the rotation around the center of mass in a periodic system, "
@@ -1001,7 +1095,7 @@ void check_ir(const char*                   mdparin,
         }
     }
 
-    if (EI_STATE_VELOCITY(ir->eI) && !EI_SD(ir->eI) && ir->ePBC == epbcNONE && ir->comm_mode != ecmANGULAR)
+    if (EI_STATE_VELOCITY(ir->eI) && !EI_SD(ir->eI) && ir->pbcType == PbcType::No && ir->comm_mode != ecmANGULAR)
     {
         sprintf(warn_buf,
                 "Tumbling and flying ice-cubes: We are not removing rotation around center of mass "
@@ -1308,18 +1402,18 @@ void check_ir(const char*                   mdparin,
     {
         if (ir->ewald_geometry == eewg3D)
         {
-            sprintf(warn_buf, "With pbc=%s you should use ewald-geometry=%s", epbc_names[ir->ePBC],
-                    eewg_names[eewg3DC]);
+            sprintf(warn_buf, "With pbc=%s you should use ewald-geometry=%s",
+                    c_pbcTypeNames[ir->pbcType].c_str(), eewg_names[eewg3DC]);
             warning(wi, warn_buf);
         }
         /* This check avoids extra pbc coding for exclusion corrections */
         sprintf(err_buf, "wall-ewald-zfac should be >= 2");
         CHECK(ir->wall_ewald_zfac < 2);
     }
-    if ((ir->ewald_geometry == eewg3DC) && (ir->ePBC != epbcXY) && EEL_FULL(ir->coulombtype))
+    if ((ir->ewald_geometry == eewg3DC) && (ir->pbcType != PbcType::XY) && EEL_FULL(ir->coulombtype))
     {
         sprintf(warn_buf, "With %s and ewald_geometry = %s you should use pbc = %s",
-                eel_names[ir->coulombtype], eewg_names[eewg3DC], epbc_names[epbcXY]);
+                eel_names[ir->coulombtype], eewg_names[eewg3DC], c_pbcTypeNames[PbcType::XY].c_str());
         warning(wi, warn_buf);
     }
     if ((ir->epsilon_surface != 0) && EEL_FULL(ir->coulombtype))
@@ -1389,13 +1483,7 @@ void check_ir(const char*                   mdparin,
 
     if (ir->bQMMM)
     {
-        warning_error(wi, "QMMM is currently not supported");
-        if (!EI_DYNAMICS(ir->eI))
-        {
-            char buf[STRLEN];
-            sprintf(buf, "QMMM is only supported with dynamics, not with integrator %s", ei_names[ir->eI]);
-            warning_error(wi, buf);
-        }
+        warning_error(wi, "The QMMM integration you are trying to use is no longer supported");
     }
 
     if (ir->bAdress)
@@ -1564,17 +1652,6 @@ static void do_fep_params(t_inputrec* ir, char fep_lambda[][STRLEN], char weight
     }
 
 
-    /* make it easier if sc_r_power = 48 by increasing it to the 4th power, to be in the right scale. */
-    if (fep->sc_r_power == 48)
-    {
-        if (fep->sc_alpha > 0.1)
-        {
-            gmx_fatal(FARGS,
-                      "sc_alpha (%f) for sc_r_power = 48 should usually be between 0.001 and 0.004",
-                      fep->sc_alpha);
-        }
-    }
-
     /* now read in the weights */
     parse_n_real(weights, &nweights, &(expand->init_lambda_weights), wi);
     if (nweights == 0)
@@ -1601,19 +1678,6 @@ static void do_simtemp_params(t_inputrec* ir)
     GetSimTemps(ir->fepvals->n_lambda, ir->simtempvals, ir->fepvals->all_lambda[efptTEMPERATURE]);
 }
 
-static void convertYesNos(warninp_t /*wi*/,
-                          gmx::ArrayRef<const std::string> inputs,
-                          const char* /*name*/,
-                          gmx_bool* outputs)
-{
-    int i = 0;
-    for (const auto& input : inputs)
-    {
-        outputs[i] = gmx::equalCaseInsensitive(input, "Y", 1);
-        ++i;
-    }
-}
-
 template<typename T>
 void convertInts(warninp_t wi, gmx::ArrayRef<const std::string> inputs, const char* name, T* outputs)
 {
@@ -1701,6 +1765,7 @@ static void do_wall_params(t_inputrec* ir, char* wall_atomtype, char* wall_densi
             gmx_fatal(FARGS, "Expected %d elements for wall_atomtype, found %zu", ir->nwall,
                       wallAtomTypes.size());
         }
+        GMX_RELEASE_ASSERT(ir->nwall < 3, "Invalid number of walls");
         for (int i = 0; i < ir->nwall; i++)
         {
             opts->wall_atomtype[i] = gmx_strdup(wallAtomTypes[i].c_str());
@@ -1908,12 +1973,30 @@ void get_ir(const char*     mdparin,
     printStringNoNewline(
             &inp, "Part index is updated automatically on checkpointing (keeps files separate)");
     ir->simulation_part = get_eint(&inp, "simulation-part", 1, wi);
+    printStringNoNewline(&inp, "Multiple time-stepping");
+    ir->useMts = (get_eeenum(&inp, "mts", yesno_names, wi) != 0);
+    if (ir->useMts)
+    {
+        opts->numMtsLevels = get_eint(&inp, "mts-levels", 2, wi);
+        ir->mtsLevels.resize(2);
+        gmx::MtsLevel& mtsLevel = ir->mtsLevels[1];
+        opts->mtsLevel2Forces   = setStringEntry(&inp, "mts-level2-forces",
+                                               "longrange-nonbonded nonbonded pair dihedral");
+        mtsLevel.stepFactor     = get_eint(&inp, "mts-level2-factor", 2, wi);
+
+        // We clear after reading without dynamics to not force the user to remove MTS mdp options
+        if (!EI_DYNAMICS(ir->eI))
+        {
+            ir->useMts = false;
+            ir->mtsLevels.clear();
+        }
+    }
     printStringNoNewline(&inp, "mode for center of mass motion removal");
     ir->comm_mode = get_eeenum(&inp, "comm-mode", ecm_names, wi);
     printStringNoNewline(&inp, "number of steps for center of mass motion removal");
     ir->nstcomm = get_eint(&inp, "nstcomm", 100, wi);
     printStringNoNewline(&inp, "group(s) for center of mass motion removal");
-    setStringEntry(&inp, "comm-grps", is->vcm, nullptr);
+    setStringEntry(&inp, "comm-grps", inputrecStrings->vcm, nullptr);
 
     printStringNewline(&inp, "LANGEVIN DYNAMICS OPTIONS");
     printStringNoNewline(&inp, "Friction coefficient (amu/ps) and random seed");
@@ -1952,9 +2035,9 @@ void get_ir(const char*     mdparin,
     printStringNoNewline(&inp, "This selects the subset of atoms for the compressed");
     printStringNoNewline(&inp, "trajectory file. You can select multiple groups. By");
     printStringNoNewline(&inp, "default, all atoms will be written.");
-    setStringEntry(&inp, "compressed-x-grps", is->x_compressed_groups, nullptr);
+    setStringEntry(&inp, "compressed-x-grps", inputrecStrings->x_compressed_groups, nullptr);
     printStringNoNewline(&inp, "Selection of energy groups");
-    setStringEntry(&inp, "energygrps", is->energy, nullptr);
+    setStringEntry(&inp, "energygrps", inputrecStrings->energy, nullptr);
 
     /* Neighbor searching */
     printStringNewline(&inp, "NEIGHBORSEARCHING PARAMETERS");
@@ -1963,7 +2046,13 @@ void get_ir(const char*     mdparin,
     printStringNoNewline(&inp, "nblist update frequency");
     ir->nstlist = get_eint(&inp, "nstlist", 10, wi);
     printStringNoNewline(&inp, "Periodic boundary conditions: xyz, no, xy");
-    ir->ePBC          = get_eeenum(&inp, "pbc", epbc_names, wi);
+    // TODO This conversion should be removed when proper std:string handling will be added to get_eeenum(...), etc.
+    std::vector<const char*> pbcTypesNamesChar;
+    for (const auto& pbcTypeName : c_pbcTypeNames)
+    {
+        pbcTypesNamesChar.push_back(pbcTypeName.c_str());
+    }
+    ir->pbcType       = static_cast<PbcType>(get_eeenum(&inp, "pbc", pbcTypesNamesChar.data(), wi));
     ir->bPeriodicMols = get_eeenum(&inp, "periodic-molecules", yesno_names, wi) != 0;
     printStringNoNewline(&inp,
                          "Allowed energy error due to the Verlet buffer in kJ/mol/ps per atom,");
@@ -1996,7 +2085,7 @@ void get_ir(const char*     mdparin,
     printStringNoNewline(&inp, "Extension of the potential lookup tables beyond the cut-off");
     ir->tabext = get_ereal(&inp, "table-extension", 1.0, wi);
     printStringNoNewline(&inp, "Separate tables between energy group pairs");
-    setStringEntry(&inp, "energygrp-table", is->egptable, nullptr);
+    setStringEntry(&inp, "energygrp-table", inputrecStrings->egptable, nullptr);
     printStringNoNewline(&inp, "Spacing for the PME/PPPM FFT grid");
     ir->fourier_spacing = get_ereal(&inp, "fourierspacing", 0.12, wi);
     printStringNoNewline(&inp, "FFT grid size, when a value is 0 fourierspacing will be used");
@@ -2024,10 +2113,10 @@ void get_ir(const char*     mdparin,
     ir->opts.nhchainlength = get_eint(&inp, "nh-chain-length", 10, wi);
     ir->bPrintNHChains = (get_eeenum(&inp, "print-nose-hoover-chain-variables", yesno_names, wi) != 0);
     printStringNoNewline(&inp, "Groups to couple separately");
-    setStringEntry(&inp, "tc-grps", is->tcgrps, nullptr);
+    setStringEntry(&inp, "tc-grps", inputrecStrings->tcgrps, nullptr);
     printStringNoNewline(&inp, "Time constant (ps) and reference temperature (K)");
-    setStringEntry(&inp, "tau-t", is->tau_t, nullptr);
-    setStringEntry(&inp, "ref-t", is->ref_t, nullptr);
+    setStringEntry(&inp, "tau-t", inputrecStrings->tau_t, nullptr);
+    setStringEntry(&inp, "ref-t", inputrecStrings->ref_t, nullptr);
     printStringNoNewline(&inp, "pressure coupling");
     ir->epc        = get_eeenum(&inp, "pcoupl", epcoupl_names, wi);
     ir->epct       = get_eeenum(&inp, "pcoupltype", epcoupltype_names, wi);
@@ -2043,39 +2132,40 @@ void get_ir(const char*     mdparin,
     printStringNewline(&inp, "OPTIONS FOR QMMM calculations");
     ir->bQMMM = (get_eeenum(&inp, "QMMM", yesno_names, wi) != 0);
     printStringNoNewline(&inp, "Groups treated Quantum Mechanically");
-    setStringEntry(&inp, "QMMM-grps", is->QMMM, nullptr);
+    setStringEntry(&inp, "QMMM-grps", inputrecStrings->QMMM, nullptr);
     printStringNoNewline(&inp, "QM method");
-    setStringEntry(&inp, "QMmethod", is->QMmethod, nullptr);
+    setStringEntry(&inp, "QMmethod", inputrecStrings->QMmethod, nullptr);
     printStringNoNewline(&inp, "QMMM scheme");
-    ir->QMMMscheme = get_eeenum(&inp, "QMMMscheme", eQMMMscheme_names, wi);
+    const char* noQMMMSchemeName = "normal";
+    get_eeenum(&inp, "QMMMscheme", &noQMMMSchemeName, wi);
     printStringNoNewline(&inp, "QM basisset");
-    setStringEntry(&inp, "QMbasis", is->QMbasis, nullptr);
+    setStringEntry(&inp, "QMbasis", inputrecStrings->QMbasis, nullptr);
     printStringNoNewline(&inp, "QM charge");
-    setStringEntry(&inp, "QMcharge", is->QMcharge, nullptr);
+    setStringEntry(&inp, "QMcharge", inputrecStrings->QMcharge, nullptr);
     printStringNoNewline(&inp, "QM multiplicity");
-    setStringEntry(&inp, "QMmult", is->QMmult, nullptr);
+    setStringEntry(&inp, "QMmult", inputrecStrings->QMmult, nullptr);
     printStringNoNewline(&inp, "Surface Hopping");
-    setStringEntry(&inp, "SH", is->bSH, nullptr);
+    setStringEntry(&inp, "SH", inputrecStrings->bSH, nullptr);
     printStringNoNewline(&inp, "CAS space options");
-    setStringEntry(&inp, "CASorbitals", is->CASorbitals, nullptr);
-    setStringEntry(&inp, "CASelectrons", is->CASelectrons, nullptr);
-    setStringEntry(&inp, "SAon", is->SAon, nullptr);
-    setStringEntry(&inp, "SAoff", is->SAoff, nullptr);
-    setStringEntry(&inp, "SAsteps", is->SAsteps, nullptr);
+    setStringEntry(&inp, "CASorbitals", inputrecStrings->CASorbitals, nullptr);
+    setStringEntry(&inp, "CASelectrons", inputrecStrings->CASelectrons, nullptr);
+    setStringEntry(&inp, "SAon", inputrecStrings->SAon, nullptr);
+    setStringEntry(&inp, "SAoff", inputrecStrings->SAoff, nullptr);
+    setStringEntry(&inp, "SAsteps", inputrecStrings->SAsteps, nullptr);
     printStringNoNewline(&inp, "Scale factor for MM charges");
-    ir->scalefactor = get_ereal(&inp, "MMChargeScaleFactor", 1.0, wi);
+    get_ereal(&inp, "MMChargeScaleFactor", 1.0, wi);
 
     /* Simulated annealing */
     printStringNewline(&inp, "SIMULATED ANNEALING");
     printStringNoNewline(&inp, "Type of annealing for each temperature group (no/single/periodic)");
-    setStringEntry(&inp, "annealing", is->anneal, nullptr);
+    setStringEntry(&inp, "annealing", inputrecStrings->anneal, nullptr);
     printStringNoNewline(&inp,
                          "Number of time points to use for specifying annealing in each group");
-    setStringEntry(&inp, "annealing-npoints", is->anneal_npoints, nullptr);
+    setStringEntry(&inp, "annealing-npoints", inputrecStrings->anneal_npoints, nullptr);
     printStringNoNewline(&inp, "List of times at the annealing points for each group");
-    setStringEntry(&inp, "annealing-time", is->anneal_time, nullptr);
+    setStringEntry(&inp, "annealing-time", inputrecStrings->anneal_time, nullptr);
     printStringNoNewline(&inp, "Temp. at each annealing point, for each group.");
-    setStringEntry(&inp, "annealing-temp", is->anneal_temp, nullptr);
+    setStringEntry(&inp, "annealing-temp", inputrecStrings->anneal_temp, nullptr);
 
     /* Startup run */
     printStringNewline(&inp, "GENERATE VELOCITIES FOR STARTUP RUN");
@@ -2111,7 +2201,7 @@ void get_ir(const char*     mdparin,
     printStringNewline(&inp, "ENERGY GROUP EXCLUSIONS");
     printStringNoNewline(
             &inp, "Pairs of energy groups for which all non-bonded interactions are excluded");
-    setStringEntry(&inp, "energygrp-excl", is->egpexcl, nullptr);
+    setStringEntry(&inp, "energygrp-excl", inputrecStrings->egpexcl, nullptr);
 
     /* Walls */
     printStringNewline(&inp, "WALLS");
@@ -2120,8 +2210,8 @@ void get_ir(const char*     mdparin,
     ir->nwall         = get_eint(&inp, "nwall", 0, wi);
     ir->wall_type     = get_eeenum(&inp, "wall-type", ewt_names, wi);
     ir->wall_r_linpot = get_ereal(&inp, "wall-r-linpot", -1, wi);
-    setStringEntry(&inp, "wall-atomtype", is->wall_atomtype, nullptr);
-    setStringEntry(&inp, "wall-density", is->wall_density, nullptr);
+    setStringEntry(&inp, "wall-atomtype", inputrecStrings->wall_atomtype, nullptr);
+    setStringEntry(&inp, "wall-density", inputrecStrings->wall_density, nullptr);
     ir->wall_ewald_zfac = get_ereal(&inp, "wall-ewald-zfac", 3, wi);
 
     /* COM pulling */
@@ -2130,23 +2220,30 @@ void get_ir(const char*     mdparin,
     if (ir->bPull)
     {
         snew(ir->pull, 1);
-        is->pull_grp = read_pullparams(&inp, ir->pull, wi);
+        inputrecStrings->pullGroupNames = read_pullparams(&inp, ir->pull, wi);
+
+        if (ir->useMts)
+        {
+            for (int c = 0; c < ir->pull->ncoord; c++)
+            {
+                if (ir->pull->coord[c].eType == epullCONSTRAINT)
+                {
+                    warning_error(wi,
+                                  "Constraint COM pulling is not supported in combination with "
+                                  "multiple time stepping");
+                    break;
+                }
+            }
+        }
     }
 
     /* AWH biasing
-       NOTE: needs COM pulling input */
+       NOTE: needs COM pulling or free energy input */
     printStringNewline(&inp, "AWH biasing");
     ir->bDoAwh = (get_eeenum(&inp, "awh", yesno_names, wi) != 0);
     if (ir->bDoAwh)
     {
-        if (ir->bPull)
-        {
-            ir->awhParams = gmx::readAndCheckAwhParams(&inp, ir, wi);
-        }
-        else
-        {
-            gmx_fatal(FARGS, "AWH biasing is only compatible with COM pulling turned on");
-        }
+        ir->awhParams = gmx::readAwhParams(&inp, wi);
     }
 
     /* Enforced rotation */
@@ -2156,14 +2253,14 @@ void get_ir(const char*     mdparin,
     if (ir->bRot)
     {
         snew(ir->rot, 1);
-        is->rot_grp = read_rotparams(&inp, ir->rot, wi);
+        inputrecStrings->rotateGroupNames = read_rotparams(&inp, ir->rot, wi);
     }
 
     /* Interactive MD */
     ir->bIMD = FALSE;
     printStringNewline(&inp, "Group to display and/or manipulate in interactive MD session");
-    setStringEntry(&inp, "IMD-group", is->imd_grp, nullptr);
-    if (is->imd_grp[0] != '\0')
+    setStringEntry(&inp, "IMD-group", inputrecStrings->imd_grp, nullptr);
+    if (inputrecStrings->imd_grp[0] != '\0')
     {
         snew(ir->imd, 1);
         ir->bIMD = TRUE;
@@ -2187,14 +2284,14 @@ void get_ir(const char*     mdparin,
     printStringNoNewline(&inp, "Orientation restraints force constant and tau for time averaging");
     ir->orires_fc  = get_ereal(&inp, "orire-fc", 0.0, wi);
     ir->orires_tau = get_ereal(&inp, "orire-tau", 0.0, wi);
-    setStringEntry(&inp, "orire-fitgrp", is->orirefitgrp, nullptr);
+    setStringEntry(&inp, "orire-fitgrp", inputrecStrings->orirefitgrp, nullptr);
     printStringNoNewline(&inp, "Output frequency for trace(SD) and S to energy file");
     ir->nstorireout = get_eint(&inp, "nstorireout", 100, wi);
 
     /* free energy variables */
     printStringNewline(&inp, "Free energy variables");
     ir->efep = get_eeenum(&inp, "free-energy", efep_names, wi);
-    setStringEntry(&inp, "couple-moltype", is->couple_moltype, nullptr);
+    setStringEntry(&inp, "couple-moltype", inputrecStrings->couple_moltype, nullptr);
     opts->couple_lam0  = get_eeenum(&inp, "couple-lambda0", couple_lam, wi);
     opts->couple_lam1  = get_eeenum(&inp, "couple-lambda1", couple_lam, wi);
     opts->bCoupleIntra = (get_eeenum(&inp, "couple-intramol", yesno_names, wi) != 0);
@@ -2205,15 +2302,15 @@ void get_ir(const char*     mdparin,
     fep->init_fep_state = get_eint(&inp, "init-lambda-state", -1, wi);
     fep->delta_lambda   = get_ereal(&inp, "delta-lambda", 0.0, wi);
     fep->nstdhdl        = get_eint(&inp, "nstdhdl", 50, wi);
-    setStringEntry(&inp, "fep-lambdas", is->fep_lambda[efptFEP], nullptr);
-    setStringEntry(&inp, "mass-lambdas", is->fep_lambda[efptMASS], nullptr);
-    setStringEntry(&inp, "coul-lambdas", is->fep_lambda[efptCOUL], nullptr);
-    setStringEntry(&inp, "vdw-lambdas", is->fep_lambda[efptVDW], nullptr);
-    setStringEntry(&inp, "bonded-lambdas", is->fep_lambda[efptBONDED], nullptr);
-    setStringEntry(&inp, "restraint-lambdas", is->fep_lambda[efptRESTRAINT], nullptr);
-    setStringEntry(&inp, "temperature-lambdas", is->fep_lambda[efptTEMPERATURE], nullptr);
+    setStringEntry(&inp, "fep-lambdas", inputrecStrings->fep_lambda[efptFEP], nullptr);
+    setStringEntry(&inp, "mass-lambdas", inputrecStrings->fep_lambda[efptMASS], nullptr);
+    setStringEntry(&inp, "coul-lambdas", inputrecStrings->fep_lambda[efptCOUL], nullptr);
+    setStringEntry(&inp, "vdw-lambdas", inputrecStrings->fep_lambda[efptVDW], nullptr);
+    setStringEntry(&inp, "bonded-lambdas", inputrecStrings->fep_lambda[efptBONDED], nullptr);
+    setStringEntry(&inp, "restraint-lambdas", inputrecStrings->fep_lambda[efptRESTRAINT], nullptr);
+    setStringEntry(&inp, "temperature-lambdas", inputrecStrings->fep_lambda[efptTEMPERATURE], nullptr);
     fep->lambda_neighbors = get_eint(&inp, "calc-lambda-neighbors", 1, wi);
-    setStringEntry(&inp, "init-lambda-weights", is->lambda_weights, nullptr);
+    setStringEntry(&inp, "init-lambda-weights", inputrecStrings->lambda_weights, nullptr);
     fep->edHdLPrintEnergy   = get_eeenum(&inp, "dhdl-print-energy", edHdLPrintEnergy_names, wi);
     fep->sc_alpha           = get_ereal(&inp, "sc-alpha", 0.0, wi);
     fep->sc_power           = get_eint(&inp, "sc-power", 1, wi);
@@ -2229,12 +2326,12 @@ void get_ir(const char*     mdparin,
 
     /* Non-equilibrium MD stuff */
     printStringNewline(&inp, "Non-equilibrium MD stuff");
-    setStringEntry(&inp, "acc-grps", is->accgrps, nullptr);
-    setStringEntry(&inp, "accelerate", is->acc, nullptr);
-    setStringEntry(&inp, "freezegrps", is->freeze, nullptr);
-    setStringEntry(&inp, "freezedim", is->frdim, nullptr);
+    setStringEntry(&inp, "acc-grps", inputrecStrings->accgrps, nullptr);
+    setStringEntry(&inp, "accelerate", inputrecStrings->acc, nullptr);
+    setStringEntry(&inp, "freezegrps", inputrecStrings->freeze, nullptr);
+    setStringEntry(&inp, "freezedim", inputrecStrings->frdim, nullptr);
     ir->cos_accel = get_ereal(&inp, "cos-acceleration", 0, wi);
-    setStringEntry(&inp, "deform", is->deform, nullptr);
+    setStringEntry(&inp, "deform", inputrecStrings->deform, nullptr);
 
     /* simulated tempering variables */
     printStringNewline(&inp, "simulated tempering variables");
@@ -2375,8 +2472,8 @@ void get_ir(const char*     mdparin,
 
     /* User defined thingies */
     printStringNewline(&inp, "User defined thingies");
-    setStringEntry(&inp, "user1-grps", is->user1, nullptr);
-    setStringEntry(&inp, "user2-grps", is->user2, nullptr);
+    setStringEntry(&inp, "user1-grps", inputrecStrings->user1, nullptr);
+    setStringEntry(&inp, "user2-grps", inputrecStrings->user2, nullptr);
     ir->userint1  = get_eint(&inp, "userint1", 0, wi);
     ir->userint2  = get_eint(&inp, "userint2", 0, wi);
     ir->userint3  = get_eint(&inp, "userint3", 0, wi);
@@ -2485,11 +2582,11 @@ void get_ir(const char*     mdparin,
     }
 
     opts->couple_moltype = nullptr;
-    if (strlen(is->couple_moltype) > 0)
+    if (strlen(inputrecStrings->couple_moltype) > 0)
     {
         if (ir->efep != efepNO)
         {
-            opts->couple_moltype = gmx_strdup(is->couple_moltype);
+            opts->couple_moltype = gmx_strdup(inputrecStrings->couple_moltype);
             if (opts->couple_lam0 == opts->couple_lam1)
             {
                 warning(wi, "The lambda=0 and lambda=1 states for coupling are identical");
@@ -2512,7 +2609,7 @@ void get_ir(const char*     mdparin,
     /* FREE ENERGY AND EXPANDED ENSEMBLE OPTIONS */
     if (ir->efep != efepNO)
     {
-        if (fep->delta_lambda > 0)
+        if (fep->delta_lambda != 0)
         {
             ir->efep = efepSLOWGROWTH;
         }
@@ -2545,7 +2642,7 @@ void get_ir(const char*     mdparin,
         {
             ir->bExpanded = TRUE;
         }
-        do_fep_params(ir, is->fep_lambda, is->lambda_weights, wi);
+        do_fep_params(ir, inputrecStrings->fep_lambda, inputrecStrings->lambda_weights, wi);
         if (ir->bSimTemp) /* done after fep params */
         {
             do_simtemp_params(ir);
@@ -2576,11 +2673,11 @@ void get_ir(const char*     mdparin,
 
     /* WALL PARAMETERS */
 
-    do_wall_params(ir, is->wall_atomtype, is->wall_density, opts, wi);
+    do_wall_params(ir, inputrecStrings->wall_atomtype, inputrecStrings->wall_density, opts, wi);
 
     /* ORIENTATION RESTRAINT PARAMETERS */
 
-    if (opts->bOrire && gmx::splitString(is->orirefitgrp).size() != 1)
+    if (opts->bOrire && gmx::splitString(inputrecStrings->orirefitgrp).size() != 1)
     {
         warning_error(wi, "ERROR: Need one orientation restraint fit group\n");
     }
@@ -2594,15 +2691,17 @@ void get_ir(const char*     mdparin,
     }
 
     double gmx_unused canary;
-    int ndeform = sscanf(is->deform, "%lf %lf %lf %lf %lf %lf %lf", &(dumdub[0][0]), &(dumdub[0][1]),
-                         &(dumdub[0][2]), &(dumdub[0][3]), &(dumdub[0][4]), &(dumdub[0][5]), &canary);
+    int ndeform = sscanf(inputrecStrings->deform, "%lf %lf %lf %lf %lf %lf %lf", &(dumdub[0][0]),
+                         &(dumdub[0][1]), &(dumdub[0][2]), &(dumdub[0][3]), &(dumdub[0][4]),
+                         &(dumdub[0][5]), &canary);
 
-    if (strlen(is->deform) > 0 && ndeform != 6)
+    if (strlen(inputrecStrings->deform) > 0 && ndeform != 6)
     {
-        warning_error(
-                wi, gmx::formatString(
-                            "Cannot parse exactly 6 box deformation velocities from string '%s'", is->deform)
-                            .c_str());
+        warning_error(wi,
+                      gmx::formatString(
+                              "Cannot parse exactly 6 box deformation velocities from string '%s'",
+                              inputrecStrings->deform)
+                              .c_str());
     }
     for (i = 0; i < 3; i++)
     {
@@ -2662,25 +2761,20 @@ void get_ir(const char*     mdparin,
         }
     }
 
-    sfree(dumstr[0]);
-    sfree(dumstr[1]);
-}
-
-static int search_QMstring(const char* s, int ng, const char* gn[])
-{
-    /* same as normal search_string, but this one searches QM strings */
-    int i;
+    /* Set up MTS levels, this needs to happen before checking AWH parameters */
+    if (ir->useMts)
+    {
+        setupMtsLevels(ir->mtsLevels, *ir, *opts, wi);
+    }
 
-    for (i = 0; (i < ng); i++)
+    if (ir->bDoAwh)
     {
-        if (gmx_strcasecmp(s, gn[i]) == 0)
-        {
-            return i;
-        }
+        gmx::checkAwhParams(ir->awhParams, ir, wi);
     }
 
-    gmx_fatal(FARGS, "this QM method or basisset (%s) is not implemented\n!", s);
-} /* search_QMstring */
+    sfree(dumstr[0]);
+    sfree(dumstr[1]);
+}
 
 /* We would like gn to be const as well, but C doesn't allow this */
 /* TODO this is utility functionality (search for the index of a
@@ -3370,9 +3464,9 @@ void do_index(const char*                   mdparin,
 
     set_warning_line(wi, mdparin, -1);
 
-    auto temperatureCouplingTauValues       = gmx::splitString(is->tau_t);
-    auto temperatureCouplingReferenceValues = gmx::splitString(is->ref_t);
-    auto temperatureCouplingGroupNames      = gmx::splitString(is->tcgrps);
+    auto temperatureCouplingTauValues       = gmx::splitString(inputrecStrings->tau_t);
+    auto temperatureCouplingReferenceValues = gmx::splitString(inputrecStrings->ref_t);
+    auto temperatureCouplingGroupNames      = gmx::splitString(inputrecStrings->tcgrps);
     if (temperatureCouplingTauValues.size() != temperatureCouplingGroupNames.size()
         || temperatureCouplingReferenceValues.size() != temperatureCouplingGroupNames.size())
     {
@@ -3509,7 +3603,7 @@ void do_index(const char*                   mdparin,
     }
 
     /* Simulated annealing for each group. There are nr groups */
-    auto simulatedAnnealingGroupNames = gmx::splitString(is->anneal);
+    auto simulatedAnnealingGroupNames = gmx::splitString(inputrecStrings->anneal);
     if (simulatedAnnealingGroupNames.size() == 1
         && gmx::equalCaseInsensitive(simulatedAnnealingGroupNames[0], "N", 1))
     {
@@ -3556,7 +3650,7 @@ void do_index(const char*                   mdparin,
             if (bAnneal)
             {
                 /* Read the other fields too */
-                auto simulatedAnnealingPoints = gmx::splitString(is->anneal_npoints);
+                auto simulatedAnnealingPoints = gmx::splitString(inputrecStrings->anneal_npoints);
                 if (simulatedAnnealingPoints.size() != simulatedAnnealingGroupNames.size())
                 {
                     gmx_fatal(FARGS, "Found %zu annealing-npoints values for %zu groups\n",
@@ -3577,14 +3671,14 @@ void do_index(const char*                   mdparin,
                     numSimulatedAnnealingFields += ir->opts.anneal_npoints[i];
                 }
 
-                auto simulatedAnnealingTimes = gmx::splitString(is->anneal_time);
+                auto simulatedAnnealingTimes = gmx::splitString(inputrecStrings->anneal_time);
 
                 if (simulatedAnnealingTimes.size() != numSimulatedAnnealingFields)
                 {
                     gmx_fatal(FARGS, "Found %zu annealing-time values, wanted %zu\n",
                               simulatedAnnealingTimes.size(), numSimulatedAnnealingFields);
                 }
-                auto simulatedAnnealingTemperatures = gmx::splitString(is->anneal_temp);
+                auto simulatedAnnealingTemperatures = gmx::splitString(inputrecStrings->anneal_temp);
                 if (simulatedAnnealingTemperatures.size() != numSimulatedAnnealingFields)
                 {
                     gmx_fatal(FARGS, "Found %zu annealing-temp values, wanted %zu\n",
@@ -3672,14 +3766,14 @@ void do_index(const char*                   mdparin,
 
     if (ir->bPull)
     {
-        make_pull_groups(ir->pull, is->pull_grp, defaultIndexGroups, gnames);
+        make_pull_groups(ir->pull, inputrecStrings->pullGroupNames, defaultIndexGroups, gnames);
 
         make_pull_coords(ir->pull);
     }
 
     if (ir->bRot)
     {
-        make_rotation_groups(ir->rot, is->rot_grp, defaultIndexGroups, gnames);
+        make_rotation_groups(ir->rot, inputrecStrings->rotateGroupNames, defaultIndexGroups, gnames);
     }
 
     if (ir->eSwapCoords != eswapNO)
@@ -3690,15 +3784,15 @@ void do_index(const char*                   mdparin,
     /* Make indices for IMD session */
     if (ir->bIMD)
     {
-        make_IMD_group(ir->imd, is->imd_grp, defaultIndexGroups, gnames);
+        make_IMD_group(ir->imd, inputrecStrings->imd_grp, defaultIndexGroups, gnames);
     }
 
     gmx::IndexGroupsAndNames defaultIndexGroupsAndNames(
             *defaultIndexGroups, gmx::arrayRefFromArray(gnames, defaultIndexGroups->nr));
-    notifier.notifier_.notify(defaultIndexGroupsAndNames);
+    notifier.preProcessingNotifications_.notify(defaultIndexGroupsAndNames);
 
-    auto accelerations          = gmx::splitString(is->acc);
-    auto accelerationGroupNames = gmx::splitString(is->accgrps);
+    auto accelerations          = gmx::splitString(inputrecStrings->acc);
+    auto accelerationGroupNames = gmx::splitString(inputrecStrings->accgrps);
     if (accelerationGroupNames.size() * DIM != accelerations.size())
     {
         gmx_fatal(FARGS, "Invalid Acceleration input: %zu groups and %zu acc. values",
@@ -3712,8 +3806,8 @@ void do_index(const char*                   mdparin,
 
     convertRvecs(wi, accelerations, "anneal-time", ir->opts.acc);
 
-    auto freezeDims       = gmx::splitString(is->frdim);
-    auto freezeGroupNames = gmx::splitString(is->freeze);
+    auto freezeDims       = gmx::splitString(inputrecStrings->frdim);
+    auto freezeGroupNames = gmx::splitString(inputrecStrings->freeze);
     if (freezeDims.size() != DIM * freezeGroupNames.size())
     {
         gmx_fatal(FARGS, "Invalid Freezing input: %zu groups and %zu freeze values",
@@ -3750,12 +3844,12 @@ void do_index(const char*                   mdparin,
         }
     }
 
-    auto energyGroupNames = gmx::splitString(is->energy);
+    auto energyGroupNames = gmx::splitString(inputrecStrings->energy);
     do_numbering(natoms, groups, energyGroupNames, defaultIndexGroups, gnames,
                  SimulationAtomGroupType::EnergyOutput, restnm, egrptpALL_GENREST, bVerbose, wi);
     add_wall_energrps(groups, ir->nwall, symtab);
     ir->opts.ngener    = groups->groups[SimulationAtomGroupType::EnergyOutput].size();
-    auto vcmGroupNames = gmx::splitString(is->vcm);
+    auto vcmGroupNames = gmx::splitString(inputrecStrings->vcm);
     do_numbering(natoms, groups, vcmGroupNames, defaultIndexGroups, gnames,
                  SimulationAtomGroupType::MassCenterVelocityRemoval, restnm,
                  vcmGroupNames.empty() ? egrptpALL_GENREST : egrptpPART, bVerbose, wi);
@@ -3768,90 +3862,32 @@ void do_index(const char*                   mdparin,
     /* Now we have filled the freeze struct, so we can calculate NRDF */
     calc_nrdf(mtop, ir, gnames);
 
-    auto user1GroupNames = gmx::splitString(is->user1);
+    auto user1GroupNames = gmx::splitString(inputrecStrings->user1);
     do_numbering(natoms, groups, user1GroupNames, defaultIndexGroups, gnames,
                  SimulationAtomGroupType::User1, restnm, egrptpALL_GENREST, bVerbose, wi);
-    auto user2GroupNames = gmx::splitString(is->user2);
+    auto user2GroupNames = gmx::splitString(inputrecStrings->user2);
     do_numbering(natoms, groups, user2GroupNames, defaultIndexGroups, gnames,
                  SimulationAtomGroupType::User2, restnm, egrptpALL_GENREST, bVerbose, wi);
-    auto compressedXGroupNames = gmx::splitString(is->x_compressed_groups);
+    auto compressedXGroupNames = gmx::splitString(inputrecStrings->x_compressed_groups);
     do_numbering(natoms, groups, compressedXGroupNames, defaultIndexGroups, gnames,
                  SimulationAtomGroupType::CompressedPositionOutput, restnm, egrptpONE, bVerbose, wi);
-    auto orirefFitGroupNames = gmx::splitString(is->orirefitgrp);
+    auto orirefFitGroupNames = gmx::splitString(inputrecStrings->orirefitgrp);
     do_numbering(natoms, groups, orirefFitGroupNames, defaultIndexGroups, gnames,
                  SimulationAtomGroupType::OrientationRestraintsFit, restnm, egrptpALL_GENREST,
                  bVerbose, wi);
 
-    /* QMMM input processing */
-    auto qmGroupNames = gmx::splitString(is->QMMM);
-    auto qmMethods    = gmx::splitString(is->QMmethod);
-    auto qmBasisSets  = gmx::splitString(is->QMbasis);
-    if (ir->eI != eiMimic)
+    /* MiMiC QMMM input processing */
+    auto qmGroupNames = gmx::splitString(inputrecStrings->QMMM);
+    if (qmGroupNames.size() > 1)
     {
-        if (qmMethods.size() != qmGroupNames.size() || qmBasisSets.size() != qmGroupNames.size())
-        {
-            gmx_fatal(FARGS,
-                      "Invalid QMMM input: %zu groups %zu basissets"
-                      " and %zu methods\n",
-                      qmGroupNames.size(), qmBasisSets.size(), qmMethods.size());
-        }
-        /* group rest, if any, is always MM! */
-        do_numbering(natoms, groups, qmGroupNames, defaultIndexGroups, gnames,
-                     SimulationAtomGroupType::QuantumMechanics, restnm, egrptpALL_GENREST, bVerbose, wi);
-        nr            = qmGroupNames.size(); /*atoms->grps[egcQMMM].nr;*/
-        ir->opts.ngQM = qmGroupNames.size();
-        snew(ir->opts.QMmethod, nr);
-        snew(ir->opts.QMbasis, nr);
-        for (i = 0; i < nr; i++)
-        {
-            /* input consists of strings: RHF CASSCF PM3 .. These need to be
-             * converted to the corresponding enum in names.c
-             */
-            ir->opts.QMmethod[i] = search_QMstring(qmMethods[i].c_str(), eQMmethodNR, eQMmethod_names);
-            ir->opts.QMbasis[i] = search_QMstring(qmBasisSets[i].c_str(), eQMbasisNR, eQMbasis_names);
-        }
-        auto qmMultiplicities = gmx::splitString(is->QMmult);
-        auto qmCharges        = gmx::splitString(is->QMcharge);
-        auto qmbSH            = gmx::splitString(is->bSH);
-        snew(ir->opts.QMmult, nr);
-        snew(ir->opts.QMcharge, nr);
-        snew(ir->opts.bSH, nr);
-        convertInts(wi, qmMultiplicities, "QMmult", ir->opts.QMmult);
-        convertInts(wi, qmCharges, "QMcharge", ir->opts.QMcharge);
-        convertYesNos(wi, qmbSH, "bSH", ir->opts.bSH);
-
-        auto CASelectrons = gmx::splitString(is->CASelectrons);
-        auto CASorbitals  = gmx::splitString(is->CASorbitals);
-        snew(ir->opts.CASelectrons, nr);
-        snew(ir->opts.CASorbitals, nr);
-        convertInts(wi, CASelectrons, "CASelectrons", ir->opts.CASelectrons);
-        convertInts(wi, CASorbitals, "CASOrbitals", ir->opts.CASorbitals);
-
-        auto SAon    = gmx::splitString(is->SAon);
-        auto SAoff   = gmx::splitString(is->SAoff);
-        auto SAsteps = gmx::splitString(is->SAsteps);
-        snew(ir->opts.SAon, nr);
-        snew(ir->opts.SAoff, nr);
-        snew(ir->opts.SAsteps, nr);
-        convertInts(wi, SAon, "SAon", ir->opts.SAon);
-        convertInts(wi, SAoff, "SAoff", ir->opts.SAoff);
-        convertInts(wi, SAsteps, "SAsteps", ir->opts.SAsteps);
+        gmx_fatal(FARGS, "Currently, having more than one QM group in MiMiC is not supported");
     }
-    else
-    {
-        /* MiMiC */
-        if (qmGroupNames.size() > 1)
-        {
-            gmx_fatal(FARGS, "Currently, having more than one QM group in MiMiC is not supported");
-        }
-        /* group rest, if any, is always MM! */
-        do_numbering(natoms, groups, qmGroupNames, defaultIndexGroups, gnames,
-                     SimulationAtomGroupType::QuantumMechanics, restnm, egrptpALL_GENREST, bVerbose, wi);
+    /* group rest, if any, is always MM! */
+    do_numbering(natoms, groups, qmGroupNames, defaultIndexGroups, gnames,
+                 SimulationAtomGroupType::QuantumMechanics, restnm, egrptpALL_GENREST, bVerbose, wi);
+    ir->opts.ngQM = qmGroupNames.size();
 
-        ir->opts.ngQM = qmGroupNames.size();
-    }
-
-    /* end of QMMM input */
+    /* end of MiMiC QMMM input */
 
     if (bVerbose)
     {
@@ -3869,7 +3905,7 @@ void do_index(const char*                   mdparin,
     nr = groups->groups[SimulationAtomGroupType::EnergyOutput].size();
     snew(ir->opts.egp_flags, nr * nr);
 
-    bExcl = do_egp_flag(ir, groups, "energygrp-excl", is->egpexcl, EGP_EXCL);
+    bExcl = do_egp_flag(ir, groups, "energygrp-excl", inputrecStrings->egpexcl, EGP_EXCL);
     if (bExcl && ir->cutoff_scheme == ecutsVERLET)
     {
         warning_error(wi, "Energy group exclusions are currently not supported");
@@ -3879,7 +3915,7 @@ void do_index(const char*                   mdparin,
         warning(wi, "Can not exclude the lattice Coulomb energy between energy groups");
     }
 
-    bTable = do_egp_flag(ir, groups, "energygrp-table", is->egptable, EGP_TABLE);
+    bTable = do_egp_flag(ir, groups, "energygrp-table", inputrecStrings->egptable, EGP_TABLE);
     if (bTable && !(ir->vdwtype == evdwUSER) && !(ir->coulombtype == eelUSER)
         && !(ir->coulombtype == eelPMEUSER) && !(ir->coulombtype == eelPMEUSERSWITCH))
     {
@@ -4154,6 +4190,9 @@ static void check_combination_rules(const t_inputrec* ir, const gmx_mtop_t* mtop
 
 void triple_check(const char* mdparin, t_inputrec* ir, gmx_mtop_t* sys, warninp_t wi)
 {
+    // Not meeting MTS requirements should have resulted in a fatal error, so we can assert here
+    gmx::assertMtsRequirements(*ir);
+
     char                      err_buf[STRLEN];
     int                       i, m, c, nmol;
     bool                      bCharge, bAcc;
@@ -4455,7 +4494,7 @@ void double_check(t_inputrec* ir, matrix box, bool bHasNormalConstraints, bool b
     char        warn_buf[STRLEN];
     const char* ptr;
 
-    ptr = check_box(ir->ePBC, box);
+    ptr = check_box(ir->pbcType, box);
     if (ptr)
     {
         warning_error(wi, ptr);
@@ -4505,7 +4544,7 @@ void double_check(t_inputrec* ir, matrix box, bool bHasNormalConstraints, bool b
         ir->LincsWarnAngle = 90.0;
     }
 
-    if (ir->ePBC != epbcNONE)
+    if (ir->pbcType != PbcType::No)
     {
         if (ir->nstlist == 0)
         {
@@ -4513,7 +4552,7 @@ void double_check(t_inputrec* ir, matrix box, bool bHasNormalConstraints, bool b
                     "With nstlist=0 atoms are only put into the box at step 0, therefore drifting "
                     "atoms might cause the simulation to crash.");
         }
-        if (gmx::square(ir->rlist) >= max_cutoff2(ir->ePBC, box))
+        if (gmx::square(ir->rlist) >= max_cutoff2(ir->pbcType, box))
         {
             sprintf(warn_buf,
                     "ERROR: The cut-off length is longer than half the shortest box vector or "
index 7e200cb7569186e70e83ebe424ad13ea3471b922..c4f0434e435fad7100b8af67f8dbc3a8b700c709 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,6 +39,8 @@
 #ifndef GMX_GMXPREPROCESS_READIR_H
 #define GMX_GMXPREPROCESS_READIR_H
 
+#include <string>
+
 #include "gromacs/fileio/readinp.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/utility/real.h"
@@ -81,21 +84,23 @@ enum
 
 struct t_gromppopts
 {
-    int   warnings;
-    int   nshake;
-    char* include;
-    char* define;
-    bool  bGenVel;
-    bool  bGenPairs;
-    real  tempi;
-    int   seed;
-    bool  bOrire;
-    bool  bMorse;
-    char* wall_atomtype[2];
-    char* couple_moltype;
-    int   couple_lam0;
-    int   couple_lam1;
-    bool  bCoupleIntra;
+    int         warnings     = 0;
+    int         nshake       = 0;
+    char*       include      = nullptr;
+    char*       define       = nullptr;
+    bool        bGenVel      = false;
+    bool        bGenPairs    = false;
+    real        tempi        = 0;
+    int         seed         = 0;
+    int         numMtsLevels = 0;
+    std::string mtsLevel2Forces;
+    bool        bOrire           = false;
+    bool        bMorse           = false;
+    char*       wall_atomtype[2] = { nullptr, nullptr };
+    char*       couple_moltype   = nullptr;
+    int         couple_lam0      = 0;
+    int         couple_lam1      = 0;
+    bool        bCoupleIntra     = false;
 };
 
 /*! \brief Initialise object to hold strings parsed from an .mdp file */
@@ -145,10 +150,13 @@ void do_index(const char*                   mdparin,
 
 /* Routines In readpull.c */
 
-char** read_pullparams(std::vector<t_inpfile>* inp, pull_params_t* pull, warninp_t wi);
+std::vector<std::string> read_pullparams(std::vector<t_inpfile>* inp, pull_params_t* pull, warninp_t wi);
 /* Reads the pull parameters, returns a list of the pull group names */
 
-void make_pull_groups(pull_params_t* pull, char** pgnames, const t_blocka* grps, char** gnames);
+void make_pull_groups(pull_params_t*                   pull,
+                      gmx::ArrayRef<const std::string> pullGroupNames,
+                      const t_blocka*                  grps,
+                      char**                           gnames);
 /* Process the pull group parameters after reading the index groups */
 
 void make_pull_coords(pull_params_t* pull);
@@ -161,10 +169,13 @@ pull_t* set_pull_init(t_inputrec* ir, const gmx_mtop_t* mtop, rvec* x, matrix bo
  * after all modules have registered their external potentials, if present.
  */
 
-char** read_rotparams(std::vector<t_inpfile>* inp, t_rot* rot, warninp_t wi);
+std::vector<std::string> read_rotparams(std::vector<t_inpfile>* inp, t_rot* rot, warninp_t wi);
 /* Reads enforced rotation parameters, returns a list of the rot group names */
 
-void make_rotation_groups(t_rot* rot, char** rotgnames, t_blocka* grps, char** gnames);
+void make_rotation_groups(t_rot*                           rot,
+                          gmx::ArrayRef<const std::string> rotateGroupNames,
+                          t_blocka*                        grps,
+                          char**                           gnames);
 /* Process the rotation parameters after reading the index groups */
 
 void set_reference_positions(t_rot* rot, rvec* x, matrix box, const char* fn, bool bSet, warninp_t wi);
index 238aca95ca14c9def421e65878e08372c1316f96..91abefcc451616d5f6f33b11f3a84eb3ab421eba 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/pull_params.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pulling/pull.h"
 #include "gromacs/topology/topology.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/futil.h"
@@ -283,13 +286,12 @@ static void init_pull_coord(t_pull_coord* pcrd,
     }
 }
 
-char** read_pullparams(std::vector<t_inpfile>* inp, pull_params_t* pull, warninp_t wi)
+std::vector<std::string> read_pullparams(std::vector<t_inpfile>* inp, pull_params_t* pull, warninp_t wi)
 {
-    int    nscan, idum;
-    char** grpbuf;
-    char   buf[STRLEN];
-    char   provider[STRLEN], groups[STRLEN], dim_buf[STRLEN];
-    char   wbuf[STRLEN], origin_buf[STRLEN], vec_buf[STRLEN];
+    int  nscan, idum;
+    char buf[STRLEN];
+    char provider[STRLEN], groups[STRLEN], dim_buf[STRLEN];
+    char wbuf[STRLEN], origin_buf[STRLEN], vec_buf[STRLEN];
 
     t_pull_group* pgrp;
     t_pull_coord* pcrd;
@@ -331,14 +333,15 @@ char** read_pullparams(std::vector<t_inpfile>* inp, pull_params_t* pull, warninp
     printStringNoNewline(inp, "Group and coordinate parameters");
 
     /* Read the pull groups */
-    snew(grpbuf, pull->ngroup);
+    std::vector<std::string> pullGroups(pull->ngroup);
+    char                     readBuffer[STRLEN];
     /* Group 0 is the absolute reference, we don't read anything for 0 */
     for (int groupNum = 1; groupNum < pull->ngroup; groupNum++)
     {
         pgrp = &pull->group[groupNum];
-        snew(grpbuf[groupNum], STRLEN);
         sprintf(buf, "pull-group%d-name", groupNum);
-        setStringEntry(inp, buf, grpbuf[groupNum], "");
+        setStringEntry(inp, buf, readBuffer, "");
+        pullGroups[groupNum] = readBuffer;
         sprintf(buf, "pull-group%d-weights", groupNum);
         setStringEntry(inp, buf, wbuf, "");
         sprintf(buf, "pull-group%d-pbcatom", groupNum);
@@ -412,10 +415,13 @@ char** read_pullparams(std::vector<t_inpfile>* inp, pull_params_t* pull, warninp
         init_pull_coord(pcrd, coordNum, dim_buf, origin_buf, vec_buf, wi);
     }
 
-    return grpbuf;
+    return pullGroups;
 }
 
-void make_pull_groups(pull_params_t* pull, char** pgnames, const t_blocka* grps, char** gnames)
+void make_pull_groups(pull_params_t*                   pull,
+                      gmx::ArrayRef<const std::string> pullGroupNames,
+                      const t_blocka*                  grps,
+                      char**                           gnames)
 {
     int           g, ig = -1, i;
     t_pull_group* pgrp;
@@ -431,19 +437,19 @@ void make_pull_groups(pull_params_t* pull, char** pgnames, const t_blocka* grps,
         pgrp                = &pull->group[g];
         pgrp->pbcatom_input = pgrp->pbcatom;
 
-        if (strcmp(pgnames[g], "") == 0)
+        if (pullGroupNames[g].empty())
         {
             gmx_fatal(FARGS, "Pull option pull_group%d required by grompp has not been set.", g);
         }
 
-        ig        = search_string(pgnames[g], grps->nr, gnames);
+        ig        = search_string(pullGroupNames[g].c_str(), grps->nr, gnames);
         pgrp->nat = grps->index[ig + 1] - grps->index[ig];
 
-        fprintf(stderr, "Pull group %d '%s' has %d atoms\n", g, pgnames[g], pgrp->nat);
+        fprintf(stderr, "Pull group %d '%s' has %d atoms\n", g, pullGroupNames[g].c_str(), pgrp->nat);
 
         if (pgrp->nat == 0)
         {
-            gmx_fatal(FARGS, "Pull group %d '%s' is empty", g, pgnames[g]);
+            gmx_fatal(FARGS, "Pull group %d '%s' is empty", g, pullGroupNames[g].c_str());
         }
 
         snew(pgrp->ind, pgrp->nat);
@@ -457,7 +463,7 @@ void make_pull_groups(pull_params_t* pull, char** pgnames, const t_blocka* grps,
             gmx_fatal(FARGS,
                       "Number of weights (%d) for pull group %d '%s' does not match the number of "
                       "atoms (%d)",
-                      pgrp->nweight, g, pgnames[g], pgrp->nat);
+                      pgrp->nweight, g, pullGroupNames[g].c_str(), pgrp->nat);
         }
 
         if (pgrp->nat == 1)
@@ -532,21 +538,21 @@ pull_t* set_pull_init(t_inputrec* ir, const gmx_mtop_t* mtop, rvec* x, matrix bo
     pull_work    = init_pull(nullptr, pull, ir, mtop, nullptr, &atomSets, lambda);
     auto mdAtoms = gmx::makeMDAtoms(nullptr, *mtop, *ir, false);
     auto md      = mdAtoms->mdatoms();
-    atoms2md(mtop, ir, -1, nullptr, mtop->natoms, mdAtoms.get());
+    atoms2md(mtop, ir, -1, {}, mtop->natoms, mdAtoms.get());
     if (ir->efep)
     {
         update_mdatoms(md, lambda);
     }
 
-    set_pbc(&pbc, ir->ePBC, box);
+    set_pbc(&pbc, ir->pbcType, box);
 
     t_start = ir->init_t + ir->init_step * ir->delta_t;
 
     if (pull->bSetPbcRefToPrevStepCOM)
     {
-        initPullComFromPrevStep(nullptr, pull_work, md, &pbc, x);
+        initPullComFromPrevStep(nullptr, pull_work, md->massT, &pbc, x);
     }
-    pull_calc_coms(nullptr, pull_work, md, &pbc, t_start, x, nullptr);
+    pull_calc_coms(nullptr, pull_work, md->massT, &pbc, t_start, x, nullptr);
 
     for (int g = 0; g < pull->ngroup; g++)
     {
index 88ec8c25156efcb002abfdb34a396aa5f77c3b3f..6ef252e7f5368aaa88bff4de1d7e3d3913f07019 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +48,7 @@
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/topology/block.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/futil.h"
@@ -69,10 +71,9 @@ static void string2dvec(char buf[], dvec nums)
 }
 
 
-extern char** read_rotparams(std::vector<t_inpfile>* inp, t_rot* rot, warninp_t wi)
+extern std::vector<std::string> read_rotparams(std::vector<t_inpfile>* inp, t_rot* rot, warninp_t wi)
 {
     int       g, m;
-    char**    grpbuf;
     char      buf[STRLEN];
     char      warn_buf[STRLEN];
     dvec      vec;
@@ -97,15 +98,15 @@ extern char** read_rotparams(std::vector<t_inpfile>* inp, t_rot* rot, warninp_t
     snew(rot->grp, rot->ngrp);
 
     /* Read the rotation groups */
-    snew(grpbuf, rot->ngrp);
+    std::vector<std::string> rotateGroups(rot->ngrp);
+    char                     readBuffer[STRLEN];
     for (g = 0; g < rot->ngrp; g++)
     {
         rotg = &rot->grp[g];
-        snew(grpbuf[g], STRLEN);
         printStringNoNewline(inp, "Rotation group name");
         sprintf(buf, "rot-group%d", g);
-        setStringEntry(inp, buf, grpbuf[g], "");
-
+        setStringEntry(inp, buf, readBuffer, "");
+        rotateGroups[g] = readBuffer;
         printStringNoNewline(inp,
                              "Rotation potential. Can be iso, iso-pf, pm, pm-pf, rm, rm-pf, rm2, "
                              "rm2-pf, flex, flex-t, flex2, flex2-t");
@@ -215,7 +216,7 @@ extern char** read_rotparams(std::vector<t_inpfile>* inp, t_rot* rot, warninp_t
         rotg->PotAngle_step = get_ereal(inp, buf, 0.25, wi);
     }
 
-    return grpbuf;
+    return rotateGroups;
 }
 
 
@@ -307,7 +308,10 @@ extern void set_reference_positions(t_rot* rot, rvec* x, matrix box, const char*
 }
 
 
-extern void make_rotation_groups(t_rot* rot, char** rotgnames, t_blocka* grps, char** gnames)
+extern void make_rotation_groups(t_rot*                           rot,
+                                 gmx::ArrayRef<const std::string> rotateGroupNames,
+                                 t_blocka*                        grps,
+                                 char**                           gnames)
 {
     int       g, ig = -1, i;
     t_rotgrp* rotg;
@@ -316,12 +320,13 @@ extern void make_rotation_groups(t_rot* rot, char** rotgnames, t_blocka* grps, c
     for (g = 0; g < rot->ngrp; g++)
     {
         rotg      = &rot->grp[g];
-        ig        = search_string(rotgnames[g], grps->nr, gnames);
+        ig        = search_string(rotateGroupNames[g].c_str(), 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);
+            fprintf(stderr, "Rotation group %d '%s' has %d atoms\n", g, rotateGroupNames[g].c_str(),
+                    rotg->nat);
             snew(rotg->ind, rotg->nat);
             for (i = 0; i < rotg->nat; i++)
             {
@@ -330,7 +335,7 @@ extern void make_rotation_groups(t_rot* rot, char** rotgnames, t_blocka* grps, c
         }
         else
         {
-            gmx_fatal(FARGS, "Rotation group %d '%s' is empty", g, rotgnames[g]);
+            gmx_fatal(FARGS, "Rotation group %d '%s' is empty", g, rotateGroupNames[g].c_str());
         }
     }
 }
index a6d2ede4a8d8e08c512cec66635c42d3b8d05be4..30a8b5cbb74b0da543dce6005f8c44c46768758a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -56,6 +57,7 @@
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/futil.h"
+#include "gromacs/utility/logger.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/strdb.h"
 
@@ -91,7 +93,6 @@ PreprocessingAtomTypes read_atype(const char* ffdir, t_symtab* tab)
             {
                 a->m = m;
                 at.addType(tab, *a, name, InteractionOfType({}, {}), 0, 0);
-                fflush(stderr);
             }
             else
             {
@@ -211,7 +212,9 @@ static void print_resbondeds(FILE* out, int bt, const PreprocessResidue& rtpDBEn
     }
 }
 
-static void check_rtp(gmx::ArrayRef<const PreprocessResidue> rtpDBEntry, const std::string& libfn)
+static void check_rtp(gmx::ArrayRef<const PreprocessResidue> rtpDBEntry,
+                      const std::string&                     libfn,
+                      const gmx::MDLogger&                   logger)
 {
     /* check for double entries, assuming list is already sorted */
     for (auto it = rtpDBEntry.begin() + 1; it != rtpDBEntry.end(); it++)
@@ -219,7 +222,9 @@ static void check_rtp(gmx::ArrayRef<const PreprocessResidue> rtpDBEntry, const s
         auto prev = it - 1;
         if (gmx::equalCaseInsensitive(prev->resname, it->resname))
         {
-            fprintf(stderr, "WARNING double entry %s in file %s\n", it->resname.c_str(), libfn.c_str());
+            GMX_LOG(logger.warning)
+                    .asParagraph()
+                    .appendTextFormatted("Double entry %s in file %s", it->resname.c_str(), libfn.c_str());
         }
     }
 }
@@ -252,6 +257,26 @@ static void print_resall_header(FILE* out, gmx::ArrayRef<const PreprocessResidue
             static_cast<int>(rtpDBEntry[0].bRemoveDihedralIfWithImproper));
 }
 
+
+static void print_resall_log(const gmx::MDLogger& logger, gmx::ArrayRef<const PreprocessResidue> rtpDBEntry)
+{
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("[ bondedtypes ]");
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted(
+                    "; bonds  angles  dihedrals  impropers all_dihedrals nr_exclusions  HH14  "
+                    "remove_dih");
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted(
+                    " %5d  %6d  %9d  %9d  %14d  %14d %14d %14d", rtpDBEntry[0].rb[0].type,
+                    rtpDBEntry[0].rb[1].type, rtpDBEntry[0].rb[2].type, rtpDBEntry[0].rb[3].type,
+                    static_cast<int>(rtpDBEntry[0].bKeepAllGeneratedDihedrals),
+                    rtpDBEntry[0].nrexcl, static_cast<int>(rtpDBEntry[0].bGenerateHH14Interactions),
+                    static_cast<int>(rtpDBEntry[0].bRemoveDihedralIfWithImproper));
+}
+
+
 void print_resall(FILE* out, gmx::ArrayRef<const PreprocessResidue> rtpDBEntry, const PreprocessingAtomTypes& atype)
 {
     if (rtpDBEntry.empty())
@@ -278,6 +303,7 @@ void readResidueDatabase(const std::string&              rrdb,
                          std::vector<PreprocessResidue>* rtpDBEntry,
                          PreprocessingAtomTypes*         atype,
                          t_symtab*                       tab,
+                         const gmx::MDLogger&            logger,
                          bool                            bAllowOverrideRTP)
 {
     FILE* in;
@@ -346,33 +372,43 @@ void readResidueDatabase(const std::string&              rrdb,
         get_a_line(in, line, STRLEN);
         if (nparam < 5)
         {
-            fprintf(stderr, "Using default: not generating all possible dihedrals\n");
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted("Using default: not generating all possible dihedrals");
             header_settings.bKeepAllGeneratedDihedrals = FALSE;
         }
         if (nparam < 6)
         {
-            fprintf(stderr, "Using default: excluding 3 bonded neighbors\n");
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted("Using default: excluding 3 bonded neighbors");
             header_settings.nrexcl = 3;
         }
         if (nparam < 7)
         {
-            fprintf(stderr, "Using default: generating 1,4 H--H interactions\n");
+            GMX_LOG(logger.info)
+                    .asParagraph()
+                    .appendTextFormatted("Using default: generating 1,4 H--H interactions");
             header_settings.bGenerateHH14Interactions = TRUE;
         }
         if (nparam < 8)
         {
-            fprintf(stderr,
-                    "Using default: removing proper dihedrals found on the same bond as a proper "
-                    "dihedral\n");
+            GMX_LOG(logger.warning)
+                    .asParagraph()
+                    .appendTextFormatted(
+                            "Using default: removing proper dihedrals found on the same bond as a "
+                            "proper dihedral");
             header_settings.bRemoveDihedralIfWithImproper = TRUE;
         }
     }
     else
     {
-        fprintf(stderr,
-                "Reading .rtp file without '[ bondedtypes ]' directive,\n"
-                "Will proceed as if the entry was:\n");
-        print_resall_header(stderr, gmx::arrayRefFromArray(&header_settings, 1));
+        GMX_LOG(logger.warning)
+                .asParagraph()
+                .appendTextFormatted(
+                        "Reading .rtp file without '[ bondedtypes ]' directive, "
+                        "Will proceed as if the entry was:");
+        print_resall_log(logger, gmx::arrayRefFromArray(&header_settings, 1));
     }
     /* We don't know the current size of rrtp, but simply realloc immediately */
     auto oldArrayEnd = rtpDBEntry->end();
@@ -441,10 +477,12 @@ void readResidueDatabase(const std::string&              rrdb,
             }
             if (bAllowOverrideRTP)
             {
-                fprintf(stderr,
-                        "Found another rtp entry for '%s' in '%s', ignoring this entry and keeping "
-                        "the one from '%s.rtp'\n",
-                        res->resname.c_str(), rrdb.c_str(), found->filebase.c_str());
+                GMX_LOG(logger.warning)
+                        .asParagraph()
+                        .appendTextFormatted(
+                                "Found another rtp entry for '%s' in '%s',"
+                                " ignoring this entry and keeping the one from '%s.rtp'",
+                                res->resname.c_str(), rrdb.c_str(), found->filebase.c_str());
                 /* We should free all the data for this entry.
                  * The current code gives a lot of dangling pointers.
                  */
@@ -467,7 +505,7 @@ void readResidueDatabase(const std::string&              rrdb,
                 [](const char& c1, const char& c2) { return std::toupper(c1) < std::toupper(c2); });
     });
 
-    check_rtp(*rtpDBEntry, rrdb);
+    check_rtp(*rtpDBEntry, rrdb, logger);
 }
 
 /************************************************************
@@ -502,7 +540,9 @@ static int neq_str_sign(const char* a1, const char* a2)
     }
 }
 
-std::string searchResidueDatabase(const std::string& key, gmx::ArrayRef<const PreprocessResidue> rtpDBEntry)
+std::string searchResidueDatabase(const std::string&                     key,
+                                  gmx::ArrayRef<const PreprocessResidue> rtpDBEntry,
+                                  const gmx::MDLogger&                   logger)
 {
     int         nbest, best, besti;
     std::string bestbuf;
@@ -558,10 +598,12 @@ std::string searchResidueDatabase(const std::string& key, gmx::ArrayRef<const Pr
     }
     if (!gmx::equalCaseInsensitive(rtpDBEntry[besti].resname, key))
     {
-        fprintf(stderr,
-                "\nWARNING: '%s' not found in residue topology database, "
-                "trying to use '%s'\n\n",
-                key.c_str(), rtpDBEntry[besti].resname.c_str());
+        GMX_LOG(logger.warning)
+                .asParagraph()
+                .appendTextFormatted(
+                        "'%s' not found in residue topology database, "
+                        "trying to use '%s'",
+                        key.c_str(), rtpDBEntry[besti].resname.c_str());
     }
 
     return rtpDBEntry[besti].resname;
index 78c74436f01c5181b3da7c10c51b54c6468480be..a4fb8761a3412992d200061074d31259c0286385 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/arrayref.h"
 
 class PreprocessingAtomTypes;
+
+namespace gmx
+{
+class MDLogger;
+}
 struct PreprocessResidue;
 struct t_symtab;
 
@@ -60,9 +65,12 @@ struct t_symtab;
  *
  * \param[in] key The atomname to search for.
  * \param[in] rtpDBEntry Database with residue information.
+ * \param[in] logger Logging object.
  * \returns The rtp residue name.
  */
-std::string searchResidueDatabase(const std::string& key, gmx::ArrayRef<const PreprocessResidue> rtpDBEntry);
+std::string searchResidueDatabase(const std::string&                     key,
+                                  gmx::ArrayRef<const PreprocessResidue> rtpDBEntry,
+                                  const gmx::MDLogger&                   logger);
 
 /*! \brief
  * Returns matching entry in database.
@@ -90,12 +98,14 @@ PreprocessingAtomTypes read_atype(const char* ffdir, t_symtab* tab);
  * \param[inout] rtpDBEntry Database to populate.
  * \param[inout] atype Atomtype information.
  * \param[inout] tab Symbol table for names.
+ * \param[in] logger MDLogger interface.
  * \param[in] bAllowOverrideRTP If entries can be overwritten in the database.
  */
 void readResidueDatabase(const std::string&              resdb,
                          std::vector<PreprocessResidue>* rtpDBEntry,
                          PreprocessingAtomTypes*         atype,
                          t_symtab*                       tab,
+                         const gmx::MDLogger&            logger,
                          bool                            bAllowOverrideRTP);
 
 /*! \brief
index 23cad4d79b2b5e355958ed8ac16a61dcd497b508..054d5b223ac1f3dff596ecd101c6de159c960903 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -607,7 +608,7 @@ static void add_solv(const char*        filename,
                      t_symtab*          symtab,
                      std::vector<RVec>* x,
                      std::vector<RVec>* v,
-                     int                ePBC,
+                     PbcType            pbcType,
                      matrix             box,
                      AtomProperties*    aps,
                      real               defaultDistance,
@@ -618,13 +619,13 @@ static void add_solv(const char*        filename,
     gmx_mtop_t        topSolvent;
     std::vector<RVec> xSolvent, vSolvent;
     matrix            boxSolvent = { { 0 } };
-    int               ePBCSolvent;
+    PbcType           pbcTypeSolvent;
 
     fprintf(stderr, "Reading solvent configuration\n");
     bool  bTprFileWasRead;
     rvec *temporaryX = nullptr, *temporaryV = nullptr;
     readConfAndTopology(gmx::findLibraryFile(filename).c_str(), &bTprFileWasRead, &topSolvent,
-                        &ePBCSolvent, &temporaryX, &temporaryV, boxSolvent);
+                        &pbcTypeSolvent, &temporaryX, &temporaryV, boxSolvent);
     t_atoms* atomsSolvent;
     snew(atomsSolvent, 1);
     *atomsSolvent = gmx_mtop_global_atoms(&topSolvent);
@@ -654,7 +655,7 @@ static void add_solv(const char*        filename,
     /* generate a new solvent configuration */
     fprintf(stderr, "Generating solvent configuration\n");
     t_pbc pbc;
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
     if (!gmx::boxesAreEqual(boxSolvent, box))
     {
         if (TRICLINIC(boxSolvent))
@@ -666,7 +667,7 @@ static void add_solv(const char*        filename,
         /* apply pbc for solvent configuration for whole molecules */
         rm_res_pbc(atomsSolvent, &xSolvent, boxSolvent);
         replicateSolventBox(atomsSolvent, &xSolvent, &vSolvent, &exclusionDistances_solvt, boxSolvent, box);
-        if (ePBC != epbcNONE)
+        if (pbcType != PbcType::No)
         {
             removeSolventBoxOverlap(atomsSolvent, &xSolvent, &vSolvent, &exclusionDistances_solvt, pbc);
         }
@@ -963,8 +964,8 @@ int gmx_solvate(int argc, char* argv[])
     /* solute configuration data */
     gmx_mtop_t        top;
     std::vector<RVec> x, v;
-    matrix            box  = { { 0 } };
-    int               ePBC = -1;
+    matrix            box     = { { 0 } };
+    PbcType           pbcType = PbcType::Unset;
     t_atoms*          atoms;
     snew(atoms, 1);
     if (bProt)
@@ -974,7 +975,7 @@ int gmx_solvate(int argc, char* argv[])
         fprintf(stderr, "Reading solute configuration%s\n", bReadV ? " and velocities" : "");
         bool  bTprFileWasRead;
         rvec *temporaryX = nullptr, *temporaryV = nullptr;
-        readConfAndTopology(conf_prot, &bTprFileWasRead, &top, &ePBC, &temporaryX,
+        readConfAndTopology(conf_prot, &bTprFileWasRead, &top, &pbcType, &temporaryX,
                             bReadV ? &temporaryV : nullptr, box);
         *atoms = gmx_mtop_global_atoms(&top);
         x.assign(temporaryX, temporaryX + top.natoms);
@@ -998,10 +999,10 @@ int gmx_solvate(int argc, char* argv[])
             firstSolventResidueIndex = atoms->nres;
         }
     }
-    int ePBCForOutput = ePBC;
+    PbcType pbcTypeForOutput = pbcType;
     if (bBox)
     {
-        ePBCForOutput = epbcXYZ;
+        pbcTypeForOutput = PbcType::Xyz;
         clear_mat(box);
         box[XX][XX] = new_box[XX];
         box[YY][YY] = new_box[YY];
@@ -1014,15 +1015,15 @@ int gmx_solvate(int argc, char* argv[])
                   "or give explicit -box command line option");
     }
 
-    add_solv(solventFileName, atoms, &top.symtab, &x, &v, ePBCForOutput, box, &aps, defaultDistance,
-             scaleFactor, r_shell, max_sol);
+    add_solv(solventFileName, atoms, &top.symtab, &x, &v, pbcTypeForOutput, box, &aps,
+             defaultDistance, scaleFactor, r_shell, max_sol);
 
     /* write new configuration 1 to file confout */
     confout = ftp2fn(efSTO, NFILE, fnm);
     fprintf(stderr, "Writing generated configuration to %s\n", confout);
     const char* outputTitle = (bProt ? *top.name : "Generated by gmx solvate");
     write_sto_conf(confout, outputTitle, atoms, as_rvec_array(x.data()),
-                   !v.empty() ? as_rvec_array(v.data()) : nullptr, ePBCForOutput, box);
+                   !v.empty() ? as_rvec_array(v.data()) : nullptr, pbcTypeForOutput, box);
 
     /* print size of generated configuration */
     fprintf(stderr, "\nOutput configuration contains %d atoms in %d residues\n", atoms->nr, atoms->nres);
index f1f22920083cfc7db58179274a1b1e83d27aaa97..5e30f10c28b57b2f4726e2f7a6c7db511177b258 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +48,7 @@
 #include "gromacs/fileio/pdbio.h"
 #include "gromacs/gmxpreprocess/pdb2top.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
index f9434ba01be5a2ce094c68de724070ccd9c77ffa..bdf1f3db8e57f7fc20713895356e9faa4eb896a9 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -424,7 +425,7 @@ std::vector<MoleculePatchDatabase*> filter_ter(gmx::ArrayRef<MoleculePatchDataba
             if (gmx::equalCaseInsensitive(resname, s, 3))
             {
                 found = true;
-                list.push_back(it);
+                list.push_back(&*it);
             }
             else
             {
@@ -477,14 +478,14 @@ std::vector<MoleculePatchDatabase*> filter_ter(gmx::ArrayRef<MoleculePatchDataba
                 });
                 if (found == list.end())
                 {
-                    list.push_back(it);
+                    list.push_back(&*it);
                 }
             }
         }
     }
     if (none_idx != tb.end())
     {
-        list.push_back(none_idx);
+        list.push_back(&*none_idx);
     }
 
     return list;
index f451c4507476dd8581b31cccdc1ad1cd0adae4c2..25288ce0a308d03e70301919160456b555f4890c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <vector>
 
-#include "gromacs/utility/arrayref.h"
-
 class PreprocessingAtomTypes;
 struct MoleculePatchDatabase;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /*! \brief
  * Read database for N&C terminal modifications.
  *
index 3be1db55286c2dbe4e91d227b518b3d3b0620c54..5398c140a3fef4406f59c50f68f76232819ef04f 100644 (file)
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-gmx_add_unit_test(GmxPreprocessTests gmxpreprocess-test
-    editconf.cpp
-    genconf.cpp
-    genion.cpp
-    genrestr.cpp
-    gpp_atomtype.cpp
-    gpp_bond_atomtype.cpp
-    insert_molecules.cpp
-    readir.cpp
-    solvate.cpp
-    topdirs.cpp
-    )
+gmx_add_gtest_executable(gmxpreprocess-test
+    CPP_SOURCE_FILES
+        editconf.cpp
+        genconf.cpp
+        genion.cpp
+        genrestr.cpp
+        gpp_atomtype.cpp
+        gpp_bond_atomtype.cpp
+        insert_molecules.cpp
+        readir.cpp
+        solvate.cpp
+        topdirs.cpp
+        )
+gmx_register_gtest_test(GmxPreprocessTests gmxpreprocess-test SLOW_TEST)
 
 # Currently these can be slow to run in Jenkins, so they are in
 # several test binaries.
 
 set(exename pdb2gmx1-test)
 gmx_add_gtest_executable(${exename}
-   pdb2gmx.cpp
-)
+    CPP_SOURCE_FILES
+       pdb2gmx.cpp
+       )
 target_compile_definitions(${exename} PRIVATE OPLSAA=1 GROMOS=0 AMBER=0 CHARMM=0)
-gmx_register_gtest_test(Pdb2gmx1Test ${exename})
+gmx_register_gtest_test(Pdb2gmx1Test ${exename} SLOW_TEST)
 
 set(exename pdb2gmx2-test)
 gmx_add_gtest_executable(${exename}
-    pdb2gmx.cpp
-    )
+    CPP_SOURCE_FILES
+        pdb2gmx.cpp
+        )
 target_compile_definitions(${exename} PRIVATE OPLSAA=0 GROMOS=1 AMBER=0 CHARMM=0)
-gmx_register_gtest_test(Pdb2gmx2Test ${exename})
+gmx_register_gtest_test(Pdb2gmx2Test ${exename} SLOW_TEST)
 
 set(exename pdb2gmx3-test)
 gmx_add_gtest_executable(${exename}
-    pdb2gmx.cpp
-    )
+    CPP_SOURCE_FILES
+        pdb2gmx.cpp
+        )
 target_compile_definitions(${exename} PRIVATE OPLSAA=0 GROMOS=0 AMBER=1 CHARMM=1)
-gmx_register_gtest_test(Pdb2gmx3Test ${exename})
+gmx_register_gtest_test(Pdb2gmx3Test ${exename} SLOW_TEST)
index 71771bcccb91c79a7cf599aaf7ca6b265e3a6c36..e2591c7392a193e6b3e28a3671d477882fda26da 100644 (file)
@@ -71,8 +71,7 @@ namespace test
 class GetIrTest : public ::testing::Test
 {
 public:
-    GetIrTest() : opts_(), wi_(init_warning(FALSE, 0)), wiGuard_(wi_)
-
+    GetIrTest() : wi_(init_warning(FALSE, 0)), wiGuard_(wi_)
     {
         snew(opts_.include, STRLEN);
         snew(opts_.define, STRLEN);
index f71418a53a8a1b43c5954ff22123b14eb0abb270..3c19abb8dcd8c0e405b70297dd63ab60a2bc28de 100644 (file)
@@ -20,6 +20,8 @@ nsteps                   = 0
 init-step                = 0
 ; Part index is updated automatically on checkpointing (keeps files separate)
 simulation-part          = 1
+; Multiple time-stepping
+mts                      = no
 ; mode for center of mass motion removal
 comm-mode                = Linear
 ; number of steps for center of mass motion removal
index ae3fb3f47c0a1b438fdf869fafa51dfe7fc76696..9e9fe2563592cea62d3828eb378ce763f20b6014 100644 (file)
@@ -20,6 +20,8 @@ nsteps                   = 0
 init-step                = 0
 ; Part index is updated automatically on checkpointing (keeps files separate)
 simulation-part          = 1
+; Multiple time-stepping
+mts                      = no
 ; mode for center of mass motion removal
 comm-mode                = Linear
 ; number of steps for center of mass motion removal
index 1315d0e103240b0f0a817b4a2872a9e59bef032c..a029bb7b3ed543934ebaa6524d987f0f6e6e5ab2 100644 (file)
@@ -20,6 +20,8 @@ nsteps                   = 0
 init-step                = 0
 ; Part index is updated automatically on checkpointing (keeps files separate)
 simulation-part          = 1
+; Multiple time-stepping
+mts                      = no
 ; mode for center of mass motion removal
 comm-mode                = Linear
 ; number of steps for center of mass motion removal
index 93e200d5cd81a78ed8eb7dfa36956324f4bd71df..ec1827434b15c29d4ffca53cb36bf8a77ea37d7b 100644 (file)
@@ -20,6 +20,8 @@ nsteps                   = 0
 init-step                = 0
 ; Part index is updated automatically on checkpointing (keeps files separate)
 simulation-part          = 1
+; Multiple time-stepping
+mts                      = no
 ; mode for center of mass motion removal
 comm-mode                = Linear
 ; number of steps for center of mass motion removal
index f403b671d2b20b6f339ee8ebbcf670a6a3fe6f59..27cc46aa6750fd2f677612e7c72ea8052f2c621c 100644 (file)
@@ -20,6 +20,8 @@ nsteps                   = 0
 init-step                = 0
 ; Part index is updated automatically on checkpointing (keeps files separate)
 simulation-part          = 1
+; Multiple time-stepping
+mts                      = no
 ; mode for center of mass motion removal
 comm-mode                = Linear
 ; number of steps for center of mass motion removal
index f403b671d2b20b6f339ee8ebbcf670a6a3fe6f59..27cc46aa6750fd2f677612e7c72ea8052f2c621c 100644 (file)
@@ -20,6 +20,8 @@ nsteps                   = 0
 init-step                = 0
 ; Part index is updated automatically on checkpointing (keeps files separate)
 simulation-part          = 1
+; Multiple time-stepping
+mts                      = no
 ; mode for center of mass motion removal
 comm-mode                = Linear
 ; number of steps for center of mass motion removal
index f403b671d2b20b6f339ee8ebbcf670a6a3fe6f59..27cc46aa6750fd2f677612e7c72ea8052f2c621c 100644 (file)
@@ -20,6 +20,8 @@ nsteps                   = 0
 init-step                = 0
 ; Part index is updated automatically on checkpointing (keeps files separate)
 simulation-part          = 1
+; Multiple time-stepping
+mts                      = no
 ; mode for center of mass motion removal
 comm-mode                = Linear
 ; number of steps for center of mass motion removal
index 96add6c35ddab08a49d11ede2d58e5b104fd2762..fb7c36b8d3e7a7e105e3818bf69cb50992c442e3 100644 (file)
@@ -20,6 +20,8 @@ nsteps                   = 0
 init-step                = 0
 ; Part index is updated automatically on checkpointing (keeps files separate)
 simulation-part          = 1
+; Multiple time-stepping
+mts                      = no
 ; mode for center of mass motion removal
 comm-mode                = Linear
 ; number of steps for center of mass motion removal
index a78794df85ed984f4d178fd4332fe5c7dc9aec5f..8c0e33901993abbc20aa62eaa7710c83be27c57f 100644 (file)
@@ -20,6 +20,8 @@ nsteps                   = 0
 init_step                = 0
 ; Part index is updated automatically on checkpointing (keeps files separate)
 simulation-part          = 1
+; Multiple time-stepping
+mts                      = no
 ; mode for center of mass motion removal
 comm-mode                = Linear
 ; number of steps for center of mass motion removal
index 7a8f2e737a3c29228b2ad12ddf9a7bfaee26e3d3..cb1b09926b5a16d98ffc976d67cd423514d84b12 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 514a35bcb08d77b65ad2f8d77b80e375b10fbc4a..f49f44ec20e132beb34434613fb69fb173a972aa 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 647279996aa372767e26a70b3d40d5ea96a46b71..e41adeb7145080a4a6ab332657620bab056e00c8 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GMXPREPROCESS_TOMORSE_H
 #define GMX_GMXPREPROCESS_TOMORSE_H
 
-#include "gromacs/utility/arrayref.h"
-
 class PreprocessingAtomTypes;
 struct MoleculeInformation;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 void convert_harmonics(gmx::ArrayRef<MoleculeInformation> mols, PreprocessingAtomTypes* atype);
 
 #endif
index c4096642224aebe92498f9dea7c9213addb7a148..a6139cf3c95b9e4827334caae6d35d2679677b2f 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d49b945f0842b9fd4c88f994c8a361d55150e169..316656d7a63ea08b7e2c5c6718bdc327247f8e5b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,8 +49,8 @@
 
 #include <algorithm>
 #include <memory>
-
 #include <unordered_set>
+
 #include <sys/types.h>
 
 #include "gromacs/fileio/gmxfio.h"
@@ -79,6 +80,7 @@
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/futil.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/logger.h"
 #include "gromacs/utility/pleasecite.h"
 #include "gromacs/utility/smalloc.h"
 
@@ -381,7 +383,8 @@ static char** read_topol(const char*                           infile,
                          bool                                  bFEP,
                          bool                                  bZero,
                          bool                                  usingFullRangeElectrostatics,
-                         warninp*                              wi)
+                         warninp*                              wi,
+                         const gmx::MDLogger&                  logger)
 {
     FILE*                out;
     int                  sl, nb_funct;
@@ -631,9 +634,7 @@ static char** read_topol(const char*                           infile,
                                     bGenPairs ? &pair : nullptr, wi);
                             break;
 
-                        case Directive::d_bondtypes:
-                            push_bt(d, interactions, 2, nullptr, &bondAtomType, pline, wi);
-                            break;
+                        case Directive::d_bondtypes: // Intended to fall through
                         case Directive::d_constrainttypes:
                             push_bt(d, interactions, 2, nullptr, &bondAtomType, pline, wi);
                             break;
@@ -659,7 +660,7 @@ static char** read_topol(const char*                           infile,
                             push_nbt(d, nbparam, atypes, pline, nb_funct, wi);
                             break;
 
-                        case Directive::d_implicit_genborn_params:
+                        case Directive::d_implicit_genborn_params: // NOLINT bugprone-branch-clone
                             // Skip this line, so old topologies with
                             // GB parameters can be read.
                             break;
@@ -693,19 +694,24 @@ static char** read_topol(const char*                           infile,
                                 generate_nbparams(*combination_rule, nb_funct,
                                                   &(interactions[nb_funct]), atypes, wi);
                                 ncopy = copy_nbparams(nbparam, nb_funct, &(interactions[nb_funct]), ntype);
-                                fprintf(stderr,
-                                        "Generated %d of the %d non-bonded parameter "
-                                        "combinations\n",
-                                        ncombs - ncopy, ncombs);
+                                GMX_LOG(logger.info)
+                                        .asParagraph()
+                                        .appendTextFormatted(
+                                                "Generated %d of the %d non-bonded parameter "
+                                                "combinations",
+                                                ncombs - ncopy, ncombs);
                                 free_nbparam(nbparam, ntype);
                                 if (bGenPairs)
                                 {
                                     gen_pairs((interactions[nb_funct]), &(interactions[F_LJ14]),
                                               fudgeLJ, *combination_rule);
                                     ncopy = copy_nbparams(pair, nb_funct, &(interactions[F_LJ14]), ntype);
-                                    fprintf(stderr,
-                                            "Generated %d of the %d 1-4 parameter combinations\n",
-                                            ncombs - ncopy, ncombs);
+                                    GMX_LOG(logger.info)
+                                            .asParagraph()
+                                            .appendTextFormatted(
+                                                    "Generated %d of the %d 1-4 parameter "
+                                                    "combinations",
+                                                    ncombs - ncopy, ncombs);
                                     free_nbparam(pair, ntype);
                                 }
                                 /* Copy GBSA parameters to atomtype array? */
@@ -818,14 +824,17 @@ static char** read_topol(const char*                           infile,
                             {
                                 gmx_fatal(FARGS, "Molecule type '%s' contains no atoms", *mi0->name);
                             }
-                            fprintf(stderr, "Excluding %d bonded neighbours molecule type '%s'\n",
-                                    mi0->nrexcl, *mi0->name);
+                            GMX_LOG(logger.info)
+                                    .asParagraph()
+                                    .appendTextFormatted(
+                                            "Excluding %d bonded neighbours molecule type '%s'",
+                                            mi0->nrexcl, *mi0->name);
                             sum_q(&mi0->atoms, nrcopies, &qt, &qBt);
                             if (!mi0->bProcessed)
                             {
                                 generate_excl(mi0->nrexcl, mi0->atoms.nr, mi0->interactions, &(mi0->excls));
                                 gmx::mergeExclusions(&(mi0->excls), exclusionBlocks[whichmol]);
-                                make_shake(mi0->interactions, &mi0->atoms, opts->nshake);
+                                make_shake(mi0->interactions, &mi0->atoms, opts->nshake, logger);
 
                                 if (bCouple)
                                 {
@@ -839,7 +848,9 @@ static char** read_topol(const char*                           infile,
                             break;
                         }
                         default:
-                            fprintf(stderr, "case: %d\n", static_cast<int>(d));
+                            GMX_LOG(logger.warning)
+                                    .asParagraph()
+                                    .appendTextFormatted("case: %d", static_cast<int>(d));
                             gmx_incons("unknown directive");
                     }
                 }
@@ -895,6 +906,7 @@ static char** read_topol(const char*                           infile,
                 "algorithms "
                 "can be found at https://doi.org/10.26434/chemrxiv.11474583.v1 .");
     }
+    // TODO: Update URL for Issue #2884 in conjunction with updating grompp.warn in regressiontests.
 
     cpp_done(handle);
 
@@ -904,7 +916,10 @@ static char** read_topol(const char*                           infile,
         {
             gmx_fatal(FARGS, "Did not find any molecules of type '%s' for coupling", opts->couple_moltype);
         }
-        fprintf(stderr, "Coupling %d copies of molecule type '%s'\n", nmol_couple, opts->couple_moltype);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Coupling %d copies of molecule type '%s'", nmol_couple,
+                                     opts->couple_moltype);
     }
 
     /* this is not very clean, but fixes core dump on empty system name */
@@ -960,7 +975,8 @@ char** do_top(bool                                  bVerbose,
               const t_inputrec*                     ir,
               std::vector<gmx_molblock_t>*          molblock,
               bool*                                 ffParametrizedWithHBondConstraints,
-              warninp*                              wi)
+              warninp*                              wi,
+              const gmx::MDLogger&                  logger)
 {
     /* Tmpfile might contain a long path */
     const char* tmpfile;
@@ -977,12 +993,12 @@ char** do_top(bool                                  bVerbose,
 
     if (bVerbose)
     {
-        printf("processing topology...\n");
+        GMX_LOG(logger.info).asParagraph().appendTextFormatted("processing topology...");
     }
     title = read_topol(topfile, tmpfile, opts->define, opts->include, symtab, atypes, molinfo,
                        intermolecular_interactions, interactions, combination_rule, repulsion_power,
                        opts, fudgeQQ, molblock, ffParametrizedWithHBondConstraints,
-                       ir->efep != efepNO, bZero, EEL_FULL(ir->coulombtype), wi);
+                       ir->efep != efepNO, bZero, EEL_FULL(ir->coulombtype), wi, logger);
 
     if ((*combination_rule != eCOMB_GEOMETRIC) && (ir->vdwtype == evdwUSER))
     {
@@ -996,19 +1012,21 @@ char** do_top(bool                                  bVerbose,
 }
 
 /*! \brief
- * Generate exclusion lists for QM/MM.
+ * Exclude molecular interactions for QM atoms handled by MiMic.
  *
- * This routine updates the exclusion lists for QM atoms in order to include all other QM
- * atoms of this molecule. Moreover, this routine replaces bonds between QM atoms with
- * CONNBOND and, when MiMiC is not used, removes bonded interactions between QM and link atoms.
- * Finally, in case if MiMiC QM/MM is used - charges of QM atoms are set to 0
+ * Update the exclusion lists to include all QM atoms of this molecule,
+ * replace bonds between QM atoms with CONNBOND and
+ * set charges of QM atoms to 0.
  *
- * @param molt molecule type with QM atoms
- * @param grpnr group informatio
- * @param ir input record
- * @param qmmmMode QM/MM mode switch: original/MiMiC
+ * \param[in,out] molt molecule type with QM atoms
+ * \param[in] grpnr group informatio
+ * \param[in,out] ir input record
+ * \param[in] logger Handle to logging interface.
  */
-static void generate_qmexcl_moltype(gmx_moltype_t* molt, const unsigned char* grpnr, t_inputrec* ir, GmxQmmmMode qmmmMode)
+static void generate_qmexcl_moltype(gmx_moltype_t*       molt,
+                                    const unsigned char* grpnr,
+                                    t_inputrec*          ir,
+                                    const gmx::MDLogger& logger)
 {
     /* This routine expects molt->ilist to be of size F_NRE and ordered. */
 
@@ -1016,9 +1034,9 @@ static void generate_qmexcl_moltype(gmx_moltype_t* molt, const unsigned char* gr
      * these interactions should be handled by the QM subroutines and
      * not by the gromacs routines
      */
-    int   qm_max = 0, qm_nr = 0, link_nr = 0, link_max = 0;
+    int   qm_max = 0, qm_nr = 0, link_nr = 0;
     int * qm_arr = nullptr, *link_arr = nullptr;
-    bool *bQMMM, *blink;
+    bool* bQMMM;
 
     /* First we search and select the QM atoms in an qm_arr array that
      * we use to create the exclusions.
@@ -1080,9 +1098,12 @@ static void generate_qmexcl_moltype(gmx_moltype_t* molt, const unsigned char* gr
      */
     int ftype_connbond = 0;
     int ind_connbond   = 0;
-    if (molt->ilist[F_CONNBONDS].size() != 0)
+    if (!molt->ilist[F_CONNBONDS].empty())
     {
-        fprintf(stderr, "nr. of CONNBONDS present already: %d\n", molt->ilist[F_CONNBONDS].size() / 3);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("nr. of CONNBONDS present already: %d",
+                                     molt->ilist[F_CONNBONDS].size() / 3);
         ftype_connbond = molt->ilist[F_CONNBONDS].iatoms[0];
         ind_connbond   = molt->ilist[F_CONNBONDS].size();
     }
@@ -1142,14 +1163,7 @@ static void generate_qmexcl_moltype(gmx_moltype_t* molt, const unsigned char* gr
 
                 /* MiMiC treats link atoms as quantum atoms - therefore
                  * we do not need do additional exclusions here */
-                if (qmmmMode == GmxQmmmMode::GMX_QMMM_MIMIC)
-                {
-                    bexcl = numQmAtoms == nratoms;
-                }
-                else
-                {
-                    bexcl = (numQmAtoms >= nratoms - 1);
-                }
+                bexcl = numQmAtoms == nratoms;
 
                 if (bexcl && ftype == F_SETTLE)
                 {
@@ -1182,51 +1196,6 @@ static void generate_qmexcl_moltype(gmx_moltype_t* molt, const unsigned char* gr
      * linkatoms interaction with the QMatoms and would be counted
      * twice.  */
 
-    if (qmmmMode != GmxQmmmMode::GMX_QMMM_MIMIC)
-    {
-        for (int i = 0; i < F_NRE; i++)
-        {
-            if (IS_CHEMBOND(i))
-            {
-                int j = 0;
-                while (j < molt->ilist[i].size())
-                {
-                    int a1 = molt->ilist[i].iatoms[j + 1];
-                    int a2 = molt->ilist[i].iatoms[j + 2];
-                    if ((bQMMM[a1] && !bQMMM[a2]) || (!bQMMM[a1] && bQMMM[a2]))
-                    {
-                        if (link_nr >= link_max)
-                        {
-                            link_max += 10;
-                            srenew(link_arr, link_max);
-                        }
-                        if (bQMMM[a1])
-                        {
-                            link_arr[link_nr++] = a2;
-                        }
-                        else
-                        {
-                            link_arr[link_nr++] = a1;
-                        }
-                    }
-                    j += 3;
-                }
-            }
-        }
-    }
-    snew(blink, molt->atoms.nr);
-    for (int i = 0; i < molt->atoms.nr; i++)
-    {
-        blink[i] = FALSE;
-    }
-
-    if (qmmmMode != GmxQmmmMode::GMX_QMMM_MIMIC)
-    {
-        for (int i = 0; i < link_nr; i++)
-        {
-            blink[link_arr[i]] = TRUE;
-        }
-    }
     /* creating the exclusion block for the QM atoms. Each QM atom has
      * as excluded elements all the other QMatoms (and itself).
      */
@@ -1251,14 +1220,6 @@ static void generate_qmexcl_moltype(gmx_moltype_t* molt, const unsigned char* gr
             }
             j += (qm_nr + link_nr);
         }
-        if (blink[i])
-        {
-            for (int k = 0; k < qm_nr; k++)
-            {
-                qmexcl.a[k + j] = qm_arr[k];
-            }
-            j += qm_nr;
-        }
     }
     qmexcl.index[qmexcl.nr] = j;
 
@@ -1280,10 +1241,9 @@ static void generate_qmexcl_moltype(gmx_moltype_t* molt, const unsigned char* gr
         int j       = 0;
         while (j < molt->ilist[i].size())
         {
-            int  a1 = molt->ilist[i].iatoms[j + 1];
-            int  a2 = molt->ilist[i].iatoms[j + 2];
-            bool bexcl =
-                    ((bQMMM[a1] && bQMMM[a2]) || (blink[a1] && bQMMM[a2]) || (bQMMM[a1] && blink[a2]));
+            int  a1    = molt->ilist[i].iatoms[j + 1];
+            int  a2    = molt->ilist[i].iatoms[j + 2];
+            bool bexcl = (bQMMM[a1] && bQMMM[a2]);
             if (bexcl)
             {
                 /* since the interaction involves QM atoms, these should be
@@ -1306,10 +1266,9 @@ static void generate_qmexcl_moltype(gmx_moltype_t* molt, const unsigned char* gr
     free(qm_arr);
     free(bQMMM);
     free(link_arr);
-    free(blink);
 } /* generate_qmexcl */
 
-void generate_qmexcl(gmx_mtop_t* sys, t_inputrec* ir, warninp* wi, GmxQmmmMode qmmmMode)
+void generate_qmexcl(gmx_mtop_t* sys, t_inputrec* ir, const gmx::MDLogger& logger)
 {
     /* This routine expects molt->molt[m].ilist to be of size F_NRE and ordered.
      */
@@ -1382,11 +1341,11 @@ void generate_qmexcl(gmx_mtop_t* sys, t_inputrec* ir, warninp* wi, GmxQmmmMode q
                     /* Copy the exclusions to a new array, since this is the only
                      * thing that needs to be modified for QMMM.
                      */
-                    copy_blocka(&sys->moltype[molb->type].excls, &sys->moltype.back().excls);
+                    sys->moltype.back().excls = sys->moltype[molb->type].excls;
                     /* Set the molecule type for the QMMM molblock */
                     molb->type = sys->moltype.size() - 1;
                 }
-                generate_qmexcl_moltype(&sys->moltype[molb->type], grpnr, ir, qmmmMode);
+                generate_qmexcl_moltype(&sys->moltype[molb->type], grpnr, ir, logger);
             }
             if (grpnr)
             {
@@ -1395,24 +1354,4 @@ void generate_qmexcl(gmx_mtop_t* sys, t_inputrec* ir, warninp* wi, GmxQmmmMode q
             index_offset += nat_mol;
         }
     }
-    if (qmmmMode == GmxQmmmMode::GMX_QMMM_ORIGINAL && nr_mol_with_qm_atoms > 1)
-    {
-        /* generate a warning is there are QM atoms in different
-         * topologies. In this case it is not possible at this stage to
-         * mutualy exclude the non-bonded interactions via the
-         * exclusions (AFAIK). Instead, the user is advised to use the
-         * energy group exclusions in the mdp file
-         */
-        warning_note(wi,
-                     "\nThe QM subsystem is divided over multiple topologies. "
-                     "The mutual non-bonded interactions cannot be excluded. "
-                     "There are two ways to achieve this:\n\n"
-                     "1) merge the topologies, such that the atoms of the QM "
-                     "subsystem are all present in one single topology file. "
-                     "In this case this warning will dissappear\n\n"
-                     "2) exclude the non-bonded interactions explicitly via the "
-                     "energygrp-excl option in the mdp file. if this is the case "
-                     "this warning may be ignored"
-                     "\n\n");
-    }
 }
index 70d85cef572a72712caff1525c344e05f09cd9d7..f39c8ec2f0fe17a100ac8deef983120655ca5dd6 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -41,7 +42,6 @@
 #include <memory>
 #include <vector>
 
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/real.h"
 
 struct gmx_molblock_t;
@@ -53,9 +53,15 @@ struct MoleculeInformation;
 struct InteractionsOfType;
 struct t_symtab;
 struct warninp;
-enum struct GmxQmmmMode;
 typedef warninp* warninp_t;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+} // namespace gmx
+
 double check_mol(const gmx_mtop_t* mtop, warninp_t wi);
 /* Check mass and charge */
 
@@ -75,9 +81,10 @@ char** do_top(bool                                  bVerbose,
               const t_inputrec*                     ir,
               std::vector<gmx_molblock_t>*          molblock,
               bool*                                 ffParametrizedWithHBondConstraints,
-              warninp_t                             wi);
+              warninp_t                             wi,
+              const gmx::MDLogger&                  logger);
 
 /* This routine expects sys->molt[m].ilist to be of size F_NRE and ordered. */
-void generate_qmexcl(gmx_mtop_t* sys, t_inputrec* ir, warninp_t wi, GmxQmmmMode qmmmMode);
+void generate_qmexcl(gmx_mtop_t* sys, t_inputrec* ir, const gmx::MDLogger& logger);
 
 #endif
index af199cdc799e50b0184338e1ce7ccc60d8217dc9..1c61976460dc08f5f04a9d49887aeac3d7511bb6 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -2615,10 +2616,8 @@ static void convert_pairs_to_pairsQ(gmx::ArrayRef<InteractionsOfType> interactio
 
 static void generate_LJCpairsNB(MoleculeInformation* mol, int nb_funct, InteractionsOfType* nbp, warninp* wi)
 {
-    int       n, ntype;
-    t_atom*   atom;
-    t_blocka* excl;
-    bool      bExcl;
+    int     n, ntype;
+    t_atom* atom;
 
     n    = mol->atoms.nr;
     atom = mol->atoms.atom;
@@ -2628,20 +2627,20 @@ static void generate_LJCpairsNB(MoleculeInformation* mol, int nb_funct, Interact
                "Number of pairs of generated non-bonded parameters should be a perfect square");
 
     /* Add a pair interaction for all non-excluded atom pairs */
-    excl = &mol->excls;
+    const auto& excls = mol->excls;
     for (int i = 0; i < n; i++)
     {
         for (int j = i + 1; j < n; j++)
         {
-            bExcl = FALSE;
-            for (int k = excl->index[i]; k < excl->index[i + 1]; k++)
+            bool pairIsExcluded = false;
+            for (const int atomK : excls[i])
             {
-                if (excl->a[k] == j)
+                if (atomK == j)
                 {
-                    bExcl = TRUE;
+                    pairIsExcluded = true;
                 }
             }
-            if (!bExcl)
+            if (!pairIsExcluded)
             {
                 if (nb_funct != F_LJ)
                 {
@@ -2661,24 +2660,20 @@ static void generate_LJCpairsNB(MoleculeInformation* mol, int nb_funct, Interact
     }
 }
 
-static void set_excl_all(t_blocka* excl)
+static void set_excl_all(gmx::ListOfLists<int>* excl)
 {
-    int nat, i, j, k;
-
     /* Get rid of the current exclusions and exclude all atom pairs */
-    nat       = excl->nr;
-    excl->nra = nat * nat;
-    srenew(excl->a, excl->nra);
-    k = 0;
-    for (i = 0; i < nat; i++)
+    const int        numAtoms = excl->ssize();
+    std::vector<int> exclusionsForAtom(numAtoms);
+    for (int i = 0; i < numAtoms; i++)
     {
-        excl->index[i] = k;
-        for (j = 0; j < nat; j++)
-        {
-            excl->a[k++] = j;
-        }
+        exclusionsForAtom[i] = i;
+    }
+    excl->clear();
+    for (int i = 0; i < numAtoms; i++)
+    {
+        excl->pushBack(exclusionsForAtom);
     }
-    excl->index[nat] = k;
 }
 
 static void decouple_atoms(t_atoms*    atoms,
index bd0f13c2695a0059e5229a5474ab534494f5d2d2..66df62b9d303446f36f023f708b54d2063a07cb3 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,7 +41,6 @@
 
 #include <vector>
 
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/real.h"
 
 enum class Directive : int;
@@ -57,6 +57,8 @@ struct warninp;
 
 namespace gmx
 {
+template<typename>
+class ArrayRef;
 struct ExclusionBlock;
 } // namespace gmx
 
index 4851e85c2c46ffebc5f29f249347d55669e7bb12..d1c8abba6723a967c5a8c39b6a4d903e91c96521 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -51,6 +51,7 @@
 #include "gromacs/math/units.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/logger.h"
 #include "gromacs/utility/smalloc.h"
 
 static int count_hydrogens(char*** atomname, int nra, gmx::ArrayRef<const int> a)
@@ -74,7 +75,7 @@ static int count_hydrogens(char*** atomname, int nra, gmx::ArrayRef<const int> a
     return nh;
 }
 
-void make_shake(gmx::ArrayRef<InteractionsOfType> plist, t_atoms* atoms, int nshake)
+void make_shake(gmx::ArrayRef<InteractionsOfType> plist, t_atoms* atoms, int nshake, const gmx::MDLogger& logger)
 {
     char*** info = atoms->atomname;
     real    b_ij, b_jk;
@@ -82,10 +83,26 @@ void make_shake(gmx::ArrayRef<InteractionsOfType> plist, t_atoms* atoms, int nsh
     {
         switch (nshake)
         {
-            case eshHBONDS: printf("turning H bonds into constraints...\n"); break;
-            case eshALLBONDS: printf("turning all bonds into constraints...\n"); break;
-            case eshHANGLES: printf("turning all bonds and H angles into constraints...\n"); break;
-            case eshALLANGLES: printf("turning all bonds and angles into constraints...\n"); break;
+            case eshHBONDS:
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted("turning H bonds into constraints...");
+                break;
+            case eshALLBONDS:
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted("turning all bonds into constraints...");
+                break;
+            case eshHANGLES:
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted("turning all bonds and H angles into constraints...");
+                break;
+            case eshALLANGLES:
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted("turning all bonds and angles into constraints...");
+                break;
             default: gmx_fatal(FARGS, "Invalid option for make_shake (%d)", nshake);
         }
 
@@ -111,7 +128,10 @@ void make_shake(gmx::ArrayRef<InteractionsOfType> plist, t_atoms* atoms, int nsh
                             {
                                 const InteractionOfType* ang = &(*parm);
 #ifdef DEBUG
-                                printf("Angle: %d-%d-%d\n", ang->ai(), ang->aj(), ang->ak());
+                                GMX_LOG(logger.info)
+                                        .asParagraph()
+                                        .appendTextFormatted("Angle: %d-%d-%d", ang->ai(),
+                                                             ang->aj(), ang->ak());
 #endif
                                 int numhydrogens = count_hydrogens(info, 3, ang->atoms());
                                 if ((nshake == eshALLANGLES) || (numhydrogens > 1)
@@ -155,8 +175,11 @@ void make_shake(gmx::ArrayRef<InteractionsOfType> plist, t_atoms* atoms, int nsh
                                         }
                                         /* apply law of cosines */
 #ifdef DEBUG
-                                        printf("p: %d, q: %d, dist: %12.5e\n", atomNumbers[0],
-                                               atomNumbers[1], forceParm[0]);
+                                        GMX_LOG(logger.info)
+                                                .asParagraph()
+                                                .appendTextFormatted("p: %d, q: %d, dist: %12.5e",
+                                                                     atomNumbers[0], atomNumbers[1],
+                                                                     forceParm[0]);
 #endif
                                         add_param_to_list(&(plist[F_CONSTR]),
                                                           InteractionOfType(atomNumbers, forceParm));
index db9f9b22009306272369ecf061d54211a63939fb..93b7bd8e389847d61c716ac8ddb765ca04d16ea9 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GMXPREPROCESS_TOPSHAKE_H
 #define GMX_GMXPREPROCESS_TOPSHAKE_H
 
-#include "gromacs/utility/arrayref.h"
-
 struct t_atoms;
 struct InteractionsOfType;
 
-void make_shake(gmx::ArrayRef<InteractionsOfType> plist, t_atoms* atoms, int nshake);
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+} // namespace gmx
+
+void make_shake(gmx::ArrayRef<InteractionsOfType> plist, t_atoms* atoms, int nshake, const gmx::MDLogger& logger);
 
 #endif
index 6ccc4d362061cd6e952fb43778c0cdf16896b5e1..10ebbfce3e417c6d23309bca8db42c32dc63d2c6 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -89,7 +90,7 @@ static void print_bt(FILE*                                   out,
     int f = 0;
     switch (ftype)
     {
-        case F_G96ANGLES: f = 1; break;
+        case F_G96ANGLES: // Intended to fall through
         case F_G96BONDS: f = 1; break;
         case F_MORSE: f = 2; break;
         case F_CUBICBONDS: f = 3; break;
@@ -98,18 +99,18 @@ static void print_bt(FILE*                                   out,
         case F_CROSS_BOND_ANGLES: f = 2; break;
         case F_CROSS_BOND_BONDS: f = 3; break;
         case F_UREY_BRADLEY: f = 4; break;
-        case F_PDIHS:
-        case F_RBDIHS:
+        case F_PDIHS:  // Intended to fall through
+        case F_RBDIHS: // Intended to fall through
         case F_FOURDIHS: bDih = TRUE; break;
         case F_IDIHS:
             f    = 1;
             bDih = TRUE;
             break;
-        case F_CONSTRNC: f = 1; break;
+        case F_CONSTRNC: // Intended to fall through
         case F_VSITE3FD: f = 1; break;
         case F_VSITE3FAD: f = 2; break;
         case F_VSITE3OUT: f = 3; break;
-        case F_VSITE4FDN: f = 1; break;
+        case F_VSITE4FDN: // Intended to fall through
         case F_CMAP: f = 1; break;
 
         default: bDih = FALSE;
index 829929bd46e32656ae84ba885843e011698fcc5a..ef1fea15f0758611dd08a47833096dbd15d03e44 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <cstdio>
 
-#include "gromacs/utility/arrayref.h"
-
-#include "gromacs/utility/arrayref.h"
-
 enum class Directive : int;
 class PreprocessingAtomTypes;
 struct t_atoms;
@@ -53,6 +49,12 @@ struct MoleculeInformation;
 class InteractionOfType;
 struct InteractionsOfType;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /* UTILITIES */
 
 void add_param_to_list(InteractionsOfType* list, const InteractionOfType& b);
index c57f4460290f7ca934d17ca1d609670cef79e8bf..8d4669f7345585078483274cccc8e0e3fa95f9af 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -59,6 +60,7 @@
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/logger.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/strconvert.h"
 
@@ -638,7 +640,8 @@ static bool calc_vsite3out_param(PreprocessingAtomTypes*                     aty
 
 static bool calc_vsite4fd_param(InteractionOfType*                          vsite,
                                 gmx::ArrayRef<const VsiteBondedInteraction> bonds,
-                                gmx::ArrayRef<const VsiteBondedInteraction> angles)
+                                gmx::ArrayRef<const VsiteBondedInteraction> angles,
+                                const gmx::MDLogger&                        logger)
 {
     /* i = virtual site          |    ,k
      * j = 1st bonded heavy atom | i-j-m
@@ -670,8 +673,11 @@ static bool calc_vsite4fd_param(InteractionOfType*                          vsit
         cosakm = (std::cos(akjm) - std::cos(aijk) * std::cos(aijm)) / (std::sin(aijk) * std::sin(aijm));
         if (cosakl < -1 || cosakl > 1 || cosakm < -1 || cosakm > 1)
         {
-            fprintf(stderr, "virtual site %d: angle ijk = %f, angle ijl = %f, angle ijm = %f\n",
-                    vsite->ai() + 1, RAD2DEG * aijk, RAD2DEG * aijl, RAD2DEG * aijm);
+            GMX_LOG(logger.warning)
+                    .asParagraph()
+                    .appendTextFormatted(
+                            "virtual site %d: angle ijk = %f, angle ijl = %f, angle ijm = %f",
+                            vsite->ai() + 1, RAD2DEG * aijk, RAD2DEG * aijl, RAD2DEG * aijm);
             gmx_fatal(FARGS,
                       "invalid construction in calc_vsite4fd for atom %d: "
                       "cosakl=%f, cosakm=%f\n",
@@ -695,7 +701,8 @@ static bool calc_vsite4fd_param(InteractionOfType*                          vsit
 
 static bool calc_vsite4fdn_param(InteractionOfType*                          vsite,
                                  gmx::ArrayRef<const VsiteBondedInteraction> bonds,
-                                 gmx::ArrayRef<const VsiteBondedInteraction> angles)
+                                 gmx::ArrayRef<const VsiteBondedInteraction> angles,
+                                 const gmx::MDLogger&                        logger)
 {
     /* i = virtual site          |    ,k
      * j = 1st bonded heavy atom | i-j-m
@@ -731,8 +738,11 @@ static bool calc_vsite4fdn_param(InteractionOfType*                          vsi
 
         if (fabs(pl) < 1000 * GMX_REAL_MIN || fabs(pm) < 1000 * GMX_REAL_MIN)
         {
-            fprintf(stderr, "virtual site %d: angle ijk = %f, angle ijl = %f, angle ijm = %f\n",
-                    vsite->ai() + 1, RAD2DEG * aijk, RAD2DEG * aijl, RAD2DEG * aijm);
+            GMX_LOG(logger.warning)
+                    .asParagraph()
+                    .appendTextFormatted(
+                            "virtual site %d: angle ijk = %f, angle ijl = %f, angle ijm = %f",
+                            vsite->ai() + 1, RAD2DEG * aijk, RAD2DEG * aijl, RAD2DEG * aijm);
             gmx_fatal(FARGS,
                       "invalid construction in calc_vsite4fdn for atom %d: "
                       "pl=%f, pm=%f\n",
@@ -751,7 +761,11 @@ static bool calc_vsite4fdn_param(InteractionOfType*                          vsi
 }
 
 
-int set_vsites(bool bVerbose, t_atoms* atoms, PreprocessingAtomTypes* atypes, gmx::ArrayRef<InteractionsOfType> plist)
+int set_vsites(bool                              bVerbose,
+               t_atoms*                          atoms,
+               PreprocessingAtomTypes*           atypes,
+               gmx::ArrayRef<InteractionsOfType> plist,
+               const gmx::MDLogger&              logger)
 {
     int  ftype;
     int  nvsite, nrset;
@@ -796,7 +810,9 @@ int set_vsites(bool bVerbose, t_atoms* atoms, PreprocessingAtomTypes* atypes, gm
                 {
                     if (bVerbose && bFirst)
                     {
-                        fprintf(stderr, "Calculating parameters for virtual sites\n");
+                        GMX_LOG(logger.info)
+                                .asParagraph()
+                                .appendTextFormatted("Calculating parameters for virtual sites");
                         bFirst = FALSE;
                     }
 
@@ -835,11 +851,11 @@ int set_vsites(bool bVerbose, t_atoms* atoms, PreprocessingAtomTypes* atypes, gm
                             break;
                         case F_VSITE4FD:
                             bERROR = calc_vsite4fd_param(&param, allVsiteBondeds.bonds,
-                                                         allVsiteBondeds.angles);
+                                                         allVsiteBondeds.angles, logger);
                             break;
                         case F_VSITE4FDN:
                             bERROR = calc_vsite4fdn_param(&param, allVsiteBondeds.bonds,
-                                                          allVsiteBondeds.angles);
+                                                          allVsiteBondeds.angles, logger);
                             break;
                         default:
                             gmx_fatal(FARGS,
@@ -863,13 +879,15 @@ int set_vsites(bool bVerbose, t_atoms* atoms, PreprocessingAtomTypes* atypes, gm
     return nvsite;
 }
 
-void set_vsites_ptype(bool bVerbose, gmx_moltype_t* molt)
+void set_vsites_ptype(bool bVerbose, gmx_moltype_t* molt, const gmx::MDLogger& logger)
 {
     int ftype, i;
 
     if (bVerbose)
     {
-        fprintf(stderr, "Setting particle type to V for virtual sites\n");
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Setting particle type to V for virtual sites");
     }
     for (ftype = 0; ftype < F_NRE; ftype++)
     {
@@ -882,8 +900,10 @@ void set_vsites_ptype(bool bVerbose, gmx_moltype_t* molt)
 
             if (debug && nrd)
             {
-                fprintf(stderr, "doing %d %s virtual sites\n", (nrd / (nra + 1)),
-                        interaction_function[ftype].longname);
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted("doing %d %s virtual sites", (nrd / (nra + 1)),
+                                             interaction_function[ftype].longname);
             }
 
             for (i = 0; (i < nrd);)
@@ -927,7 +947,10 @@ private:
     int interactionIndex_;
 };
 
-static void check_vsite_constraints(gmx::ArrayRef<InteractionsOfType> plist, int cftype, const int vsite_type[])
+static void check_vsite_constraints(gmx::ArrayRef<InteractionsOfType> plist,
+                                    int                               cftype,
+                                    const int                         vsite_type[],
+                                    const gmx::MDLogger&              logger)
 {
     int n = 0;
     for (const auto& param : plist[cftype].interactionTypes)
@@ -938,8 +961,11 @@ static void check_vsite_constraints(gmx::ArrayRef<InteractionsOfType> plist, int
             int atom = atoms[k];
             if (vsite_type[atom] != NOTSET)
             {
-                fprintf(stderr, "ERROR: Cannot have constraint (%d-%d) with virtual site (%d)\n",
-                        param.ai() + 1, param.aj() + 1, atom + 1);
+                GMX_LOG(logger.info)
+                        .asParagraph()
+                        .appendTextFormatted(
+                                "ERROR: Cannot have constraint (%d-%d) with virtual site (%d)",
+                                param.ai() + 1, param.aj() + 1, atom + 1);
                 n++;
             }
         }
@@ -953,7 +979,8 @@ static void check_vsite_constraints(gmx::ArrayRef<InteractionsOfType> plist, int
 static void clean_vsite_bonds(gmx::ArrayRef<InteractionsOfType>     plist,
                               gmx::ArrayRef<const VsiteAtomMapping> pindex,
                               int                                   cftype,
-                              const int                             vsite_type[])
+                              const int                             vsite_type[],
+                              const gmx::MDLogger&                  logger)
 {
     int                 ftype, nOut;
     int                 nconverted, nremoved;
@@ -1166,23 +1193,31 @@ static void clean_vsite_bonds(gmx::ArrayRef<InteractionsOfType>     plist,
 
     if (nremoved)
     {
-        fprintf(stderr, "Removed   %4d %15ss with virtual sites, %zu left\n", nremoved,
-                interaction_function[cftype].longname, ps->size());
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Removed   %4d %15ss with virtual sites, %zu left", nremoved,
+                                     interaction_function[cftype].longname, ps->size());
     }
     if (nconverted)
     {
-        fprintf(stderr, "Converted %4d %15ss with virtual sites to connections, %zu left\n",
-                nconverted, interaction_function[cftype].longname, ps->size());
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted(
+                        "Converted %4d %15ss with virtual sites to connections, %zu left",
+                        nconverted, interaction_function[cftype].longname, ps->size());
     }
     if (nOut)
     {
-        fprintf(stderr,
-                "Warning: removed %d %ss with vsite with %s construction\n"
-                "         This vsite construction does not guarantee constant "
-                "bond-length\n"
-                "         If the constructions were generated by pdb2gmx ignore "
-                "this warning\n",
-                nOut, interaction_function[cftype].longname, interaction_function[F_VSITE3OUT].longname);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted(
+                        "Warning: removed %d %ss with vsite with %s construction\n"
+                        "         This vsite construction does not guarantee constant "
+                        "bond-length\n"
+                        "         If the constructions were generated by pdb2gmx ignore "
+                        "this warning",
+                        nOut, interaction_function[cftype].longname,
+                        interaction_function[F_VSITE3OUT].longname);
     }
 }
 
@@ -1190,7 +1225,8 @@ static void clean_vsite_angles(gmx::ArrayRef<InteractionsOfType>         plist,
                                gmx::ArrayRef<VsiteAtomMapping>           pindex,
                                int                                       cftype,
                                const int                                 vsite_type[],
-                               gmx::ArrayRef<const Atom2VsiteConnection> at2vc)
+                               gmx::ArrayRef<const Atom2VsiteConnection> at2vc,
+                               const gmx::MDLogger&                      logger)
 {
     int                 atom, at1, at2;
     InteractionsOfType* ps;
@@ -1332,15 +1368,19 @@ static void clean_vsite_angles(gmx::ArrayRef<InteractionsOfType>         plist,
 
     if (oldSize != gmx::ssize(*ps))
     {
-        fprintf(stderr, "Removed   %4zu %15ss with virtual sites, %zu left\n", oldSize - ps->size(),
-                interaction_function[cftype].longname, ps->size());
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Removed   %4zu %15ss with virtual sites, %zu left",
+                                     oldSize - ps->size(), interaction_function[cftype].longname,
+                                     ps->size());
     }
 }
 
 static void clean_vsite_dihs(gmx::ArrayRef<InteractionsOfType>     plist,
                              gmx::ArrayRef<const VsiteAtomMapping> pindex,
                              int                                   cftype,
-                             const int                             vsite_type[])
+                             const int                             vsite_type[],
+                             const gmx::MDLogger&                  logger)
 {
     InteractionsOfType* ps;
 
@@ -1459,13 +1499,19 @@ static void clean_vsite_dihs(gmx::ArrayRef<InteractionsOfType>     plist,
 
     if (oldSize != gmx::ssize(*ps))
     {
-        fprintf(stderr, "Removed   %4zu %15ss with virtual sites, %zu left\n", oldSize - ps->size(),
-                interaction_function[cftype].longname, ps->size());
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Removed   %4zu %15ss with virtual sites, %zu left",
+                                     oldSize - ps->size(), interaction_function[cftype].longname,
+                                     ps->size());
     }
 }
 
 // TODO use gmx::compat::optional for pindex.
-void clean_vsite_bondeds(gmx::ArrayRef<InteractionsOfType> plist, int natoms, bool bRmVSiteBds)
+void clean_vsite_bondeds(gmx::ArrayRef<InteractionsOfType> plist,
+                         int                               natoms,
+                         bool                              bRmVSiteBds,
+                         const gmx::MDLogger&              logger)
 {
     int                               nvsite, vsite;
     int*                              vsite_type;
@@ -1514,8 +1560,10 @@ void clean_vsite_bondeds(gmx::ArrayRef<InteractionsOfType> plist, int natoms, bo
     /* the rest only if we have virtual sites: */
     if (nvsite)
     {
-        fprintf(stderr, "Cleaning up constraints %swith virtual sites\n",
-                bRmVSiteBds ? "and constant bonded interactions " : "");
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("Cleaning up constraints %swith virtual sites",
+                                     bRmVSiteBds ? "and constant bonded interactions " : "");
 
         /* Make a reverse list to avoid ninteractions^2 operations */
         at2vc = make_at2vsitecon(natoms, plist);
@@ -1553,15 +1601,15 @@ void clean_vsite_bondeds(gmx::ArrayRef<InteractionsOfType> plist, int natoms, bo
             {
                 if (interaction_function[ftype].flags & (IF_BTYPE | IF_CONSTRAINT))
                 {
-                    clean_vsite_bonds(plist, pindex, ftype, vsite_type);
+                    clean_vsite_bonds(plist, pindex, ftype, vsite_type, logger);
                 }
                 else if (interaction_function[ftype].flags & IF_ATYPE)
                 {
-                    clean_vsite_angles(plist, pindex, ftype, vsite_type, at2vc);
+                    clean_vsite_angles(plist, pindex, ftype, vsite_type, at2vc, logger);
                 }
                 else if ((ftype == F_PDIHS) || (ftype == F_IDIHS))
                 {
-                    clean_vsite_dihs(plist, pindex, ftype, vsite_type);
+                    clean_vsite_dihs(plist, pindex, ftype, vsite_type, logger);
                 }
             }
         }
@@ -1570,7 +1618,7 @@ void clean_vsite_bondeds(gmx::ArrayRef<InteractionsOfType> plist, int natoms, bo
         {
             if (interaction_function[ftype].flags & IF_CONSTRAINT)
             {
-                check_vsite_constraints(plist, ftype, vsite_type);
+                check_vsite_constraints(plist, ftype, vsite_type, logger);
             }
         }
     }
index 8b597f85559a2cf839649e34bd4e90c0ed30ea20..98d6615114dccc0a5aabaed58b30197c8defb821 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GMXPREPROCESS_VSITE_PARM_H
 #define GMX_GMXPREPROCESS_VSITE_PARM_H
 
-#include "gromacs/utility/arrayref.h"
-
 class PreprocessingAtomTypes;
 struct gmx_moltype_t;
 struct t_atoms;
 struct InteractionsOfType;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+} // namespace gmx
+
 int set_vsites(bool                              bVerbose,
                t_atoms*                          atoms,
                PreprocessingAtomTypes*           atype,
-               gmx::ArrayRef<InteractionsOfType> plist);
+               gmx::ArrayRef<InteractionsOfType> plist,
+               const gmx::MDLogger&              logger);
 /* set parameters for virtual sites, return number of virtual sites */
 
-void set_vsites_ptype(bool bVerbose, gmx_moltype_t* molt);
+void set_vsites_ptype(bool bVerbose, gmx_moltype_t* molt, const gmx::MDLogger& logger);
 /* set ptype to VSite for virtual sites */
 
 /*! \brief Clean up the bonded interactions
  *
  * Throw away all obsolete bonds, angles and dihedrals.
  * Throw away all constraints. */
-void clean_vsite_bondeds(gmx::ArrayRef<InteractionsOfType> ps, int natoms, bool bRmVSiteBds);
+void clean_vsite_bondeds(gmx::ArrayRef<InteractionsOfType> ps,
+                         int                               natoms,
+                         bool                              bRmVSiteBds,
+                         const gmx::MDLogger&              logger);
 
 #endif
index 287d776b8698499c167998230ce2e9f977eb5db9..ed4c0284b389ed1569e3940cecbd6225b58fba5f 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/arraysize.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/filestream.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/logger.h"
+#include "gromacs/utility/loggerbuilder.h"
 #include "gromacs/utility/smalloc.h"
 
 #include "hackblock.h"
@@ -109,15 +113,10 @@ static void mk_bonds(int                 nnm,
     std::array<real, MAXFORCEPARAM> forceParam = { 0.0 };
     if (bPBC)
     {
-        set_pbc(&pbc, -1, box);
+        set_pbc(&pbc, PbcType::Unset, box);
     }
     for (i = 0; (i < atoms->nr); i++)
     {
-        if ((i % 10) == 0)
-        {
-            fprintf(stderr, "\ratom %d", i);
-            fflush(stderr);
-        }
         for (j = i + 1; (j < atoms->nr); j++)
         {
             if (bPBC)
@@ -140,8 +139,6 @@ static void mk_bonds(int                 nnm,
             }
         }
     }
-    fprintf(stderr, "\ratom %d\n", i);
-    fflush(stderr);
 }
 
 static int* set_cgnr(t_atoms* atoms, bool bUsePDBcharge, real* qtot, real* mtot)
@@ -177,7 +174,8 @@ static void set_atom_type(PreprocessingAtomTypes* atypes,
                           InteractionsOfType*     bonds,
                           int*                    nbonds,
                           int                     nnm,
-                          t_nm2type               nm2t[])
+                          t_nm2type               nm2t[],
+                          const gmx::MDLogger&    logger)
 {
     int nresolved;
 
@@ -188,7 +186,9 @@ static void set_atom_type(PreprocessingAtomTypes* atypes,
         gmx_fatal(FARGS, "Could only find a forcefield type for %d out of %d atoms", nresolved, atoms->nr);
     }
 
-    fprintf(stderr, "There are %zu different atom types in your sample\n", atypes->size());
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted("There are %zu different atom types in your sample", atypes->size());
 }
 
 static void lo_set_force_const(InteractionsOfType* plist, real c[], int nrfp, bool bRound, bool bDih, bool bParam)
@@ -263,7 +263,7 @@ static void calc_angles_dihs(InteractionsOfType* ang, InteractionsOfType* dih, c
 
     if (bPBC)
     {
-        set_pbc(&pbc, epbcXYZ, box);
+        set_pbc(&pbc, PbcType::Xyz, box);
     }
     for (auto& angle : ang->interactionTypes)
     {
@@ -398,7 +398,7 @@ int gmx_x2top(int argc, char* argv[])
     int                                   bts[] = { 1, 1, 1, 2 };
     matrix                                box;    /* box length matrix */
     int                                   natoms; /* number of atoms in one molecule  */
-    int                                   epbc;
+    PbcType                               pbcType;
     bool                                  bRTP, bTOP, bOPLS;
     t_symtab                              symtab;
     real                                  qtot, mtot;
@@ -477,9 +477,16 @@ int gmx_x2top(int argc, char* argv[])
         gmx_fatal(FARGS, "Specify at least one output file");
     }
 
+    gmx::LoggerBuilder builder;
+    builder.addTargetStream(gmx::MDLogger::LogLevel::Info, &gmx::TextOutputFile::standardOutput());
+    builder.addTargetStream(gmx::MDLogger::LogLevel::Warning, &gmx::TextOutputFile::standardError());
+    gmx::LoggerOwner logOwner(builder.build());
+    gmx::MDLogger    logger(logOwner.logger());
+
+
     /* Force field selection, interactive or direct */
     choose_ff(strcmp(ff, "select") == 0 ? nullptr : ff, forcefield, sizeof(forcefield), ffdir,
-              sizeof(ffdir));
+              sizeof(ffdir), logger);
 
     bOPLS = (strcmp(forcefield, "oplsaa") == 0);
 
@@ -490,7 +497,7 @@ int gmx_x2top(int argc, char* argv[])
     /* Read coordinates */
     t_topology* top;
     snew(top, 1);
-    read_tps_conf(opt2fn("-f", NFILE, fnm), top, &epbc, &x, nullptr, box, FALSE);
+    read_tps_conf(opt2fn("-f", NFILE, fnm), top, &pbcType, &x, nullptr, box, FALSE);
     t_atoms* atoms = &top->atoms;
     natoms         = atoms->nr;
     if (atoms->pdbinfo == nullptr)
@@ -506,41 +513,47 @@ int gmx_x2top(int argc, char* argv[])
     }
     else
     {
-        printf("There are %d name to type translations in file %s\n", nnm, n2t);
+        GMX_LOG(logger.info)
+                .asParagraph()
+                .appendTextFormatted("There are %d name to type translations in file %s", nnm, n2t);
     }
     if (debug)
     {
         dump_nm2type(debug, nnm, nm2t);
     }
-    printf("Generating bonds from distances...\n");
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Generating bonds from distances...");
     snew(nbonds, atoms->nr);
     mk_bonds(nnm, nm2t, atoms, x, &(plist[F_BONDS]), nbonds, bPBC, box);
 
     open_symtab(&symtab);
     PreprocessingAtomTypes atypes;
-    set_atom_type(&atypes, &symtab, atoms, &(plist[F_BONDS]), nbonds, nnm, nm2t);
+    set_atom_type(&atypes, &symtab, atoms, &(plist[F_BONDS]), nbonds, nnm, nm2t, logger);
 
     /* Make Angles and Dihedrals */
     snew(excls, atoms->nr);
-    printf("Generating angles and dihedrals from bonds...\n");
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted("Generating angles and dihedrals from bonds...");
     gen_pad(atoms, gmx::arrayRefFromArray(&rtp_header_settings, 1), plist, excls, {}, TRUE);
 
     if (!bPairs)
     {
         plist[F_LJ14].interactionTypes.clear();
     }
-    fprintf(stderr,
-            "There are %4zu %s dihedrals, %4zu impropers, %4zu angles\n"
-            "          %4zu pairs,     %4zu bonds and  %4d atoms\n",
-            plist[F_PDIHS].size(), bOPLS ? "Ryckaert-Bellemans" : "proper", plist[F_IDIHS].size(),
-            plist[F_ANGLES].size(), plist[F_LJ14].size(), plist[F_BONDS].size(), atoms->nr);
+    GMX_LOG(logger.info)
+            .asParagraph()
+            .appendTextFormatted(
+                    "There are %4zu %s dihedrals, %4zu impropers, %4zu angles\n"
+                    "          %4zu pairs,     %4zu bonds and  %4d atoms\n",
+                    plist[F_PDIHS].size(), bOPLS ? "Ryckaert-Bellemans" : "proper", plist[F_IDIHS].size(),
+                    plist[F_ANGLES].size(), plist[F_LJ14].size(), plist[F_BONDS].size(), atoms->nr);
 
     calc_angles_dihs(&plist[F_ANGLES], &plist[F_PDIHS], x, bPBC, box);
 
     set_force_const(plist, kb, kt, kp, bRound, bParam);
 
     cgnr = set_cgnr(atoms, bUsePDBcharge, &qtot, &mtot);
-    printf("Total charge is %g, total mass is %g\n", qtot, mtot);
+    GMX_LOG(logger.info).asParagraph().appendTextFormatted("Total charge is %g, total mass is %g", qtot, mtot);
     if (bOPLS)
     {
         bts[2] = 3;
@@ -569,10 +582,12 @@ int gmx_x2top(int argc, char* argv[])
     }
     close_symtab(&symtab);
 
-    printf("\nWARNING: topologies generated by %s can not be trusted at face value.\n",
-           output_env_get_program_display_name(oenv));
-    printf("         Please verify atomtypes and charges by comparison to other\n");
-    printf("         topologies.\n");
+    GMX_LOG(logger.warning)
+            .asParagraph()
+            .appendTextFormatted(
+                    "Topologies generated by %s can not be trusted at face value. "
+                    "Please verify atomtypes and charges by comparison to other topologies.",
+                    output_env_get_program_display_name(oenv));
 
     return 0;
 }
index 7c81461f18708441acf1360b0fad035cebe5f679..4166698511a6bad72f85c17784e1d551a060e665 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index bb2b12b3755717849126079f4dfffe65d583e478..e37cf210e257b38d5b233e68f2a341c7b4e14dec 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GMXPREPROCESS_XLATE_H
 #define GMX_GMXPREPROCESS_XLATE_H
 
-#include "gromacs/utility/arrayref.h"
-
 class ResidueType;
 struct t_atoms;
 struct PreprocessResidue;
 struct t_symtab;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /* If bResname is true renames atoms based on residue names,
  * otherwise renames atoms based on rtp entry names.
  */
index 942141d288cdfca479ac8b121349a40763d5ccc5..ca34184f832a664253cb9cdcdd5be93df83fdf00 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 
 gmx_add_libgromacs_sources(
         clfftinitializer.cpp
+        device_stream_manager.cpp
         hostallocator.cpp
         gpu_utils.cpp
-        gpu_testutils.cpp
         )
-if(GMX_USE_OPENCL)
+if(GMX_GPU_OPENCL)
     gmx_add_libgromacs_sources(
-        gpu_utils_ocl.cpp
+        device_context_ocl.cpp
+        device_stream_ocl.cpp
         ocl_compiler.cpp
         ocl_caching.cpp
         oclutils.cpp
         )
-elseif(GMX_USE_CUDA)
+elseif(GMX_GPU_CUDA)
     gmx_add_libgromacs_sources(
-        cudautils.cu
+        device_context.cpp
+        device_stream.cu
         gpu_utils.cu
         pinning.cu
         pmalloc_cuda.cu
         )
+    _gmx_add_files_to_property(CUDA_SOURCES
+        device_stream_manager.cpp
+       )
+elseif(GMX_GPU_SYCL)
+    gmx_add_libgromacs_sources(
+        devicebuffer_sycl.cpp
+        device_context_sycl.cpp
+        device_stream_sycl.cpp
+        )
+    _gmx_add_files_to_property(SYCL_SOURCES
+        devicebuffer_sycl.cpp
+        device_context_manager.cpp
+        device_context_sycl.cpp
+        device_stream_manager.cpp
+        device_stream_sycl.cpp
+        )
+else()
+    gmx_add_libgromacs_sources(
+        device_context.cpp
+        device_stream.cpp
+    )
 endif()
 
 if (BUILD_TESTING)
index ca6b0c21450248e631b3756e61e7989ed3cb035b..5097649d478cfd8608375da038479712e52f8cb2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,7 +50,7 @@
 #include "gromacs/utility/mutex.h"
 #include "gromacs/utility/stringutil.h"
 
-#if GMX_GPU == GMX_GPU_OPENCL
+#if GMX_GPU_OPENCL
 #    include <clFFT.h>
 #endif
 
@@ -60,21 +60,23 @@ namespace gmx
 namespace
 {
 
+#if GMX_GPU_OPENCL
 /*! \brief The clFFT library may only be initialized once per process,
  * and this is orchestrated by this shared value and mutex.
  *
  * This ensures that thread-MPI and OpenMP builds can't accidentally
  * initialize it more than once. */
 //! @{
-gmx_unused bool g_clfftInitialized = false;
-gmx::Mutex      g_clfftMutex;
+bool       g_clfftInitialized = false;
+gmx::Mutex g_clfftMutex;
 //! @}
+#endif
 
 } // namespace
 
 ClfftInitializer::ClfftInitializer()
 {
-#if GMX_GPU == GMX_GPU_OPENCL
+#if GMX_GPU_OPENCL
     gmx::lock_guard<gmx::Mutex> guard(g_clfftMutex);
     clfftSetupData              fftSetup;
     int                         initErrorCode = clfftInitSetupData(&fftSetup);
@@ -90,14 +92,12 @@ ClfftInitializer::ClfftInitializer()
                 "Failed to initialize the clFFT library, error code %d", initErrorCode)));
     }
     g_clfftInitialized = true;
-#else
-    GMX_UNUSED_VALUE(g_clfftInitialized);
 #endif
 }
 
 ClfftInitializer::~ClfftInitializer()
 {
-#if GMX_GPU == GMX_GPU_OPENCL
+#if GMX_GPU_OPENCL
     gmx::lock_guard<gmx::Mutex> guard(g_clfftMutex);
     if (g_clfftInitialized)
     {
index b07b71565c64e529bc86e1efcb310457c883cceb..25847bd95744a11fba7fdcc198daf715c64da326 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -73,13 +73,13 @@ namespace gmx
  * \todo Consider making a composite object that also handles
  * on-demand compilation, managing lifetime of PME FFT kernel programs
  * to avoid exhausting resources and/or recompiling kernels previously
- * used.  See Redmine #2535.
+ * used.  See Issue #2535.
  *
  * \todo Consider implementing an appropriate flavor of the nifty
  * counter idiom so that both static initialization and
  * deinitialization can work in a fast, leak-free, and thread-safe way
  * without imposing constraints on the calling code.
- * See Redmine #2535. */
+ * See Issue #2535. */
 class ClfftInitializer
 {
 public:
index 713a1224d473ae84cef84c27c4042c7d7b4480d5..b3c4da9b1043ec22461b9243a0c97ea7c2213dd0 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 209b00a95a51515ebe79c5633404055bb34ad70d..83ee02e3b5d71583b5b0853f94e55ab06330d024 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -129,67 +130,3 @@ int cu_copy_H2D_async(void* d_dest, const void* h_src, size_t bytes, cudaStream_
 {
     return cu_copy_H2D(d_dest, h_src, bytes, GpuApiCallBehavior::Async, s);
 }
-
-/*! \brief Set up texture object for an array of type T.
- *
- * Set up texture object for an array of type T and bind it to the device memory
- * \p d_ptr points to.
- *
- * \tparam[in] T        Raw data type
- * \param[out] texObj   texture object to initialize
- * \param[in]  d_ptr    pointer to device global memory to bind \p texObj to
- * \param[in]  sizeInBytes  size of memory area to bind \p texObj to
- */
-template<typename T>
-static void setup1DTexture(cudaTextureObject_t& texObj, void* d_ptr, size_t sizeInBytes)
-{
-    assert(!c_disableCudaTextures);
-
-    cudaError_t      stat;
-    cudaResourceDesc rd;
-    cudaTextureDesc  td;
-
-    memset(&rd, 0, sizeof(rd));
-    rd.resType                = cudaResourceTypeLinear;
-    rd.res.linear.devPtr      = d_ptr;
-    rd.res.linear.desc        = cudaCreateChannelDesc<T>();
-    rd.res.linear.sizeInBytes = sizeInBytes;
-
-    memset(&td, 0, sizeof(td));
-    td.readMode = cudaReadModeElementType;
-    stat        = cudaCreateTextureObject(&texObj, &rd, &td, nullptr);
-    CU_RET_ERR(stat, "cudaCreateTextureObject failed");
-}
-
-template<typename T>
-void initParamLookupTable(T*& d_ptr, cudaTextureObject_t& texObj, const T* h_ptr, int numElem)
-{
-    const size_t sizeInBytes = numElem * sizeof(*d_ptr);
-    cudaError_t  stat        = cudaMalloc((void**)&d_ptr, sizeInBytes);
-    CU_RET_ERR(stat, "cudaMalloc failed in initParamLookupTable");
-    cu_copy_H2D_sync(d_ptr, (void*)h_ptr, sizeInBytes);
-
-    if (!c_disableCudaTextures)
-    {
-        setup1DTexture<T>(texObj, d_ptr, sizeInBytes);
-    }
-}
-
-template<typename T>
-void destroyParamLookupTable(T* d_ptr, cudaTextureObject_t texObj)
-{
-    if (!c_disableCudaTextures && texObj)
-    {
-        CU_RET_ERR(cudaDestroyTextureObject(texObj), "cudaDestroyTextureObject on texObj failed");
-    }
-    CU_RET_ERR(cudaFree(d_ptr), "cudaFree failed");
-}
-
-/*! \brief Add explicit instantiations of init/destroyParamLookupTable() here as needed.
- * One should also verify that the result of cudaCreateChannelDesc<T>() during texture setup
- * looks reasonable, when instantiating the templates for new types - just in case.
- */
-template void initParamLookupTable<float>(float*&, cudaTextureObject_t&, const float*, int);
-template void destroyParamLookupTable<float>(float*, cudaTextureObject_t);
-template void initParamLookupTable<int>(int*&, cudaTextureObject_t&, const int*, int);
-template void destroyParamLookupTable<int>(int*, cudaTextureObject_t);
index c713bc940827265f0a06518e8f4bdf48a7502245..d03768a04b949011e523ca0067f37ddcf68c1f70 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,6 +41,7 @@
 #include <array>
 #include <string>
 
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/gputraits.cuh"
 #include "gromacs/math/vec.h"
 #include "gromacs/math/vectypes.h"
@@ -141,82 +143,10 @@ enum class GpuApiCallBehavior;
 
 #endif /* CHECK_CUDA_ERRORS */
 
-/*! \brief CUDA device information.
- *
- * The CUDA device information is queried and set at detection and contains
- * both information about the device/hardware returned by the runtime as well
- * as additional data like support status.
- */
-struct gmx_device_info_t
-{
-    int            id;   /* id of the CUDA device */
-    cudaDeviceProp prop; /* CUDA device properties */
-    int            stat; /* result of the device check */
-};
-
-/*! Launches synchronous or asynchronous device to host memory copy.
- *
- *  The copy is launched in stream s or if not specified, in stream 0.
- */
-int cu_copy_D2H(void* h_dest, void* d_src, size_t bytes, GpuApiCallBehavior transferKind, cudaStream_t /*s = nullptr*/);
-
-/*! Launches synchronous host to device memory copy in stream 0. */
-int cu_copy_D2H_sync(void* /*h_dest*/, void* /*d_src*/, size_t /*bytes*/);
-
-/*! Launches asynchronous host to device memory copy in stream s. */
-int cu_copy_D2H_async(void* /*h_dest*/, void* /*d_src*/, size_t /*bytes*/, cudaStream_t /*s = nullptr*/);
-
-/*! Launches synchronous or asynchronous host to device memory copy.
- *
- *  The copy is launched in stream s or if not specified, in stream 0.
- */
-int cu_copy_H2D(void* d_dest, const void* h_src, size_t bytes, GpuApiCallBehavior transferKind, cudaStream_t /*s = nullptr*/);
-
-/*! Launches synchronous host to device memory copy. */
-int cu_copy_H2D_sync(void* /*d_dest*/, const void* /*h_src*/, size_t /*bytes*/);
-
-/*! Launches asynchronous host to device memory copy in stream s. */
-int cu_copy_H2D_async(void* /*d_dest*/, const void* /*h_src*/, size_t /*bytes*/, cudaStream_t /*s = nullptr*/);
-
 // TODO: the 2 functions below are pretty much a constructor/destructor of a simple
 // GPU table object. There is also almost self-contained fetchFromParamLookupTable()
 // in cuda_kernel_utils.cuh. They could all live in a separate class/struct file.
 
-/*! \brief Initialize parameter lookup table.
- *
- * Initializes device memory, copies data from host and binds
- * a texture to allocated device memory to be used for parameter lookup.
- *
- * \tparam[in] T         Raw data type
- * \param[out] d_ptr     device pointer to the memory to be allocated
- * \param[out] texObj    texture object to be initialized
- * \param[in]  h_ptr     pointer to the host memory to be uploaded to the device
- * \param[in]  numElem   number of elements in the h_ptr
- */
-template<typename T>
-void initParamLookupTable(T*& d_ptr, cudaTextureObject_t& texObj, const T* h_ptr, int numElem);
-
-// Add extern declarations so each translation unit understands that
-// there will be a definition provided.
-extern template void initParamLookupTable<int>(int*&, cudaTextureObject_t&, const int*, int);
-extern template void initParamLookupTable<float>(float*&, cudaTextureObject_t&, const float*, int);
-
-/*! \brief Destroy parameter lookup table.
- *
- * Unbinds texture object, deallocates device memory.
- *
- * \tparam[in] T         Raw data type
- * \param[in]  d_ptr     Device pointer to the memory to be deallocated
- * \param[in]  texObj    Texture object to be deinitialized
- */
-template<typename T>
-void destroyParamLookupTable(T* d_ptr, cudaTextureObject_t texObj);
-
-// Add extern declarations so each translation unit understands that
-// there will be a definition provided.
-extern template void destroyParamLookupTable<int>(int*, cudaTextureObject_t);
-extern template void destroyParamLookupTable<float>(float*, cudaTextureObject_t);
-
 /*! \brief Add a triplets stored in a float3 to an rvec variable.
  *
  * \param[out]  a Rvec to increment
@@ -228,25 +158,15 @@ static inline void rvec_inc(rvec a, const float3 b)
     rvec_inc(a, tmp);
 }
 
-/*! \brief Wait for all taks in stream \p s to complete.
- *
- * \param[in] s stream to synchronize with
- */
-static inline void gpuStreamSynchronize(cudaStream_t s)
-{
-    cudaError_t stat = cudaStreamSynchronize(s);
-    CU_RET_ERR(stat, "cudaStreamSynchronize failed");
-}
-
 /*! \brief  Returns true if all tasks in \p s have completed.
  *
- * \param[in] s stream to check
+ *  \param[in] deviceStream CUDA stream to check.
  *
- *  \returns     True if all tasks enqueued in the stream \p s (at the time of this call) have completed.
+ *  \returns True if all tasks enqueued in the stream \p deviceStream (at the time of this call) have completed.
  */
-static inline bool haveStreamTasksCompleted(cudaStream_t s)
+static inline bool haveStreamTasksCompleted(const DeviceStream& deviceStream)
 {
-    cudaError_t stat = cudaStreamQuery(s);
+    cudaError_t stat = cudaStreamQuery(deviceStream.stream());
 
     if (stat == cudaErrorNotReady)
     {
@@ -254,7 +174,7 @@ static inline bool haveStreamTasksCompleted(cudaStream_t s)
         return false;
     }
 
-    GMX_ASSERT(stat != cudaErrorInvalidResourceHandle, "Stream idnetifier not valid");
+    GMX_ASSERT(stat != cudaErrorInvalidResourceHandle, "Stream identifier not valid");
 
     // cudaSuccess and cudaErrorNotReady are the expected return values
     CU_RET_ERR(stat, "Unexpected cudaStreamQuery failure");
@@ -335,6 +255,7 @@ std::array<void*, sizeof...(Args)> prepareGpuKernelArguments(KernelPtr kernel,
  * \tparam    Args            Types of all the kernel arguments
  * \param[in] kernel          Kernel function handle
  * \param[in] config          Kernel configuration for launching
+ * \param[in] deviceStream    GPU stream to launch kernel in
  * \param[in] kernelName      Human readable kernel description, for error handling only
  * \param[in] kernelArgs      Array of the pointers to the kernel arguments, prepared by
  * prepareGpuKernelArguments() \throws gmx::InternalError on kernel launch failure
@@ -342,6 +263,7 @@ std::array<void*, sizeof...(Args)> prepareGpuKernelArguments(KernelPtr kernel,
 template<typename... Args>
 void launchGpuKernel(void (*kernel)(Args...),
                      const KernelLaunchConfig& config,
+                     const DeviceStream&       deviceStream,
                      CommandEvent* /*timingEvent */,
                      const char*                               kernelName,
                      const std::array<void*, sizeof...(Args)>& kernelArgs)
@@ -349,7 +271,7 @@ void launchGpuKernel(void (*kernel)(Args...),
     dim3 blockSize(config.blockSize[0], config.blockSize[1], config.blockSize[2]);
     dim3 gridSize(config.gridSize[0], config.gridSize[1], config.gridSize[2]);
     cudaLaunchKernel((void*)kernel, gridSize, blockSize, const_cast<void**>(kernelArgs.data()),
-                     config.sharedMemorySize, config.stream);
+                     config.sharedMemorySize, deviceStream.stream());
 
     cudaError_t status = cudaGetLastError();
     if (cudaSuccess != status)
similarity index 77%
rename from src/gromacs/utility/mpiinplacebuffers.cpp
rename to src/gromacs/gpu_utils/device_context.cpp
index a35c4181fdb8682adb43f8149f413a5c8ac5c0d4..63efe84bd535ec40099e3dcdedeb7cd581b4e577 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \internal \file
- * \brief Defines cleanup function for the copies necessary when
- * MPI_IN_PLACE is not supported by the MPI library.
+ *
+ * \brief Implements the DeviceContext for CPU build
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
- * \ingroup module_utility
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_gpu_utils
  */
 #include "gmxpre.h"
 
-#include "mpiinplacebuffers.h"
-
-#include "gromacs/utility/smalloc.h"
+#include "device_context.h"
 
-void done_mpi_in_place_buf(mpi_in_place_buf_t* buf)
-{
-    if (nullptr != buf)
-    {
-        sfree(buf->ibuf);
-        sfree(buf->libuf);
-        sfree(buf->fbuf);
-        sfree(buf->dbuf);
-        sfree(buf);
-    }
-}
+//! Constructor.
+DeviceContext::DeviceContext(const DeviceInformation& deviceInfo) : deviceInfo_(deviceInfo) {}
+//! Destructor
+DeviceContext::~DeviceContext() = default;
diff --git a/src/gromacs/gpu_utils/device_context.h b/src/gromacs/gpu_utils/device_context.h
new file mode 100644 (file)
index 0000000..c4ab967
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_DEVICE_CONTEXT_H
+#define GMX_GPU_UTILS_DEVICE_CONTEXT_H
+
+/*! \libinternal \file
+ *
+ * \brief Declarations for DeviceContext class.
+ *
+ * Only needed for OpenCL builds. Other platforms will be given a stub class.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ * \inlibraryapi
+ */
+
+#include "config.h"
+
+#if GMX_GPU_OPENCL
+#    include "gromacs/gpu_utils/gmxopencl.h"
+#endif
+#if GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/gmxsycl.h"
+#endif
+#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/hardware/device_management.h"
+#include "gromacs/utility/classhelpers.h"
+
+struct DeviceInformation;
+
+// Stub for device context
+class DeviceContext
+{
+public:
+    //! Constructor.
+    DeviceContext(const DeviceInformation& deviceInfo);
+    //! Destructor
+    ~DeviceContext();
+
+    //! Get the associated device information
+    const DeviceInformation& deviceInfo() const { return deviceInfo_; }
+
+    void activate() { setActiveDevice(deviceInfo_); }
+
+private:
+    //! A reference to the device information used upon context creation
+    const DeviceInformation& deviceInfo_;
+
+#if GMX_GPU_OPENCL
+public:
+    //! Getter
+    cl_context context() const;
+
+private:
+    //! OpenCL context object
+    cl_context context_ = nullptr;
+#endif
+
+#if GMX_GPU_SYCL
+public:
+    //! Const getter
+    const cl::sycl::context& context() const { return context_; }
+    //! Getter
+    cl::sycl::context& context() { return context_; }
+
+private:
+    //! SYCL context object
+    cl::sycl::context context_;
+#endif
+
+    GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(DeviceContext);
+};
+
+#endif // GMX_GPU_UTILS_DEVICE_CONTEXT_H
diff --git a/src/gromacs/gpu_utils/device_context_ocl.cpp b/src/gromacs/gpu_utils/device_context_ocl.cpp
new file mode 100644 (file)
index 0000000..c7fa8af
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements the DeviceContext for OpenCL
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ */
+#include "gmxpre.h"
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+/*! \brief Copies of values from cl_driver_diagnostics_intel.h,
+ * which isn't guaranteed to be available. */
+/**@{*/
+#define CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL 0x4106
+#define CL_CONTEXT_DIAGNOSTICS_LEVEL_GOOD_INTEL 0x1
+#define CL_CONTEXT_DIAGNOSTICS_LEVEL_BAD_INTEL 0x2
+#define CL_CONTEXT_DIAGNOSTICS_LEVEL_NEUTRAL_INTEL 0x4
+/**@}*/
+
+#ifndef DOXYGEN
+
+DeviceContext::DeviceContext(const DeviceInformation& deviceInfo) : deviceInfo_(deviceInfo)
+{
+    cl_platform_id                     platformId = deviceInfo.oclPlatformId;
+    cl_device_id                       deviceId   = deviceInfo.oclDeviceId;
+    std::vector<cl_context_properties> contextProperties;
+
+    contextProperties.emplace_back(CL_CONTEXT_PLATFORM);
+    contextProperties.emplace_back(reinterpret_cast<cl_context_properties>(platformId));
+
+    if (getenv("GMX_OCL_SHOW_DIAGNOSTICS"))
+    {
+        contextProperties.emplace_back(CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL);
+        contextProperties.emplace_back(CL_CONTEXT_DIAGNOSTICS_LEVEL_BAD_INTEL
+                                       | CL_CONTEXT_DIAGNOSTICS_LEVEL_NEUTRAL_INTEL);
+    }
+    contextProperties.emplace_back(0);
+
+    cl_int clError;
+    context_ = clCreateContext(contextProperties.data(), 1, &deviceId, nullptr, nullptr, &clError);
+    if (clError != CL_SUCCESS)
+    {
+        GMX_THROW(gmx::InternalError(gmx::formatString(
+                "Failed to create OpenCL context on device %s (OpenCL error ID %d).",
+                deviceInfo.device_name, clError)));
+    }
+}
+
+DeviceContext::~DeviceContext()
+{
+    cl_int clError;
+
+    if (context_)
+    {
+        clError = clReleaseContext(context_);
+        GMX_RELEASE_ASSERT(
+                clError == CL_SUCCESS,
+                gmx::formatString("Failed to release OpenCL context (OpenCL error ID %d).", clError).c_str());
+        context_ = nullptr;
+    }
+}
+
+cl_context DeviceContext::context() const
+{
+    return context_;
+}
+
+#endif
similarity index 73%
rename from src/gromacs/gpu_utils/gpu_testutils.cpp
rename to src/gromacs/gpu_utils/device_context_sycl.cpp
index 63a8756afca6f6fc8a05a2085e1dc618de41a40e..7b4f88f31d9c82619f04247a6d1b563adb376801 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \internal \file
- *  \brief Function definitions for GPU detection, specific for tests.
  *
- *  \author Artem Zhmurov <zhmurov@gmail.com>
+ * \brief Implements the DeviceContext for SYCL builds.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \author Andrey Alekseenko <al42and@gmail.com>
+ *
+ * \ingroup module_gpu_utils
  */
 #include "gmxpre.h"
 
-#include "gpu_testutils.h"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/hardware/device_information.h"
 
-#include "gromacs/gpu_utils/gpu_utils.h"
-#include "gromacs/hardware/gpu_hw_info.h"
 
-bool canComputeOnGpu()
+//! Constructor
+DeviceContext::DeviceContext(const DeviceInformation& deviceInfo) :
+    deviceInfo_(deviceInfo),
+    context_(cl::sycl::context(deviceInfo.syclDevice))
 {
-    bool           canComputeOnGpu = false;
-    gmx_gpu_info_t gpuInfo{};
-    if (canPerformGpuDetection())
-    {
-        findGpus(&gpuInfo);
-        canComputeOnGpu = !getCompatibleGpus(gpuInfo).empty();
-    }
-    free_gpu_info(&gpuInfo);
-    return canComputeOnGpu;
 }
+
+//! Destructor
+DeviceContext::~DeviceContext() = default;
diff --git a/src/gromacs/gpu_utils/device_stream.cpp b/src/gromacs/gpu_utils/device_stream.cpp
new file mode 100644 (file)
index 0000000..6a90ffb
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements the DeviceStream for CPU builds.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ */
+#include "gmxpre.h"
+
+#include "device_stream.h"
+
+DeviceStream::DeviceStream(const DeviceContext& /* deviceContext */,
+                           DeviceStreamPriority /* priority */,
+                           const bool /* useTiming */)
+{
+}
+
+DeviceStream::~DeviceStream() = default;
+
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
+bool DeviceStream::isValid() const
+{
+    return false;
+}
+
+void DeviceStream::synchronize() const {};
diff --git a/src/gromacs/gpu_utils/device_stream.cu b/src/gromacs/gpu_utils/device_stream.cu
new file mode 100644 (file)
index 0000000..0e07b00
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements the DeviceStream for CUDA.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ */
+#include "gmxpre.h"
+
+#include "device_stream.h"
+
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+DeviceStream::DeviceStream(const DeviceContext& /* deviceContext */,
+                           DeviceStreamPriority priority,
+                           const bool /* useTiming */)
+{
+    cudaError_t stat;
+
+    if (priority == DeviceStreamPriority::Normal)
+    {
+        stat = cudaStreamCreate(&stream_);
+        if (stat != cudaSuccess)
+        {
+            GMX_THROW(gmx::InternalError(gmx::formatString(
+                    "Could not create CUDA stream (CUDA error %d: %s).", stat, cudaGetErrorString(stat))));
+        }
+    }
+    else if (priority == DeviceStreamPriority::High)
+    {
+        // Note that the device we're running on does not have to
+        // support priorities, because we are querying the priority
+        // range, which in that case will be a single value.
+        int highestPriority;
+        stat = cudaDeviceGetStreamPriorityRange(nullptr, &highestPriority);
+        if (stat != cudaSuccess)
+        {
+            GMX_THROW(gmx::InternalError(gmx::formatString(
+                    "Could not query CUDA stream priority range (CUDA error %d: %s).", stat,
+                    cudaGetErrorString(stat))));
+        }
+
+        stat = cudaStreamCreateWithPriority(&stream_, cudaStreamDefault, highestPriority);
+        if (stat != cudaSuccess)
+        {
+            GMX_THROW(gmx::InternalError(gmx::formatString(
+                    "Could not create CUDA stream with high priority (CUDA error %d: %s).", stat,
+                    cudaGetErrorString(stat))));
+        }
+    }
+}
+
+DeviceStream::~DeviceStream()
+{
+    if (isValid())
+    {
+        cudaError_t stat = cudaStreamDestroy(stream_);
+        GMX_RELEASE_ASSERT(stat == cudaSuccess,
+                           gmx::formatString("Failed to release CUDA stream (CUDA error %d: %s).",
+                                             stat, cudaGetErrorString(stat))
+                                   .c_str());
+        stream_ = nullptr;
+    }
+}
+
+cudaStream_t DeviceStream::stream() const
+{
+    return stream_;
+}
+
+bool DeviceStream::isValid() const
+{
+    return (stream_ != nullptr);
+}
+
+void DeviceStream::synchronize() const
+{
+    cudaError_t stat = cudaStreamSynchronize(stream_);
+    GMX_RELEASE_ASSERT(stat == cudaSuccess,
+                       gmx::formatString("cudaStreamSynchronize failed  (CUDA error %d: %s).", stat,
+                                         cudaGetErrorString(stat))
+                               .c_str());
+}
\ No newline at end of file
diff --git a/src/gromacs/gpu_utils/device_stream.h b/src/gromacs/gpu_utils/device_stream.h
new file mode 100644 (file)
index 0000000..d1aca30
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_DEVICE_STREAM_H
+#define GMX_GPU_UTILS_DEVICE_STREAM_H
+
+/*! \libinternal \file
+ *
+ * \brief Declarations for DeviceStream class.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ * \inlibraryapi
+ */
+
+#include "config.h"
+
+#if GMX_GPU_CUDA
+#    include <cuda_runtime.h>
+
+#elif GMX_GPU_OPENCL
+#    include "gromacs/gpu_utils/gmxopencl.h"
+#elif GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/gmxsycl.h"
+#endif
+#include "gromacs/utility/classhelpers.h"
+
+struct DeviceInformation;
+class DeviceContext;
+
+//! Enumeration describing the priority with which a stream operates.
+enum class DeviceStreamPriority : int
+{
+    //! High-priority stream
+    High,
+    //! Normal-priority stream
+    Normal,
+    //! Conventional termination of the enumeration
+    Count
+};
+
+/*! \libinternal \brief Declaration of platform-agnostic device stream/queue.
+ *
+ * The command stream (or command queue) is a sequence of operations that are executed
+ * in they order they were issued. Several streams may co-exist to represent concurrency.
+ * This class declares the interfaces, that are exposed to platform-agnostic code and
+ * it should be implemented for each compute architecture (e.g. CUDA and OpenCL).
+ *
+ * Destruction of the \p DeviceStream calls the destructor of the underlying low-level
+ * stream/queue, hence should only be called when the stream is no longer needed. To
+ * prevent accidental stream destruction, while copying or moving a \p DeviceStream
+ * object, copy and move constructors and copy and move assignments are not allowed
+ * and the \p DeviceStream object should be passed as a pointer or constant reference.
+ *
+ */
+class DeviceStream
+{
+public:
+    /*! \brief Construct and init.
+     *
+     * \param[in] deviceContext  Device context (only used in OpenCL and SYCL).
+     * \param[in] priority       Stream priority: high or normal (only used in CUDA).
+     * \param[in] useTiming      If the timing should be enabled (only used in OpenCL and SYCL).
+     */
+    DeviceStream(const DeviceContext& deviceContext, DeviceStreamPriority priority, bool useTiming);
+
+    //! Destructor
+    ~DeviceStream();
+
+    /*! \brief Check if the underlying stream is valid.
+     *
+     *  \returns Whether the stream is valid (false in CPU-only builds).
+     */
+    bool isValid() const;
+
+    //! Synchronize the stream
+    void synchronize() const;
+
+#if GMX_GPU_CUDA
+
+    //! Getter
+    cudaStream_t stream() const;
+
+private:
+    cudaStream_t stream_ = nullptr;
+#elif GMX_GPU_SYCL
+
+    /*! \brief
+     * Getter for the underlying \c cl::sycl:queue object.
+     *
+     * Returns a copy instead of const-reference, because it's impossible to submit to or wait
+     * on a \c const cl::sycl::queue. SYCL standard guarantees that operating on copy is
+     * equivalent to operating on the original queue.
+     *
+     * \throws std::bad_optional_access if the stream is not valid.
+     *
+     * \returns A copy of the internal \c cl::sycl:queue.
+     */
+    cl::sycl::queue stream() const { return cl::sycl::queue(stream_); }
+    //! Getter. Can throw std::bad_optional_access if the stream is not valid.
+    cl::sycl::queue& stream() { return stream_; }
+    //! Synchronize the stream. Non-const version of \c ::synchronize() for SYCL that does not do unnecessary copying.
+    void synchronize();
+
+private:
+    cl::sycl::queue stream_;
+#elif GMX_GPU_OPENCL || defined DOXYGEN
+
+    //! Getter
+    cl_command_queue stream() const;
+
+private:
+    cl_command_queue stream_ = nullptr;
+
+#endif
+
+    GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(DeviceStream);
+};
+
+#endif // GMX_GPU_UTILS_DEVICE_STREAM_H
diff --git a/src/gromacs/gpu_utils/device_stream_manager.cpp b/src/gromacs/gpu_utils/device_stream_manager.cpp
new file mode 100644 (file)
index 0000000..c732156
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements GPU stream manager.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ */
+#include "gmxpre.h"
+
+#include "device_stream_manager.h"
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/mdtypes/simulation_workload.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief Impl class to manages the lifetime of the GPU streams.
+ *
+ * If supported by the GPU API, the available runtime and the
+ * indicated device, some streams will be configured at high
+ * priority. Otherwise, all streams will share the default priority
+ * appropriate to the situation.
+ */
+class DeviceStreamManager::Impl
+{
+public:
+    /*! \brief Constructor.
+     *
+     * \throws InternalError  If any of the required resources could not be initialized.
+     */
+    Impl(const DeviceInformation& deviceInfo,
+         bool                     havePpDomainDecomposition,
+         SimulationWorkload       simulationWork,
+         bool                     useTiming);
+    ~Impl();
+
+    //! Device context.
+    DeviceContext context_;
+    //! GPU command streams.
+    EnumerationArray<DeviceStreamType, std::unique_ptr<DeviceStream>> streams_;
+};
+
+// DeviceStreamManager::Impl
+DeviceStreamManager::Impl::Impl(const DeviceInformation& deviceInfo,
+                                const bool               havePpDomainDecomposition,
+                                const SimulationWorkload simulationWork,
+                                const bool               useTiming) :
+    context_(deviceInfo)
+{
+    try
+    {
+        streams_[DeviceStreamType::NonBondedLocal] =
+                std::make_unique<DeviceStream>(context_, DeviceStreamPriority::Normal, useTiming);
+
+        if (simulationWork.useGpuPme)
+        {
+            /* Creating a PME GPU stream:
+             * - default high priority with CUDA
+             * - no priorities implemented yet with OpenCL; see #2532
+             */
+            streams_[DeviceStreamType::Pme] =
+                    std::make_unique<DeviceStream>(context_, DeviceStreamPriority::High, useTiming);
+        }
+
+        if (havePpDomainDecomposition)
+        {
+            streams_[DeviceStreamType::NonBondedNonLocal] =
+                    std::make_unique<DeviceStream>(context_, DeviceStreamPriority::High, useTiming);
+        }
+        // Update stream is used both for coordinates transfers and for GPU update/constraints
+        if (simulationWork.useGpuPme || simulationWork.useGpuUpdate || simulationWork.useGpuBufferOps)
+        {
+            streams_[DeviceStreamType::UpdateAndConstraints] =
+                    std::make_unique<DeviceStream>(context_, DeviceStreamPriority::Normal, useTiming);
+        }
+        if (simulationWork.useGpuPmePpCommunication)
+        {
+            streams_[DeviceStreamType::PmePpTransfer] =
+                    std::make_unique<DeviceStream>(context_, DeviceStreamPriority::Normal, useTiming);
+        }
+    }
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
+}
+
+DeviceStreamManager::Impl::~Impl() = default;
+
+// DeviceStreamManager
+DeviceStreamManager::DeviceStreamManager(const DeviceInformation& deviceInfo,
+                                         const bool               havePpDomainDecomposition,
+                                         const SimulationWorkload simulationWork,
+                                         const bool               useTiming) :
+    impl_(new Impl(deviceInfo, havePpDomainDecomposition, simulationWork, useTiming))
+{
+}
+
+DeviceStreamManager::~DeviceStreamManager() = default;
+
+const DeviceInformation& DeviceStreamManager::deviceInfo() const
+{
+    return impl_->context_.deviceInfo();
+}
+
+const DeviceContext& DeviceStreamManager::context() const
+{
+    return impl_->context_;
+}
+
+const DeviceStream& DeviceStreamManager::stream(DeviceStreamType streamToGet) const
+{
+    return *impl_->streams_[streamToGet];
+}
+
+const DeviceStream& DeviceStreamManager::bondedStream(bool hasPPDomainDecomposition) const
+{
+    if (hasPPDomainDecomposition)
+    {
+        GMX_RELEASE_ASSERT(stream(DeviceStreamType::NonBondedNonLocal).isValid(),
+                           "GPU non-bonded non-local stream should be valid in order to use GPU "
+                           "version of bonded forces with domain decomposition.");
+        return stream(DeviceStreamType::NonBondedNonLocal);
+    }
+    else
+    {
+        GMX_RELEASE_ASSERT(stream(DeviceStreamType::NonBondedLocal).isValid(),
+                           "GPU non-bonded local stream should be valid in order to use GPU "
+                           "version of bonded forces without domain decomposition.");
+        return stream(DeviceStreamType::NonBondedLocal);
+    }
+}
+
+bool DeviceStreamManager::streamIsValid(DeviceStreamType streamToCheck) const
+{
+    return impl_->streams_[streamToCheck] != nullptr && impl_->streams_[streamToCheck]->isValid();
+}
+
+} // namespace gmx
diff --git a/src/gromacs/gpu_utils/device_stream_manager.h b/src/gromacs/gpu_utils/device_stream_manager.h
new file mode 100644 (file)
index 0000000..1f8366e
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares a manager of GPU context and streams needed for
+ * running workloads on GPUs.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_gpu_utils
+ */
+#ifndef GMX_GPU_UTILS_GPUSTREAMMANAGER_H
+#define GMX_GPU_UTILS_GPUSTREAMMANAGER_H
+
+#include <string>
+
+#include "gromacs/utility/classhelpers.h"
+
+class DeviceContext;
+struct DeviceInformation;
+class DeviceStream;
+
+namespace gmx
+{
+
+class SimulationWorkload;
+
+/*! \brief Class enum to describe the different logical streams used
+ * for GPU work.
+ *
+ * Whether the actual streams differ is an implementation detail of
+ * the manager class.
+ */
+enum class DeviceStreamType : int
+{
+    //! Stream primarily for short-ranged local nonbonded work.
+    NonBondedLocal,
+    //! Stream primarily for short-ranged nonlocal nonbonded work.
+    NonBondedNonLocal,
+    //! Stream primarily for PME work.
+    Pme,
+    //! Stream primarily for data exchange between PME and PP ranks.
+    PmePpTransfer,
+    //! Stream primarily for update and constraints.
+    UpdateAndConstraints,
+    //! Conventional termination of the enumeration.
+    Count
+};
+
+/*! \libinternal
+ * \brief Device stream and context manager.
+ *
+ * Manages the lifetime of the GPU streams and their association
+ * with context and device information that is needed to use them.
+ *
+ * If supported by the GPU API, the available runtime and the
+ * indicated device, some streams will be configured at high
+ * priority. Otherwise, all streams will share the default priority
+ * appropriate to the situation.
+ */
+class DeviceStreamManager
+{
+public:
+    /*! \brief Constructor.
+     *
+     * \throws InternalError  If any of the required resources could not be initialized.
+     */
+    DeviceStreamManager(const DeviceInformation& deviceInfo,
+                        bool                     havePpDomainDecomposition,
+                        SimulationWorkload       simulationWork,
+                        bool                     useTiming);
+    ~DeviceStreamManager();
+
+    /*! \brief Get the device information object of the associated device.
+     *
+     * \returns reference to device info.
+     */
+    const DeviceInformation& deviceInfo() const;
+
+    /*! \brief Returns a handle to the GPU context.
+     *
+     * \todo This relies on the fact that only one unique device
+     * is described by nonbondedDeviceInfo and pmeDeviceInfo.
+     */
+    const DeviceContext& context() const;
+
+    /*! \brief Returns a handle to the requested GPU stream.
+     *
+     * \param[in] streamToGet Which stream to get.
+     */
+    const DeviceStream& stream(DeviceStreamType streamToGet) const;
+
+    /*! \brief Returns a handle to the GPU stream to compute bonded forces in.
+     *
+     * \param[in] hasPPDomainDecomposition Whether there is a particle-particle domain decomposition.
+     */
+    const DeviceStream& bondedStream(bool hasPPDomainDecomposition) const;
+
+    /*! \brief Return whether the requested GPU stream is valid for use.
+     *
+     * \param[in] streamToCheck Which stream to check.
+     *
+     * \returns Whether the stream was initialized.
+     */
+    bool streamIsValid(DeviceStreamType streamToCheck) const;
+
+private:
+    class Impl;
+    PrivateImplPointer<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/gromacs/gpu_utils/device_stream_ocl.cpp b/src/gromacs/gpu_utils/device_stream_ocl.cpp
new file mode 100644 (file)
index 0000000..9140616
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements the DeviceStream for OpenCL.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ */
+#include "gmxpre.h"
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/gputraits_ocl.h"
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+DeviceStream::DeviceStream(const DeviceContext& deviceContext,
+                           DeviceStreamPriority /* priority */,
+                           const bool useTiming)
+{
+    const DeviceInformation&    deviceInfo      = deviceContext.deviceInfo();
+    cl_command_queue_properties queueProperties = useTiming ? CL_QUEUE_PROFILING_ENABLE : 0;
+    cl_device_id                deviceId        = deviceInfo.oclDeviceId;
+    cl_int                      clError;
+    stream_ = clCreateCommandQueue(deviceContext.context(), deviceId, queueProperties, &clError);
+    if (clError != CL_SUCCESS)
+    {
+        GMX_THROW(gmx::InternalError(gmx::formatString(
+                "Failed to create OpenCL command queue on GPU %s (OpenCL error ID %d).",
+                deviceInfo.device_name, clError)));
+    }
+}
+
+DeviceStream::~DeviceStream()
+{
+    if (isValid())
+    {
+        cl_int clError = clReleaseCommandQueue(stream_);
+        GMX_RELEASE_ASSERT(
+                clError == CL_SUCCESS,
+                gmx::formatString("Failed to release OpenCL stream (OpenCL error ID %d).", clError).c_str());
+        stream_ = nullptr;
+    }
+}
+
+cl_command_queue DeviceStream::stream() const
+{
+    return stream_;
+}
+
+bool DeviceStream::isValid() const
+{
+    return (stream_ != nullptr);
+}
+
+void DeviceStream::synchronize() const
+{
+    cl_int clError = clFinish(stream_);
+    GMX_RELEASE_ASSERT(
+            CL_SUCCESS == clError,
+            gmx::formatString("Error caught during clFinish (OpenCL error ID %d).", clError).c_str());
+}
diff --git a/src/gromacs/gpu_utils/device_stream_sycl.cpp b/src/gromacs/gpu_utils/device_stream_sycl.cpp
new file mode 100644 (file)
index 0000000..72a431e
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements the DeviceStream for SYCL builds.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \author Andrey Alekseenko <al42and@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ */
+#include "gmxpre.h"
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+
+DeviceStream::DeviceStream(const DeviceContext& deviceContext,
+                           DeviceStreamPriority /* priority */,
+                           const bool useTiming)
+{
+    const std::vector<cl::sycl::device> devicesInContext = deviceContext.context().get_devices();
+    // The context is constructed to have exactly one device
+    const cl::sycl::device device = devicesInContext[0];
+
+    cl::sycl::property_list propertyList = {};
+    if (useTiming)
+    {
+        const bool deviceSupportsTiming = device.get_info<cl::sycl::info::device::queue_profiling>();
+        if (deviceSupportsTiming)
+        {
+            propertyList = cl::sycl::property::queue::enable_profiling();
+        }
+    }
+    stream_ = cl::sycl::queue(deviceContext.context(), device, propertyList);
+}
+
+DeviceStream::~DeviceStream() = default;
+
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
+bool DeviceStream::isValid() const
+{
+    return true;
+}
+
+void DeviceStream::synchronize()
+{
+    stream_.wait_and_throw();
+};
+
+void DeviceStream::synchronize() const
+{
+    /* cl::sycl::queue::wait is a non-const function. However, a lot of code in GROMACS
+     * assumes DeviceStream is const, yet wants to synchronize with it.
+     * The chapter "4.3.2 Common reference semantics" of SYCL 1.2.1 specification says:
+     * > Each of the following SYCL runtime classes: [...] queue, [...] must obey the following
+     * > statements, where T is the runtime class type:
+     * > - T must be copy constructible and copy assignable on the host application [...].
+     * >   Any instance of T that is constructed as a copy of another instance, via either the
+     * >   copy constructor or copy assignment operator, must behave as-if it were the original
+     * >   instance and as-if any action performed on it were also performed on the original
+     * >   instance [...].
+     * Same in chapter "4.5.3" of provisional SYCL 2020 specification (June 30, 2020).
+     * So, we can copy-construct a new queue and wait() on it.
+     */
+    cl::sycl::queue(stream_).wait_and_throw();
+}
index 2005a71bb1020722696773c623f9fa54519aaaa3..565c2c1bc84ac64ca29f3ef538c567db57849879 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  *  \inlibraryapi
  */
 
+#include "gromacs/gpu_utils/cuda_arch_utils.cuh"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/devicebuffer_datatype.h"
 #include "gromacs/gpu_utils/gpu_utils.h" //only for GpuApiCallBehavior
 #include "gromacs/gpu_utils/gputraits.cuh"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
 
 /*! \brief
  * Allocates a device-side buffer.
@@ -60,7 +64,7 @@
  * \param[in]     deviceContext        The buffer's dummy device  context - not managed explicitly in CUDA RT.
  */
 template<typename ValueType>
-void allocateDeviceBuffer(DeviceBuffer<ValueType>* buffer, size_t numValues, DeviceContext /* deviceContext */)
+void allocateDeviceBuffer(DeviceBuffer<ValueType>* buffer, size_t numValues, const DeviceContext& /* deviceContext */)
 {
     GMX_ASSERT(buffer, "needs a buffer pointer");
     cudaError_t stat = cudaMalloc((void**)buffer, numValues * sizeof(ValueType));
@@ -88,14 +92,12 @@ void freeDeviceBuffer(DeviceBuffer* buffer)
 /*! \brief
  * Performs the host-to-device data copy, synchronous or asynchronously on request.
  *
- * TODO: This is meant to gradually replace cu/ocl_copy_h2d.
- *
  * \tparam        ValueType            Raw value type of the \p buffer.
  * \param[in,out] buffer               Pointer to the device-side buffer
  * \param[in]     hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
  * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy into.
  * \param[in]     numValues            Number of values to copy.
- * \param[in]     stream               GPU stream to perform asynchronous copy in.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
  * \param[in]     transferKind         Copy type: synchronous or asynchronous.
  * \param[out]    timingEvent          A dummy pointer to the H2D copy timing event to be filled in.
  *                                     Not used in CUDA implementation.
@@ -105,13 +107,13 @@ void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
                         const ValueType*         hostBuffer,
                         size_t                   startingOffset,
                         size_t                   numValues,
-                        CommandStream            stream,
+                        const DeviceStream&      deviceStream,
                         GpuApiCallBehavior       transferKind,
                         CommandEvent* /*timingEvent*/)
 {
     if (numValues == 0)
     {
-        return; // such calls are actually made with empty domains
+        return;
     }
     GMX_ASSERT(buffer, "needs a buffer pointer");
     GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
@@ -124,7 +126,7 @@ void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
             GMX_ASSERT(isHostMemoryPinned(hostBuffer),
                        "Source host buffer was not pinned for CUDA");
             stat = cudaMemcpyAsync(*((ValueType**)buffer) + startingOffset, hostBuffer, bytes,
-                                   cudaMemcpyHostToDevice, stream);
+                                   cudaMemcpyHostToDevice, deviceStream.stream());
             GMX_RELEASE_ASSERT(stat == cudaSuccess, "Asynchronous H2D copy failed");
             break;
 
@@ -138,18 +140,15 @@ void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
     }
 }
 
-
 /*! \brief
  * Performs the device-to-host data copy, synchronous or asynchronously on request.
  *
- * TODO: This is meant to gradually replace cu/ocl_copy_d2h.
- *
  * \tparam        ValueType            Raw value type of the \p buffer.
  * \param[in,out] hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
  * \param[in]     buffer               Pointer to the device-side buffer
  * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy from.
  * \param[in]     numValues            Number of values to copy.
- * \param[in]     stream               GPU stream to perform asynchronous copy in.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
  * \param[in]     transferKind         Copy type: synchronous or asynchronous.
  * \param[out]    timingEvent          A dummy pointer to the H2D copy timing event to be filled in.
  *                                     Not used in CUDA implementation.
@@ -159,10 +158,14 @@ void copyFromDeviceBuffer(ValueType*               hostBuffer,
                           DeviceBuffer<ValueType>* buffer,
                           size_t                   startingOffset,
                           size_t                   numValues,
-                          CommandStream            stream,
+                          const DeviceStream&      deviceStream,
                           GpuApiCallBehavior       transferKind,
                           CommandEvent* /*timingEvent*/)
 {
+    if (numValues == 0)
+    {
+        return;
+    }
     GMX_ASSERT(buffer, "needs a buffer pointer");
     GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
 
@@ -174,7 +177,7 @@ void copyFromDeviceBuffer(ValueType*               hostBuffer,
             GMX_ASSERT(isHostMemoryPinned(hostBuffer),
                        "Destination host buffer was not pinned for CUDA");
             stat = cudaMemcpyAsync(hostBuffer, *((ValueType**)buffer) + startingOffset, bytes,
-                                   cudaMemcpyDeviceToHost, stream);
+                                   cudaMemcpyDeviceToHost, deviceStream.stream());
             GMX_RELEASE_ASSERT(stat == cudaSuccess, "Asynchronous D2H copy failed");
             break;
 
@@ -191,20 +194,129 @@ void copyFromDeviceBuffer(ValueType*               hostBuffer,
 /*! \brief
  * Clears the device buffer asynchronously.
  *
- * \tparam        ValueType        Raw value type of the \p buffer.
- * \param[in,out] buffer           Pointer to the device-side buffer
- * \param[in]     startingOffset   Offset (in values) at the device-side buffer to start clearing
- * at. \param[in]     numValues        Number of values to clear. \param[in]     stream GPU stream.
+ * \tparam        ValueType       Raw value type of the \p buffer.
+ * \param[in,out] buffer          Pointer to the device-side buffer
+ * \param[in]     startingOffset  Offset (in values) at the device-side buffer to start clearing at.
+ * \param[in]     numValues       Number of values to clear.
+ * \param[in]     deviceStream    GPU stream.
  */
 template<typename ValueType>
-void clearDeviceBufferAsync(DeviceBuffer<ValueType>* buffer, size_t startingOffset, size_t numValues, CommandStream stream)
+void clearDeviceBufferAsync(DeviceBuffer<ValueType>* buffer,
+                            size_t                   startingOffset,
+                            size_t                   numValues,
+                            const DeviceStream&      deviceStream)
 {
     GMX_ASSERT(buffer, "needs a buffer pointer");
     const size_t bytes   = numValues * sizeof(ValueType);
     const char   pattern = 0;
 
-    cudaError_t stat = cudaMemsetAsync(*((ValueType**)buffer) + startingOffset, pattern, bytes, stream);
+    cudaError_t stat = cudaMemsetAsync(*((ValueType**)buffer) + startingOffset, pattern, bytes,
+                                       deviceStream.stream());
     GMX_RELEASE_ASSERT(stat == cudaSuccess, "Couldn't clear the device buffer");
 }
 
+/*! \brief Check the validity of the device buffer.
+ *
+ * Checks if the buffer is not nullptr.
+ *
+ * \todo Add checks on the buffer size when it will be possible.
+ *
+ * \param[in] buffer        Device buffer to be checked.
+ * \param[in] requiredSize  Number of elements that the buffer will have to accommodate.
+ *
+ * \returns Whether the device buffer can be set.
+ */
+template<typename T>
+gmx_unused static bool checkDeviceBuffer(DeviceBuffer<T> buffer, gmx_unused int requiredSize)
+{
+    GMX_ASSERT(buffer != nullptr, "The device pointer is nullptr");
+    return buffer != nullptr;
+}
+
+//! Device texture wrapper.
+using DeviceTexture = cudaTextureObject_t;
+
+/*! \brief Create a texture object for an array of type ValueType.
+ *
+ * Creates the device buffer, copies data and binds texture object for an array of type ValueType.
+ *
+ * \todo Test if using textures is still relevant on modern hardware.
+ *
+ * \tparam      ValueType      Raw data type.
+ *
+ * \param[out]  deviceBuffer   Device buffer to store data in.
+ * \param[out]  deviceTexture  Device texture object to initialize.
+ * \param[in]   hostBuffer     Host buffer to get date from
+ * \param[in]   numValues      Number of elements in the buffer.
+ * \param[in]   deviceContext  GPU device context.
+ */
+template<typename ValueType>
+void initParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer,
+                          DeviceTexture*           deviceTexture,
+                          const ValueType*         hostBuffer,
+                          int                      numValues,
+                          const DeviceContext&     deviceContext)
+{
+    if (numValues == 0)
+    {
+        return;
+    }
+    GMX_ASSERT(hostBuffer, "Host buffer should be specified.");
+
+    allocateDeviceBuffer(deviceBuffer, numValues, deviceContext);
+
+    const size_t sizeInBytes = numValues * sizeof(ValueType);
+
+    cudaError_t stat =
+            cudaMemcpy(*((ValueType**)deviceBuffer), hostBuffer, sizeInBytes, cudaMemcpyHostToDevice);
+
+    GMX_RELEASE_ASSERT(
+            stat == cudaSuccess,
+            gmx::formatString("Synchronous H2D copy failed (CUDA error: %s).", cudaGetErrorName(stat))
+                    .c_str());
+
+    if (!c_disableCudaTextures)
+    {
+        cudaResourceDesc rd;
+        cudaTextureDesc  td;
+
+        memset(&rd, 0, sizeof(rd));
+        rd.resType                = cudaResourceTypeLinear;
+        rd.res.linear.devPtr      = *deviceBuffer;
+        rd.res.linear.desc        = cudaCreateChannelDesc<ValueType>();
+        rd.res.linear.sizeInBytes = sizeInBytes;
+
+        memset(&td, 0, sizeof(td));
+        td.readMode = cudaReadModeElementType;
+        stat        = cudaCreateTextureObject(deviceTexture, &rd, &td, nullptr);
+        GMX_RELEASE_ASSERT(stat == cudaSuccess,
+                           gmx::formatString("cudaCreateTextureObject failed (CUDA error: %s).",
+                                             cudaGetErrorName(stat))
+                                   .c_str());
+    }
+}
+
+/*! \brief Unbind the texture and release the CUDA texture object.
+ *
+ * \tparam         ValueType      Raw data type
+ *
+ * \param[in,out]  deviceBuffer   Device buffer to store data in.
+ * \param[in,out]  deviceTexture  Device texture object to unbind.
+ */
+template<typename ValueType>
+void destroyParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer, DeviceTexture& deviceTexture)
+{
+    if (!c_disableCudaTextures && deviceTexture && deviceBuffer)
+    {
+        cudaError_t stat = cudaDestroyTextureObject(deviceTexture);
+        GMX_RELEASE_ASSERT(
+                stat == cudaSuccess,
+                gmx::formatString(
+                        "cudaDestroyTextureObject on texture object failed (CUDA error: %s).",
+                        cudaGetErrorName(stat))
+                        .c_str());
+    }
+    freeDeviceBuffer(deviceBuffer);
+}
+
 #endif
index 4c28d49613b2456396c59cc5caa670fdb6fa7587..a13f0c62625315e7979c71e931e93b00924ea382 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/smalloc.h" // TODO: this is only for over_alloc_large
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 #    include "gromacs/gpu_utils/devicebuffer.cuh"
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
 #    include "gromacs/gpu_utils/devicebuffer_ocl.h"
+#elif GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/devicebuffer_sycl.h"
 #else
 #    error "devicebuffer.h included on non-GPU build!"
 #endif
@@ -80,7 +82,7 @@ void reallocateDeviceBuffer(DeviceBuffer<ValueType>* buffer,
                             size_t                   numValues,
                             int*                     currentNumValues,
                             int*                     currentMaxNumValues,
-                            DeviceContext            deviceContext)
+                            const DeviceContext&     deviceContext)
 {
     GMX_ASSERT(buffer, "needs a buffer pointer");
     GMX_ASSERT(currentNumValues, "needs a size pointer");
index 9ee6517318346a6766a67c7faf87a03012aa896e..e88782b1ac807e7169b9e1ae3507231204a5b8bd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "config.h"
 
-#if GMX_GPU == GMX_GPU_CUDA
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+
+#if GMX_GPU_CUDA
 
 //! \brief A device-side buffer of ValueTypes
 template<typename ValueType>
 using DeviceBuffer = ValueType*;
 
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
 
 #    include "gromacs/gpu_utils/gputraits_ocl.h"
 
@@ -85,6 +89,34 @@ public:
 template<typename ValueType>
 using DeviceBuffer = TypedClMemory<ValueType>;
 
+#elif GMX_GPU_SYCL
+
+/*! \libinternal \brief
+ * A minimal wrapper around \c cl::sycl::buffer to hide it away and simplify compilation.
+ */
+template<typename ValueType>
+struct DeviceBuffer
+{
+    class ClSyclBufferWrapper;
+    std::unique_ptr<ClSyclBufferWrapper> buffer_;
+
+    DeviceBuffer();
+    ~DeviceBuffer();
+    DeviceBuffer(DeviceBuffer<ValueType> const& src);
+    DeviceBuffer(DeviceBuffer<ValueType>&& src) noexcept;
+    DeviceBuffer& operator=(DeviceBuffer<ValueType> const& src);
+    DeviceBuffer& operator=(DeviceBuffer<ValueType>&& src) noexcept;
+
+    //! Helper function to get the size in bytes of a single element
+    static constexpr size_t elementSize() { return sizeof(ValueType); }
+
+    // static_case<void*> is used in MPI+CUDA code, this stub is necessary for compilation.
+    explicit operator void*() const { throw; }
+};
+
+// Must explicitly instantiate for some types.
+extern template struct DeviceBuffer<gmx::RVec>;
+
 #else
 
 //! \brief A device-side buffer of ValueTypes
@@ -93,5 +125,4 @@ using DeviceBuffer = void*;
 
 #endif
 
-
 #endif // GMX_GPU_UTILS_DEVICEBUFFER_DATATYPE_H
index 43669df1cdb655210acf1cd5b68cd12821fdf126..227370968f5dcf97fa98f4debe75efe5a70d4dd7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,6 +45,8 @@
  *  \inlibraryapi
  */
 
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/devicebuffer_datatype.h"
 #include "gromacs/gpu_utils/gpu_utils.h" //only for GpuApiCallBehavior
 #include "gromacs/gpu_utils/gputraits_ocl.h"
  * \param[in]     deviceContext        The buffer's device context-to-be.
  */
 template<typename ValueType>
-void allocateDeviceBuffer(DeviceBuffer<ValueType>* buffer, size_t numValues, DeviceContext deviceContext)
+void allocateDeviceBuffer(DeviceBuffer<ValueType>* buffer, size_t numValues, const DeviceContext& deviceContext)
 {
     GMX_ASSERT(buffer, "needs a buffer pointer");
     void*  hostPtr = nullptr;
     cl_int clError;
-    *buffer = clCreateBuffer(deviceContext, CL_MEM_READ_WRITE, numValues * sizeof(ValueType),
-                             hostPtr, &clError);
+    *buffer = clCreateBuffer(deviceContext.context(), CL_MEM_READ_WRITE,
+                             numValues * sizeof(ValueType), hostPtr, &clError);
     GMX_RELEASE_ASSERT(clError == CL_SUCCESS,
                        gmx::formatString("clCreateBuffer failure (OpenCL error %d: %s)", clError,
                                          ocl_get_error_string(clError).c_str())
@@ -100,14 +102,15 @@ void freeDeviceBuffer(DeviceBuffer* buffer)
 /*! \brief
  * Performs the host-to-device data copy, synchronous or asynchronously on request.
  *
- * TODO: This is meant to gradually replace cu/ocl_copy_h2d.
+ * Note that synchronous copy will not synchronize the stream in case of zero \p numValues
+ * because of the early return.
  *
  * \tparam        ValueType            Raw value type of the \p buffer.
  * \param[in,out] buffer               Pointer to the device-side buffer
  * \param[in]     hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
  * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy into.
  * \param[in]     numValues            Number of values to copy.
- * \param[in]     stream               GPU stream to perform asynchronous copy in.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
  * \param[in]     transferKind         Copy type: synchronous or asynchronous.
  * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
  *                                     If the pointer is not null, the event can further be used
@@ -118,7 +121,7 @@ void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
                         const ValueType*         hostBuffer,
                         size_t                   startingOffset,
                         size_t                   numValues,
-                        CommandStream            stream,
+                        const DeviceStream&      deviceStream,
                         GpuApiCallBehavior       transferKind,
                         CommandEvent*            timingEvent)
 {
@@ -134,8 +137,8 @@ void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
     switch (transferKind)
     {
         case GpuApiCallBehavior::Async:
-            clError = clEnqueueWriteBuffer(stream, *buffer, CL_FALSE, offset, bytes, hostBuffer, 0,
-                                           nullptr, timingEvent);
+            clError = clEnqueueWriteBuffer(deviceStream.stream(), *buffer, CL_FALSE, offset, bytes,
+                                           hostBuffer, 0, nullptr, timingEvent);
             GMX_RELEASE_ASSERT(
                     clError == CL_SUCCESS,
                     gmx::formatString("Asynchronous H2D copy failed (OpenCL error %d: %s)", clError,
@@ -144,8 +147,8 @@ void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
             break;
 
         case GpuApiCallBehavior::Sync:
-            clError = clEnqueueWriteBuffer(stream, *buffer, CL_TRUE, offset, bytes, hostBuffer, 0,
-                                           nullptr, timingEvent);
+            clError = clEnqueueWriteBuffer(deviceStream.stream(), *buffer, CL_TRUE, offset, bytes,
+                                           hostBuffer, 0, nullptr, timingEvent);
             GMX_RELEASE_ASSERT(
                     clError == CL_SUCCESS,
                     gmx::formatString("Synchronous H2D copy failed (OpenCL error %d: %s)", clError,
@@ -160,14 +163,15 @@ void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
 /*! \brief
  * Performs the device-to-host data copy, synchronous or asynchronously on request.
  *
- * TODO: This is meant to gradually replace cu/ocl_copy_d2h.
+ * Note that synchronous copy will not synchronize the stream in case of zero \p numValues
+ * because of the early return.
  *
  * \tparam        ValueType            Raw value type of the \p buffer.
  * \param[in,out] hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
  * \param[in]     buffer               Pointer to the device-side buffer
  * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy from.
  * \param[in]     numValues            Number of values to copy.
- * \param[in]     stream               GPU stream to perform asynchronous copy in.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
  * \param[in]     transferKind         Copy type: synchronous or asynchronous.
  * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
  *                                     If the pointer is not null, the event can further be used
@@ -178,10 +182,14 @@ void copyFromDeviceBuffer(ValueType*               hostBuffer,
                           DeviceBuffer<ValueType>* buffer,
                           size_t                   startingOffset,
                           size_t                   numValues,
-                          CommandStream            stream,
+                          const DeviceStream&      deviceStream,
                           GpuApiCallBehavior       transferKind,
                           CommandEvent*            timingEvent)
 {
+    if (numValues == 0)
+    {
+        return;
+    }
     GMX_ASSERT(buffer, "needs a buffer pointer");
     GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
     cl_int       clError;
@@ -190,8 +198,8 @@ void copyFromDeviceBuffer(ValueType*               hostBuffer,
     switch (transferKind)
     {
         case GpuApiCallBehavior::Async:
-            clError = clEnqueueReadBuffer(stream, *buffer, CL_FALSE, offset, bytes, hostBuffer, 0,
-                                          nullptr, timingEvent);
+            clError = clEnqueueReadBuffer(deviceStream.stream(), *buffer, CL_FALSE, offset, bytes,
+                                          hostBuffer, 0, nullptr, timingEvent);
             GMX_RELEASE_ASSERT(
                     clError == CL_SUCCESS,
                     gmx::formatString("Asynchronous D2H copy failed (OpenCL error %d: %s)", clError,
@@ -200,8 +208,8 @@ void copyFromDeviceBuffer(ValueType*               hostBuffer,
             break;
 
         case GpuApiCallBehavior::Sync:
-            clError = clEnqueueReadBuffer(stream, *buffer, CL_TRUE, offset, bytes, hostBuffer, 0,
-                                          nullptr, timingEvent);
+            clError = clEnqueueReadBuffer(deviceStream.stream(), *buffer, CL_TRUE, offset, bytes,
+                                          hostBuffer, 0, nullptr, timingEvent);
             GMX_RELEASE_ASSERT(
                     clError == CL_SUCCESS,
                     gmx::formatString("Synchronous D2H copy failed (OpenCL error %d: %s)", clError,
@@ -216,27 +224,109 @@ void copyFromDeviceBuffer(ValueType*               hostBuffer,
 /*! \brief
  * Clears the device buffer asynchronously.
  *
- * \tparam        ValueType        Raw value type of the \p buffer.
- * \param[in,out] buffer           Pointer to the device-side buffer
- * \param[in]     startingOffset   Offset (in values) at the device-side buffer to start clearing
- * at. \param[in]     numValues        Number of values to clear. \param[in]     stream GPU stream.
+ * \tparam        ValueType       Raw value type of the \p buffer.
+ * \param[in,out] buffer          Pointer to the device-side buffer
+ * \param[in]     startingOffset  Offset (in values) at the device-side buffer to start clearing at.
+ * \param[in]     numValues       Number of values to clear.
+ * \param[in]     deviceStream    GPU stream.
  */
 template<typename ValueType>
-void clearDeviceBufferAsync(DeviceBuffer<ValueType>* buffer, size_t startingOffset, size_t numValues, CommandStream stream)
+void clearDeviceBufferAsync(DeviceBuffer<ValueType>* buffer,
+                            size_t                   startingOffset,
+                            size_t                   numValues,
+                            const DeviceStream&      deviceStream)
 {
     GMX_ASSERT(buffer, "needs a buffer pointer");
     const size_t    offset        = startingOffset * sizeof(ValueType);
     const size_t    bytes         = numValues * sizeof(ValueType);
-    const ValueType pattern       = 0;
+    const int       pattern       = 0;
     const cl_uint   numWaitEvents = 0;
     const cl_event* waitEvents    = nullptr;
     cl_event        commandEvent;
-    cl_int clError = clEnqueueFillBuffer(stream, *buffer, &pattern, sizeof(pattern), offset, bytes,
-                                         numWaitEvents, waitEvents, &commandEvent);
+    cl_int clError = clEnqueueFillBuffer(deviceStream.stream(), *buffer, &pattern, sizeof(pattern),
+                                         offset, bytes, numWaitEvents, waitEvents, &commandEvent);
     GMX_RELEASE_ASSERT(clError == CL_SUCCESS,
                        gmx::formatString("Couldn't clear the device buffer (OpenCL error %d: %s)",
                                          clError, ocl_get_error_string(clError).c_str())
                                .c_str());
 }
 
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wunused-template"
+#endif
+
+/*! \brief Check the validity of the device buffer.
+ *
+ * Checks if the buffer is not nullptr and if its allocation is big enough.
+ *
+ * \param[in] buffer        Device buffer to be checked.
+ * \param[in] requiredSize  Number of elements that the buffer will have to accommodate.
+ *
+ * \returns Whether the device buffer can be set.
+ */
+template<typename T>
+static bool checkDeviceBuffer(DeviceBuffer<T> buffer, int requiredSize)
+{
+    const size_t requiredSizeBytes = requiredSize * sizeof(T);
+    size_t       sizeBytes;
+    cl_int retval = clGetMemObjectInfo(buffer, CL_MEM_SIZE, sizeof(sizeBytes), &sizeBytes, nullptr);
+    GMX_ASSERT(retval == CL_SUCCESS,
+               gmx::formatString("clGetMemObjectInfo failed with error code #%d", retval).c_str());
+    GMX_ASSERT(sizeBytes >= requiredSizeBytes,
+               "Number of atoms in device buffer is smaller then required size.");
+    return retval == CL_SUCCESS && sizeBytes >= requiredSizeBytes;
+}
+
+//! Device texture wrapper.
+using DeviceTexture = void*;
+
+/*! \brief Create a texture object for an array of type ValueType.
+ *
+ * Creates the device buffer and copies read-only data for an array of type ValueType.
+ *
+ * \todo Decide if using image2d is most efficient.
+ *
+ * \tparam      ValueType      Raw data type.
+ *
+ * \param[out]  deviceBuffer   Device buffer to store data in.
+ * \param[in]   hostBuffer     Host buffer to get date from.
+ * \param[in]   numValues      Number of elements in the buffer.
+ * \param[in]   deviceContext  GPU device context.
+ */
+template<typename ValueType>
+void initParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer,
+                          DeviceTexture* /* deviceTexture */,
+                          const ValueType*     hostBuffer,
+                          int                  numValues,
+                          const DeviceContext& deviceContext)
+{
+    GMX_ASSERT(hostBuffer, "Host buffer pointer can not be null");
+    const size_t bytes = numValues * sizeof(ValueType);
+    cl_int       clError;
+    *deviceBuffer = clCreateBuffer(deviceContext.context(),
+                                   CL_MEM_READ_ONLY | CL_MEM_HOST_WRITE_ONLY | CL_MEM_COPY_HOST_PTR,
+                                   bytes, const_cast<ValueType*>(hostBuffer), &clError);
+
+    GMX_RELEASE_ASSERT(clError == CL_SUCCESS,
+                       gmx::formatString("Constant memory allocation failed (OpenCL error %d: %s)",
+                                         clError, ocl_get_error_string(clError).c_str())
+                               .c_str());
+}
+
+/*! \brief Release the OpenCL device buffer.
+ *
+ * \tparam        ValueType     Raw data type.
+ *
+ * \param[in,out] deviceBuffer  Device buffer to store data in.
+ */
+template<typename ValueType>
+void destroyParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer, DeviceTexture& /* deviceTexture*/)
+{
+    freeDeviceBuffer(deviceBuffer);
+}
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
+
 #endif
similarity index 79%
rename from src/gromacs/mdlib/qm_orca.h
rename to src/gromacs/gpu_utils/devicebuffer_sycl.cpp
index cf89d941c8fc907e3d15d3331147fc9172253487..3e34066f195db304d9672184b8a7a87af1fa0df7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-#include "gromacs/mdlib/qmmm.h"
-
-#ifndef GMX_MDLIB_QM_ORCA_H
-#    define GMX_MDLIB_QM_ORCA_H
-
-void init_orca(t_QMrec* qm);
+/*! \internal \file
+ *  \brief Implements the DeviceBuffer type and routines for SYCL.
+ *
+ *  This CPP file is only used to explicitly instantiate some templates.
+ *
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ *
+ *  \ingroup module_gpu_utils
+ */
+#include "gmxpre.h"
 
-real call_orca(const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm, rvec f[], rvec fshift[]);
+#include "devicebuffer_sycl.h"
 
-#endif
+template struct DeviceBuffer<gmx::RVec>;
diff --git a/src/gromacs/gpu_utils/devicebuffer_sycl.h b/src/gromacs/gpu_utils/devicebuffer_sycl.h
new file mode 100644 (file)
index 0000000..63a3b16
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_DEVICEBUFFER_SYCL_H
+#define GMX_GPU_UTILS_DEVICEBUFFER_SYCL_H
+
+/*! \libinternal \file
+ *  \brief Implements the DeviceBuffer type and routines for SYCL.
+ *  Should only be included directly by the main DeviceBuffer file devicebuffer.h.
+ *  TODO: the intent is for DeviceBuffer to become a class.
+ *
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *  \author Erik Lindahl <erik.lindahl@gmail.com>
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/gpu_utils/gpu_utils.h" //only for GpuApiCallBehavior
+#include "gromacs/gpu_utils/gputraits_sycl.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+#ifndef DOXYGEN
+template<typename T>
+class DeviceBuffer<T>::ClSyclBufferWrapper : public cl::sycl::buffer<T, 1>
+{
+    using cl::sycl::buffer<T, 1>::buffer; // Get all the constructors
+};
+
+template<typename T>
+using ClSyclBufferWrapper = typename DeviceBuffer<T>::ClSyclBufferWrapper;
+
+//! Constructor.
+template<typename T>
+DeviceBuffer<T>::DeviceBuffer() : buffer_(nullptr)
+{
+}
+
+//! Destructor.
+template<typename T>
+DeviceBuffer<T>::~DeviceBuffer() = default;
+
+//! Copy constructor (references the same underlying SYCL buffer).
+template<typename T>
+DeviceBuffer<T>::DeviceBuffer(DeviceBuffer<T> const& src) :
+    buffer_(new ClSyclBufferWrapper(*src.buffer_))
+{
+}
+
+//! Move constructor.
+template<typename T>
+DeviceBuffer<T>::DeviceBuffer(DeviceBuffer<T>&& src) noexcept = default;
+
+//! Copy assignment (references the same underlying SYCL buffer).
+template<typename T>
+DeviceBuffer<T>& DeviceBuffer<T>::operator=(DeviceBuffer<T> const& src)
+{
+    buffer_.reset(new ClSyclBufferWrapper(*src.buffer_));
+    return *this;
+}
+
+//! Move assignment.
+template<typename T>
+DeviceBuffer<T>& DeviceBuffer<T>::operator=(DeviceBuffer<T>&& src) noexcept = default;
+
+#endif // #ifndef DOXYGEN
+
+/*! \libinternal \brief
+ * Allocates a device-side buffer.
+ * It is currently a caller's responsibility to call it only on not-yet allocated buffers.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] buffer               Pointer to the device-side buffer.
+ * \param[in]     numValues            Number of values to accommodate.
+ * \param[in]     deviceContext        The buffer's device context-to-be.
+ */
+template<typename ValueType>
+void allocateDeviceBuffer(DeviceBuffer<ValueType>* buffer, size_t numValues, const DeviceContext& deviceContext)
+{
+    /* SYCL does not require binding buffer to a specific context or device. The ::context_bound
+     * property only enforces the use of only given context, and possibly offers some optimizations */
+    const cl::sycl::property_list bufferProperties{ cl::sycl::property::buffer::context_bound(
+            deviceContext.context()) };
+    buffer->buffer_.reset(
+            new ClSyclBufferWrapper<ValueType>(cl::sycl::range<1>(numValues), bufferProperties));
+}
+
+/*! \brief
+ * Frees a device-side buffer.
+ * This does not reset separately stored size/capacity integers,
+ * as this is planned to be a destructor of DeviceBuffer as a proper class,
+ * and no calls on \p buffer should be made afterwards.
+ *
+ * \param[in] buffer  Pointer to the buffer to free.
+ */
+template<typename ValueType>
+void freeDeviceBuffer(DeviceBuffer<ValueType>* buffer)
+{
+    buffer->buffer_.reset(nullptr);
+}
+
+/*! \brief
+ * Performs the host-to-device data copy, synchronous or asynchronously on request.
+ *
+ * Unlike in CUDA and OpenCL, synchronous call does not guarantee that all previously
+ * submitted operations are complete, only the ones that are required for \p buffer consistency.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] buffer               Pointer to the device-side buffer.
+ * \param[in]     hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType.
+ * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy into.
+ * \param[in]     numValues            Number of values to copy.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
+ * \param[in]     transferKind         Copy type: synchronous or asynchronous.
+ * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
+ *                                     Ignored in SYCL.
+ */
+template<typename ValueType>
+void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
+                        const ValueType*         hostBuffer,
+                        size_t                   startingOffset,
+                        size_t                   numValues,
+                        const DeviceStream&      deviceStream,
+                        GpuApiCallBehavior       transferKind,
+                        CommandEvent* gmx_unused timingEvent)
+{
+    if (numValues == 0)
+    {
+        return; // such calls are actually made with empty domains
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    GMX_ASSERT(buffer->buffer_, "needs an initialized buffer pointer");
+    GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
+
+    cl::sycl::buffer<ValueType>& syclBuffer = *buffer->buffer_;
+
+    cl::sycl::event ev = deviceStream.stream().submit([&](cl::sycl::handler& cgh) {
+        /* Here and elsewhere in this file, accessor constructor is user instead of a more common
+         * buffer::get_access, since the compiler (icpx 2021.1-beta09) occasionally gets confused
+         * by all the overloads */
+        auto d_bufferAccessor = cl::sycl::accessor<ValueType, 1, cl::sycl::access::mode::discard_write>{
+            syclBuffer, cgh, cl::sycl::range(numValues), cl::sycl::id(startingOffset)
+        };
+        cgh.copy(hostBuffer, d_bufferAccessor);
+    });
+    if (transferKind == GpuApiCallBehavior::Sync)
+    {
+        ev.wait_and_throw();
+    }
+}
+
+/*! \brief
+ * Performs the device-to-host data copy, synchronous or asynchronously on request.
+ *
+ * Unlike in CUDA and OpenCL, synchronous call does not guarantee that all previously
+ * submitted operations are complete, only the ones that are required for \p buffer consistency.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
+ * \param[in]     buffer               Pointer to the device-side buffer.
+ * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy from.
+ * \param[in]     numValues            Number of values to copy.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
+ * \param[in]     transferKind         Copy type: synchronous or asynchronous.
+ * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
+ *                                     Ignored in SYCL.
+ */
+template<typename ValueType>
+void copyFromDeviceBuffer(ValueType*               hostBuffer,
+                          DeviceBuffer<ValueType>* buffer,
+                          size_t                   startingOffset,
+                          size_t                   numValues,
+                          const DeviceStream&      deviceStream,
+                          GpuApiCallBehavior       transferKind,
+                          CommandEvent* gmx_unused timingEvent)
+{
+    if (numValues == 0)
+    {
+        return; // such calls are actually made with empty domains
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
+
+    cl::sycl::buffer<ValueType>& syclBuffer = *buffer->buffer_;
+
+    cl::sycl::event ev = deviceStream.stream().submit([&](cl::sycl::handler& cgh) {
+        const auto d_bufferAccessor = cl::sycl::accessor<ValueType, 1, cl::sycl::access::mode::read>{
+            syclBuffer, cgh, cl::sycl::range(numValues), cl::sycl::id(startingOffset)
+        };
+        cgh.copy(d_bufferAccessor, hostBuffer);
+    });
+    if (transferKind == GpuApiCallBehavior::Sync)
+    {
+        ev.wait_and_throw();
+    }
+}
+
+/*! \brief
+ * Clears the device buffer asynchronously.
+ *
+ * \tparam        ValueType       Raw value type of the \p buffer.
+ * \param[in,out] buffer          Pointer to the device-side buffer.
+ * \param[in]     startingOffset  Offset (in values) at the device-side buffer to start clearing at.
+ * \param[in]     numValues       Number of values to clear.
+ * \param[in]     deviceStream    GPU stream.
+ */
+template<typename ValueType>
+void clearDeviceBufferAsync(DeviceBuffer<ValueType>* buffer,
+                            size_t                   startingOffset,
+                            size_t                   numValues,
+                            const DeviceStream&      deviceStream)
+{
+    if (numValues == 0)
+    {
+        return;
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+
+    const ValueType              pattern{};
+    cl::sycl::buffer<ValueType>& syclBuffer = *(buffer->buffer_);
+
+    cl::sycl::event ev = deviceStream.stream().submit([&](cl::sycl::handler& cgh) {
+        auto d_bufferAccessor = cl::sycl::accessor<ValueType, 1, cl::sycl::access::mode::discard_write>{
+            syclBuffer, cgh, cl::sycl::range(numValues), cl::sycl::id(startingOffset)
+        };
+        cgh.fill(d_bufferAccessor, pattern);
+    });
+}
+
+/*! \brief Check the validity of the device buffer.
+ *
+ * Checks if the buffer is valid and if its allocation is big enough.
+ *
+ * \param[in] buffer        Device buffer to be checked.
+ * \param[in] requiredSize  Number of elements that the buffer will have to accommodate.
+ *
+ * \returns Whether the device buffer exists and has enough capacity.
+ */
+template<typename T>
+static gmx_unused bool checkDeviceBuffer(DeviceBuffer<T> buffer, int requiredSize)
+{
+    return buffer.buffer_ && (static_cast<int>(buffer.buffer_->get_count()) >= requiredSize);
+}
+
+/*! \brief Create a texture object for an array of type ValueType.
+ *
+ * Creates the device buffer and copies read-only data for an array of type ValueType.
+ * Like OpenCL, does not really do anything with textures, simply creates a buffer
+ * and initializes it.
+ *
+ * \tparam      ValueType      Raw data type.
+ *
+ * \param[out]  deviceBuffer   Device buffer to store data in.
+ * \param[in]   hostBuffer     Host buffer to get date from.
+ * \param[in]   numValues      Number of elements in the buffer.
+ * \param[in]   deviceContext  GPU device context.
+ */
+template<typename ValueType>
+void initParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer,
+                          DeviceTexture* /* deviceTexture */,
+                          const ValueType*     hostBuffer,
+                          int                  numValues,
+                          const DeviceContext& deviceContext)
+{
+    GMX_ASSERT(hostBuffer, "Host buffer should be specified.");
+    GMX_ASSERT(deviceBuffer, "Device buffer should be specified.");
+
+    /* Constructing buffer with cl::sycl::buffer(T* data, size_t size) will take ownership
+     * of this memory region making it unusable, which might lead to side-effects.
+     * On the other hand, cl::sycl::buffer(InputIterator<T> begin, InputIterator<T> end) will
+     * initialize the buffer without affecting ownership of the memory, although
+     * it will consume extra memory on host. */
+    const cl::sycl::property_list bufferProperties{ cl::sycl::property::buffer::context_bound(
+            deviceContext.context()) };
+    deviceBuffer->buffer_.reset(new ClSyclBufferWrapper<ValueType>(
+            hostBuffer, hostBuffer + numValues, bufferProperties));
+}
+
+/*! \brief Release the OpenCL device buffer.
+ *
+ * \tparam        ValueType     Raw data type.
+ *
+ * \param[in,out] deviceBuffer  Device buffer to store data in.
+ */
+template<typename ValueType>
+void destroyParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer, DeviceTexture& /* deviceTexture */)
+{
+    deviceBuffer->buffer_.reset(nullptr);
+}
+
+#endif // GMX_GPU_UTILS_DEVICEBUFFER_SYCL_H
similarity index 73%
rename from src/gromacs/nbnxm/gpu_types.h
rename to src/gromacs/gpu_utils/gmxsycl.h
index daded17a0be1b6cbaccee1c1029ba7b1621994d1..6c1b63573292f37f5b94d0990cf464f4ec855950 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \libinternal \file
- * \brief Sets gmx_nbnxn_gpu_t to the correct type depending on the build
+ * \brief
+ * Wraps the complexity of including SYCL in GROMACS.
  *
- * \ingroup module_nbnxm
+ * SYCL headers use symbol DIM as a template parameter, which gets broken by macro DIM defined
+ * in gromacs/math/vectypes.h. Here, we include the SYCL header while temporary undefining this macro.
+ *
+ * \inlibraryapi
  */
 
-#ifndef GMX_NBNXN_GPU_TYPES_H
-#define GMX_NBNXN_GPU_TYPES_H
-
-#include "config.h"
-
-#ifndef DOXYGEN
-
-#    if GMX_GPU == GMX_GPU_OPENCL
-struct gmx_nbnxn_ocl_t;
-using gmx_nbnxn_gpu_t = gmx_nbnxn_ocl_t;
-#    endif
+#ifndef GMX_GPU_UTILS_GMXSYCL_H
+#define GMX_GPU_UTILS_GMXSYCL_H
 
-#    if GMX_GPU == GMX_GPU_CUDA
-struct gmx_nbnxn_cuda_t;
-using gmx_nbnxn_gpu_t = gmx_nbnxn_cuda_t;
-#    endif
 
-#    if GMX_GPU == GMX_GPU_NONE
-using gmx_nbnxn_gpu_t = int;
+#ifdef DIM
+#    if DIM != 3
+#        error "The workaround here assumes we use DIM=3."
+#    else
+#        undef DIM
+#        include <CL/sycl.hpp>
+#        define DIM 3
 #    endif
-
-#endif // !DOXYGEN
+#else
+#    include <CL/sycl.hpp>
+#endif
 
 #endif
index 4f6d557537bacfbb126066b5fd81e98f45f99fd1..e7ecf83f57683d28cb95fe93eb717ee48eb20837 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -74,7 +74,7 @@
 #    define OPENCL_FUNC_TERM REAL_FUNC_TERM
 #    define OPENCL_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
 
-#elif GMX_GPU != GMX_GPU_NONE
+#elif GMX_GPU_OPENCL || GMX_GPU_CUDA
 
 /* GPU support is enabled, so these functions will have real code
  * defined somewhere */
@@ -83,7 +83,7 @@
 #    define GPU_FUNC_TERM REAL_FUNC_TERM
 #    define GPU_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
 
-#    if GMX_GPU == GMX_GPU_OPENCL
+#    if GMX_GPU_OPENCL
 
 /* OpenCL support is enabled, so CUDA-specific functions need empty
  * implementations, while OpenCL-specific functions will have real
@@ -98,7 +98,7 @@
 #        define OPENCL_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
 
 #    endif
-#    if GMX_GPU == GMX_GPU_CUDA
+#    if GMX_GPU_CUDA
 
 /* CUDA support is enabled, so OpenCL-specific functions need empty
  * implementations, while CUDA-specific functions will have real
 
 #    endif
 
-#elif GMX_GPU == GMX_GPU_NONE
+#elif !GMX_GPU || GMX_GPU_SYCL
 
 /* No GPU support is configured, so none of these functions will have
  * real definitions. */
index 51622063a7a967689ce8f4bf2d0e12d577fa45e1..1379ba83ce97f0947b4e3caeaded6ac8df35c3d6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,7 +45,6 @@
 
 #include <cassert>
 
-#include "gromacs/hardware/gpu_hw_info.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/stringutil.h"
 #    pragma warning(disable : 6237)
 #endif
 
-//! Constant used to help minimize preprocessed code
-static constexpr bool c_binarySupportsGpus = (GMX_GPU != GMX_GPU_NONE);
-
-bool canPerformGpuDetection()
-{
-    if (c_binarySupportsGpus && getenv("GMX_DISABLE_GPU_DETECTION") == nullptr)
-    {
-        return isGpuDetectionFunctional(nullptr);
-    }
-    else
-    {
-        return false;
-    }
-}
-
-#if GMX_GPU == GMX_GPU_NONE
-int gpu_info_get_stat(const gmx_gpu_info_t& /*unused*/, int /*unused*/)
-{
-    return egpuNonexistent;
-}
-#endif
-
-void free_gpu_info(const gmx_gpu_info_t* gpu_info)
-{
-    sfree(static_cast<void*>(gpu_info->gpu_dev)); // circumvent is_pod check in sfree
-}
-
-std::vector<int> getCompatibleGpus(const gmx_gpu_info_t& gpu_info)
-{
-    // Possible minor over-allocation here, but not important for anything
-    std::vector<int> compatibleGpus;
-    compatibleGpus.reserve(gpu_info.n_dev);
-    for (int i = 0; i < gpu_info.n_dev; i++)
-    {
-        assert(gpu_info.gpu_dev);
-        if (gpu_info_get_stat(gpu_info, i) == egpuCompatible)
-        {
-            compatibleGpus.push_back(i);
-        }
-    }
-    return compatibleGpus;
-}
-
-const char* getGpuCompatibilityDescription(const gmx_gpu_info_t& gpu_info, int index)
-{
-    return (index >= gpu_info.n_dev ? gpu_detect_res_str[egpuNonexistent]
-                                    : gpu_detect_res_str[gpu_info_get_stat(gpu_info, index)]);
-}
 /*! \brief Help build a descriptive message in \c error if there are
  * \c errorReasons why nonbondeds on a GPU are not supported.
  *
@@ -124,7 +75,7 @@ bool buildSupportsNonbondedOnGpu(std::string* error)
     {
         errorReasons.emplace_back("double precision");
     }
-    if (!c_binarySupportsGpus)
+    if (!GMX_GPU)
     {
         errorReasons.emplace_back("non-GPU build of GROMACS");
     }
index 6409e8b5e123da638c03df0a972a491d70448800..c68a8cda63b216212c4c33ec854566536967c688 100644 (file)
 #include <cuda_profiler_api.h>
 
 #include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/pmalloc_cuda.h"
-#include "gromacs/hardware/gpu_hw_info.h"
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/snprintf.h"
 #include "gromacs/utility/stringutil.h"
 
-/*! \internal \brief
- * Max number of devices supported by CUDA (for consistency checking).
- *
- * In reality it is 16 with CUDA <=v5.0, but let's stay on the safe side.
- */
-static int cuda_max_device_count = 32;
-
 static bool cudaProfilerRun = ((getenv("NVPROF_ID") != nullptr));
 
-/** Dummy kernel used for sanity checking. */
-static __global__ void k_dummy_test(void) {}
-
-static cudaError_t checkCompiledTargetCompatibility(int deviceId, const cudaDeviceProp& deviceProp)
-{
-    cudaFuncAttributes attributes;
-    cudaError_t        stat = cudaFuncGetAttributes(&attributes, k_dummy_test);
-
-    if (cudaErrorInvalidDeviceFunction == stat)
-    {
-        fprintf(stderr,
-                "\nWARNING: The %s binary does not include support for the CUDA architecture of "
-                "the GPU ID #%d (compute capability %d.%d) detected during detection. "
-                "By default, GROMACS supports all architectures of compute "
-                "capability >= 3.0, so your GPU "
-                "might be rare, or some architectures were disabled in the build. \n"
-                "Consult the install guide for how to use the GMX_CUDA_TARGET_SM and "
-                "GMX_CUDA_TARGET_COMPUTE CMake variables to add this architecture. \n",
-                gmx::getProgramContext().displayName(), deviceId, deviceProp.major, deviceProp.minor);
-    }
-
-    return stat;
-}
-
 bool isHostMemoryPinned(const void* h_ptr)
 {
     cudaPointerAttributes memoryAttributes;
@@ -131,404 +103,6 @@ bool isHostMemoryPinned(const void* h_ptr)
     return isPinned;
 }
 
-/*!
- * \brief Runs GPU sanity checks.
- *
- * Runs a series of checks to determine that the given GPU and underlying CUDA
- * driver/runtime functions properly.
- *
- * \param[in]  dev_id      the device ID of the GPU or -1 if the device has already been initialized
- * \param[in]  dev_prop    The device properties structure
- * \returns                0 if the device looks OK, -1 if it sanity checks failed, and -2 if the device is busy
- *
- * TODO: introduce errors codes and handle errors more smoothly.
- */
-static int do_sanity_checks(int dev_id, const cudaDeviceProp& dev_prop)
-{
-    cudaError_t cu_err;
-    int         dev_count, id;
-
-    cu_err = cudaGetDeviceCount(&dev_count);
-    if (cu_err != cudaSuccess)
-    {
-        fprintf(stderr, "Error %d while querying device count: %s\n", cu_err, cudaGetErrorString(cu_err));
-        return -1;
-    }
-
-    /* no CUDA compatible device at all */
-    if (dev_count == 0)
-    {
-        return -1;
-    }
-
-    /* things might go horribly wrong if cudart is not compatible with the driver */
-    if (dev_count < 0 || dev_count > cuda_max_device_count)
-    {
-        return -1;
-    }
-
-    if (dev_id == -1) /* device already selected let's not destroy the context */
-    {
-        cu_err = cudaGetDevice(&id);
-        if (cu_err != cudaSuccess)
-        {
-            fprintf(stderr, "Error %d while querying device id: %s\n", cu_err, cudaGetErrorString(cu_err));
-            return -1;
-        }
-    }
-    else
-    {
-        id = dev_id;
-        if (id > dev_count - 1) /* pfff there's no such device */
-        {
-            fprintf(stderr,
-                    "The requested device with id %d does not seem to exist (device count=%d)\n",
-                    dev_id, dev_count);
-            return -1;
-        }
-    }
-
-    /* both major & minor is 9999 if no CUDA capable devices are present */
-    if (dev_prop.major == 9999 && dev_prop.minor == 9999)
-    {
-        return -1;
-    }
-    /* we don't care about emulation mode */
-    if (dev_prop.major == 0)
-    {
-        return -1;
-    }
-
-    if (id != -1)
-    {
-        cu_err = cudaSetDevice(id);
-        if (cu_err != cudaSuccess)
-        {
-            fprintf(stderr, "Error %d while switching to device #%d: %s\n", cu_err, id,
-                    cudaGetErrorString(cu_err));
-            return -1;
-        }
-    }
-
-    cu_err = checkCompiledTargetCompatibility(dev_id, dev_prop);
-    // Avoid triggering an error if GPU devices are in exclusive or prohibited mode;
-    // it is enough to check for cudaErrorDevicesUnavailable only here because
-    // if we encounter it that will happen in cudaFuncGetAttributes in the above function.
-    if (cu_err == cudaErrorDevicesUnavailable)
-    {
-        return -2;
-    }
-    else if (cu_err != cudaSuccess)
-    {
-        return -1;
-    }
-
-    /* try to execute a dummy kernel */
-    try
-    {
-        KernelLaunchConfig config;
-        config.blockSize[0]       = 512;
-        const auto dummyArguments = prepareGpuKernelArguments(k_dummy_test, config);
-        launchGpuKernel(k_dummy_test, config, nullptr, "Dummy kernel", dummyArguments);
-    }
-    catch (gmx::GromacsException& ex)
-    {
-        // launchGpuKernel error is not fatal and should continue with marking the device bad
-        fprintf(stderr,
-                "Error occurred while running dummy kernel sanity check on device #%d:\n %s\n", id,
-                formatExceptionMessageToString(ex).c_str());
-        return -1;
-    }
-
-    if (cudaDeviceSynchronize() != cudaSuccess)
-    {
-        return -1;
-    }
-
-    /* destroy context if we created one */
-    if (id != -1)
-    {
-        cu_err = cudaDeviceReset();
-        CU_RET_ERR(cu_err, "cudaDeviceReset failed");
-    }
-
-    return 0;
-}
-
-void init_gpu(const gmx_device_info_t* deviceInfo)
-{
-    cudaError_t stat;
-
-    assert(deviceInfo);
-
-    stat = cudaSetDevice(deviceInfo->id);
-    if (stat != cudaSuccess)
-    {
-        auto message = gmx::formatString("Failed to initialize GPU #%d", deviceInfo->id);
-        CU_RET_ERR(stat, message.c_str());
-    }
-
-    if (debug)
-    {
-        fprintf(stderr, "Initialized GPU ID #%d: %s\n", deviceInfo->id, deviceInfo->prop.name);
-    }
-}
-
-void free_gpu(const gmx_device_info_t* deviceInfo)
-{
-    // One should only attempt to clear the device context when
-    // it has been used, but currently the only way to know that a GPU
-    // device was used is that deviceInfo will be non-null.
-    if (deviceInfo == nullptr)
-    {
-        return;
-    }
-
-    cudaError_t stat;
-
-    if (debug)
-    {
-        int gpuid;
-        stat = cudaGetDevice(&gpuid);
-        CU_RET_ERR(stat, "cudaGetDevice failed");
-        fprintf(stderr, "Cleaning up context on GPU ID #%d\n", gpuid);
-    }
-
-    stat = cudaDeviceReset();
-    if (stat != cudaSuccess)
-    {
-        gmx_warning("Failed to free GPU #%d: %s", deviceInfo->id, cudaGetErrorString(stat));
-    }
-}
-
-gmx_device_info_t* getDeviceInfo(const gmx_gpu_info_t& gpu_info, int deviceId)
-{
-    if (deviceId < 0 || deviceId >= gpu_info.n_dev)
-    {
-        gmx_incons("Invalid GPU deviceId requested");
-    }
-    return &gpu_info.gpu_dev[deviceId];
-}
-
-/*! \brief Returns true if the gpu characterized by the device properties is
- *  supported by the native gpu acceleration.
- *
- * \param[in] dev_prop  the CUDA device properties of the gpus to test.
- * \returns             true if the GPU properties passed indicate a compatible
- *                      GPU, otherwise false.
- */
-static bool is_gmx_supported_gpu(const cudaDeviceProp& dev_prop)
-{
-    return (dev_prop.major >= 3);
-}
-
-/*! \brief Checks if a GPU with a given ID is supported by the native GROMACS acceleration.
- *
- *  Returns a status value which indicates compatibility or one of the following
- *  errors: incompatibility or insanity (=unexpected behavior).
- *
- *  As the error handling only permits returning the state of the GPU, this function
- *  does not clear the CUDA runtime API status allowing the caller to inspect the error
- *  upon return. Note that this also means it is the caller's responsibility to
- *  reset the CUDA runtime state.
- *
- *  \param[in]  deviceId   the ID of the GPU to check.
- *  \param[in]  deviceProp the CUDA device properties of the device checked.
- *  \returns               the status of the requested device
- */
-static int is_gmx_supported_gpu_id(int deviceId, const cudaDeviceProp& deviceProp)
-{
-    if (!is_gmx_supported_gpu(deviceProp))
-    {
-        return egpuIncompatible;
-    }
-
-    /* TODO: currently we do not make a distinction between the type of errors
-     * that can appear during sanity checks. This needs to be improved, e.g if
-     * the dummy test kernel fails to execute with a "device busy message" we
-     * should appropriately report that the device is busy instead of insane.
-     */
-    const int checkResult = do_sanity_checks(deviceId, deviceProp);
-    switch (checkResult)
-    {
-        case 0: return egpuCompatible;
-        case -1: return egpuInsane;
-        case -2: return egpuUnavailable;
-        default:
-            GMX_RELEASE_ASSERT(false, "Invalid do_sanity_checks() return value");
-            return egpuCompatible;
-    }
-}
-
-bool isGpuDetectionFunctional(std::string* errorMessage)
-{
-    cudaError_t stat;
-    int         driverVersion = -1;
-    stat                      = cudaDriverGetVersion(&driverVersion);
-    GMX_ASSERT(stat != cudaErrorInvalidValue,
-               "An impossible null pointer was passed to cudaDriverGetVersion");
-    GMX_RELEASE_ASSERT(
-            stat == cudaSuccess,
-            gmx::formatString("An unexpected value was returned from cudaDriverGetVersion %s: %s",
-                              cudaGetErrorName(stat), cudaGetErrorString(stat))
-                    .c_str());
-    bool foundDriver = (driverVersion > 0);
-    if (!foundDriver)
-    {
-        // Can't detect GPUs if there is no driver
-        if (errorMessage != nullptr)
-        {
-            errorMessage->assign("No valid CUDA driver found");
-        }
-        return false;
-    }
-
-    int numDevices;
-    stat = cudaGetDeviceCount(&numDevices);
-    if (stat != cudaSuccess)
-    {
-        if (errorMessage != nullptr)
-        {
-            /* cudaGetDeviceCount failed which means that there is
-             * something wrong with the machine: driver-runtime
-             * mismatch, all GPUs being busy in exclusive mode,
-             * invalid CUDA_VISIBLE_DEVICES, or some other condition
-             * which should result in GROMACS issuing at least a
-             * warning. */
-            errorMessage->assign(cudaGetErrorString(stat));
-        }
-
-        // Consume the error now that we have prepared to handle
-        // it. This stops it reappearing next time we check for
-        // errors. Note that if CUDA_VISIBLE_DEVICES does not contain
-        // valid devices, then cudaGetLastError returns the
-        // (undocumented) cudaErrorNoDevice, but this should not be a
-        // problem as there should be no future CUDA API calls.
-        // NVIDIA bug report #2038718 has been filed.
-        cudaGetLastError();
-        // Can't detect GPUs
-        return false;
-    }
-
-    // We don't actually use numDevices here, that's not the job of
-    // this function.
-    return true;
-}
-
-void findGpus(gmx_gpu_info_t* gpu_info)
-{
-    assert(gpu_info);
-
-    gpu_info->n_dev_compatible = 0;
-
-    int         ndev;
-    cudaError_t stat = cudaGetDeviceCount(&ndev);
-    if (stat != cudaSuccess)
-    {
-        GMX_THROW(gmx::InternalError(
-                "Invalid call of findGpus() when CUDA API returned an error, perhaps "
-                "canDetectGpus() was not called appropriately beforehand."));
-    }
-
-    // We expect to start device support/sanity checks with a clean runtime error state
-    gmx::ensureNoPendingCudaError("");
-
-    gmx_device_info_t* devs;
-    snew(devs, ndev);
-    for (int i = 0; i < ndev; i++)
-    {
-        cudaDeviceProp prop;
-        memset(&prop, 0, sizeof(cudaDeviceProp));
-        stat = cudaGetDeviceProperties(&prop, i);
-        int checkResult;
-        if (stat != cudaSuccess)
-        {
-            // Will handle the error reporting below
-            checkResult = egpuInsane;
-        }
-        else
-        {
-            checkResult = is_gmx_supported_gpu_id(i, prop);
-        }
-
-        devs[i].id   = i;
-        devs[i].prop = prop;
-        devs[i].stat = checkResult;
-
-        if (checkResult == egpuCompatible)
-        {
-            gpu_info->n_dev_compatible++;
-        }
-        else
-        {
-            // TODO:
-            //  - we inspect the CUDA API state to retrieve and record any
-            //    errors that occurred during is_gmx_supported_gpu_id() here,
-            //    but this would be more elegant done within is_gmx_supported_gpu_id()
-            //    and only return a string with the error if one was encountered.
-            //  - we'll be reporting without rank information which is not ideal.
-            //  - we'll end up warning also in cases where users would already
-            //    get an error before mdrun aborts.
-            //
-            // Here we also clear the CUDA API error state so potential
-            // errors during sanity checks don't propagate.
-            if ((stat = cudaGetLastError()) != cudaSuccess)
-            {
-                gmx_warning("An error occurred while sanity checking device #%d; %s: %s",
-                            devs[i].id, cudaGetErrorName(stat), cudaGetErrorString(stat));
-            }
-        }
-    }
-
-    stat = cudaPeekAtLastError();
-    GMX_RELEASE_ASSERT(stat == cudaSuccess,
-                       gmx::formatString("We promise to return with clean CUDA state, but "
-                                         "non-success state encountered: %s: %s",
-                                         cudaGetErrorName(stat), cudaGetErrorString(stat))
-                               .c_str());
-
-    gpu_info->n_dev   = ndev;
-    gpu_info->gpu_dev = devs;
-}
-
-void get_gpu_device_info_string(char* s, const gmx_gpu_info_t& gpu_info, int index)
-{
-    assert(s);
-
-    if (index < 0 && index >= gpu_info.n_dev)
-    {
-        return;
-    }
-
-    gmx_device_info_t* dinfo = &gpu_info.gpu_dev[index];
-
-    bool bGpuExists = (dinfo->stat != egpuNonexistent && dinfo->stat != egpuInsane);
-
-    if (!bGpuExists)
-    {
-        sprintf(s, "#%d: %s, stat: %s", dinfo->id, "N/A", gpu_detect_res_str[dinfo->stat]);
-    }
-    else
-    {
-        sprintf(s, "#%d: NVIDIA %s, compute cap.: %d.%d, ECC: %3s, stat: %s", dinfo->id,
-                dinfo->prop.name, dinfo->prop.major, dinfo->prop.minor,
-                dinfo->prop.ECCEnabled ? "yes" : " no", gpu_detect_res_str[dinfo->stat]);
-    }
-}
-
-int get_current_cuda_gpu_device_id(void)
-{
-    int gpuid;
-    CU_RET_ERR(cudaGetDevice(&gpuid), "cudaGetDevice failed");
-
-    return gpuid;
-}
-
-size_t sizeof_gpu_dev_info(void)
-{
-    return sizeof(gmx_device_info_t);
-}
-
 void startGpuProfiler(void)
 {
     /* The NVPROF_ID environment variable is set by nvprof and indicates that
@@ -572,11 +146,6 @@ void resetGpuProfiler(void)
     }
 }
 
-int gpu_info_get_stat(const gmx_gpu_info_t& info, int index)
-{
-    return info.gpu_dev[index].stat;
-}
-
 /*! \brief Check status returned from peer access CUDA call, and error out or warn appropriately
  * \param[in] stat           CUDA call return status
  * \param[in] gpuA           ID for GPU initiating peer access call
index 5ed09f4f9bdcf72dd841e9dea15dc80f661864d0..fce1e995802450f160f91f91f9fdf38cbb9fdd6d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2010, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,9 +54,6 @@
 #include "gromacs/gpu_utils/gpu_macros.h"
 #include "gromacs/utility/basedefinitions.h"
 
-struct gmx_device_info_t;
-struct gmx_gpu_info_t;
-
 namespace gmx
 {
 class MDLogger;
@@ -75,156 +73,6 @@ enum class GpuTaskCompletion
     Check /*<< Only check whether the task has completed */
 };
 
-/*! \brief Return whether GPUs can be detected
- *
- * Returns true when this is a build of \Gromacs configured to support
- * GPU usage, GPU detection is not disabled by an environment variable
- * and a valid device driver, ICD, and/or runtime was detected.
- * Does not throw. */
-bool canPerformGpuDetection();
-
-/*! \brief Return whether GPU detection is functioning correctly
- *
- * Returns true when this is a build of \Gromacs configured to support
- * GPU usage, and a valid device driver, ICD, and/or runtime was detected.
- *
- * This function is not intended to be called from build
- * configurations that do not support GPUs, and there will be no
- * descriptive message in that case.
- *
- * \param[out] errorMessage  When returning false on a build configured with
- *                           GPU support and non-nullptr was passed,
- *                           the string contains a descriptive message about
- *                           why GPUs cannot be detected.
- *
- * Does not throw. */
-GPU_FUNC_QUALIFIER
-bool isGpuDetectionFunctional(std::string* GPU_FUNC_ARGUMENT(errorMessage))
-        GPU_FUNC_TERM_WITH_RETURN(false);
-
-/*! \brief Find all GPUs in the system.
- *
- *  Will detect every GPU supported by the device driver in use.
- *  Must only be called if canPerformGpuDetection() has returned true.
- *  This routine also checks for the compatibility of each and fill the
- *  gpu_info->gpu_dev array with the required information on each the
- *  device: ID, device properties, status.
- *
- *  Note that this function leaves the GPU runtime API error state clean;
- *  this is implemented ATM in the CUDA flavor.
- *  TODO: check if errors do propagate in OpenCL as they do in CUDA and
- *  whether there is a mechanism to "clear" them.
- *
- *  \param[in] gpu_info    pointer to structure holding GPU information.
- *
- *  \throws                InternalError if a GPU API returns an unexpected failure (because
- *                         the call to canDetectGpus() should always prevent this occuring)
- */
-GPU_FUNC_QUALIFIER
-void findGpus(gmx_gpu_info_t* GPU_FUNC_ARGUMENT(gpu_info)) GPU_FUNC_TERM;
-
-/*! \brief Return a container of the detected GPUs that are compatible.
- *
- * This function filters the result of the detection for compatible
- * GPUs, based on the previously run compatibility tests.
- *
- * \param[in]     gpu_info    Information detected about GPUs, including compatibility.
- * \return                    vector of IDs of GPUs already recorded as compatible */
-std::vector<int> getCompatibleGpus(const gmx_gpu_info_t& gpu_info);
-
-/*! \brief Return a string describing how compatible the GPU with given \c index is.
- *
- * \param[in]   gpu_info    Information about detected GPUs
- * \param[in]   index       index of GPU to ask about
- * \returns                 A null-terminated C string describing the compatibility status, useful for error messages.
- */
-const char* getGpuCompatibilityDescription(const gmx_gpu_info_t& gpu_info, int index);
-
-/*! \brief Frees the gpu_dev and dev_use array fields of \p gpu_info.
- *
- * \param[in]    gpu_info    pointer to structure holding GPU information
- */
-void free_gpu_info(const gmx_gpu_info_t* gpu_info);
-
-/*! \brief Initializes the GPU described by \c deviceInfo.
- *
- * TODO Doxygen complains about these - probably a Doxygen bug, since
- * the patterns here are the same as elsewhere in this header.
- *
- * \param[in]    deviceInfo   device info of the GPU to initialize
- *
- * Issues a fatal error for any critical errors that occur during
- * initialization.
- */
-GPU_FUNC_QUALIFIER
-void init_gpu(const gmx_device_info_t* GPU_FUNC_ARGUMENT(deviceInfo)) GPU_FUNC_TERM;
-
-/*! \brief Frees up the CUDA GPU used by the active context at the time of calling.
- *
- * If \c deviceInfo is nullptr, then it is understood that no device
- * was selected so no context is active to be freed. Otherwise, the
- * context is explicitly destroyed and therefore all data uploaded to
- * the GPU is lost. This must only be called when none of this data is
- * required anymore, because subsequent attempts to free memory
- * associated with the context will otherwise fail.
- *
- * Calls gmx_warning upon errors.
- *
- * \param[in]  deviceInfo   device info of the GPU to clean up for
- *
- * \returns                 true if no error occurs during the freeing.
- */
-CUDA_FUNC_QUALIFIER
-void free_gpu(const gmx_device_info_t* CUDA_FUNC_ARGUMENT(deviceInfo)) CUDA_FUNC_TERM;
-
-/*! \brief Return a pointer to the device info for \c deviceId
- *
- * \param[in] gpu_info      GPU info of all detected devices in the system.
- * \param[in] deviceId      ID for the GPU device requested.
- *
- * \returns                 Pointer to the device info for \c deviceId.
- */
-GPU_FUNC_QUALIFIER
-gmx_device_info_t* getDeviceInfo(const gmx_gpu_info_t& GPU_FUNC_ARGUMENT(gpu_info),
-                                 int GPU_FUNC_ARGUMENT(deviceId)) GPU_FUNC_TERM_WITH_RETURN(nullptr);
-
-/*! \brief Returns the device ID of the CUDA GPU currently in use.
- *
- * The GPU used is the one that is active at the time of the call in the active context.
- *
- * \returns                 device ID of the GPU in use at the time of the call
- */
-CUDA_FUNC_QUALIFIER
-int get_current_cuda_gpu_device_id() CUDA_FUNC_TERM_WITH_RETURN(-1);
-
-/*! \brief Formats and returns a device information string for a given GPU.
- *
- * Given an index *directly* into the array of available GPUs (gpu_dev)
- * returns a formatted info string for the respective GPU which includes
- * ID, name, compute capability, and detection status.
- *
- * \param[out]  s           pointer to output string (has to be allocated externally)
- * \param[in]   gpu_info    Information about detected GPUs
- * \param[in]   index       an index *directly* into the array of available GPUs
- */
-GPU_FUNC_QUALIFIER
-void get_gpu_device_info_string(char*                 GPU_FUNC_ARGUMENT(s),
-                                const gmx_gpu_info_t& GPU_FUNC_ARGUMENT(gpu_info),
-                                int                   GPU_FUNC_ARGUMENT(index)) GPU_FUNC_TERM;
-
-
-/*! \brief Returns the size of the gpu_dev_info struct.
- *
- * The size of gpu_dev_info can be used for allocation and communication.
- *
- * \returns                 size in bytes of gpu_dev_info
- */
-GPU_FUNC_QUALIFIER
-size_t sizeof_gpu_dev_info() GPU_FUNC_TERM_WITH_RETURN(0);
-
-//! Get status of device with specified index
-int gpu_info_get_stat(const gmx_gpu_info_t& info, int index);
-
 /*! \brief Check if GROMACS has been built with GPU support.
  *
  * \param[in] error Pointer to error string or nullptr.
diff --git a/src/gromacs/gpu_utils/gpu_vec.cuh b/src/gromacs/gpu_utils/gpu_vec.cuh
deleted file mode 100644 (file)
index fa61b15..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#ifndef GMX_GPU_UTILS_GPU_VEC_CUH
-#define GMX_GPU_UTILS_GPU_VEC_CUH
-
-/* Note that because of the duplicate of ivec, this header (or an
- * OpenCL port of it) cannot be included in a translation unit that
- * also includes the normal vectypes.h */
-#define XX 0 /* Defines for indexing in */
-#define YY 1 /* vectors                 */
-#define ZZ 2
-#define DIM 3 /* Dimension of vectors    */
-typedef int   ivec[DIM];
-typedef float fvec[DIM];
-
-/* maths operations */
-/* imported from cpu versions in math/vec.h */
-__forceinline__ __device__ void svmul_gpu(float a, const fvec v1, fvec v2)
-{
-    v2[XX] = a * v1[XX];
-    v2[YY] = a * v1[YY];
-    v2[ZZ] = a * v1[ZZ];
-}
-
-
-__forceinline__ __device__ void fvec_add_gpu(const fvec a, const fvec b, fvec c)
-{
-    float 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;
-}
-
-__forceinline__ __device__ void ivec_add_gpu(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;
-}
-
-__forceinline__ __device__ void fvec_inc_atomic(fvec a, const fvec b)
-{
-    atomicAdd(&a[XX], b[XX]);
-    atomicAdd(&a[YY], b[YY]);
-    atomicAdd(&a[ZZ], b[ZZ]);
-}
-
-__forceinline__ __device__ void fvec_inc_gpu(fvec a, const fvec b)
-{
-    float 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;
-}
-
-__forceinline__ __device__ void fvec_dec_atomic(fvec a, const fvec b)
-{
-    atomicAdd(&a[XX], -1.0f * b[XX]);
-    atomicAdd(&a[YY], -1.0f * b[YY]);
-    atomicAdd(&a[ZZ], -1.0f * b[ZZ]);
-}
-
-__forceinline__ __device__ void fvec_dec_gpu(fvec a, const fvec b)
-{
-    float 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;
-}
-
-__forceinline__ __device__ void cprod_gpu(const fvec a, const fvec b, fvec 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];
-}
-
-__forceinline__ __device__ float iprod_gpu(const fvec a, const fvec b)
-{
-    return (a[XX] * b[XX] + a[YY] * b[YY] + a[ZZ] * b[ZZ]);
-}
-
-__forceinline__ __device__ float norm_gpu(const fvec a)
-{
-    return sqrt(iprod_gpu(a, a));
-}
-
-__forceinline__ __device__ float gmx_angle_gpu(const fvec a, const fvec b)
-{
-    fvec  w;
-    float wlen, s;
-
-    cprod_gpu(a, b, w);
-
-    wlen = norm_gpu(w);
-    s    = iprod_gpu(a, b);
-
-    return atan2f(wlen, s); // requires float
-}
-
-__forceinline__ __device__ void clear_ivec_gpu(ivec a)
-{
-    a[XX] = 0;
-    a[YY] = 0;
-    a[ZZ] = 0;
-}
-__forceinline__ __device__ void fvec_sub_gpu(const fvec a, const fvec b, fvec c)
-{
-    float 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;
-}
-
-__forceinline__ __device__ float norm2_gpu(const fvec a)
-{
-    return a[XX] * a[XX] + a[YY] * a[YY] + a[ZZ] * a[ZZ];
-}
-
-__forceinline__ __device__ void copy_fvec_gpu(const fvec a, fvec b)
-{
-    b[XX] = a[XX];
-    b[YY] = a[YY];
-    b[ZZ] = a[ZZ];
-}
-
-__forceinline__ __device__ void copy_ivec_gpu(const ivec a, ivec b)
-{
-    b[XX] = a[XX];
-    b[YY] = a[YY];
-    b[ZZ] = a[ZZ];
-}
-
-__forceinline__ __device__ float cos_angle_gpu(const fvec a, const fvec b)
-{
-    /*
-     *                  ax*bx + ay*by + az*bz
-     * cos-vec (a,b) =  ---------------------
-     *                      ||a|| * ||b||
-     */
-    float cosval;
-    int   m;
-    float aa, bb, ip, ipa, ipb, ipab;
-
-    ip = ipa = ipb = 0.0f;
-    for (m = 0; (m < DIM); m++)
-    {
-        aa = a[m];
-        bb = b[m];
-        ip += aa * bb;
-        ipa += aa * aa;
-        ipb += bb * bb;
-    }
-    ipab = ipa * ipb;
-    if (ipab > 0.0f)
-    {
-        cosval = ip * rsqrt(ipab);
-    }
-    else
-    {
-        cosval = 1.0f;
-    }
-    if (cosval > 1.0f)
-    {
-        return 1.0f;
-    }
-    if (cosval < -1.0f)
-    {
-        return -1.0f;
-    }
-
-    return cosval;
-}
-
-
-__device__ static inline void unitv_gpu(const fvec src, fvec dest)
-{
-    float linv;
-
-    linv     = rsqrt(norm2_gpu(src));
-    dest[XX] = linv * src[XX];
-    dest[YY] = linv * src[YY];
-    dest[ZZ] = linv * src[ZZ];
-}
-
-#endif
index e2e6ac87044ecf5e838874214d49b0dd4e76f23b..14c3ac93cc2a9bc2c9f44333b141090eb2f6f401 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GPU_UTILS_GPUEVENTSYNCHRONIZER_CUH
 #define GMX_GPU_UTILS_GPUEVENTSYNCHRONIZER_CUH
 
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/gputraits.cuh"
 #include "gromacs/utility/gmxassert.h"
 
+#ifndef DOXYGEN
+
 /*! \libinternal \brief
  * A class which allows for CPU thread to mark and wait for certain GPU stream execution point.
  * The event can be put into the stream with markEvent() and then later waited on with waitForEvent().
@@ -80,9 +83,9 @@ public:
     /*! \brief Marks the synchronization point in the \p stream.
      * Should be followed by waitForEvent().
      */
-    inline void markEvent(CommandStream stream)
+    inline void markEvent(const DeviceStream& deviceStream)
     {
-        cudaError_t gmx_used_in_debug stat = cudaEventRecord(event_, stream);
+        cudaError_t gmx_used_in_debug stat = cudaEventRecord(event_, deviceStream.stream());
         GMX_ASSERT(stat == cudaSuccess, "cudaEventRecord failed");
     }
     /*! \brief Synchronizes the host thread on the marked event. */
@@ -92,9 +95,9 @@ public:
         GMX_ASSERT(stat == cudaSuccess, "cudaEventSynchronize failed");
     }
     /*! \brief Enqueues a wait for the recorded event in stream \p stream */
-    inline void enqueueWaitEvent(CommandStream stream)
+    inline void enqueueWaitEvent(const DeviceStream& deviceStream)
     {
-        cudaError_t gmx_used_in_debug stat = cudaStreamWaitEvent(stream, event_, 0);
+        cudaError_t gmx_used_in_debug stat = cudaStreamWaitEvent(deviceStream.stream(), event_, 0);
         GMX_ASSERT(stat == cudaSuccess, "cudaStreamWaitEvent failed");
     }
 
@@ -103,3 +106,5 @@ private:
 };
 
 #endif
+
+#endif
index b9298f385fcc82ad5173927a8585d23e521660e6..128e0564ddbfdaf6e4624f0e3f75cbdbb13b0ea7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GPU_UTILS_GPUEVENTSYNCHRONIZER_OCL_H
 #define GMX_GPU_UTILS_GPUEVENTSYNCHRONIZER_OCL_H
 
-#include "gromacs/gpu_utils/gputraits_ocl.h"
-#include "gromacs/gpu_utils/oclutils.h"
-#include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/gmxassert.h"
+#ifndef DOXYGEN
+
+#    include "gromacs/gpu_utils/gputraits_ocl.h"
+#    include "gromacs/gpu_utils/oclutils.h"
+#    include "gromacs/utility/exceptions.h"
+#    include "gromacs/utility/gmxassert.h"
 
 /*! \libinternal \brief
  * A class which allows for CPU thread to mark and wait for certain GPU stream execution point.
@@ -71,7 +73,6 @@ public:
         // being called after markEvent() but before waitForEvent() / enqueueWaitEvent().
         if (event_)
         {
-            ensureReferenceCount(event_, 1);
             clReleaseEvent(event_);
         }
     }
@@ -85,10 +86,10 @@ public:
     /*! \brief Marks the synchronization point in the \p stream.
      * Should be called first and then followed by waitForEvent().
      */
-    inline void markEvent(CommandStream stream)
+    inline void markEvent(const DeviceStream& deviceStream)
     {
         GMX_ASSERT(nullptr == event_, "Do not call markEvent more than once!");
-        cl_int clError = clEnqueueMarkerWithWaitList(stream, 0, nullptr, &event_);
+        cl_int clError = clEnqueueMarkerWithWaitList(deviceStream.stream(), 0, nullptr, &event_);
         if (CL_SUCCESS != clError)
         {
             GMX_THROW(gmx::InternalError("Failed to enqueue the GPU synchronization event: "
@@ -112,9 +113,9 @@ public:
      *  After enqueue, the associated event is released, so this method should
      *  be only called once per markEvent() call.
      */
-    inline void enqueueWaitEvent(CommandStream stream)
+    inline void enqueueWaitEvent(const DeviceStream& deviceStream)
     {
-        cl_int clError = clEnqueueBarrierWithWaitList(stream, 1, &event_, nullptr);
+        cl_int clError = clEnqueueBarrierWithWaitList(deviceStream.stream(), 1, &event_, nullptr);
         if (CL_SUCCESS != clError)
         {
             GMX_THROW(gmx::InternalError("Failed to enqueue device barrier for the GPU event: "
@@ -127,8 +128,6 @@ public:
 private:
     inline void releaseEvent()
     {
-        // Reference count can't be checked after the event's released, it seems (segfault on NVIDIA).
-        ensureReferenceCount(event_, 1);
         cl_int clError = clReleaseEvent(event_);
         if (CL_SUCCESS != clError)
         {
@@ -142,3 +141,5 @@ private:
 };
 
 #endif
+
+#endif
diff --git a/src/gromacs/gpu_utils/gpueventsynchronizer_sycl.h b/src/gromacs/gpu_utils/gpueventsynchronizer_sycl.h
new file mode 100644 (file)
index 0000000..aae05f4
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Implements a GpuEventSynchronizer class for OpenCL.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_GPUEVENTSYNCHRONIZER_SYCL_H
+#define GMX_GPU_UTILS_GPUEVENTSYNCHRONIZER_SYCL_H
+
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#ifndef DOXYGEN
+/*! \libinternal \brief
+ * A class which allows for CPU thread to mark and wait for certain GPU stream execution point.
+ * The event can be put into the stream with markEvent() and then later waited on with waitForEvent().
+ * This can be repeated as necessary, but the current implementation does not allow waiting on
+ * completed event more than once, expecting only exact pairs of markEvent(stream); waitForEvent().
+ * The class generally attempts to track the correctness of its state transitions, but
+ * please note that calling waitForEvent() right after the construction will fail with OpenCL but succeed with CUDA.
+ *
+ * Another possible mode of operation can be implemented if needed:
+ * multiple calls to waitForEvent() after a single markEvent(). For this, clReleaseEvent() call
+ * from waitForEvent() should instead happen conditionally at the beginning of markEvent(), replacing
+ * the GMX_ASSERT(). This was tested to work both with CUDA and NVidia OpenCL, but not with AMD/Intel OpenCl.
+ */
+class GpuEventSynchronizer
+{
+public:
+    //! A constructor
+    GpuEventSynchronizer() {}
+    //! A destructor
+    ~GpuEventSynchronizer() {}
+    //! No copying
+    GpuEventSynchronizer(const GpuEventSynchronizer&) = delete;
+    //! No assignment
+    GpuEventSynchronizer& operator=(GpuEventSynchronizer&&) = delete;
+    //! Moving is disabled but can be considered in the future if needed
+    GpuEventSynchronizer(GpuEventSynchronizer&&) = delete;
+
+    /*! \brief Marks the synchronization point in the \p stream.
+     * Should be called first and then followed by waitForEvent().
+     */
+    inline void markEvent(const DeviceStream& /* deviceStream */)
+    {
+        // SYCL-TODO
+    }
+    /*! \brief Synchronizes the host thread on the marked event. */
+    inline void waitForEvent()
+    {
+        // SYCL-TODO
+    }
+    /*! \brief Enqueues a wait for the recorded event in stream \p stream
+     *
+     *  After enqueue, the associated event is released, so this method should
+     *  be only called once per markEvent() call.
+     */
+    inline void enqueueWaitEvent(const DeviceStream& /* deviceStream */)
+    {
+        // SYCL-TODO
+    }
+
+private:
+    // SYCL-TODO
+};
+
+#endif // !defined DOXYGEN
+
+#endif // GMX_GPU_UTILS_GPUEVENTSYNCHRONIZER_SYCL_H
index 52e36c038ae999a4c01d1fb2739346041376b16c..c56d60da61dc17f00e29cff4ee1f993dca01dbbd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -79,15 +79,17 @@ public:
     GpuRegionTimerImpl(GpuRegionTimerImpl&&) = delete;
 
     /*! \brief Will be called before the region start. */
-    inline void openTimingRegion(CommandStream s)
+    inline void openTimingRegion(const DeviceStream& deviceStream)
     {
-        CU_RET_ERR(cudaEventRecord(eventStart_, s), "GPU timing recording failure");
+        CU_RET_ERR(cudaEventRecord(eventStart_, deviceStream.stream()),
+                   "GPU timing recording failure");
     }
 
     /*! \brief Will be called after the region end. */
-    inline void closeTimingRegion(CommandStream s)
+    inline void closeTimingRegion(const DeviceStream& deviceStream)
     {
-        CU_RET_ERR(cudaEventRecord(eventStop_, s), "GPU timing recording failure");
+        CU_RET_ERR(cudaEventRecord(eventStop_, deviceStream.stream()),
+                   "GPU timing recording failure");
     }
 
     /*! \brief Returns the last measured region timespan (in milliseconds) and calls reset() */
index 6798159ac9af5ddfdb717cbb778fa63ccc9acc30..f0860b164d9cbdbe61aade8bf3d5e127c5ed08cc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -84,9 +84,9 @@ public:
     /*! \brief
      * To be called before the region start.
      *
-     * \param[in] s   The GPU command stream where the event being measured takes place.
+     * \param[in] deviceStream   The GPU command stream where the event being measured takes place.
      */
-    void openTimingRegion(CommandStream s)
+    void openTimingRegion(const DeviceStream& deviceStream)
     {
         if (c_debugTimerState)
         {
@@ -96,14 +96,14 @@ public:
             GMX_ASSERT(debugState_ == TimerState::Idle, error.c_str());
             debugState_ = TimerState::Recording;
         }
-        impl_.openTimingRegion(s);
+        impl_.openTimingRegion(deviceStream);
     }
     /*! \brief
      * To be called after the region end.
      *
-     * \param[in] s   The GPU command stream where the event being measured takes place.
+     * \param[in] deviceStream   The GPU command stream where the event being measured takes place.
      */
-    void closeTimingRegion(CommandStream s)
+    void closeTimingRegion(const DeviceStream& deviceStream)
     {
         if (c_debugTimerState)
         {
@@ -114,7 +114,7 @@ public:
             debugState_ = TimerState::Stopped;
         }
         callCount_++;
-        impl_.closeTimingRegion(s);
+        impl_.closeTimingRegion(deviceStream);
     }
     /*! \brief
      * Accumulates the last timespan of all the events used into the total duration,
index 3c1d9b2b84b12fd6970b1d32234e1df227f77d1c..3434a451e477fb3442d94bf59ff6d36ecd7f8b43 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,6 +48,7 @@
 
 #include "gromacs/gpu_utils/gputraits_ocl.h"
 #include "gromacs/gpu_utils/oclutils.h"
+#include "gromacs/utility/stringutil.h"
 
 #include "gpuregiontimer.h"
 
@@ -82,9 +83,9 @@ public:
     GpuRegionTimerImpl(GpuRegionTimerImpl&&) = delete;
 
     /*! \brief Should be called before the region start. */
-    inline void openTimingRegion(CommandStream /*unused*/) {}
+    inline void openTimingRegion(const DeviceStream& /*unused*/) {}
     /*! \brief Should be called after the region end. */
-    inline void closeTimingRegion(CommandStream /*unused*/) {}
+    inline void closeTimingRegion(const DeviceStream& /*unused*/) {}
     /*! \brief Returns the last measured region timespan (in milliseconds) and calls reset(). */
     inline double getLastRangeTime()
     {
@@ -98,10 +99,16 @@ public:
 
                 cl_error = clGetEventProfilingInfo(events_[i], CL_PROFILING_COMMAND_START,
                                                    sizeof(cl_ulong), &start_ns, nullptr);
-                GMX_ASSERT(CL_SUCCESS == cl_error, "GPU timing update failure");
+                GMX_ASSERT(CL_SUCCESS == cl_error,
+                           gmx::formatString("GPU timing update failure (OpenCL error %d: %s).",
+                                             cl_error, ocl_get_error_string(cl_error).c_str())
+                                   .c_str());
                 cl_error = clGetEventProfilingInfo(events_[i], CL_PROFILING_COMMAND_END,
                                                    sizeof(cl_ulong), &end_ns, nullptr);
-                GMX_ASSERT(CL_SUCCESS == cl_error, "GPU timing update failure");
+                GMX_ASSERT(CL_SUCCESS == cl_error,
+                           gmx::formatString("GPU timing update failure (OpenCL error %d: %s).",
+                                             cl_error, ocl_get_error_string(cl_error).c_str())
+                                   .c_str());
                 milliseconds += (end_ns - start_ns) / 1000000.0;
             }
         }
index 7ac35d532968c201991c15631da9f4c0d049f002..fec113b4b4c1f4bcd1414e1ee8cfd4d4478b687f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 /*! \libinternal \file
  *  \brief Declares the CUDA type traits.
+ *
  *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
  *
  * \inlibraryapi
  * \ingroup module_gpu_utils
  */
+#include <cuda_runtime.h>
+
+//! Device texture for fast read-only data fetching
+using DeviceTexture = cudaTextureObject_t;
 
-//! \brief GPU command stream
-using CommandStream = cudaStream_t;
 //! \brief Single GPU call timing event - meaningless in CUDA
 using CommandEvent = void;
-//! \brief Context used explicitly in OpenCL, does nothing in CUDA
-using DeviceContext = void*;
 
 /*! \internal \brief
  * GPU kernels scheduling description. This is same in OpenCL/CUDA.
@@ -57,10 +59,12 @@ using DeviceContext = void*;
  */
 struct KernelLaunchConfig
 {
-    size_t        gridSize[3]      = { 1, 1, 1 }; //!< Block counts
-    size_t        blockSize[3]     = { 1, 1, 1 }; //!< Per-block thread counts
-    size_t        sharedMemorySize = 0;           //!< Shared memory size in bytes
-    CommandStream stream           = nullptr;     //!< Stream to launch kernel in
+    //! Block counts
+    size_t gridSize[3] = { 1, 1, 1 };
+    //! Per-block thread counts
+    size_t blockSize[3] = { 1, 1, 1 };
+    //! Shared memory size in bytes
+    size_t sharedMemorySize = 0;
 };
 
 //! Sets whether device code can use arrays that are embedded in structs.
index 9b91c0ec18e5145695afb1f71703d1fe7b2e5c6e..38c5edf8a90d0b87bcaf6a2fd48065bd4c4a26b6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_GPU_UTILS_GPUTRAITS_H
 
 /*! \libinternal \file
- *  \brief Declares the GPU type traits for non-GPU builds
+ *  \brief Declares the GPU type traits for non-GPU builds.
+ *
  *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
  *
  * \inlibraryapi
  * \ingroup module_gpu_utils
 
 #include "config.h"
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 
 #    include "gromacs/gpu_utils/gputraits.cuh"
 
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
 
 #    include "gromacs/gpu_utils/gputraits_ocl.h"
 
+#elif GMX_GPU_SYCL
+
+#    include "gromacs/gpu_utils/gputraits_sycl.h"
+
 #else
 
-//! \brief GPU command stream
-using CommandStream = void*;
+using DeviceTexture = void*;
+
 //! \brief Single GPU call timing event
 using CommandEvent = void*;
-//! \brief GPU context
-using DeviceContext = void*;
 
 #endif // GMX_GPU
 
index 865c29967c56c9a8911d86f4c80d73da5bf1a490..489bb0527c0b34a016d7e7462db236722336d9aa 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -37,7 +37,9 @@
 
 /*! \libinternal \file
  *  \brief Declares the OpenCL type traits.
+ *
  *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
  *
  * \inlibraryapi
  * \ingroup module_gpu_utils
 
 #include "gromacs/gpu_utils/gmxopencl.h"
 
-//! \brief GPU command stream
-using CommandStream = cl_command_queue;
+using DeviceTexture = void*;
+
 //! \brief Single GPU call timing event
 using CommandEvent = cl_event;
-//! \brief Context used explicitly in OpenCL
-using DeviceContext = cl_context;
 
 /*! \internal \brief
  * GPU kernels scheduling description. This is same in OpenCL/CUDA.
@@ -59,10 +59,12 @@ using DeviceContext = cl_context;
  */
 struct KernelLaunchConfig
 {
-    size_t        gridSize[3]      = { 1, 1, 1 }; //!< Work groups (CUDA blocks) counts
-    size_t        blockSize[3]     = { 1, 1, 1 }; //!< Per work group (CUDA block) thread counts
-    size_t        sharedMemorySize = 0;           //!< Shared memory size in bytes
-    CommandStream stream           = nullptr;     //!< Stream to launch kernel in
+    //! Work groups (CUDA blocks) counts
+    size_t gridSize[3] = { 1, 1, 1 };
+    //! Per work group (CUDA block) thread counts
+    size_t blockSize[3] = { 1, 1, 1 };
+    //! Shared memory size in bytes
+    size_t sharedMemorySize = 0;
 };
 
 /*! \brief Sets whether device code can use arrays that are embedded in structs.
similarity index 60%
rename from src/gromacs/gpu_utils/gpu_testutils.h
rename to src/gromacs/gpu_utils/gputraits_sycl.h
index 1ea82278e4d42aa5828b8dc5e887d8c202aa3954..ab121c8858c72fd8398a38fe6c02dc70805a9fc5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+#ifndef GMX_GPU_UTILS_GPUTRAITS_SYCL_H
+#define GMX_GPU_UTILS_GPUTRAITS_SYCL_H
+
 /*! \libinternal \file
- *  \brief Declare functions for detection of GPU devices, specific for tests.
- *
- *  \todo This should eventually go to src/testutils
+ *  \brief Declares the SYCL type traits.
  *
- *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *  \author Andrey Alekseenko <al42and@gmail.com>
  *
- *  \inlibraryapi
+ * \inlibraryapi
+ * \ingroup module_gpu_utils
  */
 
-#ifndef GMX_GPU_UTILS_GPU_TESTUTILS_H
-#define GMX_GPU_UTILS_GPU_TESTUTILS_H
+#include <cstddef>
 
-/*! \brief Checks if there is a compatible GPU to run the computations on
- *
- * There are several reasons why code can not rune on the GPU:
- * 1. The GPU can not be detected, because there is none in the system.
- * 2. GPU detection is disabled by GMX_DISABLE_GPU_DETECTION environmental variable.
- * 3. GPUs are detected, but none of them is compatible.
- * This function checks all these conditions and returns true only if there at least
- * one GPU that can be used for computations.
- *
- * \returns True, if there a GPU that can be used for computations
+#include "gromacs/gpu_utils/gmxsycl.h"
+
+using DeviceTexture = void*;
+
+//! \brief Single GPU call timing event, not used with SYCL
+using CommandEvent = void*;
+
+/*! \internal \brief
+ * GPU kernels scheduling description. This is same in OpenCL/CUDA.
+ * Provides reasonable defaults, one typically only needs to set the GPU stream
+ * and non-1 work sizes.
+ */
+struct KernelLaunchConfig
+{
+    //! Work groups (CUDA blocks) counts
+    size_t gridSize[3] = { 1, 1, 1 };
+    //! Per work group (CUDA block) thread counts
+    size_t blockSize[3] = { 1, 1, 1 };
+    //! Shared memory size in bytes
+    size_t sharedMemorySize = 0;
+};
+
+/*! \brief Sets whether device code can use arrays that are embedded in structs.
+ * \todo Probably can, must check
  */
-bool canComputeOnGpu();
+#define c_canEmbedBuffers false
 
-#endif // GMX_GPU_UTILS_GPU_TESTUTILS_H
+#endif
index 888c86062ed0013dd7feabefaf133c696d93b12b..f483f0cf286d7c954588372549a4e336deee5f46 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -186,6 +186,7 @@ public:
     //! Propagate for move
     using propagate_on_container_swap = std::true_type;
     //! Use default allocator for copy (same as construct+copy)
+    // NOLINTNEXTLINE readability-convert-member-functions-to-static
     HostAllocationPolicy select_on_container_copy_construction() const { return {}; }
 
 private:
index 44eaf47e16a49d40aef64421a8b0035c45422360..e8894aaad00b87eabd966f53695e9601ddb71b8a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -138,8 +139,8 @@ cl_program makeProgramFromCache(const std::string& filename, cl_context context,
                                                    nullptr, &cl_error);
     if (cl_error != CL_SUCCESS)
     {
-        GMX_THROW(InternalError("Could not create OpenCL program, error was "
-                                + ocl_get_error_string(cl_error)));
+        GMX_THROW(InternalError("Could not create OpenCL program from the cache file " + filename
+                                + ", error was " + ocl_get_error_string(cl_error)));
     }
 
     return program;
index df1f8ce75abd9187093da770483688e1291307b1..82b4e4576293448a555e078986cc565db25668d0 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 9d3b692df16ab1255b1807a331fb1baa68f1b9e8..1f670e8b910e26d6d4624ded3bf8b451ed76b9c8 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -154,11 +155,11 @@ static void writeOclBuildLog(FILE*              fplog,
 
 /*! \brief Construct compiler options string
  *
- * \param deviceVendorId  Device vendor id. Used to
- *          automatically enable some vendor-specific options
+ * \param deviceVendor  Device vendor. Used to automatically enable some
+ *                      vendor-specific options.
  * \return The string with the compiler options
  */
-static std::string selectCompilerOptions(ocl_vendor_id_t deviceVendorId)
+static std::string selectCompilerOptions(DeviceVendor deviceVendor)
 {
     std::string compilerOptions;
 
@@ -178,12 +179,12 @@ static std::string selectCompilerOptions(ocl_vendor_id_t deviceVendorId)
         compilerOptions += " -cl-denorms-are-zero";
     }
 
-    if ((deviceVendorId == OCL_VENDOR_NVIDIA) && getenv("GMX_OCL_VERBOSE"))
+    if ((deviceVendor == DeviceVendor::Nvidia) && getenv("GMX_OCL_VERBOSE"))
     {
         compilerOptions += " -cl-nv-verbose";
     }
 
-    if ((deviceVendorId == OCL_VENDOR_AMD) && getenv("GMX_OCL_DUMP_INTERM_FILES"))
+    if ((deviceVendor == DeviceVendor::Amd) && getenv("GMX_OCL_DUMP_INTERM_FILES"))
     {
         /* To dump OpenCL build intermediate files, caching must be off */
         if (!useBuildCache)
@@ -308,21 +309,19 @@ size_t getDeviceWarpSize(cl_context context, cl_device_id deviceId)
 
 /*! \brief Select a compilation-line define for a vendor-specific kernel choice from vendor id
  *
- * \param[in] vendorId Vendor id enumerator
+ * \param[in] deviceVendor Vendor id enumerator
  *
  * \return The appropriate compilation-line define
  */
-static const char* makeVendorFlavorChoice(ocl_vendor_id_t vendorId)
+static std::string makeVendorFlavorChoice(DeviceVendor deviceVendor)
 {
-    const char* choice;
-    switch (vendorId)
+    switch (deviceVendor)
     {
-        case OCL_VENDOR_AMD: choice = "-D_AMD_SOURCE_"; break;
-        case OCL_VENDOR_NVIDIA: choice = "-D_NVIDIA_SOURCE_"; break;
-        case OCL_VENDOR_INTEL: choice = "-D_INTEL_SOURCE_"; break;
-        default: choice = ""; break;
+        case DeviceVendor::Amd: return "-D_AMD_SOURCE_";
+        case DeviceVendor::Nvidia: return "-D_NVIDIA_SOURCE_";
+        case DeviceVendor::Intel: return "-D_INTEL_SOURCE_";
+        default: return "";
     }
-    return choice;
 }
 
 /*! \brief Create include paths for kernel sources.
@@ -379,7 +378,7 @@ static void removeExtraSpaces(std::string* str)
 static std::string makePreprocessorOptions(const std::string& kernelRootPath,
                                            const std::string& includeRootPath,
                                            size_t             warpSize,
-                                           ocl_vendor_id_t    deviceVendorId,
+                                           DeviceVendor       deviceVendor,
                                            const std::string& extraDefines)
 {
     std::string preprocessorOptions;
@@ -387,11 +386,11 @@ static std::string makePreprocessorOptions(const std::string& kernelRootPath,
     /* Compose the complete build options */
     preprocessorOptions = formatString("-DWARP_SIZE_TEST=%d", static_cast<int>(warpSize));
     preprocessorOptions += ' ';
-    preprocessorOptions += makeVendorFlavorChoice(deviceVendorId);
+    preprocessorOptions += makeVendorFlavorChoice(deviceVendor);
     preprocessorOptions += ' ';
     preprocessorOptions += extraDefines;
     preprocessorOptions += ' ';
-    preprocessorOptions += selectCompilerOptions(deviceVendorId);
+    preprocessorOptions += selectCompilerOptions(deviceVendor);
     preprocessorOptions += ' ';
     preprocessorOptions += makeKernelIncludePathOption(kernelRootPath);
     preprocessorOptions += ' ';
@@ -409,7 +408,7 @@ cl_program compileProgram(FILE*              fplog,
                           const std::string& extraDefines,
                           cl_context         context,
                           cl_device_id       deviceId,
-                          ocl_vendor_id_t    deviceVendorId)
+                          DeviceVendor       deviceVendor)
 {
     cl_int cl_error;
     // Let the kernel find include files from its module.
@@ -424,7 +423,7 @@ cl_program compileProgram(FILE*              fplog,
 
     /* Make the build options */
     std::string preprocessorOptions = makePreprocessorOptions(
-            kernelRootPath, rootPath, getDeviceWarpSize(context, deviceId), deviceVendorId, extraDefines);
+            kernelRootPath, rootPath, getDeviceWarpSize(context, deviceId), deviceVendor, extraDefines);
 
     bool buildCacheWasRead = false;
 
@@ -451,11 +450,15 @@ cl_program compileProgram(FILE*              fplog,
                 // Failing to read from the cache is not a critical error
                 formatExceptionMessageToFile(fplog, e);
             }
+            fprintf(fplog, "OpenCL binary cache file %s is present, will load kernels.\n",
+                    cacheFilename.c_str());
         }
         else
         {
             fprintf(fplog,
-                    "No OpenCL binary cache file was present, so will compile kernels normally.\n");
+                    "No OpenCL binary cache file was present for %s, so will compile kernels "
+                    "normally.\n",
+                    kernelBaseFilename.c_str());
         }
     }
     if (program == nullptr)
@@ -510,7 +513,7 @@ cl_program compileProgram(FILE*              fplog,
             }
         }
     }
-    if ((OCL_VENDOR_NVIDIA == deviceVendorId) && getenv("GMX_OCL_DUMP_INTERM_FILES"))
+    if ((deviceVendor == DeviceVendor::Nvidia) && getenv("GMX_OCL_DUMP_INTERM_FILES"))
     {
         /* If dumping intermediate files has been requested and this is an NVIDIA card
            => write PTX to file */
index 68b34c1a224d2e071750a2e311466221907ada91..00baec0f0689cd1241fdaf4d89543586603b53cb 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +48,7 @@
 #include <string>
 
 #include "gromacs/gpu_utils/oclutils.h"
+#include "gromacs/hardware/device_information.h"
 
 namespace gmx
 {
@@ -58,7 +60,7 @@ namespace ocl
  *  This is platform implementation dependent and seems to only work on the Nvidia and AMD
  * platforms! Nvidia reports 32, AMD for GPU 64. Intel seems to report 16, but that is not correct,
  *  as it execution width can be between 8-32 and it's picked per-kernel at compile-time.
- *  Therefore, for Intel it should actually be queried separately for each kernel (Redmine #2520).
+ *  Therefore, for Intel it should actually be queried separately for each kernel (Issue #2520).
  *
  *  \param  context   Current OpenCL context
  *  \param  deviceId OpenCL device with the context
@@ -85,10 +87,12 @@ size_t getKernelWarpSize(cl_kernel kernel, cl_device_id deviceId);
  * \param[in]  kernelRelativePath    Relative path to the kernel in the source tree,
  *                                   e.g. "src/gromacs/mdlib/nbnxn_ocl" for NB kernels.
  * \param[in]  kernelBaseFilename    The name of the kernel source file to compile, e.g.
- * "nbnxn_ocl_kernels.cl" \param[in]  extraDefines          Preprocessor defines required by the
- * calling code, e.g. for configuring the kernels \param[in]  context               OpenCL context
- * on the device to compile for \param[in]  deviceId              OpenCL device id of the device to
- * compile for \param[in]  deviceVendorId        Enumerator of the device vendor to compile for
+ *                                   "nbnxn_ocl_kernels.cl"
+ * \param[in]  extraDefines          Preprocessor defines required by the calling code,
+ *                                   e.g. for configuring the kernels
+ * \param[in]  context               OpenCL context on the device to compile for
+ * \param[in]  deviceId              OpenCL device id of the device to compile for
+ * \param[in]  deviceVendor          Enumerator of the device vendor to compile for
  *
  * \returns The compiled OpenCL program
  *
@@ -106,7 +110,7 @@ cl_program compileProgram(FILE*              fplog,
                           const std::string& extraDefines,
                           cl_context         context,
                           cl_device_id       deviceId,
-                          ocl_vendor_id_t    deviceVendorId);
+                          DeviceVendor       deviceVendor);
 
 } // namespace ocl
 } // namespace gmx
index b14545f7ed716868784f01e73efe623a62177a69..726e4f2cff47fdfd65afc75b5b80dacc20823700 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
 
-int ocl_copy_H2D(cl_mem             d_dest,
-                 const void*        h_src,
-                 size_t             offset,
-                 size_t             bytes,
-                 GpuApiCallBehavior transferKind,
-                 cl_command_queue   command_queue,
-                 cl_event*          copy_event)
-{
-    cl_int gmx_unused cl_error;
-
-    if (d_dest == nullptr || h_src == nullptr || bytes == 0)
-    {
-        return -1;
-    }
-
-    switch (transferKind)
-    {
-        case GpuApiCallBehavior::Async:
-            cl_error = clEnqueueWriteBuffer(command_queue, d_dest, CL_FALSE, offset, bytes, h_src,
-                                            0, nullptr, copy_event);
-            assert(cl_error == CL_SUCCESS);
-            // TODO: handle errors
-            break;
-
-        case GpuApiCallBehavior::Sync:
-            cl_error = clEnqueueWriteBuffer(command_queue, d_dest, CL_TRUE, offset, bytes, h_src, 0,
-                                            nullptr, copy_event);
-            assert(cl_error == CL_SUCCESS);
-            // TODO: handle errors
-            break;
-
-        default: throw;
-    }
-
-    return 0;
-}
-
-/*! \brief Launches asynchronous host to device memory copy.
- *
- *  If copy_event is not nullptr, on return it will contain an event object
- *  identifying this particular host to device operation. The event can further
- *  be used to queue a wait for this operation or to query profiling information.
- */
-int ocl_copy_H2D_async(cl_mem           d_dest,
-                       const void*      h_src,
-                       size_t           offset,
-                       size_t           bytes,
-                       cl_command_queue command_queue,
-                       cl_event*        copy_event)
-{
-    return ocl_copy_H2D(d_dest, h_src, offset, bytes, GpuApiCallBehavior::Async, command_queue, copy_event);
-}
-
-/*! \brief Launches synchronous host to device memory copy.
- */
-int ocl_copy_H2D_sync(cl_mem d_dest, const void* h_src, size_t offset, size_t bytes, cl_command_queue command_queue)
-{
-    return ocl_copy_H2D(d_dest, h_src, offset, bytes, GpuApiCallBehavior::Sync, command_queue, nullptr);
-}
-
-int ocl_copy_D2H(void*              h_dest,
-                 cl_mem             d_src,
-                 size_t             offset,
-                 size_t             bytes,
-                 GpuApiCallBehavior transferKind,
-                 cl_command_queue   command_queue,
-                 cl_event*          copy_event)
-{
-    cl_int gmx_unused cl_error;
-
-    if (h_dest == nullptr || d_src == nullptr || bytes == 0)
-    {
-        return -1;
-    }
-
-    switch (transferKind)
-    {
-        case GpuApiCallBehavior::Async:
-            cl_error = clEnqueueReadBuffer(command_queue, d_src, CL_FALSE, offset, bytes, h_dest, 0,
-                                           nullptr, copy_event);
-            assert(cl_error == CL_SUCCESS);
-            // TODO: handle errors
-            break;
-
-        case GpuApiCallBehavior::Sync:
-            cl_error = clEnqueueReadBuffer(command_queue, d_src, CL_TRUE, offset, bytes, h_dest, 0,
-                                           nullptr, copy_event);
-            assert(cl_error == CL_SUCCESS);
-            // TODO: handle errors
-            break;
-
-        default: throw;
-    }
-
-    return 0;
-}
-
-/*! \brief Launches asynchronous device to host memory copy.
- *
- *  If copy_event is not nullptr, on return it will contain an event object
- *  identifying this particular host to device operation. The event can further
- *  be used to queue a wait for this operation or to query profiling information.
- */
-int ocl_copy_D2H_async(void*            h_dest,
-                       cl_mem           d_src,
-                       size_t           offset,
-                       size_t           bytes,
-                       cl_command_queue command_queue,
-                       cl_event*        copy_event)
-{
-    return ocl_copy_D2H(h_dest, d_src, offset, bytes, GpuApiCallBehavior::Async, command_queue, copy_event);
-}
-
 /*! \brief \brief Allocates nbytes of host memory. Use ocl_free to free memory allocated with this function.
  *
  *  \todo
index 00df0ffcaa16e7ebfcb3cdc90fe436eaede2e397..e5d4f6da3362320f5b9227c00a77380f609d64f8 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <string>
 
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/gmxopencl.h"
 #include "gromacs/gpu_utils/gputraits_ocl.h"
 #include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
 
 enum class GpuApiCallBehavior;
 
-/*! \brief OpenCL vendor IDs */
-typedef enum
-{
-    OCL_VENDOR_NVIDIA = 0,
-    OCL_VENDOR_AMD,
-    OCL_VENDOR_INTEL,
-    OCL_VENDOR_UNKNOWN
-} ocl_vendor_id_t;
-
-/*! \internal
- * \brief OpenCL GPU device identificator
- *
- * An OpenCL device is identified by its ID.
- * The platform ID is also included for caching reasons.
- */
-typedef struct
-{
-    cl_platform_id ocl_platform_id; /**< Platform ID */
-    cl_device_id   ocl_device_id;   /**< Device ID */
-} ocl_gpu_id_t;
-
-/*! \internal
- * \brief OpenCL device information.
- *
- * The OpenCL device information is queried and set at detection and contains
- * both information about the device/hardware returned by the runtime as well
- * as additional data like support status.
- */
-struct gmx_device_info_t
-{
-    ocl_gpu_id_t    ocl_gpu_id;          /**< device ID assigned at detection   */
-    char            device_name[256];    /**< device name */
-    char            device_version[256]; /**< device version */
-    char            device_vendor[256];  /**< device vendor */
-    int             compute_units;       /**< number of compute units */
-    int             adress_bits;         /**< number of adress bits the device is capable of */
-    int             stat;                /**< device status takes values of e_gpu_detect_res_t */
-    ocl_vendor_id_t vendor_e;            /**< device vendor as defined by ocl_vendor_id_t */
-    size_t maxWorkItemSizes[3]; /**< workgroup size limits (CL_DEVICE_MAX_WORK_ITEM_SIZES) */
-    size_t maxWorkGroupSize;    /**< workgroup total size limit (CL_DEVICE_MAX_WORK_GROUP_SIZE) */
-};
-
 /*! \internal
  * \brief OpenCL GPU runtime data
  *
@@ -104,58 +67,10 @@ struct gmx_device_info_t
  */
 struct gmx_device_runtime_data_t
 {
-    cl_context context; /**< OpenCL context */
-    cl_program program; /**< OpenCL program */
+    //! OpenCL program
+    cl_program program;
 };
 
-/*! \brief Launches synchronous or asynchronous device to host memory copy.
- *
- *  If copy_event is not NULL, on return it will contain an event object
- *  identifying this particular device to host operation. The event can further
- *  be used to queue a wait for this operation or to query profiling information.
- */
-int ocl_copy_D2H(void*              h_dest,
-                 cl_mem             d_src,
-                 size_t             offset,
-                 size_t             bytes,
-                 GpuApiCallBehavior transferKind,
-                 cl_command_queue   command_queue,
-                 cl_event*          copy_event);
-
-
-/*! \brief Launches asynchronous device to host memory copy. */
-int ocl_copy_D2H_async(void*            h_dest,
-                       cl_mem           d_src,
-                       size_t           offset,
-                       size_t           bytes,
-                       cl_command_queue command_queue,
-                       cl_event*        copy_event);
-
-/*! \brief Launches synchronous or asynchronous host to device memory copy.
- *
- *  If copy_event is not NULL, on return it will contain an event object
- *  identifying this particular host to device operation. The event can further
- *  be used to queue a wait for this operation or to query profiling information.
- */
-int ocl_copy_H2D(cl_mem             d_dest,
-                 const void*        h_src,
-                 size_t             offset,
-                 size_t             bytes,
-                 GpuApiCallBehavior transferKind,
-                 cl_command_queue   command_queue,
-                 cl_event*          copy_event);
-
-/*! \brief Launches asynchronous host to device memory copy. */
-int ocl_copy_H2D_async(cl_mem           d_dest,
-                       const void*      h_src,
-                       size_t           offset,
-                       size_t           bytes,
-                       cl_command_queue command_queue,
-                       cl_event*        copy_event);
-
-/*! \brief Launches synchronous host to device memory copy. */
-int ocl_copy_H2D_sync(cl_mem d_dest, const void* h_src, size_t offset, size_t bytes, cl_command_queue command_queue);
-
 /*! \brief Allocate host memory in malloc style */
 void pmalloc(void** h_ptr, size_t nbytes);
 
@@ -165,37 +80,11 @@ void pfree(void* h_ptr);
 /*! \brief Convert error code to diagnostic string */
 std::string ocl_get_error_string(cl_int error);
 
-/*! \brief Calls clFinish() in the stream \p s.
- *
- * \param[in] s stream to synchronize with
- */
-static inline void gpuStreamSynchronize(cl_command_queue s)
-{
-    cl_int cl_error = clFinish(s);
-    GMX_RELEASE_ASSERT(CL_SUCCESS == cl_error,
-                       ("Error caught during clFinish:" + ocl_get_error_string(cl_error)).c_str());
-}
-
-//! A debug checker to track cl_events being released correctly
-inline void ensureReferenceCount(const cl_event& event, unsigned int refCount)
-{
-#ifndef NDEBUG
-    cl_int clError = clGetEventInfo(event, CL_EVENT_REFERENCE_COUNT, sizeof(refCount), &refCount, nullptr);
-    GMX_ASSERT(CL_SUCCESS == clError, ocl_get_error_string(clError).c_str());
-    GMX_ASSERT(refCount == refCount, "Unexpected reference count");
-#else
-    GMX_UNUSED_VALUE(event);
-    GMX_UNUSED_VALUE(refCount);
-#endif
-}
-
 /*! \brief Pretend to synchronize an OpenCL stream (dummy implementation).
  *
- * \param[in] s queue to check
- *
- *  \returns     True if all tasks enqueued in the stream \p s (at the time of this call) have completed.
+ *  \returns  Not implemented in OpenCL.
  */
-static inline bool haveStreamTasksCompleted(cl_command_queue gmx_unused s)
+static inline bool haveStreamTasksCompleted(const DeviceStream& /* deviceStream */)
 {
     GMX_RELEASE_ASSERT(false, "haveStreamTasksCompleted is not implemented for OpenCL");
     return false;
@@ -249,11 +138,10 @@ void prepareGpuKernelArgument(cl_kernel                 kernel,
 
     // Assert on types not allowed to be passed to a kernel
     // (as per section 6.9 of the OpenCL spec).
-    static_assert(!std::is_same<CurrentArg, bool>::value && !std::is_same<CurrentArg, size_t>::value
-                          && !std::is_same<CurrentArg, ptrdiff_t>::value
-                          && !std::is_same<CurrentArg, intptr_t>::value
-                          && !std::is_same<CurrentArg, uintptr_t>::value,
-                  "Invalid type passed to OpenCL kernel functions (see OpenCL spec section 6.9).");
+    static_assert(
+            !std::is_same_v<CurrentArg,
+                            bool> && !std::is_same_v<CurrentArg, size_t> && !std::is_same_v<CurrentArg, ptrdiff_t> && !std::is_same_v<CurrentArg, intptr_t> && !std::is_same_v<CurrentArg, uintptr_t>,
+            "Invalid type passed to OpenCL kernel functions (see OpenCL spec section 6.9).");
 
     prepareGpuKernelArgument(kernel, config, argIndex + 1, otherArgsPtrs...);
 }
@@ -280,12 +168,14 @@ void* prepareGpuKernelArguments(cl_kernel kernel, const KernelLaunchConfig& conf
  *
  * \param[in] kernel          Kernel function handle
  * \param[in] config          Kernel configuration for launching
+ * \param[in] deviceStream    GPU stream to launch kernel in
  * \param[in] timingEvent     Timing event, fetched from GpuRegionTimer
  * \param[in] kernelName      Human readable kernel description, for error handling only
  * \throws gmx::InternalError on kernel launch failure
  */
 inline void launchGpuKernel(cl_kernel                 kernel,
                             const KernelLaunchConfig& config,
+                            const DeviceStream&       deviceStream,
                             CommandEvent*             timingEvent,
                             const char*               kernelName,
                             const void* /*kernelArgs*/)
@@ -299,9 +189,9 @@ inline void launchGpuKernel(cl_kernel                 kernel,
     {
         globalWorkSize[i] = config.gridSize[i] * config.blockSize[i];
     }
-    cl_int clError = clEnqueueNDRangeKernel(config.stream, kernel, workDimensions, globalWorkOffset,
-                                            globalWorkSize, config.blockSize, waitListSize,
-                                            waitList, timingEvent);
+    cl_int clError = clEnqueueNDRangeKernel(deviceStream.stream(), kernel, workDimensions,
+                                            globalWorkOffset, globalWorkSize, config.blockSize,
+                                            waitListSize, waitList, timingEvent);
     if (CL_SUCCESS != clError)
     {
         const std::string errorMessage = "GPU kernel (" + std::string(kernelName)
index 2f2d8827df08f62ad473669830f8a5ac9c4dc642..d9ab9e088d21526db95dd5064d68a1f1321df884 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index c2fc3426bb09a97d7727b1b60ae05fde192c038d..6e4a700930ed6a1ec225b06a7920a54c1a74a6d6 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # different files and strategies for the different GPU implementation
 # flavours.
 
-# Always compiled as plain C++
-file(GLOB SOURCES_FROM_CXX
-    clfftinitializer.cpp
-    hostallocator.cpp
-    )
-
-if(GMX_USE_CUDA)
-    # CUDA-only test
-    list(APPEND SOURCES_FROM_CXX
+gmx_add_unit_test(GpuUtilsUnitTests gpu_utils-test HARDWARE_DETECTION
+    CPP_SOURCE_FILES
+        # Tests of code
+        clfftinitializer.cpp
+        device_availability.cpp
+        device_stream_manager.cpp
+        hostallocator.cpp
         pinnedmemorychecker.cpp
-        )
-    # TODO Making a separate library is heavy handed, but nothing else
-    # seems to work. Also don't use a hyphen in its name, because nvcc
-    # can't cope with that.
-    #
-    # Perhaps FindCUDA's support for single compilation units will help?
-    if (NOT GMX_CLANG_CUDA)
-        # Work around FindCUDA that prevents using target_link_libraries()
-        # with keywords otherwise...
-        set(CUDA_LIBRARIES PRIVATE ${CUDA_LIBRARIES})
-        cuda_add_library(gpu_utilstest_cuda
-            devicetransfers.cu
-            )
-    else()
-        include(gmxClangCudaUtils)
-        gmx_compile_cuda_file_with_clang(devicetransfers.cu)
-        add_library(gpu_utilstest_cuda
-            devicetransfers.cu
-            )
-    endif()
+        typecasts.cpp
+        
+    GPU_CPP_SOURCE_FILES
+        device_buffer.cpp
 
-    target_link_libraries(gpu_utilstest_cuda PRIVATE ${GMX_EXTRA_LIBRARIES} libgromacs)
+    CUDA_CU_SOURCE_FILES
+        devicetransfers.cu
+        typecasts_runner.cu
 
-elseif(GMX_USE_OPENCL)
-    # Do normal compilation of OpenCL files
-    list(APPEND SOURCES_FROM_CXX
+    OPENCL_CPP_SOURCE_FILES
         devicetransfers_ocl.cpp
-        )
-else()
-    # Do normal compilation of files with null implementations
-    list(APPEND SOURCES_FROM_CXX
-        devicetransfers.cpp
-        )
-endif()
 
-gmx_add_unit_test(GpuUtilsUnitTests gpu_utils-test
-    # Infrastructure
-    gputest.cpp
-    # Tests of code
-    ${SOURCES_FROM_CXX}
-    )
+    SYCL_CPP_SOURCE_FILES
+        devicetransfers_sycl.cpp
 
-if(GMX_USE_CUDA)
-    target_link_libraries(gpu_utils-test PRIVATE gpu_utilstest_cuda)
-endif()
-if(GMX_USE_OPENCL)
-    target_link_libraries(gpu_utils-test PRIVATE ${OpenCL_LIBRARIES})
-endif()
+    NON_GPU_CPP_SOURCE_FILES
+        devicetransfers.cpp
+    )
diff --git a/src/gromacs/gpu_utils/tests/device_availability.cpp b/src/gromacs/gpu_utils/tests/device_availability.cpp
new file mode 100644 (file)
index 0000000..1807367
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Check if GPUs are available when they should be.
+ *
+ * This is to test that CI can detect and use GPUs, when they are available.
+ * Driver and CUDA mismatch can lead to the tests falling back to the CPU
+ * code path quietly, leaving the GPU path untested. This is designed to
+ * fail when GPUs should be available, indicated by setting the environment
+ * variable \c GMX_TEST_REQUIRED_NUMBER_OF_DEVICES to 1 or greater.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \author Andrey Alekseenko <al42and@gmail.com>
+ */
+#include "gmxpre.h"
+
+#include "config.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/hardware/device_management.h"
+#include "gromacs/utility/baseversion.h"
+
+#include "testutils/test_hardware_environment.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+static unsigned long int getRequiredNumberOfDevices()
+{
+    const char* required_num_of_devices_str = getenv("GMX_TEST_REQUIRED_NUMBER_OF_DEVICES");
+    if (required_num_of_devices_str == nullptr)
+    {
+        return 0;
+    }
+    else
+    {
+        errno = 0;
+        char*          strtol_end;
+        const long int required_num_of_devices =
+                std::strtol(required_num_of_devices_str, &strtol_end, 10);
+        GMX_RELEASE_ASSERT(errno == 0, "Invalid value of GMX_TEST_REQUIRED_NUMBER_OF_DEVICES.");
+        GMX_RELEASE_ASSERT(*strtol_end == '\0',
+                           "Trailing characters in GMX_TEST_REQUIRED_NUMBER_OF_DEVICES.");
+        GMX_RELEASE_ASSERT(required_num_of_devices >= 0,
+                           "GMX_TEST_REQUIRED_NUMBER_OF_DEVICES can not be negative.");
+        return required_num_of_devices;
+    }
+}
+
+TEST(DevicesAvailable, ShouldBeAbleToRunOnDevice)
+{
+    const unsigned long int required_num_of_devices = getRequiredNumberOfDevices();
+
+    if (required_num_of_devices != 0)
+    {
+        ASSERT_TRUE(GMX_GPU) << "GROMACS was compiled without GPU support, yet "
+                                "GMX_TEST_REQUIRED_NUMBER_OF_DEVICES is set.";
+
+        std::string platformString(getGpuImplementationString());
+
+        std::string errorMessage = "Can't perform device detection in " + platformString + ":\n";
+        std::string detectionErrorMessage;
+
+        // Test if the detection is working
+        ASSERT_TRUE(isDeviceDetectionEnabled())
+                << errorMessage << "GPU device detection is disabled by "
+                << "GMX_DISABLE_GPU_DETECTION environment variable.";
+        ASSERT_TRUE(isDeviceDetectionFunctional(&detectionErrorMessage))
+                << errorMessage
+                << "GPU device checks failed with the following message: " << detectionErrorMessage;
+
+        // This is to test that the GPU detection test environment is working
+        const std::vector<std::unique_ptr<TestDevice>>& testDeviceList =
+                getTestHardwareEnvironment()->getTestDeviceList();
+        ASSERT_FALSE(testDeviceList.empty())
+                << errorMessage << "The test environment has an empty list of capable devices.";
+        ASSERT_GE(testDeviceList.size(), required_num_of_devices)
+                << errorMessage << "Requested " << required_num_of_devices << " device(s), "
+                << "but only " << testDeviceList.size() << " detected in the test environment.";
+    }
+}
+
+} // namespace test
+} // namespace gmx
diff --git a/src/gromacs/gpu_utils/tests/device_buffer.cpp b/src/gromacs/gpu_utils/tests/device_buffer.cpp
new file mode 100644 (file)
index 0000000..3acfee0
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Tests for device buffer
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ */
+#include "gmxpre.h"
+
+#include "config.h"
+
+#if GMX_GPU
+#    include <numeric>
+
+#    include <gmock/gmock.h>
+#    include <gtest/gtest.h>
+
+#    include "gromacs/gpu_utils/device_context.h"
+#    include "gromacs/gpu_utils/device_stream.h"
+#    include "gromacs/gpu_utils/devicebuffer.h"
+
+#    include "testutils/test_hardware_environment.h"
+#    include "testutils/testasserts.h"
+
+namespace gmx
+{
+
+template<typename ValueType>
+BasicVector<ValueType>& operator++(BasicVector<ValueType>& in)
+{
+    in[XX]++;
+    in[YY]++;
+    in[ZZ]++;
+    return in;
+}
+
+template<typename ValueType>
+BasicVector<ValueType>& operator++(BasicVector<ValueType>& in, int /* n */)
+{
+    BasicVector<ValueType> temp = *in;
+    ++*in;
+    return temp;
+}
+
+template<typename ValueType>
+inline bool operator==(const BasicVector<ValueType>& lhs, const BasicVector<ValueType>& rhs)
+{
+    return lhs[XX] == rhs[XX] && lhs[YY] == rhs[YY] && lhs[ZZ] == rhs[ZZ];
+}
+
+namespace test
+{
+
+namespace
+{
+
+using testing::Eq;
+using testing::Pointwise;
+
+//! Test fixture (needed for typed tests)
+template<typename T>
+class DeviceBufferTest : public ::testing::Test
+{
+};
+
+using TypeParamList = testing::Types<short, int, float, double, gmx::RVec>;
+TYPED_TEST_CASE(DeviceBufferTest, TypeParamList);
+
+TYPED_TEST(DeviceBufferTest, CanAllocateAndFreeDeviceBuffer)
+{
+    for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
+    {
+        const DeviceContext& deviceContext = testDevice->deviceContext();
+        setActiveDevice(testDevice->deviceInfo());
+
+        DeviceBuffer<TypeParam> buffer;
+        int                     numValues = 123;
+        allocateDeviceBuffer(&buffer, numValues, deviceContext);
+        freeDeviceBuffer(&buffer);
+    }
+}
+
+TYPED_TEST(DeviceBufferTest, CanReallocateAndFreeDeviceBuffer)
+{
+    for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
+    {
+        const DeviceContext& deviceContext = testDevice->deviceContext();
+        setActiveDevice(testDevice->deviceInfo());
+
+        DeviceBuffer<TypeParam> buffer;
+        int                     currentNumValues    = 456;
+        int                     newNumValues        = 789;
+        int                     currentMaxNumValues = 0;
+        allocateDeviceBuffer(&buffer, currentNumValues, deviceContext);
+        reallocateDeviceBuffer(&buffer, newNumValues, &currentNumValues, &currentMaxNumValues, deviceContext);
+        freeDeviceBuffer(&buffer);
+    }
+}
+
+//! Initial value to fill the buffer of the scalar type
+template<typename T>
+const T c_initialValue = static_cast<T>(1);
+
+//! Initial value to fill the buffer of the vector type
+template<>
+const gmx::RVec c_initialValue<gmx::RVec> = { 1, -2, 3 };
+
+
+TYPED_TEST(DeviceBufferTest, CanCopyToAndFromDevice)
+{
+    for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
+    {
+        const DeviceContext& deviceContext = testDevice->deviceContext();
+        const DeviceStream&  deviceStream  = testDevice->deviceStream();
+        setActiveDevice(testDevice->deviceInfo());
+
+        DeviceBuffer<TypeParam> buffer;
+        int                     numValues = 123;
+        allocateDeviceBuffer(&buffer, numValues, deviceContext);
+        std::vector<TypeParam> valuesIn(numValues);
+        std::vector<TypeParam> valuesOut(numValues);
+
+        std::iota(valuesIn.begin(), valuesIn.end(), c_initialValue<TypeParam>);
+
+        copyToDeviceBuffer(&buffer, valuesIn.data(), 0, numValues, deviceStream,
+                           GpuApiCallBehavior::Sync, nullptr);
+        copyFromDeviceBuffer(valuesOut.data(), &buffer, 0, numValues, deviceStream,
+                             GpuApiCallBehavior::Sync, nullptr);
+        EXPECT_THAT(valuesOut, Pointwise(Eq(), valuesIn)) << "Changed after H2D and D2H copy.";
+        freeDeviceBuffer(&buffer);
+    }
+}
+
+TYPED_TEST(DeviceBufferTest, CanCopyToAndFromDeviceWithOffset)
+{
+    for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
+    {
+        const DeviceContext& deviceContext = testDevice->deviceContext();
+        const DeviceStream&  deviceStream  = testDevice->deviceStream();
+        setActiveDevice(testDevice->deviceInfo());
+
+        DeviceBuffer<TypeParam> buffer;
+        int                     numValues = 123;
+        allocateDeviceBuffer(&buffer, 2 * numValues, deviceContext);
+        std::vector<TypeParam> valuesIn(numValues);
+        std::vector<TypeParam> valuesOut(2 * numValues);
+
+        std::iota(valuesIn.begin(), valuesIn.end(), c_initialValue<TypeParam>);
+
+        // Fill the buffer with two copies of valuesIn, one after the other.
+        copyToDeviceBuffer(&buffer, valuesIn.data(), 0, numValues, deviceStream,
+                           GpuApiCallBehavior::Sync, nullptr);
+        copyToDeviceBuffer(&buffer, valuesIn.data(), numValues, numValues, deviceStream,
+                           GpuApiCallBehavior::Sync, nullptr);
+        // Do the same copying on the CPU, so we can test it works
+        // correctly.
+        valuesIn.insert(valuesIn.end(), valuesIn.begin(), valuesIn.end());
+
+        copyFromDeviceBuffer(valuesOut.data(), &buffer, 0, 2 * numValues, deviceStream,
+                             GpuApiCallBehavior::Sync, nullptr);
+        EXPECT_THAT(valuesOut, Pointwise(Eq(), valuesIn)) << "Changed after H2D and D2H copy.";
+
+        SCOPED_TRACE("Checking the copy respects the output range");
+
+        // Remove the first element, and push another copy of the last
+        // element, so we can check that a copy of all of the data
+        // skipping the first element correctly over-writes exactly
+        // all but one of the old values.
+        valuesIn.erase(valuesIn.begin());
+        valuesIn.push_back(valuesIn.back());
+        copyFromDeviceBuffer(valuesOut.data(), &buffer, 1, 2 * numValues - 1, deviceStream,
+                             GpuApiCallBehavior::Sync, nullptr);
+        EXPECT_THAT(valuesOut, Pointwise(Eq(), valuesIn)) << "Changed after H2D and D2H copy.";
+    }
+}
+
+
+} // namespace
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_GPU
diff --git a/src/gromacs/gpu_utils/tests/device_stream_manager.cpp b/src/gromacs/gpu_utils/tests/device_stream_manager.cpp
new file mode 100644 (file)
index 0000000..d01aff2
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Tests GPU stream manager
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ */
+#include "gmxpre.h"
+
+#include "gromacs/gpu_utils/device_stream_manager.h"
+
+#include "config.h"
+
+#include <initializer_list>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/hardware/device_management.h"
+#include "gromacs/mdtypes/simulation_workload.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+#include "testutils/test_hardware_environment.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+namespace
+{
+
+//! GPU device stream names for outputs.
+const EnumerationArray<DeviceStreamType, std::string> c_deviceStreamNames = {
+    { "non-bonded local", "non-bonded non-local", "PME", "PME-PP transfer", "update" }
+};
+
+/*! \brief Non-GPU builds return nullptr instead of streams,
+ * so we have to expect that in such build configurations. */
+constexpr bool c_canExpectValidStreams = (GMX_GPU != 0);
+
+//! Helper function to implement readable testing
+void expectValidStreams(DeviceStreamManager* manager, std::initializer_list<DeviceStreamType> types)
+{
+    if (c_canExpectValidStreams)
+    {
+        for (const DeviceStreamType type : types)
+        {
+            SCOPED_TRACE("Testing " + c_deviceStreamNames[type] + " stream.");
+            EXPECT_TRUE(manager->streamIsValid(type));
+        }
+    }
+}
+//! Helper function to implement readable testing
+void expectInvalidStreams(DeviceStreamManager* manager, std::initializer_list<DeviceStreamType> types)
+{
+    for (const DeviceStreamType type : types)
+    {
+        SCOPED_TRACE("Testing " + c_deviceStreamNames[type] + " stream.");
+        EXPECT_FALSE(manager->streamIsValid(type));
+    }
+}
+
+//! Test fixture
+class DeviceStreamManagerTest : public ::testing::Test
+{
+public:
+};
+
+TEST_F(DeviceStreamManagerTest, CorrectStreamsAreReturnedOnNonbondedDevice)
+{
+    // It would be nice to test that the priority is high when it can
+    // be, but that requires calling the same API calls we're testing
+    // that we've called, so it is not very useful.
+    const bool useTiming = false;
+
+    const auto& testDeviceList = getTestHardwareEnvironment()->getTestDeviceList();
+    for (const auto& testDevice : testDeviceList)
+    {
+        const DeviceInformation& deviceInfo = testDevice->deviceInfo();
+        setActiveDevice(deviceInfo);
+
+        {
+            SCOPED_TRACE("No DD, no PME rank, no GPU update");
+            SimulationWorkload simulationWork;
+            simulationWork.useGpuPme                      = false;
+            simulationWork.useGpuPmePpCommunication       = false;
+            simulationWork.useGpuUpdate                   = false;
+            bool                havePpDomainDecomposition = false;
+            DeviceStreamManager manager(deviceInfo, havePpDomainDecomposition, simulationWork, useTiming);
+
+            expectValidStreams(&manager, { DeviceStreamType::NonBondedLocal });
+            expectInvalidStreams(&manager, { DeviceStreamType::NonBondedNonLocal,
+                                             DeviceStreamType::Pme, DeviceStreamType::PmePpTransfer,
+                                             DeviceStreamType::UpdateAndConstraints });
+        }
+
+        {
+            SCOPED_TRACE("With DD, no PME rank, no GPU update");
+            SimulationWorkload simulationWork;
+            simulationWork.useGpuPme                      = false;
+            simulationWork.useGpuPmePpCommunication       = false;
+            simulationWork.useGpuUpdate                   = false;
+            bool                havePpDomainDecomposition = true;
+            DeviceStreamManager manager(deviceInfo, havePpDomainDecomposition, simulationWork, useTiming);
+
+            expectValidStreams(&manager, { DeviceStreamType::NonBondedLocal,
+                                           DeviceStreamType::NonBondedNonLocal });
+            expectInvalidStreams(&manager, { DeviceStreamType::Pme, DeviceStreamType::PmePpTransfer,
+                                             DeviceStreamType::UpdateAndConstraints });
+        }
+
+        {
+            SCOPED_TRACE("No DD, with PME rank, no GPU update");
+            SimulationWorkload simulationWork;
+            simulationWork.useGpuPme                      = true;
+            simulationWork.useGpuPmePpCommunication       = true;
+            simulationWork.useGpuUpdate                   = false;
+            bool                havePpDomainDecomposition = false;
+            DeviceStreamManager manager(deviceInfo, havePpDomainDecomposition, simulationWork, useTiming);
+
+            expectValidStreams(&manager, { DeviceStreamType::Pme, DeviceStreamType::NonBondedLocal,
+                                           DeviceStreamType::PmePpTransfer,
+                                           DeviceStreamType::UpdateAndConstraints });
+            expectInvalidStreams(&manager, { DeviceStreamType::NonBondedNonLocal });
+        }
+
+        {
+            SCOPED_TRACE("With DD, with PME rank, no GPU update");
+            SimulationWorkload simulationWork;
+            simulationWork.useGpuPme                      = true;
+            simulationWork.useGpuPmePpCommunication       = true;
+            simulationWork.useGpuUpdate                   = false;
+            bool                havePpDomainDecomposition = true;
+            DeviceStreamManager manager(deviceInfo, havePpDomainDecomposition, simulationWork, useTiming);
+
+            expectValidStreams(&manager, { DeviceStreamType::Pme, DeviceStreamType::NonBondedLocal,
+                                           DeviceStreamType::NonBondedNonLocal, DeviceStreamType::PmePpTransfer,
+                                           DeviceStreamType::UpdateAndConstraints });
+        }
+
+        {
+            SCOPED_TRACE("No DD, no PME rank, with GPU update");
+            SimulationWorkload simulationWork;
+            simulationWork.useGpuPme                      = false;
+            simulationWork.useGpuPmePpCommunication       = false;
+            simulationWork.useGpuUpdate                   = true;
+            bool                havePpDomainDecomposition = false;
+            DeviceStreamManager manager(deviceInfo, havePpDomainDecomposition, simulationWork, useTiming);
+
+            expectValidStreams(&manager, { DeviceStreamType::NonBondedLocal,
+                                           DeviceStreamType::UpdateAndConstraints });
+            expectInvalidStreams(&manager, { DeviceStreamType::NonBondedNonLocal,
+                                             DeviceStreamType::Pme, DeviceStreamType::PmePpTransfer });
+        }
+
+        {
+            SCOPED_TRACE("With DD, no PME rank, with GPU update");
+            SimulationWorkload simulationWork;
+            simulationWork.useGpuPme                      = false;
+            simulationWork.useGpuPmePpCommunication       = false;
+            simulationWork.useGpuUpdate                   = true;
+            bool                havePpDomainDecomposition = true;
+            DeviceStreamManager manager(deviceInfo, havePpDomainDecomposition, simulationWork, useTiming);
+
+            expectValidStreams(&manager, { DeviceStreamType::NonBondedLocal, DeviceStreamType::NonBondedNonLocal,
+                                           DeviceStreamType::UpdateAndConstraints });
+            expectInvalidStreams(&manager, { DeviceStreamType::Pme, DeviceStreamType::PmePpTransfer });
+        }
+
+        {
+            SCOPED_TRACE("No DD, with PME rank, with GPU update");
+            SimulationWorkload simulationWork;
+            simulationWork.useGpuPme                      = true;
+            simulationWork.useGpuPmePpCommunication       = true;
+            simulationWork.useGpuUpdate                   = true;
+            bool                havePpDomainDecomposition = false;
+            DeviceStreamManager manager(deviceInfo, havePpDomainDecomposition, simulationWork, useTiming);
+
+            expectValidStreams(&manager, { DeviceStreamType::Pme, DeviceStreamType::NonBondedLocal,
+                                           DeviceStreamType::PmePpTransfer,
+                                           DeviceStreamType::UpdateAndConstraints });
+            expectInvalidStreams(&manager, { DeviceStreamType::NonBondedNonLocal });
+        }
+
+        {
+            SCOPED_TRACE("With DD, with PME rank, with GPU update");
+            SimulationWorkload simulationWork;
+            simulationWork.useGpuPme                      = true;
+            simulationWork.useGpuPmePpCommunication       = true;
+            simulationWork.useGpuUpdate                   = true;
+            bool                havePpDomainDecomposition = true;
+            DeviceStreamManager manager(deviceInfo, havePpDomainDecomposition, simulationWork, useTiming);
+
+            expectValidStreams(&manager, { DeviceStreamType::Pme, DeviceStreamType::NonBondedLocal,
+                                           DeviceStreamType::NonBondedNonLocal, DeviceStreamType::PmePpTransfer,
+                                           DeviceStreamType::UpdateAndConstraints });
+        }
+    }
+}
+
+} // namespace
+} // namespace test
+} // namespace gmx
index 5ea7aadbaaa20fe3cb7cf739f46c747310eb5c0c..18a32ee6f87f02423e6ee42898e1d0249f5f0b3a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <algorithm>
 
 #include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
 
 namespace gmx
 {
 
-void doDeviceTransfers(const gmx_gpu_info_t& /*gpuInfo*/, ArrayRef<const char> input, ArrayRef<char> output)
+void doDeviceTransfers(const DeviceInformation& /* deviceInfo */,
+                       ArrayRef<const char> input,
+                       ArrayRef<char>       output)
 {
     GMX_RELEASE_ASSERT(input.size() == output.size(), "Input and output must have matching size");
     // We can't have any valid GPUs for this build configuration.
index e3a56be9c3a5b2eb4cc2ed24cb7168a64aa09bde..4e7e14779d16ddfa991b6fbb2f39c9098707c9cf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,8 +49,7 @@
 #include "devicetransfers.h"
 
 #include "gromacs/gpu_utils/cudautils.cuh"
-#include "gromacs/gpu_utils/gpu_utils.h"
-#include "gromacs/hardware/gpu_hw_info.h"
+#include "gromacs/hardware/device_information.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
@@ -76,23 +75,16 @@ static void throwUponFailure(cudaError_t status, const char* message)
 
 } // namespace
 
-void doDeviceTransfers(const gmx_gpu_info_t& gpuInfo, ArrayRef<const char> input, ArrayRef<char> output)
+void doDeviceTransfers(const DeviceInformation& deviceInfo, ArrayRef<const char> input, ArrayRef<char> output)
 {
     GMX_RELEASE_ASSERT(input.size() == output.size(), "Input and output must have matching size");
-    const auto compatibleGpus = getCompatibleGpus(gpuInfo);
-    if (compatibleGpus.empty())
-    {
-        std::copy(input.begin(), input.end(), output.begin());
-        return;
-    }
     cudaError_t status;
 
-    const auto* device = getDeviceInfo(gpuInfo, compatibleGpus[0]);
-    int         oldDeviceId;
+    int oldDeviceId;
 
     status = cudaGetDevice(&oldDeviceId);
     throwUponFailure(status, "getting old device id");
-    status = cudaSetDevice(device->id);
+    status = cudaSetDevice(deviceInfo.id);
     throwUponFailure(status, "setting device id to the first compatible GPU");
 
     void* devicePointer;
index 730f2a1ec6fea28ec137684de107889383c8270a..1315741de580740302c70933e802fff4ad5088df 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_GPU_UTILS_TESTS_DEVICETRANSFERS_H
 #define GMX_GPU_UTILS_TESTS_DEVICETRANSFERS_H
 
-#include "gromacs/utility/arrayref.h"
-
-struct gmx_gpu_info_t;
+struct DeviceInformation;
 
 namespace gmx
 {
+template<typename>
+class ArrayRef;
 
 /*! \brief Helper function for GPU test code to be platform agnostic.
  *
@@ -65,7 +65,7 @@ namespace gmx
  * do a simple host-side buffer copy instead.
  *
  * \throws InternalError  Upon any GPU API error condition. */
-void doDeviceTransfers(const gmx_gpu_info_t& gpuInfo, ArrayRef<const char> input, ArrayRef<char> output);
+void doDeviceTransfers(const DeviceInformation& deviceInfo, ArrayRef<const char> input, ArrayRef<char> output);
 
 } // namespace gmx
 
index ed3808595457786544f84f8d6fc0d2c1ad53352a..1888a35729ee197f8ad9e18c18246a3079a4f55c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -41,9 +41,9 @@
 #include "gmxpre.h"
 
 #include "gromacs/gpu_utils/gmxopencl.h"
-#include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/gpu_utils/oclutils.h"
-#include "gromacs/hardware/gpu_hw_info.h"
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
@@ -71,24 +71,17 @@ void throwUponFailure(cl_int status, const char* message)
 
 } // namespace
 
-void doDeviceTransfers(const gmx_gpu_info_t& gpuInfo, ArrayRef<const char> input, ArrayRef<char> output)
+void doDeviceTransfers(const DeviceInformation& deviceInfo, ArrayRef<const char> input, ArrayRef<char> output)
 {
     GMX_RELEASE_ASSERT(input.size() == output.size(), "Input and output must have matching size");
-    const auto compatibleGpus = getCompatibleGpus(gpuInfo);
-    if (compatibleGpus.empty())
-    {
-        std::copy(input.begin(), input.end(), output.begin());
-        return;
-    }
+
     cl_int status;
 
-    const auto*           device       = getDeviceInfo(gpuInfo, compatibleGpus[0]);
     cl_context_properties properties[] = {
-        CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>(device->ocl_gpu_id.ocl_platform_id), 0
+        CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>(deviceInfo.oclPlatformId), 0
     };
-    // Give uncrustify more space
 
-    auto deviceId = device->ocl_gpu_id.ocl_device_id;
+    auto deviceId = deviceInfo.oclDeviceId;
     auto context  = clCreateContext(properties, 1, &deviceId, nullptr, nullptr, &status);
     throwUponFailure(status, "creating context");
     auto commandQueue = clCreateCommandQueue(context, deviceId, 0, &status);
diff --git a/src/gromacs/gpu_utils/tests/devicetransfers_sycl.cpp b/src/gromacs/gpu_utils/tests/devicetransfers_sycl.cpp
new file mode 100644 (file)
index 0000000..bb6d001
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Defines helper functionality for device transfers for tests
+ * for GPU host allocator.
+ *
+ * \author Andrey Alekseenko <al42and@gmail.com>
+ */
+#include "gmxpre.h"
+
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "devicetransfers.h"
+
+namespace gmx
+{
+
+void doDeviceTransfers(const DeviceInformation& deviceInfo, ArrayRef<const char> input, ArrayRef<char> output)
+{
+    GMX_RELEASE_ASSERT(input.size() == output.size(), "Input and output must have matching size");
+
+    try
+    {
+        cl::sycl::queue syclQueue(deviceInfo.syclDevice);
+
+        cl::sycl::property_list syclBufferProperties{ cl::sycl::property::buffer::context_bound(
+                syclQueue.get_context()) };
+
+        cl::sycl::buffer<char> syclBuffer(::sycl::range<1>(input.size()), syclBufferProperties);
+
+        syclQueue
+                .submit([&](cl::sycl::handler& cgh) {
+                    auto accessor = syclBuffer.get_access<cl::sycl::access_mode::discard_write>(cgh);
+                    cgh.copy(input.data(), accessor);
+                })
+                .wait_and_throw();
+
+        syclQueue
+                .submit([&](cl::sycl::handler& cgh) {
+                    auto accessor = syclBuffer.get_access<cl::sycl::access_mode::write>(cgh);
+                    cgh.copy(accessor, output.data());
+                })
+                .wait_and_throw();
+    }
+    catch (cl::sycl::exception& e)
+    {
+        GMX_THROW(InternalError(
+                formatString("Failure while checking data transfer, error was %s", e.what())));
+    }
+}
+
+} // namespace gmx
index 4b4aeb36be64514148fc8a67c5e68f544c7c63e3..173ca15ee300ccba1c15093351769c1aa55c289e 100644 (file)
 #include <gtest/gtest.h>
 
 #include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/real.h"
 
 #include "gromacs/math/tests/testarrayrefs.h"
+#include "testutils/test_hardware_environment.h"
 
 #include "devicetransfers.h"
-#include "gputest.h"
 
 namespace gmx
 {
@@ -68,7 +69,7 @@ namespace test
 /*! \internal \brief Typed test fixture for infrastructure for
  * host-side memory used for GPU transfers. */
 template<typename T>
-class HostMemoryTest : public test::GpuTest
+class HostMemoryTest : public ::testing::Test
 {
 public:
     //! Convenience type
@@ -97,7 +98,7 @@ ArrayRef<char> charArrayRefFromArray(T* data, size_t size)
 
 //! Does a device transfer of \c input to the device in \c gpuInfo, and back to \c output.
 template<typename T>
-void runTest(const gmx_gpu_info_t& gpuInfo, ArrayRef<T> input, ArrayRef<T> output)
+void runTest(const DeviceInformation& deviceInfo, ArrayRef<T> input, ArrayRef<T> output)
 {
     // Convert the views of input and output to flat non-const chars,
     // so that there's no templating when we call doDeviceTransfers.
@@ -105,7 +106,8 @@ void runTest(const gmx_gpu_info_t& gpuInfo, ArrayRef<T> input, ArrayRef<T> outpu
     auto outputRef = charArrayRefFromArray(output.data(), output.size());
 
     ASSERT_EQ(inputRef.size(), outputRef.size());
-    doDeviceTransfers(gpuInfo, inputRef, outputRef);
+
+    doDeviceTransfers(deviceInfo, inputRef, outputRef);
     compareViews(input, output);
 }
 
@@ -198,12 +200,16 @@ TYPED_TEST(HostAllocatorTestCopyable, VectorsWithDefaultHostAllocatorAlwaysWorks
 
 TYPED_TEST(HostAllocatorTestCopyable, TransfersWithoutPinningWork)
 {
-    typename TestFixture::VectorType input;
-    fillInput(&input, 1);
-    typename TestFixture::VectorType output;
-    output.resizeWithPadding(input.size());
+    for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
+    {
+        setActiveDevice(testDevice->deviceInfo());
+        typename TestFixture::VectorType input;
+        fillInput(&input, 1);
+        typename TestFixture::VectorType output;
+        output.resizeWithPadding(input.size());
 
-    runTest(*this->gpuInfo_, makeArrayRef(input), makeArrayRef(output));
+        runTest(testDevice->deviceInfo(), makeArrayRef(input), makeArrayRef(output));
+    }
 }
 
 TYPED_TEST(HostAllocatorTestCopyable, FillInputAlsoWorksAfterCallingReserve)
@@ -286,25 +292,24 @@ TYPED_TEST(HostAllocatorTestNoMem, Comparison)
     EXPECT_NE(AllocatorType{}, AllocatorType{ PinningPolicy::PinnedIfSupported });
 }
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 
 // Policy suitable for pinning is only supported for a CUDA build
 
 TYPED_TEST(HostAllocatorTestCopyable, TransfersWithPinningWorkWithCuda)
 {
-    if (!this->haveValidGpus())
+    for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
     {
-        return;
+        setActiveDevice(testDevice->deviceInfo());
+        typename TestFixture::VectorType input;
+        changePinningPolicy(&input, PinningPolicy::PinnedIfSupported);
+        fillInput(&input, 1);
+        typename TestFixture::VectorType output;
+        changePinningPolicy(&output, PinningPolicy::PinnedIfSupported);
+        output.resizeWithPadding(input.size());
+
+        runTest(testDevice->deviceInfo(), makeArrayRef(input), makeArrayRef(output));
     }
-
-    typename TestFixture::VectorType input;
-    changePinningPolicy(&input, PinningPolicy::PinnedIfSupported);
-    fillInput(&input, 1);
-    typename TestFixture::VectorType output;
-    changePinningPolicy(&output, PinningPolicy::PinnedIfSupported);
-    output.resizeWithPadding(input.size());
-
-    runTest(*this->gpuInfo_, makeArrayRef(input), makeArrayRef(output));
 }
 
 //! Helper function for wrapping a call to isHostMemoryPinned.
@@ -317,40 +322,31 @@ bool isPinned(const VectorType& v)
 
 TYPED_TEST(HostAllocatorTestCopyable, ManualPinningOperationsWorkWithCuda)
 {
-    if (!this->haveValidGpus())
+    for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
     {
-        return;
+        setActiveDevice(testDevice->deviceInfo());
+        typename TestFixture::VectorType input;
+        changePinningPolicy(&input, PinningPolicy::PinnedIfSupported);
+        EXPECT_TRUE(input.get_allocator().pinningPolicy() == PinningPolicy::PinnedIfSupported);
+        EXPECT_TRUE(input.empty());
+        fillInput(&input, 1);
+        // realloc and copy).
+        auto oldInputData = input.data();
+        changePinningPolicy(&input, PinningPolicy::CannotBePinned);
+        EXPECT_FALSE(isPinned(input));
+        // These cannot be equal as both had to be allocated at the same
+        // time for the contents to be able to be copied.
+        EXPECT_NE(oldInputData, input.data());
+
+        // Switching policy to PinnedIfSupported must pin the buffer (via
+        // realloc and copy).
+        oldInputData = input.data();
+        changePinningPolicy(&input, PinningPolicy::PinnedIfSupported);
+        EXPECT_TRUE(isPinned(input));
+        // These cannot be equal as both had to be allocated at the same
+        // time for the contents to be able to be copied.
+        EXPECT_NE(oldInputData, input.data());
     }
-
-    typename TestFixture::VectorType input;
-    changePinningPolicy(&input, PinningPolicy::PinnedIfSupported);
-    EXPECT_TRUE(input.get_allocator().pinningPolicy() == PinningPolicy::PinnedIfSupported);
-    EXPECT_EQ(0, input.size());
-    EXPECT_EQ(0, input.paddedSize());
-    EXPECT_TRUE(input.empty());
-    EXPECT_FALSE(isPinned(input)) << "should not be pinned before allocation";
-
-    // Fill some contents, which will be pinned because of the policy.
-    fillInput(&input, 1);
-    EXPECT_TRUE(isPinned(input)) << "should be pinned after allocation";
-
-    // Switching policy to CannotBePinned must unpin the buffer (via
-    // realloc and copy).
-    auto oldInputData = input.data();
-    changePinningPolicy(&input, PinningPolicy::CannotBePinned);
-    EXPECT_FALSE(isPinned(input)) << "should not be pinned after changing policy to CannotBePinned";
-    // These cannot be equal as both had to be allocated at the same
-    // time for the contents to be able to be copied.
-    EXPECT_NE(oldInputData, input.data());
-
-    // Switching policy to PinnedIfSupported must pin the buffer (via
-    // realloc and copy).
-    oldInputData = input.data();
-    changePinningPolicy(&input, PinningPolicy::PinnedIfSupported);
-    EXPECT_TRUE(isPinned(input)) << "should be pinned after changing policy to PinnedIfSupported";
-    // These cannot be equal as both had to be allocated at the same
-    // time for the contents to be able to be copied.
-    EXPECT_NE(oldInputData, input.data());
 }
 
 #endif
index 305354943a68bec72958fcdcdc6c658f24fe016e..b4493e961e460bbe5da26aef9190a53abcb20a0a 100644 (file)
  */
 #include "gmxpre.h"
 
-#include <vector>
+#include "config.h"
 
-#include <gtest/gtest.h>
+#if GMX_GPU_CUDA
 
-#include "gromacs/gpu_utils/gpu_utils.h"
-#include "gromacs/gpu_utils/hostallocator.h"
-#include "gromacs/gpu_utils/pmalloc_cuda.h"
-#include "gromacs/utility/real.h"
-#include "gromacs/utility/smalloc.h"
+#    include <vector>
 
-#include "gputest.h"
+#    include <gtest/gtest.h>
+
+#    include "gromacs/gpu_utils/gpu_utils.h"
+#    include "gromacs/gpu_utils/hostallocator.h"
+#    include "gromacs/gpu_utils/pmalloc_cuda.h"
+#    include "gromacs/utility/real.h"
+#    include "gromacs/utility/smalloc.h"
+
+#    include "testutils/test_hardware_environment.h"
 
 namespace gmx
 {
@@ -62,85 +66,72 @@ namespace
 {
 
 //! Test fixture
-using PinnedMemoryCheckerTest = GpuTest;
+using PinnedMemoryCheckerTest = ::testing::Test;
 
 TEST_F(PinnedMemoryCheckerTest, DefaultContainerIsRecognized)
 {
-    if (!haveValidGpus())
-    {
-        return;
-    }
-
-    std::vector<real> dummy(3, 1.5);
-    EXPECT_FALSE(isHostMemoryPinned(dummy.data()));
-}
-
-TEST_F(PinnedMemoryCheckerTest, NonpinnedContainerIsRecognized)
-{
-    if (!haveValidGpus())
+    /* Note that this tests can be executed even on hosts with no GPUs.
+     * However, the checks for pending CUDA errors run cudaGetLastError(...),
+     * which itself returns cudaErrorNoDevice in this case. This causes the
+     * tests to crash. The conditionals in these tests should be removed
+     * when a proper work-around for this problem is in place.
+     */
+    if (getTestHardwareEnvironment()->hasCompatibleDevices())
     {
-        return;
+        HostVector<real> dummy(3, 1.5);
+        changePinningPolicy(&dummy, PinningPolicy::CannotBePinned);
+        EXPECT_FALSE(isHostMemoryPinned(dummy.data()));
     }
-
-    HostVector<real> dummy(3, 1.5);
-    changePinningPolicy(&dummy, PinningPolicy::CannotBePinned);
-    EXPECT_FALSE(isHostMemoryPinned(dummy.data()));
 }
 
 TEST_F(PinnedMemoryCheckerTest, PinnedContainerIsRecognized)
 {
-    if (!haveValidGpus())
+    if (getTestHardwareEnvironment()->hasCompatibleDevices())
     {
-        return;
+        HostVector<real> dummy(3, 1.5);
+        changePinningPolicy(&dummy, PinningPolicy::PinnedIfSupported);
+        EXPECT_TRUE(isHostMemoryPinned(dummy.data()));
     }
-
-    HostVector<real> dummy(3, 1.5);
-    changePinningPolicy(&dummy, PinningPolicy::PinnedIfSupported);
-    EXPECT_TRUE(isHostMemoryPinned(dummy.data()));
 }
 
 TEST_F(PinnedMemoryCheckerTest, PinningChangesAreRecognized)
 {
-    if (!haveValidGpus())
+    if (getTestHardwareEnvironment()->hasCompatibleDevices())
     {
-        return;
+        HostVector<real> dummy(3, 1.5);
+        changePinningPolicy(&dummy, PinningPolicy::PinnedIfSupported);
+        EXPECT_TRUE(isHostMemoryPinned(dummy.data())) << "memory starts pinned";
+        changePinningPolicy(&dummy, PinningPolicy::CannotBePinned);
+        EXPECT_FALSE(isHostMemoryPinned(dummy.data())) << "memory is now unpinned";
+        changePinningPolicy(&dummy, PinningPolicy::PinnedIfSupported);
+        EXPECT_TRUE(isHostMemoryPinned(dummy.data())) << "memory is pinned again";
     }
-
-    HostVector<real> dummy(3, 1.5);
-    changePinningPolicy(&dummy, PinningPolicy::PinnedIfSupported);
-    EXPECT_TRUE(isHostMemoryPinned(dummy.data())) << "memory starts pinned";
-    changePinningPolicy(&dummy, PinningPolicy::CannotBePinned);
-    EXPECT_FALSE(isHostMemoryPinned(dummy.data())) << "memory is now unpinned";
-    changePinningPolicy(&dummy, PinningPolicy::PinnedIfSupported);
-    EXPECT_TRUE(isHostMemoryPinned(dummy.data())) << "memory is pinned again";
 }
 
 TEST_F(PinnedMemoryCheckerTest, DefaultCBufferIsRecognized)
 {
-    if (!haveValidGpus())
+    if (getTestHardwareEnvironment()->hasCompatibleDevices())
     {
-        return;
+        real* dummy;
+        snew(dummy, 3);
+        EXPECT_FALSE(isHostMemoryPinned(dummy));
+        sfree(dummy);
     }
-
-    real* dummy;
-    snew(dummy, 3);
-    EXPECT_FALSE(isHostMemoryPinned(dummy));
-    sfree(dummy);
 }
 
 TEST_F(PinnedMemoryCheckerTest, PinnedCBufferIsRecognized)
 {
-    if (!haveValidGpus())
+    if (getTestHardwareEnvironment()->hasCompatibleDevices())
     {
-        return;
+        real* dummy = nullptr;
+        pmalloc(reinterpret_cast<void**>(&dummy), 3 * sizeof(real));
+        EXPECT_TRUE(isHostMemoryPinned(dummy));
+        pfree(dummy);
     }
-
-    real* dummy = nullptr;
-    pmalloc(reinterpret_cast<void**>(&dummy), 3 * sizeof(real));
-    EXPECT_TRUE(isHostMemoryPinned(dummy));
-    pfree(dummy);
 }
 
 } // namespace
 } // namespace test
 } // namespace gmx
+
+#endif // GMX_GPU_CUDA
diff --git a/src/gromacs/gpu_utils/tests/typecasts.cpp b/src/gromacs/gpu_utils/tests/typecasts.cpp
new file mode 100644 (file)
index 0000000..a27e243
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for CUDA float3 type layout.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include "gmxpre.h"
+
+#include "config.h"
+
+#if GMX_GPU_CUDA
+
+#    include "gromacs/gpu_utils/gputraits.h"
+#    include "gromacs/hardware/device_management.h"
+#    include "gromacs/utility/exceptions.h"
+
+#    include "testutils/test_hardware_environment.h"
+#    include "testutils/testasserts.h"
+#    include "testutils/testmatchers.h"
+
+#    include "typecasts_runner.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+//! Test data in RVec format
+static const std::vector<RVec> rVecInput = { { 1.0, 2.0, 3.0 }, { 4.0, 5.0, 6.0 } };
+
+TEST(GpuDataTypesCompatibilityTest, RVecAndFloat3Host)
+{
+    std::vector<RVec> rVecOutput(rVecInput.size());
+    convertRVecToFloat3OnHost(rVecOutput, rVecInput);
+    EXPECT_THAT(rVecInput, testing::Pointwise(RVecEq(ulpTolerance(0)), rVecOutput));
+}
+
+TEST(GpuDataTypesCompatibilityTest, RVecAndFloat3Device)
+{
+    for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
+    {
+        setActiveDevice(testDevice->deviceInfo());
+        std::vector<RVec> rVecOutput(rVecInput.size());
+        convertRVecToFloat3OnDevice(rVecOutput, rVecInput, testDevice.get());
+        EXPECT_THAT(rVecInput, testing::Pointwise(RVecEq(ulpTolerance(0)), rVecOutput));
+    }
+}
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_GPU_CUDA
diff --git a/src/gromacs/gpu_utils/tests/typecasts_runner.cpp b/src/gromacs/gpu_utils/tests/typecasts_runner.cpp
new file mode 100644 (file)
index 0000000..023f14a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Stub for runners for tests types compatibility.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include "gmxpre.h"
+
+#include "typecasts_runner.h"
+
+#include "config.h"
+
+#include <vector>
+
+#include "testutils/testasserts.h"
+
+#if !GMX_GPU_CUDA
+
+namespace gmx
+{
+
+namespace test
+{
+
+void convertRVecToFloat3OnHost(std::vector<gmx::RVec>& /* rVecOutput */,
+                               const std::vector<gmx::RVec>& /* rVecInput */)
+{
+    FAIL() << "Can't test float3 and RVec compatibility without CUDA.";
+}
+
+void convertRVecToFloat3OnDevice(std::vector<gmx::RVec>& /* rVecOutput */,
+                                 const std::vector<gmx::RVec>& /* rVecInput */,
+                                 const TestDevice* /* testDevice */)
+{
+    FAIL() << "Can't test float3 and RVec compatibility without CUDA.";
+}
+
+} // namespace test
+} // namespace gmx
+
+#endif // !GMX_GPU_CUDA
diff --git a/src/gromacs/gpu_utils/tests/typecasts_runner.cu b/src/gromacs/gpu_utils/tests/typecasts_runner.cu
new file mode 100644 (file)
index 0000000..7d88601
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Runners for tests of CUDA types compatibility.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#include "gmxpre.h"
+
+#include "typecasts_runner.h"
+
+#include <vector>
+
+#include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/devicebuffer.h"
+#include "gromacs/gpu_utils/typecasts.cuh"
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+/* \brief Perform a component-wise conversion of the float3 vector back to RVec format.
+ *
+ * This is needed to pass the data back to the CPU testing code for comparison with the initial input.
+ *
+ * \param[out] rVecOutput    Output data in RVec format for the output.
+ * \param[in]  float3Output  Output data in float3 format.
+ * \param[in]  numElements   Size of the data buffers.
+ */
+void inline saveFloat3InRVecFormat(std::vector<gmx::RVec>& rVecOutput, const float3* float3Output, int numElements)
+{
+    for (int i = 0; i < numElements; i++)
+    {
+        rVecOutput[i][XX] = float3Output[i].x;
+        rVecOutput[i][YY] = float3Output[i].y;
+        rVecOutput[i][ZZ] = float3Output[i].z;
+    }
+}
+
+void convertRVecToFloat3OnHost(std::vector<gmx::RVec>& rVecOutput, const std::vector<gmx::RVec>& rVecInput)
+{
+    const int numElements = rVecInput.size();
+
+    float3* dataFloat3 = asFloat3(const_cast<RVec*>(rVecInput.data()));
+
+    saveFloat3InRVecFormat(rVecOutput, dataFloat3, numElements);
+}
+
+//! Number of CUDA threads in a block.
+constexpr static int c_threadsPerBlock = 256;
+
+/*! \brief GPU kernel to perform type conversion on the device.
+ *
+ * \param[out] gm_float3Output Buffer to write the output into.
+ * \param[in]  gm_rVecInput    Input data in RVec format.
+ * \param[in]  size            Size of the data buffers.
+ *
+ */
+static __global__ void convertRVecToFloat3OnDevice_kernel(DeviceBuffer<float3> gm_float3Output,
+                                                          DeviceBuffer<RVec>   gm_rVecInput,
+                                                          const int            size)
+{
+    int threadIndex = blockIdx.x * blockDim.x + threadIdx.x;
+    if (threadIndex < size)
+    {
+        gm_float3Output[threadIndex] = asFloat3(gm_rVecInput)[threadIndex];
+    }
+}
+
+void convertRVecToFloat3OnDevice(std::vector<gmx::RVec>&       h_rVecOutput,
+                                 const std::vector<gmx::RVec>& h_rVecInput,
+                                 const TestDevice*             testDevice)
+{
+    const DeviceContext& deviceContext = testDevice->deviceContext();
+    const DeviceStream&  deviceStream  = testDevice->deviceStream();
+
+    setActiveDevice(testDevice->deviceInfo());
+
+    const int numElements = h_rVecInput.size();
+
+    DeviceBuffer<RVec> d_rVecInput;
+    allocateDeviceBuffer(&d_rVecInput, numElements, deviceContext);
+    copyToDeviceBuffer(&d_rVecInput, h_rVecInput.data(), 0, numElements, deviceStream,
+                       GpuApiCallBehavior::Sync, nullptr);
+
+    DeviceBuffer<float3> d_float3Output;
+    allocateDeviceBuffer(&d_float3Output, numElements * DIM, deviceContext);
+
+    std::vector<float3> h_float3Output(numElements);
+
+    KernelLaunchConfig kernelLaunchConfig;
+    kernelLaunchConfig.gridSize[0]      = (numElements + c_threadsPerBlock - 1) / c_threadsPerBlock;
+    kernelLaunchConfig.blockSize[0]     = c_threadsPerBlock;
+    kernelLaunchConfig.blockSize[1]     = 1;
+    kernelLaunchConfig.blockSize[2]     = 1;
+    kernelLaunchConfig.sharedMemorySize = 0;
+
+    auto       kernelPtr  = convertRVecToFloat3OnDevice_kernel;
+    const auto kernelArgs = prepareGpuKernelArguments(kernelPtr, kernelLaunchConfig,
+                                                      &d_float3Output, &d_rVecInput, &numElements);
+    launchGpuKernel(kernelPtr, kernelLaunchConfig, deviceStream, nullptr,
+                    "convertRVecToFloat3OnDevice_kernel", kernelArgs);
+
+    copyFromDeviceBuffer(h_float3Output.data(), &d_float3Output, 0, numElements, deviceStream,
+                         GpuApiCallBehavior::Sync, nullptr);
+
+    saveFloat3InRVecFormat(h_rVecOutput, h_float3Output.data(), numElements);
+
+    freeDeviceBuffer(&d_rVecInput);
+    freeDeviceBuffer(&d_float3Output);
+}
+
+} // namespace test
+} // namespace gmx
diff --git a/src/gromacs/gpu_utils/tests/typecasts_runner.h b/src/gromacs/gpu_utils/tests/typecasts_runner.h
new file mode 100644 (file)
index 0000000..44a3247
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Header for runner for CUDA float3 type layout tests.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef GMX_GPU_UTILS_TESTS_TYPECASTS_RUNNER_H
+#define GMX_GPU_UTILS_TESTS_TYPECASTS_RUNNER_H
+
+#include "gmxpre.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/math/vectypes.h"
+
+#include "testutils/test_device.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+/*! \brief Tests the compatibility of RVec and float3 using the conversion on host.
+ *
+ * \param[out] rVecOutput  Data in RVec format for the output.
+ * \param[in]  rVecInput   Data in RVec format with the input.
+ */
+void convertRVecToFloat3OnHost(std::vector<gmx::RVec>& rVecOutput, const std::vector<gmx::RVec>& rVecInput);
+
+/*! \brief Tests the compatibility of RVec and float3 using the conversion on device.
+ *
+ * \param[out] rVecOutput  Data in RVec format for the output.
+ * \param[in]  rVecInput   Data in RVec format with the input.
+ * \param[in]  testDevice  Test herdware environment to get DeviceContext and DeviceStream from.
+ */
+void convertRVecToFloat3OnDevice(std::vector<gmx::RVec>&       rVecOutput,
+                                 const std::vector<gmx::RVec>& rVecInput,
+                                 const TestDevice*             testDevice);
+
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_GPU_UTILS_TESTS_TYPECASTS_RUNNER_H
diff --git a/src/gromacs/gpu_utils/typecasts.cuh b/src/gromacs/gpu_utils/typecasts.cuh
new file mode 100644 (file)
index 0000000..1dd63b7
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare functions to be used to cast CPU types to compatible GPU types.
+ *
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_TYPECASTS_CUH
+#define GMX_GPU_UTILS_TYPECASTS_CUH
+
+#include "gmxpre.h"
+
+#include "gromacs/math/vectypes.h"
+
+/*! \brief Cast RVec buffer to float3 buffer.
+ *
+ * \param[in] in The RVec buffer to cast.
+ *
+ * \returns Buffer, casted to float3*.
+ */
+static inline __host__ __device__ float3* asFloat3(gmx::RVec* in)
+{
+    static_assert(sizeof(in[0]) == sizeof(float3),
+                  "Size of the host-side data-type is different from the size of the device-side "
+                  "counterpart.");
+    return reinterpret_cast<float3*>(in);
+}
+
+#endif // GMX_GPU_UTILS_TYPECASTS_CUH
index f4e6d6af397110c12c17f3bcdb4962cfb8265855..cce3fc90082e00a5e4730c9d980a746858f2ea5b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -174,4 +174,106 @@ __forceinline__ __host__ __device__ float dist3(float4 a, float4 b)
     return norm(b - a);
 }
 
+/* \brief Compute the scalar product of two vectors.
+ *
+ * \param[in] a  First vector.
+ * \param[in] b  Second vector.
+ * \returns Scalar product.
+ */
+__forceinline__ __device__ float iprod(const float3 a, const float3 b)
+{
+    return a.x * b.x + a.y * b.y + a.z * b.z;
+}
+
+/* \brief Compute the vector product of two vectors.
+ *
+ * \param[in] a  First vector.
+ * \param[in] b  Second vector.
+ * \returns Vector product.
+ */
+__forceinline__ __device__ float3 cprod(const float3 a, const float3 b)
+{
+    float3 c;
+    c.x = a.y * b.z - a.z * b.y;
+    c.y = a.z * b.x - a.x * b.z;
+    c.z = a.x * b.y - a.y * b.x;
+    return c;
+}
+
+/* \brief Cosine of an angle between two vectors.
+ *
+ * Computes cosine using the following formula:
+ *
+ *                  ax*bx + ay*by + az*bz
+ * cos-vec (a,b) =  ---------------------
+ *                      ||a|| * ||b||
+ *
+ * This function also makes sure that the cosine does not leave the [-1, 1]
+ * interval, which can happen due to numerical errors.
+ *
+ * \param[in] a  First vector.
+ * \param[in] b  Second vector.
+ * \returns Cosine between a and b.
+ */
+__forceinline__ __device__ float cos_angle(const float3 a, const float3 b)
+{
+    float cosval;
+
+    float ipa  = norm2(a);
+    float ipb  = norm2(b);
+    float ip   = iprod(a, b);
+    float ipab = ipa * ipb;
+    if (ipab > 0.0f)
+    {
+        cosval = ip * rsqrt(ipab);
+    }
+    else
+    {
+        cosval = 1.0f;
+    }
+    if (cosval > 1.0f)
+    {
+        return 1.0f;
+    }
+    if (cosval < -1.0f)
+    {
+        return -1.0f;
+    }
+
+    return cosval;
+}
+
+/* \brief Compute the angle between two vectors.
+ *
+ * Uses atan( |axb| / a.b ) formula.
+ *
+ * \param[in] a  First vector.
+ * \param[in] b  Second vector.
+ * \returns Angle between vectors in radians.
+ */
+__forceinline__ __device__ float gmx_angle(const float3 a, const float3 b)
+{
+    float3 w = cprod(a, b);
+
+    float wlen = norm(w);
+    float s    = iprod(a, b);
+
+    return atan2f(wlen, s); // requires float
+}
+
+/* \brief Atomically add components of the vector.
+ *
+ * Executes atomicAdd one-by-one on all components of the float3 vector.
+ *
+ * \param[in] a  First vector.
+ * \param[in] b  Second vector.
+ * \returns Angle between vectors.
+ */
+__forceinline__ __device__ void atomicAdd(float3& a, const float3 b)
+{
+    atomicAdd(&a.x, b.x);
+    atomicAdd(&a.y, b.y);
+    atomicAdd(&a.z, b.z);
+}
+
 #endif /* VECTYPE_OPS_CUH */
index 7ea1facd85902c3710f1074398a73ea099b8200f..df67591a02ddbf75facb9ca5fe9dfd607415e5cf 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2016,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2016,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -57,10 +57,14 @@ endif()
 include(${_gmx_import_file})
 unset(_gmx_import_file)
 
-get_target_property(_libs libgromacs INTERFACE_LINK_LIBRARIES)
+get_target_property(_libs Gromacs::libgromacs INTERFACE_LINK_LIBRARIES)
 if (_libs MATCHES "tng_io::tng_io")
     include(CMakeFindDependencyMacro)
-    find_dependency(TNG_IO)
+    find_dependency(TNG_IO REQUIRED)
+endif()
+if (_libs MATCHES "OpenMP::OpenMP_CXX")
+    include(CMakeFindDependencyMacro)
+    find_dependency(OpenMP REQUIRED)
 endif()
 if (_libs MATCHES "OpenMP::OpenMP_CXX")
     include(CMakeFindDependencyMacro)
@@ -77,7 +81,7 @@ foreach (_dir ${_include_dirs})
         list(APPEND GROMACS_INCLUDE_DIRS ${_gmx_root_dir}/${_dir})
     endif()
 endforeach()
-set(GROMACS_LIBRARIES libgromacs)
+set(GROMACS_LIBRARIES Gromacs::libgromacs)
 set(GROMACS_DEFINITIONS @INSTALLED_HEADER_DEFINITIONS@)
 set(GROMACS_IS_DOUBLE @GMX_DOUBLE@)
 if (DEFINED GROMACS_SUFFIX AND NOT "${GROMACS_SUFFIX}" STREQUAL "@GMX_LIBS_SUFFIX@")
index 6bc268c766bc0f29f78ed4d0eed15843e5aeda7e..1209950e6ada23fc0c2dd3f44f014b3e5ab8b892 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2016,2017, by the GROMACS development team, led by
+# Copyright (c) 2015,2016,2017,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 gmx_add_libgromacs_sources(
     cpuinfo.cpp
     detecthardware.cpp
-    gpu_hw_info.cpp
+    device_management_common.cpp
     hardwaretopology.cpp
     printhardware.cpp
     identifyavx512fmaunits.cpp
     )
 
+if(GMX_GPU_OPENCL)
+    gmx_add_libgromacs_sources(
+        device_management_ocl.cpp
+        )
+elseif(GMX_GPU_CUDA)
+    gmx_add_libgromacs_sources(
+        device_management.cu
+        )
+    _gmx_add_files_to_property(CUDA_SOURCES
+        # Must add these files so they can include device_information.h
+        device_management_common.cpp
+        detecthardware.cpp
+       )
+elseif(GMX_GPU_SYCL)
+    gmx_add_libgromacs_sources(
+        device_management_sycl.cpp
+    )
+    _gmx_add_files_to_property(SYCL_SOURCES
+        device_management_sycl.cpp
+        # Must add these files so they can include device_information.h
+        device_management.cpp
+        detecthardware.cpp
+       )
+else()
+    gmx_add_libgromacs_sources(
+        device_management.cpp
+    )
+endif()
+
 if (BUILD_TESTING)
     add_subdirectory(tests)
 endif()
index 2004d8fcb173cb98509d494a6c279623fef8e848..f81a6f027998424c6aef3bcfb62f916552ad9eb8 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2012-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -85,6 +85,7 @@
 #endif
 
 #include <cctype>
+#include <cstdint> // uint32_t in X86 processor name code
 #include <cstdlib>
 
 #include <algorithm>
@@ -251,6 +252,52 @@ CpuInfo::Vendor detectX86Vendor()
     return v;
 }
 
+/*! \brief Detect second AVX-512 FMA from the processor name
+ *
+ * Should only be called for processors already determined to support AVX-512.
+ *
+ *  \param [in] brand     x86 processor name
+ *  \param [in] model     x86 model
+ *  \return               True if second FMA present
+ */
+bool detectProcCpuInfoSecondAvx512FMA(const std::string& brand, int model)
+{
+    // Skylake server
+    if (model == 0x55)
+    {
+        // detect Xeon
+        if (brand.find("Xeon") == 9)
+        {
+            // detect Silver or Bronze or specific models
+            if (brand.find("Silver") == 17 || brand.find("Bronze") == 17
+                || (brand.find('W') == 17 && brand.find('0') == 21)   // detect Xeon W 210x
+                || (brand.find('D') == 17 && brand.find("21") == 19)) // detect Xeon D 2xxx
+            {
+                return false;
+            }
+            // detect Gold 5xxx - can be corrected once Cooper Lake is added
+            else if (brand.find("Gold") == 17 && brand.find('5') == 22)
+            {
+                return (brand.find("53") == 22 || // detect Cooper Lake
+                        brand.find("22") == 24);  // detect 5[12]22
+            }
+        }
+        return true;
+    }
+    // Cannon Lake client
+    if (model == 0x66)
+    {
+        return false;
+    }
+    // Ice Lake client
+    if (model == 0x7d || model == 0x7e)
+    {
+        return false;
+    }
+    // This is the right default...
+    return true;
+}
+
 /*! \brief Simple utility function to set/clear feature in a set
  *
  *  \param featureSet    Pointer to the feature set to update
@@ -328,22 +375,6 @@ void detectX86Features(std::string* brand, int* family, int* model, int* steppin
         setFeatureFromBit(features, CpuInfo::Feature::X86_Htt, edx, 28);
     }
 
-    if (maxStdLevel >= 0x7)
-    {
-        executeX86CpuID(0x7, 0, &eax, &ebx, &ecx, &edx);
-
-        setFeatureFromBit(features, CpuInfo::Feature::X86_Hle, ebx, 4);
-        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx2, ebx, 5);
-        setFeatureFromBit(features, CpuInfo::Feature::X86_Rtm, ebx, 11);
-        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512F, ebx, 16);
-        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512PF, ebx, 26);
-        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512ER, ebx, 27);
-        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512CD, ebx, 28);
-        setFeatureFromBit(features, CpuInfo::Feature::X86_Sha, ebx, 29);
-        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512BW, ebx, 30);
-        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512VL, ebx, 31);
-    }
-
     // Check whether Hyper-threading is really possible to enable in the hardware,
     // not just technically supported by this generation of processors
     if ((features->count(CpuInfo::Feature::X86_Htt) != 0U) && maxStdLevel >= 0x4)
@@ -394,6 +425,35 @@ void detectX86Features(std::string* brand, int* family, int* model, int* steppin
         trimString(brand);
     }
 
+    if (maxStdLevel >= 0x7)
+    {
+        executeX86CpuID(0x7, 0, &eax, &ebx, &ecx, &edx);
+
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Hle, ebx, 4);
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx2, ebx, 5);
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Rtm, ebx, 11);
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512F, ebx, 16);
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512PF, ebx, 26);
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512ER, ebx, 27);
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512CD, ebx, 28);
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Sha, ebx, 29);
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512BW, ebx, 30);
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512VL, ebx, 31);
+
+        executeX86CpuID(0x7, 0x1, &eax, &ebx, &ecx, &edx);
+        setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512BF16, eax, 5);
+
+        if (features->count(CpuInfo::Feature::X86_Avx512F) != 0)
+        {
+            // Only checking if the CPU supports AVX-512. There is no CPUID bit for this.
+            if (detectProcCpuInfoSecondAvx512FMA(*brand, *model))
+            {
+                features->insert(CpuInfo::Feature::X86_Avx512secondFMA);
+            }
+        }
+    }
+
+
     if (maxExtLevel >= 0x80000007)
     {
         executeX86CpuID(0x80000007, 0, &eax, &ebx, &ecx, &edx);
@@ -887,6 +947,10 @@ void detectProcCpuInfoArm(const std::map<std::string, std::string>& cpuInfo,
                 features->insert(CpuInfo::Feature::Arm_NeonAsimd);
             }
         }
+        if (s.find("sve") != std::string::npos)
+        {
+            features->insert(CpuInfo::Feature::Arm_Sve);
+        }
     }
 }
 
@@ -983,6 +1047,9 @@ CpuInfo CpuInfo::detect()
         result.features_.insert(Feature::Arm_Neon);      // ARMv8 always has Neon
         result.features_.insert(Feature::Arm_NeonAsimd); // ARMv8 always has Neon-asimd
 #endif
+#if defined __arch64__ && defined __ARM_FEATURE_SVE
+        result.features_.insert(Feature::Arm_Sve);
+#endif
 
 #if defined sun
         result.vendor_ = CpuInfo::Vendor::Oracle;
@@ -1050,6 +1117,8 @@ const std::string& CpuInfo::featureString(Feature f)
         { Feature::X86_Avx512CD, "avx512cd" },
         { Feature::X86_Avx512BW, "avx512bw" },
         { Feature::X86_Avx512VL, "avx512vl" },
+        { Feature::X86_Avx512BF16, "avx512bf16" },
+        { Feature::X86_Avx512secondFMA, "avx512secondFMA" },
         { Feature::X86_Clfsh, "clfsh" },
         { Feature::X86_Cmov, "cmov" },
         { Feature::X86_Cx8, "cx8" },
@@ -1086,6 +1155,7 @@ const std::string& CpuInfo::featureString(Feature f)
         { Feature::X86_Xop, "xop" },
         { Feature::Arm_Neon, "neon" },
         { Feature::Arm_NeonAsimd, "neon_asimd" },
+        { Feature::Arm_Sve, "sve" },
         { Feature::Ibm_Qpx, "qpx" },
         { Feature::Ibm_Vmx, "vmx" },
         { Feature::Ibm_Vsx, "vsx" },
index b2e305838caea4834418984609b5f775435efc44..fe09e27807a74b899ee1d321f3ff5a9aa7a2305c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -97,25 +97,27 @@ public:
      */
     enum class Feature
     {
-        X86_Aes,      //!< x86 advanced encryption standard accel.
-        X86_Amd,      //!< This is an AMD x86 processor
-        X86_Apic,     //!< APIC support
-        X86_Avx,      //!< Advanced vector extensions
-        X86_Avx2,     //!< AVX2 including gather support (not used yet)
-        X86_Avx512F,  //!< Foundation AVX-512 instructions
-        X86_Avx512PF, //!< Extended gather/scatter for AVX-512
-        X86_Avx512ER, //!< AVX-512 exponential and recpirocal extensions
-        X86_Avx512CD, //!< Memory conflict-detection for AVX-512
-        X86_Avx512BW, //!< AVX-512 byte and word instructions
-        X86_Avx512VL, //!< AVX-512 vector length extensions
-        X86_Clfsh,    //!< Supports CLFLUSH instruction
-        X86_Cmov,     //!< Conditional move insn support
-        X86_Cx8,      //!< Supports CMPXCHG8B (8-byte compare-exchange)
-        X86_Cx16,     //!< Supports CMPXCHG16B (16-byte compare-exchg)
-        X86_F16C,     //!< Supports 16-bit FP conversion instructions
-        X86_Fma,      //!< Fused-multiply add support (mainly for AVX)
-        X86_Fma4,     //!< 4-operand FMA, only on AMD for now
-        X86_Hle,      //!< Hardware lock elision
+        X86_Aes,             //!< x86 advanced encryption standard accel.
+        X86_Amd,             //!< This is an AMD x86 processor
+        X86_Apic,            //!< APIC support
+        X86_Avx,             //!< Advanced vector extensions
+        X86_Avx2,            //!< AVX2 including gather support (not used yet)
+        X86_Avx512F,         //!< Foundation AVX-512 instructions
+        X86_Avx512PF,        //!< Extended gather/scatter for AVX-512
+        X86_Avx512ER,        //!< AVX-512 exponential and reciprocal extensions
+        X86_Avx512CD,        //!< Memory conflict-detection for AVX-512
+        X86_Avx512BW,        //!< AVX-512 byte and word instructions
+        X86_Avx512VL,        //!< AVX-512 vector length extensions
+        X86_Avx512BF16,      //!< AVX-512 BFloat16 instructions
+        X86_Avx512secondFMA, //!< AVX-512 second FMA unit
+        X86_Clfsh,           //!< Supports CLFLUSH instruction
+        X86_Cmov,            //!< Conditional move insn support
+        X86_Cx8,             //!< Supports CMPXCHG8B (8-byte compare-exchange)
+        X86_Cx16,            //!< Supports CMPXCHG16B (16-byte compare-exchg)
+        X86_F16C,            //!< Supports 16-bit FP conversion instructions
+        X86_Fma,             //!< Fused-multiply add support (mainly for AVX)
+        X86_Fma4,            //!< 4-operand FMA, only on AMD for now
+        X86_Hle,             //!< Hardware lock elision
         X86_Htt,   //!< Hyper-Threading enabled (NOTE: might not match the CPUID HTT support flag)
         X86_Intel, //!< This is an Intel x86 processor
         X86_Lahf,  //!< LAHF/SAHF support in 64 bits
@@ -144,6 +146,7 @@ public:
         X86_Xop,         //!< AMD extended instructions, only AMD for now
         Arm_Neon,        //!< 32-bit ARM NEON
         Arm_NeonAsimd,   //!< 64-bit ARM AArch64 Advanced SIMD
+        Arm_Sve,         //!< ARM Scalable Vector Extensions
         Ibm_Qpx,         //!< IBM QPX SIMD (BlueGene/Q)
         Ibm_Vmx,         //!< IBM VMX SIMD (Altivec on Power6 and later)
         Ibm_Vsx,         //!< IBM VSX SIMD (Power7 and later)
index c24a8b2a4cec6c4d0f9c761d97d368c952981e01..3bcdac4835f2d52b5db9855e7aaab9109b213f40 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,8 +48,8 @@
 #include <vector>
 
 #include "gromacs/compat/pointers.h"
-#include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/hardware/cpuinfo.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/hardware/hardwaretopology.h"
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/simd/support.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/inmemoryserializer.h"
 #include "gromacs/utility/logger.h"
 #include "gromacs/utility/mutex.h"
 #include "gromacs/utility/physicalnodecommunicator.h"
 
 #include "architecture.h"
+#include "device_information.h"
 
 #ifdef HAVE_UNISTD_H
 #    include <unistd.h> // sysconf()
@@ -76,10 +79,7 @@ gmx_hw_info_t::gmx_hw_info_t(std::unique_ptr<gmx::CpuInfo>          cpuInfo,
 {
 }
 
-gmx_hw_info_t::~gmx_hw_info_t()
-{
-    free_gpu_info(&gpu_info);
-}
+gmx_hw_info_t::~gmx_hw_info_t() = default;
 
 namespace gmx
 {
@@ -111,13 +111,13 @@ static void gmx_detect_gpus(const gmx::MDLogger&             mdlog,
                             const PhysicalNodeCommunicator&  physicalNodeComm,
                             compat::not_null<gmx_hw_info_t*> hardwareInfo)
 {
-    hardwareInfo->gpu_info.bDetectGPUs = canPerformGpuDetection();
-
-    if (!hardwareInfo->gpu_info.bDetectGPUs)
+    if (!isDeviceDetectionEnabled())
     {
         return;
     }
 
+    std::string errorMessage;
+
     bool isMasterRankOfPhysicalNode = true;
 #if GMX_LIB_MPI
     isMasterRankOfPhysicalNode = (physicalNodeComm.rank_ == 0);
@@ -131,15 +131,15 @@ static void gmx_detect_gpus(const gmx::MDLogger&             mdlog,
     /* The OpenCL support requires us to run detection on all ranks.
      * With CUDA we don't need to, and prefer to detect on one rank
      * and send the information to the other ranks over MPI. */
-    bool allRanksMustDetectGpus = (GMX_GPU == GMX_GPU_OPENCL);
-    bool gpusCanBeDetected      = false;
+    constexpr bool allRanksMustDetectGpus = (GMX_GPU_OPENCL != 0 || GMX_GPU_SYCL != 0);
+    bool           gpusCanBeDetected      = false;
     if (isMasterRankOfPhysicalNode || allRanksMustDetectGpus)
     {
         std::string errorMessage;
-        gpusCanBeDetected = isGpuDetectionFunctional(&errorMessage);
+        gpusCanBeDetected = isDeviceDetectionFunctional(&errorMessage);
         if (!gpusCanBeDetected)
         {
-            GMX_LOG(mdlog.info)
+            GMX_LOG(mdlog.warning)
                     .asParagraph()
                     .appendTextFormatted(
                             "NOTE: Detection of GPUs failed. The API reported:\n"
@@ -151,29 +151,37 @@ static void gmx_detect_gpus(const gmx::MDLogger&             mdlog,
 
     if (gpusCanBeDetected)
     {
-        findGpus(&hardwareInfo->gpu_info);
+        hardwareInfo->deviceInfoList = findDevices();
         // No need to tell the user anything at this point, they get a
         // hardware report later.
     }
 
 #if GMX_LIB_MPI
-    if (!allRanksMustDetectGpus)
+    if (!allRanksMustDetectGpus && (physicalNodeComm.size_ > 1))
     {
-        /* Broadcast the GPU info to the other ranks within this node */
-        MPI_Bcast(&hardwareInfo->gpu_info.n_dev, 1, MPI_INT, 0, physicalNodeComm.comm_);
-
-        if (hardwareInfo->gpu_info.n_dev > 0)
+        // Master rank must serialize the device information list and
+        // send it to the other ranks on this node.
+        std::vector<char> buffer;
+        int               sizeOfBuffer;
+        if (isMasterRankOfPhysicalNode)
         {
-            int dev_size;
-
-            dev_size = hardwareInfo->gpu_info.n_dev * sizeof_gpu_dev_info();
-
+            gmx::InMemorySerializer writer;
+            serializeDeviceInformations(hardwareInfo->deviceInfoList, &writer);
+            buffer       = writer.finishAndGetBuffer();
+            sizeOfBuffer = buffer.size();
+        }
+        // Ensure all ranks agree on the size of list to be sent
+        MPI_Bcast(&sizeOfBuffer, 1, MPI_INT, 0, physicalNodeComm.comm_);
+        buffer.resize(sizeOfBuffer);
+        if (!buffer.empty())
+        {
+            // Send the list and deserialize it
+            MPI_Bcast(buffer.data(), buffer.size(), MPI_BYTE, 0, physicalNodeComm.comm_);
             if (!isMasterRankOfPhysicalNode)
             {
-                hardwareInfo->gpu_info.gpu_dev = (struct gmx_device_info_t*)malloc(dev_size);
+                gmx::InMemoryDeserializer reader(buffer, false);
+                hardwareInfo->deviceInfoList = deserializeDeviceInformations(&reader);
             }
-            MPI_Bcast(hardwareInfo->gpu_info.gpu_dev, dev_size, MPI_BYTE, 0, physicalNodeComm.comm_);
-            MPI_Bcast(&hardwareInfo->gpu_info.n_dev_compatible, 1, MPI_INT, 0, physicalNodeComm.comm_);
         }
     }
 #endif
@@ -193,28 +201,27 @@ static void gmx_collect_hardware_mpi(const gmx::CpuInfo&              cpuInfo,
                                 && (cpuInfo.model() == 1 || cpuInfo.model() == 17
                                     || cpuInfo.model() == 8 || cpuInfo.model() == 24))
                                || cpuInfo.vendor() == CpuInfo::Vendor::Hygon);
+
+    int numCompatibleDevices = getCompatibleDevices(hardwareInfo->deviceInfoList).size();
 #if GMX_LIB_MPI
-    int nhwthread, ngpu, i;
+    int nhwthread;
     int gpu_hash;
 
     nhwthread = hardwareInfo->nthreads_hw_avail;
-    ngpu      = hardwareInfo->gpu_info.n_dev_compatible;
     /* Create a unique hash of the GPU type(s) in this node */
     gpu_hash = 0;
     /* Here it might be better to only loop over the compatible GPU, but we
      * don't have that information available and it would also require
      * removing the device ID from the device info string.
      */
-    for (i = 0; i < hardwareInfo->gpu_info.n_dev; i++)
+    for (const auto& deviceInfo : hardwareInfo->deviceInfoList)
     {
-        char stmp[STRLEN];
-
         /* Since the device ID is incorporated in the hash, the order of
          * the GPUs affects the hash. Also two identical GPUs won't give
          * a gpu_hash of zero after XORing.
          */
-        get_gpu_device_info_string(stmp, hardwareInfo->gpu_info, i);
-        gpu_hash ^= gmx_string_fullhash_func(stmp, gmx_string_hash_init);
+        std::string deviceInfoString = getDeviceInformationString(*deviceInfo);
+        gpu_hash ^= gmx_string_fullhash_func(deviceInfoString.c_str(), gmx_string_hash_init);
     }
 
     constexpr int                      numElementsCounts = 4;
@@ -229,7 +236,7 @@ static void gmx_collect_hardware_mpi(const gmx::CpuInfo&              cpuInfo,
             countsLocal[0] = 1;
             countsLocal[1] = ncore;
             countsLocal[2] = nhwthread;
-            countsLocal[3] = ngpu;
+            countsLocal[3] = numCompatibleDevices;
         }
 
         MPI_Allreduce(countsLocal.data(), countsReduced.data(), countsLocal.size(), MPI_INT,
@@ -245,7 +252,7 @@ static void gmx_collect_hardware_mpi(const gmx::CpuInfo&              cpuInfo,
          */
         maxMinLocal[0]  = ncore;
         maxMinLocal[1]  = nhwthread;
-        maxMinLocal[2]  = ngpu;
+        maxMinLocal[2]  = numCompatibleDevices;
         maxMinLocal[3]  = static_cast<int>(gmx::simdSuggested(cpuInfo));
         maxMinLocal[4]  = gpu_hash;
         maxMinLocal[5]  = -maxMinLocal[0];
@@ -282,9 +289,9 @@ static void gmx_collect_hardware_mpi(const gmx::CpuInfo&              cpuInfo,
     hardwareInfo->nhwthread_tot       = hardwareInfo->nthreads_hw_avail;
     hardwareInfo->nhwthread_min       = hardwareInfo->nthreads_hw_avail;
     hardwareInfo->nhwthread_max       = hardwareInfo->nthreads_hw_avail;
-    hardwareInfo->ngpu_compatible_tot = hardwareInfo->gpu_info.n_dev_compatible;
-    hardwareInfo->ngpu_compatible_min = hardwareInfo->gpu_info.n_dev_compatible;
-    hardwareInfo->ngpu_compatible_max = hardwareInfo->gpu_info.n_dev_compatible;
+    hardwareInfo->ngpu_compatible_tot = numCompatibleDevices;
+    hardwareInfo->ngpu_compatible_min = numCompatibleDevices;
+    hardwareInfo->ngpu_compatible_max = numCompatibleDevices;
     hardwareInfo->simd_suggest_min    = static_cast<int>(simdSuggested(cpuInfo));
     hardwareInfo->simd_suggest_max    = static_cast<int>(simdSuggested(cpuInfo));
     hardwareInfo->bIdenticalGPUs      = TRUE;
@@ -300,9 +307,17 @@ static void gmx_collect_hardware_mpi(const gmx::CpuInfo&              cpuInfo,
  *  2 seconds, or until all cores have come online. This can be used prior to
  *  hardware detection for platforms that take unused processors offline.
  *
- *  This routine will not throw exceptions.
+ *  This routine will not throw exceptions. In principle it should be
+ *  declared noexcept, but at least icc 19.1 and 21-beta08 with the
+ *  libstdc++-7.5 has difficulty implementing a std::vector of
+ *  std::thread started with this function when declared noexcept. It
+ *  is not clear whether the problem is the compiler or the standard
+ *  library. Fortunately, this function is not performance sensitive,
+ *  and only runs on platforms other than x86 and POWER (ie ARM),
+ *  so the possible overhead introduced by omitting noexcept is not
+ *  important.
  */
-static void spinUpCore() noexcept
+static void spinUpCore()
 {
 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) && defined(_SC_NPROCESSORS_ONLN)
     float dummy           = 0.1;
@@ -451,10 +466,6 @@ gmx_hw_info_t* gmx_detect_hardware(const gmx::MDLogger& mdlog, const PhysicalNod
     hardwareInfo->nthreads_hw_avail = hardwareInfo->hardwareTopology->machine().logicalProcessorCount;
 
     // Detect GPUs
-    hardwareInfo->gpu_info.n_dev            = 0;
-    hardwareInfo->gpu_info.n_dev_compatible = 0;
-    hardwareInfo->gpu_info.gpu_dev          = nullptr;
-
     gmx_detect_gpus(mdlog, physicalNodeComm, compat::make_not_null(hardwareInfo));
     gmx_collect_hardware_mpi(*hardwareInfo->cpuInfo, physicalNodeComm, compat::make_not_null(hardwareInfo));
 
@@ -465,9 +476,4 @@ gmx_hw_info_t* gmx_detect_hardware(const gmx::MDLogger& mdlog, const PhysicalNod
     return g_hardwareInfo.get();
 }
 
-bool compatibleGpusFound(const gmx_gpu_info_t& gpu_info)
-{
-    return gpu_info.n_dev_compatible > 0;
-}
-
 } // namespace gmx
index e0f5b69450622cb6791e78309fd4f9c3259f0718..efacba0028a37402a646fe69cbf9fe2b587b3c3d 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -35,7 +36,6 @@
 #ifndef GMX_HARDWARE_DETECTHARDWARE_H
 #define GMX_HARDWARE_DETECTHARDWARE_H
 
-struct gmx_gpu_info_t;
 struct gmx_hw_info_t;
 
 namespace gmx
@@ -61,9 +61,6 @@ class PhysicalNodeCommunicator;
 gmx_hw_info_t* gmx_detect_hardware(const gmx::MDLogger&            mdlog,
                                    const PhysicalNodeCommunicator& physicalNodeComm);
 
-//! Return whether compatible GPUs were found.
-bool compatibleGpusFound(const gmx_gpu_info_t& gpu_info);
-
 } // namespace gmx
 
 #endif
diff --git a/src/gromacs/hardware/device_information.h b/src/gromacs/hardware/device_information.h
new file mode 100644 (file)
index 0000000..54915dc
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declares the GPU information structure and its helpers
+ *
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Teemu Virolainen <teemu@streamcomputing.eu>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef GMX_HARDWARE_DEVICE_INFORMATION_H
+#define GMX_HARDWARE_DEVICE_INFORMATION_H
+
+#include "config.h"
+
+#if GMX_GPU_CUDA
+#    include <cuda_runtime.h>
+#endif
+
+#if GMX_GPU_OPENCL
+#    include "gromacs/gpu_utils/gmxopencl.h"
+#endif
+
+#if GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/gmxsycl.h"
+#endif
+
+#include "gromacs/utility/enumerationhelpers.h"
+
+//! Constant used to help minimize preprocessed code
+static constexpr bool c_binarySupportsGpus = (GMX_GPU != 0);
+static constexpr bool c_canSerializeDeviceInformation =
+        (!GMX_GPU_OPENCL && !GMX_GPU_SYCL); /*NOLINT(misc-redundant-expression)*/
+
+//! Possible results of the GPU detection/check.
+enum class DeviceStatus : int
+{
+    //! The device is compatible
+    Compatible = 0,
+    //! Device does not exist
+    Nonexistent = 1,
+    //! Device is not compatible
+    Incompatible = 2,
+    //! OpenCL device has incompatible cluster size for non-bonded kernels.
+    IncompatibleClusterSize = 3,
+    //! There are known issues with NVIDIA Volta and newer.
+    IncompatibleNvidiaVolta = 4,
+    /*! \brief An error occurred during the functionality checks.
+     * That indicates malfunctioning of the device, driver, or incompatible driver/runtime.
+     */
+    NonFunctional = 5,
+    /*! \brief CUDA devices are busy or unavailable.
+     * typically due to use of \p cudaComputeModeExclusive, \p cudaComputeModeProhibited modes.
+     */
+    Unavailable = 6,
+    //! Enumeration size
+    Count = 7
+};
+
+/*! \brief Names of the GPU detection/check results
+ *
+ * Check-source wants to warn about the use of a symbol name that would
+ * require an inclusion of config.h. However the use is in a comment, so that
+ * is a false warning. So C-style string concatenation is used to fool the
+ * naive parser in check-source. That needs a clang-format suppression
+ * in order to look reasonable. Also clang-tidy wants to suggest that a comma is
+ * missing, so that is suppressed.
+ */
+static const gmx::EnumerationArray<DeviceStatus, const char*> c_deviceStateString = {
+    "compatible", "nonexistent", "incompatible",
+    // clang-format off
+    // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
+    "incompatible (please recompile with correct GMX" "_OPENCL_NB_CLUSTER_SIZE of 4)",
+    // clang-format on
+    "incompatible (please use CUDA build for NVIDIA Volta GPUs or newer)", "non-functional",
+    "unavailable"
+};
+
+//! Device vendors
+enum class DeviceVendor : int
+{
+    //! No data
+    Unknown = 0,
+    //! NVIDIA
+    Nvidia = 1,
+    //! Advanced Micro Devices
+    Amd = 2,
+    //! Intel
+    Intel = 3,
+    //! Enumeration size
+    Count = 4
+};
+
+
+/*! \libinternal \brief Platform-dependent device information.
+ *
+ * The device information is queried and set at detection and contains
+ * both information about the device/hardware returned by the runtime as well
+ * as additional data like support status.
+ */
+struct DeviceInformation
+{
+    //! Device status.
+    DeviceStatus status;
+    //! ID of the device.
+    int id;
+    //! Device vendor.
+    DeviceVendor deviceVendor;
+#if GMX_GPU_CUDA
+    //! CUDA device properties.
+    cudaDeviceProp prop;
+#elif GMX_GPU_OPENCL
+    cl_platform_id oclPlatformId;       //!< OpenCL Platform ID.
+    cl_device_id   oclDeviceId;         //!< OpenCL Device ID.
+    char           device_name[256];    //!< Device name.
+    char           device_version[256]; //!< Device version.
+    char           vendorName[256];     //!< Device vendor name.
+    int            compute_units;       //!< Number of compute units.
+    int            adress_bits;         //!< Number of address bits the device is capable of.
+    size_t         maxWorkItemSizes[3]; //!< Workgroup size limits (CL_DEVICE_MAX_WORK_ITEM_SIZES).
+    size_t         maxWorkGroupSize;    //!< Workgroup total size limit (CL_DEVICE_MAX_WORK_GROUP_SIZE).
+#elif GMX_GPU_SYCL
+    cl::sycl::device syclDevice;
+#endif
+};
+
+#endif // GMX_HARDWARE_DEVICE_INFORMATION_H
diff --git a/src/gromacs/hardware/device_management.cpp b/src/gromacs/hardware/device_management.cpp
new file mode 100644 (file)
index 0000000..87384c7
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *  \brief Defines the CPU stubs for the device management.
+ *
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Teemu Virolainen <teemu@streamcomputing.eu>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_hardware
+ */
+#include "gmxpre.h"
+
+#include "device_management.h"
+
+#include "gromacs/utility/fatalerror.h"
+
+#include "device_information.h"
+
+std::vector<std::unique_ptr<DeviceInformation>> findDevices()
+{
+    return {};
+}
+
+void setActiveDevice(const DeviceInformation& /* deviceInfo */) {}
+
+void releaseDevice(DeviceInformation* /* deviceInfo */) {}
+
+std::string getDeviceInformationString(const DeviceInformation& /* deviceInfo */)
+{
+    gmx_fatal(FARGS, "Device information requested in CPU build.");
+}
+
+bool isDeviceDetectionFunctional(std::string* /* errorMessage */)
+{
+    return false;
+}
diff --git a/src/gromacs/hardware/device_management.cu b/src/gromacs/hardware/device_management.cu
new file mode 100644 (file)
index 0000000..a09d6bf
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *  \brief Defines the CUDA implementations of the device management.
+ *
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Teemu Virolainen <teemu@streamcomputing.eu>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_hardware
+ */
+#include "gmxpre.h"
+
+#include "device_management.h"
+
+#include <assert.h>
+
+#include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/programcontext.h"
+#include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "device_information.h"
+
+/*! \internal \brief
+ * Max number of devices supported by CUDA (for consistency checking).
+ *
+ * In reality it is 16 with CUDA <=v5.0, but let's stay on the safe side.
+ */
+static int c_cudaMaxDeviceCount = 32;
+
+/** Dummy kernel used for sanity checking. */
+static __global__ void dummy_kernel(void) {}
+
+static cudaError_t checkCompiledTargetCompatibility(int deviceId, const cudaDeviceProp& deviceProp)
+{
+    cudaFuncAttributes attributes;
+    cudaError_t        stat = cudaFuncGetAttributes(&attributes, dummy_kernel);
+
+    if (cudaErrorInvalidDeviceFunction == stat)
+    {
+        fprintf(stderr,
+                "\nWARNING: The %s binary does not include support for the CUDA architecture of "
+                "the GPU ID #%d (compute capability %d.%d) detected during detection. "
+                "By default, GROMACS supports all architectures of compute "
+                "capability >= 3.0, so your GPU "
+                "might be rare, or some architectures were disabled in the build. \n"
+                "Consult the install guide for how to use the GMX_CUDA_TARGET_SM and "
+                "GMX_CUDA_TARGET_COMPUTE CMake variables to add this architecture. \n",
+                gmx::getProgramContext().displayName(), deviceId, deviceProp.major, deviceProp.minor);
+    }
+
+    return stat;
+}
+
+/*!
+ * \brief Runs GPU sanity checks.
+ *
+ * Runs a series of checks to determine that the given GPU and underlying CUDA
+ * driver/runtime functions properly.
+ *
+ * \todo Currently we do not make a distinction between the type of errors
+ *       that can appear during functionality checks. This needs to be improved,
+ *       e.g if the dummy test kernel fails to execute with a "device busy message"
+ *       we should appropriately report that the device is busy instead of NonFunctional.
+ *
+ * \todo Introduce errors codes and handle errors more smoothly.
+ *
+ *
+ * \param[in]  deviceInfo  Device information on the device to check.
+ * \returns                The status enumeration value for the checked device:
+ */
+static DeviceStatus isDeviceFunctional(const DeviceInformation& deviceInfo)
+{
+    cudaError_t cu_err;
+
+    /* both major & minor is 9999 if no CUDA capable devices are present */
+    if (deviceInfo.prop.major == 9999 && deviceInfo.prop.minor == 9999)
+    {
+        return DeviceStatus::NonFunctional;
+    }
+    /* we don't care about emulation mode */
+    if (deviceInfo.prop.major == 0)
+    {
+        return DeviceStatus::NonFunctional;
+    }
+
+    cu_err = cudaSetDevice(deviceInfo.id);
+    if (cu_err != cudaSuccess)
+    {
+        fprintf(stderr, "Error %d while switching to device #%d: %s\n", cu_err, deviceInfo.id,
+                cudaGetErrorString(cu_err));
+        return DeviceStatus::NonFunctional;
+    }
+
+    cu_err = checkCompiledTargetCompatibility(deviceInfo.id, deviceInfo.prop);
+    // Avoid triggering an error if GPU devices are in exclusive or prohibited mode;
+    // it is enough to check for cudaErrorDevicesUnavailable only here because
+    // if we encounter it that will happen in cudaFuncGetAttributes in the above function.
+    if (cu_err == cudaErrorDevicesUnavailable)
+    {
+        return DeviceStatus::Unavailable;
+    }
+    else if (cu_err != cudaSuccess)
+    {
+        return DeviceStatus::NonFunctional;
+    }
+
+    /* try to execute a dummy kernel */
+    try
+    {
+        KernelLaunchConfig config;
+        config.blockSize[0]                = 512;
+        const auto          dummyArguments = prepareGpuKernelArguments(dummy_kernel, config);
+        const DeviceContext deviceContext(deviceInfo);
+        const DeviceStream  deviceStream(deviceContext, DeviceStreamPriority::Normal, false);
+        launchGpuKernel(dummy_kernel, config, deviceStream, nullptr, "Dummy kernel", dummyArguments);
+    }
+    catch (gmx::GromacsException& ex)
+    {
+        // launchGpuKernel error is not fatal and should continue with marking the device bad
+        fprintf(stderr,
+                "Error occurred while running dummy kernel sanity check on device #%d:\n %s\n",
+                deviceInfo.id, formatExceptionMessageToString(ex).c_str());
+        return DeviceStatus::NonFunctional;
+    }
+
+    if (cudaDeviceSynchronize() != cudaSuccess)
+    {
+        return DeviceStatus::NonFunctional;
+    }
+
+    cu_err = cudaDeviceReset();
+    CU_RET_ERR(cu_err, "cudaDeviceReset failed");
+
+    return DeviceStatus::Compatible;
+}
+
+/*! \brief Returns true if the gpu characterized by the device properties is supported
+ *         by the native gpu acceleration.
+ *
+ * \param[in] deviceProperties  The CUDA device properties of the gpus to test.
+ * \returns                     True if the GPU properties passed indicate a compatible
+ *                              GPU, otherwise false.
+ */
+static bool isDeviceGenerationSupported(const cudaDeviceProp& deviceProperties)
+{
+    return (deviceProperties.major >= 3);
+}
+
+/*! \brief Checks if a GPU with a given ID is supported by the native GROMACS acceleration.
+ *
+ *  Returns a status value which indicates compatibility or one of the following
+ *  errors: incompatibility or insanity (=unexpected behavior).
+ *
+ *  As the error handling only permits returning the state of the GPU, this function
+ *  does not clear the CUDA runtime API status allowing the caller to inspect the error
+ *  upon return. Note that this also means it is the caller's responsibility to
+ *  reset the CUDA runtime state.
+ *
+ *  \param[in]  deviceInfo The device information on the device to check.
+ *  \returns               the status of the requested device
+ */
+static DeviceStatus checkDeviceStatus(const DeviceInformation& deviceInfo)
+{
+    if (!isDeviceGenerationSupported(deviceInfo.prop))
+    {
+        return DeviceStatus::Incompatible;
+    }
+    return isDeviceFunctional(deviceInfo);
+}
+
+bool isDeviceDetectionFunctional(std::string* errorMessage)
+{
+    cudaError_t stat;
+    int         driverVersion = -1;
+    stat                      = cudaDriverGetVersion(&driverVersion);
+    GMX_ASSERT(stat != cudaErrorInvalidValue,
+               "An impossible null pointer was passed to cudaDriverGetVersion");
+    GMX_RELEASE_ASSERT(
+            stat == cudaSuccess,
+            gmx::formatString("An unexpected value was returned from cudaDriverGetVersion %s: %s",
+                              cudaGetErrorName(stat), cudaGetErrorString(stat))
+                    .c_str());
+    bool foundDriver = (driverVersion > 0);
+    if (!foundDriver)
+    {
+        // Can't detect GPUs if there is no driver
+        if (errorMessage != nullptr)
+        {
+            errorMessage->assign("No valid CUDA driver found");
+        }
+        return false;
+    }
+
+    int numDevices;
+    stat = cudaGetDeviceCount(&numDevices);
+    if (stat != cudaSuccess)
+    {
+        if (errorMessage != nullptr)
+        {
+            /* cudaGetDeviceCount failed which means that there is
+             * something wrong with the machine: driver-runtime
+             * mismatch, all GPUs being busy in exclusive mode,
+             * invalid CUDA_VISIBLE_DEVICES, or some other condition
+             * which should result in GROMACS issuing at least a
+             * warning. */
+            errorMessage->assign(cudaGetErrorString(stat));
+        }
+
+        // Consume the error now that we have prepared to handle
+        // it. This stops it reappearing next time we check for
+        // errors. Note that if CUDA_VISIBLE_DEVICES does not contain
+        // valid devices, then cudaGetLastError returns the
+        // (undocumented) cudaErrorNoDevice, but this should not be a
+        // problem as there should be no future CUDA API calls.
+        // NVIDIA bug report #2038718 has been filed.
+        cudaGetLastError();
+        // Can't detect GPUs
+        return false;
+    }
+
+    // We don't actually use numDevices here, that's not the job of
+    // this function.
+    return true;
+}
+
+std::vector<std::unique_ptr<DeviceInformation>> findDevices()
+{
+    int         numDevices;
+    cudaError_t stat = cudaGetDeviceCount(&numDevices);
+    if (stat != cudaSuccess)
+    {
+        GMX_THROW(gmx::InternalError(
+                "Invalid call of findDevices() when CUDA API returned an error, perhaps "
+                "canPerformDeviceDetection() was not called appropriately beforehand."));
+    }
+
+    /* things might go horribly wrong if cudart is not compatible with the driver */
+    numDevices = std::min(numDevices, c_cudaMaxDeviceCount);
+
+    // We expect to start device support/sanity checks with a clean runtime error state
+    gmx::ensureNoPendingCudaError("");
+
+    std::vector<std::unique_ptr<DeviceInformation>> deviceInfoList(numDevices);
+    for (int i = 0; i < numDevices; i++)
+    {
+        cudaDeviceProp prop;
+        memset(&prop, 0, sizeof(cudaDeviceProp));
+        stat = cudaGetDeviceProperties(&prop, i);
+
+        deviceInfoList[i]               = std::make_unique<DeviceInformation>();
+        deviceInfoList[i]->id           = i;
+        deviceInfoList[i]->prop         = prop;
+        deviceInfoList[i]->deviceVendor = DeviceVendor::Nvidia;
+
+        const DeviceStatus checkResult = (stat != cudaSuccess) ? DeviceStatus::NonFunctional
+                                                               : checkDeviceStatus(*deviceInfoList[i]);
+
+        deviceInfoList[i]->status = checkResult;
+
+        if (checkResult != DeviceStatus::Compatible)
+        {
+            // TODO:
+            //  - we inspect the CUDA API state to retrieve and record any
+            //    errors that occurred during is_gmx_supported_gpu_id() here,
+            //    but this would be more elegant done within is_gmx_supported_gpu_id()
+            //    and only return a string with the error if one was encountered.
+            //  - we'll be reporting without rank information which is not ideal.
+            //  - we'll end up warning also in cases where users would already
+            //    get an error before mdrun aborts.
+            //
+            // Here we also clear the CUDA API error state so potential
+            // errors during sanity checks don't propagate.
+            if ((stat = cudaGetLastError()) != cudaSuccess)
+            {
+                gmx_warning("An error occurred while sanity checking device #%d; %s: %s",
+                            deviceInfoList[i]->id, cudaGetErrorName(stat), cudaGetErrorString(stat));
+            }
+        }
+    }
+
+    stat = cudaPeekAtLastError();
+    GMX_RELEASE_ASSERT(stat == cudaSuccess,
+                       gmx::formatString("We promise to return with clean CUDA state, but "
+                                         "non-success state encountered: %s: %s",
+                                         cudaGetErrorName(stat), cudaGetErrorString(stat))
+                               .c_str());
+
+    return deviceInfoList;
+}
+
+void setActiveDevice(const DeviceInformation& deviceInfo)
+{
+    int         deviceId = deviceInfo.id;
+    cudaError_t stat;
+
+    stat = cudaSetDevice(deviceId);
+    if (stat != cudaSuccess)
+    {
+        auto message = gmx::formatString("Failed to initialize GPU #%d", deviceId);
+        CU_RET_ERR(stat, message.c_str());
+    }
+
+    if (debug)
+    {
+        fprintf(stderr, "Initialized GPU ID #%d: %s\n", deviceId, deviceInfo.prop.name);
+    }
+}
+
+void releaseDevice(DeviceInformation* deviceInfo)
+{
+    // device was used is that deviceInfo will be non-null.
+    if (deviceInfo != nullptr)
+    {
+        cudaError_t stat;
+
+        int gpuid;
+        stat = cudaGetDevice(&gpuid);
+        if (stat == cudaSuccess)
+        {
+            if (debug)
+            {
+                fprintf(stderr, "Cleaning up context on GPU ID #%d\n", gpuid);
+            }
+
+            stat = cudaDeviceReset();
+            if (stat != cudaSuccess)
+            {
+                gmx_warning("Failed to free GPU #%d: %s", gpuid, cudaGetErrorString(stat));
+            }
+        }
+    }
+}
+
+std::string getDeviceInformationString(const DeviceInformation& deviceInfo)
+{
+    bool gpuExists = (deviceInfo.status != DeviceStatus::Nonexistent
+                      && deviceInfo.status != DeviceStatus::NonFunctional);
+
+    if (!gpuExists)
+    {
+        return gmx::formatString("#%d: %s, stat: %s", deviceInfo.id, "N/A",
+                                 c_deviceStateString[deviceInfo.status]);
+    }
+    else
+    {
+        return gmx::formatString("#%d: NVIDIA %s, compute cap.: %d.%d, ECC: %3s, stat: %s",
+                                 deviceInfo.id, deviceInfo.prop.name, deviceInfo.prop.major,
+                                 deviceInfo.prop.minor, deviceInfo.prop.ECCEnabled ? "yes" : " no",
+                                 c_deviceStateString[deviceInfo.status]);
+    }
+}
diff --git a/src/gromacs/hardware/device_management.h b/src/gromacs/hardware/device_management.h
new file mode 100644 (file)
index 0000000..60cb54a
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declares functions to manage GPU resources.
+ *
+ *  This has several implementations: one for each supported GPU platform,
+ *  and a stub implementation if the build does not support GPUs.
+ *
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Teemu Virolainen <teemu@streamcomputing.eu>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_hardware
+ */
+#ifndef GMX_HARDWARE_DEVICE_MANAGEMENT_H
+#define GMX_HARDWARE_DEVICE_MANAGEMENT_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/iserializer.h"
+
+struct DeviceInformation;
+enum class DeviceVendor : int;
+
+/*! \brief Return whether GPUs can be detected.
+ *
+ * Returns true when this is a build of GROMACS configured to support
+ * GPU usage, GPU detection is not disabled by \c GMX_DISABLE_GPU_DETECTION
+ * environment variable and a valid device driver, ICD, and/or runtime was
+ * detected. Does not throw.
+ *
+ * \param[out] errorMessage  When returning false on a build configured with
+ *                           GPU support and non-nullptr was passed,
+ *                           the string contains a descriptive message about
+ *                           why GPUs cannot be detected.
+ */
+bool canPerformDeviceDetection(std::string* errorMessage);
+
+/*! \brief Return whether GPU detection is enabled
+ *
+ * Returns true when this is a build of GROMACS configured to support
+ * GPU usage and GPU detection is not disabled by \c GMX_DISABLE_GPU_DETECTION
+ * environment variable.
+ *
+ * Does not throw.
+ */
+bool isDeviceDetectionEnabled();
+
+/*! \brief Return whether GPU detection is functioning correctly
+ *
+ * Returns true when this is a build of GROMACS configured to support
+ * GPU usage, and a valid device driver, ICD, and/or runtime was detected.
+ *
+ * This function is not intended to be called from build
+ * configurations that do not support GPUs, and there will be no
+ * descriptive message in that case.
+ *
+ * \param[out] errorMessage  When returning false on a build configured with
+ *                           GPU support and non-nullptr was passed,
+ *                           the string contains a descriptive message about
+ *                           why GPUs cannot be detected.
+ *
+ * Does not throw.
+ */
+bool isDeviceDetectionFunctional(std::string* errorMessage);
+
+/*! \brief Returns an DeviceVendor value corresponding to the input OpenCL vendor name.
+ *
+ *  \returns               DeviceVendor value for the input vendor name
+ */
+DeviceVendor getDeviceVendor(const char* vendorName);
+
+/*! \brief Find all GPUs in the system.
+ *
+ *  Will detect every GPU supported by the device driver in use.
+ *  Must only be called if \c canPerformDeviceDetection() has returned true.
+ *  This routine also checks for the compatibility of each device and fill the
+ *  deviceInfo array with the required information on each device: ID, device
+ *  properties, status.
+ *
+ *  Note that this function leaves the GPU runtime API error state clean;
+ *  this is implemented ATM in the CUDA flavor. This invalidates any existing
+ *  CUDA streams, allocated memory on GPU, etc.
+ *
+ *  \todo:  Check if errors do propagate in OpenCL as they do in CUDA and
+ *          whether there is a mechanism to "clear" them.
+ *
+ * \return  Standard vector with the list of devices found
+ *
+ *  \throws InternalError if a GPU API returns an unexpected failure (because
+ *          the call to canDetectGpus() should always prevent this occuring)
+ */
+std::vector<std::unique_ptr<DeviceInformation>> findDevices();
+
+/*! \brief Return a container of device-information handles that are compatible.
+ *
+ * This function filters the result of the detection for compatible
+ * GPUs, based on the previously run compatibility tests.
+ *
+ * \param[in] deviceInfoList An information on available devices.
+ *
+ * \return  Vector of DeviceInformations on GPUs recorded as compatible
+ */
+std::vector<std::reference_wrapper<DeviceInformation>>
+getCompatibleDevices(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList);
+
+/*! \brief Return a container of the IDs of the compatible GPU ids.
+ *
+ * This function filters the result of the detection for compatible
+ * GPUs, based on the previously run compatibility tests.
+ *
+ * \param[in] deviceInfoList An information on available devices.
+ *
+ * \return  Vector of compatible GPU ids.
+ */
+std::vector<int> getCompatibleDeviceIds(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList);
+
+/*! \brief Return whether \p deviceId is found in \p deviceInfoList and is compatible
+ *
+ * This function filters the result of the detection for compatible
+ * GPUs, based on the previously run compatibility tests.
+ *
+ * \param[in] deviceInfoList An information on available devices.
+ * \param[in] deviceId       The device ID to find in the list.
+ *
+ * \throws RangeError If \p deviceId does not match the id of any device in \c deviceInfoList
+ *
+ * \return  Whether \c deviceId is compatible.
+ */
+bool deviceIdIsCompatible(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                          int                                                    deviceId);
+
+/*! \brief Set the active GPU.
+ *
+ * This sets the device for which the device information is passed active. Essential in CUDA, where
+ * the device buffers and kernel launches are not connected to the device context. In OpenCL, checks
+ * the device vendor and makes vendor-specific performance adjustments.
+ *
+ * \param[in] deviceInfo Information on the device to be set.
+ *
+ * Issues a fatal error for any critical errors that occur during
+ * initialization.
+ */
+void setActiveDevice(const DeviceInformation& deviceInfo);
+
+/*! \brief Releases the GPU device used by the active context at the time of calling (CUDA only).
+ *
+ * If \c deviceInfo is nullptr, then it is understood that no device
+ * was selected so no context is active to be freed. Otherwise, the
+ * context is explicitly destroyed and therefore all data uploaded to
+ * the GPU is lost. This must only be called when none of this data is
+ * required anymore, because subsequent attempts to free memory
+ * associated with the context will otherwise fail.
+ *
+ * Calls \c gmx_warning upon errors.
+ *
+ * \todo This should go through all the devices, not only the one currently active.
+ *       Reseting only one device will not work, e.g. in CUDA tests.
+ *
+ * \param[in] deviceInfo Information on the device to be released.
+ */
+void releaseDevice(DeviceInformation* deviceInfo);
+
+/*! \brief Formats and returns a device information string for a given GPU.
+ *
+ * Given an index *directly* into the array of available GPUs, returns
+ * a formatted info string for the respective GPU which includes ID, name,
+ * compute capability, and detection status.
+ *
+ * \param[in] deviceInfo  An information on device that is to be set.
+ *
+ * \returns A string describing the device.
+ */
+std::string getDeviceInformationString(const DeviceInformation& deviceInfo);
+
+/*! \brief Return a string describing how compatible the GPU with given \c deviceId is.
+ *
+ * \param[in] deviceInfoList An information on available devices.
+ * \param[in] deviceId       An index of the device to check
+ * \returns                  A string describing the compatibility status, useful for error messages.
+ */
+std::string getDeviceCompatibilityDescription(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                                              int deviceId);
+
+/*! \brief Serialization of information on devices for MPI broadcasting.
+ *
+ * \param[in] deviceInfoList  The vector with device informations to serialize.
+ * \param[in] serializer      Serializing object.
+ */
+void serializeDeviceInformations(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                                 gmx::ISerializer*                                      serializer);
+
+/*! \brief Deserialization of information on devices after MPI broadcasting.
+ *
+ * \param[in] serializer Serializing object.
+ *
+ * \return deviceInfoList   Deserialized vector with device informations.
+ */
+std::vector<std::unique_ptr<DeviceInformation>> deserializeDeviceInformations(gmx::ISerializer* serializer);
+
+#endif // GMX_HARDWARE_DEVICE_MANAGEMENT_H
diff --git a/src/gromacs/hardware/device_management_common.cpp b/src/gromacs/hardware/device_management_common.cpp
new file mode 100644 (file)
index 0000000..1392020
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *  \brief Defines the implementations of device management functions that
+ *         are common for CPU, CUDA and OpenCL.
+ *
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Teemu Virolainen <teemu@streamcomputing.eu>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_hardware
+ */
+#include "gmxpre.h"
+
+#include <algorithm>
+
+#include "gromacs/hardware/device_management.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fatalerror.h"
+
+#include "device_information.h"
+
+bool canPerformDeviceDetection(std::string* errorMessage)
+{
+    return isDeviceDetectionEnabled() && isDeviceDetectionFunctional(errorMessage);
+}
+
+bool isDeviceDetectionEnabled()
+{
+    if (c_binarySupportsGpus)
+    {
+        return getenv("GMX_DISABLE_GPU_DETECTION") == nullptr;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+DeviceVendor getDeviceVendor(const char* vendorName)
+{
+    if (vendorName)
+    {
+        if (strstr(vendorName, "NVIDIA"))
+        {
+            return DeviceVendor::Nvidia;
+        }
+        else if (strstr(vendorName, "AMD") || strstr(vendorName, "Advanced Micro Devices"))
+        {
+            return DeviceVendor::Amd;
+        }
+        else if (strstr(vendorName, "Intel"))
+        {
+            return DeviceVendor::Intel;
+        }
+    }
+    return DeviceVendor::Unknown;
+}
+
+
+std::vector<std::reference_wrapper<DeviceInformation>>
+getCompatibleDevices(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList)
+{
+    // Possible minor over-allocation here, but not important for anything
+    std::vector<std::reference_wrapper<DeviceInformation>> compatibleDeviceInfoList;
+    compatibleDeviceInfoList.reserve(deviceInfoList.size());
+    for (const auto& deviceInfo : deviceInfoList)
+    {
+        if (deviceInfo->status == DeviceStatus::Compatible)
+        {
+            compatibleDeviceInfoList.emplace_back(*deviceInfo);
+        }
+    }
+    return compatibleDeviceInfoList;
+}
+
+std::vector<int> getCompatibleDeviceIds(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList)
+{
+    // Possible minor over-allocation here, but not important for anything
+    std::vector<int> compatibleDeviceIds;
+    compatibleDeviceIds.reserve(deviceInfoList.size());
+    for (const auto& deviceInfo : deviceInfoList)
+    {
+        if (deviceInfo->status == DeviceStatus::Compatible)
+        {
+            compatibleDeviceIds.emplace_back(deviceInfo->id);
+        }
+    }
+    return compatibleDeviceIds;
+}
+
+bool deviceIdIsCompatible(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                          const int                                              deviceId)
+{
+    auto foundIt = std::find_if(deviceInfoList.begin(), deviceInfoList.end(),
+                                [deviceId](auto& deviceInfo) { return deviceInfo->id == deviceId; });
+    if (foundIt == deviceInfoList.end())
+    {
+        GMX_THROW(gmx::RangeError(gmx::formatString(
+                "Device ID %d did not correspond to any of the %zu detected device(s)", deviceId,
+                deviceInfoList.size())));
+    }
+    return (*foundIt)->status == DeviceStatus::Compatible;
+}
+
+std::string getDeviceCompatibilityDescription(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                                              int deviceId)
+{
+    return (deviceId >= static_cast<int>(deviceInfoList.size())
+                    ? c_deviceStateString[DeviceStatus::Nonexistent]
+                    : c_deviceStateString[deviceInfoList[deviceId]->status]);
+}
+
+void serializeDeviceInformations(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                                 gmx::ISerializer*                                      serializer)
+{
+    GMX_RELEASE_ASSERT(c_canSerializeDeviceInformation,
+                       "DeviceInformation for OpenCL/SYCL can not be serialized");
+    int numDevices = deviceInfoList.size();
+    serializer->doInt(&numDevices);
+    for (auto& deviceInfo : deviceInfoList)
+    {
+        serializer->doOpaque(reinterpret_cast<char*>(deviceInfo.get()), sizeof(DeviceInformation));
+    }
+}
+
+std::vector<std::unique_ptr<DeviceInformation>> deserializeDeviceInformations(gmx::ISerializer* serializer)
+{
+    GMX_RELEASE_ASSERT(c_canSerializeDeviceInformation,
+                       "DeviceInformation for OpenCL/SYCL can not be deserialized");
+    int numDevices = 0;
+    serializer->doInt(&numDevices);
+    std::vector<std::unique_ptr<DeviceInformation>> deviceInfoList(numDevices);
+    for (int i = 0; i < numDevices; i++)
+    {
+        deviceInfoList[i] = std::make_unique<DeviceInformation>();
+        serializer->doOpaque(reinterpret_cast<char*>(deviceInfoList[i].get()), sizeof(DeviceInformation));
+    }
+    return deviceInfoList;
+}
similarity index 60%
rename from src/gromacs/gpu_utils/gpu_utils_ocl.cpp
rename to src/gromacs/hardware/device_management_ocl.cpp
index 29fa7b0f72f3d026669afbc64206fb5f2eff17cc..3c25f6419999de0ff796402a7d8ec79f2df17948 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \internal \file
- *  \brief Define functions for detection and initialization for OpenCL devices.
+ *  \brief Defines the OpenCL implementations of the device management.
  *
  *  \author Anca Hamuraru <anca@streamcomputing.eu>
  *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
  *  \author Teemu Virolainen <teemu@streamcomputing.eu>
  *  \author Mark Abraham <mark.j.abraham@gmail.com>
  *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_hardware
  */
-
 #include "gmxpre.h"
 
 #include "config.h"
 
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <cstdio>
-#ifdef __APPLE__
-#    include <sys/sysctl.h>
-#endif
-
-#include <memory.h>
-
-#include "gromacs/gpu_utils/gpu_utils.h"
-#include "gromacs/gpu_utils/ocl_compiler.h"
 #include "gromacs/gpu_utils/oclraii.h"
 #include "gromacs/gpu_utils/oclutils.h"
-#include "gromacs/hardware/hw_info.h"
-#include "gromacs/utility/cstringutil.h"
-#include "gromacs/utility/exceptions.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/stringutil.h"
 
+#include "device_information.h"
+
+namespace gmx
+{
+
 /*! \brief Return true if executing on compatible OS for AMD OpenCL.
  *
  * This is assumed to be true for OS X version of at least 10.10.4 and
  * all other OS flavors.
  *
- * Uses the BSD sysctl() interfaces to extract the kernel version.
- *
  * \return true if version is 14.4 or later (= OS X version 10.10.4),
  *         or OS is not Darwin.
  */
@@ -87,9 +77,6 @@ static bool runningOnCompatibleOSForAmd()
     size_t len = sizeof(kernelVersion);
 
     mib[0] = CTL_KERN;
-    mib[1] = KERN_OSRELEASE;
-
-    sysctl(mib, sizeof(mib) / sizeof(mib[0]), kernelVersion, &len, NULL, 0);
 
     int major = strtod(kernelVersion, NULL);
     int minor = strtod(strchr(kernelVersion, '.') + 1, NULL);
@@ -101,53 +88,142 @@ static bool runningOnCompatibleOSForAmd()
 #endif
 }
 
-namespace gmx
+/*! \brief Return true if executing on compatible GPU for NVIDIA OpenCL.
+ *
+ * There are known issues with OpenCL when running on NVIDIA Volta or newer (CC 7+).
+ * As a workaround, we recommend using CUDA on such hardware.
+ *
+ * This function relies on cl_nv_device_attribute_query. In case it's not functioning properly,
+ * we trust the user and mark the device as compatible.
+ *
+ * \return true if running on Pascal (CC 6.x) or older, or if we can not determine device generation.
+ */
+static bool runningOnCompatibleHWForNvidia(const DeviceInformation& deviceInfo)
 {
+    // The macro is defined in Intel's and AMD's headers, but it's not strictly required to be there.
+#ifndef CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV
+    return true;
+#else
+    static const unsigned int ccMajorBad = 7; // Volta and Turing
+    unsigned int              ccMajor;
+    cl_device_id              devId = deviceInfo.oclDeviceId;
+    const cl_int              err   = clGetDeviceInfo(devId, CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV,
+                                       sizeof(ccMajor), &ccMajor, nullptr);
+    if (err != CL_SUCCESS)
+    {
+        return true; // Err on a side of trusting the user to know what they are doing.
+    }
+    return ccMajor < ccMajorBad;
+#endif
+}
+
+/*!
+ * \brief Checks that device \c deviceInfo is compatible with GROMACS.
+ *
+ *  Vendor and OpenCL version support checks are executed an the result
+ *  of these returned.
+ *
+ * \param[in]  deviceInfo  The device info pointer.
+ * \returns                The status enumeration value for the checked device:
+ */
+static DeviceStatus isDeviceFunctional(const DeviceInformation& deviceInfo)
+{
+    if (getenv("GMX_GPU_DISABLE_COMPATIBILITY_CHECK") != nullptr)
+    {
+        // Assume the device is compatible because checking has been disabled.
+        return DeviceStatus::Compatible;
+    }
+    if (getenv("GMX_OCL_DISABLE_COMPATIBILITY_CHECK") != nullptr)
+    {
+        fprintf(stderr,
+                "Environment variable GMX_OCL_DISABLE_COMPATIBILITY_CHECK is deprecated and will "
+                "be removed in release 2022. Please use GMX_GPU_DISABLE_COMPATIBILITY_CHECK "
+                "instead.\n");
+        return DeviceStatus::Compatible;
+    }
+
+    // OpenCL device version check, ensure >= REQUIRED_OPENCL_MIN_VERSION
+    constexpr unsigned int minVersionMajor = REQUIRED_OPENCL_MIN_VERSION_MAJOR;
+    constexpr unsigned int minVersionMinor = REQUIRED_OPENCL_MIN_VERSION_MINOR;
+
+    // Based on the OpenCL spec we're checking the version supported by
+    // the device which has the following format:
+    //      OpenCL<space><major_version.minor_version><space><vendor-specific information>
+    unsigned int deviceVersionMinor, deviceVersionMajor;
+    const int    valuesScanned = std::sscanf(deviceInfo.device_version, "OpenCL %u.%u",
+                                          &deviceVersionMajor, &deviceVersionMinor);
+    const bool   versionLargeEnough =
+            ((valuesScanned == 2)
+             && ((deviceVersionMajor > minVersionMajor)
+                 || (deviceVersionMajor == minVersionMajor && deviceVersionMinor >= minVersionMinor)));
+    if (!versionLargeEnough)
+    {
+        return DeviceStatus::Incompatible;
+    }
+
+    /* Only AMD, Intel, and NVIDIA GPUs are supported for now */
+    switch (deviceInfo.deviceVendor)
+    {
+        case DeviceVendor::Nvidia:
+            return runningOnCompatibleHWForNvidia(deviceInfo) ? DeviceStatus::Compatible
+                                                              : DeviceStatus::IncompatibleNvidiaVolta;
+        case DeviceVendor::Amd:
+            return runningOnCompatibleOSForAmd() ? DeviceStatus::Compatible : DeviceStatus::Incompatible;
+        case DeviceVendor::Intel:
+            return GMX_OPENCL_NB_CLUSTER_SIZE == 4 ? DeviceStatus::Compatible
+                                                   : DeviceStatus::IncompatibleClusterSize;
+        default: return DeviceStatus::Incompatible;
+    }
+}
 
 /*! \brief Make an error string following an OpenCL API call.
  *
  *  It is meant to be called with \p status != CL_SUCCESS, but it will
  *  work correctly even if it is called with no OpenCL failure.
  *
+ * \todo Make use of this function more.
+ *
  * \param[in]  message  Supplies context, e.g. the name of the API call that returned the error.
  * \param[in]  status   OpenCL API status code
  * \returns             A string describing the OpenCL error.
  */
-static std::string makeOpenClInternalErrorString(const char* message, cl_int status)
+inline std::string makeOpenClInternalErrorString(const char* message, cl_int status)
 {
     if (message != nullptr)
     {
-        return formatString("%s did %ssucceed %d: %s", message, ((status != CL_SUCCESS) ? "not " : ""),
-                            status, ocl_get_error_string(status).c_str());
+        return gmx::formatString("%s did %ssucceed %d: %s", message,
+                                 ((status != CL_SUCCESS) ? "not " : ""), status,
+                                 ocl_get_error_string(status).c_str());
     }
     else
     {
-        return formatString("%sOpenCL error encountered %d: %s", ((status != CL_SUCCESS) ? "" : "No "),
-                            status, ocl_get_error_string(status).c_str());
+        return gmx::formatString("%sOpenCL error encountered %d: %s",
+                                 ((status != CL_SUCCESS) ? "" : "No "), status,
+                                 ocl_get_error_string(status).c_str());
     }
 }
 
 /*!
- * \brief Checks that device \c devInfo is sane (ie can run a kernel).
+ * \brief Checks that device \c deviceInfo is sane (ie can run a kernel).
  *
  * Compiles and runs a dummy kernel to determine whether the given
  * OpenCL device functions properly.
  *
  *
- * \param[in]  devInfo         The device info pointer.
+ * \param[in]  deviceInfo      The device info pointer.
  * \param[out] errorMessage    An error message related to a failing OpenCL API call.
  * \throws     std::bad_alloc  When out of memory.
  * \returns                    Whether the device passed sanity checks
  */
-static bool isDeviceSane(const gmx_device_info_t* devInfo, std::string* errorMessage)
+static bool isDeviceFunctional(const DeviceInformation& deviceInfo, std::string* errorMessage)
 {
     cl_context_properties properties[] = {
-        CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>(devInfo->ocl_gpu_id.ocl_platform_id), 0
+        CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>(deviceInfo.oclPlatformId), 0
     };
     // uncrustify spacing
 
     cl_int    status;
-    auto      deviceId = devInfo->ocl_gpu_id.ocl_device_id;
+    auto      deviceId = deviceInfo.oclDeviceId;
     ClContext context(clCreateContext(properties, 1, &deviceId, nullptr, nullptr, &status));
     if (status != CL_SUCCESS)
     {
@@ -196,55 +272,6 @@ static bool isDeviceSane(const gmx_device_info_t* devInfo, std::string* errorMes
     return true;
 }
 
-/*!
- * \brief Checks that device \c devInfo is compatible with GROMACS.
- *
- *  Vendor and OpenCL version support checks are executed an the result
- *  of these returned.
- *
- * \param[in]  devInfo         The device info pointer.
- * \returns                    The result of the compatibility checks.
- */
-static int isDeviceSupported(const gmx_device_info_t* devInfo)
-{
-    if (getenv("GMX_OCL_DISABLE_COMPATIBILITY_CHECK") != nullptr)
-    {
-        // Assume the device is compatible because checking has been disabled.
-        return egpuCompatible;
-    }
-
-    // OpenCL device version check, ensure >= REQUIRED_OPENCL_MIN_VERSION
-    constexpr unsigned int minVersionMajor = REQUIRED_OPENCL_MIN_VERSION_MAJOR;
-    constexpr unsigned int minVersionMinor = REQUIRED_OPENCL_MIN_VERSION_MINOR;
-
-    // Based on the OpenCL spec we're checking the version supported by
-    // the device which has the following format:
-    //      OpenCL<space><major_version.minor_version><space><vendor-specific information>
-    unsigned int deviceVersionMinor, deviceVersionMajor;
-    const int    valuesScanned = std::sscanf(devInfo->device_version, "OpenCL %u.%u",
-                                          &deviceVersionMajor, &deviceVersionMinor);
-    const bool   versionLargeEnough =
-            ((valuesScanned == 2)
-             && ((deviceVersionMajor > minVersionMajor)
-                 || (deviceVersionMajor == minVersionMajor && deviceVersionMinor >= minVersionMinor)));
-    if (!versionLargeEnough)
-    {
-        return egpuIncompatible;
-    }
-
-    /* Only AMD, Intel, and NVIDIA GPUs are supported for now */
-    switch (devInfo->vendor_e)
-    {
-        case OCL_VENDOR_NVIDIA: return egpuCompatible;
-        case OCL_VENDOR_AMD:
-            return runningOnCompatibleOSForAmd() ? egpuCompatible : egpuIncompatible;
-        case OCL_VENDOR_INTEL:
-            return GMX_OPENCL_NB_CLUSTER_SIZE == 4 ? egpuCompatible : egpuIncompatibleClusterSize;
-        default: return egpuIncompatible;
-    }
-}
-
-
 /*! \brief Check whether the \c ocl_gpu_device is suitable for use by mdrun
  *
  * Runs sanity checks: checking that the runtime can compile a dummy kernel
@@ -254,56 +281,31 @@ static int isDeviceSupported(const gmx_device_info_t* devInfo)
  *
  * \param[in]  deviceId      The runtime-reported numeric ID of the device.
  * \param[in]  deviceInfo    The device info pointer.
- * \returns  An e_gpu_detect_res_t to indicate how the GPU coped with
- *           the sanity and compatibility check.
+ * \returns  A DeviceStatus to indicate if the GPU device is supported and if it was able to run
+ *           basic functionality checks.
  */
-static int checkGpu(size_t deviceId, const gmx_device_info_t* deviceInfo)
+static DeviceStatus checkGpu(size_t deviceId, const DeviceInformation& deviceInfo)
 {
 
-    int supportStatus = isDeviceSupported(deviceInfo);
-    if (supportStatus != egpuCompatible)
+    DeviceStatus supportStatus = isDeviceFunctional(deviceInfo);
+    if (supportStatus != DeviceStatus::Compatible)
     {
         return supportStatus;
     }
 
     std::string errorMessage;
-    if (!isDeviceSane(deviceInfo, &errorMessage))
+    if (!isDeviceFunctional(deviceInfo, &errorMessage))
     {
         gmx_warning("While sanity checking device #%zu, %s", deviceId, errorMessage.c_str());
-        return egpuInsane;
+        return DeviceStatus::NonFunctional;
     }
 
-    return egpuCompatible;
+    return DeviceStatus::Compatible;
 }
 
 } // namespace gmx
 
-/*! \brief Returns an ocl_vendor_id_t value corresponding to the input OpenCL vendor name.
- *
- *  \param[in] vendor_name String with OpenCL vendor name.
- *  \returns               ocl_vendor_id_t value for the input vendor_name
- */
-static ocl_vendor_id_t get_vendor_id(char* vendor_name)
-{
-    if (vendor_name)
-    {
-        if (strstr(vendor_name, "NVIDIA"))
-        {
-            return OCL_VENDOR_NVIDIA;
-        }
-        else if (strstr(vendor_name, "AMD") || strstr(vendor_name, "Advanced Micro Devices"))
-        {
-            return OCL_VENDOR_AMD;
-        }
-        else if (strstr(vendor_name, "Intel"))
-        {
-            return OCL_VENDOR_INTEL;
-        }
-    }
-    return OCL_VENDOR_UNKNOWN;
-}
-
-bool isGpuDetectionFunctional(std::string* errorMessage)
+bool isDeviceDetectionFunctional(std::string* errorMessage)
 {
     cl_uint numPlatforms;
     cl_int  status = clGetPlatformIDs(0, nullptr, &numPlatforms);
@@ -332,7 +334,7 @@ bool isGpuDetectionFunctional(std::string* errorMessage)
     return foundPlatform;
 }
 
-void findGpus(gmx_gpu_info_t* gpu_info)
+std::vector<std::unique_ptr<DeviceInformation>> findDevices()
 {
     cl_uint         ocl_platform_count;
     cl_platform_id* ocl_platform_ids;
@@ -345,6 +347,9 @@ void findGpus(gmx_gpu_info_t* gpu_info)
         req_dev_type = CL_DEVICE_TYPE_CPU;
     }
 
+    int                                             numDevices = 0;
+    std::vector<std::unique_ptr<DeviceInformation>> deviceInfoList(0);
+
     while (true)
     {
         cl_int status = clGetPlatformIDs(0, nullptr, &ocl_platform_count);
@@ -383,22 +388,22 @@ void findGpus(gmx_gpu_info_t* gpu_info)
 
             if (1 <= ocl_device_count)
             {
-                gpu_info->n_dev += ocl_device_count;
+                numDevices += ocl_device_count;
             }
         }
 
-        if (1 > gpu_info->n_dev)
+        if (1 > numDevices)
         {
             break;
         }
 
-        snew(gpu_info->gpu_dev, gpu_info->n_dev);
+        deviceInfoList.resize(numDevices);
 
         {
             int           device_index;
             cl_device_id* ocl_device_ids;
 
-            snew(ocl_device_ids, gpu_info->n_dev);
+            snew(ocl_device_ids, numDevices);
             device_index = 0;
 
             for (unsigned int i = 0; i < ocl_platform_count; i++)
@@ -407,8 +412,8 @@ void findGpus(gmx_gpu_info_t* gpu_info)
 
                 /* If requesting req_dev_type devices fails, just go to the next platform */
                 if (CL_SUCCESS
-                    != clGetDeviceIDs(ocl_platform_ids[i], req_dev_type, gpu_info->n_dev,
-                                      ocl_device_ids, &ocl_device_count))
+                    != clGetDeviceIDs(ocl_platform_ids[i], req_dev_type, numDevices, ocl_device_ids,
+                                      &ocl_device_count))
                 {
                     continue;
                 }
@@ -420,93 +425,86 @@ void findGpus(gmx_gpu_info_t* gpu_info)
 
                 for (unsigned int j = 0; j < ocl_device_count; j++)
                 {
-                    gpu_info->gpu_dev[device_index].ocl_gpu_id.ocl_platform_id = ocl_platform_ids[i];
-                    gpu_info->gpu_dev[device_index].ocl_gpu_id.ocl_device_id   = ocl_device_ids[j];
+                    deviceInfoList[device_index] = std::make_unique<DeviceInformation>();
+
+                    deviceInfoList[device_index]->id = device_index;
+
+                    deviceInfoList[device_index]->oclPlatformId = ocl_platform_ids[i];
+                    deviceInfoList[device_index]->oclDeviceId   = ocl_device_ids[j];
 
-                    gpu_info->gpu_dev[device_index].device_name[0] = 0;
+                    deviceInfoList[device_index]->device_name[0] = 0;
                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_NAME,
-                                    sizeof(gpu_info->gpu_dev[device_index].device_name),
-                                    gpu_info->gpu_dev[device_index].device_name, nullptr);
+                                    sizeof(deviceInfoList[device_index]->device_name),
+                                    deviceInfoList[device_index]->device_name, nullptr);
 
-                    gpu_info->gpu_dev[device_index].device_version[0] = 0;
+                    deviceInfoList[device_index]->device_version[0] = 0;
                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_VERSION,
-                                    sizeof(gpu_info->gpu_dev[device_index].device_version),
-                                    gpu_info->gpu_dev[device_index].device_version, nullptr);
+                                    sizeof(deviceInfoList[device_index]->device_version),
+                                    deviceInfoList[device_index]->device_version, nullptr);
 
-                    gpu_info->gpu_dev[device_index].device_vendor[0] = 0;
+                    deviceInfoList[device_index]->vendorName[0] = 0;
                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_VENDOR,
-                                    sizeof(gpu_info->gpu_dev[device_index].device_vendor),
-                                    gpu_info->gpu_dev[device_index].device_vendor, nullptr);
+                                    sizeof(deviceInfoList[device_index]->vendorName),
+                                    deviceInfoList[device_index]->vendorName, nullptr);
 
-                    gpu_info->gpu_dev[device_index].compute_units = 0;
+                    deviceInfoList[device_index]->compute_units = 0;
                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_MAX_COMPUTE_UNITS,
-                                    sizeof(gpu_info->gpu_dev[device_index].compute_units),
-                                    &(gpu_info->gpu_dev[device_index].compute_units), nullptr);
+                                    sizeof(deviceInfoList[device_index]->compute_units),
+                                    &(deviceInfoList[device_index]->compute_units), nullptr);
 
-                    gpu_info->gpu_dev[device_index].adress_bits = 0;
+                    deviceInfoList[device_index]->adress_bits = 0;
                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_ADDRESS_BITS,
-                                    sizeof(gpu_info->gpu_dev[device_index].adress_bits),
-                                    &(gpu_info->gpu_dev[device_index].adress_bits), nullptr);
+                                    sizeof(deviceInfoList[device_index]->adress_bits),
+                                    &(deviceInfoList[device_index]->adress_bits), nullptr);
 
-                    gpu_info->gpu_dev[device_index].vendor_e =
-                            get_vendor_id(gpu_info->gpu_dev[device_index].device_vendor);
+                    deviceInfoList[device_index]->deviceVendor =
+                            getDeviceVendor(deviceInfoList[device_index]->vendorName);
 
                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_MAX_WORK_ITEM_SIZES, 3 * sizeof(size_t),
-                                    &gpu_info->gpu_dev[device_index].maxWorkItemSizes, nullptr);
+                                    &deviceInfoList[device_index]->maxWorkItemSizes, nullptr);
 
                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t),
-                                    &gpu_info->gpu_dev[device_index].maxWorkGroupSize, nullptr);
+                                    &deviceInfoList[device_index]->maxWorkGroupSize, nullptr);
 
-                    gpu_info->gpu_dev[device_index].stat =
-                            gmx::checkGpu(device_index, gpu_info->gpu_dev + device_index);
-
-                    if (egpuCompatible == gpu_info->gpu_dev[device_index].stat)
-                    {
-                        gpu_info->n_dev_compatible++;
-                    }
+                    deviceInfoList[device_index]->status =
+                            gmx::checkGpu(device_index, *deviceInfoList[device_index]);
 
                     device_index++;
                 }
             }
 
-            gpu_info->n_dev = device_index;
+            numDevices = device_index;
 
             /* Dummy sort of devices -  AMD first, then NVIDIA, then Intel */
             // TODO: Sort devices based on performance.
-            if (0 < gpu_info->n_dev)
+            if (0 < numDevices)
             {
                 int last = -1;
-                for (int i = 0; i < gpu_info->n_dev; i++)
+                for (int i = 0; i < numDevices; i++)
                 {
-                    if (OCL_VENDOR_AMD == gpu_info->gpu_dev[i].vendor_e)
+                    if (deviceInfoList[i]->deviceVendor == DeviceVendor::Amd)
                     {
                         last++;
 
                         if (last < i)
                         {
-                            gmx_device_info_t ocl_gpu_info;
-                            ocl_gpu_info            = gpu_info->gpu_dev[i];
-                            gpu_info->gpu_dev[i]    = gpu_info->gpu_dev[last];
-                            gpu_info->gpu_dev[last] = ocl_gpu_info;
+                            std::swap(deviceInfoList[i], deviceInfoList[last]);
                         }
                     }
                 }
 
                 /* if more than 1 device left to be sorted */
-                if ((gpu_info->n_dev - 1 - last) > 1)
+                if ((numDevices - 1 - last) > 1)
                 {
-                    for (int i = 0; i < gpu_info->n_dev; i++)
+                    for (int i = 0; i < numDevices; i++)
                     {
-                        if (OCL_VENDOR_NVIDIA == gpu_info->gpu_dev[i].vendor_e)
+                        if (deviceInfoList[i]->deviceVendor == DeviceVendor::Nvidia)
                         {
                             last++;
 
                             if (last < i)
                             {
-                                gmx_device_info_t ocl_gpu_info;
-                                ocl_gpu_info            = gpu_info->gpu_dev[i];
-                                gpu_info->gpu_dev[i]    = gpu_info->gpu_dev[last];
-                                gpu_info->gpu_dev[last] = ocl_gpu_info;
+                                std::swap(deviceInfoList[i], deviceInfoList[last]);
                             }
                         }
                     }
@@ -520,43 +518,17 @@ void findGpus(gmx_gpu_info_t* gpu_info)
     }
 
     sfree(ocl_platform_ids);
+    return deviceInfoList;
 }
 
-void get_gpu_device_info_string(char* s, const gmx_gpu_info_t& gpu_info, int index)
+void setActiveDevice(const DeviceInformation& deviceInfo)
 {
-    assert(s);
-
-    if (index < 0 && index >= gpu_info.n_dev)
-    {
-        return;
-    }
-
-    gmx_device_info_t* dinfo = &gpu_info.gpu_dev[index];
-
-    bool bGpuExists = (dinfo->stat != egpuNonexistent && dinfo->stat != egpuInsane);
-
-    if (!bGpuExists)
-    {
-        sprintf(s, "#%d: %s, stat: %s", index, "N/A", gpu_detect_res_str[dinfo->stat]);
-    }
-    else
-    {
-        sprintf(s, "#%d: name: %s, vendor: %s, device version: %s, stat: %s", index, dinfo->device_name,
-                dinfo->device_vendor, dinfo->device_version, gpu_detect_res_str[dinfo->stat]);
-    }
-}
-
-
-void init_gpu(const gmx_device_info_t* deviceInfo)
-{
-    assert(deviceInfo);
-
     // If the device is NVIDIA, for safety reasons we disable the JIT
     // caching as this is known to be broken at least until driver 364.19;
     // the cache does not always get regenerated when the source code changes,
     // e.g. if the path to the kernel sources remains the same
 
-    if (deviceInfo->vendor_e == OCL_VENDOR_NVIDIA)
+    if (deviceInfo.deviceVendor == DeviceVendor::Nvidia)
     {
         // Ignore return values, failing to set the variable does not mean
         // that something will go wrong later.
@@ -569,21 +541,22 @@ void init_gpu(const gmx_device_info_t* deviceInfo)
     }
 }
 
-gmx_device_info_t* getDeviceInfo(const gmx_gpu_info_t& gpu_info, int deviceId)
-{
-    if (deviceId < 0 || deviceId >= gpu_info.n_dev)
-    {
-        gmx_incons("Invalid GPU deviceId requested");
-    }
-    return &gpu_info.gpu_dev[deviceId];
-}
+void releaseDevice(DeviceInformation* /* deviceInfo */) {}
 
-size_t sizeof_gpu_dev_info()
+std::string getDeviceInformationString(const DeviceInformation& deviceInfo)
 {
-    return sizeof(gmx_device_info_t);
-}
+    bool gpuExists = (deviceInfo.status != DeviceStatus::Nonexistent
+                      && deviceInfo.status != DeviceStatus::NonFunctional);
 
-int gpu_info_get_stat(const gmx_gpu_info_t& info, int index)
-{
-    return info.gpu_dev[index].stat;
+    if (!gpuExists)
+    {
+        return gmx::formatString("#%d: %s, status: %s", deviceInfo.id, "N/A",
+                                 c_deviceStateString[deviceInfo.status]);
+    }
+    else
+    {
+        return gmx::formatString("#%d: name: %s, vendor: %s, device version: %s, status: %s",
+                                 deviceInfo.id, deviceInfo.device_name, deviceInfo.vendorName,
+                                 deviceInfo.device_version, c_deviceStateString[deviceInfo.status]);
+    }
 }
diff --git a/src/gromacs/hardware/device_management_sycl.cpp b/src/gromacs/hardware/device_management_sycl.cpp
new file mode 100644 (file)
index 0000000..236c2d2
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *  \brief Defines the SYCL implementations of the device management.
+ *
+ *  \author Paul Bauer <paul.bauer.q@gmail.com>
+ *  \author Erik Lindahl <erik.lindahl@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ *
+ * \ingroup module_hardware
+ */
+#include "gmxpre.h"
+
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/hardware/device_management.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "device_information.h"
+
+
+bool isDeviceDetectionFunctional(std::string* errorMessage)
+{
+    try
+    {
+        const std::vector<cl::sycl::platform> platforms = cl::sycl::platform::get_platforms();
+        // SYCL should always have the "host" platform, but just in case:
+        if (platforms.empty() && errorMessage != nullptr)
+        {
+            errorMessage->assign("No SYCL platforms found.");
+        }
+        return !platforms.empty();
+    }
+    catch (const std::exception& e)
+    {
+        if (errorMessage != nullptr)
+        {
+            errorMessage->assign(
+                    gmx::formatString("Unable to get the list of SYCL platforms: %s", e.what()));
+        }
+        return false;
+    }
+}
+
+
+/*!
+ * \brief Checks that device \c deviceInfo is compatible with GROMACS.
+ *
+ *  For now, only checks that the vendor is Intel and it is a GPU.
+ *
+ * \param[in]  syclDevice  The SYCL device pointer.
+ * \returns                The status enumeration value for the checked device:
+ */
+static DeviceStatus isDeviceCompatible(const cl::sycl::device& syclDevice)
+{
+    if (getenv("GMX_GPU_DISABLE_COMPATIBILITY_CHECK") != nullptr)
+    {
+        // Assume the device is compatible because checking has been disabled.
+        return DeviceStatus::Compatible;
+    }
+
+    if (syclDevice.is_accelerator()) // FPGAs and FPGA emulators
+    {
+        return DeviceStatus::Incompatible;
+    }
+    else
+    {
+        return DeviceStatus::Compatible;
+    }
+}
+
+
+/*!
+ * \brief Checks that device \c deviceInfo is sane (ie can run a kernel).
+ *
+ * Compiles and runs a dummy kernel to determine whether the given
+ * SYCL device functions properly.
+ *
+ *
+ * \param[in]  syclDevice      The device info pointer.
+ * \param[out] errorMessage    An error message related to a SYCL error.
+ * \throws     std::bad_alloc  When out of memory.
+ * \returns                    Whether the device passed sanity checks
+ */
+static bool isDeviceFunctional(const cl::sycl::device& syclDevice, std::string* errorMessage)
+{
+    static const int numThreads = 8;
+    cl::sycl::queue  queue;
+    try
+    {
+        queue = cl::sycl::queue(syclDevice);
+        cl::sycl::buffer<int, 1> buffer(numThreads);
+        queue.submit([&](cl::sycl::handler& cgh) {
+                 auto d_buffer = buffer.get_access<cl::sycl::access::mode::discard_write>(cgh);
+                 cgh.parallel_for<class DummyKernel>(numThreads, [=](cl::sycl::id<1> threadId) {
+                     d_buffer[threadId] = threadId.get(0);
+                 });
+             })
+                .wait_and_throw();
+        const auto h_Buffer = buffer.get_access<cl::sycl::access::mode::read>();
+        for (int i = 0; i < numThreads; i++)
+        {
+            if (h_Buffer[i] != i)
+            {
+                if (errorMessage != nullptr)
+                {
+                    errorMessage->assign("Dummy kernel produced invalid values");
+                }
+                return false;
+            }
+        }
+    }
+    catch (const std::exception& e)
+    {
+        if (errorMessage != nullptr)
+        {
+            errorMessage->assign(gmx::formatString(
+                    "Unable to run dummy kernel on device %s: %s",
+                    syclDevice.get_info<cl::sycl::info::device::name>().c_str(), e.what()));
+        }
+        return false;
+    }
+
+    return true;
+}
+
+/*!
+ * \brief Checks that device \c deviceInfo is compatible and functioning.
+ *
+ * Checks the given SYCL device for compatibility and runs a dummy kernel on it to determine
+ * whether the device functions properly.
+ *
+ *
+ * \param[in] deviceId         Device number (internal to GROMACS).
+ * \param[in] deviceInfo       The device info pointer.
+ * \returns                    The status of device.
+ */
+static DeviceStatus checkDevice(size_t deviceId, const DeviceInformation& deviceInfo)
+{
+
+    DeviceStatus supportStatus = isDeviceCompatible(deviceInfo.syclDevice);
+    if (supportStatus != DeviceStatus::Compatible)
+    {
+        return supportStatus;
+    }
+
+    std::string errorMessage;
+    if (!isDeviceFunctional(deviceInfo.syclDevice, &errorMessage))
+    {
+        gmx_warning("While sanity checking device #%zu, %s", deviceId, errorMessage.c_str());
+        return DeviceStatus::NonFunctional;
+    }
+
+    return DeviceStatus::Compatible;
+}
+
+std::vector<std::unique_ptr<DeviceInformation>> findDevices()
+{
+    std::vector<std::unique_ptr<DeviceInformation>> deviceInfos(0);
+    const std::vector<cl::sycl::device>             devices = cl::sycl::device::get_devices();
+    deviceInfos.reserve(devices.size());
+    for (const auto& syclDevice : devices)
+    {
+        deviceInfos.emplace_back(std::make_unique<DeviceInformation>());
+
+        size_t i = deviceInfos.size() - 1;
+
+        deviceInfos[i]->id         = i;
+        deviceInfos[i]->syclDevice = syclDevice;
+        deviceInfos[i]->status     = checkDevice(i, *deviceInfos[i]);
+        deviceInfos[i]->deviceVendor =
+                getDeviceVendor(syclDevice.get_info<sycl::info::device::vendor>());
+    }
+    return deviceInfos;
+}
+
+void setActiveDevice(const DeviceInformation& /*deviceInfo*/) {}
+
+void releaseDevice(DeviceInformation* /* deviceInfo */) {}
+
+std::string getDeviceInformationString(const DeviceInformation& deviceInfo)
+{
+    bool deviceExists = (deviceInfo.status != DeviceStatus::Nonexistent
+                         && deviceInfo.status != DeviceStatus::NonFunctional);
+
+    if (!deviceExists)
+    {
+        return gmx::formatString("#%d: %s, status: %s", deviceInfo.id, "N/A",
+                                 c_deviceStateString[deviceInfo.status]);
+    }
+    else
+    {
+        return gmx::formatString(
+                "#%d: name: %s, vendor: %s, device version: %s, status: %s", deviceInfo.id,
+                deviceInfo.syclDevice.get_info<cl::sycl::info::device::name>().c_str(),
+                deviceInfo.syclDevice.get_info<cl::sycl::info::device::vendor>().c_str(),
+                deviceInfo.syclDevice.get_info<cl::sycl::info::device::version>().c_str(),
+                c_deviceStateString[deviceInfo.status]);
+    }
+}
diff --git a/src/gromacs/hardware/gpu_hw_info.h b/src/gromacs/hardware/gpu_hw_info.h
deleted file mode 100644 (file)
index a8b3144..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#ifndef GMX_HARDWARE_GPU_HW_INFO_H
-#define GMX_HARDWARE_GPU_HW_INFO_H
-
-#include "gromacs/utility/basedefinitions.h"
-
-struct gmx_device_info_t;
-
-/*! \brief Possible results of the GPU detection/check.
- *
- * The egpuInsane value means that during the sanity checks an error
- * occurred that indicates malfunctioning of the device, driver, or
- * incompatible driver/runtime.
- * eGpuUnavailable indicates that CUDA devices are busy or unavailable
- * typically due to use of cudaComputeModeExclusive, cudaComputeModeProhibited modes.
- */
-typedef enum
-{
-    egpuCompatible = 0,
-    egpuNonexistent,
-    egpuIncompatible,
-    egpuIncompatibleClusterSize,
-    egpuInsane,
-    egpuUnavailable,
-    egpuNR
-} e_gpu_detect_res_t;
-
-/*! \brief Names of the GPU detection/check results
- *
- * \todo Make a proper class enumeration with helper string */
-extern const char* const gpu_detect_res_str[egpuNR];
-
-/*! \brief Information about GPU devices on this physical node.
- *
- * Includes either CUDA or OpenCL devices.  The gmx_hardware_detect
- * module initializes it.
- *
- * \todo Use a std::vector */
-struct gmx_gpu_info_t
-{
-    //! Did we attempt GPU detection?
-    gmx_bool bDetectGPUs;
-    //! Total number of GPU devices detected on this physical node
-    int n_dev;
-    //! Information about each GPU device detected on this physical node
-    gmx_device_info_t* gpu_dev;
-    //! Number of GPU devices detected on this physical node that are compatible.
-    int n_dev_compatible;
-};
-
-#endif
index ef310340d813f20743c5691c58200100427d9181..1e9d785ce4317267a67a2597b7822905607c518a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index caf19a5c6e8bf7278a3f278b8eba8e2c82df60f0..b7b796f8b6da8ee93d13841d03fdbe92a3dfa6ac 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -39,7 +40,7 @@
 #include <string>
 #include <vector>
 
-#include "gromacs/hardware/gpu_hw_info.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/utility/basedefinitions.h"
 
 namespace gmx
@@ -47,6 +48,7 @@ namespace gmx
 class CpuInfo;
 class HardwareTopology;
 } // namespace gmx
+struct DeviceInformation;
 
 /* Hardware information structure with CPU and GPU information.
  * It is initialized by gmx_detect_hardware().
@@ -60,8 +62,6 @@ struct gmx_hw_info_t
     ~gmx_hw_info_t();
 
     /* Data for our local physical node */
-    //! Information about GPUs detected on this physical node
-    gmx_gpu_info_t gpu_info;
 
     /*! \brief Number of hardware threads available.
      *
@@ -72,6 +72,7 @@ struct gmx_hw_info_t
 
     std::unique_ptr<gmx::CpuInfo>          cpuInfo; /* Information about CPU capabilities */
     std::unique_ptr<gmx::HardwareTopology> hardwareTopology; /* Information about hardware topology */
+    std::vector<std::unique_ptr<DeviceInformation>> deviceInfoList; /* Information about GPUs detected on this physical node */
 
 
     /* Data reduced through MPI over all physical nodes */
index 43f70234094375cc7ed9b0a909b057ee30f5a93e..0720a12bbe9398a344acffd7eb8f6d24ad491268 100644 (file)
@@ -44,8 +44,8 @@
 #include <string>
 #include <vector>
 
-#include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/hardware/cpuinfo.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/hardware/hardwaretopology.h"
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/hardware/identifyavx512fmaunits.h"
 #include "architecture.h"
 
 //! Constant used to help minimize preprocessed code
-static const bool bGPUBinary = GMX_GPU != GMX_GPU_NONE;
+static constexpr bool bGPUBinary = (GMX_GPU != 0);
 
 /*! \internal \brief
  * Returns the GPU information text, one GPU per line.
  */
-static std::string sprint_gpus(const gmx_gpu_info_t& gpu_info)
+static std::string sprint_gpus(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList)
 {
-    char                     stmp[STRLEN];
-    std::vector<std::string> gpuStrings;
-    for (int i = 0; i < gpu_info.n_dev; i++)
+    std::vector<std::string> gpuStrings(0);
+    for (const auto& deviceInfo : deviceInfoList)
     {
-        get_gpu_device_info_string(stmp, gpu_info, i);
-        gpuStrings.push_back(gmx::formatString("    %s", stmp));
+        gpuStrings.emplace_back("    " + getDeviceInformationString(*deviceInfo));
     }
     return gmx::joinStrings(gpuStrings, "\n");
 }
@@ -84,7 +82,7 @@ static std::string sprint_gpus(const gmx_gpu_info_t& gpu_info)
    and runtime CPU do not match. */
 static void check_use_of_rdtscp_on_this_cpu(const gmx::MDLogger& mdlog, const gmx::CpuInfo& cpuInfo)
 {
-    bool binaryUsesRdtscp = HAVE_RDTSCP;
+    bool binaryUsesRdtscp = GMX_USE_RDTSCP;
 
     const char* programName = gmx::getProgramContext().displayName();
 
@@ -147,14 +145,21 @@ static std::string detected_hardware_string(const gmx_hw_info_t* hwinfo, bool bF
         s += gmx::formatString(" %d cores,", hwinfo->ncore_tot);
     }
     s += gmx::formatString(" %d logical cores", hwinfo->nhwthread_tot);
-    if (hwinfo->gpu_info.bDetectGPUs)
+    if (canPerformDeviceDetection(nullptr))
     {
         s += gmx::formatString(", %d compatible GPU%s", hwinfo->ngpu_compatible_tot,
                                hwinfo->ngpu_compatible_tot == 1 ? "" : "s");
     }
     else if (bGPUBinary)
     {
-        s += gmx::formatString(" (GPU detection deactivated)");
+        if (isDeviceDetectionEnabled())
+        {
+            s += gmx::formatString(" (GPU detection failed)");
+        }
+        else
+        {
+            s += gmx::formatString(" (GPU detection deactivated)");
+        }
     }
     s += gmx::formatString("\n");
 
@@ -346,11 +351,12 @@ static std::string detected_hardware_string(const gmx_hw_info_t* hwinfo, bool bF
         }
     }
 
-    if (bGPUBinary && hwinfo->gpu_info.n_dev > 0)
+    if (bGPUBinary && !hwinfo->deviceInfoList.empty())
     {
         s += gmx::formatString("  GPU info:\n");
-        s += gmx::formatString("    Number of GPUs detected: %d\n", hwinfo->gpu_info.n_dev);
-        s += sprint_gpus(hwinfo->gpu_info) + "\n";
+        s += gmx::formatString("    Number of GPUs detected: %d\n",
+                               static_cast<int>(hwinfo->deviceInfoList.size()));
+        s += sprint_gpus(hwinfo->deviceInfoList) + "\n";
     }
     return s;
 }
index ed3c77c17beeb2fb1b97aade3470ee38ac88b3d8..a8061dd5e8b6c64b6898522777daf0792094ad0a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e90b7b60286b803cff29589f3ec58f57ff54cf42..5bd154009ffd2d3ed193cfe56f7b91e083f762d3 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015, by the GROMACS development team, led by
+# Copyright (c) 2015,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,5 +33,9 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(HardwareUnitTests hardware-test
-                  cpuinfo.cpp
-                  hardwaretopology.cpp)
+    CPP_SOURCE_FILES
+        cpuinfo.cpp
+        hardwaretopology.cpp
+    GPU_CPP_SOURCE_FILES
+        device_management.cpp
+        )
diff --git a/src/gromacs/hardware/tests/device_management.cpp b/src/gromacs/hardware/tests/device_management.cpp
new file mode 100644 (file)
index 0000000..88d61cf
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for DevicesManager
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \ingroup module_hardware
+ */
+#include "gmxpre.h"
+
+#include "gromacs/hardware/device_management.h"
+
+#include "config.h"
+
+#include <algorithm>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/utility/inmemoryserializer.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace
+{
+
+TEST(DevicesManagerTest, Serialization)
+{
+    if (canPerformDeviceDetection(nullptr) && c_canSerializeDeviceInformation)
+    {
+        std::vector<std::unique_ptr<DeviceInformation>> deviceInfoListIn = findDevices();
+        gmx::InMemorySerializer                         writer;
+        serializeDeviceInformations(deviceInfoListIn, &writer);
+        auto buffer = writer.finishAndGetBuffer();
+
+        gmx::InMemoryDeserializer                       reader(buffer, false);
+        std::vector<std::unique_ptr<DeviceInformation>> deviceInfoListOut =
+                deserializeDeviceInformations(&reader);
+
+        EXPECT_EQ(deviceInfoListOut.size(), deviceInfoListIn.size())
+                << "Number of accessible devices changed after serialization/deserialization.";
+
+        for (int deviceId = 0; deviceId < static_cast<int>(deviceInfoListIn.size()); deviceId++)
+        {
+            EXPECT_FALSE(deviceInfoListIn[deviceId] == nullptr) << gmx::formatString(
+                    "Device #%d information is nullptr before serialization.", deviceId);
+            EXPECT_FALSE(deviceInfoListOut[deviceId] == nullptr) << gmx::formatString(
+                    "Device #%d information is nullptr after serialization.", deviceId);
+
+            const DeviceInformation& deviceInfoIn  = *deviceInfoListIn[deviceId];
+            const DeviceInformation& deviceInfoOut = *deviceInfoListOut[deviceId];
+            EXPECT_EQ(deviceInfoIn.status, deviceInfoOut.status) << gmx::formatString(
+                    "Device status changed after serialization/deserialization for device #%d.", deviceId);
+
+            EXPECT_EQ(deviceInfoIn.id, deviceInfoOut.id) << gmx::formatString(
+                    "Device id changed after serialization/deserialization for device #%d.", deviceId);
+
+#if GMX_GPU_OPENCL
+            EXPECT_EQ(deviceInfoIn.oclPlatformId, deviceInfoOut.oclPlatformId) << gmx::formatString(
+                    "Device OpenCL platform ID changed after serialization/deserialization for "
+                    "device "
+                    "#%d.",
+                    deviceId);
+
+#endif // GMX_GPU_OPENCL
+        }
+    }
+}
+
+} // namespace
index 1d03d00a1d4c31ebe00fe78406a420d733e67345..6b5d3d28f4443d9cb9669b039dc264b81daf27df 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -70,6 +71,7 @@
 #include "gromacs/mdlib/stat.h"
 #include "gromacs/mdrunutility/handlerestart.h"
 #include "gromacs/mdrunutility/multisim.h"
+#include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/imdmodule.h"
 #include "gromacs/mdtypes/inputrec.h"
@@ -324,6 +326,8 @@ class InteractiveMolecularDynamics final : public IMDModule
     IMdpOptionProvider* mdpOptionProvider() override { return nullptr; }
     IMDOutputProvider*  outputProvider() override { return nullptr; }
     void                initForceProviders(ForceProviders* /* forceProviders */) override {}
+    void subscribeToSimulationSetupNotifications(MdModulesNotifier* /* notifier */) override {}
+    void subscribeToPreProcessingNotifications(MdModulesNotifier* /* notifier */) override {}
 };
 
 std::unique_ptr<IMDModule> createInteractiveMolecularDynamicsModule()
@@ -522,8 +526,9 @@ void write_IMDgroup_to_file(bool              bIMD,
     if (bIMD)
     {
         IMDatoms = gmx_mtop_global_atoms(sys);
-        write_sto_conf_indexed(opt2fn("-imd", nfile, fnm), "IMDgroup", &IMDatoms, state->x.rvec_array(),
-                               state->v.rvec_array(), ir->ePBC, state->box, ir->imd->nat, ir->imd->ind);
+        write_sto_conf_indexed(opt2fn("-imd", nfile, fnm), "IMDgroup", &IMDatoms,
+                               state->x.rvec_array(), state->v.rvec_array(), ir->pbcType,
+                               state->box, ir->imd->nat, ir->imd->ind);
     }
 }
 
@@ -829,7 +834,7 @@ void ImdSession::Impl::syncNodes(const t_commrec* cr, double t)
     /* Notify the other nodes whether we are still connected. */
     if (PAR(cr))
     {
-        block_bc(cr, bConnected);
+        block_bc(cr->mpi_comm_mygroup, bConnected);
     }
 
     /* ...if not connected, the job is done here. */
@@ -841,7 +846,7 @@ void ImdSession::Impl::syncNodes(const t_commrec* cr, double t)
     /* Let the other nodes know whether we got a new IMD synchronization frequency. */
     if (PAR(cr))
     {
-        block_bc(cr, nstimd_new);
+        block_bc(cr->mpi_comm_mygroup, nstimd_new);
     }
 
     /* Now we all set the (new) nstimd communication time step */
@@ -872,7 +877,7 @@ void ImdSession::Impl::syncNodes(const t_commrec* cr, double t)
     /* make new_forces known to the clients */
     if (PAR(cr))
     {
-        block_bc(cr, new_nforces);
+        block_bc(cr->mpi_comm_mygroup, new_nforces);
     }
 
     /* When new_natoms < 0 then we know that these are still the same forces
@@ -905,8 +910,8 @@ void ImdSession::Impl::syncNodes(const t_commrec* cr, double t)
     /* In parallel mode we communicate the to-be-applied forces to the other nodes */
     if (PAR(cr))
     {
-        nblock_bc(cr, nforces, f_ind);
-        nblock_bc(cr, nforces, f);
+        nblock_bc(cr->mpi_comm_mygroup, nforces, f_ind);
+        nblock_bc(cr->mpi_comm_mygroup, nforces, f);
     }
 
     /* done communicating the forces, reset bNewForces */
@@ -1264,7 +1269,7 @@ void ImdSession::Impl::prepareForPositionAssembly(const t_commrec* cr, const rve
     /* Communicate initial coordinates xa_old to all processes */
     if (PAR(cr))
     {
-        gmx_bcast(nat * sizeof(xa_old[0]), xa_old, cr);
+        gmx_bcast(nat * sizeof(xa_old[0]), xa_old, cr->mpi_comm_mygroup);
     }
 }
 
@@ -1369,7 +1374,7 @@ std::unique_ptr<ImdSession> makeImdSession(const t_inputrec*           ir,
     /* Let the other nodes know whether we want IMD */
     if (PAR(cr))
     {
-        block_bc(cr, createSession);
+        block_bc(cr->mpi_comm_mygroup, createSession);
     }
 
     /*... if not we are done.*/
@@ -1468,7 +1473,7 @@ std::unique_ptr<ImdSession> makeImdSession(const t_inputrec*           ir,
     /* do we allow interactive pulling? If so let the other nodes know. */
     if (PAR(cr))
     {
-        block_bc(cr, impl->bForceActivated);
+        block_bc(cr->mpi_comm_mygroup, impl->bForceActivated);
     }
 
     /* setup the listening socket on master process */
index 8fb64c23177e128bbc0cac4e35c780f36c6e5619..0ef075ae60bef98a04deb1a3b26415cbbcd0864d 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 4fc4e34e4b14b35e340e14cad271eb68dadcce3e..4b45b71c990f76731a54d2933ca7bfc8ad002656 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index bc78c81c42627c60f32a8bc37c3f4f43da47e7ec..4c0ab0c09ca8794ec055ab1fc288bac34be96f64 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 827262198f2235545275df0f994773fe767d2237..f0643b413bba54f1ea79ce05226b44987aaee720 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 29a1deb73dc35f2b0a8825219e7b1b64237e23e4..4ee3114cb0b6db93eebef727a6a0c2f984dc88de 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 1991-2004 David van der Spoel, Erik Lindahl, University of Groningen.
- * Copyright (c) 2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 011614444723ac622634948451b7456d4df18d8c..089a3dc7a1676d3e73dce9c4d73fbd212e1d7717 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 638d70b39429d487b15cd3dd259dd4fcbb9103d3..f5cbc5880055a043f8d585c3bbdee56622b703c9 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 22936e1e5b4be51197832c9e2777fffb27c71396..fa1fa07902081be0d7027030b1aa0903dd2a58a2 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -45,7 +45,7 @@ gmx_add_libgromacs_sources(
     restcbt.cpp
     )
 
-if(GMX_USE_CUDA)
+if(GMX_GPU_CUDA)
     gmx_add_libgromacs_sources(
        gpubonded_impl.cu
        gpubondedkernels.cu
index 129ab880d367ea5ba1eab5461c2525adbf19e3d8..39876051f87c8ecf2560a3a2be053c8e39ff5720 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -64,7 +65,6 @@
 #include "gromacs/mdtypes/fcdata.h"
 #include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/ishift.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/pbc_simd.h"
 #include "gromacs/simd/simd.h"
@@ -92,7 +92,6 @@ using BondedFunction = real (*)(int              nbonds,
                                 rvec4            f[],
                                 rvec             fshift[],
                                 const t_pbc*     pbc,
-                                const t_graph*   g,
                                 real             lambda,
                                 real*            dvdlambda,
                                 const t_mdatoms* md,
@@ -204,26 +203,17 @@ namespace
 
 /*! \brief Spreads and accumulates the bonded forces to the two atoms and adds the virial contribution when needed
  *
- * When \p g==nullptr, \p shiftIndex is used as the periodic shift.
- * When \p g!=nullptr, the graph is used to compute the periodic shift.
+ * \p shiftIndex is used as the periodic shift.
  */
 template<BondedKernelFlavor flavor>
-inline void spreadBondForces(const real     bondForce,
-                             const rvec     dx,
-                             const int      ai,
-                             const int      aj,
-                             rvec4*         f,
-                             int            shiftIndex,
-                             const t_graph* g,
-                             rvec*          fshift)
+inline void spreadBondForces(const real bondForce,
+                             const rvec dx,
+                             const int  ai,
+                             const int  aj,
+                             rvec4*     f,
+                             int        shiftIndex,
+                             rvec*      fshift)
 {
-    if (computeVirial(flavor) && g)
-    {
-        ivec dt;
-        ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
-        shiftIndex = IVEC2IS(dt);
-    }
-
     for (int m = 0; m < DIM; m++) /*  15          */
     {
         const real fij = bondForce * dx[m];
@@ -256,7 +246,6 @@ real morse_bonds(int             nbonds,
                  rvec4           f[],
                  rvec            fshift[],
                  const t_pbc*    pbc,
-                 const t_graph*  g,
                  real            lambda,
                  real*           dvdlambda,
                  const t_mdatoms gmx_unused* md,
@@ -311,8 +300,8 @@ real morse_bonds(int             nbonds,
         *dvdlambda += (cbB - cbA) * omtemp * omtemp
                       - (2 - 2 * omtemp) * omtemp * cb * ((b0B - b0A) * be - (beB - beA) * (dr - b0)); /* 15 */
 
-        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, g, fshift); /* 15 */
-    }                                                                  /*  83 TOTAL    */
+        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, fshift); /* 15 */
+    }                                                               /*  83 TOTAL    */
     return vtot;
 }
 
@@ -325,7 +314,6 @@ real cubic_bonds(int             nbonds,
                  rvec4           f[],
                  rvec            fshift[],
                  const t_pbc*    pbc,
-                 const t_graph*  g,
                  real gmx_unused lambda,
                  real gmx_unused* dvdlambda,
                  const t_mdatoms gmx_unused* md,
@@ -368,8 +356,8 @@ real cubic_bonds(int             nbonds,
 
         vtot += vbond; /* 21 */
 
-        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, g, fshift); /* 15 */
-    }                                                                  /*  54 TOTAL    */
+        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, fshift); /* 15 */
+    }                                                               /*  54 TOTAL    */
     return vtot;
 }
 
@@ -381,7 +369,6 @@ real FENE_bonds(int             nbonds,
                 rvec4           f[],
                 rvec            fshift[],
                 const t_pbc*    pbc,
-                const t_graph*  g,
                 real gmx_unused lambda,
                 real gmx_unused* dvdlambda,
                 const t_mdatoms gmx_unused* md,
@@ -428,8 +415,8 @@ real FENE_bonds(int             nbonds,
 
         vtot += vbond; /* 35 */
 
-        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, g, fshift); /* 15 */
-    }                                                                  /*  58 TOTAL    */
+        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, fshift); /* 15 */
+    }                                                               /*  58 TOTAL    */
     return vtot;
 }
 
@@ -458,21 +445,20 @@ real harmonic(real kA, real kB, real xA, real xB, real x, real lambda, real* V,
     /* That was 19 flops */
 }
 
-
 template<BondedKernelFlavor flavor>
-real bonds(int             nbonds,
-           const t_iatom   forceatoms[],
-           const t_iparams forceparams[],
-           const rvec      x[],
-           rvec4           f[],
-           rvec            fshift[],
-           const t_pbc*    pbc,
-           const t_graph*  g,
-           real            lambda,
-           real*           dvdlambda,
-           const t_mdatoms gmx_unused* md,
-           t_fcdata gmx_unused* fcd,
-           int gmx_unused* global_atom_index)
+std::enable_if_t<flavor != BondedKernelFlavor::ForcesSimdWhenAvailable || !GMX_SIMD_HAVE_REAL, real>
+bonds(int             nbonds,
+      const t_iatom   forceatoms[],
+      const t_iparams forceparams[],
+      const rvec      x[],
+      rvec4           f[],
+      rvec            fshift[],
+      const t_pbc*    pbc,
+      real            lambda,
+      real*           dvdlambda,
+      const t_mdatoms gmx_unused* md,
+      t_fcdata gmx_unused* fcd,
+      int gmx_unused* global_atom_index)
 {
     int  i, ki, ai, aj, type;
     real dr, dr2, fbond, vbond, vtot;
@@ -502,11 +488,109 @@ real bonds(int             nbonds,
         vtot += vbond;              /* 1*/
         fbond *= gmx::invsqrt(dr2); /*   6             */
 
-        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, g, fshift); /* 15 */
-    }                                                                  /* 59 TOTAL     */
+        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, fshift); /* 15 */
+    }                                                               /* 59 TOTAL        */
     return vtot;
 }
 
+#if GMX_SIMD_HAVE_REAL
+
+/*! \brief Computes forces (only) for harmonic bonds using SIMD intrinsics
+ *
+ * As plain-C bonds(), but using SIMD to calculate many bonds at once.
+ * This routines does not calculate energies and shift forces.
+ */
+template<BondedKernelFlavor flavor>
+std::enable_if_t<flavor == BondedKernelFlavor::ForcesSimdWhenAvailable, real>
+bonds(int             nbonds,
+      const t_iatom   forceatoms[],
+      const t_iparams forceparams[],
+      const rvec      x[],
+      rvec4           f[],
+      rvec gmx_unused fshift[],
+      const t_pbc*    pbc,
+      real gmx_unused lambda,
+      real gmx_unused* dvdlambda,
+      const t_mdatoms gmx_unused* md,
+      t_fcdata gmx_unused* fcd,
+      int gmx_unused* global_atom_index)
+{
+    constexpr int                            nfa1 = 3;
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ai[GMX_SIMD_REAL_WIDTH];
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t aj[GMX_SIMD_REAL_WIDTH];
+    alignas(GMX_SIMD_ALIGNMENT) real         coeff[2 * GMX_SIMD_REAL_WIDTH];
+
+    alignas(GMX_SIMD_ALIGNMENT) real pbc_simd[9 * GMX_SIMD_REAL_WIDTH];
+
+    set_pbc_simd(pbc, pbc_simd);
+
+    const SimdReal real_eps = SimdReal(GMX_REAL_EPS);
+
+    /* nbonds is the number of bonds times nfa1, here we step GMX_SIMD_REAL_WIDTH angles */
+    for (int i = 0; i < nbonds; i += GMX_SIMD_REAL_WIDTH * nfa1)
+    {
+        /* Collect atoms for GMX_SIMD_REAL_WIDTH angles.
+         * iu indexes into forceatoms, we should not let iu go beyond nbonds.
+         */
+        int iu = i;
+        for (int s = 0; s < GMX_SIMD_REAL_WIDTH; s++)
+        {
+            const int type = forceatoms[iu];
+            ai[s]          = forceatoms[iu + 1];
+            aj[s]          = forceatoms[iu + 2];
+
+            /* At the end fill the arrays with the last atoms and 0 params */
+            if (i + s * nfa1 < nbonds)
+            {
+                coeff[s]                       = forceparams[type].harmonic.krA;
+                coeff[GMX_SIMD_REAL_WIDTH + s] = forceparams[type].harmonic.rA;
+
+                if (iu + nfa1 < nbonds)
+                {
+                    iu += nfa1;
+                }
+            }
+            else
+            {
+                coeff[s]                       = 0;
+                coeff[GMX_SIMD_REAL_WIDTH + s] = 0;
+            }
+        }
+
+        /* Store the non PBC corrected distances packed and aligned */
+        SimdReal xi, yi, zi;
+        SimdReal xj, yj, zj;
+        gatherLoadUTranspose<3>(reinterpret_cast<const real*>(x), ai, &xi, &yi, &zi);
+        gatherLoadUTranspose<3>(reinterpret_cast<const real*>(x), aj, &xj, &yj, &zj);
+        SimdReal rij_x = xi - xj;
+        SimdReal rij_y = yi - yj;
+        SimdReal rij_z = zi - zj;
+
+        pbc_correct_dx_simd(&rij_x, &rij_y, &rij_z, pbc_simd);
+
+        const SimdReal dist2 = rij_x * rij_x + rij_y * rij_y + rij_z * rij_z;
+        // Here we avoid sqrt(0), the force will be zero because rij=0
+        const SimdReal invDist = invsqrt(max(dist2, real_eps));
+
+        const SimdReal k  = load<SimdReal>(coeff);
+        const SimdReal r0 = load<SimdReal>(coeff + GMX_SIMD_REAL_WIDTH);
+
+        // Compute the force divided by the distance
+        const SimdReal forceOverR = -k * (dist2 * invDist - r0) * invDist;
+
+        const SimdReal f_x = forceOverR * rij_x;
+        const SimdReal f_y = forceOverR * rij_y;
+        const SimdReal f_z = forceOverR * rij_z;
+
+        transposeScatterIncrU<4>(reinterpret_cast<real*>(f), ai, f_x, f_y, f_z);
+        transposeScatterDecrU<4>(reinterpret_cast<real*>(f), aj, f_x, f_y, f_z);
+    }
+
+    return 0;
+}
+
+#endif // GMX_SIMD_HAVE_REAL
+
 template<BondedKernelFlavor flavor>
 real restraint_bonds(int             nbonds,
                      const t_iatom   forceatoms[],
@@ -515,7 +599,6 @@ real restraint_bonds(int             nbonds,
                      rvec4           f[],
                      rvec            fshift[],
                      const t_pbc*    pbc,
-                     const t_graph*  g,
                      real            lambda,
                      real*           dvdlambda,
                      const t_mdatoms gmx_unused* md,
@@ -590,8 +673,8 @@ real restraint_bonds(int             nbonds,
         vtot += vbond;              /* 1*/
         fbond *= gmx::invsqrt(dr2); /*   6             */
 
-        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, g, fshift); /* 15 */
-    }                                                                  /* 59 TOTAL     */
+        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, fshift); /* 15 */
+    }                                                               /* 59 TOTAL        */
 
     return vtot;
 }
@@ -604,7 +687,6 @@ real polarize(int              nbonds,
               rvec4            f[],
               rvec             fshift[],
               const t_pbc*     pbc,
-              const t_graph*   g,
               real             lambda,
               real*            dvdlambda,
               const t_mdatoms* md,
@@ -637,8 +719,8 @@ real polarize(int              nbonds,
         vtot += vbond;              /* 1*/
         fbond *= gmx::invsqrt(dr2); /*   6             */
 
-        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, g, fshift); /* 15 */
-    }                                                                  /* 59 TOTAL     */
+        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, fshift); /* 15 */
+    }                                                               /* 59 TOTAL        */
     return vtot;
 }
 
@@ -650,7 +732,6 @@ real anharm_polarize(int              nbonds,
                      rvec4            f[],
                      rvec             fshift[],
                      const t_pbc*     pbc,
-                     const t_graph*   g,
                      real             lambda,
                      real*            dvdlambda,
                      const t_mdatoms* md,
@@ -692,8 +773,8 @@ real anharm_polarize(int              nbonds,
         fbond *= gmx::invsqrt(dr2); /*   6             */
         vtot += vbond;              /* 1*/
 
-        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, g, fshift); /* 15 */
-    }                                                                  /* 72 TOTAL     */
+        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, fshift); /* 15 */
+    }                                                               /* 72 TOTAL        */
     return vtot;
 }
 
@@ -705,7 +786,6 @@ real water_pol(int             nbonds,
                rvec4           f[],
                rvec gmx_unused fshift[],
                const t_pbc gmx_unused* pbc,
-               const t_graph gmx_unused* g,
                real gmx_unused lambda,
                real gmx_unused* dvdlambda,
                const t_mdatoms gmx_unused* md,
@@ -717,7 +797,6 @@ real water_pol(int             nbonds,
      * three spatial dimensions in the molecular frame.
      */
     int  i, m, aO, aH1, aH2, aD, aS, type, type0, ki;
-    ivec dt;
     rvec dOH1, dOH2, dHH, dOD, dDS, nW, kk, dx, kdx, proj;
     real vtot, fij, r_HH, r_OD, r_nW, tx, ty, tz, qS;
 
@@ -789,12 +868,6 @@ real water_pol(int             nbonds,
             kdx[ZZ] = kk[ZZ] * dx[ZZ];
             vtot += iprod(dx, kdx);
 
-            if (computeVirial(flavor) && g)
-            {
-                ivec_sub(SHIFT_IVEC(g, aS), SHIFT_IVEC(g, aD), dt);
-                ki = IVEC2IS(dt);
-            }
-
             for (m = 0; (m < DIM); m++)
             {
                 /* This is a tensor operation but written out for speed */
@@ -857,7 +930,6 @@ real thole_pol(int             nbonds,
                rvec4           f[],
                rvec            fshift[],
                const t_pbc*    pbc,
-               const t_graph gmx_unused* g,
                real gmx_unused lambda,
                real gmx_unused* dvdlambda,
                const t_mdatoms* md,
@@ -892,7 +964,7 @@ real thole_pol(int             nbonds,
     return V;
 }
 
-// Avoid gcc 386 -O3 code generation bug in this function (see Redmine
+// Avoid gcc 386 -O3 code generation bug in this function (see Issue
 // #3205 for more information)
 #if defined(__GNUC__) && defined(__i386__) && defined(__OPTIMIZE__)
 #    pragma GCC push_options
@@ -909,7 +981,6 @@ angles(int             nbonds,
        rvec4           f[],
        rvec            fshift[],
        const t_pbc*    pbc,
-       const t_graph*  g,
        real            lambda,
        real*           dvdlambda,
        const t_mdatoms gmx_unused* md,
@@ -919,7 +990,6 @@ angles(int             nbonds,
     int  i, ai, aj, ak, t1, t2, type;
     rvec r_ij, r_kj;
     real cos_theta, cos_theta2, theta, dVdt, va, vtot;
-    ivec jt, dt_ij, dt_kj;
 
     vtot = 0.0;
     for (i = 0; i < nbonds;)
@@ -969,15 +1039,6 @@ angles(int             nbonds,
             }
             if (computeVirial(flavor))
             {
-                if (g != nullptr)
-                {
-                    copy_ivec(SHIFT_IVEC(g, aj), jt);
-
-                    ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                    ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                    t1 = IVEC2IS(dt_ij);
-                    t2 = IVEC2IS(dt_kj);
-                }
                 rvec_inc(fshift[t1], f_i);
                 rvec_inc(fshift[CENTRAL], f_j);
                 rvec_inc(fshift[t2], f_k);
@@ -1007,7 +1068,6 @@ angles(int             nbonds,
        rvec4           f[],
        rvec gmx_unused fshift[],
        const t_pbc*    pbc,
-       const t_graph gmx_unused* g,
        real gmx_unused lambda,
        real gmx_unused* dvdlambda,
        const t_mdatoms gmx_unused* md,
@@ -1029,12 +1089,13 @@ angles(int             nbonds,
     SimdReal                                 rijx_S, rijy_S, rijz_S;
     SimdReal                                 rkjx_S, rkjy_S, rkjz_S;
     SimdReal                                 one_S(1.0);
-    SimdReal min_one_plus_eps_S(-1.0 + 2.0 * GMX_REAL_EPS); // Smallest number > -1
+    SimdReal one_min_eps_S(1.0_real - GMX_REAL_EPS); // Largest number < 1
 
     SimdReal                         rij_rkj_S;
     SimdReal                         nrij2_S, nrij_1_S;
     SimdReal                         nrkj2_S, nrkj_1_S;
     SimdReal                         cos_S, invsin_S;
+    SimdReal                         cos2_S;
     SimdReal                         theta_S;
     SimdReal                         st_S, sth_S;
     SimdReal                         cik_S, cii_S, ckk_S;
@@ -1103,18 +1164,28 @@ angles(int             nbonds,
 
         cos_S = rij_rkj_S * nrij_1_S * nrkj_1_S;
 
-        /* To allow for 180 degrees, we take the max of cos and -1 + 1bit,
-         * so we can safely get the 1/sin from 1/sqrt(1 - cos^2).
-         * This also ensures that rounding errors would cause the argument
-         * of simdAcos to be < -1.
-         * Note that we do not take precautions for cos(0)=1, so the outer
-         * atoms in an angle should not be on top of each other.
+        /* We compute cos^2 using a division instead of squaring cos_S,
+         * as we would then get additional error contributions from
+         * the two invsqrt operations, which get amplified by
+         * the 1/sqrt(1-cos^2) for cos values close to 1.
+         *
+         * Note that the non-SIMD version of angles() avoids this issue
+         * by computing the cosine using double precision.
          */
-        cos_S = max(cos_S, min_one_plus_eps_S);
+        cos2_S = rij_rkj_S * rij_rkj_S / (nrij2_S * nrkj2_S);
+
+        /* To allow for 0 and 180 degrees, we need to avoid issues when
+         * the cosine is close to -1 and 1. For acos() the argument needs
+         * to be in that range. For cos^2 we take the min of cos^2 and 1 - 1bit,
+         * so we can safely compute 1/sin as 1/sqrt(1 - cos^2).
+         */
+        cos_S  = max(cos_S, -one_S);
+        cos_S  = min(cos_S, one_S);
+        cos2_S = min(cos2_S, one_min_eps_S);
 
         theta_S = acos(cos_S);
 
-        invsin_S = invsqrt(one_S - cos_S * cos_S);
+        invsin_S = invsqrt(one_S - cos2_S);
 
         st_S  = k_S * (theta0_S - theta_S) * invsin_S;
         sth_S = st_S * cos_S;
@@ -1155,7 +1226,6 @@ real linear_angles(int             nbonds,
                    rvec4           f[],
                    rvec            fshift[],
                    const t_pbc*    pbc,
-                   const t_graph*  g,
                    real            lambda,
                    real*           dvdlambda,
                    const t_mdatoms gmx_unused* md,
@@ -1165,7 +1235,6 @@ real linear_angles(int             nbonds,
     int  i, m, ai, aj, ak, t1, t2, type;
     rvec f_i, f_j, f_k;
     real L1, kA, kB, aA, aB, dr, dr2, va, vtot, a, b, klin;
-    ivec jt, dt_ij, dt_kj;
     rvec r_ij, r_kj, r_ik, dx;
 
     L1   = 1 - lambda;
@@ -1210,15 +1279,6 @@ real linear_angles(int             nbonds,
 
         if (computeVirial(flavor))
         {
-            if (g)
-            {
-                copy_ivec(SHIFT_IVEC(g, aj), jt);
-
-                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                t1 = IVEC2IS(dt_ij);
-                t2 = IVEC2IS(dt_kj);
-            }
             rvec_inc(fshift[t1], f_i);
             rvec_inc(fshift[CENTRAL], f_j);
             rvec_inc(fshift[t2], f_k);
@@ -1236,7 +1296,6 @@ urey_bradley(int             nbonds,
              rvec4           f[],
              rvec            fshift[],
              const t_pbc*    pbc,
-             const t_graph*  g,
              real            lambda,
              real*           dvdlambda,
              const t_mdatoms gmx_unused* md,
@@ -1248,7 +1307,6 @@ urey_bradley(int             nbonds,
     real cos_theta, cos_theta2, theta;
     real dVdt, va, vtot, dr, dr2, vbond, fbond, fik;
     real kthA, th0A, kUBA, r13A, kthB, th0B, kUBB, r13B;
-    ivec jt, dt_ij, dt_kj, dt_ik;
 
     vtot = 0.0;
     for (i = 0; (i < nbonds);)
@@ -1305,15 +1363,6 @@ urey_bradley(int             nbonds,
             }
             if (computeVirial(flavor))
             {
-                if (g)
-                {
-                    copy_ivec(SHIFT_IVEC(g, aj), jt);
-
-                    ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                    ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                    t1 = IVEC2IS(dt_ij);
-                    t2 = IVEC2IS(dt_kj);
-                }
                 rvec_inc(fshift[t1], f_i);
                 rvec_inc(fshift[CENTRAL], f_j);
                 rvec_inc(fshift[t2], f_k);
@@ -1328,11 +1377,6 @@ urey_bradley(int             nbonds,
         vtot += vbond;              /* 1*/
         fbond *= gmx::invsqrt(dr2); /*   6             */
 
-        if (computeVirial(flavor) && g)
-        {
-            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, ak), dt_ik);
-            ki = IVEC2IS(dt_ik);
-        }
         for (m = 0; (m < DIM); m++) /*  15             */
         {
             fik = fbond * r_ik[m];
@@ -1362,7 +1406,6 @@ urey_bradley(int             nbonds,
              rvec4           f[],
              rvec gmx_unused fshift[],
              const t_pbc*    pbc,
-             const t_graph gmx_unused* g,
              real gmx_unused lambda,
              real gmx_unused* dvdlambda,
              const t_mdatoms gmx_unused* md,
@@ -1505,7 +1548,6 @@ real quartic_angles(int             nbonds,
                     rvec4           f[],
                     rvec            fshift[],
                     const t_pbc*    pbc,
-                    const t_graph*  g,
                     real gmx_unused lambda,
                     real gmx_unused* dvdlambda,
                     const t_mdatoms gmx_unused* md,
@@ -1515,7 +1557,6 @@ real quartic_angles(int             nbonds,
     int  i, j, ai, aj, ak, t1, t2, type;
     rvec r_ij, r_kj;
     real cos_theta, cos_theta2, theta, dt, dVdt, va, dtp, c, vtot;
-    ivec jt, dt_ij, dt_kj;
 
     vtot = 0.0;
     for (i = 0; (i < nbonds);)
@@ -1573,15 +1614,6 @@ real quartic_angles(int             nbonds,
 
             if (computeVirial(flavor))
             {
-                if (g)
-                {
-                    copy_ivec(SHIFT_IVEC(g, aj), jt);
-
-                    ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                    ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                    t1 = IVEC2IS(dt_ij);
-                    t2 = IVEC2IS(dt_kj);
-                }
                 rvec_inc(fshift[t1], f_i);
                 rvec_inc(fshift[CENTRAL], f_j);
                 rvec_inc(fshift[t2], f_k);
@@ -1713,31 +1745,29 @@ inline void dih_angle_simd(const rvec* x,
 } // namespace
 
 template<BondedKernelFlavor flavor>
-void do_dih_fup(int            i,
-                int            j,
-                int            k,
-                int            l,
-                real           ddphi,
-                rvec           r_ij,
-                rvec           r_kj,
-                rvec           r_kl,
-                rvec           m,
-                rvec           n,
-                rvec4          f[],
-                rvec           fshift[],
-                const t_pbc*   pbc,
-                const t_graph* g,
-                const rvec     x[],
-                int            t1,
-                int            t2,
-                int            t3)
+void do_dih_fup(int          i,
+                int          j,
+                int          k,
+                int          l,
+                real         ddphi,
+                rvec         r_ij,
+                rvec         r_kj,
+                rvec         r_kl,
+                rvec         m,
+                rvec         n,
+                rvec4        f[],
+                rvec         fshift[],
+                const t_pbc* pbc,
+                const rvec   x[],
+                int          t1,
+                int          t2,
+                int          t3)
 {
     /* 143 FLOPS */
     rvec f_i, f_j, f_k, f_l;
     rvec uvec, vvec, svec, dx_jl;
     real iprm, iprn, nrkj, nrkj2, nrkj_1, nrkj_2;
     real a, b, p, q, toler;
-    ivec jt, dt_ij, dt_kj, dt_lj;
 
     iprm  = iprod(m, m);       /*  5    */
     iprn  = iprod(n, n);       /*  5   */
@@ -1768,17 +1798,7 @@ void do_dih_fup(int            i,
 
         if (computeVirial(flavor))
         {
-            if (g)
-            {
-                copy_ivec(SHIFT_IVEC(g, j), jt);
-                ivec_sub(SHIFT_IVEC(g, i), jt, dt_ij);
-                ivec_sub(SHIFT_IVEC(g, k), jt, dt_kj);
-                ivec_sub(SHIFT_IVEC(g, l), jt, dt_lj);
-                t1 = IVEC2IS(dt_ij);
-                t2 = IVEC2IS(dt_kj);
-                t3 = IVEC2IS(dt_lj);
-            }
-            else if (pbc)
+            if (pbc)
             {
                 t3 = pbc_rvec_sub(pbc, x[l], x[j], dx_jl);
             }
@@ -1893,7 +1913,6 @@ pdihs(int             nbonds,
       rvec4           f[],
       rvec            fshift[],
       const t_pbc*    pbc,
-      const t_graph*  g,
       real            lambda,
       real*           dvdlambda,
       const t_mdatoms gmx_unused* md,
@@ -1930,9 +1949,9 @@ pdihs(int             nbonds,
         } while (i < nbonds && forceatoms[i + 1] == ai && forceatoms[i + 2] == aj
                  && forceatoms[i + 3] == ak && forceatoms[i + 4] == al);
 
-        do_dih_fup<flavor>(ai, aj, ak, al, ddphi_tot, r_ij, r_kj, r_kl, m, n, f, fshift, pbc, g, x,
-                           t1, t2, t3); /* 112         */
-    }                                   /* 223 TOTAL  */
+        do_dih_fup<flavor>(ai, aj, ak, al, ddphi_tot, r_ij, r_kj, r_kl, m, n, f, fshift, pbc, x, t1,
+                           t2, t3); /* 112             */
+    }                               /* 223 TOTAL  */
 
     return vtot;
 }
@@ -1949,7 +1968,6 @@ pdihs(int             nbonds,
       rvec4           f[],
       rvec gmx_unused fshift[],
       const t_pbc*    pbc,
-      const t_graph gmx_unused* g,
       real gmx_unused lambda,
       real gmx_unused* dvdlambda,
       const t_mdatoms gmx_unused* md,
@@ -2064,7 +2082,6 @@ rbdihs(int             nbonds,
        rvec4           f[],
        rvec gmx_unused fshift[],
        const t_pbc*    pbc,
-       const t_graph gmx_unused* g,
        real gmx_unused lambda,
        real gmx_unused* dvdlambda,
        const t_mdatoms gmx_unused* md,
@@ -2191,7 +2208,6 @@ real idihs(int             nbonds,
            rvec4           f[],
            rvec            fshift[],
            const t_pbc*    pbc,
-           const t_graph*  g,
            real            lambda,
            real*           dvdlambda,
            const t_mdatoms gmx_unused* md,
@@ -2244,7 +2260,7 @@ real idihs(int             nbonds,
 
         dvdl_term += 0.5 * (kB - kA) * dp2 - kk * dphi0 * dp;
 
-        do_dih_fup<flavor>(ai, aj, ak, al, -ddphi, r_ij, r_kj, r_kl, m, n, f, fshift, pbc, g, x, t1,
+        do_dih_fup<flavor>(ai, aj, ak, al, -ddphi, r_ij, r_kj, r_kl, m, n, f, fshift, pbc, x, t1,
                            t2, t3); /* 112             */
         /* 218 TOTAL   */
     }
@@ -2262,7 +2278,6 @@ real low_angres(int             nbonds,
                 rvec4           f[],
                 rvec            fshift[],
                 const t_pbc*    pbc,
-                const t_graph*  g,
                 real            lambda,
                 real*           dvdlambda,
                 gmx_bool        bZAxis)
@@ -2273,7 +2288,6 @@ real low_angres(int             nbonds,
     rvec r_ij, r_kl, f_i, f_k = { 0, 0, 0 };
     real st, sth, nrij2, nrkl2, c, cij, ckl;
 
-    ivec dt;
     t2 = 0; /* avoid warning with gcc-3.3. It is never used uninitialized */
 
     vtot = 0.0;
@@ -2333,20 +2347,10 @@ real low_angres(int             nbonds,
 
             if (computeVirial(flavor))
             {
-                if (g)
-                {
-                    ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
-                    t1 = IVEC2IS(dt);
-                }
                 rvec_inc(fshift[t1], f_i);
                 rvec_dec(fshift[CENTRAL], f_i);
                 if (!bZAxis)
                 {
-                    if (g)
-                    {
-                        ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, al), dt);
-                        t2 = IVEC2IS(dt);
-                    }
                     rvec_inc(fshift[t2], f_k);
                     rvec_dec(fshift[CENTRAL], f_k);
                 }
@@ -2365,15 +2369,13 @@ real angres(int             nbonds,
             rvec4           f[],
             rvec            fshift[],
             const t_pbc*    pbc,
-            const t_graph*  g,
             real            lambda,
             real*           dvdlambda,
             const t_mdatoms gmx_unused* md,
             t_fcdata gmx_unused* fcd,
             int gmx_unused* global_atom_index)
 {
-    return low_angres<flavor>(nbonds, forceatoms, forceparams, x, f, fshift, pbc, g, lambda,
-                              dvdlambda, FALSE);
+    return low_angres<flavor>(nbonds, forceatoms, forceparams, x, f, fshift, pbc, lambda, dvdlambda, FALSE);
 }
 
 template<BondedKernelFlavor flavor>
@@ -2384,15 +2386,13 @@ real angresz(int             nbonds,
              rvec4           f[],
              rvec            fshift[],
              const t_pbc*    pbc,
-             const t_graph*  g,
              real            lambda,
              real*           dvdlambda,
              const t_mdatoms gmx_unused* md,
              t_fcdata gmx_unused* fcd,
              int gmx_unused* global_atom_index)
 {
-    return low_angres<flavor>(nbonds, forceatoms, forceparams, x, f, fshift, pbc, g, lambda,
-                              dvdlambda, TRUE);
+    return low_angres<flavor>(nbonds, forceatoms, forceparams, x, f, fshift, pbc, lambda, dvdlambda, TRUE);
 }
 
 template<BondedKernelFlavor flavor>
@@ -2403,7 +2403,6 @@ real dihres(int             nbonds,
             rvec4           f[],
             rvec            fshift[],
             const t_pbc*    pbc,
-            const t_graph*  g,
             real            lambda,
             real*           dvdlambda,
             const t_mdatoms gmx_unused* md,
@@ -2482,8 +2481,8 @@ real dihres(int             nbonds,
             {
                 *dvdlambda += kfac * ddp * ((dphiB - dphiA) - (phi0B - phi0A));
             }
-            do_dih_fup<flavor>(ai, aj, ak, al, ddphi, r_ij, r_kj, r_kl, m, n, f, fshift, pbc, g, x,
-                               t1, t2, t3); /* 112             */
+            do_dih_fup<flavor>(ai, aj, ak, al, ddphi, r_ij, r_kj, r_kl, m, n, f, fshift, pbc, x, t1,
+                               t2, t3); /* 112         */
         }
     }
     return vtot;
@@ -2497,7 +2496,6 @@ real unimplemented(int gmx_unused nbonds,
                    rvec4 gmx_unused f[],
                    rvec gmx_unused fshift[],
                    const t_pbc gmx_unused* pbc,
-                   const t_graph gmx_unused* g,
                    real gmx_unused lambda,
                    real gmx_unused* dvdlambda,
                    const t_mdatoms gmx_unused* md,
@@ -2515,7 +2513,6 @@ real restrangles(int             nbonds,
                  rvec4           f[],
                  rvec            fshift[],
                  const t_pbc*    pbc,
-                 const t_graph*  g,
                  real gmx_unused lambda,
                  real gmx_unused* dvdlambda,
                  const t_mdatoms gmx_unused* md,
@@ -2525,7 +2522,6 @@ real restrangles(int             nbonds,
     int    i, d, ai, aj, ak, type, m;
     int    t1, t2;
     real   v, vtot;
-    ivec   jt, dt_ij, dt_kj;
     rvec   f_i, f_j, f_k;
     double prefactor, ratio_ante, ratio_post;
     rvec   delta_ante, delta_post, vec_temp;
@@ -2550,7 +2546,7 @@ real restrangles(int             nbonds,
            real restrangles(int nbonds,
             const t_iatom forceatoms[],const t_iparams forceparams[],
             const rvec x[],rvec4 f[],rvec fshift[],
-            const t_pbc *pbc,const t_graph *g,
+            const t_pbc *pbc,
             real gmx_unused lambda,real gmx_unused *dvdlambda,
             const t_mdatoms gmx_unused *md,t_fcdata gmx_unused *fcd,
             int gmx_unused *global_atom_index)
@@ -2559,7 +2555,6 @@ real restrangles(int             nbonds,
            int t1, t2;
            rvec r_ij,r_kj;
            real v, vtot;
-           ivec jt,dt_ij,dt_kj;
            rvec f_i, f_j, f_k;
            real prefactor, ratio_ante, ratio_post;
            rvec delta_ante, delta_post, vec_temp;
@@ -2603,15 +2598,6 @@ real restrangles(int             nbonds,
 
         if (computeVirial(flavor))
         {
-            if (g)
-            {
-                copy_ivec(SHIFT_IVEC(g, aj), jt);
-                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                t1 = IVEC2IS(dt_ij);
-                t2 = IVEC2IS(dt_kj);
-            }
-
             rvec_inc(fshift[t1], f_i);
             rvec_inc(fshift[CENTRAL], f_j);
             rvec_inc(fshift[t2], f_k);
@@ -2629,7 +2615,6 @@ real restrdihs(int             nbonds,
                rvec4           f[],
                rvec            fshift[],
                const t_pbc*    pbc,
-               const t_graph*  g,
                real gmx_unused lambda,
                real gmx_unused* dvlambda,
                const t_mdatoms gmx_unused* md,
@@ -2639,7 +2624,6 @@ real restrdihs(int             nbonds,
     int  i, d, type, ai, aj, ak, al;
     rvec f_i, f_j, f_k, f_l;
     rvec dx_jl;
-    ivec jt, dt_ij, dt_kj, dt_lj;
     int  t1, t2, t3;
     real v, vtot;
     rvec delta_ante, delta_crnt, delta_post, vec_temp;
@@ -2711,17 +2695,7 @@ real restrdihs(int             nbonds,
 
         if (computeVirial(flavor))
         {
-            if (g)
-            {
-                copy_ivec(SHIFT_IVEC(g, aj), jt);
-                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                ivec_sub(SHIFT_IVEC(g, al), jt, dt_lj);
-                t1 = IVEC2IS(dt_ij);
-                t2 = IVEC2IS(dt_kj);
-                t3 = IVEC2IS(dt_lj);
-            }
-            else if (pbc)
+            if (pbc)
             {
                 t3 = pbc_rvec_sub(pbc, x[al], x[aj], dx_jl);
             }
@@ -2749,7 +2723,6 @@ real cbtdihs(int             nbonds,
              rvec4           f[],
              rvec            fshift[],
              const t_pbc*    pbc,
-             const t_graph*  g,
              real gmx_unused lambda,
              real gmx_unused* dvdlambda,
              const t_mdatoms gmx_unused* md,
@@ -2761,7 +2734,6 @@ real cbtdihs(int             nbonds,
     real v, vtot;
     rvec vec_temp;
     rvec f_i, f_j, f_k, f_l;
-    ivec jt, dt_ij, dt_kj, dt_lj;
     rvec dx_jl;
     rvec delta_ante, delta_crnt, delta_post;
     rvec f_phi_ai, f_phi_aj, f_phi_ak, f_phi_al;
@@ -2825,17 +2797,7 @@ real cbtdihs(int             nbonds,
 
         if (computeVirial(flavor))
         {
-            if (g)
-            {
-                copy_ivec(SHIFT_IVEC(g, aj), jt);
-                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                ivec_sub(SHIFT_IVEC(g, al), jt, dt_lj);
-                t1 = IVEC2IS(dt_ij);
-                t2 = IVEC2IS(dt_kj);
-                t3 = IVEC2IS(dt_lj);
-            }
-            else if (pbc)
+            if (pbc)
             {
                 t3 = pbc_rvec_sub(pbc, x[al], x[aj], dx_jl);
             }
@@ -2863,7 +2825,6 @@ rbdihs(int             nbonds,
        rvec4           f[],
        rvec            fshift[],
        const t_pbc*    pbc,
-       const t_graph*  g,
        real            lambda,
        real*           dvdlambda,
        const t_mdatoms gmx_unused* md,
@@ -2955,8 +2916,8 @@ rbdihs(int             nbonds,
 
         ddphi = -ddphi * sin_phi; /*  11               */
 
-        do_dih_fup<flavor>(ai, aj, ak, al, ddphi, r_ij, r_kj, r_kl, m, n, f, fshift, pbc, g, x, t1,
-                           t2, t3); /* 112             */
+        do_dih_fup<flavor>(ai, aj, ak, al, ddphi, r_ij, r_kj, r_kl, m, n, f, fshift, pbc, x, t1, t2,
+                           t3); /* 112         */
         vtot += v;
     }
     *dvdlambda += dvdl_term;
@@ -3007,15 +2968,14 @@ int cmap_setup_grid_index(int ip, int grid_spacing, int* ipm1, int* ipp1, int* i
 
 } // namespace
 
-real cmap_dihs(int                   nbonds,
-               const t_iatom         forceatoms[],
-               const t_iparams       forceparams[],
-               const gmx_cmap_t*     cmap_grid,
-               const rvec            x[],
-               rvec4                 f[],
-               rvec                  fshift[],
-               const struct t_pbc*   pbc,
-               const struct t_graph* g,
+real cmap_dihs(int                 nbonds,
+               const t_iatom       forceatoms[],
+               const t_iparams     forceparams[],
+               const gmx_cmap_t*   cmap_grid,
+               const rvec          x[],
+               rvec4               f[],
+               rvec                fshift[],
+               const struct t_pbc* pbc,
                real gmx_unused lambda,
                real gmx_unused* dvdlambda,
                const t_mdatoms gmx_unused* md,
@@ -3049,8 +3009,6 @@ real cmap_dihs(int                   nbonds,
     rvec a1, b1, a2, b2;
     rvec f1, g1, h1, f2, g2, h2;
     rvec dtf1, dtg1, dth1, dtf2, dtg2, dth2;
-    ivec jt1, dt1_ij, dt1_kj, dt1_lj;
-    ivec jt2, dt2_ij, dt2_kj, dt2_lj;
 
     int loop_index[4][4] = { { 0, 4, 8, 12 }, { 1, 5, 9, 13 }, { 2, 6, 10, 14 }, { 3, 7, 11, 15 } };
 
@@ -3355,25 +3313,7 @@ real cmap_dihs(int                   nbonds,
         /* Shift forces */
         if (fshift != nullptr)
         {
-            if (g)
-            {
-                copy_ivec(SHIFT_IVEC(g, a1j), jt1);
-                ivec_sub(SHIFT_IVEC(g, a1i), jt1, dt1_ij);
-                ivec_sub(SHIFT_IVEC(g, a1k), jt1, dt1_kj);
-                ivec_sub(SHIFT_IVEC(g, a1l), jt1, dt1_lj);
-                t11 = IVEC2IS(dt1_ij);
-                t21 = IVEC2IS(dt1_kj);
-                t31 = IVEC2IS(dt1_lj);
-
-                copy_ivec(SHIFT_IVEC(g, a2j), jt2);
-                ivec_sub(SHIFT_IVEC(g, a2i), jt2, dt2_ij);
-                ivec_sub(SHIFT_IVEC(g, a2k), jt2, dt2_kj);
-                ivec_sub(SHIFT_IVEC(g, a2l), jt2, dt2_lj);
-                t12 = IVEC2IS(dt2_ij);
-                t22 = IVEC2IS(dt2_kj);
-                t32 = IVEC2IS(dt2_lj);
-            }
-            else if (pbc)
+            if (pbc)
             {
                 t31 = pbc_rvec_sub(pbc, x[a1l], x[a1j], h1);
                 t32 = pbc_rvec_sub(pbc, x[a2l], x[a2j], h2);
@@ -3440,7 +3380,6 @@ real g96bonds(int             nbonds,
               rvec4           f[],
               rvec            fshift[],
               const t_pbc*    pbc,
-              const t_graph*  g,
               real            lambda,
               real*           dvdlambda,
               const t_mdatoms gmx_unused* md,
@@ -3467,8 +3406,8 @@ real g96bonds(int             nbonds,
 
         vtot += 0.5 * vbond; /* 1*/
 
-        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, g, fshift); /* 15 */
-    }                                                                  /* 44 TOTAL     */
+        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, fshift); /* 15 */
+    }                                                               /* 44 TOTAL        */
     return vtot;
 }
 
@@ -3493,7 +3432,6 @@ real g96angles(int             nbonds,
                rvec4           f[],
                rvec            fshift[],
                const t_pbc*    pbc,
-               const t_graph*  g,
                real            lambda,
                real*           dvdlambda,
                const t_mdatoms gmx_unused* md,
@@ -3505,7 +3443,6 @@ real g96angles(int             nbonds,
     real cos_theta, dVdt, va, vtot;
     real rij_1, rij_2, rkj_1, rkj_2, rijrkj_1;
     rvec f_i, f_j, f_k;
-    ivec jt, dt_ij, dt_kj;
 
     vtot = 0.0;
     for (i = 0; (i < nbonds);)
@@ -3540,15 +3477,6 @@ real g96angles(int             nbonds,
 
         if (computeVirial(flavor))
         {
-            if (g)
-            {
-                copy_ivec(SHIFT_IVEC(g, aj), jt);
-
-                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                t1 = IVEC2IS(dt_ij);
-                t2 = IVEC2IS(dt_kj);
-            }
             rvec_inc(fshift[t1], f_i);
             rvec_inc(fshift[CENTRAL], f_j);
             rvec_inc(fshift[t2], f_k); /* 9 */
@@ -3566,7 +3494,6 @@ real cross_bond_bond(int             nbonds,
                      rvec4           f[],
                      rvec            fshift[],
                      const t_pbc*    pbc,
-                     const t_graph*  g,
                      real gmx_unused lambda,
                      real gmx_unused* dvdlambda,
                      const t_mdatoms gmx_unused* md,
@@ -3580,7 +3507,6 @@ real cross_bond_bond(int             nbonds,
     rvec r_ij, r_kj;
     real vtot, vrr, s1, s2, r1, r2, r1e, r2e, krr;
     rvec f_i, f_j, f_k;
-    ivec jt, dt_ij, dt_kj;
 
     vtot = 0.0;
     for (i = 0; (i < nbonds);)
@@ -3623,15 +3549,6 @@ real cross_bond_bond(int             nbonds,
 
         if (computeVirial(flavor))
         {
-            if (g)
-            {
-                copy_ivec(SHIFT_IVEC(g, aj), jt);
-
-                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                t1 = IVEC2IS(dt_ij);
-                t2 = IVEC2IS(dt_kj);
-            }
             rvec_inc(fshift[t1], f_i);
             rvec_inc(fshift[CENTRAL], f_j);
             rvec_inc(fshift[t2], f_k); /* 9 */
@@ -3649,7 +3566,6 @@ real cross_bond_angle(int             nbonds,
                       rvec4           f[],
                       rvec            fshift[],
                       const t_pbc*    pbc,
-                      const t_graph*  g,
                       real gmx_unused lambda,
                       real gmx_unused* dvdlambda,
                       const t_mdatoms gmx_unused* md,
@@ -3663,7 +3579,6 @@ real cross_bond_angle(int             nbonds,
     rvec r_ij, r_kj, r_ik;
     real vtot, vrt, s1, s2, s3, r1, r2, r3, r1e, r2e, r3e, krt, k1, k2, k3;
     rvec f_i, f_j, f_k;
-    ivec jt, dt_ij, dt_kj;
 
     vtot = 0.0;
     for (i = 0; (i < nbonds);)
@@ -3716,15 +3631,6 @@ real cross_bond_angle(int             nbonds,
 
         if (computeVirial(flavor))
         {
-            if (g)
-            {
-                copy_ivec(SHIFT_IVEC(g, aj), jt);
-
-                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                t1 = IVEC2IS(dt_ij);
-                t2 = IVEC2IS(dt_kj);
-            }
             rvec_inc(fshift[t1], f_i);
             rvec_inc(fshift[CENTRAL], f_j);
             rvec_inc(fshift[t2], f_k); /* 9 */
@@ -3745,14 +3651,14 @@ real bonded_tab(const char*          type,
                 real*                V,
                 real*                F)
 {
-    real k, tabscale, *VFtab, rt, eps, eps2, Yt, Ft, Geps, Heps2, Fp, VV, FF;
+    real k, tabscale, rt, eps, eps2, Yt, Ft, Geps, Heps2, Fp, VV, FF;
     int  n0, nnn;
     real dvdlambda;
 
     k = (1.0 - lambda) * kA + lambda * kB;
 
-    tabscale = table->scale;
-    VFtab    = table->data;
+    tabscale          = table->scale;
+    const real* VFtab = table->data.data();
 
     rt = r * tabscale;
     n0 = static_cast<int>(rt);
@@ -3791,7 +3697,6 @@ real tab_bonds(int             nbonds,
                rvec4           f[],
                rvec            fshift[],
                const t_pbc*    pbc,
-               const t_graph*  g,
                real            lambda,
                real*           dvdlambda,
                const t_mdatoms gmx_unused* md,
@@ -3827,8 +3732,8 @@ real tab_bonds(int             nbonds,
         vtot += vbond;              /* 1*/
         fbond *= gmx::invsqrt(dr2); /*   6             */
 
-        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, g, fshift); /* 15 */
-    }                                                                  /* 62 TOTAL     */
+        spreadBondForces<flavor>(fbond, dx, ai, aj, f, ki, fshift); /* 15 */
+    }                                                               /* 62 TOTAL        */
     return vtot;
 }
 
@@ -3840,7 +3745,6 @@ real tab_angles(int             nbonds,
                 rvec4           f[],
                 rvec            fshift[],
                 const t_pbc*    pbc,
-                const t_graph*  g,
                 real            lambda,
                 real*           dvdlambda,
                 const t_mdatoms gmx_unused* md,
@@ -3850,7 +3754,6 @@ real tab_angles(int             nbonds,
     int  i, ai, aj, ak, t1, t2, type, table;
     rvec r_ij, r_kj;
     real cos_theta, cos_theta2, theta, dVdt, va, vtot;
-    ivec jt, dt_ij, dt_kj;
 
     vtot = 0.0;
     for (i = 0; (i < nbonds);)
@@ -3898,15 +3801,6 @@ real tab_angles(int             nbonds,
 
             if (computeVirial(flavor))
             {
-                if (g)
-                {
-                    copy_ivec(SHIFT_IVEC(g, aj), jt);
-
-                    ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
-                    ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
-                    t1 = IVEC2IS(dt_ij);
-                    t2 = IVEC2IS(dt_kj);
-                }
                 rvec_inc(fshift[t1], f_i);
                 rvec_inc(fshift[CENTRAL], f_j);
                 rvec_inc(fshift[t2], f_k);
@@ -3924,7 +3818,6 @@ real tab_dihs(int             nbonds,
               rvec4           f[],
               rvec            fshift[],
               const t_pbc*    pbc,
-              const t_graph*  g,
               real            lambda,
               real*           dvdlambda,
               const t_mdatoms gmx_unused* md,
@@ -3954,7 +3847,7 @@ real tab_dihs(int             nbonds,
                                  forceparams[type].tab.kB, phi + M_PI, lambda, &vpd, &ddphi);
 
         vtot += vpd;
-        do_dih_fup<flavor>(ai, aj, ak, al, -ddphi, r_ij, r_kj, r_kl, m, n, f, fshift, pbc, g, x, t1,
+        do_dih_fup<flavor>(ai, aj, ak, al, -ddphi, r_ij, r_kj, r_kl, m, n, f, fshift, pbc, x, t1,
                            t2, t3); /* 112     */
 
     } /* 227 TOTAL  */
@@ -4087,25 +3980,24 @@ gmx::EnumerationArray<BondedKernelFlavor, std::array<BondedInteractions, F_NRE>>
 
 } // namespace
 
-real calculateSimpleBond(const int            ftype,
-                         const int            numForceatoms,
-                         const t_iatom        forceatoms[],
-                         const t_iparams      forceparams[],
-                         const rvec           x[],
-                         rvec4                f[],
-                         rvec                 fshift[],
-                         const struct t_pbc*  pbc,
-                         const struct t_graph gmx_unused* g,
-                         const real                       lambda,
-                         real*                            dvdlambda,
-                         const t_mdatoms*                 md,
-                         t_fcdata*                        fcd,
+real calculateSimpleBond(const int           ftype,
+                         const int           numForceatoms,
+                         const t_iatom       forceatoms[],
+                         const t_iparams     forceparams[],
+                         const rvec          x[],
+                         rvec4               f[],
+                         rvec                fshift[],
+                         const struct t_pbc* pbc,
+                         const real          lambda,
+                         real*               dvdlambda,
+                         const t_mdatoms*    md,
+                         t_fcdata*           fcd,
                          int gmx_unused*          global_atom_index,
                          const BondedKernelFlavor bondedKernelFlavor)
 {
     const BondedInteractions& bonded = c_bondedInteractionFunctionsPerFlavor[bondedKernelFlavor][ftype];
 
-    real v = bonded.function(numForceatoms, forceatoms, forceparams, x, f, fshift, pbc, g, lambda,
+    real v = bonded.function(numForceatoms, forceatoms, forceparams, x, f, fshift, pbc, lambda,
                              dvdlambda, md, fcd, global_atom_index);
 
     return v;
index e6d129dd7808d1fbfd781393ada856534c34724c..4a7182c88df2b848c0c5b1ab8ee1bd84ce6f1669 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,7 +54,6 @@
 
 struct gmx_cmap_t;
 struct t_fcdata;
-struct t_graph;
 struct t_mdatom;
 struct t_nrnb;
 struct t_pbc;
@@ -85,38 +85,36 @@ real dih_angle(const rvec          xi,
                int*                t3);
 
 /*! \brief Do an update of the forces for dihedral potentials */
-void do_dih_fup(int                   i,
-                int                   j,
-                int                   k,
-                int                   l,
-                real                  ddphi,
-                rvec                  r_ij,
-                rvec                  r_kj,
-                rvec                  r_kl,
-                rvec                  m,
-                rvec                  n,
-                rvec4                 f[],
-                rvec                  fshift[],
-                const struct t_pbc*   pbc,
-                const struct t_graph* g,
-                const rvec*           x,
-                int                   t1,
-                int                   t2,
-                int                   t3);
+void do_dih_fup(int                 i,
+                int                 j,
+                int                 k,
+                int                 l,
+                real                ddphi,
+                rvec                r_ij,
+                rvec                r_kj,
+                rvec                r_kl,
+                rvec                m,
+                rvec                n,
+                rvec4               f[],
+                rvec                fshift[],
+                const struct t_pbc* pbc,
+                const rvec*         x,
+                int                 t1,
+                int                 t2,
+                int                 t3);
 
 /*! \brief Make a dihedral fall in the range (-pi,pi) */
 void make_dp_periodic(real* dp);
 
 /*! \brief Compute CMAP dihedral energies and forces */
-real cmap_dihs(int                   nbonds,
-               const t_iatom         forceatoms[],
-               const t_iparams       forceparams[],
-               const gmx_cmap_t*     cmap_grid,
-               const rvec            x[],
-               rvec4                 f[],
-               rvec                  fshift[],
-               const struct t_pbc*   pbc,
-               const struct t_graph* g,
+real cmap_dihs(int                 nbonds,
+               const t_iatom       forceatoms[],
+               const t_iparams     forceparams[],
+               const gmx_cmap_t*   cmap_grid,
+               const rvec          x[],
+               rvec4               f[],
+               rvec                fshift[],
+               const struct t_pbc* pbc,
                real gmx_unused lambda,
                real gmx_unused* dvdlambda,
                const t_mdatoms gmx_unused* md,
@@ -159,19 +157,18 @@ static constexpr inline bool computeEnergyOrVirial(const BondedKernelFlavor flav
  * All pointers should be non-null, except for pbc and g which can be nullptr.
  * \returns the energy or 0 when \p bondedKernelFlavor did not request the energy.
  */
-real calculateSimpleBond(int                  ftype,
-                         int                  numForceatoms,
-                         const t_iatom        forceatoms[],
-                         const t_iparams      forceparams[],
-                         const rvec           x[],
-                         rvec4                f[],
-                         rvec                 fshift[],
-                         const struct t_pbc*  pbc,
-                         const struct t_graph gmx_unused* g,
-                         real                             lambda,
-                         real*                            dvdlambda,
-                         const t_mdatoms*                 md,
-                         t_fcdata*                        fcd,
+real calculateSimpleBond(int                 ftype,
+                         int                 numForceatoms,
+                         const t_iatom       forceatoms[],
+                         const t_iparams     forceparams[],
+                         const rvec          x[],
+                         rvec4               f[],
+                         rvec                fshift[],
+                         const struct t_pbc* pbc,
+                         real                lambda,
+                         real*               dvdlambda,
+                         const t_mdatoms*    md,
+                         t_fcdata*           fcd,
                          int gmx_unused*    global_atom_index,
                          BondedKernelFlavor bondedKernelFlavor);
 
index 0847e759a8cd2bea570a2ad25da4ca1b5dd06e37..d95867cf7d2cdf7dceba975f07782c7100d6df28 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -57,7 +58,6 @@
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/pbcutil/ishift.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/topology/topology.h"
 void init_disres(FILE*                 fplog,
                  const gmx_mtop_t*     mtop,
                  t_inputrec*           ir,
-                 const t_commrec*      cr,
+                 DisResRunMode         disResRunMode,
+                 DDRole                ddRole,
+                 NumRanks              numRanks,
+                 MPI_Comm              communicator,
                  const gmx_multisim_t* ms,
-                 t_fcdata*             fcd,
+                 t_disresdata*         dd,
                  t_state*              state,
                  gmx_bool              bIsREMD)
 {
     int                  fa, nmol, npair, np;
-    t_disresdata*        dd;
     history_t*           hist;
     gmx_mtop_ilistloop_t iloop;
     char*                ptr;
     int                  type_min, type_max;
 
-    dd = &(fcd->disres);
-
     if (gmx_mtop_ftype_count(mtop, F_DISRES) == 0)
     {
         dd->nres = 0;
@@ -116,10 +116,10 @@ void init_disres(FILE*                 fplog,
     {
         /* We store the r^-6 time averages in an array that is indexed
          * with the local disres iatom index, so this doesn't work with DD.
-         * Note that DD is not initialized yet here, so we check for PAR(cr),
+         * Note that DD is not initialized yet here, so we check that we are on multiple ranks,
          * but there are probably also issues with e.g. NM MPI parallelization.
          */
-        if (cr && PAR(cr))
+        if ((disResRunMode == DisResRunMode::MDRun) && (numRanks == NumRanks::Multiple))
         {
             gmx_fatal(FARGS,
                       "Time-averaged distance restraints are not supported with MPI "
@@ -138,7 +138,7 @@ void init_disres(FILE*                 fplog,
     iloop     = gmx_mtop_ilistloop_init(mtop);
     while (const InteractionLists* il = gmx_mtop_ilistloop_next(iloop, &nmol))
     {
-        if (nmol > 1 && (*il)[F_DISRES].size() > 0 && ir->eDisre != edrEnsemble)
+        if (nmol > 1 && !(*il)[F_DISRES].empty() && ir->eDisre != edrEnsemble)
         {
             gmx_fatal(FARGS,
                       "NMR distance restraints with multiple copies of the same molecule are "
@@ -168,7 +168,7 @@ void init_disres(FILE*                 fplog,
         }
     }
 
-    if (cr && PAR(cr) && ir->nstdisreout > 0)
+    if ((disResRunMode == DisResRunMode::MDRun) && (numRanks == NumRanks::Multiple) && ir->nstdisreout > 0)
     {
         /* With DD we currently only have local pair information available */
         gmx_fatal(FARGS,
@@ -218,7 +218,7 @@ void init_disres(FILE*                 fplog,
     dd->Rtav_6 = &(dd->Rt_6[dd->nres]);
 
     ptr = getenv("GMX_DISRE_ENSEMBLE_SIZE");
-    if (cr && ms != nullptr && ptr != nullptr && !bIsREMD)
+    if ((disResRunMode == DisResRunMode::MDRun) && ms != nullptr && ptr != nullptr && !bIsREMD)
     {
 #if GMX_MPI
         dd->nsystems = 0;
@@ -230,32 +230,34 @@ void init_disres(FILE*                 fplog,
         /* This check is only valid on MASTER(cr), so probably
          * ensemble-averaged distance restraints are broken on more
          * than one processor per simulation system. */
-        if (MASTER(cr))
+        if (ddRole == DDRole::Master)
         {
             check_multi_int(fplog, ms, dd->nsystems, "the number of systems per ensemble", FALSE);
         }
-        gmx_bcast_sim(sizeof(int), &dd->nsystems, cr);
+        gmx_bcast(sizeof(int), &dd->nsystems, communicator);
 
         /* We use to allow any value of nsystems which was a divisor
-         * of ms->nsim. But this required an extra communicator which
-         * was stored in t_fcdata. This pulled in mpi.h in nearly all C files.
+         * of ms->numSimulations_. But this required an extra communicator which
+         * pulled in mpi.h in nearly all C files.
          */
-        if (!(ms->nsim == 1 || ms->nsim == dd->nsystems))
+        if (!(ms->numSimulations_ == 1 || ms->numSimulations_ == dd->nsystems))
         {
             gmx_fatal(FARGS,
                       "GMX_DISRE_ENSEMBLE_SIZE (%d) is not equal to 1 or the number of systems "
                       "(option -multidir) %d",
-                      dd->nsystems, ms->nsim);
+                      dd->nsystems, ms->numSimulations_);
         }
         if (fplog)
         {
             fprintf(fplog, "Our ensemble consists of systems:");
             for (int i = 0; i < dd->nsystems; i++)
             {
-                fprintf(fplog, " %d", (ms->sim / dd->nsystems) * dd->nsystems + i);
+                fprintf(fplog, " %d", (ms->simulationIndex_ / dd->nsystems) * dd->nsystems + i);
             }
             fprintf(fplog, "\n");
         }
+#else
+        GMX_UNUSED_VALUE(communicator);
 #endif
     }
     else
@@ -285,9 +287,9 @@ void init_disres(FILE*                 fplog,
          * checks from appropriate processes (since check_multi_int is
          * too broken to check whether the communication will
          * succeed...) */
-        if (cr && ms && dd->nsystems > 1 && MASTER(cr))
+        if ((disResRunMode == DisResRunMode::MDRun) && ms && dd->nsystems > 1 && (ddRole == DDRole::Master))
         {
-            check_multi_int(fplog, ms, fcd->disres.nres, "the number of distance restraints", FALSE);
+            check_multi_int(fplog, ms, dd->nres, "the number of distance restraints", FALSE);
         }
         please_cite(fplog, "Tropp80a");
         please_cite(fplog, "Torda89a");
@@ -300,16 +302,14 @@ void calc_disres_R_6(const t_commrec*      cr,
                      const t_iatom         forceatoms[],
                      const rvec            x[],
                      const t_pbc*          pbc,
-                     t_fcdata*             fcd,
+                     t_disresdata*         dd,
                      history_t*            hist)
 {
-    rvec          dx;
-    real *        rt, *rm3tav, *Rtl_6, *Rt_6, *Rtav_6;
-    t_disresdata* dd;
-    real          ETerm, ETerm1, cf1 = 0, cf2 = 0;
-    gmx_bool      bTav;
+    rvec     dx;
+    real *   rt, *rm3tav, *Rtl_6, *Rt_6, *Rtav_6;
+    real     ETerm, ETerm1, cf1 = 0, cf2 = 0;
+    gmx_bool bTav;
 
-    dd     = &(fcd->disres);
     bTav   = (dd->dr_tau != 0);
     ETerm  = dd->ETerm;
     ETerm1 = dd->ETerm1;
@@ -360,7 +360,7 @@ void calc_disres_R_6(const t_commrec*      cr,
         rt[pair] = rt2 * rt_1;
         if (bTav)
         {
-            /* Here we update rm3tav in t_fcdata using the data
+            /* Here we update rm3tav in t_disresdata using the data
              * in history_t.
              * Thus the results stay correct when this routine
              * is called multiple times.
@@ -386,7 +386,7 @@ void calc_disres_R_6(const t_commrec*      cr,
         gmx_sum(2 * dd->nres, dd->Rt_6, cr);
     }
 
-    if (fcd->disres.nsystems > 1)
+    if (dd->nsystems > 1)
     {
         real invn = 1.0 / dd->nsystems;
 
@@ -402,7 +402,7 @@ void calc_disres_R_6(const t_commrec*      cr,
 
         if (DOMAINDECOMP(cr))
         {
-            gmx_bcast(2 * dd->nres, dd->Rt_6, cr);
+            gmx_bcast(2 * dd->nres, dd->Rt_6, cr->mpi_comm_mygroup);
         }
     }
 
@@ -422,7 +422,6 @@ real ta_disres(int             nfa,
                rvec4           f[],
                rvec            fshift[],
                const t_pbc*    pbc,
-               const t_graph*  g,
                real gmx_unused lambda,
                real gmx_unused* dvdlambda,
                const t_mdatoms gmx_unused* md,
@@ -439,12 +438,11 @@ real ta_disres(int             nfa,
     real          tav_viol_Rtav7, instant_viol_Rtav7;
     real          up1, up2, low;
     gmx_bool      bConservative, bMixed, bViolation;
-    ivec          dt;
     t_disresdata* dd;
     int           dr_weighting;
     gmx_bool      dr_bMixed;
 
-    dd           = &(fcd->disres);
+    dd           = fcd->disres;
     dr_weighting = dd->dr_weighting;
     dr_bMixed    = dd->dr_bMixed;
     Rtl_6        = dd->Rtl_6;
@@ -618,12 +616,6 @@ real ta_disres(int             nfa,
 
             fk_scal = f_scal * weight_rt_1;
 
-            if (g)
-            {
-                ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
-                ki = IVEC2IS(dt);
-            }
-
             for (int m = 0; m < DIM; m++)
             {
                 fij = fk_scal * dx[m];
@@ -646,21 +638,17 @@ real ta_disres(int             nfa,
     return vtot;
 }
 
-void update_disres_history(const t_fcdata* fcd, history_t* hist)
+void update_disres_history(const t_disresdata& dd, history_t* hist)
 {
-    const t_disresdata* dd;
-    int                 pair;
-
-    dd = &(fcd->disres);
-    if (dd->dr_tau != 0)
+    if (dd.dr_tau != 0)
     {
         /* Copy the new time averages that have been calculated
          * in calc_disres_R_6.
          */
-        hist->disre_initf = dd->exp_min_t_tau;
-        for (pair = 0; pair < dd->npair; pair++)
+        hist->disre_initf = dd.exp_min_t_tau;
+        for (int pair = 0; pair < dd.npair; pair++)
         {
-            hist->disre_rm3tav[pair] = dd->rm3tav[pair];
+            hist->disre_rm3tav[pair] = dd.rm3tav[pair];
         }
     }
 }
index affe0c551e02001dfb7dcebf926526036ef8ec84..7144dd0f125d71e60fd6f5501d1d97f64150c698 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxmpi.h"
 
 struct gmx_mtop_t;
 struct gmx_multisim_t;
 class history_t;
 struct t_commrec;
+struct t_disresdata;
+struct t_fcdata;
 struct t_inputrec;
 struct t_pbc;
 class t_state;
+enum class DDRole;
+enum class NumRanks;
+
+//! Whether distance restraints are called from mdrun or from an analysis tool
+enum class DisResRunMode
+{
+    MDRun,
+    AnalysisTool
+};
 
 /*! \brief
- * Initiates *fcd data.
+ * Initiates *disresdata.
  *
  * Must be called once, nbonds is the number
  * of iatoms in the ilist of the idef struct.
@@ -70,9 +83,12 @@ class t_state;
 void init_disres(FILE*                 fplog,
                  const gmx_mtop_t*     mtop,
                  t_inputrec*           ir,
-                 const t_commrec*      cr,
+                 DisResRunMode         disResRunMode,
+                 DDRole                ddRole,
+                 NumRanks              numRanks,
+                 MPI_Comm              communicator,
                  const gmx_multisim_t* ms,
-                 t_fcdata*             fcd,
+                 t_disresdata*         disresdata,
                  t_state*              state,
                  gmx_bool              bIsREMD);
 
@@ -86,7 +102,7 @@ void calc_disres_R_6(const t_commrec*      cr,
                      const t_iatom*        fa,
                      const rvec*           x,
                      const t_pbc*          pbc,
-                     t_fcdata*             fcd,
+                     t_disresdata*         disresdata,
                      history_t*            hist);
 
 //! Calculates the distance restraint forces, return the potential.
@@ -97,14 +113,13 @@ real ta_disres(int              nfa,
                rvec4            f[],
                rvec             fshift[],
                const t_pbc*     pbc,
-               const t_graph*   g,
                real             lambda,
                real*            dvdlambda,
                const t_mdatoms* md,
-               t_fcdata*        fcd,
+               t_fcdata*        fcdata,
                int*             global_atom_index);
 
 //! Copies the new time averages that have been calculated in calc_disres_R_6.
-void update_disres_history(const t_fcdata* fcd, history_t* hist);
+void update_disres_history(const t_disresdata& disresdata, history_t* hist);
 
 #endif
index 4cdc9655a428193c0a9b2e58230ca9b62d4045de..1cfeed429d2e92e84a4019d9366ab9361cc81fd7 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_LISTED_FORCES_GPUBONDED_H
 #define GMX_LISTED_FORCES_GPUBONDED_H
 
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
 #include "gromacs/math/vectypes.h"
+#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/idef.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/classhelpers.h"
 
+class DeviceContext;
+class DeviceStream;
+
 struct gmx_enerdata_t;
 struct gmx_ffparams_t;
 struct gmx_mtop_t;
-struct t_forcerec;
-struct t_idef;
 struct t_inputrec;
 struct gmx_wallcycle;
 
@@ -65,6 +68,8 @@ struct gmx_wallcycle;
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
 class StepWorkload;
 
 /*! \brief The number on bonded function types supported on GPUs */
@@ -104,8 +109,21 @@ bool inputSupportsGpuBondeds(const t_inputrec& ir, const gmx_mtop_t& mtop, std::
 class GpuBonded
 {
 public:
-    //! Construct the manager with constant data and the stream to use.
-    GpuBonded(const gmx_ffparams_t& ffparams, void* streamPtr, gmx_wallcycle* wcycle);
+    /*! \brief Construct the manager with constant data and the stream to use.
+     *
+     * \param[in] ffparams                   Force-field parameters.
+     * \param[in] electrostaticsScaleFactor  Scaling factor for the electrostatic potential
+     *                                       (Coulomb constant, multiplied by the Fudge factor).
+     * \param[in] deviceContext              GPU device context (not used in CUDA).
+     * \param[in] deviceStream               GPU device stream.
+     * \param[in] wcycle                     The wallclock counter.
+     *
+     */
+    GpuBonded(const gmx_ffparams_t& ffparams,
+              float                 electrostaticsScaleFactor,
+              const DeviceContext&  deviceContext,
+              const DeviceStream&   deviceStream,
+              gmx_wallcycle*        wcycle);
     //! Destructor
     ~GpuBonded();
 
@@ -115,22 +133,67 @@ public:
      * Intended to be called after each neighbour search
      * stage. Copies the bonded interactions assigned to the GPU
      * to device data structures, and updates device buffers that
-     * may have been updated after search. */
-    void updateInteractionListsAndDeviceBuffers(ArrayRef<const int> nbnxnAtomOrder,
-                                                const t_idef&       idef,
-                                                void*               xqDevice,
-                                                void*               forceDevice,
-                                                void*               fshiftDevice);
+     * may have been updated after search.
+     *
+     * \param[in]     nbnxnAtomOrder  Mapping between rvec and NBNXM formats.
+     * \param[in]     idef            List of interactions to compute.
+     * \param[in]     xqDevice        Device buffer with coordinates and charge in xyzq-format.
+     * \param[in,out] forceDevice     Device buffer with forces.
+     * \param[in,out] fshiftDevice    Device buffer with shift forces.
+     */
+    void updateInteractionListsAndDeviceBuffers(ArrayRef<const int>           nbnxnAtomOrder,
+                                                const InteractionDefinitions& idef,
+                                                void*                         xqDevice,
+                                                DeviceBuffer<RVec>            forceDevice,
+                                                DeviceBuffer<RVec>            fshiftDevice);
+    /*! \brief
+     * Update PBC data.
+     *
+     * Converts PBC data from t_pbc into the PbcAiuc format and stores the latter.
+     *
+     * \param[in] pbcType The type of the periodic boundary.
+     * \param[in] box     The periodic boundary box matrix.
+     * \param[in] canMoleculeSpanPbc  Whether one molecule can have atoms in different PBC cells.
+     */
+    void setPbc(PbcType pbcType, const matrix box, bool canMoleculeSpanPbc);
+
     /*! \brief Returns whether there are bonded interactions
-     * assigned to the GPU */
+     * assigned to the GPU
+     *
+     * \returns If the list of interaction has elements.
+     */
     bool haveInteractions() const;
-    /*! \brief Launches bonded kernel on a GPU */
-    void launchKernel(const t_forcerec* fr, const gmx::StepWorkload& stepWork, const matrix box);
-    /*! \brief Launches the transfer of computed bonded energies. */
+
+    /*! \brief Launches bonded kernel on a GPU
+     *
+     * \param[in]  stepWork  Simulation step work to determine if energy/virial are to be computed on this step.
+     */
+    void launchKernel(const gmx::StepWorkload& stepWork);
+
+    /*! \brief Sets the PBC and launches bonded kernel on a GPU
+     *
+     * \param[in] pbcType The type of the periodic boundary.
+     * \param[in] box     The periodic boundary box matrix.
+     * \param[in] canMoleculeSpanPbc  Whether one molecule can have atoms in different PBC cells.
+     * \param[in] stepWork  Simulation step work to determine if energy/virial are to be computed on this step.
+     */
+    void setPbcAndlaunchKernel(PbcType                  pbcType,
+                               const matrix             box,
+                               bool                     canMoleculeSpanPbc,
+                               const gmx::StepWorkload& stepWork);
+
+    /*! \brief Launches the transfer of computed bonded energies.
+     */
     void launchEnergyTransfer();
-    /*! \brief Waits on the energy transfer, and accumulates bonded energies to \c enerd. */
+
+    /*! \brief Waits on the energy transfer, and accumulates bonded energies to \c enerd.
+     *
+     * \param[in,out] enerd The energy data object to add energy terms to.
+     */
     void waitAccumulateEnergyTerms(gmx_enerdata_t* enerd);
-    /*! \brief Clears the device side energy buffer */
+
+    /*! \brief Clears the device side energy buffer
+     */
     void clearEnergies();
 
 private:
index e6dd0d8de91e67a727fd9d2a9f6cccd8c5d230b6..ff6232572373ab96a699cf5bd32acb599e548dbf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -120,11 +120,11 @@ bool buildSupportsGpuBondeds(std::string* error)
     {
         errorReasons.emplace_back("not supported with double precision");
     }
-    if (GMX_GPU == GMX_GPU_OPENCL)
+    if (GMX_GPU_OPENCL)
     {
         errorReasons.emplace_back("not supported with OpenCL build of GROMACS");
     }
-    else if (GMX_GPU == GMX_GPU_NONE)
+    else if (!GMX_GPU)
     {
         errorReasons.emplace_back("not supported with CPU-only build of GROMACS");
     }
@@ -149,6 +149,10 @@ bool inputSupportsGpuBondeds(const t_inputrec& ir, const gmx_mtop_t& mtop, std::
     {
         errorReasons.emplace_back("MiMiC");
     }
+    if (ir.useMts)
+    {
+        errorReasons.emplace_back("Cannot run with multiple time stepping");
+    }
     if (ir.opts.ngener > 1)
     {
         errorReasons.emplace_back("Cannot run with multiple energy groups");
@@ -156,13 +160,17 @@ bool inputSupportsGpuBondeds(const t_inputrec& ir, const gmx_mtop_t& mtop, std::
     return addMessageIfNotSupported(errorReasons, error);
 }
 
-#if GMX_GPU != GMX_GPU_CUDA
+#if !GMX_GPU_CUDA
 
 class GpuBonded::Impl
 {
 };
 
-GpuBonded::GpuBonded(const gmx_ffparams_t& /* ffparams */, void* /*streamPtr */, gmx_wallcycle* /* wcycle */) :
+GpuBonded::GpuBonded(const gmx_ffparams_t& /* ffparams */,
+                     const float /* electrostaticsScaleFactor */,
+                     const DeviceContext& /* deviceContext */,
+                     const DeviceStream& /* deviceStream */,
+                     gmx_wallcycle* /* wcycle */) :
     impl_(nullptr)
 {
 }
@@ -170,21 +178,28 @@ GpuBonded::GpuBonded(const gmx_ffparams_t& /* ffparams */, void* /*streamPtr */,
 GpuBonded::~GpuBonded() = default;
 
 void GpuBonded::updateInteractionListsAndDeviceBuffers(ArrayRef<const int> /* nbnxnAtomOrder */,
-                                                       const t_idef& /* idef */,
+                                                       const InteractionDefinitions& /* idef */,
                                                        void* /* xqDevice */,
-                                                       void* /* forceDevice */,
-                                                       void* /* fshiftDevice */)
+                                                       DeviceBuffer<RVec> /* forceDevice */,
+                                                       DeviceBuffer<RVec> /* fshiftDevice */)
+{
+}
+
+void GpuBonded::setPbc(PbcType /* pbcType */, const matrix /* box */, bool /* canMoleculeSpanPbc */)
 {
 }
 
 bool GpuBonded::haveInteractions() const
 {
-    return false;
+    return !impl_;
 }
 
-void GpuBonded::launchKernel(const t_forcerec* /* fr */,
-                             const gmx::StepWorkload& /* stepWork */,
-                             const matrix /* box */)
+void GpuBonded::launchKernel(const gmx::StepWorkload& /* stepWork */) {}
+
+void GpuBonded::setPbcAndlaunchKernel(PbcType /* pbcType */,
+                                      const matrix /* box */,
+                                      bool /* canMoleculeSpanPbc */,
+                                      const gmx::StepWorkload& /* stepWork */)
 {
 }
 
@@ -194,6 +209,6 @@ void GpuBonded::waitAccumulateEnergyTerms(gmx_enerdata_t* /* enerd */) {}
 
 void GpuBonded::clearEnergies() {}
 
-#endif /* GMX_GPU != GMX_GPU_CUDA */
+#endif // !GMX_GPU_CUDA
 
 } // namespace gmx
index 041ee514b77ffa37252ad0d82769b665c9409b24..741244caaf44f7ad22b1cd27983d61ab84129500 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/gpu_utils/cuda_arch_utils.cuh"
 #include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/devicebuffer.h"
+#include "gromacs/gpu_utils/typecasts.cuh"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/timing/wallcycle.h"
 #include "gromacs/topology/forcefieldparameters.h"
@@ -59,42 +62,59 @@ struct t_forcerec;
 
 namespace gmx
 {
+// Number of CUDA threads in a block
+constexpr static int c_threadsPerBlock = 256;
 
 // ---- GpuBonded::Impl
 
-GpuBonded::Impl::Impl(const gmx_ffparams_t& ffparams, void* streamPtr, gmx_wallcycle* wcycle)
+GpuBonded::Impl::Impl(const gmx_ffparams_t& ffparams,
+                      const float           electrostaticsScaleFactor,
+                      const DeviceContext&  deviceContext,
+                      const DeviceStream&   deviceStream,
+                      gmx_wallcycle*        wcycle) :
+    deviceContext_(deviceContext),
+    deviceStream_(deviceStream)
 {
-    stream_ = *static_cast<CommandStream*>(streamPtr);
+    GMX_RELEASE_ASSERT(deviceStream.isValid(),
+                       "Can't run GPU version of bonded forces in stream that is not valid.");
+
+    static_assert(c_threadsPerBlock >= SHIFTS,
+                  "Threads per block in GPU bonded must be >= SHIFTS for the virial kernel "
+                  "(calcVir=true)");
+
     wcycle_ = wcycle;
 
-    allocateDeviceBuffer(&d_forceParams_, ffparams.numTypes(), nullptr);
+    allocateDeviceBuffer(&d_forceParams_, ffparams.numTypes(), deviceContext_);
     // This could be an async transfer (if the source is pinned), so
     // long as it uses the same stream as the kernels and we are happy
     // to consume additional pinned pages.
-    copyToDeviceBuffer(&d_forceParams_, ffparams.iparams.data(), 0, ffparams.numTypes(), stream_,
-                       GpuApiCallBehavior::Sync, nullptr);
+    copyToDeviceBuffer(&d_forceParams_, ffparams.iparams.data(), 0, ffparams.numTypes(),
+                       deviceStream_, GpuApiCallBehavior::Sync, nullptr);
     vTot_.resize(F_NRE);
-    allocateDeviceBuffer(&d_vTot_, F_NRE, nullptr);
-    clearDeviceBufferAsync(&d_vTot_, 0, F_NRE, stream_);
-
-    for (int fType = 0; fType < F_NRE; fType++)
-    {
-        d_iLists_[fType].nr     = 0;
-        d_iLists_[fType].iatoms = nullptr;
-        d_iLists_[fType].nalloc = 0;
-    }
-
-    kernelParams_.d_forceParams = d_forceParams_;
-    kernelParams_.d_xq          = d_xq_;
-    kernelParams_.d_f           = d_f_;
-    kernelParams_.d_fShift      = d_fShift_;
-    kernelParams_.d_vTot        = d_vTot_;
+    allocateDeviceBuffer(&d_vTot_, F_NRE, deviceContext_);
+    clearDeviceBufferAsync(&d_vTot_, 0, F_NRE, deviceStream_);
+
+    kernelParams_.electrostaticsScaleFactor = electrostaticsScaleFactor;
+    kernelParams_.d_forceParams             = d_forceParams_;
+    kernelParams_.d_xq                      = d_xq_;
+    kernelParams_.d_f                       = d_f_;
+    kernelParams_.d_fShift                  = d_fShift_;
+    kernelParams_.d_vTot                    = d_vTot_;
     for (int i = 0; i < numFTypesOnGpu; i++)
     {
         kernelParams_.d_iatoms[i]        = nullptr;
         kernelParams_.fTypeRangeStart[i] = 0;
         kernelParams_.fTypeRangeEnd[i]   = -1;
     }
+
+    int fTypeRangeEnd = kernelParams_.fTypeRangeEnd[numFTypesOnGpu - 1];
+
+    kernelLaunchConfig_.blockSize[0] = c_threadsPerBlock;
+    kernelLaunchConfig_.blockSize[1] = 1;
+    kernelLaunchConfig_.blockSize[2] = 1;
+    kernelLaunchConfig_.gridSize[0]  = (fTypeRangeEnd + c_threadsPerBlock) / c_threadsPerBlock;
+    kernelLaunchConfig_.gridSize[1]  = 1;
+    kernelLaunchConfig_.gridSize[2]  = 1;
 }
 
 GpuBonded::Impl::~Impl()
@@ -113,21 +133,21 @@ GpuBonded::Impl::~Impl()
 }
 
 //! Return whether function type \p fType in \p idef has perturbed interactions
-static bool fTypeHasPerturbedEntries(const t_idef& idef, int fType)
+static bool fTypeHasPerturbedEntries(const InteractionDefinitions& idef, int fType)
 {
     GMX_ASSERT(idef.ilsort == ilsortNO_FE || idef.ilsort == ilsortFE_SORTED,
                "Perturbed interations should be sorted here");
 
-    const t_ilist& ilist = idef.il[fType];
+    const InteractionList& ilist = idef.il[fType];
 
-    return (idef.ilsort != ilsortNO_FE && ilist.nr_nonperturbed != ilist.nr);
+    return (idef.ilsort != ilsortNO_FE && idef.numNonperturbedInteractions[fType] != ilist.size());
 }
 
 //! Converts \p src with atom indices in state order to \p dest in nbnxn order
-static void convertIlistToNbnxnOrder(const t_ilist&       src,
-                                     HostInteractionList* dest,
-                                     int                  numAtomsPerInteraction,
-                                     ArrayRef<const int>  nbnxnAtomOrder)
+static void convertIlistToNbnxnOrder(const InteractionList& src,
+                                     HostInteractionList*   dest,
+                                     int                    numAtomsPerInteraction,
+                                     ArrayRef<const int>    nbnxnAtomOrder)
 {
     GMX_ASSERT(src.size() == 0 || !nbnxnAtomOrder.empty(), "We need the nbnxn atom order");
 
@@ -170,12 +190,14 @@ static inline int roundUpToFactor(const int input, const int factor)
  *  types are assigned in blocks sized as <warp_size>. The beginning and end (thread index) of each
  *  interaction type are stored in kernelParams_. Pointers to the relevant data structures on the
  *  GPU are also stored in kernelParams_.
+ *
+ * \todo Use DeviceBuffer for the d_xqPtr.
  */
 void GpuBonded::Impl::updateInteractionListsAndDeviceBuffers(ArrayRef<const int> nbnxnAtomOrder,
-                                                             const t_idef&       idef,
-                                                             void*               d_xqPtr,
-                                                             void*               d_fPtr,
-                                                             void*               d_fShiftPtr)
+                                                             const InteractionDefinitions& idef,
+                                                             void*                         d_xqPtr,
+                                                             DeviceBuffer<RVec>            d_fPtr,
+                                                             DeviceBuffer<RVec> d_fShiftPtr)
 {
     // TODO wallcycle sub start
     haveInteractions_ = false;
@@ -189,7 +211,7 @@ void GpuBonded::Impl::updateInteractionListsAndDeviceBuffers(ArrayRef<const int>
          * But instead of doing all interactions on the CPU, we can
          * still easily handle the types that have no perturbed
          * interactions on the GPU. */
-        if (idef.il[fType].nr > 0 && !fTypeHasPerturbedEntries(idef, fType))
+        if (!idef.il[fType].empty() && !fTypeHasPerturbedEntries(idef, fType))
         {
             haveInteractions_ = true;
 
@@ -208,9 +230,10 @@ void GpuBonded::Impl::updateInteractionListsAndDeviceBuffers(ArrayRef<const int>
         {
             t_ilist& d_iList = d_iLists_[fType];
 
-            reallocateDeviceBuffer(&d_iList.iatoms, iList.size(), &d_iList.nr, &d_iList.nalloc, nullptr);
+            reallocateDeviceBuffer(&d_iList.iatoms, iList.size(), &d_iList.nr, &d_iList.nalloc,
+                                   deviceContext_);
 
-            copyToDeviceBuffer(&d_iList.iatoms, iList.iatoms.data(), 0, iList.size(), stream_,
+            copyToDeviceBuffer(&d_iList.iatoms, iList.iatoms.data(), 0, iList.size(), deviceStream_,
                                GpuApiCallBehavior::Async, nullptr);
         }
         kernelParams_.fTypesOnGpu[fTypesCounter]    = fType;
@@ -242,9 +265,12 @@ void GpuBonded::Impl::updateInteractionListsAndDeviceBuffers(ArrayRef<const int>
         fTypesCounter++;
     }
 
+    int fTypeRangeEnd               = kernelParams_.fTypeRangeEnd[numFTypesOnGpu - 1];
+    kernelLaunchConfig_.gridSize[0] = (fTypeRangeEnd + c_threadsPerBlock) / c_threadsPerBlock;
+
     d_xq_     = static_cast<float4*>(d_xqPtr);
-    d_f_      = static_cast<fvec*>(d_fPtr);
-    d_fShift_ = static_cast<fvec*>(d_fShiftPtr);
+    d_f_      = asFloat3(d_fPtr);
+    d_fShift_ = asFloat3(d_fShiftPtr);
 
     kernelParams_.d_xq          = d_xq_;
     kernelParams_.d_f           = d_f_;
@@ -255,6 +281,13 @@ void GpuBonded::Impl::updateInteractionListsAndDeviceBuffers(ArrayRef<const int>
     // TODO wallcycle sub stop
 }
 
+void GpuBonded::Impl::setPbc(PbcType pbcType, const matrix box, bool canMoleculeSpanPbc)
+{
+    PbcAiuc pbcAiuc;
+    setPbcAiuc(canMoleculeSpanPbc ? numPbcDimensions(pbcType) : 0, box, &pbcAiuc);
+    kernelParams_.pbcAiuc = pbcAiuc;
+}
+
 bool GpuBonded::Impl::haveInteractions() const
 {
     return haveInteractions_;
@@ -265,11 +298,10 @@ void GpuBonded::Impl::launchEnergyTransfer()
     GMX_ASSERT(haveInteractions_,
                "No GPU bonded interactions, so no energies will be computed, so transfer should "
                "not be called");
-
     wallcycle_sub_start_nocount(wcycle_, ewcsLAUNCH_GPU_BONDED);
     // TODO add conditional on whether there has been any compute (and make sure host buffer doesn't contain garbage)
     float* h_vTot = vTot_.data();
-    copyFromDeviceBuffer(h_vTot, &d_vTot_, 0, F_NRE, stream_, GpuApiCallBehavior::Async, nullptr);
+    copyFromDeviceBuffer(h_vTot, &d_vTot_, 0, F_NRE, deviceStream_, GpuApiCallBehavior::Async, nullptr);
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_GPU_BONDED);
 }
 
@@ -280,7 +312,7 @@ void GpuBonded::Impl::waitAccumulateEnergyTerms(gmx_enerdata_t* enerd)
                "accumulation should not occur");
 
     wallcycle_start(wcycle_, ewcWAIT_GPU_BONDED);
-    cudaError_t stat = cudaStreamSynchronize(stream_);
+    cudaError_t stat = cudaStreamSynchronize(deviceStream_.stream());
     CU_RET_ERR(stat, "D2H transfer of bonded energies failed");
     wallcycle_stop(wcycle_, ewcWAIT_GPU_BONDED);
 
@@ -303,34 +335,52 @@ void GpuBonded::Impl::clearEnergies()
 {
     wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
     wallcycle_sub_start_nocount(wcycle_, ewcsLAUNCH_GPU_BONDED);
-    clearDeviceBufferAsync(&d_vTot_, 0, F_NRE, stream_);
+    clearDeviceBufferAsync(&d_vTot_, 0, F_NRE, deviceStream_);
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_GPU_BONDED);
     wallcycle_stop(wcycle_, ewcLAUNCH_GPU);
 }
 
 // ---- GpuBonded
 
-GpuBonded::GpuBonded(const gmx_ffparams_t& ffparams, void* streamPtr, gmx_wallcycle* wcycle) :
-    impl_(new Impl(ffparams, streamPtr, wcycle))
+GpuBonded::GpuBonded(const gmx_ffparams_t& ffparams,
+                     const float           electrostaticsScaleFactor,
+                     const DeviceContext&  deviceContext,
+                     const DeviceStream&   deviceStream,
+                     gmx_wallcycle*        wcycle) :
+    impl_(new Impl(ffparams, electrostaticsScaleFactor, deviceContext, deviceStream, wcycle))
 {
 }
 
 GpuBonded::~GpuBonded() = default;
 
-void GpuBonded::updateInteractionListsAndDeviceBuffers(ArrayRef<const int> nbnxnAtomOrder,
-                                                       const t_idef&       idef,
-                                                       void*               d_xq,
-                                                       void*               d_f,
-                                                       void*               d_fShift)
+void GpuBonded::updateInteractionListsAndDeviceBuffers(ArrayRef<const int>           nbnxnAtomOrder,
+                                                       const InteractionDefinitions& idef,
+                                                       void*                         d_xq,
+                                                       DeviceBuffer<RVec>            d_f,
+                                                       DeviceBuffer<RVec>            d_fShift)
 {
     impl_->updateInteractionListsAndDeviceBuffers(nbnxnAtomOrder, idef, d_xq, d_f, d_fShift);
 }
 
+void GpuBonded::setPbc(PbcType pbcType, const matrix box, bool canMoleculeSpanPbc)
+{
+    impl_->setPbc(pbcType, box, canMoleculeSpanPbc);
+}
+
 bool GpuBonded::haveInteractions() const
 {
     return impl_->haveInteractions();
 }
 
+void GpuBonded::setPbcAndlaunchKernel(PbcType                  pbcType,
+                                      const matrix             box,
+                                      bool                     canMoleculeSpanPbc,
+                                      const gmx::StepWorkload& stepWork)
+{
+    setPbc(pbcType, box, canMoleculeSpanPbc);
+    launchKernel(stepWork);
+}
+
 void GpuBonded::launchEnergyTransfer()
 {
     impl_->launchEnergyTransfer();
index 236d7df9821ae89f2a4efe486752ee714a535195..dacb612308972ada61fe965ec6c1c5190bb621d8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,7 +48,7 @@
 #ifndef GMX_LISTED_FORCES_GPUBONDED_IMPL_H
 #define GMX_LISTED_FORCES_GPUBONDED_IMPL_H
 
-#include "gromacs/gpu_utils/gpu_vec.cuh"
+#include "gromacs/gpu_utils/device_context.h"
 #include "gromacs/gpu_utils/gputraits.cuh"
 #include "gromacs/gpu_utils/hostallocator.h"
 #include "gromacs/listed_forces/gpubonded.h"
@@ -81,7 +81,7 @@ struct BondedCudaKernelParameters
     //! Periodic boundary data
     PbcAiuc pbcAiuc;
     //! Scale factor
-    float scaleFactor;
+    float electrostaticsScaleFactor;
     //! The bonded types on GPU
     int fTypesOnGpu[numFTypesOnGpu];
     //! The number of interaction atom (iatom) elements for every function type
@@ -98,9 +98,9 @@ struct BondedCudaKernelParameters
     //! Coordinates before the timestep (on GPU)
     const float4* d_xq;
     //! Forces on atoms (on GPU)
-    fvec* d_f;
+    float3* d_f;
     //! Force shifts on atoms (on GPU)
-    fvec* d_fShift;
+    float3* d_fShift;
     //! Total Energy (on GPU)
     float* d_vTot;
     //! Interaction list atoms (on GPU)
@@ -112,12 +112,12 @@ struct BondedCudaKernelParameters
 
         setPbcAiuc(0, boxDummy, &pbcAiuc);
 
-        scaleFactor   = 1.0;
-        d_forceParams = nullptr;
-        d_xq          = nullptr;
-        d_f           = nullptr;
-        d_fShift      = nullptr;
-        d_vTot        = nullptr;
+        electrostaticsScaleFactor = 1.0;
+        d_forceParams             = nullptr;
+        d_xq                      = nullptr;
+        d_f                       = nullptr;
+        d_fShift                  = nullptr;
+        d_vTot                    = nullptr;
     }
 };
 
@@ -126,7 +126,11 @@ class GpuBonded::Impl
 {
 public:
     //! Constructor
-    Impl(const gmx_ffparams_t& ffparams, void* streamPtr, gmx_wallcycle* wcycle);
+    Impl(const gmx_ffparams_t& ffparams,
+         const float           electrostaticsScaleFactor,
+         const DeviceContext&  deviceContext,
+         const DeviceStream&   deviceStream,
+         gmx_wallcycle*        wcycle);
     /*! \brief Destructor, non-default needed for freeing
      * device-side buffers */
     ~Impl();
@@ -137,15 +141,25 @@ public:
      * stage. Copies the bonded interactions assigned to the GPU
      * to device data structures, and updates device buffers that
      * may have been updated after search. */
-    void updateInteractionListsAndDeviceBuffers(ArrayRef<const int> nbnxnAtomOrder,
-                                                const t_idef&       idef,
-                                                void*               xqDevice,
-                                                void*               forceDevice,
-                                                void*               fshiftDevice);
+    void updateInteractionListsAndDeviceBuffers(ArrayRef<const int>           nbnxnAtomOrder,
+                                                const InteractionDefinitions& idef,
+                                                void*                         xqDevice,
+                                                DeviceBuffer<RVec>            forceDevice,
+                                                DeviceBuffer<RVec>            fshiftDevice);
+    /*! \brief
+     * Update PBC data.
+     *
+     * Converts PBC data from t_pbc into the PbcAiuc format and stores the latter.
+     *
+     * \param[in] pbcType The type of the periodic boundary.
+     * \param[in] box     The periodic boundary box matrix.
+     * \param[in] canMoleculeSpanPbc  Whether one molecule can have atoms in different PBC cells.
+     */
+    void setPbc(PbcType pbcType, const matrix box, bool canMoleculeSpanPbc);
 
     /*! \brief Launches bonded kernel on a GPU */
     template<bool calcVir, bool calcEner>
-    void launchKernel(const t_forcerec* fr, const matrix box);
+    void launchKernel();
     /*! \brief Returns whether there are bonded interactions
      * assigned to the GPU */
     bool haveInteractions() const;
@@ -166,26 +180,31 @@ private:
     //! Tells whether there are any interaction in iLists.
     bool haveInteractions_;
     //! Interaction lists on the device.
-    t_ilist d_iLists_[F_NRE];
+    t_ilist d_iLists_[F_NRE] = {};
     //! Bonded parameters for device-side use.
     t_iparams* d_forceParams_ = nullptr;
     //! Position-charge vector on the device.
     const float4* d_xq_ = nullptr;
     //! Force vector on the device.
-    fvec* d_f_ = nullptr;
+    float3* d_f_ = nullptr;
     //! Shift force vector on the device.
-    fvec* d_fShift_ = nullptr;
+    float3* d_fShift_ = nullptr;
     //! \brief Host-side virial buffer
     HostVector<float> vTot_ = { {}, gmx::HostAllocationPolicy(gmx::PinningPolicy::PinnedIfSupported) };
     //! \brief Device-side total virial
     float* d_vTot_ = nullptr;
 
+    //! GPU context object
+    const DeviceContext& deviceContext_;
     //! \brief Bonded GPU stream, not owned by this module
-    CommandStream stream_;
+    const DeviceStream& deviceStream_;
 
     //! Parameters and pointers, passed to the CUDA kernel
     BondedCudaKernelParameters kernelParams_;
 
+    //! GPU kernel launch configuration
+    KernelLaunchConfig kernelLaunchConfig_;
+
     //! \brief Pointer to wallcycle structure.
     gmx_wallcycle* wcycle_;
 };
index fbb6baee1f5e85caeeef6444da5eb22499f78e1b..d253241ee73b6ef0b54fe3147c3b19f973c88ece 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <math_constants.h>
 
 #include "gromacs/gpu_utils/cudautils.cuh"
-#include "gromacs/gpu_utils/gpu_vec.cuh"
+#include "gromacs/gpu_utils/typecasts.cuh"
+#include "gromacs/gpu_utils/vectype_ops.cuh"
 #include "gromacs/listed_forces/gpubonded.h"
 #include "gromacs/math/units.h"
 #include "gromacs/mdlib/force_flags.h"
-#include "gromacs/mdtypes/forcerec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/simulation_workload.h"
-#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/pbc_aiuc_cuda.cuh"
 #include "gromacs/utility/gmxassert.h"
 
@@ -69,9 +69,6 @@
 #    include <limits>
 #endif
 
-// CUDA threads per block
-#define TPB_BONDED 256
-
 /*-------------------------------- CUDA kernels-------------------------------- */
 /*------------------------------------------------------------------------------*/
 
@@ -100,8 +97,8 @@ __device__ void bonds_gpu(const int       i,
                           const t_iatom   d_forceatoms[],
                           const t_iparams d_forceparams[],
                           const float4    gm_xq[],
-                          fvec            gm_f[],
-                          fvec            sm_fShiftLoc[],
+                          float3          gm_f[],
+                          float3          sm_fShiftLoc[],
                           const PbcAiuc   pbcAiuc)
 {
     if (i < numBonds)
@@ -112,10 +109,10 @@ __device__ void bonds_gpu(const int       i,
         int  aj       = bondData.z;
 
         /* dx = xi - xj, corrected for periodic boundary conditions. */
-        fvec dx;
-        int  ki = pbcDxAiuc<calcVir>(pbcAiuc, gm_xq[ai], gm_xq[aj], dx);
+        float3 dx;
+        int    ki = pbcDxAiuc<calcVir>(pbcAiuc, gm_xq[ai], gm_xq[aj], dx);
 
-        float dr2 = iprod_gpu(dx, dx);
+        float dr2 = norm2(dx);
         float dr  = sqrt(dr2);
 
         float vbond;
@@ -131,17 +128,13 @@ __device__ void bonds_gpu(const int       i,
         {
             fbond *= rsqrtf(dr2);
 
-#pragma unroll
-            for (int m = 0; m < DIM; m++)
+            float3 fij = fbond * dx;
+            atomicAdd(&gm_f[ai], fij);
+            atomicAdd(&gm_f[aj], -fij);
+            if (calcVir && ki != CENTRAL)
             {
-                float fij = fbond * dx[m];
-                atomicAdd(&gm_f[ai][m], fij);
-                atomicAdd(&gm_f[aj][m], -fij);
-                if (calcVir && ki != CENTRAL)
-                {
-                    atomicAdd(&sm_fShiftLoc[ki][m], fij);
-                    atomicAdd(&sm_fShiftLoc[CENTRAL][m], -fij);
-                }
+                atomicAdd(&sm_fShiftLoc[ki], fij);
+                atomicAdd(&sm_fShiftLoc[CENTRAL], -fij);
             }
         }
     }
@@ -152,17 +145,17 @@ __device__ __forceinline__ static float bond_angle_gpu(const float4   xi,
                                                        const float4   xj,
                                                        const float4   xk,
                                                        const PbcAiuc& pbcAiuc,
-                                                       fvec           r_ij,
-                                                       fvec           r_kj,
+                                                       float3*        r_ij,
+                                                       float3*        r_kj,
                                                        float*         costh,
                                                        int*           t1,
                                                        int*           t2)
 /* Return value is the angle between the bonds i-j and j-k */
 {
-    *t1 = pbcDxAiuc<returnShift>(pbcAiuc, xi, xj, r_ij);
-    *t2 = pbcDxAiuc<returnShift>(pbcAiuc, xk, xj, r_kj);
+    *t1 = pbcDxAiuc<returnShift>(pbcAiuc, xi, xj, *r_ij);
+    *t2 = pbcDxAiuc<returnShift>(pbcAiuc, xk, xj, *r_kj);
 
-    *costh   = cos_angle_gpu(r_ij, r_kj);
+    *costh   = cos_angle(*r_ij, *r_kj);
     float th = acosf(*costh);
 
     return th;
@@ -175,8 +168,8 @@ __device__ void angles_gpu(const int       i,
                            const t_iatom   d_forceatoms[],
                            const t_iparams d_forceparams[],
                            const float4    gm_xq[],
-                           fvec            gm_f[],
-                           fvec            sm_fShiftLoc[],
+                           float3          gm_f[],
+                           float3          sm_fShiftLoc[],
                            const PbcAiuc   pbcAiuc)
 {
     if (i < numBonds)
@@ -187,13 +180,13 @@ __device__ void angles_gpu(const int       i,
         int  aj        = angleData.z;
         int  ak        = angleData.w;
 
-        fvec  r_ij;
-        fvec  r_kj;
-        float cos_theta;
-        int   t1;
-        int   t2;
-        float theta = bond_angle_gpu<calcVir>(gm_xq[ai], gm_xq[aj], gm_xq[ak], pbcAiuc, r_ij, r_kj,
-                                              &cos_theta, &t1, &t2);
+        float3 r_ij;
+        float3 r_kj;
+        float  cos_theta;
+        int    t1;
+        int    t2;
+        float  theta = bond_angle_gpu<calcVir>(gm_xq[ai], gm_xq[aj], gm_xq[ak], pbcAiuc, &r_ij,
+                                              &r_kj, &cos_theta, &t1, &t2);
 
         float va;
         float dVdt;
@@ -210,8 +203,8 @@ __device__ void angles_gpu(const int       i,
         {
             float st    = dVdt * rsqrtf(1.0f - cos_theta2);
             float sth   = st * cos_theta;
-            float nrij2 = iprod_gpu(r_ij, r_ij);
-            float nrkj2 = iprod_gpu(r_kj, r_kj);
+            float nrij2 = norm2(r_ij);
+            float nrkj2 = norm2(r_kj);
 
             float nrij_1 = rsqrtf(nrij2);
             float nrkj_1 = rsqrtf(nrkj2);
@@ -220,24 +213,19 @@ __device__ void angles_gpu(const int       i,
             float cii = sth * nrij_1 * nrij_1;
             float ckk = sth * nrkj_1 * nrkj_1;
 
-            fvec f_i;
-            fvec f_k;
-            fvec f_j;
-#pragma unroll
-            for (int m = 0; m < DIM; m++)
+            float3 f_i = cii * r_ij - cik * r_kj;
+            float3 f_k = ckk * r_kj - cik * r_ij;
+            float3 f_j = -f_i - f_k;
+
+            atomicAdd(&gm_f[ai], f_i);
+            atomicAdd(&gm_f[aj], f_j);
+            atomicAdd(&gm_f[ak], f_k);
+
+            if (calcVir)
             {
-                f_i[m] = -(cik * r_kj[m] - cii * r_ij[m]);
-                f_k[m] = -(cik * r_ij[m] - ckk * r_kj[m]);
-                f_j[m] = -f_i[m] - f_k[m];
-                atomicAdd(&gm_f[ai][m], f_i[m]);
-                atomicAdd(&gm_f[aj][m], f_j[m]);
-                atomicAdd(&gm_f[ak][m], f_k[m]);
-                if (calcVir)
-                {
-                    atomicAdd(&sm_fShiftLoc[t1][m], f_i[m]);
-                    atomicAdd(&sm_fShiftLoc[CENTRAL][m], f_j[m]);
-                    atomicAdd(&sm_fShiftLoc[t2][m], f_k[m]);
-                }
+                atomicAdd(&sm_fShiftLoc[t1], f_i);
+                atomicAdd(&sm_fShiftLoc[CENTRAL], f_j);
+                atomicAdd(&sm_fShiftLoc[t2], f_k);
             }
         }
     }
@@ -250,8 +238,8 @@ __device__ void urey_bradley_gpu(const int       i,
                                  const t_iatom   d_forceatoms[],
                                  const t_iparams d_forceparams[],
                                  const float4    gm_xq[],
-                                 fvec            gm_f[],
-                                 fvec            sm_fShiftLoc[],
+                                 float3          gm_f[],
+                                 float3          sm_fShiftLoc[],
                                  const PbcAiuc   pbcAiuc)
 {
     if (i < numBonds)
@@ -267,13 +255,13 @@ __device__ void urey_bradley_gpu(const int       i,
         float r13A = d_forceparams[type].u_b.r13A;
         float kUBA = d_forceparams[type].u_b.kUBA;
 
-        fvec  r_ij;
-        fvec  r_kj;
-        float cos_theta;
-        int   t1;
-        int   t2;
-        float theta = bond_angle_gpu<calcVir>(gm_xq[ai], gm_xq[aj], gm_xq[ak], pbcAiuc, r_ij, r_kj,
-                                              &cos_theta, &t1, &t2);
+        float3 r_ij;
+        float3 r_kj;
+        float  cos_theta;
+        int    t1;
+        int    t2;
+        float  theta = bond_angle_gpu<calcVir>(gm_xq[ai], gm_xq[aj], gm_xq[ak], pbcAiuc, &r_ij,
+                                              &r_kj, &cos_theta, &t1, &t2);
 
         float va;
         float dVdt;
@@ -284,10 +272,10 @@ __device__ void urey_bradley_gpu(const int       i,
             *vtot_loc += va;
         }
 
-        fvec r_ik;
-        int  ki = pbcDxAiuc<calcVir>(pbcAiuc, gm_xq[ai], gm_xq[ak], r_ik);
+        float3 r_ik;
+        int    ki = pbcDxAiuc<calcVir>(pbcAiuc, gm_xq[ai], gm_xq[ak], r_ik);
 
-        float dr2 = iprod_gpu(r_ik, r_ik);
+        float dr2 = norm2(r_ik);
         float dr  = dr2 * rsqrtf(dr2);
 
         float vbond;
@@ -300,31 +288,26 @@ __device__ void urey_bradley_gpu(const int       i,
             float st  = dVdt * rsqrtf(1.0f - cos_theta2);
             float sth = st * cos_theta;
 
-            float nrkj2 = iprod_gpu(r_kj, r_kj);
-            float nrij2 = iprod_gpu(r_ij, r_ij);
+            float nrkj2 = norm2(r_kj);
+            float nrij2 = norm2(r_ij);
 
             float cik = st * rsqrtf(nrkj2 * nrij2);
             float cii = sth / nrij2;
             float ckk = sth / nrkj2;
 
-            fvec f_i;
-            fvec f_j;
-            fvec f_k;
-#pragma unroll
-            for (int m = 0; m < DIM; m++)
+            float3 f_i = cii * r_ij - cik * r_kj;
+            float3 f_k = ckk * r_kj - cik * r_ij;
+            float3 f_j = -f_i - f_k;
+
+            atomicAdd(&gm_f[ai], f_i);
+            atomicAdd(&gm_f[aj], f_j);
+            atomicAdd(&gm_f[ak], f_k);
+
+            if (calcVir)
             {
-                f_i[m] = -(cik * r_kj[m] - cii * r_ij[m]);
-                f_k[m] = -(cik * r_ij[m] - ckk * r_kj[m]);
-                f_j[m] = -f_i[m] - f_k[m];
-                atomicAdd(&gm_f[ai][m], f_i[m]);
-                atomicAdd(&gm_f[aj][m], f_j[m]);
-                atomicAdd(&gm_f[ak][m], f_k[m]);
-                if (calcVir)
-                {
-                    atomicAdd(&sm_fShiftLoc[t1][m], f_i[m]);
-                    atomicAdd(&sm_fShiftLoc[CENTRAL][m], f_j[m]);
-                    atomicAdd(&sm_fShiftLoc[t2][m], f_k[m]);
-                }
+                atomicAdd(&sm_fShiftLoc[t1], f_i);
+                atomicAdd(&sm_fShiftLoc[CENTRAL], f_j);
+                atomicAdd(&sm_fShiftLoc[t2], f_k);
             }
         }
 
@@ -338,18 +321,14 @@ __device__ void urey_bradley_gpu(const int       i,
 
             fbond *= rsqrtf(dr2);
 
-#pragma unroll
-            for (int m = 0; m < DIM; m++)
+            float3 fik = fbond * r_ik;
+            atomicAdd(&gm_f[ai], fik);
+            atomicAdd(&gm_f[ak], -fik);
+
+            if (calcVir && ki != CENTRAL)
             {
-                float fik = fbond * r_ik[m];
-                atomicAdd(&gm_f[ai][m], fik);
-                atomicAdd(&gm_f[ak][m], -fik);
-
-                if (calcVir && ki != CENTRAL)
-                {
-                    atomicAdd(&sm_fShiftLoc[ki][m], fik);
-                    atomicAdd(&sm_fShiftLoc[CENTRAL][m], -fik);
-                }
+                atomicAdd(&sm_fShiftLoc[ki], fik);
+                atomicAdd(&sm_fShiftLoc[CENTRAL], -fik);
             }
         }
     }
@@ -361,23 +340,23 @@ __device__ __forceinline__ static float dih_angle_gpu(const T        xi,
                                                       const T        xk,
                                                       const T        xl,
                                                       const PbcAiuc& pbcAiuc,
-                                                      fvec           r_ij,
-                                                      fvec           r_kj,
-                                                      fvec           r_kl,
-                                                      fvec           m,
-                                                      fvec           n,
+                                                      float3*        r_ij,
+                                                      float3*        r_kj,
+                                                      float3*        r_kl,
+                                                      float3*        m,
+                                                      float3*        n,
                                                       int*           t1,
                                                       int*           t2,
                                                       int*           t3)
 {
-    *t1 = pbcDxAiuc<returnShift>(pbcAiuc, xi, xj, r_ij);
-    *t2 = pbcDxAiuc<returnShift>(pbcAiuc, xk, xj, r_kj);
-    *t3 = pbcDxAiuc<returnShift>(pbcAiuc, xk, xl, r_kl);
-
-    cprod_gpu(r_ij, r_kj, m);
-    cprod_gpu(r_kj, r_kl, n);
-    float phi  = gmx_angle_gpu(m, n);
-    float ipr  = iprod_gpu(r_ij, n);
+    *t1 = pbcDxAiuc<returnShift>(pbcAiuc, xi, xj, *r_ij);
+    *t2 = pbcDxAiuc<returnShift>(pbcAiuc, xk, xj, *r_kj);
+    *t3 = pbcDxAiuc<returnShift>(pbcAiuc, xk, xl, *r_kl);
+
+    *m         = cprod(*r_ij, *r_kj);
+    *n         = cprod(*r_kj, *r_kl);
+    float phi  = gmx_angle(*m, *n);
+    float ipr  = iprod(*r_ij, *n);
     float sign = (ipr < 0.0f) ? -1.0f : 1.0f;
     phi        = sign * phi;
 
@@ -402,70 +381,56 @@ __device__ static void do_dih_fup_gpu(const int      i,
                                       const int      k,
                                       const int      l,
                                       const float    ddphi,
-                                      const fvec     r_ij,
-                                      const fvec     r_kj,
-                                      const fvec     r_kl,
-                                      const fvec     m,
-                                      const fvec     n,
-                                      fvec           gm_f[],
-                                      fvec           sm_fShiftLoc[],
+                                      const float3   r_ij,
+                                      const float3   r_kj,
+                                      const float3   r_kl,
+                                      const float3   m,
+                                      const float3   n,
+                                      float3         gm_f[],
+                                      float3         sm_fShiftLoc[],
                                       const PbcAiuc& pbcAiuc,
                                       const float4   gm_xq[],
                                       const int      t1,
                                       const int      t2,
                                       const int gmx_unused t3)
 {
-    float iprm  = iprod_gpu(m, m);
-    float iprn  = iprod_gpu(n, n);
-    float nrkj2 = iprod_gpu(r_kj, r_kj);
+    float iprm  = norm2(m);
+    float iprn  = norm2(n);
+    float nrkj2 = norm2(r_kj);
     float toler = nrkj2 * GMX_REAL_EPS;
     if ((iprm > toler) && (iprn > toler))
     {
-        float nrkj_1 = rsqrtf(nrkj2); // replacing std::invsqrt call
-        float nrkj_2 = nrkj_1 * nrkj_1;
-        float nrkj   = nrkj2 * nrkj_1;
-        float a      = -ddphi * nrkj / iprm;
-        fvec  f_i;
-        svmul_gpu(a, m, f_i);
-        float b = ddphi * nrkj / iprn;
-        fvec  f_l;
-        svmul_gpu(b, n, f_l);
-        float p = iprod_gpu(r_ij, r_kj);
+        float  nrkj_1 = rsqrtf(nrkj2); // replacing std::invsqrt call
+        float  nrkj_2 = nrkj_1 * nrkj_1;
+        float  nrkj   = nrkj2 * nrkj_1;
+        float  a      = -ddphi * nrkj / iprm;
+        float3 f_i    = a * m;
+        float  b      = ddphi * nrkj / iprn;
+        float3 f_l    = b * n;
+        float  p      = iprod(r_ij, r_kj);
         p *= nrkj_2;
-        float q = iprod_gpu(r_kl, r_kj);
+        float q = iprod(r_kl, r_kj);
         q *= nrkj_2;
-        fvec uvec;
-        svmul_gpu(p, f_i, uvec);
-        fvec vvec;
-        svmul_gpu(q, f_l, vvec);
-        fvec svec;
-        fvec_sub_gpu(uvec, vvec, svec);
-        fvec f_j;
-        fvec_sub_gpu(f_i, svec, f_j);
-        fvec f_k;
-        fvec_add_gpu(f_l, svec, f_k);
-#pragma unroll
-        for (int m = 0; (m < DIM); m++)
-        {
-            atomicAdd(&gm_f[i][m], f_i[m]);
-            atomicAdd(&gm_f[j][m], -f_j[m]);
-            atomicAdd(&gm_f[k][m], -f_k[m]);
-            atomicAdd(&gm_f[l][m], f_l[m]);
-        }
+        float3 uvec = p * f_i;
+        float3 vvec = q * f_l;
+        float3 svec = uvec - vvec;
+        float3 f_j  = f_i - svec;
+        float3 f_k  = f_l + svec;
+
+        atomicAdd(&gm_f[i], f_i);
+        atomicAdd(&gm_f[j], -f_j);
+        atomicAdd(&gm_f[k], -f_k);
+        atomicAdd(&gm_f[l], f_l);
 
         if (calcVir)
         {
-            fvec dx_jl;
-            int  t3 = pbcDxAiuc<calcVir>(pbcAiuc, gm_xq[l], gm_xq[j], dx_jl);
+            float3 dx_jl;
+            int    t3 = pbcDxAiuc<calcVir>(pbcAiuc, gm_xq[l], gm_xq[j], dx_jl);
 
-#pragma unroll
-            for (int m = 0; (m < DIM); m++)
-            {
-                atomicAdd(&sm_fShiftLoc[t1][m], f_i[m]);
-                atomicAdd(&sm_fShiftLoc[CENTRAL][m], -f_j[m]);
-                atomicAdd(&sm_fShiftLoc[t2][m], -f_k[m]);
-                atomicAdd(&sm_fShiftLoc[t3][m], f_l[m]);
-            }
+            atomicAdd(&sm_fShiftLoc[t1], f_i);
+            atomicAdd(&sm_fShiftLoc[CENTRAL], -f_j);
+            atomicAdd(&sm_fShiftLoc[t2], -f_k);
+            atomicAdd(&sm_fShiftLoc[t3], f_l);
         }
     }
 }
@@ -477,8 +442,8 @@ __device__ void pdihs_gpu(const int       i,
                           const t_iatom   d_forceatoms[],
                           const t_iparams d_forceparams[],
                           const float4    gm_xq[],
-                          fvec            gm_f[],
-                          fvec            sm_fShiftLoc[],
+                          float3          gm_f[],
+                          float3          sm_fShiftLoc[],
                           const PbcAiuc   pbcAiuc)
 {
     if (i < numBonds)
@@ -489,16 +454,16 @@ __device__ void pdihs_gpu(const int       i,
         int ak   = d_forceatoms[5 * i + 3];
         int al   = d_forceatoms[5 * i + 4];
 
-        fvec  r_ij;
-        fvec  r_kj;
-        fvec  r_kl;
-        fvec  m;
-        fvec  n;
-        int   t1;
-        int   t2;
-        int   t3;
-        float phi = dih_angle_gpu<calcVir>(gm_xq[ai], gm_xq[aj], gm_xq[ak], gm_xq[al], pbcAiuc,
-                                           r_ij, r_kj, r_kl, m, n, &t1, &t2, &t3);
+        float3 r_ij;
+        float3 r_kj;
+        float3 r_kl;
+        float3 m;
+        float3 n;
+        int    t1;
+        int    t2;
+        int    t3;
+        float  phi = dih_angle_gpu<calcVir>(gm_xq[ai], gm_xq[aj], gm_xq[ak], gm_xq[al], pbcAiuc,
+                                           &r_ij, &r_kj, &r_kl, &m, &n, &t1, &t2, &t3);
 
         float vpd;
         float ddphi;
@@ -522,8 +487,8 @@ __device__ void rbdihs_gpu(const int       i,
                            const t_iatom   d_forceatoms[],
                            const t_iparams d_forceparams[],
                            const float4    gm_xq[],
-                           fvec            gm_f[],
-                           fvec            sm_fShiftLoc[],
+                           float3          gm_f[],
+                           float3          sm_fShiftLoc[],
                            const PbcAiuc   pbcAiuc)
 {
     constexpr float c0 = 0.0f, c1 = 1.0f, c2 = 2.0f, c3 = 3.0f, c4 = 4.0f, c5 = 5.0f;
@@ -536,16 +501,16 @@ __device__ void rbdihs_gpu(const int       i,
         int ak   = d_forceatoms[5 * i + 3];
         int al   = d_forceatoms[5 * i + 4];
 
-        fvec  r_ij;
-        fvec  r_kj;
-        fvec  r_kl;
-        fvec  m;
-        fvec  n;
-        int   t1;
-        int   t2;
-        int   t3;
-        float phi = dih_angle_gpu<calcVir>(gm_xq[ai], gm_xq[aj], gm_xq[ak], gm_xq[al], pbcAiuc,
-                                           r_ij, r_kj, r_kl, m, n, &t1, &t2, &t3);
+        float3 r_ij;
+        float3 r_kj;
+        float3 r_kl;
+        float3 m;
+        float3 n;
+        int    t1;
+        int    t2;
+        int    t3;
+        float  phi = dih_angle_gpu<calcVir>(gm_xq[ai], gm_xq[aj], gm_xq[ak], gm_xq[al], pbcAiuc,
+                                           &r_ij, &r_kj, &r_kl, &m, &n, &t1, &t2, &t3);
 
         /* Change to polymer convention */
         if (phi < c0)
@@ -639,8 +604,8 @@ __device__ void idihs_gpu(const int       i,
                           const t_iatom   d_forceatoms[],
                           const t_iparams d_forceparams[],
                           const float4    gm_xq[],
-                          fvec            gm_f[],
-                          fvec            sm_fShiftLoc[],
+                          float3          gm_f[],
+                          float3          sm_fShiftLoc[],
                           const PbcAiuc   pbcAiuc)
 {
     if (i < numBonds)
@@ -651,16 +616,16 @@ __device__ void idihs_gpu(const int       i,
         int ak   = d_forceatoms[5 * i + 3];
         int al   = d_forceatoms[5 * i + 4];
 
-        fvec  r_ij;
-        fvec  r_kj;
-        fvec  r_kl;
-        fvec  m;
-        fvec  n;
-        int   t1;
-        int   t2;
-        int   t3;
-        float phi = dih_angle_gpu<calcVir>(gm_xq[ai], gm_xq[aj], gm_xq[ak], gm_xq[al], pbcAiuc,
-                                           r_ij, r_kj, r_kl, m, n, &t1, &t2, &t3);
+        float3 r_ij;
+        float3 r_kj;
+        float3 r_kl;
+        float3 m;
+        float3 n;
+        int    t1;
+        int    t2;
+        int    t3;
+        float  phi = dih_angle_gpu<calcVir>(gm_xq[ai], gm_xq[aj], gm_xq[ak], gm_xq[al], pbcAiuc,
+                                           &r_ij, &r_kj, &r_kl, &m, &n, &t1, &t2, &t3);
 
         /* phi can jump if phi0 is close to Pi/-Pi, which will cause huge
          * force changes if we just apply a normal harmonic.
@@ -696,8 +661,8 @@ __device__ void pairs_gpu(const int       i,
                           const t_iatom   d_forceatoms[],
                           const t_iparams iparams[],
                           const float4    gm_xq[],
-                          fvec            gm_f[],
-                          fvec            sm_fShiftLoc[],
+                          float3          gm_f[],
+                          float3          sm_fShiftLoc[],
                           const PbcAiuc   pbcAiuc,
                           const float     scale_factor,
                           float*          vtotVdw_loc,
@@ -705,6 +670,7 @@ __device__ void pairs_gpu(const int       i,
 {
     if (i < numBonds)
     {
+        // TODO this should be made into a separate type, the GPU and CPU sizes should be compared
         int3 pairData = *(int3*)(d_forceatoms + 3 * i);
         int  type     = pairData.x;
         int  ai       = pairData.y;
@@ -715,10 +681,10 @@ __device__ void pairs_gpu(const int       i,
         float c12 = iparams[type].lj14.c12A;
 
         /* Do we need to apply full periodic boundary conditions? */
-        fvec dr;
-        int  fshift_index = pbcDxAiuc<calcVir>(pbcAiuc, gm_xq[ai], gm_xq[aj], dr);
+        float3 dr;
+        int    fshift_index = pbcDxAiuc<calcVir>(pbcAiuc, gm_xq[ai], gm_xq[aj], dr);
 
-        float r2    = norm2_gpu(dr);
+        float r2    = norm2(dr);
         float rinv  = rsqrtf(r2);
         float rinv2 = rinv * rinv;
         float rinv6 = rinv2 * rinv2 * rinv2;
@@ -729,21 +695,16 @@ __device__ void pairs_gpu(const int       i,
         /* Calculate the LJ force * r and add it to the Coulomb part */
         float fr = (12.0f * c12 * rinv6 - 6.0f * c6) * rinv6 + velec;
 
-        float finvr = fr * rinv2;
-        fvec  f;
-        svmul_gpu(finvr, dr, f);
+        float  finvr = fr * rinv2;
+        float3 f     = finvr * dr;
 
         /* Add the forces */
-#pragma unroll
-        for (int m = 0; m < DIM; m++)
+        atomicAdd(&gm_f[ai], f);
+        atomicAdd(&gm_f[aj], -f);
+        if (calcVir && fshift_index != CENTRAL)
         {
-            atomicAdd(&gm_f[ai][m], f[m]);
-            atomicAdd(&gm_f[aj][m], -f[m]);
-            if (calcVir && fshift_index != CENTRAL)
-            {
-                atomicAdd(&sm_fShiftLoc[fshift_index][m], f[m]);
-                atomicAdd(&sm_fShiftLoc[CENTRAL][m], -f[m]);
-            }
+            atomicAdd(&sm_fShiftLoc[fshift_index], f);
+            atomicAdd(&sm_fShiftLoc[CENTRAL], -f);
         }
 
         if (calcEner)
@@ -765,15 +726,13 @@ __global__ void exec_kernel_gpu(BondedCudaKernelParameters kernelParams)
     float      vtot_loc     = 0;
     float      vtotVdw_loc  = 0;
     float      vtotElec_loc = 0;
-    __shared__ fvec sm_fShiftLoc[SHIFTS];
+    __shared__ float3 sm_fShiftLoc[SHIFTS];
 
     if (calcVir)
     {
         if (threadIdx.x < SHIFTS)
         {
-            sm_fShiftLoc[threadIdx.x][XX] = 0.0f;
-            sm_fShiftLoc[threadIdx.x][YY] = 0.0f;
-            sm_fShiftLoc[threadIdx.x][ZZ] = 0.0f;
+            sm_fShiftLoc[threadIdx.x] = make_float3(0.0f, 0.0f, 0.0f);
         }
         __syncthreads();
     }
@@ -828,10 +787,10 @@ __global__ void exec_kernel_gpu(BondedCudaKernelParameters kernelParams)
                                                  kernelParams.d_f, sm_fShiftLoc, kernelParams.pbcAiuc);
                     break;
                 case F_LJ14:
-                    pairs_gpu<calcVir, calcEner>(fTypeTid, numBonds, iatoms, kernelParams.d_forceParams,
-                                                 kernelParams.d_xq, kernelParams.d_f, sm_fShiftLoc,
-                                                 kernelParams.pbcAiuc, kernelParams.scaleFactor,
-                                                 &vtotVdw_loc, &vtotElec_loc);
+                    pairs_gpu<calcVir, calcEner>(
+                            fTypeTid, numBonds, iatoms, kernelParams.d_forceParams,
+                            kernelParams.d_xq, kernelParams.d_f, sm_fShiftLoc, kernelParams.pbcAiuc,
+                            kernelParams.electrostaticsScaleFactor, &vtotVdw_loc, &vtotElec_loc);
                     break;
             }
             break;
@@ -852,7 +811,7 @@ __global__ void exec_kernel_gpu(BondedCudaKernelParameters kernelParams)
         __syncthreads();
         if (threadIdx.x < SHIFTS)
         {
-            fvec_inc_atomic(kernelParams.d_fShift[threadIdx.x], sm_fShiftLoc[threadIdx.x]);
+            atomicAdd(kernelParams.d_fShift[threadIdx.x], sm_fShiftLoc[threadIdx.x]);
         }
     }
 }
@@ -862,15 +821,10 @@ __global__ void exec_kernel_gpu(BondedCudaKernelParameters kernelParams)
 
 
 template<bool calcVir, bool calcEner>
-void GpuBonded::Impl::launchKernel(const t_forcerec* fr, const matrix box)
+void GpuBonded::Impl::launchKernel()
 {
     GMX_ASSERT(haveInteractions_,
                "Cannot launch bonded GPU kernels unless bonded GPU work was scheduled");
-    static_assert(TPB_BONDED >= SHIFTS,
-                  "TPB_BONDED must be >= SHIFTS for the virial kernel (calcVir=true)");
-
-    PbcAiuc pbcAiuc;
-    setPbcAiuc(fr->bMolPBC ? ePBC2npbcdim(fr->ePBC) : 0, box, &pbcAiuc);
 
     int fTypeRangeEnd = kernelParams_.fTypeRangeEnd[numFTypesOnGpu - 1];
 
@@ -879,38 +833,28 @@ void GpuBonded::Impl::launchKernel(const t_forcerec* fr, const matrix box)
         return;
     }
 
-    KernelLaunchConfig config;
-    config.blockSize[0] = TPB_BONDED;
-    config.blockSize[1] = 1;
-    config.blockSize[2] = 1;
-    config.gridSize[0]  = (fTypeRangeEnd + TPB_BONDED) / TPB_BONDED;
-    config.gridSize[1]  = 1;
-    config.gridSize[2]  = 1;
-    config.stream       = stream_;
-
-    auto kernelPtr            = exec_kernel_gpu<calcVir, calcEner>;
-    kernelParams_.scaleFactor = fr->ic->epsfac * fr->fudgeQQ;
-    kernelParams_.pbcAiuc     = pbcAiuc;
+    auto kernelPtr = exec_kernel_gpu<calcVir, calcEner>;
 
-    const auto kernelArgs = prepareGpuKernelArguments(kernelPtr, config, &kernelParams_);
+    const auto kernelArgs = prepareGpuKernelArguments(kernelPtr, kernelLaunchConfig_, &kernelParams_);
 
-    launchGpuKernel(kernelPtr, config, nullptr, "exec_kernel_gpu<calcVir, calcEner>", kernelArgs);
+    launchGpuKernel(kernelPtr, kernelLaunchConfig_, deviceStream_, nullptr,
+                    "exec_kernel_gpu<calcVir, calcEner>", kernelArgs);
 }
 
-void GpuBonded::launchKernel(const t_forcerec* fr, const gmx::StepWorkload& stepWork, const matrix box)
+void GpuBonded::launchKernel(const gmx::StepWorkload& stepWork)
 {
     if (stepWork.computeEnergy)
     {
         // When we need the energy, we also need the virial
-        impl_->launchKernel<true, true>(fr, box);
+        impl_->launchKernel<true, true>();
     }
     else if (stepWork.computeVirial)
     {
-        impl_->launchKernel<true, false>(fr, box);
+        impl_->launchKernel<true, false>();
     }
     else
     {
-        impl_->launchKernel<false, false>(fr, box);
+        impl_->launchKernel<false, false>();
     }
 }
 
index 4b46781c2e154bb0b43c5f8b6d6773ba4a4966c6..d68b21b93f76c719d9401045c389742b93fe03da 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,6 +50,7 @@
 
 #include <algorithm>
 #include <array>
+#include <numeric>
 
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/mdlib/enerdata_utils.h"
 #include "gromacs/mdlib/force.h"
+#include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/fcdata.h"
+#include "gromacs/mdtypes/forceoutput.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
-#include "gromacs/utility/smalloc.h"
 
 #include "listed_internal.h"
+#include "manage_threading.h"
 #include "utilities.h"
 
+ListedForces::ListedForces(const gmx_ffparams_t&      ffparams,
+                           const int                  numEnergyGroups,
+                           const int                  numThreads,
+                           const InteractionSelection interactionSelection,
+                           FILE*                      fplog) :
+    idefSelection_(ffparams),
+    threading_(std::make_unique<bonded_threading_t>(numThreads, numEnergyGroups, fplog)),
+    interactionSelection_(interactionSelection)
+{
+}
+
+ListedForces::ListedForces(ListedForces&& o) noexcept = default;
+
+ListedForces::~ListedForces() = default;
+
+//! Copies the selection interactions from \p idefSrc to \p idef
+static void selectInteractions(InteractionDefinitions*                  idef,
+                               const InteractionDefinitions&            idefSrc,
+                               const ListedForces::InteractionSelection interactionSelection)
+{
+    const bool selectPairs =
+            interactionSelection.test(static_cast<int>(ListedForces::InteractionGroup::Pairs));
+    const bool selectDihedrals =
+            interactionSelection.test(static_cast<int>(ListedForces::InteractionGroup::Dihedrals));
+    const bool selectAngles =
+            interactionSelection.test(static_cast<int>(ListedForces::InteractionGroup::Angles));
+    const bool selectRest =
+            interactionSelection.test(static_cast<int>(ListedForces::InteractionGroup::Rest));
+
+    for (int ftype = 0; ftype < F_NRE; ftype++)
+    {
+        const t_interaction_function& ifunc = interaction_function[ftype];
+        if (ifunc.flags & IF_BOND)
+        {
+            bool assign = false;
+            if (ifunc.flags & IF_PAIR)
+            {
+                assign = selectPairs;
+            }
+            else if (ifunc.flags & IF_DIHEDRAL)
+            {
+                assign = selectDihedrals;
+            }
+            else if (ifunc.flags & IF_ATYPE)
+            {
+                assign = selectAngles;
+            }
+            else
+            {
+                assign = selectRest;
+            }
+            if (assign)
+            {
+                idef->il[ftype] = idefSrc.il[ftype];
+            }
+            else
+            {
+                idef->il[ftype].clear();
+            }
+        }
+    }
+}
+
+void ListedForces::setup(const InteractionDefinitions& domainIdef, const int numAtomsForce, const bool useGpu)
+{
+    if (interactionSelection_.all())
+    {
+        // Avoid the overhead of copying all interaction lists by simply setting the reference to the domain idef
+        idef_ = &domainIdef;
+    }
+    else
+    {
+        idef_ = &idefSelection_;
+
+        selectInteractions(&idefSelection_, domainIdef, interactionSelection_);
+
+        idefSelection_.ilsort = domainIdef.ilsort;
+    }
+
+    setup_bonded_threading(threading_.get(), numAtomsForce, useGpu, *idef_);
+
+    if (idef_->ilsort == ilsortFE_SORTED)
+    {
+        forceBufferLambda_.resize(numAtomsForce * sizeof(rvec4) / sizeof(real));
+        shiftForceBufferLambda_.resize(SHIFTS);
+    }
+}
+
 namespace
 {
 
+using gmx::ArrayRef;
+
 /*! \brief Return true if ftype is an explicit pair-listed LJ or
  * COULOMB interaction type: bonded LJ (usually 1-4), or special
  * listed non-bonded for FEP. */
@@ -133,7 +227,7 @@ void zero_thread_output(f_thread_t* f_t)
 #define MAX_BONDED_THREADS 256
 
 /*! \brief Reduce thread-local force buffers */
-void reduce_thread_forces(int n, gmx::ArrayRef<gmx::RVec> force, const bonded_threading_t* bt, int nthreads)
+void reduce_thread_forces(gmx::ArrayRef<gmx::RVec> force, const bonded_threading_t* bt, int nthreads)
 {
     if (nthreads > MAX_BONDED_THREADS)
     {
@@ -142,6 +236,8 @@ void reduce_thread_forces(int n, gmx::ArrayRef<gmx::RVec> force, const bonded_th
 
     rvec* gmx_restrict f = as_rvec_array(force.data());
 
+    const int numAtomsForce = bt->numAtomsForce;
+
     /* This reduction can run on any number of threads,
      * independently of bt->nthreads.
      * But if nthreads matches bt->nthreads (which it currently does)
@@ -173,7 +269,7 @@ void reduce_thread_forces(int n, gmx::ArrayRef<gmx::RVec> force, const bonded_th
                 int a0 = ind * reduction_block_size;
                 int a1 = (ind + 1) * reduction_block_size;
                 /* It would be nice if we could pad f to avoid this min */
-                a1 = std::min(a1, n);
+                a1 = std::min(a1, numAtomsForce);
                 for (int a = a0; a < a1; a++)
                 {
                     for (int fb = 0; fb < nfb; fb++)
@@ -188,8 +284,7 @@ void reduce_thread_forces(int n, gmx::ArrayRef<gmx::RVec> force, const bonded_th
 }
 
 /*! \brief Reduce thread-local forces, shift forces and energies */
-void reduce_thread_output(int                        n,
-                          gmx::ForceWithShiftForces* forceWithShiftForces,
+void reduce_thread_output(gmx::ForceWithShiftForces* forceWithShiftForces,
                           real*                      ener,
                           gmx_grppairener_t*         grpp,
                           real*                      dvdl,
@@ -201,7 +296,7 @@ void reduce_thread_output(int                        n,
     if (bt->nblock_used > 0)
     {
         /* Reduce the bonded force buffer */
-        reduce_thread_forces(n, forceWithShiftForces->force(), bt, bt->nthreads);
+        reduce_thread_forces(forceWithShiftForces->force(), bt, bt->nthreads);
     }
 
     rvec* gmx_restrict fshift = as_rvec_array(forceWithShiftForces->shiftForces().data());
@@ -295,30 +390,31 @@ BondedKernelFlavor selectBondedKernelFlavor(const gmx::StepWorkload& stepWork,
 
 /*! \brief Calculate one element of the list of bonded interactions
     for this thread */
-real calc_one_bond(int                      thread,
-                   int                      ftype,
-                   const t_idef*            idef,
-                   const WorkDivision&      workDivision,
-                   const rvec               x[],
-                   rvec4                    f[],
-                   rvec                     fshift[],
-                   const t_forcerec*        fr,
-                   const t_pbc*             pbc,
-                   const t_graph*           g,
-                   gmx_grppairener_t*       grpp,
-                   t_nrnb*                  nrnb,
-                   const real*              lambda,
-                   real*                    dvdl,
-                   const t_mdatoms*         md,
-                   t_fcdata*                fcd,
-                   const gmx::StepWorkload& stepWork,
-                   int*                     global_atom_index)
+real calc_one_bond(int                           thread,
+                   int                           ftype,
+                   const InteractionDefinitions& idef,
+                   ArrayRef<const int>           iatoms,
+                   const int                     numNonperturbedInteractions,
+                   const WorkDivision&           workDivision,
+                   const rvec                    x[],
+                   rvec4                         f[],
+                   rvec                          fshift[],
+                   const t_forcerec*             fr,
+                   const t_pbc*                  pbc,
+                   gmx_grppairener_t*            grpp,
+                   t_nrnb*                       nrnb,
+                   const real*                   lambda,
+                   real*                         dvdl,
+                   const t_mdatoms*              md,
+                   t_fcdata*                     fcd,
+                   const gmx::StepWorkload&      stepWork,
+                   int*                          global_atom_index)
 {
-    GMX_ASSERT(idef->ilsort == ilsortNO_FE || idef->ilsort == ilsortFE_SORTED,
+    GMX_ASSERT(idef.ilsort == ilsortNO_FE || idef.ilsort == ilsortFE_SORTED,
                "The topology should be marked either as no FE or sorted on FE");
 
     const bool havePerturbedInteractions =
-            (idef->ilsort == ilsortFE_SORTED && idef->il[ftype].nr_nonperturbed < idef->il[ftype].nr);
+            (idef.ilsort == ilsortFE_SORTED && numNonperturbedInteractions < iatoms.ssize());
     BondedKernelFlavor flavor =
             selectBondedKernelFlavor(stepWork, fr->use_simd_kernels, havePerturbedInteractions);
     int efptFTYPE;
@@ -331,16 +427,17 @@ real calc_one_bond(int                      thread,
         efptFTYPE = efptBONDED;
     }
 
-    const int      nat1   = interaction_function[ftype].nratoms + 1;
-    const int      nbonds = idef->il[ftype].nr / nat1;
-    const t_iatom* iatoms = idef->il[ftype].iatoms;
+    const int nat1   = interaction_function[ftype].nratoms + 1;
+    const int nbonds = iatoms.ssize() / nat1;
 
-    GMX_ASSERT(fr->gpuBonded != nullptr || workDivision.end(ftype) == idef->il[ftype].nr,
+    GMX_ASSERT(fr->gpuBonded != nullptr || workDivision.end(ftype) == iatoms.ssize(),
                "The thread division should match the topology");
 
     const int nb0 = workDivision.bound(ftype, thread);
     const int nbn = workDivision.bound(ftype, thread + 1) - nb0;
 
+    ArrayRef<const t_iparams> iparams = idef.iparams;
+
     real v = 0;
     if (!isPairInteraction(ftype))
     {
@@ -350,13 +447,13 @@ real calc_one_bond(int                      thread,
                nice to account to its own subtimer, but first
                wallcycle needs to be extended to support calling from
                multiple threads. */
-            v = cmap_dihs(nbn, iatoms + nb0, idef->iparams, idef->cmap_grid, x, f, fshift, pbc, g,
-                          lambda[efptFTYPE], &(dvdl[efptFTYPE]), md, fcd, global_atom_index);
+            v = cmap_dihs(nbn, iatoms.data() + nb0, iparams.data(), &idef.cmap_grid, x, f, fshift,
+                          pbc, lambda[efptFTYPE], &(dvdl[efptFTYPE]), md, fcd, global_atom_index);
         }
         else
         {
-            v = calculateSimpleBond(ftype, nbn, iatoms + nb0, idef->iparams, x, f, fshift, pbc, g,
-                                    lambda[efptFTYPE], &(dvdl[efptFTYPE]), md, fcd,
+            v = calculateSimpleBond(ftype, nbn, iatoms.data() + nb0, iparams.data(), x, f, fshift,
+                                    pbc, lambda[efptFTYPE], &(dvdl[efptFTYPE]), md, fcd,
                                     global_atom_index, flavor);
         }
     }
@@ -365,8 +462,8 @@ real calc_one_bond(int                      thread,
         /* TODO The execution time for pairs might be nice to account
            to its own subtimer, but first wallcycle needs to be
            extended to support calling from multiple threads. */
-        do_pairs(ftype, nbn, iatoms + nb0, idef->iparams, x, f, fshift, pbc, g, lambda, dvdl, md,
-                 fr, havePerturbedInteractions, stepWork, grpp, global_atom_index);
+        do_pairs(ftype, nbn, iatoms.data() + nb0, iparams.data(), x, f, fshift, pbc, lambda, dvdl,
+                 md, fr, havePerturbedInteractions, stepWork, grpp, global_atom_index);
     }
 
     if (thread == 0)
@@ -381,23 +478,21 @@ real calc_one_bond(int                      thread,
 
 /*! \brief Compute the bonded part of the listed forces, parallelized over threads
  */
-static void calcBondedForces(const t_idef*            idef,
-                             const rvec               x[],
-                             const t_forcerec*        fr,
-                             const t_pbc*             pbc_null,
-                             const t_graph*           g,
-                             rvec*                    fshiftMasterBuffer,
-                             gmx_enerdata_t*          enerd,
-                             t_nrnb*                  nrnb,
-                             const real*              lambda,
-                             real*                    dvdl,
-                             const t_mdatoms*         md,
-                             t_fcdata*                fcd,
-                             const gmx::StepWorkload& stepWork,
-                             int*                     global_atom_index)
+static void calcBondedForces(const InteractionDefinitions& idef,
+                             bonded_threading_t*           bt,
+                             const rvec                    x[],
+                             const t_forcerec*             fr,
+                             const t_pbc*                  pbc_null,
+                             rvec*                         fshiftMasterBuffer,
+                             gmx_enerdata_t*               enerd,
+                             t_nrnb*                       nrnb,
+                             const real*                   lambda,
+                             real*                         dvdl,
+                             const t_mdatoms*              md,
+                             t_fcdata*                     fcd,
+                             const gmx::StepWorkload&      stepWork,
+                             int*                          global_atom_index)
 {
-    bonded_threading_t* bt = fr->bondedThreading;
-
 #pragma omp parallel for num_threads(bt->nthreads) schedule(static)
     for (int thread = 0; thread < bt->nthreads; thread++)
     {
@@ -427,7 +522,7 @@ static void calcBondedForces(const t_idef*            idef,
             }
             else
             {
-                fshift = threadBuffers.fshift;
+                fshift = as_rvec_array(threadBuffers.fshift.data());
                 epot   = threadBuffers.ener;
                 grpp   = &threadBuffers.grpp;
                 dvdlt  = threadBuffers.dvdl;
@@ -435,11 +530,13 @@ static void calcBondedForces(const t_idef*            idef,
             /* Loop over all bonded force types to calculate the bonded forces */
             for (ftype = 0; (ftype < F_NRE); ftype++)
             {
-                if (idef->il[ftype].nr > 0 && ftype_is_bonded_potential(ftype))
+                const InteractionList& ilist = idef.il[ftype];
+                if (!ilist.empty() && ftype_is_bonded_potential(ftype))
                 {
-                    v = calc_one_bond(thread, ftype, idef, fr->bondedThreading->workDivision, x, ft,
-                                      fshift, fr, pbc_null, g, grpp, nrnb, lambda, dvdlt, md, fcd,
-                                      stepWork, global_atom_index);
+                    ArrayRef<const int> iatoms = gmx::makeConstArrayRef(ilist.iatoms);
+                    v = calc_one_bond(thread, ftype, idef, iatoms, idef.numNonperturbedInteractions[ftype],
+                                      bt->workDivision, x, ft, fshift, fr, pbc_null, grpp, nrnb,
+                                      lambda, dvdlt, md, fcd, stepWork, global_atom_index);
                     epot[ftype] += v;
                 }
             }
@@ -448,96 +545,44 @@ static void calcBondedForces(const t_idef*            idef,
     }
 }
 
-bool haveRestraints(const t_idef& idef, const t_fcdata& fcd)
+bool ListedForces::haveRestraints(const t_fcdata& fcdata) const
 {
-    return ((idef.il[F_POSRES].nr > 0) || (idef.il[F_FBPOSRES].nr > 0) || fcd.orires.nr > 0
-            || fcd.disres.nres > 0);
+    GMX_ASSERT(fcdata.orires && fcdata.disres, "NMR restraints objects should be set up");
+
+    return (!idef_->il[F_POSRES].empty() || !idef_->il[F_FBPOSRES].empty() || fcdata.orires->nr > 0
+            || fcdata.disres->nres > 0);
 }
 
-bool haveCpuBondeds(const t_forcerec& fr)
+bool ListedForces::haveCpuBondeds() const
 {
-    return fr.bondedThreading->haveBondeds;
+    return threading_->haveBondeds;
 }
 
-bool haveCpuListedForces(const t_forcerec& fr, const t_idef& idef, const t_fcdata& fcd)
+bool ListedForces::haveCpuListedForces(const t_fcdata& fcdata) const
 {
-    return haveCpuBondeds(fr) || haveRestraints(idef, fcd);
+    return haveCpuBondeds() || haveRestraints(fcdata);
 }
 
-void calc_listed(const t_commrec*         cr,
-                 const gmx_multisim_t*    ms,
-                 struct gmx_wallcycle*    wcycle,
-                 const t_idef*            idef,
-                 const rvec               x[],
-                 history_t*               hist,
-                 gmx::ForceOutputs*       forceOutputs,
-                 const t_forcerec*        fr,
-                 const struct t_pbc*      pbc,
-                 const struct t_pbc*      pbc_full,
-                 const struct t_graph*    g,
-                 gmx_enerdata_t*          enerd,
-                 t_nrnb*                  nrnb,
-                 const real*              lambda,
-                 const t_mdatoms*         md,
-                 t_fcdata*                fcd,
-                 int*                     global_atom_index,
-                 const gmx::StepWorkload& stepWork)
+namespace
 {
-    const t_pbc*        pbc_null;
-    bonded_threading_t* bt = fr->bondedThreading;
-
-    if (fr->bMolPBC)
-    {
-        pbc_null = pbc;
-    }
-    else
-    {
-        pbc_null = nullptr;
-    }
-
-    if (haveRestraints(*idef, *fcd))
-    {
-        /* TODO Use of restraints triggers further function calls
-           inside the loop over calc_one_bond(), but those are too
-           awkward to account to this subtimer properly in the present
-           code. We don't test / care much about performance with
-           restraints, anyway. */
-        wallcycle_sub_start(wcycle, ewcsRESTRAINTS);
-
-        if (idef->il[F_POSRES].nr > 0)
-        {
-            posres_wrapper(nrnb, idef, pbc_full, x, enerd, lambda, fr, &forceOutputs->forceWithVirial());
-        }
-
-        if (idef->il[F_FBPOSRES].nr > 0)
-        {
-            fbposres_wrapper(nrnb, idef, pbc_full, x, enerd, fr, &forceOutputs->forceWithVirial());
-        }
-
-        /* Do pre force calculation stuff which might require communication */
-        if (fcd->orires.nr > 0)
-        {
-            /* This assertion is to ensure we have whole molecules.
-             * Unfortunately we do not have an mdrun state variable that tells
-             * us if molecules in x are not broken over PBC, so we have to make
-             * do with checking graph!=nullptr, which should tell us if we made
-             * molecules whole before calling the current function.
-             */
-            GMX_RELEASE_ASSERT(fr->ePBC == epbcNONE || g != nullptr,
-                               "With orientation restraints molecules should be whole");
-            enerd->term[F_ORIRESDEV] = calc_orires_dev(ms, idef->il[F_ORIRES].nr, idef->il[F_ORIRES].iatoms,
-                                                       idef->iparams, md, x, pbc_null, fcd, hist);
-        }
-        if (fcd->disres.nres > 0)
-        {
-            calc_disres_R_6(cr, ms, idef->il[F_DISRES].nr, idef->il[F_DISRES].iatoms, x, pbc_null,
-                            fcd, hist);
-        }
-
-        wallcycle_sub_stop(wcycle, ewcsRESTRAINTS);
-    }
 
-    if (haveCpuBondeds(*fr))
+/*! \brief Calculates all listed force interactions. */
+void calc_listed(struct gmx_wallcycle*         wcycle,
+                 const InteractionDefinitions& idef,
+                 bonded_threading_t*           bt,
+                 const rvec                    x[],
+                 gmx::ForceOutputs*            forceOutputs,
+                 const t_forcerec*             fr,
+                 const t_pbc*                  pbc,
+                 gmx_enerdata_t*               enerd,
+                 t_nrnb*                       nrnb,
+                 const real*                   lambda,
+                 const t_mdatoms*              md,
+                 t_fcdata*                     fcd,
+                 int*                          global_atom_index,
+                 const gmx::StepWorkload&      stepWork)
+{
+    if (bt->haveBondeds)
     {
         gmx::ForceWithShiftForces& forceWithShiftForces = forceOutputs->forceWithShiftForces();
 
@@ -545,14 +590,13 @@ void calc_listed(const t_commrec*         cr,
         /* The dummy array is to have a place to store the dhdl at other values
            of lambda, which will be thrown away in the end */
         real dvdl[efptNR] = { 0 };
-        calcBondedForces(idef, x, fr, pbc_null, g,
+        calcBondedForces(idef, bt, x, fr, fr->bMolPBC ? pbc : nullptr,
                          as_rvec_array(forceWithShiftForces.shiftForces().data()), enerd, nrnb,
                          lambda, dvdl, md, fcd, stepWork, global_atom_index);
         wallcycle_sub_stop(wcycle, ewcsLISTED);
 
         wallcycle_sub_start(wcycle, ewcsLISTED_BUF_OPS);
-        reduce_thread_output(fr->natoms_force, &forceWithShiftForces, enerd->term, &enerd->grpp,
-                             dvdl, bt, stepWork);
+        reduce_thread_output(&forceWithShiftForces, enerd->term, &enerd->grpp, dvdl, bt, stepWork);
 
         if (stepWork.computeDhdl)
         {
@@ -567,31 +611,34 @@ void calc_listed(const t_commrec*         cr,
     /* Copy the sum of violations for the distance restraints from fcd */
     if (fcd)
     {
-        enerd->term[F_DISRESVIOL] = fcd->disres.sumviol;
+        enerd->term[F_DISRESVIOL] = fcd->disres->sumviol;
     }
 }
 
-void calc_listed_lambda(const t_idef*         idef,
-                        const rvec            x[],
-                        const t_forcerec*     fr,
-                        const struct t_pbc*   pbc,
-                        const struct t_graph* g,
-                        gmx_grppairener_t*    grpp,
-                        real*                 epot,
-                        t_nrnb*               nrnb,
-                        const real*           lambda,
-                        const t_mdatoms*      md,
-                        t_fcdata*             fcd,
-                        int*                  global_atom_index)
+/*! \brief As calc_listed(), but only determines the potential energy
+ * for the perturbed interactions.
+ *
+ * The shift forces in fr are not affected.
+ */
+void calc_listed_lambda(const InteractionDefinitions& idef,
+                        bonded_threading_t*           bt,
+                        const rvec                    x[],
+                        const t_forcerec*             fr,
+                        const struct t_pbc*           pbc,
+                        gmx::ArrayRef<real>           forceBufferLambda,
+                        gmx::ArrayRef<gmx::RVec>      shiftForceBufferLambda,
+                        gmx_grppairener_t*            grpp,
+                        real*                         epot,
+                        gmx::ArrayRef<real>           dvdl,
+                        t_nrnb*                       nrnb,
+                        const real*                   lambda,
+                        const t_mdatoms*              md,
+                        t_fcdata*                     fcd,
+                        int*                          global_atom_index)
 {
-    real          v;
-    real          dvdl_dum[efptNR] = { 0 };
-    rvec4*        f;
-    rvec*         fshift;
-    const t_pbc*  pbc_null;
-    t_idef        idef_fe;
-    WorkDivision& workDivision = fr->bondedThreading->foreignLambdaWorkDivision;
+    WorkDivision& workDivision = bt->foreignLambdaWorkDivision;
 
+    const t_pbc* pbc_null;
     if (fr->bMolPBC)
     {
         pbc_null = pbc;
@@ -601,94 +648,135 @@ void calc_listed_lambda(const t_idef*         idef,
         pbc_null = nullptr;
     }
 
-    /* Copy the whole idef, so we can modify the contents locally */
-    idef_fe = *idef;
-
     /* We already have the forces, so we use temp buffers here */
-    // TODO: Get rid of these allocations by using permanent force buffers
-    snew(f, fr->natoms_force);
-    snew(fshift, SHIFTS);
+    std::fill(forceBufferLambda.begin(), forceBufferLambda.end(), 0.0_real);
+    std::fill(shiftForceBufferLambda.begin(), shiftForceBufferLambda.end(),
+              gmx::RVec{ 0.0_real, 0.0_real, 0.0_real });
+    rvec4* f      = reinterpret_cast<rvec4*>(forceBufferLambda.data());
+    rvec*  fshift = as_rvec_array(shiftForceBufferLambda.data());
 
     /* Loop over all bonded force types to calculate the bonded energies */
     for (int ftype = 0; (ftype < F_NRE); ftype++)
     {
         if (ftype_is_bonded_potential(ftype))
         {
-            const t_ilist& ilist = idef->il[ftype];
-            /* Create a temporary t_ilist with only perturbed interactions */
-            t_ilist& ilist_fe        = idef_fe.il[ftype];
-            ilist_fe.iatoms          = ilist.iatoms + ilist.nr_nonperturbed;
-            ilist_fe.nr_nonperturbed = 0;
-            ilist_fe.nr              = ilist.nr - ilist.nr_nonperturbed;
-            /* Set the work range of thread 0 to the perturbed bondeds */
-            workDivision.setBound(ftype, 0, 0);
-            workDivision.setBound(ftype, 1, ilist_fe.nr);
-
-            if (ilist_fe.nr > 0)
+            const InteractionList& ilist = idef.il[ftype];
+            /* Create a temporary iatom list with only perturbed interactions */
+            const int           numNonperturbed = idef.numNonperturbedInteractions[ftype];
+            ArrayRef<const int> iatomsPerturbed = gmx::constArrayRefFromArray(
+                    ilist.iatoms.data() + numNonperturbed, ilist.size() - numNonperturbed);
+            if (!iatomsPerturbed.empty())
             {
+                /* Set the work range of thread 0 to the perturbed bondeds */
+                workDivision.setBound(ftype, 0, 0);
+                workDivision.setBound(ftype, 1, iatomsPerturbed.ssize());
+
                 gmx::StepWorkload tempFlags;
                 tempFlags.computeEnergy = true;
-                v = calc_one_bond(0, ftype, &idef_fe, workDivision, x, f, fshift, fr, pbc_null, g,
-                                  grpp, nrnb, lambda, dvdl_dum, md, fcd, tempFlags, global_atom_index);
+                real v = calc_one_bond(0, ftype, idef, iatomsPerturbed, iatomsPerturbed.ssize(),
+                                       workDivision, x, f, fshift, fr, pbc_null, grpp, nrnb, lambda,
+                                       dvdl.data(), md, fcd, tempFlags, global_atom_index);
                 epot[ftype] += v;
             }
         }
     }
-
-    sfree(fshift);
-    sfree(f);
 }
 
-void do_force_listed(struct gmx_wallcycle*    wcycle,
-                     const matrix             box,
-                     const t_lambda*          fepvals,
-                     const t_commrec*         cr,
-                     const gmx_multisim_t*    ms,
-                     const t_idef*            idef,
-                     const rvec               x[],
-                     history_t*               hist,
-                     gmx::ForceOutputs*       forceOutputs,
-                     const t_forcerec*        fr,
-                     const struct t_pbc*      pbc,
-                     const struct t_graph*    graph,
-                     gmx_enerdata_t*          enerd,
-                     t_nrnb*                  nrnb,
-                     const real*              lambda,
-                     const t_mdatoms*         md,
-                     t_fcdata*                fcd,
-                     int*                     global_atom_index,
-                     const gmx::StepWorkload& stepWork)
-{
-    t_pbc pbc_full; /* Full PBC is needed for position restraints */
+} // namespace
 
-    if (!stepWork.computeListedForces)
+void ListedForces::calculate(struct gmx_wallcycle*                     wcycle,
+                             const matrix                              box,
+                             const t_lambda*                           fepvals,
+                             const t_commrec*                          cr,
+                             const gmx_multisim_t*                     ms,
+                             gmx::ArrayRefWithPadding<const gmx::RVec> coordinates,
+                             gmx::ArrayRef<const gmx::RVec>            xWholeMolecules,
+                             t_fcdata*                                 fcdata,
+                             history_t*                                hist,
+                             gmx::ForceOutputs*                        forceOutputs,
+                             const t_forcerec*                         fr,
+                             const struct t_pbc*                       pbc,
+                             gmx_enerdata_t*                           enerd,
+                             t_nrnb*                                   nrnb,
+                             const real*                               lambda,
+                             const t_mdatoms*                          md,
+                             int*                                      global_atom_index,
+                             const gmx::StepWorkload&                  stepWork)
+{
+    if (interactionSelection_.none() || !stepWork.computeListedForces)
     {
         return;
     }
 
-    if ((idef->il[F_POSRES].nr > 0) || (idef->il[F_FBPOSRES].nr > 0))
+    const InteractionDefinitions& idef = *idef_;
+
+    // Todo: replace all rvec use here with ArrayRefWithPadding
+    const rvec* x = as_rvec_array(coordinates.paddedArrayRef().data());
+
+    t_pbc pbc_full; /* Full PBC is needed for position restraints */
+    if (haveRestraints(*fcdata))
     {
-        /* Not enough flops to bother counting */
-        set_pbc(&pbc_full, fr->ePBC, box);
+        if (!idef.il[F_POSRES].empty() || !idef.il[F_FBPOSRES].empty())
+        {
+            /* Not enough flops to bother counting */
+            set_pbc(&pbc_full, fr->pbcType, box);
+        }
+
+        /* TODO Use of restraints triggers further function calls
+           inside the loop over calc_one_bond(), but those are too
+           awkward to account to this subtimer properly in the present
+           code. We don't test / care much about performance with
+           restraints, anyway. */
+        wallcycle_sub_start(wcycle, ewcsRESTRAINTS);
+
+        if (!idef.il[F_POSRES].empty())
+        {
+            posres_wrapper(nrnb, idef, &pbc_full, x, enerd, lambda, fr, &forceOutputs->forceWithVirial());
+        }
+
+        if (!idef.il[F_FBPOSRES].empty())
+        {
+            fbposres_wrapper(nrnb, idef, &pbc_full, x, enerd, fr, &forceOutputs->forceWithVirial());
+        }
+
+        /* Do pre force calculation stuff which might require communication */
+        if (fcdata->orires->nr > 0)
+        {
+            GMX_ASSERT(!xWholeMolecules.empty(), "Need whole molecules for orienation restraints");
+            enerd->term[F_ORIRESDEV] = calc_orires_dev(
+                    ms, idef.il[F_ORIRES].size(), idef.il[F_ORIRES].iatoms.data(), idef.iparams.data(),
+                    md, xWholeMolecules, x, fr->bMolPBC ? pbc : nullptr, fcdata->orires, hist);
+        }
+        if (fcdata->disres->nres > 0)
+        {
+            calc_disres_R_6(cr, ms, idef.il[F_DISRES].size(), idef.il[F_DISRES].iatoms.data(), x,
+                            fr->bMolPBC ? pbc : nullptr, fcdata->disres, hist);
+        }
+
+        wallcycle_sub_stop(wcycle, ewcsRESTRAINTS);
     }
-    calc_listed(cr, ms, wcycle, idef, x, hist, forceOutputs, fr, pbc, &pbc_full, graph, enerd, nrnb,
-                lambda, md, fcd, global_atom_index, stepWork);
+
+    calc_listed(wcycle, idef, threading_.get(), x, forceOutputs, fr, pbc, enerd, nrnb, lambda, md,
+                fcdata, global_atom_index, stepWork);
 
     /* Check if we have to determine energy differences
      * at foreign lambda's.
      */
     if (fepvals->n_lambda > 0 && stepWork.computeDhdl)
     {
-        posres_wrapper_lambda(wcycle, fepvals, idef, &pbc_full, x, enerd, lambda, fr);
-
-        if (idef->ilsort != ilsortNO_FE)
+        real dvdl[efptNR] = { 0 };
+        if (!idef.il[F_POSRES].empty())
+        {
+            posres_wrapper_lambda(wcycle, fepvals, idef, &pbc_full, x, enerd, lambda, fr);
+        }
+        if (idef.ilsort != ilsortNO_FE)
         {
             wallcycle_sub_start(wcycle, ewcsLISTED_FEP);
-            if (idef->ilsort != ilsortFE_SORTED)
+            if (idef.ilsort != ilsortFE_SORTED)
             {
                 gmx_incons("The bonded interactions are not sorted for free energy");
             }
-            for (size_t i = 0; i < enerd->enerpart_lambda.size(); i++)
+            for (int i = 0; i < 1 + enerd->foreignLambdaTerms.numLambdas(); i++)
             {
                 real lam_i[efptNR];
 
@@ -697,10 +785,13 @@ void do_force_listed(struct gmx_wallcycle*    wcycle,
                 {
                     lam_i[j] = (i == 0 ? lambda[j] : fepvals->all_lambda[j][i - 1]);
                 }
-                calc_listed_lambda(idef, x, fr, pbc, graph, &(enerd->foreign_grpp),
-                                   enerd->foreign_term, nrnb, lam_i, md, fcd, global_atom_index);
-                sum_epot(&(enerd->foreign_grpp), enerd->foreign_term);
-                enerd->enerpart_lambda[i] += enerd->foreign_term[F_EPOT];
+                calc_listed_lambda(idef, threading_.get(), x, fr, pbc, forceBufferLambda_,
+                                   shiftForceBufferLambda_, &(enerd->foreign_grpp), enerd->foreign_term,
+                                   dvdl, nrnb, lam_i, md, fcdata, global_atom_index);
+                sum_epot(enerd->foreign_grpp, enerd->foreign_term);
+                const double dvdlSum = std::accumulate(std::begin(dvdl), std::end(dvdl), 0.);
+                std::fill(std::begin(dvdl), std::end(dvdl), 0.0);
+                enerd->foreignLambdaTerms.accumulate(i, enerd->foreign_term[F_EPOT], dvdlSum);
             }
             wallcycle_sub_stop(wcycle, ewcsLISTED_FEP);
         }
index 00aa11bb3e40da322db393dff752f7fbac947426..c044baf11730375ab30db3a15b08b902bdfadfb8 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,6 +49,7 @@
      and reduction of output data across threads.
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Berk Hess <hess@kth.se>
  *
  */
 /*! \libinternal \file
@@ -59,6 +61,7 @@
  * should call functions declared here.
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Berk Hess <hess@kth.se>
  *
  * \inlibraryapi
  * \ingroup module_listed_forces
 #ifndef GMX_LISTED_FORCES_LISTED_FORCES_H
 #define GMX_LISTED_FORCES_LISTED_FORCES_H
 
+#include <memory>
+#include <vector>
+
+#include <bitset>
+
 #include "gromacs/math/vectypes.h"
+#include "gromacs/topology/idef.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/classhelpers.h"
 
+struct bonded_threading_t;
 struct gmx_enerdata_t;
+struct gmx_ffparams_t;
 struct gmx_grppairener_t;
+struct gmx_localtop_t;
 struct gmx_multisim_t;
 class history_t;
 struct t_commrec;
 struct t_fcdata;
 struct t_forcerec;
-struct t_idef;
-struct t_graph;
 struct t_lambda;
 struct t_mdatoms;
 struct t_nrnb;
@@ -88,6 +99,10 @@ namespace gmx
 {
 class ForceOutputs;
 class StepWorkload;
+template<typename>
+class ArrayRef;
+template<typename>
+class ArrayRefWithPadding;
 } // namespace gmx
 
 //! Type of CPU function to compute a bonded interaction.
@@ -98,7 +113,6 @@ using BondedFunction = real (*)(int              nbonds,
                                 rvec4            f[],
                                 rvec             fshift[],
                                 const t_pbc*     pbc,
-                                const t_graph*   g,
                                 real             lambda,
                                 real*            dvdlambda,
                                 const t_mdatoms* md,
@@ -108,79 +122,116 @@ using BondedFunction = real (*)(int              nbonds,
 //! Getter for finding a callable CPU function to compute an \c ftype interaction.
 BondedFunction bondedFunction(int ftype);
 
-/*! \brief Calculates all listed force interactions.
- *
- * Note that pbc_full is used only for position restraints, and is
- * not initialized if there are none. */
-void calc_listed(const t_commrec*         cr,
-                 const gmx_multisim_t*    ms,
-                 struct gmx_wallcycle*    wcycle,
-                 const t_idef*            idef,
-                 const rvec               x[],
-                 history_t*               hist,
-                 gmx::ForceOutputs*       forceOutputs,
-                 const t_forcerec*        fr,
-                 const struct t_pbc*      pbc,
-                 const struct t_pbc*      pbc_full,
-                 const struct t_graph*    g,
-                 gmx_enerdata_t*          enerd,
-                 t_nrnb*                  nrnb,
-                 const real*              lambda,
-                 const t_mdatoms*         md,
-                 struct t_fcdata*         fcd,
-                 int*                     ddgatindex,
-                 const gmx::StepWorkload& stepWork);
-
-/*! \brief As calc_listed(), but only determines the potential energy
- * for the perturbed interactions.
+/*! \libinternal
+ * \brief Class for calculating listed interactions, uses OpenMP parallelization
  *
- * The shift forces in fr are not affected. */
-void calc_listed_lambda(const t_idef*         idef,
-                        const rvec            x[],
-                        const t_forcerec*     fr,
-                        const struct t_pbc*   pbc,
-                        const struct t_graph* g,
-                        gmx_grppairener_t*    grpp,
-                        real*                 epot,
-                        t_nrnb*               nrnb,
-                        const real*           lambda,
-                        const t_mdatoms*      md,
-                        struct t_fcdata*      fcd,
-                        int*                  global_atom_index);
-
-/*! \brief Do all aspects of energy and force calculations for mdrun
- * on the set of listed interactions */
-void do_force_listed(struct gmx_wallcycle*    wcycle,
-                     const matrix             box,
-                     const t_lambda*          fepvals,
-                     const t_commrec*         cr,
-                     const gmx_multisim_t*    ms,
-                     const t_idef*            idef,
-                     const rvec               x[],
-                     history_t*               hist,
-                     gmx::ForceOutputs*       forceOutputs,
-                     const t_forcerec*        fr,
-                     const struct t_pbc*      pbc,
-                     const struct t_graph*    graph,
-                     gmx_enerdata_t*          enerd,
-                     t_nrnb*                  nrnb,
-                     const real*              lambda,
-                     const t_mdatoms*         md,
-                     struct t_fcdata*         fcd,
-                     int*                     global_atom_index,
-                     const gmx::StepWorkload& stepWork);
-
-/*! \brief Returns true if there are position, distance or orientation restraints. */
-bool haveRestraints(const t_idef& idef, const t_fcdata& fcd);
-
-/*! \brief Returns true if there are CPU (i.e. not GPU-offloaded) bonded interactions to compute. */
-bool haveCpuBondeds(const t_forcerec& fr);
-
-/*! \brief Returns true if there are listed interactions to compute.
- *
- * NOTE: the current implementation returns true if there are position restraints
- * or any bonded interactions computed on the CPU.
+ * Listed interactions can be divided over multiple instances of ListedForces
+ * using the selection flags passed to the constructor.
  */
-bool haveCpuListedForces(const t_forcerec& fr, const t_idef& idef, const t_fcdata& fcd);
+class ListedForces
+{
+public:
+    //! Enum for selecting groups of listed interaction types
+    enum class InteractionGroup : int
+    {
+        Pairs,     //!< Pair interactions
+        Dihedrals, //!< Dihedrals, including cmap
+        Angles,    //!< Angles
+        Rest,      //!< All listed interactions that are not any of the above
+        Count      //!< The number of items above
+    };
+
+    //! Type for specifying selections of groups of interaction types
+    using InteractionSelection = std::bitset<static_cast<int>(InteractionGroup::Count)>;
+
+    //! Returns a selection with all listed interaction types selected
+    static InteractionSelection interactionSelectionAll()
+    {
+        InteractionSelection is;
+        return is.flip();
+    }
+
+    /*! \brief Constructor
+     *
+     * \param[in] ffparams         The force field parameters
+     * \param[in] numEnergyGroups  The number of energy groups, used for storage of pair energies
+     * \param[in] numThreads       The number of threads used for computed listed interactions
+     * \param[in] interactionSelection  Select of interaction groups through bits set
+     * \param[in] fplog            Log file for printing env.var. override, can be nullptr
+     */
+    ListedForces(const gmx_ffparams_t& ffparams,
+                 int                   numEnergyGroups,
+                 int                   numThreads,
+                 InteractionSelection  interactionSelection,
+                 FILE*                 fplog);
+
+    //! Move constructor, default, but in the source file to hide implementation classes
+    ListedForces(ListedForces&& o) noexcept;
+
+    //! Destructor which is actually default but in the source file to hide implementation classes
+    ~ListedForces();
+
+    /*! \brief Copy the listed interactions from \p idef and set up the thread parallelization
+     *
+     * \param[in] domainIdef     Interaction definitions for all listed interactions to be computed on this domain/rank
+     * \param[in] numAtomsForce  Force are, potentially, computed for atoms 0 to \p numAtomsForce
+     * \param[in] useGpu         Whether a GPU is used to compute (part of) the listed interactions
+     */
+    void setup(const InteractionDefinitions& domainIdef, int numAtomsForce, bool useGpu);
+
+    /*! \brief Do all aspects of energy and force calculations for mdrun
+     * on the set of listed interactions
+     *
+     * xWholeMolecules only needs to contain whole molecules when orientation
+     * restraints need to be computed and can be empty otherwise.
+     */
+    void calculate(struct gmx_wallcycle*                     wcycle,
+                   const matrix                              box,
+                   const t_lambda*                           fepvals,
+                   const t_commrec*                          cr,
+                   const gmx_multisim_t*                     ms,
+                   gmx::ArrayRefWithPadding<const gmx::RVec> coordinates,
+                   gmx::ArrayRef<const gmx::RVec>            xWholeMolecules,
+                   t_fcdata*                                 fcdata,
+                   history_t*                                hist,
+                   gmx::ForceOutputs*                        forceOutputs,
+                   const t_forcerec*                         fr,
+                   const struct t_pbc*                       pbc,
+                   gmx_enerdata_t*                           enerd,
+                   t_nrnb*                                   nrnb,
+                   const real*                               lambda,
+                   const t_mdatoms*                          md,
+                   int*                                      global_atom_index,
+                   const gmx::StepWorkload&                  stepWork);
+
+    //! Returns whether bonded interactions are assigned to the CPU
+    bool haveCpuBondeds() const;
+
+    /*! \brief Returns whether listed forces are computed on the CPU
+     *
+     * NOTE: the current implementation returns true if there are position restraints
+     * or any bonded interactions computed on the CPU.
+     */
+    bool haveCpuListedForces(const t_fcdata& fcdata) const;
+
+    //! Returns true if there are position, distance or orientation restraints
+    bool haveRestraints(const t_fcdata& fcdata) const;
+
+private:
+    //! Pointer to the interaction definitions
+    InteractionDefinitions const* idef_ = nullptr;
+    //! Interaction defintions used for storing selections
+    InteractionDefinitions idefSelection_;
+    //! Thread parallelization setup, unique_ptr to avoid declaring bonded_threading_t
+    std::unique_ptr<bonded_threading_t> threading_;
+    //! Tells which interactions to select for computation
+    const InteractionSelection interactionSelection_;
+    //! Force buffer for free-energy forces
+    std::vector<real> forceBufferLambda_;
+    //! Shift force buffer for free-energy forces
+    std::vector<gmx::RVec> shiftForceBufferLambda_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(ListedForces);
+};
 
 #endif
index a9e698deb8950d80904e387f0216c1ed8404e023..2e130dcb835930729d9623cb47413bd9a98721eb 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,6 +39,7 @@
  * internally by the module.
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Berk Hess <hess@kth.se>
  * \ingroup module_listed_forces
  */
 #ifndef GMX_LISTED_FORCES_LISTED_INTERNAL_H
@@ -49,7 +51,9 @@
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/topology/idef.h"
 #include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/alignedallocator.h"
 #include "gromacs/utility/bitmask.h"
+#include "gromacs/utility/classhelpers.h"
 
 /* We reduce the force array in blocks of 32 atoms. This is large enough
  * to not cause overhead and 32*sizeof(rvec) is a multiple of the cache-line
@@ -93,52 +97,65 @@ struct f_thread_t
     //! Constructor
     f_thread_t(int numEnergyGroups);
 
-    ~f_thread_t();
+    ~f_thread_t() = default;
 
-    rvec4*         f        = nullptr; /**< Force array */
-    int            f_nalloc = 0;       /**< Allocation size of f */
-    gmx_bitmask_t* mask =
-            nullptr; /**< Mask for marking which parts of f are filled, working array for constructing mask in bonded_threading_t */
-    int  nblock_used;            /**< Number of blocks touched by our thread */
-    int* block_index  = nullptr; /**< Index to touched blocks, size nblock_used */
-    int  block_nalloc = 0; /**< Allocation size of f (*reduction_block_size), mask_index, mask */
+    //! Force array pointer, equals fBuffer.data(), needed because rvec4 is not a C++ type
+    rvec4* f = nullptr;
+    //! Force array buffer
+    std::vector<real, gmx::AlignedAllocator<real>> fBuffer;
+    //! Mask for marking which parts of f are filled, working array for constructing mask in bonded_threading_t
+    std::vector<gmx_bitmask_t> mask;
+    //! Number of blocks touched by our thread
+    int nblock_used = 0;
+    //! Index to touched blocks
+    std::vector<int> block_index;
 
-    rvec*             fshift;       /**< Shift force array, size SHIFTS */
-    real              ener[F_NRE];  /**< Energy array */
-    gmx_grppairener_t grpp;         /**< Group pair energy data for pairs */
-    real              dvdl[efptNR]; /**< Free-energy dV/dl output */
+    //! Shift force array, size SHIFTS
+    std::vector<gmx::RVec> fshift;
+    //! Energy array
+    real ener[F_NRE];
+    //! Group pair energy data for pairs
+    gmx_grppairener_t grpp;
+    //! Free-energy dV/dl output
+    real dvdl[efptNR];
+
+    GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(f_thread_t);
 };
 
 /*! \internal \brief struct contain all data for bonded force threading */
 struct bonded_threading_t
 {
     //! Constructor
-    bonded_threading_t(int numThreads, int numEnergyGroups);
+    bonded_threading_t(int numThreads, int numEnergyGroups, FILE* fplog);
 
     //! Number of threads to be used for bondeds
-    int nthreads;
+    int nthreads = 0;
     //! Force/energy data per thread, size nthreads, stored in unique_ptr to allow thread local allocation
     std::vector<std::unique_ptr<f_thread_t>> f_t;
     //! The number of force blocks to reduce
-    int nblock_used;
+    int nblock_used = 0;
     //! Index of size nblock_used into mask
     std::vector<int> block_index;
     //! Mask array, one element corresponds to a block of reduction_block_size atoms of the force array, bit corresponding to thread indices set if a thread writes to that block
     std::vector<gmx_bitmask_t> mask;
     //! true if we have and thus need to reduce bonded forces
-    bool haveBondeds;
+    bool haveBondeds = false;
+    //! The number of atoms forces are computed for
+    int numAtomsForce = 0;
 
     /* There are two different ways to distribute the bonded force calculation
      * over the threads. We dedice which to use based on the number of threads.
      */
     //! Maximum thread count for uniform distribution of bondeds over threads
-    int max_nthread_uniform;
+    int max_nthread_uniform = 0;
 
     //! The division of work in the t_list over threads.
     WorkDivision workDivision;
 
     //! Work division for free-energy foreign lambda calculations, always uses 1 thread
     WorkDivision foreignLambdaWorkDivision;
+
+    GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(bonded_threading_t);
 };
 
 
index f8d4ff05848abe7217f334ee91e08bc1d00d5371..f666b5a012ecdb5499348fa29fa67533b631caa9 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -39,6 +40,7 @@
  * interactions.
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Berk Hess <hess@kth.se>
  * \ingroup module_listed_forces
  */
 #include "gmxpre.h"
 #include <string>
 
 #include "gromacs/listed_forces/gpubonded.h"
-#include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/pbcutil/ishift.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
-#include "gromacs/utility/smalloc.h"
 
 #include "listed_internal.h"
 #include "utilities.h"
@@ -70,9 +70,9 @@
 /*! \brief struct for passing all data required for a function type */
 typedef struct
 {
-    const t_ilist* il;    /**< pointer to t_ilist entry corresponding to ftype */
-    int            ftype; /**< the function type index */
-    int            nat;   /**< nr of atoms involved in a single ftype interaction */
+    const InteractionList* il;    /**< pointer to t_ilist entry corresponding to ftype */
+    int                    ftype; /**< the function type index */
+    int                    nat;   /**< nr of atoms involved in a single ftype interaction */
 } ilist_data_t;
 
 /*! \brief Divides listed interactions over threads
@@ -95,11 +95,11 @@ static void divide_bondeds_by_locality(bonded_threading_t* bt, int numType, cons
     for (f = 0; f < numType; f++)
     {
         /* Sum #bondeds*#atoms_per_bond over all bonded types */
-        nat_tot += ild[f].il->nr / (ild[f].nat + 1) * ild[f].nat;
+        nat_tot += ild[f].il->size() / (ild[f].nat + 1) * ild[f].nat;
         /* The start bound for thread 0 is 0 for all interactions */
         ind[f] = 0;
         /* Initialize the next atom index array */
-        assert(ild[f].il->nr > 0);
+        assert(!ild[f].il->empty());
         at_ind[f] = ild[f].il->iatoms[1];
     }
 
@@ -161,7 +161,7 @@ static void divide_bondeds_by_locality(bonded_threading_t* bt, int numType, cons
             nat_sum += ild[f_min].nat;
 
             /* Update the first unassigned atom index for this type */
-            if (ind[f_min] < ild[f_min].il->nr)
+            if (ind[f_min] < ild[f_min].il->size())
             {
                 at_ind[f_min] = ild[f_min].il->iatoms[ind[f_min] + 1];
             }
@@ -184,29 +184,33 @@ static void divide_bondeds_by_locality(bonded_threading_t* bt, int numType, cons
 
     for (f = 0; f < numType; f++)
     {
-        assert(ind[f] == ild[f].il->nr);
+        assert(ind[f] == ild[f].il->size());
     }
 }
 
 //! Return whether function type \p ftype in \p idef has perturbed interactions
-static bool ftypeHasPerturbedEntries(const t_idef& idef, int ftype)
+static bool ftypeHasPerturbedEntries(const InteractionDefinitions& idef, int ftype)
 {
     GMX_ASSERT(idef.ilsort == ilsortNO_FE || idef.ilsort == ilsortFE_SORTED,
                "Perturbed interations should be sorted here");
 
-    const t_ilist& ilist = idef.il[ftype];
+    const InteractionList& ilist = idef.il[ftype];
 
-    return (idef.ilsort != ilsortNO_FE && ilist.nr_nonperturbed != ilist.nr);
+    return (idef.ilsort != ilsortNO_FE && idef.numNonperturbedInteractions[ftype] != ilist.size());
 }
 
 //! Divides bonded interactions over threads and GPU
-static void divide_bondeds_over_threads(bonded_threading_t* bt, bool useGpuForBondeds, const t_idef& idef)
+static void divide_bondeds_over_threads(bonded_threading_t*           bt,
+                                        bool                          useGpuForBondeds,
+                                        const InteractionDefinitions& idef)
 {
     ilist_data_t ild[F_NRE];
 
     GMX_ASSERT(bt->nthreads > 0, "Must have positive number of threads");
     const int numThreads = bt->nthreads;
 
+    gmx::ArrayRef<const t_iparams> iparams = idef.iparams;
+
     bt->haveBondeds      = false;
     int    numType       = 0;
     size_t fTypeGpuIndex = 0;
@@ -217,8 +221,8 @@ static void divide_bondeds_over_threads(bonded_threading_t* bt, bool useGpuForBo
             continue;
         }
 
-        const t_ilist& il                     = idef.il[fType];
-        int            nrToAssignToCpuThreads = il.nr;
+        const InteractionList& il                     = idef.il[fType];
+        int                    nrToAssignToCpuThreads = il.size();
 
         if (useGpuForBondeds && fTypeGpuIndex < gmx::fTypesOnGpu.size()
             && gmx::fTypesOnGpu[fTypeGpuIndex] == fType)
@@ -268,8 +272,8 @@ static void divide_bondeds_over_threads(bonded_threading_t* bt, bool useGpuForBo
                      * end up on the same thread.
                      */
                     while (nr_t > 0 && nr_t < nrToAssignToCpuThreads
-                           && idef.iparams[il.iatoms[nr_t]].disres.label
-                                      == idef.iparams[il.iatoms[nr_t - stride]].disres.label)
+                           && iparams[il.iatoms[nr_t]].disres.label
+                                      == iparams[il.iatoms[nr_t - stride]].disres.label)
                     {
                         nr_t += stride;
                     }
@@ -305,7 +309,7 @@ static void divide_bondeds_over_threads(bonded_threading_t* bt, bool useGpuForBo
         fprintf(debug, "Division of bondeds over threads:\n");
         for (f = 0; f < F_NRE; f++)
         {
-            if (ftype_is_bonded_potential(f) && idef.il[f].nr > 0)
+            if (ftype_is_bonded_potential(f) && !idef.il[f].empty())
             {
                 int t;
 
@@ -323,52 +327,46 @@ static void divide_bondeds_over_threads(bonded_threading_t* bt, bool useGpuForBo
 }
 
 //! Construct a reduction mask for which parts (blocks) of the force array are touched on which thread task
-static void calc_bonded_reduction_mask(int                       natoms,
-                                       f_thread_t*               f_thread,
-                                       const t_idef&             idef,
-                                       int                       thread,
-                                       const bonded_threading_t& bondedThreading)
+static void calc_bonded_reduction_mask(int                           natoms,
+                                       f_thread_t*                   f_thread,
+                                       const InteractionDefinitions& idef,
+                                       int                           thread,
+                                       const bonded_threading_t&     bondedThreading)
 {
     static_assert(BITMASK_SIZE == GMX_OPENMP_MAX_THREADS,
                   "For the error message below we assume these two are equal.");
 
     if (bondedThreading.nthreads > BITMASK_SIZE)
     {
-#pragma omp master
         gmx_fatal(FARGS,
                   "You are using %d OpenMP threads, which is larger than GMX_OPENMP_MAX_THREADS "
                   "(%d). Decrease the number of OpenMP threads or rebuild GROMACS with a larger "
                   "value for GMX_OPENMP_MAX_THREADS passed to CMake.",
                   bondedThreading.nthreads, GMX_OPENMP_MAX_THREADS);
-#pragma omp barrier
     }
     GMX_ASSERT(bondedThreading.nthreads <= BITMASK_SIZE,
                "We need at least nthreads bits in the mask");
 
-    int nblock = (natoms + reduction_block_size - 1) >> reduction_block_bits;
+    const int nblock = (natoms + reduction_block_size - 1) >> reduction_block_bits;
 
-    if (nblock > f_thread->block_nalloc)
-    {
-        f_thread->block_nalloc = over_alloc_large(nblock);
-        srenew(f_thread->mask, f_thread->block_nalloc);
-        srenew(f_thread->block_index, f_thread->block_nalloc);
-        // NOTE: It seems f_thread->f does not need to be aligned
-        sfree_aligned(f_thread->f);
-        snew_aligned(f_thread->f, f_thread->block_nalloc * reduction_block_size, 128);
-    }
+    f_thread->mask.resize(nblock);
+    f_thread->block_index.resize(nblock);
+    // NOTE: It seems f_thread->f does not need to be aligned
+    f_thread->fBuffer.resize(nblock * reduction_block_size * sizeof(rvec4) / sizeof(real));
+    f_thread->f = reinterpret_cast<rvec4*>(f_thread->fBuffer.data());
 
-    gmx_bitmask_t* mask = f_thread->mask;
-
-    for (int b = 0; b < nblock; b++)
+    for (gmx_bitmask_t& mask : f_thread->mask)
     {
-        bitmask_clear(&mask[b]);
+        bitmask_clear(&mask);
     }
 
+    gmx::ArrayRef<gmx_bitmask_t> mask = f_thread->mask;
+
     for (int ftype = 0; ftype < F_NRE; ftype++)
     {
         if (ftype_is_bonded_potential(ftype))
         {
-            int nb = idef.il[ftype].nr;
+            int nb = idef.il[ftype].size();
             if (nb > 0)
             {
                 int nat1 = interaction_function[ftype].nratoms + 1;
@@ -400,12 +398,17 @@ static void calc_bonded_reduction_mask(int                       natoms,
     }
 }
 
-void setup_bonded_threading(bonded_threading_t* bt, int numAtoms, bool useGpuForBondeds, const t_idef& idef)
+void setup_bonded_threading(bonded_threading_t*           bt,
+                            int                           numAtomsForce,
+                            bool                          useGpuForBondeds,
+                            const InteractionDefinitions& idef)
 {
     int ctot = 0;
 
     assert(bt->nthreads >= 1);
 
+    bt->numAtomsForce = numAtomsForce;
+
     /* Divide the bonded interaction over the threads */
     divide_bondeds_over_threads(bt, useGpuForBondeds, idef);
 
@@ -423,7 +426,7 @@ void setup_bonded_threading(bonded_threading_t* bt, int numAtoms, bool useGpuFor
     {
         try
         {
-            calc_bonded_reduction_mask(numAtoms, bt->f_t[t].get(), idef, t, *bt);
+            calc_bonded_reduction_mask(numAtomsForce, bt->f_t[t].get(), idef, t, *bt);
         }
         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
     }
@@ -431,7 +434,7 @@ void setup_bonded_threading(bonded_threading_t* bt, int numAtoms, bool useGpuFor
     /* Reduce the masks over the threads and determine which blocks
      * we need to reduce over.
      */
-    int nblock_tot = (numAtoms + reduction_block_size - 1) >> reduction_block_bits;
+    int nblock_tot = (numAtomsForce + reduction_block_size - 1) >> reduction_block_bits;
     /* Ensure we have sufficient space for all blocks */
     if (static_cast<size_t>(nblock_tot) > bt->block_index.size())
     {
@@ -479,36 +482,29 @@ void setup_bonded_threading(bonded_threading_t* bt, int numAtoms, bool useGpuFor
     {
         fprintf(debug, "Number of %d atom blocks to reduce: %d\n", reduction_block_size, bt->nblock_used);
         fprintf(debug, "Reduction density %.2f for touched blocks only %.2f\n",
-                ctot * reduction_block_size / static_cast<double>(numAtoms),
+                ctot * reduction_block_size / static_cast<double>(numAtomsForce),
                 ctot / static_cast<double>(bt->nblock_used));
     }
 }
 
-void tear_down_bonded_threading(bonded_threading_t* bt)
-{
-    delete bt;
-}
+f_thread_t::f_thread_t(int numEnergyGroups) : fshift(SHIFTS), grpp(numEnergyGroups) {}
 
-f_thread_t::f_thread_t(int numEnergyGroups) : grpp(numEnergyGroups)
-{
-    snew(fshift, SHIFTS);
-}
-
-f_thread_t::~f_thread_t()
-{
-    sfree(mask);
-    sfree(fshift);
-    sfree(block_index);
-    sfree_aligned(f);
-}
-
-bonded_threading_t::bonded_threading_t(const int numThreads, const int numEnergyGroups) :
+bonded_threading_t::bonded_threading_t(const int numThreads, const int numEnergyGroups, FILE* fplog) :
     nthreads(numThreads),
     nblock_used(0),
     haveBondeds(false),
     workDivision(nthreads),
     foreignLambdaWorkDivision(1)
 {
+    /* These thread local data structures are used for bondeds only.
+     *
+     * Note that we also use there structures when running single-threaded.
+     * This is because the bonded force buffer uses type rvec4, whereas
+     * the normal force buffer is uses type rvec. This leads to a little
+     * reduction overhead, but the speed gain in the bonded calculations
+     * of doing transposeScatterIncr/DecrU with aligment 4 instead of 3
+     * is much larger than the reduction overhead.
+     */
     f_t.resize(numThreads);
 #pragma omp parallel for num_threads(nthreads) schedule(static)
     for (int t = 0; t < nthreads; t++)
@@ -522,41 +518,25 @@ bonded_threading_t::bonded_threading_t(const int numThreads, const int numEnergy
         }
         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
     }
-}
-
-bonded_threading_t* init_bonded_threading(FILE* fplog, const int nenergrp)
-{
-    /* These thread local data structures are used for bondeds only.
-     *
-     * Note that we also use there structures when running single-threaded.
-     * This is because the bonded force buffer uses type rvec4, whereas
-     * the normal force buffer is uses type rvec. This leads to a little
-     * reduction overhead, but the speed gain in the bonded calculations
-     * of doing transposeScatterIncr/DecrU with aligment 4 instead of 3
-     * is much larger than the reduction overhead.
-     */
-    bonded_threading_t* bt = new bonded_threading_t(gmx_omp_nthreads_get(emntBonded), nenergrp);
 
     /* The optimal value after which to switch from uniform to localized
      * bonded interaction distribution is 3, 4 or 5 depending on the system
      * and hardware.
      */
-    const int max_nthread_uniform = 4;
+    const int max_nthread_uniform_default = 4;
     char*     ptr;
 
     if ((ptr = getenv("GMX_BONDED_NTHREAD_UNIFORM")) != nullptr)
     {
-        sscanf(ptr, "%d", &bt->max_nthread_uniform);
+        sscanf(ptr, "%d", &max_nthread_uniform);
         if (fplog != nullptr)
         {
             fprintf(fplog, "\nMax threads for uniform bonded distribution set to %d by env.var.\n",
-                    bt->max_nthread_uniform);
+                    max_nthread_uniform);
         }
     }
     else
     {
-        bt->max_nthread_uniform = max_nthread_uniform;
+        max_nthread_uniform = max_nthread_uniform_default;
     }
-
-    return bt;
 }
index 6639b32525a42a9e9bdbbd6bde6a070379850924..9f369fb35d3926ba99fc28c4b6fca08f05b56877 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares functions for managing threading of listed forces
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
- * \inlibraryapi
  * \ingroup module_listed_forces
  */
 #ifndef GMX_LISTED_FORCES_MANAGE_THREADING_H
@@ -47,7 +47,7 @@
 #include <cstdio>
 
 struct bonded_threading_t;
-struct t_idef;
+class InteractionDefinitions;
 
 /*! \brief Divide the listed interactions over the threads and GPU
  *
@@ -56,18 +56,9 @@ struct t_idef;
  * This should be called each time the bonded setup changes;
  * i.e. at start-up without domain decomposition and at DD.
  */
-void setup_bonded_threading(bonded_threading_t* bt, int numAtoms, bool useGpuForBondes, const t_idef& idef);
-
-//! Destructor.
-void tear_down_bonded_threading(bonded_threading_t* bt);
-
-/*! \brief Initialize the bonded threading data structures
- *
- * Allocates and initializes a bonded threading data structure.
- * A pointer to this struct is returned as \p *bb_ptr.
- *
- * \todo Avoid explicit pointers by using Impl
- */
-bonded_threading_t* init_bonded_threading(FILE* fplog, int nenergrp);
+void setup_bonded_threading(bonded_threading_t*           bt,
+                            int                           numAtomsForce,
+                            bool                          useGpuForBondeds,
+                            const InteractionDefinitions& idef);
 
 #endif
index 2867c07a15118d36784a3d64cccb30acbb2e41b2..aab79ed9727067f8eed0cdd93f6da3450e5435b4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/pbcutil/ishift.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/topology/topology.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/pleasecite.h"
 #include "gromacs/utility/smalloc.h"
 
+using gmx::ArrayRef;
+using gmx::RVec;
+
 // TODO This implementation of ensemble orientation restraints is nasty because
 // a user can't just do multi-sim with single-sim orientation restraints.
 
@@ -122,7 +126,8 @@ void init_orires(FILE*                 fplog,
     int                  nmol;
     while (const InteractionLists* il = gmx_mtop_ilistloop_next(iloop, &nmol))
     {
-        if (nmol > 1 && (*il)[F_ORIRES].size() > 0)
+        const int numOrires = (*il)[F_ORIRES].size();
+        if (nmol > 1 && numOrires > 0)
         {
             gmx_fatal(FARGS,
                       "Found %d copies of a molecule with orientation restrains while the current "
@@ -131,7 +136,7 @@ void init_orires(FILE*                 fplog,
                       nmol);
         }
 
-        for (int i = 0; i < (*il)[F_ORIRES].size(); i += 3)
+        for (int i = 0; i < numOrires; i += 3)
         {
             int type = (*il)[F_ORIRES].iatoms[i];
             int ex   = mtop->ffparams.iparams[type].orires.ex;
@@ -277,7 +282,8 @@ void init_orires(FILE*                 fplog,
 
     if (ms)
     {
-        fprintf(fplog, "  the orientation restraints are ensemble averaged over %d systems\n", ms->nsim);
+        fprintf(fplog, "  the orientation restraints are ensemble averaged over %d systems\n",
+                ms->numSimulations_);
 
         check_multi_int(fplog, ms, od->nr, "the number of orientation restraints", FALSE);
         check_multi_int(fplog, ms, od->nref, "the number of fit atoms for orientation restraining", FALSE);
@@ -380,22 +386,20 @@ real calc_orires_dev(const gmx_multisim_t* ms,
                      const t_iatom         forceatoms[],
                      const t_iparams       ip[],
                      const t_mdatoms*      md,
+                     ArrayRef<const RVec>  xWholeMolecules,
                      const rvec            x[],
                      const t_pbc*          pbc,
-                     t_fcdata*             fcd,
+                     t_oriresdata*         od,
                      history_t*            hist)
 {
-    int           nref;
-    real          edt, edt_1, invn, pfac, r2, invr, corrfac, wsv2, sw, dev;
-    OriresMatEq*  matEq;
-    real*         mref;
-    double        mtot;
-    rvec *        xref, *xtmp, com, r_unrot, r;
-    t_oriresdata* od;
-    gmx_bool      bTAV;
-    const real    two_thr = 2.0 / 3.0;
-
-    od = &(fcd->orires);
+    int          nref;
+    real         edt, edt_1, invn, pfac, r2, invr, corrfac, wsv2, sw, dev;
+    OriresMatEq* matEq;
+    real*        mref;
+    double       mtot;
+    rvec *       xref, *xtmp, com, r_unrot, r;
+    gmx_bool     bTAV;
+    const real   two_thr = 2.0 / 3.0;
 
     if (od->nr == 0)
     {
@@ -429,7 +433,7 @@ real calc_orires_dev(const gmx_multisim_t* ms,
 
     if (ms)
     {
-        invn = 1.0 / ms->nsim;
+        invn = 1.0 / ms->numSimulations_;
     }
     else
     {
@@ -443,7 +447,7 @@ real calc_orires_dev(const gmx_multisim_t* ms,
     {
         if (md->cORF[i] == 0)
         {
-            copy_rvec(x[i], xtmp[j]);
+            copy_rvec(xWholeMolecules[i], xtmp[j]);
             mref[j] = md->massT[i];
             for (int d = 0; d < DIM; d++)
             {
@@ -646,7 +650,6 @@ real orires(int             nfa,
             rvec4           f[],
             rvec            fshift[],
             const t_pbc*    pbc,
-            const t_graph*  g,
             real gmx_unused lambda,
             real gmx_unused* dvdlambda,
             const t_mdatoms gmx_unused* md,
@@ -654,7 +657,6 @@ real orires(int             nfa,
             int gmx_unused* global_atom_index)
 {
     int                 ex, power, ki = CENTRAL;
-    ivec                dt;
     real                r2, invr, invr2, fc, smooth_fc, dev, devins, pfac;
     rvec                r, Sr, fij;
     real                vtot;
@@ -662,7 +664,7 @@ real orires(int             nfa,
     gmx_bool            bTAV;
 
     vtot = 0;
-    od   = &(fcd->orires);
+    od   = fcd->orires;
 
     if (od->fc != 0)
     {
@@ -731,12 +733,6 @@ real orires(int             nfa,
                 fij[i] = -pfac * dev * (4 * Sr[i] - 2 * (2 + power) * invr2 * iprod(Sr, r) * r[i]);
             }
 
-            if (g)
-            {
-                ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
-                ki = IVEC2IS(dt);
-            }
-
             for (int i = 0; i < DIM; i++)
             {
                 f[ai][i] += fij[i];
@@ -755,21 +751,19 @@ real orires(int             nfa,
     /* Approx. 80*nfa/3 flops */
 }
 
-void update_orires_history(const t_fcdata* fcd, history_t* hist)
+void update_orires_history(const t_oriresdata& od, history_t* hist)
 {
-    const t_oriresdata* od = &(fcd->orires);
-
-    if (od->edt != 0)
+    if (od.edt != 0)
     {
         /* Copy the new time averages that have been calculated
          *  in calc_orires_dev.
          */
-        hist->orire_initf = od->exp_min_t_tau;
-        for (int pair = 0; pair < od->nr; pair++)
+        hist->orire_initf = od.exp_min_t_tau;
+        for (int pair = 0; pair < od.nr; pair++)
         {
             for (int i = 0; i < 5; i++)
             {
-                hist->orire_Dtav[pair * 5 + i] = od->Dtav[pair][i];
+                hist->orire_Dtav[pair * 5 + i] = od.Dtav[pair][i];
             }
         }
     }
index 96cf3c53bd5bdf1b9fa0922d3c0f2b0d435a42a3..37065e8be559164e7f12ac6204bc5e3322edde0e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -54,10 +55,15 @@ class history_t;
 struct t_inputrec;
 struct t_pbc;
 struct t_commrec;
-struct t_fcdata;
 struct t_oriresdata;
 class t_state;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
 /*! \brief
  * Decides whether orientation restraints can work, and initializes
  * all the orientation restraint stuff in *od (and assumes *od is
@@ -79,15 +85,16 @@ void init_orires(FILE*                 fplog,
  *
  * Returns the weighted RMS deviation of the orientation restraints.
  */
-real calc_orires_dev(const gmx_multisim_t* ms,
-                     int                   nfa,
-                     const t_iatom         fa[],
-                     const t_iparams       ip[],
-                     const t_mdatoms*      md,
-                     const rvec            x[],
-                     const t_pbc*          pbc,
-                     t_fcdata*             fcd,
-                     history_t*            hist);
+real calc_orires_dev(const gmx_multisim_t*          ms,
+                     int                            nfa,
+                     const t_iatom                  fa[],
+                     const t_iparams                ip[],
+                     const t_mdatoms*               md,
+                     gmx::ArrayRef<const gmx::RVec> xWholeMolecules,
+                     const rvec                     x[],
+                     const t_pbc*                   pbc,
+                     t_oriresdata*                  oriresdata,
+                     history_t*                     hist);
 
 /*! \brief
  * Diagonalizes the order tensor(s) of the orienation restraints.
@@ -108,7 +115,6 @@ real orires(int              nfa,
             rvec4            f[],
             rvec             fshift[],
             const t_pbc*     pbc,
-            const t_graph*   g,
             real             lambda,
             real*            dvdlambda,
             const t_mdatoms* md,
@@ -116,6 +122,6 @@ real orires(int              nfa,
             int*             global_atom_index);
 
 //! Copies the new time averages that have been calculated in calc_orires_dev.
-void update_orires_history(const t_fcdata* fcd, history_t* hist);
+void update_orires_history(const t_oriresdata& oriresdata, history_t* hist);
 
 #endif
index d56a6aa546eff194730d1afd8565fb4b9377bc1c..1d0393fb71147a6b1fbd779208e8580af4414d9c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/listed_forces/bonded.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/group.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/nblist.h"
 #include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/pbcutil/ishift.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/pbc_simd.h"
 #include "gromacs/simd/simd.h"
@@ -151,48 +154,44 @@ static real evaluate_single(real        r2,
     return fscal;
 }
 
+static inline real sixthRoot(const real r)
+{
+    return gmx::invsqrt(std::cbrt(r));
+}
+
 /*! \brief Compute the energy and force for a single pair interaction under FEP */
-static real free_energy_evaluate_single(real        r2,
-                                        real        sc_r_power,
-                                        real        alpha_coul,
-                                        real        alpha_vdw,
-                                        real        tabscale,
-                                        const real* vftab,
-                                        real        tableStride,
-                                        real        qqA,
-                                        real        c6A,
-                                        real        c12A,
-                                        real        qqB,
-                                        real        c6B,
-                                        real        c12B,
-                                        const real  LFC[2],
-                                        const real  LFV[2],
-                                        const real  DLF[2],
-                                        const real  lfac_coul[2],
-                                        const real  lfac_vdw[2],
-                                        const real  dlfac_coul[2],
-                                        const real  dlfac_vdw[2],
-                                        real        sigma6_def,
-                                        real        sigma6_min,
-                                        real        sigma2_def,
-                                        real        sigma2_min,
-                                        real*       velectot,
-                                        real*       vvdwtot,
-                                        real*       dvdl)
+static real free_energy_evaluate_single(real                                           r2,
+                                        const interaction_const_t::SoftCoreParameters& scParams,
+                                        real                                           tabscale,
+                                        const real*                                    vftab,
+                                        real                                           tableStride,
+                                        real                                           qqA,
+                                        real                                           c6A,
+                                        real                                           c12A,
+                                        real                                           qqB,
+                                        real                                           c6B,
+                                        real                                           c12B,
+                                        const real                                     LFC[2],
+                                        const real                                     LFV[2],
+                                        const real                                     DLF[2],
+                                        const real                                     lfac_coul[2],
+                                        const real                                     lfac_vdw[2],
+                                        const real dlfac_coul[2],
+                                        const real dlfac_vdw[2],
+                                        real*      velectot,
+                                        real*      vvdwtot,
+                                        real*      dvdl)
 {
-    real       rp, rpm2, rtab, eps, eps2, Y, F, Geps, Heps2, Fp, VV, FF, fscal;
-    real       qq[2], c6[2], c12[2], sigma6[2], sigma2[2], sigma_pow[2];
+    real       rtab, eps, eps2, Y, F, Geps, Heps2, Fp, VV, FF, fscal;
+    real       qq[2], c6[2], c12[2], sigma6[2], sigma_pow[2];
     real       alpha_coul_eff, alpha_vdw_eff, dvdl_coul, dvdl_vdw;
     real       rpinv, r_coul, r_vdw, velecsum, vvdwsum;
     real       fscal_vdw[2], fscal_elec[2];
     real       velec[2], vvdw[2];
     int        i, ntab;
-    const real half        = 0.5;
-    const real minusOne    = -1.0;
-    const real one         = 1.0;
-    const real two         = 2.0;
-    const real six         = 6.0;
-    const real fourtyeight = 48.0;
+    const real half = 0.5_real;
+    const real one  = 1.0_real;
+    const real two  = 2.0_real;
 
     qq[0]  = qqA;
     qq[1]  = qqB;
@@ -201,24 +200,8 @@ static real free_energy_evaluate_single(real        r2,
     c12[0] = c12A;
     c12[1] = c12B;
 
-    if (sc_r_power == six)
-    {
-        rpm2 = r2 * r2;   /* r4 */
-        rp   = rpm2 * r2; /* r6 */
-    }
-    else if (sc_r_power == fourtyeight)
-    {
-        rp   = r2 * r2 * r2; /* r6 */
-        rp   = rp * rp;      /* r12 */
-        rp   = rp * rp;      /* r24 */
-        rp   = rp * rp;      /* r48 */
-        rpm2 = rp / r2;      /* r46 */
-    }
-    else
-    {
-        rp = std::pow(r2, half * sc_r_power); /* not currently supported as input, but can handle it */
-        rpm2 = rp / r2;
-    }
+    const real rpm2 = r2 * r2;   /* r4 */
+    const real rp   = rpm2 * r2; /* r6 */
 
     /* Loop over state A(0) and B(1) */
     for (i = 0; i < 2; i++)
@@ -229,34 +212,16 @@ static real free_energy_evaluate_single(real        r2,
              * Correct for this by multiplying with (1/12.0)/(1/6.0)=6.0/12.0=0.5.
              */
             sigma6[i] = half * c12[i] / c6[i];
-            sigma2[i] = std::cbrt(half * c12[i] / c6[i]);
-            /* should be able to get rid of this ^^^ internal pow call eventually.  Will require agreement on
-               what data to store externally.  Can't be fixed without larger scale changes, so not 5.0 */
-            if (sigma6[i] < sigma6_min) /* for disappearing coul and vdw with soft core at the same time */
+            if (sigma6[i] < scParams.sigma6Minimum) /* for disappearing coul and vdw with soft core at the same time */
             {
-                sigma6[i] = sigma6_min;
-                sigma2[i] = sigma2_min;
+                sigma6[i] = scParams.sigma6Minimum;
             }
         }
         else
         {
-            sigma6[i] = sigma6_def;
-            sigma2[i] = sigma2_def;
-        }
-        if (sc_r_power == six)
-        {
-            sigma_pow[i] = sigma6[i];
-        }
-        else if (sc_r_power == fourtyeight)
-        {
-            sigma_pow[i] = sigma6[i] * sigma6[i];       /* sigma^12 */
-            sigma_pow[i] = sigma_pow[i] * sigma_pow[i]; /* sigma^24 */
-            sigma_pow[i] = sigma_pow[i] * sigma_pow[i]; /* sigma^48 */
-        }
-        else
-        { /* not really supported as input, but in here for testing the general case*/
-            sigma_pow[i] = std::pow(sigma2[i], sc_r_power / 2);
+            sigma6[i] = scParams.sigma6WithInvalidSigma;
         }
+        sigma_pow[i] = sigma6[i];
     }
 
     /* only use softcore if one of the states has a zero endstate - softcore is for avoiding infinities!*/
@@ -267,8 +232,8 @@ static real free_energy_evaluate_single(real        r2,
     }
     else
     {
-        alpha_vdw_eff  = alpha_vdw;
-        alpha_coul_eff = alpha_coul;
+        alpha_vdw_eff  = scParams.alphaVdw;
+        alpha_coul_eff = scParams.alphaCoulomb;
     }
 
     /* Loop over A and B states again */
@@ -284,7 +249,7 @@ static real free_energy_evaluate_single(real        r2,
         {
             /* Coulomb */
             rpinv  = one / (alpha_coul_eff * lfac_coul[i] * sigma_pow[i] + rp);
-            r_coul = std::pow(rpinv, minusOne / sc_r_power);
+            r_coul = sixthRoot(rpinv);
 
             /* Electrostatics table lookup data */
             rtab = r_coul * tabscale;
@@ -305,7 +270,7 @@ static real free_energy_evaluate_single(real        r2,
 
             /* Vdw */
             rpinv = one / (alpha_vdw_eff * lfac_vdw[i] * sigma_pow[i] + rp);
-            r_vdw = std::pow(rpinv, minusOne / sc_r_power);
+            r_vdw = sixthRoot(rpinv);
             /* Vdw table lookup data */
             rtab = r_vdw * tabscale;
             ntab = static_cast<int>(rtab);
@@ -367,25 +332,23 @@ static real free_energy_evaluate_single(real        r2,
 
 /*! \brief Calculate pair interactions, supports all types and conditions. */
 template<BondedKernelFlavor flavor>
-static real do_pairs_general(int                   ftype,
-                             int                   nbonds,
-                             const t_iatom         iatoms[],
-                             const t_iparams       iparams[],
-                             const rvec            x[],
-                             rvec4                 f[],
-                             rvec                  fshift[],
-                             const struct t_pbc*   pbc,
-                             const struct t_graph* g,
-                             const real*           lambda,
-                             real*                 dvdl,
-                             const t_mdatoms*      md,
-                             const t_forcerec*     fr,
-                             gmx_grppairener_t*    grppener,
-                             int*                  global_atom_index)
+static real do_pairs_general(int                 ftype,
+                             int                 nbonds,
+                             const t_iatom       iatoms[],
+                             const t_iparams     iparams[],
+                             const rvec          x[],
+                             rvec4               f[],
+                             rvec                fshift[],
+                             const struct t_pbc* pbc,
+                             const real*         lambda,
+                             real*               dvdl,
+                             const t_mdatoms*    md,
+                             const t_forcerec*   fr,
+                             gmx_grppairener_t*  grppener,
+                             int*                global_atom_index)
 {
     real            qq, c6, c12;
     rvec            dx;
-    ivec            dt;
     int             i, itype, ai, aj, gid;
     int             fshift_index;
     real            r2;
@@ -394,9 +357,10 @@ static real do_pairs_general(int                   ftype,
     real*           energygrp_vdw;
     static gmx_bool warned_rlimit = FALSE;
     /* Free energy stuff */
-    gmx_bool bFreeEnergy;
-    real     LFC[2], LFV[2], DLF[2], lfac_coul[2], lfac_vdw[2], dlfac_coul[2], dlfac_vdw[2];
-    real     qqB, c6B, c12B, sigma2_def, sigma2_min;
+    gmx_bool   bFreeEnergy;
+    real       LFC[2], LFV[2], DLF[2], lfac_coul[2], lfac_vdw[2], dlfac_coul[2], dlfac_vdw[2];
+    real       qqB, c6B, c12B;
+    const real oneSixth = 1.0_real / 6.0_real;
 
     switch (ftype)
     {
@@ -427,24 +391,19 @@ static real do_pairs_general(int                   ftype,
         DLF[0] = -1;
         DLF[1] = 1;
 
-        /* precalculate */
-        sigma2_def = std::cbrt(fr->sc_sigma6_def);
-        sigma2_min = std::cbrt(fr->sc_sigma6_min);
+        GMX_ASSERT(fr->ic->softCoreParameters, "We need soft-core parameters");
+        const auto& scParams = *fr->ic->softCoreParameters;
 
         for (i = 0; i < 2; i++)
         {
-            lfac_coul[i] = (fr->sc_power == 2 ? (1 - LFC[i]) * (1 - LFC[i]) : (1 - LFC[i]));
-            dlfac_coul[i] =
-                    DLF[i] * fr->sc_power / fr->sc_r_power * (fr->sc_power == 2 ? (1 - LFC[i]) : 1);
-            lfac_vdw[i] = (fr->sc_power == 2 ? (1 - LFV[i]) * (1 - LFV[i]) : (1 - LFV[i]));
-            dlfac_vdw[i] =
-                    DLF[i] * fr->sc_power / fr->sc_r_power * (fr->sc_power == 2 ? (1 - LFV[i]) : 1);
+            lfac_coul[i] = (scParams.lambdaPower == 2 ? (1 - LFC[i]) * (1 - LFC[i]) : (1 - LFC[i]));
+            dlfac_coul[i] = DLF[i] * scParams.lambdaPower * oneSixth
+                            * (scParams.lambdaPower == 2 ? (1 - LFC[i]) : 1);
+            lfac_vdw[i]  = (scParams.lambdaPower == 2 ? (1 - LFV[i]) * (1 - LFV[i]) : (1 - LFV[i]));
+            dlfac_vdw[i] = DLF[i] * scParams.lambdaPower * oneSixth
+                           * (scParams.lambdaPower == 2 ? (1 - LFV[i]) : 1);
         }
     }
-    else
-    {
-        sigma2_min = sigma2_def = 0;
-    }
 
     /* TODO This code depends on the logic in tables.c that constructs
        the table layout, which should be made explicit in future
@@ -535,15 +494,14 @@ static real do_pairs_general(int                   ftype,
             c12B = iparams[itype].lj14.c12B * 12.0;
 
             fscal = free_energy_evaluate_single(
-                    r2, fr->sc_r_power, fr->sc_alphacoul, fr->sc_alphavdw, fr->pairsTable->scale,
-                    fr->pairsTable->data, fr->pairsTable->stride, qq, c6, c12, qqB, c6B, c12B, LFC,
-                    LFV, DLF, lfac_coul, lfac_vdw, dlfac_coul, dlfac_vdw, fr->sc_sigma6_def,
-                    fr->sc_sigma6_min, sigma2_def, sigma2_min, &velec, &vvdw, dvdl);
+                    r2, *fr->ic->softCoreParameters, fr->pairsTable->scale,
+                    fr->pairsTable->data.data(), fr->pairsTable->stride, qq, c6, c12, qqB, c6B, c12B,
+                    LFC, LFV, DLF, lfac_coul, lfac_vdw, dlfac_coul, dlfac_vdw, &velec, &vvdw, dvdl);
         }
         else
         {
             /* Evaluate tabulated interaction without free energy */
-            fscal = evaluate_single(r2, fr->pairsTable->scale, fr->pairsTable->data,
+            fscal = evaluate_single(r2, fr->pairsTable->scale, fr->pairsTable->data.data(),
                                     fr->pairsTable->stride, qq, c6, c12, &velec, &vvdw);
         }
 
@@ -557,12 +515,6 @@ static real do_pairs_general(int                   ftype,
 
         if (computeVirial(flavor))
         {
-            if (g)
-            {
-                /* Correct the shift forces using the graph */
-                ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
-                fshift_index = IVEC2IS(dt);
-            }
             if (fshift_index != CENTRAL)
             {
                 rvec_inc(fshift[fshift_index], dx);
@@ -689,7 +641,6 @@ void do_pairs(int                      ftype,
               rvec4                    f[],
               rvec                     fshift[],
               const struct t_pbc*      pbc,
-              const struct t_graph*    g,
               const real*              lambda,
               real*                    dvdl,
               const t_mdatoms*         md,
@@ -732,7 +683,7 @@ void do_pairs(int                      ftype,
             }
             else
             {
-                set_pbc(&pbc_no, epbcNONE, nullptr);
+                set_pbc(&pbc_no, PbcType::No, nullptr);
                 pbc_nonnull = &pbc_no;
             }
 
@@ -743,13 +694,13 @@ void do_pairs(int                      ftype,
     else if (stepWork.computeVirial)
     {
         do_pairs_general<BondedKernelFlavor::ForcesAndVirialAndEnergy>(
-                ftype, nbonds, iatoms, iparams, x, f, fshift, pbc, g, lambda, dvdl, md, fr,
-                grppener, global_atom_index);
+                ftype, nbonds, iatoms, iparams, x, f, fshift, pbc, lambda, dvdl, md, fr, grppener,
+                global_atom_index);
     }
     else
     {
         do_pairs_general<BondedKernelFlavor::ForcesAndEnergy>(ftype, nbonds, iatoms, iparams, x, f,
-                                                              fshift, pbc, g, lambda, dvdl, md, fr,
+                                                              fshift, pbc, lambda, dvdl, md, fr,
                                                               grppener, global_atom_index);
     }
 }
index e6760b78613c3605646db6b45451dda535d7b9dd..af9320e014af230b454861088594b11455fe788a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_LISTED_FORCES_PAIRS_H
 
 #include "gromacs/math/vec.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
 struct gmx_grppairener_t;
 struct t_forcerec;
-struct t_graph;
 struct t_pbc;
 
 namespace gmx
@@ -73,7 +72,6 @@ void do_pairs(int                      ftype,
               rvec4                    f[],
               rvec                     fshift[],
               const struct t_pbc*      pbc,
-              const struct t_graph*    g,
               const real*              lambda,
               real*                    dvdl,
               const t_mdatoms*         md,
index 939d84339698f0754f04900ce4cbc6bfac5dd322..5b12681b79b5215dd664f9b63361be6fa16d8047 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -194,7 +195,7 @@ real fbposres(int                   nbonds,
               gmx::ForceWithVirial* forceWithVirial,
               const t_pbc*          pbc,
               int                   refcoord_scaling,
-              int                   ePBC,
+              PbcType               pbcType,
               const rvec            com)
 /* compute flat-bottomed positions restraints */
 {
@@ -205,8 +206,8 @@ real fbposres(int                   nbonds,
     rvec             com_sc, rdist, dx, dpdl, fm;
     gmx_bool         bInvert;
 
-    npbcdim = ePBC2npbcdim(ePBC);
-    GMX_ASSERT((ePBC == epbcNONE) == (npbcdim == 0), "");
+    npbcdim = numPbcDimensions(pbcType);
+    GMX_ASSERT((pbcType == PbcType::No) == (npbcdim == 0), "");
     if (refcoord_scaling == erscCOM)
     {
         clear_rvec(com_sc);
@@ -327,7 +328,7 @@ real posres(int                   nbonds,
             real                  lambda,
             real*                 dvdlambda,
             int                   refcoord_scaling,
-            int                   ePBC,
+            PbcType               pbcType,
             const rvec            comA,
             const rvec            comB)
 {
@@ -336,8 +337,8 @@ real posres(int                   nbonds,
     real             kk, fm;
     rvec             comA_sc, comB_sc, rdist, dpdl, dx;
 
-    npbcdim = ePBC2npbcdim(ePBC);
-    GMX_ASSERT((ePBC == epbcNONE) == (npbcdim == 0), "");
+    npbcdim = numPbcDimensions(pbcType);
+    GMX_ASSERT((pbcType == PbcType::No) == (npbcdim == 0), "");
     if (refcoord_scaling == erscCOM)
     {
         clear_rvec(comA_sc);
@@ -400,74 +401,72 @@ real posres(int                   nbonds,
 
 } // namespace
 
-void posres_wrapper(t_nrnb*               nrnb,
-                    const t_idef*         idef,
-                    const struct t_pbc*   pbc,
-                    const rvec*           x,
-                    gmx_enerdata_t*       enerd,
-                    const real*           lambda,
-                    const t_forcerec*     fr,
-                    gmx::ForceWithVirial* forceWithVirial)
+void posres_wrapper(t_nrnb*                       nrnb,
+                    const InteractionDefinitions& idef,
+                    const struct t_pbc*           pbc,
+                    const rvec*                   x,
+                    gmx_enerdata_t*               enerd,
+                    const real*                   lambda,
+                    const t_forcerec*             fr,
+                    gmx::ForceWithVirial*         forceWithVirial)
 {
     real v, dvdl;
 
     dvdl = 0;
-    v    = posres<true>(idef->il[F_POSRES].nr, idef->il[F_POSRES].iatoms, idef->iparams_posres, x,
-                     forceWithVirial, fr->ePBC == epbcNONE ? nullptr : pbc, lambda[efptRESTRAINT],
-                     &dvdl, fr->rc_scaling, fr->ePBC, fr->posres_com, fr->posres_comB);
+    v    = posres<true>(idef.il[F_POSRES].size(), idef.il[F_POSRES].iatoms.data(),
+                     idef.iparams_posres.data(), x, forceWithVirial,
+                     fr->pbcType == PbcType::No ? nullptr : pbc, lambda[efptRESTRAINT], &dvdl,
+                     fr->rc_scaling, fr->pbcType, fr->posres_com, fr->posres_comB);
     enerd->term[F_POSRES] += v;
     /* If just the force constant changes, the FEP term is linear,
      * but if k changes, it is not.
      */
     enerd->dvdl_nonlin[efptRESTRAINT] += dvdl;
-    inc_nrnb(nrnb, eNR_POSRES, gmx::exactDiv(idef->il[F_POSRES].nr, 2));
+    inc_nrnb(nrnb, eNR_POSRES, gmx::exactDiv(idef.il[F_POSRES].size(), 2));
 }
 
-void posres_wrapper_lambda(struct gmx_wallcycle* wcycle,
-                           const t_lambda*       fepvals,
-                           const t_idef*         idef,
-                           const struct t_pbc*   pbc,
-                           const rvec            x[],
-                           gmx_enerdata_t*       enerd,
-                           const real*           lambda,
-                           const t_forcerec*     fr)
+void posres_wrapper_lambda(struct gmx_wallcycle*         wcycle,
+                           const t_lambda*               fepvals,
+                           const InteractionDefinitions& idef,
+                           const struct t_pbc*           pbc,
+                           const rvec                    x[],
+                           gmx_enerdata_t*               enerd,
+                           const real*                   lambda,
+                           const t_forcerec*             fr)
 {
-    real v;
-
-    if (0 == idef->il[F_POSRES].nr)
-    {
-        return;
-    }
-
     wallcycle_sub_start_nocount(wcycle, ewcsRESTRAINTS);
-    for (size_t i = 0; i < enerd->enerpart_lambda.size(); i++)
-    {
-        real dvdl_dum = 0, lambda_dum;
 
-        lambda_dum = (i == 0 ? lambda[efptRESTRAINT] : fepvals->all_lambda[efptRESTRAINT][i - 1]);
-        v = posres<false>(idef->il[F_POSRES].nr, idef->il[F_POSRES].iatoms, idef->iparams_posres, x,
-                          nullptr, fr->ePBC == epbcNONE ? nullptr : pbc, lambda_dum, &dvdl_dum,
-                          fr->rc_scaling, fr->ePBC, fr->posres_com, fr->posres_comB);
-        enerd->enerpart_lambda[i] += v;
+    auto& foreignTerms = enerd->foreignLambdaTerms;
+    for (int i = 0; i < 1 + foreignTerms.numLambdas(); i++)
+    {
+        real dvdl = 0;
+
+        const real lambda_dum =
+                (i == 0 ? lambda[efptRESTRAINT] : fepvals->all_lambda[efptRESTRAINT][i - 1]);
+        const real v = posres<false>(idef.il[F_POSRES].size(), idef.il[F_POSRES].iatoms.data(),
+                                     idef.iparams_posres.data(), x, nullptr,
+                                     fr->pbcType == PbcType::No ? nullptr : pbc, lambda_dum, &dvdl,
+                                     fr->rc_scaling, fr->pbcType, fr->posres_com, fr->posres_comB);
+        foreignTerms.accumulate(i, v, dvdl);
     }
     wallcycle_sub_stop(wcycle, ewcsRESTRAINTS);
 }
 
 /*! \brief Helper function that wraps calls to fbposres for
     free-energy perturbation */
-void fbposres_wrapper(t_nrnb*               nrnb,
-                      const t_idef*         idef,
-                      const struct t_pbc*   pbc,
-                      const rvec*           x,
-                      gmx_enerdata_t*       enerd,
-                      const t_forcerec*     fr,
-                      gmx::ForceWithVirial* forceWithVirial)
+void fbposres_wrapper(t_nrnb*                       nrnb,
+                      const InteractionDefinitions& idef,
+                      const struct t_pbc*           pbc,
+                      const rvec*                   x,
+                      gmx_enerdata_t*               enerd,
+                      const t_forcerec*             fr,
+                      gmx::ForceWithVirial*         forceWithVirial)
 {
     real v;
 
-    v = fbposres(idef->il[F_FBPOSRES].nr, idef->il[F_FBPOSRES].iatoms, idef->iparams_fbposres, x,
-                 forceWithVirial, fr->ePBC == epbcNONE ? nullptr : pbc, fr->rc_scaling, fr->ePBC,
-                 fr->posres_com);
+    v = fbposres(idef.il[F_FBPOSRES].size(), idef.il[F_FBPOSRES].iatoms.data(),
+                 idef.iparams_fbposres.data(), x, forceWithVirial,
+                 fr->pbcType == PbcType::No ? nullptr : pbc, fr->rc_scaling, fr->pbcType, fr->posres_com);
     enerd->term[F_FBPOSRES] += v;
-    inc_nrnb(nrnb, eNR_FBPOSRES, gmx::exactDiv(idef->il[F_FBPOSRES].nr, 2));
+    inc_nrnb(nrnb, eNR_FBPOSRES, gmx::exactDiv(idef.il[F_FBPOSRES].size(), 2));
 }
index fe9eb6cb1950375688fd62cd2b232e14eda13ae9..5d3148fb33aac3e5c986661c9dd188745c629f72 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -56,7 +56,7 @@
 struct gmx_enerdata_t;
 struct gmx_wallcycle;
 struct t_forcerec;
-struct t_idef;
+class InteractionDefinitions;
 struct t_lambda;
 struct t_nrnb;
 struct t_pbc;
@@ -67,34 +67,34 @@ class ForceWithVirial;
 }
 
 /*! \brief Helper function that wraps calls to posres */
-void posres_wrapper(t_nrnb*               nrnb,
-                    const t_idef*         idef,
-                    const struct t_pbc*   pbc,
-                    const rvec*           x,
-                    gmx_enerdata_t*       enerd,
-                    const real*           lambda,
-                    const t_forcerec*     fr,
-                    gmx::ForceWithVirial* forceWithVirial);
+void posres_wrapper(t_nrnb*                       nrnb,
+                    const InteractionDefinitions& idef,
+                    const struct t_pbc*           pbc,
+                    const rvec*                   x,
+                    gmx_enerdata_t*               enerd,
+                    const real*                   lambda,
+                    const t_forcerec*             fr,
+                    gmx::ForceWithVirial*         forceWithVirial);
 
 /*! \brief Helper function that wraps calls to posres for free-energy
     pertubation */
-void posres_wrapper_lambda(struct gmx_wallcycle* wcycle,
-                           const t_lambda*       fepvals,
-                           const t_idef*         idef,
-                           const struct t_pbc*   pbc,
-                           const rvec            x[],
-                           gmx_enerdata_t*       enerd,
-                           const real*           lambda,
-                           const t_forcerec*     fr);
+void posres_wrapper_lambda(struct gmx_wallcycle*         wcycle,
+                           const t_lambda*               fepvals,
+                           const InteractionDefinitions& idef,
+                           const struct t_pbc*           pbc,
+                           const rvec                    x[],
+                           gmx_enerdata_t*               enerd,
+                           const real*                   lambda,
+                           const t_forcerec*             fr);
 
 /*! \brief Helper function that wraps calls to fbposres for
     free-energy perturbation */
-void fbposres_wrapper(t_nrnb*               nrnb,
-                      const t_idef*         idef,
-                      const struct t_pbc*   pbc,
-                      const rvec*           x,
-                      gmx_enerdata_t*       enerd,
-                      const t_forcerec*     fr,
-                      gmx::ForceWithVirial* forceWithVirial);
+void fbposres_wrapper(t_nrnb*                       nrnb,
+                      const InteractionDefinitions& idef,
+                      const struct t_pbc*           pbc,
+                      const rvec*                   x,
+                      gmx_enerdata_t*               enerd,
+                      const t_forcerec*             fr,
+                      gmx::ForceWithVirial*         forceWithVirial);
 
 #endif
index e987e69a4b8281460b2f3092b0d62efef939c00f..ffe786b4b24c6bdfca4012a73d6a37d1b30bec86 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2016,2019, by the GROMACS development team, led by
+# Copyright (c) 2016,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,5 +33,7 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(ListedForcesTest listed_forces-test
-  bonded.cpp)
+    CPP_SOURCE_FILES
+        bonded.cpp
+        )
 
index 4447cced8c34eaa9f2950ad5a5fa9e8d4f4ad5e5..3ab4b4fd6659a66138d1ab9b6b9f9f81fd0ee843 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -51,6 +51,7 @@
 #include <gtest/gtest.h>
 
 #include "gromacs/listed_forces/listed_forces.h"
+#include "gromacs/math/paddedvector.h"
 #include "gromacs/math/units.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/math/vectypes.h"
@@ -85,20 +86,29 @@ struct OutputQuantities
     //! Shift vectors
     rvec fshift[N_IVEC] = { { 0 } };
     //! Forces
-    rvec4 f[c_numAtoms] = { { 0 } };
+    alignas(GMX_REAL_MAX_SIMD_WIDTH * sizeof(real)) rvec4 f[c_numAtoms] = { { 0 } };
 };
 
 /*! \brief Utility to check the output from bonded tests
  *
  * \param[in] checker Reference checker
  * \param[in] output  The output from the test to check
+ * \param[in] bondedKernelFlavor  Flavor for determining what output to check
  */
-void checkOutput(test::TestReferenceChecker* checker, const OutputQuantities& output)
+void checkOutput(test::TestReferenceChecker* checker,
+                 const OutputQuantities&     output,
+                 const BondedKernelFlavor    bondedKernelFlavor)
 {
-    checker->checkReal(output.energy, "Epot ");
-    // Should still be zero if not doing FEP, so may as well test it.
-    checker->checkReal(output.dvdlambda, "dVdlambda ");
-    checker->checkVector(output.fshift[CENTRAL], "Central shift forces");
+    if (computeEnergy(bondedKernelFlavor))
+    {
+        checker->checkReal(output.energy, "Epot ");
+        // Should still be zero when not doing FEP, so may as well test it.
+        checker->checkReal(output.dvdlambda, "dVdlambda ");
+    }
+    if (computeVirial(bondedKernelFlavor))
+    {
+        checker->checkVector(output.fshift[CENTRAL], "Central shift forces");
+    }
     checker->checkSequence(std::begin(output.f), std::end(output.f), "Forces");
 }
 
@@ -514,24 +524,24 @@ void fillIatoms(int ftype, std::vector<t_iatom>* iatoms)
 }
 
 class ListedForcesTest :
-    public ::testing::TestWithParam<std::tuple<iListInput, std::vector<gmx::RVec>, int>>
+    public ::testing::TestWithParam<std::tuple<iListInput, PaddedVector<RVec>, PbcType>>
 {
 protected:
     matrix                     box_;
     t_pbc                      pbc_;
-    std::vector<gmx::RVec>     x_;
-    int                        epbc_;
+    PaddedVector<RVec>         x_;
+    PbcType                    pbcType_;
     iListInput                 input_;
     test::TestReferenceData    refData_;
     test::TestReferenceChecker checker_;
     ListedForcesTest() : checker_(refData_.rootChecker())
     {
-        input_ = std::get<0>(GetParam());
-        x_     = std::get<1>(GetParam());
-        epbc_  = std::get<2>(GetParam());
+        input_   = std::get<0>(GetParam());
+        x_       = std::get<1>(GetParam());
+        pbcType_ = std::get<2>(GetParam());
         clear_mat(box_);
         box_[0][0] = box_[1][1] = box_[2][2] = 1.5;
-        set_pbc(&pbc_, epbc_, box_);
+        set_pbc(&pbc_, pbcType_, box_);
         // We need quite specific tolerances here since angle functions
         // etc. are not very precise and reproducible.
         test::FloatingPointTolerance tolerance(test::FloatingPointTolerance(
@@ -540,22 +550,34 @@ protected:
     }
     void testOneIfunc(test::TestReferenceChecker* checker, const std::vector<t_iatom>& iatoms, const real lambda)
     {
-        SCOPED_TRACE(std::string("Testing PBC ") + epbc_names[epbc_]);
+        SCOPED_TRACE(std::string("Testing PBC ") + c_pbcTypeNames[pbcType_]);
         std::vector<int>  ddgatindex = { 0, 1, 2, 3 };
         std::vector<real> chargeA    = { 1.5, -2.0, 1.5, -1.0 };
         t_mdatoms         mdatoms    = { 0 };
         mdatoms.chargeA              = chargeA.data();
-        OutputQuantities output;
-        output.energy = calculateSimpleBond(
-                input_.ftype, iatoms.size(), iatoms.data(), &input_.iparams,
-                as_rvec_array(x_.data()), output.f, output.fshift, &pbc_,
-                /* const struct t_graph *g */ nullptr, lambda, &output.dvdlambda, &mdatoms,
-                /* struct t_fcdata * */ nullptr, ddgatindex.data(),
-                BondedKernelFlavor::ForcesAndVirialAndEnergy);
-        // Internal consistency test of both test input
-        // and bonded functions.
-        EXPECT_TRUE((input_.fep || (output.dvdlambda == 0.0))) << "dvdlambda was " << output.dvdlambda;
-        checkOutput(checker, output);
+        /* Here we run both the standard, plain-C force+shift-forcs+energy+free-energy
+         * kernel flavor and the potentially optimized, with SIMD and less output,
+         * force only kernels. Note that we also run the optimized kernel for free-energy
+         * input when lambda=0, as the force output should match the non free-energy case.
+         */
+        std::vector<BondedKernelFlavor> flavors = { BondedKernelFlavor::ForcesAndVirialAndEnergy };
+        if (!input_.fep || lambda == 0)
+        {
+            flavors.push_back(BondedKernelFlavor::ForcesSimdWhenAvailable);
+        }
+        for (const auto flavor : flavors)
+        {
+            OutputQuantities output;
+            output.energy =
+                    calculateSimpleBond(input_.ftype, iatoms.size(), iatoms.data(), &input_.iparams,
+                                        as_rvec_array(x_.data()), output.f, output.fshift, &pbc_,
+                                        lambda, &output.dvdlambda, &mdatoms,
+                                        /* struct t_fcdata * */ nullptr, ddgatindex.data(), flavor);
+            // Internal consistency test of both test input
+            // and bonded functions.
+            EXPECT_TRUE((input_.fep || (output.dvdlambda == 0.0))) << "dvdlambda was " << output.dvdlambda;
+            checkOutput(checker, output, flavor);
+        }
     }
     void testIfunc()
     {
@@ -653,15 +675,35 @@ std::vector<iListInput> c_InputRestraints = {
     { iListInput(2e-3, 1e-8).setHarmonic(F_RESTRANGLES, 100.0, 50.0, 110.0, 45.0) }
 };
 
+//! Function types for testing bond with zero length, has zero reference length to make physical sense.
+std::vector<iListInput> c_InputBondsZeroLength = {
+    { iListInput().setHarmonic(F_BONDS, 0.0, 500.0) },
+};
+
+//! Function types for testing angles with zero angle, has zero reference angle to make physical sense.
+std::vector<iListInput> c_InputAnglesZeroAngle = {
+    { iListInput(2e-3, 1e-8).setHarmonic(F_ANGLES, 0.0, 50.0) },
+};
+
 //! Coordinates for testing
-std::vector<std::vector<gmx::RVec>> c_coordinatesForTests = {
+std::vector<PaddedVector<RVec>> c_coordinatesForTests = {
     { { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.2 }, { 0.005, 0.0, 0.1 }, { -0.001, 0.1, 0.0 } },
     { { 0.5, 0.0, 0.0 }, { 0.5, 0.0, 0.15 }, { 0.5, 0.07, 0.22 }, { 0.5, 0.18, 0.22 } },
     { { -0.1143, -0.0282, 0.0 }, { 0.0, 0.0434, 0.0 }, { 0.1185, -0.0138, 0.0 }, { -0.0195, 0.1498, 0.0 } }
 };
 
+//! Coordinates for testing bonds with zero length
+std::vector<PaddedVector<RVec>> c_coordinatesForTestsZeroBondLength = {
+    { { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.005, 0.0, 0.1 }, { 0.005, 0.0, 0.1 } }
+};
+
+//! Coordinates for testing bonds with zero length
+std::vector<PaddedVector<RVec>> c_coordinatesForTestsZeroAngle = {
+    { { 0.005, 0.0, 0.1 }, { 0.0, 0.0, 0.0 }, { 0.005, 0.0, 0.1 }, { 0.5, 0.18, 0.22 } }
+};
+
 //! PBC values for testing
-std::vector<int> c_pbcForTests = { epbcNONE, epbcXY, epbcXYZ };
+std::vector<PbcType> c_pbcForTests = { PbcType::No, PbcType::XY, PbcType::Xyz };
 
 // Those tests give errors with the intel compiler and nothing else, so we disable them only there.
 #ifndef __INTEL_COMPILER
@@ -694,6 +736,18 @@ INSTANTIATE_TEST_CASE_P(Restraints,
                         ::testing::Combine(::testing::ValuesIn(c_InputRestraints),
                                            ::testing::ValuesIn(c_coordinatesForTests),
                                            ::testing::ValuesIn(c_pbcForTests)));
+
+INSTANTIATE_TEST_CASE_P(BondZeroLength,
+                        ListedForcesTest,
+                        ::testing::Combine(::testing::ValuesIn(c_InputBondsZeroLength),
+                                           ::testing::ValuesIn(c_coordinatesForTestsZeroBondLength),
+                                           ::testing::ValuesIn(c_pbcForTests)));
+
+INSTANTIATE_TEST_CASE_P(AngleZero,
+                        ListedForcesTest,
+                        ::testing::Combine(::testing::ValuesIn(c_InputAnglesZeroAngle),
+                                           ::testing::ValuesIn(c_coordinatesForTestsZeroAngle),
+                                           ::testing::ValuesIn(c_pbcForTests)));
 #endif
 } // namespace
 
diff --git a/src/gromacs/listed_forces/tests/refdata/AngleZero_ListedForcesTest_Ifunc_0.xml b/src/gromacs/listed_forces/tests/refdata/AngleZero_ListedForcesTest_Ifunc_0.xml
new file mode 100644 (file)
index 0000000..bab7c13
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <FunctionType Name="ANGLES">
+    <FEP Name="No">
+      <Real Name="Epot ">84.797965428018102</Real>
+      <Real Name="dVdlambda ">0</Real>
+      <Vector Name="Central shift forces">
+        <Real Name="X">-2.1316282072803006e-14</Real>
+        <Real Name="Y">-1.7763568394002505e-15</Real>
+        <Real Name="Z">0</Real>
+      </Vector>
+      <Sequence Name="Forces">
+        <Int Name="Length">4</Int>
+        <Vector>
+          <Real Name="X">0</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">0</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">861.88775797312974</Real>
+          <Real Name="Y">318.05244565695551</Real>
+          <Real Name="Z">-43.094387898656521</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">-896.43676003712528</Real>
+          <Real Name="Y">-333.82837010100394</Real>
+          <Real Name="Z">209.27290807871069</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">34.549002063995523</Real>
+          <Real Name="Y">15.775924444048426</Real>
+          <Real Name="Z">-166.17852018005416</Real>
+        </Vector>
+      </Sequence>
+    </FEP>
+  </FunctionType>
+</ReferenceData>
diff --git a/src/gromacs/listed_forces/tests/refdata/AngleZero_ListedForcesTest_Ifunc_1.xml b/src/gromacs/listed_forces/tests/refdata/AngleZero_ListedForcesTest_Ifunc_1.xml
new file mode 100644 (file)
index 0000000..bab7c13
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <FunctionType Name="ANGLES">
+    <FEP Name="No">
+      <Real Name="Epot ">84.797965428018102</Real>
+      <Real Name="dVdlambda ">0</Real>
+      <Vector Name="Central shift forces">
+        <Real Name="X">-2.1316282072803006e-14</Real>
+        <Real Name="Y">-1.7763568394002505e-15</Real>
+        <Real Name="Z">0</Real>
+      </Vector>
+      <Sequence Name="Forces">
+        <Int Name="Length">4</Int>
+        <Vector>
+          <Real Name="X">0</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">0</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">861.88775797312974</Real>
+          <Real Name="Y">318.05244565695551</Real>
+          <Real Name="Z">-43.094387898656521</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">-896.43676003712528</Real>
+          <Real Name="Y">-333.82837010100394</Real>
+          <Real Name="Z">209.27290807871069</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">34.549002063995523</Real>
+          <Real Name="Y">15.775924444048426</Real>
+          <Real Name="Z">-166.17852018005416</Real>
+        </Vector>
+      </Sequence>
+    </FEP>
+  </FunctionType>
+</ReferenceData>
diff --git a/src/gromacs/listed_forces/tests/refdata/AngleZero_ListedForcesTest_Ifunc_2.xml b/src/gromacs/listed_forces/tests/refdata/AngleZero_ListedForcesTest_Ifunc_2.xml
new file mode 100644 (file)
index 0000000..bab7c13
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <FunctionType Name="ANGLES">
+    <FEP Name="No">
+      <Real Name="Epot ">84.797965428018102</Real>
+      <Real Name="dVdlambda ">0</Real>
+      <Vector Name="Central shift forces">
+        <Real Name="X">-2.1316282072803006e-14</Real>
+        <Real Name="Y">-1.7763568394002505e-15</Real>
+        <Real Name="Z">0</Real>
+      </Vector>
+      <Sequence Name="Forces">
+        <Int Name="Length">4</Int>
+        <Vector>
+          <Real Name="X">0</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">0</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">861.88775797312974</Real>
+          <Real Name="Y">318.05244565695551</Real>
+          <Real Name="Z">-43.094387898656521</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">-896.43676003712528</Real>
+          <Real Name="Y">-333.82837010100394</Real>
+          <Real Name="Z">209.27290807871069</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">34.549002063995523</Real>
+          <Real Name="Y">15.775924444048426</Real>
+          <Real Name="Z">-166.17852018005416</Real>
+        </Vector>
+      </Sequence>
+    </FEP>
+  </FunctionType>
+</ReferenceData>
diff --git a/src/gromacs/listed_forces/tests/refdata/BondZeroLength_ListedForcesTest_Ifunc_0.xml b/src/gromacs/listed_forces/tests/refdata/BondZeroLength_ListedForcesTest_Ifunc_0.xml
new file mode 100644 (file)
index 0000000..160a61d
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <FunctionType Name="BONDS">
+    <FEP Name="No">
+      <Real Name="Epot ">2.5062500000000005</Real>
+      <Real Name="dVdlambda ">0</Real>
+      <Vector Name="Central shift forces">
+        <Real Name="X">0</Real>
+        <Real Name="Y">0</Real>
+        <Real Name="Z">0</Real>
+      </Vector>
+      <Sequence Name="Forces">
+        <Int Name="Length">4</Int>
+        <Vector>
+          <Real Name="X">0</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">0</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">2.5</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">50</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">-2.5</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">-50</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">0</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">0</Real>
+        </Vector>
+      </Sequence>
+    </FEP>
+  </FunctionType>
+</ReferenceData>
diff --git a/src/gromacs/listed_forces/tests/refdata/BondZeroLength_ListedForcesTest_Ifunc_1.xml b/src/gromacs/listed_forces/tests/refdata/BondZeroLength_ListedForcesTest_Ifunc_1.xml
new file mode 100644 (file)
index 0000000..160a61d
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <FunctionType Name="BONDS">
+    <FEP Name="No">
+      <Real Name="Epot ">2.5062500000000005</Real>
+      <Real Name="dVdlambda ">0</Real>
+      <Vector Name="Central shift forces">
+        <Real Name="X">0</Real>
+        <Real Name="Y">0</Real>
+        <Real Name="Z">0</Real>
+      </Vector>
+      <Sequence Name="Forces">
+        <Int Name="Length">4</Int>
+        <Vector>
+          <Real Name="X">0</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">0</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">2.5</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">50</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">-2.5</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">-50</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">0</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">0</Real>
+        </Vector>
+      </Sequence>
+    </FEP>
+  </FunctionType>
+</ReferenceData>
diff --git a/src/gromacs/listed_forces/tests/refdata/BondZeroLength_ListedForcesTest_Ifunc_2.xml b/src/gromacs/listed_forces/tests/refdata/BondZeroLength_ListedForcesTest_Ifunc_2.xml
new file mode 100644 (file)
index 0000000..160a61d
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <FunctionType Name="BONDS">
+    <FEP Name="No">
+      <Real Name="Epot ">2.5062500000000005</Real>
+      <Real Name="dVdlambda ">0</Real>
+      <Vector Name="Central shift forces">
+        <Real Name="X">0</Real>
+        <Real Name="Y">0</Real>
+        <Real Name="Z">0</Real>
+      </Vector>
+      <Sequence Name="Forces">
+        <Int Name="Length">4</Int>
+        <Vector>
+          <Real Name="X">0</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">0</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">2.5</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">50</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">-2.5</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">-50</Real>
+        </Vector>
+        <Vector>
+          <Real Name="X">0</Real>
+          <Real Name="Y">0</Real>
+          <Real Name="Z">0</Real>
+        </Vector>
+      </Sequence>
+    </FEP>
+  </FunctionType>
+</ReferenceData>
index aee43d9ac96f9892aa6c3247414ae89011e43274..170bef2ea6b0cb83a0862d048de62cb01f647cc5 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index cc2b0a1409df041572a31fdc582dc5e5f289dd16..b36ac788de4f1eb7a53f73363fb1829ae44333ac 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b36ef2f7d3d81fd4cda2ded46bc978f7ad5af520..16d6ccd7408227be68d04e515f169c1a4d290314 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -35,7 +36,7 @@
 file(GLOB MATH_SOURCES *.cpp)
 set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${MATH_SOURCES} PARENT_SCOPE)
 
-# TODO: (https://redmine.gromacs.org/issues/988) Find a new convention for defining public API.
+# TODO: (https://gitlab.com/gromacs/gromacs/-/issues/988) Find a new convention for defining public API.
 install(FILES
        do_fit.h
        functions.h
index 3e5f535c6cb615015da2db6688b07149f09c09b7..b6489475c30a5a131978750827b4732b29a0a199 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -120,7 +120,10 @@ public:
         paddedEnd_(std::move(o.paddedEnd_))
     {
     }
-    //! Convenience overload constructor to make an ArrayRefWithPadding<const T> from a non-const one.
+    /*! \brief Convenience overload constructor to make an ArrayRefWithPadding<const T> from a non-const one.
+     *
+     * \todo Use std::is_same_v when CUDA 11 is a requirement.
+     */
     template<typename U, typename = std::enable_if_t<std::is_same<value_type, const typename std::remove_reference_t<U>::value_type>::value>>
     ArrayRefWithPadding(U&& o)
     {
index c380efecf37d5adb108252b31b9ebbef4e3f63a8..6555d96e2db2e072e0e8f9374ad04e3582e3f3c1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <vector>
 
 #include "gromacs/math/vec.h"
+#include "gromacs/mdspan/extensions.h"
+#include "gromacs/utility/arrayref.h"
+
+#include "matrix.h"
 
 namespace gmx
 {
+
 /********************************************************************
  * ScaleCoordinates::Impl
  */
@@ -99,11 +104,21 @@ void ScaleCoordinates::operator()(ArrayRef<RVec> coordinates) const
     impl_->scale(coordinates);
 }
 
+void ScaleCoordinates::operator()(RVec* coordinate) const
+{
+    impl_->scale({ coordinate, coordinate + 1 });
+}
+
 void ScaleCoordinates::inverseIgnoringZeroScale(ArrayRef<RVec> coordinates) const
 {
     impl_->inverseIgnoringZeroScale(coordinates);
 }
 
+void ScaleCoordinates::inverseIgnoringZeroScale(RVec* coordinate) const
+{
+    impl_->inverseIgnoringZeroScale({ coordinate, coordinate + 1 });
+}
+
 ScaleCoordinates::~ScaleCoordinates() = default;
 
 ScaleCoordinates::ScaleCoordinates(const ScaleCoordinates& other) : impl_(new Impl(*other.impl_)) {}
@@ -163,6 +178,11 @@ void TranslateAndScale::operator()(ArrayRef<RVec> coordinates) const
     impl_->transform(coordinates);
 }
 
+void TranslateAndScale::operator()(RVec* coordinate) const
+{
+    impl_->transform({ coordinate, coordinate + 1 });
+}
+
 ScaleCoordinates TranslateAndScale::scaleOperationOnly() const
 {
     return ScaleCoordinates{ impl_->scale_ };
@@ -184,5 +204,28 @@ TranslateAndScale::TranslateAndScale(TranslateAndScale&&) noexcept = default;
 
 TranslateAndScale& TranslateAndScale::operator=(TranslateAndScale&&) noexcept = default;
 
+/********************************************************************
+ * AffineTransformation
+ */
+
+AffineTransformation::AffineTransformation(Matrix3x3ConstSpan matrix, const RVec& translation) :
+    translation_{ translation }
+{
+    std::copy(begin(matrix), end(matrix), begin(matrix_));
+}
+
+void AffineTransformation::operator()(ArrayRef<RVec> vectors) const
+{
+    for (RVec& vector : vectors)
+    {
+        matrixVectorMultiply(matrix_.asConstView(), &vector);
+        vector += translation_;
+    }
+}
+
+void AffineTransformation::operator()(RVec* vector) const
+{
+    (*this)({ vector, vector + 1 });
+}
 
 } // namespace gmx
index 5e962fc92f7b402767d41473be19d2db90a6eda1..2451283b1f232a1e3ef9eb7551872e0c47508546 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_MATH_COORDINATETRANSFORMATION_H
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/classhelpers.h"
 
+#include "matrix.h"
+
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
 class ScaleCoordinates
 {
 public:
@@ -70,11 +73,22 @@ public:
      * \param[in] coordinates to be transformed
      */
     void operator()(ArrayRef<RVec> coordinates) const;
+
+    /*! \brief Perform a coordinate transformation on an input coordinate.
+     * \param[in] coordinate to be transformed
+     */
+    void operator()(RVec* coordinate) const;
+
     /*! \brief Apply the inverse scale to coordinates, ignoring dimensions for which scale is zero.
      * \param[in] coordinates to be transformed
      */
     void inverseIgnoringZeroScale(ArrayRef<RVec> coordinates) const;
 
+    /*! \brief Apply the inverse scale to a coordinate, ignoring dimensions for which scale is zero.
+     * \param[in] coordinate to be transformed
+     */
+    void inverseIgnoringZeroScale(RVec* coordinate) const;
+
 private:
     class Impl;
     PrivateImplPointer<Impl> impl_;
@@ -109,6 +123,11 @@ public:
      */
     void operator()(ArrayRef<RVec> coordinates) const;
 
+    /*! \brief Perform a coordinate transformation on a coordinate.
+     * \param[in] coordinate to be transformed
+     */
+    void operator()(RVec* coordinate) const;
+
     /*! \brief Returns the scaling operation, discarding the translation.
      */
     ScaleCoordinates scaleOperationOnly() const;
@@ -117,5 +136,39 @@ private:
     class Impl;
     PrivateImplPointer<Impl> impl_;
 };
+
+/*! \libinternal
+ * \brief Affine transformation of three-dimensional coordinates.
+ *
+ * Perfoms in-place coordinate transformations.
+ *
+ * Coordinates are first multiplied by a matrix, then translated.
+ */
+class AffineTransformation
+{
+public:
+    /*! \brief Construct a three-dimensional affine transformation.
+     * \param[in] matrix to be applied to the vectors
+     * \param[in] translation to be performed on the vectors
+     */
+    AffineTransformation(Matrix3x3ConstSpan matrix, const RVec& translation);
+
+    /*! \brief Perform an affine transformation on input vectors.
+     * \param[in,out] vectors to be transformed in-place
+     */
+    void operator()(ArrayRef<RVec> vectors) const;
+
+    /*! \brief Perform an affine transformation on a vector.
+     * \param[in,out] vector to be transformed in-place
+     */
+    void operator()(RVec* vector) const;
+
+private:
+    //! The matrix describing the affine transformation A(x) = matrix_ * x + translation_
+    Matrix3x3 matrix_;
+    //! The translation vector describing the affine transformation A(x) = matrix * x + translation
+    RVec translation_;
+};
+
 } // namespace gmx
 #endif // CoordinateTransformation
index a9ae79c5e72f8374e40d2785ab2e2a3e7bd7794a..3301fc73dfa5914f01c2a41a98c4114f4e140b1c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -283,7 +283,7 @@ public:
     }
 
 private:
-    real evaluatePrefactor(real comparisonSquaredSum, real referenceSquaredSum)
+    static real evaluatePrefactor(real comparisonSquaredSum, real referenceSquaredSum)
     {
         GMX_ASSERT(comparisonSquaredSum > 0,
                    "Squared sum of comparison values needs to be larger than zero.");
index b3a3eb3f441ccb24f5acc3337c81877186e11c42..246d6610b0e4404ef02475c408f4d7820ac818fd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,7 +45,6 @@
 
 #include "gromacs/mdspan/extensions.h"
 #include "gromacs/utility/classhelpers.h"
-#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/real.h"
 
 namespace gmx
@@ -53,7 +52,7 @@ namespace gmx
 /*! \brief
  * The methods that determine how two densities are compared to one another.
  */
-enum class DensitySimilarityMeasureMethod
+enum class DensitySimilarityMeasureMethod : int
 {
     /*! \brief Measure similarity between densities as normalized inner product of their
      * voxel values.
@@ -89,11 +88,6 @@ enum class DensitySimilarityMeasureMethod
     Count,
 };
 
-//! Name the methods that may be used to evaluate similarity between densities
-const EnumerationArray<DensitySimilarityMeasureMethod, const char* const> c_densitySimilarityMeasureMethodNames = {
-    { "inner-product", "relative-entropy", "cross-correlation" }
-};
-
 /* Forward declaration of implementation class outside class to allow
  * choose implementation class during construction of the DensitySimilarityMeasure*/
 class DensitySimilarityMeasureImpl;
index ff4f26fbd93cfc8eef4b1945247a2e3c7863f25d..0ba9878dbeaff47db11096088cf971643cad0cc0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -79,7 +79,9 @@ public:
 
 DensityFittingForce::Impl::Impl(const GaussianSpreadKernelParameters::Shape& kernelShapeParameters) :
     sigma_{ kernelShapeParameters.sigma_ },
-    latticeSpreadRange_{ kernelShapeParameters.latticeSpreadRange() },
+    latticeSpreadRange_{ kernelShapeParameters.latticeSpreadRange()[XX],
+                         kernelShapeParameters.latticeSpreadRange()[YY],
+                         kernelShapeParameters.latticeSpreadRange()[ZZ] },
     gauss1d_({ GaussianOn1DLattice(latticeSpreadRange_[XX], sigma_[XX]),
                GaussianOn1DLattice(latticeSpreadRange_[YY], sigma_[YY]),
                GaussianOn1DLattice(latticeSpreadRange_[ZZ], sigma_[ZZ]) })
index e4faa8e198a653b13179cd63889a8a64b23140d1..b8be02f06dbc903616c8643ef6e2a9209a7bcc7a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 9a969f51fe64d3d23076514b973bae65fa6ec265..17551d859a518775ccd908da122aa183f6fac264 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index c5af105177d7ee710fbaf03002907df25041a79b..40ef38d79024a835d010ec7e9dae2c5fd4d136ea 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdspan/extensions.h"
 #include "gromacs/mdspan/mdspan.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/real.h"
 
 namespace gmx
 {
-
+template<typename>
+class ArrayRef;
 /*! \internal
  * \brief Provide result of Gaussian function evaluation on a one-dimensional lattice.
  *
diff --git a/src/gromacs/math/matrix.cpp b/src/gromacs/math/matrix.cpp
new file mode 100644 (file)
index 0000000..662d6cf
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements routines in matrix.h .
+ *
+ * \author Christian Blau <blau@kth.se>
+ */
+
+#include "gmxpre.h"
+
+#include "matrix.h"
+
+namespace gmx
+{
+
+
+Matrix3x3 transpose(Matrix3x3ConstSpan matrixView)
+{
+
+    return Matrix3x3({ matrixView(0, 0), matrixView(1, 0), matrixView(2, 0), matrixView(0, 1),
+                       matrixView(1, 1), matrixView(2, 1), matrixView(0, 2), matrixView(1, 2),
+                       matrixView(2, 2) });
+}
+
+void matrixVectorMultiply(Matrix3x3ConstSpan matrix, RVec* v)
+{
+    const real resultXX =
+            matrix(XX, XX) * (*v)[XX] + matrix(XX, YY) * (*v)[YY] + matrix(XX, ZZ) * (*v)[ZZ];
+    const real resultYY =
+            matrix(YY, XX) * (*v)[XX] + matrix(YY, YY) * (*v)[YY] + matrix(YY, ZZ) * (*v)[ZZ];
+    (*v)[ZZ] = matrix(ZZ, XX) * (*v)[XX] + matrix(ZZ, YY) * (*v)[YY] + matrix(ZZ, ZZ) * (*v)[ZZ];
+    (*v)[XX] = resultXX;
+    (*v)[YY] = resultYY;
+}
+
+
+} // namespace gmx
index c4b428c9ea2b94919db0dc86fee4ad40cdbcb731..b7b03fa8c1a8633dabcd1f884f15018ee5fb9426 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,6 +46,7 @@
 #include <array>
 
 #include "gromacs/math/multidimarray.h"
+#include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/utility/real.h"
 
@@ -81,15 +82,33 @@ constexpr real trace(Matrix3x3ConstSpan matrixView)
     return matrixView(0, 0) + matrixView(1, 1) + matrixView(2, 2);
 }
 
-//! Calculate the transpose of a 3x3 matrix, from its view
-static Matrix3x3 transpose(Matrix3x3ConstSpan matrixView)
+/*! \brief Create an identity matrix of ElementType with N * M elements.
+ *
+ * \tparam ElementType type of matrix elements
+ * \tparam N number of rows
+ * \tparam M number of columns, defaults to number of rows if not set
+ *
+ * \returns a matrix with values one where row equals column index and null
+ *          where row does not equal column index
+ */
+template<typename ElementType, int N, int M = N>
+MultiDimArray<std::array<ElementType, N * M>, extents<N, M>> identityMatrix()
 {
-
-    return Matrix3x3({ matrixView(0, 0), matrixView(1, 0), matrixView(2, 0), matrixView(0, 1),
-                       matrixView(1, 1), matrixView(2, 1), matrixView(0, 2), matrixView(1, 2),
-                       matrixView(2, 2) });
+    std::array<ElementType, N * M>                               matrixEntries{};
+    MultiDimArray<std::array<ElementType, N * M>, extents<N, M>> idMatrix(matrixEntries);
+    for (int i = 0; i < std::min(N, M); i++)
+    {
+        idMatrix(i, i) = 1;
+    }
+    return idMatrix;
 }
 
+//! Calculate the transpose of a 3x3 matrix, from its view
+Matrix3x3 transpose(Matrix3x3ConstSpan matrixView);
+
+//! Multiply matrix with vector.
+void matrixVectorMultiply(Matrix3x3ConstSpan matrix, RVec* v);
+
 //! Create new matrix type from legacy type.
 static inline Matrix3x3 createMatrix3x3FromLegacyMatrix(const matrix legacyMatrix)
 {
index 2e1a9e2eb67decb3d55c90a6619c5fa6e735fdf7..8f6047cc400f8c99ca4c6ed2fe06d2b5be8649e6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -69,6 +69,7 @@ struct is_resizable<T, void_t<decltype(std::declval<T>().resize(size_t()))>> : s
 
 //! Type has a resize member function callable with size_t argument
 template<typename T>
+// NOLINTNEXTLINE misc-definitions-in-headers
 constexpr bool is_resizable_v = is_resizable<T>::value;
 } // namespace detail
 
diff --git a/src/gromacs/math/neldermead.cpp b/src/gromacs/math/neldermead.cpp
new file mode 100644 (file)
index 0000000..9bb1df2
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements routines in neldermead.h .
+ *
+ * \author Christian Blau <blau@kth.se>
+ */
+
+#include "gmxpre.h"
+
+#include "neldermead.h"
+
+#include <algorithm>
+#include <list>
+#include <numeric>
+#include <vector>
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+/*! \brief Evaluate the linear combination of two vectors a and b.
+ *
+ * \param[in] alpha scaling factor for a
+ * \param[in] a vector to be scaled
+ * \param[in] beta scaling factor for b
+ * \param[in] b vector to be scaled
+ *
+ * \returns alpha * a + beta * b.
+ */
+std::vector<real> linearCombination(real alpha, ArrayRef<const real> a, real beta, ArrayRef<const real> b)
+{
+    GMX_ASSERT(a.size() == b.size(),
+               "Input vectors have to have the same size to evaluate their linear combination.");
+    std::vector<real> result(a.size());
+    std::transform(std::begin(a), std::end(a), std::begin(b), std::begin(result),
+                   [alpha, beta](auto a, auto b) { return alpha * a + beta * b; });
+    return result;
+};
+
+/*! \internal
+ *  \brief The parameters for a Nelder-Mead optimisation.
+ */
+struct NelderMeadParameters
+{
+    //! Factor to evaluate the reflection point
+    real alpha_ = 1;
+    //! Factor to evaluate the expansion point
+    real gamma_ = 2;
+    //! Factor to evaluate the contraction point
+    real rho_ = 0.5;
+    //! Factor to evaluate the simplex shrinkage
+    real sigma_ = 0.5;
+};
+
+constexpr NelderMeadParameters defaultNelderMeadParameters = { 1, 2, 0.5, 0.5 };
+
+} // namespace
+
+
+NelderMeadSimplex::NelderMeadSimplex(const std::function<real(ArrayRef<const real>)>& f,
+                                     ArrayRef<const real>                             initalGuess)
+{
+    // initial simplex contains the initally guessed vertex
+    std::vector<real> initalVertex = copyOf(initalGuess);
+    simplex_.push_back({ initalVertex, f(initalVertex) });
+    // create the missing verticies by moving 0.05 or 0.0025 if null
+    // from the initial vertex dimension after dimension
+    for (auto& v : initalVertex)
+    {
+        const auto oldValue = v;
+        if (v == 0)
+        {
+            v = 0.0025;
+        }
+        else
+        {
+            v += 0.05;
+        }
+        simplex_.push_back({ initalVertex, f(initalVertex) });
+        v = oldValue;
+    }
+    simplex_.sort([](const RealFunctionvalueAtCoordinate& lhs,
+                     const RealFunctionvalueAtCoordinate& rhs) { return lhs.value_ < rhs.value_; });
+    updateCentroidAndReflectionPoint();
+}
+
+RealFunctionvalueAtCoordinate
+NelderMeadSimplex::evaluateReflectionPoint(const std::function<real(ArrayRef<const real>)>& f) const
+{
+    return { reflectionPointCoordinates_, f(reflectionPointCoordinates_) };
+}
+
+const RealFunctionvalueAtCoordinate& NelderMeadSimplex::bestVertex() const
+{
+    return simplex_.front();
+}
+
+const RealFunctionvalueAtCoordinate& NelderMeadSimplex::worstVertex() const
+{
+    return simplex_.back();
+}
+
+real NelderMeadSimplex::secondWorstValue() const
+{
+    // go backwards one step from the end of the sorted simplex list
+    // and look at the vertex value
+    return std::next(std::rbegin(simplex_))->value_;
+}
+
+RealFunctionvalueAtCoordinate
+NelderMeadSimplex::evaluateExpansionPoint(const std::function<real(ArrayRef<const real>)>& f) const
+{
+    const std::vector<real> expansionPointCoordinate =
+            linearCombination(1 - defaultNelderMeadParameters.gamma_, centroidWithoutWorstPoint_,
+                              defaultNelderMeadParameters.gamma_, reflectionPointCoordinates_);
+    return { expansionPointCoordinate, f(expansionPointCoordinate) };
+}
+
+RealFunctionvalueAtCoordinate
+NelderMeadSimplex::evaluateContractionPoint(const std::function<real(ArrayRef<const real>)>& f) const
+{
+    std::vector<real> contractionPoint =
+            linearCombination(1 - defaultNelderMeadParameters.rho_, centroidWithoutWorstPoint_,
+                              defaultNelderMeadParameters.rho_, worstVertex().coordinate_);
+    return { contractionPoint, f(contractionPoint) };
+}
+
+void NelderMeadSimplex::swapOutWorst(const RealFunctionvalueAtCoordinate& newVertex)
+{
+    // drop the worst point - we know it's at the back of the simplex list, because
+    // we kept the list sorted
+    simplex_.pop_back();
+    // find the point to insert the new vertex, so that the simplex vertices
+    // keep being sorted according to function value
+    const auto insertionPoint = std::lower_bound(
+            std::begin(simplex_), std::end(simplex_), newVertex.value_,
+            [](const RealFunctionvalueAtCoordinate& lhs, real value) { return lhs.value_ < value; });
+    simplex_.insert(insertionPoint, newVertex);
+    // now that the simplex has changed, it has a new centroid and reflection point
+    updateCentroidAndReflectionPoint();
+}
+
+void NelderMeadSimplex::shrinkSimplexPointsExceptBest(const std::function<real(ArrayRef<const real>)>& f)
+{
+    std::vector<real> bestPointCoordinate = simplex_.front().coordinate_;
+    // skipping over the first simplex vertex, pull points closer to the best
+    // vertex
+    std::transform(std::next(std::begin(simplex_)), std::end(simplex_), std::next(std::begin(simplex_)),
+                   [bestPointCoordinate, f](const RealFunctionvalueAtCoordinate& d) -> RealFunctionvalueAtCoordinate {
+                       const std::vector<real> shrinkPoint = linearCombination(
+                               defaultNelderMeadParameters.sigma_, d.coordinate_,
+                               1 - defaultNelderMeadParameters.sigma_, bestPointCoordinate);
+                       return { shrinkPoint, f(shrinkPoint) };
+                   });
+
+    simplex_.sort([](const RealFunctionvalueAtCoordinate& lhs,
+                     const RealFunctionvalueAtCoordinate& rhs) { return lhs.value_ < rhs.value_; });
+
+    // now that the simplex has changed, it has a new centroid and reflection point
+    updateCentroidAndReflectionPoint();
+}
+
+real NelderMeadSimplex::orientedLength() const
+{
+    real                    result                       = 0;
+    const std::vector<real> firstSimplexVertexCoordinate = simplex_.front().coordinate_;
+    // find out which vertex coordinate has the largest distance to the first simplex vertex.
+    for (const auto& simplexVertex : simplex_)
+    {
+        const std::vector<real> differenceVector =
+                linearCombination(1, firstSimplexVertexCoordinate, -1, simplexVertex.coordinate_);
+        const real thisLength =
+                std::accumulate(std::begin(differenceVector), std::end(differenceVector), 0.,
+                                [](real sum, real value) { return sum + value * value; });
+        result = std::max(result, thisLength);
+    }
+    return sqrt(result);
+}
+
+void NelderMeadSimplex::updateCentroidAndReflectionPoint()
+{
+    // intialize with first vertex, then add up all other vertex coordinates
+    // expect last one
+    centroidWithoutWorstPoint_ = std::accumulate(
+            std::next(std::begin(simplex_)), std::prev(std::end(simplex_)), simplex_.front().coordinate_,
+            [](std::vector<real> sum, const RealFunctionvalueAtCoordinate& x) {
+                std::transform(std::begin(sum), std::end(sum), std::begin(x.coordinate_),
+                               std::begin(sum), std::plus<>());
+                return sum;
+            });
+
+    // divide the summed up coordinates by N (the simplex has N+1 vertices)
+    std::transform(std::begin(centroidWithoutWorstPoint_), std::end(centroidWithoutWorstPoint_),
+                   std::begin(centroidWithoutWorstPoint_),
+                   [n = simplex_.size() - 1](const auto& x) { return x / n; });
+
+    // now, that we have evaluated the centroid, update the reflection points
+    reflectionPointCoordinates_ =
+            linearCombination(defaultNelderMeadParameters.alpha_ + 1, centroidWithoutWorstPoint_,
+                              -1, worstVertex().coordinate_);
+}
+
+} // namespace gmx
diff --git a/src/gromacs/math/neldermead.h b/src/gromacs/math/neldermead.h
new file mode 100644 (file)
index 0000000..6036ebf
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declare classes to aid Nelder-Mead downhill simplex optimisation.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_math
+ */
+
+#ifndef GMX_MATH_NELDERMEAD_H
+#define GMX_MATH_NELDERMEAD_H
+
+#include <functional>
+#include <list>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Tie together coordinate and function value at this coordinate.
+ */
+struct RealFunctionvalueAtCoordinate
+{
+    //! Vertex coordinate
+    std::vector<real> coordinate_;
+    //! Function value at this coordinate
+    real value_;
+};
+
+/*! \internal
+ * \brief The simplex for the Nelder-Mead algorithm.
+ *
+ * Contains the N+1 simplex N-dimensional coordinates and its function values.
+ * Allows for simplex manipulations as needed for the Nelder-Mead algorithm.
+ *
+ * \note Keeps the simplex sorted according to function values with the simplex
+ *       at the lowest function value first.
+ */
+class NelderMeadSimplex
+{
+public:
+    /*! \brief Set up Nelder-Mead simplex from an initial guess.
+     *
+     * \note Triggers N+1 function evaluations at all simplex points.
+     *
+     * \param[in] f the function to be evaluated
+     * \param[in] initalGuess initial guess of the coordinates.
+     *
+     */
+    NelderMeadSimplex(const std::function<real(ArrayRef<const real>)>& f, ArrayRef<const real> initalGuess);
+
+    //! Return the vertex with the lowest function value at any of the simplex vertices.
+    const RealFunctionvalueAtCoordinate& bestVertex() const;
+
+    //! Return the vertex of the simplex with the highest (worst) function value.
+    const RealFunctionvalueAtCoordinate& worstVertex() const;
+
+    //! Return the second largest function value at any of the simplex vertices.
+    real secondWorstValue() const;
+
+    //! Return the reflection point and the evaluated function value at this point.
+    RealFunctionvalueAtCoordinate
+    evaluateReflectionPoint(const std::function<real(ArrayRef<const real>)>& f) const;
+
+    //! Evaluate and return the expansion point and function value.
+    RealFunctionvalueAtCoordinate evaluateExpansionPoint(const std::function<real(ArrayRef<const real>)>& f) const;
+
+    //! Evaluate and return the contraction point and function value.
+    RealFunctionvalueAtCoordinate
+    evaluateContractionPoint(const std::function<real(ArrayRef<const real>)>& f) const;
+
+    /*! \brief Replace the simplex vertex with the largest function value.
+     *
+     * \param[in] newVertex to replace the worst vertex with
+     * \note keeps the simplex list sorted and reevaluates the reflection point
+     */
+    void swapOutWorst(const RealFunctionvalueAtCoordinate& newVertex);
+
+    /*! \brief Shrink the simplex.
+     *
+     * All points move closer to the best point by a factor \f$\sigma\f$.
+     *
+     * Replace all point coordinates, except the best, with
+     * \f$x_i = x_{\mathrm{best}} + \sigma (x_i - x_{\mathrm{best}})\f$
+     */
+    void shrinkSimplexPointsExceptBest(const std::function<real(ArrayRef<const real>)>& f);
+
+    /*! \brief The oriented length of the vertex.
+     *
+     * The oriented length of the simplex is defined as the largest distance
+     * between the first simplex vertex coordinate (with the lowest, best function
+     * value) and any other simplex coordinate.
+     *
+     * The oriented length is used as a computationally fast and simple
+     * convergence criterion because it is proven that
+     * orientedLegnth < simplex_diameter < 2 * orientedLength
+     *
+     */
+    real orientedLength() const;
+
+private:
+    /*! \brief Update centroid and reflection point.
+     *
+     * The arithmetic mean of all vertex coordinates expect the one at the
+     * highest (worst) function value.
+     *
+     */
+    void updateCentroidAndReflectionPoint();
+
+    /*! \brief The points of the simplex with the function values.
+     * \note This list stays sorted according to function value during the
+     *       life-time of this object.
+     */
+    std::list<RealFunctionvalueAtCoordinate> simplex_;
+
+    //! The centroid of the simplex, skipping the worst point is updated once the simplex changes
+    std::vector<real> centroidWithoutWorstPoint_;
+
+    //! The reflection point and its function value is updated once the simplex changes
+    std::vector<real> reflectionPointCoordinates_;
+};
+
+
+} // namespace gmx
+
+#endif // GMX_MATH_NELDERMEAD_H
diff --git a/src/gromacs/math/optimization.cpp b/src/gromacs/math/optimization.cpp
new file mode 100644 (file)
index 0000000..4535fd8
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements routines in optimization.h .
+ *
+ * \author Christian Blau <blau@kth.se>
+ */
+
+#include "gmxpre.h"
+
+#include "optimization.h"
+
+#include "neldermead.h"
+
+namespace gmx
+{
+
+OptimisationResult nelderMead(const std::function<real(ArrayRef<const real>)>& functionToMinimize,
+                              ArrayRef<const real>                             initalGuess,
+                              real minimumRelativeSimplexLength,
+                              int  maxSteps)
+{
+    // Set up the initial simplex, sorting vertices according to function value
+    NelderMeadSimplex nelderMeadSimplex(functionToMinimize, initalGuess);
+
+    // Run until maximum step size reached or algorithm is converged, e.g.,
+    // the oriented simplex length is smaller or equal a given number.
+    const real minimumSimplexLength = minimumRelativeSimplexLength * nelderMeadSimplex.orientedLength();
+    for (int currentStep = 0;
+         nelderMeadSimplex.orientedLength() > minimumSimplexLength && currentStep < maxSteps; ++currentStep)
+    {
+
+        // see if simplex can by improved by reflecing the worst vertex at the centroid
+        const RealFunctionvalueAtCoordinate& reflectionPoint =
+                nelderMeadSimplex.evaluateReflectionPoint(functionToMinimize);
+
+        // Reflection point is not better than best simplex vertex so far
+        // but better than second worst
+        if ((nelderMeadSimplex.bestVertex().value_ <= reflectionPoint.value_)
+            && (reflectionPoint.value_ < nelderMeadSimplex.secondWorstValue()))
+        {
+            nelderMeadSimplex.swapOutWorst(reflectionPoint);
+            continue;
+        }
+
+        // If the reflection point is better than the best one see if simplex
+        // can be further improved by continuing going in that direction
+        if (reflectionPoint.value_ < nelderMeadSimplex.bestVertex().value_)
+        {
+            RealFunctionvalueAtCoordinate expansionPoint =
+                    nelderMeadSimplex.evaluateExpansionPoint(functionToMinimize);
+            if (expansionPoint.value_ < reflectionPoint.value_)
+            {
+                nelderMeadSimplex.swapOutWorst(expansionPoint);
+            }
+            else
+            {
+                nelderMeadSimplex.swapOutWorst(reflectionPoint);
+            }
+            continue;
+        }
+
+        // The reflection point was a poor choice, try contracting the
+        // worst point coordinates using the centroid instead
+        RealFunctionvalueAtCoordinate contractionPoint =
+                nelderMeadSimplex.evaluateContractionPoint(functionToMinimize);
+        if (contractionPoint.value_ < nelderMeadSimplex.worstVertex().value_)
+        {
+            nelderMeadSimplex.swapOutWorst(contractionPoint);
+            continue;
+        }
+
+        // If neither expansion nor contraction of the worst point give a
+        // good result shrink the whole simplex
+        nelderMeadSimplex.shrinkSimplexPointsExceptBest(functionToMinimize);
+    }
+
+    return { nelderMeadSimplex.bestVertex().coordinate_, nelderMeadSimplex.bestVertex().value_ };
+}
+
+} // namespace gmx
diff --git a/src/gromacs/math/optimization.h b/src/gromacs/math/optimization.h
new file mode 100644 (file)
index 0000000..412a27f
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declare function optimization routines.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_math
+ */
+
+#ifndef GMX_MATH_OPTIMIZATION_H
+#define GMX_MATH_OPTIMIZATION_H
+
+#include <functional>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \internal
+ *  \brief Compiles results of an a function optimisation.
+ */
+struct OptimisationResult
+{
+    //! The coordinates at which the optimal function value has been found
+    std::vector<real> coordinates_;
+    //! The value of the function at the optimal coordinates
+    real functionValue_;
+};
+
+/*! \brief Derivative-free downhill simplex optimisation.
+ *
+ * Find a local minimum of an N-dimensional mapping
+ * \f$\mathbb{R}^N\to\mathbb{R}\f$ using the downhill simplex algorithm by
+ * Nelder and Mead as described in
+ *
+ *       Saša Singer and John Nelder (2009), Scholarpedia, 4(7):2928.
+ *       doi:10.4249/scholarpedia.2928
+ *
+ * Stops when the oriented simplex length is less than a constant factor times the
+ * initial lengths or when a maximum step size is reached.
+ *
+ * For best performance pre-condition problem magnitudes to 1.
+ *
+ * The following algorithm is implemented in this function
+ *  1 Define the N+1 vertices of the initial simplex
+ *      The inital simplex is constructed from the initial guess and N
+ *      additional vertices by adding 0.05 to the initial guess (or 0.0025 if
+ *      the initial guess is the null vector) from the initial vertex (in line
+ *      with usual implementations).
+ *
+ *  1a Sort vertices according to function value with the lowest function value
+ *    first in order to minimize the function.
+ *
+ *  2 Calculate the centroid of the simplex as arithmetic mean of all vertices
+ *    except the worst, \f$ x_c = \frac1n \sum_{i=1}{N} x_i\f$.
+ *
+ *  3 Reflect the worst simplex vertex (the one with the highest function value)
+ *    at the centroid to obtain a reflection point
+ *    \f$ x_r = x_c + \alpha (x_c - x_{N+1}) \f$ which lies outside the vertex.
+ *
+ *  3a Replace worst point with reflection point if reflection point function
+ *    value is better than second worst point, but not better than best and go
+ *    to 1a.
+ *
+ *  4 If the reflection point is better than all other points so far, attempt
+ *    an expansion by calculating the expansion point at
+ *    \f$ x_e = x_c + \gamma (x_r - x_c) \f$. Swap out the worst point in the
+ *    vertex with the expansion point if better than reflection point, otherwise
+ *    use the reflection point and go to 1a.
+ *
+ *  5 Attempt contraction, because reflection was not successful;
+ *    \f$ x_t = x_c + \rho (x_{N+1} - x_c) \f$. If the contraction point is
+ *    better than the worst point, swap out worst point with contracted point
+ *    and go to 1a.
+ *
+ *  6 Shrink the vertex. Replace all points except the best one with
+ *    \f$ x_i = x_1 + \sigma (x_i - x_1) \f$ and go to 1a.
+ *
+ * \param[in] functionToMinimize function to be minimized
+ * \param[in] initialGuess of coordinates
+ * \param[in] minimumRelativeSimplexLength minimal oriented simplex length with
+ *                                         respect to initial simplex
+ * \param[in] maxSteps to run algorithm for
+ *
+ * \returns the lowest found function value and corresponding coordinates.
+ */
+OptimisationResult nelderMead(const std::function<real(ArrayRef<const real>)>& functionToMinimize,
+                              ArrayRef<const real>                             initialGuess,
+                              real minimumRelativeSimplexLength = 1e-8,
+                              int  maxSteps                     = 10'000);
+
+} // namespace gmx
+#endif // GMX_MATH_OPTIMIZATION_H
index 79e5c1b08b8f53785213b8c12999e6a40e98aa70..6c199bc03e5c71d2126750836c8c800fdbd2c3a2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -372,13 +372,19 @@ public:
     {
         return ArrayRefWithPadding<const T>(data(), data() + size(), data() + paddedSize());
     }
-    //! Returns an rvec * pointer for containers of RVec, for use with legacy code.
+    /*! \brief Returns an rvec * pointer for containers of RVec, for use with legacy code.
+     *
+     * \todo Use std::is_same_v when CUDA 11 is a requirement.
+     */
     template<typename AlsoT = T, typename = typename std::enable_if<std::is_same<AlsoT, RVec>::value>>
     rvec* rvec_array()
     {
         return as_rvec_array(data());
     }
-    //! Returns a const rvec * pointer for containers of RVec, for use with legacy code.
+    /*! \brief Returns a const rvec * pointer for containers of RVec, for use with legacy code.
+     *
+     * \todo Use std::is_same_v when CUDA 11 is a requirement.
+     */
     template<typename AlsoT = T, typename = typename std::enable_if<std::is_same<AlsoT, RVec>::value>>
     const rvec* rvec_array() const
     {
index c55a804f2e9febc62d3f4acfee3dc18144c83a5b..199ecbf89131e6b881a5501e9d58dcbe0489cf3b 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(MathUnitTests math-test
-                  arrayrefwithpadding.cpp
-                  coordinatetransformation.cpp
-                  densityfit.cpp
-                  dofit.cpp
-                  exponentialmovingaverage.cpp
-                  functions.cpp
-                  gausstransform.cpp
-                  densityfittingforce.cpp
-                  invertmatrix.cpp
-                  matrix.cpp
-                  multidimarray.cpp
-                  paddedvector.cpp
-                  vectypes.cpp
-                  )
+    CPP_SOURCE_FILES
+        arrayrefwithpadding.cpp
+        coordinatetransformation.cpp
+        densityfit.cpp
+        dofit.cpp
+        exponentialmovingaverage.cpp
+        functions.cpp
+        gausstransform.cpp
+        densityfittingforce.cpp
+        invertmatrix.cpp
+        matrix.cpp
+        multidimarray.cpp
+        neldermead.cpp
+        optimization.cpp
+        paddedvector.cpp
+        vectypes.cpp
+        )
index 28533b42f2ea299b9e066161bc4b958bd4f7775a..19472d019afe21360bfcb83b583af322eef3e6ca 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,6 +48,9 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
 #include "testutils/testasserts.h"
 #include "testutils/testmatchers.h"
 
@@ -70,6 +73,17 @@ protected:
                                        { 3, -6, 2.5 } };
 };
 
+class AffineTransformationTest : public ::testing::Test
+{
+protected:
+    std::vector<RVec> testVectors_ = { { 0, 0, 0 },
+                                       { 1, 0, 0 },
+                                       { 0, -1, -1 },
+                                       { 1e10, 1e1, 1e-2 },
+                                       { 3, -6, 2.5 } };
+};
+
+
 TEST_F(TranslateAndScaleTest, identityTransformation)
 {
     TranslateAndScale translateAndScale(identityScale_, identityTranslation_);
@@ -108,6 +122,17 @@ TEST_F(TranslateAndScaleTest, translationAndScalingNonTrivial)
     EXPECT_THAT(expected, Pointwise(RVecEq(defaultFloatTolerance()), testVectors_));
 }
 
+TEST_F(TranslateAndScaleTest, translationAndScalingNonTrivialSingeVector)
+{
+    TranslateAndScale translateAndScale({ -1e10, 2, 0 }, { 1, -1, 2 });
+    RVec              test(0, 0, 0);
+    translateAndScale(&test);
+
+    EXPECT_REAL_EQ(-1e+10, test[XX]);
+    EXPECT_REAL_EQ(-2, test[YY]);
+    EXPECT_REAL_EQ(0, test[ZZ]);
+}
+
 TEST_F(TranslateAndScaleTest, scalingIdentity)
 {
     ScaleCoordinates scale(identityScale_);
@@ -126,6 +151,18 @@ TEST_F(TranslateAndScaleTest, scalingNonTrivial)
     EXPECT_THAT(expected, Pointwise(RVecEq(defaultFloatTolerance()), testVectors_));
 }
 
+TEST_F(TranslateAndScaleTest, scalingNonTrivialSingleVector)
+{
+    ScaleCoordinates scale({ -100, 0.1, 0 });
+    RVec             test(3, -6, 2.5);
+    scale(&test);
+
+    EXPECT_REAL_EQ(-300, test[XX]);
+    EXPECT_REAL_EQ(-0.6, test[YY]);
+    EXPECT_REAL_EQ(0, test[ZZ]);
+}
+
+
 TEST_F(TranslateAndScaleTest, scalingInverseNoZero)
 {
     ScaleCoordinates scale({ -100, 0.1, 3 });
@@ -144,5 +181,60 @@ TEST_F(TranslateAndScaleTest, scalingInverseWithOneScaleDimensionZero)
     EXPECT_THAT(expected, Pointwise(RVecEq(defaultFloatTolerance()), testVectors_));
 }
 
+TEST_F(TranslateAndScaleTest, scalingInverseWithOneScaleDimensionZeroSingleVector)
+{
+    ScaleCoordinates scale({ -100, 0.1, 0 });
+    RVec             test(3, -6, 2.5);
+
+    scale(&test);
+    scale.inverseIgnoringZeroScale(&test);
+
+    EXPECT_REAL_EQ(3, test[XX]);
+    EXPECT_REAL_EQ(-6, test[YY]);
+    EXPECT_REAL_EQ(0, test[ZZ]);
+}
+
+TEST_F(AffineTransformationTest, identityTransformYieldsSameVectors)
+{
+    const AffineTransformation identityTransformation(identityMatrix<real, 3>(), { 0, 0, 0 });
+    for (const auto& vector : testVectors_)
+    {
+        RVec vectorTransformed = vector;
+        identityTransformation(&vectorTransformed);
+        EXPECT_REAL_EQ(vector[XX], vectorTransformed[XX]);
+        EXPECT_REAL_EQ(vector[YY], vectorTransformed[YY]);
+        EXPECT_REAL_EQ(vector[ZZ], vectorTransformed[ZZ]);
+    }
+}
+
+TEST_F(AffineTransformationTest, applyTransformationToVectors)
+{
+    const Matrix3x3 transformMatrix({ 0.1, 1, 0.1, 0.4, 1, 0.6, 0.7, 0.8, 0.9 });
+    const RVec      transformVector = { 1, -1e5, 1e4 };
+
+    const AffineTransformation affineTransformation(transformMatrix, transformVector);
+
+    const std::vector<RVec> expectedResult = { { 1, -100'000, 10'000 },
+                                               { 1.1, -99999.6, 10000.7 },
+                                               { -0.1, -100'002, 9998.3 },
+                                               { 1e9, 3.9999e9, 7.00001e9 },
+                                               { -4.45, -100'003, 9'999.5 } };
+
+    auto expected = expectedResult.begin();
+    for (const auto& vector : testVectors_)
+    {
+        RVec vectorTransformed = vector;
+        affineTransformation(&vectorTransformed);
+        // need relaxed tolerance here, due to the number of operations involved
+        EXPECT_REAL_EQ_TOL((*expected)[XX], vectorTransformed[XX],
+                           relativeToleranceAsFloatingPoint((*expected)[XX], 1e-5));
+        EXPECT_REAL_EQ_TOL((*expected)[YY], vectorTransformed[YY],
+                           relativeToleranceAsFloatingPoint((*expected)[YY], 1e-5));
+        EXPECT_REAL_EQ_TOL((*expected)[ZZ], vectorTransformed[ZZ],
+                           relativeToleranceAsFloatingPoint((*expected)[ZZ], 1e-5));
+        ++expected;
+    }
+}
+
 } // namespace test
 } // namespace gmx
index fcd217dec6906fd8d772bdbd26d25b55ed2bdb8a..48da6183fe8ff0fa5bfc7a911f88b0d06b7ad589 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,6 +50,7 @@
 #include <gtest/gtest.h>
 
 #include "gromacs/math/vec.h"
+#include "gromacs/utility/real.h"
 
 #include "testutils/testasserts.h"
 
@@ -221,6 +222,25 @@ TEST_F(MatrixTest, canFillLegacyMatrix)
     }
 }
 
+TEST_F(MatrixTest, IdentityMatrix)
+{
+    const auto realIdMatrix = identityMatrix<real, 2>();
+    EXPECT_REAL_EQ(realIdMatrix(0, 0), 1);
+    EXPECT_REAL_EQ(realIdMatrix(1, 1), 1);
+    EXPECT_REAL_EQ(realIdMatrix(0, 1), 0);
+    EXPECT_REAL_EQ(realIdMatrix(1, 0), 0);
+}
+
+TEST_F(MatrixTest, MatrixVectorMultiplication)
+{
+    const Matrix3x3 matrix({ 0.1, 1, 0.1, 0.4, 1, 0.6, 0.7, 0.8, 0.9 });
+    RVec            vector(1, 2, 3);
+    matrixVectorMultiply(matrix, &vector);
+    EXPECT_REAL_EQ(2.4, vector[XX]);
+    EXPECT_REAL_EQ(4.2, vector[YY]);
+    EXPECT_REAL_EQ(5.0, vector[ZZ]);
+}
+
 } // namespace
 
 } // namespace test
diff --git a/src/gromacs/math/tests/neldermead.cpp b/src/gromacs/math/tests/neldermead.cpp
new file mode 100644 (file)
index 0000000..3a142ac
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Tests routines in neldermead.h .
+ *
+ * \author Christian Blau <blau@kth.se>
+ */
+
+#include "gmxpre.h"
+
+#include "gromacs/math/neldermead.h"
+
+#include <gtest/gtest.h>
+
+#include "testutils/testasserts.h"
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+
+/* \brief Test the NelderMeadSimplex class.
+ *
+ * Use a one-dimensional function to create a simplex with two vertices.
+ * The simplex is created at [1, 1.05] with function values [2, 2.1]
+ * respectively.
+ */
+class NelderMeadSimplexTest : public ::testing::Test
+{
+public:
+    //! Set up a one-d, two-vertices Nelder-Mead simplex
+    NelderMeadSimplexTest() :
+        initialGuess_{ 1 },
+        simplex_{ NelderMeadSimplexTest::doubleFirstCoordinateValue, initialGuess_ }
+    {
+    }
+
+    //! Function that the simplex is meant help optimise
+    static real doubleFirstCoordinateValue(ArrayRef<const real> x) { return 2 * x[0]; }
+
+protected:
+    std::vector<real> initialGuess_;
+    NelderMeadSimplex simplex_;
+};
+
+TEST_F(NelderMeadSimplexTest, BestVertex)
+{
+    EXPECT_REAL_EQ(simplex_.bestVertex().coordinate_[0], 1);
+    EXPECT_REAL_EQ(simplex_.bestVertex().value_, 2);
+}
+
+TEST_F(NelderMeadSimplexTest, WorstVertex)
+{
+    EXPECT_REAL_EQ(simplex_.worstVertex().coordinate_[0], 1.05);
+    EXPECT_REAL_EQ(simplex_.worstVertex().value_, 2.1);
+}
+
+TEST_F(NelderMeadSimplexTest, SecondWorstValue)
+{
+    EXPECT_REAL_EQ(simplex_.secondWorstValue(), 2.00);
+}
+
+TEST_F(NelderMeadSimplexTest, ReflectionPoint)
+{
+    EXPECT_REAL_EQ(
+            simplex_.evaluateReflectionPoint(NelderMeadSimplexTest::doubleFirstCoordinateValue).coordinate_[0],
+            0.95);
+    EXPECT_REAL_EQ(
+            simplex_.evaluateReflectionPoint(NelderMeadSimplexTest::doubleFirstCoordinateValue).value_, 1.9);
+}
+
+TEST_F(NelderMeadSimplexTest, EvaluateExpansionPoint)
+{
+    EXPECT_REAL_EQ(
+            simplex_.evaluateExpansionPoint(NelderMeadSimplexTest::doubleFirstCoordinateValue).coordinate_[0],
+            0.9);
+    EXPECT_REAL_EQ(
+            simplex_.evaluateExpansionPoint(NelderMeadSimplexTest::doubleFirstCoordinateValue).value_, 1.8);
+}
+
+TEST_F(NelderMeadSimplexTest, EvaluateContractionPoint)
+{
+    EXPECT_REAL_EQ(
+            simplex_.evaluateContractionPoint(NelderMeadSimplexTest::doubleFirstCoordinateValue).coordinate_[0],
+            1.025);
+    EXPECT_REAL_EQ(
+            simplex_.evaluateContractionPoint(NelderMeadSimplexTest::doubleFirstCoordinateValue).value_,
+            2.05);
+}
+
+
+TEST_F(NelderMeadSimplexTest, SwapOutWorst)
+{
+    // introduce a new vertex that we know is better than any in the
+    // intial simplex
+    RealFunctionvalueAtCoordinate newVertex = { { 0 }, 0 };
+    simplex_.swapOutWorst(newVertex);
+    EXPECT_REAL_EQ(simplex_.bestVertex().coordinate_[0], 0);
+    EXPECT_REAL_EQ(simplex_.bestVertex().value_, 0);
+    // introduce a new vertex that we know is worse than any in the
+    // intial simplex
+    newVertex = { { 3 }, 6 };
+    simplex_.swapOutWorst(newVertex);
+    EXPECT_REAL_EQ(simplex_.worstVertex().coordinate_[0], 3);
+    EXPECT_REAL_EQ(simplex_.worstVertex().value_, 6);
+    // check that also the reflection point has changed now
+    EXPECT_REAL_EQ(
+            simplex_.evaluateReflectionPoint(NelderMeadSimplexTest::doubleFirstCoordinateValue).coordinate_[0],
+            -3);
+    EXPECT_REAL_EQ(
+            simplex_.evaluateReflectionPoint(NelderMeadSimplexTest::doubleFirstCoordinateValue).value_, -6);
+}
+
+TEST_F(NelderMeadSimplexTest, ShrinkSimplexPointsExceptBest)
+{
+    simplex_.shrinkSimplexPointsExceptBest(NelderMeadSimplexTest::doubleFirstCoordinateValue);
+    EXPECT_REAL_EQ(simplex_.worstVertex().coordinate_[0], 1.025);
+    EXPECT_REAL_EQ(simplex_.worstVertex().value_, 2.05);
+    // check that also the reflection point has changed now
+    EXPECT_REAL_EQ(
+            simplex_.evaluateReflectionPoint(NelderMeadSimplexTest::doubleFirstCoordinateValue).coordinate_[0],
+            0.975);
+    EXPECT_REAL_EQ(
+            simplex_.evaluateReflectionPoint(NelderMeadSimplexTest::doubleFirstCoordinateValue).value_, 1.95);
+}
+
+TEST_F(NelderMeadSimplexTest, OrientedLength)
+{
+    // here, the distance between the two vertices
+    EXPECT_REAL_EQ(simplex_.orientedLength(), 0.05);
+}
+
+} // namespace
+} // namespace test
+} // namespace gmx
diff --git a/src/gromacs/math/tests/optimization.cpp b/src/gromacs/math/tests/optimization.cpp
new file mode 100644 (file)
index 0000000..2f31d7e
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Tests routines in neldermead.h .
+ *
+ * \author Christian Blau <blau@kth.se>
+ */
+
+#include "gmxpre.h"
+
+#include "gromacs/math/optimization.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/math/functions.h"
+
+#include "testutils/testasserts.h"
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+real mcCormick(ArrayRef<const real> x)
+{
+    return std::sin(x[0] + x[1]) + square(x[0] - x[1]) - 1.5_real * x[0] + 2.5_real * x[1] + 1._real;
+}
+
+struct RosenBrock3d
+{
+    real operator()(ArrayRef<const real> x)
+    {
+        return 100 * square(x[1] - square(x[0])) + square(1 - x[0])
+               + 100 * square(x[2] - square(x[1])) + square(1 - x[1]);
+    }
+};
+
+TEST(NelderMeat, Optimizes2DFunctionCorrectly)
+{
+    std::vector<real> initalPoint = { 1, 1 };
+    auto              result      = nelderMead(mcCormick, initalPoint);
+    EXPECT_REAL_EQ_TOL(result.functionValue_, -1.91329, relativeToleranceAsFloatingPoint(1, 5e-5));
+
+    initalPoint = { 0, 0 };
+    result      = nelderMead(mcCormick, initalPoint);
+    EXPECT_REAL_EQ_TOL(result.functionValue_, -1.91329, relativeToleranceAsFloatingPoint(1, 5e-5));
+}
+
+TEST(NelderMeat, Optimizes3DFunctorCorrectly)
+{
+    std::vector<real> initalPoint = { 0, 0, 0 };
+    auto              result      = nelderMead(RosenBrock3d(), initalPoint);
+    EXPECT_REAL_EQ_TOL(result.coordinates_[0], 1.00, relativeToleranceAsFloatingPoint(1, 1e-6));
+    EXPECT_REAL_EQ_TOL(result.coordinates_[1], 1.00, relativeToleranceAsFloatingPoint(1, 1e-6));
+    EXPECT_REAL_EQ_TOL(result.coordinates_[2], 1.00, relativeToleranceAsFloatingPoint(1, 1e-6));
+    EXPECT_REAL_EQ_TOL(result.functionValue_, 0, relativeToleranceAsFloatingPoint(1, 1e-7));
+}
+
+} // namespace
+} // namespace test
+} // namespace gmx
index 14b1c3b7637d8a679f3127420f2c1ccd0d6f10f2..963c2c31f10c69974d5bf7a8e734f1786f2aa32c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -105,6 +105,23 @@ TYPED_TEST(PaddedVectorTest, ConstructsResizesAndReserves)
     EXPECT_LE(v.paddedSize(), vReserved.paddedSize());
 }
 
+TYPED_TEST(PaddedVectorTest, ArrayRefConversionsAreIdentical)
+{
+    using VectorType = PaddedVector<typename TypeParam::value_type, TypeParam>;
+
+    VectorType v;
+    fillInput(&v, 1);
+
+    SCOPED_TRACE("Comparing different paths to create identical unpadded views");
+    compareViews(makeArrayRef(v), v.arrayRefWithPadding().unpaddedArrayRef());
+    compareViews(makeConstArrayRef(v), v.constArrayRefWithPadding().unpaddedConstArrayRef());
+    compareViews(makeConstArrayRef(v), v.constArrayRefWithPadding().unpaddedArrayRef());
+    compareViews(makeConstArrayRef(v), v.arrayRefWithPadding().unpaddedConstArrayRef());
+
+    SCOPED_TRACE("Comparing const to non-const unpadded views");
+    compareViewsIgnoreConst(makeArrayRef(v), makeConstArrayRef(v));
+}
+
 TYPED_TEST(PaddedVectorTest, CanCopyAssign)
 {
     using VectorType = PaddedVector<typename TypeParam::value_type, TypeParam>;
@@ -115,7 +132,7 @@ TYPED_TEST(PaddedVectorTest, CanCopyAssign)
 
     w = v;
     compareViews(v.arrayRefWithPadding().unpaddedArrayRef(), w.arrayRefWithPadding().unpaddedArrayRef());
-    compareViews(makeArrayRef(v), makeArrayRef(v));
+    compareViews(makeArrayRef(v), makeArrayRef(w));
 }
 
 TYPED_TEST(PaddedVectorTest, CanMoveAssign)
index 39da21cc114e92a3d45a58393a4bc03f798d0824..bdaae01d68b365c233727664b43a7555552a117a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -100,6 +100,18 @@ void compareViews(ArrayRef<T> input, ArrayRef<T> output)
     }
 }
 
+//! Comparison for non-BasicVector ignoring const qualifiers
+template<typename T, typename U>
+typename std::enable_if_t<std::is_same_v<std::remove_const_t<T>, std::remove_const_t<U>>, void>
+compareViewsIgnoreConst(ArrayRef<T> input, ArrayRef<U> output)
+{
+    ASSERT_EQ(input.size(), output.size());
+    for (index i = 0; i != input.ssize(); ++i)
+    {
+        EXPECT_EQ(input[i], output[i]) << "for index " << i;
+    }
+}
+
 //! Comparison overload for BasicVector<T>
 template<typename T>
 void compareViews(ArrayRef<BasicVector<T>> input, ArrayRef<BasicVector<T>> output)
index ed4d4f74e5752f8a32b28ab6227d7a6ccb1f022d..6881111e60613897049147da154d70721519a11c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,7 +49,7 @@ double convert2gmx(double x, int unit)
         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: return x * ONE_4PI_EPS0 / BOHR2NM; // NOLINT bugprone-branch-clone
         case eg2cHartree_e: return x * ONE_4PI_EPS0 / BOHR2NM;
         case eg2cAngstrom3: return x * A2NM * A2NM * A2NM;
         case eg2cCoulomb: return x / E_CHARGE;
@@ -68,7 +69,7 @@ double gmx2convert(double x, int unit)
         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: return x / (ONE_4PI_EPS0 / BOHR2NM); // NOLINT bugprone-branch-clone
         case eg2cHartree_e: return x / (ONE_4PI_EPS0 / BOHR2NM);
         case eg2cAngstrom3: return x / (A2NM * A2NM * A2NM);
         case eg2cCoulomb: return x * E_CHARGE;
index 68a45843ab3a86f013d5ac5fe0fdfb596f8600a2..3412f2c47208c4a6d591875ff40de9dab8e75e53 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,7 +46,6 @@
 #include <cmath>
 
 #include <algorithm>
-
 #include <cfenv>
 
 //! Floating point exception set that we use and care about
@@ -90,18 +90,6 @@ gmx_bool check_int_multiply_for_overflow(int64_t a, int64_t b, int64_t* result)
     return TRUE;
 }
 
-int gmx_greatest_common_divisor(int p, int q)
-{
-    int tmp;
-    while (q != 0)
-    {
-        tmp = q;
-        q   = p % q;
-        p   = tmp;
-    }
-    return p;
-}
-
 int gmx_feenableexcept()
 {
 #if HAVE_FEENABLEEXCEPT
@@ -152,15 +140,3 @@ int gmx_fedisableexcept()
     return -1;
 #endif
 }
-
-real max_cutoff(real cutoff1, real cutoff2)
-{
-    if (cutoff1 == 0 || cutoff2 == 0)
-    {
-        return 0;
-    }
-    else
-    {
-        return std::max(cutoff1, cutoff2);
-    }
-}
index fbf97184fd28548679f421f4804a062c9b19ebb2..5ff601d97cdfff969b386ae520fca04ec89be1fe 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -130,7 +131,7 @@ bool gmx_within_tol(double f1, double f2, double tol);
  * 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.
+ * \return True if 'almost' numerically zero, false otherwise.
  */
 bool gmx_numzero(double a);
 
@@ -140,13 +141,6 @@ bool gmx_numzero(double a);
  */
 gmx_bool check_int_multiply_for_overflow(int64_t a, int64_t b, int64_t* result);
 
-/*! \brief Find greatest common divisor of two numbers
- *
- * \return GCD of the two inputs
- */
-int gmx_greatest_common_divisor(int p, int q);
-
-
 /*! \brief Enable floating-point exceptions if supported on OS
  *
  * Enables division-by-zero, invalid value, and overflow.
@@ -163,15 +157,4 @@ int gmx_feenableexcept();
  */
 int gmx_fedisableexcept();
 
-/*! \brief Return cut-off to use
- *
- * Takes the max of two cut-offs. However a cut-off of 0
- * signifies that the cut-off in fact is infinite, and
- * this requires this special routine.
- * \param[in] cutoff1 The first cutoff (e.g. coulomb)
- * \param[in] cutoff2 The second cutoff (e.g. vdw)
- * \return 0 if either is 0, the normal max of the two otherwise.
- */
-real max_cutoff(real cutoff1, real cutoff2);
-
 #endif
index 61f51d8b9b3bdfd3fb0cd509bd3a17c5c127ec15..7666ec6d3309b5b8918a43be9b6f247d0b82f469 100644 (file)
@@ -292,9 +292,9 @@ static 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;
+    a[XX] = 0.0_real;
+    a[YY] = 0.0_real;
+    a[ZZ] = 0.0_real;
 }
 
 static inline void clear_dvec(dvec a)
index 95fc3cf0969ed5f90ec4e92f1853b8cb55881932..6e640d69ce6bf817197c6d03157692e4e0d22f2e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d3835d2049e1ab378a593a178aa9c02d4f988f04..d4d5211d11040df051f044d22918d33a4caf5588 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_MATH_VECTYPES_H
 #define GMX_MATH_VECTYPES_H
 
+#include <cassert>
 #include <cmath>
 
 #include <algorithm>
 #include <type_traits>
 
-#include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/real.h"
 
 #define XX 0 /* Defines for indexing in */
@@ -93,6 +94,8 @@ public:
     // of pointers, the implementation will be different enough that the whole
     // template class should have a separate partial specialization. We try to avoid
     // accidental matching to pointers, but this assertion is a no-cost extra check.
+    //
+    // TODO: Use std::is_pointer_v when CUDA 11 is a requirement.
     static_assert(!std::is_pointer<std::remove_cv_t<ValueType>>::value,
                   "BasicVector value type must not be a pointer.");
 
@@ -143,7 +146,7 @@ public:
     //! Allow vector scalar division
     BasicVector<ValueType> operator/(const ValueType& right) const
     {
-        GMX_ASSERT(right != 0, "Cannot divide by zero");
+        assert((right != 0 && "Cannot divide by zero"));
 
         return *this * (1 / right);
     }
@@ -159,7 +162,7 @@ public:
     //! Divide vector by a scalar
     BasicVector<ValueType>& operator/=(const ValueType& right)
     {
-        GMX_ASSERT(right != 0, "Cannot divide by zero");
+        assert((right != 0 && "Cannot divide by zero"));
 
         return *this *= 1 / right;
     }
@@ -181,7 +184,7 @@ public:
     BasicVector<ValueType> unitVector() const
     {
         const ValueType vectorNorm = norm();
-        GMX_ASSERT(vectorNorm != 0, "unitVector() should not be called with a zero vector");
+        assert((vectorNorm != 0 && "unitVector() should not be called with a zero vector"));
 
         return *this / vectorNorm;
     }
index e987cacfaa90cf787f0e6ff2f2ad30fa691dfdbf..6cb02ee51771b2c8388cbadeef6c23b1a213fb91 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -38,12 +39,13 @@ set(MDLIB_SOURCES ${MDLIB_SOURCES} PARENT_SCOPE)
 if (BUILD_TESTING)
     add_subdirectory(tests)
 endif()
-if(GMX_USE_CUDA)
+if(GMX_GPU_CUDA)
     gmx_add_libgromacs_sources(
-       lincs_cuda.cu
-       settle_cuda.cu
-       leapfrog_cuda.cu
-       update_constrain_cuda_impl.cu
+       leapfrog_gpu.cu
+       lincs_gpu.cu
+       settle_gpu.cu
+       update_constrain_gpu_impl.cu
+       gpuforcereduction_impl.cu
        )
 endif()
 
index 6adb621b64697718b315ae1f29c2b45351101349..e42a95fe46d7f15cdce072d993a8e520a6d475d9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
 
 namespace gmx
 {
 
 std::unique_ptr<BoxDeformation> prepareBoxDeformation(const matrix&     initialBox,
-                                                      t_commrec*        cr,
+                                                      DDRole            ddRole,
+                                                      NumRanks          numRanks,
+                                                      MPI_Comm          communicator,
                                                       const t_inputrec& inputrec)
 {
     if (!inputrecDeform(&inputrec))
@@ -80,13 +83,15 @@ std::unique_ptr<BoxDeformation> prepareBoxDeformation(const matrix&     initialB
     matrix box;
     // Only the rank that read the tpr has the global state, and thus
     // the initial box, so we pass that around.
-    if (SIMMASTER(cr))
+    // (numRanks != NumRanks::Multiple helps clang static analyzer to
+    // understand that box is defined in all cases)
+    if (ddRole == DDRole::Master || numRanks != NumRanks::Multiple)
     {
         copy_mat(initialBox, box);
     }
-    if (PAR(cr))
+    if (numRanks == NumRanks::Multiple)
     {
-        gmx_bcast(sizeof(box), box, cr);
+        gmx_bcast(sizeof(box), box, communicator);
     }
 
     return std::make_unique<BoxDeformation>(inputrec.delta_t, inputrec.init_step, inputrec.deform, box);
index 9280e8f2a2400e60745e5c26e12866a6ac1d4ff9..0eea78a5c28db13393dcdc84677da062b49ee035 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <memory>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxmpi.h"
 
-struct t_commrec;
 struct t_inputrec;
+enum class DDRole;
+enum class NumRanks;
 
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
 class BoxDeformation
 {
 public:
@@ -89,7 +92,9 @@ private:
  * unsupported combination.
  */
 std::unique_ptr<BoxDeformation> prepareBoxDeformation(const matrix&     initialBox,
-                                                      t_commrec*        cr,
+                                                      DDRole            ddRole,
+                                                      NumRanks          numRanks,
+                                                      MPI_Comm          communicator,
                                                       const t_inputrec& inputrec);
 
 } // namespace gmx
index 751bddf33a9612b3b4677d68a83af2aab567937c..4ab660a823ae573eabdb965cd87143a319bad844 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "broadcaststructs.h"
 
 #include "gromacs/fileio/tpxio.h"
-#include "gromacs/gmxlib/network.h"
-#include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/state.h"
 
 template<typename AllocatorType>
-static void bcastPaddedRVecVector(const t_commrec* cr, gmx::PaddedVector<gmx::RVec, AllocatorType>* v, int numAtoms)
+static void bcastPaddedRVecVector(MPI_Comm                                     communicator,
+                                  gmx::PaddedVector<gmx::RVec, AllocatorType>* v,
+                                  int                                          numAtoms)
 {
     v->resizeWithPadding(numAtoms);
-    nblock_bc(cr, makeArrayRef(*v));
+    nblock_bc(communicator, makeArrayRef(*v));
 }
 
-void broadcastStateWithoutDynamics(const t_commrec* cr, t_state* state)
+void broadcastStateWithoutDynamics(MPI_Comm communicator,
+                                   bool     useDomainDecomposition,
+                                   bool     isParallelRun,
+                                   t_state* state)
 {
-    GMX_RELEASE_ASSERT(!DOMAINDECOMP(cr),
+    GMX_RELEASE_ASSERT(!useDomainDecomposition,
                        "broadcastStateWithoutDynamics should only be used for special cases "
                        "without domain decomposition");
 
-    if (!PAR(cr))
+    if (!isParallelRun)
     {
         return;
     }
@@ -65,8 +69,8 @@ void broadcastStateWithoutDynamics(const t_commrec* cr, t_state* state)
     /* Broadcasts the state sizes and flags from the master to all ranks
      * in cr->mpi_comm_mygroup.
      */
-    block_bc(cr, state->natoms);
-    block_bc(cr, state->flags);
+    block_bc(communicator, state->natoms);
+    block_bc(communicator, state->flags);
 
     for (int i = 0; i < estNR; i++)
     {
@@ -74,10 +78,10 @@ void broadcastStateWithoutDynamics(const t_commrec* cr, t_state* state)
         {
             switch (i)
             {
-                case estLAMBDA: nblock_bc(cr, efptNR, state->lambda.data()); break;
-                case estFEPSTATE: block_bc(cr, state->fep_state); break;
-                case estBOX: block_bc(cr, state->box); break;
-                case estX: bcastPaddedRVecVector(cr, &state->x, state->natoms); break;
+                case estLAMBDA: nblock_bc(communicator, efptNR, state->lambda.data()); break;
+                case estFEPSTATE: block_bc(communicator, state->fep_state); break;
+                case estBOX: block_bc(communicator, state->box); break;
+                case estX: bcastPaddedRVecVector(communicator, &state->x, state->natoms); break;
                 default:
                     GMX_RELEASE_ASSERT(false,
                                        "The state has a dynamic entry, while no dynamic entries "
@@ -88,37 +92,41 @@ void broadcastStateWithoutDynamics(const t_commrec* cr, t_state* state)
     }
 }
 
-static void bc_tpxheader(const t_commrec* cr, TpxFileHeader* tpx)
+static void bc_tpxheader(MPI_Comm communicator, TpxFileHeader* tpx)
 {
-    block_bc(cr, tpx->bIr);
-    block_bc(cr, tpx->bBox);
-    block_bc(cr, tpx->bTop);
-    block_bc(cr, tpx->bX);
-    block_bc(cr, tpx->bV);
-    block_bc(cr, tpx->bF);
-    block_bc(cr, tpx->natoms);
-    block_bc(cr, tpx->ngtc);
-    block_bc(cr, tpx->lambda);
-    block_bc(cr, tpx->fep_state);
-    block_bc(cr, tpx->sizeOfTprBody);
-    block_bc(cr, tpx->fileVersion);
-    block_bc(cr, tpx->fileGeneration);
-    block_bc(cr, tpx->isDouble);
+    block_bc(communicator, tpx->bIr);
+    block_bc(communicator, tpx->bBox);
+    block_bc(communicator, tpx->bTop);
+    block_bc(communicator, tpx->bX);
+    block_bc(communicator, tpx->bV);
+    block_bc(communicator, tpx->bF);
+    block_bc(communicator, tpx->natoms);
+    block_bc(communicator, tpx->ngtc);
+    block_bc(communicator, tpx->lambda);
+    block_bc(communicator, tpx->fep_state);
+    block_bc(communicator, tpx->sizeOfTprBody);
+    block_bc(communicator, tpx->fileVersion);
+    block_bc(communicator, tpx->fileGeneration);
+    block_bc(communicator, tpx->isDouble);
 }
 
-static void bc_tprCharBuffer(const t_commrec* cr, std::vector<char>* charBuffer)
+static void bc_tprCharBuffer(MPI_Comm communicator, bool isMasterRank, std::vector<char>* charBuffer)
 {
     int elements = charBuffer->size();
-    block_bc(cr, elements);
+    block_bc(communicator, elements);
 
-    nblock_abc(cr, elements, charBuffer);
+    nblock_abc(isMasterRank, communicator, elements, charBuffer);
 }
 
-void init_parallel(t_commrec* cr, t_inputrec* inputrec, gmx_mtop_t* mtop, PartialDeserializedTprFile* partialDeserializedTpr)
+void init_parallel(MPI_Comm                    communicator,
+                   bool                        isMasterRank,
+                   t_inputrec*                 inputrec,
+                   gmx_mtop_t*                 mtop,
+                   PartialDeserializedTprFile* partialDeserializedTpr)
 {
-    bc_tpxheader(cr, &partialDeserializedTpr->header);
-    bc_tprCharBuffer(cr, &partialDeserializedTpr->body);
-    if (!MASTER(cr))
+    bc_tpxheader(communicator, &partialDeserializedTpr->header);
+    bc_tprCharBuffer(communicator, isMasterRank, &partialDeserializedTpr->body);
+    if (!isMasterRank)
     {
         completeTprDeserialization(partialDeserializedTpr, inputrec, mtop);
     }
index 877b39d31a83a3c9a47a78ed83d1e3adbbf27384..f8416edf20f85e9bdd6ca1c91d15d737f49936a8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/mdtypes/commrec.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/smalloc.h"
 
 struct gmx_mtop_t;
-struct t_commrec;
 struct t_inputrec;
 struct PartialDeserializedTprFile;
 class t_state;
 
-//! Convenience wrapper for gmx_bcast of a single value.
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+//! Convenience wrapper for gmx_bcast to communicator of a single value.
 template<typename T>
-void block_bc(const t_commrec* cr, T& data)
+void block_bc(MPI_Comm communicator, T& data)
 {
-    gmx_bcast(sizeof(T), static_cast<void*>(&data), cr);
+    gmx_bcast(sizeof(T), static_cast<void*>(&data), communicator);
 }
-//! Convenience wrapper for gmx_bcast of a C-style array.
+//! Convenience wrapper for gmx_bcast to communicator of a C-style array.
 template<typename T>
-void nblock_bc(const t_commrec* cr, int numElements, T* data)
+void nblock_bc(MPI_Comm communicator, int numElements, T* data)
 {
-    gmx_bcast(numElements * sizeof(T), static_cast<void*>(data), cr);
+    gmx_bcast(numElements * sizeof(T), static_cast<void*>(data), communicator);
 }
-//! Convenience wrapper for gmx_bcast of an ArrayRef<T>
+//! Convenience wrapper for gmx_bcast to communicator of an ArrayRef<T>
 template<typename T>
-void nblock_bc(const t_commrec* cr, gmx::ArrayRef<T> data)
+void nblock_bc(MPI_Comm communicator, gmx::ArrayRef<T> data)
 {
-    gmx_bcast(data.size() * sizeof(T), static_cast<void*>(data.data()), cr);
+    gmx_bcast(data.size() * sizeof(T), static_cast<void*>(data.data()), communicator);
 }
 //! Convenience wrapper for allocation with snew of vectors that need allocation on non-master ranks.
 template<typename T>
-void snew_bc(const t_commrec* cr, T*& data, int numElements)
+void snew_bc(bool isMasterRank, T*& data, int numElements)
 {
-    if (!MASTER(cr))
+    if (!isMasterRank)
     {
         snew(data, numElements);
     }
 }
 //! Convenience wrapper for gmx_bcast of a C-style array which needs allocation on non-master ranks.
 template<typename T>
-void nblock_abc(const t_commrec* cr, int numElements, T** v)
+void nblock_abc(bool isMasterRank, MPI_Comm communicator, int numElements, T** v)
 {
-    snew_bc(cr, v, numElements);
-    nblock_bc(cr, numElements, *v);
+    snew_bc(isMasterRank, v, numElements);
+    nblock_bc(communicator, numElements, *v);
 }
 //! Convenience wrapper for gmx_bcast of a std::vector which needs resizing on non-master ranks.
 template<typename T>
-void nblock_abc(const t_commrec* cr, int numElements, std::vector<T>* v)
+void nblock_abc(bool isMasterRank, MPI_Comm communicator, int numElements, std::vector<T>* v)
 {
-    if (!MASTER(cr))
+    if (!isMasterRank)
     {
         v->resize(numElements);
     }
-    gmx_bcast(numElements * sizeof(T), v->data(), cr);
+    gmx_bcast(numElements * sizeof(T), v->data(), communicator);
 }
 
 //! \brief Broadcasts the, non-dynamic, state from the master to all ranks in cr->mpi_comm_mygroup
 //
 // This is intended to be used with MPI parallelization without
 // domain decompostion (currently with NM and TPI).
-void broadcastStateWithoutDynamics(const t_commrec* cr, t_state* state);
+void broadcastStateWithoutDynamics(MPI_Comm communicator,
+                                   bool     useDomainDecomposition,
+                                   bool     isParallelRun,
+                                   t_state* state);
 
 //! \brief Broadcast inputrec and mtop and allocate node-specific settings
-void init_parallel(t_commrec*                  cr,
+void init_parallel(MPI_Comm                    communicator,
+                   bool                        isMasterRank,
                    t_inputrec*                 inputrec,
                    gmx_mtop_t*                 mtop,
                    PartialDeserializedTprFile* partialDeserializedTpr);
index 2a24be9977a3130f71ed6a73286ba8127d7fa3a2..90e9acf9275df0eb14a4ba6a3f2791c304240a50 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2012-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -1256,7 +1256,7 @@ static real chanceOfUpdateGroupCrossingCell(const gmx_moltype_t&          moltyp
             for (const int atom : block)
             {
                 const auto& ilist = moltype.ilist[F_SETTLE];
-                GMX_RELEASE_ASSERT(ilist.size() > 0,
+                GMX_RELEASE_ASSERT(!ilist.empty(),
                                    "There should be at least one settle in this moltype");
                 for (int i = 0; i < ilist.size(); i += 1 + NRAL(F_SETTLE))
                 {
index eafd6e80fcbe8dd82531087a6f3acfbeced5eb7b..a013cf2bf9aef0bf49cd541145ebf763b9d23331 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -36,7 +37,6 @@
 #ifndef GMX_MDLIB_CALC_VERLETBUF_H
 #define GMX_MDLIB_CALC_VERLETBUF_H
 
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
@@ -45,6 +45,8 @@ struct t_inputrec;
 
 namespace gmx
 {
+template<typename>
+class ArrayRef;
 class RangePartitioning;
 } // namespace gmx
 
index 4fe1eaa10450d39d36c945e49b81005a9bc873db..83a61bed785e557cc689eb8f72a3a03910ad0389 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/math/units.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
+#include "gromacs/utility/arrayref.h"
 
-void calc_mu(int                      start,
-             int                      homenr,
-             gmx::ArrayRef<gmx::RVec> x,
-             const real               q[],
-             const real               qB[],
-             int                      nChargePerturbed,
-             dvec                     mu,
-             dvec                     mu_B)
+void calc_mu(int                            start,
+             int                            homenr,
+             gmx::ArrayRef<const gmx::RVec> x,
+             const real                     q[],
+             const real                     qB[],
+             int                            nChargePerturbed,
+             dvec                           mu,
+             dvec                           mu_B)
 {
     int    end, m;
     double mu_x, mu_y, mu_z;
index fc75cc1469703922675d956221cd5eafcf240bc6..4081afc4efe16a84106468f45035c9a20ced6c20 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <stdio.h>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 
-void calc_mu(int                      start,
-             int                      homenr,
-             gmx::ArrayRef<gmx::RVec> x,
-             const real               q[],
-             const real               qB[],
-             int                      nChargePerturbed,
-             dvec                     mu,
-             dvec                     mu_B);
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+void calc_mu(int                            start,
+             int                            homenr,
+             gmx::ArrayRef<const gmx::RVec> x,
+             const real                     q[],
+             const real                     qB[],
+             int                            nChargePerturbed,
+             dvec                           mu,
+             dvec                           mu_B);
 
 gmx_bool read_mu(FILE* fp, rvec mu, real* vol);
 /* Return true on succes */
index a9d9f6b28dcb7bcefa42c0619546e3e47002dcb7..99398cff8404d42cc266860e9a01bd3c8157405a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,7 +48,6 @@
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/pbcutil/ishift.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/utility/gmxassert.h"
 
@@ -142,98 +142,7 @@ void calc_vir(int nxf, const rvec x[], const rvec f[], tensor vir, bool bScrewPB
     }
 }
 
-
-static void
-lo_fcv(int i0, int i1, const real x[], const real f[], tensor vir, const int is[], const real box[], gmx_bool bTriclinic)
-{
-    int  i, i3, tx, ty, tz;
-    real xx, yy, zz;
-    real dvxx = 0, dvxy = 0, dvxz = 0, dvyx = 0, dvyy = 0, dvyz = 0, dvzx = 0, dvzy = 0, dvzz = 0;
-
-    if (bTriclinic)
-    {
-        for (i = i0; (i < i1); i++)
-        {
-            i3 = DIM * i;
-            tx = is[i3 + XX];
-            ty = is[i3 + YY];
-            tz = is[i3 + ZZ];
-
-            xx = x[i3 + XX] - tx * box[XXXX] - ty * box[YYXX] - tz * box[ZZXX];
-            dvxx += xx * f[i3 + XX];
-            dvxy += xx * f[i3 + YY];
-            dvxz += xx * f[i3 + ZZ];
-
-            yy = x[i3 + YY] - ty * box[YYYY] - tz * box[ZZYY];
-            dvyx += yy * f[i3 + XX];
-            dvyy += yy * f[i3 + YY];
-            dvyz += yy * f[i3 + ZZ];
-
-            zz = x[i3 + ZZ] - tz * box[ZZZZ];
-            dvzx += zz * f[i3 + XX];
-            dvzy += zz * f[i3 + YY];
-            dvzz += zz * f[i3 + ZZ];
-        }
-    }
-    else
-    {
-        for (i = i0; (i < i1); i++)
-        {
-            i3 = DIM * i;
-            tx = is[i3 + XX];
-            ty = is[i3 + YY];
-            tz = is[i3 + ZZ];
-
-            xx = x[i3 + XX] - tx * box[XXXX];
-            dvxx += xx * f[i3 + XX];
-            dvxy += xx * f[i3 + YY];
-            dvxz += xx * f[i3 + ZZ];
-
-            yy = x[i3 + YY] - ty * box[YYYY];
-            dvyx += yy * f[i3 + XX];
-            dvyy += yy * f[i3 + YY];
-            dvyz += yy * f[i3 + ZZ];
-
-            zz = x[i3 + ZZ] - tz * box[ZZZZ];
-            dvzx += zz * f[i3 + XX];
-            dvzy += zz * f[i3 + YY];
-            dvzz += zz * f[i3 + ZZ];
-        }
-    }
-
-    upd_vir(vir[XX], dvxx, dvxy, dvxz);
-    upd_vir(vir[YY], dvyx, dvyy, dvyz);
-    upd_vir(vir[ZZ], dvzx, dvzy, dvzz);
-}
-
-void f_calc_vir(int i0, int i1, const rvec x[], const rvec f[], tensor vir, const t_graph* g, const matrix box)
+void f_calc_vir(int i0, int i1, const rvec x[], const rvec f[], tensor vir, const matrix box)
 {
-    int start, end;
-
-    if (g && (g->nnodes > 0))
-    {
-        /* Calculate virial for bonded forces only when they belong to
-         * this node.
-         */
-        start = std::max(i0, g->at_start);
-        end   = std::min(i1, g->at_end);
-        lo_fcv(start, end, x[0], f[0], vir, g->ishift[0], box[0], TRICLINIC(box));
-
-        /* If not all atoms are bonded, calculate their virial contribution
-         * anyway, without shifting back their coordinates.
-         * Note the nifty pointer arithmetic...
-         */
-        if (start > i0)
-        {
-            calc_vir(start - i0, x + i0, f + i0, vir, FALSE, box);
-        }
-        if (end < i1)
-        {
-            calc_vir(i1 - end, x + end, f + end, vir, FALSE, box);
-        }
-    }
-    else
-    {
-        calc_vir(i1 - i0, x + i0, f + i0, vir, FALSE, box);
-    }
+    calc_vir(i1 - i0, x + i0, f + i0, vir, FALSE, box);
 }
index c2eedbc289ab3a5c94946fd80e03d4605c1c5e81..1569c5bb7b2e6f187393506d9e1a69b62e553ea9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/math/vectypes.h"
 
-struct t_graph;
 struct t_pbc;
 
 void calc_vir(int nxf, const rvec x[], const rvec f[], tensor vir, bool bScrewPBC, const matrix box);
 /* Calculate virial for nxf atoms, and add it to vir */
 
-void f_calc_vir(int i0, int i1, const rvec x[], const rvec f[], tensor vir, const t_graph* g, const rvec shift_vec[]);
+void f_calc_vir(int i0, int i1, const rvec x[], const rvec f[], tensor vir, const rvec shift_vec[]);
 /* Calculate virial taking periodicity into account */
 
 #endif
index 32969441c1e0a54bd1bac2fe950fa99ca720bae9..0819f82ef7918b9bab13159848d815509974381d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index eccc21f1f07ba174969b664840d449d0fa5ff7e7..49d682320906e12184951599239dd2528767fb26 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pulling/pull.h"
 #include "gromacs/timing/wallcycle.h"
-#include "gromacs/topology/block.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/topology/mtop_lookup.h"
 #include "gromacs/topology/mtop_util.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/pleasecite.h"
-#include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/txtdump.h"
 
 namespace gmx
@@ -103,7 +101,6 @@ public:
          const t_inputrec&     ir_p,
          pull_t*               pull_work,
          FILE*                 log_p,
-         const t_mdatoms&      md_p,
          const t_commrec*      cr_p,
          const gmx_multisim_t* ms,
          t_nrnb*               nrnb,
@@ -112,35 +109,43 @@ public:
          int                   numConstraints,
          int                   numSettles);
     ~Impl();
-    void setConstraints(const gmx_localtop_t& top, const t_mdatoms& md);
-    bool apply(bool               bLog,
-               bool               bEner,
-               int64_t            step,
-               int                delta_step,
-               real               step_scaling,
-               rvec*              x,
-               rvec*              xprime,
-               rvec*              min_proj,
-               const matrix       box,
-               real               lambda,
-               real*              dvdlambda,
-               rvec*              v,
-               tensor*            vir,
-               ConstraintVariable econq);
+    void setConstraints(gmx_localtop_t* top,
+                        int             numAtoms,
+                        int             numHomeAtoms,
+                        real*           masses,
+                        real*           inverseMasses,
+                        bool            hasMassPerturbedAtoms,
+                        real            lambda,
+                        unsigned short* cFREEZE);
+    bool apply(bool                      bLog,
+               bool                      bEner,
+               int64_t                   step,
+               int                       delta_step,
+               real                      step_scaling,
+               ArrayRefWithPadding<RVec> x,
+               ArrayRefWithPadding<RVec> xprime,
+               ArrayRef<RVec>            min_proj,
+               const matrix              box,
+               real                      lambda,
+               real*                     dvdlambda,
+               ArrayRefWithPadding<RVec> v,
+               bool                      computeVirial,
+               tensor                    constraintsVirial,
+               ConstraintVariable        econq);
     //! The total number of constraints.
     int ncon_tot = 0;
     //! The number of flexible constraints.
     int nflexcon = 0;
     //! A list of atoms to constraints for each moleculetype.
-    std::vector<t_blocka> at2con_mt;
+    std::vector<ListOfLists<int>> at2con_mt;
     //! A list of atoms to settles for each moleculetype
     std::vector<std::vector<int>> at2settle_mt;
     //! LINCS data.
     Lincs* lincsd = nullptr; // TODO this should become a unique_ptr
     //! SHAKE data.
-    shakedata* shaked = nullptr;
+    std::unique_ptr<shakedata> shaked;
     //! SETTLE data.
-    settledata* settled = nullptr;
+    std::unique_ptr<SettleData> settled;
     //! The maximum number of warnings.
     int maxwarn = 0;
     //! The number of warnings for LINCS.
@@ -151,16 +156,28 @@ public:
     gmx_edsam* ed = nullptr;
 
     //! Thread-local virial contribution.
-    tensor* vir_r_m_dr_th = { nullptr };
+    tensor* threadConstraintsVirial = { nullptr };
     //! Did a settle error occur?
     bool* bSettleErrorHasOccurred = nullptr;
 
     //! Pointer to the global topology - only used for printing warnings.
     const gmx_mtop_t& mtop;
     //! Parameters for the interactions in this domain.
-    const t_idef* idef = nullptr;
-    //! Data about atoms in this domain.
-    const t_mdatoms& md;
+    const InteractionDefinitions* idef = nullptr;
+    //! Total number of atoms.
+    int numAtoms_ = 0;
+    //! Number of local atoms.
+    int numHomeAtoms_ = 0;
+    //! Atoms masses.
+    real* masses_;
+    //! Inverse masses.
+    real* inverseMasses_;
+    //! If there are atoms with perturbed mass (for FEP).
+    bool hasMassPerturbedAtoms_;
+    //! FEP lambda value.
+    real lambda_;
+    //! Freeze groups data
+    unsigned short* cFREEZE_;
     //! Whether we need to do pbc for handling bonds.
     bool pbcHandlingRequired_ = false;
 
@@ -206,7 +223,7 @@ bool Constraints::havePerturbedConstraints() const
 }
 
 //! Clears constraint quantities for atoms in nonlocal region.
-static void clear_constraint_quantity_nonlocal(gmx_domdec_t* dd, rvec* q)
+static void clear_constraint_quantity_nonlocal(gmx_domdec_t* dd, ArrayRef<RVec> q)
 {
     int nonlocal_at_start, nonlocal_at_end, at;
 
@@ -231,14 +248,14 @@ void too_many_constraint_warnings(int eConstrAlg, int warncount)
 }
 
 //! Writes out coordinates.
-static void write_constr_pdb(const char*       fn,
-                             const char*       title,
-                             const gmx_mtop_t& mtop,
-                             int               start,
-                             int               homenr,
-                             const t_commrec*  cr,
-                             const rvec        x[],
-                             const matrix      box)
+static void write_constr_pdb(const char*          fn,
+                             const char*          title,
+                             const gmx_mtop_t&    mtop,
+                             int                  start,
+                             int                  homenr,
+                             const t_commrec*     cr,
+                             ArrayRef<const RVec> x,
+                             const matrix         box)
 {
     char          fname[STRLEN];
     FILE*         out;
@@ -267,7 +284,7 @@ static void write_constr_pdb(const char*       fn,
     out = gmx_fio_fopen(fname, "w");
 
     fprintf(out, "TITLE     %s\n", title);
-    gmx_write_pdb_box(out, -1, box);
+    gmx_write_pdb_box(out, PbcType::Unset, box);
     int molb = 0;
     for (i = start; i < start + homenr; i++)
     {
@@ -293,15 +310,15 @@ static void write_constr_pdb(const char*       fn,
 }
 
 //! Writes out domain contents to help diagnose crashes.
-static void dump_confs(FILE*             log,
-                       int64_t           step,
-                       const gmx_mtop_t& mtop,
-                       int               start,
-                       int               homenr,
-                       const t_commrec*  cr,
-                       const rvec        x[],
-                       rvec              xprime[],
-                       const matrix      box)
+static void dump_confs(FILE*                log,
+                       int64_t              step,
+                       const gmx_mtop_t&    mtop,
+                       int                  start,
+                       int                  homenr,
+                       const t_commrec*     cr,
+                       ArrayRef<const RVec> x,
+                       ArrayRef<const RVec> xprime,
+                       const matrix         box)
 {
     char buf[STRLEN], buf2[22];
 
@@ -322,49 +339,51 @@ static void dump_confs(FILE*             log,
     fprintf(stderr, "Wrote pdb files with previous and current coordinates\n");
 }
 
-bool Constraints::apply(bool               bLog,
-                        bool               bEner,
-                        int64_t            step,
-                        int                delta_step,
-                        real               step_scaling,
-                        rvec*              x,
-                        rvec*              xprime,
-                        rvec*              min_proj,
-                        const matrix       box,
-                        real               lambda,
-                        real*              dvdlambda,
-                        rvec*              v,
-                        tensor*            vir,
-                        ConstraintVariable econq)
+bool Constraints::apply(bool                      bLog,
+                        bool                      bEner,
+                        int64_t                   step,
+                        int                       delta_step,
+                        real                      step_scaling,
+                        ArrayRefWithPadding<RVec> x,
+                        ArrayRefWithPadding<RVec> xprime,
+                        ArrayRef<RVec>            min_proj,
+                        const matrix              box,
+                        real                      lambda,
+                        real*                     dvdlambda,
+                        ArrayRefWithPadding<RVec> v,
+                        bool                      computeVirial,
+                        tensor                    constraintsVirial,
+                        ConstraintVariable        econq)
 {
-    return impl_->apply(bLog, bEner, step, delta_step, step_scaling, x, xprime, min_proj, box,
-                        lambda, dvdlambda, v, vir, econq);
+    return impl_->apply(bLog, bEner, step, delta_step, step_scaling, std::move(x),
+                        std::move(xprime), min_proj, box, lambda, dvdlambda, std::move(v),
+                        computeVirial, constraintsVirial, econq);
 }
 
-bool Constraints::Impl::apply(bool               bLog,
-                              bool               bEner,
-                              int64_t            step,
-                              int                delta_step,
-                              real               step_scaling,
-                              rvec*              x,
-                              rvec*              xprime,
-                              rvec*              min_proj,
-                              const matrix       box,
-                              real               lambda,
-                              real*              dvdlambda,
-                              rvec*              v,
-                              tensor*            vir,
-                              ConstraintVariable econq)
+bool Constraints::Impl::apply(bool                      bLog,
+                              bool                      bEner,
+                              int64_t                   step,
+                              int                       delta_step,
+                              real                      step_scaling,
+                              ArrayRefWithPadding<RVec> x,
+                              ArrayRefWithPadding<RVec> xprime,
+                              ArrayRef<RVec>            min_proj,
+                              const matrix              box,
+                              real                      lambda,
+                              real*                     dvdlambda,
+                              ArrayRefWithPadding<RVec> v,
+                              bool                      computeVirial,
+                              tensor                    constraintsVirial,
+                              ConstraintVariable        econq)
 {
-    bool   bOK, bDump;
-    int    start, homenr;
-    tensor vir_r_m_dr;
-    real   scaled_delta_t;
-    real   invdt, vir_fac = 0, t;
-    int    nsettle;
-    t_pbc  pbc, *pbc_null;
-    char   buf[22];
-    int    nth;
+    bool  bOK, bDump;
+    int   start;
+    real  scaled_delta_t;
+    real  invdt, vir_fac = 0, t;
+    int   nsettle;
+    t_pbc pbc, *pbc_null;
+    char  buf[22];
+    int   nth;
 
     wallcycle_start(wcycle, ewcCONSTR);
 
@@ -379,8 +398,7 @@ bool Constraints::Impl::apply(bool               bLog,
     bOK   = TRUE;
     bDump = FALSE;
 
-    start  = 0;
-    homenr = md.homenr;
+    start = 0;
 
     scaled_delta_t = step_scaling * ir.delta_t;
 
@@ -403,12 +421,12 @@ bool Constraints::Impl::apply(bool               bLog,
         lambda += delta_step * ir.fepvals->delta_lambda;
     }
 
-    if (vir != nullptr)
+    if (computeVirial)
     {
-        clear_mat(vir_r_m_dr);
+        clear_mat(constraintsVirial);
     }
-    const t_ilist* settle = &idef->il[F_SETTLE];
-    nsettle               = settle->nr / (1 + NRAL(F_SETTLE));
+    const InteractionList& settle = idef->il[F_SETTLE];
+    nsettle                       = settle.size() / (1 + NRAL(F_SETTLE));
 
     if (nsettle > 0)
     {
@@ -424,14 +442,14 @@ bool Constraints::Impl::apply(bool               bLog,
      * Note that PBC for constraints is different from PBC for bondeds.
      * For constraints there is both forward and backward communication.
      */
-    if (ir.ePBC != epbcNONE && (cr->dd || pbcHandlingRequired_)
+    if (ir.pbcType != PbcType::No && (cr->dd || pbcHandlingRequired_)
         && !(cr->dd && cr->dd->constraint_comm == nullptr))
     {
         /* With pbc=screw the screw has been changed to a shift
          * by the constraint coordinate communication routine,
          * so that here we can use normal pbc.
          */
-        pbc_null = set_pbc_dd(&pbc, ir.ePBC, DOMAINDECOMP(cr) ? cr->dd->nc : nullptr, FALSE, box);
+        pbc_null = set_pbc_dd(&pbc, ir.pbcType, DOMAINDECOMP(cr) ? cr->dd->numCells : nullptr, FALSE, box);
     }
     else
     {
@@ -443,23 +461,25 @@ bool Constraints::Impl::apply(bool               bLog,
      */
     if (cr->dd)
     {
-        dd_move_x_constraints(cr->dd, box, x, xprime, econq == ConstraintVariable::Positions);
+        dd_move_x_constraints(cr->dd, box, x.unpaddedArrayRef(), xprime.unpaddedArrayRef(),
+                              econq == ConstraintVariable::Positions);
 
-        if (v != nullptr)
+        if (!v.empty())
         {
             /* We need to initialize the non-local components of v.
              * We never actually use these values, but we do increment them,
              * so we should avoid uninitialized variables and overflows.
              */
-            clear_constraint_quantity_nonlocal(cr->dd, v);
+            clear_constraint_quantity_nonlocal(cr->dd, v.unpaddedArrayRef());
         }
     }
 
     if (lincsd != nullptr)
     {
-        bOK = constrain_lincs(bLog || bEner, ir, step, lincsd, md, cr, ms, x, xprime, min_proj, box,
-                              pbc_null, lambda, dvdlambda, invdt, v, vir != nullptr, vir_r_m_dr,
-                              econq, nrnb, maxwarn, &warncount_lincs);
+        bOK = constrain_lincs(bLog || bEner, ir, step, lincsd, inverseMasses_, cr, ms, x, xprime,
+                              min_proj, box, pbc_null, hasMassPerturbedAtoms_, lambda, dvdlambda,
+                              invdt, v.unpaddedArrayRef(), computeVirial, constraintsVirial, econq,
+                              nrnb, maxwarn, &warncount_lincs);
         if (!bOK && maxwarn < INT_MAX)
         {
             if (log != nullptr)
@@ -473,8 +493,10 @@ bool Constraints::Impl::apply(bool               bLog,
 
     if (shaked != nullptr)
     {
-        bOK = constrain_shake(log, shaked, md.invmass, *idef, ir, x, xprime, min_proj, nrnb, lambda,
-                              dvdlambda, invdt, v, vir != nullptr, vir_r_m_dr, maxwarn < INT_MAX, econq);
+        bOK = constrain_shake(log, shaked.get(), inverseMasses_, *idef, ir, x.unpaddedArrayRef(),
+                              xprime.unpaddedArrayRef(), min_proj, pbc_null, nrnb, lambda,
+                              dvdlambda, invdt, v.unpaddedArrayRef(), computeVirial,
+                              constraintsVirial, maxwarn < INT_MAX, econq);
 
         if (!bOK && maxwarn < INT_MAX)
         {
@@ -501,21 +523,21 @@ bool Constraints::Impl::apply(bool               bLog,
                     {
                         if (th > 0)
                         {
-                            clear_mat(vir_r_m_dr_th[th]);
+                            clear_mat(threadConstraintsVirial[th]);
                         }
 
-                        csettle(settled, nth, th, pbc_null, x[0], xprime[0], invdt, v ? v[0] : nullptr,
-                                vir != nullptr, th == 0 ? vir_r_m_dr : vir_r_m_dr_th[th],
+                        csettle(*settled, nth, th, pbc_null, x, xprime, invdt, v, computeVirial,
+                                th == 0 ? constraintsVirial : threadConstraintsVirial[th],
                                 th == 0 ? &bSettleErrorHasOccurred0 : &bSettleErrorHasOccurred[th]);
                     }
                     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
                 }
                 inc_nrnb(nrnb, eNR_SETTLE, nsettle);
-                if (v != nullptr)
+                if (!v.empty())
                 {
                     inc_nrnb(nrnb, eNR_CONSTR_V, nsettle * 3);
                 }
-                if (vir != nullptr)
+                if (computeVirial)
                 {
                     inc_nrnb(nrnb, eNR_CONSTR_VIR, nsettle * 3);
                 }
@@ -531,18 +553,18 @@ bool Constraints::Impl::apply(bool               bLog,
                     {
                         int calcvir_atom_end;
 
-                        if (vir == nullptr)
+                        if (!computeVirial)
                         {
                             calcvir_atom_end = 0;
                         }
                         else
                         {
-                            calcvir_atom_end = md.homenr;
+                            calcvir_atom_end = numHomeAtoms_;
                         }
 
                         if (th > 0)
                         {
-                            clear_mat(vir_r_m_dr_th[th]);
+                            clear_mat(threadConstraintsVirial[th]);
                         }
 
                         int start_th = (nsettle * th) / nth;
@@ -550,10 +572,11 @@ bool Constraints::Impl::apply(bool               bLog,
 
                         if (start_th >= 0 && end_th - start_th > 0)
                         {
-                            settle_proj(settled, econq, end_th - start_th,
-                                        settle->iatoms + start_th * (1 + NRAL(F_SETTLE)), pbc_null,
-                                        x, xprime, min_proj, calcvir_atom_end,
-                                        th == 0 ? vir_r_m_dr : vir_r_m_dr_th[th]);
+                            settle_proj(*settled, econq, end_th - start_th,
+                                        settle.iatoms.data() + start_th * (1 + NRAL(F_SETTLE)),
+                                        pbc_null, x.unpaddedArrayRef(), xprime.unpaddedArrayRef(),
+                                        min_proj, calcvir_atom_end,
+                                        th == 0 ? constraintsVirial : threadConstraintsVirial[th]);
                         }
                     }
                     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
@@ -567,12 +590,12 @@ bool Constraints::Impl::apply(bool               bLog,
             default: gmx_incons("Unknown constraint quantity for settle");
         }
 
-        if (vir != nullptr)
+        if (computeVirial)
         {
             /* Reduce the virial contributions over the threads */
             for (int th = 1; th < nth; th++)
             {
-                m_add(vir_r_m_dr, vir_r_m_dr_th[th], vir_r_m_dr);
+                m_add(constraintsVirial, threadConstraintsVirial[th], constraintsVirial);
             }
         }
 
@@ -609,7 +632,7 @@ bool Constraints::Impl::apply(bool               bLog,
         }
     }
 
-    if (vir != nullptr)
+    if (computeVirial)
     {
         /* The normal uses of constrain() pass step_scaling = 1.0.
          * The call to constrain() for SD1 that passes step_scaling =
@@ -636,14 +659,15 @@ bool Constraints::Impl::apply(bool               bLog,
         {
             for (int j = 0; j < DIM; j++)
             {
-                (*vir)[i][j] = vir_fac * vir_r_m_dr[i][j];
+                constraintsVirial[i][j] *= vir_fac;
             }
         }
     }
 
     if (bDump)
     {
-        dump_confs(log, step, mtop, start, homenr, cr, x, xprime, box);
+        dump_confs(log, step, mtop, start, numHomeAtoms_, cr, x.unpaddedArrayRef(),
+                   xprime.unpaddedArrayRef(), box);
     }
 
     if (econq == ConstraintVariable::Positions)
@@ -658,40 +682,45 @@ bool Constraints::Impl::apply(bool               bLog,
             {
                 t = ir.init_t;
             }
-            set_pbc(&pbc, ir.ePBC, box);
-            pull_constraint(pull_work, &md, &pbc, cr, ir.delta_t, t, x, xprime, v, *vir);
+            set_pbc(&pbc, ir.pbcType, box);
+            pull_constraint(pull_work, masses_, &pbc, cr, ir.delta_t, t,
+                            as_rvec_array(x.unpaddedArrayRef().data()),
+                            as_rvec_array(xprime.unpaddedArrayRef().data()),
+                            as_rvec_array(v.unpaddedArrayRef().data()), constraintsVirial);
         }
         if (ed && delta_step > 0)
         {
             /* apply the essential dynamics constraints here */
-            do_edsam(&ir, step, cr, xprime, v, box, ed);
+            do_edsam(&ir, step, cr, as_rvec_array(xprime.unpaddedArrayRef().data()),
+                     as_rvec_array(v.unpaddedArrayRef().data()), box, ed);
         }
     }
     wallcycle_stop(wcycle, ewcCONSTR);
 
-    if (v != nullptr && md.cFREEZE)
+    if (!v.empty() && cFREEZE_)
     {
         /* Set the velocities of frozen dimensions to zero */
+        ArrayRef<RVec> vRef = v.unpaddedArrayRef();
 
         int gmx_unused numThreads = gmx_omp_nthreads_get(emntUpdate);
 
 #pragma omp parallel for num_threads(numThreads) schedule(static)
-        for (int i = 0; i < md.homenr; i++)
+        for (int i = 0; i < numHomeAtoms_; i++)
         {
-            int freezeGroup = md.cFREEZE[i];
+            int freezeGroup = cFREEZE_[i];
 
             for (int d = 0; d < DIM; d++)
             {
                 if (ir.opts.nFreeze[freezeGroup][d])
                 {
-                    v[i][d] = 0;
+                    vRef[i][d] = 0;
                 }
             }
         }
     }
 
     return bOK;
-}
+} // namespace gmx
 
 ArrayRef<real> Constraints::rmsdData() const
 {
@@ -743,24 +772,26 @@ FlexibleConstraintTreatment flexibleConstraintTreatment(bool haveDynamicsIntegra
  * \param[in]  numAtoms  The number of atoms to construct the list for
  * \param[in]  ilists    The interaction lists, size F_NRE
  * \param[in]  iparams   Interaction parameters, can be null when
- * flexibleConstraintTreatment=Include \param[in]  flexibleConstraintTreatment  The flexible
- * constraint treatment, see enum above \returns a block struct with all constraints for each atom
+ *                       \p flexibleConstraintTreatment==Include
+ * \param[in]  flexibleConstraintTreatment  The flexible constraint treatment,
+ *                                          see enum above
+ *
+ * \returns a block struct with all constraints for each atom
  */
-template<typename T>
-static t_blocka makeAtomsToConstraintsList(int                         numAtoms,
-                                           const T*                    ilists,
-                                           const t_iparams*            iparams,
-                                           FlexibleConstraintTreatment flexibleConstraintTreatment)
+static ListOfLists<int> makeAtomsToConstraintsList(int                             numAtoms,
+                                                   ArrayRef<const InteractionList> ilists,
+                                                   ArrayRef<const t_iparams>       iparams,
+                                                   FlexibleConstraintTreatment flexibleConstraintTreatment)
 {
-    GMX_ASSERT(flexibleConstraintTreatment == FlexibleConstraintTreatment::Include || iparams != nullptr,
+    GMX_ASSERT(flexibleConstraintTreatment == FlexibleConstraintTreatment::Include || !iparams.empty(),
                "With flexible constraint detection we need valid iparams");
 
     std::vector<int> count(numAtoms);
 
     for (int ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
     {
-        const T&  ilist  = ilists[ftype];
-        const int stride = 1 + NRAL(ftype);
+        const InteractionList& ilist  = ilists[ftype];
+        const int              stride = 1 + NRAL(ftype);
         for (int i = 0; i < ilist.size(); i += stride)
         {
             if (flexibleConstraintTreatment == FlexibleConstraintTreatment::Include
@@ -775,19 +806,13 @@ static t_blocka makeAtomsToConstraintsList(int                         numAtoms,
         }
     }
 
-    t_blocka at2con;
-    at2con.nr           = numAtoms;
-    at2con.nalloc_index = at2con.nr + 1;
-    snew(at2con.index, at2con.nalloc_index);
-    at2con.index[0] = 0;
+    std::vector<int> listRanges(numAtoms + 1);
     for (int a = 0; a < numAtoms; a++)
     {
-        at2con.index[a + 1] = at2con.index[a] + count[a];
-        count[a]            = 0;
+        listRanges[a + 1] = listRanges[a] + count[a];
+        count[a]          = 0;
     }
-    at2con.nra      = at2con.index[at2con.nr];
-    at2con.nalloc_a = at2con.nra;
-    snew(at2con.a, at2con.nalloc_a);
+    std::vector<int> elements(listRanges[numAtoms]);
 
     /* The F_CONSTRNC constraints have constraint numbers
      * that continue after the last F_CONSTR constraint.
@@ -795,8 +820,8 @@ static t_blocka makeAtomsToConstraintsList(int                         numAtoms,
     int numConstraints = 0;
     for (int ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
     {
-        const T&  ilist  = ilists[ftype];
-        const int stride = 1 + NRAL(ftype);
+        const InteractionList& ilist  = ilists[ftype];
+        const int              stride = 1 + NRAL(ftype);
         for (int i = 0; i < ilist.size(); i += stride)
         {
             if (flexibleConstraintTreatment == FlexibleConstraintTreatment::Include
@@ -804,36 +829,35 @@ static t_blocka makeAtomsToConstraintsList(int                         numAtoms,
             {
                 for (int j = 1; j < 3; j++)
                 {
-                    int a                                  = ilist.iatoms[i + j];
-                    at2con.a[at2con.index[a] + count[a]++] = numConstraints;
+                    const int a                          = ilist.iatoms[i + j];
+                    elements[listRanges[a] + count[a]++] = numConstraints;
                 }
             }
             numConstraints++;
         }
     }
 
-    return at2con;
+    return ListOfLists<int>(std::move(listRanges), std::move(elements));
 }
 
-t_blocka make_at2con(int                         numAtoms,
-                     const t_ilist*              ilist,
-                     const t_iparams*            iparams,
-                     FlexibleConstraintTreatment flexibleConstraintTreatment)
+ListOfLists<int> make_at2con(int                             numAtoms,
+                             ArrayRef<const InteractionList> ilist,
+                             ArrayRef<const t_iparams>       iparams,
+                             FlexibleConstraintTreatment     flexibleConstraintTreatment)
 {
     return makeAtomsToConstraintsList(numAtoms, ilist, iparams, flexibleConstraintTreatment);
 }
 
-t_blocka make_at2con(const gmx_moltype_t&           moltype,
-                     gmx::ArrayRef<const t_iparams> iparams,
-                     FlexibleConstraintTreatment    flexibleConstraintTreatment)
+ListOfLists<int> make_at2con(const gmx_moltype_t&           moltype,
+                             gmx::ArrayRef<const t_iparams> iparams,
+                             FlexibleConstraintTreatment    flexibleConstraintTreatment)
 {
-    return makeAtomsToConstraintsList(moltype.atoms.nr, moltype.ilist.data(), iparams.data(),
+    return makeAtomsToConstraintsList(moltype.atoms.nr, makeConstArrayRef(moltype.ilist), iparams,
                                       flexibleConstraintTreatment);
 }
 
 //! Return the number of flexible constraints in the \c ilist and \c iparams.
-template<typename T>
-static int countFlexibleConstraintsTemplate(const T* ilist, const t_iparams* iparams)
+int countFlexibleConstraints(ArrayRef<const InteractionList> ilist, ArrayRef<const t_iparams> iparams)
 {
     int nflexcon = 0;
     for (int ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
@@ -852,11 +876,6 @@ static int countFlexibleConstraintsTemplate(const T* ilist, const t_iparams* ipa
     return nflexcon;
 }
 
-int countFlexibleConstraints(const t_ilist* ilist, const t_iparams* iparams)
-{
-    return countFlexibleConstraintsTemplate(ilist, iparams);
-}
-
 //! Returns the index of the settle to which each atom belongs.
 static std::vector<int> make_at2settle(int natoms, const InteractionList& ilist)
 {
@@ -875,9 +894,24 @@ static std::vector<int> make_at2settle(int natoms, const InteractionList& ilist)
     return at2s;
 }
 
-void Constraints::Impl::setConstraints(const gmx_localtop_t& top, const t_mdatoms& md)
+void Constraints::Impl::setConstraints(gmx_localtop_t* top,
+                                       int             numAtoms,
+                                       int             numHomeAtoms,
+                                       real*           masses,
+                                       real*           inverseMasses,
+                                       const bool      hasMassPerturbedAtoms,
+                                       const real      lambda,
+                                       unsigned short* cFREEZE)
 {
-    idef = &top.idef;
+    numAtoms_              = numAtoms;
+    numHomeAtoms_          = numHomeAtoms;
+    masses_                = masses;
+    inverseMasses_         = inverseMasses;
+    hasMassPerturbedAtoms_ = hasMassPerturbedAtoms;
+    lambda_                = lambda;
+    cFREEZE_               = cFREEZE;
+
+    idef = &top->idef;
 
     if (ncon_tot > 0)
     {
@@ -886,7 +920,7 @@ void Constraints::Impl::setConstraints(const gmx_localtop_t& top, const t_mdatom
          */
         if (ir.eConstrAlg == econtLINCS)
         {
-            set_lincs(top.idef, md, EI_DYNAMICS(ir.eI), cr, lincsd);
+            set_lincs(*idef, numAtoms_, inverseMasses_, lambda_, EI_DYNAMICS(ir.eI), cr, lincsd);
         }
         if (ir.eConstrAlg == econtSHAKE)
         {
@@ -894,18 +928,20 @@ void Constraints::Impl::setConstraints(const gmx_localtop_t& top, const t_mdatom
             {
                 // We are using the local topology, so there are only
                 // F_CONSTR constraints.
-                make_shake_sblock_dd(shaked, &idef->il[F_CONSTR], cr->dd);
+                GMX_RELEASE_ASSERT(idef->il[F_CONSTRNC].empty(),
+                                   "Here we should not have no-connect constraints");
+                make_shake_sblock_dd(shaked.get(), idef->il[F_CONSTR]);
             }
             else
             {
-                make_shake_sblock_serial(shaked, idef, md);
+                make_shake_sblock_serial(shaked.get(), &top->idef, numAtoms_);
             }
         }
     }
 
     if (settled)
     {
-        settle_set_constraints(settled, &idef->il[F_SETTLE], md);
+        settled->setConstraints(idef->il[F_SETTLE], numHomeAtoms_, masses_, inverseMasses_);
     }
 
     /* Make a selection of the local atoms for essential dynamics */
@@ -915,19 +951,27 @@ void Constraints::Impl::setConstraints(const gmx_localtop_t& top, const t_mdatom
     }
 }
 
-void Constraints::setConstraints(const gmx_localtop_t& top, const t_mdatoms& md)
+void Constraints::setConstraints(gmx_localtop_t* top,
+                                 const int       numAtoms,
+                                 const int       numHomeAtoms,
+                                 real*           masses,
+                                 real*           inverseMasses,
+                                 const bool      hasMassPerturbedAtoms,
+                                 const real      lambda,
+                                 unsigned short* cFREEZE)
 {
-    impl_->setConstraints(top, md);
+    impl_->setConstraints(top, numAtoms, numHomeAtoms, masses, inverseMasses, hasMassPerturbedAtoms,
+                          lambda, cFREEZE);
 }
 
 /*! \brief Makes a per-moleculetype container of mappings from atom
  * indices to constraint indices.
  *
  * Note that flexible constraints are only enabled with a dynamical integrator. */
-static std::vector<t_blocka> makeAtomToConstraintMappings(const gmx_mtop_t& mtop,
-                                                          FlexibleConstraintTreatment flexibleConstraintTreatment)
+static std::vector<ListOfLists<int>> makeAtomToConstraintMappings(const gmx_mtop_t& mtop,
+                                                                  FlexibleConstraintTreatment flexibleConstraintTreatment)
 {
-    std::vector<t_blocka> mapping;
+    std::vector<ListOfLists<int>> mapping;
     mapping.reserve(mtop.moltype.size());
     for (const gmx_moltype_t& moltype : mtop.moltype)
     {
@@ -940,7 +984,6 @@ Constraints::Constraints(const gmx_mtop_t&     mtop,
                          const t_inputrec&     ir,
                          pull_t*               pull_work,
                          FILE*                 log,
-                         const t_mdatoms&      md,
                          const t_commrec*      cr,
                          const gmx_multisim_t* ms,
                          t_nrnb*               nrnb,
@@ -948,7 +991,7 @@ Constraints::Constraints(const gmx_mtop_t&     mtop,
                          bool                  pbcHandlingRequired,
                          int                   numConstraints,
                          int                   numSettles) :
-    impl_(new Impl(mtop, ir, pull_work, log, md, cr, ms, nrnb, wcycle, pbcHandlingRequired, numConstraints, numSettles))
+    impl_(new Impl(mtop, ir, pull_work, log, cr, ms, nrnb, wcycle, pbcHandlingRequired, numConstraints, numSettles))
 {
 }
 
@@ -956,7 +999,6 @@ Constraints::Impl::Impl(const gmx_mtop_t&     mtop_p,
                         const t_inputrec&     ir_p,
                         pull_t*               pull_work,
                         FILE*                 log_p,
-                        const t_mdatoms&      md_p,
                         const t_commrec*      cr_p,
                         const gmx_multisim_t* ms_p,
                         t_nrnb*               nrnb_p,
@@ -966,7 +1008,6 @@ Constraints::Impl::Impl(const gmx_mtop_t&     mtop_p,
                         int                   numSettles) :
     ncon_tot(numConstraints),
     mtop(mtop_p),
-    md(md_p),
     pbcHandlingRequired_(pbcHandlingRequired),
     log(log_p),
     cr(cr_p),
@@ -988,8 +1029,7 @@ Constraints::Impl::Impl(const gmx_mtop_t&     mtop_p,
 
         for (const gmx_molblock_t& molblock : mtop.molblock)
         {
-            int count = countFlexibleConstraintsTemplate(mtop.moltype[molblock.type].ilist.data(),
-                                                         mtop.ffparams.iparams.data());
+            int count = countFlexibleConstraints(mtop.moltype[molblock.type].ilist, mtop.ffparams.iparams);
             nflexcon += molblock.nmol * count;
         }
 
@@ -1043,7 +1083,7 @@ Constraints::Impl::Impl(const gmx_mtop_t&     mtop_p,
                 please_cite(log, "Barth95a");
             }
 
-            shaked = shake_init();
+            shaked = std::make_unique<shakedata>();
         }
     }
 
@@ -1051,7 +1091,7 @@ Constraints::Impl::Impl(const gmx_mtop_t&     mtop_p,
     {
         please_cite(log, "Miyamoto92a");
 
-        settled = settle_init(mtop);
+        settled = std::make_unique<SettleData>(mtop);
 
         /* Make an atom to settle index for use in domain decomposition */
         for (size_t mt = 0; mt < mtop.moltype.size(); mt++)
@@ -1062,9 +1102,9 @@ Constraints::Impl::Impl(const gmx_mtop_t&     mtop_p,
 
         /* Allocate thread-local work arrays */
         int nthreads = gmx_omp_nthreads_get(emntSETTLE);
-        if (nthreads > 1 && vir_r_m_dr_th == nullptr)
+        if (nthreads > 1 && threadConstraintsVirial == nullptr)
         {
-            snew(vir_r_m_dr_th, nthreads);
+            snew(threadConstraintsVirial, nthreads);
             snew(bSettleErrorHasOccurred, nthreads);
         }
     }
@@ -1094,21 +1134,13 @@ Constraints::Impl::Impl(const gmx_mtop_t&     mtop_p,
 
 Constraints::Impl::~Impl()
 {
-    for (auto blocka : at2con_mt)
-    {
-        done_blocka(&blocka);
-    }
     if (bSettleErrorHasOccurred != nullptr)
     {
         sfree(bSettleErrorHasOccurred);
     }
-    if (vir_r_m_dr_th != nullptr)
+    if (threadConstraintsVirial != nullptr)
     {
-        sfree(vir_r_m_dr_th);
-    }
-    if (settled != nullptr)
-    {
-        settle_free(settled);
+        sfree(threadConstraintsVirial);
     }
     done_lincs(lincsd);
 }
@@ -1118,7 +1150,7 @@ void Constraints::saveEdsamPointer(gmx_edsam* ed)
     impl_->ed = ed;
 }
 
-ArrayRef<const t_blocka> Constraints::atom2constraints_moltype() const
+ArrayRef<const ListOfLists<int>> Constraints::atom2constraints_moltype() const
 {
     return impl_->at2con_mt;
 }
@@ -1131,8 +1163,8 @@ ArrayRef<const std::vector<int>> Constraints::atom2settle_moltype() const
 void do_constrain_first(FILE*                     fplog,
                         gmx::Constraints*         constr,
                         const t_inputrec*         ir,
-                        const t_mdatoms*          md,
-                        int                       natoms,
+                        int                       numAtoms,
+                        int                       numHomeAtoms,
                         ArrayRefWithPadding<RVec> x,
                         ArrayRefWithPadding<RVec> v,
                         const matrix              box,
@@ -1142,22 +1174,15 @@ void do_constrain_first(FILE*                     fplog,
     int64_t step;
     real    dt = ir->delta_t;
     real    dvdl_dum;
-    rvec*   savex;
-
-    auto xRvec = as_rvec_array(x.paddedArrayRef().data());
-    auto vRvec = as_rvec_array(v.paddedArrayRef().data());
 
-    /* We need to allocate one element extra, since we might use
-     * (unaligned) 4-wide SIMD loads to access rvec entries.
-     */
-    snew(savex, natoms + 1);
+    PaddedVector<RVec> savex(numAtoms);
 
     start = 0;
-    end   = md->homenr;
+    end   = numHomeAtoms;
 
     if (debug)
     {
-        fprintf(debug, "vcm: start=%d, homenr=%d, end=%d\n", start, md->homenr, end);
+        fprintf(debug, "vcm: start=%d, homenr=%d, end=%d\n", start, numHomeAtoms, end);
     }
     /* Do a first constrain to reset particles... */
     step = ir->init_step;
@@ -1168,15 +1193,18 @@ void do_constrain_first(FILE*                     fplog,
     }
     dvdl_dum = 0;
 
+    bool needsLogging  = true;
+    bool computeEnergy = false;
+    bool computeVirial = false;
     /* constrain the current position */
-    constr->apply(TRUE, FALSE, step, 0, 1.0, xRvec, xRvec, nullptr, box, lambda, &dvdl_dum, nullptr,
-                  nullptr, gmx::ConstraintVariable::Positions);
+    constr->apply(needsLogging, computeEnergy, step, 0, 1.0, x, x, {}, box, lambda, &dvdl_dum, {},
+                  computeVirial, nullptr, gmx::ConstraintVariable::Positions);
     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 */
-        constr->apply(TRUE, FALSE, step, 0, 1.0, xRvec, vRvec, vRvec, box, lambda, &dvdl_dum,
-                      nullptr, nullptr, gmx::ConstraintVariable::Velocities);
+        constr->apply(needsLogging, computeEnergy, step, 0, 1.0, x, v, v.unpaddedArrayRef(), box, lambda,
+                      &dvdl_dum, {}, computeVirial, nullptr, gmx::ConstraintVariable::Velocities);
     }
     /* constrain the inital velocities at t-dt/2 */
     if (EI_STATE_VELOCITY(ir->eI) && ir->eI != eiVV)
@@ -1202,8 +1230,9 @@ void do_constrain_first(FILE*                     fplog,
             fprintf(fplog, "\nConstraining the coordinates at t0-dt (step %s)\n", gmx_step_str(step, buf));
         }
         dvdl_dum = 0;
-        constr->apply(TRUE, FALSE, step, -1, 1.0, xRvec, savex, nullptr, box, lambda, &dvdl_dum,
-                      vRvec, nullptr, gmx::ConstraintVariable::Positions);
+        constr->apply(needsLogging, computeEnergy, step, -1, 1.0, x, savex.arrayRefWithPadding(),
+                      {}, box, lambda, &dvdl_dum, v, computeVirial, nullptr,
+                      gmx::ConstraintVariable::Positions);
 
         for (i = start; i < end; i++)
         {
@@ -1214,7 +1243,43 @@ void do_constrain_first(FILE*                     fplog,
             }
         }
     }
-    sfree(savex);
+}
+
+void constrain_velocities(gmx::Constraints* constr,
+                          bool              do_log,
+                          bool              do_ene,
+                          int64_t           step,
+                          t_state*          state,
+                          real*             dvdlambda,
+                          gmx_bool          computeVirial,
+                          tensor            constraintsVirial)
+{
+    if (constr != nullptr)
+    {
+        constr->apply(do_log, do_ene, step, 1, 1.0, state->x.arrayRefWithPadding(),
+                      state->v.arrayRefWithPadding(), state->v.arrayRefWithPadding().unpaddedArrayRef(),
+                      state->box, state->lambda[efptBONDED], dvdlambda, ArrayRefWithPadding<RVec>(),
+                      computeVirial, constraintsVirial, ConstraintVariable::Velocities);
+    }
+}
+
+void constrain_coordinates(gmx::Constraints*         constr,
+                           bool                      do_log,
+                           bool                      do_ene,
+                           int64_t                   step,
+                           t_state*                  state,
+                           ArrayRefWithPadding<RVec> xp,
+                           real*                     dvdlambda,
+                           gmx_bool                  computeVirial,
+                           tensor                    constraintsVirial)
+{
+    if (constr != nullptr)
+    {
+        constr->apply(do_log, do_ene, step, 1, 1.0, state->x.arrayRefWithPadding(), std::move(xp),
+                      ArrayRef<RVec>(), state->box, state->lambda[efptBONDED], dvdlambda,
+                      state->v.arrayRefWithPadding(), computeVirial, constraintsVirial,
+                      ConstraintVariable::Positions);
+    }
 }
 
 } // namespace gmx
index 2ddc2704d1dc35d791deaa1acbff2b225dd416ca..1c9f41bd34afb84875fa44d00b849fdaec95305a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -63,11 +64,9 @@ struct gmx_mtop_t;
 struct gmx_multisim_t;
 struct gmx_wallcycle;
 struct pull_t;
-struct t_blocka;
 struct t_commrec;
 struct t_ilist;
 struct t_inputrec;
-struct t_mdatoms;
 struct t_nrnb;
 struct t_pbc;
 class t_state;
@@ -76,6 +75,8 @@ namespace gmx
 {
 template<typename T>
 class ArrayRefWithPadding;
+template<typename>
+class ListOfLists;
 
 //! Describes supported flavours of constrained updates.
 enum class ConstraintVariable : int
@@ -105,7 +106,6 @@ private:
                 const t_inputrec&     ir,
                 pull_t*               pull_work,
                 FILE*                 log,
-                const t_mdatoms&      md,
                 const t_commrec*      cr,
                 const gmx_multisim_t* ms,
                 t_nrnb*               nrnb,
@@ -132,12 +132,19 @@ public:
      *
      * \todo Make this a callback that is called automatically
      * once a new domain has been made. */
-    void setConstraints(const gmx_localtop_t& top, const t_mdatoms& md);
+    void setConstraints(gmx_localtop_t* top,
+                        int             numAtoms,
+                        int             numHomeAtoms,
+                        real*           masses,
+                        real*           inverseMasses,
+                        bool            hasMassPerturbedAtoms,
+                        real            lambda,
+                        unsigned short* cFREEZE);
 
     /*! \brief Applies constraints to coordinates.
      *
      * When econq=ConstraintVariable::Positions constrains
-     * coordinates xprime using th directions in x, min_proj is
+     * coordinates xprime using the directions in x, min_proj is
      * not used.
      *
      * When econq=ConstraintVariable::Derivative, calculates the
@@ -162,28 +169,31 @@ public:
      *
      * If v!=NULL also constrain v by adding the constraint corrections / dt.
      *
-     * If vir!=NULL calculate the constraint virial.
+     * If computeVirial is true, calculate the constraint virial.
      *
      * Return whether the application of constraints succeeded without error.
+     *
+     * /note x is non-const, because non-local atoms need to be communicated.
      */
-    bool apply(bool               bLog,
-               bool               bEner,
-               int64_t            step,
-               int                delta_step,
-               real               step_scaling,
-               rvec*              x,
-               rvec*              xprime,
-               rvec*              min_proj,
-               const matrix       box,
-               real               lambda,
-               real*              dvdlambda,
-               rvec*              v,
-               tensor*            vir,
-               ConstraintVariable econq);
+    bool apply(bool                      bLog,
+               bool                      bEner,
+               int64_t                   step,
+               int                       delta_step,
+               real                      step_scaling,
+               ArrayRefWithPadding<RVec> x,
+               ArrayRefWithPadding<RVec> xprime,
+               ArrayRef<RVec>            min_proj,
+               const matrix              box,
+               real                      lambda,
+               real*                     dvdlambda,
+               ArrayRefWithPadding<RVec> v,
+               bool                      computeVirial,
+               tensor                    constraintsVirial,
+               ConstraintVariable        econq);
     //! Links the essentialdynamics and constraint code.
     void saveEdsamPointer(gmx_edsam* ed);
     //! Getter for use by domain decomposition.
-    ArrayRef<const t_blocka> atom2constraints_moltype() const;
+    ArrayRef<const ListOfLists<int>> atom2constraints_moltype() const;
     //! Getter for use by domain decomposition.
     ArrayRef<const std::vector<int>> atom2settle_moltype() const;
 
@@ -211,10 +221,8 @@ private:
 [[noreturn]] void too_many_constraint_warnings(int eConstrAlg, int warncount);
 
 /*! \brief Returns whether constraint with parameter \p iparamsIndex is a flexible constraint */
-static inline bool isConstraintFlexible(const t_iparams* iparams, int iparamsIndex)
+static inline bool isConstraintFlexible(ArrayRef<const t_iparams> iparams, int iparamsIndex)
 {
-    GMX_ASSERT(iparams != nullptr, "Need a valid iparams array");
-
     return (iparams[iparamsIndex].constr.dA == 0 && iparams[iparamsIndex].constr.dB == 0);
 };
 
@@ -234,43 +242,46 @@ enum class FlexibleConstraintTreatment
 /*! \brief Returns the flexible constraint treatment depending on whether the integrator is dynamic */
 FlexibleConstraintTreatment flexibleConstraintTreatment(bool haveDynamicsIntegrator);
 
-/*! \brief Returns a block struct to go from atoms to constraints
+/*! \brief Returns a ListOfLists object to go from atoms to constraints
  *
- * The block struct will contain constraint indices with lower indices
+ * The object will contain constraint indices with lower indices
  * directly matching the order in F_CONSTR and higher indices matching
  * the order in F_CONSTRNC offset by the number of constraints in F_CONSTR.
  *
  * \param[in]  moltype   The molecule data
  * \param[in]  iparams   Interaction parameters, can be null when
- * flexibleConstraintTreatment=Include \param[in]  flexibleConstraintTreatment  The flexible
- * constraint treatment, see enum above \returns a block struct with all constraints for each atom
+ *                       \p flexibleConstraintTreatment==Include
+ * \param[in]  flexibleConstraintTreatment  The flexible constraint treatment,
+ *                                          see enum above
+ *
+ * \returns a ListOfLists object with all constraints for each atom
  */
-t_blocka make_at2con(const gmx_moltype_t&           moltype,
-                     gmx::ArrayRef<const t_iparams> iparams,
-                     FlexibleConstraintTreatment    flexibleConstraintTreatment);
+ListOfLists<int> make_at2con(const gmx_moltype_t&           moltype,
+                             gmx::ArrayRef<const t_iparams> iparams,
+                             FlexibleConstraintTreatment    flexibleConstraintTreatment);
 
-/*! \brief Returns a block struct to go from atoms to constraints
+/*! \brief Returns a ListOfLists object to go from atoms to constraints
  *
- * The block struct will contain constraint indices with lower indices
+ * The object will contain constraint indices with lower indices
  * directly matching the order in F_CONSTR and higher indices matching
  * the order in F_CONSTRNC offset by the number of constraints in F_CONSTR.
  *
  * \param[in]  numAtoms  The number of atoms to construct the list for
  * \param[in]  ilist     Interaction list, size F_NRE
  * \param[in]  iparams   Interaction parameters, can be null when
- * flexibleConstraintTreatment=Include \param[in]  flexibleConstraintTreatment  The flexible
- * constraint treatment, see enum above \returns a block struct with all constraints for each atom
+ *                       \p flexibleConstraintTreatment==Include
+ * \param[in]  flexibleConstraintTreatment  The flexible constraint treatment,
+ *                                          see enum above
+ *
+ * \returns a ListOfLists object with all constraints for each atom
  */
-t_blocka make_at2con(int                         numAtoms,
-                     const t_ilist*              ilist,
-                     const t_iparams*            iparams,
-                     FlexibleConstraintTreatment flexibleConstraintTreatment);
-
-/*! \brief Returns an array of atom to constraints lists for the moltypes */
-const t_blocka* atom2constraints_moltype(const Constraints* constr);
+ListOfLists<int> make_at2con(int                             numAtoms,
+                             ArrayRef<const InteractionList> ilist,
+                             ArrayRef<const t_iparams>       iparams,
+                             FlexibleConstraintTreatment     flexibleConstraintTreatment);
 
 //! Return the number of flexible constraints in the \c ilist and \c iparams.
-int countFlexibleConstraints(const t_ilist* ilist, const t_iparams* iparams);
+int countFlexibleConstraints(ArrayRef<const InteractionList> ilist, ArrayRef<const t_iparams> iparams);
 
 /*! \brief Returns the constraint iatoms for a constraint number con
  * which comes from a list where F_CONSTR and F_CONSTRNC constraints
@@ -299,13 +310,40 @@ bool inter_charge_group_settles(const gmx_mtop_t& mtop);
 void do_constrain_first(FILE*                     log,
                         gmx::Constraints*         constr,
                         const t_inputrec*         inputrec,
-                        const t_mdatoms*          md,
-                        int                       natoms,
+                        int                       numAtoms,
+                        int                       numHomeAtoms,
                         ArrayRefWithPadding<RVec> x,
                         ArrayRefWithPadding<RVec> v,
                         const matrix              box,
                         real                      lambda);
 
+/*! \brief Constrain velocities only.
+ *
+ * The dvdlambda contribution has to be added to the bonded interactions
+ */
+void constrain_velocities(gmx::Constraints* constr,
+                          bool              do_log,
+                          bool              do_ene,
+                          int64_t           step,
+                          t_state*          state,
+                          real*             dvdlambda,
+                          gmx_bool          computeVirial,
+                          tensor            constraintsVirial);
+
+/*! \brief Constraint coordinates.
+ *
+ * The dvdlambda contribution has to be added to the bonded interactions
+ */
+void constrain_coordinates(gmx::Constraints*         constr,
+                           bool                      do_log,
+                           bool                      do_ene,
+                           int64_t                   step,
+                           t_state*                  state,
+                           ArrayRefWithPadding<RVec> xp,
+                           real*                     dvdlambda,
+                           gmx_bool                  computeVirial,
+                           tensor                    constraintsVirial);
+
 } // namespace gmx
 
 #endif
index 7e5b320e4db61a55b24e52389e57a48a0d8b97da..90d16294d0be06788fd6facb61d06b86a29d3d71 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/mdlib/constr.h"
 #include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/topology/block.h"
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/logger.h"
 #include "gromacs/utility/real.h"
-#include "gromacs/utility/smalloc.h"
 
 namespace gmx
 {
 
 //! Recursing function to help find all adjacent constraints.
-static void constr_recur(const t_blocka*                at2con,
+static void constr_recur(const ListOfLists<int>&        at2con,
                          const InteractionLists&        ilist,
                          gmx::ArrayRef<const t_iparams> iparams,
                          gmx_bool                       bTopB,
                          int                            at,
                          int                            depth,
                          int                            nc,
-                         int*                           path,
+                         ArrayRef<int>                  path,
                          real                           r0,
                          real                           r1,
                          real*                          r2max,
                          int*                           count)
 {
-    int      c, con, a1;
     gmx_bool bUse;
     real     len, rn0, rn1;
 
@@ -80,12 +79,11 @@ static void constr_recur(const t_blocka*                at2con,
     gmx::ArrayRef<const int> ia2 = ilist[F_CONSTRNC].iatoms;
 
     /* Loop over all constraints connected to this atom */
-    for (c = at2con->index[at]; c < at2con->index[at + 1]; c++)
+    for (const int con : at2con[at])
     {
-        con = at2con->a[c];
         /* Do not walk over already used constraints */
         bUse = TRUE;
-        for (a1 = 0; a1 < depth; a1++)
+        for (int a1 = 0; a1 < depth; a1++)
         {
             if (con == path[a1])
             {
@@ -124,7 +122,7 @@ static void constr_recur(const t_blocka*                at2con,
                     fprintf(debug,
                             "Found longer constraint distance: r0 %5.3f r1 %5.3f rmax %5.3f\n", rn0,
                             rn1, sqrt(*r2max));
-                    for (a1 = 0; a1 < depth; a1++)
+                    for (int a1 = 0; a1 < depth; a1++)
                     {
                         fprintf(debug, " %d %5.3f", path[a1],
                                 iparams[constr_iatomptr(ia1, ia2, con)[0]].constr.dA);
@@ -138,6 +136,7 @@ static void constr_recur(const t_blocka*                at2con,
              */
             if (depth + 1 < nc && *count < 1000 * nc)
             {
+                int a1;
                 if (ia[1] == at)
                 {
                     a1 = ia[2];
@@ -160,20 +159,20 @@ static real constr_r_max_moltype(const gmx_moltype_t*           molt,
                                  gmx::ArrayRef<const t_iparams> iparams,
                                  const t_inputrec*              ir)
 {
-    int natoms, *path, at, count;
+    int natoms, at, count;
 
-    t_blocka at2con;
-    real     r0, r1, r2maxA, r2maxB, rmax, lam0, lam1;
+    real r0, r1, r2maxA, r2maxB, rmax, lam0, lam1;
 
-    if (molt->ilist[F_CONSTR].size() == 0 && molt->ilist[F_CONSTRNC].size() == 0)
+    if (molt->ilist[F_CONSTR].empty() && molt->ilist[F_CONSTRNC].empty())
     {
         return 0;
     }
 
     natoms = molt->atoms.nr;
 
-    at2con = make_at2con(*molt, iparams, flexibleConstraintTreatment(EI_DYNAMICS(ir->eI)));
-    snew(path, 1 + ir->nProjOrder);
+    const ListOfLists<int> at2con =
+            make_at2con(*molt, iparams, flexibleConstraintTreatment(EI_DYNAMICS(ir->eI)));
+    std::vector<int> path(1 + ir->nProjOrder);
     for (at = 0; at < 1 + ir->nProjOrder; at++)
     {
         path[at] = -1;
@@ -186,7 +185,7 @@ static real constr_r_max_moltype(const gmx_moltype_t*           molt,
         r1 = 0;
 
         count = 0;
-        constr_recur(&at2con, molt->ilist, iparams, FALSE, at, 0, 1 + ir->nProjOrder, path, r0, r1,
+        constr_recur(at2con, molt->ilist, iparams, FALSE, at, 0, 1 + ir->nProjOrder, path, r0, r1,
                      &r2maxA, &count);
     }
     if (ir->efep == efepNO)
@@ -201,7 +200,7 @@ static real constr_r_max_moltype(const gmx_moltype_t*           molt,
             r0    = 0;
             r1    = 0;
             count = 0;
-            constr_recur(&at2con, molt->ilist, iparams, TRUE, at, 0, 1 + ir->nProjOrder, path, r0,
+            constr_recur(at2con, molt->ilist, iparams, TRUE, at, 0, 1 + ir->nProjOrder, path, r0,
                          r1, &r2maxB, &count);
         }
         lam0 = ir->fepvals->init_lambda;
@@ -217,9 +216,6 @@ static real constr_r_max_moltype(const gmx_moltype_t*           molt,
         }
     }
 
-    done_blocka(&at2con);
-    sfree(path);
-
     return rmax;
 }
 
index 909564f2af96958b0b15f0b413864324ba70aed9..43ef9d2459be5a85aa439296a3edd83fc2ae9a00 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -36,6 +37,8 @@
  */
 #include "gmxpre.h"
 
+#include "coupling.h"
+
 #include <cassert>
 #include <cmath>
 
@@ -48,6 +51,7 @@
 #include "gromacs/math/units.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/math/vecdump.h"
+#include "gromacs/mdlib/boxdeformation.h"
 #include "gromacs/mdlib/expanded.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdlib/stat.h"
@@ -88,6 +92,239 @@ static const double sy_const_5[] = { 0.2967324292201065, 0.2967324292201065, -0.
 
 static const double* sy_const[] = { nullptr, sy_const_1, nullptr, sy_const_3, nullptr, sy_const_5 };
 
+
+void update_tcouple(int64_t           step,
+                    const t_inputrec* inputrec,
+                    t_state*          state,
+                    gmx_ekindata_t*   ekind,
+                    const t_extmass*  MassQ,
+                    const t_mdatoms*  md)
+
+{
+    // This condition was explicitly checked in previous version, but should have never been satisfied
+    GMX_ASSERT(!(EI_VV(inputrec->eI)
+                 && (inputrecNvtTrotter(inputrec) || inputrecNptTrotter(inputrec)
+                     || inputrecNphTrotter(inputrec))),
+               "Temperature coupling was requested with velocity verlet and trotter");
+
+    bool doTemperatureCoupling = false;
+
+    // For VV temperature coupling parameters are updated on the current
+    // step, for the others - one step before.
+    if (inputrec->etc == etcNO)
+    {
+        doTemperatureCoupling = false;
+    }
+    else if (EI_VV(inputrec->eI))
+    {
+        doTemperatureCoupling = do_per_step(step, inputrec->nsttcouple);
+    }
+    else
+    {
+        doTemperatureCoupling = do_per_step(step + inputrec->nsttcouple - 1, inputrec->nsttcouple);
+    }
+
+    if (doTemperatureCoupling)
+    {
+        real dttc = inputrec->nsttcouple * inputrec->delta_t;
+
+        // TODO: berendsen_tcoupl(...), nosehoover_tcoupl(...) and vrescale_tcoupl(...) update
+        //      temperature coupling parameters, which should be reflected in the name of these
+        //      subroutines
+        switch (inputrec->etc)
+        {
+            case etcNO: break;
+            case etcBERENDSEN:
+                berendsen_tcoupl(inputrec, ekind, dttc, state->therm_integral);
+                break;
+            case etcNOSEHOOVER:
+                nosehoover_tcoupl(&(inputrec->opts), ekind, dttc, state->nosehoover_xi.data(),
+                                  state->nosehoover_vxi.data(), MassQ);
+                break;
+            case etcVRESCALE:
+                vrescale_tcoupl(inputrec, step, ekind, dttc, state->therm_integral.data());
+                break;
+        }
+        /* rescale in place here */
+        if (EI_VV(inputrec->eI))
+        {
+            rescale_velocities(ekind, md, 0, md->homenr, state->v.rvec_array());
+        }
+    }
+    else
+    {
+        // Set the T scaling lambda to 1 to have no scaling
+        // TODO: Do we have to do it on every non-t-couple step?
+        for (int i = 0; (i < inputrec->opts.ngtc); i++)
+        {
+            ekind->tcstat[i].lambda = 1.0;
+        }
+    }
+}
+
+void update_pcouple_before_coordinates(FILE*             fplog,
+                                       int64_t           step,
+                                       const t_inputrec* inputrec,
+                                       t_state*          state,
+                                       matrix            parrinellorahmanMu,
+                                       matrix            M,
+                                       gmx_bool          bInitStep)
+{
+    /* Berendsen P-coupling is completely handled after the coordinate update.
+     * Trotter P-coupling is handled by separate calls to trotter_update().
+     */
+    if (inputrec->epc == epcPARRINELLORAHMAN
+        && do_per_step(step + inputrec->nstpcouple - 1, inputrec->nstpcouple))
+    {
+        real dtpc = inputrec->nstpcouple * inputrec->delta_t;
+
+        parrinellorahman_pcoupl(fplog, step, inputrec, dtpc, state->pres_prev, state->box,
+                                state->box_rel, state->boxv, M, parrinellorahmanMu, bInitStep);
+    }
+}
+
+void update_pcouple_after_coordinates(FILE*                fplog,
+                                      int64_t              step,
+                                      const t_inputrec*    inputrec,
+                                      const t_mdatoms*     md,
+                                      const matrix         pressure,
+                                      const matrix         forceVirial,
+                                      const matrix         constraintVirial,
+                                      matrix               pressureCouplingMu,
+                                      t_state*             state,
+                                      t_nrnb*              nrnb,
+                                      gmx::BoxDeformation* boxDeformation,
+                                      const bool           scaleCoordinates)
+{
+    int start  = 0;
+    int homenr = md->homenr;
+
+    /* Cast to real for faster code, no loss in precision (see comment above) */
+    real dt = inputrec->delta_t;
+
+
+    /* now update boxes */
+    switch (inputrec->epc)
+    {
+        case (epcNO): break;
+        case (epcBERENDSEN):
+            if (do_per_step(step, inputrec->nstpcouple))
+            {
+                real dtpc = inputrec->nstpcouple * dt;
+                berendsen_pcoupl(fplog, step, inputrec, dtpc, pressure, state->box, forceVirial,
+                                 constraintVirial, pressureCouplingMu, &state->baros_integral);
+                berendsen_pscale(inputrec, pressureCouplingMu, state->box, state->box_rel, start,
+                                 homenr, state->x.rvec_array(), md->cFREEZE, nrnb, scaleCoordinates);
+            }
+            break;
+        case (epcCRESCALE):
+            if (do_per_step(step, inputrec->nstpcouple))
+            {
+                real dtpc = inputrec->nstpcouple * dt;
+                crescale_pcoupl(fplog, step, inputrec, dtpc, pressure, state->box, forceVirial,
+                                constraintVirial, pressureCouplingMu, &state->baros_integral);
+                crescale_pscale(inputrec, pressureCouplingMu, state->box, state->box_rel, start,
+                                homenr, state->x.rvec_array(), state->v.rvec_array(), md->cFREEZE,
+                                nrnb, scaleCoordinates);
+            }
+            break;
+        case (epcPARRINELLORAHMAN):
+            if (do_per_step(step + inputrec->nstpcouple - 1, inputrec->nstpcouple))
+            {
+                /* The box velocities were updated in do_pr_pcoupl,
+                 * but we dont change the box vectors until we get here
+                 * since we need to be able to shift/unshift above.
+                 */
+                real dtpc = inputrec->nstpcouple * dt;
+                for (int i = 0; i < DIM; i++)
+                {
+                    for (int m = 0; m <= i; m++)
+                    {
+                        state->box[i][m] += dtpc * state->boxv[i][m];
+                    }
+                }
+                preserve_box_shape(inputrec, state->box_rel, state->box);
+
+                /* Scale the coordinates */
+                if (scaleCoordinates)
+                {
+                    auto x = state->x.rvec_array();
+                    for (int n = start; n < start + homenr; n++)
+                    {
+                        tmvmul_ur0(pressureCouplingMu, x[n], x[n]);
+                    }
+                }
+            }
+            break;
+        case (epcMTTK):
+            switch (inputrec->epct)
+            {
+                case (epctISOTROPIC):
+                    /* DIM * eta = ln V.  so DIM*eta_new = DIM*eta_old + DIM*dt*veta =>
+                       ln V_new = ln V_old + 3*dt*veta => V_new = V_old*exp(3*dt*veta) =>
+                       Side length scales as exp(veta*dt) */
+
+                    msmul(state->box, std::exp(state->veta * dt), state->box);
+
+                    /* Relate veta to boxv.  veta = d(eta)/dT = (1/DIM)*1/V dV/dT.
+                       o               If we assume isotropic scaling, and box length scaling
+                       factor L, then V = L^DIM (det(M)).  So dV/dt = DIM
+                       L^(DIM-1) dL/dt det(M), and veta = (1/L) dL/dt.  The
+                       determinant of B is L^DIM det(M), and the determinant
+                       of dB/dt is (dL/dT)^DIM det (M).  veta will be
+                       (det(dB/dT)/det(B))^(1/3).  Then since M =
+                       B_new*(vol_new)^(1/3), dB/dT_new = (veta_new)*B(new). */
+
+                    msmul(state->box, state->veta, state->boxv);
+                    break;
+                default: break;
+            }
+            break;
+        default: break;
+    }
+
+    if (boxDeformation)
+    {
+        auto localX = makeArrayRef(state->x).subArray(start, homenr);
+        boxDeformation->apply(localX, state->box, step);
+    }
+}
+
+extern gmx_bool update_randomize_velocities(const t_inputrec*        ir,
+                                            int64_t                  step,
+                                            const t_commrec*         cr,
+                                            const t_mdatoms*         md,
+                                            gmx::ArrayRef<gmx::RVec> v,
+                                            const gmx::Update*       upd,
+                                            const gmx::Constraints*  constr)
+{
+
+    real rate = (ir->delta_t) / ir->opts.tau_t[0];
+
+    if (ir->etc == etcANDERSEN && constr != nullptr)
+    {
+        /* Currently, Andersen thermostat does not support constrained
+           systems. Functionality exists in the andersen_tcoupl
+           function in GROMACS 4.5.7 to allow this combination. That
+           code could be ported to the current random-number
+           generation approach, but has not yet been done because of
+           lack of time and resources. */
+        gmx_fatal(FARGS,
+                  "Normal Andersen is currently not supported with constraints, use massive "
+                  "Andersen instead");
+    }
+
+    /* proceed with andersen if 1) it's fixed probability per
+       particle andersen or 2) it's massive andersen and it's tau_t/dt */
+    if ((ir->etc == etcANDERSEN) || do_per_step(step, gmx::roundToInt(1.0 / rate)))
+    {
+        andersen_tcoupl(ir, step, cr, md, v, rate, upd->getAndersenRandomizeGroup(),
+                        upd->getBoltzmanFactor());
+        return TRUE;
+    }
+    return FALSE;
+}
+
 /*
    static const double sy_const[MAX_SUZUKI_YOSHIDA_NUM+1][MAX_SUZUKI_YOSHIDA_NUM+1] = {
     {},
@@ -288,7 +525,7 @@ static void boxv_trotter(const t_inputrec*     ir,
     /* for now, we use Elr = 0, because if you want to get it right, you
        really should be using PME. Maybe print a warning? */
 
-    pscal = calc_pres(ir->ePBC, nwall, box, ekinmod, vir, localpres) + pcorr;
+    pscal = calc_pres(ir->pbcType, nwall, box, ekinmod, vir, localpres) + pcorr;
 
     vol = det(box);
     GW  = (vol * (MassQ->Winv / PRESFAC)) * (DIM * pscal - trace(ir->ref_p)); /* W is in ps^2 * bar * nm^3 */
@@ -304,12 +541,12 @@ static void boxv_trotter(const t_inputrec*     ir,
  *
  */
 
-real calc_pres(int ePBC, int nwall, const matrix box, const tensor ekin, const tensor vir, tensor pres)
+real calc_pres(PbcType pbcType, int nwall, const matrix box, const tensor ekin, const tensor vir, tensor pres)
 {
     int  n, m;
     real fac;
 
-    if (ePBC == epbcNONE || (ePBC == epbcXY && nwall != 2))
+    if (pbcType == PbcType::No || (pbcType == PbcType::XY && nwall != 2))
     {
         clear_mat(pres);
     }
@@ -574,7 +811,6 @@ void berendsen_pcoupl(FILE*             fplog,
                       matrix            mu,
                       double*           baros_integral)
 {
-    int  d, n;
     real scalar_pressure, xy_pressure, p_corr_z;
     char buf[STRLEN];
 
@@ -583,7 +819,7 @@ void berendsen_pcoupl(FILE*             fplog,
      */
     scalar_pressure = 0;
     xy_pressure     = 0;
-    for (d = 0; d < DIM; d++)
+    for (int d = 0; d < DIM; d++)
     {
         scalar_pressure += pres[d][d] / DIM;
         if (d != ZZ)
@@ -601,22 +837,22 @@ void berendsen_pcoupl(FILE*             fplog,
     switch (ir->epct)
     {
         case epctISOTROPIC:
-            for (d = 0; d < DIM; d++)
+            for (int d = 0; d < DIM; d++)
             {
                 mu[d][d] = 1.0 - factor(d, d) * (ir->ref_p[d][d] - scalar_pressure) / DIM;
             }
             break;
         case epctSEMIISOTROPIC:
-            for (d = 0; d < ZZ; d++)
+            for (int d = 0; d < ZZ; d++)
             {
                 mu[d][d] = 1.0 - factor(d, d) * (ir->ref_p[d][d] - xy_pressure) / DIM;
             }
             mu[ZZ][ZZ] = 1.0 - factor(ZZ, ZZ) * (ir->ref_p[ZZ][ZZ] - pres[ZZ][ZZ]) / DIM;
             break;
         case epctANISOTROPIC:
-            for (d = 0; d < DIM; d++)
+            for (int d = 0; d < DIM; d++)
             {
-                for (n = 0; n < DIM; n++)
+                for (int n = 0; n < DIM; n++)
                 {
                     mu[d][n] = (d == n ? 1.0 : 0.0) - factor(d, n) * (ir->ref_p[d][n] - pres[d][n]) / DIM;
                 }
@@ -636,7 +872,7 @@ void berendsen_pcoupl(FILE*             fplog,
                 p_corr_z = 0;
             }
             mu[ZZ][ZZ] = 1.0 - ir->compress[ZZ][ZZ] * p_corr_z;
-            for (d = 0; d < DIM - 1; d++)
+            for (int d = 0; d < DIM - 1; d++)
             {
                 mu[d][d] = 1.0
                            + factor(d, d)
@@ -700,6 +936,228 @@ void berendsen_pcoupl(FILE*             fplog,
     }
 }
 
+void crescale_pcoupl(FILE*             fplog,
+                     int64_t           step,
+                     const t_inputrec* ir,
+                     real              dt,
+                     const tensor      pres,
+                     const matrix      box,
+                     const matrix      force_vir,
+                     const matrix      constraint_vir,
+                     matrix            mu,
+                     double*           baros_integral)
+{
+    /*
+     *  Calculate the scaling matrix mu
+     */
+    real scalar_pressure = 0;
+    real xy_pressure     = 0;
+    for (int d = 0; d < DIM; d++)
+    {
+        scalar_pressure += pres[d][d] / DIM;
+        if (d != ZZ)
+        {
+            xy_pressure += pres[d][d] / (DIM - 1);
+        }
+    }
+    clear_mat(mu);
+
+    gmx::ThreeFry2x64<64>         rng(ir->ld_seed, gmx::RandomDomain::Barostat);
+    gmx::NormalDistribution<real> normalDist;
+    rng.restart(step, 0);
+    real vol = 1.0;
+    for (int d = 0; d < DIM; d++)
+    {
+        vol *= box[d][d];
+    }
+    real gauss;
+    real gauss2;
+    real kt = ir->opts.ref_t[0] * BOLTZ;
+    if (kt < 0.0)
+    {
+        kt = 0.0;
+    }
+
+    switch (ir->epct)
+    {
+        case epctISOTROPIC:
+            gauss = normalDist(rng);
+            for (int d = 0; d < DIM; d++)
+            {
+                const real compressibilityFactor = ir->compress[d][d] * dt / ir->tau_p;
+                mu[d][d] = std::exp(-compressibilityFactor * (ir->ref_p[d][d] - scalar_pressure) / DIM
+                                    + std::sqrt(2.0 * kt * compressibilityFactor * PRESFAC / vol)
+                                              * gauss / DIM);
+            }
+            break;
+        case epctSEMIISOTROPIC:
+            gauss  = normalDist(rng);
+            gauss2 = normalDist(rng);
+            for (int d = 0; d < ZZ; d++)
+            {
+                const real compressibilityFactor = ir->compress[d][d] * dt / ir->tau_p;
+                mu[d][d]                         = std::exp(
+                        -compressibilityFactor * (ir->ref_p[d][d] - xy_pressure) / DIM
+                        + std::sqrt((DIM - 1) * 2.0 * kt * compressibilityFactor * PRESFAC / vol / DIM)
+                                  / (DIM - 1) * gauss);
+            }
+            {
+                const real compressibilityFactor = ir->compress[ZZ][ZZ] * dt / ir->tau_p;
+                mu[ZZ][ZZ]                       = std::exp(
+                        -compressibilityFactor * (ir->ref_p[ZZ][ZZ] - pres[ZZ][ZZ]) / DIM
+                        + std::sqrt(2.0 * kt * compressibilityFactor * PRESFAC / vol / DIM) * gauss2);
+            }
+            break;
+        case epctSURFACETENSION:
+            gauss  = normalDist(rng);
+            gauss2 = normalDist(rng);
+            for (int d = 0; d < ZZ; d++)
+            {
+                const real compressibilityFactor = ir->compress[d][d] * dt / ir->tau_p;
+                /* Notice: we here use ref_p[ZZ][ZZ] as isotropic pressure and ir->ref_p[d][d] as surface tension */
+                mu[d][d] = std::exp(
+                        -compressibilityFactor
+                                * (ir->ref_p[ZZ][ZZ] - ir->ref_p[d][d] / box[ZZ][ZZ] - xy_pressure) / DIM
+                        + std::sqrt(4.0 / 3.0 * kt * compressibilityFactor * PRESFAC / vol)
+                                  / (DIM - 1) * gauss);
+            }
+            {
+                const real compressibilityFactor = ir->compress[ZZ][ZZ] * dt / ir->tau_p;
+                mu[ZZ][ZZ]                       = std::exp(
+                        -compressibilityFactor * (ir->ref_p[ZZ][ZZ] - pres[ZZ][ZZ]) / DIM
+                        + std::sqrt(2.0 / 3.0 * kt * compressibilityFactor * PRESFAC / vol) * gauss2);
+            }
+            break;
+        default:
+            gmx_fatal(FARGS, "C-rescale pressure coupling type %s not supported yet\n",
+                      EPCOUPLTYPETYPE(ir->epct));
+    }
+    /* To fullfill the orientation restrictions on triclinic boxes
+     * we will set mu_yx, mu_zx and mu_zy to 0 and correct
+     * the other elements of mu to first order.
+     */
+    mu[YY][XX] += mu[XX][YY];
+    mu[ZZ][XX] += mu[XX][ZZ];
+    mu[ZZ][YY] += mu[YY][ZZ];
+    mu[XX][YY] = 0;
+    mu[XX][ZZ] = 0;
+    mu[YY][ZZ] = 0;
+
+    /* Keep track of the work the barostat applies on the system.
+     * Without constraints force_vir tells us how Epot changes when scaling.
+     * With constraints constraint_vir gives us the constraint contribution
+     * to both Epot and Ekin. Although we are not scaling velocities, scaling
+     * the coordinates leads to scaling of distances involved in constraints.
+     * This in turn changes the angular momentum (even if the constrained
+     * distances are corrected at the next step). The kinetic component
+     * of the constraint virial captures the angular momentum change.
+     */
+    for (int d = 0; d < DIM; d++)
+    {
+        for (int n = 0; n <= d; n++)
+        {
+            *baros_integral -=
+                    2 * (mu[d][n] - (n == d ? 1 : 0)) * (force_vir[d][n] + constraint_vir[d][n]);
+        }
+    }
+
+    if (debug)
+    {
+        pr_rvecs(debug, 0, "PC: pres ", pres, 3);
+        pr_rvecs(debug, 0, "PC: mu   ", mu, 3);
+    }
+
+    if (mu[XX][XX] < 0.99 || mu[XX][XX] > 1.01 || mu[YY][YY] < 0.99 || mu[YY][YY] > 1.01
+        || mu[ZZ][ZZ] < 0.99 || mu[ZZ][ZZ] > 1.01)
+    {
+        char buf[STRLEN];
+        char buf2[22];
+        sprintf(buf,
+                "\nStep %s  Warning: pressure scaling more than 1%%, "
+                "mu: %g %g %g\n",
+                gmx_step_str(step, buf2), mu[XX][XX], mu[YY][YY], mu[ZZ][ZZ]);
+        if (fplog)
+        {
+            fprintf(fplog, "%s", buf);
+        }
+        fprintf(stderr, "%s", buf);
+    }
+}
+
+void crescale_pscale(const t_inputrec*    ir,
+                     const matrix         mu,
+                     matrix               box,
+                     matrix               box_rel,
+                     int                  start,
+                     int                  nr_atoms,
+                     rvec                 x[],
+                     rvec                 v[],
+                     const unsigned short cFREEZE[],
+                     t_nrnb*              nrnb,
+                     const bool           scaleCoordinates)
+{
+    ivec* nFreeze = ir->opts.nFreeze;
+    int nthreads gmx_unused;
+    matrix       inv_mu;
+
+#ifndef __clang_analyzer__
+    nthreads = gmx_omp_nthreads_get(emntUpdate);
+#endif
+
+    gmx::invertBoxMatrix(mu, inv_mu);
+
+    /* Scale the positions and the velocities */
+    if (scaleCoordinates)
+    {
+#pragma omp parallel for num_threads(nthreads) schedule(static)
+        for (int n = start; n < start + nr_atoms; n++)
+        {
+            // Trivial OpenMP region that does not throw
+            int g;
+
+            if (cFREEZE == nullptr)
+            {
+                g = 0;
+            }
+            else
+            {
+                g = cFREEZE[n];
+            }
+
+            if (!nFreeze[g][XX])
+            {
+                x[n][XX] = mu[XX][XX] * x[n][XX] + mu[YY][XX] * x[n][YY] + mu[ZZ][XX] * x[n][ZZ];
+                v[n][XX] = inv_mu[XX][XX] * v[n][XX] + inv_mu[YY][XX] * v[n][YY]
+                           + inv_mu[ZZ][XX] * v[n][ZZ];
+            }
+            if (!nFreeze[g][YY])
+            {
+                x[n][YY] = mu[YY][YY] * x[n][YY] + mu[ZZ][YY] * x[n][ZZ];
+                v[n][YY] = inv_mu[YY][YY] * v[n][YY] + inv_mu[ZZ][YY] * v[n][ZZ];
+            }
+            if (!nFreeze[g][ZZ])
+            {
+                x[n][ZZ] = mu[ZZ][ZZ] * x[n][ZZ];
+                v[n][ZZ] = inv_mu[ZZ][ZZ] * v[n][ZZ];
+            }
+        }
+    }
+    /* compute final boxlengths */
+    for (int d = 0; d < DIM; d++)
+    {
+        box[d][XX] = mu[XX][XX] * box[d][XX] + mu[YY][XX] * box[d][YY] + mu[ZZ][XX] * box[d][ZZ];
+        box[d][YY] = mu[YY][YY] * box[d][YY] + mu[ZZ][YY] * box[d][ZZ];
+        box[d][ZZ] = mu[ZZ][ZZ] * box[d][ZZ];
+    }
+
+    preserve_box_shape(ir, box_rel, box);
+
+    /* (un)shifting should NOT be done after this,
+     * since the box vectors might have changed
+     */
+    inc_nrnb(nrnb, eNR_PCOUPL, nr_atoms);
+}
+
 void berendsen_pscale(const t_inputrec*    ir,
                       const matrix         mu,
                       matrix               box,
@@ -1441,7 +1899,8 @@ real NPT_energy(const t_inputrec* ir, const t_state* state, const t_extmass* Mas
                     energyNPT += energyPressureMTTK(ir, state, MassQ);
                 }
                 break;
-            case epcBERENDSEN: energyNPT += state->baros_integral; break;
+            case epcBERENDSEN:
+            case epcCRESCALE: energyNPT += state->baros_integral; break;
             default:
                 GMX_RELEASE_ASSERT(
                         false,
@@ -1742,7 +2201,7 @@ void update_annealing_target_temp(t_inputrec* ir, real t, gmx::Update* upd)
         }
     }
 
-    update_temperature_constants(upd->sd(), ir);
+    upd->update_temperature_constants(*ir);
 }
 
 void pleaseCiteCouplingAlgorithms(FILE* fplog, const t_inputrec& ir)
@@ -1757,6 +2216,10 @@ void pleaseCiteCouplingAlgorithms(FILE* fplog, const t_inputrec& ir)
         {
             please_cite(fplog, "Bussi2007a");
         }
+        if (ir.epc == epcCRESCALE)
+        {
+            please_cite(fplog, "Bernetti2020");
+        }
         // TODO this is actually an integrator, not a coupling algorithm
         if (ir.eI == eiSD1)
         {
diff --git a/src/gromacs/mdlib/coupling.h b/src/gromacs/mdlib/coupling.h
new file mode 100644 (file)
index 0000000..1d95925
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_COUPLING_H
+#define GMX_MDLIB_COUPLING_H
+
+#include "gromacs/math/paddedvector.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_ekindata_t;
+struct gmx_enerdata_t;
+struct t_commrec;
+struct t_extmass;
+struct t_grpopts;
+struct t_inputrec;
+struct t_mdatoms;
+struct t_nrnb;
+class t_state;
+
+enum class PbcType;
+
+namespace gmx
+{
+class BoxDeformation;
+class Constraints;
+class Update;
+}; // namespace gmx
+
+/* Update the size of per-atom arrays (e.g. after DD re-partitioning,
+   which might increase the number of home atoms). */
+
+void update_tcouple(int64_t           step,
+                    const t_inputrec* inputrec,
+                    t_state*          state,
+                    gmx_ekindata_t*   ekind,
+                    const t_extmass*  MassQ,
+                    const t_mdatoms*  md);
+
+/* Update Parrinello-Rahman, to be called before the coordinate update */
+void update_pcouple_before_coordinates(FILE*             fplog,
+                                       int64_t           step,
+                                       const t_inputrec* inputrec,
+                                       t_state*          state,
+                                       matrix            parrinellorahmanMu,
+                                       matrix            M,
+                                       gmx_bool          bInitStep);
+
+/* Update the box, to be called after the coordinate update.
+ * For Berendsen P-coupling, also calculates the scaling factor
+ * and scales the coordinates.
+ * When the deform option is used, scales coordinates and box here.
+ */
+void update_pcouple_after_coordinates(FILE*                fplog,
+                                      int64_t              step,
+                                      const t_inputrec*    inputrec,
+                                      const t_mdatoms*     md,
+                                      const matrix         pressure,
+                                      const matrix         forceVirial,
+                                      const matrix         constraintVirial,
+                                      matrix               pressureCouplingMu,
+                                      t_state*             state,
+                                      t_nrnb*              nrnb,
+                                      gmx::BoxDeformation* boxDeformation,
+                                      bool                 scaleCoordinates);
+
+/* Return TRUE if OK, FALSE in case of Shake Error */
+
+extern gmx_bool update_randomize_velocities(const t_inputrec*        ir,
+                                            int64_t                  step,
+                                            const t_commrec*         cr,
+                                            const t_mdatoms*         md,
+                                            gmx::ArrayRef<gmx::RVec> v,
+                                            const gmx::Update*       upd,
+                                            const gmx::Constraints*  constr);
+
+void berendsen_tcoupl(const t_inputrec*    ir,
+                      gmx_ekindata_t*      ekind,
+                      real                 dt,
+                      std::vector<double>& therm_integral); //NOLINT(google-runtime-references)
+
+void andersen_tcoupl(const t_inputrec*         ir,
+                     int64_t                   step,
+                     const t_commrec*          cr,
+                     const t_mdatoms*          md,
+                     gmx::ArrayRef<gmx::RVec>  v,
+                     real                      rate,
+                     const std::vector<bool>&  randomize,
+                     gmx::ArrayRef<const real> boltzfac);
+
+void nosehoover_tcoupl(const t_grpopts*      opts,
+                       const gmx_ekindata_t* ekind,
+                       real                  dt,
+                       double                xi[],
+                       double                vxi[],
+                       const t_extmass*      MassQ);
+
+void trotter_update(const t_inputrec*               ir,
+                    int64_t                         step,
+                    gmx_ekindata_t*                 ekind,
+                    const gmx_enerdata_t*           enerd,
+                    t_state*                        state,
+                    const tensor                    vir,
+                    const t_mdatoms*                md,
+                    const t_extmass*                MassQ,
+                    gmx::ArrayRef<std::vector<int>> trotter_seqlist,
+                    int                             trotter_seqno);
+
+std::array<std::vector<int>, ettTSEQMAX>
+init_npt_vars(const t_inputrec* ir, t_state* state, t_extmass* Mass, gmx_bool bTrotter);
+
+real NPT_energy(const t_inputrec* ir, const t_state* state, const t_extmass* MassQ);
+/* computes all the pressure/tempertature control energy terms to get a conserved energy */
+
+void vrescale_tcoupl(const t_inputrec* ir, int64_t step, gmx_ekindata_t* ekind, real dt, double therm_integral[]);
+/* Compute temperature scaling. For V-rescale it is done in update. */
+
+void rescale_velocities(const gmx_ekindata_t* ekind, const t_mdatoms* mdatoms, int start, int end, rvec v[]);
+/* Rescale the velocities with the scaling factor in ekind */
+
+//! Check whether we do simulated annealing.
+bool doSimulatedAnnealing(const t_inputrec* ir);
+
+//! Initialize simulated annealing.
+bool initSimulatedAnnealing(t_inputrec* ir, gmx::Update* upd);
+
+// TODO: This is the only function in update.h altering the inputrec
+void update_annealing_target_temp(t_inputrec* ir, real t, gmx::Update* upd);
+/* Set reference temp for simulated annealing at time t*/
+
+real calc_temp(real ekin, real nrdf);
+/* Calculate the temperature */
+
+real calc_pres(PbcType pbcType, int nwall, const matrix box, const tensor ekin, const tensor vir, tensor pres);
+/* Calculate the pressure tensor, returns the scalar pressure.
+ * The unit of pressure is bar.
+ */
+
+void parrinellorahman_pcoupl(FILE*             fplog,
+                             int64_t           step,
+                             const t_inputrec* ir,
+                             real              dt,
+                             const tensor      pres,
+                             const tensor      box,
+                             tensor            box_rel,
+                             tensor            boxv,
+                             tensor            M,
+                             matrix            mu,
+                             gmx_bool          bFirstStep);
+
+void berendsen_pcoupl(FILE*             fplog,
+                      int64_t           step,
+                      const t_inputrec* ir,
+                      real              dt,
+                      const tensor      pres,
+                      const matrix      box,
+                      const matrix      force_vir,
+                      const matrix      constraint_vir,
+                      matrix            mu,
+                      double*           baros_integral);
+
+void berendsen_pscale(const t_inputrec*    ir,
+                      const matrix         mu,
+                      matrix               box,
+                      matrix               box_rel,
+                      int                  start,
+                      int                  nr_atoms,
+                      rvec                 x[],
+                      const unsigned short cFREEZE[],
+                      t_nrnb*              nrnb,
+                      bool                 scaleCoordinates);
+void crescale_pcoupl(FILE*             fplog,
+                     int64_t           step,
+                     const t_inputrec* ir,
+                     real              dt,
+                     const tensor      pres,
+                     const matrix      box,
+                     const matrix      force_vir,
+                     const matrix      constraint_vir,
+                     matrix            mu,
+                     double*           baros_integral);
+
+void crescale_pscale(const t_inputrec*    ir,
+                     const matrix         mu,
+                     matrix               box,
+                     matrix               box_rel,
+                     int                  start,
+                     int                  nr_atoms,
+                     rvec                 x[],
+                     rvec                 v[],
+                     const unsigned short cFREEZE[],
+                     t_nrnb*              nrnb,
+                     bool                 scaleCoordinates);
+
+void pleaseCiteCouplingAlgorithms(FILE* fplog, const t_inputrec& ir);
+
+/*! \brief Generate a new kinetic energy for the v-rescale thermostat
+ *
+ * Generates a new value for the kinetic energy, according to
+ * Bussi et al JCP (2007), Eq. (A7)
+ *
+ * This is used by update_tcoupl(), and by the VRescaleThermostat of the modular
+ * simulator.
+ * \todo Move this to the VRescaleThermostat once the modular simulator becomes
+ *       the default code path.
+ *
+ * \param[in] kk     present value of the kinetic energy of the atoms to be thermalized (in
+ * arbitrary units) \param[in] sigma  target average value of the kinetic energy (ndeg k_b T/2)  (in
+ * the same units as kk) \param[in] ndeg   number of degrees of freedom of the atoms to be
+ * thermalized \param[in] taut   relaxation time of the thermostat, in units of 'how often this
+ * routine is called' \param[in] step   the time step this routine is called on \param[in] seed the
+ * random number generator seed \return  the new kinetic energy
+ */
+real vrescale_resamplekin(real kk, real sigma, real ndeg, real taut, int64_t step, int64_t seed);
+
+#endif // GMX_MDLIB_COUPLING_H
index 2d06168cbedf1fee0be36f28aeda75b5811aee51..b4871765ced15e39c3909915758cb70f15cafe28 100644 (file)
@@ -43,6 +43,7 @@
 #include "gromacs/math/vec.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/nblist.h"
 #include "gromacs/tables/forcetable.h"
@@ -186,17 +187,14 @@ DispersionCorrection::TopologyParams::TopologyParams(const gmx_mtop_t&         m
              */
             for (const gmx_molblock_t& molb : mtop.molblock)
             {
-                const int       nmol  = molb.nmol;
-                const t_atoms*  atoms = &mtop.moltype[molb.type].atoms;
-                const t_blocka* excl  = &mtop.moltype[molb.type].excls;
+                const int      nmol  = molb.nmol;
+                const t_atoms* atoms = &mtop.moltype[molb.type].atoms;
+                const auto&    excl  = mtop.moltype[molb.type].excls;
                 for (int i = 0; (i < atoms->nr); i++)
                 {
                     const int tpi = atomtypeAOrB(atoms->atom[i], q);
-                    const int j1  = excl->index[i];
-                    const int j2  = excl->index[i + 1];
-                    for (int j = j1; j < j2; j++)
+                    for (const int k : excl[i])
                     {
-                        const int k = excl->a[j];
                         if (k > i)
                         {
                             const int tpj = atomtypeAOrB(atoms->atom[k], q);
@@ -409,7 +407,7 @@ void DispersionCorrection::setInteractionParameters(InteractionParams*         i
                    "Dispersion-correction code needs a table with both repulsion and dispersion "
                    "terms");
         const real  scale  = iParams->dispersionCorrectionTable_->scale;
-        const real* vdwtab = iParams->dispersionCorrectionTable_->data;
+        const real* vdwtab = iParams->dispersionCorrectionTable_->data.data();
 
         /* Round the cut-offs to exact table values for precision */
         int ri0 = static_cast<int>(std::floor(ic.rvdw_switch * scale));
index 88c8c7b40b9c2ed409a13711e56d2d29ed8d94b1..be656e2130c4769ff0963a28d5a3127acf8ce959 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <cstdio>
 
+#include <array>
 #include <memory>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/utility/arrayref.h"
 
 struct gmx_mtop_t;
 struct interaction_const_t;
@@ -50,6 +50,8 @@ struct t_inputrec;
 
 namespace gmx
 {
+template<typename>
+class ArrayRef;
 class MDLogger;
 } // namespace gmx
 
@@ -107,18 +109,6 @@ public:
             }
         }
 
-        /*! \brief Correct the pressure tensor for the missing dispersion
-         *
-         * \param[in,out] pressureTensor  The pressure tensor to correct
-         */
-        void correctPressure(tensor pressureTensor) const
-        {
-            for (int m = 0; m < DIM; m++)
-            {
-                pressureTensor[m][m] += pressure;
-            }
-        }
-
         real virial   = 0; //!< Scalar correction to the virial
         real pressure = 0; //!< Scalar correction to the pressure
         real energy   = 0; //!< Correction to the energy
index 9fab6ae68283d3afdb72e9f0bf34a03dc2379397..6c830526bea1576778b38cc026603480c4ccdb44 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,6 +50,7 @@
 #include "gromacs/math/vec.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/trajectory/energyframe.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
index d475e37cf02dee68c66ba8d09b2082021120ab1a..ac76a2aade614fcffc7962ec951a2d61cb968ffe 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/fileio/enxio.h"
 #include "gromacs/trajectory/energyframe.h"
-#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
 /* \brief Running averaging structure ('energy bin') to store thermodynamic values.
  *
  * Collects the data on thermodynamic parameters (energy terms, temperature,
index ee70f7a1301d88bfb2a0995cd51dd3e4cb93dd9f..8f7cf633a9ca79d09220381daa55b51e143c2301 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "enerdata_utils.h"
 
+#include "gromacs/gmxlib/network.h"
+#include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
 
+ForeignLambdaTerms::ForeignLambdaTerms(int numLambdas) :
+    numLambdas_(numLambdas),
+    energies_(1 + numLambdas),
+    dhdl_(1 + numLambdas)
+{
+}
+
+std::pair<std::vector<double>, std::vector<double>> ForeignLambdaTerms::getTerms(const t_commrec* cr) const
+{
+    GMX_RELEASE_ASSERT(finalizedPotentialContributions_,
+                       "The object needs to be finalized before calling getTerms");
+
+    std::vector<double> data(2 * numLambdas_);
+    for (int i = 0; i < numLambdas_; i++)
+    {
+        data[i]               = energies_[1 + i] - energies_[0];
+        data[numLambdas_ + i] = dhdl_[1 + i];
+    }
+    if (cr && cr->nnodes > 1)
+    {
+        gmx_sumd(data.size(), data.data(), cr);
+    }
+    auto dataMid = data.begin() + numLambdas_;
+
+    return { { data.begin(), dataMid }, { dataMid, data.end() } };
+}
+
+void ForeignLambdaTerms::zeroAllTerms()
+{
+    std::fill(energies_.begin(), energies_.end(), 0.0);
+    std::fill(dhdl_.begin(), dhdl_.end(), 0.0);
+    finalizedPotentialContributions_ = false;
+}
+
 gmx_enerdata_t::gmx_enerdata_t(int numEnergyGroups, int numFepLambdas) :
     grpp(numEnergyGroups),
-    enerpart_lambda(numFepLambdas == 0 ? 0 : numFepLambdas + 1),
+    foreignLambdaTerms(numFepLambdas),
     foreign_grpp(numEnergyGroups)
 {
 }
@@ -64,20 +101,20 @@ static real sum_v(int n, gmx::ArrayRef<const real> v)
     return t;
 }
 
-void sum_epot(gmx_grppairener_t* grpp, real* epot)
+void sum_epot(const gmx_grppairener_t& grpp, real* epot)
 {
     int i;
 
     /* Accumulate energies */
-    epot[F_COUL_SR] = sum_v(grpp->nener, grpp->ener[egCOULSR]);
-    epot[F_LJ]      = sum_v(grpp->nener, grpp->ener[egLJSR]);
-    epot[F_LJ14]    = sum_v(grpp->nener, grpp->ener[egLJ14]);
-    epot[F_COUL14]  = sum_v(grpp->nener, grpp->ener[egCOUL14]);
+    epot[F_COUL_SR] = sum_v(grpp.nener, grpp.ener[egCOULSR]);
+    epot[F_LJ]      = sum_v(grpp.nener, grpp.ener[egLJSR]);
+    epot[F_LJ14]    = sum_v(grpp.nener, grpp.ener[egLJ14]);
+    epot[F_COUL14]  = sum_v(grpp.nener, grpp.ener[egCOUL14]);
 
     /* lattice part of LR doesnt belong to any group
      * and has been added earlier
      */
-    epot[F_BHAM] = sum_v(grpp->nener, grpp->ener[egBHAMSR]);
+    epot[F_BHAM] = sum_v(grpp.nener, grpp.ener[egBHAMSR]);
 
     epot[F_EPOT] = 0;
     for (i = 0; (i < F_EPOT); i++)
@@ -89,17 +126,18 @@ void sum_epot(gmx_grppairener_t* grpp, real* epot)
     }
 }
 
-void sum_dhdl(gmx_enerdata_t* enerd, gmx::ArrayRef<const real> lambda, const t_lambda& fepvals)
+// Adds computed dV/dlambda contributions to the requested outputs
+static void set_dvdl_output(gmx_enerdata_t* enerd, const t_lambda& fepvals)
 {
-    int index;
-
-    enerd->dvdl_lin[efptVDW] += enerd->term[F_DVDL_VDW]; /* include dispersion correction */
     enerd->term[F_DVDL] = 0.0;
     for (int i = 0; i < efptNR; i++)
     {
         if (fepvals.separate_dvdl[i])
         {
-            /* could this be done more readably/compactly? */
+            /* Translate free-energy term indices to idef term indices.
+             * Could this be done more readably/compactly?
+             */
+            int index;
             switch (i)
             {
                 case (efptMASS): index = F_DKDL; break;
@@ -126,16 +164,37 @@ void sum_dhdl(gmx_enerdata_t* enerd, gmx::ArrayRef<const real> lambda, const t_l
             }
         }
     }
+}
 
-    if (fepvals.separate_dvdl[efptBONDED])
+void ForeignLambdaTerms::addConstantDhdl(const double dhdl)
+{
+    for (double& foreignDhdl : dhdl_)
     {
-        enerd->term[F_DVDL_BONDED] += enerd->term[F_DVDL_CONSTR];
+        foreignDhdl += dhdl;
     }
-    else
+}
+
+void ForeignLambdaTerms::finalizePotentialContributions(gmx::ArrayRef<const double> dvdlLinear,
+                                                        gmx::ArrayRef<const real>   lambda,
+                                                        const t_lambda&             fepvals)
+{
+    if (finalizedPotentialContributions_)
     {
-        enerd->term[F_DVDL] += enerd->term[F_DVDL_CONSTR];
+        return;
     }
 
+    double dvdl_lin = 0;
+    for (int i = 0; i < efptNR; i++)
+    {
+        dvdl_lin += dvdlLinear[i];
+    }
+    addConstantDhdl(dvdl_lin);
+
+    /* Sum the foreign lambda energy difference contributions.
+     * Note that here we only add the potential energy components.
+     * The constraint and kinetic energy components are add after integration
+     * by sum_dhdl().
+     */
     for (int i = 0; i < fepvals.n_lambda; i++)
     {
         /* note we are iterating over fepvals here!
@@ -147,44 +206,93 @@ void sum_dhdl(gmx_enerdata_t* enerd, gmx::ArrayRef<const real> lambda, const t_l
            current lambda, because the contributions to the current
            lambda are automatically zeroed */
 
-        double& enerpart_lambda = enerd->enerpart_lambda[i + 1];
-
+        double enerpart_lambda = 0;
         for (gmx::index j = 0; j < lambda.ssize(); j++)
         {
             /* Note that this loop is over all dhdl components, not just the separated ones */
             const double dlam = fepvals.all_lambda[j][i] - lambda[j];
 
-            enerpart_lambda += dlam * enerd->dvdl_lin[j];
+            enerpart_lambda += dlam * dvdlLinear[j];
+        }
+        accumulate(1 + i, enerpart_lambda, 0);
+    }
 
-            /* Constraints can not be evaluated at foreign lambdas, so we add
-             * a linear extrapolation. This is an approximation, but usually
-             * quite accurate since constraints change little between lambdas.
-             */
-            if ((j == efptBONDED && fepvals.separate_dvdl[efptBONDED])
-                || (j == efptFEP && !fepvals.separate_dvdl[efptBONDED]))
-            {
-                enerpart_lambda += dlam * enerd->term[F_DVDL_CONSTR];
-            }
+    finalizedPotentialContributions_ = true;
+}
 
-            if (j == efptMASS && !fepvals.separate_dvdl[j])
-            {
-                enerpart_lambda += dlam * enerd->term[F_DKDL];
-            }
+void accumulatePotentialEnergies(gmx_enerdata_t* enerd, gmx::ArrayRef<const real> lambda, const t_lambda* fepvals)
+{
+    sum_epot(enerd->grpp, enerd->term);
 
-            if (debug)
-            {
-                fprintf(debug, "enerdiff lam %g: (%15s), non-linear %f linear %f*%f\n",
-                        fepvals.all_lambda[j][i], efpt_names[j],
-                        enerpart_lambda - enerd->enerpart_lambda[0], dlam, enerd->dvdl_lin[j]);
-            }
+    if (fepvals)
+    {
+        set_dvdl_output(enerd, *fepvals);
+
+        enerd->foreignLambdaTerms.finalizePotentialContributions(enerd->dvdl_lin, lambda, *fepvals);
+    }
+}
+
+void ForeignLambdaTerms::accumulateKinetic(int listIndex, double energy, double dhdl)
+{
+    energies_[listIndex] += energy;
+    dhdl_[listIndex] += dhdl;
+}
+
+void ForeignLambdaTerms::finalizeKineticContributions(gmx::ArrayRef<const real> energyTerms,
+                                                      const double              dhdlMass,
+                                                      gmx::ArrayRef<const real> lambda,
+                                                      const t_lambda&           fepvals)
+{
+    // Add perturbed mass contributions
+    addConstantDhdl(dhdlMass);
+
+    // Treat current lambda, the deltaH contribution is 0 as delta-lambda=0 for the current lambda
+    accumulateKinetic(0, 0.0, energyTerms[F_DVDL_CONSTR]);
+    if (!fepvals.separate_dvdl[efptMASS])
+    {
+        accumulateKinetic(0, 0.0, energyTerms[F_DKDL]);
+    }
+
+    for (int i = 0; i < fepvals.n_lambda; i++)
+    {
+        /* Note that potential energy terms have been added by sum_epot() -> sum_dvdl() */
+
+        /* Constraints can not be evaluated at foreign lambdas, so we add
+         * a linear extrapolation. This is an approximation, but usually
+         * quite accurate since constraints change little between lambdas.
+         */
+        const int    lambdaIndex = (fepvals.separate_dvdl[efptBONDED] ? efptBONDED : efptFEP);
+        const double dlam        = fepvals.all_lambda[lambdaIndex][i] - lambda[lambdaIndex];
+        accumulateKinetic(1 + i, dlam * energyTerms[F_DVDL_CONSTR], energyTerms[F_DVDL_CONSTR]);
+
+        if (!fepvals.separate_dvdl[efptMASS])
+        {
+            const double dlam = fepvals.all_lambda[efptMASS][i] - lambda[efptMASS];
+            accumulateKinetic(1 + i, dlam * energyTerms[F_DKDL], energyTerms[F_DKDL]);
         }
     }
+}
+
+void accumulateKineticLambdaComponents(gmx_enerdata_t*           enerd,
+                                       gmx::ArrayRef<const real> lambda,
+                                       const t_lambda&           fepvals)
+{
+    if (fepvals.separate_dvdl[efptBONDED])
+    {
+        enerd->term[F_DVDL_BONDED] += enerd->term[F_DVDL_CONSTR];
+    }
+    else
+    {
+        enerd->term[F_DVDL] += enerd->term[F_DVDL_CONSTR];
+    }
+
+    enerd->foreignLambdaTerms.finalizeKineticContributions(enerd->term, enerd->dvdl_lin[efptMASS],
+                                                           lambda, fepvals);
 
     /* The constrain contribution is now included in other terms, so clear it */
     enerd->term[F_DVDL_CONSTR] = 0;
 }
 
-
 void reset_foreign_enerdata(gmx_enerdata_t* enerd)
 {
     int i, j;
@@ -206,6 +314,15 @@ void reset_foreign_enerdata(gmx_enerdata_t* enerd)
     }
 }
 
+void reset_dvdl_enerdata(gmx_enerdata_t* enerd)
+{
+    for (int i = 0; i < efptNR; i++)
+    {
+        enerd->dvdl_lin[i]    = 0.0;
+        enerd->dvdl_nonlin[i] = 0.0;
+    }
+}
+
 void reset_enerdata(gmx_enerdata_t* enerd)
 {
     int i, j;
@@ -215,27 +332,24 @@ void reset_enerdata(gmx_enerdata_t* enerd)
     {
         for (j = 0; (j < enerd->grpp.nener); j++)
         {
-            enerd->grpp.ener[i][j] = 0.0;
+            enerd->grpp.ener[i][j] = 0.0_real;
         }
     }
-    for (i = 0; i < efptNR; i++)
-    {
-        enerd->dvdl_lin[i]    = 0.0;
-        enerd->dvdl_nonlin[i] = 0.0;
-    }
 
     /* Normal potential energy components */
     for (i = 0; (i <= F_EPOT); i++)
     {
-        enerd->term[i] = 0.0;
+        enerd->term[i] = 0.0_real;
     }
-    enerd->term[F_DVDL]           = 0.0;
-    enerd->term[F_DVDL_COUL]      = 0.0;
-    enerd->term[F_DVDL_VDW]       = 0.0;
-    enerd->term[F_DVDL_BONDED]    = 0.0;
-    enerd->term[F_DVDL_RESTRAINT] = 0.0;
-    enerd->term[F_DKDL]           = 0.0;
-    std::fill(enerd->enerpart_lambda.begin(), enerd->enerpart_lambda.end(), 0);
-    /* reset foreign energy data - separate function since we also call it elsewhere */
+    enerd->term[F_PDISPCORR]      = 0.0_real;
+    enerd->term[F_DVDL]           = 0.0_real;
+    enerd->term[F_DVDL_COUL]      = 0.0_real;
+    enerd->term[F_DVDL_VDW]       = 0.0_real;
+    enerd->term[F_DVDL_BONDED]    = 0.0_real;
+    enerd->term[F_DVDL_RESTRAINT] = 0.0_real;
+    enerd->term[F_DKDL]           = 0.0_real;
+    enerd->foreignLambdaTerms.zeroAllTerms();
+    /* reset foreign energy data and dvdl - separate functions since they are also called elsewhere */
     reset_foreign_enerdata(enerd);
+    reset_dvdl_enerdata(enerd);
 }
index 67e84377f8f0b614f3650c10b84245c9315fd96e..5e0b066e0c6a493ef2a94be3b620264fcdbf30d5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 struct gmx_enerdata_t;
 struct gmx_grppairener_t;
+struct t_fepvals;
 struct t_lambda;
 
 void reset_foreign_enerdata(gmx_enerdata_t* enerd);
 /* Resets only the foreign energy data */
 
+void reset_dvdl_enerdata(gmx_enerdata_t* enerd);
+/* Resets only the dvdl energy data */
+
 void reset_enerdata(gmx_enerdata_t* enerd);
 /* Resets the energy data */
 
-void sum_epot(gmx_grppairener_t* grpp, real* epot);
-/* Locally sum the non-bonded potential energy terms */
+/*! \brief Sums energy group pair contributions into epot */
+void sum_epot(const gmx_grppairener_t& grpp, real* epot);
+
+/*! \brief Accumulates potential energy contributions to obtain final potential energies
+ *
+ * Accumulates energy group pair contributions into the output energy components
+ * and sums all potential energies into the total potential energy term.
+ * With free-energy also computes the foreign lambda potential energy differences.
+ *
+ * \param[in,out] enerd    Energy data with components to sum and to accumulate into
+ * \param[in]     lambda   Vector of free-energy lambdas
+ * \param[in]     fepvals  Foreign lambda energy differences, only summed with !=nullptr
+ */
+void accumulatePotentialEnergies(gmx_enerdata_t*           enerd,
+                                 gmx::ArrayRef<const real> lambda,
+                                 const t_lambda*           fepvals);
 
-void sum_dhdl(gmx_enerdata_t* enerd, gmx::ArrayRef<const real> lambda, const t_lambda& fepvals);
-/* Sum the free energy contributions */
+/*! \brief Accumulates kinetic and constraint contributions to dH/dlambda and foreign energies */
+void accumulateKineticLambdaComponents(gmx_enerdata_t*           enerd,
+                                       gmx::ArrayRef<const real> lambda,
+                                       const t_lambda&           fepvals);
 
 #endif
index f2532f3dfe063cbfa5a30337ceb2ce11f3a89a42..b4bcbf975adbc67e57408b16e38798c38da82e7e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -54,7 +55,7 @@
 #include <array>
 #include <string>
 
-#include "gromacs/awh/awh.h"
+#include "gromacs/applied_forces/awh/awh.h"
 #include "gromacs/fileio/enxio.h"
 #include "gromacs/fileio/gmxfio.h"
 #include "gromacs/fileio/xvgr.h"
@@ -241,7 +242,7 @@ EnergyOutput::EnergyOutput(ener_file*               fp_ene,
     bEner_[F_COM_PULL]   = ((ir->bPull && pull_have_potential(pull_work)) || ir->bRot);
 
     MdModulesEnergyOutputToDensityFittingRequestChecker mdModulesAddOutputToDensityFittingFieldRequest;
-    mdModulesNotifier.notifier_.notify(&mdModulesAddOutputToDensityFittingFieldRequest);
+    mdModulesNotifier.simulationSetupNotifications_.notify(&mdModulesAddOutputToDensityFittingFieldRequest);
 
     bEner_[F_DENSITYFITTING] = mdModulesAddOutputToDensityFittingFieldRequest.energyOutputToDensityFitting_;
 
@@ -854,10 +855,11 @@ void EnergyOutput::addDataAtEnergyStep(bool                    bDoDHDL,
                                        double                  time,
                                        real                    tmass,
                                        const gmx_enerdata_t*   enerd,
-                                       const t_state*          state,
                                        const t_lambda*         fep,
                                        const t_expanded*       expand,
                                        const matrix            box,
+                                       PTCouplingArrays        ptCouplingArrays,
+                                       int                     fep_state,
                                        const tensor            svir,
                                        const tensor            fvir,
                                        const tensor            vir,
@@ -935,12 +937,12 @@ void EnergyOutput::addDataAtEnergyStep(bool                    bDoDHDL,
     }
     if (epc_ == epcPARRINELLORAHMAN || 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];
+        tmp6[0] = ptCouplingArrays.boxv[XX][XX];
+        tmp6[1] = ptCouplingArrays.boxv[YY][YY];
+        tmp6[2] = ptCouplingArrays.boxv[ZZ][ZZ];
+        tmp6[3] = ptCouplingArrays.boxv[YY][XX];
+        tmp6[4] = ptCouplingArrays.boxv[ZZ][XX];
+        tmp6[5] = ptCouplingArrays.boxv[ZZ][YY];
         add_ebin(ebin_, ipc_, bTricl_ ? 6 : 3, tmp6, bSum);
     }
     if (bMu_)
@@ -999,8 +1001,8 @@ void EnergyOutput::addDataAtEnergyStep(bool                    bDoDHDL,
                         for (j = 0; j < nNHC_; j++)
                         {
                             k                 = i * nNHC_ + j;
-                            tmp_r_[2 * k]     = state->nosehoover_xi[k];
-                            tmp_r_[2 * k + 1] = state->nosehoover_vxi[k];
+                            tmp_r_[2 * k]     = ptCouplingArrays.nosehoover_xi[k];
+                            tmp_r_[2 * k + 1] = ptCouplingArrays.nosehoover_vxi[k];
                         }
                     }
                     add_ebin(ebin_, itc_, mde_n_, tmp_r_, bSum);
@@ -1012,8 +1014,8 @@ void EnergyOutput::addDataAtEnergyStep(bool                    bDoDHDL,
                             for (j = 0; j < nNHC_; j++)
                             {
                                 k                 = i * nNHC_ + j;
-                                tmp_r_[2 * k]     = state->nhpres_xi[k];
-                                tmp_r_[2 * k + 1] = state->nhpres_vxi[k];
+                                tmp_r_[2 * k]     = ptCouplingArrays.nhpres_xi[k];
+                                tmp_r_[2 * k + 1] = ptCouplingArrays.nhpres_vxi[k];
                             }
                         }
                         add_ebin(ebin_, itcb_, mdeb_n_, tmp_r_, bSum);
@@ -1023,8 +1025,8 @@ void EnergyOutput::addDataAtEnergyStep(bool                    bDoDHDL,
                 {
                     for (int i = 0; (i < nTC_); i++)
                     {
-                        tmp_r_[2 * i]     = state->nosehoover_xi[i];
-                        tmp_r_[2 * i + 1] = state->nosehoover_vxi[i];
+                        tmp_r_[2 * i]     = ptCouplingArrays.nosehoover_xi[i];
+                        tmp_r_[2 * i + 1] = ptCouplingArrays.nosehoover_vxi[i];
                     }
                     add_ebin(ebin_, itc_, mde_n_, tmp_r_, bSum);
                 }
@@ -1054,20 +1056,21 @@ void EnergyOutput::addDataAtEnergyStep(bool                    bDoDHDL,
     // BAR + thermodynamic integration values
     if ((fp_dhdl_ || dhc_) && bDoDHDL)
     {
-        for (gmx::index i = 0; i < static_cast<gmx::index>(enerd->enerpart_lambda.size()) - 1; i++)
+        const auto& foreignTerms = enerd->foreignLambdaTerms;
+        for (int i = 0; i < foreignTerms.numLambdas(); i++)
         {
             /* zero for simulated tempering */
-            dE_[i] = enerd->enerpart_lambda[i + 1] - enerd->enerpart_lambda[0];
+            dE_[i] = foreignTerms.deltaH(i);
             if (numTemperatures_ > 0)
             {
-                GMX_RELEASE_ASSERT(numTemperatures_ > state->fep_state,
+                GMX_RELEASE_ASSERT(numTemperatures_ > fep_state,
                                    "Number of lambdas in state is bigger then in input record");
                 GMX_RELEASE_ASSERT(
-                        numTemperatures_ >= static_cast<gmx::index>(enerd->enerpart_lambda.size()) - 1,
+                        numTemperatures_ >= foreignTerms.numLambdas(),
                         "Number of lambdas in energy data is bigger then in input record");
                 /* MRS: is this right, given the way we have defined the exchange probabilities? */
                 /* is this even useful to have at all? */
-                dE_[i] += (temperatures_[i] / temperatures_[state->fep_state] - 1.0) * enerd->term[F_EKIN];
+                dE_[i] += (temperatures_[i] / temperatures_[fep_state] - 1.0) * enerd->term[F_EKIN];
             }
         }
 
@@ -1079,7 +1082,7 @@ void EnergyOutput::addDataAtEnergyStep(bool                    bDoDHDL,
             /* print the current state if we are doing expanded ensemble */
             if (expand->elmcmove > elmcmoveNO)
             {
-                fprintf(fp_dhdl_, " %4d", state->fep_state);
+                fprintf(fp_dhdl_, " %4d", fep_state);
             }
             /* total energy (for if the temperature changes */
 
@@ -1110,7 +1113,7 @@ void EnergyOutput::addDataAtEnergyStep(bool                    bDoDHDL,
             {
                 fprintf(fp_dhdl_, " %#.8g", dE_[i]);
             }
-            if (bDynBox_ && bDiagPres_ && (epc_ != epcNO) && !enerd->enerpart_lambda.empty()
+            if (bDynBox_ && bDiagPres_ && (epc_ != epcNO) && foreignTerms.numLambdas() > 0
                 && (fep->init_lambda < 0))
             {
                 fprintf(fp_dhdl_, " %#.8g", pv); /* PV term only needed when
@@ -1135,7 +1138,7 @@ void EnergyOutput::addDataAtEnergyStep(bool                    bDoDHDL,
             }
             store_energy = enerd->term[F_ETOT];
             /* store_dh is dE */
-            mde_delta_h_coll_add_dh(dhc_, static_cast<double>(state->fep_state), store_energy, pv,
+            mde_delta_h_coll_add_dh(dhc_, static_cast<double>(fep_state), store_energy, pv,
                                     store_dhdl, dE_ + fep->lambda_start_n, time);
         }
     }
@@ -1175,7 +1178,7 @@ void EnergyOutput::printStepToEnergyFile(ener_file* fp_ene,
     fr.nsum    = ebin_->nsum;
     fr.nre     = (bEne) ? ebin_->nener : 0;
     fr.ener    = ebin_->e;
-    int ndisre = bDR ? fcd->disres.npair : 0;
+    int ndisre = bDR ? fcd->disres->npair : 0;
     /* these are for the old-style blocks (1 subblock, only reals), because
        there can be only one per ID for these */
     int   nr[enxNR];
@@ -1187,17 +1190,18 @@ void EnergyOutput::printStepToEnergyFile(ener_file* fp_ene,
         nr[i] = 0;
     }
 
-    if (bOR && fcd->orires.nr > 0)
+    if (bOR && fcd->orires->nr > 0)
     {
-        diagonalize_orires_tensors(&(fcd->orires));
-        nr[enxOR]     = fcd->orires.nr;
-        block[enxOR]  = fcd->orires.otav;
+        t_oriresdata& orires = *fcd->orires;
+        diagonalize_orires_tensors(&orires);
+        nr[enxOR]     = orires.nr;
+        block[enxOR]  = orires.otav;
         id[enxOR]     = enxOR;
-        nr[enxORI]    = (fcd->orires.oinsl != fcd->orires.otav) ? fcd->orires.nr : 0;
-        block[enxORI] = fcd->orires.oinsl;
+        nr[enxORI]    = (orires.oinsl != orires.otav) ? orires.nr : 0;
+        block[enxORI] = orires.oinsl;
         id[enxORI]    = enxORI;
-        nr[enxORT]    = fcd->orires.nex * 12;
-        block[enxORT] = fcd->orires.eig;
+        nr[enxORT]    = orires.nex * 12;
+        block[enxORT] = orires.eig;
         id[enxORT]    = enxORT;
     }
 
@@ -1236,19 +1240,20 @@ void EnergyOutput::printStepToEnergyFile(ener_file* fp_ene,
             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;
+            const t_disresdata& disres = *fcd->disres;
+            fr.block[db].id            = enxDISRE;
+            fr.block[db].sub[0].nr     = ndisre;
+            fr.block[db].sub[1].nr     = ndisre;
 #if !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 = fcd->disres.rt;
-            fr.block[db].sub[1].fval = fcd->disres.rm3tav;
+            fr.block[db].sub[0].fval = disres.rt;
+            fr.block[db].sub[1].fval = disres.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 = fcd->disres.rt;
-            fr.block[db].sub[1].dval = fcd->disres.rm3tav;
+            fr.block[db].sub[0].dval = disres.rt;
+            fr.block[db].sub[1].dval = disres.rm3tav;
 #endif
         }
         /* here we can put new-style blocks */
@@ -1282,9 +1287,9 @@ void EnergyOutput::printStepToEnergyFile(ener_file* fp_ene,
     free_enxframe(&fr);
     if (log)
     {
-        if (bOR && fcd->orires.nr > 0)
+        if (bOR && fcd->orires->nr > 0)
         {
-            print_orires_log(log, &(fcd->orires));
+            print_orires_log(log, fcd->orires);
         }
 
         fprintf(log, "   Energies (%s)\n", unit_energy);
@@ -1293,7 +1298,7 @@ void EnergyOutput::printStepToEnergyFile(ener_file* fp_ene,
     }
 }
 
-void EnergyOutput::printAnnealingTemperatures(FILE* log, SimulationGroups* groups, t_grpopts* opts)
+void EnergyOutput::printAnnealingTemperatures(FILE* log, const SimulationGroups* groups, t_grpopts* opts)
 {
     if (log)
     {
index 070b7eeb230d79abf3c5e18a0d71f3ab268a2890..18bfa98de9e12900150a7bb191014403ba5459cc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -98,6 +98,23 @@ enum
 namespace gmx
 {
 
+/*! \internal
+ * \brief Arrays connected to Pressure and Temperature coupling
+ */
+struct PTCouplingArrays
+{
+    //! Box velocities for Parrinello-Rahman P-coupling.
+    const rvec* boxv;
+    //! Nose-Hoover coordinates.
+    ArrayRef<const double> nosehoover_xi;
+    //! Nose-Hoover velocities.
+    ArrayRef<const double> nosehoover_vxi;
+    //! Pressure Nose-Hoover coordinates.
+    ArrayRef<const double> nhpres_xi;
+    //! Pressure Nose-Hoover velocities.
+    ArrayRef<const double> nhpres_vxi;
+};
+
 /* Energy output class
  *
  * This is the collection of energy averages collected during mdrun, and to
@@ -136,32 +153,34 @@ public:
      *
      * Called every step on which the thermodynamic values are evaluated.
      *
-     * \param[in] bDoDHDL  Whether the FEP is enabled.
-     * \param[in] bSum     If this stepshould be recorded to compute sums and averaes.
-     * \param[in] time     Current simulation time.
-     * \param[in] tmass    Total mass
-     * \param[in] enerd    Energy data object.
-     * \param[in] state    System state.
-     * \param[in] fep      FEP data.
-     * \param[in] expand   Expanded ensemble (for FEP).
-     * \param[in] lastbox  PBC data.
-     * \param[in] svir     Constraint virial.
-     * \param[in] fvir     Force virial.
-     * \param[in] vir      Total virial.
-     * \param[in] pres     Pressure.
-     * \param[in] ekind    Kinetic energy data.
-     * \param[in] mu_tot   Total dipole.
-     * \param[in] constr   Constraints object to get RMSD from (for LINCS only).
+     * \param[in] bDoDHDL           Whether the FEP is enabled.
+     * \param[in] bSum              If this stepshould be recorded to compute sums and averages.
+     * \param[in] time              Current simulation time.
+     * \param[in] tmass             Total mass
+     * \param[in] enerd             Energy data object.
+     * \param[in] fep               FEP data.
+     * \param[in] expand            Expanded ensemble (for FEP).
+     * \param[in] lastbox           PBC data.
+     * \param[in] ptCouplingArrays  Arrays connected to pressure and temperature coupling.
+     * \param[in] fep_state         The current alchemical state we are in.
+     * \param[in] svir              Constraint virial.
+     * \param[in] fvir              Force virial.
+     * \param[in] vir               Total virial.
+     * \param[in] pres              Pressure.
+     * \param[in] ekind             Kinetic energy data.
+     * \param[in] mu_tot            Total dipole.
+     * \param[in] constr            Constraints object to get RMSD from (for LINCS only).
      */
     void addDataAtEnergyStep(bool                    bDoDHDL,
                              bool                    bSum,
                              double                  time,
                              real                    tmass,
                              const gmx_enerdata_t*   enerd,
-                             const t_state*          state,
                              const t_lambda*         fep,
                              const t_expanded*       expand,
                              const matrix            lastbox,
+                             PTCouplingArrays        ptCouplingArrays,
+                             int                     fep_state,
                              const tensor            svir,
                              const tensor            fvir,
                              const tensor            vir,
@@ -216,7 +235,7 @@ public:
      * \param[in] opts    Atom temperature coupling groups options
      *                    (annealing is done by groups).
      */
-    void printAnnealingTemperatures(FILE* log, SimulationGroups* groups, t_grpopts* opts);
+    static void printAnnealingTemperatures(FILE* log, const SimulationGroups* groups, t_grpopts* opts);
 
     /*! \brief Prints average values to log file.
      *
@@ -256,7 +275,7 @@ public:
     void restoreFromEnergyHistory(const energyhistory_t& enerhist);
 
     //! Print an output header to the log file.
-    void printHeader(FILE* log, int64_t steps, double time);
+    static void printHeader(FILE* log, int64_t steps, double time);
 
 private:
     //! Timestep
index 2d82013c81c06cca3893d0812b8fc41ba23d5bbb..9e55df33daa66c62cc1c61f6acedd7b8f3c84005 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2012-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -1248,8 +1248,7 @@ int ExpandedEnsembleDynamics(FILE*                 log,
             if (ir->bSimTemp)
             {
                 /* Note -- this assumes no mass changes, since kinetic energy is not added  . . . */
-                scaled_lamee[i] = (enerd->enerpart_lambda[i + 1] - enerd->enerpart_lambda[0])
-                                          / (simtemp->temperatures[i] * BOLTZ)
+                scaled_lamee[i] = enerd->foreignLambdaTerms.deltaH(i) / (simtemp->temperatures[i] * BOLTZ)
                                   + enerd->term[F_EPOT]
                                             * (1.0 / (simtemp->temperatures[i])
                                                - 1.0 / (simtemp->temperatures[fep_state]))
@@ -1257,8 +1256,7 @@ int ExpandedEnsembleDynamics(FILE*                 log,
             }
             else
             {
-                scaled_lamee[i] = (enerd->enerpart_lambda[i + 1] - enerd->enerpart_lambda[0])
-                                  / (expand->mc_temp * BOLTZ);
+                scaled_lamee[i] = enerd->foreignLambdaTerms.deltaH(i) / (expand->mc_temp * BOLTZ);
                 /* mc_temp is currently set to the system reft unless otherwise defined */
             }
 
index f659874b92a704244cb4c6b0d92dc9a50f89f176..2bd8088a6a95ad3ace559280359d8f861e7381e7 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/ewald/pme.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
-#include "gromacs/listed_forces/listed_forces.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/math/vecdump.h"
 #include "gromacs/mdlib/forcerec_threading.h"
-#include "gromacs/mdlib/qmmm.h"
-#include "gromacs/mdlib/rf_util.h"
-#include "gromacs/mdlib/wall.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/forceoutput.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/pbcutil/ishift.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/timing/wallcycle.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
 
+using gmx::ArrayRef;
+using gmx::RVec;
+
 static void clearEwaldThreadOutput(ewald_corr_thread_t* ewc_t)
 {
     ewc_t->Vcorr_q        = 0;
@@ -98,93 +98,21 @@ static void reduceEwaldThreadOuput(int nthreads, ewald_corr_thread_t* ewc_t)
     }
 }
 
-void do_force_lowlevel(t_forcerec*                         fr,
-                       const t_inputrec*                   ir,
-                       const t_idef*                       idef,
-                       const t_commrec*                    cr,
-                       const gmx_multisim_t*               ms,
-                       t_nrnb*                             nrnb,
-                       gmx_wallcycle_t                     wcycle,
-                       const t_mdatoms*                    md,
-                       gmx::ArrayRefWithPadding<gmx::RVec> coordinates,
-                       history_t*                          hist,
-                       gmx::ForceOutputs*                  forceOutputs,
-                       gmx_enerdata_t*                     enerd,
-                       t_fcdata*                           fcd,
-                       const matrix                        box,
-                       const real*                         lambda,
-                       const t_graph*                      graph,
-                       const rvec*                         mu_tot,
-                       const gmx::StepWorkload&            stepWork,
-                       const DDBalanceRegionHandler&       ddBalanceRegionHandler)
+void calculateLongRangeNonbondeds(t_forcerec*                   fr,
+                                  const t_inputrec*             ir,
+                                  const t_commrec*              cr,
+                                  t_nrnb*                       nrnb,
+                                  gmx_wallcycle_t               wcycle,
+                                  const t_mdatoms*              md,
+                                  gmx::ArrayRef<const RVec>     coordinates,
+                                  gmx::ForceWithVirial*         forceWithVirial,
+                                  gmx_enerdata_t*               enerd,
+                                  const matrix                  box,
+                                  const real*                   lambda,
+                                  const rvec*                   mu_tot,
+                                  const gmx::StepWorkload&      stepWork,
+                                  const DDBalanceRegionHandler& ddBalanceRegionHandler)
 {
-    // TODO: Replace all uses of x by const coordinates
-    rvec* x = as_rvec_array(coordinates.paddedArrayRef().data());
-
-    auto& forceWithVirial = forceOutputs->forceWithVirial();
-
-    /* do QMMM first if requested */
-    if (fr->bQMMM)
-    {
-        enerd->term[F_EQM] = calculate_QMMM(cr, &forceOutputs->forceWithShiftForces(), fr);
-    }
-
-    /* Call the short range functions all in one go. */
-
-    if (ir->nwall)
-    {
-        /* foreign lambda component for walls */
-        real dvdl_walls = do_walls(*ir, *fr, box, *md, x, &forceWithVirial, lambda[efptVDW],
-                                   enerd->grpp.ener[egLJSR].data(), nrnb);
-        enerd->dvdl_lin[efptVDW] += dvdl_walls;
-    }
-
-    /* Shift the coordinates. Must be done before listed forces and PPPM,
-     * but is also necessary for SHAKE and update, therefore it can NOT
-     * go when no listed forces have to be evaluated.
-     *
-     * The shifting and PBC code is deliberately not timed, since with
-     * the Verlet scheme it only takes non-zero time with triclinic
-     * boxes, and even then the time is around a factor of 100 less
-     * than the next smallest counter.
-     */
-
-
-    /* Here sometimes we would not need to shift with NBFonly,
-     * but we do so anyhow for consistency of the returned coordinates.
-     */
-    if (graph)
-    {
-        shift_self(graph, box, x);
-        if (TRICLINIC(box))
-        {
-            inc_nrnb(nrnb, eNR_SHIFTX, 2 * graph->nnodes);
-        }
-        else
-        {
-            inc_nrnb(nrnb, eNR_SHIFTX, graph->nnodes);
-        }
-    }
-
-    {
-        t_pbc pbc;
-
-        /* Check whether we need to take into account PBC in listed interactions. */
-        const auto needPbcForListedForces =
-                fr->bMolPBC && stepWork.computeListedForces && haveCpuListedForces(*fr, *idef, *fcd);
-        if (needPbcForListedForces)
-        {
-            /* Since all atoms are in the rectangular or triclinic unit-cell,
-             * only single box vector shifts (2 in x) are required.
-             */
-            set_pbc_dd(&pbc, fr->ePBC, DOMAINDECOMP(cr) ? cr->dd->nc : nullptr, TRUE, box);
-        }
-
-        do_force_listed(wcycle, box, ir->fepvals, cr, ms, idef, x, hist, forceOutputs, fr, &pbc,
-                        graph, enerd, nrnb, lambda, md, fcd,
-                        DOMAINDECOMP(cr) ? cr->dd->globalAtomIndices.data() : nullptr, stepWork);
-    }
-
     const bool computePmeOnCpu = (EEL_PME(fr->ic->eeltype) || EVDW_PME(fr->ic->vdwtype))
                                  && thisRankHasDuty(cr, DUTY_PME)
                                  && (pme_run_mode(fr->pmedata) == PmeRunMode::CPU);
@@ -194,7 +122,8 @@ void do_force_lowlevel(t_forcerec*                         fr,
     /* Do long-range electrostatics and/or LJ-PME
      * and compute PME surface terms when necessary.
      */
-    if (computePmeOnCpu || fr->ic->eeltype == eelEWALD || haveEwaldSurfaceTerm)
+    if ((computePmeOnCpu || fr->ic->eeltype == eelEWALD || haveEwaldSurfaceTerm)
+        && stepWork.computeNonbondedForces)
     {
         int  status = 0;
         real Vlr_q = 0, Vlr_lj = 0;
@@ -212,13 +141,6 @@ void do_force_lowlevel(t_forcerec*                         fr,
             {
                 wallcycle_sub_start(wcycle, ewcsEWALD_CORRECTION);
 
-                if (fr->n_tpi > 0)
-                {
-                    gmx_fatal(FARGS,
-                              "TPI with PME currently only works in a 3D geometry with tin-foil "
-                              "boundary conditions");
-                }
-
                 int nthreads = fr->nthread_ewc;
 #pragma omp parallel for num_threads(nthreads) schedule(static)
                 for (int t = 0; t < nthreads; t++)
@@ -236,9 +158,10 @@ void do_force_lowlevel(t_forcerec*                         fr,
                          * exclusion forces) are calculated, so we can store
                          * the forces in the normal, single forceWithVirial->force_ array.
                          */
+                        const rvec* x = as_rvec_array(coordinates.data());
                         ewald_LRcorrection(md->homenr, cr, nthreads, t, *fr, *ir, md->chargeA,
                                            md->chargeB, (md->nChargePerturbed != 0), x, box, mu_tot,
-                                           as_rvec_array(forceWithVirial.force_.data()),
+                                           as_rvec_array(forceWithVirial->force_.data()),
                                            &ewc_t.Vcorr_q, lambda[efptCOUL], &ewc_t.dvdl[efptCOUL]);
                     }
                     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
@@ -264,22 +187,6 @@ void do_force_lowlevel(t_forcerec*                         fr,
                 assert(fr->n_tpi >= 0);
                 if (fr->n_tpi == 0 || stepWork.stateChanged)
                 {
-                    int pme_flags = GMX_PME_SPREAD | GMX_PME_SOLVE;
-
-                    if (stepWork.computeForces)
-                    {
-                        pme_flags |= GMX_PME_CALC_F;
-                    }
-                    if (stepWork.computeVirial)
-                    {
-                        pme_flags |= GMX_PME_CALC_ENER_VIR;
-                    }
-                    if (fr->n_tpi > 0)
-                    {
-                        /* We don't calculate f, but we do want the potential */
-                        pme_flags |= GMX_PME_CALC_POT;
-                    }
-
                     /* With domain decomposition we close the CPU side load
                      * balancing region here, because PME does global
                      * communication that acts as a global barrier.
@@ -289,15 +196,14 @@ void do_force_lowlevel(t_forcerec*                         fr,
                     wallcycle_start(wcycle, ewcPMEMESH);
                     status = gmx_pme_do(
                             fr->pmedata,
-                            gmx::constArrayRefFromArray(coordinates.unpaddedConstArrayRef().data(),
-                                                        md->homenr - fr->n_tpi),
-                            forceWithVirial.force_, md->chargeA, md->chargeB, md->sqrt_c6A,
+                            gmx::constArrayRefFromArray(coordinates.data(), md->homenr - fr->n_tpi),
+                            forceWithVirial->force_, md->chargeA, md->chargeB, md->sqrt_c6A,
                             md->sqrt_c6B, md->sigmaA, md->sigmaB, box, cr,
                             DOMAINDECOMP(cr) ? dd_pme_maxshift_x(cr->dd) : 0,
                             DOMAINDECOMP(cr) ? dd_pme_maxshift_y(cr->dd) : 0, nrnb, wcycle,
                             ewaldOutput.vir_q, ewaldOutput.vir_lj, &Vlr_q, &Vlr_lj,
                             lambda[efptCOUL], lambda[efptVDW], &ewaldOutput.dvdl[efptCOUL],
-                            &ewaldOutput.dvdl[efptVDW], pme_flags);
+                            &ewaldOutput.dvdl[efptVDW], stepWork);
                     wallcycle_stop(wcycle, ewcPMEMESH);
                     if (status != 0)
                     {
@@ -314,17 +220,11 @@ void do_force_lowlevel(t_forcerec*                         fr,
                 }
                 if (fr->n_tpi > 0)
                 {
-                    if (EVDW_PME(ir->vdwtype))
-                    {
-
-                        gmx_fatal(FARGS, "Test particle insertion not implemented with LJ-PME");
-                    }
                     /* Determine the PME grid energy of the test molecule
                      * with the PME grid potential of the other charges.
                      */
                     gmx_pme_calc_energy(
-                            fr->pmedata,
-                            coordinates.unpaddedConstArrayRef().subArray(md->homenr - fr->n_tpi, fr->n_tpi),
+                            fr->pmedata, coordinates.subArray(md->homenr - fr->n_tpi, fr->n_tpi),
                             gmx::arrayRefFromArray(md->chargeA + md->homenr - fr->n_tpi, fr->n_tpi),
                             &Vlr_q);
                 }
@@ -333,7 +233,8 @@ void do_force_lowlevel(t_forcerec*                         fr,
 
         if (fr->ic->eeltype == eelEWALD)
         {
-            Vlr_q = do_ewald(ir, x, as_rvec_array(forceWithVirial.force_.data()), md->chargeA,
+            const rvec* x = as_rvec_array(coordinates.data());
+            Vlr_q = do_ewald(ir, x, as_rvec_array(forceWithVirial->force_.data()), md->chargeA,
                              md->chargeB, box, cr, md->homenr, ewaldOutput.vir_q, fr->ic->ewaldcoeff_q,
                              lambda[efptCOUL], &ewaldOutput.dvdl[efptCOUL], fr->ewald_table);
         }
@@ -341,8 +242,8 @@ void do_force_lowlevel(t_forcerec*                         fr,
         /* Note that with separate PME nodes we get the real energies later */
         // TODO it would be simpler if we just accumulated a single
         // long-range virial contribution.
-        forceWithVirial.addVirialContribution(ewaldOutput.vir_q);
-        forceWithVirial.addVirialContribution(ewaldOutput.vir_lj);
+        forceWithVirial->addVirialContribution(ewaldOutput.vir_q);
+        forceWithVirial->addVirialContribution(ewaldOutput.vir_lj);
         enerd->dvdl_lin[efptCOUL] += ewaldOutput.dvdl[efptCOUL];
         enerd->dvdl_lin[efptVDW] += ewaldOutput.dvdl[efptVDW];
         enerd->term[F_COUL_RECIP] = Vlr_q + ewaldOutput.Vcorr_q;
@@ -353,8 +254,6 @@ void do_force_lowlevel(t_forcerec*                         fr,
             fprintf(debug, "Vlr_q = %g, Vcorr_q = %g, Vlr_corr_q = %g\n", Vlr_q,
                     ewaldOutput.Vcorr_q, enerd->term[F_COUL_RECIP]);
             pr_rvecs(debug, 0, "vir_el_recip after corr", ewaldOutput.vir_q, DIM);
-            rvec* fshift = as_rvec_array(forceOutputs->forceWithShiftForces().shiftForces().data());
-            pr_rvecs(debug, 0, "fshift after LR Corrections", fshift, SHIFTS);
             fprintf(debug, "Vlr_lj: %g, Vcorr_lj = %g, Vlr_corr_lj = %g\n", Vlr_lj,
                     ewaldOutput.Vcorr_lj, enerd->term[F_LJ_RECIP]);
             pr_rvecs(debug, 0, "vir_lj_recip after corr", ewaldOutput.vir_lj, DIM);
@@ -365,10 +264,4 @@ void do_force_lowlevel(t_forcerec*                         fr,
     {
         print_nrnb(debug, nrnb);
     }
-
-    if (debug)
-    {
-        rvec* fshift = as_rvec_array(forceOutputs->forceWithShiftForces().shiftForces().data());
-        pr_rvecs(debug, 0, "fshift after bondeds", fshift, SHIFTS);
-    }
 }
index c08e113b2d98603fa940ef08a97254a5f2e9c0c7..63f80e88394acbfc85503b10c56c55207104f7a5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_MDLIB_FORCE_H
 #define GMX_MDLIB_FORCE_H
 
-#include "gromacs/math/arrayrefwithpadding.h"
+#include <cstdio>
+
 #include "gromacs/math/vectypes.h"
-#include "gromacs/mdtypes/forceoutput.h"
-#include "gromacs/utility/arrayref.h"
 
 class DDBalanceRegionHandler;
 struct gmx_edsam;
@@ -49,15 +49,12 @@ struct gmx_enfrot;
 struct SimulationGroups;
 struct gmx_localtop_t;
 struct gmx_multisim_t;
-struct gmx_vsite_t;
 struct gmx_wallcycle;
 class history_t;
+class InteractionDefinitions;
 struct pull_t;
 struct t_commrec;
-struct t_fcdata;
 struct t_forcerec;
-struct t_graph;
-struct t_idef;
 struct t_inputrec;
 struct t_lambda;
 struct t_mdatoms;
@@ -65,14 +62,33 @@ struct t_nrnb;
 
 namespace gmx
 {
+template<typename>
+class ArrayRef;
+template<typename>
+class ArrayRefWithPadding;
 class Awh;
+class ForceBuffersView;
 class ForceWithVirial;
 class ImdSession;
 class MdrunScheduleWorkload;
 class MDLogger;
 class StepWorkload;
+class VirtualSitesHandler;
 } // namespace gmx
 
+/* Perform the force and, if requested, energy computation
+ *
+ * Without multiple time stepping the force is returned in force->force().
+ *
+ * With multiple time stepping the behavior depends on the integration step.
+ * At fast steps (step % mtsFactor != 0), the fast force is returned in
+ * force->force(). The force->forceMtsCombined() buffer is unused.
+ * At slow steps, the normal force is returned in force->force(),
+ * unless the \p GMX_FORCE_DO_NOT_NEED_NORMAL_FORCE is set in \p legacyFlags.
+ * A MTS-combined force, F_fast + mtsFactor*F_slow, is always returned in
+ * force->forceMtsCombined(). This forceMts can be used directly in a standard
+ * leap-frog integrator to do multiple time stepping.
+ */
 void do_force(FILE*                               log,
               const t_commrec*                    cr,
               const gmx_multisim_t*               ms,
@@ -88,16 +104,14 @@ void do_force(FILE*                               log,
               const matrix                        box,
               gmx::ArrayRefWithPadding<gmx::RVec> coordinates,
               history_t*                          hist,
-              gmx::ArrayRefWithPadding<gmx::RVec> force,
+              gmx::ForceBuffersView*              force,
               tensor                              vir_force,
               const t_mdatoms*                    mdatoms,
               gmx_enerdata_t*                     enerd,
-              t_fcdata*                           fcd,
-              gmx::ArrayRef<real>                 lambda,
-              t_graph*                            graph,
+              gmx::ArrayRef<const real>           lambda,
               t_forcerec*                         fr,
               gmx::MdrunScheduleWorkload*         runScheduleWork,
-              const gmx_vsite_t*                  vsite,
+              gmx::VirtualSitesHandler*           vsite,
               rvec                                mu_tot,
               double                              t,
               gmx_edsam*                          ed,
@@ -114,25 +128,24 @@ void do_force(FILE*                               log,
  */
 
 
-void do_force_lowlevel(t_forcerec*                         fr,
-                       const t_inputrec*                   ir,
-                       const t_idef*                       idef,
-                       const t_commrec*                    cr,
-                       const gmx_multisim_t*               ms,
-                       t_nrnb*                             nrnb,
-                       gmx_wallcycle*                      wcycle,
-                       const t_mdatoms*                    md,
-                       gmx::ArrayRefWithPadding<gmx::RVec> coordinates,
-                       history_t*                          hist,
-                       gmx::ForceOutputs*                  forceOutputs,
-                       gmx_enerdata_t*                     enerd,
-                       t_fcdata*                           fcd,
-                       const matrix                        box,
-                       const real*                         lambda,
-                       const t_graph*                      graph,
-                       const rvec*                         mu_tot,
-                       const gmx::StepWorkload&            stepWork,
-                       const DDBalanceRegionHandler&       ddBalanceRegionHandler);
-/* Call all the force routines */
+/* Calculate CPU Ewald or PME-mesh forces when done on this rank and Ewald corrections, when used
+ *
+ * Note that Ewald dipole and net charge corrections are always computed here, independently
+ * on whether the PME-mesh contribution is computed on a separate PME rank or on a GPU.
+ */
+void calculateLongRangeNonbondeds(t_forcerec*                    fr,
+                                  const t_inputrec*              ir,
+                                  const t_commrec*               cr,
+                                  t_nrnb*                        nrnb,
+                                  gmx_wallcycle*                 wcycle,
+                                  const t_mdatoms*               md,
+                                  gmx::ArrayRef<const gmx::RVec> coordinates,
+                                  gmx::ForceWithVirial*          forceWithVirial,
+                                  gmx_enerdata_t*                enerd,
+                                  const matrix                   box,
+                                  const real*                    lambda,
+                                  const rvec*                    mu_tot,
+                                  const gmx::StepWorkload&       stepWork,
+                                  const DDBalanceRegionHandler&  ddBalanceRegionHandler);
 
 #endif
index 10f3969aaf4aba48377ff287ffaee4c1606ba447..dd138740e0e1cfae0b687efa35c3f3de85108df9 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2012, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -57,6 +57,8 @@
 #define GMX_FORCE_ENERGY (1u << 9u)
 /* Calculate dHdl */
 #define GMX_FORCE_DHDL (1u << 10u)
+/* Tells whether only the MTS combined force buffer is needed and not the normal force buffer */
+#define GMX_FORCE_DO_NOT_NEED_NORMAL_FORCE (1u << 11u)
 
 /* Normally one want all energy terms and forces */
 #define GMX_FORCE_ALLFORCES (GMX_FORCE_LISTED | GMX_FORCE_NONBONDED | GMX_FORCE_FORCES)
index c7aeca1a8b7b1379716bda636cc67bf04cfb697d..0a9f0dc89f6d760a31fcef32f4cdacd9a7982daf 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013-2020, by the GROMACS development team, led by
+ * Copyright (c) 2013-2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -60,7 +60,7 @@
 #include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/listed_forces/gpubonded.h"
-#include "gromacs/listed_forces/manage_threading.h"
+#include "gromacs/listed_forces/listed_forces.h"
 #include "gromacs/listed_forces/pairs.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/units.h"
 #include "gromacs/mdlib/forcerec_threading.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdlib/md_support.h"
-#include "gromacs/mdlib/qmmm.h"
 #include "gromacs/mdlib/rf_util.h"
 #include "gromacs/mdlib/wall.h"
+#include "gromacs/mdlib/wholemoleculetransform.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/fcdata.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/iforceprovider.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/nbnxm/gpu_data_mgmt.h"
+#include "gromacs/mdtypes/multipletimestepping.h"
 #include "gromacs/nbnxm/nbnxm.h"
-#include "gromacs/nbnxm/nbnxm_geometry.h"
 #include "gromacs/pbcutil/ishift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/tables/forcetable.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/strconvert.h"
 
-/*! \brief environment variable to enable GPU P2P communication */
-static const bool c_enableGpuPmePpComms =
-        (getenv("GMX_GPU_PME_PP_COMMS") != nullptr) && GMX_THREAD_MPI && (GMX_GPU == GMX_GPU_CUDA);
+#include "gpuforcereduction.h"
 
-static real* mk_nbfp(const gmx_ffparams_t* idef, gmx_bool bBHAM)
+ForceHelperBuffers::ForceHelperBuffers(bool haveDirectVirialContributions) :
+    haveDirectVirialContributions_(haveDirectVirialContributions)
 {
-    real* nbfp;
-    int   i, j, k, atnr;
+    shiftForces_.resize(SHIFTS);
+}
+
+void ForceHelperBuffers::resize(int numAtoms)
+{
+    if (haveDirectVirialContributions_)
+    {
+        forceBufferForDirectVirialContributions_.resize(numAtoms);
+    }
+}
+
+static std::vector<real> mk_nbfp(const gmx_ffparams_t* idef, gmx_bool bBHAM)
+{
+    std::vector<real> nbfp;
+    int               atnr;
 
     atnr = idef->atnr;
     if (bBHAM)
     {
-        snew(nbfp, 3 * atnr * atnr);
-        for (i = k = 0; (i < atnr); i++)
+        nbfp.resize(3 * atnr * atnr);
+        int k = 0;
+        for (int i = 0; (i < atnr); i++)
         {
-            for (j = 0; (j < atnr); j++, k++)
+            for (int j = 0; (j < atnr); j++, k++)
             {
                 BHAMA(nbfp, atnr, i, j) = idef->iparams[k].bham.a;
                 BHAMB(nbfp, atnr, i, j) = idef->iparams[k].bham.b;
@@ -124,10 +138,11 @@ static real* mk_nbfp(const gmx_ffparams_t* idef, gmx_bool bBHAM)
     }
     else
     {
-        snew(nbfp, 2 * atnr * atnr);
-        for (i = k = 0; (i < atnr); i++)
+        nbfp.resize(2 * atnr * atnr);
+        int k = 0;
+        for (int i = 0; (i < atnr); i++)
         {
-            for (j = 0; (j < atnr); j++, k++)
+            for (int j = 0; (j < atnr); j++, k++)
             {
                 /* nbfp now includes the 6.0/12.0 derivative prefactors */
                 C6(nbfp, atnr, i, j)  = idef->iparams[k].lj.c6 * 6.0;
@@ -186,14 +201,10 @@ enum
     acSETTLE
 };
 
-static cginfo_mb_t* init_cginfo_mb(const gmx_mtop_t* mtop, const t_forcerec* fr, gmx_bool* bFEP_NonBonded)
+static std::vector<cginfo_mb_t> init_cginfo_mb(const gmx_mtop_t* mtop, const t_forcerec* fr)
 {
-    cginfo_mb_t* cginfo_mb;
-    gmx_bool*    type_VDW;
-    int*         cginfo;
-    int*         a_con;
-
-    snew(cginfo_mb, mtop->molblock.size());
+    gmx_bool* type_VDW;
+    int*      a_con;
 
     snew(type_VDW, fr->ntype);
     for (int ai = 0; ai < fr->ntype; ai++)
@@ -206,14 +217,13 @@ static cginfo_mb_t* init_cginfo_mb(const gmx_mtop_t* mtop, const t_forcerec* fr,
         }
     }
 
-    *bFEP_NonBonded = FALSE;
-
-    int a_offset = 0;
+    std::vector<cginfo_mb_t> cginfoPerMolblock;
+    int                      a_offset = 0;
     for (size_t mb = 0; mb < mtop->molblock.size(); mb++)
     {
         const gmx_molblock_t& molb = mtop->molblock[mb];
         const gmx_moltype_t&  molt = mtop->moltype[molb.type];
-        const t_blocka&       excl = molt.excls;
+        const auto&           excl = molt.excls;
 
         /* Check if the cginfo is identical for all molecules in this block.
          * If so, we only need an array of the size of one molecule.
@@ -241,11 +251,12 @@ static cginfo_mb_t* init_cginfo_mb(const gmx_mtop_t* mtop, const t_forcerec* fr,
             }
         }
 
-        cginfo_mb[mb].cg_start = a_offset;
-        cginfo_mb[mb].cg_end   = a_offset + molb.nmol * molt.atoms.nr;
-        cginfo_mb[mb].cg_mod   = (bId ? 1 : molb.nmol) * molt.atoms.nr;
-        snew(cginfo_mb[mb].cginfo, cginfo_mb[mb].cg_mod);
-        cginfo = cginfo_mb[mb].cginfo;
+        cginfo_mb_t cginfo_mb;
+        cginfo_mb.cg_start = a_offset;
+        cginfo_mb.cg_end   = a_offset + molb.nmol * molt.atoms.nr;
+        cginfo_mb.cg_mod   = (bId ? 1 : molb.nmol) * molt.atoms.nr;
+        cginfo_mb.cginfo.resize(cginfo_mb.cg_mod);
+        gmx::ArrayRef<int> cginfo = cginfo_mb.cginfo;
 
         /* Set constraints flags for constrained atoms */
         snew(a_con, molt.atoms.nr);
@@ -285,9 +296,9 @@ static cginfo_mb_t* init_cginfo_mb(const gmx_mtop_t* mtop, const t_forcerec* fr,
 
                 bool haveExclusions = false;
                 /* Loop over all the exclusions of atom ai */
-                for (int j = excl.index[a]; j < excl.index[a + 1]; j++)
+                for (const int j : excl[a])
                 {
-                    if (excl.a[j] != a)
+                    if (j != a)
                     {
                         haveExclusions = true;
                         break;
@@ -315,21 +326,22 @@ static cginfo_mb_t* init_cginfo_mb(const gmx_mtop_t* mtop, const t_forcerec* fr,
                 if (fr->efep != efepNO && PERTURBED(atom))
                 {
                     SET_CGINFO_FEP(atomInfo);
-                    *bFEP_NonBonded = TRUE;
                 }
             }
         }
 
         sfree(a_con);
 
+        cginfoPerMolblock.push_back(cginfo_mb);
+
         a_offset += molb.nmol * molt.atoms.nr;
     }
     sfree(type_VDW);
 
-    return cginfo_mb;
+    return cginfoPerMolblock;
 }
 
-static std::vector<int> cginfo_expand(const int nmb, const cginfo_mb_t* cgi_mb)
+static std::vector<int> cginfo_expand(const int nmb, gmx::ArrayRef<const cginfo_mb_t> cgi_mb)
 {
     const int ncg = cgi_mb[nmb - 1].cg_end;
 
@@ -348,19 +360,6 @@ static std::vector<int> cginfo_expand(const int nmb, const cginfo_mb_t* cgi_mb)
     return cginfo;
 }
 
-static void done_cginfo_mb(cginfo_mb_t* cginfo_mb, int numMolBlocks)
-{
-    if (cginfo_mb == nullptr)
-    {
-        return;
-    }
-    for (int mb = 0; mb < numMolBlocks; ++mb)
-    {
-        sfree(cginfo_mb[mb].cginfo);
-    }
-    sfree(cginfo_mb);
-}
-
 /* Sets the sum of charges (squared) and C6 in the system in fr.
  * Returns whether the system has a net charge.
  */
@@ -554,26 +553,23 @@ static void count_tables(int ftype1, int ftype2, const gmx_mtop_t* mtop, int* nc
  *
  * A fatal error occurs if no matching filename is found.
  */
-static bondedtable_t* make_bonded_tables(FILE*                            fplog,
-                                         int                              ftype1,
-                                         int                              ftype2,
-                                         const gmx_mtop_t*                mtop,
-                                         gmx::ArrayRef<const std::string> tabbfnm,
-                                         const char*                      tabext)
+static std::vector<bondedtable_t> make_bonded_tables(FILE*                            fplog,
+                                                     int                              ftype1,
+                                                     int                              ftype2,
+                                                     const gmx_mtop_t*                mtop,
+                                                     gmx::ArrayRef<const std::string> tabbfnm,
+                                                     const char*                      tabext)
 {
-    int            ncount, *count;
-    bondedtable_t* tab;
-
-    tab = nullptr;
+    std::vector<bondedtable_t> tab;
 
-    ncount = 0;
-    count  = nullptr;
+    int  ncount = 0;
+    int* count  = nullptr;
     count_tables(ftype1, ftype2, mtop, &ncount, &count);
 
     // Are there any relevant tabulated bond interactions?
     if (ncount > 0)
     {
-        snew(tab, ncount);
+        tab.resize(ncount);
         for (int i = 0; i < ncount; i++)
         {
             // Do any interactions exist that requires this table?
@@ -618,14 +614,9 @@ void forcerec_set_ranges(t_forcerec* fr, int natoms_force, int natoms_force_cons
     fr->natoms_force        = natoms_force;
     fr->natoms_force_constr = natoms_force_constr;
 
-    if (fr->natoms_force_constr > fr->nalloc_force)
-    {
-        fr->nalloc_force = over_alloc_dd(fr->natoms_force_constr);
-    }
-
-    if (fr->haveDirectVirialContributions)
+    for (auto& forceHelperBuffers : fr->forceHelperBuffers)
     {
-        fr->forceBufferForDirectVirialContributions.resize(natoms_f_novirsum);
+        forceHelperBuffers.resize(natoms_f_novirsum);
     }
 }
 
@@ -733,6 +724,7 @@ static void initVdwEwaldParameters(FILE* fp, const t_inputrec* ir, interaction_c
  * both accuracy requirements, when relevant.
  */
 static void init_ewald_f_table(const interaction_const_t& ic,
+                               const real                 tableExtensionLength,
                                EwaldCorrectionTables*     coulombTables,
                                EwaldCorrectionTables*     vdwTables)
 {
@@ -744,7 +736,20 @@ static void init_ewald_f_table(const interaction_const_t& ic,
      */
     const real tableScale = ewald_spline3_table_scale(ic, useCoulombTable, useVdwTable);
 
-    const int tableSize = static_cast<int>(ic.rcoulomb * tableScale) + 2;
+    const bool havePerturbedNonbondeds = (ic.softCoreParameters != nullptr);
+
+    real tableLen = ic.rcoulomb;
+    if (useCoulombTable && havePerturbedNonbondeds && tableExtensionLength > 0.0)
+    {
+        /* TODO: Ideally this should also check if couple-intramol == no, but that isn't
+         * stored in ir. Grompp puts that info into an opts structure that doesn't make it into the tpr.
+         * The alternative is to look through all the exclusions and check if they come from
+         * couple-intramol == no. Meanwhile, always having larger tables should only affect
+         * memory consumption, not speed (barring cache issues).
+         */
+        tableLen = ic.rcoulomb + tableExtensionLength;
+    }
+    const int tableSize = static_cast<int>(tableLen * tableScale) + 2;
 
     if (useCoulombTable)
     {
@@ -758,11 +763,12 @@ static void init_ewald_f_table(const interaction_const_t& ic,
     }
 }
 
-void init_interaction_const_tables(FILE* fp, interaction_const_t* ic)
+void init_interaction_const_tables(FILE* fp, interaction_const_t* ic, const real tableExtensionLength)
 {
     if (EEL_PME_EWALD(ic->eeltype) || EVDW_PME(ic->vdwtype))
     {
-        init_ewald_f_table(*ic, ic->coulombEwaldTables.get(), ic->vdwEwaldTables.get());
+        init_ewald_f_table(*ic, tableExtensionLength, ic->coulombEwaldTables.get(),
+                           ic->vdwEwaldTables.get());
         if (fp != nullptr)
         {
             fprintf(fp, "Initialized non-bonded Ewald tables, spacing: %.2e size: %zu\n\n",
@@ -823,8 +829,6 @@ static void init_interaction_const(FILE*                 fp,
 {
     interaction_const_t* ic = new interaction_const_t;
 
-    ic->cutoff_scheme = ir->cutoff_scheme;
-
     ic->coulombEwaldTables = std::make_unique<EwaldCorrectionTables>();
     ic->vdwEwaldTables     = std::make_unique<EwaldCorrectionTables>();
 
@@ -934,82 +938,18 @@ static void init_interaction_const(FILE*                 fp,
         fprintf(fp, "\n");
     }
 
-    *interaction_const = ic;
-}
-
-bool areMoleculesDistributedOverPbc(const t_inputrec& ir, const gmx_mtop_t& mtop, const gmx::MDLogger& mdlog)
-{
-    bool       areMoleculesDistributedOverPbc = false;
-    const bool useEwaldSurfaceCorrection = (EEL_PME_EWALD(ir.coulombtype) && ir.epsilon_surface != 0);
-
-    const bool bSHAKE =
-            (ir.eConstrAlg == econtSHAKE
-             && (gmx_mtop_ftype_count(mtop, F_CONSTR) > 0 || gmx_mtop_ftype_count(mtop, F_CONSTRNC) > 0));
-
-    /* The group cut-off scheme and SHAKE assume charge groups
-     * are whole, but not using molpbc is faster in most cases.
-     * With intermolecular interactions we need PBC for calculating
-     * distances between atoms in different molecules.
-     */
-    if (bSHAKE && !mtop.bIntermolecularInteractions)
-    {
-        areMoleculesDistributedOverPbc = ir.bPeriodicMols;
-
-        if (areMoleculesDistributedOverPbc)
-        {
-            gmx_fatal(FARGS, "SHAKE is not supported with periodic molecules");
-        }
-    }
-    else
+    if (ir->efep != efepNO)
     {
-        /* Not making molecules whole is faster in most cases,
-         * but with orientation restraints or non-tinfoil boundary
-         * conditions we need whole molecules.
-         */
-        areMoleculesDistributedOverPbc =
-                (gmx_mtop_ftype_count(mtop, F_ORIRES) == 0 && !useEwaldSurfaceCorrection);
-
-        if (getenv("GMX_USE_GRAPH") != nullptr)
-        {
-            areMoleculesDistributedOverPbc = false;
-
-            GMX_LOG(mdlog.warning)
-                    .asParagraph()
-                    .appendText(
-                            "GMX_USE_GRAPH is set, using the graph for bonded "
-                            "interactions");
-
-            if (mtop.bIntermolecularInteractions)
-            {
-                GMX_LOG(mdlog.warning)
-                        .asParagraph()
-                        .appendText(
-                                "WARNING: Molecules linked by intermolecular interactions "
-                                "have to reside in the same periodic image, otherwise "
-                                "artifacts will occur!");
-            }
-        }
-
-        GMX_RELEASE_ASSERT(areMoleculesDistributedOverPbc || !mtop.bIntermolecularInteractions,
-                           "We need to use PBC within molecules with inter-molecular interactions");
-
-        if (bSHAKE && areMoleculesDistributedOverPbc)
-        {
-            gmx_fatal(FARGS,
-                      "SHAKE is not properly supported with intermolecular interactions. "
-                      "For short simulations where linked molecules remain in the same "
-                      "periodic image, the environment variable GMX_USE_GRAPH can be used "
-                      "to override this check.\n");
-        }
+        GMX_RELEASE_ASSERT(ir->fepvals, "ir->fepvals should be set wth free-energy");
+        ic->softCoreParameters = std::make_unique<interaction_const_t::SoftCoreParameters>(*ir->fepvals);
     }
 
-    return areMoleculesDistributedOverPbc;
+    *interaction_const = ic;
 }
 
 void init_forcerec(FILE*                            fp,
                    const gmx::MDLogger&             mdlog,
                    t_forcerec*                      fr,
-                   t_fcdata*                        fcd,
                    const t_inputrec*                ir,
                    const gmx_mtop_t*                mtop,
                    const t_commrec*                 cr,
@@ -1017,24 +957,14 @@ void init_forcerec(FILE*                            fp,
                    const char*                      tabfn,
                    const char*                      tabpfn,
                    gmx::ArrayRef<const std::string> tabbfnm,
-                   const gmx_hw_info_t&             hardwareInfo,
-                   const gmx_device_info_t*         deviceInfo,
-                   const bool                       useGpuForBonded,
-                   const bool                       pmeOnlyRankUsesGpu,
-                   real                             print_force,
-                   gmx_wallcycle*                   wcycle)
+                   real                             print_force)
 {
-    real     rtab;
-    char*    env;
-    double   dbl;
-    gmx_bool bFEP_NonBonded;
+    /* The CMake default turns SIMD kernels on, but it might be turned off further down... */
+    fr->use_simd_kernels = GMX_USE_SIMD_KERNELS;
 
-    /* By default we turn SIMD kernels on, but it might be turned off further down... */
-    fr->use_simd_kernels = TRUE;
-
-    if (check_box(ir->ePBC, box))
+    if (check_box(ir->pbcType, box))
     {
-        gmx_fatal(FARGS, "%s", check_box(ir->ePBC, box));
+        gmx_fatal(FARGS, "%s", check_box(ir->pbcType, box));
     }
 
     /* Test particle insertion ? */
@@ -1076,45 +1006,7 @@ void init_forcerec(FILE*                            fp,
     fr->fc_stepsize = ir->fc_stepsize;
 
     /* Free energy */
-    fr->efep        = ir->efep;
-    fr->sc_alphavdw = ir->fepvals->sc_alpha;
-    if (ir->fepvals->bScCoul)
-    {
-        fr->sc_alphacoul  = ir->fepvals->sc_alpha;
-        fr->sc_sigma6_min = gmx::power6(ir->fepvals->sc_sigma_min);
-    }
-    else
-    {
-        fr->sc_alphacoul  = 0;
-        fr->sc_sigma6_min = 0; /* only needed when bScCoul is on */
-    }
-    fr->sc_power      = ir->fepvals->sc_power;
-    fr->sc_r_power    = ir->fepvals->sc_r_power;
-    fr->sc_sigma6_def = gmx::power6(ir->fepvals->sc_sigma);
-
-    env = getenv("GMX_SCSIGMA_MIN");
-    if (env != nullptr)
-    {
-        dbl = 0;
-        sscanf(env, "%20lf", &dbl);
-        fr->sc_sigma6_min = gmx::power6(dbl);
-        if (fp)
-        {
-            fprintf(fp, "Setting the minimum soft core sigma to %g nm\n", dbl);
-        }
-    }
-
-    fr->bNonbonded = TRUE;
-    if (getenv("GMX_NO_NONBONDED") != nullptr)
-    {
-        /* turn off non-bonded calculations */
-        fr->bNonbonded = FALSE;
-        GMX_LOG(mdlog.warning)
-                .asParagraph()
-                .appendText(
-                        "Found environment variable GMX_NO_NONBONDED.\n"
-                        "Disabling nonbonded calculations.");
-    }
+    fr->efep = ir->efep;
 
     if ((getenv("GMX_DISABLE_SIMD_KERNELS") != nullptr) || (getenv("GMX_NOOPTIMIZEDKERNELS") != nullptr))
     {
@@ -1131,11 +1023,10 @@ void init_forcerec(FILE*                            fp,
     fr->bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
 
     /* Neighbour searching stuff */
-    fr->cutoff_scheme = ir->cutoff_scheme;
-    fr->ePBC          = ir->ePBC;
+    fr->pbcType = ir->pbcType;
 
     /* Determine if we will do PBC for distances in bonded interactions */
-    if (fr->ePBC == epbcNONE)
+    if (fr->pbcType == PbcType::No)
     {
         fr->bMolPBC = FALSE;
     }
@@ -1143,33 +1034,30 @@ void init_forcerec(FILE*                            fp,
     {
         const bool useEwaldSurfaceCorrection =
                 (EEL_PME_EWALD(ir->coulombtype) && ir->epsilon_surface != 0);
+        const bool haveOrientationRestraints = (gmx_mtop_ftype_count(mtop, F_ORIRES) > 0);
         if (!DOMAINDECOMP(cr))
         {
-            fr->bMolPBC = areMoleculesDistributedOverPbc(*ir, *mtop, mdlog);
-            // The assert below is equivalent to fcd->orires.nr != gmx_mtop_ftype_count(mtop, F_ORIRES)
-            GMX_RELEASE_ASSERT(!fr->bMolPBC || fcd->orires.nr == 0,
-                               "Molecules broken over PBC exist in a simulation including "
-                               "orientation restraints. "
-                               "This likely means that the global topology and the force constant "
-                               "data have gotten out of sync.");
-            if (useEwaldSurfaceCorrection)
+            fr->bMolPBC = true;
+
+            if (useEwaldSurfaceCorrection || haveOrientationRestraints)
             {
-                gmx_fatal(FARGS,
-                          "In GROMACS 2020, Ewald dipole correction is disabled when not "
-                          "using domain decomposition. With domain decomposition, it only works "
-                          "when each molecule consists of a single update group (e.g. water). "
-                          "This will be fixed in GROMACS 2021.");
+                fr->wholeMoleculeTransform =
+                        std::make_unique<gmx::WholeMoleculeTransform>(*mtop, ir->pbcType);
             }
         }
         else
         {
-            fr->bMolPBC = dd_bonded_molpbc(cr->dd, fr->ePBC);
+            fr->bMolPBC = dd_bonded_molpbc(cr->dd, fr->pbcType);
 
-            if (useEwaldSurfaceCorrection && !dd_moleculesAreAlwaysWhole(*cr->dd))
+            /* With Ewald surface correction it is useful to support e.g. running water
+             * in parallel with update groups.
+             * With orientation restraints there is no sensible use case supported with DD.
+             */
+            if ((useEwaldSurfaceCorrection && !dd_moleculesAreAlwaysWhole(*cr->dd)) || haveOrientationRestraints)
             {
                 gmx_fatal(FARGS,
-                          "You requested dipole correction (epsilon_surface > 0), but molecules "
-                          "are broken "
+                          "You requested Ewald surface correction or orientation restraints, "
+                          "but molecules are broken "
                           "over periodic boundary conditions by the domain decomposition. "
                           "Run without domain decomposition instead.");
             }
@@ -1177,8 +1065,7 @@ void init_forcerec(FILE*                            fp,
 
         if (useEwaldSurfaceCorrection)
         {
-            GMX_RELEASE_ASSERT((!DOMAINDECOMP(cr) && !fr->bMolPBC)
-                                       || (DOMAINDECOMP(cr) && dd_moleculesAreAlwaysWhole(*cr->dd)),
+            GMX_RELEASE_ASSERT(!DOMAINDECOMP(cr) || dd_moleculesAreAlwaysWhole(*cr->dd),
                                "Molecules can not be broken by PBC with epsilon_surface > 0");
         }
     }
@@ -1194,7 +1081,7 @@ void init_forcerec(FILE*                            fp,
 
     /* fr->ic is used both by verlet and group kernels (to some extent) now */
     init_interaction_const(fp, &fr->ic, ir, mtop, systemHasNetCharge);
-    init_interaction_const_tables(fp, fr->ic);
+    init_interaction_const_tables(fp, fr->ic, ir->tabext);
 
     const interaction_const_t* ic = fr->ic;
 
@@ -1215,7 +1102,6 @@ void init_forcerec(FILE*                            fp,
         case eelSWITCH:
         case eelSHIFT:
         case eelUSER:
-        case eelENCADSHIFT:
         case eelPMESWITCH:
         case eelPMEUSER:
         case eelPMEUSERSWITCH:
@@ -1248,51 +1134,57 @@ void init_forcerec(FILE*                            fp,
 
         case evdwSWITCH:
         case evdwSHIFT:
-        case evdwUSER:
-        case evdwENCADSHIFT:
-            fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_CUBICSPLINETABLE;
-            break;
+        case evdwUSER: fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_CUBICSPLINETABLE; break;
 
         default: gmx_fatal(FARGS, "Unsupported vdw interaction: %s", evdw_names[ic->vdwtype]);
     }
     fr->nbkernel_vdw_modifier = ic->vdw_modifier;
 
-    if (ir->cutoff_scheme == ecutsVERLET)
+    if (!gmx_within_tol(ic->reppow, 12.0, 10 * GMX_DOUBLE_EPS))
     {
-        if (!gmx_within_tol(ic->reppow, 12.0, 10 * GMX_DOUBLE_EPS))
-        {
-            gmx_fatal(FARGS, "Cut-off scheme %s only supports LJ repulsion power 12",
-                      ecutscheme_names[ir->cutoff_scheme]);
-        }
-        /* Older tpr files can contain Coulomb user tables with the Verlet cutoff-scheme,
-         * while mdrun does not (and never did) support this.
-         */
-        if (EEL_USER(fr->ic->eeltype))
-        {
-            gmx_fatal(FARGS, "Combination of %s and cutoff scheme %s is not supported",
-                      eel_names[ir->coulombtype], ecutscheme_names[ir->cutoff_scheme]);
-        }
-
-        fr->bvdwtab  = FALSE;
-        fr->bcoultab = FALSE;
+        gmx_fatal(FARGS, "Only LJ repulsion power 12 is supported");
+    }
+    /* Older tpr files can contain Coulomb user tables with the Verlet cutoff-scheme,
+     * while mdrun does not (and never did) support this.
+     */
+    if (EEL_USER(fr->ic->eeltype))
+    {
+        gmx_fatal(FARGS, "Electrostatics type %s is currently not supported", eel_names[ir->coulombtype]);
     }
 
+    fr->bvdwtab  = FALSE;
+    fr->bcoultab = FALSE;
+
     /* 1-4 interaction electrostatics */
     fr->fudgeQQ = mtop->ffparams.fudgeQQ;
 
-    fr->haveDirectVirialContributions =
-            (EEL_FULL(ic->eeltype) || EVDW_PME(ic->vdwtype) || fr->forceProviders->hasForceProvider()
-             || gmx_mtop_ftype_count(mtop, F_POSRES) > 0 || gmx_mtop_ftype_count(mtop, F_FBPOSRES) > 0
-             || ir->nwall > 0 || ir->bPull || ir->bRot || ir->bIMD);
+    // Multiple time stepping
+    fr->useMts = ir->useMts;
+
+    if (fr->useMts)
+    {
+        gmx::assertMtsRequirements(*ir);
+    }
+
+    const bool haveDirectVirialContributionsFast =
+            fr->forceProviders->hasForceProvider() || gmx_mtop_ftype_count(mtop, F_POSRES) > 0
+            || gmx_mtop_ftype_count(mtop, F_FBPOSRES) > 0 || ir->nwall > 0 || ir->bPull || ir->bRot
+            || ir->bIMD;
+    const bool haveDirectVirialContributionsSlow = EEL_FULL(ic->eeltype) || EVDW_PME(ic->vdwtype);
+    for (int i = 0; i < (fr->useMts ? 2 : 1); i++)
+    {
+        bool haveDirectVirialContributions =
+                (((!fr->useMts || i == 0) && haveDirectVirialContributionsFast)
+                 || ((!fr->useMts || i == 1) && haveDirectVirialContributionsSlow));
+        fr->forceHelperBuffers.emplace_back(haveDirectVirialContributions);
+    }
 
     if (fr->shift_vec == nullptr)
     {
         snew(fr->shift_vec, SHIFTS);
     }
 
-    fr->shiftForces.resize(SHIFTS);
-
-    if (fr->nbfp == nullptr)
+    if (fr->nbfp.empty())
     {
         fr->ntype = mtop->ffparams.atnr;
         fr->nbfp  = mk_nbfp(&mtop->ffparams, fr->bBHAM);
@@ -1329,15 +1221,9 @@ void init_forcerec(FILE*                            fp,
         gmx_fatal(FARGS, "Switch/shift interaction not supported with Buckingham");
     }
 
-    if (fr->bBHAM && fr->cutoff_scheme == ecutsVERLET)
+    if (fr->bBHAM)
     {
-        gmx_fatal(FARGS, "Verlet cutoff-scheme is not supported with Buckingham");
-    }
-
-    if (fp && fr->cutoff_scheme == ecutsGROUP)
-    {
-        fprintf(fp, "Cut-off's:   NS: %g   Coulomb: %g   %s: %g\n", fr->rlist, ic->rcoulomb,
-                fr->bBHAM ? "BHAM" : "LJ", ic->rvdw);
+        gmx_fatal(FARGS, "The Verlet cutoff-scheme does not (yet) support Buckingham");
     }
 
     if (ir->implicit_solvent)
@@ -1350,7 +1236,7 @@ void init_forcerec(FILE*                            fp,
      * in that case grompp should already have checked that we do not need
      * normal tables and we only generate tables for 1-4 interactions.
      */
-    rtab = ir->rlist + ir->tabext;
+    real rtab = ir->rlist + ir->tabext;
 
     /* We want to use unmodified tables for 1-4 coulombic
      * interactions, so we must in general have an extra set of
@@ -1368,15 +1254,19 @@ void init_forcerec(FILE*                            fp,
         make_wall_tables(fp, ir, tabfn, &mtop->groups, fr);
     }
 
-    if (fcd && !tabbfnm.empty())
+    fr->fcdata = std::make_unique<t_fcdata>();
+
+    if (!tabbfnm.empty())
     {
+        t_fcdata& fcdata = *fr->fcdata;
         // Need to catch std::bad_alloc
         // TODO Don't need to catch this here, when merging with master branch
         try
         {
-            fcd->bondtab  = make_bonded_tables(fp, F_TABBONDS, F_TABBONDSNC, mtop, tabbfnm, "b");
-            fcd->angletab = make_bonded_tables(fp, F_TABANGLES, -1, mtop, tabbfnm, "a");
-            fcd->dihtab   = make_bonded_tables(fp, F_TABDIHS, -1, mtop, tabbfnm, "d");
+            // TODO move these tables into a separate struct and store reference in ListedForces
+            fcdata.bondtab  = make_bonded_tables(fp, F_TABBONDS, F_TABBONDSNC, mtop, tabbfnm, "b");
+            fcdata.angletab = make_bonded_tables(fp, F_TABANGLES, -1, mtop, tabbfnm, "a");
+            fcdata.dihtab   = make_bonded_tables(fp, F_TABDIHS, -1, mtop, tabbfnm, "d");
         }
         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
     }
@@ -1390,35 +1280,53 @@ void init_forcerec(FILE*                            fp,
         }
     }
 
-    // QM/MM initialization if requested
-    fr->bQMMM = ir->bQMMM;
-    if (fr->bQMMM)
+    /* Initialize the thread working data for bonded interactions */
+    if (fr->useMts)
     {
-        // Initialize QM/MM if supported
-        if (GMX_QMMM)
-        {
-            GMX_LOG(mdlog.info)
-                    .asParagraph()
-                    .appendText(
-                            "Large parts of the QM/MM support is deprecated, and may be removed in "
-                            "a future "
-                            "version. Please get in touch with the developers if you find the "
-                            "support useful, "
-                            "as help is needed if the functionality is to continue to be "
-                            "available.");
-            fr->qr = mk_QMMMrec();
-            init_QMMMrec(cr, mtop, ir, fr);
-        }
-        else
+        // Add one ListedForces object for each MTS level
+        bool isFirstLevel = true;
+        for (const auto& mtsLevel : ir->mtsLevels)
         {
-            gmx_incons(
-                    "QM/MM was requested, but is only available when GROMACS "
-                    "is configured with QM/MM support");
+            ListedForces::InteractionSelection interactionSelection;
+            const auto&                        forceGroups = mtsLevel.forceGroups;
+            if (forceGroups[static_cast<int>(gmx::MtsForceGroups::Pair)])
+            {
+                interactionSelection.set(static_cast<int>(ListedForces::InteractionGroup::Pairs));
+            }
+            if (forceGroups[static_cast<int>(gmx::MtsForceGroups::Dihedral)])
+            {
+                interactionSelection.set(static_cast<int>(ListedForces::InteractionGroup::Dihedrals));
+            }
+            if (forceGroups[static_cast<int>(gmx::MtsForceGroups::Angle)])
+            {
+                interactionSelection.set(static_cast<int>(ListedForces::InteractionGroup::Angles));
+            }
+            if (isFirstLevel)
+            {
+                interactionSelection.set(static_cast<int>(ListedForces::InteractionGroup::Rest));
+                isFirstLevel = false;
+            }
+            fr->listedForces.emplace_back(
+                    mtop->ffparams, mtop->groups.groups[SimulationAtomGroupType::EnergyOutput].size(),
+                    gmx_omp_nthreads_get(emntBonded), interactionSelection, fp);
         }
     }
+    else
+    {
+        // Add one ListedForces object with all listed interactions
+        fr->listedForces.emplace_back(
+                mtop->ffparams, mtop->groups.groups[SimulationAtomGroupType::EnergyOutput].size(),
+                gmx_omp_nthreads_get(emntBonded), ListedForces::interactionSelectionAll(), fp);
+    }
+
+    // QM/MM initialization if requested
+    if (ir->bQMMM)
+    {
+        gmx_incons("QM/MM was requested, but is no longer available in GROMACS");
+    }
 
     /* Set all the static charge group info */
-    fr->cginfo_mb = init_cginfo_mb(mtop, fr, &bFEP_NonBonded);
+    fr->cginfo_mb = init_cginfo_mb(mtop, fr);
     if (!DOMAINDECOMP(cr))
     {
         fr->cginfo = cginfo_expand(mtop->molblock.size(), fr->cginfo_mb);
@@ -1431,50 +1339,13 @@ void init_forcerec(FILE*                            fp,
 
     fr->print_force = print_force;
 
-    /* Initialize the thread working data for bonded interactions */
-    fr->bondedThreading = init_bonded_threading(
-            fp, mtop->groups.groups[SimulationAtomGroupType::EnergyOutput].size());
-
     fr->nthread_ewc = gmx_omp_nthreads_get(emntBonded);
     snew(fr->ewc_t, fr->nthread_ewc);
 
-    if (fr->cutoff_scheme == ecutsVERLET)
-    {
-        // We checked the cut-offs in grompp, but double-check here.
-        // We have PME+LJcutoff kernels for rcoulomb>rvdw.
-        if (EEL_PME_EWALD(ir->coulombtype) && ir->vdwtype == eelCUT)
-        {
-            GMX_RELEASE_ASSERT(ir->rcoulomb >= ir->rvdw,
-                               "With Verlet lists and PME we should have rcoulomb>=rvdw");
-        }
-        else
-        {
-            GMX_RELEASE_ASSERT(
-                    ir->rcoulomb == ir->rvdw,
-                    "With Verlet lists and no PME rcoulomb and rvdw should be identical");
-        }
-
-        fr->nbv = Nbnxm::init_nb_verlet(mdlog, bFEP_NonBonded, ir, fr, cr, hardwareInfo, deviceInfo,
-                                        mtop, box, wcycle);
-
-        if (useGpuForBonded)
-        {
-            auto stream = havePPDomainDecomposition(cr)
-                                  ? Nbnxm::gpu_get_command_stream(
-                                            fr->nbv->gpu_nbv, gmx::InteractionLocality::NonLocal)
-                                  : Nbnxm::gpu_get_command_stream(fr->nbv->gpu_nbv,
-                                                                  gmx::InteractionLocality::Local);
-            // TODO the heap allocation is only needed while
-            // t_forcerec lacks a constructor.
-            fr->gpuBonded = new gmx::GpuBonded(mtop->ffparams, stream, wcycle);
-        }
-    }
-
     if (ir->eDispCorr != edispcNO)
     {
         fr->dispersionCorrection = std::make_unique<DispersionCorrection>(
-                *mtop, *ir, fr->bBHAM, fr->ntype,
-                gmx::arrayRefFromArray(fr->nbfp, fr->ntype * fr->ntype * 2), *fr->ic, tabfn);
+                *mtop, *ir, fr->bBHAM, fr->ntype, fr->nbfp, *fr->ic, tabfn);
         fr->dispersionCorrection->print(mdlog);
     }
 
@@ -1486,76 +1357,13 @@ void init_forcerec(FILE*                            fp,
          */
         fprintf(fp, "\n");
     }
-
-    if (pmeOnlyRankUsesGpu && c_enableGpuPmePpComms)
-    {
-        fr->pmePpCommGpu = std::make_unique<gmx::PmePpCommGpu>(cr->mpi_comm_mysim, cr->dd->pme_nodeid);
-    }
 }
 
 t_forcerec::t_forcerec() = default;
 
-t_forcerec::~t_forcerec() = default;
-
-/* Frees GPU memory and sets a tMPI node barrier.
- *
- * Note that this function needs to be called even if GPUs are not used
- * in this run because the PME ranks have no knowledge of whether GPUs
- * are used or not, but all ranks need to enter the barrier below.
- * \todo Remove physical node barrier from this function after making sure
- * that it's not needed anymore (with a shared GPU run).
- */
-void free_gpu_resources(t_forcerec*                          fr,
-                        const gmx::PhysicalNodeCommunicator& physicalNodeCommunicator,
-                        const gmx_gpu_info_t&                gpu_info)
+t_forcerec::~t_forcerec()
 {
-    bool isPPrankUsingGPU = (fr != nullptr) && (fr->nbv != nullptr) && fr->nbv->useGpu();
-
-    /* stop the GPU profiler (only CUDA) */
-    if (gpu_info.n_dev > 0)
-    {
-        stopGpuProfiler();
-    }
-
-    if (isPPrankUsingGPU)
-    {
-        /* Free data in GPU memory and pinned memory before destroying the GPU context */
-        fr->nbv.reset();
-
-        delete fr->gpuBonded;
-        fr->gpuBonded = nullptr;
-    }
-
-    /* With tMPI we need to wait for all ranks to finish deallocation before
-     * destroying the CUDA context in free_gpu() as some tMPI ranks may be sharing
-     * GPU and context.
-     *
-     * This is not a concern in OpenCL where we use one context per rank which
-     * is freed in nbnxn_gpu_free().
-     *
-     * Note: it is safe to not call the barrier on the ranks which do not use GPU,
-     * but it is easier and more futureproof to call it on the whole node.
-     */
-    if (GMX_THREAD_MPI)
-    {
-        physicalNodeCommunicator.barrier();
-    }
-}
-
-void done_forcerec(t_forcerec* fr, int numMolBlocks)
-{
-    if (fr == nullptr)
-    {
-        // PME-only ranks don't have a forcerec
-        return;
-    }
-    done_cginfo_mb(fr->cginfo_mb, numMolBlocks);
-    sfree(fr->nbfp);
-    delete fr->ic;
-    sfree(fr->shift_vec);
-    sfree(fr->ewc_t);
-    tear_down_bonded_threading(fr->bondedThreading);
-    GMX_RELEASE_ASSERT(fr->gpuBonded == nullptr, "Should have been deleted earlier, when used");
-    fr->bondedThreading = nullptr;
-    delete fr;
+    /* Note: This code will disappear when types are converted to C++ */
+    sfree(shift_vec);
+    sfree(ewc_t);
 }
index 39e5093af99f95a7a3c58e4b880f5bbd931591ba..aed645f1867def785f18a22fd570c5953069ca4b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013-2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_MDLIB_FORCEREC_H
 #define GMX_MDLIB_FORCEREC_H
 
-#include "gromacs/mdlib/force_flags.h"
-#include "gromacs/mdlib/tgroup.h"
-#include "gromacs/mdlib/vsite.h"
-#include "gromacs/mdtypes/forcerec.h"
+#include "gromacs/math/vec.h"
 #include "gromacs/timing/wallcycle.h"
 #include "gromacs/utility/arrayref.h"
 
-struct gmx_device_info_t;
 struct gmx_hw_info_t;
 struct t_commrec;
-struct t_fcdata;
+struct t_forcerec;
 struct t_filenm;
 struct t_inputrec;
-struct gmx_gpu_info_t;
+struct gmx_localtop_t;
+struct gmx_mtop_t;
 struct gmx_wallcycle;
+struct interaction_const_t;
 
 namespace gmx
 {
@@ -59,9 +58,6 @@ class MDLogger;
 class PhysicalNodeCommunicator;
 } // namespace gmx
 
-//! Destroy a forcerec.
-void done_forcerec(t_forcerec* fr, int numMolBlocks);
-
 /*! \brief Print the contents of the forcerec to a file
  *
  * \param[in] fplog The log file to print to
@@ -84,17 +80,17 @@ void forcerec_set_ranges(t_forcerec* fr, int natoms_force, int natoms_force_cons
 /*! \brief Initiate table constants
  *
  * Initializes the tables in the interaction constant data structure.
- * \param[in] fp   File for debugging output
- * \param[in] ic   Structure holding the table constant
+ * \param[in] fp                     File for debugging output
+ * \param[in] ic                     Structure holding the table constant
+ * \param[in] tableExtensionLength   Length by which to extend the tables. Taken from the input record.
  */
-void init_interaction_const_tables(FILE* fp, interaction_const_t* ic);
+void init_interaction_const_tables(FILE* fp, interaction_const_t* ic, real tableExtensionLength);
 
 /*! \brief Initialize forcerec structure.
  *
  * \param[in]  fplog              File for printing
  * \param[in]  mdlog              File for printing
  * \param[out] fr                 The forcerec
- * \param[in]  fcd                Force constant data
  * \param[in]  ir                 Inputrec structure
  * \param[in]  mtop               Molecular topology
  * \param[in]  cr                 Communication structures
@@ -102,17 +98,11 @@ void init_interaction_const_tables(FILE* fp, interaction_const_t* ic);
  * \param[in]  tabfn              Table potential file for non-bonded interactions
  * \param[in]  tabpfn             Table potential file for pair interactions
  * \param[in]  tabbfnm            Table potential files for bonded interactions
- * \param[in]  hardwareInfo       Information about hardware
- * \param[in]  deviceInfo         Info about GPU device to use for short-ranged work
- * \param[in]  useGpuForBonded    Whether bonded interactions will run on a GPU
- * \param[in]  pmeOnlyRankUsesGpu Whether there is a PME task on a GPU on a PME-only rank
  * \param[in]  print_force        Print forces for atoms with force >= print_force
- * \param[out] wcycle             Pointer to cycle counter object
  */
 void init_forcerec(FILE*                            fplog,
                    const gmx::MDLogger&             mdlog,
                    t_forcerec*                      fr,
-                   t_fcdata*                        fcd,
                    const t_inputrec*                ir,
                    const gmx_mtop_t*                mtop,
                    const t_commrec*                 cr,
@@ -120,12 +110,7 @@ void init_forcerec(FILE*                            fplog,
                    const char*                      tabfn,
                    const char*                      tabpfn,
                    gmx::ArrayRef<const std::string> tabbfnm,
-                   const gmx_hw_info_t&             hardwareInfo,
-                   const gmx_device_info_t*         deviceInfo,
-                   bool                             useGpuForBonded,
-                   bool                             pmeOnlyRankUsesGpu,
-                   real                             print_force,
-                   gmx_wallcycle*                   wcycle);
+                   real                             print_force);
 
 /*! \brief Check whether molecules are ever distributed over PBC boundaries
  *
@@ -146,8 +131,4 @@ bool areMoleculesDistributedOverPbc(const t_inputrec& ir, const gmx_mtop_t& mtop
  */
 void forcerec_set_excl_load(t_forcerec* fr, const gmx_localtop_t* top);
 
-void free_gpu_resources(t_forcerec*                          fr,
-                        const gmx::PhysicalNodeCommunicator& physicalNodeCommunicator,
-                        const gmx_gpu_info_t&                gpu_info);
-
 #endif
diff --git a/src/gromacs/mdlib/freeenergyparameters.cpp b/src/gromacs/mdlib/freeenergyparameters.cpp
new file mode 100644 (file)
index 0000000..ff47b98
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements routines in freeenergyparameters.h .
+ *
+ * \author Christian Blau <blau@kth.se>
+ */
+
+#include "gmxpre.h"
+
+#include "freeenergyparameters.h"
+
+#include "gromacs/mdtypes/inputrec.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+std::array<real, efptNR> lambdasAtState(const int stateIndex, double** const lambdaArray, const int lambdaArrayExtent)
+{
+    std::array<real, efptNR> lambda;
+    // set lambda from an fep state index from stateIndex, if stateIndex was defined (> -1)
+    if (stateIndex >= 0 && stateIndex < lambdaArrayExtent)
+    {
+        for (int i = 0; i < efptNR; i++)
+        {
+            lambda[i] = lambdaArray[i][stateIndex];
+        }
+    }
+    return lambda;
+}
+
+/*! \brief Evaluates where in the lambda arrays we are at currently.
+ *
+ * \param[in] step the current simulation step
+ * \param[in] deltaLambdaPerStep the change of the overall controlling lambda parameter per step
+ * \param[in] initialFEPStateIndex the FEP state at the start of the simulation. -1 if not set.
+ * \param[in] initialLambda the lambda at the start of the simulation . -1 if not set.
+ * \param[in] lambdaArrayExtent how many lambda values we have
+ * \returns a number that reflects where in the lambda arrays we are at the moment
+ */
+double currentGlobalLambda(const int64_t step,
+                           const double  deltaLambdaPerStep,
+                           const int     initialFEPStateIndex,
+                           const double  initialLambda,
+                           const int     lambdaArrayExtent)
+{
+    const real fracSimulationLambda = step * deltaLambdaPerStep;
+
+    // Set initial lambda value for the simulation either from initialFEPStateIndex or,
+    // if not set, from the initial lambda.
+    double initialGlobalLambda = 0;
+    if (initialFEPStateIndex > -1)
+    {
+        if (lambdaArrayExtent > 1)
+        {
+            initialGlobalLambda = static_cast<double>(initialFEPStateIndex) / (lambdaArrayExtent - 1);
+        }
+    }
+    else
+    {
+        if (initialLambda > -1)
+        {
+            initialGlobalLambda = initialLambda;
+        }
+    }
+
+    return initialGlobalLambda + fracSimulationLambda;
+}
+
+/*! \brief Returns an array of lambda values from linear interpolation of a lambda value matrix.
+ *
+ * \note If there is nothing to interpolate, fills the array with the global current lambda.
+ * \note Returns array boundary values if currentGlobalLambda <0 or >1 .
+ *
+ * \param[in] currentGlobalLambda determines at which position in the lambda array to interpolate
+ * \param[in] lambdaArray array of lambda values
+ * \param[in] lambdaArrayExtent number of lambda values
+ */
+std::array<real, efptNR> interpolatedLambdas(const double   currentGlobalLambda,
+                                             double** const lambdaArray,
+                                             const int      lambdaArrayExtent)
+{
+    std::array<real, efptNR> lambda;
+    // when there is no lambda value array, set all lambdas to steps * deltaLambdaPerStep
+    if (lambdaArrayExtent <= 0)
+    {
+        std::fill(std::begin(lambda), std::end(lambda), currentGlobalLambda);
+        return lambda;
+    }
+
+    // if we run over the boundary of the lambda array, return the boundary array values
+    if (currentGlobalLambda <= 0)
+    {
+        for (int i = 0; i < efptNR; i++)
+        {
+            lambda[i] = lambdaArray[i][0];
+        }
+        return lambda;
+    }
+    if (currentGlobalLambda >= 1)
+    {
+        for (int i = 0; i < efptNR; i++)
+        {
+            lambda[i] = lambdaArray[i][lambdaArrayExtent - 1];
+        }
+        return lambda;
+    }
+
+    // find out between which two value lambda array elements to interpolate
+    const int fepStateLeft = static_cast<int>(std::floor(currentGlobalLambda * (lambdaArrayExtent - 1)));
+    const int fepStateRight = fepStateLeft + 1;
+    // interpolate between this state and the next
+    const double fracBetween = currentGlobalLambda * (lambdaArrayExtent - 1) - fepStateLeft;
+    for (int i = 0; i < efptNR; i++)
+    {
+        lambda[i] = lambdaArray[i][fepStateLeft]
+                    + fracBetween * (lambdaArray[i][fepStateRight] - lambdaArray[i][fepStateLeft]);
+    }
+    return lambda;
+}
+
+} // namespace
+
+std::array<real, efptNR> currentLambdas(const int64_t step, const t_lambda& fepvals, const int currentLambdaState)
+{
+    if (fepvals.delta_lambda == 0)
+    {
+        if (currentLambdaState > -1)
+        {
+            return lambdasAtState(currentLambdaState, fepvals.all_lambda, fepvals.n_lambda);
+        }
+
+        if (fepvals.init_fep_state > -1)
+        {
+            return lambdasAtState(fepvals.init_fep_state, fepvals.all_lambda, fepvals.n_lambda);
+        }
+
+        std::array<real, efptNR> lambdas;
+        std::fill(std::begin(lambdas), std::end(lambdas), fepvals.init_lambda);
+        return lambdas;
+    }
+    const double globalLambda = currentGlobalLambda(step, fepvals.delta_lambda, fepvals.init_fep_state,
+                                                    fepvals.init_lambda, fepvals.n_lambda);
+    return interpolatedLambdas(globalLambda, fepvals.all_lambda, fepvals.n_lambda);
+}
+
+} // namespace gmx
diff --git a/src/gromacs/mdlib/freeenergyparameters.h b/src/gromacs/mdlib/freeenergyparameters.h
new file mode 100644 (file)
index 0000000..0814bc6
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Handling of free energy parameters
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_mdlib
+ */
+
+#ifndef GMX_MDLIB_FREEENERGYPARAMETERS_H
+#define GMX_MDLIB_FREEENERGYPARAMETERS_H
+
+#include <array>
+
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/real.h"
+
+struct t_lambda;
+
+namespace gmx
+{
+
+/*! \brief Evaluate the current lambdas
+ *
+ * \param[in] step the current simulation step
+ * \param[in] fepvals describing the lambda setup
+ * \param[in] currentLambdaState the lambda state to use to set the lambdas, -1 if not set
+ * \returns the current lambda-value array
+ */
+std::array<real, efptNR> currentLambdas(int64_t step, const t_lambda& fepvals, int currentLambdaState);
+
+} // namespace gmx
+
+#endif
index 6ae2e122ff49fc04a425496b68c8bcee476f3623..aa3327c8221a9703360160b55b42cdc1731ba4c4 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 30ba196a5e7c636bcf92b38543ca6eb814db70ab..11b77a681bf1600f099d102ec6c7fc6b33033832 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
diff --git a/src/gromacs/mdlib/gpuforcereduction.h b/src/gromacs/mdlib/gpuforcereduction.h
new file mode 100644 (file)
index 0000000..71a5f71
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares the GPU Force Reduction
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_GPUFORCEREDUCTION_H
+#define GMX_MDLIB_GPUFORCEREDUCTION_H
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/fixedcapacityvector.h"
+
+class GpuEventSynchronizer;
+class DeviceStream;
+class DeviceContext;
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Manages the force reduction directly in GPU memory
+ *
+ * Manages the reduction of multiple GPU force buffers into a single
+ * GPU force buffer. The reduction involves at least one (input/output)
+ * Rvec-format buffer and one (input) Nbat-format buffer, where the
+ * Nbat->Rvec conversion is handled internally. One additional (input)
+ * Rvec-format buffer is supported as optional.
+ */
+class GpuForceReduction
+{
+
+public:
+    /*! \brief Creates GPU force reduction object
+     *
+     * \param [in] deviceContext GPU device context
+     * \param [in] deviceStream  Stream to use for reduction
+     */
+    GpuForceReduction(const DeviceContext& deviceContext, const DeviceStream& deviceStream);
+    ~GpuForceReduction();
+
+    /*! \brief Register a nbnxm-format force to be reduced
+     *
+     * \param [in] forcePtr  Pointer to force to be reduced
+     */
+    void registerNbnxmForce(void* forcePtr);
+
+    /*! \brief Register a rvec-format force to be reduced
+     *
+     * \param [in] forcePtr  Pointer to force to be reduced
+     */
+    void registerRvecForce(void* forcePtr);
+
+    /*! \brief Add a dependency for this force reduction
+     *
+     * \param [in] dependency   Dependency for this reduction
+     */
+    void addDependency(GpuEventSynchronizer* dependency);
+
+    /*! \brief Reinitialize the GPU force reduction
+     *
+     * \param [in] baseForcePtr     Pointer to force to be used as a base
+     * \param [in] numAtoms         The number of atoms
+     * \param [in] cell             Pointer to the cell array
+     * \param [in] atomStart        The start atom for the reduction
+     * \param [in] accumulate       Whether reduction should be accumulated
+     * \param [in] completionMarker Event to be marked when launch of reduction is complete
+     */
+    void reinit(DeviceBuffer<RVec>    baseForcePtr,
+                int                   numAtoms,
+                ArrayRef<const int>   cell,
+                int                   atomStart,
+                bool                  accumulate,
+                GpuEventSynchronizer* completionMarker = nullptr);
+
+    /*! \brief Execute the force reduction */
+    void execute();
+
+private:
+    class Impl;
+    gmx::PrivateImplPointer<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/gromacs/mdlib/gpuforcereduction_impl.cpp b/src/gromacs/mdlib/gpuforcereduction_impl.cpp
new file mode 100644 (file)
index 0000000..ff32dee
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief May be used to implement force reduction interfaces for non-GPU builds.
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_mdlib
+ */
+
+#include "gmxpre.h"
+
+#include "config.h"
+
+#include "gpuforcereduction.h"
+
+#if !GMX_GPU_CUDA
+
+namespace gmx
+{
+
+class GpuForceReduction::Impl
+{
+};
+
+GpuForceReduction::GpuForceReduction(const DeviceContext& /* deviceContext */,
+                                     const DeviceStream& /* deviceStream */) :
+    impl_(nullptr)
+{
+    GMX_ASSERT(false, "A CPU stub has been called instead of the correct implementation.");
+}
+
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
+void GpuForceReduction::reinit(DeviceBuffer<RVec> /*baseForcePtr*/,
+                               const int /*numAtoms*/,
+                               ArrayRef<const int> /*cell*/,
+                               const int /*atomStart*/,
+                               const bool /*accumulate*/,
+                               GpuEventSynchronizer* /*completionMarker*/)
+{
+    GMX_ASSERT(false, "A CPU stub has been called instead of the correct implementation.");
+}
+
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
+void GpuForceReduction::registerNbnxmForce(void* /* forcePtr */)
+{
+    GMX_ASSERT(false, "A CPU stub has been called instead of the correct implementation.");
+}
+
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
+void GpuForceReduction::registerRvecForce(void* /* forcePtr */)
+{
+    GMX_ASSERT(false, "A CPU stub has been called instead of the correct implementation.");
+}
+
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
+void GpuForceReduction::addDependency(GpuEventSynchronizer* const /* dependency */)
+{
+    GMX_ASSERT(false, "A CPU stub has been called instead of the correct implementation.");
+}
+
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
+void GpuForceReduction::execute()
+{
+    GMX_ASSERT(false, "A CPU stub has been called instead of the correct implementation.");
+}
+
+GpuForceReduction::~GpuForceReduction() = default;
+
+} // namespace gmx
+
+#endif
diff --git a/src/gromacs/mdlib/gpuforcereduction_impl.cu b/src/gromacs/mdlib/gpuforcereduction_impl.cu
new file mode 100644 (file)
index 0000000..29c18da
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements GPU Force Reduction using CUDA
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_mdlib
+ */
+
+#include "gmxpre.h"
+
+#include "gpuforcereduction_impl.cuh"
+
+#include <stdio.h>
+
+#include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/devicebuffer.h"
+#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
+#include "gromacs/gpu_utils/typecasts.cuh"
+#include "gromacs/gpu_utils/vectype_ops.cuh"
+#include "gromacs/utility/gmxassert.h"
+
+#include "gpuforcereduction.h"
+
+namespace gmx
+{
+
+constexpr static int c_threadsPerBlock = 128;
+
+typedef struct rvecDeviceForceData rvecDeviceForceData_t;
+
+
+template<bool addRvecForce, bool accumulateForce>
+static __global__ void reduceKernel(const float3* __restrict__ gm_nbnxmForce,
+                                    const float3* __restrict__ rvecForceToAdd,
+                                    float3*    gm_fTotal,
+                                    const int* gm_cell,
+                                    const int  numAtoms)
+{
+
+    // map particle-level parallelism to 1D CUDA thread and block index
+    const int threadIndex = blockIdx.x * blockDim.x + threadIdx.x;
+
+    // perform addition for each particle
+    if (threadIndex < numAtoms)
+    {
+
+        float3* gm_fDest = &gm_fTotal[threadIndex];
+        float3  temp;
+
+        // Accumulate or set nbnxm force
+        if (accumulateForce)
+        {
+            temp = *gm_fDest;
+            temp += gm_nbnxmForce[gm_cell[threadIndex]];
+        }
+        else
+        {
+            temp = gm_nbnxmForce[gm_cell[threadIndex]];
+        }
+
+        if (addRvecForce)
+        {
+            temp += rvecForceToAdd[threadIndex];
+        }
+
+        *gm_fDest = temp;
+    }
+    return;
+}
+
+GpuForceReduction::Impl::Impl(const DeviceContext& deviceContext, const DeviceStream& deviceStream) :
+    deviceContext_(deviceContext),
+    deviceStream_(deviceStream){};
+
+void GpuForceReduction::Impl::reinit(float3*               baseForcePtr,
+                                     const int             numAtoms,
+                                     ArrayRef<const int>   cell,
+                                     const int             atomStart,
+                                     const bool            accumulate,
+                                     GpuEventSynchronizer* completionMarker)
+{
+    GMX_ASSERT((baseForcePtr != nullptr), "Input base force for reduction has no data");
+    baseForce_        = &(baseForcePtr[atomStart]);
+    numAtoms_         = numAtoms;
+    atomStart_        = atomStart;
+    accumulate_       = accumulate;
+    completionMarker_ = completionMarker;
+    cellInfo_.cell    = cell.data();
+    reallocateDeviceBuffer(&cellInfo_.d_cell, numAtoms_, &cellInfo_.cellSize,
+                           &cellInfo_.cellSizeAlloc, deviceContext_);
+    copyToDeviceBuffer(&cellInfo_.d_cell, &(cellInfo_.cell[atomStart]), 0, numAtoms_, deviceStream_,
+                       GpuApiCallBehavior::Async, nullptr);
+
+    dependencyList_.clear();
+};
+
+void GpuForceReduction::Impl::registerNbnxmForce(DeviceBuffer<RVec> forcePtr)
+{
+    GMX_ASSERT((forcePtr != nullptr), "Input force for reduction has no data");
+    nbnxmForceToAdd_ = forcePtr;
+};
+
+void GpuForceReduction::Impl::registerRvecForce(DeviceBuffer<RVec> forcePtr)
+{
+    GMX_ASSERT((forcePtr != nullptr), "Input force for reduction has no data");
+    rvecForceToAdd_ = forcePtr;
+};
+
+void GpuForceReduction::Impl::addDependency(GpuEventSynchronizer* const dependency)
+{
+    dependencyList_.push_back(dependency);
+}
+
+void GpuForceReduction::Impl::execute()
+{
+
+    if (numAtoms_ == 0)
+    {
+        return;
+    }
+
+    GMX_ASSERT((nbnxmForceToAdd_ != nullptr), "Nbnxm force for reduction has no data");
+
+    // Enqueue wait on all dependencies passed
+    for (auto const synchronizer : dependencyList_)
+    {
+        synchronizer->enqueueWaitEvent(deviceStream_);
+    }
+
+    float3* d_nbnxmForce     = asFloat3(nbnxmForceToAdd_);
+    float3* d_rvecForceToAdd = &(asFloat3(rvecForceToAdd_)[atomStart_]);
+
+    // Configure and launch kernel
+    KernelLaunchConfig config;
+    config.blockSize[0]     = c_threadsPerBlock;
+    config.blockSize[1]     = 1;
+    config.blockSize[2]     = 1;
+    config.gridSize[0]      = ((numAtoms_ + 1) + c_threadsPerBlock - 1) / c_threadsPerBlock;
+    config.gridSize[1]      = 1;
+    config.gridSize[2]      = 1;
+    config.sharedMemorySize = 0;
+
+    auto kernelFn = (rvecForceToAdd_ != nullptr)
+                            ? (accumulate_ ? reduceKernel<true, true> : reduceKernel<true, false>)
+                            : (accumulate_ ? reduceKernel<false, true> : reduceKernel<false, false>);
+
+    const auto kernelArgs = prepareGpuKernelArguments(kernelFn, config, &d_nbnxmForce, &d_rvecForceToAdd,
+                                                      &baseForce_, &cellInfo_.d_cell, &numAtoms_);
+
+    launchGpuKernel(kernelFn, config, deviceStream_, nullptr, "Force Reduction", kernelArgs);
+
+    // Mark that kernel has been launched
+    if (completionMarker_ != nullptr)
+    {
+        completionMarker_->markEvent(deviceStream_);
+    }
+}
+
+GpuForceReduction::Impl::~Impl(){};
+
+GpuForceReduction::GpuForceReduction(const DeviceContext& deviceContext, const DeviceStream& deviceStream) :
+    impl_(new Impl(deviceContext, deviceStream))
+{
+}
+
+void GpuForceReduction::registerNbnxmForce(void* forcePtr)
+{
+    impl_->registerNbnxmForce(reinterpret_cast<DeviceBuffer<RVec>>(forcePtr));
+}
+
+void GpuForceReduction::registerRvecForce(void* forcePtr)
+{
+    impl_->registerRvecForce(reinterpret_cast<DeviceBuffer<RVec>>(forcePtr));
+}
+
+void GpuForceReduction::addDependency(GpuEventSynchronizer* const dependency)
+{
+    impl_->addDependency(dependency);
+}
+
+void GpuForceReduction::reinit(DeviceBuffer<RVec>    baseForcePtr,
+                               const int             numAtoms,
+                               ArrayRef<const int>   cell,
+                               const int             atomStart,
+                               const bool            accumulate,
+                               GpuEventSynchronizer* completionMarker)
+{
+    impl_->reinit(asFloat3(baseForcePtr), numAtoms, cell, atomStart, accumulate, completionMarker);
+}
+void GpuForceReduction::execute()
+{
+    impl_->execute();
+}
+
+GpuForceReduction::~GpuForceReduction() = default;
+
+} // namespace gmx
diff --git a/src/gromacs/mdlib/gpuforcereduction_impl.cuh b/src/gromacs/mdlib/gpuforcereduction_impl.cuh
new file mode 100644 (file)
index 0000000..536e3fd
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares the GPU Force Reduction
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_GPUFORCEREDUCTION_IMPL_H
+#define GMX_MDLIB_GPUFORCEREDUCTION_IMPL_H
+
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/math/vectypes.h"
+
+#include "gpuforcereduction.h"
+
+namespace gmx
+{
+
+//! structure to hold cell information for any nbat-format forces
+struct cellInfo
+{
+    //! cell index mapping for any nbat-format forces
+    const int* cell = nullptr;
+    //! device copy of cell index mapping for any nbat-format forces
+    int* d_cell = nullptr;
+    //! number of atoms in cell array
+    int cellSize = -1;
+    //! number of atoms allocated in cell array
+    int cellSizeAlloc = -1;
+};
+
+class GpuForceReduction::Impl
+{
+
+public:
+    /*! \brief Creates GPU force reduction object
+     *
+     * \param [in] deviceStream  Stream to use for reduction
+     * \param [in] deviceContext GPU device context
+     */
+    Impl(const DeviceContext& deviceContext, const DeviceStream& deviceStream);
+    ~Impl();
+
+    /*! \brief Register a nbnxm-format force to be reduced
+     *
+     * \param [in] forcePtr  Pointer to force to be reduced
+     */
+    void registerNbnxmForce(DeviceBuffer<RVec> forcePtr);
+
+    /*! \brief Register a rvec-format force to be reduced
+     *
+     * \param [in] forcePtr  Pointer to force to be reduced
+     */
+    void registerRvecForce(DeviceBuffer<RVec> forcePtr);
+
+    /*! \brief Add a dependency for this force reduction
+     *
+     * \param [in] dependency   Dependency for this reduction
+     */
+    void addDependency(GpuEventSynchronizer* const dependency);
+
+    /*! \brief Reinitialize the GPU force reduction
+     *
+     * \param [in] baseForcePtr     Pointer to force to be used as a base
+     * \param [in] numAtoms         The number of atoms
+     * \param [in] cell             Pointer to the cell array
+     * \param [in] atomStart        The start atom for the reduction
+     * \param [in] accumulate       Whether reduction should be accumulated
+     * \param [in] completionMarker Event to be marked when launch of reduction is complete
+     */
+    void reinit(float3*               baseForcePtr,
+                const int             numAtoms,
+                ArrayRef<const int>   cell,
+                const int             atomStart,
+                const bool            accumulate,
+                GpuEventSynchronizer* completionMarker = nullptr);
+
+    /*! \brief Execute the force reduction */
+    void execute();
+
+private:
+    //! force to be used as a base for this reduction
+    float3* baseForce_ = nullptr;
+    //! starting atom
+    int atomStart_ = 0;
+    //! number of atoms
+    int numAtoms_ = 0;
+    //! whether reduction is accumulated into base force buffer
+    int accumulate_ = true;
+    //! cell information for any nbat-format forces
+    struct cellInfo cellInfo_;
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! list of dependencies
+    gmx::FixedCapacityVector<GpuEventSynchronizer*, 3> dependencyList_;
+    //! stream to be used for this reduction
+    const DeviceStream& deviceStream_;
+    //! Nbnxm force to be added in this reduction
+    DeviceBuffer<RVec> nbnxmForceToAdd_ = nullptr;
+    //! Rvec-format force to be added in this reduction
+    DeviceBuffer<RVec> rvecForceToAdd_ = nullptr;
+    //! event to be marked when redcution launch has been completed
+    GpuEventSynchronizer* completionMarker_ = nullptr;
+};
+
+} // namespace gmx
+
+#endif
index bd8711ce77f5e85d3d0975913935adcc8a019e36..34a7ca9156c69ee72bf1098a7b43d1a7f41d6bfa 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
similarity index 88%
rename from src/gromacs/mdlib/leapfrog_cuda.cu
rename to src/gromacs/mdlib/leapfrog_gpu.cu
index 4415158d32ea7d051e16d327afa515c1b4b28934..f34c51db4599da69a0bdedc1c2730acf73d4843c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * using CUDA, including class initialization, data-structures management
  * and GPU kernel.
  *
- * \todo Reconsider naming towards using "gpu" suffix instead of "cuda".
- *
  * \author Artem Zhmurov <zhmurov@gmail.com>
  *
  * \ingroup module_mdlib
  */
 #include "gmxpre.h"
 
-#include "leapfrog_cuda.cuh"
+#include "leapfrog_gpu.cuh"
 
 #include <assert.h>
 #include <stdio.h>
@@ -61,6 +59,7 @@
 #include "gromacs/gpu_utils/devicebuffer.h"
 #include "gromacs/gpu_utils/vectype_ops.cuh"
 #include "gromacs/math/vec.h"
+#include "gromacs/mdtypes/group.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/pbc_aiuc_cuda.cuh"
 #include "gromacs/utility/arrayref.h"
@@ -262,16 +261,16 @@ inline auto selectLeapFrogKernelPtr(bool                doTemperatureScaling,
     return kernelPtr;
 }
 
-void LeapFrogCuda::integrate(const float3*                     d_x,
-                             float3*                           d_xp,
-                             float3*                           d_v,
-                             const float3*                     d_f,
-                             const real                        dt,
-                             const bool                        doTemperatureScaling,
-                             gmx::ArrayRef<const t_grp_tcstat> tcstat,
-                             const bool                        doParrinelloRahman,
-                             const float                       dtPressureCouple,
-                             const matrix                      prVelocityScalingMatrix)
+void LeapFrogGpu::integrate(const float3*                     d_x,
+                            float3*                           d_xp,
+                            float3*                           d_v,
+                            const float3*                     d_f,
+                            const real                        dt,
+                            const bool                        doTemperatureScaling,
+                            gmx::ArrayRef<const t_grp_tcstat> tcstat,
+                            const bool                        doParrinelloRahman,
+                            const float                       dtPressureCouple,
+                            const matrix                      prVelocityScalingMatrix)
 {
 
     ensureNoPendingCudaError("In CUDA version of Leap-Frog integrator");
@@ -289,7 +288,7 @@ void LeapFrogCuda::integrate(const float3*                     d_x,
                 h_lambdas_[i] = tcstat[i].lambda;
             }
             copyToDeviceBuffer(&d_lambdas_, h_lambdas_.data(), 0, numTempScaleValues_,
-                               commandStream_, GpuApiCallBehavior::Async, nullptr);
+                               deviceStream_, GpuApiCallBehavior::Async, nullptr);
         }
         VelocityScalingType prVelocityScalingType = VelocityScalingType::None;
         if (doParrinelloRahman)
@@ -313,12 +312,14 @@ void LeapFrogCuda::integrate(const float3*                     d_x,
     const auto kernelArgs = prepareGpuKernelArguments(
             kernelPtr, kernelLaunchConfig_, &numAtoms_, &d_x, &d_xp, &d_v, &d_f, &d_inverseMasses_,
             &dt, &d_lambdas_, &d_tempScaleGroups_, &prVelocityScalingMatrixDiagonal_);
-    launchGpuKernel(kernelPtr, kernelLaunchConfig_, nullptr, "leapfrog_kernel", kernelArgs);
+    launchGpuKernel(kernelPtr, kernelLaunchConfig_, deviceStream_, nullptr, "leapfrog_kernel", kernelArgs);
 
     return;
 }
 
-LeapFrogCuda::LeapFrogCuda(CommandStream commandStream) : commandStream_(commandStream)
+LeapFrogGpu::LeapFrogGpu(const DeviceContext& deviceContext, const DeviceStream& deviceStream) :
+    deviceContext_(deviceContext),
+    deviceStream_(deviceStream)
 {
     numAtoms_ = 0;
 
@@ -328,37 +329,34 @@ LeapFrogCuda::LeapFrogCuda(CommandStream commandStream) : commandStream_(command
     kernelLaunchConfig_.blockSize[1]     = 1;
     kernelLaunchConfig_.blockSize[2]     = 1;
     kernelLaunchConfig_.sharedMemorySize = 0;
-    kernelLaunchConfig_.stream           = commandStream_;
 }
 
-LeapFrogCuda::~LeapFrogCuda()
+LeapFrogGpu::~LeapFrogGpu()
 {
     freeDeviceBuffer(&d_inverseMasses_);
 }
 
-void LeapFrogCuda::setPbc(const t_pbc* pbc)
-{
-    setPbcAiuc(pbc->ndim_ePBC, pbc->box, &pbcAiuc_);
-}
-
-void LeapFrogCuda::set(const t_mdatoms& md, const int numTempScaleValues, const unsigned short* tempScaleGroups)
+void LeapFrogGpu::set(const int             numAtoms,
+                      const real*           inverseMasses,
+                      const int             numTempScaleValues,
+                      const unsigned short* tempScaleGroups)
 {
-    numAtoms_                       = md.nr;
+    numAtoms_                       = numAtoms;
     kernelLaunchConfig_.gridSize[0] = (numAtoms_ + c_threadsPerBlock - 1) / c_threadsPerBlock;
 
     numTempScaleValues_ = numTempScaleValues;
 
     reallocateDeviceBuffer(&d_inverseMasses_, numAtoms_, &numInverseMasses_,
-                           &numInverseMassesAlloc_, nullptr);
-    copyToDeviceBuffer(&d_inverseMasses_, (float*)md.invmass, 0, numAtoms_, commandStream_,
+                           &numInverseMassesAlloc_, deviceContext_);
+    copyToDeviceBuffer(&d_inverseMasses_, (float*)inverseMasses, 0, numAtoms_, deviceStream_,
                        GpuApiCallBehavior::Sync, nullptr);
 
     // Temperature scale group map only used if there are more then one group
     if (numTempScaleValues > 1)
     {
         reallocateDeviceBuffer(&d_tempScaleGroups_, numAtoms_, &numTempScaleGroups_,
-                               &numTempScaleGroupsAlloc_, nullptr);
-        copyToDeviceBuffer(&d_tempScaleGroups_, tempScaleGroups, 0, numAtoms_, commandStream_,
+                               &numTempScaleGroupsAlloc_, deviceContext_);
+        copyToDeviceBuffer(&d_tempScaleGroups_, tempScaleGroups, 0, numAtoms_, deviceStream_,
                            GpuApiCallBehavior::Sync, nullptr);
     }
 
@@ -366,7 +364,8 @@ void LeapFrogCuda::set(const t_mdatoms& md, const int numTempScaleValues, const
     if (numTempScaleValues_ > 0)
     {
         h_lambdas_.resize(numTempScaleValues);
-        reallocateDeviceBuffer(&d_lambdas_, numTempScaleValues_, &numLambdas_, &numLambdasAlloc_, nullptr);
+        reallocateDeviceBuffer(&d_lambdas_, numTempScaleValues_, &numLambdas_, &numLambdasAlloc_,
+                               deviceContext_);
     }
 }
 
similarity index 84%
rename from src/gromacs/mdlib/leapfrog_cuda.cuh
rename to src/gromacs/mdlib/leapfrog_gpu.cuh
index cb71267a209ff1619f4dc331a734fdf0ceb783b0..cec20f751d0aa52f4a73c0bbac94282b38797cb3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  */
 /*! \libinternal \file
  *
- * \brief Declarations for CUDA implementation of Leap-Frog.
- *
- * \todo Reconsider naming towards using "gpu" suffix instead of "cuda".
+ * \brief Declarations for GPU implementation of Leap-Frog.
  *
  * \author Artem Zhmurov <zhmurov@gmail.com>
  *
  * \ingroup module_mdlib
  * \inlibraryapi
  */
-#ifndef GMX_MDLIB_LEAPFROG_CUDA_CUH
-#define GMX_MDLIB_LEAPFROG_CUDA_CUH
+#ifndef GMX_MDLIB_LEAPFROG_GPU_CUH
+#define GMX_MDLIB_LEAPFROG_GPU_CUH
 
 #include "gromacs/gpu_utils/gputraits.cuh"
 #include "gromacs/gpu_utils/hostallocator.h"
-#include "gromacs/mdtypes/group.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/pbc_aiuc.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/classhelpers.h"
 
+class DeviceContext;
+class DeviceStream;
+struct t_grp_tcstat;
+
 namespace gmx
 {
 
-class LeapFrogCuda
+class LeapFrogGpu
 {
 
 public:
     /*! \brief Constructor.
      *
-     * \param[in] commandStream  Device command stream to use.
-     */
-    LeapFrogCuda(CommandStream commandStream);
-    ~LeapFrogCuda();
-
-    /*! \brief
-     * Update PBC data.
-     *
-     * Converts PBC data from t_pbc into the PbcAiuc format and stores the latter.
-     *
-     * \param[in] pbc The PBC data in t_pbc format.
+     * \param[in] deviceContext  Device context (dummy in CUDA).
+     * \param[in] deviceStream   Device stream to use.
      */
-    void setPbc(const t_pbc* pbc);
+    LeapFrogGpu(const DeviceContext& deviceContext, const DeviceStream& deviceStream);
+    ~LeapFrogGpu();
 
     /*! \brief Integrate
      *
@@ -111,22 +103,26 @@ public:
      * and temperature coupling groups. Copies inverse masses and temperature coupling groups
      * to the GPU.
      *
-     * \param[in] md                  MD atoms, from which inverse masses are taken.
+     * \param[in] numAtoms            Number of atoms in the system.
+     * \param[in] inverseMasses       Inverse masses of atoms.
      * \param[in] numTempScaleValues  Number of temperature scale groups.
      * \param[in] tempScaleGroups     Maps the atom index to temperature scale value.
      */
-    void set(const t_mdatoms& md, int numTempScaleValues, const unsigned short* tempScaleGroups);
+    void set(const int             numAtoms,
+             const real*           inverseMasses,
+             int                   numTempScaleValues,
+             const unsigned short* tempScaleGroups);
 
     /*! \brief Class with hardware-specific interfaces and implementations.*/
     class Impl;
 
 private:
-    //! CUDA stream
-    CommandStream commandStream_;
-    //! CUDA kernel launch config
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! GPU stream
+    const DeviceStream& deviceStream_;
+    //! GPU kernel launch config
     KernelLaunchConfig kernelLaunchConfig_;
-    //! Periodic boundary data
-    PbcAiuc pbcAiuc_;
     //! Number of atoms
     int numAtoms_;
 
index 81fce323554e08b01c229393373f636c7a131af9..10d8acf46b93f0bb8fbedcf02dfbb1677bef4268 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/pbc_simd.h"
 #include "gromacs/simd/simd.h"
 #include "gromacs/simd/simd_math.h"
 #include "gromacs/simd/vector_operations.h"
-#include "gromacs/topology/block.h"
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/utility/alignedallocator.h"
 #include "gromacs/utility/arrayref.h"
@@ -83,6 +82,7 @@
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxomp.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/pleasecite.h"
 
 using namespace gmx; // TODO: Remove when this file is moved into gmx namespace
@@ -568,17 +568,17 @@ static void gmx_simdcall calc_dr_x_f_simd(int                           b0,
 #endif // GMX_SIMD_HAVE_REAL
 
 /*! \brief LINCS projection, works on derivatives of the coordinates. */
-static void do_lincsp(const rvec*        x,
-                      rvec*              f,
-                      rvec*              fp,
-                      t_pbc*             pbc,
-                      Lincs*             lincsd,
-                      int                th,
-                      real*              invmass,
-                      ConstraintVariable econq,
-                      bool               bCalcDHDL,
-                      bool               bCalcVir,
-                      tensor             rmdf)
+static void do_lincsp(ArrayRefWithPadding<const RVec> xPadded,
+                      ArrayRefWithPadding<RVec>       fPadded,
+                      ArrayRef<RVec>                  fp,
+                      t_pbc*                          pbc,
+                      Lincs*                          lincsd,
+                      int                             th,
+                      const real*                     invmass,
+                      ConstraintVariable              econq,
+                      bool                            bCalcDHDL,
+                      bool                            bCalcVir,
+                      tensor                          rmdf)
 {
     const int b0 = lincsd->task[th].b0;
     const int b1 = lincsd->task[th].b1;
@@ -607,6 +607,9 @@ static void do_lincsp(const rvec*        x,
     gmx::ArrayRef<real> rhs2 = lincsd->tmp2;
     gmx::ArrayRef<real> sol  = lincsd->tmp3;
 
+    const rvec* x = as_rvec_array(xPadded.paddedArrayRef().data());
+    rvec*       f = as_rvec_array(fPadded.paddedArrayRef().data());
+
 #if GMX_SIMD_HAVE_REAL
     /* This SIMD code does the same as the plain-C code after the #else.
      * The only difference is that we always call pbc code, as with SIMD
@@ -705,8 +708,8 @@ static void do_lincsp(const rvec*        x,
     /* When constraining forces, we should not use mass weighting,
      * so we pass invmass=NULL, which results in the use of 1 for all atoms.
      */
-    lincs_update_atoms(lincsd, th, 1.0, sol, r,
-                       (econq != ConstraintVariable::Force) ? invmass : nullptr, fp);
+    lincs_update_atoms(lincsd, th, 1.0, sol, r, (econq != ConstraintVariable::Force) ? invmass : nullptr,
+                       as_rvec_array(fp.data()));
 
     if (bCalcDHDL)
     {
@@ -944,22 +947,26 @@ static void gmx_simdcall calc_dist_iter_simd(int                           b0,
 #endif // GMX_SIMD_HAVE_REAL
 
 //! Implements LINCS constraining.
-static void do_lincs(const rvec*      x,
-                     rvec*            xp,
-                     const matrix     box,
-                     t_pbc*           pbc,
-                     Lincs*           lincsd,
-                     int              th,
-                     const real*      invmass,
-                     const t_commrec* cr,
-                     bool             bCalcDHDL,
-                     real             wangle,
-                     bool*            bWarn,
-                     real             invdt,
-                     rvec* gmx_restrict v,
-                     bool               bCalcVir,
-                     tensor             vir_r_m_dr)
+static void do_lincs(ArrayRefWithPadding<const RVec> xPadded,
+                     ArrayRefWithPadding<RVec>       xpPadded,
+                     const matrix                    box,
+                     t_pbc*                          pbc,
+                     Lincs*                          lincsd,
+                     int                             th,
+                     const real*                     invmass,
+                     const t_commrec*                cr,
+                     bool                            bCalcDHDL,
+                     real                            wangle,
+                     bool*                           bWarn,
+                     real                            invdt,
+                     ArrayRef<RVec>                  vRef,
+                     bool                            bCalcVir,
+                     tensor                          vir_r_m_dr)
 {
+    const rvec* x        = as_rvec_array(xPadded.paddedArrayRef().data());
+    rvec*       xp       = as_rvec_array(xpPadded.paddedArrayRef().data());
+    rvec* gmx_restrict v = as_rvec_array(vRef.data());
+
     const int b0 = lincsd->task[th].b0;
     const int b1 = lincsd->task[th].b1;
 
@@ -1097,7 +1104,7 @@ static void do_lincs(const rvec*      x,
                 /* Communicate the corrected non-local coordinates */
                 if (DOMAINDECOMP(cr))
                 {
-                    dd_move_x_constraints(cr->dd, box, xp, nullptr, FALSE);
+                    dd_move_x_constraints(cr->dd, box, xpPadded.unpaddedArrayRef(), ArrayRef<RVec>(), FALSE);
                 }
             }
 #pragma omp barrier
@@ -1294,7 +1301,7 @@ static void set_lincs_matrix_task(Lincs* li, Task* li_task, const real* invmass,
 }
 
 /*! \brief Sets the elements in the LINCS matrix. */
-static void set_lincs_matrix(Lincs* li, real* invmass, real lambda)
+static void set_lincs_matrix(Lincs* li, const real* invmass, real lambda)
 {
     const real invsqrt2 = 0.7071067811865475244;
 
@@ -1342,33 +1349,29 @@ static void set_lincs_matrix(Lincs* li, real* invmass, real lambda)
 }
 
 //! Finds all triangles of atoms that share constraints to a central atom.
-static int count_triangle_constraints(const InteractionLists& ilist, const t_blocka& at2con)
+static int count_triangle_constraints(const InteractionLists& ilist, const ListOfLists<int>& at2con)
 {
-    int ncon1, ncon_tot;
-    int c0, n1, c1, ac1, n2, c2;
-    int ncon_triangle;
-
-    ncon1    = ilist[F_CONSTR].size() / 3;
-    ncon_tot = ncon1 + ilist[F_CONSTRNC].size() / 3;
+    const int ncon1    = ilist[F_CONSTR].size() / 3;
+    const int ncon_tot = ncon1 + ilist[F_CONSTRNC].size() / 3;
 
     gmx::ArrayRef<const int> ia1 = ilist[F_CONSTR].iatoms;
     gmx::ArrayRef<const int> ia2 = ilist[F_CONSTRNC].iatoms;
 
-    ncon_triangle = 0;
-    for (c0 = 0; c0 < ncon_tot; c0++)
+    int ncon_triangle = 0;
+    for (int c0 = 0; c0 < ncon_tot; c0++)
     {
         bool       bTriangle = FALSE;
         const int* iap       = constr_iatomptr(ia1, ia2, c0);
         const int  a00       = iap[1];
         const int  a01       = iap[2];
-        for (n1 = at2con.index[a01]; n1 < at2con.index[a01 + 1]; n1++)
+        for (const int c1 : at2con[a01])
         {
-            c1 = at2con.a[n1];
             if (c1 != c0)
             {
                 const int* iap = constr_iatomptr(ia1, ia2, c1);
                 const int  a10 = iap[1];
                 const int  a11 = iap[2];
+                int        ac1;
                 if (a10 == a01)
                 {
                     ac1 = a11;
@@ -1377,9 +1380,8 @@ static int count_triangle_constraints(const InteractionLists& ilist, const t_blo
                 {
                     ac1 = a10;
                 }
-                for (n2 = at2con.index[ac1]; n2 < at2con.index[ac1 + 1]; n2++)
+                for (const int c2 : at2con[ac1])
                 {
-                    c2 = at2con.a[n2];
                     if (c2 != c0 && c2 != c1)
                     {
                         const int* iap = constr_iatomptr(ia1, ia2, c2);
@@ -1403,40 +1405,36 @@ static int count_triangle_constraints(const InteractionLists& ilist, const t_blo
 }
 
 //! Finds sequences of sequential constraints.
-static bool more_than_two_sequential_constraints(const InteractionLists& ilist, const t_blocka& at2con)
+static bool more_than_two_sequential_constraints(const InteractionLists& ilist, const ListOfLists<int>& at2con)
 {
-    int  ncon1, ncon_tot, c;
-    bool bMoreThanTwoSequentialConstraints;
-
-    ncon1    = ilist[F_CONSTR].size() / 3;
-    ncon_tot = ncon1 + ilist[F_CONSTRNC].size() / 3;
+    const int ncon1    = ilist[F_CONSTR].size() / 3;
+    const int ncon_tot = ncon1 + ilist[F_CONSTRNC].size() / 3;
 
     gmx::ArrayRef<const int> ia1 = ilist[F_CONSTR].iatoms;
     gmx::ArrayRef<const int> ia2 = ilist[F_CONSTRNC].iatoms;
 
-    bMoreThanTwoSequentialConstraints = FALSE;
-    for (c = 0; c < ncon_tot && !bMoreThanTwoSequentialConstraints; c++)
+    for (int c = 0; c < ncon_tot; c++)
     {
         const int* iap = constr_iatomptr(ia1, ia2, c);
         const int  a1  = iap[1];
         const int  a2  = iap[2];
         /* Check if this constraint has constraints connected at both atoms */
-        if (at2con.index[a1 + 1] - at2con.index[a1] > 1 && at2con.index[a2 + 1] - at2con.index[a2] > 1)
+        if (at2con[a1].ssize() > 1 && at2con[a2].ssize() > 1)
         {
-            bMoreThanTwoSequentialConstraints = TRUE;
+            return true;
         }
     }
 
-    return bMoreThanTwoSequentialConstraints;
+    return false;
 }
 
-Lincs* init_lincs(FILE*                    fplog,
-                  const gmx_mtop_t&        mtop,
-                  int                      nflexcon_global,
-                  ArrayRef<const t_blocka> at2con,
-                  bool                     bPLINCS,
-                  int                      nIter,
-                  int                      nProjOrder)
+Lincs* init_lincs(FILE*                            fplog,
+                  const gmx_mtop_t&                mtop,
+                  int                              nflexcon_global,
+                  ArrayRef<const ListOfLists<int>> atomToConstraintsPerMolType,
+                  bool                             bPLINCS,
+                  int                              nIter,
+                  int                              nProjOrder)
 {
     // TODO this should become a unique_ptr
     Lincs* li;
@@ -1458,9 +1456,10 @@ Lincs* init_lincs(FILE*                    fplog,
     li->max_connect = 0;
     for (size_t mt = 0; mt < mtop.moltype.size(); mt++)
     {
+        const auto& at2con = atomToConstraintsPerMolType[mt];
         for (int a = 0; a < mtop.moltype[mt].atoms.nr; a++)
         {
-            li->max_connect = std::max(li->max_connect, at2con[mt].index[a + 1] - at2con[mt].index[a]);
+            li->max_connect = std::max(li->max_connect, int(at2con[a].ssize()));
         }
     }
 
@@ -1468,11 +1467,12 @@ Lincs* init_lincs(FILE*                    fplog,
     bMoreThanTwoSeq  = FALSE;
     for (const gmx_molblock_t& molb : mtop.molblock)
     {
-        const gmx_moltype_t& molt = mtop.moltype[molb.type];
+        const gmx_moltype_t& molt   = mtop.moltype[molb.type];
+        const auto&          at2con = atomToConstraintsPerMolType[molb.type];
 
-        li->ncg_triangle += molb.nmol * count_triangle_constraints(molt.ilist, at2con[molb.type]);
+        li->ncg_triangle += molb.nmol * count_triangle_constraints(molt.ilist, at2con);
 
-        if (!bMoreThanTwoSeq && more_than_two_sequential_constraints(molt.ilist, at2con[molb.type]))
+        if (!bMoreThanTwoSeq && more_than_two_sequential_constraints(molt.ilist, at2con))
         {
             bMoreThanTwoSeq = TRUE;
         }
@@ -1645,7 +1645,13 @@ static void lincs_thread_setup(Lincs* li, int natoms)
 }
 
 //! Assign a constraint.
-static void assign_constraint(Lincs* li, int constraint_index, int a1, int a2, real lenA, real lenB, const t_blocka* at2con)
+static void assign_constraint(Lincs*                  li,
+                              int                     constraint_index,
+                              int                     a1,
+                              int                     a2,
+                              real                    lenA,
+                              real                    lenB,
+                              const ListOfLists<int>& at2con)
 {
     int con;
 
@@ -1664,8 +1670,7 @@ static void assign_constraint(Lincs* li, int constraint_index, int a1, int a2, r
     /* Make space in the constraint connection matrix for constraints
      * connected to both end of the current constraint.
      */
-    li->ncc += at2con->index[a1 + 1] - at2con->index[a1] - 1 + at2con->index[a2 + 1]
-               - at2con->index[a2] - 1;
+    li->ncc += at2con[a1].ssize() - 1 + at2con[a2].ssize() - 1;
 
     li->blnr[con + 1] = li->ncc;
 
@@ -1675,13 +1680,13 @@ static void assign_constraint(Lincs* li, int constraint_index, int a1, int a2, r
 
 /*! \brief Check if constraint with topology index constraint_index is connected
  * to other constraints, and if so add those connected constraints to our task. */
-static void check_assign_connected(Lincs*          li,
-                                   const t_iatom*  iatom,
-                                   const t_idef&   idef,
-                                   bool            bDynamics,
-                                   int             a1,
-                                   int             a2,
-                                   const t_blocka* at2con)
+static void check_assign_connected(Lincs*                        li,
+                                   gmx::ArrayRef<const int>      iatom,
+                                   const InteractionDefinitions& idef,
+                                   bool                          bDynamics,
+                                   int                           a1,
+                                   int                           a2,
+                                   const ListOfLists<int>&       at2con)
 {
     /* Currently this function only supports constraint groups
      * in which all constraints share at least one atom
@@ -1690,28 +1695,18 @@ static void check_assign_connected(Lincs*          li,
      * connected constraints. We need to assign those
      * to the same task.
      */
-    int end;
-
-    for (end = 0; end < 2; end++)
+    for (int end = 0; end < 2; end++)
     {
-        int a, k;
+        const int a = (end == 0 ? a1 : a2);
 
-        a = (end == 0 ? a1 : a2);
-
-        for (k = at2con->index[a]; k < at2con->index[a + 1]; k++)
+        for (const int cc : at2con[a])
         {
-            int cc;
-
-            cc = at2con->a[k];
             /* Check if constraint cc has not yet been assigned */
             if (li->con_index[cc] == -1)
             {
-                int  type;
-                real lenA, lenB;
-
-                type = iatom[cc * 3];
-                lenA = idef.iparams[type].constr.dA;
-                lenB = idef.iparams[type].constr.dB;
+                const int  type = iatom[cc * 3];
+                const real lenA = idef.iparams[type].constr.dA;
+                const real lenB = idef.iparams[type].constr.dB;
 
                 if (bDynamics || lenA != 0 || lenB != 0)
                 {
@@ -1725,24 +1720,21 @@ static void check_assign_connected(Lincs*          li,
 /*! \brief Check if constraint with topology index constraint_index is involved
  * in a constraint triangle, and if so add the other two constraints
  * in the triangle to our task. */
-static void check_assign_triangle(Lincs*          li,
-                                  const t_iatom*  iatom,
-                                  const t_idef&   idef,
-                                  bool            bDynamics,
-                                  int             constraint_index,
-                                  int             a1,
-                                  int             a2,
-                                  const t_blocka* at2con)
+static void check_assign_triangle(Lincs*                        li,
+                                  gmx::ArrayRef<const int>      iatom,
+                                  const InteractionDefinitions& idef,
+                                  bool                          bDynamics,
+                                  int                           constraint_index,
+                                  int                           a1,
+                                  int                           a2,
+                                  const ListOfLists<int>&       at2con)
 {
-    int nca, cc[32], ca[32], k;
+    int nca, cc[32], ca[32];
     int c_triangle[2] = { -1, -1 };
 
     nca = 0;
-    for (k = at2con->index[a1]; k < at2con->index[a1 + 1]; k++)
+    for (const int c : at2con[a1])
     {
-        int c;
-
-        c = at2con->a[k];
         if (c != constraint_index)
         {
             int aa1, aa2;
@@ -1764,11 +1756,8 @@ static void check_assign_triangle(Lincs*          li,
         }
     }
 
-    for (k = at2con->index[a2]; k < at2con->index[a2 + 1]; k++)
+    for (const int c : at2con[a2])
     {
-        int c;
-
-        c = at2con->a[k];
         if (c != constraint_index)
         {
             int aa1, aa2, i;
@@ -1827,7 +1816,7 @@ static void check_assign_triangle(Lincs*          li,
 }
 
 //! Sets matrix indices.
-static void set_matrix_indices(Lincs* li, const Task& li_task, const t_blocka* at2con, bool bSortMatrix)
+static void set_matrix_indices(Lincs* li, const Task& li_task, const ListOfLists<int>& at2con, bool bSortMatrix)
 {
     for (int b = li_task.b0; b < li_task.b1; b++)
     {
@@ -1835,17 +1824,17 @@ static void set_matrix_indices(Lincs* li, const Task& li_task, const t_blocka* a
         const int a2 = li->atoms[b].index2;
 
         int i = li->blnr[b];
-        for (int k = at2con->index[a1]; k < at2con->index[a1 + 1]; k++)
+        for (const int constraint : at2con[a1])
         {
-            int concon = li->con_index[at2con->a[k]];
+            const int concon = li->con_index[constraint];
             if (concon != b)
             {
                 li->blbnb[i++] = concon;
             }
         }
-        for (int k = at2con->index[a2]; k < at2con->index[a2 + 1]; k++)
+        for (const int constraint : at2con[a2])
         {
-            int concon = li->con_index[at2con->a[k]];
+            const int concon = li->con_index[constraint];
             if (concon != b)
             {
                 li->blbnb[i++] = concon;
@@ -1860,12 +1849,14 @@ static void set_matrix_indices(Lincs* li, const Task& li_task, const t_blocka* a
     }
 }
 
-void set_lincs(const t_idef& idef, const t_mdatoms& md, bool bDynamics, const t_commrec* cr, Lincs* li)
+void set_lincs(const InteractionDefinitions& idef,
+               const int                     numAtoms,
+               const real*                   invmass,
+               const real                    lambda,
+               bool                          bDynamics,
+               const t_commrec*              cr,
+               Lincs*                        li)
 {
-    int      natoms;
-    t_blocka at2con;
-    t_iatom* iatom;
-
     li->nc_real = 0;
     li->nc      = 0;
     li->ncc     = 0;
@@ -1884,7 +1875,7 @@ void set_lincs(const t_idef& idef, const t_mdatoms& md, bool bDynamics, const t_
     }
 
     /* This is the local topology, so there are only F_CONSTR constraints */
-    if (idef.il[F_CONSTR].nr == 0)
+    if (idef.il[F_CONSTR].empty())
     {
         /* There are no constraints,
          * we do not need to fill any data structures.
@@ -1897,6 +1888,7 @@ void set_lincs(const t_idef& idef, const t_mdatoms& md, bool bDynamics, const t_
         fprintf(debug, "Building the LINCS connectivity\n");
     }
 
+    int natoms;
     if (DOMAINDECOMP(cr))
     {
         if (cr->dd->constraints)
@@ -1912,12 +1904,13 @@ void set_lincs(const t_idef& idef, const t_mdatoms& md, bool bDynamics, const t_
     }
     else
     {
-        natoms = md.homenr;
+        natoms = numAtoms;
     }
 
-    at2con = make_at2con(natoms, idef.il, idef.iparams, flexibleConstraintTreatment(bDynamics));
+    const ListOfLists<int> at2con =
+            make_at2con(natoms, idef.il, idef.iparams, flexibleConstraintTreatment(bDynamics));
 
-    const int ncon_tot = idef.il[F_CONSTR].nr / 3;
+    const int ncon_tot = idef.il[F_CONSTR].size() / 3;
 
     /* Ensure we have enough padding for aligned loads for each thread */
     const int numEntries = ncon_tot + li->ntask * simd_width;
@@ -1940,7 +1933,7 @@ void set_lincs(const t_idef& idef, const t_mdatoms& md, bool bDynamics, const t_
     li->tmp4.resize(numEntries);
     li->mlambda.resize(numEntries);
 
-    iatom = idef.il[F_CONSTR].iatoms;
+    gmx::ArrayRef<const int> iatom = idef.il[F_CONSTR].iatoms;
 
     li->blnr[0] = li->ncc;
 
@@ -2003,6 +1996,8 @@ void set_lincs(const t_idef& idef, const t_mdatoms& md, bool bDynamics, const t_
          */
         li_task->b0 = li->nc;
 
+        gmx::ArrayRef<const t_iparams> iparams = idef.iparams;
+
         while (con < ncon_tot && li->nc - li_task->b0 < ncon_target)
         {
             if (li->con_index[con] == -1)
@@ -2013,26 +2008,26 @@ void set_lincs(const t_idef& idef, const t_mdatoms& md, bool bDynamics, const t_
                 type = iatom[3 * con];
                 a1   = iatom[3 * con + 1];
                 a2   = iatom[3 * con + 2];
-                lenA = idef.iparams[type].constr.dA;
-                lenB = idef.iparams[type].constr.dB;
+                lenA = iparams[type].constr.dA;
+                lenB = iparams[type].constr.dB;
                 /* Skip the flexible constraints when not doing dynamics */
                 if (bDynamics || lenA != 0 || lenB != 0)
                 {
-                    assign_constraint(li, con, a1, a2, lenA, lenB, &at2con);
+                    assign_constraint(li, con, a1, a2, lenA, lenB, at2con);
 
                     if (li->ntask > 1 && !li->bTaskDep)
                     {
                         /* We can generate independent tasks. Check if we
                          * need to assign connected constraints to our task.
                          */
-                        check_assign_connected(li, iatom, idef, bDynamics, a1, a2, &at2con);
+                        check_assign_connected(li, iatom, idef, bDynamics, a1, a2, at2con);
                     }
                     if (li->ntask > 1 && li->ncg_triangle > 0)
                     {
                         /* Ensure constraints in one triangle are assigned
                          * to the same task.
                          */
-                        check_assign_triangle(li, iatom, idef, bDynamics, con, a1, a2, &at2con);
+                        check_assign_triangle(li, iatom, idef, bDynamics, con, a1, a2, at2con);
                     }
                 }
             }
@@ -2096,13 +2091,11 @@ void set_lincs(const t_idef& idef, const t_mdatoms& md, bool bDynamics, const t_
                 li_task.tri_bits.resize(li_task.b1 - li_task.b0);
             }
 
-            set_matrix_indices(li, li_task, &at2con, bSortMatrix);
+            set_matrix_indices(li, li_task, at2con, bSortMatrix);
         }
         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
     }
 
-    done_blocka(&at2con);
-
     if (cr->dd == nullptr)
     {
         /* Since the matrix is static, we should free some memory */
@@ -2135,16 +2128,16 @@ void set_lincs(const t_idef& idef, const t_mdatoms& md, bool bDynamics, const t_
 
     if (li->ntask > 1)
     {
-        lincs_thread_setup(li, md.nr);
+        lincs_thread_setup(li, numAtoms);
     }
 
-    set_lincs_matrix(li, md.invmass, md.lambda);
+    set_lincs_matrix(li, invmass, lambda);
 }
 
 //! Issues a warning when LINCS constraints cannot be satisfied.
 static void lincs_warning(gmx_domdec_t*                 dd,
-                          const rvec*                   x,
-                          rvec*                         xprime,
+                          ArrayRef<const RVec>          x,
+                          ArrayRef<const RVec>          xprime,
                           t_pbc*                        pbc,
                           int                           ncons,
                           gmx::ArrayRef<const AtomPair> atoms,
@@ -2211,7 +2204,7 @@ struct LincsDeviations
 };
 
 //! Determine how well the constraints have been satisfied.
-static LincsDeviations makeLincsDeviations(const Lincs& lincsd, const rvec* x, const t_pbc* pbc)
+static LincsDeviations makeLincsDeviations(const Lincs& lincsd, ArrayRef<const RVec> x, const t_pbc* pbc)
 {
     LincsDeviations                result;
     const ArrayRef<const AtomPair> atoms  = lincsd.atoms;
@@ -2260,28 +2253,29 @@ static LincsDeviations makeLincsDeviations(const Lincs& lincsd, const rvec* x, c
     return result;
 }
 
-bool constrain_lincs(bool                  computeRmsd,
-                     const t_inputrec&     ir,
-                     int64_t               step,
-                     Lincs*                lincsd,
-                     const t_mdatoms&      md,
-                     const t_commrec*      cr,
-                     const gmx_multisim_t* ms,
-                     const rvec*           x,
-                     rvec*                 xprime,
-                     rvec*                 min_proj,
-                     const matrix          box,
-                     t_pbc*                pbc,
-                     real                  lambda,
-                     real*                 dvdlambda,
-                     real                  invdt,
-                     rvec*                 v,
-                     bool                  bCalcVir,
-                     tensor                vir_r_m_dr,
-                     ConstraintVariable    econq,
-                     t_nrnb*               nrnb,
-                     int                   maxwarn,
-                     int*                  warncount)
+bool constrain_lincs(bool                            computeRmsd,
+                     const t_inputrec&               ir,
+                     int64_t                         step,
+                     Lincs*                          lincsd,
+                     const real*                     invmass,
+                     const t_commrec*                cr,
+                     const gmx_multisim_t*           ms,
+                     ArrayRefWithPadding<const RVec> xPadded,
+                     ArrayRefWithPadding<RVec>       xprimePadded,
+                     ArrayRef<RVec>                  min_proj,
+                     const matrix                    box,
+                     t_pbc*                          pbc,
+                     const bool                      hasMassPerturbed,
+                     real                            lambda,
+                     real*                           dvdlambda,
+                     real                            invdt,
+                     ArrayRef<RVec>                  v,
+                     bool                            bCalcVir,
+                     tensor                          vir_r_m_dr,
+                     ConstraintVariable              econq,
+                     t_nrnb*                         nrnb,
+                     int                             maxwarn,
+                     int*                            warncount)
 {
     bool bOK = TRUE;
 
@@ -2301,6 +2295,9 @@ bool constrain_lincs(bool                  computeRmsd,
         return bOK;
     }
 
+    ArrayRef<const RVec> x      = xPadded.unpaddedArrayRef();
+    ArrayRef<RVec>       xprime = xprimePadded.unpaddedArrayRef();
+
     if (econq == ConstraintVariable::Positions)
     {
         /* We can't use bCalcDHDL here, since NULL can be passed for dvdlambda
@@ -2308,9 +2305,9 @@ bool constrain_lincs(bool                  computeRmsd,
          */
         if (ir.efep != efepNO)
         {
-            if (md.nMassPerturbed && lincsd->matlam != md.lambda)
+            if (hasMassPerturbed && lincsd->matlam != lambda)
             {
-                set_lincs_matrix(lincsd, md.invmass, md.lambda);
+                set_lincs_matrix(lincsd, invmass, lambda);
             }
 
             for (int i = 0; i < lincsd->nc; i++)
@@ -2374,8 +2371,9 @@ bool constrain_lincs(bool                  computeRmsd,
 
                 clear_mat(lincsd->task[th].vir_r_m_dr);
 
-                do_lincs(x, xprime, box, pbc, lincsd, th, md.invmass, cr, bCalcDHDL, ir.LincsWarnAngle,
-                         &bWarn, invdt, v, bCalcVir, th == 0 ? vir_r_m_dr : lincsd->task[th].vir_r_m_dr);
+                do_lincs(xPadded, xprimePadded, box, pbc, lincsd, th, invmass, cr, bCalcDHDL,
+                         ir.LincsWarnAngle, &bWarn, invdt, v, bCalcVir,
+                         th == 0 ? vir_r_m_dr : lincsd->task[th].vir_r_m_dr);
             }
             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
         }
@@ -2412,7 +2410,7 @@ bool constrain_lincs(bool                  computeRmsd,
                     std::string simMesg;
                     if (isMultiSim(ms))
                     {
-                        simMesg += gmx::formatString(" in simulation %d", ms->sim);
+                        simMesg += gmx::formatString(" in simulation %d", ms->simulationIndex_);
                     }
                     fprintf(stderr,
                             "\nStep %" PRId64
@@ -2452,8 +2450,8 @@ bool constrain_lincs(bool                  computeRmsd,
             {
                 int th = gmx_omp_get_thread_num();
 
-                do_lincsp(x, xprime, min_proj, pbc, lincsd, th, md.invmass, econq, bCalcDHDL,
-                          bCalcVir, th == 0 ? vir_r_m_dr : lincsd->task[th].vir_r_m_dr);
+                do_lincsp(xPadded, xprimePadded, min_proj, pbc, lincsd, th, invmass, econq,
+                          bCalcDHDL, bCalcVir, th == 0 ? vir_r_m_dr : lincsd->task[th].vir_r_m_dr);
             }
             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
         }
@@ -2494,7 +2492,7 @@ bool constrain_lincs(bool                  computeRmsd,
     {
         inc_nrnb(nrnb, eNR_LINCSMAT, lincsd->nOrder * lincsd->ncc_triangle);
     }
-    if (v)
+    if (!v.empty())
     {
         inc_nrnb(nrnb, eNR_CONSTR_V, lincsd->nc_real * 2);
     }
index dfc847127e6e61847c0d08cbcb3f4115649907a0..0b00370c018719e946f92c5f214b8d946579c0c2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 struct gmx_mtop_t;
 struct gmx_multisim_t;
-struct t_blocka;
+class InteractionDefinitions;
 struct t_commrec;
-struct t_idef;
 struct t_inputrec;
-struct t_mdatoms;
 struct t_nrnb;
 struct t_pbc;
 
 namespace gmx
 {
 
+template<typename>
+class ArrayRefWithPadding;
 enum class ConstraintVariable : int;
-
-/* Abstract type for LINCS that is defined only in the file that uses it */
 class Lincs;
+template<typename>
+class ListOfLists;
+
 
 /*! \brief Return the data for determining constraint RMS relative deviations. */
 ArrayRef<real> lincs_rmsdData(Lincs* lincsd);
@@ -76,45 +77,52 @@ ArrayRef<real> lincs_rmsdData(Lincs* lincsd);
 real lincs_rmsd(const Lincs* lincsd);
 
 /*! \brief Initializes and returns the lincs data struct. */
-Lincs* init_lincs(FILE*                    fplog,
-                  const gmx_mtop_t&        mtop,
-                  int                      nflexcon_global,
-                  ArrayRef<const t_blocka> at2con,
-                  bool                     bPLINCS,
-                  int                      nIter,
-                  int                      nProjOrder);
+Lincs* init_lincs(FILE*                            fplog,
+                  const gmx_mtop_t&                mtop,
+                  int                              nflexcon_global,
+                  ArrayRef<const ListOfLists<int>> atomsToConstraintsPerMolType,
+                  bool                             bPLINCS,
+                  int                              nIter,
+                  int                              nProjOrder);
 
 /*! \brief Destructs the lincs object when it is not nullptr. */
 void done_lincs(Lincs* li);
 
 /*! \brief Initialize lincs stuff */
-void set_lincs(const t_idef& idef, const t_mdatoms& md, bool bDynamics, const t_commrec* cr, Lincs* li);
+void set_lincs(const InteractionDefinitions& idef,
+               int                           numAtoms,
+               const real*                   invmass,
+               real                          lambda,
+               bool                          bDynamics,
+               const t_commrec*              cr,
+               Lincs*                        li);
 
 /*! \brief Applies LINCS constraints.
  *
  * \returns true if the constraining succeeded. */
-bool constrain_lincs(bool                  computeRmsd,
-                     const t_inputrec&     ir,
-                     int64_t               step,
-                     Lincs*                lincsd,
-                     const t_mdatoms&      md,
-                     const t_commrec*      cr,
-                     const gmx_multisim_t* ms,
-                     const rvec*           x,
-                     rvec*                 xprime,
-                     rvec*                 min_proj,
-                     const matrix          box,
-                     t_pbc*                pbc,
-                     real                  lambda,
-                     real*                 dvdlambda,
-                     real                  invdt,
-                     rvec*                 v,
-                     bool                  bCalcVir,
-                     tensor                vir_r_m_dr,
-                     ConstraintVariable    econq,
-                     t_nrnb*               nrnb,
-                     int                   maxwarn,
-                     int*                  warncount);
+bool constrain_lincs(bool                            computeRmsd,
+                     const t_inputrec&               ir,
+                     int64_t                         step,
+                     Lincs*                          lincsd,
+                     const real*                     invmass,
+                     const t_commrec*                cr,
+                     const gmx_multisim_t*           ms,
+                     ArrayRefWithPadding<const RVec> x,
+                     ArrayRefWithPadding<RVec>       xprime,
+                     ArrayRef<RVec>                  min_proj,
+                     const matrix                    box,
+                     t_pbc*                          pbc,
+                     bool                            hasMassPerturbed,
+                     real                            lambda,
+                     real*                           dvdlambda,
+                     real                            invdt,
+                     ArrayRef<RVec>                  v,
+                     bool                            bCalcVir,
+                     tensor                          vir_r_m_dr,
+                     ConstraintVariable              econq,
+                     t_nrnb*                         nrnb,
+                     int                             maxwarn,
+                     int*                            warncount);
 
 } // namespace gmx
 
similarity index 92%
rename from src/gromacs/mdlib/lincs_cuda.cu
rename to src/gromacs/mdlib/lincs_gpu.cu
index cc1356ce24efe03e32a65e81426fa8763f038a75..f3b832f18bfdcd59113a68b44f5158f92fc3d1a1 100644 (file)
  * using CUDA, including class initialization, data-structures management
  * and GPU kernel.
  *
- * \note Management of periodic boundary should be unified with SETTLE and
- *       removed from here.
- * \todo Reconsider naming, i.e. "cuda" suffics should be changed to "gpu".
- *
  * \author Artem Zhmurov <zhmurov@gmail.com>
  * \author Alan Gray <alang@nvidia.com>
  *
@@ -51,7 +47,7 @@
  */
 #include "gmxpre.h"
 
-#include "lincs_cuda.cuh"
+#include "lincs_gpu.cuh"
 
 #include <assert.h>
 #include <stdio.h>
@@ -69,6 +65,7 @@
 #include "gromacs/mdlib/constr.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/pbc_aiuc_cuda.cuh"
+#include "gromacs/topology/forcefieldparameters.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/topology/topology.h"
 
@@ -99,7 +96,7 @@ constexpr static int c_maxThreadsPerBlock = c_threadsPerBlock;
  *        3. Use analytical solution for matrix A inversion.
  *        4. Introduce mapping of thread id to both single constraint and single atom, thus designating
  *           Nth threads to deal with Nat <= Nth coupled atoms and Nc <= Nth coupled constraints.
- *       See Redmine issue #2885 for details (https://redmine.gromacs.org/issues/2885)
+ *       See Issue #2885 for details (https://gitlab.com/gromacs/gromacs/-/issues/2885)
  * \todo The use of __restrict__  for gm_xp and gm_v causes failure, probably because of the atomic
          operations. Investigate this issue further.
  *
@@ -108,7 +105,7 @@ constexpr static int c_maxThreadsPerBlock = c_threadsPerBlock;
  */
 template<bool updateVelocities, bool computeVirial>
 __launch_bounds__(c_maxThreadsPerBlock) __global__
-        void lincs_kernel(LincsCudaKernelParameters kernelParams,
+        void lincs_kernel(LincsGpuKernelParameters kernelParams,
                           const float3* __restrict__ gm_x,
                           float3*     gm_xp,
                           float3*     gm_v,
@@ -429,13 +426,14 @@ inline auto getLincsKernelPtr(const bool updateVelocities, const bool computeVir
     return kernelPtr;
 }
 
-void LincsCuda::apply(const float3* d_x,
-                      float3*       d_xp,
-                      const bool    updateVelocities,
-                      float3*       d_v,
-                      const real    invdt,
-                      const bool    computeVirial,
-                      tensor        virialScaled)
+void LincsGpu::apply(const float3* d_x,
+                     float3*       d_xp,
+                     const bool    updateVelocities,
+                     float3*       d_v,
+                     const real    invdt,
+                     const bool    computeVirial,
+                     tensor        virialScaled,
+                     const PbcAiuc pbcAiuc)
 {
     ensureNoPendingCudaError("In CUDA version of LINCS");
 
@@ -449,7 +447,7 @@ void LincsCuda::apply(const float3* d_x,
     {
         // Fill with zeros so the values can be reduced to it
         // Only 6 values are needed because virial is symmetrical
-        clearDeviceBufferAsync(&kernelParams_.d_virialScaled, 0, 6, commandStream_);
+        clearDeviceBufferAsync(&kernelParams_.d_virialScaled, 0, 6, deviceStream_);
     }
 
     auto kernelPtr = getLincsKernelPtr(updateVelocities, computeVirial);
@@ -477,18 +475,20 @@ void LincsCuda::apply(const float3* d_x,
     {
         config.sharedMemorySize = c_threadsPerBlock * 3 * sizeof(float);
     }
-    config.stream = commandStream_;
+
+    kernelParams_.pbcAiuc = pbcAiuc;
 
     const auto kernelArgs =
             prepareGpuKernelArguments(kernelPtr, config, &kernelParams_, &d_x, &d_xp, &d_v, &invdt);
 
-    launchGpuKernel(kernelPtr, config, nullptr, "lincs_kernel<updateVelocities, computeVirial>", kernelArgs);
+    launchGpuKernel(kernelPtr, config, deviceStream_, nullptr,
+                    "lincs_kernel<updateVelocities, computeVirial>", kernelArgs);
 
     if (computeVirial)
     {
         // Copy LINCS virial data and add it to the common virial
         copyFromDeviceBuffer(h_virialScaled_.data(), &kernelParams_.d_virialScaled, 0, 6,
-                             commandStream_, GpuApiCallBehavior::Sync, nullptr);
+                             deviceStream_, GpuApiCallBehavior::Sync, nullptr);
 
         // Mapping [XX, XY, XZ, YY, YZ, ZZ] internal format to a tensor object
         virialScaled[XX][XX] += h_virialScaled_[0];
@@ -507,8 +507,12 @@ void LincsCuda::apply(const float3* d_x,
     return;
 }
 
-LincsCuda::LincsCuda(int numIterations, int expansionOrder, CommandStream commandStream) :
-    commandStream_(commandStream)
+LincsGpu::LincsGpu(int                  numIterations,
+                   int                  expansionOrder,
+                   const DeviceContext& deviceContext,
+                   const DeviceStream&  deviceStream) :
+    deviceContext_(deviceContext),
+    deviceStream_(deviceStream)
 {
     kernelParams_.numIterations  = numIterations;
     kernelParams_.expansionOrder = expansionOrder;
@@ -519,7 +523,7 @@ LincsCuda::LincsCuda(int numIterations, int expansionOrder, CommandStream comman
             c_threadsPerBlock > 0 && ((c_threadsPerBlock & (c_threadsPerBlock - 1)) == 0),
             "Number of threads per block should be a power of two in order for reduction to work.");
 
-    allocateDeviceBuffer(&kernelParams_.d_virialScaled, 6, nullptr);
+    allocateDeviceBuffer(&kernelParams_.d_virialScaled, 6, deviceContext_);
     h_virialScaled_.resize(6);
 
     // The data arrays should be expanded/reallocated on first call of set() function.
@@ -527,7 +531,7 @@ LincsCuda::LincsCuda(int numIterations, int expansionOrder, CommandStream comman
     numAtomsAlloc_              = 0;
 }
 
-LincsCuda::~LincsCuda()
+LincsGpu::~LincsGpu()
 {
     freeDeviceBuffer(&kernelParams_.d_virialScaled);
 
@@ -701,7 +705,7 @@ static std::vector<int> countNumCoupledConstraints(ArrayRef<const int> iatoms,
     return numCoupledConstraints;
 }
 
-bool LincsCuda::isNumCoupledConstraintsSupported(const gmx_mtop_t& mtop)
+bool LincsGpu::isNumCoupledConstraintsSupported(const gmx_mtop_t& mtop)
 {
     for (const gmx_moltype_t& molType : mtop.moltype)
     {
@@ -721,9 +725,8 @@ bool LincsCuda::isNumCoupledConstraintsSupported(const gmx_mtop_t& mtop)
     return true;
 }
 
-void LincsCuda::set(const t_idef& idef, const t_mdatoms& md)
+void LincsGpu::set(const InteractionDefinitions& idef, const int numAtoms, const real* invmass)
 {
-    int numAtoms = md.nr;
     // List of constrained atoms (CPU memory)
     std::vector<int2> constraintsHost;
     // Equilibrium distances for the constraints (CPU)
@@ -736,10 +739,9 @@ void LincsCuda::set(const t_idef& idef, const t_mdatoms& md)
     std::vector<float> massFactorsHost;
 
     // List of constrained atoms in local topology
-    gmx::ArrayRef<const int> iatoms =
-            constArrayRefFromArray(idef.il[F_CONSTR].iatoms, idef.il[F_CONSTR].nr);
-    const int stride         = NRAL(F_CONSTR) + 1;
-    const int numConstraints = idef.il[F_CONSTR].nr / stride;
+    ArrayRef<const int> iatoms         = idef.il[F_CONSTR].iatoms;
+    const int           stride         = NRAL(F_CONSTR) + 1;
+    const int           numConstraints = idef.il[F_CONSTR].size() / stride;
 
     // Early exit if no constraints
     if (numConstraints == 0)
@@ -860,10 +862,10 @@ void LincsCuda::set(const t_idef& idef, const t_mdatoms& md)
 
                 int center = c1a1;
 
-                float sqrtmu1 = 1.0 / sqrt(md.invmass[c1a1] + md.invmass[c1a2]);
-                float sqrtmu2 = 1.0 / sqrt(md.invmass[c2a1] + md.invmass[c2a2]);
+                float sqrtmu1 = 1.0 / sqrt(invmass[c1a1] + invmass[c1a2]);
+                float sqrtmu2 = 1.0 / sqrt(invmass[c2a1] + invmass[c2a2]);
 
-                massFactorsHost.at(index) = -sign * md.invmass[center] * sqrtmu1 * sqrtmu2;
+                massFactorsHost.at(index) = -sign * invmass[center] * sqrtmu1 * sqrtmu2;
 
                 coupledConstraintsCountsHost.at(splitMap.at(c1))++;
             }
@@ -887,10 +889,10 @@ void LincsCuda::set(const t_idef& idef, const t_mdatoms& md)
 
                 int center = c1a2;
 
-                float sqrtmu1 = 1.0 / sqrt(md.invmass[c1a1] + md.invmass[c1a2]);
-                float sqrtmu2 = 1.0 / sqrt(md.invmass[c2a1] + md.invmass[c2a2]);
+                float sqrtmu1 = 1.0 / sqrt(invmass[c1a1] + invmass[c1a2]);
+                float sqrtmu2 = 1.0 / sqrt(invmass[c2a1] + invmass[c2a2]);
 
-                massFactorsHost.at(index) = sign * md.invmass[center] * sqrtmu1 * sqrtmu2;
+                massFactorsHost.at(index) = sign * invmass[center] * sqrtmu1 * sqrtmu2;
 
                 coupledConstraintsCountsHost.at(splitMap.at(c1))++;
             }
@@ -914,18 +916,19 @@ void LincsCuda::set(const t_idef& idef, const t_mdatoms& md)
 
         numConstraintsThreadsAlloc_ = kernelParams_.numConstraintsThreads;
 
-        allocateDeviceBuffer(&kernelParams_.d_constraints, kernelParams_.numConstraintsThreads, nullptr);
+        allocateDeviceBuffer(&kernelParams_.d_constraints, kernelParams_.numConstraintsThreads,
+                             deviceContext_);
         allocateDeviceBuffer(&kernelParams_.d_constraintsTargetLengths,
-                             kernelParams_.numConstraintsThreads, nullptr);
+                             kernelParams_.numConstraintsThreads, deviceContext_);
 
         allocateDeviceBuffer(&kernelParams_.d_coupledConstraintsCounts,
-                             kernelParams_.numConstraintsThreads, nullptr);
+                             kernelParams_.numConstraintsThreads, deviceContext_);
         allocateDeviceBuffer(&kernelParams_.d_coupledConstraintsIndices,
-                             maxCoupledConstraints * kernelParams_.numConstraintsThreads, nullptr);
+                             maxCoupledConstraints * kernelParams_.numConstraintsThreads, deviceContext_);
         allocateDeviceBuffer(&kernelParams_.d_massFactors,
-                             maxCoupledConstraints * kernelParams_.numConstraintsThreads, nullptr);
+                             maxCoupledConstraints * kernelParams_.numConstraintsThreads, deviceContext_);
         allocateDeviceBuffer(&kernelParams_.d_matrixA,
-                             maxCoupledConstraints * kernelParams_.numConstraintsThreads, nullptr);
+                             maxCoupledConstraints * kernelParams_.numConstraintsThreads, deviceContext_);
     }
 
     // (Re)allocate the memory, if the number of atoms has increased.
@@ -936,34 +939,29 @@ void LincsCuda::set(const t_idef& idef, const t_mdatoms& md)
             freeDeviceBuffer(&kernelParams_.d_inverseMasses);
         }
         numAtomsAlloc_ = numAtoms;
-        allocateDeviceBuffer(&kernelParams_.d_inverseMasses, numAtoms, nullptr);
+        allocateDeviceBuffer(&kernelParams_.d_inverseMasses, numAtoms, deviceContext_);
     }
 
     // Copy data to GPU.
     copyToDeviceBuffer(&kernelParams_.d_constraints, constraintsHost.data(), 0,
-                       kernelParams_.numConstraintsThreads, commandStream_,
-                       GpuApiCallBehavior::Sync, nullptr);
+                       kernelParams_.numConstraintsThreads, deviceStream_, GpuApiCallBehavior::Sync,
+                       nullptr);
     copyToDeviceBuffer(&kernelParams_.d_constraintsTargetLengths,
                        constraintsTargetLengthsHost.data(), 0, kernelParams_.numConstraintsThreads,
-                       commandStream_, GpuApiCallBehavior::Sync, nullptr);
+                       deviceStream_, GpuApiCallBehavior::Sync, nullptr);
     copyToDeviceBuffer(&kernelParams_.d_coupledConstraintsCounts,
                        coupledConstraintsCountsHost.data(), 0, kernelParams_.numConstraintsThreads,
-                       commandStream_, GpuApiCallBehavior::Sync, nullptr);
+                       deviceStream_, GpuApiCallBehavior::Sync, nullptr);
     copyToDeviceBuffer(&kernelParams_.d_coupledConstraintsIndices, coupledConstraintsIndicesHost.data(),
                        0, maxCoupledConstraints * kernelParams_.numConstraintsThreads,
-                       commandStream_, GpuApiCallBehavior::Sync, nullptr);
+                       deviceStream_, GpuApiCallBehavior::Sync, nullptr);
     copyToDeviceBuffer(&kernelParams_.d_massFactors, massFactorsHost.data(), 0,
-                       maxCoupledConstraints * kernelParams_.numConstraintsThreads, commandStream_,
+                       maxCoupledConstraints * kernelParams_.numConstraintsThreads, deviceStream_,
                        GpuApiCallBehavior::Sync, nullptr);
 
-    GMX_RELEASE_ASSERT(md.invmass != nullptr, "Masses of atoms should be specified.\n");
-    copyToDeviceBuffer(&kernelParams_.d_inverseMasses, md.invmass, 0, numAtoms, commandStream_,
+    GMX_RELEASE_ASSERT(invmass != nullptr, "Masses of atoms should be specified.\n");
+    copyToDeviceBuffer(&kernelParams_.d_inverseMasses, invmass, 0, numAtoms, deviceStream_,
                        GpuApiCallBehavior::Sync, nullptr);
 }
 
-void LincsCuda::setPbc(const t_pbc* pbc)
-{
-    setPbcAiuc(pbc->ndim_ePBC, pbc->box, &kernelParams_.pbcAiuc);
-}
-
 } // namespace gmx
similarity index 80%
rename from src/gromacs/mdlib/lincs_cuda.cuh
rename to src/gromacs/mdlib/lincs_gpu.cuh
index 06c8bb40ab0eb4abf0837c90227dd93d69a65e97..7dfa2deb82a07c8e21eeb05bb1c6943ec5d066ce 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  */
 /*! \libinternal \file
  *
- * \brief Declares the class for CUDA implementation of LINCS.
+ * \brief Declares the class for GPU implementation of LINCS.
  *
  * \author Artem Zhmurov <zhmurov@gmail.com>
  *
  * \ingroup module_mdlib
  * \inlibraryapi
  */
-#ifndef GMX_MDLIB_LINCS_CUDA_CUH
-#define GMX_MDLIB_LINCS_CUDA_CUH
+#ifndef GMX_MDLIB_LINCS_GPU_CUH
+#define GMX_MDLIB_LINCS_GPU_CUH
 
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/gputraits.cuh"
 #include "gromacs/mdlib/constr.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/pbc_aiuc.h"
-#include "gromacs/topology/idef.h"
 #include "gromacs/utility/classhelpers.h"
 
+class InteractionDefinitions;
+
 namespace gmx
 {
 
@@ -60,7 +62,7 @@ namespace gmx
  * to the GPU as a single structure.
  *
  */
-struct LincsCudaKernelParameters
+struct LincsGpuKernelParameters
 {
     //! Periodic boundary data
     PbcAiuc pbcAiuc;
@@ -93,8 +95,8 @@ struct LincsCudaKernelParameters
     float* d_massFactors;
 };
 
-/*! \internal \brief Class with interfaces and data for CUDA version of LINCS. */
-class LincsCuda
+/*! \internal \brief Class with interfaces and data for GPU version of LINCS. */
+class LincsGpu
 {
 
 public:
@@ -102,18 +104,22 @@ public:
      *
      * \param[in] numIterations    Number of iteration for the correction of the projection.
      * \param[in] expansionOrder   Order of the matrix inversion algorithm.
-     * \param[in] commandStream    Device command stream.
+     * \param[in] deviceContext    Device context (dummy in CUDA).
+     * \param[in] deviceStream     Device command stream.
      */
-    LincsCuda(int numIterations, int expansionOrder, CommandStream commandStream);
+    LincsGpu(int                  numIterations,
+             int                  expansionOrder,
+             const DeviceContext& deviceContext,
+             const DeviceStream&  deviceStream);
     /*! \brief Destructor.*/
-    ~LincsCuda();
+    ~LincsGpu();
 
     /*! \brief Apply LINCS.
      *
      * Applies LINCS to coordinates and velocities, stored on GPU.
      * The results are not automatically copied back to the CPU memory.
      * Method uses this class data structures which should be updated
-     * when needed using set() and setPbc() method.
+     * when needed using set() method.
      *
      * \param[in]     d_x               Coordinates before timestep (in GPU memory)
      * \param[in,out] d_xp              Coordinates after timestep (in GPU memory). The
@@ -125,6 +131,7 @@ public:
      *                                  multipliers when velocities are updated)
      * \param[in]     computeVirial     If virial should be updated.
      * \param[in,out] virialScaled      Scaled virial tensor to be updated.
+     * \param[in]     pbcAiuc           PBC data.
      */
     void apply(const float3* d_x,
                float3*       d_xp,
@@ -132,7 +139,8 @@ public:
                float3*       d_v,
                const real    invdt,
                const bool    computeVirial,
-               tensor        virialScaled);
+               tensor        virialScaled,
+               const PbcAiuc pbcAiuc);
 
     /*! \brief
      * Update data-structures (e.g. after NB search step).
@@ -147,39 +155,29 @@ public:
      * Information about constraints is taken from:
      *     idef.il[F_CONSTR].iatoms  --- type (T) of constraint and two atom indexes (i1, i2)
      *     idef.iparams[T].constr.dA --- target length for constraint of type T
-     * From t_mdatom, the code takes:
-     *     md.invmass  --- array of inverse square root of masses for each atom in the system.
-     *
-     * \param[in] idef  Local topology data to get information on constraints from.
-     * \param[in] md    Atoms data to get atom masses from.
-     */
-    void set(const t_idef& idef, const t_mdatoms& md);
-
-    /*! \brief
-     * Update PBC data.
-     *
-     * Converts pbc data from t_pbc into the PbcAiuc format and stores the latter.
-     *
-     * \todo Remove this method. LINCS should not manage PBC.
      *
-     * \param[in] pbc The PBC data in t_pbc format.
+     * \param[in] idef      Local topology data to get information on constraints from.
+     * \param[in] numAtoms  Number of atoms.
+     * \param[in] invmass   Inverse masses of atoms.
      */
-    void setPbc(const t_pbc* pbc);
+    void set(const InteractionDefinitions& idef, int numAtoms, const real* invmass);
 
     /*! \brief
      * Returns whether the maximum number of coupled constraints is supported
-     * by the CUDA LINCS code.
+     * by the GPU LINCS code.
      *
      * \param[in] mtop The molecular topology
      */
     static bool isNumCoupledConstraintsSupported(const gmx_mtop_t& mtop);
 
 private:
-    //! CUDA stream
-    CommandStream commandStream_;
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! GPU stream
+    const DeviceStream& deviceStream_;
 
-    //! Parameters and pointers, passed to the CUDA kernel
-    LincsCudaKernelParameters kernelParams_;
+    //! Parameters and pointers, passed to the GPU kernel
+    LincsGpuKernelParameters kernelParams_;
 
     //! Scaled virial tensor (6 floats: [XX, XY, XZ, YY, YZ, ZZ])
     std::vector<float> h_virialScaled_;
@@ -201,4 +199,4 @@ private:
 
 } // namespace gmx
 
-#endif // GMX_MDLIB_LINCS_CUDA_CUH
+#endif // GMX_MDLIB_LINCS_GPU_CUH
index dff38c6576b029af40333e80a7fde0ad0733ca7f..cecbc3ca0ca216dd06906c2ea992797ea46e04e9 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
-#include "gromacs/mdlib/dispersioncorrection.h"
+#include "gromacs/mdlib/coupling.h"
+#include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdlib/simulationsignal.h"
 #include "gromacs/mdlib/stat.h"
 #include "gromacs/mdlib/tgroup.h"
 #include "gromacs/mdlib/update.h"
 #include "gromacs/mdlib/vcm.h"
-#include "gromacs/mdrunutility/multisim.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/df_history.h"
 #include "gromacs/mdtypes/enerdata.h"
@@ -64,6 +65,7 @@
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pulling/pull.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/snprintf.h"
 
-// TODO move this to multi-sim module
-bool multisim_int_all_are_equal(const gmx_multisim_t* ms, int64_t value)
+static void calc_ke_part_normal(gmx::ArrayRef<const gmx::RVec> v,
+                                const t_grpopts*               opts,
+                                const t_mdatoms*               md,
+                                gmx_ekindata_t*                ekind,
+                                t_nrnb*                        nrnb,
+                                gmx_bool                       bEkinAveVel)
 {
-    bool     allValuesAreEqual = true;
-    int64_t* buf;
+    int                         g;
+    gmx::ArrayRef<t_grp_tcstat> tcstat  = ekind->tcstat;
+    gmx::ArrayRef<t_grp_acc>    grpstat = ekind->grpstat;
 
-    GMX_RELEASE_ASSERT(ms, "Invalid use of multi-simulation pointer");
+    /* three main: VV with AveVel, vv with AveEkin, leap with AveEkin.  Leap with AveVel is also
+       an option, but not supported now.
+       bEkinAveVel: If TRUE, we sum into ekin, if FALSE, into ekinh.
+     */
 
-    snew(buf, ms->nsim);
-    /* send our value to all other master ranks, receive all of theirs */
-    buf[ms->sim] = value;
-    gmx_sumli_sim(ms->nsim, buf, ms);
+    /* group velocities are calculated in update_ekindata and
+     * accumulated in acumulate_groups.
+     * Now the partial global and groups ekin.
+     */
+    for (g = 0; (g < opts->ngtc); g++)
+    {
+        copy_mat(tcstat[g].ekinh, tcstat[g].ekinh_old);
+        if (bEkinAveVel)
+        {
+            clear_mat(tcstat[g].ekinf);
+            tcstat[g].ekinscalef_nhc = 1.0; /* need to clear this -- logic is complicated! */
+        }
+        else
+        {
+            clear_mat(tcstat[g].ekinh);
+        }
+    }
+    ekind->dekindl_old = ekind->dekindl;
+    int nthread        = gmx_omp_nthreads_get(emntUpdate);
 
-    for (int s = 0; s < ms->nsim; s++)
+#pragma omp parallel for num_threads(nthread) schedule(static)
+    for (int thread = 0; thread < nthread; thread++)
     {
-        if (buf[s] != value)
+        // This OpenMP only loops over arrays and does not call any functions
+        // or memory allocation. It should not be able to throw, so for now
+        // we do not need a try/catch wrapper.
+        int     start_t, end_t, n;
+        int     ga, gt;
+        rvec    v_corrt;
+        real    hm;
+        int     d, m;
+        matrix* ekin_sum;
+        real*   dekindl_sum;
+
+        start_t = ((thread + 0) * md->homenr) / nthread;
+        end_t   = ((thread + 1) * md->homenr) / nthread;
+
+        ekin_sum    = ekind->ekin_work[thread];
+        dekindl_sum = ekind->dekindl_work[thread];
+
+        for (gt = 0; gt < opts->ngtc; gt++)
         {
-            allValuesAreEqual = false;
-            break;
+            clear_mat(ekin_sum[gt]);
+        }
+        *dekindl_sum = 0.0;
+
+        ga = 0;
+        gt = 0;
+        for (n = start_t; n < end_t; n++)
+        {
+            if (md->cACC)
+            {
+                ga = md->cACC[n];
+            }
+            if (md->cTC)
+            {
+                gt = md->cTC[n];
+            }
+            hm = 0.5 * md->massT[n];
+
+            for (d = 0; (d < DIM); d++)
+            {
+                v_corrt[d] = v[n][d] - grpstat[ga].u[d];
+            }
+            for (d = 0; (d < DIM); d++)
+            {
+                for (m = 0; (m < DIM); m++)
+                {
+                    /* if we're computing a full step velocity, v_corrt[d] has v(t).  Otherwise, v(t+dt/2) */
+                    ekin_sum[gt][m][d] += hm * v_corrt[m] * v_corrt[d];
+                }
+            }
+            if (md->nMassPerturbed && md->bPerturbed[n])
+            {
+                *dekindl_sum += 0.5 * (md->massB[n] - md->massA[n]) * iprod(v_corrt, v_corrt);
+            }
         }
     }
 
-    sfree(buf);
+    ekind->dekindl = 0;
+    for (int thread = 0; thread < nthread; thread++)
+    {
+        for (g = 0; g < opts->ngtc; g++)
+        {
+            if (bEkinAveVel)
+            {
+                m_add(tcstat[g].ekinf, ekind->ekin_work[thread][g], tcstat[g].ekinf);
+            }
+            else
+            {
+                m_add(tcstat[g].ekinh, ekind->ekin_work[thread][g], tcstat[g].ekinh);
+            }
+        }
 
-    return allValuesAreEqual;
+        ekind->dekindl += *ekind->dekindl_work[thread];
+    }
+
+    inc_nrnb(nrnb, eNR_EKIN, md->homenr);
 }
 
-int multisim_min(const gmx_multisim_t* ms, int nmin, int n)
+static void calc_ke_part_visc(const matrix                   box,
+                              gmx::ArrayRef<const gmx::RVec> x,
+                              gmx::ArrayRef<const gmx::RVec> v,
+                              const t_grpopts*               opts,
+                              const t_mdatoms*               md,
+                              gmx_ekindata_t*                ekind,
+                              t_nrnb*                        nrnb,
+                              gmx_bool                       bEkinAveVel)
 {
-    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++)
+    int                         start = 0, homenr = md->homenr;
+    int                         g, d, n, m, gt = 0;
+    rvec                        v_corrt;
+    real                        hm;
+    gmx::ArrayRef<t_grp_tcstat> tcstat = ekind->tcstat;
+    t_cos_acc*                  cosacc = &(ekind->cosacc);
+    real                        dekindl;
+    real                        fac, cosz;
+    double                      mvcos;
+
+    for (g = 0; g < opts->ngtc; g++)
     {
-        bPos   = bPos && (buf[s] > 0);
-        bEqual = bEqual && (buf[s] == buf[0]);
+        copy_mat(ekind->tcstat[g].ekinh, ekind->tcstat[g].ekinh_old);
+        clear_mat(ekind->tcstat[g].ekinh);
     }
-    if (bPos)
+    ekind->dekindl_old = ekind->dekindl;
+
+    fac     = 2 * M_PI / box[ZZ][ZZ];
+    mvcos   = 0;
+    dekindl = 0;
+    for (n = start; n < start + homenr; n++)
     {
-        if (bEqual)
+        if (md->cTC)
         {
-            nmin = std::min(nmin, buf[0]);
+            gt = md->cTC[n];
         }
-        else
+        hm = 0.5 * md->massT[n];
+
+        /* Note that the times of x and v differ by half a step */
+        /* MRS -- would have to be changed for VV */
+        cosz = std::cos(fac * x[n][ZZ]);
+        /* Calculate the amplitude of the new velocity profile */
+        mvcos += 2 * cosz * md->massT[n] * v[n][XX];
+
+        copy_rvec(v[n], v_corrt);
+        /* Subtract the profile for the kinetic energy */
+        v_corrt[XX] -= cosz * cosacc->vcos;
+        for (d = 0; (d < DIM); d++)
         {
-            /* Find the least common multiple */
-            for (d = 2; d < nmin; d++)
+            for (m = 0; (m < DIM); m++)
             {
-                s = 0;
-                while (s < ms->nsim && d % buf[s] == 0)
+                /* if we're computing a full step velocity, v_corrt[d] has v(t).  Otherwise, v(t+dt/2) */
+                if (bEkinAveVel)
                 {
-                    s++;
+                    tcstat[gt].ekinf[m][d] += hm * v_corrt[m] * v_corrt[d];
                 }
-                if (s == ms->nsim)
+                else
                 {
-                    /* We found the LCM and it is less than nmin */
-                    nmin = d;
-                    break;
+                    tcstat[gt].ekinh[m][d] += hm * v_corrt[m] * v_corrt[d];
                 }
             }
         }
+        if (md->nPerturbed && md->bPerturbed[n])
+        {
+            /* The minus sign here might be confusing.
+             * The kinetic contribution from dH/dl doesn't come from
+             * d m(l)/2 v^2 / dl, but rather from d p^2/2m(l) / dl,
+             * where p are the momenta. The difference is only a minus sign.
+             */
+            dekindl -= 0.5 * (md->massB[n] - md->massA[n]) * iprod(v_corrt, v_corrt);
+        }
     }
-    sfree(buf);
+    ekind->dekindl = dekindl;
+    cosacc->mvcos  = mvcos;
+
+    inc_nrnb(nrnb, eNR_EKIN, homenr);
+}
 
-    return nmin;
+static void calc_ke_part(gmx::ArrayRef<const gmx::RVec> x,
+                         gmx::ArrayRef<const gmx::RVec> v,
+                         const matrix                   box,
+                         const t_grpopts*               opts,
+                         const t_mdatoms*               md,
+                         gmx_ekindata_t*                ekind,
+                         t_nrnb*                        nrnb,
+                         gmx_bool                       bEkinAveVel)
+{
+    if (ekind->cosacc.cos_accel == 0)
+    {
+        calc_ke_part_normal(v, opts, md, ekind, nrnb, bEkinAveVel);
+    }
+    else
+    {
+        calc_ke_part_visc(box, x, v, opts, md, ekind, nrnb, bEkinAveVel);
+    }
 }
 
 /* TODO Specialize this routine into init-time and loop-time versions?
    e.g. bReadEkin is only true when restoring from checkpoint */
-void compute_globals(gmx_global_stat*          gstat,
-                     t_commrec*                cr,
-                     const t_inputrec*         ir,
-                     t_forcerec*               fr,
-                     gmx_ekindata_t*           ekind,
-                     const rvec*               x,
-                     const rvec*               v,
-                     const matrix              box,
-                     real                      vdwLambda,
-                     const 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::Constraints*         constr,
-                     gmx::SimulationSignaller* signalCoordinator,
-                     const matrix              lastbox,
-                     int*                      totalNumberOfBondedInteractions,
-                     gmx_bool*                 bSumEkinhOld,
-                     const int                 flags)
+void compute_globals(gmx_global_stat*               gstat,
+                     t_commrec*                     cr,
+                     const t_inputrec*              ir,
+                     t_forcerec*                    fr,
+                     gmx_ekindata_t*                ekind,
+                     gmx::ArrayRef<const gmx::RVec> x,
+                     gmx::ArrayRef<const gmx::RVec> v,
+                     const matrix                   box,
+                     const 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,
+                     gmx::Constraints*              constr,
+                     gmx::SimulationSignaller*      signalCoordinator,
+                     const matrix                   lastbox,
+                     int*                           totalNumberOfBondedInteractions,
+                     gmx_bool*                      bSumEkinhOld,
+                     const int                      flags)
 {
     gmx_bool bEner, bPres, bTemp;
     gmx_bool bStopCM, bGStat, bReadEkin, bEkinAveVel, bScaleEkin, bConstrain;
@@ -242,7 +381,7 @@ void compute_globals(gmx_global_stat*          gstat,
             if (PAR(cr))
             {
                 wallcycle_start(wcycle, ewcMoveE);
-                global_stat(gstat, cr, enerd, force_vir, shake_vir, mu_tot, ir, ekind, constr,
+                global_stat(gstat, cr, enerd, force_vir, shake_vir, ir, ekind, constr,
                             bStopCM ? vcm : nullptr, signalBuffer.size(), signalBuffer.data(),
                             totalNumberOfBondedInteractions, *bSumEkinhOld, flags);
                 wallcycle_stop(wcycle, ewcMoveE);
@@ -283,210 +422,7 @@ void compute_globals(gmx_global_stat*          gstat,
          * Use the box from last timestep since we already called update().
          */
 
-        enerd->term[F_PRES] = calc_pres(fr->ePBC, ir->nwall, lastbox, ekind->ekin, total_vir, pres);
-    }
-
-    /* ##########  Long range energy information ###### */
-    if ((bEner || bPres) && fr->dispersionCorrection)
-    {
-        /* 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 tensors */
-
-        const DispersionCorrection::Correction correction =
-                fr->dispersionCorrection->calculate(lastbox, vdwLambda);
-
-        if (bEner)
-        {
-            enerd->term[F_DISPCORR] = correction.energy;
-            enerd->term[F_EPOT] += correction.energy;
-            enerd->term[F_DVDL_VDW] += correction.dvdl;
-        }
-        if (bPres)
-        {
-            correction.correctVirial(total_vir);
-            correction.correctPressure(pres);
-            enerd->term[F_PDISPCORR] = correction.pressure;
-            enerd->term[F_PRES] += correction.pressure;
-        }
-    }
-}
-
-void setCurrentLambdasRerun(int64_t           step,
-                            const t_lambda*   fepvals,
-                            const t_trxframe* rerun_fr,
-                            const double* /*lam0*/,
-                            t_state* globalState)
-{
-    GMX_RELEASE_ASSERT(globalState != nullptr,
-                       "setCurrentLambdasGlobalRerun should be called with a valid state object");
-
-    if (rerun_fr->bLambda)
-    {
-        if (fepvals->delta_lambda == 0)
-        {
-            globalState->lambda[efptFEP] = rerun_fr->lambda;
-        }
-        else
-        {
-            /* find out between which two value of lambda we should be */
-            const real fracSimulationLambda = step * fepvals->delta_lambda;
-
-            // Set initial lambda value for the simulation either from initialFEPStateIndex or,
-            // if not set, from the initial lambda.
-            double initialGlobalLambda = 0;
-            if (fepvals->init_fep_state > -1)
-            {
-                if (fepvals->n_lambda > 1)
-                {
-                    initialGlobalLambda =
-                            static_cast<double>(fepvals->init_fep_state) / (fepvals->n_lambda - 1);
-                }
-            }
-            else
-            {
-                if (fepvals->init_lambda > -1)
-                {
-                    initialGlobalLambda = fepvals->init_lambda;
-                }
-            }
-
-            const double globalLambda = initialGlobalLambda + fracSimulationLambda;
-
-            // when there is no lambda value array, set all lambdas to steps * deltaLambdaPerStep
-            if (fepvals->n_lambda <= 0)
-            {
-                std::fill(std::begin(globalState->lambda), std::end(globalState->lambda), globalLambda);
-                return;
-            }
-
-            GMX_ASSERT(
-                    globalLambda <= 1 || gmx_within_tol(globalLambda, 1, 1e-5),
-                    "Lambda may not be larger than one when interpolating an array of multi-lambda "
-                    "values.");
-            GMX_ASSERT(globalLambda >= 0 || gmx_within_tol(globalLambda, 0, 1e-5),
-                       "Lambda may not be negative when interpolating an array of multi-lambda "
-                       "values.");
-
-            // find out between which two value lambda array elements to interpolate
-            // at the boundary of the lambda array, return the boundary array values
-            const int fepStateLeft =
-                    std::max(0, static_cast<int>(std::floor(globalLambda * (fepvals->n_lambda - 1))));
-
-            const int fepStateRight = std::min(fepvals->n_lambda - 1, fepStateLeft + 1);
-
-            // interpolate between this state and the next
-            const double fracBetween = globalLambda * (fepvals->n_lambda - 1) - fepStateLeft;
-            for (int i = 0; i < efptNR; i++)
-            {
-                globalState->lambda[i] = fepvals->all_lambda[i][fepStateLeft]
-                                         + fracBetween
-                                                   * (fepvals->all_lambda[i][fepStateRight]
-                                                      - fepvals->all_lambda[i][fepStateLeft]);
-            }
-        }
-    }
-    else if (rerun_fr->bFepState)
-    {
-        globalState->fep_state = rerun_fr->fep_state;
-        for (int i = 0; i < efptNR; i++)
-        {
-            globalState->lambda[i] = fepvals->all_lambda[i][globalState->fep_state];
-        }
-    }
-}
-
-void setCurrentLambdasLocal(const int64_t   step,
-                            const t_lambda* fepvals,
-                            const double* /*lam0*/,
-                            gmx::ArrayRef<real> lambda,
-                            const int           currentFEPState)
-/* find the current lambdas.  If rerunning, we either read in a state, or a lambda value,
-   requiring different logic. */
-{
-    if (fepvals->delta_lambda != 0)
-    {
-        /* find out between which two value of lambda we should be */
-        const real fracSimulationLambda = step * fepvals->delta_lambda;
-
-        // Set initial lambda value for the simulation either from initialFEPStateIndex or,
-        // if not set, from the initial lambda.
-        double initialGlobalLambda = 0;
-        if (fepvals->init_fep_state > -1)
-        {
-            if (fepvals->n_lambda > 1)
-            {
-                initialGlobalLambda =
-                        static_cast<double>(fepvals->init_fep_state) / (fepvals->n_lambda - 1);
-            }
-        }
-        else
-        {
-            if (fepvals->init_lambda > -1)
-            {
-                initialGlobalLambda = fepvals->init_lambda;
-            }
-        }
-
-        const double globalLambda = initialGlobalLambda + fracSimulationLambda;
-
-        // when there is no lambda value array, set all lambdas to steps * deltaLambdaPerStep
-        if (fepvals->n_lambda <= 0)
-        {
-            std::fill(std::begin(lambda), std::end(lambda), globalLambda);
-            return;
-        }
-
-        GMX_ASSERT(globalLambda <= 1 || gmx_within_tol(globalLambda, 1, 1e-5),
-                   "Lambda may not be larger than one when interpolating an array of multi-lambda "
-                   "values.");
-        GMX_ASSERT(
-                globalLambda >= 0 || gmx_within_tol(globalLambda, 0, 1e-5),
-                "Lambda may not be negative when interpolating an array of multi-lambda values.");
-
-        // find out between which two value lambda array elements to interpolate
-        // at the boundary of the lambda array, return the boundary array values
-        const int fepStateLeft =
-                std::max(0, static_cast<int>(std::floor(globalLambda * (fepvals->n_lambda - 1))));
-
-        const int fepStateRight = std::min(fepvals->n_lambda - 1, fepStateLeft + 1);
-
-        // interpolate between this state and the next
-        const double fracBetween = globalLambda * (fepvals->n_lambda - 1) - fepStateLeft;
-        for (int i = 0; i < efptNR; i++)
-        {
-            lambda[i] = fepvals->all_lambda[i][fepStateLeft]
-                        + fracBetween
-                                  * (fepvals->all_lambda[i][fepStateRight]
-                                     - fepvals->all_lambda[i][fepStateLeft]);
-        }
-    }
-    else
-    {
-        /* if < 0, fep_state was never defined, and we should not set lambda from the state */
-        if (currentFEPState > -1)
-        {
-            GMX_ASSERT(currentFEPState < fepvals->n_lambda,
-                       "Current FEP state may not be larger than number of given states.");
-            for (int i = 0; i < efptNR; i++)
-            {
-                lambda[i] = fepvals->all_lambda[i][currentFEPState];
-            }
-            return;
-        }
-
-        if (fepvals->init_fep_state > -1)
-        {
-            GMX_ASSERT(fepvals->init_fep_state < fepvals->n_lambda,
-                       "Initial FEP state may not be larger than number of given states.");
-            for (int i = 0; i < efptNR; i++)
-            {
-                lambda[i] = fepvals->all_lambda[i][fepvals->init_fep_state];
-            }
-            return;
-        }
-        std::fill(std::begin(lambda), std::end(lambda), fepvals->init_lambda);
+        enerd->term[F_PRES] = calc_pres(fr->pbcType, ir->nwall, lastbox, ekind->ekin, total_vir, pres);
     }
 }
 
@@ -583,7 +519,7 @@ void rerun_parallel_comm(t_commrec* cr, t_trxframe* fr, gmx_bool* bLastStep)
     }
     xp = fr->x;
     vp = fr->v;
-    gmx_bcast(sizeof(*fr), fr, cr);
+    gmx_bcast(sizeof(*fr), fr, cr->mpi_comm_mygroup);
     fr->x = xp;
     fr->v = vp;
 
@@ -611,7 +547,7 @@ void set_state_entries(t_state* state, const t_inputrec* ir, bool useModularSimu
     }
 
     state->nnhpres = 0;
-    if (ir->ePBC != epbcNONE)
+    if (ir->pbcType != PbcType::No)
     {
         state->flags |= (1 << estBOX);
         if (inputrecPreserveShape(ir))
@@ -636,7 +572,7 @@ void set_state_entries(t_state* state, const t_inputrec* ir, bool useModularSimu
             state->flags |= (1 << estVETA);
             state->flags |= (1 << estVOL0);
         }
-        if (ir->epc == epcBERENDSEN)
+        if (ir->epc == epcBERENDSEN || ir->epc == epcCRESCALE)
         {
             state->flags |= (1 << estBAROS_INT);
         }
index 2918ed87f7747d82eef7070bac411baf9c10b560..3544c6f14f68f1fef79e838f4f3eee72291fcb74 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_MDLIB_MD_SUPPORT_H
 
 #include "gromacs/mdlib/vcm.h"
+#include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/timing/wallcycle.h"
 
 struct gmx_ekindata_t;
 struct gmx_enerdata_t;
 struct gmx_global_stat;
-struct gmx_multisim_t;
 struct gmx_signalling_t;
 struct t_extmass;
 struct t_forcerec;
 struct t_grpopts;
 struct t_inputrec;
-struct t_lambda;
 struct t_nrnb;
 class t_state;
 struct t_trxframe;
@@ -96,64 +96,39 @@ class SimulationSignaller;
  * inputrec. */
 int computeGlobalCommunicationPeriod(const gmx::MDLogger& mdlog, t_inputrec* ir, const t_commrec* cr);
 
-/*! \brief Return true if the \p value is equal across the set of multi-simulations
- *
- * \todo This duplicates some of check_multi_int. Consolidate. */
-bool multisim_int_all_are_equal(const gmx_multisim_t* ms, int64_t value);
-
 void rerun_parallel_comm(t_commrec* cr, t_trxframe* fr, gmx_bool* bLastStep);
 
 //! \brief Allocate and initialize node-local state entries
 void set_state_entries(t_state* state, const t_inputrec* ir, bool useModularSimulator);
 
-/* Set the lambda values in the global state from a frame read with rerun */
-void setCurrentLambdasRerun(int64_t           step,
-                            const t_lambda*   fepvals,
-                            const t_trxframe* rerun_fr,
-                            const double*     lam0,
-                            t_state*          globalState);
-
-/* Set the lambda values at each step of mdrun when they change */
-void setCurrentLambdasLocal(int64_t             step,
-                            const t_lambda*     fepvals,
-                            const double*       lam0,
-                            gmx::ArrayRef<real> lambda,
-                            int                 currentFEPState);
-
-int multisim_min(const gmx_multisim_t* ms, int nmin, int n);
-/* Set an appropriate value for n across the whole multi-simulation */
-
-
 /* Compute global variables during integration
  *
  * Coordinates x are needed for kinetic energy calculation with cosine accelation
  * and for COM removal with rotational and acceleration correction modes.
  * Velocities v are needed for kinetic energy calculation and for COM removal.
  */
-void compute_globals(gmx_global_stat*          gstat,
-                     t_commrec*                cr,
-                     const t_inputrec*         ir,
-                     t_forcerec*               fr,
-                     gmx_ekindata_t*           ekind,
-                     const rvec*               x,
-                     const rvec*               v,
-                     const matrix              box,
-                     real                      vdwLambda,
-                     const 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::Constraints*         constr,
-                     gmx::SimulationSignaller* signalCoordinator,
-                     const matrix              lastbox,
-                     int*                      totalNumberOfBondedInteractions,
-                     gmx_bool*                 bSumEkinhOld,
-                     int                       flags);
+void compute_globals(gmx_global_stat*               gstat,
+                     t_commrec*                     cr,
+                     const t_inputrec*              ir,
+                     t_forcerec*                    fr,
+                     gmx_ekindata_t*                ekind,
+                     gmx::ArrayRef<const gmx::RVec> x,
+                     gmx::ArrayRef<const gmx::RVec> v,
+                     const matrix                   box,
+                     const 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,
+                     gmx::Constraints*              constr,
+                     gmx::SimulationSignaller*      signalCoordinator,
+                     const matrix                   lastbox,
+                     int*                           totalNumberOfBondedInteractions,
+                     gmx_bool*                      bSumEkinhOld,
+                     int                            flags);
 
 #endif
index fc5d3f2c961b9383e216a04e266d6c1290cf2ebd..0d1abd8565da3830a1ad7b112cc5b99bcffb4ccd 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,9 +47,9 @@
 #include "gromacs/gpu_utils/hostallocator.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
-#include "gromacs/mdlib/qmmm.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/topology/mtop_lookup.h"
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/topology/topology.h"
@@ -74,8 +75,9 @@ MDAtoms::~MDAtoms()
     gmx::AlignedAllocationPolicy::free(mdatoms_->invmass);
     sfree(mdatoms_->invMassPerDim);
     sfree(mdatoms_->typeA);
-    sfree(mdatoms_->chargeB);
     sfree(mdatoms_->typeB);
+    /* mdatoms->chargeA and mdatoms->chargeB point at chargeA_.data()
+     * and chargeB_.data() respectively. They get freed automatically. */
     sfree(mdatoms_->sqrt_c6A);
     sfree(mdatoms_->sigmaA);
     sfree(mdatoms_->sigma3A);
@@ -92,21 +94,32 @@ MDAtoms::~MDAtoms()
     sfree(mdatoms_->bPerturbed);
     sfree(mdatoms_->cU1);
     sfree(mdatoms_->cU2);
-    sfree(mdatoms_->bQM);
 }
 
-void MDAtoms::resize(int newSize)
+void MDAtoms::resizeChargeA(const int newSize)
 {
     chargeA_.resizeWithPadding(newSize);
     mdatoms_->chargeA = chargeA_.data();
 }
 
-void MDAtoms::reserve(int newCapacity)
+void MDAtoms::resizeChargeB(const int newSize)
+{
+    chargeB_.resizeWithPadding(newSize);
+    mdatoms_->chargeB = chargeB_.data();
+}
+
+void MDAtoms::reserveChargeA(const int newCapacity)
 {
     chargeA_.reserveWithPadding(newCapacity);
     mdatoms_->chargeA = chargeA_.data();
 }
 
+void MDAtoms::reserveChargeB(const int newCapacity)
+{
+    chargeB_.reserveWithPadding(newCapacity);
+    mdatoms_->chargeB = chargeB_.data();
+}
+
 std::unique_ptr<MDAtoms> makeMDAtoms(FILE* fp, const gmx_mtop_t& mtop, const t_inputrec& ir, const bool rankHasPmeGpuTask)
 {
     auto mdAtoms = std::make_unique<MDAtoms>();
@@ -114,6 +127,7 @@ std::unique_ptr<MDAtoms> makeMDAtoms(FILE* fp, const gmx_mtop_t& mtop, const t_i
     if (rankHasPmeGpuTask)
     {
         changePinningPolicy(&mdAtoms->chargeA_, pme_get_pinning_policy());
+        changePinningPolicy(&mdAtoms->chargeB_, pme_get_pinning_policy());
     }
     t_mdatoms* md;
     snew(md, 1);
@@ -193,7 +207,12 @@ std::unique_ptr<MDAtoms> makeMDAtoms(FILE* fp, const gmx_mtop_t& mtop, const t_i
 
 } // namespace gmx
 
-void atoms2md(const gmx_mtop_t* mtop, const t_inputrec* ir, int nindex, const int* index, int homenr, gmx::MDAtoms* mdAtoms)
+void atoms2md(const gmx_mtop_t*  mtop,
+              const t_inputrec*  ir,
+              int                nindex,
+              gmx::ArrayRef<int> index,
+              int                homenr,
+              gmx::MDAtoms*      mdAtoms)
 {
     gmx_bool         bLJPME;
     const t_grpopts* opts;
@@ -237,12 +256,16 @@ void atoms2md(const gmx_mtop_t* mtop, const t_inputrec* ir, int nindex, const in
         // everything, but for now the semantics of md->nalloc being
         // the capacity are preserved by keeping vectors within
         // mdAtoms having the same properties as the other arrays.
-        mdAtoms->reserve(md->nalloc);
-        mdAtoms->resize(md->nr);
+        mdAtoms->reserveChargeA(md->nalloc);
+        mdAtoms->resizeChargeA(md->nr);
+        if (md->nPerturbed > 0)
+        {
+            mdAtoms->reserveChargeB(md->nalloc);
+            mdAtoms->resizeChargeB(md->nr);
+        }
         srenew(md->typeA, md->nalloc);
         if (md->nPerturbed)
         {
-            srenew(md->chargeB, md->nalloc);
             srenew(md->typeB, md->nalloc);
         }
         if (bLJPME)
@@ -299,11 +322,6 @@ void atoms2md(const gmx_mtop_t* mtop, const t_inputrec* ir, int nindex, const in
         {
             srenew(md->cU2, md->nalloc);
         }
-
-        if (ir->bQMMM)
-        {
-            srenew(md->bQM, md->nalloc);
-        }
     }
 
     int molb = 0;
@@ -318,7 +336,7 @@ void atoms2md(const gmx_mtop_t* mtop, const t_inputrec* ir, int nindex, const in
             real mA, mB, fac;
             real c6, c12;
 
-            if (index == nullptr)
+            if (index.empty())
             {
                 ag = i;
             }
@@ -482,20 +500,6 @@ void atoms2md(const gmx_mtop_t* mtop, const t_inputrec* ir, int nindex, const in
             {
                 md->cU2[i] = groups.groupNumbers[SimulationAtomGroupType::User2][ag];
             }
-
-            if (ir->bQMMM)
-            {
-                if (groups.groupNumbers[SimulationAtomGroupType::QuantumMechanics].empty()
-                    || groups.groupNumbers[SimulationAtomGroupType::QuantumMechanics][ag]
-                               < groups.groups[SimulationAtomGroupType::QuantumMechanics].size() - 1)
-                {
-                    md->bQM[i] = TRUE;
-                }
-                else
-                {
-                    md->bQM[i] = FALSE;
-                }
-            }
         }
         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
     }
index 2763e00a27f2ccd6d54259f59ae290d9420d94e9..44dbcbba2395ce4dde5b95360a984b828a1ceb12 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <vector>
 
 #include "gromacs/gpu_utils/hostallocator.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 #include "gromacs/utility/unique_cptr.h"
 
 struct gmx_mtop_t;
 struct t_inputrec;
+struct t_mdatoms;
 
 namespace gmx
 {
@@ -68,6 +69,8 @@ class MDAtoms
     unique_cptr<t_mdatoms> mdatoms_;
     //! Memory for chargeA that can be set up for efficient GPU transfer.
     gmx::PaddedHostVector<real> chargeA_;
+    //! Memory for chargeB that can be set up for efficient GPU transfer.
+    gmx::PaddedHostVector<real> chargeB_;
 
 public:
     // TODO make this private
@@ -77,16 +80,26 @@ public:
     t_mdatoms* mdatoms() { return mdatoms_.get(); }
     //! Const getter.
     const t_mdatoms* mdatoms() const { return mdatoms_.get(); }
-    /*! \brief Resizes memory.
+    /*! \brief Resizes memory for charges of FEP state A.
      *
      * \throws std::bad_alloc  If out of memory.
      */
-    void resize(int newSize);
-    /*! \brief Reserves memory.
+    void resizeChargeA(int newSize);
+    /*! \brief Resizes memory for charges of FEP state B.
      *
      * \throws std::bad_alloc  If out of memory.
      */
-    void reserve(int newCapacity);
+    void resizeChargeB(int newSize);
+    /*! \brief Reserves memory for charges of FEP state A.
+     *
+     * \throws std::bad_alloc  If out of memory.
+     */
+    void reserveChargeA(int newCapacity);
+    /*! \brief Reserves memory for charges of FEP state B.
+     *
+     * \throws std::bad_alloc  If out of memory.
+     */
+    void reserveChargeB(int newCapacity);
     //! Builder function.
     friend std::unique_ptr<MDAtoms>
     makeMDAtoms(FILE* fp, const gmx_mtop_t& mtop, const t_inputrec& ir, bool rankHasPmeGpuTask);
@@ -97,12 +110,12 @@ std::unique_ptr<MDAtoms> makeMDAtoms(FILE* fp, const gmx_mtop_t& mtop, const t_i
 
 } // namespace gmx
 
-void atoms2md(const gmx_mtop_t* mtop,
-              const t_inputrec* ir,
-              int               nindex,
-              const int*        index,
-              int               homenr,
-              gmx::MDAtoms*     mdAtoms);
+void atoms2md(const gmx_mtop_t*  mtop,
+              const t_inputrec*  ir,
+              int                nindex,
+              gmx::ArrayRef<int> index,
+              int                homenr,
+              gmx::MDAtoms*      mdAtoms);
 /* This routine copies the atoms->atom struct into md.
  * If index!=NULL only the indexed atoms are copied.
  * For the masses the A-state (lambda=0) mass is used.
index a0db60909977cca9e8698b79871310818362bda6..394c225ddeb819aa704156142ed1c2f76e1a8d2d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -758,12 +759,5 @@ void mde_delta_h_coll_restore_energyhistory(t_mde_delta_h_coll* dhc, const delta
     {
         dhc->start_lambda = deltaH->start_lambda;
     }
-    if (dhc->dh[0].ndh > 0)
-    {
-        dhc->start_time_set = TRUE;
-    }
-    else
-    {
-        dhc->start_time_set = FALSE;
-    }
+    dhc->start_time_set = (dhc->dh[0].ndh > 0);
 }
index c75ab4347c0dca6bde49e075c36d431240605e0b..b1dacb29aaa5e321f46809bf974f3492782c2867 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 0825e0ab7e33c6f292bc136ab8129b2a6cfde965..6ba96c89503b6d69924eb544949906a3898e968d 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -36,6 +37,8 @@
 
 #include "mdoutf.h"
 
+#include "config.h"
+
 #include "gromacs/commandline/filenm.h"
 #include "gromacs/domdec/collect.h"
 #include "gromacs/domdec/domdec_struct.h"
 #include "gromacs/mdlib/trajectory_writing.h"
 #include "gromacs/mdrunutility/handlerestart.h"
 #include "gromacs/mdrunutility/multisim.h"
+#include "gromacs/mdtypes/awh_history.h"
 #include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/df_history.h"
+#include "gromacs/mdtypes/edsamhistory.h"
+#include "gromacs/mdtypes/energyhistory.h"
 #include "gromacs/mdtypes/imdoutputprovider.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/mdrunoptions.h"
+#include "gromacs/mdtypes/observableshistory.h"
 #include "gromacs/mdtypes/state.h"
+#include "gromacs/mdtypes/swaphistory.h"
 #include "gromacs/timing/wallcycle.h"
 #include "gromacs/topology/topology.h"
+#include "gromacs/utility/baseversion.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/pleasecite.h"
+#include "gromacs/utility/programcontext.h"
 #include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/sysinfo.h"
 
 struct gmx_mdoutf
 {
@@ -78,13 +90,13 @@ struct gmx_mdoutf
     FILE*                         fp_dhdl;
     int                           natoms_global;
     int                           natoms_x_compressed;
-    SimulationGroups*             groups; /* for compressed position writing */
+    const SimulationGroups*       groups; /* for compressed position writing */
     gmx_wallcycle_t               wcycle;
     rvec*                         f_global;
     gmx::IMDOutputProvider*       outputProvider;
     const gmx::MdModulesNotifier* mdModulesNotifier;
     bool                          simulationsShareState;
-    MPI_Comm                      mpiCommMasters;
+    MPI_Comm                      mastersComm;
 };
 
 
@@ -96,7 +108,7 @@ gmx_mdoutf_t init_mdoutf(FILE*                         fplog,
                          gmx::IMDOutputProvider*       outputProvider,
                          const gmx::MdModulesNotifier& mdModulesNotifier,
                          const t_inputrec*             ir,
-                         gmx_mtop_t*                   top_global,
+                         const gmx_mtop_t*             top_global,
                          const gmx_output_env_t*       oenv,
                          gmx_wallcycle_t               wcycle,
                          const gmx::StartingBehavior   startingBehavior,
@@ -132,7 +144,7 @@ gmx_mdoutf_t init_mdoutf(FILE*                         fplog,
     of->simulationsShareState = simulationsShareState;
     if (of->simulationsShareState)
     {
-        of->mpiCommMasters = ms->mpi_comm_masters;
+        of->mastersComm = ms->mastersComm_;
     }
 
     if (MASTER(cr))
@@ -254,19 +266,257 @@ gmx_wallcycle_t mdoutf_get_wcycle(gmx_mdoutf_t of)
     return of->wcycle;
 }
 
-void mdoutf_write_to_trajectory_files(FILE*                    fplog,
-                                      const t_commrec*         cr,
-                                      gmx_mdoutf_t             of,
-                                      int                      mdof_flags,
-                                      int                      natoms,
-                                      int64_t                  step,
-                                      double                   t,
-                                      t_state*                 state_local,
-                                      t_state*                 state_global,
-                                      ObservablesHistory*      observablesHistory,
-                                      gmx::ArrayRef<gmx::RVec> f_local)
+static void mpiBarrierBeforeRename(const bool applyMpiBarrierBeforeRename, MPI_Comm mpiBarrierCommunicator)
+{
+    if (applyMpiBarrierBeforeRename)
+    {
+#if GMX_MPI
+        MPI_Barrier(mpiBarrierCommunicator);
+#else
+        GMX_RELEASE_ASSERT(false, "Should not request a barrier without MPI");
+        GMX_UNUSED_VALUE(mpiBarrierCommunicator);
+#endif
+    }
+}
+/*! \brief Write a checkpoint to the filename
+ *
+ * Appends the _step<step>.cpt with bNumberAndKeep, otherwise moves
+ * the previous checkpoint filename with suffix _prev.cpt.
+ */
+static void write_checkpoint(const char*                     fn,
+                             gmx_bool                        bNumberAndKeep,
+                             FILE*                           fplog,
+                             const t_commrec*                cr,
+                             ivec                            domdecCells,
+                             int                             nppnodes,
+                             int                             eIntegrator,
+                             int                             simulation_part,
+                             gmx_bool                        bExpanded,
+                             int                             elamstats,
+                             int64_t                         step,
+                             double                          t,
+                             t_state*                        state,
+                             ObservablesHistory*             observablesHistory,
+                             const gmx::MdModulesNotifier&   mdModulesNotifier,
+                             gmx::WriteCheckpointDataHolder* modularSimulatorCheckpointData,
+                             bool                            applyMpiBarrierBeforeRename,
+                             MPI_Comm                        mpiBarrierCommunicator)
+{
+    t_fileio* fp;
+    char*     fntemp; /* the temporary checkpoint file name */
+    int       npmenodes;
+    char      buf[1024], suffix[5 + STEPSTRSIZE], sbuf[STEPSTRSIZE];
+    t_fileio* ret;
+
+    if (DOMAINDECOMP(cr))
+    {
+        npmenodes = cr->npmenodes;
+    }
+    else
+    {
+        npmenodes = 0;
+    }
+
+#if !GMX_NO_RENAME
+    /* make the new temporary filename */
+    snew(fntemp, std::strlen(fn) + 5 + STEPSTRSIZE);
+    std::strcpy(fntemp, fn);
+    fntemp[std::strlen(fn) - std::strlen(ftp2ext(fn2ftp(fn))) - 1] = '\0';
+    sprintf(suffix, "_%s%s", "step", gmx_step_str(step, sbuf));
+    std::strcat(fntemp, suffix);
+    std::strcat(fntemp, fn + std::strlen(fn) - std::strlen(ftp2ext(fn2ftp(fn))) - 1);
+#else
+    /* if we can't rename, we just overwrite the cpt file.
+     * dangerous if interrupted.
+     */
+    snew(fntemp, std::strlen(fn));
+    std::strcpy(fntemp, fn);
+#endif
+    std::string timebuf = gmx_format_current_time();
+
+    if (fplog)
+    {
+        fprintf(fplog, "Writing checkpoint, step %s at %s\n\n", gmx_step_str(step, buf), timebuf.c_str());
+    }
+
+    /* Get offsets for open files */
+    auto outputfiles = gmx_fio_get_output_file_positions();
+
+    fp = gmx_fio_open(fntemp, "w");
+
+    /* We can check many more things now (CPU, acceleration, etc), but
+     * it is highly unlikely to have two separate builds with exactly
+     * the same version, user, time, and build host!
+     */
+
+    int nlambda = (state->dfhist ? state->dfhist->nlambda : 0);
+
+    edsamhistory_t* edsamhist = observablesHistory->edsamHistory.get();
+    int             nED       = (edsamhist ? edsamhist->nED : 0);
+
+    swaphistory_t* swaphist    = observablesHistory->swapHistory.get();
+    int            eSwapCoords = (swaphist ? swaphist->eSwapCoords : eswapNO);
+
+    CheckpointHeaderContents headerContents = { 0,
+                                                { 0 },
+                                                { 0 },
+                                                { 0 },
+                                                { 0 },
+                                                GMX_DOUBLE,
+                                                { 0 },
+                                                { 0 },
+                                                eIntegrator,
+                                                simulation_part,
+                                                step,
+                                                t,
+                                                nppnodes,
+                                                { 0 },
+                                                npmenodes,
+                                                state->natoms,
+                                                state->ngtc,
+                                                state->nnhpres,
+                                                state->nhchainlength,
+                                                nlambda,
+                                                state->flags,
+                                                0,
+                                                0,
+                                                0,
+                                                0,
+                                                0,
+                                                nED,
+                                                eSwapCoords,
+                                                false };
+    std::strcpy(headerContents.version, gmx_version());
+    std::strcpy(headerContents.fprog, gmx::getProgramContext().fullBinaryPath());
+    std::strcpy(headerContents.ftime, timebuf.c_str());
+    if (DOMAINDECOMP(cr))
+    {
+        copy_ivec(domdecCells, headerContents.dd_nc);
+    }
+
+    write_checkpoint_data(fp, headerContents, bExpanded, elamstats, state, observablesHistory,
+                          mdModulesNotifier, &outputfiles, modularSimulatorCheckpointData);
+
+    /* we really, REALLY, want to make sure to physically write the checkpoint,
+       and all the files it depends on, out to disk. Because we've
+       opened the checkpoint with gmx_fio_open(), it's in our list
+       of open files.  */
+    ret = gmx_fio_all_output_fsync();
+
+    if (ret)
+    {
+        char buf[STRLEN];
+        sprintf(buf, "Cannot fsync '%s'; maybe you are out of disk space?", gmx_fio_getname(ret));
+
+        if (getenv(GMX_IGNORE_FSYNC_FAILURE_ENV) == nullptr)
+        {
+            gmx_file(buf);
+        }
+        else
+        {
+            gmx_warning("%s", buf);
+        }
+    }
+
+    if (gmx_fio_close(fp) != 0)
+    {
+        gmx_file("Cannot read/write checkpoint; corrupt file, or maybe you are out of disk space?");
+    }
+
+    /* we don't move the checkpoint if the user specified they didn't want it,
+       or if the fsyncs failed */
+#if !GMX_NO_RENAME
+    if (!bNumberAndKeep && !ret)
+    {
+        if (gmx_fexist(fn))
+        {
+            /* Rename the previous checkpoint file */
+            mpiBarrierBeforeRename(applyMpiBarrierBeforeRename, mpiBarrierCommunicator);
+
+            std::strcpy(buf, fn);
+            buf[std::strlen(fn) - std::strlen(ftp2ext(fn2ftp(fn))) - 1] = '\0';
+            std::strcat(buf, "_prev");
+            std::strcat(buf, fn + std::strlen(fn) - std::strlen(ftp2ext(fn2ftp(fn))) - 1);
+            if (!GMX_FAHCORE)
+            {
+                /* we copy here so that if something goes wrong between now and
+                 * the rename below, there's always a state.cpt.
+                 * If renames are atomic (such as in POSIX systems),
+                 * this copying should be unneccesary.
+                 */
+                gmx_file_copy(fn, buf, FALSE);
+                /* We don't really care if this fails:
+                 * there's already a new checkpoint.
+                 */
+            }
+            else
+            {
+                gmx_file_rename(fn, buf);
+            }
+        }
+
+        /* Rename the checkpoint file from the temporary to the final name */
+        mpiBarrierBeforeRename(applyMpiBarrierBeforeRename, mpiBarrierCommunicator);
+
+        if (gmx_file_rename(fntemp, fn) != 0)
+        {
+            gmx_file("Cannot rename checkpoint file; maybe you are out of disk space?");
+        }
+    }
+#endif /* GMX_NO_RENAME */
+
+    sfree(fntemp);
+
+#if GMX_FAHCORE
+    /*code for alternate checkpointing scheme.  moved from top of loop over
+       steps */
+    fcRequestCheckPoint();
+    if (fcCheckPointParallel(cr->nodeid, NULL, 0) == 0)
+    {
+        gmx_fatal(3, __FILE__, __LINE__, "Checkpoint error on step %d\n", step);
+    }
+#endif /* end GMX_FAHCORE block */
+}
+
+void mdoutf_write_checkpoint(gmx_mdoutf_t                    of,
+                             FILE*                           fplog,
+                             const t_commrec*                cr,
+                             int64_t                         step,
+                             double                          t,
+                             t_state*                        state_global,
+                             ObservablesHistory*             observablesHistory,
+                             gmx::WriteCheckpointDataHolder* modularSimulatorCheckpointData)
+{
+    fflush_tng(of->tng);
+    fflush_tng(of->tng_low_prec);
+    /* Write the checkpoint file.
+     * When simulations share the state, an MPI barrier is applied before
+     * renaming old and new checkpoint files to minimize the risk of
+     * checkpoint files getting out of sync.
+     */
+    ivec one_ivec = { 1, 1, 1 };
+    write_checkpoint(of->fn_cpt, of->bKeepAndNumCPT, fplog, cr,
+                     DOMAINDECOMP(cr) ? cr->dd->numCells : one_ivec,
+                     DOMAINDECOMP(cr) ? cr->dd->nnodes : cr->nnodes, of->eIntegrator,
+                     of->simulation_part, of->bExpanded, of->elamstats, step, t, state_global,
+                     observablesHistory, *(of->mdModulesNotifier), modularSimulatorCheckpointData,
+                     of->simulationsShareState, of->mastersComm);
+}
+
+void mdoutf_write_to_trajectory_files(FILE*                           fplog,
+                                      const t_commrec*                cr,
+                                      gmx_mdoutf_t                    of,
+                                      int                             mdof_flags,
+                                      int                             natoms,
+                                      int64_t                         step,
+                                      double                          t,
+                                      t_state*                        state_local,
+                                      t_state*                        state_global,
+                                      ObservablesHistory*             observablesHistory,
+                                      gmx::ArrayRef<const gmx::RVec>  f_local,
+                                      gmx::WriteCheckpointDataHolder* modularSimulatorCheckpointData)
 {
-    rvec* f_global;
+    const rvec* f_global;
 
     if (DOMAINDECOMP(cr))
     {
@@ -279,19 +529,22 @@ void mdoutf_write_to_trajectory_files(FILE*                    fplog,
             if (mdof_flags & (MDOF_X | MDOF_X_COMPRESSED))
             {
                 auto globalXRef = MASTER(cr) ? state_global->x : gmx::ArrayRef<gmx::RVec>();
-                dd_collect_vec(cr->dd, state_local, state_local->x, globalXRef);
+                dd_collect_vec(cr->dd, state_local->ddp_count, state_local->ddp_count_cg_gl,
+                               state_local->cg_gl, state_local->x, globalXRef);
             }
             if (mdof_flags & MDOF_V)
             {
                 auto globalVRef = MASTER(cr) ? state_global->v : gmx::ArrayRef<gmx::RVec>();
-                dd_collect_vec(cr->dd, state_local, state_local->v, globalVRef);
+                dd_collect_vec(cr->dd, state_local->ddp_count, state_local->ddp_count_cg_gl,
+                               state_local->cg_gl, state_local->v, globalVRef);
             }
         }
         f_global = of->f_global;
         if (mdof_flags & MDOF_F)
         {
-            dd_collect_vec(cr->dd, state_local, f_local,
-                           gmx::arrayRefFromArray(reinterpret_cast<gmx::RVec*>(f_global), f_local.size()));
+            dd_collect_vec(
+                    cr->dd, state_local->ddp_count, state_local->ddp_count_cg_gl, state_local->cg_gl, f_local,
+                    gmx::arrayRefFromArray(reinterpret_cast<gmx::RVec*>(of->f_global), f_local.size()));
         }
     }
     else
@@ -306,20 +559,8 @@ void mdoutf_write_to_trajectory_files(FILE*                    fplog,
     {
         if (mdof_flags & MDOF_CPT)
         {
-            fflush_tng(of->tng);
-            fflush_tng(of->tng_low_prec);
-            /* Write the checkpoint file.
-             * When simulations share the state, an MPI barrier is applied before
-             * renaming old and new checkpoint files to minimize the risk of
-             * checkpoint files getting out of sync.
-             */
-            ivec one_ivec = { 1, 1, 1 };
-            write_checkpoint(of->fn_cpt, of->bKeepAndNumCPT, fplog, cr,
-                             DOMAINDECOMP(cr) ? cr->dd->nc : one_ivec,
-                             DOMAINDECOMP(cr) ? cr->dd->nnodes : cr->nnodes, of->eIntegrator,
-                             of->simulation_part, of->bExpanded, of->elamstats, step, t,
-                             state_global, observablesHistory, *(of->mdModulesNotifier),
-                             of->simulationsShareState, of->mpiCommMasters);
+            mdoutf_write_checkpoint(of, fplog, cr, step, t, state_global, observablesHistory,
+                                    modularSimulatorCheckpointData);
         }
 
         if (mdof_flags & (MDOF_X | MDOF_V | MDOF_F))
index 1eca3f9a354a0a99683b912fd9d53fd4f672ad7e..88ef9068bf4457ca672759cae6ed4cbf16fef290 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -58,6 +59,7 @@ enum class StartingBehavior;
 class IMDOutputProvider;
 struct MdModulesNotifier;
 struct MdrunOptions;
+class WriteCheckpointDataHolder;
 } // namespace gmx
 
 typedef struct gmx_mdoutf* gmx_mdoutf_t;
@@ -75,7 +77,7 @@ gmx_mdoutf_t init_mdoutf(FILE*                         fplog,
                          gmx::IMDOutputProvider*       outputProvider,
                          const gmx::MdModulesNotifier& mdModulesNotifier,
                          const t_inputrec*             ir,
-                         gmx_mtop_t*                   mtop,
+                         const gmx_mtop_t*             mtop,
                          const gmx_output_env_t*       oenv,
                          gmx_wallcycle_t               wcycle,
                          gmx::StartingBehavior         startingBehavior,
@@ -108,29 +110,64 @@ void done_mdoutf(gmx_mdoutf_t of);
  * the master node only when necessary. Without domain decomposition
  * only data from state_local is used and state_global is ignored.
  *
- * \param[in] fplog              File handler to log file.
- * \param[in] cr                 Communication record.
- * \param[in] of                 File handler to trajectory file.
- * \param[in] mdof_flags         Flags indicating what data is written.
- * \param[in] natoms             The total number of atoms in the system.
- * \param[in] step               The current time step.
- * \param[in] t                  The current time.
- * \param[in] state_local        Pointer to the local state object.
- * \param[in] state_global       Pointer to the global state object.
- * \param[in] observablesHistory Pointer to the ObservableHistory object.
- * \param[in] f_local            The local forces.
+ * Note that the mdoutf_write_checkpoint function below allows to
+ * write only the checkpoint file, and does no data gathering.
+ * Except for the modular simulator checkpointing,  the
+ * mdoutf_write_to_trajectory_files function is used for
+ * _all trajectory / checkpoint writing_.
+ *
+ * \param[in] fplog                           File handler to log file.
+ * \param[in] cr                              Communication record.
+ * \param[in] of                              File handler to trajectory file.
+ * \param[in] mdof_flags                      Flags indicating what data is written.
+ * \param[in] natoms                          The total number of atoms in the system.
+ * \param[in] step                            The current time step.
+ * \param[in] t                               The current time.
+ * \param[in] state_local                     Pointer to the local state object.
+ * \param[in] state_global                    Pointer to the global state object.
+ * \param[in] observablesHistory              Pointer to the ObservableHistory object.
+ * \param[in] f_local                         The local forces.
+ * \param[in] modularSimulatorCheckpointData  CheckpointData object used by modular simulator.
+ */
+void mdoutf_write_to_trajectory_files(FILE*                          fplog,
+                                      const t_commrec*               cr,
+                                      gmx_mdoutf_t                   of,
+                                      int                            mdof_flags,
+                                      int                            natoms,
+                                      int64_t                        step,
+                                      double                         t,
+                                      t_state*                       state_local,
+                                      t_state*                       state_global,
+                                      ObservablesHistory*            observablesHistory,
+                                      gmx::ArrayRef<const gmx::RVec> f_local,
+                                      gmx::WriteCheckpointDataHolder* modularSimulatorCheckpointData);
+
+/*! \brief Routine allowing to write checkpoint files directly.
+ *
+ * Unlike mdoutf_write_to_trajectory_files, this will only write a checkpoint file,
+ * and it will not gather the state over the different ranks.
+ *
+ * This should only be called if no other trajectory file writing is needed, and
+ * the global state has all required information. Currently, this is only used by
+ * the modular checkpointing facility.
+ *
+ * \param[in] of                              File handler to trajectory file.
+ * \param[in] fplog                           File handler to log file.
+ * \param[in] cr                              Communication record.
+ * \param[in] step                            The current time step.
+ * \param[in] t                               The current time.
+ * \param[in] state_global                    Pointer to the global state object.
+ * \param[in] observablesHistory              Pointer to the ObservableHistory object.
+ * \param[in] modularSimulatorCheckpointData  CheckpointData object used by modular simulator.
  */
-void mdoutf_write_to_trajectory_files(FILE*                    fplog,
-                                      const t_commrec*         cr,
-                                      gmx_mdoutf_t             of,
-                                      int                      mdof_flags,
-                                      int                      natoms,
-                                      int64_t                  step,
-                                      double                   t,
-                                      t_state*                 state_local,
-                                      t_state*                 state_global,
-                                      ObservablesHistory*      observablesHistory,
-                                      gmx::ArrayRef<gmx::RVec> f_local);
+void mdoutf_write_checkpoint(gmx_mdoutf_t                    of,
+                             FILE*                           fplog,
+                             const t_commrec*                cr,
+                             int64_t                         step,
+                             double                          t,
+                             t_state*                        state_global,
+                             ObservablesHistory*             observablesHistory,
+                             gmx::WriteCheckpointDataHolder* modularSimulatorCheckpointData);
 
 /*! \brief Get the output interval of box size of uncompressed TNG output.
  * Returns 0 if no uncompressed TNG file is open.
index aa6ebecd6c7754d981f049e9064b1c9d786e5875..a32c758b39007ef8f5859777cb48878a95bf0a99 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -1311,7 +1311,7 @@ gmx_membed_t* init_membed(FILE*          fplog,
         /* remove overlapping lipids and water from the membrane box*/
         /*mark molecules to be removed*/
         snew(pbc, 1);
-        set_pbc(pbc, inputrec->ePBC, state->box);
+        set_pbc(pbc, inputrec->pbcType, state->box);
 
         snew(rm_p, 1);
         lip_rm = gen_rm_list(rm_p, ins_at, rest_at, pbc, mtop, state->x.rvec_array(), mem_p,
@@ -1373,7 +1373,7 @@ gmx_membed_t* init_membed(FILE*          fplog,
 
         // Re-establish the invariants of the derived values within
         // mtop.
-        gmx_mtop_finalize(mtop);
+        mtop->finalize();
 
         if (ftp2bSet(efTOP, nfile, fnm))
         {
index e210ea11aff136d72edc922796419fe58802c7de..656993a85d1ff8f9136639680ffd624015c66f1c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index db14315e70b945f2d25470d01f10efc57e7ada22..e9f7f0c9735036ad769dc4acedd1e4477ce4c4dd 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -179,7 +180,7 @@ void get_nsgrid_boundaries(int           nboundeddim,
                 bdens0     = (*gr0)[d];
             }
             /* Check for a DD cell not at a higher edge */
-            if (dd != nullptr && gr1 != nullptr && dd->ci[d] < dd->nc[d] - 1)
+            if (dd != nullptr && gr1 != nullptr && dd->ci[d] < dd->numCells[d] - 1)
             {
                 grid_x1[d] = (*gr1)[d];
                 bdens1     = (*gr1)[d];
@@ -238,7 +239,7 @@ static void set_grid_sizes(matrix              box,
         grid->cell_offset[i] = izones_x0[i];
         size                 = izones_size[i];
 
-        bDD = (dd != nullptr) && (dd->nc[i] > 1);
+        bDD = (dd != nullptr) && (dd->numCells[i] > 1);
         if (!bDD)
         {
             bDDRect = FALSE;
@@ -363,9 +364,9 @@ t_grid* init_grid(FILE* fplog, t_forcerec* fr)
 
     snew(grid, 1);
 
-    grid->npbcdim = ePBC2npbcdim(fr->ePBC);
+    grid->npbcdim = numPbcDimensions(fr->pbcType);
 
-    if (fr->ePBC == epbcXY && fr->nwall == 2)
+    if (fr->pbcType == PbcType::XY && fr->nwall == 2)
     {
         grid->nboundeddim = 3;
     }
index c93de7f1051e1d0f321699dce161a9e4a25f73a5..a0a288dbeedcc0833aad70c13582581063d9ea9e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -233,7 +234,7 @@ void count_bonded_distances(const gmx_mtop_t& mtop, const t_inputrec& ir, double
         }
         if (bExcl)
         {
-            ndtot_c += molb.nmol * (molt->excls.nra - molt->atoms.nr) / 2.;
+            ndtot_c += molb.nmol * (molt->excls.numElements() - molt->atoms.nr) / 2.;
         }
     }
 
diff --git a/src/gromacs/mdlib/qm_gamess.cpp b/src/gromacs/mdlib/qm_gamess.cpp
deleted file mode 100644 (file)
index 697cea3..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#include "gmxpre.h"
-
-#include "qm_gamess.h"
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <cmath>
-
-#include "gromacs/fileio/confio.h"
-#include "gromacs/gmxlib/network.h"
-#include "gromacs/gmxlib/nrnb.h"
-#include "gromacs/math/units.h"
-#include "gromacs/math/vec.h"
-#include "gromacs/mdlib/qmmm.h"
-#include "gromacs/mdtypes/commrec.h"
-#include "gromacs/mdtypes/forcerec.h"
-#include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/utility/fatalerror.h"
-#include "gromacs/utility/smalloc.h"
-
-// When not built in a configuration with QMMM support, much of this
-// code is unreachable by design. Tell clang not to warn about it.
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmissing-noreturn"
-
-/* QMMM sub routines */
-/* mopac interface routines */
-
-
-static void F77_FUNC(inigms, IMIGMS)();
-
-static void F77_FUNC(grads, GRADS)(const int*  nrqmat,
-                                   real*       qmcrd,
-                                   const int*  nrmmat,
-                                   const real* mmchrg,
-                                   real*       mmcrd,
-                                   real*       qmgrad,
-                                   real*       mmgrad,
-                                   real*       energy);
-
-#if !GMX_QMMM_GAMESS
-// Stub definitions to make compilation succeed when not configured
-// for GAMESS support. In that case, the module gives a fatal error
-// when the initialization function is called, so there is no need to
-// issue fatal errors here, because that introduces problems with
-// tools suggesting and prohibiting noreturn attributes.
-
-void F77_FUNC(inigms, IMIGMS)(){};
-// NOLINTNEXTLINE(readability-named-parameter)
-void F77_FUNC(grads, GRADS)(const int*, real*, const int*, const real*, real*, real*, real*, real*){};
-#endif
-
-void init_gamess(const t_commrec* cr, t_QMrec* qm, t_MMrec* mm)
-{
-    /* it works hopelessly complicated :-)
-     * first a file is written. Then the standard gamess input/output
-     * routine is called (no system()!) to set up all fortran arrays.
-     * this routine writes a punch file, like in a normal gamess run.
-     * via this punch file the other games routines, needed for gradient
-     * and energy evaluations are called. This setup works fine for
-     * dynamics simulations. 7-6-2002 (London)
-     */
-    int   i, j;
-    FILE* out;
-    char  periodic_system[37][3] = { "XX", "H ", "He", "Li", "Be", "B ", "C ", "N ", "O ", "F ",
-                                    "Ne", "Na", "Mg", "Al", "Si", "P ", "S ", "Cl", "Ar", "K ",
-                                    "Ca", "Sc", "Ti", "V ", "Cr", "Mn", "Fe", "Co", "Ni", "Cu",
-                                    "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr" };
-    if (!GMX_QMMM_GAMESS)
-    {
-        gmx_fatal(FARGS,
-                  "Cannot call GAMESS unless linked against it. Use cmake "
-                  "-DGMX_QMMM_PROGRAM=GAMESS, and ensure that linking will work correctly.");
-    }
-
-    if (PAR(cr))
-    {
-
-        if (MASTER(cr))
-        {
-            out = fopen("FOR009", "w");
-            /* of these options I am not completely sure....  the overall
-             * preformance on more than 4 cpu's is rather poor at the moment.
-             */
-            fprintf(out, "memory 48000000\nPARALLEL IOMODE SCREENED\n");
-            fprintf(out, "ELEC %d\nMULT %d\nSUPER ON\nNOSYM\nGEOMETRY ANGSTROM\n", qm->nelectrons,
-                    qm->multiplicity);
-            for (i = 0; i < qm->nrQMatoms; i++)
-            {
-#ifdef DOUBLE
-                fprintf(out, "%10.7lf  %10.7lf  %10.7lf  %5.3lf  %2s\n", i / 2., i / 3., i / 4.,
-                        qm->atomicnumberQM[i] * 1.0, periodic_system[qm->atomicnumberQM[i]]);
-#else
-                fprintf(out, "%10.7f  %10.7f  %10.7f  %5.3f  %2s\n", i / 2., i / 3., i / 4.,
-                        qm->atomicnumberQM[i] * 1.0, periodic_system[qm->atomicnumberQM[i]]);
-#endif
-            }
-            if (mm->nrMMatoms)
-            {
-                for (j = i; j < i + 2; j++)
-                {
-#ifdef DOUBLE
-                    fprintf(out, "%10.7lf  %10.7lf  %10.7lf  %5.3lf  BQ\n", j / 5., j / 6., j / 7., 1.0);
-#else
-                    fprintf(out, "%10.7f  %10.7f  %10.7f  %5.3f  BQ\n", j / 5., j / 6., j / 7., 2.0);
-#endif
-                }
-            }
-            fprintf(out, "END\nBASIS %s\nRUNTYPE GRADIENT\nSCFTYPE %s\n",
-                    eQMbasis_names[qm->QMbasis], eQMmethod_names[qm->QMmethod]); /* see enum.h */
-            fclose(out);
-        }
-        gmx_barrier(cr);
-        F77_FUNC(inigms, IMIGMS)();
-    }
-    else /* normal serial run */
-
-    {
-        out = fopen("FOR009", "w");
-        /* of these options I am not completely sure....  the overall
-         * preformance on more than 4 cpu's is rather poor at the moment.
-         */
-        fprintf(out, "ELEC %d\nMULT %d\nSUPER ON\nNOSYM\nGEOMETRY ANGSTROM\n", qm->nelectrons,
-                qm->multiplicity);
-        for (i = 0; i < qm->nrQMatoms; i++)
-        {
-#ifdef DOUBLE
-            fprintf(out, "%10.7lf  %10.7lf  %10.7lf  %5.3lf  %2s\n", i / 2., i / 3., i / 4.,
-                    qm->atomicnumberQM[i] * 1.0, periodic_system[qm->atomicnumberQM[i]]);
-#else
-            fprintf(out, "%10.7f  %10.7f  %10.7f  %5.3f  %2s\n", i / 2., i / 3., i / 4.,
-                    qm->atomicnumberQM[i] * 1.0, periodic_system[qm->atomicnumberQM[i]]);
-#endif
-        }
-        if (mm->nrMMatoms)
-        {
-            for (j = i; j < i + 2; j++)
-            {
-#ifdef DOUBLE
-                fprintf(out, "%10.7lf  %10.7lf  %10.7lf  %5.3lf  BQ\n", j / 5., j / 6., j / 7., 1.0);
-#else
-                fprintf(out, "%10.7f  %10.7f  %10.7f  %5.3f  BQ\n", j / 5., j / 6., j / 7., 2.0);
-#endif
-            }
-        }
-        fprintf(out, "END\nBASIS %s\nRUNTYPE GRADIENT\nSCFTYPE %s\n", eQMbasis_names[qm->QMbasis],
-                eQMmethod_names[qm->QMmethod]); /* see enum.h */
-        F77_FUNC(inigms, IMIGMS)();
-    }
-}
-
-real call_gamess(const t_QMrec* qm, const t_MMrec* mm, rvec f[], rvec fshift[])
-{
-    /* do the actual QMMM calculation using GAMESS-UK. In this
-     * implementation (3-2001) a system call is made to the GAMESS-UK
-     * binary. Now we are working to get the electron integral, SCF, and
-     * gradient routines linked directly
-     */
-    int  i, j;
-    real QMener = 0.0, *qmgrad, *mmgrad, *mmcrd, *qmcrd, energy = 0;
-
-    snew(qmcrd, 3 * (qm->nrQMatoms));
-    snew(mmcrd, 3 * (mm->nrMMatoms));
-    snew(qmgrad, 3 * (qm->nrQMatoms));
-    snew(mmgrad, 3 * (mm->nrMMatoms));
-
-    /* copy the data from qr into the arrays that are going to be used
-     * in the fortran routines of gamess
-     */
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            qmcrd[DIM * i + j] = 1 / BOHR2NM * qm->xQM[i][j];
-        }
-    }
-    for (i = 0; i < mm->nrMMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            mmcrd[DIM * i + j] = 1 / BOHR2NM * mm->xMM[i][j];
-        }
-    }
-    for (i = 0; i < 3 * qm->nrQMatoms; i += 3)
-    {
-        fprintf(stderr, "%8.5f, %8.5f, %8.5f\n", qmcrd[i], qmcrd[i + 1], qmcrd[i + 2]);
-    }
-
-    F77_FUNC(grads, GRADS)
-    (&qm->nrQMatoms, qmcrd, &mm->nrMMatoms, mm->MMcharges, mmcrd, qmgrad, mmgrad, &energy);
-
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            f[i][j]      = HARTREE_BOHR2MD * qmgrad[3 * i + j];
-            fshift[i][j] = HARTREE_BOHR2MD * qmgrad[3 * i + j];
-        }
-    }
-    for (i = 0; i < mm->nrMMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            f[i][j]      = HARTREE_BOHR2MD * mmgrad[3 * i + j];
-            fshift[i][j] = HARTREE_BOHR2MD * mmgrad[3 * i + j];
-        }
-    }
-    /* convert a.u to kJ/mol */
-    QMener = energy * HARTREE2KJ * AVOGADRO;
-    return (QMener);
-}
-
-#pragma GCC diagnostic pop
diff --git a/src/gromacs/mdlib/qm_gamess.h b/src/gromacs/mdlib/qm_gamess.h
deleted file mode 100644 (file)
index 81c2b4c..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#ifndef GMX_MDLIB_QMGAMESS_H
-#define GMX_MDLIB_QMGAMESS_H
-
-#include "gromacs/mdlib/qmmm.h"
-
-/*! \brief
- * Initialize gamess datastructures.
- *
- * \param[in] cr Commrec datastructure.
- * \param[in] qm QM forcerec.
- * \param[in] mm MM part of forcerec.
- */
-void init_gamess(const t_commrec* cr, t_QMrec* qm, t_MMrec* mm);
-
-/*! \brief
- * Run calculation with Gamess.
- *
- * \param[in] qm QM part of forcerec.
- * \param[in] mm MM part of forcerec.
- * \param[in] f  Force vector.
- * \param[in] fshift Force shift vector.
- */
-real call_gamess(const t_QMrec* qm, const t_MMrec* mm, rvec f[], rvec fshift[]);
-
-
-#endif
diff --git a/src/gromacs/mdlib/qm_gaussian.cpp b/src/gromacs/mdlib/qm_gaussian.cpp
deleted file mode 100644 (file)
index b9885bf..0000000
+++ /dev/null
@@ -1,902 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#include "gmxpre.h"
-
-#include "qm_gaussian.h"
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <cmath>
-
-#include "gromacs/fileio/confio.h"
-#include "gromacs/gmxlib/network.h"
-#include "gromacs/gmxlib/nrnb.h"
-#include "gromacs/math/units.h"
-#include "gromacs/math/vec.h"
-#include "gromacs/mdlib/force.h"
-#include "gromacs/mdlib/forcerec.h"
-#include "gromacs/mdlib/qmmm.h"
-#include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/utility/cstringutil.h"
-#include "gromacs/utility/fatalerror.h"
-#include "gromacs/utility/smalloc.h"
-
-// When not built in a configuration with QMMM support, much of this
-// code is unreachable by design. Tell clang not to warn about it.
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmissing-noreturn"
-
-/* TODO: this should be made thread-safe */
-
-/* Gaussian interface routines */
-
-void init_gaussian(t_QMrec* qm)
-{
-    ivec  basissets[eQMbasisNR] = { { 0, 3, 0 }, { 0, 3, 0 }, /*added for double sto-3g entry in names.c*/
-                                   { 5, 0, 0 }, { 5, 0, 1 }, { 5, 0, 11 }, { 5, 6, 0 },
-                                   { 1, 6, 0 }, { 1, 6, 1 }, { 1, 6, 11 }, { 4, 6, 0 } };
-    char* buf                   = nullptr;
-    int   i;
-
-    if (!GMX_QMMM_GAUSSIAN)
-    {
-        gmx_fatal(FARGS,
-                  "Cannot call GAUSSIAN unless linked against it. Use cmake "
-                  "-DGMX_QMMM_PROGRAM=GAUSSIAN, and ensure that linking will work correctly.");
-    }
-
-    /* using the ivec above to convert the basis read form the mdp file
-     * in a human readable format into some numbers for the gaussian
-     * route. This is necessary as we are using non standard routes to
-     * do SH.
-     */
-
-    /* per layer we make a new subdir for integral file, checkpoint
-     * files and such. These dirs are stored in the QMrec for
-     * convenience
-     */
-
-
-    if (!qm->nQMcpus) /* this we do only once per layer
-                       * as we call g01 externally
-                       */
-
-    {
-        for (i = 0; i < DIM; i++)
-        {
-            qm->SHbasis[i] = basissets[qm->QMbasis][i];
-        }
-
-        /* init gradually switching on of the SA */
-        qm->SAstep = 0;
-        /* we read the number of cpus and environment from the environment
-         * if set.
-         */
-        buf = getenv("GMX_QM_GAUSSIAN_NCPUS");
-        if (buf)
-        {
-            sscanf(buf, "%d", &qm->nQMcpus);
-        }
-        else
-        {
-            qm->nQMcpus = 1;
-        }
-        fprintf(stderr, "number of CPUs for gaussian = %d\n", qm->nQMcpus);
-        buf = getenv("GMX_QM_GAUSSIAN_MEMORY");
-        if (buf)
-        {
-            sscanf(buf, "%d", &qm->QMmem);
-        }
-        else
-        {
-            qm->QMmem = 50000000;
-        }
-        fprintf(stderr, "memory for gaussian = %d\n", qm->QMmem);
-        buf = getenv("GMX_QM_ACCURACY");
-        if (buf)
-        {
-            sscanf(buf, "%d", &qm->accuracy);
-        }
-        else
-        {
-            qm->accuracy = 8;
-        }
-        fprintf(stderr, "accuracy in l510 = %d\n", qm->accuracy);
-
-        buf = getenv("GMX_QM_CPMCSCF");
-        if (buf)
-        {
-            sscanf(buf, "%d", &i);
-            qm->cpmcscf = (i != 0);
-        }
-        else
-        {
-            qm->cpmcscf = FALSE;
-        }
-        if (qm->cpmcscf)
-        {
-            fprintf(stderr, "using cp-mcscf in l1003\n");
-        }
-        else
-        {
-            fprintf(stderr, "NOT using cp-mcscf in l1003\n");
-        }
-        buf = getenv("GMX_QM_SA_STEP");
-        if (buf)
-        {
-            sscanf(buf, "%d", &qm->SAstep);
-        }
-        else
-        {
-            /* init gradually switching on of the SA */
-            qm->SAstep = 0;
-        }
-        /* we read the number of cpus and environment from the environment
-         * if set.
-         */
-        fprintf(stderr, "Level of SA at start = %d\n", qm->SAstep);
-        /* gaussian settings on the system */
-        buf = getenv("GMX_QM_GAUSS_DIR");
-
-        if (buf)
-        {
-            qm->gauss_dir = gmx_strdup(buf);
-        }
-        else
-        {
-            gmx_fatal(FARGS, "no $GMX_QM_GAUSS_DIR, check gaussian manual\n");
-        }
-
-        buf = getenv("GMX_QM_GAUSS_EXE");
-        if (buf)
-        {
-            qm->gauss_exe = gmx_strdup(buf);
-        }
-        else
-        {
-            gmx_fatal(FARGS, "no $GMX_QM_GAUSS_EXE set, check gaussian manual\n");
-        }
-        buf = getenv("GMX_QM_MODIFIED_LINKS_DIR");
-        if (buf)
-        {
-            qm->devel_dir = gmx_strdup(buf);
-        }
-        else
-        {
-            gmx_fatal(FARGS,
-                      "no $GMX_QM_MODIFIED_LINKS_DIR, this is were the modified links reside.\n");
-        }
-
-        /*  if(fr->bRF){*/
-        /* reactionfield, file is needed using gaussian */
-        /*    rffile=fopen("rf.dat","w");*/
-        /*   fprintf(rffile,"%f %f\n",fr->epsilon_r,fr->rcoulomb/BOHR2NM);*/
-        /* fclose(rffile);*/
-        /*  }*/
-    }
-    fprintf(stderr, "gaussian initialised...\n");
-}
-
-
-static void write_gaussian_SH_input(int step, gmx_bool swap, const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm)
-{
-    int        i;
-    gmx_bool   bSA;
-    FILE*      out;
-    t_QMMMrec* QMMMrec;
-    QMMMrec = fr->qr;
-    bSA     = (qm->SAstep > 0);
-
-    out = fopen("input.com", "w");
-    /* write the route */
-    fprintf(out, "%s", "%scr=input\n");
-    fprintf(out, "%s", "%rwf=input\n");
-    fprintf(out, "%s", "%int=input\n");
-    fprintf(out, "%s", "%d2e=input\n");
-    /*  if(step)
-     *   fprintf(out,"%s","%nosave\n");
-     */
-    fprintf(out, "%s", "%chk=input\n");
-    fprintf(out, "%s%d\n", "%mem=", qm->QMmem);
-    fprintf(out, "%s%3d\n", "%nprocshare=", qm->nQMcpus);
-
-    /* use the versions of
-     * l301 that computes the interaction between MM and QM atoms.
-     * l510 that can punch the CI coefficients
-     * l701 that can do gradients on MM atoms
-     */
-
-    /* local version */
-    fprintf(out, "%s%s%s", "%subst l510 ", qm->devel_dir, "/l510\n");
-    fprintf(out, "%s%s%s", "%subst l301 ", qm->devel_dir, "/l301\n");
-    fprintf(out, "%s%s%s", "%subst l701 ", qm->devel_dir, "/l701\n");
-
-    fprintf(out, "%s%s%s", "%subst l1003 ", qm->devel_dir, "/l1003\n");
-    fprintf(out, "%s%s%s", "%subst l9999 ", qm->devel_dir, "/l9999\n");
-    /* print the nonstandard route
-     */
-    fprintf(out, "%s", "#P nonstd\n 1/18=10,20=1,38=1/1;\n");
-    fprintf(out, "%s", " 2/9=110,15=1,17=6,18=5,40=1/2;\n");
-    if (mm->nrMMatoms)
-    {
-        fprintf(out, " 3/5=%d,6=%d,7=%d,25=1,32=1,43=1,94=-2/1,2,3;\n", qm->SHbasis[0],
-                qm->SHbasis[1], qm->SHbasis[2]); /*basisset stuff */
-    }
-    else
-    {
-        fprintf(out, " 3/5=%d,6=%d,7=%d,25=1,32=1,43=0,94=-2/1,2,3;\n", qm->SHbasis[0],
-                qm->SHbasis[1], qm->SHbasis[2]); /*basisset stuff */
-    }
-    /* development */
-    if (step + 1) /* fetch initial guess from check point file */
-    {             /* hack, to alyays read from chk file!!!!! */
-        fprintf(out, "%s%d,%s%d%s", " 4/5=1,7=6,17=", qm->CASelectrons, "18=", qm->CASorbitals,
-                "/1,5;\n");
-    }
-    else /* generate the first checkpoint file */
-    {
-        fprintf(out, "%s%d,%s%d%s", " 4/5=0,7=6,17=", qm->CASelectrons, "18=", qm->CASorbitals,
-                "/1,5;\n");
-    }
-    /* the rest of the input depends on where the system is on the PES
-     */
-    if (swap && bSA) /* make a slide to the other surface */
-    {
-        if (qm->CASorbitals > 6) /* use direct and no full diag */
-        {
-            fprintf(out, " 5/5=2,16=-2,17=10000000,28=2,32=2,38=6,97=100/10;\n");
-        }
-        else
-        {
-            if (qm->cpmcscf)
-            {
-                fprintf(out, " 5/5=2,6=%d,17=31000200,28=2,32=2,38=6,97=100/10;\n", qm->accuracy);
-                if (mm->nrMMatoms > 0)
-                {
-                    fprintf(out, " 7/7=1,16=-2,30=1/1;\n");
-                }
-                fprintf(out, " 11/31=1,42=1,45=1/1;\n");
-                fprintf(out, " 10/6=1,10=700006,28=2,29=1,31=1,97=100/3;\n");
-                fprintf(out, " 7/30=1/16;\n 99/10=4/99;\n");
-            }
-            else
-            {
-                fprintf(out, " 5/5=2,6=%d,17=11000000,28=2,32=2,38=6,97=100/10;\n", qm->accuracy);
-                fprintf(out, " 7/7=1,16=-2,30=1/1,2,3,16;\n 99/10=4/99;\n");
-            }
-        }
-    }
-    else if (bSA) /* do a "state-averaged" CAS calculation */
-    {
-        if (qm->CASorbitals > 6) /* no full diag */
-        {
-            fprintf(out, " 5/5=2,16=-2,17=10000000,28=2,32=2,38=6/10;\n");
-        }
-        else
-        {
-            if (qm->cpmcscf)
-            {
-                fprintf(out, " 5/5=2,6=%d,17=31000200,28=2,32=2,38=6/10;\n", qm->accuracy);
-                if (mm->nrMMatoms > 0)
-                {
-                    fprintf(out, " 7/7=1,16=-2,30=1/1;\n");
-                }
-                fprintf(out, " 11/31=1,42=1,45=1/1;\n");
-                fprintf(out, " 10/6=1,10=700006,28=2,29=1,31=1/3;\n");
-                fprintf(out, " 7/30=1/16;\n 99/10=4/99;\n");
-            }
-            else
-            {
-                fprintf(out, " 5/5=2,6=%d,17=11000000,28=2,32=2,38=6/10;\n", qm->accuracy);
-                fprintf(out, " 7/7=1,16=-2,30=1/1,2,3,16;\n 99/10=4/99;\n");
-            }
-        }
-    }
-    else if (swap) /* do a "swapped" CAS calculation */
-    {
-        if (qm->CASorbitals > 6)
-        {
-            fprintf(out, " 5/5=2,16=-2,17=0,28=2,32=2,38=6,97=100/10;\n");
-        }
-        else
-        {
-            fprintf(out, " 5/5=2,6=%d,17=1000000,28=2,32=2,38=6,97=100/10;\n", qm->accuracy);
-        }
-        fprintf(out, " 7/7=1,16=-2,30=1/1,2,3,16;\n 99/10=4/99;\n");
-    }
-    else /* do a "normal" CAS calculation */
-    {
-        if (qm->CASorbitals > 6)
-        {
-            fprintf(out, " 5/5=2,16=-2,17=0,28=2,32=2,38=6/10;\n");
-        }
-        else
-        {
-            fprintf(out, " 5/5=2,6=%d,17=1000000,28=2,32=2,38=6/10;\n", qm->accuracy);
-        }
-        fprintf(out, " 7/7=1,16=-2,30=1/1,2,3,16;\n 99/10=4/99;\n");
-    }
-    fprintf(out, "\ninput-file generated by gromacs\n\n");
-    fprintf(out, "%2d%2d\n", qm->QMcharge, qm->multiplicity);
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        fprintf(out, "%3d %10.7f  %10.7f  %10.7f\n", qm->atomicnumberQM[i],
-                qm->xQM[i][XX] / BOHR2NM, qm->xQM[i][YY] / BOHR2NM, qm->xQM[i][ZZ] / BOHR2NM);
-    }
-    /* MM point charge data */
-    if (QMMMrec->QMMMscheme != eQMMMschemeoniom && mm->nrMMatoms)
-    {
-        fprintf(out, "\n");
-        for (i = 0; i < mm->nrMMatoms; i++)
-        {
-            fprintf(out, "%10.7f  %10.7f  %10.7f %8.4f\n", mm->xMM[i][XX] / BOHR2NM,
-                    mm->xMM[i][YY] / BOHR2NM, mm->xMM[i][ZZ] / BOHR2NM, mm->MMcharges[i]);
-        }
-    }
-    if (bSA) /* put the SA coefficients at the end of the file */
-    {
-        fprintf(out, "\n%10.8f %10.8f\n", qm->SAstep * 0.5 / qm->SAsteps, 1 - qm->SAstep * 0.5 / qm->SAsteps);
-        fprintf(stderr, "State Averaging level = %d/%d\n", qm->SAstep, qm->SAsteps);
-    }
-    fprintf(out, "\n");
-    fclose(out);
-} /* write_gaussian_SH_input */
-
-static void write_gaussian_input(int step, const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm)
-{
-    int        i;
-    t_QMMMrec* QMMMrec;
-    FILE*      out;
-
-    QMMMrec = fr->qr;
-    out     = fopen("input.com", "w");
-    /* write the route */
-
-    if (qm->QMmethod >= eQMmethodRHF)
-    {
-        fprintf(out, "%s", "%chk=input\n");
-    }
-    else
-    {
-        fprintf(out, "%s", "%chk=se\n");
-    }
-    if (qm->nQMcpus > 1)
-    {
-        fprintf(out, "%s%3d\n", "%nprocshare=", qm->nQMcpus);
-    }
-    fprintf(out, "%s%d\n", "%mem=", qm->QMmem);
-    fprintf(out, "%s%s%s", "%subst l701 ", qm->devel_dir, "/l701\n");
-    fprintf(out, "%s%s%s", "%subst l301 ", qm->devel_dir, "/l301\n");
-    fprintf(out, "%s%s%s", "%subst l9999 ", qm->devel_dir, "/l9999\n");
-    if (step)
-    {
-        fprintf(out, "%s", "#T ");
-    }
-    else
-    {
-        fprintf(out, "%s", "#P ");
-    }
-    if (qm->QMmethod == eQMmethodB3LYPLAN)
-    {
-        fprintf(out, " %s", "B3LYP/GEN Pseudo=Read");
-    }
-    else
-    {
-        fprintf(out, " %s", eQMmethod_names[qm->QMmethod]);
-
-        if (qm->QMmethod >= eQMmethodRHF)
-        {
-            if (qm->QMmethod == eQMmethodCASSCF)
-            {
-                /* in case of cas, how many electrons and orbitals do we need?
-                 */
-                fprintf(out, "(%d,%d)", qm->CASelectrons, qm->CASorbitals);
-            }
-            fprintf(out, "/%s", eQMbasis_names[qm->QMbasis]);
-        }
-    }
-    if (QMMMrec->QMMMscheme == eQMMMschemenormal && mm->nrMMatoms)
-    {
-        fprintf(out, " %s", "Charge ");
-    }
-    if (step || qm->QMmethod == eQMmethodCASSCF)
-    {
-        /* fetch guess from checkpoint file, always for CASSCF */
-        fprintf(out, "%s", " guess=read");
-    }
-    fprintf(out, "\nNosymm units=bohr\n");
-
-    fprintf(out, "FORCE Punch=(Derivatives) ");
-    fprintf(out, "iop(3/33=1)\n\n");
-    fprintf(out, "input-file generated by gromacs\n\n");
-    fprintf(out, "%2d%2d\n", qm->QMcharge, qm->multiplicity);
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        fprintf(out, "%3d %10.7f  %10.7f  %10.7f\n", qm->atomicnumberQM[i],
-                qm->xQM[i][XX] / BOHR2NM, qm->xQM[i][YY] / BOHR2NM, qm->xQM[i][ZZ] / BOHR2NM);
-    }
-
-    /* Pseudo Potential and ECP are included here if selected (MEthod suffix LAN) */
-    if (qm->QMmethod == eQMmethodB3LYPLAN)
-    {
-        fprintf(out, "\n");
-        for (i = 0; i < qm->nrQMatoms; i++)
-        {
-            if (qm->atomicnumberQM[i] < 21)
-            {
-                fprintf(out, "%d ", i + 1);
-            }
-        }
-        fprintf(out, "\n%s\n****\n", eQMbasis_names[qm->QMbasis]);
-
-        for (i = 0; i < qm->nrQMatoms; i++)
-        {
-            if (qm->atomicnumberQM[i] > 21)
-            {
-                fprintf(out, "%d ", i + 1);
-            }
-        }
-        fprintf(out, "\n%s\n****\n\n", "lanl2dz");
-
-        for (i = 0; i < qm->nrQMatoms; i++)
-        {
-            if (qm->atomicnumberQM[i] > 21)
-            {
-                fprintf(out, "%d ", i + 1);
-            }
-        }
-        fprintf(out, "\n%s\n", "lanl2dz");
-    }
-
-
-    /* MM point charge data */
-    if (QMMMrec->QMMMscheme != eQMMMschemeoniom && mm->nrMMatoms)
-    {
-        fprintf(stderr, "nr mm atoms in gaussian.c = %d\n", mm->nrMMatoms);
-        fprintf(out, "\n");
-        for (i = 0; i < mm->nrMMatoms; i++)
-        {
-            fprintf(out, "%10.7f  %10.7f  %10.7f %8.4f\n", mm->xMM[i][XX] / BOHR2NM,
-                    mm->xMM[i][YY] / BOHR2NM, mm->xMM[i][ZZ] / BOHR2NM, mm->MMcharges[i]);
-        }
-    }
-    fprintf(out, "\n");
-
-
-    fclose(out);
-
-} /* write_gaussian_input */
-
-static real read_gaussian_output(rvec QMgrad[], rvec MMgrad[], t_QMrec* qm, t_MMrec* mm)
-{
-    int   i;
-    char  buf[300];
-    real  QMener;
-    FILE* in;
-
-    in = fopen("fort.7", "r");
-
-    /* (There was additional content in the file in case
-     *    of QM optimizations / transition state search,
-     *    which was removed.
-     */
-    /* the next line is the energy and in the case of CAS, the energy
-     * difference between the two states.
-     */
-    if (nullptr == fgets(buf, 300, in))
-    {
-        gmx_fatal(FARGS, "Error reading Gaussian output");
-    }
-
-#if GMX_DOUBLE
-    sscanf(buf, "%lf\n", &QMener);
-#else
-    sscanf(buf, "%f\n", &QMener);
-#endif
-    /* next lines contain the gradients of the QM atoms */
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        if (nullptr == fgets(buf, 300, in))
-        {
-            gmx_fatal(FARGS, "Error reading Gaussian output");
-        }
-#if GMX_DOUBLE
-        sscanf(buf, "%lf %lf %lf\n", &QMgrad[i][XX], &QMgrad[i][YY], &QMgrad[i][ZZ]);
-#else
-        sscanf(buf, "%f %f %f\n", &QMgrad[i][XX], &QMgrad[i][YY], &QMgrad[i][ZZ]);
-#endif
-    }
-    /* the next lines are the gradients of the MM atoms */
-    if (qm->QMmethod >= eQMmethodRHF)
-    {
-        for (i = 0; i < mm->nrMMatoms; i++)
-        {
-            if (nullptr == fgets(buf, 300, in))
-            {
-                gmx_fatal(FARGS, "Error reading Gaussian output");
-            }
-#if GMX_DOUBLE
-            sscanf(buf, "%lf %lf %lf\n", &MMgrad[i][XX], &MMgrad[i][YY], &MMgrad[i][ZZ]);
-#else
-            sscanf(buf, "%f %f %f\n", &MMgrad[i][XX], &MMgrad[i][YY], &MMgrad[i][ZZ]);
-#endif
-        }
-    }
-    fclose(in);
-    return (QMener);
-}
-
-static real read_gaussian_SH_output(rvec QMgrad[], rvec MMgrad[], int step, t_QMrec* qm, t_MMrec* mm)
-{
-    int   i;
-    char  buf[300];
-    real  QMener, DeltaE;
-    FILE* in;
-
-    in = fopen("fort.7", "r");
-    /* first line is the energy and in the case of CAS, the energy
-     * difference between the two states.
-     */
-    if (nullptr == fgets(buf, 300, in))
-    {
-        gmx_fatal(FARGS, "Error reading Gaussian output");
-    }
-
-#if GMX_DOUBLE
-    sscanf(buf, "%lf %lf\n", &QMener, &DeltaE);
-#else
-    sscanf(buf, "%f %f\n", &QMener, &DeltaE);
-#endif
-
-    /* switch on/off the State Averaging */
-
-    if (DeltaE > qm->SAoff)
-    {
-        if (qm->SAstep > 0)
-        {
-            qm->SAstep--;
-        }
-    }
-    else if (DeltaE < qm->SAon || (qm->SAstep > 0))
-    {
-        if (qm->SAstep < qm->SAsteps)
-        {
-            qm->SAstep++;
-        }
-    }
-
-    /* for debugging: */
-    fprintf(stderr, "Gap = %5f,SA = %3d\n", DeltaE, static_cast<int>(qm->SAstep > 0));
-    /* next lines contain the gradients of the QM atoms */
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        if (nullptr == fgets(buf, 300, in))
-        {
-            gmx_fatal(FARGS, "Error reading Gaussian output");
-        }
-
-#if GMX_DOUBLE
-        sscanf(buf, "%lf %lf %lf\n", &QMgrad[i][XX], &QMgrad[i][YY], &QMgrad[i][ZZ]);
-#else
-        sscanf(buf, "%f %f %f\n", &QMgrad[i][XX], &QMgrad[i][YY], &QMgrad[i][ZZ]);
-#endif
-    }
-    /* the next lines, are the gradients of the MM atoms */
-
-    for (i = 0; i < mm->nrMMatoms; i++)
-    {
-        if (nullptr == fgets(buf, 300, in))
-        {
-            gmx_fatal(FARGS, "Error reading Gaussian output");
-        }
-#if GMX_DOUBLE
-        sscanf(buf, "%lf %lf %lf\n", &MMgrad[i][XX], &MMgrad[i][YY], &MMgrad[i][ZZ]);
-#else
-        sscanf(buf, "%f %f %f\n", &MMgrad[i][XX], &MMgrad[i][YY], &MMgrad[i][ZZ]);
-#endif
-    }
-
-    /* the next line contains the two CI eigenvector elements */
-    if (nullptr == fgets(buf, 300, in))
-    {
-        gmx_fatal(FARGS, "Error reading Gaussian output");
-    }
-    if (!step)
-    {
-        sscanf(buf, "%d", &qm->CIdim);
-        snew(qm->CIvec1, qm->CIdim);
-        snew(qm->CIvec1old, qm->CIdim);
-        snew(qm->CIvec2, qm->CIdim);
-        snew(qm->CIvec2old, qm->CIdim);
-    }
-    else
-    {
-        /* before reading in the new current CI vectors, copy the current
-         * CI vector into the old one.
-         */
-        for (i = 0; i < qm->CIdim; i++)
-        {
-            qm->CIvec1old[i] = qm->CIvec1[i];
-            qm->CIvec2old[i] = qm->CIvec2[i];
-        }
-    }
-    /* first vector */
-    for (i = 0; i < qm->CIdim; i++)
-    {
-        if (nullptr == fgets(buf, 300, in))
-        {
-            gmx_fatal(FARGS, "Error reading Gaussian output");
-        }
-#if GMX_DOUBLE
-        sscanf(buf, "%lf\n", &qm->CIvec1[i]);
-#else
-        sscanf(buf, "%f\n", &qm->CIvec1[i]);
-#endif
-    }
-    /* second vector */
-    for (i = 0; i < qm->CIdim; i++)
-    {
-        if (nullptr == fgets(buf, 300, in))
-        {
-            gmx_fatal(FARGS, "Error reading Gaussian output");
-        }
-#if GMX_DOUBLE
-        sscanf(buf, "%lf\n", &qm->CIvec2[i]);
-#else
-        sscanf(buf, "%f\n", &qm->CIvec2[i]);
-#endif
-    }
-    fclose(in);
-    return (QMener);
-}
-
-static real inproduct(const real* a, const real* b, int n)
-{
-    int  i;
-    real dot = 0.0;
-
-    /* computes the inner product between two vectors (a.b), both of
-     * which have length n.
-     */
-    for (i = 0; i < n; i++)
-    {
-        dot += a[i] * b[i];
-    }
-    return (dot);
-}
-
-static int hop(int step, t_QMrec* qm)
-{
-    int  swap = 0;
-    real d11 = 0.0, d12 = 0.0, d21 = 0.0, d22 = 0.0;
-
-    /* calculates the inproduct between the current Ci vector and the
-     * previous CI vector. A diabatic hop will be made if d12 and d21
-     * are much bigger than d11 and d22. In that case hop returns true,
-     * otherwise it returns false.
-     */
-    if (step) /* only go on if more than one step has been done */
-    {
-        d11 = inproduct(qm->CIvec1, qm->CIvec1old, qm->CIdim);
-        d12 = inproduct(qm->CIvec1, qm->CIvec2old, qm->CIdim);
-        d21 = inproduct(qm->CIvec2, qm->CIvec1old, qm->CIdim);
-        d22 = inproduct(qm->CIvec2, qm->CIvec2old, qm->CIdim);
-    }
-    fprintf(stderr, "-------------------\n");
-    fprintf(stderr, "d11 = %13.8f\n", d11);
-    fprintf(stderr, "d12 = %13.8f\n", d12);
-    fprintf(stderr, "d21 = %13.8f\n", d21);
-    fprintf(stderr, "d22 = %13.8f\n", d22);
-    fprintf(stderr, "-------------------\n");
-
-    if ((fabs(d12) > 0.5) && (fabs(d21) > 0.5))
-    {
-        swap = 1;
-    }
-
-    return (swap);
-}
-
-static void do_gaussian(int step, char* exe)
-{
-    char buf[STRLEN];
-
-    /* make the call to the gaussian binary through system()
-     * The location of the binary will be picked up from the
-     * environment using getenv().
-     */
-    if (step) /* hack to prevent long inputfiles */
-    {
-        sprintf(buf, "%s < %s > %s", exe, "input.com", "input.log");
-    }
-    else
-    {
-        sprintf(buf, "%s < %s > %s", exe, "input.com", "input.log");
-    }
-    fprintf(stderr, "Calling '%s'\n", buf);
-    if (system(buf) != 0)
-    {
-        gmx_fatal(FARGS, "Call to '%s' failed\n", buf);
-    }
-}
-
-real call_gaussian(const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm, rvec f[], rvec fshift[])
-{
-    /* normal gaussian jobs */
-    static int step = 0;
-    int        i, j;
-    real       QMener = 0.0;
-    rvec *     QMgrad, *MMgrad;
-    char*      exe;
-
-    snew(exe, 30);
-    sprintf(exe, "%s/%s", qm->gauss_dir, qm->gauss_exe);
-    snew(QMgrad, qm->nrQMatoms);
-    snew(MMgrad, mm->nrMMatoms);
-
-    write_gaussian_input(step, fr, qm, mm);
-    do_gaussian(step, exe);
-    QMener = read_gaussian_output(QMgrad, MMgrad, qm, mm);
-    /* put the QMMM forces in the force array and to the fshift
-     */
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            f[i][j]      = HARTREE_BOHR2MD * QMgrad[i][j];
-            fshift[i][j] = HARTREE_BOHR2MD * QMgrad[i][j];
-        }
-    }
-    for (i = 0; i < mm->nrMMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            f[i + qm->nrQMatoms][j]      = HARTREE_BOHR2MD * MMgrad[i][j];
-            fshift[i + qm->nrQMatoms][j] = HARTREE_BOHR2MD * MMgrad[i][j];
-        }
-    }
-    QMener = QMener * HARTREE2KJ * AVOGADRO;
-    step++;
-    free(exe);
-    return (QMener);
-
-} /* call_gaussian */
-
-real call_gaussian_SH(const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm, rvec f[], rvec fshift[])
-{
-    /* a gaussian call routine intended for doing diabatic surface
-     * "sliding". See the manual for the theoretical background of this
-     * TSH method.
-     */
-    static int      step = 0;
-    int             state, i, j;
-    real            QMener  = 0.0;
-    static gmx_bool swapped = FALSE; /* handle for identifying the current PES */
-    gmx_bool        swap    = FALSE; /* the actual swap */
-    rvec *          QMgrad, *MMgrad;
-    char*           buf;
-    char*           exe;
-
-    snew(exe, 30);
-    sprintf(exe, "%s/%s", qm->gauss_dir, qm->gauss_exe);
-    /* hack to do ground state simulations */
-    if (!step)
-    {
-        snew(buf, 20);
-        buf = getenv("GMX_QM_GROUND_STATE");
-        if (buf)
-        {
-            sscanf(buf, "%d", &state);
-        }
-        else
-        {
-            state = 2;
-        }
-        if (state == 1)
-        {
-            swapped = TRUE;
-        }
-    }
-    /* end of hack */
-
-
-    /* copy the QMMMrec pointer */
-    snew(QMgrad, qm->nrQMatoms);
-    snew(MMgrad, mm->nrMMatoms);
-    /* at step 0 there should be no SA */
-    /*  if(!step)
-     * qr->bSA=FALSE;*/
-    /* temporray set to step + 1, since there is a chk start */
-    write_gaussian_SH_input(step, swapped, fr, qm, mm);
-
-    do_gaussian(step, exe);
-    QMener = read_gaussian_SH_output(QMgrad, MMgrad, step, qm, mm);
-
-    /* check for a surface hop. Only possible if we were already state
-     * averaging.
-     */
-    if (qm->SAstep > 0)
-    {
-        if (!swapped)
-        {
-            swap    = ((step != 0) && (hop(step, qm) != 0));
-            swapped = swap;
-        }
-        else /* already on the other surface, so check if we go back */
-        {
-            swap    = ((step != 0) && (hop(step, qm) != 0));
-            swapped = !swap; /* so swapped shoud be false again */
-        }
-        if (swap) /* change surface, so do another call */
-        {
-            write_gaussian_SH_input(step, swapped, fr, qm, mm);
-            do_gaussian(step, exe);
-            QMener = read_gaussian_SH_output(QMgrad, MMgrad, step, qm, mm);
-        }
-    }
-    /* add the QMMM forces to the gmx force array and fshift
-     */
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            f[i][j]      = HARTREE_BOHR2MD * QMgrad[i][j];
-            fshift[i][j] = HARTREE_BOHR2MD * QMgrad[i][j];
-        }
-    }
-    for (i = 0; i < mm->nrMMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            f[i + qm->nrQMatoms][j]      = HARTREE_BOHR2MD * MMgrad[i][j];
-            fshift[i + qm->nrQMatoms][j] = HARTREE_BOHR2MD * MMgrad[i][j];
-        }
-    }
-    QMener = QMener * HARTREE2KJ * AVOGADRO;
-    fprintf(stderr, "step %5d, SA = %5d, swap = %5d\n", step, static_cast<int>(qm->SAstep > 0),
-            static_cast<int>(swapped));
-    step++;
-    free(exe);
-    return (QMener);
-
-} /* call_gaussian_SH */
-
-#pragma GCC diagnostic pop
diff --git a/src/gromacs/mdlib/qm_gaussian.h b/src/gromacs/mdlib/qm_gaussian.h
deleted file mode 100644 (file)
index 1933fc7..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#ifndef GMX_MDLIB_QMGAUSSIAN_H
-#define GMX_MDLIB_QMGAUSSIAN_H
-
-#include "gromacs/math/vectypes.h"
-#include "gromacs/mdlib/qmmm.h"
-
-struct t_forcerec;
-
-/*! \brief
- * Initialize gaussian datastructures.
- *
- * \param[in] qm QM forcerec.
- */
-void init_gaussian(t_QMrec* qm);
-
-/*! \brief
- * Call gaussian to do qm calculation.
- *
- * \param[in] fr Global forcerec.
- * \param[in] qm QM part of forcerec.
- * \param[in] mm mm part of forcerec.
- * \param[in] f  force vector.
- * \param[in] fshift shift of force vector.
- */
-real call_gaussian(const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm, rvec f[], rvec fshift[]);
-
-/*! \brief
- * Call gaussian SH(?) to do qm calculation.
- *
- * \param[in] fr Global forcerec.
- * \param[in] qm QM part of forcerec.
- * \param[in] mm mm part of forcerec.
- * \param[in] f  force vector.
- * \param[in] fshift shift of force vector.
- */
-real call_gaussian_SH(const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm, rvec f[], rvec fshift[]);
-
-#endif
diff --git a/src/gromacs/mdlib/qm_mopac.cpp b/src/gromacs/mdlib/qm_mopac.cpp
deleted file mode 100644 (file)
index 1b1d929..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#include "gmxpre.h"
-
-#include "qm_mopac.h"
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <cmath>
-
-#include "gromacs/fileio/confio.h"
-#include "gromacs/gmxlib/network.h"
-#include "gromacs/gmxlib/nrnb.h"
-#include "gromacs/math/units.h"
-#include "gromacs/math/vec.h"
-#include "gromacs/mdlib/qmmm.h"
-#include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/utility/fatalerror.h"
-#include "gromacs/utility/smalloc.h"
-
-
-// When not built in a configuration with QMMM support, much of this
-// code is unreachable by design. Tell clang not to warn about it.
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmissing-noreturn"
-
-#if GMX_QMMM_MOPAC
-/* mopac interface routines */
-void F77_FUNC(domldt, DOMLDT)(int* nrqmat, int labels[], char keywords[]);
-
-void F77_FUNC(domop, DOMOP)(int*    nrqmat,
-                            double  qmcrd[],
-                            int*    nrmmat,
-                            double  mmchrg[],
-                            double  mmcrd[],
-                            double  qmgrad[],
-                            double  mmgrad[],
-                            double* energy,
-                            double  qmcharges[]);
-
-#else /* GMX_QMMM_MOPAC */
-// Stub definitions to make compilation succeed when not configured
-// for MOPAC support. In that case, the module gives a fatal error
-// when the initialization function is called, so there is no need to
-// issue fatal errors here, because that introduces problems with
-// tools suggesting and prohibiting noreturn attributes.
-
-static void F77_FUNC(domldt, DOMLDT)(int* /*unused*/, int /*unused*/[], char /*unused*/[]) {}
-
-static void F77_FUNC(domop, DOMOP)(int* /*unused*/,
-                                   double /*unused*/[],
-                                   int* /*unused*/,
-                                   double /*unused*/[],
-                                   double /*unused*/[],
-                                   double /*unused*/[],
-                                   double /*unused*/[],
-                                   double* /*unused*/,
-                                   double /*unused*/[])
-{
-}
-
-#endif
-
-
-void init_mopac(t_QMrec* qm)
-{
-    /* initializes the mopac routines ans sets up the semiempirical
-     * computation by calling moldat(). The inline mopac routines can
-     * only perform gradient operations. If one would like to optimize a
-     * structure or find a transition state at PM3 level, gaussian is
-     * used instead.
-     */
-    char* keywords;
-
-    if (!GMX_QMMM_MOPAC)
-    {
-        gmx_fatal(FARGS,
-                  "Cannot call MOPAC unless linked against it. Use cmake -DGMX_QMMM_PROGRAM=MOPAC, "
-                  "and ensure that linking will work correctly.");
-    }
-
-    snew(keywords, 240);
-
-    if (!qm->bSH) /* if rerun then grad should not be done! */
-    {
-        sprintf(keywords, "PRECISE GEO-OK CHARGE=%d GRAD MMOK ANALYT %s\n", qm->QMcharge,
-                eQMmethod_names[qm->QMmethod]);
-    }
-    else
-    {
-        sprintf(keywords, "PRECISE GEO-OK CHARGE=%d SINGLET GRAD %s C.I.=(%d,%d) root=2 MECI \n",
-                qm->QMcharge, eQMmethod_names[qm->QMmethod], qm->CASorbitals, qm->CASelectrons / 2);
-    }
-    F77_FUNC(domldt, DOMLDT)(&qm->nrQMatoms, qm->atomicnumberQM, keywords);
-    fprintf(stderr, "keywords are: %s\n", keywords);
-    free(keywords);
-
-} /* init_mopac */
-
-real call_mopac(t_QMrec* qm, t_MMrec* mm, rvec f[], rvec fshift[])
-{
-    /* do the actual QMMM calculation using directly linked mopac subroutines
-     */
-    double /* always double as the MOPAC routines are always compiled in
-              double precission! */
-            *qmcrd  = nullptr,
-            *qmchrg = nullptr, *mmcrd = nullptr, *mmchrg = nullptr, *qmgrad, *mmgrad = nullptr,
-            energy = 0;
-    int  i, j;
-    real QMener = 0.0;
-    snew(qmcrd, 3 * (qm->nrQMatoms));
-    snew(qmgrad, 3 * (qm->nrQMatoms));
-    /* copy the data from qr into the arrays that are going to be used
-     * in the fortran routines of MOPAC
-     */
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            qmcrd[3 * i + j] = static_cast<double>(qm->xQM[i][j]) * 10;
-        }
-    }
-    if (mm->nrMMatoms)
-    {
-        /* later we will add the point charges here. There are some
-         * conceptual problems with semi-empirical QM in combination with
-         * point charges that we need to solve first....
-         */
-        gmx_fatal(FARGS,
-                  "At present only ONIOM is allowed in combination"
-                  " with MOPAC QM subroutines\n");
-    }
-    else
-    {
-        /* now compute the energy and the gradients.
-         */
-
-        snew(qmchrg, qm->nrQMatoms);
-        F77_FUNC(domop, DOMOP)
-        (&qm->nrQMatoms, qmcrd, &mm->nrMMatoms, mmchrg, mmcrd, qmgrad, mmgrad, &energy, qmchrg);
-        /* add the gradients to the f[] array, and also to the fshift[].
-         * the mopac gradients are in kCal/angstrom.
-         */
-        for (i = 0; i < qm->nrQMatoms; i++)
-        {
-            for (j = 0; j < DIM; j++)
-            {
-                f[i][j]      = static_cast<real>(10) * CAL2JOULE * qmgrad[3 * i + j];
-                fshift[i][j] = static_cast<real>(10) * CAL2JOULE * qmgrad[3 * i + j];
-            }
-        }
-        QMener = static_cast<real> CAL2JOULE * energy;
-        /* do we do something with the mulliken charges?? */
-
-        free(qmchrg);
-    }
-    free(qmgrad);
-    free(qmcrd);
-    return (QMener);
-}
-
-real call_mopac_SH(t_QMrec* qm, t_MMrec* mm, rvec f[], rvec fshift[])
-{
-    /* do the actual SH QMMM calculation using directly linked mopac
-       subroutines */
-
-    double /* always double as the MOPAC routines are always compiled in
-              double precission! */
-            *qmcrd  = nullptr,
-            *qmchrg = nullptr, *mmcrd = nullptr, *mmchrg = nullptr, *qmgrad, *mmgrad = nullptr,
-            energy = 0;
-    int  i, j;
-    real QMener = 0.0;
-
-    snew(qmcrd, 3 * (qm->nrQMatoms));
-    snew(qmgrad, 3 * (qm->nrQMatoms));
-    /* copy the data from qr into the arrays that are going to be used
-     * in the fortran routines of MOPAC
-     */
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            qmcrd[3 * i + j] = static_cast<double>(qm->xQM[i][j]) * 10;
-        }
-    }
-    if (mm->nrMMatoms)
-    {
-        /* later we will add the point charges here. There are some
-         * conceptual problems with semi-empirical QM in combination with
-         * point charges that we need to solve first....
-         */
-        gmx_fatal(FARGS, "At present only ONIOM is allowed in combination with MOPAC\n");
-    }
-    else
-    {
-        /* now compute the energy and the gradients.
-         */
-        snew(qmchrg, qm->nrQMatoms);
-
-        F77_FUNC(domop, DOMOP)
-        (&qm->nrQMatoms, qmcrd, &mm->nrMMatoms, mmchrg, mmcrd, qmgrad, mmgrad, &energy, qmchrg);
-        /* add the gradients to the f[] array, and also to the fshift[].
-         * the mopac gradients are in kCal/angstrom.
-         */
-        for (i = 0; i < qm->nrQMatoms; i++)
-        {
-            for (j = 0; j < DIM; j++)
-            {
-                f[i][j]      = static_cast<real>(10) * CAL2JOULE * qmgrad[3 * i + j];
-                fshift[i][j] = static_cast<real>(10) * CAL2JOULE * qmgrad[3 * i + j];
-            }
-        }
-        QMener = static_cast<real> CAL2JOULE * energy;
-    }
-    free(qmgrad);
-    free(qmcrd);
-    return (QMener);
-} /* call_mopac_SH */
-
-#pragma GCC diagnostic pop
diff --git a/src/gromacs/mdlib/qm_mopac.h b/src/gromacs/mdlib/qm_mopac.h
deleted file mode 100644 (file)
index 0f6eca8..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#ifndef GMX_MDLIB_QMMOPAC_H
-#define GMX_MDLIB_QMMOPAC_H
-
-#include "gromacs/mdlib/qmmm.h"
-
-/*! \brief
- * Initialize mopac datastructures.
- *
- * \param[in] qm QM forcerec.
- */
-void init_mopac(t_QMrec* qm);
-
-/*! \brief
- * Run calculation with MOPAC.
- *
- * \param[in] qm QM part of forcerec.
- * \param[in] mm MM part of forcerec.
- * \param[in] f  Force vector.
- * \param[in] fshift Force shift vector.
- */
-real call_mopac(t_QMrec* qm, t_MMrec* mm, rvec f[], rvec fshift[]);
-
-/*! \brief
- * Run surface-hopping calculation with MOPAC.
- *
- * \param[in] qm QM part of forcerec.
- * \param[in] mm MM part of forcerec.
- * \param[in] f  Force vector.
- * \param[in] fshift Force shift vector.
- */
-real call_mopac_SH(t_QMrec* qm, t_MMrec* mm, rvec f[], rvec fshift[]);
-
-#endif
diff --git a/src/gromacs/mdlib/qm_orca.cpp b/src/gromacs/mdlib/qm_orca.cpp
deleted file mode 100644 (file)
index 075d8d6..0000000
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#include "gmxpre.h"
-
-#include "qm_orca.h"
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include "gromacs/fileio/confio.h"
-#include "gromacs/gmxlib/network.h"
-#include "gromacs/gmxlib/nrnb.h"
-#include "gromacs/math/units.h"
-#include "gromacs/math/vec.h"
-#include "gromacs/mdlib/qmmm.h"
-#include "gromacs/mdtypes/forcerec.h"
-#include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/utility/fatalerror.h"
-#include "gromacs/utility/smalloc.h"
-
-// When not built in a configuration with QMMM support, much of this
-// code is unreachable by design. Tell clang not to warn about it.
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmissing-noreturn"
-
-/* ORCA interface routines */
-
-void init_orca(t_QMrec* qm)
-{
-    char* buf;
-    snew(buf, 200);
-
-    if (!GMX_QMMM_ORCA)
-    {
-        gmx_fatal(FARGS,
-                  "Cannot call ORCA unless linked against it. Use cmake -DGMX_QMMM_PROGRAM=ORCA, "
-                  "and ensure that linking will work correctly.");
-    }
-
-    /* ORCA settings on the system */
-    buf = getenv("GMX_QM_ORCA_BASENAME");
-    if (buf)
-    {
-        snew(qm->orca_basename, 200);
-        sscanf(buf, "%s", qm->orca_basename);
-    }
-    else
-    {
-        gmx_fatal(FARGS, "$GMX_QM_ORCA_BASENAME is not set\n");
-    }
-
-    /* ORCA directory on the system */
-    snew(buf, 200);
-    buf = getenv("GMX_ORCA_PATH");
-
-    if (buf)
-    {
-        snew(qm->orca_dir, 200);
-        sscanf(buf, "%s", qm->orca_dir);
-    }
-    else
-    {
-        gmx_fatal(FARGS, "$GMX_ORCA_PATH not set, check manual\n");
-    }
-
-    fprintf(stderr, "Setting ORCA path to: %s...\n", qm->orca_dir);
-    fprintf(stderr, "ORCA initialised...\n\n");
-    /* since we append the output to the BASENAME.out file,
-       we should delete an existent old out-file here. */
-    sprintf(buf, "%s.out", qm->orca_basename);
-    remove(buf);
-}
-
-
-static void write_orca_input(const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm)
-{
-    int        i;
-    t_QMMMrec* QMMMrec;
-    FILE *     out, *pcFile, *addInputFile;
-    char *     buf, *orcaInput, *addInputFilename, *pcFilename;
-
-    QMMMrec = fr->qr;
-
-    /* write the first part of the input-file */
-    snew(orcaInput, 200);
-    sprintf(orcaInput, "%s.inp", qm->orca_basename);
-    out = fopen(orcaInput, "w");
-
-    snew(addInputFilename, 200);
-    sprintf(addInputFilename, "%s.ORCAINFO", qm->orca_basename);
-    addInputFile = fopen(addInputFilename, "r");
-
-    fprintf(out, "#input-file generated by GROMACS\n");
-
-    fprintf(out, "!EnGrad TightSCF\n");
-
-    /* here we include the insertion of the additional orca-input */
-    snew(buf, 200);
-    if (addInputFile != nullptr)
-    {
-        while (!feof(addInputFile))
-        {
-            if (fgets(buf, 200, addInputFile) != nullptr)
-            {
-                fputs(buf, out);
-            }
-        }
-    }
-    else
-    {
-        gmx_fatal(FARGS, "No information on the calculation given in %s\n", addInputFilename);
-    }
-
-    fclose(addInputFile);
-
-    /* write charge and multiplicity */
-    fprintf(out, "*xyz %2d%2d\n", qm->QMcharge, qm->multiplicity);
-
-    /* write the QM coordinates */
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        int atomNr;
-        if (qm->atomicnumberQM[i] == 0)
-        {
-            atomNr = 1;
-        }
-        else
-        {
-            atomNr = qm->atomicnumberQM[i];
-        }
-        fprintf(out, "%3d %10.7f  %10.7f  %10.7f\n", atomNr, qm->xQM[i][XX] / 0.1,
-                qm->xQM[i][YY] / 0.1, qm->xQM[i][ZZ] / 0.1);
-    }
-    fprintf(out, "*\n");
-
-    /* write the MM point charge data */
-    if (QMMMrec->QMMMscheme != eQMMMschemeoniom && mm->nrMMatoms)
-    {
-        /* name of the point charge file */
-        snew(pcFilename, 200);
-        sprintf(pcFilename, "%s.pc", qm->orca_basename);
-        fprintf(out, "%s%s%s\n", "%pointcharges \"", pcFilename, "\"");
-        pcFile = fopen(pcFilename, "w");
-        fprintf(pcFile, "%d\n", mm->nrMMatoms);
-        for (i = 0; i < mm->nrMMatoms; i++)
-        {
-            fprintf(pcFile, "%8.4f %10.7f  %10.7f  %10.7f\n", mm->MMcharges[i],
-                    mm->xMM[i][XX] / 0.1, mm->xMM[i][YY] / 0.1, mm->xMM[i][ZZ] / 0.1);
-        }
-        fprintf(pcFile, "\n");
-        fclose(pcFile);
-    }
-    fprintf(out, "\n");
-
-    fclose(out);
-} /* write_orca_input */
-
-static real read_orca_output(rvec QMgrad[], rvec MMgrad[], const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm)
-{
-    int        i, j;
-    char       buf[300], orca_pcgradFilename[300], orca_engradFilename[300];
-    real       QMener;
-    FILE *     pcgrad, *engrad;
-    int        k;
-    t_QMMMrec* QMMMrec;
-    QMMMrec = fr->qr;
-
-    /* the energy and gradients for the QM part are stored in the engrad file
-     * and the gradients for the point charges are stored in the pc file.
-     */
-    sprintf(orca_engradFilename, "%s.engrad", qm->orca_basename);
-    engrad = fopen(orca_engradFilename, "r");
-    /* we read the energy and the gradient for the qm-atoms from the engrad file
-     */
-    /* we can skip the first seven lines
-     */
-    for (j = 0; j < 7; j++)
-    {
-        if (fgets(buf, 300, engrad) == nullptr)
-        {
-            gmx_fatal(FARGS, "Unexpected end of ORCA output");
-        }
-    }
-    /* now comes the energy
-     */
-    if (fgets(buf, 300, engrad) == nullptr)
-    {
-        gmx_fatal(FARGS, "Unexpected end of ORCA output");
-    }
-#if GMX_DOUBLE
-    sscanf(buf, "%lf\n", &QMener);
-#else
-    sscanf(buf, "%f\n", &QMener);
-#endif
-    /* we can skip the next three lines
-     */
-    for (j = 0; j < 3; j++)
-    {
-        if (fgets(buf, 300, engrad) == nullptr)
-        {
-            gmx_fatal(FARGS, "Unexpected end of ORCA output");
-        }
-    }
-    /* next lines contain the gradients of the QM atoms
-     * now comes the gradient, one value per line:
-     * (atom1 x \n atom1 y \n atom1 z \n atom2 x ...
-     */
-
-    for (i = 0; i < 3 * qm->nrQMatoms; i++)
-    {
-        k = i / 3;
-        if (fgets(buf, 300, engrad) == nullptr)
-        {
-            gmx_fatal(FARGS, "Unexpected end of ORCA output");
-        }
-#if GMX_DOUBLE
-        if (i % 3 == 0)
-        {
-            sscanf(buf, "%lf\n", &QMgrad[k][XX]);
-        }
-        else if (i % 3 == 1)
-        {
-            sscanf(buf, "%lf\n", &QMgrad[k][YY]);
-        }
-        else if (i % 3 == 2)
-        {
-            sscanf(buf, "%lf\n", &QMgrad[k][ZZ]);
-        }
-#else
-        if (i % 3 == 0)
-        {
-            sscanf(buf, "%f\n", &QMgrad[k][XX]);
-        }
-        else if (i % 3 == 1)
-        {
-            sscanf(buf, "%f\n", &QMgrad[k][YY]);
-        }
-        else if (i % 3 == 2)
-        {
-            sscanf(buf, "%f\n", &QMgrad[k][ZZ]);
-        }
-#endif
-    }
-    fclose(engrad);
-    /* write the MM point charge data
-     */
-    if (QMMMrec->QMMMscheme != eQMMMschemeoniom && mm->nrMMatoms)
-    {
-        sprintf(orca_pcgradFilename, "%s.pcgrad", qm->orca_basename);
-        pcgrad = fopen(orca_pcgradFilename, "r");
-
-        /* we read the gradient for the mm-atoms from the pcgrad file
-         */
-        /* we can skip the first line
-         */
-        if (fgets(buf, 300, pcgrad) == nullptr)
-        {
-            gmx_fatal(FARGS, "Unexpected end of ORCA output");
-        }
-        for (i = 0; i < mm->nrMMatoms; i++)
-        {
-            if (fgets(buf, 300, pcgrad) == nullptr)
-            {
-                gmx_fatal(FARGS, "Unexpected end of ORCA output");
-            }
-#if GMX_DOUBLE
-            sscanf(buf, "%lf%lf%lf\n", &MMgrad[i][XX], &MMgrad[i][YY], &MMgrad[i][ZZ]);
-#else
-            sscanf(buf, "%f%f%f\n", &MMgrad[i][XX], &MMgrad[i][YY], &MMgrad[i][ZZ]);
-#endif
-        }
-        fclose(pcgrad);
-    }
-    return (QMener);
-}
-
-static void do_orca(char* orca_dir, char* basename)
-{
-
-    /* make the call to the orca binary through system()
-     * The location of the binary is set through the
-     * environment.
-     */
-    char buf[100];
-    sprintf(buf, "%s/%s %s.inp >> %s.out", orca_dir, "orca", basename, basename);
-    fprintf(stderr, "Calling '%s'\n", buf);
-    if (system(buf) != 0)
-    {
-        gmx_fatal(FARGS, "Call to '%s' failed\n", buf);
-    }
-}
-
-real call_orca(const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm, rvec f[], rvec fshift[])
-{
-    /* normal orca jobs */
-    static int step = 0;
-    int        i, j;
-    real       QMener;
-    rvec *     QMgrad, *MMgrad;
-    char*      exe;
-
-    snew(exe, 30);
-    sprintf(exe, "%s", "orca");
-    snew(QMgrad, qm->nrQMatoms);
-    snew(MMgrad, mm->nrMMatoms);
-
-    write_orca_input(fr, qm, mm);
-    do_orca(qm->orca_dir, qm->orca_basename);
-    QMener = read_orca_output(QMgrad, MMgrad, fr, qm, mm);
-    /* put the QMMM forces in the force array and to the fshift
-     */
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            f[i][j]      = HARTREE_BOHR2MD * QMgrad[i][j];
-            fshift[i][j] = HARTREE_BOHR2MD * QMgrad[i][j];
-        }
-    }
-    for (i = 0; i < mm->nrMMatoms; i++)
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            f[i + qm->nrQMatoms][j]      = HARTREE_BOHR2MD * MMgrad[i][j];
-            fshift[i + qm->nrQMatoms][j] = HARTREE_BOHR2MD * MMgrad[i][j];
-        }
-    }
-    QMener = QMener * HARTREE2KJ * AVOGADRO;
-    step++;
-    free(exe);
-    return (QMener);
-} /* call_orca */
-
-/* end of orca sub routines */
-
-#pragma GCC diagnostic pop
diff --git a/src/gromacs/mdlib/qmmm.cpp b/src/gromacs/mdlib/qmmm.cpp
deleted file mode 100644 (file)
index 2482bf5..0000000
+++ /dev/null
@@ -1,953 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#include "gmxpre.h"
-
-#include "qmmm.h"
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include <algorithm>
-
-#include "gromacs/domdec/domdec_struct.h"
-#include "gromacs/fileio/confio.h"
-#include "gromacs/gmxlib/network.h"
-#include "gromacs/gmxlib/nrnb.h"
-#include "gromacs/math/functions.h"
-#include "gromacs/math/units.h"
-#include "gromacs/math/vec.h"
-#include "gromacs/mdlib/qm_gamess.h"
-#include "gromacs/mdlib/qm_gaussian.h"
-#include "gromacs/mdlib/qm_mopac.h"
-#include "gromacs/mdlib/qm_orca.h"
-#include "gromacs/mdtypes/commrec.h"
-#include "gromacs/mdtypes/forceoutput.h"
-#include "gromacs/mdtypes/forcerec.h"
-#include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/mdtypes/mdatom.h"
-#include "gromacs/mdtypes/nblist.h"
-#include "gromacs/pbcutil/ishift.h"
-#include "gromacs/pbcutil/pbc.h"
-#include "gromacs/topology/mtop_lookup.h"
-#include "gromacs/topology/mtop_util.h"
-#include "gromacs/topology/topology.h"
-#include "gromacs/utility/fatalerror.h"
-#include "gromacs/utility/smalloc.h"
-
-// When not built in a configuration with QMMM support, much of this
-// code is unreachable by design. Tell clang not to warn about it.
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunreachable-code"
-#pragma GCC diagnostic ignored "-Wmissing-noreturn"
-
-/* this struct and these comparison functions are needed for creating
- * a QMMM input for the QM routines from the QMMM neighbor list.
- */
-
-typedef struct
-{
-    int j;
-    int shift;
-} t_j_particle;
-
-static bool struct_comp(const t_j_particle& a, const t_j_particle& b)
-{
-    return a.j < b.j;
-}
-
-static real call_QMroutine(const t_commrec gmx_unused* cr,
-                           const t_forcerec gmx_unused* fr,
-                           t_QMrec gmx_unused* qm,
-                           t_MMrec gmx_unused* mm,
-                           rvec gmx_unused f[],
-                           rvec gmx_unused fshift[])
-{
-    /* makes a call to the requested QM routine (qm->QMmethod)
-     * Note that f is actually the gradient, i.e. -f
-     */
-    /* do a semi-empiprical calculation */
-
-    if (qm->QMmethod < eQMmethodRHF && !(mm->nrMMatoms))
-    {
-        if (GMX_QMMM_MOPAC)
-        {
-            if (qm->bSH)
-            {
-                return call_mopac_SH(qm, mm, f, fshift);
-            }
-            else
-            {
-                return call_mopac(qm, mm, f, fshift);
-            }
-        }
-        else
-        {
-            gmx_fatal(FARGS, "Semi-empirical QM only supported with Mopac.");
-        }
-    }
-    else
-    {
-        /* do an ab-initio calculation */
-        if (qm->bSH && qm->QMmethod == eQMmethodCASSCF)
-        {
-            if (GMX_QMMM_GAUSSIAN)
-            {
-                return call_gaussian_SH(fr, qm, mm, f, fshift);
-            }
-            else
-            {
-                gmx_fatal(FARGS, "Ab-initio Surface-hopping only supported with Gaussian.");
-            }
-        }
-        else
-        {
-            if (GMX_QMMM_GAMESS)
-            {
-                return call_gamess(qm, mm, f, fshift);
-            }
-            else if (GMX_QMMM_GAUSSIAN)
-            {
-                return call_gaussian(fr, qm, mm, f, fshift);
-            }
-            else if (GMX_QMMM_ORCA)
-            {
-                return call_orca(fr, qm, mm, f, fshift);
-            }
-            else
-            {
-                gmx_fatal(FARGS,
-                          "Ab-initio calculation only supported with Gamess, Gaussian or ORCA.");
-            }
-        }
-    }
-}
-
-static void init_QMroutine(const t_commrec gmx_unused* cr, t_QMrec gmx_unused* qm, t_MMrec gmx_unused* mm)
-{
-    /* makes a call to the requested QM routine (qm->QMmethod)
-     */
-    if (qm->QMmethod < eQMmethodRHF)
-    {
-        if (GMX_QMMM_MOPAC)
-        {
-            /* do a semi-empiprical calculation */
-            init_mopac(qm);
-        }
-        else
-        {
-            gmx_fatal(FARGS, "Semi-empirical QM only supported with Mopac.");
-        }
-    }
-    else
-    {
-        /* do an ab-initio calculation */
-        if (GMX_QMMM_GAMESS)
-        {
-            init_gamess(cr, qm, mm);
-        }
-        else if (GMX_QMMM_GAUSSIAN)
-        {
-            init_gaussian(qm);
-        }
-        else if (GMX_QMMM_ORCA)
-        {
-            init_orca(qm);
-        }
-        else
-        {
-            gmx_fatal(FARGS, "Ab-initio calculation only supported with Gamess, Gaussian or ORCA.");
-        }
-    }
-} /* init_QMroutine */
-
-static void update_QMMM_coord(const rvec* x, const t_forcerec* fr, t_QMrec* qm, t_MMrec* mm)
-{
-    /* shifts the QM and MM particles into the central box and stores
-     * these shifted coordinates in the coordinate arrays of the
-     * QMMMrec. These coordinates are passed on the QM subroutines.
-     */
-    int i;
-
-    /* shift the QM atoms into the central box
-     */
-    for (i = 0; i < qm->nrQMatoms; i++)
-    {
-        rvec_sub(x[qm->indexQM[i]], fr->shift_vec[qm->shiftQM[i]], qm->xQM[i]);
-    }
-    /* also shift the MM atoms into the central box, if any
-     */
-    for (i = 0; i < mm->nrMMatoms; i++)
-    {
-        rvec_sub(x[mm->indexMM[i]], fr->shift_vec[mm->shiftMM[i]], mm->xMM[i]);
-    }
-} /* update_QMMM_coord */
-
-/* end of QMMM subroutines */
-
-/* QMMM core routines */
-
-static t_QMrec* mk_QMrec()
-{
-    t_QMrec* qm;
-    snew(qm, 1);
-    return qm;
-} /* mk_QMrec */
-
-static t_MMrec* mk_MMrec()
-{
-    t_MMrec* mm;
-    snew(mm, 1);
-    return mm;
-} /* mk_MMrec */
-
-static void init_QMrec(int grpnr, t_QMrec* qm, int nr, const int* atomarray, const gmx_mtop_t* mtop, const t_inputrec* ir)
-{
-    /* fills the t_QMrec struct of QM group grpnr
-     */
-
-    qm->nrQMatoms = nr;
-    snew(qm->xQM, nr);
-    snew(qm->indexQM, nr);
-    snew(qm->shiftQM, nr); /* the shifts */
-    for (int i = 0; i < nr; i++)
-    {
-        qm->indexQM[i] = atomarray[i];
-    }
-
-    snew(qm->atomicnumberQM, nr);
-    int molb = 0;
-    for (int i = 0; i < qm->nrQMatoms; i++)
-    {
-        const t_atom& atom = mtopGetAtomParameters(mtop, qm->indexQM[i], &molb);
-        qm->nelectrons += mtop->atomtypes.atomnumber[atom.type];
-        qm->atomicnumberQM[i] = mtop->atomtypes.atomnumber[atom.type];
-    }
-
-    qm->QMcharge     = ir->opts.QMcharge[grpnr];
-    qm->multiplicity = ir->opts.QMmult[grpnr];
-    qm->nelectrons -= ir->opts.QMcharge[grpnr];
-
-    qm->QMmethod = ir->opts.QMmethod[grpnr];
-    qm->QMbasis  = ir->opts.QMbasis[grpnr];
-    /* trajectory surface hopping setup (Gaussian only) */
-    qm->bSH          = ir->opts.bSH[grpnr];
-    qm->CASorbitals  = ir->opts.CASorbitals[grpnr];
-    qm->CASelectrons = ir->opts.CASelectrons[grpnr];
-    qm->SAsteps      = ir->opts.SAsteps[grpnr];
-    qm->SAon         = ir->opts.SAon[grpnr];
-    qm->SAoff        = ir->opts.SAoff[grpnr];
-    /* hack to prevent gaussian from reinitializing all the time */
-    qm->nQMcpus = 0; /* number of CPU's to be used by g01, is set
-                      * upon initializing gaussian
-                      * (init_gaussian()
-                      */
-    /* print the current layer to allow users to check their input */
-    fprintf(stderr, "Layer %d\nnr of QM atoms %d\n", grpnr, nr);
-    fprintf(stderr, "QMlevel: %s/%s\n\n", eQMmethod_names[qm->QMmethod], eQMbasis_names[qm->QMbasis]);
-} /* init_QMrec */
-
-static t_QMrec* copy_QMrec(t_QMrec* qm)
-{
-    /* copies the contents of qm into a new t_QMrec struct */
-    t_QMrec* qmcopy;
-    int      i;
-
-    qmcopy            = mk_QMrec();
-    qmcopy->nrQMatoms = qm->nrQMatoms;
-    snew(qmcopy->xQM, qmcopy->nrQMatoms);
-    snew(qmcopy->indexQM, qmcopy->nrQMatoms);
-    snew(qmcopy->atomicnumberQM, qm->nrQMatoms);
-    snew(qmcopy->shiftQM, qmcopy->nrQMatoms); /* the shifts */
-    for (i = 0; i < qmcopy->nrQMatoms; i++)
-    {
-        qmcopy->shiftQM[i]        = qm->shiftQM[i];
-        qmcopy->indexQM[i]        = qm->indexQM[i];
-        qmcopy->atomicnumberQM[i] = qm->atomicnumberQM[i];
-    }
-    qmcopy->nelectrons   = qm->nelectrons;
-    qmcopy->multiplicity = qm->multiplicity;
-    qmcopy->QMcharge     = qm->QMcharge;
-    qmcopy->nelectrons   = qm->nelectrons;
-    qmcopy->QMmethod     = qm->QMmethod;
-    qmcopy->QMbasis      = qm->QMbasis;
-    /* trajectory surface hopping setup (Gaussian only) */
-    qmcopy->bSH          = qm->bSH;
-    qmcopy->CASorbitals  = qm->CASorbitals;
-    qmcopy->CASelectrons = qm->CASelectrons;
-    qmcopy->SAsteps      = qm->SAsteps;
-    qmcopy->SAon         = qm->SAon;
-    qmcopy->SAoff        = qm->SAoff;
-
-    /* Gaussian init. variables */
-    qmcopy->nQMcpus = qm->nQMcpus;
-    for (i = 0; i < DIM; i++)
-    {
-        qmcopy->SHbasis[i] = qm->SHbasis[i];
-    }
-    qmcopy->QMmem    = qm->QMmem;
-    qmcopy->accuracy = qm->accuracy;
-    qmcopy->cpmcscf  = qm->cpmcscf;
-    qmcopy->SAstep   = qm->SAstep;
-
-    return (qmcopy);
-
-} /*copy_QMrec */
-
-#if GMX_QMMM
-
-t_QMMMrec* mk_QMMMrec()
-{
-    t_QMMMrec* qr;
-
-    snew(qr, 1);
-
-    return qr;
-
-} /* mk_QMMMrec */
-
-#else /* GMX_QMMM */
-
-t_QMMMrec* mk_QMMMrec()
-{
-    gmx_incons("Compiled without QMMM");
-} /* mk_QMMMrec */
-#endif
-
-std::vector<int> qmmmAtomIndices(const t_inputrec& ir, const gmx_mtop_t& mtop)
-{
-    const int               numQmmmGroups = ir.opts.ngQM;
-    const SimulationGroups& groups        = mtop.groups;
-    std::vector<int>        qmmmAtoms;
-    for (int i = 0; i < numQmmmGroups; i++)
-    {
-        for (const AtomProxy atomP : AtomRange(mtop))
-        {
-            int index = atomP.globalAtomNumber();
-            if (getGroupType(groups, SimulationAtomGroupType::QuantumMechanics, index) == i)
-            {
-                qmmmAtoms.push_back(index);
-            }
-        }
-        if (ir.QMMMscheme == eQMMMschemeoniom)
-        {
-            /* I assume that users specify the QM groups from small to
-             * big(ger) in the mdp file
-             */
-            gmx_mtop_ilistloop_all_t iloop      = gmx_mtop_ilistloop_all_init(&mtop);
-            int                      nral1      = 1 + NRAL(F_VSITE2);
-            int                      atomOffset = 0;
-            while (const InteractionLists* ilists = gmx_mtop_ilistloop_all_next(iloop, &atomOffset))
-            {
-                const InteractionList& ilist = (*ilists)[F_VSITE2];
-                for (int j = 0; j < ilist.size(); j += nral1)
-                {
-                    const int vsite = atomOffset + ilist.iatoms[j];     /* the vsite         */
-                    const int ai    = atomOffset + ilist.iatoms[j + 1]; /* constructing atom */
-                    const int aj    = atomOffset + ilist.iatoms[j + 2]; /* constructing atom */
-                    if (getGroupType(groups, SimulationAtomGroupType::QuantumMechanics, vsite)
-                                == getGroupType(groups, SimulationAtomGroupType::QuantumMechanics, ai)
-                        && getGroupType(groups, SimulationAtomGroupType::QuantumMechanics, vsite)
-                                   == getGroupType(groups, SimulationAtomGroupType::QuantumMechanics, aj))
-                    {
-                        /* this dummy link atom needs to be removed from qmmmAtoms
-                         * before making the QMrec of this layer!
-                         */
-                        qmmmAtoms.erase(std::remove_if(qmmmAtoms.begin(), qmmmAtoms.end(),
-                                                       [&vsite](int atom) { return atom == vsite; }),
-                                        qmmmAtoms.end());
-                    }
-                }
-            }
-        }
-    }
-    return qmmmAtoms;
-}
-
-void removeQmmmAtomCharges(gmx_mtop_t* mtop, gmx::ArrayRef<const int> qmmmAtoms)
-{
-    int molb = 0;
-    for (gmx::index i = 0; i < qmmmAtoms.ssize(); i++)
-    {
-        int indexInMolecule;
-        mtopGetMolblockIndex(mtop, qmmmAtoms[i], &molb, nullptr, &indexInMolecule);
-        t_atom* atom = &mtop->moltype[mtop->molblock[molb].type].atoms.atom[indexInMolecule];
-        atom->q      = 0.0;
-        atom->qB     = 0.0;
-    }
-}
-
-void init_QMMMrec(const t_commrec* cr, const gmx_mtop_t* mtop, const t_inputrec* ir, const t_forcerec* fr)
-{
-    /* we put the atomsnumbers of atoms that belong to the QMMM group in
-     * an array that will be copied later to QMMMrec->indexQM[..]. Also
-     * it will be used to create an QMMMrec->bQMMM index array that
-     * simply contains true/false for QM and MM (the other) atoms.
-     */
-
-    t_QMMMrec* qr;
-    t_MMrec*   mm;
-
-    if (!GMX_QMMM)
-    {
-        gmx_incons("Compiled without QMMM");
-    }
-
-    if (ir->cutoff_scheme != ecutsGROUP)
-    {
-        gmx_fatal(FARGS, "QMMM is currently only supported with cutoff-scheme=group");
-    }
-    if (!EI_DYNAMICS(ir->eI))
-    {
-        gmx_fatal(FARGS, "QMMM is only supported with dynamics");
-    }
-
-    /* issue a fatal if the user wants to run with more than one node */
-    if (PAR(cr))
-    {
-        gmx_fatal(FARGS, "QM/MM does not work in parallel, use a single rank instead\n");
-    }
-
-    /* Make a local copy of the QMMMrec */
-    qr = fr->qr;
-
-    /* bQMMM[..] is an array containing TRUE/FALSE for atoms that are
-     * QM/not QM. We first set all elemenst at false. Afterwards we use
-     * the qm_arr (=MMrec->indexQM) to changes the elements
-     * corresponding to the QM atoms at TRUE.  */
-
-    qr->QMMMscheme = ir->QMMMscheme;
-
-    /* we take the possibility into account that a user has
-     * defined more than one QM group:
-     */
-    /* an ugly work-around in case there is only one group In this case
-     * the whole system is treated as QM. Otherwise the second group is
-     * always the rest of the total system and is treated as MM.
-     */
-
-    /* small problem if there is only QM.... so no MM */
-
-    int numQmmmGroups = ir->opts.ngQM;
-
-    if (qr->QMMMscheme == eQMMMschemeoniom)
-    {
-        qr->nrQMlayers = numQmmmGroups;
-    }
-    else
-    {
-        qr->nrQMlayers = 1;
-    }
-
-    /* there are numQmmmGroups groups of QM atoms. In case of multiple QM groups
-     * I assume that the users wants to do ONIOM. However, maybe it
-     * should also be possible to define more than one QM subsystem with
-     * independent neighbourlists. I have to think about
-     * that.. 11-11-2003
-     */
-    std::vector<int> qmmmAtoms = qmmmAtomIndices(*ir, *mtop);
-    snew(qr->qm, numQmmmGroups);
-    for (int i = 0; i < numQmmmGroups; i++)
-    {
-        /* new layer */
-        if (qr->QMMMscheme == eQMMMschemeoniom)
-        {
-            /* add the atoms to the bQMMM array
-             */
-
-            /* I assume that users specify the QM groups from small to
-             * big(ger) in the mdp file
-             */
-            qr->qm[i] = mk_QMrec();
-            /* store QM atoms in this layer in the QMrec and initialise layer
-             */
-            init_QMrec(i, qr->qm[i], qmmmAtoms.size(), qmmmAtoms.data(), mtop, ir);
-        }
-    }
-    if (qr->QMMMscheme != eQMMMschemeoniom)
-    {
-
-        /* standard QMMM, all layers are merged together so there is one QM
-         * subsystem and one MM subsystem.
-         * Also we set the charges to zero in mtop to prevent the innerloops
-         * from doubly counting the electostatic QM MM interaction
-         * TODO: Consider doing this in grompp instead.
-         */
-
-        qr->qm[0] = mk_QMrec();
-        /* store QM atoms in the QMrec and initialise
-         */
-        init_QMrec(0, qr->qm[0], qmmmAtoms.size(), qmmmAtoms.data(), mtop, ir);
-
-        /* MM rec creation */
-        mm              = mk_MMrec();
-        mm->scalefactor = ir->scalefactor;
-        mm->nrMMatoms   = (mtop->natoms) - (qr->qm[0]->nrQMatoms); /* rest of the atoms */
-        qr->mm          = mm;
-    }
-    else /* ONIOM */
-    {    /* MM rec creation */
-        mm              = mk_MMrec();
-        mm->scalefactor = ir->scalefactor;
-        mm->nrMMatoms   = 0;
-        qr->mm          = mm;
-    }
-
-    /* these variables get updated in the update QMMMrec */
-
-    if (qr->nrQMlayers == 1)
-    {
-        /* with only one layer there is only one initialisation
-         * needed. Multilayer is a bit more complicated as it requires
-         * re-initialisation at every step of the simulation. This is due
-         * to the use of COMMON blocks in the fortran QM subroutines.
-         */
-        if (qr->qm[0]->QMmethod < eQMmethodRHF)
-        {
-            if (GMX_QMMM_MOPAC)
-            {
-                /* semi-empiprical 1-layer ONIOM calculation requested (mopac93) */
-                init_mopac(qr->qm[0]);
-            }
-            else
-            {
-                gmx_fatal(FARGS, "Semi-empirical QM only supported with Mopac.");
-            }
-        }
-        else
-        {
-            /* ab initio calculation requested (gamess/gaussian/ORCA) */
-            if (GMX_QMMM_GAMESS)
-            {
-                init_gamess(cr, qr->qm[0], qr->mm);
-            }
-            else if (GMX_QMMM_GAUSSIAN)
-            {
-                init_gaussian(qr->qm[0]);
-            }
-            else if (GMX_QMMM_ORCA)
-            {
-                init_orca(qr->qm[0]);
-            }
-            else
-            {
-                gmx_fatal(FARGS,
-                          "Ab-initio calculation only supported with Gamess, Gaussian or ORCA.");
-            }
-        }
-    }
-} /* init_QMMMrec */
-
-void update_QMMMrec(const t_commrec* cr, const t_forcerec* fr, const rvec* x, const t_mdatoms* md, const matrix box)
-{
-    /* updates the coordinates of both QM atoms and MM atoms and stores
-     * them in the QMMMrec.
-     *
-     * NOTE: is NOT yet working if there are no PBC. Also in ns.c, simple
-     * ns needs to be fixed!
-     */
-    int           mm_max = 0, mm_nr = 0, mm_nr_new, i, j, is, k, shift;
-    t_j_particle *mm_j_particles = nullptr, *qm_i_particles = nullptr;
-    t_QMMMrec*    qr;
-    t_nblist*     QMMMlist;
-    rvec          dx;
-    ivec          crd;
-    t_QMrec*      qm;
-    t_MMrec*      mm;
-    t_pbc         pbc;
-    int*          parallelMMarray = nullptr;
-
-    if (!GMX_QMMM)
-    {
-        gmx_incons("Compiled without QMMM");
-    }
-
-    /* every cpu has this array. On every processor we fill this array
-     * with 1's and 0's. 1's indicate the atoms is a QM atom on the
-     * current cpu in a later stage these arrays are all summed. indexes
-     * > 0 indicate the atom is a QM atom. Every node therefore knows
-     * whcih atoms are part of the QM subsystem.
-     */
-    /* copy some pointers */
-    qr       = fr->qr;
-    mm       = qr->mm;
-    QMMMlist = fr->QMMMlist;
-
-    /*  init_pbc(box);  needs to be called first, see pbc.h */
-    ivec null_ivec;
-    clear_ivec(null_ivec);
-    set_pbc_dd(&pbc, fr->ePBC, DOMAINDECOMP(cr) ? cr->dd->nc : null_ivec, FALSE, box);
-    /* only in standard (normal) QMMM we need the neighbouring MM
-     * particles to provide a electric field of point charges for the QM
-     * atoms.
-     */
-    if (qr->QMMMscheme == eQMMMschemenormal) /* also implies 1 QM-layer */
-    {
-        /* we NOW create/update a number of QMMMrec entries:
-         *
-         * 1) the shiftQM, containing the shifts of the QM atoms
-         *
-         * 2) the indexMM array, containing the index of the MM atoms
-         *
-         * 3) the shiftMM, containing the shifts of the MM atoms
-         *
-         * 4) the shifted coordinates of the MM atoms
-         *
-         * the shifts are used for computing virial of the QM/MM particles.
-         */
-        qm = qr->qm[0]; /* in case of normal QMMM, there is only one group */
-        snew(qm_i_particles, QMMMlist->nri);
-        if (QMMMlist->nri)
-        {
-            qm_i_particles[0].shift = XYZ2IS(0, 0, 0);
-            for (i = 0; i < QMMMlist->nri; i++)
-            {
-                qm_i_particles[i].j = QMMMlist->iinr[i];
-
-                if (i)
-                {
-                    qm_i_particles[i].shift =
-                            pbc_dx_aiuc(&pbc, x[QMMMlist->iinr[0]], x[QMMMlist->iinr[i]], dx);
-                }
-                /* However, since nri >= nrQMatoms, we do a quicksort, and throw
-                 * out double, triple, etc. entries later, as we do for the MM
-                 * list too.
-                 */
-
-                /* compute the shift for the MM j-particles with respect to
-                 * the QM i-particle and store them.
-                 */
-
-                crd[0] = IS2X(QMMMlist->shift[i]) + IS2X(qm_i_particles[i].shift);
-                crd[1] = IS2Y(QMMMlist->shift[i]) + IS2Y(qm_i_particles[i].shift);
-                crd[2] = IS2Z(QMMMlist->shift[i]) + IS2Z(qm_i_particles[i].shift);
-                is     = XYZ2IS(crd[0], crd[1], crd[2]);
-                for (j = QMMMlist->jindex[i]; j < QMMMlist->jindex[i + 1]; j++)
-                {
-                    if (mm_nr >= mm_max)
-                    {
-                        mm_max += 1000;
-                        srenew(mm_j_particles, mm_max);
-                    }
-
-                    mm_j_particles[mm_nr].j     = QMMMlist->jjnr[j];
-                    mm_j_particles[mm_nr].shift = is;
-                    mm_nr++;
-                }
-            }
-
-            /* quicksort QM and MM shift arrays and throw away multiple entries */
-
-
-            std::sort(qm_i_particles, qm_i_particles + QMMMlist->nri, struct_comp);
-            /* The mm_j_particles argument to qsort is not allowed to be nullptr */
-            if (mm_nr > 0)
-            {
-                std::sort(mm_j_particles, mm_j_particles + mm_nr, struct_comp);
-            }
-            /* remove multiples in the QM shift array, since in init_QMMM() we
-             * went through the atom numbers from 0 to md.nr, the order sorted
-             * here matches the one of QMindex already.
-             */
-            j = 0;
-            for (i = 0; i < QMMMlist->nri; i++)
-            {
-                if (i == 0 || qm_i_particles[i].j != qm_i_particles[i - 1].j)
-                {
-                    qm_i_particles[j++] = qm_i_particles[i];
-                }
-            }
-            mm_nr_new = 0;
-            /* Remove double entries for the MM array.
-             * Also remove mm atoms that have no charges!
-             * actually this is already done in the ns.c
-             */
-            for (i = 0; i < mm_nr; i++)
-            {
-                if ((i == 0 || mm_j_particles[i].j != mm_j_particles[i - 1].j)
-                    && !md->bQM[mm_j_particles[i].j]
-                    && ((md->chargeA[mm_j_particles[i].j] != 0.0_real)
-                        || (md->chargeB && (md->chargeB[mm_j_particles[i].j] != 0.0_real))))
-                {
-                    mm_j_particles[mm_nr_new++] = mm_j_particles[i];
-                }
-            }
-            mm_nr = mm_nr_new;
-            /* store the data retrieved above into the QMMMrec
-             */
-            k = 0;
-            /* Keep the compiler happy,
-             * shift will always be set in the loop for i=0
-             */
-            shift = 0;
-            for (i = 0; i < qm->nrQMatoms; i++)
-            {
-                /* not all qm particles might have appeared as i
-                 * particles. They might have been part of the same charge
-                 * group for instance.
-                 */
-                if (qm->indexQM[i] == qm_i_particles[k].j)
-                {
-                    shift = qm_i_particles[k++].shift;
-                }
-                /* use previous shift, assuming they belong the same charge
-                 * group anyway,
-                 */
-
-                qm->shiftQM[i] = shift;
-            }
-        }
-        /* parallel excecution */
-        if (PAR(cr))
-        {
-            snew(parallelMMarray, 2 * (md->nr));
-            /* only MM particles have a 1 at their atomnumber. The second part
-             * of the array contains the shifts. Thus:
-             * p[i]=1/0 depending on wether atomnumber i is a MM particle in the QM
-             * step or not. p[i+md->nr] is the shift of atomnumber i.
-             */
-            for (i = 0; i < 2 * (md->nr); i++)
-            {
-                parallelMMarray[i] = 0;
-            }
-
-            for (i = 0; i < mm_nr; i++)
-            {
-                parallelMMarray[mm_j_particles[i].j]            = 1;
-                parallelMMarray[mm_j_particles[i].j + (md->nr)] = mm_j_particles[i].shift;
-            }
-            gmx_sumi(md->nr, parallelMMarray, cr);
-            mm_nr = 0;
-
-            mm_max = 0;
-            for (i = 0; i < md->nr; i++)
-            {
-                if (parallelMMarray[i])
-                {
-                    if (mm_nr >= mm_max)
-                    {
-                        mm_max += 1000;
-                        srenew(mm->indexMM, mm_max);
-                        srenew(mm->shiftMM, mm_max);
-                    }
-                    mm->indexMM[mm_nr]   = i;
-                    mm->shiftMM[mm_nr++] = parallelMMarray[i + md->nr] / parallelMMarray[i];
-                }
-            }
-            mm->nrMMatoms = mm_nr;
-            free(parallelMMarray);
-        }
-        /* serial execution */
-        else
-        {
-            mm->nrMMatoms = mm_nr;
-            srenew(mm->shiftMM, mm_nr);
-            srenew(mm->indexMM, mm_nr);
-            for (i = 0; i < mm_nr; i++)
-            {
-                mm->indexMM[i] = mm_j_particles[i].j;
-                mm->shiftMM[i] = mm_j_particles[i].shift;
-            }
-        }
-        /* (re) allocate memory for the MM coordiate array. The QM
-         * coordinate array was already allocated in init_QMMM, and is
-         * only (re)filled in the update_QMMM_coordinates routine
-         */
-        srenew(mm->xMM, mm->nrMMatoms);
-        /* now we (re) fill the array that contains the MM charges with
-         * the forcefield charges. If requested, these charges will be
-         * scaled by a factor
-         */
-        srenew(mm->MMcharges, mm->nrMMatoms);
-        for (i = 0; i < mm->nrMMatoms; i++) /* no free energy yet */
-        {
-            mm->MMcharges[i] = md->chargeA[mm->indexMM[i]] * mm->scalefactor;
-        }
-        /* the next routine fills the coordinate fields in the QMMM rec of
-         * both the qunatum atoms and the MM atoms, using the shifts
-         * calculated above.
-         */
-
-        update_QMMM_coord(x, fr, qr->qm[0], qr->mm);
-        free(qm_i_particles);
-        free(mm_j_particles);
-    }
-    else /* ONIOM */ /* ????? */
-    {
-        mm->nrMMatoms = 0;
-        /* do for each layer */
-        for (j = 0; j < qr->nrQMlayers; j++)
-        {
-            qm             = qr->qm[j];
-            qm->shiftQM[0] = XYZ2IS(0, 0, 0);
-            for (i = 1; i < qm->nrQMatoms; i++)
-            {
-                qm->shiftQM[i] = pbc_dx_aiuc(&pbc, x[qm->indexQM[0]], x[qm->indexQM[i]], dx);
-            }
-            update_QMMM_coord(x, fr, qm, mm);
-        }
-    }
-} /* update_QMMM_rec */
-
-real calculate_QMMM(const t_commrec* cr, gmx::ForceWithShiftForces* forceWithShiftForces, const t_forcerec* fr)
-{
-    real QMener = 0.0;
-    /* a selection for the QM package depending on which is requested
-     * (Gaussian, GAMESS-UK, MOPAC or ORCA) needs to be implemented here. Now
-     * it works through defines.... Not so nice yet
-     */
-    t_QMMMrec* qr;
-    t_QMrec *  qm, *qm2;
-    t_MMrec*   mm     = nullptr;
-    rvec *     forces = nullptr, *fshift = nullptr, *forces2 = nullptr,
-         *fshift2 = nullptr; /* needed for multilayer ONIOM */
-    int i, j, k;
-
-    if (!GMX_QMMM)
-    {
-        gmx_incons("Compiled without QMMM");
-    }
-
-    /* make a local copy the QMMMrec pointer
-     */
-    qr = fr->qr;
-    mm = qr->mm;
-
-    /* now different procedures are carried out for one layer ONION and
-     * normal QMMM on one hand and multilayer oniom on the other
-     */
-    gmx::ArrayRef<gmx::RVec> fMM      = forceWithShiftForces->force();
-    gmx::ArrayRef<gmx::RVec> fshiftMM = forceWithShiftForces->shiftForces();
-    if (qr->QMMMscheme == eQMMMschemenormal || qr->nrQMlayers == 1)
-    {
-        qm = qr->qm[0];
-        snew(forces, (qm->nrQMatoms + mm->nrMMatoms));
-        snew(fshift, (qm->nrQMatoms + mm->nrMMatoms));
-        QMener = call_QMroutine(cr, fr, qm, mm, forces, fshift);
-        for (i = 0; i < qm->nrQMatoms; i++)
-        {
-            for (j = 0; j < DIM; j++)
-            {
-                fMM[qm->indexQM[i]][j] -= forces[i][j];
-                fshiftMM[qm->shiftQM[i]][j] += fshift[i][j];
-            }
-        }
-        for (i = 0; i < mm->nrMMatoms; i++)
-        {
-            for (j = 0; j < DIM; j++)
-            {
-                fMM[mm->indexMM[i]][j] -= forces[qm->nrQMatoms + i][j];
-                fshiftMM[mm->shiftMM[i]][j] += fshift[qm->nrQMatoms + i][j];
-            }
-        }
-        free(forces);
-        free(fshift);
-    }
-    else /* Multi-layer ONIOM */
-    {
-        for (i = 0; i < qr->nrQMlayers - 1; i++) /* last layer is special */
-        {
-            qm  = qr->qm[i];
-            qm2 = copy_QMrec(qr->qm[i + 1]);
-
-            qm2->nrQMatoms = qm->nrQMatoms;
-
-            for (j = 0; j < qm2->nrQMatoms; j++)
-            {
-                for (k = 0; k < DIM; k++)
-                {
-                    qm2->xQM[j][k] = qm->xQM[j][k];
-                }
-                qm2->indexQM[j]        = qm->indexQM[j];
-                qm2->atomicnumberQM[j] = qm->atomicnumberQM[j];
-                qm2->shiftQM[j]        = qm->shiftQM[j];
-            }
-
-            qm2->QMcharge = qm->QMcharge;
-            /* this layer at the higher level of theory */
-            srenew(forces, qm->nrQMatoms);
-            srenew(fshift, qm->nrQMatoms);
-            /* we need to re-initialize the QMroutine every step... */
-            init_QMroutine(cr, qm, mm);
-            QMener += call_QMroutine(cr, fr, qm, mm, forces, fshift);
-
-            /* this layer at the lower level of theory */
-            srenew(forces2, qm->nrQMatoms);
-            srenew(fshift2, qm->nrQMatoms);
-            init_QMroutine(cr, qm2, mm);
-            QMener -= call_QMroutine(cr, fr, qm2, mm, forces2, fshift2);
-            /* E = E1high-E1low The next layer includes the current layer at
-             * the lower level of theory, which provides + E2low
-             * this is similar for gradients
-             */
-            for (i = 0; i < qm->nrQMatoms; i++)
-            {
-                for (j = 0; j < DIM; j++)
-                {
-                    fMM[qm->indexQM[i]][j] -= (forces[i][j] - forces2[i][j]);
-                    fshiftMM[qm->shiftQM[i]][j] += (fshift[i][j] - fshift2[i][j]);
-                }
-            }
-            free(qm2);
-        }
-        /* now the last layer still needs to be done: */
-        qm = qr->qm[qr->nrQMlayers - 1]; /* C counts from 0 */
-        init_QMroutine(cr, qm, mm);
-        srenew(forces, qm->nrQMatoms);
-        srenew(fshift, qm->nrQMatoms);
-        QMener += call_QMroutine(cr, fr, qm, mm, forces, fshift);
-        for (i = 0; i < qm->nrQMatoms; i++)
-        {
-            for (j = 0; j < DIM; j++)
-            {
-                fMM[qm->indexQM[i]][j] -= forces[i][j];
-                fshiftMM[qm->shiftQM[i]][j] += fshift[i][j];
-            }
-        }
-        free(forces);
-        free(fshift);
-        free(forces2);
-        free(fshift2);
-    }
-    return (QMener);
-} /* calculate_QMMM */
-
-#pragma GCC diagnostic pop
diff --git a/src/gromacs/mdlib/qmmm.h b/src/gromacs/mdlib/qmmm.h
deleted file mode 100644 (file)
index 8172e97..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#ifndef GMX_MDLIB_QMMM_H
-#define GMX_MDLIB_QMMM_H
-
-#include "config.h"
-
-#include <vector>
-
-#include "gromacs/math/vectypes.h"
-#include "gromacs/mdlib/tgroup.h"
-#include "gromacs/utility/arrayref.h"
-
-#define GMX_QMMM (GMX_QMMM_MOPAC || GMX_QMMM_GAMESS || GMX_QMMM_GAUSSIAN || GMX_QMMM_ORCA)
-
-struct gmx_localtop_t;
-struct gmx_mtop_t;
-struct t_commrec;
-struct t_forcerec;
-struct t_inputrec;
-struct t_mdatoms;
-struct t_QMMMrec;
-
-namespace gmx
-{
-class ForceWithShiftForces;
-}
-
-typedef struct
-{
-    int   nrQMatoms;      /* total nr of QM atoms              */
-    rvec* xQM;            /* shifted to center of box          */
-    int*  indexQM;        /* atom i = atom indexQM[i] in mdrun */
-    int*  atomicnumberQM; /* atomic numbers of QM atoms        */
-    real* QMcharges;      /* atomic charges of QM atoms(ONIOM) */
-    int*  shiftQM;
-    int   QMcharge;     /* charge of the QM system           */
-    int   multiplicity; /* multipicity (no of unpaired eln)  */
-    int   QMmethod;     /* see enums.h for all methods       */
-    int   QMbasis;      /* see enums.h for all bases         */
-    int   nelectrons;   /* total number of elecs in QM region*/
-    /* Gaussian specific stuff */
-    int      nQMcpus;  /* no. of CPUs used for the QM calc. */
-    int      QMmem;    /* memory for the gaussian calc.     */
-    int      accuracy; /* convergence criterium (E(-x))     */
-    gmx_bool cpmcscf;  /* using cpmcscf(l1003)*/
-    char*    gauss_dir;
-    char*    gauss_exe;
-    char*    devel_dir;
-    char*    orca_basename; /* basename for I/O with orca        */
-    char*    orca_dir;      /* directory for ORCA                */
-    /* Surface hopping stuff */
-    gmx_bool bSH;     /* surface hopping (diabatic only)   */
-    real     SAon;    /* at which energy gap the SA starts */
-    real     SAoff;   /* at which energy gap the SA stops  */
-    int      SAsteps; /* stepwise switchinng on the SA     */
-    int      SAstep;  /* current state of SA               */
-    int      CIdim;
-    real*    CIvec1;
-    real*    CIvec2;
-    real*    CIvec1old;
-    real*    CIvec2old;
-    ivec     SHbasis;
-    int      CASelectrons;
-    int      CASorbitals;
-} t_QMrec;
-
-typedef struct
-{
-    int   nrMMatoms; /* nr of MM atoms, updated every step*/
-    rvec* xMM;       /* shifted to center of box          */
-    int*  indexMM;   /* atom i = atom indexMM[I] in mdrun */
-    real* MMcharges; /* MM point charges in std QMMM calc.*/
-    int*  shiftMM;
-    int*  MMatomtype; /* only important for semi-emp.      */
-    real  scalefactor;
-} t_MMrec;
-
-
-typedef struct t_QMMMrec
-{
-    int       QMMMscheme; /* ONIOM (multi-layer) or normal          */
-    int       nrQMlayers; /* number of QM layers (total layers +1 (MM)) */
-    t_QMrec** qm;         /* atoms and run params for each QM group */
-    t_MMrec*  mm;         /* there can only be one MM subsystem !   */
-} t_QMMMrec;
-
-void atomic_number(int nr, char*** atomtype, int* nucnum);
-
-t_QMMMrec* mk_QMMMrec();
-/* allocates memory for QMMMrec */
-
-void init_QMMMrec(const t_commrec* cr, const gmx_mtop_t* mtop, const t_inputrec* ir, const t_forcerec* fr);
-
-/* init_QMMMrec initializes the QMMM record. From
- * topology->atoms.atomname and topology->atoms.atomtype the atom
- * names and types are read; from inputrec->QMcharge
- * resp. inputrec->QMmult the nelecs and multiplicity are determined
- * and md->cQMMM gives numbers of the MM and QM atoms
- */
-void update_QMMMrec(const t_commrec* cr, const t_forcerec* fr, const rvec* x, const t_mdatoms* md, const matrix box);
-
-/* update_QMMMrec fills the MM stuff in QMMMrec. The MM atoms are
- * taken froom the neighbourlists of the QM atoms. In a QMMM run this
- * routine should be called at every step, since it updates the MM
- * elements of the t_QMMMrec struct.
- */
-real calculate_QMMM(const t_commrec* cr, gmx::ForceWithShiftForces* forceWithShiftForces, const t_forcerec* fr);
-
-/* QMMM computes the QM forces. This routine makes either function
- * calls to gmx QM routines (derived from MOPAC7 (semi-emp.) and MPQC
- * (ab initio)) or generates input files for an external QM package
- * (listed in QMMMrec.QMpackage). The binary of the QM package is
- * called by system().
- */
-
-/*! \brief
- * Return vector of atom indices for atoms in the QMMM region.
- *
- * \param[in] mtop Topology to use for populating array.
- * \param[in] ir   Inputrec used in simulation.
- * \returns Vector of atoms.
- */
-std::vector<int> qmmmAtomIndices(const t_inputrec& ir, const gmx_mtop_t& mtop);
-
-/*! \brief
- * Remove charges from QMMM atoms.
- *
- * \param[in] mtop Topology used for removing atoms.
- * \param[in] qmmmAtoms ArrayRef to vector conatining qmmm atom indices.
- */
-void removeQmmmAtomCharges(gmx_mtop_t* mtop, gmx::ArrayRef<const int> qmmmAtoms);
-
-#endif
index 3625d4a0dd371cea24de19c54a96ee596bed001d..7e807061ac970663c1c93cc43519a220dbb447db 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,7 @@
 #include "gromacs/domdec/domdec.h"
 #include "gromacs/ewald/pme.h"
 #include "gromacs/ewald/pme_load_balancing.h"
+#include "gromacs/ewald/pme_pp.h"
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/mdrunutility/printtime.h"
index fc4822182b68109b573e6fb204f86130fb74357a..a113269537176bf3635183d78f1f12059e87e478 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index df62c5c213e5297c15b39c7e4fd6efa3aa756114..508bafc750c96cdce13d95da186ed5db65dd6c6b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -51,6 +52,7 @@
 
 #include <algorithm>
 
+#include "gromacs/math/arrayrefwithpadding.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/invertmatrix.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
-#include "gromacs/utility/smalloc.h"
 
 namespace gmx
 {
 
-struct settleparam_t
-{
-    real mO;
-    real mH;
-    real wh;
-    real dOH;
-    real dHH;
-    real ra;
-    real rb;
-    real rc;
-    real irc2;
-    /* For projection */
-    real   imO;
-    real   imH;
-    real   invdOH;
-    real   invdHH;
-    matrix invmat;
-};
-
-struct settledata
-{
-    settleparam_t massw; /* Parameters for SETTLE for coordinates */
-    settleparam_t mass1; /* Parameters with all masses 1, for forces */
-
-    int   nsettle; /* The number of settles on our rank */
-    int*  ow1;     /* Index to OW1 atoms, size nsettle + SIMD padding */
-    int*  hw2;     /* Index to HW2 atoms, size nsettle + SIMD padding */
-    int*  hw3;     /* Index to HW3 atoms, size nsettle + SIMD padding */
-    real* virfac;  /* Virial factor 0 or 1, size nsettle + SIMD pad. */
-    int   nalloc;  /* Allocation size of ow1, hw2, hw3, virfac */
-
-    bool bUseSimd; /* Use SIMD intrinsics code, if possible */
-};
-
-
-//! Initializes a projection matrix.
-static void init_proj_matrix(real invmO, real invmH, real dOH, real dHH, matrix inverseCouplingMatrix)
+/*! \brief Initializes a projection matrix.
+ *
+ * \param[in]  invmO                  Reciprocal oxygen mass
+ * \param[in]  invmH                  Reciprocal hydrogen mass
+ * \param[in]  dOH                    Target O-H bond length
+ * \param[in]  dHH                    Target H-H bond length
+ * \param[out] inverseCouplingMatrix  Inverse bond coupling matrix for the projection version of SETTLE
+ */
+static void initializeProjectionMatrix(const real invmO,
+                                       const real invmH,
+                                       const real dOH,
+                                       const real dHH,
+                                       matrix     inverseCouplingMatrix)
 {
-    /* We normalize the inverse masses with invmO for the matrix inversion.
-     * so we can keep using masses of almost zero for frozen particles,
-     * without running out of the float range in invertMatrix.
-     */
+    // We normalize the inverse masses with invmO for the matrix inversion.
+    // so we can keep using masses of almost zero for frozen particles,
+    // without running out of the float range in invertMatrix.
     double invmORelative = 1.0;
     double invmHRelative = invmH / static_cast<double>(invmO);
     double distanceRatio = dHH / static_cast<double>(dOH);
@@ -134,46 +110,51 @@ static void init_proj_matrix(real invmO, real invmH, real dOH, real dHH, matrix
     msmul(inverseCouplingMatrix, 1 / invmO, inverseCouplingMatrix);
 }
 
-//! Initializes settle parameters.
-static void settleparam_init(settleparam_t* p, real mO, real mH, real invmO, real invmH, real dOH, real dHH)
+SettleParameters
+settleParameters(const real mO, const real mH, const real invmO, const real invmH, const real dOH, const real dHH)
 {
-    /* We calculate parameters in double precision to minimize errors.
-     * The velocity correction applied during SETTLE coordinate constraining
-     * introduces a systematic error of approximately 1 bit per atom,
-     * depending on what the compiler does with the code.
-     */
+    SettleParameters params;
+
+    // We calculate parameters in double precision to minimize errors.
+    // The velocity correction applied during SETTLE coordinate constraining
+    // introduces a systematic error of approximately 1 bit per atom,
+    // depending on what the compiler does with the code.
     double wohh;
 
-    p->mO     = mO;
-    p->mH     = mH;
-    wohh      = mO + 2.0 * mH;
-    p->wh     = mH / wohh;
-    p->dOH    = dOH;
-    p->dHH    = dHH;
-    double rc = dHH / 2.0;
-    double ra = 2.0 * mH * std::sqrt(dOH * dOH - rc * rc) / wohh;
-    p->rb     = std::sqrt(dOH * dOH - rc * rc) - ra;
-    p->rc     = rc;
-    p->ra     = ra;
-    p->irc2   = 1.0 / dHH;
+    params.mO   = mO;
+    params.mH   = mH;
+    wohh        = mO + 2.0 * mH;
+    params.wh   = mH / wohh;
+    params.dOH  = dOH;
+    params.dHH  = dHH;
+    double rc   = dHH / 2.0;
+    double ra   = 2.0 * mH * std::sqrt(dOH * dOH - rc * rc) / wohh;
+    params.rb   = std::sqrt(dOH * dOH - rc * rc) - ra;
+    params.rc   = rc;
+    params.ra   = ra;
+    params.irc2 = 1.0 / dHH;
 
-    /* For projection: inverse masses and coupling matrix inversion */
-    p->imO = invmO;
-    p->imH = invmH;
+    // For projection: inverse masses and coupling matrix inversion
+    params.imO = invmO;
+    params.imH = invmH;
 
-    p->invdOH = 1.0 / dOH;
-    p->invdHH = 1.0 / dHH;
+    params.invdOH = 1.0 / dOH;
+    params.invdHH = 1.0 / dHH;
 
-    init_proj_matrix(invmO, invmH, dOH, dHH, p->invmat);
+    initializeProjectionMatrix(invmO, invmH, dOH, dHH, params.invmat);
 
     if (debug)
     {
-        fprintf(debug, "wh =%g, rc = %g, ra = %g\n", p->wh, p->rc, p->ra);
-        fprintf(debug, "rb = %g, irc2 = %g, dHH = %g, dOH = %g\n", p->rb, p->irc2, p->dHH, p->dOH);
+        fprintf(debug, "wh =%g, rc = %g, ra = %g\n", params.wh, params.rc, params.ra);
+        fprintf(debug, "rb = %g, irc2 = %g, dHH = %g, dOH = %g\n", params.rb, params.irc2,
+                params.dHH, params.dOH);
     }
+
+    return params;
 }
 
-settledata* settle_init(const gmx_mtop_t& mtop)
+SettleData::SettleData(const gmx_mtop_t& mtop) :
+    useSimd_(getenv("GMX_DISABLE_SIMD_KERNELS") == nullptr)
 {
     /* Check that we have only one settle type */
     int                  settle_type = -1;
@@ -205,42 +186,21 @@ settledata* settle_init(const gmx_mtop_t& mtop)
     }
     GMX_RELEASE_ASSERT(settle_type >= 0, "settle_init called without settles");
 
-    settledata* settled;
-
-    snew(settled, 1);
-
     /* We will not initialize the normal SETTLE parameters here yet,
      * since the atom (inv)masses can depend on the integrator and
      * free-energy perturbation. We set mO=-1 to trigger later initialization.
      */
-    settled->massw.mO = -1;
-
-    real dOH = mtop.ffparams.iparams[settle_type].settle.doh;
-    real dHH = mtop.ffparams.iparams[settle_type].settle.dhh;
-    settleparam_init(&settled->mass1, 1.0, 1.0, 1.0, 1.0, dOH, dHH);
-
-    settled->ow1    = nullptr;
-    settled->hw2    = nullptr;
-    settled->hw3    = nullptr;
-    settled->virfac = nullptr;
-    settled->nalloc = 0;
-
-    /* Without SIMD configured, this bool is not used */
-    settled->bUseSimd = (getenv("GMX_DISABLE_SIMD_KERNELS") == nullptr);
+    parametersMassWeighted_.mO = -1;
 
-    return settled;
-}
-
-void settle_free(settledata* settled)
-{
-    sfree_aligned(settled->ow1);
-    sfree_aligned(settled->hw2);
-    sfree_aligned(settled->hw3);
-    sfree_aligned(settled->virfac);
-    sfree(settled);
+    real dOH              = mtop.ffparams.iparams[settle_type].settle.doh;
+    real dHH              = mtop.ffparams.iparams[settle_type].settle.dhh;
+    parametersAllMasses1_ = settleParameters(1.0, 1.0, 1.0, 1.0, dOH, dHH);
 }
 
-void settle_set_constraints(settledata* settled, const t_ilist* il_settle, const t_mdatoms& mdatoms)
+void SettleData::setConstraints(const InteractionList& il_settle,
+                                const int              numHomeAtoms,
+                                const real*            masses,
+                                const real*            inverseMasses)
 {
 #if GMX_SIMD_HAVE_REAL
     const int pack_size = GMX_SIMD_REAL_WIDTH;
@@ -249,93 +209,85 @@ void settle_set_constraints(settledata* settled, const t_ilist* il_settle, const
 #endif
 
     const int nral1   = 1 + NRAL(F_SETTLE);
-    int       nsettle = il_settle->nr / nral1;
-    settled->nsettle  = nsettle;
+    int       nsettle = il_settle.size() / nral1;
+    numSettles_       = nsettle;
 
     if (nsettle > 0)
     {
-        const t_iatom* iatoms = il_settle->iatoms;
+        ArrayRef<const int> iatoms = il_settle.iatoms;
 
         /* Here we initialize the normal SETTLE parameters */
-        if (settled->massw.mO < 0)
+        if (parametersMassWeighted_.mO < 0)
         {
-            int firstO = iatoms[1];
-            int firstH = iatoms[2];
-            settleparam_init(&settled->massw, mdatoms.massT[firstO], mdatoms.massT[firstH],
-                             mdatoms.invmass[firstO], mdatoms.invmass[firstH], settled->mass1.dOH,
-                             settled->mass1.dHH);
+            int firstO              = iatoms[1];
+            int firstH              = iatoms[2];
+            parametersMassWeighted_ = settleParameters(
+                    masses[firstO], masses[firstH], inverseMasses[firstO], inverseMasses[firstH],
+                    parametersAllMasses1_.dOH, parametersAllMasses1_.dHH);
         }
 
-        if (nsettle + pack_size > settled->nalloc)
-        {
-            settled->nalloc = over_alloc_dd(nsettle + pack_size);
-            sfree_aligned(settled->ow1);
-            sfree_aligned(settled->hw2);
-            sfree_aligned(settled->hw3);
-            sfree_aligned(settled->virfac);
-            snew_aligned(settled->ow1, settled->nalloc, 64);
-            snew_aligned(settled->hw2, settled->nalloc, 64);
-            snew_aligned(settled->hw3, settled->nalloc, 64);
-            snew_aligned(settled->virfac, settled->nalloc, 64);
-        }
+        const int paddedSize = ((nsettle + pack_size - 1) / pack_size) * pack_size;
+        ow1_.resize(paddedSize);
+        hw2_.resize(paddedSize);
+        hw3_.resize(paddedSize);
+        virfac_.resize(paddedSize);
 
         for (int i = 0; i < nsettle; i++)
         {
-            settled->ow1[i] = iatoms[i * nral1 + 1];
-            settled->hw2[i] = iatoms[i * nral1 + 2];
-            settled->hw3[i] = iatoms[i * nral1 + 3];
+            ow1_[i] = iatoms[i * nral1 + 1];
+            hw2_[i] = iatoms[i * nral1 + 2];
+            hw3_[i] = iatoms[i * nral1 + 3];
             /* We should avoid double counting of virial contributions for
              * SETTLEs that appear in multiple DD domains, so we only count
              * the contribution on the home range of the oxygen atom.
              */
-            settled->virfac[i] = (iatoms[i * nral1 + 1] < mdatoms.homenr ? 1 : 0);
+            virfac_[i] = (iatoms[i * nral1 + 1] < numHomeAtoms ? 1 : 0);
         }
 
-        /* Pack the index array to the full SIMD width with copies from
+        /* Pad the index array to the full SIMD width with copies from
          * the last normal entry, but with no virial contribution.
          */
-        int end_packed = ((nsettle + pack_size - 1) / pack_size) * pack_size;
-        for (int i = nsettle; i < end_packed; i++)
+        for (int i = nsettle; i < paddedSize; i++)
         {
-            settled->ow1[i]    = settled->ow1[nsettle - 1];
-            settled->hw2[i]    = settled->hw2[nsettle - 1];
-            settled->hw3[i]    = settled->hw3[nsettle - 1];
-            settled->virfac[i] = 0;
+            ow1_[i]    = ow1_[nsettle - 1];
+            hw2_[i]    = hw2_[nsettle - 1];
+            hw3_[i]    = hw3_[nsettle - 1];
+            virfac_[i] = 0;
         }
     }
 }
 
-void settle_proj(settledata*        settled,
-                 ConstraintVariable econq,
-                 int                nsettle,
-                 const t_iatom      iatoms[],
-                 const t_pbc*       pbc,
-                 const rvec         x[],
-                 rvec*              der,
-                 rvec*              derp,
-                 int                calcvir_atom_end,
-                 tensor             vir_r_m_dder)
+void settle_proj(const SettleData&    settled,
+                 ConstraintVariable   econq,
+                 int                  nsettle,
+                 const t_iatom        iatoms[],
+                 const t_pbc*         pbc,
+                 ArrayRef<const RVec> x,
+                 ArrayRef<RVec>       der,
+                 ArrayRef<RVec>       derp,
+                 int                  calcvir_atom_end,
+                 tensor               vir_r_m_dder)
 {
     /* Settle for projection out constraint components
      * of derivatives of the coordinates.
      * Berk Hess 2008-1-10
      */
 
-    settleparam_t* p;
-    real           imO, imH, dOH, dHH, invdOH, invdHH;
-    matrix         invmat;
-    int            i, m, m2, ow1, hw2, hw3;
-    rvec           roh2, roh3, rhh, dc, fc;
+    const SettleParameters* p;
+    real                    imO, imH, dOH, dHH, invdOH, invdHH;
+    matrix                  invmat;
+    int                     i, m, m2, ow1, hw2, hw3;
+    rvec                    roh2, roh3, rhh, dc, fc;
 
     calcvir_atom_end *= DIM;
 
     if (econq == ConstraintVariable::Force)
     {
-        p = &settled->mass1;
+        p = &settled.parametersAllMasses1();
     }
     else
     {
-        p = &settled->massw;
+        p = &settled.parametersMassWeighted();
     }
     imO = p->imO;
     imH = p->imH;
@@ -416,7 +368,7 @@ void settle_proj(settledata*        settled,
 
 /*! \brief The actual settle code, templated for real/SimdReal and for optimization */
 template<typename T, typename TypeBool, int packSize, typename TypePbc, bool bCorrectVelocity, bool bCalcVirial>
-static void settleTemplate(const settledata* settled,
+static void settleTemplate(const SettleData& settled,
                            int               settleStart,
                            int               settleEnd,
                            const TypePbc     pbc,
@@ -436,6 +388,7 @@ static void settleTemplate(const settledata* settled,
     /*    2006-10-16 Changed velocity update to use differences         ** */
     /*    2012-09-24 Use oxygen as reference instead of COM             ** */
     /*    2016-02    Complete rewrite of the code for SIMD              ** */
+    /*    2020-06    Completely remove use of COM to minimize drift     ** */
     /*                                                                  ** */
     /*    Reference for the SETTLE algorithm                            ** */
     /*           S. Miyamoto et al., J. Comp. Chem., 13, 952 (1992).    ** */
@@ -447,14 +400,14 @@ static void settleTemplate(const settledata* settled,
 
     TypeBool bError = TypeBool(false);
 
-    const settleparam_t* p    = &settled->massw;
-    T                    wh   = T(p->wh);
-    T                    rc   = T(p->rc);
-    T                    ra   = T(p->ra);
-    T                    rb   = T(p->rb);
-    T                    irc2 = T(p->irc2);
-    T                    mO   = T(p->mO);
-    T                    mH   = T(p->mH);
+    const SettleParameters* p    = &settled.parametersMassWeighted();
+    T                       wh   = T(p->wh);
+    T                       rc   = T(p->rc);
+    T                       ra   = T(p->ra);
+    T                       rb   = T(p->rb);
+    T                       irc2 = T(p->irc2);
+    T                       mO   = T(p->mO);
+    T                       mH   = T(p->mH);
 
     T almost_zero = T(1e-12);
 
@@ -477,9 +430,9 @@ static void settleTemplate(const settledata* settled,
          * This gives correct results, since we store (not increment) all
          * output, so we store the same output multiple times.
          */
-        const int* ow1 = settled->ow1 + i;
-        const int* hw2 = settled->hw2 + i;
-        const int* hw3 = settled->hw3 + i;
+        const int* ow1 = settled.ow1() + i;
+        const int* hw2 = settled.hw2() + i;
+        const int* hw3 = settled.hw3() + i;
 
         T x_ow1[DIM], x_hw2[DIM], x_hw3[DIM];
 
@@ -495,49 +448,43 @@ static void settleTemplate(const settledata* settled,
 
         T dist21[DIM], dist31[DIM];
         T doh2[DIM], doh3[DIM];
-        T sh_hw2[DIM], sh_hw3[DIM];
 
         pbc_dx_aiuc(pbc, x_hw2, x_ow1, dist21);
 
         pbc_dx_aiuc(pbc, x_hw3, x_ow1, dist31);
 
-        /* Tedious way of doing pbc */
         pbc_dx_aiuc(pbc, xprime_hw2, xprime_ow1, doh2);
-        for (int d = 0; d < DIM; d++)
-        {
-            sh_hw2[d]     = xprime_hw2[d] - (xprime_ow1[d] + doh2[d]);
-            xprime_hw2[d] = xprime_hw2[d] - sh_hw2[d];
-        }
+
         pbc_dx_aiuc(pbc, xprime_hw3, xprime_ow1, doh3);
-        for (int d = 0; d < DIM; d++)
-        {
-            sh_hw3[d]     = xprime_hw3[d] - (xprime_ow1[d] + doh3[d]);
-            xprime_hw3[d] = xprime_hw3[d] - sh_hw3[d];
-        }
+        /* 4 * 18 flops (would be 4 * 3 without PBC) */
 
-        /* Not calculating the center of mass using the oxygen position
-         * and the O-H distances, as done below, will make SETTLE
-         * the largest source of energy drift for simulations of water,
+        /* Note that we completely avoid computing the center of mass and
+         * only use distances. This minimizes energy drift and also makes
+         * the computation slightly cheaper.
+         * Straightforward computation of the COM, as in the original algorithm,
+         * makes SETTLE the largest source of energy drift for simulations of water,
          * as then the oxygen coordinate is multiplied by 0.89 at every step,
          * which can then transfer a systematic rounding to the oxygen velocity.
+         * For some time we computed the COM using offsets from the oxygen, this
+         * significantly reduces the energy drift, but not using the COM at all,
+         * as we do now, is optimal.
          */
-        T a1[DIM], com[DIM];
+        T a1[DIM];
         for (int d = 0; d < DIM; d++)
         {
-            a1[d]  = -(doh2[d] + doh3[d]) * wh;
-            com[d] = xprime_ow1[d] - a1[d];
+            a1[d] = -(doh2[d] + doh3[d]) * wh;
         }
         T b1[DIM];
         for (int d = 0; d < DIM; d++)
         {
-            b1[d] = xprime_hw2[d] - com[d];
+            b1[d] = doh2[d] + a1[d];
         }
         T c1[DIM];
         for (int d = 0; d < DIM; d++)
         {
-            c1[d] = xprime_hw3[d] - com[d];
+            c1[d] = doh3[d] + a1[d];
         }
-        /* 15 flops */
+        /* 12 flops */
 
         T xakszd = dist21[YY] * dist31[ZZ] - dist21[ZZ] * dist31[YY];
         T yakszd = dist21[ZZ] * dist31[XX] - dist21[XX] * dist31[ZZ];
@@ -654,96 +601,84 @@ static void settleTemplate(const settledata* settled,
         /* 45 flops */
 
         /* Compute and store the corrected new coordinate */
+        T dxOw1[DIM];
         for (int d = 0; d < DIM; d++)
         {
-            xprime_ow1[d] = com[d] + a3[d];
+            dxOw1[d]      = a3[d] - a1[d];
+            xprime_ow1[d] = xprime_ow1[d] + dxOw1[d];
         }
+        T dxHw2[DIM];
         for (int d = 0; d < DIM; d++)
         {
-            xprime_hw2[d] = com[d] + b3[d] + sh_hw2[d];
+            dxHw2[d]      = b3[d] - b1[d];
+            xprime_hw2[d] = xprime_hw2[d] + dxHw2[d];
         }
+        T dxHw3[DIM];
         for (int d = 0; d < DIM; d++)
         {
-            xprime_hw3[d] = com[d] + c3[d] + sh_hw3[d];
+            dxHw3[d]      = c3[d] - c1[d];
+            xprime_hw3[d] = xprime_hw3[d] + dxHw3[d];
         }
-        /* 9 flops + 6 pbc flops */
+        /* 9 + 9 flops */
 
         transposeScatterStoreU<3>(xprime, ow1, xprime_ow1[XX], xprime_ow1[YY], xprime_ow1[ZZ]);
         transposeScatterStoreU<3>(xprime, hw2, xprime_hw2[XX], xprime_hw2[YY], xprime_hw2[ZZ]);
         transposeScatterStoreU<3>(xprime, hw3, xprime_hw3[XX], xprime_hw3[YY], xprime_hw3[ZZ]);
 
-        if (bCorrectVelocity || bCalcVirial)
+        if (bCorrectVelocity)
         {
-            T da[DIM], db[DIM], dc[DIM];
+            T v_ow1[DIM], v_hw2[DIM], v_hw3[DIM];
+
+            gatherLoadUTranspose<3>(v, ow1, &v_ow1[XX], &v_ow1[YY], &v_ow1[ZZ]);
+            gatherLoadUTranspose<3>(v, hw2, &v_hw2[XX], &v_hw2[YY], &v_hw2[ZZ]);
+            gatherLoadUTranspose<3>(v, hw3, &v_hw3[XX], &v_hw3[YY], &v_hw3[ZZ]);
+
+            /* Add the position correction divided by dt to the velocity */
             for (int d = 0; d < DIM; d++)
             {
-                da[d] = a3[d] - a1[d];
+                v_ow1[d] = gmx::fma(dxOw1[d], invdt, v_ow1[d]);
             }
             for (int d = 0; d < DIM; d++)
             {
-                db[d] = b3[d] - b1[d];
+                v_hw2[d] = gmx::fma(dxHw2[d], invdt, v_hw2[d]);
             }
             for (int d = 0; d < DIM; d++)
             {
-                dc[d] = c3[d] - c1[d];
+                v_hw3[d] = gmx::fma(dxHw3[d], invdt, v_hw3[d]);
             }
-            /* 9 flops */
+            /* 3*6 flops */
 
-            if (bCorrectVelocity)
-            {
-                T v_ow1[DIM], v_hw2[DIM], v_hw3[DIM];
+            transposeScatterStoreU<3>(v, ow1, v_ow1[XX], v_ow1[YY], v_ow1[ZZ]);
+            transposeScatterStoreU<3>(v, hw2, v_hw2[XX], v_hw2[YY], v_hw2[ZZ]);
+            transposeScatterStoreU<3>(v, hw3, v_hw3[XX], v_hw3[YY], v_hw3[ZZ]);
+        }
 
-                gatherLoadUTranspose<3>(v, ow1, &v_ow1[XX], &v_ow1[YY], &v_ow1[ZZ]);
-                gatherLoadUTranspose<3>(v, hw2, &v_hw2[XX], &v_hw2[YY], &v_hw2[ZZ]);
-                gatherLoadUTranspose<3>(v, hw3, &v_hw3[XX], &v_hw3[YY], &v_hw3[ZZ]);
+        if (bCalcVirial)
+        {
+            /* Filter out the non-local settles */
+            T filter = load<T>(settled.virfac() + i);
+            T mOf    = filter * mO;
+            T mHf    = filter * mH;
 
-                /* Add the position correction divided by dt to the velocity */
-                for (int d = 0; d < DIM; d++)
-                {
-                    v_ow1[d] = gmx::fma(da[d], invdt, v_ow1[d]);
-                }
-                for (int d = 0; d < DIM; d++)
-                {
-                    v_hw2[d] = gmx::fma(db[d], invdt, v_hw2[d]);
-                }
-                for (int d = 0; d < DIM; d++)
-                {
-                    v_hw3[d] = gmx::fma(dc[d], invdt, v_hw3[d]);
-                }
-                /* 3*6 flops */
+            T mdo[DIM], mdb[DIM], mdc[DIM];
 
-                transposeScatterStoreU<3>(v, ow1, v_ow1[XX], v_ow1[YY], v_ow1[ZZ]);
-                transposeScatterStoreU<3>(v, hw2, v_hw2[XX], v_hw2[YY], v_hw2[ZZ]);
-                transposeScatterStoreU<3>(v, hw3, v_hw3[XX], v_hw3[YY], v_hw3[ZZ]);
+            for (int d = 0; d < DIM; d++)
+            {
+                mdb[d] = mHf * dxHw2[d];
+                mdc[d] = mHf * dxHw3[d];
+                mdo[d] = mOf * dxOw1[d] + mdb[d] + mdc[d];
             }
 
-            if (bCalcVirial)
+            for (int d2 = 0; d2 < DIM; d2++)
             {
-                /* Filter out the non-local settles */
-                T filter = load<T>(settled->virfac + i);
-                T mOf    = filter * mO;
-                T mHf    = filter * mH;
-
-                T mdo[DIM], mdb[DIM], mdc[DIM];
-
                 for (int d = 0; d < DIM; d++)
                 {
-                    mdb[d] = mHf * db[d];
-                    mdc[d] = mHf * dc[d];
-                    mdo[d] = mOf * da[d] + mdb[d] + mdc[d];
-                }
-
-                for (int d2 = 0; d2 < DIM; d2++)
-                {
-                    for (int d = 0; d < DIM; d++)
-                    {
-                        sum_r_m_dr[d2][d] =
-                                sum_r_m_dr[d2][d]
-                                - (x_ow1[d2] * mdo[d] + dist21[d2] * mdb[d] + dist31[d2] * mdc[d]);
-                    }
+                    sum_r_m_dr[d2][d] =
+                            sum_r_m_dr[d2][d]
+                            - (x_ow1[d2] * mdo[d] + dist21[d2] * mdb[d] + dist31[d2] * mdc[d]);
                 }
-                /* 71 flops */
             }
+            /* 71 flops */
         }
     }
 
@@ -765,20 +700,20 @@ static void settleTemplate(const settledata* settled,
  * and instantiates the core template with instantiated booleans.
  */
 template<typename T, typename TypeBool, int packSize, typename TypePbc>
-static void settleTemplateWrapper(settledata* settled,
-                                  int         nthread,
-                                  int         thread,
-                                  TypePbc     pbc,
-                                  const real  x[],
-                                  real        xprime[],
-                                  real        invdt,
-                                  real*       v,
-                                  bool        bCalcVirial,
-                                  tensor      vir_r_m_dr,
-                                  bool*       bErrorHasOccurred)
+static void settleTemplateWrapper(const SettleData& settled,
+                                  int               nthread,
+                                  int               thread,
+                                  TypePbc           pbc,
+                                  const real        x[],
+                                  real              xprime[],
+                                  real              invdt,
+                                  real*             v,
+                                  bool              bCalcVirial,
+                                  tensor            vir_r_m_dr,
+                                  bool*             bErrorHasOccurred)
 {
     /* We need to assign settles to threads in groups of pack_size */
-    int numSettlePacks = (settled->nsettle + packSize - 1) / packSize;
+    int numSettlePacks = (settled.numSettles() + packSize - 1) / packSize;
     /* Round the end value up to give thread 0 more work */
     int settleStart = ((numSettlePacks * thread + nthread - 1) / nthread) * packSize;
     int settleEnd   = ((numSettlePacks * (thread + 1) + nthread - 1) / nthread) * packSize;
@@ -811,28 +746,32 @@ static void settleTemplateWrapper(settledata* settled,
     }
 }
 
-void csettle(settledata*  settled,
-             int          nthread,
-             int          thread,
-             const t_pbc* pbc,
-             const real   x[],
-             real         xprime[],
-             real         invdt,
-             real*        v,
-             bool         bCalcVirial,
-             tensor       vir_r_m_dr,
-             bool*        bErrorHasOccurred)
+void csettle(const SettleData&               settled,
+             int                             nthread,
+             int                             thread,
+             const t_pbc*                    pbc,
+             ArrayRefWithPadding<const RVec> x,
+             ArrayRefWithPadding<RVec>       xprime,
+             real                            invdt,
+             ArrayRefWithPadding<RVec>       v,
+             bool                            bCalcVirial,
+             tensor                          vir_r_m_dr,
+             bool*                           bErrorHasOccurred)
 {
+    const real* xPtr      = as_rvec_array(x.paddedArrayRef().data())[0];
+    real*       xprimePtr = as_rvec_array(xprime.paddedArrayRef().data())[0];
+    real*       vPtr      = as_rvec_array(v.paddedArrayRef().data())[0];
+
 #if GMX_SIMD_HAVE_REAL
-    if (settled->bUseSimd)
+    if (settled.useSimd())
     {
         /* Convert the pbc struct for SIMD */
         alignas(GMX_SIMD_ALIGNMENT) real pbcSimd[9 * GMX_SIMD_REAL_WIDTH];
         set_pbc_simd(pbc, pbcSimd);
 
         settleTemplateWrapper<SimdReal, SimdBool, GMX_SIMD_REAL_WIDTH, const real*>(
-                settled, nthread, thread, pbcSimd, x, xprime, invdt, v, bCalcVirial, vir_r_m_dr,
-                bErrorHasOccurred);
+                settled, nthread, thread, pbcSimd, xPtr, xprimePtr, invdt, vPtr, bCalcVirial,
+                vir_r_m_dr, bErrorHasOccurred);
     }
     else
 #endif
@@ -847,13 +786,13 @@ void csettle(settledata*  settled,
         }
         else
         {
-            set_pbc(&pbcNo, epbcNONE, nullptr);
+            set_pbc(&pbcNo, PbcType::No, nullptr);
             pbcNonNull = &pbcNo;
         }
 
-        settleTemplateWrapper<real, bool, 1, const t_pbc*>(settled, nthread, thread, pbcNonNull, x,
-                                                           xprime, invdt, v, bCalcVirial,
-                                                           vir_r_m_dr, bErrorHasOccurred);
+        settleTemplateWrapper<real, bool, 1, const t_pbc*>(settled, nthread, thread, pbcNonNull,
+                                                           &xPtr[0], &xprimePtr[0], invdt, &vPtr[0],
+                                                           bCalcVirial, vir_r_m_dr, bErrorHasOccurred);
     }
 }
 
index f503ac8481edce84c34316dddd775e18b25afd9f..4714bd669fcd4b9a2d78d254ec6dc54fb1dd87fb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_MDLIB_SETTLE_H
 
 #include "gromacs/topology/idef.h"
+#include "gromacs/utility/alignedallocator.h"
 
-struct gmx_cmap_t;
 struct gmx_mtop_t;
+struct InteractionList;
 struct t_inputrec;
-struct t_mdatoms;
 struct t_pbc;
 
 namespace gmx
 {
 
+template<typename>
+class ArrayRef;
+template<typename>
+class ArrayRefWithPadding;
 enum class ConstraintVariable : int;
 
-/* Abstract type for SETTLE that is defined only in the file that uses it */
-struct settledata;
+/* \brief Parameters for SETTLE algorithm.
+ *
+ * \todo Remove duplicates, check if recomputing makes more sense in some cases.
+ * \todo Move the projection parameters into separate structure.
+ */
+struct SettleParameters
+{
+    //! Mass of oxygen atom
+    real mO;
+    //! Mass of hydrogen atom
+    real mH;
+    //! Relative hydrogen mass (i.e. mH/(mO+2*mH))
+    real wh;
+    //! Target distance between oxygen and hydrogen
+    real dOH;
+    //! Target distance between hydrogen atoms
+    real dHH;
+    //! Double relative H mass times height of the H-O-H triangle.
+    real ra;
+    //! Height of the H-O-H triangle minus ra.
+    real rb;
+    //! Half the H-H distance.
+    real rc;
+    //! Reciprocal H-H distance
+    real irc2;
+
+    /* Parameters below are used for projection */
+    //! Reciprocal oxygen mass
+    real imO;
+    //! Reciprocal hydrogen mass
+    real imH;
+    //! Reciprocal O-H distance
+    real invdOH;
+    //! Reciprocal H-H distance (again)
+    real invdHH;
+    //! Reciprocal projection matrix
+    matrix invmat;
+};
+
+/*! \brief Computes and returns settle parameters.
+ *
+ * \param[in]  mO     Mass of oxygen atom
+ * \param[in]  mH     Mass of hydrogen atom
+ * \param[in]  invmO  Reciprocal mass of oxygen atom
+ * \param[in]  invmH  Reciprocal mass of hydrogen atom
+ * \param[in]  dOH    Target O-H bond length
+ * \param[in]  dHH    Target H-H bond length
+ */
+SettleParameters settleParameters(real mO, real mH, real dOH, real invmO, real invmH, real dHH);
+
+/*! \libinternal
+ * \brief Data for executing SETTLE constraining
+ */
+class SettleData
+{
+public:
+    //! Constructor
+    SettleData(const gmx_mtop_t& mtop);
+
+    //! Sets the constraints from the interaction list and the masses
+    void setConstraints(const InteractionList& il_settle,
+                        int                    numHomeAtoms,
+                        const real*            masses,
+                        const real*            inverseMasses);
+
+    //! Returns settle parameters for constraining coordinates and forces
+    const SettleParameters& parametersMassWeighted() const { return parametersMassWeighted_; }
+
+    //! Returns settle parameters for constraining forces: all masses are set to 1
+    const SettleParameters& parametersAllMasses1() const { return parametersAllMasses1_; }
+
+    //! Returns the number of SETTLEs
+    int numSettles() const { return numSettles_; }
+
+    //! Returns a pointer to the indices of oxygens for each SETTLE
+    const int* ow1() const { return ow1_.data(); }
+    //! Returns a pointer to the indices of the first hydrogen for each SETTLE
+    const int* hw2() const { return hw2_.data(); }
+    //! Returns a pointer to the indices of the second hydrogen for each SETTLE
+    const int* hw3() const { return hw3_.data(); }
+    //! Returns a pointer to the virial contribution factor (either 1 or 0) for each SETTLE
+    const real* virfac() const { return virfac_.data(); }
+
+    //! Returns whether we should use SIMD intrinsics code
+    bool useSimd() const { return useSimd_; }
+
+private:
+    //! Parameters for SETTLE for coordinates
+    SettleParameters parametersMassWeighted_;
+    //! Parameters with all masses 1, for forces
+    SettleParameters parametersAllMasses1_;
 
-/*! \brief Initializes and returns a structure with SETTLE parameters */
-settledata* settle_init(const gmx_mtop_t& mtop);
+    //! The number of settles on our rank
+    int numSettles_;
 
-//! Cleans up.
-void settle_free(settledata* settled);
+    //! Index to OW1 atoms, size numSettles_ + SIMD padding
+    std::vector<int, gmx::AlignedAllocator<int>> ow1_;
+    //! Index to HW2 atoms, size numSettles_ + SIMD padding
+    std::vector<int, gmx::AlignedAllocator<int>> hw2_;
+    //! Index to HW3 atoms, size numSettles_ + SIMD padding
+    std::vector<int, gmx::AlignedAllocator<int>> hw3_;
+    //! Virial factor 0 or 1, size numSettles_ + SIMD padding
+    std::vector<real, gmx::AlignedAllocator<real>> virfac_;
 
-/*! \brief Set up the indices for the settle constraints */
-void settle_set_constraints(settledata* settled, const t_ilist* il_settle, const t_mdatoms& mdatoms);
+    //! Tells whether we will use SIMD intrinsics code
+    bool useSimd_;
+};
 
 /*! \brief Constrain coordinates using SETTLE.
  * Can be called on any number of threads.
  */
-void csettle(settledata*  settled,          /* The SETTLE structure */
-             int          nthread,          /* The number of threads used */
-             int          thread,           /* Our thread index */
-             const t_pbc* pbc,              /* PBC data pointer, can be NULL */
-             const real   x[],              /* Reference coordinates */
-             real         xprime[],         /* New coords, to be settled */
-             real         invdt,            /* 1/delta_t */
-             real*        v,                /* Also constrain v if v!=NULL */
-             bool         bCalcVirial,      /* Calculate the virial contribution */
-             tensor       vir_r_m_dr,       /* sum r x m delta_r */
-             bool*        bErrorHasOccurred /* True if a settle error occurred */
+void csettle(const SettleData&               settled,     /* The SETTLE structure */
+             int                             nthread,     /* The number of threads used */
+             int                             thread,      /* Our thread index */
+             const t_pbc*                    pbc,         /* PBC data pointer, can be NULL */
+             ArrayRefWithPadding<const RVec> x,           /* Reference coordinates */
+             ArrayRefWithPadding<RVec>       xprime,      /* New coords, to be settled */
+             real                            invdt,       /* 1/delta_t */
+             ArrayRefWithPadding<RVec>       v,           /* Also constrain v if v!=NULL */
+             bool                            bCalcVirial, /* Calculate the virial contribution */
+             tensor                          vir_r_m_dr,  /* sum r x m delta_r */
+             bool*                           bErrorHasOccurred /* True if a settle error occurred */
 );
 
 /*! \brief Analytical algorithm to subtract the components of derivatives
  * of coordinates working on settle type constraint.
  */
-void settle_proj(settledata*        settled,
-                 ConstraintVariable econq,
-                 int                nsettle,
-                 const t_iatom      iatoms[],
-                 const t_pbc*       pbc, /* PBC data pointer, can be NULL  */
-                 const rvec         x[],
-                 rvec*              der,
-                 rvec*              derp,
-                 int                CalcVirAtomEnd,
-                 tensor             vir_r_m_dder);
+void settle_proj(const SettleData&    settled,
+                 ConstraintVariable   econq,
+                 int                  nsettle,
+                 const int            iatoms[],
+                 const t_pbc*         pbc, /* PBC data pointer, can be NULL  */
+                 ArrayRef<const RVec> x,
+                 ArrayRef<RVec>       der,
+                 ArrayRef<RVec>       derp,
+                 int                  CalcVirAtomEnd,
+                 tensor               vir_r_m_dder);
 
 } // namespace gmx
 
diff --git a/src/gromacs/mdlib/settle_cuda.cuh b/src/gromacs/mdlib/settle_cuda.cuh
deleted file mode 100644 (file)
index caef2db..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-/*! \internal \file
- *
- * \brief Declares class for CUDA implementation of SETTLE
- *
- * \author Artem Zhmurov <zhmurov@gmail.com>
- *
- * \ingroup module_mdlib
- */
-#ifndef GMX_MDLIB_SETTLE_CUDA_CUH
-#define GMX_MDLIB_SETTLE_CUDA_CUH
-
-#include "gmxpre.h"
-
-#include "gromacs/gpu_utils/gputraits.cuh"
-#include "gromacs/math/functions.h"
-#include "gromacs/math/invertmatrix.h"
-#include "gromacs/math/vec.h"
-#include "gromacs/mdtypes/mdatom.h"
-#include "gromacs/pbcutil/pbc.h"
-#include "gromacs/pbcutil/pbc_aiuc.h"
-#include "gromacs/topology/idef.h"
-#include "gromacs/topology/topology.h"
-
-namespace gmx
-{
-
-/* \brief Parameters for SETTLE algorithm.
- *
- * Following structure and subroutine are copy-pasted from the CPU version of SETTLE.
- * This is a temporary solution and they will be recycled in more appropriate way.
- * \todo Remove duplicates, check if recomputing makes more sense in some cases.
- * \todo Move the projection parameters into separate structure.
- */
-struct SettleParameters
-{
-    //! Mass of oxygen atom
-    float mO;
-    //! Mass of hydrogen atom
-    float mH;
-    //! Relative hydrogen mass (i.e. mH/(mO+2*mH))
-    float wh;
-    //! Target distance between oxygen and hydrogen
-    float dOH;
-    //! Target distance between hydrogen atoms
-    float dHH;
-    //! Double relative H mass times height of the H-O-H triangle.
-    float ra;
-    //! Height of the H-O-H triangle minus ra.
-    float rb;
-    //! Half the H-H distance.
-    float rc;
-    //! Reciprocal H-H distance
-    float irc2;
-
-    /* Parameters below are used for projection */
-    //! Reciprocal oxygen mass
-    float imO;
-    //! Reciprocal hydrogen mass
-    float imH;
-    //! Reciprocal O-H distance
-    float invdOH;
-    //! Reciprocal H-H distance (again)
-    float invdHH;
-    //! Reciprocal projection matrix
-    matrix invmat;
-};
-
-/*! \brief Initializes a projection matrix.
- *
- * \todo This is identical to to CPU code. Unification is needed.
- *
- * \param[in]  invmO                  Reciprocal oxygen mass
- * \param[in]  invmH                  Reciprocal hydrogen mass
- * \param[in]  dOH                    Target O-H bond length
- * \param[in]  dHH                    Target H-H bond length
- * \param[out] inverseCouplingMatrix  Inverse bond coupling matrix for the projection version of SETTLE
- */
-static void initializeProjectionMatrix(const real invmO,
-                                       const real invmH,
-                                       const real dOH,
-                                       const real dHH,
-                                       matrix     inverseCouplingMatrix)
-{
-    // We normalize the inverse masses with invmO for the matrix inversion.
-    // so we can keep using masses of almost zero for frozen particles,
-    // without running out of the float range in invertMatrix.
-    double invmORelative = 1.0;
-    double invmHRelative = invmH / static_cast<double>(invmO);
-    double distanceRatio = dHH / static_cast<double>(dOH);
-
-    /* Construct the constraint coupling matrix */
-    matrix mat;
-    mat[0][0] = invmORelative + invmHRelative;
-    mat[0][1] = invmORelative * (1.0 - 0.5 * gmx::square(distanceRatio));
-    mat[0][2] = invmHRelative * 0.5 * distanceRatio;
-    mat[1][1] = mat[0][0];
-    mat[1][2] = mat[0][2];
-    mat[2][2] = invmHRelative + invmHRelative;
-    mat[1][0] = mat[0][1];
-    mat[2][0] = mat[0][2];
-    mat[2][1] = mat[1][2];
-
-    gmx::invertMatrix(mat, inverseCouplingMatrix);
-
-    msmul(inverseCouplingMatrix, 1 / invmO, inverseCouplingMatrix);
-}
-
-/*! \brief Initializes settle parameters.
- *
- * \todo This is identical to the CPU code. Unification is needed.
- *
- * \param[out] p    SettleParameters structure to initialize
- * \param[in]  mO   Mass of oxygen atom
- * \param[in]  mH   Mass of hydrogen atom
- * \param[in]  dOH  Target O-H bond length
- * \param[in]  dHH  Target H-H bond length
- */
-gmx_unused // Temporary solution to keep clang happy
-        static void
-        initSettleParameters(SettleParameters* p, const real mO, const real mH, const real dOH, const real dHH)
-{
-    // We calculate parameters in double precision to minimize errors.
-    // The velocity correction applied during SETTLE coordinate constraining
-    // introduces a systematic error of approximately 1 bit per atom,
-    // depending on what the compiler does with the code.
-    double wohh;
-
-    real invmO = 1.0 / mO;
-    real invmH = 1.0 / mH;
-
-    p->mO     = mO;
-    p->mH     = mH;
-    wohh      = mO + 2.0 * mH;
-    p->wh     = mH / wohh;
-    p->dOH    = dOH;
-    p->dHH    = dHH;
-    double rc = dHH / 2.0;
-    double ra = 2.0 * mH * std::sqrt(dOH * dOH - rc * rc) / wohh;
-    p->rb     = std::sqrt(dOH * dOH - rc * rc) - ra;
-    p->rc     = rc;
-    p->ra     = ra;
-    p->irc2   = 1.0 / dHH;
-
-    // For projection: inverse masses and coupling matrix inversion
-    p->imO = invmO;
-    p->imH = invmH;
-
-    p->invdOH = 1.0 / dOH;
-    p->invdHH = 1.0 / dHH;
-
-    initializeProjectionMatrix(invmO, invmH, dOH, dHH, p->invmat);
-}
-
-/*! \internal \brief Class with interfaces and data for CUDA version of SETTLE. */
-class SettleCuda
-{
-
-public:
-    /*! \brief Create SETTLE object
-     *
-     *  Extracts masses for oxygen and hydrogen as well as the O-H and H-H target distances
-     *  from the topology data (mtop), check their values for consistency and calls the
-     *  following constructor.
-     *
-     * \param[in] mtop           Topology of the system to gen the masses for O and H atoms and
-     *                           target O-H and H-H distances. These values are also checked for
-     *                           consistency.
-     * \param[in] commandStream  Device stream to use.
-     */
-    SettleCuda(const gmx_mtop_t& mtop, CommandStream commandStream);
-
-    ~SettleCuda();
-
-    /*! \brief Apply SETTLE.
-     *
-     * Applies SETTLE to coordinates and velocities, stored on GPU. Data at pointers d_xp and
-     * d_v change in the GPU memory. The results are not automatically copied back to the CPU
-     * memory. Method uses this class data structures which should be updated when needed using
-     * update method.
-     *
-     * \param[in]     d_x               Coordinates before timestep (in GPU memory)
-     * \param[in,out] d_xp              Coordinates after timestep (in GPU memory). The
-     *                                  resulting constrained coordinates will be saved here.
-     * \param[in]     updateVelocities  If the velocities should be updated.
-     * \param[in,out] d_v               Velocities to update (in GPU memory, can be nullptr
-     *                                  if not updated)
-     * \param[in]     invdt             Reciprocal timestep (to scale Lagrange
-     *                                  multipliers when velocities are updated)
-     * \param[in]     computeVirial     If virial should be updated.
-     * \param[in,out] virialScaled      Scaled virial tensor to be updated.
-     */
-    void apply(const float3* d_x,
-               float3*       d_xp,
-               const bool    updateVelocities,
-               float3*       d_v,
-               const real    invdt,
-               const bool    computeVirial,
-               tensor        virialScaled);
-
-    /*! \brief
-     * Update data-structures (e.g. after NB search step).
-     *
-     * Updates the constraints data and copies it to the GPU. Should be
-     * called if the particles were sorted, redistributed between domains, etc.
-     * Does not recycle the data preparation routines from the CPU version.
-     * All three atoms from single water molecule should be handled by the same GPU.
-     *
-     * SETTLEs atom ID's is taken from idef.il[F_SETTLE].iatoms.
-     *
-     * \param[in] idef    System topology
-     * \param[in] md      Atoms data. Can be used to update masses if needed (not used now).
-     */
-    void set(const t_idef& idef, const t_mdatoms& md);
-
-    /*! \brief
-     * Update PBC data.
-     *
-     * Converts pbc data from t_pbc into the PbcAiuc format and stores the latter.
-     *
-     * \todo PBC should not be handled by constraints.
-     *
-     * \param[in] pbc The PBC data in t_pbc format.
-     */
-    void setPbc(const t_pbc* pbc);
-
-
-private:
-    //! CUDA stream
-    CommandStream commandStream_;
-    //! Periodic boundary data
-    PbcAiuc pbcAiuc_;
-
-    //! Scaled virial tensor (9 reals, GPU)
-    std::vector<float> h_virialScaled_;
-    //! Scaled virial tensor (9 reals, GPU)
-    float* d_virialScaled_;
-
-    //! Number of settles
-    int numSettles_ = 0;
-
-    //! Indexes of atoms (.x for oxygen, .y and.z for hydrogens, CPU)
-    std::vector<int3> h_atomIds_;
-    //! Indexes of atoms (.x for oxygen, .y and.z for hydrogens, GPU)
-    int3* d_atomIds_;
-    //! Current size of the array of atom IDs
-    int numAtomIds_ = -1;
-    //! Allocated size for the array of atom IDs
-    int numAtomIdsAlloc_ = -1;
-
-    //! Settle parameters
-    SettleParameters settleParameters_;
-};
-
-} // namespace gmx
-
-#endif
similarity index 82%
rename from src/gromacs/mdlib/settle_cuda.cu
rename to src/gromacs/mdlib/settle_gpu.cu
index d09bc9bf7a4dab51846f365ff503552ec13af1bd..8b95a9fec5df227b842e8cda895897cc705103f7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,9 +40,6 @@
  * using CUDA, including class initialization, data-structures management
  * and GPU kernel.
  *
- * \note Management of CUDA stream and periodic boundary should be unified with LINCS
- *       and removed from here once constraints are fully integrated with update module.
- * \todo Reconsider naming to use "gpu" suffix instead of "cuda".
  *
  * \author Artem Zhmurov <zhmurov@gmail.com>
  *
@@ -50,7 +47,7 @@
  */
 #include "gmxpre.h"
 
-#include "settle_cuda.cuh"
+#include "settle_gpu.cuh"
 
 #include <assert.h>
 #include <stdio.h>
@@ -89,10 +86,10 @@ constexpr static int c_maxThreadsPerBlock = c_threadsPerBlock;
  * \param [in]      gm_x             Coordinates of atoms before the timestep.
  * \param [in,out]  gm_x             Coordinates of atoms after the timestep (constrained coordinates will be
  *                                   saved here).
- * \param [in]      pbcAiuc          Periodic boundary conditions data.
  * \param [in]      invdt            Reciprocal timestep.
  * \param [in]      gm_v             Velocities of the particles.
  * \param [in]      gm_virialScaled  Virial tensor.
+ * \param [in]      pbcAiuc          Periodic boundary conditions data.
  */
 template<bool updateVelocities, bool computeVirial>
 __launch_bounds__(c_maxThreadsPerBlock) __global__
@@ -101,10 +98,10 @@ __launch_bounds__(c_maxThreadsPerBlock) __global__
                            const SettleParameters pars,
                            const float3* __restrict__ gm_x,
                            float3* __restrict__ gm_xprime,
-                           const PbcAiuc pbcAiuc,
-                           float         invdt,
+                           float invdt,
                            float3* __restrict__ gm_v,
-                           float* __restrict__ gm_virialScaled)
+                           float* __restrict__ gm_virialScaled,
+                           const PbcAiuc pbcAiuc)
 {
     /* ******************************************************************* */
     /*                                                                  ** */
@@ -115,6 +112,7 @@ __launch_bounds__(c_maxThreadsPerBlock) __global__
     /*    2006-10-16 Changed velocity update to use differences         ** */
     /*    2012-09-24 Use oxygen as reference instead of COM             ** */
     /*    2016-02    Complete rewrite of the code for SIMD              ** */
+    /*    2020-06    Completely remove use of COM to minimize drift     ** */
     /*                                                                  ** */
     /*    Reference for the SETTLE algorithm                            ** */
     /*           S. Miyamoto et al., J. Comp. Chem., 13, 952 (1992).    ** */
@@ -145,20 +143,13 @@ __launch_bounds__(c_maxThreadsPerBlock) __global__
         float3 dist31 = pbcDxAiuc(pbcAiuc, x_hw3, x_ow1);
         float3 doh2   = pbcDxAiuc(pbcAiuc, xprime_hw2, xprime_ow1);
 
-        float3 sh_hw2 = xprime_hw2 - (xprime_ow1 + doh2);
-        xprime_hw2    = xprime_hw2 - sh_hw2;
-
         float3 doh3 = pbcDxAiuc(pbcAiuc, xprime_hw3, xprime_ow1);
 
-        float3 sh_hw3 = xprime_hw3 - (xprime_ow1 + doh3);
-        xprime_hw3    = xprime_hw3 - sh_hw3;
+        float3 a1 = (-doh2 - doh3) * pars.wh;
 
-        float3 a1  = (-doh2 - doh3) * pars.wh;
-        float3 com = xprime_ow1 - a1;
+        float3 b1 = doh2 + a1;
 
-        float3 b1 = xprime_hw2 - com;
-
-        float3 c1 = xprime_hw3 - com;
+        float3 c1 = doh3 + a1;
 
         float xakszd = dist21.y * dist31.z - dist21.z * dist31.y;
         float yakszd = dist21.z * dist31.x - dist21.x * dist31.z;
@@ -277,59 +268,48 @@ __launch_bounds__(c_maxThreadsPerBlock) __global__
 
 
         /* Compute and store the corrected new coordinate */
-        xprime_ow1 = com + a3;
-        xprime_hw2 = com + b3 + sh_hw2;
-        xprime_hw3 = com + c3 + sh_hw3;
-
-        gm_xprime[indices.x] = xprime_ow1;
-        gm_xprime[indices.y] = xprime_hw2;
-        gm_xprime[indices.z] = xprime_hw3;
+        const float3 dxOw1 = a3 - a1;
+        const float3 dxHw2 = b3 - b1;
+        const float3 dxHw3 = c3 - c1;
 
+        gm_xprime[indices.x] = xprime_ow1 + dxOw1;
+        gm_xprime[indices.y] = xprime_hw2 + dxHw2;
+        gm_xprime[indices.z] = xprime_hw3 + dxHw3;
 
-        if (updateVelocities || computeVirial)
+        if (updateVelocities)
         {
+            float3 v_ow1 = gm_v[indices.x];
+            float3 v_hw2 = gm_v[indices.y];
+            float3 v_hw3 = gm_v[indices.z];
+
+            /* Add the position correction divided by dt to the velocity */
+            v_ow1 = dxOw1 * invdt + v_ow1;
+            v_hw2 = dxHw2 * invdt + v_hw2;
+            v_hw3 = dxHw3 * invdt + v_hw3;
+
+            gm_v[indices.x] = v_ow1;
+            gm_v[indices.y] = v_hw2;
+            gm_v[indices.z] = v_hw3;
+        }
 
-            float3 da = a3 - a1;
-            float3 db = b3 - b1;
-            float3 dc = c3 - c1;
-
-            if (updateVelocities)
-            {
-
-                float3 v_ow1 = gm_v[indices.x];
-                float3 v_hw2 = gm_v[indices.y];
-                float3 v_hw3 = gm_v[indices.z];
-
-                /* Add the position correction divided by dt to the velocity */
-                v_ow1 = da * invdt + v_ow1;
-                v_hw2 = db * invdt + v_hw2;
-                v_hw3 = dc * invdt + v_hw3;
-
-                gm_v[indices.x] = v_ow1;
-                gm_v[indices.y] = v_hw2;
-                gm_v[indices.z] = v_hw3;
-            }
-
-            if (computeVirial)
-            {
-
-                float3 mdb = pars.mH * db;
-                float3 mdc = pars.mH * dc;
-                float3 mdo = pars.mO * da + mdb + mdc;
-
-                sm_threadVirial[0 * blockDim.x + threadIdx.x] =
-                        -(x_ow1.x * mdo.x + dist21.x * mdb.x + dist31.x * mdc.x);
-                sm_threadVirial[1 * blockDim.x + threadIdx.x] =
-                        -(x_ow1.x * mdo.y + dist21.x * mdb.y + dist31.x * mdc.y);
-                sm_threadVirial[2 * blockDim.x + threadIdx.x] =
-                        -(x_ow1.x * mdo.z + dist21.x * mdb.z + dist31.x * mdc.z);
-                sm_threadVirial[3 * blockDim.x + threadIdx.x] =
-                        -(x_ow1.y * mdo.y + dist21.y * mdb.y + dist31.y * mdc.y);
-                sm_threadVirial[4 * blockDim.x + threadIdx.x] =
-                        -(x_ow1.y * mdo.z + dist21.y * mdb.z + dist31.y * mdc.z);
-                sm_threadVirial[5 * blockDim.x + threadIdx.x] =
-                        -(x_ow1.z * mdo.z + dist21.z * mdb.z + dist31.z * mdc.z);
-            }
+        if (computeVirial)
+        {
+            float3 mdb = pars.mH * dxHw2;
+            float3 mdc = pars.mH * dxHw3;
+            float3 mdo = pars.mO * dxOw1 + mdb + mdc;
+
+            sm_threadVirial[0 * blockDim.x + threadIdx.x] =
+                    -(x_ow1.x * mdo.x + dist21.x * mdb.x + dist31.x * mdc.x);
+            sm_threadVirial[1 * blockDim.x + threadIdx.x] =
+                    -(x_ow1.x * mdo.y + dist21.x * mdb.y + dist31.x * mdc.y);
+            sm_threadVirial[2 * blockDim.x + threadIdx.x] =
+                    -(x_ow1.x * mdo.z + dist21.x * mdb.z + dist31.x * mdc.z);
+            sm_threadVirial[3 * blockDim.x + threadIdx.x] =
+                    -(x_ow1.y * mdo.y + dist21.y * mdb.y + dist31.y * mdc.y);
+            sm_threadVirial[4 * blockDim.x + threadIdx.x] =
+                    -(x_ow1.y * mdo.z + dist21.y * mdb.z + dist31.y * mdc.z);
+            sm_threadVirial[5 * blockDim.x + threadIdx.x] =
+                    -(x_ow1.z * mdo.z + dist21.z * mdb.z + dist31.z * mdc.z);
         }
     }
     else
@@ -415,13 +395,14 @@ inline auto getSettleKernelPtr(const bool updateVelocities, const bool computeVi
     return kernelPtr;
 }
 
-void SettleCuda::apply(const float3* d_x,
-                       float3*       d_xp,
-                       const bool    updateVelocities,
-                       float3*       d_v,
-                       const real    invdt,
-                       const bool    computeVirial,
-                       tensor        virialScaled)
+void SettleGpu::apply(const float3* d_x,
+                      float3*       d_xp,
+                      const bool    updateVelocities,
+                      float3*       d_v,
+                      const real    invdt,
+                      const bool    computeVirial,
+                      tensor        virialScaled,
+                      const PbcAiuc pbcAiuc)
 {
 
     ensureNoPendingCudaError("In CUDA version SETTLE");
@@ -436,7 +417,7 @@ void SettleCuda::apply(const float3* d_x,
     {
         // Fill with zeros so the values can be reduced to it
         // Only 6 values are needed because virial is symmetrical
-        clearDeviceBufferAsync(&d_virialScaled_, 0, 6, commandStream_);
+        clearDeviceBufferAsync(&d_virialScaled_, 0, 6, deviceStream_);
     }
 
     auto kernelPtr = getSettleKernelPtr(updateVelocities, computeVirial);
@@ -457,17 +438,17 @@ void SettleCuda::apply(const float3* d_x,
     {
         config.sharedMemorySize = 0;
     }
-    config.stream = commandStream_;
 
     const auto kernelArgs = prepareGpuKernelArguments(kernelPtr, config, &numSettles_, &d_atomIds_,
-                                                      &settleParameters_, &d_x, &d_xp, &pbcAiuc_,
-                                                      &invdt, &d_v, &d_virialScaled_);
+                                                      &settleParameters_, &d_x, &d_xp, &invdt, &d_v,
+                                                      &d_virialScaled_, &pbcAiuc);
 
-    launchGpuKernel(kernelPtr, config, nullptr, "settle_kernel<updateVelocities, computeVirial>", kernelArgs);
+    launchGpuKernel(kernelPtr, config, deviceStream_, nullptr,
+                    "settle_kernel<updateVelocities, computeVirial>", kernelArgs);
 
     if (computeVirial)
     {
-        copyFromDeviceBuffer(h_virialScaled_.data(), &d_virialScaled_, 0, 6, commandStream_,
+        copyFromDeviceBuffer(h_virialScaled_.data(), &d_virialScaled_, 0, 6, deviceStream_,
                              GpuApiCallBehavior::Sync, nullptr);
 
         // Mapping [XX, XY, XZ, YY, YZ, ZZ] internal format to a tensor object
@@ -487,8 +468,9 @@ void SettleCuda::apply(const float3* d_x,
     return;
 }
 
-SettleCuda::SettleCuda(const gmx_mtop_t& mtop, CommandStream commandStream) :
-    commandStream_(commandStream)
+SettleGpu::SettleGpu(const gmx_mtop_t& mtop, const DeviceContext& deviceContext, const DeviceStream& deviceStream) :
+    deviceContext_(deviceContext),
+    deviceStream_(deviceStream)
 {
     static_assert(sizeof(real) == sizeof(float),
                   "Real numbers should be in single precision in GPU code.");
@@ -586,13 +568,13 @@ SettleCuda::SettleCuda(const gmx_mtop_t& mtop, CommandStream commandStream) :
     real dOH = mtop.ffparams.iparams[settle_type].settle.doh;
     real dHH = mtop.ffparams.iparams[settle_type].settle.dhh;
 
-    initSettleParameters(&settleParameters_, mO, mH, dOH, dHH);
+    settleParameters_ = settleParameters(mO, mH, 1.0 / mO, 1.0 / mH, dOH, dHH);
 
-    allocateDeviceBuffer(&d_virialScaled_, 6, nullptr);
+    allocateDeviceBuffer(&d_virialScaled_, 6, deviceContext_);
     h_virialScaled_.resize(6);
 }
 
-SettleCuda::~SettleCuda()
+SettleGpu::~SettleGpu()
 {
     // Early exit if there is no settles
     if (numSettles_ == 0)
@@ -606,14 +588,14 @@ SettleCuda::~SettleCuda()
     }
 }
 
-void SettleCuda::set(const t_idef& idef, const t_mdatoms gmx_unused& md)
+void SettleGpu::set(const InteractionDefinitions& idef)
 {
-    const int nral1     = 1 + NRAL(F_SETTLE);
-    t_ilist   il_settle = idef.il[F_SETTLE];
-    t_iatom*  iatoms    = il_settle.iatoms;
-    numSettles_         = il_settle.nr / nral1;
+    const int              nral1     = 1 + NRAL(F_SETTLE);
+    const InteractionList& il_settle = idef.il[F_SETTLE];
+    ArrayRef<const int>    iatoms    = il_settle.iatoms;
+    numSettles_                      = il_settle.size() / nral1;
 
-    reallocateDeviceBuffer(&d_atomIds_, numSettles_, &numAtomIds_, &numAtomIdsAlloc_, nullptr);
+    reallocateDeviceBuffer(&d_atomIds_, numSettles_, &numAtomIds_, &numAtomIdsAlloc_, deviceContext_);
     h_atomIds_.resize(numSettles_);
     for (int i = 0; i < numSettles_; i++)
     {
@@ -623,13 +605,8 @@ void SettleCuda::set(const t_idef& idef, const t_mdatoms gmx_unused& md)
         settler.z        = iatoms[i * nral1 + 3]; // Second hydrogen index
         h_atomIds_.at(i) = settler;
     }
-    copyToDeviceBuffer(&d_atomIds_, h_atomIds_.data(), 0, numSettles_, commandStream_,
+    copyToDeviceBuffer(&d_atomIds_, h_atomIds_.data(), 0, numSettles_, deviceStream_,
                        GpuApiCallBehavior::Sync, nullptr);
 }
 
-void SettleCuda::setPbc(const t_pbc* pbc)
-{
-    setPbcAiuc(pbc->ndim_ePBC, pbc->box, &pbcAiuc_);
-}
-
 } // namespace gmx
diff --git a/src/gromacs/mdlib/settle_gpu.cuh b/src/gromacs/mdlib/settle_gpu.cuh
new file mode 100644 (file)
index 0000000..3a96ec4
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares class for GPU implementation of SETTLE
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_SETTLE_GPU_CUH
+#define GMX_MDLIB_SETTLE_GPU_CUH
+
+#include "gmxpre.h"
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/gputraits.cuh"
+#include "gromacs/math/functions.h"
+#include "gromacs/math/invertmatrix.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/mdlib/settle.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/pbcutil/pbc_aiuc.h"
+#include "gromacs/topology/topology.h"
+
+class InteractionDefinitions;
+
+namespace gmx
+{
+
+/*! \internal \brief Class with interfaces and data for GPU version of SETTLE. */
+class SettleGpu
+{
+
+public:
+    /*! \brief Create SETTLE object
+     *
+     *  Extracts masses for oxygen and hydrogen as well as the O-H and H-H target distances
+     *  from the topology data (mtop), check their values for consistency and calls the
+     *  following constructor.
+     *
+     * \param[in] mtop           Topology of the system to gen the masses for O and H atoms and
+     *                           target O-H and H-H distances. These values are also checked for
+     *                           consistency.
+     * \param[in] deviceContext  Device context (dummy in CUDA).
+     * \param[in] deviceStream   Device stream to use.
+     */
+    SettleGpu(const gmx_mtop_t& mtop, const DeviceContext& deviceContext, const DeviceStream& deviceStream);
+
+    ~SettleGpu();
+
+    /*! \brief Apply SETTLE.
+     *
+     * Applies SETTLE to coordinates and velocities, stored on GPU. Data at pointers d_xp and
+     * d_v change in the GPU memory. The results are not automatically copied back to the CPU
+     * memory. Method uses this class data structures which should be updated when needed using
+     * update method.
+     *
+     * \param[in]     d_x               Coordinates before timestep (in GPU memory)
+     * \param[in,out] d_xp              Coordinates after timestep (in GPU memory). The
+     *                                  resulting constrained coordinates will be saved here.
+     * \param[in]     updateVelocities  If the velocities should be updated.
+     * \param[in,out] d_v               Velocities to update (in GPU memory, can be nullptr
+     *                                  if not updated)
+     * \param[in]     invdt             Reciprocal timestep (to scale Lagrange
+     *                                  multipliers when velocities are updated)
+     * \param[in]     computeVirial     If virial should be updated.
+     * \param[in,out] virialScaled      Scaled virial tensor to be updated.
+     * \param[in]     pbcAiuc           PBC data.
+     */
+    void apply(const float3* d_x,
+               float3*       d_xp,
+               const bool    updateVelocities,
+               float3*       d_v,
+               const real    invdt,
+               const bool    computeVirial,
+               tensor        virialScaled,
+               const PbcAiuc pbcAiuc);
+
+    /*! \brief
+     * Update data-structures (e.g. after NB search step).
+     *
+     * Updates the constraints data and copies it to the GPU. Should be
+     * called if the particles were sorted, redistributed between domains, etc.
+     * Does not recycle the data preparation routines from the CPU version.
+     * All three atoms from single water molecule should be handled by the same GPU.
+     *
+     * SETTLEs atom ID's is taken from idef.il[F_SETTLE].iatoms.
+     *
+     * \param[in] idef    System topology
+     */
+    void set(const InteractionDefinitions& idef);
+
+private:
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! GPU stream
+    const DeviceStream& deviceStream_;
+
+    //! Scaled virial tensor (9 reals, GPU)
+    std::vector<float> h_virialScaled_;
+    //! Scaled virial tensor (9 reals, GPU)
+    float* d_virialScaled_;
+
+    //! Number of settles
+    int numSettles_ = 0;
+
+    //! Indexes of atoms (.x for oxygen, .y and.z for hydrogens, CPU)
+    std::vector<int3> h_atomIds_;
+    //! Indexes of atoms (.x for oxygen, .y and.z for hydrogens, GPU)
+    int3* d_atomIds_;
+    //! Current size of the array of atom IDs
+    int numAtomIds_ = -1;
+    //! Allocated size for the array of atom IDs
+    int numAtomIdsAlloc_ = -1;
+
+    //! Settle parameters
+    SettleParameters settleParameters_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_SETTLE_GPU_CUH
index 3af852d212d7793c061b96f20accd9631ecff52a..30c965214bd1e2a244f493f7c9006440587e2a3e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,7 +51,6 @@
 
 #include <algorithm>
 
-#include "gromacs/domdec/domdec_struct.h"
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/vec.h"
@@ -58,7 +58,7 @@
 #include "gromacs/mdlib/splitter.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/invblock.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
 namespace gmx
 {
 
-struct shakedata
-{
-    rvec* rij;
-    real* half_of_reduced_mass;
-    real* distance_squared_tolerance;
-    real* constraint_distance_squared;
-    int   nalloc;
-    /* SOR stuff */
-    real delta;
-    real omega;
-    real gamma;
-    int  nblocks;       /* The number of SHAKE blocks         */
-    int* sblock;        /* The SHAKE blocks                   */
-    int  sblock_nalloc; /* The allocation size of sblock      */
-    /*! \brief Scaled Lagrange multiplier for each constraint.
-     *
-     * Value is -2 * eta from p. 336 of the paper, divided by the
-     * constraint distance. */
-    real* scaled_lagrange_multiplier;
-    int   lagr_nalloc; /* The allocation size of scaled_lagrange_multiplier */
-};
-
-shakedata* shake_init()
-{
-    shakedata* d;
-
-    snew(d, 1);
-
-    d->nalloc                      = 0;
-    d->rij                         = nullptr;
-    d->half_of_reduced_mass        = nullptr;
-    d->distance_squared_tolerance  = nullptr;
-    d->constraint_distance_squared = nullptr;
-
-    /* SOR initialization */
-    d->delta = 0.1;
-    d->omega = 1.0;
-    d->gamma = 1000000;
-
-    return d;
-}
-
-void done_shake(shakedata* d)
-{
-    sfree(d->rij);
-    sfree(d->half_of_reduced_mass);
-    sfree(d->distance_squared_tolerance);
-    sfree(d->constraint_distance_squared);
-    sfree(d->sblock);
-    sfree(d->scaled_lagrange_multiplier);
-    sfree(d);
-}
-
 typedef struct
 {
     int iatom[3];
@@ -171,44 +118,38 @@ static void pr_sortblock(FILE* fp, const char* title, int nsb, t_sortblock sb[])
 //! Reallocates a vector.
 static void resizeLagrangianData(shakedata* shaked, int ncons)
 {
-    if (ncons > shaked->lagr_nalloc)
-    {
-        shaked->lagr_nalloc = over_alloc_dd(ncons);
-        srenew(shaked->scaled_lagrange_multiplier, shaked->lagr_nalloc);
-    }
+    shaked->scaled_lagrange_multiplier.resize(ncons);
 }
 
-void make_shake_sblock_serial(shakedata* shaked, const t_idef* idef, const t_mdatoms& md)
+void make_shake_sblock_serial(shakedata* shaked, InteractionDefinitions* idef, const int numAtoms)
 {
-    int          i, j, m, ncons;
+    int          i, m, ncons;
     int          bstart, bnr;
     t_blocka     sblocks;
     t_sortblock* sb;
-    t_iatom*     iatom;
     int*         inv_sblock;
 
     /* Since we are processing the local topology,
      * the F_CONSTRNC ilist has been concatenated to the F_CONSTR ilist.
      */
-    ncons = idef->il[F_CONSTR].nr / 3;
+    ncons = idef->il[F_CONSTR].size() / 3;
 
     init_blocka(&sblocks);
     sfree(sblocks.index); // To solve memory leak
-    gen_sblocks(nullptr, 0, md.homenr, idef, &sblocks, FALSE);
+    gen_sblocks(nullptr, numAtoms, *idef, &sblocks, FALSE);
 
     /*
        bstart=(idef->nodeid > 0) ? blocks->multinr[idef->nodeid-1] : 0;
        nblocks=blocks->multinr[idef->nodeid] - bstart;
      */
-    bstart          = 0;
-    shaked->nblocks = sblocks.nr;
+    bstart = 0;
     if (debug)
     {
-        fprintf(debug, "ncons: %d, bstart: %d, nblocks: %d\n", ncons, bstart, shaked->nblocks);
+        fprintf(debug, "ncons: %d, bstart: %d, nblocks: %d\n", ncons, bstart, sblocks.nr);
     }
 
     /* Calculate block number for each atom */
-    inv_sblock = make_invblocka(&sblocks, md.nr);
+    inv_sblock = make_invblocka(&sblocks, numAtoms);
 
     done_blocka(&sblocks);
 
@@ -216,7 +157,7 @@ void make_shake_sblock_serial(shakedata* shaked, const t_idef* idef, const t_mda
      * sort the constraints in order of the sblock number
      * and the atom numbers, really sorting a segment of the array!
      */
-    iatom = idef->il[F_CONSTR].iatoms;
+    int* iatom = idef->il[F_CONSTR].iatoms.data();
     snew(sb, ncons);
     for (i = 0; (i < ncons); i++, iatom += 3)
     {
@@ -241,7 +182,7 @@ void make_shake_sblock_serial(shakedata* shaked, const t_idef* idef, const t_mda
         pr_sortblock(debug, "After sorting", ncons, sb);
     }
 
-    iatom = idef->il[F_CONSTR].iatoms;
+    iatom = idef->il[F_CONSTR].iatoms.data();
     for (i = 0; (i < ncons); i++, iatom += 3)
     {
         for (m = 0; (m < 3); m++)
@@ -250,62 +191,37 @@ void make_shake_sblock_serial(shakedata* shaked, const t_idef* idef, const t_mda
         }
     }
 
-    j = 0;
-    snew(shaked->sblock, shaked->nblocks + 1);
+    shaked->sblock.clear();
     bnr = -2;
     for (i = 0; (i < ncons); i++)
     {
         if (sb[i].blocknr != bnr)
         {
-            bnr                 = sb[i].blocknr;
-            shaked->sblock[j++] = 3 * i;
+            bnr = sb[i].blocknr;
+            shaked->sblock.push_back(3 * i);
         }
     }
     /* Last block... */
-    shaked->sblock[j++] = 3 * ncons;
+    shaked->sblock.push_back(3 * ncons);
 
-    if (j != (shaked->nblocks + 1))
-    {
-        fprintf(stderr, "bstart: %d\n", bstart);
-        fprintf(stderr, "j: %d, nblocks: %d, ncons: %d\n", j, shaked->nblocks, ncons);
-        for (i = 0; (i < ncons); i++)
-        {
-            fprintf(stderr, "i: %5d  sb[i].blocknr: %5d\n", i, sb[i].blocknr);
-        }
-        for (j = 0; (j <= shaked->nblocks); j++)
-        {
-            fprintf(stderr, "sblock[%3d]=%5d\n", j, shaked->sblock[j]);
-        }
-        gmx_fatal(FARGS,
-                  "DEATH HORROR: "
-                  "sblocks does not match idef->il[F_CONSTR]");
-    }
     sfree(sb);
     sfree(inv_sblock);
     resizeLagrangianData(shaked, ncons);
 }
 
-// TODO: Check if this code is useful. It might never be called.
-void make_shake_sblock_dd(shakedata* shaked, const t_ilist* ilcon, const gmx_domdec_t* dd)
+void make_shake_sblock_dd(shakedata* shaked, const InteractionList& ilcon)
 {
-    int      ncons, c, cg;
-    t_iatom* iatom;
+    int ncons, c, cg;
 
-    if (dd->ncg_home + 1 > shaked->sblock_nalloc)
-    {
-        shaked->sblock_nalloc = over_alloc_dd(dd->ncg_home + 1);
-        srenew(shaked->sblock, shaked->sblock_nalloc);
-    }
-
-    ncons           = ilcon->nr / 3;
-    iatom           = ilcon->iatoms;
-    shaked->nblocks = 0;
-    cg              = 0;
+    ncons            = ilcon.size() / 3;
+    const int* iatom = ilcon.iatoms.data();
+    shaked->sblock.clear();
+    cg = 0;
     for (c = 0; c < ncons; c++)
     {
         if (c == 0 || iatom[1] >= cg + 1)
         {
-            shaked->sblock[shaked->nblocks++] = 3 * c;
+            shaked->sblock.push_back(3 * c);
             while (iatom[1] >= cg + 1)
             {
                 cg++;
@@ -313,7 +229,7 @@ void make_shake_sblock_dd(shakedata* shaked, const t_ilist* ilcon, const gmx_dom
         }
         iatom += 3;
     }
-    shaked->sblock[shaked->nblocks] = 3 * ncons;
+    shaked->sblock.push_back(3 * ncons);
     resizeLagrangianData(shaked, ncons);
 }
 
@@ -326,94 +242,88 @@ void make_shake_sblock_dd(shakedata* shaked, const t_ilist* ilcon, const gmx_dom
  * The algorithm here is based section five of Ryckaert, Ciccotti and
  * Berendsen, J Comp Phys, 23, 327, 1977.
  *
- * \param[in]    iatom                         Mini-topology of triples of constraint type (unused in this
- *                                             function) and indices of the two atoms involved
+ * \param[in]    iatom                         Mini-topology of triplets of constraint type (unused
+ *                                             in this function) and indices of two atoms involved
  * \param[in]    ncon                          Number of constraints
  * \param[out]   nnit                          Number of iterations performed
  * \param[in]    maxnit                        Maximum number of iterations permitted
  * \param[in]    constraint_distance_squared   The objective value for each constraint
- * \param[inout] positions                     The initial (and final) values of the positions of all atoms
+ * \param[inout] positions                     The initial (and final) values of the positions
+ *                                             of all atoms
+ * \param[in]    pbc                           PBC information
  * \param[in]    initial_displacements         The initial displacements of each constraint
  * \param[in]    half_of_reduced_mass          Half of the reduced mass for each constraint
  * \param[in]    omega                         SHAKE over-relaxation factor (set non-1.0 by
- *                                             using shake-sor=yes in the .mdp, but there is no documentation anywhere)
+ *                                             using shake-sor=yes in the .mdp,
+ *                                             but there is no documentation anywhere)
  * \param[in]    invmass                       Inverse mass of each atom
  * \param[in]    distance_squared_tolerance    Multiplicative tolerance on the difference in the
  *                                             square of the constrained distance (see code)
- * \param[out]   scaled_lagrange_multiplier    Scaled Lagrange multiplier for each constraint (-2 * eta from p. 336
- *                                             of the paper, divided by the constraint distance)
- * \param[out]   nerror                        Zero upon success, returns one more than the index of the
- *                                             problematic constraint if the input was malformed
+ * \param[out]   scaled_lagrange_multiplier    Scaled Lagrange multiplier for each constraint
+ *                                             (-2 * eta from p. 336 of the paper, divided by
+ *                                             the constraint distance)
+ * \param[out]   nerror                        Zero upon success, returns one more than the index of
+ *                                             the problematic constraint if the input was malformed
  *
  * \todo Make SHAKE use better data structures, in particular for iatom. */
-void cshake(const int  iatom[],
-            int        ncon,
-            int*       nnit,
-            int        maxnit,
-            const real constraint_distance_squared[],
-            real       positions[],
-            const real initial_displacements[],
-            const real half_of_reduced_mass[],
-            real       omega,
-            const real invmass[],
-            const real distance_squared_tolerance[],
-            real       scaled_lagrange_multiplier[],
-            int*       nerror)
+void cshake(const int            iatom[],
+            int                  ncon,
+            int*                 nnit,
+            int                  maxnit,
+            ArrayRef<const real> constraint_distance_squared,
+            ArrayRef<RVec>       positions,
+            const t_pbc*         pbc,
+            ArrayRef<const RVec> initial_displacements,
+            ArrayRef<const real> half_of_reduced_mass,
+            real                 omega,
+            const real           invmass[],
+            ArrayRef<const real> distance_squared_tolerance,
+            ArrayRef<real>       scaled_lagrange_multiplier,
+            int*                 nerror)
 {
     /* default should be increased! MRS 8/4/2009 */
     const real mytol = 1e-10;
 
-    int  ll, i, j, i3, j3, l3;
-    int  ix, iy, iz, jx, jy, jz;
-    real r_dot_r_prime;
-    real constraint_distance_squared_ll;
-    real r_prime_squared;
-    real scaled_lagrange_multiplier_ll;
-    real r_prime_x, r_prime_y, r_prime_z, diff, im, jm;
-    real xh, yh, zh, rijx, rijy, rijz;
-    int  nit, error, nconv;
-    real iconvf;
-
     // TODO nconv is used solely as a boolean, so we should write the
     // code like that
-    error = 0;
-    nconv = 1;
+    int error = 0;
+    int nconv = 1;
+    int nit;
     for (nit = 0; (nit < maxnit) && (nconv != 0) && (error == 0); nit++)
     {
         nconv = 0;
-        for (ll = 0; (ll < ncon) && (error == 0); ll++)
+        for (int ll = 0; (ll < ncon) && (error == 0); ll++)
         {
-            l3   = 3 * ll;
-            rijx = initial_displacements[l3 + XX];
-            rijy = initial_displacements[l3 + YY];
-            rijz = initial_displacements[l3 + ZZ];
-            i    = iatom[l3 + 1];
-            j    = iatom[l3 + 2];
-            i3   = 3 * i;
-            j3   = 3 * j;
-            ix   = i3 + XX;
-            iy   = i3 + YY;
-            iz   = i3 + ZZ;
-            jx   = j3 + XX;
-            jy   = j3 + YY;
-            jz   = j3 + ZZ;
+            const int  l3   = 3 * ll;
+            const real rijx = initial_displacements[ll][XX];
+            const real rijy = initial_displacements[ll][YY];
+            const real rijz = initial_displacements[ll][ZZ];
+            const int  i    = iatom[l3 + 1];
+            const int  j    = iatom[l3 + 2];
 
             /* Compute r prime between atoms i and j, which is the
                displacement *before* this update stage */
-            r_prime_x       = positions[ix] - positions[jx];
-            r_prime_y       = positions[iy] - positions[jy];
-            r_prime_z       = positions[iz] - positions[jz];
-            r_prime_squared = (r_prime_x * r_prime_x + r_prime_y * r_prime_y + r_prime_z * r_prime_z);
-            constraint_distance_squared_ll = constraint_distance_squared[ll];
-            diff                           = constraint_distance_squared_ll - r_prime_squared;
+            rvec r_prime;
+            if (pbc)
+            {
+                pbc_dx(pbc, positions[i], positions[j], r_prime);
+            }
+            else
+            {
+                rvec_sub(positions[i], positions[j], r_prime);
+            }
+            const real r_prime_squared                = norm2(r_prime);
+            const real constraint_distance_squared_ll = constraint_distance_squared[ll];
+            const real diff = constraint_distance_squared_ll - r_prime_squared;
 
             /* iconvf is less than 1 when the error is smaller than a bound */
-            iconvf = fabs(diff) * distance_squared_tolerance[ll];
+            const real iconvf = std::abs(diff) * distance_squared_tolerance[ll];
 
-            if (iconvf > 1.0)
+            if (iconvf > 1.0_real)
             {
-                nconv         = static_cast<int>(iconvf);
-                r_dot_r_prime = (rijx * r_prime_x + rijy * r_prime_y + rijz * r_prime_z);
+                nconv = static_cast<int>(iconvf);
+                const real r_dot_r_prime =
+                        (rijx * r_prime[XX] + rijy * r_prime[YY] + rijz * r_prime[ZZ]);
 
                 if (r_dot_r_prime < constraint_distance_squared_ll * mytol)
                 {
@@ -423,19 +333,20 @@ void cshake(const int  iatom[],
                 {
                     /* The next line solves equation 5.6 (neglecting
                        the term in g^2), for g */
-                    scaled_lagrange_multiplier_ll = omega * diff * half_of_reduced_mass[ll] / r_dot_r_prime;
+                    real scaled_lagrange_multiplier_ll =
+                            omega * diff * half_of_reduced_mass[ll] / r_dot_r_prime;
                     scaled_lagrange_multiplier[ll] += scaled_lagrange_multiplier_ll;
-                    xh = rijx * scaled_lagrange_multiplier_ll;
-                    yh = rijy * scaled_lagrange_multiplier_ll;
-                    zh = rijz * scaled_lagrange_multiplier_ll;
-                    im = invmass[i];
-                    jm = invmass[j];
-                    positions[ix] += xh * im;
-                    positions[iy] += yh * im;
-                    positions[iz] += zh * im;
-                    positions[jx] -= xh * jm;
-                    positions[jy] -= yh * jm;
-                    positions[jz] -= zh * jm;
+                    const real xh = rijx * scaled_lagrange_multiplier_ll;
+                    const real yh = rijy * scaled_lagrange_multiplier_ll;
+                    const real zh = rijz * scaled_lagrange_multiplier_ll;
+                    const real im = invmass[i];
+                    const real jm = invmass[j];
+                    positions[i][XX] += xh * im;
+                    positions[i][YY] += yh * im;
+                    positions[i][ZZ] += zh * im;
+                    positions[j][XX] -= xh * jm;
+                    positions[j][YY] -= yh * jm;
+                    positions[j][ZZ] -= zh * jm;
                 }
             }
         }
@@ -445,20 +356,20 @@ void cshake(const int  iatom[],
 }
 
 //! Implements RATTLE (ie. SHAKE for velocity verlet integrators)
-static void crattle(const int  iatom[],
-                    int        ncon,
-                    int*       nnit,
-                    int        maxnit,
-                    const real constraint_distance_squared[],
-                    real       vp[],
-                    const real rij[],
-                    const real m2[],
-                    real       omega,
-                    const real invmass[],
-                    const real distance_squared_tolerance[],
-                    real       scaled_lagrange_multiplier[],
-                    int*       nerror,
-                    real       invdt)
+static void crattle(const int            iatom[],
+                    int                  ncon,
+                    int*                 nnit,
+                    int                  maxnit,
+                    ArrayRef<const real> constraint_distance_squared,
+                    ArrayRef<RVec>       vp,
+                    ArrayRef<const RVec> rij,
+                    ArrayRef<const real> m2,
+                    real                 omega,
+                    const real           invmass[],
+                    ArrayRef<const real> distance_squared_tolerance,
+                    ArrayRef<real>       scaled_lagrange_multiplier,
+                    int*                 nerror,
+                    real                 invdt)
 {
     /*
      *     r.c. van schaik and w.f. van gunsteren
@@ -469,66 +380,50 @@ static void crattle(const int  iatom[],
      *     second part of rattle algorithm
      */
 
-    int  ll, i, j, i3, j3, l3;
-    int  ix, iy, iz, jx, jy, jz;
-    real constraint_distance_squared_ll;
-    real vpijd, vx, vy, vz, acor, fac, im, jm;
-    real xh, yh, zh, rijx, rijy, rijz;
-    int  nit, error, nconv;
-    real iconvf;
-
     // TODO nconv is used solely as a boolean, so we should write the
     // code like that
-    error = 0;
-    nconv = 1;
+    int error = 0;
+    int nconv = 1;
+    int nit;
     for (nit = 0; (nit < maxnit) && (nconv != 0) && (error == 0); nit++)
     {
         nconv = 0;
-        for (ll = 0; (ll < ncon) && (error == 0); ll++)
+        for (int ll = 0; (ll < ncon) && (error == 0); ll++)
         {
-            l3   = 3 * ll;
-            rijx = rij[l3 + XX];
-            rijy = rij[l3 + YY];
-            rijz = rij[l3 + ZZ];
-            i    = iatom[l3 + 1];
-            j    = iatom[l3 + 2];
-            i3   = 3 * i;
-            j3   = 3 * j;
-            ix   = i3 + XX;
-            iy   = i3 + YY;
-            iz   = i3 + ZZ;
-            jx   = j3 + XX;
-            jy   = j3 + YY;
-            jz   = j3 + ZZ;
-            vx   = vp[ix] - vp[jx];
-            vy   = vp[iy] - vp[jy];
-            vz   = vp[iz] - vp[jz];
-
-            vpijd                          = vx * rijx + vy * rijy + vz * rijz;
-            constraint_distance_squared_ll = constraint_distance_squared[ll];
+            const int  l3   = 3 * ll;
+            const real rijx = rij[ll][XX];
+            const real rijy = rij[ll][YY];
+            const real rijz = rij[ll][ZZ];
+            const int  i    = iatom[l3 + 1];
+            const int  j    = iatom[l3 + 2];
+            rvec       v;
+            rvec_sub(vp[i], vp[j], v);
+
+            const real vpijd                          = v[XX] * rijx + v[YY] * rijy + v[ZZ] * rijz;
+            const real constraint_distance_squared_ll = constraint_distance_squared[ll];
 
             /* iconv is zero when the error is smaller than a bound */
-            iconvf = fabs(vpijd) * (distance_squared_tolerance[ll] / invdt);
+            const real iconvf = fabs(vpijd) * (distance_squared_tolerance[ll] / invdt);
 
-            if (iconvf > 1)
+            if (iconvf > 1.0_real)
             {
-                nconv = static_cast<int>(iconvf);
-                fac   = omega * 2.0 * m2[ll] / constraint_distance_squared_ll;
-                acor  = -fac * vpijd;
+                nconv           = static_cast<int>(iconvf);
+                const real fac  = omega * 2.0_real * m2[ll] / constraint_distance_squared_ll;
+                const real acor = -fac * vpijd;
                 scaled_lagrange_multiplier[ll] += acor;
-                xh = rijx * acor;
-                yh = rijy * acor;
-                zh = rijz * acor;
-
-                im = invmass[i];
-                jm = invmass[j];
-
-                vp[ix] += xh * im;
-                vp[iy] += yh * im;
-                vp[iz] += zh * im;
-                vp[jx] -= xh * jm;
-                vp[jy] -= yh * jm;
-                vp[jz] -= zh * jm;
+                const real xh = rijx * acor;
+                const real yh = rijy * acor;
+                const real zh = rijz * acor;
+
+                const real im = invmass[i];
+                const real jm = invmass[j];
+
+                vp[i][XX] += xh * im;
+                vp[i][YY] += yh * im;
+                vp[i][ZZ] += zh * im;
+                vp[j][XX] -= xh * jm;
+                vp[j][YY] -= yh * jm;
+                vp[j][ZZ] -= zh * jm;
             }
         }
     }
@@ -537,61 +432,60 @@ static void crattle(const int  iatom[],
 }
 
 //! Applies SHAKE
-static int vec_shakef(FILE*              fplog,
-                      shakedata*         shaked,
-                      const real         invmass[],
-                      int                ncon,
-                      t_iparams          ip[],
-                      t_iatom*           iatom,
-                      real               tol,
-                      const rvec         x[],
-                      rvec               prime[],
-                      real               omega,
-                      bool               bFEP,
-                      real               lambda,
-                      real               scaled_lagrange_multiplier[],
-                      real               invdt,
-                      rvec*              v,
-                      bool               bCalcVir,
-                      tensor             vir_r_m_dr,
-                      ConstraintVariable econq)
+static int vec_shakef(FILE*                     fplog,
+                      shakedata*                shaked,
+                      const real                invmass[],
+                      int                       ncon,
+                      ArrayRef<const t_iparams> ip,
+                      const int*                iatom,
+                      real                      tol,
+                      ArrayRef<const RVec>      x,
+                      ArrayRef<RVec>            prime,
+                      const t_pbc*              pbc,
+                      real                      omega,
+                      bool                      bFEP,
+                      real                      lambda,
+                      ArrayRef<real>            scaled_lagrange_multiplier,
+                      real                      invdt,
+                      ArrayRef<RVec>            v,
+                      bool                      bCalcVir,
+                      tensor                    vir_r_m_dr,
+                      ConstraintVariable        econq)
 {
-    rvec*    rij;
-    real *   half_of_reduced_mass, *distance_squared_tolerance, *constraint_distance_squared;
-    int      maxnit = 1000;
-    int      nit    = 0, ll, i, j, d, d2, type;
-    t_iatom* ia;
-    real     L1;
-    real     mm    = 0., tmp;
-    int      error = 0;
-    real     constraint_distance;
-
-    if (ncon > shaked->nalloc)
-    {
-        shaked->nalloc = over_alloc_dd(ncon);
-        srenew(shaked->rij, shaked->nalloc);
-        srenew(shaked->half_of_reduced_mass, shaked->nalloc);
-        srenew(shaked->distance_squared_tolerance, shaked->nalloc);
-        srenew(shaked->constraint_distance_squared, shaked->nalloc);
-    }
-    rij                         = shaked->rij;
-    half_of_reduced_mass        = shaked->half_of_reduced_mass;
-    distance_squared_tolerance  = shaked->distance_squared_tolerance;
-    constraint_distance_squared = shaked->constraint_distance_squared;
-
-    L1 = 1.0 - lambda;
-    ia = iatom;
+    int  maxnit = 1000;
+    int  nit    = 0, ll, i, j, d, d2, type;
+    real L1;
+    int  error = 0;
+    real constraint_distance;
+
+    shaked->rij.resize(ncon);
+    shaked->half_of_reduced_mass.resize(ncon);
+    shaked->distance_squared_tolerance.resize(ncon);
+    shaked->constraint_distance_squared.resize(ncon);
+
+    ArrayRef<RVec> rij                         = shaked->rij;
+    ArrayRef<real> half_of_reduced_mass        = shaked->half_of_reduced_mass;
+    ArrayRef<real> distance_squared_tolerance  = shaked->distance_squared_tolerance;
+    ArrayRef<real> constraint_distance_squared = shaked->constraint_distance_squared;
+
+    L1            = 1.0_real - lambda;
+    const int* ia = iatom;
     for (ll = 0; (ll < ncon); ll++, ia += 3)
     {
         type = ia[0];
         i    = ia[1];
         j    = ia[2];
 
-        mm                       = 2.0 * (invmass[i] + invmass[j]);
-        rij[ll][XX]              = x[i][XX] - x[j][XX];
-        rij[ll][YY]              = x[i][YY] - x[j][YY];
-        rij[ll][ZZ]              = x[i][ZZ] - x[j][ZZ];
-        half_of_reduced_mass[ll] = 1.0 / mm;
+        if (pbc)
+        {
+            pbc_dx(pbc, x[i], x[j], rij[ll]);
+        }
+        else
+        {
+            rvec_sub(x[i], x[j], rij[ll]);
+        }
+        const real mm            = 2.0_real * (invmass[i] + invmass[j]);
+        half_of_reduced_mass[ll] = 1.0_real / mm;
         if (bFEP)
         {
             constraint_distance = L1 * ip[type].constr.dA + lambda * ip[type].constr.dB;
@@ -607,12 +501,12 @@ static int vec_shakef(FILE*              fplog,
     switch (econq)
     {
         case ConstraintVariable::Positions:
-            cshake(iatom, ncon, &nit, maxnit, constraint_distance_squared, prime[0], rij[0],
+            cshake(iatom, ncon, &nit, maxnit, constraint_distance_squared, prime, pbc, rij,
                    half_of_reduced_mass, omega, invmass, distance_squared_tolerance,
                    scaled_lagrange_multiplier, &error);
             break;
         case ConstraintVariable::Velocities:
-            crattle(iatom, ncon, &nit, maxnit, constraint_distance_squared, prime[0], rij[0],
+            crattle(iatom, ncon, &nit, maxnit, constraint_distance_squared, prime, rij,
                     half_of_reduced_mass, omega, invmass, distance_squared_tolerance,
                     scaled_lagrange_multiplier, &error, invdt);
             break;
@@ -654,10 +548,10 @@ static int vec_shakef(FILE*              fplog,
         i    = ia[1];
         j    = ia[2];
 
-        if ((econq == ConstraintVariable::Positions) && v != nullptr)
+        if ((econq == ConstraintVariable::Positions) && !v.empty())
         {
             /* Correct the velocities */
-            mm = scaled_lagrange_multiplier[ll] * invmass[i] * invdt;
+            real mm = scaled_lagrange_multiplier[ll] * invmass[i] * invdt;
             for (d = 0; d < DIM; d++)
             {
                 v[ia[1]][d] += mm * rij[ll][d];
@@ -673,10 +567,10 @@ static int vec_shakef(FILE*              fplog,
         /* constraint virial */
         if (bCalcVir)
         {
-            mm = scaled_lagrange_multiplier[ll];
+            const real mm = scaled_lagrange_multiplier[ll];
             for (d = 0; d < DIM; d++)
             {
-                tmp = mm * rij[ll][d];
+                const real tmp = mm * rij[ll][d];
                 for (d2 = 0; d2 < DIM; d2++)
                 {
                     vir_r_m_dr[d][d2] -= tmp * rij[ll][d2];
@@ -702,25 +596,25 @@ static int vec_shakef(FILE*              fplog,
 }
 
 //! Check that constraints are satisfied.
-static void check_cons(FILE*              log,
-                       int                nc,
-                       const rvec         x[],
-                       rvec               prime[],
-                       rvec               v[],
-                       t_iparams          ip[],
-                       t_iatom*           iatom,
-                       const real         invmass[],
-                       ConstraintVariable econq)
+static void check_cons(FILE*                     log,
+                       int                       nc,
+                       ArrayRef<const RVec>      x,
+                       ArrayRef<const RVec>      prime,
+                       ArrayRef<const RVec>      v,
+                       const t_pbc*              pbc,
+                       ArrayRef<const t_iparams> ip,
+                       const int*                iatom,
+                       const real                invmass[],
+                       ConstraintVariable        econq)
 {
-    t_iatom* ia;
-    int      ai, aj;
-    int      i;
-    real     d, dp;
-    rvec     dx, dv;
+    int  ai, aj;
+    int  i;
+    real d, dp;
+    rvec dx, dv;
 
-    GMX_ASSERT(v, "Input has to be non-null");
+    GMX_ASSERT(!v.empty(), "Input has to be non-null");
     fprintf(log, "    i     mi      j     mj      before       after   should be\n");
-    ia = iatom;
+    const int* ia = iatom;
     for (i = 0; (i < nc); i++, ia += 3)
     {
         ai = ia[1];
@@ -731,7 +625,14 @@ static void check_cons(FILE*              log,
         switch (econq)
         {
             case ConstraintVariable::Positions:
-                rvec_sub(prime[ai], prime[aj], dx);
+                if (pbc)
+                {
+                    pbc_dx(pbc, prime[ai], prime[aj], dx);
+                }
+                else
+                {
+                    rvec_sub(prime[ai], prime[aj], dx);
+                }
                 dp = norm(dx);
                 fprintf(log, "%5d  %5.2f  %5d  %5.2f  %10.5f  %10.5f  %10.5f\n", ai + 1,
                         1.0 / invmass[ai], aj + 1, 1.0 / invmass[aj], d, dp, ip[ia[0]].constr.dA);
@@ -750,29 +651,29 @@ static void check_cons(FILE*              log,
 }
 
 //! Applies SHAKE.
-static bool bshakef(FILE*              log,
-                    shakedata*         shaked,
-                    const real         invmass[],
-                    const t_idef&      idef,
-                    const t_inputrec&  ir,
-                    const rvec         x_s[],
-                    rvec               prime[],
-                    t_nrnb*            nrnb,
-                    real               lambda,
-                    real*              dvdlambda,
-                    real               invdt,
-                    rvec*              v,
-                    bool               bCalcVir,
-                    tensor             vir_r_m_dr,
-                    bool               bDumpOnError,
-                    ConstraintVariable econq)
+static bool bshakef(FILE*                         log,
+                    shakedata*                    shaked,
+                    const real                    invmass[],
+                    const InteractionDefinitions& idef,
+                    const t_inputrec&             ir,
+                    ArrayRef<const RVec>          x_s,
+                    ArrayRef<RVec>                prime,
+                    const t_pbc*                  pbc,
+                    t_nrnb*                       nrnb,
+                    real                          lambda,
+                    real*                         dvdlambda,
+                    real                          invdt,
+                    ArrayRef<RVec>                v,
+                    bool                          bCalcVir,
+                    tensor                        vir_r_m_dr,
+                    bool                          bDumpOnError,
+                    ConstraintVariable            econq)
 {
-    t_iatom* iatoms;
-    real *   lam, dt_2, dvdl;
-    int      i, n0, ncon, blen, type, ll;
-    int      tnit = 0, trij = 0;
+    real dt_2, dvdl;
+    int  i, n0, ncon, blen, type, ll;
+    int  tnit = 0, trij = 0;
 
-    ncon = idef.il[F_CONSTR].nr / 3;
+    ncon = idef.il[F_CONSTR].size() / 3;
 
     for (ll = 0; ll < ncon; ll++)
     {
@@ -782,14 +683,14 @@ static bool bshakef(FILE*              log,
     // TODO Rewrite this block so that it is obvious that i, iatoms
     // and lam are all iteration variables. Is this easier if the
     // sblock data structure is organized differently?
-    iatoms = &(idef.il[F_CONSTR].iatoms[shaked->sblock[0]]);
-    lam    = shaked->scaled_lagrange_multiplier;
-    for (i = 0; (i < shaked->nblocks);)
+    const int*     iatoms = &(idef.il[F_CONSTR].iatoms[shaked->sblock[0]]);
+    ArrayRef<real> lam    = shaked->scaled_lagrange_multiplier;
+    for (i = 0; (i < shaked->numShakeBlocks());)
     {
         blen = (shaked->sblock[i + 1] - shaked->sblock[i]);
         blen /= 3;
         n0 = vec_shakef(log, shaked, invmass, blen, idef.iparams, iatoms, ir.shake_tol, x_s, prime,
-                        shaked->omega, ir.efep != efepNO, lambda, lam, invdt, v, bCalcVir,
+                        pbc, shaked->omega, ir.efep != efepNO, lambda, lam, invdt, v, bCalcVir,
                         vir_r_m_dr, econq);
 
         if (n0 == 0)
@@ -797,7 +698,7 @@ static bool bshakef(FILE*              log,
             if (bDumpOnError && log)
             {
                 {
-                    check_cons(log, blen, x_s, prime, v, idef.iparams, iatoms, invmass, econq);
+                    check_cons(log, blen, x_s, prime, v, pbc, idef.iparams, iatoms, invmass, econq);
                 }
             }
             return FALSE;
@@ -805,7 +706,7 @@ static bool bshakef(FILE*              log,
         tnit += n0 * blen;
         trij += blen;
         iatoms += 3 * blen; /* Increment pointer! */
-        lam += blen;
+        lam = lam.subArray(blen, lam.ssize() - blen);
         i++;
     }
     /* only for position part? */
@@ -813,7 +714,8 @@ static bool bshakef(FILE*              log,
     {
         if (ir.efep != efepNO)
         {
-            real bondA, bondB;
+            ArrayRef<const t_iparams> iparams = idef.iparams;
+
             /* TODO This should probably use invdt, so that sd integrator scaling works properly */
             dt_2 = 1 / gmx::square(ir.delta_t);
             dvdl = 0;
@@ -825,8 +727,8 @@ static bool bshakef(FILE*              log,
                 /* The vector scaled_lagrange_multiplier[ll] contains the value -2 r_ll eta_ll
                    (eta_ll is the estimate of the Langrangian, definition on page 336 of Ryckaert et
                    al 1977), so the pre-factors are already present. */
-                bondA = idef.iparams[type].constr.dA;
-                bondB = idef.iparams[type].constr.dB;
+                const real bondA = iparams[type].constr.dA;
+                const real bondB = iparams[type].constr.dB;
                 dvdl += shaked->scaled_lagrange_multiplier[ll] * dt_2 * (bondB - bondA);
             }
             *dvdlambda += dvdl;
@@ -843,7 +745,7 @@ static bool bshakef(FILE*              log,
     }
     inc_nrnb(nrnb, eNR_SHAKE, tnit);
     inc_nrnb(nrnb, eNR_SHAKE_RIJ, trij);
-    if (v)
+    if (!v.empty())
     {
         inc_nrnb(nrnb, eNR_CONSTR_V, trij * 2);
     }
@@ -855,25 +757,26 @@ static bool bshakef(FILE*              log,
     return TRUE;
 }
 
-bool constrain_shake(FILE*              log,
-                     shakedata*         shaked,
-                     const real         invmass[],
-                     const t_idef&      idef,
-                     const t_inputrec&  ir,
-                     const rvec         x_s[],
-                     rvec               xprime[],
-                     rvec               vprime[],
-                     t_nrnb*            nrnb,
-                     real               lambda,
-                     real*              dvdlambda,
-                     real               invdt,
-                     rvec*              v,
-                     bool               bCalcVir,
-                     tensor             vir_r_m_dr,
-                     bool               bDumpOnError,
-                     ConstraintVariable econq)
+bool constrain_shake(FILE*                         log,
+                     shakedata*                    shaked,
+                     const real                    invmass[],
+                     const InteractionDefinitions& idef,
+                     const t_inputrec&             ir,
+                     ArrayRef<const RVec>          x_s,
+                     ArrayRef<RVec>                xprime,
+                     ArrayRef<RVec>                vprime,
+                     const t_pbc*                  pbc,
+                     t_nrnb*                       nrnb,
+                     real                          lambda,
+                     real*                         dvdlambda,
+                     real                          invdt,
+                     ArrayRef<RVec>                v,
+                     bool                          bCalcVir,
+                     tensor                        vir_r_m_dr,
+                     bool                          bDumpOnError,
+                     ConstraintVariable            econq)
 {
-    if (shaked->nblocks == 0)
+    if (shaked->numShakeBlocks() == 0)
     {
         return true;
     }
@@ -881,12 +784,12 @@ bool constrain_shake(FILE*              log,
     switch (econq)
     {
         case (ConstraintVariable::Positions):
-            bOK = bshakef(log, shaked, invmass, idef, ir, x_s, xprime, nrnb, lambda, dvdlambda,
+            bOK = bshakef(log, shaked, invmass, idef, ir, x_s, xprime, pbc, nrnb, lambda, dvdlambda,
                           invdt, v, bCalcVir, vir_r_m_dr, bDumpOnError, econq);
             break;
         case (ConstraintVariable::Velocities):
-            bOK = bshakef(log, shaked, invmass, idef, ir, x_s, vprime, nrnb, lambda, dvdlambda,
-                          invdt, nullptr, bCalcVir, vir_r_m_dr, bDumpOnError, econq);
+            bOK = bshakef(log, shaked, invmass, idef, ir, x_s, vprime, pbc, nrnb, lambda, dvdlambda,
+                          invdt, {}, bCalcVir, vir_r_m_dr, bDumpOnError, econq);
             break;
         default:
             gmx_fatal(FARGS,
index 1249d3c1f577094c7bba5e64d229b029d65bf627..8559c57553855291095dedf5eadb2247512dc50c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_MDLIB_SHAKE_H
 #define GMX_MDLIB_SHAKE_H
 
+#include "gromacs/math/vec.h"
 #include "gromacs/topology/block.h"
-#include "gromacs/topology/idef.h"
+#include "gromacs/utility/real.h"
 
-struct gmx_domdec_t;
+struct InteractionList;
+class InteractionDefinitions;
 struct t_inputrec;
-struct t_mdatoms;
 struct t_nrnb;
+struct t_pbc;
 
 namespace gmx
 {
+template<typename T>
+class ArrayRef;
 
 enum class ConstraintVariable : int;
 
-/* Abstract type for SHAKE that is defined only in the file that uses it */
-struct shakedata;
-
-/*! \brief Initializes and return the SHAKE data structure */
-shakedata* shake_init();
+/*! \libinternal
+ * \brief Working data for the SHAKE algorithm
+ */
+struct shakedata
+{
+    //! Returns the number of SHAKE blocks */
+    int numShakeBlocks() const { return sblock.size() - 1; }
 
-//! Destroy SHAKE. Needed to solve memory leaks.
-void done_shake(shakedata* d);
+    //! The reference constraint vectors
+    std::vector<RVec> rij;
+    //! The reduced mass of the two atoms in each constraint times 0.5
+    std::vector<real> half_of_reduced_mass;
+    //! Multiplicative tolerance on the difference in the square of the constrained distance
+    std::vector<real> distance_squared_tolerance;
+    //! The reference constraint distances squared
+    std::vector<real> constraint_distance_squared;
+    /* SOR stuff */
+    //! SOR delta
+    real delta = 0.1;
+    //! SOR omega
+    real omega = 1.0;
+    //! SOR gamma
+    real gamma = 1000000;
+    //! The SHAKE blocks, block i contains constraints sblock[i]/3 to sblock[i+1]/3 */
+    std::vector<int> sblock = { 0 };
+    /*! \brief Scaled Lagrange multiplier for each constraint.
+     *
+     * Value is -2 * eta from p. 336 of the paper, divided by the
+     * constraint distance. */
+    std::vector<real> scaled_lagrange_multiplier;
+};
 
 //! Make SHAKE blocks when not using DD.
-void make_shake_sblock_serial(shakedata* shaked, const t_idef* idef, const t_mdatoms& md);
+void make_shake_sblock_serial(shakedata* shaked, InteractionDefinitions* idef, int numAtoms);
 
 //! Make SHAKE blocks when using DD.
-void make_shake_sblock_dd(shakedata* shaked, const t_ilist* ilcon, const gmx_domdec_t* dd);
+void make_shake_sblock_dd(shakedata* shaked, const InteractionList& ilcon);
 
 /*! \brief Shake all the atoms blockwise. It is assumed that all the constraints
  * in the idef->shakes field are sorted, to ascending block nr. The
@@ -81,38 +108,40 @@ void make_shake_sblock_dd(shakedata* shaked, const t_ilist* ilcon, const gmx_dom
  * sblock[n] to sblock[n+1]. Array sblock should be large enough.
  * Return TRUE when OK, FALSE when shake-error
  */
-bool constrain_shake(FILE*              log,          /* Log file                      */
-                     shakedata*         shaked,       /* Total number of atoms */
-                     const real         invmass[],    /* Atomic masses         */
-                     const t_idef&      idef,         /* The interaction def           */
-                     const t_inputrec&  ir,           /* Input record                  */
-                     const rvec         x_s[],        /* Coords before update          */
-                     rvec               xprime[],     /* Output coords when constraining x */
-                     rvec               vprime[],     /* Output coords when constraining v */
-                     t_nrnb*            nrnb,         /* Performance measure          */
-                     real               lambda,       /* FEP lambda                   */
-                     real*              dvdlambda,    /* FEP force                    */
-                     real               invdt,        /* 1/delta_t                    */
-                     rvec*              v,            /* Also constrain v if v!=NULL  */
-                     bool               bCalcVir,     /* Calculate r x m delta_r      */
-                     tensor             vir_r_m_dr,   /* sum r x m delta_r            */
-                     bool               bDumpOnError, /* Dump debugging stuff on error*/
-                     ConstraintVariable econq);       /* which type of constraint is occurring */
+bool constrain_shake(FILE*                         log,       /* Log file                      */
+                     shakedata*                    shaked,    /* Total number of atoms */
+                     const real                    invmass[], /* Atomic masses         */
+                     const InteractionDefinitions& idef,      /* The interaction def           */
+                     const t_inputrec&             ir,        /* Input record                  */
+                     ArrayRef<const RVec>          x_s,       /* Coords before update          */
+                     ArrayRef<RVec>                xprime, /* Output coords when constraining x */
+                     ArrayRef<RVec>                vprime, /* Output coords when constraining v */
+                     const t_pbc*                  pbc,    /* PBC information              */
+                     t_nrnb*                       nrnb,   /* Performance measure          */
+                     real                          lambda, /* FEP lambda                   */
+                     real*                         dvdlambda,  /* FEP force                    */
+                     real                          invdt,      /* 1/delta_t                    */
+                     ArrayRef<RVec>                v,          /* Also constrain v if not empty  */
+                     bool                          bCalcVir,   /* Calculate r x m delta_r      */
+                     tensor                        vir_r_m_dr, /* sum r x m delta_r            */
+                     bool                          bDumpOnError, /* Dump debugging stuff on error*/
+                     ConstraintVariable econq); /* which type of constraint is occurring */
 
 /*! \brief Regular iterative shake */
-void cshake(const int  iatom[],
-            int        ncon,
-            int*       nnit,
-            int        maxnit,
-            const real dist2[],
-            real       xp[],
-            const real rij[],
-            const real m2[],
-            real       omega,
-            const real invmass[],
-            const real tt[],
-            real       lagr[],
-            int*       nerror);
+void cshake(const int            iatom[],
+            int                  ncon,
+            int*                 nnit,
+            int                  maxnit,
+            ArrayRef<const real> dist2,
+            ArrayRef<RVec>       xp,
+            const t_pbc*         pbc,
+            ArrayRef<const RVec> rij,
+            ArrayRef<const real> half_of_reduced_mass,
+            real                 omega,
+            const real           invmass[],
+            ArrayRef<const real> distance_squared_tolerance,
+            ArrayRef<real>       scaled_lagrange_multiplier,
+            int*                 nerror);
 
 } // namespace gmx
 
index c883edee3264c824a344efbfaf1455d0512ab1a5..392afa9fada7a1a18f94ce57d723ee29b4f7235b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f2528d78b43e643e34847d67e543ac819a8dcf02..ef27166978b2d7ebeb9f5a781e211fa7d18ebaa2 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013-2020, by the GROMACS development team, led by
+ * Copyright (c) 2013-2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,8 +44,9 @@
 #include <cstring>
 
 #include <array>
+#include <optional>
 
-#include "gromacs/awh/awh.h"
+#include "gromacs/applied_forces/awh/awh.h"
 #include "gromacs/domdec/dlbtiming.h"
 #include "gromacs/domdec/domdec.h"
 #include "gromacs/domdec/domdec_struct.h"
@@ -53,6 +54,7 @@
 #include "gromacs/domdec/partition.h"
 #include "gromacs/essentialdynamics/edsam.h"
 #include "gromacs/ewald/pme.h"
+#include "gromacs/ewald/pme_pp.h"
 #include "gromacs/ewald/pme_pp_comm_gpu.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nonbonded/nb_free_energy.h"
@@ -63,7 +65,6 @@
 #include "gromacs/listed_forces/disre.h"
 #include "gromacs/listed_forces/gpubonded.h"
 #include "gromacs/listed_forces/listed_forces.h"
-#include "gromacs/listed_forces/manage_threading.h"
 #include "gromacs/listed_forces/orires.h"
 #include "gromacs/math/arrayrefwithpadding.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/mdlib/calcmu.h"
 #include "gromacs/mdlib/calcvir.h"
 #include "gromacs/mdlib/constr.h"
+#include "gromacs/mdlib/dispersioncorrection.h"
 #include "gromacs/mdlib/enerdata_utils.h"
 #include "gromacs/mdlib/force.h"
+#include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdlib/forcerec.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
-#include "gromacs/mdlib/qmmm.h"
 #include "gromacs/mdlib/update.h"
+#include "gromacs/mdlib/vsite.h"
+#include "gromacs/mdlib/wall.h"
+#include "gromacs/mdlib/wholemoleculetransform.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/enerdata.h"
+#include "gromacs/mdtypes/forcebuffers.h"
 #include "gromacs/mdtypes/forceoutput.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/iforceprovider.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/mdtypes/multipletimestepping.h"
 #include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/mdtypes/state_propagator_data_gpu.h"
 #include "gromacs/nbnxm/gpu_data_mgmt.h"
 #include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/nbnxm/nbnxm_gpu.h"
 #include "gromacs/pbcutil/ishift.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pulling/pull.h"
 #include "gromacs/pulling/pull_rotation.h"
 #include "gromacs/utility/strconvert.h"
 #include "gromacs/utility/sysinfo.h"
 
+#include "gpuforcereduction.h"
+
+using gmx::ArrayRef;
 using gmx::AtomLocality;
 using gmx::DomainLifetimeWorkload;
 using gmx::ForceOutputs;
+using gmx::ForceWithShiftForces;
 using gmx::InteractionLocality;
+using gmx::RVec;
 using gmx::SimulationWorkload;
 using gmx::StepWorkload;
 
@@ -127,8 +141,9 @@ using gmx::StepWorkload;
 // PME-first ordering would suffice).
 static const bool c_disableAlternatingWait = (getenv("GMX_DISABLE_ALTERNATING_GPU_WAIT") != nullptr);
 
-static void sum_forces(rvec f[], gmx::ArrayRef<const gmx::RVec> forceToAdd)
+static void sum_forces(ArrayRef<RVec> f, ArrayRef<const RVec> forceToAdd)
 {
+    GMX_ASSERT(f.size() >= forceToAdd.size(), "Accumulation buffer should be sufficiently large");
     const int end = forceToAdd.size();
 
     int gmx_unused nt = gmx_omp_nthreads_get(emntDefault);
@@ -144,22 +159,21 @@ static void calc_virial(int                              start,
                         const rvec                       x[],
                         const gmx::ForceWithShiftForces& forceWithShiftForces,
                         tensor                           vir_part,
-                        const t_graph*                   graph,
                         const matrix                     box,
                         t_nrnb*                          nrnb,
                         const t_forcerec*                fr,
-                        int                              ePBC)
+                        PbcType                          pbcType)
 {
     /* The short-range virial from surrounding boxes */
     const rvec* fshift = as_rvec_array(forceWithShiftForces.shiftForces().data());
-    calc_vir(SHIFTS, fr->shift_vec, fshift, vir_part, ePBC == epbcSCREW, box);
+    calc_vir(SHIFTS, fr->shift_vec, fshift, vir_part, pbcType == PbcType::Screw, 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
      */
     const rvec* f = as_rvec_array(forceWithShiftForces.force().data());
-    f_calc_vir(start, start + homenr, x, f, vir_part, graph, box);
+    f_calc_vir(start, start + homenr, x, f, vir_part, box);
     inc_nrnb(nrnb, eNR_VIRIAL, homenr);
 
     if (debug)
@@ -187,10 +201,11 @@ static void pull_potential_wrapper(const t_commrec*               cr,
      * which is why pull_potential is called close to other communication.
      */
     wallcycle_start(wcycle, ewcPULLPOT);
-    set_pbc(&pbc, ir->ePBC, box);
+    set_pbc(&pbc, ir->pbcType, box);
     dvdl = 0;
-    enerd->term[F_COM_PULL] += pull_potential(pull_work, mdatoms, &pbc, cr, t, lambda[efptRESTRAINT],
-                                              as_rvec_array(x.data()), force, &dvdl);
+    enerd->term[F_COM_PULL] +=
+            pull_potential(pull_work, mdatoms->massT, &pbc, cr, t, lambda[efptRESTRAINT],
+                           as_rvec_array(x.data()), force, &dvdl);
     enerd->dvdl_lin[efptRESTRAINT] += dvdl;
     wallcycle_stop(wcycle, ewcPULLPOT);
 }
@@ -229,13 +244,13 @@ static void pme_receive_force_ener(t_forcerec*           fr,
     wallcycle_stop(wcycle, ewcPP_PMEWAITRECVF);
 }
 
-static void print_large_forces(FILE*            fp,
-                               const t_mdatoms* md,
-                               const t_commrec* cr,
-                               int64_t          step,
-                               real             forceTolerance,
-                               const rvec*      x,
-                               const rvec*      f)
+static void print_large_forces(FILE*                fp,
+                               const t_mdatoms*     md,
+                               const t_commrec*     cr,
+                               int64_t              step,
+                               real                 forceTolerance,
+                               ArrayRef<const RVec> x,
+                               ArrayRef<const RVec> f)
 {
     real       force2Tolerance = gmx::square(forceTolerance);
     gmx::index numNonFinite    = 0;
@@ -263,27 +278,64 @@ static void print_large_forces(FILE*            fp,
     }
 }
 
-static void post_process_forces(const t_commrec*      cr,
-                                int64_t               step,
-                                t_nrnb*               nrnb,
-                                gmx_wallcycle_t       wcycle,
-                                const gmx_localtop_t* top,
-                                const matrix          box,
-                                const rvec            x[],
-                                ForceOutputs*         forceOutputs,
-                                tensor                vir_force,
-                                const t_mdatoms*      mdatoms,
-                                const t_graph*        graph,
-                                const t_forcerec*     fr,
-                                const gmx_vsite_t*    vsite,
-                                const StepWorkload&   stepWork)
+//! When necessary, spreads forces on vsites and computes the virial for \p forceOutputs->forceWithShiftForces()
+static void postProcessForceWithShiftForces(t_nrnb*                   nrnb,
+                                            gmx_wallcycle_t           wcycle,
+                                            const matrix              box,
+                                            ArrayRef<const RVec>      x,
+                                            ForceOutputs*             forceOutputs,
+                                            tensor                    vir_force,
+                                            const t_mdatoms&          mdatoms,
+                                            const t_forcerec&         fr,
+                                            gmx::VirtualSitesHandler* vsite,
+                                            const StepWorkload&       stepWork)
+{
+    ForceWithShiftForces& forceWithShiftForces = forceOutputs->forceWithShiftForces();
+
+    /* If we have NoVirSum forces, but we do not calculate the virial,
+     * we later sum the forceWithShiftForces buffer together with
+     * the noVirSum buffer and spread the combined vsite forces at once.
+     */
+    if (vsite && (!forceOutputs->haveForceWithVirial() || stepWork.computeVirial))
+    {
+        using VirialHandling = gmx::VirtualSitesHandler::VirialHandling;
+
+        auto                 f      = forceWithShiftForces.force();
+        auto                 fshift = forceWithShiftForces.shiftForces();
+        const VirialHandling virialHandling =
+                (stepWork.computeVirial ? VirialHandling::Pbc : VirialHandling::None);
+        vsite->spreadForces(x, f, virialHandling, fshift, nullptr, nrnb, box, wcycle);
+        forceWithShiftForces.haveSpreadVsiteForces() = true;
+    }
+
+    if (stepWork.computeVirial)
+    {
+        /* Calculation of the virial must be done after vsites! */
+        calc_virial(0, mdatoms.homenr, as_rvec_array(x.data()), forceWithShiftForces, vir_force,
+                    box, nrnb, &fr, fr.pbcType);
+    }
+}
+
+//! Spread, compute virial for and sum forces, when necessary
+static void postProcessForces(const t_commrec*          cr,
+                              int64_t                   step,
+                              t_nrnb*                   nrnb,
+                              gmx_wallcycle_t           wcycle,
+                              const matrix              box,
+                              ArrayRef<const RVec>      x,
+                              ForceOutputs*             forceOutputs,
+                              tensor                    vir_force,
+                              const t_mdatoms*          mdatoms,
+                              const t_forcerec*         fr,
+                              gmx::VirtualSitesHandler* vsite,
+                              const StepWorkload&       stepWork)
 {
-    rvec* f = as_rvec_array(forceOutputs->forceWithShiftForces().force().data());
+    // Extract the final output force buffer, which is also the buffer for forces with shift forces
+    ArrayRef<RVec> f = forceOutputs->forceWithShiftForces().force();
 
-    if (fr->haveDirectVirialContributions)
+    if (forceOutputs->haveForceWithVirial())
     {
         auto& forceWithVirial = forceOutputs->forceWithVirial();
-        rvec* fDirectVir      = as_rvec_array(forceWithVirial.force_.data());
 
         if (vsite)
         {
@@ -291,9 +343,18 @@ static void post_process_forces(const t_commrec*      cr,
              * This is parallellized. MPI communication is performed
              * if the constructing atoms aren't local.
              */
+            GMX_ASSERT(!stepWork.computeVirial || f.data() != forceWithVirial.force_.data(),
+                       "We need separate force buffers for shift and virial forces when "
+                       "computing the virial");
+            GMX_ASSERT(!stepWork.computeVirial
+                               || forceOutputs->forceWithShiftForces().haveSpreadVsiteForces(),
+                       "We should spread the force with shift forces separately when computing "
+                       "the virial");
+            const gmx::VirtualSitesHandler::VirialHandling virialHandling =
+                    (stepWork.computeVirial ? gmx::VirtualSitesHandler::VirialHandling::NonLinear
+                                            : gmx::VirtualSitesHandler::VirialHandling::None);
             matrix virial = { { 0 } };
-            spread_vsite_f(vsite, x, fDirectVir, nullptr, stepWork.computeVirial, virial, nrnb,
-                           &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr, wcycle);
+            vsite->spreadForces(x, forceWithVirial.force_, virialHandling, {}, virial, nrnb, box, wcycle);
             forceWithVirial.addVirialContribution(virial);
         }
 
@@ -314,6 +375,11 @@ static void post_process_forces(const t_commrec*      cr,
             }
         }
     }
+    else
+    {
+        GMX_ASSERT(vsite == nullptr || forceOutputs->forceWithShiftForces().haveSpreadVsiteForces(),
+                   "We should have spread the vsite forces (earlier)");
+    }
 
     if (fr->print_force >= 0)
     {
@@ -340,11 +406,6 @@ static void do_nb_verlet(t_forcerec*                fr,
     nonbonded_verlet_t* nbv = fr->nbv.get();
 
     /* GPU kernel launch overhead is already timed separately */
-    if (fr->cutoff_scheme != ecutsVERLET)
-    {
-        gmx_incons("Invalid cut-off scheme passed!");
-    }
-
     if (!nbv->useGpu())
     {
         /* When dynamic pair-list  pruning is requested, we need to prune
@@ -364,25 +425,25 @@ static void do_nb_verlet(t_forcerec*                fr,
     nbv->dispatchNonbondedKernel(ilocality, *ic, stepWork, clearF, *fr, enerd, nrnb);
 }
 
-static inline void clear_rvecs_omp(int n, rvec v[])
+static inline void clearRVecs(ArrayRef<RVec> v, const bool useOpenmpThreading)
 {
-    int nth = gmx_omp_nthreads_get_simple_rvec_task(emntDefault, n);
+    int nth = gmx_omp_nthreads_get_simple_rvec_task(emntDefault, v.ssize());
 
     /* Note that we would like to avoid this conditional by putting it
      * into the omp pragma instead, but then we still take the full
      * omp parallel for overhead (at least with gcc5).
      */
-    if (nth == 1)
+    if (!useOpenmpThreading || nth == 1)
     {
-        for (int i = 0; i < n; i++)
+        for (RVec& elem : v)
         {
-            clear_rvec(v[i]);
+            clear_rvec(elem);
         }
     }
     else
     {
 #pragma omp parallel for num_threads(nth) schedule(static)
-        for (int i = 0; i < n; i++)
+        for (gmx::index i = 0; i < v.ssize(); i++)
         {
             clear_rvec(v[i]);
         }
@@ -541,7 +602,7 @@ static void computeSpecialForces(FILE*                          fplog,
                                  const matrix                   box,
                                  gmx::ArrayRef<const gmx::RVec> x,
                                  const t_mdatoms*               mdatoms,
-                                 real*                          lambda,
+                                 gmx::ArrayRef<const real>      lambda,
                                  const StepWorkload&            stepWork,
                                  gmx::ForceWithVirial*          forceWithVirial,
                                  gmx_enerdata_t*                enerd,
@@ -563,13 +624,22 @@ static void computeSpecialForces(FILE*                          fplog,
     if (inputrec->bPull && pull_have_potential(pull_work))
     {
         pull_potential_wrapper(cr, inputrec, box, x, forceWithVirial, mdatoms, enerd, pull_work,
-                               lambda, t, wcycle);
-
-        if (awh)
+                               lambda.data(), t, wcycle);
+    }
+    if (awh)
+    {
+        const bool          needForeignEnergyDifferences = awh->needForeignEnergyDifferences(step);
+        std::vector<double> foreignLambdaDeltaH, foreignLambdaDhDl;
+        if (needForeignEnergyDifferences)
         {
-            enerd->term[F_COM_PULL] += awh->applyBiasForcesAndUpdateBias(
-                    inputrec->ePBC, *mdatoms, box, forceWithVirial, t, step, wcycle, fplog);
+            enerd->foreignLambdaTerms.finalizePotentialContributions(enerd->dvdl_lin, lambda,
+                                                                     *inputrec->fepvals);
+            std::tie(foreignLambdaDeltaH, foreignLambdaDhDl) = enerd->foreignLambdaTerms.getTerms(cr);
         }
+
+        enerd->term[F_COM_PULL] += awh->applyBiasForcesAndUpdateBias(
+                inputrec->pbcType, mdatoms->massT, foreignLambdaDeltaH, foreignLambdaDhDl, box,
+                forceWithVirial, t, step, wcycle, fplog);
     }
 
     rvec* f = as_rvec_array(forceWithVirial->force_.data());
@@ -599,37 +669,24 @@ static void computeSpecialForces(FILE*                          fplog,
     }
 }
 
-/*! \brief Makes PME flags from StepWorkload data.
- *
- * \param[in]  stepWork     Step schedule flags
- * \returns                 PME flags
- */
-static int makePmeFlags(const StepWorkload& stepWork)
-{
-    return GMX_PME_SPREAD | GMX_PME_SOLVE | (stepWork.computeVirial ? GMX_PME_CALC_ENER_VIR : 0)
-           | (stepWork.computeEnergy ? GMX_PME_CALC_ENER_VIR : 0)
-           | (stepWork.computeForces ? GMX_PME_CALC_F : 0);
-}
-
 /*! \brief Launch the prepare_step and spread stages of PME GPU.
  *
  * \param[in]  pmedata              The PME structure
  * \param[in]  box                  The box matrix
  * \param[in]  stepWork             Step schedule flags
- * \param[in]  pmeFlags             PME flags
- * \param[in]  xReadyOnDevice       Event synchronizer indicating that the coordinates are ready in
- * the device memory. \param[in]  wcycle               The wallcycle structure
+ * \param[in]  xReadyOnDevice       Event synchronizer indicating that the coordinates are ready in the device memory.
+ * \param[in]  lambdaQ              The Coulomb lambda of the current state.
+ * \param[in]  wcycle               The wallcycle structure
  */
 static inline void launchPmeGpuSpread(gmx_pme_t*            pmedata,
                                       const matrix          box,
                                       const StepWorkload&   stepWork,
-                                      int                   pmeFlags,
                                       GpuEventSynchronizer* xReadyOnDevice,
+                                      const real            lambdaQ,
                                       gmx_wallcycle_t       wcycle)
 {
-    pme_gpu_prepare_computation(pmedata, stepWork.haveDynamicBox, box, wcycle, pmeFlags,
-                                stepWork.useGpuPmeFReduction);
-    pme_gpu_launch_spread(pmedata, xReadyOnDevice, wcycle);
+    pme_gpu_prepare_computation(pmedata, box, wcycle, stepWork);
+    pme_gpu_launch_spread(pmedata, xReadyOnDevice, wcycle, lambdaQ);
 }
 
 /*! \brief Launch the FFT and gather stages of PME GPU
@@ -637,12 +694,17 @@ static inline void launchPmeGpuSpread(gmx_pme_t*            pmedata,
  * This function only implements setting the output forces (no accumulation).
  *
  * \param[in]  pmedata        The PME structure
+ * \param[in]  lambdaQ        The Coulomb lambda of the current system state.
  * \param[in]  wcycle         The wallcycle structure
+ * \param[in]  stepWork       Step schedule flags
  */
-static void launchPmeGpuFftAndGather(gmx_pme_t* pmedata, gmx_wallcycle_t wcycle)
+static void launchPmeGpuFftAndGather(gmx_pme_t*               pmedata,
+                                     const real               lambdaQ,
+                                     gmx_wallcycle_t          wcycle,
+                                     const gmx::StepWorkload& stepWork)
 {
-    pme_gpu_launch_complex_transforms(pmedata, wcycle);
-    pme_gpu_launch_gather(pmedata, wcycle, PmeForceOutputHandling::Set);
+    pme_gpu_launch_complex_transforms(pmedata, wcycle, stepWork);
+    pme_gpu_launch_gather(pmedata, wcycle, lambdaQ);
 }
 
 /*! \brief
@@ -656,27 +718,25 @@ static void launchPmeGpuFftAndGather(gmx_pme_t* pmedata, gmx_wallcycle_t wcycle)
  *
  * \param[in]     nbv              Nonbonded verlet structure
  * \param[in,out] pmedata          PME module data
- * \param[in,out] forceOutputs     Output buffer for the forces and virial
+ * \param[in,out] forceOutputsNonbonded  Force outputs for the non-bonded forces and shift forces
+ * \param[in,out] forceOutputsPme  Force outputs for the PME forces and virial
  * \param[in,out] enerd            Energy data structure results are reduced into
+ * \param[in]     lambdaQ          The Coulomb lambda of the current system state.
  * \param[in]     stepWork         Step schedule flags
- * \param[in]     pmeFlags         PME flags
  * \param[in]     wcycle           The wallcycle structure
  */
 static void alternatePmeNbGpuWaitReduce(nonbonded_verlet_t* nbv,
                                         gmx_pme_t*          pmedata,
-                                        gmx::ForceOutputs*  forceOutputs,
+                                        gmx::ForceOutputs*  forceOutputsNonbonded,
+                                        gmx::ForceOutputs*  forceOutputsPme,
                                         gmx_enerdata_t*     enerd,
+                                        const real          lambdaQ,
                                         const StepWorkload& stepWork,
-                                        int                 pmeFlags,
                                         gmx_wallcycle_t     wcycle)
 {
     bool isPmeGpuDone = false;
     bool isNbGpuDone  = false;
 
-
-    gmx::ForceWithShiftForces& forceWithShiftForces = forceOutputs->forceWithShiftForces();
-    gmx::ForceWithVirial&      forceWithVirial      = forceOutputs->forceWithVirial();
-
     gmx::ArrayRef<const gmx::RVec> pmeGpuForces;
 
     while (!isPmeGpuDone || !isNbGpuDone)
@@ -685,22 +745,24 @@ static void alternatePmeNbGpuWaitReduce(nonbonded_verlet_t* nbv,
         {
             GpuTaskCompletion completionType =
                     (isNbGpuDone) ? GpuTaskCompletion::Wait : GpuTaskCompletion::Check;
-            isPmeGpuDone = pme_gpu_try_finish_task(pmedata, pmeFlags, wcycle, &forceWithVirial,
-                                                   enerd, completionType);
+            isPmeGpuDone = pme_gpu_try_finish_task(pmedata, stepWork, wcycle,
+                                                   &forceOutputsPme->forceWithVirial(), enerd,
+                                                   lambdaQ, completionType);
         }
 
         if (!isNbGpuDone)
         {
+            auto&             forceBuffersNonbonded = forceOutputsNonbonded->forceWithShiftForces();
             GpuTaskCompletion completionType =
                     (isPmeGpuDone) ? GpuTaskCompletion::Wait : GpuTaskCompletion::Check;
             isNbGpuDone = Nbnxm::gpu_try_finish_task(
                     nbv->gpu_nbv, stepWork, AtomLocality::Local, enerd->grpp.ener[egLJSR].data(),
-                    enerd->grpp.ener[egCOULSR].data(), forceWithShiftForces.shiftForces(),
+                    enerd->grpp.ener[egCOULSR].data(), forceBuffersNonbonded.shiftForces(),
                     completionType, wcycle);
 
             if (isNbGpuDone)
             {
-                nbv->atomdata_add_nbat_f_to_f(AtomLocality::Local, forceWithShiftForces.force());
+                nbv->atomdata_add_nbat_f_to_f(AtomLocality::Local, forceBuffersNonbonded.force());
             }
         }
     }
@@ -708,18 +770,14 @@ static void alternatePmeNbGpuWaitReduce(nonbonded_verlet_t* nbv,
 
 /*! \brief Set up the different force buffers; also does clearing.
  *
- * \param[in] fr        force record pointer
- * \param[in] pull_work The pull work object.
- * \param[in] inputrec  input record
+ * \param[in] forceHelperBuffers  Helper force buffers
  * \param[in] force     force array
  * \param[in] stepWork  Step schedule flags
  * \param[out] wcycle   wallcycle recording structure
  *
  * \returns             Cleared force output structure
  */
-static ForceOutputs setupForceOutputs(t_forcerec*                         fr,
-                                      pull_t*                             pull_work,
-                                      const t_inputrec&                   inputrec,
+static ForceOutputs setupForceOutputs(ForceHelperBuffers*                 forceHelperBuffers,
                                       gmx::ArrayRefWithPadding<gmx::RVec> force,
                                       const StepWorkload&                 stepWork,
                                       gmx_wallcycle_t                     wcycle)
@@ -727,12 +785,16 @@ static ForceOutputs setupForceOutputs(t_forcerec*                         fr,
     wallcycle_sub_start(wcycle, ewcsCLEAR_FORCE_BUFFER);
 
     /* NOTE: We assume fr->shiftForces is all zeros here */
-    gmx::ForceWithShiftForces forceWithShiftForces(force, stepWork.computeVirial, fr->shiftForces);
+    gmx::ForceWithShiftForces forceWithShiftForces(force, stepWork.computeVirial,
+                                                   forceHelperBuffers->shiftForces());
 
     if (stepWork.computeForces)
     {
         /* Clear the short- and long-range forces */
-        clear_rvecs_omp(fr->natoms_force_constr, as_rvec_array(forceWithShiftForces.force().data()));
+        clearRVecs(forceWithShiftForces.force(), true);
+
+        /* Clear the shift forces */
+        clearRVecs(forceWithShiftForces.shiftForces(), false);
     }
 
     /* If we need to compute the virial, we might need a separate
@@ -741,11 +803,13 @@ static ForceOutputs setupForceOutputs(t_forcerec*                         fr,
      * the same force (f in legacy calls) buffer as other algorithms.
      */
     const bool useSeparateForceWithVirialBuffer =
-            (stepWork.computeForces && (stepWork.computeVirial && fr->haveDirectVirialContributions));
+            (stepWork.computeForces
+             && (stepWork.computeVirial && forceHelperBuffers->haveDirectVirialContributions()));
     /* forceWithVirial uses the local atom range only */
-    gmx::ForceWithVirial forceWithVirial(useSeparateForceWithVirialBuffer ? fr->forceBufferForDirectVirialContributions
-                                                                          : force.unpaddedArrayRef(),
-                                         stepWork.computeVirial);
+    gmx::ForceWithVirial forceWithVirial(
+            useSeparateForceWithVirialBuffer ? forceHelperBuffers->forceBufferForDirectVirialContributions()
+                                             : force.unpaddedArrayRef(),
+            stepWork.computeVirial);
 
     if (useSeparateForceWithVirialBuffer)
     {
@@ -754,17 +818,13 @@ static ForceOutputs setupForceOutputs(t_forcerec*                         fr,
          * spread to non-local atoms, but that part of the buffer is
          * cleared separately in the vsite spreading code.
          */
-        clear_rvecs_omp(forceWithVirial.force_.size(), as_rvec_array(forceWithVirial.force_.data()));
-    }
-
-    if (inputrec.bPull && pull_have_constraint(pull_work))
-    {
-        clear_pull_forces(pull_work);
+        clearRVecs(forceWithVirial.force_, true);
     }
 
     wallcycle_sub_stop(wcycle, ewcsCLEAR_FORCE_BUFFER);
 
-    return ForceOutputs(forceWithShiftForces, forceWithVirial);
+    return ForceOutputs(forceWithShiftForces, forceHelperBuffers->haveDirectVirialContributions(),
+                        forceWithVirial);
 }
 
 
@@ -774,8 +834,6 @@ static DomainLifetimeWorkload setupDomainLifetimeWorkload(const t_inputrec&
                                                           const t_forcerec&         fr,
                                                           const pull_t*             pull_work,
                                                           const gmx_edsam*          ed,
-                                                          const t_idef&             idef,
-                                                          const t_fcdata&           fcd,
                                                           const t_mdatoms&          mdatoms,
                                                           const SimulationWorkload& simulationWork,
                                                           const StepWorkload&       stepWork)
@@ -784,10 +842,20 @@ static DomainLifetimeWorkload setupDomainLifetimeWorkload(const t_inputrec&
     // Note that haveSpecialForces is constant over the whole run
     domainWork.haveSpecialForces =
             haveSpecialForces(inputrec, *fr.forceProviders, pull_work, stepWork.computeForces, ed);
-    domainWork.haveCpuBondedWork  = haveCpuBondeds(fr);
-    domainWork.haveGpuBondedWork  = ((fr.gpuBonded != nullptr) && fr.gpuBonded->haveInteractions());
-    domainWork.haveRestraintsWork = haveRestraints(idef, fcd);
-    domainWork.haveCpuListedForceWork = haveCpuListedForces(fr, idef, fcd);
+    domainWork.haveCpuListedForceWork = false;
+    domainWork.haveCpuBondedWork      = false;
+    for (const auto& listedForces : fr.listedForces)
+    {
+        if (listedForces.haveCpuListedForces(*fr.fcdata))
+        {
+            domainWork.haveCpuListedForceWork = true;
+        }
+        if (listedForces.haveCpuBondeds())
+        {
+            domainWork.haveCpuBondedWork = true;
+        }
+    }
+    domainWork.haveGpuBondedWork = ((fr.gpuBonded != nullptr) && fr.gpuBonded->haveInteractions());
     // Note that haveFreeEnergyWork is constant over the whole run
     domainWork.haveFreeEnergyWork = (fr.efep != efepNO && mdatoms.nPerturbed != 0);
     // We assume we have local force work if there are CPU
@@ -803,27 +871,35 @@ static DomainLifetimeWorkload setupDomainLifetimeWorkload(const t_inputrec&
 /*! \brief Set up force flag stuct from the force bitmask.
  *
  * \param[in]      legacyFlags          Force bitmask flags used to construct the new flags
- * \param[in]      isNonbondedOn        Global override, if false forces to turn off all nonbonded calculation.
+ * \param[in]      mtsLevels            The multiple time-stepping levels, either empty or 2 levels
+ * \param[in]      step                 The current MD step
  * \param[in]      simulationWork       Simulation workload description.
  * \param[in]      rankHasPmeDuty       If this rank computes PME.
  *
  * \returns New Stepworkload description.
  */
-static StepWorkload setupStepWorkload(const int                 legacyFlags,
-                                      const bool                isNonbondedOn,
-                                      const SimulationWorkload& simulationWork,
-                                      const bool                rankHasPmeDuty)
+static StepWorkload setupStepWorkload(const int                     legacyFlags,
+                                      ArrayRef<const gmx::MtsLevel> mtsLevels,
+                                      const int64_t                 step,
+                                      const SimulationWorkload&     simulationWork,
+                                      const bool                    rankHasPmeDuty)
 {
+    GMX_ASSERT(mtsLevels.empty() || mtsLevels.size() == 2, "Expect 0 or 2 MTS levels");
+    const bool computeSlowForces = (mtsLevels.empty() || step % mtsLevels[1].stepFactor == 0);
+
     StepWorkload flags;
-    flags.stateChanged           = ((legacyFlags & GMX_FORCE_STATECHANGED) != 0);
-    flags.haveDynamicBox         = ((legacyFlags & GMX_FORCE_DYNAMICBOX) != 0);
-    flags.doNeighborSearch       = ((legacyFlags & GMX_FORCE_NS) != 0);
-    flags.computeVirial          = ((legacyFlags & GMX_FORCE_VIRIAL) != 0);
-    flags.computeEnergy          = ((legacyFlags & GMX_FORCE_ENERGY) != 0);
-    flags.computeForces          = ((legacyFlags & GMX_FORCE_FORCES) != 0);
-    flags.computeListedForces    = ((legacyFlags & GMX_FORCE_LISTED) != 0);
-    flags.computeNonbondedForces = ((legacyFlags & GMX_FORCE_NONBONDED) != 0) && isNonbondedOn;
-    flags.computeDhdl            = ((legacyFlags & GMX_FORCE_DHDL) != 0);
+    flags.stateChanged        = ((legacyFlags & GMX_FORCE_STATECHANGED) != 0);
+    flags.haveDynamicBox      = ((legacyFlags & GMX_FORCE_DYNAMICBOX) != 0);
+    flags.doNeighborSearch    = ((legacyFlags & GMX_FORCE_NS) != 0);
+    flags.computeSlowForces   = computeSlowForces;
+    flags.computeVirial       = ((legacyFlags & GMX_FORCE_VIRIAL) != 0);
+    flags.computeEnergy       = ((legacyFlags & GMX_FORCE_ENERGY) != 0);
+    flags.computeForces       = ((legacyFlags & GMX_FORCE_FORCES) != 0);
+    flags.computeListedForces = ((legacyFlags & GMX_FORCE_LISTED) != 0);
+    flags.computeNonbondedForces =
+            ((legacyFlags & GMX_FORCE_NONBONDED) != 0) && simulationWork.computeNonbonded
+            && !(simulationWork.computeNonbondedAtMtsLevel1 && !computeSlowForces);
+    flags.computeDhdl = ((legacyFlags & GMX_FORCE_DHDL) != 0);
 
     if (simulationWork.useGpuBufferOps)
     {
@@ -832,10 +908,11 @@ static StepWorkload setupStepWorkload(const int                 legacyFlags,
     }
     flags.useGpuXBufferOps = simulationWork.useGpuBufferOps;
     // on virial steps the CPU reduction path is taken
-    flags.useGpuFBufferOps    = simulationWork.useGpuBufferOps && !flags.computeVirial;
-    flags.useGpuPmeFReduction = flags.useGpuFBufferOps
-                                && (simulationWork.useGpuPme
-                                    && (rankHasPmeDuty || simulationWork.useGpuPmePpCommunication));
+    flags.useGpuFBufferOps = simulationWork.useGpuBufferOps && !flags.computeVirial;
+    flags.useGpuPmeFReduction = flags.computeSlowForces && flags.useGpuFBufferOps && simulationWork.useGpuPme
+                                && (rankHasPmeDuty || simulationWork.useGpuPmePpCommunication);
+    flags.useGpuXHalo = simulationWork.useGpuHaloExchange;
+    flags.useGpuFHalo = simulationWork.useGpuHaloExchange && flags.useGpuFBufferOps;
 
     return flags;
 }
@@ -843,7 +920,7 @@ static StepWorkload setupStepWorkload(const int                 legacyFlags,
 
 /* \brief Launch end-of-step GPU tasks: buffer clearing and rolling pruning.
  *
- * TODO: eliminate the \p useGpuNonbonded and \p useGpuNonbonded when these are
+ * TODO: eliminate \p useGpuPmeOnThisRank when this is
  * incorporated in DomainLifetimeWorkload.
  */
 static void launchGpuEndOfStepTasks(nonbonded_verlet_t*               nbv,
@@ -851,12 +928,11 @@ static void launchGpuEndOfStepTasks(nonbonded_verlet_t*               nbv,
                                     gmx_pme_t*                        pmedata,
                                     gmx_enerdata_t*                   enerd,
                                     const gmx::MdrunScheduleWorkload& runScheduleWork,
-                                    bool                              useGpuNonbonded,
-                                    bool                              useGpuPme,
+                                    bool                              useGpuPmeOnThisRank,
                                     int64_t                           step,
                                     gmx_wallcycle_t                   wcycle)
 {
-    if (useGpuNonbonded)
+    if (runScheduleWork.simulationWork.useGpuNonbonded && runScheduleWork.stepWork.computeNonbondedForces)
     {
         /* Launch pruning before buffer clearing because the API overhead of the
          * clear kernel launches can leave the GPU idle while it could be running
@@ -875,7 +951,7 @@ static void launchGpuEndOfStepTasks(nonbonded_verlet_t*               nbv,
         wallcycle_stop(wcycle, ewcLAUNCH_GPU);
     }
 
-    if (useGpuPme)
+    if (useGpuPmeOnThisRank)
     {
         pme_gpu_reinit_computation(pmedata, wcycle);
     }
@@ -891,6 +967,146 @@ static void launchGpuEndOfStepTasks(nonbonded_verlet_t*               nbv,
     }
 }
 
+//! \brief Data structure to hold dipole-related data and staging arrays
+struct DipoleData
+{
+    //! Dipole staging for fast summing over MPI
+    gmx::DVec muStaging[2] = { { 0.0, 0.0, 0.0 } };
+    //! Dipole staging for states A and B (index 0 and 1 resp.)
+    gmx::RVec muStateAB[2] = { { 0.0_real, 0.0_real, 0.0_real } };
+};
+
+
+static void reduceAndUpdateMuTot(DipoleData*                   dipoleData,
+                                 const t_commrec*              cr,
+                                 const bool                    haveFreeEnergy,
+                                 gmx::ArrayRef<const real>     lambda,
+                                 rvec                          muTotal,
+                                 const DDBalanceRegionHandler& ddBalanceRegionHandler)
+{
+    if (PAR(cr))
+    {
+        gmx_sumd(2 * DIM, dipoleData->muStaging[0], cr);
+        ddBalanceRegionHandler.reopenRegionCpu();
+    }
+    for (int i = 0; i < 2; i++)
+    {
+        for (int j = 0; j < DIM; j++)
+        {
+            dipoleData->muStateAB[i][j] = dipoleData->muStaging[i][j];
+        }
+    }
+
+    if (!haveFreeEnergy)
+    {
+        copy_rvec(dipoleData->muStateAB[0], muTotal);
+    }
+    else
+    {
+        for (int j = 0; j < DIM; j++)
+        {
+            muTotal[j] = (1.0 - lambda[efptCOUL]) * dipoleData->muStateAB[0][j]
+                         + lambda[efptCOUL] * dipoleData->muStateAB[1][j];
+        }
+    }
+}
+
+/*! \brief Combines MTS level0 and level1 force buffes into a full and MTS-combined force buffer.
+ *
+ * \param[in]     numAtoms        The number of atoms to combine forces for
+ * \param[in,out] forceMtsLevel0  Input: F_level0, output: F_level0 + F_level1
+ * \param[in,out] forceMts        Input: F_level1, output: F_level0 + mtsFactor * F_level1
+ * \param[in]     mtsFactor       The factor between the level0 and level1 time step
+ */
+static void combineMtsForces(const int      numAtoms,
+                             ArrayRef<RVec> forceMtsLevel0,
+                             ArrayRef<RVec> forceMts,
+                             const real     mtsFactor)
+{
+    const int gmx_unused numThreads = gmx_omp_nthreads_get(emntDefault);
+#pragma omp parallel for num_threads(numThreads) schedule(static)
+    for (int i = 0; i < numAtoms; i++)
+    {
+        const RVec forceMtsLevel0Tmp = forceMtsLevel0[i];
+        forceMtsLevel0[i] += forceMts[i];
+        forceMts[i] = forceMtsLevel0Tmp + mtsFactor * forceMts[i];
+    }
+}
+
+/*! \brief Setup for the local and non-local GPU force reductions:
+ * reinitialization plus the registration of forces and dependencies.
+ *
+ * \param [in] runScheduleWork               Schedule workload flag structure
+ * \param [in] cr                            Communication record object
+ * \param [in] fr                            Force record object
+ */
+static void setupGpuForceReductions(gmx::MdrunScheduleWorkload* runScheduleWork,
+                                    const t_commrec*            cr,
+                                    t_forcerec*                 fr)
+{
+
+    nonbonded_verlet_t*          nbv      = fr->nbv.get();
+    gmx::StatePropagatorDataGpu* stateGpu = fr->stateGpu;
+
+    // (re-)initialize local GPU force reduction
+    const bool accumulate =
+            runScheduleWork->domainWork.haveCpuLocalForceWork || havePPDomainDecomposition(cr);
+    const int atomStart = 0;
+    fr->gpuForceReduction[gmx::AtomLocality::Local]->reinit(
+            stateGpu->getForces(), nbv->getNumAtoms(AtomLocality::Local), nbv->getGridIndices(),
+            atomStart, accumulate, stateGpu->fReducedOnDevice());
+
+    // register forces and add dependencies
+    fr->gpuForceReduction[gmx::AtomLocality::Local]->registerNbnxmForce(nbv->getGpuForces());
+
+    if (runScheduleWork->simulationWork.useGpuPme
+        && (thisRankHasDuty(cr, DUTY_PME) || runScheduleWork->simulationWork.useGpuPmePpCommunication))
+    {
+        void* forcePtr = thisRankHasDuty(cr, DUTY_PME) ? pme_gpu_get_device_f(fr->pmedata)
+                                                       : // PME force buffer on same GPU
+                                 fr->pmePpCommGpu->getGpuForceStagingPtr(); // buffer received from other GPU
+        fr->gpuForceReduction[gmx::AtomLocality::Local]->registerRvecForce(forcePtr);
+
+        GpuEventSynchronizer* const pmeSynchronizer =
+                (thisRankHasDuty(cr, DUTY_PME) ? pme_gpu_get_f_ready_synchronizer(fr->pmedata)
+                                               : // PME force buffer on same GPU
+                         fr->pmePpCommGpu->getForcesReadySynchronizer()); // buffer received from other GPU
+        fr->gpuForceReduction[gmx::AtomLocality::Local]->addDependency(pmeSynchronizer);
+    }
+
+    if ((runScheduleWork->domainWork.haveCpuLocalForceWork || havePPDomainDecomposition(cr))
+        && !runScheduleWork->simulationWork.useGpuHaloExchange)
+    {
+        fr->gpuForceReduction[gmx::AtomLocality::Local]->addDependency(
+                stateGpu->getForcesReadyOnDeviceEvent(AtomLocality::Local, true));
+    }
+
+    if (runScheduleWork->simulationWork.useGpuHaloExchange)
+    {
+        fr->gpuForceReduction[gmx::AtomLocality::Local]->addDependency(
+                cr->dd->gpuHaloExchange[0][0]->getForcesReadyOnDeviceEvent());
+    }
+
+    if (havePPDomainDecomposition(cr))
+    {
+        // (re-)initialize non-local GPU force reduction
+        const bool accumulate = runScheduleWork->domainWork.haveCpuBondedWork
+                                || runScheduleWork->domainWork.haveFreeEnergyWork;
+        const int atomStart = dd_numHomeAtoms(*cr->dd);
+        fr->gpuForceReduction[gmx::AtomLocality::NonLocal]->reinit(
+                stateGpu->getForces(), nbv->getNumAtoms(AtomLocality::NonLocal),
+                nbv->getGridIndices(), atomStart, accumulate);
+
+        // register forces and add dependencies
+        fr->gpuForceReduction[gmx::AtomLocality::NonLocal]->registerNbnxmForce(nbv->getGpuForces());
+        if (runScheduleWork->domainWork.haveCpuBondedWork || runScheduleWork->domainWork.haveFreeEnergyWork)
+        {
+            fr->gpuForceReduction[gmx::AtomLocality::NonLocal]->addDependency(
+                    stateGpu->getForcesReadyOnDeviceEvent(AtomLocality::NonLocal, true));
+        }
+    }
+}
+
 
 void do_force(FILE*                               fplog,
               const t_commrec*                    cr,
@@ -907,53 +1123,38 @@ void do_force(FILE*                               fplog,
               const matrix                        box,
               gmx::ArrayRefWithPadding<gmx::RVec> x,
               history_t*                          hist,
-              gmx::ArrayRefWithPadding<gmx::RVec> force,
+              gmx::ForceBuffersView*              forceView,
               tensor                              vir_force,
               const t_mdatoms*                    mdatoms,
               gmx_enerdata_t*                     enerd,
-              t_fcdata*                           fcd,
-              gmx::ArrayRef<real>                 lambda,
-              t_graph*                            graph,
+              gmx::ArrayRef<const real>           lambda,
               t_forcerec*                         fr,
               gmx::MdrunScheduleWorkload*         runScheduleWork,
-              const gmx_vsite_t*                  vsite,
-              rvec                                mu_tot,
+              gmx::VirtualSitesHandler*           vsite,
+              rvec                                muTotal,
               double                              t,
               gmx_edsam*                          ed,
               int                                 legacyFlags,
               const DDBalanceRegionHandler&       ddBalanceRegionHandler)
 {
-    int                          i, j;
-    double                       mu[2 * DIM];
-    nonbonded_verlet_t*          nbv      = fr->nbv.get();
-    interaction_const_t*         ic       = fr->ic;
-    gmx::StatePropagatorDataGpu* stateGpu = fr->stateGpu;
+    auto force = forceView->forceWithPadding();
+    GMX_ASSERT(force.unpaddedArrayRef().ssize() >= fr->natoms_force_constr,
+               "The size of the force buffer should be at least the number of atoms to compute "
+               "forces for");
 
-    // TODO remove the code below when the legacy flags are not in use anymore
-    /* modify force flag if not doing nonbonded */
-    if (!fr->bNonbonded)
-    {
-        legacyFlags &= ~GMX_FORCE_NONBONDED;
-    }
+    nonbonded_verlet_t*  nbv = fr->nbv.get();
+    interaction_const_t* ic  = fr->ic;
 
-    const SimulationWorkload& simulationWork = runScheduleWork->simulationWork;
+    gmx::StatePropagatorDataGpu* stateGpu = fr->stateGpu;
 
+    const SimulationWorkload& simulationWork = runScheduleWork->simulationWork;
 
-    runScheduleWork->stepWork    = setupStepWorkload(legacyFlags, fr->bNonbonded, simulationWork,
-                                                  thisRankHasDuty(cr, DUTY_PME));
+    runScheduleWork->stepWork    = setupStepWorkload(legacyFlags, inputrec->mtsLevels, step,
+                                                  simulationWork, thisRankHasDuty(cr, DUTY_PME));
     const StepWorkload& stepWork = runScheduleWork->stepWork;
 
-
-    const bool useGpuPmeOnThisRank = simulationWork.useGpuPme && thisRankHasDuty(cr, DUTY_PME);
-    const int  pmeFlags            = makePmeFlags(stepWork);
-
-    // Switches on whether to use GPU for position and force buffer operations
-    // TODO consider all possible combinations of triggers, and how to combine optimally in each case.
-    const BufferOpsUseGpu useGpuXBufOps =
-            stepWork.useGpuXBufferOps ? BufferOpsUseGpu::True : BufferOpsUseGpu::False;
-    // GPU Force buffer ops are disabled on virial steps, because the virial calc is not yet ported to GPU
-    const BufferOpsUseGpu useGpuFBufOps =
-            stepWork.useGpuFBufferOps ? BufferOpsUseGpu::True : BufferOpsUseGpu::False;
+    const bool useGpuPmeOnThisRank =
+            simulationWork.useGpuPme && thisRankHasDuty(cr, DUTY_PME) && stepWork.computeSlowForces;
 
     /* At a search step we need to start the first balancing region
      * somewhere early inside the step after communication during domain
@@ -964,24 +1165,9 @@ void do_force(FILE*                               fplog,
         ddBalanceRegionHandler.openBeforeForceComputationCpu(DdAllowBalanceRegionReopen::yes);
     }
 
-    const int start  = 0;
-    const int homenr = mdatoms->homenr;
-
     clear_mat(vir_force);
 
-    if (stepWork.stateChanged)
-    {
-        if (inputrecNeedMutot(inputrec))
-        {
-            /* 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.unpaddedArrayRef(), mdatoms->chargeA, mdatoms->chargeB,
-                    mdatoms->nChargePerturbed, mu, mu + DIM);
-        }
-    }
-
-    if (fr->ePBC != epbcNONE)
+    if (fr->pbcType != PbcType::No)
     {
         /* Compute shift vectors every step,
          * because of pressure coupling or box deformation!
@@ -995,40 +1181,29 @@ void do_force(FILE*                               fplog,
         const bool calcCGCM = (fillGrid && !DOMAINDECOMP(cr));
         if (calcCGCM)
         {
-            put_atoms_in_box_omp(fr->ePBC, box, x.unpaddedArrayRef().subArray(0, homenr),
+            put_atoms_in_box_omp(fr->pbcType, box, x.unpaddedArrayRef().subArray(0, mdatoms->homenr),
                                  gmx_omp_nthreads_get(emntDefault));
-            inc_nrnb(nrnb, eNR_SHIFTX, homenr);
-        }
-        else if (EI_ENERGY_MINIMIZATION(inputrec->eI) && graph)
-        {
-            unshift_self(graph, box, as_rvec_array(x.unpaddedArrayRef().data()));
+            inc_nrnb(nrnb, eNR_SHIFTX, mdatoms->homenr);
         }
     }
 
     nbnxn_atomdata_copy_shiftvec(stepWork.haveDynamicBox, fr->shift_vec, nbv->nbat.get());
 
-#if GMX_MPI
     const bool pmeSendCoordinatesFromGpu =
-            simulationWork.useGpuPmePpCommunication && !(stepWork.doNeighborSearch);
+            GMX_MPI && simulationWork.useGpuPmePpCommunication && !(stepWork.doNeighborSearch);
     const bool reinitGpuPmePpComms =
-            simulationWork.useGpuPmePpCommunication && (stepWork.doNeighborSearch);
-#endif
+            GMX_MPI && simulationWork.useGpuPmePpCommunication && (stepWork.doNeighborSearch);
 
-    const auto localXReadyOnDevice = (stateGpu != nullptr)
+    const auto localXReadyOnDevice = (useGpuPmeOnThisRank || simulationWork.useGpuBufferOps)
                                              ? stateGpu->getCoordinatesReadyOnDeviceEvent(
                                                        AtomLocality::Local, simulationWork, stepWork)
                                              : nullptr;
 
-#if GMX_MPI
     // If coordinates are to be sent to PME task from CPU memory, perform that send here.
     // Otherwise the send will occur after H2D coordinate transfer.
-    if (!thisRankHasDuty(cr, DUTY_PME) && !pmeSendCoordinatesFromGpu)
+    if (GMX_MPI && !thisRankHasDuty(cr, DUTY_PME) && !pmeSendCoordinatesFromGpu && stepWork.computeSlowForces)
     {
-        /* 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.
-         */
+        /* Send particle coordinates to the pme nodes */
         if (!stepWork.doNeighborSearch && simulationWork.useGpuUpdate)
         {
             GMX_RELEASE_ASSERT(false,
@@ -1043,16 +1218,16 @@ void do_force(FILE*                               fplog,
                                  step, simulationWork.useGpuPmePpCommunication, reinitGpuPmePpComms,
                                  pmeSendCoordinatesFromGpu, localXReadyOnDevice, wcycle);
     }
-#endif /* GMX_MPI */
 
     // Coordinates on the device are needed if PME or BufferOps are offloaded.
     // The local coordinates can be copied right away.
     // NOTE: Consider moving this copy to right after they are updated and constrained,
     //       if the later is not offloaded.
-    if (useGpuPmeOnThisRank || useGpuXBufOps == BufferOpsUseGpu::True)
+    if (useGpuPmeOnThisRank || stepWork.useGpuXBufferOps)
     {
         if (stepWork.doNeighborSearch)
         {
+            // TODO refactor this to do_md, after partitioning.
             stateGpu->reinit(mdatoms->homenr,
                              cr->dd != nullptr ? dd_numAtomsZones(*cr->dd) : mdatoms->homenr);
             if (useGpuPmeOnThisRank)
@@ -1071,19 +1246,6 @@ void do_force(FILE*                               fplog,
         }
     }
 
-    // TODO Update this comment when introducing SimulationWorkload
-    //
-    // The conditions for gpuHaloExchange e.g. using GPU buffer
-    // operations were checked before construction, so here we can
-    // just use it and assert upon any conditions.
-    gmx::GpuHaloExchange* gpuHaloExchange =
-            (havePPDomainDecomposition(cr) ? cr->dd->gpuHaloExchange.get() : nullptr);
-    const bool ddUsesGpuDirectCommunication = (gpuHaloExchange != nullptr);
-    GMX_ASSERT(!ddUsesGpuDirectCommunication || (useGpuXBufOps == BufferOpsUseGpu::True),
-               "Must use coordinate buffer ops with GPU halo exchange");
-    const bool useGpuForcesHaloExchange =
-            ddUsesGpuDirectCommunication && (useGpuFBufOps == BufferOpsUseGpu::True);
-
     // Copy coordinate from the GPU if update is on the GPU and there
     // are forces to be computed on the CPU, or for the computation of
     // virial, or if host-side data will be transferred from this task
@@ -1092,7 +1254,12 @@ void do_force(FILE*                               fplog,
     // hence copy is not needed.
     const bool haveHostPmePpComms =
             !thisRankHasDuty(cr, DUTY_PME) && !simulationWork.useGpuPmePpCommunication;
-    const bool haveHostHaloExchangeComms = havePPDomainDecomposition(cr) && !ddUsesGpuDirectCommunication;
+
+    GMX_ASSERT(simulationWork.useGpuHaloExchange
+                       == ((cr->dd != nullptr) && (!cr->dd->gpuHaloExchange[0].empty())),
+               "The GPU halo exchange is active, but it has not been constructed.");
+    const bool haveHostHaloExchangeComms =
+            havePPDomainDecomposition(cr) && !simulationWork.useGpuHaloExchange;
 
     bool gmx_used_in_debug haveCopiedXFromGpu = false;
     if (simulationWork.useGpuUpdate && !stepWork.doNeighborSearch
@@ -1104,35 +1271,30 @@ void do_force(FILE*                               fplog,
         haveCopiedXFromGpu = true;
     }
 
-#if GMX_MPI
     // If coordinates are to be sent to PME task from GPU memory, perform that send here.
     // Otherwise the send will occur before the H2D coordinate transfer.
     if (!thisRankHasDuty(cr, DUTY_PME) && pmeSendCoordinatesFromGpu)
     {
-        /* 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.
-         */
+        /* Send particle coordinates to the pme nodes */
         gmx_pme_send_coordinates(fr, cr, box, as_rvec_array(x.unpaddedArrayRef().data()), lambda[efptCOUL],
                                  lambda[efptVDW], (stepWork.computeVirial || stepWork.computeEnergy),
                                  step, simulationWork.useGpuPmePpCommunication, reinitGpuPmePpComms,
                                  pmeSendCoordinatesFromGpu, localXReadyOnDevice, wcycle);
     }
-#endif /* GMX_MPI */
 
     if (useGpuPmeOnThisRank)
     {
-        launchPmeGpuSpread(fr->pmedata, box, stepWork, pmeFlags, localXReadyOnDevice, wcycle);
+        launchPmeGpuSpread(fr->pmedata, box, stepWork, localXReadyOnDevice, lambda[efptCOUL], wcycle);
     }
 
+    const gmx::DomainLifetimeWorkload& domainWork = runScheduleWork->domainWork;
+
     /* do gridding for pair search */
     if (stepWork.doNeighborSearch)
     {
-        if (graph && stepWork.stateChanged)
+        if (fr->wholeMoleculeTransform && stepWork.stateChanged)
         {
-            /* Calculate intramolecular shift vectors to make molecules whole */
-            mk_mshift(fplog, graph, fr->ePBC, box, as_rvec_array(x.unpaddedArrayRef().data()));
+            fr->wholeMoleculeTransform->updateForAtomPbcJumps(x.unpaddedArrayRef(), box);
         }
 
         // TODO
@@ -1162,7 +1324,8 @@ void do_force(FILE*                               fplog,
             wallcycle_sub_stop(wcycle, ewcsNBS_GRID_NONLOCAL);
         }
 
-        nbv->setAtomProperties(*mdatoms, fr->cginfo);
+        nbv->setAtomProperties(gmx::constArrayRefFromArray(mdatoms->typeA, mdatoms->nr),
+                               gmx::constArrayRefFromArray(mdatoms->chargeA, mdatoms->nr), fr->cginfo);
 
         wallcycle_stop(wcycle, ewcNS);
 
@@ -1191,41 +1354,35 @@ void do_force(FILE*                               fplog,
             }
             wallcycle_stop(wcycle, ewcLAUNCH_GPU);
         }
-    }
 
-    if (stepWork.doNeighborSearch)
-    {
         // Need to run after the GPU-offload bonded interaction lists
         // are set up to be able to determine whether there is bonded work.
         runScheduleWork->domainWork = setupDomainLifetimeWorkload(
-                *inputrec, *fr, pull_work, ed, top->idef, *fcd, *mdatoms, simulationWork, stepWork);
+                *inputrec, *fr, pull_work, ed, *mdatoms, simulationWork, stepWork);
 
         wallcycle_start_nocount(wcycle, ewcNS);
         wallcycle_sub_start(wcycle, ewcsNBS_SEARCH_LOCAL);
         /* Note that with a GPU the launch overhead of the list transfer is not timed separately */
-        nbv->constructPairlist(InteractionLocality::Local, &top->excls, step, nrnb);
+        nbv->constructPairlist(InteractionLocality::Local, top->excls, step, nrnb);
 
         nbv->setupGpuShortRangeWork(fr->gpuBonded, InteractionLocality::Local);
 
         wallcycle_sub_stop(wcycle, ewcsNBS_SEARCH_LOCAL);
         wallcycle_stop(wcycle, ewcNS);
 
-        if (useGpuXBufOps == BufferOpsUseGpu::True)
+        if (stepWork.useGpuXBufferOps)
         {
             nbv->atomdata_init_copy_x_to_nbat_x_gpu();
         }
-        // For force buffer ops, we use the below conditon rather than
-        // useGpuFBufOps to ensure that init is performed even if this
-        // NS step is also a virial step (on which f buf ops are deactivated).
-        if (simulationWork.useGpuBufferOps && simulationWork.useGpuNonbonded && (GMX_GPU == GMX_GPU_CUDA))
+
+        if (simulationWork.useGpuBufferOps)
         {
-            GMX_ASSERT(stateGpu, "stateGpu should be valid when buffer ops are offloaded");
-            nbv->atomdata_init_add_nbat_f_to_f_gpu(stateGpu->fReducedOnDevice());
+            setupGpuForceReductions(runScheduleWork, cr, fr);
         }
     }
-    else if (!EI_TPI(inputrec->eI))
+    else if (!EI_TPI(inputrec->eI) && stepWork.computeNonbondedForces)
     {
-        if (useGpuXBufOps == BufferOpsUseGpu::True)
+        if (stepWork.useGpuXBufferOps)
         {
             GMX_ASSERT(stateGpu, "stateGpu should be valid when buffer ops are offloaded");
             nbv->convertCoordinatesGpu(AtomLocality::Local, false, stateGpu->getCoordinates(),
@@ -1244,9 +1401,7 @@ void do_force(FILE*                               fplog,
         }
     }
 
-    const gmx::DomainLifetimeWorkload& domainWork = runScheduleWork->domainWork;
-
-    if (simulationWork.useGpuNonbonded)
+    if (simulationWork.useGpuNonbonded && (stepWork.computeNonbondedForces || domainWork.haveGpuBondedWork))
     {
         ddBalanceRegionHandler.openBeforeForceComputationGpu();
 
@@ -1254,7 +1409,7 @@ void do_force(FILE*                               fplog,
 
         wallcycle_sub_start(wcycle, ewcsLAUNCH_GPU_NONBONDED);
         Nbnxm::gpu_upload_shiftvec(nbv->gpu_nbv, nbv->nbat.get());
-        if (stepWork.doNeighborSearch || (useGpuXBufOps == BufferOpsUseGpu::False))
+        if (stepWork.doNeighborSearch || !stepWork.useGpuXBufferOps)
         {
             Nbnxm::gpu_copy_xq_to_gpu(nbv->gpu_nbv, nbv->nbat.get(), AtomLocality::Local);
         }
@@ -1266,7 +1421,7 @@ void do_force(FILE*                               fplog,
         if (domainWork.haveGpuBondedWork && !havePPDomainDecomposition(cr))
         {
             wallcycle_sub_start(wcycle, ewcsLAUNCH_GPU_BONDED);
-            fr->gpuBonded->launchKernel(fr, stepWork, box);
+            fr->gpuBonded->setPbcAndlaunchKernel(fr->pbcType, box, fr->bMolPBC, stepWork);
             wallcycle_sub_stop(wcycle, ewcsLAUNCH_GPU_BONDED);
         }
 
@@ -1283,7 +1438,7 @@ void do_force(FILE*                               fplog,
         // X copy/transform to allow overlap as well as after the GPU NB
         // launch to avoid FFT launch overhead hijacking the CPU and delaying
         // the nonbonded kernel.
-        launchPmeGpuFftAndGather(fr->pmedata, wcycle);
+        launchPmeGpuFftAndGather(fr->pmedata, lambda[efptCOUL], wcycle, stepWork);
     }
 
     /* Communicate coordinates and sum dipole if necessary +
@@ -1296,23 +1451,27 @@ void do_force(FILE*                               fplog,
             wallcycle_start_nocount(wcycle, ewcNS);
             wallcycle_sub_start(wcycle, ewcsNBS_SEARCH_NONLOCAL);
             /* Note that with a GPU the launch overhead of the list transfer is not timed separately */
-            nbv->constructPairlist(InteractionLocality::NonLocal, &top->excls, step, nrnb);
+            nbv->constructPairlist(InteractionLocality::NonLocal, top->excls, step, nrnb);
 
             nbv->setupGpuShortRangeWork(fr->gpuBonded, InteractionLocality::NonLocal);
             wallcycle_sub_stop(wcycle, ewcsNBS_SEARCH_NONLOCAL);
             wallcycle_stop(wcycle, ewcNS);
-            if (ddUsesGpuDirectCommunication)
+            // TODO refactor this GPU halo exchange re-initialisation
+            // to location in do_md where GPU halo exchange is
+            // constructed at partitioning, after above stateGpu
+            // re-initialization has similarly been refactored
+            if (simulationWork.useGpuHaloExchange)
             {
-                gpuHaloExchange->reinitHalo(stateGpu->getCoordinates(), stateGpu->getForces());
+                reinitGpuHaloExchange(*cr, stateGpu->getCoordinates(), stateGpu->getForces());
             }
         }
         else
         {
-            if (ddUsesGpuDirectCommunication)
+            if (stepWork.useGpuXHalo)
             {
                 // The following must be called after local setCoordinates (which records an event
                 // when the coordinate data has been copied to the device).
-                gpuHaloExchange->communicateHaloCoordinates(box, localXReadyOnDevice);
+                communicateGpuHaloCoordinates(*cr, box, localXReadyOnDevice);
 
                 if (domainWork.haveCpuBondedWork || domainWork.haveFreeEnergyWork)
                 {
@@ -1329,10 +1488,9 @@ void do_force(FILE*                               fplog,
                 dd_move_x(cr->dd, box, x.unpaddedArrayRef(), wcycle);
             }
 
-            if (useGpuXBufOps == BufferOpsUseGpu::True)
+            if (stepWork.useGpuXBufferOps)
             {
-                // The condition here was (pme != nullptr && pme_gpu_get_device_x(fr->pmedata) != nullptr)
-                if (!useGpuPmeOnThisRank && !ddUsesGpuDirectCommunication)
+                if (!useGpuPmeOnThisRank && !stepWork.useGpuXHalo)
                 {
                     stateGpu->copyCoordinatesToGpu(x.unpaddedArrayRef(), AtomLocality::NonLocal);
                 }
@@ -1350,7 +1508,7 @@ void do_force(FILE*                               fplog,
         {
             wallcycle_start(wcycle, ewcLAUNCH_GPU);
 
-            if (stepWork.doNeighborSearch || (useGpuXBufOps == BufferOpsUseGpu::False))
+            if (stepWork.doNeighborSearch || !stepWork.useGpuXBufferOps)
             {
                 wallcycle_sub_start(wcycle, ewcsLAUNCH_GPU_NONBONDED);
                 Nbnxm::gpu_copy_xq_to_gpu(nbv->gpu_nbv, nbv->nbat.get(), AtomLocality::NonLocal);
@@ -1360,7 +1518,7 @@ void do_force(FILE*                               fplog,
             if (domainWork.haveGpuBondedWork)
             {
                 wallcycle_sub_start(wcycle, ewcsLAUNCH_GPU_BONDED);
-                fr->gpuBonded->launchKernel(fr, stepWork, box);
+                fr->gpuBonded->setPbcAndlaunchKernel(fr->pbcType, box, fr->bMolPBC, stepWork);
                 wallcycle_sub_stop(wcycle, ewcsLAUNCH_GPU_BONDED);
             }
 
@@ -1374,7 +1532,7 @@ void do_force(FILE*                               fplog,
         }
     }
 
-    if (simulationWork.useGpuNonbonded)
+    if (simulationWork.useGpuNonbonded && stepWork.computeNonbondedForces)
     {
         /* launch D2H copy-back F */
         wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU);
@@ -1394,43 +1552,31 @@ void do_force(FILE*                               fplog,
         wallcycle_stop(wcycle, ewcLAUNCH_GPU);
     }
 
-    if (stepWork.stateChanged && inputrecNeedMutot(inputrec))
+    gmx::ArrayRef<const gmx::RVec> xWholeMolecules;
+    if (fr->wholeMoleculeTransform)
     {
-        if (PAR(cr))
-        {
-            gmx_sumd(2 * DIM, mu, cr);
+        xWholeMolecules = fr->wholeMoleculeTransform->wholeMoleculeCoordinates(x.unpaddedArrayRef(), box);
+    }
 
-            ddBalanceRegionHandler.reopenRegionCpu();
-        }
+    DipoleData dipoleData;
 
-        for (i = 0; i < 2; i++)
-        {
-            for (j = 0; j < DIM; j++)
-            {
-                fr->mu_tot[i][j] = mu[i * DIM + j];
-            }
-        }
-    }
-    if (mdatoms->nChargePerturbed == 0)
+    if (simulationWork.computeMuTot)
     {
-        copy_rvec(fr->mu_tot[0], mu_tot);
-    }
-    else
-    {
-        for (j = 0; j < DIM; j++)
-        {
-            mu_tot[j] = (1.0 - lambda[efptCOUL]) * fr->mu_tot[0][j] + lambda[efptCOUL] * fr->mu_tot[1][j];
-        }
+        const int start = 0;
+
+        /* Calculate total (local) dipole moment in a temporary common array.
+         * This makes it possible to sum them over nodes faster.
+         */
+        gmx::ArrayRef<const gmx::RVec> xRef =
+                (xWholeMolecules.empty() ? x.unpaddedArrayRef() : xWholeMolecules);
+        calc_mu(start, mdatoms->homenr, xRef, mdatoms->chargeA, mdatoms->chargeB,
+                mdatoms->nChargePerturbed, dipoleData.muStaging[0], dipoleData.muStaging[1]);
+
+        reduceAndUpdateMuTot(&dipoleData, cr, (fr->efep != efepNO), lambda, muTotal, ddBalanceRegionHandler);
     }
 
     /* Reset energies */
     reset_enerdata(enerd);
-    /* Clear the shift forces */
-    // TODO: This should be linked to the shift force buffer in use, or cleared before use instead
-    for (gmx::RVec& elem : fr->shiftForces)
-    {
-        elem = { 0.0_real, 0.0_real, 0.0_real };
-    }
 
     if (DOMAINDECOMP(cr) && !thisRankHasDuty(cr, DUTY_PME))
     {
@@ -1459,10 +1605,35 @@ void do_force(FILE*                               fplog,
      */
     wallcycle_start(wcycle, ewcFORCE);
 
-    // Set up and clear force outputs.
-    // We use std::move to keep the compiler happy, it has no effect.
-    ForceOutputs forceOut =
-            setupForceOutputs(fr, pull_work, *inputrec, std::move(force), stepWork, wcycle);
+    /* Set up and clear force outputs:
+     * forceOutMtsLevel0:  everything except what is in the other two outputs
+     * forceOutMtsLevel1:  PME-mesh and listed-forces group 1
+     * forceOutNonbonded: non-bonded forces
+     * Without multiple time stepping all point to the same object.
+     * With multiple time-stepping the use is different for MTS fast (level0 only) and slow steps.
+     */
+    ForceOutputs forceOutMtsLevel0 =
+            setupForceOutputs(&fr->forceHelperBuffers[0], force, stepWork, wcycle);
+
+    // Force output for MTS combined forces, only set at level1 MTS steps
+    std::optional<ForceOutputs> forceOutMts =
+            (fr->useMts && stepWork.computeSlowForces)
+                    ? std::optional(setupForceOutputs(&fr->forceHelperBuffers[1],
+                                                      forceView->forceMtsCombinedWithPadding(),
+                                                      stepWork, wcycle))
+                    : std::nullopt;
+
+    ForceOutputs* forceOutMtsLevel1 =
+            fr->useMts ? (stepWork.computeSlowForces ? &forceOutMts.value() : nullptr) : &forceOutMtsLevel0;
+
+    const bool nonbondedAtMtsLevel1 = runScheduleWork->simulationWork.computeNonbondedAtMtsLevel1;
+
+    ForceOutputs* forceOutNonbonded = nonbondedAtMtsLevel1 ? forceOutMtsLevel1 : &forceOutMtsLevel0;
+
+    if (inputrec->bPull && pull_have_constraint(pull_work))
+    {
+        clear_pull_forces(pull_work);
+    }
 
     /* We calculate the non-bonded forces, when done on the CPU, here.
      * We do this before calling do_force_lowlevel, because in that
@@ -1479,26 +1650,26 @@ void do_force(FILE*                               fplog,
         do_nb_verlet(fr, ic, enerd, stepWork, InteractionLocality::Local, enbvClearFYes, step, nrnb, wcycle);
     }
 
-    if (fr->efep != efepNO)
+    if (fr->efep != efepNO && stepWork.computeNonbondedForces)
     {
         /* Calculate the local and non-local free energy interactions here.
          * Happens here on the CPU both with and without GPU.
          */
         nbv->dispatchFreeEnergyKernel(InteractionLocality::Local, fr,
                                       as_rvec_array(x.unpaddedArrayRef().data()),
-                                      &forceOut.forceWithShiftForces(), *mdatoms, inputrec->fepvals,
-                                      lambda.data(), enerd, stepWork, nrnb);
+                                      &forceOutNonbonded->forceWithShiftForces(), *mdatoms,
+                                      inputrec->fepvals, lambda, enerd, stepWork, nrnb);
 
         if (havePPDomainDecomposition(cr))
         {
             nbv->dispatchFreeEnergyKernel(InteractionLocality::NonLocal, fr,
                                           as_rvec_array(x.unpaddedArrayRef().data()),
-                                          &forceOut.forceWithShiftForces(), *mdatoms,
-                                          inputrec->fepvals, lambda.data(), enerd, stepWork, nrnb);
+                                          &forceOutNonbonded->forceWithShiftForces(), *mdatoms,
+                                          inputrec->fepvals, lambda, enerd, stepWork, nrnb);
         }
     }
 
-    if (!useOrEmulateGpuNb)
+    if (stepWork.computeNonbondedForces && !useOrEmulateGpuNb)
     {
         if (havePPDomainDecomposition(cr))
         {
@@ -1513,7 +1684,8 @@ void do_force(FILE*                               fplog,
              * communication with calculation with domain decomposition.
              */
             wallcycle_stop(wcycle, ewcFORCE);
-            nbv->atomdata_add_nbat_f_to_f(AtomLocality::All, forceOut.forceWithShiftForces().force());
+            nbv->atomdata_add_nbat_f_to_f(AtomLocality::All,
+                                          forceOutNonbonded->forceWithShiftForces().force());
             wallcycle_start_nocount(wcycle, ewcFORCE);
         }
 
@@ -1522,40 +1694,106 @@ void do_force(FILE*                               fplog,
         {
             /* This is not in a subcounter because it takes a
                negligible and constant-sized amount of time */
-            nbnxn_atomdata_add_nbat_fshift_to_fshift(*nbv->nbat,
-                                                     forceOut.forceWithShiftForces().shiftForces());
+            nbnxn_atomdata_add_nbat_fshift_to_fshift(
+                    *nbv->nbat, forceOutNonbonded->forceWithShiftForces().shiftForces());
         }
     }
 
-    /* update QMMMrec, if necessary */
-    if (fr->bQMMM)
-    {
-        update_QMMMrec(cr, fr, as_rvec_array(x.unpaddedArrayRef().data()), mdatoms, box);
-    }
-
     // TODO Force flags should include haveFreeEnergyWork for this domain
-    if (ddUsesGpuDirectCommunication && (domainWork.haveCpuBondedWork || domainWork.haveFreeEnergyWork))
+    if (stepWork.useGpuXHalo && (domainWork.haveCpuBondedWork || domainWork.haveFreeEnergyWork))
     {
         /* Wait for non-local coordinate data to be copied from device */
         stateGpu->waitCoordinatesReadyOnHost(AtomLocality::NonLocal);
     }
-    /* Compute the bonded and non-bonded energies and optionally forces */
-    do_force_lowlevel(fr, inputrec, &(top->idef), cr, ms, nrnb, wcycle, mdatoms, x, hist, &forceOut, enerd,
-                      fcd, box, lambda.data(), graph, fr->mu_tot, stepWork, ddBalanceRegionHandler);
+
+    // Compute wall interactions, when present.
+    // Note: should be moved to special forces.
+    if (inputrec->nwall && stepWork.computeNonbondedForces)
+    {
+        /* foreign lambda component for walls */
+        real dvdl_walls = do_walls(*inputrec, *fr, box, *mdatoms, x.unpaddedConstArrayRef(),
+                                   &forceOutMtsLevel0.forceWithVirial(), lambda[efptVDW],
+                                   enerd->grpp.ener[egLJSR].data(), nrnb);
+        enerd->dvdl_lin[efptVDW] += dvdl_walls;
+    }
+
+    if (stepWork.computeListedForces)
+    {
+        /* Check whether we need to take into account PBC in listed interactions */
+        bool needMolPbc = false;
+        for (const auto& listedForces : fr->listedForces)
+        {
+            if (listedForces.haveCpuListedForces(*fr->fcdata))
+            {
+                needMolPbc = fr->bMolPBC;
+            }
+        }
+
+        t_pbc pbc;
+
+        if (needMolPbc)
+        {
+            /* Since all atoms are in the rectangular or triclinic unit-cell,
+             * only single box vector shifts (2 in x) are required.
+             */
+            set_pbc_dd(&pbc, fr->pbcType, DOMAINDECOMP(cr) ? cr->dd->numCells : nullptr, TRUE, box);
+        }
+
+        for (int mtsIndex = 0; mtsIndex < (fr->useMts && stepWork.computeSlowForces ? 2 : 1); mtsIndex++)
+        {
+            ListedForces& listedForces = fr->listedForces[mtsIndex];
+            ForceOutputs& forceOut     = (mtsIndex == 0 ? forceOutMtsLevel0 : *forceOutMtsLevel1);
+            listedForces.calculate(
+                    wcycle, box, inputrec->fepvals, cr, ms, x, xWholeMolecules, fr->fcdata.get(),
+                    hist, &forceOut, fr, &pbc, enerd, nrnb, lambda.data(), mdatoms,
+                    DOMAINDECOMP(cr) ? cr->dd->globalAtomIndices.data() : nullptr, stepWork);
+        }
+    }
+
+    if (stepWork.computeSlowForces)
+    {
+        calculateLongRangeNonbondeds(fr, inputrec, cr, nrnb, wcycle, mdatoms,
+                                     x.unpaddedConstArrayRef(), &forceOutMtsLevel1->forceWithVirial(),
+                                     enerd, box, lambda.data(), as_rvec_array(dipoleData.muStateAB),
+                                     stepWork, ddBalanceRegionHandler);
+    }
 
     wallcycle_stop(wcycle, ewcFORCE);
 
-    computeSpecialForces(fplog, cr, inputrec, awh, enforcedRotation, imdSession, pull_work, step, t,
-                         wcycle, fr->forceProviders, box, x.unpaddedArrayRef(), mdatoms, lambda.data(),
-                         stepWork, &forceOut.forceWithVirial(), enerd, ed, stepWork.doNeighborSearch);
+    // VdW dispersion correction, only computed on master rank to avoid double counting
+    if ((stepWork.computeEnergy || stepWork.computeVirial) && fr->dispersionCorrection && MASTER(cr))
+    {
+        // Calculate long range corrections to pressure and energy
+        const DispersionCorrection::Correction correction =
+                fr->dispersionCorrection->calculate(box, lambda[efptVDW]);
 
+        if (stepWork.computeEnergy)
+        {
+            enerd->term[F_DISPCORR] = correction.energy;
+            enerd->term[F_DVDL_VDW] += correction.dvdl;
+            enerd->dvdl_lin[efptVDW] += correction.dvdl;
+        }
+        if (stepWork.computeVirial)
+        {
+            correction.correctVirial(vir_force);
+            enerd->term[F_PDISPCORR] = correction.pressure;
+        }
+    }
+
+    computeSpecialForces(fplog, cr, inputrec, awh, enforcedRotation, imdSession, pull_work, step, t,
+                         wcycle, fr->forceProviders, box, x.unpaddedArrayRef(), mdatoms, lambda, stepWork,
+                         &forceOutMtsLevel0.forceWithVirial(), enerd, ed, stepWork.doNeighborSearch);
 
+    GMX_ASSERT(!(nonbondedAtMtsLevel1 && stepWork.useGpuFBufferOps),
+               "The schedule below does not allow for nonbonded MTS with GPU buffer ops");
+    GMX_ASSERT(!(nonbondedAtMtsLevel1 && stepWork.useGpuFHalo),
+               "The schedule below does not allow for nonbonded MTS with GPU halo exchange");
     // Will store the amount of cycles spent waiting for the GPU that
     // will be later used in the DLB accounting.
     float cycles_wait_gpu = 0;
-    if (useOrEmulateGpuNb)
+    if (useOrEmulateGpuNb && stepWork.computeNonbondedForces)
     {
-        auto& forceWithShiftForces = forceOut.forceWithShiftForces();
+        auto& forceWithShiftForces = forceOutNonbonded->forceWithShiftForces();
 
         /* wait for non-local forces (or calculate in emulation mode) */
         if (havePPDomainDecomposition(cr))
@@ -1574,10 +1812,8 @@ void do_force(FILE*                               fplog,
                 wallcycle_stop(wcycle, ewcFORCE);
             }
 
-            if (useGpuFBufOps == BufferOpsUseGpu::True)
+            if (stepWork.useGpuFBufferOps)
             {
-                gmx::FixedCapacityVector<GpuEventSynchronizer*, 1> dependencyList;
-
                 // TODO: move this into DomainLifetimeWorkload, including the second part of the
                 // condition The bonded and free energy CPU tasks can have non-local force
                 // contributions which are a dependency for the GPU force reduction.
@@ -1586,19 +1822,17 @@ void do_force(FILE*                               fplog,
 
                 if (haveNonLocalForceContribInCpuBuffer)
                 {
-                    stateGpu->copyForcesToGpu(forceOut.forceWithShiftForces().force(),
+                    stateGpu->copyForcesToGpu(forceOutMtsLevel0.forceWithShiftForces().force(),
                                               AtomLocality::NonLocal);
-                    dependencyList.push_back(stateGpu->getForcesReadyOnDeviceEvent(
-                            AtomLocality::NonLocal, useGpuFBufOps == BufferOpsUseGpu::True));
                 }
 
-                nbv->atomdata_add_nbat_f_to_f_gpu(AtomLocality::NonLocal, stateGpu->getForces(),
-                                                  pme_gpu_get_device_f(fr->pmedata), dependencyList,
-                                                  false, haveNonLocalForceContribInCpuBuffer);
-                if (!useGpuForcesHaloExchange)
+
+                fr->gpuForceReduction[gmx::AtomLocality::NonLocal]->execute();
+
+                if (!stepWork.useGpuFHalo)
                 {
                     // copy from GPU input for dd_move_f()
-                    stateGpu->copyForcesFromGpu(forceOut.forceWithShiftForces().force(),
+                    stateGpu->copyForcesFromGpu(forceOutMtsLevel0.forceWithShiftForces().force(),
                                                 AtomLocality::NonLocal);
                 }
             }
@@ -1607,7 +1841,6 @@ void do_force(FILE*                               fplog,
                 nbv->atomdata_add_nbat_f_to_f(AtomLocality::NonLocal, forceWithShiftForces.force());
             }
 
-
             if (fr->nbv->emulateGpu() && stepWork.computeVirial)
             {
                 nbnxn_atomdata_add_nbat_fshift_to_fshift(*nbv->nbat, forceWithShiftForces.shiftForces());
@@ -1615,6 +1848,20 @@ void do_force(FILE*                               fplog,
         }
     }
 
+    /* Combining the forces for multiple time stepping before the halo exchange, when possible,
+     * avoids an extra halo exchange (when DD is used) and post-processing step.
+     */
+    const bool combineMtsForcesBeforeHaloExchange =
+            (stepWork.computeForces && fr->useMts && stepWork.computeSlowForces
+             && (legacyFlags & GMX_FORCE_DO_NOT_NEED_NORMAL_FORCE) != 0
+             && !(stepWork.computeVirial || simulationWork.useGpuNonbonded || useGpuPmeOnThisRank));
+    if (combineMtsForcesBeforeHaloExchange)
+    {
+        const int numAtoms = havePPDomainDecomposition(cr) ? dd_numAtomsZones(*cr->dd) : mdatoms->homenr;
+        combineMtsForces(numAtoms, force.unpaddedArrayRef(), forceView->forceMtsCombined(),
+                         inputrec->mtsLevels[1].stepFactor);
+    }
+
     if (havePPDomainDecomposition(cr))
     {
         /* We are done with the CPU compute.
@@ -1627,21 +1874,33 @@ void do_force(FILE*                               fplog,
         if (stepWork.computeForces)
         {
 
-            if (useGpuForcesHaloExchange)
+            if (stepWork.useGpuFHalo)
             {
                 if (domainWork.haveCpuLocalForceWork)
                 {
-                    stateGpu->copyForcesToGpu(forceOut.forceWithShiftForces().force(), AtomLocality::Local);
+                    stateGpu->copyForcesToGpu(forceOutMtsLevel0.forceWithShiftForces().force(),
+                                              AtomLocality::Local);
                 }
-                gpuHaloExchange->communicateHaloForces(domainWork.haveCpuLocalForceWork);
+                communicateGpuHaloForces(*cr, domainWork.haveCpuLocalForceWork);
             }
             else
             {
-                if (useGpuFBufOps == BufferOpsUseGpu::True)
+                if (stepWork.useGpuFBufferOps)
                 {
                     stateGpu->waitForcesReadyOnHost(AtomLocality::NonLocal);
                 }
-                dd_move_f(cr->dd, &forceOut.forceWithShiftForces(), wcycle);
+
+                // Without MTS or with MTS at slow steps with uncombined forces we need to
+                // communicate the fast forces
+                if (!fr->useMts || !combineMtsForcesBeforeHaloExchange)
+                {
+                    dd_move_f(cr->dd, &forceOutMtsLevel0.forceWithShiftForces(), wcycle);
+                }
+                // With MTS we need to communicate the slow or combined (in forceOutMtsLevel1) forces
+                if (fr->useMts && stepWork.computeSlowForces)
+                {
+                    dd_move_f(cr->dd, &forceOutMtsLevel1->forceWithShiftForces(), wcycle);
+                }
             }
         }
     }
@@ -1649,19 +1908,21 @@ void do_force(FILE*                               fplog,
     // With both nonbonded and PME offloaded a GPU on the same rank, we use
     // an alternating wait/reduction scheme.
     bool alternateGpuWait = (!c_disableAlternatingWait && useGpuPmeOnThisRank && simulationWork.useGpuNonbonded
-                             && !DOMAINDECOMP(cr) && (useGpuFBufOps == BufferOpsUseGpu::False));
+                             && !DOMAINDECOMP(cr) && !stepWork.useGpuFBufferOps);
     if (alternateGpuWait)
     {
-        alternatePmeNbGpuWaitReduce(fr->nbv.get(), fr->pmedata, &forceOut, enerd, stepWork, pmeFlags, wcycle);
+        alternatePmeNbGpuWaitReduce(fr->nbv.get(), fr->pmedata, forceOutNonbonded,
+                                    forceOutMtsLevel1, enerd, lambda[efptCOUL], stepWork, wcycle);
     }
 
     if (!alternateGpuWait && useGpuPmeOnThisRank)
     {
-        pme_gpu_wait_and_reduce(fr->pmedata, pmeFlags, wcycle, &forceOut.forceWithVirial(), enerd);
+        pme_gpu_wait_and_reduce(fr->pmedata, stepWork, wcycle,
+                                &forceOutMtsLevel1->forceWithVirial(), enerd, lambda[efptCOUL]);
     }
 
     /* Wait for local GPU NB outputs on the non-alternating wait path */
-    if (!alternateGpuWait && simulationWork.useGpuNonbonded)
+    if (!alternateGpuWait && stepWork.computeNonbondedForces && simulationWork.useGpuNonbonded)
     {
         /* Measured overhead on CUDA and OpenCL with(out) GPU sharing
          * is between 0.5 and 1.5 Mcycles. So 2 MCycles is an overestimate,
@@ -1671,7 +1932,8 @@ void do_force(FILE*                               fplog,
         const float gpuWaitApiOverheadMargin = 2e6F; /* cycles */
         const float waitCycles               = Nbnxm::gpu_wait_finish_task(
                 nbv->gpu_nbv, stepWork, AtomLocality::Local, enerd->grpp.ener[egLJSR].data(),
-                enerd->grpp.ener[egCOULSR].data(), forceOut.forceWithShiftForces().shiftForces(), wcycle);
+                enerd->grpp.ener[egCOULSR].data(),
+                forceOutNonbonded->forceWithShiftForces().shiftForces(), wcycle);
 
         if (ddBalanceRegionHandler.useBalancingRegion())
         {
@@ -1702,13 +1964,13 @@ void do_force(FILE*                               fplog,
 
     // If on GPU PME-PP comms or GPU update path, receive forces from PME before GPU buffer ops
     // TODO refactor this and unify with below default-path call to the same function
-    if (PAR(cr) && !thisRankHasDuty(cr, DUTY_PME)
+    if (PAR(cr) && !thisRankHasDuty(cr, DUTY_PME) && stepWork.computeSlowForces
         && (simulationWork.useGpuPmePpCommunication || simulationWork.useGpuUpdate))
     {
         /* In case of node-splitting, the PP nodes receive the long-range
          * forces, virial and energy from the PME nodes here.
          */
-        pme_receive_force_ener(fr, cr, &forceOut.forceWithVirial(), enerd,
+        pme_receive_force_ener(fr, cr, &forceOutMtsLevel1->forceWithVirial(), enerd,
                                simulationWork.useGpuPmePpCommunication,
                                stepWork.useGpuPmeFReduction, wcycle);
     }
@@ -1716,35 +1978,14 @@ void do_force(FILE*                               fplog,
 
     /* Do the nonbonded GPU (or emulation) force buffer reduction
      * on the non-alternating path. */
+    GMX_ASSERT(!(nonbondedAtMtsLevel1 && stepWork.useGpuFBufferOps),
+               "The schedule below does not allow for nonbonded MTS with GPU buffer ops");
     if (useOrEmulateGpuNb && !alternateGpuWait)
     {
-        // TODO simplify the below conditionals. Pass buffer and sync pointers at init stage rather than here. Unify getter fns for sameGPU/otherGPU cases.
-        void* pmeForcePtr =
-                stepWork.useGpuPmeFReduction
-                        ? (thisRankHasDuty(cr, DUTY_PME) ? pme_gpu_get_device_f(fr->pmedata)
-                                                         : // PME force buffer on same GPU
-                                   fr->pmePpCommGpu->getGpuForceStagingPtr()) // buffer received from other GPU
-                        : nullptr; // PME reduction not active on GPU
-
-        GpuEventSynchronizer* const pmeSynchronizer =
-                stepWork.useGpuPmeFReduction
-                        ? (thisRankHasDuty(cr, DUTY_PME) ? pme_gpu_get_f_ready_synchronizer(fr->pmedata)
-                                                         : // PME force buffer on same GPU
-                                   static_cast<GpuEventSynchronizer*>(
-                                           fr->pmePpCommGpu->getForcesReadySynchronizer())) // buffer received from other GPU
-                        : nullptr; // PME reduction not active on GPU
-
-        gmx::FixedCapacityVector<GpuEventSynchronizer*, 3> dependencyList;
-
-        if (stepWork.useGpuPmeFReduction)
+        if (stepWork.useGpuFBufferOps)
         {
-            dependencyList.push_back(pmeSynchronizer);
-        }
-
-        gmx::ArrayRef<gmx::RVec> forceWithShift = forceOut.forceWithShiftForces().force();
+            ArrayRef<gmx::RVec> forceWithShift = forceOutNonbonded->forceWithShiftForces().force();
 
-        if (useGpuFBufOps == BufferOpsUseGpu::True)
-        {
             // Flag to specify whether the CPU force buffer has contributions to
             // local atoms. This depends on whether there are CPU-based force tasks
             // or when DD is active the halo exchange has resulted in contributions
@@ -1759,7 +2000,7 @@ void do_force(FILE*                               fplog,
             // - copy is not perfomed if GPU force halo exchange is active, because it would overwrite the result
             //   of the halo exchange. In that case the copy is instead performed above, before the exchange.
             //   These should be unified.
-            if (haveLocalForceContribInCpuBuffer && !useGpuForcesHaloExchange)
+            if (haveLocalForceContribInCpuBuffer && !stepWork.useGpuFHalo)
             {
                 // Note: AtomLocality::All is used for the non-DD case because, as in this
                 // case copyForcesToGpu() uses a separate stream, it allows overlap of
@@ -1769,16 +2010,13 @@ void do_force(FILE*                               fplog,
                 auto locality = havePPDomainDecomposition(cr) ? AtomLocality::Local : AtomLocality::All;
 
                 stateGpu->copyForcesToGpu(forceWithShift, locality);
-                dependencyList.push_back(stateGpu->getForcesReadyOnDeviceEvent(
-                        locality, useGpuFBufOps == BufferOpsUseGpu::True));
             }
-            if (useGpuForcesHaloExchange)
+
+            if (stepWork.computeNonbondedForces)
             {
-                dependencyList.push_back(gpuHaloExchange->getForcesReadyOnDeviceEvent());
+                fr->gpuForceReduction[gmx::AtomLocality::Local]->execute();
             }
-            nbv->atomdata_add_nbat_f_to_f_gpu(AtomLocality::Local, stateGpu->getForces(), pmeForcePtr,
-                                              dependencyList, stepWork.useGpuPmeFReduction,
-                                              haveLocalForceContribInCpuBuffer);
+
             // Copy forces to host if they are needed for update or if virtual sites are enabled.
             // If there are vsites, we need to copy forces every step to spread vsite forces on host.
             // TODO: When the output flags will be included in step workload, this copy can be combined with the
@@ -1791,63 +2029,70 @@ void do_force(FILE*                               fplog,
                 stateGpu->waitForcesReadyOnHost(AtomLocality::Local);
             }
         }
-        else
+        else if (stepWork.computeNonbondedForces)
         {
+            ArrayRef<gmx::RVec> forceWithShift = forceOutNonbonded->forceWithShiftForces().force();
             nbv->atomdata_add_nbat_f_to_f(AtomLocality::Local, forceWithShift);
         }
     }
 
     launchGpuEndOfStepTasks(nbv, fr->gpuBonded, fr->pmedata, enerd, *runScheduleWork,
-                            simulationWork.useGpuNonbonded, useGpuPmeOnThisRank, step, wcycle);
+                            useGpuPmeOnThisRank, step, wcycle);
 
     if (DOMAINDECOMP(cr))
     {
         dd_force_flop_stop(cr->dd, nrnb);
     }
 
+    const bool haveCombinedMtsForces = (stepWork.computeForces && fr->useMts && stepWork.computeSlowForces
+                                        && combineMtsForcesBeforeHaloExchange);
     if (stepWork.computeForces)
     {
-        rvec* f = as_rvec_array(forceOut.forceWithShiftForces().force().data());
+        postProcessForceWithShiftForces(nrnb, wcycle, box, x.unpaddedArrayRef(), &forceOutMtsLevel0,
+                                        vir_force, *mdatoms, *fr, vsite, stepWork);
 
-        /* If we have NoVirSum forces, but we do not calculate the virial,
-         * we sum fr->f_novirsum=forceOut.f later.
-         */
-        if (vsite && !(fr->haveDirectVirialContributions && !stepWork.computeVirial))
+        if (fr->useMts && stepWork.computeSlowForces && !haveCombinedMtsForces)
         {
-            rvec* fshift = as_rvec_array(forceOut.forceWithShiftForces().shiftForces().data());
-            spread_vsite_f(vsite, as_rvec_array(x.unpaddedArrayRef().data()), f, fshift, FALSE,
-                           nullptr, nrnb, &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr, wcycle);
-        }
-
-        if (stepWork.computeVirial)
-        {
-            /* Calculation of the virial must be done after vsites! */
-            calc_virial(0, mdatoms->homenr, as_rvec_array(x.unpaddedArrayRef().data()),
-                        forceOut.forceWithShiftForces(), vir_force, graph, box, nrnb, fr, inputrec->ePBC);
+            postProcessForceWithShiftForces(nrnb, wcycle, box, x.unpaddedArrayRef(), forceOutMtsLevel1,
+                                            vir_force, *mdatoms, *fr, vsite, stepWork);
         }
     }
 
     // TODO refactor this and unify with above GPU PME-PP / GPU update path call to the same function
     if (PAR(cr) && !thisRankHasDuty(cr, DUTY_PME) && !simulationWork.useGpuPmePpCommunication
-        && !simulationWork.useGpuUpdate)
+        && !simulationWork.useGpuUpdate && stepWork.computeSlowForces)
     {
         /* In case of node-splitting, the PP nodes receive the long-range
          * forces, virial and energy from the PME nodes here.
          */
-        pme_receive_force_ener(fr, cr, &forceOut.forceWithVirial(), enerd,
+        pme_receive_force_ener(fr, cr, &forceOutMtsLevel1->forceWithVirial(), enerd,
                                simulationWork.useGpuPmePpCommunication, false, wcycle);
     }
 
     if (stepWork.computeForces)
     {
-        post_process_forces(cr, step, nrnb, wcycle, top, box, as_rvec_array(x.unpaddedArrayRef().data()),
-                            &forceOut, vir_force, mdatoms, graph, fr, vsite, stepWork);
+        /* If we don't use MTS or if we already combined the MTS forces before, we only
+         * need to post-process one ForceOutputs object here, called forceOutCombined,
+         * otherwise we have to post-process two outputs and then combine them.
+         */
+        ForceOutputs& forceOutCombined = (haveCombinedMtsForces ? forceOutMts.value() : forceOutMtsLevel0);
+        postProcessForces(cr, step, nrnb, wcycle, box, x.unpaddedArrayRef(), &forceOutCombined,
+                          vir_force, mdatoms, fr, vsite, stepWork);
+
+        if (fr->useMts && stepWork.computeSlowForces && !haveCombinedMtsForces)
+        {
+            postProcessForces(cr, step, nrnb, wcycle, box, x.unpaddedArrayRef(), forceOutMtsLevel1,
+                              vir_force, mdatoms, fr, vsite, stepWork);
+
+            combineMtsForces(mdatoms->homenr, force.unpaddedArrayRef(),
+                             forceView->forceMtsCombined(), inputrec->mtsLevels[1].stepFactor);
+        }
     }
 
     if (stepWork.computeEnergy)
     {
-        /* Sum the potential energy terms from group contributions */
-        sum_epot(&(enerd->grpp), enerd->term);
+        /* Compute the final potential energy terms */
+        accumulatePotentialEnergies(enerd, lambda, inputrec->fepvals);
 
         if (!EI_TPI(inputrec->eI))
         {
index 6ea35c37d3a9d5e5bb78cc41d51ef6edc3a1c1d9..15f44ccbf4cfadfec6671f09190c109e2c05e9d9 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -111,8 +112,11 @@ void SimulationSignaller::signalInterSim()
         // Communicate the signals between the simulations.
         gmx_sum_sim(eglsNR, mpiBuffer_.data(), ms_);
     }
-    // Communicate the signals from the master to the others.
-    gmx_bcast(eglsNR * sizeof(mpiBuffer_[0]), mpiBuffer_.data(), cr_);
+    if (DOMAINDECOMP(cr_))
+    {
+        // Communicate the signals from the master to the others.
+        gmx_bcast(eglsNR * sizeof(mpiBuffer_[0]), mpiBuffer_.data(), cr_->mpi_comm_mygroup);
+    }
 }
 
 void SimulationSignaller::setSignals()
index 1a038a180119397a5da6c1a56d16e941440a6728..db42983ae53bba2265b832419bd572cb39a443b2 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e59521ce6c0ba98402efb3632ce28b9928821bb1..a970c3fc0a71a7ba54ef9d67deb8d00f391f9cb2 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -67,26 +68,26 @@ static bool sid_comp(const t_sid& sa, const t_sid& sb)
     }
 }
 
-static int mk_grey(egCol egc[], t_graph* g, int* AtomI, int maxsid, t_sid sid[])
+static int mk_grey(gmx::ArrayRef<egCol> edgeColor, const t_graph* g, int* AtomI, int maxsid, t_sid sid[])
 {
-    int j, ng, ai, aj, g0;
+    int ng, ai, g0;
 
     ng = 0;
     ai = *AtomI;
 
-    g0 = g->at_start;
+    g0 = g->edgeAtomBegin;
     /* Loop over all the bonds */
-    for (j = 0; (j < g->nedge[ai]); j++)
+    for (int aj : g->edges[ai])
     {
-        aj = g->edge[ai][j] - g0;
+        aj -= g0;
         /* If there is a white one, make it gray and set pbc */
-        if (egc[aj] == egcolWhite)
+        if (edgeColor[aj] == egcolWhite)
         {
             if (aj < *AtomI)
             {
                 *AtomI = aj;
             }
-            egc[aj] = egcolGrey;
+            edgeColor[aj] = egcolGrey;
 
             /* Check whether this one has been set before... */
             range_check(aj + g0, 0, maxsid);
@@ -107,16 +108,16 @@ static int mk_grey(egCol egc[], t_graph* g, int* AtomI, int maxsid, t_sid sid[])
     return ng;
 }
 
-static int first_colour(int fC, egCol Col, t_graph* g, const egCol egc[])
+static int first_colour(const int fC, const egCol Col, const t_graph* g, gmx::ArrayRef<const egCol> edgeColor)
 /* Return the first node with colour Col starting at fC.
  * return -1 if none found.
  */
 {
     int i;
 
-    for (i = fC; (i < g->nnodes); i++)
+    for (i = fC; i < int(g->edges.size()); i++)
     {
-        if ((g->nedge[i] > 0) && (egc[i] == Col))
+        if (!g->edges[i].empty() && edgeColor[i] == Col)
         {
             return i;
         }
@@ -127,24 +128,22 @@ static int first_colour(int fC, egCol Col, t_graph* g, const egCol egc[])
 
 static int mk_sblocks(FILE* fp, t_graph* g, int maxsid, t_sid sid[])
 {
-    int    ng, nnodes;
-    int    nW, nG, nB;    /* Number of Grey, Black, White      */
-    int    fW, fG;        /* First of each category    */
-    egCol* egc = nullptr; /* The colour of each node   */
-    int    g0, nblock;
+    int ng;
+    int nW, nG, nB; /* Number of Grey, Black, White    */
+    int fW, fG;     /* First of each category  */
+    int g0, nblock;
 
-    if (!g->nbound)
+    if (!g->numConnectedAtoms)
     {
         return 0;
     }
 
     nblock = 0;
 
-    nnodes = g->nnodes;
-    snew(egc, nnodes);
+    std::vector<egCol> edgeColor(g->edges.size(), egcolWhite);
 
-    g0 = g->at_start;
-    nW = g->nbound;
+    g0 = g->edgeAtomBegin;
+    nW = g->numConnectedAtoms;
     nG = 0;
     nB = 0;
 
@@ -165,13 +164,13 @@ static int mk_sblocks(FILE* fp, t_graph* g, int maxsid, t_sid sid[])
          * number than before, because no nodes are made white
          * in the loop
          */
-        if ((fW = first_colour(fW, egcolWhite, g, egc)) == -1)
+        if ((fW = first_colour(fW, egcolWhite, g, edgeColor)) == -1)
         {
             gmx_fatal(FARGS, "No WHITE nodes found while nW=%d\n", nW);
         }
 
         /* Make the first white node grey, and set the block number */
-        egc[fW] = egcolGrey;
+        edgeColor[fW] = egcolGrey;
         range_check(fW + g0, 0, maxsid);
         sid[fW + g0].sid = nblock++;
         nG++;
@@ -187,26 +186,25 @@ static int mk_sblocks(FILE* fp, t_graph* g, int maxsid, t_sid sid[])
 
         while (nG > 0)
         {
-            if ((fG = first_colour(fG, egcolGrey, g, egc)) == -1)
+            if ((fG = first_colour(fG, egcolGrey, g, edgeColor)) == -1)
             {
                 gmx_fatal(FARGS, "No GREY nodes found while nG=%d\n", nG);
             }
 
             /* Make the first grey node black */
-            egc[fG] = egcolBlack;
+            edgeColor[fG] = egcolBlack;
             nB++;
             nG--;
 
             /* Make all the neighbours of this black node grey
              * and set their block number
              */
-            ng = mk_grey(egc, g, &fG, maxsid, sid);
+            ng = mk_grey(edgeColor, g, &fG, maxsid, sid);
             /* ng is the number of white nodes made grey */
             nG += ng;
             nW -= ng;
         }
     }
-    sfree(egc);
 
     if (debug)
     {
@@ -346,20 +344,20 @@ static int merge_sid(int at_start, int at_end, int nsid, t_sid sid[], t_blocka*
     return nsid;
 }
 
-void gen_sblocks(FILE* fp, int at_start, int at_end, const t_idef* idef, t_blocka* sblock, gmx_bool bSettle)
+void gen_sblocks(FILE* fp, int at_end, const InteractionDefinitions& idef, t_blocka* sblock, gmx_bool bSettle)
 {
     t_graph* g;
     int      i, i0;
     t_sid*   sid;
     int      nsid;
 
-    g = mk_graph(nullptr, idef, at_start, at_end, TRUE, bSettle);
+    g = mk_graph(nullptr, idef, at_end, TRUE, bSettle);
     if (debug)
     {
         p_graph(debug, "Graaf Dracula", g);
     }
     snew(sid, at_end);
-    for (i = at_start; (i < at_end); i++)
+    for (i = 0; i < at_end; i++)
     {
         sid[i].atom = i;
         sid[i].sid  = -1;
@@ -372,18 +370,18 @@ void gen_sblocks(FILE* fp, int at_start, int at_end, const t_idef* idef, t_block
     }
 
     /* Now sort the shake blocks... */
-    std::sort(sid + at_start, sid + at_end, sid_comp);
+    std::sort(sid, sid + at_end, sid_comp);
 
     if (debug)
     {
         fprintf(debug, "Sorted shake block\n");
-        for (i = at_start; (i < at_end); i++)
+        for (i = 0; i < at_end; i++)
         {
             fprintf(debug, "sid[%5d] = atom:%5d sid:%5d\n", i, sid[i].atom, sid[i].sid);
         }
     }
     /* Now check how many are NOT -1, i.e. how many have to be shaken */
-    for (i0 = at_start; (i0 < at_end); i0++)
+    for (i0 = 0; i0 < at_end; i0++)
     {
         if (sid[i0].sid > -1)
         {
@@ -397,11 +395,10 @@ void gen_sblocks(FILE* fp, int at_start, int at_end, const t_idef* idef, t_block
      * part of the shake block too. There may be cases where blocks overlap
      * and they will have to be merged.
      */
-    merge_sid(at_start, at_end, nsid, sid, sblock);
+    merge_sid(0, at_end, nsid, sid, sblock);
     sfree(sid);
     /* Due to unknown reason this free generates a problem sometimes */
     done_graph(g);
-    sfree(g);
     if (debug)
     {
         fprintf(debug, "Done gen_sblocks\n");
index 88fb8f205df7d41cabbdf270a7eb76a400c2e0a7..b1c751738392275d49bf2febc008dad159e2deec 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/utility/basedefinitions.h"
 
+class InteractionDefinitions;
 struct t_blocka;
-struct t_idef;
 
-void gen_sblocks(FILE* fp, int at_start, int at_end, const t_idef* idef, t_blocka* sblock, gmx_bool bSettle);
+void gen_sblocks(FILE* fp, int at_end, const InteractionDefinitions& idef, t_blocka* sblock, gmx_bool bSettle);
 /* Generate shake blocks from the constraint list. Set bSettle to yes for shake
  * blocks including settles. You normally do not want this.
  */
index 53b1c7e985faf2a6493126fc88e2232a45994143..2c8c6b44d0cdca0556f237f9d9cc5b853f35c8b8 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -140,7 +141,6 @@ void global_stat(const gmx_global_stat*  gs,
                  gmx_enerdata_t*         enerd,
                  tensor                  fvir,
                  tensor                  svir,
-                 rvec                    mu_tot,
                  const t_inputrec*       inputrec,
                  gmx_ekindata_t*         ekind,
                  const gmx::Constraints* constr,
@@ -148,13 +148,13 @@ void global_stat(const gmx_global_stat*  gs,
                  int                     nsig,
                  real*                   sig,
                  int*                    totalNumberOfBondedInteractions,
-                 gmx_bool                bSumEkinhOld,
+                 bool                    bSumEkinhOld,
                  int                     flags)
 /* instead of current system, gmx_booleans for summing virial, kinetic energy, and other terms */
 {
     t_bin* rb;
     int *  itc0, *itc1;
-    int    ie = 0, ifv = 0, isv = 0, irmsd = 0, imu = 0;
+    int    ie = 0, ifv = 0, isv = 0, irmsd = 0;
     int idedl = 0, idedlo = 0, idvdll = 0, idvdlnl = 0, iepl = 0, icm = 0, imass = 0, ica = 0, inb = 0;
     int      isig = -1;
     int      icj = -1, ici = -1, icx = -1;
@@ -247,10 +247,6 @@ void global_stat(const gmx_global_stat*  gs,
                 irmsd = add_binr(rb, 2, rmsdData.data());
             }
         }
-        if (!inputrecNeedMutot(inputrec))
-        {
-            imu = add_binr(rb, DIM, mu_tot);
-        }
 
         for (j = 0; (j < egNR); j++)
         {
@@ -260,9 +256,10 @@ void global_stat(const gmx_global_stat*  gs,
         {
             idvdll  = add_bind(rb, efptNR, enerd->dvdl_lin);
             idvdlnl = add_bind(rb, efptNR, enerd->dvdl_nonlin);
-            if (!enerd->enerpart_lambda.empty())
+            if (enerd->foreignLambdaTerms.numLambdas() > 0)
             {
-                iepl = add_bind(rb, enerd->enerpart_lambda.size(), enerd->enerpart_lambda.data());
+                iepl = add_bind(rb, enerd->foreignLambdaTerms.energies().size(),
+                                enerd->foreignLambdaTerms.energies().data());
             }
         }
     }
@@ -346,10 +343,6 @@ void global_stat(const gmx_global_stat*  gs,
         {
             extract_binr(rb, irmsd, rmsdData);
         }
-        if (!inputrecNeedMutot(inputrec))
-        {
-            extract_binr(rb, imu, DIM, mu_tot);
-        }
 
         for (j = 0; (j < egNR); j++)
         {
@@ -359,9 +352,10 @@ void global_stat(const gmx_global_stat*  gs,
         {
             extract_bind(rb, idvdll, efptNR, enerd->dvdl_lin);
             extract_bind(rb, idvdlnl, efptNR, enerd->dvdl_nonlin);
-            if (!enerd->enerpart_lambda.empty())
+            if (enerd->foreignLambdaTerms.numLambdas() > 0)
             {
-                extract_bind(rb, iepl, enerd->enerpart_lambda.size(), enerd->enerpart_lambda.data());
+                extract_bind(rb, iepl, enerd->foreignLambdaTerms.energies().size(),
+                             enerd->foreignLambdaTerms.energies().data());
             }
         }
 
index cfb21e180221d6c248b4f5a29713d76aa280619b..d69d5ceaef0240f92d64e1361e87d6d02e268ef2 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -63,7 +64,6 @@ void global_stat(const gmx_global_stat*  gs,
                  gmx_enerdata_t*         enerd,
                  tensor                  fvir,
                  tensor                  svir,
-                 rvec                    mu_tot,
                  const t_inputrec*       inputrec,
                  gmx_ekindata_t*         ekind,
                  const gmx::Constraints* constr,
@@ -71,7 +71,7 @@ void global_stat(const gmx_global_stat*  gs,
                  int                     nsig,
                  real*                   sig,
                  int*                    totalNumberOfBondedInteractions,
-                 gmx_bool                bSumEkinhOld,
+                 bool                    bSumEkinhOld,
                  int                     flags);
 
 /*! \brief Returns TRUE if io should be done */
index 496b51d655103cfbcfe761f5baa4e6486a308673..465057ea528f6a85e810657b81084c9d4d8e8d85 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2016,2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-gmx_add_unit_test(MdlibUnitTest mdlib-test
-                  calc_verletbuf.cpp
-                  constr.cpp
-                  constrtestdata.cpp
-                  constrtestrunners.cpp
-                  ebin.cpp
-                  energyoutput.cpp
-                  leapfrog.cpp
-                  leapfrogtestdata.cpp
-                  leapfrogtestrunners.cpp
-                  settle.cpp
-                  settletestdata.cpp
-                  settletestrunners.cpp
-                  shake.cpp
-                  simulationsignal.cpp
-                  updategroups.cpp
-                  updategroupscog.cpp)
-
-# TODO: Make CUDA source to compile inside the testing framework
-if(GMX_USE_CUDA)
-    gmx_add_libgromacs_sources(constrtestrunners.cu
-                               leapfrogtestrunners.cu
-                               settletestrunners.cu)
-endif()
+gmx_add_unit_test(MdlibUnitTest mdlib-test HARDWARE_DETECTION
+    CPP_SOURCE_FILES
+        calc_verletbuf.cpp
+        constr.cpp
+        constrtestdata.cpp
+        constrtestrunners.cpp
+        ebin.cpp
+        energyoutput.cpp
+        freeenergyparameters.cpp
+        leapfrog.cpp
+        leapfrogtestdata.cpp
+        leapfrogtestrunners.cpp
+        settle.cpp
+        settletestdata.cpp
+        settletestrunners.cpp
+        shake.cpp
+        simulationsignal.cpp
+        updategroups.cpp
+        updategroupscog.cpp
+    CUDA_CU_SOURCE_FILES
+        constrtestrunners.cu
+        leapfrogtestrunners.cu
+        settletestrunners.cu
+        )
index 5883fd926188246ec63401b254effa22f20f8aa1..75b691ef07813703da3cc1bdb0e9febe4a82a10c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -60,6 +60,7 @@
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/utility/stringutil.h"
 
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
 #include "constrtestdata.h"
@@ -72,30 +73,6 @@ namespace test
 namespace
 {
 
-/*! \brief The two-dimensional parameter space for test.
- *
- * The test will run for all possible combinations of accessible
- * values of the:
- * 1. PBC setup ("PBCNONE" or "PBCXYZ")
- * 2. The algorithm ("SHAKE", "LINCS" or "LINCS_GPU").
- */
-typedef std::tuple<std::string, std::string> ConstraintsTestParameters;
-
-//! Names of all availible runners
-std::vector<std::string> runnersNames;
-
-//! Method that fills and returns runnersNames to the test macros.
-std::vector<std::string> getRunnersNames()
-{
-    runnersNames.emplace_back("SHAKE");
-    runnersNames.emplace_back("LINCS");
-    if (GMX_GPU == GMX_GPU_CUDA && canComputeOnGpu())
-    {
-        runnersNames.emplace_back("LINCS_CUDA");
-    }
-    return runnersNames;
-}
-
 /*! \brief Test fixture for constraints.
  *
  * The fixture uses following test systems:
@@ -114,13 +91,11 @@ std::vector<std::string> getRunnersNames()
  * For some systems, the value for scaled virial tensor is checked against
  * pre-computed data.
  */
-class ConstraintsTest : public ::testing::TestWithParam<ConstraintsTestParameters>
+class ConstraintsTest : public ::testing::TestWithParam<std::string>
 {
 public:
     //! PBC setups
     std::unordered_map<std::string, t_pbc> pbcs_;
-    //! Algorithms (SHAKE and LINCS)
-    std::unordered_map<std::string, void (*)(ConstraintsTestData* testData, t_pbc pbc)> algorithms_;
 
     /*! \brief Test setup function.
      *
@@ -138,23 +113,13 @@ public:
 
         // Infinitely small box
         matrix boxNone = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
-        set_pbc(&pbc, epbcNONE, boxNone);
+        set_pbc(&pbc, PbcType::No, boxNone);
         pbcs_["PBCNone"] = pbc;
 
         // Rectangular box
         matrix boxXyz = { { 10.0, 0.0, 0.0 }, { 0.0, 20.0, 0.0 }, { 0.0, 0.0, 15.0 } };
-        set_pbc(&pbc, epbcXYZ, boxXyz);
+        set_pbc(&pbc, PbcType::Xyz, boxXyz);
         pbcs_["PBCXYZ"] = pbc;
-
-        //
-        // Algorithms
-        //
-        // SHAKE
-        algorithms_["SHAKE"] = applyShake;
-        // LINCS
-        algorithms_["LINCS"] = applyLincs;
-        // LINCS using CUDA (will only be called if CUDA is available)
-        algorithms_["LINCS_CUDA"] = applyLincsCuda;
     }
 
     /*! \brief
@@ -167,7 +132,9 @@ public:
      * \param[in] testData        Test data structure.
      * \param[in] pbc             Periodic boundary data.
      */
-    void checkConstrainsLength(FloatingPointTolerance tolerance, const ConstraintsTestData& testData, t_pbc pbc)
+    static void checkConstrainsLength(FloatingPointTolerance     tolerance,
+                                      const ConstraintsTestData& testData,
+                                      t_pbc                      pbc)
     {
 
         // Test if all the constraints are satisfied
@@ -178,7 +145,7 @@ public:
             int  j  = testData.constraints_.at(3 * c + 2);
             RVec xij0, xij1;
             real d0, d1;
-            if (pbc.ePBC == epbcXYZ)
+            if (pbc.pbcType == PbcType::Xyz)
             {
                 pbc_dx_aiuc(&pbc, testData.x_[i], testData.x_[j], xij0);
                 pbc_dx_aiuc(&pbc, testData.xPrime_[i], testData.xPrime_[j], xij1);
@@ -208,7 +175,7 @@ public:
      * \param[in] testData        Test data structure.
      * \param[in] pbc             Periodic boundary data.
      */
-    void checkConstrainsDirection(const ConstraintsTestData& testData, t_pbc pbc)
+    static void checkConstrainsDirection(const ConstraintsTestData& testData, t_pbc pbc)
     {
 
         for (index c = 0; c < ssize(testData.constraints_) / 3; c++)
@@ -216,7 +183,7 @@ public:
             int  i = testData.constraints_.at(3 * c + 1);
             int  j = testData.constraints_.at(3 * c + 2);
             RVec xij0, xij1;
-            if (pbc.ePBC == epbcXYZ)
+            if (pbc.pbcType == PbcType::Xyz)
             {
                 pbc_dx_aiuc(&pbc, testData.x_[i], testData.x_[j], xij0);
                 pbc_dx_aiuc(&pbc, testData.xPrime_[i], testData.xPrime_[j], xij1);
@@ -248,7 +215,7 @@ public:
      * \param[in] tolerance       Allowed tolerance in COM coordinates.
      * \param[in] testData        Test data structure.
      */
-    void checkCOMCoordinates(FloatingPointTolerance tolerance, const ConstraintsTestData& testData)
+    static void checkCOMCoordinates(FloatingPointTolerance tolerance, const ConstraintsTestData& testData)
     {
 
         RVec comPrime0({ 0.0, 0.0, 0.0 });
@@ -278,7 +245,7 @@ public:
      * \param[in] tolerance       Allowed tolerance in COM velocity components.
      * \param[in] testData        Test data structure.
      */
-    void checkCOMVelocity(FloatingPointTolerance tolerance, const ConstraintsTestData& testData)
+    static void checkCOMVelocity(FloatingPointTolerance tolerance, const ConstraintsTestData& testData)
     {
 
         RVec comV0({ 0.0, 0.0, 0.0 });
@@ -307,7 +274,7 @@ public:
      * \param[in] tolerance       Tolerance for the tensor values.
      * \param[in] testData        Test data structure.
      */
-    void checkVirialTensor(FloatingPointTolerance tolerance, const ConstraintsTestData& testData)
+    static void checkVirialTensor(FloatingPointTolerance tolerance, const ConstraintsTestData& testData)
     {
         for (int i = 0; i < DIM; i++)
         {
@@ -321,6 +288,23 @@ public:
             }
         }
     }
+    //! Before any test is run, work out whether any compatible GPUs exist.
+    static std::vector<std::unique_ptr<IConstraintsTestRunner>> getRunners()
+    {
+        std::vector<std::unique_ptr<IConstraintsTestRunner>> runners;
+        // Add runners for CPU versions of SHAKE and LINCS
+        runners.emplace_back(std::make_unique<ShakeConstraintsRunner>());
+        runners.emplace_back(std::make_unique<LincsConstraintsRunner>());
+        // If using CUDA, add runners for the GPU version of LINCS for each available GPU
+        if (GMX_GPU_CUDA)
+        {
+            for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
+            {
+                runners.emplace_back(std::make_unique<LincsDeviceConstraintsRunner>(*testDevice));
+            }
+        }
+        return runners;
+    }
 };
 
 TEST_P(ConstraintsTest, SingleConstraint)
@@ -353,20 +337,28 @@ TEST_P(ConstraintsTest, SingleConstraint)
             title, numAtoms, masses, constraints, constraintsR0, true, virialScaledRef, false, 0,
             real(0.0), real(0.001), x, xPrime, v, shakeTolerance, shakeUseSOR, lincsNIter,
             lincslincsExpansionOrder, lincsWarnAngle);
-    std::string pbcName;
-    std::string algorithmName;
-    std::tie(pbcName, algorithmName) = GetParam();
-    t_pbc pbc                        = pbcs_.at(pbcName);
 
-    // Apply constraints
-    algorithms_.at(algorithmName)(testData.get(), pbc);
+    std::string pbcName = GetParam();
+    t_pbc       pbc     = pbcs_.at(pbcName);
+
+    // Cycle through all available runners
+    for (const auto& runner : getRunners())
+    {
+        SCOPED_TRACE(formatString("Testing %s with %s using %s.", testData->title_.c_str(),
+                                  pbcName.c_str(), runner->name().c_str()));
+
+        testData->reset();
+
+        // Apply constraints
+        runner->applyConstraints(testData.get(), pbc);
 
-    checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
-    checkConstrainsDirection(*testData, pbc);
-    checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
-    checkCOMVelocity(absoluteTolerance(0.0001), *testData);
+        checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
+        checkConstrainsDirection(*testData, pbc);
+        checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
+        checkCOMVelocity(absoluteTolerance(0.0001), *testData);
 
-    checkVirialTensor(absoluteTolerance(0.0001), *testData);
+        checkVirialTensor(absoluteTolerance(0.0001), *testData);
+    }
 }
 
 TEST_P(ConstraintsTest, TwoDisjointConstraints)
@@ -405,20 +397,27 @@ TEST_P(ConstraintsTest, TwoDisjointConstraints)
             real(0.0), real(0.001), x, xPrime, v, shakeTolerance, shakeUseSOR, lincsNIter,
             lincslincsExpansionOrder, lincsWarnAngle);
 
-    std::string pbcName;
-    std::string algorithmName;
-    std::tie(pbcName, algorithmName) = GetParam();
-    t_pbc pbc                        = pbcs_.at(pbcName);
+    std::string pbcName = GetParam();
+    t_pbc       pbc     = pbcs_.at(pbcName);
+
+    // Cycle through all available runners
+    for (const auto& runner : getRunners())
+    {
+        SCOPED_TRACE(formatString("Testing %s with %s using %s.", testData->title_.c_str(),
+                                  pbcName.c_str(), runner->name().c_str()));
+
+        testData->reset();
 
-    // Apply constraints
-    algorithms_.at(algorithmName)(testData.get(), pbc);
+        // Apply constraints
+        runner->applyConstraints(testData.get(), pbc);
 
-    checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
-    checkConstrainsDirection(*testData, pbc);
-    checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
-    checkCOMVelocity(absoluteTolerance(0.0001), *testData);
+        checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
+        checkConstrainsDirection(*testData, pbc);
+        checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
+        checkCOMVelocity(absoluteTolerance(0.0001), *testData);
 
-    checkVirialTensor(absoluteTolerance(0.0001), *testData);
+        checkVirialTensor(absoluteTolerance(0.0001), *testData);
+    }
 }
 
 TEST_P(ConstraintsTest, ThreeSequentialConstraints)
@@ -457,20 +456,27 @@ TEST_P(ConstraintsTest, ThreeSequentialConstraints)
             real(0.0), real(0.001), x, xPrime, v, shakeTolerance, shakeUseSOR, lincsNIter,
             lincslincsExpansionOrder, lincsWarnAngle);
 
-    std::string pbcName;
-    std::string algorithmName;
-    std::tie(pbcName, algorithmName) = GetParam();
-    t_pbc pbc                        = pbcs_.at(pbcName);
+    std::string pbcName = GetParam();
+    t_pbc       pbc     = pbcs_.at(pbcName);
 
-    // Apply constraints
-    algorithms_.at(algorithmName)(testData.get(), pbc);
+    // Cycle through all available runners
+    for (const auto& runner : getRunners())
+    {
+        SCOPED_TRACE(formatString("Testing %s with %s using %s.", testData->title_.c_str(),
+                                  pbcName.c_str(), runner->name().c_str()));
 
-    checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
-    checkConstrainsDirection(*testData, pbc);
-    checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
-    checkCOMVelocity(absoluteTolerance(0.0001), *testData);
+        testData->reset();
 
-    checkVirialTensor(absoluteTolerance(0.0001), *testData);
+        // Apply constraints
+        runner->applyConstraints(testData.get(), pbc);
+
+        checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
+        checkConstrainsDirection(*testData, pbc);
+        checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
+        checkCOMVelocity(absoluteTolerance(0.0001), *testData);
+
+        checkVirialTensor(absoluteTolerance(0.0001), *testData);
+    }
 }
 
 TEST_P(ConstraintsTest, ThreeConstraintsWithCentralAtom)
@@ -510,20 +516,27 @@ TEST_P(ConstraintsTest, ThreeConstraintsWithCentralAtom)
             real(0.0), real(0.001), x, xPrime, v, shakeTolerance, shakeUseSOR, lincsNIter,
             lincslincsExpansionOrder, lincsWarnAngle);
 
-    std::string pbcName;
-    std::string algorithmName;
-    std::tie(pbcName, algorithmName) = GetParam();
-    t_pbc pbc                        = pbcs_.at(pbcName);
+    std::string pbcName = GetParam();
+    t_pbc       pbc     = pbcs_.at(pbcName);
+
+    // Cycle through all available runners
+    for (const auto& runner : getRunners())
+    {
+        SCOPED_TRACE(formatString("Testing %s with %s using %s.", testData->title_.c_str(),
+                                  pbcName.c_str(), runner->name().c_str()));
 
-    // Apply constraints
-    algorithms_.at(algorithmName)(testData.get(), pbc);
+        testData->reset();
 
-    checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
-    checkConstrainsDirection(*testData, pbc);
-    checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
-    checkCOMVelocity(absoluteTolerance(0.0001), *testData);
+        // Apply constraints
+        runner->applyConstraints(testData.get(), pbc);
 
-    checkVirialTensor(absoluteTolerance(0.0001), *testData);
+        checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
+        checkConstrainsDirection(*testData, pbc);
+        checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
+        checkCOMVelocity(absoluteTolerance(0.0001), *testData);
+
+        checkVirialTensor(absoluteTolerance(0.0001), *testData);
+    }
 }
 
 TEST_P(ConstraintsTest, FourSequentialConstraints)
@@ -562,20 +575,27 @@ TEST_P(ConstraintsTest, FourSequentialConstraints)
             real(0.0), real(0.001), x, xPrime, v, shakeTolerance, shakeUseSOR, lincsNIter,
             lincslincsExpansionOrder, lincsWarnAngle);
 
-    std::string pbcName;
-    std::string algorithmName;
-    std::tie(pbcName, algorithmName) = GetParam();
-    t_pbc pbc                        = pbcs_.at(pbcName);
+    std::string pbcName = GetParam();
+    t_pbc       pbc     = pbcs_.at(pbcName);
+
+    // Cycle through all available runners
+    for (const auto& runner : getRunners())
+    {
+        SCOPED_TRACE(formatString("Testing %s with %s using %s.", testData->title_.c_str(),
+                                  pbcName.c_str(), runner->name().c_str()));
+
+        testData->reset();
 
-    // Apply constraints
-    algorithms_.at(algorithmName)(testData.get(), pbc);
+        // Apply constraints
+        runner->applyConstraints(testData.get(), pbc);
 
-    checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
-    checkConstrainsDirection(*testData, pbc);
-    checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
-    checkCOMVelocity(absoluteTolerance(0.0001), *testData);
+        checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
+        checkConstrainsDirection(*testData, pbc);
+        checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
+        checkCOMVelocity(absoluteTolerance(0.0001), *testData);
 
-    checkVirialTensor(absoluteTolerance(0.01), *testData);
+        checkVirialTensor(absoluteTolerance(0.01), *testData);
+    }
 }
 
 TEST_P(ConstraintsTest, TriangleOfConstraints)
@@ -613,27 +633,31 @@ TEST_P(ConstraintsTest, TriangleOfConstraints)
             real(0.0), real(0.001), x, xPrime, v, shakeTolerance, shakeUseSOR, lincsNIter,
             lincslincsExpansionOrder, lincsWarnAngle);
 
-    std::string pbcName;
-    std::string runnerName;
-    std::tie(pbcName, runnerName) = GetParam();
-    t_pbc pbc                     = pbcs_.at(pbcName);
+    std::string pbcName = GetParam();
+    t_pbc       pbc     = pbcs_.at(pbcName);
+
+    // Cycle through all available runners
+    for (const auto& runner : getRunners())
+    {
+        SCOPED_TRACE(formatString("Testing %s with %s using %s.", testData->title_.c_str(),
+                                  pbcName.c_str(), runner->name().c_str()));
 
-    // Apply constraints
-    algorithms_.at(runnerName)(testData.get(), pbc);
+        testData->reset();
 
-    checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
-    checkConstrainsDirection(*testData, pbc);
-    checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
-    checkCOMVelocity(absoluteTolerance(0.0001), *testData);
+        // Apply constraints
+        runner->applyConstraints(testData.get(), pbc);
 
-    checkVirialTensor(absoluteTolerance(0.00001), *testData);
+        checkConstrainsLength(absoluteTolerance(0.0002), *testData, pbc);
+        checkConstrainsDirection(*testData, pbc);
+        checkCOMCoordinates(absoluteTolerance(0.0001), *testData);
+        checkCOMVelocity(absoluteTolerance(0.0001), *testData);
+
+        checkVirialTensor(absoluteTolerance(0.00001), *testData);
+    }
 }
 
 
-INSTANTIATE_TEST_CASE_P(WithParameters,
-                        ConstraintsTest,
-                        ::testing::Combine(::testing::Values("PBCNone", "PBCXYZ"),
-                                           ::testing::ValuesIn(getRunnersNames())));
+INSTANTIATE_TEST_CASE_P(WithParameters, ConstraintsTest, ::testing::Values("PBCNone", "PBCXYZ"));
 
 } // namespace
 } // namespace test
index 0679b51713e5a965ff08388110ef496efaf035a4..3de0c11b896b37adb798905aded2347f098e6e56 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -95,27 +95,12 @@ ConstraintsTestData::ConstraintsTestData(const std::string&       title,
 
     invdt_ = 1.0 / timestep; // Inverse timestep
 
-    // Communication record
-    cr_.nnodes = 1;
-    cr_.dd     = nullptr;
-
-    // Multisim data
-    ms_.sim  = 0;
-    ms_.nsim = 1;
-
     // Input record - data that usually comes from configuration file (.mdp)
     ir_.efep    = 0;
     ir_.init_t  = initialTime;
     ir_.delta_t = timestep;
     ir_.eI      = 0;
 
-    // MD atoms data
-    md_.nMassPerturbed = 0;
-    md_.lambda         = 0.0;
-    md_.invmass        = invmass_.data();
-    md_.nr             = numAtoms;
-    md_.homenr         = numAtoms;
-
     // Virial evaluation
     computeVirial_ = computeVirial;
     if (computeVirial)
@@ -130,8 +115,9 @@ ConstraintsTestData::ConstraintsTestData(const std::string&       title,
         }
     }
 
-
     // Free energy evaluation
+    hasMassPerturbed_  = false;
+    lambda_            = 0.0;
     compute_dHdLambda_ = compute_dHdLambda;
     dHdLambda_         = 0;
     if (compute_dHdLambda_)
@@ -145,31 +131,25 @@ ConstraintsTestData::ConstraintsTestData(const std::string&       title,
         dHdLambdaRef_ = 0;
     }
 
-    // Constraints and their parameters (local topology)
-    for (int i = 0; i < F_NRE; i++)
-    {
-        idef_.il[i].nr = 0;
-    }
-    idef_.il[F_CONSTR].nr = constraints.size();
-
-    snew(idef_.il[F_CONSTR].iatoms, constraints.size());
     int maxType = 0;
-    for (index i = 0; i < ssize(constraints); i++)
+    for (index i = 0; i < ssize(constraints); i += 3)
     {
-        if (i % 3 == 0)
+        if (maxType < constraints.at(i))
         {
-            if (maxType < constraints.at(i))
-            {
-                maxType = constraints.at(i);
-            }
+            maxType = constraints.at(i);
         }
-        idef_.il[F_CONSTR].iatoms[i] = constraints.at(i);
     }
-    snew(idef_.iparams, maxType + 1);
+    auto& iparams = mtop_.ffparams.iparams;
+    iparams.resize(maxType + 1);
     for (index i = 0; i < ssize(constraints) / 3; i++)
     {
-        idef_.iparams[constraints.at(3 * i)].constr.dA = constraintsR0.at(constraints.at(3 * i));
-        idef_.iparams[constraints.at(3 * i)].constr.dB = constraintsR0.at(constraints.at(3 * i));
+        iparams[constraints.at(3 * i)].constr.dA = constraintsR0.at(constraints.at(3 * i));
+        iparams[constraints.at(3 * i)].constr.dB = constraintsR0.at(constraints.at(3 * i));
+    }
+    idef_ = std::make_unique<InteractionDefinitions>(mtop_.ffparams);
+    for (index i = 0; i < ssize(constraints); i++)
+    {
+        idef_->il[F_CONSTR].iatoms.push_back(constraints.at(i));
     }
 
     // Constraints and their parameters (global topology)
@@ -190,12 +170,7 @@ ConstraintsTestData::ConstraintsTestData(const std::string&       title,
     molBlock.nmol = 1;
     mtop_.molblock.push_back(molBlock);
 
-    mtop_.natoms = numAtoms;
-    mtop_.ffparams.iparams.resize(maxType + 1);
-    for (int i = 0; i <= maxType; i++)
-    {
-        mtop_.ffparams.iparams.at(i) = idef_.iparams[i];
-    }
+    mtop_.natoms                      = numAtoms;
     mtop_.bIntermolecularInteractions = false;
 
     // Coordinates and velocities
@@ -251,14 +226,5 @@ void ConstraintsTestData::reset()
     dHdLambda_ = 0;
 }
 
-/*! \brief
- * Cleaning up the memory.
- */
-ConstraintsTestData::~ConstraintsTestData()
-{
-    sfree(idef_.il[F_CONSTR].iatoms);
-    sfree(idef_.iparams);
-}
-
 } // namespace test
 } // namespace gmx
index 4283674b1f715948e507a3bf2ff9d14e98aa7c32..9adb9c0a61765695253cd100bad5767162d77191 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_MDLIB_TESTS_CONSTRTESTDATA_H
 #define GMX_MDLIB_TESTS_CONSTRTESTDATA_H
 
+#include <memory>
 #include <vector>
 
 #include "gromacs/gmxlib/nrnb.h"
-#include "gromacs/gpu_utils/gpu_testutils.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/math/paddedvector.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdlib/lincs.h"
 #include "gromacs/mdlib/shake.h"
-#include "gromacs/mdrunutility/multisim.h"
-#include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/idef.h"
 #include "gromacs/topology/ifunc.h"
@@ -89,16 +87,10 @@ public:
     std::vector<real> masses_;
     //! Inverse masses
     std::vector<real> invmass_;
-    //! Communication record
-    t_commrec cr_;
     //! Input record (info that usually in .mdp file)
     t_inputrec ir_;
     //! Local topology
-    t_idef idef_;
-    //! MD atoms
-    t_mdatoms md_;
-    //! Multisim data
-    gmx_multisim_t ms_;
+    std::unique_ptr<InteractionDefinitions> idef_;
     //! Computational time array (normally used to benchmark performance)
     t_nrnb nrnb_;
 
@@ -114,6 +106,10 @@ public:
     tensor virialScaledRef_;
     //! If the free energy is computed
     bool compute_dHdLambda_;
+    //! If there are atoms with perturbed mass
+    bool hasMassPerturbed_ = false;
+    //! Lambda value
+    real lambda_ = 0.0;
     //! For free energy computation
     real dHdLambda_;
     //! For free energy computation (reference value)
@@ -206,11 +202,6 @@ public:
      *
      */
     void reset();
-
-    /*! \brief
-     * Cleaning up the memory.
-     */
-    ~ConstraintsTestData();
 };
 
 } // namespace test
index 710e9ac48c1b83ba40293f68dfb41ca9fba3bf4b..f57a9de20dc169a23e6d1cb99d5aed74aa61c269 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdlib/constr.h"
 #include "gromacs/mdlib/lincs.h"
 #include "gromacs/mdlib/shake.h"
+#include "gromacs/mdrunutility/multisim.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/pbc.h"
-#include "gromacs/topology/block.h"
 #include "gromacs/topology/idef.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/topology/topology.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/unique_cptr.h"
 
 #include "testutils/testasserts.h"
@@ -80,33 +80,19 @@ namespace gmx
 namespace test
 {
 
-/*! \brief
- * Initialize and apply SHAKE constraints.
- *
- * \param[in] testData        Test data structure.
- * \param[in] pbc             Periodic boundary data.
- */
-void applyShake(ConstraintsTestData* testData, t_pbc gmx_unused pbc)
+void ShakeConstraintsRunner::applyConstraints(ConstraintsTestData* testData, t_pbc /* pbc */)
 {
-    shakedata* shaked = shake_init();
-    make_shake_sblock_serial(shaked, &testData->idef_, testData->md_);
+    shakedata shaked;
+    make_shake_sblock_serial(&shaked, testData->idef_.get(), testData->numAtoms_);
     bool success = constrain_shake(
-            nullptr, shaked, testData->invmass_.data(), testData->idef_, testData->ir_,
-            as_rvec_array(testData->x_.data()), as_rvec_array(testData->xPrime_.data()),
-            as_rvec_array(testData->xPrime2_.data()), &testData->nrnb_, testData->md_.lambda,
-            &testData->dHdLambda_, testData->invdt_, as_rvec_array(testData->v_.data()),
-            testData->computeVirial_, testData->virialScaled_, false, gmx::ConstraintVariable::Positions);
+            nullptr, &shaked, testData->invmass_.data(), *testData->idef_, testData->ir_, testData->x_,
+            testData->xPrime_, testData->xPrime2_, nullptr, &testData->nrnb_, testData->lambda_,
+            &testData->dHdLambda_, testData->invdt_, testData->v_, testData->computeVirial_,
+            testData->virialScaled_, false, gmx::ConstraintVariable::Positions);
     EXPECT_TRUE(success) << "Test failed with a false return value in SHAKE.";
-    done_shake(shaked);
 }
 
-/*! \brief
- * Initialize and apply LINCS constraints.
- *
- * \param[in] testData        Test data structure.
- * \param[in] pbc             Periodic boundary data.
- */
-void applyLincs(ConstraintsTestData* testData, t_pbc pbc)
+void LincsConstraintsRunner::applyConstraints(ConstraintsTestData* testData, t_pbc pbc)
 {
 
     Lincs* lincsd;
@@ -114,8 +100,16 @@ void applyLincs(ConstraintsTestData* testData, t_pbc pbc)
     int    warncount_lincs = 0;
     gmx_omp_nthreads_set(emntLINCS, 1);
 
+    // Communication record
+    t_commrec cr;
+    cr.nnodes = 1;
+    cr.dd     = nullptr;
+
+    // Multi-sim record
+    gmx_multisim_t ms{ 1, 0, MPI_COMM_NULL, MPI_COMM_NULL };
+
     // Make blocka structure for faster LINCS setup
-    std::vector<t_blocka> at2con_mt;
+    std::vector<ListOfLists<int>> at2con_mt;
     at2con_mt.reserve(testData->mtop_.moltype.size());
     for (const gmx_moltype_t& moltype : testData->mtop_.moltype)
     {
@@ -126,38 +120,30 @@ void applyLincs(ConstraintsTestData* testData, t_pbc pbc)
     // Initialize LINCS
     lincsd = init_lincs(nullptr, testData->mtop_, testData->nflexcon_, at2con_mt, false,
                         testData->ir_.nLincsIter, testData->ir_.nProjOrder);
-    set_lincs(testData->idef_, testData->md_, EI_DYNAMICS(testData->ir_.eI), &testData->cr_, lincsd);
+    set_lincs(*testData->idef_, testData->numAtoms_, testData->invmass_.data(), testData->lambda_,
+              EI_DYNAMICS(testData->ir_.eI), &cr, lincsd);
 
     // Evaluate constraints
     bool success = constrain_lincs(
-            false, testData->ir_, 0, lincsd, testData->md_, &testData->cr_, &testData->ms_,
-            as_rvec_array(testData->x_.data()), as_rvec_array(testData->xPrime_.data()),
-            as_rvec_array(testData->xPrime2_.data()), pbc.box, &pbc, testData->md_.lambda,
-            &testData->dHdLambda_, testData->invdt_, as_rvec_array(testData->v_.data()),
-            testData->computeVirial_, testData->virialScaled_, gmx::ConstraintVariable::Positions,
-            &testData->nrnb_, maxwarn, &warncount_lincs);
+            false, testData->ir_, 0, lincsd, testData->invmass_.data(), &cr, &ms,
+            testData->x_.arrayRefWithPadding(), testData->xPrime_.arrayRefWithPadding(),
+            testData->xPrime2_.arrayRefWithPadding().unpaddedArrayRef(), pbc.box, &pbc,
+            testData->hasMassPerturbed_, testData->lambda_, &testData->dHdLambda_, testData->invdt_,
+            testData->v_.arrayRefWithPadding().unpaddedArrayRef(), testData->computeVirial_,
+            testData->virialScaled_, gmx::ConstraintVariable::Positions, &testData->nrnb_, maxwarn,
+            &warncount_lincs);
     EXPECT_TRUE(success) << "Test failed with a false return value in LINCS.";
     EXPECT_EQ(warncount_lincs, 0) << "There were warnings in LINCS.";
-    for (auto& moltype : at2con_mt)
-    {
-        sfree(moltype.index);
-        sfree(moltype.a);
-    }
     done_lincs(lincsd);
 }
 
-#if GMX_GPU != GMX_GPU_CUDA
-/*! \brief
- * Stub for LINCS constraints on CUDA-enabled GPU to satisfy compiler.
- *
- * \param[in] testData        Test data structure.
- * \param[in] pbc             Periodic boundary data.
- */
-void applyLincsCuda(ConstraintsTestData gmx_unused* testData, t_pbc gmx_unused pbc)
+#if !GMX_GPU_CUDA
+void LincsDeviceConstraintsRunner::applyConstraints(ConstraintsTestData* /* testData */, t_pbc /* pbc */)
 {
+    GMX_UNUSED_VALUE(testDevice_);
     FAIL() << "Dummy LINCS CUDA function was called instead of the real one.";
 }
-#endif
+#endif // !GMX_GPU_CUDA
 
 } // namespace test
 } // namespace gmx
index c62e67e94b151065af69960c42d3595613a542e1..88cc8e866d48b951af5c795c7e3140e388b3554d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,8 +52,8 @@
 #include <vector>
 
 #include "gromacs/gpu_utils/devicebuffer.cuh"
-#include "gromacs/gpu_utils/gpu_utils.h"
-#include "gromacs/mdlib/lincs_cuda.cuh"
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/mdlib/lincs_gpu.cuh"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/utility/unique_cptr.h"
 
@@ -62,45 +62,44 @@ namespace gmx
 namespace test
 {
 
-/*! \brief
- * Initialize and apply LINCS constraints on CUDA-enabled GPU.
- *
- * \param[in] testData        Test data structure.
- * \param[in] pbc             Periodic boundary data.
- */
-void applyLincsCuda(ConstraintsTestData* testData, t_pbc pbc)
+void LincsDeviceConstraintsRunner::applyConstraints(ConstraintsTestData* testData, t_pbc pbc)
 {
-    auto lincsCuda =
-            std::make_unique<LincsCuda>(testData->ir_.nLincsIter, testData->ir_.nProjOrder, nullptr);
+    const DeviceContext& deviceContext = testDevice_.deviceContext();
+    const DeviceStream&  deviceStream  = testDevice_.deviceStream();
+    setActiveDevice(testDevice_.deviceInfo());
+
+    auto lincsGpu = std::make_unique<LincsGpu>(testData->ir_.nLincsIter, testData->ir_.nProjOrder,
+                                               deviceContext, deviceStream);
 
     bool    updateVelocities = true;
     int     numAtoms         = testData->numAtoms_;
     float3 *d_x, *d_xp, *d_v;
 
-    lincsCuda->set(testData->idef_, testData->md_);
-    lincsCuda->setPbc(&pbc);
+    lincsGpu->set(*testData->idef_, testData->numAtoms_, testData->invmass_.data());
+    PbcAiuc pbcAiuc;
+    setPbcAiuc(pbc.ndim_ePBC, pbc.box, &pbcAiuc);
 
-    allocateDeviceBuffer(&d_x, numAtoms, nullptr);
-    allocateDeviceBuffer(&d_xp, numAtoms, nullptr);
-    allocateDeviceBuffer(&d_v, numAtoms, nullptr);
+    allocateDeviceBuffer(&d_x, numAtoms, deviceContext);
+    allocateDeviceBuffer(&d_xp, numAtoms, deviceContext);
+    allocateDeviceBuffer(&d_v, numAtoms, deviceContext);
 
-    copyToDeviceBuffer(&d_x, (float3*)(testData->x_.data()), 0, numAtoms, nullptr,
+    copyToDeviceBuffer(&d_x, (float3*)(testData->x_.data()), 0, numAtoms, deviceStream,
                        GpuApiCallBehavior::Sync, nullptr);
-    copyToDeviceBuffer(&d_xp, (float3*)(testData->xPrime_.data()), 0, numAtoms, nullptr,
+    copyToDeviceBuffer(&d_xp, (float3*)(testData->xPrime_.data()), 0, numAtoms, deviceStream,
                        GpuApiCallBehavior::Sync, nullptr);
     if (updateVelocities)
     {
-        copyToDeviceBuffer(&d_v, (float3*)(testData->v_.data()), 0, numAtoms, nullptr,
+        copyToDeviceBuffer(&d_v, (float3*)(testData->v_.data()), 0, numAtoms, deviceStream,
                            GpuApiCallBehavior::Sync, nullptr);
     }
-    lincsCuda->apply(d_x, d_xp, updateVelocities, d_v, testData->invdt_, testData->computeVirial_,
-                     testData->virialScaled_);
+    lincsGpu->apply(d_x, d_xp, updateVelocities, d_v, testData->invdt_, testData->computeVirial_,
+                    testData->virialScaled_, pbcAiuc);
 
-    copyFromDeviceBuffer((float3*)(testData->xPrime_.data()), &d_xp, 0, numAtoms, nullptr,
+    copyFromDeviceBuffer((float3*)(testData->xPrime_.data()), &d_xp, 0, numAtoms, deviceStream,
                          GpuApiCallBehavior::Sync, nullptr);
     if (updateVelocities)
     {
-        copyFromDeviceBuffer((float3*)(testData->v_.data()), &d_v, 0, numAtoms, nullptr,
+        copyFromDeviceBuffer((float3*)(testData->v_.data()), &d_v, 0, numAtoms, deviceStream,
                              GpuApiCallBehavior::Sync, nullptr);
     }
 
index f4d24f6e35f7d2c459a6edfcdf200b650fb7fd81..d479e5537cd4f7875e3514d26e562aa0cd2152d5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \internal \file
- * \brief SHAKE and LINCS tests header.
+ * \brief SHAKE and LINCS tests runners.
  *
- * Contains description and constructor for the test data accumulating object,
- * declares CPU- and GPU-based functions used to apply SHAKE or LINCS on the
- * test data.
+ * Declares test runner class for constraints. The test runner abstract class is used
+ * to unify the interfaces for different constraints methods, running on different
+ * hardware.  This allows to run the same test on the same data using different
+ * implementations of the parent class, that inherit its interfaces.
  *
  * \author Artem Zhmurov <zhmurov@gmail.com>
  * \ingroup module_mdlib
 #ifndef GMX_MDLIB_TESTS_CONSTRTESTRUNNERS_H
 #define GMX_MDLIB_TESTS_CONSTRTESTRUNNERS_H
 
+#include <gtest/gtest.h>
+
+#include "testutils/test_device.h"
+
 #include "constrtestdata.h"
 
 struct t_pbc;
@@ -55,18 +60,92 @@ namespace gmx
 namespace test
 {
 
-/*! \brief Apply SHAKE constraints to the test data.
- */
-void applyShake(ConstraintsTestData* testData, t_pbc pbc);
-/*! \brief Apply LINCS constraints to the test data.
- */
-void applyLincs(ConstraintsTestData* testData, t_pbc pbc);
-/*! \brief Apply CUDA version of LINCS constraints to the test data.
+/* \brief Constraints test runner interface.
  *
- * All the data is copied to the GPU device, then LINCS is applied and
- * the resulting coordinates are copied back.
+ * Wraps the actual implementation of constraints algorithm into common interface.
  */
-void applyLincsCuda(ConstraintsTestData* testData, t_pbc pbc);
+class IConstraintsTestRunner
+{
+public:
+    //! Virtual destructor.
+    virtual ~IConstraintsTestRunner() {}
+    /*! \brief Abstract constraining function. Should be overriden.
+     *
+     * \param[in] testData             Test data structure.
+     * \param[in] pbc                  Periodic boundary data.
+     */
+    virtual void applyConstraints(ConstraintsTestData* testData, t_pbc pbc) = 0;
+
+    /*! \brief Get the name of the implementation.
+     *
+     * \return "<algorithm> on <device>", depending on the actual implementation used. E.g., "LINCS on #0: NVIDIA GeForce GTX 1660 SUPER".
+     */
+    virtual std::string name() = 0;
+};
+
+// Runner for the CPU implementation of SHAKE constraints algorithm.
+class ShakeConstraintsRunner : public IConstraintsTestRunner
+{
+public:
+    //! Default constructor.
+    ShakeConstraintsRunner() {}
+    /*! \brief Apply SHAKE constraints to the test data.
+     *
+     * \param[in] testData             Test data structure.
+     * \param[in] pbc                  Periodic boundary data.
+     */
+    void applyConstraints(ConstraintsTestData* testData, t_pbc pbc) override;
+    /*! \brief Get the name of the implementation.
+     *
+     * \return "SHAKE" string;
+     */
+    std::string name() override { return "SHAKE on CPU"; }
+};
+
+// Runner for the CPU implementation of LINCS constraints algorithm.
+class LincsConstraintsRunner : public IConstraintsTestRunner
+{
+public:
+    //! Default constructor.
+    LincsConstraintsRunner() {}
+    /*! \brief Apply LINCS constraints to the test data on the CPU.
+     *
+     * \param[in] testData             Test data structure.
+     * \param[in] pbc                  Periodic boundary data.
+     */
+    void applyConstraints(ConstraintsTestData* testData, t_pbc pbc) override;
+    /*! \brief Get the name of the implementation.
+     *
+     * \return "LINCS" string;
+     */
+    std::string name() override { return "LINCS on CPU"; }
+};
+
+// Runner for the GPU implementation of LINCS constraints algorithm.
+class LincsDeviceConstraintsRunner : public IConstraintsTestRunner
+{
+public:
+    /*! \brief Constructor. Keeps a copy of the hardware context.
+     *
+     * \param[in] testDevice The device hardware context to be used by the runner.
+     */
+    LincsDeviceConstraintsRunner(const TestDevice& testDevice) : testDevice_(testDevice) {}
+    /*! \brief Apply LINCS constraints to the test data on the GPU.
+     *
+     * \param[in] testData             Test data structure.
+     * \param[in] pbc                  Periodic boundary data.
+     */
+    void applyConstraints(ConstraintsTestData* testData, t_pbc pbc) override;
+    /*! \brief Get the name of the implementation.
+     *
+     * \return "LINCS_GPU" string;
+     */
+    std::string name() override { return "LINCS on " + testDevice_.description(); }
+
+private:
+    //! Test device to be used in the runner.
+    const TestDevice& testDevice_;
+};
 
 } // namespace test
 } // namespace gmx
index dae26029590b2056f3f0aa21573f8dce4752469b..bb4ce4ce00b914871d5dfdc9eabddb5141f793e0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -145,8 +145,6 @@ public:
     t_inputrec inputrec_;
     //! Topology
     gmx_mtop_t mtop_;
-    //! MD atoms
-    t_mdatoms mdatoms_;
     //! Simulation time
     double time_;
     //! Total mass
@@ -175,8 +173,6 @@ public:
     std::vector<char*> groupNameHandles_;
     //! Total dipole momentum
     rvec muTotal_;
-    //! Distance and orientation restraints data
-    t_fcdata fcd_;
     //! Communication record
     t_commrec cr_;
     //! Constraints object (for constraints RMSD output in case of LINCS)
@@ -378,12 +374,8 @@ public:
         // TODO EnergyOutput should not take Constraints object
         // TODO This object will always return zero as RMSD value.
         //      It is more relevant to have non-zero value for testing.
-        constraints_ = makeConstraints(mtop_, inputrec_, nullptr, false, nullptr, mdatoms_, &cr_,
-                                       nullptr, nullptr, nullptr, false);
-
-        // No position/orientation restraints
-        fcd_.disres.npair = 0;
-        fcd_.orires.nr    = 0;
+        constraints_ = makeConstraints(mtop_, inputrec_, nullptr, false, nullptr, &cr_, nullptr,
+                                       nullptr, nullptr, false);
     }
 
     /*! \brief Helper function to generate synthetic data to output
@@ -642,9 +634,12 @@ TEST_P(EnergyOutputTest, CheckOutput)
     for (int frame = 0; frame < parameters.numFrames; frame++)
     {
         setStepData(&testValue);
-        energyOutput->addDataAtEnergyStep(false, true, time_, tmass_, enerdata_.get(), &state_, nullptr,
-                                          nullptr, box_, constraintsVirial_, forceVirial_, totalVirial_,
-                                          pressure_, &ekindata_, muTotal_, constraints_.get());
+        energyOutput->addDataAtEnergyStep(
+                false, true, time_, tmass_, enerdata_.get(), nullptr, nullptr, box_,
+                PTCouplingArrays({ state_.boxv, state_.nosehoover_xi, state_.nosehoover_vxi,
+                                   state_.nhpres_xi, state_.nhpres_vxi }),
+                state_.fep_state, constraintsVirial_, forceVirial_, totalVirial_, pressure_,
+                &ekindata_, muTotal_, constraints_.get());
 
         energyOutput->printAnnealingTemperatures(log_, &mtop_.groups, &inputrec_.opts);
         energyOutput->printStepToEnergyFile(energyFile_, true, false, false, log_, 100 * frame,
diff --git a/src/gromacs/mdlib/tests/freeenergyparameters.cpp b/src/gromacs/mdlib/tests/freeenergyparameters.cpp
new file mode 100644 (file)
index 0000000..a6ea796
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Tests routines in freeenergyparameters.h .
+ *
+ * \author Christian Blau <blau@kth.se>
+ */
+
+#include "gmxpre.h"
+
+#include "gromacs/mdlib/freeenergyparameters.h"
+
+#include <gtest/gtest.h>
+
+#include "gromacs/mdtypes/inputrec.h"
+
+#include "testutils/testasserts.h"
+#include "testutils/testmatchers.h"
+
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+
+/*! \brief Parameters that will vary from test to test.
+ */
+struct FreeEnergyParameterTestParameters
+{
+    //! current state of lambda in the simulation, -1 if not set
+    int currentLambdaState = -1;
+    //! Fractional value of lambda to start from, -1 if not set
+    double initLambda = -1;
+    //! The initial number of the state, -1 if not set
+    int initFepState = -1;
+    //! Change of lambda per time step (fraction of (0.1)
+    double deltaLambda = 0;
+    //! number of lambda entries
+    int nLambda = 0;
+    //! the current simulation step
+    int64_t step = 0;
+    //! the expected lambda at the current simulation step
+    std::array<real, efptNR> expectedLambdas = { -1, -1, -1, -1, -1, -1, -1 };
+};
+
+
+/*! \brief Sets of parameters on which to run the tests.
+ */
+const FreeEnergyParameterTestParameters freeEnergyParameterSets[] = {
+    // no parameters set at all
+    { -1, -1, -1, 0, 1, 0, { -1, -1, -1, -1, -1, -1, -1 } },
+    // setting current lambda state to 0, no other variables set, using eftpNR * [0.8] as lambda state matrix
+    { 0, -1, -1, 0, 1, 1, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    // setting current lambda state to 1, no other variables set, using eftpNR * [0.2,0.8] as lambda state matrix
+    { 1, -1, -1, 0, 2, 1, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    // test that non-zero deltaLambda trumps current lambda state
+    // setting current lambda state to 1, using deltaLambda 0.1 and setp 0 and eftpNR * [0.2,0.8] as lambda state matrix
+    { 1, -1, -1, 0.1, 2, 0, { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2 } },
+    // test that interpolating lambda values starting
+    // from lambda = 0, deltaLambda = 0.1, step = 10 results in values at end-state
+    { 1, 0, -1, 0.1, 2, 10, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    // interpolating half the way
+    { 1, 0, -1, 0.1, 2, 5, { 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 } },
+    // starting from end-state 1 and move lambda half-way with negative deltaLambda = -0.1
+    { -1, -1, 1, -0.1, 2, 5, { 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 } },
+    // starting from end-state 1 and move one step backwards with negative deltaLambda = -0.1
+    { -1, -1, 1, -0.1, 2, 1, { 0.74, 0.74, 0.74, 0.74, 0.74, 0.74, 0.74 } },
+    // three lambda states, the last two equal ([0.2,0.8,0.8]), move forward with deltaLambda = 0.1
+    { -1, -1, 0, 0.1, 3, 0, { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2 } },
+    { -1, -1, 0, 0.1, 3, 3, { 0.56, 0.56, 0.56, 0.56, 0.56, 0.56, 0.56 } },
+    { -1, -1, 0, 0.1, 3, 7, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 0, 0.1, 3, 8, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 0, 0.1, 3, 10, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    // three lambda states, the last two equal ([0.2,0.8,0.8]), move backwards
+    { -1, -1, 2, -0.1, 3, 1, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 2, -0.1, 3, 2, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 2, -0.1, 3, 3, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 2, -0.1, 3, 5, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 2, -0.1, 3, 7, { 0.56, 0.56, 0.56, 0.56, 0.56, 0.56, 0.56 } },
+    { -1, -1, 2, -0.1, 3, 8, { 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44 } },
+    { -1, -1, 2, -0.1, 3, 10, { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2 } },
+    // three lambda states, the last two equal ([0.2,0.8,0.8]), start in middle state, move backwards
+    { -1, -1, 1, -0.1, 3, 0, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 1, -0.1, 3, 2, { 0.56, 0.56, 0.56, 0.56, 0.56, 0.56, 0.56 } },
+    { -1, -1, 1, -0.1, 3, 3, { 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44 } },
+};
+
+/*! \brief Test for setting free energy parameters.
+ */
+class FreeEnergyParameterTest : public ::testing::TestWithParam<FreeEnergyParameterTestParameters>
+{
+public:
+    //! Fills in the required FEP values for testing from the test parameters
+    t_lambda getFepVals()
+    {
+        t_lambda fepvals;
+        fepvals.init_fep_state = GetParam().initFepState;
+        fepvals.init_lambda    = GetParam().initLambda;
+        fepvals.delta_lambda   = GetParam().deltaLambda;
+        fepvals.all_lambda     = getLambdaMatrix(GetParam().nLambda);
+        fepvals.n_lambda       = GetParam().nLambda;
+        return fepvals;
+    }
+
+    /*! \brief construct a lambda matrix
+     *
+     * \param[in] nLambda
+     * \returns nLambda * eftpNR matrix with pre-defined values
+     */
+    double** getLambdaMatrix(int nLambda)
+    {
+        for (int i = 0; i < efptNR; ++i)
+        {
+            allLambda_[i] = defaultLambdaArrayForTest_[nLambda].data();
+        }
+        return allLambda_.data();
+    }
+
+private:
+    //! Construction aide for double ** matrix without snew
+    std::array<double*, efptNR> allLambda_;
+    //! a set of default lambda arrays for different lengths
+    std::vector<std::vector<double>> defaultLambdaArrayForTest_ = { {}, { 0.8 }, { 0.2, 0.8 }, { 0.2, 0.8, 0.8 } };
+};
+
+TEST_P(FreeEnergyParameterTest, CorrectLambdas)
+{
+    EXPECT_THAT(GetParam().expectedLambdas,
+                Pointwise(RealEq(defaultRealTolerance()),
+                          currentLambdas(GetParam().step, getFepVals(), GetParam().currentLambdaState)));
+}
+
+INSTANTIATE_TEST_CASE_P(WithParameters, FreeEnergyParameterTest, ::testing::ValuesIn(freeEnergyParameterSets));
+
+} // namespace
+
+} // namespace test
+
+} // namespace gmx
index abfe31c3ab017285d38c2ec8e67c93cdcf0f0b75..79c257abb3bd0b588ca619ad2a160a15eb3b3802 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <gtest/gtest.h>
 
-#include "gromacs/gpu_utils/gpu_testutils.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/utility/stringutil.h"
 
 #include "testutils/refdata.h"
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
 #include "leapfrogtestdata.h"
@@ -137,8 +138,6 @@ const LeapFrogTestParameters parametersSets[] = {
 class LeapFrogTest : public ::testing::TestWithParam<LeapFrogTestParameters>
 {
 public:
-    //! Availiable runners (CPU and GPU versions of the Leap-Frog)
-    static std::unordered_map<std::string, void (*)(LeapFrogTestData* testData, const int numSteps)> s_runners_;
     //! Reference data
     TestReferenceData refData_;
     //! Checker for reference data
@@ -146,28 +145,15 @@ public:
 
     LeapFrogTest() : checker_(refData_.rootChecker()) {}
 
-    //! Setup the runners one for all parameters sets
-    static void SetUpTestCase()
-    {
-        //
-        // All runners should be registered here under appropriate conditions
-        //
-        s_runners_["LeapFrogSimple"] = integrateLeapFrogSimple;
-        if (GMX_GPU == GMX_GPU_CUDA && canComputeOnGpu())
-        {
-            s_runners_["LeapFrogGpu"] = integrateLeapFrogGpu;
-        }
-    }
-
     /*! \brief Test the numerical integrator against analytical solution for simple constant force case.
      *
      * \param[in]  tolerance  Tolerance
      * \param[in]  testData   Test data object
      * \param[in]  totalTime  Total numerical integration time
      */
-    void testAgainstAnalyticalSolution(FloatingPointTolerance  tolerance,
-                                       const LeapFrogTestData& testData,
-                                       const real              totalTime)
+    static void testAgainstAnalyticalSolution(FloatingPointTolerance  tolerance,
+                                              const LeapFrogTestData& testData,
+                                              const real              totalTime)
     {
         for (int i = 0; i < testData.numAtoms_; i++)
         {
@@ -223,21 +209,31 @@ public:
     }
 };
 
-std::unordered_map<std::string, void (*)(LeapFrogTestData* testData, const int numSteps)> LeapFrogTest::s_runners_;
-
 TEST_P(LeapFrogTest, SimpleIntegration)
 {
-    // Cycle through all available runners
-    for (const auto& runner : s_runners_)
+    // Construct the list of runners
+    std::vector<std::unique_ptr<ILeapFrogTestRunner>> runners;
+    // Add runners for CPU version
+    runners.emplace_back(std::make_unique<LeapFrogHostTestRunner>());
+    // If using CUDA, add runners for the GPU version for each available GPU
+    if (GMX_GPU_CUDA)
     {
-        std::string runnerName = runner.first;
+        for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
+        {
+            runners.emplace_back(std::make_unique<LeapFrogDeviceTestRunner>(*testDevice));
+        }
+    }
 
+    for (const auto& runner : runners)
+    {
         LeapFrogTestParameters parameters = GetParam();
 
         std::string testDescription = formatString(
-                "Testing %s with %d atoms for %d timesteps with %d temperature coupling groups and "
-                "%s pressure coupling (dt = %f, v0=(%f, %f, %f), f0=(%f, %f, %f), nstpcouple = %d)",
-                runnerName.c_str(), parameters.numAtoms, parameters.numSteps,
+                "Testing on %s with %d atoms for %d timesteps with %d temperature coupling "
+                "groups and "
+                "%s pressure coupling (dt = %f, v0=(%f, %f, %f), f0=(%f, %f, %f), nstpcouple = "
+                "%d)",
+                runner->hardwareDescription().c_str(), parameters.numAtoms, parameters.numSteps,
                 parameters.numTCoupleGroups, parameters.nstpcouple == 0 ? "without" : "with",
                 parameters.timestep, parameters.v[XX], parameters.v[YY], parameters.v[ZZ],
                 parameters.f[XX], parameters.f[YY], parameters.f[ZZ], parameters.nstpcouple);
@@ -247,7 +243,7 @@ TEST_P(LeapFrogTest, SimpleIntegration)
                 parameters.numAtoms, parameters.timestep, parameters.v, parameters.f,
                 parameters.numTCoupleGroups, parameters.nstpcouple);
 
-        runner.second(testData.get(), parameters.numSteps);
+        runner->integrate(testData.get(), parameters.numSteps);
 
         real totalTime = parameters.numSteps * parameters.timestep;
         // TODO For the case of constant force, the numerical scheme is exact and
index 38c891274d572de9a236157788556bf09f691353..2725e3dd0c21f7c8ad9f4c9824c7b9b6054a5192 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -180,7 +180,7 @@ LeapFrogTestData::LeapFrogTestData(int        numAtoms,
     mdAtoms_.havePartiallyFrozenAtoms = false;
     mdAtoms_.cFREEZE                  = nullptr;
 
-    update_ = std::make_unique<Update>(&inputRecord_, nullptr);
+    update_ = std::make_unique<Update>(inputRecord_, nullptr);
     update_->setNumAtoms(numAtoms);
 
     doPressureCouple_ = (nstpcouple != 0);
index ec6cff4b785f33cfcf5fbe65a834d763e7fd8ab5..2978665cb49f599bb90c97fdbe0cd50e51120a12 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -70,7 +70,7 @@ namespace gmx
 namespace test
 {
 
-void integrateLeapFrogSimple(LeapFrogTestData* testData, int numSteps)
+void LeapFrogHostTestRunner::integrate(LeapFrogTestData* testData, int numSteps)
 {
     testData->state_.x.resizeWithPadding(testData->numAtoms_);
     testData->state_.v.resizeWithPadding(testData->numAtoms_);
@@ -84,12 +84,12 @@ void integrateLeapFrogSimple(LeapFrogTestData* testData, int numSteps)
 
     for (int step = 0; step < numSteps; step++)
     {
-        update_coords(step, &testData->inputRecord_, &testData->mdAtoms_, &testData->state_,
-                      testData->f_, &testData->forceCalculationData_, &testData->kineticEnergyData_,
-                      testData->velocityScalingMatrix_, testData->update_.get(), etrtNONE, nullptr,
-                      nullptr);
-        finish_update(&testData->inputRecord_, &testData->mdAtoms_, &testData->state_, nullptr,
-                      nullptr, nullptr, testData->update_.get(), nullptr);
+        testData->update_->update_coords(
+                testData->inputRecord_, step, &testData->mdAtoms_, &testData->state_, testData->f_,
+                testData->forceCalculationData_, &testData->kineticEnergyData_,
+                testData->velocityScalingMatrix_, etrtNONE, nullptr, false);
+        testData->update_->finish_update(testData->inputRecord_, &testData->mdAtoms_,
+                                         &testData->state_, nullptr, false);
     }
     auto xp = makeArrayRef(*testData->update_->xp()).subArray(0, testData->numAtoms_);
     for (int i = 0; i < testData->numAtoms_; i++)
@@ -103,14 +103,15 @@ void integrateLeapFrogSimple(LeapFrogTestData* testData, int numSteps)
     }
 }
 
-#if GMX_GPU != GMX_GPU_CUDA
+#if !GMX_GPU_CUDA
 
-void integrateLeapFrogGpu(gmx_unused LeapFrogTestData* testData, gmx_unused int numSteps)
+void LeapFrogDeviceTestRunner::integrate(LeapFrogTestData* /* testData */, int /* numSteps */)
 {
+    GMX_UNUSED_VALUE(testDevice_);
     FAIL() << "Dummy Leap-Frog CUDA function was called instead of the real one.";
 }
 
-#endif // GMX_GPU != GMX_GPU_CUDA
+#endif // !GMX_GPU_CUDA
 
 } // namespace test
 } // namespace gmx
index 34bd6a2685b6bdd848562eb4a5ca24aa17f990ec..7089794f5ae2596f8f821633e5cd5f4ad4cd4280 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,8 +44,6 @@
 
 #include "leapfrogtestrunners.h"
 
-#include "config.h"
-
 #include <assert.h>
 
 #include <cmath>
@@ -55,9 +53,9 @@
 #include <vector>
 
 #include "gromacs/gpu_utils/devicebuffer.cuh"
-#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/hardware/device_information.h"
 #include "gromacs/math/vec.h"
-#include "gromacs/mdlib/leapfrog_cuda.cuh"
+#include "gromacs/mdlib/leapfrog_gpu.cuh"
 #include "gromacs/mdlib/stat.h"
 #include "gromacs/mdtypes/group.h"
 
@@ -66,10 +64,12 @@ namespace gmx
 namespace test
 {
 
-#if GMX_GPU == GMX_GPU_CUDA
-
-void integrateLeapFrogGpu(LeapFrogTestData* testData, int numSteps)
+void LeapFrogDeviceTestRunner::integrate(LeapFrogTestData* testData, int numSteps)
 {
+    const DeviceContext& deviceContext = testDevice_.deviceContext();
+    const DeviceStream&  deviceStream  = testDevice_.deviceStream();
+    setActiveDevice(testDevice_.deviceInfo());
+
     int numAtoms = testData->numAtoms_;
 
     float3* h_x  = reinterpret_cast<float3*>(testData->x_.data());
@@ -79,19 +79,20 @@ void integrateLeapFrogGpu(LeapFrogTestData* testData, int numSteps)
 
     float3 *d_x, *d_xp, *d_v, *d_f;
 
-    allocateDeviceBuffer(&d_x, numAtoms, nullptr);
-    allocateDeviceBuffer(&d_xp, numAtoms, nullptr);
-    allocateDeviceBuffer(&d_v, numAtoms, nullptr);
-    allocateDeviceBuffer(&d_f, numAtoms, nullptr);
+    allocateDeviceBuffer(&d_x, numAtoms, deviceContext);
+    allocateDeviceBuffer(&d_xp, numAtoms, deviceContext);
+    allocateDeviceBuffer(&d_v, numAtoms, deviceContext);
+    allocateDeviceBuffer(&d_f, numAtoms, deviceContext);
 
-    copyToDeviceBuffer(&d_x, h_x, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
-    copyToDeviceBuffer(&d_xp, h_xp, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
-    copyToDeviceBuffer(&d_v, h_v, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
-    copyToDeviceBuffer(&d_f, h_f, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
+    copyToDeviceBuffer(&d_x, h_x, 0, numAtoms, deviceStream, GpuApiCallBehavior::Sync, nullptr);
+    copyToDeviceBuffer(&d_xp, h_xp, 0, numAtoms, deviceStream, GpuApiCallBehavior::Sync, nullptr);
+    copyToDeviceBuffer(&d_v, h_v, 0, numAtoms, deviceStream, GpuApiCallBehavior::Sync, nullptr);
+    copyToDeviceBuffer(&d_f, h_f, 0, numAtoms, deviceStream, GpuApiCallBehavior::Sync, nullptr);
 
-    auto integrator = std::make_unique<LeapFrogCuda>(nullptr);
+    auto integrator = std::make_unique<LeapFrogGpu>(deviceContext, deviceStream);
 
-    integrator->set(testData->mdAtoms_, testData->numTCoupleGroups_, testData->mdAtoms_.cTC);
+    integrator->set(testData->numAtoms_, testData->inverseMasses_.data(),
+                    testData->numTCoupleGroups_, testData->mdAtoms_.cTC);
 
     bool doTempCouple = testData->numTCoupleGroups_ > 0;
     for (int step = 0; step < numSteps; step++)
@@ -105,8 +106,8 @@ void integrateLeapFrogGpu(LeapFrogTestData* testData, int numSteps)
                               testData->dtPressureCouple_, testData->velocityScalingMatrix_);
     }
 
-    copyFromDeviceBuffer(h_xp, &d_x, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
-    copyFromDeviceBuffer(h_v, &d_v, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
+    copyFromDeviceBuffer(h_xp, &d_x, 0, numAtoms, deviceStream, GpuApiCallBehavior::Sync, nullptr);
+    copyFromDeviceBuffer(h_v, &d_v, 0, numAtoms, deviceStream, GpuApiCallBehavior::Sync, nullptr);
 
     freeDeviceBuffer(&d_x);
     freeDeviceBuffer(&d_xp);
@@ -114,7 +115,5 @@ void integrateLeapFrogGpu(LeapFrogTestData* testData, int numSteps)
     freeDeviceBuffer(&d_f);
 }
 
-#endif // GMX_GPU == GMX_GPU_CUDA
-
 } // namespace test
 } // namespace gmx
index a47534e08a35d72a121cadf012212f49f514d956..deac1d0a68bae0404db3c74b201cb8f6b6005264 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \internal \file
- * \brief Declaration of interfaces to run various implementations of integrator (runners)
+ * \brief Leap-Frog tests runners.
+ *
+ * Declares test runner class for Leap-Frog algorithm. The test runners abstract
+ * class is used to unify the interfaces for CPU and GPU implementations of the
+ * Leap-Frog algorithm. This allows to run the same test on the same data using
+ * different implementations of the parent class, that inherit its interfaces.
  *
  * \author Artem Zhmurov <zhmurov@gmail.com>
  * \ingroup module_mdlib
  */
-
 #ifndef GMX_MDLIB_TESTS_LEAPFROGTESTRUNNERS_H
 #define GMX_MDLIB_TESTS_LEAPFROGTESTRUNNERS_H
 
 #include "gromacs/math/vec.h"
 
+#include "testutils/test_device.h"
+
 #include "leapfrogtestdata.h"
 
 namespace gmx
@@ -51,23 +57,81 @@ namespace gmx
 namespace test
 {
 
-/*! \brief Integrate using CPU version of Leap-Frog
+/* \brief LeapFrog integrator test runner interface.
  *
- * \param[in]     testData  Data needed for the integrator
- * \param[in]     numSteps  Total number of steps to run integration for.
+ * Wraps the actual implementation of LeapFrog algorithm into common interface.
  */
-void integrateLeapFrogSimple(LeapFrogTestData* testData, int numSteps);
+class ILeapFrogTestRunner
+{
+public:
+    //! Virtual destructor
+    virtual ~ILeapFrogTestRunner() {}
+    /*! \brief The abstract function that runs the integrator for a given number of steps.
+     *
+     * Should be overriden.
+     *
+     * \param[in]     testData  Data needed for the integrator
+     * \param[in]     numSteps  Total number of steps to run integration for.
+     */
+    virtual void integrate(LeapFrogTestData* testData, int numSteps) = 0;
 
-/*! \brief Integrate using CUDA version of Leap-Frog
- *
- * Copies data from CPU to GPU, integrates the equation of motion
- * for requested number of steps using Leap-Frog algorithm, copies
- * the result back.
- *
- * \param[in]     testData  Data needed for the integrator
- * \param[in]     numSteps  Total number of steps to run integration for.
- */
-void integrateLeapFrogGpu(LeapFrogTestData* testData, int numSteps);
+    /*! \brief Get the human-friendly description of hardware used by the runner.
+     *
+     * \returns String with description of the hardware.
+     */
+    virtual std::string hardwareDescription() = 0;
+};
+
+// Runner for the CPU version of Leap-Frog.
+class LeapFrogHostTestRunner : public ILeapFrogTestRunner
+{
+public:
+    //! Constructor.
+    LeapFrogHostTestRunner() {}
+    /*! \brief Integrate on the CPU for a given number of steps.
+     *
+     * Will update the test data with the integration result.
+     *
+     * \param[in]     testData  Data needed for the integrator
+     * \param[in]     numSteps  Total number of steps to run integration for.
+     */
+    void integrate(LeapFrogTestData* testData, int numSteps) override;
+    /*! \brief Get the hardware description
+     *
+     * \returns "CPU" string.
+     */
+    std::string hardwareDescription() override { return "CPU"; }
+};
+
+// Runner for the CPU version of Leap-Frog.
+class LeapFrogDeviceTestRunner : public ILeapFrogTestRunner
+{
+public:
+    /*! \brief Constructor. Keeps a copy of the hardware context.
+     *
+     * \param[in] testDevice The device hardware context to be used by the runner.
+     */
+    LeapFrogDeviceTestRunner(const TestDevice& testDevice) : testDevice_(testDevice) {}
+    /*! \brief Integrate on the GPU for a given number of steps.
+     *
+     * Copies data from CPU to GPU, integrates the equation of motion
+     * for requested number of steps using Leap-Frog algorithm, copies
+     * the result back.
+     *
+     * \param[in]     testData  Data needed for the integrator
+     * \param[in]     numSteps  Total number of steps to run integration for.
+     */
+    void integrate(LeapFrogTestData* testData, int numSteps) override;
+    /*! \brief Get the hardware description
+     *
+     * \returns A string with GPU description.
+     */
+    std::string hardwareDescription() override { return testDevice_.description(); }
+
+private:
+    //! Test device to be used in the runner.
+    const TestDevice& testDevice_;
+};
 
 } // namespace test
 } // namespace gmx
index a97a13157257ab6014cb58a4c3ae4376e54e94f8..e565b52848700f347d30fa0dbaf5213a092be50f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -80,7 +80,7 @@
 
 #include <gtest/gtest.h>
 
-#include "gromacs/gpu_utils/gpu_testutils.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdtypes/mdatom.h"
@@ -93,6 +93,7 @@
 
 #include "gromacs/mdlib/tests/watersystem.h"
 #include "testutils/refdata.h"
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
 #include "settletestdata.h"
@@ -144,10 +145,6 @@ class SettleTest : public ::testing::TestWithParam<SettleTestParameters>
 public:
     //! PBC setups
     std::unordered_map<std::string, t_pbc> pbcs_;
-    //! Runners (CPU and GPU versions of SETTLE)
-    std::unordered_map<std::string,
-                       void (*)(SettleTestData* testData, const t_pbc pbc, const bool updateVelocities, const bool calcVirial, const std::string& testDescription)>
-            runners_;
     //! Reference data
     TestReferenceData refData_;
     //! Checker for reference data
@@ -169,31 +166,13 @@ public:
 
         // Infinitely small box
         matrix boxNone = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
-        set_pbc(&pbc, epbcNONE, boxNone);
+        set_pbc(&pbc, PbcType::No, boxNone);
         pbcs_["PBCNone"] = pbc;
 
         // Rectangular box
         matrix boxXyz = { { real(1.86206), 0, 0 }, { 0, real(1.86206), 0 }, { 0, 0, real(1.86206) } };
-        set_pbc(&pbc, epbcXYZ, boxXyz);
+        set_pbc(&pbc, PbcType::Xyz, boxXyz);
         pbcs_["PBCXYZ"] = pbc;
-
-        //
-        // All SETTLE runners should be registered here under appropriate conditions
-        //
-        runners_["SETTLE"] = applySettle;
-
-        // CUDA version will be tested only if:
-        // 1. The code was compiled with CUDA
-        // 2. There is a CUDA-capable GPU in a system
-        // 3. This GPU is detectable
-        // 4. GPU detection was not disabled by GMX_DISABLE_GPU_DETECTION environment variable
-        if (s_hasCompatibleGpus)
-        {
-            if (GMX_GPU == GMX_GPU_CUDA && s_hasCompatibleGpus)
-            {
-                runners_["SETTLE_GPU"] = applySettleGpu;
-            }
-        }
     }
 
     /*! \brief Check if the final interatomic distances are equal to target set by constraints.
@@ -202,9 +181,9 @@ public:
      * \param[in]  tolerance         Tolerance to compare floating point numbers.
      * \param[in]  testData          An object, containing all the data structures needed by SETTLE.
      */
-    void checkConstrainsSatisfied(const int                    numSettles,
-                                  const FloatingPointTolerance tolerance,
-                                  const SettleTestData&        testData)
+    static void checkConstrainsSatisfied(const int                    numSettles,
+                                         const FloatingPointTolerance tolerance,
+                                         const SettleTestData&        testData)
     {
         for (int i = 0; i < numSettles; ++i)
         {
@@ -234,9 +213,9 @@ public:
      * \param[in]  tolerance         Tolerance to compare floating point numbers.
      * \param[in]  testData          An object, containing all the data structures needed by SETTLE.
      */
-    void checkVirialSymmetric(const bool                   calcVirial,
-                              const FloatingPointTolerance tolerance,
-                              const SettleTestData&        testData)
+    static void checkVirialSymmetric(const bool                   calcVirial,
+                                     const FloatingPointTolerance tolerance,
+                                     const SettleTestData&        testData)
     {
         for (int d = 0; d < DIM; ++d)
         {
@@ -325,22 +304,24 @@ public:
         virialRef.checkReal(virial[ZZ][YY], "ZY");
         virialRef.checkReal(virial[ZZ][ZZ], "ZZ");
     }
-
-    //! Store whether any compatible GPUs exist.
-    static bool s_hasCompatibleGpus;
-    //! Before any test is run, work out whether any compatible GPUs exist.
-    static void SetUpTestCase() { s_hasCompatibleGpus = canComputeOnGpu(); }
 };
 
-bool SettleTest::s_hasCompatibleGpus = false;
-
 TEST_P(SettleTest, SatisfiesConstraints)
 {
-    // Cycle through all available runners
-    for (const auto& runner : runners_)
+    // Construct the list of runners
+    std::vector<std::unique_ptr<ISettleTestRunner>> runners;
+    // Add runners for CPU version
+    runners.emplace_back(std::make_unique<SettleHostTestRunner>());
+    // If using CUDA, add runners for the GPU version for each available GPU
+    if (GMX_GPU_CUDA)
+    {
+        for (const auto& testDevice : getTestHardwareEnvironment()->getTestDeviceList())
+        {
+            runners.emplace_back(std::make_unique<SettleDeviceTestRunner>(*testDevice));
+        }
+    }
+    for (const auto& runner : runners)
     {
-        std::string runnerName = runner.first;
-
         // Make some symbolic names for the parameter combination.
         SettleTestParameters params = GetParam();
 
@@ -354,7 +335,7 @@ TEST_P(SettleTest, SatisfiesConstraints)
         // being tested, to help make failing tests comprehensible.
         std::string testDescription = formatString(
                 "Testing %s with %d SETTLEs, %s, %svelocities and %scalculating the virial.",
-                runnerName.c_str(), numSettles, pbcName.c_str(),
+                runner->hardwareDescription().c_str(), numSettles, pbcName.c_str(),
                 updateVelocities ? "with " : "without ", calcVirial ? "" : "not ");
 
         SCOPED_TRACE(testDescription);
@@ -367,7 +348,7 @@ TEST_P(SettleTest, SatisfiesConstraints)
         t_pbc pbc = pbcs_.at(pbcName);
 
         // Apply SETTLE
-        runner.second(testData.get(), pbc, updateVelocities, calcVirial, testDescription);
+        runner->applySettle(testData.get(), pbc, updateVelocities, calcVirial, testDescription);
 
         // The necessary tolerances for the test to pass were determined
         // empirically. This isn't nice, but the required behavior that
index 8feab2e0a02b2249dce77ffefc02049c309e24c3..158db19fe7442c0323d86e421fc0767405d20f03 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -73,6 +73,7 @@ namespace test
 {
 
 SettleTestData::SettleTestData(int numSettles) :
+    numSettles_(numSettles),
     x_(c_waterPositions.size()),
     xPrime_(c_waterPositions.size()),
     v_(c_waterPositions.size())
@@ -120,35 +121,29 @@ SettleTestData::SettleTestData(int numSettles) :
     // Set up the masses.
     mtop_.moltype[0].atoms.atom =
             static_cast<t_atom*>(calloc(numSettles * atomsPerSettle_, sizeof(t_atom)));
-    mdatoms_.homenr  = numSettles * atomsPerSettle_;
-    mdatoms_.massT   = static_cast<real*>(calloc(mdatoms_.homenr, sizeof(real)));
-    mdatoms_.invmass = static_cast<real*>(calloc(mdatoms_.homenr, sizeof(real)));
+    numAtoms_ = numSettles * atomsPerSettle_;
+    masses_.resize(numAtoms_);
+    inverseMasses_.resize(numAtoms_);
     for (int i = 0; i < numSettles; ++i)
     {
-        mdatoms_.massT[i * atomsPerSettle_ + 0] = oxygenMass_;
-        mdatoms_.massT[i * atomsPerSettle_ + 1] = hydrogenMass_;
-        mdatoms_.massT[i * atomsPerSettle_ + 2] = hydrogenMass_;
+        masses_[i * atomsPerSettle_ + 0] = oxygenMass_;
+        masses_[i * atomsPerSettle_ + 1] = hydrogenMass_;
+        masses_[i * atomsPerSettle_ + 2] = hydrogenMass_;
 
-        mdatoms_.invmass[i * atomsPerSettle_ + 0] = 1.0 / oxygenMass_;
-        mdatoms_.invmass[i * atomsPerSettle_ + 1] = 1.0 / hydrogenMass_;
-        mdatoms_.invmass[i * atomsPerSettle_ + 2] = 1.0 / hydrogenMass_;
+        inverseMasses_[i * atomsPerSettle_ + 0] = 1.0 / oxygenMass_;
+        inverseMasses_[i * atomsPerSettle_ + 1] = 1.0 / hydrogenMass_;
+        inverseMasses_[i * atomsPerSettle_ + 2] = 1.0 / hydrogenMass_;
 
         mtop_.moltype[0].atoms.atom[i * atomsPerSettle_ + 0].m = oxygenMass_;
         mtop_.moltype[0].atoms.atom[i * atomsPerSettle_ + 1].m = hydrogenMass_;
         mtop_.moltype[0].atoms.atom[i * atomsPerSettle_ + 2].m = hydrogenMass_;
     }
 
-    // Reshape some data so it can be directly used by the SETTLE constraints
-    ilist_             = { mtop_.moltype[0].ilist[F_SETTLE].size(), 0,
-               mtop_.moltype[0].ilist[F_SETTLE].iatoms.data(), 0 };
-    idef_.il[F_SETTLE] = ilist_;
+    idef_               = std::make_unique<InteractionDefinitions>(mtop_.ffparams);
+    idef_->il[F_SETTLE] = mtop_.moltype[0].ilist[F_SETTLE];
 }
 
-SettleTestData::~SettleTestData()
-{
-    free(mdatoms_.massT);
-    free(mdatoms_.invmass);
-}
+SettleTestData::~SettleTestData() {}
 
 } // namespace test
 } // namespace gmx
index ad07c6501b6a1279d8d6d189b112bfc2d488494c..45d1c946b898d21f8a1834e60df71bed5906c37a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,7 +45,6 @@
 
 #include "gromacs/math/paddedvector.h"
 #include "gromacs/math/vectypes.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/idef.h"
 #include "gromacs/topology/topology.h"
@@ -63,6 +62,8 @@ namespace test
 class SettleTestData
 {
 public:
+    //! Number of settles
+    int numSettles_;
     //! Initial (undisturbed) positions
     PaddedVector<gmx::RVec> x_;
     //! Updated water atom positions to constrain
@@ -74,12 +75,14 @@ public:
 
     //! Global topology
     gmx_mtop_t mtop_;
-    //! Atoms data
-    t_mdatoms mdatoms_;
-    //! Interactions list
-    t_ilist ilist_;
+    //! Number of atoms
+    int numAtoms_ = 0;
+    //! Atom masses
+    std::vector<real> masses_;
+    //! Reciprocal masses
+    std::vector<real> inverseMasses_;
     //! Local topology
-    t_idef idef_;
+    std::unique_ptr<InteractionDefinitions> idef_;
 
     //! Inverse timestep
     const real reciprocalTimeStep_ = 1.0 / 0.002;
index 88a4c1774e74d111114b5885c6976a8a643c518b..cfc2b0ca20ae3142340d0227573141337487ebb7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -57,36 +57,36 @@ namespace gmx
 namespace test
 {
 
-void applySettle(SettleTestData*    testData,
-                 const t_pbc        pbc,
-                 const bool         updateVelocities,
-                 const bool         calcVirial,
-                 const std::string& testDescription)
+void SettleHostTestRunner::applySettle(SettleTestData*    testData,
+                                       const t_pbc        pbc,
+                                       const bool         updateVelocities,
+                                       const bool         calcVirial,
+                                       const std::string& testDescription)
 {
-    settledata* settled = settle_init(testData->mtop_);
+    SettleData settled(testData->mtop_);
 
-    settle_set_constraints(settled, &testData->ilist_, testData->mdatoms_);
+    settled.setConstraints(testData->idef_->il[F_SETTLE], testData->numAtoms_,
+                           testData->masses_.data(), testData->inverseMasses_.data());
 
     bool errorOccured;
     int  numThreads  = 1;
     int  threadIndex = 0;
-    csettle(settled, numThreads, threadIndex, &pbc,
-            reinterpret_cast<real*>(as_rvec_array(testData->x_.data())),
-            reinterpret_cast<real*>(as_rvec_array(testData->xPrime_.data())), testData->reciprocalTimeStep_,
-            updateVelocities ? reinterpret_cast<real*>(as_rvec_array(testData->v_.data())) : nullptr,
+    csettle(settled, numThreads, threadIndex, &pbc, testData->x_.arrayRefWithPadding(),
+            testData->xPrime_.arrayRefWithPadding(), testData->reciprocalTimeStep_,
+            updateVelocities ? testData->v_.arrayRefWithPadding() : ArrayRefWithPadding<RVec>(),
             calcVirial, testData->virial_, &errorOccured);
-    settle_free(settled);
     EXPECT_FALSE(errorOccured) << testDescription;
 }
 
-#if GMX_GPU != GMX_GPU_CUDA
+#if !GMX_GPU_CUDA
 
-void applySettleGpu(gmx_unused SettleTestData* testData,
-                    gmx_unused const t_pbc pbc,
-                    gmx_unused const bool  updateVelocities,
-                    gmx_unused const bool  calcVirial,
-                    gmx_unused const std::string& testDescription)
+void SettleDeviceTestRunner::applySettle(SettleTestData* /* testData */,
+                                         const t_pbc /* pbc */,
+                                         const bool /* updateVelocities */,
+                                         const bool /* calcVirial */,
+                                         const std::string& /* testDescription */)
 {
+    GMX_UNUSED_VALUE(testDevice_);
     FAIL() << "Dummy SETTLE GPU function was called instead of the real one in the SETTLE test.";
 }
 
index e75466af86ac5b247a502df0e12d4bbf726e8199..1200626ad07dc69cfe97eb3cc8a60b9e65f5048f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <vector>
 
 #include "gromacs/gpu_utils/devicebuffer.cuh"
-#include "gromacs/gpu_utils/gpu_utils.h"
-#include "gromacs/mdlib/settle_cuda.cuh"
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/mdlib/settle_gpu.cuh"
 #include "gromacs/utility/unique_cptr.h"
 
+#include "testutils/test_device.h"
+
 namespace gmx
 {
 namespace test
 {
 
-/*! \brief Apply SETTLE using GPU version of the algorithm.
- *
- * Initializes SETTLE object, copied data to the GPU, applies algorithm, copies the data back,
- * destroys the object. The coordinates, velocities and virial are updated in the testData object.
- *
- * \param[in,out] testData          An object, containing all the data structures needed by SETTLE.
- * \param[in]     pbc               Periodic boundary setup.
- * \param[in]     updateVelocities  If the velocities should be updated.
- * \param[in]     calcVirial        If the virial should be computed.
- * \param[in]     testDescription   Brief description that will be printed in case of test failure.
- */
-void applySettleGpu(SettleTestData*  testData,
-                    const t_pbc      pbc,
-                    const bool       updateVelocities,
-                    const bool       calcVirial,
-                    gmx_unused const std::string& testDescription)
+void SettleDeviceTestRunner::applySettle(SettleTestData* testData,
+                                         const t_pbc     pbc,
+                                         const bool      updateVelocities,
+                                         const bool      calcVirial,
+                                         const std::string& /* testDescription */)
 {
     // These should never fail since this function should only be called if CUDA is enabled and
     // there is a CUDA-capable device available.
-    GMX_RELEASE_ASSERT(GMX_GPU == GMX_GPU_CUDA,
-                       "CUDA version of SETTLE was called from non-CUDA build.");
+    GMX_RELEASE_ASSERT(GMX_GPU_CUDA, "CUDA version of SETTLE was called from non-CUDA build.");
+
+    const DeviceContext& deviceContext = testDevice_.deviceContext();
+    const DeviceStream&  deviceStream  = testDevice_.deviceStream();
+    setActiveDevice(testDevice_.deviceInfo());
 
-    // TODO: Here we should check that at least 1 suitable GPU is available
-    GMX_RELEASE_ASSERT(canPerformGpuDetection(), "Can't detect CUDA-capable GPUs.");
+    auto settleGpu = std::make_unique<SettleGpu>(testData->mtop_, deviceContext, deviceStream);
 
-    auto settleCuda = std::make_unique<SettleCuda>(testData->mtop_, nullptr);
-    settleCuda->setPbc(&pbc);
-    settleCuda->set(testData->idef_, testData->mdatoms_);
+    settleGpu->set(*testData->idef_);
+    PbcAiuc pbcAiuc;
+    setPbcAiuc(pbc.ndim_ePBC, pbc.box, &pbcAiuc);
 
-    int numAtoms = testData->mdatoms_.homenr;
+    int numAtoms = testData->numAtoms_;
 
     float3 *d_x, *d_xp, *d_v;
 
@@ -98,23 +91,24 @@ void applySettleGpu(SettleTestData*  testData,
     float3* h_xp = (float3*)(as_rvec_array(testData->xPrime_.data()));
     float3* h_v  = (float3*)(as_rvec_array(testData->v_.data()));
 
-    allocateDeviceBuffer(&d_x, numAtoms, nullptr);
-    allocateDeviceBuffer(&d_xp, numAtoms, nullptr);
-    allocateDeviceBuffer(&d_v, numAtoms, nullptr);
+    allocateDeviceBuffer(&d_x, numAtoms, deviceContext);
+    allocateDeviceBuffer(&d_xp, numAtoms, deviceContext);
+    allocateDeviceBuffer(&d_v, numAtoms, deviceContext);
 
-    copyToDeviceBuffer(&d_x, (float3*)h_x, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
-    copyToDeviceBuffer(&d_xp, (float3*)h_xp, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
+    copyToDeviceBuffer(&d_x, (float3*)h_x, 0, numAtoms, deviceStream, GpuApiCallBehavior::Sync, nullptr);
+    copyToDeviceBuffer(&d_xp, (float3*)h_xp, 0, numAtoms, deviceStream, GpuApiCallBehavior::Sync, nullptr);
     if (updateVelocities)
     {
-        copyToDeviceBuffer(&d_v, (float3*)h_v, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
+        copyToDeviceBuffer(&d_v, (float3*)h_v, 0, numAtoms, deviceStream, GpuApiCallBehavior::Sync, nullptr);
     }
-    settleCuda->apply(d_x, d_xp, updateVelocities, d_v, testData->reciprocalTimeStep_, calcVirial,
-                      testData->virial_);
+    settleGpu->apply(d_x, d_xp, updateVelocities, d_v, testData->reciprocalTimeStep_, calcVirial,
+                     testData->virial_, pbcAiuc);
 
-    copyFromDeviceBuffer((float3*)h_xp, &d_xp, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
+    copyFromDeviceBuffer((float3*)h_xp, &d_xp, 0, numAtoms, deviceStream, GpuApiCallBehavior::Sync, nullptr);
     if (updateVelocities)
     {
-        copyFromDeviceBuffer((float3*)h_v, &d_v, 0, numAtoms, nullptr, GpuApiCallBehavior::Sync, nullptr);
+        copyFromDeviceBuffer((float3*)h_v, &d_v, 0, numAtoms, deviceStream,
+                             GpuApiCallBehavior::Sync, nullptr);
     }
 
     freeDeviceBuffer(&d_x);
index 08761213755ed4ace4d612a43d4213bb7d5c2eba..3f643f605e30b7c7c4ea62a74212e9ff2e857613 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \internal \file
- * \brief Declaration of the SETTLE tests runners.
+ * \brief SETTLE tests runners.
  *
- * Declares the functions that do the buffer management and apply
- * SETTLE constraints ("test runners").
+ * Declares test runner class for SETTLE algorithm. The test runners abstract
+ * class is used to unify the interfaces for CPU and GPU implementations of the
+ * SETTLE algorithm. This allows to run the same test on the same data using
+ * different implementations of the parent class, that inherit its interfaces.
  *
  * \author Artem Zhmurov <zhmurov@gmail.com>
  * \ingroup module_mdlib
  */
-
 #ifndef GMX_MDLIB_TESTS_SETTLETESTRUNNERS_H
 #define GMX_MDLIB_TESTS_SETTLETESTRUNNERS_H
 
+#include <gtest/gtest.h>
+
+#include "testutils/test_device.h"
+
 #include "settletestdata.h"
 
 struct t_pbc;
@@ -54,39 +59,103 @@ namespace gmx
 namespace test
 {
 
-/*! \brief Apply SETTLE using CPU version of the algorithm
- *
- * Initializes SETTLE object, applies algorithm, destroys the object. The coordinates, velocities
- * and virial are updated in the testData object.
+/* \brief SETTLE test runner interface.
  *
- * \param[in,out] testData          An object, containing all the data structures needed by SETTLE.
- * \param[in]     pbc               Periodic boundary setup.
- * \param[in]     updateVelocities  If the velocities should be updated.
- * \param[in]     calcVirial        If the virial should be computed.
- * \param[in]     testDescription   Brief description that will be printed in case of test failure.
+ * Wraps the actual implementation of SETTLE into common interface.
  */
-void applySettle(SettleTestData*    testData,
-                 t_pbc              pbc,
-                 bool               updateVelocities,
-                 bool               calcVirial,
-                 const std::string& testDescription);
+class ISettleTestRunner
+{
+public:
+    //! Virtual destructor.
+    virtual ~ISettleTestRunner() {}
 
-/*! \brief Apply SETTLE using GPU version of the algorithm
- *
- * Initializes SETTLE object, copied data to the GPU, applies algorithm, copies the data back,
- * destroys the object. The coordinates, velocities and virial are updated in the testData object.
- *
- * \param[in,out] testData          An object, containing all the data structures needed by SETTLE.
- * \param[in]     pbc               Periodic boundary setup.
- * \param[in]     updateVelocities  If the velocities should be updated.
- * \param[in]     calcVirial        If the virial should be computed.
- * \param[in]     testDescription   Brief description that will be printed in case of test failure.
- */
-void applySettleGpu(SettleTestData*    testData,
-                    t_pbc              pbc,
-                    bool               updateVelocities,
-                    bool               calcVirial,
-                    const std::string& testDescription);
+    /*! \brief Apply SETTLE using CPU version of the algorithm
+     *
+     * Initializes SETTLE object, applies algorithm, destroys the object. The coordinates, velocities
+     * and virial are updated in the testData object.
+     *
+     * \param[in,out] testData          An object, containing all the data structures needed by SETTLE.
+     * \param[in]     pbc               Periodic boundary setup.
+     * \param[in]     updateVelocities  If the velocities should be updated.
+     * \param[in]     calcVirial        If the virial should be computed.
+     * \param[in]     testDescription   Brief description that will be printed in case of test failure.
+     */
+    virtual void applySettle(SettleTestData*    testData,
+                             t_pbc              pbc,
+                             bool               updateVelocities,
+                             bool               calcVirial,
+                             const std::string& testDescription) = 0;
+    /*! \brief Get the hardware description
+     *
+     * \returns A string, describing hardware used by the runner.
+     */
+    virtual std::string hardwareDescription() = 0;
+};
+
+// Runner for the CPU implementation of SETTLE.
+class SettleHostTestRunner : public ISettleTestRunner
+{
+public:
+    //! Default constructor.
+    SettleHostTestRunner() {}
+    /*! \brief Apply SETTLE using CPU version of the algorithm
+     *
+     * Initializes SETTLE object, applies algorithm, destroys the object. The coordinates, velocities
+     * and virial are updated in the testData object.
+     *
+     * \param[in,out] testData          An object, containing all the data structures needed by SETTLE.
+     * \param[in]     pbc               Periodic boundary setup.
+     * \param[in]     updateVelocities  If the velocities should be updated.
+     * \param[in]     calcVirial        If the virial should be computed.
+     * \param[in]     testDescription   Brief description that will be printed in case of test failure.
+     */
+    void applySettle(SettleTestData*    testData,
+                     t_pbc              pbc,
+                     bool               updateVelocities,
+                     bool               calcVirial,
+                     const std::string& testDescription) override;
+    /*! \brief Get the hardware description
+     *
+     * \returns "CPU" string.
+     */
+    std::string hardwareDescription() override { return "CPU"; }
+};
+
+// Runner for the GPU implementation of SETTLE.
+class SettleDeviceTestRunner : public ISettleTestRunner
+{
+public:
+    /*! \brief Constructor. Keeps a copy of the hardware context.
+     *
+     * \param[in] testDevice The device hardware context to be used by the runner.
+     */
+    SettleDeviceTestRunner(const TestDevice& testDevice) : testDevice_(testDevice) {}
+    /*! \brief Apply SETTLE using GPU version of the algorithm
+     *
+     * Initializes SETTLE object, copied data to the GPU, applies algorithm, copies the data back,
+     * destroys the object. The coordinates, velocities and virial are updated in the testData object.
+     *
+     * \param[in,out] testData          An object, containing all the data structures needed by SETTLE.
+     * \param[in]     pbc               Periodic boundary setup.
+     * \param[in]     updateVelocities  If the velocities should be updated.
+     * \param[in]     calcVirial        If the virial should be computed.
+     * \param[in]     testDescription   Brief description that will be printed in case of test failure.
+     */
+    void applySettle(SettleTestData*    testData,
+                     t_pbc              pbc,
+                     bool               updateVelocities,
+                     bool               calcVirial,
+                     const std::string& testDescription) override;
+    /*! \brief Get the hardware description
+     *
+     * \returns A string with GPU description.
+     */
+    std::string hardwareDescription() override { return testDevice_.description(); }
+
+private:
+    //! Test test device to be used in the runner.
+    const TestDevice& testDevice_;
+};
 
 } // namespace test
 } // namespace gmx
index 44e1ec8dd29f0552f3a63f6c8e4e4f319cf6e0f9..4d29a38f918463e1e1e34f77a84e775897f94211 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,6 +45,8 @@
 
 #include <gtest/gtest.h>
 
+#include "gromacs/utility/arrayref.h"
+
 #include "testutils/refdata.h"
 #include "testutils/testasserts.h"
 
@@ -65,21 +67,18 @@ const int constraintStride = 3;
 
 /*! \brief Compute the displacements between pairs of constrained
  * atoms described in the iatom "topology". */
-std::vector<real> computeDisplacements(const std::vector<int>& iatom, const std::vector<real>& positions)
+std::vector<RVec> computeDisplacements(ArrayRef<const int> iatom, const std::vector<RVec>& positions)
 {
     assert(0 == iatom.size() % constraintStride);
     int               numConstraints = iatom.size() / constraintStride;
-    std::vector<real> displacements;
+    std::vector<RVec> displacements;
 
     for (int ll = 0; ll != numConstraints; ++ll)
     {
         int atom_i = iatom[ll * constraintStride + 1];
         int atom_j = iatom[ll * constraintStride + 2];
 
-        for (int d = 0; d != DIM; d++)
-        {
-            displacements.push_back(positions[atom_i * DIM + d] - positions[atom_j * DIM + d]);
-        }
+        displacements.push_back(positions[atom_i] - positions[atom_j]);
     }
 
     return displacements;
@@ -107,10 +106,9 @@ std::vector<real> computeHalfOfReducedMasses(const std::vector<int>&  iatom,
 }
 
 /*! \brief Compute the distances corresponding to the vector of displacements components */
-std::vector<real> computeDistancesSquared(const std::vector<real>& displacements)
+std::vector<real> computeDistancesSquared(ArrayRef<const RVec> displacements)
 {
-    assert(0 == displacements.size() % DIM);
-    int               numDistancesSquared = displacements.size() / DIM;
+    int               numDistancesSquared = displacements.size();
     std::vector<real> distanceSquared;
 
     for (int i = 0; i != numDistancesSquared; ++i)
@@ -118,7 +116,7 @@ std::vector<real> computeDistancesSquared(const std::vector<real>& displacements
         distanceSquared.push_back(0.0);
         for (int d = 0; d != DIM; ++d)
         {
-            real displacement = displacements[i * DIM + d];
+            real displacement = displacements[i][d];
             distanceSquared.back() += displacement * displacement;
         }
     }
@@ -139,36 +137,28 @@ public:
         inverseMassesDatabase_.push_back(4.0);
         inverseMassesDatabase_.push_back(1.0);
 
-        positionsDatabase_.push_back(2.5);
-        positionsDatabase_.push_back(-3.1);
-        positionsDatabase_.push_back(15.7);
+        positionsDatabase_.emplace_back(2.5, -3.1, 15.7);
 
-        positionsDatabase_.push_back(0.51);
-        positionsDatabase_.push_back(-3.02);
-        positionsDatabase_.push_back(15.55);
+        positionsDatabase_.emplace_back(0.51, -3.02, 15.55);
 
-        positionsDatabase_.push_back(-0.5);
-        positionsDatabase_.push_back(-3.0);
-        positionsDatabase_.push_back(15.2);
+        positionsDatabase_.emplace_back(-0.5, -3.0, 15.2);
 
-        positionsDatabase_.push_back(-1.51);
-        positionsDatabase_.push_back(-2.95);
-        positionsDatabase_.push_back(15.05);
+        positionsDatabase_.emplace_back(-1.51, -2.95, 15.05);
     }
 
     //! Run the test
-    void runTest(size_t gmx_unused        numAtoms,
-                 size_t                   numConstraints,
-                 const std::vector<int>&  iatom,
-                 const std::vector<real>& constrainedDistances,
-                 const std::vector<real>& inverseMasses,
-                 const std::vector<real>& positions)
+    static void runTest(size_t gmx_unused        numAtoms,
+                        size_t                   numConstraints,
+                        const std::vector<int>&  iatom,
+                        const std::vector<real>& constrainedDistances,
+                        const std::vector<real>& inverseMasses,
+                        const std::vector<RVec>& positions)
     {
         // Check the test input is consistent
         assert(numConstraints * constraintStride == iatom.size());
         assert(numConstraints == constrainedDistances.size());
         assert(numAtoms == inverseMasses.size());
-        assert(numAtoms * DIM == positions.size());
+        assert(numAtoms == positions.size());
         for (size_t i = 0; i != numConstraints; ++i)
         {
             for (size_t j = 1; j < 3; j++)
@@ -194,24 +184,23 @@ public:
             {
                 for (int d = 0; d < DIM; d++)
                 {
-                    coordMax = std::max(
-                            coordMax, std::abs(positions[iatom[i * constraintStride + j] * DIM + d]));
+                    coordMax = std::max(coordMax, std::abs(positions[iatom[i * constraintStride + j]][d]));
                 }
             }
         }
         std::vector<real> halfOfReducedMasses  = computeHalfOfReducedMasses(iatom, inverseMasses);
-        std::vector<real> initialDisplacements = computeDisplacements(iatom, positions);
+        std::vector<RVec> initialDisplacements = computeDisplacements(iatom, positions);
 
-        std::vector<real> finalPositions = positions;
+        std::vector<RVec> finalPositions = positions;
         int               numIterations  = 0;
         int               numErrors      = 0;
 
         cshake(iatom.data(), numConstraints, &numIterations, ShakeTest::maxNumIterations_,
-               constrainedDistancesSquared.data(), finalPositions.data(),
-               initialDisplacements.data(), halfOfReducedMasses.data(), omega_, inverseMasses.data(),
-               distanceSquaredTolerances.data(), lagrangianValues.data(), &numErrors);
+               constrainedDistancesSquared, finalPositions, nullptr, initialDisplacements,
+               halfOfReducedMasses, omega_, inverseMasses.data(), distanceSquaredTolerances,
+               lagrangianValues, &numErrors);
 
-        std::vector<real> finalDisplacements    = computeDisplacements(iatom, finalPositions);
+        std::vector<RVec> finalDisplacements    = computeDisplacements(iatom, finalPositions);
         std::vector<real> finalDistancesSquared = computeDistancesSquared(finalDisplacements);
         assert(numConstraints == finalDistancesSquared.size());
 
@@ -242,7 +231,7 @@ public:
     //! Database of inverse masses of atoms in the topology
     std::vector<real> inverseMassesDatabase_;
     //! Database of atom positions (three reals per atom)
-    std::vector<real> positionsDatabase_;
+    std::vector<RVec> positionsDatabase_;
 };
 
 const real ShakeTest::tolerance_        = 1e-5;
@@ -264,7 +253,7 @@ TEST_F(ShakeTest, ConstrainsOneBond)
 
     std::vector<real> inverseMasses(inverseMassesDatabase_.begin(),
                                     inverseMassesDatabase_.begin() + numAtoms);
-    std::vector<real> positions(positionsDatabase_.begin(), positionsDatabase_.begin() + numAtoms * DIM);
+    std::vector<RVec> positions(positionsDatabase_.begin(), positionsDatabase_.begin() + numAtoms);
 
     runTest(numAtoms, numConstraints, iatom, constrainedDistances, inverseMasses, positions);
 }
@@ -289,7 +278,7 @@ TEST_F(ShakeTest, ConstrainsTwoDisjointBonds)
 
     std::vector<real> inverseMasses(inverseMassesDatabase_.begin(),
                                     inverseMassesDatabase_.begin() + numAtoms);
-    std::vector<real> positions(positionsDatabase_.begin(), positionsDatabase_.begin() + numAtoms * DIM);
+    std::vector<RVec> positions(positionsDatabase_.begin(), positionsDatabase_.begin() + numAtoms);
 
     runTest(numAtoms, numConstraints, iatom, constrainedDistances, inverseMasses, positions);
 }
@@ -314,7 +303,7 @@ TEST_F(ShakeTest, ConstrainsTwoBondsWithACommonAtom)
 
     std::vector<real> inverseMasses(inverseMassesDatabase_.begin(),
                                     inverseMassesDatabase_.begin() + numAtoms);
-    std::vector<real> positions(positionsDatabase_.begin(), positionsDatabase_.begin() + numAtoms * DIM);
+    std::vector<RVec> positions(positionsDatabase_.begin(), positionsDatabase_.begin() + numAtoms);
 
     runTest(numAtoms, numConstraints, iatom, constrainedDistances, inverseMasses, positions);
 }
@@ -344,7 +333,7 @@ TEST_F(ShakeTest, ConstrainsThreeBondsWithCommonAtoms)
 
     std::vector<real> inverseMasses(inverseMassesDatabase_.begin(),
                                     inverseMassesDatabase_.begin() + numAtoms);
-    std::vector<real> positions(positionsDatabase_.begin(), positionsDatabase_.begin() + numAtoms * DIM);
+    std::vector<RVec> positions(positionsDatabase_.begin(), positionsDatabase_.begin() + numAtoms);
 
     runTest(numAtoms, numConstraints, iatom, constrainedDistances, inverseMasses, positions);
 }
index ab21b65fd4281dffd2dfee88c65cf13d4c7a6cea..b77e92b359ebfd52b89b8c3c0119b172933ca4c9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -98,7 +98,7 @@ TEST(UpdateGroupsCog, ComputesCogs)
     mtop.molblock[0].type = 0;
     mtop.molblock[0].nmol = numMolecules;
     mtop.natoms           = numAtoms;
-    gmx_mtop_finalize(&mtop);
+    mtop.finalize();
 
     // Set up the SETTLE parameters.
     const real dOH = 0.1;
index 22cb5fa1f7927db443a5123a3f2d980a86b387c6..3505a1a1ea1b53865388a55cadcb07d1c788d00a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,9 +44,9 @@
 
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/mdlib/coupling.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdlib/rbin.h"
-#include "gromacs/mdlib/update.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/mdatom.h"
index 3d99d2353dbc6f8dc236158aa0e63ed0e7024801..f0f9f25fdab8ffac7e4e13e6184a841fede8865b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017, The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +44,7 @@
 #include "gromacs/mdlib/mdoutf.h"
 #include "gromacs/mdlib/stat.h"
 #include "gromacs/mdlib/update.h"
+#include "gromacs/mdtypes/checkpointdata.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/smalloc.h"
 
-void do_md_trajectory_writing(FILE*                    fplog,
-                              t_commrec*               cr,
-                              int                      nfile,
-                              const t_filenm           fnm[],
-                              int64_t                  step,
-                              int64_t                  step_rel,
-                              double                   t,
-                              t_inputrec*              ir,
-                              t_state*                 state,
-                              t_state*                 state_global,
-                              ObservablesHistory*      observablesHistory,
-                              const gmx_mtop_t*        top_global,
-                              t_forcerec*              fr,
-                              gmx_mdoutf_t             outf,
-                              const gmx::EnergyOutput& energyOutput,
-                              gmx_ekindata_t*          ekind,
-                              gmx::ArrayRef<gmx::RVec> f,
-                              gmx_bool                 bCPT,
-                              gmx_bool                 bRerunMD,
-                              gmx_bool                 bLastStep,
-                              gmx_bool                 bDoConfOut,
-                              gmx_bool                 bSumEkinhOld)
+void do_md_trajectory_writing(FILE*                          fplog,
+                              t_commrec*                     cr,
+                              int                            nfile,
+                              const t_filenm                 fnm[],
+                              int64_t                        step,
+                              int64_t                        step_rel,
+                              double                         t,
+                              t_inputrec*                    ir,
+                              t_state*                       state,
+                              t_state*                       state_global,
+                              ObservablesHistory*            observablesHistory,
+                              const gmx_mtop_t*              top_global,
+                              t_forcerec*                    fr,
+                              gmx_mdoutf_t                   outf,
+                              const gmx::EnergyOutput&       energyOutput,
+                              gmx_ekindata_t*                ekind,
+                              gmx::ArrayRef<const gmx::RVec> f,
+                              gmx_bool                       bCPT,
+                              gmx_bool                       bRerunMD,
+                              gmx_bool                       bLastStep,
+                              gmx_bool                       bDoConfOut,
+                              gmx_bool                       bSumEkinhOld)
 {
     int   mdof_flags;
     rvec* x_for_confout = nullptr;
@@ -137,11 +139,15 @@ void do_md_trajectory_writing(FILE*                    fplog,
                 energyOutput.fillEnergyHistory(observablesHistory->energyHistory.get());
             }
         }
+        // The current function is only called by legacy code, while
+        // mdoutf_write_to_trajectory_files is also called from modular simulator. Create a dummy
+        // modular simulator checkpointing object for compatibility.
+        gmx::WriteCheckpointDataHolder checkpointDataHolder;
         // Note that part of the following code is duplicated in StatePropagatorData::trajectoryWriterTeardown.
         // This duplication is needed while both legacy and modular code paths are in use.
         // TODO: Remove duplication asap, make sure to keep in sync in the meantime.
-        mdoutf_write_to_trajectory_files(fplog, cr, outf, mdof_flags, top_global->natoms, step, t,
-                                         state, state_global, observablesHistory, f);
+        mdoutf_write_to_trajectory_files(fplog, cr, outf, mdof_flags, top_global->natoms, step, t, state,
+                                         state_global, observablesHistory, f, &checkpointDataHolder);
         if (bLastStep && step_rel == ir->nsteps && bDoConfOut && MASTER(cr) && !bRerunMD)
         {
             if (fr->bMolPBC && state == state_global)
@@ -171,10 +177,10 @@ void do_md_trajectory_writing(FILE*                    fplog,
             if (fr->bMolPBC && !ir->bPeriodicMols)
             {
                 /* Make molecules whole only for confout writing */
-                do_pbc_mtop(ir->ePBC, state->box, top_global, x_for_confout);
+                do_pbc_mtop(ir->pbcType, state->box, top_global, x_for_confout);
             }
             write_sto_conf_mtop(ftp2fn(efSTO, nfile, fnm), *top_global->name, top_global,
-                                x_for_confout, state_global->v.rvec_array(), ir->ePBC, state->box);
+                                x_for_confout, state_global->v.rvec_array(), ir->pbcType, state->box);
             if (fr->bMolPBC && state == state_global)
             {
                 sfree(x_for_confout);
index d49614100b6a9eb30b6b97197b92df737910f181..a74c707e778abf205d4f755850cfc15cda2eca03 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -59,28 +60,28 @@ class EnergyOutput;
  *
  * This routine does communication (e.g. collecting distributed coordinates)
  */
-void do_md_trajectory_writing(FILE*                    fplog,
-                              struct t_commrec*        cr,
-                              int                      nfile,
-                              const t_filenm           fnm[],
-                              int64_t                  step,
-                              int64_t                  step_rel,
-                              double                   t,
-                              t_inputrec*              ir,
-                              t_state*                 state,
-                              t_state*                 state_global,
-                              ObservablesHistory*      observablesHistory,
-                              const gmx_mtop_t*        top_global,
-                              t_forcerec*              fr,
-                              gmx_mdoutf_t             outf,
-                              const gmx::EnergyOutput& energyOutput,
-                              gmx_ekindata_t*          ekind,
-                              gmx::ArrayRef<gmx::RVec> f,
-                              gmx_bool                 bCPT,
-                              gmx_bool                 bRerunMD,
-                              gmx_bool                 bLastStep,
-                              gmx_bool                 bDoConfOut,
-                              gmx_bool                 bSumEkinhOld);
+void do_md_trajectory_writing(FILE*                          fplog,
+                              struct t_commrec*              cr,
+                              int                            nfile,
+                              const t_filenm                 fnm[],
+                              int64_t                        step,
+                              int64_t                        step_rel,
+                              double                         t,
+                              t_inputrec*                    ir,
+                              t_state*                       state,
+                              t_state*                       state_global,
+                              ObservablesHistory*            observablesHistory,
+                              const gmx_mtop_t*              top_global,
+                              t_forcerec*                    fr,
+                              gmx_mdoutf_t                   outf,
+                              const gmx::EnergyOutput&       energyOutput,
+                              gmx_ekindata_t*                ekind,
+                              gmx::ArrayRef<const gmx::RVec> f,
+                              gmx_bool                       bCPT,
+                              gmx_bool                       bRerunMD,
+                              gmx_bool                       bLastStep,
+                              gmx_bool                       bDoConfOut,
+                              gmx_bool                       bSumEkinhOld);
 
 
 #endif
index 2e6dd74950f13a37fb15f9c9e335e805dd04fac7..46f848d4e73a42a083f975935ab5780274a1a796 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdlib/stat.h"
 #include "gromacs/mdlib/tgroup.h"
 #include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/fcdata.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/pbcutil/boxutilities.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pulling/pull.h"
 #include "gromacs/random/tabulatednormaldistribution.h"
@@ -106,7 +108,7 @@ struct gmx_stochd_t
     std::vector<bool> randomize_group;
     std::vector<real> boltzfac;
 
-    explicit gmx_stochd_t(const t_inputrec* ir);
+    explicit gmx_stochd_t(const t_inputrec& inputRecord);
 };
 
 //! pImpled implementation for Update
@@ -114,35 +116,143 @@ class Update::Impl
 {
 public:
     //! Constructor
-    Impl(const t_inputrec* ir, BoxDeformation* boxDeformation);
+    Impl(const t_inputrec& inputRecord, BoxDeformation* boxDeformation);
     //! Destructor
     ~Impl() = default;
+
+    void update_coords(const t_inputrec&                                inputRecord,
+                       int64_t                                          step,
+                       const t_mdatoms*                                 md,
+                       t_state*                                         state,
+                       const gmx::ArrayRefWithPadding<const gmx::RVec>& f,
+                       const t_fcdata&                                  fcdata,
+                       const gmx_ekindata_t*                            ekind,
+                       const matrix                                     M,
+                       int                                              UpdatePart,
+                       const t_commrec*                                 cr,
+                       bool                                             haveConstraints);
+
+    void finish_update(const t_inputrec& inputRecord,
+                       const t_mdatoms*  md,
+                       t_state*          state,
+                       gmx_wallcycle_t   wcycle,
+                       bool              haveConstraints);
+
+    void update_sd_second_half(const t_inputrec& inputRecord,
+                               int64_t           step,
+                               real*             dvdlambda,
+                               const t_mdatoms*  md,
+                               t_state*          state,
+                               const t_commrec*  cr,
+                               t_nrnb*           nrnb,
+                               gmx_wallcycle_t   wcycle,
+                               gmx::Constraints* constr,
+                               bool              do_log,
+                               bool              do_ene);
+
+    void update_for_constraint_virial(const t_inputrec&                                inputRecord,
+                                      const t_mdatoms&                                 md,
+                                      const t_state&                                   state,
+                                      const gmx::ArrayRefWithPadding<const gmx::RVec>& f,
+                                      const gmx_ekindata_t&                            ekind);
+
+    void update_temperature_constants(const t_inputrec& inputRecord);
+
+    const std::vector<bool>& getAndersenRandomizeGroup() const { return sd_.randomize_group; }
+
+    const std::vector<real>& getBoltzmanFactor() const { return sd_.boltzfac; }
+
+    PaddedVector<RVec>* xp() { return &xp_; }
+
+    BoxDeformation* deform() const { return deform_; }
+
+private:
     //! stochastic dynamics struct
-    std::unique_ptr<gmx_stochd_t> sd;
+    gmx_stochd_t sd_;
     //! xprime for constraint algorithms
-    PaddedVector<RVec> xp;
+    PaddedVector<RVec> xp_;
     //! Box deformation handler (or nullptr if inactive).
-    BoxDeformation* deform = nullptr;
+    BoxDeformation* deform_ = nullptr;
 };
 
-Update::Update(const t_inputrec* ir, BoxDeformation* boxDeformation) :
-    impl_(new Impl(ir, boxDeformation)){};
+Update::Update(const t_inputrec& inputRecord, BoxDeformation* boxDeformation) :
+    impl_(new Impl(inputRecord, boxDeformation)){};
 
 Update::~Update(){};
 
-gmx_stochd_t* Update::sd() const
+const std::vector<bool>& Update::getAndersenRandomizeGroup() const
 {
-    return impl_->sd.get();
+    return impl_->getAndersenRandomizeGroup();
+}
+
+const std::vector<real>& Update::getBoltzmanFactor() const
+{
+    return impl_->getBoltzmanFactor();
 }
 
 PaddedVector<RVec>* Update::xp()
 {
-    return &impl_->xp;
+    return impl_->xp();
 }
 
 BoxDeformation* Update::deform() const
 {
-    return impl_->deform;
+    return impl_->deform();
+}
+
+void Update::update_coords(const t_inputrec&                                inputRecord,
+                           int64_t                                          step,
+                           const t_mdatoms*                                 md,
+                           t_state*                                         state,
+                           const gmx::ArrayRefWithPadding<const gmx::RVec>& f,
+                           const t_fcdata&                                  fcdata,
+                           const gmx_ekindata_t*                            ekind,
+                           const matrix                                     M,
+                           int                                              updatePart,
+                           const t_commrec*                                 cr,
+                           const bool                                       haveConstraints)
+{
+    return impl_->update_coords(inputRecord, step, md, state, f, fcdata, ekind, M, updatePart, cr,
+                                haveConstraints);
+}
+
+void Update::finish_update(const t_inputrec& inputRecord,
+                           const t_mdatoms*  md,
+                           t_state*          state,
+                           gmx_wallcycle_t   wcycle,
+                           const bool        haveConstraints)
+{
+    return impl_->finish_update(inputRecord, md, state, wcycle, haveConstraints);
+}
+
+void Update::update_sd_second_half(const t_inputrec& inputRecord,
+                                   int64_t           step,
+                                   real*             dvdlambda,
+                                   const t_mdatoms*  md,
+                                   t_state*          state,
+                                   const t_commrec*  cr,
+                                   t_nrnb*           nrnb,
+                                   gmx_wallcycle_t   wcycle,
+                                   gmx::Constraints* constr,
+                                   bool              do_log,
+                                   bool              do_ene)
+{
+    return impl_->update_sd_second_half(inputRecord, step, dvdlambda, md, state, cr, nrnb, wcycle,
+                                        constr, do_log, do_ene);
+}
+
+void Update::update_for_constraint_virial(const t_inputrec& inputRecord,
+                                          const t_mdatoms&  md,
+                                          const t_state&    state,
+                                          const gmx::ArrayRefWithPadding<const gmx::RVec>& f,
+                                          const gmx_ekindata_t&                            ekind)
+{
+    return impl_->update_for_constraint_virial(inputRecord, md, state, f, ekind);
+}
+
+void Update::update_temperature_constants(const t_inputrec& inputRecord)
+{
+    return impl_->update_temperature_constants(inputRecord);
 }
 
 /*! \brief Sets the velocities of virtual sites to zero */
@@ -157,6 +267,13 @@ static void clearVsiteVelocities(int start, int nrend, const unsigned short* par
     }
 }
 
+/*! \brief Sets whether we store the updated velocities */
+enum class StoreUpdatedVelocities
+{
+    yes, //!< Store the updated velocities
+    no   //!< Do not store the updated velocities
+};
+
 /*! \brief Sets the number of different temperature coupling values */
 enum class NumTempScaleValues
 {
@@ -178,6 +295,7 @@ enum class ApplyParrinelloRahmanVScaling
 
 /*! \brief Integrate using leap-frog with T-scaling and optionally diagonal Parrinello-Rahman p-coupling
  *
+ * \tparam       storeUpdatedVelocities Tells whether we should store the updated velocities
  * \tparam       numTempScaleValues     The number of different T-couple values
  * \tparam       applyPRVScaling        Apply Parrinello-Rahman velocity scaling
  * \param[in]    start                  Index of first atom to update
@@ -198,7 +316,7 @@ enum class ApplyParrinelloRahmanVScaling
  * Note that we might get even better SIMD acceleration when we introduce
  * aligned (and padded) memory, possibly with some hints for the compilers.
  */
-template<NumTempScaleValues numTempScaleValues, ApplyParrinelloRahmanVScaling applyPRVScaling>
+template<StoreUpdatedVelocities storeUpdatedVelocities, NumTempScaleValues numTempScaleValues, ApplyParrinelloRahmanVScaling applyPRVScaling>
 static void updateMDLeapfrogSimple(int         start,
                                    int         nrend,
                                    real        dt,
@@ -240,7 +358,10 @@ static void updateMDLeapfrogSimple(int         start,
             {
                 vNew -= dtPressureCouple * pRVScaleMatrixDiagonal[d] * v[a][d];
             }
-            v[a][d]      = vNew;
+            if (storeUpdatedVelocities == StoreUpdatedVelocities::yes)
+            {
+                v[a][d] = vNew;
+            }
             xprime[a][d] = x[a][d] + vNew * dt;
         }
     }
@@ -304,6 +425,7 @@ static inline void simdStoreRvecs(rvec* r, int index, SimdReal r0, SimdReal r1,
 
 /*! \brief Integrate using leap-frog with single group T-scaling and SIMD
  *
+ * \tparam       storeUpdatedVelocities Tells whether we should store the updated velocities
  * \param[in]    start                  Index of first atom to update
  * \param[in]    nrend                  Last atom to update: \p nrend - 1
  * \param[in]    dt                     The time step
@@ -314,6 +436,7 @@ static inline void simdStoreRvecs(rvec* r, int index, SimdReal r0, SimdReal r1,
  * \param[inout] v                      Velocities
  * \param[in]    f                      Forces
  */
+template<StoreUpdatedVelocities storeUpdatedVelocities>
 static void updateMDLeapfrogSimpleSimd(int         start,
                                        int         nrend,
                                        real        dt,
@@ -346,7 +469,10 @@ static void updateMDLeapfrogSimpleSimd(int         start,
         v1 = fma(f1 * invMass1, timestep, lambdaSystem * v1);
         v2 = fma(f2 * invMass2, timestep, lambdaSystem * v2);
 
-        simdStoreRvecs(v, a, v0, v1, v2);
+        if (storeUpdatedVelocities == StoreUpdatedVelocities::yes)
+        {
+            simdStoreRvecs(v, a, v0, v1, v2);
+        }
 
         SimdReal x0, x1, x2;
         simdLoadRvecs(x, a, &x0, &x1, &x2);
@@ -369,20 +495,26 @@ enum class AccelerationType
     cosine
 };
 
-/*! \brief Integrate using leap-frog with support for everything
+/*! \brief Integrate using leap-frog with support for everything.
  *
- * \tparam       accelerationType       Type of NEMD acceleration
- * \param[in]    start                  Index of first atom to update
- * \param[in]    nrend                  Last atom to update: \p nrend - 1
- * \param[in]    doNoseHoover           If to apply Nose-Hoover T-coupling
- * \param[in]    dt                     The time step
- * \param[in]    dtPressureCouple       Time step for pressure coupling, is 0 when no pressure
- * coupling should be applied at this step \param[in]    ir                     The input parameter
- * record \param[in]    md                     Atom properties \param[in]    ekind Kinetic energy
- * data \param[in]    box                    The box dimensions \param[in]    x Input coordinates \param[out]
- * xprime                 Updated coordinates \param[inout] v                      Velocities \param[in]
- * f                      Forces \param[in]    nh_vxi                 Nose-Hoover velocity scaling
- * factors \param[in]    M                      Parrinello-Rahman scaling matrix
+ * \tparam        accelerationType  Type of NEMD acceleration.
+ * \param[in]     start             Index of first atom to update.
+ * \param[in]     nrend             Last atom to update: \p nrend - 1.
+ * \param[in]     doNoseHoover      If to apply Nose-Hoover T-coupling.
+ * \param[in]     dt                The time step.
+ * \param[in]     dtPressureCouple  Time step for pressure coupling, is 0 when no pressure
+ *                                  coupling should be applied at this step.
+ * \param[in]     accel             Acceleration per group.
+ * \param[in]     md                Atom properties.
+ * \param[in]     ekind             Kinetic energy data.
+ * \param[in]     box               The box dimensions.
+ * \param[in]     x                 Input coordinates.
+ * \param[out]    xprime            Updated coordinates.
+ * \param[inout]  v                 Velocities.
+ * \param[in]     f                 Forces.
+ * \param[in]     nh_vxi            Nose-Hoover velocity scaling factors.
+ * \param[in]     nsttcouple        Frequency of the temperature coupling steps.
+ * \param[in]     M                 Parrinello-Rahman scaling matrix.
  */
 template<AccelerationType accelerationType>
 static void updateMDLeapfrogGeneral(int                   start,
@@ -390,7 +522,7 @@ static void updateMDLeapfrogGeneral(int                   start,
                                     bool                  doNoseHoover,
                                     real                  dt,
                                     real                  dtPressureCouple,
-                                    const t_inputrec*     ir,
+                                    const rvec*           accel,
                                     const t_mdatoms*      md,
                                     const gmx_ekindata_t* ekind,
                                     const matrix          box,
@@ -399,6 +531,7 @@ static void updateMDLeapfrogGeneral(int                   start,
                                     rvec* gmx_restrict v,
                                     const rvec* gmx_restrict f,
                                     const double* gmx_restrict nh_vxi,
+                                    const int                  nsttcouple,
                                     const matrix               M)
 {
     /* This is a version of the leap-frog integrator that supports
@@ -411,14 +544,12 @@ static void updateMDLeapfrogGeneral(int                   start,
     gmx::ArrayRef<const t_grp_acc>    grpstat = ekind->grpstat;
     const unsigned short*             cTC     = md->cTC;
     const unsigned short*             cACC    = md->cACC;
-    const rvec*                       accel   = ir->opts.acc;
 
     const rvec* gmx_restrict invMassPerDim = md->invMassPerDim;
 
     /* Initialize group values, changed later when multiple groups are used */
-    int  ga       = 0;
-    int  gt       = 0;
-    real factorNH = 0;
+    int ga = 0;
+    int gt = 0;
 
     real omega_Z = 2 * static_cast<real>(M_PI) / box[ZZ][ZZ];
 
@@ -455,12 +586,14 @@ static void updateMDLeapfrogGeneral(int                   start,
                 break;
         }
 
+        real factorNH = 0.0;
         if (doNoseHoover)
         {
             /* Here we account for multiple time stepping, by increasing
              * the Nose-Hoover correction by nsttcouple
+             * TODO: This can be pre-computed.
              */
-            factorNH = 0.5 * ir->nsttcouple * dt * nh_vxi[gt];
+            factorNH = 0.5 * nsttcouple * dt * nh_vxi[gt];
         }
 
         for (int d = 0; d < DIM; d++)
@@ -491,34 +624,36 @@ static void updateMDLeapfrogGeneral(int                   start,
 }
 
 /*! \brief Handles the Leap-frog MD x and v integration */
-static void do_update_md(int                   start,
-                         int                   nrend,
-                         int64_t               step,
-                         real                  dt,
-                         const t_inputrec*     ir,
-                         const t_mdatoms*      md,
-                         const gmx_ekindata_t* ekind,
-                         const matrix          box,
+static void do_update_md(int         start,
+                         int         nrend,
+                         real        dt,
+                         int64_t     step,
                          const rvec* gmx_restrict x,
                          rvec* gmx_restrict xprime,
                          rvec* gmx_restrict v,
                          const rvec* gmx_restrict f,
+                         const rvec* gmx_restrict accel,
+                         const int                etc,
+                         const int                epc,
+                         const int                nsttcouple,
+                         const int                nstpcouple,
+                         const t_mdatoms*         md,
+                         const gmx_ekindata_t*    ekind,
+                         const matrix             box,
                          const double* gmx_restrict nh_vxi,
                          const matrix               M)
 {
     GMX_ASSERT(nrend == start || xprime != x,
                "For SIMD optimization certain compilers need to have xprime != x");
-    GMX_ASSERT(ir->eI == eiMD,
-               "Leap-frog integrator was called while another integrator was requested");
 
     /* Note: Berendsen pressure scaling is handled after do_update_md() */
-    bool doTempCouple = (ir->etc != etcNO && do_per_step(step + ir->nsttcouple - 1, ir->nsttcouple));
-    bool doNoseHoover = (ir->etc == etcNOSEHOOVER && doTempCouple);
+    bool doTempCouple = (etc != etcNO && do_per_step(step + nsttcouple - 1, nsttcouple));
+    bool doNoseHoover = (etc == etcNOSEHOOVER && doTempCouple);
     bool doParrinelloRahman =
-            (ir->epc == epcPARRINELLORAHMAN && do_per_step(step + ir->nstpcouple - 1, ir->nstpcouple));
+            (epc == epcPARRINELLORAHMAN && do_per_step(step + nstpcouple - 1, nstpcouple));
     bool doPROffDiagonal = (doParrinelloRahman && (M[YY][XX] != 0 || M[ZZ][XX] != 0 || M[ZZ][YY] != 0));
 
-    real dtPressureCouple = (doParrinelloRahman ? ir->nstpcouple * dt : 0);
+    real dtPressureCouple = (doParrinelloRahman ? nstpcouple * dt : 0);
 
     /* NEMD (also cosine) acceleration is applied in updateMDLeapFrogGeneral */
     bool doAcceleration = (ekind->bNEMD || ekind->cosacc.cos_accel != 0);
@@ -539,20 +674,20 @@ static void do_update_md(int                   start,
         if (!doAcceleration)
         {
             updateMDLeapfrogGeneral<AccelerationType::none>(start, nrend, doNoseHoover, dt,
-                                                            dtPressureCouple, ir, md, ekind, box, x,
-                                                            xprime, v, f, nh_vxi, stepM);
+                                                            dtPressureCouple, accel, md, ekind, box, x,
+                                                            xprime, v, f, nh_vxi, nsttcouple, stepM);
         }
         else if (ekind->bNEMD)
         {
-            updateMDLeapfrogGeneral<AccelerationType::group>(start, nrend, doNoseHoover, dt,
-                                                             dtPressureCouple, ir, md, ekind, box,
-                                                             x, xprime, v, f, nh_vxi, stepM);
+            updateMDLeapfrogGeneral<AccelerationType::group>(
+                    start, nrend, doNoseHoover, dt, dtPressureCouple, accel, md, ekind, box, x,
+                    xprime, v, f, nh_vxi, nsttcouple, stepM);
         }
         else
         {
-            updateMDLeapfrogGeneral<AccelerationType::cosine>(start, nrend, doNoseHoover, dt,
-                                                              dtPressureCouple, ir, md, ekind, box,
-                                                              x, xprime, v, f, nh_vxi, stepM);
+            updateMDLeapfrogGeneral<AccelerationType::cosine>(
+                    start, nrend, doNoseHoover, dt, dtPressureCouple, accel, md, ekind, box, x,
+                    xprime, v, f, nh_vxi, nsttcouple, stepM);
         }
     }
     else
@@ -596,13 +731,15 @@ static void do_update_md(int                   start,
 
             if (haveSingleTempScaleValue)
             {
-                updateMDLeapfrogSimple<NumTempScaleValues::single, ApplyParrinelloRahmanVScaling::diagonal>(
+                updateMDLeapfrogSimple<StoreUpdatedVelocities::yes, NumTempScaleValues::single,
+                                       ApplyParrinelloRahmanVScaling::diagonal>(
                         start, nrend, dt, dtPressureCouple, invMassPerDim, tcstat, cTC, diagM, x,
                         xprime, v, f);
             }
             else
             {
-                updateMDLeapfrogSimple<NumTempScaleValues::multiple, ApplyParrinelloRahmanVScaling::diagonal>(
+                updateMDLeapfrogSimple<StoreUpdatedVelocities::yes, NumTempScaleValues::multiple,
+                                       ApplyParrinelloRahmanVScaling::diagonal>(
                         start, nrend, dt, dtPressureCouple, invMassPerDim, tcstat, cTC, diagM, x,
                         xprime, v, f);
             }
@@ -620,25 +757,58 @@ static void do_update_md(int                   start,
                 /* Check if we can use invmass instead of invMassPerDim */
                 if (!md->havePartiallyFrozenAtoms)
                 {
-                    updateMDLeapfrogSimpleSimd(start, nrend, dt, md->invmass, tcstat, x, xprime, v, f);
+                    updateMDLeapfrogSimpleSimd<StoreUpdatedVelocities::yes>(
+                            start, nrend, dt, md->invmass, tcstat, x, xprime, v, f);
                 }
                 else
 #endif
                 {
-                    updateMDLeapfrogSimple<NumTempScaleValues::single, ApplyParrinelloRahmanVScaling::no>(
+                    updateMDLeapfrogSimple<StoreUpdatedVelocities::yes, NumTempScaleValues::single,
+                                           ApplyParrinelloRahmanVScaling::no>(
                             start, nrend, dt, dtPressureCouple, invMassPerDim, tcstat, cTC, nullptr,
                             x, xprime, v, f);
                 }
             }
             else
             {
-                updateMDLeapfrogSimple<NumTempScaleValues::multiple, ApplyParrinelloRahmanVScaling::no>(
+                updateMDLeapfrogSimple<StoreUpdatedVelocities::yes, NumTempScaleValues::multiple,
+                                       ApplyParrinelloRahmanVScaling::no>(
                         start, nrend, dt, dtPressureCouple, invMassPerDim, tcstat, cTC, nullptr, x,
                         xprime, v, f);
             }
         }
     }
 }
+/*! \brief Handles the Leap-frog MD x and v integration */
+static void doUpdateMDDoNotUpdateVelocities(int         start,
+                                            int         nrend,
+                                            real        dt,
+                                            const rvec* gmx_restrict x,
+                                            rvec* gmx_restrict xprime,
+                                            rvec* gmx_restrict v,
+                                            const rvec* gmx_restrict f,
+                                            const t_mdatoms&         md,
+                                            const gmx_ekindata_t&    ekind)
+{
+    GMX_ASSERT(nrend == start || xprime != x,
+               "For SIMD optimization certain compilers need to have xprime != x");
+
+    gmx::ArrayRef<const t_grp_tcstat> tcstat = ekind.tcstat;
+
+    /* Check if we can use invmass instead of invMassPerDim */
+#if GMX_HAVE_SIMD_UPDATE
+    if (!md.havePartiallyFrozenAtoms)
+    {
+        updateMDLeapfrogSimpleSimd<StoreUpdatedVelocities::no>(start, nrend, dt, md.invmass, tcstat,
+                                                               x, xprime, v, f);
+    }
+    else
+#endif
+    {
+        updateMDLeapfrogSimple<StoreUpdatedVelocities::no, NumTempScaleValues::single, ApplyParrinelloRahmanVScaling::no>(
+                start, nrend, dt, dt, md.invMassPerDim, tcstat, nullptr, nullptr, x, xprime, v, f);
+    }
+}
 
 static void do_update_vv_vel(int                  start,
                              int                  nrend,
@@ -747,16 +917,16 @@ static void do_update_vv_pos(int                  start,
     }
 } /* do_update_vv_pos */
 
-gmx_stochd_t::gmx_stochd_t(const t_inputrec* ir)
+gmx_stochd_t::gmx_stochd_t(const t_inputrec& inputRecord)
 {
-    const t_grpopts* opts = &ir->opts;
+    const t_grpopts* opts = &inputRecord.opts;
     const int        ngtc = opts->ngtc;
 
-    if (ir->eI == eiBD)
+    if (inputRecord.eI == eiBD)
     {
         bd_rf.resize(ngtc);
     }
-    else if (EI_SD(ir->eI))
+    else if (EI_SD(inputRecord.eI))
     {
         sdc.resize(ngtc);
         sdsig.resize(ngtc);
@@ -765,7 +935,7 @@ gmx_stochd_t::gmx_stochd_t(const t_inputrec* ir)
         {
             if (opts->tau_t[gt] > 0)
             {
-                sdc[gt].em = std::exp(-ir->delta_t / opts->tau_t[gt]);
+                sdc[gt].em = std::exp(-inputRecord.delta_t / opts->tau_t[gt]);
             }
             else
             {
@@ -774,7 +944,7 @@ gmx_stochd_t::gmx_stochd_t(const t_inputrec* ir)
             }
         }
     }
-    else if (ETC_ANDERSEN(ir->etc))
+    else if (ETC_ANDERSEN(inputRecord.etc))
     {
         randomize_group.resize(ngtc);
         boltzfac.resize(ngtc);
@@ -799,48 +969,49 @@ gmx_stochd_t::gmx_stochd_t(const t_inputrec* ir)
     }
 }
 
-void update_temperature_constants(gmx_stochd_t* sd, const t_inputrec* ir)
+void Update::Impl::update_temperature_constants(const t_inputrec& inputRecord)
 {
-    if (ir->eI == eiBD)
+    if (inputRecord.eI == eiBD)
     {
-        if (ir->bd_fric != 0)
+        if (inputRecord.bd_fric != 0)
         {
-            for (int gt = 0; gt < ir->opts.ngtc; gt++)
+            for (int gt = 0; gt < inputRecord.opts.ngtc; gt++)
             {
-                sd->bd_rf[gt] = std::sqrt(2.0 * BOLTZ * ir->opts.ref_t[gt] / (ir->bd_fric * ir->delta_t));
+                sd_.bd_rf[gt] = std::sqrt(2.0 * BOLTZ * inputRecord.opts.ref_t[gt]
+                                          / (inputRecord.bd_fric * inputRecord.delta_t));
             }
         }
         else
         {
-            for (int gt = 0; gt < ir->opts.ngtc; gt++)
+            for (int gt = 0; gt < inputRecord.opts.ngtc; gt++)
             {
-                sd->bd_rf[gt] = std::sqrt(2.0 * BOLTZ * ir->opts.ref_t[gt]);
+                sd_.bd_rf[gt] = std::sqrt(2.0 * BOLTZ * inputRecord.opts.ref_t[gt]);
             }
         }
     }
-    if (ir->eI == eiSD1)
+    if (inputRecord.eI == eiSD1)
     {
-        for (int gt = 0; gt < ir->opts.ngtc; gt++)
+        for (int gt = 0; gt < inputRecord.opts.ngtc; gt++)
         {
-            real kT = BOLTZ * ir->opts.ref_t[gt];
+            real kT = BOLTZ * inputRecord.opts.ref_t[gt];
             /* The mass is accounted for later, since this differs per atom */
-            sd->sdsig[gt].V = std::sqrt(kT * (1 - sd->sdc[gt].em * sd->sdc[gt].em));
+            sd_.sdsig[gt].V = std::sqrt(kT * (1 - sd_.sdc[gt].em * sd_.sdc[gt].em));
         }
     }
 }
 
-Update::Impl::Impl(const t_inputrec* ir, BoxDeformation* boxDeformation)
+Update::Impl::Impl(const t_inputrec& inputRecord, BoxDeformation* boxDeformation) :
+    sd_(inputRecord),
+    deform_(boxDeformation)
 {
-    sd = std::make_unique<gmx_stochd_t>(ir);
-    update_temperature_constants(sd.get(), ir);
-    xp.resizeWithPadding(0);
-    deform = boxDeformation;
+    update_temperature_constants(inputRecord);
+    xp_.resizeWithPadding(0);
 }
 
-void Update::setNumAtoms(int nAtoms)
+void Update::setNumAtoms(int numAtoms)
 {
 
-    impl_->xp.resizeWithPadding(nAtoms);
+    impl_->xp()->resizeWithPadding(numAtoms);
 }
 
 /*! \brief Sets the SD update type */
@@ -966,23 +1137,58 @@ static void doSDUpdateGeneral(const gmx_stochd_t&  sd,
     }
 }
 
-static void do_update_bd(int                  start,
-                         int                  nrend,
-                         real                 dt,
-                         const ivec           nFreeze[],
-                         const real           invmass[],
-                         const unsigned short ptype[],
-                         const unsigned short cFREEZE[],
-                         const unsigned short cTC[],
-                         const rvec           x[],
-                         rvec                 xprime[],
-                         rvec                 v[],
-                         const rvec           f[],
-                         real                 friction_coefficient,
-                         const real*          rf,
-                         int64_t              step,
-                         int                  seed,
-                         const int*           gatindex)
+static void do_update_sd(int         start,
+                         int         nrend,
+                         real        dt,
+                         int64_t     step,
+                         const rvec* gmx_restrict x,
+                         rvec* gmx_restrict xprime,
+                         rvec* gmx_restrict v,
+                         const rvec* gmx_restrict f,
+                         const rvec               accel[],
+                         const ivec               nFreeze[],
+                         const real               invmass[],
+                         const unsigned short     ptype[],
+                         const unsigned short     cFREEZE[],
+                         const unsigned short     cACC[],
+                         const unsigned short     cTC[],
+                         int                      seed,
+                         const t_commrec*         cr,
+                         const gmx_stochd_t&      sd,
+                         bool                     haveConstraints)
+{
+    if (haveConstraints)
+    {
+        // With constraints, the SD update is done in 2 parts
+        doSDUpdateGeneral<SDUpdate::ForcesOnly>(sd, start, nrend, dt, accel, nFreeze, invmass,
+                                                ptype, cFREEZE, cACC, nullptr, x, xprime, v, f,
+                                                step, seed, nullptr);
+    }
+    else
+    {
+        doSDUpdateGeneral<SDUpdate::Combined>(
+                sd, start, nrend, dt, accel, nFreeze, invmass, ptype, cFREEZE, cACC, cTC, x, xprime,
+                v, f, step, seed, DOMAINDECOMP(cr) ? cr->dd->globalAtomIndices.data() : nullptr);
+    }
+}
+
+static void do_update_bd(int         start,
+                         int         nrend,
+                         real        dt,
+                         int64_t     step,
+                         const rvec* gmx_restrict x,
+                         rvec* gmx_restrict xprime,
+                         rvec* gmx_restrict v,
+                         const rvec* gmx_restrict f,
+                         const ivec               nFreeze[],
+                         const real               invmass[],
+                         const unsigned short     ptype[],
+                         const unsigned short     cFREEZE[],
+                         const unsigned short     cTC[],
+                         real                     friction_coefficient,
+                         const real*              rf,
+                         int                      seed,
+                         const int*               gatindex)
 {
     /* note -- these appear to be full step velocities . . .  */
     int  gf = 0, gt = 0;
@@ -1041,218 +1247,6 @@ static void do_update_bd(int                  start,
     }
 }
 
-static void calc_ke_part_normal(const rvec       v[],
-                                const t_grpopts* opts,
-                                const t_mdatoms* md,
-                                gmx_ekindata_t*  ekind,
-                                t_nrnb*          nrnb,
-                                gmx_bool         bEkinAveVel)
-{
-    int                         g;
-    gmx::ArrayRef<t_grp_tcstat> tcstat  = ekind->tcstat;
-    gmx::ArrayRef<t_grp_acc>    grpstat = ekind->grpstat;
-
-    /* three main: VV with AveVel, vv with AveEkin, leap with AveEkin.  Leap with AveVel is also
-       an option, but not supported now.
-       bEkinAveVel: If TRUE, we sum into ekin, if FALSE, into ekinh.
-     */
-
-    /* group velocities are calculated in update_ekindata and
-     * accumulated in acumulate_groups.
-     * Now the partial global and groups ekin.
-     */
-    for (g = 0; (g < opts->ngtc); g++)
-    {
-        copy_mat(tcstat[g].ekinh, tcstat[g].ekinh_old);
-        if (bEkinAveVel)
-        {
-            clear_mat(tcstat[g].ekinf);
-            tcstat[g].ekinscalef_nhc = 1.0; /* need to clear this -- logic is complicated! */
-        }
-        else
-        {
-            clear_mat(tcstat[g].ekinh);
-        }
-    }
-    ekind->dekindl_old = ekind->dekindl;
-    int nthread        = gmx_omp_nthreads_get(emntUpdate);
-
-#pragma omp parallel for num_threads(nthread) schedule(static)
-    for (int thread = 0; thread < nthread; thread++)
-    {
-        // This OpenMP only loops over arrays and does not call any functions
-        // or memory allocation. It should not be able to throw, so for now
-        // we do not need a try/catch wrapper.
-        int     start_t, end_t, n;
-        int     ga, gt;
-        rvec    v_corrt;
-        real    hm;
-        int     d, m;
-        matrix* ekin_sum;
-        real*   dekindl_sum;
-
-        start_t = ((thread + 0) * md->homenr) / nthread;
-        end_t   = ((thread + 1) * md->homenr) / nthread;
-
-        ekin_sum    = ekind->ekin_work[thread];
-        dekindl_sum = ekind->dekindl_work[thread];
-
-        for (gt = 0; gt < opts->ngtc; gt++)
-        {
-            clear_mat(ekin_sum[gt]);
-        }
-        *dekindl_sum = 0.0;
-
-        ga = 0;
-        gt = 0;
-        for (n = start_t; n < end_t; n++)
-        {
-            if (md->cACC)
-            {
-                ga = md->cACC[n];
-            }
-            if (md->cTC)
-            {
-                gt = md->cTC[n];
-            }
-            hm = 0.5 * md->massT[n];
-
-            for (d = 0; (d < DIM); d++)
-            {
-                v_corrt[d] = v[n][d] - grpstat[ga].u[d];
-            }
-            for (d = 0; (d < DIM); d++)
-            {
-                for (m = 0; (m < DIM); m++)
-                {
-                    /* if we're computing a full step velocity, v_corrt[d] has v(t).  Otherwise, v(t+dt/2) */
-                    ekin_sum[gt][m][d] += hm * v_corrt[m] * v_corrt[d];
-                }
-            }
-            if (md->nMassPerturbed && md->bPerturbed[n])
-            {
-                *dekindl_sum += 0.5 * (md->massB[n] - md->massA[n]) * iprod(v_corrt, v_corrt);
-            }
-        }
-    }
-
-    ekind->dekindl = 0;
-    for (int thread = 0; thread < nthread; thread++)
-    {
-        for (g = 0; g < opts->ngtc; g++)
-        {
-            if (bEkinAveVel)
-            {
-                m_add(tcstat[g].ekinf, ekind->ekin_work[thread][g], tcstat[g].ekinf);
-            }
-            else
-            {
-                m_add(tcstat[g].ekinh, ekind->ekin_work[thread][g], tcstat[g].ekinh);
-            }
-        }
-
-        ekind->dekindl += *ekind->dekindl_work[thread];
-    }
-
-    inc_nrnb(nrnb, eNR_EKIN, md->homenr);
-}
-
-static void calc_ke_part_visc(const matrix     box,
-                              const rvec       x[],
-                              const rvec       v[],
-                              const t_grpopts* opts,
-                              const t_mdatoms* md,
-                              gmx_ekindata_t*  ekind,
-                              t_nrnb*          nrnb,
-                              gmx_bool         bEkinAveVel)
-{
-    int                         start = 0, homenr = md->homenr;
-    int                         g, d, n, m, gt = 0;
-    rvec                        v_corrt;
-    real                        hm;
-    gmx::ArrayRef<t_grp_tcstat> tcstat = ekind->tcstat;
-    t_cos_acc*                  cosacc = &(ekind->cosacc);
-    real                        dekindl;
-    real                        fac, cosz;
-    double                      mvcos;
-
-    for (g = 0; g < opts->ngtc; g++)
-    {
-        copy_mat(ekind->tcstat[g].ekinh, ekind->tcstat[g].ekinh_old);
-        clear_mat(ekind->tcstat[g].ekinh);
-    }
-    ekind->dekindl_old = ekind->dekindl;
-
-    fac     = 2 * M_PI / box[ZZ][ZZ];
-    mvcos   = 0;
-    dekindl = 0;
-    for (n = start; n < start + homenr; n++)
-    {
-        if (md->cTC)
-        {
-            gt = md->cTC[n];
-        }
-        hm = 0.5 * md->massT[n];
-
-        /* Note that the times of x and v differ by half a step */
-        /* MRS -- would have to be changed for VV */
-        cosz = std::cos(fac * x[n][ZZ]);
-        /* Calculate the amplitude of the new velocity profile */
-        mvcos += 2 * cosz * md->massT[n] * v[n][XX];
-
-        copy_rvec(v[n], v_corrt);
-        /* Subtract the profile for the kinetic energy */
-        v_corrt[XX] -= cosz * cosacc->vcos;
-        for (d = 0; (d < DIM); d++)
-        {
-            for (m = 0; (m < DIM); m++)
-            {
-                /* if we're computing a full step velocity, v_corrt[d] has v(t).  Otherwise, v(t+dt/2) */
-                if (bEkinAveVel)
-                {
-                    tcstat[gt].ekinf[m][d] += hm * v_corrt[m] * v_corrt[d];
-                }
-                else
-                {
-                    tcstat[gt].ekinh[m][d] += hm * v_corrt[m] * v_corrt[d];
-                }
-            }
-        }
-        if (md->nPerturbed && md->bPerturbed[n])
-        {
-            /* The minus sign here might be confusing.
-             * The kinetic contribution from dH/dl doesn't come from
-             * d m(l)/2 v^2 / dl, but rather from d p^2/2m(l) / dl,
-             * where p are the momenta. The difference is only a minus sign.
-             */
-            dekindl -= 0.5 * (md->massB[n] - md->massA[n]) * iprod(v_corrt, v_corrt);
-        }
-    }
-    ekind->dekindl = dekindl;
-    cosacc->mvcos  = mvcos;
-
-    inc_nrnb(nrnb, eNR_EKIN, homenr);
-}
-
-void calc_ke_part(const rvec*      x,
-                  const rvec*      v,
-                  const matrix     box,
-                  const t_grpopts* opts,
-                  const t_mdatoms* md,
-                  gmx_ekindata_t*  ekind,
-                  t_nrnb*          nrnb,
-                  gmx_bool         bEkinAveVel)
-{
-    if (ekind->cosacc.cos_accel == 0)
-    {
-        calc_ke_part_normal(v, opts, md, ekind, nrnb, bEkinAveVel);
-    }
-    else
-    {
-        calc_ke_part_visc(box, x, v, opts, md, ekind, nrnb, bEkinAveVel);
-    }
-}
-
 extern void init_ekinstate(ekinstate_t* ekinstate, const t_inputrec* ir)
 {
     ekinstate->ekin_n = ir->opts.ngtc;
@@ -1311,91 +1305,27 @@ void restore_ekinstate_from_state(const t_commrec* cr, gmx_ekindata_t* ekind, co
 
     if (PAR(cr))
     {
-        gmx_bcast(sizeof(n), &n, cr);
+        gmx_bcast(sizeof(n), &n, cr->mpi_comm_mygroup);
         for (i = 0; i < n; i++)
         {
-            gmx_bcast(DIM * DIM * sizeof(ekind->tcstat[i].ekinh[0][0]), ekind->tcstat[i].ekinh[0], cr);
-            gmx_bcast(DIM * DIM * sizeof(ekind->tcstat[i].ekinf[0][0]), ekind->tcstat[i].ekinf[0], cr);
+            gmx_bcast(DIM * DIM * sizeof(ekind->tcstat[i].ekinh[0][0]), ekind->tcstat[i].ekinh[0],
+                      cr->mpi_comm_mygroup);
+            gmx_bcast(DIM * DIM * sizeof(ekind->tcstat[i].ekinf[0][0]), ekind->tcstat[i].ekinf[0],
+                      cr->mpi_comm_mygroup);
             gmx_bcast(DIM * DIM * sizeof(ekind->tcstat[i].ekinh_old[0][0]),
-                      ekind->tcstat[i].ekinh_old[0], cr);
-
-            gmx_bcast(sizeof(ekind->tcstat[i].ekinscalef_nhc), &(ekind->tcstat[i].ekinscalef_nhc), cr);
-            gmx_bcast(sizeof(ekind->tcstat[i].ekinscaleh_nhc), &(ekind->tcstat[i].ekinscaleh_nhc), cr);
-            gmx_bcast(sizeof(ekind->tcstat[i].vscale_nhc), &(ekind->tcstat[i].vscale_nhc), cr);
+                      ekind->tcstat[i].ekinh_old[0], cr->mpi_comm_mygroup);
+
+            gmx_bcast(sizeof(ekind->tcstat[i].ekinscalef_nhc), &(ekind->tcstat[i].ekinscalef_nhc),
+                      cr->mpi_comm_mygroup);
+            gmx_bcast(sizeof(ekind->tcstat[i].ekinscaleh_nhc), &(ekind->tcstat[i].ekinscaleh_nhc),
+                      cr->mpi_comm_mygroup);
+            gmx_bcast(sizeof(ekind->tcstat[i].vscale_nhc), &(ekind->tcstat[i].vscale_nhc),
+                      cr->mpi_comm_mygroup);
         }
-        gmx_bcast(DIM * DIM * sizeof(ekind->ekin[0][0]), ekind->ekin[0], cr);
+        gmx_bcast(DIM * DIM * sizeof(ekind->ekin[0][0]), ekind->ekin[0], cr->mpi_comm_mygroup);
 
-        gmx_bcast(sizeof(ekind->dekindl), &ekind->dekindl, cr);
-        gmx_bcast(sizeof(ekind->cosacc.mvcos), &ekind->cosacc.mvcos, cr);
-    }
-}
-
-void update_tcouple(int64_t           step,
-                    const t_inputrec* inputrec,
-                    t_state*          state,
-                    gmx_ekindata_t*   ekind,
-                    const t_extmass*  MassQ,
-                    const t_mdatoms*  md)
-
-{
-    // This condition was explicitly checked in previous version, but should have never been satisfied
-    GMX_ASSERT(!(EI_VV(inputrec->eI)
-                 && (inputrecNvtTrotter(inputrec) || inputrecNptTrotter(inputrec)
-                     || inputrecNphTrotter(inputrec))),
-               "Temperature coupling was requested with velocity verlet and trotter");
-
-    bool doTemperatureCoupling = false;
-
-    // For VV temperature coupling parameters are updated on the current
-    // step, for the others - one step before.
-    if (inputrec->etc == etcNO)
-    {
-        doTemperatureCoupling = false;
-    }
-    else if (EI_VV(inputrec->eI))
-    {
-        doTemperatureCoupling = do_per_step(step, inputrec->nsttcouple);
-    }
-    else
-    {
-        doTemperatureCoupling = do_per_step(step + inputrec->nsttcouple - 1, inputrec->nsttcouple);
-    }
-
-    if (doTemperatureCoupling)
-    {
-        real dttc = inputrec->nsttcouple * inputrec->delta_t;
-
-        // TODO: berendsen_tcoupl(...), nosehoover_tcoupl(...) and vrescale_tcoupl(...) update
-        //      temperature coupling parameters, which should be reflected in the name of these
-        //      subroutines
-        switch (inputrec->etc)
-        {
-            case etcNO: break;
-            case etcBERENDSEN:
-                berendsen_tcoupl(inputrec, ekind, dttc, state->therm_integral);
-                break;
-            case etcNOSEHOOVER:
-                nosehoover_tcoupl(&(inputrec->opts), ekind, dttc, state->nosehoover_xi.data(),
-                                  state->nosehoover_vxi.data(), MassQ);
-                break;
-            case etcVRESCALE:
-                vrescale_tcoupl(inputrec, step, ekind, dttc, state->therm_integral.data());
-                break;
-        }
-        /* rescale in place here */
-        if (EI_VV(inputrec->eI))
-        {
-            rescale_velocities(ekind, md, 0, md->homenr, state->v.rvec_array());
-        }
-    }
-    else
-    {
-        // Set the T scaling lambda to 1 to have no scaling
-        // TODO: Do we have to do it on every non-t-couple step?
-        for (int i = 0; (i < inputrec->opts.ngtc); i++)
-        {
-            ekind->tcstat[i].lambda = 1.0;
-        }
+        gmx_bcast(sizeof(ekind->dekindl), &ekind->dekindl, cr->mpi_comm_mygroup);
+        gmx_bcast(sizeof(ekind->cosacc.mvcos), &ekind->cosacc.mvcos, cr->mpi_comm_mygroup);
     }
 }
 
@@ -1416,117 +1346,23 @@ void getThreadAtomRange(int numThreads, int threadIndex, int numAtoms, int* star
     }
 }
 
-void update_pcouple_before_coordinates(FILE*             fplog,
-                                       int64_t           step,
-                                       const t_inputrec* inputrec,
-                                       t_state*          state,
-                                       matrix            parrinellorahmanMu,
-                                       matrix            M,
-                                       gmx_bool          bInitStep)
-{
-    /* Berendsen P-coupling is completely handled after the coordinate update.
-     * Trotter P-coupling is handled by separate calls to trotter_update().
-     */
-    if (inputrec->epc == epcPARRINELLORAHMAN
-        && do_per_step(step + inputrec->nstpcouple - 1, inputrec->nstpcouple))
-    {
-        real dtpc = inputrec->nstpcouple * inputrec->delta_t;
-
-        parrinellorahman_pcoupl(fplog, step, inputrec, dtpc, state->pres_prev, state->box,
-                                state->box_rel, state->boxv, M, parrinellorahmanMu, bInitStep);
-    }
-}
-
-void constrain_velocities(int64_t step,
-                          real* dvdlambda, /* the contribution to be added to the bonded interactions */
-                          t_state*          state,
-                          tensor            vir_part,
-                          gmx::Constraints* constr,
-                          gmx_bool          bCalcVir,
-                          bool              do_log,
-                          bool              do_ene)
-{
-    if (!constr)
-    {
-        return;
-    }
-
-    /*
-     *  Steps (7C, 8C)
-     *  APPLY CONSTRAINTS:
-     *  BLOCK SHAKE
-     */
-
-    {
-        tensor vir_con;
-
-        /* clear out constraints before applying */
-        clear_mat(vir_part);
-
-        /* Constrain the coordinates upd->xp */
-        constr->apply(do_log, do_ene, step, 1, 1.0, state->x.rvec_array(), state->v.rvec_array(),
-                      state->v.rvec_array(), state->box, state->lambda[efptBONDED], dvdlambda,
-                      nullptr, bCalcVir ? &vir_con : nullptr, ConstraintVariable::Velocities);
-
-        if (bCalcVir)
-        {
-            m_add(vir_part, vir_con, vir_part);
-        }
-    }
-}
-
-void constrain_coordinates(int64_t step,
-                           real* dvdlambda, /* the contribution to be added to the bonded interactions */
-                           t_state*          state,
-                           tensor            vir_part,
-                           Update*           upd,
-                           gmx::Constraints* constr,
-                           gmx_bool          bCalcVir,
-                           bool              do_log,
-                           bool              do_ene)
+void Update::Impl::update_sd_second_half(const t_inputrec& inputRecord,
+                                         int64_t           step,
+                                         real*             dvdlambda,
+                                         const t_mdatoms*  md,
+                                         t_state*          state,
+                                         const t_commrec*  cr,
+                                         t_nrnb*           nrnb,
+                                         gmx_wallcycle_t   wcycle,
+                                         gmx::Constraints* constr,
+                                         bool              do_log,
+                                         bool              do_ene)
 {
     if (!constr)
     {
         return;
     }
-
-    {
-        tensor vir_con;
-
-        /* clear out constraints before applying */
-        clear_mat(vir_part);
-
-        /* Constrain the coordinates upd->xp */
-        constr->apply(do_log, do_ene, step, 1, 1.0, state->x.rvec_array(), upd->xp()->rvec_array(),
-                      nullptr, state->box, state->lambda[efptBONDED], dvdlambda,
-                      as_rvec_array(state->v.data()), bCalcVir ? &vir_con : nullptr,
-                      ConstraintVariable::Positions);
-
-        if (bCalcVir)
-        {
-            m_add(vir_part, vir_con, vir_part);
-        }
-    }
-}
-
-void update_sd_second_half(int64_t step,
-                           real* dvdlambda, /* the contribution to be added to the bonded interactions */
-                           const t_inputrec* inputrec, /* input record and box stuff   */
-                           const t_mdatoms*  md,
-                           t_state*          state,
-                           const t_commrec*  cr,
-                           t_nrnb*           nrnb,
-                           gmx_wallcycle_t   wcycle,
-                           Update*           upd,
-                           gmx::Constraints* constr,
-                           bool              do_log,
-                           bool              do_ene)
-{
-    if (!constr)
-    {
-        return;
-    }
-    if (inputrec->eI == eiSD1)
+    if (inputRecord.eI == eiSD1)
     {
         int homenr = md->homenr;
 
@@ -1537,7 +1373,7 @@ void update_sd_second_half(int64_t step,
          * integral += dt*integrand the increment is nearly always (much) smaller
          * than the integral (and the integrand has real precision).
          */
-        real dt = inputrec->delta_t;
+        real dt = inputRecord.delta_t;
 
         wallcycle_start(wcycle, ewcUPDATE);
 
@@ -1552,9 +1388,9 @@ void update_sd_second_half(int64_t step,
                 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
 
                 doSDUpdateGeneral<SDUpdate::FrictionAndNoiseOnly>(
-                        *upd->sd(), start_th, end_th, dt, inputrec->opts.acc, inputrec->opts.nFreeze,
+                        sd_, start_th, end_th, dt, inputRecord.opts.acc, inputRecord.opts.nFreeze,
                         md->invmass, md->ptype, md->cFREEZE, nullptr, md->cTC, state->x.rvec_array(),
-                        upd->xp()->rvec_array(), state->v.rvec_array(), nullptr, step, inputrec->ld_seed,
+                        xp_.rvec_array(), state->v.rvec_array(), nullptr, step, inputRecord.ld_seed,
                         DOMAINDECOMP(cr) ? cr->dd->globalAtomIndices.data() : nullptr);
             }
             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
@@ -1563,213 +1399,82 @@ void update_sd_second_half(int64_t step,
         wallcycle_stop(wcycle, ewcUPDATE);
 
         /* Constrain the coordinates upd->xp for half a time step */
-        constr->apply(do_log, do_ene, step, 1, 0.5, state->x.rvec_array(), upd->xp()->rvec_array(),
-                      nullptr, state->box, state->lambda[efptBONDED], dvdlambda,
-                      as_rvec_array(state->v.data()), nullptr, ConstraintVariable::Positions);
+        bool computeVirial = false;
+        constr->apply(do_log, do_ene, step, 1, 0.5, state->x.arrayRefWithPadding(),
+                      xp_.arrayRefWithPadding(), ArrayRef<RVec>(), state->box,
+                      state->lambda[efptBONDED], dvdlambda, state->v.arrayRefWithPadding(),
+                      computeVirial, nullptr, ConstraintVariable::Positions);
     }
 }
 
-void finish_update(const t_inputrec*       inputrec, /* input record and box stuff     */
-                   const t_mdatoms*        md,
-                   t_state*                state,
-                   const t_graph*          graph,
-                   t_nrnb*                 nrnb,
-                   gmx_wallcycle_t         wcycle,
-                   Update*                 upd,
-                   const gmx::Constraints* constr)
+void Update::Impl::finish_update(const t_inputrec& inputRecord,
+                                 const t_mdatoms*  md,
+                                 t_state*          state,
+                                 gmx_wallcycle_t   wcycle,
+                                 const bool        haveConstraints)
 {
-    int homenr = md->homenr;
+    /* NOTE: Currently we always integrate to a temporary buffer and
+     * then copy the results back here.
+     */
 
-    /* We must always unshift after updating coordinates; if we did not shake
-       x was shifted in do_force */
+    wallcycle_start_nocount(wcycle, ewcUPDATE);
 
-    /* NOTE Currently we always integrate to a temporary buffer and
-     * then copy the results back. */
+    const int homenr = md->homenr;
+    auto      xp     = makeConstArrayRef(xp_).subArray(0, homenr);
+    auto      x      = makeArrayRef(state->x).subArray(0, homenr);
+
+    if (md->havePartiallyFrozenAtoms && haveConstraints)
     {
-        wallcycle_start_nocount(wcycle, ewcUPDATE);
+        /* We have atoms that are frozen along some, but not all dimensions,
+         * then constraints will have moved them also along the frozen dimensions.
+         * To freeze such degrees of freedom we do not copy them back here.
+         */
+        const ivec* nFreeze = inputRecord.opts.nFreeze;
 
-        if (md->cFREEZE != nullptr && constr != nullptr)
+        for (int i = 0; i < homenr; i++)
         {
-            /* If we have atoms that are frozen along some, but not all
-             * dimensions, then any constraints will have moved them also along
-             * the frozen dimensions. To freeze such degrees of freedom
-             * we copy them back here to later copy them forward. It would
-             * be more elegant and slightly more efficient to copies zero
-             * times instead of twice, but the graph case below prevents this.
-             */
-            const ivec* nFreeze                     = inputrec->opts.nFreeze;
-            bool        partialFreezeAndConstraints = false;
-            for (int g = 0; g < inputrec->opts.ngfrz; g++)
-            {
-                int numFreezeDim = nFreeze[g][XX] + nFreeze[g][YY] + nFreeze[g][ZZ];
-                if (numFreezeDim > 0 && numFreezeDim < 3)
-                {
-                    partialFreezeAndConstraints = true;
-                }
-            }
-            if (partialFreezeAndConstraints)
+            const int g = md->cFREEZE[i];
+
+            for (int d = 0; d < DIM; d++)
             {
-                auto xp = makeArrayRef(*upd->xp()).subArray(0, homenr);
-                auto x  = makeConstArrayRef(state->x).subArray(0, homenr);
-                for (int i = 0; i < homenr; i++)
+                if (nFreeze[g][d] == 0)
                 {
-                    int g = md->cFREEZE[i];
-
-                    for (int d = 0; d < DIM; d++)
-                    {
-                        if (nFreeze[g][d])
-                        {
-                            xp[i][d] = x[i][d];
-                        }
-                    }
+                    x[i][d] = xp[i][d];
                 }
             }
         }
-
-        if (graph && (graph->nnodes > 0))
-        {
-            unshift_x(graph, state->box, state->x.rvec_array(), upd->xp()->rvec_array());
-            if (TRICLINIC(state->box))
-            {
-                inc_nrnb(nrnb, eNR_SHIFTX, 2 * graph->nnodes);
-            }
-            else
-            {
-                inc_nrnb(nrnb, eNR_SHIFTX, graph->nnodes);
-            }
-        }
-        else
-        {
-            auto xp = makeConstArrayRef(*upd->xp()).subArray(0, homenr);
-            auto x  = makeArrayRef(state->x).subArray(0, homenr);
-
-
-            int gmx_unused nth = gmx_omp_nthreads_get(emntUpdate);
-#pragma omp parallel for num_threads(nth) schedule(static)
-            for (int i = 0; i < homenr; i++)
-            {
-                // Trivial statement, does not throw
-                x[i] = xp[i];
-            }
-        }
-        wallcycle_stop(wcycle, ewcUPDATE);
     }
-    /* ############# END the update of velocities and positions ######### */
-}
-
-void update_pcouple_after_coordinates(FILE*             fplog,
-                                      int64_t           step,
-                                      const t_inputrec* inputrec,
-                                      const t_mdatoms*  md,
-                                      const matrix      pressure,
-                                      const matrix      forceVirial,
-                                      const matrix      constraintVirial,
-                                      matrix            pressureCouplingMu,
-                                      t_state*          state,
-                                      t_nrnb*           nrnb,
-                                      Update*           upd,
-                                      const bool        scaleCoordinates)
-{
-    int start  = 0;
-    int homenr = md->homenr;
-
-    /* Cast to real for faster code, no loss in precision (see comment above) */
-    real dt = inputrec->delta_t;
-
-
-    /* now update boxes */
-    switch (inputrec->epc)
+    else
     {
-        case (epcNO): break;
-        case (epcBERENDSEN):
-            if (do_per_step(step, inputrec->nstpcouple))
-            {
-                real dtpc = inputrec->nstpcouple * dt;
-                berendsen_pcoupl(fplog, step, inputrec, dtpc, pressure, state->box, forceVirial,
-                                 constraintVirial, pressureCouplingMu, &state->baros_integral);
-                berendsen_pscale(inputrec, pressureCouplingMu, state->box, state->box_rel, start,
-                                 homenr, state->x.rvec_array(), md->cFREEZE, nrnb, scaleCoordinates);
-            }
-            break;
-        case (epcPARRINELLORAHMAN):
-            if (do_per_step(step + inputrec->nstpcouple - 1, inputrec->nstpcouple))
-            {
-                /* The box velocities were updated in do_pr_pcoupl,
-                 * but we dont change the box vectors until we get here
-                 * since we need to be able to shift/unshift above.
-                 */
-                real dtpc = inputrec->nstpcouple * dt;
-                for (int i = 0; i < DIM; i++)
-                {
-                    for (int m = 0; m <= i; m++)
-                    {
-                        state->box[i][m] += dtpc * state->boxv[i][m];
-                    }
-                }
-                preserve_box_shape(inputrec, state->box_rel, state->box);
-
-                /* Scale the coordinates */
-                if (scaleCoordinates)
-                {
-                    auto x = state->x.rvec_array();
-                    for (int n = start; n < start + homenr; n++)
-                    {
-                        tmvmul_ur0(pressureCouplingMu, x[n], x[n]);
-                    }
-                }
-            }
-            break;
-        case (epcMTTK):
-            switch (inputrec->epct)
-            {
-                case (epctISOTROPIC):
-                    /* DIM * eta = ln V.  so DIM*eta_new = DIM*eta_old + DIM*dt*veta =>
-                       ln V_new = ln V_old + 3*dt*veta => V_new = V_old*exp(3*dt*veta) =>
-                       Side length scales as exp(veta*dt) */
-
-                    msmul(state->box, std::exp(state->veta * dt), state->box);
-
-                    /* Relate veta to boxv.  veta = d(eta)/dT = (1/DIM)*1/V dV/dT.
-                       o               If we assume isotropic scaling, and box length scaling
-                       factor L, then V = L^DIM (det(M)).  So dV/dt = DIM
-                       L^(DIM-1) dL/dt det(M), and veta = (1/L) dL/dt.  The
-                       determinant of B is L^DIM det(M), and the determinant
-                       of dB/dt is (dL/dT)^DIM det (M).  veta will be
-                       (det(dB/dT)/det(B))^(1/3).  Then since M =
-                       B_new*(vol_new)^(1/3), dB/dT_new = (veta_new)*B(new). */
-
-                    msmul(state->box, state->veta, state->boxv);
-                    break;
-                default: break;
-            }
-            break;
-        default: break;
+        /* We have no frozen atoms or fully frozen atoms which have not
+         * been moved by the update, so we can simply copy all coordinates.
+         */
+        int gmx_unused nth = gmx_omp_nthreads_get(emntUpdate);
+#pragma omp parallel for num_threads(nth) schedule(static)
+        for (int i = 0; i < homenr; i++)
+        {
+            // Trivial statement, does not throw
+            x[i] = xp[i];
+        }
     }
 
-    if (upd->deform())
-    {
-        auto localX = makeArrayRef(state->x).subArray(start, homenr);
-        upd->deform()->apply(localX, state->box, step);
-    }
+    wallcycle_stop(wcycle, ewcUPDATE);
 }
 
-void update_coords(int64_t           step,
-                   const t_inputrec* inputrec, /* input record and box stuff   */
-                   const t_mdatoms*  md,
-                   t_state*          state,
-                   gmx::ArrayRefWithPadding<const gmx::RVec> f,
-                   const t_fcdata*                           fcd,
-                   const gmx_ekindata_t*                     ekind,
-                   const matrix                              M,
-                   Update*                                   upd,
-                   int                                       UpdatePart,
-                   const t_commrec* cr, /* these shouldn't be here -- need to think about it */
-                   const gmx::Constraints* constr)
+void Update::Impl::update_coords(const t_inputrec&                                inputRecord,
+                                 int64_t                                          step,
+                                 const t_mdatoms*                                 md,
+                                 t_state*                                         state,
+                                 const gmx::ArrayRefWithPadding<const gmx::RVec>& f,
+                                 const t_fcdata&                                  fcdata,
+                                 const gmx_ekindata_t*                            ekind,
+                                 const matrix                                     M,
+                                 int                                              updatePart,
+                                 const t_commrec*                                 cr,
+                                 const bool                                       haveConstraints)
 {
-    gmx_bool bDoConstr = (nullptr != constr);
-
     /* Running the velocity half does nothing except for velocity verlet */
-    if ((UpdatePart == etrtVELOCITY1 || UpdatePart == etrtVELOCITY2) && !EI_VV(inputrec->eI))
+    if ((updatePart == etrtVELOCITY1 || updatePart == etrtVELOCITY2) && !EI_VV(inputRecord.eI))
     {
         gmx_incons("update_coords called for velocity without VV integrator");
     }
@@ -1777,16 +1482,16 @@ void update_coords(int64_t           step,
     int homenr = md->homenr;
 
     /* Cast to real for faster code, no loss in precision (see comment above) */
-    real dt = inputrec->delta_t;
+    real dt = inputRecord.delta_t;
 
     /* We need to update the NMR restraint history when time averaging is used */
     if (state->flags & (1 << estDISRE_RM3TAV))
     {
-        update_disres_history(fcd, &state->hist);
+        update_disres_history(*fcdata.disres, &state->hist);
     }
     if (state->flags & (1 << estORIRE_DTAV))
     {
-        update_orires_history(fcd, &state->hist);
+        update_orires_history(*fcdata.orires, &state->hist);
     }
 
     /* ############# START The update of velocities and positions ######### */
@@ -1801,59 +1506,50 @@ void update_coords(int64_t           step,
             getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
 
             const rvec* x_rvec  = state->x.rvec_array();
-            rvec*       xp_rvec = upd->xp()->rvec_array();
+            rvec*       xp_rvec = xp_.rvec_array();
             rvec*       v_rvec  = state->v.rvec_array();
-            const rvec* f_rvec  = as_rvec_array(f.unpaddedArrayRef().data());
+            const rvec* f_rvec  = as_rvec_array(f.unpaddedConstArrayRef().data());
 
-            switch (inputrec->eI)
+            switch (inputRecord.eI)
             {
                 case (eiMD):
-                    do_update_md(start_th, end_th, step, dt, inputrec, md, ekind, state->box,
-                                 x_rvec, xp_rvec, v_rvec, f_rvec, state->nosehoover_vxi.data(), M);
+                    do_update_md(start_th, end_th, dt, step, x_rvec, xp_rvec, v_rvec, f_rvec,
+                                 inputRecord.opts.acc, inputRecord.etc, inputRecord.epc,
+                                 inputRecord.nsttcouple, inputRecord.nstpcouple, md, ekind,
+                                 state->box, state->nosehoover_vxi.data(), M);
                     break;
                 case (eiSD1):
-                    if (bDoConstr)
-                    {
-                        // With constraints, the SD update is done in 2 parts
-                        doSDUpdateGeneral<SDUpdate::ForcesOnly>(
-                                *upd->sd(), start_th, end_th, dt, inputrec->opts.acc, inputrec->opts.nFreeze,
-                                md->invmass, md->ptype, md->cFREEZE, md->cACC, nullptr, x_rvec,
-                                xp_rvec, v_rvec, f_rvec, step, inputrec->ld_seed, nullptr);
-                    }
-                    else
-                    {
-                        doSDUpdateGeneral<SDUpdate::Combined>(
-                                *upd->sd(), start_th, end_th, dt, inputrec->opts.acc,
-                                inputrec->opts.nFreeze, md->invmass, md->ptype, md->cFREEZE, md->cACC,
-                                md->cTC, x_rvec, xp_rvec, v_rvec, f_rvec, step, inputrec->ld_seed,
-                                DOMAINDECOMP(cr) ? cr->dd->globalAtomIndices.data() : nullptr);
-                    }
+                    do_update_sd(start_th, end_th, dt, step, x_rvec, xp_rvec, v_rvec, f_rvec,
+                                 inputRecord.opts.acc, inputRecord.opts.nFreeze, md->invmass,
+                                 md->ptype, md->cFREEZE, md->cACC, md->cTC, inputRecord.ld_seed, cr,
+                                 sd_, haveConstraints);
                     break;
                 case (eiBD):
-                    do_update_bd(start_th, end_th, dt, inputrec->opts.nFreeze, md->invmass,
-                                 md->ptype, md->cFREEZE, md->cTC, x_rvec, xp_rvec, v_rvec, f_rvec,
-                                 inputrec->bd_fric, upd->sd()->bd_rf.data(), step, inputrec->ld_seed,
+                    do_update_bd(start_th, end_th, dt, step, x_rvec, xp_rvec, v_rvec, f_rvec,
+                                 inputRecord.opts.nFreeze, md->invmass, md->ptype, md->cFREEZE,
+                                 md->cTC, inputRecord.bd_fric, sd_.bd_rf.data(), inputRecord.ld_seed,
                                  DOMAINDECOMP(cr) ? cr->dd->globalAtomIndices.data() : nullptr);
                     break;
                 case (eiVV):
                 case (eiVVAK):
                 {
-                    gmx_bool bExtended = (inputrec->etc == etcNOSEHOOVER || inputrec->epc == epcPARRINELLORAHMAN
-                                          || inputrec->epc == epcMTTK);
+                    gmx_bool bExtended = (inputRecord.etc == etcNOSEHOOVER || inputRecord.epc == epcPARRINELLORAHMAN
+                                          || inputRecord.epc == epcMTTK);
 
                     /* assuming barostat coupled to group 0 */
-                    real alpha = 1.0 + DIM / static_cast<real>(inputrec->opts.nrdf[0]);
-                    switch (UpdatePart)
+                    real alpha = 1.0 + DIM / static_cast<real>(inputRecord.opts.nrdf[0]);
+                    switch (updatePart)
                     {
                         case etrtVELOCITY1:
                         case etrtVELOCITY2:
-                            do_update_vv_vel(start_th, end_th, dt, inputrec->opts.acc,
-                                             inputrec->opts.nFreeze, md->invmass, md->ptype, md->cFREEZE,
+                            do_update_vv_vel(start_th, end_th, dt, inputRecord.opts.acc,
+                                             inputRecord.opts.nFreeze, md->invmass, md->ptype, md->cFREEZE,
                                              md->cACC, v_rvec, f_rvec, bExtended, state->veta, alpha);
                             break;
                         case etrtPOSITION:
-                            do_update_vv_pos(start_th, end_th, dt, inputrec->opts.nFreeze, md->ptype,
-                                             md->cFREEZE, x_rvec, xp_rvec, v_rvec, bExtended, state->veta);
+                            do_update_vv_pos(start_th, end_th, dt, inputRecord.opts.nFreeze,
+                                             md->ptype, md->cFREEZE, x_rvec, xp_rvec, v_rvec,
+                                             bExtended, state->veta);
                             break;
                     }
                     break;
@@ -1865,36 +1561,36 @@ void update_coords(int64_t           step,
     }
 }
 
-extern gmx_bool update_randomize_velocities(const t_inputrec*        ir,
-                                            int64_t                  step,
-                                            const t_commrec*         cr,
-                                            const t_mdatoms*         md,
-                                            gmx::ArrayRef<gmx::RVec> v,
-                                            const Update*            upd,
-                                            const gmx::Constraints*  constr)
+void Update::Impl::update_for_constraint_virial(const t_inputrec& inputRecord,
+                                                const t_mdatoms&  md,
+                                                const t_state&    state,
+                                                const gmx::ArrayRefWithPadding<const gmx::RVec>& f,
+                                                const gmx_ekindata_t& ekind)
 {
+    GMX_ASSERT(inputRecord.eI == eiMD || inputRecord.eI == eiSD1,
+               "Only leap-frog is supported here");
 
-    real rate = (ir->delta_t) / ir->opts.tau_t[0];
+    // Cast to real for faster code, no loss in precision
+    const real dt = inputRecord.delta_t;
 
-    if (ir->etc == etcANDERSEN && constr != nullptr)
-    {
-        /* Currently, Andersen thermostat does not support constrained
-           systems. Functionality exists in the andersen_tcoupl
-           function in GROMACS 4.5.7 to allow this combination. That
-           code could be ported to the current random-number
-           generation approach, but has not yet been done because of
-           lack of time and resources. */
-        gmx_fatal(FARGS,
-                  "Normal Andersen is currently not supported with constraints, use massive "
-                  "Andersen instead");
-    }
+    const int nth = gmx_omp_nthreads_get(emntUpdate);
 
-    /* proceed with andersen if 1) it's fixed probability per
-       particle andersen or 2) it's massive andersen and it's tau_t/dt */
-    if ((ir->etc == etcANDERSEN) || do_per_step(step, roundToInt(1.0 / rate)))
+#pragma omp parallel for num_threads(nth) schedule(static)
+    for (int th = 0; th < nth; th++)
     {
-        andersen_tcoupl(ir, step, cr, md, v, rate, upd->sd()->randomize_group, upd->sd()->boltzfac);
-        return TRUE;
+        try
+        {
+            int start_th, end_th;
+            getThreadAtomRange(nth, th, md.homenr, &start_th, &end_th);
+
+            const rvec* x_rvec  = state.x.rvec_array();
+            rvec*       xp_rvec = xp_.rvec_array();
+            rvec*       v_rvec  = const_cast<rvec*>(state.v.rvec_array());
+            const rvec* f_rvec  = as_rvec_array(f.unpaddedConstArrayRef().data());
+
+            doUpdateMDDoNotUpdateVelocities(start_th, end_th, dt, x_rvec, xp_rvec, v_rvec, f_rvec,
+                                            md, ekind);
+        }
+        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
     }
-    return FALSE;
 }
index 472d5e6bbf8a1c0ebcdb62c5585e061f3d0a3bdc..514f8e76482a13eff985864effe28f8044ca984e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,7 +50,7 @@
 class ekinstate_t;
 struct gmx_ekindata_t;
 struct gmx_enerdata_t;
-struct t_extmass;
+enum class PbcType;
 struct t_fcdata;
 struct t_graph;
 struct t_grpopts;
@@ -58,9 +59,6 @@ struct t_mdatoms;
 struct t_nrnb;
 class t_state;
 
-/* Abstract type for update */
-struct gmx_stochd_t;
-
 namespace gmx
 {
 class BoxDeformation;
@@ -72,18 +70,134 @@ class Constraints;
 class Update
 {
 public:
-    //! Constructor
-    Update(const t_inputrec* ir, BoxDeformation* boxDeformation);
+    /*! \brief Constructor
+     *
+     * \param[in] inputRecord     Input record, used to construct SD object.
+     * \param[in] boxDeformation  Periodic box deformation object.
+     */
+    Update(const t_inputrec& inputRecord, BoxDeformation* boxDeformation);
+    //! Destructor
     ~Update();
-    // TODO Get rid of getters when more free functions are incorporated as member methods
-    //! Returns handle to stochd_t struct
-    gmx_stochd_t* sd() const;
-    //! Returns pointer to PaddedVector xp
+    /*! \brief Get the pointer to updated coordinates
+     *
+     * Update saves the updated coordinates into separate buffer, so that constraints will have
+     * access to both updated and not update coordinates. For that, update owns a separate buffer.
+     * See finish_update(...) for details.
+     *
+     * \returns The pointer to the intermediate coordinates buffer.
+     */
     PaddedVector<gmx::RVec>* xp();
-    //! Returns handle to box deformation class
+    /*!\brief Getter to local copy of box deformation class.
+     *
+     * \returns handle to box deformation class
+     */
     BoxDeformation* deform() const;
-    //! Resizes xp
-    void setNumAtoms(int nAtoms);
+    /*! \brief Resizes buffer that stores intermediate coordinates.
+     *
+     * \param[in] numAtoms  Updated number of atoms.
+     */
+    void setNumAtoms(int numAtoms);
+
+    /*! \brief Perform numerical integration step.
+     *
+     * Selects the appropriate integrator, based on the input record and performs a numerical integration step.
+     *
+     * \param[in]  inputRecord      Input record.
+     * \param[in]  step             Current timestep.
+     * \param[in]  md               MD atoms data.
+     * \param[in]  state            System state object.
+     * \param[in]  f                Buffer with atomic forces for home particles.
+     * \param[in]  fcdata           Force calculation data to update distance and orientation restraints.
+     * \param[in]  ekind            Kinetic energy data (for temperature coupling, energy groups, etc.).
+     * \param[in]  M                Parrinello-Rahman velocity scaling matrix.
+     * \param[in]  updatePart       What should be updated, coordinates or velocities. This enum only used in VV integrator.
+     * \param[in]  cr               Comunication record  (Old comment: these shouldn't be here -- need to think about it).
+     * \param[in]  haveConstraints  If the system has constraints.
+     */
+    void update_coords(const t_inputrec&                                inputRecord,
+                       int64_t                                          step,
+                       const t_mdatoms*                                 md,
+                       t_state*                                         state,
+                       const gmx::ArrayRefWithPadding<const gmx::RVec>& f,
+                       const t_fcdata&                                  fcdata,
+                       const gmx_ekindata_t*                            ekind,
+                       const matrix                                     M,
+                       int                                              updatePart,
+                       const t_commrec*                                 cr,
+                       bool                                             haveConstraints);
+
+    /*! \brief Finalize the coordinate update.
+     *
+     * Copy the updated coordinates to the main coordinates buffer for the atoms that are not frozen.
+     *
+     * \param[in]  inputRecord      Input record.
+     * \param[in]  md               MD atoms data.
+     * \param[in]  state            System state object.
+     * \param[in]  wcycle           Wall-clock cycle counter.
+     * \param[in]  haveConstraints  If the system has constraints.
+     */
+    void finish_update(const t_inputrec& inputRecord,
+                       const t_mdatoms*  md,
+                       t_state*          state,
+                       gmx_wallcycle_t   wcycle,
+                       bool              haveConstraints);
+
+    /*! \brief Secong part of the SD integrator.
+     *
+     * The first part of integration is performed in the update_coords(...) method.
+     *
+     * \param[in]  inputRecord  Input record.
+     * \param[in]  step         Current timestep.
+     * \param[in]  dvdlambda    Free energy derivative. Contribution to be added to the bonded
+     * interactions. \param[in]  md           MD atoms data. \param[in]  state        System state
+     * object. \param[in]  cr           Comunication record. \param[in]  nrnb         Cycle
+     * counters. \param[in]  wcycle       Wall-clock cycle counter. \param[in]  constr Constraints
+     * object. The constraints are applied on coordinates after update. \param[in]  do_log       If
+     * this is logging step. \param[in]  do_ene       If this is an energy evaluation step.
+     */
+    void update_sd_second_half(const t_inputrec& inputRecord,
+                               int64_t           step,
+                               real*             dvdlambda,
+                               const t_mdatoms*  md,
+                               t_state*          state,
+                               const t_commrec*  cr,
+                               t_nrnb*           nrnb,
+                               gmx_wallcycle_t   wcycle,
+                               gmx::Constraints* constr,
+                               bool              do_log,
+                               bool              do_ene);
+
+    /*! \brief Performs a leap-frog update without updating \p state so the constrain virial
+     * can be computed.
+     */
+    void update_for_constraint_virial(const t_inputrec&                                inputRecord,
+                                      const t_mdatoms&                                 md,
+                                      const t_state&                                   state,
+                                      const gmx::ArrayRefWithPadding<const gmx::RVec>& f,
+                                      const gmx_ekindata_t&                            ekind);
+
+    /*! \brief Update pre-computed constants that depend on the reference temperature for coupling.
+     *
+     * This could change e.g. in simulated annealing.
+     *
+     * \param[in]  inputRecord  Input record.
+     */
+    void update_temperature_constants(const t_inputrec& inputRecord);
+
+    /*!\brief Getter for the list of the randomize groups.
+     *
+     *  Needed for Andersen temperature control.
+     *
+     * \returns Reference to the groups from the SD data object.
+     */
+    const std::vector<bool>& getAndersenRandomizeGroup() const;
+    /*!\brief Getter for the list of the Boltzmann factors.
+     *
+     *  Needed for Andersen temperature control.
+     *
+     * \returns Reference to the Boltzmann factors from the SD data object.
+     */
+    const std::vector<real>& getBoltzmanFactor() const;
 
 private:
     //! Implementation type.
@@ -94,123 +208,6 @@ private:
 
 }; // namespace gmx
 
-/* Update pre-computed constants that depend on the reference
- * temperature for coupling.
- *
- * This could change e.g. in simulated annealing. */
-void update_temperature_constants(gmx_stochd_t* sd, const t_inputrec* ir);
-
-/* Update the size of per-atom arrays (e.g. after DD re-partitioning,
-   which might increase the number of home atoms). */
-
-void update_tcouple(int64_t           step,
-                    const t_inputrec* inputrec,
-                    t_state*          state,
-                    gmx_ekindata_t*   ekind,
-                    const t_extmass*  MassQ,
-                    const t_mdatoms*  md);
-
-/* Update Parrinello-Rahman, to be called before the coordinate update */
-void update_pcouple_before_coordinates(FILE*             fplog,
-                                       int64_t           step,
-                                       const t_inputrec* inputrec,
-                                       t_state*          state,
-                                       matrix            parrinellorahmanMu,
-                                       matrix            M,
-                                       gmx_bool          bInitStep);
-
-/* Update the box, to be called after the coordinate update.
- * For Berendsen P-coupling, also calculates the scaling factor
- * and scales the coordinates.
- * When the deform option is used, scales coordinates and box here.
- */
-void update_pcouple_after_coordinates(FILE*             fplog,
-                                      int64_t           step,
-                                      const t_inputrec* inputrec,
-                                      const t_mdatoms*  md,
-                                      const matrix      pressure,
-                                      const matrix      forceVirial,
-                                      const matrix      constraintVirial,
-                                      matrix            pressureCouplingMu,
-                                      t_state*          state,
-                                      t_nrnb*           nrnb,
-                                      gmx::Update*      upd,
-                                      bool              scaleCoordinates);
-
-void update_coords(int64_t           step,
-                   const t_inputrec* inputrec, /* input record and box stuff   */
-                   const t_mdatoms*  md,
-                   t_state*          state,
-                   gmx::ArrayRefWithPadding<const gmx::RVec> f, /* forces on home particles */
-                   const t_fcdata*                           fcd,
-                   const gmx_ekindata_t*                     ekind,
-                   const matrix                              M,
-                   gmx::Update*                              upd,
-                   int                                       bUpdatePart,
-                   const t_commrec* cr, /* these shouldn't be here -- need to think about it */
-                   const gmx::Constraints* constr);
-
-/* Return TRUE if OK, FALSE in case of Shake Error */
-
-extern gmx_bool update_randomize_velocities(const t_inputrec*        ir,
-                                            int64_t                  step,
-                                            const t_commrec*         cr,
-                                            const t_mdatoms*         md,
-                                            gmx::ArrayRef<gmx::RVec> v,
-                                            const gmx::Update*       upd,
-                                            const gmx::Constraints*  constr);
-
-void constrain_velocities(int64_t step,
-                          real* dvdlambda, /* the contribution to be added to the bonded interactions */
-                          t_state*          state,
-                          tensor            vir_part,
-                          gmx::Constraints* constr,
-                          gmx_bool          bCalcVir,
-                          bool              do_log,
-                          bool              do_ene);
-
-void constrain_coordinates(int64_t step,
-                           real* dvdlambda, /* the contribution to be added to the bonded interactions */
-                           t_state*          state,
-                           tensor            vir_part,
-                           gmx::Update*      upd,
-                           gmx::Constraints* constr,
-                           gmx_bool          bCalcVir,
-                           bool              do_log,
-                           bool              do_ene);
-
-void update_sd_second_half(int64_t step,
-                           real* dvdlambda, /* the contribution to be added to the bonded interactions */
-                           const t_inputrec* inputrec, /* input record and box stuff */
-                           const t_mdatoms*  md,
-                           t_state*          state,
-                           const t_commrec*  cr,
-                           t_nrnb*           nrnb,
-                           gmx_wallcycle_t   wcycle,
-                           gmx::Update*      upd,
-                           gmx::Constraints* constr,
-                           bool              do_log,
-                           bool              do_ene);
-
-void finish_update(const t_inputrec*       inputrec,
-                   const t_mdatoms*        md,
-                   t_state*                state,
-                   const t_graph*          graph,
-                   t_nrnb*                 nrnb,
-                   gmx_wallcycle_t         wcycle,
-                   gmx::Update*            upd,
-                   const gmx::Constraints* constr);
-
-/* Return TRUE if OK, FALSE in case of Shake Error */
-
-void calc_ke_part(const rvec*      x,
-                  const rvec*      v,
-                  const matrix     box,
-                  const t_grpopts* opts,
-                  const t_mdatoms* md,
-                  gmx_ekindata_t*  ekind,
-                  t_nrnb*          nrnb,
-                  gmx_bool         bEkinAveVel);
 /*
  * Compute the partial kinetic energy for home particles;
  * will be accumulated in the calling routine.
@@ -236,104 +233,6 @@ void update_ekinstate(ekinstate_t* ekinstate, const gmx_ekindata_t* ekind);
    to the rest of the simulation */
 void restore_ekinstate_from_state(const t_commrec* cr, gmx_ekindata_t* ekind, const ekinstate_t* ekinstate);
 
-void berendsen_tcoupl(const t_inputrec*    ir,
-                      gmx_ekindata_t*      ekind,
-                      real                 dt,
-                      std::vector<double>& therm_integral); //NOLINT(google-runtime-references)
-
-void andersen_tcoupl(const t_inputrec*         ir,
-                     int64_t                   step,
-                     const t_commrec*          cr,
-                     const t_mdatoms*          md,
-                     gmx::ArrayRef<gmx::RVec>  v,
-                     real                      rate,
-                     const std::vector<bool>&  randomize,
-                     gmx::ArrayRef<const real> boltzfac);
-
-void nosehoover_tcoupl(const t_grpopts*      opts,
-                       const gmx_ekindata_t* ekind,
-                       real                  dt,
-                       double                xi[],
-                       double                vxi[],
-                       const t_extmass*      MassQ);
-
-void trotter_update(const t_inputrec*               ir,
-                    int64_t                         step,
-                    gmx_ekindata_t*                 ekind,
-                    const gmx_enerdata_t*           enerd,
-                    t_state*                        state,
-                    const tensor                    vir,
-                    const t_mdatoms*                md,
-                    const t_extmass*                MassQ,
-                    gmx::ArrayRef<std::vector<int>> trotter_seqlist,
-                    int                             trotter_seqno);
-
-std::array<std::vector<int>, ettTSEQMAX>
-init_npt_vars(const t_inputrec* ir, t_state* state, t_extmass* Mass, gmx_bool bTrotter);
-
-real NPT_energy(const t_inputrec* ir, const t_state* state, const t_extmass* MassQ);
-/* computes all the pressure/tempertature control energy terms to get a conserved energy */
-
-void vrescale_tcoupl(const t_inputrec* ir, int64_t step, gmx_ekindata_t* ekind, real dt, double therm_integral[]);
-/* Compute temperature scaling. For V-rescale it is done in update. */
-
-void rescale_velocities(const gmx_ekindata_t* ekind, const t_mdatoms* mdatoms, int start, int end, rvec v[]);
-/* Rescale the velocities with the scaling factor in ekind */
-
-//! Check whether we do simulated annealing.
-bool doSimulatedAnnealing(const t_inputrec* ir);
-
-//! Initialize simulated annealing.
-bool initSimulatedAnnealing(t_inputrec* ir, gmx::Update* upd);
-
-// TODO: This is the only function in update.h altering the inputrec
-void update_annealing_target_temp(t_inputrec* ir, real t, gmx::Update* upd);
-/* Set reference temp for simulated annealing at time t*/
-
-real calc_temp(real ekin, real nrdf);
-/* Calculate the temperature */
-
-real calc_pres(int ePBC, int nwall, const matrix box, const tensor ekin, const tensor vir, tensor pres);
-/* Calculate the pressure tensor, returns the scalar pressure.
- * The unit of pressure is bar.
- */
-
-void parrinellorahman_pcoupl(FILE*             fplog,
-                             int64_t           step,
-                             const t_inputrec* ir,
-                             real              dt,
-                             const tensor      pres,
-                             const tensor      box,
-                             tensor            box_rel,
-                             tensor            boxv,
-                             tensor            M,
-                             matrix            mu,
-                             gmx_bool          bFirstStep);
-
-void berendsen_pcoupl(FILE*             fplog,
-                      int64_t           step,
-                      const t_inputrec* ir,
-                      real              dt,
-                      const tensor      pres,
-                      const matrix      box,
-                      const matrix      force_vir,
-                      const matrix      constraint_vir,
-                      matrix            mu,
-                      double*           baros_integral);
-
-void berendsen_pscale(const t_inputrec*    ir,
-                      const matrix         mu,
-                      matrix               box,
-                      matrix               box_rel,
-                      int                  start,
-                      int                  nr_atoms,
-                      rvec                 x[],
-                      const unsigned short cFREEZE[],
-                      t_nrnb*              nrnb,
-                      bool                 scaleCoordinates);
-
-void pleaseCiteCouplingAlgorithms(FILE* fplog, const t_inputrec& ir);
-
 /*! \brief Computes the atom range for a thread to operate on, ensuring SIMD aligned ranges
  *
  * \param[in]  numThreads   The number of threads to divide atoms over
@@ -344,24 +243,4 @@ void pleaseCiteCouplingAlgorithms(FILE* fplog, const t_inputrec& ir);
  */
 void getThreadAtomRange(int numThreads, int threadIndex, int numAtoms, int* startAtom, int* endAtom);
 
-/*! \brief Generate a new kinetic energy for the v-rescale thermostat
- *
- * Generates a new value for the kinetic energy, according to
- * Bussi et al JCP (2007), Eq. (A7)
- *
- * This is used by update_tcoupl(), and by the VRescaleThermostat of the modular
- * simulator.
- * TODO: Move this to the VRescaleThermostat once the modular simulator becomes
- *       the default code path.
- *
- * @param kk     present value of the kinetic energy of the atoms to be thermalized (in arbitrary units)
- * @param sigma  target average value of the kinetic energy (ndeg k_b T/2)  (in the same units as kk)
- * @param ndeg   number of degrees of freedom of the atoms to be thermalized
- * @param taut   relaxation time of the thermostat, in units of 'how often this routine is called'
- * @param step   the time step this routine is called on
- * @param seed   the random number generator seed
- * @return  the new kinetic energy
- */
-real vrescale_resamplekin(real kk, real sigma, real ndeg, real taut, int64_t step, int64_t seed);
-
 #endif
similarity index 77%
rename from src/gromacs/mdlib/update_constrain_cuda.h
rename to src/gromacs/mdlib/update_constrain_gpu.h
index 5adca1c4339957059184cd00066d853f34071a7b..a30cbe825d305c92ccf9c211014bda5032b52dcc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  */
 /*! \libinternal \file
  *
- * \brief Declaration of high-level functions of CUDA implementation of update and constrain class.
- *
- * \todo Change "cuda" suffix to "gpu"
+ * \brief Declaration of high-level functions of GPU implementation of update and constrain class.
  *
  * \author Artem Zhmurov <zhmurov@gmail.com>
  *
  * \ingroup module_mdlib
  * \inlibraryapi
  */
-#ifndef GMX_MDLIB_UPDATE_CONSTRAIN_CUDA_H
-#define GMX_MDLIB_UPDATE_CONSTRAIN_CUDA_H
+#ifndef GMX_MDLIB_UPDATE_CONSTRAIN_GPU_H
+#define GMX_MDLIB_UPDATE_CONSTRAIN_GPU_H
 
 #include "gromacs/gpu_utils/devicebuffer_datatype.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/classhelpers.h"
 
+class DeviceContext;
+class DeviceStream;
 class GpuEventSynchronizer;
-
 struct gmx_mtop_t;
-struct t_idef;
+enum class PbcType : int;
+class InteractionDefinitions;
 struct t_inputrec;
 struct t_mdatoms;
 struct t_pbc;
@@ -62,13 +62,13 @@ struct t_pbc;
 namespace gmx
 {
 
-class UpdateConstrainCuda
+class UpdateConstrainGpu
 {
 
 public:
     /*! \brief Create Update-Constrain object.
      *
-     * The constructor is given a non-nullptr \p commandStream, in which all the update and constrain
+     * The constructor is given a non-nullptr \p deviceStream, in which all the update and constrain
      * routines are executed. \p xUpdatedOnDevice should mark the completion of all kernels that modify
      * coordinates. The event is maintained outside this class and also passed to all (if any) consumers
      * of the updated coordinates. The \p xUpdatedOnDevice also can not be a nullptr because the
@@ -78,15 +78,17 @@ public:
      *                              projection from it.
      * \param[in] mtop              Topology of the system: SETTLE gets the masses for O and H atoms
      *                              and target O-H and H-H distances from this object.
-     * \param[in] commandStream     GPU stream to use. Can be nullptr.
+     * \param[in] deviceContext     GPU device context.
+     * \param[in] deviceStream      GPU stream to use.
      * \param[in] xUpdatedOnDevice  The event synchronizer to use to mark that update is done on the GPU.
      */
-    UpdateConstrainCuda(const t_inputrec&     ir,
-                        const gmx_mtop_t&     mtop,
-                        const void*           commandStream,
-                        GpuEventSynchronizer* xUpdatedOnDevice);
+    UpdateConstrainGpu(const t_inputrec&     ir,
+                       const gmx_mtop_t&     mtop,
+                       const DeviceContext&  deviceContext,
+                       const DeviceStream&   deviceStream,
+                       GpuEventSynchronizer* xUpdatedOnDevice);
 
-    ~UpdateConstrainCuda();
+    ~UpdateConstrainGpu();
 
     /*! \brief Integrate
      *
@@ -125,6 +127,14 @@ public:
      */
     void scaleCoordinates(const matrix scalingMatrix);
 
+    /*! \brief Scale velocities on the GPU for the pressure coupling.
+     *
+     * After pressure coupling step, the box size may change. In the C-Rescale algorithm, velocities should be scaled.
+     *
+     * \param[in] scalingMatrix Velocities scaling matrix.
+     */
+    void scaleVelocities(const matrix scalingMatrix);
+
     /*! \brief Set the pointers and update data-structures (e.g. after NB search step).
      *
      * \param[in,out]  d_x                 Device buffer with coordinates.
@@ -134,21 +144,22 @@ public:
      * \param[in]      md                  Atoms data.
      * \param[in]      numTempScaleValues  Number of temperature scaling groups. Zero for no temperature scaling.
      */
-    void set(DeviceBuffer<float> d_x,
-             DeviceBuffer<float> d_v,
-             DeviceBuffer<float> d_f,
-             const t_idef&       idef,
-             const t_mdatoms&    md,
-             int                 numTempScaleValues);
+    void set(DeviceBuffer<RVec>            d_x,
+             DeviceBuffer<RVec>            d_v,
+             DeviceBuffer<RVec>            d_f,
+             const InteractionDefinitions& idef,
+             const t_mdatoms&              md,
+             int                           numTempScaleValues);
 
     /*! \brief
      * Update PBC data.
      *
      * Converts PBC data from t_pbc into the PbcAiuc format and stores the latter.
      *
-     * \param[in] pbc The PBC data in t_pbc format.
+     * \param[in] pbcType The type of the periodic boundary (Xyz, NO, XY or Screw).
+     * \param[in] box     The periodic boundary box matrix.
      */
-    void setPbc(const t_pbc* pbc);
+    void setPbc(PbcType pbcType, const matrix box);
 
     /*! \brief Return the synchronizer associated with the event indicated that the coordinates are ready on the device.
      */
@@ -156,7 +167,7 @@ public:
 
     /*! \brief
      * Returns whether the maximum number of coupled constraints is supported
-     * by the CUDA LINCS code.
+     * by the GPU LINCS code.
      *
      * \param[in] mtop The molecular topology
      */
@@ -169,4 +180,4 @@ private:
 
 } // namespace gmx
 
-#endif
+#endif // GMX_MDLIB_UPDATE_CONSTRAIN_GPU_H
similarity index 51%
rename from src/gromacs/mdlib/update_constrain_cuda_impl.cpp
rename to src/gromacs/mdlib/update_constrain_gpu_impl.cpp
index adbf2f5ba5ce60293f1276ebe8f3730e8ca5d3ac..5c4afd2acd0757b48298ff9f3b8220ab71250319 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "config.h"
 
-#include "gromacs/mdlib/update_constrain_cuda.h"
+#include "gromacs/mdlib/update_constrain_gpu.h"
+#include "gromacs/utility/gmxassert.h"
 
-#if GMX_GPU != GMX_GPU_CUDA
+#if !GMX_GPU_CUDA
 
 namespace gmx
 {
 
-class UpdateConstrainCuda::Impl
+class UpdateConstrainGpu::Impl
 {
 };
 
-UpdateConstrainCuda::UpdateConstrainCuda(const t_inputrec& /* ir   */,
-                                         const gmx_mtop_t& /* mtop */,
-                                         const void* /* commandStream */,
-                                         GpuEventSynchronizer* /* xUpdatedOnDevice */) :
+UpdateConstrainGpu::UpdateConstrainGpu(const t_inputrec& /* ir   */,
+                                       const gmx_mtop_t& /* mtop */,
+                                       const DeviceContext& /* deviceContext */,
+                                       const DeviceStream& /* deviceStream */,
+                                       GpuEventSynchronizer* /* xUpdatedOnDevice */) :
     impl_(nullptr)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for UpdateConstrain was called instead of the correct implementation.");
 }
 
-UpdateConstrainCuda::~UpdateConstrainCuda() = default;
-
-void UpdateConstrainCuda::integrate(GpuEventSynchronizer* /* fReadyOnDevice */,
-                                    const real /* dt */,
-                                    const bool /* updateVelocities */,
-                                    const bool /* computeVirial */,
-                                    tensor /* virialScaled */,
-                                    const bool /* doTemperatureScaling */,
-                                    gmx::ArrayRef<const t_grp_tcstat> /* tcstat */,
-                                    const bool /* doParrinelloRahman */,
-                                    const float /* dtPressureCouple */,
-                                    const matrix /* prVelocityScalingMatrix*/)
+UpdateConstrainGpu::~UpdateConstrainGpu() = default;
+
+void UpdateConstrainGpu::integrate(GpuEventSynchronizer* /* fReadyOnDevice */,
+                                   const real /* dt */,
+                                   const bool /* updateVelocities */,
+                                   const bool /* computeVirial */,
+                                   tensor /* virialScaled */,
+                                   const bool /* doTemperatureScaling */,
+                                   gmx::ArrayRef<const t_grp_tcstat> /* tcstat */,
+                                   const bool /* doParrinelloRahman */,
+                                   const float /* dtPressureCouple */,
+                                   const matrix /* prVelocityScalingMatrix*/)
+{
+    GMX_ASSERT(!impl_,
+               "A CPU stub for UpdateConstrain was called instead of the correct implementation.");
+}
+
+void UpdateConstrainGpu::scaleCoordinates(const matrix /* scalingMatrix */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for UpdateConstrain was called instead of the correct implementation.");
 }
 
-void UpdateConstrainCuda::scaleCoordinates(const matrix /* scalingMatrix */)
+void UpdateConstrainGpu::scaleVelocities(const matrix /* scalingMatrix */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for UpdateConstrain was called instead of the correct implementation.");
 }
 
-void UpdateConstrainCuda::set(DeviceBuffer<float> /* d_x */,
-                              DeviceBuffer<float> /* d_v */,
-                              const DeviceBuffer<float> /* d_f */,
-                              const t_idef& /* idef */,
-                              const t_mdatoms& /* md */,
-                              const int /* numTempScaleValues */)
+void UpdateConstrainGpu::set(DeviceBuffer<RVec> /* d_x */,
+                             DeviceBuffer<RVec> /* d_v */,
+                             const DeviceBuffer<RVec> /* d_f */,
+                             const InteractionDefinitions& /* idef */,
+                             const t_mdatoms& /* md */,
+                             const int /* numTempScaleValues */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for UpdateConstrain was called instead of the correct implementation.");
 }
 
-void UpdateConstrainCuda::setPbc(const t_pbc* /* pbc */)
+void UpdateConstrainGpu::setPbc(const PbcType /* pbcType */, const matrix /* box */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for UpdateConstrain was called instead of the correct implementation.");
 }
 
-GpuEventSynchronizer* UpdateConstrainCuda::getCoordinatesReadySync()
+GpuEventSynchronizer* UpdateConstrainGpu::getCoordinatesReadySync()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub for UpdateConstrain was called instead of the correct implementation.");
     return nullptr;
 }
 
-bool UpdateConstrainCuda::isNumCoupledConstraintsSupported(const gmx_mtop_t& /* mtop */)
+bool UpdateConstrainGpu::isNumCoupledConstraintsSupported(const gmx_mtop_t& /* mtop */)
 {
     return false;
 }
 
 } // namespace gmx
 
-#endif /* GMX_GPU != GMX_GPU_CUDA */
+#endif /* !GMX_GPU_CUDA */
similarity index 50%
rename from src/gromacs/mdlib/update_constrain_cuda_impl.cu
rename to src/gromacs/mdlib/update_constrain_gpu_impl.cu
index c11a74ad819ba0a58154f6f513723184bf75ee01..ac92d8f36977461c41e0092f0336b3e57757b558 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,7 +47,7 @@
  */
 #include "gmxpre.h"
 
-#include "update_constrain_cuda_impl.h"
+#include "update_constrain_gpu_impl.h"
 
 #include <assert.h>
 #include <stdio.h>
 #include <algorithm>
 
 #include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/devicebuffer.h"
 #include "gromacs/gpu_utils/gputraits.cuh"
 #include "gromacs/gpu_utils/vectype_ops.cuh"
-#include "gromacs/mdlib/leapfrog_cuda.cuh"
-#include "gromacs/mdlib/lincs_cuda.cuh"
-#include "gromacs/mdlib/settle_cuda.cuh"
-#include "gromacs/mdlib/update_constrain_cuda.h"
+#include "gromacs/mdlib/leapfrog_gpu.cuh"
+#include "gromacs/mdlib/lincs_gpu.cuh"
+#include "gromacs/mdlib/settle_gpu.cuh"
+#include "gromacs/mdlib/update_constrain_gpu.h"
+#include "gromacs/mdtypes/mdatom.h"
 
 namespace gmx
 {
@@ -102,33 +105,33 @@ __launch_bounds__(c_maxThreadsPerBlock) __global__
     }
 }
 
-void UpdateConstrainCuda::Impl::integrate(GpuEventSynchronizer*             fReadyOnDevice,
-                                          const real                        dt,
-                                          const bool                        updateVelocities,
-                                          const bool                        computeVirial,
-                                          tensor                            virial,
-                                          const bool                        doTemperatureScaling,
-                                          gmx::ArrayRef<const t_grp_tcstat> tcstat,
-                                          const bool                        doParrinelloRahman,
-                                          const float                       dtPressureCouple,
-                                          const matrix                      prVelocityScalingMatrix)
+void UpdateConstrainGpu::Impl::integrate(GpuEventSynchronizer*             fReadyOnDevice,
+                                         const real                        dt,
+                                         const bool                        updateVelocities,
+                                         const bool                        computeVirial,
+                                         tensor                            virial,
+                                         const bool                        doTemperatureScaling,
+                                         gmx::ArrayRef<const t_grp_tcstat> tcstat,
+                                         const bool                        doParrinelloRahman,
+                                         const float                       dtPressureCouple,
+                                         const matrix                      prVelocityScalingMatrix)
 {
     // Clearing virial matrix
     // TODO There is no point in having separate virial matrix for constraints
     clear_mat(virial);
 
     // Make sure that the forces are ready on device before proceeding with the update.
-    fReadyOnDevice->enqueueWaitEvent(commandStream_);
+    fReadyOnDevice->enqueueWaitEvent(deviceStream_);
 
-    // The integrate should save a copy of the current coordinates in d_xp_ and write updated once
-    // into d_x_. The d_xp_ is only needed by constraints.
+    // The integrate should save a copy of the current coordinates in d_xp_ and write updated
+    // once into d_x_. The d_xp_ is only needed by constraints.
     integrator_->integrate(d_x_, d_xp_, d_v_, d_f_, dt, doTemperatureScaling, tcstat,
                            doParrinelloRahman, dtPressureCouple, prVelocityScalingMatrix);
     // Constraints need both coordinates before (d_x_) and after (d_xp_) update. However, after constraints
     // are applied, the d_x_ can be discarded. So we intentionally swap the d_x_ and d_xp_ here to avoid the
     // d_xp_ -> d_x_ copy after constraints. Note that the integrate saves them in the wrong order as well.
-    lincsCuda_->apply(d_xp_, d_x_, updateVelocities, d_v_, 1.0 / dt, computeVirial, virial);
-    settleCuda_->apply(d_xp_, d_x_, updateVelocities, d_v_, 1.0 / dt, computeVirial, virial);
+    lincsGpu_->apply(d_xp_, d_x_, updateVelocities, d_v_, 1.0 / dt, computeVirial, virial, pbcAiuc_);
+    settleGpu_->apply(d_xp_, d_x_, updateVelocities, d_v_, 1.0 / dt, computeVirial, virial, pbcAiuc_);
 
     // scaledVirial -> virial (methods above returns scaled values)
     float scaleFactor = 0.5f / (dt * dt);
@@ -140,12 +143,12 @@ void UpdateConstrainCuda::Impl::integrate(GpuEventSynchronizer*             fRea
         }
     }
 
-    coordinatesReady_->markEvent(commandStream_);
+    coordinatesReady_->markEvent(deviceStream_);
 
     return;
 }
 
-void UpdateConstrainCuda::Impl::scaleCoordinates(const matrix scalingMatrix)
+void UpdateConstrainGpu::Impl::scaleCoordinates(const matrix scalingMatrix)
 {
     ScalingMatrix mu;
     mu.xx = scalingMatrix[XX][XX];
@@ -157,43 +160,62 @@ void UpdateConstrainCuda::Impl::scaleCoordinates(const matrix scalingMatrix)
 
     const auto kernelArgs = prepareGpuKernelArguments(
             scaleCoordinates_kernel, coordinateScalingKernelLaunchConfig_, &numAtoms_, &d_x_, &mu);
-    launchGpuKernel(scaleCoordinates_kernel, coordinateScalingKernelLaunchConfig_, nullptr,
-                    "scaleCoordinates_kernel", kernelArgs);
+    launchGpuKernel(scaleCoordinates_kernel, coordinateScalingKernelLaunchConfig_, deviceStream_,
+                    nullptr, "scaleCoordinates_kernel", kernelArgs);
     // TODO: Although this only happens on the pressure coupling steps, this synchronization
     //       can affect the perfornamce if nstpcouple is small.
-    gpuStreamSynchronize(commandStream_);
+    deviceStream_.synchronize();
 }
 
-UpdateConstrainCuda::Impl::Impl(const t_inputrec&     ir,
-                                const gmx_mtop_t&     mtop,
-                                const void*           commandStream,
-                                GpuEventSynchronizer* xUpdatedOnDevice) :
+void UpdateConstrainGpu::Impl::scaleVelocities(const matrix scalingMatrix)
+{
+    ScalingMatrix mu;
+    mu.xx = scalingMatrix[XX][XX];
+    mu.yy = scalingMatrix[YY][YY];
+    mu.zz = scalingMatrix[ZZ][ZZ];
+    mu.yx = scalingMatrix[YY][XX];
+    mu.zx = scalingMatrix[ZZ][XX];
+    mu.zy = scalingMatrix[ZZ][YY];
+
+    const auto kernelArgs = prepareGpuKernelArguments(
+            scaleCoordinates_kernel, coordinateScalingKernelLaunchConfig_, &numAtoms_, &d_v_, &mu);
+    launchGpuKernel(scaleCoordinates_kernel, coordinateScalingKernelLaunchConfig_, deviceStream_,
+                    nullptr, "scaleCoordinates_kernel", kernelArgs);
+    // TODO: Although this only happens on the pressure coupling steps, this synchronization
+    //       can affect the perfornamce if nstpcouple is small.
+    deviceStream_.synchronize();
+}
+
+UpdateConstrainGpu::Impl::Impl(const t_inputrec&     ir,
+                               const gmx_mtop_t&     mtop,
+                               const DeviceContext&  deviceContext,
+                               const DeviceStream&   deviceStream,
+                               GpuEventSynchronizer* xUpdatedOnDevice) :
+    deviceContext_(deviceContext),
+    deviceStream_(deviceStream),
     coordinatesReady_(xUpdatedOnDevice)
 {
     GMX_ASSERT(xUpdatedOnDevice != nullptr, "The event synchronizer can not be nullptr.");
-    commandStream != nullptr ? commandStream_ = *static_cast<const CommandStream*>(commandStream)
-                             : commandStream_ = nullptr;
 
 
-    integrator_ = std::make_unique<LeapFrogCuda>(commandStream_);
-    lincsCuda_  = std::make_unique<LincsCuda>(ir.nLincsIter, ir.nProjOrder, commandStream_);
-    settleCuda_ = std::make_unique<SettleCuda>(mtop, commandStream_);
+    integrator_ = std::make_unique<LeapFrogGpu>(deviceContext_, deviceStream_);
+    lincsGpu_ = std::make_unique<LincsGpu>(ir.nLincsIter, ir.nProjOrder, deviceContext_, deviceStream_);
+    settleGpu_ = std::make_unique<SettleGpu>(mtop, deviceContext_, deviceStream_);
 
     coordinateScalingKernelLaunchConfig_.blockSize[0]     = c_threadsPerBlock;
     coordinateScalingKernelLaunchConfig_.blockSize[1]     = 1;
     coordinateScalingKernelLaunchConfig_.blockSize[2]     = 1;
     coordinateScalingKernelLaunchConfig_.sharedMemorySize = 0;
-    coordinateScalingKernelLaunchConfig_.stream           = commandStream_;
 }
 
-UpdateConstrainCuda::Impl::~Impl() {}
+UpdateConstrainGpu::Impl::~Impl() {}
 
-void UpdateConstrainCuda::Impl::set(DeviceBuffer<float>       d_x,
-                                    DeviceBuffer<float>       d_v,
-                                    const DeviceBuffer<float> d_f,
-                                    const t_idef&             idef,
-                                    const t_mdatoms&          md,
-                                    const int                 numTempScaleValues)
+void UpdateConstrainGpu::Impl::set(DeviceBuffer<RVec>            d_x,
+                                   DeviceBuffer<RVec>            d_v,
+                                   const DeviceBuffer<RVec>      d_f,
+                                   const InteractionDefinitions& idef,
+                                   const t_mdatoms&              md,
+                                   const int                     numTempScaleValues)
 {
     GMX_ASSERT(d_x != nullptr, "Coordinates device buffer should not be null.");
     GMX_ASSERT(d_v != nullptr, "Velocities device buffer should not be null.");
@@ -205,86 +227,89 @@ void UpdateConstrainCuda::Impl::set(DeviceBuffer<float>       d_x,
 
     numAtoms_ = md.nr;
 
-    reallocateDeviceBuffer(&d_xp_, numAtoms_, &numXp_, &numXpAlloc_, nullptr);
+    reallocateDeviceBuffer(&d_xp_, numAtoms_, &numXp_, &numXpAlloc_, deviceContext_);
 
     reallocateDeviceBuffer(&d_inverseMasses_, numAtoms_, &numInverseMasses_,
-                           &numInverseMassesAlloc_, nullptr);
+                           &numInverseMassesAlloc_, deviceContext_);
 
     // Integrator should also update something, but it does not even have a method yet
-    integrator_->set(md, numTempScaleValues, md.cTC);
-    lincsCuda_->set(idef, md);
-    settleCuda_->set(idef, md);
+    integrator_->set(numAtoms_, md.invmass, numTempScaleValues, md.cTC);
+    lincsGpu_->set(idef, numAtoms_, md.invmass);
+    settleGpu_->set(idef);
 
     coordinateScalingKernelLaunchConfig_.gridSize[0] =
             (numAtoms_ + c_threadsPerBlock - 1) / c_threadsPerBlock;
 }
 
-void UpdateConstrainCuda::Impl::setPbc(const t_pbc* pbc)
+void UpdateConstrainGpu::Impl::setPbc(const PbcType pbcType, const matrix box)
 {
-    setPbcAiuc(pbc->ndim_ePBC, pbc->box, &pbcAiuc_);
-    integrator_->setPbc(pbc);
-    lincsCuda_->setPbc(pbc);
-    settleCuda_->setPbc(pbc);
+    setPbcAiuc(numPbcDimensions(pbcType), box, &pbcAiuc_);
 }
 
-GpuEventSynchronizer* UpdateConstrainCuda::Impl::getCoordinatesReadySync()
+GpuEventSynchronizer* UpdateConstrainGpu::Impl::getCoordinatesReadySync()
 {
     return coordinatesReady_;
 }
 
-UpdateConstrainCuda::UpdateConstrainCuda(const t_inputrec&     ir,
-                                         const gmx_mtop_t&     mtop,
-                                         const void*           commandStream,
-                                         GpuEventSynchronizer* xUpdatedOnDevice) :
-    impl_(new Impl(ir, mtop, commandStream, xUpdatedOnDevice))
+UpdateConstrainGpu::UpdateConstrainGpu(const t_inputrec&     ir,
+                                       const gmx_mtop_t&     mtop,
+                                       const DeviceContext&  deviceContext,
+                                       const DeviceStream&   deviceStream,
+                                       GpuEventSynchronizer* xUpdatedOnDevice) :
+    impl_(new Impl(ir, mtop, deviceContext, deviceStream, xUpdatedOnDevice))
 {
 }
 
-UpdateConstrainCuda::~UpdateConstrainCuda() = default;
-
-void UpdateConstrainCuda::integrate(GpuEventSynchronizer*             fReadyOnDevice,
-                                    const real                        dt,
-                                    const bool                        updateVelocities,
-                                    const bool                        computeVirial,
-                                    tensor                            virialScaled,
-                                    const bool                        doTemperatureScaling,
-                                    gmx::ArrayRef<const t_grp_tcstat> tcstat,
-                                    const bool                        doParrinelloRahman,
-                                    const float                       dtPressureCouple,
-                                    const matrix                      prVelocityScalingMatrix)
+UpdateConstrainGpu::~UpdateConstrainGpu() = default;
+
+void UpdateConstrainGpu::integrate(GpuEventSynchronizer*             fReadyOnDevice,
+                                   const real                        dt,
+                                   const bool                        updateVelocities,
+                                   const bool                        computeVirial,
+                                   tensor                            virialScaled,
+                                   const bool                        doTemperatureScaling,
+                                   gmx::ArrayRef<const t_grp_tcstat> tcstat,
+                                   const bool                        doParrinelloRahman,
+                                   const float                       dtPressureCouple,
+                                   const matrix                      prVelocityScalingMatrix)
 {
     impl_->integrate(fReadyOnDevice, dt, updateVelocities, computeVirial, virialScaled, doTemperatureScaling,
                      tcstat, doParrinelloRahman, dtPressureCouple, prVelocityScalingMatrix);
 }
 
-void UpdateConstrainCuda::scaleCoordinates(const matrix scalingMatrix)
+void UpdateConstrainGpu::scaleCoordinates(const matrix scalingMatrix)
 {
     impl_->scaleCoordinates(scalingMatrix);
 }
 
-void UpdateConstrainCuda::set(DeviceBuffer<float>       d_x,
-                              DeviceBuffer<float>       d_v,
-                              const DeviceBuffer<float> d_f,
-                              const t_idef&             idef,
-                              const t_mdatoms&          md,
-                              const int                 numTempScaleValues)
+void UpdateConstrainGpu::scaleVelocities(const matrix scalingMatrix)
+{
+    impl_->scaleVelocities(scalingMatrix);
+}
+
+void UpdateConstrainGpu::set(DeviceBuffer<RVec>            d_x,
+                             DeviceBuffer<RVec>            d_v,
+                             const DeviceBuffer<RVec>      d_f,
+                             const InteractionDefinitions& idef,
+                             const t_mdatoms&              md,
+                             const int                     numTempScaleValues)
 {
     impl_->set(d_x, d_v, d_f, idef, md, numTempScaleValues);
 }
 
-void UpdateConstrainCuda::setPbc(const t_pbc* pbc)
+void UpdateConstrainGpu::setPbc(const PbcType pbcType, const matrix box)
 {
-    impl_->setPbc(pbc);
+    impl_->setPbc(pbcType, box);
 }
 
-GpuEventSynchronizer* UpdateConstrainCuda::getCoordinatesReadySync()
+GpuEventSynchronizer* UpdateConstrainGpu::getCoordinatesReadySync()
 {
     return impl_->getCoordinatesReadySync();
 }
 
-bool UpdateConstrainCuda::isNumCoupledConstraintsSupported(const gmx_mtop_t& mtop)
+bool UpdateConstrainGpu::isNumCoupledConstraintsSupported(const gmx_mtop_t& mtop)
 {
-    return LincsCuda::isNumCoupledConstraintsSupported(mtop);
+    return LincsGpu::isNumCoupledConstraintsSupported(mtop);
 }
 
 } // namespace gmx
similarity index 77%
rename from src/gromacs/mdlib/update_constrain_cuda_impl.h
rename to src/gromacs/mdlib/update_constrain_gpu_impl.h
index 68fed99c6b3dae24f48e190f3b44c29d5aa6a9df..a191cc85b7fe9aa14ef7141f1d4e25b7a65599a7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -34,7 +34,7 @@
  */
 /*! \internal \file
  *
- * \brief Declares CUDA implementation class for update and constraints.
+ * \brief Declares GPU implementation class for update and constraints.
  *
  * This header file is needed to include from both the device-side
  * kernels file, and the host-side management code.
  *
  * \ingroup module_mdlib
  */
-#ifndef GMX_MDLIB_UPDATE_CONSTRAIN_CUDA_IMPL_H
-#define GMX_MDLIB_UPDATE_CONSTRAIN_CUDA_IMPL_H
+#ifndef GMX_MDLIB_UPDATE_CONSTRAIN_GPU_IMPL_H
+#define GMX_MDLIB_UPDATE_CONSTRAIN_GPU_IMPL_H
 
 #include "gmxpre.h"
 
 #include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
-#include "gromacs/mdlib/leapfrog_cuda.cuh"
-#include "gromacs/mdlib/lincs_cuda.cuh"
-#include "gromacs/mdlib/settle_cuda.cuh"
-#include "gromacs/mdlib/update_constrain_cuda.h"
+#include "gromacs/mdlib/leapfrog_gpu.cuh"
+#include "gromacs/mdlib/lincs_gpu.cuh"
+#include "gromacs/mdlib/settle_gpu.cuh"
+#include "gromacs/mdlib/update_constrain_gpu.h"
 #include "gromacs/mdtypes/inputrec.h"
 
 namespace gmx
 {
 
-/*! \internal \brief Class with interfaces and data for CUDA version of Update-Constraint. */
-class UpdateConstrainCuda::Impl
+/*! \internal \brief Class with interfaces and data for GPU version of Update-Constraint. */
+class UpdateConstrainGpu::Impl
 {
 
 public:
     /*! \brief Create Update-Constrain object.
      *
-     * The constructor is given a non-nullptr \p commandStream, in which all the update and constrain
+     * The constructor is given a non-nullptr \p deviceStream, in which all the update and constrain
      * routines are executed. \p xUpdatedOnDevice should mark the completion of all kernels that modify
      * coordinates. The event is maintained outside this class and also passed to all (if any) consumers
      * of the updated coordinates. The \p xUpdatedOnDevice also can not be a nullptr because the
@@ -75,10 +75,15 @@ public:
      *                              projection from it.
      * \param[in] mtop              Topology of the system: SETTLE gets the masses for O and H atoms
      *                              and target O-H and H-H distances from this object.
-     * \param[in] commandStream     GPU stream to use. Can be nullptr.
+     * \param[in] deviceContext     GPU device context.
+     * \param[in] deviceStream      GPU stream to use.
      * \param[in] xUpdatedOnDevice  The event synchronizer to use to mark that update is done on the GPU.
      */
-    Impl(const t_inputrec& ir, const gmx_mtop_t& mtop, const void* commandStream, GpuEventSynchronizer* xUpdatedOnDevice);
+    Impl(const t_inputrec&     ir,
+         const gmx_mtop_t&     mtop,
+         const DeviceContext&  deviceContext,
+         const DeviceStream&   deviceStream,
+         GpuEventSynchronizer* xUpdatedOnDevice);
 
     ~Impl();
 
@@ -124,6 +129,14 @@ public:
      */
     void scaleCoordinates(const matrix scalingMatrix);
 
+    /*! \brief Scale velocities on the GPU for the pressure coupling.
+     *
+     * After pressure coupling step, the box size may change. In the C-Rescale algorithm, velocities should be scaled.
+     *
+     * \param[in] scalingMatrix Velocities scaling matrix.
+     */
+    void scaleVelocities(const matrix scalingMatrix);
+
     /*! \brief Set the pointers and update data-structures (e.g. after NB search step).
      *
      * \param[in,out]  d_x            Device buffer with coordinates.
@@ -133,21 +146,22 @@ public:
      * \param[in] md                  Atoms data.
      * \param[in] numTempScaleValues  Number of temperature scaling groups. Set zero for no temperature coupling.
      */
-    void set(DeviceBuffer<float>       d_x,
-             DeviceBuffer<float>       d_v,
-             const DeviceBuffer<float> d_f,
-             const t_idef&             idef,
-             const t_mdatoms&          md,
-             const int                 numTempScaleValues);
+    void set(DeviceBuffer<RVec>            d_x,
+             DeviceBuffer<RVec>            d_v,
+             const DeviceBuffer<RVec>      d_f,
+             const InteractionDefinitions& idef,
+             const t_mdatoms&              md,
+             const int                     numTempScaleValues);
 
     /*! \brief
      * Update PBC data.
      *
      * Converts PBC data from t_pbc into the PbcAiuc format and stores the latter.
      *
-     * \param[in] pbc The PBC data in t_pbc format.
+     * \param[in] pbcType The type of the periodic boundary.
+     * \param[in] box     The periodic boundary box matrix.
      */
-    void setPbc(const t_pbc* pbc);
+    void setPbc(PbcType pbcType, const matrix box);
 
     /*! \brief Return the synchronizer associated with the event indicated that the coordinates are ready on the device.
      */
@@ -155,16 +169,18 @@ public:
 
     /*! \brief
      * Returns whether the maximum number of coupled constraints is supported
-     * by the CUDA LINCS code.
+     * by the GPU LINCS code.
      *
      * \param[in] mtop The molecular topology
      */
     static bool isNumCoupledConstraintsSupported(const gmx_mtop_t& mtop);
 
 private:
-    //! CUDA stream
-    CommandStream commandStream_ = nullptr;
-    //! CUDA kernel launch config
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! GPU stream
+    const DeviceStream& deviceStream_;
+    //! GPU kernel launch config
     KernelLaunchConfig coordinateScalingKernelLaunchConfig_;
 
     //! Periodic boundary data
@@ -196,11 +212,11 @@ private:
     int numInverseMassesAlloc_ = -1;
 
     //! Leap-Frog integrator
-    std::unique_ptr<LeapFrogCuda> integrator_;
-    //! LINCS CUDA object to use for non-water constraints
-    std::unique_ptr<LincsCuda> lincsCuda_;
-    //! SETTLE CUDA object for water constrains
-    std::unique_ptr<SettleCuda> settleCuda_;
+    std::unique_ptr<LeapFrogGpu> integrator_;
+    //! LINCS GPU object to use for non-water constraints
+    std::unique_ptr<LincsGpu> lincsGpu_;
+    //! SETTLE GPU object for water constrains
+    std::unique_ptr<SettleGpu> settleGpu_;
 
     //! An pointer to the event to indicate when the update of coordinates is complete
     GpuEventSynchronizer* coordinatesReady_;
@@ -208,4 +224,4 @@ private:
 
 } // namespace gmx
 
-#endif
+#endif // GMX_MDLIB_UPDATE_CONSTRAIN_GPU_IMPL_H
index 5ee169a4737271a94532bc99e41145d21ed2b87d..cf4856825c432c8599f5847f3997e62cb00fae5e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/math/units.h"
 #include "gromacs/mdlib/constr.h"
 #include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/topology/block.h"
 #include "gromacs/topology/idef.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/topology/topology.h"
+#include "gromacs/utility/listoflists.h"
 
 namespace gmx
 {
@@ -69,7 +69,7 @@ static bool hasFlexibleConstraints(const gmx_moltype_t& moltype, gmx::ArrayRef<c
         {
             for (size_t i = 0; i < ilist.iatoms.size(); i += ilistStride(ilist))
             {
-                if (isConstraintFlexible(iparams.data(), ilist.iatoms[i]))
+                if (isConstraintFlexible(iparams, ilist.iatoms[i]))
                 {
                     return true;
                 }
@@ -196,15 +196,17 @@ static AtomIndexExtremes vsiteConstructRange(int a, const gmx_moltype_t& moltype
 }
 
 /*! \brief Returns the range of atoms constrained to atom \p a (including \p a itself) */
-static AtomIndexExtremes constraintAtomRange(int a, const t_blocka& at2con, const InteractionList& ilistConstraints)
+static AtomIndexExtremes constraintAtomRange(int                     a,
+                                             const ListOfLists<int>& at2con,
+                                             const InteractionList&  ilistConstraints)
 {
     AtomIndexExtremes extremes = { a, a };
 
-    for (int i = at2con.index[a]; i < at2con.index[a + 1]; i++)
+    for (const int constraint : at2con[a])
     {
         for (int j = 0; j < 2; j++)
         {
-            int atomJ        = ilistConstraints.iatoms[at2con.a[i] * 3 + 1 + j];
+            int atomJ        = ilistConstraints.iatoms[constraint * 3 + 1 + j];
             extremes.minAtom = std::min(extremes.minAtom, atomJ);
             extremes.maxAtom = std::max(extremes.maxAtom, atomJ);
         }
@@ -231,10 +233,10 @@ static std::vector<bool> buildIsParticleVsite(const gmx_moltype_t& moltype)
 }
 
 /*! \brief Returns the size of the update group starting at \p firstAtom or 0 when criteria (see updategroups.h) are not met */
-static int detectGroup(int                    firstAtom,
-                       const gmx_moltype_t&   moltype,
-                       const t_blocka&        at2con,
-                       const InteractionList& ilistConstraints)
+static int detectGroup(int                     firstAtom,
+                       const gmx_moltype_t&    moltype,
+                       const ListOfLists<int>& at2con,
+                       const InteractionList&  ilistConstraints)
 {
     /* We should be using moltype.atoms.atom[].ptype for checking whether
      * a particle is a vsite. But the test code can't fill t_atoms,
@@ -243,7 +245,7 @@ static int detectGroup(int                    firstAtom,
     std::vector<bool> isParticleVsite = buildIsParticleVsite(moltype);
 
     /* A non-vsite atom without constraints is an update group by itself */
-    if (!isParticleVsite[firstAtom] && at2con.index[firstAtom + 1] - at2con.index[firstAtom] == 0)
+    if (!isParticleVsite[firstAtom] && at2con[firstAtom].empty())
     {
         return 1;
     }
@@ -274,7 +276,7 @@ static int detectGroup(int                    firstAtom,
         }
         else
         {
-            int numConstraints = at2con.index[a + 1] - at2con.index[a];
+            const int numConstraints = at2con[a].ssize();
             if (numConstraints == 0)
             {
                 /* We can not have unconstrained atoms in an update group */
@@ -306,7 +308,7 @@ static int detectGroup(int                    firstAtom,
     {
         AtomIndexExtremes extremes = vsiteConstructRange(lastAtom + 1, moltype);
         if (extremes.minAtom < firstAtom)
-        {
+        { // NOLINT bugprone-branch-clone
             /* Constructing atom precedes the group */
             return 0;
         }
@@ -350,21 +352,18 @@ static RangePartitioning makeUpdateGroups(const gmx_moltype_t& moltype, gmx::Arr
     }
 
     /* Combine all constraint ilists into a single one */
-    InteractionList constraintsCombined = jointConstraintList(moltype);
-    t_ilist         ilistsCombined[F_NRE];
-    ilistsCombined[F_CONSTR].nr     = constraintsCombined.iatoms.size();
-    ilistsCombined[F_CONSTR].iatoms = constraintsCombined.iatoms.data();
-    ilistsCombined[F_CONSTRNC].nr   = 0;
+    std::array<InteractionList, F_NRE> ilistsCombined;
+    ilistsCombined[F_CONSTR] = jointConstraintList(moltype);
     /* We "include" flexible constraints, but none are present (checked above) */
-    t_blocka at2con = make_at2con(moltype.atoms.nr, ilistsCombined, iparams.data(),
-                                  FlexibleConstraintTreatment::Include);
+    const ListOfLists<int> at2con = make_at2con(moltype.atoms.nr, ilistsCombined, iparams,
+                                                FlexibleConstraintTreatment::Include);
 
     bool satisfiesCriteria = true;
 
     int firstAtom = 0;
     while (satisfiesCriteria && firstAtom < moltype.atoms.nr)
     {
-        int numAtomsInGroup = detectGroup(firstAtom, moltype, at2con, constraintsCombined);
+        int numAtomsInGroup = detectGroup(firstAtom, moltype, at2con, ilistsCombined[F_CONSTR]);
 
         if (numAtomsInGroup == 0)
         {
@@ -383,8 +382,6 @@ static RangePartitioning makeUpdateGroups(const gmx_moltype_t& moltype, gmx::Arr
         groups.clear();
     }
 
-    done_blocka(&at2con);
-
     return groups;
 }
 
@@ -441,19 +438,19 @@ template<int numPartnerAtoms>
 static real constraintGroupRadius(const gmx_moltype_t&                     moltype,
                                   gmx::ArrayRef<const t_iparams>           iparams,
                                   const int                                centralAtom,
-                                  const t_blocka&                          at2con,
+                                  const ListOfLists<int>&                  at2con,
                                   const std::unordered_multimap<int, int>& angleIndices,
                                   const real                               constraintLength,
                                   const real                               temperature)
 {
-    const int numConstraints = at2con.index[centralAtom + 1] - at2con.index[centralAtom];
+    const int numConstraints = at2con[centralAtom].ssize();
     GMX_RELEASE_ASSERT(numConstraints == numPartnerAtoms,
                        "We expect as many constraints as partner atoms here");
 
     std::array<int, numPartnerAtoms> partnerAtoms;
     for (int i = 0; i < numPartnerAtoms; i++)
     {
-        const int ind = at2con.a[at2con.index[centralAtom] + i] * 3;
+        const int ind = at2con[centralAtom][i] * 3;
         if (ind >= moltype.ilist[F_CONSTR].size())
         {
             /* This is a flexible constraint, we don't optimize for that */
@@ -597,7 +594,7 @@ static real computeMaxUpdateGroupRadius(const gmx_moltype_t&           moltype,
 
     const InteractionList& settles = moltype.ilist[F_SETTLE];
 
-    t_blocka at2con = make_at2con(moltype, iparams, FlexibleConstraintTreatment::Include);
+    const ListOfLists<int> at2con = make_at2con(moltype, iparams, FlexibleConstraintTreatment::Include);
 
     const auto angleIndices = getAngleIndices(moltype);
 
@@ -615,14 +612,14 @@ static real computeMaxUpdateGroupRadius(const gmx_moltype_t&           moltype,
         int maxAtom           = -1;
         for (int a : updateGroups.block(group))
         {
-            int numConstraints = at2con.index[a + 1] - at2con.index[a];
+            const int numConstraints = at2con[a].ssize();
             if (numConstraints > maxNumConstraints)
             {
                 maxNumConstraints = numConstraints;
                 maxAtom           = a;
             }
         }
-        GMX_ASSERT(maxAtom >= 0 || settles.size() > 0,
+        GMX_ASSERT(maxAtom >= 0 || !settles.empty(),
                    "We should have at least two atoms in the group with constraints");
         if (maxAtom < 0)
         {
@@ -633,9 +630,10 @@ static real computeMaxUpdateGroupRadius(const gmx_moltype_t&           moltype,
         int  constraintType       = -1;
         real maxConstraintLength  = 0;
         real sumConstraintLengths = 0;
-        for (int i = at2con.index[maxAtom]; i < at2con.index[maxAtom + 1]; i++)
+        bool isFirstConstraint    = true;
+        for (const int constraint : at2con[maxAtom])
         {
-            int conIndex = at2con.a[i] * (1 + NRAL(F_CONSTR));
+            int conIndex = constraint * (1 + NRAL(F_CONSTR));
             int iparamsIndex;
             if (conIndex < moltype.ilist[F_CONSTR].size())
             {
@@ -646,9 +644,10 @@ static real computeMaxUpdateGroupRadius(const gmx_moltype_t&           moltype,
                 iparamsIndex =
                         moltype.ilist[F_CONSTRNC].iatoms[conIndex - moltype.ilist[F_CONSTR].size()];
             }
-            if (i == at2con.index[maxAtom])
+            if (isFirstConstraint)
             {
-                constraintType = iparamsIndex;
+                constraintType    = iparamsIndex;
+                isFirstConstraint = false;
             }
             else if (iparamsIndex != constraintType)
             {
@@ -664,7 +663,7 @@ static real computeMaxUpdateGroupRadius(const gmx_moltype_t&           moltype,
             sumConstraintLengths += constraintLength;
         }
 
-        int  numConstraints = at2con.index[maxAtom + 1] - at2con.index[maxAtom];
+        int  numConstraints = at2con[maxAtom].ssize();
         real radius;
         if (numConstraints == 1)
         {
@@ -721,8 +720,6 @@ static real computeMaxUpdateGroupRadius(const gmx_moltype_t&           moltype,
         maxRadius        = std::max(maxRadius, dCAny);
     }
 
-    done_blocka(&at2con);
-
     return maxRadius;
 }
 
index 28efb3b800f6bcba56079f61e5edaf947e032fc1..63a3df664a2855772605ccb429a10c131ae6b91a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -39,7 +40,6 @@
 
 #include "vcm.h"
 
-#include "gromacs/gmxlib/network.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/invertmatrix.h"
 #include "gromacs/math/vec.h"
@@ -47,6 +47,7 @@
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/fatalerror.h"
@@ -63,7 +64,8 @@ t_vcm::t_vcm(const SimulationGroups& groups, const t_inputrec& ir) :
 
     if (mode == ecmANGULAR && ndim < 3)
     {
-        gmx_fatal(FARGS, "Can not have angular comm removal with pbc=%s", epbc_names[ir.ePBC]);
+        gmx_fatal(FARGS, "Can not have angular comm removal with pbc=%s",
+                  c_pbcTypeNames[ir.pbcType].c_str());
     }
 
     if (mode != ecmNO)
@@ -150,12 +152,19 @@ static void update_tensor(const rvec x, real m0, tensor I)
 }
 
 /* Center of mass code for groups */
-void calc_vcm_grp(const t_mdatoms& md, const rvec x[], const rvec v[], t_vcm* vcm)
+void calc_vcm_grp(const t_mdatoms&               md,
+                  gmx::ArrayRef<const gmx::RVec> x,
+                  gmx::ArrayRef<const gmx::RVec> v,
+                  t_vcm*                         vcm)
 {
+    if (vcm->mode == ecmNO)
+    {
+        return;
+    }
     int nthreads = gmx_omp_nthreads_get(emntDefault);
-    if (vcm->mode != ecmNO)
+
     {
-#pragma omp parallel num_threads(nthreads)
+#pragma omp parallel num_threads(nthreads) default(none) shared(x, v, vcm, md)
         {
             int t = gmx_omp_get_thread_num();
             for (int g = 0; g < vcm->size; g++)
@@ -247,7 +256,7 @@ void calc_vcm_grp(const t_mdatoms& md, const rvec x[], const rvec v[], t_vcm* vc
  * \param[in]     vcm       VCM data
  */
 template<int numDimensions>
-static void doStopComMotionLinear(const t_mdatoms& mdatoms, rvec* v, const t_vcm& vcm)
+static void doStopComMotionLinear(const t_mdatoms& mdatoms, gmx::ArrayRef<gmx::RVec> v, const t_vcm& vcm)
 {
     const int             homenr   = mdatoms.homenr;
     const unsigned short* group_id = mdatoms.cVCM;
@@ -271,7 +280,7 @@ static void doStopComMotionLinear(const t_mdatoms& mdatoms, rvec* v, const t_vcm
         }
     }
     else if (group_id == nullptr)
-    {
+    { // NOLINT bugprone-branch-clone This is actually a clang-tidy bug
 #pragma omp for schedule(static)
         for (int i = 0; i < homenr; i++)
         {
@@ -307,14 +316,15 @@ static void doStopComMotionLinear(const t_mdatoms& mdatoms, rvec* v, const t_vcm
  * \param[in]     vcm       VCM data
  */
 template<int numDimensions>
-static void doStopComMotionAccelerationCorrection(int                   homenr,
-                                                  const unsigned short* group_id,
-                                                  rvec* gmx_restrict x,
-                                                  rvec* gmx_restrict v,
-                                                  const t_vcm&       vcm)
+static void doStopComMotionAccelerationCorrection(int                      homenr,
+                                                  const unsigned short*    group_id,
+                                                  gmx::ArrayRef<gmx::RVec> x,
+                                                  gmx::ArrayRef<gmx::RVec> v,
+                                                  const t_vcm&             vcm)
 {
     const real xCorrectionFactor = 0.5 * vcm.timeStep;
 
+    // NOLINTNEXTLINE bugprone-branch-clone This is actually a clang-tidy bug
     if (group_id == nullptr)
     {
 #pragma omp for schedule(static)
@@ -342,18 +352,27 @@ static void doStopComMotionAccelerationCorrection(int                   homenr,
     }
 }
 
-static void do_stopcm_grp(const t_mdatoms& mdatoms, rvec x[], rvec v[], const t_vcm& vcm)
+static void do_stopcm_grp(const t_mdatoms&         mdatoms,
+                          gmx::ArrayRef<gmx::RVec> x,
+                          gmx::ArrayRef<gmx::RVec> v,
+                          const t_vcm&             vcm)
 {
-    if (vcm.mode != ecmNO)
+    if (vcm.mode == ecmNO)
+    {
+        return;
+    }
     {
         const int             homenr   = mdatoms.homenr;
         const unsigned short* group_id = mdatoms.cVCM;
 
         int gmx_unused nth = gmx_omp_nthreads_get(emntDefault);
-#pragma omp parallel num_threads(nth)
+        // homenr could be shared, but gcc-8 & gcc-9 don't agree how to write that...
+        // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
+#pragma omp parallel num_threads(nth) default(none) shared(x, v, vcm, group_id, mdatoms) \
+        firstprivate(homenr)
         {
             if (vcm.mode == ecmLINEAR || vcm.mode == ecmANGULAR
-                || (vcm.mode == ecmLINEAR_ACCELERATION_CORRECTION && x == nullptr))
+                || (vcm.mode == ecmLINEAR_ACCELERATION_CORRECTION && x.empty()))
             {
                 /* Subtract linear momentum for v */
                 switch (vcm.ndim)
@@ -385,7 +404,7 @@ static void do_stopcm_grp(const t_mdatoms& mdatoms, rvec x[], rvec v[], const t_
             if (vcm.mode == ecmANGULAR)
             {
                 /* Subtract angular momentum */
-                GMX_ASSERT(x, "Need x to compute angular momentum correction");
+                GMX_ASSERT(!x.empty(), "Need x to compute angular momentum correction");
 
                 int g = 0;
 #pragma omp for schedule(static)
@@ -557,7 +576,11 @@ static void process_and_check_cm_grp(FILE* fp, t_vcm* vcm, real Temp_Max)
     }
 }
 
-void process_and_stopcm_grp(FILE* fplog, t_vcm* vcm, const t_mdatoms& mdatoms, rvec x[], rvec v[])
+void process_and_stopcm_grp(FILE*                    fplog,
+                            t_vcm*                   vcm,
+                            const t_mdatoms&         mdatoms,
+                            gmx::ArrayRef<gmx::RVec> x,
+                            gmx::ArrayRef<gmx::RVec> v)
 {
     if (vcm->mode != ecmNO)
     {
index 87b95e08f3e92de63284eadbc392196c6407157c..b0c142474101f740f8fda554d70e1b6ee6382118 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -42,7 +43,6 @@
 #include <vector>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
@@ -50,6 +50,12 @@ struct SimulationGroups;
 struct t_inputrec;
 struct t_mdatoms;
 
+namespace gmx
+{
+template<typename T>
+class ArrayRef;
+}
+
 struct t_vcm_thread
 {
     //! Linear momentum
@@ -113,7 +119,10 @@ void reportComRemovalInfo(FILE* fp, const t_vcm& vcm);
 
 
 /* Do a per group center of mass things */
-void calc_vcm_grp(const t_mdatoms& md, const rvec x[], const rvec v[], t_vcm* vcm);
+void calc_vcm_grp(const t_mdatoms&               md,
+                  gmx::ArrayRef<const gmx::RVec> x,
+                  gmx::ArrayRef<const gmx::RVec> v,
+                  t_vcm*                         vcm);
 
 /* Set the COM velocity to zero and potentially correct the COM position.
  *
@@ -123,6 +132,10 @@ void calc_vcm_grp(const t_mdatoms& md, const rvec x[], const rvec v[], t_vcm* vc
  * and a pointer to the coordinates at normal MD steps.
  * When fplog != nullptr, a warning is printed to fplog with high COM velocity.
  */
-void process_and_stopcm_grp(FILE* fplog, t_vcm* vcm, const t_mdatoms& mdatoms, rvec x[], rvec v[]);
+void process_and_stopcm_grp(FILE*                    fplog,
+                            t_vcm*                   vcm,
+                            const t_mdatoms&         mdatoms,
+                            gmx::ArrayRef<gmx::RVec> x,
+                            gmx::ArrayRef<gmx::RVec> v);
 
 #endif
index 59427c294485e1d1e4a7b31cae31c0a5bb97f30a..35628004ecea0774db522eebf30461901a804bc6 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+/*! \internal \file
+ * \brief Implements the VirtualSitesHandler class and vsite standalone functions
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdlib
+ */
+
 #include "gmxpre.h"
 
 #include "vsite.h"
@@ -54,7 +62,6 @@
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/ishift.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/timing/wallcycle.h"
 #include "gromacs/topology/ifunc.h"
  *
  * Any remaining vsites are assigned to a separate master thread task.
  */
+namespace gmx
+{
 
-using gmx::RVec;
+//! VirialHandling is often used outside VirtualSitesHandler class members
+using VirialHandling = VirtualSitesHandler::VirialHandling;
 
-static void init_ilist(t_ilist* ilist)
+/*! \brief Information on PBC and domain decomposition for virtual sites
+ */
+struct DomainInfo
 {
-    for (int i = 0; i < F_NRE; i++)
+public:
+    //! Constructs without PBC and DD
+    DomainInfo() = default;
+
+    //! Constructs with PBC and DD, if !=nullptr
+    DomainInfo(PbcType pbcType, bool haveInterUpdateGroupVirtualSites, gmx_domdec_t* domdec) :
+        pbcType_(pbcType),
+        useMolPbc_(pbcType != PbcType::No && haveInterUpdateGroupVirtualSites),
+        domdec_(domdec)
     {
-        ilist[i].nr     = 0;
-        ilist[i].nalloc = 0;
-        ilist[i].iatoms = nullptr;
     }
-}
 
-/*! \brief List of atom indices belonging to a task */
+    //! Returns whether we are using domain decomposition with more than 1 DD rank
+    bool useDomdec() const { return (domdec_ != nullptr); }
+
+    //! The pbc type
+    const PbcType pbcType_ = PbcType::No;
+    //! Whether molecules are broken over PBC
+    const bool useMolPbc_ = false;
+    //! Pointer to the domain decomposition struct, nullptr without PP DD
+    const gmx_domdec_t* domdec_ = nullptr;
+};
+
+/*! \brief List of atom indices belonging to a task
+ */
 struct AtomIndex
 {
     //! List of atom indices
     std::vector<int> atom;
 };
 
-/*! \brief Data structure for thread tasks that use constructing atoms outside their own atom range */
+/*! \brief Data structure for thread tasks that use constructing atoms outside their own atom range
+ */
 struct InterdependentTask
 {
     //! The interaction lists, only vsite entries are used
-    t_ilist ilist[F_NRE];
+    InteractionLists ilist;
     //! Thread/task-local force buffer
     std::vector<RVec> force;
     //! The atom indices of the vsites of our task
@@ -116,22 +145,17 @@ struct InterdependentTask
     //! Flags if elements in force are spread to or not
     std::vector<bool> use;
     //! The number of entries set to true in use
-    int nuse;
+    int nuse = 0;
     //! Array of atoms indices, size nthreads, covering all nuse set elements in use
     std::vector<AtomIndex> atomIndex;
     //! List of tasks (force blocks) this task spread forces to
     std::vector<int> spreadTask;
     //! List of tasks that write to this tasks force block range
     std::vector<int> reduceTask;
-
-    InterdependentTask()
-    {
-        init_ilist(ilist);
-        nuse = 0;
-    }
 };
 
-/*! \brief Vsite thread task data structure */
+/*! \brief Vsite thread task data structure
+ */
 struct VsiteThread
 {
     //! Start of atom range of this task
@@ -139,9 +163,9 @@ struct VsiteThread
     //! End of atom range of this task
     int rangeEnd;
     //! The interaction lists, only vsite entries are used
-    t_ilist ilist[F_NRE];
+    std::array<InteractionList, F_NRE> ilist;
     //! Local fshift accumulation buffer
-    rvec fshift[SHIFTS];
+    std::array<RVec, SHIFTS> fshift;
     //! Local virial dx*df accumulation buffer
     matrix dxdf;
     //! Tells if interdependent task idTask should be used (in addition to the rest of this task), this bool has the same value on all threads
@@ -154,19 +178,121 @@ struct VsiteThread
     {
         rangeStart = -1;
         rangeEnd   = -1;
-        init_ilist(ilist);
-        clear_rvecs(SHIFTS, fshift);
+        for (auto& elem : fshift)
+        {
+            elem = { 0.0_real, 0.0_real, 0.0_real };
+        }
         clear_mat(dxdf);
         useInterdependentTask = false;
     }
 };
 
+
+/*! \brief Information on how the virtual site work is divided over thread tasks
+ */
+class ThreadingInfo
+{
+public:
+    //! Constructor, retrieves the number of threads to use from gmx_omp_nthreads.h
+    ThreadingInfo();
+
+    //! Returns the number of threads to use for vsite operations
+    int numThreads() const { return numThreads_; }
+
+    //! Returns the thread data for the given thread
+    const VsiteThread& threadData(int threadIndex) const { return *tData_[threadIndex]; }
+
+    //! Returns the thread data for the given thread
+    VsiteThread& threadData(int threadIndex) { return *tData_[threadIndex]; }
+
+    //! Returns the thread data for vsites that depend on non-local vsites
+    const VsiteThread& threadDataNonLocalDependent() const { return *tData_[numThreads_]; }
+
+    //! Returns the thread data for vsites that depend on non-local vsites
+    VsiteThread& threadDataNonLocalDependent() { return *tData_[numThreads_]; }
+
+    //! Set VSites and distribute VSite work over threads, should be called after DD partitioning
+    void setVirtualSites(ArrayRef<const InteractionList> ilist,
+                         ArrayRef<const t_iparams>       iparams,
+                         const t_mdatoms&                mdatoms,
+                         bool                            useDomdec);
+
+private:
+    //! Number of threads used for vsite operations
+    const int numThreads_;
+    //! Thread local vsites and work structs
+    std::vector<std::unique_ptr<VsiteThread>> tData_;
+    //! Work array for dividing vsites over threads
+    std::vector<int> taskIndex_;
+};
+
+/*! \brief Impl class for VirtualSitesHandler
+ */
+class VirtualSitesHandler::Impl
+{
+public:
+    //! Constructor, domdec should be nullptr without DD
+    Impl(const gmx_mtop_t& mtop, gmx_domdec_t* domdec, PbcType pbcType);
+
+    //! Returns the number of virtual sites acting over multiple update groups
+    int numInterUpdategroupVirtualSites() const { return numInterUpdategroupVirtualSites_; }
+
+    //! Set VSites and distribute VSite work over threads, should be called after DD partitioning
+    void setVirtualSites(ArrayRef<const InteractionList> ilist, const t_mdatoms& mdatoms);
+
+    /*! \brief Create positions of vsite atoms based for the local system
+     *
+     * \param[in,out] x        The coordinates
+     * \param[in]     dt       The time step
+     * \param[in,out] v        When != nullptr, velocities for vsites are set as displacement/dt
+     * \param[in]     box      The box
+     */
+    void construct(ArrayRef<RVec> x, real dt, ArrayRef<RVec> v, const matrix box) const;
+
+    /*! \brief Spread the force operating on the vsite atoms on the surrounding atoms.
+     *
+     * vsite should point to a valid object.
+     * The virialHandling parameter determines how virial contributions are handled.
+     * If this is set to Linear, shift forces are accumulated into fshift.
+     * If this is set to NonLinear, non-linear contributions are added to virial.
+     * This non-linear correction is required when the virial is not calculated
+     * afterwards from the particle position and forces, but in a different way,
+     * as for instance for the PME mesh contribution.
+     */
+    void spreadForces(ArrayRef<const RVec> x,
+                      ArrayRef<RVec>       f,
+                      VirialHandling       virialHandling,
+                      ArrayRef<RVec>       fshift,
+                      matrix               virial,
+                      t_nrnb*              nrnb,
+                      const matrix         box,
+                      gmx_wallcycle*       wcycle);
+
+private:
+    //! The number of vsites that cross update groups, when =0 no PBC treatment is needed
+    const int numInterUpdategroupVirtualSites_;
+    //! PBC and DD information
+    const DomainInfo domainInfo_;
+    //! The interaction parameters
+    const ArrayRef<const t_iparams> iparams_;
+    //! The interaction lists
+    ArrayRef<const InteractionList> ilists_;
+    //! Information for handling vsite threading
+    ThreadingInfo threadingInfo_;
+};
+
+VirtualSitesHandler::~VirtualSitesHandler() = default;
+
+int VirtualSitesHandler::numInterUpdategroupVirtualSites() const
+{
+    return impl_->numInterUpdategroupVirtualSites();
+}
+
 /*! \brief Returns the sum of the vsite ilist sizes over all vsite types
  *
  * \param[in] ilist  The interaction list
  */
-template<typename T>
-static int vsiteIlistNrCount(const T* ilist)
+static int vsiteIlistNrCount(ArrayRef<const InteractionList> ilist)
 {
     int nr = 0;
     for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
@@ -177,6 +303,7 @@ static int vsiteIlistNrCount(const T* ilist)
     return nr;
 }
 
+//! Computes the distance between xi and xj, pbc is used when pbc!=nullptr
 static int pbc_rvec_sub(const t_pbc* pbc, const rvec xi, const rvec xj, rvec dx)
 {
     if (pbc)
@@ -190,13 +317,22 @@ static int pbc_rvec_sub(const t_pbc* pbc, const rvec xi, const rvec xj, rvec dx)
     }
 }
 
+//! Returns the 1/norm(x)
 static inline real inverseNorm(const rvec x)
 {
     return gmx::invsqrt(iprod(x, x));
 }
 
+#ifndef DOXYGEN
 /* Vsite construction routines */
 
+static void constr_vsite1(const rvec xi, rvec x)
+{
+    copy_rvec(xi, x);
+
+    /* TOTAL: 0 flops */
+}
+
 static void constr_vsite2(const rvec xi, const rvec xj, rvec x, real a, const t_pbc* pbc)
 {
     real b = 1 - a;
@@ -415,8 +551,7 @@ static void constr_vsite4FDN(const rvec   xi,
     /* TOTAL: 47 flops */
 }
 
-
-static int constr_vsiten(const t_iatom* ia, const t_iparams ip[], rvec* x, const t_pbc* pbc)
+static int constr_vsiten(const t_iatom* ia, ArrayRef<const t_iparams> ip, ArrayRef<RVec> x, const t_pbc* pbc)
 {
     rvec x1, dx;
     dvec dsum;
@@ -453,11 +588,13 @@ static int constr_vsiten(const t_iatom* ia, const t_iparams ip[], rvec* x, const
     return n3;
 }
 
-/*! \brief PBC modes for vsite construction and spreading */
+#endif // DOXYGEN
+
+//! PBC modes for vsite construction and spreading
 enum class PbcMode
 {
-    all, // Apply normal, simple PBC for all vsites
-    none // No PBC treatment needed
+    all, //!< Apply normal, simple PBC for all vsites
+    none //!< No PBC treatment needed
 };
 
 /*! \brief Returns the PBC mode based on the system PBC and vsite properties
@@ -476,15 +613,24 @@ static PbcMode getPbcMode(const t_pbc* pbcPtr)
     }
 }
 
-static void construct_vsites_thread(rvec            x[],
-                                    real            dt,
-                                    rvec*           v,
-                                    const t_iparams ip[],
-                                    const t_ilist   ilist[],
-                                    const t_pbc*    pbc_null)
+/*! \brief Executes the vsite construction task for a single thread
+ *
+ * \param[in,out] x   Coordinates to construct vsites for
+ * \param[in]     dt  Time step, needed when v is not empty
+ * \param[in,out] v   When not empty, velocities are generated for virtual sites
+ * \param[in]     ip  Interaction parameters for all interaction, only vsite parameters are used
+ * \param[in]     ilist  The interaction lists, only vsites are usesd
+ * \param[in]     pbc_null  PBC struct, used for PBC distance calculations when !=nullptr
+ */
+static void construct_vsites_thread(ArrayRef<RVec>                  x,
+                                    const real                      dt,
+                                    ArrayRef<RVec>                  v,
+                                    ArrayRef<const t_iparams>       ip,
+                                    ArrayRef<const InteractionList> ilist,
+                                    const t_pbc*                    pbc_null)
 {
     real inv_dt;
-    if (v != nullptr)
+    if (!v.empty())
     {
         inv_dt = 1.0 / dt;
     }
@@ -499,7 +645,7 @@ static void construct_vsites_thread(rvec            x[],
 
     for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
     {
-        if (ilist[ftype].nr == 0)
+        if (ilist[ftype].empty())
         {
             continue;
         }
@@ -507,9 +653,9 @@ static void construct_vsites_thread(rvec            x[],
         { // TODO remove me
             int nra = interaction_function[ftype].nratoms;
             int inc = 1 + nra;
-            int nr  = ilist[ftype].nr;
+            int nr  = ilist[ftype].size();
 
-            const t_iatom* ia = ilist[ftype].iatoms;
+            const t_iatom* ia = ilist[ftype].iatoms.data();
 
             for (int i = 0; i < nr;)
             {
@@ -528,6 +674,7 @@ static void construct_vsites_thread(rvec            x[],
                 real b1, c1;
                 switch (ftype)
                 {
+                    case F_VSITE1: constr_vsite1(x[ai], x[avsite]); break;
                     case F_VSITE2:
                         aj = ia[3];
                         constr_vsite2(x[ai], x[aj], x[avsite], a1, pbc_null2);
@@ -592,7 +739,7 @@ static void construct_vsites_thread(rvec            x[],
                         rvec_add(xv, dx, x[avsite]);
                     }
                 }
-                if (v != nullptr)
+                if (!v.empty())
                 {
                     /* Calculate velocity of vsite... */
                     rvec vv;
@@ -608,40 +755,44 @@ static void construct_vsites_thread(rvec            x[],
     }
 }
 
-void construct_vsites(const gmx_vsite_t* vsite,
-                      rvec               x[],
-                      real               dt,
-                      rvec*              v,
-                      const t_iparams    ip[],
-                      const t_ilist      ilist[],
-                      int                ePBC,
-                      gmx_bool           bMolPBC,
-                      const t_commrec*   cr,
-                      const matrix       box)
+/*! \brief Dispatch the vsite construction tasks for all threads
+ *
+ * \param[in]     threadingInfo  Used to divide work over threads when != nullptr
+ * \param[in,out] x   Coordinates to construct vsites for
+ * \param[in]     dt  Time step, needed when v is not empty
+ * \param[in,out] v   When not empty, velocities are generated for virtual sites
+ * \param[in]     ip  Interaction parameters for all interaction, only vsite parameters are used
+ * \param[in]     ilist  The interaction lists, only vsites are usesd
+ * \param[in]     domainInfo  Information about PBC and DD
+ * \param[in]     box  Used for PBC when PBC is set in domainInfo
+ */
+static void construct_vsites(const ThreadingInfo*            threadingInfo,
+                             ArrayRef<RVec>                  x,
+                             real                            dt,
+                             ArrayRef<RVec>                  v,
+                             ArrayRef<const t_iparams>       ip,
+                             ArrayRef<const InteractionList> ilist,
+                             const DomainInfo&               domainInfo,
+                             const matrix                    box)
 {
-    const bool useDomdec = (vsite != nullptr && vsite->useDomdec);
-    GMX_ASSERT(!useDomdec || (cr != nullptr && DOMAINDECOMP(cr)),
-               "When vsites are set up with domain decomposition, we need a valid commrec");
-    // TODO: Remove this assertion when we remove charge groups
-    GMX_ASSERT(vsite != nullptr || ePBC == epbcNONE,
-               "Without a vsite struct we can not do PBC (in case we have charge groups)");
+    const bool useDomdec = domainInfo.useDomdec();
 
     t_pbc pbc, *pbc_null;
 
-    /* We only need to do pbc when we have inter-cg vsites.
+    /* We only need to do pbc when we have inter update-group vsites.
      * Note that with domain decomposition we do not need to apply PBC here
      * when we have at least 3 domains along each dimension. Currently we
      * do not optimize this case.
      */
-    if (ePBC != epbcNONE && (useDomdec || bMolPBC)
-        && !(vsite != nullptr && vsite->numInterUpdategroupVsites == 0))
+    if (domainInfo.pbcType_ != PbcType::No && domainInfo.useMolPbc_)
     {
         /* This is wasting some CPU time as we now do this multiple times
          * per MD step.
          */
         ivec null_ivec;
         clear_ivec(null_ivec);
-        pbc_null = set_pbc_dd(&pbc, ePBC, useDomdec ? cr->dd->nc : null_ivec, FALSE, box);
+        pbc_null = set_pbc_dd(&pbc, domainInfo.pbcType_,
+                              useDomdec ? domainInfo.domdec_->numCells : null_ivec, FALSE, box);
     }
     else
     {
@@ -650,21 +801,21 @@ void construct_vsites(const gmx_vsite_t* vsite,
 
     if (useDomdec)
     {
-        dd_move_x_vsites(cr->dd, box, x);
+        dd_move_x_vsites(*domainInfo.domdec_, box, as_rvec_array(x.data()));
     }
 
-    if (vsite == nullptr || vsite->nthreads == 1)
+    if (threadingInfo == nullptr || threadingInfo->numThreads() == 1)
     {
         construct_vsites_thread(x, dt, v, ip, ilist, pbc_null);
     }
     else
     {
-#pragma omp parallel num_threads(vsite->nthreads)
+#pragma omp parallel num_threads(threadingInfo->numThreads())
         {
             try
             {
                 const int          th    = gmx_omp_get_thread_num();
-                const VsiteThread& tData = *vsite->tData[th];
+                const VsiteThread& tData = threadingInfo->threadData(th);
                 GMX_ASSERT(tData.rangeStart >= 0,
                            "The thread data should be initialized before calling construct_vsites");
 
@@ -681,22 +832,49 @@ void construct_vsites(const gmx_vsite_t* vsite,
             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
         }
         /* Now we can construct the vsites that might depend on other vsites */
-        construct_vsites_thread(x, dt, v, ip, vsite->tData[vsite->nthreads]->ilist, pbc_null);
+        construct_vsites_thread(x, dt, v, ip, threadingInfo->threadDataNonLocalDependent().ilist, pbc_null);
     }
 }
 
-static void spread_vsite2(const t_iatom  ia[],
-                          real           a,
-                          const rvec     x[],
-                          rvec           f[],
-                          rvec           fshift[],
-                          const t_pbc*   pbc,
-                          const t_graph* g)
+void VirtualSitesHandler::Impl::construct(ArrayRef<RVec> x, real dt, ArrayRef<RVec> v, const matrix box) const
+{
+    construct_vsites(&threadingInfo_, x, dt, v, iparams_, ilists_, domainInfo_, box);
+}
+
+void VirtualSitesHandler::construct(ArrayRef<RVec> x, real dt, ArrayRef<RVec> v, const matrix box) const
+{
+    impl_->construct(x, dt, v, box);
+}
+
+void constructVirtualSites(ArrayRef<RVec> x, ArrayRef<const t_iparams> ip, ArrayRef<const InteractionList> ilist)
+
+{
+    // No PBC, no DD
+    const DomainInfo domainInfo;
+    construct_vsites(nullptr, x, 0, {}, ip, ilist, domainInfo, nullptr);
+}
+
+#ifndef DOXYGEN
+/* Force spreading routines */
+
+static void spread_vsite1(const t_iatom ia[], ArrayRef<RVec> f)
+{
+    const int av = ia[1];
+    const int ai = ia[2];
+
+    f[av] += f[ai];
+}
+
+template<VirialHandling virialHandling>
+static void spread_vsite2(const t_iatom        ia[],
+                          real                 a,
+                          ArrayRef<const RVec> x,
+                          ArrayRef<RVec>       f,
+                          ArrayRef<RVec>       fshift,
+                          const t_pbc*         pbc)
 {
     rvec    fi, fj, dx;
     t_iatom av, ai, aj;
-    ivec    di;
-    int     siv, sij;
 
     av = ia[1];
     ai = ia[2];
@@ -710,35 +888,33 @@ static void spread_vsite2(const t_iatom  ia[],
     rvec_inc(f[aj], fj);
     /* 6 Flops */
 
-    if (g)
-    {
-        ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, av), di);
-        siv = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), di);
-        sij = IVEC2IS(di);
-    }
-    else if (pbc)
+    if (virialHandling == VirialHandling::Pbc)
     {
-        siv = pbc_dx_aiuc(pbc, x[ai], x[av], dx);
-        sij = pbc_dx_aiuc(pbc, x[ai], x[aj], dx);
-    }
-    else
-    {
-        siv = CENTRAL;
-        sij = CENTRAL;
-    }
+        int siv;
+        int sij;
+        if (pbc)
+        {
+            siv = pbc_dx_aiuc(pbc, x[ai], x[av], dx);
+            sij = pbc_dx_aiuc(pbc, x[ai], x[aj], dx);
+        }
+        else
+        {
+            siv = CENTRAL;
+            sij = CENTRAL;
+        }
 
-    if (fshift && (siv != CENTRAL || sij != CENTRAL))
-    {
-        rvec_inc(fshift[siv], f[av]);
-        rvec_dec(fshift[CENTRAL], fi);
-        rvec_dec(fshift[sij], fj);
+        if (siv != CENTRAL || sij != CENTRAL)
+        {
+            rvec_inc(fshift[siv], f[av]);
+            rvec_dec(fshift[CENTRAL], fi);
+            rvec_dec(fshift[sij], fj);
+        }
     }
 
     /* TOTAL: 13 flops */
 }
 
-void constructVsitesGlobal(const gmx_mtop_t& mtop, gmx::ArrayRef<gmx::RVec> x)
+void constructVirtualSitesGlobal(const gmx_mtop_t& mtop, gmx::ArrayRef<gmx::RVec> x)
 {
     GMX_ASSERT(x.ssize() >= mtop.natoms, "x should contain the whole system");
     GMX_ASSERT(!mtop.moleculeBlockIndices.empty(),
@@ -748,35 +924,27 @@ void constructVsitesGlobal(const gmx_mtop_t& mtop, gmx::ArrayRef<gmx::RVec> x)
     {
         const gmx_molblock_t& molb = mtop.molblock[mb];
         const gmx_moltype_t&  molt = mtop.moltype[molb.type];
-        if (vsiteIlistNrCount(molt.ilist.data()) > 0)
+        if (vsiteIlistNrCount(molt.ilist) > 0)
         {
             int atomOffset = mtop.moleculeBlockIndices[mb].globalAtomStart;
             for (int mol = 0; mol < molb.nmol; mol++)
             {
-                t_ilist ilist[F_NRE];
-                for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
-                {
-                    ilist[ftype].nr     = molt.ilist[ftype].size();
-                    ilist[ftype].iatoms = const_cast<t_iatom*>(molt.ilist[ftype].iatoms.data());
-                }
-
-                construct_vsites(nullptr, as_rvec_array(x.data()) + atomOffset, 0.0, nullptr,
-                                 mtop.ffparams.iparams.data(), ilist, epbcNONE, TRUE, nullptr, nullptr);
+                constructVirtualSites(x.subArray(atomOffset, molt.atoms.nr), mtop.ffparams.iparams,
+                                      molt.ilist);
                 atomOffset += molt.atoms.nr;
             }
         }
     }
 }
 
-static void spread_vsite2FD(const t_iatom  ia[],
-                            real           a,
-                            const rvec     x[],
-                            rvec           f[],
-                            rvec           fshift[],
-                            gmx_bool       VirCorr,
-                            matrix         dxdf,
-                            const t_pbc*   pbc,
-                            const t_graph* g)
+template<VirialHandling virialHandling>
+static void spread_vsite2FD(const t_iatom        ia[],
+                            real                 a,
+                            ArrayRef<const RVec> x,
+                            ArrayRef<RVec>       f,
+                            ArrayRef<RVec>       fshift,
+                            matrix               dxdf,
+                            const t_pbc*         pbc)
 {
     const int av = ia[1];
     const int ai = ia[2];
@@ -811,18 +979,10 @@ static void spread_vsite2FD(const t_iatom  ia[],
     f[aj][ZZ] += fj[ZZ];
     /* 9 Flops */
 
-    if (fshift)
+    if (virialHandling == VirialHandling::Pbc)
     {
         int svi;
-        if (g)
-        {
-            ivec di;
-            ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
-            svi = IVEC2IS(di);
-            ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
-            sji = IVEC2IS(di);
-        }
-        else if (pbc)
+        if (pbc)
         {
             rvec xvi;
             svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
@@ -844,9 +1004,9 @@ static void spread_vsite2FD(const t_iatom  ia[],
         }
     }
 
-    if (VirCorr)
+    if (virialHandling == VirialHandling::NonLinear)
     {
-        /* When VirCorr=TRUE, the virial for the current forces is not
+        /* Under this condition, the virial for the current forces is not
          * calculated from the redistributed forces. This means that
          * the effect of non-linear virtual site constructions on the virial
          * needs to be added separately. This contribution can be calculated
@@ -871,19 +1031,17 @@ static void spread_vsite2FD(const t_iatom  ia[],
     /* TOTAL: 38 flops */
 }
 
-static void spread_vsite3(const t_iatom  ia[],
-                          real           a,
-                          real           b,
-                          const rvec     x[],
-                          rvec           f[],
-                          rvec           fshift[],
-                          const t_pbc*   pbc,
-                          const t_graph* g)
+template<VirialHandling virialHandling>
+static void spread_vsite3(const t_iatom        ia[],
+                          real                 a,
+                          real                 b,
+                          ArrayRef<const RVec> x,
+                          ArrayRef<RVec>       f,
+                          ArrayRef<RVec>       fshift,
+                          const t_pbc*         pbc)
 {
     rvec fi, fj, fk, dx;
     int  av, ai, aj, ak;
-    ivec di;
-    int  siv, sij, sik;
 
     av = ia[1];
     ai = ia[2];
@@ -900,55 +1058,50 @@ static void spread_vsite3(const t_iatom  ia[],
     rvec_inc(f[ak], fk);
     /* 9 Flops */
 
-    if (g)
-    {
-        ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, ia[1]), di);
-        siv = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), di);
-        sij = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, ak), di);
-        sik = IVEC2IS(di);
-    }
-    else if (pbc)
+    if (virialHandling == VirialHandling::Pbc)
     {
-        siv = pbc_dx_aiuc(pbc, x[ai], x[av], dx);
-        sij = pbc_dx_aiuc(pbc, x[ai], x[aj], dx);
-        sik = pbc_dx_aiuc(pbc, x[ai], x[ak], dx);
-    }
-    else
-    {
-        siv = CENTRAL;
-        sij = CENTRAL;
-        sik = CENTRAL;
-    }
+        int siv;
+        int sij;
+        int sik;
+        if (pbc)
+        {
+            siv = pbc_dx_aiuc(pbc, x[ai], x[av], dx);
+            sij = pbc_dx_aiuc(pbc, x[ai], x[aj], dx);
+            sik = pbc_dx_aiuc(pbc, x[ai], x[ak], dx);
+        }
+        else
+        {
+            siv = CENTRAL;
+            sij = CENTRAL;
+            sik = CENTRAL;
+        }
 
-    if (fshift && (siv != CENTRAL || sij != CENTRAL || sik != CENTRAL))
-    {
-        rvec_inc(fshift[siv], f[av]);
-        rvec_dec(fshift[CENTRAL], fi);
-        rvec_dec(fshift[sij], fj);
-        rvec_dec(fshift[sik], fk);
+        if (siv != CENTRAL || sij != CENTRAL || sik != CENTRAL)
+        {
+            rvec_inc(fshift[siv], f[av]);
+            rvec_dec(fshift[CENTRAL], fi);
+            rvec_dec(fshift[sij], fj);
+            rvec_dec(fshift[sik], fk);
+        }
     }
 
     /* TOTAL: 20 flops */
 }
 
-static void spread_vsite3FD(const t_iatom  ia[],
-                            real           a,
-                            real           b,
-                            const rvec     x[],
-                            rvec           f[],
-                            rvec           fshift[],
-                            gmx_bool       VirCorr,
-                            matrix         dxdf,
-                            const t_pbc*   pbc,
-                            const t_graph* g)
+template<VirialHandling virialHandling>
+static void spread_vsite3FD(const t_iatom        ia[],
+                            real                 a,
+                            real                 b,
+                            ArrayRef<const RVec> x,
+                            ArrayRef<RVec>       f,
+                            ArrayRef<RVec>       fshift,
+                            matrix               dxdf,
+                            const t_pbc*         pbc)
 {
     real    fproj, a1;
     rvec    xvi, xij, xjk, xix, fv, temp;
     t_iatom av, ai, aj, ak;
-    int     svi, sji, skj;
-    ivec    di;
+    int     sji, skj;
 
     av = ia[1];
     ai = ia[2];
@@ -992,41 +1145,36 @@ static void spread_vsite3FD(const t_iatom  ia[],
     f[ak][ZZ] += a * temp[ZZ];
     /* 19 Flops */
 
-    if (g)
+    if (virialHandling == VirialHandling::Pbc)
     {
-        ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
-        svi = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
-        sji = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, aj), di);
-        skj = IVEC2IS(di);
-    }
-    else if (pbc)
-    {
-        svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
-    }
-    else
-    {
-        svi = CENTRAL;
-    }
+        int svi;
+        if (pbc)
+        {
+            svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
+        }
+        else
+        {
+            svi = CENTRAL;
+        }
 
-    if (fshift && (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL))
-    {
-        rvec_dec(fshift[svi], fv);
-        fshift[CENTRAL][XX] += fv[XX] - (1 + a) * temp[XX];
-        fshift[CENTRAL][YY] += fv[YY] - (1 + a) * temp[YY];
-        fshift[CENTRAL][ZZ] += fv[ZZ] - (1 + a) * temp[ZZ];
-        fshift[sji][XX] += temp[XX];
-        fshift[sji][YY] += temp[YY];
-        fshift[sji][ZZ] += temp[ZZ];
-        fshift[skj][XX] += a * temp[XX];
-        fshift[skj][YY] += a * temp[YY];
-        fshift[skj][ZZ] += a * temp[ZZ];
+        if (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL)
+        {
+            rvec_dec(fshift[svi], fv);
+            fshift[CENTRAL][XX] += fv[XX] - (1 + a) * temp[XX];
+            fshift[CENTRAL][YY] += fv[YY] - (1 + a) * temp[YY];
+            fshift[CENTRAL][ZZ] += fv[ZZ] - (1 + a) * temp[ZZ];
+            fshift[sji][XX] += temp[XX];
+            fshift[sji][YY] += temp[YY];
+            fshift[sji][ZZ] += temp[ZZ];
+            fshift[skj][XX] += a * temp[XX];
+            fshift[skj][YY] += a * temp[YY];
+            fshift[skj][ZZ] += a * temp[ZZ];
+        }
     }
 
-    if (VirCorr)
+    if (virialHandling == VirialHandling::NonLinear)
     {
-        /* When VirCorr=TRUE, the virial for the current forces is not
+        /* Under this condition, the virial for the current forces is not
          * calculated from the redistributed forces. This means that
          * the effect of non-linear virtual site constructions on the virial
          * needs to be added separately. This contribution can be calculated
@@ -1051,22 +1199,20 @@ static void spread_vsite3FD(const t_iatom  ia[],
     /* TOTAL: 61 flops */
 }
 
-static void spread_vsite3FAD(const t_iatom  ia[],
-                             real           a,
-                             real           b,
-                             const rvec     x[],
-                             rvec           f[],
-                             rvec           fshift[],
-                             gmx_bool       VirCorr,
-                             matrix         dxdf,
-                             const t_pbc*   pbc,
-                             const t_graph* g)
+template<VirialHandling virialHandling>
+static void spread_vsite3FAD(const t_iatom        ia[],
+                             real                 a,
+                             real                 b,
+                             ArrayRef<const RVec> x,
+                             ArrayRef<RVec>       f,
+                             ArrayRef<RVec>       fshift,
+                             matrix               dxdf,
+                             const t_pbc*         pbc)
 {
     rvec    xvi, xij, xjk, xperp, Fpij, Fppp, fv, f1, f2, f3;
     real    a1, b1, c1, c2, invdij, invdij2, invdp, fproj;
     t_iatom av, ai, aj, ak;
-    int     svi, sji, skj, d;
-    ivec    di;
+    int     sji, skj;
 
     av = ia[1];
     ai = ia[2];
@@ -1101,7 +1247,7 @@ static void spread_vsite3FAD(const t_iatom  ia[],
 
     rvec_sub(fv, Fpij, f1); /* f1 = f - Fpij */
     rvec_sub(f1, Fppp, f2); /* f2 = f - Fpij - Fppp */
-    for (d = 0; (d < DIM); d++)
+    for (int d = 0; d < DIM; d++)
     {
         f1[d] *= a1;
         f2[d] *= b1;
@@ -1120,48 +1266,42 @@ static void spread_vsite3FAD(const t_iatom  ia[],
     f[ak][ZZ] += f2[ZZ];
     /* 30 Flops */
 
-    if (g)
-    {
-        ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
-        svi = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
-        sji = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, aj), di);
-        skj = IVEC2IS(di);
-    }
-    else if (pbc)
+    if (virialHandling == VirialHandling::Pbc)
     {
-        svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
-    }
-    else
-    {
-        svi = CENTRAL;
-    }
+        int svi;
 
-    if (fshift && (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL))
-    {
-        rvec_dec(fshift[svi], fv);
-        fshift[CENTRAL][XX] += fv[XX] - f1[XX] - (1 - c1) * f2[XX] + f3[XX];
-        fshift[CENTRAL][YY] += fv[YY] - f1[YY] - (1 - c1) * f2[YY] + f3[YY];
-        fshift[CENTRAL][ZZ] += fv[ZZ] - f1[ZZ] - (1 - c1) * f2[ZZ] + f3[ZZ];
-        fshift[sji][XX] += f1[XX] - c1 * f2[XX] - f3[XX];
-        fshift[sji][YY] += f1[YY] - c1 * f2[YY] - f3[YY];
-        fshift[sji][ZZ] += f1[ZZ] - c1 * f2[ZZ] - f3[ZZ];
-        fshift[skj][XX] += f2[XX];
-        fshift[skj][YY] += f2[YY];
-        fshift[skj][ZZ] += f2[ZZ];
+        if (pbc)
+        {
+            svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
+        }
+        else
+        {
+            svi = CENTRAL;
+        }
+
+        if (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL)
+        {
+            rvec_dec(fshift[svi], fv);
+            fshift[CENTRAL][XX] += fv[XX] - f1[XX] - (1 - c1) * f2[XX] + f3[XX];
+            fshift[CENTRAL][YY] += fv[YY] - f1[YY] - (1 - c1) * f2[YY] + f3[YY];
+            fshift[CENTRAL][ZZ] += fv[ZZ] - f1[ZZ] - (1 - c1) * f2[ZZ] + f3[ZZ];
+            fshift[sji][XX] += f1[XX] - c1 * f2[XX] - f3[XX];
+            fshift[sji][YY] += f1[YY] - c1 * f2[YY] - f3[YY];
+            fshift[sji][ZZ] += f1[ZZ] - c1 * f2[ZZ] - f3[ZZ];
+            fshift[skj][XX] += f2[XX];
+            fshift[skj][YY] += f2[YY];
+            fshift[skj][ZZ] += f2[ZZ];
+        }
     }
 
-    if (VirCorr)
+    if (virialHandling == VirialHandling::NonLinear)
     {
         rvec xiv;
-        int  i, j;
-
         pbc_rvec_sub(pbc, x[av], x[ai], xiv);
 
-        for (i = 0; i < DIM; i++)
+        for (int i = 0; i < DIM; i++)
         {
-            for (j = 0; j < DIM; j++)
+            for (int j = 0; j < DIM; j++)
             {
                 /* Note that xik=xij+xjk, so we have to add xij*f2 */
                 dxdf[i][j] += -xiv[i] * fv[j] + xij[i] * (f1[j] + (1 - c2) * f2[j] - f3[j])
@@ -1173,23 +1313,21 @@ static void spread_vsite3FAD(const t_iatom  ia[],
     /* TOTAL: 113 flops */
 }
 
-static void spread_vsite3OUT(const t_iatom  ia[],
-                             real           a,
-                             real           b,
-                             real           c,
-                             const rvec     x[],
-                             rvec           f[],
-                             rvec           fshift[],
-                             gmx_bool       VirCorr,
-                             matrix         dxdf,
-                             const t_pbc*   pbc,
-                             const t_graph* g)
+template<VirialHandling virialHandling>
+static void spread_vsite3OUT(const t_iatom        ia[],
+                             real                 a,
+                             real                 b,
+                             real                 c,
+                             ArrayRef<const RVec> x,
+                             ArrayRef<RVec>       f,
+                             ArrayRef<RVec>       fshift,
+                             matrix               dxdf,
+                             const t_pbc*         pbc)
 {
     rvec xvi, xij, xik, fv, fj, fk;
     real cfx, cfy, cfz;
     int  av, ai, aj, ak;
-    ivec di;
-    int  svi, sji, ski;
+    int  sji, ski;
 
     av = ia[1];
     ai = ia[2];
@@ -1223,35 +1361,30 @@ static void spread_vsite3OUT(const t_iatom  ia[],
     rvec_inc(f[ak], fk);
     /* 15 Flops */
 
-    if (g)
-    {
-        ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
-        svi = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
-        sji = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, ai), di);
-        ski = IVEC2IS(di);
-    }
-    else if (pbc)
+    if (virialHandling == VirialHandling::Pbc)
     {
-        svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
-    }
-    else
-    {
-        svi = CENTRAL;
-    }
+        int svi;
+        if (pbc)
+        {
+            svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
+        }
+        else
+        {
+            svi = CENTRAL;
+        }
 
-    if (fshift && (svi != CENTRAL || sji != CENTRAL || ski != CENTRAL))
-    {
-        rvec_dec(fshift[svi], fv);
-        fshift[CENTRAL][XX] += fv[XX] - fj[XX] - fk[XX];
-        fshift[CENTRAL][YY] += fv[YY] - fj[YY] - fk[YY];
-        fshift[CENTRAL][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ];
-        rvec_inc(fshift[sji], fj);
-        rvec_inc(fshift[ski], fk);
+        if (svi != CENTRAL || sji != CENTRAL || ski != CENTRAL)
+        {
+            rvec_dec(fshift[svi], fv);
+            fshift[CENTRAL][XX] += fv[XX] - fj[XX] - fk[XX];
+            fshift[CENTRAL][YY] += fv[YY] - fj[YY] - fk[YY];
+            fshift[CENTRAL][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ];
+            rvec_inc(fshift[sji], fj);
+            rvec_inc(fshift[ski], fk);
+        }
     }
 
-    if (VirCorr)
+    if (virialHandling == VirialHandling::NonLinear)
     {
         rvec xiv;
 
@@ -1269,23 +1402,21 @@ static void spread_vsite3OUT(const t_iatom  ia[],
     /* TOTAL: 54 flops */
 }
 
-static void spread_vsite4FD(const t_iatom  ia[],
-                            real           a,
-                            real           b,
-                            real           c,
-                            const rvec     x[],
-                            rvec           f[],
-                            rvec           fshift[],
-                            gmx_bool       VirCorr,
-                            matrix         dxdf,
-                            const t_pbc*   pbc,
-                            const t_graph* g)
+template<VirialHandling virialHandling>
+static void spread_vsite4FD(const t_iatom        ia[],
+                            real                 a,
+                            real                 b,
+                            real                 c,
+                            ArrayRef<const RVec> x,
+                            ArrayRef<RVec>       f,
+                            ArrayRef<RVec>       fshift,
+                            matrix               dxdf,
+                            const t_pbc*         pbc)
 {
     real fproj, a1;
     rvec xvi, xij, xjk, xjl, xix, fv, temp;
     int  av, ai, aj, ak, al;
-    ivec di;
-    int  svi, sji, skj, slj, m;
+    int  sji, skj, slj, m;
 
     av = ia[1];
     ai = ia[2];
@@ -1332,39 +1463,32 @@ static void spread_vsite4FD(const t_iatom  ia[],
     }
     /* 26 Flops */
 
-    if (g)
-    {
-        ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
-        svi = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
-        sji = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, aj), di);
-        skj = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, al), SHIFT_IVEC(g, aj), di);
-        slj = IVEC2IS(di);
-    }
-    else if (pbc)
-    {
-        svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
-    }
-    else
+    if (virialHandling == VirialHandling::Pbc)
     {
-        svi = CENTRAL;
-    }
+        int svi;
+        if (pbc)
+        {
+            svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
+        }
+        else
+        {
+            svi = CENTRAL;
+        }
 
-    if (fshift && (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL || slj != CENTRAL))
-    {
-        rvec_dec(fshift[svi], fv);
-        for (m = 0; m < DIM; m++)
+        if (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL || slj != CENTRAL)
         {
-            fshift[CENTRAL][m] += fv[m] - (1 + a + b) * temp[m];
-            fshift[sji][m] += temp[m];
-            fshift[skj][m] += a * temp[m];
-            fshift[slj][m] += b * temp[m];
+            rvec_dec(fshift[svi], fv);
+            for (m = 0; m < DIM; m++)
+            {
+                fshift[CENTRAL][m] += fv[m] - (1 + a + b) * temp[m];
+                fshift[sji][m] += temp[m];
+                fshift[skj][m] += a * temp[m];
+                fshift[slj][m] += b * temp[m];
+            }
         }
     }
 
-    if (VirCorr)
+    if (virialHandling == VirialHandling::NonLinear)
     {
         rvec xiv;
         int  i, j;
@@ -1383,26 +1507,23 @@ static void spread_vsite4FD(const t_iatom  ia[],
     /* TOTAL: 77 flops */
 }
 
-
-static void spread_vsite4FDN(const t_iatom  ia[],
-                             real           a,
-                             real           b,
-                             real           c,
-                             const rvec     x[],
-                             rvec           f[],
-                             rvec           fshift[],
-                             gmx_bool       VirCorr,
-                             matrix         dxdf,
-                             const t_pbc*   pbc,
-                             const t_graph* g)
+template<VirialHandling virialHandling>
+static void spread_vsite4FDN(const t_iatom        ia[],
+                             real                 a,
+                             real                 b,
+                             real                 c,
+                             ArrayRef<const RVec> x,
+                             ArrayRef<RVec>       f,
+                             ArrayRef<RVec>       fshift,
+                             matrix               dxdf,
+                             const t_pbc*         pbc)
 {
     rvec xvi, xij, xik, xil, ra, rb, rja, rjb, rab, rm, rt;
     rvec fv, fj, fk, fl;
     real invrm, denom;
     real cfx, cfy, cfz;
-    ivec di;
     int  av, ai, aj, ak, al;
-    int  svi, sij, sik, sil;
+    int  sij, sik, sil;
 
     /* DEBUG: check atom indices */
     av = ia[1];
@@ -1501,38 +1622,31 @@ static void spread_vsite4FDN(const t_iatom  ia[],
     rvec_inc(f[al], fl);
     /* 21 flops */
 
-    if (g)
-    {
-        ivec_sub(SHIFT_IVEC(g, av), SHIFT_IVEC(g, ai), di);
-        svi = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
-        sij = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, ai), di);
-        sik = IVEC2IS(di);
-        ivec_sub(SHIFT_IVEC(g, al), SHIFT_IVEC(g, ai), di);
-        sil = IVEC2IS(di);
-    }
-    else if (pbc)
-    {
-        svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
-    }
-    else
+    if (virialHandling == VirialHandling::Pbc)
     {
-        svi = CENTRAL;
-    }
+        int svi;
+        if (pbc)
+        {
+            svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
+        }
+        else
+        {
+            svi = CENTRAL;
+        }
 
-    if (fshift && (svi != CENTRAL || sij != CENTRAL || sik != CENTRAL || sil != CENTRAL))
-    {
-        rvec_dec(fshift[svi], fv);
-        fshift[CENTRAL][XX] += fv[XX] - fj[XX] - fk[XX] - fl[XX];
-        fshift[CENTRAL][YY] += fv[YY] - fj[YY] - fk[YY] - fl[YY];
-        fshift[CENTRAL][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ] - fl[ZZ];
-        rvec_inc(fshift[sij], fj);
-        rvec_inc(fshift[sik], fk);
-        rvec_inc(fshift[sil], fl);
+        if (svi != CENTRAL || sij != CENTRAL || sik != CENTRAL || sil != CENTRAL)
+        {
+            rvec_dec(fshift[svi], fv);
+            fshift[CENTRAL][XX] += fv[XX] - fj[XX] - fk[XX] - fl[XX];
+            fshift[CENTRAL][YY] += fv[YY] - fj[YY] - fk[YY] - fl[YY];
+            fshift[CENTRAL][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ] - fl[ZZ];
+            rvec_inc(fshift[sij], fj);
+            rvec_inc(fshift[sik], fk);
+            rvec_inc(fshift[sil], fl);
+        }
     }
 
-    if (VirCorr)
+    if (virialHandling == VirialHandling::NonLinear)
     {
         rvec xiv;
         int  i, j;
@@ -1551,19 +1665,17 @@ static void spread_vsite4FDN(const t_iatom  ia[],
     /* Total: 207 flops (Yuck!) */
 }
 
-
-static int spread_vsiten(const t_iatom   ia[],
-                         const t_iparams ip[],
-                         const rvec      x[],
-                         rvec            f[],
-                         rvec            fshift[],
-                         const t_pbc*    pbc,
-                         const t_graph*  g)
+template<VirialHandling virialHandling>
+static int spread_vsiten(const t_iatom             ia[],
+                         ArrayRef<const t_iparams> ip,
+                         ArrayRef<const RVec>      x,
+                         ArrayRef<RVec>            f,
+                         ArrayRef<RVec>            fshift,
+                         const t_pbc*              pbc)
 {
     rvec xv, dx, fi;
     int  n3, av, i, ai;
     real a;
-    ivec di;
     int  siv;
 
     n3 = 3 * ip[ia[0]].vsiten.n;
@@ -1573,12 +1685,7 @@ static int spread_vsiten(const t_iatom   ia[],
     for (i = 0; i < n3; i += 3)
     {
         ai = ia[i + 2];
-        if (g)
-        {
-            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, av), di);
-            siv = IVEC2IS(di);
-        }
-        else if (pbc)
+        if (pbc)
         {
             siv = pbc_dx_aiuc(pbc, x[ai], xv, dx);
         }
@@ -1589,7 +1696,8 @@ static int spread_vsiten(const t_iatom   ia[],
         a = ip[ia[i]].vsiten.a;
         svmul(a, f[av], fi);
         rvec_inc(f[ai], fi);
-        if (fshift && siv != CENTRAL)
+
+        if (virialHandling == VirialHandling::Pbc && siv != CENTRAL)
         {
             rvec_inc(fshift[siv], fi);
             rvec_dec(fshift[CENTRAL], fi);
@@ -1600,28 +1708,30 @@ static int spread_vsiten(const t_iatom   ia[],
     return n3;
 }
 
+#endif // DOXYGEN
 
-static int vsite_count(const t_ilist* ilist, int ftype)
+//! Returns the number of virtual sites in the interaction list, for VSITEN the number of atoms
+static int vsite_count(ArrayRef<const InteractionList> ilist, int ftype)
 {
     if (ftype == F_VSITEN)
     {
-        return ilist[ftype].nr / 3;
+        return ilist[ftype].size() / 3;
     }
     else
     {
-        return ilist[ftype].nr / (1 + interaction_function[ftype].nratoms);
+        return ilist[ftype].size() / (1 + interaction_function[ftype].nratoms);
     }
 }
 
-static void spread_vsite_f_thread(const rvec     x[],
-                                  rvec           f[],
-                                  rvec*          fshift,
-                                  gmx_bool       VirCorr,
-                                  matrix         dxdf,
-                                  t_iparams      ip[],
-                                  const t_ilist  ilist[],
-                                  const t_graph* g,
-                                  const t_pbc*   pbc_null)
+//! Executes the force spreading task for a single thread
+template<VirialHandling virialHandling>
+static void spreadForceForThread(ArrayRef<const RVec>            x,
+                                 ArrayRef<RVec>                  f,
+                                 ArrayRef<RVec>                  fshift,
+                                 matrix                          dxdf,
+                                 ArrayRef<const t_iparams>       ip,
+                                 ArrayRef<const InteractionList> ilist,
+                                 const t_pbc*                    pbc_null)
 {
     const PbcMode pbcMode = getPbcMode(pbc_null);
     /* We need another pbc pointer, as with charge groups we switch per vsite */
@@ -1632,7 +1742,7 @@ static void spread_vsite_f_thread(const rvec     x[],
      * higher type vsites from lower types         */
     for (int ftype = c_ftypeVsiteEnd - 1; ftype >= c_ftypeVsiteStart; ftype--)
     {
-        if (ilist[ftype].nr == 0)
+        if (ilist[ftype].empty())
         {
             continue;
         }
@@ -1640,9 +1750,9 @@ static void spread_vsite_f_thread(const rvec     x[],
         { // TODO remove me
             int nra = interaction_function[ftype].nratoms;
             int inc = 1 + nra;
-            int nr  = ilist[ftype].nr;
+            int nr  = ilist[ftype].size();
 
-            const t_iatom* ia = ilist[ftype].iatoms;
+            const t_iatom* ia = ilist[ftype].iatoms.data();
 
             if (pbcMode == PbcMode::all)
             {
@@ -1659,38 +1769,43 @@ static void spread_vsite_f_thread(const rvec     x[],
                 /* Construct the vsite depending on type */
                 switch (ftype)
                 {
-                    case F_VSITE2: spread_vsite2(ia, a1, x, f, fshift, pbc_null2, g); break;
+                    case F_VSITE1: spread_vsite1(ia, f); break;
+                    case F_VSITE2:
+                        spread_vsite2<virialHandling>(ia, a1, x, f, fshift, pbc_null2);
+                        break;
                     case F_VSITE2FD:
-                        spread_vsite2FD(ia, a1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
+                        spread_vsite2FD<virialHandling>(ia, a1, x, f, fshift, dxdf, pbc_null2);
                         break;
                     case F_VSITE3:
                         b1 = ip[tp].vsite.b;
-                        spread_vsite3(ia, a1, b1, x, f, fshift, pbc_null2, g);
+                        spread_vsite3<virialHandling>(ia, a1, b1, x, f, fshift, pbc_null2);
                         break;
                     case F_VSITE3FD:
                         b1 = ip[tp].vsite.b;
-                        spread_vsite3FD(ia, a1, b1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
+                        spread_vsite3FD<virialHandling>(ia, a1, b1, x, f, fshift, dxdf, pbc_null2);
                         break;
                     case F_VSITE3FAD:
                         b1 = ip[tp].vsite.b;
-                        spread_vsite3FAD(ia, a1, b1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
+                        spread_vsite3FAD<virialHandling>(ia, a1, b1, x, f, fshift, dxdf, pbc_null2);
                         break;
                     case F_VSITE3OUT:
                         b1 = ip[tp].vsite.b;
                         c1 = ip[tp].vsite.c;
-                        spread_vsite3OUT(ia, a1, b1, c1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
+                        spread_vsite3OUT<virialHandling>(ia, a1, b1, c1, x, f, fshift, dxdf, pbc_null2);
                         break;
                     case F_VSITE4FD:
                         b1 = ip[tp].vsite.b;
                         c1 = ip[tp].vsite.c;
-                        spread_vsite4FD(ia, a1, b1, c1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
+                        spread_vsite4FD<virialHandling>(ia, a1, b1, c1, x, f, fshift, dxdf, pbc_null2);
                         break;
                     case F_VSITE4FDN:
                         b1 = ip[tp].vsite.b;
                         c1 = ip[tp].vsite.c;
-                        spread_vsite4FDN(ia, a1, b1, c1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
+                        spread_vsite4FDN<virialHandling>(ia, a1, b1, c1, x, f, fshift, dxdf, pbc_null2);
+                        break;
+                    case F_VSITEN:
+                        inc = spread_vsiten<virialHandling>(ia, ip, x, f, fshift, pbc_null2);
                         break;
-                    case F_VSITEN: inc = spread_vsiten(ia, ip, x, f, fshift, pbc_null2, g); break;
                     default:
                         gmx_fatal(FARGS, "No such vsite type %d in %s, line %d", ftype, __FILE__, __LINE__);
                 }
@@ -1704,7 +1819,37 @@ static void spread_vsite_f_thread(const rvec     x[],
     }
 }
 
-/*! \brief Clears the task force buffer elements that are written by task idTask */
+//! Wrapper function for calling the templated thread-local spread function
+static void spreadForceWrapper(ArrayRef<const RVec>            x,
+                               ArrayRef<RVec>                  f,
+                               const VirialHandling            virialHandling,
+                               ArrayRef<RVec>                  fshift,
+                               matrix                          dxdf,
+                               const bool                      clearDxdf,
+                               ArrayRef<const t_iparams>       ip,
+                               ArrayRef<const InteractionList> ilist,
+                               const t_pbc*                    pbc_null)
+{
+    if (virialHandling == VirialHandling::NonLinear && clearDxdf)
+    {
+        clear_mat(dxdf);
+    }
+
+    switch (virialHandling)
+    {
+        case VirialHandling::None:
+            spreadForceForThread<VirialHandling::None>(x, f, fshift, dxdf, ip, ilist, pbc_null);
+            break;
+        case VirialHandling::Pbc:
+            spreadForceForThread<VirialHandling::Pbc>(x, f, fshift, dxdf, ip, ilist, pbc_null);
+            break;
+        case VirialHandling::NonLinear:
+            spreadForceForThread<VirialHandling::NonLinear>(x, f, fshift, dxdf, ip, ilist, pbc_null);
+            break;
+    }
+}
+
+//! Clears the task force buffer elements that are written by task idTask
 static void clearTaskForceBufferUsedElements(InterdependentTask* idTask)
 {
     int ntask = idTask->spreadTask.size();
@@ -1720,35 +1865,28 @@ static void clearTaskForceBufferUsedElements(InterdependentTask* idTask)
     }
 }
 
-void spread_vsite_f(const gmx_vsite_t* vsite,
-                    const rvec* gmx_restrict x,
-                    rvec* gmx_restrict f,
-                    rvec* gmx_restrict fshift,
-                    gmx_bool           VirCorr,
-                    matrix             vir,
-                    t_nrnb*            nrnb,
-                    const t_idef*      idef,
-                    int                ePBC,
-                    gmx_bool           bMolPBC,
-                    const t_graph*     g,
-                    const matrix       box,
-                    const t_commrec*   cr,
-                    gmx_wallcycle*     wcycle)
+void VirtualSitesHandler::Impl::spreadForces(ArrayRef<const RVec> x,
+                                             ArrayRef<RVec>       f,
+                                             const VirialHandling virialHandling,
+                                             ArrayRef<RVec>       fshift,
+                                             matrix               virial,
+                                             t_nrnb*              nrnb,
+                                             const matrix         box,
+                                             gmx_wallcycle*       wcycle)
 {
     wallcycle_start(wcycle, ewcVSITESPREAD);
-    const bool useDomdec = vsite->useDomdec;
-    GMX_ASSERT(!useDomdec || (cr != nullptr && DOMAINDECOMP(cr)),
-               "When vsites are set up with domain decomposition, we need a valid commrec");
+
+    const bool useDomdec = domainInfo_.useDomdec();
 
     t_pbc pbc, *pbc_null;
 
-    /* We only need to do pbc when we have inter-cg vsites */
-    if ((useDomdec || bMolPBC) && vsite->numInterUpdategroupVsites)
+    if (domainInfo_.useMolPbc_)
     {
         /* This is wasting some CPU time as we now do this multiple times
          * per MD step.
          */
-        pbc_null = set_pbc_dd(&pbc, ePBC, useDomdec ? cr->dd->nc : nullptr, FALSE, box);
+        pbc_null = set_pbc_dd(&pbc, domainInfo_.pbcType_,
+                              useDomdec ? domainInfo_.domdec_->numCells : nullptr, FALSE, box);
     }
     else
     {
@@ -1757,25 +1895,23 @@ void spread_vsite_f(const gmx_vsite_t* vsite,
 
     if (useDomdec)
     {
-        dd_clear_f_vsites(cr->dd, f);
+        dd_clear_f_vsites(*domainInfo_.domdec_, f);
     }
 
-    if (vsite->nthreads == 1)
+    const int numThreads = threadingInfo_.numThreads();
+
+    if (numThreads == 1)
     {
         matrix dxdf;
-        if (VirCorr)
-        {
-            clear_mat(dxdf);
-        }
-        spread_vsite_f_thread(x, f, fshift, VirCorr, dxdf, idef->iparams, idef->il, g, pbc_null);
+        spreadForceWrapper(x, f, virialHandling, fshift, dxdf, true, iparams_, ilists_, pbc_null);
 
-        if (VirCorr)
+        if (virialHandling == VirialHandling::NonLinear)
         {
             for (int i = 0; i < DIM; i++)
             {
                 for (int j = 0; j < DIM; j++)
                 {
-                    vir[i][j] += -0.5 * dxdf[i][j];
+                    virial[i][j] += -0.5 * dxdf[i][j];
                 }
             }
         }
@@ -1783,37 +1919,33 @@ void spread_vsite_f(const gmx_vsite_t* vsite,
     else
     {
         /* First spread the vsites that might depend on non-local vsites */
-        if (VirCorr)
-        {
-            clear_mat(vsite->tData[vsite->nthreads]->dxdf);
-        }
-        spread_vsite_f_thread(x, f, fshift, VirCorr, vsite->tData[vsite->nthreads]->dxdf,
-                              idef->iparams, vsite->tData[vsite->nthreads]->ilist, g, pbc_null);
+        auto& nlDependentVSites = threadingInfo_.threadDataNonLocalDependent();
+        spreadForceWrapper(x, f, virialHandling, fshift, nlDependentVSites.dxdf, true, iparams_,
+                           nlDependentVSites.ilist, pbc_null);
 
-#pragma omp parallel num_threads(vsite->nthreads)
+#pragma omp parallel num_threads(numThreads)
         {
             try
             {
                 int          thread = gmx_omp_get_thread_num();
-                VsiteThread& tData  = *vsite->tData[thread];
+                VsiteThread& tData  = threadingInfo_.threadData(thread);
 
-                rvec* fshift_t;
-                if (thread == 0 || fshift == nullptr)
+                ArrayRef<RVec> fshift_t;
+                if (virialHandling == VirialHandling::Pbc)
                 {
-                    fshift_t = fshift;
-                }
-                else
-                {
-                    fshift_t = tData.fshift;
-
-                    for (int i = 0; i < SHIFTS; i++)
+                    if (thread == 0)
                     {
-                        clear_rvec(fshift_t[i]);
+                        fshift_t = fshift;
+                    }
+                    else
+                    {
+                        fshift_t = tData.fshift;
+
+                        for (int i = 0; i < SHIFTS; i++)
+                        {
+                            clear_rvec(fshift_t[i]);
+                        }
                     }
-                }
-                if (VirCorr)
-                {
-                    clear_mat(tData.dxdf);
                 }
 
                 if (tData.useInterdependentTask)
@@ -1834,8 +1966,8 @@ void spread_vsite_f(const gmx_vsite_t* vsite,
                     {
                         copy_rvec(f[idTask->vsite[i]], idTask->force[idTask->vsite[i]]);
                     }
-                    spread_vsite_f_thread(x, as_rvec_array(idTask->force.data()), fshift_t, VirCorr,
-                                          tData.dxdf, idef->iparams, tData.idTask.ilist, g, pbc_null);
+                    spreadForceWrapper(x, idTask->force, virialHandling, fshift_t, tData.dxdf, true,
+                                       iparams_, tData.idTask.ilist, pbc_null);
 
                     /* We need a barrier before reducing forces below
                      * that have been produced by a different thread above.
@@ -1850,15 +1982,13 @@ void spread_vsite_f(const gmx_vsite_t* vsite,
                     int ntask = idTask->reduceTask.size();
                     for (int ti = 0; ti < ntask; ti++)
                     {
-                        const InterdependentTask* idt_foreign =
-                                &vsite->tData[idTask->reduceTask[ti]]->idTask;
-                        const AtomIndex* atomList  = &idt_foreign->atomIndex[thread];
-                        const RVec*      f_foreign = idt_foreign->force.data();
+                        const InterdependentTask& idt_foreign =
+                                threadingInfo_.threadData(idTask->reduceTask[ti]).idTask;
+                        const AtomIndex& atomList  = idt_foreign.atomIndex[thread];
+                        const RVec*      f_foreign = idt_foreign.force.data();
 
-                        int natom = atomList->atom.size();
-                        for (int i = 0; i < natom; i++)
+                        for (int ind : atomList.atom)
                         {
-                            int ind = atomList->atom[i];
                             rvec_inc(f[ind], f_foreign[ind]);
                             /* Clearing of f_foreign is done at the next step */
                         }
@@ -1873,35 +2003,35 @@ void spread_vsite_f(const gmx_vsite_t* vsite,
                 }
 
                 /* Spread the vsites that spread locally only */
-                spread_vsite_f_thread(x, f, fshift_t, VirCorr, tData.dxdf, idef->iparams,
-                                      tData.ilist, g, pbc_null);
+                spreadForceWrapper(x, f, virialHandling, fshift_t, tData.dxdf, false, iparams_,
+                                   tData.ilist, pbc_null);
             }
             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
         }
 
-        if (fshift != nullptr)
+        if (virialHandling == VirialHandling::Pbc)
         {
-            for (int th = 1; th < vsite->nthreads; th++)
+            for (int th = 1; th < numThreads; th++)
             {
                 for (int i = 0; i < SHIFTS; i++)
                 {
-                    rvec_inc(fshift[i], vsite->tData[th]->fshift[i]);
+                    rvec_inc(fshift[i], threadingInfo_.threadData(th).fshift[i]);
                 }
             }
         }
 
-        if (VirCorr)
+        if (virialHandling == VirialHandling::NonLinear)
         {
-            for (int th = 0; th < vsite->nthreads + 1; th++)
+            for (int th = 0; th < numThreads + 1; th++)
             {
                 /* MSVC doesn't like matrix references, so we use a pointer */
-                const matrix* dxdf = &vsite->tData[th]->dxdf;
+                const matrix& dxdf = threadingInfo_.threadData(th).dxdf;
 
                 for (int i = 0; i < DIM; i++)
                 {
                     for (int j = 0; j < DIM; j++)
                     {
-                        vir[i][j] += -0.5 * (*dxdf)[i][j];
+                        virial[i][j] += -0.5 * dxdf[i][j];
                     }
                 }
             }
@@ -1910,18 +2040,19 @@ void spread_vsite_f(const gmx_vsite_t* vsite,
 
     if (useDomdec)
     {
-        dd_move_f_vsites(cr->dd, f, fshift);
+        dd_move_f_vsites(*domainInfo_.domdec_, f, fshift);
     }
 
-    inc_nrnb(nrnb, eNR_VSITE2, vsite_count(idef->il, F_VSITE2));
-    inc_nrnb(nrnb, eNR_VSITE2FD, vsite_count(idef->il, F_VSITE2FD));
-    inc_nrnb(nrnb, eNR_VSITE3, vsite_count(idef->il, F_VSITE3));
-    inc_nrnb(nrnb, eNR_VSITE3FD, vsite_count(idef->il, F_VSITE3FD));
-    inc_nrnb(nrnb, eNR_VSITE3FAD, vsite_count(idef->il, F_VSITE3FAD));
-    inc_nrnb(nrnb, eNR_VSITE3OUT, vsite_count(idef->il, F_VSITE3OUT));
-    inc_nrnb(nrnb, eNR_VSITE4FD, vsite_count(idef->il, F_VSITE4FD));
-    inc_nrnb(nrnb, eNR_VSITE4FDN, vsite_count(idef->il, F_VSITE4FDN));
-    inc_nrnb(nrnb, eNR_VSITEN, vsite_count(idef->il, F_VSITEN));
+    inc_nrnb(nrnb, eNR_VSITE1, vsite_count(ilists_, F_VSITE1));
+    inc_nrnb(nrnb, eNR_VSITE2, vsite_count(ilists_, F_VSITE2));
+    inc_nrnb(nrnb, eNR_VSITE2FD, vsite_count(ilists_, F_VSITE2FD));
+    inc_nrnb(nrnb, eNR_VSITE3, vsite_count(ilists_, F_VSITE3));
+    inc_nrnb(nrnb, eNR_VSITE3FD, vsite_count(ilists_, F_VSITE3FD));
+    inc_nrnb(nrnb, eNR_VSITE3FAD, vsite_count(ilists_, F_VSITE3FAD));
+    inc_nrnb(nrnb, eNR_VSITE3OUT, vsite_count(ilists_, F_VSITE3OUT));
+    inc_nrnb(nrnb, eNR_VSITE4FD, vsite_count(ilists_, F_VSITE4FD));
+    inc_nrnb(nrnb, eNR_VSITE4FDN, vsite_count(ilists_, F_VSITE4FDN));
+    inc_nrnb(nrnb, eNR_VSITEN, vsite_count(ilists_, F_VSITEN));
 
     wallcycle_stop(wcycle, ewcVSITESPREAD);
 }
@@ -1963,6 +2094,18 @@ int countNonlinearVsites(const gmx_mtop_t& mtop)
     return numNonlinearVsites;
 }
 
+void VirtualSitesHandler::spreadForces(ArrayRef<const RVec> x,
+                                       ArrayRef<RVec>       f,
+                                       const VirialHandling virialHandling,
+                                       ArrayRef<RVec>       fshift,
+                                       matrix               virial,
+                                       t_nrnb*              nrnb,
+                                       const matrix         box,
+                                       gmx_wallcycle*       wcycle)
+{
+    impl_->spreadForces(x, f, virialHandling, fshift, virial, nrnb, box, wcycle);
+}
+
 int countInterUpdategroupVsites(const gmx_mtop_t&                           mtop,
                                 gmx::ArrayRef<const gmx::RangePartitioning> updateGroupingPerMoleculetype)
 {
@@ -2006,11 +2149,13 @@ int countInterUpdategroupVsites(const gmx_mtop_t&                           mtop
     return n_intercg_vsite;
 }
 
-std::unique_ptr<gmx_vsite_t> initVsite(const gmx_mtop_t& mtop, const t_commrec* cr)
+std::unique_ptr<VirtualSitesHandler> makeVirtualSitesHandler(const gmx_mtop_t& mtop,
+                                                             const t_commrec*  cr,
+                                                             PbcType           pbcType)
 {
     GMX_RELEASE_ASSERT(cr != nullptr, "We need a valid commrec");
 
-    std::unique_ptr<gmx_vsite_t> vsite;
+    std::unique_ptr<VirtualSitesHandler> vsite;
 
     /* check if there are vsites */
     int nvsite = 0;
@@ -2035,57 +2180,68 @@ std::unique_ptr<gmx_vsite_t> initVsite(const gmx_mtop_t& mtop, const t_commrec*
         return vsite;
     }
 
-    vsite = std::make_unique<gmx_vsite_t>();
-
-    gmx::ArrayRef<const gmx::RangePartitioning> updateGroupingPerMoleculetype;
-    if (DOMAINDECOMP(cr))
-    {
-        updateGroupingPerMoleculetype = getUpdateGroupingPerMoleculetype(*cr->dd);
-    }
-    vsite->numInterUpdategroupVsites = countInterUpdategroupVsites(mtop, updateGroupingPerMoleculetype);
-
-    vsite->useDomdec = (DOMAINDECOMP(cr) && cr->dd->nnodes > 1);
-
-    vsite->nthreads = gmx_omp_nthreads_get(emntVSITE);
+    return std::make_unique<VirtualSitesHandler>(mtop, cr->dd, pbcType);
+}
 
-    if (vsite->nthreads > 1)
+ThreadingInfo::ThreadingInfo() : numThreads_(gmx_omp_nthreads_get(emntVSITE))
+{
+    if (numThreads_ > 1)
     {
         /* We need one extra thread data structure for the overlap vsites */
-        vsite->tData.resize(vsite->nthreads + 1);
-#pragma omp parallel for num_threads(vsite->nthreads) schedule(static)
-        for (int thread = 0; thread < vsite->nthreads; thread++)
+        tData_.resize(numThreads_ + 1);
+#pragma omp parallel for num_threads(numThreads_) schedule(static)
+        for (int thread = 0; thread < numThreads_; thread++)
         {
             try
             {
-                vsite->tData[thread] = std::make_unique<VsiteThread>();
+                tData_[thread] = std::make_unique<VsiteThread>();
 
-                InterdependentTask& idTask = vsite->tData[thread]->idTask;
+                InterdependentTask& idTask = tData_[thread]->idTask;
                 idTask.nuse                = 0;
-                idTask.atomIndex.resize(vsite->nthreads);
+                idTask.atomIndex.resize(numThreads_);
             }
             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
         }
-        if (vsite->nthreads > 1)
+        if (numThreads_ > 1)
         {
-            vsite->tData[vsite->nthreads] = std::make_unique<VsiteThread>();
+            tData_[numThreads_] = std::make_unique<VsiteThread>();
         }
     }
+}
 
-    return vsite;
+//! Returns the number of inter update-group vsites
+static int getNumInterUpdategroupVsites(const gmx_mtop_t& mtop, const gmx_domdec_t* domdec)
+{
+    gmx::ArrayRef<const gmx::RangePartitioning> updateGroupingPerMoleculetype;
+    if (domdec)
+    {
+        updateGroupingPerMoleculetype = getUpdateGroupingPerMoleculetype(*domdec);
+    }
+
+    return countInterUpdategroupVsites(mtop, updateGroupingPerMoleculetype);
 }
 
-gmx_vsite_t::gmx_vsite_t() {}
+VirtualSitesHandler::Impl::Impl(const gmx_mtop_t& mtop, gmx_domdec_t* domdec, const PbcType pbcType) :
+    numInterUpdategroupVirtualSites_(getNumInterUpdategroupVsites(mtop, domdec)),
+    domainInfo_({ pbcType, pbcType != PbcType::No && numInterUpdategroupVirtualSites_ > 0, domdec }),
+    iparams_(mtop.ffparams.iparams)
+{
+}
 
-gmx_vsite_t::~gmx_vsite_t() {}
+VirtualSitesHandler::VirtualSitesHandler(const gmx_mtop_t& mtop, gmx_domdec_t* domdec, const PbcType pbcType) :
+    impl_(new Impl(mtop, domdec, pbcType))
+{
+}
 
-static inline void flagAtom(InterdependentTask* idTask, int atom, int thread, int nthread, int natperthread)
+//! Flag that atom \p atom which is home in another task, if it has not already been added before
+static inline void flagAtom(InterdependentTask* idTask, const int atom, const int numThreads, const int numAtomsPerThread)
 {
     if (!idTask->use[atom])
     {
         idTask->use[atom] = true;
-        thread            = atom / natperthread;
+        int thread        = atom / numAtomsPerThread;
         /* Assign all non-local atom force writes to thread 0 */
-        if (thread >= nthread)
+        if (thread >= numThreads)
         {
             thread = 0;
         }
@@ -2093,7 +2249,7 @@ static inline void flagAtom(InterdependentTask* idTask, int atom, int thread, in
     }
 }
 
-/*\brief Here we try to assign all vsites that are in our local range.
+/*\brief Here we try to assign all vsites that are in our local range.
  *
  * Our task local atom range is tData->rangeStart - tData->rangeEnd.
  * Vsites that depend only on local atoms, as indicated by taskIndex[]==thread,
@@ -2102,35 +2258,33 @@ static inline void flagAtom(InterdependentTask* idTask, int atom, int thread, in
  * taskIndex[] is set for all vsites in our range, either to our local tasks
  * or to the single last task as taskIndex[]=2*nthreads.
  */
-static void assignVsitesToThread(VsiteThread*          tData,
-                                 int                   thread,
-                                 int                   nthread,
-                                 int                   natperthread,
-                                 gmx::ArrayRef<int>    taskIndex,
-                                 const t_ilist*        ilist,
-                                 const t_iparams*      ip,
-                                 const unsigned short* ptype)
+static void assignVsitesToThread(VsiteThread*                    tData,
+                                 int                             thread,
+                                 int                             nthread,
+                                 int                             natperthread,
+                                 gmx::ArrayRef<int>              taskIndex,
+                                 ArrayRef<const InteractionList> ilist,
+                                 ArrayRef<const t_iparams>       ip,
+                                 const unsigned short*           ptype)
 {
     for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
     {
-        tData->ilist[ftype].nr        = 0;
-        tData->idTask.ilist[ftype].nr = 0;
+        tData->ilist[ftype].clear();
+        tData->idTask.ilist[ftype].clear();
 
-        int      nral1 = 1 + NRAL(ftype);
-        int      inc   = nral1;
-        t_iatom* iat   = ilist[ftype].iatoms;
-        for (int i = 0; i < ilist[ftype].nr;)
+        const int  nral1 = 1 + NRAL(ftype);
+        const int* iat   = ilist[ftype].iatoms.data();
+        for (int i = 0; i < ilist[ftype].size();)
         {
-            if (ftype == F_VSITEN)
-            {
-                /* The 3 below is from 1+NRAL(ftype)=3 */
-                inc = ip[iat[i]].vsiten.n * 3;
-            }
+            /* Get the number of iatom entries in this virtual site.
+             * The 3 below for F_VSITEN is from 1+NRAL(ftype)=3
+             */
+            const int numIAtoms = (ftype == F_VSITEN ? ip[iat[i]].vsiten.n * 3 : nral1);
 
             if (iat[1 + i] < tData->rangeStart || iat[1 + i] >= tData->rangeEnd)
             {
                 /* This vsite belongs to a different thread */
-                i += inc;
+                i += numIAtoms;
                 continue;
             }
 
@@ -2171,7 +2325,7 @@ static void assignVsitesToThread(VsiteThread*          tData,
             }
             else
             {
-                for (int j = i + 2; j < i + inc; j += 3)
+                for (int j = i + 2; j < i + numIAtoms; j += 3)
                 {
                     /* Do a range check to avoid a harmless race on taskIndex */
                     if (iat[j] < tData->rangeStart || iat[j] >= tData->rangeEnd || taskIndex[iat[j]] != thread)
@@ -2192,7 +2346,7 @@ static void assignVsitesToThread(VsiteThread*          tData,
             if (task == thread || task == nthread + thread)
             {
                 /* Copy this vsite to the thread data struct of thread */
-                t_ilist* il_task;
+                InteractionList* il_task;
                 if (task == thread)
                 {
                     il_task = &tData->ilist[ftype];
@@ -2201,20 +2355,11 @@ static void assignVsitesToThread(VsiteThread*          tData,
                 {
                     il_task = &tData->idTask.ilist[ftype];
                 }
-                /* Ensure we have sufficient memory allocated */
-                if (il_task->nr + inc > il_task->nalloc)
-                {
-                    il_task->nalloc = over_alloc_large(il_task->nr + inc);
-                    srenew(il_task->iatoms, il_task->nalloc);
-                }
                 /* Copy the vsite data to the thread-task local array */
-                for (int j = i; j < i + inc; j++)
-                {
-                    il_task->iatoms[il_task->nr++] = iat[j];
-                }
+                il_task->push_back(iat[i], numIAtoms - 1, iat + i + 1);
                 if (task == nthread + thread)
                 {
-                    /* This vsite write outside our own task force block.
+                    /* This vsite writes outside our own task force block.
                      * Put it into the interdependent task list and flag
                      * the atoms involved for reduction.
                      */
@@ -2223,42 +2368,42 @@ static void assignVsitesToThread(VsiteThread*          tData,
                     {
                         for (int j = i + 2; j < i + nral1; j++)
                         {
-                            flagAtom(&tData->idTask, iat[j], thread, nthread, natperthread);
+                            flagAtom(&tData->idTask, iat[j], nthread, natperthread);
                         }
                     }
                     else
                     {
-                        for (int j = i + 2; j < i + inc; j += 3)
+                        for (int j = i + 2; j < i + numIAtoms; j += 3)
                         {
-                            flagAtom(&tData->idTask, iat[j], thread, nthread, natperthread);
+                            flagAtom(&tData->idTask, iat[j], nthread, natperthread);
                         }
                     }
                 }
             }
 
-            i += inc;
+            i += numIAtoms;
         }
     }
 }
 
 /*! \brief Assign all vsites with taskIndex[]==task to task tData */
-static void assignVsitesToSingleTask(VsiteThread*             tData,
-                                     int                      task,
-                                     gmx::ArrayRef<const int> taskIndex,
-                                     const t_ilist*           ilist,
-                                     const t_iparams*         ip)
+static void assignVsitesToSingleTask(VsiteThread*                    tData,
+                                     int                             task,
+                                     gmx::ArrayRef<const int>        taskIndex,
+                                     ArrayRef<const InteractionList> ilist,
+                                     ArrayRef<const t_iparams>       ip)
 {
     for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
     {
-        tData->ilist[ftype].nr        = 0;
-        tData->idTask.ilist[ftype].nr = 0;
+        tData->ilist[ftype].clear();
+        tData->idTask.ilist[ftype].clear();
 
-        int      nral1   = 1 + NRAL(ftype);
-        int      inc     = nral1;
-        t_iatom* iat     = ilist[ftype].iatoms;
-        t_ilist* il_task = &tData->ilist[ftype];
+        int              nral1   = 1 + NRAL(ftype);
+        int              inc     = nral1;
+        const int*       iat     = ilist[ftype].iatoms.data();
+        InteractionList* il_task = &tData->ilist[ftype];
 
-        for (int i = 0; i < ilist[ftype].nr;)
+        for (int i = 0; i < ilist[ftype].size();)
         {
             if (ftype == F_VSITEN)
             {
@@ -2268,17 +2413,8 @@ static void assignVsitesToSingleTask(VsiteThread*             tData,
             /* Check if the vsite is assigned to our task */
             if (taskIndex[iat[1 + i]] == task)
             {
-                /* Ensure we have sufficient memory allocated */
-                if (il_task->nr + inc > il_task->nalloc)
-                {
-                    il_task->nalloc = over_alloc_large(il_task->nr + inc);
-                    srenew(il_task->iatoms, il_task->nalloc);
-                }
                 /* Copy the vsite data to the thread-task local array */
-                for (int j = i; j < i + inc; j++)
-                {
-                    il_task->iatoms[il_task->nr++] = iat[j];
-                }
+                il_task->push_back(iat[i], inc - 1, iat + i + 1);
             }
 
             i += inc;
@@ -2286,11 +2422,12 @@ static void assignVsitesToSingleTask(VsiteThread*             tData,
     }
 }
 
-void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const t_mdatoms* mdatoms, gmx_vsite_t* vsite)
+void ThreadingInfo::setVirtualSites(ArrayRef<const InteractionList> ilists,
+                                    ArrayRef<const t_iparams>       iparams,
+                                    const t_mdatoms&                mdatoms,
+                                    const bool                      useDomdec)
 {
-    int vsite_atom_range, natperthread;
-
-    if (vsite->nthreads == 1)
+    if (numThreads_ <= 1)
     {
         /* Nothing to do */
         return;
@@ -2306,7 +2443,9 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
      * uniformly in each domain along the major dimension, usually x,
      * it will also perform well.
      */
-    if (!vsite->useDomdec)
+    int vsite_atom_range;
+    int natperthread;
+    if (!useDomdec)
     {
         vsite_atom_range = -1;
         for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
@@ -2314,9 +2453,9 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
             { // TODO remove me
                 if (ftype != F_VSITEN)
                 {
-                    int            nral1 = 1 + NRAL(ftype);
-                    const t_iatom* iat   = ilist[ftype].iatoms;
-                    for (int i = 0; i < ilist[ftype].nr; i += nral1)
+                    int                 nral1 = 1 + NRAL(ftype);
+                    ArrayRef<const int> iat   = ilists[ftype].iatoms;
+                    for (int i = 0; i < ilists[ftype].size(); i += nral1)
                     {
                         for (int j = i + 1; j < i + nral1; j++)
                         {
@@ -2328,13 +2467,13 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
                 {
                     int vs_ind_end;
 
-                    const t_iatom* iat = ilist[ftype].iatoms;
+                    ArrayRef<const int> iat = ilists[ftype].iatoms;
 
                     int i = 0;
-                    while (i < ilist[ftype].nr)
+                    while (i < ilists[ftype].size())
                     {
                         /* The 3 below is from 1+NRAL(ftype)=3 */
-                        vs_ind_end = i + ip[iat[i]].vsiten.n * 3;
+                        vs_ind_end = i + iparams[iat[i]].vsiten.n * 3;
 
                         vsite_atom_range = std::max(vsite_atom_range, iat[i + 1]);
                         while (i < vs_ind_end)
@@ -2347,7 +2486,7 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
             }
         }
         vsite_atom_range++;
-        natperthread = (vsite_atom_range + vsite->nthreads - 1) / vsite->nthreads;
+        natperthread = (vsite_atom_range + numThreads_ - 1) / numThreads_;
     }
     else
     {
@@ -2358,53 +2497,52 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
          * When assigning vsites to threads, we should take care that the last
          * threads also covers the non-local range.
          */
-        vsite_atom_range = mdatoms->nr;
-        natperthread     = (mdatoms->homenr + vsite->nthreads - 1) / vsite->nthreads;
+        vsite_atom_range = mdatoms.nr;
+        natperthread     = (mdatoms.homenr + numThreads_ - 1) / numThreads_;
     }
 
     if (debug)
     {
         fprintf(debug, "virtual site thread dist: natoms %d, range %d, natperthread %d\n",
-                mdatoms->nr, vsite_atom_range, natperthread);
+                mdatoms.nr, vsite_atom_range, natperthread);
     }
 
     /* To simplify the vsite assignment, we make an index which tells us
      * to which task particles, both non-vsites and vsites, are assigned.
      */
-    vsite->taskIndex.resize(mdatoms->nr);
+    taskIndex_.resize(mdatoms.nr);
 
     /* Initialize the task index array. Here we assign the non-vsite
      * particles to task=thread, so we easily figure out if vsites
      * depend on local and/or non-local particles in assignVsitesToThread.
      */
-    gmx::ArrayRef<int> taskIndex = vsite->taskIndex;
     {
         int thread = 0;
-        for (int i = 0; i < mdatoms->nr; i++)
+        for (int i = 0; i < mdatoms.nr; i++)
         {
-            if (mdatoms->ptype[i] == eptVSite)
+            if (mdatoms.ptype[i] == eptVSite)
             {
                 /* vsites are not assigned to a task yet */
-                taskIndex[i] = -1;
+                taskIndex_[i] = -1;
             }
             else
             {
                 /* assign non-vsite particles to task thread */
-                taskIndex[i] = thread;
+                taskIndex_[i] = thread;
             }
-            if (i == (thread + 1) * natperthread && thread < vsite->nthreads)
+            if (i == (thread + 1) * natperthread && thread < numThreads_)
             {
                 thread++;
             }
         }
     }
 
-#pragma omp parallel num_threads(vsite->nthreads)
+#pragma omp parallel num_threads(numThreads_)
     {
         try
         {
             int          thread = gmx_omp_get_thread_num();
-            VsiteThread& tData  = *vsite->tData[thread];
+            VsiteThread& tData  = *tData_[thread];
 
             /* Clear the buffer use flags that were set before */
             if (tData.useInterdependentTask)
@@ -2418,7 +2556,7 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
                 clearTaskForceBufferUsedElements(&idTask);
 
                 idTask.vsite.resize(0);
-                for (int t = 0; t < vsite->nthreads; t++)
+                for (int t = 0; t < numThreads_; t++)
                 {
                     AtomIndex& atomIndex = idTask.atomIndex[t];
                     int        natom     = atomIndex.atom.size();
@@ -2454,17 +2592,17 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
 
             /* Assign all vsites that can execute independently on threads */
             tData.rangeStart = thread * natperthread;
-            if (thread < vsite->nthreads - 1)
+            if (thread < numThreads_ - 1)
             {
                 tData.rangeEnd = (thread + 1) * natperthread;
             }
             else
             {
                 /* The last thread should cover up to the end of the range */
-                tData.rangeEnd = mdatoms->nr;
+                tData.rangeEnd = mdatoms.nr;
             }
-            assignVsitesToThread(&tData, thread, vsite->nthreads, natperthread, taskIndex, ilist,
-                                 ip, mdatoms->ptype);
+            assignVsitesToThread(&tData, thread, numThreads_, natperthread, taskIndex_, ilists,
+                                 iparams, mdatoms.ptype);
 
             if (tData.useInterdependentTask)
             {
@@ -2482,7 +2620,7 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
 
                 idTask.spreadTask.resize(0);
                 idTask.reduceTask.resize(0);
-                for (int t = 0; t < vsite->nthreads; t++)
+                for (int t = 0; t < numThreads_; t++)
                 {
                     /* Do we write to the force buffer of task t? */
                     if (!idTask.atomIndex[t].atom.empty())
@@ -2490,7 +2628,7 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
                         idTask.spreadTask.push_back(t);
                     }
                     /* Does task t write to our force buffer? */
-                    if (!vsite->tData[t]->idTask.atomIndex[thread].atom.empty())
+                    if (!tData_[t]->idTask.atomIndex[thread].atom.empty())
                     {
                         idTask.reduceTask.push_back(t);
                     }
@@ -2502,28 +2640,27 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
     /* Assign all remaining vsites, that will have taskIndex[]=2*vsite->nthreads,
      * to a single task that will not run in parallel with other tasks.
      */
-    assignVsitesToSingleTask(vsite->tData[vsite->nthreads].get(), 2 * vsite->nthreads, taskIndex,
-                             ilist, ip);
+    assignVsitesToSingleTask(tData_[numThreads_].get(), 2 * numThreads_, taskIndex_, ilists, iparams);
 
-    if (debug && vsite->nthreads > 1)
+    if (debug && numThreads_ > 1)
     {
         fprintf(debug, "virtual site useInterdependentTask %d, nuse:\n",
-                static_cast<int>(vsite->tData[0]->useInterdependentTask));
-        for (int th = 0; th < vsite->nthreads + 1; th++)
+                static_cast<int>(tData_[0]->useInterdependentTask));
+        for (int th = 0; th < numThreads_ + 1; th++)
         {
-            fprintf(debug, " %4d", vsite->tData[th]->idTask.nuse);
+            fprintf(debug, " %4d", tData_[th]->idTask.nuse);
         }
         fprintf(debug, "\n");
 
         for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
         {
-            if (ilist[ftype].nr > 0)
+            if (!ilists[ftype].empty())
             {
                 fprintf(debug, "%-20s thread dist:", interaction_function[ftype].longname);
-                for (int th = 0; th < vsite->nthreads + 1; th++)
+                for (int th = 0; th < numThreads_ + 1; th++)
                 {
-                    fprintf(debug, " %4d %4d ", vsite->tData[th]->ilist[ftype].nr,
-                            vsite->tData[th]->idTask.ilist[ftype].nr);
+                    fprintf(debug, " %4d %4d ", tData_[th]->ilist[ftype].size(),
+                            tData_[th]->idTask.ilist[ftype].size());
                 }
                 fprintf(debug, "\n");
             }
@@ -2531,12 +2668,11 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
     }
 
 #ifndef NDEBUG
-    int nrOrig     = vsiteIlistNrCount(ilist);
+    int nrOrig     = vsiteIlistNrCount(ilists);
     int nrThreaded = 0;
-    for (int th = 0; th < vsite->nthreads + 1; th++)
+    for (int th = 0; th < numThreads_ + 1; th++)
     {
-        nrThreaded += vsiteIlistNrCount(vsite->tData[th]->ilist)
-                      + vsiteIlistNrCount(vsite->tData[th]->idTask.ilist);
+        nrThreaded += vsiteIlistNrCount(tData_[th]->ilist) + vsiteIlistNrCount(tData_[th]->idTask.ilist);
     }
     GMX_ASSERT(nrThreaded == nrOrig,
                "The number of virtual sites assigned to all thread task has to match the total "
@@ -2544,10 +2680,17 @@ void split_vsites_over_threads(const t_ilist* ilist, const t_iparams* ip, const
 #endif
 }
 
-void set_vsite_top(gmx_vsite_t* vsite, const gmx_localtop_t* top, const t_mdatoms* md)
+void VirtualSitesHandler::Impl::setVirtualSites(ArrayRef<const InteractionList> ilists,
+                                                const t_mdatoms&                mdatoms)
 {
-    if (vsite->nthreads > 1)
-    {
-        split_vsites_over_threads(top->idef.il, top->idef.iparams, md, vsite);
-    }
+    ilists_ = ilists;
+
+    threadingInfo_.setVirtualSites(ilists, iparams_, mdatoms, domainInfo_.useDomdec());
 }
+
+void VirtualSitesHandler::setVirtualSites(ArrayRef<const InteractionList> ilists, const t_mdatoms& mdatoms)
+{
+    impl_->setVirtualSites(ilists, mdatoms);
+}
+
+} // namespace gmx
index a3ea432bf1349d2fa34f22b9fd1d4585d3201f33..ae7c932b5e5caf390dccc20acd6cc96d107eeccf 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+/*! \libinternal \file
+ * \brief Declares the VirtualSitesHandler class and vsite standalone functions
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+
 #ifndef GMX_MDLIB_VSITE_H
 #define GMX_MDLIB_VSITE_H
 
 #include <memory>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/pbcutil/ishift.h"
 #include "gromacs/topology/idef.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/real.h"
 
-struct gmx_localtop_t;
+struct gmx_domdec_t;
 struct gmx_mtop_t;
 struct t_commrec;
-struct t_graph;
-struct t_ilist;
+struct InteractionList;
 struct t_mdatoms;
 struct t_nrnb;
 struct gmx_wallcycle;
-struct VsiteThread;
+enum class PbcType : int;
 
 namespace gmx
 {
 class RangePartitioning;
-}
 
-/* The start and end values of for the vsite indices in the ftype enum.
- * The validity of these values is checked in init_vsite.
+/*! \brief The start value of the vsite indices in the ftype enum
+ *
+ * The validity of the start and end values is checked in makeVirtualSitesHandler().
  * This is used to avoid loops over all ftypes just to get the vsite entries.
  * (We should replace the fixed ilist array by only the used entries.)
  */
-static constexpr int c_ftypeVsiteStart = F_VSITE2;
-static constexpr int c_ftypeVsiteEnd   = F_VSITEN + 1;
+static constexpr int c_ftypeVsiteStart = F_VSITE1;
+//! The start and end value of the vsite indices in the ftype enum
+static constexpr int c_ftypeVsiteEnd = F_VSITEN + 1;
 
-/* Type for storing PBC atom information for all vsite types in the system */
+//! Type for storing PBC atom information for all vsite types in the system
 typedef std::array<std::vector<int>, c_ftypeVsiteEnd - c_ftypeVsiteStart> VsitePbc;
 
-/* Data for handling vsites, needed with OpenMP threading or with charge-groups and PBC */
-struct gmx_vsite_t
+/*! \libinternal
+ * \brief Class that handles construction of vsites and spreading of vsite forces
+ */
+class VirtualSitesHandler
 {
-    gmx_vsite_t();
-
-    ~gmx_vsite_t();
-
-    /* The number of vsites that cross update groups, when =0 no PBC treatment is needed */
-    int numInterUpdategroupVsites;
-    int nthreads;                                    /* Number of threads used for vsites       */
-    std::vector<std::unique_ptr<VsiteThread>> tData; /* Thread local vsites and work structs    */
-    std::vector<int> taskIndex;                      /* Work array                              */
-    bool useDomdec; /* Tells whether we use domain decomposition with more than 1 DD rank */
+public:
+    //! Constructor, used only be the makeVirtualSitesHandler() factory function
+    VirtualSitesHandler(const gmx_mtop_t& mtop, gmx_domdec_t* domdec, PbcType pbcType);
+
+    ~VirtualSitesHandler();
+
+    //! Returns the number of virtual sites acting over multiple update groups
+    int numInterUpdategroupVirtualSites() const;
+
+    //! Set VSites and distribute VSite work over threads, should be called after each DD partitioning
+    void setVirtualSites(ArrayRef<const InteractionList> ilist, const t_mdatoms& mdatoms);
+
+    /*! \brief Create positions of vsite atoms based for the local system
+     *
+     * \param[in,out] x        The coordinates
+     * \param[in]     dt       The time step
+     * \param[in,out] v        When not empty, velocities for vsites are set as displacement/dt
+     * \param[in]     box      The box
+     */
+    void construct(ArrayRef<RVec> x, real dt, ArrayRef<RVec> v, const matrix box) const;
+
+    //! Tells how to handle virial contributions due to virtual sites
+    enum class VirialHandling : int
+    {
+        None,     //!< Do not compute virial contributions
+        Pbc,      //!< Add contributions working over PBC to shift forces
+        NonLinear //!< Compute contributions due to non-linear virtual sites
+    };
+
+    /*! \brief Spread the force operating on the vsite atoms on the surrounding atoms.
+     *
+     * vsite should point to a valid object.
+     * The virialHandling parameter determines how virial contributions are handled.
+     * If this is set to Linear, shift forces are accumulated into fshift.
+     * If this is set to NonLinear, non-linear contributions are added to virial.
+     * This non-linear correction is required when the virial is not calculated
+     * afterwards from the particle position and forces, but in a different way,
+     * as for instance for the PME mesh contribution.
+     */
+    void spreadForces(ArrayRef<const RVec> x,
+                      ArrayRef<RVec>       f,
+                      VirialHandling       virialHandling,
+                      ArrayRef<RVec>       fshift,
+                      matrix               virial,
+                      t_nrnb*              nrnb,
+                      const matrix         box,
+                      gmx_wallcycle*       wcycle);
+
+private:
+    //! Implementation type.
+    class Impl;
+    //! Implementation object.
+    PrivateImplPointer<Impl> impl_;
 };
 
 /*! \brief Create positions of vsite atoms based for the local system
  *
- * \param[in]     vsite    The vsite struct, when nullptr is passed, no MPI and no multi-threading
- * is used \param[in,out] x        The coordinates \param[in]     dt       The time step \param[in,out]
- * v        When != nullptr, velocities for vsites are set as displacement/dt \param[in]     ip
- * Interaction parameters \param[in]     ilist    The interaction list \param[in]     ePBC     The
- * type of periodic boundary conditions \param[in]     bMolPBC  When true, molecules are broken over
- * PBC \param[in]     cr       The communication record \param[in]     box      The box
+ * \param[in,out] x        The coordinates
+ * \param[in]     ip       Interaction parameters
+ * \param[in]     ilist    The interaction list
  */
-void construct_vsites(const gmx_vsite_t* vsite,
-                      rvec               x[],
-                      real               dt,
-                      rvec               v[],
-                      const t_iparams    ip[],
-                      const t_ilist      ilist[],
-                      int                ePBC,
-                      gmx_bool           bMolPBC,
-                      const t_commrec*   cr,
-                      const matrix       box);
+void constructVirtualSites(ArrayRef<RVec>                  x,
+                           ArrayRef<const t_iparams>       ip,
+                           ArrayRef<const InteractionList> ilist);
 
 /*! \brief Create positions of vsite atoms for the whole system assuming all molecules are wholex
  *
  * \param[in]     mtop  The global topology
  * \param[in,out] x     The global coordinates
  */
-void constructVsitesGlobal(const gmx_mtop_t& mtop, gmx::ArrayRef<gmx::RVec> x);
-
-void spread_vsite_f(const gmx_vsite_t* vsite,
-                    const rvec         x[],
-                    rvec               f[],
-                    rvec*              fshift,
-                    gmx_bool           VirCorr,
-                    matrix             vir,
-                    t_nrnb*            nrnb,
-                    const t_idef*      idef,
-                    int                ePBC,
-                    gmx_bool           bMolPBC,
-                    const t_graph*     g,
-                    const matrix       box,
-                    const t_commrec*   cr,
-                    gmx_wallcycle*     wcycle);
-/* Spread the force operating on the vsite atoms on the surrounding atoms.
- * If fshift!=NULL also update the shift forces.
- * If VirCorr=TRUE add the virial correction for non-linear vsite constructs
- * to vir. This correction is required when the virial is not calculated
- * afterwards from the particle position and forces, but in a different way,
- * as for instance for the PME mesh contribution.
- */
+void constructVirtualSitesGlobal(const gmx_mtop_t& mtop, ArrayRef<RVec> x);
+
+//! Tells how to handle virial contributions due to virtual sites
+enum class VirtualSiteVirialHandling : int
+{
+    None,     //!< Do not compute virial contributions
+    Pbc,      //!< Add contributions working over PBC to shift forces
+    NonLinear //!< Compute contributions due to non-linear virtual sites
+};
 
-/* Return the number of non-linear virtual site constructions in the system */
+//! Return the number of non-linear virtual site constructions in the system
 int countNonlinearVsites(const gmx_mtop_t& mtop);
 
-/* Return the number of virtual sites that cross update groups
+/*! \brief Return the number of virtual sites that cross update groups
  *
  * \param[in] mtop                           The global topology
  * \param[in] updateGroupingPerMoleculetype  Update grouping per molecule type, pass empty when not using update groups
  */
-int countInterUpdategroupVsites(const gmx_mtop_t&                           mtop,
-                                gmx::ArrayRef<const gmx::RangePartitioning> updateGroupingPerMoleculetype);
+int countInterUpdategroupVsites(const gmx_mtop_t&                 mtop,
+                                ArrayRef<const RangePartitioning> updateGroupingPerMoleculetype);
 
-/* Initialize the virtual site struct,
+/*! \brief Create the virtual site handler
  *
- * \param[in] mtop  The global topology
- * \param[in] cr    The communication record
- * \returns A valid vsite struct or nullptr when there are no virtual sites
- */
-std::unique_ptr<gmx_vsite_t> initVsite(const gmx_mtop_t& mtop, const t_commrec* cr);
-
-void split_vsites_over_threads(const t_ilist*   ilist,
-                               const t_iparams* ip,
-                               const t_mdatoms* mdatoms,
-                               gmx_vsite_t*     vsite);
-/* Divide the vsite work-load over the threads.
- * Should be called at the end of the domain decomposition.
+ * \param[in] mtop      The global topology
+ * \param[in] cr        The communication record
+ * \param[in] pbcType   The type of PBC
+ * \returns A valid vsite handler object or nullptr when there are no virtual sites
  */
+std::unique_ptr<VirtualSitesHandler> makeVirtualSitesHandler(const gmx_mtop_t& mtop,
+                                                             const t_commrec*  cr,
+                                                             PbcType           pbcType);
 
-void set_vsite_top(gmx_vsite_t* vsite, const gmx_localtop_t* top, const t_mdatoms* md);
-/* Set some vsite data for runs without domain decomposition.
- * Should be called once after init_vsite, before calling other routines.
- */
+} // namespace gmx
 
 #endif
index d03f55344c6d58cee867920516f3740ae0e81267..b8d5d77fd9fc53aea4bf55b2e933efffc3809eac 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/mdtypes/forceoutput.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/nblist.h"
 #include "gromacs/tables/forcetable.h"
 #include "gromacs/topology/topology.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
@@ -104,7 +107,7 @@ void make_wall_tables(FILE*                   fplog,
     }
 }
 
-[[noreturn]] static void wall_error(int a, const rvec* x, real r)
+[[noreturn]] static void wall_error(int a, gmx::ArrayRef<const gmx::RVec> x, real r)
 {
     gmx_fatal(FARGS,
               "An atom is beyond the wall: coordinates %f %f %f, distance %f\n"
@@ -115,7 +118,7 @@ void make_wall_tables(FILE*                   fplog,
 static void tableForce(real r, const t_forcetable& tab, real Cd, real Cr, real* V, real* F)
 {
     const real  tabscale = tab.scale;
-    const real* VFtab    = tab.data;
+    const real* VFtab    = tab.data.data();
 
     real rt = r * tabscale;
     int  n0 = static_cast<int>(rt);
@@ -156,15 +159,15 @@ static void tableForce(real r, const t_forcetable& tab, real Cd, real Cr, real*
     }
 }
 
-real do_walls(const t_inputrec&     ir,
-              const t_forcerec&     fr,
-              const matrix          box,
-              const t_mdatoms&      md,
-              const rvec*           x,
-              gmx::ForceWithVirial* forceWithVirial,
-              real                  lambda,
-              real                  Vlj[],
-              t_nrnb*               nrnb)
+real do_walls(const t_inputrec&              ir,
+              const t_forcerec&              fr,
+              const matrix                   box,
+              const t_mdatoms&               md,
+              gmx::ArrayRef<const gmx::RVec> x,
+              gmx::ForceWithVirial*          forceWithVirial,
+              real                           lambda,
+              real                           Vlj[],
+              t_nrnb*                        nrnb)
 {
     constexpr real sixth   = 1.0 / 6.0;
     constexpr real twelfth = 1.0 / 12.0;
@@ -176,7 +179,7 @@ real do_walls(const t_inputrec&     ir,
     const int   nwall     = ir.nwall;
     const int   ngid      = ir.opts.ngener;
     const int   ntype     = fr.ntype;
-    const real* nbfp      = fr.nbfp;
+    const real* nbfp      = fr.nbfp.data();
     const int*  egp_flags = fr.egp_flags;
 
     for (int w = 0; w < nwall; w++)
index 95437da7707463c95479d0abfe84ba5e9415e884..8a6316583420e921c1c30874888e9f68928171cc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 struct SimulationGroups;
 struct t_forcerec;
-struct t_idef;
 struct t_inputrec;
 struct t_mdatoms;
 struct t_nrnb;
 
 namespace gmx
 {
+template<typename>
+class ArrayRef;
 class ForceWithVirial;
-}
+} // namespace gmx
 
 void make_wall_tables(FILE*                   fplog,
                       const t_inputrec*       ir,
@@ -57,14 +58,14 @@ void make_wall_tables(FILE*                   fplog,
                       const SimulationGroups* groups,
                       t_forcerec*             fr);
 
-real do_walls(const t_inputrec&     ir,
-              const t_forcerec&     fr,
-              const matrix          box,
-              const t_mdatoms&      md,
-              const rvec            x[],
-              gmx::ForceWithVirial* forceWithVirial,
-              real                  lambda,
-              real                  Vlj[],
-              t_nrnb*               nrnb);
+real do_walls(const t_inputrec&              ir,
+              const t_forcerec&              fr,
+              const matrix                   box,
+              const t_mdatoms&               md,
+              gmx::ArrayRef<const gmx::RVec> x,
+              gmx::ForceWithVirial*          forceWithVirial,
+              real                           lambda,
+              real                           Vlj[],
+              t_nrnb*                        nrnb);
 
 #endif
diff --git a/src/gromacs/mdlib/wholemoleculetransform.cpp b/src/gromacs/mdlib/wholemoleculetransform.cpp
new file mode 100644 (file)
index 0000000..14b4580
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#include "gmxpre.h"
+
+#include "wholemoleculetransform.h"
+
+#include "gromacs/topology/mtop_util.h"
+
+namespace gmx
+{
+
+WholeMoleculeTransform::WholeMoleculeTransform(const gmx_mtop_t& mtop, const PbcType pbcType) :
+    pbcType_(pbcType)
+{
+    gmx_localtop_t localTop(mtop.ffparams);
+
+    gmx_mtop_generate_local_top(mtop, &localTop, false);
+
+    graph_ = mk_graph(localTop.idef, mtop.natoms);
+
+    wholeMoleculeCoordinates_.resize(mtop.natoms);
+}
+
+void WholeMoleculeTransform::updateForAtomPbcJumps(ArrayRef<const RVec> x, const matrix box)
+{
+    mk_mshift(nullptr, &graph_, pbcType_, box, as_rvec_array(x.data()));
+}
+
+ArrayRef<const RVec> WholeMoleculeTransform::wholeMoleculeCoordinates(ArrayRef<const RVec> x, const matrix box)
+{
+    shift_x(&graph_, box, as_rvec_array(x.data()), as_rvec_array(wholeMoleculeCoordinates_.data()));
+
+    return wholeMoleculeCoordinates_;
+}
+
+} // namespace gmx
diff --git a/src/gromacs/mdlib/wholemoleculetransform.h b/src/gromacs/mdlib/wholemoleculetransform.h
new file mode 100644 (file)
index 0000000..b79644c
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares the WholeMolecules class for generating whole molecules
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+#ifndef GMX_MDLIB_WHOLEMOLECULETRANSFORM_H
+#define GMX_MDLIB_WHOLEMOLECULETRANSFORM_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/pbcutil/mshift.h"
+#include "gromacs/utility/arrayref.h"
+
+struct gmx_mtop_t;
+enum class PbcType : int;
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief This class manages a coordinate buffer with molecules not split
+ * over periodic boundary conditions for use in force calculations
+ * which require whole molecules.
+ *
+ * Note: This class should not be used for computation of forces which
+ *       have virial contributions through shift forces.
+ */
+class WholeMoleculeTransform
+{
+public:
+    /*! \brief Constructor */
+    WholeMoleculeTransform(const gmx_mtop_t& mtop, PbcType pbcType);
+
+    /*! \brief Updates the graph when atoms have been shifted by periodic vectors */
+    void updateForAtomPbcJumps(ArrayRef<const RVec> x, const matrix box);
+
+    /*! \brief Create and return coordinates with whole molecules for input coordinates \p x
+     *
+     * \param[in] x  Input coordinates, should not have periodic displacement compared
+     *               with the coordinates passed in the last call to \p updateForAtomPbcJumps().
+     * \param[in] box  The current periodic image vectors
+     *
+     * Note: this operation is not free. If you need whole molecules coordinates
+     * more than once during the force calculation, store the result and reuse it.
+     */
+    ArrayRef<const RVec> wholeMoleculeCoordinates(ArrayRef<const RVec> x, const matrix box);
+
+private:
+    //! The type of PBC
+    PbcType pbcType_;
+    //! The graph
+    t_graph graph_;
+    //! Buffer for storing coordinates for whole molecules
+    std::vector<RVec> wholeMoleculeCoordinates_;
+};
+
+} // namespace gmx
+
+#endif
index 9a02264c0b017dd21ed86a950ba1f410407c5328..cb714aff3de0a1a9ba8be7ea25c49bb626f4ce61 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -37,6 +37,7 @@ gmx_add_libgromacs_sources(
     legacysimulator.cpp
     md.cpp
     mdmodules.cpp
+    membedholder.cpp
     mimic.cpp
     minimize.cpp
     replicaexchange.cpp
@@ -44,9 +45,23 @@ gmx_add_libgromacs_sources(
     runner.cpp
     shellfc.cpp
     simulationcontext.cpp
+    simulationinput.cpp
+    simulationinputhandle.cpp
+    simulatorbuilder.cpp
     tpi.cpp
     )
 
+# TODO: Find a home for this header and a scheme for installation.
+# This header straddles the installed libraries and is a transitive interface
+# from libgromacs to libgmxapi to libgmxapi clients. Near term efforts are
+# planned to consolidate these libraries and refine the public interface.
+# In the mean time, this is kept out of the gmxapi to avoid circular dependencies
+# between the libraries. It can be moved to a separate interface-only target,
+# but that is beyond the scope of issue #3379, which introduces the header.
+# Ref #3152 and #3652
+install(FILES simulationinputhandle.h
+        DESTINATION include/gromacs/mdrun)
+
 if (BUILD_TESTING)
 # TODO import this from src/programs/mdrun/tests
 #    add_subdirectory(tests)
index e1ee7ccbcb94dc27c640203d003ff2a70e30bdff..86ea3279a8ae7ebc7e7aed3aae894f93034e5fd4 100644 (file)
@@ -51,14 +51,12 @@ struct gmx_mtop_t;
 struct gmx_membed_t;
 struct gmx_multisim_t;
 struct gmx_output_env_t;
-struct gmx_vsite_t;
 struct gmx_wallcycle;
 struct gmx_walltime_accounting;
 struct ObservablesHistory;
 struct pull_t;
 struct ReplicaExchangeParameters;
 struct t_commrec;
-struct t_fcdata;
 struct t_forcerec;
 struct t_filenm;
 struct t_inputrec;
@@ -79,12 +77,13 @@ class MDLogger;
 class MDAtoms;
 class StopHandlerBuilder;
 struct MdrunOptions;
+class VirtualSitesHandler;
 
 /*! \internal
  * \brief The Simulator interface
  *
  * This is the general interface for any type of simulation type
- * ran with GROMACS. This allows having a builder return different
+ * run with GROMACS. This allows having a builder return different
  * Simulator objects based on user input.
  */
 class ISimulator
@@ -98,42 +97,52 @@ public:
     virtual void run() = 0;
     //! Standard destructor
     virtual ~ISimulator() = default;
+};
+
+/*! \internal
+ * \brief The legacy simulator data
+ *
+ * This contains the data passed into the GROMACS simulators from
+ * the Mdrunner object.
+ */
+class LegacySimulatorData
+{
+public:
     //! The constructor
-    ISimulator(FILE*                               fplog,
-               t_commrec*                          cr,
-               const gmx_multisim_t*               ms,
-               const MDLogger&                     mdlog,
-               int                                 nfile,
-               const t_filenm*                     fnm,
-               const gmx_output_env_t*             oenv,
-               const MdrunOptions&                 mdrunOptions,
-               StartingBehavior                    startingBehavior,
-               gmx_vsite_t*                        vsite,
-               Constraints*                        constr,
-               gmx_enfrot*                         enforcedRotation,
-               BoxDeformation*                     deform,
-               IMDOutputProvider*                  outputProvider,
-               const MdModulesNotifier&            mdModulesNotifier,
-               t_inputrec*                         inputrec,
-               ImdSession*                         imdSession,
-               pull_t*                             pull_work,
-               t_swap*                             swap,
-               gmx_mtop_t*                         top_global,
-               t_fcdata*                           fcd,
-               t_state*                            state_global,
-               ObservablesHistory*                 observablesHistory,
-               MDAtoms*                            mdAtoms,
-               t_nrnb*                             nrnb,
-               gmx_wallcycle*                      wcycle,
-               t_forcerec*                         fr,
-               gmx_enerdata_t*                     enerd,
-               gmx_ekindata_t*                     ekind,
-               MdrunScheduleWorkload*              runScheduleWork,
-               const ReplicaExchangeParameters&    replExParams,
-               gmx_membed_t*                       membed,
-               gmx_walltime_accounting*            walltime_accounting,
-               std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder,
-               bool                                doRerun) :
+    LegacySimulatorData(FILE*                               fplog,
+                        t_commrec*                          cr,
+                        const gmx_multisim_t*               ms,
+                        const MDLogger&                     mdlog,
+                        int                                 nfile,
+                        const t_filenm*                     fnm,
+                        const gmx_output_env_t*             oenv,
+                        const MdrunOptions&                 mdrunOptions,
+                        StartingBehavior                    startingBehavior,
+                        VirtualSitesHandler*                vsite,
+                        Constraints*                        constr,
+                        gmx_enfrot*                         enforcedRotation,
+                        BoxDeformation*                     deform,
+                        IMDOutputProvider*                  outputProvider,
+                        const MdModulesNotifier&            mdModulesNotifier,
+                        t_inputrec*                         inputrec,
+                        ImdSession*                         imdSession,
+                        pull_t*                             pull_work,
+                        t_swap*                             swap,
+                        gmx_mtop_t*                         top_global,
+                        t_state*                            state_global,
+                        ObservablesHistory*                 observablesHistory,
+                        MDAtoms*                            mdAtoms,
+                        t_nrnb*                             nrnb,
+                        gmx_wallcycle*                      wcycle,
+                        t_forcerec*                         fr,
+                        gmx_enerdata_t*                     enerd,
+                        gmx_ekindata_t*                     ekind,
+                        MdrunScheduleWorkload*              runScheduleWork,
+                        const ReplicaExchangeParameters&    replExParams,
+                        gmx_membed_t*                       membed,
+                        gmx_walltime_accounting*            walltime_accounting,
+                        std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder,
+                        bool                                doRerun) :
         fplog(fplog),
         cr(cr),
         ms(ms),
@@ -154,7 +163,6 @@ public:
         pull_work(pull_work),
         swap(swap),
         top_global(top_global),
-        fcd(fcd),
         state_global(state_global),
         observablesHistory(observablesHistory),
         mdAtoms(mdAtoms),
@@ -172,7 +180,6 @@ public:
     {
     }
 
-protected:
     //! Handles logging.
     FILE* fplog;
     //! Handles communication.
@@ -192,7 +199,7 @@ protected:
     //! Whether the simulation will start afresh, or restart with/without appending.
     const StartingBehavior startingBehavior;
     //! Handles virtual sites.
-    gmx_vsite_t* vsite;
+    VirtualSitesHandler* vsite;
     //! Handles constraints.
     Constraints* constr;
     //! Handles enforced rotation.
@@ -212,9 +219,7 @@ protected:
     //! The coordinate-swapping session.
     t_swap* swap;
     //! Full system topology.
-    gmx_mtop_t* top_global;
-    //! Helper struct for force calculations.
-    t_fcdata* fcd;
+    const gmx_mtop_t* top_global;
     //! Full simulation state (only non-nullptr on master rank).
     t_state* state_global;
     //! History of simulation observables.
index 2b8e3a076021c32ac93f9f7869199fe2e6a2e75e..42d0a7df38148980c1886cf03fa015fa5066e51c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011-2019, by the GROMACS development team, led by
+ * Copyright (c) 2011-2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,6 +52,7 @@
 #include <cstring>
 
 #include "gromacs/math/functions.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/arraysize.h"
 #include "gromacs/utility/fatalerror.h"
 
index 7cf2a00e45d9a43e95c9cfea8ebd5cfc59533bd0..e6a654c98b881cbc4d6b85f33b44cea66c0efdc0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -71,7 +71,7 @@ using SimulatorFunctionType = void();
  * code. Once many of them have become modules, we should change this
  * approach.
  */
-class LegacySimulator : public ISimulator
+class LegacySimulator : public ISimulator, private LegacySimulatorData
 {
 private:
     //! Implements the normal MD simulations.
@@ -91,7 +91,7 @@ private:
     //! Implements MiMiC QM/MM workflow
     SimulatorFunctionType do_mimic;
     // Use the constructor of the base class
-    using ISimulator::ISimulator;
+    using LegacySimulatorData::LegacySimulatorData;
 
 public:
     // Only builder can construct
index 5ca5064229efa6182a30e4e2782847d41c16de22..96d0666e03ed29fbfaad97d38f6235e40691f753 100644 (file)
 
 #include <algorithm>
 #include <memory>
+#include <numeric>
 
-#include "gromacs/awh/awh.h"
+#include "gromacs/applied_forces/awh/awh.h"
 #include "gromacs/commandline/filenm.h"
 #include "gromacs/domdec/collect.h"
 #include "gromacs/domdec/dlbtiming.h"
 #include "gromacs/domdec/domdec.h"
 #include "gromacs/domdec/domdec_network.h"
 #include "gromacs/domdec/domdec_struct.h"
+#include "gromacs/domdec/gpuhaloexchange.h"
 #include "gromacs/domdec/mdsetup.h"
 #include "gromacs/domdec/partition.h"
 #include "gromacs/essentialdynamics/edsam.h"
-#include "gromacs/ewald/pme.h"
 #include "gromacs/ewald/pme_load_balancing.h"
+#include "gromacs/ewald/pme_pp.h"
 #include "gromacs/fileio/trxio.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
+#include "gromacs/gpu_utils/device_stream_manager.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/imd/imd.h"
-#include "gromacs/listed_forces/manage_threading.h"
+#include "gromacs/listed_forces/listed_forces.h"
 #include "gromacs/math/functions.h"
-#include "gromacs/math/utilities.h"
+#include "gromacs/math/invertmatrix.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdlib/checkpointhandler.h"
 #include "gromacs/mdlib/compute_io.h"
 #include "gromacs/mdlib/constr.h"
+#include "gromacs/mdlib/coupling.h"
 #include "gromacs/mdlib/ebin.h"
 #include "gromacs/mdlib/enerdata_utils.h"
 #include "gromacs/mdlib/energyoutput.h"
@@ -83,6 +87,7 @@
 #include "gromacs/mdlib/force.h"
 #include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdlib/forcerec.h"
+#include "gromacs/mdlib/freeenergyparameters.h"
 #include "gromacs/mdlib/md_support.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/mdoutf.h"
 #include "gromacs/mdlib/tgroup.h"
 #include "gromacs/mdlib/trajectory_writing.h"
 #include "gromacs/mdlib/update.h"
-#include "gromacs/mdlib/update_constrain_cuda.h"
+#include "gromacs/mdlib/update_constrain_gpu.h"
 #include "gromacs/mdlib/vcm.h"
 #include "gromacs/mdlib/vsite.h"
 #include "gromacs/mdrunutility/handlerestart.h"
 #include "gromacs/mdtypes/df_history.h"
 #include "gromacs/mdtypes/energyhistory.h"
 #include "gromacs/mdtypes/fcdata.h"
+#include "gromacs/mdtypes/forcebuffers.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/mdrunoptions.h"
+#include "gromacs/mdtypes/multipletimestepping.h"
 #include "gromacs/mdtypes/observableshistory.h"
 #include "gromacs/mdtypes/pullhistory.h"
 #include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/mdtypes/state_propagator_data_gpu.h"
-#include "gromacs/modularsimulator/energyelement.h"
+#include "gromacs/modularsimulator/energydata.h"
 #include "gromacs/nbnxm/gpu_data_mgmt.h"
 #include "gromacs/nbnxm/nbnxm.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pulling/output.h"
 #include "gromacs/pulling/pull.h"
@@ -157,30 +163,26 @@ void gmx::LegacySimulator::do_md()
     // will go away eventually.
     t_inputrec*  ir = inputrec;
     int64_t      step, step_rel;
-    double       t, t0 = ir->init_t, lam0[efptNR];
+    double       t, t0 = ir->init_t;
     gmx_bool     bGStatEveryStep, bGStat, bCalcVir, bCalcEnerStep, bCalcEner;
-    gmx_bool     bNS, bNStList, bStopCM, bFirstStep, bInitStep, bLastStep = FALSE;
+    gmx_bool     bNS = FALSE, bNStList, bStopCM, bFirstStep, bInitStep, bLastStep = FALSE;
     gmx_bool     bDoDHDL = FALSE, bDoFEP = FALSE, bDoExpanded = FALSE;
     gmx_bool     do_ene, do_log, do_verbose;
     gmx_bool     bMasterState;
     unsigned int force_flags;
-    tensor force_vir = { { 0 } }, shake_vir = { { 0 } }, total_vir = { { 0 } }, tmp_vir = { { 0 } },
-           pres = { { 0 } };
-    int                         i, m;
-    rvec                        mu_tot;
-    matrix                      pressureCouplingMu, M;
-    gmx_repl_ex_t               repl_ex = nullptr;
-    gmx_localtop_t              top;
-    PaddedHostVector<gmx::RVec> f{};
-    gmx_global_stat_t           gstat;
-    t_graph*                    graph = nullptr;
-    gmx_shellfc_t*              shellfc;
-    gmx_bool                    bSumEkinhOld, bDoReplEx, bExchanged, bNeedRepartition;
-    gmx_bool                    bTemp, bPres, bTrotter;
-    real                        dvdl_constr;
-    std::vector<RVec>           cbuf;
-    matrix                      lastbox;
-    int                         lamnew = 0;
+    tensor force_vir = { { 0 } }, shake_vir = { { 0 } }, total_vir = { { 0 } }, pres = { { 0 } };
+    int    i, m;
+    rvec   mu_tot;
+    matrix pressureCouplingMu, M;
+    gmx_repl_ex_t     repl_ex = nullptr;
+    gmx_global_stat_t gstat;
+    gmx_shellfc_t*    shellfc;
+    gmx_bool          bSumEkinhOld, bDoReplEx, bExchanged, bNeedRepartition;
+    gmx_bool          bTemp, bPres, bTrotter;
+    real              dvdl_constr;
+    std::vector<RVec> cbuf;
+    matrix            lastbox;
+    int               lamnew = 0;
     /* for FEP */
     int       nstfep = 0;
     double    cycles;
@@ -233,7 +235,7 @@ void gmx::LegacySimulator::do_md()
     int nstglobalcomm = computeGlobalCommunicationPeriod(mdlog, ir, cr);
     bGStatEveryStep   = (nstglobalcomm == 1);
 
-    SimulationGroups* groups = &top_global->groups;
+    const SimulationGroups* groups = &top_global->groups;
 
     std::unique_ptr<EssentialDynamics> ed = nullptr;
     if (opt2bSet("-ei", nfile, fnm))
@@ -250,18 +252,22 @@ void gmx::LegacySimulator::do_md()
                   "Either specify the -ei option to mdrun, or do not use this checkpoint file.");
     }
 
-    initialize_lambdas(fplog, *ir, MASTER(cr), &state_global->fep_state, state_global->lambda, lam0);
-    Update     upd(ir, deform);
+    int*                fep_state = MASTER(cr) ? &state_global->fep_state : nullptr;
+    gmx::ArrayRef<real> lambda    = MASTER(cr) ? state_global->lambda : gmx::ArrayRef<real>();
+    initialize_lambdas(fplog, *ir, MASTER(cr), fep_state, lambda);
+    Update     upd(*ir, deform);
     const bool doSimulatedAnnealing = initSimulatedAnnealing(ir, &upd);
     const bool useReplicaExchange   = (replExParams.exchangeInterval > 0);
 
+    const t_fcdata& fcdata = *fr->fcdata;
+
     bool simulationsShareState = false;
     int  nstSignalComm         = nstglobalcomm;
     {
         // TODO This implementation of ensemble orientation restraints is nasty because
         // a user can't just do multi-sim with single-sim orientation restraints.
         bool usingEnsembleRestraints =
-                (fcd->disres.nsystems > 1) || ((ms != nullptr) && (fcd->orires.nr != 0));
+                (fcdata.disres->nsystems > 1) || ((ms != nullptr) && (fcdata.orires->nr != 0));
         bool awhUsesMultiSim = (ir->bDoAwh && ir->awhParams->shareBiasMultisim && (ms != nullptr));
 
         // Replica exchange, ensemble restraints and AWH need all
@@ -311,14 +317,21 @@ void gmx::LegacySimulator::do_md()
     t_state*                 state;
 
 
+    gmx_localtop_t top(top_global->ffparams);
+
     auto mdatoms = mdAtoms->mdatoms();
 
-    std::unique_ptr<UpdateConstrainCuda> integrator;
+    const auto& simulationWork     = runScheduleWork->simulationWork;
+    const bool  useGpuForPme       = simulationWork.useGpuPme;
+    const bool  useGpuForNonbonded = simulationWork.useGpuNonbonded;
+    const bool  useGpuForBufferOps = simulationWork.useGpuBufferOps;
+    const bool  useGpuForUpdate    = simulationWork.useGpuUpdate;
 
+    ForceBuffers f(fr->useMts, ((useGpuForNonbonded && useGpuForBufferOps) || useGpuForUpdate)
+                                       ? PinningPolicy::PinnedIfSupported
+                                       : PinningPolicy::CannotBePinned);
     if (DOMAINDECOMP(cr))
     {
-        dd_init_local_top(*top_global, &top);
-
         stateInstance = std::make_unique<t_state>();
         state         = stateInstance.get();
         dd_init_local_state(cr->dd, state_global, state);
@@ -333,24 +346,20 @@ void gmx::LegacySimulator::do_md()
     else
     {
         state_change_natoms(state_global, state_global->natoms);
-        f.resizeWithPadding(state_global->natoms);
         /* Copy the pointer to the global state */
         state = state_global;
 
         /* Generate and initialize new topology */
-        mdAlgorithmsSetupAtomData(cr, ir, *top_global, &top, fr, &graph, mdAtoms, constr, vsite, shellfc);
+        mdAlgorithmsSetupAtomData(cr, ir, *top_global, &top, fr, &f, mdAtoms, constr, vsite, shellfc);
 
         upd.setNumAtoms(state->natoms);
     }
 
-    const auto& simulationWork     = runScheduleWork->simulationWork;
-    const bool  useGpuForPme       = simulationWork.useGpuPme;
-    const bool  useGpuForNonbonded = simulationWork.useGpuNonbonded;
-    const bool  useGpuForBufferOps = simulationWork.useGpuBufferOps;
-    const bool  useGpuForUpdate    = simulationWork.useGpuUpdate;
+    std::unique_ptr<UpdateConstrainGpu> integrator;
 
     StatePropagatorDataGpu* stateGpu = fr->stateGpu;
 
+    // TODO: the assertions below should be handled by UpdateConstraintsBuilder.
     if (useGpuForUpdate)
     {
         GMX_RELEASE_ASSERT(!DOMAINDECOMP(cr) || ddUsesUpdateGroups(*cr->dd) || constr == nullptr
@@ -368,20 +377,24 @@ void gmx::LegacySimulator::do_md()
         GMX_RELEASE_ASSERT(
                 ir->etc != etcNOSEHOOVER,
                 "Nose-Hoover temperature coupling is not supported with the GPU update.\n");
-        GMX_RELEASE_ASSERT(ir->epc == epcNO || ir->epc == epcPARRINELLORAHMAN || ir->epc == epcBERENDSEN,
-                           "Only Parrinello-Rahman and Berendsen pressure coupling are supported "
-                           "with the GPU update.\n");
+        GMX_RELEASE_ASSERT(
+                ir->epc == epcNO || ir->epc == epcPARRINELLORAHMAN || ir->epc == epcBERENDSEN
+                        || ir->epc == epcCRESCALE,
+                "Only Parrinello-Rahman, Berendsen, and C-rescale pressure coupling are supported "
+                "with the GPU update.\n");
         GMX_RELEASE_ASSERT(!mdatoms->haveVsites,
                            "Virtual sites are not supported with the GPU update.\n");
         GMX_RELEASE_ASSERT(ed == nullptr,
                            "Essential dynamics is not supported with the GPU update.\n");
         GMX_RELEASE_ASSERT(!ir->bPull || !pull_have_constraint(ir->pull),
                            "Constraints pulling is not supported with the GPU update.\n");
-        GMX_RELEASE_ASSERT(fcd->orires.nr == 0,
+        GMX_RELEASE_ASSERT(fcdata.orires->nr == 0,
                            "Orientation restraints are not supported with the GPU update.\n");
-        GMX_RELEASE_ASSERT(ir->efep == efepNO,
-                           "Free energy perturbations are not supported with the GPU update.");
-        GMX_RELEASE_ASSERT(graph == nullptr, "The graph is not supported with GPU update.");
+        GMX_RELEASE_ASSERT(
+                ir->efep == efepNO
+                        || (!haveFreeEnergyType(*ir, efptBONDED) && !haveFreeEnergyType(*ir, efptMASS)),
+                "Free energy perturbation of masses and constraints are not supported with the GPU "
+                "update.");
 
         if (constr != nullptr && constr->numConstraintsTotal() > 0)
         {
@@ -393,22 +406,25 @@ void gmx::LegacySimulator::do_md()
         {
             GMX_LOG(mdlog.info).asParagraph().appendText("Updating coordinates on the GPU.");
         }
-        integrator = std::make_unique<UpdateConstrainCuda>(
-                *ir, *top_global, stateGpu->getUpdateStream(), stateGpu->xUpdatedOnDevice());
-
-        t_pbc pbc;
-        set_pbc(&pbc, epbcXYZ, state->box);
-        integrator->setPbc(&pbc);
+        GMX_RELEASE_ASSERT(fr->deviceStreamManager != nullptr,
+                           "Device stream manager should be initialized in order to use GPU "
+                           "update-constraints.");
+        GMX_RELEASE_ASSERT(
+                fr->deviceStreamManager->streamIsValid(gmx::DeviceStreamType::UpdateAndConstraints),
+                "Update stream should be initialized in order to use GPU "
+                "update-constraints.");
+        integrator = std::make_unique<UpdateConstrainGpu>(
+                *ir, *top_global, fr->deviceStreamManager->context(),
+                fr->deviceStreamManager->stream(gmx::DeviceStreamType::UpdateAndConstraints),
+                stateGpu->xUpdatedOnDevice());
+
+        integrator->setPbc(PbcType::Xyz, state->box);
     }
 
     if (useGpuForPme || (useGpuForNonbonded && useGpuForBufferOps) || useGpuForUpdate)
     {
         changePinningPolicy(&state->x, PinningPolicy::PinnedIfSupported);
     }
-    if ((useGpuForNonbonded && useGpuForBufferOps) || useGpuForUpdate)
-    {
-        changePinningPolicy(&f, PinningPolicy::PinnedIfSupported);
-    }
     if (useGpuForUpdate)
     {
         changePinningPolicy(&state->v, PinningPolicy::PinnedIfSupported);
@@ -434,10 +450,10 @@ void gmx::LegacySimulator::do_md()
 
     if (MASTER(cr))
     {
-        EnergyElement::initializeEnergyHistory(startingBehavior, observablesHistory, &energyOutput);
+        EnergyData::initializeEnergyHistory(startingBehavior, observablesHistory, &energyOutput);
     }
 
-    preparePrevStepPullCom(ir, pull_work, mdatoms, state, state_global, cr,
+    preparePrevStepPullCom(ir, pull_work, mdatoms->massT, state, state_global, cr,
                            startingBehavior != StartingBehavior::NewSimulation);
 
     // TODO: Remove this by converting AWH into a ForceProvider
@@ -489,14 +505,14 @@ void gmx::LegacySimulator::do_md()
         if (constr)
         {
             /* Constrain the initial coordinates and velocities */
-            do_constrain_first(fplog, constr, ir, mdatoms, state->natoms, state->x.arrayRefWithPadding(),
-                               state->v.arrayRefWithPadding(), state->box, state->lambda[efptBONDED]);
+            do_constrain_first(fplog, constr, ir, mdatoms->nr, mdatoms->homenr,
+                               state->x.arrayRefWithPadding(), state->v.arrayRefWithPadding(),
+                               state->box, state->lambda[efptBONDED]);
         }
         if (vsite)
         {
             /* Construct the virtual sites for the initial configuration */
-            construct_vsites(vsite, state->x.rvec_array(), ir->delta_t, nullptr, top.idef.iparams,
-                             top.idef.il, fr->ePBC, fr->bMolPBC, cr, state->box);
+            vsite->construct(state->x, ir->delta_t, {}, state->box);
         }
     }
 
@@ -507,11 +523,15 @@ void gmx::LegacySimulator::do_md()
         nstfep = ir->fepvals->nstdhdl;
         if (ir->bExpanded)
         {
-            nstfep = gmx_greatest_common_divisor(ir->expandedvals->nstexpanded, nstfep);
+            nstfep = std::gcd(ir->expandedvals->nstexpanded, nstfep);
         }
         if (useReplicaExchange)
         {
-            nstfep = gmx_greatest_common_divisor(replExParams.exchangeInterval, nstfep);
+            nstfep = std::gcd(replExParams.exchangeInterval, nstfep);
+        }
+        if (ir->bDoAwh)
+        {
+            nstfep = std::gcd(ir->awhParams->nstSampleCoord, nstfep);
         }
     }
 
@@ -534,7 +554,7 @@ void gmx::LegacySimulator::do_md()
     bool hasReadEkinState = MASTER(cr) ? state_global->ekinstate.hasReadEkinState : false;
     if (PAR(cr))
     {
-        gmx_bcast(sizeof(hasReadEkinState), &hasReadEkinState, cr);
+        gmx_bcast(sizeof(hasReadEkinState), &hasReadEkinState, cr->mpi_comm_mygroup);
     }
     if (hasReadEkinState)
     {
@@ -563,9 +583,9 @@ void gmx::LegacySimulator::do_md()
             cglo_flags_iteration |= CGLO_STOPCM;
             cglo_flags_iteration &= ~CGLO_TEMPERATURE;
         }
-        compute_globals(gstat, cr, ir, fr, ekind, state->x.rvec_array(), state->v.rvec_array(),
-                        state->box, state->lambda[efptVDW], mdatoms, nrnb, &vcm, nullptr, enerd,
-                        force_vir, shake_vir, total_vir, pres, mu_tot, constr, &nullSignaller,
+        compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                        makeConstArrayRef(state->v), state->box, mdatoms, nrnb, &vcm, nullptr,
+                        enerd, force_vir, shake_vir, total_vir, pres, constr, &nullSignaller,
                         state->box, &totalNumberOfBondedInteractions, &bSumEkinhOld,
                         cglo_flags_iteration
                                 | (shouldCheckNumberOfBondedInteractions ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS
@@ -575,17 +595,14 @@ void gmx::LegacySimulator::do_md()
             /* At initialization, do not pass x with acceleration-correction mode
              * to avoid (incorrect) correction of the initial coordinates.
              */
-            rvec* xPtr = nullptr;
-            if (vcm.mode != ecmLINEAR_ACCELERATION_CORRECTION)
-            {
-                xPtr = state->x.rvec_array();
-            }
-            process_and_stopcm_grp(fplog, &vcm, *mdatoms, xPtr, state->v.rvec_array());
+            auto x = (vcm.mode == ecmLINEAR_ACCELERATION_CORRECTION) ? ArrayRef<RVec>()
+                                                                     : makeArrayRef(state->x);
+            process_and_stopcm_grp(fplog, &vcm, *mdatoms, x, makeArrayRef(state->v));
             inc_nrnb(nrnb, eNR_STOPCM, mdatoms->homenr);
         }
     }
     checkNumberOfBondedInteractions(mdlog, cr, totalNumberOfBondedInteractions, top_global, &top,
-                                    state->x.rvec_array(), state->box,
+                                    makeConstArrayRef(state->x), state->box,
                                     &shouldCheckNumberOfBondedInteractions);
     if (ir->eI == eiVVAK)
     {
@@ -595,9 +612,9 @@ void gmx::LegacySimulator::do_md()
            kinetic energy calculation.  This minimized excess variables, but
            perhaps loses some logic?*/
 
-        compute_globals(gstat, cr, ir, fr, ekind, state->x.rvec_array(), state->v.rvec_array(),
-                        state->box, state->lambda[efptVDW], mdatoms, nrnb, &vcm, nullptr, enerd,
-                        force_vir, shake_vir, total_vir, pres, mu_tot, constr, &nullSignaller,
+        compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                        makeConstArrayRef(state->v), state->box, mdatoms, nrnb, &vcm, nullptr,
+                        enerd, force_vir, shake_vir, total_vir, pres, constr, &nullSignaller,
                         state->box, nullptr, &bSumEkinhOld, cglo_flags & ~CGLO_PRESSURE);
     }
 
@@ -677,6 +694,9 @@ void gmx::LegacySimulator::do_md()
     bExchanged       = FALSE;
     bNeedRepartition = FALSE;
 
+    step     = ir->init_step;
+    step_rel = 0;
+
     auto stopHandler = stopHandlerBuilder->getStopHandlerMD(
             compat::not_null<SimulationSignal*>(&signals[eglsSTOPCOND]), simulationsShareState,
             MASTER(cr), ir->nstlist, mdrunOptions.reproducible, nstSignalComm,
@@ -695,41 +715,9 @@ void gmx::LegacySimulator::do_md()
 
     const DDBalanceRegionHandler ddBalanceRegionHandler(cr);
 
-    step     = ir->init_step;
-    step_rel = 0;
-
-    // TODO extract this to new multi-simulation module
     if (MASTER(cr) && isMultiSim(ms) && !useReplicaExchange)
     {
-        if (!multisim_int_all_are_equal(ms, ir->nsteps))
-        {
-            GMX_LOG(mdlog.warning)
-                    .appendText(
-                            "Note: The number of steps is not consistent across multi "
-                            "simulations,\n"
-                            "but we are proceeding anyway!");
-        }
-        if (!multisim_int_all_are_equal(ms, ir->init_step))
-        {
-            if (simulationsShareState)
-            {
-                if (MASTER(cr))
-                {
-                    gmx_fatal(FARGS,
-                              "The initial step is not consistent across multi simulations which "
-                              "share the state");
-                }
-                gmx_barrier(cr);
-            }
-            else
-            {
-                GMX_LOG(mdlog.warning)
-                        .appendText(
-                                "Note: The initial step is not consistent across multi "
-                                "simulations,\n"
-                                "but we are proceeding anyway!");
-            }
-        }
+        logInitialMultisimStatus(ms, cr, mdlog, simulationsShareState, ir->nsteps, ir->init_step);
     }
 
     /* and stop now if we should */
@@ -764,7 +752,7 @@ void gmx::LegacySimulator::do_md()
         if (ir->efep != efepNO || ir->bSimTemp)
         {
             /* find and set the current lambdas */
-            setCurrentLambdasLocal(step, ir->fepvals, lam0, state->lambda, state->fep_state);
+            state->lambda = currentLambdas(step, *(ir->fepvals), state->fep_state);
 
             bDoDHDL     = do_per_step(step, ir->fepvals->nstdhdl);
             bDoFEP      = ((ir->efep != efepNO) && do_per_step(step, nstfep));
@@ -822,15 +810,13 @@ void gmx::LegacySimulator::do_md()
             /* Correct the new box if it is too skewed */
             if (inputrecDynamicBox(ir))
             {
-                if (correct_box(fplog, step, state->box, graph))
+                if (correct_box(fplog, step, state->box))
                 {
                     bMasterState = TRUE;
                     // If update is offloaded, it should be informed about the box size change
                     if (useGpuForUpdate)
                     {
-                        t_pbc pbc;
-                        set_pbc(&pbc, epbcXYZ, state->box);
-                        integrator->setPbc(&pbc);
+                        integrator->setPbc(PbcType::Xyz, state->box);
                     }
                 }
             }
@@ -850,9 +836,19 @@ void gmx::LegacySimulator::do_md()
             }
         }
 
+        // Allocate or re-size GPU halo exchange object, if necessary
+        if (bNS && havePPDomainDecomposition(cr) && simulationWork.useGpuHaloExchange)
+        {
+            GMX_RELEASE_ASSERT(fr->deviceStreamManager != nullptr,
+                               "GPU device manager has to be initialized to use GPU "
+                               "version of halo exchange.");
+            constructGpuHaloExchange(mdlog, *cr, *fr->deviceStreamManager, wcycle);
+        }
+
         if (MASTER(cr) && do_log)
         {
-            energyOutput.printHeader(fplog, step, t); /* can we improve the information printed here? */
+            gmx::EnergyOutput::printHeader(fplog, step,
+                                           t); /* can we improve the information printed here? */
         }
 
         if (ir->efep != efepNO)
@@ -866,13 +862,13 @@ void gmx::LegacySimulator::do_md()
             /* 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(gstat, cr, ir, fr, ekind, state->x.rvec_array(), state->v.rvec_array(),
-                            state->box, state->lambda[efptVDW], mdatoms, nrnb, &vcm, wcycle, enerd,
-                            nullptr, nullptr, nullptr, nullptr, mu_tot, constr, &nullSignaller,
+            compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                            makeConstArrayRef(state->v), state->box, mdatoms, nrnb, &vcm, wcycle,
+                            enerd, nullptr, nullptr, nullptr, nullptr, constr, &nullSignaller,
                             state->box, &totalNumberOfBondedInteractions, &bSumEkinhOld,
                             CGLO_GSTAT | CGLO_TEMPERATURE | CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS);
             checkNumberOfBondedInteractions(mdlog, cr, totalNumberOfBondedInteractions, top_global,
-                                            &top, state->x.rvec_array(), state->box,
+                                            &top, makeConstArrayRef(state->x), state->box,
                                             &shouldCheckNumberOfBondedInteractions);
         }
         clear_mat(force_vir);
@@ -911,16 +907,20 @@ void gmx::LegacySimulator::do_md()
         force_flags = (GMX_FORCE_STATECHANGED | ((inputrecDynamicBox(ir)) ? GMX_FORCE_DYNAMICBOX : 0)
                        | GMX_FORCE_ALLFORCES | (bCalcVir ? GMX_FORCE_VIRIAL : 0)
                        | (bCalcEner ? GMX_FORCE_ENERGY : 0) | (bDoFEP ? GMX_FORCE_DHDL : 0));
+        if (fr->useMts && !do_per_step(step, ir->nstfout))
+        {
+            force_flags |= GMX_FORCE_DO_NOT_NEED_NORMAL_FORCE;
+        }
 
         if (shellfc)
         {
             /* Now is the time to relax the shells */
             relax_shell_flexcon(fplog, cr, ms, mdrunOptions.verbose, enforcedRotation, step, ir,
-                                imdSession, pull_work, bNS, force_flags, &top, constr, enerd, fcd,
+                                imdSession, pull_work, bNS, force_flags, &top, constr, enerd,
                                 state->natoms, state->x.arrayRefWithPadding(),
-                                state->v.arrayRefWithPadding(), state->box, state->lambda, &state->hist,
-                                f.arrayRefWithPadding(), force_vir, mdatoms, nrnb, wcycle, graph,
-                                shellfc, fr, runScheduleWork, t, mu_tot, vsite, ddBalanceRegionHandler);
+                                state->v.arrayRefWithPadding(), state->box, state->lambda,
+                                &state->hist, &f.view(), force_vir, mdatoms, nrnb, wcycle, shellfc,
+                                fr, runScheduleWork, t, mu_tot, vsite, ddBalanceRegionHandler);
         }
         else
         {
@@ -944,8 +944,8 @@ void gmx::LegacySimulator::do_md()
              */
             do_force(fplog, cr, ms, ir, awh.get(), enforcedRotation, imdSession, pull_work, step,
                      nrnb, wcycle, &top, state->box, state->x.arrayRefWithPadding(), &state->hist,
-                     f.arrayRefWithPadding(), force_vir, mdatoms, enerd, fcd, state->lambda, graph,
-                     fr, runScheduleWork, vsite, mu_tot, t, ed ? ed->getLegacyED() : nullptr,
+                     &f.view(), force_vir, mdatoms, enerd, state->lambda, fr, runScheduleWork,
+                     vsite, mu_tot, t, ed ? ed->getLegacyED() : nullptr,
                      (bNS ? GMX_FORCE_NS : 0) | force_flags, ddBalanceRegionHandler);
         }
 
@@ -978,11 +978,11 @@ void gmx::LegacySimulator::do_md()
                                trotter_seq, ettTSEQ1);
             }
 
-            update_coords(step, ir, mdatoms, state, f.arrayRefWithPadding(), fcd, ekind, M, &upd,
-                          etrtVELOCITY1, cr, constr);
+            upd.update_coords(*ir, step, mdatoms, state, f.view().forceWithPadding(), fcdata, ekind,
+                              M, etrtVELOCITY1, cr, constr != nullptr);
 
             wallcycle_stop(wcycle, ewcUPDATE);
-            constrain_velocities(step, nullptr, state, shake_vir, constr, bCalcVir, do_log, do_ene);
+            constrain_velocities(constr, do_log, do_ene, step, state, nullptr, bCalcVir, shake_vir);
             wallcycle_start(wcycle, ewcUPDATE);
             /* if VV, compute the pressure and constraints */
             /* For VV2, we strictly only need this if using pressure
@@ -1004,9 +1004,9 @@ void gmx::LegacySimulator::do_md()
             if (bGStat || do_per_step(step - 1, nstglobalcomm))
             {
                 wallcycle_stop(wcycle, ewcUPDATE);
-                compute_globals(gstat, cr, ir, fr, ekind, state->x.rvec_array(), state->v.rvec_array(),
-                                state->box, state->lambda[efptVDW], mdatoms, nrnb, &vcm, wcycle, enerd,
-                                force_vir, shake_vir, total_vir, pres, mu_tot, constr, &nullSignaller,
+                compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                                makeConstArrayRef(state->v), state->box, mdatoms, nrnb, &vcm, wcycle,
+                                enerd, force_vir, shake_vir, total_vir, pres, constr, &nullSignaller,
                                 state->box, &totalNumberOfBondedInteractions, &bSumEkinhOld,
                                 (bGStat ? CGLO_GSTAT : 0) | (bCalcEner ? CGLO_ENERGY : 0)
                                         | (bTemp ? CGLO_TEMPERATURE : 0) | (bPres ? CGLO_PRESSURE : 0)
@@ -1022,12 +1022,12 @@ void gmx::LegacySimulator::do_md()
                    b) If we are using EkinAveEkin for the kinetic energy for the temperature control, we still feed in
                    EkinAveVel because it's needed for the pressure */
                 checkNumberOfBondedInteractions(mdlog, cr, totalNumberOfBondedInteractions,
-                                                top_global, &top, state->x.rvec_array(), state->box,
-                                                &shouldCheckNumberOfBondedInteractions);
+                                                top_global, &top, makeConstArrayRef(state->x),
+                                                state->box, &shouldCheckNumberOfBondedInteractions);
                 if (bStopCM)
                 {
-                    process_and_stopcm_grp(fplog, &vcm, *mdatoms, state->x.rvec_array(),
-                                           state->v.rvec_array());
+                    process_and_stopcm_grp(fplog, &vcm, *mdatoms, makeArrayRef(state->x),
+                                           makeArrayRef(state->v));
                     inc_nrnb(nrnb, eNR_STOPCM, mdatoms->homenr);
                 }
                 wallcycle_start(wcycle, ewcUPDATE);
@@ -1065,11 +1065,10 @@ void gmx::LegacySimulator::do_md()
                     /* 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(gstat, cr, ir, fr, ekind, state->x.rvec_array(),
-                                    state->v.rvec_array(), state->box, state->lambda[efptVDW],
-                                    mdatoms, nrnb, &vcm, wcycle, enerd, nullptr, nullptr, nullptr,
-                                    nullptr, mu_tot, constr, &nullSignaller, state->box, nullptr,
-                                    &bSumEkinhOld, CGLO_GSTAT | CGLO_TEMPERATURE);
+                    compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                                    makeConstArrayRef(state->v), state->box, mdatoms, nrnb, &vcm, wcycle,
+                                    enerd, nullptr, nullptr, nullptr, nullptr, constr, &nullSignaller,
+                                    state->box, nullptr, &bSumEkinhOld, CGLO_GSTAT | CGLO_TEMPERATURE);
                     wallcycle_start(wcycle, ewcUPDATE);
                 }
             }
@@ -1094,10 +1093,10 @@ void gmx::LegacySimulator::do_md()
             {
                 saved_conserved_quantity -= enerd->term[F_DISPCORR];
             }
-            /* sum up the foreign energy and dhdl terms for vv.  currently done every step so that dhdl is correct in the .edr */
+            /* sum up the foreign kinetic energy and dK/dl terms for vv.  currently done every step so that dhdl is correct in the .edr */
             if (ir->efep != efepNO)
             {
-                sum_dhdl(enerd, state->lambda, *ir->fepvals);
+                accumulateKineticLambdaComponents(enerd, state->lambda, *ir->fepvals);
             }
         }
 
@@ -1149,7 +1148,7 @@ void gmx::LegacySimulator::do_md()
         if (runScheduleWork->stepWork.useGpuFBufferOps && (simulationWork.useGpuUpdate && !vsite)
             && do_per_step(step, ir->nstfout))
         {
-            stateGpu->copyForcesFromGpu(ArrayRef<RVec>(f), AtomLocality::Local);
+            stateGpu->copyForcesFromGpu(f.view().force(), AtomLocality::Local);
             stateGpu->waitForcesReadyOnHost(AtomLocality::Local);
         }
         /* Now we have the energies and forces corresponding to the
@@ -1157,9 +1156,9 @@ void gmx::LegacySimulator::do_md()
          * the update.
          */
         do_md_trajectory_writing(fplog, cr, nfile, fnm, step, step_rel, t, ir, state, state_global,
-                                 observablesHistory, top_global, fr, outf, energyOutput, ekind, f,
-                                 checkpointHandler->isCheckpointingStep(), bRerunMD, bLastStep,
-                                 mdrunOptions.writeConfout, bSumEkinhOld);
+                                 observablesHistory, top_global, fr, outf, energyOutput, ekind,
+                                 f.view().force(), checkpointHandler->isCheckpointingStep(),
+                                 bRerunMD, bLastStep, mdrunOptions.writeConfout, bSumEkinhOld);
         /* Check if IMD step and do IMD communication, if bIMD is TRUE. */
         bInteractiveMDstep = imdSession->run(step, bNS, state->box, state->x.rvec_array(), t);
 
@@ -1195,7 +1194,7 @@ void gmx::LegacySimulator::do_md()
             /* if we have constraints, we have to remove the kinetic energy parallel to the bonds */
             if (constr && bIfRandomize)
             {
-                constrain_velocities(step, nullptr, state, tmp_vir, constr, bCalcVir, do_log, do_ene);
+                constrain_velocities(constr, do_log, do_ene, step, state, nullptr, false, nullptr);
             }
         }
         /* Box is changed in update() when we do pressure coupling,
@@ -1227,8 +1226,8 @@ void gmx::LegacySimulator::do_md()
         if (EI_VV(ir->eI))
         {
             /* velocity half-step update */
-            update_coords(step, ir, mdatoms, state, f.arrayRefWithPadding(), fcd, ekind, M, &upd,
-                          etrtVELOCITY2, cr, constr);
+            upd.update_coords(*ir, step, mdatoms, state, f.view().forceWithPadding(), fcdata, ekind,
+                              M, etrtVELOCITY2, cr, constr != nullptr);
         }
 
         /* Above, initialize just copies ekinh into ekin,
@@ -1270,7 +1269,7 @@ void gmx::LegacySimulator::do_md()
             // If the buffer ops were not offloaded this step, the forces are on the host and have to be copied
             if (!runScheduleWork->stepWork.useGpuFBufferOps)
             {
-                stateGpu->copyForcesToGpu(ArrayRef<RVec>(f), AtomLocality::Local);
+                stateGpu->copyForcesToGpu(f.view().force(), AtomLocality::Local);
             }
 
             const bool doTemperatureScaling =
@@ -1293,17 +1292,35 @@ void gmx::LegacySimulator::do_md()
         }
         else
         {
-            update_coords(step, ir, mdatoms, state, f.arrayRefWithPadding(), fcd, ekind, M, &upd,
-                          etrtPOSITION, cr, constr);
+            /* With multiple time stepping we need to do an additional normal
+             * update step to obtain the virial, as the actual MTS integration
+             * using an acceleration where the slow forces are multiplied by mtsFactor.
+             * Using that acceleration would result in a virial with the slow
+             * force contribution would be a factor mtsFactor too large.
+             */
+            if (fr->useMts && bCalcVir && constr != nullptr)
+            {
+                upd.update_for_constraint_virial(*ir, *mdatoms, *state, f.view().forceWithPadding(), *ekind);
+
+                constrain_coordinates(constr, do_log, do_ene, step, state,
+                                      upd.xp()->arrayRefWithPadding(), &dvdl_constr, bCalcVir, shake_vir);
+            }
+
+            ArrayRefWithPadding<const RVec> forceCombined =
+                    (fr->useMts && step % ir->mtsLevels[1].stepFactor == 0)
+                            ? f.view().forceMtsCombinedWithPadding()
+                            : f.view().forceWithPadding();
+            upd.update_coords(*ir, step, mdatoms, state, forceCombined, fcdata, ekind, M,
+                              etrtPOSITION, cr, constr != nullptr);
 
             wallcycle_stop(wcycle, ewcUPDATE);
 
-            constrain_coordinates(step, &dvdl_constr, state, shake_vir, &upd, constr, bCalcVir,
-                                  do_log, do_ene);
+            constrain_coordinates(constr, do_log, do_ene, step, state, upd.xp()->arrayRefWithPadding(),
+                                  &dvdl_constr, bCalcVir && !fr->useMts, shake_vir);
 
-            update_sd_second_half(step, &dvdl_constr, ir, mdatoms, state, cr, nrnb, wcycle, &upd,
-                                  constr, do_log, do_ene);
-            finish_update(ir, mdatoms, state, graph, nrnb, wcycle, &upd, constr);
+            upd.update_sd_second_half(*ir, step, &dvdl_constr, mdatoms, state, cr, nrnb, wcycle,
+                                      constr, do_log, do_ene);
+            upd.finish_update(*ir, mdatoms, state, wcycle, constr != nullptr);
         }
 
         if (ir->bPull && ir->pull->bSetPbcRefToPrevStepCOM)
@@ -1315,17 +1332,17 @@ void gmx::LegacySimulator::do_md()
         {
             /* erase F_EKIN and F_TEMP here? */
             /* just compute the kinetic energy at the half step to perform a trotter step */
-            compute_globals(gstat, cr, ir, fr, ekind, state->x.rvec_array(), state->v.rvec_array(),
-                            state->box, state->lambda[efptVDW], mdatoms, nrnb, &vcm, wcycle, enerd,
-                            force_vir, shake_vir, total_vir, pres, mu_tot, constr, &nullSignaller, lastbox,
+            compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                            makeConstArrayRef(state->v), state->box, mdatoms, nrnb, &vcm, wcycle, enerd,
+                            force_vir, shake_vir, total_vir, pres, constr, &nullSignaller, lastbox,
                             nullptr, &bSumEkinhOld, (bGStat ? CGLO_GSTAT : 0) | CGLO_TEMPERATURE);
             wallcycle_start(wcycle, ewcUPDATE);
             trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ4);
             /* now we know the scaling, we can compute the positions again */
             std::copy(cbuf.begin(), cbuf.end(), state->x.begin());
 
-            update_coords(step, ir, mdatoms, state, f.arrayRefWithPadding(), fcd, ekind, M, &upd,
-                          etrtPOSITION, cr, constr);
+            upd.update_coords(*ir, step, mdatoms, state, f.view().forceWithPadding(), fcdata, ekind,
+                              M, etrtPOSITION, cr, constr != nullptr);
             wallcycle_stop(wcycle, ewcUPDATE);
 
             /* do we need an extra constraint here? just need to copy out of as_rvec_array(state->v.data()) to upd->xp? */
@@ -1333,14 +1350,14 @@ void gmx::LegacySimulator::do_md()
              * 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*/
-            finish_update(ir, mdatoms, state, graph, nrnb, wcycle, &upd, nullptr);
+            upd.finish_update(*ir, mdatoms, state, wcycle, false);
         }
         if (EI_VV(ir->eI))
         {
             /* this factor or 2 correction is necessary
                because half of the constraint force is removed
                in the vv step, so we have to double it.  See
-               the Redmine issue #1255.  It is not yet clear
+               the Issue #1255.  It is not yet clear
                if the factor of 2 is exact, or just a very
                good approximation, and this will be
                investigated.  The next step is to see if this
@@ -1361,17 +1378,7 @@ void gmx::LegacySimulator::do_md()
         if (vsite != nullptr)
         {
             wallcycle_start(wcycle, ewcVSITECONSTR);
-            if (graph != nullptr)
-            {
-                shift_self(graph, state->box, state->x.rvec_array());
-            }
-            construct_vsites(vsite, state->x.rvec_array(), ir->delta_t, state->v.rvec_array(),
-                             top.idef.iparams, top.idef.il, fr->ePBC, fr->bMolPBC, cr, state->box);
-
-            if (graph != nullptr)
-            {
-                unshift_self(graph, state->box, state->x.rvec_array());
-            }
+            vsite->construct(state->x, ir->delta_t, state->v, state->box);
             wallcycle_stop(wcycle, ewcVSITECONSTR);
         }
 
@@ -1403,24 +1410,23 @@ void gmx::LegacySimulator::do_md()
                 bool                doIntraSimSignal = true;
                 SimulationSignaller signaller(&signals, cr, ms, doInterSimSignal, doIntraSimSignal);
 
-                compute_globals(
-                        gstat, cr, ir, fr, ekind, state->x.rvec_array(), state->v.rvec_array(),
-                        state->box, state->lambda[efptVDW], mdatoms, nrnb, &vcm, wcycle, enerd,
-                        force_vir, shake_vir, total_vir, pres, mu_tot, constr, &signaller, lastbox,
-                        &totalNumberOfBondedInteractions, &bSumEkinhOld,
-                        (bGStat ? CGLO_GSTAT : 0) | (!EI_VV(ir->eI) && bCalcEner ? CGLO_ENERGY : 0)
-                                | (!EI_VV(ir->eI) && bStopCM ? CGLO_STOPCM : 0)
-                                | (!EI_VV(ir->eI) ? CGLO_TEMPERATURE : 0)
-                                | (!EI_VV(ir->eI) ? CGLO_PRESSURE : 0) | CGLO_CONSTRAINT
-                                | (shouldCheckNumberOfBondedInteractions ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS
-                                                                         : 0));
+                compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                                makeConstArrayRef(state->v), state->box, mdatoms, nrnb, &vcm,
+                                wcycle, enerd, force_vir, shake_vir, total_vir, pres, constr,
+                                &signaller, lastbox, &totalNumberOfBondedInteractions, &bSumEkinhOld,
+                                (bGStat ? CGLO_GSTAT : 0) | (!EI_VV(ir->eI) && bCalcEner ? CGLO_ENERGY : 0)
+                                        | (!EI_VV(ir->eI) && bStopCM ? CGLO_STOPCM : 0)
+                                        | (!EI_VV(ir->eI) ? CGLO_TEMPERATURE : 0)
+                                        | (!EI_VV(ir->eI) ? CGLO_PRESSURE : 0) | CGLO_CONSTRAINT
+                                        | (shouldCheckNumberOfBondedInteractions ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS
+                                                                                 : 0));
                 checkNumberOfBondedInteractions(mdlog, cr, totalNumberOfBondedInteractions,
-                                                top_global, &top, state->x.rvec_array(), state->box,
-                                                &shouldCheckNumberOfBondedInteractions);
+                                                top_global, &top, makeConstArrayRef(state->x),
+                                                state->box, &shouldCheckNumberOfBondedInteractions);
                 if (!EI_VV(ir->eI) && bStopCM)
                 {
-                    process_and_stopcm_grp(fplog, &vcm, *mdatoms, state->x.rvec_array(),
-                                           state->v.rvec_array());
+                    process_and_stopcm_grp(fplog, &vcm, *mdatoms, makeArrayRef(state->x),
+                                           makeArrayRef(state->v));
                     inc_nrnb(nrnb, eNR_STOPCM, mdatoms->homenr);
 
                     // TODO: The special case of removing CM motion should be dealt more gracefully
@@ -1450,22 +1456,29 @@ void gmx::LegacySimulator::do_md()
 
         if (ir->efep != efepNO && !EI_VV(ir->eI))
         {
-            /* Sum up the foreign energy and dhdl terms for md and sd.
-               Currently done every step so that dhdl is correct in the .edr */
-            sum_dhdl(enerd, state->lambda, *ir->fepvals);
+            /* Sum up the foreign energy and dK/dl terms for md and sd.
+               Currently done every step so that dH/dl is correct in the .edr */
+            accumulateKineticLambdaComponents(enerd, state->lambda, *ir->fepvals);
         }
 
         update_pcouple_after_coordinates(fplog, step, ir, mdatoms, pres, force_vir, shake_vir,
-                                         pressureCouplingMu, state, nrnb, &upd, !useGpuForUpdate);
+                                         pressureCouplingMu, state, nrnb, upd.deform(), !useGpuForUpdate);
 
         const bool doBerendsenPressureCoupling =
                 (inputrec->epc == epcBERENDSEN && do_per_step(step, inputrec->nstpcouple));
-        if (useGpuForUpdate && (doBerendsenPressureCoupling || doParrinelloRahman))
+        const bool doCRescalePressureCoupling =
+                (inputrec->epc == epcCRESCALE && do_per_step(step, inputrec->nstpcouple));
+        if (useGpuForUpdate
+            && (doBerendsenPressureCoupling || doCRescalePressureCoupling || doParrinelloRahman))
         {
             integrator->scaleCoordinates(pressureCouplingMu);
-            t_pbc pbc;
-            set_pbc(&pbc, epbcXYZ, state->box);
-            integrator->setPbc(&pbc);
+            if (doCRescalePressureCoupling)
+            {
+                matrix pressureCouplingInvMu;
+                gmx::invertBoxMatrix(pressureCouplingMu, pressureCouplingInvMu);
+                integrator->scaleVelocities(pressureCouplingInvMu);
+            }
+            integrator->setPbc(PbcType::Xyz, state->box);
         }
 
         /* ################# END UPDATE STEP 2 ################# */
@@ -1517,9 +1530,12 @@ void gmx::LegacySimulator::do_md()
             }
             if (bCalcEner)
             {
-                energyOutput.addDataAtEnergyStep(bDoDHDL, bCalcEnerStep, t, mdatoms->tmass, enerd, state,
-                                                 ir->fepvals, ir->expandedvals, lastbox, shake_vir,
-                                                 force_vir, total_vir, pres, ekind, mu_tot, constr);
+                energyOutput.addDataAtEnergyStep(
+                        bDoDHDL, bCalcEnerStep, t, mdatoms->tmass, enerd, ir->fepvals,
+                        ir->expandedvals, lastbox,
+                        PTCouplingArrays{ state->boxv, state->nosehoover_xi, state->nosehoover_vxi,
+                                          state->nhpres_xi, state->nhpres_vxi },
+                        state->fep_state, shake_vir, force_vir, total_vir, pres, ekind, mu_tot, constr);
             }
             else
             {
@@ -1531,12 +1547,19 @@ void gmx::LegacySimulator::do_md()
 
             if (doSimulatedAnnealing)
             {
-                energyOutput.printAnnealingTemperatures(do_log ? fplog : nullptr, groups, &(ir->opts));
+                gmx::EnergyOutput::printAnnealingTemperatures(do_log ? fplog : nullptr, groups,
+                                                              &(ir->opts));
             }
             if (do_log || do_ene || do_dr || do_or)
             {
                 energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), do_ene, do_dr, do_or,
-                                                   do_log ? fplog : nullptr, step, t, fcd, awh.get());
+                                                   do_log ? fplog : nullptr, step, t,
+                                                   fr->fcdata.get(), awh.get());
+            }
+            if (do_log && ir->bDoAwh && awh->hasFepLambdaDimension())
+            {
+                const bool isInitialOutput = false;
+                printLambdaStateToLog(fplog, state->lambda, isInitialOutput);
             }
 
             if (ir->bPull)
@@ -1558,6 +1581,10 @@ void gmx::LegacySimulator::do_md()
             /* Gets written into the state at the beginning of next loop*/
             state->fep_state = lamnew;
         }
+        else if (ir->bDoAwh && awh->needForeignEnergyDifferences(step))
+        {
+            state->fep_state = awh->fepLambdaState();
+        }
         /* Print the remaining wall clock time for the run */
         if (isMasterSimMasterRank(ms, MASTER(cr)) && (do_verbose || gmx_got_usr_signal()) && !bPMETunePrinting)
         {
@@ -1665,7 +1692,7 @@ void gmx::LegacySimulator::do_md()
     {
         if (ir->nstcalcenergy > 0)
         {
-            energyOutput.printAnnealingTemperatures(fplog, groups, &(ir->opts));
+            gmx::EnergyOutput::printAnnealingTemperatures(fplog, groups, &(ir->opts));
             energyOutput.printAverages(fplog, groups);
         }
     }
index 19dfd2869a55c3227867f3e4e8d4f556b25a47d1..1014a0adde1933fa711ff80d20f16746abd7d021 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,8 +38,8 @@
 
 #include <memory>
 
-#include "gromacs/applied_forces/densityfitting.h"
 #include "gromacs/applied_forces/electricfield.h"
+#include "gromacs/applied_forces/densityfitting/densityfitting.h"
 #include "gromacs/imd/imd.h"
 #include "gromacs/mdtypes/iforceprovider.h"
 #include "gromacs/mdtypes/imdmodule.h"
@@ -63,7 +63,7 @@ class MDModules::Impl : public IMDOutputProvider
 {
 public:
     Impl() :
-        densityFitting_(DensityFittingModuleInfo::create(&notifier_)),
+        densityFitting_(DensityFittingModuleInfo::create()),
         field_(createElectricFieldModule()),
         imd_(createInteractiveMolecularDynamicsModule()),
         swapCoordinates_(createSwapCoordinatesModule())
@@ -174,6 +174,16 @@ ForceProviders* MDModules::initForceProviders()
     return impl_->forceProviders_.get();
 }
 
+void MDModules::subscribeToPreProcessingNotifications()
+{
+    impl_->densityFitting_->subscribeToPreProcessingNotifications(&impl_->notifier_);
+}
+
+void MDModules::subscribeToSimulationSetupNotifications()
+{
+    impl_->densityFitting_->subscribeToSimulationSetupNotifications(&impl_->notifier_);
+}
+
 void MDModules::add(std::shared_ptr<gmx::IMDModule> module)
 {
     impl_->modules_.emplace_back(std::move(module));
index 3f956431e5b6048e07e6d1a0039d7053a46ab31e..6fabb761cd3854fb41bbfcd35548cb2b559ce9fa 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -144,6 +144,22 @@ public:
      */
     ForceProviders* initForceProviders();
 
+    /*! \brief Subscribe MdModules to simulation setup notifications.
+     *
+     * Allows MdModules to subscribe to notifications that are called back
+     * during the set up of an MD simulation, after the options were
+     * assigned to the modules.
+     */
+    void subscribeToSimulationSetupNotifications();
+
+    /*! \brief Subscribe MdModules to notifications during pre-processing.
+     *
+     * Allows MdModules to subscribe to notifications that are called back
+     * during pre processing an MD simulation, after the options were
+     * assigned to the modules.
+     */
+    void subscribeToPreProcessingNotifications();
+
     /*!
      * \brief Add a module to the container.
      *
diff --git a/src/gromacs/mdrun/membedholder.cpp b/src/gromacs/mdrun/membedholder.cpp
new file mode 100644 (file)
index 0000000..9429a95
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \brief Encapsulates membed methods
+ *
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \ingroup module_mdrun
+ */
+#include "gmxpre.h"
+
+#include "membedholder.h"
+
+#include "gromacs/commandline/filenm.h"
+#include "gromacs/mdlib/membed.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/state.h"
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+MembedHolder::MembedHolder(int nfile, const t_filenm fnm[]) :
+    doMembed_(opt2bSet("-membed", nfile, fnm))
+{
+}
+
+MembedHolder::~MembedHolder()
+{
+    if (doMembed_)
+    {
+        free_membed(membed_);
+    }
+}
+
+void MembedHolder::initializeMembed(FILE*          fplog,
+                                    int            nfile,
+                                    const t_filenm fnm[],
+                                    gmx_mtop_t*    mtop,
+                                    t_inputrec*    inputrec,
+                                    t_state*       state,
+                                    t_commrec*     cr,
+                                    real*          cpt)
+{
+    if (doMembed_)
+    {
+        if (MASTER(cr))
+        {
+            fprintf(stderr, "Initializing membed");
+        }
+        /* Note that membed cannot work in parallel because mtop is
+         * changed here. Fix this if we ever want to make it run with
+         * multiple ranks. */
+        membed_ = init_membed(fplog, nfile, fnm, mtop, inputrec, state, cr, cpt);
+    }
+}
+
+gmx_membed_t* MembedHolder::membed()
+{
+    return membed_;
+}
+
+MembedHolder::MembedHolder(MembedHolder&& holder) noexcept
+{
+    doMembed_        = holder.doMembed_;
+    membed_          = holder.membed_;
+    holder.membed_   = nullptr;
+    holder.doMembed_ = false;
+}
+
+MembedHolder& MembedHolder::operator=(MembedHolder&& holder) noexcept
+{
+    if (&holder != this)
+    {
+        doMembed_        = holder.doMembed_;
+        membed_          = holder.membed_;
+        holder.membed_   = nullptr;
+        holder.doMembed_ = false;
+    }
+    return *this;
+}
+
+} // namespace gmx
diff --git a/src/gromacs/mdrun/membedholder.h b/src/gromacs/mdrun/membedholder.h
new file mode 100644 (file)
index 0000000..a941394
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \brief Encapsulates membed methods
+ *
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \ingroup module_mdrun
+ */
+#ifndef GMX_MDRUN_MEMBEDHOLDER_H
+#define GMX_MDRUN_MEMBEDHOLDER_H
+
+#include <cstdio>
+
+#include "gromacs/utility/real.h"
+
+struct gmx_membed_t;
+struct gmx_mtop_t;
+struct t_commrec;
+struct t_filenm;
+struct t_inputrec;
+class t_state;
+
+namespace gmx
+{
+
+/*! \brief Membed SimulatorBuilder parameter type.
+ *
+ * Does not (yet) encapsulate ownership semantics of resources. Simulator is
+ * not (necessarily) granted ownership of resources. Client is responsible for
+ * maintaining the validity of resources for the life time of the Simulator,
+ * then for cleaning up those resources.
+ */
+class MembedHolder
+{
+public:
+    //! Build holder from input information.
+    explicit MembedHolder(int nfile, const t_filenm fnm[]);
+    //! Move is possible.
+    MembedHolder(MembedHolder&& holder) noexcept;
+    //! Move assignment is possible.
+    MembedHolder& operator=(MembedHolder&& holder) noexcept;
+    //! Copy is not allowed.
+    MembedHolder(const MembedHolder&) = delete;
+    //! Copy assignment is not allowed.
+    MembedHolder& operator=(const MembedHolder&) = delete;
+
+    ~MembedHolder();
+
+    //! Get information about membed being used.
+    [[nodiscard]] bool doMembed() const { return doMembed_; }
+
+    /*! \brief
+     * Fully initialize underlying datastructure.
+     *
+     * \param[in] fplog Handle to log file.
+     * \param[in] nfile How many input files are there.
+     * \param[in] fnm   Input file collection datastructure.
+     * \param[in,out] mtop  Handle to mtop, can be modified.
+     * \param[in,out] inputrec Handle to inputrec, can be modified.
+     * \param[in,out] state    Simulation state information, can be modified.
+     * \param[in,out] cr       Communication information.
+     * \param[out]    cpt      Some kind of checkpoint information.
+     */
+    void initializeMembed(FILE*          fplog,
+                          int            nfile,
+                          const t_filenm fnm[],
+                          gmx_mtop_t*    mtop,
+                          t_inputrec*    inputrec,
+                          t_state*       state,
+                          t_commrec*     cr,
+                          real*          cpt);
+
+    //! Get handle to membed object.
+    gmx_membed_t* membed();
+
+private:
+    //! Pointer to membed object. TODO replace with unique_ptr or get rid of this.
+    gmx_membed_t* membed_ = nullptr;
+    //! Whether membed is being used.
+    bool doMembed_ = false;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDRUN_MEMBEDHOLDER_H
index 0894cfb76168667f9ce36e676aed372a3b207cd2..d232621817b95097b149d488279b05d81ea9b398 100644 (file)
@@ -50,7 +50,7 @@
 #include <algorithm>
 #include <memory>
 
-#include "gromacs/awh/awh.h"
+#include "gromacs/applied_forces/awh/awh.h"
 #include "gromacs/commandline/filenm.h"
 #include "gromacs/domdec/collect.h"
 #include "gromacs/domdec/dlbtiming.h"
 #include "gromacs/domdec/mdsetup.h"
 #include "gromacs/domdec/partition.h"
 #include "gromacs/essentialdynamics/edsam.h"
-#include "gromacs/ewald/pme.h"
 #include "gromacs/ewald/pme_load_balancing.h"
+#include "gromacs/ewald/pme_pp.h"
 #include "gromacs/fileio/trxio.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
-#include "gromacs/listed_forces/manage_threading.h"
+#include "gromacs/listed_forces/listed_forces.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
@@ -81,6 +81,7 @@
 #include "gromacs/mdlib/force.h"
 #include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdlib/forcerec.h"
+#include "gromacs/mdlib/freeenergyparameters.h"
 #include "gromacs/mdlib/md_support.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/mdoutf.h"
 #include "gromacs/mdtypes/df_history.h"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/energyhistory.h"
-#include "gromacs/mdtypes/fcdata.h"
+#include "gromacs/mdtypes/forcebuffers.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/mimic/communicator.h"
 #include "gromacs/mimic/utilities.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pulling/pull.h"
 #include "gromacs/timing/wallcycle.h"
@@ -141,19 +141,17 @@ using gmx::SimulationSignaller;
 
 void gmx::LegacySimulator::do_mimic()
 {
-    t_inputrec*                 ir = inputrec;
-    int64_t                     step, step_rel;
-    double                      t, lam0[efptNR];
-    bool                        isLastStep               = false;
-    bool                        doFreeEnergyPerturbation = false;
-    unsigned int                force_flags;
-    tensor                      force_vir, shake_vir, total_vir, pres;
-    rvec                        mu_tot;
-    gmx_localtop_t              top;
-    PaddedHostVector<gmx::RVec> f{};
-    gmx_global_stat_t           gstat;
-    t_graph*                    graph = nullptr;
-    gmx_shellfc_t*              shellfc;
+    t_inputrec*       ir = inputrec;
+    int64_t           step, step_rel;
+    double            t;
+    bool              isLastStep               = false;
+    bool              doFreeEnergyPerturbation = false;
+    unsigned int      force_flags;
+    tensor            force_vir, shake_vir, total_vir, pres;
+    rvec              mu_tot;
+    ForceBuffers      f;
+    gmx_global_stat_t gstat;
+    gmx_shellfc_t*    shellfc;
 
     double cycles;
 
@@ -211,20 +209,22 @@ void gmx::LegacySimulator::do_mimic()
     int        nstglobalcomm = 1;
     const bool bNS           = true;
 
-    // Communicator to interact with MiMiC
-    MimicCommunicator mimicCommunicator{};
     if (MASTER(cr))
     {
-        mimicCommunicator.init();
-        mimicCommunicator.sendInitData(top_global, state_global->x);
-        ir->nsteps = mimicCommunicator.getStepNumber();
+        MimicCommunicator::init();
+        auto nonConstGlobalTopology = const_cast<gmx_mtop_t*>(top_global);
+        MimicCommunicator::sendInitData(nonConstGlobalTopology, state_global->x);
+        ir->nsteps = MimicCommunicator::getStepNumber();
     }
 
-    ir->nstxout_compressed                   = 0;
-    SimulationGroups* groups                 = &top_global->groups;
-    top_global->intermolecularExclusionGroup = genQmmmIndices(*top_global);
+    ir->nstxout_compressed         = 0;
+    const SimulationGroups* groups = &top_global->groups;
+    {
+        auto nonConstGlobalTopology                          = const_cast<gmx_mtop_t*>(top_global);
+        nonConstGlobalTopology->intermolecularExclusionGroup = genQmmmIndices(*top_global);
+    }
 
-    initialize_lambdas(fplog, *ir, MASTER(cr), &state_global->fep_state, state_global->lambda, lam0);
+    initialize_lambdas(fplog, *ir, MASTER(cr), &state_global->fep_state, state_global->lambda);
 
     const bool        simulationsShareState = false;
     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
@@ -252,10 +252,10 @@ void gmx::LegacySimulator::do_mimic()
     std::unique_ptr<t_state> stateInstance;
     t_state*                 state;
 
+    gmx_localtop_t top(top_global->ffparams);
+
     if (DOMAINDECOMP(cr))
     {
-        dd_init_local_top(*top_global, &top);
-
         stateInstance = std::make_unique<t_state>();
         state         = stateInstance.get();
         dd_init_local_state(cr->dd, state_global, state);
@@ -265,19 +265,15 @@ void gmx::LegacySimulator::do_mimic()
                             imdSession, pull_work, state, &f, mdAtoms, &top, fr, vsite, constr,
                             nrnb, nullptr, FALSE);
         shouldCheckNumberOfBondedInteractions = true;
-        gmx_bcast(sizeof(ir->nsteps), &ir->nsteps, cr);
+        gmx_bcast(sizeof(ir->nsteps), &ir->nsteps, cr->mpi_comm_mygroup);
     }
     else
     {
         state_change_natoms(state_global, state_global->natoms);
-        /* We need to allocate one element extra, since we might use
-         * (unaligned) 4-wide SIMD loads to access rvec entries.
-         */
-        f.resizeWithPadding(state_global->natoms);
         /* Copy the pointer to the global state */
         state = state_global;
 
-        mdAlgorithmsSetupAtomData(cr, ir, *top_global, &top, fr, &graph, mdAtoms, constr, vsite, shellfc);
+        mdAlgorithmsSetupAtomData(cr, ir, *top_global, &top, fr, &f, mdAtoms, constr, vsite, shellfc);
     }
 
     auto mdatoms = mdAtoms->mdatoms();
@@ -300,13 +296,13 @@ void gmx::LegacySimulator::do_mimic()
                  | (shouldCheckNumberOfBondedInteractions ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS : 0));
         bool   bSumEkinhOld = false;
         t_vcm* vcm          = nullptr;
-        compute_globals(gstat, cr, ir, fr, ekind, state->x.rvec_array(), state->v.rvec_array(),
-                        state->box, state->lambda[efptVDW], mdatoms, nrnb, vcm, nullptr, enerd,
-                        force_vir, shake_vir, total_vir, pres, mu_tot, constr, &nullSignaller,
-                        state->box, &totalNumberOfBondedInteractions, &bSumEkinhOld, cglo_flags);
+        compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                        makeConstArrayRef(state->v), state->box, mdatoms, nrnb, vcm, nullptr, enerd,
+                        force_vir, shake_vir, total_vir, pres, constr, &nullSignaller, state->box,
+                        &totalNumberOfBondedInteractions, &bSumEkinhOld, cglo_flags);
     }
     checkNumberOfBondedInteractions(mdlog, cr, totalNumberOfBondedInteractions, top_global, &top,
-                                    state->x.rvec_array(), state->box,
+                                    makeConstArrayRef(state->x), state->box,
                                     &shouldCheckNumberOfBondedInteractions);
 
     if (MASTER(cr))
@@ -347,6 +343,9 @@ void gmx::LegacySimulator::do_mimic()
                     "MiMiC does not report kinetic energy, total energy, temperature, virial and "
                     "pressure.");
 
+    step     = ir->init_step;
+    step_rel = 0;
+
     auto stopHandler = stopHandlerBuilder->getStopHandlerMD(
             compat::not_null<SimulationSignal*>(&signals[eglsSTOPCOND]), false, MASTER(cr),
             ir->nstlist, mdrunOptions.reproducible, nstglobalcomm, mdrunOptions.maximumHoursToRun,
@@ -357,9 +356,6 @@ void gmx::LegacySimulator::do_mimic()
 
     const DDBalanceRegionHandler ddBalanceRegionHandler(cr);
 
-    step     = ir->init_step;
-    step_rel = 0;
-
     /* and stop now if we should */
     isLastStep = (isLastStep || (ir->nsteps >= 0 && step_rel > ir->nsteps));
     while (!isLastStep)
@@ -371,12 +367,12 @@ void gmx::LegacySimulator::do_mimic()
 
         if (MASTER(cr))
         {
-            mimicCommunicator.getCoords(&state_global->x, state_global->natoms);
+            MimicCommunicator::getCoords(&state_global->x, state_global->natoms);
         }
 
         if (ir->efep != efepNO)
         {
-            setCurrentLambdasLocal(step, ir->fepvals, lam0, state->lambda, state->fep_state);
+            state->lambda = currentLambdas(step, *(ir->fepvals), state_global->fep_state);
         }
 
         if (MASTER(cr))
@@ -403,7 +399,7 @@ void gmx::LegacySimulator::do_mimic()
 
         if (MASTER(cr))
         {
-            energyOutput.printHeader(fplog, step, t); /* can we improve the information printed here? */
+            EnergyOutput::printHeader(fplog, step, t); /* can we improve the information printed here? */
         }
 
         if (ir->efep != efepNO)
@@ -419,11 +415,11 @@ void gmx::LegacySimulator::do_mimic()
         {
             /* Now is the time to relax the shells */
             relax_shell_flexcon(fplog, cr, ms, mdrunOptions.verbose, enforcedRotation, step, ir,
-                                imdSession, pull_work, bNS, force_flags, &top, constr, enerd, fcd,
+                                imdSession, pull_work, bNS, force_flags, &top, constr, enerd,
                                 state->natoms, state->x.arrayRefWithPadding(),
-                                state->v.arrayRefWithPadding(), state->box, state->lambda, &state->hist,
-                                f.arrayRefWithPadding(), force_vir, mdatoms, nrnb, wcycle, graph,
-                                shellfc, fr, runScheduleWork, t, mu_tot, vsite, ddBalanceRegionHandler);
+                                state->v.arrayRefWithPadding(), state->box, state->lambda,
+                                &state->hist, &f.view(), force_vir, mdatoms, nrnb, wcycle, shellfc,
+                                fr, runScheduleWork, t, mu_tot, vsite, ddBalanceRegionHandler);
         }
         else
         {
@@ -436,9 +432,8 @@ void gmx::LegacySimulator::do_mimic()
             gmx_edsam* ed  = nullptr;
             do_force(fplog, cr, ms, ir, awh, enforcedRotation, imdSession, pull_work, step, nrnb,
                      wcycle, &top, state->box, state->x.arrayRefWithPadding(), &state->hist,
-                     f.arrayRefWithPadding(), force_vir, mdatoms, enerd, fcd, state->lambda, graph,
-                     fr, runScheduleWork, vsite, mu_tot, t, ed, GMX_FORCE_NS | force_flags,
-                     ddBalanceRegionHandler);
+                     &f.view(), force_vir, mdatoms, enerd, state->lambda, fr, runScheduleWork,
+                     vsite, mu_tot, t, ed, GMX_FORCE_NS | force_flags, ddBalanceRegionHandler);
         }
 
         /* Now we have the energies and forces corresponding to the
@@ -450,33 +445,16 @@ void gmx::LegacySimulator::do_mimic()
             const bool bSumEkinhOld        = false;
             do_md_trajectory_writing(fplog, cr, nfile, fnm, step, step_rel, t, ir, state,
                                      state_global, observablesHistory, top_global, fr, outf,
-                                     energyOutput, ekind, f, isCheckpointingStep, doRerun,
-                                     isLastStep, mdrunOptions.writeConfout, bSumEkinhOld);
+                                     energyOutput, ekind, f.view().force(), isCheckpointingStep,
+                                     doRerun, isLastStep, mdrunOptions.writeConfout, bSumEkinhOld);
         }
 
         stopHandler->setSignal();
 
-        if (graph)
-        {
-            /* Need to unshift here */
-            unshift_self(graph, state->box, as_rvec_array(state->x.data()));
-        }
-
         if (vsite != nullptr)
         {
             wallcycle_start(wcycle, ewcVSITECONSTR);
-            if (graph != nullptr)
-            {
-                shift_self(graph, state->box, as_rvec_array(state->x.data()));
-            }
-            construct_vsites(vsite, as_rvec_array(state->x.data()), ir->delta_t,
-                             as_rvec_array(state->v.data()), top.idef.iparams, top.idef.il,
-                             fr->ePBC, fr->bMolPBC, cr, state->box);
-
-            if (graph != nullptr)
-            {
-                unshift_self(graph, state->box, as_rvec_array(state->x.data()));
-            }
+            vsite->construct(state->x, ir->delta_t, state->v, state->box);
             wallcycle_stop(wcycle, ewcVSITECONSTR);
         }
 
@@ -487,36 +465,37 @@ void gmx::LegacySimulator::do_mimic()
             t_vcm*              vcm              = nullptr;
             SimulationSignaller signaller(&signals, cr, ms, doInterSimSignal, doIntraSimSignal);
 
-            compute_globals(gstat, cr, ir, fr, ekind, state->x.rvec_array(), state->v.rvec_array(),
-                            state->box, state->lambda[efptVDW], mdatoms, nrnb, vcm, wcycle, enerd,
-                            nullptr, nullptr, nullptr, nullptr, mu_tot, constr, &signaller,
+            compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                            makeConstArrayRef(state->v), state->box, mdatoms, nrnb, vcm, wcycle,
+                            enerd, nullptr, nullptr, nullptr, nullptr, constr, &signaller,
                             state->box, &totalNumberOfBondedInteractions, &bSumEkinhOld,
                             CGLO_GSTAT | CGLO_ENERGY
                                     | (shouldCheckNumberOfBondedInteractions ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS
                                                                              : 0));
             checkNumberOfBondedInteractions(mdlog, cr, totalNumberOfBondedInteractions, top_global,
-                                            &top, state->x.rvec_array(), state->box,
+                                            &top, makeConstArrayRef(state->x), state->box,
                                             &shouldCheckNumberOfBondedInteractions);
         }
 
         {
             gmx::HostVector<gmx::RVec>     fglobal(top_global->natoms);
             gmx::ArrayRef<gmx::RVec>       ftemp;
-            gmx::ArrayRef<const gmx::RVec> flocal = gmx::makeArrayRef(f);
+            gmx::ArrayRef<const gmx::RVec> flocal = f.view().force();
             if (DOMAINDECOMP(cr))
             {
                 ftemp = gmx::makeArrayRef(fglobal);
-                dd_collect_vec(cr->dd, state, flocal, ftemp);
+                dd_collect_vec(cr->dd, state->ddp_count, state->ddp_count_cg_gl, state->cg_gl,
+                               flocal, ftemp);
             }
             else
             {
-                ftemp = gmx::makeArrayRef(f);
+                ftemp = f.view().force();
             }
 
             if (MASTER(cr))
             {
-                mimicCommunicator.sendEnergies(enerd->term[F_EPOT]);
-                mimicCommunicator.sendForces(ftemp, state_global->natoms);
+                MimicCommunicator::sendEnergies(enerd->term[F_EPOT]);
+                MimicCommunicator::sendForces(ftemp, state_global->natoms);
             }
         }
 
@@ -530,17 +509,19 @@ void gmx::LegacySimulator::do_mimic()
         {
             /* Sum up the foreign energy and dhdl terms for md and sd.
                Currently done every step so that dhdl is correct in the .edr */
-            sum_dhdl(enerd, state->lambda, *ir->fepvals);
+            accumulateKineticLambdaComponents(enerd, state->lambda, *ir->fepvals);
         }
 
         /* Output stuff */
         if (MASTER(cr))
         {
             const bool bCalcEnerStep = true;
-            energyOutput.addDataAtEnergyStep(doFreeEnergyPerturbation, bCalcEnerStep, t,
-                                             mdatoms->tmass, enerd, state, ir->fepvals,
-                                             ir->expandedvals, state->box, shake_vir, force_vir,
-                                             total_vir, pres, ekind, mu_tot, constr);
+            energyOutput.addDataAtEnergyStep(
+                    doFreeEnergyPerturbation, bCalcEnerStep, t, mdatoms->tmass, enerd, ir->fepvals,
+                    ir->expandedvals, state->box,
+                    PTCouplingArrays({ state->boxv, state->nosehoover_xi, state->nosehoover_vxi,
+                                       state->nhpres_xi, state->nhpres_vxi }),
+                    state->fep_state, shake_vir, force_vir, total_vir, pres, ekind, mu_tot, constr);
 
             const bool do_ene = true;
             const bool do_log = true;
@@ -548,9 +529,9 @@ void gmx::LegacySimulator::do_mimic()
             const bool do_dr  = ir->nstdisreout != 0;
             const bool do_or  = ir->nstorireout != 0;
 
-            energyOutput.printAnnealingTemperatures(do_log ? fplog : nullptr, groups, &(ir->opts));
+            EnergyOutput::printAnnealingTemperatures(do_log ? fplog : nullptr, groups, &(ir->opts));
             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), do_ene, do_dr, do_or,
-                                               do_log ? fplog : nullptr, step, t, fcd, awh);
+                                               do_log ? fplog : nullptr, step, t, fr->fcdata.get(), awh);
 
             if (do_per_step(step, ir->nstlog))
             {
@@ -592,7 +573,7 @@ void gmx::LegacySimulator::do_mimic()
 
     if (MASTER(cr))
     {
-        mimicCommunicator.finalize();
+        MimicCommunicator::finalize();
     }
 
     if (!thisRankHasDuty(cr, DUTY_PME))
index d90af4f9d791d2685ae70a3a9d81245655c8be70..f03445b605f941ec857b86e0212d4f04fed4f7c1 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/domdec/domdec_struct.h"
 #include "gromacs/domdec/mdsetup.h"
 #include "gromacs/domdec/partition.h"
-#include "gromacs/ewald/pme.h"
+#include "gromacs/ewald/pme_pp.h"
 #include "gromacs/fileio/confio.h"
 #include "gromacs/fileio/mtxio.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/imd/imd.h"
 #include "gromacs/linearalgebra/sparsematrix.h"
-#include "gromacs/listed_forces/manage_threading.h"
+#include "gromacs/listed_forces/listed_forces.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/mdlib/constr.h"
+#include "gromacs/mdlib/coupling.h"
 #include "gromacs/mdlib/dispersioncorrection.h"
 #include "gromacs/mdlib/ebin.h"
 #include "gromacs/mdlib/enerdata_utils.h"
 #include "gromacs/mdlib/energyoutput.h"
 #include "gromacs/mdlib/force.h"
+#include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdlib/forcerec.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdlib/md_support.h"
 #include "gromacs/mdlib/vsite.h"
 #include "gromacs/mdrunutility/handlerestart.h"
 #include "gromacs/mdrunutility/printtime.h"
+#include "gromacs/mdtypes/checkpointdata.h"
 #include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/forcebuffers.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/mdrunoptions.h"
 #include "gromacs/mdtypes/state.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/timing/wallcycle.h"
 #include "gromacs/timing/walltime_accounting.h"
 #include "legacysimulator.h"
 #include "shellfc.h"
 
+using gmx::ArrayRef;
 using gmx::MdrunScheduleWorkload;
+using gmx::RVec;
+using gmx::VirtualSitesHandler;
 
 //! Utility structure for manipulating states during EM
-typedef struct
+typedef struct em_state
 {
     //! Copy of the global state
     t_state s;
     //! Force array
-    PaddedHostVector<gmx::RVec> f;
+    gmx::ForceBuffers f;
     //! Potential energy
     real epot;
     //! Norm of the force
@@ -247,13 +257,13 @@ static void print_converged(FILE*             fp,
 }
 
 //! Compute the norm and max of the force array in parallel
-static void get_f_norm_max(const t_commrec* cr,
-                           t_grpopts*       opts,
-                           t_mdatoms*       mdatoms,
-                           const rvec*      f,
-                           real*            fnorm,
-                           real*            fmax,
-                           int*             a_fmax)
+static void get_f_norm_max(const t_commrec*               cr,
+                           t_grpopts*                     opts,
+                           t_mdatoms*                     mdatoms,
+                           gmx::ArrayRef<const gmx::RVec> f,
+                           real*                          fnorm,
+                           real*                          fmax,
+                           int*                           a_fmax)
 {
     double fnorm2, *sum;
     real   fmax2, fam;
@@ -347,7 +357,7 @@ static void get_f_norm_max(const t_commrec* cr,
 //! Compute the norm of the force
 static void get_state_f_norm_max(const t_commrec* cr, t_grpopts* opts, t_mdatoms* mdatoms, em_state_t* ems)
 {
-    get_f_norm_max(cr, opts, mdatoms, ems->f.rvec_array(), &ems->fnorm, &ems->fmax, &ems->a_fmax);
+    get_f_norm_max(cr, opts, mdatoms, ems->f.view().force(), &ems->fnorm, &ems->fmax, &ems->a_fmax);
 }
 
 //! Initialize the energy minimization
@@ -359,15 +369,14 @@ static void init_em(FILE*                fplog,
                     gmx::ImdSession*     imdSession,
                     pull_t*              pull_work,
                     t_state*             state_global,
-                    gmx_mtop_t*          top_global,
+                    const gmx_mtop_t*    top_global,
                     em_state_t*          ems,
                     gmx_localtop_t*      top,
                     t_nrnb*              nrnb,
                     t_forcerec*          fr,
-                    t_graph**            graph,
                     gmx::MDAtoms*        mdAtoms,
                     gmx_global_stat_t*   gstat,
-                    gmx_vsite_t*         vsite,
+                    VirtualSitesHandler* vsite,
                     gmx::Constraints*    constr,
                     gmx_shellfc_t**      shellfc)
 {
@@ -382,7 +391,9 @@ static void init_em(FILE*                fplog,
     {
         state_global->ngtc = 0;
     }
-    initialize_lambdas(fplog, *ir, MASTER(cr), &(state_global->fep_state), state_global->lambda, nullptr);
+    int*                fep_state = MASTER(cr) ? &state_global->fep_state : nullptr;
+    gmx::ArrayRef<real> lambda    = MASTER(cr) ? state_global->lambda : gmx::ArrayRef<real>();
+    initialize_lambdas(fplog, *ir, MASTER(cr), fep_state, lambda);
 
     if (ir->eI == eiNM)
     {
@@ -406,12 +417,8 @@ static void init_em(FILE*                fplog,
         }
     }
 
-    auto mdatoms = mdAtoms->mdatoms();
     if (DOMAINDECOMP(cr))
     {
-        top->useInDomainDecomp_ = true;
-        dd_init_local_top(*top_global, top);
-
         dd_init_local_state(cr->dd, state_global, &ems->s);
 
         /* Distribute the charge groups over the nodes from the master node */
@@ -419,8 +426,6 @@ static void init_em(FILE*                fplog,
                             imdSession, pull_work, &ems->s, &ems->f, mdAtoms, top, fr, vsite,
                             constr, nrnb, nullptr, FALSE);
         dd_store_state(cr->dd, &ems->s);
-
-        *graph = nullptr;
     }
     else
     {
@@ -428,15 +433,9 @@ static void init_em(FILE*                fplog,
         /* Just copy the state */
         ems->s = *state_global;
         state_change_natoms(&ems->s, ems->s.natoms);
-        ems->f.resizeWithPadding(ems->s.natoms);
 
-        mdAlgorithmsSetupAtomData(cr, ir, *top_global, top, fr, graph, mdAtoms, constr, vsite,
+        mdAlgorithmsSetupAtomData(cr, ir, *top_global, top, fr, &ems->f, mdAtoms, constr, vsite,
                                   shellfc ? *shellfc : nullptr);
-
-        if (vsite)
-        {
-            set_vsite_top(vsite, top, mdatoms);
-        }
     }
 
     update_mdatoms(mdAtoms->mdatoms(), ems->s.lambda[efptMASS]);
@@ -453,10 +452,14 @@ static void init_em(FILE*                fplog,
         if (!ir->bContinuation)
         {
             /* Constrain the starting coordinates */
-            dvdl_constr = 0;
-            constr->apply(TRUE, TRUE, -1, 0, 1.0, ems->s.x.rvec_array(), ems->s.x.rvec_array(),
-                          nullptr, ems->s.box, ems->s.lambda[efptFEP], &dvdl_constr, nullptr,
-                          nullptr, gmx::ConstraintVariable::Positions);
+            bool needsLogging  = true;
+            bool computeEnergy = true;
+            bool computeVirial = false;
+            dvdl_constr        = 0;
+            constr->apply(needsLogging, computeEnergy, -1, 0, 1.0, ems->s.x.arrayRefWithPadding(),
+                          ems->s.x.arrayRefWithPadding(), ArrayRef<RVec>(), ems->s.box,
+                          ems->s.lambda[efptFEP], &dvdl_constr, gmx::ArrayRefWithPadding<RVec>(),
+                          computeVirial, nullptr, gmx::ConstraintVariable::Positions);
         }
     }
 
@@ -506,7 +509,7 @@ static void write_em_traj(FILE*               fplog,
                           gmx_bool            bX,
                           gmx_bool            bF,
                           const char*         confout,
-                          gmx_mtop_t*         top_global,
+                          const gmx_mtop_t*   top_global,
                           t_inputrec*         ir,
                           int64_t             step,
                           em_state_t*         state,
@@ -530,9 +533,10 @@ static void write_em_traj(FILE*               fplog,
         mdof_flags |= MDOF_IMD;
     }
 
+    gmx::WriteCheckpointDataHolder checkpointDataHolder;
     mdoutf_write_to_trajectory_files(fplog, cr, outf, mdof_flags, top_global->natoms, step,
                                      static_cast<double>(step), &state->s, state_global,
-                                     observablesHistory, state->f);
+                                     observablesHistory, state->f.view().force(), &checkpointDataHolder);
 
     if (confout != nullptr)
     {
@@ -542,7 +546,8 @@ static void write_em_traj(FILE*               fplog,
             if (!bX)
             {
                 auto globalXRef = MASTER(cr) ? state_global->x : gmx::ArrayRef<gmx::RVec>();
-                dd_collect_vec(cr->dd, &state->s, state->s.x, globalXRef);
+                dd_collect_vec(cr->dd, state->s.ddp_count, state->s.ddp_count_cg_gl, state->s.cg_gl,
+                               state->s.x, globalXRef);
             }
         }
         else
@@ -553,14 +558,14 @@ static void write_em_traj(FILE*               fplog,
 
         if (MASTER(cr))
         {
-            if (ir->ePBC != epbcNONE && !ir->bPeriodicMols && DOMAINDECOMP(cr))
+            if (ir->pbcType != PbcType::No && !ir->bPeriodicMols && DOMAINDECOMP(cr))
             {
                 /* Make molecules whole only for confout writing */
-                do_pbc_mtop(ir->ePBC, state->s.box, top_global, state_global->x.rvec_array());
+                do_pbc_mtop(ir->pbcType, state->s.box, top_global, state_global->x.rvec_array());
             }
 
             write_sto_conf_mtop(confout, *top_global->name, top_global,
-                                state_global->x.rvec_array(), nullptr, ir->ePBC, state->s.box);
+                                state_global->x.rvec_array(), nullptr, ir->pbcType, state->s.box);
         }
     }
 }
@@ -568,15 +573,15 @@ static void write_em_traj(FILE*               fplog,
 //! \brief Do one minimization step
 //
 // \returns true when the step succeeded, false when a constraint error occurred
-static bool do_em_step(const t_commrec*                   cr,
-                       t_inputrec*                        ir,
-                       t_mdatoms*                         md,
-                       em_state_t*                        ems1,
-                       real                               a,
-                       const PaddedHostVector<gmx::RVec>* force,
-                       em_state_t*                        ems2,
-                       gmx::Constraints*                  constr,
-                       int64_t                            count)
+static bool do_em_step(const t_commrec*                          cr,
+                       t_inputrec*                               ir,
+                       t_mdatoms*                                md,
+                       em_state_t*                               ems1,
+                       real                                      a,
+                       gmx::ArrayRefWithPadding<const gmx::RVec> force,
+                       em_state_t*                               ems2,
+                       gmx::Constraints*                         constr,
+                       int64_t                                   count)
 
 {
     t_state *s1, *s2;
@@ -599,7 +604,7 @@ static bool do_em_step(const t_commrec*                   cr,
     if (s2->natoms != s1->natoms)
     {
         state_change_natoms(s2, s1->natoms);
-        ems2->f.resizeWithPadding(s2->natoms);
+        ems2->f.resize(s2->natoms);
     }
     if (DOMAINDECOMP(cr) && s2->cg_gl.size() != s1->cg_gl.size())
     {
@@ -619,7 +624,7 @@ static bool do_em_step(const t_commrec*                   cr,
     {
         const rvec* x1 = s1->x.rvec_array();
         rvec*       x2 = s2->x.rvec_array();
-        const rvec* f  = force->rvec_array();
+        const rvec* f  = as_rvec_array(force.unpaddedArrayRef().data());
 
         int gf = 0;
 #pragma omp for schedule(static) nowait
@@ -679,9 +684,10 @@ static bool do_em_step(const t_commrec*                   cr,
     if (constr)
     {
         dvdl_constr = 0;
-        validStep = constr->apply(TRUE, TRUE, count, 0, 1.0, s1->x.rvec_array(), s2->x.rvec_array(),
-                                  nullptr, s2->box, s2->lambda[efptBONDED], &dvdl_constr, nullptr,
-                                  nullptr, gmx::ConstraintVariable::Positions);
+        validStep   = constr->apply(
+                TRUE, TRUE, count, 0, 1.0, s1->x.arrayRefWithPadding(), s2->x.arrayRefWithPadding(),
+                ArrayRef<RVec>(), s2->box, s2->lambda[efptBONDED], &dvdl_constr,
+                gmx::ArrayRefWithPadding<RVec>(), false, nullptr, gmx::ConstraintVariable::Positions);
 
         if (cr->nnodes > 1)
         {
@@ -712,7 +718,7 @@ static void em_dd_partition_system(FILE*                fplog,
                                    const gmx::MDLogger& mdlog,
                                    int                  step,
                                    const t_commrec*     cr,
-                                   gmx_mtop_t*          top_global,
+                                   const gmx_mtop_t*    top_global,
                                    t_inputrec*          ir,
                                    gmx::ImdSession*     imdSession,
                                    pull_t*              pull_work,
@@ -720,7 +726,7 @@ static void em_dd_partition_system(FILE*                fplog,
                                    gmx_localtop_t*      top,
                                    gmx::MDAtoms*        mdAtoms,
                                    t_forcerec*          fr,
-                                   gmx_vsite_t*         vsite,
+                                   VirtualSitesHandler* vsite,
                                    gmx::Constraints*    constr,
                                    t_nrnb*              nrnb,
                                    gmx_wallcycle_t      wcycle)
@@ -767,7 +773,7 @@ public:
     //! Coordinates multi-simulations.
     const gmx_multisim_t* ms;
     //! Holds the simulation topology.
-    gmx_mtop_t* top_global;
+    const gmx_mtop_t* top_global;
     //! Holds the domain topology.
     gmx_localtop_t* top;
     //! User input options.
@@ -783,13 +789,9 @@ public:
     //! Coordinates global reduction.
     gmx_global_stat_t gstat;
     //! Handles virtual sites.
-    gmx_vsite_t* vsite;
+    VirtualSitesHandler* vsite;
     //! Handles constraints.
     gmx::Constraints* constr;
-    //! Handles strange things.
-    t_fcdata* fcd;
-    //! Molecular graph for SHAKE.
-    t_graph* graph;
     //! Per-atom data for this domain.
     gmx::MDAtoms* mdAtoms;
     //! Handles how to calculate the forces.
@@ -827,8 +829,7 @@ void EnergyEvaluator::run(em_state_t* ems, rvec mu_tot, tensor vir, tensor pres,
 
     if (vsite)
     {
-        construct_vsites(vsite, ems->s.x.rvec_array(), 1, nullptr, top->idef.iparams, top->idef.il,
-                         fr->ePBC, fr->bMolPBC, cr, ems->s.box);
+        vsite->construct(ems->s.x, 1, {}, ems->s.box);
     }
 
     if (DOMAINDECOMP(cr) && bNS)
@@ -843,9 +844,8 @@ void EnergyEvaluator::run(em_state_t* ems, rvec mu_tot, tensor vir, tensor pres,
      * We do not unshift, so molecules are always whole in congrad.c
      */
     do_force(fplog, cr, ms, inputrec, nullptr, nullptr, imdSession, pull_work, count, nrnb, wcycle,
-             top, ems->s.box, ems->s.x.arrayRefWithPadding(), &ems->s.hist,
-             ems->f.arrayRefWithPadding(), force_vir, mdAtoms->mdatoms(), enerd, fcd, ems->s.lambda,
-             graph, fr, runScheduleWork, vsite, mu_tot, t, nullptr,
+             top, ems->s.box, ems->s.x.arrayRefWithPadding(), &ems->s.hist, &ems->f.view(), force_vir,
+             mdAtoms->mdatoms(), enerd, ems->s.lambda, fr, runScheduleWork, vsite, mu_tot, t, nullptr,
              GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES | GMX_FORCE_VIRIAL | GMX_FORCE_ENERGY
                      | (bNS ? GMX_FORCE_NS : 0),
              DDBalanceRegionHandler(cr));
@@ -859,8 +859,8 @@ void EnergyEvaluator::run(em_state_t* ems, rvec mu_tot, tensor vir, tensor pres,
     {
         wallcycle_start(wcycle, ewcMoveE);
 
-        global_stat(gstat, cr, enerd, force_vir, shake_vir, mu_tot, inputrec, nullptr, nullptr, nullptr,
-                    1, &terminate, nullptr, FALSE, CGLO_ENERGY | CGLO_PRESSURE | CGLO_CONSTRAINT);
+        global_stat(gstat, cr, enerd, force_vir, shake_vir, inputrec, nullptr, nullptr, nullptr, 1,
+                    &terminate, nullptr, FALSE, CGLO_ENERGY | CGLO_PRESSURE | CGLO_CONSTRAINT);
 
         wallcycle_stop(wcycle, ewcMoveE);
     }
@@ -886,10 +886,14 @@ void EnergyEvaluator::run(em_state_t* ems, rvec mu_tot, tensor vir, tensor pres,
     if (constr)
     {
         /* Project out the constraint components of the force */
-        dvdl_constr  = 0;
-        rvec* f_rvec = ems->f.rvec_array();
-        constr->apply(FALSE, FALSE, count, 0, 1.0, ems->s.x.rvec_array(), f_rvec, f_rvec,
-                      ems->s.box, ems->s.lambda[efptBONDED], &dvdl_constr, nullptr, &shake_vir,
+        bool needsLogging  = false;
+        bool computeEnergy = false;
+        bool computeVirial = true;
+        dvdl_constr        = 0;
+        auto f             = ems->f.view().forceWithPadding();
+        constr->apply(needsLogging, computeEnergy, count, 0, 1.0, ems->s.x.arrayRefWithPadding(), f,
+                      f.unpaddedArrayRef(), ems->s.box, ems->s.lambda[efptBONDED], &dvdl_constr,
+                      gmx::ArrayRefWithPadding<RVec>(), computeVirial, shake_vir,
                       gmx::ConstraintVariable::ForceDispl);
         enerd->term[F_DVDL_CONSTR] += dvdl_constr;
         m_add(force_vir, shake_vir, vir);
@@ -900,9 +904,12 @@ void EnergyEvaluator::run(em_state_t* ems, rvec mu_tot, tensor vir, tensor pres,
     }
 
     clear_mat(ekin);
-    enerd->term[F_PRES] = calc_pres(fr->ePBC, inputrec->nwall, ems->s.box, ekin, vir, pres);
+    enerd->term[F_PRES] = calc_pres(fr->pbcType, inputrec->nwall, ems->s.box, ekin, vir, pres);
 
-    sum_dhdl(enerd, ems->s.lambda, *inputrec->fepvals);
+    if (inputrec->efep != efepNO)
+    {
+        accumulateKineticLambdaComponents(enerd, ems->s.lambda, *inputrec->fepvals);
+    }
 
     if (EI_ENERGY_MINIMIZATION(inputrec->eI))
     {
@@ -913,19 +920,19 @@ void EnergyEvaluator::run(em_state_t* ems, rvec mu_tot, tensor vir, tensor pres,
 } // namespace
 
 //! Parallel utility summing energies and forces
-static double reorder_partsum(const t_commrec* cr,
-                              t_grpopts*       opts,
-                              gmx_mtop_t*      top_global,
-                              em_state_t*      s_min,
-                              em_state_t*      s_b)
+static double reorder_partsum(const t_commrec*  cr,
+                              t_grpopts*        opts,
+                              const gmx_mtop_t* top_global,
+                              const em_state_t* s_min,
+                              const em_state_t* s_b)
 {
     if (debug)
     {
         fprintf(debug, "Doing reorder_partsum\n");
     }
 
-    const rvec* fm = s_min->f.rvec_array();
-    const rvec* fb = s_b->f.rvec_array();
+    auto fm = s_min->f.view().force();
+    auto fb = s_b->f.view().force();
 
     /* Collect fm in a global vector fmg.
      * This conflicts with the spirit of domain decomposition,
@@ -947,10 +954,10 @@ static double reorder_partsum(const t_commrec* cr,
     /* Now we will determine the part of the sum for the cgs in state s_b */
     gmx::ArrayRef<const int> indicesB = s_b->s.cg_gl;
 
-    double partsum                  = 0;
-    i                               = 0;
-    int                          gf = 0;
-    gmx::ArrayRef<unsigned char> grpnrFREEZE =
+    double partsum                        = 0;
+    i                                     = 0;
+    int                                gf = 0;
+    gmx::ArrayRef<const unsigned char> grpnrFREEZE =
             top_global->groups.groupNumbers[SimulationAtomGroupType::Freeze];
     for (int a : indicesB)
     {
@@ -974,12 +981,12 @@ static double reorder_partsum(const t_commrec* cr,
 }
 
 //! Print some stuff, like beta, whatever that means.
-static real pr_beta(const t_commrec* cr,
-                    t_grpopts*       opts,
-                    t_mdatoms*       mdatoms,
-                    gmx_mtop_t*      top_global,
-                    em_state_t*      s_min,
-                    em_state_t*      s_b)
+static real pr_beta(const t_commrec*  cr,
+                    t_grpopts*        opts,
+                    t_mdatoms*        mdatoms,
+                    const gmx_mtop_t* top_global,
+                    const em_state_t* s_min,
+                    const em_state_t* s_b)
 {
     double sum;
 
@@ -991,10 +998,10 @@ static real pr_beta(const t_commrec* cr,
     if (!DOMAINDECOMP(cr)
         || (s_min->s.ddp_count == cr->dd->ddp_count && s_b->s.ddp_count == cr->dd->ddp_count))
     {
-        const rvec* fm = s_min->f.rvec_array();
-        const rvec* fb = s_b->f.rvec_array();
-        sum            = 0;
-        int gf         = 0;
+        auto fm = s_min->f.view().force();
+        auto fb = s_b->f.view().force();
+        sum     = 0;
+        int gf  = 0;
         /* This part of code can be incorrect with DD,
          * since the atom ordering in s_b and s_min might differ.
          */
@@ -1033,9 +1040,8 @@ void LegacySimulator::do_cg()
 {
     const char* CG = "Polak-Ribiere Conjugate Gradients";
 
-    gmx_localtop_t    top;
+    gmx_localtop_t    top(top_global->ffparams);
     gmx_global_stat_t gstat;
-    t_graph*          graph;
     double            tmp, minstep;
     real              stepsize;
     real              a, b, c, beta = 0.0;
@@ -1083,7 +1089,7 @@ void LegacySimulator::do_cg()
 
     /* Init em and store the local state in s_min */
     init_em(fplog, mdlog, CG, cr, inputrec, imdSession, pull_work, state_global, top_global, s_min,
-            &top, nrnb, fr, &graph, mdAtoms, &gstat, vsite, constr, nullptr);
+            &top, nrnb, fr, mdAtoms, &gstat, vsite, constr, nullptr);
     const bool        simulationsShareState = false;
     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
@@ -1106,11 +1112,9 @@ void LegacySimulator::do_cg()
         sp_header(fplog, CG, inputrec->em_tol, number_steps);
     }
 
-    EnergyEvaluator energyEvaluator{
-        fplog,      mdlog,     cr,      ms,     top_global,      &top,  inputrec,
-        imdSession, pull_work, nrnb,    wcycle, gstat,           vsite, constr,
-        fcd,        graph,     mdAtoms, fr,     runScheduleWork, enerd
-    };
+    EnergyEvaluator energyEvaluator{ fplog,    mdlog,      cr,        ms,   top_global,      &top,
+                                     inputrec, imdSession, pull_work, nrnb, wcycle,          gstat,
+                                     vsite,    constr,     mdAtoms,   fr,   runScheduleWork, enerd };
     /* 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
@@ -1122,12 +1126,12 @@ void LegacySimulator::do_cg()
         /* Copy stuff to the energy bin for easy printing etc. */
         matrix nullBox = {};
         energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
-                                         enerd, nullptr, nullptr, nullptr, nullBox, nullptr,
-                                         nullptr, vir, pres, nullptr, mu_tot, constr);
+                                         enerd, nullptr, nullptr, nullBox, PTCouplingArrays(), 0,
+                                         nullptr, nullptr, vir, pres, nullptr, mu_tot, constr);
 
-        energyOutput.printHeader(fplog, step, step);
+        EnergyOutput::printHeader(fplog, step, step);
         energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step,
-                                           step, fcd, nullptr);
+                                           step, fr->fcdata.get(), nullptr);
     }
 
     /* Estimate/guess the initial stepsize */
@@ -1158,10 +1162,10 @@ void LegacySimulator::do_cg()
          */
 
         /* Calculate the new direction in p, and the gradient in this direction, gpa */
-        rvec*       pm  = s_min->s.cg_p.rvec_array();
-        const rvec* sfm = s_min->f.rvec_array();
-        double      gpa = 0;
-        int         gf  = 0;
+        gmx::ArrayRef<gmx::RVec>       pm  = s_min->s.cg_p;
+        gmx::ArrayRef<const gmx::RVec> sfm = s_min->f.view().force();
+        double                         gpa = 0;
+        int                            gf  = 0;
         for (int i = 0; i < mdatoms->homenr; i++)
         {
             if (mdatoms->cFREEZE)
@@ -1278,16 +1282,17 @@ void LegacySimulator::do_cg()
         }
 
         /* 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, -1);
+        do_em_step(cr, inputrec, mdatoms, s_min, c, s_min->s.cg_p.constArrayRefWithPadding(), s_c,
+                   constr, -1);
 
         neval++;
         /* Calculate energy for the trial step */
         energyEvaluator.run(s_c, mu_tot, vir, pres, -1, FALSE);
 
         /* Calc derivative along line */
-        const rvec* pc  = s_c->s.cg_p.rvec_array();
-        const rvec* sfc = s_c->f.rvec_array();
-        double      gpc = 0;
+        const rvec*                    pc  = s_c->s.cg_p.rvec_array();
+        gmx::ArrayRef<const gmx::RVec> sfc = s_c->f.view().force();
+        double                         gpc = 0;
         for (int i = 0; i < mdatoms->homenr; i++)
         {
             for (m = 0; m < DIM; m++)
@@ -1379,7 +1384,8 @@ void LegacySimulator::do_cg()
                 }
 
                 /* 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, -1);
+                do_em_step(cr, inputrec, mdatoms, s_min, b,
+                           s_min->s.cg_p.constArrayRefWithPadding(), s_b, constr, -1);
 
                 neval++;
                 /* Calculate energy for the trial step */
@@ -1388,9 +1394,9 @@ void LegacySimulator::do_cg()
                 /* p does not change within a step, but since the domain decomposition
                  * might change, we have to use cg_p of s_b here.
                  */
-                const rvec* pb  = s_b->s.cg_p.rvec_array();
-                const rvec* sfb = s_b->f.rvec_array();
-                gpb             = 0;
+                const rvec*                    pb  = s_b->s.cg_p.rvec_array();
+                gmx::ArrayRef<const gmx::RVec> sfb = s_b->f.view().force();
+                gpb                                = 0;
                 for (int i = 0; i < mdatoms->homenr; i++)
                 {
                     for (m = 0; m < DIM; m++)
@@ -1529,8 +1535,8 @@ void LegacySimulator::do_cg()
             /* Store the new (lower) energies */
             matrix nullBox = {};
             energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
-                                             enerd, nullptr, nullptr, nullptr, nullBox, nullptr,
-                                             nullptr, vir, pres, nullptr, mu_tot, constr);
+                                             enerd, nullptr, nullptr, nullBox, PTCouplingArrays(), 0,
+                                             nullptr, nullptr, vir, pres, nullptr, mu_tot, constr);
 
             do_log = do_per_step(step, inputrec->nstlog);
             do_ene = do_per_step(step, inputrec->nstenergy);
@@ -1539,10 +1545,11 @@ void LegacySimulator::do_cg()
 
             if (do_log)
             {
-                energyOutput.printHeader(fplog, step, step);
+                EnergyOutput::printHeader(fplog, step, step);
             }
             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), do_ene, FALSE, FALSE,
-                                               do_log ? fplog : nullptr, step, step, fcd, nullptr);
+                                               do_log ? fplog : nullptr, step, step,
+                                               fr->fcdata.get(), nullptr);
         }
 
         /* Send energies and positions to the IMD client if bIMD is TRUE. */
@@ -1579,13 +1586,14 @@ void LegacySimulator::do_cg()
         if (!do_log)
         {
             /* Write final value to log since we didn't do anything the last step */
-            energyOutput.printHeader(fplog, step, step);
+            EnergyOutput::printHeader(fplog, step, step);
         }
         if (!do_ene || !do_log)
         {
             /* Write final energy file entries */
             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), !do_ene, FALSE, FALSE,
-                                               !do_log ? fplog : nullptr, step, step, fcd, nullptr);
+                                               !do_log ? fplog : nullptr, step, step,
+                                               fr->fcdata.get(), nullptr);
         }
     }
 
@@ -1632,9 +1640,8 @@ void LegacySimulator::do_lbfgs()
 {
     static const char* LBFGS = "Low-Memory BFGS Minimizer";
     em_state_t         ems;
-    gmx_localtop_t     top;
+    gmx_localtop_t     top(top_global->ffparams);
     gmx_global_stat_t  gstat;
-    t_graph*           graph;
     int                ncorr, nmaxcorr, point, cp, neval, nminstep;
     double             stepsize, step_taken, gpa, gpb, gpc, tmp, minstep;
     real *             rho, *alpha, *p, *s, **dx, **dg;
@@ -1697,7 +1704,7 @@ void LegacySimulator::do_lbfgs()
 
     /* Init em */
     init_em(fplog, mdlog, LBFGS, cr, inputrec, imdSession, pull_work, state_global, top_global,
-            &ems, &top, nrnb, fr, &graph, mdAtoms, &gstat, vsite, constr, nullptr);
+            &ems, &top, nrnb, fr, mdAtoms, &gstat, vsite, constr, nullptr);
     const bool        simulationsShareState = false;
     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
@@ -1751,8 +1758,7 @@ void LegacySimulator::do_lbfgs()
 
     if (vsite)
     {
-        construct_vsites(vsite, state_global->x.rvec_array(), 1, nullptr, top.idef.iparams,
-                         top.idef.il, fr->ePBC, fr->bMolPBC, cr, state_global->box);
+        vsite->construct(state_global->x, 1, {}, state_global->box);
     }
 
     /* Call the force routine and some auxiliary (neighboursearching etc.) */
@@ -1760,11 +1766,9 @@ void LegacySimulator::do_lbfgs()
      * We do not unshift, so molecules are always whole
      */
     neval++;
-    EnergyEvaluator energyEvaluator{
-        fplog,      mdlog,     cr,      ms,     top_global,      &top,  inputrec,
-        imdSession, pull_work, nrnb,    wcycle, gstat,           vsite, constr,
-        fcd,        graph,     mdAtoms, fr,     runScheduleWork, enerd
-    };
+    EnergyEvaluator energyEvaluator{ fplog,    mdlog,      cr,        ms,   top_global,      &top,
+                                     inputrec, imdSession, pull_work, nrnb, wcycle,          gstat,
+                                     vsite,    constr,     mdAtoms,   fr,   runScheduleWork, enerd };
     energyEvaluator.run(&ems, mu_tot, vir, pres, -1, TRUE);
 
     if (MASTER(cr))
@@ -1772,12 +1776,12 @@ void LegacySimulator::do_lbfgs()
         /* Copy stuff to the energy bin for easy printing etc. */
         matrix nullBox = {};
         energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
-                                         enerd, nullptr, nullptr, nullptr, nullBox, nullptr,
-                                         nullptr, vir, pres, nullptr, mu_tot, constr);
+                                         enerd, nullptr, nullptr, nullBox, PTCouplingArrays(), 0,
+                                         nullptr, nullptr, vir, pres, nullptr, mu_tot, constr);
 
-        energyOutput.printHeader(fplog, step, step);
+        EnergyOutput::printHeader(fplog, step, step);
         energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step,
-                                           step, fcd, nullptr);
+                                           step, fr->fcdata.get(), nullptr);
     }
 
     /* Set the initial step.
@@ -1804,7 +1808,7 @@ void LegacySimulator::do_lbfgs()
     point = 0;
 
     // Set initial search direction to the force (-gradient), or 0 for frozen particles.
-    real* fInit = static_cast<real*>(ems.f.rvec_array()[0]);
+    real* fInit = static_cast<real*>(ems.f.view().force().data()[0]);
     for (i = 0; i < n; i++)
     {
         if (!frozen[i])
@@ -1855,9 +1859,10 @@ void LegacySimulator::do_lbfgs()
             mdof_flags |= MDOF_IMD;
         }
 
+        gmx::WriteCheckpointDataHolder checkpointDataHolder;
         mdoutf_write_to_trajectory_files(fplog, cr, outf, mdof_flags, top_global->natoms, step,
-                                         static_cast<real>(step), &ems.s, state_global,
-                                         observablesHistory, ems.f);
+                                         static_cast<real>(step), &ems.s, state_global, observablesHistory,
+                                         ems.f.view().force(), &checkpointDataHolder);
 
         /* Do the linesearching in the direction dx[point][0..(n-1)] */
 
@@ -1865,7 +1870,7 @@ void LegacySimulator::do_lbfgs()
         s = dx[point];
 
         real* xx = static_cast<real*>(ems.s.x.rvec_array()[0]);
-        real* ff = static_cast<real*>(ems.f.rvec_array()[0]);
+        real* ff = static_cast<real*>(ems.f.view().force().data()[0]);
 
         // calculate line gradient in position A
         for (gpa = 0, i = 0; i < n; i++)
@@ -1897,7 +1902,7 @@ void LegacySimulator::do_lbfgs()
         // Before taking any steps along the line, store the old position
         *last       = ems;
         real* lastx = static_cast<real*>(last->s.x.data()[0]);
-        real* lastf = static_cast<real*>(last->f.data()[0]);
+        real* lastf = static_cast<real*>(last->f.view().force().data()[0]);
         Epot0       = ems.epot;
 
         *sa = ems;
@@ -1969,7 +1974,7 @@ void LegacySimulator::do_lbfgs()
         energyEvaluator.run(sc, mu_tot, vir, pres, step, FALSE);
 
         // Calc line gradient in position C
-        real* fc = static_cast<real*>(sc->f.rvec_array()[0]);
+        real* fc = static_cast<real*>(sc->f.view().force()[0]);
         for (gpc = 0, i = 0; i < n; i++)
         {
             gpc -= s[i] * fc[i]; /* f is negative gradient, thus the sign */
@@ -2051,7 +2056,7 @@ void LegacySimulator::do_lbfgs()
                 fnorm = sb->fnorm;
 
                 // Calculate gradient in point B
-                real* fb = static_cast<real*>(sb->f.rvec_array()[0]);
+                real* fb = static_cast<real*>(sb->f.view().force()[0]);
                 for (gpb = 0, i = 0; i < n; i++)
                 {
                     gpb -= s[i] * fb[i]; /* f is negative gradient, thus the sign */
@@ -2256,8 +2261,8 @@ void LegacySimulator::do_lbfgs()
             /* Store the new (lower) energies */
             matrix nullBox = {};
             energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
-                                             enerd, nullptr, nullptr, nullptr, nullBox, nullptr,
-                                             nullptr, vir, pres, nullptr, mu_tot, constr);
+                                             enerd, nullptr, nullptr, nullBox, PTCouplingArrays(), 0,
+                                             nullptr, nullptr, vir, pres, nullptr, mu_tot, constr);
 
             do_log = do_per_step(step, inputrec->nstlog);
             do_ene = do_per_step(step, inputrec->nstenergy);
@@ -2266,10 +2271,11 @@ void LegacySimulator::do_lbfgs()
 
             if (do_log)
             {
-                energyOutput.printHeader(fplog, step, step);
+                EnergyOutput::printHeader(fplog, step, step);
             }
             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), do_ene, FALSE, FALSE,
-                                               do_log ? fplog : nullptr, step, step, fcd, nullptr);
+                                               do_log ? fplog : nullptr, step, step,
+                                               fr->fcdata.get(), nullptr);
         }
 
         /* Send x and E to IMD client, if bIMD is TRUE. */
@@ -2306,12 +2312,13 @@ void LegacySimulator::do_lbfgs()
      */
     if (!do_log) /* Write final value to log since we didn't do anythin last step */
     {
-        energyOutput.printHeader(fplog, step, step);
+        EnergyOutput::printHeader(fplog, step, step);
     }
     if (!do_ene || !do_log) /* Write final energy file entries */
     {
         energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), !do_ene, FALSE, FALSE,
-                                           !do_log ? fplog : nullptr, step, step, fcd, nullptr);
+                                           !do_log ? fplog : nullptr, step, step, fr->fcdata.get(),
+                                           nullptr);
     }
 
     /* Print some stuff... */
@@ -2350,9 +2357,8 @@ void LegacySimulator::do_lbfgs()
 void LegacySimulator::do_steep()
 {
     const char*       SD = "Steepest Descents";
-    gmx_localtop_t    top;
+    gmx_localtop_t    top(top_global->ffparams);
     gmx_global_stat_t gstat;
-    t_graph*          graph;
     real              stepsize;
     real              ustep;
     gmx_bool          bDone, bAbort, do_x, do_f;
@@ -2378,7 +2384,7 @@ void LegacySimulator::do_steep()
 
     /* Init em and store the local state in s_try */
     init_em(fplog, mdlog, SD, cr, inputrec, imdSession, pull_work, state_global, top_global, s_try,
-            &top, nrnb, fr, &graph, mdAtoms, &gstat, vsite, constr, nullptr);
+            &top, nrnb, fr, mdAtoms, &gstat, vsite, constr, nullptr);
     const bool        simulationsShareState = false;
     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
@@ -2407,11 +2413,9 @@ void LegacySimulator::do_steep()
     {
         sp_header(fplog, SD, inputrec->em_tol, nsteps);
     }
-    EnergyEvaluator energyEvaluator{
-        fplog,      mdlog,     cr,      ms,     top_global,      &top,  inputrec,
-        imdSession, pull_work, nrnb,    wcycle, gstat,           vsite, constr,
-        fcd,        graph,     mdAtoms, fr,     runScheduleWork, enerd
-    };
+    EnergyEvaluator energyEvaluator{ fplog,    mdlog,      cr,        ms,   top_global,      &top,
+                                     inputrec, imdSession, pull_work, nrnb, wcycle,          gstat,
+                                     vsite,    constr,     mdAtoms,   fr,   runScheduleWork, enerd };
 
     /**** HERE STARTS THE LOOP ****
      * count is the counter for the number of steps
@@ -2430,7 +2434,8 @@ void LegacySimulator::do_steep()
         bool validStep = true;
         if (count > 0)
         {
-            validStep = do_em_step(cr, inputrec, mdatoms, s_min, stepsize, &s_min->f, s_try, constr, count);
+            validStep = do_em_step(cr, inputrec, mdatoms, s_min, stepsize,
+                                   s_min->f.view().forceWithPadding(), s_try, constr, count);
         }
 
         if (validStep)
@@ -2445,7 +2450,7 @@ void LegacySimulator::do_steep()
 
         if (MASTER(cr))
         {
-            energyOutput.printHeader(fplog, count, count);
+            EnergyOutput::printHeader(fplog, count, count);
         }
 
         if (count == 0)
@@ -2468,16 +2473,17 @@ void LegacySimulator::do_steep()
             {
                 /* Store the new (lower) energies  */
                 matrix nullBox = {};
-                energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(count), mdatoms->tmass,
-                                                 enerd, nullptr, nullptr, nullptr, nullBox, nullptr,
-                                                 nullptr, vir, pres, nullptr, mu_tot, constr);
+                energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(count),
+                                                 mdatoms->tmass, enerd, nullptr, nullptr, nullBox,
+                                                 PTCouplingArrays(), 0, nullptr, nullptr, vir, pres,
+                                                 nullptr, mu_tot, constr);
 
                 imdSession->fillEnergyRecord(count, TRUE);
 
                 const bool do_dr = do_per_step(steps_accepted, inputrec->nstdisreout);
                 const bool do_or = do_per_step(steps_accepted, inputrec->nstorireout);
                 energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), TRUE, do_dr, do_or,
-                                                   fplog, count, count, fcd, nullptr);
+                                                   fplog, count, count, fr->fcdata.get(), nullptr);
                 fflush(fplog);
             }
         }
@@ -2547,7 +2553,7 @@ void LegacySimulator::do_steep()
         }
 
         /* Send IMD energies and positions, if bIMD is TRUE. */
-        if (imdSession->run(count, TRUE, state_global->box,
+        if (imdSession->run(count, TRUE, MASTER(cr) ? state_global->box : nullptr,
                             MASTER(cr) ? state_global->x.rvec_array() : nullptr, 0)
             && MASTER(cr))
         {
@@ -2585,9 +2591,8 @@ void LegacySimulator::do_nm()
 {
     const char*         NM = "Normal Mode Analysis";
     int                 nnodes;
-    gmx_localtop_t      top;
+    gmx_localtop_t      top(top_global->ffparams);
     gmx_global_stat_t   gstat;
-    t_graph*            graph;
     tensor              vir, pres;
     rvec                mu_tot = { 0 };
     rvec*               dfdx;
@@ -2624,7 +2629,7 @@ void LegacySimulator::do_nm()
 
     /* Init em and store the local state in state_minimum */
     init_em(fplog, mdlog, NM, cr, inputrec, imdSession, pull_work, state_global, top_global,
-            &state_work, &top, nrnb, fr, &graph, mdAtoms, &gstat, vsite, constr, &shellfc);
+            &state_work, &top, nrnb, fr, mdAtoms, &gstat, vsite, constr, &shellfc);
     const bool  simulationsShareState = false;
     gmx_mdoutf* outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
@@ -2701,11 +2706,9 @@ void LegacySimulator::do_nm()
 
     /* Make evaluate_energy do a single node force calculation */
     cr->nnodes = 1;
-    EnergyEvaluator energyEvaluator{
-        fplog,      mdlog,     cr,      ms,     top_global,      &top,  inputrec,
-        imdSession, pull_work, nrnb,    wcycle, gstat,           vsite, constr,
-        fcd,        graph,     mdAtoms, fr,     runScheduleWork, enerd
-    };
+    EnergyEvaluator energyEvaluator{ fplog,    mdlog,      cr,        ms,   top_global,      &top,
+                                     inputrec, imdSession, pull_work, nrnb, wcycle,          gstat,
+                                     vsite,    constr,     mdAtoms,   fr,   runScheduleWork, enerd };
     energyEvaluator.run(&state_work, mu_tot, vir, pres, -1, TRUE);
     cr->nnodes = nnodes;
 
@@ -2735,7 +2738,7 @@ void LegacySimulator::do_nm()
     /* Steps are divided one by one over the nodes */
     bool bNS          = true;
     auto state_work_x = makeArrayRef(state_work.s.x);
-    auto state_work_f = makeArrayRef(state_work.f);
+    auto state_work_f = state_work.f.view().force();
     for (index aid = cr->nodeid; aid < ssize(atom_index); aid += nnodes)
     {
         size_t atom = atom_index[aid];
@@ -2765,12 +2768,11 @@ void LegacySimulator::do_nm()
                     /* Now is the time to relax the shells */
                     relax_shell_flexcon(fplog, cr, ms, mdrunOptions.verbose, nullptr, step, inputrec,
                                         imdSession, pull_work, bNS, force_flags, &top, constr, enerd,
-                                        fcd, state_work.s.natoms, state_work.s.x.arrayRefWithPadding(),
+                                        state_work.s.natoms, state_work.s.x.arrayRefWithPadding(),
                                         state_work.s.v.arrayRefWithPadding(), state_work.s.box,
-                                        state_work.s.lambda, &state_work.s.hist,
-                                        state_work.f.arrayRefWithPadding(), vir, mdatoms, nrnb,
-                                        wcycle, graph, shellfc, fr, runScheduleWork, t, mu_tot,
-                                        vsite, DDBalanceRegionHandler(nullptr));
+                                        state_work.s.lambda, &state_work.s.hist, &state_work.f.view(),
+                                        vir, mdatoms, nrnb, wcycle, shellfc, fr, runScheduleWork, t,
+                                        mu_tot, vsite, DDBalanceRegionHandler(nullptr));
                     bNS = false;
                     step++;
                 }
index 9ff4b3817d2ad72686140755e22c7d13a6866903..c40161d9ef31b4a88521ca77c7e75e32dd207389 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011-2019, by the GROMACS development team, led by
+ * Copyright (c) 2011-2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -166,12 +166,12 @@ static gmx_bool repl_quantity(const gmx_multisim_t* ms, struct gmx_repl_ex* re,
     gmx_bool bDiff;
     int      s;
 
-    snew(qall, ms->nsim);
+    snew(qall, ms->numSimulations_);
     qall[re->repl] = q;
-    gmx_sum_sim(ms->nsim, qall, ms);
+    gmx_sum_sim(ms->numSimulations_, qall, ms);
 
     bDiff = FALSE;
-    for (s = 1; s < ms->nsim; s++)
+    for (s = 1; s < ms->numSimulations_; s++)
     {
         if (qall[s] != qall[0])
         {
@@ -185,7 +185,7 @@ static gmx_bool repl_quantity(const gmx_multisim_t* ms, struct gmx_repl_ex* re,
         re->type = ere;
 
         snew(re->q[ere], re->nrepl);
-        for (s = 0; s < ms->nsim; s++)
+        for (s = 0; s < ms->numSimulations_; s++)
         {
             re->q[ere][s] = qall[s];
         }
@@ -208,7 +208,7 @@ gmx_repl_ex_t init_replica_exchange(FILE*                            fplog,
 
     fprintf(fplog, "\nInitializing Replica Exchange\n");
 
-    if (!isMultiSim(ms) || ms->nsim == 1)
+    if (!isMultiSim(ms) || ms->numSimulations_ == 1)
     {
         gmx_fatal(FARGS,
                   "Nothing to exchange with only one replica, maybe you forgot to set the "
@@ -235,8 +235,8 @@ gmx_repl_ex_t init_replica_exchange(FILE*                            fplog,
 
     snew(re, 1);
 
-    re->repl  = ms->sim;
-    re->nrepl = ms->nsim;
+    re->repl  = ms->simulationIndex_;
+    re->nrepl = ms->numSimulations_;
     snew(re->q, ereENDSINGLE);
 
     fprintf(fplog, "Repl  There are %d replicas:\n", re->nrepl);
@@ -507,14 +507,13 @@ static void exchange_reals(const gmx_multisim_t gmx_unused* ms, int gmx_unused b
         /*
            MPI_Sendrecv(v,  n*sizeof(real),MPI_BYTE,MSRANK(ms,b),0,
            buf,n*sizeof(real),MPI_BYTE,MSRANK(ms,b),0,
-           ms->mpi_comm_masters,MPI_STATUS_IGNORE);
+           ms->mastersComm_,MPI_STATUS_IGNORE);
          */
         {
             MPI_Request mpi_req;
 
-            MPI_Isend(v, n * sizeof(real), MPI_BYTE, MSRANK(ms, b), 0, ms->mpi_comm_masters, &mpi_req);
-            MPI_Recv(buf, n * sizeof(real), MPI_BYTE, MSRANK(ms, b), 0, ms->mpi_comm_masters,
-                     MPI_STATUS_IGNORE);
+            MPI_Isend(v, n * sizeof(real), MPI_BYTE, MSRANK(ms, b), 0, ms->mastersComm_, &mpi_req);
+            MPI_Recv(buf, n * sizeof(real), MPI_BYTE, MSRANK(ms, b), 0, ms->mastersComm_, MPI_STATUS_IGNORE);
             MPI_Wait(&mpi_req, MPI_STATUS_IGNORE);
         }
 #endif
@@ -539,13 +538,13 @@ static void exchange_doubles(const gmx_multisim_t gmx_unused* ms, int gmx_unused
         /*
            MPI_Sendrecv(v,  n*sizeof(double),MPI_BYTE,MSRANK(ms,b),0,
            buf,n*sizeof(double),MPI_BYTE,MSRANK(ms,b),0,
-           ms->mpi_comm_masters,MPI_STATUS_IGNORE);
+           ms->mastersComm_,MPI_STATUS_IGNORE);
          */
         {
             MPI_Request mpi_req;
 
-            MPI_Isend(v, n * sizeof(double), MPI_BYTE, MSRANK(ms, b), 0, ms->mpi_comm_masters, &mpi_req);
-            MPI_Recv(buf, n * sizeof(double), MPI_BYTE, MSRANK(ms, b), 0, ms->mpi_comm_masters,
+            MPI_Isend(v, n * sizeof(double), MPI_BYTE, MSRANK(ms, b), 0, ms->mastersComm_, &mpi_req);
+            MPI_Recv(buf, n * sizeof(double), MPI_BYTE, MSRANK(ms, b), 0, ms->mastersComm_,
                      MPI_STATUS_IGNORE);
             MPI_Wait(&mpi_req, MPI_STATUS_IGNORE);
         }
@@ -570,13 +569,13 @@ static void exchange_rvecs(const gmx_multisim_t gmx_unused* ms, int gmx_unused b
         /*
            MPI_Sendrecv(v[0],  n*sizeof(rvec),MPI_BYTE,MSRANK(ms,b),0,
            buf[0],n*sizeof(rvec),MPI_BYTE,MSRANK(ms,b),0,
-           ms->mpi_comm_masters,MPI_STATUS_IGNORE);
+           ms->mastersComm_,MPI_STATUS_IGNORE);
          */
         {
             MPI_Request mpi_req;
 
-            MPI_Isend(v[0], n * sizeof(rvec), MPI_BYTE, MSRANK(ms, b), 0, ms->mpi_comm_masters, &mpi_req);
-            MPI_Recv(buf[0], n * sizeof(rvec), MPI_BYTE, MSRANK(ms, b), 0, ms->mpi_comm_masters,
+            MPI_Isend(v[0], n * sizeof(rvec), MPI_BYTE, MSRANK(ms, b), 0, ms->mastersComm_, &mpi_req);
+            MPI_Recv(buf[0], n * sizeof(rvec), MPI_BYTE, MSRANK(ms, b), 0, ms->mastersComm_,
                      MPI_STATUS_IGNORE);
             MPI_Wait(&mpi_req, MPI_STATUS_IGNORE);
         }
@@ -920,8 +919,7 @@ static void test_for_replica_exchange(FILE*                 fplog,
         }
         for (i = 0; i < re->nrepl; i++)
         {
-            re->de[i][re->repl] = (enerd->enerpart_lambda[static_cast<int>(re->q[ereLAMBDA][i]) + 1]
-                                   - enerd->enerpart_lambda[0]);
+            re->de[i][re->repl] = enerd->foreignLambdaTerms.deltaH(re->q[ereLAMBDA][i]);
         }
     }
 
index f6bead1071d235eed864756416c8b2f35a7acb0e..e8cb9bdce3802fbd2af5514634c03b4cc7cbe95d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011,2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 956bd1ab1cbdd33e3ca7e8f673e5ebe4586607b9..1f0f7d40cebb1b7dab411b3ed880eb3d2b263393 100644 (file)
@@ -49,7 +49,7 @@
 #include <algorithm>
 #include <memory>
 
-#include "gromacs/awh/awh.h"
+#include "gromacs/applied_forces/awh/awh.h"
 #include "gromacs/commandline/filenm.h"
 #include "gromacs/domdec/collect.h"
 #include "gromacs/domdec/dlbtiming.h"
 #include "gromacs/domdec/mdsetup.h"
 #include "gromacs/domdec/partition.h"
 #include "gromacs/essentialdynamics/edsam.h"
-#include "gromacs/ewald/pme.h"
 #include "gromacs/ewald/pme_load_balancing.h"
+#include "gromacs/ewald/pme_pp.h"
 #include "gromacs/fileio/trxio.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
-#include "gromacs/listed_forces/manage_threading.h"
+#include "gromacs/listed_forces/listed_forces.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
@@ -80,6 +80,7 @@
 #include "gromacs/mdlib/force.h"
 #include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdlib/forcerec.h"
+#include "gromacs/mdlib/freeenergyparameters.h"
 #include "gromacs/mdlib/md_support.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/mdoutf.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/df_history.h"
 #include "gromacs/mdtypes/energyhistory.h"
-#include "gromacs/mdtypes/fcdata.h"
+#include "gromacs/mdtypes/forcebuffers.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/observableshistory.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/mimic/utilities.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pulling/pull.h"
 #include "gromacs/swap/swapcoords.h"
 #include "shellfc.h"
 
 using gmx::SimulationSignaller;
+using gmx::VirtualSitesHandler;
 
 /*! \brief Copy the state from \p rerunFrame to \p globalState and, if requested, construct vsites
  *
@@ -143,19 +144,13 @@ using gmx::SimulationSignaller;
  * \param[in,out] globalState     The global state container
  * \param[in]     constructVsites When true, vsite coordinates are constructed
  * \param[in]     vsite           Vsite setup, can be nullptr when \p constructVsites = false
- * \param[in]     idef            Topology parameters, used for constructing vsites
  * \param[in]     timeStep        Time step, used for constructing vsites
- * \param[in]     forceRec        Force record, used for constructing vsites
- * \param[in,out] graph           The molecular graph, used for constructing vsites when != nullptr
  */
-static void prepareRerunState(const t_trxframe&  rerunFrame,
-                              t_state*           globalState,
-                              bool               constructVsites,
-                              const gmx_vsite_t* vsite,
-                              const t_idef&      idef,
-                              double             timeStep,
-                              const t_forcerec&  forceRec,
-                              t_graph*           graph)
+static void prepareRerunState(const t_trxframe&          rerunFrame,
+                              t_state*                   globalState,
+                              bool                       constructVsites,
+                              const VirtualSitesHandler* vsite,
+                              double                     timeStep)
 {
     auto x      = makeArrayRef(globalState->x);
     auto rerunX = arrayRefFromArray(reinterpret_cast<gmx::RVec*>(rerunFrame.x), globalState->natoms);
@@ -166,21 +161,7 @@ static void prepareRerunState(const t_trxframe&  rerunFrame,
     {
         GMX_ASSERT(vsite, "Need valid vsite for constructing vsites");
 
-        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(nullptr, graph, forceRec.ePBC, globalState->box, globalState->x.rvec_array());
-            shift_self(graph, globalState->box, as_rvec_array(globalState->x.data()));
-        }
-        construct_vsites(vsite, globalState->x.rvec_array(), timeStep, globalState->v.rvec_array(),
-                         idef.iparams, idef.il, forceRec.ePBC, forceRec.bMolPBC, nullptr,
-                         globalState->box);
-        if (graph)
-        {
-            unshift_self(graph, globalState->box, globalState->x.rvec_array());
-        }
+        vsite->construct(globalState->x, timeStep, globalState->v, globalState->box);
     }
 }
 
@@ -192,21 +173,20 @@ void gmx::LegacySimulator::do_rerun()
     // alias to avoid a large ripple of nearly useless changes.
     // t_inputrec is being replaced by IMdpOptionsProvider, so this
     // will go away eventually.
-    t_inputrec*                 ir = inputrec;
-    int64_t                     step, step_rel;
-    double                      t, lam0[efptNR];
-    bool                        isLastStep               = false;
-    bool                        doFreeEnergyPerturbation = false;
-    unsigned int                force_flags;
-    tensor                      force_vir, shake_vir, total_vir, pres;
-    t_trxstatus*                status = nullptr;
-    rvec                        mu_tot;
-    t_trxframe                  rerun_fr;
-    gmx_localtop_t              top;
-    PaddedHostVector<gmx::RVec> f{};
-    gmx_global_stat_t           gstat;
-    t_graph*                    graph = nullptr;
-    gmx_shellfc_t*              shellfc;
+    t_inputrec*       ir = inputrec;
+    int64_t           step, step_rel;
+    double            t;
+    bool              isLastStep               = false;
+    bool              doFreeEnergyPerturbation = false;
+    unsigned int      force_flags;
+    tensor            force_vir, shake_vir, total_vir, pres;
+    t_trxstatus*      status = nullptr;
+    rvec              mu_tot;
+    t_trxframe        rerun_fr;
+    gmx_localtop_t    top(top_global->ffparams);
+    ForceBuffers      f;
+    gmx_global_stat_t gstat;
+    gmx_shellfc_t*    shellfc;
 
     double cycles;
 
@@ -292,14 +272,16 @@ void gmx::LegacySimulator::do_rerun()
     int        nstglobalcomm = 1;
     const bool bNS           = true;
 
-    ir->nstxout_compressed   = 0;
-    SimulationGroups* groups = &top_global->groups;
+    ir->nstxout_compressed         = 0;
+    const SimulationGroups* groups = &top_global->groups;
     if (ir->eI == eiMimic)
     {
-        top_global->intermolecularExclusionGroup = genQmmmIndices(*top_global);
+        auto nonConstGlobalTopology                          = const_cast<gmx_mtop_t*>(top_global);
+        nonConstGlobalTopology->intermolecularExclusionGroup = genQmmmIndices(*top_global);
     }
-
-    initialize_lambdas(fplog, *ir, MASTER(cr), &state_global->fep_state, state_global->lambda, lam0);
+    int*                fep_state = MASTER(cr) ? &state_global->fep_state : nullptr;
+    gmx::ArrayRef<real> lambda    = MASTER(cr) ? state_global->lambda : gmx::ArrayRef<real>();
+    initialize_lambdas(fplog, *ir, MASTER(cr), fep_state, lambda);
     const bool        simulationsShareState = false;
     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
                                    mdModulesNotifier, ir, top_global, oenv, wcycle,
@@ -328,8 +310,6 @@ void gmx::LegacySimulator::do_rerun()
 
     if (DOMAINDECOMP(cr))
     {
-        dd_init_local_top(*top_global, &top);
-
         stateInstance = std::make_unique<t_state>();
         state         = stateInstance.get();
         dd_init_local_state(cr->dd, state_global, state);
@@ -343,14 +323,10 @@ void gmx::LegacySimulator::do_rerun()
     else
     {
         state_change_natoms(state_global, state_global->natoms);
-        /* We need to allocate one element extra, since we might use
-         * (unaligned) 4-wide SIMD loads to access rvec entries.
-         */
-        f.resizeWithPadding(state_global->natoms);
         /* Copy the pointer to the global state */
         state = state_global;
 
-        mdAlgorithmsSetupAtomData(cr, ir, *top_global, &top, fr, &graph, mdAtoms, constr, vsite, shellfc);
+        mdAlgorithmsSetupAtomData(cr, ir, *top_global, &top, fr, &f, mdAtoms, constr, vsite, shellfc);
     }
 
     auto mdatoms = mdAtoms->mdatoms();
@@ -373,13 +349,13 @@ void gmx::LegacySimulator::do_rerun()
                  | (shouldCheckNumberOfBondedInteractions ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS : 0));
         bool   bSumEkinhOld = false;
         t_vcm* vcm          = nullptr;
-        compute_globals(gstat, cr, ir, fr, ekind, state->x.rvec_array(), state->v.rvec_array(),
-                        state->box, state->lambda[efptVDW], mdatoms, nrnb, vcm, nullptr, enerd,
-                        force_vir, shake_vir, total_vir, pres, mu_tot, constr, &nullSignaller,
-                        state->box, &totalNumberOfBondedInteractions, &bSumEkinhOld, cglo_flags);
+        compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                        makeConstArrayRef(state->v), state->box, mdatoms, nrnb, vcm, nullptr, enerd,
+                        force_vir, shake_vir, total_vir, pres, constr, &nullSignaller, state->box,
+                        &totalNumberOfBondedInteractions, &bSumEkinhOld, cglo_flags);
     }
     checkNumberOfBondedInteractions(mdlog, cr, totalNumberOfBondedInteractions, top_global, &top,
-                                    state->x.rvec_array(), state->box,
+                                    makeConstArrayRef(state->x), state->box,
                                     &shouldCheckNumberOfBondedInteractions);
 
     if (MASTER(cr))
@@ -427,7 +403,7 @@ void gmx::LegacySimulator::do_rerun()
                       rerun_fr.natoms, top_global->natoms);
         }
 
-        if (ir->ePBC != epbcNONE)
+        if (ir->pbcType != PbcType::No)
         {
             if (!rerun_fr.bBox)
             {
@@ -437,7 +413,7 @@ void gmx::LegacySimulator::do_rerun()
                           "does not contain a box, while pbc is used",
                           rerun_fr.step, rerun_fr.time);
             }
-            if (max_cutoff2(ir->ePBC, rerun_fr.box) < gmx::square(fr->rlist))
+            if (max_cutoff2(ir->pbcType, rerun_fr.box) < gmx::square(fr->rlist))
             {
                 gmx_fatal(FARGS,
                           "Rerun trajectory frame step %" PRId64
@@ -459,7 +435,7 @@ void gmx::LegacySimulator::do_rerun()
         rerun_parallel_comm(cr, &rerun_fr, &isLastStep);
     }
 
-    if (ir->ePBC != epbcNONE)
+    if (ir->pbcType != PbcType::No)
     {
         /* Set the shift vectors.
          * Necessary here when have a static box different from the tpr box.
@@ -467,6 +443,9 @@ void gmx::LegacySimulator::do_rerun()
         calc_shifts(rerun_fr.box, fr->shift_vec);
     }
 
+    step     = ir->init_step;
+    step_rel = 0;
+
     auto stopHandler = stopHandlerBuilder->getStopHandlerMD(
             compat::not_null<SimulationSignal*>(&signals[eglsSTOPCOND]), false, MASTER(cr),
             ir->nstlist, mdrunOptions.reproducible, nstglobalcomm, mdrunOptions.maximumHoursToRun,
@@ -477,9 +456,6 @@ void gmx::LegacySimulator::do_rerun()
 
     const DDBalanceRegionHandler ddBalanceRegionHandler(cr);
 
-    step     = ir->init_step;
-    step_rel = 0;
-
     /* and stop now if we should */
     isLastStep = (isLastStep || (ir->nsteps >= 0 && step_rel > ir->nsteps));
     while (!isLastStep)
@@ -502,7 +478,19 @@ void gmx::LegacySimulator::do_rerun()
 
         if (ir->efep != efepNO && MASTER(cr))
         {
-            setCurrentLambdasRerun(step, ir->fepvals, &rerun_fr, lam0, state_global);
+            if (rerun_fr.bLambda)
+            {
+                ir->fepvals->init_lambda = rerun_fr.lambda;
+            }
+            else
+            {
+                if (rerun_fr.bFepState)
+                {
+                    state->fep_state = rerun_fr.fep_state;
+                }
+            }
+
+            state_global->lambda = currentLambdas(step, *(ir->fepvals), state->fep_state);
         }
 
         if (MASTER(cr))
@@ -515,8 +503,7 @@ void gmx::LegacySimulator::do_rerun()
                           "decomposition, "
                           "use a single rank");
             }
-            prepareRerunState(rerun_fr, state_global, constructVsites, vsite, top.idef, ir->delta_t,
-                              *fr, graph);
+            prepareRerunState(rerun_fr, state_global, constructVsites, vsite, ir->delta_t);
         }
 
         isLastStep = isLastStep || stopHandler->stoppingAfterCurrentStep(bNS);
@@ -533,7 +520,7 @@ void gmx::LegacySimulator::do_rerun()
 
         if (MASTER(cr))
         {
-            energyOutput.printHeader(fplog, step, t); /* can we improve the information printed here? */
+            EnergyOutput::printHeader(fplog, step, t); /* can we improve the information printed here? */
         }
 
         if (ir->efep != efepNO)
@@ -549,11 +536,11 @@ void gmx::LegacySimulator::do_rerun()
         {
             /* Now is the time to relax the shells */
             relax_shell_flexcon(fplog, cr, ms, mdrunOptions.verbose, enforcedRotation, step, ir,
-                                imdSession, pull_work, bNS, force_flags, &top, constr, enerd, fcd,
+                                imdSession, pull_work, bNS, force_flags, &top, constr, enerd,
                                 state->natoms, state->x.arrayRefWithPadding(),
-                                state->v.arrayRefWithPadding(), state->box, state->lambda, &state->hist,
-                                f.arrayRefWithPadding(), force_vir, mdatoms, nrnb, wcycle, graph,
-                                shellfc, fr, runScheduleWork, t, mu_tot, vsite, ddBalanceRegionHandler);
+                                state->v.arrayRefWithPadding(), state->box, state->lambda,
+                                &state->hist, &f.view(), force_vir, mdatoms, nrnb, wcycle, shellfc,
+                                fr, runScheduleWork, t, mu_tot, vsite, ddBalanceRegionHandler);
         }
         else
         {
@@ -566,9 +553,8 @@ void gmx::LegacySimulator::do_rerun()
             gmx_edsam* ed  = nullptr;
             do_force(fplog, cr, ms, ir, awh, enforcedRotation, imdSession, pull_work, step, nrnb,
                      wcycle, &top, state->box, state->x.arrayRefWithPadding(), &state->hist,
-                     f.arrayRefWithPadding(), force_vir, mdatoms, enerd, fcd, state->lambda, graph,
-                     fr, runScheduleWork, vsite, mu_tot, t, ed, GMX_FORCE_NS | force_flags,
-                     ddBalanceRegionHandler);
+                     &f.view(), force_vir, mdatoms, enerd, state->lambda, fr, runScheduleWork,
+                     vsite, mu_tot, t, ed, GMX_FORCE_NS | force_flags, ddBalanceRegionHandler);
         }
 
         /* Now we have the energies and forces corresponding to the
@@ -580,33 +566,16 @@ void gmx::LegacySimulator::do_rerun()
             const bool bSumEkinhOld        = false;
             do_md_trajectory_writing(fplog, cr, nfile, fnm, step, step_rel, t, ir, state,
                                      state_global, observablesHistory, top_global, fr, outf,
-                                     energyOutput, ekind, f, isCheckpointingStep, doRerun,
-                                     isLastStep, mdrunOptions.writeConfout, bSumEkinhOld);
+                                     energyOutput, ekind, f.view().force(), isCheckpointingStep,
+                                     doRerun, isLastStep, mdrunOptions.writeConfout, bSumEkinhOld);
         }
 
         stopHandler->setSignal();
 
-        if (graph)
-        {
-            /* Need to unshift here */
-            unshift_self(graph, state->box, as_rvec_array(state->x.data()));
-        }
-
         if (vsite != nullptr)
         {
             wallcycle_start(wcycle, ewcVSITECONSTR);
-            if (graph != nullptr)
-            {
-                shift_self(graph, state->box, as_rvec_array(state->x.data()));
-            }
-            construct_vsites(vsite, as_rvec_array(state->x.data()), ir->delta_t,
-                             as_rvec_array(state->v.data()), top.idef.iparams, top.idef.il,
-                             fr->ePBC, fr->bMolPBC, cr, state->box);
-
-            if (graph != nullptr)
-            {
-                unshift_self(graph, state->box, as_rvec_array(state->x.data()));
-            }
+            vsite->construct(state->x, ir->delta_t, state->v, state->box);
             wallcycle_stop(wcycle, ewcVSITECONSTR);
         }
 
@@ -617,15 +586,15 @@ void gmx::LegacySimulator::do_rerun()
             t_vcm*              vcm              = nullptr;
             SimulationSignaller signaller(&signals, cr, ms, doInterSimSignal, doIntraSimSignal);
 
-            compute_globals(gstat, cr, ir, fr, ekind, state->x.rvec_array(), state->v.rvec_array(),
-                            state->box, state->lambda[efptVDW], mdatoms, nrnb, vcm, wcycle, enerd,
-                            force_vir, shake_vir, total_vir, pres, mu_tot, constr, &signaller,
+            compute_globals(gstat, cr, ir, fr, ekind, makeConstArrayRef(state->x),
+                            makeConstArrayRef(state->v), state->box, mdatoms, nrnb, vcm, wcycle,
+                            enerd, force_vir, shake_vir, total_vir, pres, constr, &signaller,
                             state->box, &totalNumberOfBondedInteractions, &bSumEkinhOld,
                             CGLO_GSTAT | CGLO_ENERGY
                                     | (shouldCheckNumberOfBondedInteractions ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS
                                                                              : 0));
             checkNumberOfBondedInteractions(mdlog, cr, totalNumberOfBondedInteractions, top_global,
-                                            &top, state->x.rvec_array(), state->box,
+                                            &top, makeConstArrayRef(state->x), state->box,
                                             &shouldCheckNumberOfBondedInteractions);
         }
 
@@ -634,21 +603,16 @@ void gmx::LegacySimulator::do_rerun()
            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 (ir->efep != efepNO)
-        {
-            /* Sum up the foreign energy and dhdl terms for md and sd.
-               Currently done every step so that dhdl is correct in the .edr */
-            sum_dhdl(enerd, state->lambda, *ir->fepvals);
-        }
-
         /* Output stuff */
         if (MASTER(cr))
         {
             const bool bCalcEnerStep = true;
-            energyOutput.addDataAtEnergyStep(doFreeEnergyPerturbation, bCalcEnerStep, t,
-                                             mdatoms->tmass, enerd, state, ir->fepvals,
-                                             ir->expandedvals, state->box, shake_vir, force_vir,
-                                             total_vir, pres, ekind, mu_tot, constr);
+            energyOutput.addDataAtEnergyStep(
+                    doFreeEnergyPerturbation, bCalcEnerStep, t, mdatoms->tmass, enerd, ir->fepvals,
+                    ir->expandedvals, state->box,
+                    PTCouplingArrays({ state->boxv, state->nosehoover_xi, state->nosehoover_vxi,
+                                       state->nhpres_xi, state->nhpres_vxi }),
+                    state->fep_state, shake_vir, force_vir, total_vir, pres, ekind, mu_tot, constr);
 
             const bool do_ene = true;
             const bool do_log = true;
@@ -656,9 +620,9 @@ void gmx::LegacySimulator::do_rerun()
             const bool do_dr  = ir->nstdisreout != 0;
             const bool do_or  = ir->nstorireout != 0;
 
-            energyOutput.printAnnealingTemperatures(do_log ? fplog : nullptr, groups, &(ir->opts));
+            EnergyOutput::printAnnealingTemperatures(do_log ? fplog : nullptr, groups, &(ir->opts));
             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), do_ene, do_dr, do_or,
-                                               do_log ? fplog : nullptr, step, t, fcd, awh);
+                                               do_log ? fplog : nullptr, step, t, fr->fcdata.get(), awh);
 
             if (do_per_step(step, ir->nstlog))
             {
index 3c9def3eaa786fd032bbfecbde701cb33171121e..90275f50a3605c562a59e205c8c6ad41f81eae91 100644 (file)
@@ -64,8 +64,8 @@
 #include "gromacs/domdec/localatomsetmanager.h"
 #include "gromacs/domdec/partition.h"
 #include "gromacs/ewald/ewald_utils.h"
-#include "gromacs/ewald/pme.h"
 #include "gromacs/ewald/pme_gpu_program.h"
+#include "gromacs/ewald/pme_only.h"
 #include "gromacs/ewald/pme_pp_comm_gpu.h"
 #include "gromacs/fileio/checkpoint.h"
 #include "gromacs/fileio/gmxfio.h"
 #include "gromacs/fileio/tpxio.h"
 #include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
-#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/gpu_utils/device_stream_manager.h"
 #include "gromacs/hardware/cpuinfo.h"
 #include "gromacs/hardware/detecthardware.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/hardware/printhardware.h"
 #include "gromacs/imd/imd.h"
 #include "gromacs/listed_forces/disre.h"
 #include "gromacs/listed_forces/gpubonded.h"
+#include "gromacs/listed_forces/listed_forces.h"
 #include "gromacs/listed_forces/orires.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/utilities.h"
 #include "gromacs/mdlib/force.h"
 #include "gromacs/mdlib/forcerec.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
+#include "gromacs/mdlib/gpuforcereduction.h"
 #include "gromacs/mdlib/makeconstraints.h"
 #include "gromacs/mdlib/md_support.h"
 #include "gromacs/mdlib/mdatoms.h"
-#include "gromacs/mdlib/membed.h"
-#include "gromacs/mdlib/qmmm.h"
 #include "gromacs/mdlib/sighandler.h"
 #include "gromacs/mdlib/stophandler.h"
+#include "gromacs/mdlib/tgroup.h"
 #include "gromacs/mdlib/updategroups.h"
+#include "gromacs/mdlib/vsite.h"
 #include "gromacs/mdrun/mdmodules.h"
 #include "gromacs/mdrun/simulationcontext.h"
+#include "gromacs/mdrun/simulationinput.h"
+#include "gromacs/mdrun/simulationinputhandle.h"
 #include "gromacs/mdrunutility/handlerestart.h"
 #include "gromacs/mdrunutility/logging.h"
 #include "gromacs/mdrunutility/multisim.h"
 #include "gromacs/mdrunutility/printtime.h"
 #include "gromacs/mdrunutility/threadaffinity.h"
+#include "gromacs/mdtypes/checkpointdata.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/fcdata.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/mdrunoptions.h"
 #include "gromacs/mdtypes/observableshistory.h"
 #include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/mdtypes/state_propagator_data_gpu.h"
+#include "gromacs/modularsimulator/modularsimulator.h"
 #include "gromacs/nbnxm/gpu_data_mgmt.h"
 #include "gromacs/nbnxm/nbnxm.h"
 #include "gromacs/nbnxm/pairlist_tuning.h"
 #include "gromacs/utility/stringutil.h"
 
 #include "isimulator.h"
+#include "membedholder.h"
 #include "replicaexchange.h"
 #include "simulatorbuilder.h"
 
@@ -190,13 +201,14 @@ static DevelopmentFeatureFlags manageDevelopmentFeatures(const gmx::MDLogger& md
     // getenv results are ignored when clearly they are used.
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-result"
-    devFlags.enableGpuBufferOps = (getenv("GMX_USE_GPU_BUFFER_OPS") != nullptr)
-                                  && (GMX_GPU == GMX_GPU_CUDA) && useGpuForNonbonded;
+
+    devFlags.enableGpuBufferOps =
+            GMX_GPU_CUDA && useGpuForNonbonded && (getenv("GMX_USE_GPU_BUFFER_OPS") != nullptr);
+    devFlags.enableGpuHaloExchange = GMX_GPU_CUDA && GMX_THREAD_MPI && getenv("GMX_GPU_DD_COMMS") != nullptr;
     devFlags.forceGpuUpdateDefault = (getenv("GMX_FORCE_UPDATE_DEFAULT_GPU") != nullptr) || GMX_FAHCORE;
-    devFlags.enableGpuHaloExchange =
-            (getenv("GMX_GPU_DD_COMMS") != nullptr && GMX_THREAD_MPI && (GMX_GPU == GMX_GPU_CUDA));
     devFlags.enableGpuPmePPComm =
-            (getenv("GMX_GPU_PME_PP_COMMS") != nullptr && GMX_THREAD_MPI && (GMX_GPU == GMX_GPU_CUDA));
+            GMX_GPU_CUDA && GMX_THREAD_MPI && getenv("GMX_GPU_PME_PP_COMMS") != nullptr;
+
 #pragma GCC diagnostic pop
 
     if (devFlags.enableGpuBufferOps)
@@ -234,7 +246,8 @@ static DevelopmentFeatureFlags manageDevelopmentFeatures(const gmx::MDLogger& md
             GMX_LOG(mdlog.warning)
                     .asParagraph()
                     .appendTextFormatted(
-                            "This run uses the 'GPU halo exchange' feature, enabled by the "
+                            "This run has requested the 'GPU halo exchange' feature, enabled by "
+                            "the "
                             "GMX_GPU_DD_COMMS environment variable.");
         }
         else
@@ -253,6 +266,15 @@ static DevelopmentFeatureFlags manageDevelopmentFeatures(const gmx::MDLogger& md
     {
         if (pmeRunMode == PmeRunMode::GPU)
         {
+            if (!devFlags.enableGpuBufferOps)
+            {
+                GMX_LOG(mdlog.warning)
+                        .asParagraph()
+                        .appendTextFormatted(
+                                "Enabling GPU buffer operations required by GMX_GPU_PME_PP_COMMS "
+                                "(equivalent with GMX_USE_GPU_BUFFER_OPS=1).");
+                devFlags.enableGpuBufferOps = true;
+            }
             GMX_LOG(mdlog.warning)
                     .asParagraph()
                     .appendTextFormatted(
@@ -309,7 +331,7 @@ Mdrunner Mdrunner::cloneOnSpawnedThread() const
 
     // Copy members of master runner.
     // \todo Replace with builder when Simulation context and/or runner phases are better defined.
-    // Ref https://redmine.gromacs.org/issues/2587 and https://redmine.gromacs.org/issues/2375
+    // Ref https://gitlab.com/gromacs/gromacs/-/issues/2587 and https://gitlab.com/gromacs/gromacs/-/issues/2375
     newRunner.hw_opt    = hw_opt;
     newRunner.filenames = filenames;
 
@@ -326,10 +348,12 @@ Mdrunner Mdrunner::cloneOnSpawnedThread() const
     newRunner.pforce          = pforce;
     // Give the spawned thread the newly created valid communicator
     // for the simulation.
-    newRunner.communicator        = MPI_COMM_WORLD;
-    newRunner.ms                  = ms;
-    newRunner.startingBehavior    = startingBehavior;
-    newRunner.stopHandlerBuilder_ = std::make_unique<StopHandlerBuilder>(*stopHandlerBuilder_);
+    newRunner.libraryWorldCommunicator = MPI_COMM_WORLD;
+    newRunner.simulationCommunicator   = MPI_COMM_WORLD;
+    newRunner.ms                       = ms;
+    newRunner.startingBehavior         = startingBehavior;
+    newRunner.stopHandlerBuilder_      = std::make_unique<StopHandlerBuilder>(*stopHandlerBuilder_);
+    newRunner.inputHolder_             = inputHolder_;
 
     threadMpiMdrunnerAccessBarrier();
 
@@ -371,7 +395,8 @@ void Mdrunner::spawnThreads(int numThreadsToLaunch)
 
     // Give the master thread the newly created valid communicator for
     // the simulation.
-    communicator = MPI_COMM_WORLD;
+    libraryWorldCommunicator = MPI_COMM_WORLD;
+    simulationCommunicator   = MPI_COMM_WORLD;
     threadMpiMdrunnerAccessBarrier();
 #else
     GMX_UNUSED_VALUE(numThreadsToLaunch);
@@ -391,6 +416,18 @@ static void prepare_verlet_scheme(FILE*               fplog,
                                   bool                makeGpuPairList,
                                   const gmx::CpuInfo& cpuinfo)
 {
+    // We checked the cut-offs in grompp, but double-check here.
+    // We have PME+LJcutoff kernels for rcoulomb>rvdw.
+    if (EEL_PME_EWALD(ir->coulombtype) && ir->vdwtype == eelCUT)
+    {
+        GMX_RELEASE_ASSERT(ir->rcoulomb >= ir->rvdw,
+                           "With Verlet lists and PME we should have rcoulomb>=rvdw");
+    }
+    else
+    {
+        GMX_RELEASE_ASSERT(ir->rcoulomb == ir->rvdw,
+                           "With Verlet lists and no PME rcoulomb and rvdw should be identical");
+    }
     /* For NVE simulations, we will retain the initial list buffer */
     if (EI_DYNAMICS(ir->eI) && ir->verletbuf_tol > 0 && !(EI_MD(ir->eI) && ir->etc == etcNO))
     {
@@ -681,14 +718,13 @@ int Mdrunner::mdrunner()
 {
     matrix                    box;
     t_forcerec*               fr               = nullptr;
-    t_fcdata*                 fcd              = nullptr;
     real                      ewaldcoeff_q     = 0;
     real                      ewaldcoeff_lj    = 0;
     int                       nChargePerturbed = -1, nTypePerturbed = 0;
     gmx_wallcycle_t           wcycle;
     gmx_walltime_accounting_t walltime_accounting = nullptr;
-    gmx_membed_t*             membed              = nullptr;
-    gmx_hw_info_t*            hwinfo              = nullptr;
+    MembedHolder              membedHolder(filenames.size(), filenames.data());
+    gmx_hw_info_t*            hwinfo = nullptr;
 
     /* CAUTION: threads may be started later on in this function, so
        cr doesn't reflect the final parallel state right now */
@@ -696,7 +732,6 @@ int Mdrunner::mdrunner()
 
     /* TODO: inputrec should tell us whether we use an algorithm, not a file option */
     const bool doEssentialDynamics = opt2bSet("-ei", filenames.size(), filenames.data());
-    const bool doMembed            = opt2bSet("-membed", filenames.size(), filenames.data());
     const bool doRerun             = mdrunOptions.rerun;
 
     // Handle task-assignment related user options.
@@ -724,7 +759,7 @@ int Mdrunner::mdrunner()
     {
         fplog = gmx_fio_getfp(logFileHandle);
     }
-    const bool       isSimulationMasterRank = findIsSimulationMasterRank(ms, communicator);
+    const bool isSimulationMasterRank = findIsSimulationMasterRank(ms, simulationCommunicator);
     gmx::LoggerOwner logOwner(buildLogger(fplog, isSimulationMasterRank));
     gmx::MDLogger    mdlog(logOwner.logger());
 
@@ -736,39 +771,41 @@ int Mdrunner::mdrunner()
     // this is expressed, e.g. by expressly running detection only the
     // master rank for thread-MPI, rather than relying on the mutex
     // and reference count.
-    PhysicalNodeCommunicator physicalNodeComm(communicator, gmx_physicalnode_id_hash());
+    PhysicalNodeCommunicator physicalNodeComm(libraryWorldCommunicator, gmx_physicalnode_id_hash());
     hwinfo = gmx_detect_hardware(mdlog, physicalNodeComm);
 
     gmx_print_detected_hardware(fplog, isSimulationMasterRank && isMasterSim(ms), mdlog, hwinfo);
 
-    std::vector<int> gpuIdsToUse = makeGpuIdsToUse(hwinfo->gpu_info, hw_opt.gpuIdsAvailable);
+    std::vector<int> gpuIdsToUse = makeGpuIdsToUse(hwinfo->deviceInfoList, hw_opt.gpuIdsAvailable);
+    const int        numDevicesToUse = gmx::ssize(gpuIdsToUse);
 
     // Print citation requests after all software/hardware printing
     pleaseCiteGromacs(fplog);
 
-    // TODO Replace this by unique_ptr once t_inputrec is C++
-    t_inputrec               inputrecInstance;
-    t_inputrec*              inputrec = nullptr;
-    std::unique_ptr<t_state> globalState;
+    // Note: legacy program logic relies on checking whether these pointers are assigned.
+    // Objects may or may not be allocated later.
+    std::unique_ptr<t_inputrec> inputrec;
+    std::unique_ptr<t_state>    globalState;
 
     auto partialDeserializedTpr = std::make_unique<PartialDeserializedTprFile>();
 
     if (isSimulationMasterRank)
     {
+        // Allocate objects to be initialized by later function calls.
         /* Only the master rank has the global state */
         globalState = std::make_unique<t_state>();
+        inputrec    = std::make_unique<t_inputrec>();
 
         /* Read (nearly) all data required for the simulation
          * and keep the partly serialized tpr contents to send to other ranks later
          */
-        *partialDeserializedTpr = read_tpx_state(ftp2fn(efTPR, filenames.size(), filenames.data()),
-                                                 &inputrecInstance, globalState.get(), &mtop);
-        inputrec                = &inputrecInstance;
+        applyGlobalSimulationState(*inputHolder_.get(), partialDeserializedTpr.get(),
+                                   globalState.get(), inputrec.get(), &mtop);
     }
 
     /* Check and update the hardware options for internal consistency */
     checkAndUpdateHardwareOptions(mdlog, &hw_opt, isSimulationMasterRank, domdecOptions.numPmeRanks,
-                                  inputrec);
+                                  inputrec.get());
 
     if (GMX_THREAD_MPI && isSimulationMasterRank)
     {
@@ -783,13 +820,13 @@ int Mdrunner::mdrunner()
             // the number of GPUs to choose the number of ranks.
             auto canUseGpuForNonbonded = buildSupportsNonbondedOnGpu(nullptr);
             useGpuForNonbonded         = decideWhetherToUseGpusForNonbondedWithThreadMpi(
-                    nonbondedTarget, gpuIdsToUse, userGpuTaskAssignment, emulateGpuNonbonded,
+                    nonbondedTarget, numDevicesToUse, userGpuTaskAssignment, emulateGpuNonbonded,
                     canUseGpuForNonbonded,
                     gpuAccelerationOfNonbondedIsUseful(mdlog, *inputrec, GMX_THREAD_MPI),
                     hw_opt.nthreads_tmpi);
             useGpuForPme = decideWhetherToUseGpusForPmeWithThreadMpi(
-                    useGpuForNonbonded, pmeTarget, gpuIdsToUse, userGpuTaskAssignment, *hwinfo,
-                    *inputrec, mtop, hw_opt.nthreads_tmpi, domdecOptions.numPmeRanks);
+                    useGpuForNonbonded, pmeTarget, numDevicesToUse, userGpuTaskAssignment, *hwinfo,
+                    *inputrec, hw_opt.nthreads_tmpi, domdecOptions.numPmeRanks);
         }
         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
 
@@ -798,18 +835,21 @@ int Mdrunner::mdrunner()
          * TODO Over-writing the user-supplied value here does
          * prevent any possible subsequent checks from working
          * correctly. */
-        hw_opt.nthreads_tmpi = get_nthreads_mpi(hwinfo, &hw_opt, gpuIdsToUse, useGpuForNonbonded,
-                                                useGpuForPme, inputrec, &mtop, mdlog, doMembed);
+        hw_opt.nthreads_tmpi =
+                get_nthreads_mpi(hwinfo, &hw_opt, numDevicesToUse, useGpuForNonbonded, useGpuForPme,
+                                 inputrec.get(), &mtop, mdlog, membedHolder.doMembed());
 
         // Now start the threads for thread MPI.
         spawnThreads(hw_opt.nthreads_tmpi);
         // The spawned threads enter mdrunner() and execution of
         // master and spawned threads joins at the end of this block.
-        physicalNodeComm = PhysicalNodeCommunicator(communicator, gmx_physicalnode_id_hash());
+        physicalNodeComm =
+                PhysicalNodeCommunicator(libraryWorldCommunicator, gmx_physicalnode_id_hash());
     }
 
-    GMX_RELEASE_ASSERT(communicator == MPI_COMM_WORLD, "Must have valid world communicator");
-    CommrecHandle crHandle = init_commrec(communicator, ms);
+    GMX_RELEASE_ASSERT(ms || simulationCommunicator != MPI_COMM_NULL,
+                       "Must have valid communicator unless running a multi-simulation");
+    CommrecHandle crHandle = init_commrec(simulationCommunicator);
     t_commrec*    cr       = crHandle.get();
     GMX_RELEASE_ASSERT(cr != nullptr, "Must have valid commrec");
 
@@ -818,9 +858,12 @@ int Mdrunner::mdrunner()
         /* now broadcast everything to the non-master nodes/threads: */
         if (!isSimulationMasterRank)
         {
-            inputrec = &inputrecInstance;
+            // Until now, only the master rank has a non-null pointer.
+            // On non-master ranks, allocate the object that will receive data in the following call.
+            inputrec = std::make_unique<t_inputrec>();
         }
-        init_parallel(cr, inputrec, &mtop, partialDeserializedTpr.get());
+        init_parallel(cr->mpiDefaultCommunicator, MASTER(cr), inputrec.get(), &mtop,
+                      partialDeserializedTpr.get());
     }
     GMX_RELEASE_ASSERT(inputrec != nullptr, "All ranks should have a valid inputrec now");
     partialDeserializedTpr.reset(nullptr);
@@ -829,9 +872,6 @@ int Mdrunner::mdrunner()
     // the inputrec read by the master rank. The ranks can now all run
     // the task-deciding functions and will agree on the result
     // without needing to communicate.
-    //
-    // TODO Should we do the communication in debug mode to support
-    // having an assertion?
     const bool useDomainDecomposition = (PAR(cr) && !(EI_TPI(inputrec->eI) || inputrec->eI == eiNM));
 
     // Note that these variables describe only their own node.
@@ -855,8 +895,8 @@ int Mdrunner::mdrunner()
                 nonbondedTarget, userGpuTaskAssignment, emulateGpuNonbonded, canUseGpuForNonbonded,
                 gpuAccelerationOfNonbondedIsUseful(mdlog, *inputrec, !GMX_THREAD_MPI), gpusWereDetected);
         useGpuForPme = decideWhetherToUseGpusForPme(
-                useGpuForNonbonded, pmeTarget, userGpuTaskAssignment, *hwinfo, *inputrec, mtop,
-                cr->nnodes, domdecOptions.numPmeRanks, gpusWereDetected);
+                useGpuForNonbonded, pmeTarget, userGpuTaskAssignment, *hwinfo, *inputrec,
+                cr->sizeOfDefaultCommunicator, domdecOptions.numPmeRanks, gpusWereDetected);
         auto canUseGpuForBonded = buildSupportsGpuBondeds(nullptr)
                                   && inputSupportsGpuBondeds(*inputrec, mtop, nullptr);
         useGpuForBonded = decideWhetherToUseGpusForBonded(
@@ -873,10 +913,9 @@ int Mdrunner::mdrunner()
     const DevelopmentFeatureFlags devFlags =
             manageDevelopmentFeatures(mdlog, useGpuForNonbonded, pmeRunMode);
 
-    const bool inputIsCompatibleWithModularSimulator = ModularSimulator::isInputCompatible(
-            false, inputrec, doRerun, mtop, ms, replExParams, nullptr, doEssentialDynamics, doMembed);
-    const bool useModularSimulator = inputIsCompatibleWithModularSimulator
-                                     && !(getenv("GMX_DISABLE_MODULAR_SIMULATOR") != nullptr);
+    const bool useModularSimulator =
+            checkUseModularSimulator(false, inputrec.get(), doRerun, mtop, ms, replExParams,
+                                     nullptr, doEssentialDynamics, membedHolder.doMembed());
 
     // Build restraints.
     // TODO: hide restraint implementation details from Mdrunner.
@@ -892,7 +931,9 @@ int Mdrunner::mdrunner()
 
     // TODO: Error handling
     mdModules_->assignOptionsToModules(*inputrec->params, nullptr);
-    const auto& mdModulesNotifier = mdModules_->notifier().notifier_;
+    // now that the MdModules know their options, they know which callbacks to sign up to
+    mdModules_->subscribeToSimulationSetupNotifications();
+    const auto& mdModulesNotifier = mdModules_->notifier().simulationSetupNotifications_;
 
     if (inputrec->internalParameters != nullptr)
     {
@@ -901,7 +942,7 @@ int Mdrunner::mdrunner()
 
     if (fplog != nullptr)
     {
-        pr_inputrec(fplog, 0, "Input Parameters", inputrec, FALSE);
+        pr_inputrec(fplog, 0, "Input Parameters", inputrec.get(), FALSE);
         fprintf(fplog, "\n");
     }
 
@@ -924,7 +965,7 @@ int Mdrunner::mdrunner()
         }
 
         /* now make sure the state is initialized and propagated */
-        set_state_entries(globalState.get(), inputrec, useModularSimulator);
+        set_state_entries(globalState.get(), inputrec.get(), useModularSimulator);
     }
 
     /* NM and TPI parallelize over force/energy calculations, not atoms,
@@ -936,7 +977,8 @@ int Mdrunner::mdrunner()
         {
             globalState = std::make_unique<t_state>();
         }
-        broadcastStateWithoutDynamics(cr, globalState.get());
+        broadcastStateWithoutDynamics(cr->mpiDefaultCommunicator, DOMAINDECOMP(cr), PAR(cr),
+                                      globalState.get());
     }
 
     /* A parallel command line option consistency check that we can
@@ -970,7 +1012,7 @@ int Mdrunner::mdrunner()
     {
         if (domdecOptions.numPmeRanks > 0)
         {
-            gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
+            gmx_fatal_collective(FARGS, cr->mpiDefaultCommunicator, MASTER(cr),
                                  "PME-only ranks are requested, but the system does not use PME "
                                  "for electrostatics or LJ");
         }
@@ -1007,14 +1049,22 @@ int Mdrunner::mdrunner()
      * 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, ms, fcd, globalState.get(), replExParams.exchangeInterval > 0);
+    t_disresdata* disresdata;
+    snew(disresdata, 1);
+    init_disres(fplog, &mtop, inputrec.get(), DisResRunMode::MDRun,
+                MASTER(cr) ? DDRole::Master : DDRole::Agent,
+                PAR(cr) ? NumRanks::Multiple : NumRanks::Single, cr->mpi_comm_mysim, ms, disresdata,
+                globalState.get(), replExParams.exchangeInterval > 0);
 
-    init_orires(fplog, &mtop, inputrec, cr, ms, globalState.get(), &(fcd->orires));
+    t_oriresdata* oriresdata;
+    snew(oriresdata, 1);
+    init_orires(fplog, &mtop, inputrec.get(), cr, ms, globalState.get(), oriresdata);
 
-    auto deform = prepareBoxDeformation(globalState->box, cr, *inputrec);
+    auto deform = prepareBoxDeformation(
+            globalState != nullptr ? globalState->box : box, MASTER(cr) ? DDRole::Master : DDRole::Agent,
+            PAR(cr) ? NumRanks::Multiple : NumRanks::Single, cr->mpi_comm_mygroup, *inputrec);
 
 #if GMX_FAHCORE
     /* We have to remember the generation's first step before reading checkpoint.
@@ -1033,6 +1083,7 @@ int Mdrunner::mdrunner()
 
     ObservablesHistory observablesHistory = {};
 
+    auto modularSimulatorCheckpointData = std::make_unique<ReadCheckpointDataHolder>();
     if (startingBehavior != StartingBehavior::NewSimulation)
     {
         /* Check if checkpoint file exists before doing continuation.
@@ -1047,9 +1098,21 @@ int Mdrunner::mdrunner()
             inputrec->nsteps = -1;
         }
 
-        load_checkpoint(opt2fn_master("-cpi", filenames.size(), filenames.data(), cr),
-                        logFileHandle, cr, domdecOptions.numCells, inputrec, globalState.get(),
-                        &observablesHistory, mdrunOptions.reproducible, mdModules_->notifier());
+        // Finish applying initial simulation state information from external sources on all ranks.
+        // Reconcile checkpoint file data with Mdrunner state established up to this point.
+        applyLocalState(*inputHolder_.get(), logFileHandle, cr, domdecOptions.numCells,
+                        inputrec.get(), globalState.get(), &observablesHistory,
+                        mdrunOptions.reproducible, mdModules_->notifier(),
+                        modularSimulatorCheckpointData.get(), useModularSimulator);
+        // TODO: (#3652) Synchronize filesystem state, SimulationInput contents, and program
+        //  invariants
+        //  on all code paths.
+        // Write checkpoint or provide hook to update SimulationInput.
+        // If there was a checkpoint file, SimulationInput contains more information
+        // than if there wasn't. At this point, we have synchronized the in-memory
+        // state with the filesystem state only for restarted simulations. We should
+        // be calling applyLocalState unconditionally and expect that the completeness
+        // of SimulationInput is not dependent on its creation method.
 
         if (startingBehavior == StartingBehavior::RestartWithAppending && logFileHandle)
         {
@@ -1079,16 +1142,16 @@ int Mdrunner::mdrunner()
                         "file field.");
     }
     /* override nsteps with value set on the commandline */
-    override_nsteps_cmdline(mdlog, mdrunOptions.numStepsCommandline, inputrec);
+    override_nsteps_cmdline(mdlog, mdrunOptions.numStepsCommandline, inputrec.get());
 
-    if (SIMMASTER(cr))
+    if (isSimulationMasterRank)
     {
         copy_mat(globalState->box, box);
     }
 
     if (PAR(cr))
     {
-        gmx_bcast(sizeof(box), box, cr);
+        gmx_bcast(sizeof(box), box, cr->mpiDefaultCommunicator);
     }
 
     if (inputrec->cutoff_scheme != ecutsVERLET)
@@ -1098,11 +1161,14 @@ int Mdrunner::mdrunner()
                   "Verlet scheme, or use an earlier version of GROMACS if necessary.");
     }
     /* Update rlist and nstlist. */
-    prepare_verlet_scheme(fplog, cr, inputrec, nstlist_cmdline, &mtop, box,
+    /* Note: prepare_verlet_scheme is calling increaseNstlist(...), which (while attempting to
+     * increase rlist) tries to check if the newly chosen value fits with the DD scheme. As this is
+     * run before any DD scheme is set up, this check is never executed. See #3334 for more details.
+     */
+    prepare_verlet_scheme(fplog, cr, inputrec.get(), nstlist_cmdline, &mtop, box,
                           useGpuForNonbonded || (emulateGpuNonbonded == EmulateGpuNonbonded::Yes),
                           *hwinfo->cpuInfo);
 
-    const bool prefer1DAnd1PulseDD = (devFlags.enableGpuHaloExchange && useGpuForNonbonded);
     // This builder is necessary while we have multi-part construction
     // of DD. Before DD is constructed, we use the existence of
     // the builder object to indicate that further construction of DD
@@ -1111,25 +1177,27 @@ int Mdrunner::mdrunner()
     if (useDomainDecomposition)
     {
         ddBuilder = std::make_unique<DomainDecompositionBuilder>(
-                mdlog, cr, domdecOptions, mdrunOptions, prefer1DAnd1PulseDD, mtop, *inputrec, box,
+                mdlog, cr, domdecOptions, mdrunOptions, mtop, *inputrec, box,
                 positionsFromStatePointer(globalState.get()));
     }
     else
     {
         /* PME, if used, is done on all nodes with 1D decomposition */
-        cr->npmenodes = 0;
-        cr->duty      = (DUTY_PP | DUTY_PME);
+        cr->nnodes     = cr->sizeOfDefaultCommunicator;
+        cr->sim_nodeid = cr->rankInDefaultCommunicator;
+        cr->nodeid     = cr->rankInDefaultCommunicator;
+        cr->npmenodes  = 0;
+        cr->duty       = (DUTY_PP | DUTY_PME);
 
-        if (inputrec->ePBC == epbcSCREW)
+        if (inputrec->pbcType == PbcType::Screw)
         {
             gmx_fatal(FARGS, "pbc=screw is only implemented with domain decomposition");
         }
     }
 
-    // Produce the task assignment for this rank.
-    GpuTaskAssignmentsBuilder gpuTaskAssignmentsBuilder;
-    GpuTaskAssignments        gpuTaskAssignments = gpuTaskAssignmentsBuilder.build(
-            gpuIdsToUse, userGpuTaskAssignment, *hwinfo, communicator, physicalNodeComm,
+    // Produce the task assignment for this rank - done after DD is constructed
+    GpuTaskAssignments gpuTaskAssignments = GpuTaskAssignmentsBuilder::build(
+            gpuIdsToUse, userGpuTaskAssignment, *hwinfo, simulationCommunicator, physicalNodeComm,
             nonbondedTarget, pmeTarget, bondedTarget, updateTarget, useGpuForNonbonded,
             useGpuForPme, thisRankHasDuty(cr, DUTY_PP),
             // TODO cr->duty & DUTY_PME should imply that a PME
@@ -1137,10 +1205,24 @@ int Mdrunner::mdrunner()
             EEL_PME(inputrec->coulombtype) && thisRankHasDuty(cr, DUTY_PME));
 
     // Get the device handles for the modules, nullptr when no task is assigned.
-    gmx_device_info_t* nonbondedDeviceInfo = gpuTaskAssignments.initNonbondedDevice(cr);
-    gmx_device_info_t* pmeDeviceInfo       = gpuTaskAssignments.initPmeDevice();
+    int                deviceId   = -1;
+    DeviceInformation* deviceInfo = gpuTaskAssignments.initDevice(&deviceId);
+
+    // timing enabling - TODO put this in gpu_utils (even though generally this is just option handling?)
+    bool useTiming = true;
 
-    // TODO Initialize GPU streams here.
+    if (GMX_GPU_CUDA)
+    {
+        /* WARNING: CUDA timings are incorrect with multiple streams.
+         *          This is the main reason why they are disabled by default.
+         */
+        // TODO: Consider turning on by default when we can detect nr of streams.
+        useTiming = (getenv("GMX_ENABLE_GPU_TIMING") != nullptr);
+    }
+    else if (GMX_GPU_OPENCL)
+    {
+        useTiming = (getenv("GMX_DISABLE_GPU_TIMING") == nullptr);
+    }
 
     // TODO Currently this is always built, yet DD partition code
     // checks if it is built before using it. Probably it should
@@ -1177,11 +1259,45 @@ int Mdrunner::mdrunner()
     const bool printHostName = (cr->nnodes > 1);
     gpuTaskAssignments.reportGpuUsage(mdlog, printHostName, useGpuForBonded, pmeRunMode, useGpuForUpdate);
 
+    const bool disableNonbondedCalculation = (getenv("GMX_NO_NONBONDED") != nullptr);
+    if (disableNonbondedCalculation)
+    {
+        /* turn off non-bonded calculations */
+        GMX_LOG(mdlog.warning)
+                .asParagraph()
+                .appendText(
+                        "Found environment variable GMX_NO_NONBONDED.\n"
+                        "Disabling nonbonded calculations.");
+    }
+
+    MdrunScheduleWorkload runScheduleWork;
+
+    bool useGpuDirectHalo = decideWhetherToUseGpuForHalo(
+            devFlags, havePPDomainDecomposition(cr), useGpuForNonbonded, useModularSimulator,
+            doRerun, EI_ENERGY_MINIMIZATION(inputrec->eI));
+
+    // Also populates the simulation constant workload description.
+    runScheduleWork.simulationWork = createSimulationWorkload(
+            *inputrec, disableNonbondedCalculation, devFlags, useGpuForNonbonded, pmeRunMode,
+            useGpuForBonded, useGpuForUpdate, useGpuDirectHalo);
+
+    std::unique_ptr<DeviceStreamManager> deviceStreamManager = nullptr;
+
+    if (deviceInfo != nullptr)
+    {
+        if (DOMAINDECOMP(cr) && thisRankHasDuty(cr, DUTY_PP))
+        {
+            dd_setup_dlb_resource_sharing(cr, deviceId);
+        }
+        deviceStreamManager = std::make_unique<DeviceStreamManager>(
+                *deviceInfo, havePPDomainDecomposition(cr), runScheduleWork.simulationWork, useTiming);
+    }
+
     // If the user chose a task assignment, give them some hints
     // where appropriate.
     if (!userGpuTaskAssignment.empty())
     {
-        gpuTaskAssignments.logPerformanceHints(mdlog, ssize(gpuIdsToUse));
+        gpuTaskAssignments.logPerformanceHints(mdlog, numDevicesToUse);
     }
 
     if (PAR(cr))
@@ -1200,7 +1316,7 @@ int Mdrunner::mdrunner()
                 .appendTextFormatted(
                         "This is simulation %d out of %d running as a composite GROMACS\n"
                         "multi-simulation job. Setup for this simulation:\n",
-                        ms->sim, ms->nsim);
+                        ms->simulationIndex_, ms->numSimulations_);
     }
     GMX_LOG(mdlog.warning)
             .appendTextFormatted("Using %d MPI %s\n", cr->nnodes,
@@ -1260,7 +1376,8 @@ int Mdrunner::mdrunner()
     // Only for DD, only master PP rank needs to perform setup, and only if thread MPI plus
     // any of the GPU communication features are active.
     if (DOMAINDECOMP(cr) && MASTER(cr) && thisRankHasDuty(cr, DUTY_PP) && GMX_THREAD_MPI
-        && (devFlags.enableGpuHaloExchange || devFlags.enableGpuPmePPComm))
+        && (runScheduleWork.simulationWork.useGpuHaloExchange
+            || runScheduleWork.simulationWork.useGpuPmePpCommunication))
     {
         setupGpuDevicePeerAccess(gpuIdsToUse, mdlog);
     }
@@ -1297,63 +1414,69 @@ int Mdrunner::mdrunner()
         /* Master synchronizes its value of reset_counters with all nodes
          * including PME only nodes */
         int64_t reset_counters = wcycle_get_reset_counters(wcycle);
-        gmx_bcast_sim(sizeof(reset_counters), &reset_counters, cr);
+        gmx_bcast(sizeof(reset_counters), &reset_counters, cr->mpi_comm_mysim);
         wcycle_set_reset_counters(wcycle, reset_counters);
     }
 
     // Membrane embedding must be initialized before we call init_forcerec()
-    if (doMembed)
-    {
-        if (MASTER(cr))
-        {
-            fprintf(stderr, "Initializing membed");
-        }
-        /* Note that membed cannot work in parallel because mtop is
-         * changed here. Fix this if we ever want to make it run with
-         * multiple ranks. */
-        membed = init_membed(fplog, filenames.size(), filenames.data(), &mtop, inputrec,
-                             globalState.get(), cr, &mdrunOptions.checkpointOptions.period);
-    }
+    membedHolder.initializeMembed(fplog, filenames.size(), filenames.data(), &mtop, inputrec.get(),
+                                  globalState.get(), cr, &mdrunOptions.checkpointOptions.period);
 
-    const bool                   thisRankHasPmeGpuTask = gpuTaskAssignments.thisRankHasPmeGpuTask();
-    std::unique_ptr<MDAtoms>     mdAtoms;
-    std::unique_ptr<gmx_vsite_t> vsite;
+    const bool               thisRankHasPmeGpuTask = gpuTaskAssignments.thisRankHasPmeGpuTask();
+    std::unique_ptr<MDAtoms> mdAtoms;
+    std::unique_ptr<VirtualSitesHandler> vsite;
+    std::unique_ptr<GpuBonded>           gpuBonded;
 
     t_nrnb nrnb;
     if (thisRankHasDuty(cr, DUTY_PP))
     {
         mdModulesNotifier.notify(*cr);
         mdModulesNotifier.notify(&atomSets);
-        mdModulesNotifier.notify(PeriodicBoundaryConditionType{ inputrec->ePBC });
+        mdModulesNotifier.notify(inputrec->pbcType);
         mdModulesNotifier.notify(SimulationTimeStep{ inputrec->delta_t });
         /* Initiate forcerecord */
         fr                 = new t_forcerec;
         fr->forceProviders = mdModules_->initForceProviders();
-        init_forcerec(fplog, mdlog, fr, fcd, inputrec, &mtop, cr, box,
+        init_forcerec(fplog, mdlog, fr, inputrec.get(), &mtop, cr, box,
                       opt2fn("-table", filenames.size(), filenames.data()),
                       opt2fn("-tablep", filenames.size(), filenames.data()),
-                      opt2fns("-tableb", filenames.size(), filenames.data()), *hwinfo,
-                      nonbondedDeviceInfo, useGpuForBonded,
-                      pmeRunMode == PmeRunMode::GPU && !thisRankHasDuty(cr, DUTY_PME), pforce, wcycle);
-
-        // TODO Move this to happen during domain decomposition setup,
-        // once stream and event handling works well with that.
-        // TODO remove need to pass local stream into GPU halo exchange - Redmine #3093
-        if (havePPDomainDecomposition(cr) && prefer1DAnd1PulseDD && is1DAnd1PulseDD(*cr->dd))
+                      opt2fns("-tableb", filenames.size(), filenames.data()), pforce);
+        // Dirty hack, for fixing disres and orires should be made mdmodules
+        fr->fcdata->disres = disresdata;
+        fr->fcdata->orires = oriresdata;
+
+        // Save a handle to device stream manager to use elsewhere in the code
+        // TODO: Forcerec is not a correct place to store it.
+        fr->deviceStreamManager = deviceStreamManager.get();
+
+        if (runScheduleWork.simulationWork.useGpuPmePpCommunication && !thisRankHasDuty(cr, DUTY_PME))
         {
-            GMX_RELEASE_ASSERT(devFlags.enableGpuBufferOps,
-                               "Must use GMX_USE_GPU_BUFFER_OPS=1 to use GMX_GPU_DD_COMMS=1");
-            void* streamLocal =
-                    Nbnxm::gpu_get_command_stream(fr->nbv->gpu_nbv, InteractionLocality::Local);
-            void* streamNonLocal =
-                    Nbnxm::gpu_get_command_stream(fr->nbv->gpu_nbv, InteractionLocality::NonLocal);
-            GMX_LOG(mdlog.warning)
-                    .asParagraph()
-                    .appendTextFormatted(
-                            "NOTE: This run uses the 'GPU halo exchange' feature, enabled by the "
-                            "GMX_GPU_DD_COMMS environment variable.");
-            cr->dd->gpuHaloExchange = std::make_unique<GpuHaloExchange>(
-                    cr->dd, cr->mpi_comm_mysim, streamLocal, streamNonLocal);
+            GMX_RELEASE_ASSERT(
+                    deviceStreamManager != nullptr,
+                    "GPU device stream manager should be valid in order to use PME-PP direct "
+                    "communications.");
+            GMX_RELEASE_ASSERT(
+                    deviceStreamManager->streamIsValid(DeviceStreamType::PmePpTransfer),
+                    "GPU PP-PME stream should be valid in order to use GPU PME-PP direct "
+                    "communications.");
+            fr->pmePpCommGpu = std::make_unique<gmx::PmePpCommGpu>(
+                    cr->mpi_comm_mysim, cr->dd->pme_nodeid, deviceStreamManager->context(),
+                    deviceStreamManager->stream(DeviceStreamType::PmePpTransfer));
+        }
+
+        fr->nbv = Nbnxm::init_nb_verlet(mdlog, inputrec.get(), fr, cr, *hwinfo,
+                                        runScheduleWork.simulationWork.useGpuNonbonded,
+                                        deviceStreamManager.get(), &mtop, box, wcycle);
+        // TODO: Move the logic below to a GPU bonded builder
+        if (runScheduleWork.simulationWork.useGpuBonded)
+        {
+            GMX_RELEASE_ASSERT(deviceStreamManager != nullptr,
+                               "GPU device stream manager should be valid in order to use GPU "
+                               "version of bonded forces.");
+            gpuBonded = std::make_unique<GpuBonded>(
+                    mtop.ffparams, fr->ic->epsfac * fr->fudgeQQ, deviceStreamManager->context(),
+                    deviceStreamManager->bondedStream(havePPDomainDecomposition(cr)), wcycle);
+            fr->gpuBonded = gpuBonded.get();
         }
 
         /* Initialize the mdAtoms structure.
@@ -1372,19 +1495,20 @@ int Mdrunner::mdrunner()
         }
 
         /* Initialize the virtual site communication */
-        vsite = initVsite(mtop, cr);
+        vsite = makeVirtualSitesHandler(mtop, cr, fr->pbcType);
 
         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))
+        if (!inputrec->bContinuation && MASTER(cr)
+            && !(inputrec->pbcType != PbcType::No && inputrec->bPeriodicMols))
         {
             /* Make molecules whole at start of run */
-            if (fr->ePBC != epbcNONE)
+            if (fr->pbcType != PbcType::No)
             {
-                do_pbc_first_mtop(fplog, inputrec->ePBC, box, &mtop, globalState->x.rvec_array());
+                do_pbc_first_mtop(fplog, inputrec->pbcType, box, &mtop, globalState->x.rvec_array());
             }
             if (vsite)
             {
@@ -1392,7 +1516,7 @@ int Mdrunner::mdrunner()
                  * for the initial distribution in the domain decomposition
                  * and for the initial shell prediction.
                  */
-                constructVsitesGlobal(mtop, globalState->x);
+                constructVirtualSitesGlobal(mtop, globalState->x);
             }
         }
 
@@ -1427,7 +1551,12 @@ int Mdrunner::mdrunner()
     PmeGpuProgramStorage pmeGpuProgram;
     if (thisRankHasPmeGpuTask)
     {
-        pmeGpuProgram = buildPmeGpuProgram(pmeDeviceInfo);
+        GMX_RELEASE_ASSERT(
+                (deviceStreamManager != nullptr),
+                "GPU device stream manager should be initialized in order to use GPU for PME.");
+        GMX_RELEASE_ASSERT((deviceInfo != nullptr),
+                           "GPU device should be initialized in order to use GPU for PME.");
+        pmeGpuProgram = buildPmeGpuProgram(deviceStreamManager->context());
     }
 
     /* Initiate PME if necessary,
@@ -1445,18 +1574,37 @@ int Mdrunner::mdrunner()
         if (cr->npmenodes > 0)
         {
             /* The PME only nodes need to know nChargePerturbed(FEP on Q) and nTypePerturbed(FEP on LJ)*/
-            gmx_bcast_sim(sizeof(nChargePerturbed), &nChargePerturbed, cr);
-            gmx_bcast_sim(sizeof(nTypePerturbed), &nTypePerturbed, cr);
+            gmx_bcast(sizeof(nChargePerturbed), &nChargePerturbed, cr->mpi_comm_mysim);
+            gmx_bcast(sizeof(nTypePerturbed), &nTypePerturbed, cr->mpi_comm_mysim);
         }
 
         if (thisRankHasDuty(cr, DUTY_PME))
         {
             try
             {
-                pmedata = gmx_pme_init(cr, getNumPmeDomains(cr->dd), inputrec, nChargePerturbed != 0,
-                                       nTypePerturbed != 0, mdrunOptions.reproducible, ewaldcoeff_q,
-                                       ewaldcoeff_lj, gmx_omp_nthreads_get(emntPME), pmeRunMode,
-                                       nullptr, pmeDeviceInfo, pmeGpuProgram.get(), mdlog);
+                // TODO: This should be in the builder.
+                GMX_RELEASE_ASSERT(!runScheduleWork.simulationWork.useGpuPme
+                                           || (deviceStreamManager != nullptr),
+                                   "Device stream manager should be valid in order to use GPU "
+                                   "version of PME.");
+                GMX_RELEASE_ASSERT(
+                        !runScheduleWork.simulationWork.useGpuPme
+                                || deviceStreamManager->streamIsValid(DeviceStreamType::Pme),
+                        "GPU PME stream should be valid in order to use GPU version of PME.");
+
+                const DeviceContext* deviceContext = runScheduleWork.simulationWork.useGpuPme
+                                                             ? &deviceStreamManager->context()
+                                                             : nullptr;
+                const DeviceStream* pmeStream =
+                        runScheduleWork.simulationWork.useGpuPme
+                                ? &deviceStreamManager->stream(DeviceStreamType::Pme)
+                                : nullptr;
+
+                pmedata = gmx_pme_init(cr, getNumPmeDomains(cr->dd), inputrec.get(),
+                                       nChargePerturbed != 0, nTypePerturbed != 0,
+                                       mdrunOptions.reproducible, ewaldcoeff_q, ewaldcoeff_lj,
+                                       gmx_omp_nthreads_get(emntPME), pmeRunMode, nullptr,
+                                       deviceContext, pmeStream, pmeGpuProgram.get(), mdlog);
             }
             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
         }
@@ -1482,7 +1630,7 @@ int Mdrunner::mdrunner()
         if (inputrec->bPull)
         {
             /* Initialize pull code */
-            pull_work = init_pull(fplog, inputrec->pull, inputrec, &mtop, cr, &atomSets,
+            pull_work = init_pull(fplog, inputrec->pull, inputrec.get(), &mtop, cr, &atomSets,
                                   inputrec->fepvals->init_lambda);
             if (inputrec->pull->bXOutAverage || inputrec->pull->bFOutAverage)
             {
@@ -1498,24 +1646,24 @@ int Mdrunner::mdrunner()
         if (inputrec->bRot)
         {
             /* Initialize enforced rotation code */
-            enforcedRotation =
-                    init_rot(fplog, inputrec, filenames.size(), filenames.data(), cr, &atomSets,
-                             globalState.get(), &mtop, oenv, mdrunOptions, startingBehavior);
+            enforcedRotation = init_rot(fplog, inputrec.get(), filenames.size(), filenames.data(),
+                                        cr, &atomSets, globalState.get(), &mtop, oenv, mdrunOptions,
+                                        startingBehavior);
         }
 
         t_swap* swap = nullptr;
         if (inputrec->eSwapCoords != eswapNO)
         {
             /* Initialize ion swapping code */
-            swap = init_swapcoords(fplog, inputrec,
+            swap = init_swapcoords(fplog, inputrec.get(),
                                    opt2fn_master("-swap", filenames.size(), filenames.data(), cr),
                                    &mtop, globalState.get(), &observablesHistory, cr, &atomSets,
                                    oenv, mdrunOptions, startingBehavior);
         }
 
         /* Let makeConstraints know whether we have essential dynamics constraints. */
-        auto constr = makeConstraints(mtop, *inputrec, pull_work, doEssentialDynamics, fplog,
-                                      *mdAtoms->mdatoms(), cr, ms, &nrnb, wcycle, fr->bMolPBC);
+        auto constr = makeConstraints(mtop, *inputrec, pull_work, doEssentialDynamics, fplog, cr,
+                                      ms, &nrnb, wcycle, fr->bMolPBC);
 
         /* Energy terms and groups */
         gmx_enerdata_t enerd(mtop.groups.groups[SimulationAtomGroupType::EnergyOutput].size(),
@@ -1527,7 +1675,7 @@ int Mdrunner::mdrunner()
 
         /* Set up interactive MD (IMD) */
         auto imdSession =
-                makeImdSession(inputrec, cr, wcycle, &enerd, ms, &mtop, mdlog,
+                makeImdSession(inputrec.get(), cr, wcycle, &enerd, ms, &mtop, mdlog,
                                MASTER(cr) ? globalState->x.rvec_array() : nullptr, filenames.size(),
                                filenames.data(), oenv, mdrunOptions.imdOptions, startingBehavior);
 
@@ -1537,58 +1685,64 @@ int Mdrunner::mdrunner()
             /* This call is not included in init_domain_decomposition mainly
              * because fr->cginfo_mb is set later.
              */
-            dd_init_bondeds(fplog, cr->dd, &mtop, vsite.get(), inputrec,
+            dd_init_bondeds(fplog, cr->dd, mtop, vsite.get(), inputrec.get(),
                             domdecOptions.checkBondedInteractions, fr->cginfo_mb);
         }
 
-        // TODO This is not the right place to manage the lifetime of
-        // this data structure, but currently it's the easiest way to
-        // make it work.
-        MdrunScheduleWorkload runScheduleWork;
-        // Also populates the simulation constant workload description.
-        runScheduleWork.simulationWork = createSimulationWorkload(
-                useGpuForNonbonded, pmeRunMode, useGpuForBonded, useGpuForUpdate,
-                devFlags.enableGpuBufferOps, devFlags.enableGpuHaloExchange,
-                devFlags.enableGpuPmePPComm, haveEwaldSurfaceContribution(*inputrec));
+        if (runScheduleWork.simulationWork.useGpuBufferOps)
+        {
+            fr->gpuForceReduction[gmx::AtomLocality::Local] = std::make_unique<gmx::GpuForceReduction>(
+                    deviceStreamManager->context(),
+                    deviceStreamManager->stream(gmx::DeviceStreamType::NonBondedLocal));
+            fr->gpuForceReduction[gmx::AtomLocality::NonLocal] = std::make_unique<gmx::GpuForceReduction>(
+                    deviceStreamManager->context(),
+                    deviceStreamManager->stream(gmx::DeviceStreamType::NonBondedNonLocal));
+        }
 
         std::unique_ptr<gmx::StatePropagatorDataGpu> stateGpu;
         if (gpusWereDetected
-            && ((useGpuForPme && thisRankHasDuty(cr, DUTY_PME))
+            && ((runScheduleWork.simulationWork.useGpuPme && thisRankHasDuty(cr, DUTY_PME))
                 || runScheduleWork.simulationWork.useGpuBufferOps))
         {
-            const void* pmeStream = pme_gpu_get_device_stream(fr->pmedata);
-            const void* localStream =
-                    fr->nbv->gpu_nbv != nullptr
-                            ? Nbnxm::gpu_get_command_stream(fr->nbv->gpu_nbv, InteractionLocality::Local)
-                            : nullptr;
-            const void* nonLocalStream =
-                    fr->nbv->gpu_nbv != nullptr
-                            ? Nbnxm::gpu_get_command_stream(fr->nbv->gpu_nbv, InteractionLocality::NonLocal)
-                            : nullptr;
-            const void*        deviceContext = pme_gpu_get_device_context(fr->pmedata);
-            const int          paddingSize   = pme_gpu_get_padding_size(fr->pmedata);
             GpuApiCallBehavior transferKind = (inputrec->eI == eiMD && !doRerun && !useModularSimulator)
                                                       ? GpuApiCallBehavior::Async
                                                       : GpuApiCallBehavior::Sync;
-
+            GMX_RELEASE_ASSERT(deviceStreamManager != nullptr,
+                               "GPU device stream manager should be initialized to use GPU.");
             stateGpu = std::make_unique<gmx::StatePropagatorDataGpu>(
-                    pmeStream, localStream, nonLocalStream, deviceContext, transferKind, paddingSize, wcycle);
+                    *deviceStreamManager, transferKind, pme_gpu_get_block_size(fr->pmedata), wcycle);
             fr->stateGpu = stateGpu.get();
         }
 
         GMX_ASSERT(stopHandlerBuilder_, "Runner must provide StopHandlerBuilder to simulator.");
         SimulatorBuilder simulatorBuilder;
 
+        simulatorBuilder.add(SimulatorStateData(globalState.get(), &observablesHistory, &enerd, &ekind));
+        simulatorBuilder.add(std::move(membedHolder));
+        simulatorBuilder.add(std::move(stopHandlerBuilder_));
+        simulatorBuilder.add(SimulatorConfig(mdrunOptions, startingBehavior, &runScheduleWork));
+
+
+        simulatorBuilder.add(SimulatorEnv(fplog, cr, ms, mdlog, oenv));
+        simulatorBuilder.add(Profiling(&nrnb, walltime_accounting, wcycle));
+        simulatorBuilder.add(ConstraintsParam(
+                constr.get(), enforcedRotation ? enforcedRotation->getLegacyEnfrot() : nullptr,
+                vsite.get()));
+        // TODO: Separate `fr` to a separate add, and make the `build` handle the coupling sensibly.
+        simulatorBuilder.add(LegacyInput(static_cast<int>(filenames.size()), filenames.data(),
+                                         inputrec.get(), fr));
+        simulatorBuilder.add(ReplicaExchangeParameters(replExParams));
+        simulatorBuilder.add(InteractiveMD(imdSession.get()));
+        simulatorBuilder.add(SimulatorModules(mdModules_->outputProvider(), mdModules_->notifier()));
+        simulatorBuilder.add(CenterOfMassPulling(pull_work));
+        // Todo move to an MDModule
+        simulatorBuilder.add(IonSwapping(swap));
+        simulatorBuilder.add(TopologyData(&mtop, mdAtoms.get()));
+        simulatorBuilder.add(BoxDeformationHandle(deform.get()));
+        simulatorBuilder.add(std::move(modularSimulatorCheckpointData));
+
         // build and run simulator object based on user-input
-        auto simulator = simulatorBuilder.build(
-                inputIsCompatibleWithModularSimulator, fplog, cr, ms, mdlog,
-                static_cast<int>(filenames.size()), filenames.data(), oenv, mdrunOptions,
-                startingBehavior, vsite.get(), constr.get(),
-                enforcedRotation ? enforcedRotation->getLegacyEnfrot() : nullptr, deform.get(),
-                mdModules_->outputProvider(), mdModules_->notifier(), inputrec, imdSession.get(),
-                pull_work, swap, &mtop, fcd, globalState.get(), &observablesHistory, mdAtoms.get(),
-                &nrnb, wcycle, fr, &enerd, &ekind, &runScheduleWork, replExParams, membed,
-                walltime_accounting, std::move(stopHandlerBuilder_), doRerun);
+        auto simulator = simulatorBuilder.build(useModularSimulator);
         simulator->run();
 
         if (fr->pmePpCommGpu)
@@ -1608,7 +1762,8 @@ int Mdrunner::mdrunner()
         GMX_RELEASE_ASSERT(pmedata, "pmedata was NULL while cr->duty was not DUTY_PP");
         /* do PME only */
         walltime_accounting = walltime_accounting_init(gmx_omp_nthreads_get(emntPME));
-        gmx_pmeonly(pmedata, cr, &nrnb, wcycle, walltime_accounting, inputrec, pmeRunMode);
+        gmx_pmeonly(pmedata, cr, &nrnb, wcycle, walltime_accounting, inputrec.get(), pmeRunMode,
+                    deviceStreamManager.get());
     }
 
     wallcycle_stop(wcycle, ewcRUN);
@@ -1616,12 +1771,13 @@ int Mdrunner::mdrunner()
     /* Finish up, write some stuff
      * if rerunMD, don't write last frame again
      */
-    finish_run(fplog, mdlog, cr, inputrec, &nrnb, wcycle, walltime_accounting,
+    finish_run(fplog, mdlog, cr, inputrec.get(), &nrnb, wcycle, walltime_accounting,
                fr ? fr->nbv.get() : nullptr, pmedata, EI_DYNAMICS(inputrec->eI) && !isMultiSim(ms));
 
     // clean up cycle counter
     wallcycle_destroy(wcycle);
 
+    deviceStreamManager.reset(nullptr);
     // Free PME data
     if (pmedata)
     {
@@ -1630,24 +1786,46 @@ int Mdrunner::mdrunner()
     }
 
     // FIXME: this is only here to manually unpin mdAtoms->chargeA_ and state->x,
-    // before we destroy the GPU context(s) in free_gpu_resources().
+    // before we destroy the GPU context(s)
     // Pinned buffers are associated with contexts in CUDA.
     // As soon as we destroy GPU contexts after mdrunner() exits, these lines should go.
     mdAtoms.reset(nullptr);
     globalState.reset(nullptr);
     mdModules_.reset(nullptr); // destruct force providers here as they might also use the GPU
+    gpuBonded.reset(nullptr);
+    /* Free pinned buffers in *fr */
+    delete fr;
+    fr = nullptr;
+    // TODO convert to C++ so we can get rid of these frees
+    sfree(disresdata);
+    sfree(oriresdata);
 
-    /* Free GPU memory and set a physical node tMPI barrier (which should eventually go away) */
-    free_gpu_resources(fr, physicalNodeComm, hwinfo->gpu_info);
-    free_gpu(nonbondedDeviceInfo);
-    free_gpu(pmeDeviceInfo);
-    done_forcerec(fr, mtop.molblock.size());
-    sfree(fcd);
+    if (!hwinfo->deviceInfoList.empty())
+    {
+        /* stop the GPU profiler (only CUDA) */
+        stopGpuProfiler();
+    }
 
-    if (doMembed)
+    /* With tMPI we need to wait for all ranks to finish deallocation before
+     * destroying the CUDA context as some tMPI ranks may be sharing
+     * GPU and context.
+     *
+     * This is not a concern in OpenCL where we use one context per rank.
+     *
+     * Note: it is safe to not call the barrier on the ranks which do not use GPU,
+     * but it is easier and more futureproof to call it on the whole node.
+     *
+     * Note that this function needs to be called even if GPUs are not used
+     * in this run because the PME ranks have no knowledge of whether GPUs
+     * are used or not, but all ranks need to enter the barrier below.
+     * \todo Remove this physical node barrier after making sure
+     * that it's not needed anymore (with a shared GPU run).
+     */
+    if (GMX_THREAD_MPI)
     {
-        free_membed(membed);
+        physicalNodeComm.barrier();
     }
+    releaseDevice(deviceInfo);
 
     /* Does what it says */
     print_date_and_time(fplog, cr->nodeid, "Finished mdrun", gmx_gettime());
@@ -1672,13 +1850,13 @@ int Mdrunner::mdrunner()
     /* we need to join all threads. The sub-threads join when they
        exit this function, but the master thread needs to be told to
        wait for that. */
-    if (PAR(cr) && MASTER(cr))
+    if (MASTER(cr))
     {
         tMPI_Finalize();
     }
 #endif
     return rc;
-}
+} // namespace gmx
 
 Mdrunner::~Mdrunner()
 {
@@ -1711,8 +1889,7 @@ Mdrunner::Mdrunner(std::unique_ptr<MDModules> mdModules) : mdModules_(std::move(
 
 Mdrunner::Mdrunner(Mdrunner&&) noexcept = default;
 
-//NOLINTNEXTLINE(performance-noexcept-move-constructor) working around GCC bug 58265
-Mdrunner& Mdrunner::operator=(Mdrunner&& /*handle*/) noexcept(BUGFREE_NOEXCEPT_STRING) = default;
+Mdrunner& Mdrunner::operator=(Mdrunner&& /*handle*/) noexcept = default;
 
 class Mdrunner::BuilderImplementation
 {
@@ -1727,6 +1904,8 @@ public:
 
     void addDomdec(const DomdecOptions& options);
 
+    void addInput(SimulationInputHandle inputHolder);
+
     void addVerletList(int nstlist);
 
     void addReplicaExchange(const ReplicaExchangeParameters& params);
@@ -1770,11 +1949,14 @@ private:
     //! Command-line override for the duration of a neighbor list with the Verlet scheme.
     int nstlist_ = 0;
 
+    //! World communicator, used for hardware detection and task assignment
+    MPI_Comm libraryWorldCommunicator_ = MPI_COMM_NULL;
+
     //! Multisim communicator handle.
     gmx_multisim_t* multiSimulation_;
 
     //! mdrun communicator
-    MPI_Comm communicator_ = MPI_COMM_NULL;
+    MPI_Comm simulationCommunicator_ = MPI_COMM_NULL;
 
     //! Print a warning if any force is larger than this (in kJ/mol nm).
     real forceWarningThreshold_ = -1;
@@ -1811,14 +1993,24 @@ private:
      * \brief Builder for simulation stop signal handler.
      */
     std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder_ = nullptr;
+
+    /*!
+     * \brief Sources for initial simulation state.
+     *
+     * See issue #3652 for near-term refinements to the SimulationInput interface.
+     *
+     * See issue #3379 for broader discussion on API aspects of simulation inputs and outputs.
+     */
+    SimulationInputHandle inputHolder_;
 };
 
 Mdrunner::BuilderImplementation::BuilderImplementation(std::unique_ptr<MDModules> mdModules,
                                                        compat::not_null<SimulationContext*> context) :
     mdModules_(std::move(mdModules))
 {
-    communicator_    = context->communicator_;
-    multiSimulation_ = context->multiSimulation_.get();
+    libraryWorldCommunicator_ = context->libraryWorldCommunicator_;
+    simulationCommunicator_   = context->simulationCommunicator_;
+    multiSimulation_          = context->multiSimulation_.get();
 }
 
 Mdrunner::BuilderImplementation::~BuilderImplementation() = default;
@@ -1868,11 +2060,22 @@ Mdrunner Mdrunner::BuilderImplementation::build()
 
     newRunner.filenames = filenames_;
 
-    newRunner.communicator = communicator_;
+    newRunner.libraryWorldCommunicator = libraryWorldCommunicator_;
+
+    newRunner.simulationCommunicator = simulationCommunicator_;
 
     // nullptr is a valid value for the multisim handle
     newRunner.ms = multiSimulation_;
 
+    if (inputHolder_)
+    {
+        newRunner.inputHolder_ = std::move(inputHolder_);
+    }
+    else
+    {
+        GMX_THROW(gmx::APIError("MdrunnerBuilder::addInput() is required before build()."));
+    }
+
     // \todo Clarify ownership and lifetime management for gmx_output_env_t
     // \todo Update sanity checking when output environment has clearly specified invariants.
     // Initialization and default values for oenv are not well specified in the current version.
@@ -1988,6 +2191,11 @@ void Mdrunner::BuilderImplementation::addStopHandlerBuilder(std::unique_ptr<Stop
     stopHandlerBuilder_ = std::move(builder);
 }
 
+void Mdrunner::BuilderImplementation::addInput(SimulationInputHandle inputHolder)
+{
+    inputHolder_ = std::move(inputHolder);
+}
+
 MdrunnerBuilder::MdrunnerBuilder(std::unique_ptr<MDModules>           mdModules,
                                  compat::not_null<SimulationContext*> context) :
     impl_{ std::make_unique<Mdrunner::BuilderImplementation>(std::move(mdModules), context) }
@@ -2092,6 +2300,12 @@ MdrunnerBuilder& MdrunnerBuilder::addStopHandlerBuilder(std::unique_ptr<StopHand
     return *this;
 }
 
+MdrunnerBuilder& MdrunnerBuilder::addInput(SimulationInputHandle input)
+{
+    impl_->addInput(std::move(input));
+    return *this;
+}
+
 MdrunnerBuilder::MdrunnerBuilder(MdrunnerBuilder&&) noexcept = default;
 
 MdrunnerBuilder& MdrunnerBuilder::operator=(MdrunnerBuilder&&) noexcept = default;
index 82eccd97d5c83264f9436fac2575b9ced1551d16..2dd38531bb86ce11037c759501ff5be5dab30a5d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/mdrun/mdmodules.h"
+#include "gromacs/mdrun/simulationinputhandle.h"
 #include "gromacs/mdrunutility/handlerestart.h"
 #include "gromacs/mdtypes/mdrunoptions.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/gmxmpi.h"
 #include "gromacs/utility/real.h"
@@ -76,9 +78,6 @@ class RestraintManager;
 class SimulationContext;
 class StopHandlerBuilder;
 
-//! Work-around for GCC bug 58265
-constexpr bool BUGFREE_NOEXCEPT_STRING = std::is_nothrow_move_assignable<std::string>::value;
-
 /*! \libinternal \brief Runner object for supporting setup and execution of mdrun.
  *
  * This class has responsibility for the lifetime of data structures
@@ -144,8 +143,7 @@ public:
      * \{
      */
     Mdrunner(Mdrunner&& handle) noexcept;
-    //NOLINTNEXTLINE(performance-noexcept-move-constructor) working around GCC bug 58265
-    Mdrunner& operator=(Mdrunner&& handle) noexcept(BUGFREE_NOEXCEPT_STRING);
+    Mdrunner& operator=(Mdrunner&& handle) noexcept;
     /* \} */
 
     /*! \brief Driver routine, that calls the different simulation methods. */
@@ -263,12 +261,19 @@ private:
     //! \brief Non-owning handle to file used for logging.
     t_fileio* logFileHandle = nullptr;
 
-    /*! \brief Non-owning handle to communication data structure.
+    /*! \brief Non-owning handle to world communication data structure for task assigment.
+     *
+     * With real MPI, gets a value from the SimulationContext
+     * supplied to the MdrunnerBuilder. With thread-MPI gets a
+     * value after threads have been spawned. */
+    MPI_Comm libraryWorldCommunicator = MPI_COMM_NULL;
+
+    /*! \brief Non-owning handle to communication data structure for the current simulation.
      *
      * With real MPI, gets a value from the SimulationContext
      * supplied to the MdrunnerBuilder. With thread-MPI gets a
      * value after threads have been spawned. */
-    MPI_Comm communicator = MPI_COMM_NULL;
+    MPI_Comm simulationCommunicator = MPI_COMM_NULL;
 
     //! \brief Non-owning handle to multi-simulation handler.
     gmx_multisim_t* ms = nullptr;
@@ -300,6 +305,14 @@ private:
     std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder_;
     //! The modules that comprise mdrun.
     std::unique_ptr<MDModules> mdModules_;
+
+    /*!
+     * \brief Holds simulation input specification provided by client, if any.
+     *
+     * If present on any instance (rank) of a simulation runner, an identical
+     * (or compatible) SimulationInput must be held on all cooperating instances.
+     */
+    SimulationInputHandle inputHolder_;
 };
 
 /*! \libinternal
@@ -588,6 +601,18 @@ public:
      */
     MdrunnerBuilder& addStopHandlerBuilder(std::unique_ptr<StopHandlerBuilder> builder);
 
+    /*!
+     * \brief Acquire a handle to the SimulationInput.
+     *
+     * Required. SimulationInput will be taking responsibility for some of the
+     * input provided through other methods, such as addFilenames.
+     *
+     * See also issue https://gitlab.com/gromacs/gromacs/-/issues/3374
+     *
+     * \param input Shared ownership of a SimulationInput.
+     */
+    MdrunnerBuilder& addInput(SimulationInputHandle input);
+
     ~MdrunnerBuilder();
 
 private:
index 1b87331b8f862aaa12d797ead634421e77da57de..a2c8b86f0863def4d3b6f4cf685e00ad4b7c6a4b 100644 (file)
 #include "gromacs/mdlib/vsite.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/enerdata.h"
+#include "gromacs/mdtypes/forcebuffers.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/state.h"
-#include "gromacs/pbcutil/mshift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/topology/mtop_lookup.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
-#include "gromacs/utility/smalloc.h"
 
-typedef struct
+using gmx::ArrayRef;
+using gmx::ArrayRefWithPadding;
+using gmx::RVec;
+
+struct t_shell
 {
-    int nnucl;
-    int shell;                  /* The shell id                                */
-    int nucl1, nucl2, nucl3;    /* The nuclei connected to the shell   */
-    /* gmx_bool    bInterCG; */ /* Coupled to nuclei outside cg?        */
-    real k;                     /* force constant                      */
-    real k_1;                   /* 1 over force constant               */
-    rvec xold;
-    rvec fold;
-    rvec step;
-} t_shell;
+    int  nnucl      = 0;  /* The number of nuclei */
+    int  shellIndex = -1; /* The shell index */
+    int  nucl1      = -1; /* The first nuclei connected to the shell   */
+    int  nucl2      = -1; /* The second nuclei connected to the shell  */
+    int  nucl3      = -1; /* The third nuclei connected to the shell   */
+    real k          = 0;  /* force constant                    */
+    real k_1        = 0;  /* 1 over force constant             */
+    rvec xold;            /* The old shell coordinates */
+    rvec fold;            /* The old force on the shell */
+    rvec step;            /* Step size for steepest descents */
+};
 
 struct gmx_shellfc_t
 {
     /* Shell counts, indices, parameters and working data */
-    int      nshell_gl;      /* The number of shells in the system        */
-    t_shell* shell_gl;       /* All the shells (for DD only)              */
-    int*     shell_index_gl; /* Global shell index (for DD only)          */
-    gmx_bool bInterCG;       /* Are there inter charge-group shells?      */
-    int      nshell;         /* The number of local shells                */
-    t_shell* shell;          /* The local shells                          */
-    int      shell_nalloc;   /* The allocation size of shell              */
-    gmx_bool bPredict;       /* Predict shell positions                   */
-    gmx_bool bRequireInit;   /* Require initialization of shell positions */
-    int      nflexcon;       /* The number of flexible constraints        */
-
-    /* Temporary arrays, should be fixed size 2 when fully converted to C++ */
-    PaddedHostVector<gmx::RVec>* x; /* Array for iterative minimization          */
-    PaddedHostVector<gmx::RVec>* f; /* Array for iterative minimization          */
+    std::vector<t_shell> shell_gl;              /* All the shells (for DD only)              */
+    std::vector<int>     shell_index_gl;        /* Global shell index (for DD only)          */
+    gmx_bool             bInterCG;              /* Are there inter charge-group shells?      */
+    std::vector<t_shell> shells;                /* The local shells                          */
+    bool                 predictShells = false; /* Predict shell positions                   */
+    bool                 requireInit   = false; /* Require initialization of shell positions */
+    int                  nflexcon      = 0;     /* The number of flexible constraints        */
+
+    std::array<PaddedHostVector<RVec>, 2> x; /* Coordinate buffers for iterative minimization */
+    std::array<PaddedHostVector<RVec>, 2> f; /* Force buffers for iterative minimization */
 
     /* Flexible constraint working data */
-    rvec*        acc_dir;                /* Acceleration direction for flexcon        */
-    rvec*        x_old;                  /* Old coordinates for flexcon               */
-    int          flex_nalloc;            /* The allocation size of acc_dir and x_old  */
-    rvec*        adir_xnold;             /* Work space for init_adir                  */
-    rvec*        adir_xnew;              /* Work space for init_adir                  */
-    int          adir_nalloc;            /* Work space for init_adir                  */
-    std::int64_t numForceEvaluations;    /* Total number of force evaluations         */
-    int          numConvergedIterations; /* Total number of iterations that converged */
+    std::vector<RVec>       acc_dir;                /* Acceleration direction for flexcon        */
+    gmx::PaddedVector<RVec> x_old;                  /* Old coordinates for flexcon               */
+    gmx::PaddedVector<RVec> adir_xnold;             /* Work space for init_adir                  */
+    gmx::PaddedVector<RVec> adir_xnew;              /* Work space for init_adir                  */
+    std::int64_t            numForceEvaluations;    /* Total number of force evaluations         */
+    int                     numConvergedIterations; /* Total number of iterations that converged */
 };
 
 
-static void pr_shell(FILE* fplog, int ns, t_shell s[])
+static void pr_shell(FILE* fplog, ArrayRef<const t_shell> shells)
 {
-    int i;
-
     fprintf(fplog, "SHELL DATA\n");
     fprintf(fplog, "%5s  %8s  %5s  %5s  %5s\n", "Shell", "Force k", "Nucl1", "Nucl2", "Nucl3");
-    for (i = 0; (i < ns); i++)
+    for (const t_shell& shell : shells)
     {
-        fprintf(fplog, "%5d  %8.3f  %5d", s[i].shell, 1.0 / s[i].k_1, s[i].nucl1);
-        if (s[i].nnucl == 2)
+        fprintf(fplog, "%5d  %8.3f  %5d", shell.shellIndex, 1.0 / shell.k_1, shell.nucl1);
+        if (shell.nnucl == 2)
         {
-            fprintf(fplog, "  %5d\n", s[i].nucl2);
+            fprintf(fplog, "  %5d\n", shell.nucl2);
         }
-        else if (s[i].nnucl == 3)
+        else if (shell.nnucl == 3)
         {
-            fprintf(fplog, "  %5d  %5d\n", s[i].nucl2, s[i].nucl3);
+            fprintf(fplog, "  %5d  %5d\n", shell.nucl2, shell.nucl3);
         }
         else
         {
@@ -155,19 +152,17 @@ static void pr_shell(FILE* fplog, int ns, t_shell s[])
  * started, but even when called, the prediction was always
  * over-written by a subsequent call in the MD loop, so has been
  * removed. */
-static void predict_shells(FILE*       fplog,
-                           rvec        x[],
-                           rvec        v[],
-                           real        dt,
-                           int         ns,
-                           t_shell     s[],
-                           const real  mass[],
-                           gmx_mtop_t* mtop,
-                           gmx_bool    bInit)
+static void predict_shells(FILE*                   fplog,
+                           ArrayRef<RVec>          x,
+                           ArrayRef<RVec>          v,
+                           real                    dt,
+                           ArrayRef<const t_shell> shells,
+                           const real              mass[],
+                           gmx_mtop_t*             mtop,
+                           gmx_bool                bInit)
 {
-    int   i, m, s1, n1, n2, n3;
-    real  dt_1, fudge, tm, m1, m2, m3;
-    rvec* ptr;
+    int  m, n1, n2, n3;
+    real dt_1, fudge, tm, m1, m2, m3;
 
     GMX_RELEASE_ASSERT(mass || mtop, "Must have masses or a way to look them up");
 
@@ -177,41 +172,42 @@ static void predict_shells(FILE*       fplog,
      */
     fudge = 1.0;
 
+    ArrayRef<RVec> xOrV;
     if (bInit)
     {
         if (fplog)
         {
             fprintf(fplog, "RELAX: Using prediction for initial shell placement\n");
         }
-        ptr  = x;
+        xOrV = x;
         dt_1 = 1;
     }
     else
     {
-        ptr  = v;
+        xOrV = v;
         dt_1 = fudge * dt;
     }
 
     int molb = 0;
-    for (i = 0; (i < ns); i++)
+    for (const t_shell& shell : shells)
     {
-        s1 = s[i].shell;
+        const int s1 = shell.shellIndex;
         if (bInit)
         {
             clear_rvec(x[s1]);
         }
-        switch (s[i].nnucl)
+        switch (shell.nnucl)
         {
             case 1:
-                n1 = s[i].nucl1;
+                n1 = shell.nucl1;
                 for (m = 0; (m < DIM); m++)
                 {
-                    x[s1][m] += ptr[n1][m] * dt_1;
+                    x[s1][m] += xOrV[n1][m] * dt_1;
                 }
                 break;
             case 2:
-                n1 = s[i].nucl1;
-                n2 = s[i].nucl2;
+                n1 = shell.nucl1;
+                n2 = shell.nucl2;
                 if (mass)
                 {
                     m1 = mass[n1];
@@ -226,13 +222,13 @@ static void predict_shells(FILE*       fplog,
                 tm = dt_1 / (m1 + m2);
                 for (m = 0; (m < DIM); m++)
                 {
-                    x[s1][m] += (m1 * ptr[n1][m] + m2 * ptr[n2][m]) * tm;
+                    x[s1][m] += (m1 * xOrV[n1][m] + m2 * xOrV[n2][m]) * tm;
                 }
                 break;
             case 3:
-                n1 = s[i].nucl1;
-                n2 = s[i].nucl2;
-                n3 = s[i].nucl3;
+                n1 = shell.nucl1;
+                n2 = shell.nucl2;
+                n3 = shell.nucl3;
                 if (mass)
                 {
                     m1 = mass[n1];
@@ -249,10 +245,10 @@ static void predict_shells(FILE*       fplog,
                 tm = dt_1 / (m1 + m2 + m3);
                 for (m = 0; (m < DIM); m++)
                 {
-                    x[s1][m] += (m1 * ptr[n1][m] + m2 * ptr[n2][m] + m3 * ptr[n3][m]) * tm;
+                    x[s1][m] += (m1 * xOrV[n1][m] + m2 * xOrV[n2][m] + m3 * xOrV[n3][m]) * tm;
                 }
                 break;
-            default: gmx_fatal(FARGS, "Shell %d has %d nuclei!", i, s[i].nnucl);
+            default: gmx_fatal(FARGS, "Shell %d has %d nuclei!", s1, shell.nnucl);
         }
     }
 }
@@ -260,8 +256,6 @@ static void predict_shells(FILE*       fplog,
 gmx_shellfc_t* init_shell_flexcon(FILE* fplog, const gmx_mtop_t* mtop, int nflexcon, int nstcalcenergy, bool usingDomainDecomposition)
 {
     gmx_shellfc_t* shfc;
-    t_shell*       shell;
-    int*           shell_index = nullptr;
 
     int  ns, nshell, nsi;
     int  i, j, type, a_offset, mol, ftype, nra;
@@ -294,9 +288,7 @@ gmx_shellfc_t* init_shell_flexcon(FILE* fplog, const gmx_mtop_t* mtop, int nflex
         return nullptr;
     }
 
-    snew(shfc, 1);
-    shfc->x        = new PaddedHostVector<gmx::RVec>[2] {};
-    shfc->f        = new PaddedHostVector<gmx::RVec>[2] {};
+    shfc           = new gmx_shellfc_t;
     shfc->nflexcon = nflexcon;
 
     if (nshell == 0)
@@ -304,9 +296,6 @@ gmx_shellfc_t* init_shell_flexcon(FILE* fplog, const gmx_mtop_t* mtop, int nflex
         /* Only flexible constraints, no shells.
          * Note that make_local_shells() does not need to be called.
          */
-        shfc->nshell   = 0;
-        shfc->bPredict = FALSE;
-
         return shfc;
     }
 
@@ -327,7 +316,7 @@ gmx_shellfc_t* init_shell_flexcon(FILE* fplog, const gmx_mtop_t* mtop, int nflex
     /* We have shells: fill the shell data structure */
 
     /* Global system sized array, this should be avoided */
-    snew(shell_index, mtop->natoms);
+    std::vector<int> shell_index(mtop->natoms);
 
     nshell = 0;
     for (const AtomProxy atomP : AtomRange(*mtop))
@@ -340,20 +329,7 @@ gmx_shellfc_t* init_shell_flexcon(FILE* fplog, const gmx_mtop_t* mtop, int nflex
         }
     }
 
-    snew(shell, nshell);
-
-    /* Initiate the shell structures */
-    for (i = 0; (i < nshell); i++)
-    {
-        shell[i].shell = -1;
-        shell[i].nnucl = 0;
-        shell[i].nucl1 = -1;
-        shell[i].nucl2 = -1;
-        shell[i].nucl3 = -1;
-        /* shell[i].bInterCG=FALSE; */
-        shell[i].k_1 = 0;
-        shell[i].k   = 0;
-    }
+    std::vector<t_shell> shell(nshell);
 
     ffparams = &mtop->ffparams;
 
@@ -417,12 +393,12 @@ gmx_shellfc_t* init_shell_flexcon(FILE* fplog, const gmx_mtop_t* mtop, int nflex
                         {
                             gmx_fatal(FARGS, "nsi is %d should be within 0 - %d. aS = %d", nsi, nshell, aS);
                         }
-                        if (shell[nsi].shell == -1)
+                        if (shell[nsi].shellIndex == -1)
                         {
-                            shell[nsi].shell = a_offset + aS;
+                            shell[nsi].shellIndex = a_offset + aS;
                             ns++;
                         }
-                        else if (shell[nsi].shell != a_offset + aS)
+                        else if (shell[nsi].shellIndex != a_offset + aS)
                         {
                             gmx_fatal(FARGS, "Weird stuff in %s, %d", __FILE__, __LINE__);
                         }
@@ -443,7 +419,7 @@ gmx_shellfc_t* init_shell_flexcon(FILE* fplog, const gmx_mtop_t* mtop, int nflex
                         {
                             if (fplog)
                             {
-                                pr_shell(fplog, ns, shell);
+                                pr_shell(fplog, shell);
                             }
                             gmx_fatal(FARGS, "Can not handle more than three bonds per shell\n");
                         }
@@ -514,17 +490,16 @@ gmx_shellfc_t* init_shell_flexcon(FILE* fplog, const gmx_mtop_t* mtop, int nflex
 
     if (debug)
     {
-        pr_shell(debug, ns, shell);
+        pr_shell(debug, shell);
     }
 
 
-    shfc->nshell_gl      = ns;
     shfc->shell_gl       = shell;
     shfc->shell_index_gl = shell_index;
 
-    shfc->bPredict     = (getenv("GMX_NOPREDICT") == nullptr);
-    shfc->bRequireInit = FALSE;
-    if (!shfc->bPredict)
+    shfc->predictShells = (getenv("GMX_NOPREDICT") == nullptr);
+    shfc->requireInit   = false;
+    if (!shfc->predictShells)
     {
         if (fplog)
         {
@@ -533,14 +508,14 @@ gmx_shellfc_t* init_shell_flexcon(FILE* fplog, const gmx_mtop_t* mtop, int nflex
     }
     else
     {
-        shfc->bRequireInit = (getenv("GMX_REQUIRE_SHELL_INIT") != nullptr);
-        if (shfc->bRequireInit && fplog)
+        shfc->requireInit = (getenv("GMX_REQUIRE_SHELL_INIT") != nullptr);
+        if (shfc->requireInit && fplog)
         {
             fprintf(fplog, "\nWill always initiate shell positions\n");
         }
     }
 
-    if (shfc->bPredict)
+    if (shfc->predictShells)
     {
         if (shfc->bInterCG)
         {
@@ -556,17 +531,16 @@ gmx_shellfc_t* init_shell_flexcon(FILE* fplog, const gmx_mtop_t* mtop, int nflex
              *    shell velocities are zeroed, it's a bit tricky to keep
              *    track of the shell displacements and thus the velocity.
              */
-            shfc->bPredict = FALSE;
+            shfc->predictShells = false;
         }
     }
 
     return shfc;
 }
 
-void make_local_shells(const t_commrec* cr, const t_mdatoms* md, gmx_shellfc_t* shfc)
+void gmx::make_local_shells(const t_commrec* cr, const t_mdatoms* md, gmx_shellfc_t* shfc)
 {
-    t_shell*      shell;
-    int           a0, a1, *ind, nshell, i;
+    int           a0, a1;
     gmx_domdec_t* dd = nullptr;
 
     if (DOMAINDECOMP(cr))
@@ -577,57 +551,48 @@ void make_local_shells(const t_commrec* cr, const t_mdatoms* md, gmx_shellfc_t*
     }
     else
     {
-        /* Single node: we need all shells, just copy the pointer */
-        shfc->nshell = shfc->nshell_gl;
-        shfc->shell  = shfc->shell_gl;
+        /* Single node: we need all shells, copy them */
+        shfc->shells = shfc->shell_gl;
 
         return;
     }
 
-    ind = shfc->shell_index_gl;
+    ArrayRef<const int> ind = shfc->shell_index_gl;
 
-    nshell = 0;
-    shell  = shfc->shell;
-    for (i = a0; i < a1; i++)
+    std::vector<t_shell>& shells = shfc->shells;
+    shells.clear();
+    for (int i = a0; i < a1; i++)
     {
         if (md->ptype[i] == eptShell)
         {
-            if (nshell + 1 > shfc->shell_nalloc)
-            {
-                shfc->shell_nalloc = over_alloc_dd(nshell + 1);
-                srenew(shell, shfc->shell_nalloc);
-            }
             if (dd)
             {
-                shell[nshell] = shfc->shell_gl[ind[dd->globalAtomIndices[i]]];
+                shells.push_back(shfc->shell_gl[ind[dd->globalAtomIndices[i]]]);
             }
             else
             {
-                shell[nshell] = shfc->shell_gl[ind[i]];
+                shells.push_back(shfc->shell_gl[ind[i]]);
             }
+            t_shell& shell = shells.back();
 
             /* With inter-cg shells we can no do shell prediction,
              * so we do not need the nuclei numbers.
              */
             if (!shfc->bInterCG)
             {
-                shell[nshell].nucl1 = i + shell[nshell].nucl1 - shell[nshell].shell;
-                if (shell[nshell].nnucl > 1)
+                shell.nucl1 = i + shell.nucl1 - shell.shellIndex;
+                if (shell.nnucl > 1)
                 {
-                    shell[nshell].nucl2 = i + shell[nshell].nucl2 - shell[nshell].shell;
+                    shell.nucl2 = i + shell.nucl2 - shell.shellIndex;
                 }
-                if (shell[nshell].nnucl > 2)
+                if (shell.nnucl > 2)
                 {
-                    shell[nshell].nucl3 = i + shell[nshell].nucl3 - shell[nshell].shell;
+                    shell.nucl3 = i + shell.nucl3 - shell.shellIndex;
                 }
             }
-            shell[nshell].shell = i;
-            nshell++;
+            shell.shellIndex = i;
         }
     }
-
-    shfc->nshell = nshell;
-    shfc->shell  = shell;
 }
 
 static void do_1pos(rvec xnew, const rvec xold, const rvec f, real step)
@@ -666,11 +631,11 @@ static void do_1pos3(rvec xnew, const rvec xold, const rvec f, const rvec step)
     xnew[ZZ] = zo + dz;
 }
 
-static void directional_sd(gmx::ArrayRef<const gmx::RVec> xold,
-                           gmx::ArrayRef<gmx::RVec>       xnew,
-                           const rvec                     acc_dir[],
-                           int                            homenr,
-                           real                           step)
+static void directional_sd(ArrayRef<const RVec> xold,
+                           ArrayRef<RVec>       xnew,
+                           ArrayRef<const RVec> acc_dir,
+                           int                  homenr,
+                           real                 step)
 {
     const rvec* xo = as_rvec_array(xold.data());
     rvec*       xn = as_rvec_array(xnew.data());
@@ -681,16 +646,15 @@ static void directional_sd(gmx::ArrayRef<const gmx::RVec> xold,
     }
 }
 
-static void shell_pos_sd(gmx::ArrayRef<const gmx::RVec> xcur,
-                         gmx::ArrayRef<gmx::RVec>       xnew,
-                         gmx::ArrayRef<const gmx::RVec> f,
-                         int                            ns,
-                         t_shell                        s[],
-                         int                            count)
+static void shell_pos_sd(ArrayRef<const RVec> xcur,
+                         ArrayRef<RVec>       xnew,
+                         ArrayRef<const RVec> f,
+                         ArrayRef<t_shell>    shells,
+                         int                  count)
 {
     const real step_scale_min = 0.8, step_scale_increment = 0.2, step_scale_max = 1.2,
                step_scale_multiple = (step_scale_max - step_scale_min) / step_scale_increment;
-    int        i, shell, d;
+    int        d;
     real       dx, df, k_est;
     const real zero = 0;
 #ifdef PRINT_STEP
@@ -699,17 +663,17 @@ static void shell_pos_sd(gmx::ArrayRef<const gmx::RVec> xcur,
     step_min = 1e30;
     step_max = 0;
 #endif
-    for (i = 0; (i < ns); i++)
+    for (t_shell& shell : shells)
     {
-        shell = s[i].shell;
+        const int ind = shell.shellIndex;
         if (count == 1)
         {
             for (d = 0; d < DIM; d++)
             {
-                s[i].step[d] = s[i].k_1;
+                shell.step[d] = shell.k_1;
 #ifdef PRINT_STEP
-                step_min = std::min(step_min, s[i].step[d]);
-                step_max = std::max(step_max, s[i].step[d]);
+                step_min = std::min(step_min, shell.step[d]);
+                step_max = std::max(step_max, shell.step[d]);
 #endif
             }
         }
@@ -717,8 +681,8 @@ static void shell_pos_sd(gmx::ArrayRef<const gmx::RVec> xcur,
         {
             for (d = 0; d < DIM; d++)
             {
-                dx = xcur[shell][d] - s[i].xold[d];
-                df = f[shell][d] - s[i].fold[d];
+                dx = xcur[ind][d] - shell.xold[d];
+                df = f[ind][d] - shell.fold[d];
                 /* -dx/df gets used to generate an interpolated value, but would
                  * cause a NaN if df were binary-equal to zero. Values close to
                  * zero won't cause problems (because of the min() and max()), so
@@ -728,11 +692,11 @@ static void shell_pos_sd(gmx::ArrayRef<const gmx::RVec> xcur,
                     k_est = -dx / df;
                     /* Scale the step size by a factor interpolated from
                      * step_scale_min to step_scale_max, as k_est goes from 0 to
-                     * step_scale_multiple * s[i].step[d] */
-                    s[i].step[d] = step_scale_min * s[i].step[d]
-                                   + step_scale_increment
-                                             * std::min(step_scale_multiple * s[i].step[d],
-                                                        std::max(k_est, zero));
+                     * step_scale_multiple * shell.step[d] */
+                    shell.step[d] = step_scale_min * shell.step[d]
+                                    + step_scale_increment
+                                              * std::min(step_scale_multiple * shell.step[d],
+                                                         std::max(k_est, zero));
                 }
                 else
                 {
@@ -744,27 +708,27 @@ static void shell_pos_sd(gmx::ArrayRef<const gmx::RVec> xcur,
                     }
                     else /* 0 != dx */
                     {
-                        s[i].step[d] *= step_scale_max;
+                        shell.step[d] *= step_scale_max;
                     }
                 }
 #ifdef PRINT_STEP
-                step_min = std::min(step_min, s[i].step[d]);
-                step_max = std::max(step_max, s[i].step[d]);
+                step_min = std::min(step_min, shell.step[d]);
+                step_max = std::max(step_max, shell.step[d]);
 #endif
             }
         }
-        copy_rvec(xcur[shell], s[i].xold);
-        copy_rvec(f[shell], s[i].fold);
+        copy_rvec(xcur[ind], shell.xold);
+        copy_rvec(f[ind], shell.fold);
 
-        do_1pos3(xnew[shell], xcur[shell], f[shell], s[i].step);
+        do_1pos3(xnew[ind], xcur[ind], f[ind], shell.step);
 
         if (gmx_debug_at)
         {
-            fprintf(debug, "shell[%d] = %d\n", i, shell);
-            pr_rvec(debug, 0, "fshell", f[shell], DIM, TRUE);
-            pr_rvec(debug, 0, "xold", xcur[shell], DIM, TRUE);
-            pr_rvec(debug, 0, "step", s[i].step, DIM, TRUE);
-            pr_rvec(debug, 0, "xnew", xnew[shell], DIM, TRUE);
+            fprintf(debug, "shell = %d\n", ind);
+            pr_rvec(debug, 0, "fshell", f[ind], DIM, TRUE);
+            pr_rvec(debug, 0, "xold", xcur[ind], DIM, TRUE);
+            pr_rvec(debug, 0, "step", shell.step, DIM, TRUE);
+            pr_rvec(debug, 0, "xnew", xnew[ind], DIM, TRUE);
         }
     }
 #ifdef PRINT_STEP
@@ -772,13 +736,11 @@ static void shell_pos_sd(gmx::ArrayRef<const gmx::RVec> xcur,
 #endif
 }
 
-static void decrease_step_size(int nshell, t_shell s[])
+static void decrease_step_size(ArrayRef<t_shell> shells)
 {
-    int i;
-
-    for (i = 0; i < nshell; i++)
+    for (t_shell& shell : shells)
     {
-        svmul(0.8, s[i].step, s[i].step);
+        svmul(0.8, shell.step, shell.step);
     }
 }
 
@@ -798,24 +760,22 @@ static void print_epot(FILE* fp, int64_t mdstep, int count, real epot, real df,
 }
 
 
-static real rms_force(const t_commrec*               cr,
-                      gmx::ArrayRef<const gmx::RVec> force,
-                      int                            ns,
-                      t_shell                        s[],
-                      int                            ndir,
-                      real*                          sf_dir,
-                      real*                          Epot)
+static real rms_force(const t_commrec*        cr,
+                      ArrayRef<const RVec>    force,
+                      ArrayRef<const t_shell> shells,
+                      int                     ndir,
+                      real*                   sf_dir,
+                      real*                   Epot)
 {
     double      buf[4];
     const rvec* f = as_rvec_array(force.data());
 
     buf[0] = *sf_dir;
-    for (int i = 0; i < ns; i++)
+    for (const t_shell& shell : shells)
     {
-        int shell = s[i].shell;
-        buf[0] += norm2(f[shell]);
+        buf[0] += norm2(f[shell.shellIndex]);
     }
-    int ntot = ns;
+    int ntot = shells.ssize();
 
     if (PAR(cr))
     {
@@ -832,21 +792,20 @@ static real rms_force(const t_commrec*               cr,
     return (ntot ? std::sqrt(buf[0] / ntot) : 0);
 }
 
-static void dump_shells(FILE* fp, gmx::ArrayRef<gmx::RVec> f, real ftol, int ns, t_shell s[])
+static void dump_shells(FILE* fp, ArrayRef<RVec> f, real ftol, ArrayRef<const t_shell> shells)
 {
-    int  i, shell;
     real ft2, ff2;
 
     ft2 = gmx::square(ftol);
 
-    for (i = 0; (i < ns); i++)
+    for (const t_shell& shell : shells)
     {
-        shell = s[i].shell;
-        ff2   = iprod(f[shell], f[shell]);
+        const int ind = shell.shellIndex;
+        ff2           = iprod(f[ind], f[ind]);
         if (ff2 > ft2)
         {
-            fprintf(fp, "SHELL %5d, force %10.5f  %10.5f  %10.5f, |f| %10.5f\n", shell,
-                    f[shell][XX], f[shell][YY], f[shell][ZZ], std::sqrt(ff2));
+            fprintf(fp, "SHELL %5d, force %10.5f  %10.5f  %10.5f, |f| %10.5f\n", ind, f[ind][XX],
+                    f[ind][YY], f[ind][ZZ], std::sqrt(ff2));
         }
     }
 }
@@ -859,16 +818,15 @@ static void init_adir(gmx_shellfc_t*            shfc,
                       int64_t                   step,
                       const t_mdatoms*          md,
                       int                       end,
-                      rvec*                     x_old,
-                      rvec*                     x_init,
-                      rvec*                     x,
-                      rvec*                     f,
-                      rvec*                     acc_dir,
+                      ArrayRefWithPadding<RVec> xOld,
+                      ArrayRef<RVec>            x_init,
+                      ArrayRefWithPadding<RVec> xCurrent,
+                      ArrayRef<RVec>            f,
+                      ArrayRef<RVec>            acc_dir,
                       const matrix              box,
-                      gmx::ArrayRef<const real> lambda,
+                      ArrayRef<const real>      lambda,
                       real*                     dvdlambda)
 {
-    rvec *          xnold, *xnew;
     double          dt, w_dt;
     int             n, d;
     unsigned short* ptype;
@@ -881,14 +839,12 @@ static void init_adir(gmx_shellfc_t*            shfc,
     {
         n = end;
     }
-    if (n > shfc->adir_nalloc)
-    {
-        shfc->adir_nalloc = over_alloc_dd(n);
-        srenew(shfc->adir_xnold, shfc->adir_nalloc);
-        srenew(shfc->adir_xnew, shfc->adir_nalloc);
-    }
-    xnold = shfc->adir_xnold;
-    xnew  = shfc->adir_xnew;
+    shfc->adir_xnold.resizeWithPadding(n);
+    shfc->adir_xnew.resizeWithPadding(n);
+    rvec* xnold = as_rvec_array(shfc->adir_xnold.data());
+    rvec* xnew  = as_rvec_array(shfc->adir_xnew.data());
+    rvec* x_old = as_rvec_array(xOld.paddedArrayRef().data());
+    rvec* x     = as_rvec_array(xCurrent.paddedArrayRef().data());
 
     ptype = md->ptype;
 
@@ -913,10 +869,17 @@ static void init_adir(gmx_shellfc_t*            shfc,
             }
         }
     }
-    constr->apply(FALSE, FALSE, step, 0, 1.0, x, xnold, nullptr, box, lambda[efptBONDED],
-                  &(dvdlambda[efptBONDED]), nullptr, nullptr, gmx::ConstraintVariable::Positions);
-    constr->apply(FALSE, FALSE, step, 0, 1.0, x, xnew, nullptr, box, lambda[efptBONDED],
-                  &(dvdlambda[efptBONDED]), nullptr, nullptr, gmx::ConstraintVariable::Positions);
+    bool needsLogging  = false;
+    bool computeEnergy = false;
+    bool computeVirial = false;
+    constr->apply(needsLogging, computeEnergy, step, 0, 1.0, xCurrent,
+                  shfc->adir_xnold.arrayRefWithPadding(), {}, box, lambda[efptBONDED],
+                  &(dvdlambda[efptBONDED]), {}, computeVirial, nullptr,
+                  gmx::ConstraintVariable::Positions);
+    constr->apply(needsLogging, computeEnergy, step, 0, 1.0, xCurrent,
+                  shfc->adir_xnew.arrayRefWithPadding(), {}, box, lambda[efptBONDED],
+                  &(dvdlambda[efptBONDED]), {}, computeVirial, nullptr,
+                  gmx::ConstraintVariable::Positions);
 
     for (n = 0; n < end; n++)
     {
@@ -929,71 +892,59 @@ static void init_adir(gmx_shellfc_t*            shfc,
     }
 
     /* Project the acceleration on the old bond directions */
-    constr->apply(FALSE, FALSE, step, 0, 1.0, x_old, xnew, acc_dir, box, lambda[efptBONDED],
-                  &(dvdlambda[efptBONDED]), nullptr, nullptr, gmx::ConstraintVariable::Deriv_FlexCon);
+    constr->apply(needsLogging, computeEnergy, step, 0, 1.0, xOld, shfc->adir_xnew.arrayRefWithPadding(),
+                  acc_dir, box, lambda[efptBONDED], &(dvdlambda[efptBONDED]), {}, computeVirial,
+                  nullptr, gmx::ConstraintVariable::Deriv_FlexCon);
 }
 
-void relax_shell_flexcon(FILE*                               fplog,
-                         const t_commrec*                    cr,
-                         const gmx_multisim_t*               ms,
-                         gmx_bool                            bVerbose,
-                         gmx_enfrot*                         enforcedRotation,
-                         int64_t                             mdstep,
-                         const t_inputrec*                   inputrec,
-                         gmx::ImdSession*                    imdSession,
-                         pull_t*                             pull_work,
-                         gmx_bool                            bDoNS,
-                         int                                 force_flags,
-                         const gmx_localtop_t*               top,
-                         gmx::Constraints*                   constr,
-                         gmx_enerdata_t*                     enerd,
-                         t_fcdata*                           fcd,
-                         int                                 natoms,
-                         gmx::ArrayRefWithPadding<gmx::RVec> x,
-                         gmx::ArrayRefWithPadding<gmx::RVec> v,
-                         const matrix                        box,
-                         gmx::ArrayRef<real>                 lambda,
-                         history_t*                          hist,
-                         gmx::ArrayRefWithPadding<gmx::RVec> f,
-                         tensor                              force_vir,
-                         const t_mdatoms*                    md,
-                         t_nrnb*                             nrnb,
-                         gmx_wallcycle_t                     wcycle,
-                         t_graph*                            graph,
-                         gmx_shellfc_t*                      shfc,
-                         t_forcerec*                         fr,
-                         gmx::MdrunScheduleWorkload*         runScheduleWork,
-                         double                              t,
-                         rvec                                mu_tot,
-                         const gmx_vsite_t*                  vsite,
-                         const DDBalanceRegionHandler&       ddBalanceRegionHandler)
+void relax_shell_flexcon(FILE*                         fplog,
+                         const t_commrec*              cr,
+                         const gmx_multisim_t*         ms,
+                         gmx_bool                      bVerbose,
+                         gmx_enfrot*                   enforcedRotation,
+                         int64_t                       mdstep,
+                         const t_inputrec*             inputrec,
+                         gmx::ImdSession*              imdSession,
+                         pull_t*                       pull_work,
+                         gmx_bool                      bDoNS,
+                         int                           force_flags,
+                         const gmx_localtop_t*         top,
+                         gmx::Constraints*             constr,
+                         gmx_enerdata_t*               enerd,
+                         int                           natoms,
+                         ArrayRefWithPadding<RVec>     xPadded,
+                         ArrayRefWithPadding<RVec>     vPadded,
+                         const matrix                  box,
+                         ArrayRef<real>                lambda,
+                         history_t*                    hist,
+                         gmx::ForceBuffersView*        f,
+                         tensor                        force_vir,
+                         const t_mdatoms*              md,
+                         t_nrnb*                       nrnb,
+                         gmx_wallcycle_t               wcycle,
+                         gmx_shellfc_t*                shfc,
+                         t_forcerec*                   fr,
+                         gmx::MdrunScheduleWorkload*   runScheduleWork,
+                         double                        t,
+                         rvec                          mu_tot,
+                         gmx::VirtualSitesHandler*     vsite,
+                         const DDBalanceRegionHandler& ddBalanceRegionHandler)
 {
-    auto xRvec = as_rvec_array(x.paddedArrayRef().data());
-    auto vRvec = as_rvec_array(v.paddedArrayRef().data());
-
-    int           nshell;
-    t_shell*      shell;
-    const t_idef* idef;
-    rvec *        acc_dir = nullptr, *x_old = nullptr;
-    real          Epot[2], df[2];
-    real          sf_dir, invdt;
-    real          ftol, dum = 0;
-    char          sbuf[22];
-    gmx_bool      bCont, bInit, bConverged;
-    int           nat, dd_ac0, dd_ac1 = 0, i;
-    int           homenr = md->homenr, end = homenr;
-    int           nflexcon, number_steps, d, Min = 0, count = 0;
+    real Epot[2], df[2];
+    real sf_dir, invdt;
+    real dum = 0;
+    char sbuf[22];
+    int  nat, dd_ac0, dd_ac1 = 0, i;
+    int  homenr = md->homenr, end = homenr;
+    int  d, Min = 0, count = 0;
 #define Try (1 - Min) /* At start Try = 1 */
 
-    bCont        = (mdstep == inputrec->init_step) && inputrec->bContinuation;
-    bInit        = (mdstep == inputrec->init_step) || shfc->bRequireInit;
-    ftol         = inputrec->em_tol;
-    number_steps = inputrec->niter;
-    nshell       = shfc->nshell;
-    shell        = shfc->shell;
-    nflexcon     = shfc->nflexcon;
-
-    idef = &top->idef;
+    const bool        bCont        = (mdstep == inputrec->init_step) && inputrec->bContinuation;
+    const bool        bInit        = (mdstep == inputrec->init_step) || shfc->requireInit;
+    const real        ftol         = inputrec->em_tol;
+    const int         number_steps = inputrec->niter;
+    ArrayRef<t_shell> shells       = shfc->shells;
+    const int         nflexcon     = shfc->nflexcon;
 
     if (DOMAINDECOMP(cr))
     {
@@ -1015,11 +966,11 @@ void relax_shell_flexcon(FILE*                               fplog,
         shfc->f[i].resizeWithPadding(nat);
     }
 
-    /* Create views that we can swap */
-    gmx::ArrayRefWithPadding<gmx::RVec> posWithPadding[2];
-    gmx::ArrayRefWithPadding<gmx::RVec> forceWithPadding[2];
-    gmx::ArrayRef<gmx::RVec>            pos[2];
-    gmx::ArrayRef<gmx::RVec>            force[2];
+    /* Create views that we can swap for trail and minimum for positions and forces */
+    ArrayRefWithPadding<RVec> posWithPadding[2];
+    ArrayRefWithPadding<RVec> forceWithPadding[2];
+    ArrayRef<RVec>            pos[2];
+    ArrayRef<RVec>            force[2];
     for (i = 0; (i < 2); i++)
     {
         posWithPadding[i]   = shfc->x[i].arrayRefWithPadding();
@@ -1028,45 +979,29 @@ void relax_shell_flexcon(FILE*                               fplog,
         force[i]            = forceWithPadding[i].paddedArrayRef();
     }
 
-    if (bDoNS && inputrec->ePBC != epbcNONE && !DOMAINDECOMP(cr))
+    ArrayRef<RVec> x = xPadded.unpaddedArrayRef();
+    ArrayRef<RVec> v = vPadded.unpaddedArrayRef();
+
+    if (bDoNS && inputrec->pbcType != PbcType::No && !DOMAINDECOMP(cr))
     {
         /* This is the only time where the coordinates are used
          * before do_force is called, which normally puts all
          * charge groups in the box.
          */
-        auto xRef = x.paddedArrayRef();
-        put_atoms_in_box_omp(fr->ePBC, box, xRef.subArray(0, md->homenr),
+        put_atoms_in_box_omp(fr->pbcType, box, x.subArray(0, md->homenr),
                              gmx_omp_nthreads_get(emntDefault));
-
-        if (graph)
-        {
-            mk_mshift(fplog, graph, fr->ePBC, box, xRvec);
-        }
-    }
-
-    /* After this all coordinate arrays will contain whole charge groups */
-    if (graph)
-    {
-        shift_self(graph, box, xRvec);
     }
 
     if (nflexcon)
     {
-        if (nat > shfc->flex_nalloc)
-        {
-            shfc->flex_nalloc = over_alloc_dd(nat);
-            srenew(shfc->acc_dir, shfc->flex_nalloc);
-            srenew(shfc->x_old, shfc->flex_nalloc);
-        }
-        acc_dir        = shfc->acc_dir;
-        x_old          = shfc->x_old;
-        auto xArrayRef = x.paddedArrayRef();
-        auto vArrayRef = v.paddedArrayRef();
+        shfc->acc_dir.resize(nat);
+        shfc->x_old.resizeWithPadding(nat);
+        ArrayRef<RVec> x_old = shfc->x_old.arrayRefWithPadding().unpaddedArrayRef();
         for (i = 0; i < homenr; i++)
         {
             for (d = 0; d < DIM; d++)
             {
-                shfc->x_old[i][d] = xArrayRef[i][d] - vArrayRef[i][d] * inputrec->delta_t;
+                x_old[i][d] = x[i][d] - v[i][d] * inputrec->delta_t;
             }
         }
     }
@@ -1074,44 +1009,38 @@ void relax_shell_flexcon(FILE*                               fplog,
     /* Do a prediction of the shell positions, when appropriate.
      * Without velocities (EM, NM, BD) we only do initial prediction.
      */
-    if (shfc->bPredict && !bCont && (EI_STATE_VELOCITY(inputrec->eI) || bInit))
-    {
-        predict_shells(fplog, xRvec, vRvec, inputrec->delta_t, nshell, shell, md->massT, nullptr, bInit);
-    }
-
-    /* do_force expected the charge groups to be in the box */
-    if (graph)
+    if (shfc->predictShells && !bCont && (EI_STATE_VELOCITY(inputrec->eI) || bInit))
     {
-        unshift_self(graph, box, xRvec);
+        predict_shells(fplog, x, v, inputrec->delta_t, shells, md->massT, nullptr, bInit);
     }
 
     /* Calculate the forces first time around */
     if (gmx_debug_at)
     {
-        pr_rvecs(debug, 0, "x b4 do_force", xRvec, homenr);
+        pr_rvecs(debug, 0, "x b4 do_force", as_rvec_array(x.data()), homenr);
     }
-    int shellfc_flags = force_flags | (bVerbose ? GMX_FORCE_ENERGY : 0);
+    int                   shellfc_flags = force_flags | (bVerbose ? GMX_FORCE_ENERGY : 0);
+    gmx::ForceBuffersView forceViewInit = gmx::ForceBuffersView(forceWithPadding[Min], {}, false);
     do_force(fplog, cr, ms, inputrec, nullptr, enforcedRotation, imdSession, pull_work, mdstep,
-             nrnb, wcycle, top, box, x, hist, forceWithPadding[Min], force_vir, md, enerd, fcd,
-             lambda, graph, fr, runScheduleWork, vsite, mu_tot, t, nullptr,
+             nrnb, wcycle, top, box, xPadded, hist, &forceViewInit, force_vir, md, enerd, lambda,
+             fr, runScheduleWork, vsite, mu_tot, t, nullptr,
              (bDoNS ? GMX_FORCE_NS : 0) | shellfc_flags, ddBalanceRegionHandler);
 
     sf_dir = 0;
     if (nflexcon)
     {
-        init_adir(shfc, constr, inputrec, cr, dd_ac1, mdstep, md, end, shfc->x_old, xRvec, xRvec,
-                  as_rvec_array(force[Min].data()), shfc->acc_dir, box, lambda, &dum);
+        init_adir(shfc, constr, inputrec, cr, dd_ac1, mdstep, md, end, shfc->x_old.arrayRefWithPadding(),
+                  x, xPadded, force[Min], shfc->acc_dir, box, lambda, &dum);
 
         for (i = 0; i < end; i++)
         {
             sf_dir += md->massT[i] * norm2(shfc->acc_dir[i]);
         }
     }
-    sum_epot(&(enerd->grpp), enerd->term);
+    accumulatePotentialEnergies(enerd, lambda, inputrec->fepvals);
     Epot[Min] = enerd->term[F_EPOT];
 
-    df[Min] = rms_force(cr, forceWithPadding[Min].paddedArrayRef(), nshell, shell, nflexcon,
-                        &sf_dir, &Epot[Min]);
+    df[Min] = rms_force(cr, forceWithPadding[Min].paddedArrayRef(), shells, nflexcon, &sf_dir, &Epot[Min]);
     df[Try] = 0;
     if (debug)
     {
@@ -1123,15 +1052,15 @@ void relax_shell_flexcon(FILE*                               fplog,
         pr_rvecs(debug, 0, "force0", as_rvec_array(force[Min].data()), md->nr);
     }
 
-    if (nshell + nflexcon > 0)
+    if (!shells.empty() || nflexcon > 0)
     {
         /* Copy x to pos[Min] & pos[Try]: during minimization only the
          * shell positions are updated, therefore the other particles must
          * be set here, in advance.
          */
-        std::copy(x.paddedArrayRef().begin(), x.paddedArrayRef().end(),
+        std::copy(xPadded.paddedArrayRef().begin(), xPadded.paddedArrayRef().end(),
                   posWithPadding[Min].paddedArrayRef().begin());
-        std::copy(x.paddedArrayRef().begin(), x.paddedArrayRef().end(),
+        std::copy(xPadded.paddedArrayRef().begin(), xPadded.paddedArrayRef().end(),
                   posWithPadding[Try].paddedArrayRef().begin());
     }
 
@@ -1151,33 +1080,26 @@ void relax_shell_flexcon(FILE*                               fplog,
     /* First check whether we should do shells, or whether the force is
      * low enough even without minimization.
      */
-    bConverged = (df[Min] < ftol);
+    bool bConverged = (df[Min] < ftol);
 
     for (count = 1; (!(bConverged) && (count < number_steps)); count++)
     {
         if (vsite)
         {
-            construct_vsites(vsite, as_rvec_array(pos[Min].data()), inputrec->delta_t, vRvec,
-                             idef->iparams, idef->il, fr->ePBC, fr->bMolPBC, cr, box);
+            vsite->construct(pos[Min], inputrec->delta_t, v, box);
         }
 
         if (nflexcon)
         {
-            init_adir(shfc, constr, inputrec, cr, dd_ac1, mdstep, md, end, x_old, xRvec,
-                      as_rvec_array(pos[Min].data()), as_rvec_array(force[Min].data()), acc_dir,
-                      box, lambda, &dum);
+            init_adir(shfc, constr, inputrec, cr, dd_ac1, mdstep, md, end,
+                      shfc->x_old.arrayRefWithPadding(), x, posWithPadding[Min], force[Min],
+                      shfc->acc_dir, box, lambda, &dum);
 
-            directional_sd(pos[Min], pos[Try], acc_dir, end, fr->fc_stepsize);
+            directional_sd(pos[Min], pos[Try], shfc->acc_dir, end, fr->fc_stepsize);
         }
 
         /* New positions, Steepest descent */
-        shell_pos_sd(pos[Min], pos[Try], force[Min], nshell, shell, count);
-
-        /* do_force expected the charge groups to be in the box */
-        if (graph)
-        {
-            unshift_self(graph, box, as_rvec_array(pos[Try].data()));
-        }
+        shell_pos_sd(pos[Min], pos[Try], force[Min], shells, count);
 
         if (gmx_debug_at)
         {
@@ -1185,11 +1107,11 @@ void relax_shell_flexcon(FILE*                               fplog,
             pr_rvecs(debug, 0, "RELAX: pos[Try]  ", as_rvec_array(pos[Try].data()), homenr);
         }
         /* Try the new positions */
-        do_force(fplog, cr, ms, inputrec, nullptr, enforcedRotation, imdSession, pull_work, 1, nrnb,
-                 wcycle, top, box, posWithPadding[Try], hist, forceWithPadding[Try], force_vir, md,
-                 enerd, fcd, lambda, graph, fr, runScheduleWork, vsite, mu_tot, t, nullptr,
-                 shellfc_flags, ddBalanceRegionHandler);
-        sum_epot(&(enerd->grpp), enerd->term);
+        gmx::ForceBuffersView forceViewTry = gmx::ForceBuffersView(forceWithPadding[Try], {}, false);
+        do_force(fplog, cr, ms, inputrec, nullptr, enforcedRotation, imdSession, pull_work, 1, nrnb, wcycle,
+                 top, box, posWithPadding[Try], hist, &forceViewTry, force_vir, md, enerd, lambda, fr,
+                 runScheduleWork, vsite, mu_tot, t, nullptr, shellfc_flags, ddBalanceRegionHandler);
+        accumulatePotentialEnergies(enerd, lambda, inputrec->fepvals);
         if (gmx_debug_at)
         {
             pr_rvecs(debug, 0, "RELAX: force[Min]", as_rvec_array(force[Min].data()), homenr);
@@ -1198,10 +1120,11 @@ void relax_shell_flexcon(FILE*                               fplog,
         sf_dir = 0;
         if (nflexcon)
         {
-            init_adir(shfc, constr, inputrec, cr, dd_ac1, mdstep, md, end, x_old, xRvec,
-                      as_rvec_array(pos[Try].data()), as_rvec_array(force[Try].data()), acc_dir,
-                      box, lambda, &dum);
+            init_adir(shfc, constr, inputrec, cr, dd_ac1, mdstep, md, end,
+                      shfc->x_old.arrayRefWithPadding(), x, posWithPadding[Try], force[Try],
+                      shfc->acc_dir, box, lambda, &dum);
 
+            ArrayRef<const RVec> acc_dir = shfc->acc_dir;
             for (i = 0; i < end; i++)
             {
                 sf_dir += md->massT[i] * norm2(acc_dir[i]);
@@ -1210,7 +1133,7 @@ void relax_shell_flexcon(FILE*                               fplog,
 
         Epot[Try] = enerd->term[F_EPOT];
 
-        df[Try] = rms_force(cr, force[Try], nshell, shell, nflexcon, &sf_dir, &Epot[Try]);
+        df[Try] = rms_force(cr, force[Try], shells, nflexcon, &sf_dir, &Epot[Try]);
 
         if (debug)
         {
@@ -1226,7 +1149,7 @@ void relax_shell_flexcon(FILE*                               fplog,
             if (gmx_debug_at)
             {
                 fprintf(debug, "SHELL ITER %d\n", count);
-                dump_shells(debug, force[Try], ftol, nshell, shell);
+                dump_shells(debug, force[Try], ftol, shells);
             }
         }
 
@@ -1246,13 +1169,12 @@ void relax_shell_flexcon(FILE*                               fplog,
             if (nflexcon)
             {
                 /* Correct the velocities for the flexible constraints */
-                invdt          = 1 / inputrec->delta_t;
-                auto vArrayRef = v.paddedArrayRef();
+                invdt = 1 / inputrec->delta_t;
                 for (i = 0; i < end; i++)
                 {
                     for (d = 0; d < DIM; d++)
                     {
-                        vArrayRef[i][d] += (pos[Try][i][d] - pos[Min][i][d]) * invdt;
+                        v[i][d] += (pos[Try][i][d] - pos[Min][i][d]) * invdt;
                     }
                 }
             }
@@ -1260,7 +1182,7 @@ void relax_shell_flexcon(FILE*                               fplog,
         }
         else
         {
-            decrease_step_size(nshell, shell);
+            decrease_step_size(shells);
         }
     }
     shfc->numForceEvaluations += count;
@@ -1281,8 +1203,8 @@ void relax_shell_flexcon(FILE*                               fplog,
     }
 
     /* Copy back the coordinates and the forces */
-    std::copy(pos[Min].begin(), pos[Min].end(), x.paddedArrayRef().data());
-    std::copy(force[Min].begin(), force[Min].end(), f.unpaddedArrayRef().begin());
+    std::copy(pos[Min].begin(), pos[Min].end(), x.data());
+    std::copy(force[Min].begin(), force[Min].end(), f->force().begin());
 }
 
 void done_shellfc(FILE* fplog, gmx_shellfc_t* shfc, int64_t numSteps)
@@ -1296,5 +1218,5 @@ void done_shellfc(FILE* fplog, gmx_shellfc_t* shfc, int64_t numSteps)
                 shfc->numForceEvaluations / numStepsAsDouble);
     }
 
-    // TODO Deallocate memory in shfc
+    delete shfc;
 }
index 57aa4b60ddd4aa2510a0ed2ad006345d032948df..d14646b6469593ddf8838bd10095551beb8948c4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <cstdio>
 
-#include "gromacs/math/arrayrefwithpadding.h"
-#include "gromacs/mdlib/vsite.h"
+#include "gromacs/math/vectypes.h"
 #include "gromacs/timing/wallcycle.h"
 #include "gromacs/topology/atoms.h"
 
 class DDBalanceRegionHandler;
 struct gmx_enerdata_t;
 struct gmx_enfrot;
+struct gmx_localtop_t;
 struct gmx_multisim_t;
 struct gmx_shellfc_t;
 struct gmx_mtop_t;
 class history_t;
 struct pull_t;
 struct t_forcerec;
-struct t_fcdata;
-struct t_graph;
 struct t_inputrec;
+struct t_mdatoms;
+struct t_nrnb;
 class t_state;
 
 namespace gmx
 {
+template<typename>
+class ArrayRef;
+template<typename>
+class ArrayRefWithPadding;
 class Constraints;
+class ForceBuffersView;
 class ImdSession;
 class MdrunScheduleWorkload;
+class VirtualSitesHandler;
 } // namespace gmx
 
 /* Initialization function, also predicts the initial shell postions.
+ * Returns a pointer to an initialized shellfc object.
  */
 gmx_shellfc_t* init_shell_flexcon(FILE*             fplog,
                                   const gmx_mtop_t* mtop,
@@ -88,28 +96,26 @@ void relax_shell_flexcon(FILE*                               log,
                          const gmx_localtop_t*               top,
                          gmx::Constraints*                   constr,
                          gmx_enerdata_t*                     enerd,
-                         t_fcdata*                           fcd,
                          int                                 natoms,
                          gmx::ArrayRefWithPadding<gmx::RVec> x,
                          gmx::ArrayRefWithPadding<gmx::RVec> v,
                          const matrix                        box,
                          gmx::ArrayRef<real>                 lambda,
                          history_t*                          hist,
-                         gmx::ArrayRefWithPadding<gmx::RVec> f,
+                         gmx::ForceBuffersView*              f,
                          tensor                              force_vir,
                          const t_mdatoms*                    md,
                          t_nrnb*                             nrnb,
                          gmx_wallcycle_t                     wcycle,
-                         t_graph*                            graph,
                          gmx_shellfc_t*                      shfc,
                          t_forcerec*                         fr,
                          gmx::MdrunScheduleWorkload*         runScheduleWork,
                          double                              t,
                          rvec                                mu_tot,
-                         const gmx_vsite_t*                  vsite,
+                         gmx::VirtualSitesHandler*           vsite,
                          const DDBalanceRegionHandler&       ddBalanceRegionHandler);
 
-/* Print some final output */
+/* Print some final output and delete shellfc */
 void done_shellfc(FILE* fplog, gmx_shellfc_t* shellfc, int64_t numSteps);
 
 /*! \brief Count the different particle types in a system
index 91428c252b904d717e48737212c415aa76722f5f..229da88281b78f7b9ffebf4f4522ed0f2695f3cb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 namespace gmx
 {
 //! \cond libapi
-SimulationContext::SimulationContext(MPI_Comm                          communicator,
-                                     const ArrayRef<const std::string> multiSimDirectoryNames) :
-    communicator_(communicator)
+SimulationContext::SimulationContext(MPI_Comm                    communicator,
+                                     ArrayRef<const std::string> multiSimDirectoryNames) :
+    libraryWorldCommunicator_(communicator)
 {
     GMX_RELEASE_ASSERT((GMX_LIB_MPI && (communicator != MPI_COMM_NULL))
                                || (!GMX_LIB_MPI && (communicator == MPI_COMM_NULL)),
                        "With real MPI, a non-null communicator is required. "
                        "Without it, the communicator must be null.");
-    if (!multiSimDirectoryNames.empty())
+    if (multiSimDirectoryNames.empty())
     {
-        multiSimulation_ = std::make_unique<gmx_multisim_t>(communicator, multiSimDirectoryNames);
+        simulationCommunicator_ = communicator;
+    }
+    else
+    {
+        multiSimulation_ = buildMultiSimulation(communicator, multiSimDirectoryNames);
+        // Use the communicator resulting from the split for the multi-simulation.
+        simulationCommunicator_ = multiSimulation_->simulationComm_;
     }
 }
 
index 2e4ca42e3145db57dc089babf2f1b8be17d7a92c..c57027311d4c0b444d8f8653f6b36fd735fb7104 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -73,12 +73,21 @@ class ArrayRef;
  * other resources to create or configure the context. The simulation may in
  * turn consume or borrow some resources from the context. This is a new
  * framework that will evolve in the contexts of
- * https://redmine.gromacs.org/issues/2375
- * https://redmine.gromacs.org/issues/2587
+ * https://gitlab.com/gromacs/gromacs/-/issues/2375
+ * https://gitlab.com/gromacs/gromacs/-/issues/2587
  *
  * The public interface of SimulationContext is not yet well-specified.
  * Client code can create an instance with gmx::createSimulationContext()
  *
+ * \warning The SimulationContext does not yet encapsulate the resources allocated
+ * to the simulator. See https://gitlab.com/gromacs/gromacs/-/issues/3650
+ *
+ * \warning On thread-MPI code paths, the SimulationContext *does not* represent
+ * constraints on the number of simulation ranks, nor does it represent initialized
+ * communication resources.
+ *
+ * \todo Clarify and strengthen the invariant represented by SimulationContext.
+ *
  * \todo This class should also handle aspects of simulation
  * environment such as working directory and environment variables.
  *
@@ -96,7 +105,7 @@ class ArrayRef;
  * Non-const accessors to shared resources need to be associated with update
  * signals that the simulation components (modules and runner) can subscribe to.
  *
- * Also reference https://redmine.gromacs.org/issues/2587
+ * Also reference https://gitlab.com/gromacs/gromacs/-/issues/2587
  */
 class SimulationContext final
 {
@@ -106,37 +115,69 @@ public:
      */
     SimulationContext() = delete;
     /*!
-     * \brief Construct
+     * \brief Initialize from borrowed communicator.
      *
-     * \param communicator            MPI communicator for this (set of) simulations
+     * \param communicator Communicator for all collaborating simulation processes.
      * \param multiSimDirectoryNames  Names of any directories used with -multidir
+     *
+     * Caller is responsible for keeping *communicator* valid for the life of SimulationContext,
+     * and then freeing the communicator, if appropriate.
+     *
+     * With an external MPI library (non-thread-MPI chosen when configuring with CMake),
+     * the client promises that MPI has been initialized (such as by calling gmx::init()).
+     * This communicator is "borrowed" (not duplicated) from the caller.
+     * Additional communicators may be split from the provided communicator
+     * during the life of the SimulationContext or its consumers.
+     *
+     * With thread-MPI, *communicator* must be MPI_COMM_NULL.
+     * The communicator is set up later
+     * during the process of spawning the threads that will be the MPI
+     * ranks. (Multi-simulation is not supported with thread-MPI.)
+     *
+     * \todo Refine distribution of environment management.
+     *       There should be a context level at which only the current simulator directory matters,
+     *       and a level above which encapsulates multisim details in a specialized type.
      */
     explicit SimulationContext(MPI_Comm communicator, ArrayRef<const std::string> multiSimDirectoryNames);
 
     /*!
-     * \brief MPI communicator object for this simulation object.
+     * \brief MPI resources for the entire simulation context.
      *
-     * With real MPI,
-     *   the gmx wrapper binary has called MPI_Init, thus
-     *     MPI_COMM_WORLD is now valid to use, and
-     *   (in future) the gmxapi runner will handle such details
-     *     (e.g. via mpi4py) before creating its SimulationContext.
-     * In both cases, if a multi-simulation is in use, then its
-     * communicator(s) are found in multiSimulation_. This
-     * communicator is that of all ranks from all simulations, and
-     * will later be split into one for each simulation.
-     * TODO Perhaps (for simplicity) that communicator splitting
-     * task can be undertaken during multi-sim setup.
+     * With an external MPI library (non-thread-MPI chosen when configuring with CMake),
+     * gmx::init() has called MPI_Init and the provided communicator is valid to use.
+     * The communicator is "borrowed" (not duplicated) from the caller.
      *
-     * With thread-MPI in both cases, the communicator is set up later
+     * With thread-MPI, the communicator is set up later
      * during the process of spawning the threads that will be the MPI
-     * ranks. (Multi-simulation is not supported with thread-MPI.)
+     * ranks.
+     */
+    MPI_Comm libraryWorldCommunicator_ = MPI_COMM_NULL;
+
+    /*!
+     * \brief MPI communicator object for this simulation.
+     *
+     * With an external MPI library (non-thread-MPI chosen when configuring with CMake),
+     * gmx::init() has called MPI_Init and the provided communicator is valid to use.
+     * The communicator is "borrowed" (not duplicated) from the caller.
+     *
+     * With thread-MPI, the communicator is set up later
+     * during the process of spawning the threads that will be the MPI
+     * ranks.
      */
-    MPI_Comm communicator_ = MPI_COMM_NULL;
+    MPI_Comm simulationCommunicator_ = MPI_COMM_NULL;
 
     /*!
      * \brief Multi-sim handler (if required by e.g. gmx mdrun
      * -multidir; only supported with real MPI)
+     *
+     * If a multi-simulation is in use, then its
+     * communicator(s) are found in multiSimulation_. This
+     * communicator is that of all ranks from all simulations, and
+     * will later be split into one for each simulation.
+     * TODO: Perhaps (for simplicity) communicator splitting
+     *       can be undertaken during multi-sim setup (acquisition of the multisim resource).
+     *
+     * Multi-simulation is not supported with thread-MPI.
      */
     std::unique_ptr<gmx_multisim_t> multiSimulation_;
 };
diff --git a/src/gromacs/mdrun/simulationinput.cpp b/src/gromacs/mdrun/simulationinput.cpp
new file mode 100644 (file)
index 0000000..a29c252
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#include "gmxpre.h"
+
+#include "simulationinput.h"
+
+#include "gromacs/fileio/checkpoint.h"
+#include "gromacs/fileio/tpxio.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/observableshistory.h"
+#include "gromacs/mdtypes/state.h"
+
+namespace gmx
+{
+
+void applyGlobalSimulationState(const SimulationInput&      simulationInput,
+                                PartialDeserializedTprFile* partialDeserializedTpr,
+                                t_state*                    globalState,
+                                t_inputrec*                 inputRecord,
+                                gmx_mtop_t*                 molecularTopology)
+{
+    *partialDeserializedTpr = read_tpx_state(simulationInput.tprFilename_.c_str(), inputRecord,
+                                             globalState, molecularTopology);
+}
+
+void applyLocalState(const SimulationInput&         simulationInput,
+                     t_fileio*                      logfio,
+                     const t_commrec*               cr,
+                     int*                           dd_nc,
+                     t_inputrec*                    inputRecord,
+                     t_state*                       state,
+                     ObservablesHistory*            observablesHistory,
+                     bool                           reproducibilityRequested,
+                     const MdModulesNotifier&       mdModulesNotifier,
+                     gmx::ReadCheckpointDataHolder* modularSimulatorCheckpointData,
+                     const bool                     useModularSimulator)
+{
+    load_checkpoint(simulationInput.cpiFilename_.c_str(), logfio, cr, dd_nc, inputRecord, state,
+                    observablesHistory, reproducibilityRequested, mdModulesNotifier,
+                    modularSimulatorCheckpointData, useModularSimulator);
+}
+
+} // end namespace gmx
diff --git a/src/gromacs/mdrun/simulationinput.h b/src/gromacs/mdrun/simulationinput.h
new file mode 100644 (file)
index 0000000..8960182
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Utilities for interacting with SimulationInput.
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ * \ingroup module_mdrun
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDRUN_SIMULATIONINPUT_H
+#define GMX_MDRUN_SIMULATIONINPUT_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/mdrun/simulationinputhandle.h"
+#include "gromacs/mdtypes/checkpointdata.h"
+#include "gromacs/utility/mdmodulenotification.h"
+
+// Forward declarations for types from other modules that are opaque to the public API.
+// TODO: Document the sources of these symbols or import a (self-documenting) fwd header.
+struct gmx_mtop_t;
+struct t_commrec;
+struct t_fileio;
+struct t_inputrec;
+class t_state;
+struct ObservablesHistory;
+struct PartialDeserializedTprFile;
+
+namespace gmx
+{
+/*
+ * \brief Prescription for molecular simulation.
+ *
+ * In the first implementation, this is a POD struct to allow removal of direct
+ * references to TPR and CPT files from Mdrunner. The interface for SimulationInput
+ * should be considered to be *completely unspecified* until resolution of
+ * https://gitlab.com/gromacs/gromacs/-/issues/3374
+ *
+ * Clients should use the utility functions defined in simulationinpututility.h
+ *
+ * Design note: It is probably sufficient for future versions to compose SimulationInput
+ * through a Builder rather than to subclass an Interface or base class. Outside of this
+ * translation unit, we should avoid coupling to the class definition until/unless we
+ * develop a much better understanding of simulation input portability.
+ */
+class SimulationInput
+{
+public:
+    SimulationInput(const char* tprFilename, const char* cpiFilename);
+
+    std::string tprFilename_;
+    std::string cpiFilename_;
+};
+
+/*! \brief Get the global simulation input.
+ *
+ * Acquire global simulation data structures from the SimulationInput handle.
+ * Note that global data is returned in the calling thread. In parallel
+ * computing contexts, the client is responsible for calling only where needed.
+ *
+ * Example:
+ *    if (SIMMASTER(cr))
+ *    {
+ *        // Only the master rank has the global state
+ *        globalState = globalSimulationState(simulationInput);
+ *
+ *        // Read (nearly) all data required for the simulation
+ *        applyGlobalInputRecord(simulationInput, inputrec);
+ *        applyGlobalTopology(simulationInput, &mtop);
+ *     }
+ *
+ * \todo Factor the logic for global/local and master-rank-checks.
+ * The SimulationInput utilities should behave properly for the various distributed data scenarios.
+ * Consider supplying data directly to the consumers rather than exposing the
+ * implementation details of the legacy aggregate types.
+ *
+ * \{
+ */
+// TODO: Remove this monolithic detail as member data can be separately cached and managed. (#3374)
+// Note that clients still need tpxio.h for PartialDeserializedTprFile.
+void applyGlobalSimulationState(const SimulationInput&      simulationInput,
+                                PartialDeserializedTprFile* partialDeserializedTpr,
+                                t_state*                    globalState,
+                                t_inputrec*                 inputrec,
+                                gmx_mtop_t*                 globalTopology);
+// TODO: Implement the following, pending further discussion re #3374.
+std::unique_ptr<t_state> globalSimulationState(const SimulationInput&);
+void                     applyGlobalInputRecord(const SimulationInput&, t_inputrec*);
+void                     applyGlobalTopology(const SimulationInput&, gmx_mtop_t*);
+//! \}
+
+/*! \brief Initialize local stateful simulation data.
+ *
+ * Establish an invariant for the simulator at a trajectory point.
+ * Call on all ranks (after domain decomposition and task assignments).
+ *
+ * After this call, the simulator has all of the information it will
+ * receive in order to advance a trajectory from the current step.
+ * Checkpoint information has been applied, if applicable, and stateful
+ * data has been (re)initialized.
+ *
+ * \warning Mdrunner instances do not clearly distinguish between global and local
+ * versions of t_state.
+ *
+ * \todo Factor the distributed data aspects of simulation input data into the
+ *       SimulationInput implementation.
+ *
+ * \todo Consider refactoring to decouple the checkpoint facility from its consumers
+ *       (state, observablesHistory, mdModulesNotifier, and parts of ir).
+ *
+ * \warning It is the caller’s responsibility to make sure that
+ * preconditions are satisfied for the parameter objects.
+ *
+ * \see globalSimulationState()
+ * \see applyGlobalInputRecord()
+ * \see applyGlobalTopology()
+ */
+void applyLocalState(const SimulationInput&         simulationInput,
+                     t_fileio*                      logfio,
+                     const t_commrec*               cr,
+                     int*                           dd_nc,
+                     t_inputrec*                    ir,
+                     t_state*                       state,
+                     ObservablesHistory*            observablesHistory,
+                     bool                           reproducibilityRequested,
+                     const MdModulesNotifier&       notifier,
+                     gmx::ReadCheckpointDataHolder* modularSimulatorCheckpointData,
+                     bool                           useModularSimulator);
+
+} // end namespace gmx
+
+#endif // GMX_MDRUN_SIMULATIONINPUT_H
diff --git a/src/gromacs/mdrun/simulationinputhandle.cpp b/src/gromacs/mdrun/simulationinputhandle.cpp
new file mode 100644 (file)
index 0000000..ae5e2b8
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#include "gmxpre.h"
+
+#include "simulationinputhandle.h"
+
+#include <utility>
+
+#include "gromacs/mdrun/legacymdrunoptions.h"
+
+namespace gmx
+{
+
+/*! \cond
+ * SimulationInput needs much more discussion and refinement before it should be used directly.
+ * Use the interface defined in the headers and refer to #3652 for updates.
+ *
+ * Design note: It is probably sufficient for future versions to compose SimulationInput
+ * through a Builder rather than to subclass an Interface or base class. Outside of this
+ * translation unit, we should avoid coupling to the class definition until/unless we
+ * develop a much better understanding of simulation input portability.
+ *
+ */
+class SimulationInput
+{
+public:
+    SimulationInput(const char* tprFilename, const char* cpiFilename) :
+        tprFilename_(tprFilename),
+        cpiFilename_(cpiFilename)
+    {
+    }
+
+    std::string tprFilename_;
+    std::string cpiFilename_;
+};
+/*! \endcond */
+
+class detail::SimulationInputHandleImpl final
+{
+public:
+    explicit SimulationInputHandleImpl(std::shared_ptr<SimulationInput> input) :
+        simulationInput_{ std::move(input) }
+    {
+    }
+
+    SimulationInputHandleImpl(const SimulationInputHandleImpl&) = default;
+    SimulationInputHandleImpl& operator=(const SimulationInputHandleImpl&) = default;
+    SimulationInputHandleImpl(SimulationInputHandleImpl&&) noexcept        = default;
+    SimulationInputHandleImpl& operator=(SimulationInputHandleImpl&&) noexcept = default;
+
+    [[nodiscard]] SimulationInput* simulationInput() const { return simulationInput_.get(); }
+
+    /*! \brief Boolean interpretation checks for presence of payload.
+     *
+     * \return true if handle has a SimulationInput, else false.
+     */
+    operator bool() const { return bool(simulationInput_); }
+
+private:
+    // Note that we should not need to worry about managing the SimulationInput
+    // Deleter as long as we manage the SimulationInputHolderImpl Deleter and
+    // SimulationInputHolderImpl is the only way to own a SimulationInput.
+    // BUT these semantics may change, and we should try to be responsible about
+    // consistent Allocator implementation.
+    std::shared_ptr<SimulationInput> simulationInput_;
+};
+
+/*! \cond
+ *  TODO: Improve doxygen source scoping.
+ *  It seems surprising that doxygen should be looking at this source file, but we
+ *  need to exclude the following definitions to avoid warnings.
+ *  Possibly related to https://gitlab.com/gromacs/gromacs/-/issues/3402
+ */
+detail::SimulationInputHandleImplDeleter::SimulationInputHandleImplDeleter() = default;
+
+detail::SimulationInputHandleImplDeleter::SimulationInputHandleImplDeleter(
+        const SimulationInputHandleImplDeleter&) noexcept = default;
+
+detail::SimulationInputHandleImplDeleter::SimulationInputHandleImplDeleter(
+        SimulationInputHandleImplDeleter&&) noexcept = default;
+
+detail::SimulationInputHandleImplDeleter& detail::SimulationInputHandleImplDeleter::
+                                          operator=(const SimulationInputHandleImplDeleter&) noexcept = default;
+
+detail::SimulationInputHandleImplDeleter& detail::SimulationInputHandleImplDeleter::
+                                          operator=(SimulationInputHandleImplDeleter&&) noexcept = default;
+
+void detail::SimulationInputHandleImplDeleter::operator()(SimulationInputHandleImpl* impl) const
+{
+    delete impl;
+}
+
+SimulationInputHandle::SimulationInputHandle() = default;
+
+SimulationInputHandle::~SimulationInputHandle() = default;
+
+SimulationInputHandle::SimulationInputHandle(std::unique_ptr<detail::SimulationInputHandleImpl> impl) :
+    impl_(impl.release())
+{
+}
+
+SimulationInputHandle::SimulationInputHandle(const SimulationInputHandle& source)
+{
+    // Dynamically allocate a new holder implementation object using the copy constructor.
+    // Note that make_unique does not provide for custom deleters, and there is no default
+    // overload to move construct/assign from basic unique_ptr to one with custom deleter,
+    // but more elaborate creation functions for SimulationInputHolderImpl seem like
+    // overkill at this point. Still, we use make_unique to confine constructor errors
+    // to the following exception-safe line.
+    auto impl = std::make_unique<detail::SimulationInputHandleImpl>(*source.impl_);
+    // Move ownership of the heap object to the handle with a custom deleter.
+    impl_.reset(impl.release());
+}
+
+SimulationInputHandle& SimulationInputHandle::operator=(const SimulationInputHandle& source)
+{
+    if (this != &source)
+    {
+        // Dynamically allocate a new holder implementation object using the copy constructor.
+        // Note that make_unique does not provide for custom deleters, and there is no default
+        // overload to move construct/assign from basic unique_ptr to one with custom deleter,
+        // but more elaborate creation functions for SimulationInputHolderImpl seem like
+        // overkill at this point. Still, we use make_unique to confine constructor errors
+        // to the following exception-safe line.
+        auto impl = std::make_unique<detail::SimulationInputHandleImpl>(*source.impl_);
+        // Move ownership of the heap object to the handle with a custom deleter.
+        impl_.reset(impl.release());
+    }
+    return *this;
+}
+/*! \endcond */
+
+SimulationInput* SimulationInputHandle::get() const noexcept
+{
+    if (!impl_)
+    {
+        return nullptr;
+    }
+    return impl_->simulationInput();
+}
+
+SimulationInputHandle::operator bool() const
+{
+    if (impl_)
+    {
+        return bool(*impl_);
+    }
+    return false;
+}
+
+SimulationInputHandle makeSimulationInput(const LegacyMdrunOptions& options)
+{
+    // Note: it seems clear that we will want a SimulationInput to be linked to
+    // a communications context (whether the SimulationContext or something higher level)
+    // so that it can encapsulate the data locality details. Otherwise, we have
+    // to choose whether to read the files everywhere or just to store the
+    // filenames until a communications context is known.
+    const auto* const tprFilename = ftp2fn(efTPR, options.filenames.size(), options.filenames.data());
+    const auto* const cpiFilename = opt2fn("-cpi", options.filenames.size(), options.filenames.data());
+    auto              simulationInput = std::make_unique<SimulationInput>(tprFilename, cpiFilename);
+    auto impl = std::make_unique<detail::SimulationInputHandleImpl>(std::move(simulationInput));
+
+    return SimulationInputHandle(std::move(impl));
+}
+
+} // end namespace gmx
diff --git a/src/gromacs/mdrun/simulationinputhandle.h b/src/gromacs/mdrun/simulationinputhandle.h
new file mode 100644 (file)
index 0000000..d5e5bde
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief Public interface for SimulationInput facilities.
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ * \ingroup module_mdrun
+ * \inpublicapi
+ */
+
+#ifndef GMX_MDRUN_SIMULATIONINPUTHANDLE_H
+#define GMX_MDRUN_SIMULATIONINPUTHANDLE_H
+
+#include <memory>
+
+namespace gmx
+{
+
+// Forward declarations for types from other modules that are opaque to the public API.
+class LegacyMdrunOptions;
+
+/*!
+ * \brief Prescription for molecular simulation.
+ *
+ * Represent the complete and unique information needed to generate a simulation
+ * trajectory segment. SimulationInput objects are opaque to the public API.
+ * Ownership can be managed with SimulationInputHolder objects. Clients can
+ * acquire owning references to SimulationInput objects (as SimulationInputHolder)
+ * through makeSimulationInput() or from other SimulationInputHolders.
+ *
+ * A SimulationInput object represents an immutable source of data, and is safe
+ * to share. A SimulationInput object may have internal state to support
+ * performance optimizations when shared by multiple SimulationInputHolders.
+ * The SimulationInput is guaranteed to live at least as long as any associated
+ * SimulationInputHolders. The API does not specify whether it may persist
+ * longer internally or be reused for later equivalent requests.
+ *
+ * \see SimulationInputHandle
+ * \see makeSimulationInput()
+ *
+ * \internal
+ * SimulationInput has no public interface yet, but we need a forward declaration for the
+ * library symbol. Library interface provided through simulationinput.h
+ * See also https://gitlab.com/gromacs/gromacs/-/issues/3379 for design and development road map.
+ */
+class SimulationInput;
+
+/*! \cond internal
+ * Client software developers do not interact directly with the contents of gmx::detail,
+ * but some declarations and definitions are necessary in the public headers, such as
+ * forward declarations of implementation classes and definitions of custom deleters.
+ */
+namespace detail
+{
+/*!
+ * \brief Private implementation class;
+ */
+class SimulationInputHandleImpl;
+
+/*!
+ * \brief Explicit deleter details for SimulationInputHolderImpl.
+ *
+ * SimulationInputHolderImpl objects are created by the GROMACS library, but
+ * are destroyed when the SimulationInputHolder goes out of scope in the client
+ * code, which may be linked to a different allocator.
+ * We want to make sure that objects are allocated and deallocated with the same
+ * allocator, so we avoid the default deleter in unique_ptrs and compile allocation
+ * and deallocation code in the same translation unit.
+ *
+ * Note that this does not solve potential ABI incompatibilities between the
+ * unique_ptr implementations themselves, but we need to consider ABI
+ * compatibility goals and challenges as well as supported use cases and
+ * ownership semantics.
+ */
+struct SimulationInputHandleImplDeleter
+{
+    /*! \cond */
+    SimulationInputHandleImplDeleter();
+    SimulationInputHandleImplDeleter(const SimulationInputHandleImplDeleter& deleter) noexcept;
+    SimulationInputHandleImplDeleter(SimulationInputHandleImplDeleter&& deleter) noexcept;
+    SimulationInputHandleImplDeleter& operator=(const SimulationInputHandleImplDeleter& deleter) noexcept;
+    SimulationInputHandleImplDeleter& operator=(SimulationInputHandleImplDeleter&& deleter) noexcept;
+    void                              operator()(SimulationInputHandleImpl* impl) const;
+    /*! \endcond */
+};
+} // end namespace detail
+/*! \endcond internal */
+
+/*!
+ * \brief Owning handle to a SimulationInput object.
+ *
+ * SimulationInput objects are logically immutable, so ownership may be shared
+ * by multiple SimulationInputHolders.
+ *
+ * Acquire a SimulationInputHolder with makeSimulationInput() and pass to (e.g.)
+ * gmx::MdrunnerBuilder::addInput()
+ *
+ * SimulationInput has no public API yet.
+ * \see https://gitlab.com/gromacs/gromacs/-/issues/3379
+ */
+class SimulationInputHandle
+{
+public:
+    /*! \cond internal */
+    SimulationInputHandle();
+    ~SimulationInputHandle();
+
+    SimulationInputHandle(const SimulationInputHandle& source);
+    SimulationInputHandle(SimulationInputHandle&&) noexcept = default;
+
+    SimulationInputHandle& operator=(const SimulationInputHandle& rhs);
+    SimulationInputHandle& operator=(SimulationInputHandle&&) noexcept = default;
+
+    /*!
+     * \brief Take ownership of private implementation object to produce a new public holder.
+     */
+    explicit SimulationInputHandle(std::unique_ptr<detail::SimulationInputHandleImpl> impl);
+    /*! \endcond */
+
+    /*!
+     * \brief Access opaque SimulationInput pointer.
+     *
+     * \return Borrowed access to the SimulationInput, if present.
+     */
+    [[nodiscard]] SimulationInput* get() const noexcept;
+
+    /*!
+     * \brief Boolean context returns true if an input is held, else false.
+     */
+    operator bool() const;
+
+private:
+    std::unique_ptr<detail::SimulationInputHandleImpl, detail::SimulationInputHandleImplDeleter> impl_;
+};
+
+/*! \brief Direct the construction of a SimulationInput.
+ *
+ * \warning Creation methods for SimulationInput resources are under rapid development.
+ * Reference https://gitlab.com/gromacs/gromacs/-/issues/3652
+ *
+ * \param options library-internal container holding partially processed user input.
+ *
+ * \ingroup module_mdrun
+ *
+ * \internal
+ * This isn't really a public API function until its arguments are obtainable
+ * through the public API.
+ *
+ * Design notes: SimulationInput creation will warrant a builder protocol, and
+ * this helper can evolve into a director to apply the contents of LegacyMdrunOptions,
+ * while such an operation is still relevant.
+ *
+ * Example:
+ *     // After preparing a LegacyMdrunOptions and calling handleRestart()...
+ *     SimulationInputBuilder builder;
+ *     auto simulationInputHandle = makeSimulationInput(options, &builder);
+ *
+ *     // In addition to MdrunnerBuilder::addFiles(),
+ *     mdrunnerBuilder.addInput(simulationInputHandle.get());
+ *
+ */
+SimulationInputHandle makeSimulationInput(const LegacyMdrunOptions& options);
+
+} // end namespace gmx
+
+#endif // GMX_MDRUN_SIMULATIONINPUTHANDLE_H
diff --git a/src/gromacs/mdrun/simulatorbuilder.cpp b/src/gromacs/mdrun/simulatorbuilder.cpp
new file mode 100644 (file)
index 0000000..f1ed995
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019-2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Defines the simulator builder for mdrun
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_mdrun
+ */
+
+#include "gmxpre.h"
+
+#include "simulatorbuilder.h"
+
+#include <memory>
+
+#include "gromacs/mdlib/vsite.h"
+#include "gromacs/mdtypes/checkpointdata.h"
+#include "gromacs/mdtypes/mdrunoptions.h"
+#include "gromacs/mdtypes/state.h"
+#include "gromacs/modularsimulator/modularsimulator.h"
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/mdmodulenotification.h"
+
+#include "legacysimulator.h"
+#include "membedholder.h"
+#include "replicaexchange.h"
+
+
+namespace gmx
+{
+
+//! \brief Build a Simulator object
+std::unique_ptr<ISimulator> SimulatorBuilder::build(bool useModularSimulator)
+{
+    // TODO: Reduce protocol complexity.
+    //     Investigate individual paramters. Identify default-constructable parameters and clarify
+    //     usage requirements.
+    if (!stopHandlerBuilder_)
+    {
+        throw APIError("You must add a StopHandlerBuilder before calling build().");
+    }
+    if (!membedHolder_)
+    {
+        throw APIError("You must add a MembedHolder before calling build().");
+    }
+    if (!simulatorStateData_)
+    {
+        throw APIError("Simulator State Data has not been added to the builder");
+    }
+    if (!simulatorConfig_)
+    {
+        throw APIError("Simulator config should be set before building the simulator");
+    }
+    if (!simulatorEnv_)
+    {
+        throw APIError("You must add a SimulatorEnv before calling build().");
+    }
+    if (!profiling_)
+    {
+        throw APIError("You must add a Profiling before calling build().");
+    }
+    if (!constraintsParam_)
+    {
+        throw APIError("You must add a ConstraintsParam before calling build().");
+    }
+    if (!legacyInput_)
+    {
+        throw APIError("You must add a LegacyInput before calling build().");
+    }
+    if (!replicaExchangeParameters_)
+    {
+        throw APIError("You must add a ReplicaExchangeParameters before calling build().");
+    }
+    if (!interactiveMD_)
+    {
+        throw APIError("You must add a InteractiveMD before calling build().");
+    }
+    if (!simulatorModules_)
+    {
+        throw APIError("You must add a SimulatorModules before calling build().");
+    }
+    if (!centerOfMassPulling_)
+    {
+        throw APIError("You must add a CenterOfMassPulling before calling build().");
+    }
+    if (!ionSwapping_)
+    {
+        throw APIError("You must add a IonSwapping before calling build().");
+    }
+    if (!topologyData_)
+    {
+        throw APIError("You must add a TopologyData before calling build().");
+    }
+
+    if (useModularSimulator)
+    {
+        // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
+        return std::unique_ptr<ModularSimulator>(new ModularSimulator(
+                std::make_unique<LegacySimulatorData>(
+                        simulatorEnv_->fplog_, simulatorEnv_->commRec_, simulatorEnv_->multisimCommRec_,
+                        simulatorEnv_->logger_, legacyInput_->numFile, legacyInput_->filenames,
+                        simulatorEnv_->outputEnv_, simulatorConfig_->mdrunOptions_,
+                        simulatorConfig_->startingBehavior_, constraintsParam_->vsite,
+                        constraintsParam_->constr, constraintsParam_->enforcedRotation,
+                        boxDeformation_->deform, simulatorModules_->outputProvider,
+                        simulatorModules_->mdModulesNotifier, legacyInput_->inputrec,
+                        interactiveMD_->imdSession, centerOfMassPulling_->pull_work, ionSwapping_->ionSwap,
+                        topologyData_->top_global, simulatorStateData_->globalState_p,
+                        simulatorStateData_->observablesHistory_p, topologyData_->mdAtoms,
+                        profiling_->nrnb, profiling_->wallCycle, legacyInput_->forceRec,
+                        simulatorStateData_->enerdata_p, simulatorStateData_->ekindata_p,
+                        simulatorConfig_->runScheduleWork_, *replicaExchangeParameters_,
+                        membedHolder_->membed(), profiling_->walltimeAccounting,
+                        std::move(stopHandlerBuilder_), simulatorConfig_->mdrunOptions_.rerun),
+                std::move(modularSimulatorCheckpointData_)));
+    }
+    // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
+    return std::unique_ptr<LegacySimulator>(new LegacySimulator(
+            simulatorEnv_->fplog_, simulatorEnv_->commRec_, simulatorEnv_->multisimCommRec_,
+            simulatorEnv_->logger_, legacyInput_->numFile, legacyInput_->filenames,
+            simulatorEnv_->outputEnv_, simulatorConfig_->mdrunOptions_,
+            simulatorConfig_->startingBehavior_, constraintsParam_->vsite,
+            constraintsParam_->constr, constraintsParam_->enforcedRotation, boxDeformation_->deform,
+            simulatorModules_->outputProvider, simulatorModules_->mdModulesNotifier,
+            legacyInput_->inputrec, interactiveMD_->imdSession, centerOfMassPulling_->pull_work,
+            ionSwapping_->ionSwap, topologyData_->top_global, simulatorStateData_->globalState_p,
+            simulatorStateData_->observablesHistory_p, topologyData_->mdAtoms, profiling_->nrnb,
+            profiling_->wallCycle, legacyInput_->forceRec, simulatorStateData_->enerdata_p,
+            simulatorStateData_->ekindata_p, simulatorConfig_->runScheduleWork_,
+            *replicaExchangeParameters_, membedHolder_->membed(), profiling_->walltimeAccounting,
+            std::move(stopHandlerBuilder_), simulatorConfig_->mdrunOptions_.rerun));
+}
+
+void SimulatorBuilder::add(MembedHolder&& membedHolder)
+{
+    membedHolder_ = std::make_unique<MembedHolder>(std::move(membedHolder));
+}
+
+void SimulatorBuilder::add(ReplicaExchangeParameters&& replicaExchangeParameters)
+{
+    replicaExchangeParameters_ = std::make_unique<ReplicaExchangeParameters>(replicaExchangeParameters);
+}
+
+void SimulatorBuilder::add(std::unique_ptr<ReadCheckpointDataHolder> modularSimulatorCheckpointData)
+{
+    modularSimulatorCheckpointData_ = std::move(modularSimulatorCheckpointData);
+}
+
+
+} // namespace gmx
index df78b804efb3d75d3912b0deaeb92ca324a30307..761d3af220ebee7aff52ec1f26d14837a992eefb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019-2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <memory>
 
-#include "gromacs/modularsimulator/modularsimulator.h"
-
-#include "legacysimulator.h"
 
 class energyhistory_t;
+struct gmx_ekindata_t;
 struct gmx_enerdata_t;
 struct gmx_enfrot;
 struct gmx_mtop_t;
-struct gmx_membed_t;
 struct gmx_multisim_t;
 struct gmx_output_env_t;
-struct gmx_vsite_t;
 struct gmx_wallcycle;
 struct gmx_walltime_accounting;
 struct ObservablesHistory;
 struct pull_t;
 struct ReplicaExchangeParameters;
 struct t_commrec;
-struct t_fcdata;
-struct t_forcerec;
 struct t_filenm;
+struct t_forcerec;
 struct t_inputrec;
 struct t_nrnb;
-struct t_swap;
 class t_state;
+struct t_swap;
 
 namespace gmx
 {
-enum class StartingBehavior;
 class BoxDeformation;
 class Constraints;
-class MdrunScheduleWorkload;
 class IMDOutputProvider;
 class ImdSession;
-class MDLogger;
-class MDAtoms;
 class ISimulator;
-class StopHandlerBuilder;
+class MdrunScheduleWorkload;
+class MembedHolder;
+class MDAtoms;
+class MDLogger;
+struct MdModulesNotifier;
 struct MdrunOptions;
+class ReadCheckpointDataHolder;
+enum class StartingBehavior;
+class StopHandlerBuilder;
+class VirtualSitesHandler;
+
+/*! \brief
+ * Simulation configuation settings.
+ */
+struct SimulatorConfig
+{
+public:
+    //! Build from settings for this simulation.
+    SimulatorConfig(const MdrunOptions&    mdrunOptions,
+                    StartingBehavior       startingBehavior,
+                    MdrunScheduleWorkload* runScheduleWork) :
+        mdrunOptions_(mdrunOptions),
+        startingBehavior_(startingBehavior),
+        runScheduleWork_(runScheduleWork)
+    {
+    }
+    // TODO: Specify copy and move semantics.
+
+    //! Handle to user options.
+    const MdrunOptions& mdrunOptions_;
+    //! How are we starting the simulation.
+    StartingBehavior startingBehavior_;
+    //! How are we scheduling the tasks for this simulation.
+    MdrunScheduleWorkload* runScheduleWork_;
+};
+
+
+/*! \brief
+ * Data for a specific simulation state.
+ *
+ * \todo Think of a better name and annoy people that forget
+ *       to add documentation for their code.
+ */
+struct SimulatorStateData
+{
+    //! Build collection of current state data.
+    SimulatorStateData(t_state*            globalState,
+                       ObservablesHistory* observablesHistory,
+                       gmx_enerdata_t*     enerdata,
+                       gmx_ekindata_t*     ekindata) :
+        globalState_p(globalState),
+        observablesHistory_p(observablesHistory),
+        enerdata_p(enerdata),
+        ekindata_p(ekindata)
+    {
+    }
+
+    //! Can perform copy of current state.
+    SimulatorStateData(const SimulatorStateData& simulatorStateData) = default;
+
+    //! Handle to global state of the simulation.
+    t_state* globalState_p;
+    //! Handle to current simulation history.
+    ObservablesHistory* observablesHistory_p;
+    //! Handle to collected data for energy groups.
+    gmx_enerdata_t* enerdata_p;
+    //! Handle to collected data for kinectic energy.
+    gmx_ekindata_t* ekindata_p;
+};
+
+/*! \brief
+ * Collection of environmental information for a simulation.
+ *
+ * \todo Fix doxygen checks.
+ */
+class SimulatorEnv
+{
+public:
+    //! Build from current simulation environment.
+    SimulatorEnv(FILE*             fplog,
+                 t_commrec*        commRec,
+                 gmx_multisim_t*   multisimCommRec,
+                 const MDLogger&   logger,
+                 gmx_output_env_t* outputEnv) :
+        fplog_{ fplog },
+        commRec_{ commRec },
+        multisimCommRec_{ multisimCommRec },
+        logger_{ logger },
+        outputEnv_{ outputEnv }
+    {
+    }
+
+    //! Handle to log file.
+    FILE* fplog_;
+    //! Handle to communication record.
+    t_commrec* commRec_;
+    //! Handle to multisim communication record.
+    const gmx_multisim_t* multisimCommRec_;
+    //! Handle to propper logging framework.
+    const MDLogger& logger_;
+    //! Handle to file output handling.
+    const gmx_output_env_t* outputEnv_;
+};
+
+/*! \brief
+ * Collection of profiling information.
+ */
+class Profiling
+{
+public:
+    //! Build profiling information collection.
+    Profiling(t_nrnb* nrnb, gmx_walltime_accounting* walltimeAccounting, gmx_wallcycle* wallCycle) :
+        nrnb(nrnb),
+        wallCycle(wallCycle),
+        walltimeAccounting(walltimeAccounting)
+    {
+    }
+
+    //! Handle to datastructure.
+    t_nrnb* nrnb;
+    //! Handle to wallcycle stuff.
+    gmx_wallcycle* wallCycle;
+    //! Handle to wallcycle time accounting stuff.
+    gmx_walltime_accounting* walltimeAccounting;
+};
+
+/*! \brief
+ * Collection of constraint parameters.
+ */
+class ConstraintsParam
+{
+public:
+    //! Build collection with handle to actual objects.
+    ConstraintsParam(Constraints* constraints, gmx_enfrot* enforcedRotation, VirtualSitesHandler* vSite) :
+        constr(constraints),
+        enforcedRotation(enforcedRotation),
+        vsite(vSite)
+    {
+    }
+
+    //! Handle to constraint object.
+    Constraints* constr;
+    //! Handle to information about using enforced rotation.
+    gmx_enfrot* enforcedRotation;
+    //! Handle to vsite stuff.
+    VirtualSitesHandler* vsite;
+};
+
+/*! \brief
+ * Collection of legacy input information.
+ */
+class LegacyInput
+{
+public:
+    //! Build collection from legacy input data.
+    LegacyInput(int filenamesSize, const t_filenm* filenamesData, t_inputrec* inputRec, t_forcerec* forceRec) :
+        numFile(filenamesSize),
+        filenames(filenamesData),
+        inputrec(inputRec),
+        forceRec(forceRec)
+    {
+    }
+
+    //! Number of input files.
+    int numFile;
+    //! File names.
+    const t_filenm* filenames;
+    //! Handle to simulation input record.
+    t_inputrec* inputrec;
+    //! Handle to simulation force record.
+    t_forcerec* forceRec;
+};
+
+/*! \brief SimulatorBuilder parameter type for InteractiveMD.
+ *
+ * Conveys a non-owning pointer to implementation details.
+ *
+ * \todo If adding doxygen stubs actual add the full stub.
+ */
+class InteractiveMD
+{
+public:
+    //! Create handle to IMD information.
+    explicit InteractiveMD(ImdSession* imdSession) : imdSession(imdSession) {}
+
+    //! Internal handle to IMD info.
+    ImdSession* imdSession;
+};
+
+class SimulatorModules
+{
+public:
+    SimulatorModules(IMDOutputProvider* mdOutputProvider, const MdModulesNotifier& notifier) :
+        outputProvider(mdOutputProvider),
+        mdModulesNotifier(notifier)
+    {
+    }
+
+    IMDOutputProvider*       outputProvider;
+    const MdModulesNotifier& mdModulesNotifier;
+};
+
+class CenterOfMassPulling
+{
+public:
+    explicit CenterOfMassPulling(pull_t* pullWork) : pull_work(pullWork) {}
+
+    pull_t* pull_work;
+};
+
+/*! \brief
+ * Parameter type for IonSwapping SimulatorBuilder component.
+ *
+ * Conveys a non-owning pointer to implementation details.
+ *
+ * \todo Add full information.
+ */
+class IonSwapping
+{
+public:
+    //! Create handle.
+    IonSwapping(t_swap* ionSwap) : ionSwap(ionSwap) {}
+
+    //! Internal storage for handle.
+    t_swap* ionSwap;
+};
+
+/*! \brief
+ * Collection of handles to topology information.
+ */
+class TopologyData
+{
+public:
+    //! Build collection from simulation data.
+    TopologyData(gmx_mtop_t* globalTopology, MDAtoms* mdAtoms) :
+        top_global(globalTopology),
+        mdAtoms(mdAtoms)
+    {
+    }
+
+    //! Handle to global simulation topology.
+    gmx_mtop_t* top_global;
+    //! Handle to information about MDAtoms.
+    MDAtoms* mdAtoms;
+};
+
+/*! \brief
+ * Handle to information about the box.
+ *
+ * Design note: The client may own the BoxDeformation via std::unique_ptr, but we are not
+ * transferring ownership at this time. (May be the subject of future changes.)
+ */
+class BoxDeformationHandle
+{
+public:
+    //! Build handle to box stuff.
+    BoxDeformationHandle(BoxDeformation* boxDeformation) : deform(boxDeformation) {}
+
+    //! Internal storage for handle.
+    BoxDeformation* deform;
+};
 
 /*! \libinternal
  * \brief Class preparing the creation of Simulator objects
  *
  * Objects of this class build Simulator objects, which in turn are used to
- * run molecular simulations. Currently, this only has a single public
- * `build` function which takes all arguments needed to build the
- * `LegacySimulator`.
+ * run molecular simulations.
  */
 class SimulatorBuilder
 {
 public:
+    void add(MembedHolder&& membedHolder);
+
+    void add(std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder)
+    {
+        stopHandlerBuilder_ = std::move(stopHandlerBuilder);
+    }
+
+    void add(SimulatorStateData&& simulatorStateData)
+    {
+        simulatorStateData_ = std::make_unique<SimulatorStateData>(simulatorStateData);
+    }
+
+    void add(SimulatorConfig&& simulatorConfig)
+    {
+        // Note: SimulatorConfig appears to the compiler to be trivially copyable,
+        // but this may not be safe and may change in the future.
+        simulatorConfig_ = std::make_unique<SimulatorConfig>(simulatorConfig);
+    }
+
+    void add(SimulatorEnv&& simulatorEnv)
+    {
+        simulatorEnv_ = std::make_unique<SimulatorEnv>(simulatorEnv);
+    }
+
+    void add(Profiling&& profiling) { profiling_ = std::make_unique<Profiling>(profiling); }
+
+    void add(ConstraintsParam&& constraintsParam)
+    {
+        constraintsParam_ = std::make_unique<ConstraintsParam>(constraintsParam);
+    }
+
+    void add(LegacyInput&& legacyInput)
+    {
+        legacyInput_ = std::make_unique<LegacyInput>(legacyInput);
+    }
+
+    void add(ReplicaExchangeParameters&& replicaExchangeParameters);
+
+    void add(InteractiveMD&& interactiveMd)
+    {
+        interactiveMD_ = std::make_unique<InteractiveMD>(interactiveMd);
+    }
+
+    void add(SimulatorModules&& simulatorModules)
+    {
+        simulatorModules_ = std::make_unique<SimulatorModules>(simulatorModules);
+    }
+
+    void add(CenterOfMassPulling&& centerOfMassPulling)
+    {
+        centerOfMassPulling_ = std::make_unique<CenterOfMassPulling>(centerOfMassPulling);
+    }
+
+    void add(IonSwapping&& ionSwapping)
+    {
+        ionSwapping_ = std::make_unique<IonSwapping>(ionSwapping);
+    }
+
+    void add(TopologyData&& topologyData)
+    {
+        topologyData_ = std::make_unique<TopologyData>(topologyData);
+    }
+
+    void add(BoxDeformationHandle&& boxDeformation)
+    {
+        boxDeformation_ = std::make_unique<BoxDeformationHandle>(boxDeformation);
+    }
+
+    /*!
+     * \brief Pass the read checkpoint data for modular simulator
+     *
+     * Note that this is currently the point at which the ReadCheckpointDataHolder
+     * is fully filled. Consequently it stops being an object at which read
+     * operations from file are targeted, and becomes a read-only object from
+     * which elements read their data to recreate an earlier internal state.
+     *
+     * Currently, this behavior change is not enforced. Once input reading and
+     * simulator builder have matured, these restrictions could be imposed.
+     *
+     * See #3656
+     */
+    void add(std::unique_ptr<ReadCheckpointDataHolder> modularSimulatorCheckpointData);
+
     /*! \brief Build a Simulator object based on input data
      *
      * Return a pointer to a simulation object. The use of a parameter
      * pack insulates the builder from changes to the arguments of the
      * Simulator objects.
      *
-     * @return  Unique pointer to a Simulator object
+     * \throws gmx::APIError if expected set-up methods have not been called before build()
+     *
+     * \return  Unique pointer to a Simulator object
      */
-    template<typename... Args>
-    std::unique_ptr<ISimulator> build(bool inputIsCompatibleWithModularSimulator, Args&&... args);
-};
-
+    std::unique_ptr<ISimulator> build(bool useModularSimulator);
 
-//! Build a Simulator object
-template<typename... Args>
-std::unique_ptr<ISimulator> SimulatorBuilder::build(bool inputIsCompatibleWithModularSimulator, Args&&... args)
-{
-    // GMX_DISABLE_MODULAR_SIMULATOR allows to disable modular simulator for all uses
-    const auto disableModularSimulator = (getenv("GMX_DISABLE_MODULAR_SIMULATOR") != nullptr);
-
-    if (!disableModularSimulator && inputIsCompatibleWithModularSimulator)
-    {
-        // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
-        return std::unique_ptr<ModularSimulator>(new ModularSimulator(std::forward<Args>(args)...));
-    }
-    // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
-    return std::unique_ptr<LegacySimulator>(new LegacySimulator(std::forward<Args>(args)...));
-}
+private:
+    // Note: we use std::unique_ptr instead of std::optional because we want to
+    // allow for opaque types at the discretion of the module developer.
+    /*! \brief Collection of handles to  individual information. */
+    /*! \{ */
+    std::unique_ptr<SimulatorConfig>           simulatorConfig_;
+    std::unique_ptr<MembedHolder>              membedHolder_;
+    std::unique_ptr<StopHandlerBuilder>        stopHandlerBuilder_;
+    std::unique_ptr<SimulatorStateData>        simulatorStateData_;
+    std::unique_ptr<SimulatorEnv>              simulatorEnv_;
+    std::unique_ptr<Profiling>                 profiling_;
+    std::unique_ptr<ConstraintsParam>          constraintsParam_;
+    std::unique_ptr<LegacyInput>               legacyInput_;
+    std::unique_ptr<ReplicaExchangeParameters> replicaExchangeParameters_;
+    std::unique_ptr<InteractiveMD>             interactiveMD_;
+    std::unique_ptr<SimulatorModules>          simulatorModules_;
+    std::unique_ptr<CenterOfMassPulling>       centerOfMassPulling_;
+    std::unique_ptr<IonSwapping>               ionSwapping_;
+    std::unique_ptr<TopologyData>              topologyData_;
+    std::unique_ptr<BoxDeformationHandle>      boxDeformation_;
+    //! Contains checkpointing data for the modular simulator
+    std::unique_ptr<ReadCheckpointDataHolder> modularSimulatorCheckpointData_;
+    /*! \} */
+};
 
 } // namespace gmx
 
index 2ed8bd4586eb53126c8881264dbea9f9eb3ce17c..7536de08ebf5c8a641f6f670309453d292ec4f64 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,7 +50,6 @@
 #include <ctime>
 
 #include <algorithm>
-
 #include <cfenv>
 
 #include "gromacs/commandline/filenm.h"
 #include "gromacs/mdlib/vsite.h"
 #include "gromacs/mdrunutility/printtime.h"
 #include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/forcebuffers.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/mdrunoptions.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/nbnxm/nbnxm.h"
@@ -162,36 +165,47 @@ void LegacySimulator::do_tpi()
 {
     GMX_RELEASE_ASSERT(gmx_omp_nthreads_get(emntDefault) == 1, "TPI does not support OpenMP");
 
-    gmx_localtop_t              top;
-    PaddedHostVector<gmx::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;
-    tensor                      force_vir, shake_vir, vir, pres;
-    int                         a_tp0, a_tp1, ngid, gid_tp, nener, e;
-    rvec*                       x_mol;
-    rvec                        mu_tot, x_init, dx;
-    int                         nnodes, frame;
-    int64_t                     frame_step_prev, frame_step;
-    int64_t                     nsteps, stepblocksize = 0, step;
-    int64_t                     seed;
-    int                         i;
-    FILE*                       fp_tpi = nullptr;
-    char *                      ptr, *dump_pdb, **leg, str[STRLEN], str2[STRLEN];
-    double                      dbl, dump_ener;
-    gmx_bool                    bCavity;
-    int                         nat_cavity  = 0, d;
-    real *                      mass_cavity = nullptr, mass_tot;
-    int                         nbin;
-    double                      invbinw, *bin, refvolshift, logV, bUlogV;
-    gmx_bool                    bEnergyOutOfBounds;
-    const char*                 tpid_leg[2] = { "direct", "reweighted" };
-    auto                        mdatoms     = mdAtoms->mdatoms();
+    gmx_localtop_t    top(top_global->ffparams);
+    gmx::ForceBuffers 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;
+    tensor            force_vir, shake_vir, vir, pres;
+    int               a_tp0, a_tp1, ngid, gid_tp, nener, e;
+    rvec*             x_mol;
+    rvec              mu_tot, x_init, dx;
+    int               nnodes, frame;
+    int64_t           frame_step_prev, frame_step;
+    int64_t           nsteps, stepblocksize = 0, step;
+    int64_t           seed;
+    int               i;
+    FILE*             fp_tpi = nullptr;
+    char *            ptr, *dump_pdb, **leg, str[STRLEN], str2[STRLEN];
+    double            dbl, dump_ener;
+    gmx_bool          bCavity;
+    int               nat_cavity  = 0, d;
+    real *            mass_cavity = nullptr, mass_tot;
+    int               nbin;
+    double            invbinw, *bin, refvolshift, logV, bUlogV;
+    gmx_bool          bEnergyOutOfBounds;
+    const char*       tpid_leg[2] = { "direct", "reweighted" };
+    auto              mdatoms     = mdAtoms->mdatoms();
 
     GMX_UNUSED_VALUE(outputProvider);
 
+    if (EVDW_PME(inputrec->vdwtype))
+    {
+        gmx_fatal(FARGS, "Test particle insertion not implemented with LJ-PME");
+    }
+    if (haveEwaldSurfaceContribution(*inputrec))
+    {
+        gmx_fatal(FARGS,
+                  "TPI with PME currently only works in a 3D geometry with tin-foil "
+                  "boundary conditions");
+    }
+
     GMX_LOG(mdlog.info)
             .asParagraph()
             .appendText(
@@ -209,7 +223,7 @@ void LegacySimulator::do_tpi()
 
     gmx_mtop_generate_local_top(*top_global, &top, inputrec->efep != efepNO);
 
-    SimulationGroups* groups = &top_global->groups;
+    const SimulationGroups* groups = &top_global->groups;
 
     bCavity = (inputrec->eI == eiTPIC);
     if (bCavity)
@@ -244,7 +258,7 @@ void LegacySimulator::do_tpi()
        init_em(fplog,TPI,inputrec,&lambda,nrnb,mu_tot,
        state_global->box,fr,mdatoms,top,cr,nfile,fnm,NULL,NULL);*/
     /* We never need full pbc for TPI */
-    fr->ePBC = epbcXYZ;
+    fr->pbcType = PbcType::Xyz;
     /* Determine the temperature for the Boltzmann weighting */
     temp = inputrec->opts.ref_t[0];
     if (fplog)
@@ -284,10 +298,10 @@ void LegacySimulator::do_tpi()
         sscanf(dump_pdb, "%20lf", &dump_ener);
     }
 
-    atoms2md(top_global, inputrec, -1, nullptr, top_global->natoms, mdAtoms);
+    atoms2md(top_global, inputrec, -1, {}, top_global->natoms, mdAtoms);
     update_mdatoms(mdatoms, inputrec->fepvals->init_lambda);
 
-    f.resizeWithPadding(top_global->natoms);
+    f.resize(top_global->natoms);
 
     /* Print to log file  */
     walltime_accounting_start_time(walltime_accounting);
@@ -307,7 +321,7 @@ void LegacySimulator::do_tpi()
 
     if (EEL_PME(fr->ic->eeltype))
     {
-        gmx_pme_reinit_atoms(fr->pmedata, a_tp0, nullptr);
+        gmx_pme_reinit_atoms(fr->pmedata, a_tp0, nullptr, nullptr);
     }
 
     /* With reacion-field we have distance dependent potentials
@@ -583,7 +597,7 @@ void LegacySimulator::do_tpi()
         bStateChanged = TRUE;
         bNS           = TRUE;
 
-        put_atoms_in_box(fr->ePBC, box, x);
+        put_atoms_in_box(fr->pbcType, box, x);
 
         /* Put all atoms except for the inserted ones on the grid */
         rvec vzero       = { 0, 0, 0 };
@@ -664,9 +678,11 @@ void LegacySimulator::do_tpi()
                                   -1, fr->cginfo, x, 0, nullptr);
 
                 /* TODO: Avoid updating all atoms at every bNS step */
-                fr->nbv->setAtomProperties(*mdatoms, fr->cginfo);
+                fr->nbv->setAtomProperties(gmx::constArrayRefFromArray(mdatoms->typeA, mdatoms->nr),
+                                           gmx::constArrayRefFromArray(mdatoms->chargeA, mdatoms->nr),
+                                           fr->cginfo);
 
-                fr->nbv->constructPairlist(InteractionLocality::Local, &top.excls, step, nrnb);
+                fr->nbv->constructPairlist(InteractionLocality::Local, top.excls, step, nrnb);
 
                 bNS = FALSE;
             }
@@ -727,12 +743,7 @@ void LegacySimulator::do_tpi()
             clear_mat(vir);
             clear_mat(pres);
 
-            /* 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. */
+            /* Calc energy (no forces) on new positions. */
             /* Make do_force do a single node force calculation */
             cr->nnodes = 1;
 
@@ -744,8 +755,8 @@ void LegacySimulator::do_tpi()
             std::feholdexcept(&floatingPointEnvironment);
             do_force(fplog, cr, ms, inputrec, nullptr, nullptr, imdSession, pull_work, step, nrnb,
                      wcycle, &top, state_global->box, state_global->x.arrayRefWithPadding(),
-                     &state_global->hist, f.arrayRefWithPadding(), force_vir, mdatoms, enerd, fcd,
-                     state_global->lambda, nullptr, fr, runScheduleWork, nullptr, mu_tot, t, nullptr,
+                     &state_global->hist, &f.view(), force_vir, mdatoms, enerd,
+                     state_global->lambda, fr, runScheduleWork, nullptr, mu_tot, t, nullptr,
                      GMX_FORCE_NONBONDED | GMX_FORCE_ENERGY | (bStateChanged ? GMX_FORCE_STATECHANGED : 0),
                      DDBalanceRegionHandler(nullptr));
             std::feclearexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
@@ -877,7 +888,7 @@ void LegacySimulator::do_tpi()
                 sprintf(str, "t%g_step%d.pdb", t, static_cast<int>(step));
                 sprintf(str2, "t: %f step %d ener: %f", t, static_cast<int>(step), epot);
                 write_sto_conf_mtop(str, str2, top_global, state_global->x.rvec_array(),
-                                    state_global->v.rvec_array(), inputrec->ePBC, state_global->box);
+                                    state_global->v.rvec_array(), inputrec->pbcType, state_global->box);
             }
 
             step++;
index d6fbdb1a762489d9906cd4abb81d731e7ecce62a..b21f11302978c1c0019ac4a10592e7fe21c9e2d8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,6 +53,7 @@
 
 #include "config.h"
 
+#include <cerrno>
 #include <cstring>
 
 #include <fcntl.h>
 #include <algorithm>
 #include <exception>
 #include <functional>
+#include <optional>
 #include <tuple>
 
 #include "gromacs/commandline/filenm.h"
-#include "gromacs/compat/optional.h"
 #include "gromacs/fileio/checkpoint.h"
 #include "gromacs/fileio/gmxfio.h"
 #include "gromacs/mdrunutility/multisim.h"
@@ -191,14 +192,14 @@ public:
      *                                (relevant only when restarting)
      *
      * Does not throw */
-    compat::optional<int> makeIndexOfNextPart(AppendingBehavior appendingBehavior) const;
+    std::optional<int> makeIndexOfNextPart(AppendingBehavior appendingBehavior) const;
 
     //! Describes how mdrun will (re)start
     StartingBehavior startingBehavior = StartingBehavior::NewSimulation;
     //! When restarting from a checkpoint, contains the contents of its header
-    compat::optional<CheckpointHeaderContents> headerContents;
+    std::optional<CheckpointHeaderContents> headerContents;
     //! When restarting from a checkpoint, contains the names of expected output files
-    compat::optional<std::vector<gmx_file_position_t>> outputFiles;
+    std::optional<std::vector<gmx_file_position_t>> outputFiles;
 };
 
 /*! \brief Choose the starting behaviour for this simulation
@@ -532,7 +533,7 @@ checkpoint file was written).
 To help you identify which directories need attention, the %d
 simulations wanted the following respective behaviors:
 )",
-                                           ms->nsim);
+                                           ms->numSimulations_);
         for (index simIndex = 0; simIndex != ssize(startingBehaviors); ++simIndex)
         {
             auto behavior = static_cast<StartingBehavior>(startingBehaviors[simIndex]);
@@ -572,7 +573,7 @@ To help you identify which directories need attention, the %d
 simulation checkpoint files were from the following respective
 simulation parts:
 )",
-                                           ms->nsim);
+                                           ms->numSimulations_);
         for (index partIndex = 0; partIndex != ssize(simulationParts); ++partIndex)
         {
             message += formatString("  Simulation %6zd: %d\n", partIndex, simulationParts[partIndex]);
@@ -581,9 +582,9 @@ simulation parts:
     }
 }
 
-compat::optional<int> StartingBehaviorHandler::makeIndexOfNextPart(const AppendingBehavior appendingBehavior) const
+std::optional<int> StartingBehaviorHandler::makeIndexOfNextPart(const AppendingBehavior appendingBehavior) const
 {
-    compat::optional<int> indexOfNextPart;
+    std::optional<int> indexOfNextPart;
 
     if (startingBehavior == StartingBehavior::RestartWithoutAppending)
     {
@@ -627,7 +628,7 @@ std::tuple<StartingBehavior, LogFilePtr> handleRestart(const bool              i
             handler.ensureMultiSimBehaviorsMatch(ms);
 
             // When not appending, prepare a suffix for the part number
-            compat::optional<int> indexOfNextPart = handler.makeIndexOfNextPart(appendingBehavior);
+            std::optional<int> indexOfNextPart = handler.makeIndexOfNextPart(appendingBehavior);
 
             // If a part suffix is used, change the file names accordingly.
             if (indexOfNextPart)
index 290a1003a3f77a6e6abc570afd7d37f7514c823d..70aba55b948f4cfa45462e09ab322756c5385a27 100644 (file)
 
 #include "config.h"
 
+#include "gromacs/gmxlib/network.h"
 #include "gromacs/mdtypes/commrec.h"
+#include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/futil.h"
 #include "gromacs/utility/gmxassert.h"
-#include "gromacs/utility/mpiinplacebuffers.h"
+#include "gromacs/utility/logger.h"
 #include "gromacs/utility/smalloc.h"
 
-gmx_multisim_t::gmx_multisim_t() = default;
-
-gmx_multisim_t::gmx_multisim_t(MPI_Comm comm, gmx::ArrayRef<const std::string> multidirs)
+std::unique_ptr<gmx_multisim_t> buildMultiSimulation(MPI_Comm                         worldComm,
+                                                     gmx::ArrayRef<const std::string> multidirs)
 {
     if (multidirs.empty())
     {
-        return;
+        return nullptr;
     }
 
     if (!GMX_LIB_MPI && !multidirs.empty())
     {
-        gmx_fatal(FARGS,
-                  "mdrun -multidir is only supported when GROMACS has been "
-                  "configured with a proper external MPI library.");
+        GMX_THROW(gmx::NotImplementedError(
+                "Multi-simulations are only supported when GROMACS has been "
+                "configured with a proper external MPI library."));
     }
 
     if (multidirs.size() == 1)
     {
         /* NOTE: It would be nice if this special case worked, but this requires checks/tests. */
-        gmx_fatal(FARGS,
-                  "To run mdrun in multiple simulation mode, more then one "
-                  "actual simulation is required. The single simulation case is not supported.");
+        GMX_THROW(gmx::NotImplementedError(
+                "To run mdrun in multi-simulation mode, more then one "
+                "actual simulation is required. The single simulation case is not supported."));
     }
 
-#if GMX_MPI
+#if GMX_LIB_MPI
     int numRanks;
-    MPI_Comm_size(comm, &numRanks);
+    MPI_Comm_size(worldComm, &numRanks);
     if (numRanks % multidirs.size() != 0)
     {
-        gmx_fatal(FARGS,
-                  "The number of ranks (%d) is not a multiple of the number of simulations (%td)",
-                  numRanks, multidirs.ssize());
+        auto message = gmx::formatString(
+                "The number of ranks (%d) is not a multiple of the number of simulations (%td)",
+                numRanks, multidirs.ssize());
+        GMX_THROW(gmx::InconsistentInputError(message));
     }
 
-    int numRanksPerSim = numRanks / multidirs.size();
-    int rankWithinComm;
-    MPI_Comm_rank(comm, &rankWithinComm);
+    int numRanksPerSimulation = numRanks / multidirs.size();
+    int rankWithinWorldComm;
+    MPI_Comm_rank(worldComm, &rankWithinWorldComm);
 
     if (debug)
     {
         fprintf(debug, "We have %td simulations, %d ranks per simulation, local simulation is %d\n",
-                multidirs.ssize(), numRanksPerSim, rankWithinComm / numRanksPerSim);
+                multidirs.ssize(), numRanksPerSimulation, rankWithinWorldComm / numRanksPerSimulation);
     }
 
-    nsim = multidirs.size();
-    sim  = rankWithinComm / numRanksPerSim;
-    /* Create a communicator for the master nodes */
-    std::vector<int> rank(nsim);
-    for (int i = 0; i < nsim; i++)
+    int numSimulations = multidirs.size();
+    // Create a communicator for the master ranks of each simulation
+    std::vector<int> ranksOfMasters(numSimulations);
+    for (int i = 0; i < numSimulations; i++)
     {
-        rank[i] = i * numRanksPerSim;
+        ranksOfMasters[i] = i * numRanksPerSimulation;
     }
-    MPI_Group mpi_group_world;
-    MPI_Comm_group(comm, &mpi_group_world);
-    MPI_Group_incl(mpi_group_world, nsim, rank.data(), &mpi_group_masters);
-    MPI_Comm_create(comm, mpi_group_masters, &mpi_comm_masters);
-
-#    if !MPI_IN_PLACE_EXISTS
-    /* initialize the MPI_IN_PLACE replacement buffers */
-    snew(mpb, 1);
-    mpb->ibuf        = nullptr;
-    mpb->libuf       = nullptr;
-    mpb->fbuf        = nullptr;
-    mpb->dbuf        = nullptr;
-    mpb->ibuf_alloc  = 0;
-    mpb->libuf_alloc = 0;
-    mpb->fbuf_alloc  = 0;
-    mpb->dbuf_alloc  = 0;
-#    endif
+    MPI_Group worldGroup;
+    // No need to free worldGroup later, we didn't create it.
+    MPI_Comm_group(worldComm, &worldGroup);
+
+    MPI_Group mastersGroup = MPI_GROUP_NULL;
+    MPI_Group_incl(worldGroup, numSimulations, ranksOfMasters.data(), &mastersGroup);
+    MPI_Comm mastersComm = MPI_COMM_NULL;
+    MPI_Comm_create(worldComm, mastersGroup, &mastersComm);
+    if (mastersGroup != MPI_GROUP_NULL)
+    {
+        MPI_Group_free(&mastersGroup);
+    }
+
+    int      simulationIndex = rankWithinWorldComm / numRanksPerSimulation;
+    MPI_Comm simulationComm  = MPI_COMM_NULL;
+    MPI_Comm_split(worldComm, simulationIndex, rankWithinWorldComm, &simulationComm);
 
-    // TODO This should throw upon error
-    gmx_chdir(multidirs[sim].c_str());
+    try
+    {
+        gmx_chdir(multidirs[simulationIndex].c_str());
+    }
+    catch (gmx::GromacsException& e)
+    {
+        e.prependContext("While changing directory for multi-simulation to " + multidirs[simulationIndex]);
+        throw;
+    }
+    return std::make_unique<gmx_multisim_t>(numSimulations, simulationIndex, mastersComm, simulationComm);
 #else
-    GMX_UNUSED_VALUE(comm);
+    GMX_UNUSED_VALUE(worldComm);
+    return nullptr;
 #endif
 }
 
-gmx_multisim_t::~gmx_multisim_t()
+gmx_multisim_t::gmx_multisim_t(int numSimulations, int simulationIndex, MPI_Comm mastersComm, MPI_Comm simulationComm) :
+    numSimulations_(numSimulations),
+    simulationIndex_(simulationIndex),
+    mastersComm_(mastersComm),
+    simulationComm_(simulationComm)
 {
-    done_mpi_in_place_buf(mpb);
+}
 
-#if GMX_MPI
+gmx_multisim_t::~gmx_multisim_t()
+{
+#if GMX_LIB_MPI
     // TODO This would work better if the result of MPI_Comm_split was
     // put into an RAII-style guard, such as gmx::unique_cptr.
-    if (mpi_comm_masters != MPI_COMM_NULL && mpi_comm_masters != MPI_COMM_WORLD)
+    if (mastersComm_ != MPI_COMM_NULL && mastersComm_ != MPI_COMM_WORLD)
     {
-        MPI_Comm_free(&mpi_comm_masters);
+        MPI_Comm_free(&mastersComm_);
     }
-    if (mpi_group_masters != MPI_GROUP_NULL)
+    if (simulationComm_ != MPI_COMM_NULL && simulationComm_ != MPI_COMM_WORLD)
     {
-        MPI_Group_free(&mpi_group_masters);
+        MPI_Comm_free(&simulationComm_);
     }
 #endif
 }
@@ -198,7 +213,7 @@ void gmx_sumd_sim(int gmx_unused nr, double gmx_unused r[], const gmx_multisim_t
 #if !GMX_MPI
     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumd_sim");
 #else
-    gmx_sumd_comm(nr, r, ms->mpi_comm_masters);
+    gmx_sumd_comm(nr, r, ms->mastersComm_);
 #endif
 }
 
@@ -207,7 +222,7 @@ void gmx_sumf_sim(int gmx_unused nr, float gmx_unused r[], const gmx_multisim_t
 #if !GMX_MPI
     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumf_sim");
 #else
-    gmx_sumf_comm(nr, r, ms->mpi_comm_masters);
+    gmx_sumf_comm(nr, r, ms->mastersComm_);
 #endif
 }
 
@@ -217,21 +232,12 @@ void gmx_sumi_sim(int gmx_unused nr, int gmx_unused r[], const gmx_multisim_t gm
     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumi_sim");
 #else
 #    if MPI_IN_PLACE_EXISTS
-    MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, ms->mpi_comm_masters);
+    MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, ms->mastersComm_);
 #    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];
-    }
+    ms->intBuffer.resize(nr);
+    MPI_Allreduce(r, ms->intBuffer.data(), ms->intBuffer.size(), MPI_INT, MPI_SUM, ms->mastersComm_);
+    std::copy(std::begin(ms->intBuffer), std::end(ms->intBuffer), r);
 #    endif
 #endif
 }
@@ -242,21 +248,12 @@ void gmx_sumli_sim(int gmx_unused nr, int64_t gmx_unused r[], const gmx_multisim
     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumli_sim");
 #else
 #    if MPI_IN_PLACE_EXISTS
-    MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM, ms->mpi_comm_masters);
+    MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM, ms->mastersComm_);
 #    else
     /* this is thread-unsafe, but it will do for now: */
-    int i;
-
-    if (nr > ms->mpb->libuf_alloc)
-    {
-        ms->mpb->libuf_alloc = nr;
-        srenew(ms->mpb->libuf, ms->mpb->libuf_alloc);
-    }
-    MPI_Allreduce(r, ms->mpb->libuf, nr, MPI_INT64_T, MPI_SUM, ms->mpi_comm_masters);
-    for (i = 0; i < nr; i++)
-    {
-        r[i] = ms->mpb->libuf[i];
-    }
+    ms->int64Buffer.resize(nr);
+    MPI_Allreduce(r, ms->int64Buffer.data(), ms->int64Buffer.size(), MPI_INT64_T, MPI_SUM, ms->mastersComm_);
+    std::copy(std::begin(ms->int64Buffer), std::end(ms->int64Buffer), r);
 #    endif
 #endif
 }
@@ -267,9 +264,9 @@ std::vector<int> gatherIntFromMultiSimulation(const gmx_multisim_t* ms, const in
 #if GMX_MPI
     if (ms != nullptr)
     {
-        valuesFromAllRanks.resize(ms->nsim);
-        valuesFromAllRanks[ms->sim] = localValue;
-        gmx_sumi_sim(ms->nsim, valuesFromAllRanks.data(), ms);
+        valuesFromAllRanks.resize(ms->numSimulations_);
+        valuesFromAllRanks[ms->simulationIndex_] = localValue;
+        gmx_sumi_sim(ms->numSimulations_, valuesFromAllRanks.data(), ms);
     }
     else
     {
@@ -297,12 +294,12 @@ void check_multi_int(FILE* log, const gmx_multisim_t* ms, int val, const char* n
         gmx_fatal(FARGS, "check_multi_int called with a NULL communication pointer");
     }
 
-    snew(ibuf, ms->nsim);
-    ibuf[ms->sim] = val;
-    gmx_sumi_sim(ms->nsim, ibuf, ms);
+    snew(ibuf, ms->numSimulations_);
+    ibuf[ms->simulationIndex_] = val;
+    gmx_sumi_sim(ms->numSimulations_, ibuf, ms);
 
     bCompatible = TRUE;
-    for (p = 1; p < ms->nsim; p++)
+    for (p = 1; p < ms->numSimulations_; p++)
     {
         bCompatible = bCompatible && (ibuf[p - 1] == ibuf[p]);
     }
@@ -319,12 +316,12 @@ void check_multi_int(FILE* log, const gmx_multisim_t* ms, int val, const char* n
         if (nullptr != log)
         {
             fprintf(log, "\n%s is not equal for all subsystems\n", name);
-            for (p = 0; p < ms->nsim; p++)
+            for (p = 0; p < ms->numSimulations_; p++)
             {
                 fprintf(log, "  subsystem %d: %d\n", p, ibuf[p]);
             }
         }
-        gmx_fatal(FARGS, "The %d subsystems are not compatible\n", ms->nsim);
+        gmx_fatal(FARGS, "The %d subsystems are not compatible\n", ms->numSimulations_);
     }
 
     sfree(ibuf);
@@ -346,12 +343,12 @@ void check_multi_int64(FILE* log, const gmx_multisim_t* ms, int64_t val, const c
         gmx_fatal(FARGS, "check_multi_int called with a NULL communication pointer");
     }
 
-    snew(ibuf, ms->nsim);
-    ibuf[ms->sim] = val;
-    gmx_sumli_sim(ms->nsim, ibuf, ms);
+    snew(ibuf, ms->numSimulations_);
+    ibuf[ms->simulationIndex_] = val;
+    gmx_sumli_sim(ms->numSimulations_, ibuf, ms);
 
     bCompatible = TRUE;
-    for (p = 1; p < ms->nsim; p++)
+    for (p = 1; p < ms->numSimulations_; p++)
     {
         bCompatible = bCompatible && (ibuf[p - 1] == ibuf[p]);
     }
@@ -370,7 +367,7 @@ void check_multi_int64(FILE* log, const gmx_multisim_t* ms, int64_t val, const c
         if (nullptr != log)
         {
             fprintf(log, "\n%s is not equal for all subsystems\n", name);
-            for (p = 0; p < ms->nsim; p++)
+            for (p = 0; p < ms->numSimulations_; p++)
             {
                 char strbuf[255];
                 /* first make the format string */
@@ -378,7 +375,7 @@ void check_multi_int64(FILE* log, const gmx_multisim_t* ms, int64_t val, const c
                 fprintf(log, strbuf, p, ibuf[p]);
             }
         }
-        gmx_fatal(FARGS, "The %d subsystems are not compatible\n", ms->nsim);
+        gmx_fatal(FARGS, "The %d subsystems are not compatible\n", ms->numSimulations_);
     }
 
     sfree(ibuf);
@@ -391,9 +388,9 @@ bool findIsSimulationMasterRank(const gmx_multisim_t* ms, MPI_Comm communicator)
         // Ranks of multi-simulations know whether they are a master
         // rank. Ranks of non-multi simulation do not know until a
         // t_commrec is available.
-        if ((ms != nullptr) && (ms->nsim > 1))
+        if ((ms != nullptr) && (ms->numSimulations_ > 1))
         {
-            return ms->mpi_comm_masters != MPI_COMM_NULL;
+            return ms->mastersComm_ != MPI_COMM_NULL;
         }
         else
         {
@@ -422,10 +419,74 @@ bool findIsSimulationMasterRank(const gmx_multisim_t* ms, MPI_Comm communicator)
 
 bool isMasterSim(const gmx_multisim_t* ms)
 {
-    return !isMultiSim(ms) || ms->sim == 0;
+    return !isMultiSim(ms) || ms->simulationIndex_ == 0;
 }
 
 bool isMasterSimMasterRank(const gmx_multisim_t* ms, const bool isMaster)
 {
     return (isMaster && isMasterSim(ms));
 }
+
+static bool multisim_int_all_are_equal(const gmx_multisim_t* ms, int64_t value)
+{
+    bool     allValuesAreEqual = true;
+    int64_t* buf;
+
+    GMX_RELEASE_ASSERT(ms, "Invalid use of multi-simulation pointer");
+
+    snew(buf, ms->numSimulations_);
+    /* send our value to all other master ranks, receive all of theirs */
+    buf[ms->simulationIndex_] = value;
+    gmx_sumli_sim(ms->numSimulations_, buf, ms);
+
+    for (int s = 0; s < ms->numSimulations_; s++)
+    {
+        if (buf[s] != value)
+        {
+            allValuesAreEqual = false;
+            break;
+        }
+    }
+
+    sfree(buf);
+
+    return allValuesAreEqual;
+}
+
+void logInitialMultisimStatus(const gmx_multisim_t* ms,
+                              const t_commrec*      cr,
+                              const gmx::MDLogger&  mdlog,
+                              const bool            simulationsShareState,
+                              const int             numSteps,
+                              const int             initialStep)
+{
+    if (!multisim_int_all_are_equal(ms, numSteps))
+    {
+        GMX_LOG(mdlog.warning)
+                .appendText(
+                        "Note: The number of steps is not consistent across multi "
+                        "simulations,\n"
+                        "but we are proceeding anyway!");
+    }
+    if (!multisim_int_all_are_equal(ms, initialStep))
+    {
+        if (simulationsShareState)
+        {
+            if (MASTER(cr))
+            {
+                gmx_fatal(FARGS,
+                          "The initial step is not consistent across multi simulations which "
+                          "share the state");
+            }
+            gmx_barrier(cr->mpi_comm_mygroup);
+        }
+        else
+        {
+            GMX_LOG(mdlog.warning)
+                    .appendText(
+                            "Note: The initial step is not consistent across multi "
+                            "simulations,\n"
+                            "but we are proceeding anyway!");
+        }
+    }
+}
index 2a31292a913dc91bfc9a0c7487515b7c936ef779..eb0f75bec585f9ff5a8ab8c3a47fa450d6522bed 100644 (file)
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/gmxmpi.h"
-#include "gromacs/utility/mpiinplacebuffers.h"
+
+namespace gmx
+{
+class MDLogger;
+}
+
+struct gmx_multisim_t;
+struct t_commrec;
+
+/*! \libinternal
+ * \brief Builder function for gmx_multisim_t
+ *
+ * \param[in]  worldComm   MPI communicator to split when
+ *                         multi-simulation is requested.
+ * \param[in]  multidirs   Strings naming the subdirectories when
+ *                         multi-simulation is requested, otherwise empty
+ *
+ * Splits \c worldComm into \c multidirs.size() separate
+ * simulations, if >1, and creates a communication structure
+ * between the master ranks of these simulations.
+ *
+ * Valid to call regardless of build configuration, but \c
+ * multidirs must be empty unless a real MPI build is used.
+ *
+ * \throws NotImplementedError     when \c multidirs is non-empty unless using real MPI is true
+ * \throws NotImplementedError     when \c multidirs has exactly one element
+ * \throws InconsistentInputError  when the number of MPI ranks is not a multiple of the number of \c multidirs
+ * \throws FileIOError             when the simulation cannot change to the working directory in \c multidirs
+ */
+std::unique_ptr<gmx_multisim_t> buildMultiSimulation(MPI_Comm                         worldComm,
+                                                     gmx::ArrayRef<const std::string> multidirs);
 
 /*! \libinternal
  * \brief Coordinate multi-simulation resources for mdrun
  */
 struct gmx_multisim_t
 {
-    //! Default constructor
-    gmx_multisim_t();
-    /*! \brief Constructor useful for mdrun simulations
+    /*! \brief Constructor
      *
-     * Splits the communicator into multidirs.size() separate
-     * simulations, if >1, and creates a communication structure
-     * between the master these simulations.
+     * \param[in]  numSimulations   The number of simulations in the MPI world.
+     * \param[in]  simulationIndex  The index of this simulation in the set of simulations.
+     * \param[in]  mastersComm      On master ranks, the communicator among master ranks;
+     *                              otherwise MPI_COMM_NULL.
+     * \param[in]  simulationComm   The communicator among ranks of this simulation.
      *
-     * Valid to call regardless of build configuration, but \c
-     * multidirs must be empty unless a real MPI build is used. */
-    gmx_multisim_t(MPI_Comm comm, gmx::ArrayRef<const std::string> multidirs);
+     * Assumes ownership of the communicators if they are neither
+     * MPI_COMM_WORLD nor MPI_COMM_NULL. If so, upon destruction will
+     * call MPI_Comm_free on them.
+     */
+    gmx_multisim_t(int numSimulations, int simulationIndex, MPI_Comm mastersComm, MPI_Comm simulationComm);
     //! Destructor
     ~gmx_multisim_t();
 
     //! The number of simulations in the set of multi-simulations
-    int nsim = 1;
+    int numSimulations_ = 1;
     //! The index of the simulation that owns this object within the set
-    int sim = 0;
-    //! The MPI Group between master ranks of simulations, valid only on master ranks.
-    MPI_Group mpi_group_masters = MPI_GROUP_NULL;
+    int simulationIndex_ = 0;
     //! The MPI communicator between master ranks of simulations, valid only on master ranks.
-    MPI_Comm mpi_comm_masters = MPI_COMM_NULL;
-    //! Communication buffers needed if MPI_IN_PLACE isn't supported
-    mpi_in_place_buf_t* mpb = nullptr;
+    MPI_Comm mastersComm_ = MPI_COMM_NULL;
+    //! The MPI communicator between ranks of this simulation.
+    MPI_Comm simulationComm_ = MPI_COMM_NULL;
+    /*! \brief Communication buffers needed if MPI_IN_PLACE isn't supported
+     *
+     * Other types could be added as needed.
+     *
+     * These vectors are unused when MPI_IN_PLACE is available
+     * and could be removed with preprocessing (or perhaps
+     * templating) or simply requiring MPI 2.0 (the standard
+     * introduced in 1997). However, the additional cache pressure
+     * introduced by the extra size of this type is not of great
+     * concern, since we have at most one per MPI rank.
+     * See issue #3591. */
+    //! \{
+    std::vector<int>     intBuffer_;
+    std::vector<int64_t> int64Buffer_;
+    //! \}
 };
 
 //! Calculate the sum over the simulations of an array of ints
@@ -136,4 +183,23 @@ bool isMasterSim(const gmx_multisim_t* ms);
  * This rank prints the remaining run time etc. */
 bool isMasterSimMasterRank(const gmx_multisim_t* ms, bool isMaster);
 
+/*! \brief Log the initial state of the multi-sim
+ *
+ * The simulations may be at different steps, etc so we
+ * report that.
+ *
+ * \param[in]  ms                     The multi-sum object
+ * \param[in]  cr                     The commrec object
+ * \param[in]  mdlog                  Logger
+ * \param[in]  simulationsShareState  Whether the simulations share state
+ * \param[in]  numSteps               The number of steps in this simulation
+ * \param[in]  initialStep            The initial step for this simulation
+ */
+void logInitialMultisimStatus(const gmx_multisim_t* ms,
+                              const t_commrec*      cr,
+                              const gmx::MDLogger&  mdlog,
+                              bool                  simulationsShareState,
+                              int                   numSteps,
+                              int                   initialStep);
+
 #endif
index 138d012059bf9d84eb6af0985555eff5a40a1a30..5a4a3caea9ab80723da83a81fabc03e44c0aa18e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7dc3d14faaaab930298b52bd4c7cd2a354e9aeda..e538b8ffeddf3529ba31202757571cc24022d46f 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2016,2017,2019, by the GROMACS development team, led by
+# Copyright (c) 2016,2017,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -36,11 +36,15 @@ gmx_add_unit_test_library(mdrunutility-test-shared
                           threadaffinitytest.cpp)
 
 gmx_add_unit_test(MdrunUtilityUnitTests mdrunutility-test
-                  threadaffinity.cpp)
+    CPP_SOURCE_FILES
+        threadaffinity.cpp
+        )
 target_link_libraries(mdrunutility-test PRIVATE mdrunutility-test-shared)
 
 gmx_add_mpi_unit_test(MdrunUtilityMpiUnitTests mdrunutility-mpi-test 4
-                      threadaffinity_mpi.cpp)
+    CPP_SOURCE_FILES
+        threadaffinity_mpi.cpp
+        )
 # TODO This target does not exist in a non-MPI build. It would be preferable
 # to change functions like gmx_add_mpi_unit_test to add link-time dependencies
 # as well as source files.
index bc72cfaaed8ff92e2c5868adcc834244d9d58caa..9cd90ccd1e549ed281d59eac3bf2c76b54f1687a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -114,17 +114,17 @@ TEST(ThreadAffinityMultiRankTest, HandlesTooManyThreadsWithForce)
 class ThreadAffinityHeterogeneousNodesTest : public ::testing::Test
 {
 public:
-    int  currentNode() const { return gmx_node_rank() / 2; }
-    int  indexInNode() const { return gmx_node_rank() % 2; }
-    bool isMaster() const { return gmx_node_rank() == 0; }
+    static int  currentNode() { return gmx_node_rank() / 2; }
+    static int  indexInNode() { return gmx_node_rank() % 2; }
+    static bool isMaster() { return gmx_node_rank() == 0; }
 
-    void setupNodes(ThreadAffinityTestHelper* helper, std::array<int, 2> cores)
+    static void setupNodes(ThreadAffinityTestHelper* helper, std::array<int, 2> cores)
     {
         const int node = currentNode();
         helper->setPhysicalNodeId(node);
         helper->setLogicalProcessorCount(cores[node]);
     }
-    void expectNodeAffinitySet(ThreadAffinityTestHelper* helper, int node, int core)
+    static void expectNodeAffinitySet(ThreadAffinityTestHelper* helper, int node, int core)
     {
         if (currentNode() == node)
         {
index 429ac458f36091bd2ae12fb61db8c3ada7557e46..56b05ae9c3ca795a42874df2d251fd0407f2c71a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -70,7 +70,12 @@ ThreadAffinityTestHelper::ThreadAffinityTestHelper()
     snew(cr_, 1);
     cr_->nnodes = gmx_node_num();
     cr_->nodeid = gmx_node_rank();
-    cr_->duty   = DUTY_PP;
+    // Default communicator is needed for [SIM]MASTER(cr) to work
+    // TODO: Should get cleaned up once thread affinity works with
+    //       communicators rather than the full cr (part of #2395)
+    cr_->sizeOfDefaultCommunicator = gmx_node_num();
+    cr_->rankInDefaultCommunicator = gmx_node_rank();
+    cr_->duty                      = DUTY_PP;
 #if GMX_MPI
     cr_->mpi_comm_mysim = MPI_COMM_WORLD;
 #endif
index 6720e7a682f90f7c9d937ba25caf98d76cbe544d..d760c6d4b1c3ca1b866818702c07fb812bc385b8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -108,6 +108,7 @@ public:
             expectAffinitySet(core);
         }
     }
+    // NOLINTNEXTLINE readability-convert-member-functions-to-static
     void expectAffinitySetThatFails(int core)
     {
         using ::testing::Return;
index e2c099e446cafdf26a018463a1c0bddf5270c0a6..8cf377acbf160ab0ee2c720f86bea938eecc880b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,7 +45,6 @@
 
 #if HAVE_SCHED_AFFINITY
 #    include <sched.h>
-#    include <sys/syscall.h>
 #endif
 
 #include "thread_mpi/threads.h"
@@ -541,10 +541,8 @@ static bool detectDefaultAffinityMask(const int nthreads_hw_avail)
     MPI_Initialized(&mpiIsInitialized);
     if (mpiIsInitialized)
     {
-        bool detectedDefaultAffinityMaskOnAllRanks;
-        MPI_Allreduce(&detectedDefaultAffinityMask, &detectedDefaultAffinityMaskOnAllRanks, 1,
-                      MPI_C_BOOL, MPI_LAND, MPI_COMM_WORLD);
-        detectedDefaultAffinityMask = detectedDefaultAffinityMaskOnAllRanks;
+        bool maskToReduce = detectedDefaultAffinityMask;
+        MPI_Allreduce(&maskToReduce, &detectedDefaultAffinityMask, 1, MPI_C_BOOL, MPI_LAND, MPI_COMM_WORLD);
     }
 #endif
 
index 820c93a9019efad8dcf1f165e0b19e0daeb6c657..1983c58ce5ad728ba7f95f397b5e028e5b526f17 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 320fb744677ab77da23b35d338684dd4ba4cd7c4..7a62dd61c8725f99dc9568a09061ae74bc66d514 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -78,6 +78,9 @@ end(const BasicMdspan& basicMdspan)
     return basicMdspan.data() + basicMdspan.mapping().required_span_size();
 }
 
+//! Convenience type for often-used two dimensional extents
+using dynamicExtents2D = extents<dynamic_extent, dynamic_extent>;
+
 //! Convenience type for often-used three dimensional extents
 using dynamicExtents3D = extents<dynamic_extent, dynamic_extent, dynamic_extent>;
 
index 10499d655dcf55f840a81c92f2111818d17d0eba..5b2f5351975ba30d25cc58f63f1c44eb1c397f03 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -216,7 +216,7 @@ public:
      * \tparam U container type
      * \param[in] other mdspan-implementing container
      */
-    template<typename U, typename = std::enable_if_t<std::is_same<typename std::remove_reference_t<U>::view_type::element_type, ElementType>::value>>
+    template<typename U, typename = std::enable_if_t<std::is_same_v<typename std::remove_reference_t<U>::view_type::element_type, ElementType>>>
     constexpr basic_mdspan(U&& other) : basic_mdspan(other.asView())
     {
     }
@@ -230,7 +230,7 @@ public:
      * \tparam U container type
      * \param[in] other mdspan-implementing container
      */
-    template<typename U, typename = std::enable_if_t<std::is_same<typename std::remove_reference_t<U>::const_view_type::element_type, ElementType>::value>>
+    template<typename U, typename = std::enable_if_t<std::is_same_v<typename std::remove_reference_t<U>::const_view_type::element_type, ElementType>>>
     constexpr basic_mdspan(const U& other) : basic_mdspan(other.asConstView())
     {
     }
@@ -252,7 +252,7 @@ public:
      * \returns reference to element stored at position i
      */
     template<class IndexType>
-    constexpr std::enable_if_t<std::is_integral<IndexType>::value && extents_type::rank() == 1, reference>
+    constexpr std::enable_if_t<std::is_integral_v<IndexType> && extents_type::rank() == 1, reference>
     operator[](const IndexType& i) const noexcept
     {
         return acc_.access(ptr_, map_(i));
@@ -279,8 +279,8 @@ public:
      */
     template<class IndexType,
              typename sliced_mdspan_type = basic_mdspan<element_type, decltype(extents_type().sliced_extents()), LayoutPolicy, AccessorPolicy>>
-    constexpr std::enable_if_t<std::is_integral<IndexType>::value && (extents_type::rank() > 1)
-                                       && std::is_same<LayoutPolicy, layout_right>::value,
+    constexpr std::enable_if_t<std::is_integral_v<IndexType> && (extents_type::rank() > 1)
+                                       && std::is_same_v<LayoutPolicy, layout_right>,
                                sliced_mdspan_type>
     operator[](const IndexType index) const noexcept
     {
index cecf464d0f264821b2113a224565582269e8b572..79e8dc9776474dd51f6a032e066896fe31a0bc41 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(MDSpanTests mdspan-test
-                  accessor_policy.cpp
-                  extents.cpp
-                  extensions.cpp
-                  layouts.cpp
-                  mdspan.cpp
-                 )
+    CPP_SOURCE_FILES
+        accessor_policy.cpp
+        extents.cpp
+        extensions.cpp
+        layouts.cpp
+        mdspan.cpp
+        )
index f9adf04c23b78fa5697affb9a185858a0de7bad1..8f60ea640a7eec3b1004a1f52c56f1751682efac 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2019, by the GROMACS development team, led by
+# Copyright (c) 2015,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 file(GLOB MDTYPES_SOURCES
+    checkpointdata.cpp
     df_history.cpp
+    energyhistory.cpp
+    forcebuffers.cpp
     group.cpp
     iforceprovider.cpp
     inputrec.cpp
+    interaction_const.cpp
     md_enums.cpp
+    multipletimestepping.cpp
     observableshistory.cpp
     state.cpp)
 
-if(GMX_USE_CUDA OR GMX_USE_OPENCL)
+if(GMX_GPU)
     gmx_add_libgromacs_sources(
        state_propagator_data_gpu_impl_gpu.cpp
        )
-    if(GMX_USE_CUDA)
-        gmx_compile_cpp_as_cuda(
-            state_propagator_data_gpu_impl_gpu.cpp
-            )
-    endif()
+   if(GMX_GPU_CUDA)
+       _gmx_add_files_to_property(CUDA_SOURCES
+           state_propagator_data_gpu_impl_gpu.cpp
+           )
+   endif()
+   if(GMX_GPU_SYCL)
+       _gmx_add_files_to_property(SYCL_SOURCES
+           state_propagator_data_gpu_impl_gpu.cpp
+           )
+   endif()
 else()
     gmx_add_libgromacs_sources(
       state_propagator_data_gpu_impl.cpp
       )
 endif()
 
+if (BUILD_TESTING)
+    add_subdirectory(tests)
+endif()
 
 set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${MDTYPES_SOURCES} PARENT_SCOPE)
 
index 1342e90d5182d5101da2d6d7673333aedf9b233f..951a7d25907ebf55a8728690d0f147b6057a467f 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -98,6 +99,7 @@ extern const char* eawhpotential_names[eawhpotentialNR + 1];
 enum
 {
     eawhcoordproviderPULL,
+    eawhcoordproviderFREE_ENERGY_LAMBDA,
     eawhcoordproviderNR
 };
 //! String for AWH bias reaction coordinate provider.
@@ -116,7 +118,7 @@ struct AwhDimParams
     double end;            /**< End value of the interval. */
     double period;         /**< The period of this dimension (= 0 if not periodic). */
     double forceConstant;  /**< The force constant in kJ/mol/nm^2, kJ/mol/rad^2 */
-    double diffusion;      /**< Estimated diffusion constant in units of nm^2/ps or rad^2/ps. */
+    double diffusion; /**< Estimated diffusion constant in units of nm^2/ps, rad^2/ps or ps^-1. */
     double coordValueInit; /**< The initial coordinate value. */
     double coverDiameter; /**< The diameter that needs to be sampled around a point before it is considered covered. */
 };
diff --git a/src/gromacs/mdtypes/checkpointdata.cpp b/src/gromacs/mdtypes/checkpointdata.cpp
new file mode 100644 (file)
index 0000000..10ed32c
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Defines the checkpoint data structure for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_mdtypes
+ */
+
+#include "gmxpre.h"
+
+#include "checkpointdata.h"
+
+#include "gromacs/utility/iserializer.h"
+#include "gromacs/utility/keyvaluetreeserializer.h"
+
+namespace gmx
+{
+
+void ReadCheckpointDataHolder::deserialize(ISerializer* serializer)
+{
+    GMX_RELEASE_ASSERT(serializer->reading(),
+                       "Tried to deserialize using a serializing ISerializer object.");
+
+    checkpointTree_ = deserializeKeyValueTree(serializer);
+}
+
+void WriteCheckpointDataHolder::serialize(ISerializer* serializer)
+{
+    GMX_RELEASE_ASSERT(!serializer->reading(),
+                       "Tried to serialize using a deserializing ISerializer object.");
+
+    serializeKeyValueTree(outputTreeBuilder_.build(), serializer);
+
+    // Tree builder should not be used after build() (see docstring)
+    // Make new builder to leave object in valid state
+    outputTreeBuilder_ = KeyValueTreeBuilder();
+}
+
+bool ReadCheckpointDataHolder::keyExists(const std::string& key) const
+{
+    return checkpointTree_.keyExists(key);
+}
+
+std::vector<std::string> ReadCheckpointDataHolder::keys() const
+{
+    std::vector<std::string> keys;
+    for (const auto& property : checkpointTree_.properties())
+    {
+        keys.emplace_back(property.key());
+    }
+    return keys;
+}
+
+ReadCheckpointData ReadCheckpointDataHolder::checkpointData(const std::string& key) const
+{
+    return ReadCheckpointData(checkpointTree_[key].asObject());
+}
+
+WriteCheckpointData WriteCheckpointDataHolder::checkpointData(const std::string& key)
+{
+    hasCheckpointDataBeenRequested_ = true;
+    return WriteCheckpointData(outputTreeBuilder_.rootObject().addObject(key));
+}
+
+bool WriteCheckpointDataHolder::empty() const
+{
+    return !hasCheckpointDataBeenRequested_;
+}
+
+} // namespace gmx
diff --git a/src/gromacs/mdtypes/checkpointdata.h b/src/gromacs/mdtypes/checkpointdata.h
new file mode 100644 (file)
index 0000000..a01f7de
--- /dev/null
@@ -0,0 +1,535 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Provides the checkpoint data structure for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MODULARSIMULATOR_CHECKPOINTDATA_H
+#define GMX_MODULARSIMULATOR_CHECKPOINTDATA_H
+
+#include <optional>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/keyvaluetreebuilder.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+class ISerializer;
+
+/*! \libinternal
+ * \brief The operations on CheckpointData
+ *
+ * This enum defines the two modes of operation on CheckpointData objects,
+ * reading and writing. This allows to template all access functions, which
+ * in turn enables clients to write a single function for read and write
+ * access, eliminating the risk of having read and write functions getting
+ * out of sync.
+ *
+ * \ingroup module_modularsimulator
+ */
+enum class CheckpointDataOperation
+{
+    Read,
+    Write,
+    Count
+};
+
+/*! \internal
+ * \brief Get an ArrayRef whose const-ness is defined by the checkpointing operation
+ *
+ * \tparam operation  Whether we are reading or writing
+ * \tparam T          The type of values stored in the ArrayRef
+ * \param container   The container the ArrayRef is referencing to
+ * \return            The ArrayRef
+ *
+ * \see ArrayRef
+ *
+ * \ingroup module_modularsimulator
+ */
+template<CheckpointDataOperation operation, typename T>
+ArrayRef<std::conditional_t<operation == CheckpointDataOperation::Write || std::is_const<T>::value, const typename T::value_type, typename T::value_type>>
+makeCheckpointArrayRef(T& container)
+{
+    return container;
+}
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Struct allowing to check if data is serializable through the KeyValueTree serializer
+ *
+ * This list of types is copied from ValueSerializer::initSerializers()
+ * Having this here allows us to catch errors at compile time
+ * instead of having cryptic runtime errors
+ */
+template<typename T>
+struct IsSerializableType
+{
+    static bool const value = std::is_same<T, std::string>::value || std::is_same<T, bool>::value
+                              || std::is_same<T, int>::value || std::is_same<T, int64_t>::value
+                              || std::is_same<T, float>::value || std::is_same<T, double>::value;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Struct allowing to check if enum has a serializable underlying type
+ */
+//! {
+template<typename T, bool = std::is_enum<T>::value>
+struct IsSerializableEnum
+{
+    static bool const value = IsSerializableType<std::underlying_type_t<T>>::value;
+};
+template<typename T>
+struct IsSerializableEnum<T, false>
+{
+    static bool const value = false;
+};
+//! }
+
+/*! \libinternal
+ * \ingroup module_modularsimulator
+ * \brief Data type hiding checkpoint implementation details
+ *
+ * This data type allows to separate the implementation details of the
+ * checkpoint writing / reading from the implementation of the checkpoint
+ * clients. Checkpoint clients interface via the methods of the CheckpointData
+ * object, and do not need knowledge of data types used to store the data.
+ *
+ * Templating allows checkpoint clients to have symmetric (templated)
+ * implementations for checkpoint reading and writing.
+ *
+ * CheckpointData objects are dispatched via [Write|Read]CheckpointDataHolder
+ * objects, which interact with the checkpoint reading from / writing to
+ * file.
+ */
+
+template<CheckpointDataOperation operation>
+class CheckpointData;
+
+//! Convenience shortcut for reading checkpoint data.
+using ReadCheckpointData = CheckpointData<CheckpointDataOperation::Read>;
+//! Convenience shortcut for writing checkpoint data.
+using WriteCheckpointData = CheckpointData<CheckpointDataOperation::Write>;
+
+template<>
+class CheckpointData<CheckpointDataOperation::Read>
+{
+public:
+    /*! \brief Read or write a single value from / to checkpoint
+     *
+     * Allowed scalar types include std::string, bool, int, int64_t,
+     * float, double, or any enum with one of the previously mentioned
+     * scalar types as underlying type. Type compatibility is checked
+     * at compile time.
+     *
+     * \tparam operation  Whether we are reading or writing
+     * \tparam T          The type of the value
+     * \param key         The key to [read|write] the value [from|to]
+     * \param value       The value to [read|write]
+     */
+    //! {
+    template<typename T>
+    std::enable_if_t<IsSerializableType<T>::value, void> scalar(const std::string& key, T* value) const;
+    template<typename T>
+    std::enable_if_t<IsSerializableEnum<T>::value, void> enumScalar(const std::string& key, T* value) const;
+    //! }
+
+    /*! \brief Read or write an ArrayRef from / to checkpoint
+     *
+     * Allowed types stored in the ArrayRef include std::string, bool, int,
+     * int64_t, float, double, and gmx::RVec. Type compatibility is checked
+     * at compile time.
+     *
+     * \tparam operation  Whether we are reading or writing
+     * \tparam T          The type of values stored in the ArrayRef
+     * \param key         The key to [read|write] the ArrayRef [from|to]
+     * \param values      The ArrayRef to [read|write]
+     */
+    //! {
+    // Read ArrayRef of scalar
+    template<typename T>
+    std::enable_if_t<IsSerializableType<T>::value, void> arrayRef(const std::string& key,
+                                                                  ArrayRef<T>        values) const;
+    // Read ArrayRef of RVec
+    void arrayRef(const std::string& key, ArrayRef<RVec> values) const;
+    //! }
+
+    /*! \brief Read or write a tensor from / to checkpoint
+     *
+     * \tparam operation  Whether we are reading or writing
+     * \param key         The key to [read|write] the tensor [from|to]
+     * \param values      The tensor to [read|write]
+     */
+    void tensor(const std::string& key, ::tensor values) const;
+
+    /*! \brief Return a subset of the current CheckpointData
+     *
+     * \tparam operation  Whether we are reading or writing
+     * \param key         The key to [read|write] the sub data [from|to]
+     * \return            A CheckpointData object representing a subset of the current object
+     */
+    //!{
+    CheckpointData subCheckpointData(const std::string& key) const;
+    //!}
+
+private:
+    //! KV tree read from checkpoint
+    const KeyValueTreeObject* inputTree_ = nullptr;
+
+    //! Construct an input checkpoint data object
+    explicit CheckpointData(const KeyValueTreeObject& inputTree);
+
+    // Only holders should build
+    friend class ReadCheckpointDataHolder;
+};
+
+template<>
+class CheckpointData<CheckpointDataOperation::Write>
+{
+public:
+    //! \copydoc CheckpointData<CheckpointDataOperation::Read>::scalar
+    //! {
+    template<typename T>
+    std::enable_if_t<IsSerializableType<T>::value, void> scalar(const std::string& key, const T* value);
+    template<typename T>
+    std::enable_if_t<IsSerializableEnum<T>::value, void> enumScalar(const std::string& key, const T* value);
+    //! }
+
+    //! \copydoc CheckpointData<CheckpointDataOperation::Read>::arrayRef
+    //! {
+    // Write ArrayRef of scalar
+    template<typename T>
+    std::enable_if_t<IsSerializableType<T>::value, void> arrayRef(const std::string& key,
+                                                                  ArrayRef<const T>  values);
+    // Write ArrayRef of RVec
+    void arrayRef(const std::string& key, ArrayRef<const RVec> values);
+    //! }
+
+    //! \copydoc CheckpointData<CheckpointDataOperation::Read>::tensor
+    void tensor(const std::string& key, const ::tensor values);
+
+    //! \copydoc CheckpointData<CheckpointDataOperation::Read>::subCheckpointData
+    CheckpointData subCheckpointData(const std::string& key);
+
+private:
+    //! Builder for the tree to be written to checkpoint
+    std::optional<KeyValueTreeObjectBuilder> outputTreeBuilder_ = std::nullopt;
+
+    //! Construct an output checkpoint data object
+    explicit CheckpointData(KeyValueTreeObjectBuilder&& outputTreeBuilder);
+
+    // Only holders should build
+    friend class WriteCheckpointDataHolder;
+};
+
+/*! \brief Read a checkpoint version enum variable
+ *
+ * This reads the checkpoint version from file. The read version is returned.
+ *
+ * If the read version is more recent than the code version, this throws an error, since
+ * we cannot know what has changed in the meantime. Using newer checkpoint files with
+ * old code is not a functionality we can offer. Note, however, that since the checkpoint
+ * version is saved by module, older checkpoint files of all simulations that don't use
+ * that specific module can still be used.
+ *
+ * Allowing backwards compatibility of files (i.e., reading an older checkpoint file with
+ * a newer version of the code) is in the responsibility of the caller module. They can
+ * use the returned file checkpoint version to do that:
+ *
+ *     const auto fileVersion = checkpointVersion(checkpointData, "version", c_currentVersion);
+ *     if (fileVersion >= CheckpointVersion::AddedX)
+ *     {
+ *         checkpointData->scalar("x", &x_));
+ *     }
+ *
+ * @tparam VersionEnum     The type of the checkpoint version enum
+ * @param  checkpointData  A reading checkpoint data object
+ * @param  key             The key under which the version is saved - also used for error output
+ * @param  programVersion  The checkpoint version of the current code
+ * @return                 The checkpoint version read from file
+ */
+template<typename VersionEnum>
+VersionEnum checkpointVersion(const ReadCheckpointData* checkpointData,
+                              const std::string&        key,
+                              const VersionEnum         programVersion)
+{
+    VersionEnum fileVersion;
+    checkpointData->enumScalar(key, &fileVersion);
+    if (fileVersion > programVersion)
+    {
+        throw FileIOError(
+                formatString("The checkpoint file contains a %s that is more recent than the "
+                             "current program version and is not backward compatible.",
+                             key.c_str()));
+    }
+    return fileVersion;
+}
+
+/*! \brief Write the current code checkpoint version enum variable
+ *
+ * Write the current program checkpoint version to the checkpoint data object.
+ * Returns the written checkpoint version to mirror the signature of the reading version.
+ *
+ * @tparam VersionEnum     The type of the checkpoint version enum
+ * @param  checkpointData  A writing checkpoint data object
+ * @param  key             The key under which the version is saved
+ * @param  programVersion  The checkpoint version of the current code
+ * @return                 The checkpoint version written to file
+ */
+template<typename VersionEnum>
+VersionEnum checkpointVersion(WriteCheckpointData* checkpointData,
+                              const std::string&   key,
+                              const VersionEnum    programVersion)
+{
+    checkpointData->enumScalar(key, &programVersion);
+    return programVersion;
+}
+
+
+/*! \libinternal
+ * \brief Holder for read checkpoint data
+ *
+ * A ReadCheckpointDataHolder object is passed to the checkpoint reading
+ * functionality, and then passed into the SimulatorBuilder object. It
+ * holds the KV-tree read from file and dispatches CheckpointData objects
+ * to the checkpoint clients.
+ */
+class ReadCheckpointDataHolder
+{
+public:
+    //! Check whether a key exists
+    [[nodiscard]] bool keyExists(const std::string& key) const;
+
+    //! Return vector of existing keys
+    [[nodiscard]] std::vector<std::string> keys() const;
+
+    //! Deserialize serializer content into the CheckpointData object
+    void deserialize(ISerializer* serializer);
+
+    /*! \brief Return a subset of the current CheckpointData
+     *
+     * \param key         The key to [read|write] the sub data [from|to]
+     * \return            A CheckpointData object representing a subset of the current object
+     */
+    [[nodiscard]] ReadCheckpointData checkpointData(const std::string& key) const;
+
+private:
+    //! KV-tree read from checkpoint
+    KeyValueTreeObject checkpointTree_;
+};
+
+/*! \libinternal
+ * \brief Holder for write checkpoint data
+ *
+ * The WriteCheckpointDataHolder object holds the KV-tree builder and
+ * dispatches CheckpointData objects to the checkpoint clients to save
+ * their respective data. It is then passed to the checkpoint writing
+ * functionality.
+ */
+class WriteCheckpointDataHolder
+{
+public:
+    //! Serialize the content of the CheckpointData object
+    void serialize(ISerializer* serializer);
+
+    /*! \brief Return a subset of the current CheckpointData
+     *
+     * \param key         The key to [read|write] the sub data [from|to]
+     * \return            A CheckpointData object representing a subset of the current object
+     */
+    [[nodiscard]] WriteCheckpointData checkpointData(const std::string& key);
+
+    /*! \brief
+     */
+    [[nodiscard]] bool empty() const;
+
+private:
+    //! KV-tree builder
+    KeyValueTreeBuilder outputTreeBuilder_;
+    //! Whether any checkpoint data object has been requested
+    bool hasCheckpointDataBeenRequested_ = false;
+};
+
+// Function definitions - here to avoid template-related linker problems
+// doxygen doesn't like these...
+//! \cond
+template<typename T>
+std::enable_if_t<IsSerializableType<T>::value, void> ReadCheckpointData::scalar(const std::string& key,
+                                                                                T* value) const
+{
+    GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
+    *value = (*inputTree_)[key].cast<T>();
+}
+
+template<typename T>
+std::enable_if_t<IsSerializableEnum<T>::value, void> ReadCheckpointData::enumScalar(const std::string& key,
+                                                                                    T* value) const
+{
+    GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
+    std::underlying_type_t<T> castValue;
+    castValue = (*inputTree_)[key].cast<std::underlying_type_t<T>>();
+    *value    = static_cast<T>(castValue);
+}
+
+template<typename T>
+inline std::enable_if_t<IsSerializableType<T>::value, void>
+WriteCheckpointData::scalar(const std::string& key, const T* value)
+{
+    GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
+    outputTreeBuilder_->addValue(key, *value);
+}
+
+template<typename T>
+inline std::enable_if_t<IsSerializableEnum<T>::value, void>
+WriteCheckpointData::enumScalar(const std::string& key, const T* value)
+{
+    GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
+    auto castValue = static_cast<std::underlying_type_t<T>>(*value);
+    outputTreeBuilder_->addValue(key, castValue);
+}
+
+template<typename T>
+inline std::enable_if_t<IsSerializableType<T>::value, void>
+ReadCheckpointData::arrayRef(const std::string& key, ArrayRef<T> values) const
+{
+    GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
+    GMX_RELEASE_ASSERT(values.size() >= (*inputTree_)[key].asArray().values().size(),
+                       "Read vector does not fit in passed ArrayRef.");
+    auto outputIt  = values.begin();
+    auto inputIt   = (*inputTree_)[key].asArray().values().begin();
+    auto outputEnd = values.end();
+    auto inputEnd  = (*inputTree_)[key].asArray().values().end();
+    for (; outputIt != outputEnd && inputIt != inputEnd; outputIt++, inputIt++)
+    {
+        *outputIt = inputIt->cast<T>();
+    }
+}
+
+template<typename T>
+inline std::enable_if_t<IsSerializableType<T>::value, void>
+WriteCheckpointData::arrayRef(const std::string& key, ArrayRef<const T> values)
+{
+    GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
+    auto builder = outputTreeBuilder_->addUniformArray<T>(key);
+    for (const auto& value : values)
+    {
+        builder.addValue(value);
+    }
+}
+
+inline void ReadCheckpointData::arrayRef(const std::string& key, ArrayRef<RVec> values) const
+{
+    GMX_RELEASE_ASSERT(values.size() >= (*inputTree_)[key].asArray().values().size(),
+                       "Read vector does not fit in passed ArrayRef.");
+    auto outputIt  = values.begin();
+    auto inputIt   = (*inputTree_)[key].asArray().values().begin();
+    auto outputEnd = values.end();
+    auto inputEnd  = (*inputTree_)[key].asArray().values().end();
+    for (; outputIt != outputEnd && inputIt != inputEnd; outputIt++, inputIt++)
+    {
+        auto storedRVec = inputIt->asObject()["RVec"].asArray().values();
+        *outputIt       = { storedRVec[XX].cast<real>(), storedRVec[YY].cast<real>(),
+                      storedRVec[ZZ].cast<real>() };
+    }
+}
+
+inline void WriteCheckpointData::arrayRef(const std::string& key, ArrayRef<const RVec> values)
+{
+    auto builder = outputTreeBuilder_->addObjectArray(key);
+    for (const auto& value : values)
+    {
+        auto subbuilder = builder.addObject();
+        subbuilder.addUniformArray("RVec", { value[XX], value[YY], value[ZZ] });
+    }
+}
+
+inline void ReadCheckpointData::tensor(const std::string& key, ::tensor values) const
+{
+    auto array     = (*inputTree_)[key].asArray().values();
+    values[XX][XX] = array[0].cast<real>();
+    values[XX][YY] = array[1].cast<real>();
+    values[XX][ZZ] = array[2].cast<real>();
+    values[YY][XX] = array[3].cast<real>();
+    values[YY][YY] = array[4].cast<real>();
+    values[YY][ZZ] = array[5].cast<real>();
+    values[ZZ][XX] = array[6].cast<real>();
+    values[ZZ][YY] = array[7].cast<real>();
+    values[ZZ][ZZ] = array[8].cast<real>();
+}
+
+inline void WriteCheckpointData::tensor(const std::string& key, const ::tensor values)
+{
+    auto builder = outputTreeBuilder_->addUniformArray<real>(key);
+    builder.addValue(values[XX][XX]);
+    builder.addValue(values[XX][YY]);
+    builder.addValue(values[XX][ZZ]);
+    builder.addValue(values[YY][XX]);
+    builder.addValue(values[YY][YY]);
+    builder.addValue(values[YY][ZZ]);
+    builder.addValue(values[ZZ][XX]);
+    builder.addValue(values[ZZ][YY]);
+    builder.addValue(values[ZZ][ZZ]);
+}
+
+inline ReadCheckpointData ReadCheckpointData::subCheckpointData(const std::string& key) const
+{
+    return CheckpointData((*inputTree_)[key].asObject());
+}
+
+inline WriteCheckpointData WriteCheckpointData::subCheckpointData(const std::string& key)
+{
+    return CheckpointData(outputTreeBuilder_->addObject(key));
+}
+
+inline ReadCheckpointData::CheckpointData(const KeyValueTreeObject& inputTree) :
+    inputTree_(&inputTree)
+{
+}
+
+inline WriteCheckpointData::CheckpointData(KeyValueTreeObjectBuilder&& outputTreeBuilder) :
+    outputTreeBuilder_(outputTreeBuilder)
+{
+}
+//! \endcond
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_CHECKPOINTDATA_H
index 72291ff816680b7353b42d10b177bcd19308edd9..bef393da4275ac826234a620f896c330deb5a4d7 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,6 +50,20 @@ struct gmx_domdec_t;
 #define DUTY_PP (1U << 0U)
 #define DUTY_PME (1U << 1U)
 
+//! Whether the current DD role is master or slave
+enum class DDRole
+{
+    Master,
+    Agent
+};
+
+//! Whether one or more ranks are used
+enum class NumRanks
+{
+    Single,
+    Multiple
+};
+
 typedef struct
 {
     int      bUse;
@@ -78,6 +93,10 @@ struct t_commrec
                                   a single simulation */
     MPI_Comm mpi_comm_mygroup; /* subset of mpi_comm_mysim including only
                                   the ranks in the same group (PP or PME) */
+    //! The communicator used before DD was initialized
+    MPI_Comm mpiDefaultCommunicator;
+    int      sizeOfDefaultCommunicator;
+    int      rankInDefaultCommunicator;
 
     gmx_nodecomm_t nc;
 
@@ -124,11 +143,12 @@ inline bool thisRankHasDuty(const t_commrec* cr, int duty)
  * In particular, this is true for multi-rank runs with TPI and NM, because
  * they use a decomposition that is not the domain decomposition used by
  * other simulation types. */
-#define PAR(cr) ((cr)->nnodes > 1)
+#define PAR(cr) ((cr)->sizeOfDefaultCommunicator > 1)
 
 //! True of this is the master node
-#define MASTER(cr) (((cr)->nodeid == 0) || !PAR(cr))
+#define MASTER(cr) (((cr)->rankInDefaultCommunicator == 0) || !PAR(cr))
 
+// Note that currently, master is always PP master, so this is equivalent to MASTER(cr)
 //! True if this is the particle-particle master
 #define SIMMASTER(cr) ((MASTER(cr) && thisRankHasDuty((cr), DUTY_PP)) || !PAR(cr))
 
@@ -149,7 +169,7 @@ inline bool thisRankHasDuty(const t_commrec* cr, int duty)
  * This is true if havePPDomainDecomposition is true, but the converse does not
  * apply (see docs of havePpDomainDecomposition()).
  *
- * \todo As part of Redmine #2395, replace calls to this with
+ * \todo As part of Issue #2395, replace calls to this with
  * havePPDomainDecomposition or a call of some other/new function, as
  * appropriate to each case. Then eliminate this macro. */
 #define DOMAINDECOMP(cr) (((cr)->dd != nullptr) && PAR(cr))
index 5180f4e3534a85d68ad51d9d23b67799ec364aba..70ce4f66efedff1345494de40590ae08256aedae 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_MDTYPES_TYPES_ENERDATA_H
 
 #include <array>
+#include <utility>
 #include <vector>
 
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/topology/idef.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/real.h"
 
+struct t_commrec;
+struct t_lambda;
+
+// The non-bonded energy terms accumulated for energy group pairs
 enum
 {
     egCOULSR,
@@ -52,6 +59,7 @@ enum
     egNR
 };
 
+// Struct for accumulating non-bonded energies between energy group pairs
 struct gmx_grppairener_t
 {
     gmx_grppairener_t(int numEnergyGroups) : nener(numEnergyGroups * numEnergyGroups)
@@ -66,23 +74,146 @@ struct gmx_grppairener_t
     std::array<std::vector<real>, egNR> ener;  /* Energy terms for each pair of groups */
 };
 
+//! Accumulates free-energy foreign lambda energies and dH/dlamba
+class ForeignLambdaTerms
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] numLambdas  The number of foreign lambda values
+     */
+    ForeignLambdaTerms(int numLambdas);
+
+    //! Returns the number of foreign lambda values
+    int numLambdas() const { return numLambdas_; }
+
+    //! Returns the H(lambdaIndex) - H(lambda_current)
+    double deltaH(int lambdaIndex) const { return energies_[1 + lambdaIndex] - energies_[0]; }
+
+    /*! \brief Returns a list of partial energies, the part which depends on lambda),
+     * current lambda in entry 0, foreign lambda i in entry 1+i
+     *
+     * Note: the potential terms needs to be finalized before calling this method.
+     */
+    gmx::ArrayRef<double> energies()
+    {
+        GMX_ASSERT(finalizedPotentialContributions_, "Should be finalized");
+        return energies_;
+    }
+
+    /*! \brief Returns a list of partial energies, the part which depends on lambda),
+     * current lambda in entry 0, foreign lambda i in entry 1+i
+     *
+     * Note: the potential terms needs to be finalized before calling this method.
+     */
+    gmx::ArrayRef<const double> energies() const
+    {
+        GMX_ASSERT(finalizedPotentialContributions_, "Should be finalized");
+        return energies_;
+    }
+
+    /*! \brief Adds an energy and dV/dl constribution to lambda list index \p listIndex
+     *
+     * This should only be used for terms with non-linear dependence on lambda
+     * The value passed as listIndex should be 0 for the current lambda
+     * and 1+i for foreign lambda index i.
+     */
+    void accumulate(int listIndex, double energy, double dvdl)
+    {
+        GMX_ASSERT(!finalizedPotentialContributions_,
+                   "Can only accumulate with an unfinalized object");
+
+        energies_[listIndex] += energy;
+        dhdl_[listIndex] += dvdl;
+    }
+
+    /*! \brief Finalizes the potential (non-kinetic) terms
+     *
+     * Note: This can be called multiple times during the same force calculations
+     * without affecting the results.
+     *
+     * \param[in] dvdlLinear  List of dV/dlambda contributions of size efptNR with depend linearly on lambda
+     * \param[in] lambda      Lambda values for the efptNR contribution types
+     * \param[in] fepvals     Free-energy parameters
+     */
+    void finalizePotentialContributions(gmx::ArrayRef<const double> dvdlLinear,
+                                        gmx::ArrayRef<const real>   lambda,
+                                        const t_lambda&             fepvals);
+
+    /*! \brief Accumulates the kinetic and constraint free-energy contributions
+     *
+     * \param[in] energyTerms  List of energy terms, pass \p term in \p gmx_enerdata_t
+     * \param[in] dhdlMass     The mass dependent contribution to dH/dlambda
+     * \param[in] lambda       Lambda values for the efptNR contribution types
+     * \param[in] fepvals      Free-energy parameters
+     */
+    void finalizeKineticContributions(gmx::ArrayRef<const real> energyTerms,
+                                      double                    dhdlMass,
+                                      gmx::ArrayRef<const real> lambda,
+                                      const t_lambda&           fepvals);
+
+    /*! \brief Returns a pair of lists of deltaH and dH/dlambda
+     *
+     * Both lists are of size numLambdas() and are indexed with the lambda index.
+     *
+     * Note: should only be called after the object has been finalized by a call to
+     * accumulateLinearPotentialComponents() (is asserted).
+     *
+     * \param[in] cr  Communication record, used to reduce the terms when !=nullptr
+     */
+    std::pair<std::vector<double>, std::vector<double>> getTerms(const t_commrec* cr) const;
+
+    //! Sets all terms to 0
+    void zeroAllTerms();
+
+private:
+    //! As accumulate(), but for kinetic contributions
+    void accumulateKinetic(int listIndex, double energy, double dhdl);
+
+    //! Add a dH/dl contribution that does not depend on lambda to all foreign dH/dl terms
+    void addConstantDhdl(double dhdl);
+
+    //! The number of foreign lambdas
+    int numLambdas_;
+    //! Storage for foreign lambda energies
+    std::vector<double> energies_;
+    //! Storage for foreign lambda dH/dlambda
+    std::vector<double> dhdl_;
+    //! Tells whether all potential energy contributions have been accumulated
+    bool finalizedPotentialContributions_ = false;
+};
+
+//! Struct for accumulating all potential energy terms and some kinetic energy terms
 struct gmx_enerdata_t
 {
+    /*! \brief
+     * Constructor with specific number of energy groups and lambdas.
+     *
+     * \param[in] numEnergyGroups Number of energy groups used.
+     * \param[in] numFepLambdas   Number of free energy lambdas, zero if none.
+     */
     gmx_enerdata_t(int numEnergyGroups, int numFepLambdas);
 
-    real term[F_NRE] = { 0 }; /* The energies for all different interaction types */
+    //! The energies for all different interaction types
+    real term[F_NRE] = { 0 };
+    //! Energy group pair non-bonded energies
     struct gmx_grppairener_t grpp;
-    double dvdl_lin[efptNR]    = { 0 }; /* Contributions to dvdl with linear lam-dependence */
-    double dvdl_nonlin[efptNR] = { 0 }; /* Idem, but non-linear dependence                  */
+    //! Contributions to dV/dlambda with linear dependence on lambda
+    double dvdl_lin[efptNR] = { 0 };
+    //! Contributions to dV/dlambda with non-linear dependence on lambda
+    double dvdl_nonlin[efptNR] = { 0 };
     /* The idea is that dvdl terms with linear lambda dependence will be added
      * automatically to enerpart_lambda. Terms with non-linear lambda dependence
      * should explicitly determine the energies at foreign lambda points
      * when n_lambda > 0. */
 
-    int                 fep_state = 0; /*current fep state -- just for printing */
-    std::vector<double> enerpart_lambda; /* Partial Hamiltonian for lambda and flambda[], includes at least all perturbed terms */
-    real foreign_term[F_NRE] = { 0 };      /* alternate array for storing foreign lambda energies */
-    struct gmx_grppairener_t foreign_grpp; /* alternate array for storing foreign lambda energies */
+    //! Foreign lambda energies and dH/dl
+    ForeignLambdaTerms foreignLambdaTerms;
+
+    //! Alternate, temporary array for storing foreign lambda energies
+    real foreign_term[F_NRE] = { 0 };
+    //! Alternate, temporary  array for storing foreign lambda group pair energies
+    struct gmx_grppairener_t foreign_grpp;
 };
 
 #endif
diff --git a/src/gromacs/mdtypes/energyhistory.cpp b/src/gromacs/mdtypes/energyhistory.cpp
new file mode 100644 (file)
index 0000000..07da6e7
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#include "gmxpre.h"
+
+#include "energyhistory.h"
+
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "checkpointdata.h"
+
+//! \cond INTERNAL
+// mirroring the \cond from energyhistory.h to avoid Doxygen errors
+
+namespace
+{
+/*!
+ * \brief Enum describing the contents delta_h_history_t writes to modular checkpoint
+ *
+ * When changing the checkpoint content, add a new element just above Count, and adjust the
+ * checkpoint functionality.
+ */
+enum class DeltaHHistoryCheckpointVersion
+{
+    Base, //!< First version of modular checkpointing
+    Count //!< Number of entries. Add new versions right above this!
+};
+constexpr auto c_currentVersionDeltaHH =
+        DeltaHHistoryCheckpointVersion(int(DeltaHHistoryCheckpointVersion::Count) - 1);
+} // namespace
+
+//! Read / write vector size from / to checkpoint and resize vector if reading
+template<gmx::CheckpointDataOperation operation, typename T>
+static void checkpointVectorSize(gmx::CheckpointData<operation>* checkpointData,
+                                 const std::string&              name,
+                                 std::vector<T>*                 vector)
+{
+    auto size = static_cast<int64_t>(vector->size());
+    checkpointData->scalar(name, &size);
+    if (operation == gmx::CheckpointDataOperation::Read)
+    {
+        vector->resize(size);
+    }
+};
+
+template<gmx::CheckpointDataOperation operation>
+void delta_h_history_t::doCheckpoint(gmx::CheckpointData<operation> checkpointData)
+{
+    gmx::checkpointVersion(&checkpointData, "delta_h_history_t version", c_currentVersionDeltaHH);
+
+    checkpointVectorSize(&checkpointData, "numDeltaH", &dh);
+    checkpointData.scalar("start_time", &start_time);
+    checkpointData.scalar("start_lambda", &start_lambda);
+    checkpointData.scalar("start_lambda_set", &start_lambda_set);
+    for (std::size_t idx = 0; idx < dh.size(); ++idx)
+    {
+        checkpointVectorSize(&checkpointData, gmx::formatString("vecSize %zu", idx), &dh[idx]);
+        checkpointData.arrayRef(gmx::formatString("vec %zu", idx),
+                                gmx::makeCheckpointArrayRef<operation>(dh[idx]));
+    }
+}
+
+namespace
+{
+/*!
+ * \brief Enum describing the contents energyhistory_t writes to modular checkpoint
+ *
+ * When changing the checkpoint content, add a new element just above Count, and adjust the
+ * checkpoint functionality.
+ */
+enum class EnergyHistoryCheckpointVersion
+{
+    Base, //!< First version of modular checkpointing
+    Count //!< Number of entries. Add new versions right above this!
+};
+constexpr auto c_currentVersionEnergyHistory =
+        EnergyHistoryCheckpointVersion(int(EnergyHistoryCheckpointVersion::Count) - 1);
+} // namespace
+
+template<gmx::CheckpointDataOperation operation>
+void energyhistory_t::doCheckpoint(gmx::CheckpointData<operation> checkpointData)
+{
+    gmx::checkpointVersion(&checkpointData, "energyhistory_t version", c_currentVersionEnergyHistory);
+
+    bool useCheckpoint = (nsum <= 0 && nsum_sim <= 0);
+    checkpointData.scalar("useCheckpoint", &useCheckpoint);
+
+    if (!useCheckpoint)
+    {
+        return;
+    }
+
+    checkpointVectorSize(&checkpointData, "enerAveSize", &ener_ave);
+    checkpointVectorSize(&checkpointData, "enerSumSize", &ener_sum);
+    checkpointVectorSize(&checkpointData, "enerSumSimSize", &ener_sum_sim);
+
+    checkpointData.scalar("nsteps", &nsteps);
+    checkpointData.scalar("nsteps_sim", &nsteps_sim);
+
+    checkpointData.scalar("nsum", &nsum);
+    checkpointData.scalar("nsum_sim", &nsum_sim);
+
+    auto hasForeignLambdas = (deltaHForeignLambdas != nullptr);
+    checkpointData.scalar("has foreign lambdas", &hasForeignLambdas);
+    if (hasForeignLambdas && deltaHForeignLambdas == nullptr)
+    {
+        deltaHForeignLambdas = std::make_unique<delta_h_history_t>();
+    }
+
+    if (nsum > 0)
+    {
+        checkpointData.arrayRef("ener_ave", gmx::makeCheckpointArrayRef<operation>(ener_ave));
+        checkpointData.arrayRef("ener_sum", gmx::makeCheckpointArrayRef<operation>(ener_sum));
+    }
+    if (nsum_sim > 0)
+    {
+        checkpointData.arrayRef("ener_sum_sim", gmx::makeCheckpointArrayRef<operation>(ener_sum_sim));
+    }
+    if (hasForeignLambdas)
+    {
+        deltaHForeignLambdas->doCheckpoint<operation>(
+                checkpointData.subCheckpointData("deltaHForeignLambdas"));
+    }
+}
+
+// explicit template instatiation
+template void energyhistory_t::doCheckpoint(gmx::CheckpointData<gmx::CheckpointDataOperation::Read> checkpointData);
+template void energyhistory_t::doCheckpoint(gmx::CheckpointData<gmx::CheckpointDataOperation::Write> checkpointData);
+
+
+//! \endcond
index 8f463a6efd113a413d87f4c668dbcfcec4d2a9ef..638ba98ed08160d7fad4861a4e34e05eb60514fd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 //! \cond INTERNAL
 
+namespace gmx
+{
+enum class CheckpointDataOperation;
+template<CheckpointDataOperation operation>
+class CheckpointData;
+} // namespace gmx
+
 //! \brief Energy history for delta_h histograms in between energy file frames
 class delta_h_history_t
 {
@@ -69,6 +76,10 @@ public:
     //! Whether the lambda value is set. Here for backward-compatibility.
     gmx_bool start_lambda_set;
 
+    //! Read / write data from / to checkpoint object
+    template<gmx::CheckpointDataOperation operation>
+    void doCheckpoint(gmx::CheckpointData<operation> checkpointData);
+
     delta_h_history_t() : start_time(0), start_lambda(0), start_lambda_set(false) {}
 };
 
@@ -88,15 +99,11 @@ public:
     //! History for energy difference for foreign lambdas (useful for BAR)
     std::unique_ptr<delta_h_history_t> deltaHForeignLambdas;
 
-    energyhistory_t() :
-        nsteps(0),
-        nsum(0),
+    //! Read / write data from / to checkpoint object
+    template<gmx::CheckpointDataOperation operation>
+    void doCheckpoint(gmx::CheckpointData<operation> checkpointData);
 
-        nsteps_sim(0),
-        nsum_sim(0),
-        ener_sum_sim(0)
-    {
-    }
+    energyhistory_t() : nsteps(0), nsum(0), nsteps_sim(0), nsum_sim(0), ener_sum_sim(0) {}
 };
 
 //! \endcond
index 70a6b8df88f277bc8da88370242fe43bd469ffcc..09dce8f273f319b959feda55db855f28a71ad220 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -37,6 +38,8 @@
 #ifndef GMX_MDTYPES_FCDATA_H
 #define GMX_MDTYPES_FCDATA_H
 
+#include <vector>
+
 #include "gromacs/math/vectypes.h"
 #include "gromacs/topology/idef.h"
 #include "gromacs/utility/basedefinitions.h"
@@ -111,12 +114,13 @@ typedef struct t_oriresdata
     double** v;
 } t_oriresdata;
 
-typedef struct bondedtable_t
+/* Cubic spline table for tabulated bonded interactions */
+struct bondedtable_t
 {
-    int   n;     /* n+1 is the number of points */
-    real  scale; /* distance between two points */
-    real* data;  /* the actual table data, per point there are 4 numbers */
-} bondedtable_t;
+    int               n;     /* n+1 is the number of points */
+    real              scale; /* distance between two points */
+    std::vector<real> data;  /* the actual table data, per point there are 4 numbers */
+};
 
 /*
  * Data struct used in the force calculation routines
@@ -125,14 +129,15 @@ typedef struct bondedtable_t
  * (for instance for time averaging in distance retraints)
  * or for storing output, since force routines only return the potential.
  */
-typedef struct t_fcdata
+struct t_fcdata
 {
-    bondedtable_t* bondtab;
-    bondedtable_t* angletab;
-    bondedtable_t* dihtab;
+    std::vector<bondedtable_t> bondtab;
+    std::vector<bondedtable_t> angletab;
+    std::vector<bondedtable_t> dihtab;
 
-    t_disresdata disres;
-    t_oriresdata orires;
-} t_fcdata;
+    // TODO: Convert to C++ and unique_ptr (currently this data is not freed)
+    t_disresdata* disres = nullptr;
+    t_oriresdata* orires = nullptr;
+};
 
 #endif
diff --git a/src/gromacs/mdtypes/forcebuffers.cpp b/src/gromacs/mdtypes/forcebuffers.cpp
new file mode 100644 (file)
index 0000000..ee0c0d0
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements the ForceBuffers class
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdtypes
+ */
+
+#include "gmxpre.h"
+
+#include "forcebuffers.h"
+
+#include "gromacs/gpu_utils/hostallocator.h"
+
+namespace gmx
+{
+
+ForceBuffers::ForceBuffers() :
+    force_({}),
+    forceMtsCombined_({}),
+    view_({}, {}, false),
+    useForceMtsCombined_(false)
+{
+}
+
+ForceBuffers::ForceBuffers(const bool useForceMtsCombined, const PinningPolicy pinningPolicy) :
+    force_({}, { pinningPolicy }),
+    forceMtsCombined_({}),
+    view_({}, {}, useForceMtsCombined),
+    useForceMtsCombined_(useForceMtsCombined)
+{
+}
+
+ForceBuffers::~ForceBuffers() = default;
+
+ForceBuffers& ForceBuffers::operator=(ForceBuffers const& o)
+{
+    auto oForce = o.view().force();
+    resize(oForce.size());
+    std::copy(oForce.begin(), oForce.end(), view().force().begin());
+
+    return *this;
+}
+
+PinningPolicy ForceBuffers::pinningPolicy() const
+{
+    return force_.get_allocator().pinningPolicy();
+}
+
+void ForceBuffers::resize(int numAtoms)
+{
+    force_.resizeWithPadding(numAtoms);
+    if (useForceMtsCombined_)
+    {
+        forceMtsCombined_.resizeWithPadding(numAtoms);
+    }
+    view_ = ForceBuffersView(force_.arrayRefWithPadding(), forceMtsCombined_.arrayRefWithPadding(),
+                             useForceMtsCombined_);
+}
+
+} // namespace gmx
diff --git a/src/gromacs/mdtypes/forcebuffers.h b/src/gromacs/mdtypes/forcebuffers.h
new file mode 100644 (file)
index 0000000..ed14bf9
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * This file contains the definition of a container for force buffers.
+ *
+ * \author Berk Hess
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_FORCEBUFFERS_H
+#define GMX_MDTYPES_FORCEBUFFERS_H
+
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/arrayrefwithpadding.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/classhelpers.h"
+
+namespace gmx
+{
+
+enum class PinningPolicy : int;
+
+/*! \libinternal \brief A view of the force buffer
+ *
+ * This is used for read and/or write access to the force buffer.
+ */
+class ForceBuffersView
+{
+public:
+    //! Constructor, creates a view to \p force
+    ForceBuffersView(const ArrayRefWithPadding<RVec>& force,
+                     const ArrayRefWithPadding<RVec>& forceMtsCombined,
+                     const bool                       useForceMtsCombined) :
+        force_(force),
+        forceMtsCombined_(forceMtsCombined),
+        useForceMtsCombined_(useForceMtsCombined)
+    {
+    }
+
+    //! Copy constructor deleted to avoid creating non-const from const
+    ForceBuffersView(const ForceBuffersView& o) = delete;
+
+    //! Move constructor deleted, but could be implemented
+    ForceBuffersView(ForceBuffersView&& o) = delete;
+
+    ~ForceBuffersView() = default;
+
+    //! Copy assignment deleted to avoid creating non-const from const
+    ForceBuffersView& operator=(const ForceBuffersView& v) = delete;
+
+    //! Move assignment, moves the view
+    ForceBuffersView& operator=(ForceBuffersView&& v) = default;
+
+    //! Returns a const arrayref to the force buffer without padding
+    ArrayRef<const RVec> force() const { return force_.unpaddedConstArrayRef(); }
+
+    //! Returns an arrayref to the force buffer without padding
+    ArrayRef<RVec> force() { return force_.unpaddedArrayRef(); }
+
+    //! Returns an ArrayRefWithPadding to the force buffer
+    ArrayRefWithPadding<RVec> forceWithPadding() { return force_; }
+
+    //! Returns a const arrayref to the MTS force buffer without padding
+    ArrayRef<const RVec> forceMtsCombined() const
+    {
+        GMX_ASSERT(useForceMtsCombined_, "Need the MTS buffer");
+        return forceMtsCombined_.unpaddedConstArrayRef();
+    }
+
+    //! Returns an arrayref to the MTS force buffer without padding
+    ArrayRef<RVec> forceMtsCombined()
+    {
+        GMX_ASSERT(useForceMtsCombined_, "Need the MTS buffer");
+        return forceMtsCombined_.unpaddedArrayRef();
+    }
+
+    //! Returns an ArrayRefWithPadding to the MTS force buffer
+    ArrayRefWithPadding<RVec> forceMtsCombinedWithPadding()
+    {
+        GMX_ASSERT(useForceMtsCombined_, "Need the MTS buffer");
+        return forceMtsCombined_;
+    }
+
+private:
+    //! The force buffer
+    ArrayRefWithPadding<RVec> force_;
+    //! The force buffer for combined fast and slow forces with MTS
+    ArrayRefWithPadding<RVec> forceMtsCombined_;
+    //! Wether we use forceMtsCombined_
+    gmx_used_in_debug bool useForceMtsCombined_;
+};
+
+/*! \libinternal \brief Object that holds the force buffers
+ *
+ * Contains a normal force buffer and optionally a force buffer for combined fast and slow
+ * forces for use with multiple time stepping.
+ * More buffers can be added when needed. Those should also be added
+ * to ForceBuffersView.
+ * The force buffer (not forceMtsCombined) can be pinned for efficient transfer to/from GPUs.
+ * All access happens through the ForceBuffersView object.
+ */
+class ForceBuffers
+{
+public:
+    //! Constructor, creates an empty force buffer with pinning not active and no MTS force buffer
+    ForceBuffers();
+
+    /*! \brief Constructor, with options for using the MTS force buffer and the pinning policy
+     *
+     * \param[in] useForceMtsCombined  Whether to enable use of the forceMtsCombined buffer
+     * \param[in] pinningPolicy        The pinning policy for the force (not MTS) buffer
+     */
+    ForceBuffers(bool useForceMtsCombined, PinningPolicy pinningPolicy);
+
+    //! Copy constructor deleted, but could be implemented
+    ForceBuffers(const ForceBuffers& o) = delete;
+
+    //! Move constructor deleted, but could be implemented
+    ForceBuffers(ForceBuffers&& o) = delete;
+
+    ~ForceBuffers();
+
+    //! Copy assignment operator, sets the pinning policy to CannotBePinned
+    ForceBuffers& operator=(ForceBuffers const& o);
+
+    //! Move assignment operator, deleted but could be implemented
+    ForceBuffers& operator=(ForceBuffers&& o) = delete;
+
+    //! Returns a const view to the force buffers
+    const ForceBuffersView& view() const { return view_; }
+
+    //! Returns a view to the force buffer
+    ForceBuffersView& view() { return view_; }
+
+    //! Resize the force buffer
+    void resize(int numAtoms);
+
+    /*! \brief Returns the active pinning policy.
+     *
+     * Does not throw.
+     */
+    PinningPolicy pinningPolicy() const;
+
+private:
+    //! The force buffer
+    PaddedHostVector<RVec> force_;
+    //! Force buffer with combined fast and slow forces for use with multiple time stepping
+    PaddedHostVector<RVec> forceMtsCombined_;
+    //! The view to the force buffer
+    ForceBuffersView view_;
+    //! Wether we use forceMtsCombined_
+    bool useForceMtsCombined_;
+};
+
+} // namespace gmx
+
+#endif
index f17e61deebabff8e82bf52debfbc49830b77d131..c31bcf05ba5f6536fc8cfa63aacbe824ea835f08 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -83,6 +83,8 @@ public:
         computeVirial_(computeVirial),
         shiftForces_(computeVirial ? shiftForces : gmx::ArrayRef<gmx::RVec>())
     {
+        GMX_ASSERT(!computeVirial || !shiftForces.empty(),
+                   "We need a valid shift force buffer when computing the virial");
     }
 
     //! Returns an arrayref to the force buffer without padding
@@ -100,6 +102,9 @@ public:
     //! Returns a const shift forces buffer
     gmx::ArrayRef<const gmx::RVec> shiftForces() const { return shiftForces_; }
 
+    //! Returns a reference to the boolean which tells whether we have spread forces on vsites
+    bool& haveSpreadVsiteForces() { return haveSpreadVsiteForces_; }
+
 private:
     //! The force buffer
     gmx::ArrayRefWithPadding<gmx::RVec> force_;
@@ -107,6 +112,8 @@ private:
     bool computeVirial_;
     //! A buffer for storing the shift forces, size SHIFTS
     gmx::ArrayRef<gmx::RVec> shiftForces_;
+    //! Tells whether we have spread the vsite forces
+    bool haveSpreadVsiteForces_ = false;
 };
 
 /*! \libinternal \brief Container for force and virial for algorithms that provide their own virial tensor contribution
@@ -193,8 +200,11 @@ class ForceOutputs
 {
 public:
     //! Constructor
-    ForceOutputs(const ForceWithShiftForces& forceWithShiftForces, const ForceWithVirial& forceWithVirial) :
+    ForceOutputs(const ForceWithShiftForces& forceWithShiftForces,
+                 bool                        haveForceWithVirial,
+                 const ForceWithVirial&      forceWithVirial) :
         forceWithShiftForces_(forceWithShiftForces),
+        haveForceWithVirial_(haveForceWithVirial),
         forceWithVirial_(forceWithVirial)
     {
     }
@@ -202,12 +212,17 @@ public:
     //! Returns a reference to the force with shift forces object
     ForceWithShiftForces& forceWithShiftForces() { return forceWithShiftForces_; }
 
+    //! Return whether there are forces with direct virial contributions
+    bool haveForceWithVirial() const { return haveForceWithVirial_; }
+
     //! Returns a reference to the force with virial object
     ForceWithVirial& forceWithVirial() { return forceWithVirial_; }
 
 private:
     //! Force output buffer used by legacy modules (without SIMD padding)
     ForceWithShiftForces forceWithShiftForces_;
+    //! Whether we have forces with direct virial contributions
+    bool haveForceWithVirial_;
     //! Force with direct virial contribution (if there are any; without SIMD padding)
     ForceWithVirial forceWithVirial_;
 };
index 2b0832a80a7503dc61bfd10ef2488bd63022ddbd..98ad9ff1aba668fb5ef72c08fe0a20500244e7cc 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <vector>
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
+#include "locality.h"
+
 /* Abstract type for PME that is defined only in the routine that use them. */
-struct gmx_ns_t;
 struct gmx_pme_t;
 struct nonbonded_verlet_t;
 struct bonded_threading_t;
+class DeviceContext;
 class DispersionCorrection;
+class ListedForces;
+struct t_fcdata;
 struct t_forcetable;
 struct t_QMMMrec;
 
 namespace gmx
 {
+class DeviceStreamManager;
 class GpuBonded;
+class GpuForceReduction;
 class ForceProviders;
 class StatePropagatorDataGpu;
 class PmePpCommGpu;
+class WholeMoleculeTransform;
 } // namespace gmx
 
 /* macros for the cginfo data in forcerec
@@ -110,10 +119,10 @@ enum
 
 struct cginfo_mb_t
 {
-    int  cg_start;
-    int  cg_end;
-    int  cg_mod;
-    int* cginfo;
+    int              cg_start = 0;
+    int              cg_end   = 0;
+    int              cg_mod   = 0;
+    std::vector<int> cginfo;
 };
 
 
@@ -122,6 +131,49 @@ struct gmx_ewald_tab_t;
 
 struct ewald_corr_thread_t;
 
+/*! \brief Helper force buffers for ForceOutputs
+ *
+ * This class stores intermediate force buffers that are used
+ * internally in the force calculation and which are reduced into
+ * the output force buffer passed to the force calculation.
+ */
+class ForceHelperBuffers
+{
+public:
+    /*! \brief Constructs helper buffers
+     *
+     * When the forces that will be accumulated with help of these buffers
+     * have direct virial contributions, set the parameter to true, so
+     * an extra force buffer is available for these forces to enable
+     * correct virial computation.
+     */
+    ForceHelperBuffers(bool haveDirectVirialContributions);
+
+    //! Returns whether we have a direct virial contribution force buffer
+    bool haveDirectVirialContributions() const { return haveDirectVirialContributions_; }
+
+    //! Returns the buffer for direct virial contributions
+    gmx::ArrayRef<gmx::RVec> forceBufferForDirectVirialContributions()
+    {
+        GMX_ASSERT(haveDirectVirialContributions_, "Buffer can only be requested when present");
+        return forceBufferForDirectVirialContributions_;
+    }
+
+    //! Returns the buffer for shift forces, size SHIFTS
+    gmx::ArrayRef<gmx::RVec> shiftForces() { return shiftForces_; }
+
+    //! Resizes the direct virial contribution buffer, when present
+    void resize(int numAtoms);
+
+private:
+    //! True when we have contributions that are directly added to the virial
+    bool haveDirectVirialContributions_ = false;
+    //! Force buffer for force computation with direct virial contributions
+    std::vector<gmx::RVec> forceBufferForDirectVirialContributions_;
+    //! Shift force array for computing the virial, size SHIFTS
+    std::vector<gmx::RVec> shiftForces_;
+};
+
 struct t_forcerec
 { // NOLINT (clang-analyzer-optin.performance.Padding)
     // Declare an explicit constructor and destructor, so they can be
@@ -134,7 +186,7 @@ struct t_forcerec
     struct interaction_const_t* ic = nullptr;
 
     /* PBC stuff */
-    int ePBC = 0;
+    PbcType pbcType = PbcType::Xyz;
     //! Tells whether atoms inside a molecule can be in different periodic images,
     //  i.e. whether we need to take into account PBC when computing distances inside molecules.
     //  This determines whether PBC must be considered for e.g. bonded interactions.
@@ -164,11 +216,10 @@ struct t_forcerec
      */
     real rlist = 0;
 
-    /* Charge sum and dipole for topology A/B ([0]/[1]) for Ewald corrections */
-    double qsum[2]   = { 0 };
-    double q2sum[2]  = { 0 };
-    double c6sum[2]  = { 0 };
-    rvec   mu_tot[2] = { { 0 } };
+    /* Charge sum for topology A/B ([0]/[1]) for Ewald corrections */
+    double qsum[2]  = { 0 };
+    double q2sum[2] = { 0 };
+    double c6sum[2] = { 0 };
 
     /* Dispersion correction stuff */
     std::unique_ptr<DispersionCorrection> dispersionCorrection;
@@ -183,23 +234,16 @@ struct t_forcerec
     t_forcetable* pairsTable = nullptr; /* for 1-4 interactions, [pairs] and [pairs_nb] */
 
     /* Free energy */
-    int  efep          = 0;
-    real sc_alphavdw   = 0;
-    real sc_alphacoul  = 0;
-    int  sc_power      = 0;
-    real sc_r_power    = 0;
-    real sc_sigma6_def = 0;
-    real sc_sigma6_min = 0;
+    int efep = 0;
 
     /* Information about atom properties for the molecule blocks in the system */
-    struct cginfo_mb_t* cginfo_mb = nullptr;
+    std::vector<cginfo_mb_t> cginfo_mb;
     /* Information about atom properties for local and non-local atoms */
     std::vector<int> cginfo;
 
     rvec* shift_vec = nullptr;
 
-    int      cutoff_scheme = 0;     /* group- or Verlet-style cutoff */
-    gmx_bool bNonbonded    = FALSE; /* true if nonbonded calculations are *not* turned off */
+    std::unique_ptr<gmx::WholeMoleculeTransform> wholeMoleculeTransform;
 
     /* The Nbnxm Verlet non-bonded machinery */
     std::unique_ptr<nonbonded_verlet_t> nbv;
@@ -210,18 +254,11 @@ struct t_forcerec
 
     /* The number of atoms participating in do_force_lowlevel */
     int natoms_force = 0;
-    /* The number of atoms participating in force and constraints */
+    /* The number of atoms participating in force calculation and constraints */
     int natoms_force_constr = 0;
-    /* The allocation size of vectors of size natoms_force */
-    int nalloc_force = 0;
 
-    /* Forces that should not enter into the coord x force virial summation:
-     * PPPM/PME/Ewald/posres/ForceProviders
-     */
-    /* True when we have contributions that are directly added to the virial */
-    bool haveDirectVirialContributions = false;
-    /* Force buffer for force computation with direct virial contributions */
-    std::vector<gmx::RVec> forceBufferForDirectVirialContributions;
+    /* List of helper buffers for ForceOutputs, one for each time step with MTS */
+    std::vector<ForceHelperBuffers> forceHelperBuffers;
 
     /* Data for PPPM/PME/Ewald */
     struct gmx_pme_t* pmedata                = nullptr;
@@ -230,14 +267,11 @@ struct t_forcerec
     /* PME/Ewald stuff */
     struct gmx_ewald_tab_t* ewald_table = nullptr;
 
-    /* Shift force array for computing the virial, size SHIFTS */
-    std::vector<gmx::RVec> shiftForces;
-
     /* Non bonded Parameter lists */
-    int      ntype        = 0; /* Number of atom types */
-    gmx_bool bBHAM        = FALSE;
-    real*    nbfp         = nullptr;
-    real*    ljpme_c6grid = nullptr; /* C6-values used on grid in LJPME */
+    int               ntype = 0; /* Number of atom types */
+    gmx_bool          bBHAM = FALSE;
+    std::vector<real> nbfp;
+    real*             ljpme_c6grid = nullptr; /* C6-values used on grid in LJPME */
 
     /* Energy group pair flags */
     int* egp_flags = nullptr;
@@ -252,13 +286,6 @@ struct t_forcerec
      */
     int n_tpi = 0;
 
-    /* QMMM stuff */
-    gmx_bool          bQMMM = FALSE;
-    struct t_QMMMrec* qr    = nullptr;
-
-    /* QM-MM neighborlists */
-    struct t_nblist* QMMMlist = nullptr;
-
     /* Limit for printing large forces, negative is don't print */
     real print_force = 0;
 
@@ -272,8 +299,14 @@ struct t_forcerec
     real userreal3 = 0;
     real userreal4 = 0;
 
-    /* Pointer to struct for managing threading of bonded force calculation */
-    struct bonded_threading_t* bondedThreading = nullptr;
+    /* Tells whether we use multiple time stepping, computing some forces less frequently */
+    bool useMts = false;
+
+    /* Data for special listed force calculations */
+    std::unique_ptr<t_fcdata> fcdata;
+
+    // The listed forces calculation data, 1 entry or multiple entries with multiple time stepping
+    std::vector<ListedForces> listedForces;
 
     /* TODO: Replace the pointer by an object once we got rid of C */
     gmx::GpuBonded* gpuBonded = nullptr;
@@ -288,9 +321,17 @@ struct t_forcerec
     // TODO: This is not supposed to be here. StatePropagatorDataGpu should be a part of
     //       general StatePropagatorData object that is passed around
     gmx::StatePropagatorDataGpu* stateGpu = nullptr;
+    // TODO: Should not be here. This is here only to pass the pointer around.
+    gmx::DeviceStreamManager* deviceStreamManager = nullptr;
+
+    //! GPU device context
+    DeviceContext* deviceContext = nullptr;
 
     /* For PME-PP GPU communication */
     std::unique_ptr<gmx::PmePpCommGpu> pmePpCommGpu;
+
+    /* For GPU force reduction (on both local and non-local atoms) */
+    gmx::EnumerationArray<gmx::AtomLocality, std::unique_ptr<gmx::GpuForceReduction>> gpuForceReduction;
 };
 
 /* Important: Starting with Gromacs-4.6, the values of c6 and c12 in the nbfp array have
index c9b1f594024c9b7d761bc2b45f215f7b32f44549..83283417b4873cfa1b0ef9d52d0c47db6725c25b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -51,6 +51,7 @@ namespace gmx
 class ForceProviders;
 class IMDOutputProvider;
 class IMdpOptionProvider;
+struct MdModulesNotifier;
 
 /*! \libinternal \brief
  * Extension module for \Gromacs simulations.
@@ -73,6 +74,10 @@ public:
     virtual IMDOutputProvider* outputProvider() = 0;
     //! Initializes force providers from this module.
     virtual void initForceProviders(ForceProviders* forceProviders) = 0;
+    //! Subscribe to simulation setup notifications
+    virtual void subscribeToSimulationSetupNotifications(MdModulesNotifier* notifier) = 0;
+    //! Subscribe to pre processing notifications
+    virtual void subscribeToPreProcessingNotifications(MdModulesNotifier* notifier) = 0;
 };
 
 } // namespace gmx
index 04d60adb04889850f06c75b47fbaf6212ffd53b8..4f0ec9ca8feb62887dc2e51971ce07ae6f48e846 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2010, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <cstring>
 
 #include <algorithm>
+#include <numeric>
 
 #include "gromacs/math/veccompare.h"
 #include "gromacs/math/vecdump.h"
 #include "gromacs/mdtypes/awh_params.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/multipletimestepping.h"
 #include "gromacs/mdtypes/pull_params.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/utility/compare.h"
@@ -64,6 +67,9 @@
 //! Macro to select a bool name
 #define EBOOL(e) gmx::boolToString(e)
 
+/* Default values for nstcalcenergy, used when the are no other restrictions. */
+constexpr int c_defaultNstCalcEnergy = 10;
+
 /* The minimum number of integration steps required for reasonably accurate
  * integration of first and second order coupling algorithms.
  */
@@ -71,6 +77,12 @@ const int nstmin_berendsen_tcouple = 5;
 const int nstmin_berendsen_pcouple = 10;
 const int nstmin_harmonic          = 20;
 
+/* Default values for T- and P- coupling intervals, used when the are no other
+ * restrictions.
+ */
+constexpr int c_defaultNstTCouple = 10;
+constexpr int c_defaultNstPCouple = 10;
+
 t_inputrec::t_inputrec()
 {
     // TODO When this memset is removed, remove the suppression of
@@ -86,21 +98,25 @@ t_inputrec::~t_inputrec()
     done_inputrec(this);
 }
 
-static int nst_wanted(const t_inputrec* ir)
+int ir_optimal_nstcalcenergy(const t_inputrec* ir)
 {
+    int nst;
+
     if (ir->nstlist > 0)
     {
-        return ir->nstlist;
+        nst = ir->nstlist;
     }
     else
     {
-        return 10;
+        nst = c_defaultNstCalcEnergy;
     }
-}
 
-int ir_optimal_nstcalcenergy(const t_inputrec* ir)
-{
-    return nst_wanted(ir);
+    if (ir->useMts)
+    {
+        nst = std::lcm(nst, ir->mtsLevels.back().stepFactor);
+    }
+
+    return nst;
 }
 
 int tcouple_min_integration_steps(int etc)
@@ -133,7 +149,7 @@ int ir_optimal_nsttcouple(const t_inputrec* ir)
 
     nmin = tcouple_min_integration_steps(ir->etc);
 
-    nwanted = nst_wanted(ir);
+    nwanted = c_defaultNstTCouple;
 
     tau_min = 1e20;
     if (ir->etc != etcNO)
@@ -174,7 +190,8 @@ int pcouple_min_integration_steps(int epc)
     switch (epc)
     {
         case epcNO: n = 0; break;
-        case etcBERENDSEN:
+        case epcBERENDSEN:
+        case epcCRESCALE:
         case epcISOTROPIC: n = nstmin_berendsen_pcouple; break;
         case epcPARRINELLORAHMAN:
         case epcMTTK: n = nstmin_harmonic; break;
@@ -186,35 +203,47 @@ int pcouple_min_integration_steps(int epc)
 
 int ir_optimal_nstpcouple(const t_inputrec* ir)
 {
-    int nmin, nwanted, n;
+    const int minIntegrationSteps = pcouple_min_integration_steps(ir->epc);
 
-    nmin = pcouple_min_integration_steps(ir->epc);
+    const int nwanted = c_defaultNstPCouple;
 
-    nwanted = nst_wanted(ir);
+    // With multiple time-stepping we can only compute the pressure at slowest steps
+    const int minNstPCouple = (ir->useMts ? ir->mtsLevels.back().stepFactor : 1);
 
-    if (nmin == 0 || ir->delta_t * nwanted <= ir->tau_p)
+    int n;
+    if (minIntegrationSteps == 0 || ir->delta_t * nwanted <= ir->tau_p)
     {
         n = nwanted;
     }
     else
     {
-        n = static_cast<int>(ir->tau_p / (ir->delta_t * nmin) + 0.001);
-        if (n < 1)
+        n = static_cast<int>(ir->tau_p / (ir->delta_t * minIntegrationSteps) + 0.001);
+        if (n < minNstPCouple)
         {
-            n = 1;
+            n = minNstPCouple;
         }
-        while (nwanted % n != 0)
+        // Without MTS we try to make nstpcouple a "nice" number
+        if (!ir->useMts)
         {
-            n--;
+            while (nwanted % n != 0)
+            {
+                n--;
+            }
         }
     }
 
+    // With MTS, nstpcouple should be a multiple of the slowest MTS interval
+    if (ir->useMts)
+    {
+        n = n - (n % minNstPCouple);
+    }
+
     return n;
 }
 
 gmx_bool ir_coulomb_switched(const t_inputrec* ir)
 {
-    return (ir->coulombtype == eelSWITCH || ir->coulombtype == eelSHIFT || ir->coulombtype == eelENCADSHIFT
+    return (ir->coulombtype == eelSWITCH || ir->coulombtype == eelSHIFT
             || ir->coulombtype == eelPMESWITCH || ir->coulombtype == eelPMEUSERSWITCH
             || ir->coulomb_modifier == eintmodPOTSWITCH || ir->coulomb_modifier == eintmodFORCESWITCH);
 }
@@ -232,7 +261,7 @@ gmx_bool ir_coulomb_might_be_zero_at_cutoff(const t_inputrec* ir)
 
 gmx_bool ir_vdw_switched(const t_inputrec* ir)
 {
-    return (ir->vdwtype == evdwSWITCH || ir->vdwtype == evdwSHIFT || ir->vdwtype == evdwENCADSHIFT
+    return (ir->vdwtype == evdwSWITCH || ir->vdwtype == evdwSHIFT
             || ir->vdw_modifier == eintmodPOTSWITCH || ir->vdw_modifier == eintmodFORCESWITCH);
 }
 
@@ -280,6 +309,24 @@ static void done_lambdas(t_lambda* fep)
     sfree(fep->all_lambda);
 }
 
+static void done_t_rot(t_rot* rot)
+{
+    if (rot == nullptr)
+    {
+        return;
+    }
+    if (rot->grp != nullptr)
+    {
+        for (int i = 0; i < rot->ngrp; i++)
+        {
+            sfree(rot->grp[i].ind);
+            sfree(rot->grp[i].x_ref);
+        }
+        sfree(rot->grp);
+    }
+    sfree(rot);
+}
+
 void done_inputrec(t_inputrec* ir)
 {
     sfree(ir->opts.nrdf);
@@ -296,16 +343,6 @@ void done_inputrec(t_inputrec* ir)
     sfree(ir->opts.tau_t);
     sfree(ir->opts.acc);
     sfree(ir->opts.nFreeze);
-    sfree(ir->opts.QMmethod);
-    sfree(ir->opts.QMbasis);
-    sfree(ir->opts.QMcharge);
-    sfree(ir->opts.QMmult);
-    sfree(ir->opts.bSH);
-    sfree(ir->opts.CASorbitals);
-    sfree(ir->opts.CASelectrons);
-    sfree(ir->opts.SAon);
-    sfree(ir->opts.SAoff);
-    sfree(ir->opts.SAsteps);
     sfree(ir->opts.egp_flags);
     done_lambdas(ir->fepvals);
     sfree(ir->fepvals);
@@ -317,29 +354,10 @@ void done_inputrec(t_inputrec* ir)
         done_pull_params(ir->pull);
         sfree(ir->pull);
     }
+    done_t_rot(ir->rot);
     delete ir->params;
 }
 
-static void pr_qm_opts(FILE* fp, int indent, const char* title, const t_grpopts* opts)
-{
-    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, "SH", 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, "SAoff", opts->SAoff, opts->ngQM, FALSE);
-        pr_ivec(fp, indent, "SAsteps", opts->SAsteps, opts->ngQM, FALSE);
-    }
-}
-
 static void pr_grp_opts(FILE* out, int indent, const char* title, const t_grpopts* opts, gmx_bool bMDPformat)
 {
     int i, m, j;
@@ -649,8 +667,6 @@ static void pr_awh_bias_dim(FILE* fp, int indent, gmx::AwhDimParams* awhDimParam
     PR("period", awhDimParams->period);
     PR("force-constant", awhDimParams->forceConstant);
     PR("diffusion", awhDimParams->diffusion);
-    PR("start", awhDimParams->origin);
-    PR("end", awhDimParams->end);
     PR("cover-diameter", awhDimParams->coverDiameter);
 }
 
@@ -822,6 +838,30 @@ void pr_inputrec(FILE* fp, int indent, const char* title, const t_inputrec* ir,
         PSTEP("nsteps", ir->nsteps);
         PSTEP("init-step", ir->init_step);
         PI("simulation-part", ir->simulation_part);
+        PS("mts", EBOOL(ir->useMts));
+        if (ir->useMts)
+        {
+            for (int mtsIndex = 1; mtsIndex < static_cast<int>(ir->mtsLevels.size()); mtsIndex++)
+            {
+                const auto&       mtsLevel = ir->mtsLevels[mtsIndex];
+                const std::string forceKey = gmx::formatString("mts-level%d-forces", mtsIndex + 1);
+                std::string       forceGroups;
+                for (int i = 0; i < static_cast<int>(gmx::MtsForceGroups::Count); i++)
+                {
+                    if (mtsLevel.forceGroups[i])
+                    {
+                        if (!forceGroups.empty())
+                        {
+                            forceGroups += " ";
+                        }
+                        forceGroups += gmx::mtsForceGroupNames[i];
+                    }
+                }
+                PS(forceKey.c_str(), forceGroups.c_str());
+                const std::string factorKey = gmx::formatString("mts-level%d-factor", mtsIndex + 1);
+                PI(factorKey.c_str(), mtsLevel.stepFactor);
+            }
+        }
         PS("comm-mode", ECOM(ir->comm_mode));
         PI("nstcomm", ir->nstcomm);
 
@@ -853,7 +893,7 @@ void pr_inputrec(FILE* fp, int indent, const char* title, const t_inputrec* ir,
         /* Neighborsearching parameters */
         PS("cutoff-scheme", ECUTSCHEME(ir->cutoff_scheme));
         PI("nstlist", ir->nstlist);
-        PS("pbc", epbc_names[ir->ePBC]);
+        PS("pbc", c_pbcTypeNames[ir->pbcType].c_str());
         PS("periodic-molecules", EBOOL(ir->bPeriodicMols));
         PR("verlet-buffer-tolerance", ir->verletbuf_tol);
         PR("rlist", ir->rlist);
@@ -926,10 +966,8 @@ void pr_inputrec(FILE* fp, int indent, const char* title, const t_inputrec* ir,
 
         /* QMMM */
         PS("QMMM", EBOOL(ir->bQMMM));
-        PI("QMconstraints", ir->QMconstraints);
-        PI("QMMMscheme", ir->QMMMscheme);
-        PR("MMChargeScaleFactor", ir->scalefactor);
-        pr_qm_opts(fp, indent, "qm-opts", &(ir->opts));
+        fprintf(fp, "%s:\n", "qm-opts");
+        pr_int(fp, indent, "ngQM", ir->opts.ngQM);
 
         /* CONSTRAINT OPTIONS */
         PS("constraint-algorithm", ECONSTRTYPE(ir->eConstrAlg));
@@ -1292,7 +1330,16 @@ void cmp_inputrec(FILE* fp, const t_inputrec* ir1, const t_inputrec* ir2, real f
     cmp_int64(fp, "inputrec->nsteps", ir1->nsteps, ir2->nsteps);
     cmp_int64(fp, "inputrec->init_step", ir1->init_step, ir2->init_step);
     cmp_int(fp, "inputrec->simulation_part", -1, ir1->simulation_part, ir2->simulation_part);
-    cmp_int(fp, "inputrec->ePBC", -1, ir1->ePBC, ir2->ePBC);
+    cmp_int(fp, "inputrec->mts", -1, static_cast<int>(ir1->useMts), static_cast<int>(ir2->useMts));
+    if (ir1->useMts && ir2->useMts)
+    {
+        cmp_int(fp, "inputrec->mts-levels", -1, ir1->mtsLevels.size(), ir2->mtsLevels.size());
+        cmp_int(fp, "inputrec->mts-level2-forces", -1, ir1->mtsLevels[1].forceGroups.to_ulong(),
+                ir2->mtsLevels[1].forceGroups.to_ulong());
+        cmp_int(fp, "inputrec->mts-level2-factor", -1, ir1->mtsLevels[1].stepFactor,
+                ir2->mtsLevels[1].stepFactor);
+    }
+    cmp_int(fp, "inputrec->pbcType", -1, static_cast<int>(ir1->pbcType), static_cast<int>(ir2->pbcType));
     cmp_bool(fp, "inputrec->bPeriodicMols", -1, ir1->bPeriodicMols, ir2->bPeriodicMols);
     cmp_int(fp, "inputrec->cutoff_scheme", -1, ir1->cutoff_scheme, ir2->cutoff_scheme);
     cmp_int(fp, "inputrec->nstlist", -1, ir1->nstlist, ir2->nstlist);
@@ -1485,13 +1532,13 @@ gmx_bool inputrecNphTrotter(const t_inputrec* ir)
 
 bool inputrecPbcXY2Walls(const t_inputrec* ir)
 {
-    return (ir->ePBC == epbcXY && ir->nwall == 2);
+    return (ir->pbcType == PbcType::XY && ir->nwall == 2);
 }
 
 bool integratorHasConservedEnergyQuantity(const t_inputrec* ir)
 {
     if (!EI_MD(ir->eI))
-    {
+    { // NOLINT bugprone-branch-clone
         // Energy minimization or stochastic integrator: no conservation
         return false;
     }
@@ -1524,7 +1571,7 @@ int inputrec2nboundeddim(const t_inputrec* ir)
     }
     else
     {
-        return ePBC2npbcdim(ir->ePBC);
+        return numPbcDimensions(ir->pbcType);
     }
 }
 
@@ -1532,12 +1579,12 @@ int ndof_com(const t_inputrec* ir)
 {
     int n = 0;
 
-    switch (ir->ePBC)
+    switch (ir->pbcType)
     {
-        case epbcXYZ:
-        case epbcNONE: n = 3; break;
-        case epbcXY: n = (ir->nwall == 0 ? 3 : 2); break;
-        case epbcSCREW: n = 1; break;
+        case PbcType::Xyz:
+        case PbcType::No: n = 3; break;
+        case PbcType::XY: n = (ir->nwall == 0 ? 3 : 2); break;
+        case PbcType::Screw: n = 1; break;
         default: gmx_incons("Unknown pbc in calc_nrdf");
     }
 
@@ -1576,3 +1623,15 @@ bool haveEwaldSurfaceContribution(const t_inputrec& ir)
 {
     return EEL_PME_EWALD(ir.coulombtype) && (ir.ewald_geometry == eewg3DC || ir.epsilon_surface != 0);
 }
+
+bool haveFreeEnergyType(const t_inputrec& ir, const int fepType)
+{
+    for (int i = 0; i < ir.fepvals->n_lambda; i++)
+    {
+        if (ir.fepvals->all_lambda[fepType][i] > 0)
+        {
+            return true;
+        }
+    }
+    return false;
+}
index 266670f3ef5187759f1ce66e7abffee034ab8a5c..40ed79ea8de4909d66637d16e4b412c3c827e3fa 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,6 +41,7 @@
 #include <cstdio>
 
 #include <memory>
+#include <vector>
 
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdtypes/md_enums.h"
@@ -58,8 +60,11 @@ namespace gmx
 class Awh;
 struct AwhParams;
 class KeyValueTreeObject;
+struct MtsLevel;
 } // namespace gmx
 
+enum class PbcType;
+
 struct t_grpopts
 {
     //! Number of T-Coupl groups
@@ -96,26 +101,6 @@ struct t_grpopts
     /* QMMM stuff */
     //! Number of QM groups
     int ngQM;
-    //! Level of theory in the QM calculation
-    int* QMmethod;
-    //! Basisset in the QM calculation
-    int* QMbasis;
-    //! Total charge in the QM region
-    int* QMcharge;
-    //! Spin multiplicicty in the QM region
-    int* QMmult;
-    //! Surface hopping (diabatic hop only)
-    gmx_bool* bSH;
-    //! Number of orbiatls in the active space
-    int* CASorbitals;
-    //! Number of electrons in the active space
-    int* CASelectrons;
-    //! At which gap (A.U.) the SA is switched on
-    real* SAon;
-    //! At which gap (A.U.) the SA is switched off
-    real* SAoff;
-    //! In how many steps SA goes from 1-1 to 0.5-0.5
-    int* SAsteps;
 };
 
 struct t_simtemp
@@ -365,6 +350,10 @@ struct t_inputrec // NOLINT (clang-analyzer-optin.performance.Padding)
     double init_t;
     //! Time step (ps)
     double delta_t;
+    //! Whether we use multiple time stepping
+    bool useMts;
+    //! The multiple time stepping levels
+    std::vector<gmx::MtsLevel> mtsLevels;
     //! Precision of x in compressed trajectory file
     real x_compression_precision;
     //! Requested fourier_spacing, when nk? not set
@@ -388,7 +377,7 @@ struct t_inputrec // NOLINT (clang-analyzer-optin.performance.Padding)
     //! Type of combination rule in LJ-PME
     int ljpme_combination_rule;
     //! Type of periodic boundary conditions
-    int ePBC;
+    PbcType pbcType;
     //! Periodic molecules
     bool bPeriodicMols;
     //! Continuation run: starting state is correct (ie. constrained)
@@ -569,12 +558,6 @@ struct t_inputrec // NOLINT (clang-analyzer-optin.performance.Padding)
     t_grpopts opts;
     //! QM/MM calculation
     gmx_bool bQMMM;
-    //! Constraints on QM bonds
-    int QMconstraints;
-    //! Scheme: ONIOM or normal
-    int QMMMscheme;
-    //! Factor for scaling the MM charges in QM calc.
-    real scalefactor;
 
     /* Fields for removed features go here (better caching) */
     //! Whether AdResS is enabled - always false if a valid .tpr was read
@@ -662,7 +645,11 @@ gmx_bool inputrecNphTrotter(const t_inputrec* ir);
 bool inputrecPbcXY2Walls(const t_inputrec* ir);
 
 /*! \brief Returns true for MD integator with T and/or P-coupling that supports
- * calculating the conserved energy quantity.
+ * calculating a conserved energy quantity.
+ *
+ * Note that dynamical integrators without T and P coupling (ie NVE)
+ * return false, i.e. the return value refers to whether there
+ * is a conserved quantity different than the total energy.
  */
 bool integratorHasConservedEnergyQuantity(const t_inputrec* ir);
 
@@ -697,4 +684,13 @@ real maxReferenceTemperature(const t_inputrec& ir);
  */
 bool haveEwaldSurfaceContribution(const t_inputrec& ir);
 
+/*! \brief Check if calculation of the specific FEP type was requested.
+ *
+ * \param[in] ir       Input record.
+ * \param[in] fepType  Free-energy perturbation type to check for.
+ *
+ * \returns If the \p fepType is perturbed in this run.
+ */
+bool haveFreeEnergyType(const t_inputrec& ir, int fepType);
+
 #endif /* GMX_MDTYPES_INPUTREC_H */
similarity index 70%
rename from src/gromacs/hardware/gpu_hw_info.cpp
rename to src/gromacs/mdtypes/interaction_const.cpp
index 8c79bf432028c32c3f5475729ac81c6c0f73099f..230dca2bff54cdb586c950b73cdd783ee7e76f59 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  */
 #include "gmxpre.h"
 
-#include "gpu_hw_info.h"
+#include "interaction_const.h"
 
-#include "config.h"
+#include <cstdio>
 
-/* Note that some of the following arrays must match the "GPU support
- * enumeration" in src/config.h.cmakein, so that GMX_GPU looks up an
- * array entry. */
+#include "gromacs/math/functions.h"
+#include "gromacs/mdtypes/inputrec.h"
 
-// TODO If/when we unify CUDA and OpenCL support code, this should
-// move to a single place in gpu_utils.
-/* Names of the GPU detection/check results (see e_gpu_detect_res_t in hw_info.h). */
-const char* const gpu_detect_res_str[egpuNR] = {
-    "compatible",   "nonexistent",
-    "incompatible", "incompatible (please recompile with GMX_OPENCL_NB_CLUSTER_SIZE=4)",
-    "insane",       "unavailable"
-};
+interaction_const_t::SoftCoreParameters::SoftCoreParameters(const t_lambda& fepvals) :
+    alphaVdw(fepvals.sc_alpha),
+    alphaCoulomb(fepvals.bScCoul ? fepvals.sc_alpha : 0),
+    lambdaPower(fepvals.sc_power),
+    sigma6WithInvalidSigma(gmx::power6(fepvals.sc_sigma)),
+    sigma6Minimum(fepvals.bScCoul ? gmx::power6(fepvals.sc_sigma_min) : 0)
+{
+    // This is checked during tpr reading, so we can assert here
+    GMX_RELEASE_ASSERT(fepvals.sc_r_power == 6.0, "We only support soft-core r-power 6");
+}
index a034e7dd781ecefb99f7b987720608e4a65b2d9e..9114297bab5e789f37299e823228083122b0e931 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +44,8 @@
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
+struct t_lambda;
+
 /* Used with force switching or a constant potential shift:
  * rsw       = max(r - r_switch, 0)
  * force/p   = r^-(p+1) + c2*rsw^2 + c3*rsw^3
@@ -103,7 +106,25 @@ struct EwaldCorrectionTables
  */
 struct interaction_const_t
 {
-    int cutoff_scheme = ecutsVERLET;
+    /* This struct contains the soft-core parameters from t_lambda,
+     * but processed for direct use in the kernels.
+     */
+    struct SoftCoreParameters
+    {
+        // Constructor
+        SoftCoreParameters(const t_lambda& fepvals);
+
+        // Alpha parameter for Van der Waals interactions
+        real alphaVdw;
+        // Alpha parameter for Coulomb interactions
+        real alphaCoulomb;
+        // Exponent for the dependence of the soft-core on lambda
+        int lambdaPower;
+        // Value for sigma^6 for LJ interaction with C6<=0 and/or C12<=0
+        real sigma6WithInvalidSigma;
+        // Minimum value for sigma^6, used when soft-core is applied to Coulomb interactions
+        real sigma6Minimum;
+    };
 
     /* VdW */
     int                    vdwtype          = evdwCUT;
@@ -145,6 +166,9 @@ struct interaction_const_t
     std::unique_ptr<EwaldCorrectionTables> coulombEwaldTables;
     // Van der Waals Ewald correction table
     std::unique_ptr<EwaldCorrectionTables> vdwEwaldTables;
+
+    // Free-energy parameters, only present when free-energy calculations are requested
+    std::unique_ptr<SoftCoreParameters> softCoreParameters;
 };
 
 #endif
index 98d46895b98af45cb5d0dc58dc184e099a6f2ca5..65eef2ff80294259cfa8b9b8a8c5cc400b9a5fbf 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -72,8 +73,8 @@ const char* eel_names[eelNR + 1] = { "Cut-off",
                                      "Shift",
                                      "User",
                                      "Generalized-Born (unused)",
-                                     "Reaction-Field-nec",
-                                     "Encad-shift",
+                                     "Reaction-Field-nec (unsupported)",
+                                     "Encad-shift (unused)",
                                      "PME-User",
                                      "PME-Switch",
                                      "PME-User-Switch",
@@ -84,8 +85,8 @@ const char* eewg_names[eewgNR + 1] = { "3d", "3dc", nullptr };
 
 const char* eljpme_names[eljpmeNR + 1] = { "Geometric", "Lorentz-Berthelot", nullptr };
 
-const char* evdw_names[evdwNR + 1] = { "Cut-off",     "Switch", "Shift", "User",
-                                       "Encad-shift", "PME",    nullptr };
+const char* evdw_names[evdwNR + 1] = { "Cut-off", "Switch", "Shift", "User", "Encad-shift (unused)",
+                                       "PME",     nullptr };
 
 const char* econstr_names[econtNR + 1] = { "Lincs", "Shake", nullptr };
 
@@ -98,9 +99,9 @@ const char* etcoupl_names[etcNR + 1] = {
     "No", "Berendsen", "Nose-Hoover", "yes", "Andersen", "Andersen-massive", "V-rescale", nullptr
 }; /* yes is alias for berendsen */
 
-const char* epcoupl_names[epcNR + 1] = {
-    "No", "Berendsen", "Parrinello-Rahman", "Isotropic", "MTTK", nullptr
-}; /* isotropic is alias for berendsen */
+const char* epcoupl_names[epcNR + 1] = { "No",        "Berendsen", "Parrinello-Rahman",
+                                         "Isotropic", "MTTK",      "C-rescale",
+                                         nullptr }; /* isotropic is alias for berendsen */
 
 const char* epcoupltype_names[epctNR + 1] = { "Isotropic", "Semiisotropic", "Anisotropic",
                                               "Surface-Tension", nullptr };
@@ -175,17 +176,6 @@ const char* eSwapTypes_names[eSwapTypesNR + 1] = { "no", "X", "Y", "Z", nullptr
 
 const char* eSwapFixedGrp_names[eSwapFixedGrpNR + 1] = { "Split0", "Split1", "Solvent", nullptr };
 
-
-const char* eQMmethod_names[eQMmethodNR + 1] = { "AM1",      "PM3",    "RHF",  "UHF",
-                                                 "DFT",      "B3LYP",  "MP2",  "CASSCF",
-                                                 "B3LYPLAN", "DIRECT", nullptr };
-
-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", nullptr };
-
-const char* eQMMMscheme_names[eQMMMschemeNR + 1] = { "normal", "ONIOM", nullptr };
-
 const char* gmx_nblist_geometry_names[GMX_NBLIST_GEOMETRY_NR + 1] = {
     "Particle-Particle", "Water3-Particle", "Water3-Water3", "Water4-Particle",
     "Water4-Water4",     "CG-CG",           nullptr
index 307f69e56a7df21b742e7d9c552d34bfe2e75aa5..508a94f6bf16f2314cca7425277b46a6c5724563 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -114,6 +115,7 @@ enum
     epcPARRINELLORAHMAN,
     epcISOTROPIC,
     epcMTTK,
+    epcCRESCALE,
     epcNR
 };
 //! String corresponding to pressure coupling algorithm
@@ -238,7 +240,7 @@ enum
     eelUSER,
     eelGB_NOTUSED,
     eelRF_NEC_UNSUPPORTED,
-    eelENCADSHIFT,
+    eelENCADSHIFT_NOTUSED,
     eelPMEUSER,
     eelPMESWITCH,
     eelPMEUSERSWITCH,
@@ -281,7 +283,7 @@ enum
     evdwSWITCH,
     evdwSHIFT,
     evdwUSER,
-    evdwENCADSHIFT,
+    evdwENCADSHIFT_UNUSED,
     evdwPME,
     evdwNR
 };
@@ -770,58 +772,6 @@ enum
 //! String for swap group splitting
 extern const char* eSwapFixedGrp_names[eSwapFixedGrpNR + 1];
 
-//! QMMM methods.
-enum
-{
-    eQMmethodAM1,
-    eQMmethodPM3,
-    eQMmethodRHF,
-    eQMmethodUHF,
-    eQMmethodDFT,
-    eQMmethodB3LYP,
-    eQMmethodMP2,
-    eQMmethodCASSCF,
-    eQMmethodB3LYPLAN,
-    eQMmethodDIRECT,
-    eQMmethodNR
-};
-//! String corresponding to QMMM methods
-extern const char* eQMmethod_names[eQMmethodNR + 1];
-//! Macro to pick QMMM method name
-#define EQMMETHOD(e) enum_name(e, eQMmethodNR, eQMmethod_names)
-
-//! QMMM basis function for QM part
-enum
-{
-    eQMbasisSTO3G,
-    eQMbasisSTO3G2,
-    eQMbasis321G,
-    eQMbasis321Gp,
-    eQMbasis321dGp,
-    eQMbasis621G,
-    eQMbasis631G,
-    eQMbasis631Gp,
-    eQMbasis631dGp,
-    eQMbasis6311G,
-    eQMbasisNR
-};
-//! Name for QMMM basis function
-extern const char* eQMbasis_names[eQMbasisNR + 1];
-//! Macro to pick right basis function string
-#define EQMBASIS(e) enum_name(e, eQMbasisNR, eQMbasis_names)
-
-//! QMMM scheme
-enum
-{
-    eQMMMschemenormal,
-    eQMMMschemeoniom,
-    eQMMMschemeNR
-};
-//! QMMMM scheme names
-extern const char* eQMMMscheme_names[eQMMMschemeNR + 1];
-//! Macro to pick QMMMM scheme name
-#define EQMMMSCHEME(e) enum_name(e, eQMMMschemeNR, eQMMMscheme_names)
-
 /*! \brief Neighborlist geometry type.
  *
  * Kernels will compute interactions between two particles,
@@ -886,11 +836,4 @@ enum gmx_nblist_interaction_type
 //! String corresponding to interactions in neighborlist code
 extern const char* gmx_nblist_interaction_names[GMX_NBLIST_INTERACTION_NR + 1];
 
-
-//! \brief QM/MM mode
-enum struct GmxQmmmMode
-{
-    GMX_QMMM_ORIGINAL,
-    GMX_QMMM_MIMIC
-};
 #endif /* GMX_MDTYPES_MD_ENUMS_H */
index 56b323106363db5c3d28b44f05e82432ad7cb6bf..c8a41643e6daec466d5e60578e47f28300ca3cc6 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -127,8 +128,6 @@ typedef struct t_mdatoms
     unsigned short* cU2;
     //! Group index for orientation restraints
     unsigned short* cORF;
-    //! QMMM atoms
-    gmx_bool* bQM;
     //! Number of atoms on this processor. TODO is this still used?
     int homenr;
     //! The lambda value used to create the contents of the struct
diff --git a/src/gromacs/mdtypes/multipletimestepping.cpp b/src/gromacs/mdtypes/multipletimestepping.cpp
new file mode 100644 (file)
index 0000000..be56600
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#include "gmxpre.h"
+
+#include "multipletimestepping.h"
+
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+int nonbondedMtsFactor(const t_inputrec& ir)
+{
+    GMX_RELEASE_ASSERT(!ir.useMts || ir.mtsLevels.size() == 2, "Only 2 MTS levels supported here");
+
+    if (ir.useMts && ir.mtsLevels[1].forceGroups[static_cast<int>(MtsForceGroups::Nonbonded)])
+    {
+        return ir.mtsLevels[1].stepFactor;
+    }
+    else
+    {
+        return 1;
+    }
+}
+
+void assertMtsRequirements(const t_inputrec& ir)
+{
+    if (!ir.useMts)
+    {
+        return;
+    }
+
+    GMX_RELEASE_ASSERT(ir.mtsLevels.size() >= 2, "Need at least two levels for MTS");
+
+    GMX_RELEASE_ASSERT(ir.mtsLevels[0].stepFactor == 1, "Base MTS step should be 1");
+
+    GMX_RELEASE_ASSERT(
+            (!EEL_FULL(ir.coulombtype) && !EVDW_PME(ir.vdwtype))
+                    || ir.mtsLevels.back().forceGroups[static_cast<int>(MtsForceGroups::LongrangeNonbonded)],
+            "Long-range nonbondeds should be in the highest MTS level");
+
+    for (const auto& mtsLevel : ir.mtsLevels)
+    {
+        const int mtsFactor = mtsLevel.stepFactor;
+        GMX_RELEASE_ASSERT(ir.nstcalcenergy % mtsFactor == 0,
+                           "nstcalcenergy should be a multiple of mtsFactor");
+        GMX_RELEASE_ASSERT(ir.nstenergy % mtsFactor == 0,
+                           "nstenergy should be a multiple of mtsFactor");
+        GMX_RELEASE_ASSERT(ir.nstlog % mtsFactor == 0, "nstlog should be a multiple of mtsFactor");
+        GMX_RELEASE_ASSERT(ir.epc == epcNO || ir.nstpcouple % mtsFactor == 0,
+                           "nstpcouple should be a multiple of mtsFactor");
+        GMX_RELEASE_ASSERT(ir.efep == efepNO || ir.fepvals->nstdhdl % mtsFactor == 0,
+                           "nstdhdl should be a multiple of mtsFactor");
+        if (ir.mtsLevels.back().forceGroups[static_cast<int>(gmx::MtsForceGroups::Nonbonded)])
+        {
+            GMX_RELEASE_ASSERT(ir.nstlist % ir.mtsLevels.back().stepFactor == 0,
+                               "With multiple time stepping for the non-bonded pair interactions, "
+                               "nstlist should be a "
+                               "multiple of mtsFactor");
+        }
+    }
+}
+
+} // namespace gmx
diff --git a/src/gromacs/mdtypes/multipletimestepping.h b/src/gromacs/mdtypes/multipletimestepping.h
new file mode 100644 (file)
index 0000000..a1a953a
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MULTIPLETIMESTEPPING_H
+#define GMX_MULTIPLETIMESTEPPING_H
+
+#include <bitset>
+
+#include "gromacs/utility/enumerationhelpers.h"
+
+struct t_inputrec;
+
+namespace gmx
+{
+
+//! Force group available for selection for multiple time step integration
+enum class MtsForceGroups : int
+{
+    LongrangeNonbonded, //!< PME-mesh or Ewald for electrostatics and/or LJ
+    Nonbonded,          //!< Non-bonded pair interactions
+    Pair,               //!< Bonded pair interactions
+    Dihedral,           //!< Dihedrals, including cmap (not restraints)
+    Angle,              //! Bonded angle potentials (not restraints)
+    Count               //! The number of groups above
+};
+
+static const gmx::EnumerationArray<MtsForceGroups, std::string> mtsForceGroupNames = {
+    "longrange-nonbonded", "nonbonded", "pair", "dihedral", "angle"
+};
+
+//! Setting for a single level for multiple time step integration
+struct MtsLevel
+{
+    //! The force group selection for this level;
+    std::bitset<static_cast<int>(MtsForceGroups::Count)> forceGroups;
+    //! The factor between the base, fastest, time step and the time step for this level
+    int stepFactor;
+};
+
+/*! \brief Returns the interval in steps at which the non-bonded pair forces are calculated
+ *
+ * Note: returns 1 when multiple time-stepping is not activated.
+ */
+int nonbondedMtsFactor(const t_inputrec& ir);
+
+//! (Release) Asserts that all multiple time-stepping requirements on \p ir are fulfilled
+void assertMtsRequirements(const t_inputrec& ir);
+
+} // namespace gmx
+
+#endif /* GMX_MULTIPLETIMESTEPPING_H */
index 1f542d7974da109af82c96648cde6cc7b3864a95..9ac98c5aaab459d7110ff2ace87c5fde8a4870be 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -37,6 +37,9 @@
 #ifndef GMX_MDTYPES_NBLIST_H
 #define GMX_MDTYPES_NBLIST_H
 
+#include <vector>
+
+#include "gromacs/utility/alignedallocator.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
@@ -170,15 +173,13 @@ struct t_forcetable
 {
     t_forcetable(enum gmx_table_interaction interaction, enum gmx_table_format format);
 
-    ~t_forcetable();
-
     enum gmx_table_interaction interaction; /* Types of interactions stored in this table */
     enum gmx_table_format      format;      /* Interpolation type and data format */
 
-    real  r;     /* range of the table */
-    int   n;     /* n+1 is the number of table points */
-    real  scale; /* distance (nm) between two table points */
-    real* data;  /* the actual table data */
+    real r;     /* range of the table */
+    int  n;     /* n+1 is the number of table points */
+    real scale; /* distance (nm) between two table points */
+    std::vector<real, gmx::AlignedAllocator<real>> data; /* the actual table data */
 
     /* Some information about the table layout. This can also be derived from the interpolation
      * type and the table interactions, but it is convenient to have here for sanity checks, and it
@@ -190,19 +191,6 @@ struct t_forcetable
     int stride; /* Distance to next table point (number of fp variables per table point in total) */
 };
 
-struct t_nblists
-{
-    struct t_forcetable* table_elec;
-    struct t_forcetable* table_vdw;
-    struct t_forcetable* table_elec_vdw;
-
-    /* The actual neighbor lists, short and long range, see enum above
-     * for definition of neighborlist indices.
-     */
-    struct t_nblist nlist_sr[eNL_NR];
-    struct t_nblist nlist_lr[eNL_NR];
-};
-
 struct gmx_ns_t
 {
     gmx_bool       bCGlist;
index 9bf230a5938222e00dff0449441eb7351a502b9d..c24ba0724c5ade8e4b5d04594d49246086d0526a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -69,6 +69,8 @@ public:
     bool haveDynamicBox = false;
     //! Whether neighbor searching needs to be done this step
     bool doNeighborSearch = false;
+    //! Whether the slow forces need to be computed this step (in addition to the faster forces)
+    bool computeSlowForces = false;
     //! Whether virial needs to be computed this step
     bool computeVirial = false;
     //! Whether energies need to be computed this step this step
@@ -91,6 +93,10 @@ public:
     bool useGpuFBufferOps = false;
     //! Whether PME forces are reduced with other contributions on the GPU this step
     bool useGpuPmeFReduction = false; // TODO: add this flag to the internal PME GPU data structures too
+    //! Whether GPU coordinates halo exchange is active this step
+    bool useGpuXHalo = false;
+    //! Whether GPU forces halo exchange is active this step
+    bool useGpuFHalo = false;
 };
 
 /*! \libinternal
@@ -111,12 +117,9 @@ class DomainLifetimeWorkload
 public:
     //! Whether the current nstlist step-range has bonded work to run on a GPU.
     bool haveGpuBondedWork = false;
-    //! Whether the current nstlist step-range has bonded work to run on he CPU.
+    //! Whether the current nstlist step-range has bonded work to run on the CPU.
     bool haveCpuBondedWork = false;
-    //! Whether the current nstlist step-range has restraints work to run on he CPU.
-    bool haveRestraintsWork = false;
-    //! Whether the current nstlist step-range has listed forces work to run on he CPU.
-    //  Note: currently this is haveCpuBondedWork | haveRestraintsWork
+    //! Whether the current nstlist step-range has listed (bonded + restraints) forces work to run on the CPU.
     bool haveCpuListedForceWork = false;
     //! Whether the current nstlist step-range has special forces on the CPU.
     bool haveSpecialForces = false;
@@ -142,6 +145,12 @@ public:
 class SimulationWorkload
 {
 public:
+    //! Whether to compute nonbonded pair interactions
+    bool computeNonbonded = false;
+    //! Wether nonbonded pair forces are to be computed at slow MTS steps only
+    bool computeNonbondedAtMtsLevel1 = false;
+    //! Whether total dipole needs to be computed
+    bool computeMuTot = false;
     //! If we have calculation of short range nonbondeds on CPU
     bool useCpuNonbonded = false;
     //! If we have calculation of short range nonbondeds on GPU
index a949d589a3164a79688c9e1512e73a53739d54cf..0f36009513b6d8f6afa60bb12ac7cc54221de96f 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -57,6 +58,9 @@
 #include "gromacs/utility/compare.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "checkpointdata.h"
 
 /* The source code in this file should be thread-safe.
       Please keep it that way. */
@@ -67,7 +71,9 @@ history_t::history_t() :
     disre_rm3tav(nullptr),
     orire_initf(0),
     norire_Dtav(0),
-    orire_Dtav(nullptr){};
+    orire_Dtav(nullptr)
+{
+}
 
 ekinstate_t::ekinstate_t() :
     bUpToDate(FALSE),
@@ -81,7 +87,63 @@ ekinstate_t::ekinstate_t() :
     mvcos(0)
 {
     clear_mat(ekin_total);
+}
+
+namespace
+{
+/*!
+ * \brief Enum describing the contents ekinstate_t writes to modular checkpoint
+ *
+ * When changing the checkpoint content, add a new element just above Count, and adjust the
+ * checkpoint functionality.
+ */
+enum class CheckpointVersion
+{
+    Base, //!< First version of modular checkpointing
+    Count //!< Number of entries. Add new versions right above this!
 };
+constexpr auto c_currentVersion = CheckpointVersion(int(CheckpointVersion::Count) - 1);
+} // namespace
+
+template<gmx::CheckpointDataOperation operation>
+void ekinstate_t::doCheckpoint(gmx::CheckpointData<operation> checkpointData)
+{
+    gmx::checkpointVersion(&checkpointData, "ekinstate_t version", c_currentVersion);
+
+    checkpointData.scalar("bUpToDate", &bUpToDate);
+    if (!bUpToDate)
+    {
+        return;
+    }
+    auto numOfTensors = ekin_n;
+    checkpointData.scalar("ekin_n", &numOfTensors);
+    if (operation == gmx::CheckpointDataOperation::Read)
+    {
+        // If this isn't matching, we haven't allocated the right amount of data
+        GMX_RELEASE_ASSERT(numOfTensors == ekin_n,
+                           "ekinstate_t checkpoint reading: Tensor size mismatch.");
+    }
+    for (int idx = 0; idx < numOfTensors; ++idx)
+    {
+        checkpointData.tensor(gmx::formatString("ekinh %d", idx), ekinh[idx]);
+        checkpointData.tensor(gmx::formatString("ekinf %d", idx), ekinf[idx]);
+        checkpointData.tensor(gmx::formatString("ekinh_old %d", idx), ekinh_old[idx]);
+    }
+    checkpointData.arrayRef("ekinscalef_nhc", gmx::makeCheckpointArrayRef<operation>(ekinscalef_nhc));
+    checkpointData.arrayRef("ekinscaleh_nhc", gmx::makeCheckpointArrayRef<operation>(ekinscaleh_nhc));
+    checkpointData.arrayRef("vscale_nhc", gmx::makeCheckpointArrayRef<operation>(vscale_nhc));
+    checkpointData.scalar("dekindl", &dekindl);
+    checkpointData.scalar("mvcos", &mvcos);
+
+    if (operation == gmx::CheckpointDataOperation::Read)
+    {
+        hasReadEkinState = true;
+    }
+}
+
+// Explicit template instantiation
+template void ekinstate_t::doCheckpoint(gmx::CheckpointData<gmx::CheckpointDataOperation::Read> checkpointData);
+template void ekinstate_t::doCheckpoint(gmx::CheckpointData<gmx::CheckpointDataOperation::Write> checkpointData);
 
 void init_gtc_state(t_state* state, int ngtc, int nnhpres, int nhchainlength)
 {
@@ -259,7 +321,7 @@ t_state::t_state() :
 void set_box_rel(const t_inputrec* ir, t_state* state)
 {
     /* Make sure the box obeys the restrictions before we fix the ratios */
-    correct_box(nullptr, 0, state->box, nullptr);
+    correct_box(nullptr, 0, state->box);
 
     clear_mat(state->box_rel);
 
@@ -279,12 +341,20 @@ void preserve_box_shape(const t_inputrec* ir, matrix box_rel, matrix box)
     }
 }
 
-void initialize_lambdas(FILE*               fplog,
-                        const t_inputrec&   ir,
-                        bool                isMaster,
-                        int*                fep_state,
-                        gmx::ArrayRef<real> lambda,
-                        double*             lam0)
+void printLambdaStateToLog(FILE* fplog, const gmx::ArrayRef<real> lambda, const bool isInitialOutput)
+{
+    if (fplog != nullptr)
+    {
+        fprintf(fplog, "%s vector of lambda components:[ ", isInitialOutput ? "Initial" : "Current");
+        for (const auto& l : lambda)
+        {
+            fprintf(fplog, "%10.4f ", l);
+        }
+        fprintf(fplog, "]\n%s", isInitialOutput ? "" : "\n");
+    }
+}
+
+void initialize_lambdas(FILE* fplog, const t_inputrec& ir, bool isMaster, int* fep_state, gmx::ArrayRef<real> lambda)
 {
     /* TODO: Clean up initialization of fep_state and lambda in
        t_state.  This function works, but could probably use a logic
@@ -319,10 +389,6 @@ void initialize_lambdas(FILE*               fplog,
         {
             lambda[i] = thisLambda;
         }
-        if (lam0 != nullptr)
-        {
-            lam0[i] = thisLambda;
-        }
     }
     if (ir.bSimTemp)
     {
@@ -337,13 +403,6 @@ void initialize_lambdas(FILE*               fplog,
     }
 
     /* Send to the log the information on the current lambdas */
-    if (fplog != nullptr)
-    {
-        fprintf(fplog, "Initial vector of lambda components:[ ");
-        for (const auto& l : lambda)
-        {
-            fprintf(fplog, "%10.4f ", l);
-        }
-        fprintf(fplog, "]\n");
-    }
+    const bool isInitialOutput = true;
+    printLambdaStateToLog(fplog, lambda, isInitialOutput);
 }
index e3a3ce5c6bd0e1923f899ef41db8d891d65109bf..9b2db3b779d1c17592746a333d58f396dfc13693 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -71,7 +72,10 @@ struct t_inputrec;
 namespace gmx
 {
 struct AwhHistory;
-}
+enum class CheckpointDataOperation;
+template<CheckpointDataOperation operation>
+class CheckpointData;
+} // namespace gmx
 
 //! Convenience alias for until all is moved in the gmx namespace
 template<class T>
@@ -175,6 +179,14 @@ public:
      * before we enter the MD loop should compute these quantities
      * fresh, or not. */
     bool hasReadEkinState;
+
+    /*!
+     * \brief Allows to read and write checkpoint within modular simulator
+     * \tparam operation  Whether we're reading or writing
+     * \param checkpointData  The CheckpointData object
+     */
+    template<gmx::CheckpointDataOperation operation>
+    void doCheckpoint(gmx::CheckpointData<operation> checkpointData);
 };
 
 /*! \brief Free-energy sampling history struct
@@ -340,19 +352,21 @@ static inline gmx::ArrayRef<const gmx::RVec> positionsFromStatePointer(const t_s
     }
 };
 
-/*! \brief Fills fep_state, lambda, and lam0 if needed
+/*! \brief Prints the current lambda state to the log file.
  *
- * If FEP or simulated tempering is in use:
+ * \param[in] fplog  The log file. If fplog == nullptr there will be no output.
+ * \param[in] lambda The array of lambda values.
+ * \param[in] isInitialOutput Whether this output is the initial lambda state or not.
+ */
+void printLambdaStateToLog(FILE* fplog, gmx::ArrayRef<real> lambda, bool isInitialOutput);
+
+
+/*! \brief Fills fep_state and lambda if needed
  *
- *    fills non-null lam0 with the initial lambda values, and
- *    on master rank fills fep_state and lambda.
+ * If FEP or simulated tempering is in use,  fills fep_state
+ * and lambda on master rank.
  *
  * Reports the initial lambda state to the log file. */
-void initialize_lambdas(FILE*               fplog,
-                        const t_inputrec&   ir,
-                        bool                isMaster,
-                        int*                fep_state,
-                        gmx::ArrayRef<real> lambda,
-                        double*             lam0);
+void initialize_lambdas(FILE* fplog, const t_inputrec& ir, bool isMaster, int* fep_state, gmx::ArrayRef<real> lambda);
 
 #endif
index b83d327916e3548d604648d80173497882c4f3e2..89e6d622ac270a39ac566501ac558da92de7934b 100644 (file)
 
 #include "locality.h"
 
+class DeviceContext;
+class DeviceStream;
 class GpuEventSynchronizer;
 struct gmx_wallcycle;
 
 namespace gmx
 {
+class DeviceStreamManager;
 
 class StatePropagatorDataGpu
 {
@@ -86,40 +89,15 @@ public:
      * ops are offloaded. This feature is currently not available in OpenCL and
      * hence these streams are not set in these builds.
      *
-     * \note In CUDA, the update stream is created in the constructor as a temporary
-     *       solution, in place until the stream manager is introduced.
-     *       Note that this makes it impossible to construct this object in CUDA
-     *       builds executing on a host without any CUDA-capable device available.
-     *
-     * \note In CUDA, \p deviceContext is unused, hence always nullptr;
-     *       all stream arguments can also be nullptr in runs where the
-     *       respective streams are not required.
-     *       In OpenCL, \p deviceContext needs to be a valid device context.
-     *       In OpenCL runs StatePropagatorDataGpu is currently only used
-     *       with PME offload, and only on ranks with PME duty. Hence, the
-     *       \p pmeStream argument needs to be a valid OpenCL queue object
-     *       which must have been created in \p deviceContext.
-     *
-     * \todo Make a \p CommandStream visible in the CPU parts of the code so we
-     *       will not have to pass a void*.
-     * \todo Make a \p DeviceContext object visible in CPU parts of the code so we
-     *       will not have to pass a void*.
-     *
-     *  \param[in] pmeStream       Device PME stream, nullptr allowed.
-     *  \param[in] localStream     Device NBNXM local stream, nullptr allowed.
-     *  \param[in] nonLocalStream  Device NBNXM non-local stream, nullptr allowed.
-     *  \param[in] deviceContext   Device context, nullptr allowed.
-     *  \param[in] transferKind    H2D/D2H transfer call behavior (synchronous or not).
-     *  \param[in] paddingSize     Padding size for coordinates buffer.
-     *  \param[in] wcycle          Wall cycle counter data.
+     *  \param[in] deviceStreamManager         Object that owns the DeviceContext and DeviceStreams.
+     *  \param[in] transferKind                H2D/D2H transfer call behavior (synchronous or not).
+     *  \param[in] allocationBlockSizeDivisor  Deterines padding size for coordinates buffer.
+     *  \param[in] wcycle                      Wall cycle counter data.
      */
-    StatePropagatorDataGpu(const void*        pmeStream,
-                           const void*        localStream,
-                           const void*        nonLocalStream,
-                           const void*        deviceContext,
-                           GpuApiCallBehavior transferKind,
-                           int                paddingSize,
-                           gmx_wallcycle*     wcycle);
+    StatePropagatorDataGpu(const DeviceStreamManager& deviceStreamManager,
+                           GpuApiCallBehavior         transferKind,
+                           int                        allocationBlockSizeDivisor,
+                           gmx_wallcycle*             wcycle);
 
     /*! \brief Constructor to use in PME-only rank and in tests.
      *
@@ -134,14 +112,14 @@ public:
      *  \param[in] pmeStream       Device PME stream, nullptr is not allowed.
      *  \param[in] deviceContext   Device context, nullptr allowed for non-OpenCL builds.
      *  \param[in] transferKind    H2D/D2H transfer call behavior (synchronous or not).
-     *  \param[in] paddingSize     Padding size for coordinates buffer.
+     *  \param[in] allocationBlockSizeDivisor Determines padding size for coordinates buffer.
      *  \param[in] wcycle          Wall cycle counter data.
      */
-    StatePropagatorDataGpu(const void*        pmeStream,
-                           const void*        deviceContext,
-                           GpuApiCallBehavior transferKind,
-                           int                paddingSize,
-                           gmx_wallcycle*     wcycle);
+    StatePropagatorDataGpu(const DeviceStream*  pmeStream,
+                           const DeviceContext& deviceContext,
+                           GpuApiCallBehavior   transferKind,
+                           int                  allocationBlockSizeDivisor,
+                           gmx_wallcycle*       wcycle);
 
     //! Move constructor
     StatePropagatorDataGpu(StatePropagatorDataGpu&& other) noexcept;
@@ -186,7 +164,7 @@ public:
      *
      *  \returns GPU positions buffer.
      */
-    DeviceBuffer<float> getCoordinates();
+    DeviceBuffer<RVec> getCoordinates();
 
     /*! \brief Copy positions to the GPU memory.
      *
@@ -247,7 +225,7 @@ public:
      *
      *  \returns GPU velocities buffer.
      */
-    DeviceBuffer<float> getVelocities();
+    DeviceBuffer<RVec> getVelocities();
 
     /*! \brief Copy velocities to the GPU memory.
      *
@@ -282,7 +260,7 @@ public:
      *
      *  \returns GPU force buffer.
      */
-    DeviceBuffer<float> getForces();
+    DeviceBuffer<RVec> getForces();
 
     /*! \brief Copy forces to the GPU memory.
      *
@@ -332,7 +310,7 @@ public:
      *
      *  \returns The device command stream to use in update-constraints.
      */
-    void* getUpdateStream();
+    const DeviceStream* getUpdateStream();
 
     /*! \brief Getter for the number of local atoms.
      *
index 1e2eadaff463886f9b683890f6993c07d3b5dd71..3274d3dd02bf8892e3de11f951669fcdb578e1f7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,7 +46,7 @@
 
 #include "gromacs/mdtypes/state_propagator_data_gpu.h"
 
-#if GMX_GPU == GMX_GPU_NONE
+#if !GMX_GPU
 namespace gmx
 {
 
@@ -54,21 +54,18 @@ class StatePropagatorDataGpu::Impl
 {
 };
 
-StatePropagatorDataGpu::StatePropagatorDataGpu(const void* /* pmeStream       */,
-                                               const void* /* localStream     */,
-                                               const void* /* nonLocalStream  */,
-                                               const void* /* deviceContext   */,
+StatePropagatorDataGpu::StatePropagatorDataGpu(const DeviceStreamManager& /* deviceStreamManager */,
                                                GpuApiCallBehavior /* transferKind    */,
-                                               int /* paddingSize     */,
+                                               int /* allocationBlockSizeDivisor */,
                                                gmx_wallcycle* /*   wcycle */) :
     impl_(nullptr)
 {
 }
 
-StatePropagatorDataGpu::StatePropagatorDataGpu(const void* /* pmeStream       */,
-                                               const void* /* deviceContext   */,
+StatePropagatorDataGpu::StatePropagatorDataGpu(const DeviceStream* /* pmeStream       */,
+                                               const DeviceContext& /* deviceContext   */,
                                                GpuApiCallBehavior /* transferKind    */,
-                                               int /* paddingSize     */,
+                                               int /* allocationBlockSizeDivisor */,
                                                gmx_wallcycle* /*   wcycle */) :
     impl_(nullptr)
 {
@@ -82,25 +79,25 @@ StatePropagatorDataGpu::~StatePropagatorDataGpu() = default;
 
 void StatePropagatorDataGpu::reinit(int /* numAtomsLocal */, int /* numAtomsAll   */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
 
 std::tuple<int, int> StatePropagatorDataGpu::getAtomRangesFromAtomLocality(AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
     return std::make_tuple(0, 0);
 }
 
-DeviceBuffer<float> StatePropagatorDataGpu::getCoordinates()
+DeviceBuffer<RVec> StatePropagatorDataGpu::getCoordinates()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
-    return DeviceBuffer<float>{};
+    return {};
 }
 
 GpuEventSynchronizer* StatePropagatorDataGpu::getCoordinatesReadyOnDeviceEvent(
@@ -108,7 +105,7 @@ GpuEventSynchronizer* StatePropagatorDataGpu::getCoordinatesReadyOnDeviceEvent(
         const SimulationWorkload& /* simulationWork */,
         const StepWorkload& /* stepWork       */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
     return nullptr;
@@ -116,14 +113,14 @@ GpuEventSynchronizer* StatePropagatorDataGpu::getCoordinatesReadyOnDeviceEvent(
 
 void StatePropagatorDataGpu::waitCoordinatesCopiedToDevice(AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
 
 GpuEventSynchronizer* StatePropagatorDataGpu::xUpdatedOnDevice()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
     return nullptr;
@@ -132,14 +129,14 @@ GpuEventSynchronizer* StatePropagatorDataGpu::xUpdatedOnDevice()
 void StatePropagatorDataGpu::copyCoordinatesToGpu(const gmx::ArrayRef<const gmx::RVec> /* h_x */,
                                                   AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
 
 void StatePropagatorDataGpu::waitCoordinatesReadyOnHost(AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
@@ -147,31 +144,31 @@ void StatePropagatorDataGpu::waitCoordinatesReadyOnHost(AtomLocality /* atomLoca
 void StatePropagatorDataGpu::copyCoordinatesFromGpu(gmx::ArrayRef<gmx::RVec> /* h_x          */,
                                                     AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
 
 
-DeviceBuffer<float> StatePropagatorDataGpu::getVelocities()
+DeviceBuffer<RVec> StatePropagatorDataGpu::getVelocities()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
-    return DeviceBuffer<float>{};
+    return {};
 }
 
 void StatePropagatorDataGpu::copyVelocitiesToGpu(const gmx::ArrayRef<const gmx::RVec> /* h_v */,
                                                  AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
 
 GpuEventSynchronizer* StatePropagatorDataGpu::getVelocitiesReadyOnDeviceEvent(AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
     return nullptr;
@@ -180,31 +177,31 @@ GpuEventSynchronizer* StatePropagatorDataGpu::getVelocitiesReadyOnDeviceEvent(At
 void StatePropagatorDataGpu::copyVelocitiesFromGpu(gmx::ArrayRef<gmx::RVec> /* h_v          */,
                                                    AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
 
 void StatePropagatorDataGpu::waitVelocitiesReadyOnHost(AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
 
 
-DeviceBuffer<float> StatePropagatorDataGpu::getForces()
+DeviceBuffer<RVec> StatePropagatorDataGpu::getForces()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
-    return DeviceBuffer<float>{};
+    return {};
 }
 
 void StatePropagatorDataGpu::copyForcesToGpu(const gmx::ArrayRef<const gmx::RVec> /* h_f          */,
                                              AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
@@ -212,7 +209,7 @@ void StatePropagatorDataGpu::copyForcesToGpu(const gmx::ArrayRef<const gmx::RVec
 GpuEventSynchronizer* StatePropagatorDataGpu::getForcesReadyOnDeviceEvent(AtomLocality /* atomLocality */,
                                                                           bool /* useGpuFBufferOps */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
     return nullptr;
@@ -220,7 +217,7 @@ GpuEventSynchronizer* StatePropagatorDataGpu::getForcesReadyOnDeviceEvent(AtomLo
 
 GpuEventSynchronizer* StatePropagatorDataGpu::fReducedOnDevice()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
     return nullptr;
@@ -229,22 +226,22 @@ GpuEventSynchronizer* StatePropagatorDataGpu::fReducedOnDevice()
 void StatePropagatorDataGpu::copyForcesFromGpu(gmx::ArrayRef<gmx::RVec> /* h_f          */,
                                                AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
 
 void StatePropagatorDataGpu::waitForcesReadyOnHost(AtomLocality /* atomLocality */)
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
 }
 
 
-void* StatePropagatorDataGpu::getUpdateStream()
+const DeviceStream* StatePropagatorDataGpu::getUpdateStream()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
     return nullptr;
@@ -252,7 +249,7 @@ void* StatePropagatorDataGpu::getUpdateStream()
 
 int StatePropagatorDataGpu::numAtomsLocal()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
     return 0;
@@ -260,7 +257,7 @@ int StatePropagatorDataGpu::numAtomsLocal()
 
 int StatePropagatorDataGpu::numAtomsAll()
 {
-    GMX_ASSERT(false,
+    GMX_ASSERT(!impl_,
                "A CPU stub method from GPU state propagator data was called instead of one from "
                "GPU implementation.");
     return 0;
@@ -268,4 +265,4 @@ int StatePropagatorDataGpu::numAtomsAll()
 
 } // namespace gmx
 
-#endif // GMX_GPU == GMX_GPU_NONE
+#endif // !GMX_GPU
index 9ac9711d298862052e1cc3c138f93ba8ea3a292c..17737b547714846a81ad941545d64d3233bdbaad 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "config.h"
 
 #include "gromacs/gpu_utils/devicebuffer.h"
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 #    include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
-#elif GMX_GPU == GMX_GPU_OPENCL
+#elif GMX_GPU_OPENCL
 #    include "gromacs/gpu_utils/gpueventsynchronizer_ocl.h"
+#elif GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/gpueventsynchronizer_sycl.h"
 #endif
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdtypes/state_propagator_data_gpu.h"
@@ -85,40 +87,15 @@ public:
      * ops are offloaded. This feature is currently not available in OpenCL and
      * hence these streams are not set in these builds.
      *
-     * \note In CUDA, the update stream is created in the constructor as a temporary
-     *       solution, in place until the stream manager is introduced.
-     *       Note that this makes it impossible to construct this object in CUDA
-     *       builds executing on a host without any CUDA-capable device available.
-     *
-     * \note In CUDA, \p deviceContext is unused, hence always nullptr;
-     *       all stream arguments can also be nullptr in runs where the
-     *       respective streams are not required.
-     *       In OpenCL, \p deviceContext needs to be a valid device context.
-     *       In OpenCL runs StatePropagatorDataGpu is currently only used
-     *       with PME offload, and only on ranks with PME duty. Hence, the
-     *       \p pmeStream argument needs to be a valid OpenCL queue object
-     *       which must have been created in \p deviceContext.
-     *
-     * \todo Make a \p CommandStream visible in the CPU parts of the code so we
-     *       will not have to pass a void*.
-     * \todo Make a \p DeviceContext object visible in CPU parts of the code so we
-     *       will not have to pass a void*.
-     *
-     *  \param[in] pmeStream       Device PME stream, nullptr allowed.
-     *  \param[in] localStream     Device NBNXM local stream, nullptr allowed.
-     *  \param[in] nonLocalStream  Device NBNXM non-local stream, nullptr allowed.
-     *  \param[in] deviceContext   Device context, nullptr allowed.
-     *  \param[in] transferKind    H2D/D2H transfer call behavior (synchronous or not).
-     *  \param[in] paddingSize     Padding size for coordinates buffer.
-     *  \param[in] wcycle          Wall cycle counter data.
+     *  \param[in] deviceStreamManager         Object that owns the DeviceContext and DeviceStreams.
+     *  \param[in] transferKind                H2D/D2H transfer call behavior (synchronous or not).
+     *  \param[in] allocationBlockSizeDivisor  Determines the padding size for coordinates buffer.
+     *  \param[in] wcycle                      Wall cycle counter data.
      */
-    Impl(const void*        pmeStream,
-         const void*        localStream,
-         const void*        nonLocalStream,
-         const void*        deviceContext,
-         GpuApiCallBehavior transferKind,
-         int                paddingSize,
-         gmx_wallcycle*     wcycle);
+    Impl(const DeviceStreamManager& deviceStreamManager,
+         GpuApiCallBehavior         transferKind,
+         int                        allocationBlockSizeDivisor,
+         gmx_wallcycle*             wcycle);
 
     /*! \brief Constructor to use in PME-only rank and in tests.
      *
@@ -133,14 +110,14 @@ public:
      *  \param[in] pmeStream       Device PME stream, nullptr is not allowed.
      *  \param[in] deviceContext   Device context, nullptr allowed for non-OpenCL builds.
      *  \param[in] transferKind    H2D/D2H transfer call behavior (synchronous or not).
-     *  \param[in] paddingSize     Padding size for coordinates buffer.
+     *  \param[in] allocationBlockSizeDivisor  Determines the padding size for coordinates buffer.
      *  \param[in] wcycle          Wall cycle counter data.
      */
-    Impl(const void*        pmeStream,
-         const void*        deviceContext,
-         GpuApiCallBehavior transferKind,
-         int                paddingSize,
-         gmx_wallcycle*     wcycle);
+    Impl(const DeviceStream*  pmeStream,
+         const DeviceContext& deviceContext,
+         GpuApiCallBehavior   transferKind,
+         int                  allocationBlockSizeDivisor,
+         gmx_wallcycle*       wcycle);
 
     ~Impl();
 
@@ -181,7 +158,7 @@ public:
      *
      *  \returns GPU positions buffer.
      */
-    DeviceBuffer<float> getCoordinates();
+    DeviceBuffer<RVec> getCoordinates();
 
     /*! \brief Copy positions to the GPU memory.
      *
@@ -242,7 +219,7 @@ public:
      *
      *  \returns GPU velocities buffer.
      */
-    DeviceBuffer<float> getVelocities();
+    DeviceBuffer<RVec> getVelocities();
 
     /*! \brief Copy velocities to the GPU memory.
      *
@@ -277,7 +254,7 @@ public:
      *
      *  \returns GPU force buffer.
      */
-    DeviceBuffer<float> getForces();
+    DeviceBuffer<RVec> getForces();
 
     /*! \brief Copy forces to the GPU memory.
      *
@@ -327,7 +304,7 @@ public:
      *
      *  \returns The device command stream to use in update-constraints.
      */
-    void* getUpdateStream();
+    const DeviceStream* getUpdateStream();
 
     /*! \brief Getter for the number of local atoms.
      *
@@ -343,20 +320,20 @@ public:
 
 private:
     //! GPU PME stream.
-    CommandStream pmeStream_ = nullptr;
+    const DeviceStream* pmeStream_;
     //! GPU NBNXM local stream.
-    CommandStream localStream_ = nullptr;
-    //! GPU NBNXM non-local stream
-    CommandStream nonLocalStream_ = nullptr;
+    const DeviceStream* localStream_;
+    //! GPU NBNXM non-local stream.
+    const DeviceStream* nonLocalStream_;
     //! GPU Update-constreaints stream.
-    CommandStream updateStream_ = nullptr;
+    const DeviceStream* updateStream_;
 
     // Streams to use for coordinates H2D and D2H copies (one event for each atom locality)
-    EnumerationArray<AtomLocality, CommandStream> xCopyStreams_ = { { nullptr } };
+    EnumerationArray<AtomLocality, const DeviceStream*> xCopyStreams_ = { { nullptr } };
     // Streams to use for velocities H2D and D2H copies (one event for each atom locality)
-    EnumerationArray<AtomLocality, CommandStream> vCopyStreams_ = { { nullptr } };
+    EnumerationArray<AtomLocality, const DeviceStream*> vCopyStreams_ = { { nullptr } };
     // Streams to use for forces H2D and D2H copies (one event for each atom locality)
-    EnumerationArray<AtomLocality, CommandStream> fCopyStreams_ = { { nullptr } };
+    EnumerationArray<AtomLocality, const DeviceStream*> fCopyStreams_ = { { nullptr } };
 
     /*! \brief An array of events that indicate H2D copy is complete (one event for each atom locality)
      *
@@ -380,14 +357,12 @@ private:
     //! An array of events that indicate D2H copy of forces is complete (one event for each atom locality)
     EnumerationArray<AtomLocality, GpuEventSynchronizer> fReadyOnHost_;
 
-    /*! \brief GPU context (for OpenCL builds)
-     * \todo Make a Context class usable in CPU code
-     */
-    DeviceContext deviceContext_ = nullptr;
+    //! GPU context (for OpenCL builds)
+    const DeviceContext& deviceContext_;
     //! Default GPU calls behavior
     GpuApiCallBehavior transferKind_ = GpuApiCallBehavior::Async;
-    //! Padding size for the coordinates buffer
-    int paddingSize_ = 0;
+    //! Required minimum divisor of the allocation size of the coordinates buffer
+    int allocationBlockSizeDivisor_ = 0;
 
     //! Number of local atoms
     int numAtomsLocal_ = -1;
@@ -395,21 +370,21 @@ private:
     int numAtomsAll_ = -1;
 
     //! Device positions buffer
-    DeviceBuffer<float> d_x_;
+    DeviceBuffer<RVec> d_x_;
     //! Number of particles saved in the positions buffer
     int d_xSize_ = -1;
     //! Allocation size for the positions buffer
     int d_xCapacity_ = -1;
 
     //! Device velocities buffer
-    DeviceBuffer<float> d_v_;
+    DeviceBuffer<RVec> d_v_;
     //! Number of particles saved in the velocities buffer
     int d_vSize_ = -1;
     //! Allocation size for the velocities buffer
     int d_vCapacity_ = -1;
 
     //! Device force buffer
-    DeviceBuffer<float> d_f_;
+    DeviceBuffer<RVec> d_f_;
     //! Number of particles saved in the force buffer
     int d_fSize_ = -1;
     //! Allocation size for the force buffer
@@ -426,13 +401,13 @@ private:
      *  \param[in]  h_data         Host-side buffer.
      *  \param[in]  dataSize       Device-side data allocation size.
      *  \param[in]  atomLocality   If all, local or non-local ranges should be copied.
-     *  \param[in]  commandStream  GPU stream to execute copy in.
+     *  \param[in]  deviceStream   GPU stream to execute copy in.
      */
-    void copyToDevice(DeviceBuffer<float>            d_data,
+    void copyToDevice(DeviceBuffer<RVec>             d_data,
                       gmx::ArrayRef<const gmx::RVec> h_data,
                       int                            dataSize,
                       AtomLocality                   atomLocality,
-                      CommandStream                  commandStream);
+                      const DeviceStream&            deviceStream);
 
     /*! \brief Performs the copy of data from device to host buffer.
      *
@@ -440,13 +415,13 @@ private:
      *  \param[in]  d_data         Device-side buffer.
      *  \param[in]  dataSize       Device-side data allocation size.
      *  \param[in]  atomLocality   If all, local or non-local ranges should be copied.
-     *  \param[in]  commandStream  GPU stream to execute copy in.
+     *  \param[in]  deviceStream   GPU stream to execute copy in.
      */
     void copyFromDevice(gmx::ArrayRef<gmx::RVec> h_data,
-                        DeviceBuffer<float>      d_data,
+                        DeviceBuffer<RVec>       d_data,
                         int                      dataSize,
                         AtomLocality             atomLocality,
-                        CommandStream            commandStream);
+                        const DeviceStream&      deviceStream);
 };
 
 } // namespace gmx
index 29821a43133ddaacd8078a7c2ae2596c95aa2037..e290b73f4a64f73eae4e38c1c6059e179dce6601 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "config.h"
 
-#if GMX_GPU != GMX_GPU_NONE
+#if GMX_GPU
 
-#    if GMX_GPU == GMX_GPU_CUDA
-#        include "gromacs/gpu_utils/cudautils.cuh"
-#    endif
+#    include "gromacs/gpu_utils/device_stream_manager.h"
 #    include "gromacs/gpu_utils/devicebuffer.h"
-#    if GMX_GPU == GMX_GPU_OPENCL
-#        include "gromacs/gpu_utils/oclutils.h"
-#    endif
 #    include "gromacs/math/vectypes.h"
 #    include "gromacs/mdtypes/state_propagator_data_gpu.h"
 #    include "gromacs/timing/wallcycle.h"
 namespace gmx
 {
 
-StatePropagatorDataGpu::Impl::Impl(const void*        pmeStream,
-                                   const void*        localStream,
-                                   const void*        nonLocalStream,
-                                   const void*        deviceContext,
-                                   GpuApiCallBehavior transferKind,
-                                   int                paddingSize,
-                                   gmx_wallcycle*     wcycle) :
+StatePropagatorDataGpu::Impl::Impl(const DeviceStreamManager& deviceStreamManager,
+                                   GpuApiCallBehavior         transferKind,
+                                   int                        allocationBlockSizeDivisor,
+                                   gmx_wallcycle*             wcycle) :
+    deviceContext_(deviceStreamManager.context()),
     transferKind_(transferKind),
-    paddingSize_(paddingSize),
+    allocationBlockSizeDivisor_(allocationBlockSizeDivisor),
     wcycle_(wcycle)
 {
-    static_assert(GMX_GPU != GMX_GPU_NONE,
-                  "This object should only be constructed on the GPU code-paths.");
+    static_assert(
+            GMX_GPU,
+            "GPU state propagator data object should only be constructed on the GPU code-paths.");
 
-    // TODO: Refactor when the StreamManager is introduced.
-    if (GMX_GPU == GMX_GPU_OPENCL)
-    {
-        GMX_ASSERT(deviceContext != nullptr, "GPU context should be set in OpenCL builds.");
-        GMX_ASSERT(pmeStream != nullptr, "GPU PME stream should be set in OpenCL builds.");
-
-        // The update stream is set to the PME stream in OpenCL, since PME stream is the only stream created in the PME context.
-        pmeStream_     = *static_cast<const CommandStream*>(pmeStream);
-        updateStream_  = *static_cast<const CommandStream*>(pmeStream);
-        deviceContext_ = *static_cast<const DeviceContext*>(deviceContext);
-        GMX_UNUSED_VALUE(localStream);
-        GMX_UNUSED_VALUE(nonLocalStream);
-    }
-
-    if (GMX_GPU == GMX_GPU_CUDA)
-    {
-        if (pmeStream != nullptr)
-        {
-            pmeStream_ = *static_cast<const CommandStream*>(pmeStream);
-        }
-        if (localStream != nullptr)
-        {
-            localStream_ = *static_cast<const CommandStream*>(localStream);
-        }
-        if (nonLocalStream != nullptr)
-        {
-            nonLocalStream_ = *static_cast<const CommandStream*>(nonLocalStream);
-        }
-
-        // TODO: The update stream should be created only when it is needed.
-#    if (GMX_GPU == GMX_GPU_CUDA)
-        cudaError_t stat;
-        stat = cudaStreamCreate(&updateStream_);
-        CU_RET_ERR(stat, "CUDA stream creation failed in StatePropagatorDataGpu");
-#    endif
-        GMX_UNUSED_VALUE(deviceContext);
-    }
+    // We need to keep local copies for re-initialization.
+    pmeStream_      = &deviceStreamManager.stream(DeviceStreamType::Pme);
+    localStream_    = &deviceStreamManager.stream(DeviceStreamType::NonBondedLocal);
+    nonLocalStream_ = &deviceStreamManager.stream(DeviceStreamType::NonBondedNonLocal);
+    // PME stream is used in OpenCL for H2D coordinate transfer
+    updateStream_ = &deviceStreamManager.stream(
+            GMX_GPU_OPENCL ? DeviceStreamType::Pme : DeviceStreamType::UpdateAndConstraints);
 
     // Map the atom locality to the stream that will be used for coordinates,
     // velocities and forces transfers. Same streams are used for H2D and D2H copies.
@@ -132,28 +96,23 @@ StatePropagatorDataGpu::Impl::Impl(const void*        pmeStream,
     fCopyStreams_[AtomLocality::All]      = updateStream_;
 }
 
-StatePropagatorDataGpu::Impl::Impl(const void*        pmeStream,
-                                   const void*        deviceContext,
-                                   GpuApiCallBehavior transferKind,
-                                   int                paddingSize,
-                                   gmx_wallcycle*     wcycle) :
+StatePropagatorDataGpu::Impl::Impl(const DeviceStream*  pmeStream,
+                                   const DeviceContext& deviceContext,
+                                   GpuApiCallBehavior   transferKind,
+                                   int                  allocationBlockSizeDivisor,
+                                   gmx_wallcycle*       wcycle) :
+    deviceContext_(deviceContext),
     transferKind_(transferKind),
-    paddingSize_(paddingSize),
+    allocationBlockSizeDivisor_(allocationBlockSizeDivisor),
     wcycle_(wcycle)
 {
-    static_assert(GMX_GPU != GMX_GPU_NONE,
-                  "This object should only be constructed on the GPU code-paths.");
-
-    if (GMX_GPU == GMX_GPU_OPENCL)
-    {
-        GMX_ASSERT(deviceContext != nullptr, "GPU context should be set in OpenCL builds.");
-        deviceContext_ = *static_cast<const DeviceContext*>(deviceContext);
-    }
+    static_assert(
+            GMX_GPU,
+            "GPU state propagator data object should only be constructed on the GPU code-paths.");
 
-    GMX_ASSERT(pmeStream != nullptr, "GPU PME stream should be set.");
-    pmeStream_ = *static_cast<const CommandStream*>(pmeStream);
-
-    localStream_    = nullptr;
+    GMX_ASSERT(pmeStream->isValid(), "GPU PME stream should be valid.");
+    pmeStream_      = pmeStream;
+    localStream_    = pmeStream; // For clearing the force buffer
     nonLocalStream_ = nullptr;
     updateStream_   = nullptr;
 
@@ -184,33 +143,35 @@ void StatePropagatorDataGpu::Impl::reinit(int numAtomsLocal, int numAtomsAll)
     numAtomsAll_   = numAtomsAll;
 
     int numAtomsPadded;
-    if (paddingSize_ > 0)
+    if (allocationBlockSizeDivisor_ > 0)
     {
-        numAtomsPadded = ((numAtomsAll_ + paddingSize_ - 1) / paddingSize_) * paddingSize_;
+        numAtomsPadded = ((numAtomsAll_ + allocationBlockSizeDivisor_ - 1) / allocationBlockSizeDivisor_)
+                         * allocationBlockSizeDivisor_;
     }
     else
     {
         numAtomsPadded = numAtomsAll_;
     }
 
-    reallocateDeviceBuffer(&d_x_, DIM * numAtomsPadded, &d_xSize_, &d_xCapacity_, deviceContext_);
+    reallocateDeviceBuffer(&d_x_, numAtomsPadded, &d_xSize_, &d_xCapacity_, deviceContext_);
 
     const size_t paddingAllocationSize = numAtomsPadded - numAtomsAll_;
     if (paddingAllocationSize > 0)
     {
         // The PME stream is used here because the padding region of d_x_ is only in the PME task.
-        clearDeviceBufferAsync(&d_x_, DIM * numAtomsAll_, DIM * paddingAllocationSize, pmeStream_);
+        clearDeviceBufferAsync(&d_x_, numAtomsAll_, paddingAllocationSize, *pmeStream_);
     }
 
-    reallocateDeviceBuffer(&d_v_, DIM * numAtomsAll_, &d_vSize_, &d_vCapacity_, deviceContext_);
+    reallocateDeviceBuffer(&d_v_, numAtomsAll_, &d_vSize_, &d_vCapacity_, deviceContext_);
     const int d_fOldCapacity = d_fCapacity_;
-    reallocateDeviceBuffer(&d_f_, DIM * numAtomsAll_, &d_fSize_, &d_fCapacity_, deviceContext_);
+    reallocateDeviceBuffer(&d_f_, numAtomsAll_, &d_fSize_, &d_fCapacity_, deviceContext_);
+
     // Clearing of the forces can be done in local stream since the nonlocal stream cannot reach
     // the force accumulation stage before syncing with the local stream. Only done in CUDA,
     // since the force buffer ops are not implemented in OpenCL.
-    if (GMX_GPU == GMX_GPU_CUDA && d_fCapacity_ != d_fOldCapacity)
+    if (GMX_GPU_CUDA && d_fCapacity_ != d_fOldCapacity)
     {
-        clearDeviceBufferAsync(&d_f_, 0, d_fCapacity_, localStream_);
+        clearDeviceBufferAsync(&d_f_, 0, d_fCapacity_, *localStream_);
     }
 
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
@@ -249,34 +210,34 @@ std::tuple<int, int> StatePropagatorDataGpu::Impl::getAtomRangesFromAtomLocality
     return std::make_tuple(atomsStartAt, numAtomsToCopy);
 }
 
-void StatePropagatorDataGpu::Impl::copyToDevice(DeviceBuffer<float>                  d_data,
+void StatePropagatorDataGpu::Impl::copyToDevice(DeviceBuffer<RVec>                   d_data,
                                                 const gmx::ArrayRef<const gmx::RVec> h_data,
                                                 int                                  dataSize,
                                                 AtomLocality                         atomLocality,
-                                                CommandStream                        commandStream)
+                                                const DeviceStream&                  deviceStream)
 {
     GMX_UNUSED_VALUE(dataSize);
 
+    GMX_ASSERT(atomLocality < AtomLocality::Count, "Wrong atom locality.");
+
     GMX_ASSERT(dataSize >= 0, "Trying to copy to device buffer before it was allocated.");
 
+    GMX_ASSERT(deviceStream.isValid(), "No stream is valid for copying with given atom locality.");
     wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
     wallcycle_sub_start(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
 
     int atomsStartAt, numAtomsToCopy;
     std::tie(atomsStartAt, numAtomsToCopy) = getAtomRangesFromAtomLocality(atomLocality);
 
-    int elementsStartAt   = atomsStartAt * DIM;
-    int numElementsToCopy = numAtomsToCopy * DIM;
-
     if (numAtomsToCopy != 0)
     {
-        GMX_ASSERT(elementsStartAt + numElementsToCopy <= dataSize,
+        GMX_ASSERT(atomsStartAt + numAtomsToCopy <= dataSize,
                    "The device allocation is smaller than requested copy range.");
         GMX_ASSERT(atomsStartAt + numAtomsToCopy <= h_data.ssize(),
                    "The host buffer is smaller than the requested copy range.");
 
-        copyToDeviceBuffer(&d_data, reinterpret_cast<const float*>(&h_data.data()[atomsStartAt]),
-                           elementsStartAt, numElementsToCopy, commandStream, transferKind_, nullptr);
+        copyToDeviceBuffer(&d_data, reinterpret_cast<const RVec*>(&h_data.data()[atomsStartAt]),
+                           atomsStartAt, numAtomsToCopy, deviceStream, transferKind_, nullptr);
     }
 
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
@@ -284,40 +245,40 @@ void StatePropagatorDataGpu::Impl::copyToDevice(DeviceBuffer<float>
 }
 
 void StatePropagatorDataGpu::Impl::copyFromDevice(gmx::ArrayRef<gmx::RVec> h_data,
-                                                  DeviceBuffer<float>      d_data,
+                                                  DeviceBuffer<RVec>       d_data,
                                                   int                      dataSize,
                                                   AtomLocality             atomLocality,
-                                                  CommandStream            commandStream)
+                                                  const DeviceStream&      deviceStream)
 {
     GMX_UNUSED_VALUE(dataSize);
 
+    GMX_ASSERT(atomLocality < AtomLocality::Count, "Wrong atom locality.");
+
     GMX_ASSERT(dataSize >= 0, "Trying to copy from device buffer before it was allocated.");
 
+    GMX_ASSERT(deviceStream.isValid(), "No stream is valid for copying with given atom locality.");
     wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
     wallcycle_sub_start(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
 
     int atomsStartAt, numAtomsToCopy;
     std::tie(atomsStartAt, numAtomsToCopy) = getAtomRangesFromAtomLocality(atomLocality);
 
-    int elementsStartAt   = atomsStartAt * DIM;
-    int numElementsToCopy = numAtomsToCopy * DIM;
-
     if (numAtomsToCopy != 0)
     {
-        GMX_ASSERT(elementsStartAt + numElementsToCopy <= dataSize,
+        GMX_ASSERT(atomsStartAt + numAtomsToCopy <= dataSize,
                    "The device allocation is smaller than requested copy range.");
         GMX_ASSERT(atomsStartAt + numAtomsToCopy <= h_data.ssize(),
                    "The host buffer is smaller than the requested copy range.");
 
-        copyFromDeviceBuffer(reinterpret_cast<float*>(&h_data.data()[atomsStartAt]), &d_data,
-                             elementsStartAt, numElementsToCopy, commandStream, transferKind_, nullptr);
+        copyFromDeviceBuffer(reinterpret_cast<RVec*>(&h_data.data()[atomsStartAt]), &d_data,
+                             atomsStartAt, numAtomsToCopy, deviceStream, transferKind_, nullptr);
     }
 
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
     wallcycle_stop(wcycle_, ewcLAUNCH_GPU);
 }
 
-DeviceBuffer<float> StatePropagatorDataGpu::Impl::getCoordinates()
+DeviceBuffer<RVec> StatePropagatorDataGpu::Impl::getCoordinates()
 {
     return d_x_;
 }
@@ -326,22 +287,22 @@ void StatePropagatorDataGpu::Impl::copyCoordinatesToGpu(const gmx::ArrayRef<cons
                                                         AtomLocality atomLocality)
 {
     GMX_ASSERT(atomLocality < AtomLocality::Count, "Wrong atom locality.");
-    CommandStream commandStream = xCopyStreams_[atomLocality];
-    GMX_ASSERT(commandStream != nullptr,
+    const DeviceStream* deviceStream = xCopyStreams_[atomLocality];
+    GMX_ASSERT(deviceStream != nullptr,
                "No stream is valid for copying positions with given atom locality.");
 
     wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
     wallcycle_sub_start(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
 
-    copyToDevice(d_x_, h_x, d_xSize_, atomLocality, commandStream);
+    copyToDevice(d_x_, h_x, d_xSize_, atomLocality, *deviceStream);
 
     // markEvent is skipped in OpenCL as:
     //   - it's not needed, copy is done in the same stream as the only consumer task (PME)
     //   - we don't consume the events in OpenCL which is not allowed by GpuEventSynchronizer (would leak memory).
     // TODO: remove this by adding an event-mark free flavor of this function
-    if (GMX_GPU == GMX_GPU_CUDA)
+    if (GMX_GPU_CUDA)
     {
-        xReadyOnDevice_[atomLocality].markEvent(commandStream);
+        xReadyOnDevice_[atomLocality].markEvent(*deviceStream);
     }
 
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
@@ -361,7 +322,7 @@ StatePropagatorDataGpu::Impl::getCoordinatesReadyOnDeviceEvent(AtomLocality atom
     // TODO: This should be reconsidered to support the halo exchange.
     //
     // In OpenCL no events are used as coordinate sync is not necessary
-    if (GMX_GPU == GMX_GPU_OPENCL)
+    if (GMX_GPU_OPENCL)
     {
         return nullptr;
     }
@@ -391,16 +352,16 @@ GpuEventSynchronizer* StatePropagatorDataGpu::Impl::xUpdatedOnDevice()
 void StatePropagatorDataGpu::Impl::copyCoordinatesFromGpu(gmx::ArrayRef<gmx::RVec> h_x, AtomLocality atomLocality)
 {
     GMX_ASSERT(atomLocality < AtomLocality::Count, "Wrong atom locality.");
-    CommandStream commandStream = xCopyStreams_[atomLocality];
-    GMX_ASSERT(commandStream != nullptr,
+    const DeviceStream* deviceStream = xCopyStreams_[atomLocality];
+    GMX_ASSERT(deviceStream != nullptr,
                "No stream is valid for copying positions with given atom locality.");
 
     wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
     wallcycle_sub_start(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
 
-    copyFromDevice(h_x, d_x_, d_xSize_, atomLocality, commandStream);
+    copyFromDevice(h_x, d_x_, d_xSize_, atomLocality, *deviceStream);
     // Note: unlike copyCoordinatesToGpu this is not used in OpenCL, and the conditional is not needed.
-    xReadyOnHost_[atomLocality].markEvent(commandStream);
+    xReadyOnHost_[atomLocality].markEvent(*deviceStream);
 
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
     wallcycle_stop(wcycle_, ewcLAUNCH_GPU);
@@ -414,7 +375,7 @@ void StatePropagatorDataGpu::Impl::waitCoordinatesReadyOnHost(AtomLocality atomL
 }
 
 
-DeviceBuffer<float> StatePropagatorDataGpu::Impl::getVelocities()
+DeviceBuffer<RVec> StatePropagatorDataGpu::Impl::getVelocities()
 {
     return d_v_;
 }
@@ -423,15 +384,15 @@ void StatePropagatorDataGpu::Impl::copyVelocitiesToGpu(const gmx::ArrayRef<const
                                                        AtomLocality atomLocality)
 {
     GMX_ASSERT(atomLocality < AtomLocality::Count, "Wrong atom locality.");
-    CommandStream commandStream = vCopyStreams_[atomLocality];
-    GMX_ASSERT(commandStream != nullptr,
+    const DeviceStream* deviceStream = vCopyStreams_[atomLocality];
+    GMX_ASSERT(deviceStream != nullptr,
                "No stream is valid for copying velocities with given atom locality.");
 
     wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
     wallcycle_sub_start(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
 
-    copyToDevice(d_v_, h_v, d_vSize_, atomLocality, commandStream);
-    vReadyOnDevice_[atomLocality].markEvent(commandStream);
+    copyToDevice(d_v_, h_v, d_vSize_, atomLocality, *deviceStream);
+    vReadyOnDevice_[atomLocality].markEvent(*deviceStream);
 
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
     wallcycle_stop(wcycle_, ewcLAUNCH_GPU);
@@ -446,15 +407,15 @@ GpuEventSynchronizer* StatePropagatorDataGpu::Impl::getVelocitiesReadyOnDeviceEv
 void StatePropagatorDataGpu::Impl::copyVelocitiesFromGpu(gmx::ArrayRef<gmx::RVec> h_v, AtomLocality atomLocality)
 {
     GMX_ASSERT(atomLocality < AtomLocality::Count, "Wrong atom locality.");
-    CommandStream commandStream = vCopyStreams_[atomLocality];
-    GMX_ASSERT(commandStream != nullptr,
+    const DeviceStream* deviceStream = vCopyStreams_[atomLocality];
+    GMX_ASSERT(deviceStream != nullptr,
                "No stream is valid for copying velocities with given atom locality.");
 
     wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
     wallcycle_sub_start(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
 
-    copyFromDevice(h_v, d_v_, d_vSize_, atomLocality, commandStream);
-    vReadyOnHost_[atomLocality].markEvent(commandStream);
+    copyFromDevice(h_v, d_v_, d_vSize_, atomLocality, *deviceStream);
+    vReadyOnHost_[atomLocality].markEvent(*deviceStream);
 
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
     wallcycle_stop(wcycle_, ewcLAUNCH_GPU);
@@ -468,7 +429,7 @@ void StatePropagatorDataGpu::Impl::waitVelocitiesReadyOnHost(AtomLocality atomLo
 }
 
 
-DeviceBuffer<float> StatePropagatorDataGpu::Impl::getForces()
+DeviceBuffer<RVec> StatePropagatorDataGpu::Impl::getForces()
 {
     return d_f_;
 }
@@ -477,15 +438,15 @@ void StatePropagatorDataGpu::Impl::copyForcesToGpu(const gmx::ArrayRef<const gmx
                                                    AtomLocality atomLocality)
 {
     GMX_ASSERT(atomLocality < AtomLocality::Count, "Wrong atom locality.");
-    CommandStream commandStream = fCopyStreams_[atomLocality];
-    GMX_ASSERT(commandStream != nullptr,
+    const DeviceStream* deviceStream = fCopyStreams_[atomLocality];
+    GMX_ASSERT(deviceStream != nullptr,
                "No stream is valid for copying forces with given atom locality.");
 
     wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
     wallcycle_sub_start(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
 
-    copyToDevice(d_f_, h_f, d_fSize_, atomLocality, commandStream);
-    fReadyOnDevice_[atomLocality].markEvent(commandStream);
+    copyToDevice(d_f_, h_f, d_fSize_, atomLocality, *deviceStream);
+    fReadyOnDevice_[atomLocality].markEvent(*deviceStream);
 
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
     wallcycle_stop(wcycle_, ewcLAUNCH_GPU);
@@ -512,15 +473,15 @@ GpuEventSynchronizer* StatePropagatorDataGpu::Impl::fReducedOnDevice()
 void StatePropagatorDataGpu::Impl::copyForcesFromGpu(gmx::ArrayRef<gmx::RVec> h_f, AtomLocality atomLocality)
 {
     GMX_ASSERT(atomLocality < AtomLocality::Count, "Wrong atom locality.");
-    CommandStream commandStream = fCopyStreams_[atomLocality];
-    GMX_ASSERT(commandStream != nullptr,
+    const DeviceStream* deviceStream = fCopyStreams_[atomLocality];
+    GMX_ASSERT(deviceStream != nullptr,
                "No stream is valid for copying forces with given atom locality.");
 
     wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
     wallcycle_sub_start(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
 
-    copyFromDevice(h_f, d_f_, d_fSize_, atomLocality, commandStream);
-    fReadyOnHost_[atomLocality].markEvent(commandStream);
+    copyFromDevice(h_f, d_f_, d_fSize_, atomLocality, *deviceStream);
+    fReadyOnHost_[atomLocality].markEvent(*deviceStream);
 
     wallcycle_sub_stop(wcycle_, ewcsLAUNCH_STATE_PROPAGATOR_DATA);
     wallcycle_stop(wcycle_, ewcLAUNCH_GPU);
@@ -533,9 +494,9 @@ void StatePropagatorDataGpu::Impl::waitForcesReadyOnHost(AtomLocality atomLocali
     wallcycle_stop(wcycle_, ewcWAIT_GPU_STATE_PROPAGATOR_DATA);
 }
 
-void* StatePropagatorDataGpu::Impl::getUpdateStream()
+const DeviceStream* StatePropagatorDataGpu::Impl::getUpdateStream()
 {
-    return &updateStream_;
+    return updateStream_;
 }
 
 int StatePropagatorDataGpu::Impl::numAtomsLocal()
@@ -549,23 +510,20 @@ int StatePropagatorDataGpu::Impl::numAtomsAll()
 }
 
 
-StatePropagatorDataGpu::StatePropagatorDataGpu(const void*        pmeStream,
-                                               const void*        localStream,
-                                               const void*        nonLocalStream,
-                                               const void*        deviceContext,
-                                               GpuApiCallBehavior transferKind,
-                                               int                paddingSize,
-                                               gmx_wallcycle*     wcycle) :
-    impl_(new Impl(pmeStream, localStream, nonLocalStream, deviceContext, transferKind, paddingSize, wcycle))
+StatePropagatorDataGpu::StatePropagatorDataGpu(const DeviceStreamManager& deviceStreamManager,
+                                               GpuApiCallBehavior         transferKind,
+                                               int            allocationBlockSizeDivisor,
+                                               gmx_wallcycle* wcycle) :
+    impl_(new Impl(deviceStreamManager, transferKind, allocationBlockSizeDivisor, wcycle))
 {
 }
 
-StatePropagatorDataGpu::StatePropagatorDataGpu(const void*        pmeStream,
-                                               const void*        deviceContext,
-                                               GpuApiCallBehavior transferKind,
-                                               int                paddingSize,
-                                               gmx_wallcycle*     wcycle) :
-    impl_(new Impl(pmeStream, deviceContext, transferKind, paddingSize, wcycle))
+StatePropagatorDataGpu::StatePropagatorDataGpu(const DeviceStream*  pmeStream,
+                                               const DeviceContext& deviceContext,
+                                               GpuApiCallBehavior   transferKind,
+                                               int                  allocationBlockSizeDivisor,
+                                               gmx_wallcycle*       wcycle) :
+    impl_(new Impl(pmeStream, deviceContext, transferKind, allocationBlockSizeDivisor, wcycle))
 {
 }
 
@@ -587,7 +545,7 @@ std::tuple<int, int> StatePropagatorDataGpu::getAtomRangesFromAtomLocality(AtomL
 }
 
 
-DeviceBuffer<float> StatePropagatorDataGpu::getCoordinates()
+DeviceBuffer<RVec> StatePropagatorDataGpu::getCoordinates()
 {
     return impl_->getCoordinates();
 }
@@ -627,7 +585,7 @@ void StatePropagatorDataGpu::waitCoordinatesReadyOnHost(AtomLocality atomLocalit
 }
 
 
-DeviceBuffer<float> StatePropagatorDataGpu::getVelocities()
+DeviceBuffer<RVec> StatePropagatorDataGpu::getVelocities()
 {
     return impl_->getVelocities();
 }
@@ -654,7 +612,7 @@ void StatePropagatorDataGpu::waitVelocitiesReadyOnHost(AtomLocality atomLocality
 }
 
 
-DeviceBuffer<float> StatePropagatorDataGpu::getForces()
+DeviceBuffer<RVec> StatePropagatorDataGpu::getForces()
 {
     return impl_->getForces();
 }
@@ -686,7 +644,7 @@ void StatePropagatorDataGpu::waitForcesReadyOnHost(AtomLocality atomLocality)
 }
 
 
-void* StatePropagatorDataGpu::getUpdateStream()
+const DeviceStream* StatePropagatorDataGpu::getUpdateStream()
 {
     return impl_->getUpdateStream();
 }
@@ -703,4 +661,4 @@ int StatePropagatorDataGpu::numAtomsAll()
 
 } // namespace gmx
 
-#endif // GMX_GPU == GMX_GPU_NONE
+#endif // GMX_GPU
similarity index 87%
rename from src/gromacs/awh/CMakeLists.txt
rename to src/gromacs/mdtypes/tests/CMakeLists.txt
index 8fc400a9e7892fd1d57b798842243292c9eadc5a..69e1b6b00b17e8bebf82e474a0a5d423843ab274 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
+# Copyright (c) 2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -32,9 +32,8 @@
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-file(GLOB AWH_SOURCES *.cpp)
-set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${AWH_SOURCES} PARENT_SCOPE)
-
-if (BUILD_TESTING)
-    add_subdirectory(tests)
-endif()
+gmx_add_unit_test(MdtypesUnitTest mdtypes-test
+    CPP_SOURCE_FILES
+        checkpointdata.cpp
+        forcebuffers.cpp
+        )
diff --git a/src/gromacs/mdtypes/tests/checkpointdata.cpp b/src/gromacs/mdtypes/tests/checkpointdata.cpp
new file mode 100644 (file)
index 0000000..f3a899d
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#include "gmxpre.h"
+
+#include "gromacs/mdtypes/checkpointdata.h"
+
+#include <algorithm>
+#include <random>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "gromacs/fileio/gmxfio.h"
+#include "gromacs/fileio/gmxfio_xdr.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/inmemoryserializer.h"
+
+#include "testutils/testfilemanager.h"
+
+namespace gmx::test
+{
+namespace
+{
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Struct allowing to check if type is vector of serializable data
+ */
+//! \{
+template<class T>
+struct IsVectorOfSerializableType
+{
+    static bool const value = false;
+};
+template<class T>
+struct IsVectorOfSerializableType<std::vector<T>>
+{
+    static bool const value = IsSerializableType<T>::value;
+};
+//! \}
+
+/*! \internal
+ * \brief Unified looping over test data
+ *
+ * This class allows to write a loop over test data as
+ *     for (const auto& value : TestValues::testValueGenerator<type>())
+ * where type can be any of std::string, int, int64_t, bool, float, double,
+ * std::vector<[std::string, int, int64_6, float, double]>, or tensor.
+ */
+class TestValues
+{
+public:
+    /*! \internal
+     * \brief Helper class allowing to loop over test values
+     * \tparam T  type of value
+     */
+    template<typename T>
+    class TestValueGenerator
+    {
+    public:
+        //! Custom iterator
+        class Iterator
+        {
+        public:
+            explicit Iterator(const T* ptr) : ptr_(ptr) {}
+            Iterator operator++();
+            bool     operator!=(const Iterator& other) const { return ptr_ != other.ptr_; }
+            const T& operator*() const { return *ptr_; }
+
+        private:
+            const T* ptr_;
+        };
+
+        Iterator begin() const;
+        Iterator end() const;
+    };
+
+    /*! \internal
+     * \brief Static function returning a TestValueGenerator of type T
+     * \tparam T  type of values generated
+     * \return TestValueGenerator<T>
+     */
+    template<typename T>
+    static TestValueGenerator<T> testValueGenerator()
+    {
+        static const TestValueGenerator<T> testValueGenerator;
+        return testValueGenerator;
+    }
+
+private:
+    template<typename T>
+    static const std::vector<T>& getTestVector();
+
+    template<typename T>
+    static std::enable_if_t<IsSerializableType<T>::value && !std::is_same<T, bool>::value, const T*>
+    getBeginPointer();
+    template<typename T>
+    static std::enable_if_t<IsVectorOfSerializableType<T>::value, const T*> getBeginPointer();
+    template<typename T>
+    static std::enable_if_t<std::is_same<T, bool>::value, const T*> getBeginPointer();
+    template<typename T>
+    static std::enable_if_t<std::is_same<T, tensor>::value, const T*> getBeginPointer();
+
+    template<typename T>
+    static std::enable_if_t<IsSerializableType<T>::value && !std::is_same<T, bool>::value, const T*>
+    getEndPointer();
+    template<typename T>
+    static std::enable_if_t<IsVectorOfSerializableType<T>::value, const T*> getEndPointer();
+    template<typename T>
+    static std::enable_if_t<std::is_same<T, bool>::value, const T*> getEndPointer();
+    template<typename T>
+    static std::enable_if_t<std::is_same<T, tensor>::value, const T*> getEndPointer();
+
+    template<typename T>
+    static std::enable_if_t<IsSerializableType<T>::value && !std::is_same<T, bool>::value, void>
+    increment(const T** ptr);
+    template<typename T>
+    static std::enable_if_t<IsVectorOfSerializableType<T>::value, void> increment(const T** ptr);
+    template<typename T>
+    static std::enable_if_t<std::is_same<T, bool>::value, void> increment(const T** ptr);
+    template<typename T>
+    static std::enable_if_t<std::is_same<T, tensor>::value, void> increment(const T** ptr);
+
+    constexpr static bool   testTrue    = true;
+    constexpr static bool   testFalse   = false;
+    constexpr static tensor testTensor1 = { { 1.6234, 2.4632, 3.1112 },
+                                            { 4.66234, 5.9678, 6.088 },
+                                            { 7.00001, 8.43535, 9.11233 } };
+#if GMX_DOUBLE
+    constexpr static tensor testTensor2 = { { 1, GMX_DOUBLE_EPS, 3 },
+                                            { GMX_DOUBLE_MIN, 5, 6 },
+                                            { 7, 8, GMX_DOUBLE_MAX } };
+#else
+    constexpr static tensor testTensor2 = { { 1, GMX_FLOAT_EPS, 3 },
+                                            { GMX_FLOAT_MIN, 5, 6 },
+                                            { 7, 8, GMX_FLOAT_MAX } };
+#endif
+};
+
+// Begin implementations of TestValues methods
+template<>
+const std::vector<std::string>& TestValues::getTestVector()
+{
+    static const std::vector<std::string> testStrings({ "Test string\nwith newlines\n", "" });
+    return testStrings;
+}
+template<>
+const std::vector<int>& TestValues::getTestVector()
+{
+    static const std::vector<int> testInts({ { 3, INT_MAX, INT_MIN } });
+    return testInts;
+}
+template<>
+const std::vector<int64_t>& TestValues::getTestVector()
+{
+    static const std::vector<int64_t> testInt64s({ -7, LLONG_MAX, LLONG_MIN });
+    return testInt64s;
+}
+template<>
+const std::vector<float>& TestValues::getTestVector()
+{
+    static const std::vector<float> testFloats({ 33.9, GMX_FLOAT_MAX, GMX_FLOAT_MIN, GMX_FLOAT_EPS });
+    return testFloats;
+}
+template<>
+const std::vector<double>& TestValues::getTestVector()
+{
+    static const std::vector<double> testDoubles({ -123.45, GMX_DOUBLE_MAX, GMX_DOUBLE_MIN, GMX_DOUBLE_EPS });
+    return testDoubles;
+}
+
+template<typename T>
+std::enable_if_t<IsSerializableType<T>::value && !std::is_same<T, bool>::value, const T*> TestValues::getBeginPointer()
+{
+    return getTestVector<T>().data();
+}
+template<typename T>
+std::enable_if_t<IsVectorOfSerializableType<T>::value, const T*> TestValues::getBeginPointer()
+{
+    return &getTestVector<typename T::value_type>();
+}
+template<typename T>
+std::enable_if_t<std::is_same<T, bool>::value, const T*> TestValues::getBeginPointer()
+{
+    return &testTrue;
+}
+template<typename T>
+std::enable_if_t<std::is_same<T, tensor>::value, const T*> TestValues::getBeginPointer()
+{
+    return &testTensor1;
+}
+
+template<typename T>
+std::enable_if_t<IsSerializableType<T>::value && !std::is_same<T, bool>::value, const T*> TestValues::getEndPointer()
+{
+    return getTestVector<T>().data() + getTestVector<T>().size();
+}
+template<typename T>
+std::enable_if_t<IsVectorOfSerializableType<T>::value, const T*> TestValues::getEndPointer()
+{
+    return &getTestVector<typename T::value_type>() + 1;
+}
+template<typename T>
+std::enable_if_t<std::is_same<T, bool>::value, const T*> TestValues::getEndPointer()
+{
+    return nullptr;
+}
+template<typename T>
+std::enable_if_t<std::is_same<T, tensor>::value, const T*> TestValues::getEndPointer()
+{
+    return nullptr;
+}
+
+template<typename T>
+std::enable_if_t<IsSerializableType<T>::value && !std::is_same<T, bool>::value, void>
+TestValues::increment(const T** ptr)
+{
+    ++(*ptr);
+}
+template<typename T>
+std::enable_if_t<IsVectorOfSerializableType<T>::value, void> TestValues::increment(const T** ptr)
+{
+    ++(*ptr);
+}
+template<typename T>
+std::enable_if_t<std::is_same<T, bool>::value, void> TestValues::increment(const T** ptr)
+{
+    *ptr = (*ptr == &testTrue) ? &testFalse : nullptr;
+}
+template<typename T>
+std::enable_if_t<std::is_same<T, tensor>::value, void> TestValues::increment(const T** ptr)
+{
+    *ptr = (*ptr == &testTensor1) ? &testTensor2 : nullptr;
+}
+
+template<typename T>
+typename TestValues::TestValueGenerator<T>::Iterator TestValues::TestValueGenerator<T>::begin() const
+{
+    return TestValues::TestValueGenerator<T>::Iterator(getBeginPointer<T>());
+}
+
+template<typename T>
+typename TestValues::TestValueGenerator<T>::Iterator TestValues::TestValueGenerator<T>::end() const
+{
+    return TestValues::TestValueGenerator<T>::Iterator(getEndPointer<T>());
+}
+
+template<typename T>
+typename TestValues::TestValueGenerator<T>::Iterator TestValues::TestValueGenerator<T>::Iterator::operator++()
+{
+    TestValues::increment(&ptr_);
+    return *this;
+}
+// End implementations of TestValues methods
+
+//! Write scalar input to CheckpointData
+template<typename T>
+typename std::enable_if_t<IsSerializableType<T>::value, void>
+writeInput(const std::string& key, const T& inputValue, WriteCheckpointData* checkpointData)
+{
+    checkpointData->scalar(key, &inputValue);
+}
+//! Read scalar from CheckpointData and test if equal to input
+template<typename T>
+typename std::enable_if_t<IsSerializableType<T>::value, void>
+testOutput(const std::string& key, const T& inputValue, ReadCheckpointData* checkpointData)
+{
+    T outputValue;
+    checkpointData->scalar(key, &outputValue);
+    EXPECT_EQ(inputValue, outputValue);
+}
+//! Write vector input to CheckpointData
+template<typename T>
+void writeInput(const std::string& key, const std::vector<T>& inputVector, WriteCheckpointData* checkpointData)
+{
+    checkpointData->arrayRef(key, makeConstArrayRef(inputVector));
+}
+//! Read vector from CheckpointData and test if equal to input
+template<typename T>
+void testOutput(const std::string& key, const std::vector<T>& inputVector, ReadCheckpointData* checkpointData)
+{
+    std::vector<T> outputVector;
+    outputVector.resize(inputVector.size());
+    checkpointData->arrayRef(key, makeArrayRef(outputVector));
+    EXPECT_THAT(outputVector, ::testing::ContainerEq(inputVector));
+}
+//! Write tensor input to CheckpointData
+void writeInput(const std::string& key, const tensor inputTensor, WriteCheckpointData* checkpointData)
+{
+    checkpointData->tensor(key, inputTensor);
+}
+//! Read tensor from CheckpointData and test if equal to input
+void testOutput(const std::string& key, const tensor inputTensor, ReadCheckpointData* checkpointData)
+{
+    tensor outputTensor = { { 0 } };
+    checkpointData->tensor(key, outputTensor);
+    std::array<std::array<real, 3>, 3> inputTensorArray = {
+        { { inputTensor[XX][XX], inputTensor[XX][YY], inputTensor[XX][ZZ] },
+          { inputTensor[YY][XX], inputTensor[YY][YY], inputTensor[YY][ZZ] },
+          { inputTensor[ZZ][XX], inputTensor[ZZ][YY], inputTensor[ZZ][ZZ] } }
+    };
+    std::array<std::array<real, 3>, 3> outputTensorArray = {
+        { { outputTensor[XX][XX], outputTensor[XX][YY], outputTensor[XX][ZZ] },
+          { outputTensor[YY][XX], outputTensor[YY][YY], outputTensor[YY][ZZ] },
+          { outputTensor[ZZ][XX], outputTensor[ZZ][YY], outputTensor[ZZ][ZZ] } }
+    };
+    EXPECT_THAT(outputTensorArray, ::testing::ContainerEq(inputTensorArray));
+}
+
+/*! \internal
+ * \brief CheckpointData test fixture
+ *
+ * Test whether input is equal to output, either with a single data type
+ * or with a combination of three data types.
+ */
+class CheckpointDataTest : public ::testing::Test
+{
+public:
+    using WriteFunction = std::function<void(WriteCheckpointData*)>;
+    using TestFunction  = std::function<void(ReadCheckpointData*)>;
+
+    // List of functions to write values to checkpoint
+    std::vector<WriteFunction> writeFunctions_;
+    // List of functions to test read checkpoint object
+    std::vector<TestFunction> testFunctions_;
+
+    // Add values to write / test lists
+    template<typename T>
+    void addTestValues()
+    {
+        for (const auto& inputValue : TestValues::testValueGenerator<T>())
+        {
+            std::string key = "value" + std::to_string(writeFunctions_.size());
+            writeFunctions_.emplace_back([key, inputValue](WriteCheckpointData* checkpointData) {
+                writeInput(key, inputValue, checkpointData);
+            });
+            testFunctions_.emplace_back([key, inputValue](ReadCheckpointData* checkpointData) {
+                testOutput(key, inputValue, checkpointData);
+            });
+        }
+    }
+
+    /* This shuffles the write and test functions (as writing and reading can happen
+     * if different orders), then writes all data to a CheckpointData object.
+     * The CheckpointData object is serialized to file, and then re-read into
+     * a new CheckpointData object. The test functions are then used to assert
+     * that all data is present in the new object.
+     */
+    void test()
+    {
+        /* Randomize order of writing and testing - checkpoint data can be
+         * accessed in any order!
+         * Having the same order makes this reproducible, so at least for now we're
+         * ok seeding the rng with default value and silencing clang-tidy */
+        // NOLINTNEXTLINE(cert-msc51-cpp)
+        auto rng = std::default_random_engine{};
+        std::shuffle(std::begin(writeFunctions_), std::end(writeFunctions_), rng);
+        std::shuffle(std::begin(testFunctions_), std::end(testFunctions_), rng);
+
+        // Write value to CheckpointData & serialize
+        {
+            WriteCheckpointDataHolder writeCheckpointDataHolder;
+            auto writeCheckpointData = writeCheckpointDataHolder.checkpointData("test");
+            for (const auto& writeFunction : writeFunctions_)
+            {
+                writeFunction(&writeCheckpointData);
+            }
+
+            auto*               file = gmx_fio_open(filename_.c_str(), "w");
+            FileIOXdrSerializer serializer(file);
+            writeCheckpointDataHolder.serialize(&serializer);
+            gmx_fio_close(file);
+        }
+
+        // Deserialize values and test against reference
+        {
+            auto*               file = gmx_fio_open(filename_.c_str(), "r");
+            FileIOXdrSerializer deserializer(file);
+
+            ReadCheckpointDataHolder readCheckpointDataHolder;
+            readCheckpointDataHolder.deserialize(&deserializer);
+            gmx_fio_close(file);
+
+            auto readCheckpointData = readCheckpointDataHolder.checkpointData("test");
+            for (const auto& testFunction : testFunctions_)
+            {
+                testFunction(&readCheckpointData);
+            }
+        }
+
+        // Object can be reused
+        writeFunctions_.clear();
+        testFunctions_.clear();
+    }
+
+    // The different functions to add test values - in a list to simplify looping over them
+    std::vector<std::function<void()>> addTestValueFunctions_ = {
+        [this]() { addTestValues<std::string>(); },
+        [this]() { addTestValues<int>(); },
+        [this]() { addTestValues<int64_t>(); },
+        [this]() { addTestValues<bool>(); },
+        [this]() { addTestValues<float>(); },
+        [this]() { addTestValues<double>(); },
+        [this]() { addTestValues<std::vector<std::string>>(); },
+        [this]() { addTestValues<std::vector<int>>(); },
+        [this]() { addTestValues<std::vector<int64_t>>(); },
+        [this]() { addTestValues<std::vector<float>>(); },
+        [this]() { addTestValues<std::vector<double>>(); },
+        [this]() { addTestValues<tensor>(); }
+    };
+
+    // The types we're testing - for scoped trace output only
+    std::vector<std::string> testingTypes_ = { "std::string",
+                                               "int",
+                                               "int64_t",
+                                               "bool",
+                                               "float",
+                                               "double",
+                                               "std::vector<std::string>",
+                                               "std::vector<int>",
+                                               "std::vector<int64_t>",
+                                               "std::vector<float>",
+                                               "std::vector<double>",
+                                               "tensor" };
+
+    // We'll need a temporary file to write / read our dummy checkpoint to
+    TestFileManager fileManager_;
+    std::string     filename_ = fileManager_.getTemporaryFilePath("test.cpt");
+};
+
+TEST_F(CheckpointDataTest, SingleDataTest)
+{
+    // Test data types separately
+    const int numTypes = addTestValueFunctions_.size();
+    for (int type = 0; type < numTypes; ++type)
+    {
+        SCOPED_TRACE("Using test values of type " + testingTypes_[type]);
+        addTestValueFunctions_[type]();
+        test();
+    }
+}
+
+TEST_F(CheckpointDataTest, MultiDataTest)
+{
+    // All combinations of 2 different data types
+    const int numTypes = addTestValueFunctions_.size();
+    for (int type1 = 0; type1 < numTypes; ++type1)
+    {
+        for (int type2 = type1; type2 < numTypes; ++type2)
+        {
+            SCOPED_TRACE("Using test values of type " + testingTypes_[type1] + " and "
+                         + testingTypes_[type2]);
+            addTestValueFunctions_[type1]();
+            addTestValueFunctions_[type2]();
+            test();
+        }
+    }
+}
+
+} // namespace
+} // namespace gmx::test
diff --git a/src/gromacs/mdtypes/tests/forcebuffers.cpp b/src/gromacs/mdtypes/tests/forcebuffers.cpp
new file mode 100644 (file)
index 0000000..504d42f
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for the ForceBuffers and ForceBuffersView classes.
+ *
+ * \author berk Hess <hess@kth.se>
+ * \ingroup module_mdtypes
+ */
+#include "gmxpre.h"
+
+#include "gromacs/mdtypes/forcebuffers.h"
+
+#include <array>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "testutils/testasserts.h"
+
+namespace gmx
+{
+
+const std::array<RVec, 2> c_forces = { { { 0.5, 0.1, 1.2 }, { -2.1, 0.2, 0.3 } } };
+
+TEST(ForceBuffers, ConstructsUnpinned)
+{
+    ForceBuffers forceBuffers;
+
+    EXPECT_EQ(forceBuffers.pinningPolicy(), PinningPolicy::CannotBePinned);
+}
+
+TEST(ForceBuffers, ConstructsPinned)
+{
+    ForceBuffers forceBuffers(false, PinningPolicy::PinnedIfSupported);
+
+    EXPECT_EQ(forceBuffers.pinningPolicy(), PinningPolicy::PinnedIfSupported);
+}
+
+TEST(ForceBuffers, ConstructsEmpty)
+{
+    ForceBuffers forceBuffers;
+
+    EXPECT_EQ(forceBuffers.view().force().size(), 0);
+}
+
+TEST(ForceBuffers, ResizeWorks)
+{
+    ForceBuffers forceBuffers;
+
+    forceBuffers.resize(2);
+    EXPECT_EQ(forceBuffers.view().force().size(), 2);
+}
+
+TEST(ForceBuffers, PaddingWorks)
+{
+    ForceBuffers forceBuffers;
+
+    forceBuffers.resize(2);
+    auto paddedRef = forceBuffers.view().forceWithPadding();
+    EXPECT_EQ(paddedRef.unpaddedArrayRef().size(), 2);
+    EXPECT_GT(paddedRef.size(), 2);
+}
+
+
+TEST(ForceBuffers, CopyWorks)
+{
+    ForceBuffers forceBuffers;
+
+    forceBuffers.resize(2);
+    auto  force = forceBuffers.view().force();
+    index i     = 0;
+    for (RVec& v : force)
+    {
+        v = c_forces[i];
+        i++;
+    }
+
+    ForceBuffers forceBuffersCopy;
+    forceBuffersCopy = forceBuffers;
+    auto forceCopy   = forceBuffersCopy.view().force();
+    EXPECT_EQ(forceBuffersCopy.view().force().size(), 2);
+    for (index i = 0; i < ssize(forceCopy); i++)
+    {
+        for (int d = 0; d < DIM; d++)
+        {
+            EXPECT_EQ(forceCopy[i][d], force[i][d]);
+        }
+    }
+}
+
+TEST(ForceBuffers, CopyDoesNotPin)
+{
+    ForceBuffers forceBuffers(false, PinningPolicy::PinnedIfSupported);
+
+    ForceBuffers forceBuffersCopy;
+    forceBuffersCopy = forceBuffers;
+    EXPECT_EQ(forceBuffersCopy.pinningPolicy(), PinningPolicy::CannotBePinned);
+}
+
+} // namespace gmx
index 7c2b89ff7498af3fdc2bcfcae8ceb6e80da27770..12dcf874d3f976913149edf461766bd8eb2ca813 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -102,7 +102,7 @@ void gmx::MimicCommunicator::init()
 {
     char path[GMX_PATH_MAX];
     gmx_getcwd(path, GMX_PATH_MAX);
-    return MCL_init_client(path);
+    MCL_init_client(path);
 }
 
 void gmx::MimicCommunicator::sendInitData(gmx_mtop_t* mtop, PaddedHostVector<gmx::RVec> coords)
index 033a11d482ce588cb3e19fd0c1cb9ea7af5a3cfb..6d2b9c276139420de2eeb25d2e539e841d7821d8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -60,7 +60,7 @@ public:
     /*! \brief
      * Initializes the communicator
      */
-    void init();
+    static void init();
 
     /*! \brief
      * Sends the data needed for MiMiC initialization
@@ -73,14 +73,14 @@ public:
      * @param mtop global topology data
      * @param coords coordinates of all atoms
      */
-    void sendInitData(gmx_mtop_t* mtop, PaddedHostVector<gmx::RVec> coords);
+    static void sendInitData(gmx_mtop_t* mtop, PaddedHostVector<gmx::RVec> coords);
 
     /*! \brief
      * Gets the number of MD steps to perform from MiMiC
      *
      * @return nsteps the number of MD steps to perform
      */
-    int64_t getStepNumber();
+    static int64_t getStepNumber();
 
     /*! \brief
      * Receive and array of updated atomic coordinates from MiMiC
@@ -88,14 +88,14 @@ public:
      * @param x array of coordinates to fill
      * @param natoms number of atoms in the system
      */
-    void getCoords(PaddedHostVector<RVec>* x, int natoms);
+    static void getCoords(PaddedHostVector<RVec>* x, int natoms);
 
     /*! \brief
      * Send the potential energy value to MiMiC
      *
      * @param energy energy value to send
      */
-    void sendEnergies(real energy);
+    static void sendEnergies(real energy);
 
     /*! \brief
      * Send classical forces acting on all atoms in the system
@@ -104,12 +104,12 @@ public:
      * @param forces array of forces to send
      * @param natoms number of atoms in the system
      */
-    void sendForces(ArrayRef<gmx::RVec> forces, int natoms);
+    static void sendForces(ArrayRef<gmx::RVec> forces, int natoms);
 
     /*! \brief
      * Finish communications and disconnect from the server
      */
-    void finalize();
+    static void finalize();
 };
 
 } // namespace gmx
index 28d6eaac284e36901f0219ffb3fed647420da4a0..ac50973e41ceb9cd2278ad4afb69a58d2050a519 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 file(GLOB MODULARSIMULATOR_SOURCES *.cpp)
 
 add_library(modularsimulator OBJECT ${MODULARSIMULATOR_SOURCES})
+target_include_directories(modularsimulator SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/src/external)
 gmx_target_compile_options(modularsimulator)
 target_compile_definitions(modularsimulator PRIVATE HAVE_CONFIG_H)
+# Should be possible to remove this when resolving #3290
 target_include_directories(modularsimulator SYSTEM BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include)
 
 if(GMX_OPENMP)
index 6e83ded8bb1fe72b60a2f16d2b971f2b1388cbf7..64487905f47fb3b2efe8d1550030312bfc67beea 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/domdec/domdec.h"
 #include "gromacs/mdlib/mdoutf.h"
+#include "gromacs/mdtypes/checkpointdata.h"
 #include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/energyhistory.h"
+#include "gromacs/mdtypes/observableshistory.h"
+#include "gromacs/mdtypes/pullhistory.h"
 #include "gromacs/mdtypes/state.h"
+#include "gromacs/utility/stringutil.h"
 
 #include "trajectoryelement.h"
 
 namespace gmx
 {
-CheckpointHelper::CheckpointHelper(std::vector<ICheckpointHelperClient*> clients,
-                                   std::unique_ptr<CheckpointHandler>    checkpointHandler,
-                                   int                                   initStep,
-                                   TrajectoryElement*                    trajectoryElement,
-                                   int                                   globalNumAtoms,
-                                   FILE*                                 fplog,
-                                   t_commrec*                            cr,
-                                   ObservablesHistory*                   observablesHistory,
-                                   gmx_walltime_accounting*              walltime_accounting,
-                                   t_state*                              state_global,
-                                   bool                                  writeFinalCheckpoint) :
+CheckpointHelper::CheckpointHelper(std::vector<std::tuple<std::string, ICheckpointHelperClient*>>&& clients,
+                                   std::unique_ptr<CheckpointHandler> checkpointHandler,
+                                   int                                initStep,
+                                   TrajectoryElement*                 trajectoryElement,
+                                   FILE*                              fplog,
+                                   t_commrec*                         cr,
+                                   ObservablesHistory*                observablesHistory,
+                                   gmx_walltime_accounting*           walltime_accounting,
+                                   t_state*                           state_global,
+                                   bool                               writeFinalCheckpoint) :
     clients_(std::move(clients)),
     checkpointHandler_(std::move(checkpointHandler)),
     initStep_(initStep),
     lastStep_(-1),
-    globalNumAtoms_(globalNumAtoms),
     writeFinalCheckpoint_(writeFinalCheckpoint),
     trajectoryElement_(trajectoryElement),
-    localState_(nullptr),
     fplog_(fplog),
     cr_(cr),
     observablesHistory_(observablesHistory),
     walltime_accounting_(walltime_accounting),
     state_global_(state_global)
 {
-    // Get rid of nullptr in clients list
-    clients_.erase(std::remove_if(clients_.begin(), clients_.end(),
-                                  [](ICheckpointHelperClient* ptr) { return ptr == nullptr; }),
-                   clients_.end());
-    if (DOMAINDECOMP(cr))
+    if (!observablesHistory_->energyHistory)
     {
-        localState_ = std::make_unique<t_state>();
-        dd_init_local_state(cr->dd, state_global, localState_.get());
-        localStateInstance_ = localState_.get();
+        observablesHistory_->energyHistory = std::make_unique<energyhistory_t>();
     }
-    else
+    if (!observablesHistory_->pullHistory)
     {
-        state_change_natoms(state_global, state_global->natoms);
-        localStateInstance_ = state_global;
+        observablesHistory_->pullHistory = std::make_unique<PullHistory>();
     }
 }
 
@@ -107,34 +102,86 @@ void CheckpointHelper::run(Step step, Time time)
     checkpointHandler_->setSignal(walltime_accounting_);
 }
 
-void CheckpointHelper::scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction)
+void CheckpointHelper::scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction)
 {
     // Only last step checkpointing is done here
     if (step != lastStep_ || !writeFinalCheckpoint_)
     {
         return;
     }
-    (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-            [this, step, time]() { writeCheckpoint(step, time); }));
+    registerRunFunction([this, step, time]() { writeCheckpoint(step, time); });
 }
 
 void CheckpointHelper::writeCheckpoint(Step step, Time time)
 {
-    localStateInstance_->flags = 0;
-    for (const auto& client : clients_)
+    WriteCheckpointDataHolder checkpointDataHolder;
+    for (const auto& [key, client] : clients_)
     {
-        client->writeCheckpoint(localStateInstance_, state_global_);
+        client->saveCheckpointState(
+                MASTER(cr_) ? std::make_optional(checkpointDataHolder.checkpointData(key)) : std::nullopt,
+                cr_);
     }
 
-    mdoutf_write_to_trajectory_files(fplog_, cr_, trajectoryElement_->outf_, MDOF_CPT,
-                                     globalNumAtoms_, step, time, localStateInstance_,
-                                     state_global_, observablesHistory_, ArrayRef<RVec>());
+    if (MASTER(cr_))
+    {
+        mdoutf_write_checkpoint(trajectoryElement_->outf_, fplog_, cr_, step, time, state_global_,
+                                observablesHistory_, &checkpointDataHolder);
+    }
+}
+
+std::optional<SignallerCallback> CheckpointHelper::registerLastStepCallback()
+{
+    return [this](Step step, Time gmx_unused time) { this->lastStep_ = step; };
+}
+
+CheckpointHelperBuilder::CheckpointHelperBuilder(std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder,
+                                                 StartingBehavior startingBehavior,
+                                                 t_commrec*       cr) :
+    resetFromCheckpoint_(startingBehavior != StartingBehavior::NewSimulation),
+    checkpointDataHolder_(std::move(checkpointDataHolder)),
+    checkpointHandler_(nullptr),
+    cr_(cr),
+    state_(ModularSimulatorBuilderState::AcceptingClientRegistrations)
+{
+}
+
+void CheckpointHelperBuilder::registerClient(ICheckpointHelperClient* client)
+{
+    if (!client)
+    {
+        return;
+    }
+    if (state_ == ModularSimulatorBuilderState::NotAcceptingClientRegistrations)
+    {
+        throw SimulationAlgorithmSetupError(
+                "Tried to register to CheckpointHelper after it was built.");
+    }
+    const auto& key = client->clientID();
+    if (clientsMap_.count(key) != 0)
+    {
+        throw SimulationAlgorithmSetupError("CheckpointHelper client key is not unique.");
+    }
+    clientsMap_[key] = client;
+    if (resetFromCheckpoint_)
+    {
+        if (MASTER(cr_) && !checkpointDataHolder_->keyExists(key))
+        {
+            throw SimulationAlgorithmSetupError(
+                    formatString(
+                            "CheckpointHelper client with key %s registered for checkpointing, "
+                            "but %s does not exist in the input checkpoint file.",
+                            key.c_str(), key.c_str())
+                            .c_str());
+        }
+        client->restoreCheckpointState(
+                MASTER(cr_) ? std::make_optional(checkpointDataHolder_->checkpointData(key)) : std::nullopt,
+                cr_);
+    }
 }
 
-SignallerCallbackPtr CheckpointHelper::registerLastStepCallback()
+void CheckpointHelperBuilder::setCheckpointHandler(std::unique_ptr<CheckpointHandler> checkpointHandler)
 {
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { this->lastStep_ = step; });
+    checkpointHandler_ = std::move(checkpointHandler);
 }
 
 } // namespace gmx
index 905e636af3f16aaa1fa1e152993a2ea22ec38f99..dcebdc5ba928b9f6e03b21461186de7e8c67518c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the checkpoint helper for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_CHECKPOINTHELPER_H
 #define GMX_MODULARSIMULATOR_CHECKPOINTHELPER_H
 
+#include <map>
 #include <vector>
 
 #include "gromacs/mdlib/checkpointhandler.h"
+#include "gromacs/mdrunutility/handlerestart.h"
 
 #include "modularsimulatorinterfaces.h"
 
@@ -53,10 +57,11 @@ struct ObservablesHistory;
 
 namespace gmx
 {
+class KeyValueTreeObject;
 class MDLogger;
 class TrajectoryElement;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Checkpoint helper
  *
@@ -78,40 +83,36 @@ class TrajectoryElement;
  * Checkpointing happens at the end of a simulation step, which gives a
  * straightforward re-entry point at the top of the simulator loop.
  *
- * In the current implementation, the clients of CheckpointHelper fill a
- * legacy t_state object (passed via pointer) with whatever data they need
- * to store. The CheckpointHelper then writes the t_state object to file.
- * This is an intermediate state of the code, as the long-term plan is for
- * modules to read and write from a checkpoint file directly, without the
- * need for a central object. The current implementation allows, however,
- * to define clearly which modules take part in checkpointing, while using
- * the current infrastructure for reading and writing to checkpoint.
+ * Checkpoint writing is done by passing sub-objects of a
+ * WriteCheckpointDataHolder object to the clients. Checkpoint reading is
+ * done by passing sub-objects of a ReadCheckpointDataHolder object (passed
+ * in from runner level) do the clients.
  *
- * \todo Develop this into a module solely providing a file handler to
- *       modules for checkpoint reading and writing.
+ * \see ReadCheckpointDataHolder
+ * \see WriteCheckpointDataHolder
+ * \see CheckpointData
  */
 class CheckpointHelper final : public ILastStepSignallerClient, public ISimulatorElement
 {
 public:
     //! Constructor
-    CheckpointHelper(std::vector<ICheckpointHelperClient*> clients,
-                     std::unique_ptr<CheckpointHandler>    checkpointHandler,
-                     int                                   initStep,
-                     TrajectoryElement*                    trajectoryElement,
-                     int                                   globalNumAtoms,
-                     FILE*                                 fplog,
-                     t_commrec*                            cr,
-                     ObservablesHistory*                   observablesHistory,
-                     gmx_walltime_accounting*              walltime_accounting,
-                     t_state*                              state_global,
-                     bool                                  writeFinalCheckpoint);
+    CheckpointHelper(std::vector<std::tuple<std::string, ICheckpointHelperClient*>>&& clients,
+                     std::unique_ptr<CheckpointHandler> checkpointHandler,
+                     int                                initStep,
+                     TrajectoryElement*                 trajectoryElement,
+                     FILE*                              fplog,
+                     t_commrec*                         cr,
+                     ObservablesHistory*                observablesHistory,
+                     gmx_walltime_accounting*           walltime_accounting,
+                     t_state*                           state_global,
+                     bool                               writeFinalCheckpoint);
 
     /*! \brief Run checkpointing
      *
      * Sets signal and / or performs checkpointing at neighbor searching steps
      *
-     * @param step  The step number
-     * @param time  The time
+     * \param step  The step number
+     * \param time  The time
      */
     void run(Step step, Time time);
 
@@ -121,11 +122,11 @@ public:
      * list, as the checkpoint helper need to be able to react to the last step
      * being signalled.
      *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
      */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
 
     //! No element setup needed
     void elementSetup() override {}
@@ -134,7 +135,7 @@ public:
 
 private:
     //! List of checkpoint clients
-    std::vector<ICheckpointHelperClient*> clients_;
+    std::vector<std::tuple<std::string, ICheckpointHelperClient*>> clients_;
 
     //! The checkpoint handler
     std::unique_ptr<CheckpointHandler> checkpointHandler_;
@@ -143,13 +144,11 @@ private:
     const Step initStep_;
     //! The last step of the simulation
     Step lastStep_;
-    //! The total number of atoms
-    const int globalNumAtoms_;
     //! Whether a checkpoint is written on the last step
     const bool writeFinalCheckpoint_;
 
     //! ILastStepSignallerClient implementation
-    SignallerCallbackPtr registerLastStepCallback() override;
+    std::optional<SignallerCallback> registerLastStepCallback() override;
 
     //! The actual checkpoint writing function
     void writeCheckpoint(Step step, Time time);
@@ -157,12 +156,6 @@ private:
     //! Pointer to the trajectory element - to use file pointer
     TrajectoryElement* trajectoryElement_;
 
-    //! A local t_state object to gather data in
-    //! {
-    std::unique_ptr<t_state> localState_;
-    t_state*                 localStateInstance_;
-    //! }
-
     // Access to ISimulator data
     //! Handles logging.
     FILE* fplog_;
@@ -176,6 +169,68 @@ private:
     t_state* state_global_;
 };
 
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Builder for the checkpoint helper
+ */
+class CheckpointHelperBuilder
+{
+public:
+    //! Constructor
+    CheckpointHelperBuilder(std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder,
+                            StartingBehavior                          startingBehavior,
+                            t_commrec*                                cr);
+
+    //! Register checkpointing client
+    void registerClient(ICheckpointHelperClient* client);
+
+    //! Set CheckpointHandler
+    void setCheckpointHandler(std::unique_ptr<CheckpointHandler> checkpointHandler);
+
+    //! Return CheckpointHelper
+    template<typename... Args>
+    std::unique_ptr<CheckpointHelper> build(Args&&... args);
+
+private:
+    //! Map of checkpoint clients
+    std::map<std::string, ICheckpointHelperClient*> clientsMap_;
+    //! Whether we are resetting from checkpoint
+    const bool resetFromCheckpoint_;
+    //! The input checkpoint data
+    std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder_;
+    //! The checkpoint handler
+    std::unique_ptr<CheckpointHandler> checkpointHandler_;
+    //! Handles communication.
+    t_commrec* cr_;
+    //! Whether the builder accepts registrations.
+    ModularSimulatorBuilderState state_;
+};
+
+template<typename... Args>
+std::unique_ptr<CheckpointHelper> CheckpointHelperBuilder::build(Args&&... args)
+{
+    state_ = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
+    // Make sure that we don't have unused entries in checkpoint
+    if (resetFromCheckpoint_)
+    {
+        for (const auto& key : checkpointDataHolder_->keys())
+        {
+            if (clientsMap_.count(key) == 0)
+            {
+                // We have an entry in checkpointDataHolder_ which has no matching client
+                throw CheckpointError("Checkpoint entry " + key + " was not read. This "
+                                      "likely means that you are not using the same algorithm "
+                                      "that was used to create the checkpoint file.");
+            }
+        }
+    }
+
+    std::vector<std::tuple<std::string, ICheckpointHelperClient*>>&& clients = { clientsMap_.begin(),
+                                                                                 clientsMap_.end() };
+    return std::make_unique<CheckpointHelper>(std::move(clients), std::move(checkpointHandler_),
+                                              std::forward<Args>(args)...);
+}
+
 } // namespace gmx
 
 #endif // GMX_MODULARSIMULATOR_CHECKPOINTHELPER_H
index fa2e5ce46f7d20cd6d9d9bf305c54759c3b3c40d..0110bf80900cb311a7619dbc4909d5d5609559a8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,7 +53,7 @@ CompositeSimulatorElement::CompositeSimulatorElement(
 {
 }
 
-void CompositeSimulatorElement::scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction)
+void CompositeSimulatorElement::scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction)
 {
     for (auto& element : elementCallList_)
     {
index 36a05af736a72b48a1ce9a383963d70a9ea77eeb..cf730626ccb56d155b2f149a31c6b40733d7fa14 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the composite element for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 #ifndef GROMACS_MDTYPES_COMPOSITESIMULATORELEMENT_H
 #define GROMACS_MDTYPES_COMPOSITESIMULATORELEMENT_H
@@ -50,7 +52,7 @@
 namespace gmx
 {
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Composite simulator element
  *
@@ -80,11 +82,11 @@ public:
      * Lets every member of the composite simulator register run functions
      * for the given step.
      *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
      */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
 
     /*! \brief Element setup
      *
index 7e8a8199c39a862fb39cfb2cbf26f3f98a8cfdd7..ae4635d19d2405a122f2c83800fd6ba3c3a0f9b8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "computeglobalselement.h"
 
 #include "gromacs/domdec/partition.h"
+#include "gromacs/gmxlib/network.h"
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/mdlib/md_support.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/stat.h"
+#include "gromacs/mdlib/update.h"
+#include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/topology/topology.h"
 
-#include "freeenergyperturbationelement.h"
+#include "freeenergyperturbationdata.h"
+#include "modularsimulator.h"
+#include "simulatoralgorithm.h"
 
 namespace gmx
 {
 template<ComputeGlobalsAlgorithm algorithm>
 ComputeGlobalsElement<algorithm>::ComputeGlobalsElement(StatePropagatorData* statePropagatorData,
-                                                        EnergyElement*       energyElement,
-                                                        FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
+                                                        EnergyData*          energyData,
+                                                        FreeEnergyPerturbationData* freeEnergyPerturbationData,
                                                         SimulationSignals* signals,
                                                         int                nstglobalcomm,
                                                         FILE*              fplog,
                                                         const MDLogger&    mdlog,
                                                         t_commrec*         cr,
-                                                        t_inputrec*        inputrec,
+                                                        const t_inputrec*  inputrec,
                                                         const MDAtoms*     mdAtoms,
                                                         t_nrnb*            nrnb,
                                                         gmx_wallcycle*     wcycle,
                                                         t_forcerec*        fr,
                                                         const gmx_mtop_t*  global_top,
-                                                        Constraints*       constr,
-                                                        bool               hasReadEkinState) :
+                                                        Constraints*       constr) :
     energyReductionStep_(-1),
     virialReductionStep_(-1),
+    vvSchedulingStep_(-1),
     doStopCM_(inputrec->comm_mode != ecmNO),
     nstcomm_(inputrec->nstcomm),
     nstglobalcomm_(nstglobalcomm),
     lastStep_(inputrec->nsteps + inputrec->init_step),
     initStep_(inputrec->init_step),
     nullSignaller_(std::make_unique<SimulationSignaller>(nullptr, nullptr, nullptr, false, false)),
-    hasReadEkinState_(hasReadEkinState),
     totalNumberOfBondedInteractions_(0),
     shouldCheckNumberOfBondedInteractions_(false),
     statePropagatorData_(statePropagatorData),
-    energyElement_(energyElement),
+    energyData_(energyData),
     localTopology_(nullptr),
-    freeEnergyPerturbationElement_(freeEnergyPerturbationElement),
+    freeEnergyPerturbationData_(freeEnergyPerturbationData),
     vcm_(global_top->groups, *inputrec),
     signals_(signals),
     fplog_(fplog),
@@ -118,65 +123,46 @@ void ComputeGlobalsElement<algorithm>::elementSetup()
 {
     GMX_ASSERT(localTopology_, "Setup called before local topology was set.");
 
-    // Only do initial communication step for one of the velocity-verlet stages
-    if (algorithm == ComputeGlobalsAlgorithm::LeapFrog
-        || algorithm == ComputeGlobalsAlgorithm::VelocityVerletAtFullTimeStep)
+    if (doStopCM_ && !inputrec_->bContinuation)
     {
-        unsigned int cglo_flags =
-                (CGLO_TEMPERATURE | CGLO_GSTAT
-                 | (shouldCheckNumberOfBondedInteractions_ ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS : 0)
-                 | (hasReadEkinState_ ? CGLO_READEKIN : 0));
-
-        if (algorithm == ComputeGlobalsAlgorithm::VelocityVerletAtFullTimeStep)
-        {
-            cglo_flags |= CGLO_PRESSURE | CGLO_CONSTRAINT;
-        }
-
-        const bool stopCM = doStopCM_ && !inputrec_->bContinuation;
-
         // To minimize communication, compute_globals computes the COM velocity
         // and the kinetic energy for the velocities without COM motion removed.
         // Thus to get the kinetic energy without the COM contribution, we need
         // to call compute_globals twice.
-        for (int cgloIteration = 0; cgloIteration < (stopCM ? 2 : 1); cgloIteration++)
-        {
-            unsigned int cglo_flags_iteration = cglo_flags;
-            if (stopCM && cgloIteration == 0)
-            {
-                cglo_flags_iteration |= CGLO_STOPCM;
-                cglo_flags_iteration &= ~CGLO_TEMPERATURE;
-            }
 
-            compute(-1, cglo_flags_iteration, nullSignaller_.get(), false, true);
+        compute(-1, CGLO_GSTAT | CGLO_STOPCM, nullSignaller_.get(), false, true);
 
-            if (cglo_flags_iteration & CGLO_STOPCM)
-            {
-                auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
-                // At initialization, do not pass x with acceleration-correction mode
-                // to avoid (incorrect) correction of the initial coordinates.
-                rvec* xPtr = nullptr;
-                if (vcm_.mode != ecmLINEAR_ACCELERATION_CORRECTION)
-                {
-                    xPtr = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
-                }
-                process_and_stopcm_grp(fplog_, &vcm_, *mdAtoms_->mdatoms(), xPtr, v);
-                inc_nrnb(nrnb_, eNR_STOPCM, mdAtoms_->mdatoms()->homenr);
-            }
-        }
+        auto v = statePropagatorData_->velocitiesView();
+        // At initialization, do not pass x with acceleration-correction mode
+        // to avoid (incorrect) correction of the initial coordinates.
+        auto x = vcm_.mode == ecmLINEAR_ACCELERATION_CORRECTION ? ArrayRefWithPadding<RVec>()
+                                                                : statePropagatorData_->positionsView();
+        process_and_stopcm_grp(fplog_, &vcm_, *mdAtoms_->mdatoms(), x.unpaddedArrayRef(),
+                               v.unpaddedArrayRef());
+        inc_nrnb(nrnb_, eNR_STOPCM, mdAtoms_->mdatoms()->homenr);
+    }
 
-        // Calculate the initial half step temperature, and save the ekinh_old
-        for (int i = 0; (i < inputrec_->opts.ngtc); i++)
-        {
-            copy_mat(energyElement_->ekindata()->tcstat[i].ekinh,
-                     energyElement_->ekindata()->tcstat[i].ekinh_old);
-        }
+    unsigned int cglo_flags = (CGLO_TEMPERATURE | CGLO_GSTAT
+                               | (energyData_->hasReadEkinFromCheckpoint() ? CGLO_READEKIN : 0));
+
+    if (algorithm == ComputeGlobalsAlgorithm::VelocityVerlet)
+    {
+        cglo_flags |= CGLO_PRESSURE | CGLO_CONSTRAINT;
+    }
+
+    compute(-1, cglo_flags, nullSignaller_.get(), false, true);
+
+    // Calculate the initial half step temperature, and save the ekinh_old
+    for (int i = 0; (i < inputrec_->opts.ngtc); i++)
+    {
+        copy_mat(energyData_->ekindata()->tcstat[i].ekinh, energyData_->ekindata()->tcstat[i].ekinh_old);
     }
 }
 
 template<ComputeGlobalsAlgorithm algorithm>
 void ComputeGlobalsElement<algorithm>::scheduleTask(Step step,
-                                                    Time gmx_unused               time,
-                                                    const RegisterRunFunctionPtr& registerRunFunction)
+                                                    Time gmx_unused            time,
+                                                    const RegisterRunFunction& registerRunFunction)
 {
     const bool needComReduction    = doStopCM_ && do_per_step(step, nstcomm_);
     const bool needGlobalReduction = step == energyReductionStep_ || step == virialReductionStep_
@@ -202,10 +188,9 @@ void ComputeGlobalsElement<algorithm>::scheduleTask(Step step,
         }
 
         const bool doEnergy = step == energyReductionStep_;
-        int        flags =
-                (needGlobalReduction ? CGLO_GSTAT : 0) | (doEnergy ? CGLO_ENERGY : 0)
-                | (needComReduction ? CGLO_STOPCM : 0) | CGLO_TEMPERATURE | CGLO_PRESSURE | CGLO_CONSTRAINT
-                | (shouldCheckNumberOfBondedInteractions_ ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS : 0);
+        int        flags    = (needGlobalReduction ? CGLO_GSTAT : 0) | (doEnergy ? CGLO_ENERGY : 0)
+                    | (needComReduction ? CGLO_STOPCM : 0) | CGLO_TEMPERATURE | CGLO_PRESSURE
+                    | CGLO_CONSTRAINT;
 
         // Since we're already communicating at this step, we
         // can propagate intra-simulation signals. Note that
@@ -220,60 +205,66 @@ void ComputeGlobalsElement<algorithm>::scheduleTask(Step step,
         auto signaller = std::make_shared<SimulationSignaller>(signals_, cr_, nullptr,
                                                                doInterSimSignal, doIntraSimSignal);
 
-        (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-                [this, step, flags, signaller = std::move(signaller)]() {
-                    compute(step, flags, signaller.get(), true);
-                }));
+        registerRunFunction([this, step, flags, signaller = std::move(signaller)]() {
+            compute(step, flags, signaller.get(), true);
+        });
     }
-    else if (algorithm == ComputeGlobalsAlgorithm::VelocityVerletAtFullTimeStep)
+    else if (algorithm == ComputeGlobalsAlgorithm::VelocityVerlet)
     {
-        // For vv, the state at the beginning of the step is positions at time t, velocities at time t - dt/2
-        // The first velocity propagation (+dt/2) therefore actually corresponds to the previous step.
-        // So we need information from the last step in the first half of the integration
-        if (!needGlobalReduction && !do_per_step(step - 1, nstglobalcomm_))
+        // For VV, we schedule two calls to compute globals per step.
+        if (step != vvSchedulingStep_)
         {
-            return;
-        }
+            // This is the first scheduling call for this step (positions & velocities at full time
+            // step) Set this as the current scheduling step
+            vvSchedulingStep_ = step;
+
+            // For vv, the state at the beginning of the step is positions at time t, velocities at time t - dt/2
+            // The first velocity propagation (+dt/2) therefore actually corresponds to the previous step.
+            // So we need information from the last step in the first half of the integration
+            if (!needGlobalReduction && !do_per_step(step - 1, nstglobalcomm_))
+            {
+                return;
+            }
 
-        const bool doTemperature = step != initStep_ || inputrec_->bContinuation;
-        const bool doEnergy      = step == energyReductionStep_;
+            const bool doTemperature = step != initStep_ || inputrec_->bContinuation;
+            const bool doEnergy      = step == energyReductionStep_;
 
-        int flags = (needGlobalReduction ? CGLO_GSTAT : 0) | (doEnergy ? CGLO_ENERGY : 0)
-                    | (doTemperature ? CGLO_TEMPERATURE : 0) | CGLO_PRESSURE | CGLO_CONSTRAINT
-                    | (needComReduction ? CGLO_STOPCM : 0)
-                    | (shouldCheckNumberOfBondedInteractions_ ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS : 0)
-                    | CGLO_SCALEEKIN;
+            int flags = (needGlobalReduction ? CGLO_GSTAT : 0) | (doEnergy ? CGLO_ENERGY : 0)
+                        | (doTemperature ? CGLO_TEMPERATURE : 0) | CGLO_PRESSURE | CGLO_CONSTRAINT
+                        | (needComReduction ? CGLO_STOPCM : 0) | CGLO_SCALEEKIN;
 
-        (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-                [this, step, flags]() { compute(step, flags, nullSignaller_.get(), false); }));
-    }
-    else if (algorithm == ComputeGlobalsAlgorithm::VelocityVerletAfterCoordinateUpdate)
-    {
-        // second call to compute_globals for this step
-        if (!needGlobalReduction)
-        {
-            return;
+            registerRunFunction(
+                    [this, step, flags]() { compute(step, flags, nullSignaller_.get(), false); });
         }
-        int flags = CGLO_GSTAT | CGLO_CONSTRAINT
-                    | (shouldCheckNumberOfBondedInteractions_ ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS
-                                                              : 0);
-
-        // Since we're already communicating at this step, we
-        // can propagate intra-simulation signals. Note that
-        // check_nstglobalcomm has the responsibility for
-        // choosing the value of nstglobalcomm which satisfies
-        // the need of the different signallers.
-        const bool doIntraSimSignal = true;
-        // Disable functionality
-        const bool doInterSimSignal = false;
-
-        auto signaller = std::make_shared<SimulationSignaller>(signals_, cr_, nullptr,
-                                                               doInterSimSignal, doIntraSimSignal);
+        else
+        {
+            // second call to compute_globals for this step
+            // Reset the scheduling step to avoid confusion if scheduling needs
+            // to be repeated (in case of unexpected simulation termination)
+            vvSchedulingStep_ = -1;
 
-        (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-                [this, step, flags, signaller = std::move(signaller)]() {
-                    compute(step, flags, signaller.get(), true);
-                }));
+            if (!needGlobalReduction)
+            {
+                return;
+            }
+            int flags = CGLO_GSTAT | CGLO_CONSTRAINT;
+
+            // Since we're already communicating at this step, we
+            // can propagate intra-simulation signals. Note that
+            // check_nstglobalcomm has the responsibility for
+            // choosing the value of nstglobalcomm which satisfies
+            // the need of the different signallers.
+            const bool doIntraSimSignal = true;
+            // Disable functionality
+            const bool doInterSimSignal = false;
+
+            auto signaller = std::make_shared<SimulationSignaller>(
+                    signals_, cr_, nullptr, doInterSimSignal, doIntraSimSignal);
+
+            registerRunFunction([this, step, flags, signaller = std::move(signaller)]() {
+                compute(step, flags, signaller.get(), true);
+            });
+        }
     }
 }
 
@@ -284,22 +275,19 @@ void ComputeGlobalsElement<algorithm>::compute(gmx::Step            step,
                                                bool                 useLastBox,
                                                bool                 isInit)
 {
-    auto x       = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
-    auto v       = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
+    auto x       = statePropagatorData_->positionsView().unpaddedArrayRef();
+    auto v       = statePropagatorData_->velocitiesView().unpaddedArrayRef();
     auto box     = statePropagatorData_->constBox();
     auto lastbox = useLastBox ? statePropagatorData_->constPreviousBox()
                               : statePropagatorData_->constBox();
 
-    const real vdwLambda = freeEnergyPerturbationElement_
-                                   ? freeEnergyPerturbationElement_->constLambdaView()[efptVDW]
-                                   : 0;
-
-    compute_globals(gstat_, cr_, inputrec_, fr_, energyElement_->ekindata(), x, v, box, vdwLambda,
-                    mdAtoms_->mdatoms(), nrnb_, &vcm_, step != -1 ? wcycle_ : nullptr,
-                    energyElement_->enerdata(), energyElement_->forceVirial(step),
-                    energyElement_->constraintVirial(step), energyElement_->totalVirial(step),
-                    energyElement_->pressure(step), energyElement_->muTot(), constr_, signaller, lastbox,
-                    &totalNumberOfBondedInteractions_, energyElement_->needToSumEkinhOld(), flags);
+    compute_globals(
+            gstat_, cr_, inputrec_, fr_, energyData_->ekindata(), x, v, box, mdAtoms_->mdatoms(),
+            nrnb_, &vcm_, step != -1 ? wcycle_ : nullptr, energyData_->enerdata(),
+            energyData_->forceVirial(step), energyData_->constraintVirial(step),
+            energyData_->totalVirial(step), energyData_->pressure(step), constr_, signaller,
+            lastbox, &totalNumberOfBondedInteractions_, energyData_->needToSumEkinhOld(),
+            flags | (shouldCheckNumberOfBondedInteractions_ ? CGLO_CHECK_NUMBER_OF_BONDED_INTERACTIONS : 0));
     checkNumberOfBondedInteractions(mdlog_, cr_, totalNumberOfBondedInteractions_, top_global_,
                                     localTopology_, x, box, &shouldCheckNumberOfBondedInteractions_);
     if (flags & CGLO_STOPCM && !isInit)
@@ -310,10 +298,9 @@ void ComputeGlobalsElement<algorithm>::compute(gmx::Step            step,
 }
 
 template<ComputeGlobalsAlgorithm algorithm>
-CheckBondedInteractionsCallbackPtr ComputeGlobalsElement<algorithm>::getCheckNumberOfBondedInteractionsCallback()
+CheckBondedInteractionsCallback ComputeGlobalsElement<algorithm>::getCheckNumberOfBondedInteractionsCallback()
 {
-    return std::make_unique<CheckBondedInteractionsCallback>(
-            [this]() { needToCheckNumberOfBondedInteractions(); });
+    return [this]() { needToCheckNumberOfBondedInteractions(); };
 }
 
 template<ComputeGlobalsAlgorithm algorithm>
@@ -329,36 +316,92 @@ void ComputeGlobalsElement<algorithm>::setTopology(const gmx_localtop_t* top)
 }
 
 template<ComputeGlobalsAlgorithm algorithm>
-SignallerCallbackPtr ComputeGlobalsElement<algorithm>::registerEnergyCallback(EnergySignallerEvent event)
+std::optional<SignallerCallback> ComputeGlobalsElement<algorithm>::registerEnergyCallback(EnergySignallerEvent event)
 {
     if (event == EnergySignallerEvent::EnergyCalculationStep)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { energyReductionStep_ = step; });
+        return [this](Step step, Time /*unused*/) { energyReductionStep_ = step; };
     }
     if (event == EnergySignallerEvent::VirialCalculationStep)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { virialReductionStep_ = step; });
+        return [this](Step step, Time /*unused*/) { virialReductionStep_ = step; };
     }
-    return nullptr;
+    return std::nullopt;
 }
 
 template<ComputeGlobalsAlgorithm algorithm>
-SignallerCallbackPtr ComputeGlobalsElement<algorithm>::registerTrajectorySignallerCallback(TrajectoryEvent event)
+std::optional<SignallerCallback>
+ComputeGlobalsElement<algorithm>::registerTrajectorySignallerCallback(TrajectoryEvent event)
 {
     if (event == TrajectoryEvent::EnergyWritingStep)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { energyReductionStep_ = step; });
+        return [this](Step step, Time /*unused*/) { energyReductionStep_ = step; };
     }
-    return nullptr;
+    return std::nullopt;
 }
 
 //! Explicit template instantiation
-//! @{
+//! \{
 template class ComputeGlobalsElement<ComputeGlobalsAlgorithm::LeapFrog>;
-template class ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerletAtFullTimeStep>;
-template class ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerletAfterCoordinateUpdate>;
-//! @}
+template class ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerlet>;
+//! \}
+
+template<>
+ISimulatorElement* ComputeGlobalsElement<ComputeGlobalsAlgorithm::LeapFrog>::getElementPointerImpl(
+        LegacySimulatorData*                    legacySimulatorData,
+        ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+        StatePropagatorData*                    statePropagatorData,
+        EnergyData*                             energyData,
+        FreeEnergyPerturbationData*             freeEnergyPerturbationData,
+        GlobalCommunicationHelper*              globalCommunicationHelper)
+{
+    auto* element = builderHelper->storeElement(
+            std::make_unique<ComputeGlobalsElement<ComputeGlobalsAlgorithm::LeapFrog>>(
+                    statePropagatorData, energyData, freeEnergyPerturbationData,
+                    globalCommunicationHelper->simulationSignals(),
+                    globalCommunicationHelper->nstglobalcomm(), legacySimulatorData->fplog,
+                    legacySimulatorData->mdlog, legacySimulatorData->cr,
+                    legacySimulatorData->inputrec, legacySimulatorData->mdAtoms,
+                    legacySimulatorData->nrnb, legacySimulatorData->wcycle, legacySimulatorData->fr,
+                    legacySimulatorData->top_global, legacySimulatorData->constr));
+
+    // TODO: Remove this when DD can reduce bonded interactions independently (#3421)
+    auto* castedElement = static_cast<ComputeGlobalsElement<ComputeGlobalsAlgorithm::LeapFrog>*>(element);
+    globalCommunicationHelper->setCheckBondedInteractionsCallback(
+            castedElement->getCheckNumberOfBondedInteractionsCallback());
+
+    return element;
+}
+
+template<>
+ISimulatorElement* ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerlet>::getElementPointerImpl(
+        LegacySimulatorData*                    simulator,
+        ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+        StatePropagatorData*                    statePropagatorData,
+        EnergyData*                             energyData,
+        FreeEnergyPerturbationData*             freeEnergyPerturbationData,
+        GlobalCommunicationHelper*              globalCommunicationHelper)
+{
+    // We allow this element to be added multiple times to the call list, but we only want one
+    // actual element built
+    static thread_local ISimulatorElement* vvComputeGlobalsElement = nullptr;
+    if (!builderHelper->elementIsStored(vvComputeGlobalsElement))
+    {
+        vvComputeGlobalsElement = builderHelper->storeElement(
+                std::make_unique<ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerlet>>(
+                        statePropagatorData, energyData, freeEnergyPerturbationData,
+                        globalCommunicationHelper->simulationSignals(),
+                        globalCommunicationHelper->nstglobalcomm(), simulator->fplog, simulator->mdlog,
+                        simulator->cr, simulator->inputrec, simulator->mdAtoms, simulator->nrnb,
+                        simulator->wcycle, simulator->fr, simulator->top_global, simulator->constr));
+
+        // TODO: Remove this when DD can reduce bonded interactions independently (#3421)
+        auto* castedElement =
+                static_cast<ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerlet>*>(
+                        vvComputeGlobalsElement);
+        globalCommunicationHelper->setCheckBondedInteractionsCallback(
+                castedElement->getCheckNumberOfBondedInteractionsCallback());
+    }
+    return vvComputeGlobalsElement;
+}
 } // namespace gmx
index 85d0aba1ff0aea57585c5fffc8e9f35944f92f9d..28f0dbb5c9d099a657831a626d53c0b25aa5b420 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the global reduction element for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_COMPUTEGLOBALSELEMENT_H
@@ -45,7 +47,7 @@
 #include "gromacs/mdlib/simulationsignal.h"
 #include "gromacs/mdlib/vcm.h"
 
-#include "energyelement.h"
+#include "energydata.h"
 #include "modularsimulatorinterfaces.h"
 #include "statepropagatordata.h"
 #include "topologyholder.h"
@@ -56,7 +58,8 @@ struct t_nrnb;
 
 namespace gmx
 {
-class FreeEnergyPerturbationElement;
+class FreeEnergyPerturbationData;
+class LegacySimulatorData;
 class MDAtoms;
 class MDLogger;
 
@@ -67,16 +70,13 @@ class MDLogger;
 enum class ComputeGlobalsAlgorithm
 {
     LeapFrog,
-    VelocityVerletAtFullTimeStep,
-    VelocityVerletAfterCoordinateUpdate
+    VelocityVerlet
 };
 
 //! The function type allowing to request a check of the number of bonded interactions
 typedef std::function<void()> CheckBondedInteractionsCallback;
-//! Pointer to the function type allowing to request a check of the number of bonded interactions
-typedef std::unique_ptr<CheckBondedInteractionsCallback> CheckBondedInteractionsCallbackPtr;
 
-/*! \libinternal
+/*! \internal
  * \brief Encapsulate the calls to `compute_globals`
  *
  * This element aims at offering an interface to the legacy
@@ -94,7 +94,7 @@ typedef std::unique_ptr<CheckBondedInteractionsCallback> CheckBondedInteractions
  * constraint virial after the second propagation of velocities (+dt/2) and of
  * the positions (+dt).
  *
- * @tparam algorithm  The global reduction scheme
+ * \tparam algorithm  The global reduction scheme
  */
 template<ComputeGlobalsAlgorithm algorithm>
 class ComputeGlobalsElement final :
@@ -105,22 +105,21 @@ class ComputeGlobalsElement final :
 {
 public:
     //! Constructor
-    ComputeGlobalsElement(StatePropagatorData*           statePropagatorData,
-                          EnergyElement*                 energyElement,
-                          FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                          SimulationSignals*             signals,
-                          int                            nstglobalcomm,
-                          FILE*                          fplog,
-                          const MDLogger&                mdlog,
-                          t_commrec*                     cr,
-                          t_inputrec*                    inputrec,
-                          const MDAtoms*                 mdAtoms,
-                          t_nrnb*                        nrnb,
-                          gmx_wallcycle*                 wcycle,
-                          t_forcerec*                    fr,
-                          const gmx_mtop_t*              global_top,
-                          Constraints*                   constr,
-                          bool                           hasReadEkinState);
+    ComputeGlobalsElement(StatePropagatorData*        statePropagatorData,
+                          EnergyData*                 energyData,
+                          FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                          SimulationSignals*          signals,
+                          int                         nstglobalcomm,
+                          FILE*                       fplog,
+                          const MDLogger&             mdlog,
+                          t_commrec*                  cr,
+                          const t_inputrec*           inputrec,
+                          const MDAtoms*              mdAtoms,
+                          t_nrnb*                     nrnb,
+                          gmx_wallcycle*              wcycle,
+                          t_forcerec*                 fr,
+                          const gmx_mtop_t*           global_top,
+                          Constraints*                constr);
 
     //! Destructor
     ~ComputeGlobalsElement() override;
@@ -134,25 +133,43 @@ public:
      *
      * This registers the call to compute_globals when needed.
      *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
      */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
 
     //! Get callback to request checking of bonded interactions
-    CheckBondedInteractionsCallbackPtr getCheckNumberOfBondedInteractionsCallback();
+    CheckBondedInteractionsCallback getCheckNumberOfBondedInteractionsCallback();
 
     //! No element teardown needed
     void elementTeardown() override {}
 
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper);
+
 private:
     //! ITopologyClient implementation
     void setTopology(const gmx_localtop_t* top) override;
     //! IEnergySignallerClient implementation
-    SignallerCallbackPtr registerEnergyCallback(EnergySignallerEvent event) override;
+    std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent event) override;
     //! ITrajectorySignallerClient implementation
-    SignallerCallbackPtr registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
     //! The compute_globals call
     void compute(Step step, unsigned int flags, SimulationSignaller* signaller, bool useLastBox, bool isInit = false);
 
@@ -161,6 +178,9 @@ private:
     //! Next step at which virial needs to be reduced
     Step virialReductionStep_;
 
+    //! For VV only, we need to schedule twice per step. This keeps track of the scheduling stage.
+    Step vvSchedulingStep_;
+
     //! Whether center of mass motion stopping is enabled
     const bool doStopCM_;
     //! Number of steps after which center of mass motion is removed
@@ -173,8 +193,6 @@ private:
     const Step initStep_;
     //! A dummy signaller (used for setup and VV)
     std::unique_ptr<SimulationSignaller> nullSignaller_;
-    //! Whether we read kinetic energy from checkpoint
-    const bool hasReadEkinState_;
 
     /*! \brief Check that DD doesn't miss bonded interactions
      *
@@ -185,10 +203,10 @@ private:
      * after a new DD is made. These variables handle whether the
      * check happens, and the result it returns.
      */
-    //! @{
+    //! \{
     int  totalNumberOfBondedInteractions_;
     bool shouldCheckNumberOfBondedInteractions_;
-    //! @}
+    //! \}
 
     /*! \brief Signal to ComputeGlobalsElement that it should check for DD errors
      *
@@ -206,14 +224,15 @@ private:
     //! Global reduction struct
     gmx_global_stat* gstat_;
 
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
     //! Pointer to the microstate
     StatePropagatorData* statePropagatorData_;
-    //! Pointer to the energy element (needed for the tensors and mu_tot)
-    EnergyElement* energyElement_;
+    //! Pointer to the energy data (needed for the tensors and mu_tot)
+    EnergyData* energyData_;
     //! Pointer to the local topology (only needed for checkNumberOfBondedInteractions)
     const gmx_localtop_t* localTopology_;
-    //! Pointer to the free energy perturbation element
-    FreeEnergyPerturbationElement* freeEnergyPerturbationElement_;
+    //! Pointer to the free energy perturbation data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
 
     //! Center of mass motion removal
     t_vcm vcm_;
@@ -228,7 +247,7 @@ private:
     //! Handles communication.
     t_commrec* cr_;
     //! Contains user input mdp options.
-    t_inputrec* inputrec_;
+    const t_inputrec* inputrec_;
     //! Full system topology - only needed for checkNumberOfBondedInteractions.
     const gmx_mtop_t* top_global_;
     //! Atom parameters for this domain.
index 8e96e6068b19e7ddc336c044c31a509c7b31ffcc..8519279a8e8d349ba215327c0c3d142b9fc18f35 100644 (file)
 #include "constraintelement.h"
 
 #include "gromacs/math/vec.h"
+#include "gromacs/mdlib/mdatoms.h"
+#include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/utility/fatalerror.h"
 
-#include "energyelement.h"
-#include "freeenergyperturbationelement.h"
+#include "energydata.h"
+#include "freeenergyperturbationdata.h"
+#include "modularsimulator.h"
+#include "simulatoralgorithm.h"
 #include "statepropagatordata.h"
 
 namespace gmx
 {
 template<ConstraintVariable variable>
-ConstraintsElement<variable>::ConstraintsElement(Constraints*                   constr,
-                                                 StatePropagatorData*           statePropagatorData,
-                                                 EnergyElement*                 energyElement,
-                                                 FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                                                 bool                           isMaster,
-                                                 FILE*                          fplog,
-                                                 const t_inputrec*              inputrec,
-                                                 const t_mdatoms*               mdAtoms) :
+ConstraintsElement<variable>::ConstraintsElement(Constraints*                constr,
+                                                 StatePropagatorData*        statePropagatorData,
+                                                 EnergyData*                 energyData,
+                                                 FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                 bool                        isMaster,
+                                                 FILE*                       fplog,
+                                                 const t_inputrec*           inputrec,
+                                                 const t_mdatoms*            mdAtoms) :
     nextVirialCalculationStep_(-1),
     nextEnergyWritingStep_(-1),
     nextLogWritingStep_(-1),
     isMasterRank_(isMaster),
     statePropagatorData_(statePropagatorData),
-    energyElement_(energyElement),
-    freeEnergyPerturbationElement_(freeEnergyPerturbationElement),
+    energyData_(energyData),
+    freeEnergyPerturbationData_(freeEnergyPerturbationData),
     constr_(constr),
     fplog_(fplog),
     inputrec_(inputrec),
@@ -86,13 +91,14 @@ void ConstraintsElement<variable>::elementSetup()
         && ((variable == ConstraintVariable::Positions && inputrec_->eI == eiMD)
             || (variable == ConstraintVariable::Velocities && inputrec_->eI == eiVV)))
     {
-        const real lambdaBonded = freeEnergyPerturbationElement_
-                                          ? freeEnergyPerturbationElement_->constLambdaView()[efptBONDED]
+        const real lambdaBonded = freeEnergyPerturbationData_
+                                          ? freeEnergyPerturbationData_->constLambdaView()[efptBONDED]
                                           : 0;
         // Constrain the initial coordinates and velocities
-        do_constrain_first(fplog_, constr_, inputrec_, mdAtoms_, statePropagatorData_->localNumAtoms(),
-                           statePropagatorData_->positionsView(), statePropagatorData_->velocitiesView(),
-                           statePropagatorData_->box(), lambdaBonded);
+        do_constrain_first(
+                fplog_, constr_, inputrec_, statePropagatorData_->totalNumAtoms(),
+                statePropagatorData_->localNumAtoms(), statePropagatorData_->positionsView(),
+                statePropagatorData_->velocitiesView(), statePropagatorData_->box(), lambdaBonded);
 
         if (isMasterRank_)
         {
@@ -107,18 +113,17 @@ void ConstraintsElement<variable>::elementSetup()
 
 template<ConstraintVariable variable>
 void ConstraintsElement<variable>::scheduleTask(Step step,
-                                                Time gmx_unused               time,
-                                                const RegisterRunFunctionPtr& registerRunFunction)
+                                                Time gmx_unused            time,
+                                                const RegisterRunFunction& registerRunFunction)
 {
     bool calculateVirial = (step == nextVirialCalculationStep_);
     bool writeLog        = (step == nextLogWritingStep_);
     bool writeEnergy     = (step == nextEnergyWritingStep_);
 
     // register constraining
-    (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-            [this, step, calculateVirial, writeLog, writeEnergy]() {
-                apply(step, calculateVirial, writeLog, writeEnergy);
-            }));
+    registerRunFunction([this, step, calculateVirial, writeLog, writeEnergy]() {
+        apply(step, calculateVirial, writeLog, writeEnergy);
+    });
 }
 
 template<ConstraintVariable variable>
@@ -126,32 +131,32 @@ void ConstraintsElement<variable>::apply(Step step, bool calculateVirial, bool w
 {
     tensor vir_con;
 
-    rvec *x, *xprime, *min_proj, *v;
+    ArrayRefWithPadding<RVec> x;
+    ArrayRefWithPadding<RVec> xprime;
+    ArrayRef<RVec>            min_proj;
+    ArrayRefWithPadding<RVec> v;
 
-    const real lambdaBonded = freeEnergyPerturbationElement_
-                                      ? freeEnergyPerturbationElement_->constLambdaView()[efptBONDED]
-                                      : 0;
+    const real lambdaBonded =
+            freeEnergyPerturbationData_ ? freeEnergyPerturbationData_->constLambdaView()[efptBONDED] : 0;
     real dvdlambda = 0;
 
     switch (variable)
     {
         case ConstraintVariable::Positions:
-            x = as_rvec_array(statePropagatorData_->previousPositionsView().paddedArrayRef().data());
-            xprime   = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
-            min_proj = nullptr;
-            v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
+            x      = statePropagatorData_->previousPositionsView();
+            xprime = statePropagatorData_->positionsView();
+            v      = statePropagatorData_->velocitiesView();
             break;
         case ConstraintVariable::Velocities:
-            x      = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
-            xprime = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
-            min_proj = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
-            v        = nullptr;
+            x        = statePropagatorData_->positionsView();
+            xprime   = statePropagatorData_->velocitiesView();
+            min_proj = statePropagatorData_->velocitiesView().unpaddedArrayRef();
             break;
         default: gmx_fatal(FARGS, "Constraint algorithm not implemented for modular simulator.");
     }
 
     constr_->apply(writeLog, writeEnergy, step, 1, 1.0, x, xprime, min_proj, statePropagatorData_->box(),
-                   lambdaBonded, &dvdlambda, v, calculateVirial ? &vir_con : nullptr, variable);
+                   lambdaBonded, &dvdlambda, v, calculateVirial, vir_con, variable);
 
     if (calculateVirial)
     {
@@ -160,9 +165,9 @@ void ConstraintsElement<variable>::apply(Step step, bool calculateVirial, bool w
             // For some reason, the shake virial in VV is reset twice a step.
             // Energy element will only do this once per step.
             // TODO: Investigate this
-            clear_mat(energyElement_->constraintVirial(step));
+            clear_mat(energyData_->constraintVirial(step));
         }
-        energyElement_->addToConstraintVirial(vir_con, step);
+        energyData_->addToConstraintVirial(vir_con, step);
     }
 
     /* The factor of 2 correction is necessary because half of the constraint
@@ -170,45 +175,55 @@ void ConstraintsElement<variable>::apply(Step step, bool calculateVirial, bool w
      * good approximation, statistically insignificant in any real free energy
      * calculation. Any possible error is not a simulation propagation error,
      * but a potential reporting error in the data that goes to dh/dlambda.
-     * Cf. redmine issue #1255
+     * Cf. Issue #1255
      */
     const real c_dvdlConstraintCorrectionFactor = EI_VV(inputrec_->eI) ? 2.0 : 1.0;
-    energyElement_->enerdata()->term[F_DVDL_CONSTR] += c_dvdlConstraintCorrectionFactor * dvdlambda;
+    energyData_->enerdata()->term[F_DVDL_CONSTR] += c_dvdlConstraintCorrectionFactor * dvdlambda;
 }
 
 template<ConstraintVariable variable>
-SignallerCallbackPtr ConstraintsElement<variable>::registerEnergyCallback(EnergySignallerEvent event)
+std::optional<SignallerCallback> ConstraintsElement<variable>::registerEnergyCallback(EnergySignallerEvent event)
 {
     if (event == EnergySignallerEvent::VirialCalculationStep)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { nextVirialCalculationStep_ = step; });
+        return [this](Step step, Time /*unused*/) { nextVirialCalculationStep_ = step; };
     }
-    return nullptr;
+    return std::nullopt;
 }
 
 template<ConstraintVariable variable>
-SignallerCallbackPtr ConstraintsElement<variable>::registerTrajectorySignallerCallback(TrajectoryEvent event)
+std::optional<SignallerCallback> ConstraintsElement<variable>::registerTrajectorySignallerCallback(TrajectoryEvent event)
 {
     if (event == TrajectoryEvent::EnergyWritingStep)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { nextEnergyWritingStep_ = step; });
+        return [this](Step step, Time /*unused*/) { nextEnergyWritingStep_ = step; };
     }
-    return nullptr;
+    return std::nullopt;
 }
 
 template<ConstraintVariable variable>
-SignallerCallbackPtr ConstraintsElement<variable>::registerLoggingCallback()
+std::optional<SignallerCallback> ConstraintsElement<variable>::registerLoggingCallback()
 {
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time /*unused*/) { nextLogWritingStep_ = step; });
+    return [this](Step step, Time /*unused*/) { nextLogWritingStep_ = step; };
 }
 
-//! Explicit template initialization
-//! @{
+template<ConstraintVariable variable>
+ISimulatorElement* ConstraintsElement<variable>::getElementPointerImpl(
+        LegacySimulatorData*                    legacySimulatorData,
+        ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+        StatePropagatorData*                    statePropagatorData,
+        EnergyData*                             energyData,
+        FreeEnergyPerturbationData*             freeEnergyPerturbationData,
+        GlobalCommunicationHelper gmx_unused* globalCommunicationHelper)
+{
+    return builderHelper->storeElement(std::make_unique<ConstraintsElement<variable>>(
+            legacySimulatorData->constr, statePropagatorData, energyData,
+            freeEnergyPerturbationData, MASTER(legacySimulatorData->cr), legacySimulatorData->fplog,
+            legacySimulatorData->inputrec, legacySimulatorData->mdAtoms->mdatoms()));
+}
+
+// Explicit template initializations
 template class ConstraintsElement<ConstraintVariable::Positions>;
 template class ConstraintsElement<ConstraintVariable::Velocities>;
-//! @}
 
 } // namespace gmx
index 2b754d33c2edf9f7b31f20c4173f7ee6242df922..d5564ba1b5277000c0f71561b88458cb567c2246 100644 (file)
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the constraint element for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_CONSTRAINTELEMENT_H
 namespace gmx
 {
 class Constraints;
-class EnergyElement;
-class FreeEnergyPerturbationElement;
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class ModularSimulatorAlgorithmBuilderHelper;
 class StatePropagatorData;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Constraints element
  *
@@ -62,7 +67,7 @@ class StatePropagatorData;
  * but uses the current constraints implementation and the data management
  * introduced with the modular simulator.
  *
- * @tparam variable  The constraining variable
+ * \tparam variable  The constraining variable
  */
 template<ConstraintVariable variable>
 class ConstraintsElement final :
@@ -73,14 +78,14 @@ class ConstraintsElement final :
 {
 public:
     //! Constructor
-    ConstraintsElement(Constraints*                   constr,
-                       StatePropagatorData*           statePropagatorData,
-                       EnergyElement*                 energyElement,
-                       FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                       bool                           isMaster,
-                       FILE*                          fplog,
-                       const t_inputrec*              inputrec,
-                       const t_mdatoms*               mdAtoms);
+    ConstraintsElement(Constraints*                constr,
+                       StatePropagatorData*        statePropagatorData,
+                       EnergyData*                 energyData,
+                       FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                       bool                        isMaster,
+                       FILE*                       fplog,
+                       const t_inputrec*           inputrec,
+                       const t_mdatoms*            mdAtoms);
 
     /*! \brief Register constraining function for step / time
      *
@@ -88,11 +93,11 @@ public:
      * Under VV, this is expected to be run twice, once contraining velocities only,
      * a second time constraining positions and velocities.
      *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
      */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
 
     /*! \brief Performs inital constraining
      *  \todo Should this rather happen at grompp time? Right position of this operation is currently
@@ -103,16 +108,34 @@ public:
     //! No element teardown needed
     void elementTeardown() override {}
 
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper);
+
 private:
     //! The actual constraining computation
     void apply(Step step, bool calculateVirial, bool writeLog, bool writeEnergy);
 
     //! IEnergySignallerClient implementation
-    SignallerCallbackPtr registerEnergyCallback(EnergySignallerEvent event) override;
+    std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent event) override;
     //! ITrajectorySignallerClient implementation
-    SignallerCallbackPtr registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
     //! ILoggingSignallerClient implementation
-    SignallerCallbackPtr registerLoggingCallback() override;
+    std::optional<SignallerCallback> registerLoggingCallback() override;
 
     //! The next energy calculation step
     Step nextVirialCalculationStep_;
@@ -124,12 +147,13 @@ private:
     //! Whether we're master rank
     const bool isMasterRank_;
 
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
     //! Pointer to the micro state
     StatePropagatorData* statePropagatorData_;
-    //! Pointer to the energy element
-    EnergyElement* energyElement_;
-    //! Pointer to the free energy perturbation element
-    FreeEnergyPerturbationElement* freeEnergyPerturbationElement_;
+    //! Pointer to the energy data
+    EnergyData* energyData_;
+    //! Pointer to the free energy perturbation data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
 
     // Access to ISimulator data
     //! Handles constraints.
index d256c59967970f6f7539d0f363f0d8717f994eb5..9128119bfebf7724131de5b222fc4810b6937a7d 100644 (file)
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/pbcutil/pbc.h"
 
-#include "freeenergyperturbationelement.h"
+#include "freeenergyperturbationdata.h"
 #include "statepropagatordata.h"
 #include "topologyholder.h"
 
 namespace gmx
 {
-DomDecHelper::DomDecHelper(bool                               isVerbose,
-                           int                                verbosePrintInterval,
-                           StatePropagatorData*               statePropagatorData,
-                           FreeEnergyPerturbationElement*     freeEnergyPerturbationElement,
-                           TopologyHolder*                    topologyHolder,
-                           CheckBondedInteractionsCallbackPtr checkBondedInteractionsCallback,
-                           int                                nstglobalcomm,
-                           FILE*                              fplog,
-                           t_commrec*                         cr,
-                           const MDLogger&                    mdlog,
-                           Constraints*                       constr,
-                           t_inputrec*                        inputrec,
-                           MDAtoms*                           mdAtoms,
-                           t_nrnb*                            nrnb,
-                           gmx_wallcycle*                     wcycle,
-                           t_forcerec*                        fr,
-                           gmx_vsite_t*                       vsite,
-                           ImdSession*                        imdSession,
-                           pull_t*                            pull_work) :
+DomDecHelper::DomDecHelper(bool                            isVerbose,
+                           int                             verbosePrintInterval,
+                           StatePropagatorData*            statePropagatorData,
+                           FreeEnergyPerturbationData*     freeEnergyPerturbationData,
+                           TopologyHolder*                 topologyHolder,
+                           CheckBondedInteractionsCallback checkBondedInteractionsCallback,
+                           int                             nstglobalcomm,
+                           FILE*                           fplog,
+                           t_commrec*                      cr,
+                           const MDLogger&                 mdlog,
+                           Constraints*                    constr,
+                           t_inputrec*                     inputrec,
+                           MDAtoms*                        mdAtoms,
+                           t_nrnb*                         nrnb,
+                           gmx_wallcycle*                  wcycle,
+                           t_forcerec*                     fr,
+                           VirtualSitesHandler*            vsite,
+                           ImdSession*                     imdSession,
+                           pull_t*                         pull_work) :
     nextNSStep_(-1),
     isVerbose_(isVerbose),
     verbosePrintInterval_(verbosePrintInterval),
     nstglobalcomm_(nstglobalcomm),
     statePropagatorData_(statePropagatorData),
-    freeEnergyPerturbationElement_(freeEnergyPerturbationElement),
+    freeEnergyPerturbationData_(freeEnergyPerturbationData),
     topologyHolder_(topologyHolder),
     checkBondedInteractionsCallback_(std::move(checkBondedInteractionsCallback)),
     fplog_(fplog),
@@ -130,8 +130,7 @@ void DomDecHelper::run(Step step, Time gmx_unused time)
     {
         // TODO: Correcting the box is done here (if using DD) or in ForceElement (non-DD simulations).
         //       Think about unifying this responsibility, could this be done in one place?
-        t_graph* graph = nullptr;
-        if (correct_box(fplog_, step, localState->box, graph))
+        if (correct_box(fplog_, step, localState->box))
         {
             isMasterState = true;
         }
@@ -152,7 +151,7 @@ void DomDecHelper::partitionSystem(bool                     verbose,
                                    std::unique_ptr<t_state> localState,
                                    t_state*                 globalState)
 {
-    PaddedHostVector<RVec>* forcePointer = statePropagatorData_->forcePointer();
+    ForceBuffers* forcePointer = statePropagatorData_->forcePointer();
 
     // Distribute the charge groups over the nodes from the master node
     dd_partition_system(fplog_, mdlog_, inputrec_->init_step, cr_, isMasterState, nstglobalcomm,
@@ -161,18 +160,17 @@ void DomDecHelper::partitionSystem(bool                     verbose,
                         topologyHolder_->localTopology_.get(), fr_, vsite_, constr_, nrnb_, wcycle,
                         verbose);
     topologyHolder_->updateLocalTopology();
-    (*checkBondedInteractionsCallback_)();
+    checkBondedInteractionsCallback_();
     statePropagatorData_->setLocalState(std::move(localState));
-    if (freeEnergyPerturbationElement_)
+    if (freeEnergyPerturbationData_)
     {
-        freeEnergyPerturbationElement_->updateMDAtoms();
+        freeEnergyPerturbationData_->updateMDAtoms();
     }
 }
 
-SignallerCallbackPtr DomDecHelper::registerNSCallback()
+std::optional<SignallerCallback> DomDecHelper::registerNSCallback()
 {
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { this->nextNSStep_ = step; });
+    return [this](Step step, Time gmx_unused time) { this->nextNSStep_ = step; };
 }
 
 } // namespace gmx
index 89c3d5ccd0166d12b413b14be6b7e10258f15727..2b6691c1578a199618059eb3e67c90f22f3be9c2 100644 (file)
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the domain decomposition helper for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_DOMDECHELPER_H
@@ -45,7 +47,6 @@
 #include "modularsimulatorinterfaces.h"
 
 struct gmx_localtop_t;
-struct gmx_vsite_t;
 struct gmx_wallcycle;
 struct pull_t;
 struct t_commrec;
@@ -56,22 +57,21 @@ struct t_nrnb;
 namespace gmx
 {
 class Constraints;
-class FreeEnergyPerturbationElement;
+class FreeEnergyPerturbationData;
 class ImdSession;
 class MDAtoms;
 class MDLogger;
 class StatePropagatorData;
 class TopologyHolder;
+class VirtualSitesHandler;
 
 //! \addtogroup module_modularsimulator
 //! \{
 
 //! The function type allowing to request a check of the number of bonded interactions
 typedef std::function<void()> CheckBondedInteractionsCallback;
-//! Pointer to the function type allowing to request a check of the number of bonded interactions
-typedef std::unique_ptr<CheckBondedInteractionsCallback> CheckBondedInteractionsCallbackPtr;
 
-/*! \libinternal
+/*! \internal
  * \brief Infrastructure element responsible for domain decomposition
  *
  * This encapsulates the function call to domain decomposition, which is
@@ -88,32 +88,32 @@ class DomDecHelper final : public INeighborSearchSignallerClient
 {
 public:
     //! Constructor
-    DomDecHelper(bool                               isVerbose,
-                 int                                verbosePrintInterval,
-                 StatePropagatorData*               statePropagatorData,
-                 FreeEnergyPerturbationElement*     freeEnergyPerturbationElement,
-                 TopologyHolder*                    topologyHolder,
-                 CheckBondedInteractionsCallbackPtr checkBondedInteractionsCallback,
-                 int                                nstglobalcomm,
-                 FILE*                              fplog,
-                 t_commrec*                         cr,
-                 const MDLogger&                    mdlog,
-                 Constraints*                       constr,
-                 t_inputrec*                        inputrec,
-                 MDAtoms*                           mdAtoms,
-                 t_nrnb*                            nrnb,
-                 gmx_wallcycle*                     wcycle,
-                 t_forcerec*                        fr,
-                 gmx_vsite_t*                       vsite,
-                 ImdSession*                        imdSession,
-                 pull_t*                            pull_work);
+    DomDecHelper(bool                            isVerbose,
+                 int                             verbosePrintInterval,
+                 StatePropagatorData*            statePropagatorData,
+                 FreeEnergyPerturbationData*     freeEnergyPerturbationData,
+                 TopologyHolder*                 topologyHolder,
+                 CheckBondedInteractionsCallback checkBondedInteractionsCallback,
+                 int                             nstglobalcomm,
+                 FILE*                           fplog,
+                 t_commrec*                      cr,
+                 const MDLogger&                 mdlog,
+                 Constraints*                    constr,
+                 t_inputrec*                     inputrec,
+                 MDAtoms*                        mdAtoms,
+                 t_nrnb*                         nrnb,
+                 gmx_wallcycle*                  wcycle,
+                 t_forcerec*                     fr,
+                 VirtualSitesHandler*            vsite,
+                 ImdSession*                     imdSession,
+                 pull_t*                         pull_work);
 
     /*! \brief Run domain decomposition
      *
      * Does domain decomposition partitioning at neighbor searching steps
      *
-     * @param step  The step number
-     * @param time  The time
+     * \param step  The step number
+     * \param time  The time
      */
     void run(Step step, Time time);
 
@@ -124,7 +124,7 @@ public:
 
 private:
     //! INeighborSearchSignallerClient implementation
-    SignallerCallbackPtr registerNSCallback() override;
+    std::optional<SignallerCallback> registerNSCallback() override;
 
     //! The next NS step
     Step nextNSStep_;
@@ -135,14 +135,15 @@ private:
     //! The global communication frequency
     const int nstglobalcomm_;
 
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
     //! Pointer to the micro state
     StatePropagatorData* statePropagatorData_;
-    //! Pointer to the free energy element
-    FreeEnergyPerturbationElement* freeEnergyPerturbationElement_;
+    //! Pointer to the free energy data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
     //! Pointer to the topology
     TopologyHolder* topologyHolder_;
     //! Pointer to the ComputeGlobalsHelper object - to ask for # of bonded interaction checking
-    CheckBondedInteractionsCallbackPtr checkBondedInteractionsCallback_;
+    CheckBondedInteractionsCallback checkBondedInteractionsCallback_;
 
     //! Helper function unifying the DD partitioning calls in setup() and run()
     void partitionSystem(bool                     verbose,
@@ -172,7 +173,7 @@ private:
     //! Parameters for force calculations.
     t_forcerec* fr_;
     //! Handles virtual sites.
-    gmx_vsite_t* vsite_;
+    VirtualSitesHandler* vsite_;
     //! The Interactive Molecular Dynamics session.
     ImdSession* imdSession_;
     //! The pull work object.
similarity index 51%
rename from src/gromacs/modularsimulator/energyelement.cpp
rename to src/gromacs/modularsimulator/energydata.cpp
index dc8fc3e4930b5b13e246f01e9ae6dc388030e421..8bba774e60d1b00caf84bc3603a93509c39e99eb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gmxpre.h"
 
-#include "energyelement.h"
+#include "energydata.h"
 
+#include "gromacs/gmxlib/network.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/mdlib/compute_io.h"
+#include "gromacs/mdlib/coupling.h"
 #include "gromacs/mdlib/enerdata_utils.h"
 #include "gromacs/mdlib/energyoutput.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/stat.h"
 #include "gromacs/mdlib/update.h"
 #include "gromacs/mdrunutility/handlerestart.h"
+#include "gromacs/mdtypes/checkpointdata.h"
+#include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/energyhistory.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/observableshistory.h"
 #include "gromacs/mdtypes/pullhistory.h"
-#include "gromacs/mdtypes/state.h"
 #include "gromacs/topology/topology.h"
 
-#include "freeenergyperturbationelement.h"
+#include "freeenergyperturbationdata.h"
+#include "modularsimulator.h"
 #include "parrinellorahmanbarostat.h"
+#include "simulatoralgorithm.h"
 #include "statepropagatordata.h"
-#include "vrescalethermostat.h"
+#include "velocityscalingtemperaturecoupling.h"
 
 struct pull_t;
 class t_state;
@@ -72,33 +78,32 @@ namespace gmx
 {
 class Awh;
 
-EnergyElement::EnergyElement(StatePropagatorData*           statePropagatorData,
-                             FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                             const gmx_mtop_t*              globalTopology,
-                             const t_inputrec*              inputrec,
-                             const MDAtoms*                 mdAtoms,
-                             gmx_enerdata_t*                enerd,
-                             gmx_ekindata_t*                ekind,
-                             const Constraints*             constr,
-                             FILE*                          fplog,
-                             t_fcdata*                      fcd,
-                             const MdModulesNotifier&       mdModulesNotifier,
-                             bool                           isMasterRank,
-                             ObservablesHistory*            observablesHistory,
-                             StartingBehavior               startingBehavior) :
+EnergyData::EnergyData(StatePropagatorData*        statePropagatorData,
+                       FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                       const gmx_mtop_t*           globalTopology,
+                       const t_inputrec*           inputrec,
+                       const MDAtoms*              mdAtoms,
+                       gmx_enerdata_t*             enerd,
+                       gmx_ekindata_t*             ekind,
+                       const Constraints*          constr,
+                       FILE*                       fplog,
+                       t_fcdata*                   fcd,
+                       const MdModulesNotifier&    mdModulesNotifier,
+                       bool                        isMasterRank,
+                       ObservablesHistory*         observablesHistory,
+                       StartingBehavior            startingBehavior) :
+    element_(std::make_unique<Element>(this, isMasterRank)),
     isMasterRank_(isMasterRank),
-    energyWritingStep_(-1),
-    energyCalculationStep_(-1),
-    freeEnergyCalculationStep_(-1),
     forceVirialStep_(-1),
     shakeVirialStep_(-1),
     totalVirialStep_(-1),
     pressureStep_(-1),
     needToSumEkinhOld_(false),
+    hasReadEkinFromCheckpoint_(false),
     startingBehavior_(startingBehavior),
     statePropagatorData_(statePropagatorData),
-    freeEnergyPerturbationElement_(freeEnergyPerturbationElement),
-    vRescaleThermostat_(nullptr),
+    freeEnergyPerturbationData_(freeEnergyPerturbationData),
+    velocityScalingTemperatureCoupling_(nullptr),
     parrinelloRahmanBarostat_(nullptr),
     inputrec_(inputrec),
     top_global_(globalTopology),
@@ -118,13 +123,11 @@ EnergyElement::EnergyElement(StatePropagatorData*           statePropagatorData,
     clear_mat(pressure_);
     clear_rvec(muTot_);
 
-    if (freeEnergyPerturbationElement_)
-    {
-        dummyLegacyState_.flags = (1U << estFEPSTATE);
-    }
+    init_ekinstate(&ekinstate_, inputrec_);
+    observablesHistory_->energyHistory = std::make_unique<energyhistory_t>();
 }
 
-void EnergyElement::scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction)
+void EnergyData::Element::scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction)
 {
     if (!isMasterRank_)
     {
@@ -135,19 +138,17 @@ void EnergyElement::scheduleTask(Step step, Time time, const RegisterRunFunction
     auto isFreeEnergyCalculationStep = freeEnergyCalculationStep_ == step;
     if (isEnergyCalculationStep || writeEnergy)
     {
-        (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-                [this, time, isEnergyCalculationStep, isFreeEnergyCalculationStep]() {
-                    doStep(time, isEnergyCalculationStep, isFreeEnergyCalculationStep);
-                }));
+        registerRunFunction([this, time, isEnergyCalculationStep, isFreeEnergyCalculationStep]() {
+            energyData_->doStep(time, isEnergyCalculationStep, isFreeEnergyCalculationStep);
+        });
     }
     else
     {
-        (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-                [this]() { energyOutput_->recordNonEnergyStep(); }));
+        registerRunFunction([this]() { energyData_->energyOutput_->recordNonEnergyStep(); });
     }
 }
 
-void EnergyElement::elementTeardown()
+void EnergyData::teardown()
 {
     if (inputrec_->nstcalcenergy > 0 && isMasterRank_)
     {
@@ -155,7 +156,12 @@ void EnergyElement::elementTeardown()
     }
 }
 
-void EnergyElement::trajectoryWriterSetup(gmx_mdoutf* outf)
+void EnergyData::Element::trajectoryWriterSetup(gmx_mdoutf* outf)
+{
+    energyData_->setup(outf);
+}
+
+void EnergyData::setup(gmx_mdoutf* outf)
 {
     pull_t* pull_work = nullptr;
     energyOutput_ = std::make_unique<EnergyOutput>(mdoutf_get_fp_ene(outf), top_global_, inputrec_,
@@ -191,72 +197,70 @@ void EnergyElement::trajectoryWriterSetup(gmx_mdoutf* outf)
     }
 }
 
-ITrajectoryWriterCallbackPtr EnergyElement::registerTrajectoryWriterCallback(TrajectoryEvent event)
+std::optional<ITrajectoryWriterCallback> EnergyData::Element::registerTrajectoryWriterCallback(TrajectoryEvent event)
 {
     if (event == TrajectoryEvent::EnergyWritingStep && isMasterRank_)
     {
-        return std::make_unique<ITrajectoryWriterCallback>(
-                [this](gmx_mdoutf* mdoutf, Step step, Time time, bool writeTrajectory,
-                       bool writeLog) { write(mdoutf, step, time, writeTrajectory, writeLog); });
+        return [this](gmx_mdoutf* mdoutf, Step step, Time time, bool writeTrajectory, bool writeLog) {
+            energyData_->write(mdoutf, step, time, writeTrajectory, writeLog);
+        };
     }
-    return nullptr;
+    return std::nullopt;
 }
 
-SignallerCallbackPtr EnergyElement::registerTrajectorySignallerCallback(gmx::TrajectoryEvent event)
+std::optional<SignallerCallback> EnergyData::Element::registerTrajectorySignallerCallback(gmx::TrajectoryEvent event)
 {
     if (event == TrajectoryEvent::EnergyWritingStep && isMasterRank_)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { energyWritingStep_ = step; });
+        return [this](Step step, Time /*unused*/) { energyWritingStep_ = step; };
     }
-    return nullptr;
+    return std::nullopt;
 }
 
-SignallerCallbackPtr EnergyElement::registerEnergyCallback(EnergySignallerEvent event)
+std::optional<SignallerCallback> EnergyData::Element::registerEnergyCallback(EnergySignallerEvent event)
 {
     if (event == EnergySignallerEvent::EnergyCalculationStep && isMasterRank_)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { energyCalculationStep_ = step; });
+        return [this](Step step, Time /*unused*/) { energyCalculationStep_ = step; };
     }
     if (event == EnergySignallerEvent::FreeEnergyCalculationStep && isMasterRank_)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { freeEnergyCalculationStep_ = step; });
+        return [this](Step step, Time /*unused*/) { freeEnergyCalculationStep_ = step; };
     }
-    return nullptr;
+    return std::nullopt;
 }
 
-void EnergyElement::doStep(Time time, bool isEnergyCalculationStep, bool isFreeEnergyCalculationStep)
+void EnergyData::doStep(Time time, bool isEnergyCalculationStep, bool isFreeEnergyCalculationStep)
 {
     enerd_->term[F_ETOT] = enerd_->term[F_EPOT] + enerd_->term[F_EKIN];
-    if (vRescaleThermostat_)
+    if (freeEnergyPerturbationData_)
     {
-        dummyLegacyState_.therm_integral = vRescaleThermostat_->thermostatIntegral();
-    }
-    if (freeEnergyPerturbationElement_)
-    {
-        sum_dhdl(enerd_, freeEnergyPerturbationElement_->constLambdaView(), *inputrec_->fepvals);
-        dummyLegacyState_.fep_state = freeEnergyPerturbationElement_->currentFEPState();
-    }
-    if (parrinelloRahmanBarostat_)
-    {
-        copy_mat(parrinelloRahmanBarostat_->boxVelocities(), dummyLegacyState_.boxv);
-        copy_mat(statePropagatorData_->constBox(), dummyLegacyState_.box);
+        accumulateKineticLambdaComponents(enerd_, freeEnergyPerturbationData_->constLambdaView(),
+                                          *inputrec_->fepvals);
     }
     if (integratorHasConservedEnergyQuantity(inputrec_))
     {
         enerd_->term[F_ECONSERVED] =
-                enerd_->term[F_ETOT] + NPT_energy(inputrec_, &dummyLegacyState_, nullptr);
+                enerd_->term[F_ETOT]
+                + (velocityScalingTemperatureCoupling_
+                           ? velocityScalingTemperatureCoupling_->conservedEnergyContribution()
+                           : 0)
+                + (parrinelloRahmanBarostat_ ? parrinelloRahmanBarostat_->conservedEnergyContribution() : 0);
     }
-    energyOutput_->addDataAtEnergyStep(isFreeEnergyCalculationStep, isEnergyCalculationStep, time,
-                                       mdAtoms_->mdatoms()->tmass, enerd_, &dummyLegacyState_,
-                                       inputrec_->fepvals, inputrec_->expandedvals,
-                                       statePropagatorData_->constPreviousBox(), shakeVirial_,
-                                       forceVirial_, totalVirial_, pressure_, ekind_, muTot_, constr_);
+    matrix nullMatrix = {};
+    energyOutput_->addDataAtEnergyStep(
+            isFreeEnergyCalculationStep, isEnergyCalculationStep, time, mdAtoms_->mdatoms()->tmass, enerd_,
+            inputrec_->fepvals, inputrec_->expandedvals, statePropagatorData_->constPreviousBox(),
+            PTCouplingArrays({ parrinelloRahmanBarostat_ ? parrinelloRahmanBarostat_->boxVelocities() : nullMatrix,
+                               {},
+                               {},
+                               {},
+                               {} }),
+            freeEnergyPerturbationData_ ? freeEnergyPerturbationData_->currentFEPState() : 0,
+            shakeVirial_, forceVirial_, totalVirial_, pressure_, ekind_, muTot_, constr_);
 }
 
-void EnergyElement::write(gmx_mdoutf* outf, Step step, Time time, bool writeTrajectory, bool writeLog)
+void EnergyData::write(gmx_mdoutf* outf, Step step, Time time, bool writeTrajectory, bool writeLog)
 {
     if (writeLog)
     {
@@ -272,7 +276,7 @@ void EnergyElement::write(gmx_mdoutf* outf, Step step, Time time, bool writeTraj
                                          writeLog ? fplog_ : nullptr, step, time, fcd_, awh);
 }
 
-void EnergyElement::addToForceVirial(const tensor virial, Step step)
+void EnergyData::addToForceVirial(const tensor virial, Step step)
 {
     if (step > forceVirialStep_)
     {
@@ -282,7 +286,7 @@ void EnergyElement::addToForceVirial(const tensor virial, Step step)
     m_add(forceVirial_, virial, forceVirial_);
 }
 
-void EnergyElement::addToConstraintVirial(const tensor virial, Step step)
+void EnergyData::addToConstraintVirial(const tensor virial, Step step)
 {
     if (step > shakeVirialStep_)
     {
@@ -292,7 +296,7 @@ void EnergyElement::addToConstraintVirial(const tensor virial, Step step)
     m_add(shakeVirial_, virial, shakeVirial_);
 }
 
-rvec* EnergyElement::forceVirial(Step gmx_unused step)
+rvec* EnergyData::forceVirial(Step gmx_unused step)
 {
     if (step > forceVirialStep_)
     {
@@ -304,7 +308,7 @@ rvec* EnergyElement::forceVirial(Step gmx_unused step)
     return forceVirial_;
 }
 
-rvec* EnergyElement::constraintVirial(Step gmx_unused step)
+rvec* EnergyData::constraintVirial(Step gmx_unused step)
 {
     if (step > shakeVirialStep_)
     {
@@ -316,7 +320,7 @@ rvec* EnergyElement::constraintVirial(Step gmx_unused step)
     return shakeVirial_;
 }
 
-rvec* EnergyElement::totalVirial(Step gmx_unused step)
+rvec* EnergyData::totalVirial(Step gmx_unused step)
 {
     if (step > totalVirialStep_)
     {
@@ -328,7 +332,7 @@ rvec* EnergyElement::totalVirial(Step gmx_unused step)
     return totalVirial_;
 }
 
-rvec* EnergyElement::pressure(Step gmx_unused step)
+rvec* EnergyData::pressure(Step gmx_unused step)
 {
     if (step > pressureStep_)
     {
@@ -340,46 +344,105 @@ rvec* EnergyElement::pressure(Step gmx_unused step)
     return pressure_;
 }
 
-real* EnergyElement::muTot()
+real* EnergyData::muTot()
 {
     return muTot_;
 }
 
-gmx_enerdata_t* EnergyElement::enerdata()
+gmx_enerdata_t* EnergyData::enerdata()
 {
     return enerd_;
 }
 
-gmx_ekindata_t* EnergyElement::ekindata()
+gmx_ekindata_t* EnergyData::ekindata()
 {
     return ekind_;
 }
 
-bool* EnergyElement::needToSumEkinhOld()
+bool* EnergyData::needToSumEkinhOld()
 {
     return &needToSumEkinhOld_;
 }
 
-void EnergyElement::writeCheckpoint(t_state gmx_unused* localState, t_state* globalState)
+bool EnergyData::hasReadEkinFromCheckpoint() const
 {
-    if (isMasterRank_)
+    return hasReadEkinFromCheckpoint_;
+}
+
+namespace
+{
+/*!
+ * \brief Enum describing the contents EnergyData::Element writes to modular checkpoint
+ *
+ * When changing the checkpoint content, add a new element just above Count, and adjust the
+ * checkpoint functionality.
+ */
+enum class CheckpointVersion
+{
+    Base, //!< First version of modular checkpointing
+    Count //!< Number of entries. Add new versions right above this!
+};
+constexpr auto c_currentVersion = CheckpointVersion(int(CheckpointVersion::Count) - 1);
+} // namespace
+
+template<CheckpointDataOperation operation>
+void EnergyData::Element::doCheckpointData(CheckpointData<operation>* checkpointData)
+{
+    checkpointVersion(checkpointData, "EnergyData version", c_currentVersion);
+
+    energyData_->observablesHistory_->energyHistory->doCheckpoint<operation>(
+            checkpointData->subCheckpointData("energy history"));
+    energyData_->ekinstate_.doCheckpoint<operation>(checkpointData->subCheckpointData("ekinstate"));
+}
+
+void EnergyData::Element::saveCheckpointState(std::optional<WriteCheckpointData> checkpointData,
+                                              const t_commrec*                   cr)
+{
+    if (MASTER(cr))
     {
-        if (needToSumEkinhOld_)
+        if (energyData_->needToSumEkinhOld_)
         {
-            globalState->ekinstate.bUpToDate = false;
+            energyData_->ekinstate_.bUpToDate = false;
         }
         else
         {
-            update_ekinstate(&globalState->ekinstate, ekind_);
-            globalState->ekinstate.bUpToDate = true;
+            update_ekinstate(&energyData_->ekinstate_, energyData_->ekind_);
+            energyData_->ekinstate_.bUpToDate = true;
         }
-        energyOutput_->fillEnergyHistory(observablesHistory_->energyHistory.get());
+        energyData_->energyOutput_->fillEnergyHistory(
+                energyData_->observablesHistory_->energyHistory.get());
+        doCheckpointData<CheckpointDataOperation::Write>(&checkpointData.value());
+    }
+}
+
+void EnergyData::Element::restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData,
+                                                 const t_commrec*                  cr)
+{
+    if (MASTER(cr))
+    {
+        doCheckpointData<CheckpointDataOperation::Read>(&checkpointData.value());
+    }
+    energyData_->hasReadEkinFromCheckpoint_ = MASTER(cr) ? energyData_->ekinstate_.bUpToDate : false;
+    if (PAR(cr))
+    {
+        gmx_bcast(sizeof(hasReadEkinFromCheckpoint_), &energyData_->hasReadEkinFromCheckpoint_,
+                  cr->mpi_comm_mygroup);
+    }
+    if (energyData_->hasReadEkinFromCheckpoint_)
+    {
+        // this takes care of broadcasting from master to agents
+        restore_ekinstate_from_state(cr, energyData_->ekind_, &energyData_->ekinstate_);
     }
 }
 
-void EnergyElement::initializeEnergyHistory(StartingBehavior    startingBehavior,
-                                            ObservablesHistory* observablesHistory,
-                                            EnergyOutput*       energyOutput)
+const std::string& EnergyData::Element::clientID()
+{
+    return identifier_;
+}
+
+void EnergyData::initializeEnergyHistory(StartingBehavior    startingBehavior,
+                                         ObservablesHistory* observablesHistory,
+                                         EnergyOutput*       energyOutput)
 {
     if (startingBehavior != StartingBehavior::NewSimulation)
     {
@@ -421,22 +484,39 @@ void EnergyElement::initializeEnergyHistory(StartingBehavior    startingBehavior
     energyOutput->fillEnergyHistory(observablesHistory->energyHistory.get());
 }
 
-void EnergyElement::setVRescaleThermostat(const gmx::VRescaleThermostat* vRescaleThermostat)
+void EnergyData::setVelocityScalingTemperatureCoupling(const VelocityScalingTemperatureCoupling* velocityScalingTemperatureCoupling)
 {
-    vRescaleThermostat_ = vRescaleThermostat;
-    if (vRescaleThermostat_)
-    {
-        dummyLegacyState_.flags |= (1U << estTHERM_INT);
-    }
+    velocityScalingTemperatureCoupling_ = velocityScalingTemperatureCoupling;
 }
 
-void EnergyElement::setParrinelloRahamnBarostat(const gmx::ParrinelloRahmanBarostat* parrinelloRahmanBarostat)
+void EnergyData::setParrinelloRahamnBarostat(const gmx::ParrinelloRahmanBarostat* parrinelloRahmanBarostat)
 {
     parrinelloRahmanBarostat_ = parrinelloRahmanBarostat;
-    if (parrinelloRahmanBarostat_)
-    {
-        dummyLegacyState_.flags |= (1U << estBOX) | (1U << estBOXV);
-    }
+}
+
+EnergyData::Element* EnergyData::element()
+{
+    return element_.get();
+}
+
+EnergyData::Element::Element(EnergyData* energyData, bool isMasterRank) :
+    energyData_(energyData),
+    isMasterRank_(isMasterRank),
+    energyWritingStep_(-1),
+    energyCalculationStep_(-1),
+    freeEnergyCalculationStep_(-1)
+{
+}
+
+ISimulatorElement* EnergyData::Element::getElementPointerImpl(
+        LegacySimulatorData gmx_unused*        legacySimulatorData,
+        ModularSimulatorAlgorithmBuilderHelper gmx_unused* builderHelper,
+        StatePropagatorData gmx_unused* statePropagatorData,
+        EnergyData*                     energyData,
+        FreeEnergyPerturbationData gmx_unused* freeEnergyPerturbationData,
+        GlobalCommunicationHelper gmx_unused* globalCommunicationHelper)
+{
+    return energyData->element();
 }
 
 } // namespace gmx
similarity index 59%
rename from src/gromacs/modularsimulator/energyelement.h
rename to src/gromacs/modularsimulator/energydata.h
index a9440faae08ee8d3c27b72d8877ef0b91e67390f..774390a39a95704d2c7074eec22dc82a4eb87029 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the energy element for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_ENERGYELEMENT_MICROSTATE_H
@@ -60,80 +62,60 @@ namespace gmx
 enum class StartingBehavior;
 class Constraints;
 class EnergyOutput;
-class FreeEnergyPerturbationElement;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
 class MDAtoms;
+class ModularSimulatorAlgorithmBuilderHelper;
 class ParrinelloRahmanBarostat;
 class StatePropagatorData;
-class VRescaleThermostat;
+class VelocityScalingTemperatureCoupling;
 struct MdModulesNotifier;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
- * \brief Element managing energies
+ * \brief Data class managing energies
  *
- * The EnergyElement owns the EnergyObject, and is hence responsible
- * for saving energy data and writing it to trajectory. It also owns
+ * The EnergyData owns the EnergyObject,
  * the tensors for the different virials and the pressure as well as
- * the total dipole vector.
- *
- * It subscribes to the trajectory signaller, the energy signaller,
- * and the logging signaller to know when an energy calculation is
- * needed and when a non-recording step is enough. The simulator
- * builder is responsible to place the element in a location at
- * which a valid energy state is available. The EnergyElement is
- * also a subscriber to the trajectory writer element, as it is
- * responsible to write energy data to trajectory.
+ * the total dipole vector. It has a member class which is part of the
+ * simulator loop and and is responsible
+ * for saving energy data and writing it to trajectory.
  *
- * The EnergyElement offers an interface to add virial contributions,
+ * The EnergyData offers an interface to add virial contributions,
  * but also allows access to the raw pointers to tensor data, the
  * dipole vector, and the legacy energy data structures.
+ *
+ * The EnergyData owns an object of type EnergyData::Element,
+ * which takes part in the simulation loop, allowing to record
+ * and output energies during the simulation.
  */
-class EnergyElement final :
-    public ISimulatorElement,
-    public ITrajectoryWriterClient,
-    public ITrajectorySignallerClient,
-    public IEnergySignallerClient,
-    public ICheckpointHelperClient
+class EnergyData final
 {
 public:
     //! Constructor
-    EnergyElement(StatePropagatorData*           statePropagatorData,
-                  FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                  const gmx_mtop_t*              globalTopology,
-                  const t_inputrec*              inputrec,
-                  const MDAtoms*                 mdAtoms,
-                  gmx_enerdata_t*                enerd,
-                  gmx_ekindata_t*                ekind,
-                  const Constraints*             constr,
-                  FILE*                          fplog,
-                  t_fcdata*                      fcd,
-                  const MdModulesNotifier&       mdModulesNotifier,
-                  bool                           isMasterRank,
-                  ObservablesHistory*            observablesHistory,
-                  StartingBehavior               startingBehavior);
-
-    /*! \brief Register run function for step / time
-     *
-     * This needs to be called when the energies are at a full time step.
-     * Positioning this element is the responsibility of the programmer.
-     *
-     * This is also the place at which the current state becomes the previous
-     * state.
-     *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
-     */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
-
-    //! No element setup needed
-    void elementSetup() override {}
+    EnergyData(StatePropagatorData*        statePropagatorData,
+               FreeEnergyPerturbationData* freeEnergyPerturbationData,
+               const gmx_mtop_t*           globalTopology,
+               const t_inputrec*           inputrec,
+               const MDAtoms*              mdAtoms,
+               gmx_enerdata_t*             enerd,
+               gmx_ekindata_t*             ekind,
+               const Constraints*          constr,
+               FILE*                       fplog,
+               t_fcdata*                   fcd,
+               const MdModulesNotifier&    mdModulesNotifier,
+               bool                        isMasterRank,
+               ObservablesHistory*         observablesHistory,
+               StartingBehavior            startingBehavior);
 
     /*! \brief Final output
      *
-     * Prints the averages to log.
+     * Prints the averages to log. This is called from ModularSimulatorAlgorithm.
+     *
+     * \see ModularSimulatorAlgorithm::teardown
      */
-    void elementTeardown() override;
+    void teardown();
 
     /*! \brief Add contribution to force virial
      *
@@ -198,13 +180,20 @@ public:
      */
     bool* needToSumEkinhOld();
 
-    /*! \brief set vrescale thermostat
+    /*! \brief Whether kinetic energy was read from checkpoint
+     *
+     * This is needed by the compute globals element
+     * TODO: Remove this when moving global reduction to client system (#3421)
+     */
+    [[nodiscard]] bool hasReadEkinFromCheckpoint() const;
+
+    /*! \brief Set velocity scaling temperature coupling
      *
-     * This allows to set a pointer to the vrescale thermostat used to
-     * print the thermostat integral.
+     * This allows to set a pointer to a velocity scaling temperature coupling
+     * element used to obtain contributions to the conserved energy.
      * TODO: This should be made obsolete my a more modular energy element
      */
-    void setVRescaleThermostat(const VRescaleThermostat* vRescaleThermostat);
+    void setVelocityScalingTemperatureCoupling(const VelocityScalingTemperatureCoupling* velocityScalingTemperatureCoupling);
 
     /*! \brief set Parrinello-Rahman barostat
      *
@@ -223,31 +212,25 @@ public:
                                         ObservablesHistory* observablesHistory,
                                         EnergyOutput*       energyOutput);
 
+    //! The element taking part in the simulator loop
+    class Element;
+    //! Get pointer to element (whose lifetime is managed by this)
+    Element* element();
+
 private:
     /*! \brief Setup (needs file pointer)
-     *
-     * ITrajectoryWriterClient implementation.
      *
      * Initializes the EnergyOutput object, and does some logging output.
      *
-     * @param mdoutf  File pointer
+     * \param mdoutf  File pointer
      */
-    void trajectoryWriterSetup(gmx_mdoutf* mdoutf) override;
-    //! No trajectory writer teardown needed
-    void trajectoryWriterTeardown(gmx_mdoutf gmx_unused* outf) override {}
-
-    //! ITrajectoryWriterClient implementation.
-    SignallerCallbackPtr registerTrajectorySignallerCallback(TrajectoryEvent event) override;
-    //! ITrajectorySignallerClient implementation
-    ITrajectoryWriterCallbackPtr registerTrajectoryWriterCallback(TrajectoryEvent event) override;
-    //! IEnergySignallerClient implementation
-    SignallerCallbackPtr registerEnergyCallback(EnergySignallerEvent event) override;
+    void setup(gmx_mdoutf* mdoutf);
 
     /*! \brief Save data at energy steps
      *
-     * @param time  The current time
-     * @param isEnergyCalculationStep  Whether the current step is an energy calculation step
-     * @param isFreeEnergyCalculationStep  Whether the current step is a free energy calculation step
+     * \param time  The current time
+     * \param isEnergyCalculationStep  Whether the current step is an energy calculation step
+     * \param isFreeEnergyCalculationStep  Whether the current step is a free energy calculation step
      */
     void doStep(Time time, bool isEnergyCalculationStep, bool isFreeEnergyCalculationStep);
 
@@ -257,23 +240,18 @@ private:
      */
     void write(gmx_mdoutf* outf, Step step, Time time, bool writeTrajectory, bool writeLog);
 
-    //! ICheckpointHelperClient implementation
-    void writeCheckpoint(t_state* localState, t_state* globalState) override;
-
     /*
-     * Data owned by EnergyElement
+     * Data owned by EnergyData
      */
+    //! The element
+    std::unique_ptr<Element> element_;
     //! The energy output object
     std::unique_ptr<EnergyOutput> energyOutput_;
+    //! Helper object to checkpoint kinetic energy data
+    ekinstate_t ekinstate_;
 
     //! Whether this is the master rank
     const bool isMasterRank_;
-    //! The next communicated energy writing step
-    Step energyWritingStep_;
-    //! The next communicated energy calculation step
-    Step energyCalculationStep_;
-    //! The next communicated free energy calculation step
-    Step freeEnergyCalculationStep_;
 
     //! The force virial tensor
     tensor forceVirial_;
@@ -297,22 +275,22 @@ private:
 
     //! Whether ekinh_old needs to be summed up (set by compute globals)
     bool needToSumEkinhOld_;
+    //! Whether we have read ekin from checkpoint
+    bool hasReadEkinFromCheckpoint_;
 
     //! Describes how the simulation (re)starts
     const StartingBehavior startingBehavior_;
 
-    //! Legacy state object used to communicate with energy output
-    t_state dummyLegacyState_;
-
     /*
      * Pointers to Simulator data
      */
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
     //! Pointer to the state propagator data
     StatePropagatorData* statePropagatorData_;
-    //! Pointer to the free energy perturbation element
-    FreeEnergyPerturbationElement* freeEnergyPerturbationElement_;
+    //! Pointer to the free energy perturbation data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
     //! Pointer to the vrescale thermostat
-    const VRescaleThermostat* vRescaleThermostat_;
+    const VelocityScalingTemperatureCoupling* velocityScalingTemperatureCoupling_;
     //! Pointer to the Parrinello-Rahman barostat
     const ParrinelloRahmanBarostat* parrinelloRahmanBarostat_;
     //! Contains user input mdp options.
@@ -339,6 +317,116 @@ private:
     ObservablesHistory* observablesHistory_;
 };
 
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element for EnergyData
+ *
+ * This member class allows EnergyData to take part in the simulator
+ * loop.
+ *
+ * It subscribes to the trajectory signaller, the energy signaller,
+ * and the logging signaller to know when an energy calculation is
+ * needed and when a non-recording step is enough. The simulator
+ * builder is responsible to place the element in a location at
+ * which a valid energy state is available. The EnergyData::Element is
+ * also a subscriber to the trajectory writer element, as it is
+ * responsible to write energy data to trajectory.
+ */
+class EnergyData::Element final :
+    public ISimulatorElement,
+    public ITrajectoryWriterClient,
+    public ITrajectorySignallerClient,
+    public IEnergySignallerClient,
+    public ICheckpointHelperClient
+{
+public:
+    //! Constructor
+    Element(EnergyData* energyData, bool isMasterRank);
+
+    /*! \brief Register run function for step / time
+     *
+     * This needs to be called when the energies are at a full time step.
+     * Positioning this element is the responsibility of the programmer.
+     *
+     * This is also the place at which the current state becomes the previous
+     * state.
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! No element setup needed
+    void elementSetup() override {}
+
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper);
+
+private:
+    EnergyData* energyData_;
+
+    /*! \brief Setup (needs file pointer)
+     *
+     * ITrajectoryWriterClient implementation.
+     *
+     * Initializes the EnergyOutput object, and does some logging output.
+     *
+     * \param mdoutf  File pointer
+     */
+    void trajectoryWriterSetup(gmx_mdoutf* mdoutf) override;
+    //! No trajectory writer teardown needed
+    void trajectoryWriterTeardown(gmx_mdoutf gmx_unused* outf) override {}
+
+    //! ITrajectoryWriterClient implementation.
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+    //! ITrajectorySignallerClient implementation
+    std::optional<ITrajectoryWriterCallback> registerTrajectoryWriterCallback(TrajectoryEvent event) override;
+    //! IEnergySignallerClient implementation
+    std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent event) override;
+
+
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "EnergyElement";
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+
+    //! Whether this is the master rank
+    const bool isMasterRank_;
+    //! The next communicated energy writing step
+    Step energyWritingStep_;
+    //! The next communicated energy calculation step
+    Step energyCalculationStep_;
+    //! The next communicated free energy calculation step
+    Step freeEnergyCalculationStep_;
+};
+
 } // namespace gmx
 
 #endif // GMX_ENERGYELEMENT_MICROSTATE_H
index 0b28b49ed08e027d5364504807d20e8f4a51fabd..4d433767069cea38af23b0b684e9511a5f9bad55 100644 (file)
 
 #include "forceelement.h"
 
+#include "gromacs/domdec/mdsetup.h"
 #include "gromacs/math/vectypes.h"
+#include "gromacs/mdlib/constr.h"
 #include "gromacs/mdlib/force.h"
 #include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdlib/mdatoms.h"
+#include "gromacs/mdrun/shellfc.h"
+#include "gromacs/mdtypes/forcebuffers.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/mdtypes/mdrunoptions.h"
 #include "gromacs/pbcutil/pbc.h"
 
-#include "energyelement.h"
-#include "freeenergyperturbationelement.h"
+#include "energydata.h"
+#include "freeenergyperturbationdata.h"
+#include "modularsimulator.h"
+#include "simulatoralgorithm.h"
 #include "statepropagatordata.h"
 
 struct gmx_edsam;
 struct gmx_enfrot;
 struct gmx_multisim_t;
 class history_t;
-struct t_graph;
 
 namespace gmx
 {
-ForceElement::ForceElement(StatePropagatorData*           statePropagatorData,
-                           EnergyElement*                 energyElement,
-                           FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                           bool                           isDynamicBox,
-                           FILE*                          fplog,
-                           const t_commrec*               cr,
-                           const t_inputrec*              inputrec,
-                           const MDAtoms*                 mdAtoms,
-                           t_nrnb*                        nrnb,
-                           t_forcerec*                    fr,
-                           t_fcdata*                      fcd,
-                           gmx_wallcycle*                 wcycle,
-                           MdrunScheduleWorkload*         runScheduleWork,
-                           gmx_vsite_t*                   vsite,
-                           ImdSession*                    imdSession,
-                           pull_t*                        pull_work,
-                           gmx_enfrot*                    enforcedRotation) :
+ForceElement::ForceElement(StatePropagatorData*        statePropagatorData,
+                           EnergyData*                 energyData,
+                           FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                           bool                        isVerbose,
+                           bool                        isDynamicBox,
+                           FILE*                       fplog,
+                           const t_commrec*            cr,
+                           const t_inputrec*           inputrec,
+                           const MDAtoms*              mdAtoms,
+                           t_nrnb*                     nrnb,
+                           t_forcerec*                 fr,
+
+                           gmx_wallcycle*         wcycle,
+                           MdrunScheduleWorkload* runScheduleWork,
+                           VirtualSitesHandler*   vsite,
+                           ImdSession*            imdSession,
+                           pull_t*                pull_work,
+                           Constraints*           constr,
+                           const gmx_mtop_t*      globalTopology,
+                           gmx_enfrot*            enforcedRotation) :
+    shellfc_(init_shell_flexcon(fplog,
+                                globalTopology,
+                                constr ? constr->numFlexibleConstraints() : 0,
+                                inputrec->nstcalcenergy,
+                                DOMAINDECOMP(cr))),
+    doShellFC_(shellfc_ != nullptr),
     nextNSStep_(-1),
     nextEnergyCalculationStep_(-1),
     nextVirialCalculationStep_(-1),
     nextFreeEnergyCalculationStep_(-1),
     statePropagatorData_(statePropagatorData),
-    energyElement_(energyElement),
-    freeEnergyPerturbationElement_(freeEnergyPerturbationElement),
+    energyData_(energyData),
+    freeEnergyPerturbationData_(freeEnergyPerturbationData),
     localTopology_(nullptr),
     isDynamicBox_(isDynamicBox),
+    isVerbose_(isVerbose),
+    nShellRelaxationSteps_(0),
     ddBalanceRegionHandler_(cr),
     lambda_(),
     fplog_(fplog),
@@ -100,24 +118,39 @@ ForceElement::ForceElement(StatePropagatorData*           statePropagatorData,
     vsite_(vsite),
     imdSession_(imdSession),
     pull_work_(pull_work),
-    fcd_(fcd),
     runScheduleWork_(runScheduleWork),
+    constr_(constr),
     enforcedRotation_(enforcedRotation)
 {
     lambda_.fill(0);
+
+    if (doShellFC_ && !DOMAINDECOMP(cr))
+    {
+        // This was done in mdAlgorithmsSetupAtomData(), but shellfc
+        // won't be available outside this element.
+        make_local_shells(cr, mdAtoms->mdatoms(), shellfc_);
+    }
 }
 
-void ForceElement::scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction)
+void ForceElement::scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction)
 {
     unsigned int flags =
             (GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES | (isDynamicBox_ ? GMX_FORCE_DYNAMICBOX : 0)
              | (nextVirialCalculationStep_ == step ? GMX_FORCE_VIRIAL : 0)
              | (nextEnergyCalculationStep_ == step ? GMX_FORCE_ENERGY : 0)
              | (nextFreeEnergyCalculationStep_ == step ? GMX_FORCE_DHDL : 0)
-             | (nextNSStep_ == step ? GMX_FORCE_NS : 0));
+             | (!doShellFC_ && nextNSStep_ == step ? GMX_FORCE_NS : 0));
 
-    (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-            [this, step, time, flags]() { run(step, time, flags); }));
+    registerRunFunction([this, step, time, flags]() {
+        if (doShellFC_)
+        {
+            run<true>(step, time, flags);
+        }
+        else
+        {
+            run<false>(step, time, flags);
+        }
+    });
 }
 
 void ForceElement::elementSetup()
@@ -125,20 +158,19 @@ void ForceElement::elementSetup()
     GMX_ASSERT(localTopology_, "Setup called before local topology was set.");
 }
 
+template<bool doShellFC>
 void ForceElement::run(Step step, Time time, unsigned int flags)
 {
     // Disabled functionality
-    Awh*            awh   = nullptr;
-    gmx_edsam*      ed    = nullptr;
-    gmx_multisim_t* ms    = nullptr;
-    t_graph*        graph = nullptr;
+    gmx_multisim_t* ms = nullptr;
+
 
     if (!DOMAINDECOMP(cr_) && (flags & GMX_FORCE_NS) && inputrecDynamicBox(inputrec_))
     {
         // TODO: Correcting the box is done in DomDecHelper (if using DD) or here (non-DD simulations).
         //       Think about unifying this responsibility, could this be done in one place?
         auto box = statePropagatorData_->box();
-        correct_box(fplog_, step, box, graph);
+        correct_box(fplog_, step, box);
     }
 
     /* The coordinates (x) are shifted (to get whole molecules)
@@ -147,20 +179,47 @@ void ForceElement::run(Step step, Time time, unsigned int flags)
      * Check comments in sim_util.c
      */
     auto       x      = statePropagatorData_->positionsView();
-    auto       forces = statePropagatorData_->forcesView();
+    auto&      forces = statePropagatorData_->forcesView();
     auto       box    = statePropagatorData_->constBox();
     history_t* hist   = nullptr; // disabled
 
     tensor force_vir = { { 0 } };
     // TODO: Make lambda const (needs some adjustments in lower force routines)
     ArrayRef<real> lambda =
-            freeEnergyPerturbationElement_ ? freeEnergyPerturbationElement_->lambdaView() : lambda_;
+            freeEnergyPerturbationData_ ? freeEnergyPerturbationData_->lambdaView() : lambda_;
+
+    if (doShellFC)
+    {
+        auto v = statePropagatorData_->velocitiesView();
+
+        relax_shell_flexcon(
+                fplog_, cr_, ms, isVerbose_, enforcedRotation_, step, inputrec_, imdSession_,
+                pull_work_, step == nextNSStep_, static_cast<int>(flags), localTopology_, constr_,
+                energyData_->enerdata(), statePropagatorData_->localNumAtoms(), x, v, box, lambda,
+                hist, &forces, force_vir, mdAtoms_->mdatoms(), nrnb_, wcycle_, shellfc_, fr_,
+                runScheduleWork_, time, energyData_->muTot(), vsite_, ddBalanceRegionHandler_);
+        nShellRelaxationSteps_++;
+    }
+    else
+    {
+        // Disabled functionality
+        Awh*       awh = nullptr;
+        gmx_edsam* ed  = nullptr;
 
-    do_force(fplog_, cr_, ms, inputrec_, awh, enforcedRotation_, imdSession_, pull_work_, step,
-             nrnb_, wcycle_, localTopology_, box, x, hist, forces, force_vir, mdAtoms_->mdatoms(),
-             energyElement_->enerdata(), fcd_, lambda, graph, fr_, runScheduleWork_, vsite_,
-             energyElement_->muTot(), time, ed, static_cast<int>(flags), ddBalanceRegionHandler_);
-    energyElement_->addToForceVirial(force_vir, step);
+        do_force(fplog_, cr_, ms, inputrec_, awh, enforcedRotation_, imdSession_, pull_work_, step,
+                 nrnb_, wcycle_, localTopology_, box, x, hist, &forces, force_vir,
+                 mdAtoms_->mdatoms(), energyData_->enerdata(), lambda, fr_, runScheduleWork_, vsite_,
+                 energyData_->muTot(), time, ed, static_cast<int>(flags), ddBalanceRegionHandler_);
+    }
+    energyData_->addToForceVirial(force_vir, step);
+}
+
+void ForceElement::elementTeardown()
+{
+    if (doShellFC_)
+    {
+        done_shellfc(fplog_, shellfc_, nShellRelaxationSteps_);
+    }
 }
 
 void ForceElement::setTopology(const gmx_localtop_t* top)
@@ -168,29 +227,44 @@ void ForceElement::setTopology(const gmx_localtop_t* top)
     localTopology_ = top;
 }
 
-SignallerCallbackPtr ForceElement::registerNSCallback()
+std::optional<SignallerCallback> ForceElement::registerNSCallback()
 {
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { this->nextNSStep_ = step; });
+    return [this](Step step, Time gmx_unused time) { this->nextNSStep_ = step; };
 }
 
-SignallerCallbackPtr ForceElement::registerEnergyCallback(EnergySignallerEvent event)
+std::optional<SignallerCallback> ForceElement::registerEnergyCallback(EnergySignallerEvent event)
 {
     if (event == EnergySignallerEvent::EnergyCalculationStep)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { nextEnergyCalculationStep_ = step; });
+        return [this](Step step, Time /*unused*/) { nextEnergyCalculationStep_ = step; };
     }
     if (event == EnergySignallerEvent::VirialCalculationStep)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { nextVirialCalculationStep_ = step; });
+        return [this](Step step, Time /*unused*/) { nextVirialCalculationStep_ = step; };
     }
     if (event == EnergySignallerEvent::FreeEnergyCalculationStep)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { nextFreeEnergyCalculationStep_ = step; });
+        return [this](Step step, Time /*unused*/) { nextFreeEnergyCalculationStep_ = step; };
     }
-    return nullptr;
+    return std::nullopt;
+}
+
+ISimulatorElement*
+ForceElement::getElementPointerImpl(LegacySimulatorData*                    legacySimulatorData,
+                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                    StatePropagatorData*                    statePropagatorData,
+                                    EnergyData*                             energyData,
+                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                    GlobalCommunicationHelper gmx_unused* globalCommunicationHelper)
+{
+    const bool isVerbose    = legacySimulatorData->mdrunOptions.verbose;
+    const bool isDynamicBox = inputrecDynamicBox(legacySimulatorData->inputrec);
+    return builderHelper->storeElement(std::make_unique<ForceElement>(
+            statePropagatorData, energyData, freeEnergyPerturbationData, isVerbose, isDynamicBox,
+            legacySimulatorData->fplog, legacySimulatorData->cr, legacySimulatorData->inputrec,
+            legacySimulatorData->mdAtoms, legacySimulatorData->nrnb, legacySimulatorData->fr,
+            legacySimulatorData->wcycle, legacySimulatorData->runScheduleWork, legacySimulatorData->vsite,
+            legacySimulatorData->imdSession, legacySimulatorData->pull_work, legacySimulatorData->constr,
+            legacySimulatorData->top_global, legacySimulatorData->enforcedRotation));
 }
 } // namespace gmx
index a244bc432cf42dde731363642703e626d9482eed..0ee81e0430a0fa505d2a3896e1f9d96644ded86f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the force element for the modular simulator
  *
+ * This element calculates the forces, with or without shells or
+ * flexible constraints.
+ *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_FORCEELEMENT_H
 #include "topologyholder.h"
 
 struct gmx_enfrot;
+struct gmx_shellfc_t;
 struct gmx_wallcycle;
 struct pull_t;
-struct t_fcdata;
 struct t_nrnb;
 
 namespace gmx
 {
 class Awh;
-class EnergyElement;
-class FreeEnergyPerturbationElement;
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
 class ImdSession;
+class LegacySimulatorData;
 class MDAtoms;
 class MdrunScheduleWorkload;
+class ModularSimulatorAlgorithmBuilderHelper;
 class StatePropagatorData;
+class VirtualSitesHandler;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Force element
  *
- * The force element manages the call to do_force(...)
+ * The force element manages the call to either
+ * do_force(...) or relax_shell_flexcon(...)
  */
 class ForceElement final :
     public ISimulatorElement,
@@ -81,47 +91,73 @@ class ForceElement final :
 {
 public:
     //! Constructor
-    ForceElement(StatePropagatorData*           statePropagatorData,
-                 EnergyElement*                 energyElement,
-                 FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                 bool                           isDynamicBox,
-                 FILE*                          fplog,
-                 const t_commrec*               cr,
-                 const t_inputrec*              inputrec,
-                 const MDAtoms*                 mdAtoms,
-                 t_nrnb*                        nrnb,
-                 t_forcerec*                    fr,
-                 t_fcdata*                      fcd,
-                 gmx_wallcycle*                 wcycle,
-                 MdrunScheduleWorkload*         runScheduleWork,
-                 gmx_vsite_t*                   vsite,
-                 ImdSession*                    imdSession,
-                 pull_t*                        pull_work,
-                 gmx_enfrot*                    enforcedRotation);
+    ForceElement(StatePropagatorData*        statePropagatorData,
+                 EnergyData*                 energyData,
+                 FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                 bool                        isVerbose,
+                 bool                        isDynamicBox,
+                 FILE*                       fplog,
+                 const t_commrec*            cr,
+                 const t_inputrec*           inputrec,
+                 const MDAtoms*              mdAtoms,
+                 t_nrnb*                     nrnb,
+                 t_forcerec*                 fr,
+                 gmx_wallcycle*              wcycle,
+                 MdrunScheduleWorkload*      runScheduleWork,
+                 VirtualSitesHandler*        vsite,
+                 ImdSession*                 imdSession,
+                 pull_t*                     pull_work,
+                 Constraints*                constr,
+                 const gmx_mtop_t*           globalTopology,
+                 gmx_enfrot*                 enforcedRotation);
 
     /*! \brief Register force calculation for step / time
      *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
      */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
 
     //! Check that we got the local topology
     void elementSetup() override;
-    //! No element teardown needed
-    void elementTeardown() override {}
+    //! Print some final output
+    void elementTeardown() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper);
 
 private:
     //! ITopologyHolderClient implementation
     void setTopology(const gmx_localtop_t* top) override;
     //! INeighborSearchSignallerClient implementation
-    SignallerCallbackPtr registerNSCallback() override;
+    std::optional<SignallerCallback> registerNSCallback() override;
     //! IEnergySignallerClient implementation
-    SignallerCallbackPtr registerEnergyCallback(EnergySignallerEvent event) override;
+    std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent event) override;
     //! The actual do_force call
+    template<bool doShellFC>
     void run(Step step, Time time, unsigned int flags);
 
+    //! The shell / FC helper struct
+    gmx_shellfc_t* shellfc_;
+    //! Whether shells or flexible constraints are present
+    const bool doShellFC_;
+
     //! The next NS step
     Step nextNSStep_;
     //! The next energy calculation step
@@ -131,18 +167,23 @@ private:
     //! The next free energy calculation step
     Step nextFreeEnergyCalculationStep_;
 
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
     //! Pointer to the micro state
     StatePropagatorData* statePropagatorData_;
-    //! Pointer to the energy element
-    EnergyElement* energyElement_;
-    //! Pointer to the free energy perturbation element
-    FreeEnergyPerturbationElement* freeEnergyPerturbationElement_;
+    //! Pointer to the energy data
+    EnergyData* energyData_;
+    //! Pointer to the free energy perturbation data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
 
     //! The local topology - updated by Topology via Client system
     const gmx_localtop_t* localTopology_;
 
     //! Whether we're having a dynamic box
     const bool isDynamicBox_;
+    //! Whether we're being verbose
+    const bool isVerbose_;
+    //! The number of shell relaxation steps we did
+    Step nShellRelaxationSteps_;
 
     //! DD / DLB helper object
     const DDBalanceRegionHandler ddBalanceRegionHandler_;
@@ -170,15 +211,15 @@ private:
     //! Parameters for force calculations.
     t_forcerec* fr_;
     //! Handles virtual sites.
-    gmx_vsite_t* vsite_;
+    VirtualSitesHandler* vsite_;
     //! The Interactive Molecular Dynamics session.
     ImdSession* imdSession_;
     //! The pull work object.
     pull_t* pull_work_;
-    //! Helper struct for force calculations.
-    t_fcdata* fcd_;
     //! Schedule of work for each MD step for this task.
     MdrunScheduleWorkload* runScheduleWork_;
+    //! Handles constraints.
+    Constraints* constr_;
     //! Handles enforced rotation.
     gmx_enfrot* enforcedRotation_;
 };
diff --git a/src/gromacs/modularsimulator/freeenergyperturbationdata.cpp b/src/gromacs/modularsimulator/freeenergyperturbationdata.cpp
new file mode 100644 (file)
index 0000000..ef1baf0
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Defines the free energy perturbation element for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ */
+
+#include "gmxpre.h"
+
+#include "freeenergyperturbationdata.h"
+
+#include "gromacs/domdec/domdec_network.h"
+#include "gromacs/mdlib/freeenergyparameters.h"
+#include "gromacs/mdlib/md_support.h"
+#include "gromacs/mdlib/mdatoms.h"
+#include "gromacs/mdtypes/checkpointdata.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/mdtypes/state.h"
+
+#include "modularsimulator.h"
+#include "simulatoralgorithm.h"
+
+namespace gmx
+{
+
+FreeEnergyPerturbationData::FreeEnergyPerturbationData(FILE* fplog, const t_inputrec* inputrec, MDAtoms* mdAtoms) :
+    element_(std::make_unique<Element>(this, inputrec->fepvals->delta_lambda)),
+    lambda_(),
+    currentFEPState_(0),
+    fplog_(fplog),
+    inputrec_(inputrec),
+    mdAtoms_(mdAtoms)
+{
+    lambda_.fill(0);
+    // The legacy implementation only filled the lambda vector in state_global, which is only
+    // available on master. We have the lambda vector available everywhere, so we pass a `true`
+    // for isMaster on all ranks. See #3647.
+    initialize_lambdas(fplog_, *inputrec_, true, &currentFEPState_, lambda_);
+}
+
+void FreeEnergyPerturbationData::Element::scheduleTask(Step step,
+                                                       Time gmx_unused            time,
+                                                       const RegisterRunFunction& registerRunFunction)
+{
+    if (lambdasChange_)
+    {
+        registerRunFunction([this, step]() { freeEnergyPerturbationData_->updateLambdas(step); });
+    }
+}
+
+void FreeEnergyPerturbationData::updateLambdas(Step step)
+{
+    // at beginning of step (if lambdas change...)
+    lambda_ = currentLambdas(step, *(inputrec_->fepvals), currentFEPState_);
+    updateMDAtoms();
+}
+
+ArrayRef<real> FreeEnergyPerturbationData::lambdaView()
+{
+    return lambda_;
+}
+
+ArrayRef<const real> FreeEnergyPerturbationData::constLambdaView()
+{
+    return lambda_;
+}
+
+int FreeEnergyPerturbationData::currentFEPState()
+{
+    return currentFEPState_;
+}
+
+void FreeEnergyPerturbationData::updateMDAtoms()
+{
+    update_mdatoms(mdAtoms_->mdatoms(), lambda_[efptMASS]);
+}
+
+namespace
+{
+/*!
+ * \brief Enum describing the contents FreeEnergyPerturbationData::Element writes to modular checkpoint
+ *
+ * When changing the checkpoint content, add a new element just above Count, and adjust the
+ * checkpoint functionality.
+ */
+enum class CheckpointVersion
+{
+    Base, //!< First version of modular checkpointing
+    Count //!< Number of entries. Add new versions right above this!
+};
+constexpr auto c_currentVersion = CheckpointVersion(int(CheckpointVersion::Count) - 1);
+} // namespace
+
+template<CheckpointDataOperation operation>
+void FreeEnergyPerturbationData::Element::doCheckpointData(CheckpointData<operation>* checkpointData)
+{
+    checkpointVersion(checkpointData, "FreeEnergyPerturbationData version", c_currentVersion);
+
+    checkpointData->scalar("current FEP state", &freeEnergyPerturbationData_->currentFEPState_);
+    checkpointData->arrayRef("lambda vector",
+                             makeCheckpointArrayRef<operation>(freeEnergyPerturbationData_->lambda_));
+}
+
+void FreeEnergyPerturbationData::Element::saveCheckpointState(std::optional<WriteCheckpointData> checkpointData,
+                                                              const t_commrec*                   cr)
+{
+    if (MASTER(cr))
+    {
+        doCheckpointData<CheckpointDataOperation::Write>(&checkpointData.value());
+    }
+}
+
+void FreeEnergyPerturbationData::Element::restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData,
+                                                                 const t_commrec* cr)
+{
+    if (MASTER(cr))
+    {
+        doCheckpointData<CheckpointDataOperation::Read>(&checkpointData.value());
+    }
+    if (DOMAINDECOMP(cr))
+    {
+        dd_bcast(cr->dd, sizeof(int), &freeEnergyPerturbationData_->currentFEPState_);
+        dd_bcast(cr->dd, freeEnergyPerturbationData_->lambda_.size() * sizeof(real),
+                 freeEnergyPerturbationData_->lambda_.data());
+    }
+}
+
+const std::string& FreeEnergyPerturbationData::Element::clientID()
+{
+    return identifier_;
+}
+
+FreeEnergyPerturbationData::Element::Element(FreeEnergyPerturbationData* freeEnergyPerturbationElement,
+                                             double                      deltaLambda) :
+    freeEnergyPerturbationData_(freeEnergyPerturbationElement),
+    lambdasChange_(deltaLambda != 0)
+{
+}
+
+void FreeEnergyPerturbationData::Element::elementSetup()
+{
+    freeEnergyPerturbationData_->updateMDAtoms();
+}
+
+FreeEnergyPerturbationData::Element* FreeEnergyPerturbationData::element()
+{
+    return element_.get();
+}
+
+ISimulatorElement* FreeEnergyPerturbationData::Element::getElementPointerImpl(
+        LegacySimulatorData gmx_unused*        legacySimulatorData,
+        ModularSimulatorAlgorithmBuilderHelper gmx_unused* builderHelper,
+        StatePropagatorData gmx_unused* statePropagatorData,
+        EnergyData gmx_unused*      energyData,
+        FreeEnergyPerturbationData* freeEnergyPerturbationData,
+        GlobalCommunicationHelper gmx_unused* globalCommunicationHelper)
+{
+    return freeEnergyPerturbationData->element();
+}
+
+} // namespace gmx
similarity index 50%
rename from src/gromacs/modularsimulator/freeenergyperturbationelement.h
rename to src/gromacs/modularsimulator/freeenergyperturbationdata.h
index d1d95a5e3a1adcc83293c77df94a87264d9077ca..b1291dfde672b3a3004c4fbe01c4b7291e8827b4 100644 (file)
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the free energy perturbation element for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_FREEENERGYPERTURBATIONELEMENT_H
@@ -52,24 +54,28 @@ struct t_inputrec;
 
 namespace gmx
 {
+enum class CheckpointDataOperation;
+class EnergyData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
 class MDAtoms;
+class ModularSimulatorAlgorithmBuilderHelper;
+class StatePropagatorData;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
- * \brief The free energy perturbation element
+ * \brief The free energy perturbation data
  *
  * The lambda vector and the current FEP state are held by the
- * FreeEnergyPerturbationElement, offering access to its values via getter
- * functions. The FreeEnergyPerturbationElement does update the lambda
- * values during the simulation run if lambda is non-static. It does
- * implement the checkpointing client interface to save its current
- * state for restart.
+ * FreeEnergyPerturbationData, offering access to its values via getter
+ * functions. The FreeEnergyPerturbationData::Element is responsible for
+ * lambda update (if applicable) and checkpointing.
  */
-class FreeEnergyPerturbationElement final : public ISimulatorElement, public ICheckpointHelperClient
+class FreeEnergyPerturbationData final
 {
 public:
     //! Constructor
-    FreeEnergyPerturbationElement(FILE* fplog, const t_inputrec* inputrec, MDAtoms* mdAtoms);
+    FreeEnergyPerturbationData(FILE* fplog, const t_inputrec* inputrec, MDAtoms* mdAtoms);
 
     //! Get a view of the current lambda vector
     ArrayRef<real> lambdaView();
@@ -80,31 +86,23 @@ public:
     //! Update MDAtoms (public because it's called by DomDec - see #3700)
     void updateMDAtoms();
 
-    //! Update lambda and mdatoms
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
-
-    //! No setup needed
-    void elementSetup() override{};
-
-    //! No teardown needed
-    void elementTeardown() override{};
+    //! The element taking part in the simulator loop
+    class Element;
+    //! Get pointer to element (whose lifetime is managed by this)
+    Element* element();
 
 private:
-    //! ICheckpointHelperClient implementation
-    void writeCheckpoint(t_state* localState, t_state* globalState) override;
     //! Update the lambda values
     void updateLambdas(Step step);
 
+    //! The element
+    std::unique_ptr<Element> element_;
+
     //! The lambda vector
     std::array<real, efptNR> lambda_;
-    //! The starting lambda vector
-    std::array<double, efptNR> lambda0_;
     //! The current free energy state
     int currentFEPState_;
 
-    //! Whether lambda values are non-static
-    const bool lambdasChange_;
-
     //! Handles logging.
     FILE* fplog_;
     //! Contains user input mdp options.
@@ -113,6 +111,70 @@ private:
     MDAtoms* mdAtoms_;
 };
 
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief The free energy perturbation data element
+ *
+ * The FreeEnergyPerturbationData::Element does update the lambda
+ * values during the simulation run if lambda is non-static. It does
+ * implement the checkpointing client interface to save its current
+ * state for restart.
+ */
+class FreeEnergyPerturbationData::Element final : public ISimulatorElement, public ICheckpointHelperClient
+{
+public:
+    //! Constructor
+    explicit Element(FreeEnergyPerturbationData* freeEnergyPerturbationElement, double deltaLambda);
+
+    //! Update lambda and mdatoms
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! Update the MdAtoms object
+    void elementSetup() override;
+
+    //! No teardown needed
+    void elementTeardown() override{};
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper);
+
+private:
+    //! The free energy data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
+    //! Whether lambda values are non-static
+    const bool lambdasChange_;
+
+
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "FreeEnergyPerturbationElement";
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+};
+
 } // namespace gmx
 
 #endif // GMX_MODULARSIMULATOR_FREEENERGYPERTURBATIONELEMENT_H
diff --git a/src/gromacs/modularsimulator/freeenergyperturbationelement.cpp b/src/gromacs/modularsimulator/freeenergyperturbationelement.cpp
deleted file mode 100644 (file)
index f44920f..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2019,2020, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-/*! \internal \file
- * \brief Defines the free energy perturbation element for the modular simulator
- *
- * \author Pascal Merz <pascal.merz@me.com>
- * \ingroup module_modularsimulator
- */
-
-#include "gmxpre.h"
-
-#include "freeenergyperturbationelement.h"
-
-#include "gromacs/mdlib/md_support.h"
-#include "gromacs/mdlib/mdatoms.h"
-#include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/mdtypes/mdatom.h"
-#include "gromacs/mdtypes/state.h"
-
-namespace gmx
-{
-
-FreeEnergyPerturbationElement::FreeEnergyPerturbationElement(FILE*             fplog,
-                                                             const t_inputrec* inputrec,
-                                                             MDAtoms*          mdAtoms) :
-    lambda_(),
-    lambda0_(),
-    currentFEPState_(0),
-    lambdasChange_(inputrec->fepvals->delta_lambda != 0),
-    fplog_(fplog),
-    inputrec_(inputrec),
-    mdAtoms_(mdAtoms)
-{
-    lambda_.fill(0);
-    lambda0_.fill(0);
-    initialize_lambdas(fplog_, *inputrec_, true, &currentFEPState_, lambda_, lambda0_.data());
-    updateMDAtoms();
-}
-
-void FreeEnergyPerturbationElement::scheduleTask(Step step,
-                                                 Time gmx_unused               time,
-                                                 const RegisterRunFunctionPtr& registerRunFunction)
-{
-    if (lambdasChange_)
-    {
-        (*registerRunFunction)(
-                std::make_unique<SimulatorRunFunction>([this, step]() { updateLambdas(step); }));
-    }
-}
-
-void FreeEnergyPerturbationElement::updateLambdas(Step step)
-{
-    // at beginning of step (if lambdas change...)
-    setCurrentLambdasLocal(step, inputrec_->fepvals, lambda0_.data(), lambda_, currentFEPState_);
-    updateMDAtoms();
-}
-
-ArrayRef<real> FreeEnergyPerturbationElement::lambdaView()
-{
-    return lambda_;
-}
-
-ArrayRef<const real> FreeEnergyPerturbationElement::constLambdaView()
-{
-    return lambda_;
-}
-
-int FreeEnergyPerturbationElement::currentFEPState()
-{
-    return currentFEPState_;
-}
-
-void FreeEnergyPerturbationElement::updateMDAtoms()
-{
-    update_mdatoms(mdAtoms_->mdatoms(), lambda_[efptMASS]);
-}
-
-void FreeEnergyPerturbationElement::writeCheckpoint(t_state* localState, t_state gmx_unused* globalState)
-{
-    localState->fep_state = currentFEPState_;
-    localState->lambda    = lambda_;
-    localState->flags |= (1U << estLAMBDA) | (1U << estFEPSTATE);
-}
-
-} // namespace gmx
index 2d785ab50fa68c0645b8fc56fa0c76b47ae76ce5..2b31b9f11eee7a81cf79026c5d164e555f9cd751 100644 (file)
 #include "gromacs/domdec/domdec.h"
 #include "gromacs/ewald/pme.h"
 #include "gromacs/ewald/pme_load_balancing.h"
-#include "gromacs/gmxlib/network.h"
+#include "gromacs/ewald/pme_pp.h"
 #include "gromacs/gmxlib/nrnb.h"
-#include "gromacs/math/vec.h"
+#include "gromacs/listed_forces/listed_forces.h"
 #include "gromacs/mdlib/checkpointhandler.h"
 #include "gromacs/mdlib/constr.h"
+#include "gromacs/mdlib/coupling.h"
 #include "gromacs/mdlib/energyoutput.h"
-#include "gromacs/mdlib/forcerec.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/resethandler.h"
-#include "gromacs/mdlib/stat.h"
 #include "gromacs/mdlib/update.h"
 #include "gromacs/mdrun/replicaexchange.h"
 #include "gromacs/mdrun/shellfc.h"
 #include "gromacs/mdrunutility/printtime.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/fcdata.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/mdrunoptions.h"
 #include "gromacs/mdtypes/observableshistory.h"
-#include "gromacs/mdtypes/state.h"
 #include "gromacs/nbnxm/nbnxm.h"
-#include "gromacs/timing/walltime_accounting.h"
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/topology/topology.h"
-#include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
 
-#include "compositesimulatorelement.h"
 #include "computeglobalselement.h"
 #include "constraintelement.h"
-#include "energyelement.h"
 #include "forceelement.h"
-#include "freeenergyperturbationelement.h"
 #include "parrinellorahmanbarostat.h"
-#include "propagator.h"
-#include "shellfcelement.h"
-#include "signallers.h"
+#include "simulatoralgorithm.h"
 #include "statepropagatordata.h"
-#include "trajectoryelement.h"
-#include "vrescalethermostat.h"
+#include "velocityscalingtemperaturecoupling.h"
 
 namespace gmx
 {
 void ModularSimulator::run()
 {
-    GMX_LOG(mdlog.info).asParagraph().appendText("Using the modular simulator.");
-    constructElementsAndSignallers();
-    simulatorSetup();
-    for (auto& signaller : signallerCallList_)
-    {
-        signaller->signallerSetup();
-    }
-    if (domDecHelper_)
-    {
-        domDecHelper_->setup();
-    }
-
-    for (auto& element : elementsOwnershipList_)
-    {
-        element->elementSetup();
-    }
-    if (pmeLoadBalanceHelper_)
-    {
-        // State must have been initialized so pmeLoadBalanceHelper_ gets a valid box
-        pmeLoadBalanceHelper_->setup();
-    }
-
-    while (step_ <= signalHelper_->lastStep_)
-    {
-        populateTaskQueue();
-
-        while (!taskQueue_.empty())
-        {
-            auto task = std::move(taskQueue_.front());
-            taskQueue_.pop();
-            // run function
-            (*task)();
-        }
-    }
-
-    for (auto& element : elementsOwnershipList_)
-    {
-        element->elementTeardown();
-    }
-    if (pmeLoadBalanceHelper_)
-    {
-        pmeLoadBalanceHelper_->teardown();
-    }
-    simulatorTeardown();
-}
-
-void ModularSimulator::simulatorSetup()
-{
-    if (!mdrunOptions.writeConfout)
-    {
-        // This is on by default, and the main known use case for
-        // turning it off is for convenience in benchmarking, which is
-        // something that should not show up in the general user
-        // interface.
-        GMX_LOG(mdlog.info)
-                .asParagraph()
-                .appendText(
-                        "The -noconfout functionality is deprecated, and "
-                        "may be removed in a future version.");
-    }
-
-    if (MASTER(cr))
-    {
-        char        sbuf[STEPSTRSIZE], sbuf2[STEPSTRSIZE];
-        std::string timeString;
-        fprintf(stderr, "starting mdrun '%s'\n", *(top_global->name));
-        if (inputrec->nsteps >= 0)
-        {
-            timeString = formatString("%8.1f", static_cast<double>(inputrec->init_step + inputrec->nsteps)
-                                                       * inputrec->delta_t);
-        }
-        else
-        {
-            timeString = "infinite";
-        }
-        if (inputrec->init_step > 0)
-        {
-            fprintf(stderr, "%s steps, %s ps (continuing from step %s, %8.1f ps).\n",
-                    gmx_step_str(inputrec->init_step + inputrec->nsteps, sbuf), timeString.c_str(),
-                    gmx_step_str(inputrec->init_step, sbuf2), inputrec->init_step * inputrec->delta_t);
-        }
-        else
-        {
-            fprintf(stderr, "%s steps, %s ps.\n", gmx_step_str(inputrec->nsteps, sbuf),
-                    timeString.c_str());
-        }
-        fprintf(fplog, "\n");
-    }
-
-    walltime_accounting_start_time(walltime_accounting);
-    wallcycle_start(wcycle, ewcRUN);
-    print_start(fplog, cr, walltime_accounting, "mdrun");
-
-    step_ = inputrec->init_step;
-}
-
-void ModularSimulator::preStep(Step step, Time gmx_unused time, bool isNeighborSearchingStep)
-{
-    if (stopHandler_->stoppingAfterCurrentStep(isNeighborSearchingStep) && step != signalHelper_->lastStep_)
-    {
-        /*
-         * Stop handler wants to stop after the current step, which was
-         * not known when building the current task queue. This happens
-         * e.g. when a stop is signalled by OS. We therefore want to purge
-         * the task queue now, and re-schedule this step as last step.
-         */
-        // clear task queue
-        std::queue<SimulatorRunFunctionPtr>().swap(taskQueue_);
-        // rewind step
-        step_ = step;
-        return;
-    }
-
-    resetHandler_->setSignal(walltime_accounting);
-    // This is a hack to avoid having to rewrite StopHandler to be a NeighborSearchSignaller
-    // and accept the step as input. Eventually, we want to do that, but currently this would
-    // require introducing NeighborSearchSignaller in the legacy do_md or a lot of code
-    // duplication.
-    stophandlerIsNSStep_    = isNeighborSearchingStep;
-    stophandlerCurrentStep_ = step;
-    stopHandler_->setSignal();
-
-    wallcycle_start(wcycle, ewcSTEP);
-}
-
-void ModularSimulator::postStep(Step step, Time gmx_unused time)
-{
-    // Output stuff
-    if (MASTER(cr))
-    {
-        if (do_per_step(step, inputrec->nstlog))
-        {
-            if (fflush(fplog) != 0)
-            {
-                gmx_fatal(FARGS, "Cannot flush logfile - maybe you are out of disk space?");
-            }
-        }
-    }
-    const bool do_verbose = mdrunOptions.verbose
-                            && (step % mdrunOptions.verboseStepPrintInterval == 0
-                                || step == inputrec->init_step || step == signalHelper_->lastStep_);
-    // Print the remaining wall clock time for the run
-    if (MASTER(cr) && (do_verbose || gmx_got_usr_signal())
-        && !(pmeLoadBalanceHelper_ && pmeLoadBalanceHelper_->pmePrinting()))
-    {
-        print_time(stderr, walltime_accounting, step, inputrec, cr);
-    }
-
-    double cycles = wallcycle_stop(wcycle, ewcSTEP);
-    if (DOMAINDECOMP(cr) && wcycle)
-    {
-        dd_cycles_add(cr->dd, static_cast<float>(cycles), ddCyclStep);
-    }
-
-    resetHandler_->resetCounters(
-            step, step - inputrec->init_step, mdlog, fplog, cr, fr->nbv.get(), nrnb, fr->pmedata,
-            pmeLoadBalanceHelper_ ? pmeLoadBalanceHelper_->loadBalancingObject() : nullptr, wcycle,
-            walltime_accounting);
-}
-
-void ModularSimulator::simulatorTeardown()
-{
-
-    // Stop measuring walltime
-    walltime_accounting_end_time(walltime_accounting);
-
-    if (!thisRankHasDuty(cr, DUTY_PME))
-    {
-        /* Tell the PME only node to finish */
-        gmx_pme_send_finish(cr);
-    }
-
-    walltime_accounting_set_nsteps_done(walltime_accounting, step_ - inputrec->init_step);
-}
-
-void ModularSimulator::populateTaskQueue()
-{
-    auto registerRunFunction = std::make_unique<RegisterRunFunction>(
-            [this](SimulatorRunFunctionPtr ptr) { taskQueue_.push(std::move(ptr)); });
-
-    Time startTime = inputrec->init_t;
-    Time timeStep  = inputrec->delta_t;
-    Time time      = startTime + step_ * timeStep;
-
-    // Run an initial call to the signallers
-    for (auto& signaller : signallerCallList_)
-    {
-        signaller->signal(step_, time);
-    }
-
-    if (checkpointHelper_)
-    {
-        checkpointHelper_->run(step_, time);
-    }
-
-    if (pmeLoadBalanceHelper_)
-    {
-        pmeLoadBalanceHelper_->run(step_, time);
-    }
-    if (domDecHelper_)
-    {
-        domDecHelper_->run(step_, time);
-    }
-
-    do
-    {
-        // local variables for lambda capturing
-        const int  step     = step_;
-        const bool isNSStep = step == signalHelper_->nextNSStep_;
-
-        // register pre-step
-        (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-                [this, step, time, isNSStep]() { preStep(step, time, isNSStep); }));
-        // register elements for step
-        for (auto& element : elementCallList_)
-        {
-            element->scheduleTask(step_, time, registerRunFunction);
-        }
-        // register post-step
-        (*registerRunFunction)(
-                std::make_unique<SimulatorRunFunction>([this, step, time]() { postStep(step, time); }));
-
-        // prepare next step
-        step_++;
-        time = startTime + step_ * timeStep;
-        for (auto& signaller : signallerCallList_)
-        {
-            signaller->signal(step_, time);
-        }
-    } while (step_ != signalHelper_->nextNSStep_ && step_ <= signalHelper_->lastStep_);
-}
-
-void ModularSimulator::constructElementsAndSignallers()
-{
-    /* When restarting from a checkpoint, it can be appropriate to
-     * initialize ekind from quantities in the checkpoint. Otherwise,
-     * compute_globals must initialize ekind before the simulation
-     * starts/restarts. However, only the master rank knows what was
-     * found in the checkpoint file, so we have to communicate in
-     * order to coordinate the restart.
-     *
-     * TODO (modular) This should become obsolete when checkpoint reading
-     *      happens within the modular simulator framework: The energy
-     *      element should read its data from the checkpoint file pointer,
-     *      and signal to the compute globals element if it needs anything
-     *      reduced.
-     *
-     * TODO (legacy) Consider removing this communication if/when checkpoint
-     *      reading directly follows .tpr reading, because all ranks can
-     *      agree on hasReadEkinState at that time.
-     */
-    bool hasReadEkinState = MASTER(cr) ? state_global->ekinstate.hasReadEkinState : false;
-    if (PAR(cr))
-    {
-        gmx_bcast(sizeof(hasReadEkinState), &hasReadEkinState, cr);
-    }
-    if (hasReadEkinState)
-    {
-        restore_ekinstate_from_state(cr, ekind, &state_global->ekinstate);
-    }
-
-    /*
-     * Build data structures
-     */
-    topologyHolder_ =
-            std::make_unique<TopologyHolder>(*top_global, cr, inputrec, fr, mdAtoms, constr, vsite);
-
-    std::unique_ptr<FreeEnergyPerturbationElement> freeEnergyPerturbationElement    = nullptr;
-    FreeEnergyPerturbationElement*                 freeEnergyPerturbationElementPtr = nullptr;
-    if (inputrec->efep != efepNO)
-    {
-        freeEnergyPerturbationElement =
-                std::make_unique<FreeEnergyPerturbationElement>(fplog, inputrec, mdAtoms);
-        freeEnergyPerturbationElementPtr = freeEnergyPerturbationElement.get();
-    }
-
-    auto statePropagatorData = std::make_unique<StatePropagatorData>(
-            top_global->natoms, fplog, cr, state_global, inputrec->nstxout, inputrec->nstvout,
-            inputrec->nstfout, inputrec->nstxout_compressed, fr->nbv->useGpu(),
-            freeEnergyPerturbationElementPtr, topologyHolder_.get(), fr->bMolPBC,
-            mdrunOptions.writeConfout, opt2fn("-c", nfile, fnm), inputrec, mdAtoms->mdatoms());
-    auto statePropagatorDataPtr = compat::make_not_null(statePropagatorData.get());
-
-    auto energyElement = std::make_unique<EnergyElement>(
-            statePropagatorDataPtr, freeEnergyPerturbationElementPtr, top_global, inputrec, mdAtoms,
-            enerd, ekind, constr, fplog, fcd, mdModulesNotifier, MASTER(cr), observablesHistory,
-            startingBehavior);
-    auto energyElementPtr = compat::make_not_null(energyElement.get());
-
-    /*
-     * Build stop handler
-     */
-    const bool simulationsShareState = false;
-    stopHandler_                     = stopHandlerBuilder->getStopHandlerMD(
-            compat::not_null<SimulationSignal*>(&signals_[eglsSTOPCOND]), simulationsShareState,
-            MASTER(cr), inputrec->nstlist, mdrunOptions.reproducible, nstglobalcomm_,
-            mdrunOptions.maximumHoursToRun, inputrec->nstlist == 0, fplog, stophandlerCurrentStep_,
-            stophandlerIsNSStep_, walltime_accounting);
-
-    /*
-     * Create simulator builders
-     */
-    SignallerBuilder<NeighborSearchSignaller> neighborSearchSignallerBuilder;
-    SignallerBuilder<LastStepSignaller>       lastStepSignallerBuilder;
-    SignallerBuilder<LoggingSignaller>        loggingSignallerBuilder;
-    SignallerBuilder<EnergySignaller>         energySignallerBuilder;
-    TrajectoryElementBuilder                  trajectoryElementBuilder;
-
-    /*
-     * Register data structures to signallers
-     */
-    trajectoryElementBuilder.registerWriterClient(statePropagatorDataPtr);
-    trajectoryElementBuilder.registerSignallerClient(statePropagatorDataPtr);
-    lastStepSignallerBuilder.registerSignallerClient(statePropagatorDataPtr);
+    GMX_LOG(legacySimulatorData_->mdlog.info)
+            .asParagraph()
+            .appendText("Using the modular simulator.");
 
-    trajectoryElementBuilder.registerWriterClient(energyElementPtr);
-    trajectoryElementBuilder.registerSignallerClient(energyElementPtr);
-    energySignallerBuilder.registerSignallerClient(energyElementPtr);
-
-    // Register the simulator itself to the neighbor search / last step signaller
-    neighborSearchSignallerBuilder.registerSignallerClient(compat::make_not_null(signalHelper_.get()));
-    lastStepSignallerBuilder.registerSignallerClient(compat::make_not_null(signalHelper_.get()));
-
-    /*
-     * Build integrator - this takes care of force calculation, propagation,
-     * constraining, and of the place the statePropagatorData and the energy element
-     * have a full timestep state.
-     */
-    // TODO: Make a CheckpointHelperBuilder
-    std::vector<ICheckpointHelperClient*> checkpointClients = { statePropagatorDataPtr, energyElementPtr,
-                                                                freeEnergyPerturbationElementPtr };
-    CheckBondedInteractionsCallbackPtr checkBondedInteractionsCallback = nullptr;
-    auto                               integrator =
-            buildIntegrator(&neighborSearchSignallerBuilder, &energySignallerBuilder,
-                            &loggingSignallerBuilder, &trajectoryElementBuilder, &checkpointClients,
-                            &checkBondedInteractionsCallback, statePropagatorDataPtr,
-                            energyElementPtr, freeEnergyPerturbationElementPtr, hasReadEkinState);
-
-    /*
-     * Build infrastructure elements
-     */
-
-    if (PmeLoadBalanceHelper::doPmeLoadBalancing(mdrunOptions, inputrec, fr))
-    {
-        pmeLoadBalanceHelper_ = std::make_unique<PmeLoadBalanceHelper>(
-                mdrunOptions.verbose, statePropagatorDataPtr, fplog, cr, mdlog, inputrec, wcycle, fr);
-        neighborSearchSignallerBuilder.registerSignallerClient(
-                compat::make_not_null(pmeLoadBalanceHelper_.get()));
-    }
-
-    if (DOMAINDECOMP(cr))
-    {
-        GMX_ASSERT(checkBondedInteractionsCallback,
-                   "Domain decomposition needs a callback for check the number of bonded "
-                   "interactions.");
-        domDecHelper_ = std::make_unique<DomDecHelper>(
-                mdrunOptions.verbose, mdrunOptions.verboseStepPrintInterval, statePropagatorDataPtr,
-                freeEnergyPerturbationElementPtr, topologyHolder_.get(),
-                std::move(checkBondedInteractionsCallback), nstglobalcomm_, fplog, cr, mdlog,
-                constr, inputrec, mdAtoms, nrnb, wcycle, fr, vsite, imdSession, pull_work);
-        neighborSearchSignallerBuilder.registerSignallerClient(compat::make_not_null(domDecHelper_.get()));
-    }
-
-    const bool simulationsShareResetCounters = false;
-    resetHandler_                            = std::make_unique<ResetHandler>(
-            compat::make_not_null<SimulationSignal*>(&signals_[eglsRESETCOUNTERS]),
-            simulationsShareResetCounters, inputrec->nsteps, MASTER(cr),
-            mdrunOptions.timingOptions.resetHalfway, mdrunOptions.maximumHoursToRun, mdlog, wcycle,
-            walltime_accounting);
-
-    /*
-     * Build signaller list
-     *
-     * Note that as signallers depend on each others, the order of calling the signallers
-     * matters. It is the responsibility of this builder to ensure that the order is
-     * maintained.
-     */
-    auto energySignaller = energySignallerBuilder.build(
-            inputrec->nstcalcenergy, inputrec->fepvals->nstdhdl, inputrec->nstpcouple);
-    trajectoryElementBuilder.registerSignallerClient(compat::make_not_null(energySignaller.get()));
-    loggingSignallerBuilder.registerSignallerClient(compat::make_not_null(energySignaller.get()));
-    auto trajectoryElement = trajectoryElementBuilder.build(
-            fplog, nfile, fnm, mdrunOptions, cr, outputProvider, mdModulesNotifier, inputrec,
-            top_global, oenv, wcycle, startingBehavior, simulationsShareState);
-    loggingSignallerBuilder.registerSignallerClient(compat::make_not_null(trajectoryElement.get()));
-
-    // Add checkpoint helper here since we need a pointer to the trajectory element and
-    // need to register it with the lastStepSignallerBuilder
-    auto checkpointHandler = std::make_unique<CheckpointHandler>(
-            compat::make_not_null<SimulationSignal*>(&signals_[eglsCHKPT]), simulationsShareState,
-            inputrec->nstlist == 0, MASTER(cr), mdrunOptions.writeConfout,
-            mdrunOptions.checkpointOptions.period);
-    checkpointHelper_ = std::make_unique<CheckpointHelper>(
-            std::move(checkpointClients), std::move(checkpointHandler), inputrec->init_step,
-            trajectoryElement.get(), top_global->natoms, fplog, cr, observablesHistory,
-            walltime_accounting, state_global, mdrunOptions.writeConfout);
-    lastStepSignallerBuilder.registerSignallerClient(compat::make_not_null(checkpointHelper_.get()));
-
-    lastStepSignallerBuilder.registerSignallerClient(compat::make_not_null(trajectoryElement.get()));
-    auto loggingSignaller =
-            loggingSignallerBuilder.build(inputrec->nstlog, inputrec->init_step, inputrec->init_t);
-    lastStepSignallerBuilder.registerSignallerClient(compat::make_not_null(loggingSignaller.get()));
-    auto lastStepSignaller =
-            lastStepSignallerBuilder.build(inputrec->nsteps, inputrec->init_step, stopHandler_.get());
-    neighborSearchSignallerBuilder.registerSignallerClient(compat::make_not_null(lastStepSignaller.get()));
-    auto neighborSearchSignaller = neighborSearchSignallerBuilder.build(
-            inputrec->nstlist, inputrec->init_step, inputrec->init_t);
-
-    addToCallListAndMove(std::move(neighborSearchSignaller), signallerCallList_, signallersOwnershipList_);
-    addToCallListAndMove(std::move(lastStepSignaller), signallerCallList_, signallersOwnershipList_);
-    addToCallListAndMove(std::move(loggingSignaller), signallerCallList_, signallersOwnershipList_);
-    addToCallList(trajectoryElement, signallerCallList_);
-    addToCallListAndMove(std::move(energySignaller), signallerCallList_, signallersOwnershipList_);
-
-    /*
-     * Build the element list
-     *
-     * This is the actual sequence of (non-infrastructure) elements to be run.
-     * For NVE, the trajectory element is used outside of the integrator
-     * (composite) element, as well as the checkpoint helper. The checkpoint
-     * helper should be on top of the loop, and is only part of the simulator
-     * call list to be able to react to the last step being signalled.
-     */
-    addToCallList(checkpointHelper_, elementCallList_);
-    if (freeEnergyPerturbationElement)
-    {
-        addToCallListAndMove(std::move(freeEnergyPerturbationElement), elementCallList_,
-                             elementsOwnershipList_);
-    }
-    addToCallListAndMove(std::move(integrator), elementCallList_, elementsOwnershipList_);
-    addToCallListAndMove(std::move(trajectoryElement), elementCallList_, elementsOwnershipList_);
-    // for vv, we need to setup statePropagatorData after the compute
-    // globals so that we reset the right velocities
-    // TODO: Avoid this by getting rid of the need of resetting velocities in vv
-    elementsOwnershipList_.emplace_back(std::move(statePropagatorData));
-    elementsOwnershipList_.emplace_back(std::move(energyElement));
-}
-
-std::unique_ptr<ISimulatorElement>
-ModularSimulator::buildForces(SignallerBuilder<NeighborSearchSignaller>* neighborSearchSignallerBuilder,
-                              SignallerBuilder<EnergySignaller>*         energySignallerBuilder,
-                              StatePropagatorData*                       statePropagatorDataPtr,
-                              EnergyElement*                             energyElementPtr,
-                              FreeEnergyPerturbationElement* freeEnergyPerturbationElement)
-{
-    const bool isVerbose    = mdrunOptions.verbose;
-    const bool isDynamicBox = inputrecDynamicBox(inputrec);
-    // Check for polarizable models and flexible constraints
-    if (ShellFCElement::doShellsOrFlexConstraints(topologyHolder_->globalTopology(),
-                                                  constr ? constr->numFlexibleConstraints() : 0))
-    {
-        auto shellFCElement = std::make_unique<ShellFCElement>(
-                statePropagatorDataPtr, energyElementPtr, freeEnergyPerturbationElement, isVerbose,
-                isDynamicBox, fplog, cr, inputrec, mdAtoms, nrnb, fr, fcd, wcycle, runScheduleWork, vsite,
-                imdSession, pull_work, constr, &topologyHolder_->globalTopology(), enforcedRotation);
-        topologyHolder_->registerClient(shellFCElement.get());
-        neighborSearchSignallerBuilder->registerSignallerClient(
-                compat::make_not_null(shellFCElement.get()));
-        energySignallerBuilder->registerSignallerClient(compat::make_not_null(shellFCElement.get()));
+    ModularSimulatorAlgorithmBuilder algorithmBuilder(compat::make_not_null(legacySimulatorData_),
+                                                      std::move(checkpointDataHolder_));
+    addIntegrationElements(&algorithmBuilder);
+    auto algorithm = algorithmBuilder.build();
 
-        // std::move *should* not be needed with c++-14, but clang-3.6 still requires it
-        return std::move(shellFCElement);
-    }
-    else
+    while (const auto* task = algorithm.getNextTask())
     {
-        auto forceElement = std::make_unique<ForceElement>(
-                statePropagatorDataPtr, energyElementPtr, freeEnergyPerturbationElement,
-                isDynamicBox, fplog, cr, inputrec, mdAtoms, nrnb, fr, fcd, wcycle, runScheduleWork,
-                vsite, imdSession, pull_work, enforcedRotation);
-        topologyHolder_->registerClient(forceElement.get());
-        neighborSearchSignallerBuilder->registerSignallerClient(compat::make_not_null(forceElement.get()));
-        energySignallerBuilder->registerSignallerClient(compat::make_not_null(forceElement.get()));
-
-        // std::move *should* not be needed with c++-14, but clang-3.6 still requires it
-        return std::move(forceElement);
+        // execute task
+        (*task)();
     }
 }
 
-std::unique_ptr<ISimulatorElement> ModularSimulator::buildIntegrator(
-        SignallerBuilder<NeighborSearchSignaller>* neighborSearchSignallerBuilder,
-        SignallerBuilder<EnergySignaller>*         energySignallerBuilder,
-        SignallerBuilder<LoggingSignaller>*        loggingSignallerBuilder,
-        TrajectoryElementBuilder*                  trajectoryElementBuilder,
-        std::vector<ICheckpointHelperClient*>*     checkpointClients,
-        CheckBondedInteractionsCallbackPtr*        checkBondedInteractionsCallback,
-        compat::not_null<StatePropagatorData*>     statePropagatorDataPtr,
-        compat::not_null<EnergyElement*>           energyElementPtr,
-        FreeEnergyPerturbationElement*             freeEnergyPerturbationElementPtr,
-        bool                                       hasReadEkinState)
+void ModularSimulator::addIntegrationElements(ModularSimulatorAlgorithmBuilder* builder)
 {
-    auto forceElement =
-            buildForces(neighborSearchSignallerBuilder, energySignallerBuilder,
-                        statePropagatorDataPtr, energyElementPtr, freeEnergyPerturbationElementPtr);
-
-    // list of elements owned by the simulator composite object
-    std::vector<std::unique_ptr<ISimulatorElement>> elementsOwnershipList;
-    // call list of the simulator composite object
-    std::vector<compat::not_null<ISimulatorElement*>> elementCallList;
-
-    std::function<void()> needToCheckNumberOfBondedInteractions;
-    if (inputrec->eI == eiMD)
+    if (legacySimulatorData_->inputrec->eI == eiMD)
     {
-        auto computeGlobalsElement =
-                std::make_unique<ComputeGlobalsElement<ComputeGlobalsAlgorithm::LeapFrog>>(
-                        statePropagatorDataPtr, energyElementPtr, freeEnergyPerturbationElementPtr,
-                        &signals_, nstglobalcomm_, fplog, mdlog, cr, inputrec, mdAtoms, nrnb,
-                        wcycle, fr, &topologyHolder_->globalTopology(), constr, hasReadEkinState);
-        topologyHolder_->registerClient(computeGlobalsElement.get());
-        energySignallerBuilder->registerSignallerClient(compat::make_not_null(computeGlobalsElement.get()));
-        trajectoryElementBuilder->registerSignallerClient(
-                compat::make_not_null(computeGlobalsElement.get()));
-
-        *checkBondedInteractionsCallback =
-                computeGlobalsElement->getCheckNumberOfBondedInteractionsCallback();
-
-        auto propagator = std::make_unique<Propagator<IntegrationStep::LeapFrog>>(
-                inputrec->delta_t, statePropagatorDataPtr, mdAtoms, wcycle);
-
-        addToCallListAndMove(std::move(forceElement), elementCallList, elementsOwnershipList);
-        addToCallList(statePropagatorDataPtr, elementCallList); // we have a full microstate at time t here!
-        if (inputrec->etc == etcVRESCALE)
+        // The leap frog integration algorithm
+        builder->add<ForceElement>();
+        builder->add<StatePropagatorData::Element>();
+        if (legacySimulatorData_->inputrec->etc == etcVRESCALE
+            || legacySimulatorData_->inputrec->etc == etcBERENDSEN)
         {
-            // TODO: With increased complexity of the propagator, this will need further development,
-            //       e.g. using propagators templated for velocity propagation policies and a builder
-            propagator->setNumVelocityScalingVariables(inputrec->opts.ngtc);
-            auto thermostat = std::make_unique<VRescaleThermostat>(
-                    inputrec->nsttcouple, -1, false, inputrec->ld_seed, inputrec->opts.ngtc,
-                    inputrec->delta_t * inputrec->nsttcouple, inputrec->opts.ref_t, inputrec->opts.tau_t,
-                    inputrec->opts.nrdf, energyElementPtr, propagator->viewOnVelocityScaling(),
-                    propagator->velocityScalingCallback(), state_global, cr, inputrec->bContinuation);
-            checkpointClients->emplace_back(thermostat.get());
-            energyElementPtr->setVRescaleThermostat(thermostat.get());
-            addToCallListAndMove(std::move(thermostat), elementCallList, elementsOwnershipList);
+            builder->add<VelocityScalingTemperatureCoupling>(-1, UseFullStepKE::No,
+                                                             ReportPreviousStepConservedEnergy::No);
         }
-
-        std::unique_ptr<ParrinelloRahmanBarostat> prBarostat = nullptr;
-        if (inputrec->epc == epcPARRINELLORAHMAN)
+        builder->add<Propagator<IntegrationStep::LeapFrog>>(legacySimulatorData_->inputrec->delta_t,
+                                                            RegisterWithThermostat::True,
+                                                            RegisterWithBarostat::True);
+        if (legacySimulatorData_->constr)
         {
-            // Building the PR barostat here since it needs access to the propagator
-            // and we want to be able to move the propagator object
-            prBarostat = std::make_unique<ParrinelloRahmanBarostat>(
-                    inputrec->nstpcouple, -1, inputrec->delta_t * inputrec->nstpcouple,
-                    inputrec->init_step, propagator->viewOnPRScalingMatrix(),
-                    propagator->prScalingCallback(), statePropagatorDataPtr, energyElementPtr,
-                    fplog, inputrec, mdAtoms, state_global, cr, inputrec->bContinuation);
-            energyElementPtr->setParrinelloRahamnBarostat(prBarostat.get());
-            checkpointClients->emplace_back(prBarostat.get());
+            builder->add<ConstraintsElement<ConstraintVariable::Positions>>();
         }
-        addToCallListAndMove(std::move(propagator), elementCallList, elementsOwnershipList);
-        if (constr)
+        builder->add<ComputeGlobalsElement<ComputeGlobalsAlgorithm::LeapFrog>>();
+        builder->add<EnergyData::Element>();
+        if (legacySimulatorData_->inputrec->epc == epcPARRINELLORAHMAN)
         {
-            auto constraintElement = std::make_unique<ConstraintsElement<ConstraintVariable::Positions>>(
-                    constr, statePropagatorDataPtr, energyElementPtr, freeEnergyPerturbationElementPtr,
-                    MASTER(cr), fplog, inputrec, mdAtoms->mdatoms());
-            auto constraintElementPtr = compat::make_not_null(constraintElement.get());
-            energySignallerBuilder->registerSignallerClient(constraintElementPtr);
-            trajectoryElementBuilder->registerSignallerClient(constraintElementPtr);
-            loggingSignallerBuilder->registerSignallerClient(constraintElementPtr);
-
-            addToCallListAndMove(std::move(constraintElement), elementCallList, elementsOwnershipList);
-        }
-
-        addToCallListAndMove(std::move(computeGlobalsElement), elementCallList, elementsOwnershipList);
-        addToCallList(energyElementPtr, elementCallList); // we have the energies at time t here!
-        if (prBarostat)
-        {
-            addToCallListAndMove(std::move(prBarostat), elementCallList, elementsOwnershipList);
+            builder->add<ParrinelloRahmanBarostat>(-1);
         }
     }
-    else if (inputrec->eI == eiVV)
+    else if (legacySimulatorData_->inputrec->eI == eiVV)
     {
-        auto computeGlobalsElementAtFullTimeStep =
-                std::make_unique<ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerletAtFullTimeStep>>(
-                        statePropagatorDataPtr, energyElementPtr, freeEnergyPerturbationElementPtr,
-                        &signals_, nstglobalcomm_, fplog, mdlog, cr, inputrec, mdAtoms, nrnb,
-                        wcycle, fr, &topologyHolder_->globalTopology(), constr, hasReadEkinState);
-        topologyHolder_->registerClient(computeGlobalsElementAtFullTimeStep.get());
-        energySignallerBuilder->registerSignallerClient(
-                compat::make_not_null(computeGlobalsElementAtFullTimeStep.get()));
-        trajectoryElementBuilder->registerSignallerClient(
-                compat::make_not_null(computeGlobalsElementAtFullTimeStep.get()));
-
-        auto computeGlobalsElementAfterCoordinateUpdate =
-                std::make_unique<ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerletAfterCoordinateUpdate>>(
-                        statePropagatorDataPtr, energyElementPtr, freeEnergyPerturbationElementPtr,
-                        &signals_, nstglobalcomm_, fplog, mdlog, cr, inputrec, mdAtoms, nrnb,
-                        wcycle, fr, &topologyHolder_->globalTopology(), constr, hasReadEkinState);
-        topologyHolder_->registerClient(computeGlobalsElementAfterCoordinateUpdate.get());
-        energySignallerBuilder->registerSignallerClient(
-                compat::make_not_null(computeGlobalsElementAfterCoordinateUpdate.get()));
-        trajectoryElementBuilder->registerSignallerClient(
-                compat::make_not_null(computeGlobalsElementAfterCoordinateUpdate.get()));
-
-        *checkBondedInteractionsCallback =
-                computeGlobalsElementAfterCoordinateUpdate->getCheckNumberOfBondedInteractionsCallback();
-
-        auto propagatorVelocities = std::make_unique<Propagator<IntegrationStep::VelocitiesOnly>>(
-                inputrec->delta_t * 0.5, statePropagatorDataPtr, mdAtoms, wcycle);
-        auto propagatorVelocitiesAndPositions =
-                std::make_unique<Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>>(
-                        inputrec->delta_t, statePropagatorDataPtr, mdAtoms, wcycle);
-
-        addToCallListAndMove(std::move(forceElement), elementCallList, elementsOwnershipList);
-
-        std::unique_ptr<ParrinelloRahmanBarostat> prBarostat = nullptr;
-        if (inputrec->epc == epcPARRINELLORAHMAN)
+        // The velocity verlet integration algorithm
+        builder->add<ForceElement>();
+        builder->add<Propagator<IntegrationStep::VelocitiesOnly>>(
+                0.5 * legacySimulatorData_->inputrec->delta_t, RegisterWithThermostat::False,
+                RegisterWithBarostat::True);
+        if (legacySimulatorData_->constr)
         {
-            // Building the PR barostat here since it needs access to the propagator
-            // and we want to be able to move the propagator object
-            prBarostat = std::make_unique<ParrinelloRahmanBarostat>(
-                    inputrec->nstpcouple, -1, inputrec->delta_t * inputrec->nstpcouple,
-                    inputrec->init_step, propagatorVelocities->viewOnPRScalingMatrix(),
-                    propagatorVelocities->prScalingCallback(), statePropagatorDataPtr, energyElementPtr,
-                    fplog, inputrec, mdAtoms, state_global, cr, inputrec->bContinuation);
-            energyElementPtr->setParrinelloRahamnBarostat(prBarostat.get());
-            checkpointClients->emplace_back(prBarostat.get());
-        }
-        addToCallListAndMove(std::move(propagatorVelocities), elementCallList, elementsOwnershipList);
-        if (constr)
-        {
-            auto constraintElement = std::make_unique<ConstraintsElement<ConstraintVariable::Velocities>>(
-                    constr, statePropagatorDataPtr, energyElementPtr, freeEnergyPerturbationElementPtr,
-                    MASTER(cr), fplog, inputrec, mdAtoms->mdatoms());
-            energySignallerBuilder->registerSignallerClient(compat::make_not_null(constraintElement.get()));
-            trajectoryElementBuilder->registerSignallerClient(
-                    compat::make_not_null(constraintElement.get()));
-            loggingSignallerBuilder->registerSignallerClient(
-                    compat::make_not_null(constraintElement.get()));
-
-            addToCallListAndMove(std::move(constraintElement), elementCallList, elementsOwnershipList);
+            builder->add<ConstraintsElement<ConstraintVariable::Velocities>>();
         }
-        addToCallListAndMove(std::move(computeGlobalsElementAtFullTimeStep), elementCallList,
-                             elementsOwnershipList);
-        addToCallList(statePropagatorDataPtr, elementCallList); // we have a full microstate at time t here!
-        if (inputrec->etc == etcVRESCALE)
+        builder->add<ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerlet>>();
+        builder->add<StatePropagatorData::Element>();
+        if (legacySimulatorData_->inputrec->etc == etcVRESCALE
+            || legacySimulatorData_->inputrec->etc == etcBERENDSEN)
         {
-            // TODO: With increased complexity of the propagator, this will need further development,
-            //       e.g. using propagators templated for velocity propagation policies and a builder
-            propagatorVelocitiesAndPositions->setNumVelocityScalingVariables(inputrec->opts.ngtc);
-            auto thermostat = std::make_unique<VRescaleThermostat>(
-                    inputrec->nsttcouple, 0, true, inputrec->ld_seed, inputrec->opts.ngtc,
-                    inputrec->delta_t * inputrec->nsttcouple, inputrec->opts.ref_t,
-                    inputrec->opts.tau_t, inputrec->opts.nrdf, energyElementPtr,
-                    propagatorVelocitiesAndPositions->viewOnVelocityScaling(),
-                    propagatorVelocitiesAndPositions->velocityScalingCallback(), state_global, cr,
-                    inputrec->bContinuation);
-            checkpointClients->emplace_back(thermostat.get());
-            energyElementPtr->setVRescaleThermostat(thermostat.get());
-            addToCallListAndMove(std::move(thermostat), elementCallList, elementsOwnershipList);
+            builder->add<VelocityScalingTemperatureCoupling>(
+                    0, UseFullStepKE::Yes, ReportPreviousStepConservedEnergy::Yes);
         }
-        addToCallListAndMove(std::move(propagatorVelocitiesAndPositions), elementCallList,
-                             elementsOwnershipList);
-        if (constr)
+        builder->add<Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>>(
+                legacySimulatorData_->inputrec->delta_t, RegisterWithThermostat::True,
+                RegisterWithBarostat::False);
+        if (legacySimulatorData_->constr)
         {
-            auto constraintElement = std::make_unique<ConstraintsElement<ConstraintVariable::Positions>>(
-                    constr, statePropagatorDataPtr, energyElementPtr, freeEnergyPerturbationElementPtr,
-                    MASTER(cr), fplog, inputrec, mdAtoms->mdatoms());
-            energySignallerBuilder->registerSignallerClient(compat::make_not_null(constraintElement.get()));
-            trajectoryElementBuilder->registerSignallerClient(
-                    compat::make_not_null(constraintElement.get()));
-            loggingSignallerBuilder->registerSignallerClient(
-                    compat::make_not_null(constraintElement.get()));
-
-            addToCallListAndMove(std::move(constraintElement), elementCallList, elementsOwnershipList);
+            builder->add<ConstraintsElement<ConstraintVariable::Positions>>();
         }
-        addToCallListAndMove(std::move(computeGlobalsElementAfterCoordinateUpdate), elementCallList,
-                             elementsOwnershipList);
-        addToCallList(energyElementPtr, elementCallList); // we have the energies at time t here!
-        if (prBarostat)
+        builder->add<ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerlet>>();
+        builder->add<EnergyData::Element>();
+        if (legacySimulatorData_->inputrec->epc == epcPARRINELLORAHMAN)
         {
-            addToCallListAndMove(std::move(prBarostat), elementCallList, elementsOwnershipList);
+            builder->add<ParrinelloRahmanBarostat>(-1);
         }
     }
     else
     {
         gmx_fatal(FARGS, "Integrator not implemented for the modular simulator.");
     }
-
-    auto integrator = std::make_unique<CompositeSimulatorElement>(std::move(elementCallList),
-                                                                  std::move(elementsOwnershipList));
-    // std::move *should* not be needed with c++-14, but clang-3.6 still requires it
-    return std::move(integrator);
 }
 
 bool ModularSimulator::isInputCompatible(bool                             exitOnFailure,
@@ -795,8 +185,6 @@ bool ModularSimulator::isInputCompatible(bool                             exitOn
         return condition;
     };
 
-    bool isInputCompatible = true;
-
     // GMX_USE_MODULAR_SIMULATOR allows to use modular simulator also for non-standard uses,
     // such as the leap-frog integrator
     const auto modularSimulatorExplicitlyTurnedOn = (getenv("GMX_USE_MODULAR_SIMULATOR") != nullptr);
@@ -819,11 +207,9 @@ bool ModularSimulator::isInputCompatible(bool                             exitOn
             "as the Parrinello-Rahman barostat is not implemented in the legacy simulator. Unset "
             "GMX_DISABLE_MODULAR_SIMULATOR or use a different pressure control algorithm.");
 
-    isInputCompatible =
-            isInputCompatible
-            && conditionalAssert(
-                       inputrec->eI == eiMD || inputrec->eI == eiVV,
-                       "Only integrators md and md-vv are supported by the modular simulator.");
+    bool isInputCompatible = conditionalAssert(
+            inputrec->eI == eiMD || inputrec->eI == eiVV,
+            "Only integrators md and md-vv are supported by the modular simulator.");
     isInputCompatible = isInputCompatible
                         && conditionalAssert(inputrec->eI != eiMD || modularSimulatorExplicitlyTurnedOn,
                                              "Set GMX_USE_MODULAR_SIMULATOR=ON to use the modular "
@@ -831,11 +217,11 @@ bool ModularSimulator::isInputCompatible(bool                             exitOn
     isInputCompatible =
             isInputCompatible
             && conditionalAssert(!doRerun, "Rerun is not supported by the modular simulator.");
-    isInputCompatible =
-            isInputCompatible
-            && conditionalAssert(
-                       inputrec->etc == etcNO || inputrec->etc == etcVRESCALE,
-                       "Only v-rescale thermostat is supported by the modular simulator.");
+    isInputCompatible = isInputCompatible
+                        && conditionalAssert(inputrec->etc == etcNO || inputrec->etc == etcVRESCALE
+                                                     || inputrec->etc == etcBERENDSEN,
+                                             "Only v-rescale and Berendsen thermostat are "
+                                             "supported by the modular simulator.");
     isInputCompatible =
             isInputCompatible
             && conditionalAssert(
@@ -904,7 +290,7 @@ bool ModularSimulator::isInputCompatible(bool                             exitOn
     int numEnsembleRestraintSystems;
     if (fcd)
     {
-        numEnsembleRestraintSystems = fcd->disres.nsystems;
+        numEnsembleRestraintSystems = fcd->disres->nsystems;
     }
     else
     {
@@ -946,10 +332,6 @@ bool ModularSimulator::isInputCompatible(bool                             exitOn
             isInputCompatible
             && conditionalAssert(!doMembed,
                                  "Membrane embedding is not supported by the modular simulator.");
-    const bool useGraph = !areMoleculesDistributedOverPbc(*inputrec, globalTopology, MDLogger());
-    isInputCompatible =
-            isInputCompatible
-            && conditionalAssert(!useGraph, "Graph is not supported by the modular simulator.");
     // TODO: Change this to the boolean passed when we merge the user interface change for the GPU update.
     isInputCompatible =
             isInputCompatible
@@ -969,11 +351,22 @@ bool ModularSimulator::isInputCompatible(bool                             exitOn
     return isInputCompatible;
 }
 
+ModularSimulator::ModularSimulator(std::unique_ptr<LegacySimulatorData>      legacySimulatorData,
+                                   std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder) :
+    legacySimulatorData_(std::move(legacySimulatorData)),
+    checkpointDataHolder_(std::move(checkpointDataHolder))
+{
+    checkInputForDisabledFunctionality();
+}
+
 void ModularSimulator::checkInputForDisabledFunctionality()
 {
-    isInputCompatible(true, inputrec, doRerun, *top_global, ms, replExParams, fcd,
-                      opt2bSet("-ei", nfile, fnm), membed != nullptr);
-    if (observablesHistory->edsamHistory)
+    isInputCompatible(true, legacySimulatorData_->inputrec, legacySimulatorData_->mdrunOptions.rerun,
+                      *legacySimulatorData_->top_global, legacySimulatorData_->ms,
+                      legacySimulatorData_->replExParams, legacySimulatorData_->fr->fcdata.get(),
+                      opt2bSet("-ei", legacySimulatorData_->nfile, legacySimulatorData_->fnm),
+                      legacySimulatorData_->membed != nullptr);
+    if (legacySimulatorData_->observablesHistory->edsamHistory)
     {
         gmx_fatal(FARGS,
                   "The checkpoint is from a run with essential dynamics sampling, "
@@ -982,15 +375,4 @@ void ModularSimulator::checkInputForDisabledFunctionality()
     }
 }
 
-SignallerCallbackPtr ModularSimulator::SignalHelper::registerLastStepCallback()
-{
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { this->lastStep_ = step; });
-}
-
-SignallerCallbackPtr ModularSimulator::SignalHelper::registerNSCallback()
-{
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { this->nextNSStep_ = step; });
-}
 } // namespace gmx
index 5c51e1cc60448f162978fa354f1c93f485cb6eea..a46b52ebdef235bc457101076b00528463f82bc5 100644 (file)
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \libinternal \file
- * \brief Declares the modular simulator
+ * \brief Provides the modular simulator.
+ *
+ * Defines the ModularSimulator class. Provides checkUseModularSimulator() utility function
+ * to determine whether the ModularSimulator should be used.
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is currently the only part of the modular simulator module which is exposed.
+ * Mdrunner creates an object of type ModularSimulator (via SimulatorBuilder), and calls its
+ * run() method. Mdrunner also calls checkUseModularSimulator(...), which in turns calls a
+ * static method of ModularSimulator. This could easily become a free function if this requires
+ * more exposure than otherwise necessary.
  */
 #ifndef GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
 #define GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
 
-#include <queue>
-
-#include "gromacs/mdlib/md_support.h"
-#include "gromacs/mdlib/resethandler.h"
 #include "gromacs/mdrun/isimulator.h"
 
-#include "checkpointhelper.h"
-#include "domdechelper.h"
-#include "modularsimulatorinterfaces.h"
-#include "pmeloadbalancehelper.h"
-#include "topologyholder.h"
+struct t_fcdata;
 
 namespace gmx
 {
-class DomDecHelper;
-class EnergyElement;
-class EnergySignaller;
-class FreeEnergyPerturbationElement;
-class LoggingSignaller;
-class StatePropagatorData;
-class NeighborSearchSignaller;
-class PmeLoadBalanceHelper;
-class TrajectoryElementBuilder;
+class ModularSimulatorAlgorithmBuilder;
+class ReadCheckpointDataHolder;
 
 /*! \libinternal
  * \ingroup module_modularsimulator
@@ -97,226 +91,38 @@ public:
 
 private:
     //! Constructor
-    template<typename... Args>
-    explicit ModularSimulator(Args&&... args);
-
-    /*! \brief The initialisation
-     *
-     * This builds all signallers and elements, and is responsible to put
-     * them in the correct order.
-     */
-    void constructElementsAndSignallers();
-
-    /*! \brief A function called once before the simulation run
-     *
-     * This function collects calls which need to be made once at the
-     * beginning of the simulation run. These are mainly printing information
-     * to the user and starting the wall time.
-     */
-    void simulatorSetup();
-
-    /*! \brief A function called once after the simulation run
-     *
-     * This function collects calls which need to be made once at the
-     * end of the simulation run.
-     */
-    void simulatorTeardown();
-
-    /*! \brief A function called before every step
-     *
-     * This function collects calls which need to be made before every
-     * simulation step. The need for this function could likely be
-     * eliminated by rewriting the stop and reset handler to fit the
-     * modular simulator approach.
-     */
-    void preStep(Step step, Time time, bool isNeighborSearchingStep);
-
-    /*! \brief A function called after every step
-     *
-     * This function collects calls which need to be made after every
-     * simulation step.
-     */
-    void postStep(Step step, Time time);
+    ModularSimulator(std::unique_ptr<LegacySimulatorData>      legacySimulatorData,
+                     std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder);
 
-    /*! \brief Build the integrator part of the simulator
-     *
-     * This includes the force calculation, state propagation, constraints,
-     * global computation, and the points during the process at which valid
-     * micro state / energy states are found. Currently, buildIntegrator
-     * knows about NVE md and md-vv algorithms.
-     */
-    std::unique_ptr<ISimulatorElement>
-    buildIntegrator(SignallerBuilder<NeighborSearchSignaller>* neighborSearchSignallerBuilder,
-                    SignallerBuilder<EnergySignaller>*         energySignallerBuilder,
-                    SignallerBuilder<LoggingSignaller>*        loggingSignallerBuilder,
-                    TrajectoryElementBuilder*                  trajectoryElementBuilder,
-                    std::vector<ICheckpointHelperClient*>*     checkpointClients,
-                    CheckBondedInteractionsCallbackPtr*        checkBondedInteractionsCallback,
-                    compat::not_null<StatePropagatorData*>     statePropagatorDataPtr,
-                    compat::not_null<EnergyElement*>           energyElementPtr,
-                    FreeEnergyPerturbationElement*             freeEnergyPerturbationElementPtr,
-                    bool                                       hasReadEkinState);
-
-    //! Build the force element - can be normal forces or shell / flex constraints
-    std::unique_ptr<ISimulatorElement>
-    buildForces(SignallerBuilder<NeighborSearchSignaller>* neighborSearchSignallerBuilder,
-                SignallerBuilder<EnergySignaller>*         energySignallerBuilder,
-                StatePropagatorData*                       statePropagatorDataPtr,
-                EnergyElement*                             energyElementPtr,
-                FreeEnergyPerturbationElement*             freeEnergyPerturbationElement);
-
-    /*! \brief Add run functions to the task queue
-     *
-     * This function calls PME load balancing and domain decomposition first,
-     * and then queries the elements for all steps of the life time of the
-     * current neighbor list for their run functions. When the next step
-     * would be a neighbor-searching step, this function returns, such that
-     * the simulator can run the pre-computed task list before updating the
-     * neighbor list and re-filling the task list.
-     *
-     * TODO: In the current approach, unique pointers to tasks are created
-     *       repeatedly. While preliminary measures do not seem to indicate
-     *       performance problems, a pool allocator would be an obvious
-     *       optimization possibility, as would reusing the task queue if
-     *       the task queue is periodic around the rebuild point (i.e. the
-     *       task queue is identical between rebuilds).
-     */
-    void populateTaskQueue();
+    //! Populate algorithm builder with elements
+    void addIntegrationElements(ModularSimulatorAlgorithmBuilder* builder);
 
     //! Check for disabled functionality (during construction time)
     void checkInputForDisabledFunctionality();
 
-    //! The run queue
-    std::queue<SimulatorRunFunctionPtr> taskQueue_;
-
-    /* Note that the Simulator is owning the signallers and elements.
-     * The ownership list and the call list are kept separate, however,
-     * to allow to have elements more than once in the call lists -
-     * either as signaller AND element (such as the TrajectoryElement),
-     * or to have an element twice in the scheduling sequence (currently
-     * not used).
-     *
-     * For the elements, the setup and teardown is applied on the
-     * elementsOwnershipList_, to ensure that it is called only once per
-     * element. For the signallers, the setup is applied on the
-     * signallerCallList_ - this makes sure that both the elementSetup()
-     * and signallerSetup() of an object being both an element and a
-     * signaller is called. It is also not expected to run have a signaller
-     * more than once in the signallerCallList_, so we don't have to worry
-     * about calling the setup method twice. Consequently, this means that
-     * objects being both a signaller and an element should be stored in
-     * the elementsOwnershipList_.
-     */
-    //! List of signalers (ownership)
-    std::vector<std::unique_ptr<ISignaller>> signallersOwnershipList_;
-    //! List of signalers (calling sequence)
-    std::vector<compat::not_null<ISignaller*>> signallerCallList_;
-    //! List of schedulerElements (ownership)
-    std::vector<std::unique_ptr<ISimulatorElement>> elementsOwnershipList_;
-    //! List of schedulerElements (calling sequence)
-    std::vector<compat::not_null<ISimulatorElement*>> elementCallList_;
-
-    //! \cond
-    //! Helper function to add elements or signallers to the call list via raw pointer
-    template<typename T, typename U>
-    static void addToCallList(compat::not_null<U*> element, std::vector<compat::not_null<T*>>& callList);
-    //! Helper function to add elements or signallers to the call list via smart pointer
-    template<typename T, typename U>
-    static void addToCallList(std::unique_ptr<U>& element, std::vector<compat::not_null<T*>>& callList);
-    /*! \brief Helper function to add elements or signallers to the call list
-     *         and move the ownership to the ownership list
-     */
-    template<typename T, typename U>
-    static void addToCallListAndMove(std::unique_ptr<U>                 element,
-                                     std::vector<compat::not_null<T*>>& callList,
-                                     std::vector<std::unique_ptr<T>>&   elementList);
-    //! \endcond
-
-    // Infrastructure elements
-    //! The domain decomposition element
-    std::unique_ptr<DomDecHelper> domDecHelper_;
-    //! The PME load balancing element
-    std::unique_ptr<PmeLoadBalanceHelper> pmeLoadBalanceHelper_;
-    //! The checkpoint helper
-    std::unique_ptr<CheckpointHelper> checkpointHelper_;
-    //! The stop handler
-    std::unique_ptr<StopHandler> stopHandler_;
-    //! The reset handler
-    std::unique_ptr<ResetHandler> resetHandler_;
-    //! Signal vector (used by stop / reset / checkpointing signaller)
-    SimulationSignals signals_;
-    //! Compute globals communication period
-    int nstglobalcomm_;
-
-    //! The topology
-    std::unique_ptr<TopologyHolder> topologyHolder_;
-
-    //! The current step
-    Step step_ = -1;
-
-    /*! \internal
-     * \brief Signal helper
-     *
-     * The simulator needs to know about the last step and the
-     * neighbor searching step, which are determined in signallers.
-     * This object can be registered to the signals and accessed by
-     * the methods of the simulator.
-     */
-    class SignalHelper : public ILastStepSignallerClient, public INeighborSearchSignallerClient
-    {
-    public:
-        //! The last step
-        Step lastStep_ = std::numeric_limits<Step>::max();
-        //! The next NS step
-        Step nextNSStep_ = -1;
-        //! ILastStepSignallerClient implementation
-        SignallerCallbackPtr registerLastStepCallback() override;
-        //! INeighborSearchSignallerClient implementation
-        SignallerCallbackPtr registerNSCallback() override;
-    };
-    //! The signal helper object
-    std::unique_ptr<SignalHelper> signalHelper_;
-
-    // TODO: This is a hack for stop handler - needs to go once StopHandler
-    //       is adapted to the modular simulator
-    //! Whether this is a neighbor-searching step
-    bool stophandlerIsNSStep_ = false;
-    //! The current step
-    Step stophandlerCurrentStep_ = -1;
+    //! Pointer to legacy simulator data (TODO: Can we avoid using unique_ptr? #3628)
+    std::unique_ptr<LegacySimulatorData> legacySimulatorData_;
+    //! Input checkpoint data
+    std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder_;
 };
 
-//! Constructor implementation (here to avoid template-related linker problems)
-template<typename... Args>
-ModularSimulator::ModularSimulator(Args&&... args) : ISimulator(std::forward<Args>(args)...)
-{
-    nstglobalcomm_ = computeGlobalCommunicationPeriod(mdlog, inputrec, cr);
-    signalHelper_  = std::make_unique<SignalHelper>();
-    checkInputForDisabledFunctionality();
-}
-
-//! \cond
-template<typename T, typename U>
-void ModularSimulator::addToCallList(gmx::compat::not_null<U*>          element,
-                                     std::vector<compat::not_null<T*>>& callList)
-{
-    callList.emplace_back(element);
-}
-
-template<typename T, typename U>
-void ModularSimulator::addToCallList(std::unique_ptr<U>& element, std::vector<compat::not_null<T*>>& callList)
-{
-    callList.emplace_back(compat::make_not_null(element.get()));
-}
-
-template<typename T, typename U>
-void ModularSimulator::addToCallListAndMove(std::unique_ptr<U>                 element,
-                                            std::vector<compat::not_null<T*>>& callList,
-                                            std::vector<std::unique_ptr<T>>&   elementList)
+/*!
+ * \brief Whether or not to use the ModularSimulator
+ *
+ * GMX_DISABLE_MODULAR_SIMULATOR environment variable allows to disable modular simulator for
+ * all uses.
+ *
+ * See ModularSimulator::isInputCompatible() for function signature.
+ *
+ * \ingroup module_modularsimulator
+ */
+template<typename... Ts>
+auto checkUseModularSimulator(Ts&&... args)
+        -> decltype(ModularSimulator::isInputCompatible(std::forward<Ts>(args)...))
 {
-    callList.emplace_back(compat::make_not_null(element.get()));
-    elementList.emplace_back(std::move(element));
+    return ModularSimulator::isInputCompatible(std::forward<Ts>(args)...)
+           && getenv("GMX_DISABLE_MODULAR_SIMULATOR") == nullptr;
 }
-//! \endcond
 
 } // namespace gmx
 
index 6d4986d2dd9137f97ed21b9fd6fe8a9fc0d34a54..0ed26a68fbad13478689d7c650a75d439f9b2a14 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  *
  * \author Pascal Merz <pascal.merz@me.com>
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief
  * Declares the main interfaces used by the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 #ifndef GMX_MODULARSIMULATOR_MODULARSIMULATORINTERFACES_H
 #define GMX_MODULARSIMULATOR_MODULARSIMULATORINTERFACES_H
 
 #include <functional>
 #include <memory>
+#include <optional>
 
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/checkpointdata.h"
 #include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/exceptions.h"
 
 struct gmx_localtop_t;
 struct gmx_mdoutf;
+struct t_commrec;
 class t_state;
 
 namespace gmx
 {
+template<typename T>
+class ArrayRef;
 template<class Signaller>
 class SignallerBuilder;
 class NeighborSearchSignaller;
 class LastStepSignaller;
 class LoggingSignaller;
+class TrajectorySignaller;
 class EnergySignaller;
 
 //! \addtogroup module_modularsimulator
@@ -83,15 +93,11 @@ using Time = double;
 
 //! The function type that can be scheduled to be run during the simulator run
 typedef std::function<void()> SimulatorRunFunction;
-//! Pointer to the function type that can be scheduled to be run during the simulator run
-typedef std::unique_ptr<SimulatorRunFunction> SimulatorRunFunctionPtr;
 
 //! The function type that allows to register run functions
-typedef std::function<void(SimulatorRunFunctionPtr)> RegisterRunFunction;
-//! Pointer to the function type that allows to register run functions
-typedef std::unique_ptr<RegisterRunFunction> RegisterRunFunctionPtr;
+typedef std::function<void(SimulatorRunFunction)> RegisterRunFunction;
 
-/*! \libinternal
+/*! \internal
  * \brief The general interface for elements of the modular simulator
  *
  * Setup and teardown are run once at the beginning of the simulation
@@ -111,7 +117,7 @@ public:
      * Element can register one or more functions to be run at that step through
      * the registration pointer.
      */
-    virtual void scheduleTask(Step, Time, const RegisterRunFunctionPtr&) = 0;
+    virtual void scheduleTask(Step, Time, const RegisterRunFunction&) = 0;
     //! Method guaranteed to be called after construction, before simulator run
     virtual void elementSetup() = 0;
     //! Method guaranteed to be called after simulator run, before deconstruction
@@ -120,7 +126,7 @@ public:
     virtual ~ISimulatorElement() = default;
 };
 
-/*! \libinternal
+/*! \internal
  * \brief The general Signaller interface
  *
  * Signallers are run at the beginning of Simulator steps, informing
@@ -142,17 +148,15 @@ public:
     //! Function run before every step of scheduling
     virtual void signal(Step, Time) = 0;
     //! Method guaranteed to be called after construction, before simulator run
-    virtual void signallerSetup() = 0;
+    virtual void setup() = 0;
     //! Standard virtual destructor
     virtual ~ISignaller() = default;
 };
 
 //! The function type that can be registered to signallers for callback
 typedef std::function<void(Step, Time)> SignallerCallback;
-//! Pointer to the function type that can be registered to signallers for callback
-typedef std::unique_ptr<SignallerCallback> SignallerCallbackPtr;
 
-/*! \libinternal
+/*! \internal
  * \brief Interface for clients of the NeighborSearchSignaller
  *
  * Defining registerNSCallback allows clients to register an arbitrary callback
@@ -161,20 +165,20 @@ typedef std::unique_ptr<SignallerCallback> SignallerCallbackPtr;
 class INeighborSearchSignallerClient
 {
 public:
-    //! @cond
+    //! \cond
     // (doxygen doesn't like these...)
     //! Allow builder of NeighborSearchSignaller to ask for callback registration
     friend class SignallerBuilder<NeighborSearchSignaller>;
-    //! @endcond
+    //! \endcond
     //! Standard virtual destructor
     virtual ~INeighborSearchSignallerClient() = default;
 
 protected:
     //! Return callback to NeighborSearchSignaller
-    virtual SignallerCallbackPtr registerNSCallback() = 0;
+    virtual std::optional<SignallerCallback> registerNSCallback() = 0;
 };
 
-/*! \libinternal
+/*! \internal
  * \brief Interface for clients of the LastStepSignaller
  *
  * Defining registerLastStepCallback allows clients to register an arbitrary callback
@@ -183,20 +187,20 @@ protected:
 class ILastStepSignallerClient
 {
 public:
-    //! @cond
+    //! \cond
     // (doxygen doesn't like these...)
     //! Allow builder of LastStepSignaller to ask for callback registration
     friend class SignallerBuilder<LastStepSignaller>;
-    //! @endcond
+    //! \endcond
     //! Standard virtual destructor
     virtual ~ILastStepSignallerClient() = default;
 
 protected:
     //! Return callback to LastStepSignaller
-    virtual SignallerCallbackPtr registerLastStepCallback() = 0;
+    virtual std::optional<SignallerCallback> registerLastStepCallback() = 0;
 };
 
-/*! \libinternal
+/*! \internal
  * \brief Interface for clients of the LoggingSignaller
  *
  * Defining registerLoggingCallback allows clients to register an arbitrary callback
@@ -205,17 +209,17 @@ protected:
 class ILoggingSignallerClient
 {
 public:
-    //! @cond
+    //! \cond
     // (doxygen doesn't like these...)
     //! Allow builder of LoggingSignaller to ask for callback registration
     friend class SignallerBuilder<LoggingSignaller>;
-    //! @endcond
+    //! \endcond
     //! Standard virtual destructor
     virtual ~ILoggingSignallerClient() = default;
 
 protected:
     //! Return callback to LoggingSignaller
-    virtual SignallerCallbackPtr registerLoggingCallback() = 0;
+    virtual std::optional<SignallerCallback> registerLoggingCallback() = 0;
 };
 
 //! The energy events signalled by the EnergySignaller
@@ -226,7 +230,7 @@ enum class EnergySignallerEvent
     FreeEnergyCalculationStep
 };
 
-/*! \libinternal
+/*! \internal
  * \brief Interface for clients of the EnergySignaller
  *
  * Defining registerEnergyCallback allows clients to register an arbitrary callback
@@ -235,17 +239,17 @@ enum class EnergySignallerEvent
 class IEnergySignallerClient
 {
 public:
-    //! @cond
+    //! \cond
     // (doxygen doesn't like these...)
     //! Allow builder of EnergySignaller to ask for callback registration
     friend class SignallerBuilder<EnergySignaller>;
-    //! @endcond
+    //! \endcond
     //! Standard virtual destructor
     virtual ~IEnergySignallerClient() = default;
 
 protected:
     //! Return callback to EnergySignaller
-    virtual SignallerCallbackPtr registerEnergyCallback(EnergySignallerEvent) = 0;
+    virtual std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent) = 0;
 };
 
 //! The trajectory writing events
@@ -255,7 +259,7 @@ enum class TrajectoryEvent
     EnergyWritingStep
 };
 
-/*! \libinternal
+/*! \internal
  * \brief Interface for signaller clients of the TrajectoryElement
  *
  * Defining registerTrajectorySignallerCallback allows clients to register an arbitrary
@@ -264,17 +268,17 @@ enum class TrajectoryEvent
 class ITrajectorySignallerClient
 {
 public:
-    //! @cond
+    //! \cond
     // (doxygen doesn't like these...)
-    //! Allow builder of TrajectoryElement to ask for callback registration
-    friend class TrajectoryElementBuilder;
-    //! @endcond
+    //! Allow builder of TrajectorySignaller to ask for callback registration
+    friend class SignallerBuilder<TrajectorySignaller>;
+    //! \endcond
     //! Standard virtual destructor
     virtual ~ITrajectorySignallerClient() = default;
 
 protected:
     //! Return callback to TrajectoryElement
-    virtual SignallerCallbackPtr registerTrajectorySignallerCallback(TrajectoryEvent) = 0;
+    virtual std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent) = 0;
 };
 
 /* Trajectory writing clients are handed a pointer to the output file handler,
@@ -286,10 +290,8 @@ protected:
  */
 //! Function type for trajectory writing clients
 typedef std::function<void(gmx_mdoutf*, Step, Time, bool, bool)> ITrajectoryWriterCallback;
-//! Pointer to the function type for trajectory writing clients
-typedef std::unique_ptr<ITrajectoryWriterCallback> ITrajectoryWriterCallbackPtr;
 
-/*! \libinternal
+/*! \internal
  * \brief Interface for writer clients of the TrajectoryElement
  *
  * Defining registerTrajectoryWriterCallback allows clients to register an arbitrary
@@ -300,11 +302,11 @@ typedef std::unique_ptr<ITrajectoryWriterCallback> ITrajectoryWriterCallbackPtr;
 class ITrajectoryWriterClient
 {
 public:
-    //! @cond
+    //! \cond
     // (doxygen doesn't like these...)
     //! Allow TrajectoryElement to ask for callback registration
     friend class TrajectoryElement;
-    //! @endcond
+    //! \endcond
     //! Standard virtual destructor
     virtual ~ITrajectoryWriterClient() = default;
 
@@ -315,21 +317,21 @@ protected:
     virtual void trajectoryWriterTeardown(gmx_mdoutf* outf) = 0;
 
     //! Return callback to TrajectoryElement
-    virtual ITrajectoryWriterCallbackPtr registerTrajectoryWriterCallback(TrajectoryEvent) = 0;
+    virtual std::optional<ITrajectoryWriterCallback> registerTrajectoryWriterCallback(TrajectoryEvent) = 0;
 };
 
-/*! \libinternal
+/*! \internal
  * \brief Client requiring read access to the local topology
  *
  */
 class ITopologyHolderClient
 {
 public:
-    //! @cond
+    //! \cond
     // (doxygen doesn't like these...)
     //! Allow TopologyHolder to set new topology
     friend class TopologyHolder;
-    //! @endcond
+    //! \endcond
     //! Standard virtual destructor
     virtual ~ITopologyHolderClient() = default;
 
@@ -338,32 +340,144 @@ protected:
     virtual void setTopology(const gmx_localtop_t*) = 0;
 };
 
-/*! \libinternal
+/*! \internal
  * \brief Client that needs to store data during checkpointing
  *
- * The current checkpointing helper uses the legacy t_state object to collect
- * the data to be checkpointed. Clients get queried for their contributions
- * using pointers to t_state objects.
- * \todo Add checkpoint reading
- * \todo Evolve this to a model in which the checkpoint helper passes a file
- *       pointer rather than a t_state object, and the clients are responsible
- *       to read / write.
+ * Clients receive a CheckpointData object for reading and writing.
+ * Note that `ReadCheckpointData` is a typedef for
+ * `CheckpointData<CheckpointDataOperation::Read>`, and
+ * `WriteCheckpointData` is a typedef for
+ * `CheckpointData<CheckpointDataOperation::Write>`. This allows clients
+ * to write a single templated function, e.g.
+ *     template<CheckpointDataOperation operation>
+ *     void doCheckpointData(CheckpointData<operation>* checkpointData,
+ *                           const t_commrec* cr)
+ *     {
+ *         checkpointData->scalar("important value", &value_);
+ *     }
+ * for both checkpoint reading and writing. This function can then be
+ * dispatched from the interface functions,
+ *     void writeCheckpoint(WriteCheckpointData checkpointData, const t_commrec* cr)
+ *     {
+ *         doCheckpointData<CheckpointDataOperation::Write>(&checkpointData, cr);
+ *     }
+ *     void readCheckpoint(ReadCheckpointData checkpointData, const t_commrec* cr)
+ *     {
+ *         doCheckpointData<CheckpointDataOperation::Read>(&checkpointData, cr);
+ *     }
+ * This reduces code duplication and ensures that reading and writing
+ * operations will not get out of sync.
  */
 class ICheckpointHelperClient
 {
 public:
-    //! @cond
-    // (doxygen doesn't like these...)
-    //! Allow CheckpointHelper to interact
-    friend class CheckpointHelper;
-    //! @endcond
     //! Standard virtual destructor
     virtual ~ICheckpointHelperClient() = default;
 
-protected:
-    //! Write checkpoint
-    virtual void writeCheckpoint(t_state* localState, t_state* globalState) = 0;
+    //! Write checkpoint (CheckpointData object only passed on master rank)
+    virtual void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData,
+                                     const t_commrec*                   cr) = 0;
+    //! Read checkpoint (CheckpointData object only passed on master rank)
+    virtual void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData,
+                                        const t_commrec*                  cr) = 0;
+    //! Get unique client id
+    [[nodiscard]] virtual const std::string& clientID() = 0;
+};
+
+/*! \brief
+ * Exception class signalling that a requested element was not found.
+ *
+ * \internal
+ */
+class ElementNotFoundError final : public ModularSimulatorError
+{
+public:
+    //! \copydoc FileIOError::FileIOError()
+    explicit ElementNotFoundError(const ExceptionInitializer& details) :
+        ModularSimulatorError(details)
+    {
+    }
+};
+
+/*! \brief
+ * Exception class signalling that elements were not connected properly.
+ *
+ * \internal
+ */
+class MissingElementConnectionError final : public ModularSimulatorError
+{
+public:
+    //! \copydoc FileIOError::FileIOError()
+    explicit MissingElementConnectionError(const ExceptionInitializer& details) :
+        ModularSimulatorError(details)
+    {
+    }
+};
+
+/*! \brief
+ * Exception class signalling that the ModularSimulatorAlgorithm was set up
+ * in an incompatible way.
+ *
+ * \internal
+ */
+class SimulationAlgorithmSetupError final : public ModularSimulatorError
+{
+public:
+    //! \copydoc FileIOError::FileIOError()
+    explicit SimulationAlgorithmSetupError(const ExceptionInitializer& details) :
+        ModularSimulatorError(details)
+    {
+    }
+};
+
+/*! \brief
+ * Exception class signalling an error in reading or writing modular checkpoints.
+ *
+ * \internal
+ */
+class CheckpointError final : public ModularSimulatorError
+{
+public:
+    //! \copydoc FileIOError::FileIOError()
+    explicit CheckpointError(const ExceptionInitializer& details) : ModularSimulatorError(details)
+    {
+    }
+};
+
+//! Enum allowing builders to store whether they can accept client registrations
+enum class ModularSimulatorBuilderState
+{
+    AcceptingClientRegistrations,
+    NotAcceptingClientRegistrations
 };
+
+//! Generic callback to the propagator
+typedef std::function<void(Step)> PropagatorCallback;
+
+/*! \internal
+ * \brief Information needed to connect a propagator to a thermostat
+ */
+struct PropagatorThermostatConnection
+{
+    //! Function variable for setting velocity scaling variables.
+    std::function<void(int)> setNumVelocityScalingVariables;
+    //! Function variable for receiving view on velocity scaling.
+    std::function<ArrayRef<real>()> getViewOnVelocityScaling;
+    //! Function variable for callback.
+    std::function<PropagatorCallback()> getVelocityScalingCallback;
+};
+
+/*! \internal
+ * \brief Information needed to connect a propagator to a barostat
+ */
+struct PropagatorBarostatConnection
+{
+    //! Function variable for receiving view on pressure scaling matrix.
+    std::function<ArrayRef<rvec>()> getViewOnPRScalingMatrix;
+    //! Function variable for callback.
+    std::function<PropagatorCallback()> getPRScalingCallback;
+};
+
 //! /}
 } // namespace gmx
 
index 7c4e259d81c010676de80b0029c6d42610a681ab..7bf5c7fe999c4a2d43ac0652184097cfeaa9ba67 100644 (file)
 #include "parrinellorahmanbarostat.h"
 
 #include "gromacs/domdec/domdec_network.h"
+#include "gromacs/math/units.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/mdlib/coupling.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/stat.h"
-#include "gromacs/mdlib/update.h"
+#include "gromacs/mdtypes/checkpointdata.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/mdtypes/state.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/pbcutil/boxutilities.h"
 
-#include "energyelement.h"
+#include "energydata.h"
+#include "modularsimulator.h"
+#include "simulatoralgorithm.h"
 #include "statepropagatordata.h"
 
 namespace gmx
 {
 
-ParrinelloRahmanBarostat::ParrinelloRahmanBarostat(int                   nstpcouple,
-                                                   int                   offset,
-                                                   real                  couplingTimeStep,
-                                                   Step                  initStep,
-                                                   ArrayRef<rvec>        scalingTensor,
-                                                   PropagatorCallbackPtr propagatorCallback,
-                                                   StatePropagatorData*  statePropagatorData,
-                                                   EnergyElement*        energyElement,
-                                                   FILE*                 fplog,
-                                                   const t_inputrec*     inputrec,
-                                                   const MDAtoms*        mdAtoms,
-                                                   const t_state*        globalState,
-                                                   t_commrec*            cr,
-                                                   bool                  isRestart) :
+ParrinelloRahmanBarostat::ParrinelloRahmanBarostat(int                  nstpcouple,
+                                                   int                  offset,
+                                                   real                 couplingTimeStep,
+                                                   Step                 initStep,
+                                                   StatePropagatorData* statePropagatorData,
+                                                   EnergyData*          energyData,
+                                                   FILE*                fplog,
+                                                   const t_inputrec*    inputrec,
+                                                   const MDAtoms*       mdAtoms) :
     nstpcouple_(nstpcouple),
     offset_(offset),
     couplingTimeStep_(couplingTimeStep),
     initStep_(initStep),
-    scalingTensor_(scalingTensor),
-    propagatorCallback_(std::move(propagatorCallback)),
+    mu_{ { 0 } },
+    boxRel_{ { 0 } },
+    boxVelocity_{ { 0 } },
     statePropagatorData_(statePropagatorData),
-    energyElement_(energyElement),
+    energyData_(energyData),
     fplog_(fplog),
     inputrec_(inputrec),
     mdAtoms_(mdAtoms)
 {
-    clear_mat(mu_);
-    clear_mat(boxRel_);
-    clear_mat(boxVelocity_);
+    energyData->setParrinelloRahamnBarostat(this);
+}
 
-    // TODO: This is only needed to restore the thermostatIntegral_ from cpt. Remove this when
-    //       switching to purely client-based checkpointing.
-    if (isRestart)
-    {
-        if (MASTER(cr))
-        {
-            copy_mat(globalState->boxv, boxVelocity_);
-            copy_mat(globalState->box_rel, boxRel_);
-        }
-        if (DOMAINDECOMP(cr))
-        {
-            dd_bcast(cr->dd, sizeof(boxVelocity_), boxVelocity_);
-            dd_bcast(cr->dd, sizeof(boxRel_), boxRel_);
-        }
-    }
+void ParrinelloRahmanBarostat::connectWithPropagator(const PropagatorBarostatConnection& connectionData)
+{
+    scalingTensor_      = connectionData.getViewOnPRScalingMatrix();
+    propagatorCallback_ = connectionData.getPRScalingCallback();
 }
 
-void ParrinelloRahmanBarostat::scheduleTask(gmx::Step step,
-                                            gmx::Time gmx_unused               time,
-                                            const gmx::RegisterRunFunctionPtr& registerRunFunction)
+void ParrinelloRahmanBarostat::scheduleTask(Step step,
+                                            Time gmx_unused            time,
+                                            const RegisterRunFunction& registerRunFunction)
 {
     const bool scaleOnNextStep = do_per_step(step + nstpcouple_ + offset_ + 1, nstpcouple_);
     const bool scaleOnThisStep = do_per_step(step + nstpcouple_ + offset_, nstpcouple_);
 
     if (scaleOnThisStep)
     {
-        (*registerRunFunction)(
-                std::make_unique<SimulatorRunFunction>([this]() { scaleBoxAndPositions(); }));
+        registerRunFunction([this]() { scaleBoxAndPositions(); });
     }
     if (scaleOnNextStep)
     {
-        (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-                [this, step]() { integrateBoxVelocityEquations(step); }));
+        registerRunFunction([this, step]() { integrateBoxVelocityEquations(step); });
         // let propagator know that it will have to scale on next step
-        (*propagatorCallback_)(step + 1);
+        propagatorCallback_(step + 1);
     }
 }
 
 void ParrinelloRahmanBarostat::integrateBoxVelocityEquations(Step step)
 {
     auto box = statePropagatorData_->constBox();
-    parrinellorahman_pcoupl(fplog_, step, inputrec_, couplingTimeStep_, energyElement_->pressure(step),
+    parrinellorahman_pcoupl(fplog_, step, inputrec_, couplingTimeStep_, energyData_->pressure(step),
                             box, boxRel_, boxVelocity_, scalingTensor_.data(), mu_, false);
     // multiply matrix by the coupling time step to avoid having the propagator needing to know about that
     msmul(scalingTensor_.data(), couplingTimeStep_, scalingTensor_.data());
@@ -161,6 +147,15 @@ void ParrinelloRahmanBarostat::scaleBoxAndPositions()
 
 void ParrinelloRahmanBarostat::elementSetup()
 {
+    if (!propagatorCallback_ || scalingTensor_.empty())
+    {
+        throw MissingElementConnectionError(
+                "Parrinello-Rahman barostat was not connected to a propagator.\n"
+                "Connection to a propagator element is needed to scale the velocities.\n"
+                "Use connectWithPropagator(...) before building the ModularSimulatorAlgorithm "
+                "object.");
+    }
+
     if (inputrecPreserveShape(inputrec_))
     {
         auto      box  = statePropagatorData_->box();
@@ -185,7 +180,7 @@ void ParrinelloRahmanBarostat::elementSetup()
         // multiply matrix by the coupling time step to avoid having the propagator needing to know about that
         msmul(scalingTensor_.data(), couplingTimeStep_, scalingTensor_.data());
 
-        (*propagatorCallback_)(initStep_);
+        propagatorCallback_(initStep_);
     }
 }
 
@@ -194,12 +189,111 @@ const rvec* ParrinelloRahmanBarostat::boxVelocities() const
     return boxVelocity_;
 }
 
-void ParrinelloRahmanBarostat::writeCheckpoint(t_state* localState, t_state gmx_unused* globalState)
+real ParrinelloRahmanBarostat::conservedEnergyContribution() const
 {
-    copy_mat(boxVelocity_, localState->boxv);
-    copy_mat(boxRel_, localState->box_rel);
-    localState->flags |= (1U << estBOXV) | (1U << estBOX_REL);
+    real        energy       = 0;
+    const auto* box          = statePropagatorData_->constBox();
+    real        maxBoxLength = std::max({ box[XX][XX], box[YY][YY], box[ZZ][ZZ] });
+    real        volume       = det(box);
+
+    // contribution from the pressure momenta
+    for (int i = 0; i < DIM; i++)
+    {
+        for (int j = 0; j <= i; j++)
+        {
+            real invMass = PRESFAC * (4 * M_PI * M_PI * inputrec_->compress[i][j])
+                           / (3 * inputrec_->tau_p * inputrec_->tau_p * maxBoxLength);
+            if (invMass > 0)
+            {
+                energy += 0.5 * boxVelocity_[i][j] * boxVelocity_[i][j] / invMass;
+            }
+        }
+    }
+
+    /* Contribution from the PV term.
+     * Note that with non-zero off-diagonal reference pressures,
+     * i.e. applied shear stresses, there are additional terms.
+     * We don't support this here, since that requires keeping
+     * track of unwrapped box diagonal elements. This case is
+     * excluded in integratorHasConservedEnergyQuantity().
+     */
+    energy += volume * trace(inputrec_->ref_p) / (DIM * PRESFAC);
+
+    return energy;
 }
 
+namespace
+{
+/*!
+ * \brief Enum describing the contents ParrinelloRahmanBarostat writes to modular checkpoint
+ *
+ * When changing the checkpoint content, add a new element just above Count, and adjust the
+ * checkpoint functionality.
+ */
+enum class CheckpointVersion
+{
+    Base, //!< First version of modular checkpointing
+    Count //!< Number of entries. Add new versions right above this!
+};
+constexpr auto c_currentVersion = CheckpointVersion(int(CheckpointVersion::Count) - 1);
+} // namespace
+
+template<CheckpointDataOperation operation>
+void ParrinelloRahmanBarostat::doCheckpointData(CheckpointData<operation>* checkpointData)
+{
+    checkpointVersion(checkpointData, "ParrinelloRahmanBarostat version", c_currentVersion);
+
+    checkpointData->tensor("box velocity", boxVelocity_);
+    checkpointData->tensor("relative box vector", boxRel_);
+}
+
+void ParrinelloRahmanBarostat::saveCheckpointState(std::optional<WriteCheckpointData> checkpointData,
+                                                   const t_commrec*                   cr)
+{
+    if (MASTER(cr))
+    {
+        doCheckpointData<CheckpointDataOperation::Write>(&checkpointData.value());
+    }
+}
+
+void ParrinelloRahmanBarostat::restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData,
+                                                      const t_commrec*                  cr)
+{
+    if (MASTER(cr))
+    {
+        doCheckpointData<CheckpointDataOperation::Read>(&checkpointData.value());
+    }
+    if (DOMAINDECOMP(cr))
+    {
+        dd_bcast(cr->dd, sizeof(boxVelocity_), boxVelocity_);
+        dd_bcast(cr->dd, sizeof(boxRel_), boxRel_);
+    }
+}
+
+const std::string& ParrinelloRahmanBarostat::clientID()
+{
+    return identifier_;
+}
+
+ISimulatorElement* ParrinelloRahmanBarostat::getElementPointerImpl(
+        LegacySimulatorData*                    legacySimulatorData,
+        ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+        StatePropagatorData*                    statePropagatorData,
+        EnergyData*                             energyData,
+        FreeEnergyPerturbationData gmx_unused* freeEnergyPerturbationData,
+        GlobalCommunicationHelper gmx_unused* globalCommunicationHelper,
+        int                                   offset)
+{
+    auto* element  = builderHelper->storeElement(std::make_unique<ParrinelloRahmanBarostat>(
+            legacySimulatorData->inputrec->nstpcouple, offset,
+            legacySimulatorData->inputrec->delta_t * legacySimulatorData->inputrec->nstpcouple,
+            legacySimulatorData->inputrec->init_step, statePropagatorData, energyData,
+            legacySimulatorData->fplog, legacySimulatorData->inputrec, legacySimulatorData->mdAtoms));
+    auto* barostat = static_cast<ParrinelloRahmanBarostat*>(element);
+    builderHelper->registerBarostat([barostat](const PropagatorBarostatConnection& connection) {
+        barostat->connectWithPropagator(connection);
+    });
+    return element;
+}
 
 } // namespace gmx
index 25b40d546bc0a801baf9f37a3c3f221441a0e72b..77ede5316557e06407ec222c53b6426d77e99aef 100644 (file)
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the Parrinello-Rahman barostat for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_PARRINELLORAHMANBAROSTAT_H
@@ -52,11 +54,13 @@ struct t_commrec;
 
 namespace gmx
 {
-class EnergyElement;
+enum class CheckpointDataOperation;
+class EnergyData;
+class LegacySimulatorData;
 class MDAtoms;
 class StatePropagatorData;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Element implementing the Parrinello-Rahman barostat
  *
@@ -70,28 +74,23 @@ class ParrinelloRahmanBarostat final : public ISimulatorElement, public ICheckpo
 {
 public:
     //! Constructor
-    ParrinelloRahmanBarostat(int                   nstpcouple,
-                             int                   offset,
-                             real                  couplingTimeStep,
-                             Step                  initStep,
-                             ArrayRef<rvec>        scalingTensor,
-                             PropagatorCallbackPtr propagatorCallback,
-                             StatePropagatorData*  statePropagatorData,
-                             EnergyElement*        energyElement,
-                             FILE*                 fplog,
-                             const t_inputrec*     inputrec,
-                             const MDAtoms*        mdAtoms,
-                             const t_state*        globalState,
-                             t_commrec*            cr,
-                             bool                  isRestart);
+    ParrinelloRahmanBarostat(int                  nstpcouple,
+                             int                  offset,
+                             real                 couplingTimeStep,
+                             Step                 initStep,
+                             StatePropagatorData* statePropagatorData,
+                             EnergyData*          energyData,
+                             FILE*                fplog,
+                             const t_inputrec*    inputrec,
+                             const MDAtoms*       mdAtoms);
 
     /*! \brief Register run function for step / time
      *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
      */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
 
     //! Fix relative box shape
     void elementSetup() override;
@@ -99,7 +98,39 @@ public:
     void elementTeardown() override {}
 
     //! Getter for the box velocities
-    const rvec* boxVelocities() const;
+    [[nodiscard]] const rvec* boxVelocities() const;
+    //! Contribution to the conserved energy (called by energy data)
+    [[nodiscard]] real conservedEnergyContribution() const;
+
+    //! Connect this to propagator
+    void connectWithPropagator(const PropagatorBarostatConnection& connectionData);
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     * \param offset  The step offset at which the barostat is applied
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    int                        offset);
 
 private:
     //! The frequency at which the barostat is applied
@@ -114,7 +145,7 @@ private:
     //! View on the velocity scaling tensor (owned by the propagator)
     ArrayRef<rvec> scalingTensor_;
     //! Callback to let propagator know that we updated lambda
-    PropagatorCallbackPtr propagatorCallback_;
+    PropagatorCallback propagatorCallback_;
 
     //! Relative change in box before - after barostatting
     matrix mu_;
@@ -123,18 +154,22 @@ private:
     //! Box velocity
     tensor boxVelocity_;
 
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
     //! Pointer to the micro state
     StatePropagatorData* statePropagatorData_;
-    //! Pointer to the energy element
-    EnergyElement* energyElement_;
+    //! Pointer to the energy data
+    EnergyData* energyData_;
 
     //! Integrate the PR box vector equations of motion - does not alter state
     void integrateBoxVelocityEquations(Step step);
     //! Scale box and positions
     void scaleBoxAndPositions();
 
-    //! ICheckpointHelperClient implementation
-    void writeCheckpoint(t_state* localState, t_state* globalState) override;
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "ParrinelloRahmanBarostat";
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
 
     // Access to ISimulator data
     //! Handles logging.
index 54df4768d77a2b295dd5116f4a2ee60f10ab1130..a75ea901a6fb19271cf783e96c1618870a58cafd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,7 @@
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/mdrunoptions.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/nbnxm/nbnxm.h"
@@ -125,10 +126,9 @@ const pme_load_balancing_t* PmeLoadBalanceHelper::loadBalancingObject()
     return pme_loadbal_;
 }
 
-SignallerCallbackPtr PmeLoadBalanceHelper::registerNSCallback()
+std::optional<SignallerCallback> PmeLoadBalanceHelper::registerNSCallback()
 {
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { nextNSStep_ = step; });
+    return [this](Step step, Time gmx_unused time) { nextNSStep_ = step; };
 }
 
 } // namespace gmx
index 39ffa1d03936a5288318da72358baef78c4ab8dd..29e5f99a8766d6c07b67c8f03682f161752f0445 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the PME load balancing helper for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_PMELOADBALANCEHELPER_H
@@ -56,7 +58,7 @@ class MDLogger;
 struct MdrunOptions;
 class StatePropagatorData;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Infrastructure element responsible for PME load balancing
  *
@@ -104,7 +106,7 @@ private:
     pme_load_balancing_t* pme_loadbal_;
 
     //! INeighborSearchSignallerClient implementation
-    SignallerCallbackPtr registerNSCallback() override;
+    std::optional<SignallerCallback> registerNSCallback() override;
 
     //! The next NS step
     Step nextNSStep_;
@@ -113,6 +115,7 @@ private:
     //! Whether PME load balancing printing is active \todo Check this!
     bool bPMETunePrinting_;
 
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
     //! Pointer to the micro state
     StatePropagatorData* statePropagatorData_;
 
index f35c225701aac59c4f246a723837e8f012fb329d..b40abb9b1e6e2dadf7780ded7e1c69f6fdcab835 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/update.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/timing/wallcycle.h"
 #include "gromacs/utility/fatalerror.h"
 
+#include "modularsimulator.h"
+#include "simulatoralgorithm.h"
 #include "statepropagatordata.h"
 
 namespace gmx
@@ -112,6 +116,30 @@ static void inline updatePositions(int         a,
     }
 }
 
+//! Helper function diagonalizing the PR matrix if possible
+template<ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
+static inline bool diagonalizePRMatrix(matrix matrixPR, rvec diagPR)
+{
+    if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::Full)
+    {
+        return false;
+    }
+    else
+    {
+        if (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0)
+        {
+            diagPR[XX] = matrixPR[XX][XX];
+            diagPR[YY] = matrixPR[YY][YY];
+            diagPR[ZZ] = matrixPR[ZZ][ZZ];
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+}
+
 //! Propagation (position only)
 template<>
 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
@@ -152,32 +180,22 @@ void Propagator<IntegrationStep::VelocitiesOnly>::run()
     wallcycle_start(wcycle_, ewcUPDATE);
 
     auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
-    auto f = as_rvec_array(statePropagatorData_->constForcesView().paddedArrayRef().data());
+    auto f = as_rvec_array(statePropagatorData_->constForcesView().force().data());
     auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
 
     const real lambda =
             (numVelocityScalingValues == NumVelocityScalingValues::Single) ? velocityScaling_[0] : 1.0;
 
-    bool doDiagonalScaling = false;
-    if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::No)
-    {
-        // TODO: Could we know in advance whether the matrix is diagonal?
-        doDiagonalScaling = (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0);
-        if (doDiagonalScaling)
-        {
-            diagPR[XX] = matrixPR[XX][XX];
-            diagPR[YY] = matrixPR[YY][YY];
-            diagPR[ZZ] = matrixPR[ZZ][ZZ];
-        }
-    }
+    const bool isFullScalingMatrixDiagonal =
+            diagonalizePRMatrix<parrinelloRahmanVelocityScaling>(matrixPR_, diagPR_);
 
-    int nth    = gmx_omp_nthreads_get(emntUpdate);
-    int homenr = mdAtoms_->mdatoms()->homenr;
+    const int nth    = gmx_omp_nthreads_get(emntUpdate);
+    const int homenr = mdAtoms_->mdatoms()->homenr;
 
-// lambda could be shared, but gcc-8 & gcc-9 don't agree how to write that...
+// const variables could be shared, but gcc-8 & gcc-9 don't agree how to write that...
 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
 #pragma omp parallel for num_threads(nth) schedule(static) default(none) \
-        shared(nth, homenr, v, f, invMassPerDim, doDiagonalScaling) firstprivate(lambda)
+        shared(v, f, invMassPerDim) firstprivate(nth, homenr, lambda, isFullScalingMatrixDiagonal)
     for (int th = 0; th < nth; th++)
     {
         try
@@ -187,50 +205,23 @@ void Propagator<IntegrationStep::VelocitiesOnly>::run()
 
             for (int a = start_th; a < end_th; a++)
             {
-                if (parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
+                if (isFullScalingMatrixDiagonal)
                 {
-                    if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
-                    {
-                        updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>(
-                                a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
-                                invMassPerDim, v, f, diagPR, matrixPR);
-                    }
-                    else
-                    {
-                        updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::No>(
-                                a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
-                    }
+                    updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
+                            a, timestep_,
+                            numVelocityScalingValues == NumVelocityScalingValues::Multiple
+                                    ? velocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
+                                    : lambda,
+                            invMassPerDim, v, f, diagPR_, matrixPR_);
                 }
                 else
                 {
-                    if (doDiagonalScaling)
-                    {
-                        if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
-                        {
-                            updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Diagonal>(
-                                    a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
-                                    invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                        else
-                        {
-                            updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
-                                    a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                    }
-                    else
-                    {
-                        if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
-                        {
-                            updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>(
-                                    a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
-                                    invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                        else
-                        {
-                            updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Full>(
-                                    a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                    }
+                    updateVelocities<numVelocityScalingValues, parrinelloRahmanVelocityScaling>(
+                            a, timestep_,
+                            numVelocityScalingValues == NumVelocityScalingValues::Multiple
+                                    ? velocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
+                                    : lambda,
+                            invMassPerDim, v, f, diagPR_, matrixPR_);
                 }
             }
         }
@@ -249,32 +240,22 @@ void Propagator<IntegrationStep::LeapFrog>::run()
     auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
     auto x = as_rvec_array(statePropagatorData_->constPreviousPositionsView().paddedArrayRef().data());
     auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
-    auto f = as_rvec_array(statePropagatorData_->constForcesView().paddedArrayRef().data());
+    auto f = as_rvec_array(statePropagatorData_->constForcesView().force().data());
     auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
 
     const real lambda =
             (numVelocityScalingValues == NumVelocityScalingValues::Single) ? velocityScaling_[0] : 1.0;
 
-    bool doDiagonalScaling = false;
-    if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::No)
-    {
-        // TODO: Could we know in advance whether the matrix is diagonal?
-        doDiagonalScaling = (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0);
-        if (doDiagonalScaling)
-        {
-            diagPR[XX] = matrixPR[XX][XX];
-            diagPR[YY] = matrixPR[YY][YY];
-            diagPR[ZZ] = matrixPR[ZZ][ZZ];
-        }
-    }
+    const bool isFullScalingMatrixDiagonal =
+            diagonalizePRMatrix<parrinelloRahmanVelocityScaling>(matrixPR_, diagPR_);
 
-    int nth    = gmx_omp_nthreads_get(emntUpdate);
-    int homenr = mdAtoms_->mdatoms()->homenr;
+    const int nth    = gmx_omp_nthreads_get(emntUpdate);
+    const int homenr = mdAtoms_->mdatoms()->homenr;
 
-// lambda could be shared, but gcc-8 & gcc-9 don't agree how to write that...
+// const variables could be shared, but gcc-8 & gcc-9 don't agree how to write that...
 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
-#pragma omp parallel for num_threads(nth) schedule(static) default(none) \
-        shared(nth, homenr, x, xp, v, f, invMassPerDim, doDiagonalScaling) firstprivate(lambda)
+#pragma omp parallel for num_threads(nth) schedule(static) default(none) shared( \
+        x, xp, v, f, invMassPerDim) firstprivate(nth, homenr, lambda, isFullScalingMatrixDiagonal)
     for (int th = 0; th < nth; th++)
     {
         try
@@ -284,50 +265,23 @@ void Propagator<IntegrationStep::LeapFrog>::run()
 
             for (int a = start_th; a < end_th; a++)
             {
-                if (parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
+                if (isFullScalingMatrixDiagonal)
                 {
-                    if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
-                    {
-                        updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>(
-                                a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
-                                invMassPerDim, v, f, diagPR, matrixPR);
-                    }
-                    else
-                    {
-                        updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::No>(
-                                a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
-                    }
+                    updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
+                            a, timestep_,
+                            numVelocityScalingValues == NumVelocityScalingValues::Multiple
+                                    ? velocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
+                                    : lambda,
+                            invMassPerDim, v, f, diagPR_, matrixPR_);
                 }
                 else
                 {
-                    if (doDiagonalScaling)
-                    {
-                        if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
-                        {
-                            updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Diagonal>(
-                                    a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
-                                    invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                        else
-                        {
-                            updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
-                                    a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                    }
-                    else
-                    {
-                        if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
-                        {
-                            updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>(
-                                    a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
-                                    invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                        else
-                        {
-                            updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Full>(
-                                    a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                    }
+                    updateVelocities<numVelocityScalingValues, parrinelloRahmanVelocityScaling>(
+                            a, timestep_,
+                            numVelocityScalingValues == NumVelocityScalingValues::Multiple
+                                    ? velocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
+                                    : lambda,
+                            invMassPerDim, v, f, diagPR_, matrixPR_);
                 }
                 updatePositions(a, timestep_, x, xp, v);
             }
@@ -347,32 +301,22 @@ void Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>::run()
     auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
     auto x = as_rvec_array(statePropagatorData_->constPreviousPositionsView().paddedArrayRef().data());
     auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
-    auto f = as_rvec_array(statePropagatorData_->constForcesView().paddedArrayRef().data());
+    auto f = as_rvec_array(statePropagatorData_->constForcesView().force().data());
     auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
 
-    int nth    = gmx_omp_nthreads_get(emntUpdate);
-    int homenr = mdAtoms_->mdatoms()->homenr;
-
     const real lambda =
             (numVelocityScalingValues == NumVelocityScalingValues::Single) ? velocityScaling_[0] : 1.0;
 
-    bool doDiagonalScaling = false;
-    if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::No)
-    {
-        // TODO: Could we know in advance whether the matrix is diagonal?
-        doDiagonalScaling = (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0);
-        if (doDiagonalScaling)
-        {
-            diagPR[XX] = matrixPR[XX][XX];
-            diagPR[YY] = matrixPR[YY][YY];
-            diagPR[ZZ] = matrixPR[ZZ][ZZ];
-        }
-    }
+    const bool isFullScalingMatrixDiagonal =
+            diagonalizePRMatrix<parrinelloRahmanVelocityScaling>(matrixPR_, diagPR_);
+
+    const int nth    = gmx_omp_nthreads_get(emntUpdate);
+    const int homenr = mdAtoms_->mdatoms()->homenr;
 
-// lambda could be shared, but gcc-8 & gcc-9 don't agree how to write that...
+// const variables could be shared, but gcc-8 & gcc-9 don't agree how to write that...
 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
-#pragma omp parallel for num_threads(nth) schedule(static) default(none) \
-        shared(nth, homenr, x, xp, v, f, invMassPerDim, doDiagonalScaling) firstprivate(lambda)
+#pragma omp parallel for num_threads(nth) schedule(static) default(none) shared( \
+        x, xp, v, f, invMassPerDim) firstprivate(nth, homenr, lambda, isFullScalingMatrixDiagonal)
     for (int th = 0; th < nth; th++)
     {
         try
@@ -382,50 +326,23 @@ void Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>::run()
 
             for (int a = start_th; a < end_th; a++)
             {
-                if (parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
+                if (isFullScalingMatrixDiagonal)
                 {
-                    if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
-                    {
-                        updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>(
-                                a, 0.5 * timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
-                                invMassPerDim, v, f, diagPR, matrixPR);
-                    }
-                    else
-                    {
-                        updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::No>(
-                                a, 0.5 * timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
-                    }
+                    updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
+                            a, 0.5 * timestep_,
+                            numVelocityScalingValues == NumVelocityScalingValues::Multiple
+                                    ? velocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
+                                    : lambda,
+                            invMassPerDim, v, f, diagPR_, matrixPR_);
                 }
                 else
                 {
-                    if (doDiagonalScaling)
-                    {
-                        if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
-                        {
-                            updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Diagonal>(
-                                    a, 0.5 * timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
-                                    invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                        else
-                        {
-                            updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
-                                    a, 0.5 * timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                    }
-                    else
-                    {
-                        if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
-                        {
-                            updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>(
-                                    a, 0.5 * timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
-                                    invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                        else
-                        {
-                            updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Full>(
-                                    a, 0.5 * timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
-                        }
-                    }
+                    updateVelocities<numVelocityScalingValues, parrinelloRahmanVelocityScaling>(
+                            a, 0.5 * timestep_,
+                            numVelocityScalingValues == NumVelocityScalingValues::Multiple
+                                    ? velocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
+                                    : lambda,
+                            invMassPerDim, v, f, diagPR_, matrixPR_);
                 }
                 updatePositions(a, timestep_, x, xp, v);
             }
@@ -442,24 +359,24 @@ Propagator<algorithm>::Propagator(double               timestep,
                                   gmx_wallcycle*       wcycle) :
     timestep_(timestep),
     statePropagatorData_(statePropagatorData),
-    doSingleVelocityScaling(false),
-    doGroupVelocityScaling(false),
+    doSingleVelocityScaling_(false),
+    doGroupVelocityScaling_(false),
     scalingStepVelocity_(-1),
+    diagPR_{ 0 },
+    matrixPR_{ { 0 } },
     scalingStepPR_(-1),
     mdAtoms_(mdAtoms),
     wcycle_(wcycle)
 {
-    clear_rvec(diagPR);
-    clear_mat(matrixPR);
 }
 
 template<IntegrationStep algorithm>
 void Propagator<algorithm>::scheduleTask(Step gmx_unused step,
-                                         Time gmx_unused               time,
-                                         const RegisterRunFunctionPtr& registerRunFunction)
+                                         Time gmx_unused            time,
+                                         const RegisterRunFunction& registerRunFunction)
 {
-    const bool doSingleVScalingThisStep = (doSingleVelocityScaling && (step == scalingStepVelocity_));
-    const bool doGroupVScalingThisStep = (doGroupVelocityScaling && (step == scalingStepVelocity_));
+    const bool doSingleVScalingThisStep = (doSingleVelocityScaling_ && (step == scalingStepVelocity_));
+    const bool doGroupVScalingThisStep = (doGroupVelocityScaling_ && (step == scalingStepVelocity_));
 
     const bool doParrinelloRahmanThisStep = (step == scalingStepPR_);
 
@@ -467,45 +384,45 @@ void Propagator<algorithm>::scheduleTask(Step gmx_unused step,
     {
         if (doParrinelloRahmanThisStep)
         {
-            (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
+            registerRunFunction([this]() {
                 run<NumVelocityScalingValues::Single, ParrinelloRahmanVelocityScaling::Full>();
-            }));
+            });
         }
         else
         {
-            (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
+            registerRunFunction([this]() {
                 run<NumVelocityScalingValues::Single, ParrinelloRahmanVelocityScaling::No>();
-            }));
+            });
         }
     }
     else if (doGroupVScalingThisStep)
     {
         if (doParrinelloRahmanThisStep)
         {
-            (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
+            registerRunFunction([this]() {
                 run<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>();
-            }));
+            });
         }
         else
         {
-            (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
+            registerRunFunction([this]() {
                 run<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>();
-            }));
+            });
         }
     }
     else
     {
         if (doParrinelloRahmanThisStep)
         {
-            (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
+            registerRunFunction([this]() {
                 run<NumVelocityScalingValues::None, ParrinelloRahmanVelocityScaling::Full>();
-            }));
+            });
         }
         else
         {
-            (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
+            registerRunFunction([this]() {
                 run<NumVelocityScalingValues::None, ParrinelloRahmanVelocityScaling::No>();
-            }));
+            });
         }
     }
 }
@@ -521,8 +438,8 @@ void Propagator<algorithm>::setNumVelocityScalingVariables(int numVelocityScalin
                "Number of velocity scaling variables cannot be changed once set.");
 
     velocityScaling_.resize(numVelocityScalingVariables, 1.);
-    doSingleVelocityScaling = numVelocityScalingVariables == 1;
-    doGroupVelocityScaling  = numVelocityScalingVariables > 1;
+    doSingleVelocityScaling_ = numVelocityScalingVariables == 1;
+    doGroupVelocityScaling_  = numVelocityScalingVariables > 1;
 }
 
 template<IntegrationStep algorithm>
@@ -538,14 +455,14 @@ ArrayRef<real> Propagator<algorithm>::viewOnVelocityScaling()
 }
 
 template<IntegrationStep algorithm>
-std::unique_ptr<std::function<void(Step)>> Propagator<algorithm>::velocityScalingCallback()
+PropagatorCallback Propagator<algorithm>::velocityScalingCallback()
 {
     if (algorithm == IntegrationStep::PositionsOnly)
     {
         gmx_fatal(FARGS, "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
     }
 
-    return std::make_unique<PropagatorCallback>([this](Step step) { scalingStepVelocity_ = step; });
+    return [this](Step step) { scalingStepVelocity_ = step; };
 }
 
 template<IntegrationStep algorithm>
@@ -555,28 +472,58 @@ ArrayRef<rvec> Propagator<algorithm>::viewOnPRScalingMatrix()
             algorithm != IntegrationStep::PositionsOnly,
             "Parrinello-Rahman scaling not implemented for IntegrationStep::PositionsOnly.");
 
-    clear_mat(matrixPR);
+    clear_mat(matrixPR_);
     // gcc-5 needs this to be explicit (all other tested compilers would be ok
     // with simply returning matrixPR)
-    return ArrayRef<rvec>(matrixPR);
+    return ArrayRef<rvec>(matrixPR_);
 }
 
 template<IntegrationStep algorithm>
-PropagatorCallbackPtr Propagator<algorithm>::prScalingCallback()
+PropagatorCallback Propagator<algorithm>::prScalingCallback()
 {
     GMX_RELEASE_ASSERT(
             algorithm != IntegrationStep::PositionsOnly,
             "Parrinello-Rahman scaling not implemented for IntegrationStep::PositionsOnly.");
 
-    return std::make_unique<PropagatorCallback>([this](Step step) { scalingStepPR_ = step; });
+    return [this](Step step) { scalingStepPR_ = step; };
+}
+
+template<IntegrationStep algorithm>
+ISimulatorElement* Propagator<algorithm>::getElementPointerImpl(
+        LegacySimulatorData*                    legacySimulatorData,
+        ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+        StatePropagatorData*                    statePropagatorData,
+        EnergyData gmx_unused*     energyData,
+        FreeEnergyPerturbationData gmx_unused* freeEnergyPerturbationData,
+        GlobalCommunicationHelper gmx_unused* globalCommunicationHelper,
+        double                                timestep,
+        RegisterWithThermostat                registerWithThermostat,
+        RegisterWithBarostat                  registerWithBarostat)
+{
+    auto* element = builderHelper->storeElement(std::make_unique<Propagator<algorithm>>(
+            timestep, statePropagatorData, legacySimulatorData->mdAtoms, legacySimulatorData->wcycle));
+    if (registerWithThermostat == RegisterWithThermostat::True)
+    {
+        auto* propagator = static_cast<Propagator<algorithm>*>(element);
+        builderHelper->registerWithThermostat(
+                { [propagator](int num) { propagator->setNumVelocityScalingVariables(num); },
+                  [propagator]() { return propagator->viewOnVelocityScaling(); },
+                  [propagator]() { return propagator->velocityScalingCallback(); } });
+    }
+    if (registerWithBarostat == RegisterWithBarostat::True)
+    {
+        auto* propagator = static_cast<Propagator<algorithm>*>(element);
+        builderHelper->registerWithBarostat(
+                { [propagator]() { return propagator->viewOnPRScalingMatrix(); },
+                  [propagator]() { return propagator->prScalingCallback(); } });
+    }
+    return element;
 }
 
-//! Explicit template initialization
-//! @{
+// Explicit template initializations
 template class Propagator<IntegrationStep::PositionsOnly>;
 template class Propagator<IntegrationStep::VelocitiesOnly>;
 template class Propagator<IntegrationStep::LeapFrog>;
 template class Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>;
-//! @}
 
 } // namespace gmx
index 01a22d57f7a08b913906b9ac5e961a39fd3365e3..df505c5f7fc94aac0b214a9cbe6b7ba45dde8404 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the propagator element for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_PROPAGATOR_H
@@ -54,12 +56,30 @@ struct gmx_wallcycle;
 
 namespace gmx
 {
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
 class MDAtoms;
+class ModularSimulatorAlgorithmBuilderHelper;
 class StatePropagatorData;
 
 //! \addtogroup module_modularsimulator
 //! \{
 
+//! Whether built propagator should be registered with thermostat
+enum class RegisterWithThermostat
+{
+    True,
+    False
+};
+//! Whether built propagator should be registered with barostat
+enum class RegisterWithBarostat
+{
+    True,
+    False
+};
+
 /*! \brief The different integration types we know about
  *
  * PositionsOnly:
@@ -100,12 +120,7 @@ enum class ParrinelloRahmanVelocityScaling
     Count
 };
 
-//! Generic callback to the propagator
-typedef std::function<void(Step)> PropagatorCallback;
-//! Pointer to generic callback to the propagator
-typedef std::unique_ptr<PropagatorCallback> PropagatorCallbackPtr;
-
-/*! \libinternal
+/*! \internal
  * \brief Propagator element
  *
  * The propagator element can, through templating, cover the different
@@ -114,10 +129,7 @@ typedef std::unique_ptr<PropagatorCallback> PropagatorCallbackPtr;
  * functions allows to have performance comparable to fused update elements
  * while keeping easily re-orderable single instructions.
  *
- * \todo: Get rid of updateVelocities2() once we don't require identical
- *        reproduction of do_md() results.
- *
- * @tparam algorithm  The integration types
+ * \tparam algorithm  The integration types
  */
 template<IntegrationStep algorithm>
 class Propagator final : public ISimulatorElement
@@ -131,11 +143,11 @@ public:
 
     /*! \brief Register run function for step / time
      *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
      */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
 
     //! No element setup needed
     void elementSetup() override {}
@@ -147,12 +159,36 @@ public:
     //! Get view on the velocity scaling vector
     ArrayRef<real> viewOnVelocityScaling();
     //! Get velocity scaling callback
-    PropagatorCallbackPtr velocityScalingCallback();
+    PropagatorCallback velocityScalingCallback();
 
     //! Get view on the full PR scaling matrix
     ArrayRef<rvec> viewOnPRScalingMatrix();
     //! Get PR scaling callback
-    PropagatorCallbackPtr prScalingCallback();
+    PropagatorCallback prScalingCallback();
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     * \param timestep  The time step the propagator uses
+     * \param registerWithThermostat  Whether this propagator should be registered with the thermostat
+     * \param registerWithBarostat  Whether this propagator should be registered with the barostat
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    double                     timestep,
+                                                    RegisterWithThermostat registerWithThermostat,
+                                                    RegisterWithBarostat   registerWithBarostat);
 
 private:
     //! The actual propagation
@@ -162,22 +198,23 @@ private:
     //! The time step
     const real timestep_;
 
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
     //! Pointer to the micro state
     StatePropagatorData* statePropagatorData_;
 
     //! Whether we're doing single-value velocity scaling
-    bool doSingleVelocityScaling;
+    bool doSingleVelocityScaling_;
     //! Wether we're doing group-wise velocity scaling
-    bool doGroupVelocityScaling;
+    bool doGroupVelocityScaling_;
     //! The vector of velocity scaling values
     std::vector<real> velocityScaling_;
     //! The next velocity scaling step
     Step scalingStepVelocity_;
 
     //! The diagonal of the PR scaling matrix
-    rvec diagPR;
+    rvec diagPR_;
     //! The full PR scaling matrix
-    matrix matrixPR;
+    matrix matrixPR_;
     //! The next PR scaling step
     Step scalingStepPR_;
 
diff --git a/src/gromacs/modularsimulator/shellfcelement.cpp b/src/gromacs/modularsimulator/shellfcelement.cpp
deleted file mode 100644 (file)
index 8ccc6ab..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2019,2020, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-/*! \internal \file
- * \brief Defines the shell / flex constraints element for the modular simulator
- *
- * \author Pascal Merz <pascal.merz@me.com>
- * \ingroup module_modularsimulator
- */
-
-#include "gmxpre.h"
-
-#include "shellfcelement.h"
-
-#include "gromacs/domdec/mdsetup.h"
-#include "gromacs/math/vectypes.h"
-#include "gromacs/mdlib/constr.h"
-#include "gromacs/mdlib/force.h"
-#include "gromacs/mdlib/force_flags.h"
-#include "gromacs/mdlib/mdatoms.h"
-#include "gromacs/mdrun/shellfc.h"
-#include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/pbcutil/pbc.h"
-#include "gromacs/topology/atoms.h"
-#include "gromacs/topology/mtop_util.h"
-
-#include "energyelement.h"
-#include "freeenergyperturbationelement.h"
-#include "statepropagatordata.h"
-
-struct gmx_edsam;
-struct gmx_enfrot;
-struct gmx_multisim_t;
-class history_t;
-struct t_graph;
-
-namespace gmx
-{
-bool ShellFCElement::doShellsOrFlexConstraints(const gmx_mtop_t& mtop, int nflexcon)
-{
-    if (nflexcon != 0)
-    {
-        return true;
-    }
-    std::array<int, eptNR> n = gmx_mtop_particletype_count(mtop);
-    return n[eptShell] != 0;
-}
-
-ShellFCElement::ShellFCElement(StatePropagatorData*           statePropagatorData,
-                               EnergyElement*                 energyElement,
-                               FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                               bool                           isVerbose,
-                               bool                           isDynamicBox,
-                               FILE*                          fplog,
-                               const t_commrec*               cr,
-                               const t_inputrec*              inputrec,
-                               const MDAtoms*                 mdAtoms,
-                               t_nrnb*                        nrnb,
-                               t_forcerec*                    fr,
-                               t_fcdata*                      fcd,
-                               gmx_wallcycle*                 wcycle,
-                               MdrunScheduleWorkload*         runScheduleWork,
-                               gmx_vsite_t*                   vsite,
-                               ImdSession*                    imdSession,
-                               pull_t*                        pull_work,
-                               Constraints*                   constr,
-                               const gmx_mtop_t*              globalTopology,
-                               gmx_enfrot*                    enforcedRotation) :
-    nextNSStep_(-1),
-    nextEnergyCalculationStep_(-1),
-    nextVirialCalculationStep_(-1),
-    nextFreeEnergyCalculationStep_(-1),
-    statePropagatorData_(statePropagatorData),
-    energyElement_(energyElement),
-    freeEnergyPerturbationElement_(freeEnergyPerturbationElement),
-    localTopology_(nullptr),
-    isDynamicBox_(isDynamicBox),
-    isVerbose_(isVerbose),
-    nSteps_(0),
-    ddBalanceRegionHandler_(cr),
-    fplog_(fplog),
-    cr_(cr),
-    inputrec_(inputrec),
-    mdAtoms_(mdAtoms),
-    nrnb_(nrnb),
-    wcycle_(wcycle),
-    fr_(fr),
-    vsite_(vsite),
-    imdSession_(imdSession),
-    pull_work_(pull_work),
-    fcd_(fcd),
-    runScheduleWork_(runScheduleWork),
-    constr_(constr),
-    enforcedRotation_(enforcedRotation)
-{
-    lambda_.fill(0);
-
-    shellfc_ = init_shell_flexcon(fplog, globalTopology, constr_ ? constr_->numFlexibleConstraints() : 0,
-                                  inputrec->nstcalcenergy, DOMAINDECOMP(cr));
-
-    GMX_ASSERT(shellfc_, "ShellFCElement built, but init_shell_flexcon returned a nullptr");
-
-    if (!DOMAINDECOMP(cr))
-    {
-        // This was done in mdAlgorithmsSetupAtomData(), but shellfc
-        // won't be available outside this element.
-        make_local_shells(cr, mdAtoms->mdatoms(), shellfc_);
-    }
-}
-
-void ShellFCElement::scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction)
-{
-    unsigned int flags =
-            (GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES | (isDynamicBox_ ? GMX_FORCE_DYNAMICBOX : 0)
-             | (nextVirialCalculationStep_ == step ? GMX_FORCE_VIRIAL : 0)
-             | (nextEnergyCalculationStep_ == step ? GMX_FORCE_ENERGY : 0)
-             | (nextFreeEnergyCalculationStep_ == step ? GMX_FORCE_DHDL : 0));
-
-    const bool isNSStep = (step == nextNSStep_);
-    (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-            [this, step, time, flags, isNSStep]() { run(step, time, isNSStep, flags); }));
-    nSteps_++;
-}
-
-void ShellFCElement::elementSetup()
-{
-    GMX_ASSERT(localTopology_, "Setup called before local topology was set.");
-}
-
-void ShellFCElement::run(Step step, Time time, bool isNSStep, unsigned int flags)
-{
-    // Disabled functionality
-    gmx_multisim_t* ms    = nullptr;
-    t_graph*        graph = nullptr;
-
-    if (!DOMAINDECOMP(cr_) && isNSStep && inputrecDynamicBox(inputrec_))
-    {
-        // TODO: Correcting the box is done in DomDecHelper (if using DD) or here (non-DD simulations).
-        //       Think about unifying this responsibility, could this be done in one place?
-        auto box = statePropagatorData_->box();
-        correct_box(fplog_, step, box, graph);
-    }
-
-    auto       x      = statePropagatorData_->positionsView();
-    auto       v      = statePropagatorData_->velocitiesView();
-    auto       forces = statePropagatorData_->forcesView();
-    auto       box    = statePropagatorData_->constBox();
-    history_t* hist   = nullptr; // disabled
-
-    tensor force_vir = { { 0 } };
-    // TODO: Make lambda const (needs some adjustments in lower force routines)
-    ArrayRef<real> lambda =
-            freeEnergyPerturbationElement_ ? freeEnergyPerturbationElement_->lambdaView() : lambda_;
-    relax_shell_flexcon(fplog_, cr_, ms, isVerbose_, enforcedRotation_, step, inputrec_, imdSession_,
-                        pull_work_, isNSStep, static_cast<int>(flags), localTopology_, constr_,
-                        energyElement_->enerdata(), fcd_, statePropagatorData_->localNumAtoms(), x,
-                        v, box, lambda, hist, forces, force_vir, mdAtoms_->mdatoms(), nrnb_,
-                        wcycle_, graph, shellfc_, fr_, runScheduleWork_, time,
-                        energyElement_->muTot(), vsite_, ddBalanceRegionHandler_);
-    energyElement_->addToForceVirial(force_vir, step);
-}
-
-void ShellFCElement::elementTeardown()
-{
-    done_shellfc(fplog_, shellfc_, nSteps_);
-}
-
-void ShellFCElement::setTopology(const gmx_localtop_t* top)
-{
-    localTopology_ = top;
-}
-
-SignallerCallbackPtr ShellFCElement::registerNSCallback()
-{
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { this->nextNSStep_ = step; });
-}
-
-SignallerCallbackPtr ShellFCElement::registerEnergyCallback(EnergySignallerEvent event)
-{
-    if (event == EnergySignallerEvent::EnergyCalculationStep)
-    {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { nextEnergyCalculationStep_ = step; });
-    }
-    if (event == EnergySignallerEvent::VirialCalculationStep)
-    {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { nextVirialCalculationStep_ = step; });
-    }
-    if (event == EnergySignallerEvent::FreeEnergyCalculationStep)
-    {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { nextFreeEnergyCalculationStep_ = step; });
-    }
-    return nullptr;
-}
-} // namespace gmx
diff --git a/src/gromacs/modularsimulator/shellfcelement.h b/src/gromacs/modularsimulator/shellfcelement.h
deleted file mode 100644 (file)
index 548ff18..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2019,2020, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-/*! \libinternal \file
- * \brief Declares the shell / flex constraints element for the modular simulator
- *
- * \author Pascal Merz <pascal.merz@me.com>
- * \ingroup module_modularsimulator
- */
-
-#ifndef GMX_MODULARSIMULATOR_SHELLFCELEMENT_H
-#define GMX_MODULARSIMULATOR_SHELLFCELEMENT_H
-
-#include <array>
-
-#include "gromacs/domdec/dlbtiming.h"
-#include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/utility/real.h"
-
-#include "modularsimulatorinterfaces.h"
-#include "topologyholder.h"
-
-struct gmx_enfrot;
-struct gmx_shellfc_t;
-struct gmx_wallcycle;
-struct pull_t;
-struct t_fcdata;
-struct t_nrnb;
-
-namespace gmx
-{
-class Awh;
-class EnergyElement;
-class FreeEnergyPerturbationElement;
-class ImdSession;
-class MDAtoms;
-class MdrunScheduleWorkload;
-class StatePropagatorData;
-
-/*! \libinternal
- * \ingroup module_modularsimulator
- * \brief Shell & flex constraints element
- *
- * The ShellFCElement manages the call to relax_shell_flexcon(...)
- */
-class ShellFCElement final :
-    public ISimulatorElement,
-    public ITopologyHolderClient,
-    public INeighborSearchSignallerClient,
-    public IEnergySignallerClient
-{
-public:
-    //! Constructor
-    ShellFCElement(StatePropagatorData*           statePropagatorData,
-                   EnergyElement*                 energyElement,
-                   FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                   bool                           isVerbose,
-                   bool                           isDynamicBox,
-                   FILE*                          fplog,
-                   const t_commrec*               cr,
-                   const t_inputrec*              inputrec,
-                   const MDAtoms*                 mdAtoms,
-                   t_nrnb*                        nrnb,
-                   t_forcerec*                    fr,
-                   t_fcdata*                      fcd,
-                   gmx_wallcycle*                 wcycle,
-                   MdrunScheduleWorkload*         runScheduleWork,
-                   gmx_vsite_t*                   vsite,
-                   ImdSession*                    imdSession,
-                   pull_t*                        pull_work,
-                   Constraints*                   constr,
-                   const gmx_mtop_t*              globalTopology,
-                   gmx_enfrot*                    enforcedRotation);
-
-    /*! \brief Register shell / flex constraint calculation for step / time
-     *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
-     */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
-
-    //! Check that we got the local topology
-    void elementSetup() override;
-    //! Print some final output
-    void elementTeardown() override;
-
-    //! Whether either shells or flexible constraints are used
-    static bool doShellsOrFlexConstraints(const gmx_mtop_t& mtop, int nflexcon);
-
-private:
-    //! ITopologyHolderClient implementation
-    void setTopology(const gmx_localtop_t* top) override;
-    //! INeighborSearchSignallerClient implementation
-    SignallerCallbackPtr registerNSCallback() override;
-    //! IEnergySignallerClient implementation
-    SignallerCallbackPtr registerEnergyCallback(EnergySignallerEvent event) override;
-    //! The actual do_force call
-    void run(Step step, Time time, bool isNSStep, unsigned int flags);
-
-    //! The shell / FC helper struct
-    gmx_shellfc_t* shellfc_;
-
-    //! The next NS step
-    Step nextNSStep_;
-    //! The next energy calculation step
-    Step nextEnergyCalculationStep_;
-    //! The next energy calculation step
-    Step nextVirialCalculationStep_;
-    //! The next free energy calculation step
-    Step nextFreeEnergyCalculationStep_;
-
-    //! Pointer to the micro state
-    StatePropagatorData* statePropagatorData_;
-    //! Pointer to the energy element
-    EnergyElement* energyElement_;
-    //! The free energy perturbation element
-    FreeEnergyPerturbationElement* freeEnergyPerturbationElement_;
-
-    //! The local topology - updated by Topology via Client system
-    const gmx_localtop_t* localTopology_;
-
-    //! Whether we're having a dynamic box
-    const bool isDynamicBox_;
-    //! Whether we're being verbose
-    const bool isVerbose_;
-    //! The number of shell relaxation steps we did
-    Step nSteps_;
-
-    //! DD / DLB helper object
-    const DDBalanceRegionHandler ddBalanceRegionHandler_;
-
-    /* \brief The FEP lambda vector
-     *
-     * Used if FEP is off, since do_force
-     * requires lambda to be allocated anyway
-     */
-    std::array<real, efptNR> lambda_;
-
-    // Access to ISimulator data
-    //! Handles logging.
-    FILE* fplog_;
-    //! Handles communication.
-    const t_commrec* cr_;
-    //! Contains user input mdp options.
-    const t_inputrec* inputrec_;
-    //! Atom parameters for this domain.
-    const MDAtoms* mdAtoms_;
-    //! Manages flop accounting.
-    t_nrnb* nrnb_;
-    //! Manages wall cycle accounting.
-    gmx_wallcycle* wcycle_;
-    //! Parameters for force calculations.
-    t_forcerec* fr_;
-    //! Handles virtual sites.
-    gmx_vsite_t* vsite_;
-    //! The Interactive Molecular Dynamics session.
-    ImdSession* imdSession_;
-    //! The pull work object.
-    pull_t* pull_work_;
-    //! Helper struct for force calculations.
-    t_fcdata* fcd_;
-    //! Schedule of work for each MD step for this task.
-    MdrunScheduleWorkload* runScheduleWork_;
-    //! Handles constraints.
-    Constraints* constr_;
-    //! Handles enforced rotation.
-    gmx_enfrot* enforcedRotation_;
-};
-
-} // namespace gmx
-
-#endif // GMX_MODULARSIMULATOR_SHELLFCELEMENT_H
index 8e1a1a0575e5752b9d038b6e036f85bf72bbd2e5..b8804d469b13d49a660cf184bc29ea19b45ddbfd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 namespace gmx
 {
 //! Helper function to call all callbacks in a list
-static inline void runAllCallbacks(std::vector<SignallerCallbackPtr>& callbacks, Step step, Time time)
+static inline void runAllCallbacks(const std::vector<SignallerCallback>& callbacks, Step step, Time time)
 {
     for (const auto& callback : callbacks)
     {
-        (*callback)(step, time);
+        callback(step, time);
     }
 }
 
-NeighborSearchSignaller::NeighborSearchSignaller(std::vector<SignallerCallbackPtr> callbacks,
-                                                 Step                              nstlist,
-                                                 Step                              initStep,
-                                                 Time                              initTime) :
+NeighborSearchSignaller::NeighborSearchSignaller(std::vector<SignallerCallback> callbacks,
+                                                 Step                           nstlist,
+                                                 Step                           initStep,
+                                                 Time                           initTime) :
     callbacks_(std::move(callbacks)),
     nstlist_(nstlist),
     initStep_(initStep),
@@ -80,10 +80,10 @@ void NeighborSearchSignaller::signal(Step step, Time time)
     }
 }
 
-LastStepSignaller::LastStepSignaller(std::vector<SignallerCallbackPtr> callbacks,
-                                     gmx::Step                         nsteps,
-                                     gmx::Step                         initStep,
-                                     StopHandler*                      stopHandler) :
+LastStepSignaller::LastStepSignaller(std::vector<SignallerCallback> callbacks,
+                                     gmx::Step                      nsteps,
+                                     gmx::Step                      initStep,
+                                     StopHandler*                   stopHandler) :
     callbacks_(std::move(callbacks)),
     stopStep_(initStep + nsteps),
     signalledStopCondition_(false),
@@ -107,23 +107,22 @@ void LastStepSignaller::signal(Step step, Time time)
     }
 }
 
-void LastStepSignaller::signallerSetup()
+void LastStepSignaller::setup()
 {
     GMX_ASSERT(nsStepRegistrationDone_,
                "LastStepSignaller needs to be registered to NeighborSearchSignaller.");
 }
 
-SignallerCallbackPtr LastStepSignaller::registerNSCallback()
+std::optional<SignallerCallback> LastStepSignaller::registerNSCallback()
 {
     nsStepRegistrationDone_ = true;
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { this->nextNSStep_ = step; });
+    return [this](Step step, Time gmx_unused time) { this->nextNSStep_ = step; };
 }
 
-LoggingSignaller::LoggingSignaller(std::vector<SignallerCallbackPtr> callbacks,
-                                   Step                              nstlog,
-                                   Step                              initStep,
-                                   Time                              initTime) :
+LoggingSignaller::LoggingSignaller(std::vector<SignallerCallback> callbacks,
+                                   Step                           nstlog,
+                                   Step                           initStep,
+                                   Time                           initTime) :
     callbacks_(std::move(callbacks)),
     nstlog_(nstlog),
     initStep_(initStep),
@@ -141,25 +140,85 @@ void LoggingSignaller::signal(Step step, Time time)
     }
 }
 
-void LoggingSignaller::signallerSetup()
+void LoggingSignaller::setup()
 {
     GMX_ASSERT(lastStepRegistrationDone_,
                "LoggingSignaller needs to be registered to LastStepSignaller.");
 }
 
-SignallerCallbackPtr LoggingSignaller::registerLastStepCallback()
+std::optional<SignallerCallback> LoggingSignaller::registerLastStepCallback()
 {
     lastStepRegistrationDone_ = true;
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { this->lastStep_ = step; });
+    return [this](Step step, Time gmx_unused time) { this->lastStep_ = step; };
 }
 
-EnergySignaller::EnergySignaller(std::vector<SignallerCallbackPtr> calculateEnergyCallbacks,
-                                 std::vector<SignallerCallbackPtr> calculateVirialCallbacks,
-                                 std::vector<SignallerCallbackPtr> calculateFreeEnergyCallbacks,
-                                 int                               nstcalcenergy,
-                                 int                               nstcalcfreeenergy,
-                                 int                               nstcalcvirial) :
+TrajectorySignaller::TrajectorySignaller(std::vector<SignallerCallback> signalEnergyCallbacks,
+                                         std::vector<SignallerCallback> signalStateCallbacks,
+                                         int                            nstxout,
+                                         int                            nstvout,
+                                         int                            nstfout,
+                                         int                            nstxoutCompressed,
+                                         int                            tngBoxOut,
+                                         int                            tngLambdaOut,
+                                         int                            tngBoxOutCompressed,
+                                         int                            tngLambdaOutCompressed,
+                                         int                            nstenergy) :
+    nstxout_(nstxout),
+    nstvout_(nstvout),
+    nstfout_(nstfout),
+    nstxoutCompressed_(nstxoutCompressed),
+    tngBoxOut_(tngBoxOut),
+    tngLambdaOut_(tngLambdaOut),
+    tngBoxOutCompressed_(tngBoxOutCompressed),
+    tngLambdaOutCompressed_(tngLambdaOutCompressed),
+    nstenergy_(nstenergy),
+    signalEnergyCallbacks_(std::move(signalEnergyCallbacks)),
+    signalStateCallbacks_(std::move(signalStateCallbacks)),
+    lastStep_(-1),
+    lastStepRegistrationDone_(false)
+{
+}
+
+void TrajectorySignaller::setup()
+{
+    GMX_ASSERT(lastStepRegistrationDone_,
+               "TrajectoryElement needs to be registered to LastStepSignaller.");
+}
+
+void TrajectorySignaller::signal(Step step, Time time)
+{
+    if (do_per_step(step, nstxout_) || do_per_step(step, nstvout_) || do_per_step(step, nstfout_)
+        || do_per_step(step, nstxoutCompressed_) || do_per_step(step, tngBoxOut_)
+        || do_per_step(step, tngLambdaOut_) || do_per_step(step, tngBoxOutCompressed_)
+        || do_per_step(step, tngLambdaOutCompressed_))
+    {
+        for (const auto& callback : signalStateCallbacks_)
+        {
+            callback(step, time);
+        }
+    }
+
+    if (do_per_step(step, nstenergy_) || step == lastStep_)
+    {
+        for (const auto& callback : signalEnergyCallbacks_)
+        {
+            callback(step, time);
+        }
+    }
+}
+
+std::optional<SignallerCallback> TrajectorySignaller::registerLastStepCallback()
+{
+    lastStepRegistrationDone_ = true;
+    return [this](Step step, Time gmx_unused time) { this->lastStep_ = step; };
+}
+
+EnergySignaller::EnergySignaller(std::vector<SignallerCallback> calculateEnergyCallbacks,
+                                 std::vector<SignallerCallback> calculateVirialCallbacks,
+                                 std::vector<SignallerCallback> calculateFreeEnergyCallbacks,
+                                 int                            nstcalcenergy,
+                                 int                            nstcalcfreeenergy,
+                                 int                            nstcalcvirial) :
     calculateEnergyCallbacks_(std::move(calculateEnergyCallbacks)),
     calculateVirialCallbacks_(std::move(calculateVirialCallbacks)),
     calculateFreeEnergyCallbacks_(std::move(calculateFreeEnergyCallbacks)),
@@ -194,7 +253,7 @@ void EnergySignaller::signal(Step step, Time time)
     }
 }
 
-void EnergySignaller::signallerSetup()
+void EnergySignaller::setup()
 {
     GMX_ASSERT(loggingRegistrationDone_,
                "EnergySignaller needs to be registered to LoggingSignaller.");
@@ -202,22 +261,20 @@ void EnergySignaller::signallerSetup()
                "EnergySignaller needs to be registered to TrajectoryElement.");
 }
 
-SignallerCallbackPtr EnergySignaller::registerTrajectorySignallerCallback(TrajectoryEvent event)
+std::optional<SignallerCallback> EnergySignaller::registerTrajectorySignallerCallback(TrajectoryEvent event)
 {
     if (event == TrajectoryEvent::EnergyWritingStep)
     {
         trajectoryRegistrationDone_ = true;
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time gmx_unused time) { this->energyWritingStep_ = step; });
+        return [this](Step step, Time gmx_unused time) { this->energyWritingStep_ = step; };
     }
-    return nullptr;
+    return std::nullopt;
 }
 
-SignallerCallbackPtr EnergySignaller::registerLoggingCallback()
+std::optional<SignallerCallback> EnergySignaller::registerLoggingCallback()
 {
     loggingRegistrationDone_ = true;
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { this->loggingStep_ = step; });
+    return [this](Step step, Time gmx_unused time) { this->loggingStep_ = step; };
 }
 
 } // namespace gmx
index 4fd9fd549f5303e80a3956eab61f47aeb6c56ca0..2c74ab9454d1b92a3f8bf69fef552a2b70acd5a4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the signallers for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_SIGNALLERS_H
@@ -53,21 +55,21 @@ namespace gmx
 class StopHandler;
 class TrajectoryElement;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Builder for signallers
  *
  * This builder allows clients to register, and then builds the signaller
  * passing on the list of clients.
  *
- * @tparam Signaller  The signaller to be built
+ * \tparam Signaller  The signaller to be built
  */
 template<typename Signaller>
 class SignallerBuilder final
 {
 public:
     //! Allows clients to register to the signaller
-    void registerSignallerClient(compat::not_null<typename Signaller::Client*> client);
+    void registerSignallerClient(typename Signaller::Client* client);
 
     //! Build the signaller
     template<typename... Args>
@@ -76,10 +78,12 @@ public:
 private:
     //! List of signaller clients
     std::vector<typename Signaller::Client*> signallerClients_;
+    //! The state of the builder
+    ModularSimulatorBuilderState state_ = ModularSimulatorBuilderState::AcceptingClientRegistrations;
 
     //! Helper function to get the callbacks from the clients
     template<typename... Args>
-    std::vector<SignallerCallbackPtr> buildCallbackVector(Args&&... args);
+    std::vector<SignallerCallback> buildCallbackVector(Args&&... args);
 
     /*! \brief Get a callback from a single client
      *
@@ -87,10 +91,11 @@ private:
      * specific signaller / client.
      */
     template<typename... Args>
-    SignallerCallbackPtr getSignallerCallback(typename Signaller::Client* client, Args&&... args);
+    std::optional<SignallerCallback> getSignallerCallback(typename Signaller::Client* client,
+                                                          Args&&... args);
 };
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Element signalling a neighbor search step
  *
@@ -104,13 +109,13 @@ public:
      *
      * Informs callbacks if step % nstlist_ == 0
      *
-     * @param step  The current time step
-     * @param time  The current time
+     * \param step  The current time step
+     * \param time  The current time
      */
     void signal(Step step, Time time) override;
 
     //! Do nothing at setup time
-    void signallerSetup() override{};
+    void setup() override{};
 
     //! Allow builder to construct
     friend class SignallerBuilder<NeighborSearchSignaller>;
@@ -120,15 +125,15 @@ public:
 private:
     /*! \brief Constructor
      *
-     * @param callbacks  A vector of pointers to callbacks
-     * @param nstlist    The frequency at which neighbor search is performed
-     * @param initStep   The first step of the simulation
-     * @param initTime   The start time of the simulation
+     * \param callbacks  A vector of pointers to callbacks
+     * \param nstlist    The frequency at which neighbor search is performed
+     * \param initStep   The first step of the simulation
+     * \param initTime   The start time of the simulation
      */
-    NeighborSearchSignaller(std::vector<SignallerCallbackPtr> callbacks, Step nstlist, Step initStep, Time initTime);
+    NeighborSearchSignaller(std::vector<SignallerCallback> callbacks, Step nstlist, Step initStep, Time initTime);
 
     //! Client callbacks
-    std::vector<SignallerCallbackPtr> callbacks_;
+    std::vector<SignallerCallback> callbacks_;
 
     //! The NS frequency
     const Step nstlist_;
@@ -138,7 +143,7 @@ private:
     const Time initTime_;
 };
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Element signalling the last step
  *
@@ -152,13 +157,13 @@ public:
      *
      * Informs callbacks if this is the last step
      *
-     * @param step  The current time step
-     * @param time  The current time
+     * \param step  The current time step
+     * \param time  The current time
      */
     void signal(Step step, Time time) override;
 
     //! Check that necessary registration was done
-    void signallerSetup() override;
+    void setup() override;
 
     //! Allow builder to construct
     friend class SignallerBuilder<LastStepSignaller>;
@@ -168,18 +173,15 @@ public:
 private:
     /*! \brief Constructor
      *
-     * @param callbacks    A vector of pointers to callbacks
-     * @param nsteps       The total number of steps for the simulation
-     * @param initStep     The first step of the simulation
-     * @param stopHandler  A pointer to the stop handler (LastStepSignaller takes ownership)
+     * \param callbacks    A vector of pointers to callbacks
+     * \param nsteps       The total number of steps for the simulation
+     * \param initStep     The first step of the simulation
+     * \param stopHandler  A pointer to the stop handler (LastStepSignaller takes ownership)
      */
-    LastStepSignaller(std::vector<SignallerCallbackPtr> callbacks,
-                      Step                              nsteps,
-                      Step                              initStep,
-                      StopHandler*                      stopHandler);
+    LastStepSignaller(std::vector<SignallerCallback> callbacks, Step nsteps, Step initStep, StopHandler* stopHandler);
 
     //! Client callbacks
-    std::vector<SignallerCallbackPtr> callbacks_;
+    std::vector<SignallerCallback> callbacks_;
 
     //! The last step of the simulation
     const Step stopStep_;
@@ -189,14 +191,14 @@ private:
     StopHandler* stopHandler_;
 
     //! INeighborSearchSignallerClient implementation
-    SignallerCallbackPtr registerNSCallback() override;
+    std::optional<SignallerCallback> registerNSCallback() override;
     //! The next NS step (notified by NS signaller)
     Step nextNSStep_;
     //! Whether we registered to the NS signaller
     bool nsStepRegistrationDone_;
 };
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Element signalling a logging step
  *
@@ -210,13 +212,13 @@ public:
      *
      * Informs callbacks if step % nstlog_ == 0
      *
-     * @param step  The current time step
-     * @param time  The current time
+     * \param step  The current time step
+     * \param time  The current time
      */
     void signal(Step step, Time time) override;
 
     //! Check that necessary registration was done
-    void signallerSetup() override;
+    void setup() override;
 
     //! Allow builder to construct
     friend class SignallerBuilder<LoggingSignaller>;
@@ -226,15 +228,15 @@ public:
 private:
     /*! \brief Constructor
      *
-     * @param callbacks  A vector of pointers to callbacks
-     * @param nstlog     The logging frequency
-     * @param initStep   The first step of the simulation
-     * @param initTime   The start time of the simulation
+     * \param callbacks  A vector of pointers to callbacks
+     * \param nstlog     The logging frequency
+     * \param initStep   The first step of the simulation
+     * \param initTime   The start time of the simulation
      */
-    LoggingSignaller(std::vector<SignallerCallbackPtr> callbacks, Step nstlog, Step initStep, Time initTime);
+    LoggingSignaller(std::vector<SignallerCallback> callbacks, Step nstlog, Step initStep, Time initTime);
 
     //! Client callbacks
-    std::vector<SignallerCallbackPtr> callbacks_;
+    std::vector<SignallerCallback> callbacks_;
 
     //! The logging frequency
     const Step nstlog_;
@@ -244,14 +246,90 @@ private:
     const Time initTime_;
 
     //! ILastStepSignallerClient implementation
-    SignallerCallbackPtr registerLastStepCallback() override;
+    std::optional<SignallerCallback> registerLastStepCallback() override;
     //! The last step (notified by signaller)
     Step lastStep_;
     //! Whether we registered to the last step signaller
     bool lastStepRegistrationDone_;
 };
 
-/*! \libinternal
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element signalling trajectory writing
+ *
+ * During signalling phase, it checks whether the current step is a writing
+ * step for either the energy or the state (position, velocity, forces)
+ * trajectory. It then notifies the signaller clients of the upcoming step.
+ *
+ * The TrajectorySignaller works in close collaboration with the TrajectoryElement
+ * which does the actual trajectory writing during the simulation step.
+ */
+class TrajectorySignaller final : public ISignaller, public ILastStepSignallerClient
+{
+public:
+    /*! \brief Prepare signaller
+     *
+     * Check that necessary registration was done
+     */
+    void setup() override;
+
+    /*! \brief Run the signaller at a specific step / time
+     *
+     * Informs clients when energy or state will be written.
+     *
+     * \param step           The current time step
+     * \param time           The current time
+     */
+    void signal(Step step, Time time) override;
+
+    //! Allow builder to construct
+    friend class SignallerBuilder<TrajectorySignaller>;
+    //! Define client type
+    typedef ITrajectorySignallerClient Client;
+
+private:
+    //! Constructor
+    TrajectorySignaller(std::vector<SignallerCallback> signalEnergyCallbacks,
+                        std::vector<SignallerCallback> signalStateCallbacks,
+                        int                            nstxout,
+                        int                            nstvout,
+                        int                            nstfout,
+                        int                            nstxoutCompressed,
+                        int                            tngBoxOut,
+                        int                            tngLambdaOut,
+                        int                            tngBoxOutCompressed,
+                        int                            tngLambdaOutCompressed,
+                        int                            nstenergy);
+
+    //! Output frequencies
+    //! {
+    const int nstxout_;
+    const int nstvout_;
+    const int nstfout_;
+    const int nstxoutCompressed_;
+    const int tngBoxOut_;
+    const int tngLambdaOut_;
+    const int tngBoxOutCompressed_;
+    const int tngLambdaOutCompressed_;
+    const int nstenergy_;
+    //! }
+
+    //! Callbacks to signal events
+    //! {
+    std::vector<SignallerCallback> signalEnergyCallbacks_;
+    std::vector<SignallerCallback> signalStateCallbacks_;
+    //! }
+
+    /*
+     * Last step client
+     */
+    Step lastStep_;
+    bool lastStepRegistrationDone_;
+    //! ILastStepSignallerClient implementation
+    std::optional<SignallerCallback> registerLastStepCallback() override;
+};
+
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Element signalling energy related special steps
  *
@@ -268,13 +346,13 @@ public:
      *
      * Informs callbacks of energy / virial / free energy special steps
      *
-     * @param step  The current time step
-     * @param time  The current time
+     * \param step  The current time step
+     * \param time  The current time
      */
     void signal(Step step, Time time) override;
 
     //! Check that necessary registration was done
-    void signallerSetup() override;
+    void setup() override;
 
     //! Allow builder to construct
     friend class SignallerBuilder<EnergySignaller>;
@@ -284,25 +362,25 @@ public:
 private:
     /*! \brief Constructor
      *
-     * @param calculateEnergyCallbacks      A vector of pointers to callbacks (energy steps)
-     * @param calculateVirialCallbacks      A vector of pointers to callbacks (virial steps)
-     * @param calculateFreeEnergyCallbacks  A vector of pointers to callbacks (free energy steps)
-     * @param nstcalcenergy                 The energy calculation frequency
-     * @param nstcalcfreeenergy             The free energy calculation frequency
-     * @param nstcalcvirial                 The free energy calculation frequency
+     * \param calculateEnergyCallbacks      A vector of pointers to callbacks (energy steps)
+     * \param calculateVirialCallbacks      A vector of pointers to callbacks (virial steps)
+     * \param calculateFreeEnergyCallbacks  A vector of pointers to callbacks (free energy steps)
+     * \param nstcalcenergy                 The energy calculation frequency
+     * \param nstcalcfreeenergy             The free energy calculation frequency
+     * \param nstcalcvirial                 The free energy calculation frequency
      */
-    EnergySignaller(std::vector<SignallerCallbackPtr> calculateEnergyCallbacks,
-                    std::vector<SignallerCallbackPtr> calculateVirialCallbacks,
-                    std::vector<SignallerCallbackPtr> calculateFreeEnergyCallbacks,
-                    int                               nstcalcenergy,
-                    int                               nstcalcfreeenergy,
-                    int                               nstcalcvirial);
+    EnergySignaller(std::vector<SignallerCallback> calculateEnergyCallbacks,
+                    std::vector<SignallerCallback> calculateVirialCallbacks,
+                    std::vector<SignallerCallback> calculateFreeEnergyCallbacks,
+                    int                            nstcalcenergy,
+                    int                            nstcalcfreeenergy,
+                    int                            nstcalcvirial);
 
     //! Client callbacks
     //! {
-    std::vector<SignallerCallbackPtr> calculateEnergyCallbacks_;
-    std::vector<SignallerCallbackPtr> calculateVirialCallbacks_;
-    std::vector<SignallerCallbackPtr> calculateFreeEnergyCallbacks_;
+    std::vector<SignallerCallback> calculateEnergyCallbacks_;
+    std::vector<SignallerCallback> calculateVirialCallbacks_;
+    std::vector<SignallerCallback> calculateFreeEnergyCallbacks_;
     //! }
 
     //! The energy calculation frequency
@@ -313,14 +391,14 @@ private:
     const int nstcalcvirial_;
 
     //! ITrajectorySignallerClient implementation
-    SignallerCallbackPtr registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
     //! The energy writing step (notified by signaller)
     Step energyWritingStep_;
     //! Whether we registered to the trajectory signaller
     bool trajectoryRegistrationDone_;
 
     //! ILoggingSignallerClient implementation
-    SignallerCallbackPtr registerLoggingCallback() override;
+    std::optional<SignallerCallback> registerLoggingCallback() override;
     //! The next logging step (notified by signaller)
     Step loggingStep_;
     //! Whether we registered to the logging signaller
@@ -329,9 +407,17 @@ private:
 
 //! Allows clients to register to the signaller
 template<class Signaller>
-void SignallerBuilder<Signaller>::registerSignallerClient(compat::not_null<typename Signaller::Client*> client)
+void SignallerBuilder<Signaller>::registerSignallerClient(typename Signaller::Client* client)
 {
-    signallerClients_.emplace_back(client);
+    if (client)
+    {
+        if (state_ == ModularSimulatorBuilderState::NotAcceptingClientRegistrations)
+        {
+            throw SimulationAlgorithmSetupError(
+                    "Tried to register to signaller after it was built.");
+        }
+        signallerClients_.emplace_back(client);
+    }
 }
 
 /*! \brief Build the signaller
@@ -342,11 +428,28 @@ template<class Signaller>
 template<typename... Args>
 std::unique_ptr<Signaller> SignallerBuilder<Signaller>::build(Args&&... args)
 {
+    state_         = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
     auto callbacks = buildCallbackVector();
     // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
     return std::unique_ptr<Signaller>(new Signaller(std::move(callbacks), std::forward<Args>(args)...));
 }
 
+/*! \brief Build the signaller
+ *
+ * Specialized version - TrajectorySignaller has a different build process
+ */
+template<>
+template<typename... Args>
+std::unique_ptr<TrajectorySignaller> SignallerBuilder<TrajectorySignaller>::build(Args&&... args)
+{
+    state_                     = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
+    auto signalEnergyCallbacks = buildCallbackVector(TrajectoryEvent::EnergyWritingStep);
+    auto signalStateCallbacks  = buildCallbackVector(TrajectoryEvent::StateWritingStep);
+    // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
+    return std::unique_ptr<TrajectorySignaller>(new TrajectorySignaller(
+            std::move(signalEnergyCallbacks), std::move(signalStateCallbacks), std::forward<Args>(args)...));
+}
+
 /*! \brief Build the signaller
  *
  * Specialized version - EnergySignaller has a significantly different build process
@@ -355,6 +458,7 @@ template<>
 template<typename... Args>
 std::unique_ptr<EnergySignaller> SignallerBuilder<EnergySignaller>::build(Args&&... args)
 {
+    state_                        = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
     auto calculateEnergyCallbacks = buildCallbackVector(EnergySignallerEvent::EnergyCalculationStep);
     auto calculateVirialCallbacks = buildCallbackVector(EnergySignallerEvent::VirialCalculationStep);
     auto calculateFreeEnergyCallbacks =
@@ -368,15 +472,15 @@ std::unique_ptr<EnergySignaller> SignallerBuilder<EnergySignaller>::build(Args&&
 //! Helper function to get the callbacks from the clients
 template<typename Signaller>
 template<typename... Args>
-std::vector<SignallerCallbackPtr> SignallerBuilder<Signaller>::buildCallbackVector(Args&&... args)
+std::vector<SignallerCallback> SignallerBuilder<Signaller>::buildCallbackVector(Args&&... args)
 {
-    std::vector<SignallerCallbackPtr> callbacks;
+    std::vector<SignallerCallback> callbacks;
     // Allow clients to register their callbacks
     for (auto& client : signallerClients_)
     {
         if (auto callback = getSignallerCallback(client, std::forward<Args>(args)...)) // don't register nullptr
         {
-            callbacks.emplace_back(std::move(callback));
+            callbacks.emplace_back(std::move(*callback));
         }
     }
     return callbacks;
@@ -385,7 +489,7 @@ std::vector<SignallerCallbackPtr> SignallerBuilder<Signaller>::buildCallbackVect
 //! Get a callback from a single client - NeighborSearchSignaller
 template<>
 template<typename... Args>
-SignallerCallbackPtr SignallerBuilder<NeighborSearchSignaller>::getSignallerCallback(
+std::optional<SignallerCallback> SignallerBuilder<NeighborSearchSignaller>::getSignallerCallback(
         typename NeighborSearchSignaller::Client* client,
         Args&&... args)
 {
@@ -395,7 +499,7 @@ SignallerCallbackPtr SignallerBuilder<NeighborSearchSignaller>::getSignallerCall
 //! Get a callback from a single client - LastStepSignaller
 template<>
 template<typename... Args>
-SignallerCallbackPtr
+std::optional<SignallerCallback>
 SignallerBuilder<LastStepSignaller>::getSignallerCallback(typename LastStepSignaller::Client* client,
                                                           Args&&... args)
 {
@@ -405,18 +509,29 @@ SignallerBuilder<LastStepSignaller>::getSignallerCallback(typename LastStepSigna
 //! Get a callback from a single client - LoggingSignaller
 template<>
 template<typename... Args>
-SignallerCallbackPtr
+std::optional<SignallerCallback>
 SignallerBuilder<LoggingSignaller>::getSignallerCallback(typename LoggingSignaller::Client* client,
                                                          Args&&... args)
 {
     return client->registerLoggingCallback(std::forward<Args>(args)...);
 }
 
+//! Get a callback from a single client - TrajectorySignaller
+template<>
+template<typename... Args>
+std::optional<SignallerCallback>
+SignallerBuilder<TrajectorySignaller>::getSignallerCallback(typename TrajectorySignaller::Client* client,
+                                                            Args&&... args)
+{
+    return client->registerTrajectorySignallerCallback(std::forward<Args>(args)...);
+}
+
 //! Get a callback from a single client - EnergySignaller
 template<>
 template<typename... Args>
-SignallerCallbackPtr SignallerBuilder<EnergySignaller>::getSignallerCallback(typename EnergySignaller::Client* client,
-                                                                             Args&&... args)
+std::optional<SignallerCallback>
+SignallerBuilder<EnergySignaller>::getSignallerCallback(typename EnergySignaller::Client* client,
+                                                        Args&&... args)
 {
     return client->registerEnergyCallback(std::forward<Args>(args)...);
 }
diff --git a/src/gromacs/modularsimulator/simulatoralgorithm.cpp b/src/gromacs/modularsimulator/simulatoralgorithm.cpp
new file mode 100644 (file)
index 0000000..aa18bd7
--- /dev/null
@@ -0,0 +1,720 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Defines the modular simulator algorithm
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ */
+
+#include "gmxpre.h"
+
+#include "simulatoralgorithm.h"
+
+#include "gromacs/commandline/filenm.h"
+#include "gromacs/domdec/domdec.h"
+#include "gromacs/ewald/pme.h"
+#include "gromacs/ewald/pme_load_balancing.h"
+#include "gromacs/ewald/pme_pp.h"
+#include "gromacs/gmxlib/nrnb.h"
+#include "gromacs/listed_forces/listed_forces.h"
+#include "gromacs/mdlib/checkpointhandler.h"
+#include "gromacs/mdlib/constr.h"
+#include "gromacs/mdlib/energyoutput.h"
+#include "gromacs/mdlib/md_support.h"
+#include "gromacs/mdlib/mdatoms.h"
+#include "gromacs/mdlib/resethandler.h"
+#include "gromacs/mdlib/stat.h"
+#include "gromacs/mdrun/replicaexchange.h"
+#include "gromacs/mdrun/shellfc.h"
+#include "gromacs/mdrunutility/handlerestart.h"
+#include "gromacs/mdrunutility/printtime.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/fcdata.h"
+#include "gromacs/mdtypes/forcerec.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/mdtypes/mdrunoptions.h"
+#include "gromacs/mdtypes/observableshistory.h"
+#include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/timing/walltime_accounting.h"
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/fatalerror.h"
+
+#include "checkpointhelper.h"
+#include "domdechelper.h"
+#include "energydata.h"
+#include "freeenergyperturbationdata.h"
+#include "modularsimulator.h"
+#include "parrinellorahmanbarostat.h"
+#include "pmeloadbalancehelper.h"
+#include "propagator.h"
+#include "statepropagatordata.h"
+#include "velocityscalingtemperaturecoupling.h"
+
+namespace gmx
+{
+ModularSimulatorAlgorithm::ModularSimulatorAlgorithm(std::string              topologyName,
+                                                     FILE*                    fplog,
+                                                     t_commrec*               cr,
+                                                     const MDLogger&          mdlog,
+                                                     const MdrunOptions&      mdrunOptions,
+                                                     t_inputrec*              inputrec,
+                                                     t_nrnb*                  nrnb,
+                                                     gmx_wallcycle*           wcycle,
+                                                     t_forcerec*              fr,
+                                                     gmx_walltime_accounting* walltime_accounting) :
+    taskIterator_(taskQueue_.end()),
+    statePropagatorData_(nullptr),
+    energyData_(nullptr),
+    freeEnergyPerturbationData_(nullptr),
+    step_(-1),
+    runFinished_(false),
+    topologyName_(std::move(topologyName)),
+    fplog(fplog),
+    cr(cr),
+    mdlog(mdlog),
+    mdrunOptions(mdrunOptions),
+    inputrec(inputrec),
+    nrnb(nrnb),
+    wcycle(wcycle),
+    fr(fr),
+    walltime_accounting(walltime_accounting)
+{
+    signalHelper_ = std::make_unique<SignalHelper>();
+}
+
+void ModularSimulatorAlgorithm::setup()
+{
+    simulatorSetup();
+    for (auto& signaller : signallerList_)
+    {
+        signaller->setup();
+    }
+    if (domDecHelper_)
+    {
+        domDecHelper_->setup();
+    }
+
+    for (auto& element : elementsOwnershipList_)
+    {
+        element->elementSetup();
+    }
+    statePropagatorData_->setup();
+    if (pmeLoadBalanceHelper_)
+    {
+        // State must have been initialized so pmeLoadBalanceHelper_ gets a valid box
+        pmeLoadBalanceHelper_->setup();
+    }
+}
+
+const SimulatorRunFunction* ModularSimulatorAlgorithm::getNextTask()
+{
+    if (!taskQueue_.empty())
+    {
+        taskIterator_++;
+    }
+    if (taskIterator_ == taskQueue_.end())
+    {
+        if (runFinished_)
+        {
+            return nullptr;
+        }
+        updateTaskQueue();
+        taskIterator_ = taskQueue_.begin();
+    }
+    return &*taskIterator_;
+}
+
+void ModularSimulatorAlgorithm::updateTaskQueue()
+{
+    // For now, we'll just clean the task queue and then re-populate
+    // TODO: If tasks are periodic around updates of the task queue,
+    //       we should reuse it instead
+    taskQueue_.clear();
+    populateTaskQueue();
+}
+
+void ModularSimulatorAlgorithm::teardown()
+{
+    for (auto& element : elementsOwnershipList_)
+    {
+        element->elementTeardown();
+    }
+    energyData_->teardown();
+    if (pmeLoadBalanceHelper_)
+    {
+        pmeLoadBalanceHelper_->teardown();
+    }
+    simulatorTeardown();
+}
+
+void ModularSimulatorAlgorithm::simulatorSetup()
+{
+    if (!mdrunOptions.writeConfout)
+    {
+        // This is on by default, and the main known use case for
+        // turning it off is for convenience in benchmarking, which is
+        // something that should not show up in the general user
+        // interface.
+        GMX_LOG(mdlog.info)
+                .asParagraph()
+                .appendText(
+                        "The -noconfout functionality is deprecated, and "
+                        "may be removed in a future version.");
+    }
+
+    if (MASTER(cr))
+    {
+        char        sbuf[STEPSTRSIZE], sbuf2[STEPSTRSIZE];
+        std::string timeString;
+        fprintf(stderr, "starting mdrun '%s'\n", topologyName_.c_str());
+        if (inputrec->nsteps >= 0)
+        {
+            timeString = formatString("%8.1f", static_cast<double>(inputrec->init_step + inputrec->nsteps)
+                                                       * inputrec->delta_t);
+        }
+        else
+        {
+            timeString = "infinite";
+        }
+        if (inputrec->init_step > 0)
+        {
+            fprintf(stderr, "%s steps, %s ps (continuing from step %s, %8.1f ps).\n",
+                    gmx_step_str(inputrec->init_step + inputrec->nsteps, sbuf), timeString.c_str(),
+                    gmx_step_str(inputrec->init_step, sbuf2), inputrec->init_step * inputrec->delta_t);
+        }
+        else
+        {
+            fprintf(stderr, "%s steps, %s ps.\n", gmx_step_str(inputrec->nsteps, sbuf),
+                    timeString.c_str());
+        }
+        fprintf(fplog, "\n");
+    }
+
+    walltime_accounting_start_time(walltime_accounting);
+    wallcycle_start(wcycle, ewcRUN);
+    print_start(fplog, cr, walltime_accounting, "mdrun");
+
+    step_ = inputrec->init_step;
+}
+
+void ModularSimulatorAlgorithm::simulatorTeardown()
+{
+
+    // Stop measuring walltime
+    walltime_accounting_end_time(walltime_accounting);
+
+    if (!thisRankHasDuty(cr, DUTY_PME))
+    {
+        /* Tell the PME only node to finish */
+        gmx_pme_send_finish(cr);
+    }
+
+    walltime_accounting_set_nsteps_done(walltime_accounting, step_ - inputrec->init_step);
+}
+
+void ModularSimulatorAlgorithm::preStep(Step step, Time gmx_unused time, bool isNeighborSearchingStep)
+{
+    if (stopHandler_->stoppingAfterCurrentStep(isNeighborSearchingStep) && step != signalHelper_->lastStep_)
+    {
+        /*
+         * Stop handler wants to stop after the current step, which was
+         * not known when building the current task queue. This happens
+         * e.g. when a stop is signalled by OS. We therefore want to purge
+         * the task queue now, and re-schedule this step as last step.
+         */
+        // clear task queue
+        taskQueue_.clear();
+        // rewind step
+        step_ = step;
+        return;
+    }
+
+    resetHandler_->setSignal(walltime_accounting);
+    // This is a hack to avoid having to rewrite StopHandler to be a NeighborSearchSignaller
+    // and accept the step as input. Eventually, we want to do that, but currently this would
+    // require introducing NeighborSearchSignaller in the legacy do_md or a lot of code
+    // duplication.
+    stophandlerIsNSStep_    = isNeighborSearchingStep;
+    stophandlerCurrentStep_ = step;
+    stopHandler_->setSignal();
+
+    wallcycle_start(wcycle, ewcSTEP);
+}
+
+void ModularSimulatorAlgorithm::postStep(Step step, Time gmx_unused time)
+{
+    // Output stuff
+    if (MASTER(cr))
+    {
+        if (do_per_step(step, inputrec->nstlog))
+        {
+            if (fflush(fplog) != 0)
+            {
+                gmx_fatal(FARGS, "Cannot flush logfile - maybe you are out of disk space?");
+            }
+        }
+    }
+    const bool do_verbose = mdrunOptions.verbose
+                            && (step % mdrunOptions.verboseStepPrintInterval == 0
+                                || step == inputrec->init_step || step == signalHelper_->lastStep_);
+    // Print the remaining wall clock time for the run
+    if (MASTER(cr) && (do_verbose || gmx_got_usr_signal())
+        && !(pmeLoadBalanceHelper_ && pmeLoadBalanceHelper_->pmePrinting()))
+    {
+        print_time(stderr, walltime_accounting, step, inputrec, cr);
+    }
+
+    double cycles = wallcycle_stop(wcycle, ewcSTEP);
+    if (DOMAINDECOMP(cr) && wcycle)
+    {
+        dd_cycles_add(cr->dd, static_cast<float>(cycles), ddCyclStep);
+    }
+
+    resetHandler_->resetCounters(
+            step, step - inputrec->init_step, mdlog, fplog, cr, fr->nbv.get(), nrnb, fr->pmedata,
+            pmeLoadBalanceHelper_ ? pmeLoadBalanceHelper_->loadBalancingObject() : nullptr, wcycle,
+            walltime_accounting);
+}
+
+void ModularSimulatorAlgorithm::populateTaskQueue()
+{
+    /*
+     * The registerRunFunction emplaces functions to the task queue.
+     * All elements are owned by the ModularSimulatorAlgorithm, as is the task queue.
+     * Elements can hence register lambdas capturing their `this` pointers without expecting
+     * life time issues, as the task queue and the elements are in the same scope.
+     */
+    auto registerRunFunction = [this](SimulatorRunFunction function) {
+        taskQueue_.emplace_back(std::move(function));
+    };
+
+    Time startTime = inputrec->init_t;
+    Time timeStep  = inputrec->delta_t;
+    Time time      = startTime + step_ * timeStep;
+
+    // Run an initial call to the signallers
+    for (auto& signaller : signallerList_)
+    {
+        signaller->signal(step_, time);
+    }
+
+    if (checkpointHelper_)
+    {
+        checkpointHelper_->run(step_, time);
+    }
+
+    if (pmeLoadBalanceHelper_)
+    {
+        pmeLoadBalanceHelper_->run(step_, time);
+    }
+    if (domDecHelper_)
+    {
+        domDecHelper_->run(step_, time);
+    }
+
+    do
+    {
+        // local variables for lambda capturing
+        const int  step     = step_;
+        const bool isNSStep = step == signalHelper_->nextNSStep_;
+
+        // register pre-step (task queue is local, so no problem with `this`)
+        registerRunFunction([this, step, time, isNSStep]() { preStep(step, time, isNSStep); });
+        // register elements for step
+        for (auto& element : elementCallList_)
+        {
+            element->scheduleTask(step_, time, registerRunFunction);
+        }
+        // register post-step (task queue is local, so no problem with `this`)
+        registerRunFunction([this, step, time]() { postStep(step, time); });
+
+        // prepare next step
+        step_++;
+        time = startTime + step_ * timeStep;
+        for (auto& signaller : signallerList_)
+        {
+            signaller->signal(step_, time);
+        }
+    } while (step_ != signalHelper_->nextNSStep_ && step_ <= signalHelper_->lastStep_);
+
+    runFinished_ = (step_ > signalHelper_->lastStep_);
+
+    if (runFinished_)
+    {
+        // task queue is local, so no problem with `this`
+        registerRunFunction([this]() { teardown(); });
+    }
+}
+
+ModularSimulatorAlgorithmBuilder::ModularSimulatorAlgorithmBuilder(
+        compat::not_null<LegacySimulatorData*>    legacySimulatorData,
+        std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder) :
+    legacySimulatorData_(legacySimulatorData),
+    signals_(std::make_unique<SimulationSignals>()),
+    elementAdditionHelper_(this),
+    globalCommunicationHelper_(computeGlobalCommunicationPeriod(legacySimulatorData->mdlog,
+                                                                legacySimulatorData->inputrec,
+                                                                legacySimulatorData->cr),
+                               signals_.get()),
+    checkpointHelperBuilder_(std::move(checkpointDataHolder),
+                             legacySimulatorData->startingBehavior,
+                             legacySimulatorData->cr)
+{
+    if (legacySimulatorData->inputrec->efep != efepNO)
+    {
+        freeEnergyPerturbationData_ = std::make_unique<FreeEnergyPerturbationData>(
+                legacySimulatorData->fplog, legacySimulatorData->inputrec, legacySimulatorData->mdAtoms);
+    }
+
+    statePropagatorData_ = std::make_unique<StatePropagatorData>(
+            legacySimulatorData->top_global->natoms, legacySimulatorData->fplog, legacySimulatorData->cr,
+            legacySimulatorData->state_global, legacySimulatorData->fr->nbv->useGpu(),
+            legacySimulatorData->fr->bMolPBC, legacySimulatorData->mdrunOptions.writeConfout,
+            opt2fn("-c", legacySimulatorData->nfile, legacySimulatorData->fnm), legacySimulatorData->inputrec,
+            legacySimulatorData->mdAtoms->mdatoms(), legacySimulatorData->top_global);
+
+    energyData_ = std::make_unique<EnergyData>(
+            statePropagatorData_.get(), freeEnergyPerturbationData_.get(),
+            legacySimulatorData->top_global, legacySimulatorData->inputrec, legacySimulatorData->mdAtoms,
+            legacySimulatorData->enerd, legacySimulatorData->ekind, legacySimulatorData->constr,
+            legacySimulatorData->fplog, legacySimulatorData->fr->fcdata.get(),
+            legacySimulatorData->mdModulesNotifier, MASTER(legacySimulatorData->cr),
+            legacySimulatorData->observablesHistory, legacySimulatorData->startingBehavior);
+}
+
+ModularSimulatorAlgorithm ModularSimulatorAlgorithmBuilder::build()
+{
+    if (algorithmHasBeenBuilt_)
+    {
+        throw SimulationAlgorithmSetupError(
+                "Tried to build ModularSimulationAlgorithm more than once.");
+    }
+    algorithmHasBeenBuilt_ = true;
+
+    // Connect propagators with thermostat / barostat
+    for (const auto& thermostatRegistration : thermostatRegistrationFunctions_)
+    {
+        for (const auto& connection : propagatorThermostatConnections_)
+        {
+            thermostatRegistration(connection);
+        }
+    }
+    for (const auto& barostatRegistration : barostatRegistrationFunctions_)
+    {
+        for (const auto& connection : propagatorBarostatConnections_)
+        {
+            barostatRegistration(connection);
+        }
+    }
+
+    ModularSimulatorAlgorithm algorithm(
+            *(legacySimulatorData_->top_global->name), legacySimulatorData_->fplog,
+            legacySimulatorData_->cr, legacySimulatorData_->mdlog, legacySimulatorData_->mdrunOptions,
+            legacySimulatorData_->inputrec, legacySimulatorData_->nrnb, legacySimulatorData_->wcycle,
+            legacySimulatorData_->fr, legacySimulatorData_->walltime_accounting);
+    registerWithInfrastructureAndSignallers(algorithm.signalHelper_.get());
+    algorithm.statePropagatorData_        = std::move(statePropagatorData_);
+    algorithm.energyData_                 = std::move(energyData_);
+    algorithm.freeEnergyPerturbationData_ = std::move(freeEnergyPerturbationData_);
+    algorithm.signals_                    = std::move(signals_);
+
+    // Multi sim is turned off
+    const bool simulationsShareState = false;
+
+    // Build stop handler
+    algorithm.stopHandler_ = legacySimulatorData_->stopHandlerBuilder->getStopHandlerMD(
+            compat::not_null<SimulationSignal*>(
+                    &(*globalCommunicationHelper_.simulationSignals())[eglsSTOPCOND]),
+            simulationsShareState, MASTER(legacySimulatorData_->cr),
+            legacySimulatorData_->inputrec->nstlist, legacySimulatorData_->mdrunOptions.reproducible,
+            globalCommunicationHelper_.nstglobalcomm(), legacySimulatorData_->mdrunOptions.maximumHoursToRun,
+            legacySimulatorData_->inputrec->nstlist == 0, legacySimulatorData_->fplog,
+            algorithm.stophandlerCurrentStep_, algorithm.stophandlerIsNSStep_,
+            legacySimulatorData_->walltime_accounting);
+
+    // Build reset handler
+    const bool simulationsShareResetCounters = false;
+    algorithm.resetHandler_                  = std::make_unique<ResetHandler>(
+            compat::make_not_null<SimulationSignal*>(
+                    &(*globalCommunicationHelper_.simulationSignals())[eglsRESETCOUNTERS]),
+            simulationsShareResetCounters, legacySimulatorData_->inputrec->nsteps,
+            MASTER(legacySimulatorData_->cr), legacySimulatorData_->mdrunOptions.timingOptions.resetHalfway,
+            legacySimulatorData_->mdrunOptions.maximumHoursToRun, legacySimulatorData_->mdlog,
+            legacySimulatorData_->wcycle, legacySimulatorData_->walltime_accounting);
+
+    // Build topology holder
+    algorithm.topologyHolder_ = topologyHolderBuilder_.build(
+            *legacySimulatorData_->top_global, legacySimulatorData_->cr,
+            legacySimulatorData_->inputrec, legacySimulatorData_->fr, legacySimulatorData_->mdAtoms,
+            legacySimulatorData_->constr, legacySimulatorData_->vsite);
+
+    // Build PME load balance helper
+    if (PmeLoadBalanceHelper::doPmeLoadBalancing(legacySimulatorData_->mdrunOptions,
+                                                 legacySimulatorData_->inputrec,
+                                                 legacySimulatorData_->fr))
+    {
+        algorithm.pmeLoadBalanceHelper_ = std::make_unique<PmeLoadBalanceHelper>(
+                legacySimulatorData_->mdrunOptions.verbose, algorithm.statePropagatorData_.get(),
+                legacySimulatorData_->fplog, legacySimulatorData_->cr, legacySimulatorData_->mdlog,
+                legacySimulatorData_->inputrec, legacySimulatorData_->wcycle, legacySimulatorData_->fr);
+        registerWithInfrastructureAndSignallers(algorithm.pmeLoadBalanceHelper_.get());
+    }
+    // Build domdec helper
+    if (DOMAINDECOMP(legacySimulatorData_->cr))
+    {
+        algorithm.domDecHelper_ = std::make_unique<DomDecHelper>(
+                legacySimulatorData_->mdrunOptions.verbose,
+                legacySimulatorData_->mdrunOptions.verboseStepPrintInterval,
+                algorithm.statePropagatorData_.get(), algorithm.freeEnergyPerturbationData_.get(),
+                algorithm.topologyHolder_.get(),
+                globalCommunicationHelper_.moveCheckBondedInteractionsCallback(),
+                globalCommunicationHelper_.nstglobalcomm(), legacySimulatorData_->fplog,
+                legacySimulatorData_->cr, legacySimulatorData_->mdlog, legacySimulatorData_->constr,
+                legacySimulatorData_->inputrec, legacySimulatorData_->mdAtoms, legacySimulatorData_->nrnb,
+                legacySimulatorData_->wcycle, legacySimulatorData_->fr, legacySimulatorData_->vsite,
+                legacySimulatorData_->imdSession, legacySimulatorData_->pull_work);
+        registerWithInfrastructureAndSignallers(algorithm.domDecHelper_.get());
+    }
+
+    // Build trajectory element
+    auto trajectoryElement = trajectoryElementBuilder_.build(
+            legacySimulatorData_->fplog, legacySimulatorData_->nfile, legacySimulatorData_->fnm,
+            legacySimulatorData_->mdrunOptions, legacySimulatorData_->cr,
+            legacySimulatorData_->outputProvider, legacySimulatorData_->mdModulesNotifier,
+            legacySimulatorData_->inputrec, legacySimulatorData_->top_global,
+            legacySimulatorData_->oenv, legacySimulatorData_->wcycle,
+            legacySimulatorData_->startingBehavior, simulationsShareState);
+    registerWithInfrastructureAndSignallers(trajectoryElement.get());
+
+    // Build free energy element
+    std::unique_ptr<FreeEnergyPerturbationData::Element> freeEnergyPerturbationElement = nullptr;
+    if (algorithm.freeEnergyPerturbationData_)
+    {
+        freeEnergyPerturbationElement = std::make_unique<FreeEnergyPerturbationData::Element>(
+                algorithm.freeEnergyPerturbationData_.get(),
+                legacySimulatorData_->inputrec->fepvals->delta_lambda);
+        registerWithInfrastructureAndSignallers(freeEnergyPerturbationElement.get());
+    }
+
+    // Build checkpoint helper (do this last so everyone else can be a checkpoint client!)
+    {
+        checkpointHelperBuilder_.setCheckpointHandler(std::make_unique<CheckpointHandler>(
+                compat::make_not_null<SimulationSignal*>(&(*algorithm.signals_)[eglsCHKPT]),
+                simulationsShareState, legacySimulatorData_->inputrec->nstlist == 0,
+                MASTER(legacySimulatorData_->cr), legacySimulatorData_->mdrunOptions.writeConfout,
+                legacySimulatorData_->mdrunOptions.checkpointOptions.period));
+        algorithm.checkpointHelper_ = checkpointHelperBuilder_.build(
+                legacySimulatorData_->inputrec->init_step, trajectoryElement.get(),
+                legacySimulatorData_->fplog, legacySimulatorData_->cr,
+                legacySimulatorData_->observablesHistory, legacySimulatorData_->walltime_accounting,
+                legacySimulatorData_->state_global, legacySimulatorData_->mdrunOptions.writeConfout);
+        registerWithInfrastructureAndSignallers(algorithm.checkpointHelper_.get());
+    }
+
+    // Build signallers
+    {
+        /* Signallers need to be called in an exact order. Some signallers are clients
+         * of other signallers, which requires the clients signallers to be called
+         * _after_ any signaller they are registered to - otherwise, they couldn't
+         * adapt their behavior to the information they got signalled.
+         *
+         * Signallers being clients of other signallers require registration.
+         * That registration happens during construction, which in turn means that
+         * we want to construct the signallers in the reverse order of their later
+         * call order.
+         *
+         * For the above reasons, the `addSignaller` lambda defined below emplaces
+         * added signallers at the beginning of the signaller list, which will yield
+         * a signaller list which is inverse to the build order (and hence equal to
+         * the intended call order).
+         */
+        auto addSignaller = [this, &algorithm](auto signaller) {
+            registerWithInfrastructureAndSignallers(signaller.get());
+            algorithm.signallerList_.emplace(algorithm.signallerList_.begin(), std::move(signaller));
+        };
+        const auto* inputrec = legacySimulatorData_->inputrec;
+        addSignaller(energySignallerBuilder_.build(
+                inputrec->nstcalcenergy, inputrec->fepvals->nstdhdl, inputrec->nstpcouple));
+        addSignaller(trajectorySignallerBuilder_.build(
+                inputrec->nstxout, inputrec->nstvout, inputrec->nstfout,
+                inputrec->nstxout_compressed, trajectoryElement->tngBoxOut(),
+                trajectoryElement->tngLambdaOut(), trajectoryElement->tngBoxOutCompressed(),
+                trajectoryElement->tngLambdaOutCompressed(), inputrec->nstenergy));
+        addSignaller(loggingSignallerBuilder_.build(inputrec->nstlog, inputrec->init_step, inputrec->init_t));
+        addSignaller(lastStepSignallerBuilder_.build(inputrec->nsteps, inputrec->init_step,
+                                                     algorithm.stopHandler_.get()));
+        addSignaller(neighborSearchSignallerBuilder_.build(inputrec->nstlist, inputrec->init_step,
+                                                           inputrec->init_t));
+    }
+
+    // Create element list
+    // Checkpoint helper needs to be in the call list (as first element!) to react to last step
+    algorithm.elementCallList_.emplace_back(algorithm.checkpointHelper_.get());
+    // Next, update the free energy lambda vector if needed
+    if (freeEnergyPerturbationElement)
+    {
+        algorithm.elementsOwnershipList_.emplace_back(std::move(freeEnergyPerturbationElement));
+        algorithm.elementCallList_.emplace_back(algorithm.elementsOwnershipList_.back().get());
+    }
+    // Then, move the built algorithm
+    algorithm.elementsOwnershipList_.insert(algorithm.elementsOwnershipList_.end(),
+                                            std::make_move_iterator(elements_.begin()),
+                                            std::make_move_iterator(elements_.end()));
+    algorithm.elementCallList_.insert(algorithm.elementCallList_.end(),
+                                      std::make_move_iterator(callList_.begin()),
+                                      std::make_move_iterator(callList_.end()));
+    // Finally, all trajectory writing is happening after the step
+    // (relevant data was stored by elements through energy signaller)
+    algorithm.elementsOwnershipList_.emplace_back(std::move(trajectoryElement));
+    algorithm.elementCallList_.emplace_back(algorithm.elementsOwnershipList_.back().get());
+
+    algorithm.setup();
+    return algorithm;
+}
+
+ISimulatorElement* ModularSimulatorAlgorithmBuilder::addElementToSimulatorAlgorithm(
+        std::unique_ptr<ISimulatorElement> element)
+{
+    elements_.emplace_back(std::move(element));
+    return elements_.back().get();
+}
+
+bool ModularSimulatorAlgorithmBuilder::elementExists(const ISimulatorElement* element) const
+{
+    // Check whether element exists in element list
+    if (std::any_of(elements_.begin(), elements_.end(),
+                    [element](auto& existingElement) { return element == existingElement.get(); }))
+    {
+        return true;
+    }
+    // Check whether element exists in other places controlled by *this
+    return (statePropagatorData_->element() == element || energyData_->element() == element
+            || (freeEnergyPerturbationData_ && freeEnergyPerturbationData_->element() == element));
+}
+
+void ModularSimulatorAlgorithmBuilder::addElementToSetupTeardownList(ISimulatorElement* element)
+{
+    // Add element if it's not already in the list
+    if (std::find(setupAndTeardownList_.begin(), setupAndTeardownList_.end(), element)
+        == setupAndTeardownList_.end())
+    {
+        setupAndTeardownList_.emplace_back(element);
+    }
+}
+
+std::optional<SignallerCallback> ModularSimulatorAlgorithm::SignalHelper::registerLastStepCallback()
+{
+    return [this](Step step, Time gmx_unused time) { this->lastStep_ = step; };
+}
+
+std::optional<SignallerCallback> ModularSimulatorAlgorithm::SignalHelper::registerNSCallback()
+{
+    return [this](Step step, Time gmx_unused time) { this->nextNSStep_ = step; };
+}
+
+GlobalCommunicationHelper::GlobalCommunicationHelper(int nstglobalcomm, SimulationSignals* simulationSignals) :
+    nstglobalcomm_(nstglobalcomm),
+    simulationSignals_(simulationSignals)
+{
+}
+
+int GlobalCommunicationHelper::nstglobalcomm() const
+{
+    return nstglobalcomm_;
+}
+
+SimulationSignals* GlobalCommunicationHelper::simulationSignals()
+{
+    return simulationSignals_;
+}
+
+void GlobalCommunicationHelper::setCheckBondedInteractionsCallback(CheckBondedInteractionsCallback callback)
+{
+    checkBondedInteractionsCallback_ = std::move(callback);
+}
+
+CheckBondedInteractionsCallback GlobalCommunicationHelper::moveCheckBondedInteractionsCallback()
+{
+    if (!checkBondedInteractionsCallback_)
+    {
+        throw SimulationAlgorithmSetupError(
+                "Requested CheckBondedInteractionsCallback before it was set.");
+    }
+    return *checkBondedInteractionsCallback_;
+}
+
+ModularSimulatorAlgorithmBuilderHelper::ModularSimulatorAlgorithmBuilderHelper(
+        ModularSimulatorAlgorithmBuilder* builder) :
+    builder_(builder)
+{
+}
+
+ISimulatorElement* ModularSimulatorAlgorithmBuilderHelper::storeElement(std::unique_ptr<ISimulatorElement> element)
+{
+    return builder_->addElementToSimulatorAlgorithm(std::move(element));
+}
+
+bool ModularSimulatorAlgorithmBuilderHelper::elementIsStored(const ISimulatorElement* element) const
+{
+    return builder_->elementExists(element);
+}
+
+void ModularSimulatorAlgorithmBuilderHelper::registerThermostat(
+        std::function<void(const PropagatorThermostatConnection&)> registrationFunction)
+{
+    builder_->thermostatRegistrationFunctions_.emplace_back(std::move(registrationFunction));
+}
+
+void ModularSimulatorAlgorithmBuilderHelper::registerBarostat(
+        std::function<void(const PropagatorBarostatConnection&)> registrationFunction)
+{
+    builder_->barostatRegistrationFunctions_.emplace_back(std::move(registrationFunction));
+}
+
+void ModularSimulatorAlgorithmBuilderHelper::registerWithThermostat(PropagatorThermostatConnection connectionData)
+{
+    builder_->propagatorThermostatConnections_.emplace_back(std::move(connectionData));
+}
+
+void ModularSimulatorAlgorithmBuilderHelper::registerWithBarostat(PropagatorBarostatConnection connectionData)
+{
+    builder_->propagatorBarostatConnections_.emplace_back(std::move(connectionData));
+}
+
+
+} // namespace gmx
diff --git a/src/gromacs/modularsimulator/simulatoralgorithm.h b/src/gromacs/modularsimulator/simulatoralgorithm.h
new file mode 100644 (file)
index 0000000..8073f00
--- /dev/null
@@ -0,0 +1,610 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Provides the modular simulator algorithm.
+ *
+ * Defines the ModularSimulatorAlgorithm class and its builder.
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module.
+ * Moving forward, the ModularSimulatorAlgorithmBuilder could be exposed to allow users to
+ * create custom algorithm - currently algorithms are only created an used by the ModularSimulator,
+ * meaning that this file is not exposed outside of the modular simulator module.
+ */
+#ifndef GROMACS_MODULARSIMULATOR_SIMULATORALGORITHM_H
+#define GROMACS_MODULARSIMULATOR_SIMULATORALGORITHM_H
+
+#include <string>
+
+#include "gromacs/mdrun/isimulator.h"
+#include "gromacs/mdtypes/state.h"
+
+#include "checkpointhelper.h"
+#include "domdechelper.h"
+#include "freeenergyperturbationdata.h"
+#include "modularsimulatorinterfaces.h"
+#include "pmeloadbalancehelper.h"
+#include "signallers.h"
+#include "topologyholder.h"
+#include "trajectoryelement.h"
+
+namespace gmx
+{
+enum class IntegrationStep;
+class EnergyData;
+class ModularSimulator;
+class ResetHandler;
+template<IntegrationStep algorithm>
+class Propagator;
+class TopologyHolder;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief The modular simulator
+ *
+ * Based on the input given, this simulator builds independent elements and
+ * signallers and stores them in a respective vector. The run function
+ * runs the simulation by, in turn, building a task list from the elements
+ * for a predefined number of steps, then running the task list, and repeating
+ * until the stop criterion is fulfilled.
+ *
+ * The simulator algorithm is owning all elements involved in the simulation
+ * and is hence controlling their lifetime. This ensures that pointers and
+ * callbacks exchanged between elements remain valid throughout the duration
+ * of the simulation run.
+ */
+class ModularSimulatorAlgorithm final
+{
+public:
+    //! Get next task in queue
+    [[nodiscard]] const SimulatorRunFunction* getNextTask();
+
+    // Only builder can construct
+    friend class ModularSimulatorAlgorithmBuilder;
+
+private:
+    //! Constructor
+    ModularSimulatorAlgorithm(std::string              topologyName,
+                              FILE*                    fplog,
+                              t_commrec*               cr,
+                              const MDLogger&          mdlog,
+                              const MdrunOptions&      mdrunOptions,
+                              t_inputrec*              inputrec,
+                              t_nrnb*                  nrnb,
+                              gmx_wallcycle*           wcycle,
+                              t_forcerec*              fr,
+                              gmx_walltime_accounting* walltime_accounting);
+
+    //! Calls setup for the simulator and all elements and signallers
+    void setup();
+    //! Calls teardown for the simulator and all elements
+    void teardown();
+    //! Re-populate task queue
+    void updateTaskQueue();
+
+    /*! \brief A function called once before the simulation run
+     *
+     * This function collects calls which need to be made once at the
+     * beginning of the simulation run. These are mainly printing information
+     * to the user and starting the wall time.
+     */
+    void simulatorSetup();
+
+    /*! \brief A function called once after the simulation run
+     *
+     * This function collects calls which need to be made once at the
+     * end of the simulation run.
+     */
+    void simulatorTeardown();
+
+    /*! \brief A function called before every step
+     *
+     * This function collects calls which need to be made before every
+     * simulation step. The need for this function could likely be
+     * eliminated by rewriting the stop and reset handler to fit the
+     * modular simulator approach.
+     */
+    void preStep(Step step, Time time, bool isNeighborSearchingStep);
+
+    /*! \brief A function called after every step
+     *
+     * This function collects calls which need to be made after every
+     * simulation step.
+     */
+    void postStep(Step step, Time time);
+
+    /*! \brief Add run functions to the task queue
+     *
+     * This function calls PME load balancing and domain decomposition first,
+     * and then queries the elements for all steps of the life time of the
+     * current neighbor list for their run functions. When the next step
+     * would be a neighbor-searching step, this function returns, such that
+     * the simulator can run the pre-computed task list before updating the
+     * neighbor list and re-filling the task list.
+     *
+     * TODO: In the current approach, unique pointers to tasks are created
+     *       repeatedly. While preliminary measurements do not seem to indicate
+     *       performance problems, a pool allocator would be an obvious
+     *       optimization possibility, as would reusing the task queue if
+     *       the task queue is periodic around the rebuild point (i.e. the
+     *       task queue is identical between rebuilds).
+     */
+    void populateTaskQueue();
+
+    //! The run queue
+    std::vector<SimulatorRunFunction> taskQueue_;
+    //! The task iterator
+    std::vector<SimulatorRunFunction>::const_iterator taskIterator_;
+
+    /* Note that the Simulator is owning the signallers and elements.
+     * The ownership list and the call list of the elements are kept
+     * separate, to allow to have elements more than once in the call
+     * lists - for example, using velocity verlet, the compute globals
+     * element needs to be scheduled more than once per step. For the
+     * signallers, no distinction between ownership and call list is
+     * made, all signallers are called exactly once per scheduling step.
+     *
+     * Objects being both a signaller and an element are not supported.
+     */
+    //! List of signalers (ownership and calling sequence)
+    std::vector<std::unique_ptr<ISignaller>> signallerList_;
+    //! List of schedulerElements (ownership)
+    std::vector<std::unique_ptr<ISimulatorElement>> elementsOwnershipList_;
+    //! List of schedulerElements (calling sequence)
+    std::vector<ISimulatorElement*> elementCallList_;
+
+    // Infrastructure elements
+    //! The domain decomposition element
+    std::unique_ptr<DomDecHelper> domDecHelper_;
+    //! The PME load balancing element
+    std::unique_ptr<PmeLoadBalanceHelper> pmeLoadBalanceHelper_;
+    //! The checkpoint helper
+    std::unique_ptr<CheckpointHelper> checkpointHelper_;
+    //! The stop handler
+    std::unique_ptr<StopHandler> stopHandler_;
+    //! The reset handler
+    std::unique_ptr<ResetHandler> resetHandler_;
+    //! Signal vector (used by stop / reset / checkpointing signaller)
+    std::unique_ptr<SimulationSignals> signals_;
+    //! The topology
+    std::unique_ptr<TopologyHolder> topologyHolder_;
+
+    // Data structures
+    //! The state propagator data
+    std::unique_ptr<StatePropagatorData> statePropagatorData_;
+    //! The energy data
+    std::unique_ptr<EnergyData> energyData_;
+    //! The free energy data
+    std::unique_ptr<FreeEnergyPerturbationData> freeEnergyPerturbationData_;
+
+    //! The current step
+    Step step_;
+    //! Whether the simulation run is finished
+    bool runFinished_;
+
+    /*! \internal
+     * \brief Signal helper
+     *
+     * The simulator needs to know about the last step and the
+     * neighbor searching step, which are determined in signallers.
+     * This object can be registered to the signals and accessed by
+     * the methods of the simulator.
+     */
+    class SignalHelper : public ILastStepSignallerClient, public INeighborSearchSignallerClient
+    {
+    public:
+        //! The last step
+        Step lastStep_ = std::numeric_limits<Step>::max();
+        //! The next NS step
+        Step nextNSStep_ = -1;
+        //! ILastStepSignallerClient implementation
+        std::optional<SignallerCallback> registerLastStepCallback() override;
+        //! INeighborSearchSignallerClient implementation
+        std::optional<SignallerCallback> registerNSCallback() override;
+    };
+    //! The signal helper object
+    std::unique_ptr<SignalHelper> signalHelper_;
+
+    // TODO: This is a hack for stop handler - needs to go once StopHandler
+    //       is adapted to the modular simulator
+    //! Whether this is a neighbor-searching step
+    bool stophandlerIsNSStep_ = false;
+    //! The current step
+    Step stophandlerCurrentStep_ = -1;
+
+    // ISimulator data - used for setup, teardown, pre step and post step
+    //! Name of the topology
+    std::string topologyName_;
+    //! Handles logging.
+    FILE* fplog;
+    //! Handles communication.
+    t_commrec* cr;
+    //! Handles logging.
+    const MDLogger& mdlog;
+    //! Contains command-line options to mdrun.
+    const MdrunOptions& mdrunOptions;
+    //! Contains user input mdp options.
+    t_inputrec* inputrec;
+    //! Manages flop accounting.
+    t_nrnb* nrnb;
+    //! Manages wall cycle accounting.
+    gmx_wallcycle* wcycle;
+    //! Parameters for force calculations.
+    t_forcerec* fr;
+    //! Manages wall time accounting.
+    gmx_walltime_accounting* walltime_accounting;
+};
+
+/*! \internal
+ * \brief Helper container with data connected to global communication
+ *
+ * This includes data that needs to be shared between elements involved in
+ * global communication. This will become obsolete as soon as global
+ * communication is moved to a client system (#3421).
+ */
+class GlobalCommunicationHelper
+{
+public:
+    //! Constructor
+    GlobalCommunicationHelper(int nstglobalcomm, SimulationSignals* simulationSignals);
+
+    //! Get the compute globals communication period
+    [[nodiscard]] int nstglobalcomm() const;
+    //! Get a pointer to the signals vector
+    [[nodiscard]] SimulationSignals* simulationSignals();
+
+    //! Set the callback to check the number of bonded interactions
+    void setCheckBondedInteractionsCallback(CheckBondedInteractionsCallback callback);
+    //! Move the callback to check the number of bonded interactions
+    [[nodiscard]] CheckBondedInteractionsCallback moveCheckBondedInteractionsCallback();
+
+private:
+    //! Compute globals communication period
+    const int nstglobalcomm_;
+    //! Signal vector (used by stop / reset / checkpointing signaller)
+    SimulationSignals* simulationSignals_;
+    //! Callback to check the number of bonded interactions
+    std::optional<CheckBondedInteractionsCallback> checkBondedInteractionsCallback_;
+};
+
+class ModularSimulatorAlgorithmBuilder;
+
+/*! \internal
+ * \brief Helper for element addition
+ *
+ * Such an object will be given to each invocation of getElementPointer
+ *
+ * Note: It would be nicer to define this as a member type of
+ * ModularSimulatorAlgorithmBuilder, but this would break forward declaration.
+ * This object is therefore defined as friend class.
+ */
+class ModularSimulatorAlgorithmBuilderHelper
+{
+public:
+    //! Constructor
+    ModularSimulatorAlgorithmBuilderHelper(ModularSimulatorAlgorithmBuilder* builder);
+    //! Store an element to the ModularSimulatorAlgorithmBuilder
+    ISimulatorElement* storeElement(std::unique_ptr<ISimulatorElement> element);
+    //! Check if an element is stored in the ModularSimulatorAlgorithmBuilder
+    bool elementIsStored(const ISimulatorElement* element) const;
+    //! Register a thermostat that accepts propagator registrations
+    void registerThermostat(std::function<void(const PropagatorThermostatConnection&)> registrationFunction);
+    //! Register a barostat that accepts propagator registrations
+    void registerBarostat(std::function<void(const PropagatorBarostatConnection&)> registrationFunction);
+    //! Register a propagator to the thermostat used
+    void registerWithThermostat(PropagatorThermostatConnection connectionData);
+    //! Register a propagator to the barostat used
+    void registerWithBarostat(PropagatorBarostatConnection connectionData);
+
+private:
+    //! Pointer to the associated ModularSimulatorAlgorithmBuilder
+    ModularSimulatorAlgorithmBuilder* builder_;
+};
+
+/*!\internal
+ * \brief Builder for ModularSimulatorAlgorithm objects
+ *
+ * This builds a ModularSimulatorAlgorithm.
+ *
+ * Users can add elements and define their call order by calling the templated
+ * add<Element> function. Note that only elements that have a static
+ * getElementPointerImpl factory method can be built in that way.
+ *
+ * Note that each ModularSimulatorAlgorithmBuilder can only be used to build
+ * one ModularSimulatorAlgorithm object, i.e. build() can only be called once.
+ * During the call to build, all elements and other infrastructure objects will
+ * be moved to the built ModularSimulatorAlgorithm object, such that further use
+ * of the builder would not make sense.
+ * Any access to the build or add<> methods after the first call to
+ * build() will result in an exception being thrown.
+ */
+class ModularSimulatorAlgorithmBuilder final
+{
+public:
+    //! Constructor
+    ModularSimulatorAlgorithmBuilder(compat::not_null<LegacySimulatorData*>    legacySimulatorData,
+                                     std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder);
+    //! Build algorithm
+    ModularSimulatorAlgorithm build();
+
+    /*! \brief  Add element to the modular simulator algorithm builder
+     *
+     * This function has a general implementation, which will call the getElementPointer(...)
+     * factory function.
+     *
+     * \tparam Element  The element type
+     * \tparam Args     A variable number of argument types
+     * \param args      A variable number of arguments
+     */
+    template<typename Element, typename... Args>
+    void add(Args&&... args);
+
+    //! Allow access from helper
+    friend class ModularSimulatorAlgorithmBuilderHelper;
+
+private:
+    //! The state of the builder
+    bool algorithmHasBeenBuilt_ = false;
+
+    // Data structures
+    //! The state propagator data
+    std::unique_ptr<StatePropagatorData> statePropagatorData_;
+    //! The energy data
+    std::unique_ptr<EnergyData> energyData_;
+    //! The free energy data
+    std::unique_ptr<FreeEnergyPerturbationData> freeEnergyPerturbationData_;
+
+    //! Pointer to the LegacySimulatorData object
+    compat::not_null<LegacySimulatorData*> legacySimulatorData_;
+
+    // Helper objects
+    //! Signal vector (used by stop / reset / checkpointing signaller)
+    std::unique_ptr<SimulationSignals> signals_;
+    //! Helper object passed to element factory functions
+    ModularSimulatorAlgorithmBuilderHelper elementAdditionHelper_;
+    //! Container for global computation data
+    GlobalCommunicationHelper globalCommunicationHelper_;
+
+    /*! \brief  Register an element to all applicable signallers and infrastructure elements
+     *
+     * \tparam Element  Type of the Element
+     * \param element   Pointer to the element
+     */
+    template<typename Element>
+    void registerWithInfrastructureAndSignallers(Element* element);
+
+    /*! \brief Take ownership of element
+     *
+     * This function returns a non-owning pointer to the new location of that
+     * element, allowing further usage (e.g. adding the element to the call list).
+     * Note that simply addin an element using this function will not call it
+     * during the simulation - it needs to be added to the call list separately.
+     * Note that generally, users will want to add elements to the call list, but
+     * it might not be practical to do this in the same order.
+     *
+     * \param element  A unique pointer to the element
+     * \return  A non-owning (raw) pointer to the element for further usage
+     */
+    ISimulatorElement* addElementToSimulatorAlgorithm(std::unique_ptr<ISimulatorElement> element);
+
+    /*! \brief Check if element is owned by *this
+     *
+     * \param element  Pointer to the element
+     * \return  Bool indicating whether element is owned by *this
+     */
+    [[nodiscard]] bool elementExists(const ISimulatorElement* element) const;
+
+    /*! \brief Add element to setupAndTeardownList_ if it's not already there
+     *
+     * \param element  Element pointer to be added
+     */
+    void addElementToSetupTeardownList(ISimulatorElement* element);
+
+    //! Vector to store elements, allowing the SimulatorAlgorithm to control their lifetime
+    std::vector<std::unique_ptr<ISimulatorElement>> elements_;
+    /*! \brief List defining in which order elements are called every step
+     *
+     * Elements may be referenced more than once if they should be called repeatedly
+     */
+    std::vector<ISimulatorElement*> callList_;
+    /*! \brief  List defining in which order elements are set up and torn down
+     *
+     * Elements should only appear once in this list
+     */
+    std::vector<ISimulatorElement*> setupAndTeardownList_;
+
+    //! Builder for the NeighborSearchSignaller
+    SignallerBuilder<NeighborSearchSignaller> neighborSearchSignallerBuilder_;
+    //! Builder for the LastStepSignaller
+    SignallerBuilder<LastStepSignaller> lastStepSignallerBuilder_;
+    //! Builder for the LoggingSignaller
+    SignallerBuilder<LoggingSignaller> loggingSignallerBuilder_;
+    //! Builder for the EnergySignaller
+    SignallerBuilder<EnergySignaller> energySignallerBuilder_;
+    //! Builder for the TrajectorySignaller
+    SignallerBuilder<TrajectorySignaller> trajectorySignallerBuilder_;
+    //! Builder for the TrajectoryElementBuilder
+    TrajectoryElementBuilder trajectoryElementBuilder_;
+    //! Builder for the TopologyHolder
+    TopologyHolder::Builder topologyHolderBuilder_;
+    //! Builder for the CheckpointHelper
+    CheckpointHelperBuilder checkpointHelperBuilder_;
+
+    /*! \brief List of clients for the CheckpointHelper
+     *
+     * \todo Replace this by proper builder (#3422)
+     */
+    std::vector<ICheckpointHelperClient*> checkpointClients_;
+
+    //! List of thermostat registration functions
+    std::vector<std::function<void(const PropagatorThermostatConnection&)>> thermostatRegistrationFunctions_;
+    //! List of barostat registration functions
+    std::vector<std::function<void(const PropagatorBarostatConnection&)>> barostatRegistrationFunctions_;
+    //! List of data to connect propagators to thermostats
+    std::vector<PropagatorThermostatConnection> propagatorThermostatConnections_;
+    //! List of data to connect propagators to barostats
+    std::vector<PropagatorBarostatConnection> propagatorBarostatConnections_;
+};
+
+/*! \internal
+ * \brief Factory function for elements that can be added via ModularSimulatorAlgorithmBuilder:
+ *        Get a pointer to an object of type \c Element to add to the call list
+ *
+ * This allows elements to be built via the templated ModularSimulatorAlgorithmBuilder::add<Element>
+ * method. Elements buildable throught this factor function are required to implement a static
+ * function with minimal signature
+ *
+ *     static ISimulatorElement* getElementPointerImpl(
+ *             LegacySimulatorData*                    legacySimulatorData,
+ *             ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+ *             StatePropagatorData*                    statePropagatorData,
+ *             EnergyData*                             energyData,
+ *             FreeEnergyPerturbationData*             freeEnergyPerturbationData,
+ *             GlobalCommunicationHelper*              globalCommunicationHelper)
+ *
+ * This function may also accept additional parameters which are passed using the variadic
+ * template parameter pack forwarded in getElementPointer.
+ *
+ * This function returns a pointer to an object of the Element type. Note that the caller will
+ * check whether the returned object has previously been stored using the `storeElement`
+ * function, and throw an exception if the element is not found.
+ * The function can check whether a previously stored pointer is valid using
+ * the `checkElementExistence` function. Most implementing functions will simply want
+ * to create an object, store it using `storeElement`, and then use the return value of
+ * `storeElement` as a return value to the caller. However, this setup allows the function
+ * to store a created element (using a static pointer inside the function) and return it
+ * in case that the factory function is called repeatedly. This allows to create an element
+ * once, but have it called multiple times during the simulation run.
+ *
+ * \see ModularSimulatorAlgorithmBuilder::add
+ *      Function using this functionality
+ * \see ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerlet>::getElementPointerImpl
+ *      Implementation using the single object / multiple call sites functionality
+ *
+ * \tparam Element The type of the element
+ * \tparam Args  Variable number of argument types allowing specific implementations to have
+ *               additional arguments
+ *
+ * \param legacySimulatorData  Pointer allowing access to simulator level data
+ * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+ * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+ * \param energyData  Pointer to the \c EnergyData object
+ * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+ * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+ * \param args  Variable number of additional parameters to be forwarded
+ *
+ * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+ */
+template<typename Element, typename... Args>
+ISimulatorElement* getElementPointer(LegacySimulatorData*                    legacySimulatorData,
+                                     ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                     StatePropagatorData*                    statePropagatorData,
+                                     EnergyData*                             energyData,
+                                     FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                     GlobalCommunicationHelper*  globalCommunicationHelper,
+                                     Args&&... args)
+{
+    return Element::getElementPointerImpl(legacySimulatorData, builderHelper, statePropagatorData,
+                                          energyData, freeEnergyPerturbationData,
+                                          globalCommunicationHelper, std::forward<Args>(args)...);
+}
+
+template<typename Element, typename... Args>
+void ModularSimulatorAlgorithmBuilder::add(Args&&... args)
+{
+    if (algorithmHasBeenBuilt_)
+    {
+        throw SimulationAlgorithmSetupError(
+                "Tried to add an element after ModularSimulationAlgorithm was built.");
+    }
+
+    // Get element from factory method
+    auto* element = static_cast<Element*>(getElementPointer<Element>(
+            legacySimulatorData_, &elementAdditionHelper_, statePropagatorData_.get(),
+            energyData_.get(), freeEnergyPerturbationData_.get(), &globalCommunicationHelper_,
+            std::forward<Args>(args)...));
+
+    // Make sure returned element pointer is owned by *this
+    // Ensuring this makes sure we can control the life time
+    if (!elementExists(element))
+    {
+        throw ElementNotFoundError("Tried to append non-existing element to call list.");
+    }
+    // Add to call list
+    callList_.emplace_back(element);
+    // Add to setup / teardown list if element hasn't been added yet
+    addElementToSetupTeardownList(element);
+    // Register element to all applicable signallers
+    registerWithInfrastructureAndSignallers(element);
+}
+
+//! Returns a pointer casted to type Base if the Element is derived from Base
+template<typename Base, typename Element>
+static std::enable_if_t<std::is_base_of<Base, Element>::value, Base*> castOrNull(Element* element)
+{
+    return static_cast<Base*>(element);
+}
+
+//! Returns a nullptr of type Base if Element is not derived from Base
+template<typename Base, typename Element>
+static std::enable_if_t<!std::is_base_of<Base, Element>::value, Base*> castOrNull(Element gmx_unused* element)
+{
+    return nullptr;
+}
+
+template<typename Element>
+void ModularSimulatorAlgorithmBuilder::registerWithInfrastructureAndSignallers(Element* element)
+{
+    // Register element to all applicable signallers
+    neighborSearchSignallerBuilder_.registerSignallerClient(
+            castOrNull<INeighborSearchSignallerClient, Element>(element));
+    lastStepSignallerBuilder_.registerSignallerClient(castOrNull<ILastStepSignallerClient, Element>(element));
+    loggingSignallerBuilder_.registerSignallerClient(castOrNull<ILoggingSignallerClient, Element>(element));
+    energySignallerBuilder_.registerSignallerClient(castOrNull<IEnergySignallerClient, Element>(element));
+    trajectorySignallerBuilder_.registerSignallerClient(
+            castOrNull<ITrajectorySignallerClient, Element>(element));
+    // Register element to trajectory element (if applicable)
+    trajectoryElementBuilder_.registerWriterClient(castOrNull<ITrajectoryWriterClient, Element>(element));
+    // Register element to topology holder (if applicable)
+    topologyHolderBuilder_.registerClient(castOrNull<ITopologyHolderClient, Element>(element));
+    // Register element to checkpoint client (if applicable)
+    checkpointHelperBuilder_.registerClient(castOrNull<ICheckpointHelperClient, Element>(element));
+}
+
+} // namespace gmx
+
+#endif // GROMACS_MODULARSIMULATOR_SIMULATORALGORITHM_H
index 17b8eb47cb30ccf1cdafda2a07c38fa3ea9c9097..bf5bf8e10f9a7e7937b1340c968847582778f00a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "statepropagatordata.h"
 
+#include "gromacs/commandline/filenm.h"
 #include "gromacs/domdec/collect.h"
 #include "gromacs/domdec/domdec.h"
 #include "gromacs/fileio/confio.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
+#include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/mdoutf.h"
 #include "gromacs/mdlib/stat.h"
 #include "gromacs/mdlib/update.h"
+#include "gromacs/mdtypes/checkpointdata.h"
 #include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/forcebuffers.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/mdtypes/mdrunoptions.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/atoms.h"
 #include "gromacs/topology/topology.h"
 
-#include "freeenergyperturbationelement.h"
+#include "freeenergyperturbationdata.h"
+#include "modularsimulator.h"
+#include "simulatoralgorithm.h"
 
 namespace gmx
 {
-StatePropagatorData::StatePropagatorData(int                            numAtoms,
-                                         FILE*                          fplog,
-                                         const t_commrec*               cr,
-                                         t_state*                       globalState,
-                                         int                            nstxout,
-                                         int                            nstvout,
-                                         int                            nstfout,
-                                         int                            nstxout_compressed,
-                                         bool                           useGPU,
-                                         FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                                         const TopologyHolder*          topologyHolder,
-                                         bool              canMoleculesBeDistributedOverPBC,
-                                         bool              writeFinalConfiguration,
-                                         std::string       finalConfigurationFilename,
-                                         const t_inputrec* inputrec,
-                                         const t_mdatoms*  mdatoms) :
+StatePropagatorData::StatePropagatorData(int                numAtoms,
+                                         FILE*              fplog,
+                                         const t_commrec*   cr,
+                                         t_state*           globalState,
+                                         bool               useGPU,
+                                         bool               canMoleculesBeDistributedOverPBC,
+                                         bool               writeFinalConfiguration,
+                                         const std::string& finalConfigurationFilename,
+                                         const t_inputrec*  inputrec,
+                                         const t_mdatoms*   mdatoms,
+                                         const gmx_mtop_t*  globalTop) :
     totalNumAtoms_(numAtoms),
-    nstxout_(nstxout),
-    nstvout_(nstvout),
-    nstfout_(nstfout),
-    nstxout_compressed_(nstxout_compressed),
     localNAtoms_(0),
+    box_{ { 0 } },
+    previousBox_{ { 0 } },
     ddpCount_(0),
-    writeOutStep_(-1),
+    element_(std::make_unique<Element>(this,
+                                       fplog,
+                                       cr,
+                                       inputrec->nstxout,
+                                       inputrec->nstvout,
+                                       inputrec->nstfout,
+                                       inputrec->nstxout_compressed,
+                                       canMoleculesBeDistributedOverPBC,
+                                       writeFinalConfiguration,
+                                       finalConfigurationFilename,
+                                       inputrec,
+                                       globalTop)),
     vvResetVelocities_(false),
-    freeEnergyPerturbationElement_(freeEnergyPerturbationElement),
     isRegularSimulationEnd_(false),
     lastStep_(-1),
-    canMoleculesBeDistributedOverPBC_(canMoleculesBeDistributedOverPBC),
-    systemHasPeriodicMolecules_(inputrec->bPeriodicMols),
-    pbcType_(inputrec->ePBC),
-    topologyHolder_(topologyHolder),
-    lastPlannedStep_(inputrec->nsteps + inputrec->init_step),
-    writeFinalConfiguration_(writeFinalConfiguration),
-    finalConfigurationFilename_(std::move(finalConfigurationFilename)),
-    fplog_(fplog),
-    cr_(cr),
     globalState_(globalState)
 {
-    // Initialize these here, as box_{{0}} in the initialization list
-    // is confusing uncrustify and doxygen
-    clear_mat(box_);
-    clear_mat(previousBox_);
-
     bool stateHasVelocities;
     // Local state only becomes valid now.
     if (DOMAINDECOMP(cr))
     {
         auto localState = std::make_unique<t_state>();
         dd_init_local_state(cr->dd, globalState, localState.get());
-        stateHasVelocities = ((static_cast<unsigned int>(localState->flags) & (1U << estV)) != 0u);
+        stateHasVelocities = ((static_cast<unsigned int>(localState->flags) & (1U << estV)) != 0U);
         setLocalState(std::move(localState));
     }
     else
     {
         state_change_natoms(globalState, globalState->natoms);
-        f_.resizeWithPadding(globalState->natoms);
+        f_.resize(globalState->natoms);
         localNAtoms_ = globalState->natoms;
         x_           = globalState->x;
         v_           = globalState->v;
         copy_mat(globalState->box, box_);
-        stateHasVelocities = ((static_cast<unsigned int>(globalState->flags) & (1U << estV)) != 0u);
+        stateHasVelocities = ((static_cast<unsigned int>(globalState->flags) & (1U << estV)) != 0U);
         previousX_.resizeWithPadding(localNAtoms_);
         ddpCount_ = globalState->ddp_count;
         copyPosition();
@@ -134,6 +131,14 @@ StatePropagatorData::StatePropagatorData(int                            numAtoms
         changePinningPolicy(&x_, gmx::PinningPolicy::PinnedIfSupported);
     }
 
+    if (DOMAINDECOMP(cr) && MASTER(cr))
+    {
+        xGlobal_.resizeWithPadding(totalNumAtoms_);
+        previousXGlobal_.resizeWithPadding(totalNumAtoms_);
+        vGlobal_.resizeWithPadding(totalNumAtoms_);
+        fGlobal_.resizeWithPadding(totalNumAtoms_);
+    }
+
     if (!inputrec->bContinuation)
     {
         if (stateHasVelocities)
@@ -165,6 +170,19 @@ StatePropagatorData::StatePropagatorData(int                            numAtoms
     }
 }
 
+StatePropagatorData::Element* StatePropagatorData::element()
+{
+    return element_.get();
+}
+
+void StatePropagatorData::setup()
+{
+    if (element_)
+    {
+        element_->elementSetup();
+    }
+}
+
 ArrayRefWithPadding<RVec> StatePropagatorData::positionsView()
 {
     return x_.arrayRefWithPadding();
@@ -195,14 +213,14 @@ ArrayRefWithPadding<const RVec> StatePropagatorData::constVelocitiesView() const
     return v_.constArrayRefWithPadding();
 }
 
-ArrayRefWithPadding<RVec> StatePropagatorData::forcesView()
+ForceBuffersView& StatePropagatorData::forcesView()
 {
-    return f_.arrayRefWithPadding();
+    return f_.view();
 }
 
-ArrayRefWithPadding<const RVec> StatePropagatorData::constForcesView() const
+const ForceBuffersView& StatePropagatorData::constForcesView() const
 {
-    return f_.constArrayRefWithPadding();
+    return f_.view();
 }
 
 rvec* StatePropagatorData::box()
@@ -210,7 +228,7 @@ rvec* StatePropagatorData::box()
     return box_;
 }
 
-const rvec* StatePropagatorData::constBox()
+const rvec* StatePropagatorData::constBox() const
 {
     return box_;
 }
@@ -220,16 +238,21 @@ rvec* StatePropagatorData::previousBox()
     return previousBox_;
 }
 
-const rvec* StatePropagatorData::constPreviousBox()
+const rvec* StatePropagatorData::constPreviousBox() const
 {
     return previousBox_;
 }
 
-int StatePropagatorData::localNumAtoms()
+int StatePropagatorData::localNumAtoms() const
 {
     return localNAtoms_;
 }
 
+int StatePropagatorData::totalNumAtoms() const
+{
+    return totalNumAtoms_;
+}
+
 std::unique_ptr<t_state> StatePropagatorData::localState()
 {
     auto state   = std::make_unique<t_state>();
@@ -238,7 +261,9 @@ std::unique_ptr<t_state> StatePropagatorData::localState()
     state->x = x_;
     state->v = v_;
     copy_mat(box_, state->box);
-    state->ddp_count = ddpCount_;
+    state->ddp_count       = ddpCount_;
+    state->ddp_count_cg_gl = ddpCountCgGl_;
+    state->cg_gl           = cgGl_;
     return state;
 }
 
@@ -252,7 +277,9 @@ void StatePropagatorData::setLocalState(std::unique_ptr<t_state> state)
     v_ = state->v;
     copy_mat(state->box, box_);
     copyPosition();
-    ddpCount_ = state->ddp_count;
+    ddpCount_     = state->ddp_count;
+    ddpCountCgGl_ = state->ddp_count_cg_gl;
+    cgGl_         = state->cg_gl;
 
     if (vvResetVelocities_)
     {
@@ -272,7 +299,7 @@ t_state* StatePropagatorData::globalState()
     return globalState_;
 }
 
-PaddedHostVector<RVec>* StatePropagatorData::forcePointer()
+ForceBuffers* StatePropagatorData::forcePointer()
 {
     return &f_;
 }
@@ -305,63 +332,64 @@ void StatePropagatorData::copyPosition(int start, int end)
     }
 }
 
-void StatePropagatorData::scheduleTask(Step step, Time gmx_unused time, const RegisterRunFunctionPtr& registerRunFunction)
+void StatePropagatorData::Element::scheduleTask(Step step,
+                                                Time gmx_unused            time,
+                                                const RegisterRunFunction& registerRunFunction)
 {
-    if (vvResetVelocities_)
+    if (statePropagatorData_->vvResetVelocities_)
     {
-        vvResetVelocities_ = false;
-        (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() { resetVelocities(); }));
+        statePropagatorData_->vvResetVelocities_ = false;
+        registerRunFunction([this]() { statePropagatorData_->resetVelocities(); });
     }
     // copy x -> previousX
-    (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() { copyPosition(); }));
+    registerRunFunction([this]() { statePropagatorData_->copyPosition(); });
     // if it's a write out step, keep a copy for writeout
     if (step == writeOutStep_ || (step == lastStep_ && writeFinalConfiguration_))
     {
-        (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() { saveState(); }));
+        registerRunFunction([this]() { saveState(); });
     }
 }
 
-void StatePropagatorData::saveState()
+void StatePropagatorData::Element::saveState()
 {
     GMX_ASSERT(!localStateBackup_, "Save state called again before previous state was written.");
-    localStateBackup_ = localState();
-    if (freeEnergyPerturbationElement_)
+    localStateBackup_ = statePropagatorData_->localState();
+    if (freeEnergyPerturbationData_)
     {
-        localStateBackup_->fep_state = freeEnergyPerturbationElement_->currentFEPState();
+        localStateBackup_->fep_state = freeEnergyPerturbationData_->currentFEPState();
         for (unsigned long i = 0; i < localStateBackup_->lambda.size(); ++i)
         {
-            localStateBackup_->lambda[i] = freeEnergyPerturbationElement_->constLambdaView()[i];
+            localStateBackup_->lambda[i] = freeEnergyPerturbationData_->constLambdaView()[i];
         }
         localStateBackup_->flags |= (1U << estLAMBDA) | (1U << estFEPSTATE);
     }
 }
 
-SignallerCallbackPtr StatePropagatorData::registerTrajectorySignallerCallback(TrajectoryEvent event)
+std::optional<SignallerCallback> StatePropagatorData::Element::registerTrajectorySignallerCallback(TrajectoryEvent event)
 {
     if (event == TrajectoryEvent::StateWritingStep)
     {
-        return std::make_unique<SignallerCallback>(
-                [this](Step step, Time /*unused*/) { this->writeOutStep_ = step; });
+        return [this](Step step, Time /*unused*/) { this->writeOutStep_ = step; };
     }
-    return nullptr;
+    return std::nullopt;
 }
 
-ITrajectoryWriterCallbackPtr StatePropagatorData::registerTrajectoryWriterCallback(TrajectoryEvent event)
+std::optional<ITrajectoryWriterCallback>
+StatePropagatorData::Element::registerTrajectoryWriterCallback(TrajectoryEvent event)
 {
     if (event == TrajectoryEvent::StateWritingStep)
     {
-        return std::make_unique<ITrajectoryWriterCallback>(
-                [this](gmx_mdoutf* outf, Step step, Time time, bool writeTrajectory, bool gmx_unused writeLog) {
-                    if (writeTrajectory)
-                    {
-                        write(outf, step, time);
-                    }
-                });
+        return [this](gmx_mdoutf* outf, Step step, Time time, bool writeTrajectory, bool gmx_unused writeLog) {
+            if (writeTrajectory)
+            {
+                write(outf, step, time);
+            }
+        };
     }
-    return nullptr;
+    return std::nullopt;
 }
 
-void StatePropagatorData::write(gmx_mdoutf_t outf, Step currentStep, Time currentTime)
+void StatePropagatorData::Element::write(gmx_mdoutf_t outf, Step currentStep, Time currentTime)
 {
     wallcycle_start(mdoutf_get_wcycle(outf), ewcTRAJ);
     unsigned int mdof_flags = 0;
@@ -408,9 +436,10 @@ void StatePropagatorData::write(gmx_mdoutf_t outf, Step currentStep, Time curren
     // TODO: This is only used for CPT - needs to be filled when we turn CPT back on
     ObservablesHistory* observablesHistory = nullptr;
 
-    mdoutf_write_to_trajectory_files(fplog_, cr_, outf, static_cast<int>(mdof_flags),
-                                     totalNumAtoms_, currentStep, currentTime,
-                                     localStateBackup_.get(), globalState_, observablesHistory, f_);
+    mdoutf_write_to_trajectory_files(
+            fplog_, cr_, outf, static_cast<int>(mdof_flags), statePropagatorData_->totalNumAtoms_,
+            currentStep, currentTime, localStateBackup_.get(), statePropagatorData_->globalState_,
+            observablesHistory, statePropagatorData_->f_.view().force(), &dummyCheckpointDataHolder_);
 
     if (currentStep != lastStep_ || !isRegularSimulationEnd_)
     {
@@ -419,14 +448,14 @@ void StatePropagatorData::write(gmx_mdoutf_t outf, Step currentStep, Time curren
     wallcycle_stop(mdoutf_get_wcycle(outf), ewcTRAJ);
 }
 
-void StatePropagatorData::elementSetup()
+void StatePropagatorData::Element::elementSetup()
 {
-    if (vvResetVelocities_)
+    if (statePropagatorData_->vvResetVelocities_)
     {
         // MD-VV does the first velocity half-step only to calculate the constraint virial,
         // then resets the velocities since the input is assumed to be positions and velocities
         // at full time step. TODO: Change this to have input at half time steps.
-        velocityBackup_ = v_;
+        statePropagatorData_->velocityBackup_ = statePropagatorData_->v_;
     }
 }
 
@@ -435,17 +464,112 @@ void StatePropagatorData::resetVelocities()
     v_ = velocityBackup_;
 }
 
-void StatePropagatorData::writeCheckpoint(t_state* localState, t_state gmx_unused* globalState)
+namespace
 {
-    state_change_natoms(localState, localNAtoms_);
-    localState->x = x_;
-    localState->v = v_;
-    copy_mat(box_, localState->box);
-    localState->ddp_count = ddpCount_;
-    localState->flags |= (1U << estX) | (1U << estV) | (1U << estBOX);
+/*!
+ * \brief Enum describing the contents StatePropagatorData::Element writes to modular checkpoint
+ *
+ * When changing the checkpoint content, add a new element just above Count, and adjust the
+ * checkpoint functionality.
+ */
+enum class CheckpointVersion
+{
+    Base, //!< First version of modular checkpointing
+    Count //!< Number of entries. Add new versions right above this!
+};
+constexpr auto c_currentVersion = CheckpointVersion(int(CheckpointVersion::Count) - 1);
+} // namespace
+
+template<CheckpointDataOperation operation>
+void StatePropagatorData::Element::doCheckpointData(CheckpointData<operation>* checkpointData,
+                                                    const t_commrec*           cr)
+{
+    ArrayRef<RVec> xGlobalRef;
+    ArrayRef<RVec> vGlobalRef;
+    if (DOMAINDECOMP(cr))
+    {
+        if (MASTER(cr))
+        {
+            xGlobalRef = statePropagatorData_->xGlobal_;
+            vGlobalRef = statePropagatorData_->vGlobal_;
+        }
+        if (operation == CheckpointDataOperation::Write)
+        {
+            dd_collect_vec(cr->dd, statePropagatorData_->ddpCount_, statePropagatorData_->ddpCountCgGl_,
+                           statePropagatorData_->cgGl_, statePropagatorData_->x_, xGlobalRef);
+            dd_collect_vec(cr->dd, statePropagatorData_->ddpCount_, statePropagatorData_->ddpCountCgGl_,
+                           statePropagatorData_->cgGl_, statePropagatorData_->v_, vGlobalRef);
+        }
+    }
+    else
+    {
+        xGlobalRef = statePropagatorData_->x_;
+        vGlobalRef = statePropagatorData_->v_;
+    }
+    if (MASTER(cr))
+    {
+        GMX_ASSERT(checkpointData, "Master needs a valid pointer to a CheckpointData object");
+        checkpointVersion(checkpointData, "StatePropagatorData version", c_currentVersion);
+
+        checkpointData->arrayRef("positions", makeCheckpointArrayRef<operation>(xGlobalRef));
+        checkpointData->arrayRef("velocities", makeCheckpointArrayRef<operation>(vGlobalRef));
+        checkpointData->tensor("box", statePropagatorData_->box_);
+        checkpointData->scalar("ddpCount", &statePropagatorData_->ddpCount_);
+        checkpointData->scalar("ddpCountCgGl", &statePropagatorData_->ddpCountCgGl_);
+        checkpointData->arrayRef("cgGl", makeCheckpointArrayRef<operation>(statePropagatorData_->cgGl_));
+    }
+}
+
+void StatePropagatorData::Element::saveCheckpointState(std::optional<WriteCheckpointData> checkpointData,
+                                                       const t_commrec*                   cr)
+{
+    doCheckpointData<CheckpointDataOperation::Write>(
+            checkpointData ? &checkpointData.value() : nullptr, cr);
+}
+
+/*!
+ * \brief Update the legacy global state
+ *
+ * When restoring from checkpoint, data will be distributed during domain decomposition at setup stage.
+ * Domain decomposition still uses the legacy global t_state object so make sure it's up-to-date.
+ */
+static void updateGlobalState(t_state*                      globalState,
+                              const PaddedHostVector<RVec>& x,
+                              const PaddedHostVector<RVec>& v,
+                              const tensor                  box,
+                              int                           ddpCount,
+                              int                           ddpCountCgGl,
+                              const std::vector<int>&       cgGl)
+{
+    globalState->x = x;
+    globalState->v = v;
+    copy_mat(box, globalState->box);
+    globalState->ddp_count       = ddpCount;
+    globalState->ddp_count_cg_gl = ddpCountCgGl;
+    globalState->cg_gl           = cgGl;
+}
+
+void StatePropagatorData::Element::restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData,
+                                                          const t_commrec*                  cr)
+{
+    doCheckpointData<CheckpointDataOperation::Read>(checkpointData ? &checkpointData.value() : nullptr, cr);
+
+    // Copy data to global state to be distributed by DD at setup stage
+    if (DOMAINDECOMP(cr) && MASTER(cr))
+    {
+        updateGlobalState(statePropagatorData_->globalState_, statePropagatorData_->xGlobal_,
+                          statePropagatorData_->vGlobal_, statePropagatorData_->box_,
+                          statePropagatorData_->ddpCount_, statePropagatorData_->ddpCountCgGl_,
+                          statePropagatorData_->cgGl_);
+    }
+}
+
+const std::string& StatePropagatorData::Element::clientID()
+{
+    return identifier_;
 }
 
-void StatePropagatorData::trajectoryWriterTeardown(gmx_mdoutf* gmx_unused outf)
+void StatePropagatorData::Element::trajectoryWriterTeardown(gmx_mdoutf* gmx_unused outf)
 {
     // Note that part of this code is duplicated in do_md_trajectory_writing.
     // This duplication is needed while both legacy and modular code paths are in use.
@@ -460,15 +584,19 @@ void StatePropagatorData::trajectoryWriterTeardown(gmx_mdoutf* gmx_unused outf)
     wallcycle_start(mdoutf_get_wcycle(outf), ewcTRAJ);
     if (DOMAINDECOMP(cr_))
     {
-        auto globalXRef = MASTER(cr_) ? globalState_->x : gmx::ArrayRef<gmx::RVec>();
-        dd_collect_vec(cr_->dd, localStateBackup_.get(), localStateBackup_->x, globalXRef);
-        auto globalVRef = MASTER(cr_) ? globalState_->v : gmx::ArrayRef<gmx::RVec>();
-        dd_collect_vec(cr_->dd, localStateBackup_.get(), localStateBackup_->v, globalVRef);
+        auto globalXRef =
+                MASTER(cr_) ? statePropagatorData_->globalState_->x : gmx::ArrayRef<gmx::RVec>();
+        dd_collect_vec(cr_->dd, localStateBackup_->ddp_count, localStateBackup_->ddp_count_cg_gl,
+                       localStateBackup_->cg_gl, localStateBackup_->x, globalXRef);
+        auto globalVRef =
+                MASTER(cr_) ? statePropagatorData_->globalState_->v : gmx::ArrayRef<gmx::RVec>();
+        dd_collect_vec(cr_->dd, localStateBackup_->ddp_count, localStateBackup_->ddp_count_cg_gl,
+                       localStateBackup_->cg_gl, localStateBackup_->v, globalVRef);
     }
     else
     {
         // We have the whole state locally: copy the local state pointer
-        globalState_ = localStateBackup_.get();
+        statePropagatorData_->globalState_ = localStateBackup_.get();
     }
 
     if (MASTER(cr_))
@@ -477,22 +605,72 @@ void StatePropagatorData::trajectoryWriterTeardown(gmx_mdoutf* gmx_unused outf)
         if (canMoleculesBeDistributedOverPBC_ && !systemHasPeriodicMolecules_)
         {
             // Make molecules whole only for confout writing
-            do_pbc_mtop(pbcType_, localStateBackup_->box, &topologyHolder_->globalTopology(),
-                        globalState_->x.rvec_array());
+            do_pbc_mtop(pbcType_, localStateBackup_->box, top_global_,
+                        statePropagatorData_->globalState_->x.rvec_array());
         }
-        write_sto_conf_mtop(finalConfigurationFilename_.c_str(), *topologyHolder_->globalTopology().name,
-                            &topologyHolder_->globalTopology(), globalState_->x.rvec_array(),
-                            globalState_->v.rvec_array(), pbcType_, localStateBackup_->box);
+        write_sto_conf_mtop(finalConfigurationFilename_.c_str(), *top_global_->name, top_global_,
+                            statePropagatorData_->globalState_->x.rvec_array(),
+                            statePropagatorData_->globalState_->v.rvec_array(), pbcType_,
+                            localStateBackup_->box);
     }
     wallcycle_stop(mdoutf_get_wcycle(outf), ewcTRAJ);
 }
 
-SignallerCallbackPtr StatePropagatorData::registerLastStepCallback()
+std::optional<SignallerCallback> StatePropagatorData::Element::registerLastStepCallback()
 {
-    return std::make_unique<SignallerCallback>([this](Step step, Time) {
+    return [this](Step step, Time /*time*/) {
         lastStep_               = step;
         isRegularSimulationEnd_ = (step == lastPlannedStep_);
-    });
+    };
+}
+
+StatePropagatorData::Element::Element(StatePropagatorData* statePropagatorData,
+                                      FILE*                fplog,
+                                      const t_commrec*     cr,
+                                      int                  nstxout,
+                                      int                  nstvout,
+                                      int                  nstfout,
+                                      int                  nstxout_compressed,
+                                      bool                 canMoleculesBeDistributedOverPBC,
+                                      bool                 writeFinalConfiguration,
+                                      std::string          finalConfigurationFilename,
+                                      const t_inputrec*    inputrec,
+                                      const gmx_mtop_t*    globalTop) :
+    statePropagatorData_(statePropagatorData),
+    nstxout_(nstxout),
+    nstvout_(nstvout),
+    nstfout_(nstfout),
+    nstxout_compressed_(nstxout_compressed),
+    writeOutStep_(-1),
+    freeEnergyPerturbationData_(nullptr),
+    isRegularSimulationEnd_(false),
+    lastStep_(-1),
+    canMoleculesBeDistributedOverPBC_(canMoleculesBeDistributedOverPBC),
+    systemHasPeriodicMolecules_(inputrec->bPeriodicMols),
+    pbcType_(inputrec->pbcType),
+    lastPlannedStep_(inputrec->nsteps + inputrec->init_step),
+    writeFinalConfiguration_(writeFinalConfiguration),
+    finalConfigurationFilename_(std::move(finalConfigurationFilename)),
+    fplog_(fplog),
+    cr_(cr),
+    top_global_(globalTop)
+{
+}
+void StatePropagatorData::Element::setFreeEnergyPerturbationData(FreeEnergyPerturbationData* freeEnergyPerturbationData)
+{
+    freeEnergyPerturbationData_ = freeEnergyPerturbationData;
+}
+
+ISimulatorElement* StatePropagatorData::Element::getElementPointerImpl(
+        LegacySimulatorData gmx_unused*        legacySimulatorData,
+        ModularSimulatorAlgorithmBuilderHelper gmx_unused* builderHelper,
+        StatePropagatorData*                               statePropagatorData,
+        EnergyData gmx_unused*      energyData,
+        FreeEnergyPerturbationData* freeEnergyPerturbationData,
+        GlobalCommunicationHelper gmx_unused* globalCommunicationHelper)
+{
+    statePropagatorData->element()->setFreeEnergyPerturbationData(freeEnergyPerturbationData);
+    return statePropagatorData->element();
 }
 
 } // namespace gmx
index d6a7fac8d385700b3e20373b05a8d8187b1e8a77..b3c1b050cac28d488b5d59dd2af5778968f20af7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the state for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_STATEPROPAGATORDATA_H
 #include "gromacs/gpu_utils/hostallocator.h"
 #include "gromacs/math/paddedvector.h"
 #include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/checkpointdata.h"
+#include "gromacs/mdtypes/forcebuffers.h"
+#include "gromacs/utility/keyvaluetree.h"
 
 #include "modularsimulatorinterfaces.h"
 #include "topologyholder.h"
 
 struct gmx_mdoutf;
+enum class PbcType : int;
 struct t_commrec;
 struct t_inputrec;
 class t_state;
@@ -57,10 +63,15 @@ struct t_mdatoms;
 
 namespace gmx
 {
+enum class CheckpointDataOperation;
 enum class ConstraintVariable;
-class FreeEnergyPerturbationElement;
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class ModularSimulatorAlgorithmBuilderHelper;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief StatePropagatorData and associated data
  *
@@ -68,7 +79,8 @@ class FreeEnergyPerturbationElement;
  * statistical-physical micro state, namely the positions,
  * velocities, forces, and box matrix, as well as a backup of
  * the positions and box of the last time step. While it takes
- * part in the simulator loop to be able to backup positions /
+ * part in the simulator loop via its member class `Element`
+ * to be able to backup positions /
  * boxes and save the current state if needed, it's main purpose
  * is to offer access to its data via getter methods. All elements
  * reading or writing to this data need a pointer to the
@@ -76,20 +88,6 @@ class FreeEnergyPerturbationElement;
  * will later simplify the understanding of data dependencies
  * between elements.
  *
- * The `StatePropagatorData` takes part in the simulator run, as it might
- * have to save a valid state at the right moment during the
- * integration. Placing the StatePropagatorData correctly is for now the
- * duty of the simulator builder - this might be automatized later
- * if we have enough meta-data of the variables (i.e., if
- * `StatePropagatorData` knows at which time the variables currently are,
- * and can decide when a valid state (full-time step of all
- * variables) is reached. The `StatePropagatorData` is also a client of
- * both the trajectory signaller and writer - it will save a
- * state for later writeout during the simulator step if it
- * knows that trajectory writing will occur later in the step,
- * and it knows how to write to file given a file pointer by
- * the `TrajectoryElement`.
- *
  * Note that the `StatePropagatorData` can be converted to and from the
  * legacy `t_state` object. This is useful when dealing with
  * functionality which has not yet been adapted to use the new
@@ -97,31 +95,21 @@ class FreeEnergyPerturbationElement;
  * domain decomposition, PME load balancing, and the initial
  * constraining are using this.
  */
-class StatePropagatorData final :
-    public ISimulatorElement,
-    public ITrajectoryWriterClient,
-    public ITrajectorySignallerClient,
-    public ICheckpointHelperClient,
-    public ILastStepSignallerClient
+class StatePropagatorData final
 {
 public:
     //! Constructor
-    StatePropagatorData(int                            numAtoms,
-                        FILE*                          fplog,
-                        const t_commrec*               cr,
-                        t_state*                       globalState,
-                        int                            nstxout,
-                        int                            nstvout,
-                        int                            nstfout,
-                        int                            nstxout_compressed,
-                        bool                           useGPU,
-                        FreeEnergyPerturbationElement* freeEnergyPerturbationElement,
-                        const TopologyHolder*          topologyHolder,
-                        bool                           canMoleculesBeDistributedOverPBC,
-                        bool                           writeFinalConfiguration,
-                        std::string                    finalConfigurationFilename,
-                        const t_inputrec*              inputrec,
-                        const t_mdatoms*               mdatoms);
+    StatePropagatorData(int                numAtoms,
+                        FILE*              fplog,
+                        const t_commrec*   cr,
+                        t_state*           globalState,
+                        bool               useGPU,
+                        bool               canMoleculesBeDistributedOverPBC,
+                        bool               writeFinalConfiguration,
+                        const std::string& finalConfigurationFilename,
+                        const t_inputrec*  inputrec,
+                        const t_mdatoms*   mdatoms,
+                        const gmx_mtop_t*  globalTop);
 
     // Allow access to state
     //! Get write access to position vector
@@ -137,67 +125,38 @@ public:
     //! Get read access to velocity vector
     ArrayRefWithPadding<const RVec> constVelocitiesView() const;
     //! Get write access to force vector
-    ArrayRefWithPadding<RVec> forcesView();
+    ForceBuffersView& forcesView();
     //! Get read access to force vector
-    ArrayRefWithPadding<const RVec> constForcesView() const;
+    const ForceBuffersView& constForcesView() const;
     //! Get pointer to box
     rvec* box();
     //! Get const pointer to box
-    const rvec* constBox();
+    const rvec* constBox() const;
     //! Get pointer to previous box
     rvec* previousBox();
     //! Get const pointer to previous box
-    const rvec* constPreviousBox();
+    const rvec* constPreviousBox() const;
     //! Get the local number of atoms
-    int localNumAtoms();
+    int localNumAtoms() const;
+    //! Get the total number of atoms
+    int totalNumAtoms() const;
 
-    /*! \brief Register run function for step / time
-     *
-     * This needs to be called during the integration part of the simulator,
-     * at the moment at which the state is at a full time step. Positioning
-     * this element is the responsibility of the programmer writing the
-     * integration algorithm! If the current step is a trajectory writing
-     * step, StatePropagatorData will save a backup for later writeout.
-     *
-     * This is also the place at which the current state becomes the previous
-     * state.
-     *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
-     */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
+    //! The element taking part in the simulator loop
+    class Element;
+    //! Get pointer to element (whose lifetime is managed by this)
+    Element* element();
+    //! Initial set up for the associated element
+    void setup();
 
-    /*! \brief Backup starting velocities
-     *
-     * This is only needed for vv, where the first (velocity) half step is only
-     * used to compute the constraint virial, but the velocities need to be reset
-     * after.
-     * TODO: There must be a more elegant solution to this!
-     */
-    void elementSetup() override;
-
-    //! No element teardown needed
-    void elementTeardown() override {}
-
-    //! @cond
+    //! \cond
     // (doxygen doesn't like these)
     // Classes which need access to legacy state
     friend class DomDecHelper;
-    //! @endcond
+    //! \endcond
 
 private:
     //! The total number of atoms in the system
     int totalNumAtoms_;
-    //! The position writeout frequency
-    int nstxout_;
-    //! The velocity writeout frequency
-    int nstvout_;
-    //! The force writeout frequency
-    int nstfout_;
-    //! The compressed position writeout frequency
-    int nstxout_compressed_;
-
     //! The local number of atoms
     int localNAtoms_;
     //! The position vector
@@ -207,13 +166,29 @@ private:
     //! The velocity vector
     PaddedHostVector<RVec> v_;
     //! The force vector
-    PaddedHostVector<RVec> f_;
+    ForceBuffers f_;
     //! The box matrix
     matrix box_;
     //! The box matrix of the previous step
     matrix previousBox_;
-    //! The DD partitioning count for legacy t_state compatibility
+    //! The DD partitioning count
     int ddpCount_;
+    //! The DD partitioning count for index_gl
+    int ddpCountCgGl_;
+    //! The global cg number of the local cgs
+    std::vector<int> cgGl_;
+
+    //! The global position vector
+    PaddedHostVector<RVec> xGlobal_;
+    //! The global position vector of the previous step
+    PaddedHostVector<RVec> previousXGlobal_;
+    //! The global velocity vector
+    PaddedHostVector<RVec> vGlobal_;
+    //! The global force vector
+    PaddedHostVector<RVec> fGlobal_;
+
+    //! The element
+    std::unique_ptr<Element> element_;
 
     //! Move x_ to previousX_
     void copyPosition();
@@ -228,7 +203,136 @@ private:
     //! Get a pointer to the global state
     t_state* globalState();
     //! Get a force pointer
-    PaddedHostVector<gmx::RVec>* forcePointer();
+    ForceBuffers* forcePointer();
+
+    //! Whether we're doing VV and need to reset velocities after the first half step
+    bool vvResetVelocities_;
+    //! Velocities backup for VV
+    PaddedHostVector<RVec> velocityBackup_;
+    //! Function resetting the velocities
+    void resetVelocities();
+
+    //! Whether planned total number of steps was reached (used for final output only)
+    bool isRegularSimulationEnd_;
+    //! The signalled last step (used for final output only)
+    Step lastStep_;
+
+    // Access to ISimulator data
+    //! Full simulation state (only non-nullptr on master rank).
+    t_state* globalState_;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element for StatePropagatorData
+ *
+ * The `StatePropagatorData::Element` takes part in the simulator run, as it might
+ * have to save a valid state at the right moment during the
+ * integration. Placing the StatePropagatorData::Element correctly is the
+ * duty of the simulator builder - this might be automatized later
+ * if we have enough meta-data of the variables (i.e., if
+ * `StatePropagatorData` knows at which time the variables currently are,
+ * and can decide when a valid state (full-time step of all
+ * variables) is reached. The `StatePropagatorData::Element` is also a client of
+ * both the trajectory signaller and writer - it will save a
+ * state for later writeout during the simulator step if it
+ * knows that trajectory writing will occur later in the step,
+ * and it knows how to write to file given a file pointer by
+ * the `TrajectoryElement`. It is also responsible to store
+ * the state for checkpointing.
+ *
+ */
+class StatePropagatorData::Element final :
+    public ISimulatorElement,
+    public ITrajectoryWriterClient,
+    public ITrajectorySignallerClient,
+    public ICheckpointHelperClient,
+    public ILastStepSignallerClient
+{
+public:
+    //! Constructor
+    Element(StatePropagatorData* statePropagatorData,
+            FILE*                fplog,
+            const t_commrec*     cr,
+            int                  nstxout,
+            int                  nstvout,
+            int                  nstfout,
+            int                  nstxout_compressed,
+            bool                 canMoleculesBeDistributedOverPBC,
+            bool                 writeFinalConfiguration,
+            std::string          finalConfigurationFilename,
+            const t_inputrec*    inputrec,
+            const gmx_mtop_t*    globalTop);
+
+    /*! \brief Register run function for step / time
+     *
+     * This needs to be called during the integration part of the simulator,
+     * at the moment at which the state is at a full time step. Positioning
+     * this element is the responsibility of the programmer writing the
+     * integration algorithm! If the current step is a trajectory writing
+     * step, StatePropagatorData will save a backup for later writeout.
+     *
+     * This is also the place at which the current state becomes the previous
+     * state.
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    /*! \brief Backup starting velocities
+     *
+     * This is only needed for vv, where the first (velocity) half step is only
+     * used to compute the constraint virial, but the velocities need to be reset
+     * after.
+     * TODO: There must be a more elegant solution to this!
+     */
+    void elementSetup() override;
+
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    //! Set free energy data
+    void setFreeEnergyPerturbationData(FreeEnergyPerturbationData* freeEnergyPerturbationData);
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper);
+
+private:
+    //! Pointer to the associated StatePropagatorData
+    StatePropagatorData* statePropagatorData_;
+
+    //! The position writeout frequency
+    const int nstxout_;
+    //! The velocity writeout frequency
+    const int nstvout_;
+    //! The force writeout frequency
+    const int nstfout_;
+    //! The compressed position writeout frequency
+    const int nstxout_compressed_;
 
     //! Pointer to keep a backup of the state for later writeout
     std::unique_ptr<t_state> localStateBackup_;
@@ -238,43 +342,44 @@ private:
     void saveState();
 
     //! ITrajectorySignallerClient implementation
-    SignallerCallbackPtr registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
 
     //! ITrajectoryWriterClient implementation
-    ITrajectoryWriterCallbackPtr registerTrajectoryWriterCallback(TrajectoryEvent event) override;
+    std::optional<ITrajectoryWriterCallback> registerTrajectoryWriterCallback(TrajectoryEvent event) override;
 
-    //! ICheckpointHelperClient implementation
-    void writeCheckpoint(t_state* localState, t_state* globalState) override;
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "StatePropagatorData";
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData, const t_commrec* cr);
 
     //! ILastStepSignallerClient implementation (used for final output only)
-    SignallerCallbackPtr registerLastStepCallback() override;
+    std::optional<SignallerCallback> registerLastStepCallback() override;
 
     //! Callback writing the state to file
     void write(gmx_mdoutf* outf, Step step, Time time);
 
-    //! Whether we're doing VV and need to reset velocities after the first half step
-    bool vvResetVelocities_;
-    //! Velocities backup for VV
-    PaddedHostVector<RVec> velocityBackup_;
-    //! Function resetting the velocities
-    void resetVelocities();
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the free energy perturbation data (for trajectory writing only)
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
 
-    //! Pointer to the free energy perturbation element (for trajectory writing only)
-    FreeEnergyPerturbationElement* freeEnergyPerturbationElement_;
+    //! No trajectory writer setup needed
+    void trajectoryWriterSetup(gmx_mdoutf gmx_unused* outf) override {}
+    //! Trajectory writer teardown - write final coordinates
+    void trajectoryWriterTeardown(gmx_mdoutf* outf) override;
+    //! A dummy CheckpointData - remove when we stop using the legacy trajectory writing function
+    WriteCheckpointDataHolder dummyCheckpointDataHolder_;
 
     //! Whether planned total number of steps was reached (used for final output only)
     bool isRegularSimulationEnd_;
     //! The signalled last step (used for final output only)
     Step lastStep_;
-
     //! Whether system can have molecules distributed over PBC boundaries (used for final output only)
     const bool canMoleculesBeDistributedOverPBC_;
     //! Whether system has molecules self-interacting through PBC (used for final output only)
     const bool systemHasPeriodicMolecules_;
     //! The PBC type (used for final output only)
-    const int pbcType_;
-    //! Pointer to the topology (used for final output only)
-    const TopologyHolder* topologyHolder_;
+    const PbcType pbcType_;
     //! The (planned) last step - determines whether final configuration is written (used for final output only)
     const Step lastPlannedStep_;
     //! Whether final configuration was chosen in mdrun options (used for final output only)
@@ -287,13 +392,8 @@ private:
     FILE* fplog_;
     //! Handles communication.
     const t_commrec* cr_;
-    //! Full simulation state (only non-nullptr on master rank).
-    t_state* globalState_;
-
-    //! No trajectory writer setup needed
-    void trajectoryWriterSetup(gmx_mdoutf gmx_unused* outf) override {}
-    //! Trajectory writer teardown - write final coordinates
-    void trajectoryWriterTeardown(gmx_mdoutf* outf) override;
+    //! Full system topology.
+    const gmx_mtop_t* top_global_;
 };
 
 } // namespace gmx
index e5bf8f5b874704069ee63d483571b3bbb8d66a2d..8fb91190b05ee8f9cd7d8ba3fd7354392e4dc3b2 100644 (file)
 
 namespace gmx
 {
-TopologyHolder::TopologyHolder(const gmx_mtop_t& globalTopology,
-                               const t_commrec*  cr,
-                               const t_inputrec* inputrec,
-                               t_forcerec*       fr,
-                               MDAtoms*          mdAtoms,
-                               Constraints*      constr,
-                               gmx_vsite_t*      vsite) :
+TopologyHolder::TopologyHolder(std::vector<ITopologyHolderClient*> clients,
+                               const gmx_mtop_t&                   globalTopology,
+                               const t_commrec*                    cr,
+                               const t_inputrec*                   inputrec,
+                               t_forcerec*                         fr,
+                               MDAtoms*                            mdAtoms,
+                               Constraints*                        constr,
+                               VirtualSitesHandler*                vsite) :
     globalTopology_(globalTopology),
-    localTopology_(std::make_unique<gmx_localtop_t>())
+    localTopology_(std::make_unique<gmx_localtop_t>(globalTopology.ffparams)),
+    clients_(std::move(clients))
 {
-    if (DOMAINDECOMP(cr))
+    if (!DOMAINDECOMP(cr))
     {
-        dd_init_local_top(globalTopology, localTopology_.get());
-    }
-    else
-    {
-        t_graph* graph = nullptr;
         // Generate and initialize new topology
         // Note that most of the data needed for the constructor is used here -
         // this function should probably be simplified sooner or later.
-        mdAlgorithmsSetupAtomData(cr, inputrec, globalTopology, localTopology_.get(), fr, &graph,
+        // Note: Legacy mdrun resizes the force buffer in mdAlgorithmsSetupAtomData()
+        //       TopologyHolder has no access to the forces, so we are passing a nullptr
+        //       TODO: Find a unique approach to resizing the forces in modular simulator (#3461)
+        mdAlgorithmsSetupAtomData(cr, inputrec, globalTopology, localTopology_.get(), fr, nullptr,
                                   mdAtoms, constr, vsite, nullptr);
-        GMX_RELEASE_ASSERT(graph == nullptr, "Graph is not implemented for the modular simulator.");
     }
+    // Send copy of initial topology to clients
+    updateLocalTopology();
 }
 
 const gmx_mtop_t& TopologyHolder::globalTopology() const
@@ -89,11 +90,17 @@ void TopologyHolder::updateLocalTopology()
     }
 }
 
-void TopologyHolder::registerClient(ITopologyHolderClient* client)
+void TopologyHolder::Builder::registerClient(ITopologyHolderClient* client)
 {
     // Register client
-    clients_.emplace_back(client);
-    // Send copy of current topology
-    client->setTopology(localTopology_.get());
+    if (client)
+    {
+        if (state_ == ModularSimulatorBuilderState::NotAcceptingClientRegistrations)
+        {
+            throw SimulationAlgorithmSetupError(
+                    "Tried to register to signaller after it was built.");
+        }
+        clients_.emplace_back(client);
+    }
 }
 } // namespace gmx
index 37170225b988ef967fcdd07662789a9533e71937..c6bb939e5fc1bf0d0efc6828e53f3f7eac0a3b58 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the topology class for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 
 
 #include <vector>
 
+#include "gromacs/compat/pointers.h"
+
 #include "modularsimulatorinterfaces.h"
 
 struct gmx_localtop_t;
 struct gmx_mtop_t;
-struct gmx_vsite_t;
 struct t_commrec;
 struct t_forcerec;
 struct t_inputrec;
@@ -58,8 +61,9 @@ namespace gmx
 {
 class Constraints;
 class MDAtoms;
+class VirtualSitesHandler;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Object holding the topology
  *
@@ -70,23 +74,24 @@ class TopologyHolder final
 {
 public:
     //! Constructor
-    TopologyHolder(const gmx_mtop_t& globalTopology,
-                   const t_commrec*  cr,
-                   const t_inputrec* inputrec,
-                   t_forcerec*       fr,
-                   MDAtoms*          mdAtoms,
-                   Constraints*      constr,
-                   gmx_vsite_t*      vsite);
+    TopologyHolder(std::vector<ITopologyHolderClient*> clients,
+                   const gmx_mtop_t&                   globalTopology,
+                   const t_commrec*                    cr,
+                   const t_inputrec*                   inputrec,
+                   t_forcerec*                         fr,
+                   MDAtoms*                            mdAtoms,
+                   Constraints*                        constr,
+                   VirtualSitesHandler*                vsite);
 
     //! Get global topology
     const gmx_mtop_t& globalTopology() const;
 
-    //! Register topology client
-    void registerClient(ITopologyHolderClient* client);
-
     //! Allow domdec to update local topology
     friend class DomDecHelper;
 
+    //! The builder
+    class Builder;
+
 private:
     //! Constant reference to the global topolgy
     const gmx_mtop_t& globalTopology_;
@@ -100,6 +105,34 @@ private:
     void updateLocalTopology();
 };
 
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Builder for the topology holder
+ */
+class TopologyHolder::Builder
+{
+public:
+    //! Register topology client
+    void registerClient(ITopologyHolderClient* client);
+
+    //! Build TopologyHolder
+    template<typename... Args>
+    std::unique_ptr<TopologyHolder> build(Args&&... args);
+
+private:
+    //! List of clients to be updated if local topology changes
+    std::vector<ITopologyHolderClient*> clients_;
+    //! The state of the builder
+    ModularSimulatorBuilderState state_ = ModularSimulatorBuilderState::AcceptingClientRegistrations;
+};
+
+template<typename... Args>
+std::unique_ptr<TopologyHolder> TopologyHolder::Builder::build(Args&&... args)
+{
+    state_ = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
+    return std::make_unique<TopologyHolder>(std::move(clients_), std::forward<Args>(args)...);
+}
+
 } // namespace gmx
 
 #endif // GMX_MODULARSIMULATOR_TOPOLOGYHOLDER_H
index 1195b294cb70b286d0bcead32fe0ebe83126b723..bf00357f377b09dfd24e7c14d9a6ab3cbf7cfd3b 100644 (file)
@@ -49,9 +49,7 @@
 
 namespace gmx
 {
-TrajectoryElement::TrajectoryElement(std::vector<SignallerCallbackPtr>     signalEnergyCallbacks,
-                                     std::vector<SignallerCallbackPtr>     signalStateCallbacks,
-                                     std::vector<ITrajectoryWriterClient*> writerClients,
+TrajectoryElement::TrajectoryElement(std::vector<ITrajectoryWriterClient*> writerClients,
                                      FILE*                                 fplog,
                                      int                                   nfile,
                                      const t_filenm                        fnm[],
@@ -60,7 +58,7 @@ TrajectoryElement::TrajectoryElement(std::vector<SignallerCallbackPtr>     signa
                                      gmx::IMDOutputProvider*               outputProvider,
                                      const MdModulesNotifier&              mdModulesNotifier,
                                      const t_inputrec*                     inputrec,
-                                     gmx_mtop_t*                           top_global,
+                                     const gmx_mtop_t*                     top_global,
                                      const gmx_output_env_t*               oenv,
                                      gmx_wallcycle*                        wcycle,
                                      StartingBehavior                      startingBehavior,
@@ -81,51 +79,25 @@ TrajectoryElement::TrajectoryElement(std::vector<SignallerCallbackPtr>     signa
                       startingBehavior,
                       simulationsShareState,
                       nullptr)),
-    nstxout_(inputrec->nstxout),
-    nstvout_(inputrec->nstvout),
-    nstfout_(inputrec->nstfout),
-    nstxoutCompressed_(inputrec->nstxout_compressed),
-    tngBoxOut_(mdoutf_get_tng_box_output_interval(outf_)),
-    tngLambdaOut_(mdoutf_get_tng_lambda_output_interval(outf_)),
-    tngBoxOutCompressed_(mdoutf_get_tng_compressed_box_output_interval(outf_)),
-    tngLambdaOutCompressed_(mdoutf_get_tng_compressed_lambda_output_interval(outf_)),
-    nstenergy_(inputrec->nstenergy),
-    signalEnergyCallbacks_(std::move(signalEnergyCallbacks)),
-    signalStateCallbacks_(std::move(signalStateCallbacks)),
-    lastStep_(-1),
-    lastStepRegistrationDone_(false),
     writerClients_(std::move(writerClients))
 {
 }
 
-void TrajectoryElement::signallerSetup()
+int TrajectoryElement::tngBoxOut() const
 {
-    GMX_ASSERT(lastStepRegistrationDone_,
-               "TrajectoryElement needs to be registered to LastStepSignaller.");
+    return mdoutf_get_tng_box_output_interval(outf_);
 }
-
-void TrajectoryElement::signal(Step step, Time time)
+int TrajectoryElement::tngLambdaOut() const
 {
-    if (do_per_step(step, nstxout_) || do_per_step(step, nstvout_) || do_per_step(step, nstfout_)
-        || do_per_step(step, nstxoutCompressed_) || do_per_step(step, tngBoxOut_)
-        || do_per_step(step, tngLambdaOut_) || do_per_step(step, tngBoxOutCompressed_)
-        || do_per_step(step, tngLambdaOutCompressed_))
-    {
-        writeStateStep_ = step;
-        for (const auto& callback : signalStateCallbacks_)
-        {
-            (*callback)(step, time);
-        }
-    }
-
-    if (do_per_step(step, nstenergy_) || step == lastStep_)
-    {
-        writeEnergyStep_ = step;
-        for (const auto& callback : signalEnergyCallbacks_)
-        {
-            (*callback)(step, time);
-        }
-    }
+    return mdoutf_get_tng_lambda_output_interval(outf_);
+}
+int TrajectoryElement::tngBoxOutCompressed() const
+{
+    return mdoutf_get_tng_compressed_box_output_interval(outf_);
+}
+int TrajectoryElement::tngLambdaOutCompressed() const
+{
+    return mdoutf_get_tng_compressed_lambda_output_interval(outf_);
 }
 
 void TrajectoryElement::elementSetup()
@@ -135,28 +107,27 @@ void TrajectoryElement::elementSetup()
         auto callback = client->registerTrajectoryWriterCallback(TrajectoryEvent::StateWritingStep);
         if (callback)
         {
-            runStateCallbacks_.emplace_back(std::move(callback));
+            runStateCallbacks_.emplace_back(std::move(*callback));
         }
         callback = client->registerTrajectoryWriterCallback(TrajectoryEvent::EnergyWritingStep);
         if (callback)
         {
-            runEnergyCallbacks_.emplace_back(std::move(callback));
+            runEnergyCallbacks_.emplace_back(std::move(*callback));
         }
         client->trajectoryWriterSetup(outf_);
     }
 }
 
-void TrajectoryElement::scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction)
+void TrajectoryElement::scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction)
 {
     const bool writeEnergyThisStep = writeEnergyStep_ == step;
     const bool writeStateThisStep  = writeStateStep_ == step;
     const bool writeLogThisStep    = logWritingStep_ == step;
     if (writeEnergyThisStep || writeStateThisStep || writeLogThisStep)
     {
-        (*registerRunFunction)(std::make_unique<SimulatorRunFunction>(
-                [this, step, time, writeStateThisStep, writeEnergyThisStep, writeLogThisStep]() {
-                    write(step, time, writeStateThisStep, writeEnergyThisStep, writeLogThisStep);
-                }));
+        registerRunFunction([this, step, time, writeStateThisStep, writeEnergyThisStep, writeLogThisStep]() {
+            write(step, time, writeStateThisStep, writeEnergyThisStep, writeLogThisStep);
+        });
     }
 }
 
@@ -176,39 +147,47 @@ void TrajectoryElement::write(Step step, Time time, bool writeState, bool writeE
     {
         for (auto& callback : runStateCallbacks_)
         {
-            (*callback)(outf_, step, time, writeState, writeLog);
+            callback(outf_, step, time, writeState, writeLog);
         }
     }
     if (writeEnergy || writeLog)
     {
         for (auto& callback : runEnergyCallbacks_)
         {
-            (*callback)(outf_, step, time, writeEnergy, writeLog);
+            callback(outf_, step, time, writeEnergy, writeLog);
         }
     }
 }
 
-SignallerCallbackPtr TrajectoryElement::registerLastStepCallback()
-{
-    lastStepRegistrationDone_ = true;
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time gmx_unused time) { this->lastStep_ = step; });
-}
-
-SignallerCallbackPtr TrajectoryElement::registerLoggingCallback()
+std::optional<SignallerCallback> TrajectoryElement::registerLoggingCallback()
 {
-    return std::make_unique<SignallerCallback>(
-            [this](Step step, Time /*unused*/) { logWritingStep_ = step; });
+    return [this](Step step, Time /*unused*/) { logWritingStep_ = step; };
 }
 
-void TrajectoryElementBuilder::registerSignallerClient(compat::not_null<ITrajectorySignallerClient*> client)
+std::optional<SignallerCallback> TrajectoryElement::registerTrajectorySignallerCallback(TrajectoryEvent event)
 {
-    signallerClients_.emplace_back(client);
+    if (event == TrajectoryEvent::StateWritingStep)
+    {
+        return [this](Step step, Time /*unused*/) { this->writeStateStep_ = step; };
+    }
+    if (event == TrajectoryEvent::EnergyWritingStep)
+    {
+        return [this](Step step, Time /*unused*/) { this->writeEnergyStep_ = step; };
+    }
+    return std::nullopt;
 }
 
-void TrajectoryElementBuilder::registerWriterClient(compat::not_null<ITrajectoryWriterClient*> client)
+void TrajectoryElementBuilder::registerWriterClient(ITrajectoryWriterClient* client)
 {
-    writerClients_.emplace_back(client);
+    if (client)
+    {
+        if (state_ == ModularSimulatorBuilderState::NotAcceptingClientRegistrations)
+        {
+            throw SimulationAlgorithmSetupError(
+                    "Tried to register to signaller after it was built.");
+        }
+        writerClients_.emplace_back(client);
+    }
 }
 
 } // namespace gmx
index fd7060202439d0c6ff4fc8d8e98f75ecabbf29aa..a0b3d05e603d639ca96c7f160eace680781e2d04 100644 (file)
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
+/*! \internal \file
  * \brief Declares the trajectory element for the modular simulator
  *
  * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
  */
 
 #ifndef GMX_MODULARSIMULATOR_TRAJECTORYELEMENT_H
@@ -62,7 +64,7 @@ struct MdModulesNotifier;
 struct MdrunOptions;
 enum class StartingBehavior;
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Trajectory element signals and handles trajectory writing
  *
@@ -77,37 +79,18 @@ enum class StartingBehavior;
  * element only prepares the output struct, and passes it to the clients who
  * write their part of the trajectory.
  */
-class TrajectoryElement final :
-    public ISimulatorElement,
-    public ISignaller,
-    public ILastStepSignallerClient,
-    public ILoggingSignallerClient
+class TrajectoryElement final : public ISimulatorElement, public ILoggingSignallerClient, public ITrajectorySignallerClient
 {
 public:
     friend class TrajectoryElementBuilder;
-
-    /*
-     * Methods for the signaller part of the element
-     */
-
-    /*! \brief Prepare signaller
-     *
-     * Check that necessary registration was done
-     */
-    void signallerSetup() override;
-
-    /*! \brief Run the signaller at a specific step / time
-     *
-     * Informs clients when energy or state will be written.
-     *
-     * @param step           The current time step
-     * @param time           The current time
-     */
-    void signal(Step step, Time time) override;
-
-    /*
-     * Methods for the trajectory writing part of the element
-     */
+    //! Get the box writeout frequency for TNG
+    [[nodiscard]] int tngBoxOut() const;
+    //! Get the lambda writeout frequency for TNG
+    [[nodiscard]] int tngLambdaOut() const;
+    //! Get the compressed box writeout frequency for TNG
+    [[nodiscard]] int tngBoxOutCompressed() const;
+    //! Get the compressed lambda writeout frequency for TNG
+    [[nodiscard]] int tngLambdaOutCompressed() const;
 
     /*! \brief Prepare trajectory writer
      *
@@ -123,11 +106,11 @@ public:
      * Registers a trajectory writing function if the current step / time is
      * either a state or energy writing step, as defined by the signaller
      *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
      */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
 
     /*! \brief Teardown trajectory writer
      *
@@ -137,17 +120,15 @@ public:
      */
     void elementTeardown() override;
 
-    //! @cond
+    //! \cond
     // (doxygen doesn't like these...)
     //! Allow CheckpointHelper to use outf_ (TODO: Can we improve this?)
     friend class CheckpointHelper;
-    //! @endcond
+    //! \endcond
 
 private:
     //! Constructor
-    TrajectoryElement(std::vector<SignallerCallbackPtr>     signalEnergyCallbacks,
-                      std::vector<SignallerCallbackPtr>     signalStateCallbacks,
-                      std::vector<ITrajectoryWriterClient*> writerClients,
+    TrajectoryElement(std::vector<ITrajectoryWriterClient*> writerClients,
                       FILE*                                 fplog,
                       int                                   nfile,
                       const t_filenm                        fnm[],
@@ -156,7 +137,7 @@ private:
                       IMDOutputProvider*                    outputProvider,
                       const MdModulesNotifier&              mdModulesNotifier,
                       const t_inputrec*                     inputrec,
-                      gmx_mtop_t*                           top_global,
+                      const gmx_mtop_t*                     top_global,
                       const gmx_output_env_t*               oenv,
                       gmx_wallcycle*                        wcycle,
                       StartingBehavior                      startingBehavior,
@@ -173,37 +154,9 @@ private:
     gmx_mdoutf* outf_;
 
     //! ILoggingSignallerClient implementation
-    SignallerCallbackPtr registerLoggingCallback() override;
-
-    /*
-     * Signaller
-     */
-    //! Output frequencies
-    //! {
-    const int nstxout_;
-    const int nstvout_;
-    const int nstfout_;
-    const int nstxoutCompressed_;
-    const int tngBoxOut_;
-    const int tngLambdaOut_;
-    const int tngBoxOutCompressed_;
-    const int tngLambdaOutCompressed_;
-    const int nstenergy_;
-    //! }
-
-    //! Callbacks to signal events
-    //! {
-    std::vector<SignallerCallbackPtr> signalEnergyCallbacks_;
-    std::vector<SignallerCallbackPtr> signalStateCallbacks_;
-    //! }
-
-    /*
-     * Last step client
-     */
-    Step lastStep_;
-    bool lastStepRegistrationDone_;
-    //! ILastStepSignallerClient implementation
-    SignallerCallbackPtr registerLastStepCallback() override;
+    std::optional<SignallerCallback> registerLoggingCallback() override;
+    //! ITrajectorySignallerClient implementation
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
 
     /*
      * Trajectory writing
@@ -213,66 +166,45 @@ private:
 
     //! Callbacks to write trajectory
     //! {
-    std::vector<ITrajectoryWriterCallbackPtr> runStateCallbacks_;
-    std::vector<ITrajectoryWriterCallbackPtr> runEnergyCallbacks_;
+    std::vector<ITrajectoryWriterCallback> runStateCallbacks_;
+    std::vector<ITrajectoryWriterCallback> runEnergyCallbacks_;
     //! }
 
     //! The writing function - calls the clients to get their contributions
     void write(Step step, Time time, bool writeState, bool writeEnergy, bool writeLog);
 };
 
-/*! \libinternal
+/*! \internal
  * \ingroup module_modularsimulator
  * \brief Build the `TrajectoryElement`
  *
- * This builder allows clients to register with the trajectory element, either
- * as signaller clients or as writer clients. The builder then builds the
- * element.
+ * This builder allows clients to register with the trajectory element
+ * as writer clients. The builder then builds the trajectory element.
  */
 class TrajectoryElementBuilder final
 {
 public:
-    //! Allows clients to register to the signaller
-    void registerSignallerClient(compat::not_null<ITrajectorySignallerClient*> client);
-
     //! Allows clients to register as trajectory writers
-    void registerWriterClient(compat::not_null<ITrajectoryWriterClient*> client);
+    void registerWriterClient(ITrajectoryWriterClient* client);
 
     //! Build the TrajectoryElement
     template<typename... Args>
     std::unique_ptr<TrajectoryElement> build(Args&&... args);
 
 private:
-    //! List of signaller clients
-    std::vector<ITrajectorySignallerClient*> signallerClients_;
     //! List of writer clients
     std::vector<ITrajectoryWriterClient*> writerClients_;
+    //! The state of the builder
+    ModularSimulatorBuilderState state_ = ModularSimulatorBuilderState::AcceptingClientRegistrations;
 };
 
 template<typename... Args>
 std::unique_ptr<TrajectoryElement> TrajectoryElementBuilder::build(Args&&... args)
 {
-    std::vector<SignallerCallbackPtr> signalEnergyCallbacks;
-    std::vector<SignallerCallbackPtr> signalStateCallbacks;
-    // Allow clients to register their callbacks
-    for (auto& client : signallerClients_)
-    {
-        // don't register nullptr
-        if (auto energyCallback =
-                    client->registerTrajectorySignallerCallback(TrajectoryEvent::EnergyWritingStep))
-        {
-            signalEnergyCallbacks.emplace_back(std::move(energyCallback));
-        }
-        if (auto stateCallback =
-                    client->registerTrajectorySignallerCallback(TrajectoryEvent::StateWritingStep))
-        {
-            signalStateCallbacks.emplace_back(std::move(stateCallback));
-        }
-    }
+    state_ = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
     // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
     return std::unique_ptr<TrajectoryElement>(
-            new TrajectoryElement(std::move(signalEnergyCallbacks), std::move(signalStateCallbacks),
-                                  std::move(writerClients_), std::forward<Args>(args)...));
+            new TrajectoryElement(std::move(writerClients_), std::forward<Args>(args)...));
 }
 
 } // namespace gmx
diff --git a/src/gromacs/modularsimulator/velocityscalingtemperaturecoupling.cpp b/src/gromacs/modularsimulator/velocityscalingtemperaturecoupling.cpp
new file mode 100644 (file)
index 0000000..1cc09b0
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Defines a velocity-scaling temperature coupling element for
+ * the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ */
+
+#include "gmxpre.h"
+
+#include "velocityscalingtemperaturecoupling.h"
+
+#include <numeric>
+
+#include "gromacs/domdec/domdec_network.h"
+#include "gromacs/math/units.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/mdlib/coupling.h"
+#include "gromacs/mdlib/stat.h"
+#include "gromacs/mdtypes/checkpointdata.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/group.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/utility/fatalerror.h"
+
+#include "modularsimulator.h"
+#include "simulatoralgorithm.h"
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Data used by the concrete temperature coupling implementations
+ */
+struct TemperatureCouplingData
+{
+    //! The coupling time step - simulation time step x nstcouple_
+    const double couplingTimeStep;
+    //! Coupling temperature per group
+    ArrayRef<const real> referenceTemperature;
+    //! Coupling time per group
+    ArrayRef<const real> couplingTime;
+    //! Number of degrees of freedom per group
+    ArrayRef<const real> numDegreesOfFreedom;
+    //! Work exerted by thermostat per group
+    ArrayRef<const double> temperatureCouplingIntegral;
+};
+
+/*! \internal
+ * \brief Interface for temperature coupling implementations
+ */
+class ITemperatureCouplingImpl
+{
+public:
+    //! Allow access to the scaling vectors
+    virtual void connectWithPropagator(const PropagatorThermostatConnection& connectionData,
+                                       int numTemperatureGroups) = 0;
+
+    /*! \brief Make a temperature control step
+     *
+     * \param step                     The current step
+     * \param temperatureGroup         The current temperature group
+     * \param currentKineticEnergy     The kinetic energy of the temperature group
+     * \param currentTemperature       The temperature of the temperature group
+     * \param temperatureCouplingData  Access to general temperature coupling data
+     *
+     * \return  The temperature coupling integral for the current temperature group
+     */
+    [[nodiscard]] virtual real apply(Step                           step,
+                                     int                            temperatureGroup,
+                                     real                           currentKineticEnergy,
+                                     real                           currentTemperature,
+                                     const TemperatureCouplingData& temperatureCouplingData) = 0;
+
+    //! Write private data to checkpoint
+    virtual void writeCheckpoint(std::optional<WriteCheckpointData> checkpointData,
+                                 const t_commrec*                   cr) = 0;
+    //! Read private data from checkpoint
+    virtual void readCheckpoint(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) = 0;
+
+    //! Standard virtual destructor
+    virtual ~ITemperatureCouplingImpl() = default;
+};
+
+/*! \internal
+ * \brief Implements v-rescale temperature coupling
+ */
+class VRescaleTemperatureCoupling final : public ITemperatureCouplingImpl
+{
+public:
+    //! Apply the v-rescale temperature control
+    real apply(Step step,
+               int  temperatureGroup,
+               real currentKineticEnergy,
+               real gmx_unused                currentTemperature,
+               const TemperatureCouplingData& temperatureCouplingData) override
+    {
+        if (!(temperatureCouplingData.couplingTime[temperatureGroup] >= 0
+              && temperatureCouplingData.numDegreesOfFreedom[temperatureGroup] > 0
+              && currentKineticEnergy > 0))
+        {
+            lambdaStartVelocities_[temperatureGroup] = 1.0;
+            return temperatureCouplingData.temperatureCouplingIntegral[temperatureGroup];
+        }
+
+        const real referenceKineticEnergy =
+                0.5 * temperatureCouplingData.referenceTemperature[temperatureGroup] * BOLTZ
+                * temperatureCouplingData.numDegreesOfFreedom[temperatureGroup];
+
+        const real newKineticEnergy =
+                vrescale_resamplekin(currentKineticEnergy, referenceKineticEnergy,
+                                     temperatureCouplingData.numDegreesOfFreedom[temperatureGroup],
+                                     temperatureCouplingData.couplingTime[temperatureGroup]
+                                             / temperatureCouplingData.couplingTimeStep,
+                                     step, seed_);
+
+        // Analytically newKineticEnergy >= 0, but we check for rounding errors
+        if (newKineticEnergy <= 0)
+        {
+            lambdaStartVelocities_[temperatureGroup] = 0.0;
+        }
+        else
+        {
+            lambdaStartVelocities_[temperatureGroup] = std::sqrt(newKineticEnergy / currentKineticEnergy);
+        }
+
+        if (debug)
+        {
+            fprintf(debug, "TC: group %d: Ekr %g, Ek %g, Ek_new %g, Lambda: %g\n", temperatureGroup,
+                    referenceKineticEnergy, currentKineticEnergy, newKineticEnergy,
+                    lambdaStartVelocities_[temperatureGroup]);
+        }
+
+        return temperatureCouplingData.temperatureCouplingIntegral[temperatureGroup]
+               - (newKineticEnergy - currentKineticEnergy);
+    }
+
+    //! Connect with propagator - v-rescale only scales start step velocities
+    void connectWithPropagator(const PropagatorThermostatConnection& connectionData,
+                               int                                   numTemperatureGroups) override
+    {
+        connectionData.setNumVelocityScalingVariables(numTemperatureGroups);
+        lambdaStartVelocities_ = connectionData.getViewOnVelocityScaling();
+    }
+
+    //! No data to write to checkpoint
+    void writeCheckpoint(std::optional<WriteCheckpointData> gmx_unused checkpointData,
+                         const t_commrec gmx_unused* cr) override
+    {
+    }
+    //! No data to read from checkpoints
+    void readCheckpoint(std::optional<ReadCheckpointData> gmx_unused checkpointData,
+                        const t_commrec gmx_unused* cr) override
+    {
+    }
+
+    //! Constructor
+    VRescaleTemperatureCoupling(int64_t seed) : seed_(seed) {}
+
+private:
+    //! The random seed
+    const int64_t seed_;
+
+    //! View on the scaling factor of the propagator (pre-step velocities)
+    ArrayRef<real> lambdaStartVelocities_;
+};
+
+/*! \internal
+ * \brief Implements Berendsen temperature coupling
+ */
+class BerendsenTemperatureCoupling final : public ITemperatureCouplingImpl
+{
+public:
+    //! Apply the v-rescale temperature control
+    real apply(Step gmx_unused                step,
+               int                            temperatureGroup,
+               real                           currentKineticEnergy,
+               real                           currentTemperature,
+               const TemperatureCouplingData& temperatureCouplingData) override
+    {
+        if (!(temperatureCouplingData.couplingTime[temperatureGroup] >= 0
+              && temperatureCouplingData.numDegreesOfFreedom[temperatureGroup] > 0
+              && currentKineticEnergy > 0))
+        {
+            lambdaStartVelocities_[temperatureGroup] = 1.0;
+            return temperatureCouplingData.temperatureCouplingIntegral[temperatureGroup];
+        }
+
+        real lambda =
+                std::sqrt(1.0
+                          + (temperatureCouplingData.couplingTimeStep
+                             / temperatureCouplingData.couplingTime[temperatureGroup])
+                                    * (temperatureCouplingData.referenceTemperature[temperatureGroup] / currentTemperature
+                                       - 1.0));
+        lambdaStartVelocities_[temperatureGroup] = std::max<real>(std::min<real>(lambda, 1.25), 0.8);
+        if (debug)
+        {
+            fprintf(debug, "TC: group %d: T: %g, Lambda: %g\n", temperatureGroup,
+                    currentTemperature, lambdaStartVelocities_[temperatureGroup]);
+        }
+        return temperatureCouplingData.temperatureCouplingIntegral[temperatureGroup]
+               - (lambdaStartVelocities_[temperatureGroup] * lambdaStartVelocities_[temperatureGroup]
+                  - 1) * currentKineticEnergy;
+    }
+
+    //! Connect with propagator - Berendsen only scales start step velocities
+    void connectWithPropagator(const PropagatorThermostatConnection& connectionData,
+                               int                                   numTemperatureGroups) override
+    {
+        connectionData.setNumVelocityScalingVariables(numTemperatureGroups);
+        lambdaStartVelocities_ = connectionData.getViewOnVelocityScaling();
+    }
+
+    //! No data to write to checkpoint
+    void writeCheckpoint(std::optional<WriteCheckpointData> gmx_unused checkpointData,
+                         const t_commrec gmx_unused* cr) override
+    {
+    }
+    //! No data to read from checkpoints
+    void readCheckpoint(std::optional<ReadCheckpointData> gmx_unused checkpointData,
+                        const t_commrec gmx_unused* cr) override
+    {
+    }
+
+private:
+    //! View on the scaling factor of the propagator (pre-step velocities)
+    ArrayRef<real> lambdaStartVelocities_;
+};
+
+VelocityScalingTemperatureCoupling::VelocityScalingTemperatureCoupling(
+        int                               nstcouple,
+        int                               offset,
+        UseFullStepKE                     useFullStepKE,
+        ReportPreviousStepConservedEnergy reportPreviousConservedEnergy,
+        int64_t                           seed,
+        int                               numTemperatureGroups,
+        double                            couplingTimeStep,
+        const real*                       referenceTemperature,
+        const real*                       couplingTime,
+        const real*                       numDegreesOfFreedom,
+        EnergyData*                       energyData,
+        TemperatureCouplingType           couplingType) :
+    nstcouple_(nstcouple),
+    offset_(offset),
+    useFullStepKE_(useFullStepKE),
+    reportPreviousConservedEnergy_(reportPreviousConservedEnergy),
+    numTemperatureGroups_(numTemperatureGroups),
+    couplingTimeStep_(couplingTimeStep),
+    referenceTemperature_(referenceTemperature, referenceTemperature + numTemperatureGroups),
+    couplingTime_(couplingTime, couplingTime + numTemperatureGroups),
+    numDegreesOfFreedom_(numDegreesOfFreedom, numDegreesOfFreedom + numTemperatureGroups),
+    temperatureCouplingIntegral_(numTemperatureGroups, 0.0),
+    energyData_(energyData)
+{
+    if (reportPreviousConservedEnergy_ == ReportPreviousStepConservedEnergy::Yes)
+    {
+        temperatureCouplingIntegralPreviousStep_ = temperatureCouplingIntegral_;
+    }
+    energyData->setVelocityScalingTemperatureCoupling(this);
+    if (couplingType == etcVRESCALE)
+    {
+        temperatureCouplingImpl_ = std::make_unique<VRescaleTemperatureCoupling>(seed);
+    }
+    else if (couplingType == etcBERENDSEN)
+    {
+        temperatureCouplingImpl_ = std::make_unique<BerendsenTemperatureCoupling>();
+    }
+    else
+    {
+        throw NotImplementedError("Temperature coupling " + std::string(ETCOUPLTYPE(couplingType))
+                                  + " is not implemented for modular simulator.");
+    }
+}
+
+void VelocityScalingTemperatureCoupling::connectWithPropagator(const PropagatorThermostatConnection& connectionData)
+{
+    temperatureCouplingImpl_->connectWithPropagator(connectionData, numTemperatureGroups_);
+    propagatorCallback_ = connectionData.getVelocityScalingCallback();
+}
+
+void VelocityScalingTemperatureCoupling::elementSetup()
+{
+    if (!propagatorCallback_)
+    {
+        throw MissingElementConnectionError(
+                "Velocity scaling temperature coupling was not connected to a propagator.\n"
+                "Connection to a propagator element is needed to scale the velocities.\n"
+                "Use connectWithPropagator(...) before building the ModularSimulatorAlgorithm "
+                "object.");
+    }
+}
+
+void VelocityScalingTemperatureCoupling::scheduleTask(Step step,
+                                                      Time gmx_unused            time,
+                                                      const RegisterRunFunction& registerRunFunction)
+{
+    /* The thermostat will need a valid kinetic energy when it is running.
+     * Currently, computeGlobalCommunicationPeriod() is making sure this
+     * happens on time.
+     * TODO: Once we're switching to a new global communication scheme, we
+     *       will want the thermostat to signal that global reduction
+     *       of the kinetic energy is needed.
+     *
+     */
+    if (do_per_step(step + nstcouple_ + offset_, nstcouple_))
+    {
+        // do T-coupling this step
+        registerRunFunction([this, step]() { setLambda(step); });
+
+        // Let propagator know that we want to do T-coupling
+        propagatorCallback_(step);
+    }
+}
+
+void VelocityScalingTemperatureCoupling::setLambda(Step step)
+{
+    // if we report the previous energy, calculate before the step
+    if (reportPreviousConservedEnergy_ == ReportPreviousStepConservedEnergy::Yes)
+    {
+        temperatureCouplingIntegralPreviousStep_ = temperatureCouplingIntegral_;
+    }
+
+    const auto*             ekind          = energyData_->ekindata();
+    TemperatureCouplingData thermostatData = { couplingTimeStep_, referenceTemperature_, couplingTime_,
+                                               numDegreesOfFreedom_, temperatureCouplingIntegral_ };
+
+    for (int temperatureGroup = 0; (temperatureGroup < numTemperatureGroups_); temperatureGroup++)
+    {
+        const real currentKineticEnergy = useFullStepKE_ == UseFullStepKE::Yes
+                                                  ? trace(ekind->tcstat[temperatureGroup].ekinf)
+                                                  : trace(ekind->tcstat[temperatureGroup].ekinh);
+        const real currentTemperature = useFullStepKE_ == UseFullStepKE::Yes
+                                                ? ekind->tcstat[temperatureGroup].T
+                                                : ekind->tcstat[temperatureGroup].Th;
+
+        temperatureCouplingIntegral_[temperatureGroup] = temperatureCouplingImpl_->apply(
+                step, temperatureGroup, currentKineticEnergy, currentTemperature, thermostatData);
+    }
+}
+
+namespace
+{
+/*!
+ * \brief Enum describing the contents VelocityScalingTemperatureCoupling writes to modular checkpoint
+ *
+ * When changing the checkpoint content, add a new element just above Count, and adjust the
+ * checkpoint functionality.
+ */
+enum class CheckpointVersion
+{
+    Base, //!< First version of modular checkpointing
+    Count //!< Number of entries. Add new versions right above this!
+};
+constexpr auto c_currentVersion = CheckpointVersion(int(CheckpointVersion::Count) - 1);
+} // namespace
+
+template<CheckpointDataOperation operation>
+void VelocityScalingTemperatureCoupling::doCheckpointData(CheckpointData<operation>* checkpointData)
+{
+    checkpointVersion(checkpointData, "VRescaleThermostat version", c_currentVersion);
+
+    checkpointData->arrayRef("thermostat integral",
+                             makeCheckpointArrayRef<operation>(temperatureCouplingIntegral_));
+}
+
+void VelocityScalingTemperatureCoupling::saveCheckpointState(std::optional<WriteCheckpointData> checkpointData,
+                                                             const t_commrec*                   cr)
+{
+    if (MASTER(cr))
+    {
+        doCheckpointData<CheckpointDataOperation::Write>(&checkpointData.value());
+    }
+    temperatureCouplingImpl_->writeCheckpoint(
+            checkpointData
+                    ? std::make_optional(checkpointData->subCheckpointData("thermostat impl"))
+                    : std::nullopt,
+            cr);
+}
+
+void VelocityScalingTemperatureCoupling::restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData,
+                                                                const t_commrec* cr)
+{
+    if (MASTER(cr))
+    {
+        doCheckpointData<CheckpointDataOperation::Read>(&checkpointData.value());
+    }
+    if (DOMAINDECOMP(cr))
+    {
+        dd_bcast(cr->dd, temperatureCouplingIntegral_.size() * sizeof(double),
+                 temperatureCouplingIntegral_.data());
+    }
+    temperatureCouplingImpl_->readCheckpoint(
+            checkpointData
+                    ? std::make_optional(checkpointData->subCheckpointData("thermostat impl"))
+                    : std::nullopt,
+            cr);
+}
+
+const std::string& VelocityScalingTemperatureCoupling::clientID()
+{
+    return identifier_;
+}
+
+real VelocityScalingTemperatureCoupling::conservedEnergyContribution() const
+{
+    if (reportPreviousConservedEnergy_ == ReportPreviousStepConservedEnergy::Yes)
+    {
+        return std::accumulate(temperatureCouplingIntegralPreviousStep_.begin(),
+                               temperatureCouplingIntegralPreviousStep_.end(), 0.0);
+    }
+    else
+    {
+        return std::accumulate(temperatureCouplingIntegral_.begin(),
+                               temperatureCouplingIntegral_.end(), 0.0);
+    }
+}
+
+ISimulatorElement* VelocityScalingTemperatureCoupling::getElementPointerImpl(
+        LegacySimulatorData*                    legacySimulatorData,
+        ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+        StatePropagatorData gmx_unused* statePropagatorData,
+        EnergyData gmx_unused*     energyData,
+        FreeEnergyPerturbationData gmx_unused* freeEnergyPerturbationData,
+        GlobalCommunicationHelper gmx_unused* globalCommunicationHelper,
+        int                                   offset,
+        UseFullStepKE                         useFullStepKE,
+        ReportPreviousStepConservedEnergy     reportPreviousStepConservedEnergy)
+{
+    // Element is now owned by the caller of this method, who will handle lifetime (see ModularSimulatorAlgorithm)
+    auto* element = builderHelper->storeElement(std::make_unique<VelocityScalingTemperatureCoupling>(
+            legacySimulatorData->inputrec->nsttcouple, offset, useFullStepKE, reportPreviousStepConservedEnergy,
+            legacySimulatorData->inputrec->ld_seed, legacySimulatorData->inputrec->opts.ngtc,
+            legacySimulatorData->inputrec->delta_t * legacySimulatorData->inputrec->nsttcouple,
+            legacySimulatorData->inputrec->opts.ref_t, legacySimulatorData->inputrec->opts.tau_t,
+            legacySimulatorData->inputrec->opts.nrdf, energyData, legacySimulatorData->inputrec->etc));
+    auto* thermostat = static_cast<VelocityScalingTemperatureCoupling*>(element);
+    // Capturing pointer is safe because lifetime is handled by caller
+    builderHelper->registerThermostat([thermostat](const PropagatorThermostatConnection& connection) {
+        thermostat->connectWithPropagator(connection);
+    });
+    return element;
+}
+
+} // namespace gmx
diff --git a/src/gromacs/modularsimulator/velocityscalingtemperaturecoupling.h b/src/gromacs/modularsimulator/velocityscalingtemperaturecoupling.h
new file mode 100644 (file)
index 0000000..9bd3f74
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares a velocity-scaling temperature coupling element for
+ * the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_VELOCITYSCALINGTEMPERATURECOUPLING_H
+#define GMX_MODULARSIMULATOR_VELOCITYSCALINGTEMPERATURECOUPLING_H
+
+#include "gromacs/utility/arrayref.h"
+
+#include "energydata.h"
+#include "modularsimulatorinterfaces.h"
+#include "propagator.h"
+
+struct t_commrec;
+
+namespace gmx
+{
+class ITemperatureCouplingImpl;
+class LegacySimulatorData;
+struct TemperatureCouplingData;
+
+//! Enum describing whether the thermostat is using full or half step kinetic energy
+enum class UseFullStepKE
+{
+    Yes,
+    No,
+    Count
+};
+
+//! Enum describing whether the thermostat is reporting conserved energy from the previous step
+enum class ReportPreviousStepConservedEnergy
+{
+    Yes,
+    No,
+    Count
+};
+
+//! Typedef to match current use of ints as types.
+using TemperatureCouplingType = int;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element implementing the a velocity-scaling thermostat
+ *
+ * This element takes a callback to the propagator and updates the velocity
+ * scaling factor according to the internal temperature coupling implementation.
+ *
+ * Note that the concrete implementation is handled by the concrete
+ * implementations of the ITemperatureCouplingImpl interface, while the element
+ * handles the scheduling and interfacing with other elements.
+ */
+class VelocityScalingTemperatureCoupling final : public ISimulatorElement, public ICheckpointHelperClient
+{
+public:
+    //! Constructor
+    VelocityScalingTemperatureCoupling(int                               nstcouple,
+                                       int                               offset,
+                                       UseFullStepKE                     useFullStepKE,
+                                       ReportPreviousStepConservedEnergy reportPreviousConservedEnergy,
+                                       int64_t                           seed,
+                                       int                               numTemperatureGroups,
+                                       double                            couplingTimeStep,
+                                       const real*                       referenceTemperature,
+                                       const real*                       couplingTime,
+                                       const real*                       numDegreesOfFreedom,
+                                       EnergyData*                       energyData,
+                                       TemperatureCouplingType           couplingType);
+
+    /*! \brief Register run function for step / time
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! Sanity check at setup time
+    void elementSetup() override;
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    //! Contribution to the conserved energy (called by energy data)
+    [[nodiscard]] real conservedEnergyContribution() const;
+
+    //! Connect this to propagator
+    void connectWithPropagator(const PropagatorThermostatConnection& connectionData);
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     * \param offset  The step offset at which the thermostat is applied
+     * \param useFullStepKE  Whether full step or half step KE is used
+     * \param reportPreviousStepConservedEnergy  Report the previous or the current step conserved energy
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement*
+    getElementPointerImpl(LegacySimulatorData*                    legacySimulatorData,
+                          ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                          StatePropagatorData*                    statePropagatorData,
+                          EnergyData*                             energyData,
+                          FreeEnergyPerturbationData*             freeEnergyPerturbationData,
+                          GlobalCommunicationHelper*              globalCommunicationHelper,
+                          int                                     offset,
+                          UseFullStepKE                           useFullStepKE,
+                          ReportPreviousStepConservedEnergy reportPreviousStepConservedEnergy);
+
+private:
+    //! The frequency at which the thermostat is applied
+    const int nstcouple_;
+    //! If != 0, offset the step at which the thermostat is applied
+    const int offset_;
+    //! Whether we're using full step kinetic energy
+    const UseFullStepKE useFullStepKE_;
+    //! Whether we are reporting the conserved energy from the previous step
+    const ReportPreviousStepConservedEnergy reportPreviousConservedEnergy_;
+
+    //! The number of temperature groups
+    const int numTemperatureGroups_;
+    //! The coupling time step - simulation time step x nstcouple_
+    const double couplingTimeStep_;
+    //! Coupling temperature per group
+    const std::vector<real> referenceTemperature_;
+    //! Coupling time per group
+    const std::vector<real> couplingTime_;
+    //! Number of degrees of freedom per group
+    const std::vector<real> numDegreesOfFreedom_;
+    //! Work exerted by thermostat per group
+    std::vector<double> temperatureCouplingIntegral_;
+    //! Work exerted by thermostat per group (backup from previous step)
+    std::vector<double> temperatureCouplingIntegralPreviousStep_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the energy data (for ekindata)
+    EnergyData* energyData_;
+
+    //! Callback to let propagator know that we updated lambda
+    PropagatorCallback propagatorCallback_;
+
+    //! Set new lambda value (at T-coupling steps)
+    void setLambda(Step step);
+
+    //! The temperature coupling implementation
+    std::unique_ptr<ITemperatureCouplingImpl> temperatureCouplingImpl_;
+
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "VelocityScalingTemperatureCoupling";
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_VELOCITYSCALINGTEMPERATURECOUPLING_H
diff --git a/src/gromacs/modularsimulator/vrescalethermostat.cpp b/src/gromacs/modularsimulator/vrescalethermostat.cpp
deleted file mode 100644 (file)
index be1c4bc..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-/*! \internal \file
- * \brief Defines the v-rescale thermostat for the modular simulator
- *
- * \author Pascal Merz <pascal.merz@me.com>
- * \ingroup module_modularsimulator
- */
-
-#include "gmxpre.h"
-
-#include "vrescalethermostat.h"
-
-#include "gromacs/domdec/domdec_network.h"
-#include "gromacs/math/units.h"
-#include "gromacs/math/vec.h"
-#include "gromacs/mdlib/stat.h"
-#include "gromacs/mdlib/update.h"
-#include "gromacs/mdtypes/commrec.h"
-#include "gromacs/mdtypes/group.h"
-#include "gromacs/mdtypes/state.h"
-#include "gromacs/utility/fatalerror.h"
-
-namespace gmx
-{
-
-VRescaleThermostat::VRescaleThermostat(int                   nstcouple,
-                                       int                   offset,
-                                       bool                  useFullStepKE,
-                                       int64_t               seed,
-                                       int                   numTemperatureGroups,
-                                       double                couplingTimeStep,
-                                       const real*           referenceTemperature,
-                                       const real*           couplingTime,
-                                       const real*           numDegreesOfFreedom,
-                                       EnergyElement*        energyElement,
-                                       ArrayRef<real>        lambdaView,
-                                       PropagatorCallbackPtr propagatorCallback,
-                                       const t_state*        globalState,
-                                       t_commrec*            cr,
-                                       bool                  isRestart) :
-    nstcouple_(nstcouple),
-    offset_(offset),
-    useFullStepKE_(useFullStepKE),
-    seed_(seed),
-    numTemperatureGroups_(numTemperatureGroups),
-    couplingTimeStep_(couplingTimeStep),
-    referenceTemperature_(referenceTemperature, referenceTemperature + numTemperatureGroups),
-    couplingTime_(couplingTime, couplingTime + numTemperatureGroups),
-    numDegreesOfFreedom_(numDegreesOfFreedom, numDegreesOfFreedom + numTemperatureGroups),
-    thermostatIntegral_(numTemperatureGroups, 0.0),
-    energyElement_(energyElement),
-    lambda_(lambdaView),
-    propagatorCallback_(std::move(propagatorCallback))
-{
-    // TODO: This is only needed to restore the thermostatIntegral_ from cpt. Remove this when
-    //       switching to purely client-based checkpointing.
-    if (isRestart)
-    {
-        if (MASTER(cr))
-        {
-            for (unsigned long i = 0; i < thermostatIntegral_.size(); ++i)
-            {
-                thermostatIntegral_[i] = globalState->therm_integral[i];
-            }
-        }
-        if (DOMAINDECOMP(cr))
-        {
-            dd_bcast(cr->dd, int(thermostatIntegral_.size() * sizeof(double)), thermostatIntegral_.data());
-        }
-    }
-}
-
-void VRescaleThermostat::scheduleTask(Step step, Time gmx_unused time, const RegisterRunFunctionPtr& registerRunFunction)
-{
-    /* The thermostat will need a valid kinetic energy when it is running.
-     * Currently, computeGlobalCommunicationPeriod() is making sure this
-     * happens on time.
-     * TODO: Once we're switching to a new global communication scheme, we
-     *       will want the thermostat to signal that global reduction
-     *       of the kinetic energy is needed.
-     *
-     */
-    if (do_per_step(step + nstcouple_ + offset_, nstcouple_))
-    {
-        // do T-coupling this step
-        (*registerRunFunction)(
-                std::make_unique<SimulatorRunFunction>([this, step]() { setLambda(step); }));
-
-        // Let propagator know that we want to do T-coupling
-        (*propagatorCallback_)(step);
-    }
-}
-
-void VRescaleThermostat::setLambda(Step step)
-{
-    real currentKineticEnergy, referenceKineticEnergy, newKineticEnergy;
-
-    auto ekind = energyElement_->ekindata();
-
-    for (int i = 0; (i < numTemperatureGroups_); i++)
-    {
-        if (useFullStepKE_)
-        {
-            currentKineticEnergy = trace(ekind->tcstat[i].ekinf);
-        }
-        else
-        {
-            currentKineticEnergy = trace(ekind->tcstat[i].ekinh);
-        }
-
-        if (couplingTime_[i] >= 0 && numDegreesOfFreedom_[i] > 0 && currentKineticEnergy > 0)
-        {
-            referenceKineticEnergy = 0.5 * referenceTemperature_[i] * BOLTZ * numDegreesOfFreedom_[i];
-
-            newKineticEnergy = vrescale_resamplekin(currentKineticEnergy, referenceKineticEnergy,
-                                                    numDegreesOfFreedom_[i],
-                                                    couplingTime_[i] / couplingTimeStep_, step, seed_);
-
-            // Analytically newKineticEnergy >= 0, but we check for rounding errors
-            if (newKineticEnergy <= 0)
-            {
-                lambda_[i] = 0.0;
-            }
-            else
-            {
-                lambda_[i] = std::sqrt(newKineticEnergy / currentKineticEnergy);
-            }
-
-            thermostatIntegral_[i] -= newKineticEnergy - currentKineticEnergy;
-
-            if (debug)
-            {
-                fprintf(debug, "TC: group %d: Ekr %g, Ek %g, Ek_new %g, Lambda: %g\n", i,
-                        referenceKineticEnergy, currentKineticEnergy, newKineticEnergy, lambda_[i]);
-            }
-        }
-        else
-        {
-            lambda_[i] = 1.0;
-        }
-    }
-}
-
-void VRescaleThermostat::writeCheckpoint(t_state* localState, t_state gmx_unused* globalState)
-{
-    localState->therm_integral = thermostatIntegral_;
-    localState->flags |= (1U << estTHERM_INT);
-}
-
-const std::vector<double>& VRescaleThermostat::thermostatIntegral() const
-{
-    return thermostatIntegral_;
-}
-
-} // namespace gmx
diff --git a/src/gromacs/modularsimulator/vrescalethermostat.h b/src/gromacs/modularsimulator/vrescalethermostat.h
deleted file mode 100644 (file)
index 7ae9be1..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-/*! \libinternal \file
- * \brief Declares the v-rescale thermostat for the modular simulator
- *
- * \author Pascal Merz <pascal.merz@me.com>
- * \ingroup module_modularsimulator
- */
-
-#ifndef GMX_MODULARSIMULATOR_VRESCALETHERMOSTAT_H
-#define GMX_MODULARSIMULATOR_VRESCALETHERMOSTAT_H
-
-#include "gromacs/utility/arrayref.h"
-
-#include "energyelement.h"
-#include "modularsimulatorinterfaces.h"
-#include "propagator.h"
-
-struct t_commrec;
-
-namespace gmx
-{
-
-/*! \libinternal
- * \ingroup module_modularsimulator
- * \brief Element implementing the v-rescale thermostat
- *
- * This element takes a callback to the propagator and updates the velocity
- * scaling factor according to the v-rescale thermostat.
- */
-class VRescaleThermostat final : public ISimulatorElement, public ICheckpointHelperClient
-{
-public:
-    //! Constructor
-    VRescaleThermostat(int                   nstcouple,
-                       int                   offset,
-                       bool                  useFullStepKE,
-                       int64_t               seed,
-                       int                   numTemperatureGroups,
-                       double                couplingTimeStep,
-                       const real*           referenceTemperature,
-                       const real*           couplingTime,
-                       const real*           numDegreesOfFreedom,
-                       EnergyElement*        energyElement,
-                       ArrayRef<real>        lambdaView,
-                       PropagatorCallbackPtr propagatorCallback,
-                       const t_state*        globalState,
-                       t_commrec*            cr,
-                       bool                  isRestart);
-
-    /*! \brief Register run function for step / time
-     *
-     * @param step                 The step number
-     * @param time                 The time
-     * @param registerRunFunction  Function allowing to register a run function
-     */
-    void scheduleTask(Step step, Time time, const RegisterRunFunctionPtr& registerRunFunction) override;
-
-    //! No element setup needed
-    void elementSetup() override {}
-    //! No element teardown needed
-    void elementTeardown() override {}
-
-    //! Getter for the thermostatIntegral
-    const std::vector<double>& thermostatIntegral() const;
-
-private:
-    //! The frequency at which the thermostat is applied
-    const int nstcouple_;
-    //! If != 0, offset the step at which the thermostat is applied
-    const int offset_;
-    //! Whether we're using full step kinetic energy
-    const bool useFullStepKE_;
-    //! The random seed
-    const int64_t seed_;
-
-    //! The number of temperature groups
-    const int numTemperatureGroups_;
-    //! The coupling time step - simulation time step x nstcouple_
-    const double couplingTimeStep_;
-    //! Coupling temperature per group
-    const std::vector<real> referenceTemperature_;
-    //! Coupling time per group
-    const std::vector<real> couplingTime_;
-    //! Number of degrees of freedom per group
-    const std::vector<real> numDegreesOfFreedom_;
-    //! Work exerted by thermostat
-    std::vector<double> thermostatIntegral_;
-
-    //! Pointer to the energy element (for ekindata)
-    EnergyElement* energyElement_;
-
-    //! View on the scaling factor of the propagator
-    ArrayRef<real> lambda_;
-    //! Callback to let propagator know that we updated lambda
-    PropagatorCallbackPtr propagatorCallback_;
-
-    //! Set new lambda value (at T-coupling steps)
-    void setLambda(Step step);
-
-    //! ICheckpointHelperClient implementation
-    void writeCheckpoint(t_state* localState, t_state* globalState) override;
-};
-
-} // namespace gmx
-
-#endif // GMX_MODULARSIMULATOR_VRESCALETHERMOSTAT_H
index 087c2696a4af2541594d537c23eaae5f9ccb2176..c576b8558d34a2831f7876d08ff661671b25e9a8 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-file(GLOB NBNXM_SOURCES kernels_reference/*.cpp kernels_simd_4xm/*.cpp kernels_simd_2xmm/*.cpp *.cpp benchmark/*.cpp)
+add_subdirectory(kernels_simd_4xm)
+add_subdirectory(kernels_simd_2xmm)
 
-if(GMX_USE_CUDA)
+file (GLOB NBNXM_SOURCES
+    # Source files
+    atomdata.cpp
+    grid.cpp
+    gridset.cpp
+    kernel_common.cpp
+    kerneldispatch.cpp
+    nbnxm.cpp
+    nbnxm_geometry.cpp
+    nbnxm_setup.cpp
+    pairlist.cpp
+    pairlistparams.cpp
+    pairlistset.cpp
+    pairlist_tuning.cpp
+    pairsearch.cpp
+    prunekerneldispatch.cpp
+    # Reference kernel source files
+    kernels_reference/kernel_gpu_ref.cpp
+    kernels_reference/kernel_ref.cpp
+    kernels_reference/kernel_ref_prune.cpp
+    # Benchmark source files
+    # TODO these should not be in libgromacs
+    benchmark/bench_setup.cpp
+    benchmark/bench_system.cpp
+    )
+
+if(GMX_GPU_CUDA)
     add_subdirectory(cuda)
+    gmx_add_libgromacs_sources(nbnxm_gpu_data_mgmt.cpp)
+    _gmx_add_files_to_property(CUDA_SOURCES
+        nbnxm_gpu_data_mgmt.cpp
+       )
 endif()
 
-if(GMX_USE_OPENCL)
+if(GMX_GPU_OPENCL)
     add_subdirectory(opencl)
     set(NBNXM_OPENCL_KERNELS ${NBNXM_OPENCL_KERNELS} PARENT_SCOPE)
+    gmx_add_libgromacs_sources(nbnxm_gpu_data_mgmt.cpp)
 endif()
 
 set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${NBNXM_SOURCES} PARENT_SCOPE)
index c4a15ba97481dd5a79421b220172412c47bd296e..dd4eddd0ca38ca1c9c3ebd903256047cc9145b2b 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2012-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,7 +52,6 @@
 #include "gromacs/math/vec.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdtypes/forcerec.h" // only for GET_CGINFO_*
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/nbnxm/nbnxm.h"
 #include "gromacs/pbcutil/ishift.h"
 #include "gromacs/simd/simd.h"
@@ -67,6 +66,7 @@
 #include "grid.h"
 #include "gridset.h"
 #include "nbnxm_geometry.h"
+#include "nbnxm_gpu.h"
 #include "pairlist.h"
 
 using namespace gmx; // TODO: Remove when this file is moved into gmx namespace
@@ -442,7 +442,7 @@ static void nbnxn_atomdata_params_init(const gmx::MDLogger&      mdlog,
                                        const Nbnxm::KernelType   kernelType,
                                        int                       enbnxninitcombrule,
                                        int                       ntype,
-                                       const real*               nbfp,
+                                       ArrayRef<const real>      nbfp,
                                        int                       n_energygroups)
 {
     real     c6, c12, tol;
@@ -625,7 +625,7 @@ void nbnxn_atomdata_init(const gmx::MDLogger&    mdlog,
                          const Nbnxm::KernelType kernelType,
                          int                     enbnxninitcombrule,
                          int                     ntype,
-                         const real*             nbfp,
+                         ArrayRef<const real>    nbfp,
                          int                     n_energygroups,
                          int                     nout)
 {
@@ -675,8 +675,7 @@ void nbnxn_atomdata_init(const gmx::MDLogger&    mdlog,
                                pinningPolicy);
     }
 
-    nbat->buffer_flags.flag        = nullptr;
-    nbat->buffer_flags.flag_nalloc = 0;
+    nbat->buffer_flags.clear();
 
     const int nth = gmx_omp_nthreads_get(emntNonbonded);
 
@@ -723,7 +722,7 @@ static void copy_lj_to_nbat_lj_comb(gmx::ArrayRef<const real> ljparam_type, cons
 /* Sets the atom type in nbnxn_atomdata_t */
 static void nbnxn_atomdata_set_atomtypes(nbnxn_atomdata_t::Params* params,
                                          const Nbnxm::GridSet&     gridSet,
-                                         const int*                type)
+                                         ArrayRef<const int>       atomTypes)
 {
     params->type.resize(gridSet.numGridAtomsTotal());
 
@@ -735,8 +734,9 @@ static void nbnxn_atomdata_set_atomtypes(nbnxn_atomdata_t::Params* params,
             const int numAtoms   = grid.paddedNumAtomsInColumn(i);
             const int atomOffset = grid.firstAtomInColumn(i);
 
-            copy_int_to_nbat_int(gridSet.atomIndices().data() + atomOffset, grid.numAtomsInColumn(i),
-                                 numAtoms, type, params->numTypes - 1, params->type.data() + atomOffset);
+            copy_int_to_nbat_int(gridSet.atomIndices().data() + atomOffset,
+                                 grid.numAtomsInColumn(i), numAtoms, atomTypes.data(),
+                                 params->numTypes - 1, params->type.data() + atomOffset);
         }
     }
 }
@@ -781,7 +781,9 @@ static void nbnxn_atomdata_set_ljcombparams(nbnxn_atomdata_t::Params* params,
 }
 
 /* Sets the charges in nbnxn_atomdata_t *nbat */
-static void nbnxn_atomdata_set_charges(nbnxn_atomdata_t* nbat, const Nbnxm::GridSet& gridSet, const real* charge)
+static void nbnxn_atomdata_set_charges(nbnxn_atomdata_t*     nbat,
+                                       const Nbnxm::GridSet& gridSet,
+                                       ArrayRef<const real>  charges)
 {
     if (nbat->XFormat != nbatXYZQ)
     {
@@ -803,7 +805,7 @@ static void nbnxn_atomdata_set_charges(nbnxn_atomdata_t* nbat, const Nbnxm::Grid
                 int   i;
                 for (i = 0; i < numAtoms; i++)
                 {
-                    *q = charge[gridSet.atomIndices()[atomOffset + i]];
+                    *q = charges[gridSet.atomIndices()[atomOffset + i]];
                     q += STRIDE_XYZQ;
                 }
                 /* Complete the partially filled last cell with zeros */
@@ -819,7 +821,7 @@ static void nbnxn_atomdata_set_charges(nbnxn_atomdata_t* nbat, const Nbnxm::Grid
                 int   i;
                 for (i = 0; i < numAtoms; i++)
                 {
-                    *q = charge[gridSet.atomIndices()[atomOffset + i]];
+                    *q = charges[gridSet.atomIndices()[atomOffset + i]];
                     q++;
                 }
                 /* Complete the partially filled last cell with zeros */
@@ -925,7 +927,7 @@ copy_egp_to_nbat_egps(const int* a, int na, int na_round, int na_c, int bit_shif
 /* Set the energy group indices for atoms in nbnxn_atomdata_t */
 static void nbnxn_atomdata_set_energygroups(nbnxn_atomdata_t::Params* params,
                                             const Nbnxm::GridSet&     gridSet,
-                                            const int*                atinfo)
+                                            ArrayRef<const int>       atomInfo)
 {
     if (params->nenergrp == 1)
     {
@@ -943,7 +945,7 @@ static void nbnxn_atomdata_set_energygroups(nbnxn_atomdata_t::Params* params,
             const int atomOffset = grid.firstAtomInColumn(i);
 
             copy_egp_to_nbat_egps(gridSet.atomIndices().data() + atomOffset, grid.numAtomsInColumn(i),
-                                  numAtoms, c_nbnxnCpuIClusterSize, params->neg_2log, atinfo,
+                                  numAtoms, c_nbnxnCpuIClusterSize, params->neg_2log, atomInfo.data(),
                                   params->energrp.data() + grid.atomToCluster(atomOffset));
         }
     }
@@ -952,14 +954,15 @@ static void nbnxn_atomdata_set_energygroups(nbnxn_atomdata_t::Params* params,
 /* Sets all required atom parameter data in nbnxn_atomdata_t */
 void nbnxn_atomdata_set(nbnxn_atomdata_t*     nbat,
                         const Nbnxm::GridSet& gridSet,
-                        const t_mdatoms*      mdatoms,
-                        const int*            atinfo)
+                        ArrayRef<const int>   atomTypes,
+                        ArrayRef<const real>  atomCharges,
+                        ArrayRef<const int>   atomInfo)
 {
     nbnxn_atomdata_t::Params& params = nbat->paramsDeprecated();
 
-    nbnxn_atomdata_set_atomtypes(&params, gridSet, mdatoms->typeA);
+    nbnxn_atomdata_set_atomtypes(&params, gridSet, atomTypes);
 
-    nbnxn_atomdata_set_charges(nbat, gridSet, mdatoms->chargeA);
+    nbnxn_atomdata_set_charges(nbat, gridSet, atomCharges);
 
     if (gridSet.haveFep())
     {
@@ -969,7 +972,7 @@ void nbnxn_atomdata_set(nbnxn_atomdata_t*     nbat,
     /* This must be done after masking types for FEP */
     nbnxn_atomdata_set_ljcombparams(&params, nbat->XFormat, gridSet);
 
-    nbnxn_atomdata_set_energygroups(&params, gridSet, atinfo);
+    nbnxn_atomdata_set_energygroups(&params, gridSet, atomInfo);
 }
 
 /* Copies the shift vector array to nbnxn_atomdata_t */
@@ -1073,8 +1076,8 @@ void nbnxn_atomdata_copy_x_to_nbat_x(const Nbnxm::GridSet&   gridSet,
 void nbnxn_atomdata_x_to_nbat_x_gpu(const Nbnxm::GridSet&   gridSet,
                                     const gmx::AtomLocality locality,
                                     bool                    fillLocal,
-                                    gmx_nbnxn_gpu_t*        gpu_nbv,
-                                    DeviceBuffer<float>     d_x,
+                                    NbnxmGpu*               gpu_nbv,
+                                    DeviceBuffer<RVec>      d_x,
                                     GpuEventSynchronizer*   xReadyOnDevice)
 {
 
@@ -1232,7 +1235,7 @@ static inline unsigned char reverse_bits(unsigned char b)
 
 static void nbnxn_atomdata_add_nbat_f_to_f_treereduce(nbnxn_atomdata_t* nbat, int nth)
 {
-    const nbnxn_buffer_flags_t* flags = &nbat->buffer_flags;
+    gmx::ArrayRef<const gmx_bitmask_t> flags = nbat->buffer_flags;
 
     int next_pow2 = 1 << (gmx::log2I(nth - 1) + 1);
 
@@ -1263,7 +1266,14 @@ static void nbnxn_atomdata_add_nbat_f_to_f_treereduce(nbnxn_atomdata_t* nbat, in
                     /* wait on partner thread - replaces full barrier */
                     int sync_th, sync_group_size;
 
-                    tMPI_Atomic_memory_barrier(); /* gurantee data is saved before marking work as done */
+#    if defined(__clang__) && __clang_major__ >= 8
+                    // Suppress warnings that the use of memory_barrier may be excessive
+                    // Only exists beginning with clang-8
+#        pragma clang diagnostic push
+#        pragma clang diagnostic ignored "-Watomic-implicit-seq-cst"
+#    endif
+
+                    tMPI_Atomic_memory_barrier(); /* guarantee data is saved before marking work as done */
                     tMPI_Atomic_set(&(nbat->syncStep[th]), group_size / 2); /* mark previous step as completed */
 
                     /* find thread to sync with. Equal to partner_th unless nth is not a power of two. */
@@ -1283,6 +1293,9 @@ static void nbnxn_atomdata_add_nbat_f_to_f_treereduce(nbnxn_atomdata_t* nbat, in
                         /* guarantee that no later load happens before wait loop is finisehd */
                         tMPI_Atomic_memory_barrier();
                     }
+#    if defined(__clang__) && __clang_major__ >= 8
+#        pragma clang diagnostic pop
+#    endif
 #else /* TMPI_ATOMICS */
 #    pragma omp barrier
 #endif
@@ -1326,15 +1339,15 @@ static void nbnxn_atomdata_add_nbat_f_to_f_treereduce(nbnxn_atomdata_t* nbat, in
                     }
 
                     /* Calculate the cell-block range for our thread */
-                    b0 = (flags->nflag * group_pos) / group_size;
-                    b1 = (flags->nflag * (group_pos + 1)) / group_size;
+                    b0 = (flags.size() * group_pos) / group_size;
+                    b1 = (flags.size() * (group_pos + 1)) / group_size;
 
                     for (b = b0; b < b1; b++)
                     {
                         i0 = b * NBNXN_BUFFERFLAG_SIZE * nbat->fstride;
                         i1 = (b + 1) * NBNXN_BUFFERFLAG_SIZE * nbat->fstride;
 
-                        if (bitmask_is_set(flags->flag[b], index[1]) || group_size > 2)
+                        if (bitmask_is_set(flags[b], index[1]) || group_size > 2)
                         {
                             const real* fIndex1 = nbat->out[index[1]].f.data();
 #if GMX_SIMD
@@ -1343,10 +1356,10 @@ static void nbnxn_atomdata_add_nbat_f_to_f_treereduce(nbnxn_atomdata_t* nbat, in
                             nbnxn_atomdata_reduce_reals
 #endif
                                     (nbat->out[index[0]].f.data(),
-                                     bitmask_is_set(flags->flag[b], index[0]) || group_size > 2,
-                                     &fIndex1, 1, i0, i1);
+                                     bitmask_is_set(flags[b], index[0]) || group_size > 2, &fIndex1,
+                                     1, i0, i1);
                         }
-                        else if (!bitmask_is_set(flags->flag[b], index[0]))
+                        else if (!bitmask_is_set(flags[b], index[0]))
                         {
                             nbnxn_atomdata_clear_reals(nbat->out[index[0]].f, i0, i1);
                         }
@@ -1366,15 +1379,14 @@ static void nbnxn_atomdata_add_nbat_f_to_f_stdreduce(nbnxn_atomdata_t* nbat, int
     {
         try
         {
-            const nbnxn_buffer_flags_t* flags;
-            int                         nfptr;
-            const real*                 fptr[NBNXN_BUFFERFLAG_MAX_THREADS];
+            int         nfptr;
+            const real* fptr[NBNXN_BUFFERFLAG_MAX_THREADS];
 
-            flags = &nbat->buffer_flags;
+            gmx::ArrayRef<const gmx_bitmask_t> flags = nbat->buffer_flags;
 
             /* Calculate the cell-block range for our thread */
-            int b0 = (flags->nflag * th) / nth;
-            int b1 = (flags->nflag * (th + 1)) / nth;
+            int b0 = (flags.size() * th) / nth;
+            int b1 = (flags.size() * (th + 1)) / nth;
 
             for (int b = b0; b < b1; b++)
             {
@@ -1384,7 +1396,7 @@ static void nbnxn_atomdata_add_nbat_f_to_f_stdreduce(nbnxn_atomdata_t* nbat, int
                 nfptr = 0;
                 for (gmx::index out = 1; out < gmx::ssize(nbat->out); out++)
                 {
-                    if (bitmask_is_set(flags->flag[b], out))
+                    if (bitmask_is_set(flags[b], out))
                     {
                         fptr[nfptr++] = nbat->out[out].f.data();
                     }
@@ -1396,9 +1408,9 @@ static void nbnxn_atomdata_add_nbat_f_to_f_stdreduce(nbnxn_atomdata_t* nbat, int
 #else
                     nbnxn_atomdata_reduce_reals
 #endif
-                            (nbat->out[0].f.data(), bitmask_is_set(flags->flag[b], 0), fptr, nfptr, i0, i1);
+                            (nbat->out[0].f.data(), bitmask_is_set(flags[b], 0), fptr, nfptr, i0, i1);
                 }
-                else if (!bitmask_is_set(flags->flag[b], 0))
+                else if (!bitmask_is_set(flags[b], 0))
                 {
                     nbnxn_atomdata_clear_reals(nbat->out[0].f, i0, i1);
                 }
@@ -1456,31 +1468,6 @@ void reduceForces(nbnxn_atomdata_t* nbat, const gmx::AtomLocality locality, cons
     }
 }
 
-/* Add the force array(s) from nbnxn_atomdata_t to f */
-void reduceForcesGpu(const gmx::AtomLocality                    locality,
-                     DeviceBuffer<float>                        totalForcesDevice,
-                     const Nbnxm::GridSet&                      gridSet,
-                     void*                                      pmeForcesDevice,
-                     gmx::ArrayRef<GpuEventSynchronizer* const> dependencyList,
-                     gmx_nbnxn_gpu_t*                           gpu_nbv,
-                     bool                                       useGpuFPmeReduction,
-                     bool                                       accumulateForce)
-{
-    int atomsStart = 0;
-    int numAtoms   = 0;
-
-    nbnxn_get_atom_range(locality, gridSet, &atomsStart, &numAtoms);
-
-    if (numAtoms == 0)
-    {
-        /* The are no atoms for this reduction, avoid some overhead */
-        return;
-    }
-
-    Nbnxm::nbnxn_gpu_add_nbat_f_to_f(locality, totalForcesDevice, gpu_nbv, pmeForcesDevice, dependencyList,
-                                     atomsStart, numAtoms, useGpuFPmeReduction, accumulateForce);
-}
-
 void nbnxn_atomdata_add_nbat_fshift_to_fshift(const nbnxn_atomdata_t& nbat, gmx::ArrayRef<gmx::RVec> fshift)
 {
     gmx::ArrayRef<const nbnxn_atomdata_output_t> outputBuffers = nbat.out;
index 1b1e3b763a17f9c23b09007c27a5233e835b6750..df3362b11489ed1de4dcef1a53880ea8b32a3d6c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+/*! \libinternal \file
+ *  \brief
+ *  Functionality for per-atom data in the nbnxm module
+ *
+ *  \author Berk Hess <hess@kth.se>
+ *  \ingroup module_nbnxm
+ *  \inlibraryapi
+ */
+
 
 #ifndef GMX_NBNXN_ATOMDATA_H
 #define GMX_NBNXN_ATOMDATA_H
 #include "gromacs/utility/bitmask.h"
 #include "gromacs/utility/real.h"
 
-#include "gpu_types.h"
-
 namespace gmx
 {
 class MDLogger;
 }
 
+struct NbnxmGpu;
 struct nbnxn_atomdata_t;
 struct nonbonded_verlet_t;
-struct t_mdatoms;
 struct tMPI_Atomic;
 
 class GpuEventSynchronizer;
@@ -66,7 +74,7 @@ class GridSet;
 enum class KernelType;
 } // namespace Nbnxm
 
-/* Convenience type for vector with aligned memory */
+//! Convenience type for vector with aligned memory
 template<typename T>
 using AlignedVector = std::vector<T, gmx::AlignedAllocator<T>>;
 
@@ -98,10 +106,11 @@ static inline int atom_to_x_index(int a)
     return DIM * (a & ~(packSize - 1)) + (a & (packSize - 1));
 }
 
-// Struct that holds force and energy output buffers
+/*! \internal
+ * \brief Struct that holds force and energy output buffers */
 struct nbnxn_atomdata_output_t
 {
-    /* Constructor
+    /*! \brief Constructor
      *
      * \param[in] kernelType              Type of non-bonded kernel
      * \param[in] numEnergyGroups         The number of energy groups
@@ -110,19 +119,26 @@ struct nbnxn_atomdata_output_t
      */
     nbnxn_atomdata_output_t(Nbnxm::KernelType  kernelType,
                             int                numEnergyGroups,
-                            int                simdEnergyBUfferStride,
+                            int                simdEnergyBufferStride,
                             gmx::PinningPolicy pinningPolicy);
 
-    gmx::HostVector<real> f;      // f, size natoms*fstride
-    gmx::HostVector<real> fshift; // Shift force array, size SHIFTS*DIM
-    gmx::HostVector<real> Vvdw;   // Temporary Van der Waals group energy storage
-    gmx::HostVector<real> Vc;     // Temporary Coulomb group energy storage
-    AlignedVector<real>   VSvdw;  // Temporary SIMD Van der Waals group energy storage
-    AlignedVector<real>   VSc;    // Temporary SIMD Coulomb group energy storage
+    //! f, size natoms*fstride
+    gmx::HostVector<real> f;
+    //! Shift force array, size SHIFTS*DIM
+    gmx::HostVector<real> fshift;
+    //! Temporary Van der Waals group energy storage
+    gmx::HostVector<real> Vvdw;
+    //! Temporary Coulomb group energy storage
+    gmx::HostVector<real> Vc;
+    //! Temporary SIMD Van der Waals group energy storage
+    AlignedVector<real> VSvdw;
+    //! Temporary SIMD Coulomb group energy storage
+    AlignedVector<real> VSc;
 };
 
-/* Block size in atoms for the non-bonded thread force-buffer reduction,
- * should be a multiple of all cell and x86 SIMD sizes (i.e. 2, 4 and 8).
+/*! \brief Block size in atoms for the non-bonded thread force-buffer reduction.
+ *
+ * Should be a multiple of all cell and x86 SIMD sizes (i.e. 2, 4 and 8).
  * Should be small to reduce the reduction and zeroing cost,
  * but too small will result in overhead.
  * Currently the block size is NBNXN_BUFFERFLAG_SIZE*3*sizeof(real)=192 bytes.
@@ -133,20 +149,13 @@ struct nbnxn_atomdata_output_t
 #    define NBNXN_BUFFERFLAG_SIZE 16
 #endif
 
-/* We store the reduction flags as gmx_bitmask_t.
+/*! \brief We store the reduction flags as gmx_bitmask_t.
  * This limits the number of flags to BITMASK_SIZE.
  */
 #define NBNXN_BUFFERFLAG_MAX_THREADS (BITMASK_SIZE)
 
-/* Flags for telling if threads write to force output buffers */
-typedef struct
-{
-    int            nflag;       /* The number of flag blocks                         */
-    gmx_bitmask_t* flag;        /* Bit i is set when thread i writes to a cell-block */
-    int            flag_nalloc; /* Allocation size of cxy_flag                       */
-} nbnxn_buffer_flags_t;
 
-/* LJ combination rules: geometric, Lorentz-Berthelot, none */
+/*! \brief LJ combination rules: geometric, Lorentz-Berthelot, none */
 enum
 {
     ljcrGEOM,
@@ -155,125 +164,145 @@ enum
     ljcrNR
 };
 
-/* Struct that stores atom related data for the nbnxn module
+/*! \internal
+ * \brief Struct that stores atom related data for the nbnxn module
  *
  * Note: performance would improve slightly when all std::vector containers
  *       in this struct would not initialize during resize().
  */
 struct nbnxn_atomdata_t
 { //NOLINT(clang-analyzer-optin.performance.Padding)
+    /*! \internal
+     * \brief The actual atom data parameter values */
     struct Params
     {
-        /* Constructor
+        /*! \brief Constructor
          *
          * \param[in] pinningPolicy  Sets the pinning policy for all data that might be transfered to a GPU
          */
         Params(gmx::PinningPolicy pinningPolicy);
 
-        // The number of different atom types
+        //! The number of different atom types
         int numTypes;
-        // Lennard-Jone 6*C6 and 12*C12 parameters, size numTypes*2*2
+        //! Lennard-Jone 6*C6 and 12*C12 parameters, size numTypes*2*2
         gmx::HostVector<real> nbfp;
-        // Combination rule, see enum defined above
+        //! Combination rule, see enum defined above
         int comb_rule;
-        // LJ parameters per atom type, size numTypes*2
+        //! LJ parameters per atom type, size numTypes*2
         gmx::HostVector<real> nbfp_comb;
-        // As nbfp, but with a stride for the present SIMD architecture
+        //! As nbfp, but with a stride for the present SIMD architecture
         AlignedVector<real> nbfp_aligned;
-        // Atom types per atom
+        //! Atom types per atom
         gmx::HostVector<int> type;
-        // LJ parameters per atom for fast SIMD loading
+        //! LJ parameters per atom for fast SIMD loading
         gmx::HostVector<real> lj_comb;
-        // Charges per atom, not set with format nbatXYZQ
+        //! Charges per atom, not set with format nbatXYZQ
         gmx::HostVector<real> q;
-        // The number of energy groups
+        //! The number of energy groups
         int nenergrp;
-        // 2log(nenergrp)
+        //! 2log(nenergrp)
         int neg_2log;
-        // The energy groups, one int entry per cluster, only set when needed
+        //! The energy groups, one int entry per cluster, only set when needed
         gmx::HostVector<int> energrp;
     };
 
-    // Diagonal and topology exclusion helper data for all SIMD kernels
+    /*! \internal
+     * \brief Diagonal and topology exclusion helper data for all SIMD kernels. */
     struct SimdMasks
     {
         SimdMasks();
 
-        // Helper data for setting up diagonal exclusion masks in the SIMD 4xN kernels
+        //! Helper data for setting up diagonal exclusion masks in the SIMD 4xN kernels
         AlignedVector<real> diagonal_4xn_j_minus_i;
-        // Helper data for setting up diaginal exclusion masks in the SIMD 2xNN kernels
+        //! Helper data for setting up diaginal exclusion masks in the SIMD 2xNN kernels
         AlignedVector<real> diagonal_2xnn_j_minus_i;
-        // Filters for topology exclusion masks for the SIMD kernels
+        //! Filters for topology exclusion masks for the SIMD kernels
         AlignedVector<uint32_t> exclusion_filter;
-        // Filters for topology exclusion masks for double SIMD kernels without SIMD int32 logical support
+        //! Filters for topology exclusion masks for double SIMD kernels without SIMD int32 logical support
         AlignedVector<uint64_t> exclusion_filter64;
-        // Array of masks needed for exclusions
+        //! Array of masks needed for exclusions
         AlignedVector<real> interaction_array;
     };
 
-    /* Constructor
+    /*! \brief Constructor
      *
      * \param[in] pinningPolicy  Sets the pinning policy for all data that might be transfered to a GPU
      */
     nbnxn_atomdata_t(gmx::PinningPolicy pinningPolicy);
 
-    /* Returns a const reference to the parameters */
+    //! Returns a const reference to the parameters
     const Params& params() const { return params_; }
 
-    /* Returns a non-const reference to the parameters */
+    //! Returns a non-const reference to the parameters
     Params& paramsDeprecated() { return params_; }
 
-    /* Returns the current total number of atoms stored */
+    //! Returns the current total number of atoms stored
     int numAtoms() const { return numAtoms_; }
 
-    /* Return the coordinate buffer, and q with xFormat==nbatXYZQ */
+    //! Return the coordinate buffer, and q with xFormat==nbatXYZQ
     gmx::ArrayRef<const real> x() const { return x_; }
 
-    /* Return the coordinate buffer, and q with xFormat==nbatXYZQ */
+    //! Return the coordinate buffer, and q with xFormat==nbatXYZQ
     gmx::ArrayRef<real> x() { return x_; }
 
-    /* Resizes the coordinate buffer and sets the number of atoms */
+    //! Resizes the coordinate buffer and sets the number of atoms
     void resizeCoordinateBuffer(int numAtoms);
 
-    /* Resizes the force buffers for the current number of atoms */
+    //! Resizes the force buffers for the current number of atoms
     void resizeForceBuffers();
 
 private:
-    // The LJ and charge parameters
+    //! The LJ and charge parameters
     Params params_;
-    // The total number of atoms currently stored
+    //! The total number of atoms currently stored
     int numAtoms_;
 
 public:
-    int                        natoms_local; /* Number of local atoms                           */
-    int                        XFormat;     /* The format of x (and q), enum                      */
-    int                        FFormat;     /* The format of f, enum                              */
-    gmx_bool                   bDynamicBox; /* Do we need to update shift_vec every step?    */
-    gmx::HostVector<gmx::RVec> shift_vec;   /* Shift vectors, copied from t_forcerec              */
-    int                        xstride;     /* stride for a coordinate in x (usually 3 or 4)      */
-    int                        fstride;     /* stride for a coordinate in f (usually 3 or 4)      */
+    //! Number of local atoms
+    int natoms_local;
+    //! The format of x (and q), enum
+    int XFormat;
+    //! The format of f, enum
+    int FFormat;
+    //! Do we need to update shift_vec every step?
+    gmx_bool bDynamicBox;
+    //! Shift vectors, copied from t_forcerec
+    gmx::HostVector<gmx::RVec> shift_vec;
+    //! stride for a coordinate in x (usually 3 or 4)
+    int xstride;
+    //! stride for a coordinate in f (usually 3 or 4)
+    int fstride;
+
 private:
-    gmx::HostVector<real> x_; /* x and possibly q, size natoms*xstride              */
+    //! x and possibly q, size natoms*xstride
+    gmx::HostVector<real> x_;
 
 public:
-    // Masks for handling exclusions in the SIMD kernels
+    //! Masks for handling exclusions in the SIMD kernels
     const SimdMasks simdMasks;
 
-    /* Output data */
-    std::vector<nbnxn_atomdata_output_t> out; /* Output data structures, 1 per thread */
-
-    /* Reduction related data */
-    gmx_bool             bUseBufferFlags; /* Use the flags or operate on all atoms     */
-    nbnxn_buffer_flags_t buffer_flags;    /* Flags for buffer zeroing+reduc.  */
-    gmx_bool             bUseTreeReduce;  /* Use tree for force reduction */
-    tMPI_Atomic*         syncStep;        /* Synchronization step for tree reduce */
+    //! Output data structures, 1 per thread
+    std::vector<nbnxn_atomdata_output_t> out;
+
+    //! Reduction related data
+    //! \{
+    //! Use the flags or operate on all atoms
+    gmx_bool bUseBufferFlags;
+    //! Flags for buffer zeroing+reduc.
+    std::vector<gmx_bitmask_t> buffer_flags;
+    //! Use tree for force reduction
+    gmx_bool bUseTreeReduce;
+    //! Synchronization step for tree reduce
+    tMPI_Atomic* syncStep;
+    //! \}
 };
 
-/* Copy na rvec elements from x to xnb using nbatFormat, start dest a0,
+/*! \brief Copy na rvec elements from x to xnb using nbatFormat, start dest a0,
  * and fills up to na_round with coordinates that are far away.
  */
 void copy_rvec_to_nbat_real(const int* a, int na, int na_round, const rvec* x, int nbatFormat, real* xnb, int a0);
 
+//! Describes the combination rule in use by this force field
 enum
 {
     enbnxninitcombruleDETECT,
@@ -282,27 +311,30 @@ enum
     enbnxninitcombruleNONE
 };
 
-/* Initialize the non-bonded atom data structure.
+/*! \brief Initialize the non-bonded atom data structure.
+ *
  * The enum for nbatXFormat is in the file defining nbnxn_atomdata_t.
  * Copy the ntypes*ntypes*2 sized nbfp non-bonded parameter list
  * to the atom data structure.
  * enbnxninitcombrule sets what combination rule data gets stored in nbat.
  */
-void nbnxn_atomdata_init(const gmx::MDLogger& mdlog,
-                         nbnxn_atomdata_t*    nbat,
-                         Nbnxm::KernelType    kernelType,
-                         int                  enbnxninitcombrule,
-                         int                  ntype,
-                         const real*          nbfp,
-                         int                  n_energygroups,
-                         int                  nout);
-
-void nbnxn_atomdata_set(nbnxn_atomdata_t*     nbat,
-                        const Nbnxm::GridSet& gridSet,
-                        const t_mdatoms*      mdatoms,
-                        const int*            atinfo);
-
-/* Copy the shift vectors to nbat */
+void nbnxn_atomdata_init(const gmx::MDLogger&      mdlog,
+                         nbnxn_atomdata_t*         nbat,
+                         Nbnxm::KernelType         kernelType,
+                         int                       enbnxninitcombrule,
+                         int                       ntype,
+                         gmx::ArrayRef<const real> nbfp,
+                         int                       n_energygroups,
+                         int                       nout);
+
+//! Sets the atomdata after pair search
+void nbnxn_atomdata_set(nbnxn_atomdata_t*         nbat,
+                        const Nbnxm::GridSet&     gridSet,
+                        gmx::ArrayRef<const int>  atomTypes,
+                        gmx::ArrayRef<const real> atomCharges,
+                        gmx::ArrayRef<const int>  atomInfo);
+
+//! Copy the shift vectors to nbat
 void nbnxn_atomdata_copy_shiftvec(gmx_bool dynamic_box, rvec* shift_vec, nbnxn_atomdata_t* nbat);
 
 /*! \brief Transform coordinates to xbat layout
@@ -333,12 +365,12 @@ void nbnxn_atomdata_copy_x_to_nbat_x(const Nbnxm::GridSet& gridSet,
  * \param[in]     d_x        Coordinates to be copied (in plain rvec format).
  * \param[in]     xReadyOnDevice   Event synchronizer indicating that the coordinates are ready in the device memory.
  */
-void nbnxn_atomdata_x_to_nbat_x_gpu(const Nbnxm::GridSet& gridSet,
-                                    gmx::AtomLocality     locality,
-                                    bool                  fillLocal,
-                                    gmx_nbnxn_gpu_t*      gpu_nbv,
-                                    DeviceBuffer<float>   d_x,
-                                    GpuEventSynchronizer* xReadyOnDevice);
+void nbnxn_atomdata_x_to_nbat_x_gpu(const Nbnxm::GridSet&   gridSet,
+                                    gmx::AtomLocality       locality,
+                                    bool                    fillLocal,
+                                    NbnxmGpu*               gpu_nbv,
+                                    DeviceBuffer<gmx::RVec> d_x,
+                                    GpuEventSynchronizer*   xReadyOnDevice);
 
 /*! \brief Add the computed forces to \p f, an internal reduction might be performed as well
  *
@@ -349,30 +381,10 @@ void nbnxn_atomdata_x_to_nbat_x_gpu(const Nbnxm::GridSet& gridSet,
  */
 void reduceForces(nbnxn_atomdata_t* nbat, gmx::AtomLocality locality, const Nbnxm::GridSet& gridSet, rvec* totalForce);
 
-/*! \brief Reduce forces on the GPU
- *
- * \param[in]  locality             If the reduction should be performed on local or non-local atoms.
- * \param[out] totalForcesDevice    Device buffer to accumulate resulting force.
- * \param[in]  gridSet              The grids data.
- * \param[in]  pmeForcesDevice      Device buffer with PME forces.
- * \param[in]  dependencyList       List of synchronizers that represent the dependencies the reduction task needs to sync on.
- * \param[in]  gpu_nbv              The NBNXM GPU data structure.
- * \param[in]  useGpuFPmeReduction  Whether PME forces should be added.
- * \param[in]  accumulateForce      Whether there are usefull data already in the total force buffer.
- */
-void reduceForcesGpu(gmx::AtomLocality                          locality,
-                     DeviceBuffer<float>                        totalForcesDevice,
-                     const Nbnxm::GridSet&                      gridSet,
-                     void*                                      pmeForcesDevice,
-                     gmx::ArrayRef<GpuEventSynchronizer* const> dependencyList,
-                     gmx_nbnxn_gpu_t*                           gpu_nbv,
-                     bool                                       useGpuFPmeReduction,
-                     bool                                       accumulateForce);
-
-/* Add the fshift force stored in nbat to fshift */
+//! Add the fshift force stored in nbat to fshift
 void nbnxn_atomdata_add_nbat_fshift_to_fshift(const nbnxn_atomdata_t& nbat, gmx::ArrayRef<gmx::RVec> fshift);
 
-/* Get the atom start index and number of atoms for a given locality */
+//! Get the atom start index and number of atoms for a given locality
 void nbnxn_get_atom_range(gmx::AtomLocality     atomLocality,
                           const Nbnxm::GridSet& gridSet,
                           int*                  atomStart,
index ecfe6af14de3ee1e349ed0f3d2d3a0679f376120..ee4427e9b255438108ffeec00ac835afecd1d395 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,7 +45,8 @@
 
 #include "bench_setup.h"
 
-#include "gromacs/compat/optional.h"
+#include <optional>
+
 #include "gromacs/gmxlib/nrnb.h"
 #include "gromacs/mdlib/dispersioncorrection.h"
 #include "gromacs/mdlib/force_flags.h"
@@ -80,7 +81,7 @@ namespace Nbnxm
  *
  * Returns an error string when the kernel is not available.
  */
-static gmx::compat::optional<std::string> checkKernelSetup(const KernelBenchOptions& options)
+static std::optional<std::string> checkKernelSetup(const KernelBenchOptions& options)
 {
     GMX_RELEASE_ASSERT(options.nbnxmSimd < BenchMarkKernels::Count
                                && options.nbnxmSimd != BenchMarkKernels::SimdAuto,
@@ -159,7 +160,7 @@ static interaction_const_t setupInteractionConst(const KernelBenchOptions& optio
         GMX_RELEASE_ASSERT(options.ewaldcoeff_q > 0, "Ewald coefficient should be > 0");
         ic.ewaldcoeff_q       = options.ewaldcoeff_q;
         ic.coulombEwaldTables = std::make_unique<EwaldCorrectionTables>();
-        init_interaction_const_tables(nullptr, &ic);
+        init_interaction_const_tables(nullptr, &ic, 0);
     }
 
     return ic;
@@ -184,13 +185,14 @@ static std::unique_ptr<nonbonded_verlet_t> setupNbnxmForBenchInstance(const Kern
 
     PairlistParams pairlistParams(kernelSetup.kernelType, false, options.pairlistCutoff, false);
 
-    GridSet gridSet(epbcXYZ, false, nullptr, nullptr, pairlistParams.pairlistType, false,
+    GridSet gridSet(PbcType::Xyz, false, nullptr, nullptr, pairlistParams.pairlistType, false,
                     numThreads, pinPolicy);
 
     auto pairlistSets = std::make_unique<PairlistSets>(pairlistParams, false, 0);
 
-    auto pairSearch = std::make_unique<PairSearch>(
-            epbcXYZ, false, nullptr, nullptr, pairlistParams.pairlistType, false, numThreads, pinPolicy);
+    auto pairSearch =
+            std::make_unique<PairSearch>(PbcType::Xyz, false, nullptr, nullptr,
+                                         pairlistParams.pairlistType, false, numThreads, pinPolicy);
 
     auto atomData = std::make_unique<nbnxn_atomdata_t>(pinPolicy);
 
@@ -199,7 +201,7 @@ static std::unique_ptr<nonbonded_verlet_t> setupNbnxmForBenchInstance(const Kern
                                                     std::move(atomData), kernelSetup, nullptr, nullptr);
 
     nbnxn_atomdata_init(gmx::MDLogger(), nbv->nbat.get(), kernelSetup.kernelType, combinationRule,
-                        system.numAtomTypes, system.nonbondedParameters.data(), 1, numThreads);
+                        system.numAtomTypes, system.nonbondedParameters, 1, numThreads);
 
     t_nrnb nrnb;
 
@@ -223,13 +225,9 @@ static std::unique_ptr<nonbonded_verlet_t> setupNbnxmForBenchInstance(const Kern
                       { 0, int(system.coordinates.size()) }, atomDensity, atomInfo,
                       system.coordinates, 0, nullptr);
 
-    nbv->constructPairlist(gmx::InteractionLocality::Local, &system.excls, 0, &nrnb);
+    nbv->constructPairlist(gmx::InteractionLocality::Local, system.excls, 0, &nrnb);
 
-    t_mdatoms mdatoms;
-    // We only use (read) the atom type and charge from mdatoms
-    mdatoms.typeA   = const_cast<int*>(system.atomTypes.data());
-    mdatoms.chargeA = const_cast<real*>(system.charges.data());
-    nbv->setAtomProperties(mdatoms, atomInfo);
+    nbv->setAtomProperties(system.atomTypes, system.charges, atomInfo);
 
     return nbv;
 }
@@ -377,10 +375,6 @@ void bench(const int sizeFactor, const KernelBenchOptions& options)
                 gmx::EnumerationWrapper<BenchMarkCombRule> combRuleIter;
                 for (auto combRule : combRuleIter)
                 {
-                    if (combRule == BenchMarkCombRule::RuleNone)
-                    {
-                        continue;
-                    }
                     opt.ljCombinationRule = combRule;
 
                     expandSimdOptionAndPushBack(opt, &optionsList);
index ffe0a899ff9b7e820f95ac5b1652561c421c447f..553f4cafd26df838b92570d1e4187bfe4bc5bc98 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,6 +45,7 @@
 
 #include "bench_system.h"
 
+#include <numeric>
 #include <vector>
 
 #include "gromacs/math/vec.h"
@@ -157,7 +158,7 @@ BenchmarkSystem::BenchmarkSystem(const int multiplicationFactor)
     nonbondedParameters[1] = c12Oxygen;
 
     generateCoordinates(multiplicationFactor, &coordinates, box);
-    put_atoms_in_box(epbcXYZ, box, coordinates);
+    put_atoms_in_box(PbcType::Xyz, box, coordinates);
 
     int numAtoms = coordinates.size();
     GMX_RELEASE_ASSERT(numAtoms % numAtomsInMolecule == 0,
@@ -167,9 +168,6 @@ BenchmarkSystem::BenchmarkSystem(const int multiplicationFactor)
     charges.resize(numAtoms);
     atomInfoAllVdw.resize(numAtoms);
     atomInfoOxygenVdw.resize(numAtoms);
-    snew(excls.index, numAtoms + 1);
-    snew(excls.a, numAtoms * numAtomsInMolecule);
-    excls.index[0] = 0;
 
     for (int a = 0; a < numAtoms; a++)
     {
@@ -191,16 +189,14 @@ BenchmarkSystem::BenchmarkSystem(const int multiplicationFactor)
         SET_CGINFO_HAS_Q(atomInfoAllVdw[a]);
         SET_CGINFO_HAS_Q(atomInfoOxygenVdw[a]);
 
-        const int firstAtomInMolecule = a - (a % numAtomsInMolecule);
-        for (int aj = 0; aj < numAtomsInMolecule; aj++)
-        {
-            excls.a[a * numAtomsInMolecule + aj] = firstAtomInMolecule + aj;
-        }
-        excls.index[a + 1] = (a + 1) * numAtomsInMolecule;
+        excls.pushBackListOfSize(numAtomsInMolecule);
+        gmx::ArrayRef<int> exclusionsForAtom   = excls.back();
+        const int          firstAtomInMolecule = a - (a % numAtomsInMolecule);
+        std::iota(exclusionsForAtom.begin(), exclusionsForAtom.end(), firstAtomInMolecule);
     }
 
     forceRec.ntype = numAtomTypes;
-    forceRec.nbfp  = nonbondedParameters.data();
+    forceRec.nbfp  = nonbondedParameters;
     snew(forceRec.shift_vec, SHIFTS);
     calc_shifts(box, forceRec.shift_vec);
 }
index 512368ddda507fe8937ab58d4e797a08d813553a..adcc85d4ffacc443b36b48dc8040e09ffa2fb1aa 100644 (file)
@@ -48,7 +48,7 @@
 
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdtypes/forcerec.h"
-#include "gromacs/topology/block.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/smalloc.h"
 
 namespace gmx
@@ -80,7 +80,7 @@ struct BenchmarkSystem
     //! Atom info where only oxygen atoms are marked to have Van der Waals interactions
     std::vector<int> atomInfoOxygenVdw;
     //! Information about exclusions.
-    t_blocka excls;
+    ListOfLists<int> excls;
     //! Storage for atom positions.
     std::vector<gmx::RVec> coordinates;
     //! System simulation box.
index 945fbc5961f62565781e9d1cef4178e8faf4d05f..986dde307cc6701088ec19b8d6836c8f77bb48cf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -33,7 +33,8 @@
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 
-/*! \internal \file
+/*! \file
+ * \internal
  *
  * \brief Declares constants and helper functions used when handling
  * bounding boxes for clusters of particles.
@@ -57,7 +58,7 @@ static constexpr int c_numBoundingBoxBounds1D = 2;
 
 #ifndef DOXYGEN
 
-/* Bounding box calculations are (currently) always in single precision, so
+/*! \brief Bounding box calculations are (currently) always in single precision, so
  * we only need to check for single precision support here.
  * This uses less (cache-)memory and SIMD is faster, at least on x86.
  */
index 43ce747dade7cdc2bd50b74770505ac938398cbd..c7f680ab0c76f32b82ba1c51c5c9af0bb6a77388 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_NBNXM_CLUSTERDISTANCEKERNELTYPE_H
 #define GMX_NBNXM_CLUSTERDISTANCEKERNELTYPE_H
 
+#include "gromacs/nbnxm/atomdata.h"
 #include "gromacs/simd/simd.h"
 #include "gromacs/utility/gmxassert.h"
 
-#include "atomdata.h"
 #include "pairlistparams.h"
 
 //! The types of kernel for calculating the distance between pairs of atom clusters
diff --git a/src/gromacs/nbnxm/constants.h b/src/gromacs/nbnxm/constants.h
deleted file mode 100644 (file)
index f38f1b1..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-
-#ifndef GMX_NBNXN_CONSTANTS_H
-#define GMX_NBNXN_CONSTANTS_H
-
-// Lower limit for square interaction distances in nonbonded kernels.
-// For smaller values we will overflow when calculating r^-1 or r^-12, but
-// to keep it simple we always apply the limit from the tougher r^-12 condition.
-#if GMX_DOUBLE
-// Some double precision SIMD architectures use single precision in the first
-// step, so although the double precision criterion would allow smaller rsq,
-// we need to stay in single precision with some margin for the N-R iterations.
-#    define NBNXN_MIN_RSQ 1.0e-36
-#else
-// The worst intermediate value we might evaluate is r^-12, which
-// means we should ensure r^2 stays above pow(GMX_FLOAT_MAX,-1.0/6.0)*1.01 (some margin)
-#    define NBNXN_MIN_RSQ 3.82e-07f // r > 6.2e-4
-#endif
-
-
-/* The number of clusters in a super-cluster, used for GPU */
-#define c_nbnxnGpuNumClusterPerSupercluster 8
-
-/* With GPU kernels we group cluster pairs in 4 to optimize memory usage
- * of integers containing 32 bits.
- */
-#define c_nbnxnGpuJgroupSize (32 / c_nbnxnGpuNumClusterPerSupercluster)
-
-#endif
index 73d1d7559095974e46eec76b928ce213b25ae19b..15c047605a625e164b3d502c541f831e8b82f2c9 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -32,7 +33,7 @@
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-if(GMX_USE_CUDA)
+if(GMX_GPU_CUDA)
     if(NOT GMX_CUDA_NB_SINGLE_COMPILATION_UNIT)
         set(NBNXM_CUDA_KERNEL_SOURCES
                 nbnxm_cuda_kernel_F_noprune.cu
index 53bd7541efddd79f6758ee27091f9dcf45374021..db3fb4a939ddcc5ae1704241fe9028bc61ef06d6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 /*! \brief CUDA kernel for transforming position coordinates from rvec to nbnxm layout.
  *
  * TODO:
- *  - improve/simplify/document use of cxy_na and na_round
  *  - rename kernel so naming matches with the other NBNXM kernels;
  *  - enable separate compilation unit
 
  * \param[in]     numColumns          Extent of cell-level parallelism.
  * \param[out]    gm_xq               Coordinates buffer in nbnxm layout.
- * \param[in]     setFillerCoords     Whether to set the coordinates of the filler particles.
+ * \tparam        setFillerCoords     Whether to set the coordinates of the filler particles.
  * \param[in]     gm_x                Coordinates buffer.
  * \param[in]     gm_atomIndex        Atom index mapping.
  * \param[in]     gm_numAtoms         Array of number of atoms.
  * \param[in]     gm_cellIndex        Array of cell indices.
- * \param[in]     cellOffset          Airst cell.
+ * \param[in]     cellOffset          First cell.
  * \param[in]     numAtomsPerCell     Number of atoms per cell.
  */
+template<bool setFillerCoords>
 static __global__ void nbnxn_gpu_x_to_nbat_x_kernel(int numColumns,
                                                     float4* __restrict__ gm_xq,
-                                                    bool setFillerCoords,
                                                     const float3* __restrict__ gm_x,
                                                     const int* __restrict__ gm_atomIndex,
                                                     const int* __restrict__ gm_numAtoms,
index acf3717d8eba6746aa389cc392cdd111f9a82b84..48f02fde9e55783bef5f8592247c5499d8c020b7 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "nbnxm_cuda.h"
 
-#include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
+#include "gromacs/gpu_utils/typecasts.cuh"
 #include "gromacs/gpu_utils/vectype_ops.cuh"
+#include "gromacs/hardware/device_information.h"
 #include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/nbnxm/atomdata.h"
 #include "gromacs/nbnxm/gpu_common.h"
@@ -119,21 +122,21 @@ namespace Nbnxm
 constexpr static int c_bufOpsThreadsPerBlock = 128;
 
 /*! Nonbonded kernel function pointer type */
-typedef void (*nbnxn_cu_kfunc_ptr_t)(const cu_atomdata_t, const cu_nbparam_t, const cu_plist_t, bool);
+typedef void (*nbnxn_cu_kfunc_ptr_t)(const cu_atomdata_t, const NBParamGpu, const gpu_plist, bool);
 
 /*********************************/
 
 /*! Returns the number of blocks to be used for the nonbonded GPU kernel. */
-static inline int calc_nb_kernel_nblock(int nwork_units, const gmx_device_info_t* dinfo)
+static inline int calc_nb_kernel_nblock(int nwork_units, const DeviceInformation* deviceInfo)
 {
     int max_grid_x_size;
 
-    assert(dinfo);
+    assert(deviceInfo);
     /* CUDA does not accept grid dimension of 0 (which can happen e.g. with an
        empty domain) and that case should be handled before this point. */
     assert(nwork_units > 0);
 
-    max_grid_x_size = dinfo->prop.maxGridSize[0];
+    max_grid_x_size = deviceInfo->prop.maxGridSize[0];
 
     /* do we exceed the grid x dimension limit? */
     if (nwork_units > max_grid_x_size)
@@ -161,7 +164,7 @@ static inline int calc_nb_kernel_nblock(int nwork_units, const gmx_device_info_t
  */
 
 /*! Force-only kernel function pointers. */
-static const nbnxn_cu_kfunc_ptr_t nb_kfunc_noener_noprune_ptr[eelCuNR][evdwCuNR] = {
+static const nbnxn_cu_kfunc_ptr_t nb_kfunc_noener_noprune_ptr[eelTypeNR][evdwTypeNR] = {
     { nbnxn_kernel_ElecCut_VdwLJ_F_cuda, nbnxn_kernel_ElecCut_VdwLJCombGeom_F_cuda,
       nbnxn_kernel_ElecCut_VdwLJCombLB_F_cuda, nbnxn_kernel_ElecCut_VdwLJFsw_F_cuda,
       nbnxn_kernel_ElecCut_VdwLJPsw_F_cuda, nbnxn_kernel_ElecCut_VdwLJEwCombGeom_F_cuda,
@@ -189,7 +192,7 @@ static const nbnxn_cu_kfunc_ptr_t nb_kfunc_noener_noprune_ptr[eelCuNR][evdwCuNR]
 };
 
 /*! Force + energy kernel function pointers. */
-static const nbnxn_cu_kfunc_ptr_t nb_kfunc_ener_noprune_ptr[eelCuNR][evdwCuNR] = {
+static const nbnxn_cu_kfunc_ptr_t nb_kfunc_ener_noprune_ptr[eelTypeNR][evdwTypeNR] = {
     { nbnxn_kernel_ElecCut_VdwLJ_VF_cuda, nbnxn_kernel_ElecCut_VdwLJCombGeom_VF_cuda,
       nbnxn_kernel_ElecCut_VdwLJCombLB_VF_cuda, nbnxn_kernel_ElecCut_VdwLJFsw_VF_cuda,
       nbnxn_kernel_ElecCut_VdwLJPsw_VF_cuda, nbnxn_kernel_ElecCut_VdwLJEwCombGeom_VF_cuda,
@@ -217,7 +220,7 @@ static const nbnxn_cu_kfunc_ptr_t nb_kfunc_ener_noprune_ptr[eelCuNR][evdwCuNR] =
 };
 
 /*! Force + pruning kernel function pointers. */
-static const nbnxn_cu_kfunc_ptr_t nb_kfunc_noener_prune_ptr[eelCuNR][evdwCuNR] = {
+static const nbnxn_cu_kfunc_ptr_t nb_kfunc_noener_prune_ptr[eelTypeNR][evdwTypeNR] = {
     { nbnxn_kernel_ElecCut_VdwLJ_F_prune_cuda, nbnxn_kernel_ElecCut_VdwLJCombGeom_F_prune_cuda,
       nbnxn_kernel_ElecCut_VdwLJCombLB_F_prune_cuda, nbnxn_kernel_ElecCut_VdwLJFsw_F_prune_cuda,
       nbnxn_kernel_ElecCut_VdwLJPsw_F_prune_cuda, nbnxn_kernel_ElecCut_VdwLJEwCombGeom_F_prune_cuda,
@@ -247,7 +250,7 @@ static const nbnxn_cu_kfunc_ptr_t nb_kfunc_noener_prune_ptr[eelCuNR][evdwCuNR] =
 };
 
 /*! Force + energy + pruning kernel function pointers. */
-static const nbnxn_cu_kfunc_ptr_t nb_kfunc_ener_prune_ptr[eelCuNR][evdwCuNR] = {
+static const nbnxn_cu_kfunc_ptr_t nb_kfunc_ener_prune_ptr[eelTypeNR][evdwTypeNR] = {
     { nbnxn_kernel_ElecCut_VdwLJ_VF_prune_cuda, nbnxn_kernel_ElecCut_VdwLJCombGeom_VF_prune_cuda,
       nbnxn_kernel_ElecCut_VdwLJCombLB_VF_prune_cuda, nbnxn_kernel_ElecCut_VdwLJFsw_VF_prune_cuda,
       nbnxn_kernel_ElecCut_VdwLJPsw_VF_prune_cuda, nbnxn_kernel_ElecCut_VdwLJEwCombGeom_VF_prune_cuda,
@@ -283,18 +286,18 @@ static inline nbnxn_cu_kfunc_ptr_t select_nbnxn_kernel(int                     e
                                                        int                     evdwtype,
                                                        bool                    bDoEne,
                                                        bool                    bDoPrune,
-                                                       const gmx_device_info_t gmx_unused* devInfo)
+                                                       const DeviceInformation gmx_unused* deviceInfo)
 {
     nbnxn_cu_kfunc_ptr_t res;
 
-    GMX_ASSERT(eeltype < eelCuNR,
+    GMX_ASSERT(eeltype < eelTypeNR,
                "The electrostatics type requested is not implemented in the CUDA kernels.");
-    GMX_ASSERT(evdwtype < evdwCuNR,
+    GMX_ASSERT(evdwtype < evdwTypeNR,
                "The VdW type requested is not implemented in the CUDA kernels.");
 
     /* assert assumptions made by the kernels */
     GMX_ASSERT(c_nbnxnGpuClusterSize * c_nbnxnGpuClusterSize / c_nbnxnGpuClusterpairSplit
-                       == devInfo->prop.warpSize,
+                       == deviceInfo->prop.warpSize,
                "The CUDA kernels require the "
                "cluster_size_i*cluster_size_j/nbnxn_gpu_clusterpair_split to match the warp size "
                "of the architecture targeted.");
@@ -327,29 +330,29 @@ static inline nbnxn_cu_kfunc_ptr_t select_nbnxn_kernel(int                     e
 
 /*! \brief Calculates the amount of shared memory required by the nonbonded kernel in use. */
 static inline int calc_shmem_required_nonbonded(const int               num_threads_z,
-                                                const gmx_device_info_t gmx_unused* dinfo,
-                                                const cu_nbparam_t*                 nbp)
+                                                const DeviceInformation gmx_unused* deviceInfo,
+                                                const NBParamGpu*                   nbp)
 {
     int shmem;
 
-    assert(dinfo);
+    assert(deviceInfo);
 
     /* size of shmem (force-buffers/xq/atom type preloading) */
     /* NOTE: with the default kernel on sm3.0 we need shmem only for pre-loading */
     /* i-atom x+q in shared memory */
-    shmem = c_numClPerSupercl * c_clSize * sizeof(float4);
+    shmem = c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(float4);
     /* cj in shared memory, for each warp separately */
     shmem += num_threads_z * c_nbnxnGpuClusterpairSplit * c_nbnxnGpuJgroupSize * sizeof(int);
 
-    if (nbp->vdwtype == evdwCuCUTCOMBGEOM || nbp->vdwtype == evdwCuCUTCOMBLB)
+    if (nbp->vdwtype == evdwTypeCUTCOMBGEOM || nbp->vdwtype == evdwTypeCUTCOMBLB)
     {
         /* i-atom LJ combination parameters in shared memory */
-        shmem += c_numClPerSupercl * c_clSize * sizeof(float2);
+        shmem += c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(float2);
     }
     else
     {
         /* i-atom types in shared memory */
-        shmem += c_numClPerSupercl * c_clSize * sizeof(int);
+        shmem += c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(int);
     }
 
     return shmem;
@@ -362,9 +365,9 @@ static inline int calc_shmem_required_nonbonded(const int               num_thre
  *  the local, this function records the event if called with the local stream as
  *  argument and inserts in the GPU stream a wait on the event on the nonlocal.
  */
-void nbnxnInsertNonlocalGpuDependency(const gmx_nbnxn_cuda_t* nb, const InteractionLocality interactionLocality)
+void nbnxnInsertNonlocalGpuDependency(const NbnxmGpu* nb, const InteractionLocality interactionLocality)
 {
-    cudaStream_t stream = nb->stream[interactionLocality];
+    const DeviceStream& deviceStream = *nb->deviceStreams[interactionLocality];
 
     /* When we get here all misc operations issued in the local stream as well as
        the local xq H2D are done,
@@ -376,19 +379,20 @@ void nbnxnInsertNonlocalGpuDependency(const gmx_nbnxn_cuda_t* nb, const Interact
     {
         if (interactionLocality == InteractionLocality::Local)
         {
-            cudaError_t stat = cudaEventRecord(nb->misc_ops_and_local_H2D_done, stream);
+            cudaError_t stat = cudaEventRecord(nb->misc_ops_and_local_H2D_done, deviceStream.stream());
             CU_RET_ERR(stat, "cudaEventRecord on misc_ops_and_local_H2D_done failed");
         }
         else
         {
-            cudaError_t stat = cudaStreamWaitEvent(stream, nb->misc_ops_and_local_H2D_done, 0);
+            cudaError_t stat =
+                    cudaStreamWaitEvent(deviceStream.stream(), nb->misc_ops_and_local_H2D_done, 0);
             CU_RET_ERR(stat, "cudaStreamWaitEvent on misc_ops_and_local_H2D_done failed");
         }
     }
 }
 
 /*! \brief Launch asynchronously the xq buffer host to device copy. */
-void gpu_copy_xq_to_gpu(gmx_nbnxn_cuda_t* nb, const nbnxn_atomdata_t* nbatom, const AtomLocality atomLocality)
+void gpu_copy_xq_to_gpu(NbnxmGpu* nb, const nbnxn_atomdata_t* nbatom, const AtomLocality atomLocality)
 {
     GMX_ASSERT(nb, "Need a valid nbnxn_gpu object");
 
@@ -399,10 +403,10 @@ void gpu_copy_xq_to_gpu(gmx_nbnxn_cuda_t* nb, const nbnxn_atomdata_t* nbatom, co
 
     int adat_begin, adat_len; /* local/nonlocal offset and length used for xq and f */
 
-    cu_atomdata_t* adat   = nb->atdat;
-    cu_plist_t*    plist  = nb->plist[iloc];
-    cu_timers_t*   t      = nb->timers;
-    cudaStream_t   stream = nb->stream[iloc];
+    cu_atomdata_t*      adat         = nb->atdat;
+    gpu_plist*          plist        = nb->plist[iloc];
+    cu_timers_t*        t            = nb->timers;
+    const DeviceStream& deviceStream = *nb->deviceStreams[iloc];
 
     bool bDoTime = nb->bDoTime;
 
@@ -438,16 +442,17 @@ void gpu_copy_xq_to_gpu(gmx_nbnxn_cuda_t* nb, const nbnxn_atomdata_t* nbatom, co
     /* beginning of timed HtoD section */
     if (bDoTime)
     {
-        t->xf[atomLocality].nb_h2d.openTimingRegion(stream);
+        t->xf[atomLocality].nb_h2d.openTimingRegion(deviceStream);
     }
 
-    cu_copy_H2D_async(adat->xq + adat_begin,
-                      static_cast<const void*>(nbatom->x().data() + adat_begin * 4),
-                      adat_len * sizeof(*adat->xq), stream);
+    static_assert(sizeof(adat->xq[0]) == sizeof(float4),
+                  "The size of the xyzq buffer element should be equal to the size of float4.");
+    copyToDeviceBuffer(&adat->xq, reinterpret_cast<const float4*>(nbatom->x().data()) + adat_begin,
+                       adat_begin, adat_len, deviceStream, GpuApiCallBehavior::Async, nullptr);
 
     if (bDoTime)
     {
-        t->xf[atomLocality].nb_h2d.closeTimingRegion(stream);
+        t->xf[atomLocality].nb_h2d.closeTimingRegion(deviceStream);
     }
 
     /* When we get here all misc operations issued in the local stream as well as
@@ -476,13 +481,13 @@ void gpu_copy_xq_to_gpu(gmx_nbnxn_cuda_t* nb, const nbnxn_atomdata_t* nbatom, co
    the local x+q H2D (and all preceding) tasks are complete and synchronize
    with this event in the non-local stream before launching the non-bonded kernel.
  */
-void gpu_launch_kernel(gmx_nbnxn_cuda_t* nb, const gmx::StepWorkload& stepWork, const InteractionLocality iloc)
+void gpu_launch_kernel(NbnxmGpu* nb, const gmx::StepWorkload& stepWork, const InteractionLocality iloc)
 {
-    cu_atomdata_t* adat   = nb->atdat;
-    cu_nbparam_t*  nbp    = nb->nbparam;
-    cu_plist_t*    plist  = nb->plist[iloc];
-    cu_timers_t*   t      = nb->timers;
-    cudaStream_t   stream = nb->stream[iloc];
+    cu_atomdata_t*      adat         = nb->atdat;
+    NBParamGpu*         nbp          = nb->nbparam;
+    gpu_plist*          plist        = nb->plist[iloc];
+    cu_timers_t*        t            = nb->timers;
+    const DeviceStream& deviceStream = *nb->deviceStreams[iloc];
 
     bool bDoTime = nb->bDoTime;
 
@@ -520,7 +525,7 @@ void gpu_launch_kernel(gmx_nbnxn_cuda_t* nb, const gmx::StepWorkload& stepWork,
     /* beginning of timed nonbonded calculation section */
     if (bDoTime)
     {
-        t->interaction[iloc].nb_k.openTimingRegion(stream);
+        t->interaction[iloc].nb_k.openTimingRegion(deviceStream);
     }
 
     /* Kernel launch config:
@@ -529,20 +534,20 @@ void gpu_launch_kernel(gmx_nbnxn_cuda_t* nb, const gmx::StepWorkload& stepWork,
      * - The 1D block-grid contains as many blocks as super-clusters.
      */
     int num_threads_z = 1;
-    if (nb->dev_info->prop.major == 3 && nb->dev_info->prop.minor == 7)
+    if (nb->deviceContext_->deviceInfo().prop.major == 3 && nb->deviceContext_->deviceInfo().prop.minor == 7)
     {
         num_threads_z = 2;
     }
-    int nblock = calc_nb_kernel_nblock(plist->nsci, nb->dev_info);
+    int nblock = calc_nb_kernel_nblock(plist->nsci, &nb->deviceContext_->deviceInfo());
 
 
     KernelLaunchConfig config;
-    config.blockSize[0]     = c_clSize;
-    config.blockSize[1]     = c_clSize;
-    config.blockSize[2]     = num_threads_z;
-    config.gridSize[0]      = nblock;
-    config.sharedMemorySize = calc_shmem_required_nonbonded(num_threads_z, nb->dev_info, nbp);
-    config.stream           = stream;
+    config.blockSize[0] = c_clSize;
+    config.blockSize[1] = c_clSize;
+    config.blockSize[2] = num_threads_z;
+    config.gridSize[0]  = nblock;
+    config.sharedMemorySize =
+            calc_shmem_required_nonbonded(num_threads_z, &nb->deviceContext_->deviceInfo(), nbp);
 
     if (debug)
     {
@@ -551,27 +556,28 @@ void gpu_launch_kernel(gmx_nbnxn_cuda_t* nb, const gmx::StepWorkload& stepWork,
                 "\tGrid: %zux%zu\n\t#Super-clusters/clusters: %d/%d (%d)\n"
                 "\tShMem: %zu\n",
                 config.blockSize[0], config.blockSize[1], config.blockSize[2], config.gridSize[0],
-                config.gridSize[1], plist->nsci * c_numClPerSupercl, c_numClPerSupercl, plist->na_c,
-                config.sharedMemorySize);
+                config.gridSize[1], plist->nsci * c_nbnxnGpuNumClusterPerSupercluster,
+                c_nbnxnGpuNumClusterPerSupercluster, plist->na_c, config.sharedMemorySize);
     }
 
     auto*      timingEvent = bDoTime ? t->interaction[iloc].nb_k.fetchNextEvent() : nullptr;
-    const auto kernel      = select_nbnxn_kernel(
-            nbp->eeltype, nbp->vdwtype, stepWork.computeEnergy,
-            (plist->haveFreshList && !nb->timers->interaction[iloc].didPrune), nb->dev_info);
+    const auto kernel =
+            select_nbnxn_kernel(nbp->eeltype, nbp->vdwtype, stepWork.computeEnergy,
+                                (plist->haveFreshList && !nb->timers->interaction[iloc].didPrune),
+                                &nb->deviceContext_->deviceInfo());
     const auto kernelArgs =
             prepareGpuKernelArguments(kernel, config, adat, nbp, plist, &stepWork.computeVirial);
-    launchGpuKernel(kernel, config, timingEvent, "k_calc_nb", kernelArgs);
+    launchGpuKernel(kernel, config, deviceStream, timingEvent, "k_calc_nb", kernelArgs);
 
     if (bDoTime)
     {
-        t->interaction[iloc].nb_k.closeTimingRegion(stream);
+        t->interaction[iloc].nb_k.closeTimingRegion(deviceStream);
     }
 
     if (GMX_NATIVE_WINDOWS)
     {
         /* Windows: force flushing WDDM queue */
-        cudaStreamQuery(stream);
+        cudaStreamQuery(deviceStream.stream());
     }
 }
 
@@ -581,20 +587,20 @@ static inline int calc_shmem_required_prune(const int num_threads_z)
     int shmem;
 
     /* i-atom x in shared memory */
-    shmem = c_numClPerSupercl * c_clSize * sizeof(float4);
+    shmem = c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(float4);
     /* cj in shared memory, for each warp separately */
     shmem += num_threads_z * c_nbnxnGpuClusterpairSplit * c_nbnxnGpuJgroupSize * sizeof(int);
 
     return shmem;
 }
 
-void gpu_launch_kernel_pruneonly(gmx_nbnxn_cuda_t* nb, const InteractionLocality iloc, const int numParts)
+void gpu_launch_kernel_pruneonly(NbnxmGpu* nb, const InteractionLocality iloc, const int numParts)
 {
-    cu_atomdata_t* adat   = nb->atdat;
-    cu_nbparam_t*  nbp    = nb->nbparam;
-    cu_plist_t*    plist  = nb->plist[iloc];
-    cu_timers_t*   t      = nb->timers;
-    cudaStream_t   stream = nb->stream[iloc];
+    cu_atomdata_t*      adat         = nb->atdat;
+    NBParamGpu*         nbp          = nb->nbparam;
+    gpu_plist*          plist        = nb->plist[iloc];
+    cu_timers_t*        t            = nb->timers;
+    const DeviceStream& deviceStream = *nb->deviceStreams[iloc];
 
     bool bDoTime = nb->bDoTime;
 
@@ -650,7 +656,7 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_cuda_t* nb, const InteractionLocality
     /* beginning of timed prune calculation section */
     if (bDoTime)
     {
-        timer->openTimingRegion(stream);
+        timer->openTimingRegion(deviceStream);
     }
 
     /* Kernel launch config:
@@ -658,15 +664,14 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_cuda_t* nb, const InteractionLocality
      *   and j-cluster concurrency, in x, y, and z, respectively.
      * - The 1D block-grid contains as many blocks as super-clusters.
      */
-    int                num_threads_z = c_cudaPruneKernelJ4Concurrency;
-    int                nblock        = calc_nb_kernel_nblock(numSciInPart, nb->dev_info);
+    int num_threads_z = c_cudaPruneKernelJ4Concurrency;
+    int nblock        = calc_nb_kernel_nblock(numSciInPart, &nb->deviceContext_->deviceInfo());
     KernelLaunchConfig config;
     config.blockSize[0]     = c_clSize;
     config.blockSize[1]     = c_clSize;
     config.blockSize[2]     = num_threads_z;
     config.gridSize[0]      = nblock;
     config.sharedMemorySize = calc_shmem_required_prune(num_threads_z);
-    config.stream           = stream;
 
     if (debug)
     {
@@ -675,8 +680,8 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_cuda_t* nb, const InteractionLocality
                 "\tGrid: %zux%zu\n\t#Super-clusters/clusters: %d/%d (%d)\n"
                 "\tShMem: %zu\n",
                 config.blockSize[0], config.blockSize[1], config.blockSize[2], config.gridSize[0],
-                config.gridSize[1], numSciInPart * c_numClPerSupercl, c_numClPerSupercl,
-                plist->na_c, config.sharedMemorySize);
+                config.gridSize[1], numSciInPart * c_nbnxnGpuNumClusterPerSupercluster,
+                c_nbnxnGpuNumClusterPerSupercluster, plist->na_c, config.sharedMemorySize);
     }
 
     auto*          timingEvent  = bDoTime ? timer->fetchNextEvent() : nullptr;
@@ -684,7 +689,7 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_cuda_t* nb, const InteractionLocality
     const auto     kernel =
             plist->haveFreshList ? nbnxn_kernel_prune_cuda<true> : nbnxn_kernel_prune_cuda<false>;
     const auto kernelArgs = prepareGpuKernelArguments(kernel, config, adat, nbp, plist, &numParts, &part);
-    launchGpuKernel(kernel, config, timingEvent, kernelName, kernelArgs);
+    launchGpuKernel(kernel, config, deviceStream, timingEvent, kernelName, kernelArgs);
 
     /* TODO: consider a more elegant way to track which kernel has been called
        (combined or separate 1st pass prune, rolling prune). */
@@ -702,17 +707,17 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_cuda_t* nb, const InteractionLocality
 
     if (bDoTime)
     {
-        timer->closeTimingRegion(stream);
+        timer->closeTimingRegion(deviceStream);
     }
 
     if (GMX_NATIVE_WINDOWS)
     {
         /* Windows: force flushing WDDM queue */
-        cudaStreamQuery(stream);
+        cudaStreamQuery(deviceStream.stream());
     }
 }
 
-void gpu_launch_cpyback(gmx_nbnxn_cuda_t*        nb,
+void gpu_launch_cpyback(NbnxmGpu*                nb,
                         nbnxn_atomdata_t*        nbatom,
                         const gmx::StepWorkload& stepWork,
                         const AtomLocality       atomLocality)
@@ -726,10 +731,10 @@ void gpu_launch_cpyback(gmx_nbnxn_cuda_t*        nb,
     const InteractionLocality iloc = gpuAtomToInteractionLocality(atomLocality);
 
     /* extract the data */
-    cu_atomdata_t* adat    = nb->atdat;
-    cu_timers_t*   t       = nb->timers;
-    bool           bDoTime = nb->bDoTime;
-    cudaStream_t   stream  = nb->stream[iloc];
+    cu_atomdata_t*      adat         = nb->atdat;
+    cu_timers_t*        t            = nb->timers;
+    bool                bDoTime      = nb->bDoTime;
+    const DeviceStream& deviceStream = *nb->deviceStreams[iloc];
 
     /* don't launch non-local copy-back if there was no non-local work to do */
     if ((iloc == InteractionLocality::NonLocal) && !haveGpuShortRangeWork(*nb, iloc))
@@ -742,14 +747,14 @@ void gpu_launch_cpyback(gmx_nbnxn_cuda_t*        nb,
     /* beginning of timed D2H section */
     if (bDoTime)
     {
-        t->xf[atomLocality].nb_d2h.openTimingRegion(stream);
+        t->xf[atomLocality].nb_d2h.openTimingRegion(deviceStream);
     }
 
     /* With DD the local D2H transfer can only start after the non-local
        kernel has finished. */
     if (iloc == InteractionLocality::Local && nb->bUseTwoStreams)
     {
-        stat = cudaStreamWaitEvent(stream, nb->nonlocal_done, 0);
+        stat = cudaStreamWaitEvent(deviceStream.stream(), nb->nonlocal_done, 0);
         CU_RET_ERR(stat, "cudaStreamWaitEvent on nonlocal_done failed");
     }
 
@@ -758,8 +763,11 @@ void gpu_launch_cpyback(gmx_nbnxn_cuda_t*        nb,
      */
     if (!stepWork.useGpuFBufferOps)
     {
-        cu_copy_D2H_async(nbatom->out[0].f.data() + adat_begin * 3, adat->f + adat_begin,
-                          (adat_len) * sizeof(*adat->f), stream);
+        static_assert(
+                sizeof(adat->f[0]) == sizeof(float3),
+                "The size of the force buffer element should be equal to the size of float3.");
+        copyFromDeviceBuffer(reinterpret_cast<float3*>(nbatom->out[0].f.data()) + adat_begin, &adat->f,
+                             adat_begin, adat_len, deviceStream, GpuApiCallBehavior::Async, nullptr);
     }
 
     /* After the non-local D2H is launched the nonlocal_done event can be
@@ -768,7 +776,7 @@ void gpu_launch_cpyback(gmx_nbnxn_cuda_t*        nb,
        back first. */
     if (iloc == InteractionLocality::NonLocal)
     {
-        stat = cudaEventRecord(nb->nonlocal_done, stream);
+        stat = cudaEventRecord(nb->nonlocal_done, deviceStream.stream());
         CU_RET_ERR(stat, "cudaEventRecord on nonlocal_done failed");
     }
 
@@ -778,20 +786,30 @@ void gpu_launch_cpyback(gmx_nbnxn_cuda_t*        nb,
         /* DtoH fshift when virial is needed */
         if (stepWork.computeVirial)
         {
-            cu_copy_D2H_async(nb->nbst.fshift, adat->fshift, SHIFTS * sizeof(*nb->nbst.fshift), stream);
+            static_assert(sizeof(nb->nbst.fshift[0]) == sizeof(adat->fshift[0]),
+                          "Sizes of host- and device-side shift vectors should be the same.");
+            copyFromDeviceBuffer(nb->nbst.fshift, &adat->fshift, 0, SHIFTS, deviceStream,
+                                 GpuApiCallBehavior::Async, nullptr);
         }
 
         /* DtoH energies */
         if (stepWork.computeEnergy)
         {
-            cu_copy_D2H_async(nb->nbst.e_lj, adat->e_lj, sizeof(*nb->nbst.e_lj), stream);
-            cu_copy_D2H_async(nb->nbst.e_el, adat->e_el, sizeof(*nb->nbst.e_el), stream);
+            static_assert(sizeof(nb->nbst.e_lj[0]) == sizeof(adat->e_lj[0]),
+                          "Sizes of host- and device-side LJ energy terms should be the same.");
+            copyFromDeviceBuffer(nb->nbst.e_lj, &adat->e_lj, 0, 1, deviceStream,
+                                 GpuApiCallBehavior::Async, nullptr);
+            static_assert(sizeof(nb->nbst.e_el[0]) == sizeof(adat->e_el[0]),
+                          "Sizes of host- and device-side electrostatic energy terms should be the "
+                          "same.");
+            copyFromDeviceBuffer(nb->nbst.e_el, &adat->e_el, 0, 1, deviceStream,
+                                 GpuApiCallBehavior::Async, nullptr);
         }
     }
 
     if (bDoTime)
     {
-        t->xf[atomLocality].nb_d2h.closeTimingRegion(stream);
+        t->xf[atomLocality].nb_d2h.closeTimingRegion(deviceStream);
     }
 }
 
@@ -799,9 +817,9 @@ void cuda_set_cacheconfig()
 {
     cudaError_t stat;
 
-    for (int i = 0; i < eelCuNR; i++)
+    for (int i = 0; i < eelTypeNR; i++)
     {
-        for (int j = 0; j < evdwCuNR; j++)
+        for (int j = 0; j < evdwTypeNR; j++)
         {
             /* Default kernel 32/32 kB Shared/L1 */
             cudaFuncSetCacheConfig(nb_kfunc_ener_prune_ptr[i][j], cudaFuncCachePreferEqual);
@@ -816,8 +834,8 @@ void cuda_set_cacheconfig()
 /* X buffer operations on GPU: performs conversion from rvec to nb format. */
 void nbnxn_gpu_x_to_nbat_x(const Nbnxm::Grid&        grid,
                            bool                      setFillerCoords,
-                           gmx_nbnxn_gpu_t*          nb,
-                           DeviceBuffer<float>       d_x,
+                           NbnxmGpu*                 nb,
+                           DeviceBuffer<gmx::RVec>   d_x,
                            GpuEventSynchronizer*     xReadyOnDevice,
                            const Nbnxm::AtomLocality locality,
                            int                       gridId,
@@ -832,7 +850,7 @@ void nbnxn_gpu_x_to_nbat_x(const Nbnxm::Grid&        grid,
     const int                  numAtomsPerCell = grid.numAtomsPerCell();
     Nbnxm::InteractionLocality interactionLoc  = gpuAtomToInteractionLocality(locality);
 
-    cudaStream_t stream = nb->stream[interactionLoc];
+    const DeviceStream& deviceStream = *nb->deviceStreams[interactionLoc];
 
     int numAtoms = grid.srcAtomEnd() - grid.srcAtomBegin();
     // avoid empty kernel launch, skip to inserting stream dependency
@@ -843,7 +861,7 @@ void nbnxn_gpu_x_to_nbat_x(const Nbnxm::Grid&        grid,
 
         // ensure that coordinates are ready on the device before launching the kernel
         GMX_ASSERT(xReadyOnDevice, "Need a valid GpuEventSynchronizer object");
-        xReadyOnDevice->enqueueWaitEvent(stream);
+        xReadyOnDevice->enqueueWaitEvent(deviceStream);
 
         KernelLaunchConfig config;
         config.blockSize[0] = c_bufOpsThreadsPerBlock;
@@ -856,98 +874,29 @@ void nbnxn_gpu_x_to_nbat_x(const Nbnxm::Grid&        grid,
         GMX_ASSERT(config.gridSize[0] > 0,
                    "Can not have empty grid, early return above avoids this");
         config.sharedMemorySize = 0;
-        config.stream           = stream;
 
-        auto       kernelFn      = nbnxn_gpu_x_to_nbat_x_kernel;
+        auto kernelFn = setFillerCoords ? nbnxn_gpu_x_to_nbat_x_kernel<true>
+                                        : nbnxn_gpu_x_to_nbat_x_kernel<false>;
         float4*    d_xq          = adat->xq;
+        float3*    d_xFloat3     = asFloat3(d_x);
         const int* d_atomIndices = nb->atomIndices;
         const int* d_cxy_na      = &nb->cxy_na[numColumnsMax * gridId];
         const int* d_cxy_ind     = &nb->cxy_ind[numColumnsMax * gridId];
-        const auto kernelArgs    = prepareGpuKernelArguments(
-                kernelFn, config, &numColumns, &d_xq, &setFillerCoords, &d_x, &d_atomIndices,
-                &d_cxy_na, &d_cxy_ind, &cellOffset, &numAtomsPerCell);
-        launchGpuKernel(kernelFn, config, nullptr, "XbufferOps", kernelArgs);
+        const auto kernelArgs    = prepareGpuKernelArguments(kernelFn, config, &numColumns, &d_xq,
+                                                          &d_xFloat3, &d_atomIndices, &d_cxy_na,
+                                                          &d_cxy_ind, &cellOffset, &numAtomsPerCell);
+        launchGpuKernel(kernelFn, config, deviceStream, nullptr, "XbufferOps", kernelArgs);
     }
 
-    // TODO: note that this is not necessary when there are no local atoms, that is:
+    // TODO: note that this is not necessary when there astreamre no local atoms, that is:
     // (numAtoms == 0 && interactionLoc == InteractionLocality::Local)
     // but for now we avoid that optimization
     nbnxnInsertNonlocalGpuDependency(nb, interactionLoc);
 }
 
-/* F buffer operations on GPU: performs force summations and conversion from nb to rvec format.
- *
- * NOTE: When the total force device buffer is reallocated and its size increases, it is cleared in
- *       Local stream. Hence, if accumulateForce is true, NonLocal stream should start accumulating
- *       forces only after Local stream already done so.
- */
-void nbnxn_gpu_add_nbat_f_to_f(const AtomLocality                         atomLocality,
-                               DeviceBuffer<float>                        totalForcesDevice,
-                               gmx_nbnxn_gpu_t*                           nb,
-                               void*                                      pmeForcesDevice,
-                               gmx::ArrayRef<GpuEventSynchronizer* const> dependencyList,
-                               int                                        atomStart,
-                               int                                        numAtoms,
-                               bool                                       useGpuFPmeReduction,
-                               bool                                       accumulateForce)
+void* getGpuForces(NbnxmGpu* nb)
 {
-    GMX_ASSERT(nb, "Need a valid nbnxn_gpu object");
-    GMX_ASSERT(numAtoms != 0, "Cannot call function with no atoms");
-    GMX_ASSERT(totalForcesDevice, "Need a valid totalForcesDevice pointer");
-
-    const InteractionLocality iLocality = gpuAtomToInteractionLocality(atomLocality);
-    cudaStream_t              stream    = nb->stream[iLocality];
-    cu_atomdata_t*            adat      = nb->atdat;
-
-    size_t gmx_used_in_debug numDependency = static_cast<size_t>((useGpuFPmeReduction == true))
-                                             + static_cast<size_t>((accumulateForce == true));
-    GMX_ASSERT(numDependency >= dependencyList.size(),
-               "Mismatching number of dependencies and call signature");
-
-    // Enqueue wait on all dependencies passed
-    for (auto const synchronizer : dependencyList)
-    {
-        synchronizer->enqueueWaitEvent(stream);
-    }
-
-    /* launch kernel */
-
-    KernelLaunchConfig config;
-    config.blockSize[0] = c_bufOpsThreadsPerBlock;
-    config.blockSize[1] = 1;
-    config.blockSize[2] = 1;
-    config.gridSize[0]  = ((numAtoms + 1) + c_bufOpsThreadsPerBlock - 1) / c_bufOpsThreadsPerBlock;
-    config.gridSize[1]  = 1;
-    config.gridSize[2]  = 1;
-    config.sharedMemorySize = 0;
-    config.stream           = stream;
-
-    auto kernelFn = accumulateForce ? nbnxn_gpu_add_nbat_f_to_f_kernel<true, false>
-                                    : nbnxn_gpu_add_nbat_f_to_f_kernel<false, false>;
-
-    if (useGpuFPmeReduction)
-    {
-        GMX_ASSERT(pmeForcesDevice, "Need a valid pmeForcesDevice pointer");
-        kernelFn = accumulateForce ? nbnxn_gpu_add_nbat_f_to_f_kernel<true, true>
-                                   : nbnxn_gpu_add_nbat_f_to_f_kernel<false, true>;
-    }
-
-    const float3* d_fNB    = adat->f;
-    const float3* d_fPme   = (float3*)pmeForcesDevice;
-    float3*       d_fTotal = (float3*)totalForcesDevice;
-    const int*    d_cell   = nb->cell;
-
-    const auto kernelArgs = prepareGpuKernelArguments(kernelFn, config, &d_fNB, &d_fPme, &d_fTotal,
-                                                      &d_cell, &atomStart, &numAtoms);
-
-    launchGpuKernel(kernelFn, config, nullptr, "FbufferOps", kernelArgs);
-
-    if (atomLocality == AtomLocality::Local)
-    {
-        GMX_ASSERT(nb->localFReductionDone != nullptr,
-                   "localFReductionDone has to be a valid pointer");
-        nb->localFReductionDone->markEvent(stream);
-    }
+    return nb->atdat->f;
 }
 
 } // namespace Nbnxm
index d5d0474bbe27e3da90c231977bec6c48576a31c9..b1d6774a26eb329362fc11cee409be22644c11a8 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <stdio.h>
 #include <stdlib.h>
 
-// TODO We would like to move this down, but the way gmx_nbnxn_gpu_t
+// TODO We would like to move this down, but the way NbnxmGpu
 //      is currently declared means this has to be before gpu_types.h
 #include "nbnxm_cuda_types.h"
 
 // TODO Remove this comment when the above order issue is resolved
 #include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream_manager.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/gpu_utils/gpueventsynchronizer.cuh"
 #include "gromacs/gpu_utils/pmalloc_cuda.h"
-#include "gromacs/hardware/gpu_hw_info.h"
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdtypes/interaction_const.h"
@@ -63,6 +67,7 @@
 #include "gromacs/nbnxm/gridset.h"
 #include "gromacs/nbnxm/nbnxm.h"
 #include "gromacs/nbnxm/nbnxm_gpu.h"
+#include "gromacs/nbnxm/nbnxm_gpu_data_mgmt.h"
 #include "gromacs/nbnxm/pairlistsets.h"
 #include "gromacs/pbcutil/ishift.h"
 #include "gromacs/timing/gpu_timing.h"
@@ -88,58 +93,19 @@ namespace Nbnxm
 static unsigned int gpu_min_ci_balanced_factor = 44;
 
 /* Fw. decl. */
-static void nbnxn_cuda_clear_e_fshift(gmx_nbnxn_cuda_t* nb);
-
-/* Fw. decl, */
-static void nbnxn_cuda_free_nbparam_table(cu_nbparam_t* nbparam);
-
-/*! \brief Return whether combination rules are used.
- *
- * \param[in]   pointer to nonbonded paramter struct
- * \return      true if combination rules are used in this run, false otherwise
- */
-static inline bool useLjCombRule(const cu_nbparam_t* nbparam)
-{
-    return (nbparam->vdwtype == evdwCuCUTCOMBGEOM || nbparam->vdwtype == evdwCuCUTCOMBLB);
-}
-
-/*! \brief Initialized the Ewald Coulomb correction GPU table.
-
-    Tabulates the Ewald Coulomb force and initializes the size/scale
-    and the table GPU array. If called with an already allocated table,
-    it just re-uploads the table.
- */
-static void init_ewald_coulomb_force_table(const EwaldCorrectionTables& tables, cu_nbparam_t* nbp)
-{
-    if (nbp->coulomb_tab != nullptr)
-    {
-        nbnxn_cuda_free_nbparam_table(nbp);
-    }
-
-    nbp->coulomb_tab_scale = tables.scale;
-    initParamLookupTable(nbp->coulomb_tab, nbp->coulomb_tab_texobj, tables.tableF.data(),
-                         tables.tableF.size());
-}
-
+static void nbnxn_cuda_clear_e_fshift(NbnxmGpu* nb);
 
 /*! Initializes the atomdata structure first time, it only gets filled at
     pair-search. */
-static void init_atomdata_first(cu_atomdata_t* ad, int ntypes)
+static void init_atomdata_first(cu_atomdata_t* ad, int ntypes, const DeviceContext& deviceContext)
 {
-    cudaError_t stat;
-
     ad->ntypes = ntypes;
-    stat       = cudaMalloc((void**)&ad->shift_vec, SHIFTS * sizeof(*ad->shift_vec));
-    CU_RET_ERR(stat, "cudaMalloc failed on ad->shift_vec");
+    allocateDeviceBuffer(&ad->shift_vec, SHIFTS, deviceContext);
     ad->bShiftVecUploaded = false;
 
-    stat = cudaMalloc((void**)&ad->fshift, SHIFTS * sizeof(*ad->fshift));
-    CU_RET_ERR(stat, "cudaMalloc failed on ad->fshift");
-
-    stat = cudaMalloc((void**)&ad->e_lj, sizeof(*ad->e_lj));
-    CU_RET_ERR(stat, "cudaMalloc failed on ad->e_lj");
-    stat = cudaMalloc((void**)&ad->e_el, sizeof(*ad->e_el));
-    CU_RET_ERR(stat, "cudaMalloc failed on ad->e_el");
+    allocateDeviceBuffer(&ad->fshift, SHIFTS, deviceContext);
+    allocateDeviceBuffer(&ad->e_lj, 1, deviceContext);
+    allocateDeviceBuffer(&ad->e_el, 1, deviceContext);
 
     /* initialize to nullptr poiters to data that is not allocated here and will
        need reallocation in nbnxn_cuda_init_atomdata */
@@ -151,87 +117,12 @@ static void init_atomdata_first(cu_atomdata_t* ad, int ntypes)
     ad->nalloc = -1;
 }
 
-/*! Selects the Ewald kernel type, analytical on SM 3.0 and later, tabulated on
-    earlier GPUs, single or twin cut-off. */
-static int pick_ewald_kernel_type(const interaction_const_t& ic)
-{
-    bool bTwinCut = (ic.rcoulomb != ic.rvdw);
-    bool bUseAnalyticalEwald, bForceAnalyticalEwald, bForceTabulatedEwald;
-    int  kernel_type;
-
-    /* Benchmarking/development environment variables to force the use of
-       analytical or tabulated Ewald kernel. */
-    bForceAnalyticalEwald = (getenv("GMX_CUDA_NB_ANA_EWALD") != nullptr);
-    bForceTabulatedEwald  = (getenv("GMX_CUDA_NB_TAB_EWALD") != nullptr);
-
-    if (bForceAnalyticalEwald && bForceTabulatedEwald)
-    {
-        gmx_incons(
-                "Both analytical and tabulated Ewald CUDA non-bonded kernels "
-                "requested through environment variables.");
-    }
-
-    /* By default use analytical Ewald. */
-    bUseAnalyticalEwald = true;
-    if (bForceAnalyticalEwald)
-    {
-        if (debug)
-        {
-            fprintf(debug, "Using analytical Ewald CUDA kernels\n");
-        }
-    }
-    else if (bForceTabulatedEwald)
-    {
-        bUseAnalyticalEwald = false;
-
-        if (debug)
-        {
-            fprintf(debug, "Using tabulated Ewald CUDA kernels\n");
-        }
-    }
-
-    /* Use twin cut-off kernels if requested by bTwinCut or the env. var.
-       forces it (use it for debugging/benchmarking only). */
-    if (!bTwinCut && (getenv("GMX_CUDA_NB_EWALD_TWINCUT") == nullptr))
-    {
-        kernel_type = bUseAnalyticalEwald ? eelCuEWALD_ANA : eelCuEWALD_TAB;
-    }
-    else
-    {
-        kernel_type = bUseAnalyticalEwald ? eelCuEWALD_ANA_TWIN : eelCuEWALD_TAB_TWIN;
-    }
-
-    return kernel_type;
-}
-
-/*! Copies all parameters related to the cut-off from ic to nbp */
-static void set_cutoff_parameters(cu_nbparam_t* nbp, const interaction_const_t* ic, const PairlistParams& listParams)
-{
-    nbp->ewald_beta        = ic->ewaldcoeff_q;
-    nbp->sh_ewald          = ic->sh_ewald;
-    nbp->epsfac            = ic->epsfac;
-    nbp->two_k_rf          = 2.0 * ic->k_rf;
-    nbp->c_rf              = ic->c_rf;
-    nbp->rvdw_sq           = ic->rvdw * ic->rvdw;
-    nbp->rcoulomb_sq       = ic->rcoulomb * ic->rcoulomb;
-    nbp->rlistOuter_sq     = listParams.rlistOuter * listParams.rlistOuter;
-    nbp->rlistInner_sq     = listParams.rlistInner * listParams.rlistInner;
-    nbp->useDynamicPruning = listParams.useDynamicPruning;
-
-    nbp->sh_lj_ewald   = ic->sh_lj_ewald;
-    nbp->ewaldcoeff_lj = ic->ewaldcoeff_lj;
-
-    nbp->rvdw_switch      = ic->rvdw_switch;
-    nbp->dispersion_shift = ic->dispersion_shift;
-    nbp->repulsion_shift  = ic->repulsion_shift;
-    nbp->vdw_switch       = ic->vdw_switch;
-}
-
 /*! Initializes the nonbonded parameter data structure. */
-static void init_nbparam(cu_nbparam_t*                   nbp,
+static void init_nbparam(NBParamGpu*                     nbp,
                          const interaction_const_t*      ic,
                          const PairlistParams&           listParams,
-                         const nbnxn_atomdata_t::Params& nbatParams)
+                         const nbnxn_atomdata_t::Params& nbatParams,
+                         const DeviceContext&            deviceContext)
 {
     int ntypes;
 
@@ -256,17 +147,17 @@ static void init_nbparam(cu_nbparam_t*                   nbp,
             case eintmodPOTSHIFT:
                 switch (nbatParams.comb_rule)
                 {
-                    case ljcrNONE: nbp->vdwtype = evdwCuCUT; break;
-                    case ljcrGEOM: nbp->vdwtype = evdwCuCUTCOMBGEOM; break;
-                    case ljcrLB: nbp->vdwtype = evdwCuCUTCOMBLB; break;
+                    case ljcrNONE: nbp->vdwtype = evdwTypeCUT; break;
+                    case ljcrGEOM: nbp->vdwtype = evdwTypeCUTCOMBGEOM; break;
+                    case ljcrLB: nbp->vdwtype = evdwTypeCUTCOMBLB; break;
                     default:
                         gmx_incons(
                                 "The requested LJ combination rule is not implemented in the CUDA "
                                 "GPU accelerated kernels!");
                 }
                 break;
-            case eintmodFORCESWITCH: nbp->vdwtype = evdwCuFSWITCH; break;
-            case eintmodPOTSWITCH: nbp->vdwtype = evdwCuPSWITCH; break;
+            case eintmodFORCESWITCH: nbp->vdwtype = evdwTypeFSWITCH; break;
+            case eintmodPOTSWITCH: nbp->vdwtype = evdwTypePSWITCH; break;
             default:
                 gmx_incons(
                         "The requested VdW interaction modifier is not implemented in the CUDA GPU "
@@ -278,12 +169,12 @@ static void init_nbparam(cu_nbparam_t*                   nbp,
         if (ic->ljpme_comb_rule == ljcrGEOM)
         {
             assert(nbatParams.comb_rule == ljcrGEOM);
-            nbp->vdwtype = evdwCuEWALDGEOM;
+            nbp->vdwtype = evdwTypeEWALDGEOM;
         }
         else
         {
             assert(nbatParams.comb_rule == ljcrLB);
-            nbp->vdwtype = evdwCuEWALDLB;
+            nbp->vdwtype = evdwTypeEWALDLB;
         }
     }
     else
@@ -294,15 +185,15 @@ static void init_nbparam(cu_nbparam_t*                   nbp,
 
     if (ic->eeltype == eelCUT)
     {
-        nbp->eeltype = eelCuCUT;
+        nbp->eeltype = eelTypeCUT;
     }
     else if (EEL_RF(ic->eeltype))
     {
-        nbp->eeltype = eelCuRF;
+        nbp->eeltype = eelTypeRF;
     }
     else if ((EEL_PME(ic->eeltype) || ic->eeltype == eelEWALD))
     {
-        nbp->eeltype = pick_ewald_kernel_type(*ic);
+        nbp->eeltype = nbnxn_gpu_pick_ewald_kernel_type(*ic);
     }
     else
     {
@@ -314,114 +205,50 @@ static void init_nbparam(cu_nbparam_t*                   nbp,
 
     /* generate table for PME */
     nbp->coulomb_tab = nullptr;
-    if (nbp->eeltype == eelCuEWALD_TAB || nbp->eeltype == eelCuEWALD_TAB_TWIN)
+    if (nbp->eeltype == eelTypeEWALD_TAB || nbp->eeltype == eelTypeEWALD_TAB_TWIN)
     {
         GMX_RELEASE_ASSERT(ic->coulombEwaldTables, "Need valid Coulomb Ewald correction tables");
-        init_ewald_coulomb_force_table(*ic->coulombEwaldTables, nbp);
+        init_ewald_coulomb_force_table(*ic->coulombEwaldTables, nbp, deviceContext);
     }
 
     /* set up LJ parameter lookup table */
-    if (!useLjCombRule(nbp))
+    if (!useLjCombRule(nbp->vdwtype))
     {
-        initParamLookupTable(nbp->nbfp, nbp->nbfp_texobj, nbatParams.nbfp.data(), 2 * ntypes * ntypes);
+        initParamLookupTable(&nbp->nbfp, &nbp->nbfp_texobj, nbatParams.nbfp.data(),
+                             2 * ntypes * ntypes, deviceContext);
     }
 
     /* set up LJ-PME parameter lookup table */
     if (ic->vdwtype == evdwPME)
     {
-        initParamLookupTable(nbp->nbfp_comb, nbp->nbfp_comb_texobj, nbatParams.nbfp_comb.data(), 2 * ntypes);
+        initParamLookupTable(&nbp->nbfp_comb, &nbp->nbfp_comb_texobj, nbatParams.nbfp_comb.data(),
+                             2 * ntypes, deviceContext);
     }
 }
 
-/*! Re-generate the GPU Ewald force table, resets rlist, and update the
- *  electrostatic type switching to twin cut-off (or back) if needed. */
-void gpu_pme_loadbal_update_param(const nonbonded_verlet_t* nbv, const interaction_const_t* ic)
-{
-    if (!nbv || !nbv->useGpu())
-    {
-        return;
-    }
-    cu_nbparam_t* nbp = nbv->gpu_nbv->nbparam;
-
-    set_cutoff_parameters(nbp, ic, nbv->pairlistSets().params());
-
-    nbp->eeltype = pick_ewald_kernel_type(*ic);
-
-    GMX_RELEASE_ASSERT(ic->coulombEwaldTables, "Need valid Coulomb Ewald correction tables");
-    init_ewald_coulomb_force_table(*ic->coulombEwaldTables, nbp);
-}
-
-/*! Initializes the pair list data structure. */
-static void init_plist(cu_plist_t* pl)
-{
-    /* initialize to nullptr pointers to data that is not allocated here and will
-       need reallocation in nbnxn_gpu_init_pairlist */
-    pl->sci   = nullptr;
-    pl->cj4   = nullptr;
-    pl->imask = nullptr;
-    pl->excl  = nullptr;
-
-    /* size -1 indicates that the respective array hasn't been initialized yet */
-    pl->na_c          = -1;
-    pl->nsci          = -1;
-    pl->sci_nalloc    = -1;
-    pl->ncj4          = -1;
-    pl->cj4_nalloc    = -1;
-    pl->nimask        = -1;
-    pl->imask_nalloc  = -1;
-    pl->nexcl         = -1;
-    pl->excl_nalloc   = -1;
-    pl->haveFreshList = false;
-}
-
-/*! Initializes the timings data structure. */
-static void init_timings(gmx_wallclock_gpu_nbnxn_t* t)
-{
-    int i, j;
-
-    t->nb_h2d_t = 0.0;
-    t->nb_d2h_t = 0.0;
-    t->nb_c     = 0;
-    t->pl_h2d_t = 0.0;
-    t->pl_h2d_c = 0;
-    for (i = 0; i < 2; i++)
-    {
-        for (j = 0; j < 2; j++)
-        {
-            t->ktime[i][j].t = 0.0;
-            t->ktime[i][j].c = 0;
-        }
-    }
-    t->pruneTime.c        = 0;
-    t->pruneTime.t        = 0.0;
-    t->dynamicPruneTime.c = 0;
-    t->dynamicPruneTime.t = 0.0;
-}
-
 /*! Initializes simulation constant data. */
-static void cuda_init_const(gmx_nbnxn_cuda_t*               nb,
+static void cuda_init_const(NbnxmGpu*                       nb,
                             const interaction_const_t*      ic,
                             const PairlistParams&           listParams,
                             const nbnxn_atomdata_t::Params& nbatParams)
 {
-    init_atomdata_first(nb->atdat, nbatParams.numTypes);
-    init_nbparam(nb->nbparam, ic, listParams, nbatParams);
+    init_atomdata_first(nb->atdat, nbatParams.numTypes, *nb->deviceContext_);
+    init_nbparam(nb->nbparam, ic, listParams, nbatParams, *nb->deviceContext_);
 
     /* clear energy and shift force outputs */
     nbnxn_cuda_clear_e_fshift(nb);
 }
 
-gmx_nbnxn_cuda_t* gpu_init(const gmx_device_info_t*   deviceInfo,
-                           const interaction_const_t* ic,
-                           const PairlistParams&      listParams,
-                           const nbnxn_atomdata_t*    nbat,
-                           int /*rank*/,
-                           gmx_bool bLocalAndNonlocal)
+NbnxmGpu* gpu_init(const gmx::DeviceStreamManager& deviceStreamManager,
+                   const interaction_const_t*      ic,
+                   const PairlistParams&           listParams,
+                   const nbnxn_atomdata_t*         nbat,
+                   bool                            bLocalAndNonlocal)
 {
     cudaError_t stat;
 
-    gmx_nbnxn_cuda_t* nb;
-    snew(nb, 1);
+    auto nb            = new NbnxmGpu();
+    nb->deviceContext_ = &deviceStreamManager.context();
     snew(nb->atdat, 1);
     snew(nb->nbparam, 1);
     snew(nb->plist[InteractionLocality::Local], 1);
@@ -442,12 +269,11 @@ gmx_nbnxn_cuda_t* gpu_init(const gmx_device_info_t*   deviceInfo,
 
     init_plist(nb->plist[InteractionLocality::Local]);
 
-    /* set device info, just point it to the right GPU among the detected ones */
-    nb->dev_info = deviceInfo;
-
     /* local/non-local GPU streams */
-    stat = cudaStreamCreate(&nb->stream[InteractionLocality::Local]);
-    CU_RET_ERR(stat, "cudaStreamCreate on stream[InterationLocality::Local] failed");
+    GMX_RELEASE_ASSERT(deviceStreamManager.streamIsValid(gmx::DeviceStreamType::NonBondedLocal),
+                       "Local non-bonded stream should be initialized to use GPU for non-bonded.");
+    nb->deviceStreams[InteractionLocality::Local] =
+            &deviceStreamManager.stream(gmx::DeviceStreamType::NonBondedLocal);
     if (nb->bUseTwoStreams)
     {
         init_plist(nb->plist[InteractionLocality::NonLocal]);
@@ -456,14 +282,12 @@ gmx_nbnxn_cuda_t* gpu_init(const gmx_device_info_t*   deviceInfo,
          * priorities, because we are querying the priority range which in this
          * case will be a single value.
          */
-        int highest_priority;
-        stat = cudaDeviceGetStreamPriorityRange(nullptr, &highest_priority);
-        CU_RET_ERR(stat, "cudaDeviceGetStreamPriorityRange failed");
-
-        stat = cudaStreamCreateWithPriority(&nb->stream[InteractionLocality::NonLocal],
-                                            cudaStreamDefault, highest_priority);
-        CU_RET_ERR(stat,
-                   "cudaStreamCreateWithPriority on stream[InteractionLocality::NonLocal] failed");
+        GMX_RELEASE_ASSERT(deviceStreamManager.streamIsValid(gmx::DeviceStreamType::NonBondedNonLocal),
+                           "Non-local non-bonded stream should be initialized to use GPU for "
+                           "non-bonded with domain decomposition.");
+        nb->deviceStreams[InteractionLocality::NonLocal] =
+                &deviceStreamManager.stream(gmx::DeviceStreamType::NonBondedNonLocal);
+        ;
     }
 
     /* init events for sychronization (timing disabled for performance reasons!) */
@@ -497,8 +321,6 @@ gmx_nbnxn_cuda_t* gpu_init(const gmx_device_info_t*   deviceInfo,
     nb->ncxy_na_alloc         = 0;
     nb->ncxy_ind              = 0;
     nb->ncxy_ind_alloc        = 0;
-    nb->ncell                 = 0;
-    nb->ncell_alloc           = 0;
 
     if (debug)
     {
@@ -508,102 +330,42 @@ gmx_nbnxn_cuda_t* gpu_init(const gmx_device_info_t*   deviceInfo,
     return nb;
 }
 
-void gpu_init_pairlist(gmx_nbnxn_cuda_t* nb, const NbnxnPairlistGpu* h_plist, const InteractionLocality iloc)
+void gpu_upload_shiftvec(NbnxmGpu* nb, const nbnxn_atomdata_t* nbatom)
 {
-    char         sbuf[STRLEN];
-    bool         bDoTime = (nb->bDoTime && !h_plist->sci.empty());
-    cudaStream_t stream  = nb->stream[iloc];
-    cu_plist_t*  d_plist = nb->plist[iloc];
-
-    if (d_plist->na_c < 0)
-    {
-        d_plist->na_c = h_plist->na_ci;
-    }
-    else
-    {
-        if (d_plist->na_c != h_plist->na_ci)
-        {
-            sprintf(sbuf, "In cu_init_plist: the #atoms per cell has changed (from %d to %d)",
-                    d_plist->na_c, h_plist->na_ci);
-            gmx_incons(sbuf);
-        }
-    }
-
-    gpu_timers_t::Interaction& iTimers = nb->timers->interaction[iloc];
-
-    if (bDoTime)
-    {
-        iTimers.pl_h2d.openTimingRegion(stream);
-        iTimers.didPairlistH2D = true;
-    }
-
-    DeviceContext context = nullptr;
-
-    reallocateDeviceBuffer(&d_plist->sci, h_plist->sci.size(), &d_plist->nsci, &d_plist->sci_nalloc, context);
-    copyToDeviceBuffer(&d_plist->sci, h_plist->sci.data(), 0, h_plist->sci.size(), stream,
-                       GpuApiCallBehavior::Async, bDoTime ? iTimers.pl_h2d.fetchNextEvent() : nullptr);
-
-    reallocateDeviceBuffer(&d_plist->cj4, h_plist->cj4.size(), &d_plist->ncj4, &d_plist->cj4_nalloc, context);
-    copyToDeviceBuffer(&d_plist->cj4, h_plist->cj4.data(), 0, h_plist->cj4.size(), stream,
-                       GpuApiCallBehavior::Async, bDoTime ? iTimers.pl_h2d.fetchNextEvent() : nullptr);
-
-    reallocateDeviceBuffer(&d_plist->imask, h_plist->cj4.size() * c_nbnxnGpuClusterpairSplit,
-                           &d_plist->nimask, &d_plist->imask_nalloc, context);
-
-    reallocateDeviceBuffer(&d_plist->excl, h_plist->excl.size(), &d_plist->nexcl,
-                           &d_plist->excl_nalloc, context);
-    copyToDeviceBuffer(&d_plist->excl, h_plist->excl.data(), 0, h_plist->excl.size(), stream,
-                       GpuApiCallBehavior::Async, bDoTime ? iTimers.pl_h2d.fetchNextEvent() : nullptr);
-
-    if (bDoTime)
-    {
-        iTimers.pl_h2d.closeTimingRegion(stream);
-    }
-
-    /* the next use of thist list we be the first one, so we need to prune */
-    d_plist->haveFreshList = true;
-}
-
-void gpu_upload_shiftvec(gmx_nbnxn_cuda_t* nb, const nbnxn_atomdata_t* nbatom)
-{
-    cu_atomdata_t* adat = nb->atdat;
-    cudaStream_t   ls   = nb->stream[InteractionLocality::Local];
+    cu_atomdata_t*      adat        = nb->atdat;
+    const DeviceStream& localStream = *nb->deviceStreams[InteractionLocality::Local];
 
     /* only if we have a dynamic box */
     if (nbatom->bDynamicBox || !adat->bShiftVecUploaded)
     {
-        cu_copy_H2D_async(adat->shift_vec, nbatom->shift_vec.data(), SHIFTS * sizeof(*adat->shift_vec), ls);
+        static_assert(sizeof(adat->shift_vec[0]) == sizeof(nbatom->shift_vec[0]),
+                      "Sizes of host- and device-side shift vectors should be the same.");
+        copyToDeviceBuffer(&adat->shift_vec, reinterpret_cast<const float3*>(nbatom->shift_vec.data()),
+                           0, SHIFTS, localStream, GpuApiCallBehavior::Async, nullptr);
         adat->bShiftVecUploaded = true;
     }
 }
 
 /*! Clears the first natoms_clear elements of the GPU nonbonded force output array. */
-static void nbnxn_cuda_clear_f(gmx_nbnxn_cuda_t* nb, int natoms_clear)
+static void nbnxn_cuda_clear_f(NbnxmGpu* nb, int natoms_clear)
 {
-    cudaError_t    stat;
-    cu_atomdata_t* adat = nb->atdat;
-    cudaStream_t   ls   = nb->stream[InteractionLocality::Local];
-
-    stat = cudaMemsetAsync(adat->f, 0, natoms_clear * sizeof(*adat->f), ls);
-    CU_RET_ERR(stat, "cudaMemsetAsync on f falied");
+    cu_atomdata_t*      adat        = nb->atdat;
+    const DeviceStream& localStream = *nb->deviceStreams[InteractionLocality::Local];
+    clearDeviceBufferAsync(&adat->f, 0, natoms_clear, localStream);
 }
 
 /*! Clears nonbonded shift force output array and energy outputs on the GPU. */
-static void nbnxn_cuda_clear_e_fshift(gmx_nbnxn_cuda_t* nb)
+static void nbnxn_cuda_clear_e_fshift(NbnxmGpu* nb)
 {
-    cudaError_t    stat;
-    cu_atomdata_t* adat = nb->atdat;
-    cudaStream_t   ls   = nb->stream[InteractionLocality::Local];
-
-    stat = cudaMemsetAsync(adat->fshift, 0, SHIFTS * sizeof(*adat->fshift), ls);
-    CU_RET_ERR(stat, "cudaMemsetAsync on fshift falied");
-    stat = cudaMemsetAsync(adat->e_lj, 0, sizeof(*adat->e_lj), ls);
-    CU_RET_ERR(stat, "cudaMemsetAsync on e_lj falied");
-    stat = cudaMemsetAsync(adat->e_el, 0, sizeof(*adat->e_el), ls);
-    CU_RET_ERR(stat, "cudaMemsetAsync on e_el falied");
+    cu_atomdata_t*      adat        = nb->atdat;
+    const DeviceStream& localStream = *nb->deviceStreams[InteractionLocality::Local];
+
+    clearDeviceBufferAsync(&adat->fshift, 0, SHIFTS, localStream);
+    clearDeviceBufferAsync(&adat->e_lj, 0, 1, localStream);
+    clearDeviceBufferAsync(&adat->e_el, 0, 1, localStream);
 }
 
-void gpu_clear_outputs(gmx_nbnxn_cuda_t* nb, bool computeVirial)
+void gpu_clear_outputs(NbnxmGpu* nb, bool computeVirial)
 {
     nbnxn_cuda_clear_f(nb, nb->atdat->natoms);
     /* clear shift force array and energies if the outputs were
@@ -614,15 +376,15 @@ void gpu_clear_outputs(gmx_nbnxn_cuda_t* nb, bool computeVirial)
     }
 }
 
-void gpu_init_atomdata(gmx_nbnxn_cuda_t* nb, const nbnxn_atomdata_t* nbat)
+void gpu_init_atomdata(NbnxmGpu* nb, const nbnxn_atomdata_t* nbat)
 {
-    cudaError_t    stat;
-    int            nalloc, natoms;
-    bool           realloced;
-    bool           bDoTime = nb->bDoTime;
-    cu_timers_t*   timers  = nb->timers;
-    cu_atomdata_t* d_atdat = nb->atdat;
-    cudaStream_t   ls      = nb->stream[InteractionLocality::Local];
+    int                  nalloc, natoms;
+    bool                 realloced;
+    bool                 bDoTime       = nb->bDoTime;
+    cu_timers_t*         timers        = nb->timers;
+    cu_atomdata_t*       d_atdat       = nb->atdat;
+    const DeviceContext& deviceContext = *nb->deviceContext_;
+    const DeviceStream&  localStream   = *nb->deviceStreams[InteractionLocality::Local];
 
     natoms    = nbat->numAtoms();
     realloced = false;
@@ -630,7 +392,7 @@ void gpu_init_atomdata(gmx_nbnxn_cuda_t* nb, const nbnxn_atomdata_t* nbat)
     if (bDoTime)
     {
         /* time async copy */
-        timers->atdat.openTimingRegion(ls);
+        timers->atdat.openTimingRegion(localStream);
     }
 
     /* need to reallocate if we have to copy more atoms than the amount of space
@@ -648,19 +410,15 @@ void gpu_init_atomdata(gmx_nbnxn_cuda_t* nb, const nbnxn_atomdata_t* nbat)
             freeDeviceBuffer(&d_atdat->lj_comb);
         }
 
-        stat = cudaMalloc((void**)&d_atdat->f, nalloc * sizeof(*d_atdat->f));
-        CU_RET_ERR(stat, "cudaMalloc failed on d_atdat->f");
-        stat = cudaMalloc((void**)&d_atdat->xq, nalloc * sizeof(*d_atdat->xq));
-        CU_RET_ERR(stat, "cudaMalloc failed on d_atdat->xq");
-        if (useLjCombRule(nb->nbparam))
+        allocateDeviceBuffer(&d_atdat->f, nalloc, deviceContext);
+        allocateDeviceBuffer(&d_atdat->xq, nalloc, deviceContext);
+        if (useLjCombRule(nb->nbparam->vdwtype))
         {
-            stat = cudaMalloc((void**)&d_atdat->lj_comb, nalloc * sizeof(*d_atdat->lj_comb));
-            CU_RET_ERR(stat, "cudaMalloc failed on d_atdat->lj_comb");
+            allocateDeviceBuffer(&d_atdat->lj_comb, nalloc, deviceContext);
         }
         else
         {
-            stat = cudaMalloc((void**)&d_atdat->atom_types, nalloc * sizeof(*d_atdat->atom_types));
-            CU_RET_ERR(stat, "cudaMalloc failed on d_atdat->atom_types");
+            allocateDeviceBuffer(&d_atdat->atom_types, nalloc, deviceContext);
         }
 
         d_atdat->nalloc = nalloc;
@@ -676,36 +434,33 @@ void gpu_init_atomdata(gmx_nbnxn_cuda_t* nb, const nbnxn_atomdata_t* nbat)
         nbnxn_cuda_clear_f(nb, nalloc);
     }
 
-    if (useLjCombRule(nb->nbparam))
+    if (useLjCombRule(nb->nbparam->vdwtype))
     {
-        cu_copy_H2D_async(d_atdat->lj_comb, nbat->params().lj_comb.data(),
-                          natoms * sizeof(*d_atdat->lj_comb), ls);
+        static_assert(sizeof(d_atdat->lj_comb[0]) == sizeof(float2),
+                      "Size of the LJ parameters element should be equal to the size of float2.");
+        copyToDeviceBuffer(&d_atdat->lj_comb,
+                           reinterpret_cast<const float2*>(nbat->params().lj_comb.data()), 0,
+                           natoms, localStream, GpuApiCallBehavior::Async, nullptr);
     }
     else
     {
-        cu_copy_H2D_async(d_atdat->atom_types, nbat->params().type.data(),
-                          natoms * sizeof(*d_atdat->atom_types), ls);
+        static_assert(sizeof(d_atdat->atom_types[0]) == sizeof(nbat->params().type[0]),
+                      "Sizes of host- and device-side atom types should be the same.");
+        copyToDeviceBuffer(&d_atdat->atom_types, nbat->params().type.data(), 0, natoms, localStream,
+                           GpuApiCallBehavior::Async, nullptr);
     }
 
     if (bDoTime)
     {
-        timers->atdat.closeTimingRegion(ls);
-    }
-}
-
-static void nbnxn_cuda_free_nbparam_table(cu_nbparam_t* nbparam)
-{
-    if (nbparam->eeltype == eelCuEWALD_TAB || nbparam->eeltype == eelCuEWALD_TAB_TWIN)
-    {
-        destroyParamLookupTable(nbparam->coulomb_tab, nbparam->coulomb_tab_texobj);
+        timers->atdat.closeTimingRegion(localStream);
     }
 }
 
-void gpu_free(gmx_nbnxn_cuda_t* nb)
+void gpu_free(NbnxmGpu* nb)
 {
     cudaError_t    stat;
     cu_atomdata_t* atdat;
-    cu_nbparam_t*  nbparam;
+    NBParamGpu*    nbparam;
 
     if (nb == nullptr)
     {
@@ -715,7 +470,11 @@ void gpu_free(gmx_nbnxn_cuda_t* nb)
     atdat   = nb->atdat;
     nbparam = nb->nbparam;
 
-    nbnxn_cuda_free_nbparam_table(nbparam);
+    if ((!nbparam->coulomb_tab)
+        && (nbparam->eeltype == eelTypeEWALD_TAB || nbparam->eeltype == eelTypeEWALD_TAB_TWIN))
+    {
+        destroyParamLookupTable(&nbparam->coulomb_tab, nbparam->coulomb_tab_texobj);
+    }
 
     stat = cudaEventDestroy(nb->nonlocal_done);
     CU_RET_ERR(stat, "cudaEventDestroy failed on timers->nonlocal_done");
@@ -723,35 +482,22 @@ void gpu_free(gmx_nbnxn_cuda_t* nb)
     CU_RET_ERR(stat, "cudaEventDestroy failed on timers->misc_ops_and_local_H2D_done");
 
     delete nb->timers;
-    if (nb->bDoTime)
-    {
-        /* The non-local counters/stream (second in the array) are needed only with DD. */
-        for (int i = 0; i <= (nb->bUseTwoStreams ? 1 : 0); i++)
-        {
-            stat = cudaStreamDestroy(nb->stream[i]);
-            CU_RET_ERR(stat, "cudaStreamDestroy failed on stream");
-        }
-    }
 
-    if (!useLjCombRule(nb->nbparam))
+    if (!useLjCombRule(nb->nbparam->vdwtype))
     {
-        destroyParamLookupTable(nbparam->nbfp, nbparam->nbfp_texobj);
+        destroyParamLookupTable(&nbparam->nbfp, nbparam->nbfp_texobj);
     }
 
-    if (nbparam->vdwtype == evdwCuEWALDGEOM || nbparam->vdwtype == evdwCuEWALDLB)
+    if (nbparam->vdwtype == evdwTypeEWALDGEOM || nbparam->vdwtype == evdwTypeEWALDLB)
     {
-        destroyParamLookupTable(nbparam->nbfp_comb, nbparam->nbfp_comb_texobj);
+        destroyParamLookupTable(&nbparam->nbfp_comb, nbparam->nbfp_comb_texobj);
     }
 
-    stat = cudaFree(atdat->shift_vec);
-    CU_RET_ERR(stat, "cudaFree failed on atdat->shift_vec");
-    stat = cudaFree(atdat->fshift);
-    CU_RET_ERR(stat, "cudaFree failed on atdat->fshift");
+    freeDeviceBuffer(&atdat->shift_vec);
+    freeDeviceBuffer(&atdat->fshift);
 
-    stat = cudaFree(atdat->e_lj);
-    CU_RET_ERR(stat, "cudaFree failed on atdat->e_lj");
-    stat = cudaFree(atdat->e_el);
-    CU_RET_ERR(stat, "cudaFree failed on atdat->e_el");
+    freeDeviceBuffer(&atdat->e_lj);
+    freeDeviceBuffer(&atdat->e_el);
 
     freeDeviceBuffer(&atdat->f);
     freeDeviceBuffer(&atdat->xq);
@@ -788,7 +534,7 @@ void gpu_free(gmx_nbnxn_cuda_t* nb)
     sfree(atdat);
     sfree(nbparam);
     sfree(nb->timings);
-    sfree(nb);
+    delete nb;
 
     if (debug)
     {
@@ -796,70 +542,45 @@ void gpu_free(gmx_nbnxn_cuda_t* nb)
     }
 }
 
-//! This function is documented in the header file
-gmx_wallclock_gpu_nbnxn_t* gpu_get_timings(gmx_nbnxn_cuda_t* nb)
-{
-    return (nb != nullptr && nb->bDoTime) ? nb->timings : nullptr;
-}
-
-void gpu_reset_timings(nonbonded_verlet_t* nbv)
-{
-    if (nbv->gpu_nbv && nbv->gpu_nbv->bDoTime)
-    {
-        init_timings(nbv->gpu_nbv->timings);
-    }
-}
-
-int gpu_min_ci_balanced(gmx_nbnxn_cuda_t* nb)
-{
-    return nb != nullptr ? gpu_min_ci_balanced_factor * nb->dev_info->prop.multiProcessorCount : 0;
-}
-
-gmx_bool gpu_is_kernel_ewald_analytical(const gmx_nbnxn_cuda_t* nb)
+int gpu_min_ci_balanced(NbnxmGpu* nb)
 {
-    return ((nb->nbparam->eeltype == eelCuEWALD_ANA) || (nb->nbparam->eeltype == eelCuEWALD_ANA_TWIN));
+    return nb != nullptr ? gpu_min_ci_balanced_factor * nb->deviceContext_->deviceInfo().prop.multiProcessorCount
+                         : 0;
 }
 
-void* gpu_get_command_stream(gmx_nbnxn_gpu_t* nb, const InteractionLocality iloc)
-{
-    assert(nb);
-
-    return static_cast<void*>(&nb->stream[iloc]);
-}
-
-void* gpu_get_xq(gmx_nbnxn_gpu_t* nb)
+void* gpu_get_xq(NbnxmGpu* nb)
 {
     assert(nb);
 
     return static_cast<void*>(nb->atdat->xq);
 }
 
-void* gpu_get_f(gmx_nbnxn_gpu_t* nb)
+DeviceBuffer<gmx::RVec> gpu_get_f(NbnxmGpu* nb)
 {
     assert(nb);
 
-    return static_cast<void*>(nb->atdat->f);
+    return reinterpret_cast<DeviceBuffer<gmx::RVec>>(nb->atdat->f);
 }
 
-rvec* gpu_get_fshift(gmx_nbnxn_gpu_t* nb)
+DeviceBuffer<gmx::RVec> gpu_get_fshift(NbnxmGpu* nb)
 {
     assert(nb);
 
-    return reinterpret_cast<rvec*>(nb->atdat->fshift);
+    return reinterpret_cast<DeviceBuffer<gmx::RVec>>(nb->atdat->fshift);
 }
 
 /* Initialization for X buffer operations on GPU. */
 /* TODO  Remove explicit pinning from host arrays from here and manage in a more natural way*/
-void nbnxn_gpu_init_x_to_nbat_x(const Nbnxm::GridSet& gridSet, gmx_nbnxn_gpu_t* gpu_nbv)
+void nbnxn_gpu_init_x_to_nbat_x(const Nbnxm::GridSet& gridSet, NbnxmGpu* gpu_nbv)
 {
-    cudaStream_t stream        = gpu_nbv->stream[InteractionLocality::Local];
-    bool         bDoTime       = gpu_nbv->bDoTime;
-    const int    maxNumColumns = gridSet.numColumnsMax();
+    const DeviceStream& deviceStream  = *gpu_nbv->deviceStreams[InteractionLocality::Local];
+    bool                bDoTime       = gpu_nbv->bDoTime;
+    const int           maxNumColumns = gridSet.numColumnsMax();
 
     reallocateDeviceBuffer(&gpu_nbv->cxy_na, maxNumColumns * gridSet.grids().size(),
-                           &gpu_nbv->ncxy_na, &gpu_nbv->ncxy_na_alloc, nullptr);
+                           &gpu_nbv->ncxy_na, &gpu_nbv->ncxy_na_alloc, *gpu_nbv->deviceContext_);
     reallocateDeviceBuffer(&gpu_nbv->cxy_ind, maxNumColumns * gridSet.grids().size(),
-                           &gpu_nbv->ncxy_ind, &gpu_nbv->ncxy_ind_alloc, nullptr);
+                           &gpu_nbv->ncxy_ind, &gpu_nbv->ncxy_ind_alloc, *gpu_nbv->deviceContext_);
 
     for (unsigned int g = 0; g < gridSet.grids().size(); g++)
     {
@@ -873,22 +594,22 @@ void nbnxn_gpu_init_x_to_nbat_x(const Nbnxm::GridSet& gridSet, gmx_nbnxn_gpu_t*
         const int* cxy_ind         = grid.cxy_ind().data();
 
         reallocateDeviceBuffer(&gpu_nbv->atomIndices, atomIndicesSize, &gpu_nbv->atomIndicesSize,
-                               &gpu_nbv->atomIndicesSize_alloc, nullptr);
+                               &gpu_nbv->atomIndicesSize_alloc, *gpu_nbv->deviceContext_);
 
         if (atomIndicesSize > 0)
         {
 
             if (bDoTime)
             {
-                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.openTimingRegion(stream);
+                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.openTimingRegion(deviceStream);
             }
 
-            copyToDeviceBuffer(&gpu_nbv->atomIndices, atomIndices, 0, atomIndicesSize, stream,
+            copyToDeviceBuffer(&gpu_nbv->atomIndices, atomIndices, 0, atomIndicesSize, deviceStream,
                                GpuApiCallBehavior::Async, nullptr);
 
             if (bDoTime)
             {
-                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.closeTimingRegion(stream);
+                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.closeTimingRegion(deviceStream);
             }
         }
 
@@ -896,28 +617,30 @@ void nbnxn_gpu_init_x_to_nbat_x(const Nbnxm::GridSet& gridSet, gmx_nbnxn_gpu_t*
         {
             if (bDoTime)
             {
-                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.openTimingRegion(stream);
+                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.openTimingRegion(deviceStream);
             }
 
             int* destPtr = &gpu_nbv->cxy_na[maxNumColumns * g];
-            copyToDeviceBuffer(&destPtr, cxy_na, 0, numColumns, stream, GpuApiCallBehavior::Async, nullptr);
+            copyToDeviceBuffer(&destPtr, cxy_na, 0, numColumns, deviceStream,
+                               GpuApiCallBehavior::Async, nullptr);
 
             if (bDoTime)
             {
-                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.closeTimingRegion(stream);
+                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.closeTimingRegion(deviceStream);
             }
 
             if (bDoTime)
             {
-                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.openTimingRegion(stream);
+                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.openTimingRegion(deviceStream);
             }
 
             destPtr = &gpu_nbv->cxy_ind[maxNumColumns * g];
-            copyToDeviceBuffer(&destPtr, cxy_ind, 0, numColumns, stream, GpuApiCallBehavior::Async, nullptr);
+            copyToDeviceBuffer(&destPtr, cxy_ind, 0, numColumns, deviceStream,
+                               GpuApiCallBehavior::Async, nullptr);
 
             if (bDoTime)
             {
-                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.closeTimingRegion(stream);
+                gpu_nbv->timers->xf[AtomLocality::Local].nb_h2d.closeTimingRegion(deviceStream);
             }
         }
     }
@@ -934,25 +657,4 @@ void nbnxn_gpu_init_x_to_nbat_x(const Nbnxm::GridSet& gridSet, gmx_nbnxn_gpu_t*
     return;
 }
 
-/* Initialization for F buffer operations on GPU. */
-void nbnxn_gpu_init_add_nbat_f_to_f(const int*                  cell,
-                                    gmx_nbnxn_gpu_t*            gpu_nbv,
-                                    int                         natoms_total,
-                                    GpuEventSynchronizer* const localReductionDone)
-{
-
-    cudaStream_t stream = gpu_nbv->stream[InteractionLocality::Local];
-
-    GMX_ASSERT(localReductionDone, "localReductionDone should be a valid pointer");
-    gpu_nbv->localFReductionDone = localReductionDone;
-
-    if (natoms_total > 0)
-    {
-        reallocateDeviceBuffer(&gpu_nbv->cell, natoms_total, &gpu_nbv->ncell, &gpu_nbv->ncell_alloc, nullptr);
-        copyToDeviceBuffer(&gpu_nbv->cell, cell, 0, natoms_total, stream, GpuApiCallBehavior::Async, nullptr);
-    }
-
-    return;
-}
-
 } // namespace Nbnxm
index 2201009170f84792b21a2536deb417cce82a4dc2..9cddbc199942b957298edc794e9cea840628a7b2 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -157,7 +158,7 @@ __launch_bounds__(THREADS_PER_BLOCK)
         __global__ void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_cuda)
 #    endif /* CALC_ENERGIES */
 #endif     /* PRUNE_NBL */
-                (const cu_atomdata_t atdat, const cu_nbparam_t nbparam, const cu_plist_t plist, bool bCalcFshift)
+                (const cu_atomdata_t atdat, const NBParamGpu nbparam, const Nbnxm::gpu_plist plist, bool bCalcFshift)
 #ifdef FUNCTION_DECLARATION_ONLY
                         ; /* Only do function declaration, omit the function body. */
 #else
@@ -243,11 +244,11 @@ __launch_bounds__(THREADS_PER_BLOCK)
     unsigned int wexcl, imask, mask_ji;
     float4       xqbuf;
     float3       xi, xj, rv, f_ij, fcj_buf;
-    float3       fci_buf[c_numClPerSupercl]; /* i force buffer */
+    float3       fci_buf[c_nbnxnGpuNumClusterPerSupercluster]; /* i force buffer */
     nbnxn_sci_t  nb_sci;
 
-    /*! i-cluster interaction mask for a super-cluster with all c_numClPerSupercl=8 bits set */
-    const unsigned superClInteractionMask = ((1U << c_numClPerSupercl) - 1U);
+    /*! i-cluster interaction mask for a super-cluster with all c_nbnxnGpuNumClusterPerSupercluster=8 bits set */
+    const unsigned superClInteractionMask = ((1U << c_nbnxnGpuNumClusterPerSupercluster) - 1U);
 
     /*********************************************************************
      * Set up shared memory pointers.
@@ -261,7 +262,7 @@ __launch_bounds__(THREADS_PER_BLOCK)
 
     /* shmem buffer for i x+q pre-loading */
     float4* xqib = (float4*)sm_nextSlotPtr;
-    sm_nextSlotPtr += (c_numClPerSupercl * c_clSize * sizeof(*xqib));
+    sm_nextSlotPtr += (c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(*xqib));
 
     /* shmem buffer for cj, for each warp separately */
     int* cjs = (int*)(sm_nextSlotPtr);
@@ -272,11 +273,11 @@ __launch_bounds__(THREADS_PER_BLOCK)
 #    ifndef LJ_COMB
     /* shmem buffer for i atom-type pre-loading */
     int* atib = (int*)sm_nextSlotPtr;
-    sm_nextSlotPtr += (c_numClPerSupercl * c_clSize * sizeof(*atib));
+    sm_nextSlotPtr += (c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(*atib));
 #    else
     /* shmem buffer for i-atom LJ combination rule parameters */
     float2* ljcpib = (float2*)sm_nextSlotPtr;
-    sm_nextSlotPtr += (c_numClPerSupercl * c_clSize * sizeof(*ljcpib));
+    sm_nextSlotPtr += (c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(*ljcpib));
 #    endif
     /*********************************************************************/
 
@@ -288,7 +289,7 @@ __launch_bounds__(THREADS_PER_BLOCK)
     if (tidxz == 0)
     {
         /* Pre-load i-atom x and q into shared memory */
-        ci = sci * c_numClPerSupercl + tidxj;
+        ci = sci * c_nbnxnGpuNumClusterPerSupercluster + tidxj;
         ai = ci * c_clSize + tidxi;
 
         float* shiftptr = (float*)&shift_vec[nb_sci.shift];
@@ -306,7 +307,7 @@ __launch_bounds__(THREADS_PER_BLOCK)
     }
     __syncthreads();
 
-    for (i = 0; i < c_numClPerSupercl; i++)
+    for (i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i++)
     {
         fci_buf[i] = make_float3(0.0f);
     }
@@ -323,10 +324,10 @@ __launch_bounds__(THREADS_PER_BLOCK)
     E_el         = 0.0f;
 
 #        ifdef EXCLUSION_FORCES /* Ewald or RF */
-    if (nb_sci.shift == CENTRAL && pl_cj4[cij4_start].cj[0] == sci * c_numClPerSupercl)
+    if (nb_sci.shift == CENTRAL && pl_cj4[cij4_start].cj[0] == sci * c_nbnxnGpuNumClusterPerSupercluster)
     {
         /* we have the diagonal: add the charge and LJ self interaction energy term */
-        for (i = 0; i < c_numClPerSupercl; i++)
+        for (i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i++)
         {
 #            if defined EL_EWALD_ANY || defined EL_RF || defined EL_CUTOFF
             qi = xqib[i * c_clSize + tidxi].w;
@@ -335,12 +336,13 @@ __launch_bounds__(THREADS_PER_BLOCK)
 
 #            ifdef LJ_EWALD
 #                if DISABLE_CUDA_TEXTURES
-            E_lj += LDG(
-                    &nbparam.nbfp[atom_types[(sci * c_numClPerSupercl + i) * c_clSize + tidxi] * (ntypes + 1) * 2]);
+            E_lj += LDG(&nbparam.nbfp[atom_types[(sci * c_nbnxnGpuNumClusterPerSupercluster + i) * c_clSize + tidxi]
+                                      * (ntypes + 1) * 2]);
 #                else
             E_lj += tex1Dfetch<float>(
                     nbparam.nbfp_texobj,
-                    atom_types[(sci * c_numClPerSupercl + i) * c_clSize + tidxi] * (ntypes + 1) * 2);
+                    atom_types[(sci * c_nbnxnGpuNumClusterPerSupercluster + i) * c_clSize + tidxi]
+                            * (ntypes + 1) * 2);
 #                endif
 #            endif
         }
@@ -396,9 +398,9 @@ __launch_bounds__(THREADS_PER_BLOCK)
                Tested with up to nvcc 7.5 */
             for (jm = 0; jm < c_nbnxnGpuJgroupSize; jm++)
             {
-                if (imask & (superClInteractionMask << (jm * c_numClPerSupercl)))
+                if (imask & (superClInteractionMask << (jm * c_nbnxnGpuNumClusterPerSupercluster)))
                 {
-                    mask_ji = (1U << (jm * c_numClPerSupercl));
+                    mask_ji = (1U << (jm * c_nbnxnGpuNumClusterPerSupercluster));
 
                     cj = cjs[jm + (tidxj & 4) * c_nbnxnGpuJgroupSize / c_splitClSize];
                     aj = cj * c_clSize + tidxj;
@@ -418,11 +420,11 @@ __launch_bounds__(THREADS_PER_BLOCK)
 #    if !defined PRUNE_NBL
 #        pragma unroll 8
 #    endif
-                    for (i = 0; i < c_numClPerSupercl; i++)
+                    for (i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i++)
                     {
                         if (imask & mask_ji)
                         {
-                            ci = sci * c_numClPerSupercl + i; /* i cluster index */
+                            ci = sci * c_nbnxnGpuNumClusterPerSupercluster + i; /* i cluster index */
 
                             /* all threads load an atom from i cluster ci into shmem! */
                             xqbuf = xqib[i * c_clSize + tidxi];
@@ -474,7 +476,7 @@ __launch_bounds__(THREADS_PER_BLOCK)
 #    endif     /* LJ_COMB */
 
                                 // Ensure distance do not become so small that r^-12 overflows
-                                r2 = max(r2, NBNXN_MIN_RSQ);
+                                r2 = max(r2, c_nbnxnMinDistanceSquared);
 
                                 inv_r  = rsqrt(r2);
                                 inv_r2 = inv_r * inv_r;
@@ -628,9 +630,9 @@ __launch_bounds__(THREADS_PER_BLOCK)
     float fshift_buf = 0.0f;
 
     /* reduce i forces */
-    for (i = 0; i < c_numClPerSupercl; i++)
+    for (i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i++)
     {
-        ai = (sci * c_numClPerSupercl + i) * c_clSize + tidxi;
+        ai = (sci * c_nbnxnGpuNumClusterPerSupercluster + i) * c_clSize + tidxi;
         reduce_force_i_warp_shfl(fci_buf[i], f, &fshift_buf, bCalcFshift, tidxj, ai, c_fullWarpMask);
     }
 
index 945b1912fced824dc8345836bd2c6d0d455a9d42..fb8ebb2e766fec2574438098b1ef3efe34b52758 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -39,7 +39,7 @@
 #ifndef FUNCTION_DECLARATION_ONLY
 /* Instantiate external template functions */
 template __global__ void
-nbnxn_kernel_prune_cuda<false>(const cu_atomdata_t, const cu_nbparam_t, const cu_plist_t, int, int);
+nbnxn_kernel_prune_cuda<false>(const cu_atomdata_t, const NBParamGpu, const Nbnxm::gpu_plist, int, int);
 template __global__ void
-nbnxn_kernel_prune_cuda<true>(const cu_atomdata_t, const cu_nbparam_t, const cu_plist_t, int, int);
+nbnxn_kernel_prune_cuda<true>(const cu_atomdata_t, const NBParamGpu, const Nbnxm::gpu_plist, int, int);
 #endif
index 8570e55c0bd09774b0cd5fabc5976fb4d6654c1b..563e1edc0c74b4faa13492f3d2af70b19c5a5d4d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  */
 template<bool haveFreshList>
 __launch_bounds__(THREADS_PER_BLOCK, MIN_BLOCKS_PER_MP) __global__
-        void nbnxn_kernel_prune_cuda(const cu_atomdata_t atdat,
-                                     const cu_nbparam_t  nbparam,
-                                     const cu_plist_t    plist,
-                                     int                 numParts,
-                                     int                 part)
+        void nbnxn_kernel_prune_cuda(const cu_atomdata_t    atdat,
+                                     const NBParamGpu       nbparam,
+                                     const Nbnxm::gpu_plist plist,
+                                     int                    numParts,
+                                     int                    part)
 #ifdef FUNCTION_DECLARATION_ONLY
                 ; /* Only do function declaration, omit the function body. */
 
 // Add extern declarations so each translation unit understands that
 // there will be a definition provided.
 extern template __global__ void
-nbnxn_kernel_prune_cuda<true>(const cu_atomdata_t, const cu_nbparam_t, const cu_plist_t, int, int);
+nbnxn_kernel_prune_cuda<true>(const cu_atomdata_t, const NBParamGpu, const Nbnxm::gpu_plist, int, int);
 extern template __global__ void
-nbnxn_kernel_prune_cuda<false>(const cu_atomdata_t, const cu_nbparam_t, const cu_plist_t, int, int);
+nbnxn_kernel_prune_cuda<false>(const cu_atomdata_t, const NBParamGpu, const Nbnxm::gpu_plist, int, int);
 #else
 {
 
@@ -152,7 +152,7 @@ nbnxn_kernel_prune_cuda<false>(const cu_atomdata_t, const cu_nbparam_t, const cu
 
     /* shmem buffer for i x+q pre-loading */
     float4* xib = (float4*)sm_nextSlotPtr;
-    sm_nextSlotPtr += (c_numClPerSupercl * c_clSize * sizeof(*xib));
+    sm_nextSlotPtr += (c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(*xib));
 
     /* shmem buffer for cj, for each warp separately */
     int* cjs = (int*)(sm_nextSlotPtr);
@@ -171,7 +171,7 @@ nbnxn_kernel_prune_cuda<false>(const cu_atomdata_t, const cu_nbparam_t, const cu
     if (tidxz == 0)
     {
         /* Pre-load i-atom x and q into shared memory */
-        int ci = sci * c_numClPerSupercl + tidxj;
+        int ci = sci * c_nbnxnGpuNumClusterPerSupercluster + tidxj;
         int ai = ci * c_clSize + tidxi;
 
         /* We don't need q, but using float4 in shmem avoids bank conflicts.
@@ -220,9 +220,9 @@ nbnxn_kernel_prune_cuda<false>(const cu_atomdata_t, const cu_nbparam_t, const cu
 #    pragma unroll 4
             for (int jm = 0; jm < c_nbnxnGpuJgroupSize; jm++)
             {
-                if (imaskCheck & (superClInteractionMask << (jm * c_numClPerSupercl)))
+                if (imaskCheck & (superClInteractionMask << (jm * c_nbnxnGpuNumClusterPerSupercluster)))
                 {
-                    unsigned int mask_ji = (1U << (jm * c_numClPerSupercl));
+                    unsigned int mask_ji = (1U << (jm * c_nbnxnGpuNumClusterPerSupercluster));
 
                     int cj = cjs[jm + (tidxj & 4) * c_nbnxnGpuJgroupSize / c_splitClSize];
                     int aj = cj * c_clSize + tidxj;
@@ -232,7 +232,7 @@ nbnxn_kernel_prune_cuda<false>(const cu_atomdata_t, const cu_nbparam_t, const cu
                     float3 xj  = make_float3(tmp.x, tmp.y, tmp.z);
 
 #    pragma unroll 8
-                    for (int i = 0; i < c_numClPerSupercl; i++)
+                    for (int i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i++)
                     {
                         if (imaskCheck & mask_ji)
                         {
index e3724d2a1dd955210680f9eb3dba3ca31e395584..4850298f8f154082b8b19c2a395aab938fe8d59c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -67,8 +68,9 @@ static const int __device__ c_clSizeSq = c_clSize * c_clSize;
 static const int __device__ c_splitClSize = c_clSize / c_nbnxnGpuClusterpairSplit;
 /*! \brief Stride in the force accumualation buffer */
 static const int __device__ c_fbufStride = c_clSizeSq;
-/*! \brief i-cluster interaction mask for a super-cluster with all c_numClPerSupercl=8 bits set */
-static const unsigned __device__ superClInteractionMask = ((1U << c_numClPerSupercl) - 1U);
+/*! \brief i-cluster interaction mask for a super-cluster with all c_nbnxnGpuNumClusterPerSupercluster=8 bits set */
+static const unsigned __device__ superClInteractionMask =
+        ((1U << c_nbnxnGpuNumClusterPerSupercluster) - 1U);
 
 static const float __device__ c_oneSixth    = 0.16666667f;
 static const float __device__ c_oneTwelveth = 0.08333333f;
@@ -88,7 +90,7 @@ static __forceinline__ __device__ void
 
 /*! Apply force switch,  force + energy version. */
 static __forceinline__ __device__ void
-                       calculate_force_switch_F(const cu_nbparam_t nbparam, float c6, float c12, float inv_r, float r2, float* F_invr)
+                       calculate_force_switch_F(const NBParamGpu nbparam, float c6, float c12, float inv_r, float r2, float* F_invr)
 {
     float r, r_switch;
 
@@ -107,13 +109,13 @@ static __forceinline__ __device__ void
 }
 
 /*! Apply force switch, force-only version. */
-static __forceinline__ __device__ void calculate_force_switch_F_E(const cu_nbparam_t nbparam,
-                                                                  float              c6,
-                                                                  float              c12,
-                                                                  float              inv_r,
-                                                                  float              r2,
-                                                                  float*             F_invr,
-                                                                  float*             E_lj)
+static __forceinline__ __device__ void calculate_force_switch_F_E(const NBParamGpu nbparam,
+                                                                  float            c6,
+                                                                  float            c12,
+                                                                  float            inv_r,
+                                                                  float            r2,
+                                                                  float*           F_invr,
+                                                                  float*           E_lj)
 {
     float r, r_switch;
 
@@ -140,7 +142,7 @@ static __forceinline__ __device__ void calculate_force_switch_F_E(const cu_nbpar
 
 /*! Apply potential switch, force-only version. */
 static __forceinline__ __device__ void
-                       calculate_potential_switch_F(const cu_nbparam_t nbparam, float inv_r, float r2, float* F_invr, float* E_lj)
+                       calculate_potential_switch_F(const NBParamGpu nbparam, float inv_r, float r2, float* F_invr, float* E_lj)
 {
     float r, r_switch;
     float sw, dsw;
@@ -168,7 +170,7 @@ static __forceinline__ __device__ void
 
 /*! Apply potential switch, force + energy version. */
 static __forceinline__ __device__ void
-                       calculate_potential_switch_F_E(const cu_nbparam_t nbparam, float inv_r, float r2, float* F_invr, float* E_lj)
+                       calculate_potential_switch_F_E(const NBParamGpu nbparam, float inv_r, float r2, float* F_invr, float* E_lj)
 {
     float r, r_switch;
     float sw, dsw;
@@ -199,7 +201,7 @@ static __forceinline__ __device__ void
  *  Depending on what is supported, it fetches parameters either
  *  using direct load, texture objects, or texrefs.
  */
-static __forceinline__ __device__ float calculate_lj_ewald_c6grid(const cu_nbparam_t nbparam, int typei, int typej)
+static __forceinline__ __device__ float calculate_lj_ewald_c6grid(const NBParamGpu nbparam, int typei, int typej)
 {
 #    if DISABLE_CUDA_TEXTURES
     return LDG(&nbparam.nbfp_comb[2 * typei]) * LDG(&nbparam.nbfp_comb[2 * typej]);
@@ -213,14 +215,14 @@ static __forceinline__ __device__ float calculate_lj_ewald_c6grid(const cu_nbpar
 /*! Calculate LJ-PME grid force contribution with
  *  geometric combination rule.
  */
-static __forceinline__ __device__ void calculate_lj_ewald_comb_geom_F(const cu_nbparam_t nbparam,
-                                                                      int                typei,
-                                                                      int                typej,
-                                                                      float              r2,
-                                                                      float              inv_r2,
-                                                                      float              lje_coeff2,
-                                                                      float  lje_coeff6_6,
-                                                                      float* F_invr)
+static __forceinline__ __device__ void calculate_lj_ewald_comb_geom_F(const NBParamGpu nbparam,
+                                                                      int              typei,
+                                                                      int              typej,
+                                                                      float            r2,
+                                                                      float            inv_r2,
+                                                                      float            lje_coeff2,
+                                                                      float            lje_coeff6_6,
+                                                                      float*           F_invr)
 {
     float c6grid, inv_r6_nm, cr2, expmcr2, poly;
 
@@ -240,12 +242,12 @@ static __forceinline__ __device__ void calculate_lj_ewald_comb_geom_F(const cu_n
 /*! Calculate LJ-PME grid force + energy contribution with
  *  geometric combination rule.
  */
-static __forceinline__ __device__ void calculate_lj_ewald_comb_geom_F_E(const cu_nbparam_t nbparam,
-                                                                        int                typei,
-                                                                        int                typej,
-                                                                        float              r2,
-                                                                        float              inv_r2,
-                                                                        float  lje_coeff2,
+static __forceinline__ __device__ void calculate_lj_ewald_comb_geom_F_E(const NBParamGpu nbparam,
+                                                                        int              typei,
+                                                                        int              typej,
+                                                                        float            r2,
+                                                                        float            inv_r2,
+                                                                        float            lje_coeff2,
                                                                         float  lje_coeff6_6,
                                                                         float  int_bit,
                                                                         float* F_invr,
@@ -274,7 +276,7 @@ static __forceinline__ __device__ void calculate_lj_ewald_comb_geom_F_E(const cu
  *  Depending on what is supported, it fetches parameters either
  *  using direct load, texture objects, or texrefs.
  */
-static __forceinline__ __device__ float2 fetch_nbfp_comb_c6_c12(const cu_nbparam_t nbparam, int type)
+static __forceinline__ __device__ float2 fetch_nbfp_comb_c6_c12(const NBParamGpu nbparam, int type)
 {
     float2 c6c12;
 #    if DISABLE_CUDA_TEXTURES
@@ -297,16 +299,16 @@ static __forceinline__ __device__ float2 fetch_nbfp_comb_c6_c12(const cu_nbparam
  *  We use a single F+E kernel with conditional because the performance impact
  *  of this is pretty small and LB on the CPU is anyway very slow.
  */
-static __forceinline__ __device__ void calculate_lj_ewald_comb_LB_F_E(const cu_nbparam_t nbparam,
-                                                                      int                typei,
-                                                                      int                typej,
-                                                                      float              r2,
-                                                                      float              inv_r2,
-                                                                      float              lje_coeff2,
-                                                                      float  lje_coeff6_6,
-                                                                      float  int_bit,
-                                                                      float* F_invr,
-                                                                      float* E_lj)
+static __forceinline__ __device__ void calculate_lj_ewald_comb_LB_F_E(const NBParamGpu nbparam,
+                                                                      int              typei,
+                                                                      int              typej,
+                                                                      float            r2,
+                                                                      float            inv_r2,
+                                                                      float            lje_coeff2,
+                                                                      float            lje_coeff6_6,
+                                                                      float            int_bit,
+                                                                      float*           F_invr,
+                                                                      float*           E_lj)
 {
     float c6grid, inv_r6_nm, cr2, expmcr2, poly;
     float sigma, sigma2, epsilon;
@@ -346,7 +348,7 @@ static __forceinline__ __device__ void calculate_lj_ewald_comb_LB_F_E(const cu_n
  *  Depending on what is supported, it fetches parameters either
  *  using direct load, texture objects, or texrefs.
  */
-static __forceinline__ __device__ float2 fetch_coulomb_force_r(const cu_nbparam_t nbparam, int index)
+static __forceinline__ __device__ float2 fetch_coulomb_force_r(const NBParamGpu nbparam, int index)
 {
     float2 d;
 
@@ -377,7 +379,7 @@ __forceinline__ __host__ __device__ T lerp(T d0, T d1, T t)
 
 /*! Interpolate Ewald coulomb force correction using the F*r table.
  */
-static __forceinline__ __device__ float interpolate_coulomb_force_r(const cu_nbparam_t nbparam, float r)
+static __forceinline__ __device__ float interpolate_coulomb_force_r(const NBParamGpu nbparam, float r)
 {
     float normalized = nbparam.coulomb_tab_scale * r;
     int   index      = (int)normalized;
@@ -393,7 +395,7 @@ static __forceinline__ __device__ float interpolate_coulomb_force_r(const cu_nbp
  *  Depending on what is supported, it fetches parameters either
  *  using direct load, texture objects, or texrefs.
  */
-static __forceinline__ __device__ void fetch_nbfp_c6_c12(float& c6, float& c12, const cu_nbparam_t nbparam, int baseIndex)
+static __forceinline__ __device__ void fetch_nbfp_c6_c12(float& c6, float& c12, const NBParamGpu nbparam, int baseIndex)
 {
 #    if DISABLE_CUDA_TEXTURES
     /* Force an 8-byte fetch to save a memory instruction. */
index 58647c3181e04ad9aed3b0896e85e52b5223f5c1..6bee7c354b7e2b1caec3620390c00fd2d4d9fbc1 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d65d308c48f612bf2ad75d8c078ed39d5a51abe1..7c92a1abdc648ab403a495a0687ce796a3e19c66 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2012, The GROMACS development team.
- * Copyright (c) 2013-2019, by the GROMACS development team, led by
+ * Copyright (c) 2013-2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,6 +49,7 @@
 #include "gromacs/gpu_utils/cuda_arch_utils.cuh"
 #include "gromacs/gpu_utils/cudautils.cuh"
 #include "gromacs/gpu_utils/devicebuffer.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
 #include "gromacs/gpu_utils/gputraits.cuh"
 #include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/nbnxm/gpu_types_common.h"
@@ -72,63 +73,13 @@ const int c_cudaPruneKernelJ4Concurrency = GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY
 
 /* TODO: consider moving this to kernel_utils */
 /* Convenience defines */
-/*! \brief number of clusters per supercluster. */
-static const int c_numClPerSupercl = c_nbnxnGpuNumClusterPerSupercluster;
 /*! \brief cluster size = number of atoms per cluster. */
-static const int c_clSize = c_nbnxnGpuClusterSize;
-
-/*! \brief Electrostatic CUDA kernel flavors.
- *
- *  Types of electrostatics implementations available in the CUDA non-bonded
- *  force kernels. These represent both the electrostatics types implemented
- *  by the kernels (cut-off, RF, and Ewald - a subset of what's defined in
- *  enums.h) as well as encode implementation details analytical/tabulated
- *  and single or twin cut-off (for Ewald kernels).
- *  Note that the cut-off and RF kernels have only analytical flavor and unlike
- *  in the CPU kernels, the tabulated kernels are ATM Ewald-only.
- *
- *  The row-order of pointers to different electrostatic kernels defined in
- *  nbnxn_cuda.cu by the nb_*_kfunc_ptr function pointer table
- *  should match the order of enumerated types below.
- */
-enum eelCu
-{
-    eelCuCUT,
-    eelCuRF,
-    eelCuEWALD_TAB,
-    eelCuEWALD_TAB_TWIN,
-    eelCuEWALD_ANA,
-    eelCuEWALD_ANA_TWIN,
-    eelCuNR
-};
-
-/*! \brief VdW CUDA kernel flavors.
- *
- * The enumerates values correspond to the LJ implementations in the CUDA non-bonded
- * kernels.
- *
- * The column-order of pointers to different electrostatic kernels defined in
- * nbnxn_cuda.cu by the nb_*_kfunc_ptr function pointer table
- * should match the order of enumerated types below.
- */
-enum evdwCu
-{
-    evdwCuCUT,
-    evdwCuCUTCOMBGEOM,
-    evdwCuCUTCOMBLB,
-    evdwCuFSWITCH,
-    evdwCuPSWITCH,
-    evdwCuEWALDGEOM,
-    evdwCuEWALDLB,
-    evdwCuNR
-};
+static constexpr int c_clSize = c_nbnxnGpuClusterSize;
 
 /* All structs prefixed with "cu_" hold data used in GPU calculations and
  * are passed to the kernels, except cu_timers_t. */
 /*! \cond */
 typedef struct cu_atomdata cu_atomdata_t;
-typedef struct cu_nbparam  cu_nbparam_t;
-typedef struct nb_staging  nb_staging_t;
 /*! \endcond */
 
 
@@ -138,11 +89,14 @@ typedef struct nb_staging  nb_staging_t;
  *  The energies/shift forces get downloaded here first, before getting added
  *  to the CPU-side aggregate values.
  */
-struct nb_staging
+struct nb_staging_t
 {
-    float*  e_lj;   /**< LJ energy            */
-    float*  e_el;   /**< electrostatic energy */
-    float3* fshift; /**< shift forces         */
+    //! LJ energy
+    float* e_lj = nullptr;
+    //! electrostatic energy
+    float* e_el = nullptr;
+    //! shift forces
+    float3* fshift = nullptr;
 };
 
 /** \internal
@@ -150,72 +104,39 @@ struct nb_staging
  */
 struct cu_atomdata
 {
-    int natoms;       /**< number of atoms                              */
-    int natoms_local; /**< number of local atoms                        */
-    int nalloc;       /**< allocation size for the atom data (xq, f)    */
-
-    float4* xq; /**< atom coordinates + charges, size natoms      */
-    float3* f;  /**< force output array, size natoms              */
-
-    float* e_lj; /**< LJ energy output, size 1                     */
-    float* e_el; /**< Electrostatics energy input, size 1          */
-
-    float3* fshift; /**< shift forces                                 */
-
-    int     ntypes;     /**< number of atom types                         */
-    int*    atom_types; /**< atom type indices, size natoms               */
-    float2* lj_comb;    /**< sqrt(c6),sqrt(c12) size natoms               */
-
-    float3* shift_vec;         /**< shifts                                       */
-    bool    bShiftVecUploaded; /**< true if the shift vector has been uploaded   */
+    //! number of atoms
+    int natoms;
+    //! number of local atoms
+    int natoms_local;
+    //! allocation size for the atom data (xq, f)
+    int nalloc;
+
+    //! atom coordinates + charges, size natoms
+    DeviceBuffer<float4> xq;
+    //! force output array, size natoms
+    DeviceBuffer<float3> f;
+
+    //! LJ energy output, size 1
+    DeviceBuffer<float> e_lj;
+    //! Electrostatics energy input, size 1
+    DeviceBuffer<float> e_el;
+
+    //! shift forces
+    DeviceBuffer<float3> fshift;
+
+    //! number of atom types
+    int ntypes;
+    //! atom type indices, size natoms
+    DeviceBuffer<int> atom_types;
+    //! sqrt(c6),sqrt(c12) size natoms
+    DeviceBuffer<float2> lj_comb;
+
+    //! shifts
+    DeviceBuffer<float3> shift_vec;
+    //! true if the shift vector has been uploaded
+    bool bShiftVecUploaded;
 };
 
-/** \internal
- * \brief Parameters required for the CUDA nonbonded calculations.
- */
-struct cu_nbparam
-{
-
-    int eeltype; /**< type of electrostatics, takes values from #eelCu */
-    int vdwtype; /**< type of VdW impl., takes values from #evdwCu     */
-
-    float epsfac;      /**< charge multiplication factor                      */
-    float c_rf;        /**< Reaction-field/plain cutoff electrostatics const. */
-    float two_k_rf;    /**< Reaction-field electrostatics constant            */
-    float ewald_beta;  /**< Ewald/PME parameter                               */
-    float sh_ewald;    /**< Ewald/PME correction term substracted from the direct-space potential */
-    float sh_lj_ewald; /**< LJ-Ewald/PME correction term added to the correction potential        */
-    float ewaldcoeff_lj; /**< LJ-Ewald/PME coefficient                          */
-
-    float rcoulomb_sq; /**< Coulomb cut-off squared                           */
-
-    float rvdw_sq;           /**< VdW cut-off squared                               */
-    float rvdw_switch;       /**< VdW switched cut-off                              */
-    float rlistOuter_sq;     /**< Full, outer pair-list cut-off squared             */
-    float rlistInner_sq;     /**< Inner, dynamic pruned pair-list cut-off squared   */
-    bool  useDynamicPruning; /**< True if we use dynamic pair-list pruning          */
-
-    shift_consts_t  dispersion_shift; /**< VdW shift dispersion constants           */
-    shift_consts_t  repulsion_shift;  /**< VdW shift repulsion constants            */
-    switch_consts_t vdw_switch;       /**< VdW switch constants                     */
-
-    /* LJ non-bonded parameters - accessed through texture memory */
-    float*              nbfp; /**< nonbonded parameter table with C6/C12 pairs per atom type-pair, 2*ntype^2 elements */
-    cudaTextureObject_t nbfp_texobj; /**< texture object bound to nbfp */
-    float*              nbfp_comb; /**< nonbonded parameter table per atom type, 2*ntype elements */
-    cudaTextureObject_t nbfp_comb_texobj; /**< texture object bound to nbfp_texobj */
-
-    /* Ewald Coulomb force table data - accessed through texture memory */
-    float               coulomb_tab_scale;  /**< table scale/spacing                        */
-    float*              coulomb_tab;        /**< pointer to the table in the device memory  */
-    cudaTextureObject_t coulomb_tab_texobj; /**< texture object bound to coulomb_tab        */
-};
-
-/** \internal
- * \brief Pair list data.
- */
-using cu_plist_t = Nbnxm::gpu_plist;
-
 /** \internal
  * \brief Typedef of actual timer type.
  */
@@ -223,85 +144,84 @@ typedef struct Nbnxm::gpu_timers_t cu_timers_t;
 
 class GpuEventSynchronizer;
 
-/** \internal
+/*! \internal
  * \brief Main data structure for CUDA nonbonded force calculations.
  */
-struct gmx_nbnxn_cuda_t
+struct NbnxmGpu
 {
-    //! CUDA device information
-    const gmx_device_info_t* dev_info;
-    //! true if doing both local/non-local NB work on GPU
-    bool bUseTwoStreams;
-    //! atom data
-    cu_atomdata_t* atdat;
-    //! f buf ops cell index mapping
-    int* cell;
-    //! number of indices in cell buffer
-    int ncell;
-    //! number of indices allocated in cell buffer
-    int ncell_alloc;
-    //! array of atom indices
-    int* atomIndices;
-    //! size of atom indices
-    int atomIndicesSize;
-    //! size of atom indices allocated in device buffer
-    int atomIndicesSize_alloc;
-    //! x buf ops num of atoms
-    int* cxy_na;
-    //! number of elements in cxy_na
-    int ncxy_na;
-    //! number of elements allocated allocated in device buffer
-    int ncxy_na_alloc;
-    //! x buf ops cell index mapping
-    int* cxy_ind;
-    //! number of elements in cxy_ind
-    int ncxy_ind;
-    //! number of elements allocated allocated in device buffer
-    int ncxy_ind_alloc;
-    //! parameters required for the non-bonded calc.
-    cu_nbparam_t* nbparam;
-    //! pair-list data structures (local and non-local)
-    gmx::EnumerationArray<Nbnxm::InteractionLocality, cu_plist_t*> plist;
-    //! staging area where fshift/energies get downloaded
-    nb_staging_t nbst;
-    //! local and non-local GPU streams
-    gmx::EnumerationArray<Nbnxm::InteractionLocality, cudaStream_t> stream;
-
-    /** events used for synchronization */
-    cudaEvent_t nonlocal_done; /**< event triggered when the non-local non-bonded kernel
-                                  is done (and the local transfer can proceed)           */
-    cudaEvent_t misc_ops_and_local_H2D_done; /**< event triggered when the tasks issued in
-                                                the local stream that need to precede the
-                                                non-local force or buffer operation calculations are
-                                                done (e.g. f buffer 0-ing, local x/q H2D, buffer op
-                                                initialization in local stream that is required also
-                                                by nonlocal stream ) */
-
-    //! True if there has been local/nonlocal GPU work, either bonded or nonbonded, scheduled
-    //  to be executed in the current domain. As long as bonded work is not split up into
-    //  local/nonlocal, if there is bonded GPU work, both flags will be true.
-    gmx::EnumerationArray<Nbnxm::InteractionLocality, bool> haveWork;
-
-    /*! \brief Pointer to event synchronizer triggered when the local GPU buffer ops / reduction is complete
+    /*! \brief GPU device context.
      *
-     * \note That the synchronizer is managed outside of this module in StatePropagatorDataGpu.
+     * \todo Make it constant reference, once NbnxmGpu is a proper class.
      */
-    GpuEventSynchronizer* localFReductionDone;
+    const DeviceContext* deviceContext_;
+    /*! \brief true if doing both local/non-local NB work on GPU */
+    bool bUseTwoStreams = false;
+    /*! \brief atom data */
+    cu_atomdata_t* atdat = nullptr;
+    /*! \brief array of atom indices */
+    int* atomIndices = nullptr;
+    /*! \brief size of atom indices */
+    int atomIndicesSize = 0;
+    /*! \brief size of atom indices allocated in device buffer */
+    int atomIndicesSize_alloc = 0;
+    /*! \brief x buf ops num of atoms */
+    int* cxy_na = nullptr;
+    /*! \brief number of elements in cxy_na */
+    int ncxy_na = 0;
+    /*! \brief number of elements allocated allocated in device buffer */
+    int ncxy_na_alloc = 0;
+    /*! \brief x buf ops cell index mapping */
+    int* cxy_ind = nullptr;
+    /*! \brief number of elements in cxy_ind */
+    int ncxy_ind = 0;
+    /*! \brief number of elements allocated allocated in device buffer */
+    int ncxy_ind_alloc = 0;
+    /*! \brief parameters required for the non-bonded calc. */
+    NBParamGpu* nbparam = nullptr;
+    /*! \brief pair-list data structures (local and non-local) */
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, Nbnxm::gpu_plist*> plist = { { nullptr } };
+    /*! \brief staging area where fshift/energies get downloaded */
+    nb_staging_t nbst;
+    /*! \brief local and non-local GPU streams */
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, const DeviceStream*> deviceStreams;
+
+    /*! \brief Events used for synchronization */
+    /*! \{ */
+    /*! \brief Event triggered when the non-local non-bonded
+     * kernel is done (and the local transfer can proceed) */
+    cudaEvent_t nonlocal_done = nullptr;
+    /*! \brief Event triggered when the tasks issued in the local
+     * stream that need to precede the non-local force or buffer
+     * operation calculations are done (e.g. f buffer 0-ing, local
+     * x/q H2D, buffer op initialization in local stream that is
+     * required also by nonlocal stream ) */
+    cudaEvent_t misc_ops_and_local_H2D_done = nullptr;
+    /*! \} */
+
+    /*! \brief True if there is work for the current domain in the
+     * respective locality.
+     *
+     * This includes local/nonlocal GPU work, either bonded or
+     * nonbonded, scheduled to be executed in the current
+     * domain. As long as bonded work is not split up into
+     * local/nonlocal, if there is bonded GPU work, both flags
+     * will be true. */
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, bool> haveWork = { { false } };
 
-    GpuEventSynchronizer* xNonLocalCopyD2HDone; /**< event triggered when
-                                                   non-local coordinate buffer has been
-                                                   copied from device to host*/
+    /*! \brief Event triggered when non-local coordinate buffer
+     * has been copied from device to host. */
+    GpuEventSynchronizer* xNonLocalCopyD2HDone = nullptr;
 
     /* NOTE: With current CUDA versions (<=5.0) timing doesn't work with multiple
      * concurrent streams, so we won't time if both l/nl work is done on GPUs.
      * Timer init/uninit is still done even with timing off so only the condition
      * setting bDoTime needs to be change if this CUDA "feature" gets fixed. */
-    //! True if event-based timing is enabled.
-    bool bDoTime;
-    //! CUDA event-based timers.
-    cu_timers_t* timers;
-    //! Timing data. TODO: deprecate this and query timers for accumulated data instead
-    gmx_wallclock_gpu_nbnxn_t* timings;
+    /*! \brief True if event-based timing is enabled. */
+    bool bDoTime = false;
+    /*! \brief CUDA event-based timers. */
+    cu_timers_t* timers = nullptr;
+    /*! \brief Timing data. TODO: deprecate this and query timers for accumulated data instead */
+    gmx_wallclock_gpu_nbnxn_t* timings = nullptr;
 };
 
 #endif /* NBNXN_CUDA_TYPES_H */
index d209f0319abcd55f4dcd3c66df03d0e0f8a9d25d..7d41ee618022e3dbb6bfb18b6c9f22aeeda9c622 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <string>
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 #    include "cuda/nbnxm_cuda_types.h"
 #endif
 
-#if GMX_GPU == GMX_GPU_OPENCL
+#if GMX_GPU_OPENCL
 #    include "opencl/nbnxm_ocl_types.h"
 #endif
 
@@ -124,9 +124,7 @@ static inline InteractionLocality gpuAtomToInteractionLocality(const AtomLocalit
 
 
 //NOLINTNEXTLINE(misc-definitions-in-headers)
-void setupGpuShortRangeWork(gmx_nbnxn_gpu_t*               nb,
-                            const gmx::GpuBonded*          gpuBonded,
-                            const gmx::InteractionLocality iLocality)
+void setupGpuShortRangeWork(NbnxmGpu* nb, const gmx::GpuBonded* gpuBonded, const gmx::InteractionLocality iLocality)
 {
     GMX_ASSERT(nb, "Need a valid nbnxn_gpu object");
 
@@ -146,13 +144,13 @@ void setupGpuShortRangeWork(gmx_nbnxn_gpu_t*               nb,
  * \param[inout]  nb        Pointer to the nonbonded GPU data structure
  * \param[in]     iLocality Interaction locality identifier
  */
-static bool haveGpuShortRangeWork(const gmx_nbnxn_gpu_t& nb, const gmx::InteractionLocality iLocality)
+static bool haveGpuShortRangeWork(const NbnxmGpu& nb, const gmx::InteractionLocality iLocality)
 {
     return nb.haveWork[iLocality];
 }
 
 //NOLINTNEXTLINE(misc-definitions-in-headers)
-bool haveGpuShortRangeWork(const gmx_nbnxn_gpu_t* nb, const gmx::AtomLocality aLocality)
+bool haveGpuShortRangeWork(const NbnxmGpu* nb, const gmx::AtomLocality aLocality)
 {
     GMX_ASSERT(nb, "Need a valid nbnxn_gpu object");
 
@@ -359,10 +357,12 @@ static inline void gpu_accumulate_timings(gmx_wallclock_gpu_nbnxn_t* timings,
  *
  * See documentation in nbnxm_gpu.h for details.
  *
- * \todo Move into shared source file with gmx_compile_cpp_as_cuda
+ * \todo Move into shared source file, perhaps including
+ * cuda_runtime.h if needed for any remaining CUDA-specific
+ * objects.
  */
 //NOLINTNEXTLINE(misc-definitions-in-headers)
-bool gpu_try_finish_task(gmx_nbnxn_gpu_t*         nb,
+bool gpu_try_finish_task(NbnxmGpu*                nb,
                          const gmx::StepWorkload& stepWork,
                          const AtomLocality       aloc,
                          real*                    e_lj,
@@ -401,7 +401,7 @@ bool gpu_try_finish_task(gmx_nbnxn_gpu_t*         nb,
             // GpuTaskCompletion::Wait mode the timing is expected to be done in the caller.
             wallcycle_start_nocount(wcycle, ewcWAIT_GPU_NB_L);
 
-            if (!haveStreamTasksCompleted(nb->stream[iLocality]))
+            if (!haveStreamTasksCompleted(*nb->deviceStreams[iLocality]))
             {
                 wallcycle_stop(wcycle, ewcWAIT_GPU_NB_L);
 
@@ -414,14 +414,13 @@ bool gpu_try_finish_task(gmx_nbnxn_gpu_t*         nb,
         }
         else if (haveResultToWaitFor)
         {
-            gpuStreamSynchronize(nb->stream[iLocality]);
+            nb->deviceStreams[iLocality]->synchronize();
         }
 
         // TODO: this needs to be moved later because conditional wait could brake timing
         // with a future OpenCL implementation, but with CUDA timing is anyway disabled
         // in all cases where we skip the wait.
-        gpu_accumulate_timings(nb->timings, nb->timers, nb->plist[iLocality], aloc, stepWork,
-                               nb->bDoTime != 0);
+        gpu_accumulate_timings(nb->timings, nb->timers, nb->plist[iLocality], aloc, stepWork, nb->bDoTime);
 
         if (stepWork.computeEnergy || stepWork.computeVirial)
         {
@@ -458,7 +457,7 @@ bool gpu_try_finish_task(gmx_nbnxn_gpu_t*         nb,
  * \return            The number of cycles the gpu wait took
  */
 //NOLINTNEXTLINE(misc-definitions-in-headers) TODO: move into source file
-float gpu_wait_finish_task(gmx_nbnxn_gpu_t*         nb,
+float gpu_wait_finish_task(NbnxmGpu*                nb,
                            const gmx::StepWorkload& stepWork,
                            AtomLocality             aloc,
                            real*                    e_lj,
index 4c3333d82a622ba09883fba637d5fb63c54b5689..af0c69f36c8ddb12e49849ae742616a289d99c93 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/nbnxm/nbnxm.h"
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 #    include "cuda/nbnxm_cuda_types.h"
 #endif
 
-#if GMX_GPU == GMX_GPU_OPENCL
+#if GMX_GPU_OPENCL
 #    include "opencl/nbnxm_ocl_types.h"
 #endif
 
@@ -64,7 +64,7 @@ namespace Nbnxm
  * local part of the force array also depends on the non-local kernel.
  * The skip of the local kernel is taken care of separately.
  */
-static inline bool canSkipNonbondedWork(const gmx_nbnxn_gpu_t& nb, InteractionLocality iloc)
+static inline bool canSkipNonbondedWork(const NbnxmGpu& nb, InteractionLocality iloc)
 {
     assert(nb.plist[iloc]);
     return (iloc == InteractionLocality::NonLocal && nb.plist[iloc]->nsci == 0);
index c93c536becb1f31795b498fb283e984f6bafa4ba..a472cb437dabaf08cc4b8603cdf692614000ca85 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <memory>
 
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
 #include "gromacs/gpu_utils/gpu_macros.h"
-#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/locality.h"
 
-#include "gpu_types.h"
-
-struct NbnxnPairlistGpu;
+struct NbnxmGpu;
+struct DeviceInformation;
+struct gmx_wallclock_gpu_nbnxn_t;
 struct nbnxn_atomdata_t;
+struct NbnxnPairlistGpu;
 struct PairlistParams;
-struct gmx_wallclock_gpu_nbnxn_t;
-struct gmx_gpu_info_t;
-struct gmx_device_info_t;
+struct interaction_const_t;
+
+class DeviceStream;
+
+namespace gmx
+{
+class DeviceStreamManager;
+}
 
 namespace Nbnxm
 {
 
 /** Initializes the data structures related to GPU nonbonded calculations. */
 GPU_FUNC_QUALIFIER
-gmx_nbnxn_gpu_t* gpu_init(const gmx_device_info_t gmx_unused* deviceInfo,
-                          const interaction_const_t gmx_unused* ic,
-                          const PairlistParams gmx_unused& listParams,
-                          const nbnxn_atomdata_t gmx_unused* nbat,
-                          int gmx_unused rank,
-                          /* true if both local and non-local are done on GPU */
-                          gmx_bool gmx_unused bLocalAndNonlocal) GPU_FUNC_TERM_WITH_RETURN(nullptr);
+NbnxmGpu* gpu_init(const gmx::DeviceStreamManager gmx_unused& deviceStreamManager,
+                   const interaction_const_t gmx_unused* ic,
+                   const PairlistParams gmx_unused& listParams,
+                   const nbnxn_atomdata_t gmx_unused* nbat,
+                   /* true if both local and non-local are done on GPU */
+                   bool gmx_unused bLocalAndNonlocal) GPU_FUNC_TERM_WITH_RETURN(nullptr);
 
 /** Initializes pair-list data for GPU, called at every pair search step. */
 GPU_FUNC_QUALIFIER
-void gpu_init_pairlist(gmx_nbnxn_gpu_t gmx_unused*   nb,
+void gpu_init_pairlist(NbnxmGpu gmx_unused*          nb,
                        const struct NbnxnPairlistGpu gmx_unused* h_nblist,
                        gmx::InteractionLocality gmx_unused iloc) GPU_FUNC_TERM;
 
 /** Initializes atom-data on the GPU, called at every pair search step. */
 GPU_FUNC_QUALIFIER
-void gpu_init_atomdata(gmx_nbnxn_gpu_t gmx_unused* nb, const nbnxn_atomdata_t gmx_unused* nbat) GPU_FUNC_TERM;
+void gpu_init_atomdata(NbnxmGpu gmx_unused* nb, const nbnxn_atomdata_t gmx_unused* nbat) GPU_FUNC_TERM;
 
 /*! \brief Re-generate the GPU Ewald force table, resets rlist, and update the
  *  electrostatic type switching to twin cut-off (or back) if needed.
@@ -91,19 +96,19 @@ void gpu_pme_loadbal_update_param(const struct nonbonded_verlet_t gmx_unused* nb
 
 /** Uploads shift vector to the GPU if the box is dynamic (otherwise just returns). */
 GPU_FUNC_QUALIFIER
-void gpu_upload_shiftvec(gmx_nbnxn_gpu_t gmx_unused* nb, const nbnxn_atomdata_t gmx_unused* nbatom) GPU_FUNC_TERM;
+void gpu_upload_shiftvec(NbnxmGpu gmx_unused* nb, const nbnxn_atomdata_t gmx_unused* nbatom) GPU_FUNC_TERM;
 
 /** Clears GPU outputs: nonbonded force, shift force and energy. */
 GPU_FUNC_QUALIFIER
-void gpu_clear_outputs(gmx_nbnxn_gpu_t gmx_unused* nb, bool gmx_unused computeVirial) GPU_FUNC_TERM;
+void gpu_clear_outputs(NbnxmGpu gmx_unused* nb, bool gmx_unused computeVirial) GPU_FUNC_TERM;
 
 /** Frees all GPU resources used for the nonbonded calculations. */
 GPU_FUNC_QUALIFIER
-void gpu_free(gmx_nbnxn_gpu_t gmx_unused* nb) GPU_FUNC_TERM;
+void gpu_free(NbnxmGpu gmx_unused* nb) GPU_FUNC_TERM;
 
 /** Returns the GPU timings structure or NULL if GPU is not used or timing is off. */
 GPU_FUNC_QUALIFIER
-struct gmx_wallclock_gpu_nbnxn_t* gpu_get_timings(gmx_nbnxn_gpu_t gmx_unused* nb)
+struct gmx_wallclock_gpu_nbnxn_t* gpu_get_timings(NbnxmGpu gmx_unused* nb)
         GPU_FUNC_TERM_WITH_RETURN(nullptr);
 
 /** Resets nonbonded GPU timings. */
@@ -113,37 +118,38 @@ void gpu_reset_timings(struct nonbonded_verlet_t gmx_unused* nbv) GPU_FUNC_TERM;
 /** Calculates the minimum size of proximity lists to improve SM load balance
  *  with GPU non-bonded kernels. */
 GPU_FUNC_QUALIFIER
-int gpu_min_ci_balanced(gmx_nbnxn_gpu_t gmx_unused* nb) GPU_FUNC_TERM_WITH_RETURN(-1);
+int gpu_min_ci_balanced(NbnxmGpu gmx_unused* nb) GPU_FUNC_TERM_WITH_RETURN(-1);
 
 /** Returns if analytical Ewald GPU kernels are used. */
 GPU_FUNC_QUALIFIER
-gmx_bool gpu_is_kernel_ewald_analytical(const gmx_nbnxn_gpu_t gmx_unused* nb)
-        GPU_FUNC_TERM_WITH_RETURN(FALSE);
+bool gpu_is_kernel_ewald_analytical(const NbnxmGpu gmx_unused* nb) GPU_FUNC_TERM_WITH_RETURN(FALSE);
 
 /** Returns an opaque pointer to the GPU command stream
  *  Note: CUDA only.
  */
 CUDA_FUNC_QUALIFIER
-void* gpu_get_command_stream(gmx_nbnxn_gpu_t gmx_unused* nb, gmx::InteractionLocality gmx_unused iloc)
+const DeviceStream* gpu_get_command_stream(NbnxmGpu gmx_unused* nb, gmx::InteractionLocality gmx_unused iloc)
         CUDA_FUNC_TERM_WITH_RETURN(nullptr);
 
 /** Returns an opaque pointer to the GPU coordinate+charge array
  *  Note: CUDA only.
  */
 CUDA_FUNC_QUALIFIER
-void* gpu_get_xq(gmx_nbnxn_gpu_t gmx_unused* nb) CUDA_FUNC_TERM_WITH_RETURN(nullptr);
+void* gpu_get_xq(NbnxmGpu gmx_unused* nb) CUDA_FUNC_TERM_WITH_RETURN(nullptr);
 
 /** Returns an opaque pointer to the GPU force array
  *  Note: CUDA only.
  */
 CUDA_FUNC_QUALIFIER
-void* gpu_get_f(gmx_nbnxn_gpu_t gmx_unused* nb) CUDA_FUNC_TERM_WITH_RETURN(nullptr);
+DeviceBuffer<gmx::RVec> gpu_get_f(NbnxmGpu gmx_unused* nb)
+        CUDA_FUNC_TERM_WITH_RETURN(DeviceBuffer<gmx::RVec>{});
 
 /** Returns an opaque pointer to the GPU shift force array
  *  Note: CUDA only.
  */
 CUDA_FUNC_QUALIFIER
-rvec* gpu_get_fshift(gmx_nbnxn_gpu_t gmx_unused* nb) CUDA_FUNC_TERM_WITH_RETURN(nullptr);
+DeviceBuffer<gmx::RVec> gpu_get_fshift(NbnxmGpu gmx_unused* nb)
+        CUDA_FUNC_TERM_WITH_RETURN(DeviceBuffer<gmx::RVec>{});
 
 } // namespace Nbnxm
 
index b784d37ce8fa7e9940f01f91a244dbbd2dab2efd..183fcadc7ca929991308dbd4299be938d567d60d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,9 +46,9 @@
 
 #include "gromacs/utility/basedefinitions.h"
 
-#include "gpu_types.h"
+struct NbnxmGpu;
 
 /*! \brief Handles any JIT compilation of nbnxn kernels for the selected device */
-OPENCL_FUNC_QUALIFIER void nbnxn_gpu_compile_kernels(gmx_nbnxn_gpu_t gmx_unused* nb) OPENCL_FUNC_TERM;
+OPENCL_FUNC_QUALIFIER void nbnxn_gpu_compile_kernels(NbnxmGpu gmx_unused* nb) OPENCL_FUNC_TERM;
 
 #endif
index 0ec0c6e9654fb07e6809f8d185dd9da9c7cff245..9166a3e50a2497cfedbbee4c4a09ef4de12034e5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "pairlist.h"
 
-#if GMX_GPU == GMX_GPU_OPENCL
+#if GMX_GPU_OPENCL
 #    include "gromacs/gpu_utils/gpuregiontimer_ocl.h"
 #endif
 
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
 #    include "gromacs/gpu_utils/gpuregiontimer.cuh"
 #endif
 
+/** \internal
+ * \brief Parameters required for the GPU nonbonded calculations.
+ */
+struct NBParamGpu
+{
+
+    //! type of electrostatics, takes values from #eelType
+    int eeltype;
+    //! type of VdW impl., takes values from #evdwType
+    int vdwtype;
+
+    //! charge multiplication factor
+    float epsfac;
+    //! Reaction-field/plain cutoff electrostatics const.
+    float c_rf;
+    //! Reaction-field electrostatics constant
+    float two_k_rf;
+    //! Ewald/PME parameter
+    float ewald_beta;
+    //! Ewald/PME correction term substracted from the direct-space potential
+    float sh_ewald;
+    //! LJ-Ewald/PME correction term added to the correction potential
+    float sh_lj_ewald;
+    //! LJ-Ewald/PME coefficient
+    float ewaldcoeff_lj;
+
+    //! Coulomb cut-off squared
+    float rcoulomb_sq;
+
+    //! VdW cut-off squared
+    float rvdw_sq;
+    //! VdW switched cut-off
+    float rvdw_switch;
+    //! Full, outer pair-list cut-off squared
+    float rlistOuter_sq;
+    //! Inner, dynamic pruned pair-list cut-off squared
+    float rlistInner_sq;
+    //! True if we use dynamic pair-list pruning
+    bool useDynamicPruning;
+
+    //! VdW shift dispersion constants
+    shift_consts_t dispersion_shift;
+    //! VdW shift repulsion constants
+    shift_consts_t repulsion_shift;
+    //! VdW switch constants
+    switch_consts_t vdw_switch;
+
+    /* LJ non-bonded parameters - accessed through texture memory */
+    //! nonbonded parameter table with C6/C12 pairs per atom type-pair, 2*ntype^2 elements
+    DeviceBuffer<float> nbfp;
+    //! texture object bound to nbfp
+    DeviceTexture nbfp_texobj;
+    //! nonbonded parameter table per atom type, 2*ntype elements
+    DeviceBuffer<float> nbfp_comb;
+    //! texture object bound to nbfp_comb
+    DeviceTexture nbfp_comb_texobj;
+
+    /* Ewald Coulomb force table data - accessed through texture memory */
+    //! table scale/spacing
+    float coulomb_tab_scale;
+    //! pointer to the table in the device memory
+    DeviceBuffer<float> coulomb_tab;
+    //! texture object bound to coulomb_tab
+    DeviceTexture coulomb_tab_texobj;
+};
+
 namespace Nbnxm
 {
 
@@ -76,8 +142,10 @@ struct gpu_timers_t
      */
     struct XFTransfers
     {
-        GpuRegionTimer nb_h2d; /**< timer for x/q H2D transfers (l/nl, every step) */
-        GpuRegionTimer nb_d2h; /**< timer for f D2H transfer (l/nl, every step) */
+        //! timer for x/q H2D transfers (l/nl, every step)
+        GpuRegionTimer nb_h2d;
+        //! timer for f D2H transfer (l/nl, every step)
+        GpuRegionTimer nb_d2h;
     };
 
     /*! \internal
@@ -85,14 +153,20 @@ struct gpu_timers_t
      */
     struct Interaction
     {
-        GpuRegionTimer pl_h2d;       /**< timer for pair-list H2D transfers (l/nl, every PS step) */
-        bool didPairlistH2D = false; /**< true when a pair-list transfer has been done at this step */
-        GpuRegionTimer nb_k;         /**< timer for non-bonded kernels (l/nl, every step)         */
-        GpuRegionTimer prune_k; /**< timer for the 1st pass list pruning kernel (l/nl, every PS step) */
-        bool didPrune = false; /**< true when we timed pruning and the timings need to be accounted for */
-        GpuRegionTimer rollingPrune_k; /**< timer for rolling pruning kernels (l/nl, frequency depends on chunk size)  */
-        bool           didRollingPrune =
-                false; /**< true when we timed rolling pruning (at the previous step) and the timings need to be accounted for */
+        //! timer for pair-list H2D transfers (l/nl, every PS step)
+        GpuRegionTimer pl_h2d;
+        //! true when a pair-list transfer has been done at this step
+        bool didPairlistH2D = false;
+        //! timer for non-bonded kernels (l/nl, every step)
+        GpuRegionTimer nb_k;
+        //! timer for the 1st pass list pruning kernel (l/nl, every PS step)
+        GpuRegionTimer prune_k;
+        //! true when we timed pruning and the timings need to be accounted for
+        bool didPrune = false;
+        //! timer for rolling pruning kernels (l/nl, frequency depends on chunk size)
+        GpuRegionTimer rollingPrune_k;
+        //! true when we timed rolling pruning (at the previous step) and the timings need to be accounted for
+        bool didRollingPrune = false;
     };
 
     //! timer for atom data transfer (every PS step)
@@ -103,29 +177,46 @@ struct gpu_timers_t
     gmx::EnumerationArray<InteractionLocality, Nbnxm::gpu_timers_t::Interaction> interaction;
 };
 
+/*! \internal
+ * \brief GPU pair list structure */
 struct gpu_plist
 {
-    int na_c; /**< number of atoms per cluster                  */
-
-    int                       nsci;       /**< size of sci, # of i clusters in the list     */
-    int                       sci_nalloc; /**< allocation size of sci                       */
-    DeviceBuffer<nbnxn_sci_t> sci;        /**< list of i-cluster ("super-clusters")         */
-
-    int                       ncj4;          /**< total # of 4*j clusters                      */
-    int                       cj4_nalloc;    /**< allocation size of cj4                       */
-    DeviceBuffer<nbnxn_cj4_t> cj4;           /**< 4*j cluster list, contains j cluster number
-                                                and index into the i cluster list            */
-    int                        nimask;       /**< # of 4*j clusters * # of warps               */
-    int                        imask_nalloc; /**< allocation size of imask                     */
-    DeviceBuffer<unsigned int> imask;        /**< imask for 2 warps for each 4*j cluster group */
-    DeviceBuffer<nbnxn_excl_t> excl;         /**< atom interaction bits                        */
-    int                        nexcl;        /**< count for excl                               */
-    int                        excl_nalloc;  /**< allocation size of excl                      */
+    //! number of atoms per cluster
+    int na_c;
+
+    //! size of sci, # of i clusters in the list
+    int nsci;
+    //! allocation size of sci
+    int sci_nalloc;
+    //! list of i-cluster ("super-clusters")
+    DeviceBuffer<nbnxn_sci_t> sci;
+
+    //! total # of 4*j clusters
+    int ncj4;
+    //! allocation size of cj4
+    int cj4_nalloc;
+    //! 4*j cluster list, contains j cluster number and index into the i cluster list
+    DeviceBuffer<nbnxn_cj4_t> cj4;
+    //! # of 4*j clusters * # of warps
+    int nimask;
+    //! allocation size of imask
+    int imask_nalloc;
+    //! imask for 2 warps for each 4*j cluster group
+    DeviceBuffer<unsigned int> imask;
+    //! atom interaction bits
+    DeviceBuffer<nbnxn_excl_t> excl;
+    //! count for excl
+    int nexcl;
+    //! allocation size of excl
+    int excl_nalloc;
 
     /* parameter+variables for normal and rolling pruning */
-    bool haveFreshList; /**< true after search, indictes that initial pruning with outer prunning is needed */
-    int  rollingPruningNumParts; /**< the number of parts/steps over which one cyle of roling pruning takes places */
-    int  rollingPruningPart; /**< the next part to which the roling pruning needs to be applied */
+    //! true after search, indictes that initial pruning with outer prunning is needed
+    bool haveFreshList;
+    //! the number of parts/steps over which one cyle of roling pruning takes places
+    int rollingPruningNumParts;
+    //! the next part to which the roling pruning needs to be applied
+    int rollingPruningPart;
 };
 
 } // namespace Nbnxm
index 8843dc6634bd73b0bab857792c27c47e45eaa12b..d852ec4f0899db0a550287aaa9419b28ea9a9fce 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdlib/updategroupscog.h"
 #include "gromacs/mdtypes/forcerec.h" // only for GET_CGINFO_*
+#include "gromacs/nbnxm/atomdata.h"
 #include "gromacs/simd/simd.h"
 #include "gromacs/simd/vector_operations.h"
 
-#include "atomdata.h"
 #include "boundingboxes.h"
 #include "gridsetdata.h"
 #include "nbnxm_geometry.h"
index 1e68c14dae2d16a9b0f4fb8a75a89773bc1af4be..806ee91f2bdb8826801f7d4816e6081197a7ee53 100644 (file)
@@ -125,14 +125,20 @@ struct BoundingBox
         //! Returns a pointer for SIMD storing of a Corner object
         float* ptr() { return &x; }
 
-        float x;       //!< x coordinate
-        float y;       //!< y coordinate
-        float z;       //!< z coordinate
-        float padding; //!< padding, unused, but should be set to avoid operations on unitialized data
+        //! x coordinate
+        float x;
+        //! y coordinate
+        float y;
+        //! z coordinate
+        float z;
+        //! padding, unused, but should be set to avoid operations on unitialized data
+        float padding;
     };
 
-    Corner lower; //!< lower, along x and y and z, corner
-    Corner upper; //!< upper, along x and y and z, corner
+    //! lower, along x and y and z, corner
+    Corner lower;
+    //! upper, along x and y and z, corner
+    Corner upper;
 };
 
 /*! \internal
@@ -140,8 +146,10 @@ struct BoundingBox
  */
 struct BoundingBox1D
 {
-    float lower; //!< lower bound
-    float upper; //!< upper bound
+    //! lower bound
+    float lower;
+    //! upper bound
+    float upper;
 };
 
 } // namespace Nbnxm
@@ -182,14 +190,19 @@ public:
         //! Constructs the cluster/cell geometry given the type of pairlist
         Geometry(PairlistType pairlistType);
 
-        bool isSimple;             //!< Is this grid simple (CPU) or hierarchical (GPU)
-        int  numAtomsICluster;     //!< Number of atoms per cluster
-        int  numAtomsJCluster;     //!< Number of atoms for list j-clusters
-        int  numAtomsPerCell;      //!< Number of atoms per cell
-        int  numAtomsICluster2Log; //!< 2log of na_c
+        //! Is this grid simple (CPU) or hierarchical (GPU)
+        bool isSimple;
+        //! Number of atoms per cluster
+        int numAtomsICluster;
+        //! Number of atoms for list j-clusters
+        int numAtomsJCluster;
+        //! Number of atoms per cell
+        int numAtomsPerCell;
+        //! 2log of na_c
+        int numAtomsICluster2Log;
     };
 
-    // The physical dimensions of a grid
+    //! The physical dimensions of a grid \internal
     struct Dimensions
     {
         //! The lower corner of the (local) grid
index f171317019f93533ec40d4605343cbb9d38f7931..a2e83ddc6a5e72b94159af34902013bc5fc37e4e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdlib/updategroupscog.h"
+#include "gromacs/nbnxm/atomdata.h"
 #include "gromacs/utility/fatalerror.h"
 
-#include "atomdata.h"
-
 namespace Nbnxm
 {
 
@@ -78,11 +77,11 @@ static int numGrids(const GridSet::DomainSetup& domainSetup)
     return numGrids;
 }
 
-GridSet::DomainSetup::DomainSetup(const int                 ePBC,
+GridSet::DomainSetup::DomainSetup(const PbcType             pbcType,
                                   const bool                doTestParticleInsertion,
                                   const ivec*               numDDCells,
                                   const gmx_domdec_zones_t* ddZones) :
-    ePBC(ePBC),
+    pbcType(pbcType),
     doTestParticleInsertion(doTestParticleInsertion),
     haveMultipleDomains(numDDCells != nullptr),
     zones(ddZones)
@@ -93,7 +92,7 @@ GridSet::DomainSetup::DomainSetup(const int                 ePBC,
     }
 }
 
-GridSet::GridSet(const int                 ePBC,
+GridSet::GridSet(const PbcType             pbcType,
                  const bool                doTestParticleInsertion,
                  const ivec*               numDDCells,
                  const gmx_domdec_zones_t* ddZones,
@@ -101,7 +100,7 @@ GridSet::GridSet(const int                 ePBC,
                  const bool                haveFep,
                  const int                 numThreads,
                  gmx::PinningPolicy        pinningPolicy) :
-    domainSetup_(ePBC, doTestParticleInsertion, numDDCells, ddZones),
+    domainSetup_(pbcType, doTestParticleInsertion, numDDCells, ddZones),
     grids_(numGrids(domainSetup_), Grid(pairlistType, haveFep_)),
     haveFep_(haveFep),
     numRealAtomsLocal_(0),
index 3980a37c6852246909cc8111a995d02070838fec..3e9ffe4c8a9ff7a856a1c50a543b13f16d664f79 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -63,6 +63,7 @@
 
 struct nbnxn_atomdata_t;
 enum class PairlistType;
+enum class PbcType : int;
 
 namespace gmx
 {
@@ -90,13 +91,13 @@ public:
     struct DomainSetup
     {
         //! Constructor, without DD \p numDDCells and \p ddZones should be nullptr
-        DomainSetup(int                       ePBC,
+        DomainSetup(PbcType                   pbcType,
                     bool                      doTestParticleInsertion,
                     const ivec*               numDDCells,
                     const gmx_domdec_zones_t* ddZones);
 
         //! The type of PBC
-        int ePBC;
+        PbcType pbcType;
         //! Tells whether we are doing test-particle insertion
         bool doTestParticleInsertion;
         //! Are there multiple domains?
@@ -108,7 +109,7 @@ public:
     };
 
     //! Constructs a grid set for 1 or multiple DD zones, when numDDCells!=nullptr
-    GridSet(int                       ePBC,
+    GridSet(PbcType                   pbcType,
             bool                      doTestParticleInsertion,
             const ivec*               numDDCells,
             const gmx_domdec_zones_t* ddZones,
index ed8fc182bdbaf2c7cd9cac54ddeca779b9bbc21e..9d7301d34522a35a8d61f8ed27da6177fe7d91cd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,6 +49,7 @@
 
 #include <vector>
 
+#include "gromacs/gpu_utils/hostallocator.h"
 
 namespace Nbnxm
 {
index 50fc7d163daef0b7a4a998279e0aae299f7e9687..ac176fa6cd4407fa0b98f613bed0daf0a4f4d0a5 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -66,15 +67,15 @@ static void clearBufferAll(gmx::ArrayRef<real> buffer)
 template<int numComponentsPerElement>
 static void clearBufferFlagged(const nbnxn_atomdata_t& nbat, int outputIndex, gmx::ArrayRef<real> buffer)
 {
-    const nbnxn_buffer_flags_t& flags = nbat.buffer_flags;
-    gmx_bitmask_t               our_flag;
+    gmx::ArrayRef<const gmx_bitmask_t> flags = nbat.buffer_flags;
+    gmx_bitmask_t                      our_flag;
     bitmask_init_bit(&our_flag, outputIndex);
 
     constexpr size_t numComponentsPerBlock = NBNXN_BUFFERFLAG_SIZE * numComponentsPerElement;
 
-    for (int b = 0; b < flags.nflag; b++)
+    for (size_t b = 0; b < flags.size(); b++)
     {
-        if (!bitmask_is_disjoint(flags.flag[b], our_flag))
+        if (!bitmask_is_disjoint(flags[b], our_flag))
         {
             clearBufferAll(buffer.subArray(b * numComponentsPerBlock, numComponentsPerBlock));
         }
index 80492f006d8e5f0de100a1ff24c5e81efe1ea2b6..f93e03521d35b1f98080f028ec5b0b7ee9c08a50 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,9 +48,9 @@
 
 #include "gromacs/math/vectypes.h"
 /* nbnxn_atomdata_t and nbnxn_pairlist_t could be forward declared, but that requires modifications in all SIMD kernel files */
+#include "gromacs/nbnxm/atomdata.h"
 #include "gromacs/utility/real.h"
 
-#include "atomdata.h"
 #include "pairlist.h"
 
 struct interaction_const_t;
index 23820d9b94ed62f3f739572247512b3cf65ccfce..a677e346106dc3e13de313daf480ba72665d7cc5 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 0f435c2b899e82a010ea7b2cb20d117d365a1f7f..85fcc5d964fe4f17f5e974bec1551508e7c1ce4c 100755 (executable)
@@ -2,7 +2,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 1dd8af848f2e3236813349a2cde5ff882d78f80d..aaf9bd863e04a50c198bf840f3998fc9a176165e 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,6 +46,7 @@
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdtypes/enerdata.h"
 #include "gromacs/mdtypes/forceoutput.h"
+#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
@@ -58,6 +60,8 @@
 #include "gromacs/utility/real.h"
 
 #include "kernel_common.h"
+#include "nbnxm_gpu.h"
+#include "nbnxm_gpu_data_mgmt.h"
 #include "nbnxm_simd.h"
 #include "pairlistset.h"
 #include "pairlistsets.h"
@@ -457,7 +461,7 @@ void nonbonded_verlet_t::dispatchFreeEnergyKernel(gmx::InteractionLocality   iLo
                                                   gmx::ForceWithShiftForces* forceWithShiftForces,
                                                   const t_mdatoms&           mdatoms,
                                                   t_lambda*                  fepvals,
-                                                  real*                      lambda,
+                                                  gmx::ArrayRef<real const>  lambda,
                                                   gmx_enerdata_t*            enerd,
                                                   const gmx::StepWorkload&   stepWork,
                                                   t_nrnb*                    nrnb)
@@ -490,7 +494,7 @@ void nonbonded_verlet_t::dispatchFreeEnergyKernel(gmx::InteractionLocality   iLo
     nb_kernel_data_t kernel_data;
     real             dvdl_nb[efptNR] = { 0 };
     kernel_data.flags                = donb_flags;
-    kernel_data.lambda               = lambda;
+    kernel_data.lambda               = lambda.data();
     kernel_data.dvdl                 = dvdl_nb;
 
     kernel_data.energygrp_elec = enerd->grpp.ener[egCOULSR].data();
@@ -531,12 +535,13 @@ void nonbonded_verlet_t::dispatchFreeEnergyKernel(gmx::InteractionLocality   iLo
         kernel_data.flags = (donb_flags & ~(GMX_NONBONDED_DO_FORCE | GMX_NONBONDED_DO_SHIFTFORCE))
                             | GMX_NONBONDED_DO_FOREIGNLAMBDA;
         kernel_data.lambda         = lam_i;
+        kernel_data.dvdl           = dvdl_nb;
         kernel_data.energygrp_elec = enerd->foreign_grpp.ener[egCOULSR].data();
         kernel_data.energygrp_vdw  = enerd->foreign_grpp.ener[egLJSR].data();
-        /* Note that we add to kernel_data.dvdl, but ignore the result */
 
-        for (size_t i = 0; i < enerd->enerpart_lambda.size(); i++)
+        for (gmx::index i = 0; i < 1 + enerd->foreignLambdaTerms.numLambdas(); i++)
         {
+            std::fill(std::begin(dvdl_nb), std::end(dvdl_nb), 0);
             for (int j = 0; j < efptNR; j++)
             {
                 lam_i[j] = (i == 0 ? lambda[j] : fepvals->all_lambda[j][i - 1]);
@@ -553,8 +558,9 @@ void nonbonded_verlet_t::dispatchFreeEnergyKernel(gmx::InteractionLocality   iLo
                 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
             }
 
-            sum_epot(&(enerd->foreign_grpp), enerd->foreign_term);
-            enerd->enerpart_lambda[i] += enerd->foreign_term[F_EPOT];
+            sum_epot(enerd->foreign_grpp, enerd->foreign_term);
+            enerd->foreignLambdaTerms.accumulate(i, enerd->foreign_term[F_EPOT],
+                                                 dvdl_nb[efptVDW] + dvdl_nb[efptCOUL]);
         }
     }
     wallcycle_sub_stop(wcycle_, ewcsNONBONDED_FEP);
index 3820a719fa95c20495a51d845b59b31b42f5f4cb..0b25a95da821aee752c1d00278a000f7e903839a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +44,7 @@
 #include "gromacs/math/functions.h"
 #include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/nbnxm/atomdata.h"
@@ -51,8 +53,7 @@
 #include "gromacs/pbcutil/ishift.h"
 #include "gromacs/utility/fatalerror.h"
 
-static const int c_numClPerSupercl = c_nbnxnGpuNumClusterPerSupercluster;
-static const int c_clSize          = c_nbnxnGpuClusterSize;
+static constexpr int c_clSize = c_nbnxnGpuClusterSize;
 
 void nbnxn_kernel_gpu_ref(const NbnxnPairlistGpu*    nbl,
                           const nbnxn_atomdata_t*    nbat,
@@ -150,14 +151,14 @@ void nbnxn_kernel_gpu_ref(const NbnxnPairlistGpu*    nbl,
         vctot    = 0;
         Vvdwtot  = 0;
 
-        if (nbln.shift == CENTRAL && nbl->cj4[cj4_ind0].cj[0] == sci * c_numClPerSupercl)
+        if (nbln.shift == CENTRAL && nbl->cj4[cj4_ind0].cj[0] == sci * c_nbnxnGpuNumClusterPerSupercluster)
         {
             /* we have the diagonal:
              * add the charge self interaction energy term
              */
-            for (im = 0; im < c_numClPerSupercl; im++)
+            for (im = 0; im < c_nbnxnGpuNumClusterPerSupercluster; im++)
             {
-                ci = sci * c_numClPerSupercl + im;
+                ci = sci * c_nbnxnGpuNumClusterPerSupercluster + im;
                 for (ic = 0; ic < c_clSize; ic++)
                 {
                     ia = ci * c_clSize + ic;
@@ -185,16 +186,17 @@ void nbnxn_kernel_gpu_ref(const NbnxnPairlistGpu*    nbl,
             {
                 cj = nbl->cj4[cj4_ind].cj[jm];
 
-                for (im = 0; im < c_numClPerSupercl; im++)
+                for (im = 0; im < c_nbnxnGpuNumClusterPerSupercluster; im++)
                 {
                     /* We're only using the first imask,
                      * but here imei[1].imask is identical.
                      */
-                    if ((nbl->cj4[cj4_ind].imei[0].imask >> (jm * c_numClPerSupercl + im)) & 1)
+                    if ((nbl->cj4[cj4_ind].imei[0].imask >> (jm * c_nbnxnGpuNumClusterPerSupercluster + im))
+                        & 1)
                     {
                         gmx_bool within_rlist;
 
-                        ci = sci * c_numClPerSupercl + im;
+                        ci = sci * c_nbnxnGpuNumClusterPerSupercluster + im;
 
                         within_rlist = FALSE;
                         npair        = 0;
@@ -227,7 +229,7 @@ void nbnxn_kernel_gpu_ref(const NbnxnPairlistGpu*    nbl,
                                         c_nbnxnGpuClusterSize / c_nbnxnGpuClusterpairSplit;
                                 int_bit = static_cast<real>(
                                         (excl[jc / clusterPerSplit]->pair[(jc & (clusterPerSplit - 1)) * c_clSize + ic]
-                                         >> (jm * c_numClPerSupercl + im))
+                                         >> (jm * c_nbnxnGpuNumClusterPerSupercluster + im))
                                         & 1);
 
                                 js  = ja * nbat->xstride;
@@ -254,7 +256,7 @@ void nbnxn_kernel_gpu_ref(const NbnxnPairlistGpu*    nbl,
                                 }
 
                                 // Ensure distance do not become so small that r^-12 overflows
-                                rsq = std::max(rsq, NBNXN_MIN_RSQ);
+                                rsq = std::max(rsq, c_nbnxnMinDistanceSquared);
 
                                 rinv   = gmx::invsqrt(rsq);
                                 rinvsq = rinv * rinv;
index 13516940879089f783c6d49801c49347465934d1..57570a7efb6b5f05ec8b74b47b075a35299efebd 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+/*! \internal \file
+ *
+ * \brief
+ * Declares GPU reference kernel
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
 
-#ifndef _nbnxn_kernel_gpu_ref_h
-#define _nbnxn_kernel_gpu_ref_h
+#ifndef GMX_NBNXM_KERNELS_REFERENCE_KERNEL_GPU_REF_H
+#define GMX_NBNXM_KERNELS_REFERENCE_KERNEL_GPU_REF_H
 
 #include "gromacs/math/vectypes.h"
-#include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/real.h"
 
 struct NbnxnPairlistGpu;
 struct nbnxn_atomdata_t;
+struct interaction_const_t;
+struct t_forcerec;
 
 namespace gmx
 {
 class StepWorkload;
 }
 
-/* Reference (slow) kernel for nb n vs n GPU type pair lists */
+//! Reference (slow) kernel for nb n vs n GPU type pair lists
 void nbnxn_kernel_gpu_ref(const NbnxnPairlistGpu*    nbl,
                           const nbnxn_atomdata_t*    nbat,
                           const interaction_const_t* iconst,
index 2ae8cb7b90e56fd47efaf291ff30417b79769394..7275e7bc8d057770224e442359e2aee813cb4d9c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 85080ab6267d1132ecc035d011e279e19ccbce2c..242536c99f7784930b795552a39db7ceef2e188a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+/*! \internal \file
+ *
+ * \brief
+ * Declares CPU reference kernels
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+#ifndef GMX_NBNXM_KERNELS_REFERENCE_KERNEL_REF_H
+#define GMX_NBNXM_KERNELS_REFERENCE_KERNEL_REF_H
 
 #include "gromacs/nbnxm/kernel_common.h"
 
-/* Declare all the different kernel functions.
- */
+//! All the different CPU reference kernel functions.
+//! \{
 nbk_func_noener nbnxn_kernel_ElecRF_VdwLJ_F_ref;
 nbk_func_noener nbnxn_kernel_ElecRF_VdwLJFsw_F_ref;
 nbk_func_noener nbnxn_kernel_ElecRF_VdwLJPsw_F_ref;
@@ -84,15 +95,18 @@ nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_VgrpF_ref;
 nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_VgrpF_ref;
 nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_ref;
 nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_VgrpF_ref;
+//! \}
 
 #ifdef INCLUDE_KERNELFUNCTION_TABLES
 
-/* Declare and define the kernel function pointer lookup tables.
+/*! \brief Declare and define the kernel function pointer lookup tables.
+ *
  * The minor index of the array goes over both the LJ combination rules,
  * which is only supported by plain cut-off, and the LJ switch/PME functions.
  * For the C reference kernels, unlike the SIMD kernels, there is not much
  * advantage in using combination rules, so we (re-)use the same kernel.
  */
+//! \{
 static p_nbk_func_noener nbnxn_kernel_noener_ref[coulktNR][vdwktNR_ref] = {
     { nbnxn_kernel_ElecRF_VdwLJ_F_ref, nbnxn_kernel_ElecRF_VdwLJ_F_ref, nbnxn_kernel_ElecRF_VdwLJ_F_ref,
       nbnxn_kernel_ElecRF_VdwLJFsw_F_ref, nbnxn_kernel_ElecRF_VdwLJPsw_F_ref,
@@ -158,5 +172,8 @@ static p_nbk_func_ener nbnxn_kernel_energrp_ref[coulktNR][vdwktNR_ref] = {
       nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_VgrpF_ref, nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_ref,
       nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_VgrpF_ref }
 };
+//! \}
 
 #endif /* INCLUDE_KERNELFUNCTION_TABLES */
+
+#endif
index 41813679412ee7f9ddd415d1b6121737e55132c0..59d9147aa6d2d4e57026e0ee73f2cc3ac06756aa 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
             // Ensure the distances do not fall below the limit where r^-12 overflows.
             // This should never happen for normal interactions.
-            rsq = std::max(rsq, NBNXN_MIN_RSQ);
+            rsq = std::max(rsq, c_nbnxnMinDistanceSquared);
 
 #ifdef COUNT_PAIRS
             npair++;
index a166ab79e8face485d8a3ead8111fff50b750648..fe53ea2775a161aa0e91174c2f3aa144fe81b499 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
diff --git a/src/gromacs/nbnxm/kernels_simd_2xmm/CMakeLists.txt b/src/gromacs/nbnxm/kernels_simd_2xmm/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1896bfc
--- /dev/null
@@ -0,0 +1,131 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+if (GMX_USE_SIMD_KERNELS)
+    file(GLOB KERNEL_SOURCES
+        kernel_ElecEwTwinCut_VdwLJCombGeom_F.cpp
+        kernel_ElecEwTwinCut_VdwLJCombGeom_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJCombGeom_VgrpF.cpp
+        kernel_ElecEwTwinCut_VdwLJCombLB_F.cpp
+        kernel_ElecEwTwinCut_VdwLJCombLB_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJCombLB_VgrpF.cpp
+        kernel_ElecEwTwinCut_VdwLJEwCombGeom_F.cpp
+        kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJEwCombGeom_VgrpF.cpp
+        kernel_ElecEwTwinCut_VdwLJ_F.cpp
+        kernel_ElecEwTwinCut_VdwLJFSw_F.cpp
+        kernel_ElecEwTwinCut_VdwLJFSw_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJFSw_VgrpF.cpp
+        kernel_ElecEwTwinCut_VdwLJPSw_F.cpp
+        kernel_ElecEwTwinCut_VdwLJPSw_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJPSw_VgrpF.cpp
+        kernel_ElecEwTwinCut_VdwLJ_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJ_VgrpF.cpp
+        kernel_ElecEw_VdwLJCombGeom_F.cpp
+        kernel_ElecEw_VdwLJCombGeom_VF.cpp
+        kernel_ElecEw_VdwLJCombGeom_VgrpF.cpp
+        kernel_ElecEw_VdwLJCombLB_F.cpp
+        kernel_ElecEw_VdwLJCombLB_VF.cpp
+        kernel_ElecEw_VdwLJCombLB_VgrpF.cpp
+        kernel_ElecEw_VdwLJEwCombGeom_F.cpp
+        kernel_ElecEw_VdwLJEwCombGeom_VF.cpp
+        kernel_ElecEw_VdwLJEwCombGeom_VgrpF.cpp
+        kernel_ElecEw_VdwLJ_F.cpp
+        kernel_ElecEw_VdwLJFSw_F.cpp
+        kernel_ElecEw_VdwLJFSw_VF.cpp
+        kernel_ElecEw_VdwLJFSw_VgrpF.cpp
+        kernel_ElecEw_VdwLJPSw_F.cpp
+        kernel_ElecEw_VdwLJPSw_VF.cpp
+        kernel_ElecEw_VdwLJPSw_VgrpF.cpp
+        kernel_ElecEw_VdwLJ_VF.cpp
+        kernel_ElecEw_VdwLJ_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombGeom_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombGeom_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombGeom_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombLB_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombLB_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombLB_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJ_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJFSw_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJFSw_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJFSw_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJPSw_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJPSw_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJPSw_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJ_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJ_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJCombGeom_F.cpp
+        kernel_ElecQSTab_VdwLJCombGeom_VF.cpp
+        kernel_ElecQSTab_VdwLJCombGeom_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJCombLB_F.cpp
+        kernel_ElecQSTab_VdwLJCombLB_VF.cpp
+        kernel_ElecQSTab_VdwLJCombLB_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJEwCombGeom_F.cpp
+        kernel_ElecQSTab_VdwLJEwCombGeom_VF.cpp
+        kernel_ElecQSTab_VdwLJEwCombGeom_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJ_F.cpp
+        kernel_ElecQSTab_VdwLJFSw_F.cpp
+        kernel_ElecQSTab_VdwLJFSw_VF.cpp
+        kernel_ElecQSTab_VdwLJFSw_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJPSw_F.cpp
+        kernel_ElecQSTab_VdwLJPSw_VF.cpp
+        kernel_ElecQSTab_VdwLJPSw_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJ_VF.cpp
+        kernel_ElecQSTab_VdwLJ_VgrpF.cpp
+        kernel_ElecRF_VdwLJCombGeom_F.cpp
+        kernel_ElecRF_VdwLJCombGeom_VF.cpp
+        kernel_ElecRF_VdwLJCombGeom_VgrpF.cpp
+        kernel_ElecRF_VdwLJCombLB_F.cpp
+        kernel_ElecRF_VdwLJCombLB_VF.cpp
+        kernel_ElecRF_VdwLJCombLB_VgrpF.cpp
+        kernel_ElecRF_VdwLJEwCombGeom_F.cpp
+        kernel_ElecRF_VdwLJEwCombGeom_VF.cpp
+        kernel_ElecRF_VdwLJEwCombGeom_VgrpF.cpp
+        kernel_ElecRF_VdwLJ_F.cpp
+        kernel_ElecRF_VdwLJFSw_F.cpp
+        kernel_ElecRF_VdwLJFSw_VF.cpp
+        kernel_ElecRF_VdwLJFSw_VgrpF.cpp
+        kernel_ElecRF_VdwLJPSw_F.cpp
+        kernel_ElecRF_VdwLJPSw_VF.cpp
+        kernel_ElecRF_VdwLJPSw_VgrpF.cpp
+        kernel_ElecRF_VdwLJ_VF.cpp
+        kernel_ElecRF_VdwLJ_VgrpF.cpp
+        kernel_prune.cpp
+        )
+endif()
+
+set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${KERNEL_SOURCES} PARENT_SCOPE)
index 9694949fa94e7dd6b3d0b32d0906f3997ce5d2eb..6f6a2400b5ac5c333f6adffeac2fb539a627710b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f109430c019fd62c96c199ed4362454ae984ea7c..458c23b1b8fa2fe9caf8008fe23f1e141cc5d447 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
     fiz_S2 = fiz_S2 + tz_S2;
 
     /* Decrement j atom force */
-    decrHsimd(f + ajx, tx_S0 + tx_S2);
-    decrHsimd(f + ajy, ty_S0 + ty_S2);
-    decrHsimd(f + ajz, tz_S0 + tz_S2);
+    decr3Hsimd(f + aj * DIM, tx_S0 + tx_S2, ty_S0 + ty_S2, tz_S0 + tz_S2);
 }
 
 #undef rinv_ex_S0
index e17c121123bde8e801aa06f94d7469354e68469f..8d0c0251287981beb0936b5e35608f35e8a81279 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #endif
 #ifdef LJ_EWALD_GEOM
     real     lj_ewaldcoeff2, lj_ewaldcoeff6_6;
-    SimdReal mone_S, half_S, lje_c2_S, lje_c6_6_S;
+    SimdReal half_S, lje_c2_S, lje_c6_6_S;
 #endif
 
 #ifdef LJ_COMB_LB
 #    endif
 #endif
 #ifdef LJ_EWALD_GEOM
-    mone_S           = SimdReal(-1.0);
     half_S           = SimdReal(0.5);
     lj_ewaldcoeff2   = ic->ewaldcoeff_lj * ic->ewaldcoeff_lj;
     lj_ewaldcoeff6_6 = lj_ewaldcoeff2 * lj_ewaldcoeff2 * lj_ewaldcoeff2 / 6;
     rcvdw2_S = SimdReal(ic->rvdw * ic->rvdw);
 #endif
 
-    minRsq_S = SimdReal(NBNXN_MIN_RSQ);
+    minRsq_S = SimdReal(c_nbnxnMinDistanceSquared);
 
     const real* gmx_restrict q        = nbatParams.q.data();
     const real               facel    = ic->epsfac;
diff --git a/src/gromacs/nbnxm/kernels_simd_4xm/CMakeLists.txt b/src/gromacs/nbnxm/kernels_simd_4xm/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1896bfc
--- /dev/null
@@ -0,0 +1,131 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2020, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+if (GMX_USE_SIMD_KERNELS)
+    file(GLOB KERNEL_SOURCES
+        kernel_ElecEwTwinCut_VdwLJCombGeom_F.cpp
+        kernel_ElecEwTwinCut_VdwLJCombGeom_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJCombGeom_VgrpF.cpp
+        kernel_ElecEwTwinCut_VdwLJCombLB_F.cpp
+        kernel_ElecEwTwinCut_VdwLJCombLB_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJCombLB_VgrpF.cpp
+        kernel_ElecEwTwinCut_VdwLJEwCombGeom_F.cpp
+        kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJEwCombGeom_VgrpF.cpp
+        kernel_ElecEwTwinCut_VdwLJ_F.cpp
+        kernel_ElecEwTwinCut_VdwLJFSw_F.cpp
+        kernel_ElecEwTwinCut_VdwLJFSw_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJFSw_VgrpF.cpp
+        kernel_ElecEwTwinCut_VdwLJPSw_F.cpp
+        kernel_ElecEwTwinCut_VdwLJPSw_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJPSw_VgrpF.cpp
+        kernel_ElecEwTwinCut_VdwLJ_VF.cpp
+        kernel_ElecEwTwinCut_VdwLJ_VgrpF.cpp
+        kernel_ElecEw_VdwLJCombGeom_F.cpp
+        kernel_ElecEw_VdwLJCombGeom_VF.cpp
+        kernel_ElecEw_VdwLJCombGeom_VgrpF.cpp
+        kernel_ElecEw_VdwLJCombLB_F.cpp
+        kernel_ElecEw_VdwLJCombLB_VF.cpp
+        kernel_ElecEw_VdwLJCombLB_VgrpF.cpp
+        kernel_ElecEw_VdwLJEwCombGeom_F.cpp
+        kernel_ElecEw_VdwLJEwCombGeom_VF.cpp
+        kernel_ElecEw_VdwLJEwCombGeom_VgrpF.cpp
+        kernel_ElecEw_VdwLJ_F.cpp
+        kernel_ElecEw_VdwLJFSw_F.cpp
+        kernel_ElecEw_VdwLJFSw_VF.cpp
+        kernel_ElecEw_VdwLJFSw_VgrpF.cpp
+        kernel_ElecEw_VdwLJPSw_F.cpp
+        kernel_ElecEw_VdwLJPSw_VF.cpp
+        kernel_ElecEw_VdwLJPSw_VgrpF.cpp
+        kernel_ElecEw_VdwLJ_VF.cpp
+        kernel_ElecEw_VdwLJ_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombGeom_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombGeom_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombGeom_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombLB_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombLB_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJCombLB_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJ_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJFSw_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJFSw_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJFSw_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJPSw_F.cpp
+        kernel_ElecQSTabTwinCut_VdwLJPSw_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJPSw_VgrpF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJ_VF.cpp
+        kernel_ElecQSTabTwinCut_VdwLJ_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJCombGeom_F.cpp
+        kernel_ElecQSTab_VdwLJCombGeom_VF.cpp
+        kernel_ElecQSTab_VdwLJCombGeom_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJCombLB_F.cpp
+        kernel_ElecQSTab_VdwLJCombLB_VF.cpp
+        kernel_ElecQSTab_VdwLJCombLB_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJEwCombGeom_F.cpp
+        kernel_ElecQSTab_VdwLJEwCombGeom_VF.cpp
+        kernel_ElecQSTab_VdwLJEwCombGeom_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJ_F.cpp
+        kernel_ElecQSTab_VdwLJFSw_F.cpp
+        kernel_ElecQSTab_VdwLJFSw_VF.cpp
+        kernel_ElecQSTab_VdwLJFSw_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJPSw_F.cpp
+        kernel_ElecQSTab_VdwLJPSw_VF.cpp
+        kernel_ElecQSTab_VdwLJPSw_VgrpF.cpp
+        kernel_ElecQSTab_VdwLJ_VF.cpp
+        kernel_ElecQSTab_VdwLJ_VgrpF.cpp
+        kernel_ElecRF_VdwLJCombGeom_F.cpp
+        kernel_ElecRF_VdwLJCombGeom_VF.cpp
+        kernel_ElecRF_VdwLJCombGeom_VgrpF.cpp
+        kernel_ElecRF_VdwLJCombLB_F.cpp
+        kernel_ElecRF_VdwLJCombLB_VF.cpp
+        kernel_ElecRF_VdwLJCombLB_VgrpF.cpp
+        kernel_ElecRF_VdwLJEwCombGeom_F.cpp
+        kernel_ElecRF_VdwLJEwCombGeom_VF.cpp
+        kernel_ElecRF_VdwLJEwCombGeom_VgrpF.cpp
+        kernel_ElecRF_VdwLJ_F.cpp
+        kernel_ElecRF_VdwLJFSw_F.cpp
+        kernel_ElecRF_VdwLJFSw_VF.cpp
+        kernel_ElecRF_VdwLJFSw_VgrpF.cpp
+        kernel_ElecRF_VdwLJPSw_F.cpp
+        kernel_ElecRF_VdwLJPSw_VF.cpp
+        kernel_ElecRF_VdwLJPSw_VgrpF.cpp
+        kernel_ElecRF_VdwLJ_VF.cpp
+        kernel_ElecRF_VdwLJ_VgrpF.cpp
+        kernel_prune.cpp
+        )
+endif()
+
+set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${KERNEL_SOURCES} PARENT_SCOPE)
index 3d83a4ae9b2ec6d46c19a36502abe75f96c8d9ca..3b4dca5715ef68de3cfee912e84d704e94230c87 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 8294692c4f8f789d21d4dc375110f267da8a7bb7..01646f3b04dabfc41239c09bbed8d2e5f5f17de7 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 03f2d544b652f86fbc451f267737efd58d969287..19ea6304ba15f17bacf99d28213a7b51261b6983 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #endif
 #ifdef LJ_EWALD_GEOM
     real     lj_ewaldcoeff2, lj_ewaldcoeff6_6;
-    SimdReal mone_S, half_S, lje_c2_S, lje_c6_6_S;
+    SimdReal half_S, lje_c2_S, lje_c6_6_S;
 #endif
 
 #ifdef LJ_COMB_LB
 #    endif
 #endif
 #ifdef LJ_EWALD_GEOM
-    mone_S           = SimdReal(-1.0);
     half_S           = SimdReal(0.5);
     lj_ewaldcoeff2   = ic->ewaldcoeff_lj * ic->ewaldcoeff_lj;
     lj_ewaldcoeff6_6 = lj_ewaldcoeff2 * lj_ewaldcoeff2 * lj_ewaldcoeff2 / 6;
     rcvdw2_S = SimdReal(ic->rvdw * ic->rvdw);
 #endif
 
-    minRsq_S = SimdReal(NBNXN_MIN_RSQ);
+    minRsq_S = SimdReal(c_nbnxnMinDistanceSquared);
 
     const real* gmx_restrict q        = nbatParams.q.data();
     const real               facel    = ic->epsfac;
index 1531f02816f73744b0cf3d687040079a296ab983..76e2eead9f07d18144c568948f3b7b5c34b95667 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "nbnxm.h"
 
 #include "gromacs/domdec/domdec_struct.h"
+#include "gromacs/nbnxm/atomdata.h"
 #include "gromacs/timing/wallcycle.h"
 
-#include "atomdata.h"
+#include "nbnxm_gpu.h"
 #include "pairlistsets.h"
 #include "pairsearch.h"
 
@@ -118,9 +119,11 @@ void nonbonded_verlet_t::setLocalAtomOrder()
     pairSearch_->setLocalAtomOrder();
 }
 
-void nonbonded_verlet_t::setAtomProperties(const t_mdatoms& mdatoms, gmx::ArrayRef<const int> atomInfo)
+void nonbonded_verlet_t::setAtomProperties(gmx::ArrayRef<const int>  atomTypes,
+                                           gmx::ArrayRef<const real> atomCharges,
+                                           gmx::ArrayRef<const int>  atomInfo)
 {
-    nbnxn_atomdata_set(nbat.get(), pairSearch_->gridSet(), &mdatoms, atomInfo.data());
+    nbnxn_atomdata_set(nbat.get(), pairSearch_->gridSet(), atomTypes, atomCharges, atomInfo);
 }
 
 void nonbonded_verlet_t::convertCoordinates(const gmx::AtomLocality        locality,
@@ -139,16 +142,16 @@ void nonbonded_verlet_t::convertCoordinates(const gmx::AtomLocality        local
 
 void nonbonded_verlet_t::convertCoordinatesGpu(const gmx::AtomLocality locality,
                                                const bool              fillLocal,
-                                               DeviceBuffer<float>     d_x,
+                                               DeviceBuffer<gmx::RVec> d_x,
                                                GpuEventSynchronizer*   xReadyOnDevice)
 {
-    wallcycle_start(wcycle_, ewcNB_XF_BUF_OPS);
-    wallcycle_sub_start(wcycle_, ewcsNB_X_BUF_OPS);
+    wallcycle_start(wcycle_, ewcLAUNCH_GPU);
+    wallcycle_sub_start(wcycle_, ewcsLAUNCH_GPU_NB_X_BUF_OPS);
 
     nbnxn_atomdata_x_to_nbat_x_gpu(pairSearch_->gridSet(), locality, fillLocal, gpu_nbv, d_x, xReadyOnDevice);
 
-    wallcycle_sub_stop(wcycle_, ewcsNB_X_BUF_OPS);
-    wallcycle_stop(wcycle_, ewcNB_XF_BUF_OPS);
+    wallcycle_sub_stop(wcycle_, ewcsLAUNCH_GPU_NB_X_BUF_OPS);
+    wallcycle_stop(wcycle_, ewcLAUNCH_GPU);
 }
 
 gmx::ArrayRef<const int> nonbonded_verlet_t::getGridIndices() const
@@ -162,7 +165,7 @@ void nonbonded_verlet_t::atomdata_add_nbat_f_to_f(const gmx::AtomLocality  local
 
     /* Skip the reduction if there was no short-range GPU work to do
      * (either NB or both NB and bonded work). */
-    if (!pairlistIsSimple() && !haveGpuShortRangeWork(locality))
+    if (!pairlistIsSimple() && !Nbnxm::haveGpuShortRangeWork(gpu_nbv, locality))
     {
         return;
     }
@@ -176,48 +179,27 @@ void nonbonded_verlet_t::atomdata_add_nbat_f_to_f(const gmx::AtomLocality  local
     wallcycle_stop(wcycle_, ewcNB_XF_BUF_OPS);
 }
 
-void nonbonded_verlet_t::atomdata_add_nbat_f_to_f_gpu(const gmx::AtomLocality locality,
-                                                      DeviceBuffer<float>     totalForcesDevice,
-                                                      void*                   forcesPmeDevice,
-                                                      gmx::ArrayRef<GpuEventSynchronizer* const> dependencyList,
-                                                      bool useGpuFPmeReduction,
-                                                      bool accumulateForce)
+int nonbonded_verlet_t::getNumAtoms(const gmx::AtomLocality locality)
 {
-
-    GMX_ASSERT((useGpuFPmeReduction == (forcesPmeDevice != nullptr)),
-               "GPU PME force reduction is only valid when a non-null GPU PME force pointer is "
-               "available");
-
-    /* Skip the reduction if there was no short-range GPU work to do
-     * (either NB or both NB and bonded work). */
-    if (!pairlistIsSimple() && !haveGpuShortRangeWork(locality))
+    int numAtoms = 0;
+    switch (locality)
     {
-        return;
+        case gmx::AtomLocality::All: numAtoms = pairSearch_->gridSet().numRealAtomsTotal(); break;
+        case gmx::AtomLocality::Local: numAtoms = pairSearch_->gridSet().numRealAtomsLocal(); break;
+        case gmx::AtomLocality::NonLocal:
+            numAtoms = pairSearch_->gridSet().numRealAtomsTotal()
+                       - pairSearch_->gridSet().numRealAtomsLocal();
+            break;
+        case gmx::AtomLocality::Count:
+            GMX_ASSERT(false, "Count is invalid locality specifier");
+            break;
     }
-
-    wallcycle_start(wcycle_, ewcNB_XF_BUF_OPS);
-    wallcycle_sub_start(wcycle_, ewcsNB_F_BUF_OPS);
-
-    reduceForcesGpu(locality, totalForcesDevice, pairSearch_->gridSet(), forcesPmeDevice,
-                    dependencyList, gpu_nbv, useGpuFPmeReduction, accumulateForce);
-
-    wallcycle_sub_stop(wcycle_, ewcsNB_F_BUF_OPS);
-    wallcycle_stop(wcycle_, ewcNB_XF_BUF_OPS);
+    return numAtoms;
 }
 
-void nonbonded_verlet_t::atomdata_init_add_nbat_f_to_f_gpu(GpuEventSynchronizer* const localReductionDone)
+void* nonbonded_verlet_t::getGpuForces()
 {
-
-    wallcycle_start(wcycle_, ewcNB_XF_BUF_OPS);
-    wallcycle_sub_start(wcycle_, ewcsNB_F_BUF_OPS);
-
-    const Nbnxm::GridSet& gridSet = pairSearch_->gridSet();
-
-    Nbnxm::nbnxn_gpu_init_add_nbat_f_to_f(gridSet.cells().data(), gpu_nbv,
-                                          gridSet.numRealAtomsTotal(), localReductionDone);
-
-    wallcycle_sub_stop(wcycle_, ewcsNB_F_BUF_OPS);
-    wallcycle_stop(wcycle_, ewcNB_XF_BUF_OPS);
+    return Nbnxm::getGpuForces(gpu_nbv);
 }
 
 real nonbonded_verlet_t::pairlistInnerRadius() const
@@ -235,6 +217,15 @@ void nonbonded_verlet_t::changePairlistRadii(real rlistOuter, real rlistInner)
     pairlistSets_->changePairlistRadii(rlistOuter, rlistInner);
 }
 
+void nonbonded_verlet_t::setupGpuShortRangeWork(const gmx::GpuBonded*          gpuBonded,
+                                                const gmx::InteractionLocality iLocality)
+{
+    if (useGpu() && !emulateGpu())
+    {
+        Nbnxm::setupGpuShortRangeWork(gpu_nbv, gpuBonded, iLocality);
+    }
+}
+
 void nonbonded_verlet_t::atomdata_init_copy_x_to_nbat_x_gpu()
 {
     Nbnxm::nbnxn_gpu_init_x_to_nbat_x(pairSearch_->gridSet(), gpu_nbv);
index 1548f3704ff9c74b83d01b82d29a29ce1e2a1cd4..2596350e52dc8096590c8278f4583ca0ec68f77f 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdtypes/locality.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/enumerationhelpers.h"
-#include "gromacs/utility/range.h"
 #include "gromacs/utility/real.h"
 
-// TODO: Remove this include
-#include "nbnxm_gpu.h"
-
-struct gmx_device_info_t;
+struct DeviceInformation;
 struct gmx_domdec_zones_t;
 struct gmx_enerdata_t;
 struct gmx_hw_info_t;
 struct gmx_mtop_t;
+struct NbnxmGpu;
 struct gmx_wallcycle;
 struct interaction_const_t;
+struct nbnxn_atomdata_t;
 struct nonbonded_verlet_t;
 class PairSearch;
 class PairlistSets;
-struct t_blocka;
 struct t_commrec;
 struct t_lambda;
 struct t_mdatoms;
@@ -141,19 +139,19 @@ struct t_nrnb;
 struct t_forcerec;
 struct t_inputrec;
 
-/*! \brief Switch for whether to use GPU for buffer ops*/
-enum class BufferOpsUseGpu
-{
-    True,
-    False
-};
-
 class GpuEventSynchronizer;
 
 namespace gmx
 {
+class DeviceStreamManager;
 class ForceWithShiftForces;
+class GpuBonded;
+template<typename>
+class ListOfLists;
 class MDLogger;
+template<typename>
+class Range;
+class StepWorkload;
 class UpdateGroupsCog;
 } // namespace gmx
 
@@ -221,7 +219,7 @@ public:
                        std::unique_ptr<PairSearch>       pairSearch,
                        std::unique_ptr<nbnxn_atomdata_t> nbat,
                        const Nbnxm::KernelSetup&         kernelSetup,
-                       gmx_nbnxn_gpu_t*                  gpu_nbv,
+                       NbnxmGpu*                         gpu_nbv,
                        gmx_wallcycle*                    wcycle);
 
     ~nonbonded_verlet_t();
@@ -250,11 +248,27 @@ public:
     //! Returns the index position of the atoms on the search grid
     gmx::ArrayRef<const int> getGridIndices() const;
 
-    //! Constructs the pairlist for the given locality
-    void constructPairlist(gmx::InteractionLocality iLocality, const t_blocka* excl, int64_t step, t_nrnb* nrnb);
+    /*! \brief Constructs the pairlist for the given locality
+     *
+     * When there are no non-self exclusions, \p exclusions can be empty.
+     * Otherwise the number of lists in \p exclusions should match the number
+     * of atoms when not using DD, or the total number of atoms in the i-zones
+     * when using DD.
+     *
+     * \param[in] iLocality   The interaction locality: local or non-local
+     * \param[in] exclusions  Lists of exclusions for every atom.
+     * \param[in] step        Used to set the list creation step
+     * \param[in,out] nrnb    Flop accounting struct, can be nullptr
+     */
+    void constructPairlist(gmx::InteractionLocality     iLocality,
+                           const gmx::ListOfLists<int>& exclusions,
+                           int64_t                      step,
+                           t_nrnb*                      nrnb);
 
     //! Updates all the atom properties in Nbnxm
-    void setAtomProperties(const t_mdatoms& mdatoms, gmx::ArrayRef<const int> atomInfo);
+    void setAtomProperties(gmx::ArrayRef<const int>  atomTypes,
+                           gmx::ArrayRef<const real> atomCharges,
+                           gmx::ArrayRef<const int>  atomInfo);
 
     /*!\brief Convert the coordinates to NBNXM format for the given locality.
      *
@@ -275,10 +289,10 @@ public:
      * \param[in] d_x             GPU coordinates buffer in plain rvec format to be transformed.
      * \param[in] xReadyOnDevice  Event synchronizer indicating that the coordinates are ready in the device memory.
      */
-    void convertCoordinatesGpu(gmx::AtomLocality     locality,
-                               bool                  fillLocal,
-                               DeviceBuffer<float>   d_x,
-                               GpuEventSynchronizer* xReadyOnDevice);
+    void convertCoordinatesGpu(gmx::AtomLocality       locality,
+                               bool                    fillLocal,
+                               DeviceBuffer<gmx::RVec> d_x,
+                               GpuEventSynchronizer*   xReadyOnDevice);
 
     //! Init for GPU version of setup coordinates in Nbnxm
     void atomdata_init_copy_x_to_nbat_x_gpu();
@@ -317,7 +331,7 @@ public:
                                   gmx::ForceWithShiftForces* forceWithShiftForces,
                                   const t_mdatoms&           mdatoms,
                                   t_lambda*                  fepvals,
-                                  real*                      lambda,
+                                  gmx::ArrayRef<const real>  lambda,
                                   gmx_enerdata_t*            enerd,
                                   const gmx::StepWorkload&   stepWork,
                                   t_nrnb*                    nrnb);
@@ -338,20 +352,24 @@ public:
      * \param [in]     accumulateForce      If the total force buffer already contains data
      */
     void atomdata_add_nbat_f_to_f_gpu(gmx::AtomLocality                          locality,
-                                      DeviceBuffer<float>                        totalForcesDevice,
+                                      DeviceBuffer<gmx::RVec>                    totalForcesDevice,
                                       void*                                      forcesPmeDevice,
                                       gmx::ArrayRef<GpuEventSynchronizer* const> dependencyList,
                                       bool useGpuFPmeReduction,
                                       bool accumulateForce);
 
-    /*! \brief Outer body of function to perform initialization for F buffer operations on GPU.
+    /*! \brief Get the number of atoms for a given locality
      *
-     * \param localReductionDone     Pointer to an event synchronizer that marks the completion of the local f buffer ops kernel.
+     * \param [in] locality   Local or non-local
+     * \returns               The number of atoms for given locality
      */
-    void atomdata_init_add_nbat_f_to_f_gpu(GpuEventSynchronizer* localReductionDone);
+    int getNumAtoms(gmx::AtomLocality locality);
 
-    /*! \brief return GPU pointer to f in rvec format */
-    void* get_gpu_frvec();
+    /*! \brief Get the pointer to the GPU nonbonded force buffer
+     *
+     * \returns A pointer to the force buffer in GPU memory
+     */
+    void* getGpuForces();
 
     //! Return the kernel setup
     const Nbnxm::KernelSetup& kernelSetup() const { return kernelSetup_; }
@@ -366,19 +384,7 @@ public:
     void changePairlistRadii(real rlistOuter, real rlistInner);
 
     //! Set up internal flags that indicate what type of short-range work there is.
-    void setupGpuShortRangeWork(const gmx::GpuBonded* gpuBonded, const gmx::InteractionLocality iLocality)
-    {
-        if (useGpu() && !emulateGpu())
-        {
-            Nbnxm::setupGpuShortRangeWork(gpu_nbv, gpuBonded, iLocality);
-        }
-    }
-
-    //! Returns true if there is GPU short-range work for the given atom locality.
-    bool haveGpuShortRangeWork(const gmx::AtomLocality aLocality)
-    {
-        return ((useGpu() && !emulateGpu()) && Nbnxm::haveGpuShortRangeWork(gpu_nbv, aLocality));
-    }
+    void setupGpuShortRangeWork(const gmx::GpuBonded* gpuBonded, gmx::InteractionLocality iLocality);
 
     // TODO: Make all data members private
 public:
@@ -397,23 +403,23 @@ private:
 
 public:
     //! GPU Nbnxm data, only used with a physical GPU (TODO: use unique_ptr)
-    gmx_nbnxn_gpu_t* gpu_nbv;
+    NbnxmGpu* gpu_nbv;
 };
 
 namespace Nbnxm
 {
 
 /*! \brief Creates an Nbnxm object */
-std::unique_ptr<nonbonded_verlet_t> init_nb_verlet(const gmx::MDLogger&     mdlog,
-                                                   gmx_bool                 bFEP_NonBonded,
-                                                   const t_inputrec*        ir,
-                                                   const t_forcerec*        fr,
-                                                   const t_commrec*         cr,
-                                                   const gmx_hw_info_t&     hardwareInfo,
-                                                   const gmx_device_info_t* deviceInfo,
-                                                   const gmx_mtop_t*        mtop,
-                                                   matrix                   box,
-                                                   gmx_wallcycle*           wcycle);
+std::unique_ptr<nonbonded_verlet_t> init_nb_verlet(const gmx::MDLogger& mdlog,
+                                                   const t_inputrec*    ir,
+                                                   const t_forcerec*    fr,
+                                                   const t_commrec*     cr,
+                                                   const gmx_hw_info_t& hardwareInfo,
+                                                   bool                 useGpuForNonbonded,
+                                                   const gmx::DeviceStreamManager* deviceStreamManager,
+                                                   const gmx_mtop_t*               mtop,
+                                                   matrix                          box,
+                                                   gmx_wallcycle*                  wcycle);
 
 } // namespace Nbnxm
 
index 5a999e355c970b030a39665f68b83ea7eeef112b..6bc25302fd57c0671a29e66ba8047ca02cc557e7 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 2b724323719ea72132a0623b70304125cb9af268..3362a410db3c7fd50ec1a92e6ff6985f9a505d60 100644 (file)
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-
+/*! \internal \file
+ *
+ * \brief
+ * Declares the geometry-related functionality
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
 #ifndef GMX_NBNXM_NBNXM_GEOMETRY_H
 #define GMX_NBNXM_NBNXM_GEOMETRY_H
 
@@ -45,7 +52,8 @@
 #include "pairlist.h"
 
 
-/* Returns the base-2 log of n.
+/*! \copybrief Returns the base-2 log of n.
+ * *
  * Generates a fatal error when n is not an integer power of 2.
  */
 static inline int get_2log(int n)
@@ -68,13 +76,13 @@ static inline int get_2log(int n)
 namespace Nbnxm
 {
 
-/* The nbnxn i-cluster size in atoms for each nbnxn kernel type */
+/*! \brief The nbnxn i-cluster size in atoms for each nbnxn kernel type */
 static constexpr gmx::EnumerationArray<KernelType, int> IClusterSizePerKernelType = {
     { 0, c_nbnxnCpuIClusterSize, c_nbnxnCpuIClusterSize, c_nbnxnCpuIClusterSize,
       c_nbnxnGpuClusterSize, c_nbnxnGpuClusterSize }
 };
 
-/* The nbnxn j-cluster size in atoms for each nbnxn kernel type */
+/*! \brief The nbnxn j-cluster size in atoms for each nbnxn kernel type */
 static constexpr gmx::EnumerationArray<KernelType, int> JClusterSizePerKernelType = {
     { 0, c_nbnxnCpuIClusterSize,
 #if GMX_SIMD
@@ -85,13 +93,14 @@ static constexpr gmx::EnumerationArray<KernelType, int> JClusterSizePerKernelTyp
       c_nbnxnGpuClusterSize, c_nbnxnGpuClusterSize / 2 }
 };
 
-/* Returns whether the pair-list corresponding to nb_kernel_type is simple */
+/*! \brief Returns whether the pair-list corresponding to nb_kernel_type is simple */
 static inline bool kernelTypeUsesSimplePairlist(const KernelType kernelType)
 {
     return (kernelType == KernelType::Cpu4x4_PlainC || kernelType == KernelType::Cpu4xN_Simd_4xN
             || kernelType == KernelType::Cpu4xN_Simd_2xNN);
 }
 
+//! Returns whether a SIMD kernel is in use
 static inline bool kernelTypeIsSimd(const KernelType kernelType)
 {
     return (kernelType == KernelType::Cpu4xN_Simd_4xN || kernelType == KernelType::Cpu4xN_Simd_2xNN);
@@ -99,7 +108,7 @@ static inline bool kernelTypeIsSimd(const KernelType kernelType)
 
 } // namespace Nbnxm
 
-/* Returns the effective list radius of the pair-list
+/*! \brief Returns the effective list radius of the pair-list
  *
  * Due to the cluster size the effective pair-list is longer than
  * that of a simple atom pair-list. This function gives the extra distance.
@@ -110,7 +119,7 @@ static inline bool kernelTypeIsSimd(const KernelType kernelType)
  */
 real nbnxn_get_rlist_effective_inc(int jClusterSize, real atomDensity);
 
-/* Returns the effective list radius of the pair-list
+/*! \brief Returns the effective list radius of the pair-list
  *
  * Due to the cluster size the effective pair-list is longer than
  * that of a simple atom pair-list. This function gives the extra distance.
index 7a4a94b6a52f5888932418bcce202b1391e97808..00e7ae11f5a1240b351033e622ba615314957eab 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/gpu_utils/gpu_macros.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdtypes/locality.h"
+#include "gromacs/nbnxm/atomdata.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
-#include "atomdata.h"
-#include "gpu_types.h"
-
 struct interaction_const_t;
 struct nbnxn_atomdata_t;
 struct gmx_wallcycle;
@@ -63,11 +62,69 @@ class GpuBonded;
 class StepWorkload;
 } // namespace gmx
 
+/*! \brief Nbnxm electrostatic GPU kernel flavors.
+ *
+ *  Types of electrostatics implementations available in the GPU non-bonded
+ *  force kernels. These represent both the electrostatics types implemented
+ *  by the kernels (cut-off, RF, and Ewald - a subset of what's defined in
+ *  enums.h) as well as encode implementation details analytical/tabulated
+ *  and single or twin cut-off (for Ewald kernels).
+ *  Note that the cut-off and RF kernels have only analytical flavor and unlike
+ *  in the CPU kernels, the tabulated kernels are ATM Ewald-only.
+ *
+ *  The row-order of pointers to different electrostatic kernels defined in
+ *  nbnxn_cuda.cu by the nb_*_kfunc_ptr function pointer table
+ *  should match the order of enumerated types below.
+ */
+enum eelType
+{
+    eelTypeCUT,
+    eelTypeRF,
+    eelTypeEWALD_TAB,
+    eelTypeEWALD_TAB_TWIN,
+    eelTypeEWALD_ANA,
+    eelTypeEWALD_ANA_TWIN,
+    eelTypeNR
+};
+
+/*! \brief Nbnxm VdW GPU kernel flavors.
+ *
+ * The enumerates values correspond to the LJ implementations in the GPU non-bonded
+ * kernels.
+ *
+ * The column-order of pointers to different electrostatic kernels defined in
+ * nbnxn_cuda_ocl.cpp/.cu by the nb_*_kfunc_ptr function pointer table
+ * should match the order of enumerated types below.
+ */
+enum evdwType
+{
+    evdwTypeCUT,
+    evdwTypeCUTCOMBGEOM,
+    evdwTypeCUTCOMBLB,
+    evdwTypeFSWITCH,
+    evdwTypePSWITCH,
+    evdwTypeEWALDGEOM,
+    evdwTypeEWALDLB,
+    evdwTypeNR
+};
+
 namespace Nbnxm
 {
 
 class Grid;
 
+/*! \brief Returns true if LJ combination rules are used in the non-bonded kernels.
+ *
+ *  \param[in] vdwType  The VdW interaction/implementation type as defined by evdwType
+ *                      enumeration.
+ *
+ * \returns Whether combination rules are used by the run.
+ */
+static inline bool useLjCombRule(const int vdwType)
+{
+    return (vdwType == evdwTypeCUTCOMBGEOM || vdwType == evdwTypeCUTCOMBLB);
+}
+
 /*! \brief
  * Launch asynchronously the xq buffer host to device copy.
  *
@@ -79,7 +136,7 @@ class Grid;
  * \param [in]    aloc      Atom locality flag.
  */
 GPU_FUNC_QUALIFIER
-void gpu_copy_xq_to_gpu(gmx_nbnxn_gpu_t gmx_unused*   nb,
+void gpu_copy_xq_to_gpu(NbnxmGpu gmx_unused*          nb,
                         const struct nbnxn_atomdata_t gmx_unused* nbdata,
                         gmx::AtomLocality gmx_unused aloc) GPU_FUNC_TERM;
 
@@ -94,7 +151,7 @@ void gpu_copy_xq_to_gpu(gmx_nbnxn_gpu_t gmx_unused*   nb,
  *
  */
 GPU_FUNC_QUALIFIER
-void gpu_launch_kernel(gmx_nbnxn_gpu_t gmx_unused* nb,
+void gpu_launch_kernel(NbnxmGpu gmx_unused*    nb,
                        const gmx::StepWorkload gmx_unused& stepWork,
                        gmx::InteractionLocality gmx_unused iloc) GPU_FUNC_TERM;
 
@@ -134,7 +191,7 @@ void gpu_launch_kernel(gmx_nbnxn_gpu_t gmx_unused* nb,
  * \param [in]    numParts  Number of parts the pair list is split into in the rolling kernel.
  */
 GPU_FUNC_QUALIFIER
-void gpu_launch_kernel_pruneonly(gmx_nbnxn_gpu_t gmx_unused* nb,
+void gpu_launch_kernel_pruneonly(NbnxmGpu gmx_unused*     nb,
                                  gmx::InteractionLocality gmx_unused iloc,
                                  int gmx_unused numParts) GPU_FUNC_TERM;
 
@@ -143,7 +200,7 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_gpu_t gmx_unused* nb,
  * (and energies/shift forces if required).
  */
 GPU_FUNC_QUALIFIER
-void gpu_launch_cpyback(gmx_nbnxn_gpu_t gmx_unused* nb,
+void gpu_launch_cpyback(NbnxmGpu gmx_unused* nb,
                         nbnxn_atomdata_t gmx_unused* nbatom,
                         const gmx::StepWorkload gmx_unused& stepWork,
                         gmx::AtomLocality gmx_unused aloc) GPU_FUNC_TERM;
@@ -186,7 +243,7 @@ void gpu_launch_cpyback(gmx_nbnxn_gpu_t gmx_unused* nb,
  * \returns                   True if the nonbonded tasks associated with \p aloc locality have completed
  */
 GPU_FUNC_QUALIFIER
-bool gpu_try_finish_task(gmx_nbnxn_gpu_t gmx_unused* nb,
+bool gpu_try_finish_task(NbnxmGpu gmx_unused*    nb,
                          const gmx::StepWorkload gmx_unused& stepWork,
                          gmx::AtomLocality gmx_unused aloc,
                          real gmx_unused* e_lj,
@@ -210,7 +267,7 @@ bool gpu_try_finish_task(gmx_nbnxn_gpu_t gmx_unused* nb,
  * \param[out] shiftForces Shift forces buffer to accumulate into
  * \param[out] wcycle         Pointer to wallcycle data structure               */
 GPU_FUNC_QUALIFIER
-float gpu_wait_finish_task(gmx_nbnxn_gpu_t gmx_unused* nb,
+float gpu_wait_finish_task(NbnxmGpu gmx_unused*    nb,
                            const gmx::StepWorkload gmx_unused& stepWork,
                            gmx::AtomLocality gmx_unused aloc,
                            real gmx_unused* e_lj,
@@ -218,16 +275,11 @@ float gpu_wait_finish_task(gmx_nbnxn_gpu_t gmx_unused* nb,
                            gmx::ArrayRef<gmx::RVec> gmx_unused shiftForces,
                            gmx_wallcycle gmx_unused* wcycle) GPU_FUNC_TERM_WITH_RETURN(0.0);
 
-/*! \brief Selects the Ewald kernel type, analytical or tabulated, single or twin cut-off. */
-GPU_FUNC_QUALIFIER
-int nbnxn_gpu_pick_ewald_kernel_type(const interaction_const_t gmx_unused& ic)
-        GPU_FUNC_TERM_WITH_RETURN(-1);
-
 /*! \brief Initialization for X buffer operations on GPU.
  * Called on the NS step and performs (re-)allocations and memory copies. !*/
 CUDA_FUNC_QUALIFIER
 void nbnxn_gpu_init_x_to_nbat_x(const Nbnxm::GridSet gmx_unused& gridSet,
-                                gmx_nbnxn_gpu_t gmx_unused* gpu_nbv) CUDA_FUNC_TERM;
+                                NbnxmGpu gmx_unused* gpu_nbv) CUDA_FUNC_TERM;
 
 /*! \brief X buffer operations on GPU: performs conversion from rvec to nb format.
  *
@@ -243,8 +295,8 @@ void nbnxn_gpu_init_x_to_nbat_x(const Nbnxm::GridSet gmx_unused& gridSet,
 CUDA_FUNC_QUALIFIER
 void nbnxn_gpu_x_to_nbat_x(const Nbnxm::Grid gmx_unused& grid,
                            bool gmx_unused setFillerCoords,
-                           gmx_nbnxn_gpu_t gmx_unused* gpu_nbv,
-                           DeviceBuffer<float> gmx_unused d_x,
+                           NbnxmGpu gmx_unused*    gpu_nbv,
+                           DeviceBuffer<gmx::RVec> gmx_unused d_x,
                            GpuEventSynchronizer gmx_unused* xReadyOnDevice,
                            gmx::AtomLocality gmx_unused locality,
                            int gmx_unused gridId,
@@ -255,7 +307,7 @@ void nbnxn_gpu_x_to_nbat_x(const Nbnxm::Grid gmx_unused& grid,
  * \param[in] interactionLocality  Local or NonLocal sync point
  */
 CUDA_FUNC_QUALIFIER
-void nbnxnInsertNonlocalGpuDependency(const gmx_nbnxn_gpu_t gmx_unused* nb,
+void nbnxnInsertNonlocalGpuDependency(const NbnxmGpu gmx_unused* nb,
                                       gmx::InteractionLocality gmx_unused interactionLocality) CUDA_FUNC_TERM;
 
 /*! \brief Set up internal flags that indicate what type of short-range work there is.
@@ -271,7 +323,7 @@ void nbnxnInsertNonlocalGpuDependency(const gmx_nbnxn_gpu_t gmx_unused* nb,
  * \param[in]     iLocality  Interaction locality identifier
  */
 GPU_FUNC_QUALIFIER
-void setupGpuShortRangeWork(gmx_nbnxn_gpu_t gmx_unused* nb,
+void setupGpuShortRangeWork(NbnxmGpu gmx_unused* nb,
                             const gmx::GpuBonded gmx_unused* gpuBonded,
                             gmx::InteractionLocality gmx_unused iLocality) GPU_FUNC_TERM;
 
@@ -285,48 +337,22 @@ void setupGpuShortRangeWork(gmx_nbnxn_gpu_t gmx_unused* nb,
  * \param[in]     aLocality Atom locality identifier
  */
 GPU_FUNC_QUALIFIER
-bool haveGpuShortRangeWork(const gmx_nbnxn_gpu_t gmx_unused* nb, gmx::AtomLocality gmx_unused aLocality)
+bool haveGpuShortRangeWork(const NbnxmGpu gmx_unused* nb, gmx::AtomLocality gmx_unused aLocality)
         GPU_FUNC_TERM_WITH_RETURN(false);
 
-/*! \brief Initialization for F buffer operations on GPU */
-CUDA_FUNC_QUALIFIER
-void nbnxn_gpu_init_add_nbat_f_to_f(const int gmx_unused* cell,
-                                    gmx_nbnxn_gpu_t gmx_unused* gpu_nbv,
-                                    int gmx_unused       natoms_total,
-                                    GpuEventSynchronizer gmx_unused* localReductionDone) CUDA_FUNC_TERM;
-
-/*! \brief Force buffer operations on GPU.
- *
- * Transforms non-bonded forces into plain rvec format and add all the force components to the total
- * force buffer
- *
- * \param[in]   atomLocality         If the reduction should be performed on local or non-local atoms.
- * \param[in]   totalForcesDevice    Device buffer to accumulate resulting force.
- * \param[in]   gpu_nbv              The NBNXM GPU data structure.
- * \param[in]   pmeForcesDevice      Device buffer with PME forces.
- * \param[in]   dependencyList       List of synchronizers that represent the dependencies the reduction task needs to sync on.
- * \param[in]   atomStart            Index of the first atom to reduce forces for.
- * \param[in]   numAtoms             Number of atoms to reduce forces for.
- * \param[in]   useGpuFPmeReduction  Whether PME forces should be added.
- * \param[in]   accumulateForce      Whether there are usefull data already in the total force buffer.
- *
+/*! \brief sync CPU thread on coordinate copy to device
+ * \param[in] nb                   The nonbonded data GPU structure
  */
 CUDA_FUNC_QUALIFIER
-void nbnxn_gpu_add_nbat_f_to_f(gmx::AtomLocality gmx_unused atomLocality,
-                               DeviceBuffer<float> gmx_unused totalForcesDevice,
-                               gmx_nbnxn_gpu_t gmx_unused* gpu_nbv,
-                               void gmx_unused*                           pmeForcesDevice,
-                               gmx::ArrayRef<GpuEventSynchronizer* const> gmx_unused dependencyList,
-                               int gmx_unused atomStart,
-                               int gmx_unused numAtoms,
-                               bool gmx_unused useGpuFPmeReduction,
-                               bool gmx_unused accumulateForce) CUDA_FUNC_TERM;
+void nbnxn_wait_x_on_device(NbnxmGpu gmx_unused* nb) CUDA_FUNC_TERM;
 
-/*! \brief sync CPU thread on coordinate copy to device
- * \param[in] nb                   The nonbonded data GPU structure
+/*! \brief Get the pointer to the GPU nonbonded force buffer
+ *
+ * \param[in] nb  The nonbonded data GPU structure
+ * \returns       A pointer to the force buffer in GPU memory
  */
 CUDA_FUNC_QUALIFIER
-void nbnxn_wait_x_on_device(gmx_nbnxn_gpu_t gmx_unused* nb) CUDA_FUNC_TERM;
+void* getGpuForces(NbnxmGpu gmx_unused* nb) CUDA_FUNC_TERM_WITH_RETURN(nullptr);
 
 } // namespace Nbnxm
 #endif
diff --git a/src/gromacs/nbnxm/nbnxm_gpu_data_mgmt.cpp b/src/gromacs/nbnxm/nbnxm_gpu_data_mgmt.cpp
new file mode 100644 (file)
index 0000000..47cfa5b
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *  \brief Define common implementation of nbnxm_gpu_data_mgmt.h
+ *
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Teemu Virolainen <teemu@streamcomputing.eu>
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ *  \ingroup module_nbnxm
+ */
+#include "gmxpre.h"
+
+#include "config.h"
+
+#if GMX_GPU_CUDA
+#    include "cuda/nbnxm_cuda_types.h"
+#endif
+
+#if GMX_GPU_OPENCL
+#    include "opencl/nbnxm_ocl_types.h"
+#endif
+
+#include "nbnxm_gpu_data_mgmt.h"
+
+#include "gromacs/nbnxm/gpu_data_mgmt.h"
+#include "gromacs/timing/gpu_timing.h"
+#include "gromacs/utility/cstringutil.h"
+
+#include "nbnxm_gpu.h"
+#include "pairlistsets.h"
+
+namespace Nbnxm
+{
+
+void init_ewald_coulomb_force_table(const EwaldCorrectionTables& tables,
+                                    NBParamGpu*                  nbp,
+                                    const DeviceContext&         deviceContext)
+{
+    if (nbp->coulomb_tab)
+    {
+        destroyParamLookupTable(&nbp->coulomb_tab, nbp->coulomb_tab_texobj);
+    }
+
+    nbp->coulomb_tab_scale = tables.scale;
+    initParamLookupTable(&nbp->coulomb_tab, &nbp->coulomb_tab_texobj, tables.tableF.data(),
+                         tables.tableF.size(), deviceContext);
+}
+
+void inline printEnviromnentVariableDeprecationMessage(bool               isEnvironmentVariableSet,
+                                                       const std::string& environmentVariableSuffix)
+{
+    if (isEnvironmentVariableSet)
+    {
+        fprintf(stderr,
+                "Environment variables GMX_CUDA_%s and GMX_OCL_%s are deprecated and will be\n"
+                "removed in release 2022, please use GMX_GPU_%s instead.",
+                environmentVariableSuffix.c_str(), environmentVariableSuffix.c_str(),
+                environmentVariableSuffix.c_str());
+    }
+}
+
+int nbnxn_gpu_pick_ewald_kernel_type(const interaction_const_t& ic)
+{
+    bool bTwinCut = (ic.rcoulomb != ic.rvdw);
+    int  kernel_type;
+
+    /* Benchmarking/development environment variables to force the use of
+       analytical or tabulated Ewald kernel. */
+
+    // Remove these when old environment variables are deprecated
+    const bool forceAnalyticalEwaldLegacy = (getenv("GMX_CUDA_NB_ANA_EWALD") != nullptr)
+                                            || (getenv("GMX_OCL_NB_ANA_EWALD") != nullptr);
+    const bool forceTabulatedEwaldLegacy = (getenv("GMX_CUDA_NB_TAB_EWALD") != nullptr)
+                                           || (getenv("GMX_OCL_NB_TAB_EWALD") != nullptr);
+    const bool forceTwinCutoffEwaldLegacy = (getenv("GMX_CUDA_NB_EWALD_TWINCUT") != nullptr)
+                                            || (getenv("GMX_OCL_NB_EWALD_TWINCUT") != nullptr);
+
+    printEnviromnentVariableDeprecationMessage(forceAnalyticalEwaldLegacy, "NB_ANA_EWALD");
+    printEnviromnentVariableDeprecationMessage(forceTabulatedEwaldLegacy, "NB_TAB_EWALD");
+    printEnviromnentVariableDeprecationMessage(forceTwinCutoffEwaldLegacy, "NB_EWALD_TWINCUT");
+
+    const bool forceAnalyticalEwald =
+            (getenv("GMX_GPU_NB_ANA_EWALD") != nullptr) || forceAnalyticalEwaldLegacy;
+    const bool forceTabulatedEwald =
+            (getenv("GMX_GPU_NB_TAB_EWALD") != nullptr) || forceTabulatedEwaldLegacy;
+
+    if (forceAnalyticalEwald && forceTabulatedEwald)
+    {
+        gmx_incons(
+                "Both analytical and tabulated Ewald GPU non-bonded kernels "
+                "requested through environment variables.");
+    }
+
+    /* By default, use analytical Ewald
+     * TODO: tabulated does not work in OpenCL, it needs fixing, see init_nbparam() in nbnxn_ocl_data_mgmt.cpp
+     *
+     */
+    bool bUseAnalyticalEwald = true;
+    if (forceAnalyticalEwald)
+    {
+        if (debug)
+        {
+            fprintf(debug, "Using analytical Ewald GPU kernels\n");
+        }
+    }
+    else if (forceTabulatedEwald)
+    {
+        bUseAnalyticalEwald = false;
+
+        if (debug)
+        {
+            fprintf(debug, "Using tabulated Ewald GPU kernels\n");
+        }
+    }
+
+    /* Use twin cut-off kernels if requested by bTwinCut or the env. var.
+       forces it (use it for debugging/benchmarking only). */
+    if (!bTwinCut && ((getenv("GMX_GPU_NB_EWALD_TWINCUT") == nullptr) || forceTwinCutoffEwaldLegacy))
+    {
+        kernel_type = bUseAnalyticalEwald ? eelTypeEWALD_ANA : eelTypeEWALD_TAB;
+    }
+    else
+    {
+        kernel_type = bUseAnalyticalEwald ? eelTypeEWALD_ANA_TWIN : eelTypeEWALD_TAB_TWIN;
+    }
+
+    return kernel_type;
+}
+
+void set_cutoff_parameters(NBParamGpu* nbp, const interaction_const_t* ic, const PairlistParams& listParams)
+{
+    nbp->ewald_beta        = ic->ewaldcoeff_q;
+    nbp->sh_ewald          = ic->sh_ewald;
+    nbp->epsfac            = ic->epsfac;
+    nbp->two_k_rf          = 2.0 * ic->k_rf;
+    nbp->c_rf              = ic->c_rf;
+    nbp->rvdw_sq           = ic->rvdw * ic->rvdw;
+    nbp->rcoulomb_sq       = ic->rcoulomb * ic->rcoulomb;
+    nbp->rlistOuter_sq     = listParams.rlistOuter * listParams.rlistOuter;
+    nbp->rlistInner_sq     = listParams.rlistInner * listParams.rlistInner;
+    nbp->useDynamicPruning = listParams.useDynamicPruning;
+
+    nbp->sh_lj_ewald   = ic->sh_lj_ewald;
+    nbp->ewaldcoeff_lj = ic->ewaldcoeff_lj;
+
+    nbp->rvdw_switch      = ic->rvdw_switch;
+    nbp->dispersion_shift = ic->dispersion_shift;
+    nbp->repulsion_shift  = ic->repulsion_shift;
+    nbp->vdw_switch       = ic->vdw_switch;
+}
+
+void gpu_pme_loadbal_update_param(const nonbonded_verlet_t* nbv, const interaction_const_t* ic)
+{
+    if (!nbv || !nbv->useGpu())
+    {
+        return;
+    }
+    NbnxmGpu*   nb  = nbv->gpu_nbv;
+    NBParamGpu* nbp = nb->nbparam;
+
+    set_cutoff_parameters(nbp, ic, nbv->pairlistSets().params());
+
+    nbp->eeltype = nbnxn_gpu_pick_ewald_kernel_type(*ic);
+
+    GMX_RELEASE_ASSERT(ic->coulombEwaldTables, "Need valid Coulomb Ewald correction tables");
+    init_ewald_coulomb_force_table(*ic->coulombEwaldTables, nbp, *nb->deviceContext_);
+}
+
+void init_plist(gpu_plist* pl)
+{
+    /* initialize to nullptr pointers to data that is not allocated here and will
+       need reallocation in nbnxn_gpu_init_pairlist */
+    pl->sci   = nullptr;
+    pl->cj4   = nullptr;
+    pl->imask = nullptr;
+    pl->excl  = nullptr;
+
+    /* size -1 indicates that the respective array hasn't been initialized yet */
+    pl->na_c          = -1;
+    pl->nsci          = -1;
+    pl->sci_nalloc    = -1;
+    pl->ncj4          = -1;
+    pl->cj4_nalloc    = -1;
+    pl->nimask        = -1;
+    pl->imask_nalloc  = -1;
+    pl->nexcl         = -1;
+    pl->excl_nalloc   = -1;
+    pl->haveFreshList = false;
+}
+
+void init_timings(gmx_wallclock_gpu_nbnxn_t* t)
+{
+    int i, j;
+
+    t->nb_h2d_t = 0.0;
+    t->nb_d2h_t = 0.0;
+    t->nb_c     = 0;
+    t->pl_h2d_t = 0.0;
+    t->pl_h2d_c = 0;
+    for (i = 0; i < 2; i++)
+    {
+        for (j = 0; j < 2; j++)
+        {
+            t->ktime[i][j].t = 0.0;
+            t->ktime[i][j].c = 0;
+        }
+    }
+    t->pruneTime.c        = 0;
+    t->pruneTime.t        = 0.0;
+    t->dynamicPruneTime.c = 0;
+    t->dynamicPruneTime.t = 0.0;
+}
+
+//! This function is documented in the header file
+void gpu_init_pairlist(NbnxmGpu* nb, const NbnxnPairlistGpu* h_plist, const InteractionLocality iloc)
+{
+    char sbuf[STRLEN];
+    // Timing accumulation should happen only if there was work to do
+    // because getLastRangeTime() gets skipped with empty lists later
+    // which leads to the counter not being reset.
+    bool                bDoTime      = (nb->bDoTime && !h_plist->sci.empty());
+    const DeviceStream& deviceStream = *nb->deviceStreams[iloc];
+    gpu_plist*          d_plist      = nb->plist[iloc];
+
+    if (d_plist->na_c < 0)
+    {
+        d_plist->na_c = h_plist->na_ci;
+    }
+    else
+    {
+        if (d_plist->na_c != h_plist->na_ci)
+        {
+            sprintf(sbuf, "In init_plist: the #atoms per cell has changed (from %d to %d)",
+                    d_plist->na_c, h_plist->na_ci);
+            gmx_incons(sbuf);
+        }
+    }
+
+    gpu_timers_t::Interaction& iTimers = nb->timers->interaction[iloc];
+
+    if (bDoTime)
+    {
+        iTimers.pl_h2d.openTimingRegion(deviceStream);
+        iTimers.didPairlistH2D = true;
+    }
+
+    // TODO most of this function is same in CUDA and OpenCL, move into the header
+    const DeviceContext& deviceContext = *nb->deviceContext_;
+
+    reallocateDeviceBuffer(&d_plist->sci, h_plist->sci.size(), &d_plist->nsci, &d_plist->sci_nalloc,
+                           deviceContext);
+    copyToDeviceBuffer(&d_plist->sci, h_plist->sci.data(), 0, h_plist->sci.size(), deviceStream,
+                       GpuApiCallBehavior::Async, bDoTime ? iTimers.pl_h2d.fetchNextEvent() : nullptr);
+
+    reallocateDeviceBuffer(&d_plist->cj4, h_plist->cj4.size(), &d_plist->ncj4, &d_plist->cj4_nalloc,
+                           deviceContext);
+    copyToDeviceBuffer(&d_plist->cj4, h_plist->cj4.data(), 0, h_plist->cj4.size(), deviceStream,
+                       GpuApiCallBehavior::Async, bDoTime ? iTimers.pl_h2d.fetchNextEvent() : nullptr);
+
+    reallocateDeviceBuffer(&d_plist->imask, h_plist->cj4.size() * c_nbnxnGpuClusterpairSplit,
+                           &d_plist->nimask, &d_plist->imask_nalloc, deviceContext);
+
+    reallocateDeviceBuffer(&d_plist->excl, h_plist->excl.size(), &d_plist->nexcl,
+                           &d_plist->excl_nalloc, deviceContext);
+    copyToDeviceBuffer(&d_plist->excl, h_plist->excl.data(), 0, h_plist->excl.size(), deviceStream,
+                       GpuApiCallBehavior::Async, bDoTime ? iTimers.pl_h2d.fetchNextEvent() : nullptr);
+
+    if (bDoTime)
+    {
+        iTimers.pl_h2d.closeTimingRegion(deviceStream);
+    }
+
+    /* need to prune the pair list during the next step */
+    d_plist->haveFreshList = true;
+}
+
+//! This function is documented in the header file
+gmx_wallclock_gpu_nbnxn_t* gpu_get_timings(NbnxmGpu* nb)
+{
+    return (nb != nullptr && nb->bDoTime) ? nb->timings : nullptr;
+}
+
+//! This function is documented in the header file
+void gpu_reset_timings(nonbonded_verlet_t* nbv)
+{
+    if (nbv->gpu_nbv && nbv->gpu_nbv->bDoTime)
+    {
+        init_timings(nbv->gpu_nbv->timings);
+    }
+}
+
+bool gpu_is_kernel_ewald_analytical(const NbnxmGpu* nb)
+{
+    return ((nb->nbparam->eeltype == eelTypeEWALD_ANA) || (nb->nbparam->eeltype == eelTypeEWALD_ANA_TWIN));
+}
+
+} // namespace Nbnxm
diff --git a/src/gromacs/nbnxm/nbnxm_gpu_data_mgmt.h b/src/gromacs/nbnxm/nbnxm_gpu_data_mgmt.h
new file mode 100644 (file)
index 0000000..761737d
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare common functions for NBNXM GPU data management.
+ *
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ *  \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_NBNXM_GPU_DATA_MGMT_H
+#define GMX_NBNXM_NBNXM_GPU_DATA_MGMT_H
+
+struct interaction_const_t;
+struct NBParamGpu;
+struct PairlistParams;
+
+namespace gmx
+{
+enum class InteractionLocality;
+}
+
+namespace Nbnxm
+{
+
+struct gpu_plist;
+
+/*! \brief Tabulates the Ewald Coulomb force and initializes the size/scale and the table GPU array.
+ *
+ * If called with an already allocated table, it just re-uploads the
+ * table.
+ */
+void init_ewald_coulomb_force_table(const EwaldCorrectionTables& tables,
+                                    NBParamGpu*                  nbp,
+                                    const DeviceContext&         deviceContext);
+
+/*! \brief Selects the Ewald kernel type, analytical or tabulated, single or twin cut-off. */
+int nbnxn_gpu_pick_ewald_kernel_type(const interaction_const_t gmx_unused& ic);
+
+/*! \brief Copies all parameters related to the cut-off from ic to nbp
+ */
+void set_cutoff_parameters(NBParamGpu* nbp, const interaction_const_t* ic, const PairlistParams& listParams);
+
+/*! \brief Initializes the pair list data structure.
+ */
+void init_plist(gpu_plist* pl);
+
+/*! \brief Initializes the timings data structure. */
+void init_timings(gmx_wallclock_gpu_nbnxn_t* t);
+
+} // namespace Nbnxm
+
+#endif // GMX_NBNXM_NBNXM_GPU_DATA_MGMT_H
index 26a9bba0da94ffef9ec382aec0e1dd9f8b3581b3..74086df3dea8b21e94ca2da97fdf639d1ece69ea 100644 (file)
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/forcerec.h"
 #include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/interaction_const.h"
+#include "gromacs/nbnxm/atomdata.h"
 #include "gromacs/nbnxm/gpu_data_mgmt.h"
 #include "gromacs/nbnxm/nbnxm.h"
 #include "gromacs/nbnxm/pairlist_tuning.h"
 #include "gromacs/simd/simd.h"
+#include "gromacs/topology/mtop_util.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/logger.h"
 
-#include "atomdata.h"
-#include "gpu_types.h"
 #include "grid.h"
 #include "nbnxm_geometry.h"
 #include "nbnxm_simd.h"
@@ -237,8 +238,7 @@ static KernelSetup pick_nbnxn_kernel(const gmx::MDLogger&     mdlog,
                                      gmx_bool                 use_simd_kernels,
                                      const gmx_hw_info_t&     hardwareInfo,
                                      const NonbondedResource& nonbondedResource,
-                                     const t_inputrec*        ir,
-                                     gmx_bool                 bDoNonbonded)
+                                     const t_inputrec*        ir)
 {
     KernelSetup kernelSetup;
 
@@ -247,12 +247,7 @@ static KernelSetup pick_nbnxn_kernel(const gmx::MDLogger&     mdlog,
         kernelSetup.kernelType         = KernelType::Cpu8x8x8_PlainC;
         kernelSetup.ewaldExclusionType = EwaldExclusionType::DecidedByGpuModule;
 
-        if (bDoNonbonded)
-        {
-            GMX_LOG(mdlog.warning)
-                    .asParagraph()
-                    .appendText("Emulating a GPU run on the CPU (slow)");
-        }
+        GMX_LOG(mdlog.warning).asParagraph().appendText("Emulating a GPU run on the CPU (slow)");
     }
     else if (nonbondedResource == NonbondedResource::Gpu)
     {
@@ -272,25 +267,22 @@ static KernelSetup pick_nbnxn_kernel(const gmx::MDLogger&     mdlog,
         }
     }
 
-    if (bDoNonbonded)
+    GMX_LOG(mdlog.info)
+            .asParagraph()
+            .appendTextFormatted("Using %s %dx%d nonbonded short-range kernels",
+                                 lookup_kernel_name(kernelSetup.kernelType),
+                                 IClusterSizePerKernelType[kernelSetup.kernelType],
+                                 JClusterSizePerKernelType[kernelSetup.kernelType]);
+
+    if (KernelType::Cpu4x4_PlainC == kernelSetup.kernelType
+        || KernelType::Cpu8x8x8_PlainC == kernelSetup.kernelType)
     {
-        GMX_LOG(mdlog.info)
+        GMX_LOG(mdlog.warning)
                 .asParagraph()
-                .appendTextFormatted("Using %s %dx%d nonbonded short-range kernels",
-                                     lookup_kernel_name(kernelSetup.kernelType),
-                                     IClusterSizePerKernelType[kernelSetup.kernelType],
-                                     JClusterSizePerKernelType[kernelSetup.kernelType]);
-
-        if (KernelType::Cpu4x4_PlainC == kernelSetup.kernelType
-            || KernelType::Cpu8x8x8_PlainC == kernelSetup.kernelType)
-        {
-            GMX_LOG(mdlog.warning)
-                    .asParagraph()
-                    .appendTextFormatted(
-                            "WARNING: Using the slow %s kernels. This should\n"
-                            "not happen during routine usage on supported platforms.",
-                            lookup_kernel_name(kernelSetup.kernelType));
-        }
+                .appendTextFormatted(
+                        "WARNING: Using the slow %s kernels. This should\n"
+                        "not happen during routine usage on supported platforms.",
+                        lookup_kernel_name(kernelSetup.kernelType));
     }
 
     GMX_RELEASE_ASSERT(kernelSetup.kernelType != KernelType::NotSet
@@ -320,7 +312,7 @@ namespace Nbnxm
 {
 
 /*! \brief Gets and returns the minimum i-list count for balacing based on the GPU used or env.var. when set */
-static int getMinimumIlistCountForGpuBalancing(gmx_nbnxn_gpu_t* nbnxmGpu)
+static int getMinimumIlistCountForGpuBalancing(NbnxmGpu* nbnxmGpu)
 {
     int minimumIlistCount;
 
@@ -356,25 +348,24 @@ static int getMinimumIlistCountForGpuBalancing(gmx_nbnxn_gpu_t* nbnxmGpu)
     return minimumIlistCount;
 }
 
-std::unique_ptr<nonbonded_verlet_t> init_nb_verlet(const gmx::MDLogger&     mdlog,
-                                                   gmx_bool                 bFEP_NonBonded,
-                                                   const t_inputrec*        ir,
-                                                   const t_forcerec*        fr,
-                                                   const t_commrec*         cr,
-                                                   const gmx_hw_info_t&     hardwareInfo,
-                                                   const gmx_device_info_t* deviceInfo,
-                                                   const gmx_mtop_t*        mtop,
-                                                   matrix                   box,
-                                                   gmx_wallcycle*           wcycle)
+std::unique_ptr<nonbonded_verlet_t> init_nb_verlet(const gmx::MDLogger& mdlog,
+                                                   const t_inputrec*    ir,
+                                                   const t_forcerec*    fr,
+                                                   const t_commrec*     cr,
+                                                   const gmx_hw_info_t& hardwareInfo,
+                                                   const bool           useGpuForNonbonded,
+                                                   const gmx::DeviceStreamManager* deviceStreamManager,
+                                                   const gmx_mtop_t*               mtop,
+                                                   matrix                          box,
+                                                   gmx_wallcycle*                  wcycle)
 {
     const bool emulateGpu = (getenv("GMX_EMULATE_GPU") != nullptr);
-    const bool useGpu     = deviceInfo != nullptr;
 
-    GMX_RELEASE_ASSERT(!(emulateGpu && useGpu),
+    GMX_RELEASE_ASSERT(!(emulateGpu && useGpuForNonbonded),
                        "When GPU emulation is active, there cannot be a GPU assignment");
 
     NonbondedResource nonbondedResource;
-    if (useGpu)
+    if (useGpuForNonbonded)
     {
         nonbondedResource = NonbondedResource::Gpu;
     }
@@ -387,13 +378,13 @@ std::unique_ptr<nonbonded_verlet_t> init_nb_verlet(const gmx::MDLogger&     mdlo
         nonbondedResource = NonbondedResource::Cpu;
     }
 
-    Nbnxm::KernelSetup kernelSetup = pick_nbnxn_kernel(mdlog, fr->use_simd_kernels, hardwareInfo,
-                                                       nonbondedResource, ir, fr->bNonbonded);
+    Nbnxm::KernelSetup kernelSetup =
+            pick_nbnxn_kernel(mdlog, fr->use_simd_kernels, hardwareInfo, nonbondedResource, ir);
 
-    const bool haveMultipleDomains = (DOMAINDECOMP(cr) && cr->dd->nnodes > 1);
+    const bool haveMultipleDomains = havePPDomainDecomposition(cr);
 
-    PairlistParams pairlistParams(kernelSetup.kernelType, bFEP_NonBonded, ir->rlist,
-                                  havePPDomainDecomposition(cr));
+    bool           bFEP_NonBonded = (fr->efep != efepNO) && haveFepPerturbedNBInteractions(*mtop);
+    PairlistParams pairlistParams(kernelSetup.kernelType, bFEP_NonBonded, ir->rlist, haveMultipleDomains);
 
     setupDynamicPairlistPruning(mdlog, ir, mtop, box, fr->ic, &pairlistParams);
 
@@ -423,7 +414,8 @@ std::unique_ptr<nonbonded_verlet_t> init_nb_verlet(const gmx::MDLogger&     mdlo
         enbnxninitcombrule = enbnxninitcombruleNONE;
     }
 
-    auto pinPolicy = (useGpu ? gmx::PinningPolicy::PinnedIfSupported : gmx::PinningPolicy::CannotBePinned);
+    auto pinPolicy = (useGpuForNonbonded ? gmx::PinningPolicy::PinnedIfSupported
+                                         : gmx::PinningPolicy::CannotBePinned);
 
     auto nbat = std::make_unique<nbnxn_atomdata_t>(pinPolicy);
 
@@ -438,15 +430,18 @@ std::unique_ptr<nonbonded_verlet_t> init_nb_verlet(const gmx::MDLogger&     mdlo
     }
     nbnxn_atomdata_init(mdlog, nbat.get(), kernelSetup.kernelType, enbnxninitcombrule, fr->ntype,
                         fr->nbfp, mimimumNumEnergyGroupNonbonded,
-                        (useGpu || emulateGpu) ? 1 : gmx_omp_nthreads_get(emntNonbonded));
+                        (useGpuForNonbonded || emulateGpu) ? 1 : gmx_omp_nthreads_get(emntNonbonded));
 
-    gmx_nbnxn_gpu_t* gpu_nbv                          = nullptr;
-    int              minimumIlistCountForGpuBalancing = 0;
-    if (useGpu)
+    NbnxmGpu* gpu_nbv                          = nullptr;
+    int       minimumIlistCountForGpuBalancing = 0;
+    if (useGpuForNonbonded)
     {
         /* init the NxN GPU data; the last argument tells whether we'll have
          * both local and non-local NB calculation on GPU */
-        gpu_nbv = gpu_init(deviceInfo, fr->ic, pairlistParams, nbat.get(), cr->nodeid, haveMultipleDomains);
+        GMX_RELEASE_ASSERT(
+                (deviceStreamManager != nullptr),
+                "Device stream manager should be initialized in order to use GPU for non-bonded.");
+        gpu_nbv = gpu_init(*deviceStreamManager, fr->ic, pairlistParams, nbat.get(), haveMultipleDomains);
 
         minimumIlistCountForGpuBalancing = getMinimumIlistCountForGpuBalancing(gpu_nbv);
     }
@@ -455,7 +450,7 @@ std::unique_ptr<nonbonded_verlet_t> init_nb_verlet(const gmx::MDLogger&     mdlo
                                                        minimumIlistCountForGpuBalancing);
 
     auto pairSearch = std::make_unique<PairSearch>(
-            ir->ePBC, EI_TPI(ir->eI), DOMAINDECOMP(cr) ? &cr->dd->nc : nullptr,
+            ir->pbcType, EI_TPI(ir->eI), DOMAINDECOMP(cr) ? &cr->dd->numCells : nullptr,
             DOMAINDECOMP(cr) ? domdec_zones(cr->dd) : nullptr, pairlistParams.pairlistType,
             bFEP_NonBonded, gmx_omp_nthreads_get(emntPairsearch), pinPolicy);
 
@@ -469,7 +464,7 @@ nonbonded_verlet_t::nonbonded_verlet_t(std::unique_ptr<PairlistSets>     pairlis
                                        std::unique_ptr<PairSearch>       pairSearch,
                                        std::unique_ptr<nbnxn_atomdata_t> nbat_in,
                                        const Nbnxm::KernelSetup&         kernelSetup,
-                                       gmx_nbnxn_gpu_t*                  gpu_nbv_ptr,
+                                       NbnxmGpu*                         gpu_nbv_ptr,
                                        gmx_wallcycle*                    wcycle) :
     pairlistSets_(std::move(pairlistSets)),
     pairSearch_(std::move(pairSearch)),
index 79c92f59fc290d0d454d49bbde8917fcd248c3cb..dec5605f1f8624f0b1655462342ae41270e98193 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-
+/*! \internal \file
+ *
+ * \brief
+ * Defines constants used to know which Nbnxm kernel flavours (4xn or 2xnn)
+ * can be supported by the SIMD layer in use.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
 #ifndef GMX_NBNXM_NBNXM_SIMD_H
 #define GMX_NBNXM_NBNXM_SIMD_H
 
+#include "config.h"
+
 #include "gromacs/math/vectypes.h"
 #include "gromacs/simd/simd.h"
 #include "gromacs/utility/real.h"
 
-#if GMX_SIMD
-/* The nbnxn SIMD 4xN and 2x(N+N) kernels can be added independently.
+#if GMX_SIMD && GMX_USE_SIMD_KERNELS
+/*! \brief The nbnxn SIMD 4xN and 2x(N+N) kernels can be added independently.
  * Currently the 2xNN SIMD kernels only make sense with:
  *  8-way SIMD: 4x4 setup, works with AVX-256 in single precision
  * 16-way SIMD: 4x8 setup, works with Intel MIC in single precision
@@ -57,6 +67,6 @@
 #        error "No SIMD kernel type defined"
 #    endif
 
-#endif // GMX_SIMD
+#endif // GMX_SIMD && GMX_USE_SIMD_KERNELS
 
-#endif /* _nbnxn_simd_h */
+#endif
index 2263a7874528d6552f4089b4431f174f07bce19a..69d86e96ba2bcbaa229bef10f3b5acd7e369af94 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -32,7 +33,7 @@
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-if(GMX_USE_OPENCL)
+if(GMX_GPU_OPENCL)
     file(GLOB OPENCL_NB_SOURCES *.cpp)
     set(NBNXM_SOURCES ${NBNXM_SOURCES} ${OPENCL_NB_SOURCES} PARENT_SCOPE)
 endif()
@@ -70,11 +71,18 @@ foreach(ELEC_DEF IN LISTS ELEC_DEFS)
             string(REGEX REPLACE ".*=" "" ELEC_NAME "${ELEC_DEF}")
             string(REGEX REPLACE ".*=" "" VDW_NAME "${VDW_DEF}")
             set(OBJ_FILE nbnxm_ocl_kernel${ELEC_NAME}${VDW_NAME}_${VENDOR}.o)
+            # The constants below duplicate various others (e.g. from pairlist.h)
+            # but as the kernels compiled here are not used for production,
+            # it will be OK if the values would fall out of sync.
             add_custom_command(OUTPUT ${OBJ_FILE} COMMAND ${OCL_COMPILER}
                 ${CMAKE_CURRENT_SOURCE_DIR}/nbnxm_ocl_kernels.cl ${CLANG_TIDY_ARGS}
                 -Xclang -finclude-default-header  -D_${VENDOR}_SOURCE_
                 -DGMX_OCL_FASTGEN ${ELEC_DEF} ${VDW_DEF}
-                -DNBNXN_GPU_CLUSTER_SIZE=${CLUSTER_SIZE} -DIATYPE_SHMEM
+                -Dc_nbnxnGpuClusterSize=${CLUSTER_SIZE}
+                -Dc_nbnxnMinDistanceSquared=3.82e-07F
+                -Dc_nbnxnGpuNumClusterPerSupercluster=8
+                -Dc_nbnxnGpuJgroupSize=4
+                -DIATYPE_SHMEM
                 -c -I ${CMAKE_SOURCE_DIR}/src -std=cl1.2
                 -Weverything  -Wno-conversion -Wno-missing-variable-declarations -Wno-used-but-marked-unused
                 -Wno-cast-align -Wno-incompatible-pointer-types
index 08829331c6423f49239a4a172f38dd3b5338d94c..359683337a41829e07d97f34e095fbfe4d590024 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "thread_mpi/atomic.h"
 
+#include "gromacs/gpu_utils/device_context.h"
 #include "gromacs/gpu_utils/gputraits_ocl.h"
 #include "gromacs/gpu_utils/oclutils.h"
+#include "gromacs/hardware/device_information.h"
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/mdtypes/simulation_workload.h"
 #include "gromacs/nbnxm/atomdata.h"
@@ -86,7 +89,6 @@
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
 
-#include "nbnxm_ocl_internal.h"
 #include "nbnxm_ocl_types.h"
 
 namespace Nbnxm
@@ -94,8 +96,7 @@ namespace Nbnxm
 
 /*! \brief Convenience constants */
 //@{
-static const int c_numClPerSupercl = c_nbnxnGpuNumClusterPerSupercluster;
-static const int c_clSize          = c_nbnxnGpuClusterSize;
+static constexpr int c_clSize = c_nbnxnGpuClusterSize;
 //@}
 
 
@@ -103,12 +104,12 @@ static const int c_clSize          = c_nbnxnGpuClusterSize;
  */
 static inline void validate_global_work_size(const KernelLaunchConfig& config,
                                              int                       work_dim,
-                                             const gmx_device_info_t*  dinfo)
+                                             const DeviceInformation*  dinfo)
 {
     cl_uint device_size_t_size_bits;
     cl_uint host_size_t_size_bits;
 
-    assert(dinfo);
+    GMX_ASSERT(dinfo, "Need a valid device info object");
 
     size_t global_work_size[3];
     GMX_ASSERT(work_dim <= 3, "Not supporting hyper-grids just yet");
@@ -161,7 +162,7 @@ static inline void validate_global_work_size(const KernelLaunchConfig& config,
  */
 
 /*! \brief Force-only kernel function names. */
-static const char* nb_kfunc_noener_noprune_ptr[eelOclNR][evdwOclNR] = {
+static const char* nb_kfunc_noener_noprune_ptr[eelTypeNR][evdwTypeNR] = {
     { "nbnxn_kernel_ElecCut_VdwLJ_F_opencl", "nbnxn_kernel_ElecCut_VdwLJCombGeom_F_opencl",
       "nbnxn_kernel_ElecCut_VdwLJCombLB_F_opencl", "nbnxn_kernel_ElecCut_VdwLJFsw_F_opencl",
       "nbnxn_kernel_ElecCut_VdwLJPsw_F_opencl", "nbnxn_kernel_ElecCut_VdwLJEwCombGeom_F_opencl",
@@ -195,7 +196,7 @@ static const char* nb_kfunc_noener_noprune_ptr[eelOclNR][evdwOclNR] = {
 };
 
 /*! \brief Force + energy kernel function pointers. */
-static const char* nb_kfunc_ener_noprune_ptr[eelOclNR][evdwOclNR] = {
+static const char* nb_kfunc_ener_noprune_ptr[eelTypeNR][evdwTypeNR] = {
     { "nbnxn_kernel_ElecCut_VdwLJ_VF_opencl", "nbnxn_kernel_ElecCut_VdwLJCombGeom_VF_opencl",
       "nbnxn_kernel_ElecCut_VdwLJCombLB_VF_opencl", "nbnxn_kernel_ElecCut_VdwLJFsw_VF_opencl",
       "nbnxn_kernel_ElecCut_VdwLJPsw_VF_opencl", "nbnxn_kernel_ElecCut_VdwLJEwCombGeom_VF_opencl",
@@ -230,7 +231,7 @@ static const char* nb_kfunc_ener_noprune_ptr[eelOclNR][evdwOclNR] = {
 };
 
 /*! \brief Force + pruning kernel function pointers. */
-static const char* nb_kfunc_noener_prune_ptr[eelOclNR][evdwOclNR] = {
+static const char* nb_kfunc_noener_prune_ptr[eelTypeNR][evdwTypeNR] = {
     { "nbnxn_kernel_ElecCut_VdwLJ_F_prune_opencl",
       "nbnxn_kernel_ElecCut_VdwLJCombGeom_F_prune_opencl",
       "nbnxn_kernel_ElecCut_VdwLJCombLB_F_prune_opencl",
@@ -271,7 +272,7 @@ static const char* nb_kfunc_noener_prune_ptr[eelOclNR][evdwOclNR] = {
 };
 
 /*! \brief Force + energy + pruning kernel function pointers. */
-static const char* nb_kfunc_ener_prune_ptr[eelOclNR][evdwOclNR] = {
+static const char* nb_kfunc_ener_prune_ptr[eelTypeNR][evdwTypeNR] = {
     { "nbnxn_kernel_ElecCut_VdwLJ_VF_prune_opencl",
       "nbnxn_kernel_ElecCut_VdwLJCombGeom_VF_prune_opencl",
       "nbnxn_kernel_ElecCut_VdwLJCombLB_VF_prune_opencl",
@@ -340,14 +341,16 @@ static inline cl_kernel selectPruneKernel(cl_kernel kernel_pruneonly[], bool fir
  *  OpenCL kernel objects are cached in nb. If the requested kernel is not
  *  found in the cache, it will be created and the cache will be updated.
  */
-static inline cl_kernel select_nbnxn_kernel(gmx_nbnxn_ocl_t* nb, int eeltype, int evdwtype, bool bDoEne, bool bDoPrune)
+static inline cl_kernel select_nbnxn_kernel(NbnxmGpu* nb, int eeltype, int evdwtype, bool bDoEne, bool bDoPrune)
 {
     const char* kernel_name_to_run;
     cl_kernel*  kernel_ptr;
     cl_int      cl_error;
 
-    assert(eeltype < eelOclNR);
-    assert(evdwtype < evdwOclNR);
+    GMX_ASSERT(eeltype < eelTypeNR,
+               "The electrostatics type requested is not implemented in the OpenCL kernels.");
+    GMX_ASSERT(evdwtype < evdwTypeNR,
+               "The VdW type requested is not implemented in the OpenCL kernels.");
 
     if (bDoEne)
     {
@@ -379,9 +382,10 @@ static inline cl_kernel select_nbnxn_kernel(gmx_nbnxn_ocl_t* nb, int eeltype, in
     if (nullptr == kernel_ptr[0])
     {
         *kernel_ptr = clCreateKernel(nb->dev_rundata->program, kernel_name_to_run, &cl_error);
-        assert(cl_error == CL_SUCCESS);
+        GMX_ASSERT(cl_error == CL_SUCCESS, ("clCreateKernel failed: " + ocl_get_error_string(cl_error)
+                                            + " for kernel named " + kernel_name_to_run)
+                                                   .c_str());
     }
-    // TODO: handle errors
 
     return *kernel_ptr;
 }
@@ -395,7 +399,7 @@ static inline int calc_shmem_required_nonbonded(int vdwType, bool bPrefetchLjPar
     /* size of shmem (force-buffers/xq/atom type preloading) */
     /* NOTE: with the default kernel on sm3.0 we need shmem only for pre-loading */
     /* i-atom x+q in shared memory */
-    shmem = c_numClPerSupercl * c_clSize * sizeof(float) * 4; /* xqib */
+    shmem = c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(float) * 4; /* xqib */
     /* cj in shared memory, for both warps separately
      * TODO: in the "nowarp kernels we load cj only once  so the factor 2 is not needed.
      */
@@ -405,12 +409,13 @@ static inline int calc_shmem_required_nonbonded(int vdwType, bool bPrefetchLjPar
         if (useLjCombRule(vdwType))
         {
             /* i-atom LJ combination parameters in shared memory */
-            shmem += c_numClPerSupercl * c_clSize * 2 * sizeof(float); /* atib abused for ljcp, float2 */
+            shmem += c_nbnxnGpuNumClusterPerSupercluster * c_clSize * 2
+                     * sizeof(float); /* atib abused for ljcp, float2 */
         }
         else
         {
             /* i-atom types in shared memory */
-            shmem += c_numClPerSupercl * c_clSize * sizeof(int); /* atib */
+            shmem += c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(int); /* atib */
         }
     }
     /* force reduction buffers in shared memory */
@@ -428,7 +433,7 @@ static inline int calc_shmem_required_nonbonded(int vdwType, bool bPrefetchLjPar
  *
  *  This function is called before the launch of both nbnxn and prune kernels.
  */
-static void fillin_ocl_structures(cl_nbparam_t* nbp, cl_nbparam_params_t* nbparams_params)
+static void fillin_ocl_structures(NBParamGpu* nbp, cl_nbparam_params_t* nbparams_params)
 {
     nbparams_params->coulomb_tab_scale = nbp->coulomb_tab_scale;
     nbparams_params->c_rf              = nbp->c_rf;
@@ -465,12 +470,13 @@ static void sync_ocl_event(cl_command_queue stream, cl_event* ocl_event)
 
     /* Release event and reset it to 0. It is ok to release it as enqueuewaitforevents performs implicit retain for events. */
     cl_error = clReleaseEvent(*ocl_event);
-    assert(CL_SUCCESS == cl_error);
+    GMX_ASSERT(cl_error == CL_SUCCESS,
+               ("clReleaseEvent failed: " + ocl_get_error_string(cl_error)).c_str());
     *ocl_event = nullptr;
 }
 
 /*! \brief Launch asynchronously the xq buffer host to device copy. */
-void gpu_copy_xq_to_gpu(gmx_nbnxn_ocl_t* nb, const nbnxn_atomdata_t* nbatom, const AtomLocality atomLocality)
+void gpu_copy_xq_to_gpu(NbnxmGpu* nb, const nbnxn_atomdata_t* nbatom, const AtomLocality atomLocality)
 {
     GMX_ASSERT(nb, "Need a valid nbnxn_gpu object");
 
@@ -479,12 +485,12 @@ void gpu_copy_xq_to_gpu(gmx_nbnxn_ocl_t* nb, const nbnxn_atomdata_t* nbatom, con
     /* local/nonlocal offset and length used for xq and f */
     int adat_begin, adat_len;
 
-    cl_atomdata_t*   adat   = nb->atdat;
-    cl_plist_t*      plist  = nb->plist[iloc];
-    cl_timers_t*     t      = nb->timers;
-    cl_command_queue stream = nb->stream[iloc];
+    cl_atomdata_t*      adat         = nb->atdat;
+    gpu_plist*          plist        = nb->plist[iloc];
+    cl_timers_t*        t            = nb->timers;
+    const DeviceStream& deviceStream = *nb->deviceStreams[iloc];
 
-    bool bDoTime = (nb->bDoTime) != 0;
+    bool bDoTime = nb->bDoTime;
 
     /* Don't launch the non-local H2D copy if there is no dependent
        work to do: neither non-local nor other (e.g. bonded) work
@@ -517,17 +523,19 @@ void gpu_copy_xq_to_gpu(gmx_nbnxn_ocl_t* nb, const nbnxn_atomdata_t* nbatom, con
     /* beginning of timed HtoD section */
     if (bDoTime)
     {
-        t->xf[atomLocality].nb_h2d.openTimingRegion(stream);
+        t->xf[atomLocality].nb_h2d.openTimingRegion(deviceStream);
     }
 
     /* HtoD x, q */
-    ocl_copy_H2D_async(adat->xq, nbatom->x().data() + adat_begin * 4,
-                       adat_begin * sizeof(float) * 4, adat_len * sizeof(float) * 4, stream,
+    GMX_ASSERT(sizeof(float) == sizeof(*nbatom->x().data()),
+               "The size of the xyzq buffer element should be equal to the size of float4.");
+    copyToDeviceBuffer(&adat->xq, nbatom->x().data() + adat_begin * 4, adat_begin * 4, adat_len * 4,
+                       deviceStream, GpuApiCallBehavior::Async,
                        bDoTime ? t->xf[atomLocality].nb_h2d.fetchNextEvent() : nullptr);
 
     if (bDoTime)
     {
-        t->xf[atomLocality].nb_h2d.closeTimingRegion(stream);
+        t->xf[atomLocality].nb_h2d.closeTimingRegion(deviceStream);
     }
 
     /* When we get here all misc operations issues in the local stream as well as
@@ -538,19 +546,21 @@ void gpu_copy_xq_to_gpu(gmx_nbnxn_ocl_t* nb, const nbnxn_atomdata_t* nbatom, con
         if (iloc == InteractionLocality::Local)
         {
             cl_int gmx_used_in_debug cl_error = clEnqueueMarkerWithWaitList(
-                    stream, 0, nullptr, &(nb->misc_ops_and_local_H2D_done));
-            assert(CL_SUCCESS == cl_error);
+                    deviceStream.stream(), 0, nullptr, &(nb->misc_ops_and_local_H2D_done));
+            GMX_ASSERT(cl_error == CL_SUCCESS,
+                       ("clEnqueueMarkerWithWaitList failed: " + ocl_get_error_string(cl_error)).c_str());
 
             /* Based on the v1.2 section 5.13 of the OpenCL spec, a flush is needed
              * in the local stream in order to be able to sync with the above event
              * from the non-local stream.
              */
-            cl_error = clFlush(stream);
-            assert(CL_SUCCESS == cl_error);
+            cl_error = clFlush(deviceStream.stream());
+            GMX_ASSERT(cl_error == CL_SUCCESS,
+                       ("clFlush failed: " + ocl_get_error_string(cl_error)).c_str());
         }
         else
         {
-            sync_ocl_event(stream, &(nb->misc_ops_and_local_H2D_done));
+            sync_ocl_event(deviceStream.stream(), &(nb->misc_ops_and_local_H2D_done));
         }
     }
 }
@@ -574,15 +584,15 @@ void gpu_copy_xq_to_gpu(gmx_nbnxn_ocl_t* nb, const nbnxn_atomdata_t* nbatom, con
    misc_ops_done event to record the point in time when the above  operations
    are finished and synchronize with this event in the non-local stream.
  */
-void gpu_launch_kernel(gmx_nbnxn_ocl_t* nb, const gmx::StepWorkload& stepWork, const Nbnxm::InteractionLocality iloc)
+void gpu_launch_kernel(NbnxmGpu* nb, const gmx::StepWorkload& stepWork, const Nbnxm::InteractionLocality iloc)
 {
-    cl_atomdata_t*   adat   = nb->atdat;
-    cl_nbparam_t*    nbp    = nb->nbparam;
-    cl_plist_t*      plist  = nb->plist[iloc];
-    cl_timers_t*     t      = nb->timers;
-    cl_command_queue stream = nb->stream[iloc];
+    cl_atomdata_t*      adat         = nb->atdat;
+    NBParamGpu*         nbp          = nb->nbparam;
+    gpu_plist*          plist        = nb->plist[iloc];
+    cl_timers_t*        t            = nb->timers;
+    const DeviceStream& deviceStream = *nb->deviceStreams[iloc];
 
-    bool bDoTime = (nb->bDoTime) != 0;
+    bool bDoTime = nb->bDoTime;
 
     cl_nbparam_params_t nbparams_params;
 
@@ -621,19 +631,18 @@ void gpu_launch_kernel(gmx_nbnxn_ocl_t* nb, const gmx::StepWorkload& stepWork, c
     /* beginning of timed nonbonded calculation section */
     if (bDoTime)
     {
-        t->interaction[iloc].nb_k.openTimingRegion(stream);
+        t->interaction[iloc].nb_k.openTimingRegion(deviceStream);
     }
 
     /* kernel launch config */
 
     KernelLaunchConfig config;
     config.sharedMemorySize = calc_shmem_required_nonbonded(nbp->vdwtype, nb->bPrefetchLjParam);
-    config.stream           = stream;
     config.blockSize[0]     = c_clSize;
     config.blockSize[1]     = c_clSize;
     config.gridSize[0]      = plist->nsci;
 
-    validate_global_work_size(config, 3, nb->dev_info);
+    validate_global_work_size(config, 3, &nb->deviceContext_->deviceInfo());
 
     if (debug)
     {
@@ -642,7 +651,8 @@ void gpu_launch_kernel(gmx_nbnxn_ocl_t* nb, const gmx::StepWorkload& stepWork, c
                 "Global work size : %zux%zu\n\t#Super-clusters/clusters: %d/%d (%d)\n",
                 config.blockSize[0], config.blockSize[1], config.blockSize[2],
                 config.blockSize[0] * config.gridSize[0], config.blockSize[1] * config.gridSize[1],
-                plist->nsci * c_numClPerSupercl, c_numClPerSupercl, plist->na_c);
+                plist->nsci * c_nbnxnGpuNumClusterPerSupercluster,
+                c_nbnxnGpuNumClusterPerSupercluster, plist->na_c);
     }
 
     fillin_ocl_structures(nbp, &nbparams_params);
@@ -661,24 +671,23 @@ void gpu_launch_kernel(gmx_nbnxn_ocl_t* nb, const gmx::StepWorkload& stepWork, c
     {
         const auto kernelArgs = prepareGpuKernelArguments(
                 kernel, config, &nbparams_params, &adat->xq, &adat->f, &adat->e_lj, &adat->e_el,
-                &adat->fshift, &adat->lj_comb, &adat->shift_vec, &nbp->nbfp_climg2d, &nbp->nbfp_comb_climg2d,
-                &nbp->coulomb_tab_climg2d, &plist->sci, &plist->cj4, &plist->excl, &computeFshift);
+                &adat->fshift, &adat->lj_comb, &adat->shift_vec, &nbp->nbfp, &nbp->nbfp_comb,
+                &nbp->coulomb_tab, &plist->sci, &plist->cj4, &plist->excl, &computeFshift);
 
-        launchGpuKernel(kernel, config, timingEvent, kernelName, kernelArgs);
+        launchGpuKernel(kernel, config, deviceStream, timingEvent, kernelName, kernelArgs);
     }
     else
     {
         const auto kernelArgs = prepareGpuKernelArguments(
                 kernel, config, &adat->ntypes, &nbparams_params, &adat->xq, &adat->f, &adat->e_lj,
-                &adat->e_el, &adat->fshift, &adat->atom_types, &adat->shift_vec, &nbp->nbfp_climg2d,
-                &nbp->nbfp_comb_climg2d, &nbp->coulomb_tab_climg2d, &plist->sci, &plist->cj4,
-                &plist->excl, &computeFshift);
-        launchGpuKernel(kernel, config, timingEvent, kernelName, kernelArgs);
+                &adat->e_el, &adat->fshift, &adat->atom_types, &adat->shift_vec, &nbp->nbfp, &nbp->nbfp_comb,
+                &nbp->coulomb_tab, &plist->sci, &plist->cj4, &plist->excl, &computeFshift);
+        launchGpuKernel(kernel, config, deviceStream, timingEvent, kernelName, kernelArgs);
     }
 
     if (bDoTime)
     {
-        t->interaction[iloc].nb_k.closeTimingRegion(stream);
+        t->interaction[iloc].nb_k.closeTimingRegion(deviceStream);
     }
 }
 
@@ -696,7 +705,7 @@ static inline int calc_shmem_required_prune(const int num_threads_z)
     int shmem;
 
     /* i-atom x in shared memory (for convenience we load all 4 components including q) */
-    shmem = c_numClPerSupercl * c_clSize * sizeof(float) * 4;
+    shmem = c_nbnxnGpuNumClusterPerSupercluster * c_clSize * sizeof(float) * 4;
     /* cj in shared memory, for each warp separately
      * Note: only need to load once per wavefront, but to keep the code simple,
      * for now we load twice on AMD.
@@ -712,14 +721,14 @@ static inline int calc_shmem_required_prune(const int num_threads_z)
  * Launch the pairlist prune only kernel for the given locality.
  * \p numParts tells in how many parts, i.e. calls the list will be pruned.
  */
-void gpu_launch_kernel_pruneonly(gmx_nbnxn_gpu_t* nb, const InteractionLocality iloc, const int numParts)
+void gpu_launch_kernel_pruneonly(NbnxmGpu* nb, const InteractionLocality iloc, const int numParts)
 {
-    cl_atomdata_t*   adat    = nb->atdat;
-    cl_nbparam_t*    nbp     = nb->nbparam;
-    cl_plist_t*      plist   = nb->plist[iloc];
-    cl_timers_t*     t       = nb->timers;
-    cl_command_queue stream  = nb->stream[iloc];
-    bool             bDoTime = nb->bDoTime == CL_TRUE;
+    cl_atomdata_t*      adat         = nb->atdat;
+    NBParamGpu*         nbp          = nb->nbparam;
+    gpu_plist*          plist        = nb->plist[iloc];
+    cl_timers_t*        t            = nb->timers;
+    const DeviceStream& deviceStream = *nb->deviceStreams[iloc];
+    bool                bDoTime      = nb->bDoTime;
 
     if (plist->haveFreshList)
     {
@@ -773,7 +782,7 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_gpu_t* nb, const InteractionLocality
     /* beginning of timed prune calculation section */
     if (bDoTime)
     {
-        timer->openTimingRegion(stream);
+        timer->openTimingRegion(deviceStream);
     }
 
     /* Kernel launch config:
@@ -781,18 +790,18 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_gpu_t* nb, const InteractionLocality
      *   and j-cluster concurrency, in x, y, and z, respectively.
      * - The 1D block-grid contains as many blocks as super-clusters.
      */
-    int num_threads_z = getOclPruneKernelJ4Concurrency(nb->dev_info->vendor_e);
+    int num_threads_z = c_oclPruneKernelJ4ConcurrencyDEFAULT;
+
 
     /* kernel launch config */
     KernelLaunchConfig config;
     config.sharedMemorySize = calc_shmem_required_prune(num_threads_z);
-    config.stream           = stream;
     config.blockSize[0]     = c_clSize;
     config.blockSize[1]     = c_clSize;
     config.blockSize[2]     = num_threads_z;
     config.gridSize[0]      = numSciInPart;
 
-    validate_global_work_size(config, 3, nb->dev_info);
+    validate_global_work_size(config, 3, &nb->deviceContext_->deviceInfo());
 
     if (debug)
     {
@@ -802,7 +811,8 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_gpu_t* nb, const InteractionLocality
                 "\tShMem: %zu\n",
                 config.blockSize[0], config.blockSize[1], config.blockSize[2],
                 config.blockSize[0] * config.gridSize[0], config.blockSize[1] * config.gridSize[1],
-                plist->nsci * c_numClPerSupercl, c_numClPerSupercl, plist->na_c, config.sharedMemorySize);
+                plist->nsci * c_nbnxnGpuNumClusterPerSupercluster,
+                c_nbnxnGpuNumClusterPerSupercluster, plist->na_c, config.sharedMemorySize);
     }
 
     cl_nbparam_params_t nbparams_params;
@@ -814,7 +824,7 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_gpu_t* nb, const InteractionLocality
     const auto     kernelArgs   = prepareGpuKernelArguments(pruneKernel, config, &nbparams_params,
                                                       &adat->xq, &adat->shift_vec, &plist->sci,
                                                       &plist->cj4, &plist->imask, &numParts, &part);
-    launchGpuKernel(pruneKernel, config, timingEvent, kernelName, kernelArgs);
+    launchGpuKernel(pruneKernel, config, deviceStream, timingEvent, kernelName, kernelArgs);
 
     if (plist->haveFreshList)
     {
@@ -830,7 +840,7 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_gpu_t* nb, const InteractionLocality
 
     if (bDoTime)
     {
-        timer->closeTimingRegion(stream);
+        timer->closeTimingRegion(deviceStream);
     }
 }
 
@@ -838,7 +848,7 @@ void gpu_launch_kernel_pruneonly(gmx_nbnxn_gpu_t* nb, const InteractionLocality
  * Launch asynchronously the download of nonbonded forces from the GPU
  * (and energies/shift forces if required).
  */
-void gpu_launch_cpyback(gmx_nbnxn_ocl_t*         nb,
+void gpu_launch_cpyback(NbnxmGpu*                nb,
                         struct nbnxn_atomdata_t* nbatom,
                         const gmx::StepWorkload& stepWork,
                         const AtomLocality       aloc)
@@ -851,10 +861,10 @@ void gpu_launch_cpyback(gmx_nbnxn_ocl_t*         nb,
     /* determine interaction locality from atom locality */
     const InteractionLocality iloc = gpuAtomToInteractionLocality(aloc);
 
-    cl_atomdata_t*   adat    = nb->atdat;
-    cl_timers_t*     t       = nb->timers;
-    bool             bDoTime = nb->bDoTime == CL_TRUE;
-    cl_command_queue stream  = nb->stream[iloc];
+    cl_atomdata_t*      adat         = nb->atdat;
+    cl_timers_t*        t            = nb->timers;
+    bool                bDoTime      = nb->bDoTime;
+    const DeviceStream& deviceStream = *nb->deviceStreams[iloc];
 
     /* don't launch non-local copy-back if there was no non-local work to do */
     if ((iloc == InteractionLocality::NonLocal) && !haveGpuShortRangeWork(*nb, iloc))
@@ -876,24 +886,26 @@ void gpu_launch_cpyback(gmx_nbnxn_ocl_t*         nb,
     /* beginning of timed D2H section */
     if (bDoTime)
     {
-        t->xf[aloc].nb_d2h.openTimingRegion(stream);
+        t->xf[aloc].nb_d2h.openTimingRegion(deviceStream);
     }
 
     /* With DD the local D2H transfer can only start after the non-local
        has been launched. */
     if (iloc == InteractionLocality::Local && nb->bNonLocalStreamActive)
     {
-        sync_ocl_event(stream, &(nb->nonlocal_done));
+        sync_ocl_event(deviceStream.stream(), &(nb->nonlocal_done));
     }
 
     /* DtoH f */
-    ocl_copy_D2H_async(nbatom->out[0].f.data() + adat_begin * 3, adat->f,
-                       adat_begin * 3 * sizeof(float), (adat_len)*adat->f_elem_size, stream,
-                       bDoTime ? t->xf[aloc].nb_d2h.fetchNextEvent() : nullptr);
+    GMX_ASSERT(sizeof(*nbatom->out[0].f.data()) == sizeof(float),
+               "The host force buffer should be in single precision to match device data size.");
+    copyFromDeviceBuffer(&nbatom->out[0].f.data()[adat_begin * DIM], &adat->f, adat_begin * DIM,
+                         adat_len * DIM, deviceStream, GpuApiCallBehavior::Async,
+                         bDoTime ? t->xf[aloc].nb_d2h.fetchNextEvent() : nullptr);
 
     /* kick off work */
-    cl_error = clFlush(stream);
-    assert(CL_SUCCESS == cl_error);
+    cl_error = clFlush(deviceStream.stream());
+    GMX_ASSERT(cl_error == CL_SUCCESS, ("clFlush failed: " + ocl_get_error_string(cl_error)).c_str());
 
     /* After the non-local D2H is launched the nonlocal_done event can be
        recorded which signals that the local D2H can proceed. This event is not
@@ -901,8 +913,9 @@ void gpu_launch_cpyback(gmx_nbnxn_ocl_t*         nb,
        data back first. */
     if (iloc == InteractionLocality::NonLocal)
     {
-        cl_error = clEnqueueMarkerWithWaitList(stream, 0, nullptr, &(nb->nonlocal_done));
-        assert(CL_SUCCESS == cl_error);
+        cl_error = clEnqueueMarkerWithWaitList(deviceStream.stream(), 0, nullptr, &(nb->nonlocal_done));
+        GMX_ASSERT(cl_error == CL_SUCCESS,
+                   ("clEnqueueMarkerWithWaitList failed: " + ocl_get_error_string(cl_error)).c_str());
         nb->bNonLocalStreamActive = CL_TRUE;
     }
 
@@ -912,84 +925,32 @@ void gpu_launch_cpyback(gmx_nbnxn_ocl_t*         nb,
         /* DtoH fshift when virial is needed */
         if (stepWork.computeVirial)
         {
-            ocl_copy_D2H_async(nb->nbst.fshift, adat->fshift, 0, SHIFTS * adat->fshift_elem_size,
-                               stream, bDoTime ? t->xf[aloc].nb_d2h.fetchNextEvent() : nullptr);
+            GMX_ASSERT(sizeof(*nb->nbst.fshift) == DIM * sizeof(float),
+                       "Sizes of host- and device-side shift vector elements should be the same.");
+            copyFromDeviceBuffer(reinterpret_cast<float*>(nb->nbst.fshift), &adat->fshift, 0,
+                                 SHIFTS * DIM, deviceStream, GpuApiCallBehavior::Async,
+                                 bDoTime ? t->xf[aloc].nb_d2h.fetchNextEvent() : nullptr);
         }
 
         /* DtoH energies */
         if (stepWork.computeEnergy)
         {
-            ocl_copy_D2H_async(nb->nbst.e_lj, adat->e_lj, 0, sizeof(float), stream,
-                               bDoTime ? t->xf[aloc].nb_d2h.fetchNextEvent() : nullptr);
-
-            ocl_copy_D2H_async(nb->nbst.e_el, adat->e_el, 0, sizeof(float), stream,
-                               bDoTime ? t->xf[aloc].nb_d2h.fetchNextEvent() : nullptr);
+            GMX_ASSERT(sizeof(*nb->nbst.e_lj) == sizeof(float),
+                       "Sizes of host- and device-side LJ energy terms should be the same.");
+            copyFromDeviceBuffer(nb->nbst.e_lj, &adat->e_lj, 0, 1, deviceStream, GpuApiCallBehavior::Async,
+                                 bDoTime ? t->xf[aloc].nb_d2h.fetchNextEvent() : nullptr);
+            GMX_ASSERT(sizeof(*nb->nbst.e_el) == sizeof(float),
+                       "Sizes of host- and device-side electrostatic energy terms should be the "
+                       "same.");
+            copyFromDeviceBuffer(nb->nbst.e_el, &adat->e_el, 0, 1, deviceStream, GpuApiCallBehavior::Async,
+                                 bDoTime ? t->xf[aloc].nb_d2h.fetchNextEvent() : nullptr);
         }
     }
 
     if (bDoTime)
     {
-        t->xf[aloc].nb_d2h.closeTimingRegion(stream);
-    }
-}
-
-
-/*! \brief Selects the Ewald kernel type, analytical or tabulated, single or twin cut-off. */
-int nbnxn_gpu_pick_ewald_kernel_type(const interaction_const_t& ic)
-{
-    bool bTwinCut = (ic.rcoulomb != ic.rvdw);
-    bool bUseAnalyticalEwald, bForceAnalyticalEwald, bForceTabulatedEwald;
-    int  kernel_type;
-
-    /* Benchmarking/development environment variables to force the use of
-       analytical or tabulated Ewald kernel. */
-    bForceAnalyticalEwald = (getenv("GMX_OCL_NB_ANA_EWALD") != nullptr);
-    bForceTabulatedEwald  = (getenv("GMX_OCL_NB_TAB_EWALD") != nullptr);
-
-    if (bForceAnalyticalEwald && bForceTabulatedEwald)
-    {
-        gmx_incons(
-                "Both analytical and tabulated Ewald OpenCL non-bonded kernels "
-                "requested through environment variables.");
-    }
-
-    /* OpenCL: By default, use analytical Ewald
-     * TODO: tabulated does not work, it needs fixing, see init_nbparam() in nbnxn_ocl_data_mgmt.cpp
-     *
-     * TODO: decide if dev_info parameter should be added to recognize NVIDIA CC>=3.0 devices.
-     *
-     */
-    /* By default use analytical Ewald. */
-    bUseAnalyticalEwald = true;
-    if (bForceAnalyticalEwald)
-    {
-        if (debug)
-        {
-            fprintf(debug, "Using analytical Ewald OpenCL kernels\n");
-        }
-    }
-    else if (bForceTabulatedEwald)
-    {
-        bUseAnalyticalEwald = false;
-
-        if (debug)
-        {
-            fprintf(debug, "Using tabulated Ewald OpenCL kernels\n");
-        }
+        t->xf[aloc].nb_d2h.closeTimingRegion(deviceStream);
     }
-
-    /* Use twin cut-off kernels if requested by bTwinCut or the env. var.
-       forces it (use it for debugging/benchmarking only). */
-    if (!bTwinCut && (getenv("GMX_OCL_NB_EWALD_TWINCUT") == nullptr))
-    {
-        kernel_type = bUseAnalyticalEwald ? eelOclEWALD_ANA : eelOclEWALD_TAB;
-    }
-    else
-    {
-        kernel_type = bUseAnalyticalEwald ? eelOclEWALD_ANA_TWIN : eelOclEWALD_TAB_TWIN;
-    }
-
-    return kernel_type;
 }
 
 } // namespace Nbnxm
index 2d3d29110ca82396d51e299af27f74ba920622cc..87872c6552eec67a01d182e40fe1786a81ebe64a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+/*! \internal \file
+ *
+ * \brief
+ * Declares constants for OpenCL code
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
 #ifndef NBNXN_OPENCL_CONSTS_H
 #define NBNXN_OPENCL_CONSTS_H
 
index ced508feda2515d6a18f4a254e41c3acc36ed33e..19b861db0ce02ce59408146b07e406cdd20ad2b5 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <cmath>
 
-// TODO We would like to move this down, but the way gmx_nbnxn_gpu_t
-//      is currently declared means this has to be before gpu_types.h
-#include "nbnxm_ocl_types.h"
-
-// TODO Remove this comment when the above order issue is resolved
-#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/gpu_utils/device_stream_manager.h"
 #include "gromacs/gpu_utils/oclutils.h"
-#include "gromacs/hardware/gpu_hw_info.h"
+#include "gromacs/hardware/device_information.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdtypes/interaction_const.h"
@@ -68,6 +65,7 @@
 #include "gromacs/nbnxm/gpu_jit_support.h"
 #include "gromacs/nbnxm/nbnxm.h"
 #include "gromacs/nbnxm/nbnxm_gpu.h"
+#include "gromacs/nbnxm/nbnxm_gpu_data_mgmt.h"
 #include "gromacs/nbnxm/pairlistsets.h"
 #include "gromacs/pbcutil/ishift.h"
 #include "gromacs/timing/gpu_timing.h"
@@ -77,7 +75,7 @@
 #include "gromacs/utility/real.h"
 #include "gromacs/utility/smalloc.h"
 
-#include "nbnxm_ocl_internal.h"
+#include "nbnxm_ocl_types.h"
 
 namespace Nbnxm
 {
@@ -102,93 +100,19 @@ namespace Nbnxm
 static unsigned int gpu_min_ci_balanced_factor = 50;
 
 
-/*! \brief Returns true if LJ combination rules are used in the non-bonded kernels.
- *
- * Full doc in nbnxn_ocl_internal.h */
-bool useLjCombRule(int vdwType)
-{
-    return (vdwType == evdwOclCUTCOMBGEOM || vdwType == evdwOclCUTCOMBLB);
-}
-
-/*! \brief Tabulates the Ewald Coulomb force and initializes the size/scale
- * and the table GPU array.
- *
- * If called with an already allocated table, it just re-uploads the
- * table.
- */
-static void init_ewald_coulomb_force_table(const EwaldCorrectionTables&     tables,
-                                           cl_nbparam_t*                    nbp,
-                                           const gmx_device_runtime_data_t* runData)
-{
-    cl_mem coul_tab;
-
-    cl_int cl_error;
-
-    if (nbp->coulomb_tab_climg2d != nullptr)
-    {
-        freeDeviceBuffer(&(nbp->coulomb_tab_climg2d));
-    }
-
-    /* Switched from using textures to using buffers */
-    // TODO: decide which alternative is most efficient - textures or buffers.
-    /*
-       cl_image_format array_format;
-
-       array_format.image_channel_data_type = CL_FLOAT;
-       array_format.image_channel_order     = CL_R;
-
-       coul_tab = clCreateImage2D(runData->context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR,
-       &array_format, tabsize, 1, 0, ftmp, &cl_error);
-     */
-
-    coul_tab = clCreateBuffer(
-            runData->context, CL_MEM_READ_ONLY | CL_MEM_HOST_WRITE_ONLY | CL_MEM_COPY_HOST_PTR,
-            tables.tableF.size() * sizeof(cl_float), const_cast<real*>(tables.tableF.data()), &cl_error);
-    GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                       ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
-
-    nbp->coulomb_tab_climg2d = coul_tab;
-    nbp->coulomb_tab_scale   = tables.scale;
-}
-
-
 /*! \brief Initializes the atomdata structure first time, it only gets filled at
     pair-search.
  */
-static void init_atomdata_first(cl_atomdata_t* ad, int ntypes, gmx_device_runtime_data_t* runData)
+static void init_atomdata_first(cl_atomdata_t* ad, int ntypes, const DeviceContext& deviceContext)
 {
-    cl_int cl_error;
-
     ad->ntypes = ntypes;
 
-    /* An element of the shift_vec device buffer has the same size as one element
-       of the host side shift_vec buffer. */
-    ad->shift_vec_elem_size = sizeof(*nbnxn_atomdata_t::shift_vec.data());
-
-    ad->shift_vec = clCreateBuffer(runData->context, CL_MEM_READ_ONLY | CL_MEM_HOST_WRITE_ONLY,
-                                   SHIFTS * ad->shift_vec_elem_size, nullptr, &cl_error);
-    GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                       ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
+    allocateDeviceBuffer(&ad->shift_vec, SHIFTS * DIM, deviceContext);
     ad->bShiftVecUploaded = CL_FALSE;
 
-    /* An element of the fshift device buffer has the same size as one element
-       of the host side fshift buffer. */
-    ad->fshift_elem_size = sizeof(*cl_nb_staging_t::fshift);
-
-    ad->fshift = clCreateBuffer(runData->context, CL_MEM_READ_WRITE | CL_MEM_HOST_READ_ONLY,
-                                SHIFTS * ad->fshift_elem_size, nullptr, &cl_error);
-    GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                       ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
-
-    ad->e_lj = clCreateBuffer(runData->context, CL_MEM_READ_WRITE | CL_MEM_HOST_READ_ONLY,
-                              sizeof(float), nullptr, &cl_error);
-    GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                       ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
-
-    ad->e_el = clCreateBuffer(runData->context, CL_MEM_READ_WRITE | CL_MEM_HOST_READ_ONLY,
-                              sizeof(float), nullptr, &cl_error);
-    GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                       ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
+    allocateDeviceBuffer(&ad->fshift, SHIFTS * DIM, deviceContext);
+    allocateDeviceBuffer(&ad->e_lj, 1, deviceContext);
+    allocateDeviceBuffer(&ad->e_el, 1, deviceContext);
 
     /* initialize to nullptr pointers to data that is not allocated here and will
        need reallocation in nbnxn_gpu_init_atomdata */
@@ -200,30 +124,6 @@ static void init_atomdata_first(cl_atomdata_t* ad, int ntypes, gmx_device_runtim
     ad->nalloc = -1;
 }
 
-/*! \brief Copies all parameters related to the cut-off from ic to nbp
- */
-static void set_cutoff_parameters(cl_nbparam_t* nbp, const interaction_const_t* ic, const PairlistParams& listParams)
-{
-    nbp->ewald_beta        = ic->ewaldcoeff_q;
-    nbp->sh_ewald          = ic->sh_ewald;
-    nbp->epsfac            = ic->epsfac;
-    nbp->two_k_rf          = 2.0 * ic->k_rf;
-    nbp->c_rf              = ic->c_rf;
-    nbp->rvdw_sq           = ic->rvdw * ic->rvdw;
-    nbp->rcoulomb_sq       = ic->rcoulomb * ic->rcoulomb;
-    nbp->rlistOuter_sq     = listParams.rlistOuter * listParams.rlistOuter;
-    nbp->rlistInner_sq     = listParams.rlistInner * listParams.rlistInner;
-    nbp->useDynamicPruning = listParams.useDynamicPruning;
-
-    nbp->sh_lj_ewald   = ic->sh_lj_ewald;
-    nbp->ewaldcoeff_lj = ic->ewaldcoeff_lj;
-
-    nbp->rvdw_switch      = ic->rvdw_switch;
-    nbp->dispersion_shift = ic->dispersion_shift;
-    nbp->repulsion_shift  = ic->repulsion_shift;
-    nbp->vdw_switch       = ic->vdw_switch;
-}
-
 /*! \brief Returns the kinds of electrostatics and Vdw OpenCL
  *  kernels that will be used.
  *
@@ -242,17 +142,17 @@ static void map_interaction_types_to_gpu_kernel_flavors(const interaction_const_
             case eintmodPOTSHIFT:
                 switch (combRule)
                 {
-                    case ljcrNONE: *gpu_vdwtype = evdwOclCUT; break;
-                    case ljcrGEOM: *gpu_vdwtype = evdwOclCUTCOMBGEOM; break;
-                    case ljcrLB: *gpu_vdwtype = evdwOclCUTCOMBLB; break;
+                    case ljcrNONE: *gpu_vdwtype = evdwTypeCUT; break;
+                    case ljcrGEOM: *gpu_vdwtype = evdwTypeCUTCOMBGEOM; break;
+                    case ljcrLB: *gpu_vdwtype = evdwTypeCUTCOMBLB; break;
                     default:
                         gmx_incons(
                                 "The requested LJ combination rule is not implemented in the "
                                 "OpenCL GPU accelerated kernels!");
                 }
                 break;
-            case eintmodFORCESWITCH: *gpu_vdwtype = evdwOclFSWITCH; break;
-            case eintmodPOTSWITCH: *gpu_vdwtype = evdwOclPSWITCH; break;
+            case eintmodFORCESWITCH: *gpu_vdwtype = evdwTypeFSWITCH; break;
+            case eintmodPOTSWITCH: *gpu_vdwtype = evdwTypePSWITCH; break;
             default:
                 gmx_incons(
                         "The requested VdW interaction modifier is not implemented in the GPU "
@@ -263,11 +163,11 @@ static void map_interaction_types_to_gpu_kernel_flavors(const interaction_const_
     {
         if (ic->ljpme_comb_rule == ljcrGEOM)
         {
-            *gpu_vdwtype = evdwOclEWALDGEOM;
+            *gpu_vdwtype = evdwTypeEWALDGEOM;
         }
         else
         {
-            *gpu_vdwtype = evdwOclEWALDLB;
+            *gpu_vdwtype = evdwTypeEWALDLB;
         }
     }
     else
@@ -277,11 +177,11 @@ static void map_interaction_types_to_gpu_kernel_flavors(const interaction_const_
 
     if (ic->eeltype == eelCUT)
     {
-        *gpu_eeltype = eelOclCUT;
+        *gpu_eeltype = eelTypeCUT;
     }
     else if (EEL_RF(ic->eeltype))
     {
-        *gpu_eeltype = eelOclRF;
+        *gpu_eeltype = eelTypeRF;
     }
     else if ((EEL_PME(ic->eeltype) || ic->eeltype == eelEWALD))
     {
@@ -298,14 +198,12 @@ static void map_interaction_types_to_gpu_kernel_flavors(const interaction_const_
 
 /*! \brief Initializes the nonbonded parameter data structure.
  */
-static void init_nbparam(cl_nbparam_t*                    nbp,
-                         const interaction_const_t*       ic,
-                         const PairlistParams&            listParams,
-                         const nbnxn_atomdata_t::Params&  nbatParams,
-                         const gmx_device_runtime_data_t* runData)
+static void init_nbparam(NBParamGpu*                     nbp,
+                         const interaction_const_t*      ic,
+                         const PairlistParams&           listParams,
+                         const nbnxn_atomdata_t::Params& nbatParams,
+                         const DeviceContext&            deviceContext)
 {
-    cl_int cl_error;
-
     set_cutoff_parameters(nbp, ic, listParams);
 
     map_interaction_types_to_gpu_kernel_flavors(ic, nbatParams.comb_rule, &(nbp->eeltype), &(nbp->vdwtype));
@@ -322,215 +220,37 @@ static void init_nbparam(cl_nbparam_t*                    nbp,
         }
     }
     /* generate table for PME */
-    nbp->coulomb_tab_climg2d = nullptr;
-    if (nbp->eeltype == eelOclEWALD_TAB || nbp->eeltype == eelOclEWALD_TAB_TWIN)
+    nbp->coulomb_tab = nullptr;
+    if (nbp->eeltype == eelTypeEWALD_TAB || nbp->eeltype == eelTypeEWALD_TAB_TWIN)
     {
         GMX_RELEASE_ASSERT(ic->coulombEwaldTables, "Need valid Coulomb Ewald correction tables");
-        init_ewald_coulomb_force_table(*ic->coulombEwaldTables, nbp, runData);
+        init_ewald_coulomb_force_table(*ic->coulombEwaldTables, nbp, deviceContext);
     }
     else
-    // TODO: improvement needed.
-    // The image2d is created here even if eeltype is not eelCuEWALD_TAB or eelCuEWALD_TAB_TWIN
-    // because the OpenCL kernels don't accept nullptr values for image2D parameters.
     {
-        /* Switched from using textures to using buffers */
-        // TODO: decide which alternative is most efficient - textures or buffers.
-        /*
-           cl_image_format array_format;
-
-           array_format.image_channel_data_type = CL_FLOAT;
-           array_format.image_channel_order     = CL_R;
-
-           nbp->coulomb_tab_climg2d = clCreateImage2D(runData->context, CL_MEM_READ_WRITE,
-            &array_format, 1, 1, 0, nullptr, &cl_error);
-         */
-
-        nbp->coulomb_tab_climg2d = clCreateBuffer(runData->context, CL_MEM_READ_ONLY,
-                                                  sizeof(cl_float), nullptr, &cl_error);
-        GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                           ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
+        allocateDeviceBuffer(&nbp->coulomb_tab, 1, deviceContext);
     }
 
     const int nnbfp      = 2 * nbatParams.numTypes * nbatParams.numTypes;
     const int nnbfp_comb = 2 * nbatParams.numTypes;
 
     {
-        /* Switched from using textures to using buffers */
-        // TODO: decide which alternative is most efficient - textures or buffers.
-        /*
-           cl_image_format array_format;
-
-           array_format.image_channel_data_type = CL_FLOAT;
-           array_format.image_channel_order     = CL_R;
-
-           nbp->nbfp_climg2d = clCreateImage2D(runData->context, CL_MEM_READ_ONLY |
-           CL_MEM_COPY_HOST_PTR, &array_format, nnbfp, 1, 0, nbat->nbfp, &cl_error);
-         */
-
-        nbp->nbfp_climg2d = clCreateBuffer(
-                runData->context, CL_MEM_READ_ONLY | CL_MEM_HOST_WRITE_ONLY | CL_MEM_COPY_HOST_PTR,
-                nnbfp * sizeof(cl_float), const_cast<float*>(nbatParams.nbfp.data()), &cl_error);
-        GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                           ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
+        /* set up LJ parameter lookup table */
+        DeviceBuffer<real> nbfp;
+        initParamLookupTable(&nbfp, nullptr, nbatParams.nbfp.data(), nnbfp, deviceContext);
+        nbp->nbfp = nbfp;
 
         if (ic->vdwtype == evdwPME)
         {
-            /* Switched from using textures to using buffers */
-            // TODO: decide which alternative is most efficient - textures or buffers.
-            /*  nbp->nbfp_comb_climg2d = clCreateImage2D(runData->context, CL_MEM_READ_WRITE |
-               CL_MEM_COPY_HOST_PTR, &array_format, nnbfp_comb, 1, 0, nbat->nbfp_comb, &cl_error);*/
-            nbp->nbfp_comb_climg2d = clCreateBuffer(
-                    runData->context, CL_MEM_READ_ONLY | CL_MEM_HOST_WRITE_ONLY | CL_MEM_COPY_HOST_PTR,
-                    nnbfp_comb * sizeof(cl_float), const_cast<float*>(nbatParams.nbfp_comb.data()),
-                    &cl_error);
-            GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                               ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
-        }
-        else
-        {
-            // TODO: improvement needed.
-            // The image2d is created here even if vdwtype is not evdwPME because the OpenCL kernels
-            // don't accept nullptr values for image2D parameters.
-            /* Switched from using textures to using buffers */
-            // TODO: decide which alternative is most efficient - textures or buffers.
-            /* nbp->nbfp_comb_climg2d = clCreateImage2D(runData->context, CL_MEM_READ_WRITE,
-                &array_format, 1, 1, 0, nullptr, &cl_error);*/
-            nbp->nbfp_comb_climg2d = clCreateBuffer(runData->context, CL_MEM_READ_ONLY,
-                                                    sizeof(cl_float), nullptr, &cl_error);
-            GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                               ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
-        }
-    }
-}
-
-//! This function is documented in the header file
-void gpu_pme_loadbal_update_param(const nonbonded_verlet_t* nbv, const interaction_const_t* ic)
-{
-    if (!nbv || !nbv->useGpu())
-    {
-        return;
-    }
-    gmx_nbnxn_ocl_t* nb  = nbv->gpu_nbv;
-    cl_nbparam_t*    nbp = nb->nbparam;
-
-    set_cutoff_parameters(nbp, ic, nbv->pairlistSets().params());
-
-    nbp->eeltype = nbnxn_gpu_pick_ewald_kernel_type(*ic);
-
-    GMX_RELEASE_ASSERT(ic->coulombEwaldTables, "Need valid Coulomb Ewald correction tables");
-    init_ewald_coulomb_force_table(*ic->coulombEwaldTables, nbp, nb->dev_rundata);
-}
-
-/*! \brief Initializes the pair list data structure.
- */
-static void init_plist(cl_plist_t* pl)
-{
-    /* initialize to nullptr pointers to data that is not allocated here and will
-       need reallocation in nbnxn_gpu_init_pairlist */
-    pl->sci   = nullptr;
-    pl->cj4   = nullptr;
-    pl->imask = nullptr;
-    pl->excl  = nullptr;
-
-    /* size -1 indicates that the respective array hasn't been initialized yet */
-    pl->na_c          = -1;
-    pl->nsci          = -1;
-    pl->sci_nalloc    = -1;
-    pl->ncj4          = -1;
-    pl->cj4_nalloc    = -1;
-    pl->nimask        = -1;
-    pl->imask_nalloc  = -1;
-    pl->nexcl         = -1;
-    pl->excl_nalloc   = -1;
-    pl->haveFreshList = false;
-}
-
-/*! \brief Initializes the timings data structure.
- */
-static void init_timings(gmx_wallclock_gpu_nbnxn_t* t)
-{
-    int i, j;
-
-    t->nb_h2d_t = 0.0;
-    t->nb_d2h_t = 0.0;
-    t->nb_c     = 0;
-    t->pl_h2d_t = 0.0;
-    t->pl_h2d_c = 0;
-    for (i = 0; i < 2; i++)
-    {
-        for (j = 0; j < 2; j++)
-        {
-            t->ktime[i][j].t = 0.0;
-            t->ktime[i][j].c = 0;
+            DeviceBuffer<float> nbfp_comb;
+            initParamLookupTable(&nbfp_comb, nullptr, nbatParams.nbfp_comb.data(), nnbfp_comb, deviceContext);
+            nbp->nbfp_comb = nbfp_comb;
         }
     }
-
-    t->pruneTime.c        = 0;
-    t->pruneTime.t        = 0.0;
-    t->dynamicPruneTime.c = 0;
-    t->dynamicPruneTime.t = 0.0;
-}
-
-
-//! OpenCL notification callback function
-static void CL_CALLBACK ocl_notify_fn(const char* pErrInfo,
-                                      const void gmx_unused* private_info,
-                                      size_t gmx_unused cb,
-                                      void gmx_unused* user_data)
-{
-    if (pErrInfo != nullptr)
-    {
-        printf("%s\n", pErrInfo); // Print error/hint
-    }
-}
-
-/*! \brief Creates context for OpenCL GPU given by \p mygpu
- *
- * A fatal error results if creation fails.
- *
- * \param[inout] runtimeData runtime data including program and context
- * \param[in]    devInfo     device info struct
- * \param[in]    rank        MPI rank (for error reporting)
- */
-static void nbnxn_gpu_create_context(gmx_device_runtime_data_t* runtimeData,
-                                     const gmx_device_info_t*   devInfo,
-                                     int                        rank)
-{
-    cl_context_properties context_properties[5];
-    cl_platform_id        platform_id;
-    cl_device_id          device_id;
-    cl_context            context;
-    cl_int                cl_error;
-
-    assert(runtimeData != nullptr);
-    assert(devInfo != nullptr);
-
-    platform_id = devInfo->ocl_gpu_id.ocl_platform_id;
-    device_id   = devInfo->ocl_gpu_id.ocl_device_id;
-
-    int i                   = 0;
-    context_properties[i++] = CL_CONTEXT_PLATFORM;
-    context_properties[i++] = reinterpret_cast<cl_context_properties>(platform_id);
-    if (getenv("GMX_OCL_SHOW_DIAGNOSTICS"))
-    {
-        context_properties[i++] = CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL;
-        context_properties[i++] =
-                CL_CONTEXT_DIAGNOSTICS_LEVEL_BAD_INTEL | CL_CONTEXT_DIAGNOSTICS_LEVEL_NEUTRAL_INTEL;
-    }
-    context_properties[i++] = 0; /* Terminates the list of properties */
-
-    context = clCreateContext(context_properties, 1, &device_id, ocl_notify_fn, nullptr, &cl_error);
-    if (CL_SUCCESS != cl_error)
-    {
-        gmx_fatal(FARGS, "On rank %d failed to create context for GPU #%s:\n OpenCL error %d: %s",
-                  rank, devInfo->device_name, cl_error, ocl_get_error_string(cl_error).c_str());
-    }
-
-    runtimeData->context = context;
 }
 
 /*! \brief Initializes the OpenCL kernel pointers of the nbnxn_ocl_ptr_t input data structure. */
-static cl_kernel nbnxn_gpu_create_kernel(gmx_nbnxn_ocl_t* nb, const char* kernel_name)
+static cl_kernel nbnxn_gpu_create_kernel(NbnxmGpu* nb, const char* kernel_name)
 {
     cl_kernel kernel;
     cl_int    cl_error;
@@ -539,7 +259,7 @@ static cl_kernel nbnxn_gpu_create_kernel(gmx_nbnxn_ocl_t* nb, const char* kernel
     if (CL_SUCCESS != cl_error)
     {
         gmx_fatal(FARGS, "Failed to create kernel '%s' for GPU #%s: OpenCL error %d", kernel_name,
-                  nb->dev_info->device_name, cl_error);
+                  nb->deviceContext_->deviceInfo().device_name, cl_error);
     }
 
     return kernel;
@@ -547,12 +267,12 @@ static cl_kernel nbnxn_gpu_create_kernel(gmx_nbnxn_ocl_t* nb, const char* kernel
 
 /*! \brief Clears nonbonded shift force output array and energy outputs on the GPU.
  */
-static void nbnxn_ocl_clear_e_fshift(gmx_nbnxn_ocl_t* nb)
+static void nbnxn_ocl_clear_e_fshift(NbnxmGpu* nb)
 {
 
     cl_int           cl_error;
     cl_atomdata_t*   adat = nb->atdat;
-    cl_command_queue ls   = nb->stream[InteractionLocality::Local];
+    cl_command_queue ls   = nb->deviceStreams[InteractionLocality::Local]->stream();
 
     size_t local_work_size[3]  = { 1, 1, 1 };
     size_t global_work_size[3] = { 1, 1, 1 };
@@ -580,7 +300,7 @@ static void nbnxn_ocl_clear_e_fshift(gmx_nbnxn_ocl_t* nb)
 }
 
 /*! \brief Initializes the OpenCL kernel pointers of the nbnxn_ocl_ptr_t input data structure. */
-static void nbnxn_gpu_init_kernels(gmx_nbnxn_ocl_t* nb)
+static void nbnxn_gpu_init_kernels(NbnxmGpu* nb)
 {
     /* Init to 0 main kernel arrays */
     /* They will be later on initialized in select_nbnxn_kernel */
@@ -609,31 +329,29 @@ static void nbnxn_gpu_init_kernels(gmx_nbnxn_ocl_t* nb)
  *  Initializes members of the atomdata and nbparam structs and
  *  clears e/fshift output buffers.
  */
-static void nbnxn_ocl_init_const(gmx_nbnxn_ocl_t*                nb,
+static void nbnxn_ocl_init_const(cl_atomdata_t*                  atomData,
+                                 NBParamGpu*                     nbParams,
                                  const interaction_const_t*      ic,
                                  const PairlistParams&           listParams,
-                                 const nbnxn_atomdata_t::Params& nbatParams)
+                                 const nbnxn_atomdata_t::Params& nbatParams,
+                                 const DeviceContext&            deviceContext)
 {
-    init_atomdata_first(nb->atdat, nbatParams.numTypes, nb->dev_rundata);
-    init_nbparam(nb->nbparam, ic, listParams, nbatParams, nb->dev_rundata);
+    init_atomdata_first(atomData, nbatParams.numTypes, deviceContext);
+    init_nbparam(nbParams, ic, listParams, nbatParams, deviceContext);
 }
 
 
 //! This function is documented in the header file
-gmx_nbnxn_ocl_t* gpu_init(const gmx_device_info_t*   deviceInfo,
-                          const interaction_const_t* ic,
-                          const PairlistParams&      listParams,
-                          const nbnxn_atomdata_t*    nbat,
-                          const int                  rank,
-                          const gmx_bool             bLocalAndNonlocal)
+NbnxmGpu* gpu_init(const gmx::DeviceStreamManager& deviceStreamManager,
+                   const interaction_const_t*      ic,
+                   const PairlistParams&           listParams,
+                   const nbnxn_atomdata_t*         nbat,
+                   const bool                      bLocalAndNonlocal)
 {
-    gmx_nbnxn_ocl_t*            nb;
-    cl_int                      cl_error;
-    cl_command_queue_properties queue_properties;
-
-    assert(ic);
+    GMX_ASSERT(ic, "Need a valid interaction constants object");
 
-    snew(nb, 1);
+    auto nb            = new NbnxmGpu();
+    nb->deviceContext_ = &deviceStreamManager.context();
     snew(nb->atdat, 1);
     snew(nb->nbparam, 1);
     snew(nb->plist[InteractionLocality::Local], 1);
@@ -642,14 +360,13 @@ gmx_nbnxn_ocl_t* gpu_init(const gmx_device_info_t*   deviceInfo,
         snew(nb->plist[InteractionLocality::NonLocal], 1);
     }
 
-    nb->bUseTwoStreams = static_cast<cl_bool>(bLocalAndNonlocal);
+    nb->bUseTwoStreams = bLocalAndNonlocal;
 
     nb->timers = new cl_timers_t();
     snew(nb->timings, 1);
 
     /* set device info, just point it to the right GPU among the detected ones */
-    nb->dev_info = deviceInfo;
-    snew(nb->dev_rundata, 1);
+    nb->dev_rundata = new gmx_device_runtime_data_t();
 
     /* init nbst */
     pmalloc(reinterpret_cast<void**>(&nb->nbst.e_lj), sizeof(*nb->nbst.e_lj));
@@ -659,41 +376,23 @@ gmx_nbnxn_ocl_t* gpu_init(const gmx_device_info_t*   deviceInfo,
     init_plist(nb->plist[InteractionLocality::Local]);
 
     /* OpenCL timing disabled if GMX_DISABLE_GPU_TIMING is defined. */
-    nb->bDoTime = static_cast<cl_bool>(getenv("GMX_DISABLE_GPU_TIMING") == nullptr);
-
-    /* Create queues only after bDoTime has been initialized */
-    if (nb->bDoTime)
-    {
-        queue_properties = CL_QUEUE_PROFILING_ENABLE;
-    }
-    else
-    {
-        queue_properties = 0;
-    }
-
-    nbnxn_gpu_create_context(nb->dev_rundata, nb->dev_info, rank);
+    nb->bDoTime = (getenv("GMX_DISABLE_GPU_TIMING") == nullptr);
 
     /* local/non-local GPU streams */
-    nb->stream[InteractionLocality::Local] = clCreateCommandQueue(
-            nb->dev_rundata->context, nb->dev_info->ocl_gpu_id.ocl_device_id, queue_properties, &cl_error);
-    if (CL_SUCCESS != cl_error)
-    {
-        gmx_fatal(FARGS, "On rank %d failed to create context for GPU #%s: OpenCL error %d", rank,
-                  nb->dev_info->device_name, cl_error);
-    }
+    GMX_RELEASE_ASSERT(deviceStreamManager.streamIsValid(gmx::DeviceStreamType::NonBondedLocal),
+                       "Local non-bonded stream should be initialized to use GPU for non-bonded.");
+    nb->deviceStreams[InteractionLocality::Local] =
+            &deviceStreamManager.stream(gmx::DeviceStreamType::NonBondedLocal);
 
     if (nb->bUseTwoStreams)
     {
         init_plist(nb->plist[InteractionLocality::NonLocal]);
 
-        nb->stream[InteractionLocality::NonLocal] =
-                clCreateCommandQueue(nb->dev_rundata->context, nb->dev_info->ocl_gpu_id.ocl_device_id,
-                                     queue_properties, &cl_error);
-        if (CL_SUCCESS != cl_error)
-        {
-            gmx_fatal(FARGS, "On rank %d failed to create context for GPU #%s: OpenCL error %d",
-                      rank, nb->dev_info->device_name, cl_error);
-        }
+        GMX_RELEASE_ASSERT(deviceStreamManager.streamIsValid(gmx::DeviceStreamType::NonBondedNonLocal),
+                           "Non-local non-bonded stream should be initialized to use GPU for "
+                           "non-bonded with domain decomposition.");
+        nb->deviceStreams[InteractionLocality::NonLocal] =
+                &deviceStreamManager.stream(gmx::DeviceStreamType::NonBondedNonLocal);
     }
 
     if (nb->bDoTime)
@@ -701,14 +400,14 @@ gmx_nbnxn_ocl_t* gpu_init(const gmx_device_info_t*   deviceInfo,
         init_timings(nb->timings);
     }
 
-    nbnxn_ocl_init_const(nb, ic, listParams, nbat->params());
+    nbnxn_ocl_init_const(nb->atdat, nb->nbparam, ic, listParams, nbat->params(), *nb->deviceContext_);
 
     /* Enable LJ param manual prefetch for AMD or Intel or if we request through env. var.
      * TODO: decide about NVIDIA
      */
     nb->bPrefetchLjParam = (getenv("GMX_OCL_DISABLE_I_PREFETCH") == nullptr)
-                           && ((nb->dev_info->vendor_e == OCL_VENDOR_AMD)
-                               || (nb->dev_info->vendor_e == OCL_VENDOR_INTEL)
+                           && ((nb->deviceContext_->deviceInfo().deviceVendor == DeviceVendor::Amd)
+                               || (nb->deviceContext_->deviceInfo().deviceVendor == DeviceVendor::Intel)
                                || (getenv("GMX_OCL_ENABLE_I_PREFETCH") != nullptr));
 
     /* NOTE: in CUDA we pick L1 cache configuration for the nbnxn kernels here,
@@ -731,27 +430,21 @@ gmx_nbnxn_ocl_t* gpu_init(const gmx_device_info_t*   deviceInfo,
 
 /*! \brief Clears the first natoms_clear elements of the GPU nonbonded force output array.
  */
-static void nbnxn_ocl_clear_f(gmx_nbnxn_ocl_t* nb, int natoms_clear)
+static void nbnxn_ocl_clear_f(NbnxmGpu* nb, int natoms_clear)
 {
     if (natoms_clear == 0)
     {
         return;
     }
 
-    cl_int gmx_used_in_debug cl_error;
+    cl_atomdata_t*      atomData    = nb->atdat;
+    const DeviceStream& localStream = *nb->deviceStreams[InteractionLocality::Local];
 
-    cl_atomdata_t*   atomData = nb->atdat;
-    cl_command_queue ls       = nb->stream[InteractionLocality::Local];
-    cl_float         value    = 0.0F;
-
-    cl_error = clEnqueueFillBuffer(ls, atomData->f, &value, sizeof(cl_float), 0,
-                                   natoms_clear * sizeof(rvec), 0, nullptr, nullptr);
-    GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                       ("clEnqueueFillBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
+    clearDeviceBufferAsync(&atomData->f, 0, natoms_clear * DIM, localStream);
 }
 
 //! This function is documented in the header file
-void gpu_clear_outputs(gmx_nbnxn_ocl_t* nb, bool computeVirial)
+void gpu_clear_outputs(NbnxmGpu* nb, bool computeVirial)
 {
     nbnxn_ocl_clear_f(nb, nb->atdat->natoms);
     /* clear shift force array and energies if the outputs were
@@ -763,96 +456,38 @@ void gpu_clear_outputs(gmx_nbnxn_ocl_t* nb, bool computeVirial)
 
     /* kick off buffer clearing kernel to ensure concurrency with constraints/update */
     cl_int gmx_unused cl_error;
-    cl_error = clFlush(nb->stream[InteractionLocality::Local]);
-    assert(CL_SUCCESS == cl_error);
-}
-
-//! This function is documented in the header file
-void gpu_init_pairlist(gmx_nbnxn_ocl_t* nb, const NbnxnPairlistGpu* h_plist, const InteractionLocality iloc)
-{
-    char sbuf[STRLEN];
-    // Timing accumulation should happen only if there was work to do
-    // because getLastRangeTime() gets skipped with empty lists later
-    // which leads to the counter not being reset.
-    bool             bDoTime = ((nb->bDoTime == CL_TRUE) && !h_plist->sci.empty());
-    cl_command_queue stream  = nb->stream[iloc];
-    cl_plist_t*      d_plist = nb->plist[iloc];
-
-    if (d_plist->na_c < 0)
-    {
-        d_plist->na_c = h_plist->na_ci;
-    }
-    else
-    {
-        if (d_plist->na_c != h_plist->na_ci)
-        {
-            sprintf(sbuf, "In cu_init_plist: the #atoms per cell has changed (from %d to %d)",
-                    d_plist->na_c, h_plist->na_ci);
-            gmx_incons(sbuf);
-        }
-    }
-
-    gpu_timers_t::Interaction& iTimers = nb->timers->interaction[iloc];
-
-    if (bDoTime)
-    {
-        iTimers.pl_h2d.openTimingRegion(stream);
-        iTimers.didPairlistH2D = true;
-    }
-
-    // TODO most of this function is same in CUDA and OpenCL, move into the header
-    DeviceContext context = nb->dev_rundata->context;
-
-    reallocateDeviceBuffer(&d_plist->sci, h_plist->sci.size(), &d_plist->nsci, &d_plist->sci_nalloc, context);
-    copyToDeviceBuffer(&d_plist->sci, h_plist->sci.data(), 0, h_plist->sci.size(), stream,
-                       GpuApiCallBehavior::Async, bDoTime ? iTimers.pl_h2d.fetchNextEvent() : nullptr);
-
-    reallocateDeviceBuffer(&d_plist->cj4, h_plist->cj4.size(), &d_plist->ncj4, &d_plist->cj4_nalloc, context);
-    copyToDeviceBuffer(&d_plist->cj4, h_plist->cj4.data(), 0, h_plist->cj4.size(), stream,
-                       GpuApiCallBehavior::Async, bDoTime ? iTimers.pl_h2d.fetchNextEvent() : nullptr);
-
-    reallocateDeviceBuffer(&d_plist->imask, h_plist->cj4.size() * c_nbnxnGpuClusterpairSplit,
-                           &d_plist->nimask, &d_plist->imask_nalloc, context);
-
-    reallocateDeviceBuffer(&d_plist->excl, h_plist->excl.size(), &d_plist->nexcl,
-                           &d_plist->excl_nalloc, context);
-    copyToDeviceBuffer(&d_plist->excl, h_plist->excl.data(), 0, h_plist->excl.size(), stream,
-                       GpuApiCallBehavior::Async, bDoTime ? iTimers.pl_h2d.fetchNextEvent() : nullptr);
-
-    if (bDoTime)
-    {
-        iTimers.pl_h2d.closeTimingRegion(stream);
-    }
-
-    /* need to prune the pair list during the next step */
-    d_plist->haveFreshList = true;
+    cl_error = clFlush(nb->deviceStreams[InteractionLocality::Local]->stream());
+    GMX_ASSERT(cl_error == CL_SUCCESS, ("clFlush failed: " + ocl_get_error_string(cl_error)).c_str());
 }
 
 //! This function is documented in the header file
-void gpu_upload_shiftvec(gmx_nbnxn_ocl_t* nb, const nbnxn_atomdata_t* nbatom)
+void gpu_upload_shiftvec(NbnxmGpu* nb, const nbnxn_atomdata_t* nbatom)
 {
-    cl_atomdata_t*   adat = nb->atdat;
-    cl_command_queue ls   = nb->stream[InteractionLocality::Local];
+    cl_atomdata_t*      adat         = nb->atdat;
+    const DeviceStream& deviceStream = *nb->deviceStreams[InteractionLocality::Local];
 
     /* only if we have a dynamic box */
     if (nbatom->bDynamicBox || !adat->bShiftVecUploaded)
     {
-        ocl_copy_H2D_async(adat->shift_vec, nbatom->shift_vec.data(), 0,
-                           SHIFTS * adat->shift_vec_elem_size, ls, nullptr);
+        GMX_ASSERT(sizeof(float) * DIM == sizeof(*nbatom->shift_vec.data()),
+                   "Sizes of host- and device-side shift vectors should be the same.");
+        copyToDeviceBuffer(&adat->shift_vec, reinterpret_cast<const float*>(nbatom->shift_vec.data()),
+                           0, SHIFTS * DIM, deviceStream, GpuApiCallBehavior::Async, nullptr);
         adat->bShiftVecUploaded = CL_TRUE;
     }
 }
 
 //! This function is documented in the header file
-void gpu_init_atomdata(gmx_nbnxn_ocl_t* nb, const nbnxn_atomdata_t* nbat)
+void gpu_init_atomdata(NbnxmGpu* nb, const nbnxn_atomdata_t* nbat)
 {
-    cl_int           cl_error;
-    int              nalloc, natoms;
-    bool             realloced;
-    bool             bDoTime = nb->bDoTime == CL_TRUE;
-    cl_timers_t*     timers  = nb->timers;
-    cl_atomdata_t*   d_atdat = nb->atdat;
-    cl_command_queue ls      = nb->stream[InteractionLocality::Local];
+    cl_int               cl_error;
+    int                  nalloc, natoms;
+    bool                 realloced;
+    bool                 bDoTime       = nb->bDoTime;
+    cl_timers_t*         timers        = nb->timers;
+    cl_atomdata_t*       d_atdat       = nb->atdat;
+    const DeviceContext& deviceContext = *nb->deviceContext_;
+    const DeviceStream&  deviceStream  = *nb->deviceStreams[InteractionLocality::Local];
 
     natoms    = nbat->numAtoms();
     realloced = false;
@@ -860,7 +495,7 @@ void gpu_init_atomdata(gmx_nbnxn_ocl_t* nb, const nbnxn_atomdata_t* nbat)
     if (bDoTime)
     {
         /* time async copy */
-        timers->atdat.openTimingRegion(ls);
+        timers->atdat.openTimingRegion(deviceStream);
     }
 
     /* need to reallocate if we have to copy more atoms than the amount of space
@@ -878,33 +513,18 @@ void gpu_init_atomdata(gmx_nbnxn_ocl_t* nb, const nbnxn_atomdata_t* nbat)
             freeDeviceBuffer(&d_atdat->atom_types);
         }
 
-        d_atdat->f_elem_size = sizeof(rvec);
 
-        d_atdat->f = clCreateBuffer(nb->dev_rundata->context, CL_MEM_READ_WRITE | CL_MEM_HOST_READ_ONLY,
-                                    nalloc * d_atdat->f_elem_size, nullptr, &cl_error);
-        GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                           ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
-
-        d_atdat->xq = clCreateBuffer(nb->dev_rundata->context, CL_MEM_READ_ONLY | CL_MEM_HOST_WRITE_ONLY,
-                                     nalloc * sizeof(cl_float4), nullptr, &cl_error);
-        GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                           ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
+        allocateDeviceBuffer(&d_atdat->f, nalloc * DIM, deviceContext);
+        allocateDeviceBuffer(&d_atdat->xq, nalloc * (DIM + 1), deviceContext);
 
         if (useLjCombRule(nb->nbparam->vdwtype))
         {
-            d_atdat->lj_comb = clCreateBuffer(nb->dev_rundata->context,
-                                              CL_MEM_READ_ONLY | CL_MEM_HOST_WRITE_ONLY,
-                                              nalloc * sizeof(cl_float2), nullptr, &cl_error);
-            GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                               ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
+            // Two Lennard-Jones parameters per atom
+            allocateDeviceBuffer(&d_atdat->lj_comb, nalloc * 2, deviceContext);
         }
         else
         {
-            d_atdat->atom_types = clCreateBuffer(nb->dev_rundata->context,
-                                                 CL_MEM_READ_ONLY | CL_MEM_HOST_WRITE_ONLY,
-                                                 nalloc * sizeof(int), nullptr, &cl_error);
-            GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                               ("clCreateBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
+            allocateDeviceBuffer(&d_atdat->atom_types, nalloc, deviceContext);
         }
 
         d_atdat->nalloc = nalloc;
@@ -922,22 +542,27 @@ void gpu_init_atomdata(gmx_nbnxn_ocl_t* nb, const nbnxn_atomdata_t* nbat)
 
     if (useLjCombRule(nb->nbparam->vdwtype))
     {
-        ocl_copy_H2D_async(d_atdat->lj_comb, nbat->params().lj_comb.data(), 0, natoms * sizeof(cl_float2),
-                           ls, bDoTime ? timers->atdat.fetchNextEvent() : nullptr);
+        GMX_ASSERT(sizeof(float) == sizeof(*nbat->params().lj_comb.data()),
+                   "Size of the LJ parameters element should be equal to the size of float2.");
+        copyToDeviceBuffer(&d_atdat->lj_comb, nbat->params().lj_comb.data(), 0, 2 * natoms,
+                           deviceStream, GpuApiCallBehavior::Async,
+                           bDoTime ? timers->atdat.fetchNextEvent() : nullptr);
     }
     else
     {
-        ocl_copy_H2D_async(d_atdat->atom_types, nbat->params().type.data(), 0, natoms * sizeof(int),
-                           ls, bDoTime ? timers->atdat.fetchNextEvent() : nullptr);
+        GMX_ASSERT(sizeof(int) == sizeof(*nbat->params().type.data()),
+                   "Sizes of host- and device-side atom types should be the same.");
+        copyToDeviceBuffer(&d_atdat->atom_types, nbat->params().type.data(), 0, natoms, deviceStream,
+                           GpuApiCallBehavior::Async, bDoTime ? timers->atdat.fetchNextEvent() : nullptr);
     }
 
     if (bDoTime)
     {
-        timers->atdat.closeTimingRegion(ls);
+        timers->atdat.closeTimingRegion(deviceStream);
     }
 
     /* kick off the tasks enqueued above to ensure concurrency with the search */
-    cl_error = clFlush(ls);
+    cl_error = clFlush(deviceStream.stream());
     GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
                        ("clFlush failed: " + ocl_get_error_string(cl_error)).c_str());
 }
@@ -947,7 +572,7 @@ static void free_kernel(cl_kernel* kernel_ptr)
 {
     cl_int gmx_unused cl_error;
 
-    assert(nullptr != kernel_ptr);
+    GMX_ASSERT(kernel_ptr, "Need a valid kernel pointer");
 
     if (*kernel_ptr)
     {
@@ -970,41 +595,26 @@ static void free_kernels(cl_kernel* kernels, int count)
     }
 }
 
-/*! \brief Free the OpenCL runtime data (context and program).
+/*! \brief Free the OpenCL program.
  *
- *  The function releases the OpenCL context and program assuciated with the
+ *  The function releases the OpenCL program assuciated with the
  *  device that the calling PP rank is running on.
  *
- *  \param runData [in]  porinter to the structure with runtime data.
+ *  \param program [in]  OpenCL program to release.
  */
-static void free_gpu_device_runtime_data(gmx_device_runtime_data_t* runData)
+static void freeGpuProgram(cl_program program)
 {
-    if (runData == nullptr)
-    {
-        return;
-    }
-
-    cl_int gmx_unused cl_error;
-
-    if (runData->context)
+    if (program)
     {
-        cl_error = clReleaseContext(runData->context);
-        GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
-                           ("clReleaseContext failed: " + ocl_get_error_string(cl_error)).c_str());
-        runData->context = nullptr;
-    }
-
-    if (runData->program)
-    {
-        cl_error = clReleaseProgram(runData->program);
+        cl_int cl_error = clReleaseProgram(program);
         GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
                            ("clReleaseProgram failed: " + ocl_get_error_string(cl_error)).c_str());
-        runData->program = nullptr;
+        program = nullptr;
     }
 }
 
 //! This function is documented in the header file
-void gpu_free(gmx_nbnxn_ocl_t* nb)
+void gpu_free(NbnxmGpu* nb)
 {
     if (nb == nullptr)
     {
@@ -1038,9 +648,9 @@ void gpu_free(gmx_nbnxn_ocl_t* nb)
     sfree(nb->atdat);
 
     /* Free nbparam */
-    freeDeviceBuffer(&(nb->nbparam->nbfp_climg2d));
-    freeDeviceBuffer(&(nb->nbparam->nbfp_comb_climg2d));
-    freeDeviceBuffer(&(nb->nbparam->coulomb_tab_climg2d));
+    freeDeviceBuffer(&(nb->nbparam->nbfp));
+    freeDeviceBuffer(&(nb->nbparam->nbfp_comb));
+    freeDeviceBuffer(&(nb->nbparam->coulomb_tab));
     sfree(nb->nbparam);
 
     /* Free plist */
@@ -1070,14 +680,6 @@ void gpu_free(gmx_nbnxn_ocl_t* nb)
     pfree(nb->nbst.fshift);
     nb->nbst.fshift = nullptr;
 
-    /* Free command queues */
-    clReleaseCommandQueue(nb->stream[InteractionLocality::Local]);
-    nb->stream[InteractionLocality::Local] = nullptr;
-    if (nb->bUseTwoStreams)
-    {
-        clReleaseCommandQueue(nb->stream[InteractionLocality::NonLocal]);
-        nb->stream[InteractionLocality::NonLocal] = nullptr;
-    }
     /* Free other events */
     if (nb->nonlocal_done)
     {
@@ -1090,13 +692,13 @@ void gpu_free(gmx_nbnxn_ocl_t* nb)
         nb->misc_ops_and_local_H2D_done = nullptr;
     }
 
-    free_gpu_device_runtime_data(nb->dev_rundata);
-    sfree(nb->dev_rundata);
+    freeGpuProgram(nb->dev_rundata->program);
+    delete nb->dev_rundata;
 
     /* Free timers and timings */
     delete nb->timers;
     sfree(nb->timings);
-    sfree(nb);
+    delete nb;
 
     if (debug)
     {
@@ -1105,30 +707,9 @@ void gpu_free(gmx_nbnxn_ocl_t* nb)
 }
 
 //! This function is documented in the header file
-gmx_wallclock_gpu_nbnxn_t* gpu_get_timings(gmx_nbnxn_ocl_t* nb)
-{
-    return (nb != nullptr && nb->bDoTime) ? nb->timings : nullptr;
-}
-
-//! This function is documented in the header file
-void gpu_reset_timings(nonbonded_verlet_t* nbv)
-{
-    if (nbv->gpu_nbv && nbv->gpu_nbv->bDoTime)
-    {
-        init_timings(nbv->gpu_nbv->timings);
-    }
-}
-
-//! This function is documented in the header file
-int gpu_min_ci_balanced(gmx_nbnxn_ocl_t* nb)
-{
-    return nb != nullptr ? gpu_min_ci_balanced_factor * nb->dev_info->compute_units : 0;
-}
-
-//! This function is documented in the header file
-gmx_bool gpu_is_kernel_ewald_analytical(const gmx_nbnxn_ocl_t* nb)
+int gpu_min_ci_balanced(NbnxmGpu* nb)
 {
-    return ((nb->nbparam->eeltype == eelOclEWALD_ANA) || (nb->nbparam->eeltype == eelOclEWALD_ANA_TWIN));
+    return nb != nullptr ? gpu_min_ci_balanced_factor * nb->deviceContext_->deviceInfo().compute_units : 0;
 }
 
 } // namespace Nbnxm
index 9e45e80d3ca5d92efe738c144df3e9cab798a1e2..40eb905184bd1c5ae2405639f5a288f76b818b78 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -98,12 +99,12 @@ static const char* kernel_VdW_family_definitions[] = {
 
 /*! \brief Returns a string with the compiler defines required to avoid all flavour generation
  *
- * For example if flavour eelOclRF with evdwOclFSWITCH, the output will be such that the corresponding
+ * For example if flavour eelTypeRF with evdwTypeFSWITCH, the output will be such that the corresponding
  * kernel flavour is generated:
  * -DGMX_OCL_FASTGEN          (will replace flavour generator nbnxn_ocl_kernels.clh with nbnxn_ocl_kernels_fastgen.clh)
- * -DEL_RF                    (The eelOclRF flavour)
+ * -DEL_RF                    (The eelTypeRF flavour)
  * -DEELNAME=_ElecRF          (The first part of the generated kernel name )
- * -DLJ_EWALD_COMB_GEOM       (The evdwOclFSWITCH flavour)
+ * -DLJ_EWALD_COMB_GEOM       (The evdwTypeFSWITCH flavour)
  * -DVDWNAME=_VdwLJEwCombGeom (The second part of the generated kernel name )
  *
  * prune/energy are still generated as originally. It is only the flavour-level that has changed, so that
@@ -133,7 +134,7 @@ static std::string makeDefinesForKernelTypes(bool bFastGen, int eeltype, int vdw
 
     if (bFastGen)
     {
-        bool bIsEwaldSingleCutoff = (eeltype == eelOclEWALD_TAB || eeltype == eelOclEWALD_ANA);
+        bool bIsEwaldSingleCutoff = (eeltype == eelTypeEWALD_TAB || eeltype == eelTypeEWALD_ANA);
 
         if (bIsEwaldSingleCutoff)
         {
@@ -162,11 +163,11 @@ static std::string makeDefinesForKernelTypes(bool bFastGen, int eeltype, int vdw
  *
  * A fatal error results if compilation fails.
  *
- * \param[inout] nb  Manages OpenCL non-bonded calculations; compiled kernels returned in dev_info members
+ * \param[inout] nb  Manages OpenCL non-bonded calculations; compiled kernels returned in deviceInfo members
  *
  * Does not throw
  */
-void nbnxn_gpu_compile_kernels(gmx_nbnxn_ocl_t* nb)
+void nbnxn_gpu_compile_kernels(NbnxmGpu* nb)
 {
     gmx_bool   bFastGen = TRUE;
     cl_program program  = nullptr;
@@ -183,29 +184,32 @@ void nbnxn_gpu_compile_kernels(gmx_nbnxn_ocl_t* nb)
         std::string extraDefines =
                 makeDefinesForKernelTypes(bFastGen, nb->nbparam->eeltype, nb->nbparam->vdwtype);
 
-        /* Here we pass macros and static const int variables defined
+        /* Here we pass macros and static const/constexpr int variables defined
          * in include files outside the opencl as macros, to avoid
-         * including those files in the JIT compilation that happens
-         * at runtime. This is particularly a problem for headers that
-         * depend on config.h, such as pairlist.h. */
+         * including those files in the plain-C JIT compilation that happens
+         * at runtime. */
         extraDefines += gmx::formatString(
-                " -DNBNXN_GPU_CLUSTER_SIZE=%d "
+                " -Dc_nbnxnGpuClusterSize=%d"
+                " -Dc_nbnxnMinDistanceSquared=%g"
+                " -Dc_nbnxnGpuNumClusterPerSupercluster=%d"
+                " -Dc_nbnxnGpuJgroupSize=%d"
                 "%s",
-                c_nbnxnGpuClusterSize, /* Defined in nbnxn_pairlist.h */
-                (nb->bPrefetchLjParam) ? "-DIATYPE_SHMEM" : "");
+                c_nbnxnGpuClusterSize, c_nbnxnMinDistanceSquared, c_nbnxnGpuNumClusterPerSupercluster,
+                c_nbnxnGpuJgroupSize, (nb->bPrefetchLjParam) ? " -DIATYPE_SHMEM" : "");
         try
         {
             /* TODO when we have a proper MPI-aware logging module,
                the log output here should be written there */
             program = gmx::ocl::compileProgram(
                     stderr, "gromacs/nbnxm/opencl", "nbnxm_ocl_kernels.cl", extraDefines,
-                    nb->dev_rundata->context, nb->dev_info->ocl_gpu_id.ocl_device_id,
-                    nb->dev_info->vendor_e);
+                    nb->deviceContext_->context(), nb->deviceContext_->deviceInfo().oclDeviceId,
+                    nb->deviceContext_->deviceInfo().deviceVendor);
         }
         catch (gmx::GromacsException& e)
         {
-            e.prependContext(gmx::formatString("Failed to compile NBNXN kernels for GPU #%s\n",
-                                               nb->dev_info->device_name));
+            e.prependContext(gmx::formatString(
+                    "Failed to compile/load nbnxm kernels for GPU #%d %s\n",
+                    nb->deviceContext_->deviceInfo().id, nb->deviceContext_->deviceInfo().device_name));
             throw;
         }
     }
index cbdb45f01a68310725983d1d23682ec367e17e68..b238843a91efe632b73285eca77573db6689c97e 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2012-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -177,10 +177,10 @@ __kernel void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_opencl)
     const int bidx  = get_group_id(0);
     const int widx  = tidx / WARP_SIZE; /* warp index */
 
-    /*! i-cluster interaction mask for a super-cluster with all NCL_PER_SUPERCL=8 bits set */
-    const unsigned superClInteractionMask = ((1U << NCL_PER_SUPERCL) - 1U);
+    /*! i-cluster interaction mask for a super-cluster with all c_nbnxnGpuNumClusterPerSupercluster=8 bits set */
+    const unsigned superClInteractionMask = ((1U << c_nbnxnGpuNumClusterPerSupercluster) - 1U);
 
-#define LOCAL_OFFSET (xqib + NCL_PER_SUPERCL * CL_SIZE)
+#define LOCAL_OFFSET (xqib + c_nbnxnGpuNumClusterPerSupercluster * CL_SIZE)
     CjType cjs = 0;
 #if USE_CJ_PREFETCH
     /* shmem buffer for cj, for both warps separately */
@@ -194,11 +194,11 @@ __kernel void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_opencl)
     /* shmem buffer for i atom-type pre-loading */
     __local int* atib = (__local int*)(LOCAL_OFFSET); //NOLINT(google-readability-casting)
 #        undef LOCAL_OFFSET
-#        define LOCAL_OFFSET (atib + NCL_PER_SUPERCL * CL_SIZE)
+#        define LOCAL_OFFSET (atib + c_nbnxnGpuNumClusterPerSupercluster * CL_SIZE)
 #    else
     __local float2* ljcpib      = (__local float2*)(LOCAL_OFFSET);
 #        undef LOCAL_OFFSET
-#        define LOCAL_OFFSET (ljcpib + NCL_PER_SUPERCL * CL_SIZE)
+#        define LOCAL_OFFSET (ljcpib + c_nbnxnGpuNumClusterPerSupercluster * CL_SIZE)
 #    endif
 #endif
 
@@ -225,10 +225,10 @@ __kernel void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_opencl)
     const int         cij4_start = nb_sci.cj4_ind_start; /* first ...*/
     const int         cij4_end   = nb_sci.cj4_ind_end;   /* and last index of j clusters */
 
-    for (int i = 0; i < NCL_PER_SUPERCL; i += CL_SIZE)
+    for (int i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i += CL_SIZE)
     {
         /* Pre-load i-atom x and q into shared memory */
-        const int ci = sci * NCL_PER_SUPERCL + tidxj + i;
+        const int ci = sci * c_nbnxnGpuNumClusterPerSupercluster + tidxj + i;
         const int ai = ci * CL_SIZE + tidxi;
 
         float4 xqbuf = xq[ai]
@@ -254,8 +254,8 @@ __kernel void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_opencl)
 #endif
     barrier(CLK_LOCAL_MEM_FENCE);
 
-    float3 fci_buf[NCL_PER_SUPERCL]; /* i force buffer */
-    for (int ci_offset = 0; ci_offset < NCL_PER_SUPERCL; ci_offset++)
+    float3 fci_buf[c_nbnxnGpuNumClusterPerSupercluster]; /* i force buffer */
+    for (int ci_offset = 0; ci_offset < c_nbnxnGpuNumClusterPerSupercluster; ci_offset++)
     {
         fci_buf[ci_offset] = (float3)(0.0F);
     }
@@ -272,17 +272,18 @@ __kernel void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_opencl)
     float E_el = 0.0F;
 
 #    if defined EXCLUSION_FORCES /* Ewald or RF */
-    if (nb_sci.shift == CENTRAL && pl_cj4[cij4_start].cj[0] == sci * NCL_PER_SUPERCL)
+    if (nb_sci.shift == CENTRAL && pl_cj4[cij4_start].cj[0] == sci * c_nbnxnGpuNumClusterPerSupercluster)
     {
         /* we have the diagonal: add the charge and LJ self interaction energy term */
-        for (int i = 0; i < NCL_PER_SUPERCL; i++)
+        for (int i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i++)
         {
 #        if defined EL_EWALD_ANY || defined EL_RF || defined EL_CUTOFF
             const float qi = xqib[i * CL_SIZE + tidxi].w;
             E_el += qi * qi;
 #        endif
 #        if defined LJ_EWALD
-            E_lj += nbfp_climg2d[atom_types[(sci * NCL_PER_SUPERCL + i) * CL_SIZE + tidxi] * (ntypes + 1) * 2];
+            E_lj += nbfp_climg2d[atom_types[(sci * c_nbnxnGpuNumClusterPerSupercluster + i) * CL_SIZE + tidxi]
+                                 * (ntypes + 1) * 2];
 #        endif /* LJ_EWALD */
         }
 
@@ -335,9 +336,9 @@ __kernel void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_opencl)
 #endif
             for (int jm = 0; jm < c_nbnxnGpuJgroupSize; jm++)
             {
-                if (imask & (superClInteractionMask << (jm * NCL_PER_SUPERCL)))
+                if (imask & (superClInteractionMask << (jm * c_nbnxnGpuNumClusterPerSupercluster)))
                 {
-                    unsigned int mask_ji = (1U << (jm * NCL_PER_SUPERCL));
+                    unsigned int mask_ji = (1U << (jm * c_nbnxnGpuNumClusterPerSupercluster));
 
                     const int cj = loadCj(cjs, pl_cj4[j4].cj, jm, tidxi, tidxj);
                     const int aj = cj * CL_SIZE + tidxj;
@@ -357,11 +358,11 @@ __kernel void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_opencl)
 #if !defined PRUNE_NBL
 #    pragma unroll 8
 #endif
-                    for (int i = 0; i < NCL_PER_SUPERCL; i++)
+                    for (int i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i++)
                     {
                         if (imask & mask_ji)
                         {
-                            const int gmx_unused ci = sci * NCL_PER_SUPERCL + i; /* i cluster index */
+                            const int gmx_unused ci = sci * c_nbnxnGpuNumClusterPerSupercluster + i; /* i cluster index */
 
                             /* all threads load an atom from i cluster ci into shmem! */
                             const float4 xiqbuf = xqib[i * CL_SIZE + tidxi];
@@ -423,8 +424,10 @@ __kernel void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_opencl)
 #    endif /* LJ_COMB_GEOM */
 #endif     /* LJ_COMB */
 
-                                // Ensure distance do not become so small that r^-12 overflows
-                                r2 = max(r2, NBNXN_MIN_RSQ);
+                                // Ensure distance do not become so small that r^-12 overflows.
+                                // Cast to float to ensure the correct built-in max() function
+                                // is called.
+                                r2 = max(r2, (float)c_nbnxnMinDistanceSquared);
 
                                 const float inv_r  = rsqrt(r2);
                                 const float inv_r2 = inv_r * inv_r;
index 475c1d68b063d692ca5ed82bbfa4b41825395d97..16dd0c4962dbf0b853403b9ec955b6029b1d4e16 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -101,24 +101,23 @@ nbnxn_kernel_prune_rolling_opencl
 #endif
 
     // TODO move these consts to utils and unify their use with the nonbonded kernels
-    const int c_numClPerSupercl = NCL_PER_SUPERCL;
-    const int c_clSize          = CL_SIZE;
+    const int c_clSize = CL_SIZE;
 
     // TODO pass this value at compile-time as a macro
     const int c_nbnxnGpuClusterpairSplit = 2;
 
-    /*! i-cluster interaction mask for a super-cluster with all c_numClPerSupercl=8 bits set */
-    const unsigned superClInteractionMask = ((1U << c_numClPerSupercl) - 1U);
+    /*! i-cluster interaction mask for a super-cluster with all c_nbnxnGpuNumClusterPerSupercluster=8 bits set */
+    const unsigned superClInteractionMask = ((1U << c_nbnxnGpuNumClusterPerSupercluster) - 1U);
 
-#define LOCAL_OFFSET (xib + c_numClPerSupercl * c_clSize)
+#define LOCAL_OFFSET (xib + c_nbnxnGpuNumClusterPerSupercluster * c_clSize)
     /* shmem buffer for i cj pre-loading */
     CjType cjs = 0;
 #if USE_CJ_PREFETCH
     cjs = (((__local int*)(LOCAL_OFFSET)) + tidxz * c_nbnxnGpuClusterpairSplit * c_nbnxnGpuJgroupSize);
 #    undef LOCAL_OFFSET
 /* Offset calculated using xib because cjs depends on on tidxz! */
-#    define LOCAL_OFFSET                                      \
-        (((__local int*)(xib + c_numClPerSupercl * c_clSize)) \
+#    define LOCAL_OFFSET                                                        \
+        (((__local int*)(xib + c_nbnxnGpuNumClusterPerSupercluster * c_clSize)) \
          + (NTHREAD_Z * c_nbnxnGpuClusterpairSplit * c_nbnxnGpuJgroupSize))
 #endif
 #if !USE_SUBGROUP_ANY
@@ -147,10 +146,10 @@ nbnxn_kernel_prune_rolling_opencl
 
     if (tidxz == 0)
     {
-        for (int i = 0; i < NCL_PER_SUPERCL; i += CL_SIZE)
+        for (int i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i += CL_SIZE)
         {
             /* Pre-load i-atom x and q into shared memory */
-            const int ci = sci * c_numClPerSupercl + tidxj + i;
+            const int ci = sci * c_nbnxnGpuNumClusterPerSupercluster + tidxj + i;
             const int ai = ci * c_clSize + tidxi;
 
             /* We don't need q, but using float4 in shmem avoids bank conflicts */
@@ -194,9 +193,9 @@ nbnxn_kernel_prune_rolling_opencl
 #pragma unroll 4
             for (int jm = 0; jm < c_nbnxnGpuJgroupSize; jm++)
             {
-                if (imaskCheck & (superClInteractionMask << (jm * c_numClPerSupercl)))
+                if (imaskCheck & (superClInteractionMask << (jm * c_nbnxnGpuNumClusterPerSupercluster)))
                 {
-                    unsigned int mask_ji = (1U << (jm * c_numClPerSupercl));
+                    unsigned int mask_ji = (1U << (jm * c_nbnxnGpuNumClusterPerSupercluster));
 
                     const int cj = loadCj(cjs, pl_cj4[j4].cj, jm, tidxi, tidxj);
                     const int aj = cj * c_clSize + tidxj;
@@ -206,7 +205,7 @@ nbnxn_kernel_prune_rolling_opencl
                     const float3 xj  = (float3)(tmp.xyz);
 
 #pragma unroll 8
-                    for (int i = 0; i < c_numClPerSupercl; i++)
+                    for (int i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i++)
                     {
                         if (imaskCheck & mask_ji)
                         {
index ec5d40f83d484e38531bea0d7eb0086404478c85..7d70a5908abf11b4595df527e7560e7e1fdb38a9 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+/*! \internal \file
+ *  \brief
+ *  Utility constant and function declaration for the OpenCL non-bonded kernels.
+ *  This header should be included once at the top level, just before the
+ *  kernels are included (has to be preceded by nbnxn_ocl_types.h).
+ *
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \ingroup module_nbnxm
+ */
 
 #define GMX_DOUBLE 0
 
 #include "gromacs/gpu_utils/device_utils.clh"
 #include "gromacs/gpu_utils/vectype_ops.clh"
-#include "gromacs/nbnxm/constants.h"
 #include "gromacs/pbcutil/ishift.h"
 
 #include "nbnxm_ocl_consts.h"
 
-#define CL_SIZE (NBNXN_GPU_CLUSTER_SIZE)
-#define NCL_PER_SUPERCL c_nbnxnGpuNumClusterPerSupercluster
+#define CL_SIZE (c_nbnxnGpuClusterSize)
 
 #define WARP_SIZE (CL_SIZE * CL_SIZE / 2) // Currently only c_nbnxnGpuClusterpairSplit=2 supported
 
@@ -143,50 +151,76 @@ typedef struct
 typedef struct cl_nbparam_params
 {
 
-    int eeltype; /**< type of electrostatics, takes values from #eelCu */
-    int vdwtype; /**< type of VdW impl., takes values from #evdwCu     */
-
-    float epsfac;      /**< charge multiplication factor                      */
-    float c_rf;        /**< Reaction-field/plain cutoff electrostatics const. */
-    float two_k_rf;    /**< Reaction-field electrostatics constant            */
-    float ewald_beta;  /**< Ewald/PME parameter                               */
-    float sh_ewald;    /**< Ewald/PME correction term substracted from the direct-space potential */
-    float sh_lj_ewald; /**< LJ-Ewald/PME correction term added to the correction potential        */
-    float ewaldcoeff_lj; /**< LJ-Ewald/PME coefficient                          */
-
-    float rcoulomb_sq; /**< Coulomb cut-off squared                           */
-
-    float rvdw_sq;       /**< VdW cut-off squared                               */
-    float rvdw_switch;   /**< VdW switched cut-off                              */
-    float rlistOuter_sq; /**< Full, outer pair-list cut-off squared             */
-    float rlistInner_sq; /**< Inner, dynamic pruned pair-list cut-off squared  XXX: this is only needed in the pruning kernels, but for now we also pass it to the nonbondeds */
-
-    shift_consts_t  dispersion_shift; /**< VdW shift dispersion constants           */
-    shift_consts_t  repulsion_shift;  /**< VdW shift repulsion constants            */
-    switch_consts_t vdw_switch;       /**< VdW switch constants                     */
+    //! type of electrostatics, takes values from #eelCu
+    int eeltype;
+    //! type of VdW impl., takes values from #evdwCu
+    int vdwtype;
+
+    //! charge multiplication factor
+    float epsfac;
+    //! Reaction-field/plain cutoff electrostatics const.
+    float c_rf;
+    //! Reaction-field electrostatics constant
+    float two_k_rf;
+    //! Ewald/PME parameter
+    float ewald_beta;
+    //! Ewald/PME correction term substracted from the direct-space potential
+    float sh_ewald;
+    //! LJ-Ewald/PME correction term added to the correction potential
+    float sh_lj_ewald;
+    //! LJ-Ewald/PME coefficient
+    float ewaldcoeff_lj;
+
+    //! Coulomb cut-off squared
+    float rcoulomb_sq;
+
+    //! VdW cut-off squared
+    float rvdw_sq;
+    //! VdW switched cut-off
+    float rvdw_switch;
+    //! Full, outer pair-list cut-off squared
+    float rlistOuter_sq;
+    //! Inner, dynamic pruned pair-list cut-off squared  XXX: this is only needed in the pruning kernels, but for now we also pass it to the nonbondeds
+    float rlistInner_sq;
+
+    //! VdW shift dispersion constants
+    shift_consts_t dispersion_shift;
+    //! VdW shift repulsion constants
+    shift_consts_t repulsion_shift;
+    //! VdW switch constants
+    switch_consts_t vdw_switch;
 
     /* Ewald Coulomb force table data - accessed through texture memory */
-    float coulomb_tab_scale; /**< table scale/spacing                        */
+    //! table scale/spacing
+    float coulomb_tab_scale;
 } cl_nbparam_params_t;
 
 typedef struct
 {
-    int sci;           /* i-super-cluster       */
-    int shift;         /* Shift vector index plus possible flags */
-    int cj4_ind_start; /* Start index into cj4  */
-    int cj4_ind_end;   /* End index into cj4    */
+    //! i-super-cluster
+    int sci;
+    //! Shift vector index plus possible flags
+    int shift;
+    //! Start index into cj4
+    int cj4_ind_start;
+    //! End index into cj4
+    int cj4_ind_end;
 } nbnxn_sci_t;
 
 typedef struct
 {
-    unsigned int imask;    /* The i-cluster interactions mask for 1 warp  */
-    int          excl_ind; /* Index into the exclusion array for 1 warp   */
+    //! The i-cluster interactions mask for 1 warp
+    unsigned int imask;
+    //! Index into the exclusion array for 1 warp
+    int excl_ind;
 } nbnxn_im_ei_t;
 
 typedef struct
 {
-    int           cj[4];   /* The 4 j-clusters                            */
-    nbnxn_im_ei_t imei[2]; /* The i-cluster mask data       for 2 warps   */
+    //! The 4 j-clusters
+    int cj[4];
+    //! The i-cluster mask data       for 2 warps
+    nbnxn_im_ei_t imei[2];
 } nbnxn_cj4_t;
 
 
@@ -197,8 +231,8 @@ typedef struct
                                                */
 } nbnxn_excl_t;
 
-/*! i-cluster interaction mask for a super-cluster with all NCL_PER_SUPERCL bits set */
-__constant unsigned supercl_interaction_mask = ((1U << NCL_PER_SUPERCL) - 1U);
+/*! i-cluster interaction mask for a super-cluster with all c_nbnxnGpuNumClusterPerSupercluster bits set */
+__constant unsigned supercl_interaction_mask = ((1U << c_nbnxnGpuNumClusterPerSupercluster) - 1U);
 
 gmx_opencl_inline void preloadCj4Generic(__local int*        sm_cjPreload,
                                          const __global int* gm_cj,
@@ -662,9 +696,9 @@ gmx_opencl_inline void reduce_force_i_and_shift_shfl(float3*         fci_buf,
     /* Only does reduction over 4 elements in cluster (2 per warp). Needs to be changed
      * for CL_SIZE>4.*/
     float2 fshift_buf = 0;
-    for (int ci_offset = 0; ci_offset < NCL_PER_SUPERCL; ci_offset++)
+    for (int ci_offset = 0; ci_offset < c_nbnxnGpuNumClusterPerSupercluster; ci_offset++)
     {
-        int    aidx = (sci * NCL_PER_SUPERCL + ci_offset) * CL_SIZE + tidxi;
+        int    aidx = (sci * c_nbnxnGpuNumClusterPerSupercluster + ci_offset) * CL_SIZE + tidxi;
         float3 fin  = fci_buf[ci_offset];
         fin.x += intel_sub_group_shuffle_down(fin.x, fin.x, CL_SIZE);
         fin.y += intel_sub_group_shuffle_up(fin.y, fin.y, CL_SIZE);
@@ -718,9 +752,9 @@ gmx_opencl_inline void reduce_force_i_and_shift_pow2(volatile __local float* f_b
                                                      __global float*         fshift)
 {
     float fshift_buf = 0;
-    for (int ci_offset = 0; ci_offset < NCL_PER_SUPERCL; ci_offset++)
+    for (int ci_offset = 0; ci_offset < c_nbnxnGpuNumClusterPerSupercluster; ci_offset++)
     {
-        int aidx = (sci * NCL_PER_SUPERCL + ci_offset) * CL_SIZE + tidxi;
+        int aidx = (sci * c_nbnxnGpuNumClusterPerSupercluster + ci_offset) * CL_SIZE + tidxi;
         int tidx = tidxi + tidxj * CL_SIZE;
         /* store i forces in shmem */
         f_buf[tidx]                   = fci_buf[ci_offset].x;
index 6b35a5d0e0aa4a9dc4bd8384692eec330814855a..cda1c0045b1b65f8ddc02858dcdcb2647cf7852c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f12193e11f5615fa528b84779d59d7c249b27320..886298a20efeb06fda1cf12b8b3877a0da5d63ca 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,6 +53,7 @@
 #include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/nbnxm/gpu_types_common.h"
 #include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/nbnxm/nbnxm_gpu.h"
 #include "gromacs/nbnxm/pairlist.h"
 #include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/fatalerror.h"
@@ -59,6 +61,8 @@
 
 #include "nbnxm_ocl_consts.h"
 
+struct gmx_wallclock_gpu_nbnxn_t;
+
 /* kernel does #include "gromacs/math/utilities.h" */
 /* Move the actual useful stuff here: */
 
 const int c_oclPruneKernelJ4ConcurrencyDEFAULT = GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY_DEFAULT;
 /*! @} */
 
-/*! \brief Returns the j4 processing concurrency parameter for the vendor \p vendorId
- *  \param vendorId takes values from #ocl_vendor_id_t.
- */
-static inline int getOclPruneKernelJ4Concurrency(int vendorId)
-{
-    switch (vendorId)
-    {
-        default: return c_oclPruneKernelJ4ConcurrencyDEFAULT;
-    }
-}
-
-
-/*! \brief Electrostatic OpenCL kernel flavors.
- *
- *  Types of electrostatics implementations available in the OpenCL non-bonded
- *  force kernels. These represent both the electrostatics types implemented
- *  by the kernels (cut-off, RF, and Ewald - a subset of what's defined in
- *  enums.h) as well as encode implementation details analytical/tabulated
- *  and single or twin cut-off (for Ewald kernels).
- *  Note that the cut-off and RF kernels have only analytical flavor and unlike
- *  in the CPU kernels, the tabulated kernels are ATM Ewald-only.
- *
- *  The row-order of pointers to different electrostatic kernels defined in
- *  nbnxn_cuda.cu by the nb_*_kfunc_ptr function pointer table
- *  should match the order of enumerated types below.
- */
-enum eelOcl
-{
-    eelOclCUT,
-    eelOclRF,
-    eelOclEWALD_TAB,
-    eelOclEWALD_TAB_TWIN,
-    eelOclEWALD_ANA,
-    eelOclEWALD_ANA_TWIN,
-    eelOclNR
-};
-
-/*! \brief VdW OpenCL kernel flavors.
- *
- * The enumerates values correspond to the LJ implementations in the OpenCL non-bonded
- * kernels.
- *
- * The column-order of pointers to different electrostatic kernels defined in
- * nbnxn_cuda.cu by the nb_*_kfunc_ptr function pointer table
- * should match the order of enumerated types below.
- */
-enum evdwOcl
-{
-    evdwOclCUT,
-    evdwOclCUTCOMBGEOM,
-    evdwOclCUTCOMBLB,
-    evdwOclFSWITCH,
-    evdwOclPSWITCH,
-    evdwOclEWALDGEOM,
-    evdwOclEWALDLB,
-    evdwOclNR
-};
-
 /*! \brief Pruning kernel flavors.
  *
  * The values correspond to the first call of the pruning post-list generation
@@ -150,81 +96,56 @@ enum ePruneKind
  *  The energies/shift forces get downloaded here first, before getting added
  *  to the CPU-side aggregate values.
  */
-typedef struct cl_nb_staging
+struct nb_staging_t
 {
-    float* e_lj;        /**< LJ energy                       */
-    float* e_el;        /**< electrostatic energy            */
-    float (*fshift)[3]; /**< float3 buffer with shift forces */
-} cl_nb_staging_t;
+    //! LJ energy
+    float* e_lj = nullptr;
+    //! electrostatic energy
+    float* e_el = nullptr;
+    //! float3 buffer with shift forces
+    float (*fshift)[3] = nullptr;
+};
 
 /*! \internal
  * \brief Nonbonded atom data - both inputs and outputs.
  */
 typedef struct cl_atomdata
 {
-    int natoms;       /**< number of atoms                              */
-    int natoms_local; /**< number of local atoms                        */
-    int nalloc;       /**< allocation size for the atom data (xq, f)    */
-
-    cl_mem xq; /**< float4 buffer with atom coordinates + charges, size natoms */
-
-    cl_mem f;           /**< float3 buffer with force output array, size natoms         */
-    size_t f_elem_size; /**< Size in bytes for one element of f buffer      */
-
-    cl_mem e_lj; /**< LJ energy output, size 1                       */
-    cl_mem e_el; /**< Electrostatics energy input, size 1            */
-
-    cl_mem fshift;           /**< float3 buffer with shift forces                */
-    size_t fshift_elem_size; /**< Size in bytes for one element of fshift buffer */
-
-    int    ntypes;     /**< number of atom types                           */
-    cl_mem atom_types; /**< int buffer with atom type indices, size natoms */
-    cl_mem lj_comb;    /**< float2 buffer with sqrt(c6),sqrt(c12), size natoms */
-
-    cl_mem shift_vec;           /**< float3 buffer with shifts values               */
-    size_t shift_vec_elem_size; /**< Size in bytes for one element of shift_vec buffer */
-
-    cl_bool bShiftVecUploaded; /**< true if the shift vector has been uploaded  */
+    //! number of atoms
+    int natoms;
+    //! number of local atoms
+    int natoms_local;
+    //! allocation size for the atom data (xq, f)
+    int nalloc;
+
+    //! float4 buffer with atom coordinates + charges, size natoms
+    DeviceBuffer<float> xq;
+
+    //! float3 buffer with force output array, size natoms
+    DeviceBuffer<float> f;
+
+    //! LJ energy output, size 1
+    DeviceBuffer<float> e_lj;
+    //! Electrostatics energy input, size 1
+    DeviceBuffer<float> e_el;
+
+    //! float3 buffer with shift forces
+    DeviceBuffer<float> fshift;
+
+    //! number of atom types
+    int ntypes;
+    //! int buffer with atom type indices, size natoms
+    DeviceBuffer<int> atom_types;
+    //! float2 buffer with sqrt(c6),sqrt(c12), size natoms
+    DeviceBuffer<float> lj_comb;
+
+    //! float3 buffer with shifts values
+    DeviceBuffer<float> shift_vec;
+
+    //! true if the shift vector has been uploaded
+    bool bShiftVecUploaded;
 } cl_atomdata_t;
 
-/*! \internal
- * \brief Parameters required for the OpenCL nonbonded calculations.
- */
-typedef struct cl_nbparam
-{
-
-    int eeltype; /**< type of electrostatics, takes values from #eelOcl */
-    int vdwtype; /**< type of VdW impl., takes values from #evdwOcl     */
-
-    float epsfac;      /**< charge multiplication factor                      */
-    float c_rf;        /**< Reaction-field/plain cutoff electrostatics const. */
-    float two_k_rf;    /**< Reaction-field electrostatics constant            */
-    float ewald_beta;  /**< Ewald/PME parameter                               */
-    float sh_ewald;    /**< Ewald/PME correction term substracted from the direct-space potential */
-    float sh_lj_ewald; /**< LJ-Ewald/PME correction term added to the correction potential        */
-    float ewaldcoeff_lj; /**< LJ-Ewald/PME coefficient                          */
-
-    float rcoulomb_sq; /**< Coulomb cut-off squared                           */
-
-    float rvdw_sq;           /**< VdW cut-off squared                               */
-    float rvdw_switch;       /**< VdW switched cut-off                              */
-    float rlistOuter_sq;     /**< Full, outer pair-list cut-off squared             */
-    float rlistInner_sq;     /**< Inner, dynamic pruned pair-list cut-off squared   */
-    bool  useDynamicPruning; /**< True if we use dynamic pair-list pruning          */
-
-    shift_consts_t  dispersion_shift; /**< VdW shift dispersion constants           */
-    shift_consts_t  repulsion_shift;  /**< VdW shift repulsion constants            */
-    switch_consts_t vdw_switch;       /**< VdW switch constants                     */
-
-    /* LJ non-bonded parameters - accessed through texture memory */
-    cl_mem nbfp_climg2d; /**< nonbonded parameter table with C6/C12 pairs per atom type-pair, 2*ntype^2 elements */
-    cl_mem nbfp_comb_climg2d; /**< nonbonded parameter table per atom type, 2*ntype elements */
-
-    /* Ewald Coulomb force table data - accessed through texture memory */
-    float  coulomb_tab_scale;   /**< table scale/spacing                        */
-    cl_mem coulomb_tab_climg2d; /**< pointer to the table in the device memory  */
-} cl_nbparam_t;
-
 /*! \internal
  * \brief Data structure shared between the OpenCL device code and OpenCL host code
  *
@@ -233,38 +154,51 @@ typedef struct cl_nbparam
 typedef struct cl_nbparam_params
 {
 
-    int eeltype; /**< type of electrostatics, takes values from #eelCu */
-    int vdwtype; /**< type of VdW impl., takes values from #evdwCu     */
-
-    float epsfac;      /**< charge multiplication factor                      */
-    float c_rf;        /**< Reaction-field/plain cutoff electrostatics const. */
-    float two_k_rf;    /**< Reaction-field electrostatics constant            */
-    float ewald_beta;  /**< Ewald/PME parameter                               */
-    float sh_ewald;    /**< Ewald/PME correction term substracted from the direct-space potential */
-    float sh_lj_ewald; /**< LJ-Ewald/PME correction term added to the correction potential        */
-    float ewaldcoeff_lj; /**< LJ-Ewald/PME coefficient                          */
-
-    float rcoulomb_sq; /**< Coulomb cut-off squared                           */
-
-    float rvdw_sq;       /**< VdW cut-off squared                               */
-    float rvdw_switch;   /**< VdW switched cut-off                              */
-    float rlistOuter_sq; /**< Full, outer pair-list cut-off squared             */
-    float rlistInner_sq; /**< Inner, dynamic pruned pair-list cut-off squared   */
-
-    shift_consts_t  dispersion_shift; /**< VdW shift dispersion constants           */
-    shift_consts_t  repulsion_shift;  /**< VdW shift repulsion constants            */
-    switch_consts_t vdw_switch;       /**< VdW switch constants                     */
+    //! type of electrostatics, takes values from #eelType
+    int eeltype;
+    //! type of VdW impl., takes values from #evdwType
+    int vdwtype;
+
+    //! charge multiplication factor
+    float epsfac;
+    //! Reaction-field/plain cutoff electrostatics const.
+    float c_rf;
+    //! Reaction-field electrostatics constant
+    float two_k_rf;
+    //! Ewald/PME parameter
+    float ewald_beta;
+    //! Ewald/PME correction term substracted from the direct-space potential
+    float sh_ewald;
+    //! LJ-Ewald/PME correction term added to the correction potential
+    float sh_lj_ewald;
+    //! LJ-Ewald/PME coefficient
+    float ewaldcoeff_lj;
+
+    //! Coulomb cut-off squared
+    float rcoulomb_sq;
+
+    //! VdW cut-off squared
+    float rvdw_sq;
+    //! VdW switched cut-off
+    float rvdw_switch;
+    //! Full, outer pair-list cut-off squared
+    float rlistOuter_sq;
+    //! Inner, dynamic pruned pair-list cut-off squared
+    float rlistInner_sq;
+
+    //! VdW shift dispersion constants
+    shift_consts_t dispersion_shift;
+    //! VdW shift repulsion constants
+    shift_consts_t repulsion_shift;
+    //! VdW switch constants
+    switch_consts_t vdw_switch;
 
     /* Ewald Coulomb force table data - accessed through texture memory */
-    float coulomb_tab_scale; /**< table scale/spacing                        */
+    //! table scale/spacing
+    float coulomb_tab_scale;
 } cl_nbparam_params_t;
 
 
-/*! \internal
- * \brief Pair list data.
- */
-using cl_plist_t = Nbnxm::gpu_plist;
-
 /** \internal
  * \brief Typedef of actual timer type.
  */
@@ -273,48 +207,67 @@ typedef struct Nbnxm::gpu_timers_t cl_timers_t;
 /*! \internal
  * \brief Main data structure for OpenCL nonbonded force calculations.
  */
-struct gmx_nbnxn_ocl_t
+struct NbnxmGpu
 {
-    const gmx_device_info_t*          dev_info;    /**< OpenCL device information    */
-    struct gmx_device_runtime_data_t* dev_rundata; /**< OpenCL runtime data (context, kernels) */
+    /* \brief OpenCL device context
+     *
+     * \todo Make it constant reference, once NbnxmGpu is a proper class.
+     */
+    const DeviceContext* deviceContext_;
+    //! OpenCL runtime data (context, kernels)
+    struct gmx_device_runtime_data_t* dev_rundata = nullptr;
 
     /**< Pointers to non-bonded kernel functions
      * organized similar with nb_kfunc_xxx arrays in nbnxn_ocl.cpp */
     ///@{
-    cl_kernel kernel_noener_noprune_ptr[eelOclNR][evdwOclNR];
-    cl_kernel kernel_ener_noprune_ptr[eelOclNR][evdwOclNR];
-    cl_kernel kernel_noener_prune_ptr[eelOclNR][evdwOclNR];
-    cl_kernel kernel_ener_prune_ptr[eelOclNR][evdwOclNR];
+    cl_kernel kernel_noener_noprune_ptr[eelTypeNR][evdwTypeNR] = { { nullptr } };
+    cl_kernel kernel_ener_noprune_ptr[eelTypeNR][evdwTypeNR]   = { { nullptr } };
+    cl_kernel kernel_noener_prune_ptr[eelTypeNR][evdwTypeNR]   = { { nullptr } };
+    cl_kernel kernel_ener_prune_ptr[eelTypeNR][evdwTypeNR]     = { { nullptr } };
     ///@}
-    cl_kernel kernel_pruneonly[ePruneNR]; /**< prune kernels, ePruneKind defined the kernel kinds */
+    //! prune kernels, ePruneKind defined the kernel kinds
+    cl_kernel kernel_pruneonly[ePruneNR] = { nullptr };
 
-    bool bPrefetchLjParam; /**< true if prefetching fg i-atom LJ parameters should be used in the kernels */
+    //! true if prefetching fg i-atom LJ parameters should be used in the kernels
+    bool bPrefetchLjParam = false;
 
     /**< auxiliary kernels implementing memset-like functions */
     ///@{
-    cl_kernel kernel_memset_f;
-    cl_kernel kernel_memset_f2;
-    cl_kernel kernel_memset_f3;
-    cl_kernel kernel_zero_e_fshift;
+    cl_kernel kernel_memset_f      = nullptr;
+    cl_kernel kernel_memset_f2     = nullptr;
+    cl_kernel kernel_memset_f3     = nullptr;
+    cl_kernel kernel_zero_e_fshift = nullptr;
     ///@}
 
-    cl_bool bUseTwoStreams; /**< true if doing both local/non-local NB work on GPU          */
-    cl_bool bNonLocalStreamActive; /**< true indicates that the nonlocal_done event was enqueued */
-
-    cl_atomdata_t* atdat;   /**< atom data                                                  */
-    cl_nbparam_t*  nbparam; /**< parameters required for the non-bonded calc.               */
-    gmx::EnumerationArray<Nbnxm::InteractionLocality, cl_plist_t*> plist; /**< pair-list data structures (local and non-local)            */
-    cl_nb_staging_t nbst; /**< staging area where fshift/energies get downloaded          */
-
-    gmx::EnumerationArray<Nbnxm::InteractionLocality, cl_command_queue> stream; /**< local and non-local GPU queues                             */
-
-    /** events used for synchronization */
-    cl_event nonlocal_done;               /**< event triggered when the non-local non-bonded kernel
-                                             is done (and the local transfer can proceed) */
-    cl_event misc_ops_and_local_H2D_done; /**< event triggered when the tasks issued in
-                                             the local stream that need to precede the
-                                             non-local force calculations are done
-                                             (e.g. f buffer 0-ing, local x/q H2D) */
+    //! true if doing both local/non-local NB work on GPU
+    bool bUseTwoStreams = false;
+    //! true indicates that the nonlocal_done event was enqueued
+    bool bNonLocalStreamActive = false;
+
+    //! atom data
+    cl_atomdata_t* atdat = nullptr;
+    //! parameters required for the non-bonded calc.
+    NBParamGpu* nbparam = nullptr;
+    //! pair-list data structures (local and non-local)
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, Nbnxm::gpu_plist*> plist = { nullptr };
+    //! staging area where fshift/energies get downloaded
+    nb_staging_t nbst;
+
+    //! local and non-local GPU queues
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, const DeviceStream*> deviceStreams;
+
+    /*! \brief Events used for synchronization */
+    /*! \{ */
+    /*! \brief Event triggered when the non-local non-bonded
+     * kernel is done (and the local transfer can proceed) */
+    cl_event nonlocal_done = nullptr;
+    /*! \brief Event triggered when the tasks issued in the local
+     * stream that need to precede the non-local force or buffer
+     * operation calculations are done (e.g. f buffer 0-ing, local
+     * x/q H2D, buffer op initialization in local stream that is
+     * required also by nonlocal stream ) */
+    cl_event misc_ops_and_local_H2D_done = nullptr;
+    /*! \} */
 
     //! True if there has been local/nonlocal GPU work, either bonded or nonbonded, scheduled
     //  to be executed in the current domain. As long as bonded work is not split up into
@@ -322,9 +275,12 @@ struct gmx_nbnxn_ocl_t
     gmx::EnumerationArray<Nbnxm::InteractionLocality, bool> haveWork;
 
 
-    cl_bool      bDoTime; /**< True if event-based timing is enabled.                     */
-    cl_timers_t* timers;  /**< OpenCL event-based timers.                                 */
-    struct gmx_wallclock_gpu_nbnxn_t* timings; /**< Timing data. TODO: deprecate this and query timers for accumulated data instead */
+    //! True if event-based timing is enabled.
+    bool bDoTime = false;
+    //! OpenCL event-based timers.
+    cl_timers_t* timers = nullptr;
+    //! Timing data. TODO: deprecate this and query timers for accumulated data instead
+    gmx_wallclock_gpu_nbnxn_t* timings = nullptr;
 };
 
 #endif /* NBNXN_OPENCL_TYPES_H */
index 6174905d578fffe2f68497beee7ea8e84cf12139..aa29c61cdee6f7057a5014fbdaf4139fc09ce19c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
 #include "gromacs/mdtypes/group.h"
 #include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/nblist.h"
+#include "gromacs/nbnxm/atomdata.h"
 #include "gromacs/nbnxm/gpu_data_mgmt.h"
 #include "gromacs/pbcutil/ishift.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/simd/simd.h"
 #include "gromacs/simd/vector_operations.h"
-#include "gromacs/topology/block.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxomp.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/smalloc.h"
 
-#include "atomdata.h"
 #include "boundingboxes.h"
 #include "clusterdistancekerneltype.h"
 #include "gridset.h"
@@ -100,7 +102,7 @@ enum class NbnxnLayout
     Gpu8x8x8   // i-cluster size 8, j-cluster size 8 + super-clustering
 };
 
-#if GMX_SIMD
+#if defined(GMX_NBNXN_SIMD_4XN) || defined(GMX_NBNXN_SIMD_2XNN)
 /* Returns the j-cluster size */
 template<NbnxnLayout layout>
 static constexpr int jClusterSize()
@@ -213,7 +215,7 @@ static inline int xIndexFromCj(int cj)
         return cj * STRIDE_P8;
     }
 }
-#endif // GMX_SIMD
+#endif // defined(GMX_NBNXN_SIMD_4XN) || defined(GMX_NBNXN_SIMD_2XNN)
 
 
 void nbnxn_init_pairlist_fep(t_nblist* nl)
@@ -238,20 +240,19 @@ void nbnxn_init_pairlist_fep(t_nblist* nl)
     nl->excl_fep = nullptr;
 }
 
-static void init_buffer_flags(nbnxn_buffer_flags_t* flags, int natoms)
+static constexpr int sizeNeededForBufferFlags(const int numAtoms)
 {
-    flags->nflag = (natoms + NBNXN_BUFFERFLAG_SIZE - 1) / NBNXN_BUFFERFLAG_SIZE;
-    if (flags->nflag > flags->flag_nalloc)
-    {
-        flags->flag_nalloc = over_alloc_large(flags->nflag);
-        srenew(flags->flag, flags->flag_nalloc);
-    }
-    for (int b = 0; b < flags->nflag; b++)
-    {
-        bitmask_clear(&(flags->flag[b]));
-    }
+    return (numAtoms + NBNXN_BUFFERFLAG_SIZE - 1) / NBNXN_BUFFERFLAG_SIZE;
 }
 
+// Resets current flags to 0 and adds more flags if needed.
+static void resizeAndZeroBufferFlags(std::vector<gmx_bitmask_t>* flags, const int numAtoms)
+{
+    flags->clear();
+    flags->resize(sizeNeededForBufferFlags(numAtoms), gmx_bitmask_t{ 0 });
+}
+
+
 /* Returns the pair-list cutoff between a bounding box and a grid cell given an atom-to-atom pair-list cutoff
  *
  * Given a cutoff distance between atoms, this functions returns the cutoff
@@ -1363,12 +1364,12 @@ static nbnxn_sci_t* getOpenIEntry(NbnxnPairlistGpu* nbl)
  * Set all atom-pair exclusions from the topology stored in exclusions
  * as masks in the pair-list for simple list entry iEntry.
  */
-static void setExclusionsForIEntry(const Nbnxm::GridSet& gridSet,
-                                   NbnxnPairlistCpu*     nbl,
-                                   gmx_bool              diagRemoved,
-                                   int                   na_cj_2log,
-                                   const nbnxn_ci_t&     iEntry,
-                                   const t_blocka&       exclusions)
+static void setExclusionsForIEntry(const Nbnxm::GridSet&   gridSet,
+                                   NbnxnPairlistCpu*       nbl,
+                                   gmx_bool                diagRemoved,
+                                   int                     na_cj_2log,
+                                   const nbnxn_ci_t&       iEntry,
+                                   const ListOfLists<int>& exclusions)
 {
     if (iEntry.cj_ind_end == iEntry.cj_ind_start)
     {
@@ -1391,11 +1392,8 @@ static void setExclusionsForIEntry(const Nbnxm::GridSet& gridSet,
         if (iAtom >= 0)
         {
             /* Loop over the topology-based exclusions for this i-atom */
-            for (int exclIndex = exclusions.index[iAtom]; exclIndex < exclusions.index[iAtom + 1];
-                 exclIndex++)
+            for (const int jAtom : exclusions[iAtom])
             {
-                const int jAtom = exclusions.a[exclIndex];
-
                 if (jAtom == iAtom)
                 {
                     /* The self exclusion are already set, save some time */
@@ -1876,9 +1874,9 @@ static void make_fep_list(gmx::ArrayRef<const int> atomIndices,
 static void setExclusionsForIEntry(const Nbnxm::GridSet& gridSet,
                                    NbnxnPairlistGpu*     nbl,
                                    gmx_bool              diagRemoved,
-                                   int gmx_unused     na_cj_2log,
-                                   const nbnxn_sci_t& iEntry,
-                                   const t_blocka&    exclusions)
+                                   int gmx_unused          na_cj_2log,
+                                   const nbnxn_sci_t&      iEntry,
+                                   const ListOfLists<int>& exclusions)
 {
     if (iEntry.numJClusterGroups() == 0)
     {
@@ -1912,11 +1910,8 @@ static void setExclusionsForIEntry(const Nbnxm::GridSet& gridSet,
             const int iCluster = i / c_clusterSize;
 
             /* Loop over the topology-based exclusions for this i-atom */
-            for (int exclIndex = exclusions.index[iAtom]; exclIndex < exclusions.index[iAtom + 1];
-                 exclIndex++)
+            for (const int jAtom : exclusions[iAtom])
             {
-                const int jAtom = exclusions.a[exclIndex];
-
                 if (jAtom == iAtom)
                 {
                     /* The self exclusions are already set, save some time */
@@ -3083,7 +3078,7 @@ static void nbnxn_make_pairlist_part(const Nbnxm::GridSet&   gridSet,
                                      const Grid&             jGrid,
                                      PairsearchWork*         work,
                                      const nbnxn_atomdata_t* nbat,
-                                     const t_blocka&         exclusions,
+                                     const ListOfLists<int>& exclusions,
                                      real                    rlist,
                                      const PairlistType      pairlistType,
                                      int                     ci_block,
@@ -3131,7 +3126,7 @@ static void nbnxn_make_pairlist_part(const Nbnxm::GridSet&   gridSet,
         gridi_flag_shift = getBufferFlagShift(nbl->na_ci);
         gridj_flag_shift = getBufferFlagShift(nbl->na_cj);
 
-        gridj_flag = work->buffer_flags.flag;
+        gridj_flag = work->buffer_flags.data();
     }
 
     gridSet.getBox(box);
@@ -3178,7 +3173,7 @@ static void nbnxn_make_pairlist_part(const Nbnxm::GridSet&   gridSet,
         /* Check if we need periodicity shifts.
          * Without PBC or with domain decomposition we don't need them.
          */
-        if (d >= ePBC2npbcdim(gridSet.domainSetup().ePBC)
+        if (d >= numPbcDimensions(gridSet.domainSetup().pbcType)
             || gridSet.domainSetup().haveMultipleDomainsPerDim[d])
         {
             shp[d] = 0;
@@ -3544,9 +3539,12 @@ static void nbnxn_make_pairlist_part(const Nbnxm::GridSet&   gridSet,
                         }
                     }
 
-                    /* Set the exclusions for this ci list */
-                    setExclusionsForIEntry(gridSet, nbl, excludeSubDiagonal, na_cj_2log,
-                                           *getOpenIEntry(nbl), exclusions);
+                    if (!exclusions.empty())
+                    {
+                        /* Set the exclusions for this ci list */
+                        setExclusionsForIEntry(gridSet, nbl, excludeSubDiagonal, na_cj_2log,
+                                               *getOpenIEntry(nbl), exclusions);
+                    }
 
                     if (haveFep)
                     {
@@ -3562,7 +3560,7 @@ static void nbnxn_make_pairlist_part(const Nbnxm::GridSet&   gridSet,
 
         if (bFBufferFlag && getNumSimpleJClustersInList(*nbl) > ncj_old_i)
         {
-            bitmask_init_bit(&(work->buffer_flags.flag[(iGrid.cellOffset() + ci) >> gridi_flag_shift]), th);
+            bitmask_init_bit(&(work->buffer_flags[(iGrid.cellOffset() + ci) >> gridi_flag_shift]), th);
         }
     }
 
@@ -3585,20 +3583,21 @@ static void nbnxn_make_pairlist_part(const Nbnxm::GridSet&   gridSet,
 
 static void reduce_buffer_flags(gmx::ArrayRef<PairsearchWork> searchWork,
                                 int                           nsrc,
-                                const nbnxn_buffer_flags_t*   dest)
+                                gmx::ArrayRef<gmx_bitmask_t>  dest)
 {
     for (int s = 0; s < nsrc; s++)
     {
-        gmx_bitmask_t* flag = searchWork[s].buffer_flags.flag;
+        gmx::ArrayRef<gmx_bitmask_t> flags(searchWork[s].buffer_flags);
 
-        for (int b = 0; b < dest->nflag; b++)
+        for (size_t b = 0; b < dest.size(); b++)
         {
-            bitmask_union(&(dest->flag[b]), flag[b]);
+            gmx_bitmask_t& flag = dest[b];
+            bitmask_union(&flag, flags[b]);
         }
     }
 }
 
-static void print_reduction_cost(const nbnxn_buffer_flags_t* flags, int nout)
+static void print_reduction_cost(gmx::ArrayRef<const gmx_bitmask_t> flags, int nout)
 {
     int           nelem, nkeep, ncopy, nred, out;
     gmx_bitmask_t mask_0;
@@ -3608,20 +3607,20 @@ static void print_reduction_cost(const nbnxn_buffer_flags_t* flags, int nout)
     ncopy = 0;
     nred  = 0;
     bitmask_init_bit(&mask_0, 0);
-    for (int b = 0; b < flags->nflag; b++)
+    for (const gmx_bitmask_t& flag_mask : flags)
     {
-        if (bitmask_is_equal(flags->flag[b], mask_0))
+        if (bitmask_is_equal(flag_mask, mask_0))
         {
             /* Only flag 0 is set, no copy of reduction required */
             nelem++;
             nkeep++;
         }
-        else if (!bitmask_is_zero(flags->flag[b]))
+        else if (!bitmask_is_zero(flag_mask))
         {
             int c = 0;
             for (out = 0; out < nout; out++)
             {
-                if (bitmask_is_set(flags->flag[b], out))
+                if (bitmask_is_set(flag_mask, out))
                 {
                     c++;
                 }
@@ -3637,12 +3636,10 @@ static void print_reduction_cost(const nbnxn_buffer_flags_t* flags, int nout)
             }
         }
     }
-
+    const auto numFlags = static_cast<double>(flags.size());
     fprintf(debug,
-            "nbnxn reduction: #flag %d #list %d elem %4.2f, keep %4.2f copy %4.2f red %4.2f\n",
-            flags->nflag, nout, nelem / static_cast<double>(flags->nflag),
-            nkeep / static_cast<double>(flags->nflag), ncopy / static_cast<double>(flags->nflag),
-            nred / static_cast<double>(flags->nflag));
+            "nbnxn reduction: #flag %zu #list %d elem %4.2f, keep %4.2f copy %4.2f red %4.2f\n",
+            flags.size(), nout, nelem / numFlags, nkeep / numFlags, ncopy / numFlags, nred / numFlags);
 }
 
 /* Copies the list entries from src to dest when cjStart <= *cjGlobal < cjEnd.
@@ -3742,7 +3739,7 @@ static void rebalanceSimpleLists(gmx::ArrayRef<const NbnxnPairlistCpu> srcSet,
         /* Note that the flags in the work struct (still) contain flags
          * for all entries that are present in srcSet->nbl[t].
          */
-        gmx_bitmask_t* flag = searchWork[t].buffer_flags.flag;
+        gmx_bitmask_t* flag = &searchWork[t].buffer_flags[0];
 
         int iFlagShift = getBufferFlagShift(dest.na_ci);
         int jFlagShift = getBufferFlagShift(dest.na_cj);
@@ -3897,7 +3894,7 @@ static Range<int> getIZoneRange(const Nbnxm::GridSet::DomainSetup& domainSetup,
 }
 
 /* Returns the j-zone range for pairlist construction for the give locality and i-zone */
-static Range<int> getJZoneRange(const gmx_domdec_zones_t& ddZones,
+static Range<int> getJZoneRange(const gmx_domdec_zones_t* ddZones,
                                 const InteractionLocality locality,
                                 const int                 iZone)
 {
@@ -3909,12 +3906,12 @@ static Range<int> getJZoneRange(const gmx_domdec_zones_t& ddZones,
     else if (iZone == 0)
     {
         /* Non-local: we need to avoid the local (zone 0 vs 0) interactions */
-        return { 1, *ddZones.iZones[iZone].jZoneRange.end() };
+        return { 1, *ddZones->iZones[iZone].jZoneRange.end() };
     }
     else
     {
         /* Non-local with non-local i-zone: use all j-zones */
-        return ddZones.iZones[iZone].jZoneRange;
+        return ddZones->iZones[iZone].jZoneRange;
     }
 }
 
@@ -3924,7 +3921,7 @@ static void prepareListsForDynamicPruning(gmx::ArrayRef<NbnxnPairlistCpu> lists)
 void PairlistSet::constructPairlists(const Nbnxm::GridSet&         gridSet,
                                      gmx::ArrayRef<PairsearchWork> searchWork,
                                      nbnxn_atomdata_t*             nbat,
-                                     const t_blocka*               excl,
+                                     const ListOfLists<int>&       exclusions,
                                      const int                     minimumIlistCountForGpuBalancing,
                                      t_nrnb*                       nrnb,
                                      SearchCycleCounting*          searchCycleCounting)
@@ -3948,7 +3945,7 @@ void PairlistSet::constructPairlists(const Nbnxm::GridSet&         gridSet,
     /* We should re-init the flags before making the first list */
     if (nbat->bUseBufferFlags && locality_ == InteractionLocality::Local)
     {
-        init_buffer_flags(&nbat->buffer_flags, nbat->numAtoms());
+        resizeAndZeroBufferFlags(&nbat->buffer_flags, nbat->numAtoms());
     }
 
     if (!isCpuType_ && minimumIlistCountForGpuBalancing > 0)
@@ -3980,7 +3977,9 @@ void PairlistSet::constructPairlists(const Nbnxm::GridSet&         gridSet,
         }
     }
 
-    const gmx_domdec_zones_t& ddZones = *gridSet.domainSetup().zones;
+    const gmx_domdec_zones_t* ddZones = gridSet.domainSetup().zones;
+    GMX_ASSERT(locality_ == InteractionLocality::Local || ddZones != nullptr,
+               "Nonlocal interaction locality with null ddZones.");
 
     const auto iZoneRange = getIZoneRange(gridSet.domainSetup(), locality_);
 
@@ -4006,7 +4005,7 @@ void PairlistSet::constructPairlists(const Nbnxm::GridSet&         gridSet,
             /* With GPU: generate progressively smaller lists for
              * load balancing for local only or non-local with 2 zones.
              */
-            progBal = (locality_ == InteractionLocality::Local || ddZones.n <= 2);
+            progBal = (locality_ == InteractionLocality::Local || ddZones->n <= 2);
 
 #pragma omp parallel for num_threads(numLists) schedule(static)
             for (int th = 0; th < numLists; th++)
@@ -4018,7 +4017,7 @@ void PairlistSet::constructPairlists(const Nbnxm::GridSet&         gridSet,
                      */
                     if (nbat->bUseBufferFlags && (iZone == 0 && jZone == 0))
                     {
-                        init_buffer_flags(&searchWork[th].buffer_flags, nbat->numAtoms());
+                        resizeAndZeroBufferFlags(&searchWork[th].buffer_flags, nbat->numAtoms());
                     }
 
                     if (combineLists_ && th > 0)
@@ -4037,14 +4036,14 @@ void PairlistSet::constructPairlists(const Nbnxm::GridSet&         gridSet,
                     /* Divide the i cells equally over the pairlists */
                     if (isCpuType_)
                     {
-                        nbnxn_make_pairlist_part(gridSet, iGrid, jGrid, &work, nbat, *excl, rlist,
+                        nbnxn_make_pairlist_part(gridSet, iGrid, jGrid, &work, nbat, exclusions, rlist,
                                                  params_.pairlistType, ci_block, nbat->bUseBufferFlags,
                                                  nsubpair_target, progBal, nsubpair_tot_est, th,
                                                  numLists, &cpuLists_[th], fepListPtr);
                     }
                     else
                     {
-                        nbnxn_make_pairlist_part(gridSet, iGrid, jGrid, &work, nbat, *excl, rlist,
+                        nbnxn_make_pairlist_part(gridSet, iGrid, jGrid, &work, nbat, exclusions, rlist,
                                                  params_.pairlistType, ci_block, nbat->bUseBufferFlags,
                                                  nsubpair_target, progBal, nsubpair_tot_est, th,
                                                  numLists, &gpuLists_[th], fepListPtr);
@@ -4135,7 +4134,7 @@ void PairlistSet::constructPairlists(const Nbnxm::GridSet&         gridSet,
 
     if (nbat->bUseBufferFlags)
     {
-        reduce_buffer_flags(searchWork, numLists, &nbat->buffer_flags);
+        reduce_buffer_flags(searchWork, numLists, nbat->buffer_flags);
     }
 
     if (gridSet.haveFep())
@@ -4189,7 +4188,7 @@ void PairlistSet::constructPairlists(const Nbnxm::GridSet&         gridSet,
 
         if (nbat->bUseBufferFlags)
         {
-            print_reduction_cost(&nbat->buffer_flags, numLists);
+            print_reduction_cost(nbat->buffer_flags, numLists);
         }
     }
 
@@ -4202,11 +4201,23 @@ void PairlistSet::constructPairlists(const Nbnxm::GridSet&         gridSet,
 void PairlistSets::construct(const InteractionLocality iLocality,
                              PairSearch*               pairSearch,
                              nbnxn_atomdata_t*         nbat,
-                             const t_blocka*           excl,
+                             const ListOfLists<int>&   exclusions,
                              const int64_t             step,
                              t_nrnb*                   nrnb)
 {
-    pairlistSet(iLocality).constructPairlists(pairSearch->gridSet(), pairSearch->work(), nbat, excl,
+    const auto& gridSet = pairSearch->gridSet();
+    const auto* ddZones = gridSet.domainSetup().zones;
+
+    /* The Nbnxm code can also work with more exclusions than those in i-zones only
+     * when using DD, but the equality check can catch more issues.
+     */
+    GMX_RELEASE_ASSERT(
+            exclusions.empty() || (!ddZones && exclusions.ssize() == gridSet.numRealAtomsTotal())
+                    || (ddZones && exclusions.ssize() == ddZones->cg_range[ddZones->iZones.size()]),
+            "exclusions should either be empty or the number of lists should match the number of "
+            "local i-atoms");
+
+    pairlistSet(iLocality).constructPairlists(gridSet, pairSearch->work(), nbat, exclusions,
                                               minimumIlistCountForGpuBalancing_, nrnb,
                                               &pairSearch->cycleCounting_);
 
@@ -4234,11 +4245,11 @@ void PairlistSets::construct(const InteractionLocality iLocality,
 }
 
 void nonbonded_verlet_t::constructPairlist(const InteractionLocality iLocality,
-                                           const t_blocka*           excl,
+                                           const ListOfLists<int>&   exclusions,
                                            int64_t                   step,
                                            t_nrnb*                   nrnb)
 {
-    pairlistSets_->construct(iLocality, pairSearch_.get(), nbat.get(), excl, step, nrnb);
+    pairlistSets_->construct(iLocality, pairSearch_.get(), nbat.get(), exclusions, step, nrnb);
 
     if (useGpu())
     {
index 162954c89d0fc87871208fb7e09c548d14ac5ab1..5bdfba33a83a672f98b6ef8dff6db958ab1b5683 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015, The GROMACS development team.
- * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/gpu_utils/hostallocator.h"
 #include "gromacs/math/vectypes.h"
 #include "gromacs/mdtypes/locality.h"
-#include "gromacs/mdtypes/nblist.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/defaultinitializationallocator.h"
 #include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/real.h"
 
-// This file with constants is separate from this file to be able
-// to include it during OpenCL jitting without including config.h
-#include "constants.h"
 #include "pairlistparams.h"
 
 struct NbnxnPairlistCpuWork;
 struct NbnxnPairlistGpuWork;
+struct t_nblist;
 
 
-/* Convenience type for vector with aligned memory */
+//! Convenience type for vector with aligned memory
 template<typename T>
 using AlignedVector = std::vector<T, gmx::AlignedAllocator<T>>;
 
-/* Convenience type for vector that avoids initialization at resize() */
+//! Convenience type for vector that avoids initialization at resize()
 template<typename T>
 using FastVector = std::vector<T, gmx::DefaultInitializationAllocator<T>>;
 
-/* A buffer data structure of 64 bytes
+/*! \brief Cache-line protection buffer
+ *
+ * A buffer data structure of 64 bytes
  * to be placed at the beginning and end of structs
  * to avoid cache invalidation of the real contents
  * of the struct by writes to neighboring memory.
  */
 typedef struct
 {
+    //! Unused field used to create space to protect cache lines that are in use
     int dummy[16];
 } gmx_cache_protect_t;
 
-/* This is the actual cluster-pair list j-entry.
+/*! \brief This is the actual cluster-pair list j-entry.
+ *
  * cj is the j-cluster.
  * The interaction bits in excl are indexed i-major, j-minor.
  * The cj entries are sorted such that ones with exclusions come first.
@@ -84,11 +85,15 @@ typedef struct
  */
 struct nbnxn_cj_t
 {
-    int          cj;   /* The j-cluster                    */
-    unsigned int excl; /* The exclusion (interaction) bits */
+    //! The j-cluster
+    int cj;
+    //! The exclusion (interaction) bits
+    unsigned int excl;
 };
 
-/* In nbnxn_ci_t the integer shift contains the shift in the lower 7 bits.
+/*! \brief Constants for interpreting interaction flags
+ *
+ * In nbnxn_ci_t the integer shift contains the shift in the lower 7 bits.
  * The upper bits contain information for non-bonded kernel optimization.
  * Simply calculating LJ and Coulomb for all pairs in a cluster pair is fine.
  * But three flags can be used to skip interactions, currently only for subc=0
@@ -96,66 +101,113 @@ struct nbnxn_cj_t
  * shift & NBNXN_CI_HALF_LJ(subc)    => we can skip LJ for the second half of i
  * !(shift & NBNXN_CI_DO_COUL(subc)) => we can skip Coulomb for all pairs
  */
+//! \{
 #define NBNXN_CI_SHIFT 127
 #define NBNXN_CI_DO_LJ(subc) (1 << (7 + 3 * (subc)))
 #define NBNXN_CI_HALF_LJ(subc) (1 << (8 + 3 * (subc)))
 #define NBNXN_CI_DO_COUL(subc) (1 << (9 + 3 * (subc)))
+//! \}
 
-/* Cluster-pair Interaction masks
+/*! \brief Cluster-pair Interaction masks
+ *
  * Bit i*j-cluster-size + j tells if atom i and j interact.
  */
+//! \{
 // TODO: Rename according to convention when moving into Nbnxn namespace
-/* All interaction mask is the same for all kernels */
+//! All interaction mask is the same for all kernels
 constexpr unsigned int NBNXN_INTERACTION_MASK_ALL = 0xffffffffU;
-/* 4x4 kernel diagonal mask */
+//! 4x4 kernel diagonal mask
 constexpr unsigned int NBNXN_INTERACTION_MASK_DIAG = 0x08ceU;
-/* 4x2 kernel diagonal masks */
+//! 4x2 kernel diagonal masks
+//! \{
 constexpr unsigned int NBNXN_INTERACTION_MASK_DIAG_J2_0 = 0x0002U;
 constexpr unsigned int NBNXN_INTERACTION_MASK_DIAG_J2_1 = 0x002fU;
-/* 4x8 kernel diagonal masks */
+//! \}
+//! 4x8 kernel diagonal masks
+//! \{
 constexpr unsigned int NBNXN_INTERACTION_MASK_DIAG_J8_0 = 0xf0f8fcfeU;
 constexpr unsigned int NBNXN_INTERACTION_MASK_DIAG_J8_1 = 0x0080c0e0U;
+//! \}
+//! \}
+
+/*! \brief Lower limit for square interaction distances in nonbonded kernels.
+ *
+ * For smaller values we will overflow when calculating r^-1 or r^-12, but
+ * to keep it simple we always apply the limit from the tougher r^-12 condition.
+ */
+#if GMX_DOUBLE
+// Some double precision SIMD architectures use single precision in the first
+// step, so although the double precision criterion would allow smaller rsq,
+// we need to stay in single precision with some margin for the N-R iterations.
+constexpr double c_nbnxnMinDistanceSquared = 1.0e-36;
+#else
+// The worst intermediate value we might evaluate is r^-12, which
+// means we should ensure r^2 stays above pow(GMX_FLOAT_MAX,-1.0/6.0)*1.01 (some margin)
+constexpr float c_nbnxnMinDistanceSquared = 3.82e-07F; // r > 6.2e-4
+#endif
+
 
-/* Simple pair-list i-unit */
+//! The number of clusters in a super-cluster, used for GPU
+constexpr int c_nbnxnGpuNumClusterPerSupercluster = 8;
+
+/*! \brief With GPU kernels we group cluster pairs in 4 to optimize memory usage
+ * of integers containing 32 bits.
+ */
+constexpr int c_nbnxnGpuJgroupSize = (32 / c_nbnxnGpuNumClusterPerSupercluster);
+
+/*! \internal
+ * \brief Simple pair-list i-unit
+ */
 struct nbnxn_ci_t
 {
-    int ci;           /* i-cluster             */
-    int shift;        /* Shift vector index plus possible flags, see above */
-    int cj_ind_start; /* Start index into cj   */
-    int cj_ind_end;   /* End index into cj     */
+    //! i-cluster
+    int ci;
+    //! Shift vector index plus possible flags, see above
+    int shift;
+    //! Start index into cj
+    int cj_ind_start;
+    //! End index into cj
+    int cj_ind_end;
 };
 
-/* Grouped pair-list i-unit */
-typedef struct
+//! Grouped pair-list i-unit
+typedef struct nbnxn_sci
 {
-    /* Returns the number of j-cluster groups in this entry */
+    //! Returns the number of j-cluster groups in this entry
     int numJClusterGroups() const { return cj4_ind_end - cj4_ind_start; }
 
-    int sci;           /* i-super-cluster       */
-    int shift;         /* Shift vector index plus possible flags */
-    int cj4_ind_start; /* Start index into cj4  */
-    int cj4_ind_end;   /* End index into cj4    */
+    //! i-super-cluster
+    int sci;
+    //! Shift vector index plus possible flags
+    int shift;
+    //! Start index into cj4
+    int cj4_ind_start;
+    //! End index into cj4
+    int cj4_ind_end;
 } nbnxn_sci_t;
 
-/* Interaction data for a j-group for one warp */
+//! Interaction data for a j-group for one warp
 struct nbnxn_im_ei_t
 {
-    // The i-cluster interactions mask for 1 warp
+    //! The i-cluster interactions mask for 1 warp
     unsigned int imask = 0U;
-    // Index into the exclusion array for 1 warp, default index 0 which means no exclusions
+    //! Index into the exclusion array for 1 warp, default index 0 which means no exclusions
     int excl_ind = 0;
 };
 
+//! Four-way j-cluster lists
 typedef struct
 {
-    int           cj[c_nbnxnGpuJgroupSize];         /* The 4 j-clusters */
-    nbnxn_im_ei_t imei[c_nbnxnGpuClusterpairSplit]; /* The i-cluster mask data       for 2 warps */
+    //! The 4 j-clusters
+    int cj[c_nbnxnGpuJgroupSize];
+    //! The i-cluster mask data for 2 warps
+    nbnxn_im_ei_t imei[c_nbnxnGpuClusterpairSplit];
 } nbnxn_cj4_t;
 
-/* Struct for storing the atom-pair interaction bits for a cluster pair in a GPU pairlist */
+//! Struct for storing the atom-pair interaction bits for a cluster pair in a GPU pairlist
 struct nbnxn_excl_t
 {
-    /* Constructor, sets no exclusions, so all atom pairs interacting */
+    //! Constructor, sets no exclusions, so all atom pairs interacting
     MSVC_DIAGNOSTIC_IGNORE(26495) // pair is not being initialized!
     nbnxn_excl_t()
     {
@@ -166,32 +218,43 @@ struct nbnxn_excl_t
     }
     MSVC_DIAGNOSTIC_RESET
 
-    /* Topology exclusion interaction bits per warp */
+    //! Topology exclusion interaction bits per warp
     unsigned int pair[c_nbnxnGpuExclSize];
 };
 
-/* Cluster pairlist type for use on CPUs */
+//! Cluster pairlist type for use on CPUs
 struct NbnxnPairlistCpu
 {
     NbnxnPairlistCpu();
 
+    //! Cache protection
     gmx_cache_protect_t cp0;
 
-    int                    na_ci;   /* The number of atoms per i-cluster        */
-    int                    na_cj;   /* The number of atoms per j-cluster        */
-    real                   rlist;   /* The radius for constructing the list     */
-    FastVector<nbnxn_ci_t> ci;      /* The i-cluster list                       */
-    FastVector<nbnxn_ci_t> ciOuter; /* The outer, unpruned i-cluster list       */
-
-    FastVector<nbnxn_cj_t> cj;      /* The j-cluster list, size ncj             */
-    FastVector<nbnxn_cj_t> cjOuter; /* The outer, unpruned j-cluster list       */
-    int                    ncjInUse; /* The number of j-clusters that are used by ci entries in this list, will be <= cj.size() */
-
-    int nci_tot; /* The total number of i clusters           */
+    //! The number of atoms per i-cluster
+    int na_ci;
+    //! The number of atoms per j-cluster
+    int na_cj;
+    //! The radius for constructing the list
+    real rlist;
+    //! The i-cluster list
+    FastVector<nbnxn_ci_t> ci;
+    //! The outer, unpruned i-cluster list
+    FastVector<nbnxn_ci_t> ciOuter;
+
+    //! The j-cluster list, size ncj
+    FastVector<nbnxn_cj_t> cj;
+    //! The outer, unpruned j-cluster list
+    FastVector<nbnxn_cj_t> cjOuter;
+    //! The number of j-clusters that are used by ci entries in this list, will be <= cj.size()
+    int ncjInUse;
+
+    //! The total number of i clusters
+    int nci_tot;
 
-    /* Working data storage for list construction */
+    //! Working data storage for list construction
     std::unique_ptr<NbnxnPairlistCpuWork> work;
 
+    //! Cache protection
     gmx_cache_protect_t cp1;
 };
 
@@ -203,18 +266,23 @@ struct NbnxnPairlistCpu
  */
 struct NbnxnPairlistGpu
 {
-    /* Constructor
+    /*! \brief Constructor
      *
      * \param[in] pinningPolicy  Sets the pinning policy for all buffers used on the GPU
      */
     NbnxnPairlistGpu(gmx::PinningPolicy pinningPolicy);
 
+    //! Cache protection
     gmx_cache_protect_t cp0;
 
-    int  na_ci; /* The number of atoms per i-cluster        */
-    int  na_cj; /* The number of atoms per j-cluster        */
-    int  na_sc; /* The number of atoms per super cluster    */
-    real rlist; /* The radius for constructing the list     */
+    //! The number of atoms per i-cluster
+    int na_ci;
+    //! The number of atoms per j-cluster
+    int na_cj;
+    //! The number of atoms per super cluster
+    int na_sc;
+    //! The radius for constructing the list
+    real rlist;
     // The i-super-cluster list, indexes into cj4;
     gmx::HostVector<nbnxn_sci_t> sci;
     // The list of 4*j-cluster groups
@@ -224,9 +292,10 @@ struct NbnxnPairlistGpu
     // The total number of i-clusters
     int nci_tot;
 
-    /* Working data storage for list construction */
+    //! Working data storage for list construction
     std::unique_ptr<NbnxnPairlistGpuWork> work;
 
+    //! Cache protection
     gmx_cache_protect_t cp1;
 };
 
index 20037e050fe60a2d1c1b567a3cc9aa9063276504..c7f72b4d9d85ad531dae49bb7dcbee095dc63b76 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+/*! \internal \file
+ *
+ * \brief
+ * Declares inline-friendly code for making 2xNN pairlists
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
 
-/* Stride of the packed x coordinate array */
+//! Stride of the packed x coordinate array
 static constexpr int c_xStride2xNN = (GMX_SIMD_REAL_WIDTH >= 2 * c_nbnxnCpuIClusterSize)
                                              ? GMX_SIMD_REAL_WIDTH / 2
                                              : c_nbnxnCpuIClusterSize;
 
-/* Copies PBC shifted i-cell packed atom coordinates to working array */
+//! Copies PBC shifted i-cell packed atom coordinates to working array
 static inline void icell_set_x_simd_2xnn(int  ci,
                                          real shx,
                                          real shy,
@@ -66,7 +76,7 @@ static inline void icell_set_x_simd_2xnn(int  ci,
           loadU1DualHsimd(x + ia + 2 * c_xStride2xNN + 2) + SimdReal(shz));
 }
 
-/* SIMD code for checking and adding cluster-pairs to the list using coordinates in packed format.
+/*! \brief SIMD code for checking and adding cluster-pairs to the list using coordinates in packed format.
  *
  * Checks bouding box distances and possibly atom pair distances.
  * This is an accelerated version of make_cluster_list_simple.
index 1cadfff9327d498f722e74ff7a31cd4b6de815d7..ce8cf3f28f446204653cefa837bbaf5e7f8e8a96 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * the research papers on the package. Check out http://www.gromacs.org.
  */
 
-/* Stride of the packed x coordinate array */
+/*! \internal \file
+ *
+ * \brief
+ * Declares inline-friendly code for making 4xN pairlists
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+//! Stride of the packed x coordinate array
 static constexpr int c_xStride4xN =
         (GMX_SIMD_REAL_WIDTH > c_nbnxnCpuIClusterSize ? GMX_SIMD_REAL_WIDTH : c_nbnxnCpuIClusterSize);
 
-/* Copies PBC shifted i-cell packed atom coordinates to working array */
+//! Copies PBC shifted i-cell packed atom coordinates to working array
 static inline void icell_set_x_simd_4xn(int  ci,
                                         real shx,
                                         real shy,
@@ -65,7 +75,7 @@ static inline void icell_set_x_simd_4xn(int  ci,
     store(x_ci_simd + 11 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 2 * c_xStride4xN + 3] + shz));
 }
 
-/* SIMD code for checking and adding cluster-pairs to the list using coordinates in packed format.
+/*! \brief SIMD code for checking and adding cluster-pairs to the list using coordinates in packed format.
  *
  * Checks bouding box distances and possibly atom pair distances.
  * This is an accelerated version of make_cluster_list_simple.
index c997ba8709124ef8b4b23344d2cdab4e70851593..6d0afa41cec234a0ab1e901848ac1be04803a59e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -59,6 +59,7 @@
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/interaction_const.h"
+#include "gromacs/mdtypes/multipletimestepping.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/topology.h"
@@ -152,25 +153,33 @@ void increaseNstlist(FILE*               fp,
     const char* dd_err  = "Can not increase nstlist because of domain decomposition limitations";
     char        buf[STRLEN];
 
+    /* When most of the computation, and in particular the non-bondeds is only
+     * performed every ir->mtsFactor steps due to multiple time stepping,
+     * we scale all nstlist values by this factor.
+     */
+    const int mtsFactor = gmx::nonbondedMtsFactor(*ir);
+
     if (nstlist_cmdline <= 0)
     {
-        if (ir->nstlist == 1)
+        if (ir->nstlist <= mtsFactor)
         {
-            /* The user probably set nstlist=1 for a reason,
-             * don't mess with the settings.
+            /* The user probably set nstlist<=mtsFactor for a reason,
+             * don't mess with the settings, except when < mtsFactor.
              */
+            ir->nstlist = mtsFactor;
+
             return;
         }
 
         /* With a GPU and fixed nstlist suggest tuning nstlist */
-        if (fp != nullptr && useOrEmulateGpuForNonbondeds && ir->nstlist < nstlist_try[0]
+        if (fp != nullptr && useOrEmulateGpuForNonbondeds && ir->nstlist < nstlist_try[0] * mtsFactor
             && !supportsDynamicPairlistGenerationInterval(*ir))
         {
             fprintf(fp, nstl_gpu, ir->nstlist);
         }
 
         nstlist_ind = 0;
-        while (nstlist_ind < NNSTL && ir->nstlist >= nstlist_try[nstlist_ind])
+        while (nstlist_ind < NNSTL && ir->nstlist >= nstlist_try[nstlist_ind] * mtsFactor)
         {
             nstlist_ind++;
         }
@@ -249,10 +258,10 @@ void increaseNstlist(FILE*               fp,
     VerletbufListSetup listSetup = verletbufGetSafeListSetup(listType);
 
     /* Allow rlist to make the list a given factor larger than the list
-     * would be with the reference value for nstlist (10).
+     * would be with the reference value for nstlist (10*mtsFactor).
      */
     nstlist_prev = ir->nstlist;
-    ir->nstlist  = nbnxnReferenceNstlist;
+    ir->nstlist  = nbnxnReferenceNstlist * mtsFactor;
     const real rlistWithReferenceNstlist =
             calcVerletBufferSize(*mtop, det(box), *ir, ir->nstlist, ir->nstlist - 1, -1, listSetup);
     ir->nstlist = nstlist_prev;
@@ -273,17 +282,23 @@ void increaseNstlist(FILE*               fp,
     {
         if (nstlist_cmdline <= 0)
         {
-            ir->nstlist = nstlist_try[nstlist_ind];
+            ir->nstlist = nstlist_try[nstlist_ind] * mtsFactor;
         }
 
         /* Set the pair-list buffer size in ir */
-        rlist_new = calcVerletBufferSize(*mtop, det(box), *ir, ir->nstlist, ir->nstlist - 1, -1, listSetup);
+        rlist_new = calcVerletBufferSize(*mtop, det(box), *ir, ir->nstlist, ir->nstlist - mtsFactor,
+                                         -1, listSetup);
 
         /* Does rlist fit in the box? */
-        bBox = (gmx::square(rlist_new) < max_cutoff2(ir->ePBC, box));
+        bBox = (gmx::square(rlist_new) < max_cutoff2(ir->pbcType, box));
         bDD  = TRUE;
         if (bBox && DOMAINDECOMP(cr))
         {
+            /* Currently (as of July 2020), the code in this if clause is never executed.
+             * increaseNstlist(...) is only called from prepare_verlet_scheme, which in turns
+             * gets called by the runner _before_ setting up DD. DOMAINDECOMP(cr) will therefore
+             * always be false here. See #3334.
+             */
             /* Check if rlist fits in the domain decomposition */
             if (inputrec2nboundeddim(ir) < DIM)
             {
@@ -389,7 +404,17 @@ static void setDynamicPairlistPruningParameters(const t_inputrec*          ir,
                                                 const interaction_const_t* ic,
                                                 PairlistParams*            listParams)
 {
-    listParams->lifetime = ir->nstlist - 1;
+    /* When applying multiple time stepping to the non-bonded forces,
+     * we only compute them every mtsFactor steps, so all parameters here
+     * should be a multiple of mtsFactor.
+     */
+    listParams->mtsFactor = gmx::nonbondedMtsFactor(*ir);
+
+    const int mtsFactor = listParams->mtsFactor;
+
+    GMX_RELEASE_ASSERT(ir->nstlist % mtsFactor == 0, "nstlist should be a multiple of mtsFactor");
+
+    listParams->lifetime = ir->nstlist - mtsFactor;
 
     /* When nstlistPrune was set by the user, we need to execute one loop
      * iteration to determine rlistInner.
@@ -402,9 +427,9 @@ static void setDynamicPairlistPruningParameters(const t_inputrec*          ir,
     {
         /* Dynamic pruning on the GPU is performed on the list for
          * the next step on the coordinates of the current step,
-         * so the list lifetime is nstlistPrune (not the usual nstlist-1).
+         * so the list lifetime is nstlistPrune (not the usual nstlist-mtsFactor).
          */
-        int listLifetime         = tunedNstlistPrune - (useGpuList ? 0 : 1);
+        int listLifetime         = tunedNstlistPrune - (useGpuList ? 0 : mtsFactor);
         listParams->nstlistPrune = tunedNstlistPrune;
         listParams->rlistInner   = calcVerletBufferSize(*mtop, det(box), *ir, tunedNstlistPrune,
                                                       listLifetime, -1, listSetup);
@@ -413,7 +438,7 @@ static void setDynamicPairlistPruningParameters(const t_inputrec*          ir,
          * every c_nbnxnGpuRollingListPruningInterval steps,
          * so keep nstlistPrune a multiple of the interval.
          */
-        tunedNstlistPrune += useGpuList ? c_nbnxnGpuRollingListPruningInterval : 1;
+        tunedNstlistPrune += (useGpuList ? c_nbnxnGpuRollingListPruningInterval : 1) * mtsFactor;
     } while (!userSetNstlistPrune && tunedNstlistPrune < ir->nstlist
              && listParams->rlistInner == interactionCutoff);
 
index 26f6fec3f9b210be259a9fd043f2a53a32e34ffb..32f2299a3271da8d4b909533dae64ccc8f9d1cf3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -60,6 +60,7 @@ PairlistParams::PairlistParams(const Nbnxm::KernelType kernelType,
     rlistInner(rlist),
     haveMultipleDomains(haveMultipleDomains),
     useDynamicPruning(false),
+    mtsFactor(1),
     nstlistPrune(-1),
     numRollingPruningParts(1),
     lifetime(-1)
index 01755808f581f226155c711a02105db738c9b176..9eefac4a9d656e873ccf9f3fdce68f05d5443a3b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -60,7 +60,7 @@ enum class KernelType;
 static constexpr int c_nbnxnCpuIClusterSize = 4;
 
 //! The i- and j-cluster size for GPU lists, 8 atoms for CUDA, set at compile time for OpenCL
-#if GMX_GPU == GMX_GPU_OPENCL
+#if GMX_GPU_OPENCL
 static constexpr int c_nbnxnGpuClusterSize = GMX_OPENCL_NB_CLUSTER_SIZE;
 #else
 static constexpr int c_nbnxnGpuClusterSize = 8;
@@ -118,15 +118,26 @@ struct PairlistParams
      */
     PairlistParams(Nbnxm::KernelType kernelType, bool haveFep, real rlist, bool haveMultipleDomains);
 
-    PairlistType pairlistType;           //!< The type of cluster-pair list
-    bool         haveFep;                //!< Tells whether we have perturbed interactions
-    real         rlistOuter;             //!< Cut-off of the larger, outer pair-list
-    real         rlistInner;             //!< Cut-off of the smaller, inner pair-list
-    bool         haveMultipleDomains;    //!< True when using DD with multiple domains
-    bool         useDynamicPruning;      //!< Are we using dynamic pair-list pruning
-    int          nstlistPrune;           //!< Pair-list dynamic pruning interval
-    int          numRollingPruningParts; //!< The number parts to divide the pair-list into for rolling pruning, a value of 1 gives no rolling pruning
-    int          lifetime;               //!< Lifetime in steps of the pair-list
+    //! The type of cluster-pair list
+    PairlistType pairlistType;
+    //! Tells whether we have perturbed interactions
+    bool haveFep;
+    //! Cut-off of the larger, outer pair-list
+    real rlistOuter;
+    //! Cut-off of the smaller, inner pair-list
+    real rlistInner;
+    //! True when using DD with multiple domains
+    bool haveMultipleDomains;
+    //! Are we using dynamic pair-list pruning
+    bool useDynamicPruning;
+    //! The interval in steps for computing non-bonded interactions, =1 without MTS
+    int mtsFactor;
+    //! Pair-list dynamic pruning interval
+    int nstlistPrune;
+    //! The number parts to divide the pair-list into for rolling pruning, a value of 1 gives no rolling pruning
+    int numRollingPruningParts;
+    //! Lifetime in steps of the pair-list
+    int lifetime;
 };
 
 #endif
index 65216be66abb7721a2a2f0f11e75eb7eed348a69..82c344b191f6aa73340d896bba5d4857f4fb9aa4 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,6 +46,8 @@
 
 #include "pairlistset.h"
 
+#include "gromacs/mdtypes/nblist.h"
+
 #include "pairlistwork.h"
 
 PairlistSet::~PairlistSet() = default;
index d0e930330c54202269dabe7b4878d6dc9332f38c..3728dc9863a83f750efff75d0c76e8bdb51817ad 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -62,9 +63,14 @@ struct nbnxn_atomdata_t;
 struct PairlistParams;
 struct PairsearchWork;
 struct SearchCycleCounting;
-struct t_blocka;
 struct t_nrnb;
 
+namespace gmx
+{
+template<typename>
+class ListOfLists;
+}
+
 namespace Nbnxm
 {
 class GridSet;
@@ -85,7 +91,7 @@ public:
     void constructPairlists(const Nbnxm::GridSet&         gridSet,
                             gmx::ArrayRef<PairsearchWork> searchWork,
                             nbnxn_atomdata_t*             nbat,
-                            const t_blocka*               excl,
+                            const gmx::ListOfLists<int>&  exclusions,
                             int                           minimumIlistCountForGpuBalancing,
                             t_nrnb*                       nrnb,
                             SearchCycleCounting*          searchCycleCounting);
index 2349159a1dd328df5537ebe8166a395f0490eff9..0119680c72581d53faf78fc73523264b1a4b473c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -57,24 +58,30 @@ struct nbnxn_atomdata_t;
 class PairlistSet;
 enum class PairlistType;
 class PairSearch;
-struct t_blocka;
 struct t_nrnb;
 
+namespace gmx
+{
+template<typename>
+class ListOfLists;
+}
 
+//! Contains sets of pairlists \internal
 class PairlistSets
 {
 public:
+    //! Constructor
     PairlistSets(const PairlistParams& pairlistParams,
                  bool                  haveMultipleDomains,
                  int                   minimumIlistCountForGpuBalancing);
 
     //! Construct the pairlist set for the given locality
-    void construct(gmx::InteractionLocality iLocality,
-                   PairSearch*              pairSearch,
-                   nbnxn_atomdata_t*        nbat,
-                   const t_blocka*          excl,
-                   int64_t                  step,
-                   t_nrnb*                  nrnb);
+    void construct(gmx::InteractionLocality     iLocality,
+                   PairSearch*                  pairSearch,
+                   nbnxn_atomdata_t*            nbat,
+                   const gmx::ListOfLists<int>& exclusions,
+                   int64_t                      step,
+                   t_nrnb*                      nrnb);
 
     //! Dispatches the dynamic pruning kernel for the given locality
     void dispatchPruneKernel(gmx::InteractionLocality iLocality,
@@ -102,7 +109,8 @@ public:
         const int age = numStepsWithPairlist(step);
 
         return (params_.useDynamicPruning && age > 0 && age < params_.lifetime
-                && (params_.haveMultipleDomains || age % 2 == 0));
+                && step % params_.mtsFactor == 0
+                && (params_.haveMultipleDomains || age % (2 * params_.mtsFactor) == 0));
     }
 
     //! Changes the pair-list outer and inner radius
index 3f6aec35165694c1f386198bb78f9f0e82fbcb64..3fce22eab74e0aa0eb30dbc65c8f39056f42f804 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "grid.h"
 #include "pairlist.h"
 
-/* Working data for the actual i-supercell during pair search */
+//! Working data for the actual i-supercell during pair search \internal
 struct NbnxnPairlistCpuWork
 {
-    // Struct for storing coordinats and bounding box for an i-entry during search
+    //! Struct for storing coordinats and bounding box for an i-entry during search \internal
     struct IClusterData
     {
         IClusterData() :
@@ -67,30 +67,30 @@ struct NbnxnPairlistCpuWork
         {
         }
 
-        // The bounding boxes, pbc shifted, for each cluster
+        //! The bounding boxes, pbc shifted, for each cluster
         AlignedVector<Nbnxm::BoundingBox> bb;
-        // The coordinates, pbc shifted, for each atom
+        //! The coordinates, pbc shifted, for each atom
         std::vector<real> x;
-        // Aligned list for storing 4*DIM*GMX_SIMD_REAL_WIDTH reals
+        //! Aligned list for storing 4*DIM*GMX_SIMD_REAL_WIDTH reals
         AlignedVector<real> xSimd;
     };
 
-    // Protect data from cache pollution between threads
+    //! Protect data from cache pollution between threads
     gmx_cache_protect_t cp0;
 
-    // Work data for generating an IEntry in the pairlist
+    //! Work data for generating an IEntry in the pairlist
     IClusterData iClusterData;
-    // The current cj_ind index for the current list
+    //! The current cj_ind index for the current list
     int cj_ind;
-    // Temporary j-cluster list, used for sorting on exclusions
+    //! Temporary j-cluster list, used for sorting on exclusions
     std::vector<nbnxn_cj_t> cj;
 
-    // Nr. of cluster pairs without Coulomb for flop counting
+    //! Nr. of cluster pairs without Coulomb for flop counting
     int ncj_noq;
-    // Nr. of cluster pairs with 1/2 LJ for flop count
+    //! Nr. of cluster pairs with 1/2 LJ for flop count
     int ncj_hlj;
 
-    // Protect data from cache pollution between threads
+    //! Protect data from cache pollution between threads
     gmx_cache_protect_t cp1;
 };
 
@@ -109,13 +109,13 @@ struct NbnxnPairlistGpuWork
         {
         }
 
-        // The bounding boxes, pbc shifted, for each cluster
+        //! The bounding boxes, pbc shifted, for each cluster
         AlignedVector<Nbnxm::BoundingBox> bb;
-        // As bb, but in packed xxxx format
+        //! As bb, but in packed xxxx format
         AlignedVector<float> bbPacked;
-        // The coordinates, pbc shifted, for each atom
+        //! The coordinates, pbc shifted, for each atom
         AlignedVector<real> x;
-        // Aligned coordinate list used for 4*DIM*GMX_SIMD_REAL_WIDTH floats
+        //! Aligned coordinate list used for 4*DIM*GMX_SIMD_REAL_WIDTH floats
         AlignedVector<real> xSimd;
     };
 
@@ -125,23 +125,23 @@ struct NbnxnPairlistGpuWork
     {
     }
 
-    // Protect data from cache pollution between threads
+    //! Protect data from cache pollution between threads
     gmx_cache_protect_t cp0;
 
-    // Work data for generating an i-entry in the pairlist
+    //! Work data for generating an i-entry in the pairlist
     ISuperClusterData iSuperClusterData;
-    // The current j-cluster index for the current list
+    //! The current j-cluster index for the current list
     int cj_ind;
-    // Bounding box distance work array
+    //! Bounding box distance work array
     AlignedVector<float> distanceBuffer;
 
-    // Buffer for sorting list entries
+    //! Buffer for sorting list entries
     std::vector<int> sortBuffer;
 
-    // Second sci array, for sorting
+    //! Second sci array, for sorting
     gmx::HostVector<nbnxn_sci_t> sci_sort;
 
-    // Protect data from cache pollution between threads
+    //! Protect data from cache pollution between threads
     gmx_cache_protect_t cp1;
 };
 
index fee162cbdcddf3e4788a41069554c06f88886244..be981232e2182344757d52db878a3f0da8920e0b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,6 +45,7 @@
 
 #include "pairsearch.h"
 
+#include "gromacs/mdtypes/nblist.h"
 #include "gromacs/utility/smalloc.h"
 
 #include "pairlist.h"
@@ -84,12 +85,7 @@ static void free_nblist(t_nblist* nl)
 
 #ifndef DOXYGEN
 
-PairsearchWork::PairsearchWork() :
-    cp0({ { 0 } }),
-    buffer_flags({ 0, nullptr, 0 }),
-    ndistc(0),
-    nbl_fep(new t_nblist),
-    cp1({ { 0 } })
+PairsearchWork::PairsearchWork() : cp0({ { 0 } }), ndistc(0), nbl_fep(new t_nblist), cp1({ { 0 } })
 {
     nbnxn_init_pairlist_fep(nbl_fep.get());
 }
@@ -98,12 +94,10 @@ PairsearchWork::PairsearchWork() :
 
 PairsearchWork::~PairsearchWork()
 {
-    sfree(buffer_flags.flag);
-
     free_nblist(nbl_fep.get());
 }
 
-PairSearch::PairSearch(const int                 ePBC,
+PairSearch::PairSearch(const PbcType             pbcType,
                        const bool                doTestParticleInsertion,
                        const ivec*               numDDCells,
                        const gmx_domdec_zones_t* ddZones,
@@ -111,7 +105,7 @@ PairSearch::PairSearch(const int                 ePBC,
                        const bool                haveFep,
                        const int                 maxNumThreads,
                        gmx::PinningPolicy        pinningPolicy) :
-    gridSet_(ePBC, doTestParticleInsertion, numDDCells, ddZones, pairlistType, haveFep, maxNumThreads, pinningPolicy),
+    gridSet_(pbcType, doTestParticleInsertion, numDDCells, ddZones, pairlistType, haveFep, maxNumThreads, pinningPolicy),
     work_(maxNumThreads)
 {
     cycleCounting_.recordCycles_ = (getenv("GMX_NBNXN_CYCLE") != nullptr);
index 668f8c809322a59d30c0c81ec6744a5c79065c28..bd5529dbd516b332a01abe63b8b9e7797f9137e2 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/domdec/domdec.h"
 #include "gromacs/math/vectypes.h"
+#include "gromacs/nbnxm/atomdata.h"
 #include "gromacs/timing/cyclecounter.h"
 #include "gromacs/utility/alignedallocator.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/real.h"
 
-#include "atomdata.h"
 #include "gridset.h"
 #include "pairlist.h"
 
@@ -73,20 +74,22 @@ template<class T>
 using AlignedVector = std::vector<T, gmx::AlignedAllocator<T>>;
 
 
-/* Local cycle count struct for profiling */
+//! Local cycle count struct for profiling \internal
 class nbnxn_cycle_t
 {
 public:
+    //! Start counting cycles
     void start() { start_ = gmx_cycles_read(); }
-
+    //! Stop counting cycles
     void stop()
     {
         cycles_ += gmx_cycles_read() - start_;
         count_++;
     }
-
+    //! Return the number of periods of cycle counting
     int count() const { return count_; }
 
+    //! Return the average number of million cycles per counting period
     double averageMCycles() const
     {
         if (count_ > 0)
@@ -100,9 +103,12 @@ public:
     }
 
 private:
-    int          count_  = 0;
+    //! Number of counting periods
+    int count_ = 0;
+    //! Total cycles in all counting periods
     gmx_cycles_t cycles_ = 0;
-    gmx_cycles_t start_  = 0;
+    //! Cycle count at the most recent start
+    gmx_cycles_t start_ = 0;
 };
 
 //! Local cycle count enum for profiling different parts of search
@@ -138,30 +144,37 @@ struct SearchCycleCounting
 
 // TODO: Move nbnxn_search_work_t definition to its own file
 
-/* Thread-local work struct, contains working data for Grid */
+//! Thread-local work struct, contains working data for Grid \internal
 struct PairsearchWork
 {
     PairsearchWork();
 
     ~PairsearchWork();
 
-    gmx_cache_protect_t cp0; /* Buffer to avoid cache polution */
+    //! Buffer to avoid cache polution
+    gmx_cache_protect_t cp0;
 
-    std::vector<int> sortBuffer; /* Temporary buffer for sorting atoms within a grid column */
+    //! Temporary buffer for sorting atoms within a grid column
+    std::vector<int> sortBuffer;
 
-    nbnxn_buffer_flags_t buffer_flags; /* Flags for force buffer access */
+    //! Flags for force buffer access
+    std::vector<gmx_bitmask_t> buffer_flags;
 
-    int ndistc; /* Number of distance checks for flop counting */
+    //! Number of distance checks for flop counting
+    int ndistc;
 
 
-    std::unique_ptr<t_nblist> nbl_fep; /* Temporary FEP list for load balancing */
+    //! Temporary FEP list for load balancing
+    std::unique_ptr<t_nblist> nbl_fep;
 
-    nbnxn_cycle_t cycleCounter; /* Counter for thread-local cycles */
+    //! Counter for thread-local cycles
+    nbnxn_cycle_t cycleCounter;
 
-    gmx_cache_protect_t cp1; /* Buffer to avoid cache polution */
+    //! Buffer to avoid cache polution
+    gmx_cache_protect_t cp1;
 };
 
-/* Main pair-search struct, contains the grid(s), not the pair-list(s) */
+//! Main pair-search struct, contains the grid(s), not the pair-list(s) \internal
 class PairSearch
 {
 public:
@@ -187,21 +200,24 @@ public:
         cycleCounting_.stop(enbsCCgrid);
     }
 
-    /* \brief Constructor
+    /*! \brief Constructor
      *
-     * \param[in] ePBC            The periodic boundary conditions
-     * \param[in] numDDCells      The number of domain decomposition cells per dimension, without DD nullptr should be passed
-     * \param[in] zones           The domain decomposition zone setup, without DD nullptr should be passed
-     * \param[in] haveFep         Tells whether non-bonded interactions are perturbed
-     * \param[in] maxNumThreads   The maximum number of threads used in the search
+     * \param[in] pbcType                  The periodic boundary conditions
+     * \param[in] doTestParticleInsertion  Whether test-particle insertion is active
+     * \param[in] numDDCells               The number of domain decomposition cells per dimension, without DD nullptr should be passed
+     * \param[in] zones                    The domain decomposition zone setup, without DD nullptr should be passed
+     * \param[in] pairlistType             The type of tte pair list
+     * \param[in] haveFep                  Tells whether non-bonded interactions are perturbed
+     * \param[in] maxNumThreads            The maximum number of threads used in the search
+     * \param[in] pinningPolicy            Sets the pinning policy for all buffers used on the GPU
      */
-    PairSearch(int                       ePBC,
+    PairSearch(PbcType                   pbcType,
                bool                      doTestParticleInsertion,
                const ivec*               numDDCells,
                const gmx_domdec_zones_t* zones,
                PairlistType              pairlistType,
                bool                      haveFep,
-               int                       maxNumthreads,
+               int                       maxNumThreads,
                gmx::PinningPolicy        pinningPolicy);
 
     //! Sets the order of the local atoms to the order grid atom ordering
index 141b1ea9034d131224754efdc14a522a2c938e38..1ad8ef347a39dfe8fba74f2b7e9e307f6a42ab0c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -41,6 +41,8 @@
 #include "gromacs/utility/gmxassert.h"
 
 #include "clusterdistancekerneltype.h"
+#include "nbnxm_gpu.h"
+#include "nbnxm_simd.h"
 #include "pairlistset.h"
 #include "pairlistsets.h"
 #include "kernels_reference/kernel_ref_prune.h"
@@ -71,12 +73,16 @@ void PairlistSet::dispatchPruneKernel(const nbnxn_atomdata_t* nbat, const rvec*
 
         switch (getClusterDistanceKernelType(params_.pairlistType, *nbat))
         {
+#ifdef GMX_NBNXN_SIMD_4XN
             case ClusterDistanceKernelType::CpuSimd_4xM:
                 nbnxn_kernel_prune_4xn(nbl, nbat, shift_vec, rlistInner);
                 break;
+#endif
+#ifdef GMX_NBNXN_SIMD_2XNN
             case ClusterDistanceKernelType::CpuSimd_2xMM:
                 nbnxn_kernel_prune_2xnn(nbl, nbat, shift_vec, rlistInner);
                 break;
+#endif
             case ClusterDistanceKernelType::CpuPlainC:
                 nbnxn_kernel_prune_ref(nbl, nbat, shift_vec, rlistInner);
                 break;
@@ -95,7 +101,8 @@ void nonbonded_verlet_t::dispatchPruneKernelGpu(int64_t step)
     wallcycle_start_nocount(wcycle_, ewcLAUNCH_GPU);
     wallcycle_sub_start_nocount(wcycle_, ewcsLAUNCH_GPU_NONBONDED);
 
-    const bool stepIsEven = (pairlistSets().numStepsWithPairlist(step) % 2 == 0);
+    const bool stepIsEven =
+            (pairlistSets().numStepsWithPairlist(step) % (2 * pairlistSets().params().mtsFactor) == 0);
 
     Nbnxm::gpu_launch_kernel_pruneonly(
             gpu_nbv, stepIsEven ? gmx::InteractionLocality::Local : gmx::InteractionLocality::NonLocal,
index f348952c9952a595f75eac3fdec2c118bd9127ab..56062527346af2fbeacf9858cf9d6d72f9773a26 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 45b5054e9b8e5728ec4e5e4c98ddcc573350be03..ffd13506cce407d41c3832d3853bcac98c54b87a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 3b022a17436a59429e54ae47420a7b2a1201209f..f89611ab9c861b02d7cc30e4d840188a971c3a35 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 41dfd64f0473023bfa45cf12f68d9142701edfde..ad53c3b6ddb9fa2528a29c0bc9dd616a86073494 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2014,2015,2016,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2014,2015,2016,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -36,7 +36,9 @@ gmx_add_unit_test_library(onlinehelp-test-shared
                           mock_helptopic.cpp)
 
 gmx_add_unit_test(OnlineHelpUnitTests onlinehelp-test
-                  helpformat.cpp
-                  helpmanager.cpp
-                  helpwritercontext.cpp)
+    CPP_SOURCE_FILES
+        helpformat.cpp
+        helpmanager.cpp
+        helpwritercontext.cpp
+        )
 target_link_libraries(onlinehelp-test PRIVATE onlinehelp-test-shared)
index ef9ca04bead5f5234a01ed95540aaf2f06cf3cd8..65f601133da25c1dff829820d74167a4920b0ad3 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index eba2f1abf6f0313fa40df9b8b10c756c3b88dac7..bab07ea99ce1572e864de0a957fcaf52ef7dcb74 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,6 +50,7 @@
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
 
 #include "testutils/stringtest.h"
 
@@ -65,7 +66,9 @@ class HelpWriterContextTest : public gmx::test::StringTestBase
 public:
     void testFormatting(const std::string& text, gmx::HelpOutputFormat format, const char* id)
     {
-        gmx::HelpWriterContext context(nullptr, format);
+        FILE*                  fp = nullptr;
+        gmx::TextWriter        writerStub(fp);
+        gmx::HelpWriterContext context(&writerStub, format);
         std::string            result = context.substituteMarkupAndWrapToString(settings_, text);
         if (id == nullptr)
         {
index b8c64fd6e1d974f7ea7a9adda555a7c7e1190763..ef5bc379cd4b0d6a66bc7e100947a01e0a20e413 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index ef5af6be30dea02f38df796ebbb3440348c7156d..d67ef3fa03bc27b18aec9f14624ab06734f56b92 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2012,2013,2014,2015,2016,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+# Copyright (c) 2016,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index d82551aa8900b7bc842aba3d193cc7e9eabfe506..f6dd415181c8099a6673806b82124906e8c46c44 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 75d8e2d4549972d01e8c5d0143b75d78bd3debca..65e489568048e64fe1e57bfafef1b514dd2b5159 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -54,6 +54,7 @@
 #include "gromacs/options/abstractoption.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/gmxassert.h"
 
 #include "ivaluestore.h"
@@ -487,35 +488,33 @@ AbstractOptionStorage* createEnumOptionStorage(const AbstractOption& option,
 //! \{
 
 /*! \brief
- * Specifies an option that accepts enumerated string values and writes the
- * selected index into an `enum` variable.
- *
- * \tparam EnumType  Type of the variable that receives the values
- *     (can also be `int`).
- *
- * Examples:
- * \code
-   enum MyEnum { eAtom, eRes, eMol };
-   using gmx::EnumOption;
-   const char * const  allowed[] = { "atom", "residue", "molecule" };
-   MyEnum       value = eAtom; // default value
-   options.addOption(EnumOption<MyEnum>("type").enumValue(allowed).store(&value));
- * \endcode
- *
- * storeCount() is not currently implemented for this option type, and
- * providing multiple default values through an array passed to store() does
- * not work consistently in all cases.
- * In the current implementation, the values of the enum type should correspond
- * to indices in the array passed to enumValue(), i.e., be consencutive
- * starting from zero.  Only values corresponding to valid indices are accepted
- * as parameters to, e.g., defaultValue().  However, other values can be used
- * as the initial value of the variable (`value` in the above example), and
- * those will be preserved if the option is not set.
- *
- * Public methods in this class do not throw.
- *
- * \inpublicapi
- */
+* Specifies an option that accepts an EnumerationArray of string values and writes the
+* selected index into an `enum` variable.
+*
+* \tparam EnumType  DataType of the variable that receives the values
+*
+* Examples:
+* \code
+  enum class MyEnum { Atom, Res, Mol, Count } : int;
+  EnumerationArray<MyEnum, const char *> myEnumNames = { "atom", "residue", "molecule" };
+  MyEnum       value = MyEnum::Atom; // default value
+  options.addOption(EnumOption<MyEnum>("type").enumValue(myEnumNames).store(&value));
+* \endcode
+*
+* storeCount() is not currently implemented for this option type, and
+* providing multiple default values through an array passed to store() does
+* not work consistently in all cases.
+* In the current implementation, the values of the enum type should correspond
+* to indices in the array passed to enumValue(), i.e., be consecutive
+* starting from zero.  Only values corresponding to valid indices are accepted
+* as parameters to, e.g., defaultValue().  However, other values can be used
+* as the initial value of the variable (`value` in the above example), and
+* those will be preserved if the option is not set.
+*
+* Public methods in this class do not throw.
+*
+* \inpublicapi
+*/
 template<typename EnumType>
 class EnumOption : public OptionTemplate<EnumType, EnumOption<EnumType>>
 {
@@ -533,6 +532,99 @@ public:
     {
     }
 
+    /*! \brief
+     * Sets the option to only accept one of a fixed set of strings.
+     *
+
+     * \param[in] values  Array of strings to accept.
+     *
+     * 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).
+     *
+     * The strings are copied once the option is created.
+     */
+    EnumOption& enumValue(const EnumerationArray<EnumType, const char*>& values)
+    {
+        GMX_ASSERT(enumValues_ == nullptr, "Multiple sets of enumerated values specified");
+        enumValues_      = values.data();
+        enumValuesCount_ = values.size();
+        return MyBase::me();
+    }
+
+private:
+    //! Helper function to convert default values for storage initialization.
+    static int convertToInt(const EnumType* defaultValue)
+    {
+        return defaultValue != nullptr ? static_cast<int>(*defaultValue) : -1;
+    }
+
+    //! Creates a EnumOptionStorage object.
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& /*managers*/) const override
+    {
+        // TODO: Implement storeCount() if necessary.
+        return internal::createEnumOptionStorage(*this, enumValues_, enumValuesCount_,
+                                                 convertToInt(MyBase::defaultValue()),
+                                                 convertToInt(MyBase::defaultValueIfSet()),
+                                                 std::make_unique<internal::EnumIndexStore<EnumType>>(
+                                                         MyBase::store(), MyBase::storeVector()));
+    }
+
+    const char* const* enumValues_;
+    int                enumValuesCount_;
+
+    /*! \brief
+     * Needed to initialize EnumOptionStorage from this class without
+     * otherwise unnecessary accessors.
+     */
+    friend class EnumOptionStorage;
+};
+
+/*! \brief
+* Specifies an option that accepts enumerated string values and writes the
+* selected index into an `enum` variable.
+*
+* \tparam EnumType  Type of the variable that receives the values
+*     (can also be `int`).
+*
+* Examples:
+* \code
+  enum MyEnum { eAtom, eRes, eMol };
+  using gmx::LegacyEnumOption;
+  const char * const  allowed[] = { "atom", "residue", "molecule" };
+  MyEnum       value = eAtom; // default value
+  options.addOption(LegacyEnumOption<MyEnum>("type").enumValue(allowed).store(&value));
+* \endcode
+*
+* Works exactly as EnumOption.
+*
+* This is legacy support for pargsToOptions and can be removed when it
+* is removed.  No new uses of it should be made.
+*
+* Public methods in this class do not throw.
+*
+* \inpublicapi
+*/
+template<typename EnumType>
+class LegacyEnumOption : public OptionTemplate<EnumType, LegacyEnumOption<EnumType>>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef EnumOptionInfo InfoType;
+
+    // This needs to be duplicated from OptionTemplate because this class
+    // is a template.
+    //! Short-hand for the base class.
+    typedef OptionTemplate<EnumType, LegacyEnumOption<EnumType>> MyBase;
+
+    //! Initializes an option with the given name.
+    explicit LegacyEnumOption(const char* name) :
+        MyBase(name),
+        enumValues_(nullptr),
+        enumValuesCount_(0)
+    {
+    }
+
     /*! \brief
      * Sets the option to only accept one of a fixed set of strings.
      *
@@ -545,7 +637,7 @@ public:
      * The strings are copied once the option is created.
      */
     template<size_t count>
-    EnumOption& enumValue(const char* const (&values)[count])
+    LegacyEnumOption& enumValue(const char* const (&values)[count])
     {
         GMX_ASSERT(enumValues_ == nullptr, "Multiple sets of enumerated values specified");
         enumValues_      = values;
@@ -564,7 +656,7 @@ public:
      *
      * \see enumValue()
      */
-    EnumOption& enumValueFromNullTerminatedArray(const char* const* values)
+    LegacyEnumOption& enumValueFromNullTerminatedArray(const char* const* values)
     {
         GMX_ASSERT(enumValues_ == nullptr, "Multiple sets of enumerated values specified");
         enumValues_      = values;
@@ -573,7 +665,7 @@ public:
     }
 
 private:
-    //! Helper function to convert default values for storate initialization.
+    //! Helper function to convert default values for storage initialization.
     static int convertToInt(const EnumType* defaultValue)
     {
         return defaultValue != nullptr ? static_cast<int>(*defaultValue) : -1;
@@ -600,9 +692,6 @@ private:
     friend class EnumOptionStorage;
 };
 
-//! Shorthand for an enumerated option that stores into an `int` variable.
-typedef EnumOption<int> EnumIntOption;
-
 /*! \brief
  * Wrapper class for accessing boolean option information.
  *
index 2577549bada8c04ecd2e997523aeeacc41b559be..acbb852be33e58d95ac6794f68ba6b2c1422acde 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f56d93005af8aa36bd271a9d90ccbed43da91f73..1aaef894be43be9ba7ee5fba25444857b096ddc6 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 9d15b56c83f70baf5023e98a347cb0785b411092..54a4d407ff064101ee7a79983e18df6ac54f4806 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -198,12 +198,12 @@ std::string FileNameOptionManager::completeFileName(const std::string& value, co
                 return processedValue;
             }
             if (bAllowMissing)
-            {
+            { // NOLINT bugprone-branch-clone
                 return value + option.defaultExtension();
             }
             else if (option.isLibraryFile())
             {
-                // TODO: Treat also library files here.
+                // TODO: Treat also library files here and remove the NOLINT.
                 return value + option.defaultExtension();
             }
             else
@@ -262,12 +262,12 @@ std::string FileNameOptionManager::completeDefaultFileName(const std::string&
             return completedName;
         }
         if (option.allowMissing())
-        {
+        { // NOLINT bugprone-branch-clone
             return realPrefix + option.defaultExtension();
         }
-        else if (option.isLibraryFile())
+        else if (option.isLibraryFile()) // NOLINT bugprone-branch-clone
         {
-            // TODO: Treat also library files here.
+            // TODO: Treat also library files here and remove the NOLINT
             return realPrefix + option.defaultExtension();
         }
         else if (option.isSet())
index c32b97d927232430a851310cf95c6458a633dcad..adc1aeac3555ec088c19c0614faaf8a7c2941dcf 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 6eb2ace8db3444c23f3474a660229cb1ab0e5fcc..575853334c8e726fb0a0511574b1e77efd38bd34 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2014,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2014,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -62,7 +62,7 @@ namespace gmx
  * The flags related to default values are confusing, consider reorganizing
  * them.
  */
-enum OptionFlag
+enum OptionFlag : uint64_t
 {
     //! %Option has been set.
     efOption_Set = 1 << 0,
index 9ca066ec2404980427b25efaa6c62944cd90084d..69dcb4c52570ab7ad0043b550107555bc7355968 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index df1ea44ff018bd0f7180f4ba41c637c07c049552..3892140566f1a3a5a994163025e5d47487e9f1e2 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 6c5f24ad876909e908fbfcbabab1bc89e00b50d4..0ec97a877cd36ced66847a4d3f98229c446c88bd 100644 (file)
@@ -554,7 +554,7 @@ void OptionStorageTemplate<T>::commitValues()
     store_->reserve(setValues_.size());
     // For bool the loop variable isn't a reference (it's its special reference type)
     // clang-format off
-    CLANG_DIAGNOSTIC_IGNORE(-Wrange-loop-analysis)
+    CLANG_DIAGNOSTIC_IGNORE(-Wrange-loop-analysis);
     // clang-format on
     for (const auto& value : setValues_)
     {
index 498dcf5cd0f8dd0007b1d74cec08fe48077a5e45..af5fff86cee51e574ee16c0bf161b3856b34896b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2012,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2012,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d6ac5a5474b4e5b11d7ecf6f28c8f880d8d06a28..5a47d077c2f10bacf02bd517bdedd3bfbaaef2d9 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2014,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e99c2a97750dff81e3e373d85cc5b80eeda24715..398ffa76716367c9cbae82f16f8a3156df9f4405 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2011,2012,2014,2016, by the GROMACS development team, led by
+# Copyright (c) 2010,2011,2012,2014,2016,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(OptionsUnitTests options-test
-                  abstractoptionstorage.cpp
-                  filenameoption.cpp
-                  filenameoptionmanager.cpp
-                  option.cpp
-                  optionsassigner.cpp
-                  repeatingsection.cpp
-                  timeunitmanager.cpp
-                  treesupport.cpp
-                  )
+    CPP_SOURCE_FILES
+        abstractoptionstorage.cpp
+        filenameoption.cpp
+        filenameoptionmanager.cpp
+        option.cpp
+        optionsassigner.cpp
+        repeatingsection.cpp
+        timeunitmanager.cpp
+        treesupport.cpp
+        )
index 5f23659821fe3c733dc4ec3a74bcb7c7441ef344..bf316ec8097a56b9215c8bc483395fcdf33e45ed 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 6b411030bd9ef4bfc252dd288bca053b058fab47..c8a2afd86a4aef659ab3b64f762169cc85ac90f7 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 8ba0aa25e02cc15fe3e4e950d7c09a2e312b4a44..7aea5bc05aee2207e4ac97d5a2e777755d4691be 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 9d6d7ca1dc2282e510ee8a83e11542588ddb6c7a..80f917b5a8c1cc11d357f775e1f69cf0fc30613e 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2017, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -876,21 +876,23 @@ TEST(OptionsAssignerStringTest, HandlesEnumDefaultValueFromVector)
  */
 
 //! Enum type for EnumOption tests.
-enum TestEnum
+enum class TestEnum : int
 {
-    etestNone,
-    etestTest,
-    etestValue,
-    etestNR
+    None,
+    Test,
+    Value,
+    Count
 };
+//! Set of allowed values for enum option tests.
+const gmx::EnumerationArray<TestEnum, const char*> c_testEnumNames = { { "none", "test", "value" } };
 
 TEST(OptionsAssignerEnumTest, StoresSingleValue)
 {
     gmx::Options options;
-    TestEnum     value = etestNone;
+    TestEnum     value = TestEnum::None;
     using gmx::EnumOption;
-    ASSERT_NO_THROW(options.addOption(EnumOption<TestEnum>("p").store(&value).enumValue(c_allowed)));
-    EXPECT_EQ(etestNone, value);
+    ASSERT_NO_THROW(options.addOption(EnumOption<TestEnum>("p").store(&value).enumValue(c_testEnumNames)));
+    EXPECT_EQ(TestEnum::None, value);
 
     gmx::OptionsAssigner assigner(&options);
     EXPECT_NO_THROW(assigner.start());
@@ -900,7 +902,7 @@ TEST(OptionsAssignerEnumTest, StoresSingleValue)
     EXPECT_NO_THROW(assigner.finish());
     EXPECT_NO_THROW(options.finish());
 
-    EXPECT_EQ(etestTest, value);
+    EXPECT_EQ(TestEnum::Test, value);
 }
 
 TEST(OptionsAssignerEnumTest, StoresVectorValues)
@@ -909,7 +911,7 @@ TEST(OptionsAssignerEnumTest, StoresVectorValues)
     std::vector<TestEnum> values;
     using gmx::EnumOption;
     ASSERT_NO_THROW(options.addOption(
-            EnumOption<TestEnum>("p").storeVector(&values).multiValue().enumValue(c_allowed)));
+            EnumOption<TestEnum>("p").storeVector(&values).multiValue().enumValue(c_testEnumNames)));
     EXPECT_TRUE(values.empty());
 
     gmx::OptionsAssigner assigner(&options);
@@ -922,71 +924,71 @@ TEST(OptionsAssignerEnumTest, StoresVectorValues)
     EXPECT_NO_THROW(options.finish());
 
     ASSERT_EQ(2U, values.size());
-    EXPECT_EQ(etestTest, values[0]);
-    EXPECT_EQ(etestValue, values[1]);
+    EXPECT_EQ(TestEnum::Test, values[0]);
+    EXPECT_EQ(TestEnum::Value, values[1]);
 }
 
 TEST(OptionsAssignerEnumTest, HandlesInitialValueOutOfRange)
 {
     gmx::Options options;
-    TestEnum     value = etestNR;
+    TestEnum     value = TestEnum::Count;
     using gmx::EnumOption;
-    ASSERT_NO_THROW(options.addOption(EnumOption<TestEnum>("p").store(&value).enumValue(c_allowed)));
-    EXPECT_EQ(etestNR, value);
+    ASSERT_NO_THROW(options.addOption(EnumOption<TestEnum>("p").store(&value).enumValue(c_testEnumNames)));
+    EXPECT_EQ(TestEnum::Count, value);
 
     gmx::OptionsAssigner assigner(&options);
     EXPECT_NO_THROW(assigner.start());
     EXPECT_NO_THROW(assigner.finish());
     EXPECT_NO_THROW(options.finish());
 
-    EXPECT_EQ(etestNR, value);
+    EXPECT_EQ(TestEnum::Count, value);
 }
 
 TEST(OptionsAssignerEnumTest, HandlesEnumDefaultValue)
 {
     gmx::Options options;
-    TestEnum     value = etestNone;
+    TestEnum     value = TestEnum::None;
     using gmx::EnumOption;
     ASSERT_NO_THROW(options.addOption(
-            EnumOption<TestEnum>("p").store(&value).enumValue(c_allowed).defaultValue(etestTest)));
-    EXPECT_EQ(etestTest, value);
+            EnumOption<TestEnum>("p").store(&value).enumValue(c_testEnumNames).defaultValue(TestEnum::Test)));
+    EXPECT_EQ(TestEnum::Test, value);
 
     gmx::OptionsAssigner assigner(&options);
     EXPECT_NO_THROW(assigner.start());
     EXPECT_NO_THROW(assigner.finish());
     EXPECT_NO_THROW(options.finish());
 
-    EXPECT_EQ(etestTest, value);
+    EXPECT_EQ(TestEnum::Test, value);
 }
 
 TEST(OptionsAssignerEnumTest, HandlesEnumDefaultValueFromVariable)
 {
     gmx::Options options;
-    TestEnum     value = etestTest;
+    TestEnum     value = TestEnum::Test;
     using gmx::EnumOption;
-    ASSERT_NO_THROW(options.addOption(EnumOption<TestEnum>("p").store(&value).enumValue(c_allowed)));
-    EXPECT_EQ(etestTest, value);
+    ASSERT_NO_THROW(options.addOption(EnumOption<TestEnum>("p").store(&value).enumValue(c_testEnumNames)));
+    EXPECT_EQ(TestEnum::Test, value);
 
     gmx::OptionsAssigner assigner(&options);
     EXPECT_NO_THROW(assigner.start());
     EXPECT_NO_THROW(assigner.finish());
     EXPECT_NO_THROW(options.finish());
 
-    EXPECT_EQ(etestTest, value);
+    EXPECT_EQ(TestEnum::Test, value);
 }
 
 TEST(OptionsAssignerEnumTest, HandlesEnumDefaultValueFromVector)
 {
     gmx::Options          options;
     std::vector<TestEnum> value;
-    value.push_back(etestNone);
-    value.push_back(etestTest);
+    value.push_back(TestEnum::None);
+    value.push_back(TestEnum::Test);
     using gmx::EnumOption;
     ASSERT_NO_THROW(options.addOption(
-            EnumOption<TestEnum>("p").storeVector(&value).valueCount(2).enumValue(c_allowed)));
+            EnumOption<TestEnum>("p").storeVector(&value).valueCount(2).enumValue(c_testEnumNames)));
     ASSERT_EQ(2U, value.size());
-    EXPECT_EQ(etestNone, value[0]);
-    EXPECT_EQ(etestTest, value[1]);
+    EXPECT_EQ(TestEnum::None, value[0]);
+    EXPECT_EQ(TestEnum::Test, value[1]);
 
     gmx::OptionsAssigner assigner(&options);
     EXPECT_NO_THROW(assigner.start());
@@ -994,8 +996,8 @@ TEST(OptionsAssignerEnumTest, HandlesEnumDefaultValueFromVector)
     EXPECT_NO_THROW(options.finish());
 
     ASSERT_EQ(2U, value.size());
-    EXPECT_EQ(etestNone, value[0]);
-    EXPECT_EQ(etestTest, value[1]);
+    EXPECT_EQ(TestEnum::None, value[0]);
+    EXPECT_EQ(TestEnum::Test, value[1]);
 }
 
 } // namespace
index 5d1adaf30108898c7f49b2907e7a7c5ca1b9e0c0..7715c9142f41055fa1b69d5914bc4698c8e29635 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -60,10 +61,10 @@ namespace
 TEST(TimeUnitManagerTest, BasicOperations)
 {
     gmx::TimeUnitManager manager;
-    EXPECT_EQ(gmx::TimeUnit_ps, manager.timeUnit());
+    EXPECT_EQ(gmx::TimeUnit::Picoseconds, manager.timeUnit());
     EXPECT_DOUBLE_EQ(1.0, manager.timeScaleFactor());
-    manager.setTimeUnit(gmx::TimeUnit_ns);
-    EXPECT_EQ(gmx::TimeUnit_ns, manager.timeUnit());
+    manager.setTimeUnit(gmx::TimeUnit::Nanoseconds);
+    EXPECT_EQ(gmx::TimeUnit::Nanoseconds, manager.timeUnit());
     EXPECT_DOUBLE_EQ(1e3, manager.timeScaleFactor());
     EXPECT_DOUBLE_EQ(1e-3, manager.inverseTimeScaleFactor());
 }
@@ -85,21 +86,21 @@ TEST(TimeUnitBehaviorTest, ScalesAssignedOptionValue)
     EXPECT_NO_THROW_GMX(assigner.finish());
 
     EXPECT_DOUBLE_EQ(1.5, value);
-    behavior.setTimeUnit(gmx::TimeUnit_ns);
+    behavior.setTimeUnit(gmx::TimeUnit::Nanoseconds);
     behavior.optionsFinishing(&options);
     EXPECT_DOUBLE_EQ(1500, value);
 
     EXPECT_NO_THROW_GMX(options.finish());
 
-    behavior.setTimeUnit(gmx::TimeUnit_us);
+    behavior.setTimeUnit(gmx::TimeUnit::Microseconds);
     behavior.optionsFinishing(&options);
     EXPECT_DOUBLE_EQ(1500000, value);
 
-    behavior.setTimeUnit(gmx::TimeUnit_fs);
+    behavior.setTimeUnit(gmx::TimeUnit::Femtoseconds);
     behavior.optionsFinishing(&options);
     EXPECT_DOUBLE_EQ(0.0015, value);
 
-    behavior.setTimeUnit(gmx::TimeUnit_ps);
+    behavior.setTimeUnit(gmx::TimeUnit::Picoseconds);
     behavior.optionsFinishing(&options);
     EXPECT_DOUBLE_EQ(1.5, value);
 }
@@ -123,7 +124,7 @@ TEST(TimeUnitBehaviorTest, DoesNotScaleDefaultValues)
     EXPECT_NO_THROW_GMX(options.finish());
 
     EXPECT_DOUBLE_EQ(2.5, value2);
-    behavior.setTimeUnit(gmx::TimeUnit_ns);
+    behavior.setTimeUnit(gmx::TimeUnit::Nanoseconds);
     behavior.optionsFinishing(&options);
     EXPECT_DOUBLE_EQ(1.5, value);
     EXPECT_DOUBLE_EQ(2.5, value2);
@@ -150,7 +151,7 @@ TEST(TimeUnitBehaviorTest, ScalesUserInputWithMultipleSources)
     EXPECT_NO_THROW_GMX(options.finish());
 
     EXPECT_DOUBLE_EQ(1.5, value);
-    behavior.setTimeUnit(gmx::TimeUnit_ns);
+    behavior.setTimeUnit(gmx::TimeUnit::Nanoseconds);
     behavior.optionsFinishing(&options);
     EXPECT_DOUBLE_EQ(1500, value);
 }
@@ -176,7 +177,7 @@ TEST(TimeUnitBehaviorTest, TimeUnitOptionWorks)
     EXPECT_NO_THROW_GMX(assigner.finish());
 
     EXPECT_DOUBLE_EQ(1.5, value);
-    EXPECT_EQ(gmx::TimeUnit_ns, behavior.timeUnit());
+    EXPECT_EQ(gmx::TimeUnit::Nanoseconds, behavior.timeUnit());
     behavior.optionsFinishing(&options);
     EXPECT_DOUBLE_EQ(1500, value);
 
index 853646a642cf3c0ccf808e9e1254e668295e3cc2..ef4e48026704f0e2a762f7c9946b3617f5246156 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -347,14 +347,14 @@ public:
     gmx::KeyValueTreeBuilder builder_;
 
 private:
-    std::vector<char> serializeTree(const gmx::KeyValueTreeObject& tree)
+    static std::vector<char> serializeTree(const gmx::KeyValueTreeObject& tree)
     {
         gmx::InMemorySerializer serializer;
         gmx::serializeKeyValueTree(tree, &serializer);
         return serializer.finishAndGetBuffer();
     }
 
-    std::string formatBuffer(const std::vector<char>& buffer)
+    static std::string formatBuffer(const std::vector<char>& buffer)
     {
         return gmx::formatAndJoin(buffer, " ", [](char c) {
             return gmx::formatString("%02x", static_cast<unsigned char>(c));
@@ -398,15 +398,8 @@ TEST_F(TreeValueSupportTest, SupportsDoubleOption)
     runTest();
 }
 
-TEST_F(TreeValueSupportTest, SupportsEnumIntOption)
-{
-    const char* const values[] = { "foo", "bar" };
-    options_.addOption(gmx::EnumIntOption("a").enumValue(values).defaultValue(0));
-    runTest();
-}
-
 //! Enum for testing EnumOption.
-enum class TestEnum
+enum class TestEnum : int
 {
     Foo,
     Bar
@@ -414,8 +407,14 @@ enum class TestEnum
 
 TEST_F(TreeValueSupportTest, SupportsEnumOption)
 {
-    const char* const values[] = { "foo", "bar" };
-    options_.addOption(gmx::EnumOption<TestEnum>("a").enumValue(values).defaultValue(TestEnum::Foo));
+    enum class TestEnum : int
+    {
+        Foo,
+        Bar,
+        Count
+    };
+    const gmx::EnumerationArray<TestEnum, const char*> testEnumNames = { { "foo", "bar" } };
+    options_.addOption(gmx::EnumOption<TestEnum>("a").enumValue(testEnumNames).defaultValue(TestEnum::Foo));
     runTest();
 }
 
index 8ac822a2bb8a408687280749c7c6086de374b5b0..0dc7a4f2d41621976641904656b9f5acdd8ec150 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/options/options.h"
 #include "gromacs/options/optionsvisitor.h"
 #include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
 
+namespace gmx
+{
+
 namespace
 {
 
@@ -64,45 +69,34 @@ namespace
  *
  * These must correspond to the TimeUnit enum in the header!
  */
-const char* const g_timeUnits[] = { "fs", "ps", "ns", "us", "ms", "s" };
+const EnumerationArray<TimeUnit, const char*> c_timeUnitNames = { { "fs", "ps", "ns", "us", "ms",
+                                                                    "s" } };
 /*! \brief
  * Scaling factors from each time unit to internal units (=picoseconds).
  *
  * These must correspond to the TimeUnit enum in the header!
  */
-const double g_timeScaleFactors[] = { 1e-3, 1, 1e3, 1e6, 1e9, 1e12 };
+const EnumerationArray<TimeUnit, double> c_timeUnitScaleFactors = { { 1e-3, 1, 1e3, 1e6, 1e9, 1e12 } };
 
 } // namespace
 
-namespace gmx
-{
-
-TimeUnitManager::TimeUnitManager() : timeUnit_(TimeUnit_Default) {}
+TimeUnitManager::TimeUnitManager() : timeUnit_(TimeUnit::Default) {}
 
-TimeUnitManager::TimeUnitManager(TimeUnit unit) : timeUnit_(unit)
-{
-    GMX_RELEASE_ASSERT(unit >= 0 && unit <= TimeUnit_s, "Invalid time unit");
-}
+TimeUnitManager::TimeUnitManager(TimeUnit unit) : timeUnit_(unit) {}
 
 void TimeUnitManager::setTimeUnit(TimeUnit unit)
 {
-    GMX_RELEASE_ASSERT(unit >= 0 && unit <= TimeUnit_s, "Invalid time unit");
     timeUnit_ = unit;
 }
 
 const char* TimeUnitManager::timeUnitAsString() const
 {
-    GMX_RELEASE_ASSERT(timeUnit_ >= 0 && timeUnit_ <= TimeUnit_s, "Invalid time unit");
-    return g_timeUnits[timeUnit_];
+    return c_timeUnitNames[timeUnit_];
 }
 
 double TimeUnitManager::timeScaleFactor() const
 {
-    GMX_RELEASE_ASSERT(timeUnit_ >= 0
-                               && static_cast<size_t>(timeUnit_)
-                                          < sizeof(g_timeScaleFactors) / sizeof(g_timeScaleFactors[0]),
-                       "Time unit index has become out-of-range");
-    return g_timeScaleFactors[timeUnit_];
+    return c_timeUnitScaleFactors[timeUnit_];
 }
 
 double TimeUnitManager::inverseTimeScaleFactor() const
@@ -114,11 +108,10 @@ double TimeUnitManager::inverseTimeScaleFactor() const
  * TimeUnitBehavior
  */
 
-TimeUnitBehavior::TimeUnitBehavior() : timeUnit_(TimeUnit_Default), timeUnitStore_(nullptr) {}
+TimeUnitBehavior::TimeUnitBehavior() : timeUnit_(TimeUnit::Default), timeUnitStore_(nullptr) {}
 
 void TimeUnitBehavior::setTimeUnit(TimeUnit unit)
 {
-    GMX_RELEASE_ASSERT(unit >= 0 && unit <= TimeUnit_s, "Invalid time unit");
     timeUnit_ = unit;
     if (timeUnitStore_ != nullptr)
     {
@@ -137,26 +130,32 @@ void TimeUnitBehavior::setTimeUnitFromEnvironment()
     const char* const value = std::getenv("GMXTIMEUNIT");
     if (value != nullptr)
     {
-        ArrayRef<const char* const>                 timeUnits(g_timeUnits);
-        ArrayRef<const char* const>::const_iterator i =
-                std::find(timeUnits.begin(), timeUnits.end(), std::string(value));
-        if (i == timeUnits.end())
+        TimeUnit result = TimeUnit::Count;
+        for (TimeUnit t : keysOf(c_timeUnitNames))
+        {
+            if (std::strcmp(value, c_timeUnitNames[t]) == 0)
+            {
+                result = t;
+                break;
+            }
+        }
+        if (result == TimeUnit::Count)
         {
             std::string message = formatString(
                     "Time unit provided with environment variable GMXTIMEUNIT=%s "
                     "is not recognized as a valid time unit.\n"
                     "Possible values are: %s",
-                    value, joinStrings(timeUnits, ", ").c_str());
+                    value, joinStrings(c_timeUnitNames, ", ").c_str());
             GMX_THROW(InvalidInputError(message));
         }
-        setTimeUnit(static_cast<TimeUnit>(i - timeUnits.begin()));
+        setTimeUnit(result);
     }
 }
 
 void TimeUnitBehavior::addTimeUnitOption(IOptionsContainer* options, const char* name)
 {
     options->addOption(
-            EnumOption<TimeUnit>(name).enumValue(g_timeUnits).store(&timeUnit_).description("Unit for time values"));
+            EnumOption<TimeUnit>(name).enumValue(c_timeUnitNames).store(&timeUnit_).description("Unit for time values"));
 }
 
 namespace
index 5aa9742a501a0fab982d922ff574ea7f8e7e9a5f..222771b3e28266042bdde4caeaa01611cce395b0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,9 +43,9 @@
 #ifndef GMX_OPTIONS_TIMEUNITMANAGER_H
 #define GMX_OPTIONS_TIMEUNITMANAGER_H
 
+#include "gromacs/fileio/oenv.h"
 #include "gromacs/options/ioptionsbehavior.h"
 #include "gromacs/utility/classhelpers.h"
-#include "gromacs/utility/gmxassert.h"
 
 namespace gmx
 {
@@ -53,27 +53,6 @@ namespace gmx
 class IOptionsContainer;
 class Options;
 
-/*! \brief
- * Time values for TimeUnitManager.
- *
- * \if internal
- * Currently, this should match with the time_unit_t enum defined in oenv.h
- * except that there is no NULL first item in this enum.
- * \endif
- *
- * \inpublicapi
- */
-enum TimeUnit
-{
-    TimeUnit_fs,                   //!< Femtoseconds.
-    TimeUnit_ps,                   //!< Picoseconds.
-    TimeUnit_ns,                   //!< Nanoseconds.
-    TimeUnit_us,                   //!< Microseconds.
-    TimeUnit_ms,                   //!< Milliseconds.
-    TimeUnit_s,                    //!< Seconds.
-    TimeUnit_Default = TimeUnit_ps //!< Default time unit.
-};
-
 /*! \brief
  * Provides common functionality for time unit conversions.
  *
@@ -103,12 +82,7 @@ public:
     explicit TimeUnitManager(TimeUnit unit);
 
     //! Returns the currently selected time unit.
-    TimeUnit timeUnit() const
-    {
-        GMX_ASSERT(timeUnit_ >= 0 && timeUnit_ <= TimeUnit_s,
-                   "Time unit index has become out-of-range");
-        return timeUnit_;
-    }
+    TimeUnit timeUnit() const { return timeUnit_; }
     //! Set a new time unit for the manager.
     void setTimeUnit(TimeUnit unit);
 
@@ -144,12 +118,7 @@ public:
     TimeUnitBehavior();
 
     //! Returns the current time unit.
-    TimeUnit timeUnit() const
-    {
-        GMX_ASSERT(timeUnit_ >= 0 && timeUnit_ <= TimeUnit_s,
-                   "Time unit index has become out-of-range");
-        return static_cast<TimeUnit>(timeUnit_);
-    }
+    TimeUnit timeUnit() const { return timeUnit_; }
     //! Sets the time unit.
     void setTimeUnit(TimeUnit unit);
 
index 822a1b04a19cabd1aeefbdd931c7a7c2790c8634..63a5dacfcb851d02010ba126a500d65bed456dc3 100644 (file)
@@ -37,7 +37,7 @@ set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${PBCUTIL_SOURCES} PARENT_SCOPE)
 
 if(GMX_INSTALL_LEGACY_API)
   install(FILES
-         pbc.h
+          pbc.h
           DESTINATION include/gromacs/pbcutil)
 endif()
 
diff --git a/src/gromacs/pbcutil/com.cpp b/src/gromacs/pbcutil/com.cpp
new file mode 100644 (file)
index 0000000..3aef25f
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*!\file
+ * \internal
+ * \brief
+ * Implements helper methods to place particle COM in boxes.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \ingroup module_pbcutil
+ */
+#include "gmxpre.h"
+
+#include "com.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/topology/mtop_util.h"
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/range.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+/*! \brief
+ * Calculates shift to place COM into a box.
+ *
+ * \param[in] position     Unshifted COM position.
+ * \param[in] box          The current box to place the COM in.
+ * \param[in] pbcType      What kind of box is being used.
+ * \param[in] unitCellType Type of unitcell being used.
+ * \param[in] centerType   How things should be centered.
+ * \returns The shift needed to place the COM into the box.
+ */
+RVec evaluateShiftToBox(const RVec&          position,
+                        const matrix         box,
+                        const PbcType&       pbcType,
+                        const UnitCellType&  unitCellType,
+                        const CenteringType& centerType)
+{
+    RVec newCOMPosition(position);
+    auto comArrayRef = arrayRefFromArray(&newCOMPosition, 1);
+    switch (unitCellType)
+    {
+        case (UnitCellType::Rectangular): put_atoms_in_box(pbcType, box, comArrayRef); break;
+        case (UnitCellType::Triclinic):
+            put_atoms_in_triclinic_unitcell(static_cast<int>(centerType), box, comArrayRef);
+            break;
+        case (UnitCellType::Compact):
+            put_atoms_in_compact_unitcell(pbcType, static_cast<int>(centerType), box, comArrayRef);
+            break;
+        default: GMX_RELEASE_ASSERT(false, "Unhandled type of unit cell");
+    }
+    RVec shift(0, 0, 0);
+    rvec_sub(newCOMPosition, position, shift);
+    return shift;
+}
+
+/*! \brief
+ * Calculates the COM for each collection of atoms.
+ *
+ * \param[in] x       View on coordinates of the molecule.
+ * \param[in] moltype Which molecule type to calculate for.
+ * \param[in] atomOffset If needed, point from where to count the first atom to process.
+ * \returns The center of mass for the molecule.
+ */
+RVec calculateCOM(ArrayRef<const RVec> x, const gmx_moltype_t& moltype, const int atomOffset = 0)
+{
+    RVec   com(0, 0, 0);
+    double totalMass   = 0;
+    int    currentAtom = atomOffset;
+    for (const auto& coord : x)
+    {
+        real mass = moltype.atoms.atom[currentAtom].m;
+        for (int d = 0; d < DIM; d++)
+        {
+            com[d] += mass * coord[d];
+        }
+        totalMass += mass;
+        currentAtom++;
+    }
+    svmul(1.0 / totalMass, com, com);
+    return com;
+}
+
+} // namespace
+
+void shiftAtoms(const RVec& shift, ArrayRef<RVec> x)
+{
+    std::transform(std::begin(x), std::end(x), std::begin(x), [shift](RVec x) { return x + shift; });
+}
+
+void placeCoordinatesWithCOMInBox(const PbcType&      pbcType,
+                                  const UnitCellType  unitCellType,
+                                  const CenteringType centerType,
+                                  const matrix        box,
+                                  ArrayRef<RVec>      x,
+                                  const gmx_mtop_t&   mtop,
+                                  const COMShiftType  comShiftType)
+{
+    GMX_RELEASE_ASSERT(comShiftType != COMShiftType::Count, "Using COUNT of enumeration");
+    // loop over all molecule blocks, then over all molecules in these blocks
+    int molb = 0;
+    for (const auto& molblock : mtop.molblock)
+    {
+        const MoleculeBlockIndices& ind              = mtop.moleculeBlockIndices[molb];
+        const gmx_moltype_t&        moltype          = mtop.moltype[molblock.type];
+        const int                   atomsPerMolecule = ind.numAtomsPerMolecule;
+        int                         atomStart        = ind.globalAtomStart;
+        for (int mol = 0; mol < molblock.nmol; mol++)
+        {
+            std::vector<Range<int>> atomRanges;
+            if (comShiftType == COMShiftType::Molecule)
+            {
+                atomRanges.emplace_back(gmx::Range<int>(0, atomsPerMolecule));
+            }
+            else if (comShiftType == COMShiftType::Residue)
+            {
+                atomRanges = atomRangeOfEachResidue(moltype);
+            }
+            for (const auto& atomRange : atomRanges)
+            {
+                const int      firstAtomGlobalPosition = atomStart + *atomRange.begin();
+                ArrayRef<RVec> coords = x.subArray(firstAtomGlobalPosition, atomRange.size());
+                const RVec     com    = calculateCOM(coords, moltype, *atomRange.begin());
+                const RVec shift = evaluateShiftToBox(com, box, pbcType, unitCellType, centerType);
+                shiftAtoms(shift, coords);
+            }
+            atomStart += atomsPerMolecule;
+        }
+        molb++;
+    }
+}
+
+} // namespace gmx
diff --git a/src/gromacs/pbcutil/com.h b/src/gromacs/pbcutil/com.h
new file mode 100644 (file)
index 0000000..870db8c
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Helper methods to place particle COM in boxes.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_pbcutil
+ */
+#ifndef GMX_PBCUTIL_COM_H
+#define GMX_PBCUTIL_COM_H
+
+#include <algorithm>
+
+#include "gromacs/math/vec.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/classhelpers.h"
+
+#include "pbcenums.h"
+
+struct gmx_mtop_t;
+enum class PbcType : int;
+namespace gmx
+{
+
+//! How COM shifting should be applied.
+enum class COMShiftType : int
+{
+    Residue,
+    Molecule,
+    Count
+};
+
+/*! \brief
+ * Shift all coordinates.
+ *
+ * Shift coordinates by a previously calculated value.
+ *
+ * Can be used to e.g. place particles in a box.
+ *
+ * \param[in] shift Translation that should be applied.
+ * \param[in] x Coordinates to translate.
+ */
+void shiftAtoms(const RVec& shift, ArrayRef<RVec> x);
+
+/*! \brief
+ * Moves collection of atoms along the center of mass into a box.
+ *
+ * This ensures that the centre of mass (COM) of a molecule is placed
+ * within a predefined coordinate space (usually a simulation box).
+ *
+ * \param[in]      pbcType      What kind of PBC are we handling today.
+ * \param[in]      unitCellType Kind of unitcell used for the box.
+ * \param[in]      centerType   How atoms should be centered.
+ * \param[in]      box          The currently available box to place things into.
+ * \param[in, out] x            View in coordinates to shift.
+ * \param[in]      mtop         Topology with residue and molecule information.
+ * \param[in]      comShiftType Whether residues or molecules are shifted.
+ */
+void placeCoordinatesWithCOMInBox(const PbcType&    pbcType,
+                                  UnitCellType      unitCellType,
+                                  CenteringType     centerType,
+                                  const matrix      box,
+                                  ArrayRef<RVec>    x,
+                                  const gmx_mtop_t& mtop,
+                                  COMShiftType      comShiftType);
+
+} // namespace gmx
+
+#endif
index 1941716347649a103e217aa42330601da33df2c4..99824fc17274af270eb60b82cdb891e9cd2ccdb4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,7 +49,6 @@
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/fatalerror.h"
-#include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/strconvert.h"
 #include "gromacs/utility/stringutil.h"
 
  *
  ************************************************************/
 
-static void add_gbond(t_graph* g, int a0, int a1)
+using gmx::ArrayRef;
+using gmx::IVec;
+
+// Class for generating the edges of the graph
+class EdgesGenerator
 {
-    int      i;
-    int      inda0, inda1;
-    gmx_bool bFound;
-
-    inda0  = a0 - g->at_start;
-    inda1  = a1 - g->at_start;
-    bFound = FALSE;
-    /* Search for a direct edge between a0 and a1.
-     * All egdes are bidirectional, so we only need to search one way.
-     */
-    for (i = 0; (i < g->nedge[inda0] && !bFound); i++)
-    {
-        bFound = (g->edge[inda0][i] == a1);
-    }
+public:
+    EdgesGenerator(const int endAtom) : edges_(endAtom) {}
+
+    // Adds an edge, bi-directional
+    void addEdge(int a0, int a1);
+
+    // Returns the edges
+    const std::vector<std::vector<int>>& edges() const { return edges_; }
+
+private:
+    // The edges stored as list (for each atom starting at \p startAtom_) of lists of atoms
+    std::vector<std::vector<int>> edges_;
+};
 
-    if (!bFound)
+void EdgesGenerator::addEdge(const int a0, const int a1)
+{
+    const auto& edges = edges_[a0];
+    if (std::find(edges.begin(), edges.end(), a1) == edges.end())
     {
-        g->edge[inda0][g->nedge[inda0]++] = a1;
-        g->edge[inda1][g->nedge[inda1]++] = a0;
+        edges_[a0].push_back(a1);
+        edges_[a1].push_back(a0);
     }
 }
 
@@ -95,7 +101,7 @@ static void add_gbond(t_graph* g, int a0, int a1)
  * edges are only added when atoms have a different part index.
  */
 template<typename T>
-static bool mk_igraph(t_graph* g, int ftype, const T& il, int at_start, int at_end, const int* part)
+static bool mk_igraph(EdgesGenerator* edgesG, int ftype, const T& il, int at_end, ArrayRef<const int> part)
 {
     int  i, j, np;
     int  end;
@@ -108,7 +114,7 @@ static bool mk_igraph(t_graph* g, int ftype, const T& il, int at_start, int at_e
     {
         np = interaction_function[ftype].nratoms;
 
-        if (np > 1 && il.iatoms[i + 1] >= at_start && il.iatoms[i + 1] < at_end)
+        if (np > 1 && il.iatoms[i + 1] < at_end)
         {
             if (il.iatoms[i + np] >= at_end)
             {
@@ -123,16 +129,16 @@ static bool mk_igraph(t_graph* g, int ftype, const T& il, int at_start, int at_e
             if (ftype == F_SETTLE)
             {
                 /* Bond all the atoms in the settle */
-                add_gbond(g, il.iatoms[i + 1], il.iatoms[i + 2]);
-                add_gbond(g, il.iatoms[i + 1], il.iatoms[i + 3]);
+                edgesG->addEdge(il.iatoms[i + 1], il.iatoms[i + 2]);
+                edgesG->addEdge(il.iatoms[i + 1], il.iatoms[i + 3]);
                 addedEdge = true;
             }
-            else if (part == nullptr)
+            else if (part.empty())
             {
                 /* Simply add this bond */
                 for (j = 1; j < np; j++)
                 {
-                    add_gbond(g, il.iatoms[i + j], il.iatoms[i + j + 1]);
+                    edgesG->addEdge(il.iatoms[i + j], il.iatoms[i + j + 1]);
                 }
                 addedEdge = true;
             }
@@ -143,7 +149,7 @@ static bool mk_igraph(t_graph* g, int ftype, const T& il, int at_start, int at_e
                 {
                     if (part[il.iatoms[i + j]] != part[il.iatoms[i + j + 1]])
                     {
-                        add_gbond(g, il.iatoms[i + j], il.iatoms[i + j + 1]);
+                        edgesG->addEdge(il.iatoms[i + j], il.iatoms[i + j + 1]);
                         addedEdge = true;
                     }
                 }
@@ -164,28 +170,29 @@ static bool mk_igraph(t_graph* g, int ftype, const T& il, int at_start, int at_e
     if ((g) == NULL) \
     g_error(__LINE__, __FILE__)
 
-void p_graph(FILE* log, const char* title, t_graph* g)
+void p_graph(FILE* log, const char* title, const t_graph* g)
 {
-    int         i, j;
+    int         i;
     const char* cc[egcolNR] = { "W", "G", "B" };
 
     GCHECK(g);
     fprintf(log, "graph:  %s\n", title);
-    fprintf(log, "nnodes: %d\n", g->nnodes);
-    fprintf(log, "nbound: %d\n", g->nbound);
-    fprintf(log, "start:  %d\n", g->at_start);
-    fprintf(log, "end:    %d\n", g->at_end);
+    fprintf(log, "nnodes: %d\n", g->numNodes());
+    fprintf(log, "nbound: %d\n", g->numConnectedAtoms);
+    fprintf(log, "start:  %d\n", g->edgeAtomBegin);
+    fprintf(log, "end:    %d\n", g->edgeAtomEnd);
     fprintf(log, " atom shiftx shifty shiftz C nedg    e1    e2 etc.\n");
-    for (i = 0; (i < g->nnodes); i++)
+    for (i = 0; i < int(g->edges.size()); i++)
     {
-        if (g->nedge[i] > 0)
+        if (!g->edges[i].empty())
         {
-            fprintf(log, "%5d%7d%7d%7d %1s%5d", g->at_start + i + 1, g->ishift[g->at_start + i][XX],
-                    g->ishift[g->at_start + i][YY], g->ishift[g->at_start + i][ZZ],
-                    (g->negc > 0) ? cc[g->egc[i]] : " ", g->nedge[i]);
-            for (j = 0; (j < g->nedge[i]); j++)
+            fprintf(log, "%5d%7d%7d%7d %1s%5zu", g->edgeAtomBegin + i + 1,
+                    g->ishift[g->edgeAtomBegin + i][XX], g->ishift[g->edgeAtomBegin + i][YY],
+                    g->ishift[g->edgeAtomBegin + i][ZZ],
+                    (!g->edgeColor.empty()) ? cc[g->edgeColor[i]] : " ", g->edges[i].size());
+            for (const int edge : g->edges[i])
             {
-                fprintf(log, " %5d", g->edge[i][j] + 1);
+                fprintf(log, " %5d", edge + 1);
             }
             fprintf(log, "\n");
         }
@@ -193,241 +200,134 @@ void p_graph(FILE* log, const char* title, t_graph* g)
     fflush(log);
 }
 
-template<typename T>
-static void calc_1se(t_graph* g, int ftype, const T& il, int nbond[], int at_start, int at_end)
+/* Converts the vector of vector of edges to ListOfLists
+ * and removes leading and trailing uncoupled atoms
+ */
+static gmx::ListOfLists<int> convertGraph(FILE*                 fplog,
+                                          const EdgesGenerator& edgesG,
+                                          int*                  firstConnectedAtom,
+                                          int*                  numConnectedAtoms)
 {
-    int k, nratoms, end, j;
+    gmx::ListOfLists<int> edgesLists;
 
-    end = il.size();
-
-    for (j = 0; (j < end); j += nratoms + 1)
+    *firstConnectedAtom        = edgesG.edges().size();
+    *numConnectedAtoms         = 0;
+    int numEmptyEntriesSkipped = 0;
+    int max_nedge              = 0;
+    for (const auto& edges : edgesG.edges())
     {
-        nratoms = interaction_function[ftype].nratoms;
-
-        if (ftype == F_SETTLE)
+        if (edges.empty())
         {
-            const int iaa = il.iatoms[j + 1];
-            if (iaa >= at_start && iaa < at_end)
-            {
-                nbond[iaa] += 2;
-                nbond[il.iatoms[j + 2]] += 1;
-                nbond[il.iatoms[j + 3]] += 1;
-                g->at_start = std::min(g->at_start, iaa);
-                g->at_end   = std::max(g->at_end, iaa + 2 + 1);
-            }
+            numEmptyEntriesSkipped++;
         }
         else
         {
-            for (k = 1; (k <= nratoms); k++)
+            if (edgesLists.empty())
+            {
+                /* We ignore empty entries before the first connected entry */
+                *firstConnectedAtom = numEmptyEntriesSkipped;
+            }
+            else
             {
-                const int iaa = il.iatoms[j + k];
-                if (iaa >= at_start && iaa < at_end)
+                /* Push any empty entries we skipped */
+                for (int i = 0; i < numEmptyEntriesSkipped; i++)
                 {
-                    g->at_start = std::min(g->at_start, iaa);
-                    g->at_end   = std::max(g->at_end, iaa + 1);
-                    /* When making the graph we (might) link all atoms in an interaction
-                     * sequentially. Therefore the end atoms add 1 to the count,
-                     * the middle atoms 2.
-                     */
-                    if (k == 1 || k == nratoms)
-                    {
-                        nbond[iaa] += 1;
-                    }
-                    else
-                    {
-                        nbond[iaa] += 2;
-                    }
+                    edgesLists.pushBack({});
                 }
             }
-        }
-    }
-}
+            numEmptyEntriesSkipped = 0;
 
-template<typename T>
-static int calc_start_end(FILE* fplog, t_graph* g, const T il[], int at_start, int at_end, int nbond[])
-{
-    int i, nnb, nbtot;
+            edgesLists.pushBack(edges);
 
-    g->at_start = at_end;
-    g->at_end   = 0;
+            (*numConnectedAtoms)++;
 
-    /* First add all the real bonds: they should determine the molecular
-     * graph.
-     */
-    for (i = 0; (i < F_NRE); i++)
-    {
-        if (interaction_function[i].flags & IF_CHEMBOND)
-        {
-            calc_1se(g, i, il[i], nbond, at_start, at_end);
-        }
-    }
-    /* Then add all the other interactions in fixed lists, but first
-     * check to see what's there already.
-     */
-    for (i = 0; (i < F_NRE); i++)
-    {
-        if (!(interaction_function[i].flags & IF_CHEMBOND))
-        {
-            calc_1se(g, i, il[i], nbond, at_start, at_end);
+            max_nedge = std::max(max_nedge, int(gmx::ssize(edges)));
         }
     }
 
-    nnb   = 0;
-    nbtot = 0;
-    for (i = g->at_start; (i < g->at_end); i++)
-    {
-        nbtot += nbond[i];
-        nnb = std::max(nnb, nbond[i]);
-    }
-    if (fplog)
-    {
-        fprintf(fplog, "Max number of connections per atom is %d\n", nnb);
-        fprintf(fplog, "Total number of connections is %d\n", nbtot);
-    }
-    return nbtot;
-}
-
-
-static void compact_graph(FILE* fplog, t_graph* g)
-{
-    int i, j, n, max_nedge;
-
-    max_nedge = 0;
-    n         = 0;
-    for (i = 0; i < g->nnodes; i++)
-    {
-        for (j = 0; j < g->nedge[i]; j++)
-        {
-            g->edge[0][n++] = g->edge[i][j];
-        }
-        max_nedge = std::max(max_nedge, g->nedge[i]);
-    }
-    srenew(g->edge[0], n);
-    /* set pointers after srenew because edge[0] might move */
-    for (i = 1; i < g->nnodes; i++)
-    {
-        g->edge[i] = g->edge[i - 1] + g->nedge[i - 1];
-    }
-
     if (fplog)
     {
         fprintf(fplog, "Max number of graph edges per atom is %d\n", max_nedge);
-        fprintf(fplog, "Total number of graph edges is %d\n", n);
+        fprintf(fplog, "Total number of graph edges is %d\n", edgesLists.numElements());
     }
+
+    return edgesLists;
 }
 
-static gmx_bool determine_graph_parts(t_graph* g, int* part)
+static gmx_bool determine_graph_parts(const EdgesGenerator& edgesG, ArrayRef<int> partNr)
 {
-    int      i, e;
-    int      nchanged;
-    int      at_i, *at_i2;
-    gmx_bool bMultiPart;
-
     /* Initialize the part array with all entries different */
-    for (at_i = g->at_start; at_i < g->at_end; at_i++)
+    const int endAtom = edgesG.edges().size();
+    for (int at_i = 0; at_i < endAtom; at_i++)
     {
-        part[at_i] = at_i;
+        partNr[at_i] = at_i;
     }
 
     /* Loop over the graph until the part array is fixed */
+    bool haveMultipleParts = false;
+    int  numAtomsChanged   = 0;
     do
     {
-        bMultiPart = FALSE;
-        nchanged   = 0;
-        for (i = 0; (i < g->nnodes); i++)
+        haveMultipleParts = false;
+        numAtomsChanged   = 0;
+        for (gmx::index at_i = 0; at_i < gmx::ssize(edgesG.edges()); at_i++)
         {
-            at_i  = g->at_start + i;
-            at_i2 = g->edge[i];
-            for (e = 0; e < g->nedge[i]; e++)
+            for (const int at_i2 : edgesG.edges()[at_i])
             {
                 /* Set part for both nodes to the minimum */
-                if (part[at_i2[e]] > part[at_i])
+                if (partNr[at_i2] > partNr[at_i])
                 {
-                    part[at_i2[e]] = part[at_i];
-                    nchanged++;
+                    partNr[at_i2] = partNr[at_i];
+                    numAtomsChanged++;
                 }
-                else if (part[at_i2[e]] < part[at_i])
+                else if (partNr[at_i2] < partNr[at_i])
                 {
-                    part[at_i] = part[at_i2[e]];
-                    nchanged++;
+                    partNr[at_i] = partNr[at_i2];
+                    numAtomsChanged++;
                 }
             }
-            if (part[at_i] != part[g->at_start])
+            if (partNr[at_i] != partNr[0])
             {
-                bMultiPart = TRUE;
+                haveMultipleParts = true;
             }
         }
         if (debug)
         {
-            fprintf(debug, "graph part[] nchanged=%d, bMultiPart=%s\n", nchanged,
-                    gmx::boolToString(bMultiPart));
+            fprintf(debug, "graph partNr[] numAtomsChanged=%d, bMultiPart=%s\n", numAtomsChanged,
+                    gmx::boolToString(haveMultipleParts));
         }
-    } while (nchanged > 0);
+    } while (numAtomsChanged > 0);
 
-    return bMultiPart;
+    return haveMultipleParts;
 }
 
 template<typename T>
-static void mk_graph_ilist(FILE*    fplog,
-                           const T* ilist,
-                           int      at_start,
-                           int      at_end,
-                           gmx_bool bShakeOnly,
-                           gmx_bool bSettle,
-                           t_graph* g)
+static t_graph mk_graph_ilist(FILE* fplog, const T* ilist, int at_end, gmx_bool bShakeOnly, gmx_bool bSettle)
 {
-    int*     nbond;
-    int      i, nbtot;
-    gmx_bool bMultiPart;
+    EdgesGenerator edgesG(at_end);
 
-    /* The naming is somewhat confusing, but we need g->at0 and g->at1
-     * for shifthing coordinates to a new array (not in place) when
-     * some atoms are not connected by the graph, which runs from
-     * g->at_start (>= g->at0) to g->at_end (<= g->at1).
-     */
-    g->at0   = at_start;
-    g->at1   = at_end;
-    g->parts = t_graph::BondedParts::Single;
+    t_graph::BondedParts parts = t_graph::BondedParts::Single;
 
-    snew(nbond, at_end);
-    nbtot = calc_start_end(fplog, g, ilist, at_start, at_end, nbond);
-
-    if (g->at_start >= g->at_end)
-    {
-        g->at_start = at_start;
-        g->at_end   = at_end;
-        g->nnodes   = 0;
-        g->nbound   = 0;
-    }
-    else
+    if (at_end > 0)
     {
-        g->nnodes = g->at_end - g->at_start;
-        snew(g->nedge, g->nnodes);
-        snew(g->edge, g->nnodes);
-        /* Allocate a single array and set pointers into it */
-        snew(g->edge[0], nbtot);
-        for (i = 1; (i < g->nnodes); i++)
-        {
-            g->edge[i] = g->edge[i - 1] + nbond[g->at_start + i - 1];
-        }
-
         if (!bShakeOnly)
         {
             /* First add all the real bonds: they should determine the molecular
              * graph.
              */
-            for (i = 0; (i < F_NRE); i++)
+            for (int i = 0; (i < F_NRE); i++)
             {
                 if (interaction_function[i].flags & IF_CHEMBOND)
                 {
-                    mk_igraph(g, i, ilist[i], at_start, at_end, nullptr);
+                    mk_igraph(&edgesG, i, ilist[i], at_end, {});
                 }
             }
 
             /* Determine of which separated parts the IF_CHEMBOND graph consists.
-             * Store the parts in the nbond array.
+             * Store the part numbers in the partNr array.
              */
-            bMultiPart = determine_graph_parts(g, nbond);
+            std::vector<int> partNr(at_end);
+            const bool       bMultiPart = determine_graph_parts(edgesG, partNr);
 
             if (bMultiPart)
             {
@@ -436,87 +336,96 @@ static void mk_graph_ilist(FILE*    fplog,
                  * that are not connected through IF_CHEMBOND interactions.
                  */
                 bool addedEdge = false;
-                for (i = 0; (i < F_NRE); i++)
+                for (int i = 0; (i < F_NRE); i++)
                 {
                     if (!(interaction_function[i].flags & IF_CHEMBOND))
                     {
-                        bool addedEdgeForType = mk_igraph(g, i, ilist[i], at_start, at_end, nbond);
+                        bool addedEdgeForType = mk_igraph(&edgesG, i, ilist[i], at_end, partNr);
                         addedEdge             = (addedEdge || addedEdgeForType);
                     }
                 }
 
                 if (addedEdge)
                 {
-                    g->parts = t_graph::BondedParts::MultipleConnected;
+                    parts = t_graph::BondedParts::MultipleConnected;
                 }
                 else
                 {
-                    g->parts = t_graph::BondedParts::MultipleDisconnected;
+                    parts = t_graph::BondedParts::MultipleDisconnected;
                 }
             }
-
-            /* Removed all the unused space from the edge array */
-            compact_graph(fplog, g);
         }
         else
         {
             /* This is a special thing used in splitter.c to generate shake-blocks */
-            mk_igraph(g, F_CONSTR, ilist[F_CONSTR], at_start, at_end, nullptr);
+            mk_igraph(&edgesG, F_CONSTR, ilist[F_CONSTR], at_end, {});
             if (bSettle)
             {
-                mk_igraph(g, F_SETTLE, ilist[F_SETTLE], at_start, at_end, nullptr);
-            }
-        }
-        g->nbound = 0;
-        for (i = 0; (i < g->nnodes); i++)
-        {
-            if (g->nedge[i] > 0)
-            {
-                g->nbound++;
+                mk_igraph(&edgesG, F_SETTLE, ilist[F_SETTLE], at_end, {});
             }
         }
     }
 
-    g->negc = 0;
-    g->egc  = nullptr;
+    t_graph graph;
+    /* The naming is somewhat confusing, but we need g->shiftAtomEnd
+     * for shifthing coordinates to a new array (not in place) when
+     * some atoms are not connected by the graph, which runs from
+     * g->edgeAtomBegin (>= 0) to g->edgeAtomEnd (<= g->shiftAtomEnd).
+     */
+    graph.shiftAtomEnd  = at_end;
+    graph.edgeAtomBegin = 0;
+    graph.edgeAtomEnd   = at_end;
+    graph.parts         = parts;
+    if (at_end > 0)
+    {
+        /* Convert the vector of vector of edges to ListOfLists */
+        graph.edges = convertGraph(fplog, edgesG, &graph.edgeAtomBegin, &graph.numConnectedAtoms);
 
-    sfree(nbond);
+        graph.edgeAtomEnd = graph.edgeAtomBegin + graph.edges.ssize();
+    }
 
-    snew(g->ishift, g->at1);
+    graph.edgeColor.resize(graph.edges.size());
+    graph.ishift.resize(graph.shiftAtomEnd);
 
     if (gmx_debug_at)
     {
-        p_graph(debug, "graph", g);
+        p_graph(debug, "graph", &graph);
     }
+
+    return graph;
+}
+
+t_graph mk_graph_moltype(const gmx_moltype_t& moltype)
+{
+    return mk_graph_ilist(nullptr, moltype.ilist.data(), moltype.atoms.nr, FALSE, FALSE);
 }
 
-void mk_graph_moltype(const gmx_moltype_t& moltype, t_graph* g)
+t_graph mk_graph(const InteractionDefinitions& idef, const int numAtoms)
 {
-    mk_graph_ilist(nullptr, moltype.ilist.data(), 0, moltype.atoms.nr, FALSE, FALSE, g);
+    return mk_graph_ilist(nullptr, idef.il.data(), numAtoms, false, false);
 }
 
-t_graph* mk_graph(FILE* fplog, const t_idef* idef, int at_start, int at_end, gmx_bool bShakeOnly, gmx_bool bSettle)
+t_graph* mk_graph(FILE* fplog, const InteractionDefinitions& idef, int at_end, gmx_bool bShakeOnly, gmx_bool bSettle)
 {
-    t_graph* g;
+    t_graph* g = new (t_graph);
 
-    snew(g, 1);
+    *g = mk_graph_ilist(fplog, idef.il.data(), at_end, bShakeOnly, bSettle);
 
-    mk_graph_ilist(fplog, idef->il, at_start, at_end, bShakeOnly, bSettle, g);
+    return g;
+}
+
+t_graph* mk_graph(FILE* fplog, const t_idef* idef, int at_end, gmx_bool bShakeOnly, gmx_bool bSettle)
+{
+    t_graph* g = new (t_graph);
+
+    *g = mk_graph_ilist(fplog, idef->il, at_end, bShakeOnly, bSettle);
 
     return g;
 }
 
 void done_graph(t_graph* g)
 {
-    GCHECK(g);
-    if (g->nnodes > 0)
-    {
-        sfree(g->nedge);
-        sfree(g->edge[0]);
-        sfree(g->edge);
-        sfree(g->egc);
-    }
-    sfree(g->ishift);
+    delete g;
 }
 
 /************************************************************
@@ -652,9 +561,15 @@ static void mk_1shift_screw(const matrix box, const rvec hbox, const rvec xi, co
     }
 }
 
-static int mk_grey(egCol egc[], t_graph* g, int* AtomI, int npbcdim, const matrix box, const rvec x[], int* nerror)
+static int mk_grey(ArrayRef<egCol> edgeColor,
+                   t_graph*        g,
+                   int*            AtomI,
+                   int             npbcdim,
+                   const matrix    box,
+                   const rvec      x[],
+                   int*            nerror)
 {
-    int      m, j, ng, ai, aj, g0;
+    int      m, ng, ai, g0;
     rvec     dx, hbox;
     gmx_bool bTriclinic;
     ivec     is_aj;
@@ -666,16 +581,15 @@ static int mk_grey(egCol egc[], t_graph* g, int* AtomI, int npbcdim, const matri
     }
     bTriclinic = TRICLINIC(box);
 
-    g0 = g->at_start;
+    g0 = g->edgeAtomBegin;
     ng = 0;
     ai = g0 + *AtomI;
 
     /* Loop over all the bonds */
-    for (j = 0; (j < g->nedge[ai - g0]); j++)
+    for (const int aj : g->edges[ai - g0])
     {
-        aj = g->edge[ai - g0][j];
         /* If there is a white one, make it grey and set pbc */
-        if (g->bScrewPBC)
+        if (g->useScrewPbc)
         {
             mk_1shift_screw(box, hbox, x[ai], x[aj], g->ishift[ai], is_aj);
         }
@@ -688,13 +602,13 @@ static int mk_grey(egCol egc[], t_graph* g, int* AtomI, int npbcdim, const matri
             mk_1shift(npbcdim, hbox, x[ai], x[aj], g->ishift[ai], is_aj);
         }
 
-        if (egc[aj - g0] == egcolWhite)
+        if (edgeColor[aj - g0] == egcolWhite)
         {
             if (aj - g0 < *AtomI)
             {
                 *AtomI = aj - g0;
             }
-            egc[aj - g0] = egcolGrey;
+            edgeColor[aj - g0] = egcolGrey;
 
             copy_ivec(is_aj, g->ishift[aj]);
 
@@ -705,7 +619,7 @@ static int mk_grey(egCol egc[], t_graph* g, int* AtomI, int npbcdim, const matri
         {
             if (gmx_debug_at)
             {
-                set_pbc(&pbc, -1, box);
+                set_pbc(&pbc, PbcType::Unset, box);
                 pbc_dx(&pbc, x[ai], x[aj], dx);
                 fprintf(debug,
                         "mk_grey: shifts for atom %d due to atom %d\n"
@@ -720,16 +634,14 @@ static int mk_grey(egCol egc[], t_graph* g, int* AtomI, int npbcdim, const matri
     return ng;
 }
 
-static int first_colour(int fC, egCol Col, t_graph* g, const egCol egc[])
-/* Return the first node with colour Col starting at fC.
+/* Return the first node/atom with colour Col starting at fC.
  * return -1 if none found.
  */
+static gmx::index first_colour(const int fC, const egCol Col, const t_graph* g, ArrayRef<const egCol> edgeColor)
 {
-    int i;
-
-    for (i = fC; (i < g->nnodes); i++)
+    for (gmx::index i = fC; i < gmx::ssize(g->edges); i++)
     {
-        if ((g->nedge[i] > 0) && (egc[i] == Col))
+        if (!g->edges[i].empty() && edgeColor[i] == Col)
         {
             return i;
         }
@@ -739,21 +651,20 @@ static int first_colour(int fC, egCol Col, t_graph* g, const egCol egc[])
 }
 
 /* Returns the maximum length of the graph edges for coordinates x */
-static real maxEdgeLength(const t_graph g, int ePBC, const matrix box, const rvec x[])
+static real maxEdgeLength(const t_graph& g, PbcType pbcType, const matrix box, const rvec x[])
 {
     t_pbc pbc;
 
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
 
     real maxEdgeLength2 = 0;
 
-    for (int node = 0; node < g.nnodes; node++)
+    for (int node = 0; node < int(g.edges.size()); node++)
     {
-        for (int edge = 0; edge < g.nedge[node]; edge++)
+        for (const int nodeJ : g.edges[node])
         {
-            int  nodeJ = g.edge[node][edge];
             rvec dx;
-            pbc_dx(&pbc, x[g.at0 + node], x[g.at0 + nodeJ], dx);
+            pbc_dx(&pbc, x[node], x[nodeJ], dx);
             maxEdgeLength2 = std::max(maxEdgeLength2, norm2(dx));
         }
     }
@@ -761,18 +672,18 @@ static real maxEdgeLength(const t_graph g, int ePBC, const matrix box, const rve
     return std::sqrt(maxEdgeLength2);
 }
 
-void mk_mshift(FILE* log, t_graph* g, int ePBC, const matrix box, const rvec x[])
+void mk_mshift(FILE* log, t_graph* g, PbcType pbcType, const matrix box, const rvec x[])
 {
     static int nerror_tot = 0;
     int        npbcdim;
-    int        ng, nnodes, i;
+    int        ng, i;
     int        nW, nG, nB; /* Number of Grey, Black, White     */
     int        fW, fG;     /* First of each category   */
     int        nerror = 0;
 
-    g->bScrewPBC = (ePBC == epbcSCREW);
+    g->useScrewPbc = (pbcType == PbcType::Screw);
 
-    if (ePBC == epbcXY)
+    if (pbcType == PbcType::XY)
     {
         npbcdim = 2;
     }
@@ -786,25 +697,19 @@ void mk_mshift(FILE* log, t_graph* g, int ePBC, const matrix box, const rvec x[]
      * at all. If we return without doing this for a system without bonds
      * (i.e. only settles) all water molecules are moved to the opposite octant
      */
-    for (i = g->at0; (i < g->at1); i++)
+    for (i = 0; i < g->shiftAtomEnd; i++)
     {
         g->ishift[i][XX] = g->ishift[i][YY] = g->ishift[i][ZZ] = 0;
     }
 
-    if (!g->nbound)
+    if (!g->numConnectedAtoms)
     {
         return;
     }
 
-    nnodes = g->nnodes;
-    if (nnodes > g->negc)
-    {
-        g->negc = nnodes;
-        srenew(g->egc, g->negc);
-    }
-    memset(g->egc, 0, static_cast<size_t>(nnodes * sizeof(g->egc[0])));
+    std::fill(g->edgeColor.begin(), g->edgeColor.end(), egcolWhite);
 
-    nW = g->nbound;
+    nW = g->numConnectedAtoms;
     nG = 0;
     nB = 0;
 
@@ -822,13 +727,13 @@ void mk_mshift(FILE* log, t_graph* g, int ePBC, const matrix box, const rvec x[]
          * number than before, because no nodes are made white
          * in the loop
          */
-        if ((fW = first_colour(fW, egcolWhite, g, g->egc)) == -1)
+        if ((fW = first_colour(fW, egcolWhite, g, g->edgeColor)) == -1)
         {
             gmx_fatal(FARGS, "No WHITE nodes found while nW=%d\n", nW);
         }
 
         /* Make the first white node grey */
-        g->egc[fW] = egcolGrey;
+        g->edgeColor[fW] = egcolGrey;
         nG++;
         nW--;
 
@@ -839,20 +744,20 @@ void mk_mshift(FILE* log, t_graph* g, int ePBC, const matrix box, const rvec x[]
 #endif
         while (nG > 0)
         {
-            if ((fG = first_colour(fG, egcolGrey, g, g->egc)) == -1)
+            if ((fG = first_colour(fG, egcolGrey, g, g->edgeColor)) == -1)
             {
                 gmx_fatal(FARGS, "No GREY nodes found while nG=%d\n", nG);
             }
 
             /* Make the first grey node black */
-            g->egc[fG] = egcolBlack;
+            g->edgeColor[fG] = egcolBlack;
             nB++;
             nG--;
 
             /* Make all the neighbours of this black node grey
              * and set their periodicity
              */
-            ng = mk_grey(g->egc, g, &fG, npbcdim, box, x, &nerror);
+            ng = mk_grey(g->edgeColor, g, &fG, npbcdim, box, x, &nerror);
             /* ng is the number of white nodes made grey */
             nG += ng;
             nW -= ng;
@@ -869,21 +774,21 @@ void mk_mshift(FILE* log, t_graph* g, int ePBC, const matrix box, const rvec x[]
          */
         constexpr real c_relativeDistanceThreshold = 0.25;
 
-        int numPbcDimensions = ePBC2npbcdim(ePBC);
-        GMX_RELEASE_ASSERT(numPbcDimensions > 0, "Expect PBC with graph");
+        int npbcdim = numPbcDimensions(pbcType);
+        GMX_RELEASE_ASSERT(npbcdim > 0, "Expect PBC with graph");
         real minBoxSize = norm(box[XX]);
-        for (int d = 1; d < numPbcDimensions; d++)
+        for (int d = 1; d < npbcdim; d++)
         {
             minBoxSize = std::min(minBoxSize, norm(box[d]));
         }
-        real maxDistance = maxEdgeLength(*g, ePBC, box, x);
+        real maxDistance = maxEdgeLength(*g, pbcType, box, x);
         if (maxDistance >= c_relativeDistanceThreshold * minBoxSize)
         {
             std::string mesg = gmx::formatString(
                     "There are inconsistent shifts over periodic boundaries in a molecule type "
                     "consisting of %d atoms. The longest distance involved in such interactions is "
                     "%.3f nm which is %s half the box length.",
-                    g->at1 - g->at0, maxDistance, maxDistance >= 0.5 * minBoxSize ? "above" : "close to");
+                    g->shiftAtomEnd, maxDistance, maxDistance >= 0.5 * minBoxSize ? "above" : "close to");
 
             switch (g->parts)
             {
@@ -934,21 +839,19 @@ void mk_mshift(FILE* log, t_graph* g, int ePBC, const matrix box, const rvec x[]
 
 void shift_x(const t_graph* g, const matrix box, const rvec x[], rvec x_s[])
 {
-    ivec* is;
-    int   g0, g1;
-    int   j, tx, ty, tz;
+    int j, tx, ty, tz;
 
     GCHECK(g);
-    g0 = g->at_start;
-    g1 = g->at_end;
-    is = g->ishift;
+    const int            g0 = g->edgeAtomBegin;
+    const int            g1 = g->edgeAtomEnd;
+    ArrayRef<const IVec> is = g->ishift;
 
-    for (j = g->at0; j < g0; j++)
+    for (j = 0; j < g0; j++)
     {
         copy_rvec(x[j], x_s[j]);
     }
 
-    if (g->bScrewPBC)
+    if (g->useScrewPbc)
     {
         for (j = g0; (j < g1); j++)
         {
@@ -997,26 +900,21 @@ void shift_x(const t_graph* g, const matrix box, const rvec x[], rvec x_s[])
         }
     }
 
-    for (j = g1; j < g->at1; j++)
+    for (j = g1; j < g->shiftAtomEnd; j++)
     {
         copy_rvec(x[j], x_s[j]);
     }
 }
 
-void shift_self(const t_graph* g, const matrix box, rvec x[])
+void shift_self(const t_graph& g, const matrix box, rvec x[])
 {
-    ivec* is;
-    int   g0, g1;
-    int   j, tx, ty, tz;
+    int j, tx, ty, tz;
 
-    if (g->bScrewPBC)
-    {
-        gmx_incons("screw pbc not implemented for shift_self");
-    }
+    GMX_RELEASE_ASSERT(!g.useScrewPbc, "screw pbc not implemented for shift_self");
 
-    g0 = g->at_start;
-    g1 = g->at_end;
-    is = g->ishift;
+    const int            g0 = g.edgeAtomBegin;
+    const int            g1 = g.edgeAtomEnd;
+    ArrayRef<const IVec> is = g.ishift;
 
 #ifdef DEBUG
     fprintf(stderr, "Shifting atoms %d to %d\n", g0, g0 + gn);
@@ -1049,22 +947,25 @@ void shift_self(const t_graph* g, const matrix box, rvec x[])
     }
 }
 
+void shift_self(const t_graph* g, const matrix box, rvec x[])
+{
+    shift_self(*g, box, x);
+}
+
 void unshift_x(const t_graph* g, const matrix box, rvec x[], const rvec x_s[])
 {
-    ivec* is;
-    int   g0, g1;
-    int   j, tx, ty, tz;
+    int j, tx, ty, tz;
 
-    if (g->bScrewPBC)
+    if (g->useScrewPbc)
     {
         gmx_incons("screw pbc not implemented (yet) for unshift_x");
     }
 
-    g0 = g->at_start;
-    g1 = g->at_end;
-    is = g->ishift;
+    const int            g0 = g->edgeAtomBegin;
+    const int            g1 = g->edgeAtomEnd;
+    ArrayRef<const IVec> is = g->ishift;
 
-    for (j = g->at0; j < g0; j++)
+    for (j = 0; j < g0; j++)
     {
         copy_rvec(x_s[j], x[j]);
     }
@@ -1096,7 +997,7 @@ void unshift_x(const t_graph* g, const matrix box, rvec x[], const rvec x_s[])
         }
     }
 
-    for (j = g1; j < g->at1; j++)
+    for (j = g1; j < g->shiftAtomEnd; j++)
     {
         copy_rvec(x_s[j], x[j]);
     }
@@ -1104,18 +1005,16 @@ void unshift_x(const t_graph* g, const matrix box, rvec x[], const rvec x_s[])
 
 void unshift_self(const t_graph* g, const matrix box, rvec x[])
 {
-    ivec* is;
-    int   g0, g1;
-    int   j, tx, ty, tz;
+    int j, tx, ty, tz;
 
-    if (g->bScrewPBC)
+    if (g->useScrewPbc)
     {
         gmx_incons("screw pbc not implemented for unshift_self");
     }
 
-    g0 = g->at_start;
-    g1 = g->at_end;
-    is = g->ishift;
+    const int            g0 = g->edgeAtomBegin;
+    const int            g1 = g->edgeAtomEnd;
+    ArrayRef<const IVec> is = g->ishift;
 
     if (TRICLINIC(box))
     {
index 5990c37c778cee58ca5b75079568857b2caf1e9e..7b3838c9989b72d2b1239905e1fd5594174be125 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_PBCUTIL_MSHIFT_H
 #define GMX_PBCUTIL_MSHIFT_H
 
-#include <stdio.h>
+#include <cstdio>
+
+#include <vector>
 
 #include "gromacs/math/vectypes.h"
 #include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/listoflists.h"
 
 struct InteractionList;
 struct gmx_moltype_t;
+class InteractionDefinitions;
 struct t_idef;
+enum class PbcType : int;
 
 typedef enum
 {
@@ -54,9 +60,13 @@ typedef enum
     egcolNR
 } egCol;
 
+/* Struct used to make molecules broken over PBC whole
+ *
+ * TODO: Should be turned into a proper class
+ */
 struct t_graph
 {
-    /* Described the connectivity between, potentially, multiple parts of
+    /* Describes the connectivity between, potentially, multiple parts of
      * the ilist that are internally chemically bonded together.
      */
     enum class BondedParts
@@ -66,48 +76,78 @@ struct t_graph
         MultipleConnected /* There are multiple parts, e.g. monomers, that are partially or fully connected between each other by interactions other than chemical bonds */
     };
 
-    int         at0;       /* The first atom the graph was constructed for */
-    int         at1;       /* The last atom the graph was constructed for  */
-    int         nnodes;    /* The number of nodes, nnodes=at_end-at_start  */
-    int         nbound;    /* The number of nodes with edges               */
-    int         at_start;  /* The first connected atom in this graph       */
-    int         at_end;    /* The last+1 connected atom in this graph      */
-    int*        nedge;     /* For each node the number of edges            */
-    int**       edge;      /* For each node, the actual edges (bidirect.)  */
-    gmx_bool    bScrewPBC; /* Screw boundary conditions                    */
-    ivec*       ishift;    /* Shift for each particle                      */
-    int         negc;
-    egCol*      egc;   /* color of each node */
-    BondedParts parts; /* How chemically bonded parts are connected    */
+    // Returns the number of nodes stored in the graph (can be less than shiftAtomEnd)
+    int numNodes() const { return edges.size(); }
+
+    // Shift atoms up to shiftAtomEnd
+    int shiftAtomEnd = 0;
+    // The number of atoms that are connected to other atoms in the graph
+    int numConnectedAtoms = 0;
+    // The first connected atom in the graph
+    int edgeAtomBegin = 0;
+    // The last connected atom in the graph
+    int edgeAtomEnd = 0;
+    //  The graph: list of atoms connected to each atom, indexing is offset by -edgeAtomBegin
+    gmx::ListOfLists<int> edges;
+    // Whether we are using screw PBC
+    bool useScrewPbc = false;
+    // Shift for each particle, updated after putting atoms in the box
+    std::vector<gmx::IVec> ishift;
+    // Work buffer for coloring nodes
+    std::vector<egCol> edgeColor;
+    // Tells how connected this graph is
+    BondedParts parts = BondedParts::Single;
 };
 
 #define SHIFT_IVEC(g, i) ((g)->ishift[i])
 
-t_graph* mk_graph(FILE* fplog, const struct t_idef* idef, int at_start, int at_end, gmx_bool bShakeOnly, gmx_bool bSettle);
+t_graph mk_graph(const InteractionDefinitions& idef, int numAtoms);
+/* Build a graph from an idef description. The graph can be used
+ * to generate mol-shift indices.
+ * numAtoms should coincide will molecule boundaries,
+ */
+
+t_graph* mk_graph(FILE*                         fplog,
+                  const InteractionDefinitions& idef,
+                  int                           shiftAtomEnd,
+                  gmx_bool                      bShakeOnly,
+                  gmx_bool                      bSettle);
 /* Build a graph from an idef description. The graph can be used
  * to generate mol-shift indices.
- * at_start and at_end should coincide will molecule boundaries,
- * for the whole system this is simply 0 and natoms.
+ * Shifts atoms up to shiftAtomEnd, which should coincide with a molecule boundary,
+ * for the whole system this is simply natoms.
  * If bShakeOnly, only the connections in the shake list are used.
  * If bSettle && bShakeOnly the settles are used too.
  */
 
-void mk_graph_moltype(const gmx_moltype_t& moltype, t_graph* g);
-/* As mk_graph, but takes gmx_moltype_t iso t_idef and does not allocate g */
+t_graph* mk_graph(FILE* fplog, const struct t_idef* idef, int shiftAtomEnd, gmx_bool bShakeOnly, gmx_bool bSettle);
+/* Build a graph from an idef description. The graph can be used
+ * to generate mol-shift indices.
+ * Shifts atoms up to shiftAtomEnd, which should coincide with a molecule boundary,
+ * for the whole system this is simply natoms.
+ * If bShakeOnly, only the connections in the shake list are used.
+ * If bSettle && bShakeOnly the settles are used too.
+ */
+
+t_graph mk_graph_moltype(const gmx_moltype_t& moltype);
+/* As mk_graph, but takes gmx_moltype_t iso t_idef */
 
 
 void done_graph(t_graph* g);
-/* Free the memory in g */
+/* Free the memory in *g and the pointer g */
 
-void p_graph(FILE* log, const char* title, t_graph* g);
+void p_graph(FILE* log, const char* title, const t_graph* g);
 /* Print a graph to log */
 
-void mk_mshift(FILE* log, t_graph* g, int ePBC, const matrix box, const rvec x[]);
+void mk_mshift(FILE* log, t_graph* g, PbcType pbcType, const matrix box, const rvec x[]);
 /* Calculate the mshift codes, based on the connection graph in g. */
 
 void shift_x(const t_graph* g, const matrix box, const rvec x[], rvec x_s[]);
 /* Add the shift vector to x, and store in x_s (may be same array as x) */
 
+void shift_self(const t_graph& g, const matrix box, rvec x[]);
+/* Id. but in place */
+
 void shift_self(const t_graph* g, const matrix box, rvec x[]);
 /* Id. but in place */
 
index b60c20aa2dfeb2c54312a60637308ede8c2e87e9..a97372f88ffa2adf2cfb0d51f0454b5813c9b4f5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -62,7 +63,8 @@
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/smalloc.h"
 
-const char* epbc_names[epbcNR + 1] = { "xyz", "no", "xy", "screw", nullptr };
+const gmx::EnumerationArray<PbcType, std::string> c_pbcTypeNames = { { "xyz", "no", "xy", "screw",
+                                                                       "unset" } };
 
 /* Skip 0 so we have more chance of detecting if we forgot to call set_pbc. */
 enum
@@ -84,17 +86,20 @@ enum
 //! Margin correction if the box is too skewed
 #define BOX_MARGIN_CORRECT 1.0005
 
-int ePBC2npbcdim(int ePBC)
+int numPbcDimensions(PbcType pbcType)
 {
     int npbcdim = 0;
 
-    switch (ePBC)
+    switch (pbcType)
     {
-        case epbcXYZ: npbcdim = 3; break;
-        case epbcXY: npbcdim = 2; break;
-        case epbcSCREW: npbcdim = 3; break;
-        case epbcNONE: npbcdim = 0; break;
-        default: gmx_fatal(FARGS, "Unknown ePBC=%d in ePBC2npbcdim", ePBC);
+        case PbcType::Unset:
+            gmx_fatal(FARGS, "Number of PBC dimensions was requested before the PBC type set.");
+        case PbcType::Xyz: npbcdim = 3; break;
+        case PbcType::XY: npbcdim = 2; break;
+        case PbcType::Screw: npbcdim = 3; break;
+        case PbcType::No: npbcdim = 0; break;
+        default:
+            gmx_fatal(FARGS, "Unknown pbcType=%s in numPbcDimensions", c_pbcTypeNames[pbcType].c_str());
     }
 
     return npbcdim;
@@ -104,7 +109,7 @@ void dump_pbc(FILE* fp, t_pbc* pbc)
 {
     rvec sum_box;
 
-    fprintf(fp, "ePBCDX = %d\n", pbc->ePBCDX);
+    fprintf(fp, "pbcTypeDX = %d\n", pbc->pbcTypeDX);
     pr_rvecs(fp, 0, "box", pbc->box, DIM);
     pr_rvecs(fp, 0, "fbox_diag", &pbc->fbox_diag, 1);
     pr_rvecs(fp, 0, "hbox_diag", &pbc->hbox_diag, 1);
@@ -120,16 +125,16 @@ void dump_pbc(FILE* fp, t_pbc* pbc)
     }
 }
 
-const char* check_box(int ePBC, const matrix box)
+const char* check_box(PbcType pbcType, const matrix box)
 {
     const char* ptr;
 
-    if (ePBC == -1)
+    if (pbcType == PbcType::Unset)
     {
-        ePBC = guess_ePBC(box);
+        pbcType = guessPbcType(box);
     }
 
-    if (ePBC == epbcNONE)
+    if (pbcType == PbcType::No)
     {
         return nullptr;
     }
@@ -139,12 +144,12 @@ const char* check_box(int ePBC, const matrix box)
         ptr = "Only triclinic boxes with the first vector parallel to the x-axis and the second "
               "vector in the xy-plane are supported.";
     }
-    else if (ePBC == epbcSCREW && (box[YY][XX] != 0 || box[ZZ][XX] != 0))
+    else if (pbcType == PbcType::Screw && (box[YY][XX] != 0 || box[ZZ][XX] != 0))
     {
         ptr = "The unit cell can not have off-diagonal x-components with screw pbc";
     }
     else if (std::fabs(box[YY][XX]) > BOX_MARGIN * 0.5 * box[XX][XX]
-             || (ePBC != epbcXY
+             || (pbcType != PbcType::XY
                  && (std::fabs(box[ZZ][XX]) > BOX_MARGIN * 0.5 * box[XX][XX]
                      || std::fabs(box[ZZ][YY]) > BOX_MARGIN * 0.5 * box[YY][YY])))
     {
@@ -171,7 +176,7 @@ void matrix_convert(matrix box, const rvec vec, const rvec angleInDegrees)
             std::sqrt(gmx::square(vec[ZZ]) - box[ZZ][XX] * box[ZZ][XX] - box[ZZ][YY] * box[ZZ][YY]);
 }
 
-real max_cutoff2(int ePBC, const matrix box)
+real max_cutoff2(PbcType pbcType, const matrix box)
 {
     real       min_hv2, min_ss;
     const real oneFourth = 0.25;
@@ -180,7 +185,7 @@ real max_cutoff2(int ePBC, const matrix box)
      * by half the length of the shortest box vector.
      */
     min_hv2 = oneFourth * std::min(norm2(box[XX]), norm2(box[YY]));
-    if (ePBC != epbcXY)
+    if (pbcType != PbcType::XY)
     {
         min_hv2 = std::min(min_hv2, oneFourth * norm2(box[ZZ]));
     }
@@ -190,7 +195,7 @@ real max_cutoff2(int ePBC, const matrix box)
      * in the grid search and pbc_dx is a lot faster
      * than checking all possible combinations.
      */
-    if (ePBC == epbcXY)
+    if (pbcType == PbcType::XY)
     {
         min_ss = std::min(box[XX][XX], box[YY][YY]);
     }
@@ -205,21 +210,21 @@ real max_cutoff2(int ePBC, const matrix box)
 //! Set to true if warning has been printed
 static gmx_bool bWarnedGuess = FALSE;
 
-int guess_ePBC(const matrix box)
+PbcType guessPbcType(const matrix box)
 {
-    int ePBC;
+    PbcType pbcType;
 
     if (box[XX][XX] > 0 && box[YY][YY] > 0 && box[ZZ][ZZ] > 0)
     {
-        ePBC = epbcXYZ;
+        pbcType = PbcType::Xyz;
     }
     else if (box[XX][XX] > 0 && box[YY][YY] > 0 && box[ZZ][ZZ] == 0)
     {
-        ePBC = epbcXY;
+        pbcType = PbcType::XY;
     }
     else if (box[XX][XX] == 0 && box[YY][YY] == 0 && box[ZZ][ZZ] == 0)
     {
-        ePBC = epbcNONE;
+        pbcType = PbcType::No;
     }
     else
     {
@@ -231,15 +236,15 @@ int guess_ePBC(const matrix box)
                     box[XX][XX], box[YY][YY], box[ZZ][ZZ]);
             bWarnedGuess = TRUE;
         }
-        ePBC = epbcNONE;
+        pbcType = PbcType::No;
     }
 
     if (debug)
     {
-        fprintf(debug, "Guessed pbc = %s from the box matrix\n", epbc_names[ePBC]);
+        fprintf(debug, "Guessed pbc = %s from the box matrix\n", c_pbcTypeNames[pbcType].c_str());
     }
 
-    return ePBC;
+    return pbcType;
 }
 
 //! Check if the box still obeys the restrictions, if not, correct it
@@ -290,9 +295,9 @@ static int correct_box_elem(FILE* fplog, int step, tensor box, int v, int d)
     return shift;
 }
 
-gmx_bool correct_box(FILE* fplog, int step, tensor box, t_graph* graph)
+gmx_bool correct_box(FILE* fplog, int step, tensor box)
 {
-    int      zy, zx, yx, i;
+    int      zy, zx, yx;
     gmx_bool bCorrected;
 
     zy = correct_box_elem(fplog, step, box, ZZ, YY);
@@ -301,33 +306,22 @@ gmx_bool correct_box(FILE* fplog, int step, tensor box, t_graph* graph)
 
     bCorrected = ((zy != 0) || (zx != 0) || (yx != 0));
 
-    if (bCorrected && graph)
-    {
-        /* correct the graph */
-        for (i = graph->at_start; i < graph->at_end; i++)
-        {
-            graph->ishift[i][YY] -= graph->ishift[i][ZZ] * zy;
-            graph->ishift[i][XX] -= graph->ishift[i][ZZ] * zx;
-            graph->ishift[i][XX] -= graph->ishift[i][YY] * yx;
-        }
-    }
-
     return bCorrected;
 }
 
 //! Do the real arithmetic for filling the pbc struct
-static void low_set_pbc(t_pbc* pbc, int ePBC, const ivec dd_pbc, const matrix box)
+static void low_set_pbc(t_pbc* pbc, PbcType pbcType, const ivec dd_pbc, const matrix box)
 {
     int         order[3] = { 0, -1, 1 };
     ivec        bPBC;
     const char* ptr;
 
-    pbc->ePBC      = ePBC;
-    pbc->ndim_ePBC = ePBC2npbcdim(ePBC);
+    pbc->pbcType   = pbcType;
+    pbc->ndim_ePBC = numPbcDimensions(pbcType);
 
-    if (pbc->ePBC == epbcNONE)
+    if (pbc->pbcType == PbcType::No)
     {
-        pbc->ePBCDX = epbcdxNOPBC;
+        pbc->pbcTypeDX = epbcdxNOPBC;
 
         return;
     }
@@ -344,17 +338,17 @@ static void low_set_pbc(t_pbc* pbc, int ePBC, const ivec dd_pbc, const matrix bo
         pbc->mhbox_diag[i] = -pbc->hbox_diag[i];
     }
 
-    ptr = check_box(ePBC, box);
+    ptr = check_box(pbcType, box);
     if (ptr)
     {
         fprintf(stderr, "Warning: %s\n", ptr);
         pr_rvecs(stderr, 0, "         Box", box, DIM);
         fprintf(stderr, "         Can not fix pbc.\n\n");
-        pbc->ePBCDX = epbcdxUNSUPPORTED;
+        pbc->pbcTypeDX = epbcdxUNSUPPORTED;
     }
     else
     {
-        if (ePBC == epbcSCREW && nullptr != dd_pbc)
+        if (pbcType == PbcType::Screw && nullptr != dd_pbc)
         {
             /* This combinated should never appear here */
             gmx_incons("low_set_pbc called with screw pbc and dd_nc != NULL");
@@ -363,7 +357,7 @@ static void low_set_pbc(t_pbc* pbc, int ePBC, const ivec dd_pbc, const matrix bo
         int npbcdim = 0;
         for (int i = 0; i < DIM; i++)
         {
-            if ((dd_pbc && dd_pbc[i] == 0) || (ePBC == epbcXY && i == ZZ))
+            if ((dd_pbc && dd_pbc[i] == 0) || (pbcType == PbcType::XY && i == ZZ))
             {
                 bPBC[i] = 0;
             }
@@ -379,7 +373,7 @@ static void low_set_pbc(t_pbc* pbc, int ePBC, const ivec dd_pbc, const matrix bo
                 /* 1D pbc is not an mdp option and it is therefore only used
                  * with single shifts.
                  */
-                pbc->ePBCDX = epbcdx1D_RECT;
+                pbc->pbcTypeDX = epbcdx1D_RECT;
                 for (int i = 0; i < DIM; i++)
                 {
                     if (bPBC[i])
@@ -392,12 +386,12 @@ static void low_set_pbc(t_pbc* pbc, int ePBC, const ivec dd_pbc, const matrix bo
                 {
                     if (pbc->box[pbc->dim][i] != 0)
                     {
-                        pbc->ePBCDX = epbcdx1D_TRIC;
+                        pbc->pbcTypeDX = epbcdx1D_TRIC;
                     }
                 }
                 break;
             case 2:
-                pbc->ePBCDX = epbcdx2D_RECT;
+                pbc->pbcTypeDX = epbcdx2D_RECT;
                 for (int i = 0; i < DIM; i++)
                 {
                     if (!bPBC[i])
@@ -413,41 +407,42 @@ static void low_set_pbc(t_pbc* pbc, int ePBC, const ivec dd_pbc, const matrix bo
                         {
                             if (pbc->box[i][j] != 0)
                             {
-                                pbc->ePBCDX = epbcdx2D_TRIC;
+                                pbc->pbcTypeDX = epbcdx2D_TRIC;
                             }
                         }
                     }
                 }
                 break;
             case 3:
-                if (ePBC != epbcSCREW)
+                if (pbcType != PbcType::Screw)
                 {
                     if (TRICLINIC(box))
                     {
-                        pbc->ePBCDX = epbcdxTRICLINIC;
+                        pbc->pbcTypeDX = epbcdxTRICLINIC;
                     }
                     else
                     {
-                        pbc->ePBCDX = epbcdxRECTANGULAR;
+                        pbc->pbcTypeDX = epbcdxRECTANGULAR;
                     }
                 }
                 else
                 {
-                    pbc->ePBCDX = (box[ZZ][YY] == 0 ? epbcdxSCREW_RECT : epbcdxSCREW_TRIC);
-                    if (pbc->ePBCDX == epbcdxSCREW_TRIC)
+                    pbc->pbcTypeDX = (box[ZZ][YY] == 0 ? epbcdxSCREW_RECT : epbcdxSCREW_TRIC);
+                    if (pbc->pbcTypeDX == epbcdxSCREW_TRIC)
                     {
                         fprintf(stderr,
                                 "Screw pbc is not yet implemented for triclinic boxes.\n"
                                 "Can not fix pbc.\n");
-                        pbc->ePBCDX = epbcdxUNSUPPORTED;
+                        pbc->pbcTypeDX = epbcdxUNSUPPORTED;
                     }
                 }
                 break;
             default: gmx_fatal(FARGS, "Incorrect number of pbc dimensions with DD: %d", npbcdim);
         }
-        pbc->max_cutoff2 = max_cutoff2(ePBC, box);
+        pbc->max_cutoff2 = max_cutoff2(pbcType, box);
 
-        if (pbc->ePBCDX == epbcdxTRICLINIC || pbc->ePBCDX == epbcdx2D_TRIC || pbc->ePBCDX == epbcdxSCREW_TRIC)
+        if (pbc->pbcTypeDX == epbcdxTRICLINIC || pbc->pbcTypeDX == epbcdx2D_TRIC
+            || pbc->pbcTypeDX == epbcdxSCREW_TRIC)
         {
             if (debug)
             {
@@ -570,35 +565,35 @@ static void low_set_pbc(t_pbc* pbc, int ePBC, const ivec dd_pbc, const matrix bo
     }
 }
 
-void set_pbc(t_pbc* pbc, int ePBC, const matrix box)
+void set_pbc(t_pbc* pbc, PbcType pbcType, const matrix box)
 {
-    if (ePBC == -1)
+    if (pbcType == PbcType::Unset)
     {
-        ePBC = guess_ePBC(box);
+        pbcType = guessPbcType(box);
     }
 
-    low_set_pbc(pbc, ePBC, nullptr, box);
+    low_set_pbc(pbc, pbcType, nullptr, box);
 }
 
-t_pbc* set_pbc_dd(t_pbc* pbc, int ePBC, const ivec domdecCells, gmx_bool bSingleDir, const matrix box)
+t_pbc* set_pbc_dd(t_pbc* pbc, PbcType pbcType, const ivec domdecCells, gmx_bool bSingleDir, const matrix box)
 {
-    if (ePBC == epbcNONE)
+    if (pbcType == PbcType::No)
     {
-        pbc->ePBC = ePBC;
+        pbc->pbcType = pbcType;
 
         return nullptr;
     }
 
     if (nullptr == domdecCells)
     {
-        low_set_pbc(pbc, ePBC, nullptr, box);
+        low_set_pbc(pbc, pbcType, nullptr, box);
     }
     else
     {
-        if (ePBC == epbcSCREW && domdecCells[XX] > 1)
+        if (pbcType == PbcType::Screw && domdecCells[XX] > 1)
         {
             /* The rotation has been taken care of during coordinate communication */
-            ePBC = epbcXYZ;
+            pbcType = PbcType::Xyz;
         }
 
         ivec usePBC;
@@ -606,7 +601,7 @@ t_pbc* set_pbc_dd(t_pbc* pbc, int ePBC, const ivec domdecCells, gmx_bool bSingle
         for (int i = 0; i < DIM; i++)
         {
             usePBC[i] = 0;
-            if (domdecCells[i] <= (bSingleDir ? 1 : 2) && !(ePBC == epbcXY && i == ZZ))
+            if (domdecCells[i] <= (bSingleDir ? 1 : 2) && !(pbcType == PbcType::XY && i == ZZ))
             {
                 usePBC[i] = 1;
                 npbcdim++;
@@ -615,15 +610,15 @@ t_pbc* set_pbc_dd(t_pbc* pbc, int ePBC, const ivec domdecCells, gmx_bool bSingle
 
         if (npbcdim > 0)
         {
-            low_set_pbc(pbc, ePBC, usePBC, box);
+            low_set_pbc(pbc, pbcType, usePBC, box);
         }
         else
         {
-            pbc->ePBC = epbcNONE;
+            pbc->pbcType = PbcType::No;
         }
     }
 
-    return (pbc->ePBC != epbcNONE ? pbc : nullptr);
+    return (pbc->pbcType != PbcType::No ? pbc : nullptr);
 }
 
 void pbc_dx(const t_pbc* pbc, const rvec x1, const rvec x2, rvec dx)
@@ -635,7 +630,7 @@ void pbc_dx(const t_pbc* pbc, const rvec x1, const rvec x2, rvec dx)
 
     rvec_sub(x1, x2, dx);
 
-    switch (pbc->ePBCDX)
+    switch (pbc->pbcTypeDX)
     {
         case epbcdxRECTANGULAR:
             for (i = 0; i < DIM; i++)
@@ -806,7 +801,7 @@ int pbc_dx_aiuc(const t_pbc* pbc, const rvec x1, const rvec x2, rvec dx)
     rvec_sub(x1, x2, dx);
     clear_ivec(ishift);
 
-    switch (pbc->ePBCDX)
+    switch (pbc->pbcTypeDX)
     {
         case epbcdxRECTANGULAR:
             for (i = 0; i < DIM; i++)
@@ -1078,7 +1073,7 @@ void pbc_dx_d(const t_pbc* pbc, const dvec x1, const dvec x2, dvec dx)
 
     dvec_sub(x1, x2, dx);
 
-    switch (pbc->ePBCDX)
+    switch (pbc->pbcTypeDX)
     {
         case epbcdxRECTANGULAR:
         case epbcdx2D_RECT:
@@ -1391,16 +1386,16 @@ int* compact_unitcell_edges()
     return edge;
 }
 
-void put_atoms_in_box(int ePBC, const matrix box, gmx::ArrayRef<gmx::RVec> x)
+void put_atoms_in_box(PbcType pbcType, const matrix box, gmx::ArrayRef<gmx::RVec> x)
 {
     int npbcdim, m, d;
 
-    if (ePBC == epbcSCREW)
+    if (pbcType == PbcType::Screw)
     {
-        gmx_fatal(FARGS, "Sorry, %s pbc is not yet supported", epbc_names[ePBC]);
+        gmx_fatal(FARGS, "Sorry, %s pbc is not yet supported", c_pbcTypeNames[pbcType].c_str());
     }
 
-    if (ePBC == epbcXY)
+    if (pbcType == PbcType::XY)
     {
         npbcdim = 2;
     }
@@ -1451,7 +1446,7 @@ void put_atoms_in_box(int ePBC, const matrix box, gmx::ArrayRef<gmx::RVec> x)
     }
 }
 
-void put_atoms_in_box_omp(int ePBC, const matrix box, gmx::ArrayRef<gmx::RVec> x, gmx_unused int nth)
+void put_atoms_in_box_omp(PbcType pbcType, const matrix box, gmx::ArrayRef<gmx::RVec> x, gmx_unused int nth)
 {
 #pragma omp parallel for num_threads(nth) schedule(static)
     for (int t = 0; t < nth; t++)
@@ -1461,7 +1456,7 @@ void put_atoms_in_box_omp(int ePBC, const matrix box, gmx::ArrayRef<gmx::RVec> x
             size_t natoms = x.size();
             size_t offset = (natoms * t) / nth;
             size_t len    = (natoms * (t + 1)) / nth - offset;
-            put_atoms_in_box(ePBC, box, x.subArray(offset, len));
+            put_atoms_in_box(pbcType, box, x.subArray(offset, len));
         }
         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
     }
@@ -1524,14 +1519,14 @@ void put_atoms_in_triclinic_unitcell(int ecenter, const matrix box, gmx::ArrayRe
     }
 }
 
-void put_atoms_in_compact_unitcell(int ePBC, int ecenter, const matrix box, gmx::ArrayRef<gmx::RVec> x)
+void put_atoms_in_compact_unitcell(PbcType pbcType, int ecenter, const matrix box, gmx::ArrayRef<gmx::RVec> x)
 {
     t_pbc pbc;
     rvec  box_center, dx;
 
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
 
-    if (pbc.ePBCDX == epbcdxUNSUPPORTED)
+    if (pbc.pbcTypeDX == epbcdxUNSUPPORTED)
     {
         gmx_fatal(FARGS, "Can not put atoms in compact unitcell with unsupported PBC");
     }
@@ -1547,23 +1542,22 @@ void put_atoms_in_compact_unitcell(int ePBC, int ecenter, const matrix box, gmx:
 /*! \brief Make molecules whole by shifting positions
  *
  * \param[in]     fplog     Log file
- * \param[in]     ePBC      The PBC type
+ * \param[in]     pbcType   The PBC type
  * \param[in]     box       The simulation box
  * \param[in]     mtop      System topology definition
  * \param[in,out] x         The coordinates of the atoms
  * \param[in]     bFirst    Specifier for first-time PBC removal
  */
-static void low_do_pbc_mtop(FILE* fplog, int ePBC, const matrix box, const gmx_mtop_t* mtop, rvec x[], gmx_bool bFirst)
+static void
+low_do_pbc_mtop(FILE* fplog, PbcType pbcType, const matrix box, const gmx_mtop_t* mtop, rvec x[], gmx_bool bFirst)
 {
-    t_graph* graph;
-    int      as, mol;
+    int as, mol;
 
     if (bFirst && fplog)
     {
         fprintf(fplog, "Removing pbc first time\n");
     }
 
-    snew(graph, 1);
     as = 0;
     for (const gmx_molblock_t& molb : mtop->molblock)
     {
@@ -1575,11 +1569,11 @@ static void low_do_pbc_mtop(FILE* fplog, int ePBC, const matrix box, const gmx_m
         }
         else
         {
-            mk_graph_moltype(moltype, graph);
+            t_graph graph = mk_graph_moltype(moltype);
 
             for (mol = 0; mol < molb.nmol; mol++)
             {
-                mk_mshift(fplog, graph, ePBC, box, x + as);
+                mk_mshift(fplog, &graph, pbcType, box, x + as);
 
                 shift_self(graph, box, x + as);
                 /* The molecule is whole now.
@@ -1589,18 +1583,16 @@ static void low_do_pbc_mtop(FILE* fplog, int ePBC, const matrix box, const gmx_m
 
                 as += moltype.atoms.nr;
             }
-            done_graph(graph);
         }
     }
-    sfree(graph);
 }
 
-void do_pbc_first_mtop(FILE* fplog, int ePBC, const matrix box, const gmx_mtop_t* mtop, rvec x[])
+void do_pbc_first_mtop(FILE* fplog, PbcType pbcType, const matrix box, const gmx_mtop_t* mtop, rvec x[])
 {
-    low_do_pbc_mtop(fplog, ePBC, box, mtop, x, TRUE);
+    low_do_pbc_mtop(fplog, pbcType, box, mtop, x, TRUE);
 }
 
-void do_pbc_mtop(int ePBC, const matrix box, const gmx_mtop_t* mtop, rvec x[])
+void do_pbc_mtop(PbcType pbcType, const matrix box, const gmx_mtop_t* mtop, rvec x[])
 {
-    low_do_pbc_mtop(nullptr, ePBC, box, mtop, x, FALSE);
+    low_do_pbc_mtop(nullptr, pbcType, box, mtop, x, FALSE);
 }
index 5bc0ee5ed90c1ea0fa05cdf3302fc47edf9a3054..1a67c9b98fbc313d10e489bd7150f3be055edee5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <stdio.h>
 
+#include <string>
+
 #include "gromacs/math/vectypes.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/real.h"
 
 struct gmx_domdec_t;
 struct gmx_mtop_t;
 
-enum
+//! Enumeration that contains all supported periodic boundary setups.
+enum class PbcType : int
 {
-    epbcXYZ,
-    epbcNONE,
-    epbcXY,
-    epbcSCREW,
-    epbcNR
+    Xyz   = 0, //!< Periodic boundaries in all dimensions.
+    No    = 1, //!< No periodic boundaries.
+    XY    = 2, //!< Only two dimensions are periodic.
+    Screw = 3, //!< Screw.
+    Unset = 4, //!< The type of PBC is not set or invalid.
+    Count = 5
 };
 
-//! Strings corresponding to epbc enum values.
-extern const char* epbc_names[epbcNR + 1];
+//! Names for all values in PBC types enumeration
+extern const gmx::EnumerationArray<PbcType, std::string> c_pbcTypeNames;
 
 /* Maximum number of combinations of single triclinic box vectors
  * required to shift atoms that are within a brick of the size of
@@ -69,16 +75,16 @@ extern const char* epbc_names[epbcNR + 1];
 typedef struct t_pbc
 {
     //! The PBC type
-    int ePBC;
+    PbcType pbcType;
     //! Number of dimensions in which PBC is exerted
     int ndim_ePBC;
     /*! \brief Determines how to compute distance vectors.
      *
      *  Indicator of how to compute distance vectors, depending
-     *  on PBC type (depends on ePBC and dimensions with(out) DD)
+     *  on PBC type (depends on pbcType and dimensions with(out) DD)
      *  and the box angles.
      */
-    int ePBCDX;
+    int pbcTypeDX;
     /*! \brief Used for selecting which dimensions to use in PBC.
      *
      *  In case of 1-D PBC this indicates which dimension is used,
@@ -126,14 +132,12 @@ enum
     ecenterDEF = ecenterTRIC
 };
 
-struct t_graph;
-
 /*! \brief Returns the number of dimensions that use pbc
  *
- * \param[in] ePBC The periodic boundary condition type
+ * \param[in] pbcType The periodic boundary condition type
  * \return the number of dimensions that use pbc, starting at X
  */
-int ePBC2npbcdim(int ePBC);
+int numPbcDimensions(PbcType pbcType);
 
 /*! \brief Dump the contents of the pbc structure to the file
  *
@@ -144,17 +148,18 @@ void dump_pbc(FILE* fp, t_pbc* pbc);
 
 /*! \brief Check the box for consistency
  *
- * \param[in] ePBC The pbc identifier
- * \param[in] box  The box matrix
+ * When \p pbcType=PbcTypes::Unset, the type of pbc is guessed from the box matrix.
+ *
+ * \param[in] pbcType The pbc identifier
+ * \param[in] box     The box matrix
  * \return NULL if the box is supported by Gromacs.
- * Otherwise returns a string with the problem.
- * When ePBC=-1, the type of pbc is guessed from the box matrix.
+ *         Otherwise returns a string with the problem.
  */
-const char* check_box(int ePBC, const matrix box);
+const char* check_box(PbcType pbcType, const matrix box);
 
 /*! \brief Creates box matrix from edge lengths and angles.
  *
- * \param[in,out] box         The box matrix
+ * \param[in,out] box        The box matrix
  * \param[in] vec            The edge lengths
  * \param[in] angleInDegrees The angles
  */
@@ -165,42 +170,44 @@ void matrix_convert(matrix box, const rvec vec, const rvec angleInDegrees);
  * Returns the square of the maximum cut-off allowed for the box,
  * taking into account that the grid neighborsearch code and pbc_dx
  * only check combinations of single box-vector shifts.
- * \param[in] ePBC The pbc identifier
+ *
+ * \param[in] pbcType The pbc identifier
  * \param[in] box  The box matrix
  * \return the maximum cut-off.
  */
-real max_cutoff2(int ePBC, const matrix box);
+real max_cutoff2(PbcType pbcType, const matrix box);
 
-/*! \brief Guess PBC typr
+/*! \brief Guess PBC type
  *
  * Guesses the type of periodic boundary conditions using the box
+ *
  * \param[in] box  The box matrix
- * \return The pbc identifier
+ * \return The pbc type identifier
  */
-int guess_ePBC(const matrix box);
+PbcType guessPbcType(const matrix box);
 
 /*! \brief Corrects the box if necessary
  *
- * Checks for un-allowed box angles and corrects the box
- * and the integer shift vectors in the graph (if graph!=NULL) if necessary.
+ * Checks for un-allowed box angles and corrects the box.
+ *
  * \param[in] fplog File for debug output
  * \param[in] step  The MD step number
  * \param[in] box   The simulation cell
- * \param[in] graph Information about molecular connectivity
  * \return TRUE when the box was corrected.
  */
-gmx_bool correct_box(FILE* fplog, int step, tensor box, struct t_graph* graph);
+gmx_bool correct_box(FILE* fplog, int step, tensor box);
 
 /*! \brief Initiate the periodic boundary condition algorithms.
  *
  * pbc_dx will not use pbc and return the normal difference vector
  * when one or more of the diagonal elements of box are zero.
- * When ePBC=-1, the type of pbc is guessed from the box matrix.
+ * When \p pbcType=PbcType::Unset, the type of pbc is guessed from the box matrix.
+ *
  * \param[in,out] pbc The pbc information structure
- * \param[in] ePBC The PBC identifier
- * \param[in] box  The box tensor
+ * \param[in] pbcType The PBC identifier
+ * \param[in] box     The box tensor
  */
-void set_pbc(t_pbc* pbc, int ePBC, const matrix box);
+void set_pbc(t_pbc* pbc, PbcType pbcType, const matrix box);
 
 /*! \brief Initiate the periodic boundary condition algorithms.
  *
@@ -210,17 +217,18 @@ void set_pbc(t_pbc* pbc, int ePBC, const matrix box);
  * If domdecCells!=NULL pbc is not used for directions
  * with dd->nc[i]==1 with bSingleDir==TRUE or
  * with dd->nc[i]<=2 with bSingleDir==FALSE.
- * Note that when no PBC is required only pbc->ePBC is set,
+ * Note that when no PBC is required only pbc->pbcType is set,
  * the rest of the struct will be invalid.
- * \param[in,out] pbc The pbc information structure
- * \param[in] ePBC        The PBC identifier
+ *
+ * \param[in,out] pbc     The pbc information structure
+ * \param[in] pbcType     The PBC identifier
  * \param[in] domdecCells 3D integer vector describing the number of DD cells
  *                        or nullptr if not using DD.
  * \param[in] bSingleDir  TRUE if DD communicates only in one direction along dimensions
  * \param[in] box         The box tensor
  * \return the pbc structure when pbc operations are required, NULL otherwise.
  */
-t_pbc* set_pbc_dd(t_pbc* pbc, int ePBC, const ivec domdecCells, gmx_bool bSingleDir, const matrix box);
+t_pbc* set_pbc_dd(t_pbc* pbc, PbcType pbcType, const ivec domdecCells, gmx_bool bSingleDir, const matrix box);
 
 /*! \brief Compute distance with PBC
  *
@@ -310,23 +318,25 @@ int* compact_unitcell_edges();
  * These routines puts ONE or ALL atoms in the box, not caring
  * about charge groups!
  * Also works for triclinic cells.
- * \param[in]     ePBC   The pbc type
- * \param[in]     box    The simulation box
- * \param[in,out] x      The coordinates of the atoms
+ *
+ * \param[in]     pbcType The pbc type
+ * \param[in]     box     The simulation box
+ * \param[in,out] x       The coordinates of the atoms
  */
-void put_atoms_in_box(int ePBC, const matrix box, gmx::ArrayRef<gmx::RVec> x);
+void put_atoms_in_box(PbcType pbcType, const matrix box, gmx::ArrayRef<gmx::RVec> x);
 
 /*! \brief Parallellizes put_atoms_in_box()
  *
  * This wrapper function around put_atoms_in_box() with the ugly manual
  * workload splitting is needed to avoid silently introducing multithreading
  * in tools.
- * \param[in]     ePBC       The pbc type
+ *
+ * \param[in]     pbcType    The pbc type
  * \param[in]     box        The simulation box
  * \param[in,out] x          The coordinates of the atoms
  * \param[in]     nth        number of threads to be used in the given module
  */
-void put_atoms_in_box_omp(int ePBC, const matrix box, gmx::ArrayRef<gmx::RVec> x, gmx_unused int nth);
+void put_atoms_in_box_omp(PbcType pbcType, const matrix box, gmx::ArrayRef<gmx::RVec> x, gmx_unused int nth);
 
 /*! \brief Put atoms inside triclinic box
  *
@@ -342,31 +352,32 @@ void put_atoms_in_triclinic_unitcell(int ecenter, const matrix box, gmx::ArrayRe
  *
  * This puts ALL atoms at the closest distance for the center of the box
  * as calculated by calc_box_center.
- * When ePBC=-1, the type of pbc is guessed from the box matrix.
- * \param[in]    ePBC    The pbc type
+ * When \p pbcType=PbcType::Unset, the type of pbc is guessed from the box matrix.
+ *
+ * \param[in]    pbcType The pbc type
  * \param[in]    ecenter The pbc center type
  * \param[in]    box     The simulation box
- * \param[in,out] x       The coordinates of the atoms
+ * \param[in,out] x      The coordinates of the atoms
  */
-void put_atoms_in_compact_unitcell(int ePBC, int ecenter, const matrix box, gmx::ArrayRef<gmx::RVec> x);
+void put_atoms_in_compact_unitcell(PbcType pbcType, int ecenter, const matrix box, gmx::ArrayRef<gmx::RVec> x);
 
 /*! \brief Make all molecules whole by shifting positions
  *
  * \param[in]     fplog     Log file
- * \param[in]     ePBC      The PBC type
+ * \param[in]     pbcType   The PBC type
  * \param[in]     box       The simulation box
  * \param[in]     mtop      System topology definition
  * \param[in,out] x         The coordinates of the atoms
  */
-void do_pbc_first_mtop(FILE* fplog, int ePBC, const matrix box, const gmx_mtop_t* mtop, rvec x[]);
+void do_pbc_first_mtop(FILE* fplog, PbcType pbcType, const matrix box, const gmx_mtop_t* mtop, rvec x[]);
 
 /*! \brief Make molecules consisting of multiple charge groups whole by shifting positions
  *
- * \param[in]     ePBC      The PBC type
+ * \param[in]     pbcType   The PBC type
  * \param[in]     box       The simulation box
  * \param[in]     mtop      System topology definition
  * \param[in,out] x         The coordinates of the atoms
  */
-void do_pbc_mtop(int ePBC, const matrix box, const gmx_mtop_t* mtop, rvec x[]);
+void do_pbc_mtop(PbcType pbcType, const matrix box, const gmx_mtop_t* mtop, rvec x[]);
 
 #endif
index e8888f65d60be87a96cfd88ee7fe8bf954307186..bd45ec5d5c4b8c61e4b40c498a21541ecd1fde0e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,8 +40,8 @@
  *
  * \todo CPU, GPU and SIMD routines essentially do the same operations on
  *       different data-types. Currently this leads to code duplication,
- *       which has to be resolved. For details, see Redmine task #2863
- *       https://redmine.gromacs.org/issues/2863
+ *       which has to be resolved. For details, see Issue #2863
+ *       https://gitlab.com/gromacs/gromacs/-/issues/2863
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
  * \author Berk Hess <hess@kth.se>
@@ -143,8 +143,8 @@ static inline void setPbcAiuc(int numPbcDim, const matrix box, PbcAiuc* pbcAiuc)
  * \todo This routine operates on rvec types and uses PbcAiuc to define
  *       periodic box, but essentially does the same thing as SIMD and GPU
  *       version. These will have to be unified in future to avoid code
- *       duplication. See Redmine task #2863:
- *       https://redmine.gromacs.org/issues/2863
+ *       duplication. See Issue #2863:
+ *       https://gitlab.com/gromacs/gromacs/-/issues/2863
  *
  * \param[in]  pbcAiuc  PBC object.
  * \param[in]  r1       Coordinates of the first point.
index 0444a9e3e6ec129e2d3ca42f8c278ddee9cdaf95..96661613f6765a10dfcb4f5c1541598ce21f50ee 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,8 +40,8 @@
  *
  * \todo CPU, GPU and SIMD routines essentially do the same operations on
  *       different data-types. Currently this leads to code duplication,
- *       which has to be resolved. For details, see Redmine task #2863
- *       https://redmine.gromacs.org/issues/2863
+ *       which has to be resolved. For details, see Issue #2863
+ *       https://gitlab.com/gromacs/gromacs/-/issues/2863
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
  * \author Berk Hess <hess@kth.se>
@@ -53,7 +53,6 @@
 #ifndef GMX_PBCUTIL_PBC_AIUC_CUDA_CUH
 #define GMX_PBCUTIL_PBC_AIUC_CUDA_CUH
 
-#include "gromacs/gpu_utils/gpu_vec.cuh"
 #include "gromacs/gpu_utils/vectype_ops.cuh"
 #include "gromacs/pbcutil/pbc_aiuc.h"
 
@@ -73,8 +72,8 @@
  *       the same thing as the version below, as well as SIMD and CPU
  *       versions. This routine is used in gpubonded module.
  *       To avoid code duplication, these implementations should be
- *       unified. See Redmine task #2863:
- *       https://redmine.gromacs.org/issues/2863
+ *       unified. See Issue #2863:
+ *       https://gitlab.com/gromacs/gromacs/-/issues/2863
  *
  * \param[in]  pbcAiuc  PBC object.
  * \param[in]  r1       Coordinates of the first point.
  */
 template<bool returnShift>
 static __forceinline__ __device__ int
-                       pbcDxAiuc(const PbcAiuc& pbcAiuc, const float4& r1, const float4& r2, fvec dr)
+                       pbcDxAiuc(const PbcAiuc& pbcAiuc, const float4 r1, const float4 r2, float3& dr)
 {
-    dr[XX] = r1.x - r2.x;
-    dr[YY] = r1.y - r2.y;
-    dr[ZZ] = r1.z - r2.z;
+    dr.x = r1.x - r2.x;
+    dr.y = r1.y - r2.y;
+    dr.z = r1.z - r2.z;
 
-    float shz = rintf(dr[ZZ] * pbcAiuc.invBoxDiagZ);
-    dr[XX] -= shz * pbcAiuc.boxZX;
-    dr[YY] -= shz * pbcAiuc.boxZY;
-    dr[ZZ] -= shz * pbcAiuc.boxZZ;
+    float shz = rintf(dr.z * pbcAiuc.invBoxDiagZ);
+    dr.x -= shz * pbcAiuc.boxZX;
+    dr.y -= shz * pbcAiuc.boxZY;
+    dr.z -= shz * pbcAiuc.boxZZ;
 
-    float shy = rintf(dr[YY] * pbcAiuc.invBoxDiagY);
-    dr[XX] -= shy * pbcAiuc.boxYX;
-    dr[YY] -= shy * pbcAiuc.boxYY;
+    float shy = rintf(dr.y * pbcAiuc.invBoxDiagY);
+    dr.x -= shy * pbcAiuc.boxYX;
+    dr.y -= shy * pbcAiuc.boxYY;
 
-    float shx = rintf(dr[XX] * pbcAiuc.invBoxDiagX);
-    dr[XX] -= shx * pbcAiuc.boxXX;
+    float shx = rintf(dr.x * pbcAiuc.invBoxDiagX);
+    dr.x -= shx * pbcAiuc.boxXX;
 
     if (returnShift)
     {
@@ -128,8 +127,8 @@ static __forceinline__ __device__ int
  *       version above, as well as SIMD and CPU versions. This routine is
  *       used in GPU-based constraints.
  *       To avoid code duplication, these implementations should be
- *       unified. See Redmine task #2863:
- *       https://redmine.gromacs.org/issues/2863
+ *       unified. See Issue #2863:
+ *       https://gitlab.com/gromacs/gromacs/-/issues/2863
  *
  * \param[in]  pbcAiuc  PBC object.
  * \param[in]  r1       Coordinates of the first point.
index d27661821c31bef364b3c831389a7df423b6a365..fa824891a7b514b89b1e444b30dec63defd120d6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,7 +53,7 @@ using namespace gmx; // TODO: Remove when this file is moved into gmx namespace
 void set_pbc_simd(const t_pbc gmx_unused* pbc, real gmx_unused* pbc_simd)
 {
 #if GMX_SIMD_HAVE_REAL
-    if (pbc != nullptr && pbc->ePBC != epbcNONE)
+    if (pbc != nullptr && pbc->pbcType != PbcType::No)
     {
         rvec inv_box_diag = { 0, 0, 0 };
 
index d7e1a91f550cba3f3f1da9998232343d8c99d67d..c12f03dea0e712cd3925dbdd149b4e15f114cb56 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,7 +49,7 @@ namespace gmx
 /*! \brief
  * Helper enum class to define centering types.
  */
-enum class CenteringType
+enum class CenteringType : int
 {
     Triclinic,
     Rectangular,
@@ -58,9 +58,9 @@ enum class CenteringType
 };
 
 /*! \brief
- * Helper enum class to define unit cell representation types.
+ * Helper enum class to define Unit cell representation types.
  */
-enum class UnitCellType
+enum class UnitCellType : int
 {
     Triclinic,
     Rectangular,
@@ -71,14 +71,14 @@ enum class UnitCellType
 /*! \brief
  * Get names for the different centering types.
  *
- * \param[in] type Centering type to get a name for.
+ * \param[in] type What name needs to be provided.
  */
 const char* centerTypeNames(CenteringType type);
 
 /*! \brief
  * Get names for the different unit cell representation types.
  *
- * \param[in] type Unit cell type to get a name for.
+ * \param[in] type What name needs to be provided.
  */
 const char* unitCellTypeNames(UnitCellType type);
 
index 7cdd4e1ca72097eec26def9bb201eae12f693919..3b8f887feed3c4c2a19fa0a85717992f67f0a2ac 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,7 +44,7 @@
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
 
-void calc_pbc_cluster(int ecenter, int nrefat, t_topology* top, int ePBC, rvec x[], const int index[], matrix box)
+void calc_pbc_cluster(int ecenter, int nrefat, t_topology* top, PbcType pbcType, rvec x[], const int index[], matrix box)
 {
     int       m, i, j, j0, j1, jj, ai, aj;
     int       imin, jmin;
@@ -64,7 +64,7 @@ void calc_pbc_cluster(int ecenter, int nrefat, t_topology* top, int ePBC, rvec x
 
     /* Initiate the pbc structure */
     std::memset(&pbc, 0, sizeof(pbc));
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
 
     /* Convert atom index to molecular */
     nmol   = top->mols.nr;
@@ -242,7 +242,7 @@ void put_molecule_com_in_box(int      unitcell_enum,
                              t_block* mols,
                              int      natoms,
                              t_atom   atom[],
-                             int      ePBC,
+                             PbcType  pbcType,
                              matrix   box,
                              rvec     x[])
 {
@@ -254,7 +254,7 @@ void put_molecule_com_in_box(int      unitcell_enum,
     t_pbc  pbc;
 
     calc_box_center(ecenter, box, box_center);
-    set_pbc(&pbc, ePBC, box);
+    set_pbc(&pbc, pbcType, box);
     if (mols->nr <= 0)
     {
         gmx_fatal(FARGS,
@@ -283,10 +283,10 @@ void put_molecule_com_in_box(int      unitcell_enum,
         auto newComArrayRef = gmx::arrayRefFromArray(&newCom, 1);
         switch (unitcell_enum)
         {
-            case euRect: put_atoms_in_box(ePBC, box, newComArrayRef); break;
+            case euRect: put_atoms_in_box(pbcType, box, newComArrayRef); break;
             case euTric: put_atoms_in_triclinic_unitcell(ecenter, box, newComArrayRef); break;
             case euCompact:
-                put_atoms_in_compact_unitcell(ePBC, ecenter, box, newComArrayRef);
+                put_atoms_in_compact_unitcell(pbcType, ecenter, box, newComArrayRef);
                 break;
         }
         rvec_sub(newCom, com, shift);
@@ -307,7 +307,13 @@ void put_molecule_com_in_box(int      unitcell_enum,
     }
 }
 
-void put_residue_com_in_box(int unitcell_enum, int ecenter, int natoms, t_atom atom[], int ePBC, matrix box, rvec x[])
+void put_residue_com_in_box(int     unitcell_enum,
+                            int     ecenter,
+                            int     natoms,
+                            t_atom  atom[],
+                            PbcType pbcType,
+                            matrix  box,
+                            rvec    x[])
 {
     int              i, j, res_start, res_end;
     int              d, presnr;
@@ -335,10 +341,10 @@ void put_residue_com_in_box(int unitcell_enum, int ecenter, int natoms, t_atom a
             auto newComArrayRef = gmx::arrayRefFromArray(&newCom, 1);
             switch (unitcell_enum)
             {
-                case euRect: put_atoms_in_box(ePBC, box, newComArrayRef); break;
+                case euRect: put_atoms_in_box(pbcType, box, newComArrayRef); break;
                 case euTric: put_atoms_in_triclinic_unitcell(ecenter, box, newComArrayRef); break;
                 case euCompact:
-                    put_atoms_in_compact_unitcell(ePBC, ecenter, box, newComArrayRef);
+                    put_atoms_in_compact_unitcell(pbcType, ecenter, box, newComArrayRef);
                     break;
             }
             rvec_sub(newCom, com, shift);
index ff796c5c9b5c49bbbacd0f79f35b17efee89d743..262d32a8629370cb3784062f33a3dae25c9971e1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,6 +38,7 @@
 struct t_topology;
 struct t_block;
 struct t_atom;
+enum class PbcType : int;
 
 #include "gmxpre.h"
 
@@ -52,7 +53,7 @@ enum
     euNR
 };
 
-void calc_pbc_cluster(int ecenter, int nrefat, t_topology* top, int ePBC, rvec x[], const int index[], matrix box);
+void calc_pbc_cluster(int ecenter, int nrefat, t_topology* top, PbcType pbcType, rvec x[], const int index[], matrix box);
 
 
 void put_molecule_com_in_box(int      unitcell_enum,
@@ -60,11 +61,17 @@ void put_molecule_com_in_box(int      unitcell_enum,
                              t_block* mols,
                              int      natoms,
                              t_atom   atom[],
-                             int      ePBC,
+                             PbcType  pbcType,
                              matrix   box,
                              rvec     x[]);
 
-void put_residue_com_in_box(int unitcell_enum, int ecenter, int natoms, t_atom atom[], int ePBC, matrix box, rvec x[]);
+void put_residue_com_in_box(int     unitcell_enum,
+                            int     ecenter,
+                            int     natoms,
+                            t_atom  atom[],
+                            PbcType pbcType,
+                            matrix  box,
+                            rvec    x[]);
 
 void center_x(int ecenter, rvec x[], matrix box, int n, int nc, const int ci[]);
 
index 483dc715136c6d04f417519177fee7238a36ddda..a355d6df668d49cfff726f8d87f1d87529d80770 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -61,19 +62,21 @@ typedef struct
 
 struct gmx_rmpbc
 {
-    const t_idef*  idef;
-    int            natoms_init;
-    int            ePBC;
-    int            ngraph;
-    rmpbc_graph_t* graph;
+    const InteractionDefinitions* interactionDefinitions;
+    const t_idef*                 idef;
+    int                           natoms_init;
+    PbcType                       pbcType;
+    int                           ePBC;
+    int                           ngraph;
+    rmpbc_graph_t*                graph;
 };
 
-static t_graph* gmx_rmpbc_get_graph(gmx_rmpbc_t gpbc, int ePBC, int natoms)
+static t_graph* gmx_rmpbc_get_graph(gmx_rmpbc_t gpbc, PbcType pbcType, int natoms)
 {
     int            i;
     rmpbc_graph_t* gr;
 
-    if (ePBC == epbcNONE || nullptr == gpbc || nullptr == gpbc->idef || gpbc->idef->ntypes <= 0)
+    if (pbcType == PbcType::No || nullptr == gpbc || nullptr == gpbc->idef || gpbc->idef->ntypes <= 0)
     {
         return nullptr;
     }
@@ -103,13 +106,38 @@ static t_graph* gmx_rmpbc_get_graph(gmx_rmpbc_t gpbc, int ePBC, int natoms)
         srenew(gpbc->graph, gpbc->ngraph);
         gr         = &gpbc->graph[gpbc->ngraph - 1];
         gr->natoms = natoms;
-        gr->gr     = mk_graph(nullptr, gpbc->idef, 0, natoms, FALSE, FALSE);
+        if (gpbc->interactionDefinitions)
+        {
+            gr->gr = mk_graph(nullptr, *gpbc->interactionDefinitions, natoms, FALSE, FALSE);
+        }
+        else
+        {
+            gr->gr = mk_graph(nullptr, gpbc->idef, natoms, FALSE, FALSE);
+        }
     }
 
     return gr->gr;
 }
 
-gmx_rmpbc_t gmx_rmpbc_init(const t_idef* idef, int ePBC, int natoms)
+gmx_rmpbc_t gmx_rmpbc_init(const InteractionDefinitions& idef, PbcType pbcType, int natoms)
+{
+    gmx_rmpbc_t gpbc;
+
+    snew(gpbc, 1);
+
+    gpbc->natoms_init = natoms;
+
+    /* This sets pbc when we now it,
+     * otherwise we guess it from the instantaneous box in the trajectory.
+     */
+    gpbc->pbcType = pbcType;
+
+    gpbc->interactionDefinitions = &idef;
+
+    return gpbc;
+}
+
+gmx_rmpbc_t gmx_rmpbc_init(const t_idef* idef, PbcType pbcType, int natoms)
 {
     gmx_rmpbc_t gpbc;
 
@@ -120,7 +148,7 @@ gmx_rmpbc_t gmx_rmpbc_init(const t_idef* idef, int ePBC, int natoms)
     /* This sets pbc when we now it,
      * otherwise we guess it from the instantaneous box in the trajectory.
      */
-    gpbc->ePBC = ePBC;
+    gpbc->pbcType = pbcType;
 
     gpbc->idef = idef;
     if (gpbc->idef->ntypes <= 0)
@@ -144,8 +172,7 @@ void gmx_rmpbc_done(gmx_rmpbc_t gpbc)
     {
         for (i = 0; i < gpbc->ngraph; i++)
         {
-            done_graph(gpbc->graph[i].gr);
-            sfree(gpbc->graph[i].gr);
+            delete gpbc->graph[i].gr;
         }
         if (gpbc->graph != nullptr)
         {
@@ -155,43 +182,43 @@ void gmx_rmpbc_done(gmx_rmpbc_t gpbc)
     }
 }
 
-static int gmx_rmpbc_ePBC(gmx_rmpbc_t gpbc, const matrix box)
+static PbcType gmx_rmpbc_ePBC(gmx_rmpbc_t gpbc, const matrix box)
 {
-    if (nullptr != gpbc && gpbc->ePBC >= 0)
+    if (nullptr != gpbc && gpbc->pbcType != PbcType::Unset)
     {
-        return gpbc->ePBC;
+        return gpbc->pbcType;
     }
     else
     {
-        return guess_ePBC(box);
+        return guessPbcType(box);
     }
 }
 
 void gmx_rmpbc(gmx_rmpbc_t gpbc, int natoms, const matrix box, rvec x[])
 {
-    int      ePBC;
+    PbcType  pbcType;
     t_graph* gr;
 
-    ePBC = gmx_rmpbc_ePBC(gpbc, box);
-    gr   = gmx_rmpbc_get_graph(gpbc, ePBC, natoms);
+    pbcType = gmx_rmpbc_ePBC(gpbc, box);
+    gr      = gmx_rmpbc_get_graph(gpbc, pbcType, natoms);
     if (gr != nullptr)
     {
-        mk_mshift(stdout, gr, ePBC, box, x);
+        mk_mshift(stdout, gr, pbcType, box, x);
         shift_self(gr, box, x);
     }
 }
 
 void gmx_rmpbc_copy(gmx_rmpbc_t gpbc, int natoms, const matrix box, rvec x[], rvec x_s[])
 {
-    int      ePBC;
+    PbcType  pbcType;
     t_graph* gr;
     int      i;
 
-    ePBC = gmx_rmpbc_ePBC(gpbc, box);
-    gr   = gmx_rmpbc_get_graph(gpbc, ePBC, natoms);
+    pbcType = gmx_rmpbc_ePBC(gpbc, box);
+    gr      = gmx_rmpbc_get_graph(gpbc, pbcType, natoms);
     if (gr != nullptr)
     {
-        mk_mshift(stdout, gr, ePBC, box, x);
+        mk_mshift(stdout, gr, pbcType, box, x);
         shift_x(gr, box, x, x_s);
     }
     else
@@ -205,16 +232,16 @@ void gmx_rmpbc_copy(gmx_rmpbc_t gpbc, int natoms, const matrix box, rvec x[], rv
 
 void gmx_rmpbc_trxfr(gmx_rmpbc_t gpbc, t_trxframe* fr)
 {
-    int      ePBC;
+    PbcType  pbcType;
     t_graph* gr;
 
     if (fr->bX && fr->bBox)
     {
-        ePBC = gmx_rmpbc_ePBC(gpbc, fr->box);
-        gr   = gmx_rmpbc_get_graph(gpbc, ePBC, fr->natoms);
+        pbcType = gmx_rmpbc_ePBC(gpbc, fr->box);
+        gr      = gmx_rmpbc_get_graph(gpbc, pbcType, fr->natoms);
         if (gr != nullptr)
         {
-            mk_mshift(stdout, gr, ePBC, fr->box, fr->x);
+            mk_mshift(stdout, gr, pbcType, fr->box, fr->x);
             shift_self(gr, fr->box, fr->x);
         }
     }
index 0feb096495a13e1541502761f2e5d4925f60ef53..ac7411ee9cd6e5701b4fd2fc2ea86a2585941b82 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/math/vectypes.h"
 
+class InteractionDefinitions;
 struct t_atoms;
 struct t_idef;
 struct t_trxframe;
+enum class PbcType : int;
 
 typedef struct gmx_rmpbc* gmx_rmpbc_t;
 
-gmx_rmpbc_t gmx_rmpbc_init(const t_idef* idef, int ePBC, int natoms);
+gmx_rmpbc_t gmx_rmpbc_init(const InteractionDefinitions& idef, PbcType pbcType, int natoms);
+
+gmx_rmpbc_t gmx_rmpbc_init(const t_idef* idef, PbcType pbcType, int natoms);
 
 void gmx_rmpbc_done(gmx_rmpbc_t gpbc);
 
@@ -54,7 +58,7 @@ void gmx_rmpbc(gmx_rmpbc_t gpbc, int natoms, const matrix box, rvec x[]);
  * boundary conditions such that every molecule is whole.
  * natoms is the size x and can be smaller than the number
  * of atoms in idef, but should only contain complete molecules.
- * When ePBC=-1, the type of pbc is guessed from the box matrix.
+ * When pbcType=PbcType::Unset, the type of pbc is guessed from the box matrix.
  */
 
 void gmx_rmpbc_copy(gmx_rmpbc_t gpbc, int natoms, const matrix box, rvec x[], rvec x_s[]);
index 2b9cfd5e66b298f0d10c71e9f060345a96660f2e..729b52106ce7c8f4467dd8ef9c840e328a50b4d6 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,6 +33,9 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(PbcutilUnitTest pbcutil-test
-                  pbc.cpp
-                  pbcenums.cpp
-                  )
+    CPP_SOURCE_FILES
+        com.cpp
+        mshift.cpp
+        pbc.cpp
+        pbcenums.cpp
+        )
diff --git a/src/gromacs/pbcutil/tests/com.cpp b/src/gromacs/pbcutil/tests/com.cpp
new file mode 100644 (file)
index 0000000..84249c4
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests COM handling code.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \ingroup module_pbcutil
+ */
+#include "gmxpre.h"
+
+#include "gromacs/pbcutil/com.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/topology/mtop_util.h"
+#include "gromacs/topology/topology.h"
+
+#include "testutils/refdata.h"
+#include "testutils/testasserts.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+namespace
+{
+
+/*! \brief Populates a molectype for generate a graph
+ *
+ * This moleculetype has the first and last atom not connected to the other atoms
+ * so we test optimizations in the shift code that generates the actual graph only
+ * for the atom range from the first to the last connected atoms.
+ *
+ * Defines three residues to test residue and molecule COM handling.
+ *
+ * \todo Return moltype once t_atoms is a proper class.
+ */
+void populateMoleculeType(gmx_moltype_t* moltype)
+{
+    constexpr int atomNumber        = 5;
+    constexpr int residueNumber     = 3;
+    moltype->atoms.nr               = atomNumber;
+    moltype->ilist[F_CONSTR].iatoms = { 0, 1, 2 };
+    moltype->ilist[F_ANGLES].iatoms = { 1, 2, 1, 3 };
+
+    snew(moltype->atoms.atom, atomNumber);
+    snew(moltype->atoms.resinfo, residueNumber);
+    moltype->atoms.atom[0].resind = 0;
+    moltype->atoms.atom[1].resind = 1;
+    moltype->atoms.atom[2].resind = 1;
+    moltype->atoms.atom[3].resind = 1;
+    moltype->atoms.atom[4].resind = 2;
+
+    moltype->atoms.atom[0].m = 42;
+    moltype->atoms.atom[1].m = 13;
+    moltype->atoms.atom[2].m = 7;
+    moltype->atoms.atom[3].m = 23;
+    moltype->atoms.atom[4].m = 2;
+}
+
+//! Set up initial coordinates.
+std::vector<RVec> initialCoordinates()
+{
+    std::vector<RVec> coordinates;
+    coordinates.emplace_back(-1, 0, 3);
+    coordinates.emplace_back(1.5, 1.5, 4.5);
+    coordinates.emplace_back(1.6, 1.5, 4.5);
+    coordinates.emplace_back(1.6, 1.7, 4.5);
+    coordinates.emplace_back(1, 4, 2);
+
+    coordinates.emplace_back(1, 0, -3);
+    coordinates.emplace_back(-1.5, -1.5, -4.5);
+    coordinates.emplace_back(-1.6, -1.5, -4.5);
+    coordinates.emplace_back(-1.6, -1.7, -4.5);
+    coordinates.emplace_back(-1, -4, -2);
+
+    return coordinates;
+}
+
+using COMInPlaceTestParams = std::tuple<UnitCellType, CenteringType, PbcType>;
+using test::FloatingPointTolerance;
+
+/*! \brief
+ * Test fixture for checking correct molecule COM treatment.
+ */
+class COMInPlaceTest : public ::testing::Test, public ::testing::WithParamInterface<COMInPlaceTestParams>
+{
+public:
+    COMInPlaceTest();
+
+    //! Run the test with the given input for molecule COM.
+    void runTestMolecule(const matrix box);
+    //! Run the test with the given input for residue COM.
+    void runTestResidue(const matrix box);
+
+private:
+    //! Coordinates to use.
+    std::vector<RVec> testCoordinates_;
+    //! Dummy topology to use.
+    gmx_mtop_t testTopology_;
+    //! Reference data holder.
+    TestReferenceData data_;
+    //! Checker for data.
+    TestReferenceChecker checker_;
+};
+
+COMInPlaceTest::COMInPlaceTest() :
+    testCoordinates_(initialCoordinates()),
+    checker_(data_.rootChecker())
+{
+    auto& moltype = testTopology_.moltype.emplace_back();
+    populateMoleculeType(&moltype);
+    auto& molblock       = testTopology_.molblock.emplace_back();
+    molblock.nmol        = 2;
+    molblock.type        = 0;
+    testTopology_.natoms = moltype.atoms.nr * molblock.nmol;
+    testTopology_.finalize();
+    FloatingPointTolerance tolerance(
+            FloatingPointTolerance(1.0e-6, 1.0e-6, 1.0e-8, 1.0e-12, 10000, 100, false));
+    checker_.setDefaultTolerance(tolerance);
+}
+
+void COMInPlaceTest::runTestMolecule(const matrix box)
+{
+    auto params   = GetParam();
+    auto unitcell = std::get<0>(params);
+    auto center   = std::get<1>(params);
+    auto pbcType  = std::get<2>(params);
+    placeCoordinatesWithCOMInBox(pbcType, unitcell, center, box, testCoordinates_, testTopology_,
+                                 COMShiftType::Molecule);
+    std::string testString = "Molecule " + std::string(unitCellTypeNames(unitcell))
+                             + std::string(centerTypeNames(center)) + c_pbcTypeNames[pbcType]
+                             + c_pbcTypeNames[guessPbcType(box)];
+    checker_.checkSequence(std::begin(testCoordinates_), std::end(testCoordinates_), testString.c_str());
+}
+
+void COMInPlaceTest::runTestResidue(const matrix box)
+{
+    auto params   = GetParam();
+    auto unitcell = std::get<0>(params);
+    auto center   = std::get<1>(params);
+    auto pbcType  = std::get<2>(params);
+    placeCoordinatesWithCOMInBox(pbcType, unitcell, center, box, testCoordinates_, testTopology_,
+                                 COMShiftType::Residue);
+    std::string testString = "Residue " + std::string(unitCellTypeNames(unitcell))
+                             + std::string(centerTypeNames(center)) + c_pbcTypeNames[pbcType]
+                             + c_pbcTypeNames[guessPbcType(box)];
+    checker_.checkSequence(std::begin(testCoordinates_), std::end(testCoordinates_), testString.c_str());
+}
+
+TEST(ShiftTest, CoordinateShiftWorks)
+{
+    auto       coords = initialCoordinates();
+    const RVec shift(1.5, -2.2, 0);
+
+    shiftAtoms(shift, coords);
+    EXPECT_FLOAT_EQ(coords[4][0], 2.5);
+    EXPECT_FLOAT_EQ(coords[3][1], -0.5);
+    EXPECT_FLOAT_EQ(coords[9][2], -2);
+}
+
+
+TEST_P(COMInPlaceTest, MatrixDefault)
+{
+    const matrix box = { { 3, 0, 0 }, { 0, 3, 0 }, { 0, 0, 3 } };
+
+    runTestMolecule(box);
+    runTestResidue(box);
+}
+
+INSTANTIATE_TEST_CASE_P(CorrectCoordinates,
+                        COMInPlaceTest,
+                        testing::Combine(::testing::Values(UnitCellType::Compact,
+                                                           UnitCellType::Rectangular,
+                                                           UnitCellType::Triclinic),
+                                         ::testing::Values(CenteringType::Rectangular,
+                                                           CenteringType::Triclinic,
+                                                           CenteringType::Zero),
+                                         ::testing::Values(PbcType::No, PbcType::Xyz, PbcType::XY)));
+
+// TODO add PbcType::Screw once it is fully supported.
+
+} // namespace
+
+} // namespace test
+
+} // namespace gmx
diff --git a/src/gromacs/pbcutil/tests/mshift.cpp b/src/gromacs/pbcutil/tests/mshift.cpp
new file mode 100644 (file)
index 0000000..aaf61e1
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for the graph functionality to make molecules whole that are broken over pbc.
+ *
+ * \author berk Hess <hess@kth.se>
+ * \ingroup module_pbcutil
+ */
+#include "gmxpre.h"
+
+#include "gromacs/pbcutil/mshift.h"
+
+#include <gtest/gtest.h>
+
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/topology/topology.h"
+
+#include "testutils/testasserts.h"
+#include "testutils/testmatchers.h"
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+/* TODO: Actually initialize moltype.atoms.atom when this is converted to C++ */
+
+/*! \brief Returns a molectype for generate a graph
+ *
+ * This moleculetype has the first and last atom not connected to the other atoms
+ * so we test optimizations in the shift code that generates the actual graph only
+ * for the atom range from the first to the last connected atoms.
+ */
+gmx_moltype_t moleculeType()
+{
+    gmx_moltype_t moltype = {};
+
+    moltype.atoms.nr               = 5;
+    moltype.ilist[F_CONSTR].iatoms = { 0, 1, 2 };
+    moltype.ilist[F_ANGLES].iatoms = { 1, 2, 1, 3 };
+
+    return moltype;
+}
+
+//! Box to go with \p coordinates()
+constexpr matrix c_box = { { 3, 0, 0 }, { 0, 3, 0 }, { 0, 0, 3 } };
+
+/*! \brief Coordinates for \p moleculeType() broken over PBC
+ *
+ * The middle 3 atoms all need to be shifted with respect to each other
+ * to make the molecule whole. The first and last atom are not connected,
+ * so their coordinates are irrelevant for this test.
+ */
+std::vector<RVec> coordinates()
+{
+    std::vector<RVec> x;
+
+    x.emplace_back(-1, 0, 3);
+    x.emplace_back(1.5, 1.5, 4.5);
+    x.emplace_back(1.6, 1.5, 1.5);
+    x.emplace_back(1.6, -1.3, 1.5);
+    x.emplace_back(1, 4, 2);
+
+    return x;
+}
+
+/*! \brief Coordinates for \p moleculeType() made whole
+ *
+ * These coordinates assume the the periodic image for the molecule
+ * is chosen the same as the first connected atom.
+ */
+std::vector<RVec> coordinatesWhole()
+{
+    std::vector<RVec> x;
+
+    x.emplace_back(-1, 0, 3);
+    x.emplace_back(1.5, 1.5, 4.5);
+    x.emplace_back(1.6, 1.5, 4.5);
+    x.emplace_back(1.6, 1.7, 4.5);
+    x.emplace_back(1, 4, 2);
+
+    return x;
+}
+
+//! Tests where (un)shifting works to new coordinate buffers
+TEST(MShift, shiftsAndUnshifts)
+{
+    const gmx_moltype_t     molType = moleculeType();
+    const std::vector<RVec> x       = coordinates();
+    GMX_RELEASE_ASSERT(int(x.size()) == molType.atoms.nr, "coordinates should match moltype");
+
+    t_graph graph = mk_graph_moltype(molType);
+    mk_mshift(nullptr, &graph, PbcType::Xyz, c_box, as_rvec_array(x.data()));
+
+    std::vector<RVec> xShifted(molType.atoms.nr);
+    shift_x(&graph, c_box, as_rvec_array(x.data()), as_rvec_array(xShifted.data()));
+    EXPECT_THAT(coordinatesWhole(), Pointwise(RVecEq(defaultFloatTolerance()), xShifted));
+
+    std::vector<RVec> xUnshifted(molType.atoms.nr);
+    unshift_x(&graph, c_box, as_rvec_array(xUnshifted.data()), as_rvec_array(xShifted.data()));
+    EXPECT_THAT(x, Pointwise(RVecEq(defaultFloatTolerance()), xUnshifted));
+}
+
+//! Tests where (un)shifting works in place
+TEST(MShift, shiftsAndUnshiftsSelf)
+{
+    const gmx_moltype_t molType = moleculeType();
+    std::vector<RVec>   x       = coordinates();
+    GMX_RELEASE_ASSERT(int(x.size()) == molType.atoms.nr, "coordinates should match moltype");
+
+    t_graph graph = mk_graph_moltype(molType);
+    mk_mshift(nullptr, &graph, PbcType::Xyz, c_box, as_rvec_array(x.data()));
+
+    shift_self(&graph, c_box, as_rvec_array(x.data()));
+    EXPECT_THAT(coordinatesWhole(), Pointwise(RVecEq(defaultFloatTolerance()), x));
+
+    unshift_self(&graph, c_box, as_rvec_array(x.data()));
+    EXPECT_THAT(coordinates(), Pointwise(RVecEq(defaultFloatTolerance()), x));
+}
+
+} // namespace
+} // namespace test
+} // namespace gmx
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_0.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_0.xml
new file mode 100644 (file)
index 0000000..2bc8411
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule CompactRectangularnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-0.99999994</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.99999994</Real>
+      <Real Name="Y">-1.1920929e-07</Real>
+      <Real Name="Z">-2.9999998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5</Real>
+      <Real Name="Y">-1.5000001</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6000001</Real>
+      <Real Name="Y">-1.5000001</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6000001</Real>
+      <Real Name="Y">-1.7000002</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">-4</Real>
+      <Real Name="Z">-1.9999998</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue CompactRectangularnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.99999994</Real>
+      <Real Name="Y">-1.1920929e-07</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5</Real>
+      <Real Name="Y">-1.5000002</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6000001</Real>
+      <Real Name="Y">-1.5000002</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6000001</Real>
+      <Real Name="Y">-1.7000003</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">-4</Real>
+      <Real Name="Z">-1.9999998</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_1.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_1.xml
new file mode 100644 (file)
index 0000000..aab2041
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule CompactRectangularxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-0.99999994</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue CompactRectangularxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_10.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_10.xml
new file mode 100644 (file)
index 0000000..54727ae
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule RectangularRectangularxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue RectangularRectangularxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_11.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_11.xml
new file mode 100644 (file)
index 0000000..9866f0f
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule RectangularRectangularxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">-2</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue RectangularRectangularxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">-2</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_12.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_12.xml
new file mode 100644 (file)
index 0000000..7e4ff15
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule RectangularTriclinicnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue RectangularTriclinicnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_13.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_13.xml
new file mode 100644 (file)
index 0000000..f1efc1c
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule RectangularTriclinicxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue RectangularTriclinicxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_14.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_14.xml
new file mode 100644 (file)
index 0000000..5c15676
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule RectangularTriclinicxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">-2</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue RectangularTriclinicxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">-2</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_15.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_15.xml
new file mode 100644 (file)
index 0000000..ef8d2a8
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule RectangularZero-Basednoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue RectangularZero-Basednoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_16.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_16.xml
new file mode 100644 (file)
index 0000000..737acdf
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule RectangularZero-Basedxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue RectangularZero-Basedxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_17.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_17.xml
new file mode 100644 (file)
index 0000000..2aea6d1
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule RectangularZero-Basedxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">-2</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue RectangularZero-Basedxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">-2</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_18.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_18.xml
new file mode 100644 (file)
index 0000000..503353c
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule TriclinicRectangularnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue TriclinicRectangularnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_19.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_19.xml
new file mode 100644 (file)
index 0000000..add0f5f
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule TriclinicRectangularxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue TriclinicRectangularxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_2.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_2.xml
new file mode 100644 (file)
index 0000000..9b8eeed
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule CompactRectangularxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-0.99999994</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">-2.9999998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">-1.9999998</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue CompactRectangularxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">-1.9999998</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_20.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_20.xml
new file mode 100644 (file)
index 0000000..4cde230
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule TriclinicRectangularxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue TriclinicRectangularxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_21.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_21.xml
new file mode 100644 (file)
index 0000000..112030f
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule TriclinicTriclinicnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue TriclinicTriclinicnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_22.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_22.xml
new file mode 100644 (file)
index 0000000..cd2192e
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule TriclinicTriclinicxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue TriclinicTriclinicxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_23.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_23.xml
new file mode 100644 (file)
index 0000000..fd3af9d
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule TriclinicTriclinicxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue TriclinicTriclinicxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_24.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_24.xml
new file mode 100644 (file)
index 0000000..6b552b7
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule TriclinicZero-Basednoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue TriclinicZero-Basednoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_25.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_25.xml
new file mode 100644 (file)
index 0000000..334f012
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule TriclinicZero-Basedxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue TriclinicZero-Basedxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_26.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_26.xml
new file mode 100644 (file)
index 0000000..4fdb84d
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule TriclinicZero-Basedxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue TriclinicZero-Basedxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_3.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_3.xml
new file mode 100644 (file)
index 0000000..876fe18
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule CompactTriclinicnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-0.99999994</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.99999994</Real>
+      <Real Name="Y">-1.1920929e-07</Real>
+      <Real Name="Z">-2.9999998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5</Real>
+      <Real Name="Y">-1.5000001</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6000001</Real>
+      <Real Name="Y">-1.5000001</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6000001</Real>
+      <Real Name="Y">-1.7000002</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">-4</Real>
+      <Real Name="Z">-1.9999998</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue CompactTriclinicnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.99999994</Real>
+      <Real Name="Y">-1.1920929e-07</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5</Real>
+      <Real Name="Y">-1.5000002</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6000001</Real>
+      <Real Name="Y">-1.5000002</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6000001</Real>
+      <Real Name="Y">-1.7000003</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">-4</Real>
+      <Real Name="Z">-1.9999998</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_4.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_4.xml
new file mode 100644 (file)
index 0000000..52d89fe
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule CompactTriclinicxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-0.99999994</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue CompactTriclinicxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_5.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_5.xml
new file mode 100644 (file)
index 0000000..9ce84fb
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule CompactTriclinicxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-0.99999994</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">-2.9999998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">-1.9999998</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue CompactTriclinicxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6000001</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">-1.9999998</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_6.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_6.xml
new file mode 100644 (file)
index 0000000..9ae3df8
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule CompactZero-Basednoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6</Real>
+      <Real Name="Y">-1.7</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">-4</Real>
+      <Real Name="Z">-2</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue CompactZero-Basednoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6</Real>
+      <Real Name="Y">-1.7</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">-4</Real>
+      <Real Name="Z">-2</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_7.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_7.xml
new file mode 100644 (file)
index 0000000..f642049
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule CompactZero-Basedxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">-1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">-1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6</Real>
+      <Real Name="Y">-1.7</Real>
+      <Real Name="Z">-1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">-4</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue CompactZero-Basedxyzxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.4</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.4</Real>
+      <Real Name="Y">-1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_8.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_8.xml
new file mode 100644 (file)
index 0000000..39d150b
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule CompactZero-Basedxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6</Real>
+      <Real Name="Y">-1.7</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">-4</Real>
+      <Real Name="Z">-2</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue CompactZero-Basedxyxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.5</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.4</Real>
+      <Real Name="Y">-1.5</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.4</Real>
+      <Real Name="Y">-1.3</Real>
+      <Real Name="Z">4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">-3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">-4.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">-2</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_9.xml b/src/gromacs/pbcutil/tests/refdata/CorrectCoordinates_COMInPlaceTest_MatrixDefault_9.xml
new file mode 100644 (file)
index 0000000..4205c86
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Sequence Name="Molecule RectangularRectangularnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">-1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">4</Real>
+      <Real Name="Z">-1</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4</Real>
+      <Real Name="Y">3</Real>
+      <Real Name="Z">3</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">-1</Real>
+      <Real Name="Z">4</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Residue RectangularRectangularnoxyz">
+    <Int Name="Length">10</Int>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.6</Real>
+      <Real Name="Y">1.7</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">1</Real>
+      <Real Name="Z">2</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1</Real>
+      <Real Name="Y">0</Real>
+      <Real Name="Z">0</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.5</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.5</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4</Real>
+      <Real Name="Y">1.3</Real>
+      <Real Name="Z">1.5</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2</Real>
+      <Real Name="Y">2</Real>
+      <Real Name="Z">1</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
index fa07f8b6848c2c9cc52971964c3e0488e5f07a95..e534415e70a6b3678ed26aa4ecbcab26f4114324 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index db4517bdef49a7f3d1a41a589a6439e812a30a26..d68f6adb151ab54196864695df7a670eeed92d1e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index a93cda1ea6d0638ce7dbf72242bfaf052dc80b5f..67cf362e79100c5a1b87802201b240503a891a6c 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -63,7 +64,6 @@
 #include "gromacs/mdtypes/forceoutput.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/mdtypes/mdatom.h"
 #include "gromacs/mdtypes/state.h"
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/mtop_lookup.h"
@@ -174,7 +174,7 @@ double pull_conversion_factor_internal2userinput(const t_pull_coord* pcrd)
 static void apply_forces_grp_part(const pull_group_work_t* pgrp,
                                   int                      ind_start,
                                   int                      ind_end,
-                                  const t_mdatoms*         md,
+                                  const real*              masses,
                                   const dvec               f_pull,
                                   int                      sign,
                                   rvec*                    f)
@@ -185,7 +185,7 @@ static void apply_forces_grp_part(const pull_group_work_t* pgrp,
     for (int i = ind_start; i < ind_end; i++)
     {
         int    ii    = localAtomIndices[i];
-        double wmass = md->massT[ii];
+        double wmass = masses[ii];
         if (!pgrp->localWeights.empty())
         {
             wmass *= pgrp->localWeights[i];
@@ -200,7 +200,7 @@ static void apply_forces_grp_part(const pull_group_work_t* pgrp,
 
 /* Apply forces in a mass weighted fashion */
 static void apply_forces_grp(const pull_group_work_t* pgrp,
-                             const t_mdatoms*         md,
+                             const real*              masses,
                              const dvec               f_pull,
                              int                      sign,
                              rvec*                    f,
@@ -223,7 +223,7 @@ static void apply_forces_grp(const pull_group_work_t* pgrp,
     {
         if (localAtomIndices.size() <= c_pullMaxNumLocalAtomsSingleThreaded)
         {
-            apply_forces_grp_part(pgrp, 0, localAtomIndices.size(), md, f_pull, sign, f);
+            apply_forces_grp_part(pgrp, 0, localAtomIndices.size(), masses, f_pull, sign, f);
         }
         else
         {
@@ -232,7 +232,7 @@ static void apply_forces_grp(const pull_group_work_t* pgrp,
             {
                 int ind_start = (localAtomIndices.size() * (th + 0)) / nthreads;
                 int ind_end   = (localAtomIndices.size() * (th + 1)) / nthreads;
-                apply_forces_grp_part(pgrp, ind_start, ind_end, md, f_pull, sign, f);
+                apply_forces_grp_part(pgrp, ind_start, ind_end, masses, f_pull, sign, f);
             }
         }
     }
@@ -241,7 +241,7 @@ static void apply_forces_grp(const pull_group_work_t* pgrp,
 /* Apply forces in a mass weighted fashion to a cylinder group */
 static void apply_forces_cyl_grp(const pull_group_work_t* pgrp,
                                  const double             dv_corr,
-                                 const t_mdatoms*         md,
+                                 const real*              masses,
                                  const dvec               f_pull,
                                  double                   f_scal,
                                  int                      sign,
@@ -265,7 +265,7 @@ static void apply_forces_cyl_grp(const pull_group_work_t* pgrp,
             continue;
         }
         int    ii   = localAtomIndices[i];
-        double mass = md->massT[ii];
+        double mass = masses[ii];
         /* The stored axial distance from the cylinder center (dv) needs
          * to be corrected for an offset (dv_corr), which was unknown when
          * we calculated dv.
@@ -288,7 +288,7 @@ static void apply_forces_cyl_grp(const pull_group_work_t* pgrp,
  */
 static void apply_forces_vec_torque(const struct pull_t*     pull,
                                     const pull_coord_work_t* pcrd,
-                                    const t_mdatoms*         md,
+                                    const real*              masses,
                                     rvec*                    f)
 {
     const PullCoordSpatialData& spatialData = pcrd->spatialData;
@@ -314,15 +314,15 @@ static void apply_forces_vec_torque(const struct pull_t*     pull,
     }
 
     /* Apply the force to the groups defining the vector using opposite signs */
-    apply_forces_grp(&pull->group[pcrd->params.group[2]], md, f_perp, -1, f, pull->nthreads);
-    apply_forces_grp(&pull->group[pcrd->params.group[3]], md, f_perp, 1, f, pull->nthreads);
+    apply_forces_grp(&pull->group[pcrd->params.group[2]], masses, f_perp, -1, f, pull->nthreads);
+    apply_forces_grp(&pull->group[pcrd->params.group[3]], masses, f_perp, 1, f, pull->nthreads);
 }
 
 /* Apply forces in a mass weighted fashion */
 static void apply_forces_coord(struct pull_t*               pull,
                                int                          coord,
                                const PullCoordVectorForces& forces,
-                               const t_mdatoms*             md,
+                               const real*                  masses,
                                rvec*                        f)
 {
     /* Here it would be more efficient to use one large thread-parallel
@@ -335,7 +335,7 @@ static void apply_forces_coord(struct pull_t*               pull,
 
     if (pcrd.params.eGeom == epullgCYL)
     {
-        apply_forces_cyl_grp(&pull->dyna[coord], pcrd.spatialData.cyl_dev, md, forces.force01,
+        apply_forces_cyl_grp(&pull->dyna[coord], pcrd.spatialData.cyl_dev, masses, forces.force01,
                              pcrd.scalarForce, -1, f, pull->nthreads);
 
         /* Sum the force along the vector and the radial force */
@@ -344,7 +344,7 @@ static void apply_forces_coord(struct pull_t*               pull,
         {
             f_tot[m] = forces.force01[m] + pcrd.scalarForce * pcrd.spatialData.ffrad[m];
         }
-        apply_forces_grp(&pull->group[pcrd.params.group[1]], md, f_tot, 1, f, pull->nthreads);
+        apply_forces_grp(&pull->group[pcrd.params.group[1]], masses, f_tot, 1, f, pull->nthreads);
     }
     else
     {
@@ -353,24 +353,29 @@ static void apply_forces_coord(struct pull_t*               pull,
             /* We need to apply the torque forces to the pull groups
              * that define the pull vector.
              */
-            apply_forces_vec_torque(pull, &pcrd, md, f);
+            apply_forces_vec_torque(pull, &pcrd, masses, f);
         }
 
         if (pull->group[pcrd.params.group[0]].params.nat > 0)
         {
-            apply_forces_grp(&pull->group[pcrd.params.group[0]], md, forces.force01, -1, f, pull->nthreads);
+            apply_forces_grp(&pull->group[pcrd.params.group[0]], masses, forces.force01, -1, f,
+                             pull->nthreads);
         }
-        apply_forces_grp(&pull->group[pcrd.params.group[1]], md, forces.force01, 1, f, pull->nthreads);
+        apply_forces_grp(&pull->group[pcrd.params.group[1]], masses, forces.force01, 1, f, pull->nthreads);
 
         if (pcrd.params.ngroup >= 4)
         {
-            apply_forces_grp(&pull->group[pcrd.params.group[2]], md, forces.force23, -1, f, pull->nthreads);
-            apply_forces_grp(&pull->group[pcrd.params.group[3]], md, forces.force23, 1, f, pull->nthreads);
+            apply_forces_grp(&pull->group[pcrd.params.group[2]], masses, forces.force23, -1, f,
+                             pull->nthreads);
+            apply_forces_grp(&pull->group[pcrd.params.group[3]], masses, forces.force23, 1, f,
+                             pull->nthreads);
         }
         if (pcrd.params.ngroup >= 6)
         {
-            apply_forces_grp(&pull->group[pcrd.params.group[4]], md, forces.force45, -1, f, pull->nthreads);
-            apply_forces_grp(&pull->group[pcrd.params.group[5]], md, forces.force45, 1, f, pull->nthreads);
+            apply_forces_grp(&pull->group[pcrd.params.group[4]], masses, forces.force45, -1, f,
+                             pull->nthreads);
+            apply_forces_grp(&pull->group[pcrd.params.group[5]], masses, forces.force45, 1, f,
+                             pull->nthreads);
         }
     }
 }
@@ -1443,7 +1448,7 @@ static void check_external_potential_registration(const struct pull_t* pull)
 void apply_external_pull_coord_force(struct pull_t*        pull,
                                      int                   coord_index,
                                      double                coord_force,
-                                     const t_mdatoms*      mdatoms,
+                                     const real*           masses,
                                      gmx::ForceWithVirial* forceWithVirial)
 {
     pull_coord_work_t* pcrd;
@@ -1476,7 +1481,7 @@ void apply_external_pull_coord_force(struct pull_t*        pull,
             forceWithVirial->addVirialContribution(virial);
         }
 
-        apply_forces_coord(pull, coord_index, pullCoordForces, mdatoms,
+        apply_forces_coord(pull, coord_index, pullCoordForces, masses,
                            as_rvec_array(forceWithVirial->force_.data()));
     }
 
@@ -1511,7 +1516,7 @@ static PullCoordVectorForces do_pull_pot_coord(struct pull_t* pull,
 }
 
 real pull_potential(struct pull_t*        pull,
-                    const t_mdatoms*      md,
+                    const real*           masses,
                     t_pbc*                pbc,
                     const t_commrec*      cr,
                     double                t,
@@ -1534,7 +1539,7 @@ real pull_potential(struct pull_t*        pull,
     {
         real dVdl = 0;
 
-        pull_calc_coms(cr, pull, md, pbc, t, x, nullptr);
+        pull_calc_coms(cr, pull, masses, pbc, t, x, nullptr);
 
         rvec*      f             = as_rvec_array(force->force_.data());
         matrix     virial        = { { 0 } };
@@ -1555,7 +1560,7 @@ real pull_potential(struct pull_t*        pull,
                     pull, c, pbc, t, lambda, &V, computeVirial ? virial : nullptr, &dVdl);
 
             /* Distribute the force over the atoms in the pulled groups */
-            apply_forces_coord(pull, c, pullCoordForces, md, f);
+            apply_forces_coord(pull, c, pullCoordForces, masses, f);
         }
 
         if (MASTER(cr))
@@ -1574,7 +1579,7 @@ real pull_potential(struct pull_t*        pull,
 }
 
 void pull_constraint(struct pull_t*   pull,
-                     const t_mdatoms* md,
+                     const real*      masses,
                      t_pbc*           pbc,
                      const t_commrec* cr,
                      double           dt,
@@ -1588,7 +1593,7 @@ void pull_constraint(struct pull_t*   pull,
 
     if (pull->comm.bParticipate)
     {
-        pull_calc_coms(cr, pull, md, pbc, t, x, xp);
+        pull_calc_coms(cr, pull, masses, pbc, t, x, xp);
 
         do_constraint(pull, pbc, xp, v, MASTER(cr), vir, dt, t);
     }
@@ -1717,7 +1722,7 @@ void dd_make_local_pull_groups(const t_commrec* cr, struct pull_t* pull)
                                "date prev. COM "
                                "to bcast here as well as to e.g. checkpointing");
 
-                    gmx_bcast(sizeof(dvec), group.x_prev_step, cr);
+                    gmx_bcast(sizeof(dvec), group.x_prev_step, cr->mpi_comm_mygroup);
                 }
             }
         }
@@ -1983,6 +1988,10 @@ struct pull_t* init_pull(FILE*                     fplog,
                           epull_names[epullUMBRELLA]);
             }
 
+            GMX_RELEASE_ASSERT(
+                    !ir->useMts,
+                    "Constraint pulling can not be combined with multiple time stepping");
+
             pull->bConstraint = TRUE;
         }
         else
@@ -2052,11 +2061,11 @@ struct pull_t* init_pull(FILE*                     fplog,
     pull->numUnregisteredExternalPotentials             = pull->numCoordinatesWithExternalPotential;
     pull->numExternalPotentialsStillToBeAppliedThisStep = 0;
 
-    pull->ePBC = ir->ePBC;
-    switch (pull->ePBC)
+    pull->pbcType = ir->pbcType;
+    switch (pull->pbcType)
     {
-        case epbcNONE: pull->npbcdim = 0; break;
-        case epbcXY: pull->npbcdim = 2; break;
+        case PbcType::No: pull->npbcdim = 0; break;
+        case PbcType::XY: pull->npbcdim = 2; break;
         default: pull->npbcdim = 3; break;
     }
 
@@ -2293,7 +2302,7 @@ static void destroy_pull(struct pull_t* pull)
 
 void preparePrevStepPullCom(const t_inputrec* ir,
                             pull_t*           pull_work,
-                            const t_mdatoms*  md,
+                            const real*       masses,
                             t_state*          state,
                             const t_state*    state_global,
                             const t_commrec*  cr,
@@ -2313,15 +2322,16 @@ void preparePrevStepPullCom(const t_inputrec* ir,
         if (PAR(cr))
         {
             /* Only the master rank has the checkpointed COM from the previous step */
-            gmx_bcast(sizeof(double) * state->pull_com_prev_step.size(), &state->pull_com_prev_step[0], cr);
+            gmx_bcast(sizeof(double) * state->pull_com_prev_step.size(),
+                      &state->pull_com_prev_step[0], cr->mpi_comm_mygroup);
         }
         setPrevStepPullComFromState(pull_work, state);
     }
     else
     {
         t_pbc pbc;
-        set_pbc(&pbc, ir->ePBC, state->box);
-        initPullComFromPrevStep(cr, pull_work, md, &pbc, state->x.rvec_array());
+        set_pbc(&pbc, ir->pbcType, state->box);
+        initPullComFromPrevStep(cr, pull_work, masses, &pbc, state->x.rvec_array());
         updatePrevStepPullCom(pull_work, state);
     }
 }
index de6ec28f5abcc90d666f2036f18afabfc727392c..faf4152bbf6cdf3ff2a2e9b2934bbc9c2f1a891f 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -66,7 +67,6 @@ struct pull_t;
 struct t_commrec;
 struct t_filenm;
 struct t_inputrec;
-struct t_mdatoms;
 struct t_pbc;
 class t_state;
 
@@ -152,13 +152,13 @@ void register_external_pull_potential(struct pull_t* pull, int coord_index, cons
  * \param[in,out] pull             The pull struct.
  * \param[in]     coord_index      The pull coordinate index to set the force for.
  * \param[in]     coord_force      The scalar force for the pull coordinate.
- * \param[in]     mdatoms          Atom properties, only masses are used.
+ * \param[in]     masses           Atoms masses.
  * \param[in,out] forceWithVirial  Force and virial buffers.
  */
 void apply_external_pull_coord_force(struct pull_t*        pull,
                                      int                   coord_index,
                                      double                coord_force,
-                                     const t_mdatoms*      mdatoms,
+                                     const real*           masses,
                                      gmx::ForceWithVirial* forceWithVirial);
 
 
@@ -172,7 +172,7 @@ void clear_pull_forces(struct pull_t* pull);
 /*! \brief Determine the COM pull forces and add them to f, return the potential
  *
  * \param[in,out] pull   The pull struct.
- * \param[in]     md     All atoms.
+ * \param[in]     masses Atoms masses.
  * \param[in]     pbc    Information struct about periodicity.
  * \param[in]     cr     Struct for communication info.
  * \param[in]     t      Time.
@@ -184,7 +184,7 @@ void clear_pull_forces(struct pull_t* pull);
  * \returns The pull potential energy.
  */
 real pull_potential(struct pull_t*        pull,
-                    const t_mdatoms*      md,
+                    const real*           masses,
                     struct t_pbc*         pbc,
                     const t_commrec*      cr,
                     double                t,
@@ -198,7 +198,7 @@ real pull_potential(struct pull_t*        pull,
  * and also constrain v when v != NULL.
  *
  * \param[in,out] pull   The pull data.
- * \param[in]     md     All atoms.
+ * \param[in]     masses Atoms masses.
  * \param[in]     pbc    Information struct about periodicity.
  * \param[in]     cr     Struct for communication info.
  * \param[in]     dt     The time step length.
@@ -209,7 +209,7 @@ real pull_potential(struct pull_t*        pull,
  * \param[in,out] vir    The virial, which, if != NULL, gets a pull correction.
  */
 void pull_constraint(struct pull_t*   pull,
-                     const t_mdatoms* md,
+                     const real*      masses,
                      struct t_pbc*    pbc,
                      const t_commrec* cr,
                      double           dt,
@@ -259,7 +259,7 @@ void finish_pull(struct pull_t* pull);
  *
  * \param[in] cr       Struct for communication info.
  * \param[in] pull     The pull data structure.
- * \param[in] md       All atoms.
+ * \param[in] masses   Atoms masses.
  * \param[in] pbc      Information struct about periodicity.
  * \param[in] t        Time, only used for cylinder ref.
  * \param[in] x        The local positions.
@@ -268,7 +268,7 @@ void finish_pull(struct pull_t* pull);
  */
 void pull_calc_coms(const t_commrec* cr,
                     pull_t*          pull,
-                    const t_mdatoms* md,
+                    const real*      masses,
                     t_pbc*           pbc,
                     double           t,
                     const rvec       x[],
@@ -370,7 +370,7 @@ void updatePrevStepPullCom(struct pull_t* pull, t_state* state);
  *
  * \param[in] ir                     The input options/settings of the simulation.
  * \param[in] pull_work              The COM pull force calculation data structure
- * \param[in] md                     All atoms.
+ * \param[in] masses                 Atoms masses.
  * \param[in] state                  The local (to this rank) state.
  * \param[in] state_global           The global state.
  * \param[in] cr                     Struct for communication info.
@@ -378,7 +378,7 @@ void updatePrevStepPullCom(struct pull_t* pull, t_state* state);
  */
 void preparePrevStepPullCom(const t_inputrec* ir,
                             pull_t*           pull_work,
-                            const t_mdatoms*  md,
+                            const real*       masses,
                             t_state*          state,
                             const t_state*    state_global,
                             const t_commrec*  cr,
@@ -388,10 +388,10 @@ void preparePrevStepPullCom(const t_inputrec* ir,
  *
  * \param[in] cr       Struct for communication info.
  * \param[in] pull     The pull data structure.
- * \param[in] md       All atoms.
+ * \param[in] masses   Atoms masses.
  * \param[in] pbc      Information struct about periodicity.
  * \param[in] x        The local positions.
  */
-void initPullComFromPrevStep(const t_commrec* cr, pull_t* pull, const t_mdatoms* md, t_pbc* pbc, const rvec x[]);
+void initPullComFromPrevStep(const t_commrec* cr, pull_t* pull, const real* masses, t_pbc* pbc, const rvec x[]);
 
 #endif
index bab31a3234ca539ef99f26d9d8b9136b24f573fb..1973e5f116034ecdb0c190e1b487ace9b6e22610 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -70,6 +71,7 @@ static const int c_pullMaxNumLocalAtomsSingleThreaded = 1;
 #endif
 
 class PullHistory;
+enum class PbcType : int;
 
 enum
 {
@@ -226,7 +228,7 @@ struct pull_t
     gmx_bool bConstraint; /* Are there constrained coordinates? */
     gmx_bool bAngle;      /* Are there angle geometry coordinates? */
 
-    int      ePBC;      /* the boundary conditions */
+    PbcType  pbcType;   /* 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 */
index 5c831c016648e74afe55e734ac39f7b62aafb903..17cb7bdd8cf6e9b6e9ce0e9e4f05910d02f9f823 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -3493,7 +3494,7 @@ static void init_rot_group(FILE*            fplog,
 #if GMX_MPI
         if (PAR(cr))
         {
-            gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
+            gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr->mpi_comm_mygroup);
         }
 #endif
     }
@@ -3528,7 +3529,7 @@ static void init_rot_group(FILE*            fplog,
 #if GMX_MPI
         if (PAR(cr))
         {
-            gmx_bcast(erg->rotg->nat * sizeof(erg->xc_old[0]), erg->xc_old, cr);
+            gmx_bcast(erg->rotg->nat * sizeof(erg->xc_old[0]), erg->xc_old, cr->mpi_comm_mygroup);
         }
 #endif
     }
@@ -3635,14 +3636,7 @@ std::unique_ptr<gmx::EnforcedRotation> init_rot(FILE*                       fplo
     er->restartWithAppending = (startingBehavior == gmx::StartingBehavior::RestartWithAppending);
 
     /* When appending, skip first output to avoid duplicate entries in the data files */
-    if (er->restartWithAppending)
-    {
-        er->bOut = FALSE;
-    }
-    else
-    {
-        er->bOut = TRUE;
-    }
+    er->bOut = er->restartWithAppending;
 
     if (MASTER(cr) && er->bOut)
     {
@@ -3677,7 +3671,7 @@ std::unique_ptr<gmx::EnforcedRotation> init_rot(FILE*                       fplo
          * When ir->bContinuation=TRUE this has already been done, but ok. */
         snew(x_pbc, mtop->natoms);
         copy_rvecn(globalState->x.rvec_array(), x_pbc, 0, mtop->natoms);
-        do_pbc_first_mtop(nullptr, ir->ePBC, globalState->box, mtop, x_pbc);
+        do_pbc_first_mtop(nullptr, ir->pbcType, globalState->box, mtop, x_pbc);
         /* All molecules will be whole now, but not necessarily in the home box.
          * Additionally, if a rotation group consists of more than one molecule
          * (e.g. two strands of DNA), each one of them can end up in a different
index a0626386cd570df8799b060004030890fabc069c..b8af04c51e8f61ed7316058f8ffc30bb164a1b9a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 92dc7ef24ce035306b5ede508f06ab6834ac8613..f3163957deb92e91f292cd82b02cdfef3796cfc2 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -178,7 +179,7 @@ static void pull_set_pbcatoms(const t_commrec* cr, struct pull_t* pull, const rv
 }
 
 static void
-make_cyl_refgrps(const t_commrec* cr, pull_t* pull, const t_mdatoms* md, t_pbc* pbc, double t, const rvec* x)
+make_cyl_refgrps(const t_commrec* cr, pull_t* pull, const real* masses, t_pbc* pbc, double t, const rvec* x)
 {
     pull_comm_t* comm = &pull->comm;
 
@@ -258,7 +259,7 @@ make_cyl_refgrps(const t_commrec* cr, pull_t* pull, const t_mdatoms* md, t_pbc*
                 {
                     /* add atom to sum of COM and to weight array */
 
-                    double mass = md->massT[atomIndex];
+                    double mass = masses[atomIndex];
                     /* The radial weight function is 1-2x^2+x^4,
                      * where x=r/cylinder_r. Since this function depends
                      * on the radial component, we also get radial forces
@@ -522,13 +523,7 @@ static void sum_com_part_cosweight(const pull_group_work_t* pgrp,
         && ((__INTEL_COMPILER == 1900 && __INTEL_COMPILER_UPDATE >= 5) || __INTEL_COMPILER >= 1910)
 #    pragma intel optimization_level 2
 #endif
-void pull_calc_coms(const t_commrec* cr,
-                    pull_t*          pull,
-                    const t_mdatoms* md,
-                    t_pbc*           pbc,
-                    double           t,
-                    const rvec       x[],
-                    rvec*            xp)
+void pull_calc_coms(const t_commrec* cr, pull_t* pull, const real* masses, t_pbc* pbc, double t, const rvec x[], rvec* xp)
 {
     real         twopi_box = 0;
     pull_comm_t* comm;
@@ -616,7 +611,7 @@ void pull_calc_coms(const t_commrec* cr,
                  * in that case a check group mass != 0 has been done before.
                  */
                 if (pgrp->params.nat == 1 && pgrp->atomSet.numAtomsLocal() == 1
-                    && md->massT[pgrp->atomSet.localIndex()[0]] == 0)
+                    && masses[pgrp->atomSet.localIndex()[0]] == 0)
                 {
                     GMX_ASSERT(xp == nullptr,
                                "We should not have groups with zero mass with constraints, i.e. "
@@ -633,8 +628,8 @@ void pull_calc_coms(const t_commrec* cr,
                 }
                 else if (pgrp->atomSet.numAtomsLocal() <= c_pullMaxNumLocalAtomsSingleThreaded)
                 {
-                    sum_com_part(pgrp, 0, pgrp->atomSet.numAtomsLocal(), x, xp, md->massT, pbc,
-                                 x_pbc, &comSumsTotal);
+                    sum_com_part(pgrp, 0, pgrp->atomSet.numAtomsLocal(), x, xp, masses, pbc, x_pbc,
+                                 &comSumsTotal);
                 }
                 else
                 {
@@ -643,7 +638,7 @@ void pull_calc_coms(const t_commrec* cr,
                     {
                         int ind_start = (pgrp->atomSet.numAtomsLocal() * (t + 0)) / pull->nthreads;
                         int ind_end   = (pgrp->atomSet.numAtomsLocal() * (t + 1)) / pull->nthreads;
-                        sum_com_part(pgrp, ind_start, ind_end, x, xp, md->massT, pbc, x_pbc,
+                        sum_com_part(pgrp, ind_start, ind_end, x, xp, masses, pbc, x_pbc,
                                      &pull->comSums[t]);
                     }
 
@@ -683,7 +678,7 @@ void pull_calc_coms(const t_commrec* cr,
                     int ind_start = (pgrp->atomSet.numAtomsLocal() * (t + 0)) / pull->nthreads;
                     int ind_end   = (pgrp->atomSet.numAtomsLocal() * (t + 1)) / pull->nthreads;
                     sum_com_part_cosweight(pgrp, ind_start, ind_end, pull->cosdim, twopi_box, x, xp,
-                                           md->massT, &pull->comSums[t]);
+                                           masses, &pull->comSums[t]);
                 }
 
                 /* Reduce the thread contributions to comSums[0] */
@@ -813,7 +808,7 @@ void pull_calc_coms(const t_commrec* cr,
     if (pull->bCylinder)
     {
         /* Calculate the COMs for the cyclinder reference groups */
-        make_cyl_refgrps(cr, pull, md, pbc, t, x);
+        make_cyl_refgrps(cr, pull, masses, pbc, t, x);
     }
 }
 
@@ -910,7 +905,7 @@ static bool pullGroupObeysPbcRestrictions(const pull_group_work_t& group,
 
 int pullCheckPbcWithinGroups(const pull_t& pull, const rvec* x, const t_pbc& pbc, real pbcMargin)
 {
-    if (pbc.ePBC == epbcNONE)
+    if (pbc.pbcType == PbcType::No)
     {
         return -1;
     }
@@ -952,7 +947,7 @@ bool pullCheckPbcWithinGroup(const pull_t&                  pull,
                              int                            groupNr,
                              real                           pbcMargin)
 {
-    if (pbc.ePBC == epbcNONE)
+    if (pbc.pbcType == PbcType::No)
     {
         return true;
     }
@@ -1029,7 +1024,7 @@ void allocStatePrevStepPullCom(t_state* state, const pull_t* pull)
     }
 }
 
-void initPullComFromPrevStep(const t_commrec* cr, pull_t* pull, const t_mdatoms* md, t_pbc* pbc, const rvec x[])
+void initPullComFromPrevStep(const t_commrec* cr, pull_t* pull, const real* masses, t_pbc* pbc, const rvec x[])
 {
     pull_comm_t* comm   = &pull->comm;
     size_t       ngroup = pull->group.size();
@@ -1078,8 +1073,8 @@ void initPullComFromPrevStep(const t_commrec* cr, pull_t* pull, const t_mdatoms*
 
             if (pgrp->atomSet.numAtomsLocal() <= c_pullMaxNumLocalAtomsSingleThreaded)
             {
-                sum_com_part(pgrp, 0, pgrp->atomSet.numAtomsLocal(), x, nullptr, md->massT, pbc,
-                             x_pbc, &comSumsTotal);
+                sum_com_part(pgrp, 0, pgrp->atomSet.numAtomsLocal(), x, nullptr, masses, pbc, x_pbc,
+                             &comSumsTotal);
             }
             else
             {
@@ -1088,7 +1083,7 @@ void initPullComFromPrevStep(const t_commrec* cr, pull_t* pull, const t_mdatoms*
                 {
                     int ind_start = (pgrp->atomSet.numAtomsLocal() * (t + 0)) / pull->nthreads;
                     int ind_end   = (pgrp->atomSet.numAtomsLocal() * (t + 1)) / pull->nthreads;
-                    sum_com_part(pgrp, ind_start, ind_end, x, nullptr, md->massT, pbc, x_pbc,
+                    sum_com_part(pgrp, ind_start, ind_end, x, nullptr, masses, pbc, x_pbc,
                                  &pull->comSums[t]);
                 }
 
index 537fbb6220b3a097df10bdafa3e0a7b4e9f7dd72..99e8e7ae7a884e746de5cd9371e0b65718674bbe 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2016, by the GROMACS development team, led by
+# Copyright (c) 2016,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,5 +33,7 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(PullTest  pull-test
-  pull.cpp)
+    CPP_SOURCE_FILES
+        pull.cpp
+        )
 
index 416f02deb152424eb7d63543aa63582db17eb624..fe8e604d6c656ab620a684173b69717b159e7fb8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -70,12 +70,12 @@ class PullTest : public ::testing::Test
 protected:
     PullTest() {}
 
-    void test(int epbc, matrix box)
+    static void test(PbcType pbcType, matrix box)
     {
         t_pbc pbc;
 
         // PBC stuff
-        set_pbc(&pbc, epbc, box);
+        set_pbc(&pbc, pbcType, box);
 
         GMX_ASSERT(pbc.ndim_ePBC >= 1 && pbc.ndim_ePBC <= DIM,
                    "Tests only support PBC along at least x and at most x, y, and z");
@@ -161,35 +161,35 @@ TEST_F(PullTest, MaxPullDistanceXyzScrewBox)
 {
     matrix box = { { 10, 0, 0 }, { 0, 10, 0 }, { 0, 0, 10 } };
 
-    test(epbcSCREW, box);
+    test(PbcType::Screw, box);
 }
 
 TEST_F(PullTest, MaxPullDistanceXyzCubicBox)
 {
     matrix box = { { 10, 0, 0 }, { 0, 10, 0 }, { 0, 0, 10 } };
 
-    test(epbcXYZ, box);
+    test(PbcType::Xyz, box);
 }
 
 TEST_F(PullTest, MaxPullDistanceXyzTricBox)
 {
     matrix box = { { 10, 0, 0 }, { 3, 10, 0 }, { 3, 4, 10 } };
 
-    test(epbcXYZ, box);
+    test(PbcType::Xyz, box);
 }
 
 TEST_F(PullTest, MaxPullDistanceXyzLongBox)
 {
     matrix box = { { 10, 0, 0 }, { 0, 10, 0 }, { 0, 0, 30 } };
 
-    test(epbcXYZ, box);
+    test(PbcType::Xyz, box);
 }
 
 TEST_F(PullTest, MaxPullDistanceXySkewedBox)
 {
     matrix box = { { 10, 0, 0 }, { 5, 8, 0 }, { 0, 0, 0 } };
 
-    test(epbcXY, box);
+    test(PbcType::XY, box);
 }
 
 } // namespace
index e40918c3479756ee7cfe88e71bbb597193ef58bf..9618198a3e76f7d5d3ded0022ff63d0e2a307030 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(RandomUnitTests random-test
-                  exponentialdistribution.cpp
-                  gammadistribution.cpp
-                  normaldistribution.cpp
-                  seed.cpp
-                  tabulatednormaldistribution.cpp
-                  threefry.cpp
-                  uniformintdistribution.cpp
-                  uniformrealdistribution.cpp
-                  )
+    CPP_SOURCE_FILES
+        exponentialdistribution.cpp
+        gammadistribution.cpp
+        normaldistribution.cpp
+        seed.cpp
+        tabulatednormaldistribution.cpp
+        threefry.cpp
+        uniformintdistribution.cpp
+        uniformrealdistribution.cpp
+        )
index a90e320f2fc807052d9af1590e0fc563ae1c908b..17a1e9240fd383c2990466a91d134fbabd65cfde 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,6 +52,7 @@
 
 #include <algorithm>
 #include <limits>
+#include <type_traits>
 
 #include "gromacs/math/functions.h"
 #include "gromacs/utility/basedefinitions.h"
@@ -106,13 +107,18 @@ RealType generateCanonical(Rng& g)
     // No point in using more bits than fit in RealType
     const uint64_t digits   = std::numeric_limits<RealType>::digits;
     const uint64_t realBits = std::min(digits, static_cast<uint64_t>(Bits));
-    const uint64_t range    = Rng::max() - Rng::min() + uint64_t(1);
-    uint64_t       log2R    = (range == 0) ? std::numeric_limits<uint64_t>::digits : log2I(range);
+    const uint64_t log2R    = std::numeric_limits<typename Rng::result_type>::digits;
     uint64_t       k        = realBits / log2R + (realBits % log2R != 0) + (realBits == 0);
-    RealType       r        = Rng::max() - Rng::min() + RealType(1);
-    RealType       s        = g() - Rng::min();
-    RealType       base     = r;
-    RealType       result;
+    // Note that Rng::max and Rng::min are typically an integer type.
+    // Only unsigned integer types can express the range using the
+    // same type. Converting to RealType before computing the range
+    // would work but we have no need for that.
+    static_assert(std::is_unsigned_v<decltype(Rng::max())> && std::is_unsigned_v<decltype(Rng::min())>,
+                  "Rng::max and Rng::min must be unsigned");
+    RealType r    = RealType(Rng::max() - Rng::min()) + RealType(1);
+    RealType s    = g() - Rng::min();
+    RealType base = r;
+    RealType result;
 
     for (uint64_t i = 1; i < k; ++i)
     {
index e1e7f65a88f77d346fdf55813cb9fbefa56af324..b1dcb03d09c0f3eef63b75d5b6e2062fc581d262 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -72,9 +72,9 @@ void RestraintForceProvider::calculateForces(const ForceProviderInput& forceProv
     GMX_ASSERT(mdatoms.homenr >= 0, "number of home atoms must be non-negative.");
 
     const auto& box = forceProviderInput.box_;
-    GMX_ASSERT(check_box(-1, box) == nullptr, "Invalid box.");
+    GMX_ASSERT(check_box(PbcType::Unset, box) == nullptr, "Invalid box.");
     t_pbc pbc{};
-    set_pbc(&pbc, -1, box);
+    set_pbc(&pbc, PbcType::Unset, box);
 
     const auto& x  = forceProviderInput.x_;
     const auto& cr = forceProviderInput.cr_;
@@ -119,7 +119,7 @@ void RestraintForceProvider::calculateForces(const ForceProviderInput& forceProv
     {
         // Note: this assumes that all ranks are hitting this line, which is not generally true.
         // I need to find the right subcommunicator. What I really want is a _scoped_ communicator...
-        gmx_barrier(&cr);
+        gmx_barrier(cr.mpi_comm_mygroup);
     }
 
     // Apply restraint on all thread ranks only after any updates have been made.
@@ -158,16 +158,6 @@ RestraintMDModuleImpl::RestraintMDModuleImpl(std::shared_ptr<IRestraintPotential
     GMX_ASSERT(forceProvider_, "Class invariant implies non-null ForceProvider.");
 }
 
-IMdpOptionProvider* RestraintMDModuleImpl::mdpOptionProvider()
-{
-    return nullptr;
-}
-
-IMDOutputProvider* RestraintMDModuleImpl::outputProvider()
-{
-    return nullptr;
-}
-
 void RestraintMDModuleImpl::initForceProviders(ForceProviders* forceProviders)
 {
     GMX_ASSERT(forceProvider_, "Class invariant implies non-null ForceProvider member.");
@@ -204,6 +194,10 @@ std::unique_ptr<RestraintMDModule> RestraintMDModule::create(std::shared_ptr<IRe
     return newModule;
 }
 
+void RestraintMDModule::subscribeToSimulationSetupNotifications(MdModulesNotifier* /*notifier*/) {}
+
+void RestraintMDModule::subscribeToPreProcessingNotifications(MdModulesNotifier* /*notifier*/) {}
+
 // private constructor to implement static create() method.
 RestraintMDModule::RestraintMDModule(std::unique_ptr<RestraintMDModuleImpl> restraint) :
     impl_{ std::move(restraint) }
index c6ec4b56153a883d0f0107a34ec9079ebfd60320..2a0c75784a93de6f2f8580a1ea7983829fb3a57c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,6 +53,7 @@ namespace gmx
 
 // Forward declaration to allow opaque pointer to library internal class.
 class RestraintMDModuleImpl;
+struct MdModulesNotifier;
 
 /*! \libinternal \ingroup module_restraint
  * \brief MDModule wrapper for Restraint implementations.
@@ -115,6 +116,11 @@ public:
      */
     void initForceProviders(ForceProviders* forceProviders) override;
 
+    //! Subscribe to simulation setup notifications
+    void subscribeToSimulationSetupNotifications(MdModulesNotifier* notifier) override;
+    //! Subscribe to pre processing notifications
+    void subscribeToPreProcessingNotifications(MdModulesNotifier* notifier) override;
+
 private:
     /*!
      * \brief Private implementation opaque pointer.
index b1a62a9d69440782663cc74d41df7af8d595d04a..328d0ba6bc4135c49c5a8923b5b146ce91ad32b2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -290,16 +290,6 @@ public:
 
     ~RestraintMDModuleImpl();
 
-    /*!
-     * \brief Unused implementation of IMDModule aspect
-     */
-    IMdpOptionProvider* mdpOptionProvider();
-
-    /*!
-     * \brief Unused implementation of IMDModule aspect
-     */
-    IMDOutputProvider* outputProvider();
-
     /*!
      * \brief Implement IMDModule interface.
      *
index 3d5a420fd1c5ccc159a6f9eb2b88c082fce1c84b..6f09cf42e6b55265073bcfeb64eb2789d8b4edda 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2018, by the GROMACS development team, led by
+# Copyright (c) 2018,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,4 +33,6 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(RestraintTests restraintpotential-test
-                  manager.cpp)
+    CPP_SOURCE_FILES
+        manager.cpp
+        )
index a89242016c26a800f3c9a2c4c902688614e5697c..a37d6fe133bbd27f157804c6b1cf4a25b5dbe59b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 9c83ba1dfaa8f717b4a8dbcc7745361f89229771..e621fa58813b9033bb42c1d709a304e2ffe9c414 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2012,2013,2014,2015, The GROMACS development team.
+# Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
 # Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
@@ -55,7 +55,6 @@ else()
     gmx_target_warning_suppression(scanner -Wno-unused-parameter HAS_NO_UNUSED_PARAMETER)
     gmx_target_warning_suppression(scanner -Wno-missing-declarations HAS_NO_MISSING_DECLARATIONS)
     gmx_target_warning_suppression(scanner -Wno-null-conversion HAS_NO_NULL_CONVERSIONS)
-    gmx_target_warning_suppression(scanner -wd1419 HAS_DECL_IN_SOURCE)
 endif()
 list(APPEND libgromacs_object_library_dependencies scanner)
 set(libgromacs_object_library_dependencies ${libgromacs_object_library_dependencies} PARENT_SCOPE)
index e44c86467525ca591385f81015bf91e2f1ec12c8..155f942aed2b8f45e68384ac7d1cbc41751a282f 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 2547a7ba1edafd5e07c480861839c10a09c78099..52a26628d936f2b75bb2a1fd77c2674c7708e4a7 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -2584,22 +2586,7 @@ static void free_item_compilerdata(const SelectionTreeElementPointer& sel)
 namespace gmx
 {
 
-SelectionCompiler::SelectionCompiler() {}
-
-/*!
- * \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.
- */
-void SelectionCompiler::compile(SelectionCollection* coll)
+void compileSelection(SelectionCollection* coll)
 {
     gmx_ana_selcollection_t*    sc = &coll->impl_->sc_;
     gmx_sel_evaluate_t          evaldata;
@@ -2607,7 +2594,8 @@ void SelectionCompiler::compile(SelectionCollection* coll)
     e_poscalc_t                 post;
     size_t                      i;
     int                         flags;
-    bool bDebug = (coll->impl_->debugLevel_ >= 2 && coll->impl_->debugLevel_ != 3);
+    bool bDebug = (coll->impl_->debugLevel_ == SelectionCollection::Impl::DebugLevel::Compiled
+                   && coll->impl_->debugLevel_ == SelectionCollection::Impl::DebugLevel::Full);
 
     /* FIXME: Clean up the collection on exceptions */
 
index 08ff2c51a608c4843f603e7a9b1928fc16851638..97c9fb99f8a7f20c245446f0f96487f516f78d98 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2013,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2013,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -51,23 +51,26 @@ class SelectionCollection;
  * \brief
  * Implements selection compilation.
  *
- * This class is used to implement SelectionCollection::compile().
+ * This function is used to implement SelectionCollection::compile().
  * It prepares the selections in a selection collection for evaluation and
  * performs some optimizations.
  *
+ * 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 coll is cleared.
+ *
+ * The covered fraction information in \p coll is initialized to
+ * \ref CFRAC_NONE.
+ *
  * See \ref page_module_selection_compiler.
  *
+ * \param[in, out] coll Selection collection to work on.
+ *
  * \ingroup module_selection
  */
-class SelectionCompiler
-{
-public:
-    //! Creates a selection compiler.
-    SelectionCompiler();
-
-    //! Compiles the given selection collection.
-    void compile(SelectionCollection* coll);
-};
+void compileSelection(SelectionCollection* coll);
 
 } // namespace gmx
 
index a182c4988bc348819921faa83bbce6a37ac68153..fd2be1443437a43bf7c4dee377c195985ed0b1c8 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -417,6 +419,7 @@ SelectionEvaluator::SelectionEvaluator() {}
  * This is the only function that user code should call if they want to
  * evaluate a selection for a new frame.
  */
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
 void SelectionEvaluator::evaluate(SelectionCollection* coll, t_trxframe* fr, t_pbc* pbc)
 {
     gmx_ana_selcollection_t* sc = &coll->impl_->sc_;
@@ -462,6 +465,7 @@ void SelectionEvaluator::evaluate(SelectionCollection* coll, t_trxframe* fr, t_p
  * \param[in,out] coll  The selection collection to evaluate.
  * \param[in]     nframes Total number of frames.
  */
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
 void SelectionEvaluator::evaluateFinal(SelectionCollection* coll, int nframes)
 {
     gmx_ana_selcollection_t* sc = &coll->impl_->sc_;
index 61291f97e8d5502a4e904b0f74c0a0ffffac082e..f78f06b225296d3bb36984bb4b18a7cd1ace81e7 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b79f2cfebcca1ebd009298937f9fb629ffb03578..5a6a4507bc3845ce3d261a1d3933d56d8f3eb94c 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -847,7 +849,10 @@ void gmx_ana_index_make_block(t_blocka* t, const gmx_mtop_t* top, gmx_ana_index_
             srenew(t->a, g->isize);
             t->nalloc_a = g->isize;
         }
-        std::memcpy(t->a, g->index, g->isize * sizeof(*(t->a)));
+        if (t->nra > 0)
+        {
+            std::memcpy(t->a, g->index, g->isize * sizeof(*(t->a)));
+        }
     }
 
     /* Allocate memory for the block index. We don't know in advance
@@ -1293,7 +1298,10 @@ void gmx_ana_indexmap_copy(gmx_ana_indexmap_t* dest, gmx_ana_indexmap_t* src, bo
         dest->b.nra = src->b.nra;
         std::memcpy(dest->orgid, src->orgid, dest->b.nr * sizeof(*dest->orgid));
         std::memcpy(dest->b.index, src->b.index, (dest->b.nr + 1) * sizeof(*dest->b.index));
-        std::memcpy(dest->b.a, src->b.a, dest->b.nra * sizeof(*dest->b.a));
+        if (dest->b.nra > 0)
+        {
+            std::memcpy(dest->b.a, src->b.a, dest->b.nra * sizeof(*dest->b.a));
+        }
     }
     dest->mapb.nr  = src->mapb.nr;
     dest->mapb.nra = src->mapb.nra;
index 1b0db8cc458ce0cc83f1d4fe3101d0e9b22a98ce..c6060d0afae0d8506ba3d53d8cb15c63091e9280 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2012,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2012,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 23194782939fa10e543a33133488529a2b990042..ff28ee3a1e4f56390f9dd7661b4c8e94ca1e5b01 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2014,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2014,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index ddc8bd67d341b1dee67fb8009a6f21076bf6ed13..96443c208da233a26f60c0cc009cad3e5f18fb74 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "gromacs/math/functions.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/pbcutil/pbc.h"
-#include "gromacs/topology/block.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/mutex.h"
 #include "gromacs/utility/stringutil.h"
 
@@ -137,7 +139,7 @@ public:
      */
     void                  init(AnalysisNeighborhood::SearchMode     mode,
                                bool                                 bXY,
-                               const t_blocka*                      excls,
+                               const ListOfLists<int>*              excls,
                                const t_pbc*                         pbc,
                                const AnalysisNeighborhoodPositions& positions);
     PairSearchImplPointer getPairSearch();
@@ -280,7 +282,7 @@ private:
     //! Reference position indices (NULL if no indices).
     const int* refIndices_;
     //! Exclusions.
-    const t_blocka* excls_;
+    const ListOfLists<int>* excls_;
     //! PBC data.
     t_pbc pbc_;
 
@@ -337,8 +339,6 @@ public:
         testPositions_    = nullptr;
         testExclusionIds_ = nullptr;
         testIndices_      = nullptr;
-        nexcl_            = 0;
-        excl_             = nullptr;
         clear_rvec(xtest_);
         clear_rvec(testcell_);
         clear_ivec(currCell_);
@@ -376,10 +376,8 @@ private:
     const int* testExclusionIds_;
     //! Reference to the test position indices.
     const int* testIndices_;
-    //! Number of excluded reference positions for current test particle.
-    int nexcl_;
     //! Exclusions for current test particle.
-    const int* excl_;
+    ArrayRef<const int> excl_;
     //! Index of the currently active test position in \p testPositions_.
     int testIndex_;
     //! Stores test position during a pair loop.
@@ -563,19 +561,19 @@ bool AnalysisNeighborhoodSearchImpl::initGrid(const t_pbc& pbc, int posCount, co
     // or remove throughout.
     GMX_UNUSED_VALUE(bForce);
 
-    switch (pbc.ePBC)
+    switch (pbc.pbcType)
     {
-        case epbcNONE:
+        case PbcType::No:
             bGridPBC_[XX] = false;
             bGridPBC_[YY] = false;
             bGridPBC_[ZZ] = false;
             break;
-        case epbcXY:
+        case PbcType::XY:
             bGridPBC_[XX] = true;
             bGridPBC_[YY] = true;
             bGridPBC_[ZZ] = false;
             break;
-        case epbcXYZ:
+        case PbcType::Xyz:
             bGridPBC_[XX] = true;
             bGridPBC_[YY] = true;
             bGridPBC_[ZZ] = true;
@@ -862,23 +860,23 @@ int AnalysisNeighborhoodSearchImpl::shiftCell(const ivec cell, rvec shift) const
 
 void AnalysisNeighborhoodSearchImpl::init(AnalysisNeighborhood::SearchMode     mode,
                                           bool                                 bXY,
-                                          const t_blocka*                      excls,
+                                          const ListOfLists<int>*              excls,
                                           const t_pbc*                         pbc,
                                           const AnalysisNeighborhoodPositions& positions)
 {
     GMX_RELEASE_ASSERT(positions.index_ == -1,
                        "Individual indexed positions not supported as reference");
     bXY_ = bXY;
-    if (bXY_ && pbc != nullptr && pbc->ePBC != epbcNONE)
+    if (bXY_ && pbc != nullptr && pbc->pbcType != PbcType::No)
     {
-        if (pbc->ePBC != epbcXY && pbc->ePBC != epbcXYZ)
+        if (pbc->pbcType != PbcType::XY && pbc->pbcType != PbcType::Xyz)
         {
             std::string message = formatString(
                     "Computations in the XY plane are not supported with PBC type '%s'",
-                    epbc_names[pbc->ePBC]);
+                    c_pbcTypeNames[pbc->pbcType].c_str());
             GMX_THROW(NotImplementedError(message));
         }
-        if (pbc->ePBC == epbcXYZ
+        if (pbc->pbcType == PbcType::Xyz
             && (std::fabs(pbc->box[ZZ][XX]) > GMX_REAL_EPS * pbc->box[ZZ][ZZ]
                 || std::fabs(pbc->box[ZZ][YY]) > GMX_REAL_EPS * pbc->box[ZZ][ZZ]))
         {
@@ -890,7 +888,7 @@ void AnalysisNeighborhoodSearchImpl::init(AnalysisNeighborhood::SearchMode     m
         matrix box;
         copy_mat(pbc->box, box);
         clear_rvec(box[ZZ]);
-        set_pbc(&pbc_, epbcXY, box);
+        set_pbc(&pbc_, PbcType::XY, box);
     }
     else if (pbc != nullptr)
     {
@@ -898,7 +896,7 @@ void AnalysisNeighborhoodSearchImpl::init(AnalysisNeighborhood::SearchMode     m
     }
     else
     {
-        pbc_.ePBC = epbcNONE;
+        pbc_.pbcType = PbcType::No;
         clear_mat(pbc_.box);
     }
     nref_ = positions.count_;
@@ -988,16 +986,13 @@ void AnalysisNeighborhoodPairSearchImpl::reset(int testIndex)
         if (search_.excls_ != nullptr)
         {
             const int exclIndex = testExclusionIds_[index];
-            if (exclIndex < search_.excls_->nr)
+            if (exclIndex < search_.excls_->ssize())
             {
-                const int startIndex = search_.excls_->index[exclIndex];
-                nexcl_               = search_.excls_->index[exclIndex + 1] - startIndex;
-                excl_                = &search_.excls_->a[startIndex];
+                excl_ = (*search_.excls_)[exclIndex];
             }
             else
             {
-                nexcl_ = 0;
-                excl_  = nullptr;
+                excl_ = ArrayRef<const int>();
             }
         }
     }
@@ -1014,15 +1009,16 @@ void AnalysisNeighborhoodPairSearchImpl::nextTestPosition()
 
 bool AnalysisNeighborhoodPairSearchImpl::isExcluded(int j)
 {
-    if (exclind_ < nexcl_)
+    const int nexcl = excl_.ssize();
+    if (exclind_ < nexcl)
     {
         const int index = (search_.refIndices_ != nullptr ? search_.refIndices_[j] : j);
         const int refId = search_.refExclusionIds_[index];
-        while (exclind_ < nexcl_ && excl_[exclind_] < refId)
+        while (exclind_ < nexcl && excl_[exclind_] < refId)
         {
             ++exclind_;
         }
-        if (exclind_ < nexcl_ && refId == excl_[exclind_])
+        if (exclind_ < nexcl && refId == excl_[exclind_])
         {
             ++exclind_;
             return true;
@@ -1123,7 +1119,7 @@ bool AnalysisNeighborhoodPairSearchImpl::searchNext(Action action)
                     continue;
                 }
                 rvec dx;
-                if (search_.pbc_.ePBC != epbcNONE)
+                if (search_.pbc_.pbcType != PbcType::No)
                 {
                     pbc_dx(&search_.pbc_, search_.xref_[i], xtest_, dx);
                 }
@@ -1258,12 +1254,12 @@ public:
 
     SearchImplPointer getSearch();
 
-    Mutex           createSearchMutex_;
-    SearchList      searchList_;
-    real            cutoff_;
-    const t_blocka* excls_;
-    SearchMode      mode_;
-    bool            bXY_;
+    Mutex                   createSearchMutex_;
+    SearchList              searchList_;
+    real                    cutoff_;
+    const ListOfLists<int>* excls_;
+    SearchMode              mode_;
+    bool                    bXY_;
 };
 
 AnalysisNeighborhood::Impl::SearchImplPointer AnalysisNeighborhood::Impl::getSearch()
@@ -1304,7 +1300,7 @@ void AnalysisNeighborhood::setXYMode(bool bXY)
     impl_->bXY_ = bXY;
 }
 
-void AnalysisNeighborhood::setTopologyExclusions(const t_blocka* excls)
+void AnalysisNeighborhood::setTopologyExclusions(const ListOfLists<int>* excls)
 {
     GMX_RELEASE_ASSERT(impl_->searchList_.empty(),
                        "Changing the exclusions after initSearch() not currently supported");
index ffe93854b644f4ddba04e4a2a41c4ac90f37f7ba..7066cafd12d14bedad6061bdf7e26e821a58c8ff 100644 (file)
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/real.h"
 
-struct t_blocka;
 struct t_pbc;
 
 namespace gmx
 {
+template<typename>
+class ListOfLists;
 
 namespace internal
 {
@@ -282,7 +283,7 @@ public:
      *
      * \see AnalysisNeighborhoodPositions::exclusionIds()
      */
-    void setTopologyExclusions(const t_blocka* excls);
+    void setTopologyExclusions(const ListOfLists<int>* excls);
     /*! \brief
      * Sets the algorithm to use for searching.
      *
index 4f6e029095b7c2ff575e9be211467ca10e2f2385..9c44d422da13e859f4f3734391206cf19e47fdeb 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 27bb03189a933c86600adecf19c81ed464e36994..209fd018e6ea116279c4cddfbc904d204e08e28b 100644 (file)
@@ -142,7 +142,8 @@ extern int _gmx_sel_yydebug;
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 51f723bcf63db5f736d1de21c4f4bf234aa5ebd5..2e04ac75332195ac9a6783f86ddab7d7c31fd8c2 100644 (file)
@@ -45,7 +45,8 @@ extern int _gmx_sel_yydebug;
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 5f8197db4741d9d3dff591e471f518618331f1b7..064f46e8a644e3dd954293692c6141071c5bc3cf 100644 (file)
@@ -2,7 +2,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e99aa0375050c6c509efd914ef91ce0c11f50190..bc2f63bf56a609a6ebd0f0f1dc5cd088f827cc3c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 05ea9b6c6f1f65b4d9f3eeb06fbabb9801a8fcb9..d7e536358e8a8d8225215d90d8b38fb2fa002973 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -495,7 +497,10 @@ void _gmx_selelem_init_method_params(const gmx::SelectionTreeElementPointer& sel
     nparams  = sel->u.expr.method->nparams;
     orgparam = sel->u.expr.method->param;
     snew(param, nparams);
-    memcpy(param, orgparam, nparams * sizeof(gmx_ana_selparam_t));
+    if (nparams > 0)
+    {
+        memcpy(param, orgparam, nparams * sizeof(gmx_ana_selparam_t));
+    }
     for (i = 0; i < nparams; ++i)
     {
         param[i].flags &= ~SPAR_SET;
index 3c466926032ad3e8c86ce297272104f2c032b82b..38b8bb67ea73cf0132c5a972db564988fb8662c7 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -253,7 +255,7 @@ static e_index_t index_type_for_poscalc(e_poscalc_t 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: // Intended fall through
         case POS_ALL_PBC: return INDEX_ALL;
     }
     return INDEX_UNKNOWN;
index f95b6766cfb9f815e579b9a9e977dc4883fb08b3..be559bce5053d7c40e3d3aec979fc70f0666065f 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 0f506f14d4a6481e960c3bdafded2c54fc9fb077..bce1d67548e415008c15c1b3f13c345eb9c63898 100644 (file)
@@ -507,7 +507,8 @@ static yyconst flex_int16_t yy_chk[151] =
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 0220dbd0764a5702be182992faff6dae296d8951..32ad4286446947c425744021537df1139fde0543 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 105250f2d4711ca117109cb66f6894baff99c0db..cd0f9512577db30f0b29cda8f61f01123d8ecfa1 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b1c345ee06d678e128b22c988be70a09a72beadb..ff040fe8546adefee741d11fb4e8e62b795af99f 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2009-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -167,7 +167,7 @@ static int init_method_token(YYSTYPE*                          yylval,
         }
         switch (method->type)
         {
-            case INT_VALUE: return METHOD_NUMERIC;
+            case INT_VALUE: // Intended fall through
             case REAL_VALUE: return METHOD_NUMERIC;
             case POS_VALUE: return METHOD_POS;
             case GROUP_VALUE: return METHOD_GROUP;
@@ -304,7 +304,7 @@ int _gmx_sel_lexer_process_identifier(YYSTYPE* yylval, YYLTYPE* yylloc, char* yy
         yylval->sel = new gmx::SelectionTreeElementPointer(var);
         switch (var->v.type)
         {
-            case INT_VALUE: return VARIABLE_NUMERIC;
+            case INT_VALUE: // Intended fall through
             case REAL_VALUE: return VARIABLE_NUMERIC;
             case POS_VALUE: return VARIABLE_POS;
             case GROUP_VALUE: return VARIABLE_GROUP;
index 720c61e49ee668c19b77580aa74e5f55a6affa11..20926675fdcbb4a5f6d2b0626a8dcee95d934297 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 21f3cd5146ab45598bcd9f1c88c685a5170341ea..733e37628a08c2240ce8baa90474abd61b1f886f 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -633,7 +635,7 @@ public:
      */
     const rvec& f() const
     {
-        GMX_ASSERT(sel_->rawPositions_.f != nullptr, "Velocities accessed, but unavailable");
+        GMX_ASSERT(sel_->rawPositions_.f != nullptr, "Forces accessed, but unavailable");
         return sel_->rawPositions_.f[i_];
     }
     /*! \brief
index 4b78f5436234ed0e23ddff2ce2c160fddb607055..a9c6e6825aea95cfa26d8577b2915cc9d9c11983 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -84,7 +84,10 @@ namespace gmx
  * SelectionCollection::Impl
  */
 
-SelectionCollection::Impl::Impl() : debugLevel_(0), bExternalGroupsSet_(false), grps_(nullptr)
+SelectionCollection::Impl::Impl() :
+    debugLevel_(DebugLevel::None),
+    bExternalGroupsSet_(false),
+    grps_(nullptr)
 {
     sc_.nvars   = 0;
     sc_.varstrs = nullptr;
@@ -500,7 +503,7 @@ bool SelectionCollection::Impl::areForcesRequested() const
 
 
 SelectionTopologyProperties
-SelectionCollection::Impl::requiredTopologyPropertiesForPositionType(const std::string& post, bool forces) const
+SelectionCollection::Impl::requiredTopologyPropertiesForPositionType(const std::string& post, bool forces)
 {
     SelectionTopologyProperties props;
     if (!post.empty())
@@ -532,7 +535,9 @@ SelectionCollection::~SelectionCollection() {}
 
 void SelectionCollection::initOptions(IOptionsContainer* options, SelectionTypeOption selectionTypeOption)
 {
-    const char* const debug_levels[] = { "no", "basic", "compile", "eval", "full" };
+    static const EnumerationArray<Impl::DebugLevel, const char*> s_debugLevelNames = {
+        { "no", "basic", "compile", "eval", "full" }
+    };
 
     const char* const* postypes = PositionCalculationCollection::typeEnumValues;
     options->addOption(StringOption("selrpos")
@@ -552,11 +557,10 @@ void SelectionCollection::initOptions(IOptionsContainer* options, SelectionTypeO
     {
         impl_->spost_ = postypes[0];
     }
-    GMX_RELEASE_ASSERT(impl_->debugLevel_ >= 0 && impl_->debugLevel_ <= 4,
-                       "Debug level out of range");
-    options->addOption(EnumIntOption("seldebug")
-                               .hidden(impl_->debugLevel_ == 0)
-                               .enumValue(debug_levels)
+    GMX_RELEASE_ASSERT(impl_->debugLevel_ != Impl::DebugLevel::Count, "Debug level out of range");
+    options->addOption(EnumOption<Impl::DebugLevel>("seldebug")
+                               .hidden(impl_->debugLevel_ == Impl::DebugLevel::None)
+                               .enumValue(s_debugLevelNames)
                                .store(&impl_->debugLevel_)
                                .description("Print out selection trees for debugging"));
 }
@@ -586,7 +590,7 @@ void SelectionCollection::setOutputPosType(const char* type)
 
 void SelectionCollection::setDebugLevel(int debugLevel)
 {
-    impl_->debugLevel_ = debugLevel;
+    impl_->debugLevel_ = Impl::DebugLevel(debugLevel);
 }
 
 
@@ -683,7 +687,8 @@ bool SelectionCollection::requiresIndexGroups() const
 
 SelectionList SelectionCollection::parseFromStdin(int count, bool bInteractive, const std::string& context)
 {
-    return parseInteractive(count, &StandardInputStream::instance(),
+    StandardInputStream inputStream;
+    return parseInteractive(count, &inputStream,
                             bInteractive ? &TextOutputFile::standardError() : nullptr, context);
 }
 
@@ -755,15 +760,14 @@ void SelectionCollection::compile()
     {
         setIndexGroups(nullptr);
     }
-    if (impl_->debugLevel_ >= 1)
+    if (impl_->debugLevel_ != Impl::DebugLevel::None)
     {
         printTree(stderr, false);
     }
 
-    SelectionCompiler compiler;
-    compiler.compile(this);
+    compileSelection(this);
 
-    if (impl_->debugLevel_ >= 1)
+    if (impl_->debugLevel_ != Impl::DebugLevel::None)
     {
         std::fprintf(stderr, "\n");
         printTree(stderr, false);
@@ -772,7 +776,7 @@ void SelectionCollection::compile()
         std::fprintf(stderr, "\n");
     }
     impl_->sc_.pcc.initEvaluation();
-    if (impl_->debugLevel_ >= 1)
+    if (impl_->debugLevel_ != Impl::DebugLevel::None)
     {
         impl_->sc_.pcc.printTree(stderr);
         std::fprintf(stderr, "\n");
@@ -858,7 +862,7 @@ void SelectionCollection::evaluate(t_trxframe* fr, t_pbc* pbc)
     SelectionEvaluator evaluator;
     evaluator.evaluate(this, fr, pbc);
 
-    if (impl_->debugLevel_ >= 3)
+    if (impl_->debugLevel_ == Impl::DebugLevel::Evaluated || impl_->debugLevel_ == Impl::DebugLevel::Full)
     {
         std::fprintf(stderr, "\n");
         printTree(stderr, true);
index 3545171137c7e9e1fb60cc8278d796747b1f1000..a168aaa1b22551c138fb6c977d38579550944284 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -415,13 +416,9 @@ private:
 
     PrivateImplPointer<Impl> impl_;
 
-    /*! \brief
-     * Needed for the compiler to freely modify the collection.
-     */
-    friend class SelectionCompiler;
-    /*! \brief
-     * Needed for the evaluator to freely modify the collection.
-     */
+    // Needed for the compiler to freely modify the collection.
+    friend void compileSelection(SelectionCollection* coll);
+    // Needed for the evaluator to freely modify the collection.
     friend class SelectionEvaluator;
 };
 
index 28b54e9ad560d1c32dc083150bc234a5e5c82dfd..f5cec6b3a00b9ee3ed4e112799b86cad102c48a3 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2009-2016, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -160,8 +160,25 @@ public:
     /*! \brief
      * Returns topology properties needed for a certain position type.
      */
-    SelectionTopologyProperties requiredTopologyPropertiesForPositionType(const std::string& post,
-                                                                          bool forces) const;
+    static SelectionTopologyProperties requiredTopologyPropertiesForPositionType(const std::string& post,
+                                                                                 bool forces);
+
+    //! Describes the available debugging levels
+    enum class DebugLevel : int
+    {
+        //! No debugging
+        None,
+        //! Print selection trees after parsing and compilation
+        Basic,
+        //! As PrintCompiled, also print intermediate compilation trees
+        Compiled,
+        //! As PrintCompiled, also print the tree after evaluation
+        Evaluated,
+        //! Combine Compiled and Evaluated
+        Full,
+        //! Ends the enumeration
+        Count
+    };
 
     //! Internal data, used for interfacing with old C code.
     gmx_ana_selcollection_t sc_;
@@ -171,17 +188,8 @@ public:
     std::string spost_;
     //! Atoms needed for evaluating the selections.
     gmx_ana_index_t requiredAtoms_;
-    /*! \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_;
+    //! Debugging level for the collection.
+    DebugLevel debugLevel_;
     //! Whether setIndexGroups() has been called.
     bool bExternalGroupsSet_;
     //! External index groups (can be NULL).
index b62652bb006b28cb0762de444acee00cf351491d..8ae77c809b587444c1fc239f82e269cd00f85f66 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -65,7 +66,7 @@ namespace gmx
  * 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
+enum SelectionFlag : uint64_t
 {
     efSelection_OnlyStatic = 1 << 0,
     efSelection_OnlyAtoms  = 1 << 1,
index bbfee71903a8d3c93e26824393da34052d4168ca..f41b1946414d427ab47cea5dc985a4c00215fc02 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b9435f45805026eebaab07937e28b4be1f5b0a25..a69b0ba1b108f0e1f132a908940703ec2339bc0f 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index c6c68d8805a3946dfecbc78e55023a1e39000654..5419a6bb876ae1cf1ef4f2a80e28039d6171bf16 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -92,7 +92,7 @@ public:
 
     void promptSelections()
     {
-        const bool isInteractive = StandardInputStream::instance().isInteractive();
+        const bool isInteractive = StandardInputStream::isInteractive();
         initIndexGroups();
         manager_.parseRequestedFromStdin(isInteractive);
         doneIndexGroups();
index 6627942cff53256bdf615d4c57398083f8bb42ab..bc755086c4e51833d0330ff26694eef9089e31e4 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 963d9d1e1bd55f883b4c999f75f2cb083a46fe7a..96baec9eff5f20b8cf02fa036ab631f94f6d55ca 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e9619c63ec8b9a843874ccb4270efb1605e5a16a..d81d5ff4a8223486ada26e481455acec3d47c8d3 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 159cda551d795868106002d7f932cd659668678d..ec96ec50fdf868f6d48db210e7154bd89188a33c 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -586,11 +588,11 @@ private:
     /*! \brief
      * Prints markup for starting a list of keywords.
      */
-    void writeKeywordListStart(const HelpWriterContext& context, const char* heading) const;
+    static void writeKeywordListStart(const HelpWriterContext& context, const char* heading);
     /*! \brief
      * Prints markup for ending a list of keywords.
      */
-    void writeKeywordListEnd(const HelpWriterContext& context, const char* extraInfo) const;
+    static void writeKeywordListEnd(const HelpWriterContext& context, const char* extraInfo);
 
     /*! \brief
      * Prints a brief list of keywords (selection methods) available.
@@ -665,7 +667,7 @@ void KeywordsHelpTopic::writeHelp(const HelpWriterContext& context) const
     writeKeywordSubTopics(context);
 }
 
-void KeywordsHelpTopic::writeKeywordListStart(const HelpWriterContext& context, const char* heading) const
+void KeywordsHelpTopic::writeKeywordListStart(const HelpWriterContext& context, const char* heading)
 {
     context.paragraphBreak();
     std::string fullHeading("* ");
@@ -679,7 +681,7 @@ void KeywordsHelpTopic::writeKeywordListStart(const HelpWriterContext& context,
     }
 }
 
-void KeywordsHelpTopic::writeKeywordListEnd(const HelpWriterContext& context, const char* extraInfo) const
+void KeywordsHelpTopic::writeKeywordListEnd(const HelpWriterContext& context, const char* extraInfo)
 {
     if (context.outputFormat() == eHelpOutputFormat_Rst)
     {
index bfcea9ccfc8ea2b37084a93db16b1bc4136fabd0..ec11760c0a523918803c7c2c00978fda8561d421 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2014,2015, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 90c468744e0e4efeed86edeae4bf72035bfa0dc9..784db15a4624be5f4f574df6ac7fd153c66c792b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2013,2014,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 172bf4ff7de1c7b634f78e1fae41329ff6f02a59..74c75a3d02a52fbfc2306c2fe540109f3d60877a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2012,2013,2014,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 754a02be42ab077cfc6a724ba0f02b14458ebc26..1745d11ee3e6648f439d02e6f5d0ee79e85134e2 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index c9dc219fde7a0fe7f9ce3de314f195edf1ca5011..3d9a347691092606ba0bf0c726a3cd96b11005ac 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 4ad20f4e2cb92cf6471e04e25e66c9c1b9e4b991..d649a874a4f396552de2a8aebf8588eba700ecb8 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -162,7 +164,7 @@ typedef struct
  *
  * \ingroup module_selection
  */
-typedef struct
+typedef struct partition
 {
     /** Number of partition items (\p p contains \p n+1 items). */
     int n;
@@ -180,7 +182,7 @@ typedef struct
  *
  * \ingroup module_selection
  */
-typedef struct
+typedef struct spheresurfacebin
 {
     /** Number of points in the array \p x, -1 if whole bin covered. */
     int n;
@@ -198,7 +200,7 @@ typedef struct
  *
  * \ingroup module_selection
  */
-typedef struct
+typedef struct methoddata_insolidangle
 {
     /** Center of the solid angle. */
     gmx_ana_pos_t center;
index c2fe093f860d81ac70fb733476128eb526f24beb..6b32b40b5375ed715e291af1cbc9133276ee2cb7 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 553c10fc543eaf2f59444aa01f8315003255cacb..1bf833e32a336465ef1a048a77bf7a41d148caec 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -54,7 +56,7 @@
 /*! \internal \brief
  * Data structure for the merging selection modifiers.
  */
-typedef struct
+typedef struct methoddata_merge
 {
     /** Input positions. */
     gmx_ana_pos_t p1;
index a5d7222ae46551ee3faffb7aa112598467f1f049..f8a4e8c17518bc7bbf677bf4bbd2365c9746ab17 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -54,7 +56,7 @@
 /*! \internal \brief
  * Data structure for the \p permute selection modifier.
  */
-typedef struct
+typedef struct methoddata_permute
 {
     /** Positions to permute. */
     gmx_ana_pos_t p;
index 7e103e243b7d3bec957f06623f42aaa3fd72af6d..cc857839a273dc205bc422fbe6fcb7bb849abcc5 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d9cc3a869515d6bfb293522b7cafdcecbada4be5..abcd1745cdd29eceaa1c86a89ba7321cec6d753d 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -511,8 +513,11 @@ static void evaluate_same_str(const gmx::SelMethodEvalContext& /*context*/,
     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);
+        void* ptr = nullptr;
+        if (d->nas > 0)
+        {
+            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 == nullptr)
         {
index f5ec185c29536789d28579f8008d9938c6d30494..2083f1203302decf93cb9f99661731200426c55f 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 41cf89becb7a770c4f19571b17701d0beedaf3ef..5feeeac876cbaf67ecbf8cedba0babcd2b1bd98e 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +48,8 @@
 #include <iterator>
 #include <string>
 
+#include <boost/stl_interfaces/iterator_interface.hpp>
+
 #include "gromacs/utility/classhelpers.h"
 
 #include "selelem.h"
@@ -125,9 +128,9 @@ private:
 
 /*! \internal
  * \brief
- * Input iterator for iterating symbols of a given type.
+ * Forward iterator for iterating symbols of a given type.
  *
- * Behaves as standard C++ input iterator.  To get an iterator, call
+ * Behaves as standard C++ forward iterator.  To get an iterator, call
  * SelectionParserSymbolTable::beginIterator().  Each time the iterator is
  * incremented, it moves to the next symbol of the type given when the iterator
  * was created.  When there are no more symbols, the iterator will equal
@@ -141,20 +144,13 @@ private:
  *
  * \ingroup module_selection
  */
-class SelectionParserSymbolIterator
+class SelectionParserSymbolIterator :
+    public boost::stl_interfaces::iterator_interface<SelectionParserSymbolIterator, std::forward_iterator_tag, const SelectionParserSymbol>
 {
-public:
-    /*! \name Iterator type traits
-     * Satisfies the requirements for STL input iterator.
-     * \{
-     */
-    using iterator_category = std::input_iterator_tag;
-    using value_type        = const SelectionParserSymbol;
-    using difference_type   = std::ptrdiff_t;
-    using pointer           = const SelectionParserSymbol*;
-    using reference         = const SelectionParserSymbol&;
-    //! \}
+    using Base =
+            boost::stl_interfaces::iterator_interface<SelectionParserSymbolIterator, std::forward_iterator_tag, const SelectionParserSymbol>;
 
+public:
     //! Creates an independent copy of an iterator.
     SelectionParserSymbolIterator(const SelectionParserSymbolIterator& other);
     ~SelectionParserSymbolIterator();
@@ -164,21 +160,11 @@ public:
 
     //! Equality comparison for iterators.
     bool operator==(const SelectionParserSymbolIterator& other) const;
-    //! Inequality comparison for iterators.
-    bool operator!=(const SelectionParserSymbolIterator& other) const { return !operator==(other); }
     //! Dereferences the iterator.
     reference operator*() const;
-    //! Dereferences the iterator.
-    pointer operator->() const { return &operator*(); }
     //! Moves the iterator to the next symbol.
     SelectionParserSymbolIterator& operator++();
-    //! Moves the iterator to the next symbol.
-    SelectionParserSymbolIterator operator++(int)
-    {
-        SelectionParserSymbolIterator tmp(*this);
-                                      operator++();
-        return tmp;
-    }
+    using Base::                   operator++;
 
 private:
     class Impl;
index 8c9046376b08ef47858920ec5d8650a72d7a3ce1..fb22371e0be04d697ee7fe6d8f7efd1b63c09d9d 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+# Copyright (c) 2010,2011,2012,2013,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(SelectionUnitTests selection-test
-                  indexutil.cpp
-                  nbsearch.cpp
-                  poscalc.cpp
-                  selectioncollection.cpp
-                  selectionoption.cpp
-                  toputils.cpp)
+    CPP_SOURCE_FILES
+        indexutil.cpp
+        nbsearch.cpp
+        poscalc.cpp
+        selectioncollection.cpp
+        selectionoption.cpp
+        toputils.cpp
+        )
index 7bce7c26b9f9e48bbe69f1b94767ffef2fcd8cad..a58289be0d15530522e5668902cdcfc0ea404c06 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7e85995a1dda77e71e6e2e62fd10a1b9812eef4b..d0f61f30f7d934a480198c574a3c12e382fe3c7f 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -64,6 +65,7 @@
 #include "gromacs/random/threefry.h"
 #include "gromacs/random/uniformrealdistribution.h"
 #include "gromacs/topology/block.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/stringutil.h"
 
@@ -147,13 +149,13 @@ public:
         GMX_RELEASE_ASSERT(testPos_.empty(), "Cannot add positions after testPositions() call");
         testPositions_.emplace_back(x);
     }
-    gmx::RVec        generateRandomPosition();
-    std::vector<int> generateIndex(int count, uint64_t seed) const;
-    void             generateRandomRefPositions(int count);
-    void             generateRandomTestPositions(int count);
-    void             useRefPositionsAsTestPositions();
-    void             computeReferences(t_pbc* pbc) { computeReferencesInternal(pbc, false); }
-    void             computeReferencesXY(t_pbc* pbc) { computeReferencesInternal(pbc, true); }
+    gmx::RVec               generateRandomPosition();
+    static std::vector<int> generateIndex(int count, uint64_t seed);
+    void                    generateRandomRefPositions(int count);
+    void                    generateRandomTestPositions(int count);
+    void                    useRefPositionsAsTestPositions();
+    void                    computeReferences(t_pbc* pbc) { computeReferencesInternal(pbc, false); }
+    void computeReferencesXY(t_pbc* pbc) { computeReferencesInternal(pbc, true); }
 
     bool containsPair(int testIndex, const RefPair& pair) const
     {
@@ -197,7 +199,7 @@ NeighborhoodSearchTestData::NeighborhoodSearchTestData(uint64_t seed, real cutof
     refPosCount_(0)
 {
     clear_mat(box_);
-    set_pbc(&pbc_, epbcNONE, box_);
+    set_pbc(&pbc_, PbcType::No, box_);
 }
 
 gmx::RVec NeighborhoodSearchTestData::generateRandomPosition()
@@ -215,7 +217,7 @@ gmx::RVec NeighborhoodSearchTestData::generateRandomPosition()
     return x;
 }
 
-std::vector<int> NeighborhoodSearchTestData::generateIndex(int count, uint64_t seed) const
+std::vector<int> NeighborhoodSearchTestData::generateIndex(int count, uint64_t seed)
 {
     gmx::DefaultRandomEngine           rngIndex(seed);
     gmx::UniformRealDistribution<real> dist;
@@ -309,13 +311,13 @@ void NeighborhoodSearchTestData::computeReferencesInternal(t_pbc* pbc, bool bXY)
 class ExclusionsHelper
 {
 public:
-    static void markExcludedPairs(RefPairList* refPairs, int testIndex, const t_blocka* excls);
+    static void markExcludedPairs(RefPairList* refPairs, int testIndex, const gmx::ListOfLists<int>* excls);
 
     ExclusionsHelper(int refPosCount, int testPosCount);
 
     void generateExclusions();
 
-    const t_blocka* exclusions() const { return &excls_; }
+    const gmx::ListOfLists<int>* exclusions() const { return &excls_; }
 
     gmx::ArrayRef<const int> refPosIds() const
     {
@@ -327,21 +329,18 @@ public:
     }
 
 private:
-    int              refPosCount_;
-    int              testPosCount_;
-    std::vector<int> exclusionIds_;
-    std::vector<int> exclsIndex_;
-    std::vector<int> exclsAtoms_;
-    t_blocka         excls_;
+    int                   refPosCount_;
+    int                   testPosCount_;
+    std::vector<int>      exclusionIds_;
+    gmx::ListOfLists<int> excls_;
 };
 
 // static
-void ExclusionsHelper::markExcludedPairs(RefPairList* refPairs, int testIndex, const t_blocka* excls)
+void ExclusionsHelper::markExcludedPairs(RefPairList* refPairs, int testIndex, const gmx::ListOfLists<int>* excls)
 {
     int count = 0;
-    for (int i = excls->index[testIndex]; i < excls->index[testIndex + 1]; ++i)
+    for (const int excludedIndex : (*excls)[testIndex])
     {
-        const int                           excludedIndex = excls->a[i];
         NeighborhoodSearchTestData::RefPair searchPair(excludedIndex, 0.0);
         RefPairList::iterator               excludedRefPair =
                 std::lower_bound(refPairs->begin(), refPairs->end(), searchPair);
@@ -364,13 +363,6 @@ ExclusionsHelper::ExclusionsHelper(int refPosCount, int testPosCount) :
     exclusionIds_.resize(std::max(refPosCount, testPosCount), 1);
     exclusionIds_[0] = 0;
     std::partial_sum(exclusionIds_.begin(), exclusionIds_.end(), exclusionIds_.begin());
-
-    excls_.nr           = 0;
-    excls_.index        = nullptr;
-    excls_.nra          = 0;
-    excls_.a            = nullptr;
-    excls_.nalloc_index = 0;
-    excls_.nalloc_a     = 0;
 }
 
 void ExclusionsHelper::generateExclusions()
@@ -379,21 +371,15 @@ void ExclusionsHelper::generateExclusions()
     // particles would be higher, or where the exclusions would not be random,
     // to make a higher percentage of the exclusions to actually be within the
     // cutoff.
-    exclsIndex_.reserve(testPosCount_ + 1);
-    exclsAtoms_.reserve(testPosCount_ * 20);
-    exclsIndex_.push_back(0);
     for (int i = 0; i < testPosCount_; ++i)
     {
+        excls_.pushBackListOfSize(20);
+        gmx::ArrayRef<int> exclusionsForAtom = excls_.back();
         for (int j = 0; j < 20; ++j)
         {
-            exclsAtoms_.push_back(i + j * 3);
+            exclusionsForAtom[j] = i + j * 3;
         }
-        exclsIndex_.push_back(exclsAtoms_.size());
     }
-    excls_.nr    = exclsIndex_.size();
-    excls_.index = exclsIndex_.data();
-    excls_.nra   = exclsAtoms_.size();
-    excls_.a     = exclsAtoms_.data();
 }
 
 /********************************************************************
@@ -403,21 +389,24 @@ void ExclusionsHelper::generateExclusions()
 class NeighborhoodSearchTest : public ::testing::Test
 {
 public:
-    void testIsWithin(gmx::AnalysisNeighborhoodSearch* search, const NeighborhoodSearchTestData& data);
-    void testMinimumDistance(gmx::AnalysisNeighborhoodSearch*  search,
+    static void testIsWithin(gmx::AnalysisNeighborhoodSearch*  search,
                              const NeighborhoodSearchTestData& data);
-    void testNearestPoint(gmx::AnalysisNeighborhoodSearch* search, const NeighborhoodSearchTestData& data);
-    void testPairSearch(gmx::AnalysisNeighborhoodSearch* search, const NeighborhoodSearchTestData& data);
-    void testPairSearchIndexed(gmx::AnalysisNeighborhood*        nb,
-                               const NeighborhoodSearchTestData& data,
-                               uint64_t                          seed);
-    void testPairSearchFull(gmx::AnalysisNeighborhoodSearch*          search,
-                            const NeighborhoodSearchTestData&         data,
-                            const gmx::AnalysisNeighborhoodPositions& pos,
-                            const t_blocka*                           excls,
-                            const gmx::ArrayRef<const int>&           refIndices,
-                            const gmx::ArrayRef<const int>&           testIndices,
-                            bool                                      selfPairs);
+    static void testMinimumDistance(gmx::AnalysisNeighborhoodSearch*  search,
+                                    const NeighborhoodSearchTestData& data);
+    static void testNearestPoint(gmx::AnalysisNeighborhoodSearch*  search,
+                                 const NeighborhoodSearchTestData& data);
+    static void testPairSearch(gmx::AnalysisNeighborhoodSearch*  search,
+                               const NeighborhoodSearchTestData& data);
+    static void testPairSearchIndexed(gmx::AnalysisNeighborhood*        nb,
+                                      const NeighborhoodSearchTestData& data,
+                                      uint64_t                          seed);
+    static void testPairSearchFull(gmx::AnalysisNeighborhoodSearch*          search,
+                                   const NeighborhoodSearchTestData&         data,
+                                   const gmx::AnalysisNeighborhoodPositions& pos,
+                                   const gmx::ListOfLists<int>*              excls,
+                                   const gmx::ArrayRef<const int>&           refIndices,
+                                   const gmx::ArrayRef<const int>&           testIndices,
+                                   bool                                      selfPairs);
 
     gmx::AnalysisNeighborhood nb_;
 };
@@ -522,7 +511,7 @@ void NeighborhoodSearchTest::testPairSearchIndexed(gmx::AnalysisNeighborhood*
 void NeighborhoodSearchTest::testPairSearchFull(gmx::AnalysisNeighborhoodSearch*          search,
                                                 const NeighborhoodSearchTestData&         data,
                                                 const gmx::AnalysisNeighborhoodPositions& pos,
-                                                const t_blocka*                           excls,
+                                                const gmx::ListOfLists<int>*              excls,
                                                 const gmx::ArrayRef<const int>& refIndices,
                                                 const gmx::ArrayRef<const int>& testIndices,
                                                 bool                            selfPairs)
@@ -675,7 +664,7 @@ public:
         data_.box_[ZZ][ZZ] = 3.0;
         data_.generateRandomRefPositions(10);
         data_.generateRandomTestPositions(5);
-        set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
+        set_pbc(&data_.pbc_, PbcType::Xyz, data_.box_);
         data_.computeReferences(&data_.pbc_);
     }
 
@@ -699,7 +688,7 @@ public:
         data_.box_[ZZ][ZZ] = 3.0;
         data_.generateRandomRefPositions(20);
         data_.useRefPositionsAsTestPositions();
-        set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
+        set_pbc(&data_.pbc_, PbcType::Xyz, data_.box_);
         data_.computeReferences(&data_.pbc_);
     }
 
@@ -745,7 +734,7 @@ public:
         // test coverage.
         data_.generateRandomRefPositions(1000);
         data_.generateRandomTestPositions(100);
-        set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
+        set_pbc(&data_.pbc_, PbcType::Xyz, data_.box_);
         data_.computeReferences(&data_.pbc_);
     }
 
@@ -769,7 +758,7 @@ public:
         data_.box_[ZZ][ZZ] = 7.0;
         data_.generateRandomRefPositions(1000);
         data_.useRefPositionsAsTestPositions();
-        set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
+        set_pbc(&data_.pbc_, PbcType::Xyz, data_.box_);
         data_.computeReferences(&data_.pbc_);
     }
 
@@ -795,7 +784,7 @@ public:
         // test coverage.
         data_.generateRandomRefPositions(1000);
         data_.generateRandomTestPositions(100);
-        set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
+        set_pbc(&data_.pbc_, PbcType::Xyz, data_.box_);
         data_.computeReferencesXY(&data_.pbc_);
     }
 
@@ -824,7 +813,7 @@ public:
         // test coverage.
         data_.generateRandomRefPositions(1000);
         data_.generateRandomTestPositions(100);
-        set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
+        set_pbc(&data_.pbc_, PbcType::Xyz, data_.box_);
         data_.computeReferences(&data_.pbc_);
     }
 
@@ -850,7 +839,7 @@ public:
         // test coverage.
         data_.generateRandomRefPositions(1000);
         data_.generateRandomTestPositions(100);
-        set_pbc(&data_.pbc_, epbcXY, data_.box_);
+        set_pbc(&data_.pbc_, PbcType::XY, data_.box_);
         data_.computeReferences(&data_.pbc_);
     }
 
@@ -876,7 +865,7 @@ public:
         // test coverage.
         data_.generateRandomRefPositions(1000);
         data_.generateRandomTestPositions(100);
-        set_pbc(&data_.pbc_, epbcNONE, data_.box_);
+        set_pbc(&data_.pbc_, PbcType::No, data_.box_);
         data_.computeReferences(nullptr);
     }
 
index 37ac153d63b695c6a2fd4f208724fefdde36506f..9748909eb1e31ff202b75071062dc5776989e656 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -122,8 +123,11 @@ private:
 
     typedef std::vector<PositionTest> PositionTestList;
 
-    void setTopologyIfRequired();
-    void checkPositions(gmx::test::TestReferenceChecker* checker, const char* name, gmx_ana_pos_t* p, bool bCoordinates);
+    void        setTopologyIfRequired();
+    static void checkPositions(gmx::test::TestReferenceChecker* checker,
+                               const char*                      name,
+                               gmx_ana_pos_t*                   p,
+                               bool                             bCoordinates);
 
     std::vector<gmx_ana_poscalc_t*> pcList_;
     PositionTestList                posList_;
index 94484badc2f8441588e9c12ea57f19691fb684be..b8c02bb836e3e2a62a66096a21690c718b816797 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -174,7 +174,7 @@ void SelectionCollectionInteractiveTest::runTest(int  count,
 class SelectionCollectionDataTest : public SelectionCollectionTest
 {
 public:
-    enum TestFlag
+    enum TestFlag : uint64_t
     {
         efTestEvaluation          = 1 << 0,
         efTestPositionAtoms       = 1 << 1,
index 9ce9ed93ccb10f3d35397b90d7694e7753390bf2..8b6388db44bfe893d199f9e5437902f12b53b9e3 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -118,15 +119,15 @@ void TopologyManager::requestForces()
 
 void TopologyManager::loadTopology(const char* filename)
 {
-    bool   fullTopology;
-    int    ePBC;
-    rvec*  xtop = nullptr;
-    matrix box;
+    bool    fullTopology;
+    PbcType pbcType;
+    rvec*   xtop = nullptr;
+    matrix  box;
 
     GMX_RELEASE_ASSERT(mtop_ == nullptr, "Topology initialized more than once");
     mtop_ = std::make_unique<gmx_mtop_t>();
     readConfAndTopology(gmx::test::TestFileManager::getInputFilePath(filename).c_str(), &fullTopology,
-                        mtop_.get(), &ePBC, frame_ != nullptr ? &xtop : nullptr, nullptr, box);
+                        mtop_.get(), &pbcType, frame_ != nullptr ? &xtop : nullptr, nullptr, box);
 
     if (frame_ != nullptr)
     {
@@ -152,9 +153,8 @@ void TopologyManager::initAtoms(int count)
     mtop_->molblock[0].type = 0;
     mtop_->molblock[0].nmol = 1;
     mtop_->natoms           = count;
-    mtop_->maxres_renum     = 0;
-    gmx_mtop_finalize(mtop_.get());
-    GMX_RELEASE_ASSERT(mtop_->maxres_renum == 0,
+    mtop_->finalize();
+    GMX_RELEASE_ASSERT(mtop_->maxResiduesPerMoleculeToTriggerRenumber() == 0,
                        "maxres_renum in mtop can be modified by an env.var., that is not supported "
                        "in this test");
     t_atoms& atoms = this->atoms();
@@ -253,9 +253,8 @@ void TopologyManager::finalizeTopology()
 {
     GMX_RELEASE_ASSERT(mtop_ != nullptr, "Topology not initialized");
 
-    mtop_->maxres_renum        = 0;
     mtop_->haveMoleculeIndices = true;
-    gmx_mtop_finalize(mtop_.get());
+    mtop_->finalize();
 }
 
 void TopologyManager::initUniformResidues(int residueSize)
@@ -290,7 +289,7 @@ void TopologyManager::initUniformMolecules(int moleculeSize)
                        "The residues should break at molecule boundaries");
     atoms.nres                 = nres;
     mtop_->haveMoleculeIndices = true;
-    gmx_mtop_finalize(mtop_.get());
+    mtop_->finalize();
 }
 
 void TopologyManager::initFrameIndices(const ArrayRef<const int>& index)
index 93949600001a7cb79a59d42d63a17b064d637c56..533c2457f44ae10be0763e8b8bceb37dd47dcc12 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index a00c9e34c6cf1d72492bac7f8a48f7482ee9489e..5aa27d56dd2455a387e6753750ec29ba6dc4da6d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 8918e8fe9542ca3957f11113626ad8fa3f48b2d8..18348056cb369bb39a93c856624a542a721acf88 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 3b058916fa3dd92feef785900f6db214c0b690f2..f40c43b1b13a167357a77b03196848afdd1b7f38 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
diff --git a/src/gromacs/simd/impl_arm_sve/impl_arm_sve.h b/src/gromacs/simd/impl_arm_sve/impl_arm_sve.h
new file mode 100644 (file)
index 0000000..41240fc
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_H
+#define GMX_SIMD_IMPL_ARM_SVE_H
+
+#include "impl_arm_sve_definitions.h"
+#include "impl_arm_sve_general.h"
+#include "impl_arm_sve_simd4_double.h"
+#include "impl_arm_sve_simd4_float.h"
+#include "impl_arm_sve_simd_double.h"
+#include "impl_arm_sve_simd_float.h"
+#include "impl_arm_sve_util_double.h"
+#include "impl_arm_sve_util_float.h"
+
+/*
+ * armv8+sve support is implemented via the ARM C Language Extensions (ACLE)
+ * that introduces scalable/sizeless types such as svfloat32_t (vector of FP32),
+ * svint32_t (vector of int) or svbool_t (bit mask).
+ * These sizeless types are not a fit for GROMACS that needs to know the vector
+ * length at cmake time. GCC 10 and later have the -msve-vector-bits=<len> option
+ * in order to fix the vector length at compile time. This feature is currently
+ * planned in LLVM 12.
+ * Even with this option, svfloat32_t is considered as sizeless
+ * by the compiler. However, a float __attribute((vector_size(len/8))) variable is
+ * automagically converted as a svfloat32_t and can hence be used to invoke the SIMD
+ * intrinsics specified by ACLE.
+ * Unfortunately, there is no such thing for svbool_t, so a bit mask (SimdBool) is implemented
+ * as a vector of integers.
+ */
+#endif // GMX_SIMD_IMPL_ARM_SVE_H
diff --git a/src/gromacs/simd/impl_arm_sve/impl_arm_sve_definitions.h b/src/gromacs/simd/impl_arm_sve/impl_arm_sve_definitions.h
new file mode 100644 (file)
index 0000000..b15caaa
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_DEFINITIONS_H
+#define GMX_SIMD_IMPL_ARM_SVE_DEFINITIONS_H
+
+#include "config.h"
+
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 1
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT \
+    0 // Although there is support, it is disabled in GROMACS, because rsqrtIter does not work correctly for inputs near MAX_FLOAT
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 1
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 1
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 1
+#define GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT 0
+#define GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE 0
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 1
+
+#define GMX_SIMD_ALIGNMENT 16
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH (GMX_SIMD_ARM_SVE_LENGTH / 32)
+#define GMX_SIMD_DOUBLE_WIDTH (GMX_SIMD_ARM_SVE_LENGTH / 64)
+#define GMX_SIMD_FINT32_WIDTH GMX_SIMD_FLOAT_WIDTH
+#define GMX_SIMD_DINT32_WIDTH GMX_SIMD_DOUBLE_WIDTH
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_RSQRT_BITS 8
+#define GMX_SIMD_RCP_BITS 8
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_DEFINITIONS_H
diff --git a/src/gromacs/simd/impl_arm_sve/impl_arm_sve_general.h b/src/gromacs/simd/impl_arm_sve/impl_arm_sve_general.h
new file mode 100644 (file)
index 0000000..147155c
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2014,2015,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_GENERAL_H
+#define GMX_SIMD_IMPL_ARM_SVE_GENERAL_H
+
+namespace gmx
+{
+
+static inline void simdPrefetch(void* m)
+{
+#ifdef __GNUC__
+    __builtin_prefetch(m);
+#endif
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_GENERAL_H
diff --git a/src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_double.h b/src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_double.h
new file mode 100644 (file)
index 0000000..fd80e6f
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_SIMD4_DOUBLE_H
+#define GMX_SIMD_IMPL_ARM_SVE_SIMD4_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_sve.h>
+
+#include "gromacs/math/utilities.h"
+
+namespace gmx
+{
+
+typedef struct Simd4Double
+{
+    float64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH / 8)));
+
+    Simd4Double(const double d) { this->simdInternal_ = svdup_f64(d); }
+
+    Simd4Double(svfloat64_t simd) : simdInternal_(simd) {}
+
+    Simd4Double() {}
+} Simd4Double;
+
+typedef struct Simd4DBool
+{
+    uint64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH / 8)));
+
+    Simd4DBool(const bool b) { this->simdInternal_ = svdup_n_u64_z(svptrue_b64(), b ? 1 : 0); }
+
+    Simd4DBool(svbool_t simd) { this->simdInternal_ = svdup_n_u64_z(simd, 1); }
+
+    Simd4DBool() {}
+} Simd4DBool;
+
+static inline svbool_t getMask(Simd4DBool m)
+{
+    return svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+}
+
+static inline Simd4Double gmx_simdcall load4(const double* m)
+{
+    assert(std::size_t(m) % 32 == 0);
+    svbool_t pg = svwhilelt_b64(0, 4);
+    return { svld1_f64(pg, m) };
+}
+
+static inline void gmx_simdcall store4(double* m, Simd4Double a)
+{
+    assert(std::size_t(m) % 32 == 0);
+    svbool_t pg = svwhilelt_b64(0, 4);
+    svst1_f64(pg, m, a.simdInternal_);
+}
+
+static inline Simd4Double gmx_simdcall load4U(const double* m)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    return { svld1_f64(pg, m) };
+}
+
+static inline void gmx_simdcall store4U(double* m, Simd4Double a)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    svst1_f64(pg, m, a.simdInternal_);
+}
+
+static inline Simd4Double gmx_simdcall simd4SetZeroD()
+{
+    return { svdup_f64(0.0f) };
+}
+
+static inline Simd4Double gmx_simdcall operator&(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svand_s64_z(pg, svreinterpret_s64_f64(a.simdInternal_),
+                                               svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall andNot(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svbic_s64_z(pg, svreinterpret_s64_f64(b.simdInternal_),
+                                               svreinterpret_s64_f64(a.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall operator|(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svorr_s64_z(pg, svreinterpret_s64_f64(a.simdInternal_),
+                                               svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall operator^(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(sveor_s64_z(pg, svreinterpret_s64_f64(a.simdInternal_),
+                                               svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall operator+(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svadd_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator-(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svsub_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator-(Simd4Double a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svneg_f64_z(pg, a.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator*(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmul_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmad_f64_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svnmsb_f64_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmsb_f64_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svnmad_f64_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall rsqrt(Simd4Double x)
+{
+    svbool_t    pg = svwhilelt_b64(0, 4);
+    svfloat64_t f  = svsplice_f64(pg, x.simdInternal_, svdup_n_f64(1.0f));
+    return { svrsqrte_f64(f) };
+}
+
+static inline Simd4Double gmx_simdcall abs(Simd4Double x)
+{
+    svbool_t pg = svptrue_b64();
+    return { svabs_f64_z(pg, x.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall max(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmax_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall min(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmin_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+// Round and trunc operations are defined at the end of this file, since they
+// need to use float-to-integer and integer-to-float conversions.
+
+static inline double gmx_simdcall reduce(Simd4Double a)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    return svadda_f64(pg, 0.0f, a.simdInternal_);
+}
+
+static inline Simd4DBool gmx_simdcall operator==(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpeq_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4DBool gmx_simdcall operator!=(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpne_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4DBool gmx_simdcall operator<(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmplt_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4DBool gmx_simdcall operator<=(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmple_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4DBool gmx_simdcall operator&&(Simd4DBool a, Simd4DBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svand_z(pg, getMask(a), getMask(b)) };
+}
+
+static inline Simd4DBool gmx_simdcall operator||(Simd4DBool a, Simd4DBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svorr_b_z(pg, getMask(a), getMask(b)) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4DBool a)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    return svptest_any(pg, getMask(a));
+}
+
+static inline Simd4Double gmx_simdcall selectByMask(Simd4Double a, Simd4DBool m)
+{
+    return { svsel_f64(getMask(m), a.simdInternal_, svdup_f64(0.0f)) };
+}
+
+static inline Simd4Double gmx_simdcall selectByNotMask(Simd4Double a, Simd4DBool m)
+{
+    svbool_t pg = svptrue_b64();
+    return { svsel_f64(sveor_b_z(pg, getMask(m), pg), a.simdInternal_, svdup_f64(0.0f)) };
+}
+
+static inline Simd4Double gmx_simdcall blend(Simd4Double a, Simd4Double b, Simd4DBool sel)
+{
+    return { svsel_f64(getMask(sel), b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall round(Simd4Double x)
+{
+    svbool_t pg = svptrue_b64();
+    return { svrinta_f64_z(pg, x.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall trunc(Simd4Double x)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    return { svcvt_f64_z(pg, svcvt_s64_z(pg, x.simdInternal_)) };
+}
+
+static inline double gmx_simdcall dotProduct(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svwhilelt_b64(0, 3);
+    return svadda_f64(pg, 0.0f, svmul_f64_z(pg, a.simdInternal_, b.simdInternal_));
+}
+
+static inline void gmx_simdcall transpose(Simd4Double* v0, Simd4Double* v1, Simd4Double* v2, Simd4Double* v3)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    double   tmp[16];
+    svst1_f64(pg, tmp, v0->simdInternal_);
+    svst1_f64(pg, tmp + 4, v1->simdInternal_);
+    svst1_f64(pg, tmp + 8, v2->simdInternal_);
+    svst1_f64(pg, tmp + 12, v3->simdInternal_);
+
+    svfloat64x4_t vec = svld4_f64(pg, tmp);
+
+    v0->simdInternal_ = svget4_f64(vec, 0);
+    v1->simdInternal_ = svget4_f64(vec, 1);
+    v2->simdInternal_ = svget4_f64(vec, 2);
+    v3->simdInternal_ = svget4_f64(vec, 3);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_SIMD4_DOUBLE_H
diff --git a/src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_float.h b/src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_float.h
new file mode 100644 (file)
index 0000000..959f55a
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_ARM_SVE_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_neon.h>
+
+#include "gromacs/math/utilities.h"
+
+namespace gmx
+{
+
+class Simd4Float
+{
+public:
+    Simd4Float() {}
+
+    Simd4Float(float f) : simdInternal_(vdupq_n_f32(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4Float(float32x4_t simd) : simdInternal_(simd) {}
+
+    float32x4_t simdInternal_;
+};
+
+class Simd4FBool
+{
+public:
+    Simd4FBool() {}
+
+    Simd4FBool(bool b) : simdInternal_(vdupq_n_u32(b ? 0xFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4FBool(uint32x4_t simd) : simdInternal_(simd) {}
+
+    uint32x4_t simdInternal_;
+};
+
+static inline Simd4Float gmx_simdcall load4(const float* m)
+{
+    assert(std::size_t(m) % 16 == 0);
+    return { vld1q_f32(m) };
+}
+
+static inline void gmx_simdcall store4(float* m, Simd4Float a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    vst1q_f32(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall load4U(const float* m)
+{
+    return { vld1q_f32(m) };
+}
+
+static inline void gmx_simdcall store4U(float* m, Simd4Float a)
+{
+    vst1q_f32(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall simd4SetZeroF()
+{
+    return { vdupq_n_f32(0.0f) };
+}
+
+static inline Simd4Float gmx_simdcall operator&(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(vandq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall andNot(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(b.simdInternal_),
+                                             vreinterpretq_s32_f32(a.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall operator|(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(vorrq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall operator^(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(veorq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall operator+(Simd4Float a, Simd4Float b)
+{
+    return { vaddq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a, Simd4Float b)
+{
+    return { vsubq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a)
+{
+    return { vnegq_f32(a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator*(Simd4Float a, Simd4Float b)
+{
+    return { vmulq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vfmaq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vnegq_f32(vfmsq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall fnma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vfmsq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vnegq_f32(vfmaq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall rsqrt(Simd4Float x)
+{
+    return { vrsqrteq_f32(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall abs(Simd4Float x)
+{
+    return { vabsq_f32(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall max(Simd4Float a, Simd4Float b)
+{
+    return { vmaxq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall min(Simd4Float a, Simd4Float b)
+{
+    return { vminq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+// Round and trunc operations are defined at the end of this file, since they
+// need to use float-to-integer and integer-to-float conversions.
+
+static inline float gmx_simdcall reduce(Simd4Float a)
+{
+    float32x4_t b = a.simdInternal_;
+    b             = vpaddq_f32(b, b);
+    b             = vpaddq_f32(b, b);
+    return vgetq_lane_f32(b, 0);
+}
+
+static inline Simd4FBool gmx_simdcall operator==(Simd4Float a, Simd4Float b)
+{
+    return { vceqq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator!=(Simd4Float a, Simd4Float b)
+{
+    return { vmvnq_u32(vceqq_f32(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<(Simd4Float a, Simd4Float b)
+{
+    return { vcltq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<=(Simd4Float a, Simd4Float b)
+{
+    return { vcleq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator&&(Simd4FBool a, Simd4FBool b)
+{
+    return { vandq_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator||(Simd4FBool a, Simd4FBool b)
+{
+    return { vorrq_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4FBool a)
+{
+    return (vmaxvq_u32(a.simdInternal_) != 0);
+}
+
+static inline Simd4Float gmx_simdcall selectByMask(Simd4Float a, Simd4FBool m)
+{
+    return { vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall selectByNotMask(Simd4Float a, Simd4FBool m)
+{
+    return { vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall blend(Simd4Float a, Simd4Float b, Simd4FBool sel)
+{
+    return { vbslq_f32(sel.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall round(Simd4Float x)
+{
+    return { vrndnq_f32(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall trunc(Simd4Float x)
+{
+    return { vrndq_f32(x.simdInternal_) };
+}
+
+static inline float gmx_simdcall dotProduct(Simd4Float a, Simd4Float b)
+{
+    Simd4Float c;
+
+    c = a * b;
+    /* set 4th element to 0, then add all of them */
+    c.simdInternal_ = vsetq_lane_f32(0.0f, c.simdInternal_, 3);
+    return reduce(c);
+}
+
+static inline void gmx_simdcall transpose(Simd4Float* v0, Simd4Float* v1, Simd4Float* v2, Simd4Float* v3)
+{
+    float32x4x2_t t0  = vuzpq_f32(v0->simdInternal_, v2->simdInternal_);
+    float32x4x2_t t1  = vuzpq_f32(v1->simdInternal_, v3->simdInternal_);
+    float32x4x2_t t2  = vtrnq_f32(t0.val[0], t1.val[0]);
+    float32x4x2_t t3  = vtrnq_f32(t0.val[1], t1.val[1]);
+    v0->simdInternal_ = t2.val[0];
+    v1->simdInternal_ = t3.val[0];
+    v2->simdInternal_ = t2.val[1];
+    v3->simdInternal_ = t3.val[1];
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_SIMD4_FLOAT_H
diff --git a/src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_double.h b/src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_double.h
new file mode 100644 (file)
index 0000000..010f03d
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_ARM_SVE_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_sve.h>
+
+#include "gromacs/math/utilities.h"
+
+#include "impl_arm_sve_simd_float.h"
+
+#define SVE_DOUBLE_MASK svptrue_b64()
+#define SVE_DINT32_MASK svptrue_b64()
+
+namespace gmx
+{
+
+class SimdDouble
+{
+public:
+    SimdDouble() {}
+
+    SimdDouble(const double d) { this->simdInternal_ = svdup_f64(d); }
+
+    SimdDouble(svfloat64_t simd) : simdInternal_(simd) {}
+
+    float64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH / 8)));
+};
+
+class SimdDInt32
+{
+public:
+    SimdDInt32() {}
+
+    SimdDInt32(const int32_t i) { this->simdInternal_ = svdup_s64(i); }
+
+    SimdDInt32(svint64_t simd) : simdInternal_(simd) {}
+
+    int64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH / 8)));
+};
+
+class SimdDBool
+{
+public:
+    SimdDBool() {}
+
+    SimdDBool(const bool b)
+    {
+        this->simdInternal_ = svdup_n_u64_x(svptrue_b64(), b ? 0xFFFFFFFFFFFFFFFF : 0);
+    }
+
+    SimdDBool(svbool_t simd) { this->simdInternal_ = svdup_n_u64_z(simd, 0xFFFFFFFFFFFFFFFF); }
+
+    SimdDBool(svuint64_t simd) : simdInternal_(simd) {}
+
+    uint64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH / 8)));
+};
+
+class SimdDIBool
+{
+public:
+    SimdDIBool() {}
+
+    SimdDIBool(const bool b)
+    {
+        this->simdInternal_ = svdup_n_u64_x(svptrue_b64(), b ? 0xFFFFFFFFFFFFFFFF : 0);
+    }
+
+    SimdDIBool(svbool_t simd) { this->simdInternal_ = svdup_n_u64_z(simd, 0xFFFFFFFFFFFFFFFF); }
+
+    SimdDIBool(svuint64_t simd) : simdInternal_(simd) {}
+
+    uint64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH / 8)));
+};
+
+static inline SimdDouble gmx_simdcall simdLoad(const double* m, SimdDoubleTag = {})
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = SVE_DOUBLE_MASK;
+    return { svld1_f64(pg, m) };
+}
+
+static inline SimdDouble gmx_simdcall simdLoad(SimdDouble* m, int offset, SimdDoubleTag = {})
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = SVE_DOUBLE_MASK;
+    return { svld1_f64(pg, reinterpret_cast<double*>(m) + offset * svcntd()) };
+}
+
+static inline SimdDouble gmx_simdcall simdLoadDouble(const double* m)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = SVE_DOUBLE_MASK;
+    return { svld1_f64(pg, m) };
+}
+
+static inline void gmx_simdcall store(double* m, SimdDouble a)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = SVE_DOUBLE_MASK;
+    svst1_f64(pg, m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall simdLoadU(const double* m, SimdDoubleTag = {})
+{
+    svbool_t pg = SVE_DOUBLE_MASK;
+    return { svld1_f64(pg, m) };
+}
+
+static inline void gmx_simdcall storeU(double* m, SimdDouble a)
+{
+    svbool_t pg = SVE_DOUBLE_MASK;
+    svst1_f64(pg, m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall setZeroD()
+{
+    return { svdup_f64(0.0) };
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdDInt32Tag)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH);
+    return { svunpklo_s64(svld1_s32(pg, m)) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdDInt32 a)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH);
+    svst1_s32(pg, m,
+              svuzp1(svreinterpret_s32_s64(a.simdInternal_), svreinterpret_s32_s64(a.simdInternal_)));
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdDInt32Tag)
+{
+    svbool_t pg = svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH);
+    return { svunpklo_s64(svld1_s32(pg, m)) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdDInt32 a)
+{
+    svbool_t pg = svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH);
+    svst1_s32(pg, m,
+              svuzp1(svreinterpret_s32_s64(a.simdInternal_), svreinterpret_s32_s64(a.simdInternal_)));
+}
+
+static inline SimdDInt32 gmx_simdcall setZeroDI()
+{
+    return { svdup_s64(0) };
+}
+
+template<int index>
+gmx_simdcall static inline std::int32_t extract(SimdDInt32 a)
+{
+    svbool_t pg = svwhilelt_b64(0, index);
+    return svlasta_s64(pg, a.simdInternal_);
+}
+
+template<int index>
+gmx_simdcall static inline double extract(SimdDouble a)
+{
+    svbool_t pg = svwhilelt_b64(0, index);
+    return svlasta_f64(pg, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall operator&(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svand_s64_x(pg, svreinterpret_s64_f64(a.simdInternal_),
+                                               svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall andNot(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svbic_s64_x(pg, svreinterpret_s64_f64(b.simdInternal_),
+                                               svreinterpret_s64_f64(a.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator|(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svorr_s64_x(pg, svreinterpret_s64_f64(a.simdInternal_),
+                                               svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator^(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(sveor_s64_x(pg, svreinterpret_s64_f64(a.simdInternal_),
+                                               svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator+(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svadd_f64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svsub_f64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svneg_f64_x(pg, a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator*(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmul_f64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmad_f64_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svnmsb_f64_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmsb_f64_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svnmad_f64_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall rsqrt(SimdDouble x)
+{
+    return { svrsqrte_f64(x.simdInternal_) };
+}
+
+// The SIMD implementation seems to overflow when we square lu for
+// values close to FLOAT_MAX, so we fall back on the version in
+// simd_math.h, which is probably slightly slower.
+#if GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE
+static inline SimdDouble gmx_simdcall rsqrtIter(SimdDouble lu, SimdDouble x)
+{
+    return { vmulq_f64(lu.simdInternal_,
+                       vrsqrtsq_f32(vmulq_f32(lu.simdInternal_, lu.simdInternal_), x.simdInternal_)) };
+}
+#endif
+
+static inline SimdDouble gmx_simdcall rcp(SimdDouble x)
+{
+    return { svrecpe_f64(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall rcpIter(SimdDouble lu, SimdDouble x)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmul_f64_x(pg, lu.simdInternal_, svrecps_f64(lu.simdInternal_, x.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskAdd(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    return { svadd_f64_m(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzMul(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    return { svmul_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzFma(SimdDouble a, SimdDouble b, SimdDouble c, SimdDBool m)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    return { svmad_f64_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRsqrt(SimdDouble x, SimdDBool m)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = svsel_f64(pg, x.simdInternal_, svdup_n_f64(1.0));
+#endif
+    return { svreinterpret_f64_u64(svand_n_u64_z(
+            pg, svreinterpret_u64_f64(svrsqrte_f64(x.simdInternal_)), 0xFFFFFFFFFFFFFFFF)) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRcp(SimdDouble x, SimdDBool m)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = svsel_f64(m, x.simdInternal_, svdup_n_f64(1.0));
+#endif
+    return { svreinterpret_f64_u64(svand_n_u64_z(
+            pg, svreinterpret_u64_f64(svrecpe_f64(x.simdInternal_)), 0xFFFFFFFFFFFFFFFF)) };
+}
+
+static inline SimdDouble gmx_simdcall abs(SimdDouble x)
+{
+    svbool_t pg = svptrue_b64();
+    return { svabs_f64_x(pg, x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall max(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmax_f64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall min(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmin_f64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+// Round and trunc operations are defined at the end of this file, since they
+// need to use double-to-integer and integer-to-double conversions.
+
+static inline SimdDouble gmx_simdcall frexp(SimdDouble value, SimdDInt32* exponent)
+{
+    svbool_t        pg           = svptrue_b64();
+    const svint64_t exponentMask = svdup_n_s64(0x7FF0000000000000LL);
+    const svint64_t mantissaMask = svdup_n_s64(0x800FFFFFFFFFFFFFLL);
+    const svint64_t exponentBias = svdup_n_s64(1022LL); // add 1 to make our definition identical to frexp()
+    const svfloat64_t half = svdup_n_f64(0.5);
+    svint64_t         iExponent;
+
+    iExponent = svand_s64_x(pg, svreinterpret_s64_f64(value.simdInternal_), exponentMask);
+    // iExponent               = svsub_s64_x(pg, svlsr_n_s64_x(pg, iExponent, 52), exponentBias);
+    iExponent = svsub_s64_x(
+            pg, svreinterpret_s64_u64(svlsr_n_u64_x(pg, svreinterpret_u64_s64(iExponent), 52)), exponentBias);
+
+    exponent->simdInternal_ = iExponent;
+
+    return { svreinterpret_f64_s64(svorr_s64_x(
+            pg, svand_s64_x(pg, svreinterpret_s64_f64(value.simdInternal_), mantissaMask),
+            svreinterpret_s64_f64(half))) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    svbool_t        pg           = svptrue_b64();
+    const svint64_t exponentBias = svdup_n_s64(1023);
+    svint64_t       iExponent    = svadd_s64_x(pg, exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = svmax_n_s64_x(pg, iExponent, 0);
+    }
+
+    iExponent = svlsl_n_s64_x(pg, iExponent, 52);
+
+    return { svmul_f64_x(pg, value.simdInternal_, svreinterpret_f64_s64(iExponent)) };
+}
+
+static inline double gmx_simdcall reduce(SimdDouble a)
+{
+    svbool_t pg = svptrue_b64();
+    return svadda_f64(pg, 0.0f, a.simdInternal_);
+}
+
+static inline SimdDBool gmx_simdcall operator==(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpeq_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator!=(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpne_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator<(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmplt_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator<=(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmple_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpne_n_s64(pg, svreinterpret_s64_f64(a.simdInternal_), 0) };
+}
+
+static inline SimdDBool gmx_simdcall operator&&(SimdDBool a, SimdDBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svand_u64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator||(SimdDBool a, SimdDBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svorr_u64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDBool a)
+{
+    svbool_t pg = svptrue_b64();
+    return svptest_any(pg, svcmpne_n_u64(pg, a.simdInternal_, 0));
+}
+
+static inline bool gmx_simdcall extractFirst(SimdDBool a)
+{
+    svbool_t pg = svptrue_b64();
+    return svptest_first(pg, svcmpne_n_u64(pg, a.simdInternal_, 0));
+}
+
+static inline SimdDouble gmx_simdcall selectByMask(SimdDouble a, SimdDBool m)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_u64(svand_u64_x(pg, svreinterpret_u64_f64(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall selectByNotMask(SimdDouble a, SimdDBool m)
+{
+    svbool_t pg = svcmpeq_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    return { svsel_f64(pg, a.simdInternal_, svdup_f64(0.0f)) };
+}
+
+static inline SimdDouble gmx_simdcall blend(SimdDouble a, SimdDouble b, SimdDBool sel)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), sel.simdInternal_, 0);
+    return { svsel_f64(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator&(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svand_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall andNot(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svbic_s64_x(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator|(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svorr_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator^(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { sveor_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator+(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svadd_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator-(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svsub_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator*(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmul_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator==(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpeq_s64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall testBits(SimdDInt32 a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpne_n_s64(pg, a.simdInternal_, (int64_t)0) };
+}
+
+static inline SimdDIBool gmx_simdcall operator<(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmplt_s64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator&&(SimdDIBool a, SimdDIBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svand_u64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator||(SimdDIBool a, SimdDIBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svorr_u64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDIBool a)
+{
+    svbool_t pg = svptrue_b64();
+    return svptest_any(pg, svcmpne_n_u64(pg, a.simdInternal_, 0));
+}
+
+static inline SimdDInt32 gmx_simdcall selectByMask(SimdDInt32 a, SimdDIBool m)
+{
+    svbool_t pg = svptrue_b64();
+    return { svand_s64_x(pg, a.simdInternal_, svreinterpret_s64_u64(m.simdInternal_)) };
+}
+
+static inline SimdDInt32 gmx_simdcall selectByNotMask(SimdDInt32 a, SimdDIBool m)
+{
+    svbool_t pg = svcmpeq_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    return { svadd_n_s64_z(pg, a.simdInternal_, 0) };
+}
+
+static inline SimdDInt32 gmx_simdcall blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), sel.simdInternal_, 0);
+    return { svsel_s64(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvtR2I(SimdDouble a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcvt_s64_x(pg, svrinta_f64_x(pg, a.simdInternal_)) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvttR2I(SimdDouble a)
+{
+    // FIXME ???
+    svbool_t pg = svptrue_b64();
+    return { svcvt_s64_x(pg, a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall cvtI2R(SimdDInt32 a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcvt_f64_x(pg, a.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall cvtB2IB(SimdDBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdDBool gmx_simdcall cvtIB2B(SimdDIBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdDouble gmx_simdcall round(SimdDouble x)
+{
+    svbool_t pg = svptrue_b64();
+    return { svrinta_f64_x(pg, x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall trunc(SimdDouble x)
+{
+    return cvtI2R(cvttR2I(x));
+}
+
+static inline void gmx_simdcall cvtF2DD(SimdFloat gmx_unused f,
+                                        SimdDouble gmx_unused* d0,
+                                        SimdDouble gmx_unused* d1)
+{
+    assert(GMX_SIMD_FLOAT_WIDTH == 2 * GMX_SIMD_DOUBLE_WIDTH);
+    svbool_t pg       = svptrue_b32();
+    d0->simdInternal_ = svcvt_f64_f32_x(pg, svzip1(f.simdInternal_, f.simdInternal_));
+    d1->simdInternal_ = svcvt_f64_f32_x(pg, svzip2(f.simdInternal_, f.simdInternal_));
+}
+
+static inline SimdFloat gmx_simdcall cvtDD2F(SimdDouble gmx_unused d0, SimdDouble gmx_unused d1)
+{
+    svbool_t pg = svptrue_b64();
+    assert(GMX_SIMD_FLOAT_WIDTH == 2 * GMX_SIMD_DOUBLE_WIDTH);
+    return { svuzp1_f32(svcvt_f32_f64_x(pg, d0.simdInternal_), svcvt_f32_f64_x(pg, d1.simdInternal_)) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_SIMD_DOUBLE_H
diff --git a/src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_float.h b/src/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_float.h
new file mode 100644 (file)
index 0000000..3c9cecc
--- /dev/null
@@ -0,0 +1,634 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_ARM_SVE_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_sve.h>
+
+#include "gromacs/math/utilities.h"
+
+namespace gmx
+{
+
+class SimdFloat
+{
+public:
+    SimdFloat() {}
+
+    SimdFloat(const float f) { this->simdInternal_ = svdup_f32(f); }
+
+    SimdFloat(svfloat32_t simd) : simdInternal_(simd) {}
+
+    float32_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH / 8)));
+};
+
+class SimdFInt32
+{
+public:
+    SimdFInt32() {}
+
+    SimdFInt32(const int32_t i) { this->simdInternal_ = svdup_s32(i); }
+
+    SimdFInt32(svint32_t simd) : simdInternal_(simd) {}
+
+    int32_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH / 8)));
+};
+
+class SimdFBool
+{
+public:
+    SimdFBool() {}
+
+    SimdFBool(const bool b)
+    {
+        this->simdInternal_ = svdup_n_u32_x(svptrue_b32(), b ? 0xFFFFFFFF : 0);
+    }
+
+    SimdFBool(svbool_t simd) { this->simdInternal_ = svdup_n_u32_z(simd, 0xFFFFFFFF); }
+
+    SimdFBool(svuint32_t simd) : simdInternal_(simd) {}
+
+    uint32_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH / 8)));
+};
+
+class SimdFIBool
+{
+public:
+    SimdFIBool() {}
+
+    SimdFIBool(const bool b)
+    {
+        this->simdInternal_ = svdup_n_u32_x(svptrue_b32(), b ? 0xFFFFFFFF : 0);
+    }
+
+    SimdFIBool(svbool_t simd) { this->simdInternal_ = svdup_n_u32_z(simd, 0xFFFFFFFF); }
+
+    SimdFIBool(svuint32_t simd) : simdInternal_(simd) {}
+
+    uint32_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH / 8)));
+};
+
+static inline SimdFloat gmx_simdcall simdLoad(const float* m, SimdFloatTag = {})
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    return { svld1_f32(pg, m) };
+}
+
+static inline SimdFloat gmx_simdcall simdLoad(SimdFloat* m, int offset, SimdFloatTag = {})
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    return { svld1_f32(pg, reinterpret_cast<float*>(m) + offset * svcntw()) };
+}
+
+static inline SimdFloat gmx_simdcall simdLoadFloat(const float* m)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    return { svld1_f32(pg, m) };
+}
+
+static inline void gmx_simdcall store(float* m, SimdFloat a)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    svst1_f32(pg, m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall simdLoadU(const float* m, SimdFloatTag = {})
+{
+    svbool_t pg = svptrue_b32();
+    return { svld1_f32(pg, m) };
+}
+
+static inline void gmx_simdcall storeU(float* m, SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    svst1_f32(pg, m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall setZeroF()
+{
+    return { svdup_f32(0.0f) };
+}
+
+static inline void gmx_simdcall simdIncr(SimdFloat*& p, SimdFloatTag)
+{
+    p = reinterpret_cast<SimdFloat*>(reinterpret_cast<uint64_t>(p) + svcntw());
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdFInt32Tag)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    return { svld1_s32(pg, m) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdFInt32 a)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    svst1_s32(pg, m, a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdFInt32Tag)
+{
+    svbool_t pg = svptrue_b32();
+    return { svld1_s32(pg, m) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdFInt32 a)
+{
+    svbool_t pg = svptrue_b32();
+    svst1_s32(pg, m, a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall setZeroFI()
+{
+    return { svdup_s32(0) };
+}
+
+template<int index>
+gmx_simdcall static inline std::int32_t extract(SimdFInt32 a)
+{
+    svbool_t pg = svwhilelt_b32(0, index);
+    return svlasta_s32(pg, a.simdInternal_);
+}
+
+template<int index>
+gmx_simdcall static inline float extract(SimdFloat a)
+{
+    svbool_t pg = svwhilelt_b32(0, index);
+    return svlasta_f32(pg, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall operator&(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svreinterpret_f32_s32(svand_s32_x(pg, svreinterpret_s32_f32(a.simdInternal_),
+                                               svreinterpret_s32_f32(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall andNot(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svreinterpret_f32_s32(svbic_s32_x(pg, svreinterpret_s32_f32(b.simdInternal_),
+                                               svreinterpret_s32_f32(a.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator|(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svreinterpret_f32_s32(svorr_s32_x(pg, svreinterpret_s32_f32(a.simdInternal_),
+                                               svreinterpret_s32_f32(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator^(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svreinterpret_f32_s32(sveor_s32_x(pg, svreinterpret_s32_f32(a.simdInternal_),
+                                               svreinterpret_s32_f32(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator+(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svadd_f32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svsub_f32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svneg_f32_x(pg, a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator*(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmul_f32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmad_f32_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    svbool_t pg = svptrue_b32();
+    return { svnmsb_f32_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmsb_f32_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    svbool_t pg = svptrue_b32();
+    return { svnmad_f32_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rsqrt(SimdFloat x)
+{
+    return { svrsqrte_f32(x.simdInternal_) };
+}
+
+// The SIMD implementation seems to overflow when we square lu for
+// values close to FLOAT_MAX, so we fall back on the version in
+// simd_math.h, which is probably slightly slower.
+#if GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT
+static inline SimdFloat gmx_simdcall rsqrtIter(SimdFloat lu, SimdFloat x)
+{
+    svbool_t    pg = svptrue_b32();
+    svfloat32_t tmp1, tmp2;
+    tmp1 = svmul_f32_x(pg, x.simdInternal_, lu.simdInternal_);
+    tmp2 = svmul_n_f32_x(pg, lu.simdInternal_, -0.5f);
+    tmp1 = svmad_n_f32_x(pg, tmp1, lu.simdInternal_, -3.0f);
+    return { svmul_f32_x(pg, tmp1, tmp2) };
+}
+
+#endif
+
+static inline SimdFloat gmx_simdcall rcp(SimdFloat x)
+{
+    return { svrecpe_f32(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rcpIter(SimdFloat lu, SimdFloat x)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmul_f32_x(pg, lu.simdInternal_, svrecps_f32(lu.simdInternal_, x.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskAdd(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    return { svadd_f32_m(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzMul(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    return { svmul_f32_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzFma(SimdFloat a, SimdFloat b, SimdFloat c, SimdFBool m)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    return { svmad_f32_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRsqrt(SimdFloat x, SimdFBool m)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = svsel_f32(pg, x.simdInternal_, svdup_n_f32(1.0f));
+#endif
+    return { svreinterpret_f32_u32(
+            svand_n_u32_z(pg, svreinterpret_u32_f32(svrsqrte_f32(x.simdInternal_)), 0xFFFFFFFF)) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRcp(SimdFloat x, SimdFBool m)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = svsel_f32(pg, x.simdInternal_, svdup_n_f32(1.0f));
+#endif
+    return { svreinterpret_f32_u32(
+            svand_n_u32_z(pg, svreinterpret_u32_f32(svrecpe_f32(x.simdInternal_)), 0xFFFFFFFF)) };
+}
+
+static inline SimdFloat gmx_simdcall abs(SimdFloat x)
+{
+    svbool_t pg = svptrue_b32();
+    return { svabs_f32_x(pg, x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall max(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmax_f32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall min(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmin_f32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+// Round and trunc operations are defined at the end of this file, since they
+// need to use float-to-integer and integer-to-float conversions.
+
+static inline SimdFloat gmx_simdcall frexp(SimdFloat value, SimdFInt32* exponent)
+{
+    svbool_t        pg           = svptrue_b32();
+    const svint32_t exponentMask = svdup_n_s32(0x7F800000);
+    const svint32_t mantissaMask = svdup_n_s32(0x807FFFFF);
+    const svint32_t exponentBias = svdup_n_s32(126); // add 1 to make our definition identical to frexp()
+    const svfloat32_t half = svdup_n_f32(0.5f);
+    svint32_t         iExponent;
+
+    iExponent = svand_s32_x(pg, svreinterpret_s32_f32(value.simdInternal_), exponentMask);
+    iExponent = svsub_s32_x(
+            pg, svreinterpret_s32_u32(svlsr_n_u32_x(pg, svreinterpret_u32_s32(iExponent), 23)), exponentBias);
+    exponent->simdInternal_ = iExponent;
+
+    return { svreinterpret_f32_s32(svorr_s32_x(
+            pg, svand_s32_x(pg, svreinterpret_s32_f32(value.simdInternal_), mantissaMask),
+            svreinterpret_s32_f32(half))) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    svbool_t        pg           = svptrue_b32();
+    const svint32_t exponentBias = svdup_n_s32(127);
+    svint32_t       iExponent    = svadd_s32_x(pg, exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = svmax_n_s32_x(pg, iExponent, 0);
+    }
+
+    iExponent = svlsl_n_s32_x(pg, iExponent, 23);
+
+    return { svmul_f32_x(pg, value.simdInternal_, svreinterpret_f32_s32(iExponent)) };
+}
+
+static inline float gmx_simdcall reduce(SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    return svaddv_f32(pg, a.simdInternal_);
+}
+
+static inline SimdFBool gmx_simdcall operator==(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmpeq_f32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator!=(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmpne_f32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator<(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmplt_f32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator<=(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmple_f32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall testBits(SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmpne_n_s32(pg, svreinterpret_s32_f32(a.simdInternal_), 0) };
+}
+
+static inline SimdFBool gmx_simdcall operator&&(SimdFBool a, SimdFBool b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svand_u32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator||(SimdFBool a, SimdFBool b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svorr_u32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFBool a)
+{
+    svbool_t pg = svptrue_b32();
+    return svptest_any(pg, svcmpne_n_u32(svptrue_b32(), a.simdInternal_, 0));
+}
+
+static inline bool gmx_simdcall extractFirst(SimdFBool a)
+{
+    svbool_t pg = svptrue_b32();
+    return svptest_first(pg, svcmpne_n_u32(svptrue_b32(), a.simdInternal_, 0));
+}
+
+static inline SimdFloat gmx_simdcall selectByMask(SimdFloat a, SimdFBool m)
+{
+    svbool_t pg = svptrue_b32();
+    return { svreinterpret_f32_u32(svand_u32_x(pg, svreinterpret_u32_f32(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall selectByNotMask(SimdFloat a, SimdFBool m)
+{
+    svbool_t pg = svcmpeq_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    return { svsel_f32(pg, a.simdInternal_, svdup_f32(0.0f)) };
+}
+
+static inline SimdFloat gmx_simdcall blend(SimdFloat a, SimdFloat b, SimdFBool sel)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), sel.simdInternal_, 0);
+    return { svsel_f32(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator&(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svand_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall andNot(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svbic_s32_x(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator|(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svorr_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator^(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { sveor_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator+(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svadd_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator-(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svsub_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator*(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmul_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator==(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmpeq_s32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall testBits(SimdFInt32 a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmpne_n_s32(pg, a.simdInternal_, (int32_t)0) };
+}
+
+static inline SimdFIBool gmx_simdcall operator<(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmplt_s32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator&&(SimdFIBool a, SimdFIBool b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svand_u32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator||(SimdFIBool a, SimdFIBool b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svorr_u32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFIBool a)
+{
+    svbool_t pg = svptrue_b32();
+    return svptest_any(pg, svcmpne_n_u32(pg, a.simdInternal_, 0));
+}
+
+static inline SimdFInt32 gmx_simdcall selectByMask(SimdFInt32 a, SimdFIBool m)
+{
+    svbool_t pg = svptrue_b32();
+    return { svand_s32_x(pg, a.simdInternal_, svreinterpret_s32_u32(m.simdInternal_)) };
+}
+
+static inline SimdFInt32 gmx_simdcall selectByNotMask(SimdFInt32 a, SimdFIBool m)
+{
+    svbool_t pg = svcmpeq_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    return { svadd_n_s32_z(pg, a.simdInternal_, 0) };
+}
+
+static inline SimdFInt32 gmx_simdcall blend(SimdFInt32 a, SimdFInt32 b, SimdFIBool sel)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), sel.simdInternal_, 0);
+    return { svsel_s32(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvtR2I(SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcvt_s32_x(pg, svrinta_f32_x(pg, a.simdInternal_)) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvttR2I(SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcvt_s32_x(pg, a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall cvtI2R(SimdFInt32 a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcvt_f32_x(pg, a.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall cvtB2IB(SimdFBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdFBool gmx_simdcall cvtIB2B(SimdFIBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdFloat gmx_simdcall round(SimdFloat x)
+{
+    svbool_t pg = svptrue_b32();
+    return { svrinta_f32_x(pg, x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall trunc(SimdFloat x)
+{
+    return cvtI2R(cvttR2I(x));
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_SIMD_FLOAT_H
diff --git a/src/gromacs/simd/impl_arm_sve/impl_arm_sve_util_double.h b/src/gromacs/simd/impl_arm_sve/impl_arm_sve_util_double.h
new file mode 100644 (file)
index 0000000..f4bfe49
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_ARM_SVE_UTIL_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_sve.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_arm_sve_simd_double.h"
+
+
+namespace gmx
+{
+
+namespace
+{
+inline void gmx_simdcall decrHsimd(double* m, SimdDouble a)
+{
+    // Make sure the memory pointer is aligned to half float SIMD width
+    assert(std::size_t(m) % 32 == 0);
+
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    svfloat64_t v0, v1, v2, v3;
+    v0 = svld1_f64(pg, m);
+    v1 = svext_f64(a.simdInternal_, a.simdInternal_, GMX_SIMD_DOUBLE_WIDTH / 2);
+    v2 = svadd_f64_x(pg, a.simdInternal_, v1);
+    v3 = svsub_f64_x(pg, v0, v2);
+    svst1_f64(pg, m, v3);
+}
+} // namespace
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const double*      base,
+                                                    const std::int32_t offset[],
+                                                    SimdDouble*        v0,
+                                                    SimdDouble*        v1,
+                                                    SimdDouble*        v2,
+                                                    SimdDouble*        v3)
+{
+    assert(std::size_t(offset) % 16 == 0);
+    assert(std::size_t(base) % 64 == 0);
+    assert(align % 4 == 0);
+
+    svint64_t offsets;
+    svbool_t  pg = svptrue_b64();
+    offsets      = svmul_n_s64_x(
+            pg, svunpklo_s64(svld1_s32(svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH), offset)),
+            align * sizeof(double));
+    v0->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v1->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v2->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v3->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+}
+
+template<int align>
+static inline void gmx_simdcall
+                   gatherLoadBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    // Base pointer must be aligned to the smaller of 2 elements and float SIMD width
+    assert(std::size_t(base) % 8 == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % 2 == 0);
+
+    svbool_t  pg = svptrue_b64();
+    svint64_t offsets;
+    offsets           = svmul_n_s64_x(pg, offset.simdInternal_, align * sizeof(double));
+    v0->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v1->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+}
+
+template<int align>
+static inline void gmx_simdcall
+                   gatherLoadTranspose(const double* base, const std::int32_t offset[], SimdDouble* v0, SimdDouble* v1)
+{
+    assert(std::size_t(offset) % 64 == 0);
+    assert(std::size_t(base) % 8 == 0);
+    assert(align % 2 == 0);
+
+    SimdDInt32 offsets;
+    svbool_t   pg         = svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH);
+    offsets.simdInternal_ = svunpklo_s64(svld1_s32(pg, offset));
+    gatherLoadBySimdIntTranspose<align>(base, offsets, v0, v1);
+}
+
+static const int c_simdBestPairAlignmentDouble = 2;
+
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const double*      base,
+                                                     const std::int32_t offset[],
+                                                     SimdDouble*        v0,
+                                                     SimdDouble*        v1,
+                                                     SimdDouble*        v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svint64_t offsets;
+    svbool_t  pg = svptrue_b64();
+    offsets      = svmul_n_s64_x(
+            pg, svunpklo_s64(svld1_s32(svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH), offset)),
+            align * sizeof(double));
+    v0->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v1->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v2->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+}
+
+
+template<int align>
+static inline void gmx_simdcall transposeScatterStoreU(double*            base,
+                                                       const std::int32_t offset[],
+                                                       SimdDouble         v0,
+                                                       SimdDouble         v1,
+                                                       SimdDouble         v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svint64_t offsets;
+    svbool_t  pg = svptrue_b64();
+    offsets      = svmul_n_s64_x(
+            pg, svunpklo_s64(svld1_s32(svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH), offset)),
+            align * sizeof(double));
+    svst1_scatter_s64offset_f64(pg, base, offsets, v0.simdInternal_);
+    offsets = svadd_n_s64_x(pg, offsets, sizeof(double));
+    svst1_scatter_s64offset_f64(pg, base, offsets, v1.simdInternal_);
+    offsets = svadd_n_s64_x(pg, offsets, sizeof(double));
+    svst1_scatter_s64offset_f64(pg, base, offsets, v2.simdInternal_);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+                   transposeScatterIncrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    assert(std::size_t(offset) % 32 == 0);
+
+    svbool_t                           pg = svptrue_b64();
+    svfloat64x3_t                      v;
+    alignas(GMX_SIMD_ALIGNMENT) double tvec[3 * GMX_SIMD_DOUBLE_WIDTH];
+    v = svcreate3_f64(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_);
+    svst3_f64(pg, tvec, v);
+    pg = svwhilelt_b64(0, 3);
+    for (int i = 0; i < GMX_SIMD_DOUBLE_WIDTH; i++)
+    {
+        svfloat64_t t1 = svld1_f64(pg, base + align * offset[i]);
+        svfloat64_t t2 = svld1_f64(pg, tvec + 3 * i);
+        svfloat64_t t3 = svadd_f64_x(pg, t1, t2);
+        svst1_f64(pg, base + align * offset[i], t3);
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall
+                   transposeScatterDecrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svbool_t                           pg = svptrue_b64();
+    svfloat64x3_t                      v;
+    alignas(GMX_SIMD_ALIGNMENT) double tvec[3 * GMX_SIMD_DOUBLE_WIDTH];
+    v = svcreate3_f64(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_);
+    svst3_f64(pg, tvec, v);
+    pg = svwhilelt_b64(0, 3);
+    for (int i = 0; i < GMX_SIMD_DOUBLE_WIDTH; i++)
+    {
+        svfloat64_t t1 = svld1_f64(pg, base + align * offset[i]);
+        svfloat64_t t2 = svld1_f64(pg, tvec + 3 * i);
+        svfloat64_t t3 = svsub_f64_x(pg, t1, t2);
+        svst1_f64(pg, base + align * offset[i], t3);
+    }
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdDouble  scalar,
+                                                        SimdDouble* triplets0,
+                                                        SimdDouble* triplets1,
+                                                        SimdDouble* triplets2)
+{
+    assert(GMX_SIMD_DOUBLE_WIDTH <= 16);
+    uint64_t   ind[48] = { 0,  0,  0,  1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  5,
+                         5,  5,  6,  6,  6,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10,
+                         10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15 };
+    svbool_t   pg;
+    svuint64_t idx;
+
+    pg                       = svptrue_b64();
+    idx                      = svld1_u64(pg, ind);
+    triplets0->simdInternal_ = svtbl_f64(scalar.simdInternal_, idx);
+    idx                      = svld1_u64(pg, ind + GMX_SIMD_DOUBLE_WIDTH);
+    triplets1->simdInternal_ = svtbl_f64(scalar.simdInternal_, idx);
+    idx                      = svld1_u64(pg, ind + 2 * GMX_SIMD_DOUBLE_WIDTH);
+    triplets2->simdInternal_ = svtbl_f64(scalar.simdInternal_, idx);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const double* base,
+                                                             SimdDInt32    offset,
+                                                             SimdDouble*   v0,
+                                                             SimdDouble*   v1,
+                                                             SimdDouble*   v2,
+                                                             SimdDouble*   v3)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 4 == 0);
+
+    store(ioffset, offset);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1, v2, v3);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+                   gatherLoadUBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    svbool_t  pg      = svptrue_b64();
+    svint64_t offsets = svmul_n_s64_x(pg, offset.simdInternal_, align * sizeof(double));
+    v0->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v1->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+}
+
+static inline double gmx_simdcall
+                     reduceIncr4ReturnSum(double* m, SimdDouble v0, SimdDouble v1, SimdDouble v2, SimdDouble v3)
+{
+    assert(std::size_t(m) % 16 == 0);
+    svbool_t    pg = svptrue_b64();
+    svfloat64_t _m, _s;
+    double      sum[4];
+    sum[0] = svadda_f64(pg, 0.0, v0.simdInternal_);
+    sum[1] = svadda_f64(pg, 0.0, v1.simdInternal_);
+    sum[2] = svadda_f64(pg, 0.0, v2.simdInternal_);
+    sum[3] = svadda_f64(pg, 0.0, v3.simdInternal_);
+    pg     = svwhilelt_b64(0, 4);
+    _m     = svld1_f64(pg, m);
+    _s     = svld1_f64(pg, sum);
+    svst1_f64(pg, m, svadd_f64_x(pg, _m, _s));
+    return svadda_f64(pg, 0.0, _s);
+}
+
+static inline SimdDouble gmx_simdcall loadDualHsimd(const double* m0, const double* m1)
+{
+    svfloat64_t v0, v1;
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    v0             = svld1_f64(pg, m0);
+    v1             = svld1_f64(pg, m1);
+    return { svsplice_f64(pg, v0, v1) };
+}
+
+static inline SimdDouble gmx_simdcall loadDuplicateHsimd(const double* m)
+{
+    svfloat64_t v;
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    v              = svld1_f64(pg, m);
+    return { svsplice_f64(pg, v, v) };
+}
+
+static inline SimdDouble gmx_simdcall loadU1DualHsimd(const double* m)
+{
+    svfloat64_t v0, v1;
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    v0             = svdup_f64(m[0]);
+    v1             = svdup_f64(m[1]);
+    return { svsplice_f64(pg, v0, v1) };
+}
+
+static inline void gmx_simdcall storeDualHsimd(double* m0, double* m1, SimdDouble a)
+{
+    svbool_t pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    svst1_f64(pg, m0, a.simdInternal_);
+    pg = sveor_b_z(svptrue_b64(), pg, svptrue_b64());
+    svst1_f64(pg, m1 - GMX_SIMD_DOUBLE_WIDTH / 2, a.simdInternal_);
+}
+
+static inline void gmx_simdcall incrDualHsimd(double* m0, double* m1, SimdDouble a)
+{
+    // Make sure the memory pointer is aligned to half float SIMD width
+    assert(std::size_t(m0) % 32 == 0);
+    assert(std::size_t(m1) % 32 == 0);
+
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    svfloat64_t v0, v2, v3;
+    v0 = svld1_f64(pg, m0);
+    v2 = svadd_f64_x(pg, v0, a.simdInternal_);
+    svst1_f64(pg, m0, v2);
+    v0 = svld1_f64(pg, m1);
+    v3 = svext_f64(a.simdInternal_, a.simdInternal_, GMX_SIMD_DOUBLE_WIDTH / 2);
+    v2 = svadd_f64_x(pg, v0, v3);
+    svst1_f64(pg, m1, v2);
+}
+
+static inline void gmx_simdcall decr3Hsimd(double* m, SimdDouble a0, SimdDouble a1, SimdDouble a2)
+{
+    decrHsimd(m, a0);
+    decrHsimd(m + GMX_SIMD_DOUBLE_WIDTH / 2, a1);
+    decrHsimd(m + GMX_SIMD_DOUBLE_WIDTH, a2);
+}
+
+static inline double gmx_simdcall reduceIncr4ReturnSumHsimd(double* m, SimdDouble v0, SimdDouble v1)
+{
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    svfloat64_t _m, _s;
+    double      sum[4];
+    sum[0] = svadda_f64(pg, 0.0, v0.simdInternal_);
+    sum[2] = svadda_f64(pg, 0.0, v1.simdInternal_);
+    pg     = sveor_b_z(svptrue_b64(), pg, svptrue_b64());
+    sum[1] = svadda_f64(pg, 0.0, v0.simdInternal_);
+    sum[3] = svadda_f64(pg, 0.0, v1.simdInternal_);
+
+    pg = svwhilelt_b64(0, 4);
+    _m = svld1_f64(pg, m);
+    _s = svld1_f64(pg, sum);
+    svst1_f64(pg, m, svadd_f64_x(pg, _m, _s));
+    return svadda_f64(pg, 0.0, _s);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTransposeHsimd(const double*      base0,
+                                                         const double*      base1,
+                                                         const std::int32_t offset[],
+                                                         SimdDouble*        v0,
+                                                         SimdDouble*        v1)
+{
+    svint64_t   offsets;
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    svfloat64_t _v0, _v1;
+    offsets = svmul_n_s64_x(
+            pg, svunpklo(svld1_s32(svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH / 2), offset)),
+            align * sizeof(double));
+    _v0               = svld1_gather_s64offset_f64(pg, base0, offsets);
+    _v1               = svld1_gather_s64offset_f64(pg, base1, offsets);
+    v0->simdInternal_ = svsplice_f64(pg, _v0, _v1);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    _v0               = svld1_gather_s64offset_f64(pg, base0, offsets);
+    _v1               = svld1_gather_s64offset_f64(pg, base1, offsets);
+    v1->simdInternal_ = svsplice_f64(pg, _v0, _v1);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_UTIL_DOUBLE_H
diff --git a/src/gromacs/simd/impl_arm_sve/impl_arm_sve_util_float.h b/src/gromacs/simd/impl_arm_sve/impl_arm_sve_util_float.h
new file mode 100644 (file)
index 0000000..18df95a
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_ARM_SVE_UTIL_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_sve.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_arm_sve_simd_float.h"
+
+#define SVE_FLOAT_HALF_MASK svwhilelt_b32(0, GMX_SIMD_FLOAT_WIDTH / 2)
+#define SVE_FINT32_HALF_MASK svwhilelt_b32(0, GMX_SIMD_FLOAT_WIDTH / 2)
+
+#define SVE_FLOAT4_MASK svptrue_pat_b32(SV_VL4)
+#define SVE_FLOAT3_MASK svptrue_pat_b32(SV_VL3)
+
+namespace gmx
+{
+
+template<int align>
+static inline void gmx_simdcall
+                   gatherLoadBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    // Base pointer must be aligned to the smaller of 2 elements and float SIMD width
+    assert(std::size_t(base) % 8 == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % 2 == 0);
+
+    if (align < 2)
+    {
+        svbool_t  pg = svptrue_b32();
+        svint32_t offsets;
+        offsets           = svmul_n_s32_x(pg, offset.simdInternal_, align * 4);
+        v0->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+        offsets           = svadd_n_s32_x(pg, offsets, 4);
+        v1->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+    }
+    else if (2 == align)
+    {
+        assert(0);
+        svbool_t    pg    = svptrue_b32();
+        svfloat32_t t0    = svreinterpret_f32_u64(svld1_gather_s64index_u64(
+                svunpklo_b(pg), (uint64_t*)base, svunpklo_s64(offset.simdInternal_)));
+        svfloat32_t t1    = svreinterpret_f32_u64(svld1_gather_s64index_u64(
+                svunpkhi_b(pg), (uint64_t*)base, svunpkhi_s64(offset.simdInternal_)));
+        v0->simdInternal_ = svuzp1(t0, t1);
+        v1->simdInternal_ = svuzp2(t0, t1);
+    }
+    else
+    {
+        svbool_t    pg      = svptrue_b32();
+        svint32_t   offsets = svmul_n_s32_x(pg, offset.simdInternal_, align / 2);
+        svfloat32_t t0      = svreinterpret_f32_u64(
+                svld1_gather_s64index_u64(svunpklo_b(pg), (uint64_t*)base, svunpklo_s64(offsets)));
+        svfloat32_t t1 = svreinterpret_f32_u64(
+                svld1_gather_s64index_u64(svunpkhi_b(pg), (uint64_t*)base, svunpkhi_s64(offsets)));
+        v0->simdInternal_ = svuzp1(t0, t1);
+        v1->simdInternal_ = svuzp2(t0, t1);
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const float*       base,
+                                                    const std::int32_t offset[],
+                                                    SimdFloat*         v0,
+                                                    SimdFloat*         v1,
+                                                    SimdFloat*         v2,
+                                                    SimdFloat*         v3)
+{
+    assert(std::size_t(offset) % 16 == 0);
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 4 == 0);
+
+    svint32_t offsets;
+    offsets = svld1_s32(svptrue_b32(), offset);
+    gatherLoadBySimdIntTranspose<align>(base, offsets, v0, v1, v2, v3);
+}
+
+template<int align>
+static inline void gmx_simdcall
+                   gatherLoadTranspose(const float* base, const std::int32_t offset[], SimdFloat* v0, SimdFloat* v1)
+{
+    assert(std::size_t(offset) % 64 == 0);
+    assert(std::size_t(base) % 8 == 0);
+    assert(align % 2 == 0);
+
+    SimdFInt32 offsets;
+    svbool_t   pg         = svptrue_b32();
+    offsets.simdInternal_ = svld1(pg, offset);
+    gatherLoadBySimdIntTranspose<align>(base, offsets, v0, v1);
+}
+
+static const int c_simdBestPairAlignmentFloat = 2;
+
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const float*       base,
+                                                     const std::int32_t offset[],
+                                                     SimdFloat*         v0,
+                                                     SimdFloat*         v1,
+                                                     SimdFloat*         v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svint32_t offsets;
+    svbool_t  pg      = svptrue_b32();
+    offsets           = svmul_n_s32_x(pg, svld1_s32(pg, offset), align * 4);
+    v0->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+    offsets           = svadd_n_s32_x(pg, offsets, 4);
+    v1->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+    offsets           = svadd_n_s32_x(pg, offsets, 4);
+    v2->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+                   transposeScatterStoreU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svint32_t offsets;
+    svbool_t  pg = svptrue_b32();
+    offsets      = svmul_n_s32_x(pg, svld1_s32(pg, offset), align * 4);
+    svst1_scatter_s32offset_f32(pg, base, offsets, v0.simdInternal_);
+    offsets = svadd_n_s32_x(pg, offsets, 4);
+    svst1_scatter_s32offset_f32(pg, base, offsets, v1.simdInternal_);
+    offsets = svadd_n_s32_x(pg, offsets, 4);
+    svst1_scatter_s32offset_f32(pg, base, offsets, v2.simdInternal_);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+                   transposeScatterIncrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    assert(std::size_t(offset) % 64 == 0);
+
+    svbool_t                          pg = svptrue_b32();
+    svfloat32x3_t                     v;
+    alignas(GMX_SIMD_ALIGNMENT) float tvec[3 * GMX_SIMD_FLOAT_WIDTH];
+    v = svcreate3_f32(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_);
+    svst3_f32(pg, tvec, v);
+    pg = SVE_FLOAT3_MASK;
+    for (int i = 0; i < GMX_SIMD_FLOAT_WIDTH; i++)
+    {
+        svfloat32_t t1 = svld1_f32(pg, base + align * offset[i]);
+        svfloat32_t t2 = svld1_f32(pg, tvec + 3 * i);
+        svfloat32_t t3 = svadd_f32_x(pg, t1, t2);
+        svst1_f32(pg, base + align * offset[i], t3);
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall
+                   transposeScatterDecrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svbool_t                          pg = svptrue_b32();
+    svfloat32x3_t                     v;
+    alignas(GMX_SIMD_ALIGNMENT) float tvec[3 * GMX_SIMD_FLOAT_WIDTH];
+    v = svcreate3_f32(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_);
+    svst3_f32(pg, tvec, v);
+    pg = SVE_FLOAT3_MASK;
+    for (int i = 0; i < GMX_SIMD_FLOAT_WIDTH; i++)
+    {
+        svfloat32_t t1 = svld1_f32(pg, base + align * offset[i]);
+        svfloat32_t t2 = svld1_f32(pg, tvec + 3 * i);
+        svfloat32_t t3 = svsub_f32_x(pg, t1, t2);
+        svst1_f32(pg, base + align * offset[i], t3);
+    }
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdFloat  scalar,
+                                                        SimdFloat* triplets0,
+                                                        SimdFloat* triplets1,
+                                                        SimdFloat* triplets2)
+{
+    assert(GMX_SIMD_FLOAT_WIDTH <= 16);
+    uint32_t   ind[48] = { 0,  0,  0,  1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  5,
+                         5,  5,  6,  6,  6,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10,
+                         10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15 };
+    svbool_t   pg;
+    svuint32_t idx;
+
+    pg                       = svptrue_b32();
+    idx                      = svld1_u32(pg, ind);
+    triplets0->simdInternal_ = svtbl_f32(scalar.simdInternal_, idx);
+    idx                      = svld1_u32(pg, ind + GMX_SIMD_FLOAT_WIDTH);
+    triplets1->simdInternal_ = svtbl_f32(scalar.simdInternal_, idx);
+    idx                      = svld1_u32(pg, ind + 2 * GMX_SIMD_FLOAT_WIDTH);
+    triplets2->simdInternal_ = svtbl_f32(scalar.simdInternal_, idx);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const float* base,
+                                                             SimdFInt32   offset,
+                                                             SimdFloat*   v0,
+                                                             SimdFloat*   v1,
+                                                             SimdFloat*   v2,
+                                                             SimdFloat*   v3)
+{
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 4 == 0);
+
+    svbool_t pg          = svptrue_b32();
+    offset.simdInternal_ = svmul_n_s32_x(pg, offset.simdInternal_, align);
+    v0->simdInternal_    = svld1_gather_s32index_f32(pg, base, offset.simdInternal_);
+    offset.simdInternal_ = svadd_n_s32_x(pg, offset.simdInternal_, 1);
+    v1->simdInternal_    = svld1_gather_s32index_f32(pg, base, offset.simdInternal_);
+    offset.simdInternal_ = svadd_n_s32_x(pg, offset.simdInternal_, 1);
+    v2->simdInternal_    = svld1_gather_s32index_f32(pg, base, offset.simdInternal_);
+    offset.simdInternal_ = svadd_n_s32_x(pg, offset.simdInternal_, 1);
+    v3->simdInternal_    = svld1_gather_s32index_f32(pg, base, offset.simdInternal_);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+                   gatherLoadUBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    svbool_t  pg      = svptrue_b32();
+    svint32_t offsets = svmul_n_s32_x(pg, offset.simdInternal_, align * 4);
+    v0->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+    offsets           = svadd_n_s32_x(pg, offsets, 4);
+    v1->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+}
+
+static inline float gmx_simdcall reduceIncr4ReturnSum(float* m, SimdFloat v0, SimdFloat v1, SimdFloat v2, SimdFloat v3)
+{
+    assert(std::size_t(m) % 16 == 0);
+    svbool_t    pg = svptrue_b32();
+    svfloat32_t _m, _s;
+    float32_t   sum[4];
+    sum[0] = svadda_f32(pg, 0.0f, v0.simdInternal_);
+    sum[1] = svadda_f32(pg, 0.0f, v1.simdInternal_);
+    sum[2] = svadda_f32(pg, 0.0f, v2.simdInternal_);
+    sum[3] = svadda_f32(pg, 0.0f, v3.simdInternal_);
+    pg     = SVE_FLOAT4_MASK;
+    _m     = svld1_f32(pg, m);
+    _s     = svld1_f32(pg, sum);
+    svst1_f32(pg, m, svadd_f32_x(pg, _m, _s));
+    return svadda_f32(pg, 0.0f, _s);
+}
+
+static inline SimdFloat gmx_simdcall loadDualHsimd(const float* m0, const float* m1)
+{
+    svfloat32_t v0, v1;
+    svbool_t    pg = SVE_FLOAT_HALF_MASK;
+    v0             = svld1_f32(pg, m0);
+    v1             = svld1_f32(pg, m1);
+    return { svsplice_f32(pg, v0, v1) };
+}
+
+static inline SimdFloat gmx_simdcall loadDuplicateHsimd(const float* m)
+{
+    svfloat32_t v;
+    svbool_t    pg = SVE_FLOAT_HALF_MASK;
+    v              = svld1_f32(pg, m);
+    return { svsplice_f32(pg, v, v) };
+}
+
+static inline SimdFloat gmx_simdcall loadU1DualHsimd(const float* m)
+{
+    svfloat32_t v0, v1;
+    svbool_t    pg = SVE_FLOAT_HALF_MASK;
+    v0             = svdup_f32(m[0]);
+    v1             = svdup_f32(m[1]);
+    return { svsplice_f32(pg, v0, v1) };
+}
+
+static inline void gmx_simdcall storeDualHsimd(float* m0, float* m1, SimdFloat a)
+{
+    svbool_t pg = SVE_FLOAT_HALF_MASK;
+    svst1_f32(pg, m0, a.simdInternal_);
+    svst1_f32(pg, m1, svext_f32(a.simdInternal_, a.simdInternal_, GMX_SIMD_FLOAT_WIDTH / 2));
+}
+
+static inline void gmx_simdcall incrDualHsimd(float* m0, float* m1, SimdFloat a)
+{
+    // Make sure the memory pointer is aligned to half float SIMD width
+    assert(std::size_t(m0) % (GMX_SIMD_FLOAT_WIDTH * sizeof(float) / 2) == 0);
+    assert(std::size_t(m1) % (GMX_SIMD_FLOAT_WIDTH * sizeof(float) / 2) == 0);
+
+    svbool_t    pg = SVE_FLOAT_HALF_MASK;
+    svfloat32_t v0, v2, v3;
+    v0 = svld1_f32(pg, m0);
+    v2 = svadd_f32_x(pg, v0, a.simdInternal_);
+    svst1_f32(pg, m0, v2);
+    v0 = svld1_f32(pg, m1);
+    v3 = svext_f32(a.simdInternal_, a.simdInternal_, GMX_SIMD_FLOAT_WIDTH / 2);
+    v2 = svadd_f32_x(pg, v0, v3);
+    svst1_f32(pg, m1, v2);
+}
+
+static inline void gmx_simdcall decr3Hsimd(float* m, SimdFloat a0, SimdFloat a1, SimdFloat a2)
+{
+    svbool_t    pg  = svptrue_b32();
+    svbool_t    pg2 = SVE_FLOAT_HALF_MASK;
+    svfloat32_t v0, v1, v2, v3;
+    v0 = svld1_f32(pg, m);
+    v1 = svext_f32(a0.simdInternal_, a1.simdInternal_, GMX_SIMD_FLOAT_WIDTH / 2);
+    v2 = svsel_f32(pg2, a0.simdInternal_, a1.simdInternal_);
+    v1 = svadd_f32_x(pg, v1, v2);
+    v0 = svsub_f32_z(pg, v0, v1);
+    svst1_f32(pg, m, v0);
+    v0 = svld1_f32(pg2, m + GMX_SIMD_FLOAT_WIDTH);
+    v1 = svext_f32(a2.simdInternal_, a0.simdInternal_, GMX_SIMD_FLOAT_WIDTH / 2);
+    v2 = svadd_f32_x(pg2, a2.simdInternal_, v1);
+    v3 = svsub_f32_x(pg2, v0, v2);
+    svst1_f32(pg2, m + GMX_SIMD_FLOAT_WIDTH, v3);
+}
+
+static inline float gmx_simdcall reduceIncr4ReturnSumHsimd(float* m, SimdFloat v0, SimdFloat v1)
+{
+    svbool_t    pg  = SVE_FLOAT_HALF_MASK;
+    svbool_t    pg2 = sveor_b_z(svptrue_b32(), pg, svptrue_b32());
+    svfloat32_t _m, _s;
+
+    _s = svdup_f32(0.0f);
+    _s = svinsr_n_f32(_s, svaddv_f32(pg2, v1.simdInternal_));
+    _s = svinsr_n_f32(_s, svaddv_f32(pg, v1.simdInternal_));
+    _s = svinsr_n_f32(_s, svaddv_f32(pg2, v0.simdInternal_));
+    _s = svinsr_n_f32(_s, svaddv_f32(pg, v0.simdInternal_));
+
+    pg = SVE_FLOAT4_MASK;
+    _m = svld1_f32(pg, m);
+    svst1_f32(pg, m, svadd_f32_x(pg, _m, _s));
+    return svaddv_f32(pg, _s);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTransposeHsimd(const float*       base0,
+                                                         const float*       base1,
+                                                         const std::int32_t offset[],
+                                                         SimdFloat*         v0,
+                                                         SimdFloat*         v1)
+{
+    svint64_t   offsets = svunpklo_s64(svld1_s32(svptrue_b32(), offset));
+    svfloat32_t _v0, _v1;
+    if (2 == align)
+    {
+        _v0 = svreinterpret_f32_f64(svld1_gather_s64index_f64(SVE_DOUBLE_MASK, (double*)base0, offsets));
+        _v1 = svreinterpret_f32_f64(svld1_gather_s64index_f64(SVE_DOUBLE_MASK, (double*)base1, offsets));
+    }
+    else
+    {
+        offsets = svmul_n_s64_x(svptrue_b64(), offsets, align * 4);
+        _v0 = svreinterpret_f32_f64(svld1_gather_s64offset_f64(SVE_DOUBLE_MASK, (double*)base0, offsets));
+        _v1 = svreinterpret_f32_f64(svld1_gather_s64offset_f64(SVE_DOUBLE_MASK, (double*)base1, offsets));
+    }
+    v0->simdInternal_ = svuzp1(_v0, _v1);
+    v1->simdInternal_ = svuzp2(_v0, _v1);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_UTIL_FLOAT_H
index d91fc6db79162e9778ec06d0cfcd0258f2ea2f43..ece4a52b9f2f367fcd3e7153abe498364e5b00cf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 20d1709907cca3ea162a1e95fa53fa13899005d5..95f424d5c25eca216fd6c64f312e406de2fe03bd 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 76bd1cb50000e30ed0ff25cec33ba585062981a1..fe67d4b4f28ac84add54ee10973454d14c2d459a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index da46d0852d16628fd37335a191d05dd77a45aa6f..d21cc14e39e1987e29a72459e7c6b2f0a1c56636 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 66b338f37444309eb8c007487db58e98eca52a42..04504ffbde53e65da9f75f56cde7e24a32921480 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index ca5852f421f9bb5f72a8c33161eb925b7f2f832a..a84317a3edb6d9b6ca46e258460507f9c27829b4 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7013a3622351d00984b14532fb489741f39fb3af..da57a8ae669bac7494134c257f4c06973ffbf67b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f98de3b99073c2e1a3d9ce1476506461315e86fe..e9ce083df89a9ed7c31f3f7af3fa539d3f282356 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,6 +52,8 @@
 #define GMX_SIMD_HAVE_DINT32_ARITHMETICS 0
 #define GMX_SIMD4_HAVE_FLOAT 0
 #define GMX_SIMD4_HAVE_DOUBLE 0
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 0
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 0
 
 #undef GMX_SIMD_FLOAT_WIDTH
 #undef GMX_SIMD_DOUBLE_WIDTH
index ab0a2f0938139112d37899987cfb78c01bbc9a9a..4c2df75fc8c189d701162a242801d4448defdf39 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -736,27 +736,40 @@ static inline void gmx_simdcall incrDualHsimd(double* m0, double* m1, SimdDouble
     }
 }
 
-/*! \brief Add the two halves of a SIMD double, subtract the sum from
- *         half-SIMD-width consecutive doubles in memory.
+/*! \brief Add the two halves of three SIMD doubles, subtract the sum from
+ *         three half-SIMD-width consecutive doubles in memory.
  *
  * \param m  half-width aligned memory, from which sum of the halves will be subtracted.
- * \param a  SIMD variable. Upper & lower halves will first be added.
+ * \param a0 SIMD variable. Upper & lower halves will first be added.
+ * \param a1 SIMD variable. Upper & lower halves will second be added.
+ * \param a2 SIMD variable. Upper & lower halves will third be added.
  *
- * If the SIMD width is 8 and contains [a b c d e f g h], the
- * memory will be modified to [m[0]-(a+e) m[1]-(b+f) m[2]-(c+g) m[3]-(d+h)].
+ * If the SIMD width is 8 and the vectors contain [a0 b0 c0 d0 e0 f0 g0 h0],
+ * [a1 b1 c1 d1 e1 f1 g1 g1] and [a2 b2 c2 d2 e2 f2 g2 h2], the
+ * memory will be modified to [m[0]-(a0+e0) m[1]-(b0+f0) m[2]-(c0+g0) m[3]-(d0+h0)
+ *                             m[4]-(a1+e1) m[5]-(b1+f1) m[6]-(c1+g1) m[7]-(d1+h1)
+ *                             m[8]-(a2+e2) m[9]-(b2+f2) m[10]-(c2+g2) m[11]-(d2+h2)].
  *
  * The memory must be aligned to half SIMD width.
  *
  * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE is 1.
  */
-static inline void gmx_simdcall decrHsimd(double* m, SimdDouble a)
+static inline void gmx_simdcall decr3Hsimd(double* m, SimdDouble a0, SimdDouble a1, SimdDouble a2)
 {
-    // Make sure the memory pointer is aligned to half double SIMD width
     assert(std::size_t(m) % (GMX_SIMD_DOUBLE_WIDTH / 2 * sizeof(double)) == 0);
-
-    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    for (std::size_t i = 0; i < a0.simdInternal_.size() / 2; i++)
+    {
+        m[i] -= a0.simdInternal_[i] + a0.simdInternal_[a0.simdInternal_.size() / 2 + i];
+    }
+    for (std::size_t i = 0; i < a1.simdInternal_.size() / 2; i++)
+    {
+        m[a1.simdInternal_.size() / 2 + i] -=
+                a1.simdInternal_[i] + a1.simdInternal_[a1.simdInternal_.size() / 2 + i];
+    }
+    for (std::size_t i = 0; i < a2.simdInternal_.size() / 2; i++)
     {
-        m[i] -= a.simdInternal_[i] + a.simdInternal_[a.simdInternal_.size() / 2 + i];
+        m[a2.simdInternal_.size() + i] -=
+                a2.simdInternal_[i] + a2.simdInternal_[a2.simdInternal_.size() / 2 + i];
     }
 }
 
index 54f66b2af0c7c8d551f8c5fe1508c50dec80de8a..99919f322301e6023988b4f9b8d214a935c5d202 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -781,27 +781,40 @@ static inline void gmx_simdcall incrDualHsimd(float* m0, float* m1, SimdFloat a)
     }
 }
 
-/*! \brief Add the two halves of a SIMD float, subtract the sum from
- *         half-SIMD-width consecutive floats in memory.
+/*! \brief Add the two halves of three SIMD floats, subtract the sum from
+ *         three half-SIMD-width consecutive floats in memory.
  *
  * \param m  half-width aligned memory, from which sum of the halves will be subtracted.
- * \param a  SIMD variable. Upper & lower halves will first be added.
+ * \param a0 SIMD variable. Upper & lower halves will first be added.
+ * \param a1 SIMD variable. Upper & lower halves will second be added.
+ * \param a2 SIMD variable. Upper & lower halves will third be added.
  *
- * If the SIMD width is 8 and contains [a b c d e f g h], the
- * memory will be modified to [m[0]-(a+e) m[1]-(b+f) m[2]-(c+g) m[3]-(d+h)].
+ * If the SIMD width is 8 and the vectors contain [a0 b0 c0 d0 e0 f0 g0 h0],
+ * [a1 b1 c1 d1 e1 f1 g1 g1] and [a2 b2 c2 d2 e2 f2 g2 h2], the
+ * memory will be modified to [m[0]-(a0+e0) m[1]-(b0+f0) m[2]-(c0+g0) m[3]-(d0+h0)
+ *                             m[4]-(a1+e1) m[5]-(b1+f1) m[6]-(c1+g1) m[7]-(d1+h1)
+ *                             m[8]-(a2+e2) m[9]-(b2+f2) m[10]-(c2+g2) m[11]-(d2+h2)].
  *
  * The memory must be aligned to half SIMD width.
  *
  * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT is 1.
  */
-static inline void gmx_simdcall decrHsimd(float* m, SimdFloat a)
+static inline void gmx_simdcall decr3Hsimd(float* m, SimdFloat a0, SimdFloat a1, SimdFloat a2)
 {
-    // Make sure the memory pointer is aligned to half float SIMD width
     assert(std::size_t(m) % (GMX_SIMD_FLOAT_WIDTH / 2 * sizeof(float)) == 0);
-
-    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    for (std::size_t i = 0; i < a0.simdInternal_.size() / 2; i++)
+    {
+        m[i] -= a0.simdInternal_[i] + a0.simdInternal_[a0.simdInternal_.size() / 2 + i];
+    }
+    for (std::size_t i = 0; i < a1.simdInternal_.size() / 2; i++)
+    {
+        m[a1.simdInternal_.size() / 2 + i] -=
+                a1.simdInternal_[i] + a1.simdInternal_[a1.simdInternal_.size() / 2 + i];
+    }
+    for (std::size_t i = 0; i < a2.simdInternal_.size() / 2; i++)
     {
-        m[i] -= a.simdInternal_[i] + a.simdInternal_[a.simdInternal_.size() / 2 + i];
+        m[a2.simdInternal_.size() + i] -=
+                a2.simdInternal_[i] + a2.simdInternal_[a2.simdInternal_.size() / 2 + i];
     }
 }
 
index 85c07b44d02fcf51db5f48994251b5f04a425dc4..cffb4c6ba1314721ef1fc9485d67b16de4a7d255 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 8af57a24866b2469078e6cb40925c2c1dbd605aa..7c2a6b198d8767319789a65497d7e335b4bd631b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 09784412530a7881496d32c5e0f2ac5e66b0d44b..9b65882f7cc66805fca4f0f51eda59e00a343d54 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 13974aecb6b5759ff190963f54f800f9386f3645..aa633c24edf6086d4c919543018675b4dd3b465a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 1ca12e817114c248be9ca6923ceae1b7fed63bb8..e0984d0d05d4fc2ee0d2506ee2db05fa6cceb1a1 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 36496ca3ec5294395942fb58749fe7086ca3a697..7f219d9937fc967e07ab5e4f45ed4fda6f6cad92 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f6aadba6b8695e4ee4b5c26e6159ce9840b69500..0e0957a8130962aa7f21cea9155a91213d9fe058 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 namespace gmx
 {
 
+/* This is an internal helper function used by decr3Hsimd(...).
+ */
+static inline void gmx_simdcall decrHsimd(float* m, SimdFloat a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    __m128 asum = _mm_add_ps(_mm256_castps256_ps128(a.simdInternal_),
+                             _mm256_extractf128_ps(a.simdInternal_, 0x1));
+    _mm_store_ps(m, _mm_sub_ps(_mm_load_ps(m), asum));
+}
+
 /* This is an internal helper function used by the three functions storing,
  * incrementing, or decrementing data. Do NOT use it outside this file.
  *
@@ -605,12 +615,12 @@ static inline void gmx_simdcall incrDualHsimd(float* m0, float* m1, SimdFloat a)
     _mm_store_ps(m1, _mm_add_ps(_mm256_extractf128_ps(a.simdInternal_, 0x1), _mm_load_ps(m1)));
 }
 
-static inline void gmx_simdcall decrHsimd(float* m, SimdFloat a)
+static inline void gmx_simdcall decr3Hsimd(float* m, SimdFloat a0, SimdFloat a1, SimdFloat a2)
 {
     assert(std::size_t(m) % 16 == 0);
-    __m128 asum = _mm_add_ps(_mm256_castps256_ps128(a.simdInternal_),
-                             _mm256_extractf128_ps(a.simdInternal_, 0x1));
-    _mm_store_ps(m, _mm_sub_ps(_mm_load_ps(m), asum));
+    decrHsimd(m, a0);
+    decrHsimd(m + GMX_SIMD_FLOAT_WIDTH / 2, a1);
+    decrHsimd(m + GMX_SIMD_FLOAT_WIDTH, a2);
 }
 
 
index 3a173dc40dfcc307d44e23bd51f4ad812f58bc0d..75e4a97fe8c99de74ec64515892848feb3ef210a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 1bb0ae6737b64a7c9e7c16fe86a05c6b90fed862..6ef1b20feb053dfcbe20ee68cd3b1eaa0fb433bf 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 282789b89a1ce3cf749a050cda37b5cff4cef500..ee025bfa9637ab1a7afd314a1e543b52854a58c8 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e722b77846fb8570f366c73a373eb262fddd7167..15b05d7848d469188e70492a1546bbb9c605f90f 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2014-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -85,6 +85,21 @@ static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const double*, Simd
 {
     // Nothing to do. Termination of recursion.
 }
+
+/* This is an internal helper function used by decr3Hsimd(...).
+ */
+inline void gmx_simdcall decrHsimd(double* m, SimdDouble a)
+{
+    __m256d t;
+
+    assert(std::size_t(m) % 32 == 0);
+
+    a.simdInternal_ = _mm512_add_pd(a.simdInternal_,
+                                    _mm512_shuffle_f64x2(a.simdInternal_, a.simdInternal_, 0xEE));
+    t               = _mm256_load_pd(m);
+    t               = _mm256_sub_pd(t, _mm512_castpd512_pd256(a.simdInternal_));
+    _mm256_store_pd(m, t);
+}
 } // namespace
 
 
@@ -348,20 +363,13 @@ static inline void gmx_simdcall incrDualHsimd(double* m0, double* m1, SimdDouble
     _mm256_store_pd(m1, x);
 }
 
-static inline void gmx_simdcall decrHsimd(double* m, SimdDouble a)
+static inline void gmx_simdcall decr3Hsimd(double* m, SimdDouble a0, SimdDouble a1, SimdDouble a2)
 {
-    __m256d t;
-
-    assert(std::size_t(m) % 32 == 0);
-
-    a.simdInternal_ = _mm512_add_pd(a.simdInternal_,
-                                    _mm512_shuffle_f64x2(a.simdInternal_, a.simdInternal_, 0xEE));
-    t               = _mm256_load_pd(m);
-    t               = _mm256_sub_pd(t, _mm512_castpd512_pd256(a.simdInternal_));
-    _mm256_store_pd(m, t);
+    decrHsimd(m, a0);
+    decrHsimd(m + GMX_SIMD_DOUBLE_WIDTH / 2, a1);
+    decrHsimd(m + GMX_SIMD_DOUBLE_WIDTH, a2);
 }
 
-
 template<int align>
 static inline void gmx_simdcall gatherLoadTransposeHsimd(const double*      base0,
                                                          const double*      base1,
index ae50e73f630abd5e898c5aadc61d45fdaf0edfa0..99daabee6fcc265daebe4d2c7fa4b3a7850b1695 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -84,6 +85,21 @@ static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const float*, SimdF
 {
     // Nothing to do. Termination of recursion.
 }
+
+/* This is an internal helper function used by decr3Hsimd(...).
+ */
+inline void gmx_simdcall decrHsimd(float* m, SimdFloat a)
+{
+    __m256 t;
+
+    assert(std::size_t(m) % 32 == 0);
+
+    a.simdInternal_ = _mm512_add_ps(a.simdInternal_,
+                                    _mm512_shuffle_f32x4(a.simdInternal_, a.simdInternal_, 0xEE));
+    t               = _mm256_load_ps(m);
+    t               = _mm256_sub_ps(t, _mm512_castps512_ps256(a.simdInternal_));
+    _mm256_store_ps(m, t);
+}
 } // namespace
 
 template<int align, typename... Targs>
@@ -383,17 +399,11 @@ static inline void gmx_simdcall incrDualHsimd(float* m0, float* m1, SimdFloat a)
     _mm256_store_ps(m1, x);
 }
 
-static inline void gmx_simdcall decrHsimd(float* m, SimdFloat a)
+static inline void gmx_simdcall decr3Hsimd(float* m, SimdFloat a0, SimdFloat a1, SimdFloat a2)
 {
-    __m256 t;
-
-    assert(std::size_t(m) % 32 == 0);
-
-    a.simdInternal_ = _mm512_add_ps(a.simdInternal_,
-                                    _mm512_shuffle_f32x4(a.simdInternal_, a.simdInternal_, 0xEE));
-    t               = _mm256_load_ps(m);
-    t               = _mm256_sub_ps(t, _mm512_castps512_ps256(a.simdInternal_));
-    _mm256_store_ps(m, t);
+    decrHsimd(m, a0);
+    decrHsimd(m + GMX_SIMD_FLOAT_WIDTH / 2, a1);
+    decrHsimd(m + GMX_SIMD_FLOAT_WIDTH, a2);
 }
 
 
index 8d26f739390f757c3e03e3417aa7260ce08ed0ea..13e924da8aa76805da9e6bd186dcdda52115d120 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 2e9a6b6880b97a3927b5300500cf5212213dc483..2f85b0cbc159172972218e8528a04c4171350a50 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index bc4fd5ec3f2106fb74c3f7a2bef188eebb2e042c..18769d61e43990578788d41fc031582b76f3684c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 namespace gmx
 {
 
+namespace
+{
+/* This is an internal helper function used by decr3Hsimd(...).
+ */
+inline void gmx_simdcall decrHsimd(double* m, SimdDouble a)
+{
+    __m512d t;
+
+    assert(std::size_t(m) % 32 == 0);
+
+    t               = _mm512_extload_pd(m, _MM_UPCONV_PD_NONE, _MM_BROADCAST_4X8, _MM_HINT_NONE);
+    a.simdInternal_ = _mm512_add_pd(
+            a.simdInternal_,
+            _mm512_castps_pd(_mm512_permute4f128_ps(_mm512_castpd_ps(a.simdInternal_), _MM_PERM_BADC)));
+    t = _mm512_sub_pd(t, a.simdInternal_);
+    _mm512_mask_packstorelo_pd(m, _mm512_int2mask(0x0F), t);
+}
+} // namespace
+
 // On MIC it is better to use scatter operations, so we define the load routines
 // that use a SIMD offset variable first.
 
@@ -360,18 +380,12 @@ static inline void gmx_simdcall incrDualHsimd(double* m0, double* m1, SimdDouble
     _mm512_mask_packstorelo_pd(m1, _mm512_int2mask(0xF0), x);
 }
 
-static inline void gmx_simdcall decrHsimd(double* m, SimdDouble a)
+static inline void gmx_simdcall decr3Hsimd(double* m, SimdDouble a0, SimdDouble a1, SimdDouble a2)
 {
-    __m512d t;
-
     assert(std::size_t(m) % 32 == 0);
-
-    t               = _mm512_extload_pd(m, _MM_UPCONV_PD_NONE, _MM_BROADCAST_4X8, _MM_HINT_NONE);
-    a.simdInternal_ = _mm512_add_pd(
-            a.simdInternal_,
-            _mm512_castps_pd(_mm512_permute4f128_ps(_mm512_castpd_ps(a.simdInternal_), _MM_PERM_BADC)));
-    t = _mm512_sub_pd(t, a.simdInternal_);
-    _mm512_mask_packstorelo_pd(m, _mm512_int2mask(0x0F), t);
+    decrHsimd(m, a0);
+    decrHsimd(m + GMX_SIMD_DOUBLE_WIDTH / 2, a1);
+    decrHsimd(m + GMX_SIMD_DOUBLE_WIDTH, a2);
 }
 
 
index 4911d74c5b3f346d631c656e3a86fca7348314f3..13a6147f877cd7d3d2441a0ba76142a081d5a30a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 namespace gmx
 {
 
+namespace
+{
+/* This is an internal helper function used by decr3Hsimd(...).
+ */
+inline void gmx_simdcall decrHsimd(float* m, SimdFloat a)
+{
+    __m512 t;
+
+    assert(std::size_t(m) % 32 == 0);
+
+    t = _mm512_castpd_ps(_mm512_extload_pd(reinterpret_cast<const double*>(m), _MM_UPCONV_PD_NONE,
+                                           _MM_BROADCAST_4X8, _MM_HINT_NONE));
+    a = _mm512_add_ps(a.simdInternal_, _mm512_permute4f128_ps(a.simdInternal_, _MM_PERM_BADC));
+    t = _mm512_sub_ps(t, a.simdInternal_);
+    _mm512_mask_packstorelo_ps(m, _mm512_int2mask(0x00FF), t);
+}
+} // namespace
+
 // On MIC it is better to use scatter operations, so we define the load routines
 // that use a SIMD offset variable first.
 
@@ -363,17 +382,12 @@ static inline void gmx_simdcall incrDualHsimd(float* m0, float* m1, SimdFloat a)
     _mm512_mask_packstorelo_ps(m1, _mm512_int2mask(0xFF00), x);
 }
 
-static inline void gmx_simdcall decrHsimd(float* m, SimdFloat a)
+static inline void gmx_simdcall decr3Hsimd(float* m, SimdFloat a0, SimdFloat a1, SimdFloat a2)
 {
-    __m512 t;
-
     assert(std::size_t(m) % 32 == 0);
-
-    t = _mm512_castpd_ps(_mm512_extload_pd(reinterpret_cast<const double*>(m), _MM_UPCONV_PD_NONE,
-                                           _MM_BROADCAST_4X8, _MM_HINT_NONE));
-    a = _mm512_add_ps(a.simdInternal_, _mm512_permute4f128_ps(a.simdInternal_, _MM_PERM_BADC));
-    t = _mm512_sub_ps(t, a.simdInternal_);
-    _mm512_mask_packstorelo_ps(m, _mm512_int2mask(0x00FF), t);
+    decrHsimd(m, a0);
+    decrHsimd(m + GMX_SIMD_FLOAT_WIDTH / 2, a1);
+    decrHsimd(m + GMX_SIMD_FLOAT_WIDTH, a2);
 }
 
 
index d22389cb4182f584714036a8b61b96cbf0d77299..f47f56b5b8ef6000faee4712a79ff36101b09f4b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 933754c3a5cab7a758fd9972ccc044883facd3c2..0e50fb0aec0e47e98e64867e588de9f0ff328397 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index bd9b678e1f8cbd60ed503bdd5ffaa0741232b592..536d14675c1c6996ba1b7cb72b9a83c584a4035e 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -151,6 +152,8 @@ struct SimdDInt32Tag
 #    include "impl_arm_neon/impl_arm_neon.h"
 #elif GMX_SIMD_ARM_NEON_ASIMD
 #    include "impl_arm_neon_asimd/impl_arm_neon_asimd.h"
+#elif GMX_SIMD_ARM_SVE
+#    include "impl_arm_sve/impl_arm_sve.h"
 #elif GMX_SIMD_IBM_VMX
 #    include "impl_ibm_vmx/impl_ibm_vmx.h"
 #elif GMX_SIMD_IBM_VSX
@@ -481,11 +484,12 @@ struct SimdTraits<SimdDInt32>
     using tag                  = SimdDInt32Tag;
 };
 #endif
-
+template<typename T>
+using SimdTraitsT = typename SimdTraits<T>::type;
 template<typename T>
 struct SimdTraits<const T>
 {
-    using type                 = const typename SimdTraits<T>::type;
+    using type                 = const SimdTraitsT<T>;
     static constexpr int width = SimdTraits<T>::width;
     using tag                  = typename SimdTraits<T>::tag;
 };
@@ -502,8 +506,7 @@ struct SimdTraits<const T>
  * \return   Loaded value
  */
 template<typename T>
-static inline std::remove_const_t<T>
-load(const typename internal::SimdTraits<T>::type* m) // disabled by SFINAE for non-SIMD types
+static inline std::remove_const_t<T> load(const internal::SimdTraitsT<T>* m) // disabled by SFINAE for non-SIMD types
 {
     return simdLoad(m, typename internal::SimdTraits<T>::tag());
 }
@@ -515,13 +518,13 @@ static inline T
  * 2) load(real*); template parameter is mandatory because otherwise ambiguity is
  *    created. The dependent type disables type deduction.
  */
-load(const std::enable_if_t<std::is_arithmetic<T>::value, T> *m)
+load(const std::enable_if_t<std::is_arithmetic_v<T>, T> *m)
 {
     return *m;
 }
 
 template<typename T, size_t N>
-static inline T gmx_simdcall load(const AlignedArray<typename internal::SimdTraits<T>::type, N>& m)
+static inline T gmx_simdcall load(const AlignedArray<internal::SimdTraitsT<T>, N>& m)
 {
     return simdLoad(m.data(), typename internal::SimdTraits<T>::tag());
 }
@@ -533,19 +536,19 @@ static inline T gmx_simdcall load(const AlignedArray<typename internal::SimdTrai
  * \return Loaded SimdFloat/Double/Int or basic scalar type
  */
 template<typename T>
-static inline T loadU(const typename internal::SimdTraits<T>::type* m)
+static inline T loadU(const internal::SimdTraitsT<T>* m)
 {
     return simdLoadU(m, typename internal::SimdTraits<T>::tag());
 }
 
 template<typename T>
-static inline T loadU(const std::enable_if_t<std::is_arithmetic<T>::value, T>* m)
+static inline T loadU(const std::enable_if_t<std::is_arithmetic_v<T>, T>* m)
 {
     return *m;
 }
 
 template<typename T, size_t N>
-static inline T gmx_simdcall loadU(const AlignedArray<typename internal::SimdTraits<T>::type, N>& m)
+static inline T gmx_simdcall loadU(const AlignedArray<internal::SimdTraitsT<T>, N>& m)
 {
     return simdLoadU(m.data(), typename internal::SimdTraits<T>::tag());
 }
@@ -620,16 +623,18 @@ struct Simd4Traits<Simd4Double>
     using type = double;
 };
 #endif
+template<typename T>
+using Simd4TraitsT = typename Simd4Traits<T>::type;
 } // namespace internal
 
 #if GMX_SIMD4_HAVE_REAL
 template<typename T>
-T load(const typename internal::Simd4Traits<T>::type* m)
+T load(const internal::Simd4TraitsT<T>* m)
 {
     return load4(m);
 }
 template<typename T>
-T loadU(const typename internal::Simd4Traits<T>::type* m)
+T loadU(const internal::Simd4TraitsT<T>* m)
 {
     return load4U(m);
 }
index 09ea3757a3914929e2c06d68285dba2a323d5a4f..d5dfec2ac173d25db335982fda997215c7d783fa 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 0244284df95d7618c5217021118c639e960b2a64..9a770ce906e81538253a56ef56be570136d7c783 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -55,7 +55,7 @@ class SimdReference
 {
 private:
     using non_const_T = std::remove_const_t<T>;
-    using pointer     = typename SimdTraits<T>::type*;
+    using pointer     = SimdTraitsT<T>*;
 
 public:
     //! \brief Constructor
@@ -92,73 +92,30 @@ private:
 };
 
 template<typename T>
-class SimdIterator
+class SimdIterator :
+    public boost::stl_interfaces::iterator_interface<SimdIterator<T>, std::random_access_iterator_tag, T, SimdReference<T>>
 {
-public:
-    //! Type for representing size of the container.
-    using size_type = size_t;
-    //! Type for representing difference between two container indices.
-    using difference_type = std::ptrdiff_t;
-    //! Type of values stored in the container.
-    using value_type = T;
-    //! Pointer to a container element.
-    using pointer = typename SimdTraits<T>::type*;
-    //! Reference to a container element.
-    using reference = internal::SimdReference<T>;
+    using Base =
+            boost::stl_interfaces::iterator_interface<SimdIterator<T>, std::random_access_iterator_tag, T, SimdReference<T>>;
+    // pointer is T*
+    using DataPointer = SimdTraitsT<T>*;
 
-    explicit SimdIterator(pointer p = 0) : p_(p)
+public:
+    explicit SimdIterator(DataPointer p = 0) : p_(p)
     {
-        GMX_ASSERT((reinterpret_cast<size_type>(p) / sizeof(*p)) % simdWidth == 0,
+        GMX_ASSERT((reinterpret_cast<size_t>(p) / sizeof(*p)) % simdWidth == 0,
                    "Trying to create aligned iterator for non aligned address.");
     }
-    SimdIterator& operator++()
-    {
-        p_ += simdWidth;
-        return *this;
-    }
-    SimdIterator operator++(int)
-    {
-        SimdIterator retval = *this;
-        ++(*this);
-        return retval;
-    }
-    SimdIterator& operator--()
-    {
-        p_ -= simdWidth;
-        return *this;
-    }
-    SimdIterator operator--(int)
-    {
-        SimdIterator retval = *this;
-        --(*this);
-        return retval;
-    }
-    SimdIterator& operator+=(difference_type d)
+    SimdIterator& operator+=(typename Base::difference_type d)
     {
         p_ += simdWidth * d;
         return *this;
     }
-    SimdIterator& operator-=(difference_type d)
-    {
-        p_ -= simdWidth * d;
-        return *this;
-    }
-    SimdIterator operator+(difference_type d) { return SimdIterator(p_ + simdWidth * d); }
-    SimdIterator operator-(difference_type d) { return SimdIterator(p_ - simdWidth * d); }
-
-    difference_type operator-(SimdIterator o) { return (p_ - o.p_) / simdWidth; }
-
-    bool operator==(SimdIterator other) const { return p_ == other.p_; }
-    bool operator!=(SimdIterator other) const { return p_ != other.p_; }
-    bool operator<(SimdIterator other) const { return p_ < other.p_; }
-    bool operator>(SimdIterator other) const { return p_ > other.p_; }
-    bool operator<=(SimdIterator other) const { return p_ <= other.p_; }
-    bool operator>=(SimdIterator other) const { return p_ >= other.p_; }
-
-    reference operator*() const { return reference(p_); }
+    typename Base::difference_type operator-(SimdIterator o) { return (p_ - o.p_) / simdWidth; }
+    typename Base::reference       operator*() const { return typename Base::reference(p_); }
 
 private:
-    pointer              p_;
+    DataPointer          p_;
     static constexpr int simdWidth = SimdTraits<T>::width;
 };
 
@@ -187,7 +144,7 @@ public:
     //! Type of values stored in the container.
     using value_type = T;
     //! Pointer to a container element.
-    using pointer = typename SimdTraits<T>::type*;
+    using pointer = SimdTraitsT<T>*;
     //! Reference to a container element.
     using reference = internal::SimdReference<T>;
     //! Iterator type for the container.
@@ -205,7 +162,7 @@ public:
                    "Size of ArrayRef needs to be divisible by type size");
     }
     //! \copydoc ArrayRef::ArrayRef(U)
-    template<typename U, typename = std::enable_if_t<std::is_convertible<typename std::remove_reference_t<U>::pointer, pointer>::value>>
+    template<typename U, typename = std::enable_if_t<std::is_convertible_v<typename std::remove_reference_t<U>::pointer, pointer>>>
     SimdArrayRef(U&& o) :
         begin_(reinterpret_cast<pointer>(o.data())),
         end_(reinterpret_cast<pointer>(o.data() + o.size()))
@@ -233,7 +190,7 @@ public:
 
 private:
     static constexpr int simdWidth = SimdTraits<T>::width;
-    using pack_type                = typename SimdTraits<T>::type[simdWidth];
+    using pack_type                = SimdTraitsT<T>[simdWidth];
     // Private because dereferencing return value is undefined behavior (strict aliasing rule)
     // Only use is conversion constructor above which immediately casts it back.
     // Return type is not "pointer" because then data()+size() would be ill defined.
index 7512e0e909353fc3bef38b89a19cae41d77c1989..f88b801944a7e081060655b2200fe0a6e253c287 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "config.h"
 
+#if GMX_SIMD_ARM_SVE
+#    include <arm_sve.h>
+#endif
+
 #include <cstdio>
 #include <cstdlib>
 
@@ -56,6 +60,7 @@
 
 #include "gromacs/hardware/cpuinfo.h"
 #include "gromacs/hardware/identifyavx512fmaunits.h"
+#include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/stringutil.h"
 
 namespace gmx
@@ -80,6 +85,7 @@ const std::string& simdString(SimdType s)
         { SimdType::X86_Mic, "X86_MIC" },
         { SimdType::Arm_Neon, "ARM_NEON" },
         { SimdType::Arm_NeonAsimd, "ARM_NEON_ASIMD" },
+        { SimdType::Arm_Sve, "ARM_SVE" },
         { SimdType::Ibm_Vmx, "IBM_VMX" },
         { SimdType::Ibm_Vsx, "IBM_VSX" },
         { SimdType::Fujitsu_HpcAce, "Fujitsu HPC-ACE" }
@@ -179,7 +185,11 @@ SimdType simdSuggested(const CpuInfo& c)
 
                 break;
             case CpuInfo::Vendor::Arm:
-                if (c.feature(CpuInfo::Feature::Arm_NeonAsimd))
+                if (c.feature(CpuInfo::Feature::Arm_Sve))
+                {
+                    suggested = SimdType::Arm_Sve;
+                }
+                else if (c.feature(CpuInfo::Feature::Arm_NeonAsimd))
                 {
                     suggested = SimdType::Arm_NeonAsimd;
                 }
@@ -234,6 +244,8 @@ SimdType simdCompiled()
     return SimdType::Arm_Neon;
 #elif GMX_SIMD_ARM_NEON_ASIMD
     return SimdType::Arm_NeonAsimd;
+#elif GMX_SIMD_ARM_SVE
+    return SimdType::Arm_Sve;
 #elif GMX_SIMD_IBM_VMX
     return SimdType::Ibm_Vmx;
 #elif GMX_SIMD_IBM_VSX
@@ -319,6 +331,21 @@ bool simdCheck(gmx::SimdType wanted, FILE* log, bool warnToStdErr)
         warnMsg = wrapper.wrapToString(formatString(
                 "Compiled SIMD: %s, but for this host/run %s might be better (see log).",
                 simdString(compiled).c_str(), simdString(wanted).c_str()));
+#if GMX_SIMD_ARM_SVE
+    }
+    else if ((compiled == SimdType::Arm_Sve) && (svcntb() != GMX_SIMD_ARM_SVE_LENGTH / 8))
+    {
+        logMsg  = wrapper.wrapToString(formatString(
+                "Longest SVE length requested by all nodes in run: %d\n"
+                "SVE length selected at compile time:               %ld\n"
+                "This program was compiled for different hardware than you are running on, "
+                "which will lead to incorrect behavior.\n"
+                "Aborting",
+                GMX_SIMD_ARM_SVE_LENGTH, svcntb() * 8));
+        warnMsg = wrapper.wrapToString(formatString(
+                "Compiled SVE Length: %d, but for this process requires %ld (see log).",
+                GMX_SIMD_ARM_SVE_LENGTH, svcntb() * 8));
+#endif
     }
 
     if (!logMsg.empty() && log != nullptr)
@@ -329,6 +356,12 @@ bool simdCheck(gmx::SimdType wanted, FILE* log, bool warnToStdErr)
     {
         fprintf(stderr, "%s\n", warnMsg.c_str());
     }
+#if GMX_SIMD_ARM_SVE
+    if ((compiled == SimdType::Arm_Sve) && (svcntb() != GMX_SIMD_ARM_SVE_LENGTH / 8))
+    {
+        gmx_exit_on_fatal_error(ExitType_Abort, 1);
+    }
+#endif
 
     return (wanted == compiled);
 }
index 1fd891658dfbb1909a2a859081dd59b12de491ce..2e108126749e06f8c8efc3f78a7cef4f7980f748 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -70,6 +70,7 @@ enum class SimdType
     X86_Mic,       //!< Knight's corner
     Arm_Neon,      //!< 32-bit ARM NEON
     Arm_NeonAsimd, //!< 64-bit ARM AArch64 Advanced SIMD
+    Arm_Sve,       //!< ARM Scalable Vector Extensions
     Ibm_Vmx,       //!< IBM VMX SIMD (Altivec on Power6 and later)
     Ibm_Vsx,       //!< IBM VSX SIMD (Power7 and later)
     Fujitsu_HpcAce //!< Fujitsu K-computer
index 78730cef426b1e024c1308753de44c3b66cfffe9..6f7bea406c8ac459a268e6d9d8b557b642bffa26 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2017,2018, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2017,2018,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(SimdUnitTests simd-test
-                  bootstrap_loadstore.cpp
-                 base.cpp
-                  simd.cpp
-                 simd_floatingpoint.cpp
-                  simd_floatingpoint_util.cpp
-                  simd_vector_operations.cpp
-                  simd_math.cpp
-                  simd_memory.cpp
-                 simd_integer.cpp
-                  simd4.cpp
-                  simd4_floatingpoint.cpp
-                  simd4_vector_operations.cpp
-                  simd4_math.cpp
-                  scalar.cpp
-                  scalar_util.cpp
-                  scalar_math.cpp)
+    CPP_SOURCE_FILES
+        base.cpp
+        bootstrap_loadstore.cpp
+        scalar.cpp
+        scalar_util.cpp
+        scalar_math.cpp
+        simd.cpp
+        simd_floatingpoint.cpp
+        simd_floatingpoint_util.cpp
+        simd_integer.cpp
+        simd_math.cpp
+        simd_memory.cpp
+        simd_vector_operations.cpp
+        simd4.cpp
+        simd4_floatingpoint.cpp
+        simd4_math.cpp
+        simd4_vector_operations.cpp
+        )
index 52b36e15b8530c6f9551c31cdffe583f6b4c8db1..97935508035287856ebac6d7da59bceb6034152a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 4eb3d30a4d550126f441414657fede0e0e5d3bd1..e40db8aa9f6bcc1981b609f5423baedbce86788d 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 556e5b420592b847008834d5581b2cb740691585..048386b148d573350e587da53145acecbcd011d3 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 69aa82b216a5060196399b71c1587914d1884b42..490e1f183cf87d3916993b163dd1570f325501ce 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e519643d7cd6d22b0a97cc67956df0e6b7e08689..70b425074e468db8ace299f9009918f6f886c622 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 9e94c2ea73b4c726e3a6d36632696c6ff224f510..41fd57415e6172c26942f65b9bb9db20e31dd02c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index ea5c4f584497b3f88b9d14adc93212a827456d43..2c786ab4f4c3dfa4c1a723f53f31ebe1925ee981 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index a0d2d9649883716deba66507e9e655ecf17d92fc..3dbcbc20284f2d9c62ee0da084e02855299a7b2a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 89cb01018e5b06eb3e3c376714f55014ef617f04..ee43c3cada45242f2fe5c960048e3b7ae7c7dac1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -810,11 +810,11 @@ TEST_F(SimdFloatingpointUtilTest, incrDualHsimdOverlapping)
     }
 }
 
-TEST_F(SimdFloatingpointUtilTest, decrHsimd)
+TEST_F(SimdFloatingpointUtilTest, decr3Hsimd)
 {
-    SimdReal               v0;
-    real                   ref[GMX_SIMD_REAL_WIDTH / 2];
-    int                    i;
+    SimdReal               v0, v1, v2;
+    real                   ref[3 * GMX_SIMD_REAL_WIDTH / 2];
+    int                    i, j;
     FloatingPointTolerance tolerance(defaultRealTolerance());
 
     // Point p to the upper half of val1_
@@ -823,11 +823,23 @@ TEST_F(SimdFloatingpointUtilTest, decrHsimd)
     {
         ref[i] = val0_[i] - (val1_[i] + p[i]);
     }
+    p = val2_ + GMX_SIMD_REAL_WIDTH / 2;
+    for (j = 0; j < GMX_SIMD_REAL_WIDTH / 2; i++, j++)
+    {
+        ref[i] = val0_[i] - (val2_[j] + p[j]);
+    }
+    p = val3_ + GMX_SIMD_REAL_WIDTH / 2;
+    for (j = 0; j < GMX_SIMD_REAL_WIDTH / 2; i++, j++)
+    {
+        ref[i] = val0_[i] - (val3_[j] + p[j]);
+    }
 
     v0 = load<SimdReal>(val1_);
-    decrHsimd(val0_, v0);
+    v1 = load<SimdReal>(val2_);
+    v2 = load<SimdReal>(val3_);
+    decr3Hsimd(val0_, v0, v1, v2);
 
-    for (i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
+    for (i = 0; i < 3 * GMX_SIMD_REAL_WIDTH / 2; i++)
     {
         EXPECT_REAL_EQ_TOL(ref[i], val0_[i], tolerance);
     }
index d0422fda88382f13912c01f06a34f2ae96c1a7f6..ffcec29abc9a5c819a0fb9d00b08e77463ccc10f 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -227,8 +228,8 @@ TEST_F(SimdIntegerTest, cvtI2R)
 {
     GMX_EXPECT_SIMD_REAL_EQ(setSimdRealFrom1R(2.0), cvtI2R(SimdInt32(2)));
     GMX_EXPECT_SIMD_REAL_EQ(setSimdRealFrom1R(-2.0), cvtI2R(SimdInt32(-2)));
-    GMX_EXPECT_SIMD_REAL_EQ(setSimdRealFrom1R(102448689), cvtI2R(SimdInt32(102448689)));
-    GMX_EXPECT_SIMD_REAL_EQ(setSimdRealFrom1R(-102448689), cvtI2R(SimdInt32(-102448689)));
+    GMX_EXPECT_SIMD_REAL_EQ(setSimdRealFrom1R(102448689._real), cvtI2R(SimdInt32(102448689)));
+    GMX_EXPECT_SIMD_REAL_EQ(setSimdRealFrom1R(-102448689._real), cvtI2R(SimdInt32(-102448689)));
 }
 #    endif // GMX_SIMD_HAVE_REAL
 
index f5e9f74e3855324a2bb72bfdd8a0ed8688a3ed85..20d7b916dbdf47b26b305a2806a8b3d355de9db4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -149,11 +149,11 @@ public:
      *        to avoid altering any value the user has set on the command line; since
      *        it's a static member, changing it would have permanent effect.
      */
-    std::vector<real> generateTestPoints(Range range, std::size_t points);
+    static std::vector<real> generateTestPoints(Range range, std::size_t points);
 
     /*! \brief Test routine for the test point vector generation
      */
-    void generateTestPointsTest();
+    static void generateTestPointsTest();
 };
 
 /*! \brief Test approximate equality of SIMD vs reference version of a function.
index b9604b49b895168a376a30ddbbde7c38ba7ecde2..22f19fc03de5d5475f406eea3f93c1ec7472b95e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -94,10 +94,10 @@ template<typename TypeParam>
 class ArrayRefTest : public test::SimdTest
 {
 public:
-    using ArrayRefType = TypeParam;
-    using PointerType  = typename ArrayRefType::pointer;
-    using ValueType    = typename ArrayRefType::value_type;
-    using ElementType  = std::remove_const_t<typename gmx::internal::SimdTraits<ValueType>::type>;
+    using ArrayRefType         = TypeParam;
+    using PointerType          = typename ArrayRefType::pointer;
+    using ValueType            = typename ArrayRefType::value_type;
+    using ElementType          = std::remove_const_t<gmx::internal::SimdTraitsT<ValueType>>;
     static constexpr int width = gmx::internal::SimdTraits<ValueType>::width;
 
     /*! \brief Run the same tests all the time
index f7d8cf5d377c59233a749d7b3d6be6a52393e847..5e1315cfeb1e2869763e98bfc0fa88518b92b220 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 0a05fe62ec3defb74ce299375a86af68044167b0..e6debca14eacd2f66ac809f715bf0b334961f9d0 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -125,6 +126,8 @@ class SwapCoordinates final : public IMDModule
     IMdpOptionProvider* mdpOptionProvider() override { return nullptr; }
     IMDOutputProvider*  outputProvider() override { return nullptr; }
     void                initForceProviders(ForceProviders* /* forceProviders */) override {}
+    void subscribeToSimulationSetupNotifications(MdModulesNotifier* /* notifier */) override {}
+    void subscribeToPreProcessingNotifications(MdModulesNotifier* /* notifier */) override {}
 };
 
 std::unique_ptr<IMDModule> createSwapCoordinatesModule()
@@ -930,9 +933,10 @@ static void bc_initial_concentrations(t_commrec* cr, t_swapcoords* swap, t_swap*
 
         for (ic = 0; ic < eCompNR; ic++)
         {
-            gmx_bcast(sizeof(g->comp[ic].nMolReq), &(g->comp[ic].nMolReq), cr);
-            gmx_bcast(sizeof(g->comp[ic].nMol), &(g->comp[ic].nMol), cr);
-            gmx_bcast(swap->nAverage * sizeof(g->comp[ic].nMolPast[0]), g->comp[ic].nMolPast, cr);
+            gmx_bcast(sizeof(g->comp[ic].nMolReq), &(g->comp[ic].nMolReq), cr->mpi_comm_mygroup);
+            gmx_bcast(sizeof(g->comp[ic].nMol), &(g->comp[ic].nMol), cr->mpi_comm_mygroup);
+            gmx_bcast(swap->nAverage * sizeof(g->comp[ic].nMolPast[0]), g->comp[ic].nMolPast,
+                      cr->mpi_comm_mygroup);
         }
     }
 }
@@ -1208,7 +1212,7 @@ static void detect_flux_per_channel_init(t_swap* s, swaphistory_t* swapstate, co
  * If this is not correct, the ion counts per channel will be very likely
  * wrong.
  */
-static void outputStartStructureIfWanted(gmx_mtop_t* mtop, rvec* x, int ePBC, const matrix box)
+static void outputStartStructureIfWanted(gmx_mtop_t* mtop, rvec* x, PbcType pbcType, const matrix box)
 {
     char* env = getenv("GMX_COMPELDUMP");
 
@@ -1222,7 +1226,7 @@ static void outputStartStructureIfWanted(gmx_mtop_t* mtop, rvec* x, int ePBC, co
                 SwS, SwSEmpty);
 
         write_sto_conf_mtop("CompELAssumedWholeConfiguration.pdb", *mtop->name, mtop, x, nullptr,
-                            ePBC, box);
+                            pbcType, box);
     }
 }
 
@@ -1288,10 +1292,10 @@ static void init_swapstate(swaphistory_t*    swapstate,
         copy_rvecn(x, x_pbc, 0, mtop->natoms);
 
         /* This can only make individual molecules whole, not multimers */
-        do_pbc_mtop(ir->ePBC, box, mtop, x_pbc);
+        do_pbc_mtop(ir->pbcType, box, mtop, x_pbc);
 
         /* Output the starting structure? */
-        outputStartStructureIfWanted(mtop, x_pbc, ir->ePBC, box);
+        outputStartStructureIfWanted(mtop, x_pbc, ir->pbcType, box);
 
         /* If this is the first run (i.e. no checkpoint present) we assume
          * that the starting positions give us the correct PBC representation */
@@ -1559,7 +1563,8 @@ t_swap* init_swapcoords(FILE*                       fplog,
         for (int ig = eGrpSplit0; ig <= eGrpSplit1; ig++)
         {
             g = &(s->group[ig]);
-            gmx_bcast((g->atomset.numAtomsGlobal()) * sizeof((g->xc_old)[0]), g->xc_old, (cr));
+            gmx_bcast((g->atomset.numAtomsGlobal()) * sizeof((g->xc_old)[0]), g->xc_old,
+                      cr->mpi_comm_mygroup);
         }
     }
 
@@ -1943,7 +1948,7 @@ gmx_bool do_swapcoords(t_commrec*     cr,
 
     sc = ir->swap;
 
-    set_pbc(s->pbc, ir->ePBC, box);
+    set_pbc(s->pbc, ir->pbcType, box);
 
     /* Assemble the positions of the split groups, i.e. the channels.
      * Here we also pass a shifts array to communicate_group_positions(), so that it can make
index 913ef99b240bed00dd9fa3dcd539c5451437c692..6cfdd53fd4dced594a64809f95e18e0abc450b40 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2013, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 5d1834ee947ae31a553e6e59e03f0be2da55bbcb..2e61e90b08c0f5268cf5d934c9259d6eb4877314 100644 (file)
 
 #include "gromacs/fileio/xvgr.h"
 #include "gromacs/math/functions.h"
+#include "gromacs/math/multidimarray.h"
 #include "gromacs/math/units.h"
 #include "gromacs/math/utilities.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/mdspan/extensions.h"
 #include "gromacs/mdtypes/fcdata.h"
+#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/nblist.h"
 #include "gromacs/utility/arrayref.h"
@@ -76,9 +79,6 @@ enum
     etabLJ6Switch,
     etabLJ12Switch,
     etabCOULSwitch,
-    etabLJ6Encad,
-    etabLJ12Encad,
-    etabCOULEncad,
     etabEXPMIN,
     etabUSER,
     etabNR
@@ -96,27 +96,12 @@ typedef struct
 /* This structure holds name and a flag that tells whether
    this is a Coulomb type funtion */
 static const t_tab_props tprops[etabNR] = {
-    { "LJ6", FALSE },
-    { "LJ12", FALSE },
-    { "LJ6Shift", FALSE },
-    { "LJ12Shift", FALSE },
-    { "Shift", TRUE },
-    { "RF", TRUE },
-    { "RF-zero", TRUE },
-    { "COUL", TRUE },
-    { "Ewald", TRUE },
-    { "Ewald-Switch", TRUE },
-    { "Ewald-User", TRUE },
-    { "Ewald-User-Switch", TRUE },
-    { "LJ6Ewald", FALSE },
-    { "LJ6Switch", FALSE },
-    { "LJ12Switch", FALSE },
-    { "COULSwitch", TRUE },
-    { "LJ6-Encad shift", FALSE },
-    { "LJ12-Encad shift", FALSE },
-    { "COUL-Encad shift", TRUE },
-    { "EXPMIN", FALSE },
-    { "USER", FALSE },
+    { "LJ6", FALSE },         { "LJ12", FALSE },      { "LJ6Shift", FALSE },
+    { "LJ12Shift", FALSE },   { "Shift", TRUE },      { "RF", TRUE },
+    { "RF-zero", TRUE },      { "COUL", TRUE },       { "Ewald", TRUE },
+    { "Ewald-Switch", TRUE }, { "Ewald-User", TRUE }, { "Ewald-User-Switch", TRUE },
+    { "LJ6Ewald", FALSE },    { "LJ6Switch", FALSE }, { "LJ12Switch", FALSE },
+    { "COULSwitch", TRUE },   { "EXPMIN", FALSE },    { "USER", FALSE },
 };
 
 typedef struct
@@ -598,19 +583,23 @@ static void set_forces(FILE* fp, int angle, int nx, double h, double v[], double
 static void read_tables(FILE* fp, const char* filename, int ntab, int angle, t_tabledata td[])
 {
     char     buf[STRLEN];
-    double **yy = nullptr, start, end, dx0, dx1, ssd, vm, vp, f, numf;
-    int      k, i, nx, nx0 = 0, ny, nny, ns;
+    double   start, end, dx0, dx1, ssd, vm, vp, f, numf;
+    int      k, i, nx0 = 0, nny, ns;
     gmx_bool bAllZero, bZeroV, bZeroF;
     double   tabscale;
 
     nny               = 2 * ntab + 1;
     std::string libfn = gmx::findLibraryFile(filename);
-    nx                = read_xvg(libfn.c_str(), &yy, &ny);
-    if (ny != nny)
+    gmx::MultiDimArray<std::vector<double>, gmx::dynamicExtents2D> xvgData    = readXvgData(libfn);
+    int                                                            numColumns = xvgData.extent(0);
+    if (numColumns != nny)
     {
         gmx_fatal(FARGS, "Trying to read file %s, but nr columns = %d, should be %d", libfn.c_str(),
-                  ny, nny);
+                  numColumns, nny);
     }
+    int numRows = xvgData.extent(1);
+
+    const auto& yy = xvgData.asView();
     if (angle == 0)
     {
         if (yy[0][0] != 0.0)
@@ -630,18 +619,18 @@ static void read_tables(FILE* fp, const char* filename, int ntab, int angle, t_t
             start = -180.0;
         }
         end = 180.0;
-        if (yy[0][0] != start || yy[0][nx - 1] != end)
+        if (yy[0][0] != start || yy[0][numRows - 1] != end)
         {
             gmx_fatal(FARGS, "The angles in file %s should go from %f to %f instead of %f to %f\n",
-                      libfn.c_str(), start, end, yy[0][0], yy[0][nx - 1]);
+                      libfn.c_str(), start, end, yy[0][0], yy[0][numRows - 1]);
         }
     }
 
-    tabscale = (nx - 1) / (yy[0][nx - 1] - yy[0][0]);
+    tabscale = (numRows - 1) / (yy[0][numRows - 1] - yy[0][0]);
 
     if (fp)
     {
-        fprintf(fp, "Read user tables from %s with %d data points.\n", libfn.c_str(), nx);
+        fprintf(fp, "Read user tables from %s with %d data points.\n", libfn.c_str(), numRows);
         if (angle == 0)
         {
             fprintf(fp, "Tabscale = %g points/nm\n", tabscale);
@@ -653,7 +642,7 @@ static void read_tables(FILE* fp, const char* filename, int ntab, int angle, t_t
     {
         bZeroV = TRUE;
         bZeroF = TRUE;
-        for (i = 0; (i < nx); i++)
+        for (i = 0; (i < numRows); i++)
         {
             if (i >= 2)
             {
@@ -699,7 +688,8 @@ static void read_tables(FILE* fp, const char* filename, int ntab, int angle, t_t
 
         if (!bZeroV && bZeroF)
         {
-            set_forces(fp, angle, nx, 1 / tabscale, yy[1 + k * 2], yy[1 + k * 2 + 1], k);
+            set_forces(fp, angle, numRows, 1 / tabscale, yy[1 + k * 2].data(),
+                       yy[1 + k * 2 + 1].data(), k);
         }
         else
         {
@@ -708,7 +698,7 @@ static void read_tables(FILE* fp, const char* filename, int ntab, int angle, t_t
              */
             ssd = 0;
             ns  = 0;
-            for (i = 1; (i < nx - 1); i++)
+            for (i = 1; (i < numRows - 1); i++)
             {
                 vm = yy[1 + 2 * k][i - 1];
                 vp = yy[1 + 2 * k][i + 1];
@@ -754,19 +744,14 @@ static void read_tables(FILE* fp, const char* filename, int ntab, int angle, t_t
 
     for (k = 0; (k < ntab); k++)
     {
-        init_table(nx, nx0, tabscale, &(td[k]), TRUE);
-        for (i = 0; (i < nx); i++)
+        init_table(numRows, nx0, tabscale, &(td[k]), TRUE);
+        for (i = 0; (i < numRows); i++)
         {
             td[k].x[i] = yy[0][i];
             td[k].v[i] = yy[2 * k + 1][i];
             td[k].f[i] = yy[2 * k + 2][i];
         }
     }
-    for (i = 0; (i < ny); i++)
-    {
-        sfree(yy[i]);
-    }
-    sfree(yy);
 }
 
 static void done_tabledata(t_tabledata* td)
@@ -796,7 +781,7 @@ static void fill_table(t_tabledata* td, int tp, const interaction_const_t* ic, g
     int    i;
     double reppow, p;
     double r1, rc, r12, r13;
-    double r, r2, r6, rc2, rc6, rc12;
+    double r, r2, r6, rc2;
     double expr, Vtab, Ftab;
     /* Parameters for David's function */
     double A = 0, B = 0, C = 0, A_3 = 0, B_4 = 0;
@@ -882,8 +867,9 @@ static void fill_table(t_tabledata* td, int tp, const interaction_const_t* ic, g
 
     if (bPotentialShift)
     {
-        rc2 = rc * rc;
-        rc6 = 1.0 / (rc2 * rc2 * rc2);
+        rc2        = rc * rc;
+        double rc6 = 1.0 / (rc2 * rc2 * rc2);
+        double rc12;
         if (gmx_within_tol(reppow, 12.0, 10 * GMX_DOUBLE_EPS))
         {
             rc12 = rc6 * rc6;
@@ -979,9 +965,6 @@ static void fill_table(t_tabledata* td, int tp, const interaction_const_t* ic, g
             swi1 = 0.0;
         }
 
-        rc6 = rc * rc * rc;
-        rc6 = 1.0 / (rc6 * rc6);
-
         switch (tp)
         {
             case etabLJ6:
@@ -1013,30 +996,6 @@ static void fill_table(t_tabledata* td, int tp, const interaction_const_t* ic, g
                     Ftab = reppow * Vtab / r;
                 }
                 break;
-            case etabLJ6Encad:
-                if (r < rc)
-                {
-                    Vtab = -(r6 - 6.0 * (rc - r) * rc6 / rc - rc6);
-                    Ftab = -(6.0 * r6 / r - 6.0 * rc6 / rc);
-                }
-                else /* r>rc */
-                {
-                    Vtab = 0;
-                    Ftab = 0;
-                }
-                break;
-            case etabLJ12Encad:
-                if (r < rc)
-                {
-                    Vtab = -(r6 - 6.0 * (rc - r) * rc6 / rc - rc6);
-                    Ftab = -(6.0 * r6 / r - 6.0 * rc6 / rc);
-                }
-                else /* r>rc */
-                {
-                    Vtab = 0;
-                    Ftab = 0;
-                }
-                break;
             case etabCOUL:
                 Vtab = 1.0 / r;
                 Ftab = 1.0 / r2;
@@ -1081,18 +1040,6 @@ static void fill_table(t_tabledata* td, int tp, const interaction_const_t* ic, g
                 Vtab = expr;
                 Ftab = expr;
                 break;
-            case etabCOULEncad:
-                if (r < rc)
-                {
-                    Vtab = 1.0 / r - (rc - r) / (rc * rc) - 1.0 / rc;
-                    Ftab = 1.0 / r2 - 1.0 / (rc * rc);
-                }
-                else /* r>rc */
-                {
-                    Vtab = 0;
-                    Ftab = 0;
-                }
-                break;
             default:
                 gmx_fatal(FARGS, "Table type %d not implemented yet. (%s,%d)", tp, __FILE__, __LINE__);
         }
@@ -1215,7 +1162,6 @@ static void set_table_type(int tabsel[], const interaction_const_t* ic, gmx_bool
         case eelRF_ZERO: tabsel[etiCOUL] = etabRF_ZERO; break;
         case eelSWITCH: tabsel[etiCOUL] = etabCOULSwitch; break;
         case eelUSER: tabsel[etiCOUL] = etabUSER; break;
-        case eelENCADSHIFT: tabsel[etiCOUL] = etabCOULEncad; break;
         default: gmx_fatal(FARGS, "Invalid eeltype %d", eltype);
     }
 
@@ -1254,10 +1200,6 @@ static void set_table_type(int tabsel[], const interaction_const_t* ic, gmx_bool
                 tabsel[etiLJ6]  = etabLJ6;
                 tabsel[etiLJ12] = etabLJ12;
                 break;
-            case evdwENCADSHIFT:
-                tabsel[etiLJ6]  = etabLJ6Encad;
-                tabsel[etiLJ12] = etabLJ12Encad;
-                break;
             case evdwPME:
                 tabsel[etiLJ6]  = etabLJ6Ewald;
                 tabsel[etiLJ12] = etabLJ12;
@@ -1377,9 +1319,9 @@ t_forcetable* make_tables(FILE* out, const interaction_const_t* ic, const char*
 
     /* Each table type (e.g. coul,lj6,lj12) requires four
      * numbers per table->n+1 data points. For performance reasons we want
-     * the table data to be aligned to a 32-byte boundary.
+     * the table data to be aligned to (at least) a 32-byte boundary.
      */
-    snew_aligned(table->data, table->stride * (table->n + 1) * sizeof(real), 32);
+    table->data.resize(table->stride * (table->n + 1) * sizeof(real));
 
     for (int k = 0; (k < etiNR); k++)
     {
@@ -1425,7 +1367,7 @@ t_forcetable* make_tables(FILE* out, const interaction_const_t* ic, const char*
         }
 
         copy2table(table->n, k * table->formatsize, table->stride, td[k].x, td[k].v, td[k].f,
-                   scalefactor, table->data);
+                   scalefactor, table->data.data());
 
         done_tabledata(&(td[k]));
     }
@@ -1454,8 +1396,8 @@ bondedtable_t make_bonded_table(FILE* fplog, const char* fn, int angle)
     }
     tab.n     = td.nx;
     tab.scale = td.tabscale;
-    snew(tab.data, tab.n * stride);
-    copy2table(tab.n, 0, stride, td.x, td.v, td.f, 1.0, tab.data);
+    tab.data.resize(tab.n * stride);
+    copy2table(tab.n, 0, stride, td.x, td.v, td.f, 1.0, tab.data.data());
     done_tabledata(&td);
 
     return tab;
@@ -1481,8 +1423,8 @@ makeDispersionCorrectionTable(FILE* fp, const interaction_const_t* ic, real rtab
     dispersionCorrectionTable->ninteractions = 2;
     dispersionCorrectionTable->stride =
             dispersionCorrectionTable->formatsize * dispersionCorrectionTable->ninteractions;
-    snew_aligned(dispersionCorrectionTable->data,
-                 dispersionCorrectionTable->stride * (dispersionCorrectionTable->n + 1), 32);
+    dispersionCorrectionTable->data.resize(dispersionCorrectionTable->stride
+                                           * (dispersionCorrectionTable->n + 1));
 
     for (int i = 0; i <= fullTable->n; i++)
     {
@@ -1502,14 +1444,8 @@ t_forcetable::t_forcetable(enum gmx_table_interaction interaction, enum gmx_tabl
     r(0),
     n(0),
     scale(0),
-    data(nullptr),
     formatsize(0),
     ninteractions(0),
     stride(0)
 {
 }
-
-t_forcetable::~t_forcetable()
-{
-    sfree_aligned(data);
-}
index 7b687250dc3ad81976a279d85c8049a032574b5f..a889cca9536bee87466e8e559fd0e4e0b690c434 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <memory>
 
-#include "gromacs/mdtypes/fcdata.h"
-#include "gromacs/mdtypes/forcerec.h"
-#include "gromacs/mdtypes/interaction_const.h"
 #include "gromacs/utility/real.h"
 
+struct EwaldCorrectionTables;
+struct bondedtable_t;
+struct interaction_const_t;
+struct t_forcetable;
+
 /*! \brief Flag to select user tables for make_tables */
 #define GMX_MAKETABLES_FORCEUSER (1 << 0)
 /*! \brief Flag to only make 1,4 pair tables for make_tables */
index 2733b2e961f9c2af2895adf9632a4eb885ee1933..695e06dc8893d87ac453507ed07d6a132ea0e179 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -68,7 +68,7 @@ namespace
 
 /*! \brief Construct the data for a single quadratic table from analytical functions
  *
- * \param[in]  function             Analytical functiojn
+ * \param[in]  function             Analytical function
  * \param[in]  derivative           Analytical derivative
  * \param[in]  range                Upper/lower limit of region to tabulate
  * \param[in]  spacing              Distance between table points
@@ -169,11 +169,11 @@ void fillSingleQuadraticSplineTableData(ArrayRef<const double>       function,
 
     std::vector<double> thirdDerivative(internal::vectorSecondDerivative(derivative, inputSpacing));
 
-    double      maxMagnitude      = 0.0001 * GMX_REAL_MAX;
-    bool        functionIsInRange = true;
-    std::size_t lastIndexInRange  = endIndex - 1;
+    double maxMagnitude      = 0.0001 * GMX_REAL_MAX;
+    bool   functionIsInRange = true;
+    int    lastIndexInRange  = static_cast<int>(endIndex) - 1;
 
-    for (int i = endIndex - 1; i >= 0; i--)
+    for (int i = lastIndexInRange; i >= 0; i--)
     {
         double x = i * spacing;
         double tmpFunctionValue;
@@ -220,6 +220,7 @@ void fillSingleQuadraticSplineTableData(ArrayRef<const double>       function,
         {
             // Once the function or derivative (more likely) has reached very large values,
             // we simply make a linear function from the last in-range value of the derivative.
+            GMX_ASSERT(lastIndexInRange >= 0, "Array index is unexpectedly negative.");
             double lastIndexFunction   = (*functionTableData)[lastIndexInRange];
             double lastIndexDerivative = (*derivativeTableData)[lastIndexInRange];
             (*functionTableData)[i] =
index c1cd34191ba983fb66c5e618497a2512411a1f7e..d40f412d7c341d36be0a8760da33b1561b716143 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2016, by the GROMACS development team, led by
+# Copyright (c) 2016,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,5 +33,6 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(TableUnitTests table-test
-                  splinetable.cpp
-                  )
+    CPP_SOURCE_FILES
+        splinetable.cpp
+        )
index 9afd05e13b5e227d1f0f0c372510ff22f7c56cbd..22a1c70eec9ad851df1f6fe19f4d761d3e3a645c 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2017,2019, by the GROMACS development team, led by
+# Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
+# Note that this is a higher-level module that should not need
+# special compilation to suit e.g. GPU or MPI dependencies.
+# If you find you want to do that, consider a preliminary
+# refactoring.
 gmx_add_libgromacs_sources(
     decidegpuusage.cpp
     decidesimulationworkload.cpp
index e9f98298b824f1e28e4722dd0d6dc74f709f7a97..3ec7ce027e3bb3c0fed3876f0a7bb0315e517789 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019 by the GROMACS development team.
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -57,7 +58,7 @@
 #include "gromacs/hardware/hardwaretopology.h"
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
-#include "gromacs/mdlib/update_constrain_cuda.h"
+#include "gromacs/mdlib/update_constrain_gpu.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
@@ -84,16 +85,19 @@ namespace
 const char* g_specifyEverythingFormatString =
         "When you use mdrun -gputasks, %s must be set to non-default "
         "values, so that the device IDs can be interpreted correctly."
-#if GMX_GPU != GMX_GPU_NONE
+#if GMX_GPU
         " If you simply want to restrict which GPUs are used, then it is "
         "better to use mdrun -gpu_id. Otherwise, setting the "
-#    if GMX_GPU == GMX_GPU_CUDA
+#    if GMX_GPU_CUDA
         "CUDA_VISIBLE_DEVICES"
-#    elif GMX_GPU == GMX_GPU_OPENCL
+#    elif GMX_GPU_OPENCL
         // Technically there is no portable way to do this offered by the
         // OpenCL standard, but the only current relevant case for GROMACS
         // is AMD OpenCL, which offers this variable.
         "GPU_DEVICE_ORDINAL"
+#    elif GMX_GPU_SYCL
+        // SYCL-TODO:
+        "How to restrict visible devices in SYCL?"
 #    else
 #        error "Unreachable branch"
 #    endif
@@ -105,7 +109,7 @@ const char* g_specifyEverythingFormatString =
 } // namespace
 
 bool decideWhetherToUseGpusForNonbondedWithThreadMpi(const TaskTarget        nonbondedTarget,
-                                                     const std::vector<int>& gpuIdsToUse,
+                                                     const int               numDevicesToUse,
                                                      const std::vector<int>& userGpuTaskAssignment,
                                                      const EmulateGpuNonbonded emulateGpuNonbonded,
                                                      const bool buildSupportsNonbondedOnGpu,
@@ -141,26 +145,22 @@ bool decideWhetherToUseGpusForNonbondedWithThreadMpi(const TaskTarget        non
     // Because this is thread-MPI, we already know about the GPUs that
     // all potential ranks can use, and can use that in a global
     // decision that will later be consistent.
-    auto haveGpus = !gpuIdsToUse.empty();
-
     // If we get here, then the user permitted or required GPUs.
-    return haveGpus;
+    return (numDevicesToUse > 0);
 }
 
 bool decideWhetherToUseGpusForPmeWithThreadMpi(const bool              useGpuForNonbonded,
                                                const TaskTarget        pmeTarget,
-                                               const std::vector<int>& gpuIdsToUse,
+                                               const int               numDevicesToUse,
                                                const std::vector<int>& userGpuTaskAssignment,
                                                const gmx_hw_info_t&    hardwareInfo,
                                                const t_inputrec&       inputrec,
-                                               const gmx_mtop_t&       mtop,
                                                const int               numRanksPerSimulation,
                                                const int               numPmeRanksPerSimulation)
 {
     // First, exclude all cases where we can't run PME on GPUs.
     if ((pmeTarget == TaskTarget::Cpu) || !useGpuForNonbonded || !pme_gpu_supports_build(nullptr)
-        || !pme_gpu_supports_hardware(hardwareInfo, nullptr)
-        || !pme_gpu_supports_input(inputrec, mtop, nullptr))
+        || !pme_gpu_supports_hardware(hardwareInfo, nullptr) || !pme_gpu_supports_input(inputrec, nullptr))
     {
         // PME can't run on a GPU. If the user required that, we issue
         // an error later.
@@ -220,7 +220,7 @@ bool decideWhetherToUseGpusForPmeWithThreadMpi(const bool              useGpuFor
     {
         // PME can run well on a GPU shared with NB, and we permit
         // mdrun to default to try that.
-        return !gpuIdsToUse.empty();
+        return numDevicesToUse > 0;
     }
 
     if (numRanksPerSimulation < 1)
@@ -228,7 +228,7 @@ bool decideWhetherToUseGpusForPmeWithThreadMpi(const bool              useGpuFor
         // Full automated mode for thread-MPI (the default). PME can
         // run well on a GPU shared with NB, and we permit mdrun to
         // default to it if there is only one GPU available.
-        return (gpuIdsToUse.size() == 1);
+        return (numDevicesToUse == 1);
     }
 
     // Not enough support for PME on GPUs for anything else
@@ -327,7 +327,6 @@ bool decideWhetherToUseGpusForPme(const bool              useGpuForNonbonded,
                                   const std::vector<int>& userGpuTaskAssignment,
                                   const gmx_hw_info_t&    hardwareInfo,
                                   const t_inputrec&       inputrec,
-                                  const gmx_mtop_t&       mtop,
                                   const int               numRanksPerSimulation,
                                   const int               numPmeRanksPerSimulation,
                                   const bool              gpusWereDetected)
@@ -364,7 +363,7 @@ bool decideWhetherToUseGpusForPme(const bool              useGpuForNonbonded,
         }
         return false;
     }
-    if (!pme_gpu_supports_input(inputrec, mtop, &message))
+    if (!pme_gpu_supports_input(inputrec, &message))
     {
         if (pmeTarget == TaskTarget::Gpu)
         {
@@ -587,6 +586,11 @@ bool decideWhetherToUseGpuForUpdate(const bool                     isDomainDecom
         }
     }
 
+    if (inputrec.useMts)
+    {
+        errorMessage += "Multiple time stepping is not supported.\n";
+    }
+
     if (inputrec.eConstrAlg == econtSHAKE && hasAnyConstraints && gmx_mtop_ftype_count(mtop, F_CONSTR) > 0)
     {
         errorMessage += "SHAKE constraints are not supported.\n";
@@ -604,7 +608,7 @@ bool decideWhetherToUseGpuForUpdate(const bool                     isDomainDecom
     {
         errorMessage += "Compatible GPUs must have been found.\n";
     }
-    if (GMX_GPU != GMX_GPU_CUDA)
+    if (!GMX_GPU_CUDA)
     {
         errorMessage += "Only a CUDA build is supported.\n";
     }
@@ -616,9 +620,12 @@ bool decideWhetherToUseGpuForUpdate(const bool                     isDomainDecom
     {
         errorMessage += "Nose-Hoover temperature coupling is not supported.\n";
     }
-    if (!(inputrec.epc == epcNO || inputrec.epc == epcPARRINELLORAHMAN || inputrec.epc == epcBERENDSEN))
+    if (!(inputrec.epc == epcNO || inputrec.epc == epcPARRINELLORAHMAN
+          || inputrec.epc == epcBERENDSEN || inputrec.epc == epcCRESCALE))
     {
-        errorMessage += "Only Parrinello-Rahman and Berendsen pressure coupling are supported.\n";
+        errorMessage +=
+                "Only Parrinello-Rahman, Berendsen, and C-rescale pressure coupling are "
+                "supported.\n";
     }
     if (EEL_PME_EWALD(inputrec.coulombtype) && inputrec.epsilon_surface != 0)
     {
@@ -642,10 +649,10 @@ bool decideWhetherToUseGpuForUpdate(const bool                     isDomainDecom
         // The graph is needed, but not supported
         errorMessage += "Orientation restraints are not supported.\n";
     }
-    if (inputrec.efep != efepNO)
+    if (inputrec.efep != efepNO
+        && (haveFreeEnergyType(inputrec, efptBONDED) || haveFreeEnergyType(inputrec, efptMASS)))
     {
-        // Actually all free-energy options except for mass and constraint perturbation are supported
-        errorMessage += "Free energy perturbations are not supported.\n";
+        errorMessage += "Free energy perturbation for mass and constraints are not supported.\n";
     }
     const auto particleTypes = gmx_mtop_particletype_count(mtop);
     if (particleTypes[eptShell] > 0)
@@ -671,10 +678,10 @@ bool decideWhetherToUseGpuForUpdate(const bool                     isDomainDecom
     {
         errorMessage += "Non-connecting constraints are not supported\n";
     }
-    if (!UpdateConstrainCuda::isNumCoupledConstraintsSupported(mtop))
+    if (!UpdateConstrainGpu::isNumCoupledConstraintsSupported(mtop))
     {
         errorMessage +=
-                "The number of coupled constraints is higher than supported in the CUDA LINCS "
+                "The number of coupled constraints is higher than supported in the GPU LINCS "
                 "code.\n";
     }
 
@@ -705,4 +712,15 @@ bool decideWhetherToUseGpuForUpdate(const bool                     isDomainDecom
             || (updateTarget == TaskTarget::Auto && devFlags.forceGpuUpdateDefault));
 }
 
+bool decideWhetherToUseGpuForHalo(const DevelopmentFeatureFlags& devFlags,
+                                  bool                           havePPDomainDecomposition,
+                                  bool                           useGpuForNonbonded,
+                                  bool                           useModularSimulator,
+                                  bool                           doRerun,
+                                  bool                           haveEnergyMinimization)
+{
+    return havePPDomainDecomposition && devFlags.enableGpuHaloExchange && useGpuForNonbonded
+           && !useModularSimulator && !doRerun && !haveEnergyMinimization;
+}
+
 } // namespace gmx
index bfb002547a36996a9beb535ee09d108c55b26826..7dd6ae9b3008fd451dae97f4615f9e33085cb6d7 100644 (file)
@@ -101,20 +101,24 @@ class MDAtoms;
  * user. So we need to consider this before any automated choice of
  * the number of thread-MPI ranks.
  *
- * \param[in]  nonbondedTarget             The user's choice for mdrun -nb for where to assign short-ranged nonbonded interaction tasks.
- * \param[in]  gpuIdsToUse                 The compatible GPUs that the user permitted us to use.
- * \param[in]  userGpuTaskAssignment       The user-specified assignment of GPU tasks to device IDs.
- * \param[in]  emulateGpuNonbonded         Whether we will emulate GPU calculation of nonbonded interactions.
- * \param[in]  buildSupportsNonbondedOnGpu Whether GROMACS was built with GPU support.
- * \param[in]  nonbondedOnGpuIsUseful      Whether computing nonbonded interactions on a GPU is useful for this calculation.
- * \param[in]  numRanksPerSimulation       The number of ranks in each simulation.
+ * \param[in] nonbondedTarget              The user's choice for mdrun -nb for where to assign
+ *                                         short-ranged nonbonded interaction tasks.
+ * \param[in] numDevicesToUse              Number of compatible GPUs that the user permitted
+ *                                         us to use.
+ * \param[in] userGpuTaskAssignment        The user-specified assignment of GPU tasks to device IDs.
+ * \param[in] emulateGpuNonbonded          Whether we will emulate GPU calculation of nonbonded
+ *                                         interactions.
+ * \param[in] buildSupportsNonbondedOnGpu  Whether GROMACS was built with GPU support.
+ * \param[in] nonbondedOnGpuIsUseful       Whether computing nonbonded interactions on a GPU is
+ *                                         useful for this calculation.
+ * \param[in] numRanksPerSimulation        The number of ranks in each simulation.
  *
  * \returns    Whether the simulation will run nonbonded tasks on GPUs.
  *
  * \throws     std::bad_alloc          If out of memory
  *             InconsistentInputError  If the user requirements are inconsistent. */
 bool decideWhetherToUseGpusForNonbondedWithThreadMpi(TaskTarget              nonbondedTarget,
-                                                     const std::vector<int>& gpuIdsToUse,
+                                                     int                     numDevicesToUse,
                                                      const std::vector<int>& userGpuTaskAssignment,
                                                      EmulateGpuNonbonded     emulateGpuNonbonded,
                                                      bool buildSupportsNonbondedOnGpu,
@@ -132,11 +136,10 @@ bool decideWhetherToUseGpusForNonbondedWithThreadMpi(TaskTarget              non
  * \param[in]  useGpuForNonbonded        Whether GPUs will be used for nonbonded interactions.
  * \param[in]  pmeTarget                 The user's choice for mdrun -pme for where to assign
  *                                       long-ranged PME nonbonded interaction tasks.
- * \param[in]  gpuIdsToUse               The compatible GPUs that the user permitted us to use.
+ * \param[in]  numDevicesToUse           The number of compatible GPUs that the user permitted us to use.
  * \param[in]  userGpuTaskAssignment     The user-specified assignment of GPU tasks to device IDs.
  * \param[in]  hardwareInfo              Hardware information
  * \param[in]  inputrec                  The user input
- * \param[in]  mtop                      Global system topology
  * \param[in]  numRanksPerSimulation     The number of ranks in each simulation.
  * \param[in]  numPmeRanksPerSimulation  The number of PME ranks in each simulation.
  *
@@ -146,11 +149,10 @@ bool decideWhetherToUseGpusForNonbondedWithThreadMpi(TaskTarget              non
  *             InconsistentInputError  If the user requirements are inconsistent. */
 bool decideWhetherToUseGpusForPmeWithThreadMpi(bool                    useGpuForNonbonded,
                                                TaskTarget              pmeTarget,
-                                               const std::vector<int>& gpuIdsToUse,
+                                               int                     numDevicesToUse,
                                                const std::vector<int>& userGpuTaskAssignment,
                                                const gmx_hw_info_t&    hardwareInfo,
                                                const t_inputrec&       inputrec,
-                                               const gmx_mtop_t&       mtop,
                                                int                     numRanksPerSimulation,
                                                int                     numPmeRanksPerSimulation);
 
@@ -209,7 +211,6 @@ bool decideWhetherToUseGpusForNonbonded(TaskTarget              nonbondedTarget,
  * \param[in]  userGpuTaskAssignment     The user-specified assignment of GPU tasks to device IDs.
  * \param[in]  hardwareInfo              Hardware information
  * \param[in]  inputrec                  The user input
- * \param[in]  mtop                      Global system topology
  * \param[in]  numRanksPerSimulation     The number of ranks in each simulation.
  * \param[in]  numPmeRanksPerSimulation  The number of PME ranks in each simulation.
  * \param[in]  gpusWereDetected          Whether compatible GPUs were detected on any node.
@@ -223,7 +224,6 @@ bool decideWhetherToUseGpusForPme(bool                    useGpuForNonbonded,
                                   const std::vector<int>& userGpuTaskAssignment,
                                   const gmx_hw_info_t&    hardwareInfo,
                                   const t_inputrec&       inputrec,
-                                  const gmx_mtop_t&       mtop,
                                   int                     numRanksPerSimulation,
                                   int                     numPmeRanksPerSimulation,
                                   bool                    gpusWereDetected);
@@ -305,6 +305,24 @@ bool decideWhetherToUseGpuForUpdate(bool                           isDomainDecom
                                     const gmx::MDLogger&           mdlog);
 
 
+/*! \brief Decide whether to use GPU for halo exchange.
+ *
+ * \param[in]  devFlags                     GPU development / experimental feature flags.
+ * \param[in]  havePPDomainDecomposition    Whether PP domain decomposition is in use.
+ * \param[in]  useGpuForNonbonded           Whether GPUs will be used for nonbonded interactions.
+ * \param[in]  useModularSimulator          Whether modularsimulator is in use.
+ * \param[in]  doRerun                      Whether this is a rerun.
+ * \param[in]  haveEnergyMinimization       Whether energy minimization is in use.
+ *
+ * \returns    Whether halo exchange can be run on GPU.
+ */
+bool decideWhetherToUseGpuForHalo(const DevelopmentFeatureFlags& devFlags,
+                                  bool                           havePPDomainDecomposition,
+                                  bool                           useGpuForNonbonded,
+                                  bool                           useModularSimulator,
+                                  bool                           doRerun,
+                                  bool                           haveEnergyMinimization);
+
 } // namespace gmx
 
 #endif
index 16849d4e5dd611856ca9e537b5b6a7663bcfd79f..5c5cdaeb3f606e453b68bdf9833152407f4d921f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "decidesimulationworkload.h"
 
 #include "gromacs/ewald/pme.h"
+#include "gromacs/mdtypes/multipletimestepping.h"
+#include "gromacs/taskassignment/decidegpuusage.h"
 #include "gromacs/taskassignment/taskassignment.h"
 #include "gromacs/utility/arrayref.h"
 
 namespace gmx
 {
 
-SimulationWorkload createSimulationWorkload(bool       useGpuForNonbonded,
-                                            PmeRunMode pmeRunMode,
-                                            bool       useGpuForBonded,
-                                            bool       useGpuForUpdate,
-                                            bool       useGpuForBufferOps,
-                                            bool       useGpuHaloExchange,
-                                            bool       useGpuPmePpComm,
-                                            bool       haveEwaldSurfaceContribution)
+SimulationWorkload createSimulationWorkload(const t_inputrec& inputrec,
+                                            const bool        disableNonbondedCalculation,
+                                            const DevelopmentFeatureFlags& devFlags,
+                                            bool                           useGpuForNonbonded,
+                                            PmeRunMode                     pmeRunMode,
+                                            bool                           useGpuForBonded,
+                                            bool                           useGpuForUpdate,
+                                            bool                           useGpuDirectHalo)
 {
     SimulationWorkload simulationWorkload;
+    simulationWorkload.computeNonbonded = !disableNonbondedCalculation;
+    simulationWorkload.computeNonbondedAtMtsLevel1 =
+            simulationWorkload.computeNonbonded && inputrec.useMts
+            && inputrec.mtsLevels.back().forceGroups[static_cast<int>(MtsForceGroups::Nonbonded)];
+    simulationWorkload.computeMuTot    = inputrecNeedMutot(&inputrec);
     simulationWorkload.useCpuNonbonded = !useGpuForNonbonded;
     simulationWorkload.useGpuNonbonded = useGpuForNonbonded;
     simulationWorkload.useCpuPme       = (pmeRunMode == PmeRunMode::CPU);
     simulationWorkload.useGpuPme = (pmeRunMode == PmeRunMode::GPU || pmeRunMode == PmeRunMode::Mixed);
-    simulationWorkload.useGpuPmeFft             = (pmeRunMode == PmeRunMode::Mixed);
-    simulationWorkload.useGpuBonded             = useGpuForBonded;
-    simulationWorkload.useGpuUpdate             = useGpuForUpdate;
-    simulationWorkload.useGpuBufferOps          = useGpuForBufferOps || useGpuForUpdate;
-    simulationWorkload.useGpuHaloExchange       = useGpuHaloExchange;
-    simulationWorkload.useGpuPmePpCommunication = useGpuPmePpComm && (pmeRunMode == PmeRunMode::GPU);
-    simulationWorkload.useGpuDirectCommunication    = useGpuHaloExchange || useGpuPmePpComm;
-    simulationWorkload.haveEwaldSurfaceContribution = haveEwaldSurfaceContribution;
+    simulationWorkload.useGpuPmeFft    = (pmeRunMode == PmeRunMode::Mixed);
+    simulationWorkload.useGpuBonded    = useGpuForBonded;
+    simulationWorkload.useGpuUpdate    = useGpuForUpdate;
+    simulationWorkload.useGpuBufferOps = (devFlags.enableGpuBufferOps || useGpuForUpdate)
+                                         && !simulationWorkload.computeNonbondedAtMtsLevel1;
+    simulationWorkload.useGpuHaloExchange = useGpuDirectHalo;
+    simulationWorkload.useGpuPmePpCommunication =
+            devFlags.enableGpuPmePPComm && (pmeRunMode == PmeRunMode::GPU);
+    simulationWorkload.useGpuDirectCommunication =
+            devFlags.enableGpuHaloExchange || devFlags.enableGpuPmePPComm;
+    simulationWorkload.haveEwaldSurfaceContribution = haveEwaldSurfaceContribution(inputrec);
 
     return simulationWorkload;
 }
index 538fa823ed2bc4fbc1b9e248dd897416b450e9e6..c18f9a04d184b852fa84c579855e227aa111fd05 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,30 +52,32 @@ enum class PmeRunMode;
 namespace gmx
 {
 
+struct DevelopmentFeatureFlags;
+
 /*! \brief
  * Build datastructure that contains decisions whether to run different workload
  * task on GPUs.
  *
+ * \param[in] inputrec           The input record
+ * \param[in] disableNonbondedCalculation  Disable calculation of nonbonded forces
+ * \param[in] devFlags           The development feature flags
  * \param[in] useGpuForNonbonded Whether we have short-range nonbonded interactions
  *                               calculations on GPU(s).
  * \param[in] pmeRunMode         Run mode indicating what resource is PME execured on.
  * \param[in] useGpuForBonded    Whether bonded interactions are calculated on GPU(s).
  * \param[in] useGpuForUpdate    Whether coordinate update and constraint solving is performed on
  *                               GPU(s).
- * \param[in] useGpuForBufferOps Whether buffer ops / reduction are calculated on GPU(s).
- * \param[in] useGpuHaloExchange Whether GPU direct communication is used in halo exchange.
- * \param[in] useGpuPmePpComm    Whether GPU direct communication is used in PME-PP communication.
- * \param[in] haveEwaldSurfaceContribution Whether there is an Ewald surface contribution
+ * \param[in] useGpuDirectHalo   Whether halo exchange is performed directly between GPUs.
  * \returns Simulation lifetime constant workload description.
  */
-SimulationWorkload createSimulationWorkload(bool       useGpuForNonbonded,
-                                            PmeRunMode pmeRunMode,
-                                            bool       useGpuForBonded,
-                                            bool       useGpuForUpdate,
-                                            bool       useGpuForBufferOps,
-                                            bool       useGpuHaloExchange,
-                                            bool       useGpuPmePpComm,
-                                            bool       haveEwaldSurfaceContribution);
+SimulationWorkload createSimulationWorkload(const t_inputrec& inputrec,
+                                            bool              disableNonbondedCalculation,
+                                            const DevelopmentFeatureFlags& devFlags,
+                                            bool                           useGpuForNonbonded,
+                                            PmeRunMode                     pmeRunMode,
+                                            bool                           useGpuForBonded,
+                                            bool                           useGpuForUpdate,
+                                            bool                           useGpuDirectHalo);
 
 } // namespace gmx
 
index 5d0070d5615e5df9fd74f8c23e8239bef2addc72..a967ed1c1b5d265154194c33953b58cb4e473ce2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,6 +50,7 @@
 #include <cstring>
 
 #include <algorithm>
+#include <array>
 
 #include "gromacs/ewald/pme.h"
 #include "gromacs/hardware/cpuinfo.h"
@@ -338,15 +339,15 @@ private:
  * Thus all options should be internally consistent and consistent
  * with the hardware, except that ntmpi could be larger than #GPU.
  */
-int get_nthreads_mpi(const gmx_hw_info_t*    hwinfo,
-                     gmx_hw_opt_t*           hw_opt,
-                     const std::vector<int>& gpuIdsToUse,
-                     bool                    nonbondedOnGpu,
-                     bool                    pmeOnGpu,
-                     const t_inputrec*       inputrec,
-                     const gmx_mtop_t*       mtop,
-                     const gmx::MDLogger&    mdlog,
-                     bool                    doMembed)
+int get_nthreads_mpi(const gmx_hw_info_t* hwinfo,
+                     gmx_hw_opt_t*        hw_opt,
+                     const int            numDevicesToUse,
+                     bool                 nonbondedOnGpu,
+                     bool                 pmeOnGpu,
+                     const t_inputrec*    inputrec,
+                     const gmx_mtop_t*    mtop,
+                     const gmx::MDLogger& mdlog,
+                     bool                 doMembed)
 {
     int nthreads_hw, nthreads_tot_max, nrank, ngpu;
     int min_atoms_per_mpi_rank;
@@ -359,7 +360,7 @@ int get_nthreads_mpi(const gmx_hw_info_t*    hwinfo,
         GMX_RELEASE_ASSERT((EEL_PME(inputrec->coulombtype) || EVDW_PME(inputrec->vdwtype))
                                    && pme_gpu_supports_build(nullptr)
                                    && pme_gpu_supports_hardware(*hwinfo, nullptr)
-                                   && pme_gpu_supports_input(*inputrec, *mtop, nullptr),
+                                   && pme_gpu_supports_input(*inputrec, nullptr),
                            "PME can't be on GPUs unless we are using PME");
 
         // PME on GPUs supports a single PME rank with PP running on the same or few other ranks.
@@ -431,7 +432,7 @@ int get_nthreads_mpi(const gmx_hw_info_t*    hwinfo,
 
     /* nonbondedOnGpu might be false e.g. because this simulation
      * is a rerun with energy groups. */
-    ngpu = (nonbondedOnGpu ? gmx::ssize(gpuIdsToUse) : 0);
+    ngpu = (nonbondedOnGpu ? numDevicesToUse : 0);
 
     nrank = get_tmpi_omp_thread_division(hwinfo, *hw_opt, nthreads_tot_max, ngpu);
 
@@ -550,7 +551,7 @@ void check_resource_division_efficiency(const gmx_hw_info_t* hwinfo,
 #if GMX_OPENMP && GMX_MPI
     GMX_UNUSED_VALUE(hwinfo);
 
-    int         nth_omp_min, nth_omp_max;
+    int         nth_omp_max;
     char        buf[1000];
     const char* mpi_option = GMX_THREAD_MPI ? " (option -ntmpi)" : "";
 
@@ -565,25 +566,22 @@ void check_resource_division_efficiency(const gmx_hw_info_t* hwinfo,
     GMX_RELEASE_ASSERT(gmx_omp_nthreads_get(emntDefault) >= 1,
                        "Must have at least one OpenMP thread");
 
-    nth_omp_min = gmx_omp_nthreads_get(emntDefault);
     nth_omp_max = gmx_omp_nthreads_get(emntDefault);
 
     bool anyRankIsUsingGpus = willUsePhysicalGpu;
     /* Thread-MPI seems to have a bug with reduce on 1 node, so use a cond. */
     if (cr->nnodes > 1)
     {
-        int count[3], count_max[3];
+        std::array<int, 2> count, count_max;
 
-        count[0] = -nth_omp_min;
-        count[1] = nth_omp_max;
-        count[2] = int(willUsePhysicalGpu);
+        count[0] = nth_omp_max;
+        count[1] = int(willUsePhysicalGpu);
 
-        MPI_Allreduce(count, count_max, 3, MPI_INT, MPI_MAX, cr->mpi_comm_mysim);
+        MPI_Allreduce(count.data(), count_max.data(), count.size(), MPI_INT, MPI_MAX, cr->mpi_comm_mysim);
 
         /* In case of an inhomogeneous run setup we use the maximum counts */
-        nth_omp_min        = -count_max[0];
-        nth_omp_max        = count_max[1];
-        anyRankIsUsingGpus = count_max[2] > 0;
+        nth_omp_max        = count_max[0];
+        anyRankIsUsingGpus = count_max[1] > 0;
     }
 
     int nthreads_omp_mpi_ok_min;
@@ -911,7 +909,7 @@ void checkAndUpdateRequestedNumOpenmpThreads(gmx_hw_opt_t*         hw_opt,
          * all detected ncore_tot physical cores. We are currently not
          * checking for that here.
          */
-        int numRanksTot     = cr->nnodes * (isMultiSim(ms) ? ms->nsim : 1);
+        int numRanksTot     = cr->nnodes * (isMultiSim(ms) ? ms->numSimulations_ : 1);
         int numAtomsPerRank = mtop.natoms / cr->nnodes;
         int numCoresPerRank = hwinfo.ncore_tot / numRanksTot;
         if (numAtomsPerRank < c_numAtomsPerCoreSquaredSmtThreshold * gmx::square(numCoresPerRank))
index a2185babf4a2aa2bd83d2dde99133576890e9282..c45ed954939848314a45f63525b9bc8043ae7e67 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -73,15 +73,15 @@ class PhysicalNodeCommunicator;
  * with the hardware, except that ntmpi could be larger than number of GPUs.
  * If necessary, this function will modify hw_opt->nthreads_omp.
  */
-int get_nthreads_mpi(const gmx_hw_info_t*    hwinfo,
-                     gmx_hw_opt_t*           hw_opt,
-                     const std::vector<int>& gpuIdsToUse,
-                     bool                    nonbondedOnGpu,
-                     bool                    pmeOnGpu,
-                     const t_inputrec*       inputrec,
-                     const gmx_mtop_t*       mtop,
-                     const gmx::MDLogger&    mdlog,
-                     bool                    doMembed);
+int get_nthreads_mpi(const gmx_hw_info_t* hwinfo,
+                     gmx_hw_opt_t*        hw_opt,
+                     int                  numDevicesToUse,
+                     bool                 nonbondedOnGpu,
+                     bool                 pmeOnGpu,
+                     const t_inputrec*    inputrec,
+                     const gmx_mtop_t*    mtop,
+                     const gmx::MDLogger& mdlog,
+                     bool                 doMembed);
 
 /*! \brief Check if the number of OpenMP threads is within reasonable range
  * considering the hardware used. This is a crude check, but mainly
index 6df5a4614e651a5e03df7fc159d140446078d222..2a823e57281a1f80357c7d9470d65db8af8caf31 100644 (file)
@@ -60,7 +60,7 @@
 
 #include "gromacs/domdec/domdec.h"
 #include "gromacs/gmxlib/network.h"
-#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/mdrunutility/multisim.h"
 #include "gromacs/mdtypes/commrec.h"
@@ -330,7 +330,7 @@ GpuTaskAssignments GpuTaskAssignmentsBuilder::build(const std::vector<int>& gpuI
                         userGpuTaskAssignment.size(), host, numGpuTasksOnThisNode)));
             }
             // Did the user choose compatible GPUs?
-            checkUserGpuIds(hardwareInfo.gpu_info, gpuIdsToUse, userGpuTaskAssignment);
+            checkUserGpuIds(hardwareInfo.deviceInfoList, gpuIdsToUse, userGpuTaskAssignment);
 
             gpuIdsForTaskAssignment = userGpuTaskAssignment;
         }
@@ -400,44 +400,41 @@ void GpuTaskAssignments::reportGpuUsage(const MDLogger& mdlog,
                         numRanksOnThisNode_, printHostName, useGpuForBonded, pmeRunMode, useGpuForUpdate);
 }
 
-gmx_device_info_t* GpuTaskAssignments::initNonbondedDevice(const t_commrec* cr) const
+/*! \brief Function for whether the task of \c mapping has value \c TaskType.
+ *
+ * \param[in] mapping  Current GPU task mapping.
+ * \returns If \c TaskType task was assigned to the \c mapping.
+ */
+template<GpuTask TaskType>
+static bool hasTaskType(const GpuTaskMapping& mapping)
 {
-    gmx_device_info_t*       deviceInfo        = nullptr;
-    const GpuTaskAssignment& gpuTaskAssignment = assignmentForAllRanksOnThisNode_[indexOfThisRank_];
-
-    // This works because only one task of each type per rank is currently permitted.
-    auto nbGpuTaskMapping = std::find_if(gpuTaskAssignment.begin(), gpuTaskAssignment.end(),
-                                         hasTaskType<GpuTask::Nonbonded>);
-    if (nbGpuTaskMapping != gpuTaskAssignment.end())
-    {
-        int deviceId = nbGpuTaskMapping->deviceId_;
-        deviceInfo   = getDeviceInfo(hardwareInfo_.gpu_info, deviceId);
-        init_gpu(deviceInfo);
+    return mapping.task_ == TaskType;
+}
 
-        // TODO Setting up this sharing should probably part of
-        // init_domain_decomposition after further refactoring.
-        if (DOMAINDECOMP(cr))
-        {
-            /* When we share GPUs over ranks, we need to know this for the DLB */
-            dd_setup_dlb_resource_sharing(cr, deviceId);
-        }
-    }
-    return deviceInfo;
+/*! \brief Function for whether the \c mapping has the GPU PME or Nonbonded task.
+ *
+ * \param[in] mapping  Current GPU task mapping.
+ * \returns If PME on Nonbonded GPU task was assigned to this mapping.
+ */
+static bool hasPmeOrNonbondedTask(const GpuTaskMapping& mapping)
+{
+    return hasTaskType<GpuTask::Pme>(mapping) || hasTaskType<GpuTask::Nonbonded>(mapping);
 }
 
-gmx_device_info_t* GpuTaskAssignments::initPmeDevice() const
+DeviceInformation* GpuTaskAssignments::initDevice(int* deviceId) const
 {
-    gmx_device_info_t*       deviceInfo        = nullptr;
+    DeviceInformation*       deviceInfo        = nullptr;
     const GpuTaskAssignment& gpuTaskAssignment = assignmentForAllRanksOnThisNode_[indexOfThisRank_];
 
-    // This works because only one task of each type is currently permitted.
-    auto       pmeGpuTaskMapping = std::find_if(gpuTaskAssignment.begin(), gpuTaskAssignment.end(),
-                                          hasTaskType<GpuTask::Pme>);
-    const bool thisRankHasPmeGpuTask = (pmeGpuTaskMapping != gpuTaskAssignment.end());
-    if (thisRankHasPmeGpuTask)
+    // This works because only one task of each type per rank is currently permitted.
+    auto gpuTaskMapping =
+            std::find_if(gpuTaskAssignment.begin(), gpuTaskAssignment.end(), hasPmeOrNonbondedTask);
+
+    if (gpuTaskMapping != gpuTaskAssignment.end())
     {
-        deviceInfo = getDeviceInfo(hardwareInfo_.gpu_info, pmeGpuTaskMapping->deviceId_);
-        init_gpu(deviceInfo);
+        *deviceId  = gpuTaskMapping->deviceId_;
+        deviceInfo = hardwareInfo_.deviceInfoList[*deviceId].get();
+        setActiveDevice(*deviceInfo);
     }
     return deviceInfo;
 }
index 6ef380385d3906d0b873da3b32454d7454c60106..f709f24e4067e0a6813bc36bb9266ec03a297f0d 100644 (file)
@@ -55,7 +55,7 @@
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/gmxmpi.h"
 
-struct gmx_device_info_t;
+struct DeviceInformation;
 struct gmx_hw_info_t;
 struct t_commrec;
 
@@ -159,19 +159,19 @@ public:
      * \throws   std::bad_alloc          If out of memory.
      *           InconsistentInputError  If user and/or detected inputs are inconsistent.
      */
-    GpuTaskAssignments build(const std::vector<int>&         gpuIdsToUse,
-                             const std::vector<int>&         userGpuTaskAssignment,
-                             const gmx_hw_info_t&            hardwareInfo,
-                             MPI_Comm                        gromacsWorldComm,
-                             const PhysicalNodeCommunicator& physicalNodeComm,
-                             TaskTarget                      nonbondedTarget,
-                             TaskTarget                      pmeTarget,
-                             TaskTarget                      bondedTarget,
-                             TaskTarget                      updateTarget,
-                             bool                            useGpuForNonbonded,
-                             bool                            useGpuForPme,
-                             bool                            rankHasPpTask,
-                             bool                            rankHasPmeTask);
+    static GpuTaskAssignments build(const std::vector<int>&         gpuIdsToUse,
+                                    const std::vector<int>&         userGpuTaskAssignment,
+                                    const gmx_hw_info_t&            hardwareInfo,
+                                    MPI_Comm                        gromacsWorldComm,
+                                    const PhysicalNodeCommunicator& physicalNodeComm,
+                                    TaskTarget                      nonbondedTarget,
+                                    TaskTarget                      pmeTarget,
+                                    TaskTarget                      bondedTarget,
+                                    TaskTarget                      updateTarget,
+                                    bool                            useGpuForNonbonded,
+                                    bool                            useGpuForPme,
+                                    bool                            rankHasPpTask,
+                                    bool                            rankHasPmeTask);
 };
 
 /*! \libinternal
@@ -239,33 +239,20 @@ public:
      * \param[in]  numCompatibleGpusOnThisNode  The number of compatible GPUs on this node.
      * */
     void logPerformanceHints(const MDLogger& mdlog, size_t numCompatibleGpusOnThisNode);
-    /*! \brief Return handle to the initialized GPU to use for the
-     * nonbonded task on this rank, if any.
+    /*! \brief Return handle to the initialized GPU to use in the this rank.
      *
-     * Returns nullptr if no such task is assigned to this rank.
+     * \param[out] deviceId Index of the assigned device.
      *
-     * \todo This also sets up DLB for device sharing, where
-     * appropriate, but that responsbility should move
-     * elsewhere. */
-    gmx_device_info_t* initNonbondedDevice(const t_commrec* cr) const;
-    /*! \brief Return handle to the initialized GPU to use for the
-     * PME task on this rank, if any.
-     *
-     * Returns nullptr if no such task is assigned to this rank. */
-    gmx_device_info_t* initPmeDevice() const;
+     * \returns Device information on the selected devicce. Returns nullptr if no GPU task
+     *          is assigned to this rank.
+     */
+    DeviceInformation* initDevice(int* deviceId) const;
     //! Return whether this rank has a PME task running on a GPU
     bool thisRankHasPmeGpuTask() const;
     //! Return whether this rank has any task running on a GPU
     bool thisRankHasAnyGpuTask() const;
 };
 
-//! Function for whether the task of \c mapping has value \c TaskType.
-template<GpuTask TaskType>
-bool hasTaskType(const GpuTaskMapping& mapping)
-{
-    return mapping.task_ == TaskType;
-}
-
 } // namespace gmx
 
 #endif
index 1525d1a761b6f819cfbd29f43a238703c1257b33..13710c2e176ec96050c72d2e140386a6f390cea9 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2017, by the GROMACS development team, led by
+# Copyright (c) 2017,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,5 +33,6 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(TaskAssignmentUnitTests taskassignment-test
-                  usergpuids.cpp
-                  )
+    CPP_SOURCE_FILES
+        usergpuids.cpp
+        )
index a370e506f749a8b99abecbe29efcd9cb7241dcea..8271d00913d467ad258f6694760049954a78f4e4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,7 +49,7 @@
 #include <string>
 #include <vector>
 
-#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/stringutil.h"
@@ -136,31 +136,23 @@ std::vector<int> parseUserGpuIdString(const std::string& gpuIdString)
     return digits;
 }
 
-std::vector<int> makeGpuIdsToUse(const gmx_gpu_info_t& gpuInfo, const std::string& gpuIdsAvailableString)
+std::vector<int> makeGpuIdsToUse(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                                 const std::string& gpuIdsAvailableString)
 {
-    auto             compatibleGpus  = getCompatibleGpus(gpuInfo);
     std::vector<int> gpuIdsAvailable = parseUserGpuIdString(gpuIdsAvailableString);
 
     if (gpuIdsAvailable.empty())
     {
-        return compatibleGpus;
+        // The user didn't restrict the choice, so we use all compatible GPUs
+        return getCompatibleDeviceIds(deviceInfoList);
     }
 
     std::vector<int> gpuIdsToUse;
     gpuIdsToUse.reserve(gpuIdsAvailable.size());
     std::vector<int> availableGpuIdsThatAreIncompatible;
-    for (const auto& availableGpuId : gpuIdsAvailable)
+    for (const int& availableGpuId : gpuIdsAvailable)
     {
-        bool availableGpuIsCompatible = false;
-        for (const auto& compatibleGpuId : compatibleGpus)
-        {
-            if (availableGpuId == compatibleGpuId)
-            {
-                availableGpuIsCompatible = true;
-                break;
-            }
-        }
-        if (availableGpuIsCompatible)
+        if (deviceIdIsCompatible(deviceInfoList, availableGpuId))
         {
             gpuIdsToUse.push_back(availableGpuId);
         }
@@ -217,9 +209,9 @@ std::string makeGpuIdString(const std::vector<int>& gpuIds, int totalNumberOfTas
     return formatAndJoin(resultGpuIds, ",", StringFormatter("%d"));
 }
 
-void checkUserGpuIds(const gmx_gpu_info_t&   gpu_info,
-                     const std::vector<int>& compatibleGpus,
-                     const std::vector<int>& gpuIds)
+void checkUserGpuIds(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                     const std::vector<int>&                                compatibleGpus,
+                     const std::vector<int>&                                gpuIds)
 {
     bool        foundIncompatibleGpuIds = false;
     std::string message =
@@ -231,7 +223,7 @@ void checkUserGpuIds(const gmx_gpu_info_t&   gpu_info,
         {
             foundIncompatibleGpuIds = true;
             message += gmx::formatString("    GPU #%d: %s\n", gpuId,
-                                         getGpuCompatibilityDescription(gpu_info, gpuId));
+                                         getDeviceCompatibilityDescription(deviceInfoList, gpuId).c_str());
         }
     }
     if (foundIncompatibleGpuIds)
index 9d9bef4967a4296778554148b382f81cb118835e..e85d15bf18d6d446956ffaff249af399b6e00016 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <cstddef>
 
+#include <memory>
 #include <string>
 #include <vector>
 
 #include "gromacs/utility/arrayref.h"
 
-struct gmx_gpu_info_t;
+struct DeviceInformation;
 
 namespace gmx
 {
@@ -83,7 +84,7 @@ std::vector<int> parseUserGpuIdString(const std::string& gpuIdString);
  * all compatible GPUs on this physical node. Otherwise, check the
  * user specified compatible GPUs and return their IDs.
  *
- * \param[in]  gpuInfo                Information detected about GPUs on this physical node
+ * \param[in]  deviceInfoList         Information on the GPUs on this physical node.
  * \param[in]  gpuIdsAvailableString  String like "013" or "0,1,3" typically
  *                                    supplied by the user to mdrun -gpu_id.
  *                                    Must contain only unique decimal digits, or only decimal
@@ -99,7 +100,8 @@ std::vector<int> parseUserGpuIdString(const std::string& gpuIdString);
  *           InvalidInputError  If gpuIdsAvailableString specifies GPU IDs that are
  *                              not compatible.
  */
-std::vector<int> makeGpuIdsToUse(const gmx_gpu_info_t& gpuInfo, const std::string& gpuIdsAvailableString);
+std::vector<int> makeGpuIdsToUse(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                                 const std::string& gpuIdsAvailableString);
 
 /*! \brief Parse a GPU ID specifier string into a container describing device ID to task mapping.
  *
@@ -163,16 +165,16 @@ std::string makeGpuIdString(const std::vector<int>& gpuIds, int totalNumberOfTas
  * infrastructure to do a good job of coordinating error messages and
  * behaviour across MPMD ranks and multiple simulations.
  *
- * \param[in]   gpu_info        Information detected about GPUs
+ * \param[in]   deviceInfoList  Information on the GPUs on this physical node.
  * \param[in]   compatibleGpus  Vector of GPUs that are compatible
  * \param[in]   gpuIds          The GPU IDs selected by the user.
  *
  * \throws  std::bad_alloc          If out of memory
  *          InconsistentInputError  If the assigned GPUs are not valid
  */
-void checkUserGpuIds(const gmx_gpu_info_t&   gpu_info,
-                     const std::vector<int>& compatibleGpus,
-                     const std::vector<int>& gpuIds);
+void checkUserGpuIds(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                     const std::vector<int>&                                compatibleGpus,
+                     const std::vector<int>&                                gpuIds);
 
 } // namespace gmx
 
index b4557e90765e5e146156a14e123775e41cf49d01..6c1a0d43669e1265d9d51efcfc250567fc54df9b 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 1991-2006 David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7be59377af786604e39edc8bbe56256d1e44d90a..956386a0762cf1bceee73da570d77c26fe4eefee 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 1991-2006 David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,8 +47,8 @@
 #define GMX_TIMING_CYCLECOUNTER_H
 
 /*
- * Define HAVE_RDTSCP=1 to use the serializing rdtscp instruction instead of rdtsc.
- * This is only supported on newer Intel/AMD hardware, but provides better accuracy.
+ * Define GMX_USE_RDTSCP=1 to use the serializing rdtscp instruction instead of rdtsc.
+ * This is supported on essentially all Intel/AMD hardware still in use, and provides better accuracy.
  */
 #include "config.h"
 
@@ -71,17 +72,17 @@ typedef unsigned long long     gmx_cycles_t;
 
 #elif defined(_MSC_VER)
 #    include <windows.h>
-typedef __int64                gmx_cycles_t;
+typedef __int64              gmx_cycles_t;
 
 #elif (defined(__hpux) || defined(__HP_cc)) && defined(__ia64)
 /* HP compiler on ia64 */
 #    include <machine/sys/inline.h>
-typedef unsigned long        gmx_cycles_t;
+typedef unsigned long      gmx_cycles_t;
 
 #elif (defined(__INTEL_COMPILER) || defined(__ECC)) && defined(__ia64__)
 /* Intel compiler on ia64 */
 #    include <ia64intrin.h>
-typedef unsigned long      gmx_cycles_t;
+typedef unsigned long          gmx_cycles_t;
 
 #elif defined(__GNUC__) && defined(__ia64__)
 /* ia64 with GCC inline assembly */
@@ -94,11 +95,11 @@ typedef unsigned long          gmx_cycles_t;
 #elif ((defined(__hppa__) || defined(__hppa)) && defined(__hpux))
 /* HP PA-RISC, instruction when using HP compiler */
 #    include <machine/inline.h>
-typedef unsigned long          gmx_cycles_t;
+typedef unsigned long      gmx_cycles_t;
 
 #elif defined(__GNUC__) && defined(__s390__)
 /* S390, taken from FFTW who got it from James Treacy */
-typedef unsigned long long gmx_cycles_t;
+typedef unsigned long long     gmx_cycles_t;
 
 #elif defined(__GNUC__) && defined(__alpha__)
 /* gcc inline assembly on alpha CPUs */
@@ -111,7 +112,7 @@ typedef unsigned long          gmx_cycles_t;
 #elif defined(__DECC) && defined(__alpha)
 /* Digital GEM C compiler on alpha */
 #    include <c_asm.h>
-typedef unsigned long          gmx_cycles_t;
+typedef unsigned long        gmx_cycles_t;
 
 #elif (defined(__sgi) && defined(CLOCK_SGI_CYCLE))
 /* Irix compilers on SGI hardware. Get nanoseconds from struct timespec */
@@ -119,7 +120,7 @@ typedef unsigned long long   gmx_cycles_t;
 
 #elif (defined(__SVR4) && defined(__SUNPRO_CC))
 /* Solaris high-resolution timers */
-typedef hrtime_t             gmx_cycles_t;
+typedef hrtime_t           gmx_cycles_t;
 
 #elif defined(__xlC__) && defined(_AIX)
 /* AIX compilers */
@@ -130,7 +131,7 @@ typedef unsigned long long gmx_cycles_t;
 #elif ((defined(__GNUC__) || defined(__IBM_GCC_ASM) || defined(__IBM_STDCPP_ASM)) \
        && (defined(__powerpc__) || defined(__ppc__)))
 /* PowerPC using gcc inline assembly (also works on xlc>=7.0 with -qasm=gcc) */
-typedef unsigned long long gmx_cycles_t;
+typedef unsigned long long     gmx_cycles_t;
 
 #elif (defined(__MWERKS__) && (defined(MAC) || defined(macintosh)))
 /* Metrowerks on macintosh */
@@ -138,7 +139,7 @@ typedef unsigned long long     gmx_cycles_t;
 
 #elif defined(__sun) && defined(__sparcv9)
 
-typedef unsigned long          gmx_cycles_t;
+typedef unsigned long gmx_cycles_t;
 
 #else
 /*! \brief Integer-like datatype for cycle counter values
@@ -173,19 +174,14 @@ typedef long gmx_cycles_t;
  *  the difference between two gmx_cycles_t values returned from this
  *  routine.
  */
-#if (GMX_CYCLECOUNTERS == 0)
-static __inline__ gmx_cycles_t gmx_cycles_read(void)
-{
-    return 0;
-}
-#elif ((defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__) || defined(__PGIC__)) \
-       && (defined(__i386__) || defined(__x86_64__)) && !defined(_CRAYC))
+#if ((defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__) || defined(__PGIC__)) \
+     && (defined(__i386__) || defined(__x86_64__)) && !defined(_CRAYC))
 static __inline__ gmx_cycles_t gmx_cycles_read()
 {
     /* x86 with GCC inline assembly - pentium TSC register */
     unsigned low, high;
 
-#    if HAVE_RDTSCP
+#    if GMX_USE_RDTSCP
     __asm__ __volatile__("rdtscp" : "=a"(low), "=d"(high)::"ecx");
 #    else
     __asm__ __volatile__("rdtsc" : "=a"(low), "=d"(high));
@@ -219,7 +215,7 @@ static __inline gmx_cycles_t gmx_cycles_read(void)
     return __rdpmccntr64();
 #    else
     /* x86 */
-#        if HAVE_RDTSCP
+#        if GMX_USE_RDTSCP
     unsigned int ui;
     return __rdtscp(&ui);
 #        else
@@ -407,14 +403,9 @@ static gmx_cycles_t gmx_cycles_read(void)
  *       one when later linking to the library it might happen that the
  *       library supports cyclecounters but not the headers, or vice versa.
  */
-#if (GMX_CYCLECOUNTERS == 0)
-static __inline__ bool gmx_cycles_have_counter(void)
-{
-    return 0;
-}
-#elif ((defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__) \
-        || defined(__PGIC__) || defined(_CRAYC))                                 \
-       && (defined(__i386__) || defined(__x86_64__)))
+#if ((defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__) \
+      || defined(__PGIC__) || defined(_CRAYC))                                 \
+     && (defined(__i386__) || defined(__x86_64__)))
 static __inline__ bool gmx_cycles_have_counter()
 {
     /* x86 or x86-64 with GCC inline assembly - pentium TSC register */
index 82d444ea7e00decd7bd858e01df8b719a8dd0d78..bcefcac5ba39d7a4106fa1f7e96aa2848a5d43cb 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 638b8dc475e7da59a742f77479e202089109fdb0..45714a1da2cca611d7ea0709687ed15dae5aff0b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -174,6 +175,10 @@ static const char* wcsn[ewcsNR] = {
     "NB X buffer ops.",
     "NB F buffer ops.",
     "Clear force buffer",
+    "Launch GPU NB X buffer ops.",
+    "Launch GPU NB F buffer ops.",
+    "Launch GPU Comm. coord.",
+    "Launch GPU Comm. force.",
     "Test subcounter",
 };
 
@@ -546,9 +551,9 @@ WallcycleCounts wallcycle_sum(const t_commrec* cr, gmx_wallcycle_t wc)
 {
     WallcycleCounts cycles_sum;
     wallcc_t*       wcc;
-    double          cycles[ewcNR + ewcsNR];
+    double          cycles[int(ewcNR) + int(ewcsNR)];
 #if GMX_MPI
-    double cycles_n[ewcNR + ewcsNR + 1];
+    double cycles_n[int(ewcNR) + int(ewcsNR) + 1];
 #endif
     int i;
     int nsum;
@@ -610,7 +615,7 @@ WallcycleCounts wallcycle_sum(const t_commrec* cr, gmx_wallcycle_t wc)
 #if GMX_MPI
     if (cr->nnodes > 1)
     {
-        double buf[ewcNR + ewcsNR + 1];
+        double buf[int(ewcNR) + int(ewcsNR) + 1];
 
         // TODO this code is used only at the end of the run, so we
         // can just do a simple reduce of haveInvalidCount in
index c2d5bc8bf51798a5dc8514c85377b5d12ac8cbd9..83be456671553574acb6d265310769794f342278 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -126,6 +127,10 @@ enum
     ewcsNB_X_BUF_OPS,
     ewcsNB_F_BUF_OPS,
     ewcsCLEAR_FORCE_BUFFER,
+    ewcsLAUNCH_GPU_NB_X_BUF_OPS,
+    ewcsLAUNCH_GPU_NB_F_BUF_OPS,
+    ewcsLAUNCH_GPU_MOVEX,
+    ewcsLAUNCH_GPU_MOVEF,
     ewcsTEST,
     ewcsNR
 };
index 270605772c52ecab644c1f66ac182ad658807bf8..1bf3096fea4871a5e8d23d8565f3373123be4557 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -57,7 +58,7 @@ typedef struct gmx_wallcycle* gmx_wallcycle_t;
 struct gmx_wallclock_gpu_nbnxn_t;
 struct gmx_wallclock_gpu_pme_t;
 
-typedef std::array<double, ewcNR + ewcsNR> WallcycleCounts;
+typedef std::array<double, int(ewcNR) + int(ewcsNR)> WallcycleCounts;
 /* Convenience typedef */
 
 WallcycleCounts wallcycle_sum(const t_commrec* cr, gmx_wallcycle_t wc);
index 16b056826a0ea47e480ca271ab6f1025c25e434e..6b8c1095f4d7711c5ff9c15eaf090e94171a8e4c 100644 (file)
@@ -2,7 +2,8 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2013, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e34ea7a553b81c8ef3fb3990c029eb93a3a46f1a..5aa81509a78d80ed329d127eb340d7fb0b3366d7 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2013, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -129,6 +130,8 @@ static void comp_tpx(const char* fn1, const char* fn2, gmx_bool bRMSD, real ftol
             compareMtopAB(stdout, mtop[0], ftol, abstol);
         }
     }
+    delete ir[0];
+    delete ir[1];
 }
 
 static void comp_trx(const gmx_output_env_t* oenv, const char* fn1, const char* fn2, gmx_bool bRMSD, real ftol, real abstol)
@@ -232,19 +235,21 @@ static void chk_forces(int frame, int natoms, rvec* f)
     }
 }
 
-static void chk_bonds(t_idef* idef, int ePBC, rvec* x, matrix box, real tol)
+static void chk_bonds(const InteractionDefinitions* idef, PbcType pbcType, rvec* x, matrix box, real tol)
 {
     int   ftype, k, ai, aj, type;
     real  b0, blen, deviation;
     t_pbc pbc;
     rvec  dx;
 
-    set_pbc(&pbc, ePBC, box);
+    gmx::ArrayRef<const t_iparams> iparams = idef->iparams;
+
+    set_pbc(&pbc, pbcType, box);
     for (ftype = 0; (ftype < F_NRE); ftype++)
     {
         if ((interaction_function[ftype].flags & IF_CHEMBOND) == IF_CHEMBOND)
         {
-            for (k = 0; (k < idef->il[ftype].nr);)
+            for (k = 0; (k < idef->il[ftype].size());)
             {
                 type = idef->il[ftype].iatoms[k++];
                 ai   = idef->il[ftype].iatoms[k++];
@@ -252,11 +257,11 @@ static void chk_bonds(t_idef* idef, int ePBC, rvec* x, matrix box, real tol)
                 b0   = 0;
                 switch (ftype)
                 {
-                    case F_BONDS: b0 = idef->iparams[type].harmonic.rA; break;
-                    case F_G96BONDS: b0 = std::sqrt(idef->iparams[type].harmonic.rA); break;
-                    case F_MORSE: b0 = idef->iparams[type].morse.b0A; break;
-                    case F_CUBICBONDS: b0 = idef->iparams[type].cubic.b0; break;
-                    case F_CONSTR: b0 = idef->iparams[type].constr.dA; break;
+                    case F_BONDS: b0 = iparams[type].harmonic.rA; break;
+                    case F_G96BONDS: b0 = std::sqrt(iparams[type].harmonic.rA); break;
+                    case F_MORSE: b0 = iparams[type].morse.b0A; break;
+                    case F_CUBICBONDS: b0 = iparams[type].cubic.b0; break;
+                    case F_CONSTR: b0 = iparams[type].constr.dA; break;
                     default: break;
                 }
                 if (b0 != 0)
@@ -277,22 +282,23 @@ static void chk_bonds(t_idef* idef, int ePBC, rvec* x, matrix box, real tol)
 
 static void chk_trj(const gmx_output_env_t* oenv, const char* fn, const char* tpr, real tol)
 {
-    t_trxframe     fr;
-    t_count        count;
-    t_fr_time      first, last;
-    int            j = -1, new_natoms, natoms;
-    real           old_t1, old_t2;
-    gmx_bool       bShowTimestep = TRUE, newline = FALSE;
-    t_trxstatus*   status;
-    gmx_mtop_t     mtop;
-    gmx_localtop_t top;
-    t_state        state;
-    t_inputrec     ir;
-
+    t_trxframe   fr;
+    t_count      count;
+    t_fr_time    first, last;
+    int          j = -1, new_natoms, natoms;
+    real         old_t1, old_t2;
+    gmx_bool     bShowTimestep = TRUE, newline = FALSE;
+    t_trxstatus* status;
+    gmx_mtop_t   mtop;
+    t_state      state;
+    t_inputrec   ir;
+
+    std::unique_ptr<gmx_localtop_t> top;
     if (tpr)
     {
         read_tpx_state(tpr, &ir, &state, &mtop);
-        gmx_mtop_generate_local_top(mtop, &top, ir.efep != efepNO);
+        top = std::make_unique<gmx_localtop_t>(mtop.ffparams);
+        gmx_mtop_generate_local_top(mtop, top.get(), ir.efep != efepNO);
     }
     new_natoms = -1;
     natoms     = -1;
@@ -358,7 +364,7 @@ static void chk_trj(const gmx_output_env_t* oenv, const char* fn, const char* tp
         natoms = new_natoms;
         if (tpr)
         {
-            chk_bonds(&top.idef, ir.ePBC, fr.x, fr.box, tol);
+            chk_bonds(&top->idef, ir.pbcType, fr.x, fr.box, tol);
         }
         if (fr.bX)
         {
@@ -428,7 +434,7 @@ static void chk_tps(const char* fn, real vdw_fac, real bon_lo, real bon_hi)
 {
     int        natom, i, j, k;
     t_topology top;
-    int        ePBC;
+    PbcType    pbcType;
     t_atoms*   atoms;
     rvec *     x, *v;
     rvec       dx;
@@ -439,7 +445,7 @@ static void chk_tps(const char* fn, real vdw_fac, real bon_lo, real bon_hi)
     real*      atom_vdw;
 
     fprintf(stderr, "Checking coordinate file %s\n", fn);
-    read_tps_conf(fn, &top, &ePBC, &x, &v, box, TRUE);
+    read_tps_conf(fn, &top, &pbcType, &x, &v, box, TRUE);
     atoms = &top.atoms;
     natom = atoms->nr;
     fprintf(stderr, "%d atoms in file\n", atoms->nr);
@@ -516,7 +522,7 @@ static void chk_tps(const char* fn, real vdw_fac, real bon_lo, real bon_hi)
         }
         if (bB)
         {
-            set_pbc(&pbc, ePBC, box);
+            set_pbc(&pbc, pbcType, box);
         }
 
         bFirst = TRUE;
@@ -811,6 +817,7 @@ int gmx_check(int argc, char* argv[])
     {
         fprintf(stderr, "Please give me TWO trajectory (.xtc/.trr/.tng) files!\n");
     }
+    output_env_done(oenv);
 
     fn1 = opt2fn_null("-s1", NFILE, fnm);
     fn2 = opt2fn_null("-s2", NFILE, fnm);
index 09adddc79534ef0660d78c4fd3eda462db9ff7cb..fe66b9f58814f5d3ad38b6a47dd3bb60284cfe75 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,7 +41,7 @@
 
 #include <cmath>
 
-#include "gromacs/commandline/pargs.h"
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
 #include "gromacs/fileio/checkpoint.h"
 #include "gromacs/fileio/enxio.h"
 #include "gromacs/fileio/tpxio.h"
@@ -49,6 +50,9 @@
 #include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/mdtypes/state.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/filenameoption.h"
+#include "gromacs/options/ioptionscontainer.h"
 #include "gromacs/random/seed.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/topology/index.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/stringutil.h"
 
-#define RANGECHK(i, n)                                                                           \
-    if ((i) >= (n))                                                                              \
-    gmx_fatal(FARGS,                                                                             \
-              "Your index file contains atomnumbers (e.g. %d)\nthat are larger than the number " \
-              "of atoms in the tpr file (%d)",                                                   \
-              (i), (n))
+static void rangeCheck(int numberInIndexFile, int maxAtomNumber)
+{
+    if ((numberInIndexFile) >= (maxAtomNumber))
+    {
+        gmx_fatal(FARGS,
+                  "Your index file contains atomnumbers (e.g. %d)\nthat are larger than the number "
+                  "of atoms in the tpr file (%d)",
+                  (numberInIndexFile), (maxAtomNumber));
+    }
+}
 
-static gmx_bool* bKeepIt(int gnx, int natoms, int index[])
+static std::vector<bool> bKeepIt(int gnx, int natoms, int index[])
 {
-    gmx_bool* b;
-    int       i;
+    std::vector<bool> b(natoms);
 
-    snew(b, natoms);
-    for (i = 0; (i < gnx); i++)
+    for (int i = 0; (i < gnx); i++)
     {
-        RANGECHK(index[i], natoms);
+        rangeCheck(index[i], natoms);
         b[index[i]] = TRUE;
     }
 
     return b;
 }
 
-static int* invind(int gnx, int natoms, int index[])
+static std::vector<int> invind(int gnx, int natoms, int index[])
 {
-    int* inv;
-    int  i;
+    std::vector<int> inv(natoms);
 
-    snew(inv, natoms);
-    for (i = 0; (i < gnx); i++)
+    for (int i = 0; (i < gnx); i++)
     {
-        RANGECHK(index[i], natoms);
+        rangeCheck(index[i], natoms);
         inv[index[i]] = i;
     }
 
     return inv;
 }
 
-static void reduce_block(const gmx_bool bKeep[], t_block* block, const char* name)
+static gmx::ListOfLists<int> reduce_listoflists(gmx::ArrayRef<const int>     invindex,
+                                                const std::vector<bool>&     bKeep,
+                                                const gmx::ListOfLists<int>& src,
+                                                const char*                  name)
 {
-    int* index;
-    int  i, j, newi, newj;
+    gmx::ListOfLists<int> lists;
 
-    snew(index, block->nr);
-
-    newi = newj = 0;
-    for (i = 0; (i < block->nr); i++)
+    std::vector<int> exclusionsForAtom;
+    for (gmx::index i = 0; i < src.ssize(); i++)
     {
-        for (j = block->index[i]; (j < block->index[i + 1]); j++)
+        if (bKeep[i])
         {
-            if (bKeep[j])
+            exclusionsForAtom.clear();
+            for (const int j : src[i])
             {
-                newj++;
+                if (bKeep[j])
+                {
+                    exclusionsForAtom.push_back(invindex[j]);
+                }
             }
-        }
-        if (newj > index[newi])
-        {
-            newi++;
-            index[newi] = newj;
+            lists.pushBack(exclusionsForAtom);
         }
     }
 
-    fprintf(stderr, "Reduced block %8s from %6d to %6d index-, %6d to %6d a-entries\n", name,
-            block->nr, newi, block->index[block->nr], newj);
-    block->index = index;
-    block->nr    = newi;
-}
+    fprintf(stderr, "Reduced block %8s from %6zu to %6zu index-, %6d to %6d a-entries\n", name,
+            src.size(), lists.size(), src.numElements(), lists.numElements());
 
-static void reduce_blocka(const int invindex[], const gmx_bool bKeep[], t_blocka* block, const char* name)
-{
-    int *index, *a;
-    int  i, j, k, newi, newj;
-
-    snew(index, block->nr);
-    snew(a, block->nra);
-
-    newi = newj = 0;
-    for (i = 0; (i < block->nr); i++)
-    {
-        for (j = block->index[i]; (j < block->index[i + 1]); j++)
-        {
-            k = block->a[j];
-            if (bKeep[k])
-            {
-                a[newj] = invindex[k];
-                newj++;
-            }
-        }
-        if (newj > index[newi])
-        {
-            newi++;
-            index[newi] = newj;
-        }
-    }
-
-    fprintf(stderr, "Reduced block %8s from %6d to %6d index-, %6d to %6d a-entries\n", name,
-            block->nr, newi, block->nra, newj);
-    block->index = index;
-    block->a     = a;
-    block->nr    = newi;
-    block->nra   = newj;
+    return lists;
 }
 
 static void reduce_rvec(int gnx, const int index[], rvec vv[])
@@ -220,90 +189,74 @@ static void reduce_atom(int gnx, const int index[], t_atom atom[], char*** atomn
     sfree(rinfo);
 }
 
-static void reduce_ilist(const int invindex[], const gmx_bool bKeep[], t_ilist* il, int nratoms, const char* name)
+static void reduce_ilist(gmx::ArrayRef<const int> invindex,
+                         const std::vector<bool>& bKeep,
+                         InteractionList*         il,
+                         int                      nratoms,
+                         const char*              name)
 {
-    t_iatom* ia;
-    int      i, j, newnr;
-    gmx_bool bB;
-
-    if (il->nr)
+    if (!il->empty())
     {
-        snew(ia, il->nr);
-        newnr = 0;
-        for (i = 0; (i < il->nr); i += nratoms + 1)
+        std::vector<int> newAtoms(nratoms);
+        InteractionList  ilReduced;
+        for (int i = 0; i < il->size(); i += nratoms + 1)
         {
-            bB = TRUE;
-            for (j = 1; (j <= nratoms); j++)
+            bool bB = true;
+            for (int j = 0; j < nratoms; j++)
             {
-                bB = bB && bKeep[il->iatoms[i + j]];
+                bB = bB && bKeep[il->iatoms[i + 1 + j]];
             }
             if (bB)
             {
-                ia[newnr++] = il->iatoms[i];
-                for (j = 1; (j <= nratoms); j++)
+                for (int j = 0; j < nratoms; j++)
                 {
-                    ia[newnr++] = invindex[il->iatoms[i + j]];
+                    newAtoms[j] = invindex[il->iatoms[i + 1 + j]];
                 }
+                ilReduced.push_back(il->iatoms[i], nratoms, newAtoms.data());
             }
         }
-        fprintf(stderr, "Reduced ilist %8s from %6d to %6d entries\n", name, il->nr / (nratoms + 1),
-                newnr / (nratoms + 1));
-
-        il->nr = newnr;
-        for (i = 0; (i < newnr); i++)
-        {
-            il->iatoms[i] = ia[i];
-        }
+        fprintf(stderr, "Reduced ilist %8s from %6d to %6d entries\n", name,
+                il->size() / (nratoms + 1), ilReduced.size() / (nratoms + 1));
 
-        sfree(ia);
+        *il = std::move(ilReduced);
     }
 }
 
 static void reduce_topology_x(int gnx, int index[], gmx_mtop_t* mtop, rvec x[], rvec v[])
 {
-    t_topology top;
-    gmx_bool*  bKeep;
-    int*       invindex;
-    int        i;
+    gmx_localtop_t top(mtop->ffparams);
+    gmx_mtop_generate_local_top(*mtop, &top, false);
+    t_atoms atoms = gmx_mtop_global_atoms(mtop);
 
-    top      = gmx_mtop_t_to_t_topology(mtop, false);
-    bKeep    = bKeepIt(gnx, top.atoms.nr, index);
-    invindex = invind(gnx, top.atoms.nr, index);
+    const std::vector<bool> bKeep    = bKeepIt(gnx, atoms.nr, index);
+    const std::vector<int>  invindex = invind(gnx, atoms.nr, index);
 
-    reduce_block(bKeep, &(top.mols), "mols");
-    reduce_blocka(invindex, bKeep, &(top.excls), "excls");
     reduce_rvec(gnx, index, x);
     reduce_rvec(gnx, index, v);
-    reduce_atom(gnx, index, top.atoms.atom, top.atoms.atomname, &(top.atoms.nres), top.atoms.resinfo);
+    reduce_atom(gnx, index, atoms.atom, atoms.atomname, &(atoms.nres), atoms.resinfo);
 
-    for (i = 0; (i < F_NRE); i++)
+    for (int i = 0; (i < F_NRE); i++)
     {
         reduce_ilist(invindex, bKeep, &(top.idef.il[i]), interaction_function[i].nratoms,
                      interaction_function[i].name);
     }
 
-    top.atoms.nr = gnx;
+    atoms.nr = gnx;
 
     mtop->moltype.resize(1);
     mtop->moltype[0].name  = mtop->name;
-    mtop->moltype[0].atoms = top.atoms;
-    for (i = 0; i < F_NRE; i++)
+    mtop->moltype[0].atoms = atoms;
+    mtop->moltype[0].excls = reduce_listoflists(invindex, bKeep, top.excls, "excls");
+    for (int i = 0; i < F_NRE; i++)
     {
-        InteractionList& ilist = mtop->moltype[0].ilist[i];
-        ilist.iatoms.resize(top.idef.il[i].nr);
-        for (int j = 0; j < top.idef.il[i].nr; j++)
-        {
-            ilist.iatoms[j] = top.idef.il[i].iatoms[j];
-        }
+        mtop->moltype[0].ilist[i] = std::move(top.idef.il[i]);
     }
-    mtop->moltype[0].atoms = top.atoms;
-    mtop->moltype[0].excls = top.excls;
 
     mtop->molblock.resize(1);
     mtop->molblock[0].type = 0;
     mtop->molblock[0].nmol = 1;
 
-    mtop->natoms = top.atoms.nr;
+    mtop->natoms = atoms.nr;
 }
 
 static void zeroq(const int index[], gmx_mtop_t* mtop)
@@ -318,9 +271,51 @@ static void zeroq(const int index[], gmx_mtop_t* mtop)
     }
 }
 
-int gmx_convert_tpr(int argc, char* argv[])
+namespace gmx
+{
+
+namespace
+{
+
+class ConvertTpr : public ICommandLineOptionsModule
 {
-    const char* desc[] = {
+public:
+    ConvertTpr() {}
+
+    // From ICommandLineOptionsModule
+    void init(CommandLineModuleSettings* /*settings*/) override {}
+    void initOptions(IOptionsContainer* options, ICommandLineOptionsModuleSettings* settings) override;
+    void optionsFinished() override;
+    int  run() override;
+
+private:
+    //! Name of input tpr file.
+    std::string inputTprFileName_;
+    //! Name of input index file.
+    std::string inputIndexFileName_;
+    //! Name of output tpr file.
+    std::string outputTprFileName_;
+    //! If we have read in an index file.
+    bool haveReadIndexFile_ = false;
+    //! Time to extend simulation by.
+    real extendTime_ = 0;
+    //! If the option to extend simulation time is set.
+    bool extendTimeIsSet_ = false;
+    //! Final run time value.
+    real runToMaxTime_ = 0;
+    //! If the option to run simulation until specified time is set.
+    bool runToMaxTimeIsSet_ = false;
+    //! Maximum number of steps to run.
+    int64_t maxSteps_ = 0;
+    //! If the option to use maximumstep number is set.
+    bool maxStepsIsSet_ = false;
+    //! If the option to zero charge is set.
+    bool zeroQIsSet_ = false;
+};
+
+void ConvertTpr::initOptions(IOptionsContainer* options, ICommandLineOptionsModuleSettings* settings)
+{
+    std::vector<const char*> desc = {
         "[THISMODULE] can edit run input files in three ways.[PAR]",
         "[BB]1.[bb] by modifying the number of steps in a run input file",
         "with options [TT]-extend[tt], [TT]-until[tt] or [TT]-nsteps[tt]",
@@ -337,111 +332,118 @@ int gmx_convert_tpr(int argc, char* argv[])
         "using the LIE (Linear Interaction Energy) method."
     };
 
-    const char*       top_fn;
-    int               i;
-    int64_t           nsteps_req, run_step;
-    double            run_t, state_t;
-    gmx_bool          bSel;
-    gmx_bool          bNsteps, bExtend, bUntil;
-    gmx_mtop_t        mtop;
-    t_atoms           atoms;
-    t_state           state;
-    int               gnx;
-    char*             grpname;
-    int*              index = nullptr;
-    char              buf[200], buf2[200];
-    gmx_output_env_t* oenv;
-    t_filenm          fnm[] = { { efTPR, nullptr, nullptr, ffREAD },
-                       { efNDX, nullptr, nullptr, ffOPTRD },
-                       { efTPR, "-o", "tprout", ffWRITE } };
-#define NFILE asize(fnm)
-
-    /* Command line options */
-    static int      nsteps_req_int = 0;
-    static real     extend_t = 0.0, until_t = 0.0;
-    static gmx_bool bZeroQ = FALSE;
-    static t_pargs  pa[]   = {
-        { "-extend", FALSE, etREAL, { &extend_t }, "Extend runtime by this amount (ps)" },
-        { "-until", FALSE, etREAL, { &until_t }, "Extend runtime until this ending time (ps)" },
-        { "-nsteps", FALSE, etINT, { &nsteps_req_int }, "Change the number of steps" },
-        { "-zeroq",
-          FALSE,
-          etBOOL,
-          { &bZeroQ },
-          "Set the charges of a group (from the index) to zero" }
-    };
+    settings->setHelpText(desc);
+
+    options->addOption(FileNameOption("s")
+                               .filetype(eftTopology)
+                               .inputFile()
+                               .required()
+                               .store(&inputTprFileName_)
+                               .defaultBasename("topol")
+                               .description("Run input file to modify"));
+    options->addOption(FileNameOption("n")
+                               .filetype(eftIndex)
+                               .inputFile()
+                               .store(&inputIndexFileName_)
+                               .storeIsSet(&haveReadIndexFile_)
+                               .defaultBasename("index")
+                               .description("File containing additional index groups"));
+    options->addOption(FileNameOption("o")
+                               .filetype(eftTopology)
+                               .outputFile()
+                               .store(&outputTprFileName_)
+                               .defaultBasename("tprout")
+                               .description("Generated modified run input file"));
+    options->addOption(RealOption("extend")
+                               .store(&extendTime_)
+                               .storeIsSet(&extendTimeIsSet_)
+                               .timeValue()
+                               .description("Extend runtime by this amount (ps)"));
+    options->addOption(RealOption("until")
+                               .store(&runToMaxTime_)
+                               .storeIsSet(&runToMaxTimeIsSet_)
+                               .timeValue()
+                               .description("Extend runtime until this ending time (ps)"));
+    options->addOption(
+            Int64Option("nsteps").store(&maxSteps_).storeIsSet(&maxStepsIsSet_).description("Change the number of steps"));
+    options->addOption(
+            BooleanOption("zeroq").store(&zeroQIsSet_).description("Set the charges of a group (from the index) to zero"));
+}
 
-    /* Parse the command line */
-    if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
-    {
-        return 0;
-    }
+void ConvertTpr::optionsFinished() {}
 
-    /* Convert int to int64_t */
-    nsteps_req = nsteps_req_int;
-    bNsteps    = opt2parg_bSet("-nsteps", asize(pa), pa);
-    bExtend    = opt2parg_bSet("-extend", asize(pa), pa);
-    bUntil     = opt2parg_bSet("-until", asize(pa), pa);
+int ConvertTpr::run()
+{
+    gmx_mtop_t mtop;
+    t_atoms    atoms;
+    t_state    state;
+    char       buf[200], buf2[200];
 
-    top_fn = ftp2fn(efTPR, NFILE, fnm);
-    fprintf(stderr, "Reading toplogy and stuff from %s\n", top_fn);
+    fprintf(stderr, "Reading toplogy and stuff from %s\n", inputTprFileName_.c_str());
 
     t_inputrec  irInstance;
     t_inputrec* ir = &irInstance;
-    read_tpx_state(top_fn, ir, &state, &mtop);
-    run_step = ir->init_step;
-    run_t    = ir->init_step * ir->delta_t + ir->init_t;
+    read_tpx_state(inputTprFileName_.c_str(), ir, &state, &mtop);
+    int64_t currentMaxStep    = ir->init_step;
+    double  currentRunTime    = ir->init_step * ir->delta_t + ir->init_t;
+    real    currentMaxRunTime = 0.0;
 
-    if (bNsteps)
+    if (maxStepsIsSet_)
     {
-        fprintf(stderr, "Setting nsteps to %s\n", gmx_step_str(nsteps_req, buf));
-        ir->nsteps = nsteps_req;
+        fprintf(stderr, "Setting nsteps to %s\n", gmx_step_str(maxSteps_, buf));
+        ir->nsteps = maxSteps_;
     }
     else
     {
         /* Determine total number of steps remaining */
-        if (bExtend)
+        if (extendTimeIsSet_)
         {
-            ir->nsteps = ir->nsteps - (run_step - ir->init_step) + gmx::roundToInt64(extend_t / ir->delta_t);
-            printf("Extending remaining runtime of by %g ps (now %s steps)\n", extend_t,
+            ir->nsteps = ir->nsteps - (currentMaxStep - ir->init_step)
+                         + gmx::roundToInt64(extendTime_ / ir->delta_t);
+            printf("Extending remaining runtime of by %g ps (now %s steps)\n", extendTime_,
                    gmx_step_str(ir->nsteps, buf));
         }
-        else if (bUntil)
+        else if (runToMaxTimeIsSet_)
         {
             printf("nsteps = %s, run_step = %s, current_t = %g, until = %g\n",
-                   gmx_step_str(ir->nsteps, buf), gmx_step_str(run_step, buf2), run_t, until_t);
-            ir->nsteps = gmx::roundToInt64((until_t - run_t) / ir->delta_t);
-            printf("Extending remaining runtime until %g ps (now %s steps)\n", until_t,
+                   gmx_step_str(ir->nsteps, buf), gmx_step_str(currentMaxStep, buf2),
+                   currentRunTime, runToMaxTime_);
+            ir->nsteps = gmx::roundToInt64((currentMaxRunTime - currentRunTime) / ir->delta_t);
+            printf("Extending remaining runtime until %g ps (now %s steps)\n", currentMaxRunTime,
                    gmx_step_str(ir->nsteps, buf));
         }
         else
         {
-            ir->nsteps -= run_step - ir->init_step;
+            ir->nsteps -= currentMaxStep - ir->init_step;
             /* Print message */
             printf("%s steps (%g ps) remaining from first run.\n", gmx_step_str(ir->nsteps, buf),
                    ir->nsteps * ir->delta_t);
         }
     }
 
-    if (bNsteps || bZeroQ || (ir->nsteps > 0))
+    if (maxStepsIsSet_ || zeroQIsSet_ || (ir->nsteps > 0))
     {
-        ir->init_step = run_step;
+        ir->init_step = currentMaxStep;
 
-        if (ftp2bSet(efNDX, NFILE, fnm) || !(bNsteps || bExtend || bUntil))
+        if (haveReadIndexFile_ || !(maxStepsIsSet_ || extendTimeIsSet_ || runToMaxTimeIsSet_))
         {
-            atoms = gmx_mtop_global_atoms(&mtop);
-            get_index(&atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &gnx, &index, &grpname);
-            if (!bZeroQ)
+            atoms         = gmx_mtop_global_atoms(&mtop);
+            int   gnx     = 0;
+            int*  index   = nullptr;
+            char* grpname = nullptr;
+            get_index(&atoms, inputIndexFileName_.c_str(), 1, &gnx, &index, &grpname);
+            bool bSel = false;
+            if (!zeroQIsSet_)
             {
                 bSel = (gnx != state.natoms);
-                for (i = 0; ((i < gnx) && (!bSel)); i++)
+                for (int i = 0; ((i < gnx) && (!bSel)); i++)
                 {
                     bSel = (i != index[i]);
                 }
             }
             else
             {
-                bSel = FALSE;
+                bSel = false;
             }
             if (bSel)
             {
@@ -452,7 +454,7 @@ int gmx_convert_tpr(int argc, char* argv[])
                 reduce_topology_x(gnx, index, &mtop, state.x.rvec_array(), state.v.rvec_array());
                 state.natoms = gnx;
             }
-            else if (bZeroQ)
+            else if (zeroQIsSet_)
             {
                 zeroq(index, &mtop);
                 fprintf(stderr, "Zero-ing charges for group %s\n", grpname);
@@ -463,13 +465,13 @@ int gmx_convert_tpr(int argc, char* argv[])
             }
         }
 
-        state_t = ir->init_t + ir->init_step * ir->delta_t;
+        double stateTime = ir->init_t + ir->init_step * ir->delta_t;
         sprintf(buf, "Writing statusfile with starting step %s%s and length %s%s steps...\n", "%10",
                 PRId64, "%10", PRId64);
         fprintf(stderr, buf, ir->init_step, ir->nsteps);
         fprintf(stderr, "                                 time %10.3f and length %10.3f ps\n",
-                state_t, ir->nsteps * ir->delta_t);
-        write_tpx_state(opt2fn("-o", NFILE, fnm), ir, &state, &mtop);
+                stateTime, ir->nsteps * ir->delta_t);
+        write_tpx_state(outputTprFileName_.c_str(), ir, &state, &mtop);
     }
     else
     {
@@ -478,3 +480,14 @@ int gmx_convert_tpr(int argc, char* argv[])
 
     return 0;
 }
+
+} // namespace
+
+const char ConvertTprInfo::name[]             = "convert-tpr";
+const char ConvertTprInfo::shortDescription[] = "Make a modifed run-input file";
+ICommandLineOptionsModulePointer ConvertTprInfo::create()
+{
+    return ICommandLineOptionsModulePointer(std::make_unique<ConvertTpr>());
+}
+
+} // namespace gmx
index ed8bf8adb6b1fbfa7290d478ce3f58273913b48c..7af6d4e420254714b629fe635d8853cc7431b6f7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_TOOLS_CONVERT_TPR_H
 #define GMX_TOOLS_CONVERT_TPR_H
 
-/*! \brief Implements gmx convert-tpr
- *
- * \param[in] argc  argc value passed to main().
- * \param[in] argv  argv array passed to main().
- */
-int gmx_convert_tpr(int argc, char* argv[]);
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
+
+namespace gmx
+{
+
+//! Declares gmx convert-tpr
+class ConvertTprInfo
+{
+public:
+    //! Name of the module.
+    static const char name[];
+    //! Short description what the module does.
+    static const char shortDescription[];
+    //! Instantiatiates the module.
+    static ICommandLineOptionsModulePointer create();
+};
+
+} // namespace gmx
 
 #endif
index 622b4c1eb31daa50b7b37f19f5a015edf7e2ad2c..9f004bc9fac8af98e5e9162d0c43d3c6d9f28126 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2013, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 4f9b1281f765eb4aabf4f110e171f63395117249..f918abed80abcd6c62be83a94927b4afdb9d3c21 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e2e6b15083d0c2be8742178cb7c2246955aa5b2a..34ecf8179847b002e30cef56fc4f7724f5753521 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -1534,7 +1535,7 @@ int gmx_make_ndx(int argc, char* argv[])
     int               j;
     t_atoms           atoms;
     rvec *            x, *v;
-    int               ePBC;
+    PbcType           pbcType;
     matrix            box;
     t_blocka *        block, *block2;
     char **           gnames, **gnames2;
@@ -1563,7 +1564,7 @@ int gmx_make_ndx(int argc, char* argv[])
     {
         bool haveFullTopology = false;
         fprintf(stderr, "\nReading structure file\n");
-        readConfAndTopology(stxfile, &haveFullTopology, &mtop, &ePBC, &x, &v, box);
+        readConfAndTopology(stxfile, &haveFullTopology, &mtop, &pbcType, &x, &v, box);
         atoms = gmx_mtop_global_atoms(&mtop);
         if (atoms.pdbinfo == nullptr)
         {
index baa59f8bd9cc2ab4a7e7ba9fba4648462b2463b0..ad7a15459c53303224f196eb201e3b8ab7b14063 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -119,9 +120,7 @@ static void fill_ft_ind(int nft, const int* ft, const t_idef* idef, int ft_ind[]
                         sprintf(buf, "RB-A1=%.2f", idef->iparams[i].rbdihs.rbcA[1]);
                         break;
                     case F_RESTRANGLES:
-                        sprintf(buf, "Theta=%.1f_%.2f", idef->iparams[i].harmonic.rA,
-                                idef->iparams[i].harmonic.krA);
-                        break;
+                        // Fall through intended
                     case F_RESTRDIHS:
                         sprintf(buf, "Theta=%.1f_%.2f", idef->iparams[i].harmonic.rA,
                                 idef->iparams[i].harmonic.krA);
index 26eef655ec15314f7e4d2509565787a2cac2f431..ba1fb3b14216d366716ccc5e358f85e0053fba10 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2017,2018,2019 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -645,7 +647,7 @@ static real estimate_reciprocal(t_inputinfo* info,
         /* Broadcast the random number array to the other nodes */
         if (PAR(cr))
         {
-            nblock_bc(cr, xtot, numbers);
+            nblock_bc(cr->mpi_comm_mygroup, xtot, numbers);
         }
 
         if (bVerbose && MASTER(cr))
@@ -823,11 +825,11 @@ static int prepare_x_q(real* q[], rvec* x[], const gmx_mtop_t* mtop, const rvec
     if (PAR(cr))
     {
         /* Transfer the number of charges */
-        block_bc(cr, nq);
-        snew_bc(cr, *x, nq);
-        snew_bc(cr, *q, nq);
-        nblock_bc(cr, nq, *x);
-        nblock_bc(cr, nq, *q);
+        block_bc(cr->mpi_comm_mygroup, nq);
+        snew_bc(MASTER(cr), *x, nq);
+        snew_bc(MASTER(cr), *q, nq);
+        nblock_bc(cr->mpi_comm_mygroup, nq, *x);
+        nblock_bc(cr->mpi_comm_mygroup, nq, *q);
     }
 
     return nq;
@@ -882,20 +884,20 @@ static void read_tpr_file(const char*  fn_sim_tpr,
 /* Transfer what we need for parallelizing the reciprocal error estimate */
 static void bcast_info(t_inputinfo* info, const t_commrec* cr)
 {
-    nblock_bc(cr, info->n_entries, info->nkx);
-    nblock_bc(cr, info->n_entries, info->nky);
-    nblock_bc(cr, info->n_entries, info->nkz);
-    nblock_bc(cr, info->n_entries, info->ewald_beta);
-    nblock_bc(cr, info->n_entries, info->pme_order);
-    nblock_bc(cr, info->n_entries, info->e_dir);
-    nblock_bc(cr, info->n_entries, info->e_rec);
-    block_bc(cr, info->volume);
-    block_bc(cr, info->recipbox);
-    block_bc(cr, info->natoms);
-    block_bc(cr, info->fracself);
-    block_bc(cr, info->bTUNE);
-    block_bc(cr, info->q2all);
-    block_bc(cr, info->q2allnr);
+    nblock_bc(cr->mpi_comm_mygroup, info->n_entries, info->nkx);
+    nblock_bc(cr->mpi_comm_mygroup, info->n_entries, info->nky);
+    nblock_bc(cr->mpi_comm_mygroup, info->n_entries, info->nkz);
+    nblock_bc(cr->mpi_comm_mygroup, info->n_entries, info->ewald_beta);
+    nblock_bc(cr->mpi_comm_mygroup, info->n_entries, info->pme_order);
+    nblock_bc(cr->mpi_comm_mygroup, info->n_entries, info->e_dir);
+    nblock_bc(cr->mpi_comm_mygroup, info->n_entries, info->e_rec);
+    block_bc(cr->mpi_comm_mygroup, info->volume);
+    block_bc(cr->mpi_comm_mygroup, info->recipbox);
+    block_bc(cr->mpi_comm_mygroup, info->natoms);
+    block_bc(cr->mpi_comm_mygroup, info->fracself);
+    block_bc(cr->mpi_comm_mygroup, info->bTUNE);
+    block_bc(cr->mpi_comm_mygroup, info->q2all);
+    block_bc(cr->mpi_comm_mygroup, info->q2allnr);
 }
 
 
@@ -1119,7 +1121,7 @@ int gmx_pme_error(int argc, char* argv[])
 
 #define NFILE asize(fnm)
 
-    CommrecHandle commrecHandle = init_commrec(MPI_COMM_WORLD, nullptr);
+    CommrecHandle commrecHandle = init_commrec(MPI_COMM_WORLD);
     t_commrec*    cr            = commrecHandle.get();
     PCA_Flags                   = PCA_NOEXIT_ON_ARGS;
 
index 4512b24b2a560bfbb30e6bc8d31e6a89b4bfc9ad..1e75dd0caaacfd12b49033b7c4d63600737db172 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_TOOLS_REPORT_METHODS_H
 #define GMX_TOOLS_REPORT_METHODS_H
 
+#include <string>
+
 #include "gromacs/commandline/cmdlineoptionsmodule.h"
-#include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/topology/topology.h"
 #include "gromacs/utility/filestream.h"
 #include "gromacs/utility/textwriter.h"
 
+struct gmx_mtop_t;
+struct t_inputrec;
+
 namespace gmx
 {
 
index 6796538f7cda0fc23eae5e0988a228af720ae324..22f15ad5853e93c25d5d67da986651619f5f4ab2 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-gmx_add_unit_test(ToolUnitTests tool-test
-                  dump.cpp
-                  report_methods.cpp
-                  trjconv.cpp)
-
+gmx_add_gtest_executable(tool-test
+    CPP_SOURCE_FILES
+        dump.cpp
+        helpwriting.cpp
+        report_methods.cpp
+        trjconv.cpp
+        )
+gmx_register_gtest_test(ToolUnitTests tool-test SLOW_TEST)
index 05982fa110c25ce7c3892a3adbc291ec83cdd699..8e61b543c644d9138e8d32a6a2aa5d349d683a89 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -36,7 +36,7 @@
  * \brief
  * Tests for functionality of the "dump" tool.
  *
- * \author
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
  */
 #include "gmxpre.h"
 
@@ -59,7 +59,7 @@ class DumpTest : public ::testing::Test
 {
 public:
     //! Run test case.
-    void runTest(CommandLine* cmdline);
+    static void runTest(CommandLine* cmdline);
 
 protected:
     // TODO this is changed in newer googletest versions
diff --git a/src/gromacs/tools/tests/helpwriting.cpp b/src/gromacs/tools/tests/helpwriting.cpp
new file mode 100644 (file)
index 0000000..a34cf10
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements tests on tool help writing. Based on mdrun test version.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ */
+#include "gmxpre.h"
+
+#include <memory>
+
+#include "gromacs/commandline/cmdlinehelpcontext.h"
+#include "gromacs/commandline/cmdlinemodule.h"
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
+#include "gromacs/tools/convert_tpr.h"
+#include "gromacs/tools/dump.h"
+#include "gromacs/tools/report_methods.h"
+#include "gromacs/utility/stringstream.h"
+#include "gromacs/utility/textwriter.h"
+
+#include "testutils/cmdlinetest.h"
+#include "testutils/refdata.h"
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+class HelpwritingTest : public gmx::test::CommandLineTestBase
+{
+public:
+    void runTest(gmx::ICommandLineModule* module) { testWriteHelp(module); }
+};
+
+TEST_F(HelpwritingTest, ConvertTprWritesHelp)
+{
+    const std::unique_ptr<gmx::ICommandLineModule> module(gmx::ICommandLineOptionsModule::createModule(
+            "convert-tpr", "Dummy Info", ConvertTprInfo::create()));
+    runTest(module.get());
+};
+
+
+TEST_F(HelpwritingTest, DumpWritesHelp)
+{
+    const std::unique_ptr<gmx::ICommandLineModule> module(
+            gmx::ICommandLineOptionsModule::createModule("dump", "Dummy Info", DumpInfo::create()));
+    runTest(module.get());
+};
+
+TEST_F(HelpwritingTest, ReportMethodsWritesHelp)
+{
+    const std::unique_ptr<gmx::ICommandLineModule> module(gmx::ICommandLineOptionsModule::createModule(
+            "report-methods", "Dummy Info", ReportMethodsInfo::create()));
+    runTest(module.get());
+};
+
+} // namespace
+} // namespace test
+} // namespace gmx
diff --git a/src/gromacs/tools/tests/refdata/HelpwritingTest_ConvertTprWritesHelp.xml b/src/gromacs/tools/tests/refdata/HelpwritingTest_ConvertTprWritesHelp.xml
new file mode 100644 (file)
index 0000000..b0c8905
--- /dev/null
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="HelpOutput"><![CDATA[
+SYNOPSIS
+
+test convert-tpr [-s [<.tpr/.gro/...>]] [-n [<.ndx>]] [-o [<.tpr/.gro/...>]]
+             [-extend <time>] [-until <time>] [-nsteps <int>] [-[no]zeroq]
+
+DESCRIPTION
+
+test convert-tpr can edit run input files in three ways.
+
+1. by modifying the number of steps in a run input file with options -extend,
+-until or -nsteps (nsteps=-1 means unlimited number of steps)
+
+2. by creating a .tpx file for a subset of your original tpx file, which is
+useful when you want to remove the solvent from your .tpx file, or when you
+want to make e.g. a pure Calpha .tpx file. Note that you may need to use
+-nsteps -1 (or similar) to get this to work. WARNING: this .tpx file is not
+fully functional.
+
+3. by setting the charges of a specified group to zero. This is useful when
+doing free energy estimates using the LIE (Linear Interaction Energy) method.
+
+OPTIONS
+
+Options to specify input files:
+
+ -s      [<.tpr/.gro/...>]  (topol.tpr)
+           Run input file to modify: tpr gro g96 pdb brk ent
+ -n      [<.ndx>]           (index.ndx)      (Opt.)
+           File containing additional index groups
+
+Options to specify output files:
+
+ -o      [<.tpr/.gro/...>]  (tprout.tpr)     (Opt.)
+           Generated modified run input file: tpr gro g96 pdb brk ent
+
+Other options:
+
+ -extend <time>             (0)
+           Extend runtime by this amount (ps)
+ -until  <time>             (0)
+           Extend runtime until this ending time (ps)
+ -nsteps <int>              (0)
+           Change the number of steps
+ -[no]zeroq                 (no)
+           Set the charges of a group (from the index) to zero
+]]></String>
+</ReferenceData>
diff --git a/src/gromacs/tools/tests/refdata/HelpwritingTest_DumpWritesHelp.xml b/src/gromacs/tools/tests/refdata/HelpwritingTest_DumpWritesHelp.xml
new file mode 100644 (file)
index 0000000..fd83c33
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="HelpOutput"><![CDATA[
+SYNOPSIS
+
+test dump [-s <.tpr>] [-f <.xtc/.trr/...>] [-e <.edr>] [-cp <.cpt>]
+          [-p <.top>] [-mtx <.mtx>] [-om <.mdp>] [-[no]nr] [-[no]param]
+          [-[no]sys] [-[no]orgir]
+
+DESCRIPTION
+
+test dump reads a run input file (.tpr), a trajectory (.trr/.xtc/tng), an
+energy file (.edr), a checkpoint file (.cpt) or topology file (.top) and
+prints that to standard output in a readable format. This program is essential
+for checking your run input file in case of problems.
+
+OPTIONS
+
+Options to specify input files:
+
+ -s      <.tpr>                              (Opt.)
+           Run input file to dump
+ -f      <.xtc/.trr/...>                     (Opt.)
+           Trajectory file to dump: xtc trr cpt gro g96 pdb tng
+ -e      <.edr>                              (Opt.)
+           Energy file to dump
+ -cp     <.cpt>                              (Opt.)
+           Checkpoint file to dump
+ -p      <.top>                              (Opt.)
+           Topology file to dump
+ -mtx    <.mtx>                              (Opt.)
+           Hessian matrix to dump
+
+Options to specify output files:
+
+ -om     <.mdp>                              (Opt.)
+           grompp input file from run input file
+
+Other options:
+
+ -[no]nr                    (yes)
+           Show index numbers in output (leaving them out makes comparison
+           easier, but creates a useless topology)
+ -[no]param                 (no)
+           Show parameters for each bonded interaction (for comparing dumps,
+           it is useful to combine this with -nonr)
+ -[no]sys                   (no)
+           List the atoms and bonded interactions for the whole system instead
+           of for each molecule type
+ -[no]orgir                 (no)
+           Show input parameters from tpr as they were written by the version
+           that produced the file, instead of how the current version reads
+           them
+
+KNOWN ISSUES
+
+* The .mdp file produced by -om can not be read by grompp.
+]]></String>
+</ReferenceData>
diff --git a/src/gromacs/tools/tests/refdata/HelpwritingTest_ReportMethodsWritesHelp.xml b/src/gromacs/tools/tests/refdata/HelpwritingTest_ReportMethodsWritesHelp.xml
new file mode 100644 (file)
index 0000000..e4415ef
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="HelpOutput"><![CDATA[
+SYNOPSIS
+
+test report-methods [-s [<.tpr/.gro/...>]] [-m [<.tex>]] [-o [<.out>]]
+
+DESCRIPTION
+
+test report-methods reports basic system information for the run input file
+specfied with -s either to the terminal, to a LaTeX formatted output file if
+run with the -m option or to an unformatted file with the -o option. The
+functionality has been moved here from its previous place in [gmx-check].
+
+OPTIONS
+
+Options to specify input files:
+
+ -s      [<.tpr/.gro/...>]  (topol.tpr)
+           Run input file for report: tpr gro g96 pdb brk ent
+
+Options to specify output files:
+
+ -m      [<.tex>]           (report.tex)     (Opt.)
+           LaTeX formatted report output
+ -o      [<.out>]           (report.out)     (Opt.)
+           Unformatted report output to file
+]]></String>
+</ReferenceData>
index 97b17600710161e388a4e8ce82ee3fa91801387b..1283cb18659ea1495ec9a6d90984bb67e842c32d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -36,7 +36,7 @@
  * \brief
  * Tests for functionality of the "report" tool to write system information.
  *
- * \author
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
  */
 #include "gmxpre.h"
 
@@ -44,7 +44,9 @@
 
 #include "gromacs/fileio/tpxio.h"
 #include "gromacs/gmxpreprocess/grompp.h"
+#include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/mdtypes/state.h"
+#include "gromacs/topology/topology.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/stringstream.h"
 #include "gromacs/utility/textwriter.h"
index f220c7d4f55e104846e6f067fc1809c6b229ca59..f4c7b162c20c9f16edd0c2931864669754d48f19 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 0d5e1f45cbf806f19ff188a66d4e27caaa313583..b2377eafd6cf650372b3eac5a858ccb1879a069d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017, The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f222e4ead8108d638acf3fac7574c3aa42607dff..43274a7522d4cfdb496383daa71bb10959466e5e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -483,10 +484,10 @@ int gmx_trjconv(int argc, char* argv[])
     real*        w_rls = nullptr;
     int          m, i, d, frame, outframe, natoms, nout, ncent, newstep = 0, model_nr;
 #define SKIP 10
-    t_topology* top   = nullptr;
-    gmx_conect  gc    = nullptr;
-    int         ePBC  = -1;
-    t_atoms *   atoms = nullptr, useatoms;
+    t_topology* top     = nullptr;
+    gmx_conect  gc      = nullptr;
+    PbcType     pbcType = PbcType::Unset;
+    t_atoms *   atoms   = nullptr, useatoms;
     matrix      top_box;
     int *       index = nullptr, *cindex = nullptr;
     char*       grpnm = nullptr;
@@ -668,7 +669,7 @@ int gmx_trjconv(int argc, char* argv[])
         if (bTPS)
         {
             snew(top, 1);
-            read_tps_conf(top_file, top, &ePBC, &xp, nullptr, top_box, bReset || bPBCcomRes);
+            read_tps_conf(top_file, top, &pbcType, &xp, nullptr, top_box, bReset || bPBCcomRes);
             std::strncpy(top_title, *top->name, 255);
             top_title[255] = '\0';
             atoms          = &top->atoms;
@@ -701,7 +702,7 @@ int gmx_trjconv(int argc, char* argv[])
             }
             if (bRmPBC)
             {
-                gpbc = gmx_rmpbc_init(&top->idef, ePBC, top->atoms.nr);
+                gpbc = gmx_rmpbc_init(&top->idef, pbcType, top->atoms.nr);
             }
         }
 
@@ -897,7 +898,7 @@ int gmx_trjconv(int argc, char* argv[])
                 read_first_frame(oenv, &trxin, in_file, &fr, flags);
             }
 
-            set_trxframe_ePBC(&fr, ePBC);
+            setTrxFramePbcType(&fr, pbcType);
             natoms = fr.natoms;
 
             if (bSetTime)
@@ -1077,7 +1078,7 @@ int gmx_trjconv(int argc, char* argv[])
                 }
                 else if (bCluster)
                 {
-                    calc_pbc_cluster(ecenter, ifit, top, ePBC, fr.x, ind_fit, fr.box);
+                    calc_pbc_cluster(ecenter, ifit, top, pbcType, fr.x, ind_fit, fr.box);
                 }
 
                 if (bPFit)
@@ -1236,25 +1237,26 @@ int gmx_trjconv(int argc, char* argv[])
                             switch (unitcell_enum)
                             {
                                 case euRect:
-                                    put_atoms_in_box(ePBC, fr.box, positionsArrayRef);
+                                    put_atoms_in_box(pbcType, fr.box, positionsArrayRef);
                                     break;
                                 case euTric:
                                     put_atoms_in_triclinic_unitcell(ecenter, fr.box, positionsArrayRef);
                                     break;
                                 case euCompact:
-                                    put_atoms_in_compact_unitcell(ePBC, ecenter, fr.box, positionsArrayRef);
+                                    put_atoms_in_compact_unitcell(pbcType, ecenter, fr.box,
+                                                                  positionsArrayRef);
                                     break;
                             }
                         }
                         if (bPBCcomRes)
                         {
                             put_residue_com_in_box(unitcell_enum, ecenter, natoms, atoms->atom,
-                                                   ePBC, fr.box, fr.x);
+                                                   pbcType, fr.box, fr.x);
                         }
                         if (bPBCcomMol)
                         {
                             put_molecule_com_in_box(unitcell_enum, ecenter, &top->mols, natoms,
-                                                    atoms->atom, ePBC, fr.box, fr.x);
+                                                    atoms->atom, pbcType, fr.box, fr.x);
                         }
                         /* Copy the input trxframe struct to the output trxframe struct */
                         frout        = fr;
@@ -1388,7 +1390,7 @@ int gmx_trjconv(int argc, char* argv[])
                                             model_nr++;
                                         }
                                         write_pdbfile(out, title.c_str(), &useatoms, frout.x,
-                                                      frout.ePBC, frout.box, ' ', model_nr, gc);
+                                                      frout.pbcType, frout.box, ' ', model_nr, gc);
                                         break;
                                     case efG96:
                                         const char* outputTitle = "";
index ed2c06f717d1057d0d36a890ba812adf720174f0..da954ea332a983fad4499ebef2af002b02c91355 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2009-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -45,6 +45,7 @@
 #include <ctime>
 
 #include <algorithm>
+#include <numeric>
 #include <string>
 
 #ifdef HAVE_SYS_TIME_H
@@ -1294,7 +1295,7 @@ static void make_npme_list(const char* npmevalues_opt, /* Make a complete list w
                 break;
             default: gmx_fatal(FARGS, "Unknown option for eNPME in make_npme_list");
         }
-        if (gmx_greatest_common_divisor(npp, npme) >= min_factor)
+        if (std::gcd(npp, npme) >= min_factor)
         {
             (*nPMEnodes)[nlist] = npme;
             nlist++;
index f67bf0e2e1e19b28197f5a7a7d9fbf68c9798a22..c415086bfb0f97a29aa71b7b83419ef9f99fe592 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 4200d75983495ea2731a32b06fe16ce6e773db21..20ce6be8bb049e77b4b0a8ec20e272989ebce63a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index a7314cf7fceaebb7d66404d72e870682a8eda585..513d1cf22734425cb697a6054ae0fb17a666db2b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7c1cc84357f905775b0f3f38749d84ea473567a1..cc6ad6b191fb593251125fc68a1569d4bff94ae7 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -42,6 +43,7 @@
 
 #include <algorithm>
 
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/txtdump.h"
 
@@ -218,6 +220,19 @@ static int pr_blocka_title(FILE* fp, int indent, const char* title, const t_bloc
     return indent;
 }
 
+static int pr_listoflists_title(FILE* fp, int indent, const char* title, const gmx::ListOfLists<int>* lists)
+{
+    if (available(fp, lists, indent, title))
+    {
+        indent = pr_title(fp, indent, title);
+        pr_indent(fp, indent);
+        fprintf(fp, "numLists=%zu\n", lists->size());
+        pr_indent(fp, indent);
+        fprintf(fp, "numElements=%d\n", lists->numElements());
+    }
+    return indent;
+}
+
 static void low_pr_blocka(FILE* fp, int indent, const char* title, const t_blocka* block, gmx_bool bShowNumbers)
 {
     int i;
@@ -325,6 +340,43 @@ void pr_blocka(FILE* fp, int indent, const char* title, const t_blocka* block, g
     }
 }
 
+void pr_listoflists(FILE* fp, int indent, const char* title, const gmx::ListOfLists<int>* lists, gmx_bool bShowNumbers)
+{
+    if (available(fp, lists, indent, title))
+    {
+        indent = pr_listoflists_title(fp, indent, title, lists);
+        for (gmx::index i = 0; i < lists->ssize(); i++)
+        {
+            int                      size = pr_indent(fp, indent);
+            gmx::ArrayRef<const int> list = (*lists)[i];
+            if (list.empty())
+            {
+                size += fprintf(fp, "%s[%d]={", title, int(i));
+            }
+            else
+            {
+                size += fprintf(fp, "%s[%d][num=%zu]={", title, bShowNumbers ? int(i) : -1, list.size());
+            }
+            bool isFirst = true;
+            for (const int j : list)
+            {
+                if (!isFirst)
+                {
+                    size += fprintf(fp, ", ");
+                }
+                if ((size) > (USE_WIDTH))
+                {
+                    fprintf(fp, "\n");
+                    size = pr_indent(fp, indent + INDENT);
+                }
+                size += fprintf(fp, "%d", j);
+                isFirst = false;
+            }
+            fprintf(fp, "}\n");
+        }
+    }
+}
+
 void copy_block(const t_block* src, t_block* dst)
 {
     dst->nr = src->nr;
index db9065a2b2ad221bc8cd5ba04f6bf36a2e1265d1..10fee47a24b40f31275487be8f752c85de506af9 100644 (file)
@@ -48,6 +48,9 @@
 namespace gmx
 {
 
+template<typename>
+class ListOfLists;
+
 /*! \brief Division of a range of indices into consecutive blocks
  *
  * A range of consecutive indices 0 to full.range.end() is divided
@@ -220,5 +223,6 @@ void stupid_fill_blocka(t_blocka* grp, int natom);
 
 void pr_block(FILE* fp, int indent, const char* title, const t_block* block, gmx_bool bShowNumbers);
 void pr_blocka(FILE* fp, int indent, const char* title, const t_blocka* block, gmx_bool bShowNumbers);
+void pr_listoflists(FILE* fp, int indent, const char* title, const gmx::ListOfLists<int>* block, gmx_bool bShowNumbers);
 
 #endif
index 9472c43781e67e9c831f26ca6521ca807ae0a00f..c44f53125e5f29854ec9db91db2ea54d48d1e659 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "gromacs/topology/block.h"
 #include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/stringutil.h"
 
 namespace gmx
 {
 
+namespace
+{
+
+//! Converts ListOfLists to a list of ExclusionBlocks
+void listOfListsToExclusionBlocks(const ListOfLists<int>& b, gmx::ArrayRef<ExclusionBlock> b2)
+{
+    for (gmx::index i = 0; i < b.ssize(); i++)
+    {
+        for (int jAtom : b[i])
+        {
+            b2[i].atomNumber.push_back(jAtom);
+        }
+    }
+}
+
+//! Converts a list of ExclusionBlocks to ListOfLists
+void exclusionBlocksToListOfLists(gmx::ArrayRef<const ExclusionBlock> b2, ListOfLists<int>* b)
+{
+    b->clear();
+
+    for (const auto& block : b2)
+    {
+        b->pushBack(block.atomNumber);
+    }
+}
+
+} // namespace
+
 void blockaToExclusionBlocks(const t_blocka* b, gmx::ArrayRef<ExclusionBlock> b2)
 {
     for (int i = 0; (i < b->nr); i++)
@@ -79,21 +109,12 @@ void exclusionBlocksToBlocka(gmx::ArrayRef<const ExclusionBlock> b2, t_blocka* b
     b->index[i] = nra;
 }
 
-void mergeExclusions(t_blocka* excl, gmx::ArrayRef<ExclusionBlock> b2)
+namespace
 {
-    if (b2.empty())
-    {
-        return;
-    }
-    GMX_RELEASE_ASSERT(b2.ssize() == excl->nr,
-                       "Cannot merge exclusions for "
-                       "blocks that do not describe the same number "
-                       "of particles");
 
-    /* Convert the t_blocka entries to ExclusionBlock form */
-    blockaToExclusionBlocks(excl, b2);
-
-    /* Count and sort the exclusions */
+//! Counts and sorts the exclusions
+int countAndSortExclusions(gmx::ArrayRef<ExclusionBlock> b2)
+{
     int nra = 0;
     for (auto& block : b2)
     {
@@ -118,10 +139,29 @@ void mergeExclusions(t_blocka* excl, gmx::ArrayRef<ExclusionBlock> b2)
             nra += block.nra();
         }
     }
-    excl->nra = nra;
-    srenew(excl->a, excl->nra);
 
-    exclusionBlocksToBlocka(b2, excl);
+    return nra;
+}
+
+} // namespace
+
+void mergeExclusions(ListOfLists<int>* excl, gmx::ArrayRef<ExclusionBlock> b2)
+{
+    if (b2.empty())
+    {
+        return;
+    }
+    GMX_RELEASE_ASSERT(b2.ssize() == excl->ssize(),
+                       "Cannot merge exclusions for "
+                       "blocks that do not describe the same number "
+                       "of particles");
+
+    /* Convert the t_blocka entries to ExclusionBlock form */
+    listOfListsToExclusionBlocks(*excl, b2);
+
+    countAndSortExclusions(b2);
+
+    exclusionBlocksToListOfLists(b2, excl);
 }
 
 } // namespace gmx
index b27db36093d71f0c40a945ea8562a80079d86a93..69c9c54c21047ce8bc8add7e2403c8df4c1a37d9 100644 (file)
@@ -44,6 +44,8 @@ struct t_blocka;
 
 namespace gmx
 {
+template<typename>
+class ListOfLists;
 
 /*! \libinternal \brief
  * Describes exclusions for a single atom.
@@ -61,7 +63,7 @@ struct ExclusionBlock
  * Requires that \c b2 and \c excl describe the same number of
  * particles, if \c b2 describes a non-zero number.
  */
-void mergeExclusions(t_blocka* excl, gmx::ArrayRef<ExclusionBlock> b2);
+void mergeExclusions(ListOfLists<int>* excl, gmx::ArrayRef<ExclusionBlock> b2);
 
 /*! \brief
  * Convert the exclusions.
index da58aa3b2e6189b3a4f714772f73cf3107789f77..036960895d0bd45b626a03411963d8cbb0105535 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 11d9905e4f1334389cb094967eb6278436f57b7e..05342612190d128d08c7e8784afdeb002fbf3278 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -54,7 +55,7 @@ struct gmx_ffparams_t
     {
         GMX_ASSERT(iparams.size() == functype.size(), "Parameters and function types go together");
 
-        return gmx::ssize(functype);
+        return static_cast<int>(functype.size());
     }
 
     /* TODO: Consider merging functype and iparams, either by storing
index ef3f17e8374d4ec1197a70dd7a35adc51c02d8ca..a82e6e5754bbb112fe7d8f2fab1953dd5827c9df 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,6 +41,7 @@
 
 #include <cstdio>
 
+#include "gromacs/topology/forcefieldparameters.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/smalloc.h"
@@ -330,7 +332,7 @@ static void printIlist(FILE*             fp,
     indent = pr_title(fp, indent, title);
     pr_indent(fp, indent);
     fprintf(fp, "nr: %d\n", ilist.size());
-    if (ilist.size() > 0)
+    if (!ilist.empty())
     {
         pr_indent(fp, indent);
         fprintf(fp, "iatoms:\n");
@@ -411,15 +413,28 @@ void init_idef(t_idef* idef)
     idef->iparams_fbposres = nullptr;
     for (int f = 0; f < F_NRE; ++f)
     {
-        idef->il[f].iatoms          = nullptr;
-        idef->il[f].nalloc          = 0;
-        idef->il[f].nr              = 0;
-        idef->il[f].nr_nonperturbed = 0;
+        idef->il[f].iatoms = nullptr;
+        idef->il[f].nalloc = 0;
+        idef->il[f].nr     = 0;
     }
-    idef->cmap_grid               = nullptr;
-    idef->iparams_posres_nalloc   = 0;
-    idef->iparams_fbposres_nalloc = 0;
-    idef->ilsort                  = 0;
+}
+
+InteractionDefinitions::InteractionDefinitions(const gmx_ffparams_t& ffparams) :
+    iparams(ffparams.iparams),
+    functype(ffparams.functype),
+    cmap_grid(ffparams.cmap_grid)
+{
+}
+
+void InteractionDefinitions::clear()
+{
+    /* Clear the counts */
+    for (auto& ilist : il)
+    {
+        ilist.clear();
+    }
+    iparams_posres.clear();
+    iparams_fbposres.clear();
 }
 
 void done_idef(t_idef* idef)
@@ -433,19 +448,5 @@ void done_idef(t_idef* idef)
         sfree(idef->il[f].iatoms);
     }
 
-    delete idef->cmap_grid;
     init_idef(idef);
 }
-
-void copy_ilist(const t_ilist* src, t_ilist* dst)
-{
-    dst->nr              = src->nr;
-    dst->nr_nonperturbed = src->nr_nonperturbed;
-    dst->nalloc          = src->nalloc;
-
-    snew(dst->iatoms, dst->nr);
-    for (int i = 0; i < dst->nr; ++i)
-    {
-        dst->iatoms[i] = src->iatoms[i];
-    }
-}
index 15c010e286c48ee702f994c67efa113c799d1bdc..40a4913cfbb16b15652112801468ec393f35c78f 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +48,8 @@
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
+struct gmx_ffparams_t;
+
 typedef union t_iparams {
     /* Some parameters have A and B values for free energy calculations.
      * The B values are not used for regular simulations of course.
@@ -219,7 +222,44 @@ typedef int t_functype;
 struct InteractionList
 {
     /* Returns the total number of elements in iatoms */
-    int size() const { return gmx::ssize(iatoms); }
+    int size() const { return static_cast<int>(iatoms.size()); }
+
+    /* Returns whether the list is empty */
+    bool empty() const { return iatoms.empty(); }
+
+    /* Adds one interaction to the list */
+    template<std::size_t numAtoms>
+    void push_back(const int parameterType, const std::array<int, numAtoms>& atoms)
+    {
+        const std::size_t oldSize = iatoms.size();
+        iatoms.resize(iatoms.size() + 1 + numAtoms);
+        iatoms[oldSize] = parameterType;
+        for (std::size_t i = 0; i < numAtoms; i++)
+        {
+            iatoms[oldSize + 1 + i] = atoms[i];
+        }
+    }
+
+    /* Adds one interaction to the list */
+    void push_back(const int parameterType, const int numAtoms, const int* atoms)
+    {
+        const std::size_t oldSize = iatoms.size();
+        iatoms.resize(iatoms.size() + 1 + numAtoms);
+        iatoms[oldSize] = parameterType;
+        for (int i = 0; i < numAtoms; i++)
+        {
+            iatoms[oldSize + 1 + i] = atoms[i];
+        }
+    }
+
+    /* Appends \p ilist at the back of the list */
+    void append(const InteractionList& ilist)
+    {
+        iatoms.insert(iatoms.end(), ilist.iatoms.begin(), ilist.iatoms.end());
+    }
+
+    /* Clears the list */
+    void clear() { iatoms.clear(); }
 
     /* List of interactions, see explanation further down */
     std::vector<int> iatoms;
@@ -229,32 +269,23 @@ struct InteractionList
  *
  * TODO: Consider only including entries in use instead of all F_NRE
  */
-typedef std::array<InteractionList, F_NRE> InteractionLists;
+using InteractionLists = std::array<InteractionList, F_NRE>;
 
-/* Deprecated list of listed interactions.
- *
- * The nonperturbed/perturbed interactions are now separated (sorted) in the
- * ilist, such that the first 0..(nr_nonperturbed-1) ones are exactly that, and
- * the remaining ones from nr_nonperturbed..(nr-1) are perturbed bonded
- * interactions.
- */
+/* Deprecated list of listed interactions */
 struct t_ilist
 {
     /* Returns the total number of elements in iatoms */
     int size() const { return nr; }
 
+    /* Returns whether the list is empty */
+    bool empty() const { return nr == 0; }
+
     int      nr;
-    int      nr_nonperturbed;
     t_iatom* iatoms;
     int      nalloc;
 };
 
-/* TODO: Replace t_ilist in gmx_localtop_t by InteractionList.
- *       The nr_nonperturbed functionality needs to be ported.
- *       Remove t_topology.
- *       Remove t_ilist and remove templating on list type
- *       in mshift.cpp, constr.cpp, vsite.cpp and domdec_topology.cpp.
- */
+/* TODO: Remove t_ilist and remove templating on list type in mshift.cpp */
 
 /*
  * The structs InteractionList and t_ilist defines a list of atoms with their interactions.
@@ -295,7 +326,7 @@ static inline std::vector<InteractionListHandle> extractILists(const Interaction
     std::vector<InteractionListHandle> handles;
     for (size_t ftype = 0; ftype < ilists.size(); ftype++)
     {
-        if ((interaction_function[ftype].flags & flags) && ilists[ftype].size() > 0)
+        if ((interaction_function[ftype].flags & flags) && !ilists[ftype].empty())
         {
             handles.push_back({ static_cast<int>(ftype), ilists[ftype].iatoms });
         }
@@ -333,20 +364,54 @@ enum
     ilsortFE_SORTED
 };
 
-typedef struct t_idef
+/* Struct with list of interaction parameters and lists of interactions
+ *
+ * TODO: Convert to a proper class with private data members so we can
+ * ensure that the free-energy sorting and sorting setting is consistent.
+ */
+class InteractionDefinitions
+{
+public:
+    /* Constructor
+     *
+     * \param[in] ffparams  The interaction parameters, the lifetime of the created object should not exceed the lifetime of the passed parameters
+     */
+    InteractionDefinitions(const gmx_ffparams_t& ffparams);
+
+    // Clears data not read in from ffparams
+    void clear();
+
+    // The interaction parameters
+    const std::vector<t_iparams>& iparams;
+    // The function type per type
+    const std::vector<int>& functype;
+    // Position restraint interaction parameters
+    std::vector<t_iparams> iparams_posres;
+    // Flat-bottomed position restraint parameters
+    std::vector<t_iparams> iparams_fbposres;
+    // The list of interactions for each type. Note that some, such as LJ and COUL will have 0 entries.
+    std::array<InteractionList, F_NRE> il;
+    /* The number of non-perturbed interactions at the start of each entry in il */
+    std::array<int, F_NRE> numNonperturbedInteractions;
+    // The sorting state of interaction in il
+    int ilsort = ilsortUNKNOWN;
+    // The dihedral correction maps
+    gmx_cmap_t cmap_grid;
+};
+
+/* Deprecated interation definitions, used in t_topology */
+struct t_idef
 {
     int         ntypes;
     int         atnr;
     t_functype* functype;
     t_iparams*  iparams;
     real        fudgeQQ;
-    gmx_cmap_t* cmap_grid;
     t_iparams * iparams_posres, *iparams_fbposres;
-    int         iparams_posres_nalloc, iparams_fbposres_nalloc;
 
     t_ilist il[F_NRE];
     int     ilsort;
-} t_idef;
+};
 
 /*
  * The struct t_idef defines all the interactions for the complete
@@ -413,6 +478,4 @@ void init_idef(t_idef* idef);
  */
 void done_idef(t_idef* idef);
 
-void copy_ilist(const t_ilist* src, t_ilist* dst);
-
 #endif
index dded7eeb4df16e491db12ac0147dd300563d89f9..0be9b8bb1ec0b5b0028c83ade77edef8209508f4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,9 +45,9 @@
         str, lstr, (nra), (nrpa), (nrpb), IF_BOND \
     }
 
-#define def_bondedz(str, lstr, nra, nrpa, nrpb)                \
-    {                                                          \
-        str, lstr, (nra), (nrpa), (nrpb), IF_BOND | IF_LIMZERO \
+#define def_pair(str, lstr, nra, nrpa, nrpb)                             \
+    {                                                                    \
+        str, lstr, (nra), (nrpa), (nrpb), IF_BOND | IF_PAIR | IF_LIMZERO \
     }
 
 #define def_bondedt(str, lstr, nra, nrpa, nrpb)                  \
         str, lstr, (nra), (nrpa), (nrpb), IF_BOND | IF_ATYPE \
     }
 
+#define def_dihedral(str, lstr, nra, nrpa, nrpb)                \
+    {                                                           \
+        str, lstr, (nra), (nrpa), (nrpb), IF_BOND | IF_DIHEDRAL \
+    }
+
+#define def_dihedral_tabulated(str, lstr, nra, nrpa, nrpb)                     \
+    {                                                                          \
+        str, lstr, (nra), (nrpa), (nrpb), IF_BOND | IF_DIHEDRAL | IF_TABULATED \
+    }
+
 #define def_bond(str, lstr, nra, nrpa, nrpb)                               \
     {                                                                      \
         str, lstr, (nra), (nrpa), (nrpb), IF_BOND | IF_CHEMBOND | IF_BTYPE \
@@ -117,15 +128,16 @@ const t_interaction_function interaction_function[F_NRE] = {
     def_bonded("CROSS_BOND_BOND", "Bond-Cross", 3, 3, 0),
     def_bonded("CROSS_BOND_ANGLE", "BA-Cross", 3, 4, 0), def_angle("UREY_BRADLEY", "U-B", 3, 4, 4),
     def_angle("QANGLES", "Quartic Angles", 3, 6, 0), def_bondedt("TABANGLES", "Tab. Angles", 3, 2, 2),
-    def_bonded("PDIHS", "Proper Dih.", 4, 3, 3), def_bonded("RBDIHS", "Ryckaert-Bell.", 4, 6, 6),
-    def_bonded("RESTRDIHS", "Restricted Dih.", 4, 2, 2), def_bonded("CBTDIHS", "CBT Dih.", 4, 6, 6),
-    def_bonded("FOURDIHS", "Fourier Dih.", 4, 4, 4), def_bonded("IDIHS", "Improper Dih.", 4, 2, 2),
-    def_bonded("PIDIHS", "Improper Dih.", 4, 3, 3), def_bondedt("TABDIHS", "Tab. Dih.", 4, 2, 2),
-    def_bonded("CMAP", "CMAP Dih.", 5, -1, -1), def_nofc("GB12", "GB 1-2 Pol. (unused)"),
+    def_dihedral("PDIHS", "Proper Dih.", 4, 3, 3), def_dihedral("RBDIHS", "Ryckaert-Bell.", 4, 6, 6),
+    def_dihedral("RESTRDIHS", "Restricted Dih.", 4, 2, 2),
+    def_dihedral("CBTDIHS", "CBT Dih.", 4, 6, 6), def_dihedral("FOURDIHS", "Fourier Dih.", 4, 4, 4),
+    def_dihedral("IDIHS", "Improper Dih.", 4, 2, 2), def_dihedral("PIDIHS", "Improper Dih.", 4, 3, 3),
+    def_dihedral_tabulated("TABDIHS", "Tab. Dih.", 4, 2, 2),
+    def_dihedral("CMAP", "CMAP Dih.", 5, -1, -1), def_nofc("GB12", "GB 1-2 Pol. (unused)"),
     def_nofc("GB13", "GB 1-3 Pol. (unused)"), def_nofc("GB14", "GB 1-4 Pol. (unused)"),
     def_nofc("GBPOL", "GB Polarization (unused)"), def_nofc("NPSOLVATION", "Nonpolar Sol. (unused)"),
-    def_bondedz("LJ14", "LJ-14", 2, 2, 2), def_nofc("COUL14", "Coulomb-14"),
-    def_bondedz("LJC14_Q", "LJC-14 q", 2, 5, 0), def_bondedz("LJC_NB", "LJC Pairs NB", 2, 4, 0),
+    def_pair("LJ14", "LJ-14", 2, 2, 2), def_nofc("COUL14", "Coulomb-14"),
+    def_pair("LJC14_Q", "LJC-14 q", 2, 5, 0), def_pair("LJC_NB", "LJC Pairs NB", 2, 4, 0),
     def_nb("LJ_SR", "LJ (SR)", 2, 2), def_nb("BHAM", "Buck.ham (SR)", 2, 3),
     def_nofc("LJ_LR", "LJ (unused)"), def_nofc("BHAM_LR", "B.ham (unused)"),
     def_nofc("DISPCORR", "Disper. corr."), def_nofc("COUL_SR", "Coulomb (SR)"),
@@ -140,9 +152,10 @@ const t_interaction_function interaction_function[F_NRE] = {
     def_bonded("ANGRESZ", "Angle Rest. Z", 2, 3, 3), def_bonded("DIHRES", "Dih. Rest.", 4, 3, 3),
     def_nofc("DIHRESVIOL", "Dih. Rest. Viol."), /* obsolete */
     def_shkcb("CONSTR", "Constraint", 2, 1, 1), def_shk("CONSTRNC", "Constr. No Conn.", 2, 1, 1),
-    def_shkcb("SETTLE", "Settle", 3, 2, 0), def_vsite("VSITE2", "Virtual site 2", 3, 1),
-    def_vsite("VSITE2FD", "Virtual site 2fd", 3, 1), def_vsite("VSITE3", "Virtual site 3", 4, 2),
-    def_vsite("VSITE3FD", "Virtual site 3fd", 4, 2), def_vsite("VSITE3FAD", "Virtual site 3fad", 4, 2),
+    def_shkcb("SETTLE", "Settle", 3, 2, 0), def_vsite("VSITE1", "Virtual site 1", 2, 0),
+    def_vsite("VSITE2", "Virtual site 2", 3, 1), def_vsite("VSITE2FD", "Virtual site 2fd", 3, 1),
+    def_vsite("VSITE3", "Virtual site 3", 4, 2), def_vsite("VSITE3FD", "Virtual site 3fd", 4, 2),
+    def_vsite("VSITE3FAD", "Virtual site 3fad", 4, 2),
     def_vsite("VSITE3OUT", "Virtual site 3out", 4, 3), def_vsite("VSITE4FD", "Virtual site 4fd", 5, 3),
     def_vsite("VSITE4FDN", "Virtual site 4fdn", 5, 3), def_vsite("VSITEN", "Virtual site N", 2, 2),
     def_nofc("COM_PULL", "COM Pull En."), def_nofc("DENSITYFIT", "Density fitting"),
index 49ead04b1a81830c149a6d2864cdae7de393a2da..0ac9c0c3275133e4c6d496701db8a085b20497ff 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -74,8 +75,10 @@ constexpr unsigned int IF_CONSTRAINT = 1 << 2;
 constexpr unsigned int IF_CHEMBOND   = 1 << 3;
 constexpr unsigned int IF_BTYPE      = 1 << 4;
 constexpr unsigned int IF_ATYPE      = 1 << 5;
-constexpr unsigned int IF_TABULATED  = 1 << 6;
-constexpr unsigned int IF_LIMZERO    = 1 << 7;
+constexpr unsigned int IF_DIHEDRAL   = 1 << 6;
+constexpr unsigned int IF_PAIR       = 1 << 7;
+constexpr unsigned int IF_TABULATED  = 1 << 8;
+constexpr unsigned int IF_LIMZERO    = 1 << 9;
 /* These flags tell to some of the routines what can be done with this
  * item in the list.
  * With IF_BOND a bonded interaction will be calculated.
@@ -187,6 +190,7 @@ enum
     F_CONSTR,
     F_CONSTRNC,
     F_SETTLE,
+    F_VSITE1,
     F_VSITE2,
     F_VSITE2FD,
     F_VSITE3,
index 27b03c4fc67c215ad4feef148cde24a228a9cc63..46dd0653dc44cbdaa43c91905a518bd13ba47203 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index c6f1fb726385c12d3a360eacc9dbafcf73ef2042..638c0b64bce42052933c8d0c1a413f1b8fc8e148 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f6491bbc9c052cc35e716d43734ee51a36311aa4..0c61cc0265f6621558a4d8ed36407f41ec9ba597 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -219,7 +219,7 @@ static inline void mtopGetAtomAndResidueName(const gmx_mtop_t* mtop,
     }
     if (residueNumber != nullptr)
     {
-        if (atoms.nres > mtop->maxres_renum)
+        if (atoms.nres > mtop->maxResiduesPerMoleculeToTriggerRenumber())
         {
             *residueNumber = atoms.resinfo[atoms.atom[atomIndexInMolecule].resind].nr;
         }
index e41d9586d50136eafe2d3460546065e2c21f353b..789a35eced49199550f9f64bb52dc9bcce53028a 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2008,2009,2010,
- * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2008,2009,2010, The GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2016 The GROMACS development team.
  * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
 #include "gromacs/utility/real.h"
 #include "gromacs/utility/smalloc.h"
 
-static int gmx_mtop_maxresnr(const gmx_mtop_t* mtop, int maxres_renum)
-{
-    int maxresnr = 0;
-
-    for (const gmx_moltype_t& moltype : mtop->moltype)
-    {
-        const t_atoms& atoms = moltype.atoms;
-        if (atoms.nres > maxres_renum)
-        {
-            for (int r = 0; r < atoms.nres; r++)
-            {
-                if (atoms.resinfo[r].nr > maxresnr)
-                {
-                    maxresnr = atoms.resinfo[r].nr;
-                }
-            }
-        }
-    }
-
-    return maxresnr;
-}
-
-static void buildMolblockIndices(gmx_mtop_t* mtop)
-{
-    mtop->moleculeBlockIndices.resize(mtop->molblock.size());
-
-    int atomIndex          = 0;
-    int residueIndex       = 0;
-    int residueNumberStart = mtop->maxresnr + 1;
-    int moleculeIndexStart = 0;
-    for (size_t mb = 0; mb < mtop->molblock.size(); mb++)
-    {
-        const gmx_molblock_t& molb         = mtop->molblock[mb];
-        MoleculeBlockIndices& indices      = mtop->moleculeBlockIndices[mb];
-        const int             numResPerMol = mtop->moltype[molb.type].atoms.nres;
-
-        indices.numAtomsPerMolecule = mtop->moltype[molb.type].atoms.nr;
-        indices.globalAtomStart     = atomIndex;
-        indices.globalResidueStart  = residueIndex;
-        atomIndex += molb.nmol * indices.numAtomsPerMolecule;
-        residueIndex += molb.nmol * numResPerMol;
-        indices.globalAtomEnd      = atomIndex;
-        indices.residueNumberStart = residueNumberStart;
-        if (numResPerMol <= mtop->maxres_renum)
-        {
-            residueNumberStart += molb.nmol * numResPerMol;
-        }
-        indices.moleculeIndexStart = moleculeIndexStart;
-        moleculeIndexStart += molb.nmol;
-    }
-}
-
-void gmx_mtop_finalize(gmx_mtop_t* mtop)
-{
-    char* env;
-
-    if (mtop->molblock.size() == 1 && mtop->molblock[0].nmol == 1)
-    {
-        /* We have a single molecule only, no renumbering needed.
-         * This case also covers an mtop converted from pdb/gro/... input,
-         * so we retain the original residue numbering.
-         */
-        mtop->maxres_renum = 0;
-    }
-    else
-    {
-        /* We only renumber single residue molecules. Their intra-molecular
-         * residue numbering is anyhow irrelevant.
-         */
-        mtop->maxres_renum = 1;
-    }
-
-    env = getenv("GMX_MAXRESRENUM");
-    if (env != nullptr)
-    {
-        sscanf(env, "%d", &mtop->maxres_renum);
-    }
-    if (mtop->maxres_renum == -1)
-    {
-        /* -1 signals renumber residues in all molecules */
-        mtop->maxres_renum = INT_MAX;
-    }
-
-    mtop->maxresnr = gmx_mtop_maxresnr(mtop, mtop->maxres_renum);
-
-    buildMolblockIndices(mtop);
-}
-
 void gmx_mtop_count_atomtypes(const gmx_mtop_t* mtop, int state, int typecount[])
 {
     for (int i = 0; i < mtop->ffparams.atnr; ++i)
@@ -195,7 +107,7 @@ AtomIterator::AtomIterator(const gmx_mtop_t& mtop, int globalAtomNumber) :
     mblock_(0),
     atoms_(&mtop.moltype[mtop.molblock[0].type].atoms),
     currentMolecule_(0),
-    highestResidueNumber_(mtop.maxresnr),
+    highestResidueNumber_(mtop.maxResNumberNotRenumbered()),
     localAtomNumber_(0),
     globalAtomNumber_(globalAtomNumber)
 {
@@ -210,7 +122,7 @@ AtomIterator& AtomIterator::operator++()
 
     if (localAtomNumber_ >= atoms_->nr)
     {
-        if (atoms_->nres <= mtop_->maxres_renum)
+        if (atoms_->nres <= mtop_->maxResiduesPerMoleculeToTriggerRenumber())
         {
             /* Single residue molecule, increase the count with one */
             highestResidueNumber_ += atoms_->nres;
@@ -231,23 +143,11 @@ AtomIterator& AtomIterator::operator++()
     return *this;
 }
 
-AtomIterator AtomIterator::operator++(int)
-{
-    AtomIterator temp = *this;
-    ++(*this);
-    return temp;
-}
-
 bool AtomIterator::operator==(const AtomIterator& o) const
 {
     return mtop_ == o.mtop_ && globalAtomNumber_ == o.globalAtomNumber_;
 }
 
-bool AtomIterator::operator!=(const AtomIterator& o) const
-{
-    return !(*this == o);
-}
-
 const t_atom& AtomProxy::atom() const
 {
     return it_->atoms_->atom[it_->localAtomNumber_];
@@ -272,7 +172,7 @@ const char* AtomProxy::residueName() const
 int AtomProxy::residueNumber() const
 {
     int residueIndexInMolecule = it_->atoms_->atom[it_->localAtomNumber_].resind;
-    if (it_->atoms_->nres <= it_->mtop_->maxres_renum)
+    if (it_->atoms_->nres <= it_->mtop_->maxResiduesPerMoleculeToTriggerRenumber())
     {
         return it_->highestResidueNumber_ + 1 + residueIndexInMolecule;
     }
@@ -406,68 +306,6 @@ typedef struct gmx_mtop_ilistloop_all
     int               a_offset;
 } t_gmx_mtop_ilist_all;
 
-gmx_mtop_ilistloop_all_t gmx_mtop_ilistloop_all_init(const gmx_mtop_t* mtop)
-{
-    struct gmx_mtop_ilistloop_all* iloop;
-
-    snew(iloop, 1);
-
-    iloop->mtop     = mtop;
-    iloop->mblock   = 0;
-    iloop->mol      = -1;
-    iloop->a_offset = 0;
-
-    return iloop;
-}
-
-static void gmx_mtop_ilistloop_all_destroy(gmx_mtop_ilistloop_all_t iloop)
-{
-    sfree(iloop);
-}
-
-const InteractionLists* gmx_mtop_ilistloop_all_next(gmx_mtop_ilistloop_all_t iloop, int* atnr_offset)
-{
-
-    if (iloop == nullptr)
-    {
-        gmx_incons(
-                "gmx_mtop_ilistloop_all_next called without calling gmx_mtop_ilistloop_all_init");
-    }
-
-    if (iloop->mol >= 0)
-    {
-        iloop->a_offset += iloop->mtop->moleculeBlockIndices[iloop->mblock].numAtomsPerMolecule;
-    }
-
-    iloop->mol++;
-
-    /* Inter-molecular interactions, if present, are indexed with
-     * iloop->mblock == iloop->mtop->nmolblock, thus we should separately
-     * check for this value in this conditional.
-     */
-    if (iloop->mblock == iloop->mtop->molblock.size()
-        || iloop->mol >= iloop->mtop->molblock[iloop->mblock].nmol)
-    {
-        iloop->mblock++;
-        iloop->mol = 0;
-        if (iloop->mblock >= iloop->mtop->molblock.size())
-        {
-            if (iloop->mblock == iloop->mtop->molblock.size() && iloop->mtop->bIntermolecularInteractions)
-            {
-                *atnr_offset = 0;
-                return iloop->mtop->intermolecular_ilist.get();
-            }
-
-            gmx_mtop_ilistloop_all_destroy(iloop);
-            return nullptr;
-        }
-    }
-
-    *atnr_offset = iloop->a_offset;
-
-    return &iloop->mtop->moltype[iloop->mtop->molblock[iloop->mblock].type].ilist;
-}
-
 int gmx_mtop_ftype_count(const gmx_mtop_t* mtop, int ftype)
 {
     gmx_mtop_ilistloop_t iloop;
@@ -654,10 +492,11 @@ t_atoms gmx_mtop_global_atoms(const gmx_mtop_t* mtop)
 
     init_t_atoms(&atoms, 0, FALSE);
 
-    int maxresnr = mtop->maxresnr;
+    int maxresnr = mtop->maxResNumberNotRenumbered();
     for (const gmx_molblock_t& molb : mtop->molblock)
     {
-        atomcat(&atoms, &mtop->moltype[molb.type].atoms, molb.nmol, mtop->maxres_renum, &maxresnr);
+        atomcat(&atoms, &mtop->moltype[molb.type].atoms, molb.nmol,
+                mtop->maxResiduesPerMoleculeToTriggerRenumber(), &maxresnr);
     }
 
     return atoms;
@@ -667,43 +506,27 @@ t_atoms gmx_mtop_global_atoms(const gmx_mtop_t* mtop)
  * The cat routines below are old code from src/kernel/topcat.c
  */
 
-static void blockacat(t_blocka* dest, const t_blocka* src, int copies, int dnum, int snum)
+static void ilistcat(int ftype, InteractionList* dest, const InteractionList& src, int copies, int dnum, int snum)
 {
-    int i, j, l, size;
-    int destnr  = dest->nr;
-    int destnra = dest->nra;
+    int nral, c, i, a;
 
-    if (src->nr)
-    {
-        size = (dest->nr + copies * src->nr + 1);
-        srenew(dest->index, size);
-    }
-    if (src->nra)
-    {
-        size = (dest->nra + copies * src->nra);
-        srenew(dest->a, size);
-    }
+    nral = NRAL(ftype);
 
-    for (l = destnr, j = 0; (j < copies); j++)
-    {
-        for (i = 0; (i < src->nr); i++)
-        {
-            dest->index[l++] = dest->nra + src->index[i];
-        }
-        dest->nra += src->nra;
-    }
-    for (l = destnra, j = 0; (j < copies); j++)
+    size_t destIndex = dest->iatoms.size();
+    dest->iatoms.resize(dest->iatoms.size() + copies * src.size());
+
+    for (c = 0; c < copies; c++)
     {
-        for (i = 0; (i < src->nra); i++)
+        for (i = 0; i < src.size();)
         {
-            dest->a[l++] = dnum + src->a[i];
+            dest->iatoms[destIndex++] = src.iatoms[i++];
+            for (a = 0; a < nral; a++)
+            {
+                dest->iatoms[destIndex++] = dnum + src.iatoms[i++];
+            }
         }
         dnum += snum;
-        dest->nr += src->nr;
     }
-    dest->index[dest->nr] = dest->nra;
-    dest->nalloc_index    = dest->nr;
-    dest->nalloc_a        = dest->nra;
 }
 
 static void ilistcat(int ftype, t_ilist* dest, const InteractionList& src, int copies, int dnum, int snum)
@@ -729,21 +552,40 @@ static void ilistcat(int ftype, t_ilist* dest, const InteractionList& src, int c
     }
 }
 
-static void set_posres_params(t_idef* idef, const gmx_molblock_t* molb, int i0, int a_offset)
+static const t_iparams& getIparams(const InteractionDefinitions& idef, const int index)
+{
+    return idef.iparams[index];
+}
+
+static const t_iparams& getIparams(const t_idef& idef, const int index)
+{
+    return idef.iparams[index];
+}
+
+static void resizeIParams(std::vector<t_iparams>* iparams, const int newSize)
+{
+    iparams->resize(newSize);
+}
+
+static void resizeIParams(t_iparams** iparams, const int newSize)
+{
+    srenew(*iparams, newSize);
+}
+
+template<typename IdefType>
+static void set_posres_params(IdefType* idef, const gmx_molblock_t* molb, int i0, int a_offset)
 {
-    t_ilist*   il;
     int        i1, i, a_molb;
     t_iparams* ip;
 
-    il                          = &idef->il[F_POSRES];
-    i1                          = il->nr / 2;
-    idef->iparams_posres_nalloc = i1;
-    srenew(idef->iparams_posres, idef->iparams_posres_nalloc);
+    auto* il = &idef->il[F_POSRES];
+    i1       = il->size() / 2;
+    resizeIParams(&idef->iparams_posres, i1);
     for (i = i0; i < i1; i++)
     {
         ip = &idef->iparams_posres[i];
         /* Copy the force constants */
-        *ip    = idef->iparams[il->iatoms[i * 2]];
+        *ip    = getIparams(*idef, il->iatoms[i * 2]);
         a_molb = il->iatoms[i * 2 + 1] - a_offset;
         if (molb->posres_xA.empty())
         {
@@ -769,21 +611,20 @@ static void set_posres_params(t_idef* idef, const gmx_molblock_t* molb, int i0,
     }
 }
 
-static void set_fbposres_params(t_idef* idef, const gmx_molblock_t* molb, int i0, int a_offset)
+template<typename IdefType>
+static void set_fbposres_params(IdefType* idef, const gmx_molblock_t* molb, int i0, int a_offset)
 {
-    t_ilist*   il;
     int        i1, i, a_molb;
     t_iparams* ip;
 
-    il                            = &idef->il[F_FBPOSRES];
-    i1                            = il->nr / 2;
-    idef->iparams_fbposres_nalloc = i1;
-    srenew(idef->iparams_fbposres, idef->iparams_fbposres_nalloc);
+    auto* il = &idef->il[F_FBPOSRES];
+    i1       = il->size() / 2;
+    resizeIParams(&idef->iparams_fbposres, i1);
     for (i = i0; i < i1; i++)
     {
         ip = &idef->iparams_fbposres[i];
         /* Copy the force constants */
-        *ip    = idef->iparams[il->iatoms[i * 2]];
+        *ip    = getIparams(*idef, il->iatoms[i * 2]);
         a_molb = il->iatoms[i * 2 + 1] - a_offset;
         if (molb->posres_xA.empty())
         {
@@ -800,18 +641,15 @@ static void set_fbposres_params(t_idef* idef, const gmx_molblock_t* molb, int i0
     }
 }
 
-/*! \brief Copy idef structure from mtop.
+/*! \brief Copy parameters to idef structure from mtop.
  *
- * Makes a deep copy of an idef data structure from a gmx_mtop_t.
+ * Makes a deep copy of the force field parameters data structure from a gmx_mtop_t.
  * Used to initialize legacy topology types.
  *
  * \param[in] mtop Reference to input mtop.
  * \param[in] idef Pointer to idef to populate.
- * \param[in] mergeConstr Decide if constraints will be merged.
- * \param[in] freeEnergyInteractionsAtEnd Decide if free energy stuff should
- *              be added at the end.
  */
-static void copyIdefFromMtop(const gmx_mtop_t& mtop, t_idef* idef, bool freeEnergyInteractionsAtEnd, bool mergeConstr)
+static void copyFFParametersFromMtop(const gmx_mtop_t& mtop, t_idef* idef)
 {
     const gmx_ffparams_t* ffp = &mtop.ffparams;
 
@@ -840,22 +678,24 @@ static void copyIdefFromMtop(const gmx_mtop_t& mtop, t_idef* idef, bool freeEner
     {
         idef->iparams = nullptr;
     }
-    idef->iparams_posres          = nullptr;
-    idef->iparams_posres_nalloc   = 0;
-    idef->iparams_fbposres        = nullptr;
-    idef->iparams_fbposres_nalloc = 0;
-    idef->fudgeQQ                 = ffp->fudgeQQ;
-    idef->cmap_grid               = new gmx_cmap_t;
-    *idef->cmap_grid              = ffp->cmap_grid;
-    idef->ilsort                  = ilsortUNKNOWN;
-
-    for (int ftype = 0; ftype < F_NRE; ftype++)
-    {
-        idef->il[ftype].nr     = 0;
-        idef->il[ftype].nalloc = 0;
-        idef->il[ftype].iatoms = nullptr;
-    }
+    idef->iparams_posres   = nullptr;
+    idef->iparams_fbposres = nullptr;
+    idef->fudgeQQ          = ffp->fudgeQQ;
+    idef->ilsort           = ilsortUNKNOWN;
+}
 
+/*! \brief Copy idef structure from mtop.
+ *
+ * Makes a deep copy of an idef data structure from a gmx_mtop_t.
+ * Used to initialize legacy topology types.
+ *
+ * \param[in] mtop Reference to input mtop.
+ * \param[in] idef Pointer to idef to populate.
+ * \param[in] mergeConstr Decide if constraints will be merged.
+ */
+template<typename IdefType>
+static void copyIListsFromMtop(const gmx_mtop_t& mtop, IdefType* idef, bool mergeConstr)
+{
     int natoms = 0;
     for (const gmx_molblock_t& molb : mtop.molblock)
     {
@@ -864,11 +704,11 @@ static void copyIdefFromMtop(const gmx_mtop_t& mtop, t_idef* idef, bool freeEner
         int srcnr  = molt.atoms.nr;
         int destnr = natoms;
 
-        int nposre_old   = idef->il[F_POSRES].nr;
-        int nfbposre_old = idef->il[F_FBPOSRES].nr;
+        int nposre_old   = idef->il[F_POSRES].size();
+        int nfbposre_old = idef->il[F_FBPOSRES].size();
         for (int ftype = 0; ftype < F_NRE; ftype++)
         {
-            if (mergeConstr && ftype == F_CONSTR && molt.ilist[F_CONSTRNC].size() > 0)
+            if (mergeConstr && ftype == F_CONSTR && !molt.ilist[F_CONSTRNC].empty())
             {
                 /* Merge all constrains into one ilist.
                  * This simplifies the constraint code.
@@ -886,13 +726,13 @@ static void copyIdefFromMtop(const gmx_mtop_t& mtop, t_idef* idef, bool freeEner
                 ilistcat(ftype, &idef->il[ftype], molt.ilist[ftype], molb.nmol, destnr, srcnr);
             }
         }
-        if (idef->il[F_POSRES].nr > nposre_old)
+        if (idef->il[F_POSRES].size() > nposre_old)
         {
             /* Executing this line line stops gmxdump -sys working
              * correctly. I'm not aware there's an elegant fix. */
             set_posres_params(idef, &molb, nposre_old / 2, natoms);
         }
-        if (idef->il[F_FBPOSRES].nr > nfbposre_old)
+        if (idef->il[F_FBPOSRES].size() > nfbposre_old)
         {
             set_fbposres_params(idef, &molb, nfbposre_old / 2, natoms);
         }
@@ -908,23 +748,8 @@ static void copyIdefFromMtop(const gmx_mtop_t& mtop, t_idef* idef, bool freeEner
         }
     }
 
-    if (freeEnergyInteractionsAtEnd && gmx_mtop_bondeds_free_energy(&mtop))
-    {
-        std::vector<real> qA(mtop.natoms);
-        std::vector<real> qB(mtop.natoms);
-        for (const AtomProxy atomP : AtomRange(mtop))
-        {
-            const t_atom& local = atomP.atom();
-            int           index = atomP.globalAtomNumber();
-            qA[index]           = local.q;
-            qB[index]           = local.qB;
-        }
-        gmx_sort_ilist_fe(idef, qA.data(), qB.data());
-    }
-    else
-    {
-        idef->ilsort = ilsortNO_FE;
-    }
+    // We have not (yet) sorted free-energy interactions to the end of the ilists
+    idef->ilsort = ilsortNO_FE;
 }
 
 /*! \brief Copy atomtypes from mtop
@@ -950,29 +775,28 @@ static void copyAtomtypesFromMtop(const gmx_mtop_t& mtop, t_atomtypes* atomtypes
     }
 }
 
-/*! \brief Copy excls from mtop.
- *
- * Makes a deep copy of excls(t_blocka) from gmx_mtop_t.
- * Used to initialize legacy topology types.
+/*! \brief Generate a single list of lists of exclusions for the whole system
  *
  * \param[in] mtop  Reference to input mtop.
- * \param[in] excls Pointer to final excls data structure.
  */
-static void copyExclsFromMtop(const gmx_mtop_t& mtop, t_blocka* excls)
+static gmx::ListOfLists<int> globalExclusionLists(const gmx_mtop_t& mtop)
 {
-    init_blocka(excls);
-    int natoms = 0;
+    gmx::ListOfLists<int> excls;
+
+    int atomIndex = 0;
     for (const gmx_molblock_t& molb : mtop.molblock)
     {
         const gmx_moltype_t& molt = mtop.moltype[molb.type];
 
-        int srcnr  = molt.atoms.nr;
-        int destnr = natoms;
-
-        blockacat(excls, &molt.excls, molb.nmol, destnr, srcnr);
+        for (int mol = 0; mol < molb.nmol; mol++)
+        {
+            excls.appendListOfLists(molt.excls, atomIndex);
 
-        natoms += molb.nmol * srcnr;
+            atomIndex += molt.atoms.nr;
+        }
     }
+
+    return excls;
 }
 
 /*! \brief Updates inter-molecular exclusion lists
@@ -983,21 +807,21 @@ static void copyExclsFromMtop(const gmx_mtop_t& mtop, t_blocka* excls)
  * \param[inout]    excls   existing exclusions in local topology
  * \param[in]       ids     list of global IDs of atoms
  */
-static void addMimicExclusions(t_blocka* excls, const gmx::ArrayRef<const int> ids)
+static void addMimicExclusions(gmx::ListOfLists<int>* excls, const gmx::ArrayRef<const int> ids)
 {
     t_blocka inter_excl{};
     init_blocka(&inter_excl);
     size_t n_q = ids.size();
 
-    inter_excl.nr  = excls->nr;
+    inter_excl.nr  = excls->ssize();
     inter_excl.nra = n_q * n_q;
 
     size_t total_nra = n_q * n_q;
 
-    snew(inter_excl.index, excls->nr + 1);
+    snew(inter_excl.index, excls->ssize() + 1);
     snew(inter_excl.a, total_nra);
 
-    for (int i = 0; i < excls->nr; ++i)
+    for (int i = 0; i < inter_excl.nr; ++i)
     {
         inter_excl.index[i] = 0;
     }
@@ -1029,21 +853,38 @@ static void addMimicExclusions(t_blocka* excls, const gmx::ArrayRef<const int> i
 
     inter_excl.index[inter_excl.nr] = n_q * n_q;
 
-    std::vector<gmx::ExclusionBlock> qmexcl2(excls->nr);
+    std::vector<gmx::ExclusionBlock> qmexcl2(excls->size());
     gmx::blockaToExclusionBlocks(&inter_excl, qmexcl2);
 
     // Merge the created exclusion list with the existing one
     gmx::mergeExclusions(excls, qmexcl2);
 }
 
+static void sortFreeEnergyInteractionsAtEnd(const gmx_mtop_t& mtop, InteractionDefinitions* idef)
+{
+    std::vector<real> qA(mtop.natoms);
+    std::vector<real> qB(mtop.natoms);
+    for (const AtomProxy atomP : AtomRange(mtop))
+    {
+        const t_atom& local = atomP.atom();
+        int           index = atomP.globalAtomNumber();
+        qA[index]           = local.q;
+        qB[index]           = local.qB;
+    }
+    gmx_sort_ilist_fe(idef, qA.data(), qB.data());
+}
+
 static void gen_local_top(const gmx_mtop_t& mtop,
                           bool              freeEnergyInteractionsAtEnd,
                           bool              bMergeConstr,
                           gmx_localtop_t*   top)
 {
-    copyAtomtypesFromMtop(mtop, &top->atomtypes);
-    copyIdefFromMtop(mtop, &top->idef, freeEnergyInteractionsAtEnd, bMergeConstr);
-    copyExclsFromMtop(mtop, &top->excls);
+    copyIListsFromMtop(mtop, &top->idef, bMergeConstr);
+    if (freeEnergyInteractionsAtEnd)
+    {
+        sortFreeEnergyInteractionsAtEnd(mtop, &top->idef);
+    }
+    top->excls = globalExclusionLists(mtop);
     if (!mtop.intermolecularExclusionGroup.empty())
     {
         addMimicExclusions(&top->excls, mtop.intermolecularExclusionGroup);
@@ -1093,6 +934,32 @@ gmx::RangePartitioning gmx_mtop_molecules(const gmx_mtop_t& mtop)
     return mols;
 }
 
+std::vector<gmx::Range<int>> atomRangeOfEachResidue(const gmx_moltype_t& moltype)
+{
+    std::vector<gmx::Range<int>> atomRanges;
+    int                          currentResidueNumber = moltype.atoms.atom[0].resind;
+    int                          startAtom            = 0;
+    // Go through all atoms in a molecule to store first and last atoms in each residue.
+    for (int i = 0; i < moltype.atoms.nr; i++)
+    {
+        int residueOfThisAtom = moltype.atoms.atom[i].resind;
+        if (residueOfThisAtom != currentResidueNumber)
+        {
+            // This atom belongs to the next residue, so record the range for the previous residue,
+            // remembering that end points to one place past the last atom.
+            int endAtom = i;
+            atomRanges.emplace_back(startAtom, endAtom);
+            // Prepare for the current residue
+            startAtom            = endAtom;
+            currentResidueNumber = residueOfThisAtom;
+        }
+    }
+    // special treatment for last residue in this molecule.
+    atomRanges.emplace_back(startAtom, moltype.atoms.nr);
+
+    return atomRanges;
+}
+
 /*! \brief Creates and returns a deprecated t_block struct with molecule indices
  *
  * \param[in] mtop  The global topology
@@ -1110,14 +977,17 @@ static t_block gmx_mtop_molecules_t_block(const gmx_mtop_t& mtop)
     return mols;
 }
 
-static void gen_t_topology(const gmx_mtop_t& mtop,
-                           bool              freeEnergyInteractionsAtEnd,
-                           bool              bMergeConstr,
-                           t_topology*       top)
+static void gen_t_topology(const gmx_mtop_t& mtop, bool bMergeConstr, t_topology* top)
 {
     copyAtomtypesFromMtop(mtop, &top->atomtypes);
-    copyIdefFromMtop(mtop, &top->idef, freeEnergyInteractionsAtEnd, bMergeConstr);
-    copyExclsFromMtop(mtop, &top->excls);
+    for (int ftype = 0; ftype < F_NRE; ftype++)
+    {
+        top->idef.il[ftype].nr     = 0;
+        top->idef.il[ftype].nalloc = 0;
+        top->idef.il[ftype].iatoms = nullptr;
+    }
+    copyFFParametersFromMtop(mtop, &top->idef);
+    copyIListsFromMtop(mtop, &top->idef, bMergeConstr);
 
     top->name                        = mtop.name;
     top->atoms                       = gmx_mtop_global_atoms(&mtop);
@@ -1130,7 +1000,7 @@ t_topology gmx_mtop_t_to_t_topology(gmx_mtop_t* mtop, bool freeMTop)
 {
     t_topology top;
 
-    gen_t_topology(*mtop, false, false, &top);
+    gen_t_topology(*mtop, false, &top);
 
     if (freeMTop)
     {
@@ -1178,5 +1048,58 @@ void convertAtomsToMtop(t_symtab* symtab, char** name, t_atoms* atoms, gmx_mtop_
 
     mtop->haveMoleculeIndices = false;
 
-    gmx_mtop_finalize(mtop);
+    mtop->finalize();
+}
+
+bool haveFepPerturbedNBInteractions(const gmx_mtop_t& mtop)
+{
+    for (const gmx_moltype_t& molt : mtop.moltype)
+    {
+        for (int a = 0; a < molt.atoms.nr; a++)
+        {
+            if (PERTURBED(molt.atoms.atom[a]))
+            {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+bool haveFepPerturbedMasses(const gmx_mtop_t& mtop)
+{
+    for (const gmx_moltype_t& molt : mtop.moltype)
+    {
+        for (int a = 0; a < molt.atoms.nr; a++)
+        {
+            const t_atom& atom = molt.atoms.atom[a];
+            if (atom.m != atom.mB)
+            {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+bool havePerturbedConstraints(const gmx_mtop_t& mtop)
+{
+    // This code assumes that all perturbed constraints parameters are actually used
+    const auto& ffparams = mtop.ffparams;
+
+    for (gmx::index i = 0; i < gmx::ssize(ffparams.functype); i++)
+    {
+        if (ffparams.functype[i] == F_CONSTR || ffparams.functype[i] == F_CONSTRNC)
+        {
+            const auto& iparams = ffparams.iparams[i];
+            if (iparams.constr.dA != iparams.constr.dB)
+            {
+                return true;
+            }
+        }
+    }
+
+    return false;
 }
index d4a451f632a54740271acaaf96f38e8671e1019c..7d0527e654cfa4f844f39acb30ed06111e7b1a91 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2016,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -42,6 +43,8 @@
 #include <array>
 #include <vector>
 
+#include <boost/stl_interfaces/iterator_interface.hpp>
+
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/basedefinitions.h"
 
@@ -50,18 +53,11 @@ struct t_atom;
 struct t_atoms;
 struct t_block;
 struct t_symtab;
-enum struct GmxQmmmMode;
 
 // TODO All of the functions taking a const gmx_mtop * are deprecated
 // and should be replaced by versions taking const gmx_mtop & when
 // their callers are refactored similarly.
 
-/* Should be called after generating or reading mtop,
- * to set some compute intesive variables to avoid
- * N^2 operations later on.
- */
-void gmx_mtop_finalize(gmx_mtop_t* mtop);
-
 /* Counts the number of atoms of each type. State should be 0 for
  * state A and 1 for state B types.  typecount should have at
  * least mtop->ffparams.atnr elements.
@@ -104,43 +100,28 @@ private:
     const AtomIterator* it_;
 };
 
-//! Wrapper around proxy object to implement operator->
-template<typename T>
-class ProxyPtr
-{
-public:
-    //! Construct with proxy object.
-    ProxyPtr(T t) : t_(t) {}
-    //! Member of pointer operator.
-    T* operator->() { return &t_; }
-
-private:
-    T t_;
-};
-
 /*! \brief
  * Object that allows looping over all atoms in an mtop.
  */
-class AtomIterator
+class AtomIterator :
+    public boost::stl_interfaces::proxy_iterator_interface<AtomIterator, std::forward_iterator_tag, t_atom, AtomProxy>
 {
+    using Base =
+            boost::stl_interfaces::proxy_iterator_interface<AtomIterator, std::forward_iterator_tag, t_atom, AtomProxy>;
+
 public:
     //! Construct from topology and optionalally a global atom number.
     explicit AtomIterator(const gmx_mtop_t& mtop, int globalAtomNumber = 0);
 
     //! Prefix increment.
     AtomIterator& operator++();
-    //! Postfix increment.
-    AtomIterator operator++(int);
+    using Base::  operator++;
 
     //! Equality comparison.
     bool operator==(const AtomIterator& o) const;
-    //! Non-equal comparison.
-    bool operator!=(const AtomIterator& o) const;
 
     //! Dereference operator. Returns proxy.
     AtomProxy operator*() const { return { this }; }
-    //! Member of pointer operator.
-    ProxyPtr<AtomProxy> operator->() const { return { this }; }
 
 private:
     //! Global topology.
@@ -216,25 +197,6 @@ gmx_mtop_ilistloop_t gmx_mtop_ilistloop_init(const gmx_mtop_t& mtop);
  */
 const InteractionLists* gmx_mtop_ilistloop_next(gmx_mtop_ilistloop_t iloop, int* nmol);
 
-/* Abstract type for ilist loop over all ilists of all molecules */
-typedef struct gmx_mtop_ilistloop_all* gmx_mtop_ilistloop_all_t;
-
-/* Initialize an ilist loop over all molecule types in the system.
- * Only use this when you really need to loop over all molecules,
- * i.e. when you use groups which might differ per molecule,
- * otherwise use gmx_mtop_ilistloop.
- */
-gmx_mtop_ilistloop_all_t gmx_mtop_ilistloop_all_init(const gmx_mtop_t* mtop);
-
-/* Loop to the next molecule,
- * When not at the end:
- *   returns a valid pointer to the next array ilist_mol[F_NRE],
- *   writes the atom offset which should be added to iatoms in atnr_offset.
- * When at the end, destroys iloop and returns nullptr.
- */
-const InteractionLists* gmx_mtop_ilistloop_all_next(gmx_mtop_ilistloop_all_t iloop, int* atnr_offset);
-
-
 /* Returns the total number of interactions in the system of type ftype */
 int gmx_mtop_ftype_count(const gmx_mtop_t* mtop, int ftype);
 
@@ -273,6 +235,15 @@ void gmx_mtop_generate_local_top(const gmx_mtop_t& mtop, gmx_localtop_t* top, bo
  */
 gmx::RangePartitioning gmx_mtop_molecules(const gmx_mtop_t& mtop);
 
+/*! \brief
+ * Returns the index range from residue begin to end for each residue in a molecule block.
+ *
+ * Note that residues will always have consecutive atoms numbers internally.
+ *
+ * \param[in] moltype  Molecule Type to parse for start and end.
+ * \returns Vector of ranges for all residues.
+ */
+std::vector<gmx::Range<int>> atomRangeOfEachResidue(const gmx_moltype_t& moltype);
 
 /* Converts a gmx_mtop_t struct to t_topology.
  *
@@ -306,4 +277,13 @@ std::vector<int> get_atom_index(const gmx_mtop_t* mtop);
  */
 void convertAtomsToMtop(t_symtab* symtab, char** name, t_atoms* atoms, gmx_mtop_t* mtop);
 
+//! Checks and returns whether non-bonded interactions are perturbed for free-energy calculations
+bool haveFepPerturbedNBInteractions(const gmx_mtop_t& mtop);
+
+//! Checks whether masses are perturbed for free-energy calculations
+bool haveFepPerturbedMasses(const gmx_mtop_t& mtop);
+
+//! Checks whether constraints are perturbed for free-energy calculations
+bool havePerturbedConstraints(const gmx_mtop_t& mtop);
+
 #endif
index 4c2577499b7950a46a7d520c2603627c9bc08fc5..73aad6d57f7f7dd9bf4b0b4f27c2620eef0af61c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -41,9 +42,9 @@
 
 #include <algorithm>
 #include <iterator>
+#include <optional>
 #include <string>
 
-#include "gromacs/compat/optional.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/fatalerror.h"
@@ -110,14 +111,14 @@ ResidueType::~ResidueType() {}
  * \param[in] residueName Name of a residue to compare to database.
  * \returns An optional iterator to the residue entry that was found.
  */
-static gmx::compat::optional<gmx::ArrayRef<const ResidueTypeEntry>::const_iterator>
+static std::optional<gmx::ArrayRef<const ResidueTypeEntry>::const_iterator>
 findResidueEntryWithName(gmx::ArrayRef<const ResidueTypeEntry> entries, const std::string& residueName)
 {
     auto foundIt =
             std::find_if(entries.begin(), entries.end(), [&residueName](const ResidueTypeEntry& old) {
                 return gmx::equalCaseInsensitive(residueName, old.residueName);
             });
-    return (foundIt != entries.end()) ? gmx::compat::make_optional(foundIt) : gmx::compat::nullopt;
+    return (foundIt != entries.end()) ? std::make_optional(foundIt) : std::nullopt;
 }
 
 bool ResidueType::nameIndexedInResidueTypes(const std::string& residueName)
@@ -179,8 +180,8 @@ std::string ResidueType::typeOfNamedDatabaseResidue(const std::string& residueNa
     return foundIt ? (*foundIt)->residueType : c_undefinedResidueType;
 }
 
-gmx::compat::optional<std::string> ResidueType::optionalTypeOfNamedDatabaseResidue(const std::string& residueName)
+std::optional<std::string> ResidueType::optionalTypeOfNamedDatabaseResidue(const std::string& residueName)
 {
     auto foundIt = findResidueEntryWithName(impl_->entry, residueName);
-    return foundIt ? gmx::compat::make_optional((*foundIt)->residueType) : gmx::compat::nullopt;
+    return foundIt ? std::make_optional((*foundIt)->residueType) : std::nullopt;
 }
index 588617502fe30db80ad92e7bcf134df494ec3b1d..104101d4b777ea66dbcf7d87b27b21eff1ec7d8f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2014,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -35,9 +35,9 @@
 #ifndef GMX_TOPOLOGY_RESIDUETYPES_H
 #define GMX_TOPOLOGY_RESIDUETYPES_H
 
+#include <optional>
 #include <string>
 
-#include "gromacs/compat/optional.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/classhelpers.h"
 
@@ -105,7 +105,7 @@ public:
      * \param[in] residueName Name of the residue to search for.
      * \returns An optional containing the residue type of any matching residue
      */
-    gmx::compat::optional<std::string> optionalTypeOfNamedDatabaseResidue(const std::string& residueName);
+    std::optional<std::string> optionalTypeOfNamedDatabaseResidue(const std::string& residueName);
 
 private:
     //! Implementation pointer.
index ff3d3109d00be0a6081796cb4119eb82750b6ed9..d7432ebfaf001eb09be2c4a6fc05f1e0b9fdac3d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+/*! \internal \file
+ * \brief
+ * Implements new and legacy symbol table routines.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \ingroup module_topology
+ */
 #include "gmxpre.h"
 
 #include "symtab.h"
 
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/iserializer.h"
 #include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/stringutil.h"
 #include "gromacs/utility/txtdump.h"
 
-constexpr int c_trimSize   = 1024;
+StringTableEntry StringTableBuilder::addString(const std::string& theString)
+{
+    int         size     = map_.size();
+    std::string stripped = gmx::stripString(theString);
+
+    const auto foundEntry = map_.insert(StringTablePair(stripped, size));
+    return StringTableEntry(foundEntry.first->first, foundEntry.first->second);
+}
+
+int StringTableBuilder::findEntryByName(const std::string& name) const
+{
+    auto foundEntry = map_.find(name);
+    if (foundEntry != map_.end())
+    {
+        return foundEntry->second;
+    }
+    else
+    {
+        GMX_THROW(gmx::InternalError(
+                gmx::formatString("Could not find string \"%s\" in SymbolTable", name.c_str())));
+    }
+}
+
+StringTable StringTableBuilder::build()
+{
+    std::vector<std::string> table(map_.size());
+    for (const auto& entry : map_)
+    {
+        table[entry.second] = entry.first;
+    }
+    map_.clear();
+    return StringTable(table);
+}
+
+void StringTable::printStringTableStorageToFile(FILE* fp, int indent, const char* title) const
+{
+    indent = pr_title_n(fp, indent, title, table_.size());
+    int i  = 0;
+    for (const auto& entry : table_)
+    {
+        pr_indent(fp, indent);
+        fprintf(fp, "%s[%d]=\"%s\"\n", title, i++, entry.c_str());
+    }
+}
+
+StringTable::StringTable(gmx::ISerializer* serializer)
+{
+    GMX_RELEASE_ASSERT(serializer->reading(),
+                       "Can not use writing serializer to read string table");
+    int nr = 0;
+    serializer->doInt(&nr);
+    table_.resize(nr);
+    for (auto& entry : table_)
+    {
+        serializer->doString(&entry);
+    }
+}
+
+void StringTable::serializeStringTable(gmx::ISerializer* serializer)
+{
+    GMX_RELEASE_ASSERT(!serializer->reading(),
+                       "Can not use reading serializer to write string table");
+    int nr = table_.size();
+    serializer->doInt(&nr);
+    for (auto& entry : table_)
+    {
+        serializer->doString(&entry);
+    }
+}
+
+StringTableEntry StringTable::at(gmx::index index) const
+{
+    if (index >= gmx::ssize(table_))
+    {
+        GMX_THROW(gmx::InternalError("Can't read beyond last entry"));
+    }
+    return StringTableEntry(table_[index], index);
+}
+
+StringTableEntry StringTable::operator[](gmx::index index) const
+{
+    GMX_ASSERT(index < gmx::ssize(table_), "Can't read beyond last entry");
+    return StringTableEntry(table_[index], index);
+}
+
+void StringTableEntry::serialize(gmx::ISerializer* serializer) const
+{
+    GMX_RELEASE_ASSERT(!serializer->reading(),
+                       "Can not use reading serializer to write string index");
+    int entry = tableIndex_;
+    serializer->doInt(&entry);
+}
+
+StringTableEntry readStringTableEntry(gmx::ISerializer* serializer, const StringTable& table)
+{
+    GMX_RELEASE_ASSERT(serializer->reading(),
+                       "Can not use writing serializer to read string index");
+    int entry = 0;
+    serializer->doInt(&entry);
+    return table.at(entry);
+}
+
+void StringTable::copyToLegacySymtab(struct t_symtab* symtab) const
+{
+    for (const auto& entry : table_)
+    {
+        put_symtab(symtab, entry.c_str());
+    }
+}
+
+// Old code for legacy data structure starts below.
+//! Maximum size of character string in table.
+constexpr int c_trimSize = 1024;
+//! Maximum number of entries in each element of the linked list.
 constexpr int c_maxBufSize = 5;
 
-static char* trim_string(const char* s, char* out, int maxlen)
-/*
- * Returns a pointer to a static area which contains a copy
- * of s without leading or trailing spaces. Strings are
- * truncated to c_trimSize positions.
+/*! \brief
+ * Remove leading and trailing whitespace from string and enforce maximum length.
  *
- * TODO This partially duplicates code in trim(), but perhaps
- * replacing symtab with a std::map is a better fix.
+ * \param[in]    s      String to trim.
+ * \param[inout] out    String to return.
+ * \param[in]    maxlen Maximum string length to use.
+ * \returns New pruned string.
  */
+static char* trim_string(const char* s, char* out, int maxlen)
 {
     int len, i;
 
@@ -132,6 +257,7 @@ char** get_symtab_handle(t_symtab* symtab, int name)
     gmx_fatal(FARGS, "symtab get_symtab_handle %d not found", name);
 }
 
+//! Returns a new initialized entry into the symtab linked list.
 static t_symbuf* new_symbuf()
 {
     t_symbuf* symbuf;
@@ -144,6 +270,13 @@ static t_symbuf* new_symbuf()
     return symbuf;
 }
 
+/*! \brief
+ * Low level function to enter new string into legacy symtab.
+ *
+ * \param[inout] symtab Symbol table to add entry to.
+ * \param[in]    name   New string to add to symtab.
+ * \returns Pointer to new entry in the legacy symbol table, or to existing entry if it already existed.
+ */
 static char** enter_buf(t_symtab* symtab, char* name)
 {
     int       i;
index cfb1678e4fd8cf10a7e0ed8efdb5db81d8fc094e..826fb86eb37284d5d9eb773279ee3048c140e427 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2010,2014,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2014,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+/*! \file
+ * \brief
+ * Declares modern and legacy symbol table used to store strings of characters.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ *
+ * \ingroup module_topology
+ * \inlibraryapi
+ */
 #ifndef GMX_TOPOLOGY_SYMTAB_H
 #define GMX_TOPOLOGY_SYMTAB_H
 
 #include <stdio.h>
 
-typedef struct t_symbuf
+#include <functional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "gromacs/utility/gmxassert.h"
+
+struct t_commrec;
+struct t_fileio;
+
+namespace gmx
 {
-    int              bufsize;
-    char**           buf;
+class ISerializer;
+namespace test
+{
+class StringTableTest;
+} // namespace test
+} // namespace gmx
+
+//! Convenience typedef for pair stored in map.
+using StringTablePair = std::pair<std::string, int>;
+//! Convenience typedef for string reference wrapper.
+using StringReference = std::reference_wrapper<const std::string>;
+
+class StringTableBuilder;
+class StringTableEntry;
+/*! \brief
+ * A class to store strings for lookup.
+ *
+ * We store the strings in a dedicated object to avoid
+ * wrong usage of the flat string vector, and forcing people
+ * to use an object that can only be constructed from the transitional
+ * StringTableBuilder or filled during file IO.
+ *
+ * Note that strings are stripped of trailing and leading whitespace.
+ */
+class StringTable
+{
+public:
+    //! Constructor used to generate table object from file reading.
+    StringTable(gmx::ISerializer* serializer);
+    //! Can move construct.
+    StringTable(StringTable&&) = default;
+    //! Can move assign.
+    StringTable& operator=(StringTable&&) = default;
+    //! No copy constructor.
+    StringTable(const StringTable&) = delete;
+    //! No copy assign.
+    StringTable& operator=(const StringTable&) = delete;
+    /*! \brief
+     *  Access string at \p index.
+     *
+     *  \returns Entry type that constains both the string and the index,
+     *           with the index needed during (de-)serialization.
+     *  \throws  On index being out of range.
+     */
+    StringTableEntry at(gmx::index index) const;
+    //! Bracket operator.
+    StringTableEntry operator[](gmx::index index) const;
+    //! Handle file IO.
+    void serializeStringTable(gmx::ISerializer* serializer);
+
+    //! Print human readable format of storage.
+    void printStringTableStorageToFile(FILE* fp, int indent, const char* title) const;
+
+    /*! \brief
+     * Copy data in new datastructure to legacy version.
+     *
+     * The legacy datastructures need to be already initialized.
+     *
+     * \param[in] symtab Legacy symbol table to add entries to.
+     */
+    void copyToLegacySymtab(struct t_symtab* symtab) const;
+
+    friend class StringTableBuilder;
+
+private:
+    /*! \brief
+     * Private constructor so that only builder can create the final table.
+     *
+     * \param[in] table A vector of strings to be stored in the table.
+     */
+    StringTable(const std::vector<std::string>& table) : table_(table) {}
+
+    //! The table is stored as a vector of strings.
+    std::vector<std::string> table_;
+};
+
+/*! \brief
+ * Helper class to access members in StringTable.
+ *
+ * This class is a wrapper around a string reference to access
+ * the actual entry in the table, as well as an index used for
+ * serializing the datastructure.
+ *
+ * This also provides efficient comparison calls between different entries.
+ */
+class StringTableEntry
+{
+public:
+    //! Copy construct.
+    StringTableEntry(const StringTableEntry&) = default;
+    //! Move construct.
+    StringTableEntry(StringTableEntry&&) noexcept = default;
+    //! Copy assign.
+    StringTableEntry& operator=(const StringTableEntry&) = default;
+    //! Move assign.
+    StringTableEntry& operator=(StringTableEntry&&) = default;
+
+    //! Compare entries by indices. Same string should always have same index.
+    bool operator==(const StringTableEntry& o) const { return tableIndex_ == o.tableIndex_; }
+    //! Unequal comparisson.
+    bool operator!=(const StringTableEntry& o) const { return !(*this == o); }
+    //! Access to underlying view.
+    const std::string& operator*() const { return entry_; }
+    //! Access to underlying view.
+    const std::string* operator->() const { return &entry_.get(); }
+    //! Serialize index.
+    void serialize(gmx::ISerializer* serializer) const;
+
+    // We only allow construction from the places that are known to create
+    // valid objects for us.
+    friend StringTableEntry readStringTableEntry(gmx::ISerializer* serializer, const StringTable& table);
+    friend class StringTableBuilder;
+    friend class StringTable;
+
+private:
+    //! Only allow construct with all information present.
+    StringTableEntry(StringReference entry, int tableIndex) : entry_(entry), tableIndex_(tableIndex)
+    {
+    }
+    //! The actual string reference that is stored.
+    StringReference entry_;
+    //! The index into the table.
+    int tableIndex_ = -1;
+};
+
+/*! \brief
+ * De-serialize StringTableEntry using the index into the \p table.
+ *
+ * \param[in] serializer  The object containing the serialized index.
+ * \param[in] table       The storage object holding all strings.
+ * \returns The entry into the Table as StringTableEntry.
+ */
+StringTableEntry readStringTableEntry(gmx::ISerializer* serializer, const StringTable& table);
+
+/*! \libinternal \brief
+ * Builds a memory efficient storage for strings of characters.
+ *
+ * Allows storing strings of characters with unique entries.
+ */
+class StringTableBuilder
+{
+public:
+    /*! \brief
+     * Place new unique string in storage object.
+     *
+     * Enters new string into the underlying storage or recovers existing entry.
+     * \param[in] theString New string to enter.
+     * \returns New entry object with reference to string and index into storage.
+     *          The reference is only valid while the builder is in use, and becomes
+     *          invalidated when generating the StringTable.
+     */
+    StringTableEntry addString(const std::string& theString);
+    //! Find matching entry in storage by name as string.
+    int findEntryByName(const std::string& name) const;
+    /*! \brief
+     * Build the StringTable from the internal map of strings.
+     *
+     * The unique indices returned from addString() can be used
+     * to index into the returned StringTable. Clears the
+     * temporary storage so that the StringTableBuilder can be re-used to
+     * build a distinct StringTable.
+     */
+    StringTable build();
+
+private:
+    //! Storage object for entries.
+    std::unordered_map<std::string, int> map_;
+};
+
+// Below this is the legacy code for the old symbol table, only used in
+// deprecated datastructures.
+/*! \libinternal \brief
+ * Legacy symbol table entry as linked list.
+ */
+struct t_symbuf
+{
+    //! Number of entries in this item
+    int bufsize;
+    //! Storage for strings in this item.
+    char** buf;
+    //! Next item in linked list.
     struct t_symbuf* next;
-} t_symbuf;
+};
 
-typedef struct t_symtab
+/* \libinternal \brief
+ * Legacy symbol table.
+ */
+struct t_symtab
 {
-    int       nr;
+    //! Total number of entries stored.
+    int nr;
+    //! First item in linked list of storage elements.
     t_symbuf* symbuf;
-} t_symtab;
+};
 
 /*
  * This module handles symbol table manipulation. All text strings
@@ -63,64 +267,74 @@ typedef struct t_symtab
  * back to a text string handle by get_symtab_handle().
  */
 
+//! Initialises the symbol table symtab.
 void open_symtab(t_symtab* symtab);
-/* Initialises the symbol table symtab.
- */
 
-void close_symtab(t_symtab* symtab);
-/* Undoes the effect of open_symtab(), after invoking this function,
- * no value can be added to the symbol table, only values can be
- * retrieved using get_symtab().
+/*! \brief
+ * Undoes the effect of open_symtab()
+ *
+ * After invoking this function, no value can be added to the
+ * symbol table, only values can be retrieved using get_symtab_handle().
  *
  * Note that this does no work.
+ * \param[inout] symtab Symbol table to close.
  */
+void close_symtab(t_symtab* symtab);
 
 /*! \brief Returns a deep copy of \c symtab. */
 t_symtab* duplicateSymtab(const t_symtab* symtab);
 
+//! Frees the space allocated by the symbol table itself.
 void free_symtab(t_symtab* symtab);
-/* Frees the space allocated by the symbol table itself */
 
+//! Frees the space allocated by the symbol table, including all entries in it.
 void done_symtab(t_symtab* symtab);
-/* Frees the space allocated by the symbol table, including all
- * entries in it */
-
-char** put_symtab(t_symtab* symtab, const char* name);
-/* Enters a string into the symbol table symtab, if it was not
- * available, a reference to a copy is returned else a reference
- * to the earlier entered value is returned. Strings are trimmed
- * of spaces.
- */
 
-int lookup_symtab(t_symtab* symtab, char** name);
-/* Returns a unique handle for **name, without a memory reference.
- * It is a failure when name cannot be found in the symbol table,
- * it should be entered before with put_symtab().
+/*! \brief
+ * Enters a string into the symbol table.
+ *
+ * If the string \p name was not present before, a reference to a copy is returned,
+ * else a reference to the earlier entered value is returned. Strings are trimmed of spaces.
+ *
+ * \param[inout] symtab Symbol table to add string to.
+ * \param[in] name String to add.
+ * \returns Pointer to entry of string in symtab.
  */
+char** put_symtab(t_symtab* symtab, const char* name);
 
-char** get_symtab_handle(t_symtab* symtab, int name);
-/* Returns a text string handle for name. Name should be a value
- * returned from lookup_symtab(). So get_symtab_handle() and
- * lookup_symtab() are inverse functions.
+/*! \brief
+ * Returns unique handle for \p name.
+ *
+ * Looks up the string pointer \p name in the symbol table and returns the
+ * index in it to the matching entry. Gives fatal error if \p name is
+ * not found. \p name has to be entered first using put_symtab().
+ *
+ * \param[in] symtab Symbol table to search.
+ * \param[in] name String pointer into \p symtab.
+ * \returns Unique index to position in symbol table.
  */
+int lookup_symtab(t_symtab* symtab, char** name);
 
-long wr_symtab(FILE* fp, t_symtab* symtab);
-/* Writes the symbol table symtab to the file, specified by fp.
- * The function returns the number of bytes written.
+/*! \brief
+ * Returns text string corresponding to \p index.
+ *
+ * \p index needs to be value obtained from call to lookup_symtab().
+ * get_symtab_handle() and lookup_symtab() are inverse functions.
+ *
+ * \param[in] symtab Symbol table to search.
+ * \param[in] index  Entry to find in table.
+ * \returns String pointer into \p symtab corresponding to the entry.
  */
+char** get_symtab_handle(t_symtab* symtab, int index);
 
-long rd_symtab(FILE* fp, t_symtab* symtab);
-/* Reads the symbol table symtab from the file, specified by fp.
- * This will include allocating the needed space. The function
- * returns the number of bytes read. The symtab is in the closed
- * state afterwards, so no strings can be added to it.
+/*! \brief
+ * Prints human readable form of \p symtab.
+ *
+ * \param[in] fp File to print to.
+ * \param[in] indent Number of spaces to use for indentation.
+ * \param[in] title Name for header text.
+ * \param[in] symtab Symbol table to print out.
  */
-
 void pr_symtab(FILE* fp, int indent, const char* title, t_symtab* symtab);
-/* This routine prints out a (human) readable representation of
- * the symbol table symtab to the file fp. Ident specifies the
- * number of spaces the text should be indented. Title is used
- * to print a header text.
- */
 
 #endif
index 534a756cc1844021a5a9d273d7f34134d1209a62..5484ca5081ce69d458f14345f0fc1fe60b2c6c42 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019, by the GROMACS development team, led by
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(TopologyTest topology-test
-  exclusionblocks.cpp
-  mtop.cpp
-  symtab.cpp)
+    CPP_SOURCE_FILES
+        exclusionblocks.cpp
+        idef.cpp
+        mtop.cpp
+        symtab.cpp
+        )
 
index ab9f1ad1227d98909f09b37739599105213112c2..0a45f88c03f3cbf7242b823a13a60a57e4523714 100644 (file)
 
 #include "gromacs/topology/exclusionblocks.h"
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "gromacs/topology/block.h"
 #include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/smalloc.h"
 
 #include "testutils/cmdlinetest.h"
@@ -101,6 +103,21 @@ void makeTestBlockAData(t_blocka* ba)
     addGroupToBlocka(ba, indices);
 }
 
+//! Return ListOfLists filled with some datastructures
+ListOfLists<int> makeTestListOfLists()
+{
+    ListOfLists<int> list;
+
+    std::vector<int> indices = { 12, 11, 9, 6, 2 };
+    list.pushBack(indices);
+    indices = { 10, 8, 5, 1 };
+    list.pushBack(indices);
+    indices = { 7, 4, 0 };
+    list.pushBack(indices);
+
+    return list;
+}
+
 class ExclusionBlockTest : public ::testing::Test
 {
 public:
@@ -108,6 +125,7 @@ public:
     {
         const int natom = 3;
         makeTestBlockAData(&ba_);
+        list_ = makeTestListOfLists();
         b_.resize(natom);
     }
     ~ExclusionBlockTest() override { done_blocka(&ba_); }
@@ -126,8 +144,20 @@ public:
         }
     }
 
+    void compareBlocksAndList()
+    {
+        GMX_RELEASE_ASSERT(ssize(b_) == list_.ssize(), "The list counts should match");
+        for (index i = 0; i < ssize(b_); i++)
+        {
+            gmx::ArrayRef<const int> jList = list_[i];
+            ASSERT_EQ(b_[i].nra(), jList.ssize()) << "Block size mismatch at " << i << ".";
+            EXPECT_THAT(b_[i].atomNumber, ::testing::Pointwise(::testing::Eq(), jList));
+        }
+    }
+
 protected:
     t_blocka                    ba_;
+    ListOfLists<int>            list_;
     std::vector<ExclusionBlock> b_;
 };
 
@@ -148,8 +178,8 @@ TEST_F(ExclusionBlockTest, ConvertExclusionBlockToBlocka)
 
 TEST_F(ExclusionBlockTest, MergeExclusions)
 {
-    mergeExclusions(&ba_, b_);
-    compareBlocks();
+    mergeExclusions(&list_, b_);
+    compareBlocksAndList();
 }
 
 } // namespace
diff --git a/src/gromacs/topology/tests/idef.cpp b/src/gromacs/topology/tests/idef.cpp
new file mode 100644 (file)
index 0000000..77f0dbd
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements test of InteractionList routines
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \ingroup module_topology
+ */
+#include "gmxpre.h"
+
+#include "gromacs/topology/idef.h"
+
+#include <array>
+
+#include <gtest/gtest.h>
+
+namespace gmx
+{
+namespace
+{
+
+TEST(InteractionListTest, EmptyWorks)
+{
+    InteractionList ilist;
+    EXPECT_TRUE(ilist.empty());
+    EXPECT_EQ(ilist.size(), 0);
+}
+
+TEST(InteractionListTest, CanAddInteractionArray)
+{
+    InteractionList    ilist;
+    int                parameterType  = 0;
+    std::array<int, 1> singleAtomList = { 1 };
+    ilist.push_back(parameterType, singleAtomList);
+    EXPECT_FALSE(ilist.empty());
+    EXPECT_EQ(ilist.size(), 2);
+    EXPECT_EQ(ilist.iatoms[0], parameterType);
+    EXPECT_EQ(ilist.iatoms[1], 1);
+}
+
+TEST(InteractionListTest, CanAddInteractionArrayMultipleAtoms)
+{
+    InteractionList    ilist;
+    int                parameterType = 0;
+    std::array<int, 3> atomList      = { 1, 2, 3 };
+    ilist.push_back(parameterType, atomList);
+    EXPECT_FALSE(ilist.empty());
+    EXPECT_EQ(ilist.size(), 4);
+    EXPECT_EQ(ilist.iatoms[0], parameterType);
+    EXPECT_EQ(ilist.iatoms[1], 1);
+    EXPECT_EQ(ilist.iatoms[2], 2);
+    EXPECT_EQ(ilist.iatoms[3], 3);
+}
+
+TEST(InteractionListTest, CanAddInteractionPointer)
+{
+    InteractionList    ilist;
+    int                parameterType  = 0;
+    std::array<int, 1> singleAtomList = { 1 };
+    ilist.push_back(parameterType, singleAtomList.size(), singleAtomList.data());
+    EXPECT_FALSE(ilist.empty());
+    EXPECT_EQ(ilist.size(), 2);
+    EXPECT_EQ(ilist.iatoms[0], parameterType);
+    EXPECT_EQ(ilist.iatoms[1], 1);
+}
+
+TEST(InteractionListTest, CanAddListToOtherList)
+{
+    InteractionList firstList;
+    int             firstParameterType = 0;
+    {
+        std::array<int, 1> singleAtomList = { 1 };
+        firstList.push_back(firstParameterType, singleAtomList);
+        EXPECT_FALSE(firstList.empty());
+        EXPECT_EQ(firstList.size(), 2);
+        EXPECT_EQ(firstList.iatoms[0], firstParameterType);
+        EXPECT_EQ(firstList.iatoms[1], 1);
+    }
+    InteractionList secondList;
+    int             secondParameterType = 1;
+    {
+        std::array<int, 3> atomList = { 1, 2, 3 };
+        secondList.push_back(secondParameterType, atomList);
+        EXPECT_FALSE(secondList.empty());
+        EXPECT_EQ(secondList.size(), 4);
+        EXPECT_EQ(secondList.iatoms[0], secondParameterType);
+        EXPECT_EQ(secondList.iatoms[1], 1);
+        EXPECT_EQ(secondList.iatoms[2], 2);
+        EXPECT_EQ(secondList.iatoms[3], 3);
+    }
+    firstList.append(secondList);
+    EXPECT_EQ(firstList.size(), 6);
+    EXPECT_EQ(firstList.iatoms[2], secondParameterType);
+    EXPECT_EQ(firstList.iatoms[3], 1);
+    EXPECT_EQ(firstList.iatoms[4], 2);
+    EXPECT_EQ(firstList.iatoms[5], 3);
+}
+
+TEST(InteractionListTest, ClearingWorks)
+{
+    InteractionList    ilist;
+    int                parameterType  = 0;
+    std::array<int, 1> singleAtomList = { 1 };
+    ilist.push_back(parameterType, singleAtomList);
+    EXPECT_FALSE(ilist.empty());
+    EXPECT_EQ(ilist.size(), 2);
+    EXPECT_EQ(ilist.iatoms[0], parameterType);
+    EXPECT_EQ(ilist.iatoms[1], 1);
+    ilist.clear();
+    EXPECT_TRUE(ilist.empty());
+    EXPECT_EQ(ilist.size(), 0);
+}
+
+} // namespace
+
+} // namespace gmx
index f555b461813ef69a69f140ddfa1da06963016939..08713b39eb85787ef0d3f523d46169fc64464d48 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,6 +44,7 @@
 #include <gtest/gtest.h>
 
 #include "gromacs/topology/mtop_util.h"
+#include "gromacs/utility/basedefinitions.h"
 
 namespace gmx
 {
@@ -67,7 +68,42 @@ void createBasicTop(gmx_mtop_t* mtop)
     mtop->molblock[0].type = 0;
     mtop->molblock[0].nmol = 3;
     mtop->natoms           = moltype.atoms.nr * mtop->molblock[0].nmol;
-    gmx_mtop_finalize(mtop);
+    mtop->finalize();
+}
+
+/*! \brief
+ * Creates dummy topology with two differently sized residues.
+ *
+ * Residue begin and end are set to allow checking routines
+ * that make use of the boundaries.
+ *
+ * \returns The residue ranges.
+ */
+std::vector<gmx::Range<int>> createTwoResidueTopology(gmx_mtop_t* mtop)
+{
+    auto& moltype        = mtop->moltype.emplace_back();
+    int   residueOneSize = 5;
+    int   residueTwoSize = 4;
+    moltype.atoms.nr     = residueOneSize + residueTwoSize;
+    snew(moltype.atoms.atom, residueOneSize + residueTwoSize);
+    for (int i = 0; i < residueOneSize; i++)
+    {
+        moltype.atoms.atom[i].resind = 0;
+    }
+    for (int i = residueOneSize; i < residueOneSize + residueTwoSize; i++)
+    {
+        moltype.atoms.atom[i].resind = 1;
+    }
+
+    mtop->molblock.resize(1);
+    mtop->molblock[0].type = 0;
+    mtop->molblock[0].nmol = 1;
+    mtop->natoms           = moltype.atoms.nr * mtop->molblock[0].nmol;
+    mtop->finalize();
+    std::vector<gmx::Range<int>> residueRange;
+    residueRange.emplace_back(0, residueOneSize);
+    residueRange.emplace_back(residueOneSize, residueOneSize + residueTwoSize);
+    return residueRange;
 }
 
 TEST(MtopTest, RangeBasedLoop)
@@ -101,6 +137,32 @@ TEST(MtopTest, Operators)
     EXPECT_FALSE(it == otherIt);
 }
 
+TEST(MtopTest, CanFindResidueStartAndEndAtoms)
+{
+    gmx_mtop_t mtop;
+    auto       expectedResidueRange = createTwoResidueTopology(&mtop);
+
+    auto atomRanges = atomRangeOfEachResidue(mtop.moltype[0]);
+    ASSERT_EQ(atomRanges.size(), expectedResidueRange.size());
+    for (gmx::index i = 0; i < gmx::ssize(atomRanges); i++)
+    {
+        EXPECT_EQ(atomRanges[i].begin(), expectedResidueRange[i].begin());
+        EXPECT_EQ(atomRanges[i].end(), expectedResidueRange[i].end());
+        ASSERT_EQ(atomRanges[i].size(), expectedResidueRange[i].size());
+    }
+    int rangeIndex = 0;
+    for (const auto& range : atomRanges)
+    {
+        auto referenceRangeIt = expectedResidueRange[rangeIndex].begin();
+        for (int i : range)
+        {
+            EXPECT_EQ(i, *referenceRangeIt);
+            referenceRangeIt++;
+        }
+        rangeIndex++;
+    }
+}
+
 } // namespace
 
 } // namespace gmx
diff --git a/src/gromacs/topology/tests/refdata/StringTableTest_AddLargeNumberOfEntries.xml b/src/gromacs/topology/tests/refdata/StringTableTest_AddLargeNumberOfEntries.xml
new file mode 100644 (file)
index 0000000..1d1bfb5
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="Output"><![CDATA[
+    Test title (8):
+       Test title[0]="0"
+       Test title[1]="1"
+       Test title[2]="2"
+       Test title[3]="3"
+       Test title[4]="4"
+       Test title[5]="5"
+       Test title[6]="6"
+       Test title[7]="foobar"
+]]></String>
+</ReferenceData>
diff --git a/src/gromacs/topology/tests/refdata/StringTableTest_AddSingleEntry.xml b/src/gromacs/topology/tests/refdata/StringTableTest_AddSingleEntry.xml
new file mode 100644 (file)
index 0000000..fb12c9a
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="Output"><![CDATA[
+    Test title (1):
+       Test title[0]="foo"
+]]></String>
+</ReferenceData>
diff --git a/src/gromacs/topology/tests/refdata/StringTableTest_AddTwoDistinctEntries.xml b/src/gromacs/topology/tests/refdata/StringTableTest_AddTwoDistinctEntries.xml
new file mode 100644 (file)
index 0000000..34a8fee
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="Output"><![CDATA[
+    Test title (2):
+       Test title[0]="foo"
+       Test title[1]="Bar"
+]]></String>
+</ReferenceData>
diff --git a/src/gromacs/topology/tests/refdata/StringTableTest_CanAccessWithAt.xml b/src/gromacs/topology/tests/refdata/StringTableTest_CanAccessWithAt.xml
new file mode 100644 (file)
index 0000000..fb12c9a
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="Output"><![CDATA[
+    Test title (1):
+       Test title[0]="foo"
+]]></String>
+</ReferenceData>
diff --git a/src/gromacs/topology/tests/refdata/StringTableTest_CanAccessWithBracket.xml b/src/gromacs/topology/tests/refdata/StringTableTest_CanAccessWithBracket.xml
new file mode 100644 (file)
index 0000000..fb12c9a
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="Output"><![CDATA[
+    Test title (1):
+       Test title[0]="foo"
+]]></String>
+</ReferenceData>
diff --git a/src/gromacs/topology/tests/refdata/StringTableTest_NoDuplicatesInLargeTable.xml b/src/gromacs/topology/tests/refdata/StringTableTest_NoDuplicatesInLargeTable.xml
new file mode 100644 (file)
index 0000000..d054c47
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="Output"><![CDATA[
+    Test title (15):
+       Test title[0]="0"
+       Test title[1]="1"
+       Test title[2]="2"
+       Test title[3]="3"
+       Test title[4]="4"
+       Test title[5]="5"
+       Test title[6]="6"
+       Test title[7]="baz"
+       Test title[8]="7"
+       Test title[9]="8"
+       Test title[10]="9"
+       Test title[11]="10"
+       Test title[12]="11"
+       Test title[13]="12"
+       Test title[14]="13"
+]]></String>
+</ReferenceData>
diff --git a/src/gromacs/topology/tests/refdata/StringTableTest_StringCompareIsCorrect.xml b/src/gromacs/topology/tests/refdata/StringTableTest_StringCompareIsCorrect.xml
new file mode 100644 (file)
index 0000000..fb12c9a
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="Output"><![CDATA[
+    Test title (1):
+       Test title[0]="foo"
+]]></String>
+</ReferenceData>
diff --git a/src/gromacs/topology/tests/refdata/StringTableTest_ThrowsOutOfRange.xml b/src/gromacs/topology/tests/refdata/StringTableTest_ThrowsOutOfRange.xml
new file mode 100644 (file)
index 0000000..fb12c9a
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="Output"><![CDATA[
+    Test title (1):
+       Test title[0]="foo"
+]]></String>
+</ReferenceData>
diff --git a/src/gromacs/topology/tests/refdata/StringTableTest_TryToAddDuplicates.xml b/src/gromacs/topology/tests/refdata/StringTableTest_TryToAddDuplicates.xml
new file mode 100644 (file)
index 0000000..5889ed6
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="Output"><![CDATA[
+    Test title (3):
+       Test title[0]="foo"
+       Test title[1]="Bar"
+       Test title[2]="Foo"
+]]></String>
+</ReferenceData>
index 301685fdcf5d72113b5559abae0f4db4875f1b19..f9a0d67d0ef377c1457248bc3397de159215bd05 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -34,7 +34,7 @@
  */
 /*! \internal \file
  * \brief
- * Tests for legacy symbol table
+ * Tests for legacy symbol table and replacement.
  *
  * \author Paul Bauer <paul.bauer.q@gmail.com>
  */
 
 #include <gtest/gtest.h>
 
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/inmemoryserializer.h"
 #include "gromacs/utility/strconvert.h"
 #include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textreader.h"
 
 #include "testutils/refdata.h"
+#include "testutils/testfilemanager.h"
 
 namespace gmx
 {
@@ -57,14 +62,355 @@ namespace gmx
 namespace test
 {
 
+class StringTableTest : public ::testing::Test
+{
+public:
+    StringTableTest() {}
+    //! Get handle to symbol table.
+    StringTableBuilder& builder() { return stringTableBuilder_; }
+    /* \brief
+     * Check human readable format of the table.
+     * \todo change when table writing to users is done also with serializer.
+     * \parm[in] table The string table to check the serialization.
+     */
+    void checkTable(const StringTable& table);
+
+private:
+    //! Get reference checker using lazy initialization
+    TestReferenceChecker* checker()
+    {
+        if (!checker_)
+        {
+            checker_ = std::make_unique<TestReferenceChecker>(data_.rootChecker());
+        }
+        return checker_.get();
+    }
+    //! Symbol table for testing purposes.
+    StringTableBuilder stringTableBuilder_;
+    //! Handler for reference data.
+    TestReferenceData data_;
+    //! Handler for checking reference data.
+    std::unique_ptr<TestReferenceChecker> checker_;
+};
+
+void StringTableTest::checkTable(const StringTable& table)
+{
+    TestFileManager files;
+    std::string     filename(files.getTemporaryFilePath("table.txt"));
+    FILE*           fp = fopen(filename.c_str(), "w");
+    table.printStringTableStorageToFile(fp, 4, "Test title");
+    fclose(fp);
+    const std::string text = TextReader::readFileToString(filename);
+    checker()->checkTextBlock(text, "Output");
+}
+
+/*! \brief
+ *  Check that symbols obtained from symtab compare correctly.
+ *
+ *  Helper function to find out if two entries obtained by a symtab lookup
+ *  are equivalent or not, according to testing criteria.
+ *  Checks that the indices match before finalizing storage.
+ *
+ *  \param[in] builder     StringTableBuilder table that contains the entries to validate.
+ *  \param[in] firstSymbol Handle into \p builder obtained from placing string in \p builder.
+ *  \param[in] otherSymbol Other handle from obtained from separate string deposit.
+ *  \param[in] expectedOutcome If the handles should result in equal entries or not.
+ */
+static void compareDifferentIndices(const StringTableBuilder& builder,
+                                    const StringTableEntry&   firstSymbol,
+                                    const StringTableEntry&   otherSymbol,
+                                    bool                      expectedOutcome)
+{
+    EXPECT_EQ(expectedOutcome, (firstSymbol == otherSymbol));
+    auto firstIndex = builder.findEntryByName(*firstSymbol);
+    auto otherIndex = builder.findEntryByName(*otherSymbol);
+    EXPECT_EQ(expectedOutcome, (firstIndex == otherIndex))
+            << "Expected was " << expectedOutcome << " firstIndex is " << firstIndex
+            << " otherIndex is " << otherIndex;
+}
+
+/*! \brief
+ * Helper to obtain the integer index from an entry.
+ *
+ * As the index is only used during (de-) serialization, use this machinery
+ * to obtain it.
+ *
+ * \param[in] symbol Single StringTableEntry to obtain the index of.
+ * \returns Integer index to be used to obtain value from StringTable.
+ */
+static int readIndexFromSerializer(const StringTableEntry& symbol)
+{
+    gmx::InMemorySerializer writer;
+    symbol.serialize(&writer);
+    auto                      buffer = writer.finishAndGetBuffer();
+    gmx::InMemoryDeserializer reader(buffer, false);
+    int                       index = 0;
+    reader.doInt(&index);
+    return index;
+}
+
+/*! \brief
+ * Helper function to check that a string in matches when looked up in non finalized table.
+ *
+ * Checks that a string looked up by using the index in the symbol table matches
+ * the string stored in the wrapper object obtained by entering a string.
+ *
+ * \param[in] symtab Symbol table that contains the entries.
+ * \param[in] symbol The entry obtained from placing a string in the symbol table.
+ * \param[in] string The string the entry should match.
+ */
+static void stringMatches(const StringTable& symtab, const StringTableEntry& symbol, const char* string)
+{
+    int  index          = readIndexFromSerializer(symbol);
+    auto entryFromIndex = symtab.at(index);
+
+    EXPECT_EQ(*entryFromIndex, string)
+            << "Index is " << index << " Entry from index is " << entryFromIndex->c_str();
+}
+
+
+TEST_F(StringTableTest, AddSingleEntry)
+{
+    builder().addString("foo");
+    StringTable table = builder().build();
+    checkTable(table);
+}
+
+TEST_F(StringTableTest, CanAccessWithAt)
+{
+    builder().addString("foo");
+    StringTable table = builder().build();
+    EXPECT_NO_THROW(table.at(0));
+    checkTable(table);
+}
+
+TEST_F(StringTableTest, CanAccessWithBracket)
+{
+    builder().addString("foo");
+    StringTable table = builder().build();
+    checkTable(table);
+    auto entry = table[0];
+    EXPECT_EQ(*entry, "foo");
+}
+
+TEST_F(StringTableTest, ThrowsOutOfRange)
+{
+    builder().addString("foo");
+    StringTable table = builder().build();
+    EXPECT_THROW(table.at(1), InternalError);
+    checkTable(table);
+}
+
+TEST_F(StringTableTest, StringCompareIsCorrect)
+{
+    auto        fooSymbol = builder().addString("foo");
+    StringTable table     = builder().build();
+    stringMatches(table, fooSymbol, "foo");
+    checkTable(table);
+}
+
+TEST_F(StringTableTest, AddTwoDistinctEntries)
+{
+    auto fooSymbol = builder().addString("foo");
+    auto barSymbol = builder().addString("Bar");
+
+    EXPECT_FALSE(fooSymbol == barSymbol);
+    compareDifferentIndices(builder(), fooSymbol, barSymbol, false);
+    EXPECT_TRUE("foo" == *fooSymbol);
+    EXPECT_TRUE("Bar" == *barSymbol);
+    auto table = builder().build();
+    stringMatches(table, fooSymbol, "foo");
+    stringMatches(table, barSymbol, "Bar");
+    checkTable(table);
+}
+
+TEST_F(StringTableTest, TryToAddDuplicates)
+{
+    auto fooSymbol = builder().addString("foo");
+    auto barSymbol = builder().addString("Bar");
+
+    EXPECT_FALSE(fooSymbol == barSymbol);
+    EXPECT_FALSE(fooSymbol->empty());
+    compareDifferentIndices(builder(), fooSymbol, barSymbol, false);
+    EXPECT_TRUE("foo" == *fooSymbol);
+    EXPECT_TRUE("Bar" == *barSymbol);
+
+    // Insert a duplicate element
+    auto anotherFooSymbol = builder().addString("foo");
+    // Insert element with different case
+    auto capitalFooSymbol = builder().addString("Foo");
+
+    // Check that no duplicate is made
+    EXPECT_TRUE(fooSymbol == anotherFooSymbol);
+    // Check case sensitivity
+    EXPECT_FALSE(fooSymbol == capitalFooSymbol);
+
+    // Check that underlying representation is same
+    EXPECT_TRUE("foo" == *anotherFooSymbol);
+    EXPECT_TRUE("foo" == *fooSymbol);
+    EXPECT_FALSE(*fooSymbol == *capitalFooSymbol);
+    EXPECT_TRUE("Bar" == *barSymbol);
+
+    // Check for correct behaviours with new and old symbols
+    compareDifferentIndices(builder(), fooSymbol, anotherFooSymbol, true);
+    compareDifferentIndices(builder(), barSymbol, anotherFooSymbol, false);
+    compareDifferentIndices(builder(), fooSymbol, barSymbol, false);
+    compareDifferentIndices(builder(), fooSymbol, capitalFooSymbol, false);
+    auto table = builder().build();
+    checkTable(table);
+}
+
+TEST_F(StringTableTest, AddLargeNumberOfEntries)
+{
+    int                           numStringsToAdd = 7; // Random number of strings.
+    std::vector<StringTableEntry> symbolsAdded;
+    symbolsAdded.reserve(numStringsToAdd);
+    for (int i = 0; i < numStringsToAdd; ++i)
+    {
+        symbolsAdded.push_back(builder().addString(toString(i)));
+    }
+    for (int i = 0; i < numStringsToAdd; ++i)
+    {
+        EXPECT_TRUE(toString(i) == *symbolsAdded[i]) << "index is " << i;
+    }
+    // Add something unrelated and check that indices still work afterward.
+    builder().addString("foobar");
+    for (int i = 0; i < numStringsToAdd; ++i)
+    {
+        EXPECT_TRUE(toString(i) == *symbolsAdded[i]) << "index is " << i;
+    }
+    auto table = builder().build();
+    for (int i = 0; i < numStringsToAdd; ++i)
+    {
+        stringMatches(table, symbolsAdded[i], toString(i).c_str());
+    }
+    checkTable(table);
+}
+
+TEST_F(StringTableTest, NoDuplicatesInLargeTable)
+{
+    int                           halfOfStringsToAdd   = 7; // Random number of strings.
+    int                           totalNumStringsToAdd = 2 * halfOfStringsToAdd;
+    std::vector<StringTableEntry> symbolsAdded;
+    symbolsAdded.reserve(halfOfStringsToAdd);
+    for (int i = 0; i < halfOfStringsToAdd; ++i)
+    {
+        symbolsAdded.push_back(builder().addString(toString(i)));
+    }
+
+    // We now try to mess around in the symtab.
+    auto bazSymbol = builder().addString("baz");
+
+    // Now try to add more symbols, also including those that are already there.
+    for (int i = 0; i < totalNumStringsToAdd; i++)
+    {
+        symbolsAdded.push_back(builder().addString(toString(i)));
+    }
+
+    //! Check that entries that should be equal are, and new ones are not.
+    for (int i = 0; i < halfOfStringsToAdd; i++)
+    {
+        compareDifferentIndices(builder(), symbolsAdded[i], symbolsAdded[halfOfStringsToAdd + i], true);
+        compareDifferentIndices(builder(), symbolsAdded[i], symbolsAdded[2 * halfOfStringsToAdd + i], false);
+        compareDifferentIndices(builder(), symbolsAdded[i], bazSymbol, false);
+    }
+    EXPECT_TRUE("baz" == *bazSymbol);
+    symbolsAdded.emplace_back(bazSymbol);
+    auto table = builder().build();
+    checkTable(table);
+}
+
+
+TEST_F(StringTableTest, CanWriteToBuffer)
+{
+    builder().addString("foo");
+    builder().addString("bar");
+    builder().addString("baz");
+    auto               finalTable = builder().build();
+    InMemorySerializer writer;
+    finalTable.serializeStringTable(&writer);
+
+    auto buffer = writer.finishAndGetBuffer();
+    EXPECT_EQ(buffer.size(), 37); // 4 (size) + 3*(8 (string size) + 3*1 (char size) )
+}
+
+TEST_F(StringTableTest, Roundtrip)
+{
+    // First generate a buffer from a string table
+    builder().addString("foo");
+    builder().addString("bar");
+    builder().addString("baz");
+    auto               finalTable = builder().build();
+    InMemorySerializer writer;
+    finalTable.serializeStringTable(&writer);
+
+    auto buffer = writer.finishAndGetBuffer();
+    EXPECT_EQ(buffer.size(), 37); // 4 (size) + 3*(8 (string size) + 3*1 (char size) )
+
+    // Now try to make a new table from it.
+    InMemoryDeserializer reader(buffer, false);
+    StringTable          readInTable(&reader);
+    EXPECT_EQ(*(finalTable.at(0)), *(readInTable.at(0)));
+    EXPECT_EQ(*(finalTable.at(1)), *(readInTable.at(1)));
+    EXPECT_EQ(*(finalTable.at(2)), *(readInTable.at(2)));
+}
+
+TEST_F(StringTableTest, RoundtripWithCorrectStringIndices)
+{
+    std::vector<StringTableEntry> testEntries;
+    // First generate a buffer from a string table
+    testEntries.emplace_back(builder().addString("foo"));
+    testEntries.emplace_back(builder().addString("bar"));
+    testEntries.emplace_back(builder().addString("baz"));
+    auto               finalTable = builder().build();
+    InMemorySerializer writer;
+    finalTable.serializeStringTable(&writer);
+    for (const auto& stringEntry : testEntries)
+    {
+        stringEntry.serialize(&writer);
+    }
+
+    auto buffer = writer.finishAndGetBuffer();
+    EXPECT_EQ(buffer.size(), 49); // 4 (size) + 3*(8 (string size) + 3*1 (char size) + 3*4 (int size))
+
+    // Now try to make a new table from it.
+    InMemoryDeserializer          reader(buffer, false);
+    StringTable                   readInTable(&reader);
+    std::vector<StringTableEntry> deserializedEntries;
+    for (index gmx_unused i = 0; i < gmx::ssize(testEntries); i++)
+    {
+        deserializedEntries.emplace_back(readStringTableEntry(&reader, readInTable));
+    }
+    EXPECT_EQ(*(finalTable.at(0)), *(deserializedEntries[0]));
+    EXPECT_EQ(*(finalTable.at(1)), *(deserializedEntries[1]));
+    EXPECT_EQ(*(finalTable.at(2)), *(deserializedEntries[2]));
+}
+
+TEST_F(StringTableTest, CanCopyToLegacyTable)
+{
+    auto fooSymbol = builder().addString("foo");
+    auto barSymbol = builder().addString("Bar");
+
+    StringTable finalTable = builder().build();
+
+    t_symtab legacySymtab;
+    open_symtab(&legacySymtab);
+    finalTable.copyToLegacySymtab(&legacySymtab);
+    int fooEntryIndex = readIndexFromSerializer(fooSymbol);
+    int barEntryIndex = readIndexFromSerializer(barSymbol);
+    EXPECT_STREQ(finalTable.at(fooEntryIndex)->c_str(), *get_symtab_handle(&legacySymtab, fooEntryIndex));
+    EXPECT_STREQ(finalTable.at(barEntryIndex)->c_str(), *get_symtab_handle(&legacySymtab, barEntryIndex));
+    done_symtab(&legacySymtab);
+}
+
 namespace
 {
 
-class SymtabTest : public ::testing::Test
+class LegacySymtabTest : public ::testing::Test
 {
 public:
-    SymtabTest() { open_symtab(&symtab_); }
-    ~SymtabTest() override
+    LegacySymtabTest() { open_symtab(&symtab_); }
+    ~LegacySymtabTest() override
     {
         done_symtab(&symtab_);
         EXPECT_EQ(symtab_.nr, 0);
@@ -94,7 +440,7 @@ private:
     std::unique_ptr<TestReferenceChecker> checker_;
 };
 
-void SymtabTest::dumpSymtab()
+void LegacySymtabTest::dumpSymtab()
 {
     int                      nr     = symtab_.nr;
     t_symbuf*                symbuf = symtab_.symbuf;
@@ -161,13 +507,13 @@ void compareDifferentHandles(t_symtab* symtab, char** firstSymbol, char** otherS
     EXPECT_EQ(expectedOutcome, entriesAreEqual(symtab, otherSymbol, firstIndex));
 }
 
-TEST_F(SymtabTest, EmptyOnOpen)
+TEST_F(LegacySymtabTest, EmptyOnOpen)
 {
     ASSERT_EQ(0, symtab()->nr);
     ASSERT_EQ(nullptr, symtab()->symbuf);
 }
 
-TEST_F(SymtabTest, AddSingleEntry)
+TEST_F(LegacySymtabTest, AddSingleEntry)
 {
     auto fooSymbol = put_symtab(symtab(), "Foo");
     ASSERT_EQ(1, symtab()->nr);
@@ -175,7 +521,7 @@ TEST_F(SymtabTest, AddSingleEntry)
     EXPECT_STREQ("Foo", *fooSymbol);
 }
 
-TEST_F(SymtabTest, AddTwoDistinctEntries)
+TEST_F(LegacySymtabTest, AddTwoDistinctEntries)
 {
     auto fooSymbol = put_symtab(symtab(), "Foo");
     auto barSymbol = put_symtab(symtab(), "Bar");
@@ -190,7 +536,7 @@ TEST_F(SymtabTest, AddTwoDistinctEntries)
     EXPECT_STREQ("Bar", *barSymbol);
 }
 
-TEST_F(SymtabTest, TryToAddDuplicates)
+TEST_F(LegacySymtabTest, TryToAddDuplicates)
 {
     auto fooSymbol = put_symtab(symtab(), "Foo");
     auto barSymbol = put_symtab(symtab(), "Bar");
@@ -220,7 +566,7 @@ TEST_F(SymtabTest, TryToAddDuplicates)
     compareDifferentHandles(symtab(), fooSymbol, barSymbol, false);
 }
 
-TEST_F(SymtabTest, AddLargeNumberOfEntries)
+TEST_F(LegacySymtabTest, AddLargeNumberOfEntries)
 {
     int                 numStringsToAdd = 7; // Larger than c_maxBufSize limit for size of symbuf.
     std::vector<char**> symbolsAdded;
@@ -249,7 +595,7 @@ TEST_F(SymtabTest, AddLargeNumberOfEntries)
     dumpSymtab();
 }
 
-TEST_F(SymtabTest, NoDuplicatesInLargeTable)
+TEST_F(LegacySymtabTest, NoDuplicatesInLargeTable)
 {
     int halfOfStringsToAdd   = 7; // Larger than c_maxBufSize limit for size of symbuf.
     int totalNumStringsToAdd = 2 * halfOfStringsToAdd;
index e01993300bb2ab27429e88d045912376cb8f1816..6c9bfc1d60d2e8209947590076dd281aa770963d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -71,12 +72,11 @@ void init_top(t_topology* top)
     init_atom(&(top->atoms));
     init_atomtypes(&(top->atomtypes));
     init_block(&top->mols);
-    init_blocka(&top->excls);
     open_symtab(&top->symtab);
 }
 
 
-gmx_moltype_t::gmx_moltype_t() : name(nullptr), excls()
+gmx_moltype_t::gmx_moltype_t() : name(nullptr)
 {
     init_t_atoms(&atoms, 0, FALSE);
 }
@@ -84,7 +84,28 @@ gmx_moltype_t::gmx_moltype_t() : name(nullptr), excls()
 gmx_moltype_t::~gmx_moltype_t()
 {
     done_atom(&atoms);
-    done_blocka(&excls);
+}
+
+static int gmx_mtop_maxresnr(const gmx::ArrayRef<const gmx_moltype_t> moltypes, int maxres_renum)
+{
+    int maxresnr = 0;
+
+    for (const gmx_moltype_t& moltype : moltypes)
+    {
+        const t_atoms& atoms = moltype.atoms;
+        if (atoms.nres > maxres_renum)
+        {
+            for (int r = 0; r < atoms.nres; r++)
+            {
+                if (atoms.resinfo[r].nr > maxresnr)
+                {
+                    maxresnr = atoms.resinfo[r].nr;
+                }
+            }
+        }
+    }
+
+    return maxresnr;
 }
 
 gmx_mtop_t::gmx_mtop_t()
@@ -102,6 +123,70 @@ gmx_mtop_t::~gmx_mtop_t()
     done_atomtypes(&atomtypes);
 }
 
+void gmx_mtop_t::finalize()
+{
+    if (molblock.size() == 1 && molblock[0].nmol == 1)
+    {
+        /* We have a single molecule only, no renumbering needed.
+         * This case also covers an mtop converted from pdb/gro/... input,
+         * so we retain the original residue numbering.
+         */
+        maxResiduesPerMoleculeToTriggerRenumber_ = 0;
+    }
+    else
+    {
+        /* We only renumber single residue molecules. Their intra-molecular
+         * residue numbering is anyhow irrelevant.
+         */
+        maxResiduesPerMoleculeToTriggerRenumber_ = 1;
+    }
+
+    const char* env = getenv("GMX_MAXRESRENUM");
+    if (env != nullptr)
+    {
+        sscanf(env, "%d", &maxResiduesPerMoleculeToTriggerRenumber_);
+    }
+    if (maxResiduesPerMoleculeToTriggerRenumber_ == -1)
+    {
+        /* -1 signals renumber residues in all molecules */
+        maxResiduesPerMoleculeToTriggerRenumber_ = std::numeric_limits<int>::max();
+    }
+
+    maxResNumberNotRenumbered_ = gmx_mtop_maxresnr(moltype, maxResiduesPerMoleculeToTriggerRenumber_);
+
+    buildMolblockIndices();
+}
+
+void gmx_mtop_t::buildMolblockIndices()
+{
+    moleculeBlockIndices.resize(molblock.size());
+
+    int atomIndex          = 0;
+    int residueIndex       = 0;
+    int residueNumberStart = maxResNumberNotRenumbered_ + 1;
+    int moleculeIndexStart = 0;
+    for (size_t mb = 0; mb < molblock.size(); mb++)
+    {
+        const gmx_molblock_t& molb         = molblock[mb];
+        MoleculeBlockIndices& indices      = moleculeBlockIndices[mb];
+        const int             numResPerMol = moltype[molb.type].atoms.nres;
+
+        indices.numAtomsPerMolecule = moltype[molb.type].atoms.nr;
+        indices.globalAtomStart     = atomIndex;
+        indices.globalResidueStart  = residueIndex;
+        atomIndex += molb.nmol * indices.numAtomsPerMolecule;
+        residueIndex += molb.nmol * numResPerMol;
+        indices.globalAtomEnd      = atomIndex;
+        indices.residueNumberStart = residueNumberStart;
+        if (numResPerMol <= maxResiduesPerMoleculeToTriggerRenumber_)
+        {
+            residueNumberStart += molb.nmol * numResPerMol;
+        }
+        indices.moleculeIndexStart = moleculeIndexStart;
+        moleculeIndexStart += molb.nmol;
+    }
+}
+
 void done_top(t_topology* top)
 {
     done_idef(&top->idef);
@@ -112,7 +197,6 @@ void done_top(t_topology* top)
 
     done_symtab(&(top->symtab));
     done_block(&(top->mols));
-    done_blocka(&(top->excls));
 }
 
 void done_top_mtop(t_topology* top, gmx_mtop_t* mtop)
@@ -123,7 +207,6 @@ void done_top_mtop(t_topology* top, gmx_mtop_t* mtop)
         {
             done_idef(&top->idef);
             done_atom(&top->atoms);
-            done_blocka(&top->excls);
             done_block(&top->mols);
             done_symtab(&top->symtab);
             open_symtab(&mtop->symtab);
@@ -134,22 +217,7 @@ void done_top_mtop(t_topology* top, gmx_mtop_t* mtop)
     }
 }
 
-gmx_localtop_t::gmx_localtop_t()
-{
-    init_blocka_null(&excls);
-    init_idef(&idef);
-    init_atomtypes(&atomtypes);
-}
-
-gmx_localtop_t::~gmx_localtop_t()
-{
-    if (!useInDomainDecomp_)
-    {
-        done_idef(&idef);
-        done_blocka(&excls);
-        done_atomtypes(&atomtypes);
-    }
-}
+gmx_localtop_t::gmx_localtop_t(const gmx_ffparams_t& ffparams) : idef(ffparams) {}
 
 bool gmx_mtop_has_masses(const gmx_mtop_t* mtop)
 {
@@ -287,7 +355,7 @@ static void pr_moltype(FILE*                 fp,
     pr_indent(fp, indent);
     fprintf(fp, "name=\"%s\"\n", *(molt->name));
     pr_atoms(fp, indent, "atoms", &(molt->atoms), bShowNumbers);
-    pr_blocka(fp, indent, "excls", &molt->excls, bShowNumbers);
+    pr_listoflists(fp, indent, "excls", &molt->excls, bShowNumbers);
     for (j = 0; (j < F_NRE); j++)
     {
         pr_ilist(fp, indent, interaction_function[j].longname, ffparams->functype.data(),
@@ -365,7 +433,6 @@ void pr_top(FILE* fp, int indent, const char* title, const t_topology* top, gmx_
         pr_block(fp, indent, "mols", &top->mols, bShowNumbers);
         pr_str(fp, indent, "bIntermolecularInteractions",
                gmx::boolToString(top->bIntermolecularInteractions));
-        pr_blocka(fp, indent, "excls", &top->excls, bShowNumbers);
         pr_idef(fp, indent, "idef", &top->idef, bShowNumbers, bShowParameters);
     }
 }
@@ -457,15 +524,18 @@ static void cmp_cmap(FILE* fp, const gmx_cmap_t* cmap1, const gmx_cmap_t* cmap2,
     }
 }
 
-static void cmp_blocka(FILE* fp, const t_blocka* b1, const t_blocka* b2, const char* s)
+static void cmp_listoflists(FILE*                        fp,
+                            const gmx::ListOfLists<int>& list1,
+                            const gmx::ListOfLists<int>& list2,
+                            const char*                  s)
 {
     char buf[32];
 
     fprintf(fp, "comparing blocka %s\n", s);
-    sprintf(buf, "%s.nr", s);
-    cmp_int(fp, buf, -1, b1->nr, b2->nr);
-    sprintf(buf, "%s.nra", s);
-    cmp_int(fp, buf, -1, b1->nra, b2->nra);
+    sprintf(buf, "%s.numLists", s);
+    cmp_int(fp, buf, -1, list1.ssize(), list2.ssize());
+    sprintf(buf, "%s.numElements", s);
+    cmp_int(fp, buf, -1, list1.numElements(), list2.numElements());
 }
 
 static void compareFfparams(FILE*                 fp,
@@ -534,7 +604,7 @@ static void compareMoltypes(FILE*                              fp,
         compareAtoms(fp, &mt1[i].atoms, &mt2[i].atoms, relativeTolerance, absoluteTolerance);
         compareInteractionLists(fp, &mt1[i].ilist, &mt2[i].ilist);
         std::string buf = gmx::formatString("excls[%d]", i);
-        cmp_blocka(fp, &mt1[i].excls, &mt2[i].excls, buf.c_str());
+        cmp_listoflists(fp, mt1[i].excls, mt2[i].excls, buf.c_str());
     }
 }
 
@@ -609,8 +679,9 @@ void compareMtop(FILE* fp, const gmx_mtop_t& mtop1, const gmx_mtop_t& mtop2, rea
     fprintf(fp, "comparing mtop topology\n");
     cmp_str(fp, "Name", -1, *mtop1.name, *mtop2.name);
     cmp_int(fp, "natoms", -1, mtop1.natoms, mtop2.natoms);
-    cmp_int(fp, "maxres_renum", -1, mtop1.maxres_renum, mtop2.maxres_renum);
-    cmp_int(fp, "maxresnr", -1, mtop1.maxresnr, mtop2.maxresnr);
+    cmp_int(fp, "maxres_renum", -1, mtop1.maxResiduesPerMoleculeToTriggerRenumber(),
+            mtop2.maxResiduesPerMoleculeToTriggerRenumber());
+    cmp_int(fp, "maxresnr", -1, mtop1.maxResNumberNotRenumbered(), mtop2.maxResNumberNotRenumbered());
     cmp_bool(fp, "bIntermolecularInteractions", -1, mtop1.bIntermolecularInteractions,
              mtop2.bIntermolecularInteractions);
     cmp_bool(fp, "haveMoleculeIndices", -1, mtop1.haveMoleculeIndices, mtop2.haveMoleculeIndices);
@@ -674,8 +745,8 @@ int getGroupType(const SimulationGroups& group, SimulationAtomGroupType type, in
 
 void copy_moltype(const gmx_moltype_t* src, gmx_moltype_t* dst)
 {
-    dst->name = src->name;
-    copy_blocka(&src->excls, &dst->excls);
+    dst->name          = src->name;
+    dst->excls         = src->excls;
     t_atoms* atomsCopy = copy_t_atoms(&src->atoms);
     dst->atoms         = *atomsCopy;
     sfree(atomsCopy);
index 1d8f9abc219be1267815038b4a84d2f44322db76..cda4ec7e942e5bba57d907e69566725d709c82f1 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011,2014,2015,2016,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2011,2014,2015,2016,2018, The GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,6 +49,7 @@
 #include "gromacs/topology/idef.h"
 #include "gromacs/topology/symtab.h"
 #include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/listoflists.h"
 #include "gromacs/utility/unique_cptr.h"
 
 enum class SimulationAtomGroupType : int
@@ -83,10 +85,10 @@ struct gmx_moltype_t
     /*! \brief Default copy constructor */
     gmx_moltype_t(const gmx_moltype_t&) = default;
 
-    char**           name;  /**< Name of the molecule type            */
-    t_atoms          atoms; /**< The atoms in this molecule           */
-    InteractionLists ilist; /**< Interaction list with local indices  */
-    t_blocka         excls; /**< The exclusions                       */
+    char**                name;  /**< Name of the molecule type            */
+    t_atoms               atoms; /**< The atoms in this molecule           */
+    InteractionLists      ilist; /**< Interaction list with local indices  */
+    gmx::ListOfLists<int> excls; /**< The exclusions                       */
 };
 
 /*! \brief Block of molecules of the same type, used in gmx_mtop_t */
@@ -131,7 +133,7 @@ struct SimulationGroups
      */
     int numberOfGroupNumbers(SimulationAtomGroupType group) const
     {
-        return gmx::ssize(groupNumbers[group]);
+        return static_cast<int>(groupNumbers[group].size());
     }
 };
 
@@ -176,15 +178,11 @@ struct gmx_mtop_t //NOLINT(clang-analyzer-optin.performance.Padding)
     std::unique_ptr<InteractionLists> intermolecular_ilist = nullptr;
     //! Number of global atoms.
     int natoms = 0;
-    //! Parameter for residue numbering.
-    int maxres_renum = 0;
-    //! The maximum residue number in moltype
-    int maxresnr = -1;
     //! Atomtype properties
     t_atomtypes atomtypes;
     //! Groups of atoms for different purposes
     SimulationGroups groups;
-    //! The symbol table
+    //! The legacy symbol table
     t_symtab symtab;
     //! Tells whether we have valid molecule indices
     bool haveMoleculeIndices = false;
@@ -193,9 +191,33 @@ struct gmx_mtop_t //NOLINT(clang-analyzer-optin.performance.Padding)
      */
     std::vector<int> intermolecularExclusionGroup;
 
-    /* Derived data  below */
+    //! Maximum number of residues in molecule to trigger renumbering of residues
+    int maxResiduesPerMoleculeToTriggerRenumber() const
+    {
+        return maxResiduesPerMoleculeToTriggerRenumber_;
+    }
+    //! Maximum residue number that is not renumbered.
+    int maxResNumberNotRenumbered() const { return maxResNumberNotRenumbered_; }
+    /*! \brief Finalize this data structure.
+     *
+     * Should be called after generating or reading mtop, to set some compute
+     * intesive variables to avoid N^2 operations later on.
+     *
+     * \todo Move into a builder class, once available.
+     */
+    void finalize();
+
+    /* Derived data below */
     //! Indices for each molblock entry for fast lookup of atom properties
     std::vector<MoleculeBlockIndices> moleculeBlockIndices;
+
+private:
+    //! Build the molblock indices
+    void buildMolblockIndices();
+    //! Maximum number of residues in molecule to trigger renumbering of residues
+    int maxResiduesPerMoleculeToTriggerRenumber_ = 0;
+    //! The maximum residue number in moltype that is not renumbered
+    int maxResNumberNotRenumbered_ = -1;
 };
 
 /*! \brief
@@ -206,18 +228,12 @@ struct gmx_mtop_t //NOLINT(clang-analyzer-optin.performance.Padding)
 struct gmx_localtop_t
 {
     //! Constructor used for normal operation, manages own resources.
-    gmx_localtop_t();
-
-    ~gmx_localtop_t();
+    gmx_localtop_t(const gmx_ffparams_t& ffparams);
 
     //! The interaction function definition
-    t_idef idef;
-    //! Atomtype properties
-    t_atomtypes atomtypes;
+    InteractionDefinitions idef;
     //! The exclusions
-    t_blocka excls;
-    //! Flag for domain decomposition so we don't free already freed memory.
-    bool useInDomainDecomp_ = false;
+    gmx::ListOfLists<int> excls;
 };
 
 /* The old topology struct, completely written out, used in analysis tools */
@@ -229,8 +245,8 @@ typedef struct t_topology
     t_atomtypes atomtypes;                   /* Atomtype properties                  */
     t_block     mols;                        /* The molecules                        */
     gmx_bool    bIntermolecularInteractions; /* Inter.mol. int. ?   */
-    t_blocka    excls;                       /* The exclusions                       */
-    t_symtab    symtab;                      /* The symbol table                     */
+    /* Note that the exclusions are not stored in t_topology */
+    t_symtab symtab; /* The symbol table                     */
 } t_topology;
 
 void init_top(t_topology* top);
index cf7ed9d3564a2ddb72ba9bd19d33c185d008bb44..5971a45094ed2eeb266ca86d9df273603156e070 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2008, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -175,11 +176,9 @@ gmx_bool gmx_mtop_bondeds_free_energy(const gmx_mtop_t* mtop)
     return bPert;
 }
 
-void gmx_sort_ilist_fe(t_idef* idef, const real* qA, const real* qB)
+void gmx_sort_ilist_fe(InteractionDefinitions* idef, const real* qA, const real* qB)
 {
     int      ftype, nral, i, ic, ib, a;
-    t_ilist* ilist;
-    t_iatom* iatoms;
     t_iatom* iabuf;
     int      iabuf_nalloc;
 
@@ -188,25 +187,25 @@ void gmx_sort_ilist_fe(t_idef* idef, const real* qA, const real* qB)
         qB = qA;
     }
 
+    bool havePerturbedInteractions = false;
+
     iabuf_nalloc = 0;
     iabuf        = nullptr;
 
-    const t_iparams* iparams = idef->iparams;
-
     for (ftype = 0; ftype < F_NRE; ftype++)
     {
         if (interaction_function[ftype].flags & IF_BOND)
         {
-            ilist  = &idef->il[ftype];
-            iatoms = ilist->iatoms;
-            nral   = NRAL(ftype);
-            ic     = 0;
-            ib     = 0;
-            i      = 0;
-            while (i < ilist->nr)
+            InteractionList* ilist  = &idef->il[ftype];
+            int*             iatoms = ilist->iatoms.data();
+            nral                    = NRAL(ftype);
+            ic                      = 0;
+            ib                      = 0;
+            i                       = 0;
+            while (i < ilist->size())
             {
                 /* Check if this interaction is perturbed */
-                if (ip_q_pert(ftype, iatoms + i, iparams, qA, qB))
+                if (ip_q_pert(ftype, iatoms + i, idef->iparams.data(), qA, qB))
                 {
                     /* Copy to the perturbed buffer */
                     if (ib + 1 + nral > iabuf_nalloc)
@@ -218,6 +217,8 @@ void gmx_sort_ilist_fe(t_idef* idef, const real* qA, const real* qB)
                     {
                         iabuf[ib++] = iatoms[i++];
                     }
+
+                    havePerturbedInteractions = true;
                 }
                 else
                 {
@@ -228,8 +229,8 @@ void gmx_sort_ilist_fe(t_idef* idef, const real* qA, const real* qB)
                     }
                 }
             }
-            /* Now we now the number of non-perturbed interactions */
-            ilist->nr_nonperturbed = ic;
+            /* Now we know the number of non-perturbed interactions */
+            idef->numNonperturbedInteractions[ftype] = ic;
 
             /* Copy the buffer with perturbed interactions to the ilist */
             for (a = 0; a < ib; a++)
@@ -239,13 +240,14 @@ void gmx_sort_ilist_fe(t_idef* idef, const real* qA, const real* qB)
 
             if (debug)
             {
+                const int numNonperturbed = idef->numNonperturbedInteractions[ftype];
                 fprintf(debug, "%s non-pert %d pert %d\n", interaction_function[ftype].longname,
-                        ilist->nr_nonperturbed, ilist->nr - ilist->nr_nonperturbed);
+                        numNonperturbed, ilist->size() - numNonperturbed);
             }
         }
     }
 
     sfree(iabuf);
 
-    idef->ilsort = ilsortFE_SORTED;
+    idef->ilsort = (havePerturbedInteractions ? ilsortFE_SORTED : ilsortNO_FE);
 }
index 6bc89eb68e76bc5c1b36afd5d037cd108a422445..2237b659031cf9fbf8b142bc09089bff87e3685b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2008,2009,2010,2013,2014,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2008,2009,2010,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -39,7 +40,7 @@
 #include "gromacs/utility/real.h"
 
 struct gmx_mtop_t;
-struct t_idef;
+class InteractionDefinitions;
 
 /* Returns if there are perturbed bonded interactions */
 gmx_bool gmx_mtop_bondeds_free_energy(const struct gmx_mtop_t* mtop);
@@ -47,6 +48,6 @@ gmx_bool gmx_mtop_bondeds_free_energy(const struct gmx_mtop_t* mtop);
 /* Sort all the bonded ilists in idef to have the perturbed ones at the end
  * and set nr_nr_nonperturbed in ilist.
  */
-void gmx_sort_ilist_fe(struct t_idef* idef, const real* qA, const real* qB);
+void gmx_sort_ilist_fe(InteractionDefinitions* idef, const real* qA, const real* qB);
 
 #endif
index ce46bdae5b6012c09c552e5bc81794724ec5413c..b37f14d4c8c49c03ca4031b0cf496bb66c7487c5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7baabd02a5a4e9e3933a39271de7fa96ed746868..c45a3b7878908fcf456eb7641d4a754c349115b4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -147,9 +147,9 @@ double TrajectoryFrame::time() const
     return frame_.time;
 }
 
-int TrajectoryFrame::pbc() const
+PbcType TrajectoryFrame::pbc() const
 {
-    return frame_.ePBC;
+    return frame_.pbcType;
 }
 
 ArrayRef<const RVec> TrajectoryFrame::x() const
index 93d26dc78dec6408a892989a86cc6ed0d9a33034..0adc4dbfad36fff25714edad40dc738d3ebef170 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,6 +53,7 @@
 #include "gromacs/utility/real.h"
 
 struct t_atoms;
+enum class PbcType : int;
 
 typedef struct t_trxframe // NOLINT (clang-analyzer-optin.performance.Padding)
 {
@@ -79,7 +81,7 @@ typedef struct t_trxframe // NOLINT (clang-analyzer-optin.performance.Padding)
     gmx_bool bBox;
     matrix   box; /* the 3 box vectors                */
     gmx_bool bPBC;
-    int      ePBC; /* the type of pbc                  */
+    PbcType  pbcType; /* the type of pbc                  */
     gmx_bool bIndex;
     int*     index; /* atom indices of contained coordinates */
 } t_trxframe;
@@ -126,7 +128,7 @@ public:
     //! Time read from the trajectory file frame.
     double time() const;
     //! The PBC characteristics of the box.
-    int pbc() const;
+    PbcType pbc() const;
     //! Get a view of position coordinates of the frame (which could be empty).
     ArrayRef<const RVec> x() const;
     //! Get a view of velocity coordinates of the frame (which could be empty).
index 804d251f319849ced44cd7a350f72094e682e493..9065899640aa867f90ee23c6fbf5f83008f016a2 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2010,2013,2014,2015,2018 by the GROMACS development team.
+# Copyright (c) 2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 06e289a4a46d4b6a5879589ec370c83fcf9d0ed1..263fa1cbe88be0e3acb6e1cb4ca933f3b369d97b 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -105,7 +105,7 @@ public:
          const SelectionCollection&         selections);
 
     //! Checks whether the given AnalysisData has been initialized.
-    bool isInitialized(const AnalysisData& data) const;
+    static bool isInitialized(const AnalysisData& data);
 
     //! Keeps a data handle for each AnalysisData object.
     HandleContainer handles_;
@@ -130,7 +130,7 @@ TrajectoryAnalysisModuleData::Impl::Impl(TrajectoryAnalysisModule*          modu
     }
 }
 
-bool TrajectoryAnalysisModuleData::Impl::isInitialized(const AnalysisData& data) const
+bool TrajectoryAnalysisModuleData::Impl::isInitialized(const AnalysisData& data)
 {
     for (int i = 0; i < data.dataSetCount(); ++i)
     {
index 6326b81dc9c895f63c5392ae1815bd10b4553277..7462f19badb02604ef287afe0524015bc4c2d57e 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -129,7 +130,7 @@ public:
      *
      * Does not throw.
      */
-    Selection parallelSelection(const Selection& selection);
+    static Selection parallelSelection(const Selection& selection);
     /*! \brief
      * Returns a set of selection that corresponds to the given selections.
      *
@@ -139,7 +140,7 @@ public:
      *
      * \see parallelSelection()
      */
-    SelectionList parallelSelections(const SelectionList& selections);
+    static SelectionList parallelSelections(const SelectionList& selections);
 
 protected:
     /*! \brief
index 68378ec95b240a174ec70a6a6df0a77c065cdae3..a83eb39777a8aece5b963f8e047c862229f4478b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -63,7 +64,7 @@ class TrajectoryAnalysisSettings::Impl
 public:
     //! Initializes the default values for the settings object.
     Impl() :
-        timeUnit(TimeUnit_Default),
+        timeUnit(TimeUnit::Default),
         flags(0),
         frflags(0),
         bRmPBC(true),
index 755782d8270a2aabdfe376922121b46e184b9336..fb0d2e17bf538ae4b596e22257edc54338f674e6 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -138,7 +138,7 @@ int RunnerModule::run()
         t_trxframe& frame = common_.frame();
         if (ppbc != nullptr)
         {
-            set_pbc(ppbc, topology.ePBC(), frame.box);
+            set_pbc(ppbc, topology.pbcType(), frame.box);
         }
 
         selections_.evaluate(&frame, ppbc);
index 246f70d9e722c796d668efd48dc5d88ff7fe6654..8dcb40202eba379787f9add9e93782a12a8e1f2f 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 9c266bc03e5c8fef557daf2e18fd5454d869b5c7..b8b9d85844bf1648915cc07a47c6fbf57e25a26e 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 344c4ee748fec8faff2de3a1989342518091272f..d4a8f18c0261f0f3dda03ee1a3b381e04b89abab 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2011-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -64,6 +64,7 @@
 #include "gromacs/trajectory/trajectoryframe.h"
 #include "gromacs/trajectoryanalysis/analysissettings.h"
 #include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/enumerationhelpers.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
@@ -237,27 +238,32 @@ private:
  */
 
 //! How to interpret the selections in -group1.
-enum Group1Type
+enum class Group1Type : int
 {
-    Group1Type_Angle,
-    Group1Type_Dihedral,
-    Group1Type_Vector,
-    Group1Type_Plane
+    Angle,
+    Dihedral,
+    Vector,
+    Plane,
+    Count
 };
 //! How to interpret the selections in -group2.
-enum Group2Type
+enum class Group2Type : int
 {
-    Group2Type_None,
-    Group2Type_Vector,
-    Group2Type_Plane,
-    Group2Type_TimeZero,
-    Group2Type_Z,
-    Group2Type_SphereNormal
+    None,
+    Vector,
+    Plane,
+    TimeZero,
+    Z,
+    SphereNormal,
+    Count
 };
 //! String values corresponding to Group1Type.
-const char* const cGroup1TypeEnum[] = { "angle", "dihedral", "vector", "plane" };
+const EnumerationArray<Group1Type, const char*> c_group1TypeEnumNames = { { "angle", "dihedral",
+                                                                            "vector", "plane" } };
 //! String values corresponding to Group2Type.
-const char* const cGroup2TypeEnum[] = { "none", "vector", "plane", "t0", "z", "sphnorm" };
+const EnumerationArray<Group2Type, const char*> c_group2TypeEnumNames = {
+    { "none", "vector", "plane", "t0", "z", "sphnorm" }
+};
 
 class Angle : public TrajectoryAnalysisModule
 {
@@ -304,8 +310,8 @@ private:
 Angle::Angle() :
     sel1info_(nullptr),
     sel2info_(nullptr),
-    g1type_(Group1Type_Angle),
-    g2type_(Group2Type_None),
+    g1type_(Group1Type::Angle),
+    g2type_(Group2Type::None),
     binWidth_(1.0),
     natoms1_(0),
     natoms2_(0)
@@ -393,10 +399,12 @@ void Angle::initOptions(IOptionsContainer* options, TrajectoryAnalysisSettings*
                                .defaultBasename("anghist")
                                .description("Histogram of the angles"));
 
+    options->addOption(EnumOption<Group1Type>("g1")
+                               .enumValue(c_group1TypeEnumNames)
+                               .store(&g1type_)
+                               .description("Type of analysis/first vector group"));
     options->addOption(
-            EnumOption<Group1Type>("g1").enumValue(cGroup1TypeEnum).store(&g1type_).description("Type of analysis/first vector group"));
-    options->addOption(
-            EnumOption<Group2Type>("g2").enumValue(cGroup2TypeEnum).store(&g2type_).description("Type of second vector group"));
+            EnumOption<Group2Type>("g2").enumValue(c_group2TypeEnumNames).store(&g2type_).description("Type of second vector group"));
     options->addOption(
             DoubleOption("binw").store(&binWidth_).description("Binwidth for -oh in degrees"));
 
@@ -411,9 +419,9 @@ void Angle::initOptions(IOptionsContainer* options, TrajectoryAnalysisSettings*
 
 void Angle::optionsFinished(TrajectoryAnalysisSettings* /* settings */)
 {
-    const bool bSingle = (g1type_ == Group1Type_Angle || g1type_ == Group1Type_Dihedral);
+    const bool bSingle = (g1type_ == Group1Type::Angle || g1type_ == Group1Type::Dihedral);
 
-    if (bSingle && g2type_ != Group2Type_None)
+    if (bSingle && g2type_ != Group2Type::None)
     {
         GMX_THROW(
                 InconsistentInputError("Cannot use a second group (-g2) with "
@@ -425,7 +433,7 @@ void Angle::optionsFinished(TrajectoryAnalysisSettings* /* settings */)
                 InconsistentInputError("Cannot provide a second selection "
                                        "(-group2) with -g1 angle or dihedral"));
     }
-    if (!bSingle && g2type_ == Group2Type_None)
+    if (!bSingle && g2type_ == Group2Type::None)
     {
         GMX_THROW(
                 InconsistentInputError("Should specify a second group (-g2) "
@@ -435,20 +443,20 @@ void Angle::optionsFinished(TrajectoryAnalysisSettings* /* settings */)
     // Set up the number of positions per angle.
     switch (g1type_)
     {
-        case Group1Type_Angle: natoms1_ = 3; break;
-        case Group1Type_Dihedral: natoms1_ = 4; break;
-        case Group1Type_Vector: natoms1_ = 2; break;
-        case Group1Type_Plane: natoms1_ = 3; break;
+        case Group1Type::Angle: natoms1_ = 3; break;
+        case Group1Type::Dihedral: natoms1_ = 4; break;
+        case Group1Type::Vector: natoms1_ = 2; break;
+        case Group1Type::Plane: natoms1_ = 3; break;
         default: GMX_THROW(InternalError("invalid -g1 value"));
     }
     switch (g2type_)
     {
-        case Group2Type_None: natoms2_ = 0; break;
-        case Group2Type_Vector: natoms2_ = 2; break;
-        case Group2Type_Plane: natoms2_ = 3; break;
-        case Group2Type_TimeZero: natoms2_ = 0; break;
-        case Group2Type_Z: natoms2_ = 0; break;
-        case Group2Type_SphereNormal: natoms2_ = 1; break;
+        case Group2Type::None: natoms2_ = 0; break;
+        case Group2Type::Vector: natoms2_ = 2; break;
+        case Group2Type::Plane: natoms2_ = 3; break;
+        case Group2Type::TimeZero: // Intended to fall through
+        case Group2Type::Z: natoms2_ = 0; break;
+        case Group2Type::SphereNormal: natoms2_ = 1; break;
         default: GMX_THROW(InternalError("invalid -g2 value"));
     }
     if (natoms2_ == 0 && sel2info_->isSet())
@@ -496,7 +504,7 @@ void Angle::initFromSelections(const SelectionList& sel1, const SelectionList& s
                                      "divisible by %d",
                                      static_cast<int>(g + 1), natoms2_)));
             }
-            if (g2type_ == Group2Type_SphereNormal && posCount2 != 1)
+            if (g2type_ == Group2Type::SphereNormal && posCount2 != 1)
             {
                 GMX_THROW(InconsistentInputError(
                         "The second group should contain a single position with -g2 sphnorm"));
@@ -575,10 +583,10 @@ void Angle::initAnalysis(const TrajectoryAnalysisSettings& settings, const Topol
     {
         angles_.setColumnCount(i, angleCount_[i]);
     }
-    double histogramMin = (g1type_ == Group1Type_Dihedral ? -180.0 : 0);
+    double histogramMin = (g1type_ == Group1Type::Dihedral ? -180.0 : 0);
     histogramModule_->init(histogramFromRange(histogramMin, 180.0).binWidth(binWidth_).includeAll());
 
-    if (g2type_ == Group2Type_TimeZero)
+    if (g2type_ == Group2Type::TimeZero)
     {
         vt0_.resize(sel1_.size());
         for (size_t g = 0; g < sel1_.size(); ++g)
@@ -676,8 +684,8 @@ void calc_vec(int natoms, rvec x[], t_pbc* pbc, rvec xout, rvec cout)
 void Angle::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryAnalysisModuleData* pdata)
 {
     AnalysisDataHandle   dh   = pdata->dataHandle(angles_);
-    const SelectionList& sel1 = pdata->parallelSelections(sel1_);
-    const SelectionList& sel2 = pdata->parallelSelections(sel2_);
+    const SelectionList& sel1 = TrajectoryAnalysisModuleData::parallelSelections(sel1_);
+    const SelectionList& sel2 = TrajectoryAnalysisModuleData::parallelSelections(sel2_);
 
     checkSelections(sel1, sel2);
 
@@ -701,8 +709,8 @@ void Angle::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryA
 
         switch (g2type_)
         {
-            case Group2Type_Z: v2[ZZ] = 1.0; break;
-            case Group2Type_SphereNormal: copy_rvec(sel2_[g].position(0).x(), c2); break;
+            case Group2Type::Z: v2[ZZ] = 1.0; break;
+            case Group2Type::SphereNormal: copy_rvec(sel2_[g].position(0).x(), c2); break;
             default:
                 // do nothing
                 break;
@@ -724,7 +732,7 @@ void Angle::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryA
             iter1.getCurrentPositions(x);
             switch (g1type_)
             {
-                case Group1Type_Angle:
+                case Group1Type::Angle:
                     if (pbc)
                     {
                         pbc_dx(pbc, x[0], x[1], v1);
@@ -737,7 +745,7 @@ void Angle::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryA
                     }
                     angle = gmx_angle(v1, v2);
                     break;
-                case Group1Type_Dihedral:
+                case Group1Type::Dihedral:
                 {
                     rvec dx[3];
                     if (pbc)
@@ -762,17 +770,17 @@ void Angle::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryA
                     }
                     break;
                 }
-                case Group1Type_Vector:
-                case Group1Type_Plane:
+                case Group1Type::Vector:
+                case Group1Type::Plane:
                     calc_vec(natoms1_, x, pbc, v1, c1);
                     switch (g2type_)
                     {
-                        case Group2Type_Vector:
-                        case Group2Type_Plane:
+                        case Group2Type::Vector:
+                        case Group2Type::Plane:
                             iter2.getCurrentPositions(x);
                             calc_vec(natoms2_, x, pbc, v2, c2);
                             break;
-                        case Group2Type_TimeZero:
+                        case Group2Type::TimeZero:
                             // FIXME: This is not parallelizable.
                             if (frnr == 0)
                             {
@@ -780,8 +788,8 @@ void Angle::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryA
                             }
                             copy_rvec(vt0_[g][n], v2);
                             break;
-                        case Group2Type_Z: c1[XX] = c1[YY] = 0.0; break;
-                        case Group2Type_SphereNormal:
+                        case Group2Type::Z: c1[XX] = c1[YY] = 0.0; break;
+                        case Group2Type::SphereNormal:
                             if (pbc)
                             {
                                 pbc_dx(pbc, c1, c2, v2);
index 3a77d338531091d617ade17b72894065e205d32e..5fe70281ec29b711676a8a7d9d402ff0df57c4fa 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013,2014,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index be07f61f5ef5fb99787942761199db22506306f4..9673a8aaec87f68964cb9a7239a3af7fad02e84a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,6 +48,7 @@
 
 #include "gromacs/coordinateio/coordinatefile.h"
 #include "gromacs/coordinateio/requirements.h"
+#include "gromacs/fileio/trxio.h"
 #include "gromacs/options/filenameoption.h"
 #include "gromacs/options/ioptionscontainer.h"
 #include "gromacs/selection/selectionoption.h"
index b23cd8ff27e5a87d7f8926c6b2b6c7457d1405bc..6acf73ab1d2a344614b13634fa0c0ed342b42014 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -324,7 +324,7 @@ void Distance::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, Trajecto
 {
     AnalysisDataHandle   distHandle = pdata->dataHandle(distances_);
     AnalysisDataHandle   xyzHandle  = pdata->dataHandle(xyz_);
-    const SelectionList& sel        = pdata->parallelSelections(sel_);
+    const SelectionList& sel        = TrajectoryAnalysisModuleData::parallelSelections(sel_);
 
     checkSelections(sel);
 
index 571a5951cfa5ad50399aa6788a996fffa77a074a..ef2709b7d6d4500321011340aa842f6919b67537 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 6fdfd8f9d625e55138ad403057cd09f0081f1bf3..7e92eb0e81c21ded479fc7f7626f6fdf6464f20f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -48,6 +48,7 @@
 
 #include "gromacs/coordinateio/coordinatefile.h"
 #include "gromacs/coordinateio/requirements.h"
+#include "gromacs/fileio/trxio.h"
 #include "gromacs/options/filenameoption.h"
 #include "gromacs/options/ioptionscontainer.h"
 #include "gromacs/selection/selectionoption.h"
index 1b9362b11d667e3521485b7466446d9b3dc5b194..2597e359ea3306133075e9a7ffbaa7b782a17761 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -302,7 +303,7 @@ void FreeVolume::initAnalysis(const TrajectoryAnalysisSettings& settings, const
 void FreeVolume::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryAnalysisModuleData* pdata)
 {
     AnalysisDataHandle                 dh  = pdata->dataHandle(data_);
-    const Selection&                   sel = pdata->parallelSelection(sel_);
+    const Selection&                   sel = TrajectoryAnalysisModuleData::parallelSelection(sel_);
     gmx::UniformRealDistribution<real> dist;
 
     GMX_RELEASE_ASSERT(nullptr != pbc, "You have no periodic boundary conditions");
index 41109ee5487cb015aa4aabe3c189b9a641e47358..f1d470779a7d743d5a5f15617fbb7bba056cbf71 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -78,25 +78,28 @@ namespace
 //! \{
 
 //! Enum value to store the selected value for `-type`.
-enum DistanceType
+enum class DistanceType : int
 {
-    eDistanceType_Min,
-    eDistanceType_Max
+    Min,
+    Max,
+    Count
 };
 
 //! Enum value to store the selected value for `-refgrouping`/`-selgrouping`.
-enum GroupType
+enum class GroupType : int
 {
-    eGroupType_All,
-    eGroupType_Residue,
-    eGroupType_Molecule,
-    eGroupType_None
+    All,
+    Residue,
+    Molecule,
+    None,
+    Count
 };
 
 //! Strings corresponding to DistanceType.
-const char* const c_distanceTypes[] = { "min", "max" };
+const EnumerationArray<DistanceType, const char*> c_distanceTypeNames = { { "min", "max" } };
 //! Strings corresponding to GroupType.
-const char* const c_groupTypes[] = { "all", "res", "mol", "none" };
+const EnumerationArray<GroupType, const char*> c_groupTypeNames = { { "all", "res", "mol",
+                                                                      "none" } };
 
 /*! \brief
  * Implements `gmx pairdist` trajectory analysis module.
@@ -165,9 +168,9 @@ private:
 
 PairDistance::PairDistance() :
     cutoff_(0.0),
-    distanceType_(eDistanceType_Min),
-    refGroupType_(eGroupType_All),
-    selGroupType_(eGroupType_All),
+    distanceType_(DistanceType::Min),
+    refGroupType_(GroupType::All),
+    selGroupType_(GroupType::All),
     refGroupCount_(0),
     maxGroupCount_(0),
     initialDist2_(0.0),
@@ -228,17 +231,17 @@ void PairDistance::initOptions(IOptionsContainer* options, TrajectoryAnalysisSet
             DoubleOption("cutoff").store(&cutoff_).description("Maximum distance to consider"));
     options->addOption(EnumOption<DistanceType>("type")
                                .store(&distanceType_)
-                               .enumValue(c_distanceTypes)
+                               .enumValue(c_distanceTypeNames)
                                .description("Type of distances to calculate"));
     options->addOption(
             EnumOption<GroupType>("refgrouping")
                     .store(&refGroupType_)
-                    .enumValue(c_groupTypes)
+                    .enumValue(c_groupTypeNames)
                     .description("Grouping of -ref positions to compute the min/max over"));
     options->addOption(
             EnumOption<GroupType>("selgrouping")
                     .store(&selGroupType_)
-                    .enumValue(c_groupTypes)
+                    .enumValue(c_groupTypeNames)
                     .description("Grouping of -sel positions to compute the min/max over"));
 
     options->addOption(SelectionOption("ref").store(&refSel_).required().description(
@@ -248,15 +251,16 @@ void PairDistance::initOptions(IOptionsContainer* options, TrajectoryAnalysisSet
 }
 
 //! Helper function to initialize the grouping for a selection.
-int initSelectionGroups(Selection* sel, const gmx_mtop_t* top, int type)
+int initSelectionGroups(Selection* sel, const gmx_mtop_t* top, GroupType type)
 {
     e_index_t indexType = INDEX_UNKNOWN;
     switch (type)
     {
-        case eGroupType_All: indexType = INDEX_ALL; break;
-        case eGroupType_Residue: indexType = INDEX_RES; break;
-        case eGroupType_Molecule: indexType = INDEX_MOL; break;
-        case eGroupType_None: indexType = INDEX_ATOM; break;
+        case GroupType::All: indexType = INDEX_ALL; break;
+        case GroupType::Residue: indexType = INDEX_RES; break;
+        case GroupType::Molecule: indexType = INDEX_MOL; break;
+        case GroupType::None: indexType = INDEX_ATOM; break;
+        case GroupType::Count: GMX_THROW(InternalError("Invalid GroupType"));
     }
     return sel->initOriginalIdsToGroup(top, indexType);
 }
@@ -280,7 +284,7 @@ void PairDistance::initAnalysis(const TrajectoryAnalysisSettings& settings, cons
     {
         AnalysisDataPlotModulePointer plotm(new AnalysisDataPlotModule(settings.plotSettings()));
         plotm->setFileName(fnDist_);
-        if (distanceType_ == eDistanceType_Max)
+        if (distanceType_ == DistanceType::Max)
         {
             plotm->setTitle("Maximum distance");
         }
@@ -310,7 +314,7 @@ void PairDistance::initAnalysis(const TrajectoryAnalysisSettings& settings, cons
     {
         initialDist2_ = cutoff_ * cutoff_;
     }
-    if (distanceType_ == eDistanceType_Max)
+    if (distanceType_ == DistanceType::Max)
     {
         initialDist2_ = 0.0;
     }
@@ -406,8 +410,8 @@ TrajectoryAnalysisModuleDataPointer PairDistance::startFrames(const AnalysisData
 void PairDistance::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryAnalysisModuleData* pdata)
 {
     AnalysisDataHandle      dh         = pdata->dataHandle(distances_);
-    const Selection&        refSel     = pdata->parallelSelection(refSel_);
-    const SelectionList&    sel        = pdata->parallelSelections(sel_);
+    const Selection&        refSel     = TrajectoryAnalysisModuleData::parallelSelection(refSel_);
+    const SelectionList&    sel        = TrajectoryAnalysisModuleData::parallelSelections(sel_);
     PairDistanceModuleData& frameData  = *static_cast<PairDistanceModuleData*>(pdata);
     std::vector<real>&      distArray  = frameData.distArray_;
     std::vector<int>&       countArray = frameData.countArray_;
@@ -444,7 +448,7 @@ void PairDistance::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, Traj
             const int                selIndex = selPos.mappedId();
             const int                index    = selIndex * refGroupCount_ + refIndex;
             const real               r2       = pair.distance2();
-            if (distanceType_ == eDistanceType_Min)
+            if (distanceType_ == DistanceType::Min)
             {
                 if (distArray[index] > r2)
                 {
@@ -496,7 +500,7 @@ void PairDistance::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, Traj
                     // update the distance if necessary and the count.
                     if (countArray[index] < totalCount)
                     {
-                        if (distanceType_ == eDistanceType_Max)
+                        if (distanceType_ == DistanceType::Max)
                         {
                             distArray[index] = cutoff2_;
                         }
index cc45dadebcee5dcbef639e6e4dce08c45b02bd10..bc088be33f9083172c1da5ab25d694ce18a2793a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -91,23 +92,27 @@ namespace
  */
 
 //! Normalization for the computed distribution.
-enum Normalization
+enum class Normalization : int
 {
-    Normalization_Rdf,
-    Normalization_NumberDensity,
-    Normalization_None
+    Rdf,
+    NumberDensity,
+    None,
+    Count
 };
 //! String values corresponding to Normalization.
-const char* const c_NormalizationEnum[] = { "rdf", "number_density", "none" };
+const EnumerationArray<Normalization, const char*> c_normalizationNames = {
+    { "rdf", "number_density", "none" }
+};
 //! Whether to compute RDF wrt. surface of the reference group.
-enum SurfaceType
+enum class SurfaceType : int
 {
-    SurfaceType_None,
-    SurfaceType_Molecule,
-    SurfaceType_Residue
+    None,
+    Molecule,
+    Residue,
+    Count
 };
 //! String values corresponding to SurfaceType.
-const char* const c_SurfaceEnum[] = { "no", "mol", "res" };
+const EnumerationArray<SurfaceType, const char*> c_surfaceTypeNames = { { "no", "mol", "res" } };
 
 /*! \brief
  * Implements `gmx rdf` trajectory analysis module.
@@ -201,14 +206,14 @@ private:
 };
 
 Rdf::Rdf() :
-    surface_(SurfaceType_None),
+    surface_(SurfaceType::None),
     pairCounts_(new AnalysisDataSimpleHistogramModule()),
     normAve_(new AnalysisDataAverageModule()),
     localTop_(nullptr),
     binwidth_(0.002),
     cutoff_(0.0),
     rmax_(0.0),
-    normalization_(Normalization_Rdf),
+    normalization_(Normalization::Rdf),
     bNormalizationSet_(false),
     bXY_(false),
     bExclusions_(false),
@@ -296,7 +301,7 @@ void Rdf::initOptions(IOptionsContainer* options, TrajectoryAnalysisSettings* se
 
     options->addOption(DoubleOption("bin").store(&binwidth_).description("Bin width (nm)"));
     options->addOption(EnumOption<Normalization>("norm")
-                               .enumValue(c_NormalizationEnum)
+                               .enumValue(c_normalizationNames)
                                .store(&normalization_)
                                .storeIsSet(&bNormalizationSet_)
                                .description("Normalization"));
@@ -310,7 +315,7 @@ void Rdf::initOptions(IOptionsContainer* options, TrajectoryAnalysisSettings* se
             DoubleOption("rmax").store(&rmax_).description("Largest distance (nm) to calculate"));
 
     options->addOption(EnumOption<SurfaceType>("surf")
-                               .enumValue(c_SurfaceEnum)
+                               .enumValue(c_surfaceTypeNames)
                                .store(&surface_)
                                .description("RDF with respect to the surface of the reference"));
 
@@ -322,15 +327,15 @@ void Rdf::initOptions(IOptionsContainer* options, TrajectoryAnalysisSettings* se
 
 void Rdf::optionsFinished(TrajectoryAnalysisSettings* settings)
 {
-    if (surface_ != SurfaceType_None)
+    if (surface_ != SurfaceType::None)
     {
         settings->setFlag(TrajectoryAnalysisSettings::efRequireTop);
 
-        if (bNormalizationSet_ && normalization_ != Normalization_None)
+        if (bNormalizationSet_ && normalization_ != Normalization::None)
         {
             GMX_THROW(InconsistentInputError("-surf cannot be combined with -norm"));
         }
-        normalization_ = Normalization_None;
+        normalization_ = Normalization::None;
         if (bExclusions_)
         {
             GMX_THROW(InconsistentInputError("-surf cannot be combined with -excl"));
@@ -358,14 +363,14 @@ void Rdf::initAnalysis(const TrajectoryAnalysisSettings& settings, const Topolog
 
     normFactors_.setColumnCount(0, sel_.size() + 1);
 
-    const bool bSurface = (surface_ != SurfaceType_None);
+    const bool bSurface = (surface_ != SurfaceType::None);
     if (bSurface)
     {
         if (!refSel_.hasOnlyAtoms())
         {
             GMX_THROW(InconsistentInputError("-surf only works with -ref that consists of atoms"));
         }
-        const e_index_t type = (surface_ == SurfaceType_Molecule ? INDEX_MOL : INDEX_RES);
+        const e_index_t type = (surface_ == SurfaceType::Molecule ? INDEX_MOL : INDEX_RES);
         surfaceGroupCount_   = refSel_.initOriginalIdsToGroup(top.mtop(), type);
     }
 
@@ -386,7 +391,7 @@ void Rdf::initAnalysis(const TrajectoryAnalysisSettings& settings, const Topolog
             }
         }
         localTop_ = top.expandedTopology();
-        if (localTop_->excls.nr == 0)
+        if (localTop_->excls.empty())
         {
             GMX_THROW(InconsistentInputError(
                     "-excl is set, but the file provided to -s does not define exclusions"));
@@ -408,7 +413,7 @@ void Rdf::initAfterFirstFrame(const TrajectoryAnalysisSettings& settings, const
             {
                 box[ZZ][ZZ] = 2 * std::max(box[XX][XX], box[YY][YY]);
             }
-            rmax_ = std::sqrt(0.99 * 0.99 * max_cutoff2(bXY_ ? epbcXY : epbcXYZ, box));
+            rmax_ = std::sqrt(0.99 * 0.99 * max_cutoff2(bXY_ ? PbcType::XY : PbcType::Xyz, box));
         }
         else
         {
@@ -473,8 +478,8 @@ void Rdf::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryAna
 {
     AnalysisDataHandle   dh        = pdata->dataHandle(pairDist_);
     AnalysisDataHandle   nh        = pdata->dataHandle(normFactors_);
-    const Selection&     refSel    = pdata->parallelSelection(refSel_);
-    const SelectionList& sel       = pdata->parallelSelections(sel_);
+    const Selection&     refSel    = TrajectoryAnalysisModuleData::parallelSelection(refSel_);
+    const SelectionList& sel       = TrajectoryAnalysisModuleData::parallelSelections(sel_);
     RdfModuleData&       frameData = *static_cast<RdfModuleData*>(pdata);
     const bool           bSurface  = !frameData.surfaceDist2_.empty();
 
@@ -599,7 +604,7 @@ void Rdf::finishAnalysis(int /*nframes*/)
     // through the dataset registration mechanism.
     AverageHistogramPointer finalRdf = pairCounts_->averager().resampleDoubleBinWidth(true);
 
-    if (normalization_ != Normalization_None)
+    if (normalization_ != Normalization::None)
     {
         // Normalize by the volume of the bins (volume of sphere segments or
         // length of circle segments).
@@ -625,7 +630,7 @@ void Rdf::finishAnalysis(int /*nframes*/)
         }
         finalRdf->scaleAllByVector(invBinVolume.data());
 
-        if (normalization_ == Normalization_Rdf)
+        if (normalization_ == Normalization::Rdf)
         {
             // Normalize by particle density.
             for (size_t g = 0; g < sel_.size(); ++g)
index 06e20da4256b1a565d9291dc1fb63f4c0c50b477..502f51d07300fce28869d575e297d99cb69741a6 100644 (file)
@@ -3,7 +3,9 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2006, The GROMACS development team.
- * Copyright (c) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2008,2009,2010,2011,2012 by the GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -114,12 +116,12 @@ struct t_conect
 void add_rec(t_conect c[], int i, int j, real d2)
 {
     if (c[i].aa == -1)
-    {
+    { // NOLINT bugprone-branch-clone
         c[i].aa  = j;
         c[i].d2a = d2;
     }
     else if (c[i].ab == -1)
-    {
+    { // NOLINT bugprone-branch-clone
         c[i].ab  = j;
         c[i].d2b = d2;
     }
@@ -203,7 +205,7 @@ void connolly_plot(const char*  fn,
                    rvec         x[],
                    t_atoms*     atoms,
                    t_symtab*    symtab,
-                   int          ePBC,
+                   PbcType      pbcType,
                    const matrix box,
                    gmx_bool     bIncludeSolute)
 {
@@ -252,7 +254,7 @@ void connolly_plot(const char*  fn,
         }
         atoms->nr   = i0 + ndots;
         atoms->nres = r0 + 1;
-        write_sto_conf(fn, title, atoms, xnew, nullptr, ePBC, const_cast<rvec*>(box));
+        write_sto_conf(fn, title, atoms, xnew, nullptr, pbcType, const_cast<rvec*>(box));
         atoms->nres = r0;
         atoms->nr   = i0;
     }
@@ -276,7 +278,7 @@ void connolly_plot(const char*  fn,
             aaa.pdbinfo[ii0].occup  = 0.0;
         }
         aaa.nr = ndots;
-        write_sto_conf(fn, title, &aaa, xnew, nullptr, ePBC, const_cast<rvec*>(box));
+        write_sto_conf(fn, title, &aaa, xnew, nullptr, pbcType, const_cast<rvec*>(box));
         do_conect(fn, ndots, xnew);
         done_atom(&aaa);
     }
@@ -547,7 +549,7 @@ void Sasa::initAnalysis(const TrajectoryAnalysisSettings& settings, const Topolo
     }
 
     please_cite(stderr, "Eisenhaber95");
-    // if ((top.ePBC() != epbcXYZ) || (TRICLINIC(fr.box)))
+    // if ((top.pbcType() != PbcType::Xyz) || (TRICLINIC(fr.box)))
     //{
     //    fprintf(stderr, "\n\nWARNING: non-rectangular boxes may give erroneous results or crashes.\n"
     //            "Analysis based on vacuum simulations (with the possibility of evaporation)\n"
@@ -904,8 +906,8 @@ void Sasa::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryAn
     AnalysisDataHandle   aah        = pdata->dataHandle(atomArea_);
     AnalysisDataHandle   rah        = pdata->dataHandle(residueArea_);
     AnalysisDataHandle   vh         = pdata->dataHandle(volume_);
-    const Selection&     surfaceSel = pdata->parallelSelection(surfaceSel_);
-    const SelectionList& outputSel  = pdata->parallelSelections(outputSel_);
+    const Selection&     surfaceSel = TrajectoryAnalysisModuleData::parallelSelection(surfaceSel_);
+    const SelectionList& outputSel  = TrajectoryAnalysisModuleData::parallelSelections(outputSel_);
     SasaModuleData&      frameData  = *static_cast<SasaModuleData*>(pdata);
 
     const bool bResAt    = !frameData.res_a_.empty();
@@ -984,7 +986,7 @@ void Sasa::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryAn
         // one else uses the topology after initialization, it may just work
         // even with future parallelization.
         connolly_plot(fnConnolly_.c_str(), nsurfacedots, surfacedots, fr.x, atoms_.get(),
-                      &mtop_->symtab, fr.ePBC, fr.box, bIncludeSolute_);
+                      &mtop_->symtab, fr.pbcType, fr.box, bIncludeSolute_);
     }
 
     ah.startFrame(frnr, fr.time);
index 9979991ed2705a61c1a0a52e932113f3caa2c454..77fec1d469a62519c7d188f265c728308ce54775 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -255,22 +257,26 @@ void IndexFileWriterModule::dataFinished()
  */
 
 //! How to identify residues in output files.
-enum ResidueNumbering
+enum class ResidueNumbering : int
 {
-    ResidueNumbering_ByNumber,
-    ResidueNumbering_ByIndex
+    ByNumber,
+    ByIndex,
+    Count
 };
 //! Which atoms to write out to PDB files.
-enum PdbAtomsSelection
+enum class PdbAtomsSelection : int
 {
-    PdbAtomsSelection_All,
-    PdbAtomsSelection_MaxSelection,
-    PdbAtomsSelection_Selected
+    All,
+    MaxSelection,
+    Selected,
+    Count
 };
 //! String values corresponding to ResidueNumbering.
-const char* const cResNumberEnum[] = { "number", "index" };
+const EnumerationArray<ResidueNumbering, const char*> c_residueNumberingTypeNames = { { "number",
+                                                                                        "index" } };
 //! String values corresponding to PdbAtomsSelection.
-const char* const cPDBAtomsEnum[] = { "all", "maxsel", "selected" };
+const EnumerationArray<PdbAtomsSelection, const char*> c_pdbAtomsTypeNames = { { "all", "maxsel",
+                                                                                 "selected" } };
 
 class Select : public TrajectoryAnalysisModule
 {
@@ -320,8 +326,8 @@ Select::Select() :
     bFracNorm_(false),
     bResInd_(false),
     bCumulativeLifetimes_(true),
-    resNumberType_(ResidueNumbering_ByNumber),
-    pdbAtoms_(PdbAtomsSelection_All),
+    resNumberType_(ResidueNumbering::ByNumber),
+    pdbAtoms_(PdbAtomsSelection::All),
     top_(nullptr),
     occupancyModule_(new AnalysisDataAverageModule()),
     lifetimeModule_(new AnalysisDataLifetimeModule())
@@ -480,11 +486,11 @@ void Select::initOptions(IOptionsContainer* options, TrajectoryAnalysisSettings*
             BooleanOption("cfnorm").store(&bFracNorm_).description("Normalize by covered fraction with -os"));
     options->addOption(EnumOption<ResidueNumbering>("resnr")
                                .store(&resNumberType_)
-                               .enumValue(cResNumberEnum)
+                               .enumValue(c_residueNumberingTypeNames)
                                .description("Residue number output type with -oi and -on"));
     options->addOption(EnumOption<PdbAtomsSelection>("pdbatoms")
                                .store(&pdbAtoms_)
-                               .enumValue(cPDBAtomsEnum)
+                               .enumValue(c_pdbAtomsTypeNames)
                                .description("Atoms to write with -ofpdb"));
     options->addOption(BooleanOption("cumlt")
                                .store(&bCumulativeLifetimes_)
@@ -502,7 +508,7 @@ void Select::optionsFinished(TrajectoryAnalysisSettings* settings)
 
 void Select::initAnalysis(const TrajectoryAnalysisSettings& settings, const TopologyInformation& top)
 {
-    bResInd_ = (resNumberType_ == ResidueNumbering_ByIndex);
+    bResInd_ = (resNumberType_ == ResidueNumbering::ByIndex);
 
     for (SelectionList::iterator i = sel_.begin(); i != sel_.end(); ++i)
     {
@@ -620,7 +626,7 @@ void Select::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* /* pbc */, Traj
     AnalysisDataHandle   cdh = pdata->dataHandle(cdata_);
     AnalysisDataHandle   idh = pdata->dataHandle(idata_);
     AnalysisDataHandle   mdh = pdata->dataHandle(mdata_);
-    const SelectionList& sel = pdata->parallelSelections(sel_);
+    const SelectionList& sel = TrajectoryAnalysisModuleData::parallelSelections(sel_);
 
     sdh.startFrame(frnr, fr.time);
     for (size_t g = 0; g < sel.size(); ++g)
@@ -723,14 +729,14 @@ void Select::writeOutput()
 
         switch (pdbAtoms_)
         {
-            case PdbAtomsSelection_All:
+            case PdbAtomsSelection::All:
             {
                 t_trxstatus* status = open_trx(fnPDB_.c_str(), "w");
                 write_trxframe(status, &fr, nullptr);
                 close_trx(status);
                 break;
             }
-            case PdbAtomsSelection_MaxSelection:
+            case PdbAtomsSelection::MaxSelection:
             {
                 std::set<int> atomIndicesSet;
                 for (size_t g = 0; g < sel_.size(); ++g)
@@ -744,7 +750,7 @@ void Select::writeOutput()
                 close_trx(status);
                 break;
             }
-            case PdbAtomsSelection_Selected:
+            case PdbAtomsSelection::Selected:
             {
                 std::vector<int> indices;
                 for (int i = 0; i < atoms->nr; ++i)
@@ -759,7 +765,7 @@ void Select::writeOutput()
                 close_trx(status);
                 break;
             }
-            default:
+            case PdbAtomsSelection::Count:
                 GMX_RELEASE_ASSERT(false,
                                    "Mismatch between -pdbatoms enum values and implementation");
         }
index bf938526226ddde59ab2be14d1ae316cbd8774e9..1e28faf599700073f77745f141f816fab4735bc0 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2007, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 303d5dd7920a045c3369bc72e582172a83879600..1247ea9b8b5aa22f400e03d93a286e9d57712993 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -253,7 +253,7 @@ void analyzeFrameImpl(int frnr, const t_trxframe& fr, AnalysisDataHandle* dh, co
 void Trajectory::analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* /* pbc */, TrajectoryAnalysisModuleData* pdata)
 {
     AnalysisDataHandle   dh  = pdata->dataHandle(xdata_);
-    const SelectionList& sel = pdata->parallelSelections(sel_);
+    const SelectionList& sel = TrajectoryAnalysisModuleData::parallelSelections(sel_);
     analyzeFrameImpl(frnr, fr, &dh, sel, [](const SelectionPosition& pos) { return pos.x(); });
     if (fr.bV)
     {
index 7496d39102097565003ed6cc36bd61a8b0a85da7..2807d5894e725cbfb732ca56cbc226fa8dc001cf 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -208,8 +208,7 @@ void TrajectoryAnalysisRunnerCommon::Impl::initFirstFrame()
     {
         return;
     }
-    time_unit_t time_unit = static_cast<time_unit_t>(settings_.timeUnit() + 1); // NOLINT(bugprone-misplaced-widening-cast)
-    output_env_init(&oenv_, getProgramContext(), time_unit, FALSE, exvgNONE, 0);
+    output_env_init(&oenv_, getProgramContext(), settings_.timeUnit(), FALSE, XvgFormat::None, 0);
 
     int frflags = settings_.frflags();
     frflags |= TRX_NEED_X;
@@ -262,7 +261,7 @@ void TrajectoryAnalysisRunnerCommon::Impl::initFirstFrame()
         copy_mat(topInfo_.boxtop_, fr->box);
     }
 
-    set_trxframe_ePBC(fr, topInfo_.ePBC());
+    setTrxFramePbcType(fr, topInfo_.pbcType());
     if (topInfo_.hasTopology() && settings_.hasRmPBC())
     {
         gpbc_ = gmx_rmpbc_init(topInfo_);
index a9bd8af782b36bda005e8df529993c1e83fb57e9..beb63214aa0168dac094317d64a20da502089a28 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index beff3a1eebd925309acd5a165bc8ec14762476fe..cca089f3142ff98ed90e13d79063cfa6eb832d7c 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+# Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-gmx_add_unit_test(TrajectoryAnalysisUnitTests trajectoryanalysis-test
-                  moduletest.cpp
-                  clustsize.cpp
-                  cmdlinerunner.cpp
-                  angle.cpp
-                  convert_trj.cpp
-                  distance.cpp
-                  extract_cluster.cpp
-                  freevolume.cpp
-                  pairdist.cpp
-                  rdf.cpp
-                  sasa.cpp
-                  select.cpp
-                  surfacearea.cpp
-                  topologyinformation.cpp
-                  trajectory.cpp
-                  unionfind.cpp)
+gmx_add_gtest_executable(trajectoryanalysis-test
+    CPP_SOURCE_FILES
+        # Infrastructure
+        moduletest.cpp
+        # Tests
+        angle.cpp
+        clustsize.cpp
+        cmdlinerunner.cpp
+        convert_trj.cpp
+        distance.cpp
+        extract_cluster.cpp
+        freevolume.cpp
+        pairdist.cpp
+        rdf.cpp
+        sasa.cpp
+        select.cpp
+        surfacearea.cpp
+        topologyinformation.cpp
+        trajectory.cpp
+        unionfind.cpp
+        )
+gmx_register_gtest_test(TrajectoryAnalysisUnitTests trajectoryanalysis-test SLOW_TEST)
 target_link_libraries(trajectoryanalysis-test PRIVATE analysisdata-test-shared)
 
 add_executable(test_selection ${UNITTEST_TARGET_OPTIONS} test_selection.cpp)
index d614f500cfaf3da65736c1722f8dc7569f096254..228fd8ee468e45d8c494e9222d33ac244d2ecfea 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 7af475e6c772c26f0df6fdaff15a0676386705d4..db884d923b61c86a3fd7e5e406a8a665ba09a7c1 100644 (file)
@@ -39,7 +39,7 @@ Other options:
            Atoms stored in the trajectory file (if not set, assume first N
            atoms)
  -xvg    <enum>             (xmgrace)
-           Plot formatting: none, xmgrace, xmgr
+           Plot formatting: xmgrace, xmgr, none
  -[no]rmpbc                 (yes)
            Make molecules whole for each frame
  -[no]pbc                   (yes)
index 880e34ac2295334411c44bbd6c8b6cb314352af4..1bb99bac10abc737ec0b75bc25db9e8fb85a9a56 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -141,15 +142,13 @@ public:
         t_pbc pbc;
         if (bPBC)
         {
-            set_pbc(&pbc, epbcXYZ, box_);
+            set_pbc(&pbc, PbcType::Xyz, box_);
         }
-        ASSERT_NO_THROW_GMX({
-            gmx::SurfaceAreaCalculator calculator;
-            calculator.setDotCount(ndots);
-            calculator.setRadii(radius_);
-            calculator.calculate(as_rvec_array(x_.data()), bPBC ? &pbc : nullptr, index_.size(),
-                                 index_.data(), flags, &area_, &volume_, &atomArea_, &dots_, &dotCount_);
-        });
+        gmx::SurfaceAreaCalculator calculator;
+        calculator.setDotCount(ndots);
+        calculator.setRadii(radius_);
+        calculator.calculate(as_rvec_array(x_.data()), bPBC ? &pbc : nullptr, index_.size(),
+                             index_.data(), flags, &area_, &volume_, &atomArea_, &dots_, &dotCount_);
     }
     real resultArea() const { return area_; }
     real resultVolume() const { return volume_; }
index 7124600ac93fce4b5bd1f3dea512ca1ccd88d147..4728ed6d9652557b3f19c706c621f1bf41a452af 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,7 @@
 
 #include "gromacs/gmxpreprocess/grompp.h"
 #include "gromacs/math/vectypes.h"
+#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
@@ -77,7 +78,7 @@ TEST(TopologyInformation, CantWorkWithoutReadingAFile)
     ASSERT_TRUE(atoms2);
     EXPECT_NE(atoms1.get(), atoms2.get());
     EXPECT_EQ(0, atoms1->nr);
-    EXPECT_EQ(-1, topInfo.ePBC());
+    EXPECT_EQ(PbcType::Unset, topInfo.pbcType());
     EXPECT_THROW(topInfo.x().size(), gmx::APIError);
     EXPECT_THROW(topInfo.v().size(), gmx::APIError);
     matrix box{ { -2 } };
@@ -135,7 +136,7 @@ TEST(TopologyInformation, WorksWithGroFile)
     topInfo.fillFromInputFile(TestFileManager::getInputFilePath("lysozyme.gro"));
     EXPECT_FALSE(topInfo.hasFullTopology());
     runCommonTests(topInfo, numAtoms);
-    EXPECT_EQ(-1, topInfo.ePBC());
+    EXPECT_EQ(PbcType::Unset, topInfo.pbcType());
 
     // Check the per-atom data
     auto atoms = topInfo.copyAtoms();
@@ -168,7 +169,7 @@ TEST(TopologyInformation, WorksWithPdbFile)
     EXPECT_FALSE(topInfo.hasFullTopology());
     runCommonTests(topInfo, numAtoms);
     // TODO why does this differ from .gro?
-    EXPECT_EQ(0, topInfo.ePBC());
+    EXPECT_EQ(PbcType::Xyz, topInfo.pbcType());
 
     // Check the per-atom data
     auto atoms = topInfo.copyAtoms();
@@ -220,7 +221,7 @@ TEST(TopologyInformation, WorksWithTprFromPdbFile)
     EXPECT_TRUE(topInfo.hasFullTopology());
     runCommonTests(topInfo, numAtoms);
     // TODO why does this differ from .gro?
-    EXPECT_EQ(0, topInfo.ePBC());
+    EXPECT_EQ(PbcType::Xyz, topInfo.pbcType());
 
     // Check the per-atom data
     auto atoms = topInfo.copyAtoms();
index 74f1609e51235feea3a2c6c6c33630d42c9cfbae..5369b95611648aaf2cf5828e0da7dcce4f9bd543 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,7 @@
 
 #include "gromacs/fileio/confio.h"
 #include "gromacs/math/vec.h"
+#include "gromacs/pbcutil/pbc.h"
 #include "gromacs/pbcutil/rmpbc.h"
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/topology/topology.h"
@@ -64,7 +65,7 @@ TopologyInformation::TopologyInformation() :
     expandedTopology_(nullptr),
     atoms_(nullptr),
     bTop_(false),
-    ePBC_(-1)
+    pbcType_(PbcType::Unset)
 {
 }
 
@@ -81,7 +82,7 @@ void TopologyInformation::fillFromInputFile(const std::string& filename)
     // TODO Once there are fewer callers of the file-reading
     // functionality, make them read directly into std::vector.
     rvec *x, *v;
-    readConfAndTopology(filename.c_str(), &bTop_, mtop_.get(), &ePBC_, &x, &v, boxtop_);
+    readConfAndTopology(filename.c_str(), &bTop_, mtop_.get(), &pbcType_, &x, &v, boxtop_);
     xtop_.assign(x, x + mtop_->natoms);
     vtop_.assign(v, v + mtop_->natoms);
     sfree(x);
@@ -104,7 +105,7 @@ const gmx_localtop_t* TopologyInformation::expandedTopology() const
     // Do lazy initialization
     if (expandedTopology_ == nullptr && hasTopology())
     {
-        expandedTopology_ = std::make_unique<gmx_localtop_t>();
+        expandedTopology_ = std::make_unique<gmx_localtop_t>(mtop_->ffparams);
         gmx_mtop_generate_local_top(*mtop_, expandedTopology_.get(), false);
     }
 
@@ -188,7 +189,7 @@ gmx_rmpbc_t gmx_rmpbc_init(const gmx::TopologyInformation& topInfo)
 {
     GMX_RELEASE_ASSERT(topInfo.hasTopology(), "Cannot remove PBC without a topology");
 
-    return gmx_rmpbc_init(&topInfo.expandedTopology()->idef, topInfo.ePBC(), topInfo.mtop()->natoms);
+    return gmx_rmpbc_init(topInfo.expandedTopology()->idef, topInfo.pbcType(), topInfo.mtop()->natoms);
 }
 
 } // namespace gmx
index e9f9d5374d47be1e683ade016c4402e0d1595192..2f353d000720a445dfaae5bc6b49b575677e927b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -55,6 +55,7 @@
 
 //! Forward declaration
 typedef struct gmx_rmpbc* gmx_rmpbc_t;
+enum class PbcType : int;
 
 namespace gmx
 {
@@ -134,8 +135,8 @@ public:
     /*! \brief Copies the fully expanded atom data arrays, which
      * might be valid but empty if no topology is available. */
     AtomsDataPtr copyAtoms() const;
-    //! Returns the ePBC field from the topology.
-    int ePBC() const { return ePBC_; }
+    //! Returns the pbcType field from the topology.
+    PbcType pbcType() const { return pbcType_; }
     /*! \brief
      * Gets the configuration positions from the topology file.
      *
@@ -186,8 +187,8 @@ private:
     std::vector<RVec> vtop_;
     //! The box loaded from the topology file.
     matrix boxtop_{};
-    //! The ePBC field loaded from the topology file.
-    int ePBC_;
+    //! The pbcType field loaded from the topology file.
+    PbcType pbcType_;
 
     // TODO This type is probably movable if we need that.
     GMX_DISALLOW_COPY_AND_ASSIGN(TopologyInformation);
index 47385350fc34a05e478ea657e42a44225d5062f0..99bfd43eea74984d1d0cbdbbf774e0505559fbb7 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -94,7 +94,7 @@
  *
  * The header path.h declares helpers for manipulating paths as strings and for
  * managing directories and files.
- * The fate of this header depends on what is decided in Redmine issue #950.
+ * The fate of this header depends on what is decided in Issue #950.
  *
  * <H3>Logging</H3>
  *
index 739dfaed6605985020e9f380c66374f89e1f6b19..93e82808cde05fa4fe50da929ba1f9a2f8eb29b3 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2011,2012,2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+# Copyright (c) 2015,2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 file(GLOB UTILITY_SOURCES *.cpp)
-if (GMX_GPU AND NOT GMX_USE_OPENCL)
+if (GMX_GPU_CUDA)
     gmx_add_libgromacs_sources(cuda_version_information.cu)
 endif()
 set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${UTILITY_SOURCES} PARENT_SCOPE)
 
-# TODO: (https://redmine.gromacs.org/issues/988) Find a new convention for defining public API.
+# TODO: (https://gitlab.com/gromacs/gromacs/-/issues/988) Find a new convention for defining public API.
 install(FILES
         basedefinitions.h
         current_function.h
@@ -50,9 +51,10 @@ if(GMX_INSTALL_LEGACY_API)
   install(FILES
           arrayref.h
           baseversion.h
-         classhelpers.h
+          classhelpers.h
           enumerationhelpers.h
-         exceptions.h
+          exceptions.h
+          listoflists.h
          fileptr.h
          futil.h
          flags.h
index c4d4a230278791b1c195625a365c0b9c579ca43d..74ec200324e9c0f60497eb77136e4f4ee5e4d9ba 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -152,6 +152,8 @@ public:
      * This is a member function of the left-hand-side allocator.
      * Always true for stateless polcies. Has to be defined in the policy for stateful policies.
      * FUTURE: Can be removed with C++17 (is_always_equal)
+     *
+     * \todo Use std::is_empty_v when CUDA 11 is a requirement.
      */
     template<class T2, class A = AllocationPolicy, typename = std::enable_if_t<std::is_empty<A>::value>>
     bool operator==(const Allocator<T2, AllocationPolicy>& /*unused*/) const
index 7e2c86f4411f0228d408f12e34041100bd283736..77f70a7863e5e6cdf6c0a96b3a5d6cde2ad76816 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -112,7 +112,7 @@ public:
      *
      * \throws std::bad_alloc if out of memory.
      */
-    template<typename T, typename = std::enable_if_t<!std::is_same<T, Any>::value>>
+    template<typename T, typename = std::enable_if_t<!std::is_same_v<T, Any>>>
     explicit Any(T&& value) : content_(new Content<std::decay_t<T>>(std::forward<T>(value)))
     {
     }
index e8d242a168ed5ab1300283b552ce0f15ecbcbe62..5eacdcb672fc1e63e2fbaa497440e4404b770dce 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,6 +47,7 @@
 #ifndef GMX_UTILITY_ARRAYREF_H
 #define GMX_UTILITY_ARRAYREF_H
 
+#include <cassert>
 #include <cstddef>
 
 #include <array>
 #include <utility>
 #include <vector>
 
-#include "gromacs/utility/gmxassert.h"
+#if __has_include(<boost/stl_interfaces/iterator_interface.hpp>)
+#    include <boost/stl_interfaces/iterator_interface.hpp>
+#else // fallback for installed headers
+#    include <gromacs/external/boost/stl_interfaces/iterator_interface.hpp>
+#endif
 
 namespace gmx
 {
 
+template<class T>
+struct ArrayRefIter :
+    boost::stl_interfaces::iterator_interface<ArrayRefIter<T>, std::random_access_iterator_tag, T>
+{
+    // This default constructor does not initialize it_
+    constexpr ArrayRefIter() noexcept {}
+    constexpr explicit ArrayRefIter(T* it) noexcept : it_(it) {}
+    // TODO: Use std::is_const_v when CUDA 11 is a requirement.
+    template<class T2 = T, class = std::enable_if_t<std::is_const<T2>::value>>
+    constexpr ArrayRefIter(ArrayRefIter<std::remove_const_t<T2>> it) noexcept : it_(&*it)
+    {
+    }
+    constexpr T*            data() const noexcept { return it_; }
+    constexpr T&            operator*() const noexcept { return *it_; }
+    constexpr ArrayRefIter& operator+=(std::ptrdiff_t i) noexcept
+    {
+        it_ += i;
+        return *this;
+    }
+    constexpr auto operator-(ArrayRefIter other) const noexcept { return it_ - other.it_; }
+
+private:
+    T* it_;
+};
+
 /*! \brief STL-like interface to a C array of T (or part
  * of a std container of T).
  *
@@ -114,13 +145,13 @@ public:
     //! Const pointer to an element.
     typedef const T* const_pointer;
     //! Const iterator type to an element.
-    typedef const T* const_iterator;
+    typedef ArrayRefIter<const T> const_iterator;
     //! Reference to an element.
     typedef T& reference;
     //! Pointer to an element.
     typedef T* pointer;
     //! Iterator type to an element.
-    typedef T* iterator;
+    typedef ArrayRefIter<T> iterator;
     //! Standard reverse iterator.
     typedef std::reverse_iterator<iterator> reverse_iterator;
     //! Standard reverse iterator.
@@ -144,6 +175,8 @@ public:
      *
      * This constructor is not explicit to allow directly passing
      * a container to a method that takes ArrayRef.
+     *
+     * \todo Use std::is_convertible_v when CUDA 11 is a requirement.
      */
     template<typename U, typename = std::enable_if_t<std::is_convertible<typename std::remove_reference_t<U>::pointer, pointer>::value>>
     ArrayRef(U&& o) : begin_(o.data()), end_(o.data() + o.size())
@@ -159,7 +192,19 @@ public:
      */
     ArrayRef(pointer begin, pointer end) : begin_(begin), end_(end)
     {
-        GMX_ASSERT(end >= begin, "Invalid range");
+        assert((end >= begin && "Invalid range"));
+    }
+    /*! \brief
+     * Constructs a reference to a particular range.
+     *
+     * \param[in] begin  Iterator to the beginning of a range.
+     * \param[in] end    iterator to the end of a range.
+     *
+     * Passed iterators must remain valid for the lifetime of this object.
+     */
+    ArrayRef(iterator begin, iterator end) : begin_(begin), end_(end)
+    {
+        assert((end >= begin && "Invalid range"));
     }
     //! \cond
     // Doxygen 1.8.5 doesn't parse the declaration correctly...
@@ -191,9 +236,9 @@ public:
         return { begin_ + start, begin_ + start + count };
     }
     //! Returns an iterator to the beginning of the reference.
-    iterator begin() const { return begin_; }
+    iterator begin() const { return iterator(begin_); }
     //! Returns an iterator to the end of the reference.
-    iterator end() const { return end_; }
+    iterator end() const { return iterator(end_); }
     //! Returns an iterator to the reverse beginning of the reference.
     reverse_iterator rbegin() const { return reverse_iterator(end()); }
     //! Returns an iterator to the reverse end of the reference.
@@ -206,7 +251,7 @@ public:
      */
     size_type size() const { return end_ - begin_; }
     //! Returns the signed size of the reference.
-    index ssize() const { return size(); }
+    difference_type ssize() const { return size(); }
     //! Identical to size().
     size_type capacity() const { return end_ - begin_; }
     //! Whether the reference refers to no memory.
@@ -224,12 +269,12 @@ public:
         return begin_[n];
     }
     //! Returns the first element.
-    reference front() const { return *begin_; }
+    reference front() const { return *(begin_); }
     //! Returns the first element.
     reference back() const { return *(end_ - 1); }
 
     //! Returns a raw pointer to the contents of the array.
-    pointer data() const { return begin_; }
+    pointer data() const { return begin_.data(); }
 
     /*! \brief
      * Swaps referenced memory with the other object.
@@ -244,8 +289,8 @@ public:
     }
 
 private:
-    pointer begin_;
-    pointer end_;
+    iterator begin_;
+    iterator end_;
 };
 
 //! \copydoc ArrayRef::fromArray()
@@ -268,6 +313,8 @@ ArrayRef<const T> constArrayRefFromArray(const T* begin, size_t size)
  * Create ArrayRef from container with type deduction
  *
  * \see ArrayRef
+ *
+ * \todo Use std::is_const_v when CUDA 11 is a requirement.
  */
 template<typename T>
 ArrayRef<std::conditional_t<std::is_const<T>::value, const typename T::value_type, typename T::value_type>>
index d1ae0c6ce71f26f60b9398f866daf8008be71ff3..bfb155651abac78b7e354662b12245f91064b67b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -169,6 +170,18 @@ index ssize(const T& t)
 #    define MSVC_DIAGNOSTIC_RESET
 #endif
 
+#ifdef __INTEL_COMPILER
+//! Ignore unused loop variable warning - it was used until the compiler removes the use!
+#    define DO_PRAGMA(x) _Pragma(#    x)
+#    define INTEL_DIAGNOSTIC_IGNORE(id) DO_PRAGMA(warning push) DO_PRAGMA(warning(disable : id))
+#    define INTEL_DIAGNOSTIC_RESET DO_PRAGMA(warning pop)
+#else
+//! Ignore specified diagnostic message from Intel compiler.
+#    define INTEL_DIAGNOSTIC_IGNORE(id)
+//! Reset the diagnostic message setting.
+#    define INTEL_DIAGNOSTIC_RESET
+#endif
+
 namespace gmx
 {
 namespace internal
index 5bb5a90158f218df891211263cbc3e79a15eb03b..6fade03e577a04d6be1bea33987e1ed80e3b2847 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e9038ce9adccd99a4c78bc65b73ff8d426fa16ba..7c324773219cbbbc46a8ed90c9c1d2a2843e2a73 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2012,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2012,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 90828c320866da97c25d911e80e1d5d0a0e1a1c3..f0638775db17202074b9d22d28a7412c008ae4c7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,6 +38,8 @@
 
 #include "config.h"
 
+#include "gromacs/utility/gmxassert.h"
+
 #include "baseversion_gen.h"
 
 const char* gmx_version()
@@ -76,11 +78,29 @@ void gmx_is_double_precision() {}
 void gmx_is_single_precision() {}
 #endif
 
-/* Note that this array (and some which follow) must match the "GPU
- * support enumeration" in src/config.h.cmakein */
-static const char* const gpuImplementationStrings[] = { "disabled", "CUDA", "OpenCL" };
-
 const char* getGpuImplementationString()
 {
-    return gpuImplementationStrings[GMX_GPU];
+    if (GMX_GPU)
+    {
+        if (GMX_GPU_CUDA)
+        {
+            return "CUDA";
+        }
+        else if (GMX_GPU_OPENCL)
+        {
+            return "OpenCL";
+        }
+        else if (GMX_GPU_SYCL)
+        {
+            return "SYCL";
+        }
+        else
+        {
+            GMX_RELEASE_ASSERT(false, "Unknown GPU configuration");
+        }
+    }
+    else
+    {
+        return "disabled";
+    }
 }
index cf54a4ff6905437c4e8f60d421fdab189e349d2d..aa0d2841f814b2b3cbdbc5b53966fe6861ec548c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -93,7 +93,11 @@ void gmx_is_double_precision();
 
 void gmx_is_single_precision();
 
-/*! \brief Return a string describing what kind of GPU suport was configured in the build. */
+/*! \brief Return a string describing what kind of GPU suport was configured in the build.
+ *
+ * Currently returns correctly for CUDA, OpenCL and SYCL.
+ * Needs to be updated when adding new acceleration options.
+ */
 const char* getGpuImplementationString();
 
 /*! \brief
index f970e18a48fbc2497b6cb281dbe9452747ed5e45..b2f026f81a3e6336a056784343d9db63d9cda5e9 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 1a96d71ce18ece1f7b3691c9f4eb039580978a3f..8548a1818bac48a02dbe0a80da89901f70b4643c 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -270,7 +271,9 @@ void gmx_print_version_info(gmx::TextWriter* writer)
     writer->writeLine(formatString("GPU support:        %s", getGpuImplementationString()));
     writer->writeLine(formatString("SIMD instructions:  %s", GMX_SIMD_STRING));
     writer->writeLine(formatString("FFT library:        %s", getFftDescriptionString()));
-    writer->writeLine(formatString("RDTSCP usage:       %s", HAVE_RDTSCP ? "enabled" : "disabled"));
+#if GMX_TARGET_X86
+    writer->writeLine(formatString("RDTSCP usage:       %s", GMX_USE_RDTSCP ? "enabled" : "disabled"));
+#endif
 #if GMX_USE_TNG
     writer->writeLine("TNG support:        enabled");
 #else
@@ -305,12 +308,12 @@ void gmx_print_version_info(gmx::TextWriter* writer)
     writer->writeLine(formatString("Linked with Intel MKL version %d.%d.%d.", __INTEL_MKL__,
                                    __INTEL_MKL_MINOR__, __INTEL_MKL_UPDATE__));
 #endif
-#if GMX_GPU == GMX_GPU_OPENCL
+#if GMX_GPU_OPENCL
     writer->writeLine(formatString("OpenCL include dir: %s", OPENCL_INCLUDE_DIR));
     writer->writeLine(formatString("OpenCL library:     %s", OPENCL_LIBRARY));
     writer->writeLine(formatString("OpenCL version:     %s", OPENCL_VERSION_STRING));
 #endif
-#if GMX_GPU == GMX_GPU_CUDA
+#if GMX_GPU_CUDA
     writer->writeLine(formatString("CUDA compiler:      %s", CUDA_COMPILER_INFO));
     writer->writeLine(formatString("CUDA compiler flags:%s %s", CUDA_COMPILER_FLAGS,
                                    CMAKE_BUILD_CONFIGURATION_CXX_FLAGS));
index 9542305898f82656d6c67ddc3f887fb80aeea653..6449ff73ea646a3c9ef6697fc85a5f4f12efdd5b 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 46c7a5c3a88126a1d9581eb054c99f04b587589f..ae4458fdda49848a5b947f1c72deb09c66cd63cd 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -66,6 +67,23 @@ namespace gmx
         ClassName& operator=(const ClassName&) = delete; \
         ClassName(const ClassName&)            = delete
 #endif
+#ifdef DOXYGEN
+/*! \brief
+ * Macro to declare a class non-copyable, non-movable, non-copy-assignable and
+ * non-move-assignable.
+ *
+ * For consistency, should appear last in the class declaration.
+ *
+ * \ingroup module_utility
+ */
+#    define GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(ClassName)
+#else
+#    define GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(ClassName)                                                            \
+        ClassName& operator=(const ClassName&) = delete;                                                            \
+        ClassName(const ClassName&)            = delete;                                                            \
+        ClassName& operator=(ClassName&&) = delete; /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) */ \
+        ClassName(ClassName&&) = delete /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) */
+#endif
 /*! \brief
  * Macro to declare a class non-assignable.
  *
index 7a32bf107b9c067ba7d99b8d4d4200c8b8fc8a61..32f06836c7bc6818ed8498f6b1535a791a455c7d 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 96b4a269f25898b806f38b0910c2d9430c5a3941..6ab41cd3d3e113ee327325041f6ecfbe61abc765 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 54dbf014b35558ad25555f70c71875f8a0ac3f98..5ab575cb848ebf443389a16875ec289ef1b0c649 100644 (file)
@@ -447,15 +447,12 @@ std::string getCoolQuote()
         { "Shoot them in the back now", "The Ramones" },
         { "Read me your scripture and I will twist it", "Red Hot Chili Peppers" },
         { "Good Music Saves your Soul", "Lemmy" },
-        { "I believe in miracles cause I'm one", "The Ramones" },
-        { "Gabba Gabba Hey!", "The Ramones" },
-        { "Good Music Saves your Soul", "Lemmy" },
         { "Move about like a Scientist, lay down, get kissed", "Red Hot Chili Peppars" },
         { "California, R.I.P.", "Red Hot Chili Peppars" },
         { "Don't You Wish You Never Met Her, Dirty Blue Gene?", "Captain Beefheart" },
         { "Nobody Never Learnt No-Nothing from No History", "Gogol Bordello" },
         { "I'd be Safe and Warm if I was in L.A.", "The Mamas and the Papas" },
-        { "It's Unacceptable That Choclate Makes You Fat", "MI 3" },
+        { "It's Unacceptable That Chocolate Makes You Fat", "MI 3" },
         { "My Brothers are Protons (Protons!), My Sisters are Neurons (Neurons)",
           "Gogol Bordello" },
         { "Put Me Inside SSC, Let's Test Superstring Theory, Oh Yoi Yoi Accelerate the Protons",
@@ -776,7 +773,7 @@ std::string getCoolQuote()
           "it. Burn them, it's a great symbolic gesture.",
           "Linus Torvalds" },
         { "I invented the term 'Object-Oriented', and I can tell you I did not have C++ in mind.",
-          "Alay Kay, author of Smalltalk" },
+          "Alan Kay, author of Smalltalk" },
         { "FORTRAN, the infantile disorder, by now nearly 20 years old, is hopelessly inadequate "
           "for whatever computer application you have in mind today: it is now too clumsy, too "
           "risky, and too expensive to use.",
@@ -1510,6 +1507,27 @@ std::string getCoolQuote()
           "takes all the running YOU can do, to keep in the same place. If you want "
           "to get somewhere else, you must run at least twice as fast as that!\"",
           "Lewis Carroll" },
+        { "More than 10000000 total errors detected.  I'm not reporting any more. "
+          "Final error counts will be inaccurate.  Go fix your program!",
+          "Valgrind while memory debugging mdrun" },
+        { "If we are going to have SYCL, can we have a hammer as well?", "Joe Jordan" },
+        { "We can make it into a friend class. But I don't like having friends.", "Joe Jordan" },
+        { "A method is more important than a discovery, since the right method will lead to new "
+          "and even more important discoveries.",
+          "Lev Landau" },
+        { "Product of optimism and knowledge is a constant.", "Lev Landau" },
+        { "Why add prime numbers? Prime numbers are made to be multiplied.", "Lev Landau" },
+        { "How wonderful that we have met with a paradox. Now we have some hope of making "
+          "progress.",
+          "Niels Bohr" },
+        { "We must be clear that when it comes to atoms, language can be used only as in poetry. ",
+          "Niels Bohr" },
+        { "\"What are the biological implications of your research?\" - \"Well, I simulate "
+          "water.\" ",
+          "Petter Johansson" },
+        { "Everything what mathematicians were saying for the last 50 years is slowly catching up "
+          "with us.",
+          "David van der Spoel" },
     };
 
     if (beCool())
index 2afafc3c97f0266e8f71f8f09a97c1178cf32119..576f79352705090984f0c9575a072940f1f18ce5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index c80cdcf9a34e60ce0c625c09e047a1cac9a3eca6..e2b45501cc98cac56b16fc4b2557e08385d101b6 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f67e1eceb69e069c830d2055feb98bdf86c48ab3..5980cf8e2f79762c1674919372124a8c5fc29dda 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 8df8f8857483b407653262b674c6a60488913d0f..9afb8c36ef44d72c8b4016298888f9e3e9557c58 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index a98dc4491d5488b729f736cb57f044b7877769e0..205b1231cbba220f17fdc3af274f8098b9148c61 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -68,7 +68,10 @@ public:
 
     using A::A;
 
-    /*! \brief Constructs an object and default initializes */
+    /*! \brief Constructs an object and default initializes
+     *
+     * \todo Use std::is_nothrow_default_constructible_v when CUDA 11 is a requirement.
+     */
     template<typename U>
     void construct(U* ptr) noexcept(std::is_nothrow_default_constructible<U>::value)
     {
index 203d5ac971be0c3c273d3632b0cdc6cecb67840a..dea093d1cad608b63920cd8ec1eae5efd8b7630c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,7 +53,7 @@
  * Native Windows uses backslash path separators (but accepts also slashes).
  * Cygwin and most other systems use slash.
  *
- * \todo Get rid of this (Redmine #950). It is not necessary for
+ * \todo Get rid of this (Issue #950). It is not necessary for
  * constructing paths on the systems that it currently supports, and
  * is not reliable in parsing input paths either, since Windows needs
  * to accept both instead of only DIR_SEPARATOR. At the very least, we
index 439473af3ab612d793fa32411844fb575ffcc6f9..6631b74c97012f0f0341efcae20893e58ffe9fd3 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 21793ff34fbadcb5fdca796db6d0d6f3290d1005..c74866f9a8ad78f0fc35d8d7177594d8c31a7e54 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  *
  * Usage examples:
  *
- *  enum class Foo
+ *  enum class Foo : int
  *  {
- *      Bar, Baz, Fooz,
+ *      Bar,
+ *      Baz,
+ *      Fooz,
  *      Count
  *  };
  *
- *  EnumerationWrapper<Foo> iter;
- *
- *  for (Foo c : iter)
+ *  for (Foo c : EnumerationWrapper<Foo>{})
  *  {
  *      // 'c' is a constant from Foo
  *  }
  *
+ *
  *  const EnumerationArray<Foo, std::string> fooStrings = { { "Bar", "Baz", "Fooz" } };
+ *  std::cout << fooStrings[Foo::Baz];
+ *  std::cout << fooStrings[Foo::Count]; // Triggers an assertion
  *
  *  for (Foo c : keysOf(fooStrings))
  *  {
 #include <iterator>
 #include <type_traits>
 
+#if __has_include(<boost/stl_interfaces/iterator_interface.hpp>)
+#    include <boost/stl_interfaces/iterator_interface.hpp>
+#else // fallback for installed headers
+#    include <gromacs/external/boost/stl_interfaces/iterator_interface.hpp>
+#endif
+
 #include "gromacs/utility/gmxassert.h"
 
 namespace gmx
@@ -103,79 +112,41 @@ namespace gmx
  * \tparam  Last       Last constant or number thereof (assumes a default 'Count' member).
  * \tparam  Step       Step increment.
  */
-template<typename EnumType, EnumType Last = EnumType::Count, unsigned int Step = 1>
-class EnumerationIterator final
+template<typename EnumType, EnumType Last = EnumType::Count, std::ptrdiff_t Step = 1>
+class EnumerationIterator final :
+    public boost::stl_interfaces::iterator_interface<EnumerationIterator<EnumType, Last, Step>, std::random_access_iterator_tag, EnumType>
 {
 public:
+    // TODO: Use std::is_enum_v when CUDA 11 is a requirement.
+    static_assert(std::is_enum<EnumType>::value, "Enumeration iterator must be over an enum type.");
     //! Convenience alias
     using IntegerType = std::underlying_type_t<EnumType>;
 
-    /*! \name Iterator type traits
-     * Satisfies the requirements for STL forward iterator.
-     * \{
-     */
-    using iterator_category = std::forward_iterator_tag;
-    using value_type        = EnumType;
-    using difference_type   = std::ptrdiff_t;
-    using pointer           = EnumType*;
-    using reference         = EnumType&;
-    //! \}
-
     constexpr EnumerationIterator() noexcept : m_current{ 0 } // Assumes 0 is the first constant
     {
     }
-    //! Copy constructor
-    constexpr EnumerationIterator(const EnumType index) noexcept :
+    //! Conversion constructor
+    explicit constexpr EnumerationIterator(const EnumType index) noexcept :
         m_current(static_cast<IntegerType>(index))
     {
     }
-    //! Pre-increment operator
-    EnumerationIterator operator++()
+    //! Addition-assignment operator
+    constexpr EnumerationIterator& operator+=(std::ptrdiff_t i) noexcept
     {
-        m_current += Step;
+        m_current += Step * i;
         return *this;
     }
-    //! Post-increment operator
-    EnumerationIterator operator++(int)
-    {
-        EnumerationIterator old_val{ *this };
-        m_current += Step;
-        return old_val;
-    }
     //! Dereference operator
-    EnumType operator*() const
+    constexpr EnumType operator*() const noexcept
     {
         GMX_ASSERT(m_current < static_cast<IntegerType>(Last), "dereferencing out of range");
         return static_cast<EnumType>(m_current);
     }
-
-    /*!@{*/
-    //! Comparision operators
-    bool operator==(const EnumerationIterator other) const noexcept
+    //! Difference operator
+    constexpr std::ptrdiff_t operator-(const EnumerationIterator other) const noexcept
     {
-        return m_current == other.m_current;
+        return (static_cast<std::ptrdiff_t>(m_current) - static_cast<std::ptrdiff_t>(other.m_current)) / Step;
     }
-    bool operator!=(const EnumerationIterator other) const noexcept
-    {
-        return m_current != other.m_current;
-    }
-    bool operator<(const EnumerationIterator other) const noexcept
-    {
-        return m_current < other.m_current;
-    }
-    bool operator>(const EnumerationIterator other) const noexcept
-    {
-        return m_current > other.m_current;
-    }
-    bool operator<=(const EnumerationIterator other) const noexcept
-    {
-        return m_current <= other.m_current;
-    }
-    bool operator>=(const EnumerationIterator other) const noexcept
-    {
-        return m_current >= other.m_current;
-    }
-    /*!@}*/
 
 private:
     IntegerType m_current;
index 76cb0e4efe253b944d62d356dda40c3b620f5f7b..055c71b6527ff319b68322ccc0c8d8f83383dc47 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -73,6 +74,7 @@ const char* const error_names[] = {
     "API error (bug)",
     "Range checking error (possible bug)",
     "Communication (parallel processing) problem",
+    "Modular simulator error",
 
     "Unknown error",
 };
index 3af1c18990e920abb04595c606a708a9addecf0f..f1db79b5c86ec1ab843922d0d26a918968ba965d 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -93,6 +94,9 @@ enum ErrorCode
 
     //! Parallel consistency check failed.
     eeParallelConsistency,
+
+    //! Error specific for modular simulator.
+    eeModularSimulator,
     //!\}
 
     //! Unknown error detected.
index 142ac476fcae17ed6560b9697f9bb4231cc68ada..84e57940a50f43b119e119d514588507e31a274e 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2011-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -272,6 +272,11 @@ int ParallelConsistencyError::errorCode() const
     return eeParallelConsistency;
 }
 
+int ModularSimulatorError::errorCode() const
+{
+    return eeModularSimulator;
+}
+
 
 /********************************************************************
  * Global functions
index fbcc2de183e1cda18342e09f490e91f708e78f9b..8d7b62d6017b6454582c24721e8ba69ff74f4535 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <vector>
 
 #include "gromacs/utility/basedefinitions.h"
-#include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/gmxassert.h"
 
-#include "current_function.h"
-
 namespace gmx
 {
 
@@ -87,7 +85,11 @@ class IExceptionInfo
 {
 public:
     virtual ~IExceptionInfo();
-    GMX_DEFAULT_CONSTRUCTORS(IExceptionInfo);
+    IExceptionInfo()                          = default;
+    IExceptionInfo(const IExceptionInfo&)     = default;
+    IExceptionInfo(IExceptionInfo&&) noexcept = default;
+    IExceptionInfo& operator=(const IExceptionInfo&) = default;
+    IExceptionInfo& operator=(IExceptionInfo&&) noexcept = default;
 };
 
 //! Smart pointer to manage IExceptionInfo ownership.
@@ -251,7 +253,11 @@ public:
     // about missing noexcept otherwise.
     ~GromacsException() noexcept override {}
 
-    GMX_DEFAULT_CONSTRUCTORS(GromacsException);
+    GromacsException()                            = default;
+    GromacsException(const GromacsException&)     = default;
+    GromacsException(GromacsException&&) noexcept = default;
+    GromacsException& operator=(const GromacsException&) = default;
+    GromacsException& operator=(GromacsException&&) noexcept = default;
 
     /*! \brief
      * Returns the reason string for the exception.
@@ -371,6 +377,8 @@ private:
  * other overloads of `operator<<` for ExceptionInfo objects, in case someone
  * would like to declare those.  But currently we do not have such overloads, so
  * if the enable_if causes problems with some compilers, it can be removed.
+ *
+ * \todo Use std::is_base_of_v when CUDA 11 is a requirement.
  */
 template<class Exception, class Tag, class T>
 inline std::enable_if_t<std::is_base_of<GromacsException, Exception>::value, Exception>
@@ -563,6 +571,22 @@ public:
     int errorCode() const override;
 };
 
+/*! \brief
+ * Exception class for modular simulator.
+ *
+ * \inpublicapi
+ */
+class ModularSimulatorError : public GromacsException
+{
+public:
+    //! \copydoc FileIOError::FileIOError()
+    explicit ModularSimulatorError(const ExceptionInitializer& details) : GromacsException(details)
+    {
+    }
+
+    [[nodiscard]] int errorCode() const override;
+};
+
 /*! \brief
  * Macro for throwing an exception.
  *
index e27f64b69276bb1d2d959d48b64f0c23b80e6462..f29b400a807e489e70b8633a3d55e85279eff8d0 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017, The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -64,8 +65,7 @@
 
 #include "errorformat.h"
 
-static bool       bDebug = false;
-static gmx::Mutex where_mutex;
+static bool bDebug = false;
 
 FILE*    debug        = nullptr;
 gmx_bool gmx_debug_at = FALSE;
index 4893c509fe1cda87cf94c9bbc6cc2075aad0a6ae..4773b5c2c8ba886b5271b614b66252cd969c0489 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -153,7 +153,8 @@ using internal::FileStreamImpl;
  * StandardInputStream
  */
 
-bool StandardInputStream::isInteractive() const
+// static
+bool StandardInputStream::isInteractive()
 {
 #ifdef HAVE_UNISTD_H
     return isatty(fileno(stdin)) != 0;
@@ -167,13 +168,6 @@ bool StandardInputStream::readLine(std::string* line)
     return readLineImpl(stdin, line);
 }
 
-// static
-StandardInputStream& StandardInputStream::instance()
-{
-    static StandardInputStream stdinObject;
-    return stdinObject;
-}
-
 /********************************************************************
  * TextInputFile
  */
index 26c240a6bf8ce1efd1038f68eeac771134e41cc8..5309fb217822938478e17823ab2d83c415584649 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -78,18 +78,11 @@ public:
      *
      * Does not throw.
      */
-    bool isInteractive() const;
+    static bool isInteractive();
 
     // From TextInputStream
     bool readLine(std::string* line) override;
     void close() override {}
-
-    /*! \brief
-     * Returns a stream for accessing `stdin`.
-     *
-     * Does not throw.
-     */
-    static StandardInputStream& instance();
 };
 
 /*! \libinternal \brief
index 03c6d79443361fb6867847176ad0117da699bcd8..ace651cdeb97529760cf523f658f3c15bfb84192 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d2f49344e2ed86c19b764d3bbda8c88a76663c7b..4be79c783cda178a0b3078a5fec14cfc8c1101a5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -49,7 +49,8 @@ namespace gmx
 /*! \brief
  * Template class for typesafe handling of combination of flags.
  *
- * \tparam FlagType An enumerated type that holds the possible single flags.
+ * \tparam FlagType An enumerated type that holds the possible single flags. The enum
+ * must be of unsigned underlying type.
  *
  * This class is not used publicly, but is present in an installed header
  * because it is used internally in public template classes.
@@ -66,6 +67,8 @@ template<typename FlagType>
 class FlagsTemplate
 {
 public:
+    static_assert(std::is_enum_v<FlagType> && std::is_unsigned_v<std::underlying_type_t<FlagType>>,
+                  "Flags must be an unsigned enum type.");
     //! Creates a flags object with no flags set.
     FlagsTemplate() : flags_(0) {}
     //! Creates a flags object from a single flag.
@@ -120,7 +123,7 @@ private:
     //! Creates a flags object with the given flags.
     explicit FlagsTemplate(unsigned long flags) : flags_(flags) {}
 
-    unsigned long flags_;
+    uint64_t flags_;
 };
 
 } // namespace gmx
index c16605e90acc72bbb5c0ce2914e4dae08dbb216b..6f631efc8b3f608b01e18e76e90fb30c41d66b18 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017, The GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -165,14 +166,14 @@ static void push_ps(FILE* fp)
 #    endif
 #endif
 #if (!HAVE_PIPES && !defined(__native_client__))
-static FILE* popen(const char* nm, const char* mode)
+static FILE* popen(const char* /* nm */, const char* /* mode */)
 {
     gmx_impl("Sorry no pipes...");
 
     return NULL;
 }
 
-static int pclose(FILE* fp)
+static int pclose(FILE* /* fp */)
 {
     gmx_impl("Sorry no pipes...");
 
@@ -683,6 +684,7 @@ int gmx_fsync(FILE* fp)
 #elif HAVE__FILENO
         fn = _fileno(fp);
 #else
+        GMX_UNUSED_VALUE(fp);
         fn = -1;
 #endif
 
@@ -727,7 +729,9 @@ void gmx_chdir(const char* directory)
 #endif
     if (rc != 0)
     {
-        gmx_fatal(FARGS, "Cannot change directory to '%s'. Reason: %s", directory, strerror(errno));
+        auto message = gmx::formatString("Cannot change directory to '%s'. Reason: %s", directory,
+                                         strerror(errno));
+        GMX_THROW(gmx::FileIOError(message));
     }
 }
 
index 6c4d7a2281e06901c7fa8454966e52c872a62f84..5c2a2475268475d8785da613be852e56e061cfc5 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 35f3323dd2dcf44684b6889063bc1e970a265237..1acd0f7d2a65f78a7dbdf1fab036c5b563c00989 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index ab23f9065178cdc2f5c6d8989684df228e0c7329..b4a6f146190d562874f939defebdf385ace24faa 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b9ee23851725d1f39b7fb8483162d520efb6e5c3..1f6e07414bdb765f98457fffd926bc1ee1a06b0c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018, The GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 2dec0caec9ae0345c3609152cf09e265d63be4d0..832077e9da5c354dc7f182c8d5825e16b7b96ee3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b9370404a5ea1d5af6c7aa9252cff54089bfb8aa..252eeae886a9760b032e30ca82050b6efca97c17 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 6cde441f1f33ad2e1e9294b18376e46fb2a8c030..aa68dea4f7d637c5a5e69806bf42f23f9048f05f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 3d5b32abf0eb2d02f5a46ed3aeaf7d2b7c65a27f..2d49c09448ead4caff0eea692aa103a907a4baf0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -275,7 +275,7 @@ private:
         writer_->writeLine(message);
     }
 
-    std::string formatValueForMissingMessage(const KeyValueTreeValue& value)
+    static std::string formatValueForMissingMessage(const KeyValueTreeValue& value)
     {
         if (value.isObject() || value.isArray())
         {
index f1481d8160903740b0be0974d8095b2086e159a4..45ac7b87237b1abe0317d9a1b407cc74ddc006df 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -519,7 +519,7 @@ KeyValueTreeTransformRuleBuilder::KeyValueTreeTransformRuleBuilder(internal::Key
 // warning is about as bad as any alternative.
 KeyValueTreeTransformRuleBuilder::~KeyValueTreeTransformRuleBuilder() // NOLINT(bugprone-exception-escape)
 {
-    if (!std::uncaught_exception())
+    if (!std::uncaught_exceptions())
     {
         data_->createRule(impl_);
     }
diff --git a/src/gromacs/utility/listoflists.h b/src/gromacs/utility/listoflists.h
new file mode 100644 (file)
index 0000000..8d707a0
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::ListOfLists
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_LISTOFLISTS_H
+#define GMX_UTILITY_LISTOFLISTS_H
+
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/exceptions.h"
+
+namespace gmx
+{
+
+/*! \brief A list of lists, optimized for performance
+ *
+ * This class holds a list of \p size() lists of elements of type \p T.
+ * To optimize performance, the only modification operation supporting
+ * is adding a new list at the end of the list of lists.
+ *
+ * This implementation stores all data internally in two std::vector objects
+ * and thereby avoids the overhead of managing \p size() separate objects
+ * in memory.
+ *
+ * Internal storage consists of one std::vector<int> listRanges_ of size number
+ * of lists plus one and a std::vector<T> elements_ with the elements of all
+ * lists concatenated. List i is stored in entries listRanges_[i] to
+ * listRanges_[i+1] in elements_.
+ *
+ * \note This class is currently limited to arithmetic types, mainly because
+ * this should only be used for performance critical applications.
+ * When performance is not critical, a std::vector of std::vector can be used.
+ *
+ * \tparam T value type
+ */
+
+template<typename T>
+class ListOfLists
+{
+    // TODO: Use std::is_arithmetic_v when CUDA 11 is a requirement.
+    static_assert(std::is_arithmetic<T>::value, "This class is limited to arithmetic types");
+
+public:
+    //! Constructs an empty list of lists
+    ListOfLists() = default;
+
+    /*! \brief Constructs a list of list from raw data in internal layout
+     *
+     * Does basic consistency checks and throws when one of those fail.
+     *
+     * \param[in] listRanges  Ranges of the lists concatenated (see above), is consumed
+     * \param[in] elements    Elements for all lists concatenated, is consumed
+     */
+    ListOfLists(std::vector<int>&& listRanges, std::vector<T>&& elements) :
+        listRanges_(std::move(listRanges)),
+        elements_(std::move(elements))
+    {
+        if (listRanges_.empty() || listRanges_.at(0) != 0)
+        {
+            GMX_THROW(InconsistentInputError(
+                    "listRanges does not have a first element with value 0"));
+        }
+        if (int(elements_.size()) != listRanges_.back())
+        {
+            GMX_THROW(InconsistentInputError(
+                    "The size of elements does not match the last value in listRanges"));
+        }
+    }
+
+    //! Returns the number of lists
+    std::size_t size() const { return listRanges_.size() - 1; }
+
+    /*! \brief Returns the number of lists
+     *
+     * \note Use ssize for any expression involving arithmetic operations
+     * (including loop indices).
+     */
+    index ssize() const { return index(listRanges_.size()) - 1; }
+
+    //! Returns whether the list holds no lists
+    bool empty() const { return listRanges_.size() == 1; }
+
+    //! Returns the sum of the number of elements over all lists
+    int numElements() const { return listRanges_.back(); }
+
+    //! Appends a new list with elements \p values, pass {} to add an empty list
+    void pushBack(ArrayRef<const T> values)
+    {
+        elements_.insert(elements_.end(), values.begin(), values.end());
+        listRanges_.push_back(int(elements_.size()));
+    }
+
+    //! Appends a new list with \p numElements elements
+    void pushBackListOfSize(int numElements)
+    {
+        // With arithmetic types enforced, this assertion is always true
+        // TODO: Use std::is_default_constructible_v when CUDA 11 is a requirement.
+        static_assert(std::is_default_constructible<T>::value,
+                      "pushBackListOfSize should only be called with default constructable types");
+        elements_.resize(elements_.size() + numElements);
+        listRanges_.push_back(int(elements_.size()));
+    }
+
+    //! Returns an ArrayRef to the elements of the list with the given index
+    ArrayRef<const T> operator[](std::size_t listIndex) const
+    {
+        return ArrayRef<const T>(elements_.data() + listRanges_[listIndex],
+                                 elements_.data() + listRanges_[listIndex + 1]);
+    }
+
+    //! Returns the list of elements for the list with index \p listIndex, throws an \p out_of_range exception when out of range
+    ArrayRef<const T> at(std::size_t listIndex) const
+    {
+        return ArrayRef<const T>(elements_.data() + listRanges_.at(listIndex),
+                                 elements_.data() + listRanges_.at(listIndex + 1));
+    }
+
+    /*! \brief Returns a reference to the first list
+     *
+     * \returns a reference to the first list
+     */
+    ArrayRef<T> front()
+    {
+        GMX_ASSERT(size() > 0, "Must contain a list if front() is called");
+        auto beginPtr = elements_.data();
+        auto endPtr   = beginPtr + listRanges_[1];
+        return { beginPtr, endPtr };
+    }
+    /*! \brief Returns a reference to the final list
+     *
+     * \returns a reference to the final list
+     */
+    ArrayRef<T> back()
+    {
+        GMX_ASSERT(size() > 0, "Must contain a list if bank() is called");
+        auto endIndex   = *(listRanges_.end() - 1);
+        auto beginIndex = *(listRanges_.end() - 2);
+        return { elements_.data() + beginIndex, elements_.data() + endIndex };
+    }
+
+    //! Clears the list
+    void clear()
+    {
+        listRanges_.resize(1);
+        elements_.clear();
+    }
+
+    //! Appends a ListOfLists at the end and increments the appended elements by \p offset
+    void appendListOfLists(const ListOfLists& listOfLists, const T offset = 0)
+    {
+        listRanges_.insert(listRanges_.end(), listOfLists.listRanges_.begin() + 1,
+                           listOfLists.listRanges_.end());
+        const int oldNumElements = elements_.size();
+        for (std::size_t i = listRanges_.size() - listOfLists.size(); i < listRanges_.size(); i++)
+        {
+            listRanges_[i] += oldNumElements;
+        }
+        elements_.insert(elements_.end(), listOfLists.elements_.begin(), listOfLists.elements_.end());
+
+        if (offset != 0)
+        {
+            for (std::size_t i = elements_.size() - listOfLists.elements_.size(); i < elements_.size(); i++)
+            {
+                elements_[i] += offset;
+            }
+        }
+    }
+
+    //! Returns concatenated ranges of the lists (see above for details)
+    ArrayRef<const int> listRangesView() const { return listRanges_; }
+
+    //! Returns the a view of the elements of all lists concatenated
+    ArrayRef<const T> elementsView() const { return elements_; }
+
+private:
+    //! The ranges of the lists, list i uses range \p listRanges_[i], \p listRanges_[i+1].
+    std::vector<int> listRanges_ = { 0 };
+    //! The elements in all lists concatenated
+    std::vector<T> elements_;
+};
+
+} // namespace gmx
+
+#endif
index 1cc1e8a7adddc5e6121ed22837a9b3afbde88c7d..5948a01e6c5c21ef1b6459ab302fee613ce60f9d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -111,7 +111,7 @@ private:
  *
  * This class is responsible of managing all memory allocated by LoggerBuilder
  * that is needed for operation of the actual logger.  Also the actual logger
- * instance is owned by this class.  This allows keeing the actual logger simple
+ * instance is owned by this class.  This allows keeping the actual logger simple
  * and streamlined.
  *
  * This class supports move construction and assignment, which allows
diff --git a/src/gromacs/utility/mdmodulenotification-impl.h b/src/gromacs/utility/mdmodulenotification-impl.h
new file mode 100644 (file)
index 0000000..a402504
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::MdModuleNotification.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+
+#ifndef GMX_UTILITY_MDMODULENOTIFICATION_IMPL_H
+#define GMX_UTILITY_MDMODULENOTIFICATION_IMPL_H
+
+#include <functional>
+#include <vector>
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Subscribe and trigger notification functions.
+ *
+ * Extends MdModuleNotificationBase with new notification function and routine
+ * to subscribe new listeners.
+ *
+ * To create a class of this type that provides callbacks, e.g., for events
+ * EventA, and EventB use registerMdModuleNotification<EventA, EventB>::type.
+ *
+ * \tparam CallParameter of the function to be notified
+ * \tparam MdModuleNotificationBase class to be extended with a notification
+ *                                  with CallParameter
+ *
+ * \note All added subscribers are required to out-live the MdModuleNotification
+ *
+ */
+template<class CallParameter, class MdModuleNotificationBase>
+class MdModuleNotification : public MdModuleNotificationBase
+{
+public:
+    //! Make base class notification trigger available to this class
+    using MdModuleNotificationBase::notify;
+    //! Make base class subscription available to this class
+    using MdModuleNotificationBase::subscribe;
+
+    /*! \brief Trigger the subscribed notifications.
+     * \param[in] callParameter of the function to be called back
+     */
+    void notify(CallParameter callParameter) const
+    {
+        for (auto& callBack : callBackFunctions_)
+        {
+            callBack(callParameter);
+        }
+    }
+
+    /*! \brief
+     * Add callback function to be called when notification is triggered.
+     *
+     * Notifications are distinguished by their call signature.
+     *
+     * \param[in] callBackFunction to be called from this class
+     */
+    void subscribe(std::function<void(CallParameter)> callBackFunction)
+    {
+        callBackFunctions_.emplace_back(callBackFunction);
+    }
+
+private:
+    std::vector<std::function<void(CallParameter)>> callBackFunctions_;
+};
+
+/*! \internal
+ * \brief Aide to avoid nested MdModuleNotification definition.
+ *
+ * Instead of
+ * MdModuleNotification<CallParameterA, MdModuleNotification<CallParameterB, etc ... >>
+ * this allows to write
+ * registerMdModuleNotification<CallParameterA, CallParameterB, ...>::type
+ *
+ * \tparam CallParameter all the event types to be registered
+ */
+template<class... CallParameter>
+struct registerMdModuleNotification;
+
+/*! \internal \brief Template specialization to end parameter unpacking recursion.
+ */
+template<>
+struct registerMdModuleNotification<>
+{
+    /*! \internal
+     * \brief Do nothing but be base class of MdModuleNotification.
+     *
+     * Required so that using MdModuleNotificationBase::notify and
+     * MdModuleNotificationBase::subscribe are valid in derived class.
+     */
+    class NoCallParameter
+    {
+    public:
+        //! Do nothing but provide MdModuleNotification::notify to derived class
+        void notify() {}
+        //! Do nothing but provide MdModuleNotification::subscribe to derived class
+        void subscribe() {}
+    };
+    /*! \brief Defines a type if no notifications are managed.
+     *
+     * This ensures that code works with MdModuleCallParameterManagement that
+     * does not manage any notifications.
+     */
+    using type = NoCallParameter;
+};
+
+/*! \libinternal
+ * \brief Template specialization to assemble MdModuleNotification.
+ *
+ * Assembly of MdModuleNotification is performed by recursively taking off the
+ * front of the CallParameter parameter pack and constructing the nested type
+ * definition of MdModuleNotification base classes.
+ *
+ * \tparam CurrentCallParameter front of the template parameter pack
+ * \tparam CallParameter rest of the event types
+ */
+template<class CurrentCallParameter, class... CallParameter>
+struct registerMdModuleNotification<CurrentCallParameter, CallParameter...>
+{
+    // private:
+    //! The next type with rest of the arguments with the front parameter removed.
+    using next_type = typename registerMdModuleNotification<CallParameter...>::type;
+    //! The type of the MdModuleNotification
+    using type = MdModuleNotification<CurrentCallParameter, next_type>;
+};
+
+} // namespace gmx
+
+#endif
index c56160420d3457ae8bf6886ab386666d2895f79f..b5df1e334096f7213d7e146870077140e390b936 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  */
 /*! \libinternal \file
  * \brief
- * Declares gmx::MdModuleNotification.
+ * Declares gmx::MdModulesNotifier.
  *
  * \author Christian Blau <blau@kth.se>
  * \inlibraryapi
  * \ingroup module_utility
  */
 
-#ifndef GMX_MDRUNUTILITY_MDMODULENOTIFICATION_H
-#define GMX_MDRUNUTILITY_MDMODULENOTIFICATION_H
+#ifndef GMX_UTILITY_MDMODULENOTIFICATION_H
+#define GMX_UTILITY_MDMODULENOTIFICATION_H
 
-#include <functional>
 #include <string>
 #include <vector>
 
+#include "gromacs/utility/mdmodulenotification-impl.h"
+
 struct t_commrec;
+enum class PbcType : int;
 
 namespace gmx
 {
 
-/*! \libinternal \brief
- * Subscribe and trigger notification functions.
- *
- * Extends MdModuleNotificationBase with new notification function and routine
- * to subscribe new listeners.
- *
- * To create a class of this type that provides callbacks, e.g., for events
- * EventA, and EventB use registerMdModuleNotification<EventA, EventB>::type.
- *
- * \tparam CallParameter of the function to be notified
- * \tparam MdModuleNotificationBase class to be extended with a notification
- *                                  with CallParameter
- *
-   \msc
-   wordwraparcs=true,
-   hscale="2";
-
-   runner [label="runner:\nMdrunner"],
-   CallParameter [label = "eventA:\nCallParameter"],
-   MOD [label = "mdModules_:\nMdModules"],
-   ModuleA [label="moduleA"],
-   ModuleB [label="moduleB"],
-   MdModuleNotification [label="notifier_:\nMdModuleNotification"];
-
-   MOD box MdModuleNotification [label = "mdModules_ owns notifier_ and moduleA/B"];
-   MOD =>> ModuleA [label="instantiates(notifier_)"];
-   ModuleA =>> MdModuleNotification [label="subscribe(otherfunc)"];
-   ModuleA =>> MOD;
-   MOD =>> ModuleB [label="instantiates(notifier_)"];
-   ModuleB =>> MdModuleNotification [label="subscribe(func)"];
-   ModuleB =>> MOD;
-   runner =>> CallParameter [label="instantiate"];
-   CallParameter =>> runner ;
-   runner =>> MOD [label="notify(eventA)"];
-   MOD =>> MdModuleNotification [label="notify(eventA)"];
-   MdModuleNotification =>> ModuleA [label="notify(eventA)"];
-   ModuleA -> ModuleA [label="func(eventA)"];
-   MdModuleNotification =>> ModuleB [label="notify(eventA)"];
-   ModuleB -> ModuleB [label="otherfunc(eventA)"];
-
-   \endmsc
- *
- * \note All added subscribers are required to out-live the MdModuleNotification
- *
- */
-template<class CallParameter, class MdModuleNotificationBase>
-class MdModuleNotification : public MdModuleNotificationBase
-{
-public:
-    //! Make base class notification trigger available to this class
-    using MdModuleNotificationBase::notify;
-    //! Make base class subscription available to this class
-    using MdModuleNotificationBase::subscribe;
-
-    /*! \brief Trigger the subscribed notifications.
-     * \param[in] callParameter of the function to be called back
-     */
-    void notify(CallParameter callParameter) const
-    {
-        for (auto& callBack : callBackFunctions_)
-        {
-            callBack(callParameter);
-        }
-    }
-
-    /*! \brief
-     * Add callback function to be called when notification is triggered.
-     *
-     * Notifications are distinguished by their call signature.
-     *
-     * \param[in] callBackFunction to be called from this class
-     */
-    void subscribe(std::function<void(CallParameter)> callBackFunction)
-    {
-        callBackFunctions_.emplace_back(callBackFunction);
-    }
-
-private:
-    std::vector<std::function<void(CallParameter)>> callBackFunctions_;
-};
-
-/*! \internal
- * \brief Aide to avoid nested MdModuleNotification definition.
- *
- * Instead of
- * MdModuleNotification<CallParameterA, MdModuleNotification<CallParameterB, etc ... >>
- * this allows to write
- * registerMdModuleNotification<CallParameterA, CallParameterB, ...>::type
- *
- * \tparam CallParameter all the event types to be registered
- */
-template<class... CallParameter>
-struct registerMdModuleNotification;
-
-/*! \internal \brief Template specialization to end parameter unpacking recursion.
- */
-template<>
-struct registerMdModuleNotification<>
-{
-    /*! \internal
-     * \brief Do nothing but be base class of MdModuleNotification.
-     *
-     * Required so that using MdModuleNotificationBase::notify and
-     * MdModuleNotificationBase::subscribe are valid in derived class.
-     */
-    class NoCallParameter
-    {
-    public:
-        //! Do nothing but provide MdModuleNotification::notify to derived class
-        void notify() {}
-        //! Do nothing but provide MdModuleNotification::subscribe to derived class
-        void subscribe() {}
-    };
-    /*! \brief Defines a type if no notifications are managed.
-     *
-     * This ensures that code works with MdModuleCallParameterManagement that
-     * does not manage any notifications.
-     */
-    using type = NoCallParameter;
-};
-
-/*! \libinternal
- * \brief Template specialization to assemble MdModuleNotification.
- *
- * Assembly of MdModuleNotification is performed by recursively taking off the
- * front of the CallParameter parameter pack and constructing the nested type
- * definition of MdModuleNotification base classes.
- *
- * \tparam CurrentCallParameter front of the template parameter pack
- * \tparam CallParameter rest of the event types
- */
-template<class CurrentCallParameter, class... CallParameter>
-struct registerMdModuleNotification<CurrentCallParameter, CallParameter...>
-{
-    // private:
-    //! The next type with rest of the arguments with the front parameter removed.
-    using next_type = typename registerMdModuleNotification<CallParameter...>::type;
-    //! The type of the MdModuleNotification
-    using type = MdModuleNotification<CurrentCallParameter, next_type>;
-};
-
 class KeyValueTreeObject;
 class KeyValueTreeObjectBuilder;
 class LocalAtomSetManager;
@@ -201,13 +62,14 @@ class IndexGroupsAndNames;
 struct MdModulesCheckpointReadingDataOnMaster;
 struct MdModulesCheckpointReadingBroadcast;
 struct MdModulesWriteCheckpointData;
-struct PeriodicBoundaryConditionType
-{
-    int pbcType;
-};
 
+/*! \libinternal \brief Check if module outputs energy to a specific field.
+ *
+ * Ensures that energy is output for this module.
+ */
 struct MdModulesEnergyOutputToDensityFittingRequestChecker
 {
+    //! Trigger output to density fitting energy field
     bool energyOutputToDensityFitting_ = false;
 };
 
@@ -246,27 +108,112 @@ private:
     std::vector<std::string> errorMessages_;
 };
 
+/*! \libinternal \brief Provides the simulation time step in ps.
+ */
 struct SimulationTimeStep
 {
     //! Time step (ps)
     const double delta_t;
 };
 
+/*! \libinternal
+ * \brief Collection of callbacks to MDModules at differnt run-times.
+ *
+ * MDModules use members of this struct to sign up for callback functionality.
+ *
+ * The members of the struct represent callbacks at these run-times:
+ *
+ *  When pre-processing the simulation data
+ *  When reading and writing check-pointing data
+ *  When setting up simulation after reading in the tpr file
+ *
+   \msc
+   wordwraparcs=true,
+   hscale="2";
+
+   runner [label="runner:\nMdrunner"],
+   CallParameter [label = "eventA:\nCallParameter"],
+   MOD [label = "mdModules_:\nMdModules"],
+   ModuleA [label="moduleA"],
+   ModuleB [label="moduleB"],
+   MdModuleNotification [label="notifier_:\nMdModuleNotification"];
+
+   MOD box MdModuleNotification [label = "mdModules_ owns notifier_ and moduleA/B"];
+   MOD =>> ModuleA [label="instantiates(notifier_)"];
+   ModuleA =>> MdModuleNotification [label="subscribe(otherfunc)"];
+   ModuleA =>> MOD;
+   MOD =>> ModuleB [label="instantiates(notifier_)"];
+   ModuleB =>> MdModuleNotification [label="subscribe(func)"];
+   ModuleB =>> MOD;
+   runner =>> CallParameter [label="instantiate"];
+   CallParameter =>> runner ;
+   runner =>> MOD [label="notify(eventA)"];
+   MOD =>> MdModuleNotification [label="notify(eventA)"];
+   MdModuleNotification =>> ModuleA [label="notify(eventA)"];
+   ModuleA -> ModuleA [label="func(eventA)"];
+   MdModuleNotification =>> ModuleB [label="notify(eventA)"];
+   ModuleB -> ModuleB [label="otherfunc(eventA)"];
+
+   \endmsc
+ *
+ * The template arguments to the members of this struct directly reflect
+ * the callback function signature. Arguments passed as pointers are always
+ * meant to be modified, but never meant to be stored (in line with the policy
+ * everywhere else).
+ */
 struct MdModulesNotifier
 {
-    //! Register callback function types for MdModule
-    registerMdModuleNotification<const t_commrec&,
-                                 EnergyCalculationFrequencyErrors*,
-                                 IndexGroupsAndNames,
-                                 KeyValueTreeObjectBuilder,
-                                 const KeyValueTreeObject&,
+    /*! \brief Pre-processing callback functions.
+     *
+     * EnergyCalculationFrequencyErrors* allows modules to check if they match
+     *                                   their required calculation frequency
+     *                                   and add their error message if needed
+     *                                   to the collected error messages
+     * IndexGroupsAndNames provides modules with atom indices and their names
+     * KeyValueTreeObjectBuilder enables writing of module internal data to
+     *                           .tpr files.
+     */
+    registerMdModuleNotification<EnergyCalculationFrequencyErrors*, IndexGroupsAndNames, KeyValueTreeObjectBuilder>::type preProcessingNotifications_;
+
+    /*! \brief Checkpointing callback functions.
+     *
+     * MdModulesCheckpointReadingDataOnMaster provides modules with their
+     *                                        checkpointed data on the master
+     *                                        node and checkpoint file version
+     * MdModulesCheckpointReadingBroadcast provides modules with a communicator
+     *                                     and the checkpoint file version to
+     *                                     distribute their data
+     * MdModulesWriteCheckpointData provides the modules with a key-value-tree
+     *                              builder to store their checkpoint data and
+     *                              the checkpoint file version
+     */
+    registerMdModuleNotification<MdModulesCheckpointReadingDataOnMaster,
+                                 MdModulesCheckpointReadingBroadcast,
+                                 MdModulesWriteCheckpointData>::type checkpointingNotifications_;
+
+    /*! \brief Callbacks during simulation setup.
+     *
+     * const KeyValueTreeObject& provides modules with the internal data they
+     *                           wrote to .tpr files
+     * LocalAtomSetManager* enables modules to add atom indices to local atom sets
+     *                      to be managed
+     * MdModulesEnergyOutputToDensityFittingRequestChecker* enables modules to
+     *                      report if they want to write their energy output
+     *                      to the density fitting field in the energy files
+     * const PbcType& provides modules with the periodic boundary condition type
+     *                that is used during the simulation
+     * const SimulationTimeStep& provides modules with the simulation time-step
+     *                           that allows them to interconvert between step
+     *                           time information
+     * const t_commrec& provides a communicator to the modules during simulation
+     *                  setup
+     */
+    registerMdModuleNotification<const KeyValueTreeObject&,
                                  LocalAtomSetManager*,
                                  MdModulesEnergyOutputToDensityFittingRequestChecker*,
-                                 MdModulesCheckpointReadingDataOnMaster,
-                                 MdModulesCheckpointReadingBroadcast,
-                                 MdModulesWriteCheckpointData,
-                                 PeriodicBoundaryConditionType,
-                                 const SimulationTimeStep&>::type notifier_;
+                                 const PbcType&,
+                                 const SimulationTimeStep&,
+                                 const t_commrec&>::type simulationSetupNotifications_;
 };
 
 } // namespace gmx
index 7214749dee7c01e0230af00e38387bd702fb4152..1b0f4645af6ec9f72660429a5aba23909fbbcf51 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2011-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -55,6 +55,7 @@
 
 #include <algorithm>
 #include <string>
+#include <string_view>
 #include <utility>
 
 #include <sys/stat.h>
@@ -250,13 +251,13 @@ namespace
  *
  * \returns A view of the parent-path components, or empty if no
  * directory separator exists. */
-compat::string_view getParentPathView(const std::string& input)
+std::string_view getParentPathView(const std::string& input)
 {
-    auto   inputView = compat::to_string_view(input);
+    auto   inputView = std::string_view(input);
     size_t pos       = inputView.find_last_of(cDirSeparators);
     if (pos == std::string::npos)
     {
-        return compat::string_view();
+        return std::string_view();
     }
     return inputView.substr(0, pos);
 }
@@ -265,7 +266,7 @@ compat::string_view getParentPathView(const std::string& input)
  * last directory separator (if one exists).
  *
  * \returns A view of the filename component. */
-compat::string_view getFilenameView(const compat::string_view input)
+std::string_view getFilenameView(const std::string_view input)
 {
     size_t pos = input.find_last_of(cDirSeparators);
     if (pos == std::string::npos)
@@ -281,7 +282,7 @@ compat::string_view getFilenameView(const compat::string_view input)
  * filename component, ie. omitting any leading directories.
  *
  * \returns  The view of the filename stem, or empty if none exists. */
-compat::string_view getStemView(const std::string& input)
+std::string_view getStemView(const std::string& input)
 {
     auto   filenameView               = getFilenameView(input);
     size_t extensionSeparatorPosition = filenameView.find_last_of('.');
@@ -295,14 +296,14 @@ compat::string_view getStemView(const std::string& input)
  * filename component, ie. omitting any leading directories.
  *
  * \returns  The view of the file extension, or empty if none exists. */
-compat::string_view getExtensionView(const compat::string_view input)
+std::string_view getExtensionView(const std::string_view input)
 {
     auto   filenameView               = getFilenameView(input);
     size_t extensionSeparatorPosition = filenameView.find_last_of('.');
-    if (extensionSeparatorPosition == compat::string_view::npos)
+    if (extensionSeparatorPosition == std::string_view::npos)
     {
         // No separator was found
-        return compat::string_view();
+        return std::string_view();
     }
     return filenameView.substr(extensionSeparatorPosition);
 }
@@ -311,12 +312,12 @@ compat::string_view getExtensionView(const compat::string_view input)
 
 std::string Path::getParentPath(const std::string& input)
 {
-    return compat::to_string(getParentPathView(input));
+    return std::string(getParentPathView(input));
 }
 
 std::string Path::getFilename(const std::string& input)
 {
-    return to_string(getFilenameView(input));
+    return std::string(getFilenameView(input));
 }
 
 bool Path::hasExtension(const std::string& input)
@@ -327,7 +328,7 @@ bool Path::hasExtension(const std::string& input)
     return getFilenameView(input).find('.') != std::string::npos;
 }
 
-bool Path::extensionMatches(const compat::string_view input, const compat::string_view extension)
+bool Path::extensionMatches(const std::string_view input, const std::string_view extension)
 {
     auto extensionWithSeparator = getExtensionView(input);
     return (!extensionWithSeparator.empty() && extensionWithSeparator.substr(1) == extension);
index f203e96b7bb6a0154c1e8549409359ecf66294e6..354ae7ea80bbc55054eb7aea63608502d0f6d801 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #define GMX_UTILITY_PATH_H
 
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
-#include "gromacs/compat/string_view.h"
-
 namespace gmx
 {
 
@@ -79,7 +79,7 @@ public:
     /*! \brief Returns whether the extension present in \c input
      * matches \c extension (which does not include the separator
      * character). */
-    static bool extensionMatches(compat::string_view input, compat::string_view extension);
+    static bool extensionMatches(std::string_view input, std::string_view extension);
     /*! \brief Returns a copy of the input without any trailing
      * extension found in the filename component. */
     static std::string stripExtension(const std::string& input);
index 4c0b71b390dc66691e9a8753b80866f4477e00d6..c5985c48fdbc7e1dd791a349233537951f17a1e4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -292,6 +293,9 @@ void please_cite(FILE* fp, const char* key)
         { "Lindahl2014", "V. Lindahl, J. Lidmar, B. Hess",
           "Accelerated weight histogram method for exploring free energy landscapes",
           "J. Chem. Phys.", 141, 2014, "044110" },
+        { "Bernetti2020", "M. Bernetti, G. Bussi",
+          "Pressure control using stochastic cell rescaling", "J. Chem. Phys.", 153, 2020,
+          "114107" },
     };
 #define NSTR static_cast<int>(asize(citedb))
 
index c0f7800f8baa246cfa82d8c2f73741178a3093bc..acc70d8d1586d1b0a37599de5e4a027c0668eea6 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 56f92045b9e933a4f1b726f93548ee4b74fb684f..01d13705c41f11852b393244ce235eb0b116df73 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +43,8 @@
 #ifndef GMX_UTILITY_RANGE_H
 #define GMX_UTILITY_RANGE_H
 
+#include <type_traits>
+
 #include "gromacs/utility/gmxassert.h"
 
 namespace gmx
@@ -61,6 +63,7 @@ namespace gmx
 template<typename T>
 class Range
 {
+    // TODO: Use std::is_integral_v when CUDA 11 is a requirement.
     static_assert(std::is_integral<T>::value, "Range can only be used with integral types");
 
     // Note: This class has as invariant: begin_ <= end_
@@ -76,7 +79,7 @@ public:
         //! Reference
         operator T&() { return value_; }
         //! Pointer
-        int operator*() const { return value_; }
+        T operator*() const { return value_; }
         //! Inequality comparison
         bool operator!=(const iterator other) { return value_ != other.value_; }
         //! Increment operator
@@ -86,7 +89,7 @@ public:
             return *this;
         }
         //! Increment operator
-        iterator operator++(T gmx_unused dummy)
+        iterator operator++(int gmx_unused dummy)
         {
             iterator tmp(*this);
             ++value_;
index 13676a6a04350710e9dfbc35b3525bb1e53de676..2a959637a195a04ca2ad44f1b9aafff659c8ed93 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b6dd271c3304650cc913b1d0a6538d8f42d26d2d..81895f9c3ed0eaea61bd8cfe262fb8f20afd7287 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index adb5c9a0babf407c127ef763568b32f74ddbc1f5..f68de025d347e350bb597075b73eb22b980dd644 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -202,20 +202,25 @@ void save_free_aligned(const char* name, const char* file, int line, void* ptr);
 template<typename T>
 static inline void gmx_snew_impl(const char* name, const char* file, int line, T*& ptr, size_t nelem)
 {
+    // TODO: Use std::is_pod_v when CUDA 11 is a requirement.
     static_assert(std::is_pod<T>::value, "snew() called on C++ type");
+    // NOLINTNEXTLINE bugprone-sizeof-expression
     ptr = static_cast<T*>(save_calloc(name, file, line, nelem, sizeof(T)));
 }
 /** C++ helper for srenew(). */
 template<typename T>
 static inline void gmx_srenew_impl(const char* name, const char* file, int line, T*& ptr, size_t nelem)
 {
+    // TODO: Use std::is_pod_v when CUDA 11 is a requirement.
     static_assert(std::is_pod<T>::value, "srenew() called on C++ type");
+    // NOLINTNEXTLINE bugprone-sizeof-expression
     ptr = static_cast<T*>(save_realloc(name, file, line, ptr, nelem, sizeof(T)));
 }
 /** C++ helper for smalloc(). */
 template<typename T>
 static inline void gmx_smalloc_impl(const char* name, const char* file, int line, T*& ptr, size_t size)
 {
+    // TODO: Use std::is_pod_v when CUDA 11 is a requirement.
     static_assert(std::is_pod<T>::value, "smalloc() called on C++ type");
     ptr = static_cast<T*>(save_malloc(name, file, line, size));
 }
@@ -224,6 +229,7 @@ template<typename T>
 static inline void
 gmx_snew_aligned_impl(const char* name, const char* file, int line, T*& ptr, size_t nelem, size_t alignment)
 {
+    // TODO: Use std::is_pod_v when CUDA 11 is a requirement.
     static_assert(std::is_pod<T>::value, "snew_aligned() called on C++ type");
     ptr = static_cast<T*>(save_calloc_aligned(name, file, line, nelem, sizeof(T), alignment));
 }
@@ -231,6 +237,7 @@ gmx_snew_aligned_impl(const char* name, const char* file, int line, T*& ptr, siz
 template<typename T>
 static inline void gmx_sfree_impl(const char* name, const char* file, int line, T* ptr)
 {
+    // TODO: Use std::is_pod_v and std::is_void_v when CUDA 11 is a requirement.
     static_assert(std::is_pod<T>::value || std::is_void<T>::value, "sfree() called on C++ type");
     save_free(name, file, line, ptr);
 }
@@ -238,6 +245,7 @@ static inline void gmx_sfree_impl(const char* name, const char* file, int line,
 template<typename T>
 static inline void gmx_sfree_aligned_impl(const char* name, const char* file, int line, T* ptr)
 {
+    // TODO: Use std::is_pod_v and std::is_void_v when CUDA 11 is a requirement.
     static_assert(std::is_pod<T>::value || std::is_void<T>::value,
                   "sfree_aligned() called on C++ type");
     save_free_aligned(name, file, line, ptr);
index cff46c1c061e301a08fea3c7dbd5f210cd2b8d94..94900a97521d1169e585f565a878dc3441f40b2a 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index f9eaa30accd0fdadbd52b5c62f16c7c46bc91d67..805717fb7e5ec7678da375d515d3306a0def8bc2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef GMX_UTILITY_STRCONVERT_H
 #define GMX_UTILITY_STRCONVERT_H
 
+#include <algorithm>
+#include <array>
+#include <optional>
 #include <string>
 
 #include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/stringutil.h"
 
 namespace gmx
@@ -218,6 +222,100 @@ static inline std::string toString(std::string t)
 //! \}
 //! \endcond
 
+/*! \brief Convert a string into an array of values.
+ *
+ * \tparam ValueType array element type to convert into
+ * \tparam NumExpectedValues number of values of the array
+ *
+ * \returns an array containing the converted string, optionally null if
+ *          the white-space stripped string is empty
+ *
+ * \throws InvalidInputError if splitting the string at whitespaces does not
+ *                           result in NumExpectedValues or zero substrings
+ *
+ * \throws InvalidInputError if conversion of any of the NumExpectedValues
+ *                           substrings of the splitted input string fails
+ *
+ * Converts a string into an array of type ValueType with exactly NumExpectedValues.
+ *
+ * No result is returned if the string is empty or contains only whitespace .
+ *
+ */
+template<typename ValueType, int NumExpectedValues>
+static inline std::optional<std::array<ValueType, NumExpectedValues>>
+parsedArrayFromInputString(const std::string& str)
+{
+    // return nullopt right away if the string is just whitespace or empty
+    {
+        const std::string& strippedString = stripString(str);
+        if (strippedString.empty())
+        {
+            return std::nullopt;
+        }
+    }
+
+    const std::vector<std::string>& valuesAsStrings = splitString(str);
+
+    // throw right away if we don't have the expected number of string entries
+    if (valuesAsStrings.size() != NumExpectedValues)
+    {
+        const std::string errorMessage =
+                "Expected empty string or string with " + intToString(NumExpectedValues)
+                + " elements to convert, but received " + intToString(valuesAsStrings.size())
+                + " elements instead.";
+        GMX_THROW(InvalidInputError(errorMessage));
+    }
+
+    // will throw if any conversion from string to value fails
+    std::array<ValueType, NumExpectedValues> valuesAsArray;
+    std::transform(std::begin(valuesAsStrings), std::end(valuesAsStrings), std::begin(valuesAsArray),
+                   [](const std::string& split) { return fromString<ValueType>(split); });
+
+    return { valuesAsArray };
+}
+
+/*! \brief Returns the input string, throwing an excpetion if the demanded
+ *         conversion to an array will not succeed.
+ *
+ * \tparam ValueType array element type to convert into
+ * \tparam NumExpectedValues number of values of the array
+ *
+ * \param[in] toConvert the string to convert
+ * \param[in] errorContextMessage the message to add to the thrown exceptions if
+ *                                conversion of the string is bound to fail at
+ *                                some point
+ * \returns the input string
+ *
+ * \throws InvalidInputError if splitting the string at whitespaces does not
+ *                           result in NumExpectedValues or zero substrings
+ *
+ * \throws InvalidInputError if conversion of any of the NumExpectedValues
+ *                           substrings of the splitted input string fails
+ *
+ * A typical use of this function would be in .mdp string option parsing
+ * where information in the .mdp file is transformed into the data that is
+ * stored in the .tpr file.
+ */
+template<typename ValueType, int NumExpectedValues>
+static inline std::string stringIdentityTransformWithArrayCheck(const std::string& toConvert,
+                                                                const std::string& errorContextMessage)
+{
+    // Attempt the conversion to an array so that the string parsing routine
+    // will throw an InvalidInputError if the string is not fit for conversion.
+    try
+    {
+        // The converted array is discarded.
+        gmx_unused const auto& val = parsedArrayFromInputString<ValueType, NumExpectedValues>(toConvert);
+    }
+    catch (const InvalidInputError& e)
+    {
+        InvalidInputError toThrow(errorContextMessage + std::string(e.what()));
+        GMX_THROW(toThrow);
+    }
+
+    return toConvert;
+}
+
 } // namespace gmx
 
 #endif
index ab8e315a9803d6a4e36a5640d1a9a49d2b932e6b..4b3642d25778e4c88d9eaf88f4064615f66490a8 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index fefb8758bc6adbc4b5c381655ba61419c7e665d5..616dce2d64d32909e90350c377c8bfd70a2ba61d 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e8040f05f7a5909654efe9add4ab6402d3b91263..e3f60426fbf36c19b9cd503c52e36aa83758ff32 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+# Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(UtilityUnitTests utility-test
-                  alignedallocator.cpp
-                  arrayref.cpp
-                  bitmask32.cpp bitmask64.cpp bitmask128.cpp
-                  cstringutil.cpp
-                  defaultinitializationallocator.cpp
-                  enumerationhelpers.cpp
-                  fixedcapacityvector.cpp
-                  inmemoryserializer.cpp
-                  keyvaluetreeserializer.cpp
-                  keyvaluetreetransform.cpp
-                  logger.cpp
-                  mutex.cpp
-                  path.cpp
-                  physicalnodecommunicator.cpp
-                  stringutil.cpp
-                  textreader.cpp
-                  textwriter.cpp
-                  typetraits.cpp
-                  )
+    CPP_SOURCE_FILES
+        alignedallocator.cpp
+        arrayref.cpp
+        bitmask32.cpp bitmask64.cpp bitmask128.cpp
+        cstringutil.cpp
+        defaultinitializationallocator.cpp
+        enumerationhelpers.cpp
+        fixedcapacityvector.cpp
+        inmemoryserializer.cpp
+        keyvaluetreeserializer.cpp
+        keyvaluetreetransform.cpp
+        listoflists.cpp
+        logger.cpp
+        mdmodulenotification-impl.cpp
+        mutex.cpp
+        path.cpp
+        physicalnodecommunicator.cpp
+        range.cpp
+        strconvert.cpp
+        stringutil.cpp
+        textreader.cpp
+        textwriter.cpp
+        typetraits.cpp
+        )
 
 gmx_add_mpi_unit_test(UtilityMpiUnitTests utility-mpi-test 4
-                  physicalnodecommunicator_mpi.cpp
-                  )
+    CPP_SOURCE_FILES
+        physicalnodecommunicator_mpi.cpp
+        )
index 8c9e2759ec67f6880bbacd323597b0d30bb28214..17cd919ce644a528782eb425a350be668b2a378f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -176,7 +176,7 @@ using makeConstIf_t = std::conditional_t<c, const T, T>;
 
 TYPED_TEST(ArrayRefTest, ConstructFromVectorWorks)
 {
-    makeConstIf_t<std::is_const<typename TestFixture::ValueType>::value, std::vector<typename TestFixture::NonConstValueType>> v(
+    makeConstIf_t<std::is_const_v<typename TestFixture::ValueType>, std::vector<typename TestFixture::NonConstValueType>> v(
             this->a, this->a + aSize);
     typename TestFixture::ArrayRefType arrayRef(v);
     this->runTests(v.data(), arrayRef);
index f17a83894f3c1ca0b8b8feda1bed6a21d2c678bc..2db9b643f50a4923553354736f6478f1cb22c29f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -109,13 +109,13 @@ public:
         real           realValue_;
     };
 
-    void serialize(ISerializer* serializer, SerializerValues* values)
+    static void serialize(ISerializer* serializer, SerializerValues* values)
     {
         EXPECT_FALSE(serializer->reading());
         doValues(serializer, values);
     }
 
-    SerializerValues deserialize(ISerializer* serializer)
+    static SerializerValues deserialize(ISerializer* serializer)
     {
         EXPECT_TRUE(serializer->reading());
         SerializerValues result;
@@ -123,7 +123,7 @@ public:
         return result;
     }
 
-    void checkSerializerValuesforEquality(const SerializerValues& lhs, const SerializerValues& rhs)
+    static void checkSerializerValuesforEquality(const SerializerValues& lhs, const SerializerValues& rhs)
     {
         EXPECT_EQ(lhs.boolValue_, rhs.boolValue_);
         EXPECT_EQ(lhs.unsignedCharValue_, rhs.unsignedCharValue_);
@@ -138,7 +138,7 @@ public:
     }
 
 private:
-    void doValues(ISerializer* serializer, SerializerValues* values)
+    static void doValues(ISerializer* serializer, SerializerValues* values)
     {
         serializer->doBool(&values->boolValue_);
         serializer->doUChar(&values->unsignedCharValue_);
@@ -162,7 +162,7 @@ protected:
                                         c_int64Value,
                                         c_intAndFloat64.doubleValue_,
                                         integerSizeDependentTestingValue(),
-                                        std::is_same<real, double>::value
+                                        std::is_same_v<real, double>
                                                 ? static_cast<real>(c_intAndFloat64.doubleValue_)
                                                 : static_cast<real>(c_intAndFloat32.floatValue_) };
 
@@ -176,8 +176,8 @@ protected:
         c_int64ValueSwapped,
         c_intAndFloat64Swapped.doubleValue_,
         integerSizeDependentTestingValueEndianessSwapped(),
-        std::is_same<real, float>::value ? static_cast<real>(c_intAndFloat32Swapped.floatValue_)
-                                         : static_cast<real>(c_intAndFloat64Swapped.doubleValue_)
+        std::is_same_v<real, float> ? static_cast<real>(c_intAndFloat32Swapped.floatValue_)
+                                    : static_cast<real>(c_intAndFloat64Swapped.doubleValue_)
     };
 };
 
@@ -189,7 +189,7 @@ TEST_F(InMemorySerializerTest, Roundtrip)
 
     auto buffer = serializer.finishAndGetBuffer();
 
-    InMemoryDeserializer deserializer(buffer, std::is_same<real, double>::value);
+    InMemoryDeserializer deserializer(buffer, std::is_same_v<real, double>);
 
     SerializerValues deserialisedValues = deserialize(&deserializer);
 
@@ -204,7 +204,7 @@ TEST_F(InMemorySerializerTest, RoundtripWithEndianessSwap)
 
     auto buffer = serializerWithSwap.finishAndGetBuffer();
 
-    InMemoryDeserializer deserializerWithSwap(buffer, std::is_same<real, double>::value,
+    InMemoryDeserializer deserializerWithSwap(buffer, std::is_same_v<real, double>,
                                               EndianSwapBehavior::Swap);
 
     SerializerValues deserialisedValues = deserialize(&deserializerWithSwap);
@@ -220,7 +220,7 @@ TEST_F(InMemorySerializerTest, SerializerExplicitEndianessSwap)
 
     auto buffer = serializerWithSwap.finishAndGetBuffer();
 
-    InMemoryDeserializer deserializerWithOutSwap(buffer, std::is_same<real, double>::value);
+    InMemoryDeserializer deserializerWithOutSwap(buffer, std::is_same_v<real, double>);
 
     SerializerValues deserialisedValues = deserialize(&deserializerWithOutSwap);
     checkSerializerValuesforEquality(endianessSwappedValues_, deserialisedValues);
@@ -234,7 +234,7 @@ TEST_F(InMemorySerializerTest, DeserializerExplicitEndianessSwap)
 
     auto buffer = serializer.finishAndGetBuffer();
 
-    InMemoryDeserializer deserializerWithSwap(buffer, std::is_same<real, double>::value,
+    InMemoryDeserializer deserializerWithSwap(buffer, std::is_same_v<real, double>,
                                               EndianSwapBehavior::Swap);
 
     SerializerValues deserialisedValues = deserialize(&deserializerWithSwap);
index 83eefe635a10f7cfb673cd21f6ee29116f8d2249..4d24c608ad301cefa57e7d9bd5ac0b94487b10f2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -108,7 +108,7 @@ public:
     gmx::KeyValueTreeBuilder builder_;
 
 private:
-    std::vector<char> serializeTree(const gmx::KeyValueTreeObject& tree)
+    static std::vector<char> serializeTree(const gmx::KeyValueTreeObject& tree)
     {
         gmx::InMemorySerializer serializer;
         gmx::serializeKeyValueTree(tree, &serializer);
diff --git a/src/gromacs/utility/tests/listoflists.cpp b/src/gromacs/utility/tests/listoflists.cpp
new file mode 100644 (file)
index 0000000..e36fb75
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for the ListOfLists class.
+ *
+ * \author berk Hess <hess@kth.se>
+ * \ingroup module_utility
+ */
+#include "gmxpre.h"
+
+#include "gromacs/utility/listoflists.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "testutils/testasserts.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+using ::testing::Eq;
+using ::testing::Pointwise;
+
+//! Compares all element between two lists of lists
+template<typename T>
+void compareLists(const ListOfLists<T>& list, const std::vector<std::vector<T>>& v)
+{
+    ASSERT_EQ(list.size(), v.size());
+    for (std::size_t i = 0; i < list.size(); i++)
+    {
+        ASSERT_EQ(list[i].size(), v[i].size());
+        EXPECT_THAT(list[i], Pointwise(Eq(), v[i]));
+    }
+}
+
+TEST(ListOfLists, EmptyListOfListsWorks)
+{
+    ListOfLists<char> list;
+
+    EXPECT_EQ(list.size(), 0);
+    EXPECT_EQ(list.empty(), true);
+    EXPECT_EQ(list.numElements(), 0);
+}
+
+//! Checks whether append works and stores the data correctly
+template<typename T>
+void checkAppend(const std::vector<std::vector<T>> inputLists)
+{
+    ListOfLists<T> list;
+
+    for (const auto& inputList : inputLists)
+    {
+        list.pushBack(inputList);
+    }
+    EXPECT_EQ(list.size(), 2);
+    compareLists(list, inputLists);
+}
+
+TEST(ListOfLists, AppendWorks)
+{
+    const std::vector<std::vector<char>> v = { { 5, 3 }, { char(-1), 7, 4 } };
+
+    checkAppend(v);
+}
+
+TEST(ListOfLists, EmptyListWorks)
+{
+    ListOfLists<char> list;
+
+    std::vector<char> v = { 5, 3 };
+    list.pushBack(v);
+    list.pushBack({});
+    EXPECT_EQ(list.size(), 2);
+    auto a = list[1];
+    EXPECT_EQ(a.empty(), true);
+}
+
+TEST(ListOfLists, AppendAccessWorks)
+{
+    const std::vector<std::vector<char>> v = { { 5, 3 }, { char(-1), 4 } };
+
+    ListOfLists<char> list;
+    list.pushBack(v[0]);
+    list.pushBackListOfSize(v[1].size());
+    std::copy(v[1].begin(), v[1].end(), list.back().begin());
+    compareLists(list, v);
+}
+
+TEST(ListOfLists, ClearWorks)
+{
+    ListOfLists<char> list;
+
+    std::vector<char> v = { 5, 3 };
+    list.pushBack(v);
+    list.pushBack({});
+    list.clear();
+    EXPECT_EQ(list.empty(), true);
+    EXPECT_EQ(list.numElements(), 0);
+}
+
+TEST(ListOfLists, OutOfRangeAccessThrows)
+{
+    ListOfLists<char> list;
+
+    std::vector<char> v = { 5, 3 };
+    EXPECT_THROW(list.at(1), std::out_of_range);
+}
+
+TEST(ListOfLists, FrontAndBackWork)
+{
+    ListOfLists<char> list1;
+    std::vector<char> v1{ { 3, 4 } };
+    list1.pushBack(v1);
+    EXPECT_THAT(list1.front(), Pointwise(Eq(), v1));
+    EXPECT_THAT(list1.back(), Pointwise(Eq(), v1));
+
+    std::vector<char> v2{ { 12, 63, 1 } };
+    list1.pushBack(v2);
+    EXPECT_THAT(list1.front(), Pointwise(Eq(), v1));
+    EXPECT_THAT(list1.back(), Pointwise(Eq(), v2));
+
+    list1.pushBack({});
+    EXPECT_THAT(list1.front(), Pointwise(Eq(), v1));
+    EXPECT_THAT(list1.back(), Pointwise(Eq(), std::vector<char>{}));
+
+    std::vector<char> v3{ { 99, 0, char(-1) } };
+    list1.pushBack(v3);
+    EXPECT_THAT(list1.front(), Pointwise(Eq(), v1));
+    EXPECT_THAT(list1.back(), Pointwise(Eq(), v3));
+
+    ListOfLists<char> list2;
+    list2.pushBack(v2);
+    EXPECT_THAT(list2.front(), Pointwise(Eq(), v2));
+    EXPECT_THAT(list2.back(), Pointwise(Eq(), v2));
+
+    list2.appendListOfLists(list1);
+    EXPECT_THAT(list2.front(), Pointwise(Eq(), v2));
+    EXPECT_THAT(list2.back(), Pointwise(Eq(), v3));
+    EXPECT_EQ(list2.back().size(), v3.size());
+
+    list2.pushBackListOfSize(1);
+    EXPECT_EQ(list2.back().size(), 1);
+}
+
+TEST(ListOfLists, ExtractsAndRestores)
+{
+    const std::vector<std::vector<char>> v({ { 5, 3 }, {}, { char(-1), 4 } });
+
+    ListOfLists<char> list1;
+    for (const auto& vlist : v)
+    {
+        list1.pushBack(vlist);
+    }
+
+    auto             listRanges = list1.listRangesView();
+    auto             elements   = list1.elementsView();
+    std::vector<int> listRangesVector;
+    listRangesVector.insert(listRangesVector.begin(), listRanges.begin(), listRanges.end());
+    std::vector<char> elementsVector;
+    elementsVector.insert(elementsVector.begin(), elements.begin(), elements.end());
+    ListOfLists<char> list2(std::move(listRangesVector), std::move(elementsVector));
+    compareLists(list2, v);
+}
+
+TEST(ListOfLists, AppendsListOfListsWithOffset)
+{
+    std::vector<std::vector<char>> v = { { 5, 3 }, { 2, char(-1) }, { 4 } };
+
+    ListOfLists<char> list1;
+    ListOfLists<char> list2;
+
+    list1.pushBack(v[0]);
+    list2.pushBack(v[1]);
+    list2.pushBack(v[2]);
+    const char offset = 2;
+    list1.appendListOfLists(list2, offset);
+    for (std::size_t i = 1; i < v.size(); i++)
+    {
+        for (auto& elem : v[i])
+        {
+            elem += offset;
+        }
+    }
+    compareLists(list1, v);
+}
+
+} // namespace
+
+} // namespace gmx
similarity index 94%
rename from src/programs/mdrun/tests/mdmodulenotification.cpp
rename to src/gromacs/utility/tests/mdmodulenotification-impl.cpp
index a2a32ecc8363d4e24f126fa25d9299bbf8f6721c..5107ed92a9e95f71ac59e27666d1d0ff45e70dd2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * Tests MdModuleNotification
  *
  * \author Christian Blau <blau@kth.se>
- * \ingroup module_mdrun_integration_tests
+ * \ingroup module_utility
  */
 #include "gmxpre.h"
 
-#include <gmock/gmock.h>
+#include "gromacs/utility/mdmodulenotification-impl.h"
 
-#include "gromacs/utility/mdmodulenotification.h"
+#include <gmock/gmock.h>
 
 namespace gmx
 {
@@ -61,7 +61,7 @@ struct EventB
 class EventACallee final
 {
 public:
-    void callback(EventA /*a*/) { notifiedEventA_ = true; };
+    void callback(EventA /*a*/) { notifiedEventA_ = true; }
 
     bool notifiedEventA() { return notifiedEventA_; }
 
@@ -72,7 +72,7 @@ private:
 class EventBCallee final
 {
 public:
-    void callback(EventB* /* bPointer */) { notifiedEventB_ = true; };
+    void callback(EventB* /* bPointer */) { notifiedEventB_ = true; }
 
     bool notifiedEventB() { return notifiedEventB_; }
 
@@ -83,9 +83,9 @@ private:
 class EventAandBCallee final
 {
 public:
-    void notify(EventB* /* bPointer */) { notifiedEventB_ = true; };
+    void notify(EventB* /* bPointer */) { notifiedEventB_ = true; }
 
-    void callback(EventA /* a */) { notifiedEventA_ = true; };
+    void callback(EventA /* a */) { notifiedEventA_ = true; }
 
     bool notifiedEventB() { return notifiedEventB_; }
     bool notifiedEventA() { return notifiedEventA_; }
index 8b1725c5c7a1cc7dc0334b3a18634f971ea757df..dba7019add389c842503a52a21e436d18e9eecc6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -55,6 +55,7 @@
 #include "config.h"
 
 #include <future>
+
 #include <gtest/gtest.h>
 
 
diff --git a/src/gromacs/utility/tests/range.cpp b/src/gromacs/utility/tests/range.cpp
new file mode 100644 (file)
index 0000000..287cf7d
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for the Range class.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_utility
+ */
+#include "gmxpre.h"
+
+#include "gromacs/utility/range.h"
+
+#include <limits>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "testutils/testasserts.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+TEST(Range, EmptyRangeWorks)
+{
+    Range<int> range;
+
+    EXPECT_EQ(range.empty(), true);
+    EXPECT_EQ(range.size(), 0);
+}
+
+TEST(Range, NonEmptyRangeWorks)
+{
+    const Range<char> range(3, 5);
+
+    EXPECT_EQ(range.empty(), false);
+    EXPECT_EQ(range.size(), 2);
+}
+
+TEST(Range, BeginEnd)
+{
+    const Range<long> range(-2, 9);
+
+    EXPECT_EQ(range.begin(), -2);
+    EXPECT_EQ(*range.begin(), -2);
+    EXPECT_EQ(range.end(), 9);
+    EXPECT_EQ(*range.end(), 9);
+}
+
+TEST(Range, IsInRangeWorks)
+{
+    const Range<size_t> range(5, 8);
+
+    EXPECT_EQ(range.isInRange(4), false);
+    EXPECT_EQ(range.isInRange(5), true);
+    EXPECT_EQ(range.isInRange(6), true);
+    EXPECT_EQ(range.isInRange(7), true);
+    EXPECT_EQ(range.isInRange(8), false);
+}
+
+TEST(Range, IteratorWorks)
+{
+    const Range<index> range(-1, 3);
+
+    int minValue = std::numeric_limits<int>::max();
+    int maxValue = std::numeric_limits<int>::min();
+    for (int i : range)
+    {
+        minValue = std::min(minValue, i);
+        maxValue = std::max(maxValue, i);
+    }
+    EXPECT_EQ(minValue, -1);
+    EXPECT_EQ(maxValue, 2);
+}
+
+} // namespace
+
+} // namespace gmx
diff --git a/src/gromacs/utility/tests/strconvert.cpp b/src/gromacs/utility/tests/strconvert.cpp
new file mode 100644 (file)
index 0000000..73730fc
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Tests routines in strconvert.h.
+ *
+ * \author Christian Blau <blau@kth.se>
+ *
+ * \ingroup module_utility
+ */
+
+#include "gmxpre.h"
+
+#include "gromacs/utility/strconvert.h"
+
+#include <gtest/gtest.h>
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+TEST(StringConvert, NoResultFromEptyString)
+{
+    const auto parsedArray = parsedArrayFromInputString<float, 3>("");
+    EXPECT_FALSE(parsedArray);
+}
+
+TEST(StringConvert, ThreeFloatsSuccessfully)
+{
+    const auto parsedArray = parsedArrayFromInputString<float, 3>("1.2 .5 -6e5");
+    EXPECT_FLOAT_EQ((*parsedArray)[0], 1.2);
+    EXPECT_FLOAT_EQ((*parsedArray)[1], .5);
+    EXPECT_FLOAT_EQ((*parsedArray)[2], -6e5);
+}
+
+TEST(StringConvert, OneIntSucessfully)
+{
+    const auto parsedArray = parsedArrayFromInputString<int, 1>(" 1 \t  ");
+    EXPECT_FLOAT_EQ((*parsedArray)[0], 1);
+}
+
+TEST(StringConvert, FloatAsStringToIntArrayThrows)
+{
+    const auto& toTest = []() { return parsedArrayFromInputString<int, 1>(" 1.2 "); };
+    EXPECT_THROW(toTest(), InvalidInputError);
+}
+
+TEST(StringConvert, ThrowsWhenWrongSize)
+{
+    // use the lambda due to aviod Macro substitution error with template function
+    const auto& toTest = []() { return parsedArrayFromInputString<float, 2>("1.2\t\n  .5 -6e5"); };
+    EXPECT_THROW(toTest(), InvalidInputError);
+}
+
+TEST(StringConvert, StringIdentityTransformWithArrayThrows)
+{
+    // use the lambda due to aviod Macro substitution error with template function
+    const auto& toTest = []() {
+        return stringIdentityTransformWithArrayCheck<float, 3>(
+                "-10 5 4 1", "Here, I explain where the error occured: ");
+    };
+    EXPECT_THROW(toTest(), InvalidInputError);
+}
+
+TEST(StringConvert, StringIdentityTransformWithArrayOkay)
+{
+    // use the lambda due to aviod Macro substitution error with template function
+    const std::string input("1.2\t\n  .5 -6e5");
+    const std::string output = stringIdentityTransformWithArrayCheck<float, 3>(
+            input, "Here, I explain where the error occured: ");
+    EXPECT_EQ(input, output);
+}
+
+
+} // namespace
+} // namespace test
+} // namespace gmx
index 65fbe513116cb8d72b977572167b24542a6f47ae..0b2acccc24fbf04f965070cda6704c4c955f8816 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 52fc2718e2d830754edf86acc89743f821956e83..1198fa9dde26af610e9b14d43f7390e569daa86f 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 1949ceea69703552c8a4200d93dc55f19fa44ee8..fcf6cd0defc6110c751f0eb743c15e917e439411 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 8d632261b91e9332033edabc8b7eca11c6fe095a..6a336db5d324494316472f78d2916af6c65112d1 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+# Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+# Copyright (c) 2015,2016,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -38,6 +39,8 @@ add_library(mdrun_objlib OBJECT ${MDRUN_SOURCES})
 gmx_target_compile_options(mdrun_objlib)
 target_compile_definitions(mdrun_objlib PRIVATE HAVE_CONFIG_H)
 target_include_directories(mdrun_objlib SYSTEM BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include)
+# Should be possible to remove this when resolving #3290
+target_include_directories(mdrun_objlib SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/src/external)
 
 if(GMX_FAHCORE)
     # The lack of a real source file here alongside the object library
@@ -70,10 +73,14 @@ else()
     add_library(view_objlib OBJECT ${VIEW_SOURCES})
     gmx_target_compile_options(view_objlib)
     target_compile_definitions(view_objlib PRIVATE HAVE_CONFIG_H)
+    target_include_directories(view_objlib SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/src/external)
+    add_library(gmx_objlib OBJECT ${GMX_MAIN_SOURCES})
+    target_include_directories(gmx_objlib SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/src/external)
     add_executable(gmx
-        ${GMX_MAIN_SOURCES}
+        $<TARGET_OBJECTS:gmx_objlib>
         $<TARGET_OBJECTS:mdrun_objlib>
         $<TARGET_OBJECTS:view_objlib>)
+    add_executable(Gromacs::gmx ALIAS gmx)
     gmx_target_compile_options(gmx)
     target_compile_definitions(gmx PRIVATE HAVE_CONFIG_H)
     target_link_libraries(gmx libgromacs
@@ -85,7 +92,14 @@ else()
     set(BINARY_NAME "gmx${GMX_BINARY_SUFFIX}")
     set_target_properties(gmx PROPERTIES
         OUTPUT_NAME "${BINARY_NAME}")
+    # Add the executable to the INSTALL target and to the libgromacs EXPORT configuration.
+    # The GROMACS installation then provides an importable (e.g. find_package(GROMACS))
+    # Gromacs::gmx target, which may be used as the executable COMMAND argument for functions like
+    # add_custom_command() or add_test(). The full path to the installed binary may retrieved
+    # from the LOCATION property or with a $<TARGET_FILE:Gromacs:gmx> generator expression
+    # (where generator expressions are allowed).
     install(TARGETS gmx
+            EXPORT libgromacs
             RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 
     ########################
index d46ffa034df9572dc19b58cb939023474674346d..e0be0e23239572817afde0240abeb56ee86f1591 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014,2019, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e86c5e0534ef487a8ed29bd2a16434e22465cedf..f591e2c61b8a9f70760d88c430b9e78893627645 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -102,7 +103,7 @@ public:
     }
 
 private:
-    void printMessage() const
+    static void printMessage()
     {
         std::fprintf(stderr,
                      "This tool is no longer present in GROMACS. Please see\n"
@@ -175,7 +176,9 @@ void registerLegacyModules(gmx::CommandLineModuleManager* manager)
     gmx::ICommandLineOptionsModule::registerModuleFactory(
             manager, gmx::DumpInfo::name, gmx::DumpInfo::shortDescription, &gmx::DumpInfo::create);
     registerModule(manager, &gmx_grompp, "grompp", "Make a run input file");
-    registerModule(manager, &gmx_convert_tpr, "convert-tpr", "Make a modifed run-input file");
+    gmx::ICommandLineOptionsModule::registerModuleFactory(manager, gmx::ConvertTprInfo::name,
+                                                          gmx::ConvertTprInfo::shortDescription,
+                                                          &gmx::ConvertTprInfo::create);
     registerObsoleteTool(manager, "tpbconv");
     registerModule(manager, &gmx_x2top, "x2top", "Generate a primitive topology from coordinates");
 
index 5a4a46d7f43d70eecefcc8c488ad4d821d9ae651..f85fe269318ba8729cf720ad880d7eae035ff187 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2011-2019, by the GROMACS development team, led by
+ * Copyright (c) 2011-2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -259,6 +259,7 @@ int gmx_mdrun(int argc, char* argv[])
     builder.addHardwareOptions(options.hw_opt);
     // \todo File names are parameters that should be managed modularly through further factoring.
     builder.addFilenames(options.filenames);
+    builder.addInput(makeSimulationInput(options));
     // Note: The gmx_output_env_t life time is not managed after the call to parse_common_args.
     // \todo Implement lifetime management for gmx_output_env_t.
     // \todo Output environment should be configured outside of Mdrunner and provided as a resource.
index 3f4308cfe078d90c1455fc96c91af24b06e4f029..9d4cd52eeb04860b614f4c1b2dab611706c90ca3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -157,9 +157,15 @@ void NonbondedBenchmark::initOptions(IOptionsContainer* options, ICommandLineOpt
 
     settings->setHelpText(desc);
 
-    const char* const cNbnxmSimdStrings[]   = { "auto", "no", "4xm", "2xmm" };
-    const char* const cCombRuleStrings[]    = { "geometric", "lb", "none" };
-    const char* const cCoulombTypeStrings[] = { "ewald", "reaction-field" };
+    static const EnumerationArray<Nbnxm::BenchMarkKernels, const char*> c_nbnxmSimdStrings = {
+        { "auto", "no", "4xm", "2xmm" }
+    };
+    static const EnumerationArray<Nbnxm::BenchMarkCombRule, const char*> c_combRuleStrings = {
+        { "geometric", "lb", "none" }
+    };
+    static const EnumerationArray<Nbnxm::BenchMarkCoulomb, const char*> c_coulombTypeStrings = {
+        { "ewald", "reaction-field" }
+    };
 
     options->addOption(
             IntegerOption("size").store(&sizeFactor_).description("The system size is 3000 atoms times this value"));
@@ -167,12 +173,12 @@ void NonbondedBenchmark::initOptions(IOptionsContainer* options, ICommandLineOpt
             IntegerOption("nt").store(&benchmarkOptions_.numThreads).description("The number of OpenMP threads to use"));
     options->addOption(EnumOption<Nbnxm::BenchMarkKernels>("simd")
                                .store(&benchmarkOptions_.nbnxmSimd)
-                               .enumValue(cNbnxmSimdStrings)
+                               .enumValue(c_nbnxmSimdStrings)
                                .description("SIMD type, auto runs all supported SIMD setups or no "
                                             "SIMD when SIMD is not supported"));
     options->addOption(EnumOption<Nbnxm::BenchMarkCoulomb>("coulomb")
                                .store(&benchmarkOptions_.coulombType)
-                               .enumValue(cCoulombTypeStrings)
+                               .enumValue(c_coulombTypeStrings)
                                .description("The functional form for the Coulomb interactions"));
     options->addOption(
             BooleanOption("table")
@@ -180,7 +186,7 @@ void NonbondedBenchmark::initOptions(IOptionsContainer* options, ICommandLineOpt
                     .description("Use lookup table for Ewald correction instead of analytical"));
     options->addOption(EnumOption<Nbnxm::BenchMarkCombRule>("combrule")
                                .store(&benchmarkOptions_.ljCombinationRule)
-                               .enumValue(cCombRuleStrings)
+                               .enumValue(c_combRuleStrings)
                                .description("The LJ combination rule"));
     options->addOption(BooleanOption("halflj")
                                .store(&benchmarkOptions_.useHalfLJOptimization)
index 9ba03c04fc8d9d1dbc1c54c9df9ac052c2acbd0e..b051068c0670184835bd4dda10d283a2b7e00d10 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+# Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -38,11 +39,14 @@ gmx_add_unit_test_library(mdrun_test_infrastructure
     energyreader.cpp
     energycomparison.cpp
     moduletest.cpp
-    mdmodulenotification.cpp
+    simulatorcomparison.cpp
     terminationhelper.cpp
     trajectorycomparison.cpp
     trajectoryreader.cpp
+    # pseudo-library for code for mdrun
+    $<TARGET_OBJECTS:mdrun_objlib>
     )
+target_include_directories(mdrun_test_infrastructure SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/src/external)
 
 # To avoid running into test timeouts, some end-to-end tests of mdrun
 # functionality are split off. This can be rearranged in future as we
@@ -50,15 +54,14 @@ gmx_add_unit_test_library(mdrun_test_infrastructure
 set(testname "MdrunOutputTests")
 set(exename "mdrun-output-test")
 
-gmx_add_gtest_executable(
-    ${exename}
-    # files with code for tests
-    compressed_x_output.cpp
-    helpwriting.cpp
-    outputfiles.cpp
-    trajectory_writing.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
+gmx_add_gtest_executable(${exename}
+    CPP_SOURCE_FILES
+        compressed_x_output.cpp
+        helpwriting.cpp
+        outputfiles.cpp
+        trajectory_writing.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
     )
 target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
 gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
@@ -66,13 +69,13 @@ gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST
 set(testname "MdrunModulesTests")
 set(exename "mdrun-modules-test")
 
-gmx_add_gtest_executable(
-    ${exename}
-    densityfittingmodule.cpp
-    interactiveMD.cpp
-    mimic.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
+gmx_add_gtest_executable(${exename}
+    CPP_SOURCE_FILES
+        densityfittingmodule.cpp
+        interactiveMD.cpp
+        mimic.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
     )
 target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
 gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
@@ -80,96 +83,128 @@ gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST
 set(testname "MdrunIOTests")
 set(exename "mdrun-io-test")
 
-gmx_add_gtest_executable(
-    ${exename}
-    exactcontinuation.cpp
-    grompp.cpp
-    initialconstraints.cpp
-    termination.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
+gmx_add_gtest_executable(${exename}
+    CPP_SOURCE_FILES
+        exactcontinuation.cpp
+        grompp.cpp
+        initialconstraints.cpp
+        termination.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
     )
 target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
 gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
 
+# To avoid running into test timeouts, some end-to-end tests of mdrun
+# functionality are split off. This can be rearranged in future as we
+# see fit.
 set(testname "MdrunTests")
 set(exename "mdrun-test")
 
-gmx_add_gtest_executable(
-    ${exename}
-    dispersion_correction.cpp
-    orires.cpp
-    pmetest.cpp
-    simulator.cpp
-    swapcoords.cpp
-    tabulated_bonded_interactions.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
+gmx_add_gtest_executable(${exename}
+    CPP_SOURCE_FILES
+        ewaldsurfaceterm.cpp
+        multiple_time_stepping.cpp
+        orires.cpp
+        simulator.cpp
+        swapcoords.cpp
+        tabulated_bonded_interactions.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
+    )
+target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
+gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
+
+
+set(testname "MdrunPmeTests")
+set(exename "mdrun-pme-test")
+
+gmx_add_gtest_executable(${exename}
+    CPP_SOURCE_FILES
+        pmetest.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
     )
 target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
-gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST)
+gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
+
 
 set(testname "MdrunNonIntegratorTests")
 set(exename "mdrun-non-integrator-test")
 
-gmx_add_gtest_executable(
-    ${exename}
-    # files with code for tests
-    minimize.cpp
-    nonbonded_bench.cpp
-    normalmodes.cpp
-    rerun.cpp
-    simple_mdrun.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
-    )
+gmx_add_gtest_executable(${exename}
+    CPP_SOURCE_FILES
+        # files with code for tests
+        minimize.cpp
+        nonbonded_bench.cpp
+        normalmodes.cpp
+        rerun.cpp
+        simple_mdrun.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
+        )
 target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
-gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST)
+gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
 
 # TPI does not support OpenMP, so we need a separate test binary
 set(testname "MdrunTpiTests")
 set(exename "mdrun-tpi-test")
 
-gmx_add_gtest_executable(
-    ${exename}
-    # files with code for tests
-    tpitest.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
-    )
+gmx_add_gtest_executable(${exename}
+    CPP_SOURCE_FILES
+        # files with code for tests
+        tpitest.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
+        )
 target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
-gmx_register_gtest_test(${testname} ${exename} INTEGRATION_TEST)
+gmx_register_gtest_test(${testname} ${exename} INTEGRATION_TEST IGNORE_LEAKS)
 
 # Tests that only make sense to run with multiple ranks and/or real
 # MPI are implemented here.
 set(testname "MdrunMpiTests")
 set(exename "mdrun-mpi-test")
 
-gmx_add_gtest_executable(
-    ${exename} MPI
-    # files with code for tests
-    domain_decomposition.cpp
-    minimize.cpp
-    mimic.cpp
-    multisim.cpp
-    multisimtest.cpp
-    pmetest.cpp
-    replicaexchange.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
-    )
+gmx_add_gtest_executable(${exename} MPI
+    CPP_SOURCE_FILES
+        # files with code for tests
+        domain_decomposition.cpp
+        minimize.cpp
+        mimic.cpp
+        multisim.cpp
+        multisimtest.cpp
+        replicaexchange.cpp
+        pmetest.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
+        )
 target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
-gmx_register_gtest_test(${testname} ${exename} MPI_RANKS 2 OPENMP_THREADS 2 INTEGRATION_TEST)
+gmx_register_gtest_test(${testname} ${exename} MPI_RANKS 2 OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
+
+# Tests that only make sense to run with multiple ranks and/or real
+# MPI are implemented here. Special case for slow PME tests
+set(testname "MdrunMpiPmeTests")
+set(exename "mdrun-mpi-pme-test")
+
+gmx_add_gtest_executable(${exename} MPI
+    CPP_SOURCE_FILES
+        # files with code for tests
+        pmetest.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
+        )
+target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
+gmx_register_gtest_test(${testname} ${exename} MPI_RANKS 2 OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
 
 # Slow-running tests that target testing multiple-rank coordination behaviors
 set(exename "mdrun-mpi-coordination-test")
-gmx_add_gtest_executable(
-    ${exename} MPI
-    # files with code for tests
-    periodicactions.cpp
-    # pseudo-library for code for mdrun
-    $<TARGET_OBJECTS:mdrun_objlib>
-    )
+gmx_add_gtest_executable(${exename} MPI
+    CPP_SOURCE_FILES
+        # files with code for tests
+        periodicactions.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
+        )
 target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
 
 # These tests are extremely slow without optimization or OpenMP, so only run them for
@@ -177,7 +212,22 @@ target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
 # with OpenMP enabled.
 if (CMAKE_BUILD_TYPE MATCHES "Rel" AND GMX_OPENMP)
     set(testname "MdrunMpiCoordinationTestsOneRank")
-    gmx_register_gtest_test(${testname} ${exename} MPI_RANKS 1 SLOW_TEST)
+    gmx_register_gtest_test(${testname} ${exename} MPI_RANKS 1 SLOW_TEST IGNORE_LEAKS)
     set(testname "MdrunMpiCoordinationTestsTwoRanks")
-    gmx_register_gtest_test(${testname} ${exename} MPI_RANKS 2 SLOW_TEST)
+    gmx_register_gtest_test(${testname} ${exename} MPI_RANKS 2 SLOW_TEST IGNORE_LEAKS)
 endif()
+
+# Keeping the FEP tests separate for now to be able to judge runtime more easily
+# Can be included in mdrun tests later
+set(testname "MdrunFEPTests")
+set(exename "mdrunfep-test")
+
+gmx_add_gtest_executable(${exename}
+        CPP_SOURCE_FILES
+        # files with code for tests
+        freeenergy.cpp
+        # pseudo-library for code for mdrun
+        $<TARGET_OBJECTS:mdrun_objlib>
+)
+target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
+gmx_register_gtest_test(${testname} ${exename} OPENMP_THREADS 2 INTEGRATION_TEST IGNORE_LEAKS)
similarity index 62%
rename from src/gromacs/compat/optional.h
rename to src/programs/mdrun/tests/comparison_helpers.h
index b85e0190c58dbe1861baf45b614cf492a3b31840..4f91f91472d4366b0bc76f6a30a4f1e17f471d0e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-/*! \libinternal \file
- * \brief Provides C++14-compatible implementation of std::optional.
- *
- * This imports the implementation found in src/external from
- * https://github.com/martinmoene/optional-lite.git.
- *
- * There is no Doxygen for this code, but it is intended to conform to
- * that of std::optional, so look in the usual C++17 documentation for
- * std::optional for that.
- *
- * \todo Remove when requiring C++17, which has a standardized version
- * of std::optional.
+
+/*! \internal \file
+ * \brief Declares types and functions common to comparing either
+ * energies or trajectories produced by mdrun.
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
- * \ingroup module_compat
- * \inlibraryapi
+ * \ingroup module_mdrun_integration_tests
  */
-#ifndef GMX_COMPAT_OPTIONAL_H
-#define GMX_COMPAT_OPTIONAL_H
+#ifndef GMX_PROGRAMS_MDRUN_TESTS_COMPARISON_HELPERS_H
+#define GMX_PROGRAMS_MDRUN_TESTS_COMPARISON_HELPERS_H
 
-#include <nonstd/optional.hpp>
+#include <limits>
 
 namespace gmx
 {
-namespace compat
+
+namespace test
 {
 
-using nonstd::bad_optional_access;
-using nonstd::in_place;
-using nonstd::make_optional;
-using nonstd::nullopt;
-using nonstd::optional;
+/*! \internal
+ * \brief Named struct indicating the max number of frames to be compared */
+struct MaxNumFrames
+{
+    //! Explicit constructor
+    explicit MaxNumFrames(unsigned int maxFrame) : maxFrame_(maxFrame) {}
+
+    //! Implicit conversion to int - struct can be used like underlying type
+    operator unsigned int() const { return maxFrame_; }
+
+    //! Return a MaxNumFrames that will try to compare all frames
+    [[nodiscard]] static MaxNumFrames compareAllFrames()
+    {
+        return MaxNumFrames(std::numeric_limits<decltype(maxFrame_)>::max());
+    }
+
+private:
+    //! Internal value
+    const unsigned int maxFrame_;
+};
 
-} // namespace compat
+} // namespace test
 } // namespace gmx
 
 #endif
index ab7cc8798f04c283d435349198830bcaeef0314f..15501b455a0019e52362d6fd883975cec0c4683f 100644 (file)
@@ -131,6 +131,25 @@ public:
     const std::string mdpSkipDensityfittingEveryOtherStep_ = formatString(
             "nstenergy = 2\n"
             "density-guided-simulation-nst = 2\n");
+    //! A properly set shift vector
+    const std::string mdpTranslationSet_ =
+            formatString("density-guided-simulation-shift-vector = 0.1 -0.2 0.3\n");
+    //! A shift vector that is lacking an entry
+    const std::string mdpTranslationSetWrongValues_ =
+            formatString("density-guided-simulation-shift-vector = 0.1 -0.2\n");
+    //! A 45 degree rotation around the y axis expressed as matrix transformation
+    const std::string mdpTransformationMatrix1degAroundY_ = formatString(
+            "density-guided-simulation-transformation-matrix = 0.9998477 0.0000000 0.0174524 "
+            "0.0000000 1.0000000 0.0000000 -0.0174524 0.0000000 0.9998477 \n");
+    //! The identity matrix as transformation matrix
+    const std::string mdpTransformationMatrixIdentity_ = formatString(
+            "density-guided-simulation-transformation-matrix = 1 0 0 "
+            "0 1 0 0 0 1 \n");
+    //! A transformation matrix string where only eight values are given
+    const std::string mdpTransformationMatrixWrongValues_ = formatString(
+            "density-guided-simulation-transformation-matrix = 0.7071068 0.0000000 0.7071068 "
+            "0.0000000 0.0000000 -0.7071068 0.0000000 0.7071068 \n");
+
     //! The command line to call mdrun
     CommandLine commandLineForMdrun_;
 };
@@ -151,6 +170,79 @@ TEST_F(DensityFittingTest, EnergyMinimizationEnergyCorrectInnerProduct)
     checkMdrun(expectedEnergyTermMagnitude);
 }
 
+/* Fit a subset of three of twelve argon atoms into a reference density
+ * whose origin is offset from the simulation box origin.
+ *
+ * All density fitting mdp parameters are set to defaults
+ */
+TEST_F(DensityFittingTest, EnergyMinimizationEnergyCorrectInnerProductTranslation)
+{
+    runner_.useStringAsMdpFile(mdpEminDensfitYesUnsetValues + mdpTranslationSet_);
+
+    ASSERT_EQ(0, runner_.callGrompp());
+    ASSERT_EQ(0, runner_.callMdrun(commandLineForMdrun_));
+
+    const real expectedEnergyTermMagnitude = -8991;
+    checkMdrun(expectedEnergyTermMagnitude);
+}
+
+/* Fit a subset of three of twelve argon atoms into a reference density
+ * whose origin is offset from the simulation box origin.
+ *
+ * All density fitting mdp parameters are set to defaults
+ */
+TEST_F(DensityFittingTest, EnergyMinimizationEnergyTranslationParametersOff)
+{
+    runner_.useStringAsMdpFile(mdpEminDensfitYesUnsetValues + mdpTranslationSetWrongValues_);
+
+    GMX_EXPECT_DEATH_IF_SUPPORTED(runner_.callGrompp(), ".*Reading three real values.*");
+}
+
+/* Fit a subset of three of twelve argon atoms into a reference density
+ * that are rotated around the simulation box origin by a matrix multiplication.
+ *
+ * All density fitting mdp parameters are set to defaults
+ */
+TEST_F(DensityFittingTest, EnergyMinimizationEnergyCorrectInnerProductTranslationAndTransformationMatrix)
+{
+    runner_.useStringAsMdpFile(mdpEminDensfitYesUnsetValues + mdpTranslationSet_
+                               + mdpTransformationMatrix1degAroundY_);
+
+    ASSERT_EQ(0, runner_.callGrompp());
+    ASSERT_EQ(0, runner_.callMdrun(commandLineForMdrun_));
+
+    const real expectedEnergyTermMagnitude = -8991;
+    checkMdrun(expectedEnergyTermMagnitude);
+}
+
+/* Fit a subset of three of twelve argon atoms into a reference density
+ * whose origin is offset from the simulation box origin.
+ *
+ * All density fitting mdp parameters are set to defaults
+ */
+TEST_F(DensityFittingTest, EnergyMinimizationEnergyMatrixTransfromationOff)
+{
+    runner_.useStringAsMdpFile(mdpEminDensfitYesUnsetValues + mdpTransformationMatrixWrongValues_);
+
+    GMX_EXPECT_DEATH_IF_SUPPORTED(runner_.callGrompp(), ".*Reading nine real values.*");
+}
+
+/* Fit a subset of three of twelve argon atoms into a reference density
+ * where the given matrix transformation is the identity transformation.
+ *
+ * All density fitting mdp parameters are set to defaults
+ */
+TEST_F(DensityFittingTest, EnergyMinimizationEnergyCorrectInnerProductIdentityMatrix)
+{
+    runner_.useStringAsMdpFile(mdpEminDensfitYesUnsetValues + mdpTransformationMatrixIdentity_);
+
+    ASSERT_EQ(0, runner_.callGrompp());
+    ASSERT_EQ(0, runner_.callMdrun(commandLineForMdrun_));
+
+    const real expectedEnergyTermMagnitude = -8991;
+    checkMdrun(expectedEnergyTermMagnitude);
+}
+
 /* Like above, but with as many parameters reversed as possible
  */
 TEST_F(DensityFittingTest, EnergyMinimizationEnergyCorrectForRelativeEntropy)
index fbdd46faf795d9cc65b802a533082ebd2c7cca66..cea5e9d021dd1ace8f69b2d42c6a7976c9acf045 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -64,18 +64,23 @@ namespace gmx
 namespace test
 {
 
-const EnergyTermsToCompare EnergyComparison::s_defaultEnergyTermsToCompare = {
-    { interaction_function[F_EPOT].longname, relativeToleranceAsUlp(10.0, 50) },
-    { interaction_function[F_EKIN].longname, relativeToleranceAsUlp(10.0, 50) },
-    // The pressure is very strongly affected by summation errors,
-    // so we need a large tolerance.
-    // The value of 15000 is calibrated for running a small water box for 16 steps.
-    // For a single frame for a water box a value of 150 could work.
-    { interaction_function[F_PRES].longname, relativeToleranceAsUlp(10.0, 15000) },
+EnergyTermsToCompare EnergyComparison::defaultEnergyTermsToCompare()
+{
+    return {
+        { interaction_function[F_EPOT].longname, relativeToleranceAsUlp(10.0, 50) },
+        { interaction_function[F_EKIN].longname, relativeToleranceAsUlp(10.0, 50) },
+        // The pressure is very strongly affected by summation errors,
+        // so we need a large tolerance.
+        // The value of 15000 is calibrated for running a small water box for 16 steps.
+        // For a single frame for a water box a value of 150 could work.
+        { interaction_function[F_PRES].longname, relativeToleranceAsUlp(10.0, 15000) },
+    };
 };
 
-EnergyComparison::EnergyComparison(const EnergyTermsToCompare& energyTermsToCompare) :
-    energyTermsToCompare_(energyTermsToCompare)
+EnergyComparison::EnergyComparison(const EnergyTermsToCompare& energyTermsToCompare,
+                                   MaxNumFrames                maxNumFrames) :
+    energyTermsToCompare_(energyTermsToCompare),
+    maxNumFrames_(maxNumFrames)
 {
 }
 
@@ -92,6 +97,12 @@ std::vector<std::string> EnergyComparison::getEnergyNames() const
 
 void EnergyComparison::operator()(const EnergyFrame& reference, const EnergyFrame& test) const
 {
+    if (numComparedFrames_ >= maxNumFrames_)
+    {
+        // Nothing should be compared
+        return;
+    }
+
     SCOPED_TRACE("Comparing energy reference frame " + reference.frameName() + " and test frame "
                  + test.frameName());
     for (auto referenceIt = reference.begin(); referenceIt != reference.end(); ++referenceIt)
@@ -111,17 +122,19 @@ void EnergyComparison::operator()(const EnergyFrame& reference, const EnergyFram
             ADD_FAILURE() << "Could not find energy component from reference frame in test frame";
         }
     }
+    numComparedFrames_++;
 }
 
 void checkEnergiesAgainstReferenceData(const std::string&          energyFilename,
                                        const EnergyTermsToCompare& energyTermsToCompare,
-                                       TestReferenceChecker*       checker)
+                                       TestReferenceChecker*       checker,
+                                       MaxNumFrames                maxNumEnergyFrames)
 {
     const bool thisRankChecks = (gmx_node_rank() == 0);
 
     if (thisRankChecks)
     {
-        EnergyComparison energyComparison(energyTermsToCompare);
+        EnergyComparison energyComparison(energyTermsToCompare, maxNumEnergyFrames);
         auto energyReader = openEnergyFileToReadTerms(energyFilename, energyComparison.getEnergyNames());
 
         std::unordered_map<std::string, TestReferenceChecker> checkers;
@@ -138,22 +151,29 @@ void checkEnergiesAgainstReferenceData(const std::string&          energyFilenam
         // frames with the same step number. But we need a unique
         // identifier so we match the intended reference data, so we
         // keep track of the number of the frame read from the file.
-        int frameNumber = 0;
-        while (energyReader->readNextFrame())
+        unsigned int frameNumber = 0;
+        while (frameNumber < maxNumEnergyFrames && energyReader->readNextFrame())
         {
-            const EnergyFrame& frame     = energyReader->frame();
-            const std::string  frameName = frame.frameName() + " in frame " + toString(frameNumber);
+            const EnergyFrame& frame = energyReader->frame();
+            const std::string  frameName =
+                    frame.frameName() + " in frame " + toString(static_cast<int64_t>(frameNumber));
 
+            SCOPED_TRACE("Comparing frame " + frameName);
             for (const auto& energyTermToCompare : energyTermsToCompare)
             {
                 const std::string& energyName  = energyTermToCompare.first;
                 const real         energyValue = frame.at(energyName);
 
-                SCOPED_TRACE("Comparing " + energyName + " in " + frameName);
+                SCOPED_TRACE("Comparing energy " + energyName);
                 checkers[energyName].checkReal(energyValue, frameName.c_str());
             }
             ++frameNumber;
         }
+        if (frameNumber == maxNumEnergyFrames && energyReader->readNextFrame())
+        {
+            // There would have been at least one more frame!
+            checker->disableUnusedEntriesCheck();
+        }
     }
     else
     {
@@ -161,5 +181,13 @@ void checkEnergiesAgainstReferenceData(const std::string&          energyFilenam
     }
 }
 
+void checkEnergiesAgainstReferenceData(const std::string&          energyFilename,
+                                       const EnergyTermsToCompare& energyTermsToCompare,
+                                       TestReferenceChecker*       checker)
+{
+    checkEnergiesAgainstReferenceData(energyFilename, energyTermsToCompare, checker,
+                                      MaxNumFrames::compareAllFrames());
+}
+
 } // namespace test
 } // namespace gmx
index 980f5223712e5bd694e726bb405e11858baa7ff7..c33f66331b260d9c52af6506da4f9aefe754598f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,8 @@
 
 #include "testutils/testasserts.h"
 
+#include "comparison_helpers.h"
+
 namespace gmx
 {
 
@@ -67,9 +69,9 @@ class EnergyComparison
 {
 public:
     //! Defaults for energy comparisons
-    static const EnergyTermsToCompare s_defaultEnergyTermsToCompare;
+    static EnergyTermsToCompare defaultEnergyTermsToCompare();
     //! Constructor
-    EnergyComparison(const EnergyTermsToCompare& energyTermsToCompare);
+    EnergyComparison(const EnergyTermsToCompare& energyTermsToCompare, MaxNumFrames maxNumFrames);
     /*! \brief Return the names of energies that will be compared
      *
      * This function can be used to provide an input for
@@ -89,8 +91,18 @@ public:
      * key. */
     void operator()(const EnergyFrame& reference, const EnergyFrame& test) const;
 
+private:
     //! Energy terms to match with given tolerances.
     EnergyTermsToCompare energyTermsToCompare_;
+    //! How many frames should be compared.
+    MaxNumFrames maxNumFrames_ = MaxNumFrames::compareAllFrames();
+    /*! \brief The number of frames that have been compared until now
+     *
+     * This field is mutable because the need to update the flag
+     * when checking frames is merely an implementation detail,
+     * rather than a proper change of internal state triggered
+     * by the caller. */
+    mutable unsigned int numComparedFrames_ = 0;
 };
 
 /*! \brief Check a subset of the energies found in an energy file
@@ -102,10 +114,24 @@ public:
  * \param[in]  energyFilename        The name of an energy file.
  * \param[in]  energyTermsToCompare  Set of energies to match at given tolerances.
  * \param[in]  checker               Root checker for reference data.
+ * \param[in]  maxNumEnergyFrames    The maximum number of frames to check
  *
  * \todo This is quite similar to the functionality used in PmeTest,
  * and we should consider reducing the duplication.
  */
+void checkEnergiesAgainstReferenceData(const std::string&          energyFilename,
+                                       const EnergyTermsToCompare& energyTermsToCompare,
+                                       TestReferenceChecker*       checker,
+                                       MaxNumFrames                maxNumEnergyFrames);
+
+/*!
+ * \brief Check a subset of the energies found in an energy file
+ * against reference data.
+ *
+ * Convenience overload using all frames
+ *
+ * \see checkEnergiesAgainstReferenceData(const std::string&, const EnergyTermsToCompare&, TestReferenceChecker*, MaxNumFrames)
+ */
 void checkEnergiesAgainstReferenceData(const std::string&          energyFilename,
                                        const EnergyTermsToCompare& energyTermsToCompare,
                                        TestReferenceChecker*       checker);
index fa8daa9e6f6fb4d15ca20af91b5e758dc7787cd6..0012c02fb983d1df11c70f3162a927e952e90e87 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -60,6 +60,7 @@
 #include <vector>
 
 #include "gromacs/fileio/enxio.h"
+#include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/unique_cptr.h"
 
 #include "testutils/testasserts.h"
diff --git a/src/programs/mdrun/tests/ewaldsurfaceterm.cpp b/src/programs/mdrun/tests/ewaldsurfaceterm.cpp
new file mode 100644 (file)
index 0000000..367d0ab
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ * \brief
+ * Test for Ewald 3DC and epsilon-surface terms.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdrun_integration_tests
+ */
+#include "gmxpre.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/topology/idef.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/trajectory/trajectoryframe.h"
+#include "gromacs/utility/basenetwork.h"
+#include "gromacs/utility/filestream.h"
+#include "gromacs/utility/strconvert.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "testutils/mpitest.h"
+#include "testutils/refdata.h"
+#include "testutils/simulationdatabase.h"
+#include "testutils/testasserts.h"
+
+#include "energycomparison.h"
+#include "moduletest.h"
+#include "trajectoryreader.h"
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+//! Helper type
+using MdpField = MdpFieldValues::value_type;
+
+/*! \brief Test fixture base for simple mdrun systems
+ *
+ * This test ensures mdrun can run a simulation, reaching reproducible
+ * energies and forces.
+ * The starting coordinates are set up such that both molecules are
+ * broken over PBC and the PBC treatment is tested.
+ */
+class EwaldSurfaceTermTest : public MdrunTestFixture, public ::testing::WithParamInterface<std::string>
+{
+};
+
+TEST_P(EwaldSurfaceTermTest, WithinTolerances)
+{
+    auto simulationName = GetParam();
+    SCOPED_TRACE(formatString("Comparing simple mdrun for '%s'", simulationName.c_str()));
+
+    int numRanksAvailable = getNumberOfTestMpiRanks();
+    /* For epsilon-surface we need whole molecules.
+     * Without constraints we can make molecules whole on a sinlge rank.
+     * With constraints molecules are whole with update groups with DD.
+     *
+     * TODO: Remove the rank=1 check when DD also runs single rank.
+     */
+    if ((simulationName == "epsilon-surface" && numRanksAvailable > 1)
+        || (simulationName == "epsilon-surface-constraint" && numRanksAvailable == 1))
+    {
+        fprintf(stdout,
+                "Test system '%s' cannot run with %d ranks.\n"
+                "The supported numbers are %s 1.\n",
+                simulationName.c_str(), numRanksAvailable, numRanksAvailable == 1 ? ">" : "=");
+        return;
+    }
+
+    std::string theMdpFile =
+            "coulombtype     = PME\n"
+            "nstcalcenergy   = 1\n"
+            "nstenergy       = 4\n"
+            "nstfout         = 4\n"
+            "rcoulomb        = 1.5\n"
+            "rvdw            = 1.5\n"
+            "pme-order       = 4\n"
+            "fourier-spacing = 0.2\n"
+            "dt              = 0.0025\n"
+            "nsteps          = 20\n";
+
+    if (simulationName == "3DC")
+    {
+        theMdpFile +=
+                "ewald-geometry = 3DC\n"
+                "pbc             = xy\n"
+                "nwall           = 2\n"
+                "wall-type       = 12-6\n"
+                "wall-atomtype   = C C\n"
+                "wall-ewald-zfac = 2\n";
+    }
+    else
+    {
+        theMdpFile += "epsilon-surface = 1\n";
+        if (simulationName == "epsilon-surface-constraint")
+        {
+            theMdpFile += "constraints = all-bonds";
+        }
+    }
+
+    // Prepare the .tpr file
+    {
+        CommandLine caller;
+        runner_.useTopGroAndNdxFromDatabase("dipoles");
+        runner_.useStringAsMdpFile(theMdpFile);
+        EXPECT_EQ(0, runner_.callGrompp(caller));
+    }
+    // Do mdrun
+    {
+        CommandLine mdrunCaller;
+        ASSERT_EQ(0, runner_.callMdrun(mdrunCaller));
+        EnergyTermsToCompare energyTermsToCompare{
+            { { interaction_function[F_EPOT].longname, absoluteTolerance(1e-3) },
+              { interaction_function[F_ETOT].longname, absoluteTolerance(1e-3) } }
+        };
+        TestReferenceData refData;
+        auto checker = refData.rootChecker().checkCompound("Simulation", simulationName);
+        checkEnergiesAgainstReferenceData(runner_.edrFileName_, energyTermsToCompare, &checker);
+        // Now check the forces
+        TrajectoryFrameReader reader(runner_.fullPrecisionTrajectoryFileName_);
+        checker.setDefaultTolerance(relativeToleranceAsFloatingPoint(1, 1e-3));
+        do
+        {
+            auto frame = reader.frame();
+            auto force = frame.f();
+            int  atom  = 0;
+            for (auto& f : force)
+            {
+                std::string forceName = frame.frameName() + " F[" + toString(atom) + "]";
+
+                checker.checkVector(f, forceName.c_str());
+                atom++;
+            }
+        } while (reader.readNextFrame());
+    }
+}
+
+//! Containers of systems to test.
+//! \{
+std::vector<std::string> surfaceTerm = { "3DC", "epsilon-surface-constraint", "epsilon-surface" };
+//! \}
+
+INSTANTIATE_TEST_CASE_P(EwaldSurfaceTerm, EwaldSurfaceTermTest, ::testing::ValuesIn(surfaceTerm));
+
+} // namespace
+} // namespace test
+} // namespace gmx
index 6e40311a5a424603421d497ef43874882180fc07..ed70e3d6635cefb9a5aa1694f9cf8eb6122cfed5 100644 (file)
@@ -260,7 +260,7 @@ void runTest(TestFileManager*            fileManager,
         caller.addOption("-maxwarn", maxWarningsTolerated);
         runner->useTopGroAndNdxFromDatabase(simulationName);
         auto firstPartMdpFieldValues      = mdpFieldValues;
-        firstPartMdpFieldValues["nsteps"] = "8";
+        firstPartMdpFieldValues["nsteps"] = std::to_string(std::stoi(mdpFieldValues.at("nsteps")) / 2);
         runner->useStringAsMdpFile(prepareMdpFileContents(firstPartMdpFieldValues));
         runner->tprFileName_ = firstPartRunTprFileName;
         EXPECT_EQ(0, runner->callGrompp(caller));
@@ -303,7 +303,7 @@ void runTest(TestFileManager*            fileManager,
 
     // Build the functor that will compare energy frames on the chosen
     // energy terms.
-    EnergyComparison energyComparison(energyTermsToCompare);
+    EnergyComparison energyComparison(energyTermsToCompare, MaxNumFrames::compareAllFrames());
 
     // Build the manager that will present matching pairs of frames to compare.
     //
@@ -359,7 +359,8 @@ TEST_P(MdrunNoAppendContinuationIsExact, WithinTolerances)
     // TODO: Update this as modular simulator gains functionality
     const bool isModularSimulatorExplicitlyDisabled = (getenv("GMX_DISABLE_MODULAR_SIMULATOR") != nullptr);
     const bool isTCouplingCompatibleWithModularSimulator =
-            (temperatureCoupling == "no" || temperatureCoupling == "v-rescale");
+            (temperatureCoupling == "no" || temperatureCoupling == "v-rescale"
+             || temperatureCoupling == "berendsen");
     if (integrator == "md-vv" && pressureCoupling == "parrinello-rahman"
         && (isModularSimulatorExplicitlyDisabled || !isTCouplingCompatibleWithModularSimulator))
     {
@@ -383,6 +384,7 @@ TEST_P(MdrunNoAppendContinuationIsExact, WithinTolerances)
     // The exact lambda state choice is unimportant, so long as there
     // is one when using an FEP input.
     mdpFieldValues["other"] += formatString("\ninit-lambda-state = %d", 3);
+    mdpFieldValues["nsteps"] = "16";
 
     // Forces on GPUs are generally not reproducible enough for a tight
     // tolerance. Similarly, the propagation of sd and bd are not as
@@ -397,12 +399,34 @@ TEST_P(MdrunNoAppendContinuationIsExact, WithinTolerances)
     {
         // Somehow, the bd integrator has never been as reproducible
         // as the others, either in continuations or reruns.
-        ulpToleranceInMixed = 128;
+        ulpToleranceInMixed = 200;
+    }
+    EnergyTermsToCompare energyTermsToCompare{
+        { { interaction_function[F_EPOT].longname,
+            relativeToleranceAsPrecisionDependentUlp(10.0, ulpToleranceInMixed, ulpToleranceInDouble) },
+          { interaction_function[F_EKIN].longname,
+            relativeToleranceAsPrecisionDependentUlp(10.0, ulpToleranceInMixed, ulpToleranceInDouble) } }
+    };
+
+    if (temperatureCoupling != "no" || pressureCoupling != "no")
+    {
+        energyTermsToCompare.insert({ interaction_function[F_ECONSERVED].longname,
+                                      relativeToleranceAsPrecisionDependentUlp(
+                                              10.0, ulpToleranceInMixed, ulpToleranceInDouble) });
+    }
+
+    if (pressureCoupling == "parrinello-rahman")
+    {
+        energyTermsToCompare.insert(
+                { "Box-Vel-XX", relativeToleranceAsPrecisionDependentUlp(1e-12, ulpToleranceInMixed,
+                                                                         ulpToleranceInDouble) });
+        energyTermsToCompare.insert(
+                { "Box-Vel-YY", relativeToleranceAsPrecisionDependentUlp(1e-12, ulpToleranceInMixed,
+                                                                         ulpToleranceInDouble) });
+        energyTermsToCompare.insert(
+                { "Box-Vel-ZZ", relativeToleranceAsPrecisionDependentUlp(1e-12, ulpToleranceInMixed,
+                                                                         ulpToleranceInDouble) });
     }
-    EnergyTermsToCompare energyTermsToCompare{ {
-            { interaction_function[F_EPOT].longname,
-              relativeToleranceAsPrecisionDependentUlp(10.0, ulpToleranceInMixed, ulpToleranceInDouble) },
-    } };
 
     int numWarningsToTolerate = 1;
     runTest(&fileManager_, &runner_, simulationName, numWarningsToTolerate, mdpFieldValues,
@@ -412,7 +436,7 @@ TEST_P(MdrunNoAppendContinuationIsExact, WithinTolerances)
 // TODO The time for OpenCL kernel compilation means these tests time
 // out. Once that compilation is cached for the whole process, these
 // tests can run in such configurations.
-#if GMX_GPU != GMX_GPU_OPENCL
+#if !GMX_GPU_OPENCL
 
 INSTANTIATE_TEST_CASE_P(
         NormalIntegrators,
@@ -437,12 +461,13 @@ INSTANTIATE_TEST_CASE_P(
                            ::testing::Values("berendsen", "v-rescale", "nose-hoover"),
                            ::testing::Values("no")));
 
-INSTANTIATE_TEST_CASE_P(NPH,
-                        MdrunNoAppendContinuationIsExact,
-                        ::testing::Combine(::testing::Values("argon12"),
-                                           ::testing::Values("md", "md-vv"),
-                                           ::testing::Values("no"),
-                                           ::testing::Values("berendsen", "parrinello-rahman")));
+INSTANTIATE_TEST_CASE_P(
+        NPH,
+        MdrunNoAppendContinuationIsExact,
+        ::testing::Combine(::testing::Values("argon12"),
+                           ::testing::Values("md", "md-vv"),
+                           ::testing::Values("no"),
+                           ::testing::Values("berendsen", "parrinello-rahman", "C-rescale")));
 
 INSTANTIATE_TEST_CASE_P(
         NPT,
@@ -450,7 +475,7 @@ INSTANTIATE_TEST_CASE_P(
         ::testing::Combine(::testing::Values("argon12"),
                            ::testing::Values("md", "md-vv"),
                            ::testing::Values("berendsen", "v-rescale", "nose-hoover"),
-                           ::testing::Values("berendsen", "parrinello-rahman")));
+                           ::testing::Values("berendsen", "parrinello-rahman", "C-rescale")));
 
 INSTANTIATE_TEST_CASE_P(MTTK,
                         MdrunNoAppendContinuationIsExact,
diff --git a/src/programs/mdrun/tests/freeenergy.cpp b/src/programs/mdrun/tests/freeenergy.cpp
new file mode 100644 (file)
index 0000000..2e012bf
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ * \brief
+ * Tests to compare free energy simulations to reference
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_mdrun_integration_tests
+ */
+#include "gmxpre.h"
+
+#include "config.h"
+
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/filestream.h"
+#include "gromacs/utility/path.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "testutils/mpitest.h"
+#include "testutils/refdata.h"
+#include "testutils/setenv.h"
+#include "testutils/simulationdatabase.h"
+#include "testutils/xvgtest.h"
+
+#include "moduletest.h"
+#include "simulatorcomparison.h"
+
+namespace gmx::test
+{
+namespace
+{
+
+/*! \brief Test fixture base for free energy calculations
+ *
+ * This test ensures that selected free energy perturbation calculations produce
+ * results identical to an earlier version. The results of this earlier version
+ * have been verified manually to ensure physical correctness.
+ */
+using MaxNumWarnings                = int;
+using ListOfInteractionsToTest      = std::vector<int>;
+using FreeEnergyReferenceTestParams = std::tuple<std::string, MaxNumWarnings, ListOfInteractionsToTest>;
+class FreeEnergyReferenceTest :
+    public MdrunTestFixture,
+    public ::testing::WithParamInterface<FreeEnergyReferenceTestParams>
+{
+public:
+    struct PrintParametersToString
+    {
+        template<class ParamType>
+        std::string operator()(const testing::TestParamInfo<ParamType>& parameter) const
+        {
+            auto simulationName = std::get<0>(parameter.param);
+            std::replace(simulationName.begin(), simulationName.end(), '-', '_');
+            return simulationName + (GMX_DOUBLE ? "_d" : "_s");
+        }
+    };
+};
+
+TEST_P(FreeEnergyReferenceTest, WithinTolerances)
+{
+    const auto& simulationName   = std::get<0>(GetParam());
+    const auto  maxNumWarnings   = std::get<1>(GetParam());
+    const auto& interactionsList = std::get<2>(GetParam());
+
+    // TODO In similar tests, we are checking if the tests
+    //      can be run with the number of MPI ranks available
+
+    SCOPED_TRACE(formatString("Comparing FEP simulation '%s' to reference", simulationName.c_str()));
+
+    // Tolerance set to pass with identical code version and a range of different test setups
+    const auto defaultEnergyTolerance = relativeToleranceAsFloatingPoint(50.0, GMX_DOUBLE ? 1e-5 : 1e-4);
+
+    EnergyTermsToCompare energyTermsToCompare{ { interaction_function[F_EPOT].longname,
+                                                 defaultEnergyTolerance } };
+    for (const auto& interaction : interactionsList)
+    {
+        energyTermsToCompare.emplace(interaction_function[interaction].longname, defaultEnergyTolerance);
+    }
+
+    // Specify how trajectory frame matching must work (only testing forces).
+    TrajectoryFrameMatchSettings trajectoryMatchSettings{ false,
+                                                          false,
+                                                          false,
+                                                          ComparisonConditions::NoComparison,
+                                                          ComparisonConditions::NoComparison,
+                                                          ComparisonConditions::MustCompare };
+    TrajectoryTolerances trajectoryTolerances = TrajectoryComparison::s_defaultTrajectoryTolerances;
+
+    // Build the functor that will compare reference and test
+    // trajectory frames in the chosen way.
+    TrajectoryComparison trajectoryComparison{ trajectoryMatchSettings, trajectoryTolerances };
+
+    // Set simulation file names
+    auto simulationTrajectoryFileName = fileManager_.getTemporaryFilePath("trajectory.trr");
+    auto simulationEdrFileName        = fileManager_.getTemporaryFilePath("energy.edr");
+    auto simulationDhdlFileName       = fileManager_.getTemporaryFilePath("dhdl.xvg");
+
+    // Run grompp
+    runner_.tprFileName_ = fileManager_.getTemporaryFilePath("sim.tpr");
+    runner_.useTopGroAndMdpFromFepTestDatabase(simulationName);
+    runGrompp(&runner_, { SimulationOptionTuple("-maxwarn", std::to_string(maxNumWarnings)) });
+
+    // Do mdrun
+    runner_.fullPrecisionTrajectoryFileName_ = simulationTrajectoryFileName;
+    runner_.edrFileName_                     = simulationEdrFileName;
+    runner_.dhdlFileName_                    = simulationDhdlFileName;
+    runMdrun(&runner_);
+
+    // Currently used tests write trajectory (x/v/f) frames every 20 steps.
+    // Testing more than the first force frame is only feasible in double precision
+    // using a single rank.
+    // Note that this only concerns trajectory frames, energy frames are checked
+    // in all cases.
+    const bool testAllTrajectoryFrames = (GMX_DOUBLE && (getNumberOfTestMpiRanks() == 1));
+
+    // Compare simulation results
+    TestReferenceData    refData;
+    TestReferenceChecker rootChecker(refData.rootChecker());
+    // Check that the energies agree with the refdata within tolerance.
+    checkEnergiesAgainstReferenceData(simulationEdrFileName, energyTermsToCompare, &rootChecker);
+    // Check that the trajectories agree with the refdata within tolerance.
+    if (testAllTrajectoryFrames)
+    {
+        checkTrajectoryAgainstReferenceData(simulationTrajectoryFileName, trajectoryComparison, &rootChecker);
+    }
+    else
+    {
+        checkTrajectoryAgainstReferenceData(simulationTrajectoryFileName, trajectoryComparison,
+                                            &rootChecker, MaxNumFrames(1));
+    }
+    if (File::exists(simulationDhdlFileName, File::returnFalseOnError))
+    {
+        TextInputFile dhdlFile(simulationDhdlFileName);
+        auto          settings = XvgMatchSettings();
+        settings.tolerance     = defaultEnergyTolerance;
+        checkXvgFile(&dhdlFile, &rootChecker, settings);
+    }
+}
+
+// TODO: The time for OpenCL kernel compilation means these tests time
+//       out. Once that compilation is cached for the whole process, these
+//       tests can run in such configurations.
+#if !GMX_GPU_OPENCL
+INSTANTIATE_TEST_CASE_P(
+        FreeEnergyCalculationsAreEquivalentToReference,
+        FreeEnergyReferenceTest,
+        ::testing::Values(
+                FreeEnergyReferenceTestParams{ "coulandvdwsequential_coul",
+                                               MaxNumWarnings(0),
+                                               { F_DVDL_COUL, F_DVDL_VDW } },
+                FreeEnergyReferenceTestParams{ "coulandvdwsequential_vdw",
+                                               MaxNumWarnings(0),
+                                               { F_DVDL_COUL, F_DVDL_VDW } },
+                FreeEnergyReferenceTestParams{ "coulandvdwtogether", MaxNumWarnings(0), { F_DVDL } },
+                FreeEnergyReferenceTestParams{ "expanded", MaxNumWarnings(0), { F_DVDL_COUL, F_DVDL_VDW } },
+                // Tolerated warnings: No default bonded interaction types for perturbed atoms (10x)
+                FreeEnergyReferenceTestParams{ "relative",
+                                               MaxNumWarnings(10),
+                                               { F_DVDL, F_DVDL_COUL, F_DVDL_VDW, F_DVDL_BONDED } },
+                // Tolerated warnings: No default bonded interaction types for perturbed atoms (10x)
+                FreeEnergyReferenceTestParams{
+                        "relative-position-restraints",
+                        MaxNumWarnings(10),
+                        { F_DVDL, F_DVDL_COUL, F_DVDL_VDW, F_DVDL_BONDED, F_DVDL_RESTRAINT } },
+                FreeEnergyReferenceTestParams{ "restraints", MaxNumWarnings(0), { F_DVDL_RESTRAINT } },
+                FreeEnergyReferenceTestParams{ "simtemp", MaxNumWarnings(0), {} },
+                FreeEnergyReferenceTestParams{ "transformAtoB", MaxNumWarnings(0), { F_DVDL } },
+                FreeEnergyReferenceTestParams{ "vdwalone", MaxNumWarnings(0), { F_DVDL } }),
+        FreeEnergyReferenceTest::PrintParametersToString());
+#else
+INSTANTIATE_TEST_CASE_P(
+        DISABLED_FreeEnergyCalculationsAreEquivalentToReference,
+        FreeEnergyReferenceTest,
+        ::testing::Values(
+                FreeEnergyReferenceTestParams{ "coulandvdwsequential_coul",
+                                               MaxNumWarnings(0),
+                                               { F_DVDL_COUL, F_DVDL_VDW } },
+                FreeEnergyReferenceTestParams{ "coulandvdwsequential_vdw",
+                                               MaxNumWarnings(0),
+                                               { F_DVDL_COUL, F_DVDL_VDW } },
+                FreeEnergyReferenceTestParams{ "coulandvdwtogether", MaxNumWarnings(0), { F_DVDL } },
+                FreeEnergyReferenceTestParams{ "expanded", MaxNumWarnings(0), { F_DVDL_COUL, F_DVDL_VDW } },
+                // Tolerated warnings: No default bonded interaction types for perturbed atoms (10x)
+                FreeEnergyReferenceTestParams{ "relative",
+                                               MaxNumWarnings(10),
+                                               { F_DVDL, F_DVDL_COUL, F_DVDL_VDW, F_DVDL_BONDED } },
+                // Tolerated warnings: No default bonded interaction types for perturbed atoms (10x)
+                FreeEnergyReferenceTestParams{
+                        "relative-position-restraints",
+                        MaxNumWarnings(10),
+                        { F_DVDL, F_DVDL_COUL, F_DVDL_VDW, F_DVDL_BONDED, F_DVDL_RESTRAINT } },
+                FreeEnergyReferenceTestParams{ "restraints", MaxNumWarnings(0), { F_DVDL_RESTRAINT } },
+                FreeEnergyReferenceTestParams{ "simtemp", MaxNumWarnings(0), {} },
+                FreeEnergyReferenceTestParams{ "transformAtoB", MaxNumWarnings(0), { F_DVDL } },
+                FreeEnergyReferenceTestParams{ "vdwalone", MaxNumWarnings(0), { F_DVDL } }),
+        FreeEnergyReferenceTest::PrintParametersToString());
+#endif
+
+} // namespace
+} // namespace gmx::test
index e2e94fafaed6c50aacbf12141dcfd0e5521692cd..0861d949ea734bf884caf0d777d6c075d8dfdfba 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 4447ae8f0798602b00c27e1ee9d9ddc45b8b3e44..a551798aacfe34a07f2f577420d255890c45929f 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -102,6 +103,7 @@ SimulationRunner::SimulationRunner(TestFileManager* fileManager) :
     mtxFileName_(fileManager->getTemporaryFilePath(".mtx")),
 
     nsteps_(-2),
+    mdpSource_(SimulationRunnerMdpSource::Undefined),
     fileManager_(*fileManager)
 {
 #if GMX_LIB_MPI
@@ -114,7 +116,7 @@ SimulationRunner::SimulationRunner(TestFileManager* fileManager) :
 // things that way, this function should be renamed. For now,
 // we use the Verlet scheme and hard-code a tolerance.
 // TODO There is possible outstanding unexplained behaviour of mdp
-// input parsing e.g. Redmine 2074, so this particular set of mdp
+// input parsing e.g. Issue #2074, so this particular set of mdp
 // contents is also tested with GetIrTest in gmxpreprocess-test.
 void SimulationRunner::useEmptyMdpFile()
 {
@@ -128,6 +130,9 @@ void SimulationRunner::useStringAsMdpFile(const char* mdpString)
 
 void SimulationRunner::useStringAsMdpFile(const std::string& mdpString)
 {
+    GMX_RELEASE_ASSERT(mdpSource_ != SimulationRunnerMdpSource::File,
+                       "Cannot mix .mdp file from database with options set via string.");
+    mdpSource_        = SimulationRunnerMdpSource::String;
     mdpInputContents_ = mdpString;
 }
 
@@ -155,10 +160,29 @@ void SimulationRunner::useGroFromDatabase(const char* name)
     groFileName_ = gmx::test::TestFileManager::getInputFilePath((std::string(name) + ".gro").c_str());
 }
 
+void SimulationRunner::useTopGroAndMdpFromFepTestDatabase(const std::string& name)
+{
+    GMX_RELEASE_ASSERT(mdpSource_ != SimulationRunnerMdpSource::String,
+                       "Cannot mix .mdp file from database with options set via string.");
+    mdpSource_   = SimulationRunnerMdpSource::File;
+    topFileName_ = gmx::test::TestFileManager::getInputFilePath("freeenergy/" + name + "/topol.top");
+    groFileName_ = gmx::test::TestFileManager::getInputFilePath("freeenergy/" + name + "/conf.gro");
+    mdpFileName_ =
+            gmx::test::TestFileManager::getInputFilePath("freeenergy/" + name + "/grompp.mdp");
+}
+
 int SimulationRunner::callGromppOnThisRank(const CommandLine& callerRef)
 {
-    const std::string mdpInputFileName(fileManager_.getTemporaryFilePath("input.mdp"));
-    gmx::TextWriter::writeFileFromString(mdpInputFileName, mdpInputContents_);
+    std::string mdpInputFileName;
+    if (mdpSource_ == SimulationRunnerMdpSource::File)
+    {
+        mdpInputFileName = mdpFileName_;
+    }
+    else
+    {
+        mdpInputFileName = fileManager_.getTemporaryFilePath("input.mdp");
+        gmx::TextWriter::writeFileFromString(mdpInputFileName, mdpInputContents_);
+    }
 
     CommandLine caller;
     caller.append("grompp");
@@ -219,7 +243,7 @@ int SimulationRunner::changeTprNsteps(int nsteps)
     caller.addOption("-s", tprFileName_);
     caller.addOption("-o", tprFileName_);
 
-    return gmx_convert_tpr(caller.argc(), caller.argv());
+    return gmx::test::CommandLineTestHelper::runModuleFactory(&gmx::ConvertTprInfo::create, &caller);
 }
 
 int SimulationRunner::callNmeig()
@@ -260,6 +284,10 @@ int SimulationRunner::callMdrun(const CommandLine& callerRef)
     caller.addOption("-mtx", mtxFileName_);
     caller.addOption("-o", fullPrecisionTrajectoryFileName_);
     caller.addOption("-x", reducedPrecisionTrajectoryFileName_);
+    if (!dhdlFileName_.empty())
+    {
+        caller.addOption("-dhdl", dhdlFileName_);
+    }
 
     caller.addOption("-deffnm", fileManager_.getTemporaryFilePath("state"));
 
index 3790cd220d1a0f46b6a7532310add1e8eaf574a1..bbf140cee51e981e0b3ce6aee5ac79e3a7235ae5 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -59,6 +60,21 @@ namespace gmx
 namespace test
 {
 
+/*! \internal
+ * \brief How the mdp file of the SimulationRunner is defined
+ */
+enum class SimulationRunnerMdpSource
+{
+    //! The default behavior. Will result in an empty mdp file.
+    Undefined,
+    //! Mdp options are set via string using SimulationRunner::useStringAsMdpFile
+    String,
+    //! Mdp options are read from a file set in SimulationRunner::useTopGroAndMdpFromFepTestDatabase
+    File,
+    //! Signals the last enum entry
+    Count
+};
+
 /*! \internal
  * \brief Helper object for running grompp and mdrun in
  * integration tests of mdrun functionality
@@ -104,6 +120,8 @@ public:
     void useTopGroAndNdxFromDatabase(const std::string& name);
     //! Use a standard .gro file as input to grompp
     void useGroFromDatabase(const char* name);
+    //! Use .top, .gro, and .mdp from FEP test database
+    void useTopGroAndMdpFromFepTestDatabase(const std::string& name);
     //! Calls grompp (on rank 0, with a customized command line) to prepare for the mdrun test
     int callGrompp(const CommandLine& callerRef);
     //! Convenience wrapper for a default call to \c callGrompp
@@ -135,6 +153,7 @@ public:
      */
     std::string topFileName_;
     std::string groFileName_;
+    std::string mdpFileName_;
     std::string fullPrecisionTrajectoryFileName_;
     std::string reducedPrecisionTrajectoryFileName_;
     std::string groOutputFileName_;
@@ -146,8 +165,11 @@ public:
     std::string mtxFileName_;
     std::string cptFileName_;
     std::string swapFileName_;
+    std::string dhdlFileName_;
     int         nsteps_;
     //@}
+    //! How the mdp options are defined
+    SimulationRunnerMdpSource mdpSource_;
     //! What will be written into a temporary mdp file before the grompp call
     std::string mdpInputContents_;
 
diff --git a/src/programs/mdrun/tests/multiple_time_stepping.cpp b/src/programs/mdrun/tests/multiple_time_stepping.cpp
new file mode 100644 (file)
index 0000000..44d6ffd
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ * \brief
+ * Tests to compare that multiple time stepping is (nearly) identical to normal integration.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdrun_integration_tests
+ */
+#include "gmxpre.h"
+
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "testutils/mpitest.h"
+#include "testutils/setenv.h"
+#include "testutils/simulationdatabase.h"
+
+#include "moduletest.h"
+#include "simulatorcomparison.h"
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+/*! \brief Test fixture base for two integration schemes
+ *
+ * This test ensures that integration with(out) different multiple time stepping
+ * scheems (called via different mdp options) yield near identical energies,
+ * forces and virial at step 0 and similar energies and virial after 4 steps.
+ */
+using MtsComparisonTestParams = std::tuple<std::string, std::string>;
+class MtsComparisonTest : public MdrunTestFixture, public ::testing::WithParamInterface<MtsComparisonTestParams>
+{
+};
+
+//! Returns set of energy terms to compare with associated tolerances
+EnergyTermsToCompare energyTermsToCompare(const real energyTol, const real virialTol)
+{
+    return EnergyTermsToCompare{ { { interaction_function[F_EPOT].longname,
+                                     relativeToleranceAsFloatingPoint(100.0, energyTol) },
+                                   { "Vir-XX", relativeToleranceAsFloatingPoint(30.0, virialTol) },
+                                   { "Vir-YY", relativeToleranceAsFloatingPoint(30.0, virialTol) },
+                                   { "Vir-ZZ", relativeToleranceAsFloatingPoint(30.0, virialTol) } } };
+}
+
+TEST_P(MtsComparisonTest, WithinTolerances)
+{
+    auto params         = GetParam();
+    auto simulationName = std::get<0>(params);
+    auto mtsScheme      = std::get<1>(params);
+
+    // Note that there should be no relevant limitation on MPI ranks and OpenMP threads
+    SCOPED_TRACE(formatString("Comparing for '%s' no MTS with MTS scheme '%s'",
+                              simulationName.c_str(), mtsScheme.c_str()));
+
+    const int numSteps         = 4;
+    auto      sharedMdpOptions = gmx::formatString(
+            "integrator   = md\n"
+            "dt           = 0.001\n"
+            "nsteps       = %d\n"
+            "verlet-buffer-tolerance = -1\n"
+            "rlist        = 1.0\n"
+            "coulomb-type = PME\n"
+            "vdw-type     = cut-off\n"
+            "rcoulomb     = 0.9\n"
+            "rvdw         = 0.9\n"
+            "constraints  = h-bonds\n",
+            numSteps);
+
+    // set nstfout to > numSteps so we only write forces at step 0
+    const int nstfout       = 2 * numSteps;
+    auto      refMdpOptions = sharedMdpOptions
+                         + gmx::formatString(
+                                   "mts       = no\n"
+                                   "nstcalcenergy = %d\n"
+                                   "nstenergy = %d\n"
+                                   "nstxout   = 0\n"
+                                   "nstvout   = 0\n"
+                                   "nstfout   = %d\n",
+                                   numSteps, numSteps, nstfout);
+
+    auto mtsMdpOptions = sharedMdpOptions
+                         + gmx::formatString(
+                                   "mts        = yes\n"
+                                   "mts-levels = 2\n"
+                                   "mts-level2-forces = %s\n"
+                                   "mts-level2-factor = 2\n"
+                                   "nstcalcenergy = %d\n"
+                                   "nstenergy  = %d\n"
+                                   "nstxout    = 0\n"
+                                   "nstvout    = 0\n"
+                                   "nstfout    = %d\n",
+                                   mtsScheme.c_str(), numSteps, numSteps, nstfout);
+
+    // At step 0 the energy and virial should only differ due to rounding errors
+    EnergyTermsToCompare energyTermsToCompareStep0 = energyTermsToCompare(0.001, 0.01);
+    EnergyTermsToCompare energyTermsToCompareAllSteps =
+            energyTermsToCompare(mtsScheme == "pme" ? 0.015 : 0.04, mtsScheme == "pme" ? 0.1 : 0.2);
+
+    // Specify how trajectory frame matching must work.
+    TrajectoryFrameMatchSettings trajectoryMatchSettings{ true,
+                                                          true,
+                                                          true,
+                                                          ComparisonConditions::NoComparison,
+                                                          ComparisonConditions::NoComparison,
+                                                          ComparisonConditions::MustCompare };
+    TrajectoryTolerances trajectoryTolerances = TrajectoryComparison::s_defaultTrajectoryTolerances;
+
+    // Build the functor that will compare reference and test
+    // trajectory frames in the chosen way.
+    TrajectoryComparison trajectoryComparison{ trajectoryMatchSettings, trajectoryTolerances };
+
+    // Set file names
+    auto simulator1TrajectoryFileName = fileManager_.getTemporaryFilePath("sim1.trr");
+    auto simulator1EdrFileName        = fileManager_.getTemporaryFilePath("sim1.edr");
+    auto simulator2TrajectoryFileName = fileManager_.getTemporaryFilePath("sim2.trr");
+    auto simulator2EdrFileName        = fileManager_.getTemporaryFilePath("sim2.edr");
+
+    // Run grompp
+    runner_.tprFileName_ = fileManager_.getTemporaryFilePath("sim.tpr");
+    runner_.useTopGroAndNdxFromDatabase(simulationName);
+    runner_.useStringAsMdpFile(refMdpOptions);
+    runGrompp(&runner_);
+
+    // Do first mdrun
+    runner_.fullPrecisionTrajectoryFileName_ = simulator1TrajectoryFileName;
+    runner_.edrFileName_                     = simulator1EdrFileName;
+    runMdrun(&runner_);
+
+    runner_.useStringAsMdpFile(mtsMdpOptions);
+    runGrompp(&runner_);
+
+    // Do second mdrun
+    runner_.fullPrecisionTrajectoryFileName_ = simulator2TrajectoryFileName;
+    runner_.edrFileName_                     = simulator2EdrFileName;
+    runMdrun(&runner_);
+
+    // Compare simulation results at step 0, which should be indentical
+    compareEnergies(simulator1EdrFileName, simulator2EdrFileName, energyTermsToCompareStep0,
+                    MaxNumFrames(1));
+    compareTrajectories(simulator1TrajectoryFileName, simulator2TrajectoryFileName, trajectoryComparison);
+
+    // Compare energies at the last step (and step 0 again) with lower tolerance
+    compareEnergies(simulator1EdrFileName, simulator2EdrFileName, energyTermsToCompareAllSteps,
+                    MaxNumFrames::compareAllFrames());
+}
+
+INSTANTIATE_TEST_CASE_P(
+        MultipleTimeSteppingIsNearSingleTimeStepping,
+        MtsComparisonTest,
+        ::testing::Combine(::testing::Values("ala"),
+                           ::testing::Values("longrange-nonbonded",
+                                             "longrange-nonbonded nonbonded pair dihedral")));
+
+} // namespace
+} // namespace test
+} // namespace gmx
index 2d6912517c060b9608f3535052bd143631b05cd0..8d5673ff1df26e041d4f0679475274aa51e15dc1 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d836c8f5b03771bc65788a8ff2b6f55d21ff08a2..b53756599b8a77727bcdf203a7a70764b9e5cb11 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 11419d86aca037528c4a30f0a080a610aec24abe..05990d6981af98f42603d882b2bbbbcc704d9fb1 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 09108fb45a7c27507c5ed0a90ed7b1a74661d0da..23bf340824dbda264493d6d7f1d59c4a8a212ebc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -142,6 +142,13 @@ TEST_P(NormalModesTest, WithinTolerances)
     }
 }
 
+// The time for OpenCL kernel compilation means these tests might time
+// out. If that proves to be a problem, these can be disabled for
+// OpenCL builds. However, once that compilation is cached for the
+// lifetime of the whole test binary process, these tests should run in
+// such configurations.
+#if GMX_DOUBLE
+
 //! Containers of systems and integrators to test.
 //! \{
 std::vector<std::string> systemsToTest_g     = { "scaled-water", "villin", "spc-dimer", "one-tip5p",
@@ -150,12 +157,6 @@ std::vector<std::string> integratorsToTest_g = { "nm" };
 
 //! \}
 
-// The time for OpenCL kernel compilation means these tests might time
-// out. If that proves to be a problem, these can be disabled for
-// OpenCL builds. However, once that compilation is cached for the
-// lifetime of the whole test binary process, these tests should run in
-// such configurations.
-#if GMX_DOUBLE
 INSTANTIATE_TEST_CASE_P(NormalModesWorks,
                         NormalModesTest,
                         ::testing::Combine(::testing::ValuesIn(systemsToTest_g),
index 5b0db0bfe0dadf01ec5f93ac32b2a13a58963d30..9414b16c9f2e2659a8b53e2728627c363f1c0668 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index cd5180bd5ca392d1554cf27c1c7f0aa6705f2cb7..6dc8f02e42893306476c85f3410e3f5058363c83 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -106,7 +106,8 @@ public:
     //! Names for the output files from the reference mdrun call
     ReferenceFileNames referenceFileNames_ = { fileManager_.getTemporaryFilePath("reference.edr") };
     //! Functor for energy comparison
-    EnergyComparison energyComparison_{ EnergyComparison::s_defaultEnergyTermsToCompare };
+    EnergyComparison energyComparison_{ EnergyComparison::defaultEnergyTermsToCompare(),
+                                        MaxNumFrames::compareAllFrames() };
     //! Names of energies compared by energyComparison_
     std::vector<std::string> namesOfEnergiesToMatch_ = energyComparison_.getEnergyNames();
 };
@@ -314,18 +315,18 @@ std::vector<PropagationParameters> propagationParametersWithCoupling()
     std::string nstcomm    = "5";
 
     std::vector<PropagationParameters> parameterSets;
-    for (std::string simulationName : { "argon12" })
+    for (const std::string& simulationName : { "argon12" })
     {
-        for (std::string integrator : { "md", "sd", "md-vv" })
+        for (const std::string& integrator : { "md", "sd", "md-vv" })
         {
-            for (std::string tcoupl : { "no", "v-rescale", "Nose-Hoover" })
+            for (const std::string& tcoupl : { "no", "v-rescale", "Nose-Hoover" })
             {
                 // SD doesn't support temperature-coupling algorithms,
                 if (integrator == "sd" && tcoupl != "no")
                 {
                     continue;
                 }
-                for (std::string pcoupl : { "no", "Berendsen", "Parrinello-Rahman" })
+                for (std::string pcoupl : { "no", "Berendsen", "Parrinello-Rahman", "C-rescale" })
                 {
                     // VV supports few algorithm combinations
                     if (integrator == "md-vv")
@@ -381,18 +382,18 @@ std::vector<PropagationParameters> propagationParametersWithConstraints()
     std::string nstcomm    = "5";
 
     std::vector<PropagationParameters> parameterSets;
-    for (std::string simulationName : { "tip3p5" })
+    for (const std::string& simulationName : { "tip3p5" })
     {
-        for (std::string integrator : { "md", "sd", "md-vv" })
+        for (const std::string& integrator : { "md", "sd", "md-vv" })
         {
-            for (std::string tcoupl : { "no", "v-rescale" })
+            for (const std::string& tcoupl : { "no", "v-rescale" })
             {
                 // SD doesn't support temperature-coupling algorithms,
                 if (integrator == "sd" && tcoupl != "no")
                 {
                     continue;
                 }
-                for (std::string pcoupl : { "no", "Parrinello-Rahman" })
+                for (std::string pcoupl : { "no", "Parrinello-Rahman", "C-rescale" })
                 {
                     // VV supports few algorithm combinations
                     if (integrator == "md-vv")
@@ -439,7 +440,7 @@ using ::testing::ValuesIn;
 // TODO The time for OpenCL kernel compilation means these tests time
 // out. Once that compilation is cached for the whole process, these
 // tests can run in such configurations.
-#if GMX_GPU != GMX_GPU_OPENCL
+#if !GMX_GPU_OPENCL
 INSTANTIATE_TEST_CASE_P(BasicPropagators,
                         PeriodicActionsTest,
                         Combine(ValuesIn(simplePropagationParameters()), Values(outputParameters)));
index 285adf87e76f72a4ebf18bdab31c12cb35e11f74..4aa1219ccade36859d14b72dc98c4831486c2e9e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -55,9 +55,9 @@
 #include <gtest/gtest-spi.h>
 
 #include "gromacs/ewald/pme.h"
-#include "gromacs/gpu_utils/gpu_testutils.h"
 #include "gromacs/hardware/detecthardware.h"
-#include "gromacs/hardware/gpu_hw_info.h"
+#include "gromacs/hardware/device_management.h"
+#include "gromacs/hardware/hw_info.h"
 #include "gromacs/trajectory/energyframe.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/gmxmpi.h"
@@ -84,23 +84,12 @@ namespace
 class PmeTest : public MdrunTestFixture
 {
 public:
-    //! Before any test is run, work out whether any compatible GPUs exist.
-    static void SetUpTestCase();
-    //! Store whether any compatible GPUs exist.
-    static bool s_hasCompatibleGpus;
     //! Convenience typedef
     using RunModesList = std::map<std::string, std::vector<const char*>>;
     //! Runs the test with the given inputs
     void runTest(const RunModesList& runModes);
 };
 
-bool PmeTest::s_hasCompatibleGpus = false;
-
-void PmeTest::SetUpTestCase()
-{
-    s_hasCompatibleGpus = canComputeOnGpu();
-}
-
 void PmeTest::runTest(const RunModesList& runModes)
 {
     const std::string inputFile = "spc-and-methanol";
@@ -126,8 +115,9 @@ void PmeTest::runTest(const RunModesList& runModes)
 
     for (const auto& mode : runModes)
     {
+        SCOPED_TRACE("mdrun " + joinStrings(mode.second, " "));
         auto modeTargetsGpus = (mode.first.find("Gpu") != std::string::npos);
-        if (modeTargetsGpus && !s_hasCompatibleGpus)
+        if (modeTargetsGpus && getCompatibleDevices(hardwareInfo_->deviceInfoList).empty())
         {
             // This run mode will cause a fatal error from mdrun when
             // it can't find GPUs, which is not something we're trying
diff --git a/src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductIdentityMatrix.xml b/src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductIdentityMatrix.xml
new file mode 100644 (file)
index 0000000..5659fa1
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-3379.9202</Real>
+    <Real Name="Time 1.000000 Step 1 in frame 1">-3591.7776</Real>
+    <Real Name="Time 2.000000 Step 2 in frame 2">-3856.54</Real>
+  </Energy>
+  <Energy Name="Density fitting">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-3378.9026</Real>
+    <Real Name="Time 1.000000 Step 1 in frame 1">-3590.7383</Real>
+    <Real Name="Time 2.000000 Step 2 in frame 2">-3855.5586</Real>
+  </Energy>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductTranslation.xml b/src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductTranslation.xml
new file mode 100644 (file)
index 0000000..4d3ec70
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-8991.123</Real>
+    <Real Name="Time 1.000000 Step 1 in frame 1">-9367.3584</Real>
+    <Real Name="Time 2.000000 Step 2 in frame 2">-9820.8447</Real>
+  </Energy>
+  <Energy Name="Density fitting">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-8990.1055</Real>
+    <Real Name="Time 1.000000 Step 1 in frame 1">-9366.3193</Real>
+    <Real Name="Time 2.000000 Step 2 in frame 2">-9819.8525</Real>
+  </Energy>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductTranslationAndTransformationMatrix.xml b/src/programs/mdrun/tests/refdata/DensityFittingTest_EnergyMinimizationEnergyCorrectInnerProductTranslationAndTransformationMatrix.xml
new file mode 100644 (file)
index 0000000..f272948
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-8933.2021</Real>
+    <Real Name="Time 1.000000 Step 1 in frame 1">-9308.2793</Real>
+    <Real Name="Time 2.000000 Step 2 in frame 2">-9760.4385</Real>
+  </Energy>
+  <Energy Name="Density fitting">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-8932.1846</Real>
+    <Real Name="Time 1.000000 Step 1 in frame 1">-9307.2402</Real>
+    <Real Name="Time 2.000000 Step 2 in frame 2">-9759.457</Real>
+  </Energy>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/EwaldSurfaceTerm_EwaldSurfaceTermTest_WithinTolerances_0.xml b/src/programs/mdrun/tests/refdata/EwaldSurfaceTerm_EwaldSurfaceTermTest_WithinTolerances_0.xml
new file mode 100644 (file)
index 0000000..047a65e
--- /dev/null
@@ -0,0 +1,142 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Simulation Name="3DC">
+    <Energy Name="Total Energy">
+      <Real Name="Time 0.000000 Step 0 in frame 0">35.954109</Real>
+      <Real Name="Time 0.010000 Step 4 in frame 1">35.93243</Real>
+      <Real Name="Time 0.020000 Step 8 in frame 2">35.888763</Real>
+      <Real Name="Time 0.030000 Step 12 in frame 3">35.851433</Real>
+      <Real Name="Time 0.040000 Step 16 in frame 4">35.843254</Real>
+      <Real Name="Time 0.050000 Step 20 in frame 5">35.868946</Real>
+    </Energy>
+    <Energy Name="Potential">
+      <Real Name="Time 0.000000 Step 0 in frame 0">35.838486</Real>
+      <Real Name="Time 0.010000 Step 4 in frame 1">31.51033</Real>
+      <Real Name="Time 0.020000 Step 8 in frame 2">22.831844</Real>
+      <Real Name="Time 0.030000 Step 12 in frame 3">15.060135</Real>
+      <Real Name="Time 0.040000 Step 16 in frame 4">12.742168</Real>
+      <Real Name="Time 0.050000 Step 20 in frame 5">16.857098</Real>
+    </Energy>
+    <Vector Name="Time 0.000000 Step 0 F[0]">
+      <Real Name="X">0.63591957</Real>
+      <Real Name="Y">519.72107</Real>
+      <Real Name="Z">387.92822</Real>
+    </Vector>
+    <Vector Name="Time 0.000000 Step 0 F[1]">
+      <Real Name="X">-0.77027768</Real>
+      <Real Name="Y">-519.39612</Real>
+      <Real Name="Z">-377.65155</Real>
+    </Vector>
+    <Vector Name="Time 0.000000 Step 0 F[2]">
+      <Real Name="X">18.781555</Real>
+      <Real Name="Y">-2.0529842</Real>
+      <Real Name="Z">14.781036</Real>
+    </Vector>
+    <Vector Name="Time 0.000000 Step 0 F[3]">
+      <Real Name="X">-18.647247</Real>
+      <Real Name="Y">1.7278194</Real>
+      <Real Name="Z">-234.13368</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[0]">
+      <Real Name="X">0.62046039</Real>
+      <Real Name="Y">466.01166</Real>
+      <Real Name="Z">349.07852</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[1]">
+      <Real Name="X">-0.75117296</Real>
+      <Real Name="Y">-465.69577</Real>
+      <Real Name="Z">-339.0498</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[2]">
+      <Real Name="X">11.485992</Real>
+      <Real Name="Y">-2.0129957</Real>
+      <Real Name="Z">7.3729095</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[3]">
+      <Real Name="X">-11.355316</Real>
+      <Real Name="Y">1.6968718</Real>
+      <Real Name="Z">-222.63599</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[0]">
+      <Real Name="X">0.59271532</Real>
+      <Real Name="Y">334.89679</Real>
+      <Real Name="Z">254.41725</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[1]">
+      <Real Name="X">-0.71474606</Real>
+      <Real Name="Y">-334.60272</Real>
+      <Real Name="Z">-244.95985</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[2]">
+      <Real Name="X">-6.2411346</Real>
+      <Real Name="Y">-1.9218638</Real>
+      <Real Name="Z">-10.294846</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[3]">
+      <Real Name="X">6.3630676</Real>
+      <Real Name="Y">1.6275122</Real>
+      <Real Name="Z">-195.02248</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[0]">
+      <Real Name="X">0.58192861</Real>
+      <Real Name="Y">147.74336</Real>
+      <Real Name="Z">119.82979</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[1]">
+      <Real Name="X">-0.69187623</Real>
+      <Real Name="Y">-147.47998</Real>
+      <Real Name="Z">-111.1104</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[2]">
+      <Real Name="X">-31.277664</Real>
+      <Real Name="Y">-1.8096745</Real>
+      <Real Name="Z">-34.310486</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[3]">
+      <Real Name="X">31.387344</Real>
+      <Real Name="Y">1.5458357</Real>
+      <Real Name="Z">-156.77382</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[0]">
+      <Real Name="X">0.62886125</Real>
+      <Real Name="Y">-64.947739</Real>
+      <Real Name="Z">-32.052841</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[1]">
+      <Real Name="X">-0.72547024</Real>
+      <Real Name="Y">65.176849</Real>
+      <Real Name="Z">40.033974</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[2]">
+      <Real Name="X">-59.076599</Real>
+      <Real Name="Y">-1.7106733</Real>
+      <Real Name="Z">-59.353546</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[3]">
+      <Real Name="X">59.172882</Real>
+      <Real Name="Y">1.4807854</Real>
+      <Real Name="Z">-115.10541</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[0]">
+      <Real Name="X">0.78214669</Real>
+      <Real Name="Y">-268.51788</Real>
+      <Real Name="Z">-175.65146</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[1]">
+      <Real Name="X">-0.86627579</Real>
+      <Real Name="Y">268.71423</Real>
+      <Real Name="Z">183.01443</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[2]">
+      <Real Name="X">-84.336151</Real>
+      <Real Name="Y">-1.6569676</Real>
+      <Real Name="Z">-79.859665</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[3]">
+      <Real Name="X">84.419815</Real>
+      <Real Name="Y">1.4594903</Real>
+      <Real Name="Z">-77.279404</Real>
+    </Vector>
+  </Simulation>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/EwaldSurfaceTerm_EwaldSurfaceTermTest_WithinTolerances_1.xml b/src/programs/mdrun/tests/refdata/EwaldSurfaceTerm_EwaldSurfaceTermTest_WithinTolerances_1.xml
new file mode 100644 (file)
index 0000000..91be5ea
--- /dev/null
@@ -0,0 +1,142 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Simulation Name="epsilon-surface-constraint">
+    <Energy Name="Total Energy">
+      <Real Name="Time 0.000000 Step 0 in frame 0">-0.34057474</Real>
+      <Real Name="Time 0.010000 Step 4 in frame 1">-0.34058204</Real>
+      <Real Name="Time 0.020000 Step 8 in frame 2">-0.34044197</Real>
+      <Real Name="Time 0.030000 Step 12 in frame 3">-0.34037</Real>
+      <Real Name="Time 0.040000 Step 16 in frame 4">-0.34102595</Real>
+      <Real Name="Time 0.050000 Step 20 in frame 5">-0.34038031</Real>
+    </Energy>
+    <Energy Name="Potential">
+      <Real Name="Time 0.000000 Step 0 in frame 0">-0.34057617</Real>
+      <Real Name="Time 0.010000 Step 4 in frame 1">-0.34064102</Real>
+      <Real Name="Time 0.020000 Step 8 in frame 2">-0.34065056</Real>
+      <Real Name="Time 0.030000 Step 12 in frame 3">-0.34082031</Real>
+      <Real Name="Time 0.040000 Step 16 in frame 4">-0.34181023</Real>
+      <Real Name="Time 0.050000 Step 20 in frame 5">-0.34159088</Real>
+    </Energy>
+    <Vector Name="Time 0.000000 Step 0 F[0]">
+      <Real Name="X">1.6149496</Real>
+      <Real Name="Y">-0.086868286</Real>
+      <Real Name="Z">-1.0419159</Real>
+    </Vector>
+    <Vector Name="Time 0.000000 Step 0 F[1]">
+      <Real Name="X">-1.8542147</Real>
+      <Real Name="Y">-0.25299072</Real>
+      <Real Name="Z">0.80174255</Real>
+    </Vector>
+    <Vector Name="Time 0.000000 Step 0 F[2]">
+      <Real Name="X">-0.064361572</Real>
+      <Real Name="Y">-0.8647213</Real>
+      <Real Name="Z">-1.2425385</Real>
+    </Vector>
+    <Vector Name="Time 0.000000 Step 0 F[3]">
+      <Real Name="X">0.30360413</Real>
+      <Real Name="Y">1.2046714</Real>
+      <Real Name="Z">1.4829559</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[0]">
+      <Real Name="X">1.6154348</Real>
+      <Real Name="Y">-0.087356567</Real>
+      <Real Name="Z">-1.0413895</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[1]">
+      <Real Name="X">-1.8548124</Real>
+      <Real Name="Y">-0.25245667</Real>
+      <Real Name="Z">0.80117035</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[2]">
+      <Real Name="X">-0.064651489</Real>
+      <Real Name="Y">-0.864492</Real>
+      <Real Name="Z">-1.2429199</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[3]">
+      <Real Name="X">0.30400085</Real>
+      <Real Name="Y">1.2044551</Real>
+      <Real Name="Z">1.4832764</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[0]">
+      <Real Name="X">1.6154604</Real>
+      <Real Name="Y">-0.087097168</Real>
+      <Real Name="Z">-1.0424576</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[1]">
+      <Real Name="X">-1.8550944</Real>
+      <Real Name="Y">-0.25256348</Real>
+      <Real Name="Z">0.80217743</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[2]">
+      <Real Name="X">-0.064407349</Real>
+      <Real Name="Y">-0.86516654</Real>
+      <Real Name="Z">-1.2433014</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[3]">
+      <Real Name="X">0.3039093</Real>
+      <Real Name="Y">1.2051598</Real>
+      <Real Name="Z">1.4837341</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[0]">
+      <Real Name="X">1.6158544</Real>
+      <Real Name="Y">-0.087295532</Real>
+      <Real Name="Z">-1.0429993</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[1]">
+      <Real Name="X">-1.8559394</Real>
+      <Real Name="Y">-0.25222778</Real>
+      <Real Name="Z">0.80271912</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[2]">
+      <Real Name="X">-0.065734863</Real>
+      <Real Name="Y">-0.86546493</Real>
+      <Real Name="Z">-1.2434998</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[3]">
+      <Real Name="X">0.30551147</Real>
+      <Real Name="Y">1.2055182</Real>
+      <Real Name="Z">1.4839935</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[0]">
+      <Real Name="X">1.6170367</Real>
+      <Real Name="Y">-0.08883667</Real>
+      <Real Name="Z">-1.0432281</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[1]">
+      <Real Name="X">-1.8577462</Real>
+      <Real Name="Y">-0.25038147</Real>
+      <Real Name="Z">0.80291748</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[2]">
+      <Real Name="X">-0.067520142</Real>
+      <Real Name="Y">-0.86665791</Real>
+      <Real Name="Z">-1.2446899</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[3]">
+      <Real Name="X">0.30763245</Real>
+      <Real Name="Y">1.2067299</Real>
+      <Real Name="Z">1.4853821</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[0]">
+      <Real Name="X">1.6183333</Real>
+      <Real Name="Y">-0.088378906</Real>
+      <Real Name="Z">-1.0447006</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[1]">
+      <Real Name="X">-1.859828</Real>
+      <Real Name="Y">-0.25050354</Real>
+      <Real Name="Z">0.80425262</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[2]">
+      <Real Name="X">-0.068191528</Real>
+      <Real Name="Y">-0.86807513</Real>
+      <Real Name="Z">-1.2454529</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[3]">
+      <Real Name="X">0.3087616</Real>
+      <Real Name="Y">1.2082047</Real>
+      <Real Name="Z">1.4863129</Real>
+    </Vector>
+  </Simulation>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/EwaldSurfaceTerm_EwaldSurfaceTermTest_WithinTolerances_2.xml b/src/programs/mdrun/tests/refdata/EwaldSurfaceTerm_EwaldSurfaceTermTest_WithinTolerances_2.xml
new file mode 100644 (file)
index 0000000..1ece247
--- /dev/null
@@ -0,0 +1,142 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Simulation Name="epsilon-surface">
+    <Energy Name="Total Energy">
+      <Real Name="Time 0.000000 Step 0 in frame 0">20.32147</Real>
+      <Real Name="Time 0.010000 Step 4 in frame 1">20.300804</Real>
+      <Real Name="Time 0.020000 Step 8 in frame 2">20.259245</Real>
+      <Real Name="Time 0.030000 Step 12 in frame 3">20.223288</Real>
+      <Real Name="Time 0.040000 Step 16 in frame 4">20.215561</Real>
+      <Real Name="Time 0.050000 Step 20 in frame 5">20.240704</Real>
+    </Energy>
+    <Energy Name="Potential">
+      <Real Name="Time 0.000000 Step 0 in frame 0">20.214478</Real>
+      <Real Name="Time 0.010000 Step 4 in frame 1">16.217239</Real>
+      <Real Name="Time 0.020000 Step 8 in frame 2">8.2757568</Real>
+      <Real Name="Time 0.030000 Step 12 in frame 3">1.3982239</Real>
+      <Real Name="Time 0.040000 Step 16 in frame 4">-0.07736969</Real>
+      <Real Name="Time 0.050000 Step 20 in frame 5">4.7798805</Real>
+    </Energy>
+    <Vector Name="Time 0.000000 Step 0 F[0]">
+      <Real Name="X">1.6004291</Real>
+      <Real Name="Y">522.04138</Real>
+      <Real Name="Z">370.92786</Real>
+    </Vector>
+    <Vector Name="Time 0.000000 Step 0 F[1]">
+      <Real Name="X">-1.900637</Real>
+      <Real Name="Y">-522.46527</Real>
+      <Real Name="Z">-371.22949</Real>
+    </Vector>
+    <Vector Name="Time 0.000000 Step 0 F[2]">
+      <Real Name="X">20.091843</Real>
+      <Real Name="Y">-1.0637188</Real>
+      <Real Name="Z">18.672668</Real>
+    </Vector>
+    <Vector Name="Time 0.000000 Step 0 F[3]">
+      <Real Name="X">-19.791595</Real>
+      <Real Name="Y">1.4878402</Real>
+      <Real Name="Z">-18.371048</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[0]">
+      <Real Name="X">1.5716085</Real>
+      <Real Name="Y">468.56262</Real>
+      <Real Name="Z">332.87283</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[1]">
+      <Real Name="X">-1.8655734</Real>
+      <Real Name="Y">-468.97778</Real>
+      <Real Name="Z">-333.16809</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[2]">
+      <Real Name="X">18.114136</Real>
+      <Real Name="Y">-1.0417742</Real>
+      <Real Name="Z">16.720505</Real>
+    </Vector>
+    <Vector Name="Time 0.010000 Step 4 F[3]">
+      <Real Name="X">-17.82019</Real>
+      <Real Name="Y">1.4571649</Real>
+      <Real Name="Z">-16.425293</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[0]">
+      <Real Name="X">1.5227978</Real>
+      <Real Name="Y">338.07782</Real>
+      <Real Name="Z">239.98503</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[1]">
+      <Real Name="X">-1.801703</Real>
+      <Real Name="Y">-338.47165</Real>
+      <Real Name="Z">-240.26477</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[2]">
+      <Real Name="X">13.286667</Real>
+      <Real Name="Y">-0.99115908</Real>
+      <Real Name="Z">11.956177</Real>
+    </Vector>
+    <Vector Name="Time 0.020000 Step 8 F[3]">
+      <Real Name="X">-13.007904</Real>
+      <Real Name="Y">1.3853663</Real>
+      <Real Name="Z">-11.676315</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[0]">
+      <Real Name="X">1.5251596</Real>
+      <Real Name="Y">152.05093</Real>
+      <Real Name="Z">107.45719</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[1]">
+      <Real Name="X">-1.7829351</Real>
+      <Real Name="Y">-152.41469</Real>
+      <Real Name="Z">-107.71528</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[2]">
+      <Real Name="X">6.4033661</Real>
+      <Real Name="Y">-0.92074591</Real>
+      <Real Name="Z">5.1584015</Real>
+    </Vector>
+    <Vector Name="Time 0.030000 Step 12 F[3]">
+      <Real Name="X">-6.1460266</Real>
+      <Real Name="Y">1.2851006</Real>
+      <Real Name="Z">-4.9001007</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[0]">
+      <Real Name="X">1.6834296</Real>
+      <Real Name="Y">-58.880402</Real>
+      <Real Name="Z">-43.009636</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[1]">
+      <Real Name="X">-1.9177186</Real>
+      <Real Name="Y">58.550491</Real>
+      <Real Name="Z">42.775757</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[2]">
+      <Real Name="X">-1.4052582</Real>
+      <Real Name="Y">-0.84537828</Real>
+      <Real Name="Z">-2.5614777</Real>
+    </Vector>
+    <Vector Name="Time 0.040000 Step 16 F[3]">
+      <Real Name="X">1.6388092</Real>
+      <Real Name="Y">1.1762809</Real>
+      <Real Name="Z">2.7957458</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[0]">
+      <Real Name="X">2.1072018</Real>
+      <Real Name="Y">-259.95499</Real>
+      <Real Name="Z">-186.73514</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[1]">
+      <Real Name="X">-2.3196166</Real>
+      <Real Name="Y">259.6571</Real>
+      <Real Name="Z">186.52396</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[2]">
+      <Real Name="X">-8.8654175</Real>
+      <Real Name="Y">-0.77974892</Real>
+      <Real Name="Z">-9.9438782</Real>
+    </Vector>
+    <Vector Name="Time 0.050000 Step 20 F[3]">
+      <Real Name="X">9.0768127</Real>
+      <Real Name="Y">1.0791473</Real>
+      <Real Name="Z">10.155777</Real>
+    </Vector>
+  </Simulation>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_coul_d.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_coul_d.xml
new file mode 100644 (file)
index 0000000..fefb675
--- /dev/null
@@ -0,0 +1,1849 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVvdw/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">29.433894330051928</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">30.417130846259589</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">31.414134936055238</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">32.401794799591528</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">33.352140525496097</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">34.231034496428215</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">35.000598929083388</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">35.623438951713432</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">36.067562330871745</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">36.310764622676608</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">36.343586561876116</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">36.170448038245517</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">35.808945605312623</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">35.287119026624488</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">34.641621413048796</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">33.911043711616166</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">33.133378572654848</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">32.341857203606843</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">31.562034233592158</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">30.810368968245555</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">30.091952842926048</Real>
+  </Energy>
+  <Energy Name="dVcoul/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-77.8723566401097</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-77.918582136826871</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-78.124028618156075</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-78.494137096512077</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-79.031570998273594</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-79.737380506903918</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-80.609921965073937</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-81.641203629735571</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-82.812078546976551</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-84.088865310692285</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-85.423667967862372</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-86.759022507510224</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-88.035827369731862</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-89.202559066450505</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-90.223739445468937</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-91.086115035376309</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-91.801438920119665</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-92.405181063462749</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-92.950836665173227</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-93.500176168343785</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-94.110809201461151</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1525.248411374062</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1525.0825593635209</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1523.1784334722129</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1519.5758481531782</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1514.560690362274</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1508.6133333608059</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1502.311378889505</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1496.2028688526416</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1490.6892487311616</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1485.9586756010892</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1481.9876648877857</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1478.6034861584553</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1475.5829698252924</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1472.7552659251564</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1470.0723995866124</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1467.6307362386647</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1465.6332992798243</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1464.319400024071</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1463.8891863801446</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1464.4471138446297</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1465.9785237943797</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">599.66148406351431</Real>
+      <Real Name="Y">-789.42802256167397</Real>
+      <Real Name="Z">-85.742992577159555</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-165.11818414023247</Real>
+      <Real Name="Y">-249.46780326492339</Real>
+      <Real Name="Z">33.568037508189256</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-450.2038067172893</Real>
+      <Real Name="Y">276.42125685135187</Real>
+      <Real Name="Z">-221.56920079712444</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.36555125519544</Real>
+      <Real Name="Y">187.51780634665093</Real>
+      <Real Name="Z">30.101004879106622</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">914.49641407652871</Real>
+      <Real Name="Y">783.78409954206518</Real>
+      <Real Name="Z">-20.489505146047286</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-105.64825471129032</Real>
+      <Real Name="Y">193.10440479443781</Real>
+      <Real Name="Z">-234.32509816367735</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1202.8380359088278</Real>
+      <Real Name="Y">-103.95788935753482</Real>
+      <Real Name="Z">940.76412173852918</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">192.58082734769425</Real>
+      <Real Name="Y">-154.02862728087422</Real>
+      <Real Name="Z">-462.12572950935339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">282.3891512282317</Real>
+      <Real Name="Y">-299.01221051005683</Real>
+      <Real Name="Z">-66.355192218570394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.1403582916694575</Real>
+      <Real Name="Y">-33.489750948495725</Real>
+      <Real Name="Z">24.556114163488019</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.7329972478043203</Real>
+      <Real Name="Y">29.402907775985859</Real>
+      <Real Name="Z">-14.660433461517535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.2075827327598105</Real>
+      <Real Name="Y">15.43297843791283</Real>
+      <Real Name="Z">-5.7962964074414813</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.9841489443781199</Real>
+      <Real Name="Y">19.277789185526686</Real>
+      <Real Name="Z">-6.2162135910797787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.2312090471230732</Real>
+      <Real Name="Y">-7.7022802804644996</Real>
+      <Real Name="Z">4.5336116064264615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.1441412301523286</Real>
+      <Real Name="Y">-22.890385127522528</Real>
+      <Real Name="Z">3.8413811747584532</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.87064472990949</Real>
+      <Real Name="Y">-59.133290854386829</Real>
+      <Real Name="Z">-670.26269286045738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-396.02211549585331</Real>
+      <Real Name="Y">36.171974358918938</Real>
+      <Real Name="Z">385.31047202600769</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.6108380692463413</Real>
+      <Real Name="Y">-11.414581838927019</Real>
+      <Real Name="Z">206.51899743613592</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">359.08579491733713</Real>
+      <Real Name="Y">404.16668315281674</Real>
+      <Real Name="Z">-112.62010323843887</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-34.417420693301651</Real>
+      <Real Name="Y">-230.07641431869285</Real>
+      <Real Name="Z">93.074494854988671</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.9662914048069</Real>
+      <Real Name="Y">-578.23339208632694</Real>
+      <Real Name="Z">256.85887054999881</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">650.07453088237764</Real>
+      <Real Name="Y">-499.18991248214741</Real>
+      <Real Name="Z">-425.14023720092428</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.62937082152888</Real>
+      <Real Name="Y">124.84721473435984</Real>
+      <Real Name="Z">76.514003578131252</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-649.70293972131367</Real>
+      <Real Name="Y">443.28003729615745</Real>
+      <Real Name="Z">221.66619494346477</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.780282145279454</Real>
+      <Real Name="Y">282.62462678501481</Real>
+      <Real Name="Z">55.015134298323773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.357666926066173</Real>
+      <Real Name="Y">-158.4395737938782</Real>
+      <Real Name="Z">-105.95357925437992</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.21489426747121</Real>
+      <Real Name="Y">-209.83136391603426</Real>
+      <Real Name="Z">-134.28935129876157</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-590.19216596834485</Real>
+      <Real Name="Y">249.39420413575147</Real>
+      <Real Name="Z">846.56721528494631</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">396.16613178031832</Real>
+      <Real Name="Y">-41.720031378786331</Real>
+      <Real Name="Z">-873.43508296051607</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">174.59613470119513</Real>
+      <Real Name="Y">-183.38934048261828</Real>
+      <Real Name="Z">-65.695971380702744</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1102.7766555107692</Real>
+      <Real Name="Y">569.10994855510887</Real>
+      <Real Name="Z">881.84856349989832</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.20990184477338</Real>
+      <Real Name="Y">-842.7814031648719</Real>
+      <Real Name="Z">-367.55219669216501</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-842.85295732650866</Real>
+      <Real Name="Y">326.81303925023565</Real>
+      <Real Name="Z">-591.10453402083795</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">365.26405537734104</Real>
+      <Real Name="Y">749.39587784508922</Real>
+      <Real Name="Z">138.22807349698184</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.043159354460641</Real>
+      <Real Name="Y">-647.12827990691596</Real>
+      <Real Name="Z">-49.255778265871818</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-94.231938580458262</Real>
+      <Real Name="Y">-129.66282482199921</Real>
+      <Real Name="Z">-34.17604385208687</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.840136770904991</Real>
+      <Real Name="Y">-161.75831011392282</Real>
+      <Real Name="Z">-904.26087237619515</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.23093515160498</Real>
+      <Real Name="Y">41.079198578946809</Real>
+      <Real Name="Z">266.30557004573399</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">384.25480213057796</Real>
+      <Real Name="Y">-7.361796734664118</Real>
+      <Real Name="Z">682.93955528877598</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">121.4024189716839</Real>
+      <Real Name="Y">49.978363747851688</Real>
+      <Real Name="Z">-862.31016789681041</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-263.69385848324839</Real>
+      <Real Name="Y">160.94141815534095</Real>
+      <Real Name="Z">484.39237873144776</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">9.5567914635048723</Real>
+      <Real Name="Y">-196.69603201430377</Real>
+      <Real Name="Z">167.59836090490961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-639.63852605914133</Real>
+      <Real Name="Y">69.0197223171877</Real>
+      <Real Name="Z">365.01670271266539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">546.63896546837032</Real>
+      <Real Name="Y">-57.676908826908772</Real>
+      <Real Name="Z">-10.671688148830679</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.84251475290461</Real>
+      <Real Name="Y">-27.192756179106382</Real>
+      <Real Name="Z">-246.20876894680356</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-100.70241006337409</Real>
+      <Real Name="Y">36.043716772199076</Real>
+      <Real Name="Z">360.7868237578204</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">244.54082092311617</Real>
+      <Real Name="Y">-4.8417196040946067</Real>
+      <Real Name="Z">-127.42955578591724</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">56.342714736828476</Real>
+      <Real Name="Y">-113.62138432409198</Real>
+      <Real Name="Z">-114.04264606171147</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-338.25164741137019</Real>
+      <Real Name="Y">-2032.7714410367862</Real>
+      <Real Name="Z">-703.48859759356571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">332.59031976782114</Real>
+      <Real Name="Y">1309.0555454474725</Real>
+      <Real Name="Z">324.71451780424928</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">274.43577913261726</Real>
+      <Real Name="Y">405.14083971170544</Real>
+      <Real Name="Z">72.871768277207565</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">444.82413464997319</Real>
+      <Real Name="Y">1024.4483752979231</Real>
+      <Real Name="Z">-839.75004470955832</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.34314287198492</Real>
+      <Real Name="Y">-543.42128977486971</Real>
+      <Real Name="Z">228.31351113253004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-286.54086737229085</Real>
+      <Real Name="Y">-412.26870447092176</Real>
+      <Real Name="Z">553.85556341741369</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">51.170949004185317</Real>
+      <Real Name="Y">94.107181308326375</Real>
+      <Real Name="Z">1.8888341191848355</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.8091322932281457</Real>
+      <Real Name="Y">-41.414991991188685</Real>
+      <Real Name="Z">-1.8277098718926297</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.258621441439406</Real>
+      <Real Name="Y">-30.903676040053085</Real>
+      <Real Name="Z">10.073764971216793</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.89976036139325</Real>
+      <Real Name="Y">975.09125957393996</Real>
+      <Real Name="Z">405.99573491979504</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">612.23440077940063</Real>
+      <Real Name="Y">-687.55381457311762</Real>
+      <Real Name="Z">-457.86982337013154</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">39.639587097937117</Real>
+      <Real Name="Y">-155.08219915979885</Real>
+      <Real Name="Z">-20.219171525837929</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">245.15579212652059</Real>
+      <Real Name="Y">-469.84691826050727</Real>
+      <Real Name="Z">-1398.091869714649</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.319934623917533</Real>
+      <Real Name="Y">340.41623511387002</Real>
+      <Real Name="Z">1038.1434531758346</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-373.03421300112558</Real>
+      <Real Name="Y">23.998761495962725</Real>
+      <Real Name="Z">222.23101220865775</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.82804259514347</Real>
+      <Real Name="Y">-125.39296846437639</Real>
+      <Real Name="Z">-119.56787210502098</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.638347983839893</Real>
+      <Real Name="Y">126.63300211484767</Real>
+      <Real Name="Z">74.149329600623375</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-63.871630990079851</Real>
+      <Real Name="Y">41.512094757447159</Real>
+      <Real Name="Z">151.88657526865458</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">330.09496088407212</Real>
+      <Real Name="Y">-340.53530727715912</Real>
+      <Real Name="Z">-69.699985795076145</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.47853969208876</Real>
+      <Real Name="Y">415.31674327016583</Real>
+      <Real Name="Z">94.90186953306322</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.4351400061786</Real>
+      <Real Name="Y">68.482885727330569</Real>
+      <Real Name="Z">38.340615111124706</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">796.45817190124342</Real>
+      <Real Name="Y">-867.4354607273408</Real>
+      <Real Name="Z">-491.38198034149951</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-355.24816874642954</Real>
+      <Real Name="Y">324.91775133422664</Real>
+      <Real Name="Z">569.44159118399921</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-357.68136049644784</Real>
+      <Real Name="Y">862.52102688285402</Real>
+      <Real Name="Z">114.71741582134786</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.8469795125673159</Real>
+      <Real Name="Y">-8.8891679189191564</Real>
+      <Real Name="Z">3.4020465637764588</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.46401218284297485</Real>
+      <Real Name="Y">5.805223486931169</Real>
+      <Real Name="Z">-0.43183214044072926</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.620526876109075</Real>
+      <Real Name="Y">3.7740044804390607</Real>
+      <Real Name="Z">-3.8263168472768925</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-388.64818861959429</Real>
+      <Real Name="Y">864.46173679984076</Real>
+      <Real Name="Z">241.60050612704723</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">284.96616870201751</Real>
+      <Real Name="Y">-486.15223668791799</Real>
+      <Real Name="Z">-677.66055376392057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-319.14378579140219</Real>
+      <Real Name="Y">-446.48337730322504</Real>
+      <Real Name="Z">300.51997941838493</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.90834415341078</Real>
+      <Real Name="Y">25.464994559346504</Real>
+      <Real Name="Z">-150.63468120442286</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.06679320270814</Real>
+      <Real Name="Y">61.210441998383402</Real>
+      <Real Name="Z">39.572364795407609</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.584065463470751</Real>
+      <Real Name="Y">-35.130231066195492</Real>
+      <Real Name="Z">73.54832515632927</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-532.5547119272187</Real>
+      <Real Name="Y">-1287.4266144582382</Real>
+      <Real Name="Z">-326.60081414902464</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">190.56330920315742</Real>
+      <Real Name="Y">620.62213514197026</Real>
+      <Real Name="Z">-282.01938811786459</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.54238895806475</Real>
+      <Real Name="Y">654.19447094351472</Real>
+      <Real Name="Z">629.88316407336731</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1196.5435157066352</Real>
+      <Real Name="Y">443.23816867525153</Real>
+      <Real Name="Z">-794.15295657978083</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">820.78712695722754</Real>
+      <Real Name="Y">-311.40386385703624</Real>
+      <Real Name="Z">572.94694292425004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.53029922047915</Real>
+      <Real Name="Y">-31.918272466149173</Real>
+      <Real Name="Z">40.225628120813042</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">220.80069328265</Real>
+      <Real Name="Y">426.47674863576606</Real>
+      <Real Name="Z">166.91868029107846</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.688960780150051</Real>
+      <Real Name="Y">-132.65828778401135</Real>
+      <Real Name="Z">-72.78415380848881</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-110.54602194276842</Real>
+      <Real Name="Y">-122.81860793305621</Real>
+      <Real Name="Z">-18.197823837437547</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.8588691716673</Real>
+      <Real Name="Y">-1077.1702635438457</Real>
+      <Real Name="Z">-856.85079142093571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.2146088170002</Real>
+      <Real Name="Y">210.81412220105963</Real>
+      <Real Name="Z">194.85024675602637</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">576.33526821526129</Real>
+      <Real Name="Y">692.7116684217367</Real>
+      <Real Name="Z">680.67437035958892</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.82164160831681</Real>
+      <Real Name="Y">385.5300055163741</Real>
+      <Real Name="Z">-1021.2007193399551</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.929591319519773</Real>
+      <Real Name="Y">-315.53358468245159</Real>
+      <Real Name="Z">752.41601691575931</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.987575611341107</Real>
+      <Real Name="Y">34.174609359270057</Real>
+      <Real Name="Z">253.83989914786724</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.6363893334501</Real>
+      <Real Name="Y">-515.03241543508329</Real>
+      <Real Name="Z">-927.7465526110874</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.86982714997052</Real>
+      <Real Name="Y">80.804707408898025</Real>
+      <Real Name="Z">145.1923515505456</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.646619517292521</Real>
+      <Real Name="Y">258.37967644349203</Real>
+      <Real Name="Z">803.24053316201218</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-563.25560176241663</Real>
+      <Real Name="Y">-97.636989695653796</Real>
+      <Real Name="Z">-137.89491306140602</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">384.1588231189952</Real>
+      <Real Name="Y">-86.690509663389932</Real>
+      <Real Name="Z">72.182818694473468</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">249.70572209218884</Real>
+      <Real Name="Y">26.184528726006931</Real>
+      <Real Name="Z">92.113585416055571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">602.62885462681766</Real>
+      <Real Name="Y">834.70991015144159</Real>
+      <Real Name="Z">-264.96429000207002</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-127.28081553658942</Real>
+      <Real Name="Y">-205.22998883457399</Real>
+      <Real Name="Z">15.862610232191969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-469.48528722585525</Real>
+      <Real Name="Y">-874.80276172207175</Real>
+      <Real Name="Z">-39.326613086042826</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1620.0278624153771</Real>
+      <Real Name="Y">-215.45963134789611</Real>
+      <Real Name="Z">826.48665766986869</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-828.2430440406971</Real>
+      <Real Name="Y">378.34925050782186</Real>
+      <Real Name="Z">-612.61121333934625</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-235.54464141227169</Real>
+      <Real Name="Y">-151.28981441915386</Real>
+      <Real Name="Z">-333.93866562470146</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.36602376061506</Real>
+      <Real Name="Y">54.837674386965226</Real>
+      <Real Name="Z">125.73314300122468</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.84251453400262</Real>
+      <Real Name="Y">-162.27315924033854</Real>
+      <Real Name="Z">29.681109843758399</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">199.78666524908786</Real>
+      <Real Name="Y">-7.0598525521722078</Real>
+      <Real Name="Z">24.994894302613929</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">819.86408085618132</Real>
+      <Real Name="Y">-433.5570477401501</Real>
+      <Real Name="Z">-61.049355598357245</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-563.82018759538767</Real>
+      <Real Name="Y">320.68271903878809</Real>
+      <Real Name="Z">65.781993364532042</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-438.63777057407367</Real>
+      <Real Name="Y">415.33371231174539</Real>
+      <Real Name="Z">-106.02226892579071</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.80855621434054</Real>
+      <Real Name="Y">-1826.0918605286099</Real>
+      <Real Name="Z">30.864931681790182</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">130.8215108258089</Real>
+      <Real Name="Y">244.33777906117948</Real>
+      <Real Name="Z">-31.603245122590028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.80052084631785</Real>
+      <Real Name="Y">1231.0638578258872</Real>
+      <Real Name="Z">-56.150735110623629</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">444.71822033570265</Real>
+      <Real Name="Y">1039.0778849015653</Real>
+      <Real Name="Z">875.43831809345033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-316.32572116854351</Real>
+      <Real Name="Y">-644.35867040904736</Real>
+      <Real Name="Z">-597.62398162994862</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-61.119026796049866</Real>
+      <Real Name="Y">-239.92668606779426</Real>
+      <Real Name="Z">-234.54045008670539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.60429964451578</Real>
+      <Real Name="Y">-356.75848074018359</Real>
+      <Real Name="Z">-437.82345235172124</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-254.14954321630924</Real>
+      <Real Name="Y">352.25815000600176</Real>
+      <Real Name="Z">252.35498747457288</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.1123600048642</Real>
+      <Real Name="Y">139.91166021684242</Real>
+      <Real Name="Z">494.03863217198227</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">734.00302241802717</Real>
+      <Real Name="Y">-329.26686364592098</Real>
+      <Real Name="Z">-216.82577158287188</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-135.59850391514649</Real>
+      <Real Name="Y">454.66613222480373</Real>
+      <Real Name="Z">-8.4051727701447305</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-729.81024371973467</Real>
+      <Real Name="Y">-149.13864402724744</Real>
+      <Real Name="Z">169.20240072328397</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1101.7918039220315</Real>
+      <Real Name="Y">1490.8150870507029</Real>
+      <Real Name="Z">-676.02054936036188</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.91159846649563</Real>
+      <Real Name="Y">-207.40566825193088</Real>
+      <Real Name="Z">94.613789382388831</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">630.96981227514766</Real>
+      <Real Name="Y">-958.14948334969313</Real>
+      <Real Name="Z">444.97882850303068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">744.72925465988897</Real>
+      <Real Name="Y">19.087582186126596</Real>
+      <Real Name="Z">141.17647947063887</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.20645681823626</Real>
+      <Real Name="Y">97.165281813446512</Real>
+      <Real Name="Z">-33.396087927071733</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-916.78932361755369</Real>
+      <Real Name="Y">125.11235397401278</Real>
+      <Real Name="Z">-124.6134300372624</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-782.60825327530574</Real>
+      <Real Name="Y">-1260.8697151304505</Real>
+      <Real Name="Z">103.52679437655507</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">695.71450100922425</Real>
+      <Real Name="Y">961.59161044268922</Real>
+      <Real Name="Z">64.14751860129077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.193845320428721</Real>
+      <Real Name="Y">262.62560761991182</Real>
+      <Real Name="Z">-73.878069683348926</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.12047523501889</Real>
+      <Real Name="Y">-687.41714700842294</Real>
+      <Real Name="Z">815.01183320354346</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.29571627599975</Real>
+      <Real Name="Y">501.02447140738497</Real>
+      <Real Name="Z">-584.47946607466281</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-55.166392632957127</Real>
+      <Real Name="Y">74.3653147096623</Real>
+      <Real Name="Z">-119.77147012555125</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">63.644586752882532</Real>
+      <Real Name="Y">-117.68734575096221</Real>
+      <Real Name="Z">283.99231285331604</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.981186863523781</Real>
+      <Real Name="Y">248.23730798601073</Real>
+      <Real Name="Z">-50.688756643922417</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.86043432975396</Real>
+      <Real Name="Y">354.6899417096588</Real>
+      <Real Name="Z">61.467005381327638</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">85.829788453610007</Real>
+      <Real Name="Y">487.71734353366969</Real>
+      <Real Name="Z">-76.984652011052489</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.223277742726133</Real>
+      <Real Name="Y">-181.17121629838869</Real>
+      <Real Name="Z">69.197622612854303</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">29.321290550842292</Real>
+      <Real Name="Y">-193.3974683831091</Real>
+      <Real Name="Z">30.131736347959894</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">82.019862775671271</Real>
+      <Real Name="Y">773.69175139918696</Real>
+      <Real Name="Z">72.968295424138603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.041324699944191</Real>
+      <Real Name="Y">-170.12681447626284</Real>
+      <Real Name="Z">-65.02601038783007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-70.856996002135034</Real>
+      <Real Name="Y">-729.27256554574808</Real>
+      <Real Name="Z">-10.792637674116431</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.279206630012908</Real>
+      <Real Name="Y">-726.13974232486032</Real>
+      <Real Name="Z">-204.97138135692097</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.01608859762553</Real>
+      <Real Name="Y">627.70462166910966</Real>
+      <Real Name="Z">109.59385789552326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.085013694016595</Real>
+      <Real Name="Y">178.7113107185142</Real>
+      <Real Name="Z">41.241894196809682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-55.392494604161925</Real>
+      <Real Name="Y">898.7551105292016</Real>
+      <Real Name="Z">723.7041408514657</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">75.322500806807582</Real>
+      <Real Name="Y">-165.60404054435747</Real>
+      <Real Name="Z">-174.96873097686671</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">170.40934950716232</Real>
+      <Real Name="Y">-685.98694056131308</Real>
+      <Real Name="Z">-509.84541444330756</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-287.38995825974507</Real>
+      <Real Name="Y">317.65946252445571</Real>
+      <Real Name="Z">-417.03441113364045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.86288329613473</Real>
+      <Real Name="Y">-261.68004320411774</Real>
+      <Real Name="Z">437.62711652896155</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.79834949095869</Real>
+      <Real Name="Y">-124.24080127820056</Real>
+      <Real Name="Z">88.911590198283534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-879.32183538017557</Real>
+      <Real Name="Y">-115.07131957479666</Real>
+      <Real Name="Z">-61.031622324079592</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.70179632819929</Real>
+      <Real Name="Y">-53.022234247226201</Real>
+      <Real Name="Z">6.7644305627259413</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">310.87380285562892</Real>
+      <Real Name="Y">55.322182222114087</Real>
+      <Real Name="Z">7.078653076604013</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1514.207556878732</Real>
+      <Real Name="Y">-279.21080693451455</Real>
+      <Real Name="Z">-661.54603455784206</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">291.38063893325756</Real>
+      <Real Name="Y">-39.185620551299621</Real>
+      <Real Name="Z">211.91340698961687</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1065.5133843356498</Real>
+      <Real Name="Y">346.20728881341881</Real>
+      <Real Name="Z">337.10830942610079</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.36522585063494</Real>
+      <Real Name="Y">1468.5195319979093</Real>
+      <Real Name="Z">329.46284047448319</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.2953210015833001</Real>
+      <Real Name="Y">-745.29971598581358</Real>
+      <Real Name="Z">-585.06276867430608</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">365.44011039039498</Real>
+      <Real Name="Y">-471.62213080075702</Real>
+      <Real Name="Z">486.09923514847389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">202.49945777896576</Real>
+      <Real Name="Y">435.46511199192832</Real>
+      <Real Name="Z">127.56404419175399</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-67.853112544247026</Real>
+      <Real Name="Y">-122.33837686484307</Real>
+      <Real Name="Z">-82.67532187726502</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.90235961736099</Real>
+      <Real Name="Y">-493.67771682994356</Real>
+      <Real Name="Z">-109.64146567940142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1166.0328110951323</Real>
+      <Real Name="Y">-2044.1047733660228</Real>
+      <Real Name="Z">660.11291226317451</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-132.71930467474701</Real>
+      <Real Name="Y">1257.2082563876957</Real>
+      <Real Name="Z">324.11646134941947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1029.0412725766007</Real>
+      <Real Name="Y">404.56419119000901</Real>
+      <Real Name="Z">-796.89409519552737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">791.57113672650644</Real>
+      <Real Name="Y">-1315.8502137580797</Real>
+      <Real Name="Z">-301.19561579047371</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-250.38938666335423</Real>
+      <Real Name="Y">689.69947200386162</Real>
+      <Real Name="Z">27.984292198128319</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-503.78531668658604</Real>
+      <Real Name="Y">709.33151266549078</Real>
+      <Real Name="Z">325.7642616531088</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">71.337883915046433</Real>
+      <Real Name="Y">804.25935078190548</Real>
+      <Real Name="Z">1317.2119792921069</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.43286478155539</Real>
+      <Real Name="Y">-711.0880474017456</Real>
+      <Real Name="Z">-1002.9364951759326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-102.17204480140933</Real>
+      <Real Name="Y">26.345234270970668</Real>
+      <Real Name="Z">-236.93621001815399</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.020000 Step 20 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-1184.6083952815834</Real>
+      <Real Name="Y">403.0878764632555</Real>
+      <Real Name="Z">1238.5318031785603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.659053286726206</Real>
+      <Real Name="Y">-102.66194809051458</Real>
+      <Real Name="Z">-94.955818108071853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">401.47859222171871</Real>
+      <Real Name="Y">5.9104078915414444</Real>
+      <Real Name="Z">-242.16632046032723</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">362.81227612770817</Real>
+      <Real Name="Y">-487.12377041173079</Real>
+      <Real Name="Z">-592.99565446535985</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1277.9757737737809</Real>
+      <Real Name="Y">273.29028579588748</Real>
+      <Real Name="Z">-522.85761672553565</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-945.04838855063065</Real>
+      <Real Name="Y">-316.9324530272346</Real>
+      <Real Name="Z">-43.041779606890842</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-329.7619053159919</Real>
+      <Real Name="Y">556.02205403708945</Real>
+      <Real Name="Z">208.88791410998257</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">337.11624693261871</Real>
+      <Real Name="Y">-106.49105932688582</Real>
+      <Real Name="Z">-42.262757261755205</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.938291163617649</Real>
+      <Real Name="Y">-359.85912071430766</Real>
+      <Real Name="Z">-47.082132825220768</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.12944586799039826</Real>
+      <Real Name="Y">1.0216389411832871</Real>
+      <Real Name="Z">19.494512292010697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.8952742008970489</Real>
+      <Real Name="Y">8.3758492569011675</Real>
+      <Real Name="Z">-4.3502465349946178</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.9733169867459708</Real>
+      <Real Name="Y">2.0946500233736618</Real>
+      <Real Name="Z">-10.492661399759875</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.514242888202006</Real>
+      <Real Name="Y">5.7783102772427029</Real>
+      <Real Name="Z">-26.036020400989685</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.0181274412695842</Real>
+      <Real Name="Y">-1.7810437718992702</Real>
+      <Real Name="Z">12.731254498956829</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.418519064543943</Real>
+      <Real Name="Y">-12.822442245420959</Real>
+      <Real Name="Z">11.245605792504142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">286.49742765951191</Real>
+      <Real Name="Y">-118.49170805010222</Real>
+      <Real Name="Z">-750.5122334603393</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.38484637886972</Real>
+      <Real Name="Y">202.14598632094641</Real>
+      <Real Name="Z">405.80208224359558</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.49040279084867</Real>
+      <Real Name="Y">9.8595954770648007</Real>
+      <Real Name="Z">174.85217803181416</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">412.53754736675432</Real>
+      <Real Name="Y">622.62494531699815</Real>
+      <Real Name="Z">-228.21776996830243</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.17712618736897</Real>
+      <Real Name="Y">-250.27284344661038</Real>
+      <Real Name="Z">85.873086006788441</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-372.03498819824841</Real>
+      <Real Name="Y">-582.93779866444049</Real>
+      <Real Name="Z">306.18673823348269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">436.18190111484944</Real>
+      <Real Name="Y">-423.672408869121</Real>
+      <Real Name="Z">-482.73223203265729</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-78.497661063660843</Real>
+      <Real Name="Y">146.51608768400962</Real>
+      <Real Name="Z">125.28001931048539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-451.92946479271461</Real>
+      <Real Name="Y">416.38458430649621</Real>
+      <Real Name="Z">461.08466993444506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">121.40582980708649</Real>
+      <Real Name="Y">227.3515065246763</Real>
+      <Real Name="Z">276.63730857004771</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.409656520825779</Real>
+      <Real Name="Y">-161.96359415056742</Real>
+      <Real Name="Z">-132.00029158945722</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-184.33609734261665</Real>
+      <Real Name="Y">-240.54463778406694</Real>
+      <Real Name="Z">-141.74498166466489</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-419.00415112164671</Real>
+      <Real Name="Y">393.00978418091358</Real>
+      <Real Name="Z">887.82609625039038</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">400.32283791478199</Real>
+      <Real Name="Y">-249.68427194372134</Real>
+      <Real Name="Z">-829.3859476014261</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">152.01134742941483</Real>
+      <Real Name="Y">-216.81534131813544</Real>
+      <Real Name="Z">-117.94338720578594</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1230.2145431083645</Real>
+      <Real Name="Y">304.79426363785495</Real>
+      <Real Name="Z">848.67686548235918</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">48.72981007373577</Real>
+      <Real Name="Y">-533.55457251131372</Real>
+      <Real Name="Z">-196.95495238558036</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-970.81872352953712</Real>
+      <Real Name="Y">522.6942450451005</Real>
+      <Real Name="Z">-550.80242547524301</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">254.33525873720197</Real>
+      <Real Name="Y">1007.6673870298308</Real>
+      <Real Name="Z">169.73789891974877</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-81.531887617729808</Real>
+      <Real Name="Y">-374.58869985393238</Real>
+      <Real Name="Z">-48.420934646155388</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.08043204438199</Real>
+      <Real Name="Y">-179.30284286963916</Real>
+      <Real Name="Z">-53.425485441654118</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.891078836135108</Real>
+      <Real Name="Y">10.262282040993959</Real>
+      <Real Name="Z">-1001.2356143551303</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-138.42288920557002</Real>
+      <Real Name="Y">277.93538102178326</Real>
+      <Real Name="Z">319.89610643741196</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">286.79715917336512</Real>
+      <Real Name="Y">137.38299976598995</Real>
+      <Real Name="Z">300.45998202846135</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">495.78094626759338</Real>
+      <Real Name="Y">214.53887848593817</Real>
+      <Real Name="Z">-393.47973041018997</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-481.73023253981415</Real>
+      <Real Name="Y">71.786139432957867</Real>
+      <Real Name="Z">659.66061980545146</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-181.84274104560504</Real>
+      <Real Name="Y">-231.22233479807539</Real>
+      <Real Name="Z">49.177605035850917</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-964.88532367727282</Real>
+      <Real Name="Y">178.13609299041107</Real>
+      <Real Name="Z">262.91128803062315</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">578.05772724796259</Real>
+      <Real Name="Y">-312.15631375318492</Real>
+      <Real Name="Z">-163.89985684602411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">173.39330154041929</Real>
+      <Real Name="Y">4.6078561439152441</Real>
+      <Real Name="Z">-175.03106256612128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-423.4360027729341</Real>
+      <Real Name="Y">77.528429456078783</Real>
+      <Real Name="Z">261.94954984516789</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">143.1289365037415</Real>
+      <Real Name="Y">-1.6686381054302473</Real>
+      <Real Name="Z">-50.858443277575603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.32495637553686</Real>
+      <Real Name="Y">-191.90220365563897</Real>
+      <Real Name="Z">-95.393812517919898</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-166.10162204003774</Real>
+      <Real Name="Y">-1439.8104422732345</Real>
+      <Real Name="Z">-662.84070198311031</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">39.464705612249986</Real>
+      <Real Name="Y">1349.5567679671544</Real>
+      <Real Name="Z">457.22270516585536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">355.41082219876887</Real>
+      <Real Name="Y">364.27616128923063</Real>
+      <Real Name="Z">113.38241797457493</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">453.99420253824633</Real>
+      <Real Name="Y">768.49736016189445</Real>
+      <Real Name="Z">-905.63498523106705</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.75054243611157</Real>
+      <Real Name="Y">-461.23478253620874</Real>
+      <Real Name="Z">34.604810282948009</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-436.42381651180989</Real>
+      <Real Name="Y">-178.80214956054664</Real>
+      <Real Name="Z">321.4572675779205</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.417104511422167</Real>
+      <Real Name="Y">125.72363428844884</Real>
+      <Real Name="Z">32.547282647481595</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-15.973780589938109</Real>
+      <Real Name="Y">-51.281672030464406</Real>
+      <Real Name="Z">-12.913214830423982</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-31.127750397599399</Real>
+      <Real Name="Y">-41.912073146648027</Real>
+      <Real Name="Z">-11.363574445047696</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.74345847099335</Real>
+      <Real Name="Y">913.8160971517708</Real>
+      <Real Name="Z">244.29277281159489</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">448.09694265153774</Real>
+      <Real Name="Y">-698.51038290337169</Real>
+      <Real Name="Z">-410.56190938572519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.76866910257084</Real>
+      <Real Name="Y">-157.27188804390514</Real>
+      <Real Name="Z">-28.531347747885484</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">619.68787119399815</Real>
+      <Real Name="Y">-695.83490996346984</Real>
+      <Real Name="Z">-1247.201438359064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-389.19095489660549</Real>
+      <Real Name="Y">288.56159429218178</Real>
+      <Real Name="Z">972.93930107485426</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-470.57258871370522</Real>
+      <Real Name="Y">207.72926547537313</Real>
+      <Real Name="Z">55.209312884775656</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">42.944749182425397</Real>
+      <Real Name="Y">-260.37572949967648</Real>
+      <Real Name="Z">21.729377241409935</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.611708252017351</Real>
+      <Real Name="Y">113.52937797493412</Real>
+      <Real Name="Z">35.45155348366761</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-38.030831909453404</Real>
+      <Real Name="Y">65.682356004304182</Real>
+      <Real Name="Z">130.11583652629287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">334.46658356389378</Real>
+      <Real Name="Y">-307.47166747884415</Real>
+      <Real Name="Z">-97.257200737129978</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-311.94113007891804</Real>
+      <Real Name="Y">377.55141084383854</Real>
+      <Real Name="Z">156.96546210280567</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-88.09608577472342</Real>
+      <Real Name="Y">46.126273146009765</Real>
+      <Real Name="Z">53.098513567887807</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">776.83002590976241</Real>
+      <Real Name="Y">-1152.8750884992883</Real>
+      <Real Name="Z">-593.44678667993901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-451.49359664550582</Real>
+      <Real Name="Y">278.16253660796622</Real>
+      <Real Name="Z">717.62232042834751</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-144.23366220179443</Real>
+      <Real Name="Y">792.59897980839526</Real>
+      <Real Name="Z">173.6970397413865</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.7593989420869534</Real>
+      <Real Name="Y">-11.764944502918482</Real>
+      <Real Name="Z">25.143118685173391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.4528406977217045</Real>
+      <Real Name="Y">6.1570153394572422</Real>
+      <Real Name="Z">-9.2904579466071056</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.99381606969008374</Real>
+      <Real Name="Y">6.3285649818789551</Real>
+      <Real Name="Z">-14.573616963723829</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-165.29885759335156</Real>
+      <Real Name="Y">810.82428419363919</Real>
+      <Real Name="Z">106.52389439223688</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">480.57331085506542</Real>
+      <Real Name="Y">-385.40832164366719</Real>
+      <Real Name="Z">-510.74923421957857</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-206.77571532966081</Real>
+      <Real Name="Y">-360.72633609807929</Real>
+      <Real Name="Z">315.54962522535402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">339.40413582255997</Real>
+      <Real Name="Y">42.437726820782608</Real>
+      <Real Name="Z">-320.2875726961077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-306.66145296111063</Real>
+      <Real Name="Y">47.67871901899732</Real>
+      <Real Name="Z">84.352555654674916</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-80.06315602770465</Real>
+      <Real Name="Y">-17.510679103857541</Real>
+      <Real Name="Z">103.84655075854724</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-398.96548819981263</Real>
+      <Real Name="Y">-1117.3436194748137</Real>
+      <Real Name="Z">-519.3473112885921</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">154.63232190338988</Real>
+      <Real Name="Y">317.10066478072758</Real>
+      <Real Name="Z">-148.84243685315789</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">406.76699261207091</Real>
+      <Real Name="Y">693.37110599493121</Real>
+      <Real Name="Z">675.69074951203822</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-854.92104832342955</Real>
+      <Real Name="Y">326.88693669523894</Real>
+      <Real Name="Z">-506.17148568066904</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">678.3516546692756</Real>
+      <Real Name="Y">-269.59593038033938</Real>
+      <Real Name="Z">485.19725819256536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">170.63340124922632</Real>
+      <Real Name="Y">-1.8335470199485826</Real>
+      <Real Name="Z">33.31549021269862</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">254.62029890632473</Real>
+      <Real Name="Y">426.69579451565085</Real>
+      <Real Name="Z">78.930061853452187</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.633285793254174</Real>
+      <Real Name="Y">-153.65143919403425</Real>
+      <Real Name="Z">-62.929555065570632</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.82679248058456</Real>
+      <Real Name="Y">-151.48995459193591</Real>
+      <Real Name="Z">11.276782994241081</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-817.43087777087419</Real>
+      <Real Name="Y">-792.79263807492146</Real>
+      <Real Name="Z">-518.30556425673615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">148.90464245231797</Real>
+      <Real Name="Y">199.4494583633043</Real>
+      <Real Name="Z">153.27410262983011</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">706.11076300963691</Real>
+      <Real Name="Y">518.59163381361441</Real>
+      <Real Name="Z">393.87596945763499</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.306939738971522</Real>
+      <Real Name="Y">-77.05450183487531</Real>
+      <Real Name="Z">-778.05013352802325</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.784137734098735</Real>
+      <Real Name="Y">-193.39709808671242</Real>
+      <Real Name="Z">727.6649082460608</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.281151862432708</Real>
+      <Real Name="Y">57.381628896808898</Real>
+      <Real Name="Z">153.0378422924714</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-539.64172499716346</Real>
+      <Real Name="Y">-517.22978684086684</Real>
+      <Real Name="Z">-920.96320877692483</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">124.62072040660652</Real>
+      <Real Name="Y">76.836898408369805</Real>
+      <Real Name="Z">159.93834561510064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">248.50345398577602</Real>
+      <Real Name="Y">494.45956880811508</Real>
+      <Real Name="Z">736.61436081242539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-507.89829885777868</Real>
+      <Real Name="Y">-377.521723086186</Real>
+      <Real Name="Z">-18.605488247413451</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">519.3201374252659</Real>
+      <Real Name="Y">128.01894253443032</Real>
+      <Real Name="Z">-81.306145804465132</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">232.30289281012804</Real>
+      <Real Name="Y">100.40158927731079</Real>
+      <Real Name="Z">-11.223366544172059</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">162.71492089038333</Real>
+      <Real Name="Y">782.41377237988581</Real>
+      <Real Name="Z">75.332447172315398</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.728144917678982</Real>
+      <Real Name="Y">-165.56579377696539</Real>
+      <Real Name="Z">-23.189795087757645</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-197.90886540218918</Real>
+      <Real Name="Y">-818.25214765245221</Real>
+      <Real Name="Z">-145.76036160645248</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1771.4679676267381</Real>
+      <Real Name="Y">-212.88521445805199</Real>
+      <Real Name="Z">732.39971942991735</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1040.3685517381955</Real>
+      <Real Name="Y">287.5190518816089</Real>
+      <Real Name="Z">-580.94419574034316</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-376.68628062561464</Real>
+      <Real Name="Y">-185.23254094471892</Real>
+      <Real Name="Z">-311.93279201371183</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-304.62494390263328</Real>
+      <Real Name="Y">70.531430230290724</Real>
+      <Real Name="Z">-102.50131899519613</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.32902921950412</Real>
+      <Real Name="Y">-134.92969747318341</Real>
+      <Real Name="Z">52.561884432456097</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.21638803137859</Real>
+      <Real Name="Y">-5.3115604251547204</Real>
+      <Real Name="Z">37.976364735494762</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">922.628211569717</Real>
+      <Real Name="Y">-236.56469550352392</Real>
+      <Real Name="Z">25.783940492337692</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-731.16750501815852</Real>
+      <Real Name="Y">275.06705928293979</Real>
+      <Real Name="Z">98.048661926078069</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-426.24486308253233</Real>
+      <Real Name="Y">280.76878705278756</Real>
+      <Real Name="Z">-54.044081518410351</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-197.6428621403588</Real>
+      <Real Name="Y">-1131.4415859173307</Real>
+      <Real Name="Z">-4.9007986716224394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.032731969889824</Real>
+      <Real Name="Y">173.89934414698837</Real>
+      <Real Name="Z">-34.518570845008107</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.09748529762385</Real>
+      <Real Name="Y">1122.7040033755873</Real>
+      <Real Name="Z">35.365563833579039</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">213.96666665297232</Real>
+      <Real Name="Y">1106.9786744431519</Real>
+      <Real Name="Z">788.70595619560822</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-176.84969933109892</Real>
+      <Real Name="Y">-676.97163402950014</Real>
+      <Real Name="Z">-647.49561078432475</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.319995353528057</Real>
+      <Real Name="Y">-292.56849923635832</Real>
+      <Real Name="Z">-141.5811477466458</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">123.56492405999373</Real>
+      <Real Name="Y">-299.8761292898962</Real>
+      <Real Name="Z">-289.04068295243644</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-147.45289684819829</Real>
+      <Real Name="Y">358.09256722026367</Real>
+      <Real Name="Z">-2.6409740012061675</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">117.6327678825226</Real>
+      <Real Name="Y">37.172248800729719</Real>
+      <Real Name="Z">310.65290364059155</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">716.32115978495347</Real>
+      <Real Name="Y">-440.7555598253511</Real>
+      <Real Name="Z">-181.19065573861144</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-336.05357404288861</Real>
+      <Real Name="Y">525.01907384480796</Real>
+      <Real Name="Z">51.779273999240466</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-603.10535744797221</Real>
+      <Real Name="Y">-77.733121988685511</Real>
+      <Real Name="Z">203.43134764534435</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-973.32016241602162</Real>
+      <Real Name="Y">1154.9852767178704</Real>
+      <Real Name="Z">-650.30297663348722</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.35220032131778</Real>
+      <Real Name="Y">-167.58255554453496</Real>
+      <Real Name="Z">98.477944225098895</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">565.47172356332214</Real>
+      <Real Name="Y">-824.11895242707169</Real>
+      <Real Name="Z">522.69872634626165</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">940.08586694587223</Real>
+      <Real Name="Y">-126.2636254750137</Real>
+      <Real Name="Z">49.729682992252322</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.80033738018923</Real>
+      <Real Name="Y">90.884354361746233</Real>
+      <Real Name="Z">-30.196371811979368</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-892.1293330646007</Real>
+      <Real Name="Y">-62.281272464604243</Real>
+      <Real Name="Z">-96.447566918332029</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-570.87300025154184</Real>
+      <Real Name="Y">-696.02584870028329</Real>
+      <Real Name="Z">124.37864312580911</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">590.7066901145763</Real>
+      <Real Name="Y">698.52710996369137</Real>
+      <Real Name="Z">26.012175726003697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">28.546646488435314</Real>
+      <Real Name="Y">126.95536045795514</Real>
+      <Real Name="Z">-78.852969285108685</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-278.51080661199671</Real>
+      <Real Name="Y">-1097.0830102712944</Real>
+      <Real Name="Z">1203.4623499879003</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">316.13698858297755</Real>
+      <Real Name="Y">619.13869620558353</Real>
+      <Real Name="Z">-557.29965936769054</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-73.887861609772827</Real>
+      <Real Name="Y">139.55551889766312</Real>
+      <Real Name="Z">-194.49892113086383</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">398.96867223910056</Real>
+      <Real Name="Y">-490.2658479264087</Real>
+      <Real Name="Z">-0.78797134492731402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-94.345254240416324</Real>
+      <Real Name="Y">267.46727394552761</Real>
+      <Real Name="Z">-51.322748697497936</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-417.32808528404951</Real>
+      <Real Name="Y">341.49926338936007</Real>
+      <Real Name="Z">-29.649814541943947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.10930474925385</Real>
+      <Real Name="Y">443.33570763593951</Real>
+      <Real Name="Z">-5.7679762093616382</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-40.89775953611354</Real>
+      <Real Name="Y">-215.44412005177605</Real>
+      <Real Name="Z">40.705554199958584</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">39.53952827497762</Real>
+      <Real Name="Y">-135.68795966836302</Real>
+      <Real Name="Z">4.7480849396957012</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">11.297999177373775</Real>
+      <Real Name="Y">777.70998837916068</Real>
+      <Real Name="Z">70.151767023409988</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.788242999725117</Real>
+      <Real Name="Y">-182.27511568064094</Real>
+      <Real Name="Z">-62.507679826872923</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.88411557230816</Real>
+      <Real Name="Y">-674.16432688477028</Real>
+      <Real Name="Z">-45.656073469369588</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.40385898806787</Real>
+      <Real Name="Y">-680.78883072413339</Real>
+      <Real Name="Z">-272.59576013210693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">210.17077421403602</Real>
+      <Real Name="Y">598.44388545916104</Real>
+      <Real Name="Z">155.674981018111</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">94.199984899909438</Real>
+      <Real Name="Y">151.90843315568944</Real>
+      <Real Name="Z">38.751016796326695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.283594857406712</Real>
+      <Real Name="Y">951.55150875583411</Real>
+      <Real Name="Z">773.36451860082514</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">48.132262816770115</Real>
+      <Real Name="Y">-185.17695569731077</Real>
+      <Real Name="Z">-178.98353790080259</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-131.70403401411733</Real>
+      <Real Name="Y">-690.35076661614232</Real>
+      <Real Name="Z">-499.62436976168817</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-222.62612322251653</Real>
+      <Real Name="Y">179.07952912336009</Real>
+      <Real Name="Z">-349.6922368512395</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">110.31057020694607</Real>
+      <Real Name="Y">-102.66026628990696</Real>
+      <Real Name="Z">348.47654082803547</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">126.30058906043632</Real>
+      <Real Name="Y">-63.570590334506413</Real>
+      <Real Name="Z">111.58393763916899</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-990.70101098018506</Real>
+      <Real Name="Y">4.7057387363395833</Real>
+      <Real Name="Z">-55.335388573486654</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">722.31019119876578</Real>
+      <Real Name="Y">-129.59885484017866</Real>
+      <Real Name="Z">-19.514295572242595</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.42415260528759</Real>
+      <Real Name="Y">4.8105537366082771</Real>
+      <Real Name="Z">6.0017028947895028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1553.4543633171211</Real>
+      <Real Name="Y">-896.94113728361413</Real>
+      <Real Name="Z">-472.01000584365727</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">337.68087450465185</Real>
+      <Real Name="Y">68.116731299032324</Real>
+      <Real Name="Z">139.89508145241948</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1182.4494961742585</Real>
+      <Real Name="Y">215.43826624920672</Real>
+      <Real Name="Z">333.09318107945171</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.28680285749155843</Real>
+      <Real Name="Y">1445.0058079096659</Real>
+      <Real Name="Z">65.835813040235408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.631155113815367</Real>
+      <Real Name="Y">-809.54587108842338</Real>
+      <Real Name="Z">-429.01532236311289</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">169.61043373954055</Real>
+      <Real Name="Y">-622.4744305739224</Real>
+      <Real Name="Z">581.51041839529807</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">245.83074887665907</Real>
+      <Real Name="Y">507.43268392918219</Real>
+      <Real Name="Z">163.90229950260343</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.942706096248386</Real>
+      <Real Name="Y">-131.09973161443443</Real>
+      <Real Name="Z">-107.37953375131607</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-260.07549221695365</Real>
+      <Real Name="Y">-514.63318308549924</Real>
+      <Real Name="Z">-78.337938167411508</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">757.9468593093369</Real>
+      <Real Name="Y">-1668.3556411162426</Real>
+      <Real Name="Z">822.14488275254496</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.465224079030548</Real>
+      <Real Name="Y">1168.8124115623239</Real>
+      <Real Name="Z">115.60052128492758</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-941.74617791442336</Real>
+      <Real Name="Y">558.12759819384246</Real>
+      <Real Name="Z">-732.20273620281466</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">799.19396017966096</Real>
+      <Real Name="Y">-1986.3345844478649</Real>
+      <Real Name="Z">-664.37239204233924</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.466269713547206</Real>
+      <Real Name="Y">843.59862607687205</Real>
+      <Real Name="Z">211.27206638029816</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-546.88054614997088</Real>
+      <Real Name="Y">681.72620723535977</Real>
+      <Real Name="Z">429.52667831977453</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">98.008350115911838</Real>
+      <Real Name="Y">729.74183921110205</Real>
+      <Real Name="Z">1184.5261159719385</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-108.74176270784919</Real>
+      <Real Name="Y">-587.28523476850785</Real>
+      <Real Name="Z">-1008.6548166100885</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-55.186167624251574</Real>
+      <Real Name="Y">-70.164840259260814</Real>
+      <Real Name="Z">-264.81905528275144</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_coul_s.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_coul_s.xml
new file mode 100644 (file)
index 0000000..89daacc
--- /dev/null
@@ -0,0 +1,961 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVvdw/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">29.43363</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">30.416641</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">31.413523</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">32.401215</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">33.351364</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">34.230022</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">34.999779</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">35.622658</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">36.06641</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">36.309753</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">36.342499</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">36.169155</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">35.807827</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">35.286404</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">34.641403</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">33.910927</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">33.133469</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">32.342255</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">31.56241</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">30.810646</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">30.092226</Real>
+  </Energy>
+  <Energy Name="dVcoul/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-77.872757</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-77.918587</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-78.123314</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-78.493698</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-79.030884</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-79.736992</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-80.609894</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-81.641418</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-82.811539</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-84.088631</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-85.423309</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-86.759109</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-88.036163</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-89.202881</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-90.223831</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-91.08606</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-91.802109</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-92.405746</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-92.950943</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-93.500961</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-94.111801</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1525.2646</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1525.0984</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1523.1953</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1519.5933</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1514.5776</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1508.6312</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1502.3276</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1496.2202</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1490.7045</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1485.9755</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1482.0021</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1478.6196</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1475.6003</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1472.7715</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1470.088</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1467.6455</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1465.6481</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1464.3367</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1463.9037</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1464.4622</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1465.9945</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">599.66528</Real>
+      <Real Name="Y">-789.44562</Real>
+      <Real Name="Z">-85.737778</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-165.11247</Real>
+      <Real Name="Y">-249.46057</Real>
+      <Real Name="Z">33.569336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-450.1994</Real>
+      <Real Name="Y">276.42099</Real>
+      <Real Name="Z">-221.56969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.37119</Real>
+      <Real Name="Y">187.52614</Real>
+      <Real Name="Z">30.099119</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">914.44623</Real>
+      <Real Name="Y">783.73859</Real>
+      <Real Name="Z">-20.484112</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-105.63852</Real>
+      <Real Name="Y">193.12509</Real>
+      <Real Name="Z">-234.33603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1202.8019</Real>
+      <Real Name="Y">-103.90425</Real>
+      <Real Name="Z">940.77008</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">192.58644</Real>
+      <Real Name="Y">-154.03302</Real>
+      <Real Name="Z">-462.12759</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">282.3826</Real>
+      <Real Name="Y">-299.03049</Real>
+      <Real Name="Z">-66.356911</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.1401443</Real>
+      <Real Name="Y">-33.490257</Real>
+      <Real Name="Z">24.554802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.7333183</Real>
+      <Real Name="Y">29.402895</Real>
+      <Real Name="Z">-14.659874</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.2076035</Real>
+      <Real Name="Y">15.433332</Real>
+      <Real Name="Z">-5.7955589</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.984848</Real>
+      <Real Name="Y">19.279953</Real>
+      <Real Name="Z">-6.2148895</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.230835</Real>
+      <Real Name="Y">-7.7033081</Real>
+      <Real Name="Z">4.5330448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.1444435</Real>
+      <Real Name="Y">-22.891417</Real>
+      <Real Name="Z">3.8408356</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.86951</Real>
+      <Real Name="Y">-59.13237</Real>
+      <Real Name="Z">-670.26263</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-396.02078</Real>
+      <Real Name="Y">36.171539</Real>
+      <Real Name="Z">385.30988</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.6104279</Real>
+      <Real Name="Y">-11.415237</Real>
+      <Real Name="Z">206.51918</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">359.0834</Real>
+      <Real Name="Y">404.16077</Real>
+      <Real Name="Z">-112.61637</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-34.417107</Real>
+      <Real Name="Y">-230.0759</Real>
+      <Real Name="Z">93.073967</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.96454</Real>
+      <Real Name="Y">-578.23145</Real>
+      <Real Name="Z">256.85809</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">650.07117</Real>
+      <Real Name="Y">-499.1936</Real>
+      <Real Name="Z">-425.14249</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.62727</Real>
+      <Real Name="Y">124.84856</Real>
+      <Real Name="Z">76.514702</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-649.7002</Real>
+      <Real Name="Y">443.28278</Real>
+      <Real Name="Z">221.66734</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.781006</Real>
+      <Real Name="Y">282.62637</Real>
+      <Real Name="Z">55.019501</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.358116</Real>
+      <Real Name="Y">-158.43953</Real>
+      <Real Name="Z">-105.95421</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.21526</Real>
+      <Real Name="Y">-209.83156</Real>
+      <Real Name="Z">-134.28979</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-590.19458</Real>
+      <Real Name="Y">249.39679</Real>
+      <Real Name="Z">846.56866</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">396.16833</Real>
+      <Real Name="Y">-41.720047</Real>
+      <Real Name="Z">-873.43469</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">174.59697</Real>
+      <Real Name="Y">-183.39058</Real>
+      <Real Name="Z">-65.696205</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1102.7775</Real>
+      <Real Name="Y">569.10339</Real>
+      <Real Name="Z">881.85455</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.20837</Real>
+      <Real Name="Y">-842.78052</Real>
+      <Real Name="Z">-367.55264</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-842.85242</Real>
+      <Real Name="Y">326.81381</Real>
+      <Real Name="Z">-591.1051</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">365.26489</Real>
+      <Real Name="Y">749.39349</Real>
+      <Real Name="Z">138.22836</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.044109</Real>
+      <Real Name="Y">-647.12854</Real>
+      <Real Name="Z">-49.255638</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-94.231606</Real>
+      <Real Name="Y">-129.66402</Real>
+      <Real Name="Z">-34.176052</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.838226</Real>
+      <Real Name="Y">-161.75587</Real>
+      <Real Name="Z">-904.25763</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.23129</Real>
+      <Real Name="Y">41.078434</Real>
+      <Real Name="Z">266.30493</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">384.25595</Real>
+      <Real Name="Y">-7.3632507</Real>
+      <Real Name="Z">682.93738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">121.4017</Real>
+      <Real Name="Y">49.980804</Real>
+      <Real Name="Z">-862.30389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-263.69293</Real>
+      <Real Name="Y">160.94006</Real>
+      <Real Name="Z">484.38959</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">9.5569305</Real>
+      <Real Name="Y">-196.69731</Real>
+      <Real Name="Z">167.59607</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-639.63373</Real>
+      <Real Name="Y">69.017532</Real>
+      <Real Name="Z">365.01788</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">546.63702</Real>
+      <Real Name="Y">-57.677101</Real>
+      <Real Name="Z">-10.672101</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.84175</Real>
+      <Real Name="Y">-27.191811</Real>
+      <Real Name="Z">-246.2094</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-100.70122</Real>
+      <Real Name="Y">36.045456</Real>
+      <Real Name="Z">360.78549</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">244.54025</Real>
+      <Real Name="Y">-4.8426361</Real>
+      <Real Name="Z">-127.42924</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">56.342514</Real>
+      <Real Name="Y">-113.62152</Real>
+      <Real Name="Z">-114.04127</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-338.24884</Real>
+      <Real Name="Y">-2032.7753</Real>
+      <Real Name="Z">-703.48566</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">332.58862</Real>
+      <Real Name="Y">1309.0573</Real>
+      <Real Name="Z">324.7132</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">274.43466</Real>
+      <Real Name="Y">405.14215</Real>
+      <Real Name="Z">72.871353</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">444.82458</Real>
+      <Real Name="Y">1024.4498</Real>
+      <Real Name="Z">-839.75104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.34293</Real>
+      <Real Name="Y">-543.42224</Real>
+      <Real Name="Z">228.31209</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-286.54129</Real>
+      <Real Name="Y">-412.26746</Real>
+      <Real Name="Z">553.85535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">51.171143</Real>
+      <Real Name="Y">94.10833</Real>
+      <Real Name="Z">1.8887901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.809124</Real>
+      <Real Name="Y">-41.415657</Real>
+      <Real Name="Z">-1.8277617</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.258768</Real>
+      <Real Name="Y">-30.904259</Real>
+      <Real Name="Z">10.073853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.90063</Real>
+      <Real Name="Y">975.09003</Real>
+      <Real Name="Z">405.9978</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">612.23328</Real>
+      <Real Name="Y">-687.55365</Real>
+      <Real Name="Z">-457.87106</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">39.639423</Real>
+      <Real Name="Y">-155.08127</Real>
+      <Real Name="Z">-20.219311</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">245.15596</Real>
+      <Real Name="Y">-469.85022</Real>
+      <Real Name="Z">-1398.0934</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.322693</Real>
+      <Real Name="Y">340.41776</Real>
+      <Real Name="Z">1038.1437</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-373.03137</Real>
+      <Real Name="Y">24.000935</Real>
+      <Real Name="Z">222.23291</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.82599</Real>
+      <Real Name="Y">-125.39177</Real>
+      <Real Name="Z">-119.56694</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.637161</Real>
+      <Real Name="Y">126.63292</Real>
+      <Real Name="Z">74.149368</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-63.870872</Real>
+      <Real Name="Y">41.511715</Real>
+      <Real Name="Z">151.88623</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">330.09677</Real>
+      <Real Name="Y">-340.53506</Real>
+      <Real Name="Z">-69.700348</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.47961</Real>
+      <Real Name="Y">415.31592</Real>
+      <Real Name="Z">94.901894</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.43569</Real>
+      <Real Name="Y">68.48262</Real>
+      <Real Name="Z">38.340366</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">796.4538</Real>
+      <Real Name="Y">-867.43793</Real>
+      <Real Name="Z">-491.37979</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-355.24631</Real>
+      <Real Name="Y">324.9202</Real>
+      <Real Name="Z">569.43988</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-357.68066</Real>
+      <Real Name="Y">862.52191</Real>
+      <Real Name="Z">114.71693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.8463516</Real>
+      <Real Name="Y">-8.8901978</Real>
+      <Real Name="Z">3.4024582</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.46365166</Real>
+      <Real Name="Y">5.8056717</Real>
+      <Real Name="Z">-0.43207932</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.620285</Real>
+      <Real Name="Y">3.7745323</Real>
+      <Real Name="Z">-3.8265266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-388.64661</Real>
+      <Real Name="Y">864.46527</Real>
+      <Real Name="Z">241.60117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">284.96515</Real>
+      <Real Name="Y">-486.15344</Real>
+      <Real Name="Z">-677.65973</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-319.14352</Real>
+      <Real Name="Y">-446.4856</Real>
+      <Real Name="Z">300.52011</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.90872</Real>
+      <Real Name="Y">25.464417</Real>
+      <Real Name="Z">-150.63272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.06683</Real>
+      <Real Name="Y">61.210426</Real>
+      <Real Name="Z">39.571358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.584198</Real>
+      <Real Name="Y">-35.130154</Real>
+      <Real Name="Z">73.548088</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-532.55835</Real>
+      <Real Name="Y">-1287.4309</Real>
+      <Real Name="Z">-326.60446</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">190.56363</Real>
+      <Real Name="Y">620.62494</Real>
+      <Real Name="Z">-282.01801</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.54196</Real>
+      <Real Name="Y">654.19672</Real>
+      <Real Name="Z">629.88391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1196.5449</Real>
+      <Real Name="Y">443.23883</Real>
+      <Real Name="Z">-794.1532</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">820.78809</Real>
+      <Real Name="Y">-311.40421</Real>
+      <Real Name="Z">572.94653</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.53062</Real>
+      <Real Name="Y">-31.918495</Real>
+      <Real Name="Z">40.225952</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">220.80016</Real>
+      <Real Name="Y">426.47736</Real>
+      <Real Name="Z">166.91711</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.688135</Real>
+      <Real Name="Y">-132.65866</Real>
+      <Real Name="Z">-72.784142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-110.54634</Real>
+      <Real Name="Y">-122.819</Real>
+      <Real Name="Z">-18.196966</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.8549</Real>
+      <Real Name="Y">-1077.1697</Real>
+      <Real Name="Z">-856.84955</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.21437</Real>
+      <Real Name="Y">210.81461</Real>
+      <Real Name="Z">194.85037</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">576.33301</Real>
+      <Real Name="Y">692.71161</Real>
+      <Real Name="Z">680.67407</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.82358</Real>
+      <Real Name="Y">385.53003</Real>
+      <Real Name="Z">-1021.1998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.929909</Real>
+      <Real Name="Y">-315.53308</Real>
+      <Real Name="Z">752.41522</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.986607</Real>
+      <Real Name="Y">34.175095</Real>
+      <Real Name="Z">253.84048</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.63455</Real>
+      <Real Name="Y">-515.03473</Real>
+      <Real Name="Z">-927.74969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.86819</Real>
+      <Real Name="Y">80.805466</Real>
+      <Real Name="Z">145.19298</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.64436</Real>
+      <Real Name="Y">258.38016</Real>
+      <Real Name="Z">803.24005</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-563.25543</Real>
+      <Real Name="Y">-97.633316</Real>
+      <Real Name="Z">-137.89397</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">384.15817</Real>
+      <Real Name="Y">-86.692322</Real>
+      <Real Name="Z">72.182175</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">249.70477</Real>
+      <Real Name="Y">26.183556</Real>
+      <Real Name="Z">92.113327</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">602.62756</Real>
+      <Real Name="Y">834.71289</Real>
+      <Real Name="Z">-264.96277</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-127.28015</Real>
+      <Real Name="Y">-205.22997</Real>
+      <Real Name="Z">15.862991</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-469.48383</Real>
+      <Real Name="Y">-874.80377</Real>
+      <Real Name="Z">-39.327316</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1620.0297</Real>
+      <Real Name="Y">-215.46436</Real>
+      <Real Name="Z">826.48676</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-828.24255</Real>
+      <Real Name="Y">378.35068</Real>
+      <Real Name="Z">-612.61035</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-235.54477</Real>
+      <Real Name="Y">-151.28896</Real>
+      <Real Name="Z">-333.93884</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.36661</Real>
+      <Real Name="Y">54.838631</Real>
+      <Real Name="Z">125.73298</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.84279</Real>
+      <Real Name="Y">-162.27307</Real>
+      <Real Name="Z">29.681534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">199.78683</Real>
+      <Real Name="Y">-7.0608482</Real>
+      <Real Name="Z">24.995243</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">819.8606</Real>
+      <Real Name="Y">-433.56052</Real>
+      <Real Name="Z">-61.051918</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-563.81854</Real>
+      <Real Name="Y">320.68457</Real>
+      <Real Name="Z">65.782906</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-438.63693</Real>
+      <Real Name="Y">415.3363</Real>
+      <Real Name="Z">-106.02238</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.81209</Real>
+      <Real Name="Y">-1826.0874</Real>
+      <Real Name="Z">30.866051</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">130.82208</Real>
+      <Real Name="Y">244.3372</Real>
+      <Real Name="Z">-31.60387</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.79926</Real>
+      <Real Name="Y">1231.0607</Real>
+      <Real Name="Z">-56.151333</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">444.7149</Real>
+      <Real Name="Y">1039.0757</Real>
+      <Real Name="Z">875.4375</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-316.3251</Real>
+      <Real Name="Y">-644.35712</Real>
+      <Real Name="Z">-597.62427</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-61.118347</Real>
+      <Real Name="Y">-239.92633</Real>
+      <Real Name="Z">-234.54068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.60233</Real>
+      <Real Name="Y">-356.75443</Real>
+      <Real Name="Z">-437.8269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-254.14807</Real>
+      <Real Name="Y">352.2558</Real>
+      <Real Name="Z">252.35645</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.10988</Real>
+      <Real Name="Y">139.90904</Real>
+      <Real Name="Z">494.03998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">733.99982</Real>
+      <Real Name="Y">-329.26593</Real>
+      <Real Name="Z">-216.83014</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-135.59769</Real>
+      <Real Name="Y">454.66699</Real>
+      <Real Name="Z">-8.4051304</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-729.81018</Real>
+      <Real Name="Y">-149.13763</Real>
+      <Real Name="Z">169.20285</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1101.7931</Real>
+      <Real Name="Y">1490.8157</Real>
+      <Real Name="Z">-676.02142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.91183</Real>
+      <Real Name="Y">-207.40578</Real>
+      <Real Name="Z">94.613853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">630.97168</Real>
+      <Real Name="Y">-958.14746</Real>
+      <Real Name="Z">444.9769</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">744.73083</Real>
+      <Real Name="Y">19.087006</Real>
+      <Real Name="Z">141.17676</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.20728</Real>
+      <Real Name="Y">97.164536</Real>
+      <Real Name="Z">-33.396427</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-916.78821</Real>
+      <Real Name="Y">125.11245</Real>
+      <Real Name="Z">-124.61278</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-782.60638</Real>
+      <Real Name="Y">-1260.8704</Real>
+      <Real Name="Z">103.52603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">695.71466</Real>
+      <Real Name="Y">961.59021</Real>
+      <Real Name="Z">64.14856</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.193283</Real>
+      <Real Name="Y">262.62622</Real>
+      <Real Name="Z">-73.877975</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.11838</Real>
+      <Real Name="Y">-687.42004</Real>
+      <Real Name="Z">815.01263</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.29356</Real>
+      <Real Name="Y">501.02615</Real>
+      <Real Name="Z">-584.47968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-55.167038</Real>
+      <Real Name="Y">74.365921</Real>
+      <Real Name="Z">-119.77187</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">63.648689</Real>
+      <Real Name="Y">-117.68619</Real>
+      <Real Name="Z">283.99329</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.982372</Real>
+      <Real Name="Y">248.23776</Real>
+      <Real Name="Z">-50.688805</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.86185</Real>
+      <Real Name="Y">354.68918</Real>
+      <Real Name="Z">61.466476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">85.829468</Real>
+      <Real Name="Y">487.71689</Real>
+      <Real Name="Z">-76.985275</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.223026</Real>
+      <Real Name="Y">-181.17096</Real>
+      <Real Name="Z">69.19838</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">29.322023</Real>
+      <Real Name="Y">-193.39767</Real>
+      <Real Name="Z">30.1318</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">82.020027</Real>
+      <Real Name="Y">773.6925</Real>
+      <Real Name="Z">72.968086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.041302</Real>
+      <Real Name="Y">-170.12675</Real>
+      <Real Name="Z">-65.025864</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-70.857193</Real>
+      <Real Name="Y">-729.27319</Real>
+      <Real Name="Z">-10.792496</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.279152</Real>
+      <Real Name="Y">-726.14093</Real>
+      <Real Name="Z">-204.97104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.01529</Real>
+      <Real Name="Y">627.7052</Real>
+      <Real Name="Z">109.59398</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.084702</Real>
+      <Real Name="Y">178.71202</Real>
+      <Real Name="Z">41.241814</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-55.39518</Real>
+      <Real Name="Y">898.74902</Real>
+      <Real Name="Z">723.70233</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">75.323074</Real>
+      <Real Name="Y">-165.60365</Real>
+      <Real Name="Z">-174.96848</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">170.40924</Real>
+      <Real Name="Y">-685.98511</Real>
+      <Real Name="Z">-509.84503</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-287.3905</Real>
+      <Real Name="Y">317.66013</Real>
+      <Real Name="Z">-417.0358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.86263</Real>
+      <Real Name="Y">-261.68091</Real>
+      <Real Name="Z">437.6268</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.79877</Real>
+      <Real Name="Y">-124.24107</Real>
+      <Real Name="Z">88.912071</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-879.32556</Real>
+      <Real Name="Y">-115.06973</Real>
+      <Real Name="Z">-61.033722</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.70117</Real>
+      <Real Name="Y">-53.022957</Real>
+      <Real Name="Z">6.7651672</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">310.87463</Real>
+      <Real Name="Y">55.321651</Real>
+      <Real Name="Z">7.0785828</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1514.2024</Real>
+      <Real Name="Y">-279.20386</Real>
+      <Real Name="Z">-661.54224</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">291.3811</Real>
+      <Real Name="Y">-39.18541</Real>
+      <Real Name="Z">211.91287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1065.5118</Real>
+      <Real Name="Y">346.20602</Real>
+      <Real Name="Z">337.10712</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.3623</Real>
+      <Real Name="Y">1468.5234</Real>
+      <Real Name="Z">329.46335</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.2939692</Real>
+      <Real Name="Y">-745.30011</Real>
+      <Real Name="Z">-585.06238</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">365.43927</Real>
+      <Real Name="Y">-471.62317</Real>
+      <Real Name="Z">486.09906</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">202.49811</Real>
+      <Real Name="Y">435.46371</Real>
+      <Real Name="Z">127.56297</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-67.852455</Real>
+      <Real Name="Y">-122.33794</Real>
+      <Real Name="Z">-82.674789</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.90147</Real>
+      <Real Name="Y">-493.6763</Real>
+      <Real Name="Z">-109.64038</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1166.0264</Real>
+      <Real Name="Y">-2044.1102</Real>
+      <Real Name="Z">660.10229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-132.72098</Real>
+      <Real Name="Y">1257.2104</Real>
+      <Real Name="Z">324.11588</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1029.0393</Real>
+      <Real Name="Y">404.56741</Real>
+      <Real Name="Z">-796.89166</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">791.56354</Real>
+      <Real Name="Y">-1315.8544</Real>
+      <Real Name="Z">-301.19534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-250.3862</Real>
+      <Real Name="Y">689.70142</Real>
+      <Real Name="Z">27.984131</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-503.78079</Real>
+      <Real Name="Y">709.33191</Real>
+      <Real Name="Z">325.76355</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">71.341377</Real>
+      <Real Name="Y">804.25159</Real>
+      <Real Name="Z">1317.2124</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.43126</Real>
+      <Real Name="Y">-711.08521</Real>
+      <Real Name="Z">-1002.9359</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-102.1713</Real>
+      <Real Name="Y">26.346628</Real>
+      <Real Name="Z">-236.93565</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_vdw_d.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_vdw_d.xml
new file mode 100644 (file)
index 0000000..6a51e3a
--- /dev/null
@@ -0,0 +1,1849 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVvdw/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-7.2474133975080894</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-7.0423185340332566</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-6.8322697931983516</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-6.622751028022499</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-6.4189106758545771</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-6.2274333231504837</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-6.0562438038587105</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-5.9136619627519389</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-5.8072600382440154</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-5.7427327709996412</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-5.7230368535198135</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-5.747926655653389</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-5.8139159726892551</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-5.9150637140935665</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-6.0419944262342105</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-6.1853211786448039</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-6.3349527601906361</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-6.4814221598863586</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-6.6162371771406034</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-6.7341386032752428</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-6.8309551871941947</Real>
+  </Energy>
+  <Energy Name="dVcoul/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-77.8723566401097</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-77.711012001234735</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-77.492250690806941</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-77.223788930799998</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-76.92491278155066</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-76.629116727890903</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-76.3818040591011</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-76.231984705519125</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-76.21987385298425</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-76.364760653914942</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-76.657603978066106</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-77.060540493328602</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-77.512923367539287</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-77.942033603515682</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-78.276167670454242</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-78.457802813319603</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-78.454395238772634</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-78.264252790040217</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-77.915430446875618</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-77.457059694213513</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-76.944895839121145</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1465.6824123113265</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1465.6704323763943</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1463.7472661883646</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1459.8908397262849</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1454.3286967671422</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1447.5109981353921</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1440.0462119761876</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1432.5884755269585</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1425.7001621025679</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1419.7445561955328</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1414.8537474909911</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1410.9766159168028</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1407.9762316897663</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1405.7322517170321</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1404.2061242607365</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1403.4493995728233</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1403.5526694032958</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1404.5673108049409</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1406.4388637093459</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1408.986184562069</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1411.9316178765794</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">633.8831907282846</Real>
+      <Real Name="Y">-773.73944595150226</Real>
+      <Real Name="Z">-86.059591160334378</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-173.46731556093474</Real>
+      <Real Name="Y">-254.11888495307664</Real>
+      <Real Name="Z">31.397869491014376</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-460.76501830912116</Real>
+      <Real Name="Y">273.63247505151912</Real>
+      <Real Name="Z">-218.70907884498195</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-124.67788966717067</Real>
+      <Real Name="Y">188.88487825397664</Real>
+      <Real Name="Z">21.257665770753611</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">744.90394728041974</Real>
+      <Real Name="Y">615.74166358912441</Real>
+      <Real Name="Z">-32.889248111150501</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.813564324649853</Real>
+      <Real Name="Y">330.25254637860229</Real>
+      <Real Name="Z">-154.8953460121906</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-846.96529188380964</Real>
+      <Real Name="Y">354.03634339639132</Real>
+      <Real Name="Z">1107.6673029861554</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-40.535521099933845</Real>
+      <Real Name="Y">-295.73418901352028</Real>
+      <Real Name="Z">-453.53486241339243</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">275.31895981939721</Real>
+      <Real Name="Y">-437.95383381694387</Real>
+      <Real Name="Z">-229.88298380654226</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">10.926011005091937</Real>
+      <Real Name="Y">-30.975674760252105</Real>
+      <Real Name="Z">20.381590937576163</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.0138701254607909</Real>
+      <Real Name="Y">27.856316975829493</Real>
+      <Real Name="Z">-12.695918110904778</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.2674211619326883</Real>
+      <Real Name="Y">14.475177408717883</Real>
+      <Real Name="Z">-4.0467350436091323</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.22538190930259816</Real>
+      <Real Name="Y">23.69679661203174</Real>
+      <Real Name="Z">-7.1117981649562125</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.8462065976892745</Real>
+      <Real Name="Y">-9.5171155293956566</Real>
+      <Real Name="Z">4.7696350465498654</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.3453596266597003</Real>
+      <Real Name="Y">-24.902907360873492</Real>
+      <Real Name="Z">4.5991521089141614</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.96295187530052</Real>
+      <Real Name="Y">-59.605635568030849</Real>
+      <Real Name="Z">-670.15721785214919</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.81279122705769</Real>
+      <Real Name="Y">36.20469042291834</Real>
+      <Real Name="Z">385.08925762579611</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.8215464435196651</Real>
+      <Real Name="Y">-11.298418225397187</Real>
+      <Real Name="Z">206.28479303022127</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">394.35470607345832</Real>
+      <Real Name="Y">432.30803454757972</Real>
+      <Real Name="Z">-97.792777800132214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.85200933759662</Real>
+      <Real Name="Y">-238.51162915060058</Real>
+      <Real Name="Z">85.782972880969737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-370.93129038488684</Real>
+      <Real Name="Y">-600.14277237904264</Real>
+      <Real Name="Z">242.79153355662427</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">643.13454308172754</Real>
+      <Real Name="Y">-492.2755956328503</Real>
+      <Real Name="Z">-420.98535893572955</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.97384167216792</Real>
+      <Real Name="Y">121.35582381662744</Real>
+      <Real Name="Z">75.022959572071613</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.06277071622355</Real>
+      <Real Name="Y">438.33123420494866</Real>
+      <Real Name="Z">218.98937252178408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.026151986322798</Real>
+      <Real Name="Y">295.71912343694339</Real>
+      <Real Name="Z">69.878283185313336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.252333884320294</Real>
+      <Real Name="Y">-163.91312109375451</Real>
+      <Real Name="Z">-112.35407810341539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.12078254896801</Real>
+      <Real Name="Y">-215.13020707664643</Real>
+      <Real Name="Z">-140.17665530901047</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-591.92793713914398</Real>
+      <Real Name="Y">297.47231779947958</Real>
+      <Real Name="Z">826.39431962506217</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">397.93639892947715</Real>
+      <Real Name="Y">-70.314953968874306</Real>
+      <Real Name="Z">-868.24762476380488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.87258573868061</Real>
+      <Real Name="Y">-199.27650127733096</Real>
+      <Real Name="Z">-58.263933884350259</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1114.872282398017</Real>
+      <Real Name="Y">629.37746128075889</Real>
+      <Real Name="Z">795.97290119327931</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.54644283236559</Real>
+      <Real Name="Y">-873.28714057360503</Real>
+      <Real Name="Z">-339.25254967654172</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-870.47703909844574</Real>
+      <Real Name="Y">286.55402923641446</Real>
+      <Real Name="Z">-544.52466870103444</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">357.06165810383635</Real>
+      <Real Name="Y">756.97747171028197</Real>
+      <Real Name="Z">146.16694346497687</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.1031911259369593</Real>
+      <Real Name="Y">-651.95807639425993</Real>
+      <Real Name="Z">-54.851563453446659</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-90.346160228676467</Real>
+      <Real Name="Y">-131.84844724474854</Real>
+      <Real Name="Z">-37.247322365720336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.064197944013795</Real>
+      <Real Name="Y">-139.34794301185769</Real>
+      <Real Name="Z">-869.22184148273175</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.014249513270272</Real>
+      <Real Name="Y">7.3692522628366071</Real>
+      <Real Name="Z">237.5634519065969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">409.19492757353737</Real>
+      <Real Name="Y">-13.742542210541785</Real>
+      <Real Name="Z">674.31104622319117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.803564202261356</Real>
+      <Real Name="Y">49.904231840779801</Real>
+      <Real Name="Z">-863.79020796780287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-246.26232236317969</Real>
+      <Real Name="Y">161.94006229271525</Real>
+      <Real Name="Z">488.15427934452742</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">20.568406030418696</Real>
+      <Real Name="Y">-198.6892899661438</Real>
+      <Real Name="Z">167.75248290904577</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-614.6713159502674</Real>
+      <Real Name="Y">104.43279901190823</Real>
+      <Real Name="Z">354.05139285459956</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">537.6262257717641</Real>
+      <Real Name="Y">-78.613538027351382</Real>
+      <Real Name="Z">-4.2479072666175819</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.13435916894088</Real>
+      <Real Name="Y">-43.663731879204583</Real>
+      <Real Name="Z">-245.23655340698801</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-106.55377501777602</Real>
+      <Real Name="Y">29.609788591987467</Real>
+      <Real Name="Z">363.04297804747847</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">246.89597123723604</Real>
+      <Real Name="Y">-1.2874127968376285</Real>
+      <Real Name="Z">-127.71291982473038</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">59.181669587130301</Real>
+      <Real Name="Y">-111.70268840757583</Real>
+      <Real Name="Z">-115.61203661582745</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-356.0952727291866</Real>
+      <Real Name="Y">-2083.620423280739</Real>
+      <Real Name="Z">-694.72816876457114</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">338.53999378384719</Real>
+      <Real Name="Y">1335.4212804188292</Real>
+      <Real Name="Z">310.34795082844755</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">276.91145975674965</Real>
+      <Real Name="Y">426.71413088577714</Real>
+      <Real Name="Z">68.866527233272805</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">409.91825107144814</Real>
+      <Real Name="Y">978.05623519357584</Real>
+      <Real Name="Z">-851.61639115879404</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-141.87811546680328</Real>
+      <Real Name="Y">-531.63312668379092</Real>
+      <Real Name="Z">230.46019394509702</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-250.61603960596403</Real>
+      <Real Name="Y">-386.7770679612899</Real>
+      <Real Name="Z">568.34530693562169</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.995587717934299</Real>
+      <Real Name="Y">98.712841095074324</Real>
+      <Real Name="Z">5.9983147624240729</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.455554648307631</Real>
+      <Real Name="Y">-43.457716027921336</Real>
+      <Real Name="Z">-3.6230677277036509</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.238894805797361</Real>
+      <Real Name="Y">-33.183328509177713</Real>
+      <Real Name="Z">8.290184048624738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-423.92366366087379</Real>
+      <Real Name="Y">988.95013163207</Real>
+      <Real Name="Z">389.62350355164324</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">598.97332901184529</Real>
+      <Real Name="Y">-698.11664446353973</Real>
+      <Real Name="Z">-450.10730846376248</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.34364562827345</Real>
+      <Real Name="Y">-160.39853837320439</Real>
+      <Real Name="Z">-11.909701289496965</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">157.71052465818451</Real>
+      <Real Name="Y">-504.38369107289623</Real>
+      <Real Name="Z">-1441.915964648316</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.961575921876957</Real>
+      <Real Name="Y">370.46015000533043</Real>
+      <Real Name="Z">1053.1544367911799</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-283.3786389780056</Real>
+      <Real Name="Y">14.028334421804722</Real>
+      <Real Name="Z">269.58718254533261</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.54500817120885</Real>
+      <Real Name="Y">-124.32060349534872</Real>
+      <Real Name="Z">-117.58242863217779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-85.882129825154834</Real>
+      <Real Name="Y">126.24593710256312</Real>
+      <Real Name="Z">72.835100905771725</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.93886218419312</Real>
+      <Real Name="Y">40.618534773196188</Real>
+      <Real Name="Z">150.70574056399448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.19566872099546</Real>
+      <Real Name="Y">-338.35445076270076</Real>
+      <Real Name="Z">-76.46875527369231</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.71627371711179</Real>
+      <Real Name="Y">414.14781220451323</Real>
+      <Real Name="Z">98.183831527550083</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.25066405747654</Real>
+      <Real Name="Y">67.408305033815282</Real>
+      <Real Name="Z">41.300523465622632</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">722.24959434233187</Real>
+      <Real Name="Y">-186.35460709027907</Real>
+      <Real Name="Z">-593.45876718534771</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.00030760178515</Real>
+      <Real Name="Y">205.71913222890808</Real>
+      <Real Name="Z">640.11714131550389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-247.02309440129918</Real>
+      <Real Name="Y">324.86740306678087</Real>
+      <Real Name="Z">148.15237130971008</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.68425059783170639</Real>
+      <Real Name="Y">-10.919011428478669</Real>
+      <Real Name="Z">9.5547827827777922</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.52749685515944478</Real>
+      <Real Name="Y">6.9939843698716828</Real>
+      <Real Name="Z">-2.9467326595193413</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.7972037103186267</Real>
+      <Real Name="Y">4.938949961419226</Real>
+      <Real Name="Z">-7.0645942266187021</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-406.91966616346724</Real>
+      <Real Name="Y">846.02391661995625</Real>
+      <Real Name="Z">252.74394661018539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">291.08358489735321</Real>
+      <Real Name="Y">-480.01846139005033</Real>
+      <Real Name="Z">-680.65370051849561</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.95701713939837</Real>
+      <Real Name="Y">-441.64736765089032</Real>
+      <Real Name="Z">290.35016275333317</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.52710645366994</Real>
+      <Real Name="Y">32.475232520892938</Real>
+      <Real Name="Z">-151.8656151387286</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.64908440468173</Real>
+      <Real Name="Y">57.971079189980131</Real>
+      <Real Name="Z">40.011441768176269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.314549130369073</Real>
+      <Real Name="Y">-39.18358876805712</Real>
+      <Real Name="Z">74.100816205379033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-596.87915178420781</Real>
+      <Real Name="Y">-1290.8719019403063</Real>
+      <Real Name="Z">-302.8778355227418</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">218.88063888924594</Real>
+      <Real Name="Y">615.97043682708647</Real>
+      <Real Name="Z">-293.46806381886199</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">393.941095378147</Real>
+      <Real Name="Y">650.36246860577478</Real>
+      <Real Name="Z">617.0186070104761</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1193.5625541953978</Real>
+      <Real Name="Y">443.09774155104782</Real>
+      <Real Name="Z">-792.07554408218812</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">819.54805096295104</Real>
+      <Real Name="Y">-311.5272381428573</Real>
+      <Real Name="Z">572.28102205107109</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.21525938176103</Real>
+      <Real Name="Y">-31.884059360249722</Real>
+      <Real Name="Z">38.946004715384362</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">216.15679149354395</Real>
+      <Real Name="Y">430.48509361696927</Real>
+      <Real Name="Z">164.33620619293754</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.186488846853734</Real>
+      <Real Name="Y">-134.86982156614835</Real>
+      <Real Name="Z">-70.851499829831525</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-108.02250844410699</Real>
+      <Real Name="Y">-124.21383949030434</Real>
+      <Real Name="Z">-17.182284298308229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.2102139643716</Real>
+      <Real Name="Y">-1074.0600988861561</Real>
+      <Real Name="Z">-860.12040003988045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.09631351027141</Real>
+      <Real Name="Y">209.20112844331436</Real>
+      <Real Name="Z">196.16055872121922</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">575.92938929566276</Real>
+      <Real Name="Y">691.03595203238672</Real>
+      <Real Name="Z">682.34735623050392</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.87293238339231</Real>
+      <Real Name="Y">390.16563378138687</Real>
+      <Real Name="Z">-1022.8807635907668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.946766957038406</Real>
+      <Real Name="Y">-318.27966099818241</Real>
+      <Real Name="Z">753.25575310306294</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.999201433446238</Real>
+      <Real Name="Y">32.162087183582138</Real>
+      <Real Name="Z">254.53780749288188</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.83319417526474</Real>
+      <Real Name="Y">-511.42160111938347</Real>
+      <Real Name="Z">-930.7545916378549</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.2684316090332</Real>
+      <Real Name="Y">79.126072631981458</Real>
+      <Real Name="Z">146.98848078531603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.37241768883009</Real>
+      <Real Name="Y">256.5071378774756</Real>
+      <Real Name="Z">804.46725926185024</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-439.91194625373691</Real>
+      <Real Name="Y">-223.48846629598808</Real>
+      <Real Name="Z">-198.46378508729268</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">224.94665536147181</Real>
+      <Real Name="Y">60.397394961607503</Real>
+      <Real Name="Z">145.17132426402827</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">225.78108849706797</Real>
+      <Real Name="Y">77.066302993338425</Real>
+      <Real Name="Z">123.62725603169116</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">568.81537722740859</Real>
+      <Real Name="Y">813.69044316630232</Real>
+      <Real Name="Z">-260.34116180778386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.33249425656366</Real>
+      <Real Name="Y">-198.43558046463738</Real>
+      <Real Name="Z">15.699489687745341</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.96701168161371</Real>
+      <Real Name="Y">-861.01786705400093</Real>
+      <Real Name="Z">-32.84708944114638</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1744.8689533359084</Real>
+      <Real Name="Y">-146.70035552252369</Real>
+      <Real Name="Z">947.84878947623065</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-863.39841375327842</Real>
+      <Real Name="Y">303.0700408385091</Real>
+      <Real Name="Z">-704.97142507446495</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.04914583004569</Real>
+      <Real Name="Y">-176.10333275396232</Real>
+      <Real Name="Z">-382.00197588191077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-153.51265365766068</Real>
+      <Real Name="Y">55.682711632940524</Real>
+      <Real Name="Z">125.95119344694072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.04454259918046</Real>
+      <Real Name="Y">-162.75546905473379</Real>
+      <Real Name="Z">29.863997850178237</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.87969745224927</Real>
+      <Real Name="Y">-7.5572463100664482</Real>
+      <Real Name="Z">25.019076372669105</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">685.05840366861094</Real>
+      <Real Name="Y">-377.92289284630556</Real>
+      <Real Name="Z">33.148906735946163</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-478.77452411476037</Real>
+      <Real Name="Y">292.29483850177564</Real>
+      <Real Name="Z">25.483874925481288</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.64533271491956</Real>
+      <Real Name="Y">328.12365259750294</Real>
+      <Real Name="Z">-128.69673281129479</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.69123487431202</Real>
+      <Real Name="Y">-1811.091652606945</Real>
+      <Real Name="Z">50.045896165855751</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">128.98055318112472</Real>
+      <Real Name="Y">237.2512769076682</Real>
+      <Real Name="Z">-41.16764066459227</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.38657318257609</Real>
+      <Real Name="Y">1224.5307775252847</Real>
+      <Real Name="Z">-63.083839118256932</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.29900563639086</Real>
+      <Real Name="Y">1028.8218251579569</Real>
+      <Real Name="Z">826.06015033496908</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.67824706426148</Real>
+      <Real Name="Y">-646.08000387039488</Real>
+      <Real Name="Z">-555.45222920819947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.18806432884773</Real>
+      <Real Name="Y">-238.70602173311332</Real>
+      <Real Name="Z">-216.718232485985</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.743615613823849</Real>
+      <Real Name="Y">-451.46618091899347</Real>
+      <Real Name="Z">-521.94833069171148</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-158.08391737849212</Real>
+      <Real Name="Y">376.09770431398573</Real>
+      <Real Name="Z">254.38054752941696</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-93.630378338001904</Real>
+      <Real Name="Y">161.07479823713899</Real>
+      <Real Name="Z">477.56914648109358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">845.28178393819189</Real>
+      <Real Name="Y">-284.70722842252195</Real>
+      <Real Name="Z">-278.5973676047646</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-199.05726966148305</Real>
+      <Real Name="Y">452.46098622046281</Real>
+      <Real Name="Z">22.097044488990065</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-761.22769586733045</Real>
+      <Real Name="Y">-163.09510880542257</Real>
+      <Real Name="Z">188.53999117987485</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1086.9764743941132</Real>
+      <Real Name="Y">1521.1297897028976</Real>
+      <Real Name="Z">-673.27738831452154</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.6615627106128</Real>
+      <Real Name="Y">-219.61038108924546</Real>
+      <Real Name="Z">95.491183339214487</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">620.03307835052578</Real>
+      <Real Name="Y">-974.0329995458751</Real>
+      <Real Name="Z">441.57011906625632</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.21516961358407</Real>
+      <Real Name="Y">29.398694511016174</Real>
+      <Real Name="Z">149.53052810177633</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.68534657016986</Real>
+      <Real Name="Y">92.883491557986673</Real>
+      <Real Name="Z">-36.451953545999842</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-914.89867906860502</Real>
+      <Real Name="Y">119.96913372915684</Real>
+      <Real Name="Z">-128.94440853053979</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-822.63751423698488</Real>
+      <Real Name="Y">-1220.0169118907552</Real>
+      <Real Name="Z">114.36266096758671</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">707.68357775193681</Real>
+      <Real Name="Y">941.3378086730138</Real>
+      <Real Name="Z">60.08123952200971</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">68.118872494929988</Real>
+      <Real Name="Y">251.30475317549264</Real>
+      <Real Name="Z">-76.473949582558873</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-136.63343409269237</Real>
+      <Real Name="Y">-697.25335551751539</Real>
+      <Real Name="Z">809.23678896896297</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.89338735627115</Real>
+      <Real Name="Y">509.83954744747388</Real>
+      <Real Name="Z">-581.41940130466764</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.67823961465966</Real>
+      <Real Name="Y">79.157760754683295</Real>
+      <Real Name="Z">-119.01844882242128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">69.390688842257106</Real>
+      <Real Name="Y">-148.7275117059186</Real>
+      <Real Name="Z">342.33604110634462</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.625517994538882</Real>
+      <Real Name="Y">255.32914403115907</Real>
+      <Real Name="Z">-74.753944992517532</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.06049523514946</Real>
+      <Real Name="Y">369.96027004780274</Real>
+      <Real Name="Z">26.579877362345343</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">82.454766830205088</Real>
+      <Real Name="Y">466.03511758434416</Real>
+      <Real Name="Z">-66.963728038410636</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.497384168902684</Real>
+      <Real Name="Y">-170.16035372964217</Real>
+      <Real Name="Z">64.422695946654542</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.735839783873956</Real>
+      <Real Name="Y">-177.85511618815505</Real>
+      <Real Name="Z">21.9668699820024</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.705127879362209</Real>
+      <Real Name="Y">767.03716438886499</Real>
+      <Real Name="Z">75.47363806032763</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.58821232340248</Real>
+      <Real Name="Y">-166.13191453869467</Real>
+      <Real Name="Z">-66.48763753035945</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.568875992909028</Real>
+      <Real Name="Y">-724.08089225765798</Real>
+      <Real Name="Z">-12.754418581012846</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.510695019133635</Real>
+      <Real Name="Y">-723.00308685261155</Real>
+      <Real Name="Z">-210.45500936138853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.88143271417943</Real>
+      <Real Name="Y">626.18311519882616</Real>
+      <Real Name="Z">111.89769239385279</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.520329053469681</Real>
+      <Real Name="Y">176.58493843761451</Real>
+      <Real Name="Z">44.482642739153206</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.84346893044922</Real>
+      <Real Name="Y">777.96690850348796</Real>
+      <Real Name="Z">560.51527021110735</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.387112705658673</Real>
+      <Real Name="Y">-180.11031922451212</Real>
+      <Real Name="Z">-82.721588495514993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">139.17770138934267</Real>
+      <Real Name="Y">-585.66213346215727</Real>
+      <Real Name="Z">-443.16657208056012</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.05452893715108</Real>
+      <Real Name="Y">299.31714988646905</Real>
+      <Real Name="Z">-420.42841904213833</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">164.5557151620923</Real>
+      <Real Name="Y">-252.18778117176723</Real>
+      <Real Name="Z">436.97612822774579</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.03195801634098</Real>
+      <Real Name="Y">-111.08763640750979</Real>
+      <Real Name="Z">92.585505294879098</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-885.43447932613628</Real>
+      <Real Name="Y">-83.609965176931951</Real>
+      <Real Name="Z">-57.044806588887326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.8127298383738</Real>
+      <Real Name="Y">-69.788124065908164</Real>
+      <Real Name="Z">3.6633299646789794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">312.90062775162704</Real>
+      <Real Name="Y">42.177833602993232</Real>
+      <Real Name="Z">7.3131809492190882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1533.2173688216571</Real>
+      <Real Name="Y">-267.98936438531155</Real>
+      <Real Name="Z">-639.77896891008436</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">298.84618125819253</Real>
+      <Real Name="Y">-41.605345118276801</Real>
+      <Real Name="Z">203.82151757771993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1071.9022296426933</Real>
+      <Real Name="Y">337.55449647805744</Real>
+      <Real Name="Z">324.01417532920465</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-412.06611998161907</Real>
+      <Real Name="Y">1395.4183753381469</Real>
+      <Real Name="Z">324.65125038848481</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.270992502602507</Real>
+      <Real Name="Y">-723.13253896504352</Real>
+      <Real Name="Z">-590.00231110063214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">383.87370665683045</Real>
+      <Real Name="Y">-438.32728665511968</Real>
+      <Real Name="Z">485.93865538869841</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.20567418801926</Real>
+      <Real Name="Y">440.55155433582911</Real>
+      <Real Name="Z">126.63485402593076</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.498271116675255</Real>
+      <Real Name="Y">-125.11100292886805</Real>
+      <Real Name="Z">-82.322492614618866</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-223.47859284693843</Real>
+      <Real Name="Y">-496.10913637041182</Real>
+      <Real Name="Z">-108.89657536146126</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1155.9503899856595</Real>
+      <Real Name="Y">-1972.6389411022967</Real>
+      <Real Name="Z">678.11071089893187</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-122.94929937397522</Real>
+      <Real Name="Y">1194.1370171101557</Real>
+      <Real Name="Z">314.51716430902258</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1030.773302548535</Real>
+      <Real Name="Y">372.63604053007759</Real>
+      <Real Name="Z">-811.24852228744567</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">776.9017501522311</Real>
+      <Real Name="Y">-1294.8584462385397</Real>
+      <Real Name="Z">-289.82149542121937</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-239.76900063202052</Real>
+      <Real Name="Y">679.80430081036627</Real>
+      <Real Name="Z">20.614068686324089</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.5961873087308</Real>
+      <Real Name="Y">693.43540616696725</Real>
+      <Real Name="Z">319.41442387566809</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">85.820924009783084</Real>
+      <Real Name="Y">842.83177643700219</Real>
+      <Real Name="Z">1349.6880334987554</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-105.20094992303544</Real>
+      <Real Name="Y">-723.44706372341761</Real>
+      <Real Name="Z">-1015.3103937742756</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.93788989090284</Real>
+      <Real Name="Y">7.4760387114040654</Real>
+      <Real Name="Z">-260.68171216377658</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.020000 Step 20 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-1239.4463684930795</Real>
+      <Real Name="Y">484.51478145125276</Real>
+      <Real Name="Z">1275.135561320425</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-109.90918892520371</Real>
+      <Real Name="Y">-106.88550583185275</Real>
+      <Real Name="Z">-91.406563386126379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">389.90200018421325</Real>
+      <Real Name="Y">-12.360041200596545</Real>
+      <Real Name="Z">-244.3701095571671</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">355.17908145722186</Real>
+      <Real Name="Y">-485.66165597754815</Real>
+      <Real Name="Z">-586.54852785186108</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1058.0264847538285</Real>
+      <Real Name="Y">287.63464538151811</Real>
+      <Real Name="Z">-577.67823410242102</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-649.51489724666953</Real>
+      <Real Name="Y">-447.13463567706521</Real>
+      <Real Name="Z">-45.514069122845584</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">308.02766500995966</Real>
+      <Real Name="Y">850.15298249605962</Real>
+      <Real Name="Z">385.45895796851545</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">84.997353395742778</Real>
+      <Real Name="Y">-247.44111120999304</Real>
+      <Real Name="Z">11.279756614194902</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-207.41222588589346</Real>
+      <Real Name="Y">-322.7354781198672</Real>
+      <Real Name="Z">-149.31498641606416</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.3316185049299065</Real>
+      <Real Name="Y">3.1288521923143682</Real>
+      <Real Name="Z">15.089041544236849</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.8321268531816202</Real>
+      <Real Name="Y">7.1961526899911341</Real>
+      <Real Name="Z">-2.5448552038679182</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.0924428696579298</Real>
+      <Real Name="Y">1.3774259223203211</Real>
+      <Real Name="Z">-8.6615877366083041</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.086426220368196</Real>
+      <Real Name="Y">10.692449895923176</Real>
+      <Real Name="Z">-26.206387985624481</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.0864293742924644</Real>
+      <Real Name="Y">-3.9553095583933171</Real>
+      <Real Name="Z">12.507954705927972</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.393435618616238</Real>
+      <Real Name="Y">-14.822709141752853</Real>
+      <Real Name="Z">11.518611579841904</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">286.65401244264194</Real>
+      <Real Name="Y">-118.86481520713721</Real>
+      <Real Name="Z">-749.96438104417757</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.56418009901836</Real>
+      <Real Name="Y">202.04873353353818</Real>
+      <Real Name="Z">405.65295765137711</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.322232937590847</Real>
+      <Real Name="Y">9.9322797082933416</Real>
+      <Real Name="Z">174.60894621712546</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">444.98012346892733</Real>
+      <Real Name="Y">675.69850170488144</Real>
+      <Real Name="Z">-196.24515146549675</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.22728843543352</Real>
+      <Real Name="Y">-262.00558865242829</Real>
+      <Real Name="Z">63.968706596861104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-404.10673128248783</Real>
+      <Real Name="Y">-639.64053685501926</Real>
+      <Real Name="Z">275.2067860973084</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">431.45109324180595</Real>
+      <Real Name="Y">-418.79561803127388</Real>
+      <Real Name="Z">-478.21109790655089</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.456953775067433</Real>
+      <Real Name="Y">143.41472638224803</Real>
+      <Real Name="Z">123.16929511277553</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-448.75553878460954</Real>
+      <Real Name="Y">414.06906377907427</Real>
+      <Real Name="Z">457.31033018818846</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">119.63400581157282</Real>
+      <Real Name="Y">220.58998716549317</Real>
+      <Real Name="Z">291.25574100538017</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.72652587136769</Real>
+      <Real Name="Y">-162.71023356514488</Real>
+      <Real Name="Z">-137.90623225292376</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-183.97492775037128</Real>
+      <Real Name="Y">-241.74900721064392</Real>
+      <Real Name="Z">-149.74839776845943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-434.09749474828476</Real>
+      <Real Name="Y">375.48677208026277</Real>
+      <Real Name="Z">866.98510435391734</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">400.95738743541716</Real>
+      <Real Name="Y">-254.03712916171335</Real>
+      <Real Name="Z">-821.8295672929703</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.17978429299924</Real>
+      <Real Name="Y">-229.24463341008888</Real>
+      <Real Name="Z">-112.27858137075779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1276.5904137031816</Real>
+      <Real Name="Y">333.58308608038436</Real>
+      <Real Name="Z">807.72550375574076</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">11.164372707728727</Real>
+      <Real Name="Y">-521.75749791148951</Real>
+      <Real Name="Z">-194.70325671510682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-963.27940757146507</Real>
+      <Real Name="Y">512.22840061583202</Real>
+      <Real Name="Z">-534.34074662550802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">253.3973443892329</Real>
+      <Real Name="Y">1013.8662085664398</Real>
+      <Real Name="Z">173.90109980892618</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-83.617456531493076</Real>
+      <Real Name="Y">-377.61889073564038</Real>
+      <Real Name="Z">-53.807658427864077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.576201747401541</Real>
+      <Real Name="Y">-183.43269546665579</Real>
+      <Real Name="Z">-57.446837303990065</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-152.5751099112924</Real>
+      <Real Name="Y">39.342883507829029</Real>
+      <Real Name="Z">-983.07077960789775</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-107.54657776583011</Real>
+      <Real Name="Y">242.07536432001351</Real>
+      <Real Name="Z">291.59713184364125</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">319.95017177378344</Real>
+      <Real Name="Y">124.59647979894515</Real>
+      <Real Name="Z">309.02114208057304</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">492.74907076717517</Real>
+      <Real Name="Y">214.94350225311882</Real>
+      <Real Name="Z">-391.89600178110697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-486.61583070668422</Real>
+      <Real Name="Y">71.856789496537644</Real>
+      <Real Name="Z">656.29512996602591</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-172.40329857697469</Real>
+      <Real Name="Y">-226.97271696771307</Real>
+      <Real Name="Z">49.202568041920777</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-964.38004649933441</Real>
+      <Real Name="Y">191.19701257696406</Real>
+      <Real Name="Z">261.69401286061577</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">613.58216990336598</Real>
+      <Real Name="Y">-320.98305205384418</Real>
+      <Real Name="Z">-151.65082139776786</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">170.53078619957395</Real>
+      <Real Name="Y">-6.583604142621839</Real>
+      <Real Name="Z">-174.67339191501898</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-424.07568799349048</Real>
+      <Real Name="Y">78.709845068624787</Real>
+      <Real Name="Z">267.32243649320191</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">145.38028068345761</Real>
+      <Real Name="Y">-0.47216567801407905</Real>
+      <Real Name="Z">-52.483851664047741</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.29841478928654</Real>
+      <Real Name="Y">-190.22270574702196</Real>
+      <Real Name="Z">-96.609071240685807</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-167.11951893990087</Real>
+      <Real Name="Y">-1444.1602176793051</Real>
+      <Real Name="Z">-700.99555100966859</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">42.614883091935155</Real>
+      <Real Name="Y">1352.0617501818524</Real>
+      <Real Name="Z">472.77367469660641</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">380.73619718762683</Real>
+      <Real Name="Y">376.50284283496569</Real>
+      <Real Name="Z">139.59713485234349</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">418.80343514069875</Real>
+      <Real Name="Y">737.54970941298711</Real>
+      <Real Name="Z">-876.08940148642887</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">166.41790960059225</Real>
+      <Real Name="Y">-450.46413319386602</Real>
+      <Real Name="Z">20.987806875332609</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-387.14218376415658</Real>
+      <Real Name="Y">-155.24999103365144</Real>
+      <Real Name="Z">283.39524974637891</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">68.406849900763049</Real>
+      <Real Name="Y">129.4051254354595</Real>
+      <Real Name="Z">36.664179037260581</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-16.271044383019653</Real>
+      <Real Name="Y">-52.838379133487429</Real>
+      <Real Name="Z">-14.667724530598854</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-31.842719372311997</Real>
+      <Real Name="Y">-43.518614351912326</Real>
+      <Real Name="Z">-13.420068229416298</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-364.13174074994384</Real>
+      <Real Name="Y">925.37245665357261</Real>
+      <Real Name="Z">237.11429248725852</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">446.93544286332622</Real>
+      <Real Name="Y">-699.93491434521729</Real>
+      <Real Name="Z">-413.07788513005329</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">19.678880498905006</Real>
+      <Real Name="Y">-157.79974325654388</Real>
+      <Real Name="Z">-22.789199030542601</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">511.59464661819294</Real>
+      <Real Name="Y">-714.49524540893719</Real>
+      <Real Name="Z">-1254.9720965895488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-393.18825004162107</Real>
+      <Real Name="Y">290.11672998149891</Real>
+      <Real Name="Z">998.71093813361915</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-367.70638336032476</Real>
+      <Real Name="Y">174.1331265233548</Real>
+      <Real Name="Z">92.526759672548337</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">42.36425113714327</Real>
+      <Real Name="Y">-259.89230639902286</Real>
+      <Real Name="Z">23.063149021095441</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.222964160866582</Real>
+      <Real Name="Y">113.59395699682949</Real>
+      <Real Name="Z">34.516253438364828</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-37.477680745904408</Real>
+      <Real Name="Y">65.191130774708611</Real>
+      <Real Name="Z">129.09683458379007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">334.47941088467508</Real>
+      <Real Name="Y">-306.09355860191249</Real>
+      <Real Name="Z">-101.58129364033857</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-312.23666599937587</Real>
+      <Real Name="Y">377.69056468420814</Real>
+      <Real Name="Z">157.29298046630615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-88.91299641612396</Real>
+      <Real Name="Y">45.56535002840738</Real>
+      <Real Name="Z">55.645569621474991</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">695.55826365498831</Real>
+      <Real Name="Y">-456.92632363631077</Real>
+      <Real Name="Z">-686.81564742709816</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-457.62105756777976</Real>
+      <Real Name="Y">175.01094867895554</Real>
+      <Real Name="Z">778.53059954782361</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-181.65260363041793</Real>
+      <Real Name="Y">276.37632414000041</Real>
+      <Real Name="Z">224.00977892793148</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.4793540127705072</Real>
+      <Real Name="Y">-12.297225067431503</Real>
+      <Real Name="Z">29.605299938096579</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.1293827086978077</Real>
+      <Real Name="Y">6.1733144756544887</Real>
+      <Real Name="Z">-11.007915655444194</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.40762211653127167</Real>
+      <Real Name="Y">6.605287745821621</Real>
+      <Real Name="Z">-16.998651040983667</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-154.86358222149101</Real>
+      <Real Name="Y">795.2410182860209</Real>
+      <Real Name="Z">122.0011322924375</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">480.24108222685078</Real>
+      <Real Name="Y">-389.05126809253625</Real>
+      <Real Name="Z">-521.12499298205421</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-204.71028922094672</Real>
+      <Real Name="Y">-354.4945248257136</Real>
+      <Real Name="Z">302.73599724536564</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">338.3174409853325</Real>
+      <Real Name="Y">47.906250326419624</Real>
+      <Real Name="Z">-322.80524146067336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.10346356459848</Real>
+      <Real Name="Y">44.783512830386897</Real>
+      <Real Name="Z">84.669208322113832</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.461073523820119</Real>
+      <Real Name="Y">-21.661384461322889</Real>
+      <Real Name="Z">105.11411920885659</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-417.32355400173907</Real>
+      <Real Name="Y">-1049.9188210711361</Real>
+      <Real Name="Z">-494.61538773522506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">155.35113580298707</Real>
+      <Real Name="Y">332.45297928142281</Real>
+      <Real Name="Z">-161.13951203015435</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">400.62211612411448</Real>
+      <Real Name="Y">700.81021591237743</Real>
+      <Real Name="Z">655.11488682015101</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-853.83068490355174</Real>
+      <Real Name="Y">325.78238680238087</Real>
+      <Real Name="Z">-504.49647493060957</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">678.73460731928174</Real>
+      <Real Name="Y">-268.6812405248279</Real>
+      <Real Name="Z">484.32847630348488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">169.96468583601003</Real>
+      <Real Name="Y">-1.1660523750632308</Real>
+      <Real Name="Z">32.513617573963529</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">253.30392650275971</Real>
+      <Real Name="Y">427.38835060467994</Real>
+      <Real Name="Z">75.951837330767745</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.510846164833424</Real>
+      <Real Name="Y">-154.66090970944913</Real>
+      <Real Name="Z">-61.265450500643915</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-122.33140424516861</Real>
+      <Real Name="Y">-151.87660446862199</Real>
+      <Real Name="Z">12.69132994213634</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-816.61115603332951</Real>
+      <Real Name="Y">-792.86928289833918</Real>
+      <Real Name="Z">-519.36453365434045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">148.44461364837275</Real>
+      <Real Name="Y">199.47784171562748</Real>
+      <Real Name="Z">153.91160626903769</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">705.61710566780198</Real>
+      <Real Name="Y">519.05189114360223</Real>
+      <Real Name="Z">393.6057276303527</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.555757781544429</Real>
+      <Real Name="Y">-75.167222711863218</Real>
+      <Real Name="Z">-779.47492057779505</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.315253383049363</Real>
+      <Real Name="Y">-193.06435120625272</Real>
+      <Real Name="Z">730.36620574490928</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.045169537832066</Real>
+      <Real Name="Y">55.369605692708717</Real>
+      <Real Name="Z">153.96149446225203</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-539.36744362959121</Real>
+      <Real Name="Y">-516.82723981676804</Real>
+      <Real Name="Z">-922.40195466688203</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">124.19367476985356</Real>
+      <Real Name="Y">76.307073274984788</Real>
+      <Real Name="Z">161.13764468086663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">248.8333914349183</Real>
+      <Real Name="Y">494.17671938579258</Real>
+      <Real Name="Z">736.39279290179286</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-428.42629395071498</Real>
+      <Real Name="Y">-486.3923833975241</Real>
+      <Real Name="Z">-95.611248984183547</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">323.25490665159163</Real>
+      <Real Name="Y">222.95817769174289</Real>
+      <Real Name="Z">19.266354976249552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.57974667189751</Real>
+      <Real Name="Y">153.72973572515392</Real>
+      <Real Name="Z">39.230732424560777</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">132.79913938915502</Real>
+      <Real Name="Y">780.7923635423283</Real>
+      <Real Name="Z">-9.7365933793842601</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-5.9520975902036994</Real>
+      <Real Name="Y">-157.82528441194657</Real>
+      <Real Name="Z">-10.539090203982113</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-200.16100749108074</Real>
+      <Real Name="Y">-815.11162321163204</Real>
+      <Real Name="Z">-133.92977159667686</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1867.9880338241874</Real>
+      <Real Name="Y">-163.23923013297406</Real>
+      <Real Name="Z">839.2662910573888</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1050.3002210836175</Real>
+      <Real Name="Y">241.12643447853813</Real>
+      <Real Name="Z">-638.59276659875059</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-434.13849141279923</Real>
+      <Real Name="Y">-181.71789988650167</Real>
+      <Real Name="Z">-378.80955074216587</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-304.59678359112706</Real>
+      <Real Name="Y">69.881786217781396</Real>
+      <Real Name="Z">-102.25757772378475</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.38241450009988</Real>
+      <Real Name="Y">-135.06056867467345</Real>
+      <Real Name="Z">53.034088505574701</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.81354016287241</Real>
+      <Real Name="Y">-5.3833774756347559</Real>
+      <Real Name="Z">38.017945933007248</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">754.13107014364437</Real>
+      <Real Name="Y">-262.58765310252068</Real>
+      <Real Name="Z">110.91341165934453</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-677.98487451195081</Real>
+      <Real Name="Y">244.7355624533968</Real>
+      <Real Name="Z">56.472371582754214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-338.86372422394965</Real>
+      <Real Name="Y">165.69769392825273</Real>
+      <Real Name="Z">-89.370197653863457</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-204.93314475874217</Real>
+      <Real Name="Y">-1124.8848989438188</Real>
+      <Real Name="Z">8.8669942919392639</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">91.648878495995319</Real>
+      <Real Name="Y">169.37864006831214</Real>
+      <Real Name="Z">-46.653195395816532</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.49045724300106</Real>
+      <Real Name="Y">1114.6494701504023</Real>
+      <Real Name="Z">32.565790727418722</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">215.48917258000958</Real>
+      <Real Name="Y">1073.8073690380256</Real>
+      <Real Name="Z">761.90718620332359</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-175.05174466135915</Real>
+      <Real Name="Y">-645.32199230804895</Real>
+      <Real Name="Z">-634.64228999102522</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-27.168728764378173</Real>
+      <Real Name="Y">-288.96293304620946</Real>
+      <Real Name="Z">-123.11311622127141</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.523877826146133</Real>
+      <Real Name="Y">-413.1061994370458</Real>
+      <Real Name="Z">-395.31343971009721</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-93.674675662312708</Real>
+      <Real Name="Y">335.84284284308512</Real>
+      <Real Name="Z">-7.0131023927249672</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">153.30559404240924</Real>
+      <Real Name="Y">78.63624094721547</Real>
+      <Real Name="Z">372.65881149940321</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">752.69489299330291</Real>
+      <Real Name="Y">-435.25643406581912</Real>
+      <Real Name="Z">-201.94763852987663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-344.33616780748423</Real>
+      <Real Name="Y">557.86899890798838</Real>
+      <Real Name="Z">65.140056591455732</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-637.17689168026561</Real>
+      <Real Name="Y">-86.374432901503226</Real>
+      <Real Name="Z">210.49082601415131</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-965.2580482315343</Real>
+      <Real Name="Y">1183.4409066744659</Real>
+      <Real Name="Z">-655.13824680166715</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.79209375317265</Real>
+      <Real Name="Y">-180.97528390766217</Real>
+      <Real Name="Z">102.69536266143733</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">564.53062054744464</Real>
+      <Real Name="Y">-837.78649789485826</Real>
+      <Real Name="Z">529.37064253658696</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">938.62237642758089</Real>
+      <Real Name="Y">-115.61745696551549</Real>
+      <Real Name="Z">51.577914230939058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.52737916776073</Real>
+      <Real Name="Y">86.330447416934078</Real>
+      <Real Name="Z">-33.318406630343794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-892.0298542271986</Real>
+      <Real Name="Y">-65.411211264752779</Real>
+      <Real Name="Z">-96.614938375644769</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-627.12689406118898</Real>
+      <Real Name="Y">-665.05065336173152</Real>
+      <Real Name="Z">155.2806457316579</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">595.94653336448698</Real>
+      <Real Name="Y">682.05655567891267</Real>
+      <Real Name="Z">24.522821355088553</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">45.554552302474164</Real>
+      <Real Name="Y">120.61695775177391</Real>
+      <Real Name="Z">-77.098338168203838</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-293.78025255676459</Real>
+      <Real Name="Y">-1100.8061703434898</Real>
+      <Real Name="Z">1194.6624321527338</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">324.42177790251532</Real>
+      <Real Name="Y">622.85416030644546</Real>
+      <Real Name="Z">-559.39144377162313</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-67.48618395686583</Real>
+      <Real Name="Y">141.68535506711939</Real>
+      <Real Name="Z">-189.22285362391523</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">372.83233432790519</Real>
+      <Real Name="Y">-511.17101098962553</Real>
+      <Real Name="Z">33.059390149434932</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-84.119296939636968</Real>
+      <Real Name="Y">280.74819387978908</Real>
+      <Real Name="Z">-68.28358596601332</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-378.28308187701873</Real>
+      <Real Name="Y">354.98088992966655</Real>
+      <Real Name="Z">-71.60662113600948</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.80754327525493</Real>
+      <Real Name="Y">429.72496679297001</Real>
+      <Real Name="Z">2.2477974479608207</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-44.897761860637488</Real>
+      <Real Name="Y">-207.43766849152729</Real>
+      <Real Name="Z">36.538359681736651</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">39.472277797693557</Real>
+      <Real Name="Y">-126.28181131915025</Real>
+      <Real Name="Z">-3.3596204092858031</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.590958799272137</Real>
+      <Real Name="Y">773.67512520446485</Real>
+      <Real Name="Z">66.813563788529862</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.479080289535112</Real>
+      <Real Name="Y">-180.37512802462012</Real>
+      <Real Name="Z">-61.317570761450781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">160.53312896226569</Real>
+      <Real Name="Y">-672.51097358492882</Real>
+      <Real Name="Z">-42.557405275651561</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.07703422932973</Real>
+      <Real Name="Y">-680.1521943116278</Real>
+      <Real Name="Z">-274.81430958608024</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">210.32581592824499</Real>
+      <Real Name="Y">598.42020812477165</Real>
+      <Real Name="Z">155.5470910545572</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">93.877862202054189</Real>
+      <Real Name="Y">150.67258221374806</Real>
+      <Real Name="Z">41.339460870179167</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">130.89989737454601</Real>
+      <Real Name="Y">872.38616550754818</Real>
+      <Real Name="Z">615.1030169756001</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-5.8152109032037806</Real>
+      <Real Name="Y">-213.8215678949833</Real>
+      <Real Name="Z">-113.50282748325105</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-179.48469489675477</Real>
+      <Real Name="Y">-675.42210837157597</Real>
+      <Real Name="Z">-459.35297251688439</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-227.29868624007324</Real>
+      <Real Name="Y">171.33600660437145</Real>
+      <Real Name="Z">-352.89296858357994</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">109.03776782542596</Real>
+      <Real Name="Y">-100.96245605229829</Real>
+      <Real Name="Z">350.21075906991518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">128.64411300962581</Real>
+      <Real Name="Y">-60.736237101625917</Real>
+      <Real Name="Z">112.73312204639866</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-995.95228561725082</Real>
+      <Real Name="Y">17.827362165228095</Real>
+      <Real Name="Z">-60.41457502614</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">728.53366237323553</Real>
+      <Real Name="Y">-138.68013864617069</Real>
+      <Real Name="Z">-18.277565536100781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">336.4834553296464</Real>
+      <Real Name="Y">-3.9471262564869178</Real>
+      <Real Name="Z">9.8423465857690786</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1552.8021603539912</Real>
+      <Real Name="Y">-905.10316868270252</Real>
+      <Real Name="Z">-463.78954994720539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">341.93150235722482</Real>
+      <Real Name="Y">73.887312358622907</Real>
+      <Real Name="Z">133.0983907725203</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1184.1368173498499</Real>
+      <Real Name="Y">216.29829478624043</Real>
+      <Real Name="Z">334.48308581067442</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-96.209415483650233</Real>
+      <Real Name="Y">1484.3895233743997</Real>
+      <Real Name="Z">67.757332047668058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.73693652933791</Real>
+      <Real Name="Y">-803.64428101457668</Real>
+      <Real Name="Z">-436.80685954043071</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">208.15783548906677</Real>
+      <Real Name="Y">-614.54482322644105</Real>
+      <Real Name="Z">586.56190676107644</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">244.68107992796334</Real>
+      <Real Name="Y">510.14447446502743</Real>
+      <Real Name="Z">162.56376032830684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.257514090482942</Real>
+      <Real Name="Y">-132.73970312525685</Real>
+      <Real Name="Z">-106.48829329001364</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-260.44314089892083</Real>
+      <Real Name="Y">-514.67205118182164</Real>
+      <Real Name="Z">-77.550037676423926</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">766.46417501708106</Real>
+      <Real Name="Y">-1624.1682903318801</Real>
+      <Real Name="Z">841.42844254507224</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.020934204443051</Real>
+      <Real Name="Y">1126.6570670703536</Real>
+      <Real Name="Z">93.101240018070129</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-940.81306524017259</Real>
+      <Real Name="Y">559.78763093592056</Real>
+      <Real Name="Z">-735.29816772846584</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">783.12053654917031</Real>
+      <Real Name="Y">-1980.7666391433409</Real>
+      <Real Name="Z">-650.98638022036891</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.430012982238338</Real>
+      <Real Name="Y">838.25927476445554</Real>
+      <Real Name="Z">204.08972132732603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-538.7002711507422</Real>
+      <Real Name="Y">671.56802281110333</Real>
+      <Real Name="Z">423.78339174487331</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">109.19930685553689</Real>
+      <Real Name="Y">742.8545269037495</Real>
+      <Real Name="Z">1198.6416796827787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.94122142760465</Real>
+      <Real Name="Y">-585.19901752633871</Real>
+      <Real Name="Z">-1016.5237521902745</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.371625394152289</Real>
+      <Real Name="Y">-83.719088523310958</Real>
+      <Real Name="Z">-276.38192921786447</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_vdw_s.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwsequential_vdw_s.xml
new file mode 100644 (file)
index 0000000..e2b8c95
--- /dev/null
@@ -0,0 +1,961 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVvdw/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-7.247479</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-7.0424113</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-6.8323665</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-6.6228771</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-6.4189973</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-6.2276378</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-6.0564666</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-5.9139009</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-5.8075461</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-5.7429395</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-5.7231169</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-5.7479558</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-5.8138938</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-5.9150362</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-6.0419688</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-6.1852517</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-6.3348541</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-6.4813347</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-6.6161499</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-6.7340264</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-6.8308268</Real>
+  </Energy>
+  <Energy Name="dVcoul/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-77.872757</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-77.710526</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-77.492188</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-77.22374</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-76.925095</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-76.629395</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-76.381966</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-76.231613</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-76.220352</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-76.364891</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-76.657516</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-77.060791</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-77.51268</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-77.941872</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-78.276093</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-78.457672</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-78.454803</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-78.264023</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-77.915649</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-77.457748</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-76.94519</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1465.6984</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1465.6885</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1463.7625</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1459.9094</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1454.3463</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1447.5281</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1440.0631</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1432.605</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1425.7157</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1419.7595</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1414.8716</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1410.993</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1407.9927</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1405.7484</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1404.2217</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1403.4679</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1403.5713</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1404.5859</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1406.4597</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1409.0063</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1411.9519</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">633.88702</Real>
+      <Real Name="Y">-773.7572</Real>
+      <Real Name="Z">-86.054512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-173.46164</Real>
+      <Real Name="Y">-254.11159</Real>
+      <Real Name="Z">31.399275</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-460.76065</Real>
+      <Real Name="Y">273.63226</Real>
+      <Real Name="Z">-218.70953</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-124.68356</Real>
+      <Real Name="Y">188.89328</Real>
+      <Real Name="Z">21.255823</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">744.85315</Real>
+      <Real Name="Y">615.69745</Real>
+      <Real Name="Z">-32.883961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.804447</Real>
+      <Real Name="Y">330.27072</Real>
+      <Real Name="Z">-154.90648</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-846.92877</Real>
+      <Real Name="Y">354.086</Real>
+      <Real Name="Z">1107.674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-40.530876</Real>
+      <Real Name="Y">-295.73813</Real>
+      <Real Name="Z">-453.5372</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">275.31131</Real>
+      <Real Name="Y">-437.97052</Real>
+      <Real Name="Z">-229.88564</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">10.925804</Real>
+      <Real Name="Y">-30.976189</Real>
+      <Real Name="Z">20.380272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.0141983</Real>
+      <Real Name="Y">27.856314</Real>
+      <Real Name="Z">-12.695366</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.2674408</Real>
+      <Real Name="Y">14.475534</Real>
+      <Real Name="Z">-4.0459976</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.22610474</Real>
+      <Real Name="Y">23.698967</Real>
+      <Real Name="Z">-7.1104736</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.8458405</Real>
+      <Real Name="Y">-9.5181484</Real>
+      <Real Name="Z">4.7690716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.3456688</Real>
+      <Real Name="Y">-24.903942</Real>
+      <Real Name="Z">4.5986137</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.96179</Real>
+      <Real Name="Y">-59.604706</Real>
+      <Real Name="Z">-670.1571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.81146</Real>
+      <Real Name="Y">36.204243</Real>
+      <Real Name="Z">385.08865</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.8211288</Real>
+      <Real Name="Y">-11.299082</Real>
+      <Real Name="Z">206.28496</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">394.35254</Real>
+      <Real Name="Y">432.302</Real>
+      <Real Name="Z">-97.789093</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.851814</Real>
+      <Real Name="Y">-238.51105</Real>
+      <Real Name="Z">85.78244</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-370.92975</Real>
+      <Real Name="Y">-600.14075</Real>
+      <Real Name="Z">242.79077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">643.13123</Real>
+      <Real Name="Y">-492.27921</Real>
+      <Real Name="Z">-420.98755</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.97176</Real>
+      <Real Name="Y">121.35711</Real>
+      <Real Name="Z">75.023636</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.06006</Real>
+      <Real Name="Y">438.33389</Real>
+      <Real Name="Z">218.99048</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.026768</Real>
+      <Real Name="Y">295.72083</Real>
+      <Real Name="Z">69.88269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.252739</Real>
+      <Real Name="Y">-163.91307</Real>
+      <Real Name="Z">-112.35473</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.12119</Real>
+      <Real Name="Y">-215.1304</Real>
+      <Real Name="Z">-140.17712</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-591.93005</Real>
+      <Real Name="Y">297.4751</Real>
+      <Real Name="Z">826.39581</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">397.93842</Real>
+      <Real Name="Y">-70.315125</Real>
+      <Real Name="Z">-868.24725</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.87331</Real>
+      <Real Name="Y">-199.27788</Real>
+      <Real Name="Z">-58.264145</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1114.8737</Real>
+      <Real Name="Y">629.37103</Real>
+      <Real Name="Z">795.97894</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.5452</Real>
+      <Real Name="Y">-873.28632</Real>
+      <Real Name="Z">-339.25296</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-870.47675</Real>
+      <Real Name="Y">286.55475</Real>
+      <Real Name="Z">-544.52521</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">357.06244</Real>
+      <Real Name="Y">756.97516</Real>
+      <Real Name="Z">146.16736</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.1041641</Real>
+      <Real Name="Y">-651.95844</Real>
+      <Real Name="Z">-54.851482</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-90.345825</Real>
+      <Real Name="Y">-131.84966</Real>
+      <Real Name="Z">-37.247372</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.065874</Real>
+      <Real Name="Y">-139.34502</Real>
+      <Real Name="Z">-869.21814</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.01487</Real>
+      <Real Name="Y">7.3681002</Real>
+      <Real Name="Z">237.56253</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">409.19614</Real>
+      <Real Name="Y">-13.744324</Real>
+      <Real Name="Z">674.30859</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.802597</Real>
+      <Real Name="Y">49.907135</Real>
+      <Real Name="Z">-863.78381</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-246.26122</Real>
+      <Real Name="Y">161.93843</Real>
+      <Real Name="Z">488.15155</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">20.568638</Real>
+      <Real Name="Y">-198.69077</Real>
+      <Real Name="Z">167.75015</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-614.66638</Real>
+      <Real Name="Y">104.43056</Real>
+      <Real Name="Z">354.05258</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">537.62415</Real>
+      <Real Name="Y">-78.613785</Real>
+      <Real Name="Z">-4.2482901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.13351</Real>
+      <Real Name="Y">-43.662785</Real>
+      <Real Name="Z">-245.23721</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-106.55264</Real>
+      <Real Name="Y">29.611603</Real>
+      <Real Name="Z">363.04178</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">246.89545</Real>
+      <Real Name="Y">-1.2883682</Real>
+      <Real Name="Z">-127.71266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">59.181488</Real>
+      <Real Name="Y">-111.70288</Real>
+      <Real Name="Z">-115.61073</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-356.0928</Real>
+      <Real Name="Y">-2083.6243</Real>
+      <Real Name="Z">-694.72595</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">338.5383</Real>
+      <Real Name="Y">1335.4231</Real>
+      <Real Name="Z">310.34689</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">276.91052</Real>
+      <Real Name="Y">426.71542</Real>
+      <Real Name="Z">68.866402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">409.91794</Real>
+      <Real Name="Y">978.05811</Real>
+      <Real Name="Z">-851.61719</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-141.87756</Real>
+      <Real Name="Y">-531.63434</Real>
+      <Real Name="Z">230.45869</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-250.61594</Real>
+      <Real Name="Y">-386.77625</Real>
+      <Real Name="Z">568.34503</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.995796</Real>
+      <Real Name="Y">98.713974</Real>
+      <Real Name="Z">5.9983025</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.455554</Real>
+      <Real Name="Y">-43.458389</Real>
+      <Real Name="Z">-3.6231194</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.239048</Real>
+      <Real Name="Y">-33.183907</Real>
+      <Real Name="Z">8.2902775</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-423.92447</Real>
+      <Real Name="Y">988.94885</Real>
+      <Real Name="Z">389.62555</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">598.97217</Real>
+      <Real Name="Y">-698.11646</Real>
+      <Real Name="Z">-450.10861</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.343487</Real>
+      <Real Name="Y">-160.39758</Real>
+      <Real Name="Z">-11.909855</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">157.71036</Real>
+      <Real Name="Y">-504.38611</Real>
+      <Real Name="Z">-1441.918</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.964081</Real>
+      <Real Name="Y">370.46133</Real>
+      <Real Name="Z">1053.1552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-283.37589</Real>
+      <Real Name="Y">14.029888</Real>
+      <Real Name="Z">269.58932</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.54297</Real>
+      <Real Name="Y">-124.31937</Real>
+      <Real Name="Z">-117.58147</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-85.880951</Real>
+      <Real Name="Y">126.24585</Real>
+      <Real Name="Z">72.835114</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.938107</Real>
+      <Real Name="Y">40.618141</Real>
+      <Real Name="Z">150.70537</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.19748</Real>
+      <Real Name="Y">-338.35422</Real>
+      <Real Name="Z">-76.46907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.71738</Real>
+      <Real Name="Y">414.147</Real>
+      <Real Name="Z">98.183876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.25122</Real>
+      <Real Name="Y">67.408051</Real>
+      <Real Name="Z">41.300266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">722.24652</Real>
+      <Real Name="Y">-186.35585</Real>
+      <Real Name="Z">-593.45697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-344.99899</Real>
+      <Real Name="Y">205.72141</Real>
+      <Real Name="Z">640.11542</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-247.02272</Real>
+      <Real Name="Y">324.86865</Real>
+      <Real Name="Z">148.15186</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.68489075</Real>
+      <Real Name="Y">-10.920052</Real>
+      <Real Name="Z">9.5551529</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.5278492</Real>
+      <Real Name="Y">6.9944229</Real>
+      <Real Name="Z">-2.9469452</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.7969704</Real>
+      <Real Name="Y">4.9394989</Real>
+      <Real Name="Z">-7.0647907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-406.91809</Real>
+      <Real Name="Y">846.02759</Real>
+      <Real Name="Z">252.74513</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">291.08258</Real>
+      <Real Name="Y">-480.01971</Real>
+      <Real Name="Z">-680.65302</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.95673</Real>
+      <Real Name="Y">-441.6496</Real>
+      <Real Name="Z">290.35007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.52753</Real>
+      <Real Name="Y">32.474689</Real>
+      <Real Name="Z">-151.86365</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.64911</Real>
+      <Real Name="Y">57.971031</Real>
+      <Real Name="Z">40.010414</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.314713</Real>
+      <Real Name="Y">-39.183544</Real>
+      <Real Name="Z">74.100586</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-596.88306</Real>
+      <Real Name="Y">-1290.8757</Real>
+      <Real Name="Z">-302.88104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">218.88113</Real>
+      <Real Name="Y">615.97302</Real>
+      <Real Name="Z">-293.4668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">393.9408</Real>
+      <Real Name="Y">650.36456</Real>
+      <Real Name="Z">617.01917</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1193.564</Real>
+      <Real Name="Y">443.09839</Real>
+      <Real Name="Z">-792.07574</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">819.54901</Real>
+      <Real Name="Y">-311.52759</Real>
+      <Real Name="Z">572.28064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.21558</Real>
+      <Real Name="Y">-31.884295</Real>
+      <Real Name="Z">38.946358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">216.15625</Real>
+      <Real Name="Y">430.48578</Real>
+      <Real Name="Z">164.33459</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.185656</Real>
+      <Real Name="Y">-134.87021</Real>
+      <Real Name="Z">-70.851448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-108.02281</Real>
+      <Real Name="Y">-124.21425</Real>
+      <Real Name="Z">-17.181404</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.2062</Real>
+      <Real Name="Y">-1074.0594</Real>
+      <Real Name="Z">-860.1192</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.09605</Real>
+      <Real Name="Y">209.2016</Real>
+      <Real Name="Z">196.16069</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">575.92712</Real>
+      <Real Name="Y">691.03589</Real>
+      <Real Name="Z">682.34711</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.87489</Real>
+      <Real Name="Y">390.16568</Real>
+      <Real Name="Z">-1022.8798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.947098</Real>
+      <Real Name="Y">-318.27917</Real>
+      <Real Name="Z">753.25494</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.99823</Real>
+      <Real Name="Y">32.162571</Real>
+      <Real Name="Z">254.53839</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.83133</Real>
+      <Real Name="Y">-511.42389</Real>
+      <Real Name="Z">-930.75775</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.26678</Real>
+      <Real Name="Y">79.126808</Real>
+      <Real Name="Z">146.98911</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.370152</Real>
+      <Real Name="Y">256.5076</Real>
+      <Real Name="Z">804.46674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-439.91162</Real>
+      <Real Name="Y">-223.48511</Real>
+      <Real Name="Z">-198.46278</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">224.94597</Real>
+      <Real Name="Y">60.396034</Real>
+      <Real Name="Z">145.17058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">225.78027</Real>
+      <Real Name="Y">77.065506</Real>
+      <Real Name="Z">123.62689</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">568.81421</Real>
+      <Real Name="Y">813.69324</Real>
+      <Real Name="Z">-260.33954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.33185</Real>
+      <Real Name="Y">-198.43544</Real>
+      <Real Name="Z">15.699821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.9657</Real>
+      <Real Name="Y">-861.01874</Real>
+      <Real Name="Z">-32.847767</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1744.8718</Real>
+      <Real Name="Y">-146.70538</Real>
+      <Real Name="Z">947.849</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-863.39832</Real>
+      <Real Name="Y">303.07144</Real>
+      <Real Name="Z">-704.97058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.04962</Real>
+      <Real Name="Y">-176.10233</Real>
+      <Real Name="Z">-382.00201</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-153.51324</Real>
+      <Real Name="Y">55.68367</Real>
+      <Real Name="Z">125.95101</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.0448</Real>
+      <Real Name="Y">-162.75539</Real>
+      <Real Name="Z">29.864437</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.87985</Real>
+      <Real Name="Y">-7.5582418</Real>
+      <Real Name="Z">25.019432</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">685.05609</Real>
+      <Real Name="Y">-377.9234</Real>
+      <Real Name="Z">33.146976</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-478.77271</Real>
+      <Real Name="Y">292.2962</Real>
+      <Real Name="Z">25.484558</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.64474</Real>
+      <Real Name="Y">328.12592</Real>
+      <Real Name="Z">-128.69678</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.69466</Real>
+      <Real Name="Y">-1811.087</Real>
+      <Real Name="Z">50.047211</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">128.98105</Real>
+      <Real Name="Y">237.25067</Real>
+      <Real Name="Z">-41.168339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.38527</Real>
+      <Real Name="Y">1224.5275</Real>
+      <Real Name="Z">-63.084476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.29596</Real>
+      <Real Name="Y">1028.8195</Real>
+      <Real Name="Z">826.05914</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.67789</Real>
+      <Real Name="Y">-646.07843</Real>
+      <Real Name="Z">-555.45239</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.187481</Real>
+      <Real Name="Y">-238.70564</Real>
+      <Real Name="Z">-216.71841</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.746235</Real>
+      <Real Name="Y">-451.4632</Real>
+      <Real Name="Z">-521.95026</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-158.08191</Real>
+      <Real Name="Y">376.0954</Real>
+      <Real Name="Z">254.38142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-93.62793</Real>
+      <Real Name="Y">161.07248</Real>
+      <Real Name="Z">477.57007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">845.27881</Real>
+      <Real Name="Y">-284.70667</Real>
+      <Real Name="Z">-278.60162</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-199.05646</Real>
+      <Real Name="Y">452.46191</Real>
+      <Real Name="Z">22.097107</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-761.22772</Real>
+      <Real Name="Y">-163.09402</Real>
+      <Real Name="Z">188.54041</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1086.9775</Real>
+      <Real Name="Y">1521.1305</Real>
+      <Real Name="Z">-673.2782</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.66171</Real>
+      <Real Name="Y">-219.61053</Real>
+      <Real Name="Z">95.491249</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">620.03485</Real>
+      <Real Name="Y">-974.03101</Real>
+      <Real Name="Z">441.56821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.2168</Real>
+      <Real Name="Y">29.398201</Real>
+      <Real Name="Z">149.53085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.6862</Real>
+      <Real Name="Y">92.882713</Real>
+      <Real Name="Z">-36.452312</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-914.89764</Real>
+      <Real Name="Y">119.96918</Real>
+      <Real Name="Z">-128.94379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-822.6358</Real>
+      <Real Name="Y">-1220.017</Real>
+      <Real Name="Z">114.36189</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">707.68378</Real>
+      <Real Name="Y">941.33618</Real>
+      <Real Name="Z">60.082314</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">68.118355</Real>
+      <Real Name="Y">251.30519</Real>
+      <Real Name="Z">-76.473816</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-136.63136</Real>
+      <Real Name="Y">-697.25623</Real>
+      <Real Name="Z">809.2373</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.8913</Real>
+      <Real Name="Y">509.84113</Real>
+      <Real Name="Z">-581.41943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.67886</Real>
+      <Real Name="Y">79.15834</Real>
+      <Real Name="Z">-119.01872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">69.394867</Real>
+      <Real Name="Y">-148.72679</Real>
+      <Real Name="Z">342.33685</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.626726</Real>
+      <Real Name="Y">255.32982</Real>
+      <Real Name="Z">-74.753937</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.06201</Real>
+      <Real Name="Y">369.95981</Real>
+      <Real Name="Z">26.579353</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">82.454201</Real>
+      <Real Name="Y">466.0347</Real>
+      <Real Name="Z">-66.964439</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.497044</Real>
+      <Real Name="Y">-170.16011</Real>
+      <Real Name="Z">64.423462</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.73671</Real>
+      <Real Name="Y">-177.85536</Real>
+      <Real Name="Z">21.967014</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.705185</Real>
+      <Real Name="Y">767.03796</Real>
+      <Real Name="Z">75.473351</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.588104</Real>
+      <Real Name="Y">-166.13193</Real>
+      <Real Name="Z">-66.487488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.568993</Real>
+      <Real Name="Y">-724.08154</Real>
+      <Real Name="Z">-12.754246</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.510574</Real>
+      <Real Name="Y">-723.00421</Real>
+      <Real Name="Z">-210.45476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.88062</Real>
+      <Real Name="Y">626.18372</Real>
+      <Real Name="Z">111.89783</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.519997</Real>
+      <Real Name="Y">176.58563</Real>
+      <Real Name="Z">44.482582</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.842163</Real>
+      <Real Name="Y">777.96057</Real>
+      <Real Name="Z">560.51276</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.387653</Real>
+      <Real Name="Y">-180.10947</Real>
+      <Real Name="Z">-82.72142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">139.17728</Real>
+      <Real Name="Y">-585.65979</Real>
+      <Real Name="Z">-443.16635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.05496</Real>
+      <Real Name="Y">299.31772</Real>
+      <Real Name="Z">-420.42972</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">164.55542</Real>
+      <Real Name="Y">-252.1886</Real>
+      <Real Name="Z">436.97571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.03229</Real>
+      <Real Name="Y">-111.08784</Real>
+      <Real Name="Z">92.585892</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-885.43817</Real>
+      <Real Name="Y">-83.6082</Real>
+      <Real Name="Z">-57.046852</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.81207</Real>
+      <Real Name="Y">-69.788956</Real>
+      <Real Name="Z">3.6640511</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">312.90143</Real>
+      <Real Name="Y">42.177246</Real>
+      <Real Name="Z">7.3131104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1533.2123</Real>
+      <Real Name="Y">-267.98224</Real>
+      <Real Name="Z">-639.77484</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">298.84668</Real>
+      <Real Name="Y">-41.605171</Real>
+      <Real Name="Z">203.82086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1071.9005</Real>
+      <Real Name="Y">337.55316</Real>
+      <Real Name="Z">324.01285</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-412.06342</Real>
+      <Real Name="Y">1395.422</Real>
+      <Real Name="Z">324.65247</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.269663</Real>
+      <Real Name="Y">-723.13293</Real>
+      <Real Name="Z">-590.00214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">383.87311</Real>
+      <Real Name="Y">-438.32825</Real>
+      <Real Name="Z">485.93811</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.20433</Real>
+      <Real Name="Y">440.55023</Real>
+      <Real Name="Z">126.63379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.497604</Real>
+      <Real Name="Y">-125.11058</Real>
+      <Real Name="Z">-82.321968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-223.47771</Real>
+      <Real Name="Y">-496.10773</Real>
+      <Real Name="Z">-108.89548</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1155.9442</Real>
+      <Real Name="Y">-1972.644</Real>
+      <Real Name="Z">678.10028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-122.95121</Real>
+      <Real Name="Y">1194.1389</Real>
+      <Real Name="Z">314.51654</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1030.7716</Real>
+      <Real Name="Y">372.63919</Real>
+      <Real Name="Z">-811.24615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">776.89423</Real>
+      <Real Name="Y">-1294.8624</Real>
+      <Real Name="Z">-289.82108</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-239.76584</Real>
+      <Real Name="Y">679.80609</Real>
+      <Real Name="Z">20.613815</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.59177</Real>
+      <Real Name="Y">693.43573</Real>
+      <Real Name="Z">319.41367</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">85.824654</Real>
+      <Real Name="Y">842.82404</Real>
+      <Real Name="Z">1349.6887</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-105.1994</Real>
+      <Real Name="Y">-723.44421</Real>
+      <Real Name="Z">-1015.3097</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.93722</Real>
+      <Real Name="Y">7.4774971</Real>
+      <Real Name="Z">-260.68109</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwtogether_d.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwtogether_d.xml
new file mode 100644 (file)
index 0000000..6c04dda
--- /dev/null
@@ -0,0 +1,1826 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVremain/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-49.748861754355445</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-49.087635938958016</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-48.420827371129846</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-47.757143864144922</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-47.120764644084218</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-46.553220462816782</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-46.107243666004528</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-45.836046753043775</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-45.782058072440648</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-45.96877120384098</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-46.397469313343358</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-47.048555043756124</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-47.886202371873125</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-48.865196495780353</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-49.936032414296307</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-51.053423878720707</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-52.178924340645324</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-53.283508320132796</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-54.347672028882215</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-55.359465702492002</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-56.313432087825802</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1477.1836915709043</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1476.990405235666</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1474.9051161998627</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1470.9320546350946</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1465.3321195953495</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1458.5833257301852</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1451.2966323762541</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1444.0915921724641</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1437.4719801448696</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1431.7432330877066</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1427.0030472752987</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1423.1947711274092</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1420.1967174478646</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1417.9129361693549</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1416.323432836374</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1415.4872340564291</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1415.4889628136593</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1416.3650553433285</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1418.0473350079105</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1420.351359261517</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1423.017381532319</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">617.33281896591268</Real>
+      <Real Name="Y">-780.49676281198981</Real>
+      <Real Name="Z">-86.699539787811872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-169.13998866038679</Real>
+      <Real Name="Y">-251.97194999928769</Real>
+      <Real Name="Z">32.740740711687224</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-454.91985722935533</Real>
+      <Real Name="Y">275.33664509379014</Real>
+      <Real Name="Z">-220.35904592770066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.52055470449203</Real>
+      <Real Name="Y">187.7520939091437</Real>
+      <Real Name="Z">25.40910975323585</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">805.53807717972325</Real>
+      <Real Name="Y">669.27226409491709</Real>
+      <Real Name="Z">-34.906400373980262</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-23.661620930942707</Real>
+      <Real Name="Y">322.58532971909671</Real>
+      <Real Name="Z">-212.34496838171287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-963.71046461137894</Real>
+      <Real Name="Y">320.06678706303597</Real>
+      <Real Name="Z">1073.1386585326015</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">17.536146575457384</Real>
+      <Real Name="Y">-305.06439144253608</Real>
+      <Real Name="Z">-406.89714976481901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">344.26132992259255</Real>
+      <Real Name="Y">-377.20429520577102</Real>
+      <Real Name="Z">-211.73543904165814</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.9349780592028765</Real>
+      <Real Name="Y">-32.546972377904353</Real>
+      <Real Name="Z">22.990667953771066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.7133245769255012</Real>
+      <Real Name="Y">28.822936225927222</Real>
+      <Real Name="Z">-13.923740205037745</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.9800221436996424</Real>
+      <Real Name="Y">15.073803051964724</Real>
+      <Real Name="Z">-5.1402108960043549</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.5746113062247957</Real>
+      <Real Name="Y">20.934916970466077</Real>
+      <Real Name="Z">-6.5520578062834431</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.8368331285853969</Real>
+      <Real Name="Y">-8.3828434988136848</Real>
+      <Real Name="Z">4.622120396472738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.4695981288425912</Real>
+      <Real Name="Y">-23.645080965029138</Real>
+      <Real Name="Z">4.1255452750668482</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.90525990943109</Real>
+      <Real Name="Y">-59.310420122003336</Real>
+      <Real Name="Z">-670.22313973234179</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.94361889505495</Real>
+      <Real Name="Y">36.18424288291871</Real>
+      <Real Name="Z">385.22751662592833</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.689853709598836</Real>
+      <Real Name="Y">-11.371020483853332</Real>
+      <Real Name="Z">206.43117078391793</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">372.76393928079415</Real>
+      <Real Name="Y">415.08998718738826</Real>
+      <Real Name="Z">-106.87946620383809</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-40.294412396577428</Real>
+      <Real Name="Y">-233.29610649536724</Real>
+      <Real Name="Z">90.301838392086268</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-356.02648456891313</Real>
+      <Real Name="Y">-587.08540925728016</Real>
+      <Real Name="Z">251.21187497169115</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">647.47182106807213</Real>
+      <Real Name="Y">-496.59681752404015</Real>
+      <Real Name="Z">-423.58143407053132</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.63354739051852</Real>
+      <Real Name="Y">123.53794314021019</Real>
+      <Real Name="Z">75.954862075858898</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-647.96075913911875</Real>
+      <Real Name="Y">441.41788740222836</Real>
+      <Real Name="Z">220.6601815575051</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.763190598907304</Real>
+      <Real Name="Y">287.49338695476013</Real>
+      <Real Name="Z">60.571233102387765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.317858175149098</Real>
+      <Real Name="Y">-160.49480818188698</Real>
+      <Real Name="Z">-108.3566696197835</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.93066968744019</Real>
+      <Real Name="Y">-211.82093180626183</Real>
+      <Real Name="Z">-136.49940714565696</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-590.8834031288319</Real>
+      <Real Name="Y">267.35957460492745</Real>
+      <Real Name="Z">839.02782389361209</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">396.81068729422805</Real>
+      <Real Name="Y">-52.672480719703614</Real>
+      <Real Name="Z">-871.45417541628933</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">175.44814598652675</Real>
+      <Real Name="Y">-189.393327954017</Real>
+      <Real Name="Z">-62.891131784575471</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1107.5151075479537</Real>
+      <Real Name="Y">592.51299806274744</Real>
+      <Real Name="Z">848.62828066738018</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.84166284848152</Real>
+      <Real Name="Y">-854.79787303268745</Real>
+      <Real Name="Z">-356.51805515069185</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-854.727325331305</Real>
+      <Real Name="Y">309.68897486364875</Real>
+      <Real Name="Z">-571.80851200541383</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">362.1833700793278</Real>
+      <Real Name="Y">752.28444340794886</Real>
+      <Real Name="Z">141.2365685848761</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.8102657282486092</Real>
+      <Real Name="Y">-648.95259801745203</Real>
+      <Real Name="Z">-51.363530208067928</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-92.773216395730785</Real>
+      <Real Name="Y">-130.48734910608118</Real>
+      <Real Name="Z">-35.331129092386121</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.514864059186706</Real>
+      <Real Name="Y">-155.12439173487491</Real>
+      <Real Name="Z">-891.4182414110461</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-93.937856697322005</Real>
+      <Real Name="Y">26.178871160232994</Real>
+      <Real Name="Z">254.1770275055203</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">393.79700074187622</Real>
+      <Real Name="Y">-9.9351921279301934</Real>
+      <Real Name="Z">679.60231612057896</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">110.12377130678296</Real>
+      <Real Name="Y">49.639334217998623</Real>
+      <Real Name="Z">-862.86148050730458</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-257.10334228994049</Real>
+      <Real Name="Y">161.30506965811571</Real>
+      <Real Name="Z">485.82910933807858</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.701822515806761</Real>
+      <Real Name="Y">-197.45497266922177</Real>
+      <Real Name="Z">167.65701938888733</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-630.30811985391165</Real>
+      <Real Name="Y">82.255372593030955</Real>
+      <Real Name="Z">360.92395722605045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">543.21096168403358</Real>
+      <Real Name="Y">-65.623887786286929</Real>
+      <Real Name="Z">-8.2373414002724683</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">137.03250447137728</Real>
+      <Real Name="Y">-33.425666439814933</Real>
+      <Real Name="Z">-245.8404772817581</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-102.85722873113403</Real>
+      <Real Name="Y">33.589668564068489</Real>
+      <Real Name="Z">361.58584583556137</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">245.42391884453798</Real>
+      <Real Name="Y">-3.5074313148496259</Real>
+      <Real Name="Z">-127.53563695611086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">57.408650095689779</Real>
+      <Real Name="Y">-112.90288700915622</Real>
+      <Real Name="Z">-114.63355538206797</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-343.6852660740995</Real>
+      <Real Name="Y">-2053.6295863431692</Real>
+      <Real Name="Z">-698.38840507402085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">334.17987419579066</Real>
+      <Real Name="Y">1319.8078347447668</Real>
+      <Real Name="Z">317.98314365553233</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">275.01981782917386</Real>
+      <Real Name="Y">413.62395320898486</Real>
+      <Real Name="Z">71.078291514337906</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">432.78374991752639</Real>
+      <Real Name="Y">1006.4028708916649</Real>
+      <Real Name="Z">-844.37434191462216</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-151.4674486063648</Real>
+      <Real Name="Y">-538.99033684355607</Real>
+      <Real Name="Z">229.15193602088164</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.52917961414101</Real>
+      <Real Name="Y">-402.38903458847921</Real>
+      <Real Name="Z">560.7202028222074</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">51.855188521841185</Real>
+      <Real Name="Y">95.834303728356858</Real>
+      <Real Name="Z">3.4298893603995495</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.051540676382952</Real>
+      <Real Name="Y">-42.181013504963431</Real>
+      <Real Name="Z">-2.5009690678217638</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.626223953073637</Real>
+      <Real Name="Y">-31.758545715974819</Real>
+      <Real Name="Z">9.4049221252447737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-440.19132353774444</Real>
+      <Real Name="Y">980.26083381458318</Real>
+      <Real Name="Z">399.87565128406175</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">607.22450250852069</Real>
+      <Real Name="Y">-691.54618623260478</Real>
+      <Real Name="Z">-454.94059046544129</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">36.138311760698528</Real>
+      <Real Name="Y">-157.08604495342831</Real>
+      <Real Name="Z">-17.092210527733961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">213.9111539439354</Real>
+      <Real Name="Y">-488.60574186180008</Real>
+      <Real Name="Z">-1414.5389703237483</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-65.929894089759358</Real>
+      <Real Name="Y">354.58184595850582</Real>
+      <Real Name="Z">1043.2616248077886</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-331.36843136322017</Real>
+      <Real Name="Y">19.884067961441374</Real>
+      <Real Name="Z">245.51605409744408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.34690468616799</Real>
+      <Real Name="Y">-124.99083160099102</Real>
+      <Real Name="Z">-118.82333080270479</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.354766174332994</Real>
+      <Real Name="Y">126.48785273524096</Real>
+      <Real Name="Z">73.656493840053997</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-63.521842687872329</Real>
+      <Real Name="Y">41.177009763353041</Real>
+      <Real Name="Z">151.44376225440703</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">330.50825127007829</Real>
+      <Real Name="Y">-339.71649852536177</Real>
+      <Real Name="Z">-72.239918155369423</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.56777052518521</Real>
+      <Real Name="Y">414.87814847133444</Real>
+      <Real Name="Z">96.132970222831716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.74096152541533</Real>
+      <Real Name="Y">68.079917967262332</Real>
+      <Real Name="Z">39.450580744061426</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">755.08127156793341</Real>
+      <Real Name="Y">-267.23578119289016</Real>
+      <Real Name="Z">-577.4678000810469</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-353.18023762402026</Real>
+      <Real Name="Y">255.45159587366834</Real>
+      <Real Name="Z">608.131121467139</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-268.47894567013401</Real>
+      <Real Name="Y">283.2490882609199</Real>
+      <Real Name="Z">151.70445572497334</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.90287208431354316</Real>
+      <Real Name="Y">-9.6516639194486515</Real>
+      <Real Name="Z">5.705197975286552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.093035494872424351</Real>
+      <Real Name="Y">6.2511145265127084</Real>
+      <Real Name="Z">-1.3742659425865327</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.3118780340250211</Real>
+      <Real Name="Y">4.2108735521397378</Real>
+      <Real Name="Z">-5.0405609684854333</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.94838302003222</Real>
+      <Real Name="Y">856.92006963504048</Real>
+      <Real Name="Z">245.18011557256489</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">287.25952285462711</Real>
+      <Real Name="Y">-483.8422725798394</Real>
+      <Real Name="Z">-678.7851477576877</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-314.17978541950123</Real>
+      <Real Name="Y">-444.65481397640224</Real>
+      <Real Name="Z">296.65945508060042</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.76538001600795</Real>
+      <Real Name="Y">28.093833794926422</Real>
+      <Real Name="Z">-151.09628142978752</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.91015240344825</Real>
+      <Real Name="Y">59.995680945232181</Real>
+      <Real Name="Z">39.737018660195858</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.482996838557625</Real>
+      <Real Name="Y">-36.650240204393604</Real>
+      <Real Name="Z">73.755509299722931</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-557.96513721165843</Real>
+      <Real Name="Y">-1285.3939809685339</Real>
+      <Real Name="Z">-314.92539370319054</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">201.6077304218918</Real>
+      <Real Name="Y">618.40411471040386</Real>
+      <Real Name="Z">-286.63586900407137</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">379.58467235955692</Real>
+      <Real Name="Y">652.41167966329454</Real>
+      <Real Name="Z">624.79330759036588</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1195.425655139921</Real>
+      <Real Name="Y">443.18550850367512</Real>
+      <Real Name="Z">-793.37392689318358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">820.3224734593739</Real>
+      <Real Name="Y">-311.45012921421915</Real>
+      <Real Name="Z">572.69722259680793</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.03715928095986</Real>
+      <Real Name="Y">-31.905442551436877</Real>
+      <Real Name="Z">39.745769343777283</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">219.05689346212338</Real>
+      <Real Name="Y">427.98772059727088</Real>
+      <Real Name="Z">165.94672647109283</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-29.750384407303034</Real>
+      <Real Name="Y">-133.48829740143859</Real>
+      <Real Name="Z">-72.059053864228815</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-109.59951083061291</Real>
+      <Real Name="Y">-123.34232674542901</Real>
+      <Real Name="Z">-17.816776793285591</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.6156234689315</Real>
+      <Real Name="Y">-1076.003951797212</Real>
+      <Real Name="Z">-858.07689465303997</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.17024807697692</Real>
+      <Real Name="Y">210.20924954190514</Real>
+      <Real Name="Z">195.34161374297369</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">576.18306362041187</Real>
+      <Real Name="Y">692.08327477573039</Real>
+      <Real Name="Z">681.30174006118204</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.8408756489701</Real>
+      <Real Name="Y">387.26836611575391</Real>
+      <Real Name="Z">-1021.8307359340095</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.936032183589262</Real>
+      <Real Name="Y">-316.5633633008506</Real>
+      <Real Name="Z">752.73091798599819</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.991935294630522</Real>
+      <Real Name="Y">33.419913543387089</Real>
+      <Real Name="Z">254.10161477724773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.33519114913059</Real>
+      <Real Name="Y">-513.67836006669586</Real>
+      <Real Name="Z">-928.87456724612525</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.64430382211901</Real>
+      <Real Name="Y">80.17521936755432</Real>
+      <Real Name="Z">145.86590001358451</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.543793831619112</Real>
+      <Real Name="Y">257.67747448123589</Real>
+      <Real Name="Z">803.70055544945149</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-504.87703335877836</Real>
+      <Real Name="Y">-155.16034355347688</Real>
+      <Real Name="Z">-164.86457532171269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.19195637360579</Real>
+      <Real Name="Y">16.230024527759362</Real>
+      <Real Name="Z">135.49445239090522</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">238.37217087916977</Real>
+      <Real Name="Y">47.363931298997535</Real>
+      <Real Name="Z">105.86329079320791</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">589.66163889749532</Real>
+      <Real Name="Y">826.87348064881678</Real>
+      <Real Name="Z">-262.17181120767913</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.52001313418374</Real>
+      <Real Name="Y">-202.67862887024151</Real>
+      <Real Name="Z">15.754529213651765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-461.92561378948926</Real>
+      <Real Name="Y">-869.53689825137712</Real>
+      <Real Name="Z">-37.157221661206606</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1693.0636321817162</Real>
+      <Real Name="Y">-174.14882185304083</Real>
+      <Real Name="Z">896.7911594567513</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-862.94995710660362</Real>
+      <Real Name="Y">314.30460200894777</Real>
+      <Real Name="Z">-687.11155881341733</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-322.71522553350064</Real>
+      <Real Name="Y">-175.96913921373513</Real>
+      <Real Name="Z">-374.29044570694981</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-154.67100997200717</Real>
+      <Real Name="Y">55.154563354205962</Real>
+      <Real Name="Z">125.81491191836821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.54327505844429</Real>
+      <Real Name="Y">-162.45402542073677</Real>
+      <Real Name="Z">29.749692846165843</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">199.44655232527339</Real>
+      <Real Name="Y">-7.2463752113825475</Real>
+      <Real Name="Z">25.003962578884618</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">725.2101511518448</Real>
+      <Real Name="Y">-430.76322184027435</Real>
+      <Real Name="Z">7.2028014490889234</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-512.58409845475808</Real>
+      <Real Name="Y">294.11484741468178</Real>
+      <Real Name="Z">37.104134201585836</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-414.48924667557799</Real>
+      <Real Name="Y">350.93844749545735</Real>
+      <Real Name="Z">-121.14144391715614</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.1055671613467</Real>
+      <Real Name="Y">-1820.3757559171986</Real>
+      <Real Name="Z">38.143328668058018</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">130.12295976727256</Real>
+      <Real Name="Y">241.66161837279432</Real>
+      <Real Name="Z">-35.208072608269788</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.01875797541962</Real>
+      <Real Name="Y">1228.6038493474841</Real>
+      <Real Name="Z">-58.758618357115779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">436.39856742431476</Real>
+      <Real Name="Y">1034.9412165932338</Real>
+      <Real Name="Z">857.30418055379971</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-312.27589470430235</Real>
+      <Real Name="Y">-645.20548515479413</Real>
+      <Real Name="Z">-580.97274593144209</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-59.26130526069042</Real>
+      <Real Name="Y">-239.47577413583252</Real>
+      <Real Name="Z">-227.7952330908293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">40.696277726396218</Real>
+      <Real Name="Y">-377.21195615004763</Real>
+      <Real Name="Z">-473.37970897863033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-196.81007831223314</Real>
+      <Real Name="Y">346.03974113735683</Real>
+      <Real Name="Z">245.53794869435433</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-125.08963101514334</Real>
+      <Real Name="Y">145.33763213196659</Real>
+      <Real Name="Z">477.83760159084426</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">778.95854066350739</Real>
+      <Real Name="Y">-311.18790033365042</Real>
+      <Real Name="Z">-241.47657388580109</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.85479206669925</Real>
+      <Real Name="Y">453.18065534823381</Real>
+      <Real Name="Z">4.5314856163097907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-742.05728461530578</Real>
+      <Real Name="Y">-154.62700340786608</Real>
+      <Real Name="Z">176.70148137339874</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1096.1038075981498</Real>
+      <Real Name="Y">1502.3928371070558</Real>
+      <Real Name="Z">-674.96425773535714</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.17130933436175</Real>
+      <Real Name="Y">-212.01626988017523</Real>
+      <Real Name="Z">94.944479024985668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">626.79899555749807</Real>
+      <Real Name="Y">-964.20248505944892</Real>
+      <Real Name="Z">443.68091887072086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">744.16186014984032</Real>
+      <Real Name="Y">22.960880127378033</Real>
+      <Real Name="Z">144.31310105015524</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.01131004042725</Real>
+      <Real Name="Y">95.557187406408161</Real>
+      <Real Name="Z">-34.543537145871809</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-916.08065265160087</Real>
+      <Real Name="Y">123.1769495324443</Real>
+      <Real Name="Z">-126.2418102464388</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-798.04204985983006</Real>
+      <Real Name="Y">-1242.7557983995825</Real>
+      <Real Name="Z">108.21409879417342</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">700.27887147105423</Real>
+      <Real Name="Y">953.69482002084885</Real>
+      <Real Name="Z">62.574470818033092</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">58.252500480966347</Real>
+      <Real Name="Y">258.16685503157578</Real>
+      <Real Name="Z">-74.889780838012797</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.15642534479917</Real>
+      <Real Name="Y">-691.18796905320653</Real>
+      <Real Name="Z">812.94310385163851</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.8917907393481</Real>
+      <Real Name="Y">504.39536629263785</Real>
+      <Real Name="Z">-583.37570922149871</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-53.484677641090158</Real>
+      <Real Name="Y">76.17122435068471</Real>
+      <Real Name="Z">-119.49623434067794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.589426579265691</Real>
+      <Real Name="Y">-129.65238554696057</Real>
+      <Real Name="Z">309.32933503794004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-25.436804707623061</Real>
+      <Real Name="Y">250.85395381169238</Real>
+      <Real Name="Z">-60.291980128410508</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-290.61796792348753</Real>
+      <Real Name="Y">360.50071138770284</Real>
+      <Real Name="Z">45.284729621870895</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">83.77334233790755</Real>
+      <Real Name="Y">479.87806870139349</Real>
+      <Real Name="Z">-73.341110014924197</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.435579079417145</Real>
+      <Real Name="Y">-177.02850713466208</Real>
+      <Real Name="Z">67.395780747796238</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.986825804632154</Real>
+      <Real Name="Y">-187.59691760746853</Real>
+      <Real Name="Z">27.103978075797166</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">78.773027732366444</Real>
+      <Real Name="Y">771.31478642813397</Real>
+      <Real Name="Z">73.895525057534371</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.361487765087844</Real>
+      <Real Name="Y">-168.63476388410712</Real>
+      <Real Name="Z">-65.57686828806952</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.227544458225879</Real>
+      <Real Name="Y">-727.33683078838408</Real>
+      <Real Name="Z">-11.528329045214861</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.366014775933195</Real>
+      <Real Name="Y">-724.96349652276706</Real>
+      <Real Name="Z">-207.0277418585963</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.96559264133325</Real>
+      <Real Name="Y">627.13405674275327</Real>
+      <Real Name="Z">110.45779583239684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.24826563672039</Real>
+      <Real Name="Y">177.91391982390414</Real>
+      <Real Name="Z">42.45721735974513</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.941076883330922</Real>
+      <Real Name="Y">814.14019046836881</Real>
+      <Real Name="Z">588.21610063177843</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">24.188482969290234</Real>
+      <Real Name="Y">-185.5811597914583</Real>
+      <Real Name="Z">-105.79506652308595</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">130.35300148402598</Real>
+      <Real Name="Y">-616.07956597636507</Real>
+      <Real Name="Z">-446.3922398279505</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.98220703118125</Real>
+      <Real Name="Y">310.52745983907829</Real>
+      <Real Name="Z">-419.0326906909894</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">161.59489047871489</Real>
+      <Real Name="Y">-258.08242819820498</Real>
+      <Real Name="Z">437.42570069199826</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">146.62509375458473</Real>
+      <Real Name="Y">-119.18902675018859</Real>
+      <Real Name="Z">90.629335138422448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-881.5688710746665</Real>
+      <Real Name="Y">-102.95718594797395</Real>
+      <Real Name="Z">-59.488578734613654</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.73279158921468</Real>
+      <Real Name="Y">-59.389446804499492</Real>
+      <Real Name="Z">5.5883890999756787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">311.63311836305377</Real>
+      <Real Name="Y">50.356167508328156</Real>
+      <Real Name="Z">7.1670339717353357</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1521.3603745924952</Real>
+      <Real Name="Y">-274.71291544024564</Real>
+      <Real Name="Z">-653.08673195594633</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">294.19116166533848</Real>
+      <Real Name="Y">-40.116290944191647</Real>
+      <Real Name="Z">208.85196572003545</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1067.9179532090652</Real>
+      <Real Name="Y">342.88379572438561</Real>
+      <Real Name="Z">332.12468539351653</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.48103573205015</Real>
+      <Real Name="Y">1438.0037781109183</Real>
+      <Real Name="Z">325.95371678370833</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.0096636607556029</Real>
+      <Real Name="Y">-736.71160479161506</Real>
+      <Real Name="Z">-586.96294127464046</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">373.00146565442935</Real>
+      <Real Name="Y">-458.66234121058739</Real>
+      <Real Name="Z">485.56024454518047</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">201.63928893236084</Real>
+      <Real Name="Y">437.37252787089113</Real>
+      <Real Name="Z">127.21559787957028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-67.345047008907613</Real>
+      <Real Name="Y">-123.37811163885243</Real>
+      <Real Name="Z">-82.543010903772711</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.36844707845253</Real>
+      <Real Name="Y">-494.58949915761917</Real>
+      <Real Name="Z">-109.36213181017386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1162.1333262288767</Real>
+      <Real Name="Y">-2017.4161833975334</Real>
+      <Real Name="Z">666.98157111050625</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.03424354195855</Real>
+      <Real Name="Y">1230.6159594746828</Real>
+      <Real Name="Z">320.18006676510373</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1029.7612211034962</Real>
+      <Real Name="Y">392.19107422107152</Real>
+      <Real Name="Z">-802.41822122759822</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">786.08274702082872</Real>
+      <Real Name="Y">-1308.1093327628923</Real>
+      <Real Name="Z">-296.95190333548214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-246.3852215726352</Real>
+      <Real Name="Y">685.94618951477787</Real>
+      <Real Name="Z">25.197797050381013</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-501.07697400405613</Real>
+      <Real Name="Y">703.30776765820099</Real>
+      <Real Name="Z">323.36315239807772</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">76.648204201512726</Real>
+      <Real Name="Y">818.49355452792645</Real>
+      <Real Name="Z">1329.3804071468894</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-102.86174394969622</Real>
+      <Real Name="Y">-715.76159900622258</Real>
+      <Real Name="Z">-1007.6072910112118</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-106.73382954630682</Real>
+      <Real Name="Y">19.042014532171805</Real>
+      <Real Name="Z">-246.05458796681086</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.020000 Step 20 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-1181.7926203424859</Real>
+      <Real Name="Y">493.25511832601291</Real>
+      <Real Name="Z">1283.0442370849159</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-106.85410863426783</Real>
+      <Real Name="Y">-108.40499843530579</Real>
+      <Real Name="Z">-91.450272767576763</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">392.50272291204192</Real>
+      <Real Name="Y">-9.9803236967717925</Real>
+      <Real Name="Z">-236.26211529911734</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">355.01158487783778</Real>
+      <Real Name="Y">-485.82588484744844</Real>
+      <Real Name="Z">-591.39772284542494</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1079.5905055486799</Real>
+      <Real Name="Y">298.75852235003521</Real>
+      <Real Name="Z">-573.11129079530394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-658.62110643858387</Real>
+      <Real Name="Y">-450.73243662731801</Real>
+      <Real Name="Z">-47.035502571484322</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">167.35192017903535</Real>
+      <Real Name="Y">864.90539146124729</Real>
+      <Real Name="Z">285.2599714863963</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">104.21326595559526</Real>
+      <Real Name="Y">-252.5339141983911</Real>
+      <Real Name="Z">107.07611384678944</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.4787966224321</Real>
+      <Real Name="Y">-298.97476215000614</Real>
+      <Real Name="Z">-158.03070172666995</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.9171682144966837</Real>
+      <Real Name="Y">1.6673832908497932</Real>
+      <Real Name="Z">17.868228321342535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.7191315837015821</Real>
+      <Real Name="Y">7.9972061548417832</Real>
+      <Real Name="Z">-3.6847433726213765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.751263505051682</Real>
+      <Real Name="Y">1.9159546872775914</Real>
+      <Real Name="Z">-9.8489023309630674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.755928056322944</Real>
+      <Real Name="Y">7.6991490334788111</Real>
+      <Real Name="Z">-26.111028937820137</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.2349218713297887</Real>
+      <Real Name="Y">-2.6384268536519393</Real>
+      <Real Name="Z">12.64666760750919</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.618408053773479</Real>
+      <Real Name="Y">-13.593795231396376</Real>
+      <Real Name="Z">11.337699978005169</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">286.46124673597814</Real>
+      <Real Name="Y">-118.44100934073408</Real>
+      <Real Name="Z">-750.10607525003832</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.35909546164578</Real>
+      <Real Name="Y">202.02544545757681</Real>
+      <Real Name="Z">405.62849211025025</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.452768058948791</Real>
+      <Real Name="Y">9.7962494829856528</Real>
+      <Real Name="Z">174.62512910242421</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">430.90184013822744</Real>
+      <Real Name="Y">657.88309463162523</Real>
+      <Real Name="Z">-204.23384382570947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.5196419809045</Real>
+      <Real Name="Y">-257.74447492695072</Real>
+      <Real Name="Z">71.418023683412997</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-390.78757056313452</Real>
+      <Real Name="Y">-623.15459130729596</Real>
+      <Real Name="Z">282.68483428819309</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">434.23842216179816</Real>
+      <Real Name="Y">-421.415929133997</Real>
+      <Real Name="Z">-481.06903208649959</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-77.650942898424532</Real>
+      <Real Name="Y">145.12447229938147</Real>
+      <Real Name="Z">124.49531570536763</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-450.60051099401517</Real>
+      <Real Name="Y">415.31420531824875</Real>
+      <Real Name="Z">459.72502827368385</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">120.48760149280297</Real>
+      <Real Name="Y">225.41503968780827</Real>
+      <Real Name="Z">282.29497981599127</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.153039052270984</Real>
+      <Real Name="Y">-162.6105847324462</Real>
+      <Real Name="Z">-134.28117624829784</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-184.29552685131981</Real>
+      <Real Name="Y">-241.48267614196416</Real>
+      <Real Name="Z">-144.93461909336924</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-427.02012624762023</Real>
+      <Real Name="Y">386.98221707492428</Real>
+      <Real Name="Z">878.96055904690331</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">401.69141832283793</Real>
+      <Real Name="Y">-253.30106341433068</Real>
+      <Real Name="Z">-823.29294730618358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">154.80163784752474</Real>
+      <Real Name="Y">-221.29795186423485</Real>
+      <Real Name="Z">-115.88637727307655</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1250.5526843687694</Real>
+      <Real Name="Y">317.77775539848261</Real>
+      <Real Name="Z">823.78223086673381</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">34.668871229515538</Real>
+      <Real Name="Y">-532.00391171308672</Real>
+      <Real Name="Z">-194.64711071790001</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-970.99084127192418</Real>
+      <Real Name="Y">519.72389353269136</Real>
+      <Real Name="Z">-541.69792367099797</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">253.51049666388116</Real>
+      <Real Name="Y">1009.3716656572485</Real>
+      <Real Name="Z">171.04564739602964</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-82.125729104630409</Real>
+      <Real Name="Y">-375.11024511459419</Real>
+      <Real Name="Z">-50.276243264970773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.642214686764291</Real>
+      <Real Name="Y">-180.65263493401602</Real>
+      <Real Name="Z">-54.841227192630981</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-120.10164873612752</Real>
+      <Real Name="Y">25.673444321489299</Real>
+      <Real Name="Z">-998.45360068274726</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-127.02309119614293</Real>
+      <Real Name="Y">254.38525181944979</Real>
+      <Real Name="Z">310.6093346440619</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">301.58386408257979</Real>
+      <Real Name="Y">130.15746329080639</Real>
+      <Real Name="Z">305.08462484157883</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">493.27400067417034</Real>
+      <Real Name="Y">214.42467496170309</Real>
+      <Real Name="Z">-392.60706578533791</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-482.8504286491189</Real>
+      <Real Name="Y">72.376725997868022</Real>
+      <Real Name="Z">656.81663752596046</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-177.56113620446496</Real>
+      <Real Name="Y">-230.56814831394763</Real>
+      <Real Name="Z">49.466716311698036</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-964.34941622728331</Real>
+      <Real Name="Y">184.61583534435002</Real>
+      <Real Name="Z">261.81645783823279</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">590.94097382918437</Real>
+      <Real Name="Y">-317.01012934863456</Real>
+      <Real Name="Z">-158.69595584294217</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.78359060411887</Real>
+      <Real Name="Y">-0.39500477221150021</Real>
+      <Real Name="Z">-174.66959388230626</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-425.14433627499773</Real>
+      <Real Name="Y">79.732001297099032</Real>
+      <Real Name="Z">264.5694300197128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.86256530234942</Real>
+      <Real Name="Y">-1.5852110070335925</Real>
+      <Real Name="Z">-51.653339631052781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.66619794161497</Real>
+      <Real Name="Y">-191.77403306623876</Real>
+      <Real Name="Z">-95.900636085137037</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.05050705404309</Real>
+      <Real Name="Y">-1440.7896174545822</Real>
+      <Real Name="Z">-680.52233971169017</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">40.276738844083724</Real>
+      <Real Name="Y">1350.3847129786304</Real>
+      <Real Name="Z">465.2031542329492</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">366.92876319869788</Real>
+      <Real Name="Y">368.73371320656679</Real>
+      <Real Name="Z">126.76210902501907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">437.98914145117806</Real>
+      <Real Name="Y">757.68140898657975</Real>
+      <Real Name="Z">-889.87023330372472</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">162.14801587342222</Real>
+      <Real Name="Y">-457.40483359760958</Real>
+      <Real Name="Z">28.73110606748125</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-412.31569346739974</Real>
+      <Real Name="Y">-171.37271199394826</Real>
+      <Real Name="Z">302.980736792247</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.899488156738741</Real>
+      <Real Name="Y">127.31269597627303</Real>
+      <Real Name="Z">34.252898980114246</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-16.123499162158605</Real>
+      <Real Name="Y">-51.936144070867336</Real>
+      <Real Name="Z">-13.646482649162365</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-31.46143963388829</Real>
+      <Real Name="Y">-42.603454943153977</Real>
+      <Real Name="Z">-12.212756908735308</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-382.03095018449466</Real>
+      <Real Name="Y">918.31550338902514</Real>
+      <Real Name="Z">240.01084707786006</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">446.93516382452742</Real>
+      <Real Name="Y">-700.03330563841553</Real>
+      <Real Name="Z">-410.87470140932157</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">26.218561048120748</Real>
+      <Real Name="Y">-157.43940252392125</Real>
+      <Real Name="Z">-25.90767158324762</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">577.00850291261327</Real>
+      <Real Name="Y">-698.61452518855447</Real>
+      <Real Name="Z">-1253.4646133052072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-392.62494512681155</Real>
+      <Real Name="Y">287.58675329535799</Real>
+      <Real Name="Z">986.82896974115783</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-427.77393521701589</Real>
+      <Real Name="Y">182.45384243895182</Real>
+      <Real Name="Z">75.457514172914287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">42.544304824332499</Real>
+      <Real Name="Y">-259.88676182697549</Real>
+      <Real Name="Z">22.503380875210198</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.38836757274305</Real>
+      <Real Name="Y">113.3469140784365</Real>
+      <Real Name="Z">34.957225160434547</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-37.747883595077141</Real>
+      <Real Name="Y">65.337880040207637</Real>
+      <Real Name="Z">129.64186121032</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">333.79753387749037</Real>
+      <Real Name="Y">-307.49072193807905</Real>
+      <Real Name="Z">-98.526252590258196</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-311.8328680627809</Real>
+      <Real Name="Y">377.88529328274348</Real>
+      <Real Name="Z">156.90121211028566</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-88.136227311095851</Real>
+      <Real Name="Y">46.097149850651618</Real>
+      <Real Name="Z">53.94524854595695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">713.45990673377582</Real>
+      <Real Name="Y">-524.1889110142032</Real>
+      <Real Name="Z">-655.83926490229533</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-455.48775988609913</Real>
+      <Real Name="Y">226.47985271611401</Real>
+      <Real Name="Z">750.83699763355469</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-189.64577624905408</Real>
+      <Real Name="Y">252.16004806902603</Real>
+      <Real Name="Z">217.53242006938808</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.2012251216854466</Real>
+      <Real Name="Y">-11.783282293614453</Real>
+      <Real Name="Z">27.247860726150321</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.0432473449831008</Real>
+      <Real Name="Y">6.0922560335519762</Real>
+      <Real Name="Z">-10.122761232428061</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.37107228311221974</Real>
+      <Real Name="Y">6.3011800273888765</Real>
+      <Real Name="Z">-15.769787347542987</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-159.39711495901989</Real>
+      <Real Name="Y">808.57390404683781</Real>
+      <Real Name="Z">109.61194784033266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">481.34992228195529</Real>
+      <Real Name="Y">-389.26271248495641</Real>
+      <Real Name="Z">-515.26086921268563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-205.67188142353183</Real>
+      <Real Name="Y">-359.61039280831653</Real>
+      <Real Name="Z">312.4931134100126</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">338.62669566394464</Real>
+      <Real Name="Y">44.873719898599489</Real>
+      <Real Name="Z">-321.45833892404499</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.86006960371077</Real>
+      <Real Name="Y">46.448623236663082</Real>
+      <Real Name="Z">84.535869529326646</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.621677120370961</Real>
+      <Real Name="Y">-19.31944085017258</Real>
+      <Real Name="Z">104.44763933485316</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.27357548060513</Real>
+      <Real Name="Y">-1073.1027702652266</Real>
+      <Real Name="Z">-511.06015000728598</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">149.7013053121095</Real>
+      <Real Name="Y">323.58848530587966</Real>
+      <Real Name="Z">-151.29944414631234</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">401.55223294844905</Real>
+      <Real Name="Y">696.32372809316939</Real>
+      <Real Name="Z">667.71351329144352</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-854.4046124708982</Real>
+      <Real Name="Y">326.23075862659687</Real>
+      <Real Name="Z">-505.08906644832797</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">678.43921283264842</Real>
+      <Real Name="Y">-269.15341838824685</Real>
+      <Real Name="Z">484.69630488779194</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">170.36955472199122</Real>
+      <Real Name="Y">-1.4499829508004041</Real>
+      <Real Name="Z">32.808968419892281</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">254.23821228822379</Real>
+      <Real Name="Y">426.08880275941311</Real>
+      <Real Name="Z">77.827015626043135</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.279695085263832</Real>
+      <Real Name="Y">-153.49920242965604</Real>
+      <Real Name="Z">-62.395298808130057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.40756435300229</Real>
+      <Real Name="Y">-151.16824288756811</Real>
+      <Real Name="Z">11.821983148086998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-817.30634905100601</Real>
+      <Real Name="Y">-793.12419970434291</Real>
+      <Real Name="Z">-518.71654206917856</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">148.84075605333948</Real>
+      <Real Name="Y">199.59582690055205</Real>
+      <Real Name="Z">153.49970364807621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">706.034463998256</Real>
+      <Real Name="Y">518.88448073265931</Real>
+      <Real Name="Z">393.82741856307081</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.623841181437172</Real>
+      <Real Name="Y">-76.114611344681961</Real>
+      <Real Name="Z">-778.7932072164142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.691318862763467</Real>
+      <Real Name="Y">-193.38444928593967</Real>
+      <Real Name="Z">728.83115623240872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.282597829156195</Real>
+      <Real Name="Y">56.482538452869456</Real>
+      <Real Name="Z">153.46466505680326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-539.7871081348394</Real>
+      <Real Name="Y">-517.09022803878986</Real>
+      <Real Name="Z">-921.70814825369973</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">124.58249697254412</Real>
+      <Real Name="Y">76.617677095964353</Real>
+      <Real Name="Z">160.48574873849606</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">248.74011622026401</Real>
+      <Real Name="Y">494.32809304850076</Real>
+      <Real Name="Z">736.61485857948071</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-467.41363472348007</Real>
+      <Real Name="Y">-432.73023314375376</Real>
+      <Real Name="Z">-58.017217848356921</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">353.51165077608283</Real>
+      <Real Name="Y">202.40216278451302</Real>
+      <Real Name="Z">-7.438686222299232</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">238.90349778915618</Real>
+      <Real Name="Y">124.56359945566463</Real>
+      <Real Name="Z">12.917637711529801</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">152.12369170628966</Real>
+      <Real Name="Y">785.75087637378658</Real>
+      <Real Name="Z">33.847794451733023</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.091663758777671</Real>
+      <Real Name="Y">-163.00890630678319</Real>
+      <Real Name="Z">-17.21866050170587</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-199.82753412491533</Real>
+      <Real Name="Y">-819.491961942334</Real>
+      <Real Name="Z">-140.36358130819076</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1843.3468321062742</Real>
+      <Real Name="Y">-179.31692060229568</Real>
+      <Real Name="Z">800.05006114690059</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1053.2765006360983</Real>
+      <Real Name="Y">239.34410101478758</Real>
+      <Real Name="Z">-626.67198892506997</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-422.3328015499942</Real>
+      <Real Name="Y">-186.46235181170763</Real>
+      <Real Name="Z">-368.79566447927311</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-304.59370089711734</Real>
+      <Real Name="Y">70.130402416580978</Real>
+      <Real Name="Z">-102.15508166513062</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.3531000820463</Real>
+      <Real Name="Y">-134.87525204484172</Real>
+      <Real Name="Z">52.622392571109557</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.06160731973648</Real>
+      <Real Name="Y">-5.2829287797106268</Real>
+      <Real Name="Z">37.875679723751077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">780.19716584575224</Real>
+      <Real Name="Y">-305.09219915191602</Real>
+      <Real Name="Z">53.964119930903578</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-707.17966534926813</Real>
+      <Real Name="Y">257.29102792805514</Real>
+      <Real Name="Z">80.992883192288076</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-364.96595089319248</Real>
+      <Real Name="Y">191.73662979074459</Real>
+      <Real Name="Z">-72.751705758614165</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-202.8395237524411</Real>
+      <Real Name="Y">-1129.1365268030161</Real>
+      <Real Name="Z">2.1103924298670762</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.571657481256622</Real>
+      <Real Name="Y">172.12475630005295</Real>
+      <Real Name="Z">-40.717030490087133</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.52556749645007</Real>
+      <Real Name="Y">1119.3958214587828</Real>
+      <Real Name="Z">33.840440400966052</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">215.61675657730078</Real>
+      <Real Name="Y">1092.9026704673943</Real>
+      <Real Name="Z">780.46312689175534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-176.44664029737356</Real>
+      <Real Name="Y">-663.82000397589923</Real>
+      <Real Name="Z">-642.33721562311985</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-27.048148555395798</Real>
+      <Real Name="Y">-290.88913983327018</Real>
+      <Real Name="Z">-134.60747102487687</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-16.287033367649101</Real>
+      <Real Name="Y">-341.90821583741081</Real>
+      <Real Name="Z">-333.4934726678382</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.6214337508495</Real>
+      <Real Name="Y">337.1737879096097</Real>
+      <Real Name="Z">-16.499827561224219</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.09425546975962</Real>
+      <Real Name="Y">57.010586555613081</Real>
+      <Real Name="Z">330.34667820842213</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">733.32016822280912</Real>
+      <Real Name="Y">-438.52810800375931</Real>
+      <Real Name="Z">-193.73620406870279</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.17903864672144</Real>
+      <Real Name="Y">535.41578841820058</Real>
+      <Real Name="Z">65.15489871460737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-618.03134746077876</Real>
+      <Real Name="Y">-80.364111342171427</Real>
+      <Real Name="Z">207.84596127518779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-971.71380731713612</Real>
+      <Real Name="Y">1174.4890163812356</Real>
+      <Real Name="Z">-652.15354923429345</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">179.84408889310583</Real>
+      <Real Name="Y">-176.50046000455345</Real>
+      <Real Name="Z">100.55795661751715</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">565.36127722209142</Real>
+      <Real Name="Y">-835.07302979607402</Real>
+      <Real Name="Z">525.10199436614096</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">938.57755304100647</Real>
+      <Real Name="Y">-121.72098603092249</Real>
+      <Real Name="Z">50.659317162592941</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.32957230595389</Real>
+      <Real Name="Y">88.891656592782681</Real>
+      <Real Name="Z">-31.64297706659519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-891.64424332677913</Real>
+      <Real Name="Y">-63.48329974234089</Real>
+      <Real Name="Z">-96.516062545200271</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-598.75622978474235</Real>
+      <Real Name="Y">-687.63594361285152</Real>
+      <Real Name="Z">139.02539631747641</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">595.83996197564261</Real>
+      <Real Name="Y">690.99804937298802</Real>
+      <Real Name="Z">25.141147248436738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">36.519286816044058</Real>
+      <Real Name="Y">126.24008415004928</Real>
+      <Real Name="Z">-76.677947825242541</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-283.09253809120207</Real>
+      <Real Name="Y">-1097.0838426534656</Real>
+      <Real Name="Z">1198.2002564379343</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">318.13634653756628</Real>
+      <Real Name="Y">619.43411244206607</Real>
+      <Real Name="Z">-557.1077876415809</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-71.597017329138552</Real>
+      <Real Name="Y">139.95622801957043</Real>
+      <Real Name="Z">-191.58596687005331</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">389.49679740165595</Real>
+      <Real Name="Y">-498.33716670363827</Real>
+      <Real Name="Z">11.565678801269925</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-89.279122737845057</Real>
+      <Real Name="Y">272.86958045098851</Real>
+      <Real Name="Z">-56.331893761321552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-403.10277439015977</Real>
+      <Real Name="Y">346.12739162646824</Real>
+      <Real Name="Z">-51.396514401196697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.80664192407386</Real>
+      <Real Name="Y">437.21161463565193</Real>
+      <Real Name="Z">-4.0838300201829938</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-43.277992944892404</Real>
+      <Real Name="Y">-212.16880470338629</Real>
+      <Real Name="Z">39.905678457398359</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">37.983574566137563</Real>
+      <Real Name="Y">-131.35066695993547</Real>
+      <Real Name="Z">2.0007445736086851</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">9.6422437086736679</Real>
+      <Real Name="Y">775.71181881207121</Real>
+      <Real Name="Z">68.008675479600072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.90346688869467</Real>
+      <Real Name="Y">-181.37257679822201</Real>
+      <Real Name="Z">-61.456433886006401</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.06514898018392</Real>
+      <Real Name="Y">-673.02948932576044</Real>
+      <Real Name="Z">-44.122307264898289</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.23748304895545</Real>
+      <Real Name="Y">-681.27093436606663</Real>
+      <Real Name="Z">-273.14265567559244</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">210.32860714423731</Real>
+      <Real Name="Y">598.72697503518145</Real>
+      <Real Name="Z">155.5896264489806</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">94.327946424087841</Real>
+      <Real Name="Y">151.94668771243084</Real>
+      <Real Name="Z">39.55624094189514</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">121.16279168262488</Real>
+      <Real Name="Y">908.18225165266767</Real>
+      <Real Name="Z">621.97604427569331</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">10.836782471395763</Real>
+      <Real Name="Y">-214.84048988544339</Real>
+      <Real Name="Z">-131.70294745298969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-170.52172555627249</Real>
+      <Real Name="Y">-703.78665898412851</Real>
+      <Real Name="Z">-472.30588674607611</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-225.06298704385239</Real>
+      <Real Name="Y">179.87239159475686</Real>
+      <Real Name="Z">-346.62351546602457</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">110.29583505007564</Real>
+      <Real Name="Y">-104.17603538862377</Real>
+      <Real Name="Z">347.29285032096317</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">126.85577791570137</Real>
+      <Real Name="Y">-65.368337938844107</Real>
+      <Real Name="Z">108.65240930420431</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-995.85826900394159</Real>
+      <Real Name="Y">11.292578607775582</Real>
+      <Real Name="Z">-57.40732932306058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">726.75559914109658</Real>
+      <Real Name="Y">-134.57214369063848</Real>
+      <Real Name="Z">-19.253755747224417</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">334.7391261311916</Real>
+      <Real Name="Y">0.68239278545043192</Real>
+      <Real Name="Z">8.3127591205029034</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1553.9593032192213</Real>
+      <Real Name="Y">-901.91536241977747</Real>
+      <Real Name="Z">-469.6472308605841</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">339.53531051515296</Real>
+      <Real Name="Y">71.203787855101368</Real>
+      <Real Name="Z">137.82572914165689</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1184.0803409801567</Real>
+      <Real Name="Y">216.62789536472886</Real>
+      <Real Name="Z">334.21891286522748</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-82.529092333768958</Real>
+      <Real Name="Y">1517.7160916930577</Real>
+      <Real Name="Z">80.749602901366984</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.281638003530645</Real>
+      <Real Name="Y">-816.28532832920075</Real>
+      <Real Name="Z">-434.76058972803162</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">202.2196845270274</Real>
+      <Real Name="Y">-626.13298937039099</Real>
+      <Real Name="Z">586.1741988325391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">245.21343570351866</Real>
+      <Real Name="Y">508.16491000537252</Real>
+      <Real Name="Z">163.2633214789891</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.586357873294105</Real>
+      <Real Name="Y">-131.56760685117936</Real>
+      <Real Name="Z">-106.94875601115177</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-260.1332413162624</Real>
+      <Real Name="Y">-514.43917170212376</Real>
+      <Real Name="Z">-77.985110049395615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">758.68062297222184</Real>
+      <Real Name="Y">-1667.0575245193613</Real>
+      <Real Name="Z">814.50166628276611</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.662431508885618</Real>
+      <Real Name="Y">1159.9848301409461</Real>
+      <Real Name="Z">116.04731840277552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-941.59387704347489</Real>
+      <Real Name="Y">557.44049500728215</Real>
+      <Real Name="Z">-732.3445615613324</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">795.52114353932029</Real>
+      <Real Name="Y">-1984.3068643923064</Real>
+      <Real Name="Z">-660.44489163774153</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-54.477941577361605</Real>
+      <Real Name="Y">840.28080746696889</Real>
+      <Real Name="Z">207.8966459703351</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-545.76565993494467</Real>
+      <Real Name="Y">678.01763463226382</Real>
+      <Real Name="Z">428.30417575316403</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">101.38394412139735</Real>
+      <Real Name="Y">737.1214824924765</Real>
+      <Real Name="Z">1190.7442589073137</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.44208724016089</Real>
+      <Real Name="Y">-587.13625396541204</Real>
+      <Real Name="Z">-1011.7529397993393</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.773197754191692</Real>
+      <Real Name="Y">-76.729007199668928</Real>
+      <Real Name="Z">-269.56026103474471</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwtogether_s.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_coulandvdwtogether_s.xml
new file mode 100644 (file)
index 0000000..b92bb79
--- /dev/null
@@ -0,0 +1,938 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVremain/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-49.749237</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-49.088078</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-48.420498</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-47.756775</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-47.120941</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-46.552887</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-46.1078</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-45.835808</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-45.781483</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-45.969391</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-46.398273</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-47.049259</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-47.886086</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-48.865166</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-49.936127</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-51.053741</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-52.179111</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-53.283607</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-54.347523</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-55.35965</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-56.313282</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1477.1995</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1477.0083</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1474.9198</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1470.9485</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1465.3501</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1458.6001</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1451.3123</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1444.1089</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1437.4869</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1431.761</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1427.0208</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1423.2124</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1420.2153</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1417.9304</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1416.3389</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1415.5026</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1415.5073</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1416.382</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1418.0682</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1420.3698</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1423.0364</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">617.33661</Real>
+      <Real Name="Y">-780.51447</Real>
+      <Real Name="Z">-86.694389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-169.13429</Real>
+      <Real Name="Y">-251.96469</Real>
+      <Real Name="Z">32.742085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-454.9155</Real>
+      <Real Name="Y">275.3364</Real>
+      <Real Name="Z">-220.35951</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.52621</Real>
+      <Real Name="Y">187.76048</Real>
+      <Real Name="Z">25.407236</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">805.48761</Real>
+      <Real Name="Y">669.22736</Real>
+      <Real Name="Z">-34.901096</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-23.652775</Real>
+      <Real Name="Y">322.60388</Real>
+      <Real Name="Z">-212.35614</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-963.67493</Real>
+      <Real Name="Y">320.117</Real>
+      <Real Name="Z">1073.1449</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">17.54126</Real>
+      <Real Name="Y">-305.06851</Real>
+      <Real Name="Z">-406.89926</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">344.25427</Real>
+      <Real Name="Y">-377.22156</Real>
+      <Real Name="Z">-211.73772</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.9347687</Real>
+      <Real Name="Y">-32.547478</Real>
+      <Real Name="Z">22.989349</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.7136459</Real>
+      <Real Name="Y">28.822924</Real>
+      <Real Name="Z">-13.923183</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.9800415</Real>
+      <Real Name="Y">15.074156</Real>
+      <Real Name="Z">-5.139473</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.5753174</Real>
+      <Real Name="Y">20.93708</Real>
+      <Real Name="Z">-6.5507355</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.8364716</Real>
+      <Real Name="Y">-8.383873</Real>
+      <Real Name="Z">4.6215553</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.4699059</Real>
+      <Real Name="Y">-23.646114</Real>
+      <Real Name="Z">4.1250038</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.90411</Real>
+      <Real Name="Y">-59.309494</Real>
+      <Real Name="Z">-670.22308</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.94229</Real>
+      <Real Name="Y">36.183804</Real>
+      <Real Name="Z">385.22693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.6894455</Real>
+      <Real Name="Y">-11.371679</Real>
+      <Real Name="Z">206.43135</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">372.76163</Real>
+      <Real Name="Y">415.08405</Real>
+      <Real Name="Z">-106.87575</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-40.294144</Real>
+      <Real Name="Y">-233.29556</Real>
+      <Real Name="Z">90.301315</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-356.02478</Real>
+      <Real Name="Y">-587.08337</Real>
+      <Real Name="Z">251.21111</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">647.46844</Real>
+      <Real Name="Y">-496.60052</Real>
+      <Real Name="Z">-423.58368</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.63145</Real>
+      <Real Name="Y">123.53926</Real>
+      <Real Name="Z">75.955551</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-647.95801</Real>
+      <Real Name="Y">441.42059</Real>
+      <Real Name="Z">220.66132</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.763874</Real>
+      <Real Name="Y">287.49512</Real>
+      <Real Name="Z">60.575615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.318283</Real>
+      <Real Name="Y">-160.49477</Real>
+      <Real Name="Z">-108.35732</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.93105</Real>
+      <Real Name="Y">-211.82112</Real>
+      <Real Name="Z">-136.49985</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-590.88574</Real>
+      <Real Name="Y">267.36221</Real>
+      <Real Name="Z">839.0293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">396.81281</Real>
+      <Real Name="Y">-52.672554</Real>
+      <Real Name="Z">-871.45374</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">175.44894</Real>
+      <Real Name="Y">-189.39462</Real>
+      <Real Name="Z">-62.891357</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1107.5161</Real>
+      <Real Name="Y">592.50647</Real>
+      <Real Name="Z">848.63428</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.84024</Real>
+      <Real Name="Y">-854.797</Real>
+      <Real Name="Z">-356.51849</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-854.72687</Real>
+      <Real Name="Y">309.68973</Real>
+      <Real Name="Z">-571.80908</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">362.18417</Real>
+      <Real Name="Y">752.2821</Real>
+      <Real Name="Z">141.23689</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.8112259</Real>
+      <Real Name="Y">-648.95288</Real>
+      <Real Name="Z">-51.363411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-92.772888</Real>
+      <Real Name="Y">-130.48854</Real>
+      <Real Name="Z">-35.331146</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.513041</Real>
+      <Real Name="Y">-155.12177</Real>
+      <Real Name="Z">-891.41486</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-93.938316</Real>
+      <Real Name="Y">26.177977</Real>
+      <Real Name="Z">254.17628</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">393.79816</Real>
+      <Real Name="Y">-9.936779</Real>
+      <Real Name="Z">679.60004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">110.12297</Real>
+      <Real Name="Y">49.641937</Real>
+      <Real Name="Z">-862.85516</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-257.10236</Real>
+      <Real Name="Y">161.30362</Real>
+      <Real Name="Z">485.82635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.702003</Real>
+      <Real Name="Y">-197.45633</Real>
+      <Real Name="Z">167.65471</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-630.30328</Real>
+      <Real Name="Y">82.253143</Real>
+      <Real Name="Z">360.92517</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">543.20892</Real>
+      <Real Name="Y">-65.6241</Real>
+      <Real Name="Z">-8.2377453</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">137.03171</Real>
+      <Real Name="Y">-33.424728</Real>
+      <Real Name="Z">-245.84113</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-102.85608</Real>
+      <Real Name="Y">33.591461</Real>
+      <Real Name="Z">361.58456</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">245.42336</Real>
+      <Real Name="Y">-3.5083618</Real>
+      <Real Name="Z">-127.53534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">57.408455</Real>
+      <Real Name="Y">-112.90305</Real>
+      <Real Name="Z">-114.6322</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-343.68259</Real>
+      <Real Name="Y">-2053.6333</Real>
+      <Real Name="Z">-698.3858</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">334.17819</Real>
+      <Real Name="Y">1319.8096</Real>
+      <Real Name="Z">317.98193</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">275.0188</Real>
+      <Real Name="Y">413.62527</Real>
+      <Real Name="Z">71.077988</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">432.78394</Real>
+      <Real Name="Y">1006.4045</Real>
+      <Real Name="Z">-844.37518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-151.4671</Real>
+      <Real Name="Y">-538.99146</Real>
+      <Real Name="Z">229.15048</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.52936</Real>
+      <Real Name="Y">-402.388</Real>
+      <Real Name="Z">560.71997</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">51.855389</Real>
+      <Real Name="Y">95.835449</Real>
+      <Real Name="Z">3.4298553</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.051535</Real>
+      <Real Name="Y">-42.181683</Real>
+      <Real Name="Z">-2.5010204</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.626373</Real>
+      <Real Name="Y">-31.759125</Real>
+      <Real Name="Z">9.4050121</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-440.19217</Real>
+      <Real Name="Y">980.25952</Real>
+      <Real Name="Z">399.87769</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">607.22333</Real>
+      <Real Name="Y">-691.54602</Real>
+      <Real Name="Z">-454.94186</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">36.138149</Real>
+      <Real Name="Y">-157.0851</Real>
+      <Real Name="Z">-17.09235</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">213.91118</Real>
+      <Real Name="Y">-488.6087</Real>
+      <Real Name="Z">-1414.5408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-65.932564</Real>
+      <Real Name="Y">354.58322</Real>
+      <Real Name="Z">1043.2621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-331.3656</Real>
+      <Real Name="Y">19.885906</Real>
+      <Real Name="Z">245.51808</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.34485</Real>
+      <Real Name="Y">-124.98962</Real>
+      <Real Name="Z">-118.82239</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.353584</Real>
+      <Real Name="Y">126.48776</Real>
+      <Real Name="Z">73.656525</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-63.521088</Real>
+      <Real Name="Y">41.176628</Real>
+      <Real Name="Z">151.44341</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">330.51007</Real>
+      <Real Name="Y">-339.71625</Real>
+      <Real Name="Z">-72.240265</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.56885</Real>
+      <Real Name="Y">414.87732</Real>
+      <Real Name="Z">96.133003</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.74152</Real>
+      <Real Name="Y">68.079659</Real>
+      <Real Name="Z">39.450329</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">755.07751</Real>
+      <Real Name="Y">-267.23724</Real>
+      <Real Name="Z">-577.46594</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-353.17859</Real>
+      <Real Name="Y">255.45401</Real>
+      <Real Name="Z">608.12933</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-268.47809</Real>
+      <Real Name="Y">283.25037</Real>
+      <Real Name="Z">151.70406</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.90224457</Real>
+      <Real Name="Y">-9.65271</Real>
+      <Real Name="Z">5.7056007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.092679977</Real>
+      <Real Name="Y">6.251564</Real>
+      <Real Name="Z">-1.3744984</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.3116379</Real>
+      <Real Name="Y">4.2114067</Real>
+      <Real Name="Z">-5.0407639</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.94681</Real>
+      <Real Name="Y">856.92358</Real>
+      <Real Name="Z">245.18097</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">287.25851</Real>
+      <Real Name="Y">-483.84351</Real>
+      <Real Name="Z">-678.78436</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-314.1795</Real>
+      <Real Name="Y">-444.65701</Real>
+      <Real Name="Z">296.65948</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.76575</Real>
+      <Real Name="Y">28.093269</Real>
+      <Real Name="Z">-151.09431</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.91019</Real>
+      <Real Name="Y">59.995651</Real>
+      <Real Name="Z">39.736</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.483139</Real>
+      <Real Name="Y">-36.650173</Real>
+      <Real Name="Z">73.75528</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-557.96887</Real>
+      <Real Name="Y">-1285.3981</Real>
+      <Real Name="Z">-314.92886</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">201.60812</Real>
+      <Real Name="Y">618.4068</Real>
+      <Real Name="Z">-286.63455</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">379.58429</Real>
+      <Real Name="Y">652.41388</Real>
+      <Real Name="Z">624.79401</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1195.427</Real>
+      <Real Name="Y">443.18616</Real>
+      <Real Name="Z">-793.37415</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">820.32343</Real>
+      <Real Name="Y">-311.45047</Real>
+      <Real Name="Z">572.69684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.03748</Real>
+      <Real Name="Y">-31.90567</Real>
+      <Real Name="Z">39.746109</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">219.05635</Real>
+      <Real Name="Y">427.98837</Real>
+      <Real Name="Z">165.94514</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-29.749557</Real>
+      <Real Name="Y">-133.48866</Real>
+      <Real Name="Z">-72.059021</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-109.59982</Real>
+      <Real Name="Y">-123.34273</Real>
+      <Real Name="Z">-17.81591</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.6116</Real>
+      <Real Name="Y">-1076.0034</Real>
+      <Real Name="Z">-858.07568</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.17</Real>
+      <Real Name="Y">210.20972</Real>
+      <Real Name="Z">195.34174</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">576.18079</Real>
+      <Real Name="Y">692.08319</Real>
+      <Real Name="Z">681.30145</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.84282</Real>
+      <Real Name="Y">387.26837</Real>
+      <Real Name="Z">-1021.8298</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.936356</Real>
+      <Real Name="Y">-316.56287</Real>
+      <Real Name="Z">752.7301</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.990967</Real>
+      <Real Name="Y">33.420403</Real>
+      <Real Name="Z">254.1022</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.33334</Real>
+      <Real Name="Y">-513.68066</Real>
+      <Real Name="Z">-928.87775</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.64267</Real>
+      <Real Name="Y">80.175964</Real>
+      <Real Name="Z">145.86652</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.541534</Real>
+      <Real Name="Y">257.67795</Real>
+      <Real Name="Z">803.70007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-504.87671</Real>
+      <Real Name="Y">-155.15685</Real>
+      <Real Name="Z">-164.86368</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.1913</Real>
+      <Real Name="Y">16.228439</Real>
+      <Real Name="Z">135.49379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">238.37128</Real>
+      <Real Name="Y">47.363029</Real>
+      <Real Name="Z">105.86298</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">589.6604</Real>
+      <Real Name="Y">826.8764</Real>
+      <Real Name="Z">-262.17026</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.51935</Real>
+      <Real Name="Y">-202.67856</Real>
+      <Real Name="Z">15.75489</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-461.92422</Real>
+      <Real Name="Y">-869.53784</Real>
+      <Real Name="Z">-37.157898</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1693.066</Real>
+      <Real Name="Y">-174.15359</Real>
+      <Real Name="Z">896.79138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-862.94971</Real>
+      <Real Name="Y">314.30588</Real>
+      <Real Name="Z">-687.11066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-322.71545</Real>
+      <Real Name="Y">-175.96829</Real>
+      <Real Name="Z">-374.29053</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-154.6716</Real>
+      <Real Name="Y">55.155525</Real>
+      <Real Name="Z">125.81474</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.54355</Real>
+      <Real Name="Y">-162.45393</Real>
+      <Real Name="Z">29.750122</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">199.44672</Real>
+      <Real Name="Y">-7.2473717</Real>
+      <Real Name="Z">25.004314</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">725.20752</Real>
+      <Real Name="Y">-430.76373</Real>
+      <Real Name="Z">7.2008438</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-512.58234</Real>
+      <Real Name="Y">294.11615</Real>
+      <Real Name="Z">37.104877</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-414.48853</Real>
+      <Real Name="Y">350.94092</Real>
+      <Real Name="Z">-121.14153</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.10907</Real>
+      <Real Name="Y">-1820.3712</Real>
+      <Real Name="Z">38.144524</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">130.1235</Real>
+      <Real Name="Y">241.66101</Real>
+      <Real Name="Z">-35.208729</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.01749</Real>
+      <Real Name="Y">1228.6007</Real>
+      <Real Name="Z">-58.759235</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">436.39532</Real>
+      <Real Name="Y">1034.939</Real>
+      <Real Name="Z">857.30328</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-312.27539</Real>
+      <Real Name="Y">-645.20398</Real>
+      <Real Name="Z">-580.97296</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-59.260658</Real>
+      <Real Name="Y">-239.47542</Real>
+      <Real Name="Z">-227.79544</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">40.693493</Real>
+      <Real Name="Y">-377.20816</Real>
+      <Real Name="Z">-473.38165</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-196.80806</Real>
+      <Real Name="Y">346.03714</Real>
+      <Real Name="Z">245.53894</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-125.08713</Real>
+      <Real Name="Y">145.33511</Real>
+      <Real Name="Z">477.83868</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">778.95544</Real>
+      <Real Name="Y">-311.1871</Real>
+      <Real Name="Z">-241.48091</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.85399</Real>
+      <Real Name="Y">453.18155</Real>
+      <Real Name="Z">4.5315399</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-742.05725</Real>
+      <Real Name="Y">-154.62598</Real>
+      <Real Name="Z">176.70192</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1096.1051</Real>
+      <Real Name="Y">1502.3936</Real>
+      <Real Name="Z">-674.96509</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.17151</Real>
+      <Real Name="Y">-212.01642</Real>
+      <Real Name="Z">94.94455</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">626.80078</Real>
+      <Real Name="Y">-964.20044</Real>
+      <Real Name="Z">443.67899</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">744.16345</Real>
+      <Real Name="Y">22.960342</Real>
+      <Real Name="Z">144.31339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.01215</Real>
+      <Real Name="Y">95.556435</Real>
+      <Real Name="Z">-34.543884</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-916.07959</Real>
+      <Real Name="Y">123.17702</Real>
+      <Real Name="Z">-126.24118</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-798.04028</Real>
+      <Real Name="Y">-1242.7562</Real>
+      <Real Name="Z">108.21335</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">700.27899</Real>
+      <Real Name="Y">953.6933</Real>
+      <Real Name="Z">62.575523</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">58.251953</Real>
+      <Real Name="Y">258.16742</Real>
+      <Real Name="Z">-74.889671</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.15436</Real>
+      <Real Name="Y">-691.19086</Real>
+      <Real Name="Z">812.94385</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.88965</Real>
+      <Real Name="Y">504.397</Real>
+      <Real Name="Z">-583.37585</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-53.485317</Real>
+      <Real Name="Y">76.171822</Real>
+      <Real Name="Z">-119.49659</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.593575</Real>
+      <Real Name="Y">-129.6514</Real>
+      <Real Name="Z">309.33026</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-25.43799</Real>
+      <Real Name="Y">250.85448</Real>
+      <Real Name="Z">-60.291992</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-290.61942</Real>
+      <Real Name="Y">360.50009</Real>
+      <Real Name="Z">45.284187</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">83.772934</Real>
+      <Real Name="Y">479.87762</Real>
+      <Real Name="Z">-73.341759</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.435299</Real>
+      <Real Name="Y">-177.02826</Real>
+      <Real Name="Z">67.396545</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.987606</Real>
+      <Real Name="Y">-187.59714</Real>
+      <Real Name="Z">27.104071</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">78.773163</Real>
+      <Real Name="Y">771.31555</Real>
+      <Real Name="Z">73.895294</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.361427</Real>
+      <Real Name="Y">-168.63474</Real>
+      <Real Name="Z">-65.576714</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.227707</Real>
+      <Real Name="Y">-727.33746</Real>
+      <Real Name="Z">-11.528174</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.365936</Real>
+      <Real Name="Y">-724.96466</Real>
+      <Real Name="Z">-207.02744</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.96478</Real>
+      <Real Name="Y">627.13464</Real>
+      <Real Name="Z">110.45792</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.24794</Real>
+      <Real Name="Y">177.91463</Real>
+      <Real Name="Z">42.457146</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.939697</Real>
+      <Real Name="Y">814.13458</Real>
+      <Real Name="Z">588.21356</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">24.189129</Real>
+      <Real Name="Y">-185.58057</Real>
+      <Real Name="Z">-105.79498</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">130.35266</Real>
+      <Real Name="Y">-616.07758</Real>
+      <Real Name="Z">-446.39188</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.9827</Real>
+      <Real Name="Y">310.52808</Real>
+      <Real Name="Z">-419.03406</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">161.59464</Real>
+      <Real Name="Y">-258.08325</Real>
+      <Real Name="Z">437.42535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">146.62549</Real>
+      <Real Name="Y">-119.18927</Real>
+      <Real Name="Z">90.629784</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-881.57263</Real>
+      <Real Name="Y">-102.95556</Real>
+      <Real Name="Z">-59.490646</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.73218</Real>
+      <Real Name="Y">-59.390209</Real>
+      <Real Name="Z">5.589119</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">311.63391</Real>
+      <Real Name="Y">50.355629</Real>
+      <Real Name="Z">7.166954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1521.3552</Real>
+      <Real Name="Y">-274.7059</Real>
+      <Real Name="Z">-653.08289</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">294.19162</Real>
+      <Real Name="Y">-40.116096</Real>
+      <Real Name="Z">208.85138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1067.9164</Real>
+      <Real Name="Y">342.88251</Real>
+      <Real Name="Z">332.12344</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.47821</Real>
+      <Real Name="Y">1438.0076</Real>
+      <Real Name="Z">325.95453</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.0083065</Real>
+      <Real Name="Y">-736.71198</Real>
+      <Real Name="Z">-586.96265</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">373.00073</Real>
+      <Real Name="Y">-458.66336</Real>
+      <Real Name="Z">485.55991</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">201.63794</Real>
+      <Real Name="Y">437.37115</Real>
+      <Real Name="Z">127.21452</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-67.344383</Real>
+      <Real Name="Y">-123.37768</Real>
+      <Real Name="Z">-82.54248</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.36757</Real>
+      <Real Name="Y">-494.58807</Real>
+      <Real Name="Z">-109.36105</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1162.127</Real>
+      <Real Name="Y">-2017.4215</Real>
+      <Real Name="Z">666.97101</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.03601</Real>
+      <Real Name="Y">1230.618</Real>
+      <Real Name="Z">320.17947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1029.7594</Real>
+      <Real Name="Y">392.19427</Real>
+      <Real Name="Z">-802.41577</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">786.0752</Real>
+      <Real Name="Y">-1308.1134</Real>
+      <Real Name="Z">-296.95157</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-246.38203</Real>
+      <Real Name="Y">685.94806</Real>
+      <Real Name="Z">25.197598</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-501.07248</Real>
+      <Real Name="Y">703.30817</Real>
+      <Real Name="Z">323.36243</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">76.651764</Real>
+      <Real Name="Y">818.48578</Real>
+      <Real Name="Z">1329.381</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-102.86015</Real>
+      <Real Name="Y">-715.75873</Real>
+      <Real Name="Z">-1007.6066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-106.73311</Real>
+      <Real Name="Y">19.04343</Real>
+      <Real Name="Z">-246.054</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_expanded_d.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_expanded_d.xml
new file mode 100644 (file)
index 0000000..def03f7
--- /dev/null
@@ -0,0 +1,5641 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVvdw/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">4.7801821609733199</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">5.2078579394423397</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">5.6309368575394299</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">6.0403832417395273</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">6.424690099827794</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">6.7720086370097148</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">34.62981136992515</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">35.175560560863879</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">35.555587380874847</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">35.745601774411803</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">35.737775933976955</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">35.541493344314439</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">35.181358317956025</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">34.69277741075458</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">34.118552949450631</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">33.501115418958335</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">32.879561813034798</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">32.285677115696018</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">31.741569026385367</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">31.258840298098281</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">30.836705331575811</Real>
+    <Real Name="Time 0.021000 Step 21 in frame 21">30.465744075836334</Real>
+    <Real Name="Time 0.022000 Step 22 in frame 22">30.111162177456315</Real>
+    <Real Name="Time 0.023000 Step 23 in frame 23">29.749916181202924</Real>
+    <Real Name="Time 0.024000 Step 24 in frame 24">29.357572338216787</Real>
+    <Real Name="Time 0.025000 Step 25 in frame 25">28.912808124018003</Real>
+    <Real Name="Time 0.026000 Step 26 in frame 26">28.38824027857029</Real>
+    <Real Name="Time 0.027000 Step 27 in frame 27">27.777560728327707</Real>
+    <Real Name="Time 0.028000 Step 28 in frame 28">27.073303548428534</Real>
+    <Real Name="Time 0.029000 Step 29 in frame 29">26.277499777052341</Real>
+    <Real Name="Time 0.030000 Step 30 in frame 30">25.399376465022396</Real>
+    <Real Name="Time 0.031000 Step 31 in frame 31">24.450339749411711</Real>
+    <Real Name="Time 0.032000 Step 32 in frame 32">23.442862766198527</Real>
+    <Real Name="Time 0.033000 Step 33 in frame 33">22.389964814754826</Real>
+    <Real Name="Time 0.034000 Step 34 in frame 34">21.301744845509369</Real>
+    <Real Name="Time 0.035000 Step 35 in frame 35">20.184688809061122</Real>
+    <Real Name="Time 0.036000 Step 36 in frame 36">19.041414294560546</Real>
+    <Real Name="Time 0.037000 Step 37 in frame 37">17.877755523494567</Real>
+    <Real Name="Time 0.038000 Step 38 in frame 38">16.690534196551528</Real>
+    <Real Name="Time 0.039000 Step 39 in frame 39">15.478477060666446</Real>
+    <Real Name="Time 0.040000 Step 40 in frame 40">14.244616683547285</Real>
+    <Real Name="Time 0.041000 Step 41 in frame 41">12.997927445258503</Real>
+    <Real Name="Time 0.042000 Step 42 in frame 42">11.753694336055011</Real>
+    <Real Name="Time 0.043000 Step 43 in frame 43">10.532410280503019</Real>
+    <Real Name="Time 0.044000 Step 44 in frame 44">9.3573979384270167</Real>
+    <Real Name="Time 0.045000 Step 45 in frame 45">8.2517831595155418</Real>
+    <Real Name="Time 0.046000 Step 46 in frame 46">7.2357056735565628</Real>
+    <Real Name="Time 0.047000 Step 47 in frame 47">6.3094401550787111</Real>
+    <Real Name="Time 0.048000 Step 48 in frame 48">5.4816923309376691</Real>
+    <Real Name="Time 0.049000 Step 49 in frame 49">4.7545124193937083</Real>
+    <Real Name="Time 0.050000 Step 50 in frame 50">4.1312055056581816</Real>
+    <Real Name="Time 0.051000 Step 51 in frame 51">3.6071586938775164</Real>
+    <Real Name="Time 0.052000 Step 52 in frame 52">3.1867858891537377</Real>
+    <Real Name="Time 0.053000 Step 53 in frame 53">2.8636896074955862</Real>
+    <Real Name="Time 0.054000 Step 54 in frame 54">2.6291564744160461</Real>
+    <Real Name="Time 0.055000 Step 55 in frame 55">2.4765002077428528</Real>
+    <Real Name="Time 0.056000 Step 56 in frame 56">2.3925930963060402</Real>
+    <Real Name="Time 0.057000 Step 57 in frame 57">2.3606324012549509</Real>
+    <Real Name="Time 0.058000 Step 58 in frame 58">2.3685108590163457</Real>
+    <Real Name="Time 0.059000 Step 59 in frame 59">2.4022596321375751</Real>
+    <Real Name="Time 0.060000 Step 60 in frame 60">2.446145999923659</Real>
+    <Real Name="Time 0.061000 Step 61 in frame 61">2.4833253343051318</Real>
+    <Real Name="Time 0.062000 Step 62 in frame 62">2.4967363027835079</Real>
+    <Real Name="Time 0.063000 Step 63 in frame 63">2.4747812418511241</Real>
+    <Real Name="Time 0.064000 Step 64 in frame 64">2.4018509965827404</Real>
+    <Real Name="Time 0.065000 Step 65 in frame 65">2.2743204897800346</Real>
+    <Real Name="Time 0.066000 Step 66 in frame 66">2.0917843103733826</Real>
+    <Real Name="Time 0.067000 Step 67 in frame 67">1.8688003584880415</Real>
+    <Real Name="Time 0.068000 Step 68 in frame 68">1.6148656533788477</Real>
+    <Real Name="Time 0.069000 Step 69 in frame 69">1.3430324685606669</Real>
+    <Real Name="Time 0.070000 Step 70 in frame 70">1.0694173196510455</Real>
+    <Real Name="Time 0.071000 Step 71 in frame 71">0.81085641685332743</Real>
+    <Real Name="Time 0.072000 Step 72 in frame 72">0.58629275403813463</Real>
+    <Real Name="Time 0.073000 Step 73 in frame 73">0.41580754959977251</Real>
+    <Real Name="Time 0.074000 Step 74 in frame 74">0.31695003462430915</Real>
+    <Real Name="Time 0.075000 Step 75 in frame 75">0.30423943184884272</Real>
+    <Real Name="Time 0.076000 Step 76 in frame 76">0.38602304622082728</Real>
+    <Real Name="Time 0.077000 Step 77 in frame 77">0.56054009912059066</Real>
+    <Real Name="Time 0.078000 Step 78 in frame 78">0.82118977647166402</Real>
+    <Real Name="Time 0.079000 Step 79 in frame 79">1.1551876474903329</Real>
+    <Real Name="Time 0.080000 Step 80 in frame 80">1.546221774887248</Real>
+    <Real Name="Time 0.081000 Step 81 in frame 81">1.9759953831992052</Real>
+    <Real Name="Time 0.082000 Step 82 in frame 82">2.4318561969501724</Real>
+    <Real Name="Time 0.083000 Step 83 in frame 83">2.9072950832241347</Real>
+    <Real Name="Time 0.084000 Step 84 in frame 84">3.4023967180540202</Real>
+    <Real Name="Time 0.085000 Step 85 in frame 85">3.9231343326577681</Real>
+    <Real Name="Time 0.086000 Step 86 in frame 86">4.477289429167536</Real>
+    <Real Name="Time 0.087000 Step 87 in frame 87">5.0684873141139315</Real>
+    <Real Name="Time 0.088000 Step 88 in frame 88">5.6959223948362387</Real>
+    <Real Name="Time 0.089000 Step 89 in frame 89">6.3520294094563789</Real>
+    <Real Name="Time 0.090000 Step 90 in frame 90">7.0240041106698667</Real>
+    <Real Name="Time 0.091000 Step 91 in frame 91">-13.677121119003697</Real>
+    <Real Name="Time 0.092000 Step 92 in frame 92">-13.535276506741651</Real>
+    <Real Name="Time 0.093000 Step 93 in frame 93">-13.392468562225764</Real>
+    <Real Name="Time 0.094000 Step 94 in frame 94">-13.250421506607966</Real>
+    <Real Name="Time 0.095000 Step 95 in frame 95">-13.110453262991683</Real>
+    <Real Name="Time 0.096000 Step 96 in frame 96">11.095835072632243</Real>
+    <Real Name="Time 0.097000 Step 97 in frame 97">11.736012647987232</Real>
+    <Real Name="Time 0.098000 Step 98 in frame 98">12.349442676934324</Real>
+    <Real Name="Time 0.099000 Step 99 in frame 99">12.950024535582697</Real>
+    <Real Name="Time 0.100000 Step 100 in frame 100">13.552714790872287</Real>
+  </Energy>
+  <Energy Name="dVcoul/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-77.8723566401097</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-77.7167069130698</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-77.504538019882148</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-77.241986972113608</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-76.94418861569747</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-76.638612657375745</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-76.364325984603042</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-76.499833038534334</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-77.07106726823983</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-78.057734247165712</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-79.391317405669525</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-80.964019165874305</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-82.64642122675059</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-84.309599249022313</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-85.846865312153867</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-87.190729654585908</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-88.321856636036728</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-89.268522352527071</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-90.0970930379018</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-90.895834876066402</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-91.75555464236497</Real>
+    <Real Name="Time 0.021000 Step 21 in frame 21">-92.751062295310703</Real>
+    <Real Name="Time 0.022000 Step 22 in frame 22">-93.854268518833834</Real>
+    <Real Name="Time 0.023000 Step 23 in frame 23">-95.071429270337006</Real>
+    <Real Name="Time 0.024000 Step 24 in frame 24">-96.37134900224018</Real>
+    <Real Name="Time 0.025000 Step 25 in frame 25">-97.687254822341487</Real>
+    <Real Name="Time 0.026000 Step 26 in frame 26">-98.921920110387617</Real>
+    <Real Name="Time 0.027000 Step 27 in frame 27">-99.956057390485597</Real>
+    <Real Name="Time 0.028000 Step 28 in frame 28">-100.66127693461635</Real>
+    <Real Name="Time 0.029000 Step 29 in frame 29">-100.91778504384668</Real>
+    <Real Name="Time 0.030000 Step 30 in frame 30">-100.63480447477396</Real>
+    <Real Name="Time 0.031000 Step 31 in frame 31">-99.768957810073374</Real>
+    <Real Name="Time 0.032000 Step 32 in frame 32">-98.334724693711536</Real>
+    <Real Name="Time 0.033000 Step 33 in frame 33">-96.403243207560195</Real>
+    <Real Name="Time 0.034000 Step 34 in frame 34">-94.090183586608418</Real>
+    <Real Name="Time 0.035000 Step 35 in frame 35">-91.537358681385442</Real>
+    <Real Name="Time 0.036000 Step 36 in frame 36">-88.893864937520505</Real>
+    <Real Name="Time 0.037000 Step 37 in frame 37">-86.395775750000922</Real>
+    <Real Name="Time 0.038000 Step 38 in frame 38">-84.163669661191705</Real>
+    <Real Name="Time 0.039000 Step 39 in frame 39">-82.28691247203065</Real>
+    <Real Name="Time 0.040000 Step 40 in frame 40">-80.820311143939463</Real>
+    <Real Name="Time 0.041000 Step 41 in frame 41">-79.782523306961068</Real>
+    <Real Name="Time 0.042000 Step 42 in frame 42">-79.156401397675268</Real>
+    <Real Name="Time 0.043000 Step 43 in frame 43">-78.892569549919813</Real>
+    <Real Name="Time 0.044000 Step 44 in frame 44">-78.917330236536529</Real>
+    <Real Name="Time 0.045000 Step 45 in frame 45">-79.144600202348073</Real>
+    <Real Name="Time 0.046000 Step 46 in frame 46">-79.48879885603256</Real>
+    <Real Name="Time 0.047000 Step 47 in frame 47">-79.62316937115898</Real>
+    <Real Name="Time 0.048000 Step 48 in frame 48">-79.511622926608709</Real>
+    <Real Name="Time 0.049000 Step 49 in frame 49">-79.137831264951274</Real>
+    <Real Name="Time 0.050000 Step 50 in frame 50">-78.500981173554436</Real>
+    <Real Name="Time 0.051000 Step 51 in frame 51">-77.61254783977364</Real>
+    <Real Name="Time 0.052000 Step 52 in frame 52">-76.642226694475681</Real>
+    <Real Name="Time 0.053000 Step 53 in frame 53">-75.611405004204926</Real>
+    <Real Name="Time 0.054000 Step 54 in frame 54">-74.547715985118685</Real>
+    <Real Name="Time 0.055000 Step 55 in frame 55">-73.484700344204981</Real>
+    <Real Name="Time 0.056000 Step 56 in frame 56">-72.461038497678047</Real>
+    <Real Name="Time 0.057000 Step 57 in frame 57">-71.386039864939818</Real>
+    <Real Name="Time 0.058000 Step 58 in frame 58">-70.31231756759891</Real>
+    <Real Name="Time 0.059000 Step 59 in frame 59">-69.294696073686524</Real>
+    <Real Name="Time 0.060000 Step 60 in frame 60">-68.387160385429056</Real>
+    <Real Name="Time 0.061000 Step 61 in frame 61">-67.639127771841203</Real>
+    <Real Name="Time 0.062000 Step 62 in frame 62">-67.090807058596155</Real>
+    <Real Name="Time 0.063000 Step 63 in frame 63">-66.768023280788043</Real>
+    <Real Name="Time 0.064000 Step 64 in frame 64">-66.67791454926207</Real>
+    <Real Name="Time 0.065000 Step 65 in frame 65">-66.807259748498836</Real>
+    <Real Name="Time 0.066000 Step 66 in frame 66">-67.124409279901585</Real>
+    <Real Name="Time 0.067000 Step 67 in frame 67">-67.788491147784953</Real>
+    <Real Name="Time 0.068000 Step 68 in frame 68">-68.743156746709701</Real>
+    <Real Name="Time 0.069000 Step 69 in frame 69">-69.921891203317557</Real>
+    <Real Name="Time 0.070000 Step 70 in frame 70">-71.253011305740841</Real>
+    <Real Name="Time 0.071000 Step 71 in frame 71">-72.6633831567664</Real>
+    <Real Name="Time 0.072000 Step 72 in frame 72">-74.081439740027378</Real>
+    <Real Name="Time 0.073000 Step 73 in frame 73">-75.440754447786219</Real>
+    <Real Name="Time 0.074000 Step 74 in frame 74">-76.685476532415237</Real>
+    <Real Name="Time 0.075000 Step 75 in frame 75">-77.777893324193315</Real>
+    <Real Name="Time 0.076000 Step 76 in frame 76">-78.706523466566352</Real>
+    <Real Name="Time 0.077000 Step 77 in frame 77">-79.432796870106159</Real>
+    <Real Name="Time 0.078000 Step 78 in frame 78">-80.009735173140726</Real>
+    <Real Name="Time 0.079000 Step 79 in frame 79">-80.511357517342816</Real>
+    <Real Name="Time 0.080000 Step 80 in frame 80">-81.015994031309958</Real>
+    <Real Name="Time 0.081000 Step 81 in frame 81">-81.586945838181578</Real>
+    <Real Name="Time 0.082000 Step 82 in frame 82">-82.254888648210056</Real>
+    <Real Name="Time 0.083000 Step 83 in frame 83">-83.005730329297279</Real>
+    <Real Name="Time 0.084000 Step 84 in frame 84">-83.776855790135514</Real>
+    <Real Name="Time 0.085000 Step 85 in frame 85">-84.464189010981386</Real>
+    <Real Name="Time 0.086000 Step 86 in frame 86">-84.941409188230153</Real>
+    <Real Name="Time 0.087000 Step 87 in frame 87">-85.089290243256727</Real>
+    <Real Name="Time 0.088000 Step 88 in frame 88">-84.827772502342839</Real>
+    <Real Name="Time 0.089000 Step 89 in frame 89">-84.139440203066016</Real>
+    <Real Name="Time 0.090000 Step 90 in frame 90">-83.075301806441246</Real>
+    <Real Name="Time 0.091000 Step 91 in frame 91">-81.741896297091415</Real>
+    <Real Name="Time 0.092000 Step 92 in frame 92">-79.956098737850141</Real>
+    <Real Name="Time 0.093000 Step 93 in frame 93">-77.848592918820032</Real>
+    <Real Name="Time 0.094000 Step 94 in frame 94">-75.556143310741092</Real>
+    <Real Name="Time 0.095000 Step 95 in frame 95">-73.205387459851167</Real>
+    <Real Name="Time 0.096000 Step 96 in frame 96">-70.900410415119723</Real>
+    <Real Name="Time 0.097000 Step 97 in frame 97">-68.712660241273511</Real>
+    <Real Name="Time 0.098000 Step 98 in frame 98">-66.681454015849653</Real>
+    <Real Name="Time 0.099000 Step 99 in frame 99">-64.812274192206132</Real>
+    <Real Name="Time 0.100000 Step 100 in frame 100">-63.081207809337776</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1466.0645446442834</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1464.0411179378359</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1460.3772419465681</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1455.2130208861226</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1448.867534225898</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1441.7861437888535</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1507.0313390574411</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1500.2682626559665</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1494.8732187832115</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1490.9690796443908</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1488.3528020429112</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1486.6230368213974</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1485.3547145135512</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1484.2508538265042</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1483.2195486473433</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1482.3640153394929</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1481.8935675769094</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1482.0065020470281</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1482.795061678605</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1484.2090218472613</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1486.0883014913766</Real>
+    <Real Name="Time 0.021000 Step 21 in frame 21">-1469.688712458709</Real>
+    <Real Name="Time 0.022000 Step 22 in frame 22">-1471.7266338989189</Real>
+    <Real Name="Time 0.023000 Step 23 in frame 23">-1473.8398138303653</Real>
+    <Real Name="Time 0.024000 Step 24 in frame 24">-1476.1762030018956</Real>
+    <Real Name="Time 0.025000 Step 25 in frame 25">-1478.9920952904965</Real>
+    <Real Name="Time 0.026000 Step 26 in frame 26">-1482.5692091225628</Real>
+    <Real Name="Time 0.027000 Step 27 in frame 27">-1487.0879422101934</Real>
+    <Real Name="Time 0.028000 Step 28 in frame 28">-1492.5821198906126</Real>
+    <Real Name="Time 0.029000 Step 29 in frame 29">-1498.9183175408302</Real>
+    <Real Name="Time 0.030000 Step 30 in frame 30">-1505.8433455814952</Real>
+    <Real Name="Time 0.031000 Step 31 in frame 31">-1513.0573790984504</Real>
+    <Real Name="Time 0.032000 Step 32 in frame 32">-1520.2616389608647</Real>
+    <Real Name="Time 0.033000 Step 33 in frame 33">-1527.1606707797007</Real>
+    <Real Name="Time 0.034000 Step 34 in frame 34">-1533.4409528815743</Real>
+    <Real Name="Time 0.035000 Step 35 in frame 35">-1538.7656873280016</Real>
+    <Real Name="Time 0.036000 Step 36 in frame 36">-1560.6035852788907</Real>
+    <Real Name="Time 0.037000 Step 37 in frame 37">-1562.7945325562491</Real>
+    <Real Name="Time 0.038000 Step 38 in frame 38">-1563.7684209933489</Real>
+    <Real Name="Time 0.039000 Step 39 in frame 39">-1563.9053400167493</Real>
+    <Real Name="Time 0.040000 Step 40 in frame 40">-1563.7928316945117</Real>
+    <Real Name="Time 0.041000 Step 41 in frame 41">-1564.0755220115423</Real>
+    <Real Name="Time 0.042000 Step 42 in frame 42">-1565.2499004760803</Real>
+    <Real Name="Time 0.043000 Step 43 in frame 43">-1567.4878282707323</Real>
+    <Real Name="Time 0.044000 Step 44 in frame 44">-1570.5693097443711</Real>
+    <Real Name="Time 0.045000 Step 45 in frame 45">-1573.9503574308826</Real>
+    <Real Name="Time 0.046000 Step 46 in frame 46">-1529.2288845405669</Real>
+    <Real Name="Time 0.047000 Step 47 in frame 47">-1530.8973940655094</Real>
+    <Real Name="Time 0.048000 Step 48 in frame 48">-1531.113730116389</Real>
+    <Real Name="Time 0.049000 Step 49 in frame 49">-1529.7834547663208</Real>
+    <Real Name="Time 0.050000 Step 50 in frame 50">-1527.1382301349072</Real>
+    <Real Name="Time 0.051000 Step 51 in frame 51">-1554.7912859931887</Real>
+    <Real Name="Time 0.052000 Step 52 in frame 52">-1551.0162583611022</Real>
+    <Real Name="Time 0.053000 Step 53 in frame 53">-1548.1188661850722</Real>
+    <Real Name="Time 0.054000 Step 54 in frame 54">-1546.8997743683744</Real>
+    <Real Name="Time 0.055000 Step 55 in frame 55">-1547.8347241266595</Real>
+    <Real Name="Time 0.056000 Step 56 in frame 56">-1521.9435731595736</Real>
+    <Real Name="Time 0.057000 Step 57 in frame 57">-1527.0457013181735</Real>
+    <Real Name="Time 0.058000 Step 58 in frame 58">-1532.7298674471022</Real>
+    <Real Name="Time 0.059000 Step 59 in frame 59">-1537.7856590093679</Real>
+    <Real Name="Time 0.060000 Step 60 in frame 60">-1541.1354046998019</Real>
+    <Real Name="Time 0.061000 Step 61 in frame 61">-1542.1439409014135</Real>
+    <Real Name="Time 0.062000 Step 62 in frame 62">-1540.7970860493874</Real>
+    <Real Name="Time 0.063000 Step 63 in frame 63">-1537.6618484500759</Real>
+    <Real Name="Time 0.064000 Step 64 in frame 64">-1533.6651355684917</Real>
+    <Real Name="Time 0.065000 Step 65 in frame 65">-1529.7669134298262</Real>
+    <Real Name="Time 0.066000 Step 66 in frame 66">-1566.985358451165</Real>
+    <Real Name="Time 0.067000 Step 67 in frame 67">-1565.7155418349387</Real>
+    <Real Name="Time 0.068000 Step 68 in frame 68">-1565.9582210209348</Real>
+    <Real Name="Time 0.069000 Step 69 in frame 69">-1567.3028796694873</Real>
+    <Real Name="Time 0.070000 Step 70 in frame 70">-1569.1399466709515</Real>
+    <Real Name="Time 0.071000 Step 71 in frame 71">-1570.782410393691</Real>
+    <Real Name="Time 0.072000 Step 72 in frame 72">-1571.5603750744622</Real>
+    <Real Name="Time 0.073000 Step 73 in frame 73">-1570.9045062577197</Real>
+    <Real Name="Time 0.074000 Step 74 in frame 74">-1568.4415102327744</Real>
+    <Real Name="Time 0.075000 Step 75 in frame 75">-1564.1079778321025</Real>
+    <Real Name="Time 0.076000 Step 76 in frame 76">-1542.523917791191</Real>
+    <Real Name="Time 0.077000 Step 77 in frame 77">-1535.8425512309782</Real>
+    <Real Name="Time 0.078000 Step 78 in frame 78">-1529.7192817116088</Real>
+    <Real Name="Time 0.079000 Step 79 in frame 79">-1525.4922182828932</Real>
+    <Real Name="Time 0.080000 Step 80 in frame 80">-1524.274873405061</Real>
+    <Real Name="Time 0.081000 Step 81 in frame 81">-1526.6180931620913</Real>
+    <Real Name="Time 0.082000 Step 82 in frame 82">-1532.3021624808812</Real>
+    <Real Name="Time 0.083000 Step 83 in frame 83">-1540.3466372968824</Real>
+    <Real Name="Time 0.084000 Step 84 in frame 84">-1549.2348193080848</Real>
+    <Real Name="Time 0.085000 Step 85 in frame 85">-1557.2952028411564</Real>
+    <Real Name="Time 0.086000 Step 86 in frame 86">-1563.1358240499169</Real>
+    <Real Name="Time 0.087000 Step 87 in frame 87">-1565.9975173277855</Real>
+    <Real Name="Time 0.088000 Step 88 in frame 88">-1565.8991311822506</Real>
+    <Real Name="Time 0.089000 Step 89 in frame 89">-1563.5325234423635</Real>
+    <Real Name="Time 0.090000 Step 90 in frame 90">-1559.9550275920242</Real>
+    <Real Name="Time 0.091000 Step 91 in frame 91">-1488.7126023980618</Real>
+    <Real Name="Time 0.092000 Step 92 in frame 92">-1486.956610450623</Real>
+    <Real Name="Time 0.093000 Step 93 in frame 93">-1486.035954006006</Real>
+    <Real Name="Time 0.094000 Step 94 in frame 94">-1485.6643339670802</Real>
+    <Real Name="Time 0.095000 Step 95 in frame 95">-1485.4201437475795</Real>
+    <Real Name="Time 0.096000 Step 96 in frame 96">-1486.3990054752526</Real>
+    <Real Name="Time 0.097000 Step 97 in frame 97">-1485.5126521744423</Real>
+    <Real Name="Time 0.098000 Step 98 in frame 98">-1484.5262354228057</Real>
+    <Real Name="Time 0.099000 Step 99 in frame 99">-1483.8650207359062</Real>
+    <Real Name="Time 0.100000 Step 100 in frame 100">-1484.0093501579659</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">634.1887582263048</Real>
+      <Real Name="Y">-774.10928481169697</Real>
+      <Real Name="Z">-84.308600533050978</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-173.32107808952622</Real>
+      <Real Name="Y">-254.02048675169274</Real>
+      <Real Name="Z">32.146861728878505</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-460.16253374395501</Real>
+      <Real Name="Y">273.38021042700836</Real>
+      <Real Name="Z">-218.39638579675284</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.13573550812066</Real>
+      <Real Name="Y">188.8805458533362</Real>
+      <Real Name="Z">21.107798732648391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">746.21074052158747</Real>
+      <Real Name="Y">612.80760233606952</Real>
+      <Real Name="Z">-42.133644374143138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-24.306684492924305</Real>
+      <Real Name="Y">422.14992209623642</Real>
+      <Real Name="Z">-169.39604112072846</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-869.61385336000433</Real>
+      <Real Name="Y">288.46966725544627</Real>
+      <Real Name="Z">1096.8762778888649</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-40.535521099933845</Real>
+      <Real Name="Y">-295.73418901352028</Real>
+      <Real Name="Z">-453.53486241339243</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">275.31895981939721</Real>
+      <Real Name="Y">-437.95383381694387</Real>
+      <Real Name="Z">-229.88298380654226</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">10.926011005091937</Real>
+      <Real Name="Y">-30.975674760252105</Real>
+      <Real Name="Z">20.381590937576163</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.0138701254607909</Real>
+      <Real Name="Y">27.856316975829493</Real>
+      <Real Name="Z">-12.695918110904778</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.2674211619326883</Real>
+      <Real Name="Y">14.475177408717883</Real>
+      <Real Name="Z">-4.0467350436091323</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.22538190930259816</Real>
+      <Real Name="Y">23.69679661203174</Real>
+      <Real Name="Z">-7.1117981649562125</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.8462065976892745</Real>
+      <Real Name="Y">-9.5171155293956566</Real>
+      <Real Name="Z">4.7696350465498654</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.3453596266597003</Real>
+      <Real Name="Y">-24.902907360873492</Real>
+      <Real Name="Z">4.5991521089141614</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.96295187530052</Real>
+      <Real Name="Y">-59.605635568030849</Real>
+      <Real Name="Z">-670.15721785214919</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.81279122705769</Real>
+      <Real Name="Y">36.20469042291834</Real>
+      <Real Name="Z">385.08925762579611</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.8215464435196651</Real>
+      <Real Name="Y">-11.298418225397187</Real>
+      <Real Name="Z">206.28479303022127</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">394.2359369069934</Real>
+      <Real Name="Y">432.20994096051083</Real>
+      <Real Name="Z">-97.835776933808276</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.85200933759662</Real>
+      <Real Name="Y">-238.51162915060058</Real>
+      <Real Name="Z">85.782972880969737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-370.93129038488684</Real>
+      <Real Name="Y">-600.14277237904264</Real>
+      <Real Name="Z">242.79153355662427</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">643.13294921108331</Real>
+      <Real Name="Y">-492.27155715756072</Real>
+      <Real Name="Z">-420.98396170662772</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.97384167216792</Real>
+      <Real Name="Y">121.35582381662744</Real>
+      <Real Name="Z">75.022959572071613</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.06277071622355</Real>
+      <Real Name="Y">438.33123420494866</Real>
+      <Real Name="Z">218.98937252178408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.014544670929521</Real>
+      <Real Name="Y">295.74954357715103</Real>
+      <Real Name="Z">69.894555129322129</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.252333884320294</Real>
+      <Real Name="Y">-163.91312109375451</Real>
+      <Real Name="Z">-112.35407810341539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.12078254896801</Real>
+      <Real Name="Y">-215.13020707664643</Real>
+      <Real Name="Z">-140.17665530901047</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-591.88661545059654</Real>
+      <Real Name="Y">297.67293830011806</Real>
+      <Real Name="Z">826.32556478647189</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">397.93639892947715</Real>
+      <Real Name="Y">-70.314953968874306</Real>
+      <Real Name="Z">-868.24762476380488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.87258573868061</Real>
+      <Real Name="Y">-199.27650127733096</Real>
+      <Real Name="Z">-58.263933884350259</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1115.2414554917407</Real>
+      <Real Name="Y">630.13765118652259</Real>
+      <Real Name="Z">795.26889601670575</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.54644283236559</Real>
+      <Real Name="Y">-873.28714057360503</Real>
+      <Real Name="Z">-339.25254967654172</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-870.47703909844574</Real>
+      <Real Name="Y">286.55402923641446</Real>
+      <Real Name="Z">-544.52466870103444</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">357.06327985441129</Real>
+      <Real Name="Y">756.95518124975411</Real>
+      <Real Name="Z">146.15158808384231</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.1031911259369593</Real>
+      <Real Name="Y">-651.95807639425993</Real>
+      <Real Name="Z">-54.851563453446659</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-90.346160228676467</Real>
+      <Real Name="Y">-131.84844724474854</Real>
+      <Real Name="Z">-37.247322365720336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.521072971712503</Real>
+      <Real Name="Y">-138.2689214112886</Real>
+      <Real Name="Z">-868.8136039521338</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.014249513270272</Real>
+      <Real Name="Y">7.3692522628366071</Real>
+      <Real Name="Z">237.5634519065969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">409.19492757353737</Real>
+      <Real Name="Y">-13.742542210541785</Real>
+      <Real Name="Z">674.31104622319117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.659473626879418</Real>
+      <Real Name="Y">50.110645860879288</Real>
+      <Real Name="Z">-863.79034822029041</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-246.26232236317969</Real>
+      <Real Name="Y">161.94006229271525</Real>
+      <Real Name="Z">488.15427934452742</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">20.568406030418696</Real>
+      <Real Name="Y">-198.6892899661438</Real>
+      <Real Name="Z">167.75248290904577</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-614.59818505026306</Real>
+      <Real Name="Y">104.53188191490503</Real>
+      <Real Name="Z">354.02175633622505</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">537.6262257717641</Real>
+      <Real Name="Y">-78.613538027351382</Real>
+      <Real Name="Z">-4.2479072666175819</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.13435916894088</Real>
+      <Real Name="Y">-43.663731879204583</Real>
+      <Real Name="Z">-245.23655340698801</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-106.57483929941625</Real>
+      <Real Name="Y">29.631385426448077</Real>
+      <Real Name="Z">363.06870503124429</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">246.89597123723604</Real>
+      <Real Name="Y">-1.2874127968376285</Real>
+      <Real Name="Z">-127.71291982473038</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">59.181669587130301</Real>
+      <Real Name="Y">-111.70268840757583</Real>
+      <Real Name="Z">-115.61203661582745</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-356.38891633691321</Real>
+      <Real Name="Y">-2083.3236504731262</Real>
+      <Real Name="Z">-695.17090879229158</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">338.53999378384719</Real>
+      <Real Name="Y">1335.4212804188292</Real>
+      <Real Name="Z">310.34795082844755</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">276.91145975674965</Real>
+      <Real Name="Y">426.71413088577714</Real>
+      <Real Name="Z">68.866527233272805</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">408.59507850441543</Real>
+      <Real Name="Y">979.02141074081044</Real>
+      <Real Name="Z">-851.526963141637</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-141.87811546680328</Real>
+      <Real Name="Y">-531.63312668379092</Real>
+      <Real Name="Z">230.46019394509702</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-250.61603960596403</Real>
+      <Real Name="Y">-386.7770679612899</Real>
+      <Real Name="Z">568.34530693562169</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.995587717934299</Real>
+      <Real Name="Y">98.712841095074324</Real>
+      <Real Name="Z">5.9983147624240729</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.455554648307631</Real>
+      <Real Name="Y">-43.457716027921336</Real>
+      <Real Name="Z">-3.6230677277036509</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.238894805797361</Real>
+      <Real Name="Y">-33.183328509177713</Real>
+      <Real Name="Z">8.290184048624738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-423.87268137722117</Real>
+      <Real Name="Y">988.98544984437262</Real>
+      <Real Name="Z">389.59578780715793</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">598.97332901184529</Real>
+      <Real Name="Y">-698.11664446353973</Real>
+      <Real Name="Z">-450.10730846376248</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.34364562827345</Real>
+      <Real Name="Y">-160.39853837320439</Real>
+      <Real Name="Z">-11.909701289496965</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.38227541939267</Real>
+      <Real Name="Y">-502.43505656060677</Real>
+      <Real Name="Z">-1442.8567335744576</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.961575921876957</Real>
+      <Real Name="Y">370.46015000533043</Real>
+      <Real Name="Z">1053.1544367911799</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-283.3786389780056</Real>
+      <Real Name="Y">14.028334421804722</Real>
+      <Real Name="Z">269.58718254533261</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.54500817120885</Real>
+      <Real Name="Y">-124.32060349534872</Real>
+      <Real Name="Z">-117.58242863217779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-85.882129825154834</Real>
+      <Real Name="Y">126.24593710256312</Real>
+      <Real Name="Z">72.835100905771725</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.93886218419312</Real>
+      <Real Name="Y">40.618534773196188</Real>
+      <Real Name="Z">150.70574056399448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.19548516870333</Real>
+      <Real Name="Y">-338.35479607116042</Real>
+      <Real Name="Z">-76.46818050284358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.71627371711179</Real>
+      <Real Name="Y">414.14781220451323</Real>
+      <Real Name="Z">98.183831527550083</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.25066405747654</Real>
+      <Real Name="Y">67.408305033815282</Real>
+      <Real Name="Z">41.300523465622632</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">724.84747541170509</Real>
+      <Real Name="Y">-281.00289923583131</Real>
+      <Real Name="Z">-579.82146340419433</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.00030760178515</Real>
+      <Real Name="Y">205.71913222890808</Real>
+      <Real Name="Z">640.11714131550389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-247.02309440129918</Real>
+      <Real Name="Y">324.86740306678087</Real>
+      <Real Name="Z">148.15237130971008</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.68794587121838902</Real>
+      <Real Name="Y">-10.918056414628396</Real>
+      <Real Name="Z">9.5577584396643402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.52749685515944478</Real>
+      <Real Name="Y">6.9939843698716828</Real>
+      <Real Name="Z">-2.9467326595193413</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.7972037103186267</Real>
+      <Real Name="Y">4.938949961419226</Real>
+      <Real Name="Z">-7.0645942266187021</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-407.1777678437532</Real>
+      <Real Name="Y">846.31940310022628</Real>
+      <Real Name="Z">253.0508594524718</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">291.08358489735321</Real>
+      <Real Name="Y">-480.01846139005033</Real>
+      <Real Name="Z">-680.65370051849561</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.95701713939837</Real>
+      <Real Name="Y">-441.64736765089032</Real>
+      <Real Name="Z">290.35016275333317</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.52710645366994</Real>
+      <Real Name="Y">32.475232520892938</Real>
+      <Real Name="Z">-151.8656151387286</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.64908440468173</Real>
+      <Real Name="Y">57.971079189980131</Real>
+      <Real Name="Z">40.011441768176269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.314549130369073</Real>
+      <Real Name="Y">-39.18358876805712</Real>
+      <Real Name="Z">74.100816205379033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-596.39670293649624</Real>
+      <Real Name="Y">-1292.3005142204042</Real>
+      <Real Name="Z">-304.08335809891724</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">218.88063888924594</Real>
+      <Real Name="Y">615.97043682708647</Real>
+      <Real Name="Z">-293.46806381886199</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">393.941095378147</Real>
+      <Real Name="Y">650.36246860577478</Real>
+      <Real Name="Z">617.0186070104761</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1193.5625541953978</Real>
+      <Real Name="Y">443.09774155104782</Real>
+      <Real Name="Z">-792.07554408218812</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">819.54805096295104</Real>
+      <Real Name="Y">-311.5272381428573</Real>
+      <Real Name="Z">572.28102205107109</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.21525938176103</Real>
+      <Real Name="Y">-31.884059360249722</Real>
+      <Real Name="Z">38.946004715384362</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">216.15850170635446</Real>
+      <Real Name="Y">430.47933713906048</Real>
+      <Real Name="Z">164.33873622429152</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.186488846853734</Real>
+      <Real Name="Y">-134.86982156614835</Real>
+      <Real Name="Z">-70.851499829831525</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-108.02250844410699</Real>
+      <Real Name="Y">-124.21383949030434</Real>
+      <Real Name="Z">-17.182284298308229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.2102139643716</Real>
+      <Real Name="Y">-1074.0600988861561</Real>
+      <Real Name="Z">-860.12040003988045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.09631351027141</Real>
+      <Real Name="Y">209.20112844331436</Real>
+      <Real Name="Z">196.16055872121922</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">575.92938929566276</Real>
+      <Real Name="Y">691.03595203238672</Real>
+      <Real Name="Z">682.34735623050392</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.87293238339231</Real>
+      <Real Name="Y">390.16563378138687</Real>
+      <Real Name="Z">-1022.8807635907668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.946766957038406</Real>
+      <Real Name="Y">-318.27966099818241</Real>
+      <Real Name="Z">753.25575310306294</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.999201433446238</Real>
+      <Real Name="Y">32.162087183582138</Real>
+      <Real Name="Z">254.53780749288188</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.83319417526474</Real>
+      <Real Name="Y">-511.42160111938347</Real>
+      <Real Name="Z">-930.7545916378549</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.2684316090332</Real>
+      <Real Name="Y">79.126072631981458</Real>
+      <Real Name="Z">146.98848078531603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.37241768883009</Real>
+      <Real Name="Y">256.5071378774756</Real>
+      <Real Name="Z">804.46725926185024</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-438.49135878308761</Real>
+      <Real Name="Y">-223.30238733342128</Real>
+      <Real Name="Z">-199.42557980663062</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">224.94665536147181</Real>
+      <Real Name="Y">60.397394961607503</Real>
+      <Real Name="Z">145.17132426402827</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">225.78108849706797</Real>
+      <Real Name="Y">77.066302993338425</Real>
+      <Real Name="Z">123.62725603169116</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">568.84886865473914</Real>
+      <Real Name="Y">813.66417315414492</Real>
+      <Real Name="Z">-260.69733547705505</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.33249425656366</Real>
+      <Real Name="Y">-198.43558046463738</Real>
+      <Real Name="Z">15.699489687745341</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.96701168161371</Real>
+      <Real Name="Y">-861.01786705400093</Real>
+      <Real Name="Z">-32.84708944114638</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1744.757462296134</Real>
+      <Real Name="Y">-146.66621063577247</Real>
+      <Real Name="Z">947.77034102187622</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-863.39841375327842</Real>
+      <Real Name="Y">303.0700408385091</Real>
+      <Real Name="Z">-704.97142507446495</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.04914583004569</Real>
+      <Real Name="Y">-176.10333275396232</Real>
+      <Real Name="Z">-382.00197588191077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-153.51265365766068</Real>
+      <Real Name="Y">55.682711632940524</Real>
+      <Real Name="Z">125.95119344694072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.04454259918046</Real>
+      <Real Name="Y">-162.75546905473379</Real>
+      <Real Name="Z">29.863997850178237</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.87969745224927</Real>
+      <Real Name="Y">-7.5572463100664482</Real>
+      <Real Name="Z">25.019076372669105</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">688.7002995656652</Real>
+      <Real Name="Y">-329.95110474947739</Real>
+      <Real Name="Z">47.700853116411245</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-478.77452411476037</Real>
+      <Real Name="Y">292.29483850177564</Real>
+      <Real Name="Z">25.483874925481288</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.64533271491956</Real>
+      <Real Name="Y">328.12365259750294</Real>
+      <Real Name="Z">-128.69673281129479</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.70724968711534</Real>
+      <Real Name="Y">-1811.1284849631049</Real>
+      <Real Name="Z">50.011064274194879</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">128.98055318112472</Real>
+      <Real Name="Y">237.2512769076682</Real>
+      <Real Name="Z">-41.16764066459227</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.38657318257609</Real>
+      <Real Name="Y">1224.5307775252847</Real>
+      <Real Name="Z">-63.083839118256932</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.3952758499596</Real>
+      <Real Name="Y">1028.9705065304152</Real>
+      <Real Name="Z">825.64960192855472</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.67824706426148</Real>
+      <Real Name="Y">-646.08000387039488</Real>
+      <Real Name="Z">-555.45222920819947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.18806432884773</Real>
+      <Real Name="Y">-238.70602173311332</Real>
+      <Real Name="Z">-216.718232485985</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.872790373120161</Real>
+      <Real Name="Y">-445.93196712230281</Real>
+      <Real Name="Z">-491.63477130461644</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-158.08391737849212</Real>
+      <Real Name="Y">376.09770431398573</Real>
+      <Real Name="Z">254.38054752941696</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-93.630378338001904</Real>
+      <Real Name="Y">161.07479823713899</Real>
+      <Real Name="Z">477.56914648109358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">846.5829467664347</Real>
+      <Real Name="Y">-283.94843993019714</Real>
+      <Real Name="Z">-279.26547864362033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-199.05726966148305</Real>
+      <Real Name="Y">452.46098622046281</Real>
+      <Real Name="Z">22.097044488990065</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-761.22769586733045</Real>
+      <Real Name="Y">-163.09510880542257</Real>
+      <Real Name="Z">188.53999117987485</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1087.0226409823592</Real>
+      <Real Name="Y">1521.0629379593793</Real>
+      <Real Name="Z">-673.28708208710202</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.6615627106128</Real>
+      <Real Name="Y">-219.61038108924546</Real>
+      <Real Name="Z">95.491183339214487</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">620.03307835052578</Real>
+      <Real Name="Y">-974.0329995458751</Real>
+      <Real Name="Z">441.57011906625632</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.21437858901504</Real>
+      <Real Name="Y">29.394177803019943</Real>
+      <Real Name="Z">149.52787398836566</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.68534657016986</Real>
+      <Real Name="Y">92.883491557986673</Real>
+      <Real Name="Z">-36.451953545999842</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-914.89867906860502</Real>
+      <Real Name="Y">119.96913372915684</Real>
+      <Real Name="Z">-128.94440853053979</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-822.57591016044296</Real>
+      <Real Name="Y">-1221.0958091522396</Real>
+      <Real Name="Z">114.10865199930426</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">707.68357775193681</Real>
+      <Real Name="Y">941.3378086730138</Real>
+      <Real Name="Z">60.08123952200971</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">68.118872494929988</Real>
+      <Real Name="Y">251.30475317549264</Real>
+      <Real Name="Z">-76.473949582558873</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-136.65282148789936</Real>
+      <Real Name="Y">-697.21910253717851</Real>
+      <Real Name="Z">809.18798441336287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.89338735627115</Real>
+      <Real Name="Y">509.83954744747388</Real>
+      <Real Name="Z">-581.41940130466764</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.67823961465966</Real>
+      <Real Name="Y">79.157760754683295</Real>
+      <Real Name="Z">-119.01844882242128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">69.131623534012746</Real>
+      <Real Name="Y">-148.68344991621862</Real>
+      <Real Name="Z">341.82533875048694</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.625517994538882</Real>
+      <Real Name="Y">255.32914403115907</Real>
+      <Real Name="Z">-74.753944992517532</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.06049523514946</Real>
+      <Real Name="Y">369.96027004780274</Real>
+      <Real Name="Z">26.579877362345343</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">82.820827698233543</Real>
+      <Real Name="Y">465.88350598932919</Real>
+      <Real Name="Z">-66.907734395389554</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.497384168902684</Real>
+      <Real Name="Y">-170.16035372964217</Real>
+      <Real Name="Z">64.422695946654542</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.735839783873956</Real>
+      <Real Name="Y">-177.85511618815505</Real>
+      <Real Name="Z">21.9668699820024</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.768738756618845</Real>
+      <Real Name="Y">766.97744301649209</Real>
+      <Real Name="Z">75.479567431575134</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.58821232340248</Real>
+      <Real Name="Y">-166.13191453869467</Real>
+      <Real Name="Z">-66.48763753035945</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.568875992909028</Real>
+      <Real Name="Y">-724.08089225765798</Real>
+      <Real Name="Z">-12.754418581012846</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.510695019133635</Real>
+      <Real Name="Y">-723.00308685261155</Real>
+      <Real Name="Z">-210.45500936138853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.88143271417943</Real>
+      <Real Name="Y">626.18311519882616</Real>
+      <Real Name="Z">111.89769239385279</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.520329053469681</Real>
+      <Real Name="Y">176.58493843761451</Real>
+      <Real Name="Z">44.482642739153206</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">101.52451045017516</Real>
+      <Real Name="Y">789.97553692922509</Real>
+      <Real Name="Z">537.76276371298889</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.387112705658673</Real>
+      <Real Name="Y">-180.11031922451212</Real>
+      <Real Name="Z">-82.721588495514993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">139.17770138934267</Real>
+      <Real Name="Y">-585.66213346215727</Real>
+      <Real Name="Z">-443.16657208056012</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.18694320390682</Real>
+      <Real Name="Y">299.44004616555748</Real>
+      <Real Name="Z">-420.08667543296684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">164.5557151620923</Real>
+      <Real Name="Y">-252.18778117176723</Real>
+      <Real Name="Z">436.97612822774579</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.03195801634098</Real>
+      <Real Name="Y">-111.08763640750979</Real>
+      <Real Name="Z">92.585505294879098</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-885.46052772018641</Real>
+      <Real Name="Y">-83.732003959749477</Real>
+      <Real Name="Z">-57.064862482521292</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.8127298383738</Real>
+      <Real Name="Y">-69.788124065908164</Real>
+      <Real Name="Z">3.6633299646789794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">312.90062775162704</Real>
+      <Real Name="Y">42.177833602993232</Real>
+      <Real Name="Z">7.3131809492190882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1533.2174799720942</Real>
+      <Real Name="Y">-268.10905253353292</Real>
+      <Real Name="Z">-639.90636683554339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">298.84618125819253</Real>
+      <Real Name="Y">-41.605345118276801</Real>
+      <Real Name="Z">203.82151757771993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1071.9022296426933</Real>
+      <Real Name="Y">337.55449647805744</Real>
+      <Real Name="Z">324.01417532920465</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-414.60805681345437</Real>
+      <Real Name="Y">1396.6925824766813</Real>
+      <Real Name="Z">326.29888074705963</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.270992502602507</Real>
+      <Real Name="Y">-723.13253896504352</Real>
+      <Real Name="Z">-590.00231110063214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">383.87370665683045</Real>
+      <Real Name="Y">-438.32728665511968</Real>
+      <Real Name="Z">485.93865538869841</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.20567418801926</Real>
+      <Real Name="Y">440.55155433582911</Real>
+      <Real Name="Z">126.63485402593076</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.498271116675255</Real>
+      <Real Name="Y">-125.11100292886805</Real>
+      <Real Name="Z">-82.322492614618866</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-223.47859284693843</Real>
+      <Real Name="Y">-496.10913637041182</Real>
+      <Real Name="Z">-108.89657536146126</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1156.0118303174443</Real>
+      <Real Name="Y">-1972.0456679060248</Real>
+      <Real Name="Z">678.16981557974532</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-122.94929937397522</Real>
+      <Real Name="Y">1194.1370171101557</Real>
+      <Real Name="Z">314.51716430902258</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1030.773302548535</Real>
+      <Real Name="Y">372.63604053007759</Real>
+      <Real Name="Z">-811.24852228744567</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">776.88680358458089</Real>
+      <Real Name="Y">-1294.7655146112756</Real>
+      <Real Name="Z">-289.79864401115424</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-239.76900063202052</Real>
+      <Real Name="Y">679.80430081036627</Real>
+      <Real Name="Z">20.614068686324089</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.5961873087308</Real>
+      <Real Name="Y">693.43540616696725</Real>
+      <Real Name="Z">319.41442387566809</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">85.941922480906157</Real>
+      <Real Name="Y">843.08532003855134</Real>
+      <Real Name="Z">1349.7845943028874</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-105.20094992303544</Real>
+      <Real Name="Y">-723.44706372341761</Real>
+      <Real Name="Z">-1015.3103937742756</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.93788989090284</Real>
+      <Real Name="Y">7.4760387114040654</Real>
+      <Real Name="Z">-260.68171216377658</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.020000 Step 20 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-1226.134884609939</Real>
+      <Real Name="Y">413.4570837726485</Real>
+      <Real Name="Z">1231.3630258432593</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-85.323268735250764</Real>
+      <Real Name="Y">-94.736218644359184</Real>
+      <Real Name="Z">-80.607295451969009</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">416.58319150103438</Real>
+      <Real Name="Y">-2.5906028992653063</Real>
+      <Real Name="Z">-241.77794491837852</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">356.88891085292403</Real>
+      <Real Name="Y">-484.69307904314979</Real>
+      <Real Name="Z">-595.83876857588473</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1140.5148370510988</Real>
+      <Real Name="Y">156.60016116581224</Real>
+      <Real Name="Z">-520.97893579789604</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-894.44076039146694</Real>
+      <Real Name="Y">-321.5321429167879</Real>
+      <Real Name="Z">-133.06542698593765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-141.36937949825162</Real>
+      <Real Name="Y">591.52721342974542</Real>
+      <Real Name="Z">396.61853356030281</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">340.49499705181398</Real>
+      <Real Name="Y">-131.67405230585439</Real>
+      <Real Name="Z">-179.76227417927015</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-181.4284500149648</Real>
+      <Real Name="Y">-360.46158000584489</Real>
+      <Real Name="Z">-21.723253263998327</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.8494149637819532</Real>
+      <Real Name="Y">-0.062188736532164945</Real>
+      <Real Name="Z">21.115406793313518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">9.8103750088951358</Real>
+      <Real Name="Y">8.9768431692237058</Real>
+      <Real Name="Z">-4.9662625888240797</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.1876162192455695</Real>
+      <Real Name="Y">2.6049272707433726</Real>
+      <Real Name="Z">-11.364989974970783</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-23.589093445598081</Real>
+      <Real Name="Y">4.4827914352118654</Real>
+      <Real Name="Z">-25.418491476976591</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.5206613308997987</Real>
+      <Real Name="Y">-1.1942363341385835</Real>
+      <Real Name="Z">12.545218988250785</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.996395891588762</Real>
+      <Real Name="Y">-12.319839149418927</Real>
+      <Real Name="Z">10.795663472562108</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">287.22493919617665</Real>
+      <Real Name="Y">-118.12495141873987</Real>
+      <Real Name="Z">-750.57181033572124</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.11205343437706</Real>
+      <Real Name="Y">200.02660922774584</Real>
+      <Real Name="Z">399.91748430708003</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">34.043044054912585</Real>
+      <Real Name="Y">9.4493219372098114</Real>
+      <Real Name="Z">177.06018355924593</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">401.55874628723257</Real>
+      <Real Name="Y">607.79232053944793</Real>
+      <Real Name="Z">-222.06431397796919</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.236754774461502</Real>
+      <Real Name="Y">-249.95582033955174</Real>
+      <Real Name="Z">86.703177361509262</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-355.53835972546113</Real>
+      <Real Name="Y">-572.48431282935212</Real>
+      <Real Name="Z">306.38377797245374</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">439.10790196275843</Real>
+      <Real Name="Y">-423.30707732123153</Real>
+      <Real Name="Z">-483.35727903210642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.776282954693698</Real>
+      <Real Name="Y">146.56024933385959</Real>
+      <Real Name="Z">125.66443649332481</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-456.79421717143822</Real>
+      <Real Name="Y">420.35385929627034</Real>
+      <Real Name="Z">456.84582739106855</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">121.22366515471623</Real>
+      <Real Name="Y">220.07652361751786</Real>
+      <Real Name="Z">269.83839782912031</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.195459227921063</Real>
+      <Real Name="Y">-160.12204412055732</Real>
+      <Real Name="Z">-130.50790469638937</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-184.04589401916672</Real>
+      <Real Name="Y">-237.89251938277587</Real>
+      <Real Name="Z">-139.97685481303424</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-427.4494252919618</Real>
+      <Real Name="Y">377.18264005779537</Real>
+      <Real Name="Z">881.84235130078582</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">401.99346788161307</Real>
+      <Real Name="Y">-239.87787935558734</Real>
+      <Real Name="Z">-825.69634428643383</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">152.44523315452295</Real>
+      <Real Name="Y">-213.16213982280661</Real>
+      <Real Name="Z">-118.20663312463178</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1222.3987300445078</Real>
+      <Real Name="Y">285.22693974295703</Real>
+      <Real Name="Z">876.56042006603707</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">43.749854735121026</Real>
+      <Real Name="Y">-523.30976736444177</Real>
+      <Real Name="Z">-210.30895416564724</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-959.11228684983109</Real>
+      <Real Name="Y">539.46051227846726</Real>
+      <Real Name="Z">-560.12567424315512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">266.1200134130213</Real>
+      <Real Name="Y">998.19763676146067</Real>
+      <Real Name="Z">168.67704904378411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.37629957501133</Real>
+      <Real Name="Y">-367.78966460043461</Real>
+      <Real Name="Z">-48.718822856079811</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.74063976377036</Real>
+      <Real Name="Y">-178.6736644174062</Real>
+      <Real Name="Z">-53.733696737177027</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-78.085323053363169</Real>
+      <Real Name="Y">-0.58095258845514763</Real>
+      <Real Name="Z">-1016.9423037811068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-153.21441257179075</Real>
+      <Real Name="Y">282.17283108432616</Real>
+      <Real Name="Z">330.88840096446933</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">280.27361501109499</Real>
+      <Real Name="Y">138.53046747066472</Real>
+      <Real Name="Z">316.33865622309111</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">503.82028252492182</Real>
+      <Real Name="Y">214.34872346435304</Real>
+      <Real Name="Z">-402.60548759877827</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-487.63291726648765</Real>
+      <Real Name="Y">70.254282261956561</Real>
+      <Real Name="Z">647.13361908242382</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-187.24985181112197</Real>
+      <Real Name="Y">-228.47645834470777</Real>
+      <Real Name="Z">53.170512508158865</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-966.83725705457925</Real>
+      <Real Name="Y">159.98949532531174</Real>
+      <Real Name="Z">268.62586726097635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">588.19200972303543</Real>
+      <Real Name="Y">-302.76188418099366</Real>
+      <Real Name="Z">-159.78552745938293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.38577081521686</Real>
+      <Real Name="Y">12.59115478445487</Real>
+      <Real Name="Z">-175.94437215330095</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-419.14698203385615</Real>
+      <Real Name="Y">85.781262833141454</Real>
+      <Real Name="Z">271.07936934839944</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.81341489186963</Real>
+      <Real Name="Y">-3.9580190645192346</Real>
+      <Real Name="Z">-52.260299987987729</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">154.08324039954428</Real>
+      <Real Name="Y">-195.7416015656421</Real>
+      <Real Name="Z">-96.362896584008354</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-150.97505450118103</Real>
+      <Real Name="Y">-1445.7778235058506</Real>
+      <Real Name="Z">-666.33037717225238</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">39.760046234305136</Real>
+      <Real Name="Y">1347.7356059414117</Real>
+      <Real Name="Z">460.59589305950846</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">351.69195691122741</Real>
+      <Real Name="Y">364.22124904493194</Real>
+      <Real Name="Z">117.74474505618326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">483.97774575555633</Real>
+      <Real Name="Y">788.20244431726212</Real>
+      <Real Name="Z">-898.00217515329916</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.83434428601964</Real>
+      <Real Name="Y">-466.69129913785258</Real>
+      <Real Name="Z">35.306396505878624</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-446.74963507587461</Real>
+      <Real Name="Y">-188.75365052318301</Real>
+      <Real Name="Z">311.43169111935202</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">66.352989492426218</Real>
+      <Real Name="Y">124.24035412499379</Real>
+      <Real Name="Z">31.138376432549755</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-15.578056952468984</Real>
+      <Real Name="Y">-50.655376229574046</Real>
+      <Real Name="Z">-12.320553283078642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.588597863697125</Real>
+      <Real Name="Y">-41.283290487256288</Real>
+      <Real Name="Z">-10.72324445519317</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-404.81501497894283</Real>
+      <Real Name="Y">910.37984806142538</Real>
+      <Real Name="Z">251.31628052288676</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">457.44738879380282</Real>
+      <Real Name="Y">-690.72044135801525</Real>
+      <Real Name="Z">-415.63494020669594</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.865136607959819</Real>
+      <Real Name="Y">-154.70730997270374</Real>
+      <Real Name="Z">-30.768307529923526</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">637.85142917536177</Real>
+      <Real Name="Y">-693.59617340601005</Real>
+      <Real Name="Z">-1242.7120496224723</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-398.96047096365999</Real>
+      <Real Name="Y">288.43127720179336</Real>
+      <Real Name="Z">975.48086703897366</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-481.16252164309992</Real>
+      <Real Name="Y">205.57064998000726</Real>
+      <Real Name="Z">47.91356358195263</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">41.452463512827478</Real>
+      <Real Name="Y">-259.0150920911654</Real>
+      <Real Name="Z">25.882413986179941</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.308123730202837</Real>
+      <Real Name="Y">113.73544186218739</Real>
+      <Real Name="Z">36.006956282678125</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-38.610836330324936</Real>
+      <Real Name="Y">66.517223218832427</Real>
+      <Real Name="Z">129.56131612120521</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">333.22003208537973</Real>
+      <Real Name="Y">-307.04558201539305</Real>
+      <Real Name="Z">-94.404367934991996</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-307.0846074306765</Real>
+      <Real Name="Y">376.19480028311256</Real>
+      <Real Name="Z">155.31830398189643</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-88.001165765187835</Real>
+      <Real Name="Y">46.383966323524589</Real>
+      <Real Name="Z">52.333256882467012</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">787.34271280633141</Real>
+      <Real Name="Y">-1258.7948721127473</Real>
+      <Real Name="Z">-576.95650363415803</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-450.95943015522369</Real>
+      <Real Name="Y">312.8075581122348</Real>
+      <Real Name="Z">704.72333421202677</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-151.94314280286</Real>
+      <Real Name="Y">948.50757037134838</Real>
+      <Real Name="Z">160.98965936963674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.1639458318481886</Real>
+      <Real Name="Y">-10.89869240761281</Real>
+      <Real Name="Z">23.048195403788071</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.2636240465431712</Real>
+      <Real Name="Y">5.8245410131114879</Real>
+      <Real Name="Z">-8.4986717496583424</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.2125780810363125</Real>
+      <Real Name="Y">5.8670645724154378</Real>
+      <Real Name="Z">-13.459163423826993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-148.28038718826517</Real>
+      <Real Name="Y">808.49212816371141</Real>
+      <Real Name="Z">99.087596893847206</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">471.75293784583488</Real>
+      <Real Name="Y">-385.75740340481201</Real>
+      <Real Name="Z">-514.95345485354653</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-216.42761927616834</Real>
+      <Real Name="Y">-359.03434979543977</Real>
+      <Real Name="Z">318.6004041578903</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">342.22979335226597</Real>
+      <Real Name="Y">40.162076990385543</Real>
+      <Real Name="Z">-314.49977326687429</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-307.38652148247752</Real>
+      <Real Name="Y">48.819360812239623</Real>
+      <Real Name="Z">81.076184657328326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-80.756587207593213</Real>
+      <Real Name="Y">-16.234102944348201</Real>
+      <Real Name="Z">102.64183200150346</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-376.73471452754228</Real>
+      <Real Name="Y">-1096.821009218417</Real>
+      <Real Name="Z">-523.72986033109214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">147.54275820403473</Real>
+      <Real Name="Y">318.48649388436962</Real>
+      <Real Name="Z">-145.38270570975743</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">392.35555349593233</Real>
+      <Real Name="Y">696.51637464340843</Real>
+      <Real Name="Z">673.72564604723016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-864.10804340009258</Real>
+      <Real Name="Y">329.18038894323729</Real>
+      <Real Name="Z">-510.27437727433664</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">684.43994444707414</Real>
+      <Real Name="Y">-270.31717206686614</Real>
+      <Real Name="Z">485.15465344508334</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.23314491781383</Real>
+      <Real Name="Y">-1.4473788359119268</Real>
+      <Real Name="Z">33.464913905850814</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">258.86950902791978</Real>
+      <Real Name="Y">422.60250474917842</Real>
+      <Real Name="Z">79.858461253500948</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-54.078916250470627</Real>
+      <Real Name="Y">-152.45709725898186</Real>
+      <Real Name="Z">-63.407574512561432</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-125.09124298901529</Real>
+      <Real Name="Y">-149.61862551204754</Real>
+      <Real Name="Z">10.95512748414988</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-821.2705475190246</Real>
+      <Real Name="Y">-795.01297586438614</Real>
+      <Real Name="Z">-521.29566524896018</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.6882772050223</Real>
+      <Real Name="Y">200.33464639561245</Real>
+      <Real Name="Z">153.63255523515213</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">705.22509544979835</Real>
+      <Real Name="Y">521.58503720123724</Real>
+      <Real Name="Z">398.27792966241316</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.417946054917088</Real>
+      <Real Name="Y">-76.560528213780032</Real>
+      <Real Name="Z">-784.62604242554198</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.94408043254051</Real>
+      <Real Name="Y">-195.15124144610473</Real>
+      <Real Name="Z">724.10582973624787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">63.077700915656848</Real>
+      <Real Name="Y">59.803024048014464</Real>
+      <Real Name="Z">153.67109292988064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-537.00134976057063</Real>
+      <Real Name="Y">-519.5853343654552</Real>
+      <Real Name="Z">-917.08004280413377</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">126.97381694275751</Real>
+      <Real Name="Y">76.931620354047922</Real>
+      <Real Name="Z">156.36339141069044</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.95134097470088</Real>
+      <Real Name="Y">494.17874168583882</Real>
+      <Real Name="Z">743.29178504807192</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-554.90964266252854</Real>
+      <Real Name="Y">-337.64498058493683</Real>
+      <Real Name="Z">-11.292775020575551</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">560.88029613788808</Real>
+      <Real Name="Y">80.929143749276164</Real>
+      <Real Name="Z">-82.131272793696326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">250.9661783393812</Real>
+      <Real Name="Y">82.968128608720292</Real>
+      <Real Name="Z">-15.759139457738211</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.37882920688924</Real>
+      <Real Name="Y">797.14111587064815</Real>
+      <Real Name="Z">44.689397147357624</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-15.999424473374567</Real>
+      <Real Name="Y">-168.86055637489818</Real>
+      <Real Name="Z">-19.451327769921647</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-205.87885876975719</Real>
+      <Real Name="Y">-831.79344657212437</Real>
+      <Real Name="Z">-141.50712524749633</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1740.0884931562277</Real>
+      <Real Name="Y">-245.49802944071882</Real>
+      <Real Name="Z">704.9733559529908</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1031.0865108108121</Real>
+      <Real Name="Y">308.45158309093824</Real>
+      <Real Name="Z">-554.48739282125814</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-338.90671003455344</Real>
+      <Real Name="Y">-182.23457422232573</Real>
+      <Real Name="Z">-304.29632139225555</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-303.74474946001163</Real>
+      <Real Name="Y">68.709876507162562</Real>
+      <Real Name="Z">-99.110940421594961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.17472102105108</Real>
+      <Real Name="Y">-135.22151687561336</Real>
+      <Real Name="Z">53.167680614632552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.7380253201172</Real>
+      <Real Name="Y">-5.1081653215689506</Real>
+      <Real Name="Z">37.898641348546057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">936.41878124851496</Real>
+      <Real Name="Y">-265.98276706104008</Real>
+      <Real Name="Z">-0.94309784912736916</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-743.8729486803868</Real>
+      <Real Name="Y">282.44415632772194</Real>
+      <Real Name="Z">100.14017609825402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-427.4321037516375</Real>
+      <Real Name="Y">303.73276554566115</Real>
+      <Real Name="Z">-49.723644065192218</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-196.12661632392775</Real>
+      <Real Name="Y">-1142.9362130046231</Real>
+      <Real Name="Z">-15.563918553483873</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">89.821441687424866</Real>
+      <Real Name="Y">177.07608354897184</Real>
+      <Real Name="Z">-30.789927586541719</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.65589542190935</Real>
+      <Real Name="Y">1126.6780492604585</Real>
+      <Real Name="Z">37.862379578921598</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">230.60225592269848</Real>
+      <Real Name="Y">1110.276624010759</Real>
+      <Real Name="Z">817.57970830526642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-183.74141483575679</Real>
+      <Real Name="Y">-673.29450028432984</Real>
+      <Real Name="Z">-666.63551914096604</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.493168574083768</Real>
+      <Real Name="Y">-291.35708810992838</Real>
+      <Real Name="Z">-150.46193594913785</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.66185351085642</Real>
+      <Real Name="Y">-272.93025383184488</Real>
+      <Real Name="Z">-278.24997731520318</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-175.75337068211886</Real>
+      <Real Name="Y">333.93358504708516</Real>
+      <Real Name="Z">2.6739866996949075</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">101.34633262592938</Real>
+      <Real Name="Y">30.468736403743748</Real>
+      <Real Name="Z">309.22164559950141</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">671.78971451359894</Real>
+      <Real Name="Y">-466.16204579108887</Real>
+      <Real Name="Z">-169.32876990953267</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.51788445112254</Real>
+      <Real Name="Y">536.74253664974276</Real>
+      <Real Name="Z">35.34918641870604</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-591.85833143777779</Real>
+      <Real Name="Y">-75.07835293510513</Real>
+      <Real Name="Z">199.1222263746597</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-985.42386132351351</Real>
+      <Real Name="Y">1152.6249021561748</Real>
+      <Real Name="Z">-659.76445860566616</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.0673335664155</Real>
+      <Real Name="Y">-163.11512248286698</Real>
+      <Real Name="Z">98.92056939246001</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">572.10469393472738</Real>
+      <Real Name="Y">-819.86204865844593</Real>
+      <Real Name="Z">525.56868739869674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">933.52450166631445</Real>
+      <Real Name="Y">-129.13587235031437</Real>
+      <Real Name="Z">45.631931930666894</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-156.16146583631783</Real>
+      <Real Name="Y">92.54705380850622</Real>
+      <Real Name="Z">-28.981773741355216</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-894.83366499896306</Real>
+      <Real Name="Y">-54.832211277043321</Real>
+      <Real Name="Z">-93.921509391228412</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-566.70053828538789</Real>
+      <Real Name="Y">-707.68013651557089</Real>
+      <Real Name="Z">121.02148506413731</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">586.51191022304226</Real>
+      <Real Name="Y">702.97953205344572</Real>
+      <Real Name="Z">25.959556143591414</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">24.432909838824724</Real>
+      <Real Name="Y">132.55954581557694</Real>
+      <Real Name="Z">-75.778045815222896</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-269.7108293214531</Real>
+      <Real Name="Y">-1092.1174564042183</Real>
+      <Real Name="Z">1204.9527550017294</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">307.9602222670967</Real>
+      <Real Name="Y">615.57774126466404</Real>
+      <Real Name="Z">-559.5512407833703</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.222622180636392</Real>
+      <Real Name="Y">136.15050677042584</Real>
+      <Real Name="Z">-192.69182126570178</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">389.00519608835748</Real>
+      <Real Name="Y">-472.73393032344103</Real>
+      <Real Name="Z">-7.4154379569036166</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-89.390295254023357</Real>
+      <Real Name="Y">265.82336436915176</Real>
+      <Real Name="Z">-44.256453579258604</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-408.82185837299397</Real>
+      <Real Name="Y">334.99719238724629</Real>
+      <Real Name="Z">-17.209497971827453</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-107.60667118396105</Real>
+      <Real Name="Y">453.8729617250309</Real>
+      <Real Name="Z">-10.619189663593239</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-41.328841122456225</Real>
+      <Real Name="Y">-218.18359602625904</Real>
+      <Real Name="Z">42.840526728595961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">37.145938501768143</Real>
+      <Real Name="Y">-141.36063796041469</Real>
+      <Real Name="Z">7.4662555729026607</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.395931762371223</Real>
+      <Real Name="Y">777.83573458109356</Real>
+      <Real Name="Z">68.064452401653043</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-23.820888171982524</Real>
+      <Real Name="Y">-184.06304569686597</Real>
+      <Real Name="Z">-61.515096422562401</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">155.11468240262053</Real>
+      <Real Name="Y">-676.86547493790215</Real>
+      <Real Name="Z">-43.441726292772806</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-122.97434541756124</Real>
+      <Real Name="Y">-678.64250966122142</Real>
+      <Real Name="Z">-268.47426819914529</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">210.91604796195148</Real>
+      <Real Name="Y">596.24769633345056</Real>
+      <Real Name="Z">153.57747693255016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">95.543045585335207</Real>
+      <Real Name="Y">152.44487096824867</Real>
+      <Real Name="Z">36.180572019463597</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-167.76082249636374</Real>
+      <Real Name="Y">973.20040960400502</Real>
+      <Real Name="Z">843.21032426194552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">69.978279221035905</Real>
+      <Real Name="Y">-183.75635519990141</Real>
+      <Real Name="Z">-203.80481655021742</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.96503751014711</Real>
+      <Real Name="Y">-726.06648663399176</Real>
+      <Real Name="Z">-512.64707116067939</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-218.41133889205884</Real>
+      <Real Name="Y">188.69444108533131</Real>
+      <Real Name="Z">-347.07911436938167</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">105.82024286391415</Real>
+      <Real Name="Y">-107.92259386253197</Real>
+      <Real Name="Z">347.2005850261354</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">123.86096087692292</Real>
+      <Real Name="Y">-70.974887010711868</Real>
+      <Real Name="Z">110.44998000259854</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-986.11760969857949</Real>
+      <Real Name="Y">-8.0523498477007394</Real>
+      <Real Name="Z">-54.145837386561283</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">719.91412932695982</Real>
+      <Real Name="Y">-123.21591293929663</Real>
+      <Real Name="Z">-18.329190894522675</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.14909767290123</Real>
+      <Real Name="Y">9.3425929511972754</Real>
+      <Real Name="Z">6.0789272599549804</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1543.6673472263922</Real>
+      <Real Name="Y">-906.41922509844153</Real>
+      <Real Name="Z">-475.19141266508996</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">335.50031775907632</Real>
+      <Real Name="Y">70.280347089101909</Real>
+      <Real Name="Z">139.54129323817091</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1176.4797473363456</Real>
+      <Real Name="Y">220.32316255082517</Real>
+      <Real Name="Z">338.56862496789563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.3690768116666021</Real>
+      <Real Name="Y">1506.471223473603</Real>
+      <Real Name="Z">67.999303356171907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.297411224195898</Real>
+      <Real Name="Y">-824.02663908165437</Real>
+      <Real Name="Z">-431.94925007517202</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.43140295523148</Real>
+      <Real Name="Y">-633.5963020274221</Real>
+      <Real Name="Z">584.90150895039642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">245.62588340998698</Real>
+      <Real Name="Y">502.81533666787163</Real>
+      <Real Name="Z">163.19635633575436</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.633770303451911</Real>
+      <Real Name="Y">-129.63880190309661</Real>
+      <Real Name="Z">-107.20032227229198</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-261.44000590837595</Real>
+      <Real Name="Y">-510.39996357435024</Real>
+      <Real Name="Z">-78.160369827052037</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">767.19098020582157</Real>
+      <Real Name="Y">-1703.2021536086988</Real>
+      <Real Name="Z">816.02204901545417</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-55.374224108607933</Real>
+      <Real Name="Y">1197.7825303347081</Real>
+      <Real Name="Z">114.99452095367641</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-944.44488217551122</Real>
+      <Real Name="Y">572.98136712967698</Real>
+      <Real Name="Z">-727.06867234014419</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">815.40010432511838</Real>
+      <Real Name="Y">-2000.8283633569085</Real>
+      <Real Name="Z">-661.73240763626688</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.850913816851971</Real>
+      <Real Name="Y">846.53280013084043</Real>
+      <Real Name="Z">209.93122192586452</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-550.60992671067368</Real>
+      <Real Name="Y">691.60979641736583</Real>
+      <Real Name="Z">432.24250809916759</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">87.540625732800052</Real>
+      <Real Name="Y">716.75060065963282</Real>
+      <Real Name="Z">1173.9394460700912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-107.54709272179335</Real>
+      <Real Name="Y">-584.70951970237581</Real>
+      <Real Name="Z">-1007.3080821507901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.864476561377188</Real>
+      <Real Name="Y">-63.737951455560598</Real>
+      <Real Name="Z">-256.32515956116606</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.040000 Step 40 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">693.79815369223968</Real>
+      <Real Name="Y">-390.36441929407209</Real>
+      <Real Name="Z">54.710577492616977</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.974938170523814</Real>
+      <Real Name="Y">-39.875308969799995</Real>
+      <Real Name="Z">518.11171214441538</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">303.33173780175179</Real>
+      <Real Name="Y">269.30264354776301</Real>
+      <Real Name="Z">-702.03405872475503</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.782495713763247</Real>
+      <Real Name="Y">-180.51967725256705</Real>
+      <Real Name="Z">-396.01884238244611</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.524357285703545</Real>
+      <Real Name="Y">1520.7075345127323</Real>
+      <Real Name="Z">579.52018048643356</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-253.3481931598088</Real>
+      <Real Name="Y">-634.7530598614012</Real>
+      <Real Name="Z">461.074546915415</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-498.79736474127014</Real>
+      <Real Name="Y">-1732.9560043497872</Real>
+      <Real Name="Z">-952.37602451148041</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.364464343582171</Real>
+      <Real Name="Y">758.49483673975533</Real>
+      <Real Name="Z">-53.780619089886272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-135.38270920686503</Real>
+      <Real Name="Y">386.08297294531332</Real>
+      <Real Name="Z">158.29103164102506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">12.271265937215389</Real>
+      <Real Name="Y">33.905192639722578</Real>
+      <Real Name="Z">1.5195768834209926</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.06346553411353284</Real>
+      <Real Name="Y">-11.742090662798628</Real>
+      <Real Name="Z">10.97606369572723</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-17.093858758387462</Real>
+      <Real Name="Y">-11.225622046670232</Real>
+      <Real Name="Z">-9.8742671046275969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.0335157817758045</Real>
+      <Real Name="Y">-9.7149920624907011</Real>
+      <Real Name="Z">-50.078880419175064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.46469069673456431</Real>
+      <Real Name="Y">2.3982315628647353</Real>
+      <Real Name="Z">19.683544429989553</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.6680773219634517</Real>
+      <Real Name="Y">0.82743473356893116</Real>
+      <Real Name="Z">23.164539021553168</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">192.09798095978056</Real>
+      <Real Name="Y">-112.75582012385024</Real>
+      <Real Name="Z">-672.35987741168992</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-190.89586640934152</Real>
+      <Real Name="Y">-42.129752774949878</Real>
+      <Real Name="Z">640.48280526834469</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">28.896622523178181</Real>
+      <Real Name="Y">-0.33072724178046542</Real>
+      <Real Name="Z">119.88466584052814</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">466.56324425214905</Real>
+      <Real Name="Y">1237.1307690030278</Real>
+      <Real Name="Z">-179.36756479028054</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-53.286090103821849</Real>
+      <Real Name="Y">-259.55196845301737</Real>
+      <Real Name="Z">13.64799129339336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-577.308746780761</Real>
+      <Real Name="Y">-939.97662233851054</Real>
+      <Real Name="Z">96.498644090661571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">455.3383304250562</Real>
+      <Real Name="Y">-462.7017677141211</Real>
+      <Real Name="Z">-495.73528419326828</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-81.17550679640668</Real>
+      <Real Name="Y">172.51218743960857</Real>
+      <Real Name="Z">119.7906489245494</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-506.38633553053694</Real>
+      <Real Name="Y">382.94179002149588</Real>
+      <Real Name="Z">459.16462331690309</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">117.49784932864381</Real>
+      <Real Name="Y">463.37165042062776</Real>
+      <Real Name="Z">226.09222600027422</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-17.439253378993847</Real>
+      <Real Name="Y">-178.09450979140445</Real>
+      <Real Name="Z">-100.67882591749306</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-160.80165620408826</Real>
+      <Real Name="Y">-267.07686177905242</Real>
+      <Real Name="Z">-62.986609278236408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-428.3171215505389</Real>
+      <Real Name="Y">-68.426192639882231</Real>
+      <Real Name="Z">1005.6115350560759</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">422.79045649752726</Real>
+      <Real Name="Y">-57.60027453198343</Real>
+      <Real Name="Z">-917.43138808955882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">244.90912134708893</Real>
+      <Real Name="Y">-125.01113124771904</Real>
+      <Real Name="Z">-13.678685052797945</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1531.4941741357998</Real>
+      <Real Name="Y">397.984818367755</Real>
+      <Real Name="Z">675.107799148298</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-356.9755065158007</Real>
+      <Real Name="Y">-667.70007844719373</Real>
+      <Real Name="Z">-233.7585423056203</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1034.0291453437815</Real>
+      <Real Name="Y">236.03346760994211</Real>
+      <Real Name="Z">-666.97113984222779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">126.63570106979485</Real>
+      <Real Name="Y">1059.9966438745951</Real>
+      <Real Name="Z">6.5748773391200714</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.38783517747892</Real>
+      <Real Name="Y">-531.01751530141917</Real>
+      <Real Name="Z">1.7814731558464381</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.207665367480466</Real>
+      <Real Name="Y">-326.36034885239093</Real>
+      <Real Name="Z">-39.522212613996423</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-156.69923844075558</Real>
+      <Real Name="Y">-54.678616500009241</Real>
+      <Real Name="Z">-816.76195335363491</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-173.31705880097849</Real>
+      <Real Name="Y">298.23475355602278</Real>
+      <Real Name="Z">260.89171587985595</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">426.74999838748045</Real>
+      <Real Name="Y">237.56683890619098</Real>
+      <Real Name="Z">367.21351407417023</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">656.75626596531697</Real>
+      <Real Name="Y">5.1732450755659647</Real>
+      <Real Name="Z">-479.36930655333185</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-336.03753520294231</Real>
+      <Real Name="Y">213.17518061490296</Real>
+      <Real Name="Z">639.06293637418742</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.49912224427652</Real>
+      <Real Name="Y">-289.89616944742744</Real>
+      <Real Name="Z">50.728804585497983</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1584.5517588944808</Real>
+      <Real Name="Y">298.31471687679311</Real>
+      <Real Name="Z">-82.491781272748909</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">840.59709794510434</Real>
+      <Real Name="Y">-89.255269281785289</Real>
+      <Real Name="Z">1.6914673805335738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">284.26651850504129</Real>
+      <Real Name="Y">-17.704982947228778</Real>
+      <Real Name="Z">-122.53126301352552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-360.99436530419666</Real>
+      <Real Name="Y">61.415595660198733</Real>
+      <Real Name="Z">373.32945717362037</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.864834995639683</Real>
+      <Real Name="Y">22.279190856142122</Real>
+      <Real Name="Z">-107.31547443491087</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">118.28571251074503</Real>
+      <Real Name="Y">-137.42388707030773</Real>
+      <Real Name="Z">-113.16186274425201</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.59081179416492</Real>
+      <Real Name="Y">-1083.8687095100222</Real>
+      <Real Name="Z">-155.52436385163884</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.5406244419540229</Real>
+      <Real Name="Y">1207.2181220040602</Real>
+      <Real Name="Z">359.55305665456092</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">242.24234782907092</Real>
+      <Real Name="Y">231.71660423726885</Real>
+      <Real Name="Z">14.907578113701275</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.741708750010943</Real>
+      <Real Name="Y">900.52260046291576</Real>
+      <Real Name="Z">-707.72231709343998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">53.13045459992469</Real>
+      <Real Name="Y">-549.26577062774584</Real>
+      <Real Name="Z">194.30707583521831</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-219.69099927777077</Real>
+      <Real Name="Y">-527.2144422726102</Real>
+      <Real Name="Z">618.24237317025211</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">63.421962822290531</Real>
+      <Real Name="Y">139.46372559272797</Real>
+      <Real Name="Z">59.880011980440472</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-12.174398292482365</Real>
+      <Real Name="Y">-55.029211425188699</Real>
+      <Real Name="Z">-23.342335951250426</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-32.681827558122649</Real>
+      <Real Name="Y">-48.271487041327596</Real>
+      <Real Name="Z">-34.369328993680561</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-598.4363113234856</Real>
+      <Real Name="Y">867.11105648409637</Real>
+      <Real Name="Z">331.96121073007316</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">663.28353163366091</Real>
+      <Real Name="Y">-602.31053114472525</Real>
+      <Real Name="Z">-307.2971113404277</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">94.426592610785406</Real>
+      <Real Name="Y">-131.9742172188659</Real>
+      <Real Name="Z">-46.46605273809223</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">475.39922359319962</Real>
+      <Real Name="Y">-580.56235211914964</Real>
+      <Real Name="Z">-1571.0578757899759</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.095620649942759</Real>
+      <Real Name="Y">302.48551689881191</Real>
+      <Real Name="Z">1049.7188464922137</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-730.2632725277216</Real>
+      <Real Name="Y">87.727169767916763</Real>
+      <Real Name="Z">195.97808307993191</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.191374134400689</Real>
+      <Real Name="Y">94.761309003199329</Real>
+      <Real Name="Z">-392.63746375124617</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.47379533930342</Real>
+      <Real Name="Y">78.589936940219815</Real>
+      <Real Name="Z">175.35817806114721</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.900450265180538</Real>
+      <Real Name="Y">-7.4777394716009091</Real>
+      <Real Name="Z">158.06255357046695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">325.58296274158954</Real>
+      <Real Name="Y">-257.35261839544427</Real>
+      <Real Name="Z">-48.956443860098432</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-413.56981901540189</Real>
+      <Real Name="Y">270.70597704750844</Real>
+      <Real Name="Z">-26.383720943880185</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-75.803770380222716</Real>
+      <Real Name="Y">19.191479992717063</Real>
+      <Real Name="Z">31.954266743019978</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1153.4983962821461</Real>
+      <Real Name="Y">-1029.260755849931</Real>
+      <Real Name="Z">-822.80633699760301</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-543.8116749727609</Real>
+      <Real Name="Y">357.57410586580068</Real>
+      <Real Name="Z">690.35101311528229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-497.45996481842678</Real>
+      <Real Name="Y">888.0790678747918</Real>
+      <Real Name="Z">280.72844702393627</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.284060234046276</Real>
+      <Real Name="Y">-12.254565481702087</Real>
+      <Real Name="Z">26.999123828614913</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.0098181858902269</Real>
+      <Real Name="Y">5.915580075548263</Real>
+      <Real Name="Z">-10.020936019104319</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-5.6857636354811376</Real>
+      <Real Name="Y">6.4797276117258349</Real>
+      <Real Name="Z">-15.363806948318182</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-232.03337905112505</Real>
+      <Real Name="Y">1038.0646633815027</Real>
+      <Real Name="Z">286.13078946961974</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">504.25962480895345</Real>
+      <Real Name="Y">-414.64791445059996</Real>
+      <Real Name="Z">-522.03230180629384</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-153.36933265876874</Real>
+      <Real Name="Y">-411.1620562186522</Real>
+      <Real Name="Z">192.71398948492856</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">295.43534527656305</Real>
+      <Real Name="Y">18.153549285255341</Real>
+      <Real Name="Z">-326.94037073013817</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.04960596258064</Real>
+      <Real Name="Y">-22.012578287542194</Real>
+      <Real Name="Z">71.024172164210682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.424798961369575</Real>
+      <Real Name="Y">-12.173268869841435</Real>
+      <Real Name="Z">120.61221342539071</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-317.59847713959948</Real>
+      <Real Name="Y">-1057.5499100765214</Real>
+      <Real Name="Z">-601.10284259265393</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-59.027150819262083</Real>
+      <Real Name="Y">484.54916387361379</Real>
+      <Real Name="Z">-307.59204269740758</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">345.36130249036933</Real>
+      <Real Name="Y">429.77727493880616</Real>
+      <Real Name="Z">683.18008372439897</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-607.60050820173421</Real>
+      <Real Name="Y">274.71922838925684</Real>
+      <Real Name="Z">-337.3184482623974</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">472.61678988346699</Real>
+      <Real Name="Y">-354.26531925520288</Real>
+      <Real Name="Z">346.46868466783008</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.34372758156718</Real>
+      <Real Name="Y">0.27392826404025072</Real>
+      <Real Name="Z">54.547748301661514</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">25.853295076187059</Real>
+      <Real Name="Y">395.09035431376202</Real>
+      <Real Name="Z">48.685440859814804</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-24.446765782933703</Real>
+      <Real Name="Y">-141.24776476784265</Real>
+      <Real Name="Z">-82.74776487713973</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-87.436736311814713</Real>
+      <Real Name="Y">-193.4712448380327</Real>
+      <Real Name="Z">11.981330265463184</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-467.90325604079044</Real>
+      <Real Name="Y">-655.81637546647994</Real>
+      <Real Name="Z">-346.49149405614787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">92.109674936785325</Real>
+      <Real Name="Y">136.00970364352682</Real>
+      <Real Name="Z">123.30537162347903</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">591.30622397416244</Real>
+      <Real Name="Y">407.66055943005972</Real>
+      <Real Name="Z">278.79173691320551</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-47.880469558046492</Real>
+      <Real Name="Y">-60.383278175076413</Real>
+      <Real Name="Z">-925.37858658890752</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.25929940181045</Real>
+      <Real Name="Y">-23.216776712698476</Real>
+      <Real Name="Z">748.62044410464864</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">22.743589903014247</Real>
+      <Real Name="Y">23.980154643692181</Real>
+      <Real Name="Z">231.58332560117276</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-507.9014542328178</Real>
+      <Real Name="Y">-570.54243604991302</Real>
+      <Real Name="Z">-903.96617438961096</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">83.234484013249428</Real>
+      <Real Name="Y">114.01545553451683</Real>
+      <Real Name="Z">234.50531660975221</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">308.86023693970139</Real>
+      <Real Name="Y">447.47380302960164</Real>
+      <Real Name="Z">654.76648916247336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-609.31551612151475</Real>
+      <Real Name="Y">-304.86774169748344</Real>
+      <Real Name="Z">71.24425684170663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">474.25886189628926</Real>
+      <Real Name="Y">-16.424616613430352</Real>
+      <Real Name="Z">-47.243450448061907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">250.78163315897726</Real>
+      <Real Name="Y">65.845124911008469</Real>
+      <Real Name="Z">5.5923986557716425</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">477.21126080559924</Real>
+      <Real Name="Y">1015.7541676835483</Real>
+      <Real Name="Z">93.414898503995573</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-78.101682424739209</Real>
+      <Real Name="Y">-175.87404638536918</Real>
+      <Real Name="Z">-22.901310835912781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-479.58416966902962</Real>
+      <Real Name="Y">-711.89838554064113</Real>
+      <Real Name="Z">-243.7706419672266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1338.4165294453721</Real>
+      <Real Name="Y">14.663378996631629</Real>
+      <Real Name="Z">704.77173090078907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1080.6085651485309</Real>
+      <Real Name="Y">199.3512181402125</Real>
+      <Real Name="Z">-462.74001009010414</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-220.90870159828998</Real>
+      <Real Name="Y">-34.515039895302053</Real>
+      <Real Name="Z">-262.3293945427921</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-336.10792313768718</Real>
+      <Real Name="Y">213.37156584914743</Real>
+      <Real Name="Z">-135.93120858040871</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">131.33417623477249</Real>
+      <Real Name="Y">-126.08764618577973</Real>
+      <Real Name="Z">38.540712030933854</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">174.06853047810617</Real>
+      <Real Name="Y">-24.069711862143976</Real>
+      <Real Name="Z">38.943197018954685</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1058.6756748838627</Real>
+      <Real Name="Y">-319.76586455480384</Real>
+      <Real Name="Z">-59.906262013033952</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-546.64955816343991</Real>
+      <Real Name="Y">65.950213691203999</Real>
+      <Real Name="Z">263.9996989203259</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-310.90481996313048</Real>
+      <Real Name="Y">217.05679662652778</Real>
+      <Real Name="Z">-35.258437410525744</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-241.77266058080428</Real>
+      <Real Name="Y">-1135.5051500435466</Real>
+      <Real Name="Z">-45.44585122873707</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">115.19684244404129</Real>
+      <Real Name="Y">197.73844855707242</Real>
+      <Real Name="Z">-23.788426026604277</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">109.38402814681805</Real>
+      <Real Name="Y">969.51355035433676</Real>
+      <Real Name="Z">62.311038296114667</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">418.1379460404367</Real>
+      <Real Name="Y">967.04272206060205</Real>
+      <Real Name="Z">898.54573115192579</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-198.85969714655812</Real>
+      <Real Name="Y">-773.91949744861563</Real>
+      <Real Name="Z">-659.43259124805559</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-96.60068825214104</Real>
+      <Real Name="Y">-395.25492010154289</Real>
+      <Real Name="Z">-179.9308122652622</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">440.56650841678942</Real>
+      <Real Name="Y">-410.38470618804183</Real>
+      <Real Name="Z">-368.62756435318266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-109.58534387654473</Real>
+      <Real Name="Y">477.88749669060093</Real>
+      <Real Name="Z">67.964624776446868</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-160.38469377742638</Real>
+      <Real Name="Y">14.268724389752975</Real>
+      <Real Name="Z">406.96998836367891</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">473.01264692509062</Real>
+      <Real Name="Y">-533.8288569839026</Real>
+      <Real Name="Z">-73.755200871294704</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-74.908490666891439</Real>
+      <Real Name="Y">776.63418943170348</Real>
+      <Real Name="Z">-17.369714564832222</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-385.57187530308232</Real>
+      <Real Name="Y">-90.688191452946683</Real>
+      <Real Name="Z">244.26445195398392</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-752.78222148769225</Real>
+      <Real Name="Y">874.03644099260896</Real>
+      <Real Name="Z">-386.88993273787867</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.77516452137914</Real>
+      <Real Name="Y">-207.25938102497906</Real>
+      <Real Name="Z">95.546566508058078</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">497.73425841319113</Real>
+      <Real Name="Y">-776.07486714550669</Real>
+      <Real Name="Z">406.26985930901361</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1328.3194966304927</Real>
+      <Real Name="Y">-100.74687288983419</Real>
+      <Real Name="Z">231.29021392716518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-200.73461084803671</Real>
+      <Real Name="Y">79.080191687974093</Real>
+      <Real Name="Z">-59.800900568268048</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1093.2249579161232</Real>
+      <Real Name="Y">-53.662076258033096</Real>
+      <Real Name="Z">-130.93480386493064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-561.81009874671201</Real>
+      <Real Name="Y">-852.74499293675478</Real>
+      <Real Name="Z">295.1949324938019</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">524.58278929084463</Real>
+      <Real Name="Y">772.84151633423903</Real>
+      <Real Name="Z">-23.577407058392268</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">59.901730563937178</Real>
+      <Real Name="Y">174.60927879870155</Real>
+      <Real Name="Z">-94.108722000508749</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-251.73575436570715</Real>
+      <Real Name="Y">-612.40195496392505</Real>
+      <Real Name="Z">881.14783777517573</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">166.20805172688409</Real>
+      <Real Name="Y">453.22533707721209</Real>
+      <Real Name="Z">-656.98997084501457</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.211035679315238</Real>
+      <Real Name="Y">47.723920525614083</Real>
+      <Real Name="Z">-130.2680570528899</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">462.63879612668217</Real>
+      <Real Name="Y">-678.23579549815793</Real>
+      <Real Name="Z">-7.8576266796153362</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-107.5666328114971</Real>
+      <Real Name="Y">274.1985840177058</Real>
+      <Real Name="Z">-44.753419819254134</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-474.83570937382206</Real>
+      <Real Name="Y">352.30841735057282</Real>
+      <Real Name="Z">-7.7582977901127279</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.89224393947265</Real>
+      <Real Name="Y">411.85554157018964</Real>
+      <Real Name="Z">29.126491949741496</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-108.63351071687877</Real>
+      <Real Name="Y">-154.65837783004923</Real>
+      <Real Name="Z">19.656708770131935</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-15.435791013642083</Real>
+      <Real Name="Y">-155.96309833306765</Real>
+      <Real Name="Z">-30.271404232977375</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.448498353634832</Real>
+      <Real Name="Y">829.97539404585666</Real>
+      <Real Name="Z">94.23120301958366</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.9912875783237567</Real>
+      <Real Name="Y">-157.23155479085116</Real>
+      <Real Name="Z">-52.988957441940016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.1369746113234243</Real>
+      <Real Name="Y">-764.93823376319267</Real>
+      <Real Name="Z">-57.728962464083097</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">162.89236348026586</Real>
+      <Real Name="Y">-707.07427449772126</Real>
+      <Real Name="Z">-221.7988736957596</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">26.523301810012583</Real>
+      <Real Name="Y">736.0041523371184</Real>
+      <Real Name="Z">209.24747806697275</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.037373669645262</Real>
+      <Real Name="Y">155.60404771539163</Real>
+      <Real Name="Z">104.25764863714589</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.04969244229085</Real>
+      <Real Name="Y">692.00708618670728</Real>
+      <Real Name="Z">717.11154244825889</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.998221777464877</Real>
+      <Real Name="Y">-165.42352082216121</Real>
+      <Real Name="Z">-99.63109545703621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">189.50949465332977</Real>
+      <Real Name="Y">-796.86490097207979</Real>
+      <Real Name="Z">-670.50471091960389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-199.60190284165952</Real>
+      <Real Name="Y">213.13798802914675</Real>
+      <Real Name="Z">-338.35558498809326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">70.505226326624779</Real>
+      <Real Name="Y">-218.57861167367162</Real>
+      <Real Name="Z">323.88299126622275</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">109.5802983639027</Real>
+      <Real Name="Y">-68.460217478443283</Real>
+      <Real Name="Z">72.338444192718015</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1045.2736239956478</Real>
+      <Real Name="Y">-113.09046184273281</Real>
+      <Real Name="Z">-58.138231366945782</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">806.23883677109825</Real>
+      <Real Name="Y">-115.19495146619019</Real>
+      <Real Name="Z">-76.241650065767402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">317.98733087924495</Real>
+      <Real Name="Y">10.925063047931609</Real>
+      <Real Name="Z">11.935007702736613</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1324.8870293113018</Real>
+      <Real Name="Y">-46.386549580777739</Real>
+      <Real Name="Z">-223.36422813095916</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">224.12790871267282</Real>
+      <Real Name="Y">-141.92601249543753</Real>
+      <Real Name="Z">131.06316292930177</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1126.7990375084644</Real>
+      <Real Name="Y">244.33901882463624</Real>
+      <Real Name="Z">159.71286426769348</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-464.12679293405074</Real>
+      <Real Name="Y">1512.2647247725349</Real>
+      <Real Name="Z">-259.29420013269953</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">36.332127364334227</Real>
+      <Real Name="Y">-730.72735120704624</Real>
+      <Real Name="Z">-336.27671118200675</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">543.844014275787</Real>
+      <Real Name="Y">-626.94066876973056</Real>
+      <Real Name="Z">582.9080308368915</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">319.14386588904239</Real>
+      <Real Name="Y">700.48267298206292</Real>
+      <Real Name="Z">312.20367391367569</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.34323628415823</Real>
+      <Real Name="Y">-117.71731627331583</Real>
+      <Real Name="Z">-124.99239997399621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.81539665059239</Real>
+      <Real Name="Y">-648.85868641307218</Real>
+      <Real Name="Z">-162.22161810389107</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">753.59174680051319</Real>
+      <Real Name="Y">-1452.1502505197157</Real>
+      <Real Name="Z">914.18031609774459</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.522671635316385</Real>
+      <Real Name="Y">955.3297675449013</Real>
+      <Real Name="Z">160.74330086631693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-820.59881692643194</Real>
+      <Real Name="Y">484.32118094109336</Real>
+      <Real Name="Z">-729.91543008396684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">765.93961452739927</Real>
+      <Real Name="Y">-1992.1919602435084</Real>
+      <Real Name="Z">-617.67506599339913</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-175.58930185707419</Real>
+      <Real Name="Y">1093.1976072483401</Real>
+      <Real Name="Z">56.202491063374573</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-521.8738414652355</Real>
+      <Real Name="Y">503.61573277510945</Real>
+      <Real Name="Z">258.93274330122478</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">213.41892444911409</Real>
+      <Real Name="Y">609.26201506226982</Real>
+      <Real Name="Z">943.78153949300281</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-107.29166563416928</Real>
+      <Real Name="Y">-679.51512926709643</Real>
+      <Real Name="Z">-837.19855935846942</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-77.069671706191485</Real>
+      <Real Name="Y">-35.670524561943857</Real>
+      <Real Name="Z">-204.42797279376742</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.060000 Step 60 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-389.48521184246658</Real>
+      <Real Name="Y">150.80867911810898</Real>
+      <Real Name="Z">1069.9549281192863</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">35.215855807575977</Real>
+      <Real Name="Y">229.4571198622163</Real>
+      <Real Name="Z">-598.20696671353426</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">11.383061165166644</Real>
+      <Real Name="Y">-97.495197765021146</Real>
+      <Real Name="Z">89.690785172488958</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">19.317183459296459</Real>
+      <Real Name="Y">-29.216576749912932</Real>
+      <Real Name="Z">20.181990589683959</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">726.09578903061333</Real>
+      <Real Name="Y">52.318665199288077</Real>
+      <Real Name="Z">-765.9376827817581</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-89.469921442818517</Real>
+      <Real Name="Y">-33.617655890167427</Real>
+      <Real Name="Z">-14.915505627204944</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-510.54929724867014</Real>
+      <Real Name="Y">-424.73016216193486</Real>
+      <Real Name="Z">150.37997299666782</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">20.009693997091553</Real>
+      <Real Name="Y">-2.9183046556527348</Real>
+      <Real Name="Z">-102.80521196166973</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.70792962267109</Real>
+      <Real Name="Y">238.90131925393391</Real>
+      <Real Name="Z">54.792219689895617</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">11.940725844342353</Real>
+      <Real Name="Y">53.678704621736301</Real>
+      <Real Name="Z">6.956716717966188</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.86847464743122771</Real>
+      <Real Name="Y">-23.768068456109638</Real>
+      <Real Name="Z">9.6510115957689209</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-12.689526594739533</Real>
+      <Real Name="Y">-15.621466679021481</Real>
+      <Real Name="Z">-15.52783629431487</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6598848334416942</Real>
+      <Real Name="Y">-33.931215355221724</Real>
+      <Real Name="Z">-52.126793480503778</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.1454966442467747</Real>
+      <Real Name="Y">13.829256308667627</Real>
+      <Real Name="Z">19.995125669036511</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.177016947710527</Real>
+      <Real Name="Y">11.946682136519451</Real>
+      <Real Name="Z">22.938506003425857</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">216.32821582804698</Real>
+      <Real Name="Y">-166.30841632667028</Real>
+      <Real Name="Z">-620.72667879711832</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-143.87096807951806</Real>
+      <Real Name="Y">121.95126578331384</Real>
+      <Real Name="Z">664.24334659144927</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.3101915916343216</Real>
+      <Real Name="Y">14.995196272137434</Real>
+      <Real Name="Z">118.90123329427496</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">757.80934858003798</Real>
+      <Real Name="Y">1814.7988048507714</Real>
+      <Real Name="Z">-108.78797382889712</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.127521075048179</Real>
+      <Real Name="Y">-234.99657137802959</Real>
+      <Real Name="Z">-4.8377548765911342</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-640.55016029051683</Real>
+      <Real Name="Y">-1134.3794550671289</Real>
+      <Real Name="Z">-11.262027027911031</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">553.462071323847</Real>
+      <Real Name="Y">-628.62179614142644</Real>
+      <Real Name="Z">-696.42174740610824</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.819241707879115</Real>
+      <Real Name="Y">184.83707701807856</Real>
+      <Real Name="Z">120.22416215490944</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-568.80606669622273</Real>
+      <Real Name="Y">388.85410708968124</Real>
+      <Real Name="Z">515.30072603343694</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">97.480744191250565</Real>
+      <Real Name="Y">510.41222773081495</Real>
+      <Real Name="Z">38.732799777629907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.3836211310371151</Real>
+      <Real Name="Y">-179.85276889436153</Real>
+      <Real Name="Z">-77.321996729389753</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.372306707893941</Real>
+      <Real Name="Y">-217.34568107473635</Real>
+      <Real Name="Z">-8.8192642273732247</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-635.51440794675557</Real>
+      <Real Name="Y">261.32866602021193</Real>
+      <Real Name="Z">1443.3049064320917</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">554.52583614931712</Real>
+      <Real Name="Y">-110.42217487306996</Real>
+      <Real Name="Z">-1068.1386908023919</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">213.91460965755633</Real>
+      <Real Name="Y">-207.99700590441699</Real>
+      <Real Name="Z">-67.95093650757947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1147.1683792302917</Real>
+      <Real Name="Y">332.72848344858323</Real>
+      <Real Name="Z">350.00082620885394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.06128617134624</Real>
+      <Real Name="Y">-683.06959227601067</Real>
+      <Real Name="Z">-196.45306588974063</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-902.50311949083789</Real>
+      <Real Name="Y">348.24124997231274</Real>
+      <Real Name="Z">-452.21886878826513</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.59937577716744</Real>
+      <Real Name="Y">818.43787996201684</Real>
+      <Real Name="Z">-56.176665366784356</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.218765610380586</Real>
+      <Real Name="Y">-589.83769159781218</Real>
+      <Real Name="Z">62.965482817022462</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-131.78093155032025</Real>
+      <Real Name="Y">-405.41973744902737</Real>
+      <Real Name="Z">51.592981927728523</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-446.94217896825938</Real>
+      <Real Name="Y">-277.6798714455112</Real>
+      <Real Name="Z">-1210.905676240965</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-47.408038099622281</Real>
+      <Real Name="Y">166.214355384534</Real>
+      <Real Name="Z">403.90208172204592</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">552.32598689453323</Real>
+      <Real Name="Y">64.206318498659755</Real>
+      <Real Name="Z">809.69599635724546</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">707.12142333276495</Real>
+      <Real Name="Y">-332.52028272102689</Real>
+      <Real Name="Z">-847.38090116671799</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-252.70788993445316</Real>
+      <Real Name="Y">154.36590391000101</Real>
+      <Real Name="Z">271.04968165010632</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-92.85196885729863</Real>
+      <Real Name="Y">-279.24324250224976</Real>
+      <Real Name="Z">36.932768423870925</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-885.24778290993891</Real>
+      <Real Name="Y">309.28125589221037</Real>
+      <Real Name="Z">143.60552578910713</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">790.04658589960354</Real>
+      <Real Name="Y">-171.37881843648063</Real>
+      <Real Name="Z">38.269592410789322</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">146.44350457903823</Real>
+      <Real Name="Y">-13.179663584564484</Real>
+      <Real Name="Z">-165.6447990218004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.19602789801479</Real>
+      <Real Name="Y">2.1715338570852225</Real>
+      <Real Name="Z">457.01996941709592</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-53.404185881426194</Real>
+      <Real Name="Y">103.07727876508211</Real>
+      <Real Name="Z">-173.10364628861316</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">24.069933736888828</Real>
+      <Real Name="Y">-40.045565562989708</Real>
+      <Real Name="Z">-111.21951821617529</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">146.71819942075876</Real>
+      <Real Name="Y">-1545.0397280172444</Real>
+      <Real Name="Z">-330.47487374942068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.62261837031187</Real>
+      <Real Name="Y">1133.4866734546499</Real>
+      <Real Name="Z">289.27616525563894</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.12945914488742</Real>
+      <Real Name="Y">372.62263227294517</Real>
+      <Real Name="Z">30.272216511970214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">924.60721995132394</Real>
+      <Real Name="Y">950.20111588201939</Real>
+      <Real Name="Z">-323.71720152140585</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-150.20136428291863</Real>
+      <Real Name="Y">-397.13254290643778</Real>
+      <Real Name="Z">162.4855141837582</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-466.04116733327618</Real>
+      <Real Name="Y">-660.48797512221392</Real>
+      <Real Name="Z">562.03694971796756</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">66.08326711351188</Real>
+      <Real Name="Y">124.67482599200372</Real>
+      <Real Name="Z">97.118723516388087</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.229044128012964</Real>
+      <Real Name="Y">-55.746586678181771</Real>
+      <Real Name="Z">-36.067982473585815</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.107282451196689</Real>
+      <Real Name="Y">-44.757281983944949</Real>
+      <Real Name="Z">-54.630833592895613</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-500.1800360470765</Real>
+      <Real Name="Y">1045.5941532978811</Real>
+      <Real Name="Z">520.92121851225181</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">298.27814779703749</Real>
+      <Real Name="Y">-755.52900959986277</Real>
+      <Real Name="Z">-501.81523203952753</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">115.04596002007911</Real>
+      <Real Name="Y">-164.35026142670452</Real>
+      <Real Name="Z">-85.657526732088499</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.72637907793143</Real>
+      <Real Name="Y">-65.538426012660977</Real>
+      <Real Name="Z">-389.90827964705375</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-96.3645277662512</Real>
+      <Real Name="Y">384.55433419786686</Real>
+      <Real Name="Z">860.37467769784428</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-416.43058161962108</Real>
+      <Real Name="Y">67.652402162826533</Real>
+      <Real Name="Z">31.666445624489807</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.734251313742391</Real>
+      <Real Name="Y">-46.284432766682585</Real>
+      <Real Name="Z">-472.70854008830918</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-33.72812394605625</Real>
+      <Real Name="Y">71.16228005245307</Real>
+      <Real Name="Z">182.92901894884693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">23.007510186271858</Real>
+      <Real Name="Y">12.456719766018999</Real>
+      <Real Name="Z">170.61364709605638</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">425.80657018760036</Real>
+      <Real Name="Y">-411.3614847027319</Real>
+      <Real Name="Z">-123.12349572668572</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-448.39422275338131</Real>
+      <Real Name="Y">449.56045621219607</Real>
+      <Real Name="Z">183.01218913835163</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.34815444295289</Real>
+      <Real Name="Y">35.257420791054642</Real>
+      <Real Name="Z">56.259024244806639</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">388.48851837001854</Real>
+      <Real Name="Y">-1046.0925044652204</Real>
+      <Real Name="Z">-502.82732028965148</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-437.07595840101902</Real>
+      <Real Name="Y">419.12616131886722</Real>
+      <Real Name="Z">580.94445028804091</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-189.90388016793028</Real>
+      <Real Name="Y">562.79835107824238</Real>
+      <Real Name="Z">143.18840542632165</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">9.7106363085723544</Real>
+      <Real Name="Y">3.1316802297745312</Real>
+      <Real Name="Z">19.885968724063218</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-5.5483066195968505</Real>
+      <Real Name="Y">-1.5522416713415339</Real>
+      <Real Name="Z">-6.5343202899433876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.0801868363178517</Real>
+      <Real Name="Y">-3.2079068899512322</Real>
+      <Real Name="Z">-11.76264884850773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-168.62794633434353</Real>
+      <Real Name="Y">1063.2380868339801</Real>
+      <Real Name="Z">-2.3127056045556031</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">414.93878790908497</Real>
+      <Real Name="Y">-456.40240969785913</Real>
+      <Real Name="Z">-418.97487724572972</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-253.62097856756307</Real>
+      <Real Name="Y">-392.39283900702219</Real>
+      <Real Name="Z">332.48078948295358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">250.21231707571556</Real>
+      <Real Name="Y">62.996157520253334</Real>
+      <Real Name="Z">-362.23863713815496</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-143.67706065790338</Real>
+      <Real Name="Y">-63.114964297476973</Real>
+      <Real Name="Z">74.999243797575218</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-14.648204848062992</Real>
+      <Real Name="Y">-17.42087739032776</Real>
+      <Real Name="Z">156.94925494279784</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-355.35816795408698</Real>
+      <Real Name="Y">-1387.3804427718858</Real>
+      <Real Name="Z">-362.50830142526104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.650522728498643</Real>
+      <Real Name="Y">532.56869448547559</Real>
+      <Real Name="Z">-294.78701682933536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">212.36734756414077</Real>
+      <Real Name="Y">635.17461968928524</Real>
+      <Real Name="Z">516.75176497758184</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-488.63359238697552</Real>
+      <Real Name="Y">250.31793816306899</Real>
+      <Real Name="Z">-275.04619266433349</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">447.39835515584332</Real>
+      <Real Name="Y">-333.26080400562523</Real>
+      <Real Name="Z">297.39614621698325</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">149.53904588939838</Real>
+      <Real Name="Y">-4.6312447635460501</Real>
+      <Real Name="Z">78.372466636551636</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">201.65702772709827</Real>
+      <Real Name="Y">70.901172294867337</Real>
+      <Real Name="Z">147.38263141857104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-88.416128738203582</Real>
+      <Real Name="Y">-97.723097079642827</Real>
+      <Real Name="Z">-108.18587655821516</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-144.39702222021677</Real>
+      <Real Name="Y">-243.26994312701143</Real>
+      <Real Name="Z">-0.24238140922607165</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-637.29123030668495</Real>
+      <Real Name="Y">-480.33407167525621</Real>
+      <Real Name="Z">-276.16864930703662</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">165.88240953532767</Real>
+      <Real Name="Y">179.29314779972822</Real>
+      <Real Name="Z">152.73579045842919</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">486.63929471276595</Real>
+      <Real Name="Y">339.9903841421791</Real>
+      <Real Name="Z">254.29581670336108</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">281.11921512643499</Real>
+      <Real Name="Y">-124.09185895981983</Real>
+      <Real Name="Z">-1101.3938028323046</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-318.27999099485123</Real>
+      <Real Name="Y">43.148018625670602</Real>
+      <Real Name="Z">752.2137927362669</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-72.661398092550741</Real>
+      <Real Name="Y">44.953173155117881</Real>
+      <Real Name="Z">260.36389687511161</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-402.41808615309526</Real>
+      <Real Name="Y">-527.59656585080779</Real>
+      <Real Name="Z">-784.18409938955915</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">97.403375552252896</Real>
+      <Real Name="Y">94.09398703197337</Real>
+      <Real Name="Z">161.91687652746936</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">155.96696994812521</Real>
+      <Real Name="Y">391.94290957963705</Real>
+      <Real Name="Z">735.69049585965604</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-612.59917955735273</Real>
+      <Real Name="Y">-173.56594310523261</Real>
+      <Real Name="Z">80.601793218782547</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">374.14312303039554</Real>
+      <Real Name="Y">16.208262836987657</Real>
+      <Real Name="Z">-6.4694356291145354</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">203.34972831609531</Real>
+      <Real Name="Y">103.17932129909292</Real>
+      <Real Name="Z">32.642892900706101</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1303.1381763738957</Real>
+      <Real Name="Y">1699.0096484679843</Real>
+      <Real Name="Z">42.743834128461714</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-221.93762675661992</Real>
+      <Real Name="Y">-225.51810811530538</Real>
+      <Real Name="Z">33.042716270844011</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-701.16425082360956</Real>
+      <Real Name="Y">-1272.5730967088764</Real>
+      <Real Name="Z">-137.61830076322164</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">917.65040231233434</Real>
+      <Real Name="Y">-326.28571134219828</Real>
+      <Real Name="Z">838.1302237131265</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-888.95053000462406</Real>
+      <Real Name="Y">69.181000116478145</Real>
+      <Real Name="Z">-483.02938868809088</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-276.17847420991296</Real>
+      <Real Name="Y">-92.592752765351065</Real>
+      <Real Name="Z">-317.11204996250297</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-391.51279909076186</Real>
+      <Real Name="Y">197.59658964578963</Real>
+      <Real Name="Z">-192.23713927827805</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">127.55958478297683</Real>
+      <Real Name="Y">-115.71265426359427</Real>
+      <Real Name="Z">61.977340751880959</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">165.38545336404536</Real>
+      <Real Name="Y">-8.6623444308787576</Real>
+      <Real Name="Z">44.106719253538003</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">874.97548014149038</Real>
+      <Real Name="Y">-299.85907039455947</Real>
+      <Real Name="Z">-148.13224193637905</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-318.69523576437166</Real>
+      <Real Name="Y">77.942915769342022</Real>
+      <Real Name="Z">72.229082953364966</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-336.78450072282209</Real>
+      <Real Name="Y">382.25319246431957</Real>
+      <Real Name="Z">-94.458069983738881</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-430.03996922440984</Real>
+      <Real Name="Y">-1036.9046132812364</Real>
+      <Real Name="Z">105.21411299558571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">160.88059288894723</Real>
+      <Real Name="Y">171.96951520954482</Real>
+      <Real Name="Z">-135.67267305621789</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">175.05609506684414</Real>
+      <Real Name="Y">921.22671253185672</Real>
+      <Real Name="Z">45.477575556080474</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.508542660919137</Real>
+      <Real Name="Y">863.68004351882837</Real>
+      <Real Name="Z">781.80532189948065</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-149.40849112652521</Real>
+      <Real Name="Y">-756.33516347155125</Real>
+      <Real Name="Z">-712.14588034222754</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.038400633146367</Real>
+      <Real Name="Y">-328.23817895357814</Real>
+      <Real Name="Z">-182.24281849041404</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">157.17272472300459</Real>
+      <Real Name="Y">-239.35072092432708</Real>
+      <Real Name="Z">-427.92544471872185</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-251.77198610614795</Real>
+      <Real Name="Y">502.54559339517732</Real>
+      <Real Name="Z">166.85433673756108</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.199290094483111</Real>
+      <Real Name="Y">18.625876283590401</Real>
+      <Real Name="Z">409.27681976530704</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">486.51587283048212</Real>
+      <Real Name="Y">-1172.3190881487174</Real>
+      <Real Name="Z">-215.90811209861647</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.187101638985041</Real>
+      <Real Name="Y">896.47746433412181</Real>
+      <Real Name="Z">-21.661384517858348</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-571.87423625314898</Real>
+      <Real Name="Y">43.056967937428084</Real>
+      <Real Name="Z">192.64870741516336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-553.78076945819021</Real>
+      <Real Name="Y">849.14464636213609</Real>
+      <Real Name="Z">-369.33246159334817</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.61408336253837</Real>
+      <Real Name="Y">-231.95697570227708</Real>
+      <Real Name="Z">84.075512717477693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">363.87603787124476</Real>
+      <Real Name="Y">-645.16810054095515</Real>
+      <Real Name="Z">448.82591738447633</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1888.3965930284335</Real>
+      <Real Name="Y">76.389759561518318</Real>
+      <Real Name="Z">273.3111123136585</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-207.13841510372868</Real>
+      <Real Name="Y">81.647868907211176</Real>
+      <Real Name="Z">-58.925060148085272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1095.7830321348315</Real>
+      <Real Name="Y">-103.23185554890068</Real>
+      <Real Name="Z">-290.87799593409568</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-609.57618291409665</Real>
+      <Real Name="Y">-1044.5233884201209</Real>
+      <Real Name="Z">407.71042302143462</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">658.6783053226452</Real>
+      <Real Name="Y">644.20085354306468</Real>
+      <Real Name="Z">-174.38245743844794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">126.07314421176241</Real>
+      <Real Name="Y">213.50612440222159</Real>
+      <Real Name="Z">-114.47318303503468</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-217.04597459665914</Real>
+      <Real Name="Y">-399.56480480516672</Real>
+      <Real Name="Z">492.76108962896342</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">50.496996135409631</Real>
+      <Real Name="Y">411.18926367043304</Real>
+      <Real Name="Z">-478.03458745096799</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.1820575807360285</Real>
+      <Real Name="Y">49.605465334808549</Real>
+      <Real Name="Z">-89.481673361102594</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">551.72981359494736</Real>
+      <Real Name="Y">-494.53315491945153</Real>
+      <Real Name="Z">-263.77285676916756</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-106.92931507559089</Real>
+      <Real Name="Y">221.99785038147621</Real>
+      <Real Name="Z">4.1534996815670411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-586.8921786280165</Real>
+      <Real Name="Y">162.19353170363175</Real>
+      <Real Name="Z">296.75430685755555</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">48.88394849587749</Real>
+      <Real Name="Y">332.69126740489304</Real>
+      <Real Name="Z">143.67074800907244</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.74392933003423</Real>
+      <Real Name="Y">-195.92670165555725</Real>
+      <Real Name="Z">-14.434401249180954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">22.178525848661607</Real>
+      <Real Name="Y">-139.29234098713022</Real>
+      <Real Name="Z">-61.380369294073432</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-85.590716871116427</Real>
+      <Real Name="Y">1013.6221622617455</Real>
+      <Real Name="Z">141.75683662830585</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.485068534515669</Real>
+      <Real Name="Y">-144.622790502237</Real>
+      <Real Name="Z">-57.132558761851143</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.43105491833231</Real>
+      <Real Name="Y">-855.71489663193051</Real>
+      <Real Name="Z">-152.44907254501692</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.04533900370595</Real>
+      <Real Name="Y">-828.37046589235968</Real>
+      <Real Name="Z">-489.94165651435964</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">145.23325317438528</Real>
+      <Real Name="Y">749.73857071825375</Real>
+      <Real Name="Z">224.2175258878496</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.75678968842638</Real>
+      <Real Name="Y">96.209403207902852</Real>
+      <Real Name="Z">120.37610985887764</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.120487026539635</Real>
+      <Real Name="Y">937.51111656872138</Real>
+      <Real Name="Z">575.62224791973017</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-60.235812611157314</Real>
+      <Real Name="Y">-162.04378384107585</Real>
+      <Real Name="Z">-90.760954994656601</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">81.297813221145319</Real>
+      <Real Name="Y">-784.5706972950494</Real>
+      <Real Name="Z">-431.6040073130979</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.09258602407516</Real>
+      <Real Name="Y">271.44311637863581</Real>
+      <Real Name="Z">-542.10892110976806</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">42.80507821910173</Real>
+      <Real Name="Y">-348.74960873073849</Real>
+      <Real Name="Z">544.27754783309001</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">84.977310245326663</Real>
+      <Real Name="Y">-87.614704687568803</Real>
+      <Real Name="Z">124.37097082954084</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1358.0905352372893</Real>
+      <Real Name="Y">197.23097681006544</Real>
+      <Real Name="Z">55.329811430777539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1030.4503890321892</Real>
+      <Real Name="Y">-281.40203880595959</Real>
+      <Real Name="Z">-6.7409975731880252</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">265.1041288859924</Real>
+      <Real Name="Y">-36.213484678103129</Real>
+      <Real Name="Z">-2.1561403568936157</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-987.19589307079173</Real>
+      <Real Name="Y">125.93019704906847</Real>
+      <Real Name="Z">-188.70800878188021</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">147.76134645404773</Real>
+      <Real Name="Y">-183.95555651109089</Real>
+      <Real Name="Z">184.71140456669391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1060.6551083152553</Real>
+      <Real Name="Y">172.40173463563613</Real>
+      <Real Name="Z">61.882782854305312</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-725.7672897091968</Real>
+      <Real Name="Y">1452.9192418005703</Real>
+      <Real Name="Z">-1053.705914625489</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.0805520727013</Real>
+      <Real Name="Y">-529.53596320548979</Real>
+      <Real Name="Z">-234.64565863843407</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">211.59842247534363</Real>
+      <Real Name="Y">-810.36253768904498</Real>
+      <Real Name="Z">757.87332637910833</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">461.76653457164184</Real>
+      <Real Name="Y">1116.9789783588603</Real>
+      <Real Name="Z">578.02238632893432</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.826700816850341</Real>
+      <Real Name="Y">-105.2805188799652</Real>
+      <Real Name="Z">-131.08358799468323</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-383.73510029313547</Real>
+      <Real Name="Y">-756.5457180469383</Real>
+      <Real Name="Z">-476.11740222717492</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">474.53794104446018</Real>
+      <Real Name="Y">-1256.8511608845076</Real>
+      <Real Name="Z">576.64471711566318</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">207.7280599828311</Real>
+      <Real Name="Y">918.66020890114498</Real>
+      <Real Name="Z">-120.44472562734418</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-671.98899325102707</Real>
+      <Real Name="Y">449.13350345815422</Real>
+      <Real Name="Z">-815.9688109372986</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">840.09999280195598</Real>
+      <Real Name="Y">-1322.033605206492</Real>
+      <Real Name="Z">-199.64330690279849</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-244.56596485844008</Real>
+      <Real Name="Y">986.09522968474198</Real>
+      <Real Name="Z">68.730932289299176</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-380.62249192053036</Real>
+      <Real Name="Y">421.23122825840414</Real>
+      <Real Name="Z">210.58736921348384</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">366.97189913872432</Real>
+      <Real Name="Y">521.48198778764186</Real>
+      <Real Name="Z">941.1433577111568</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-208.12694019350394</Real>
+      <Real Name="Y">-672.27132140353524</Real>
+      <Real Name="Z">-563.57045202533675</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-94.152199013393783</Real>
+      <Real Name="Y">1.4640290445417961</Real>
+      <Real Name="Z">-203.15221635963428</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.080000 Step 80 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">417.48313581710227</Real>
+      <Real Name="Y">-1660.5583323946253</Real>
+      <Real Name="Z">745.1288554135698</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">20.125857663098419</Real>
+      <Real Name="Y">3.1858859084688347</Real>
+      <Real Name="Z">-23.828802696090502</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-676.77789720623366</Real>
+      <Real Name="Y">130.79487614674301</Real>
+      <Real Name="Z">94.728665655805713</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.897305257888235</Real>
+      <Real Name="Y">897.37759920111489</Real>
+      <Real Name="Z">104.01795851324815</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1092.9509197024086</Real>
+      <Real Name="Y">489.05127990095872</Real>
+      <Real Name="Z">-1733.9507997566086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-859.47247873686104</Real>
+      <Real Name="Y">-446.91276435745016</Real>
+      <Real Name="Z">228.12616034970992</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1217.8302829061527</Real>
+      <Real Name="Y">1449.3693504398129</Real>
+      <Real Name="Z">528.85659862114073</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1009.7447680721041</Real>
+      <Real Name="Y">-379.946527723239</Real>
+      <Real Name="Z">84.257862629016046</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">454.17186019312311</Real>
+      <Real Name="Y">-466.24597165799292</Real>
+      <Real Name="Z">-12.80499375153417</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">18.699216953351318</Real>
+      <Real Name="Y">86.117309623008879</Real>
+      <Real Name="Z">-5.5601621194502684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.396351940605037</Real>
+      <Real Name="Y">-41.471792180051352</Real>
+      <Real Name="Z">13.057904103461119</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.039670957727125</Real>
+      <Real Name="Y">-35.051184089052704</Real>
+      <Real Name="Z">-14.204692411084473</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.7531601265988925</Real>
+      <Real Name="Y">-42.174406074258073</Real>
+      <Real Name="Z">-60.03794000349157</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.032788710332230409</Real>
+      <Real Name="Y">18.736094499523798</Real>
+      <Real Name="Z">27.535824865739869</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.5571909236487258</Real>
+      <Real Name="Y">13.885300125209159</Real>
+      <Real Name="Z">25.93811034248931</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">264.15885817563401</Real>
+      <Real Name="Y">-204.11474699794846</Real>
+      <Real Name="Z">-448.99209199553417</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-286.92699378722364</Real>
+      <Real Name="Y">291.48123448703393</Real>
+      <Real Name="Z">399.26036506335373</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-42.310636923052627</Real>
+      <Real Name="Y">20.052604565870038</Real>
+      <Real Name="Z">141.72764036618545</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">458.43553524080528</Real>
+      <Real Name="Y">1870.2694913120299</Real>
+      <Real Name="Z">18.59824039528187</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.073446955553763</Real>
+      <Real Name="Y">-282.70625199375331</Real>
+      <Real Name="Z">17.952957421343548</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-228.579932480667</Real>
+      <Real Name="Y">-1185.6491819114631</Real>
+      <Real Name="Z">2.170561152438907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">695.92925412569707</Real>
+      <Real Name="Y">-828.70347308473913</Real>
+      <Real Name="Z">-1073.3377398440537</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-74.601474593544381</Real>
+      <Real Name="Y">172.74115888602307</Real>
+      <Real Name="Z">129.12307602646146</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-368.72269830692534</Real>
+      <Real Name="Y">473.89448422073468</Real>
+      <Real Name="Z">820.48827148910527</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">38.643236053293968</Real>
+      <Real Name="Y">509.30443383540182</Real>
+      <Real Name="Z">295.91351087619</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.4692848691631077</Real>
+      <Real Name="Y">-141.58091869550196</Real>
+      <Real Name="Z">-108.97409821426174</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.824254509499838</Real>
+      <Real Name="Y">-226.94268660746098</Real>
+      <Real Name="Z">-57.189861252544326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-838.87892231992578</Real>
+      <Real Name="Y">402.62837296308936</Real>
+      <Real Name="Z">1505.8198295117813</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">589.65731013198899</Real>
+      <Real Name="Y">-104.28382526362206</Real>
+      <Real Name="Z">-1159.6189120058636</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">137.14513269378944</Real>
+      <Real Name="Y">-139.61265746895691</Real>
+      <Real Name="Z">-153.18197635278827</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1241.4943597622923</Real>
+      <Real Name="Y">202.74190604981388</Real>
+      <Real Name="Z">88.584487834631531</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-248.02397182473484</Real>
+      <Real Name="Y">-500.64258586277879</Real>
+      <Real Name="Z">-14.205002329278631</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1161.9256915600979</Real>
+      <Real Name="Y">277.2457566216558</Real>
+      <Real Name="Z">-392.57951689989346</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">78.306458917731078</Real>
+      <Real Name="Y">551.71092912183826</Real>
+      <Real Name="Z">-182.64447460452976</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-189.9888911192206</Real>
+      <Real Name="Y">-492.38648892642391</Real>
+      <Real Name="Z">78.885549229949703</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-179.25985792694883</Real>
+      <Real Name="Y">-185.83010404318685</Real>
+      <Real Name="Z">100.33032570536751</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-359.67387384421727</Real>
+      <Real Name="Y">-182.69020217599819</Real>
+      <Real Name="Z">-1025.9294802350314</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-126.92919397985645</Real>
+      <Real Name="Y">163.32233551990771</Real>
+      <Real Name="Z">450.83390986526723</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">781.85327177238059</Real>
+      <Real Name="Y">-49.687375006206267</Real>
+      <Real Name="Z">638.88593209796761</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.203280768021022</Real>
+      <Real Name="Y">52.901838092009413</Real>
+      <Real Name="Z">-697.69900134900831</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-232.05821266536435</Real>
+      <Real Name="Y">223.19265708807245</Real>
+      <Real Name="Z">732.80376019718653</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.4280176718089521</Real>
+      <Real Name="Y">-180.68738157511387</Real>
+      <Real Name="Z">77.48224315251295</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-796.00462902465881</Real>
+      <Real Name="Y">179.41199121882985</Real>
+      <Real Name="Z">-90.302719322760765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">665.8801619221324</Real>
+      <Real Name="Y">-371.53329876652242</Real>
+      <Real Name="Z">125.95336454367821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">334.50433546637589</Real>
+      <Real Name="Y">172.11081935537504</Real>
+      <Real Name="Z">-254.68240814744115</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-80.302005503463377</Real>
+      <Real Name="Y">-214.67905351187289</Real>
+      <Real Name="Z">368.85487745345324</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-84.030281362201336</Real>
+      <Real Name="Y">247.7006180227481</Real>
+      <Real Name="Z">-267.49948993835136</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.015378149256684</Real>
+      <Real Name="Y">55.286442944873855</Real>
+      <Real Name="Z">-78.450220895270832</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-316.9363962625622</Real>
+      <Real Name="Y">-1111.8370775880337</Real>
+      <Real Name="Z">-184.8205161283642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.483218193230631</Real>
+      <Real Name="Y">1030.7411570160241</Real>
+      <Real Name="Z">206.78623833740579</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">136.99397731571298</Real>
+      <Real Name="Y">236.20565102071856</Real>
+      <Real Name="Z">9.892061882549406</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.51315632164122</Real>
+      <Real Name="Y">800.28305583743258</Real>
+      <Real Name="Z">-220.83179571479448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-13.409831298728797</Real>
+      <Real Name="Y">-305.09629317166639</Real>
+      <Real Name="Z">6.2744221228296908</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-264.835754981126</Real>
+      <Real Name="Y">-475.55751190418749</Real>
+      <Real Name="Z">423.52025382788185</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">63.669977715217549</Real>
+      <Real Name="Y">77.255806618596864</Real>
+      <Real Name="Z">109.66238867774069</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-13.108301192713029</Real>
+      <Real Name="Y">-40.509172289404546</Real>
+      <Real Name="Z">-40.266115569838298</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.012897537731924</Real>
+      <Real Name="Y">-35.789953333083105</Real>
+      <Real Name="Z">-62.705808662766202</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-591.49359906302186</Real>
+      <Real Name="Y">1035.8396745418888</Real>
+      <Real Name="Z">592.53778840121527</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">350.88598848193209</Real>
+      <Real Name="Y">-820.46749073356523</Real>
+      <Real Name="Z">-301.47277535459074</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">59.679941645013962</Real>
+      <Real Name="Y">-190.23756297913002</Real>
+      <Real Name="Z">-38.18495903863198</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1293.3416386249837</Real>
+      <Real Name="Y">-682.5109098535346</Real>
+      <Real Name="Z">-610.92397553282285</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">91.010368583949528</Real>
+      <Real Name="Y">269.8783153985421</Real>
+      <Real Name="Z">750.70965102371497</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-738.47561658264385</Real>
+      <Real Name="Y">244.75311421396219</Real>
+      <Real Name="Z">-80.03878605028757</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.137610288870771</Real>
+      <Real Name="Y">-227.11124001665746</Real>
+      <Real Name="Z">-438.30487633524882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.93305978182644578</Real>
+      <Real Name="Y">69.373768697705245</Real>
+      <Real Name="Z">175.15901069807865</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">76.865988968402618</Real>
+      <Real Name="Y">52.643888268262728</Real>
+      <Real Name="Z">218.96790506747033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">647.5820876416069</Real>
+      <Real Name="Y">-542.6678191271069</Real>
+      <Real Name="Z">-246.11583139543339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-513.3364737094106</Real>
+      <Real Name="Y">548.73152319539008</Real>
+      <Real Name="Z">270.22993780701506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-160.59594363575371</Real>
+      <Real Name="Y">19.230395423233631</Real>
+      <Real Name="Z">73.991171199862947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">545.45291833100327</Real>
+      <Real Name="Y">-1100.7642380163113</Real>
+      <Real Name="Z">-1010.1761687784979</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-716.01713021239607</Real>
+      <Real Name="Y">299.75146837411546</Real>
+      <Real Name="Z">775.26673873170216</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-349.29757719233987</Real>
+      <Real Name="Y">664.11882542434614</Real>
+      <Real Name="Z">157.35067633506821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">11.189486716014116</Real>
+      <Real Name="Y">10.700714854694347</Real>
+      <Real Name="Z">25.948213275718487</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.5030939349850883</Real>
+      <Real Name="Y">-5.890133911178232</Real>
+      <Real Name="Z">-10.075794226481491</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-8.6720071969075647</Real>
+      <Real Name="Y">-6.4884953709419868</Real>
+      <Real Name="Z">-17.551032302900865</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-427.34732974259293</Real>
+      <Real Name="Y">945.62422956676289</Real>
+      <Real Name="Z">-243.11294174324556</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">527.77844013169192</Real>
+      <Real Name="Y">-343.89957542883809</Real>
+      <Real Name="Z">-121.68383574359319</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-242.3323330695456</Real>
+      <Real Name="Y">-489.84962102612627</Real>
+      <Real Name="Z">575.57893197393673</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">20.974366514392152</Real>
+      <Real Name="Y">127.50609506487979</Real>
+      <Real Name="Z">-371.31540613302684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-65.704450374835702</Real>
+      <Real Name="Y">-90.111944058379891</Real>
+      <Real Name="Z">91.964425100193964</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">68.290450632337624</Real>
+      <Real Name="Y">-20.81648876649869</Real>
+      <Real Name="Z">151.07577339124282</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-446.81154632831942</Real>
+      <Real Name="Y">-985.97749426816904</Real>
+      <Real Name="Z">-330.87578866282968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-36.685831489296397</Real>
+      <Real Name="Y">454.75164755222039</Real>
+      <Real Name="Z">-214.35226174657154</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">187.19317485067984</Real>
+      <Real Name="Y">646.21620659531368</Real>
+      <Real Name="Z">481.23543625059261</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-478.47142589645489</Real>
+      <Real Name="Y">258.67600313589764</Real>
+      <Real Name="Z">-232.62138820277872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">536.91673733136668</Real>
+      <Real Name="Y">-264.34920417941703</Real>
+      <Real Name="Z">215.88604810441117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">122.42904354969878</Real>
+      <Real Name="Y">-19.040065857125711</Real>
+      <Real Name="Z">78.269783476907634</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.474171924871627</Real>
+      <Real Name="Y">35.612989645909863</Real>
+      <Real Name="Z">-110.07688152541418</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-110.96471615485953</Real>
+      <Real Name="Y">-130.22067791532029</Real>
+      <Real Name="Z">-142.08653944891736</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-55.074732302726005</Real>
+      <Real Name="Y">-207.23162513401388</Real>
+      <Real Name="Z">22.736303658806293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-416.12979118799961</Real>
+      <Real Name="Y">-532.23016808687191</Real>
+      <Real Name="Z">-318.14815003404601</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">143.2150814863362</Real>
+      <Real Name="Y">196.17331852851072</Real>
+      <Real Name="Z">181.41353286726695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">412.72649434347926</Real>
+      <Real Name="Y">412.36456996117465</Real>
+      <Real Name="Z">244.77867276205637</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">639.85266994109247</Real>
+      <Real Name="Y">-101.01127123917598</Real>
+      <Real Name="Z">-818.68049112602682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.95982756618065</Real>
+      <Real Name="Y">20.601175509182028</Real>
+      <Real Name="Z">681.49365196990914</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-168.45611037365828</Real>
+      <Real Name="Y">51.152760889132082</Real>
+      <Real Name="Z">166.57172747561964</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-351.83318164110523</Real>
+      <Real Name="Y">-417.46240914232328</Real>
+      <Real Name="Z">-645.38544651672953</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">100.94163194264792</Real>
+      <Real Name="Y">70.621933752140066</Real>
+      <Real Name="Z">100.15300556226805</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">31.323084771760463</Real>
+      <Real Name="Y">241.63725190461298</Real>
+      <Real Name="Z">567.98954213766353</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-703.03260782612699</Real>
+      <Real Name="Y">-185.602993543022</Real>
+      <Real Name="Z">-0.59853561341891748</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">621.69474220540815</Real>
+      <Real Name="Y">117.82533899266475</Real>
+      <Real Name="Z">-92.769963217147776</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.64754848905289</Real>
+      <Real Name="Y">108.73553447561764</Real>
+      <Real Name="Z">41.331261098124159</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1197.4804812717562</Real>
+      <Real Name="Y">1761.1787724638814</Real>
+      <Real Name="Z">411.53285599378648</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-282.10275508644304</Real>
+      <Real Name="Y">-203.19624419585853</Real>
+      <Real Name="Z">98.987671253915437</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-921.57739533721065</Real>
+      <Real Name="Y">-1072.0346967875478</Real>
+      <Real Name="Z">-362.49159803273699</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">552.34537369327779</Real>
+      <Real Name="Y">-134.66622484998922</Real>
+      <Real Name="Z">476.5462874068599</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-825.87053763926485</Real>
+      <Real Name="Y">110.63862670886428</Real>
+      <Real Name="Z">-469.08984323495338</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-142.90542190901516</Real>
+      <Real Name="Y">-113.63958207580447</Real>
+      <Real Name="Z">-169.03001690049996</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-444.56165600850778</Real>
+      <Real Name="Y">71.188279850209895</Real>
+      <Real Name="Z">-134.6366948363476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">132.61094037807808</Real>
+      <Real Name="Y">-95.731937378984298</Real>
+      <Real Name="Z">65.38939059767371</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">136.64887304762496</Real>
+      <Real Name="Y">33.82687463806031</Real>
+      <Real Name="Z">24.674013484615262</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1131.6822805242764</Real>
+      <Real Name="Y">-448.3669674091675</Real>
+      <Real Name="Z">-133.47298486997047</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-436.98660504161296</Real>
+      <Real Name="Y">45.269070447271169</Real>
+      <Real Name="Z">123.8992503202055</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-420.22942702975985</Real>
+      <Real Name="Y">235.41910841906116</Real>
+      <Real Name="Z">-112.0095531586733</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-491.03661237649249</Real>
+      <Real Name="Y">-836.61706746357163</Real>
+      <Real Name="Z">252.85816072162146</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">219.867563700727</Real>
+      <Real Name="Y">105.62448381059905</Real>
+      <Real Name="Z">-169.00678297257159</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">193.60069899401893</Real>
+      <Real Name="Y">849.00920904618215</Real>
+      <Real Name="Z">18.622482993276588</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">271.98674165859194</Real>
+      <Real Name="Y">551.70697526216361</Real>
+      <Real Name="Z">856.87522463461357</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-32.117681516582444</Real>
+      <Real Name="Y">-572.6158772099551</Real>
+      <Real Name="Z">-570.33868276245721</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-74.744567482858031</Real>
+      <Real Name="Y">-426.31190903888694</Real>
+      <Real Name="Z">-205.33857100813142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">405.67754670713117</Real>
+      <Real Name="Y">-507.07876224786577</Real>
+      <Real Name="Z">-901.51804348362828</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-167.96692731227478</Real>
+      <Real Name="Y">774.34191094699963</Real>
+      <Real Name="Z">128.06895723828433</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-187.44572561475289</Real>
+      <Real Name="Y">-223.55627651083958</Real>
+      <Real Name="Z">658.88942684897393</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">507.53772470647743</Real>
+      <Real Name="Y">-1273.4527922422096</Real>
+      <Real Name="Z">11.851926645724937</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-110.50153487965139</Real>
+      <Real Name="Y">1023.7788867750337</Real>
+      <Real Name="Z">-120.81773417729761</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-354.87255532282586</Real>
+      <Real Name="Y">61.164956544004383</Real>
+      <Real Name="Z">164.07579829833514</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.90132861893801</Real>
+      <Real Name="Y">589.91654030747907</Real>
+      <Real Name="Z">-489.08782624465886</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.48164837450554</Real>
+      <Real Name="Y">-197.10042969488302</Real>
+      <Real Name="Z">103.83666370189484</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">371.95717267686092</Real>
+      <Real Name="Y">-565.68147119096807</Real>
+      <Real Name="Z">448.57624367535755</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1605.8317243035747</Real>
+      <Real Name="Y">97.40376166094471</Real>
+      <Real Name="Z">191.41688404228884</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-180.48261929680177</Real>
+      <Real Name="Y">55.227492069559133</Real>
+      <Real Name="Z">-25.659610581756056</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1187.4098356639367</Real>
+      <Real Name="Y">-200.24574806739946</Real>
+      <Real Name="Z">-178.4185187645474</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-776.04619967225153</Real>
+      <Real Name="Y">-854.14329384957273</Real>
+      <Real Name="Z">703.08772375584374</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">699.83503680531737</Real>
+      <Real Name="Y">682.03015619827897</Real>
+      <Real Name="Z">-359.7940557479738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">193.69522003492455</Real>
+      <Real Name="Y">178.0224559202137</Real>
+      <Real Name="Z">-153.16116270175257</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-281.73657564374889</Real>
+      <Real Name="Y">-160.1098405043962</Real>
+      <Real Name="Z">302.07944030938995</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">314.11087222769828</Real>
+      <Real Name="Y">190.55456361496346</Real>
+      <Real Name="Z">-372.87905333924368</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">59.446762007682459</Real>
+      <Real Name="Y">1.5527080288712511</Real>
+      <Real Name="Z">-74.016383383087032</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">834.30297309556624</Real>
+      <Real Name="Y">-558.97919233869879</Real>
+      <Real Name="Z">-470.72040476920819</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-167.27927245193501</Real>
+      <Real Name="Y">176.25996038586027</Real>
+      <Real Name="Z">25.906500388042986</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-583.33522870085096</Real>
+      <Real Name="Y">264.214828591936</Real>
+      <Real Name="Z">200.07769517661993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.950264603451238</Real>
+      <Real Name="Y">194.66424384509469</Real>
+      <Real Name="Z">70.575248537817984</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.494957010406317</Real>
+      <Real Name="Y">-187.38988065228995</Real>
+      <Real Name="Z">-15.261642766005561</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">74.39095410717708</Real>
+      <Real Name="Y">-141.10678622244825</Real>
+      <Real Name="Z">-61.803642656093189</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-130.77929498714747</Real>
+      <Real Name="Y">1252.1139327884555</Real>
+      <Real Name="Z">178.95834059737058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">38.140245773770062</Real>
+      <Real Name="Y">-154.74381067933058</Real>
+      <Real Name="Z">-75.452988650123658</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">143.53967873337564</Real>
+      <Real Name="Y">-953.24907511564959</Real>
+      <Real Name="Z">-101.34154661247503</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.4578809337321</Real>
+      <Real Name="Y">-973.90214781208147</Real>
+      <Real Name="Z">-614.51814783719408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-96.16279190725804</Real>
+      <Real Name="Y">905.50042697831668</Real>
+      <Real Name="Z">342.92672331250844</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-54.405694183941293</Real>
+      <Real Name="Y">125.96469527599442</Real>
+      <Real Name="Z">142.62462150365187</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-314.02406735423295</Real>
+      <Real Name="Y">1215.8573545506904</Real>
+      <Real Name="Z">812.28121755056145</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">74.090579319896023</Real>
+      <Real Name="Y">-150.56986488606358</Real>
+      <Real Name="Z">-116.35944648345418</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">86.742963398878061</Real>
+      <Real Name="Y">-1019.5218529605444</Real>
+      <Real Name="Z">-591.3487884053028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-172.01455267172878</Real>
+      <Real Name="Y">269.07649174669456</Real>
+      <Real Name="Z">-639.17091448645647</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">255.67067034368353</Real>
+      <Real Name="Y">-290.12782764883707</Real>
+      <Real Name="Z">532.18580175690408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">69.22641394752165</Real>
+      <Real Name="Y">-39.950670244759756</Real>
+      <Real Name="Z">92.765442681400572</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1673.7927896692222</Real>
+      <Real Name="Y">303.78465487901167</Real>
+      <Real Name="Z">61.984690575592474</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1152.1813887163071</Real>
+      <Real Name="Y">-366.18637870336897</Real>
+      <Real Name="Z">110.65381068396444</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">209.99455096612022</Real>
+      <Real Name="Y">-45.745498707542524</Real>
+      <Real Name="Z">-24.782742610748933</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1272.0781554064033</Real>
+      <Real Name="Y">-110.37135699682059</Real>
+      <Real Name="Z">-337.17222473072201</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">388.65217333572281</Real>
+      <Real Name="Y">-45.727534523200475</Real>
+      <Real Name="Z">218.7604114779586</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1050.3336098599909</Real>
+      <Real Name="Y">84.194711180556538</Real>
+      <Real Name="Z">34.147408226943384</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-380.86778006205708</Real>
+      <Real Name="Y">1224.2991521825045</Real>
+      <Real Name="Z">-575.40187398249986</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">134.61354201623757</Real>
+      <Real Name="Y">-594.21486444748939</Real>
+      <Real Name="Z">-286.3118066730205</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">451.06725133012515</Real>
+      <Real Name="Y">-744.69461997358314</Real>
+      <Real Name="Z">605.70723571122267</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">565.38214341123705</Real>
+      <Real Name="Y">1419.0257991196877</Real>
+      <Real Name="Z">681.44399285758311</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-14.445745051650974</Real>
+      <Real Name="Y">-156.01324009696719</Real>
+      <Real Name="Z">-116.634004729851</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-323.72947069390909</Real>
+      <Real Name="Y">-972.23731480397907</Real>
+      <Real Name="Z">-330.28992189122346</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">557.59113984151804</Real>
+      <Real Name="Y">-956.96616397144896</Real>
+      <Real Name="Z">701.86802834897549</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.826835958369983</Real>
+      <Real Name="Y">671.26600223988464</Real>
+      <Real Name="Z">-117.36753370529625</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-619.59573189563116</Real>
+      <Real Name="Y">509.57917320092116</Real>
+      <Real Name="Z">-854.63428806543698</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">179.54534306558807</Real>
+      <Real Name="Y">-607.50008210681858</Real>
+      <Real Name="Z">-56.680398082541814</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-204.33101923943909</Real>
+      <Real Name="Y">564.74974602323573</Real>
+      <Real Name="Z">-102.49790255805648</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-354.55571051756419</Real>
+      <Real Name="Y">276.12499450262214</Real>
+      <Real Name="Z">216.72824712153778</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">543.44571560746272</Real>
+      <Real Name="Y">410.01304514012247</Real>
+      <Real Name="Z">680.16001350062868</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-326.91030353269366</Real>
+      <Real Name="Y">-583.36174948576263</Real>
+      <Real Name="Z">-576.03655185693481</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-134.9241843260387</Real>
+      <Real Name="Y">-54.539363373134591</Real>
+      <Real Name="Z">-165.16868615908751</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.100000 Step 100 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-80.470375508402569</Real>
+      <Real Name="Y">350.75499280724966</Real>
+      <Real Name="Z">-256.9574860396956</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">80.615204766185599</Real>
+      <Real Name="Y">79.850719932664049</Real>
+      <Real Name="Z">-429.01502461358302</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-318.33290835696528</Real>
+      <Real Name="Y">-242.66792718273337</Real>
+      <Real Name="Z">424.28199385495111</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">91.389396581251845</Real>
+      <Real Name="Y">184.24457638440325</Real>
+      <Real Name="Z">435.33119894370327</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">995.84329585834223</Real>
+      <Real Name="Y">481.80307289536813</Real>
+      <Real Name="Z">-1605.2372628615319</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-124.45876484535816</Real>
+      <Real Name="Y">-143.00344326620601</Real>
+      <Real Name="Z">663.1472491894142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-910.63048602609683</Real>
+      <Real Name="Y">-2089.3544882773494</Real>
+      <Real Name="Z">272.75970790246066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-73.115642699948566</Real>
+      <Real Name="Y">800.98355269321826</Real>
+      <Real Name="Z">146.38716209031418</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">468.73336654611683</Real>
+      <Real Name="Y">528.28127655732919</Real>
+      <Real Name="Z">188.46963576543939</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">56.853877200843556</Real>
+      <Real Name="Y">91.767030375547421</Real>
+      <Real Name="Z">-73.245854234100747</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-36.949098480181007</Real>
+      <Real Name="Y">-62.854317944078332</Real>
+      <Real Name="Z">53.107061862847047</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-29.51221180981063</Real>
+      <Real Name="Y">-39.74461767409715</Real>
+      <Real Name="Z">22.842985520097074</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.406390675182905</Real>
+      <Real Name="Y">-43.919910077225012</Real>
+      <Real Name="Z">-43.059097144534633</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-13.022368220103083</Real>
+      <Real Name="Y">21.72868019647435</Real>
+      <Real Name="Z">21.067162220603677</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.861143337000982</Real>
+      <Real Name="Y">16.289635242648529</Real>
+      <Real Name="Z">19.23940986121309</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">284.37452198703164</Real>
+      <Real Name="Y">-206.54227124075854</Real>
+      <Real Name="Z">-387.45943197330359</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-430.72074017231614</Real>
+      <Real Name="Y">208.11857749446932</Real>
+      <Real Name="Z">280.94112908969623</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-72.58486671315957</Real>
+      <Real Name="Y">26.803364805793329</Real>
+      <Real Name="Z">127.97167307433519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">217.50629860699033</Real>
+      <Real Name="Y">1323.3296729186259</Real>
+      <Real Name="Z">-69.175786428811861</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-47.944219582943163</Real>
+      <Real Name="Y">-272.2085432441433</Real>
+      <Real Name="Z">58.89692112387241</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-279.71239297522726</Real>
+      <Real Name="Y">-960.71499344671361</Real>
+      <Real Name="Z">-173.00739706596559</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">691.78324897017887</Real>
+      <Real Name="Y">-719.23362791137549</Real>
+      <Real Name="Z">-1131.4428639906926</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.32786205982254</Real>
+      <Real Name="Y">149.80746159076256</Real>
+      <Real Name="Z">138.36340594414679</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-590.61374256254771</Real>
+      <Real Name="Y">403.4039102007099</Real>
+      <Real Name="Z">761.4888783196094</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.82642064275439</Real>
+      <Real Name="Y">537.34184215662606</Real>
+      <Real Name="Z">294.8702839645066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.8187804874068418</Real>
+      <Real Name="Y">-180.03982466156822</Real>
+      <Real Name="Z">-165.43363750231765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-70.722385711306075</Real>
+      <Real Name="Y">-176.71675051746115</Real>
+      <Real Name="Z">-91.339657977643114</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-634.15511312513433</Real>
+      <Real Name="Y">418.02501779202595</Real>
+      <Real Name="Z">929.42232586489899</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">409.87032459193495</Real>
+      <Real Name="Y">-134.4878014433103</Real>
+      <Real Name="Z">-1011.1664359638603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">127.86142734363521</Real>
+      <Real Name="Y">-138.50679608615576</Real>
+      <Real Name="Z">-88.109814869908604</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1414.010648122154</Real>
+      <Real Name="Y">-122.0641935677959</Real>
+      <Real Name="Z">469.37037726472556</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-445.17948385564847</Real>
+      <Real Name="Y">-629.83247831870256</Real>
+      <Real Name="Z">129.40214266103925</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1304.7877450708149</Real>
+      <Real Name="Y">447.05908036971107</Real>
+      <Real Name="Z">-211.45264013816688</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">405.91798160130395</Real>
+      <Real Name="Y">572.43463161994623</Real>
+      <Real Name="Z">-138.91971943440359</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-145.23467733429612</Real>
+      <Real Name="Y">-367.33675042617932</Real>
+      <Real Name="Z">134.65728199117399</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-188.67952661331245</Real>
+      <Real Name="Y">-102.2894345906753</Real>
+      <Real Name="Z">66.67607700877484</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-205.20131437765127</Real>
+      <Real Name="Y">-24.103884409514578</Real>
+      <Real Name="Z">-885.49406749815159</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.51471524416732</Real>
+      <Real Name="Y">154.51904695464395</Real>
+      <Real Name="Z">374.15370064831228</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">432.59248016850353</Real>
+      <Real Name="Y">-284.86905860034904</Real>
+      <Real Name="Z">554.1577070836438</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">392.06717609939767</Real>
+      <Real Name="Y">220.61703863501089</Real>
+      <Real Name="Z">-650.33843934020615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-400.1801541359149</Real>
+      <Real Name="Y">93.605218491932817</Real>
+      <Real Name="Z">775.80605089267544</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-88.335920800575309</Real>
+      <Real Name="Y">-98.829660739046005</Real>
+      <Real Name="Z">118.44104694775272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-533.22486451835186</Real>
+      <Real Name="Y">-304.67221681906352</Real>
+      <Real Name="Z">-171.16693936475977</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">543.98101869887682</Real>
+      <Real Name="Y">-120.92635097869447</Real>
+      <Real Name="Z">145.04280317069779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.57226627591885</Real>
+      <Real Name="Y">134.32523334115871</Real>
+      <Real Name="Z">-32.998844685195273</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">189.22001440376246</Real>
+      <Real Name="Y">-170.92260258309506</Real>
+      <Real Name="Z">342.172136781414</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-264.68815779150765</Real>
+      <Real Name="Y">221.93944245006728</Real>
+      <Real Name="Z">-235.98071088581645</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-65.412902420931786</Real>
+      <Real Name="Y">68.072078800944553</Real>
+      <Real Name="Z">-87.826083487195405</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-93.092420120685205</Real>
+      <Real Name="Y">-1297.6295013688382</Real>
+      <Real Name="Z">-311.89193615443577</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-251.1963113967202</Real>
+      <Real Name="Y">999.37593338340878</Real>
+      <Real Name="Z">251.16187982880442</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">137.2496122533941</Real>
+      <Real Name="Y">256.61500940332633</Real>
+      <Real Name="Z">77.896414092182169</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">147.10226077152717</Real>
+      <Real Name="Y">455.04017376189807</Real>
+      <Real Name="Z">-590.62487522252377</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">115.05580255428606</Real>
+      <Real Name="Y">-250.45871681873393</Real>
+      <Real Name="Z">89.49512117542659</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-298.11032483571466</Real>
+      <Real Name="Y">-245.51015051313607</Real>
+      <Real Name="Z">368.43348305771747</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">61.057755808604767</Real>
+      <Real Name="Y">91.268221083173657</Real>
+      <Real Name="Z">134.55255442321968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-15.536847554435605</Real>
+      <Real Name="Y">-40.388750799070387</Real>
+      <Real Name="Z">-45.140883650764223</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.6788295183347017</Real>
+      <Real Name="Y">-46.284572466158643</Real>
+      <Real Name="Z">-72.650428124390004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-597.91352128935796</Real>
+      <Real Name="Y">1112.4556805275611</Real>
+      <Real Name="Z">593.41550174263386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">444.871609454652</Real>
+      <Real Name="Y">-443.73997874408377</Real>
+      <Real Name="Z">-469.25800970037375</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.13257022307665522</Real>
+      <Real Name="Y">-199.35021180138574</Real>
+      <Real Name="Z">-21.117018973553652</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1682.1351572508358</Real>
+      <Real Name="Y">-665.88314197241641</Real>
+      <Real Name="Z">-421.07778403784204</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.34063269504415</Real>
+      <Real Name="Y">396.87089922318262</Real>
+      <Real Name="Z">626.1343178555851</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1030.9644765523522</Real>
+      <Real Name="Y">212.16276955863495</Real>
+      <Real Name="Z">-54.515094359155512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">154.52898852621161</Real>
+      <Real Name="Y">-197.70360275330569</Real>
+      <Real Name="Z">-508.5862923066345</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-41.254588364137462</Real>
+      <Real Name="Y">102.3688593731716</Real>
+      <Real Name="Z">214.17885178075139</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">86.323995101269517</Real>
+      <Real Name="Y">79.285454366317808</Real>
+      <Real Name="Z">326.50442896874324</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1096.8155865542828</Real>
+      <Real Name="Y">-903.58172303025253</Real>
+      <Real Name="Z">-573.63956993499028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-680.40941059592637</Real>
+      <Real Name="Y">731.96316675120477</Real>
+      <Real Name="Z">179.79679141648126</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-214.97565510644586</Real>
+      <Real Name="Y">41.942638164221876</Real>
+      <Real Name="Z">133.70778549092017</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1528.2382395764212</Real>
+      <Real Name="Y">-398.86573518382028</Real>
+      <Real Name="Z">-1756.2318334449187</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-940.71009658176365</Real>
+      <Real Name="Y">328.44256142239499</Real>
+      <Real Name="Z">948.39362883227182</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.00726429746948</Real>
+      <Real Name="Y">225.93670085097347</Real>
+      <Real Name="Z">163.30037240194861</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.58010500634429718</Real>
+      <Real Name="Y">17.013798792665085</Real>
+      <Real Name="Z">22.849762241174474</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.53662189605013566</Real>
+      <Real Name="Y">-9.0077658723944314</Real>
+      <Real Name="Z">-8.4459946389454466</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.9073981593518567</Real>
+      <Real Name="Y">-6.5636201490962733</Real>
+      <Real Name="Z">-13.663898432045574</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">533.23871425842935</Real>
+      <Real Name="Y">1007.0394154924159</Real>
+      <Real Name="Z">-480.18500341061758</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">214.85279863649492</Real>
+      <Real Name="Y">-517.59329996214387</Real>
+      <Real Name="Z">-26.284205349687507</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-566.10264063152442</Real>
+      <Real Name="Y">-518.93790719729986</Real>
+      <Real Name="Z">583.49486218652976</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.5900056554803825</Real>
+      <Real Name="Y">101.19334933592913</Real>
+      <Real Name="Z">-385.88886417469536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-42.623586101870032</Real>
+      <Real Name="Y">-95.381976889716469</Real>
+      <Real Name="Z">115.31521538474293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">113.7193296062938</Real>
+      <Real Name="Y">12.115894024602676</Real>
+      <Real Name="Z">200.39525990251997</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-469.28552061898358</Real>
+      <Real Name="Y">-591.5663580025348</Real>
+      <Real Name="Z">-201.06668344044266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">130.07713160919141</Real>
+      <Real Name="Y">470.9262512974916</Real>
+      <Real Name="Z">-205.16050461135794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">306.152029602315</Real>
+      <Real Name="Y">470.18757688629569</Real>
+      <Real Name="Z">585.73845993749308</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-539.82327761137981</Real>
+      <Real Name="Y">291.95850725044608</Real>
+      <Real Name="Z">-204.87246425179256</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">564.92226996899694</Real>
+      <Real Name="Y">-323.45535746138034</Real>
+      <Real Name="Z">213.6628979429135</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">114.96542942624916</Real>
+      <Real Name="Y">-40.357390920796199</Real>
+      <Real Name="Z">86.404650698957312</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">175.69145827035427</Real>
+      <Real Name="Y">165.02257919357237</Real>
+      <Real Name="Z">118.67955970079535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-126.23311484605364</Real>
+      <Real Name="Y">-68.622104847425305</Real>
+      <Real Name="Z">-124.5101623410911</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.952144857058542</Real>
+      <Real Name="Y">-162.88030148504902</Real>
+      <Real Name="Z">-20.09353308527546</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.30271893456592</Real>
+      <Real Name="Y">-370.37257072821069</Real>
+      <Real Name="Z">-371.84712902114995</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">76.343516001430274</Real>
+      <Real Name="Y">171.76878791921439</Real>
+      <Real Name="Z">179.83467938376367</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">467.86087721395381</Real>
+      <Real Name="Y">402.95359683629869</Real>
+      <Real Name="Z">206.29434437732829</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">550.00371072675171</Real>
+      <Real Name="Y">-198.90170415950496</Real>
+      <Real Name="Z">-1030.4683362838441</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.320087292327</Real>
+      <Real Name="Y">15.573301495136221</Real>
+      <Real Name="Z">678.88011579200133</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-171.25089027177316</Real>
+      <Real Name="Y">159.05066843685651</Real>
+      <Real Name="Z">203.06403755555374</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-472.54497944084181</Real>
+      <Real Name="Y">-317.56356035502665</Real>
+      <Real Name="Z">-574.3359092738059</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">113.05925571186343</Real>
+      <Real Name="Y">46.616327103672745</Real>
+      <Real Name="Z">116.52175041886659</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">207.61688567754553</Real>
+      <Real Name="Y">180.03176836992236</Real>
+      <Real Name="Z">617.50887474607077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-727.91188605012371</Real>
+      <Real Name="Y">-53.232749912941173</Real>
+      <Real Name="Z">-103.59249611118864</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">327.93905433357008</Real>
+      <Real Name="Y">205.18150005924883</Real>
+      <Real Name="Z">171.9638717891437</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">202.07683314875794</Real>
+      <Real Name="Y">192.82433748619763</Real>
+      <Real Name="Z">110.14927965284616</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">340.74417163539397</Real>
+      <Real Name="Y">960.7999225377805</Real>
+      <Real Name="Z">375.63818105329278</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-202.12643849786735</Real>
+      <Real Name="Y">-133.9928974745327</Real>
+      <Real Name="Z">150.72280337935254</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-662.18847898477338</Real>
+      <Real Name="Y">-1134.125955512827</Real>
+      <Real Name="Z">-84.852711911976144</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1159.4322770996482</Real>
+      <Real Name="Y">-56.610963457778197</Real>
+      <Real Name="Z">380.82996094267293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-874.16343127792425</Real>
+      <Real Name="Y">385.7497069869903</Real>
+      <Real Name="Z">-289.99078849033361</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-273.62001723329837</Real>
+      <Real Name="Y">-72.168746790355812</Real>
+      <Real Name="Z">28.307125561827316</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-403.91534326804765</Real>
+      <Real Name="Y">62.873110094603511</Real>
+      <Real Name="Z">-212.15751347965494</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">108.98891621693943</Real>
+      <Real Name="Y">-64.482453690004746</Real>
+      <Real Name="Z">62.233184894454041</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">162.07772034712616</Real>
+      <Real Name="Y">69.345488985092629</Real>
+      <Real Name="Z">59.55714364391828</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">772.76436666144537</Real>
+      <Real Name="Y">-524.79775287055543</Real>
+      <Real Name="Z">187.23846686847705</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-264.98509096920668</Real>
+      <Real Name="Y">37.665715572679794</Real>
+      <Real Name="Z">13.024677647843134</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-348.25337451623045</Real>
+      <Real Name="Y">371.84466962337598</Real>
+      <Real Name="Z">-179.02371952592384</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-549.28877180090694</Real>
+      <Real Name="Y">-821.31319241851111</Real>
+      <Real Name="Z">196.80794005573404</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">274.50236712231094</Real>
+      <Real Name="Y">72.581755928740364</Real>
+      <Real Name="Z">-182.68425753488387</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">116.75811248239779</Real>
+      <Real Name="Y">683.07793040935906</Real>
+      <Real Name="Z">-35.421281815473087</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">93.740065802858368</Real>
+      <Real Name="Y">879.03374968897492</Real>
+      <Real Name="Z">738.89723120028407</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.47979607943159</Real>
+      <Real Name="Y">-543.89243128183239</Real>
+      <Real Name="Z">-639.10506376935803</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.864925023173058</Real>
+      <Real Name="Y">-329.88777255751518</Real>
+      <Real Name="Z">-102.7810023074663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">360.11943503670011</Real>
+      <Real Name="Y">-1233.1779807140078</Real>
+      <Real Name="Z">-913.06029874709463</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-339.93448112201713</Real>
+      <Real Name="Y">983.55211495574019</Real>
+      <Real Name="Z">-33.293443249194077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">49.867111823209903</Real>
+      <Real Name="Y">107.19249957400177</Real>
+      <Real Name="Z">524.16574288438892</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">361.34181285778578</Real>
+      <Real Name="Y">-1205.9768619832532</Real>
+      <Real Name="Z">-153.79773162773461</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">98.122030385179272</Real>
+      <Real Name="Y">963.622355248541</Real>
+      <Real Name="Z">-5.0337807202246481</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-393.38743329111622</Real>
+      <Real Name="Y">-133.59762578895231</Real>
+      <Real Name="Z">249.15780407339963</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-329.36400465759164</Real>
+      <Real Name="Y">540.02667124724394</Real>
+      <Real Name="Z">-379.47457291559954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.66217069581518</Real>
+      <Real Name="Y">-169.31399747841655</Real>
+      <Real Name="Z">62.995933264664075</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">350.93706805090727</Real>
+      <Real Name="Y">-615.22496673547857</Real>
+      <Real Name="Z">542.39718117818813</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">958.8348297547858</Real>
+      <Real Name="Y">169.44870725020175</Real>
+      <Real Name="Z">90.889116507278345</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-141.91877012282299</Real>
+      <Real Name="Y">-16.40148520052368</Real>
+      <Real Name="Z">-1.0194681269216517</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-863.97996901846489</Real>
+      <Real Name="Y">-311.63068993327192</Real>
+      <Real Name="Z">-95.751021380317141</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-740.39729230238629</Real>
+      <Real Name="Y">-1067.9582462984197</Real>
+      <Real Name="Z">843.67397086576284</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">733.28392148637079</Real>
+      <Real Name="Y">727.17031470846007</Real>
+      <Real Name="Z">-351.2882100377301</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">98.908263162233197</Real>
+      <Real Name="Y">188.73447094919487</Real>
+      <Real Name="Z">-276.3472174081719</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-222.91942135925493</Real>
+      <Real Name="Y">-163.15361823305022</Real>
+      <Real Name="Z">149.44052924138788</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">146.58440040238409</Real>
+      <Real Name="Y">263.44178011419206</Real>
+      <Real Name="Z">-242.39008504879013</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">35.79539196350585</Real>
+      <Real Name="Y">45.676648878868995</Real>
+      <Real Name="Z">-46.76263536001504</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2247.7903082525827</Real>
+      <Real Name="Y">-620.33115722059324</Real>
+      <Real Name="Z">-626.15367486079197</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-617.35656414400057</Real>
+      <Real Name="Y">316.10177748329323</Real>
+      <Real Name="Z">-52.365142069876576</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1121.1386205133208</Real>
+      <Real Name="Y">257.25995178265737</Real>
+      <Real Name="Z">397.39531621657829</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">72.85078125153656</Real>
+      <Real Name="Y">169.11484988364555</Real>
+      <Real Name="Z">55.985740726388798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.552564779788725</Real>
+      <Real Name="Y">-153.75708001004099</Real>
+      <Real Name="Z">-27.936313505590991</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.540564040855628</Real>
+      <Real Name="Y">-172.36168871122584</Real>
+      <Real Name="Z">-96.438546351881357</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-65.246780548120071</Real>
+      <Real Name="Y">1234.2535063887406</Real>
+      <Real Name="Z">198.04343090486495</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">19.895777724114737</Real>
+      <Real Name="Y">-157.68944150107194</Real>
+      <Real Name="Z">-90.680288632861391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.796634273445036</Real>
+      <Real Name="Y">-927.13378102733293</Real>
+      <Real Name="Z">-46.188775910015934</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.93640039449204</Real>
+      <Real Name="Y">-1052.9284615169117</Real>
+      <Real Name="Z">-292.04874454470206</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-124.10251294371136</Real>
+      <Real Name="Y">902.07435218013779</Real>
+      <Real Name="Z">524.06345064898369</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-70.710089949217377</Real>
+      <Real Name="Y">217.59016635516207</Real>
+      <Real Name="Z">82.680400505852418</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">91.798559446723885</Real>
+      <Real Name="Y">975.33725458002061</Real>
+      <Real Name="Z">226.67370601455917</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.7548410158735095</Real>
+      <Real Name="Y">73.078056367197433</Real>
+      <Real Name="Z">91.714689506775912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-303.22562825492088</Real>
+      <Real Name="Y">-735.25728584295337</Real>
+      <Real Name="Z">-392.41633573838567</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-77.691049214432894</Real>
+      <Real Name="Y">577.57615117413366</Real>
+      <Real Name="Z">-947.95927674705808</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-31.06808001828685</Real>
+      <Real Name="Y">-585.67695233801999</Real>
+      <Real Name="Z">674.35188272220216</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.388297464535597</Real>
+      <Real Name="Y">-33.688072569121701</Real>
+      <Real Name="Z">102.19386813705826</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1710.608915206268</Real>
+      <Real Name="Y">445.58984398929567</Real>
+      <Real Name="Z">-309.47986695016459</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1222.0269262775732</Real>
+      <Real Name="Y">-338.61498536017916</Real>
+      <Real Name="Z">161.77822708133056</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">248.94191337800689</Real>
+      <Real Name="Y">-69.122394288675423</Real>
+      <Real Name="Z">40.158422008350954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1396.1548887018905</Real>
+      <Real Name="Y">-219.7841661067186</Real>
+      <Real Name="Z">-353.11999671586051</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.08508683863562</Real>
+      <Real Name="Y">24.70025154764026</Real>
+      <Real Name="Z">87.082581647696202</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1173.2269677971703</Real>
+      <Real Name="Y">272.48308566068607</Real>
+      <Real Name="Z">51.723242690437495</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.4178515056752019</Real>
+      <Real Name="Y">679.38690421674778</Real>
+      <Real Name="Z">-317.1291228661932</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.506338640600653</Real>
+      <Real Name="Y">-441.44288170479507</Real>
+      <Real Name="Z">-123.07978759742602</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">214.75480347773242</Real>
+      <Real Name="Y">-559.09242782614058</Real>
+      <Real Name="Z">557.49546136203946</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">399.84997713148175</Real>
+      <Real Name="Y">967.80180779656757</Real>
+      <Real Name="Z">391.8221794740461</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.2083924670883448</Real>
+      <Real Name="Y">-172.12220962096438</Real>
+      <Real Name="Z">-72.032481249598874</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-391.6116567420932</Real>
+      <Real Name="Y">-733.88694130558724</Real>
+      <Real Name="Z">-305.59803260264488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">788.88040134345886</Real>
+      <Real Name="Y">-854.10894681477816</Real>
+      <Real Name="Z">1371.4034117595063</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.646189526332087</Real>
+      <Real Name="Y">558.33570350317723</Real>
+      <Real Name="Z">-292.16666751875476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-588.00643267754833</Real>
+      <Real Name="Y">450.39769815834609</Real>
+      <Real Name="Z">-995.66142533643438</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">268.85427302238236</Real>
+      <Real Name="Y">-751.06051775782566</Real>
+      <Real Name="Z">41.871227908847061</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-160.20060292251719</Real>
+      <Real Name="Y">624.46879688282138</Real>
+      <Real Name="Z">71.392363524358601</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-320.62064214309487</Real>
+      <Real Name="Y">262.19127815756889</Real>
+      <Real Name="Z">66.478562474853987</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">377.59767922175115</Real>
+      <Real Name="Y">567.94026278069782</Real>
+      <Real Name="Z">651.91997202184416</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-409.91145740910378</Real>
+      <Real Name="Y">-675.95385792490492</Real>
+      <Real Name="Z">-501.94217462418743</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-118.68909340409331</Real>
+      <Real Name="Y">-117.02319901261541</Real>
+      <Real Name="Z">-184.70561693311549</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_expanded_s.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_expanded_s.xml
new file mode 100644 (file)
index 0000000..4f5e84b
--- /dev/null
@@ -0,0 +1,1201 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVvdw/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">4.7800546</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">5.2079234</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">5.6310158</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">6.0403891</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">6.424715</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">6.7718639</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">34.629845</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">35.175533</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">35.555824</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">35.746628</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">35.738613</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">35.541836</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">35.181416</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">34.692268</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">34.117912</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">33.500462</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">32.878761</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">32.284508</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">31.740307</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">31.257408</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">30.834925</Real>
+    <Real Name="Time 0.021000 Step 21 in frame 21">30.464111</Real>
+    <Real Name="Time 0.022000 Step 22 in frame 22">30.109394</Real>
+    <Real Name="Time 0.023000 Step 23 in frame 23">29.748163</Real>
+    <Real Name="Time 0.024000 Step 24 in frame 24">29.355972</Real>
+    <Real Name="Time 0.025000 Step 25 in frame 25">28.911327</Real>
+    <Real Name="Time 0.026000 Step 26 in frame 26">28.387049</Real>
+    <Real Name="Time 0.027000 Step 27 in frame 27">27.776436</Real>
+    <Real Name="Time 0.028000 Step 28 in frame 28">27.072624</Real>
+    <Real Name="Time 0.029000 Step 29 in frame 29">26.277346</Real>
+    <Real Name="Time 0.030000 Step 30 in frame 30">25.399197</Real>
+    <Real Name="Time 0.031000 Step 31 in frame 31">24.449884</Real>
+    <Real Name="Time 0.032000 Step 32 in frame 32">23.442257</Real>
+    <Real Name="Time 0.033000 Step 33 in frame 33">22.389338</Real>
+    <Real Name="Time 0.034000 Step 34 in frame 34">21.301222</Real>
+    <Real Name="Time 0.035000 Step 35 in frame 35">20.184431</Real>
+    <Real Name="Time 0.036000 Step 36 in frame 36">19.041292</Real>
+    <Real Name="Time 0.037000 Step 37 in frame 37">17.877878</Real>
+    <Real Name="Time 0.038000 Step 38 in frame 38">16.690737</Real>
+    <Real Name="Time 0.039000 Step 39 in frame 39">15.478399</Real>
+    <Real Name="Time 0.040000 Step 40 in frame 40">14.244552</Real>
+    <Real Name="Time 0.041000 Step 41 in frame 41">12.997849</Real>
+    <Real Name="Time 0.042000 Step 42 in frame 42">11.753716</Real>
+    <Real Name="Time 0.043000 Step 43 in frame 43">10.532389</Real>
+    <Real Name="Time 0.044000 Step 44 in frame 44">9.3571129</Real>
+    <Real Name="Time 0.045000 Step 45 in frame 45">8.2514114</Real>
+    <Real Name="Time 0.046000 Step 46 in frame 46">7.2352495</Real>
+    <Real Name="Time 0.047000 Step 47 in frame 47">6.3088546</Real>
+    <Real Name="Time 0.048000 Step 48 in frame 48">5.4812493</Real>
+    <Real Name="Time 0.049000 Step 49 in frame 49">4.7540174</Real>
+    <Real Name="Time 0.050000 Step 50 in frame 50">4.1306443</Real>
+    <Real Name="Time 0.051000 Step 51 in frame 51">3.6067312</Real>
+    <Real Name="Time 0.052000 Step 52 in frame 52">3.1865196</Real>
+    <Real Name="Time 0.053000 Step 53 in frame 53">2.8634665</Real>
+    <Real Name="Time 0.054000 Step 54 in frame 54">2.6289072</Real>
+    <Real Name="Time 0.055000 Step 55 in frame 55">2.4763718</Real>
+    <Real Name="Time 0.056000 Step 56 in frame 56">2.3925138</Real>
+    <Real Name="Time 0.057000 Step 57 in frame 57">2.3606334</Real>
+    <Real Name="Time 0.058000 Step 58 in frame 58">2.3686244</Real>
+    <Real Name="Time 0.059000 Step 59 in frame 59">2.4026456</Real>
+    <Real Name="Time 0.060000 Step 60 in frame 60">2.4466541</Real>
+    <Real Name="Time 0.061000 Step 61 in frame 61">2.483947</Real>
+    <Real Name="Time 0.062000 Step 62 in frame 62">2.4974856</Real>
+    <Real Name="Time 0.063000 Step 63 in frame 63">2.4757094</Real>
+    <Real Name="Time 0.064000 Step 64 in frame 64">2.402739</Real>
+    <Real Name="Time 0.065000 Step 65 in frame 65">2.2751596</Real>
+    <Real Name="Time 0.066000 Step 66 in frame 66">2.0926239</Real>
+    <Real Name="Time 0.067000 Step 67 in frame 67">1.8694811</Real>
+    <Real Name="Time 0.068000 Step 68 in frame 68">1.6155019</Real>
+    <Real Name="Time 0.069000 Step 69 in frame 69">1.3437223</Real>
+    <Real Name="Time 0.070000 Step 70 in frame 70">1.0700464</Real>
+    <Real Name="Time 0.071000 Step 71 in frame 71">0.81150562</Real>
+    <Real Name="Time 0.072000 Step 72 in frame 72">0.5867157</Real>
+    <Real Name="Time 0.073000 Step 73 in frame 73">0.41625035</Real>
+    <Real Name="Time 0.074000 Step 74 in frame 74">0.3173905</Real>
+    <Real Name="Time 0.075000 Step 75 in frame 75">0.3048237</Real>
+    <Real Name="Time 0.076000 Step 76 in frame 76">0.38662881</Real>
+    <Real Name="Time 0.077000 Step 77 in frame 77">0.56125587</Real>
+    <Real Name="Time 0.078000 Step 78 in frame 78">0.82186919</Real>
+    <Real Name="Time 0.079000 Step 79 in frame 79">1.1558673</Real>
+    <Real Name="Time 0.080000 Step 80 in frame 80">1.5468981</Real>
+    <Real Name="Time 0.081000 Step 81 in frame 81">1.9766276</Real>
+    <Real Name="Time 0.082000 Step 82 in frame 82">2.4323692</Real>
+    <Real Name="Time 0.083000 Step 83 in frame 83">2.9078076</Real>
+    <Real Name="Time 0.084000 Step 84 in frame 84">3.4029188</Real>
+    <Real Name="Time 0.085000 Step 85 in frame 85">3.9236453</Real>
+    <Real Name="Time 0.086000 Step 86 in frame 86">4.4778805</Real>
+    <Real Name="Time 0.087000 Step 87 in frame 87">5.0692177</Real>
+    <Real Name="Time 0.088000 Step 88 in frame 88">5.6967297</Real>
+    <Real Name="Time 0.089000 Step 89 in frame 89">6.3527784</Real>
+    <Real Name="Time 0.090000 Step 90 in frame 90">7.0247569</Real>
+    <Real Name="Time 0.091000 Step 91 in frame 91">-13.676886</Real>
+    <Real Name="Time 0.092000 Step 92 in frame 92">-13.535053</Real>
+    <Real Name="Time 0.093000 Step 93 in frame 93">-13.392182</Real>
+    <Real Name="Time 0.094000 Step 94 in frame 94">-13.250134</Real>
+    <Real Name="Time 0.095000 Step 95 in frame 95">-13.110174</Real>
+    <Real Name="Time 0.096000 Step 96 in frame 96">11.09705</Real>
+    <Real Name="Time 0.097000 Step 97 in frame 97">11.736928</Real>
+    <Real Name="Time 0.098000 Step 98 in frame 98">12.350535</Real>
+    <Real Name="Time 0.099000 Step 99 in frame 99">12.951029</Real>
+    <Real Name="Time 0.100000 Step 100 in frame 100">13.553705</Real>
+  </Energy>
+  <Energy Name="dVcoul/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-77.872757</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-77.716484</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-77.504288</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-77.242493</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-76.94445</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-76.638924</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-76.364861</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-76.499763</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-77.071358</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-78.057755</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-79.391968</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-80.964645</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-82.647415</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-84.310303</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-85.847183</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-87.190964</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-88.322319</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-89.269363</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-90.098076</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-90.895874</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-91.756027</Real>
+    <Real Name="Time 0.021000 Step 21 in frame 21">-92.751175</Real>
+    <Real Name="Time 0.022000 Step 22 in frame 22">-93.853714</Real>
+    <Real Name="Time 0.023000 Step 23 in frame 23">-95.070648</Real>
+    <Real Name="Time 0.024000 Step 24 in frame 24">-96.371155</Real>
+    <Real Name="Time 0.025000 Step 25 in frame 25">-97.685806</Real>
+    <Real Name="Time 0.026000 Step 26 in frame 26">-98.920258</Real>
+    <Real Name="Time 0.027000 Step 27 in frame 27">-99.954559</Real>
+    <Real Name="Time 0.028000 Step 28 in frame 28">-100.6597</Real>
+    <Real Name="Time 0.029000 Step 29 in frame 29">-100.91674</Real>
+    <Real Name="Time 0.030000 Step 30 in frame 30">-100.63353</Real>
+    <Real Name="Time 0.031000 Step 31 in frame 31">-99.768555</Real>
+    <Real Name="Time 0.032000 Step 32 in frame 32">-98.333786</Real>
+    <Real Name="Time 0.033000 Step 33 in frame 33">-96.40303</Real>
+    <Real Name="Time 0.034000 Step 34 in frame 34">-94.089249</Real>
+    <Real Name="Time 0.035000 Step 35 in frame 35">-91.536819</Real>
+    <Real Name="Time 0.036000 Step 36 in frame 36">-88.894089</Real>
+    <Real Name="Time 0.037000 Step 37 in frame 37">-86.395172</Real>
+    <Real Name="Time 0.038000 Step 38 in frame 38">-84.163986</Real>
+    <Real Name="Time 0.039000 Step 39 in frame 39">-82.286827</Real>
+    <Real Name="Time 0.040000 Step 40 in frame 40">-80.820839</Real>
+    <Real Name="Time 0.041000 Step 41 in frame 41">-79.782623</Real>
+    <Real Name="Time 0.042000 Step 42 in frame 42">-79.156143</Real>
+    <Real Name="Time 0.043000 Step 43 in frame 43">-78.892593</Real>
+    <Real Name="Time 0.044000 Step 44 in frame 44">-78.917526</Real>
+    <Real Name="Time 0.045000 Step 45 in frame 45">-79.144478</Real>
+    <Real Name="Time 0.046000 Step 46 in frame 46">-79.489128</Real>
+    <Real Name="Time 0.047000 Step 47 in frame 47">-79.623444</Real>
+    <Real Name="Time 0.048000 Step 48 in frame 48">-79.511932</Real>
+    <Real Name="Time 0.049000 Step 49 in frame 49">-79.138031</Real>
+    <Real Name="Time 0.050000 Step 50 in frame 50">-78.500427</Real>
+    <Real Name="Time 0.051000 Step 51 in frame 51">-77.612648</Real>
+    <Real Name="Time 0.052000 Step 52 in frame 52">-76.643219</Real>
+    <Real Name="Time 0.053000 Step 53 in frame 53">-75.611252</Real>
+    <Real Name="Time 0.054000 Step 54 in frame 54">-74.548294</Real>
+    <Real Name="Time 0.055000 Step 55 in frame 55">-73.485031</Real>
+    <Real Name="Time 0.056000 Step 56 in frame 56">-72.4608</Real>
+    <Real Name="Time 0.057000 Step 57 in frame 57">-71.385513</Real>
+    <Real Name="Time 0.058000 Step 58 in frame 58">-70.311348</Real>
+    <Real Name="Time 0.059000 Step 59 in frame 59">-69.294205</Real>
+    <Real Name="Time 0.060000 Step 60 in frame 60">-68.385994</Real>
+    <Real Name="Time 0.061000 Step 61 in frame 61">-67.637817</Real>
+    <Real Name="Time 0.062000 Step 62 in frame 62">-67.089478</Real>
+    <Real Name="Time 0.063000 Step 63 in frame 63">-66.766228</Real>
+    <Real Name="Time 0.064000 Step 64 in frame 64">-66.676643</Real>
+    <Real Name="Time 0.065000 Step 65 in frame 65">-66.805649</Real>
+    <Real Name="Time 0.066000 Step 66 in frame 66">-67.123459</Real>
+    <Real Name="Time 0.067000 Step 67 in frame 67">-67.788086</Real>
+    <Real Name="Time 0.068000 Step 68 in frame 68">-68.743294</Real>
+    <Real Name="Time 0.069000 Step 69 in frame 69">-69.921448</Real>
+    <Real Name="Time 0.070000 Step 70 in frame 70">-71.252861</Real>
+    <Real Name="Time 0.071000 Step 71 in frame 71">-72.66301</Real>
+    <Real Name="Time 0.072000 Step 72 in frame 72">-74.08239</Real>
+    <Real Name="Time 0.073000 Step 73 in frame 73">-75.440865</Real>
+    <Real Name="Time 0.074000 Step 74 in frame 74">-76.68605</Real>
+    <Real Name="Time 0.075000 Step 75 in frame 75">-77.778305</Real>
+    <Real Name="Time 0.076000 Step 76 in frame 76">-78.707558</Real>
+    <Real Name="Time 0.077000 Step 77 in frame 77">-79.432983</Real>
+    <Real Name="Time 0.078000 Step 78 in frame 78">-80.010605</Real>
+    <Real Name="Time 0.079000 Step 79 in frame 79">-80.512024</Real>
+    <Real Name="Time 0.080000 Step 80 in frame 80">-81.016243</Real>
+    <Real Name="Time 0.081000 Step 81 in frame 81">-81.58696</Real>
+    <Real Name="Time 0.082000 Step 82 in frame 82">-82.254745</Real>
+    <Real Name="Time 0.083000 Step 83 in frame 83">-83.005646</Real>
+    <Real Name="Time 0.084000 Step 84 in frame 84">-83.777626</Real>
+    <Real Name="Time 0.085000 Step 85 in frame 85">-84.464981</Real>
+    <Real Name="Time 0.086000 Step 86 in frame 86">-84.94146</Real>
+    <Real Name="Time 0.087000 Step 87 in frame 87">-85.089493</Real>
+    <Real Name="Time 0.088000 Step 88 in frame 88">-84.828125</Real>
+    <Real Name="Time 0.089000 Step 89 in frame 89">-84.139664</Real>
+    <Real Name="Time 0.090000 Step 90 in frame 90">-83.075638</Real>
+    <Real Name="Time 0.091000 Step 91 in frame 91">-81.742142</Real>
+    <Real Name="Time 0.092000 Step 92 in frame 92">-79.956558</Real>
+    <Real Name="Time 0.093000 Step 93 in frame 93">-77.849442</Real>
+    <Real Name="Time 0.094000 Step 94 in frame 94">-75.557968</Real>
+    <Real Name="Time 0.095000 Step 95 in frame 95">-73.206947</Real>
+    <Real Name="Time 0.096000 Step 96 in frame 96">-70.902786</Real>
+    <Real Name="Time 0.097000 Step 97 in frame 97">-68.715012</Real>
+    <Real Name="Time 0.098000 Step 98 in frame 98">-66.684258</Real>
+    <Real Name="Time 0.099000 Step 99 in frame 99">-64.815567</Real>
+    <Real Name="Time 0.100000 Step 100 in frame 100">-63.084229</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1466.0806</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1464.0562</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1460.3931</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1455.229</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1448.8822</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1441.8026</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1507.046</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1500.2825</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1494.8878</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1490.9832</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1488.3712</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1486.641</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1485.3728</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1484.2686</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1483.2332</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1482.3788</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1481.9104</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1482.0206</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1482.8102</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1484.2251</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1486.1049</Real>
+    <Real Name="Time 0.021000 Step 21 in frame 21">-1469.7079</Real>
+    <Real Name="Time 0.022000 Step 22 in frame 22">-1471.7432</Real>
+    <Real Name="Time 0.023000 Step 23 in frame 23">-1473.8549</Real>
+    <Real Name="Time 0.024000 Step 24 in frame 24">-1476.1924</Real>
+    <Real Name="Time 0.025000 Step 25 in frame 25">-1479.0073</Real>
+    <Real Name="Time 0.026000 Step 26 in frame 26">-1482.5864</Real>
+    <Real Name="Time 0.027000 Step 27 in frame 27">-1487.1035</Real>
+    <Real Name="Time 0.028000 Step 28 in frame 28">-1492.6003</Real>
+    <Real Name="Time 0.029000 Step 29 in frame 29">-1498.9333</Real>
+    <Real Name="Time 0.030000 Step 30 in frame 30">-1505.8589</Real>
+    <Real Name="Time 0.031000 Step 31 in frame 31">-1513.0725</Real>
+    <Real Name="Time 0.032000 Step 32 in frame 32">-1520.2764</Real>
+    <Real Name="Time 0.033000 Step 33 in frame 33">-1527.1776</Real>
+    <Real Name="Time 0.034000 Step 34 in frame 34">-1533.4562</Real>
+    <Real Name="Time 0.035000 Step 35 in frame 35">-1538.7783</Real>
+    <Real Name="Time 0.036000 Step 36 in frame 36">-1560.6183</Real>
+    <Real Name="Time 0.037000 Step 37 in frame 37">-1562.8105</Real>
+    <Real Name="Time 0.038000 Step 38 in frame 38">-1563.7844</Real>
+    <Real Name="Time 0.039000 Step 39 in frame 39">-1563.9221</Real>
+    <Real Name="Time 0.040000 Step 40 in frame 40">-1563.8104</Real>
+    <Real Name="Time 0.041000 Step 41 in frame 41">-1564.0935</Real>
+    <Real Name="Time 0.042000 Step 42 in frame 42">-1565.2661</Real>
+    <Real Name="Time 0.043000 Step 43 in frame 43">-1567.5031</Real>
+    <Real Name="Time 0.044000 Step 44 in frame 44">-1570.5859</Real>
+    <Real Name="Time 0.045000 Step 45 in frame 45">-1573.9664</Real>
+    <Real Name="Time 0.046000 Step 46 in frame 46">-1529.2445</Real>
+    <Real Name="Time 0.047000 Step 47 in frame 47">-1530.9165</Real>
+    <Real Name="Time 0.048000 Step 48 in frame 48">-1531.1292</Real>
+    <Real Name="Time 0.049000 Step 49 in frame 49">-1529.8002</Real>
+    <Real Name="Time 0.050000 Step 50 in frame 50">-1527.1536</Real>
+    <Real Name="Time 0.051000 Step 51 in frame 51">-1554.8087</Real>
+    <Real Name="Time 0.052000 Step 52 in frame 52">-1551.031</Real>
+    <Real Name="Time 0.053000 Step 53 in frame 53">-1548.1353</Real>
+    <Real Name="Time 0.054000 Step 54 in frame 54">-1546.9164</Real>
+    <Real Name="Time 0.055000 Step 55 in frame 55">-1547.8556</Real>
+    <Real Name="Time 0.056000 Step 56 in frame 56">-1521.9631</Real>
+    <Real Name="Time 0.057000 Step 57 in frame 57">-1527.0684</Real>
+    <Real Name="Time 0.058000 Step 58 in frame 58">-1532.7517</Real>
+    <Real Name="Time 0.059000 Step 59 in frame 59">-1537.8038</Real>
+    <Real Name="Time 0.060000 Step 60 in frame 60">-1541.1539</Real>
+    <Real Name="Time 0.061000 Step 61 in frame 61">-1542.1595</Real>
+    <Real Name="Time 0.062000 Step 62 in frame 62">-1540.8126</Real>
+    <Real Name="Time 0.063000 Step 63 in frame 63">-1537.6787</Real>
+    <Real Name="Time 0.064000 Step 64 in frame 64">-1533.6814</Real>
+    <Real Name="Time 0.065000 Step 65 in frame 65">-1529.7848</Real>
+    <Real Name="Time 0.066000 Step 66 in frame 66">-1567.0042</Real>
+    <Real Name="Time 0.067000 Step 67 in frame 67">-1565.7349</Real>
+    <Real Name="Time 0.068000 Step 68 in frame 68">-1565.9775</Real>
+    <Real Name="Time 0.069000 Step 69 in frame 69">-1567.3188</Real>
+    <Real Name="Time 0.070000 Step 70 in frame 70">-1569.1547</Real>
+    <Real Name="Time 0.071000 Step 71 in frame 71">-1570.7939</Real>
+    <Real Name="Time 0.072000 Step 72 in frame 72">-1571.5699</Real>
+    <Real Name="Time 0.073000 Step 73 in frame 73">-1570.9155</Real>
+    <Real Name="Time 0.074000 Step 74 in frame 74">-1568.4536</Real>
+    <Real Name="Time 0.075000 Step 75 in frame 75">-1564.1188</Real>
+    <Real Name="Time 0.076000 Step 76 in frame 76">-1542.5352</Real>
+    <Real Name="Time 0.077000 Step 77 in frame 77">-1535.8567</Real>
+    <Real Name="Time 0.078000 Step 78 in frame 78">-1529.7339</Real>
+    <Real Name="Time 0.079000 Step 79 in frame 79">-1525.5062</Real>
+    <Real Name="Time 0.080000 Step 80 in frame 80">-1524.2909</Real>
+    <Real Name="Time 0.081000 Step 81 in frame 81">-1526.635</Real>
+    <Real Name="Time 0.082000 Step 82 in frame 82">-1532.3204</Real>
+    <Real Name="Time 0.083000 Step 83 in frame 83">-1540.3643</Real>
+    <Real Name="Time 0.084000 Step 84 in frame 84">-1549.2531</Real>
+    <Real Name="Time 0.085000 Step 85 in frame 85">-1557.3151</Real>
+    <Real Name="Time 0.086000 Step 86 in frame 86">-1563.156</Real>
+    <Real Name="Time 0.087000 Step 87 in frame 87">-1566.0171</Real>
+    <Real Name="Time 0.088000 Step 88 in frame 88">-1565.9149</Real>
+    <Real Name="Time 0.089000 Step 89 in frame 89">-1563.5468</Real>
+    <Real Name="Time 0.090000 Step 90 in frame 90">-1559.9679</Real>
+    <Real Name="Time 0.091000 Step 91 in frame 91">-1488.7239</Real>
+    <Real Name="Time 0.092000 Step 92 in frame 92">-1486.9701</Real>
+    <Real Name="Time 0.093000 Step 93 in frame 93">-1486.0492</Real>
+    <Real Name="Time 0.094000 Step 94 in frame 94">-1485.6787</Real>
+    <Real Name="Time 0.095000 Step 95 in frame 95">-1485.4366</Real>
+    <Real Name="Time 0.096000 Step 96 in frame 96">-1486.4124</Real>
+    <Real Name="Time 0.097000 Step 97 in frame 97">-1485.5287</Real>
+    <Real Name="Time 0.098000 Step 98 in frame 98">-1484.5422</Real>
+    <Real Name="Time 0.099000 Step 99 in frame 99">-1483.8816</Real>
+    <Real Name="Time 0.100000 Step 100 in frame 100">-1484.0251</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">634.19263</Real>
+      <Real Name="Y">-774.12708</Real>
+      <Real Name="Z">-84.303528</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-173.31541</Real>
+      <Real Name="Y">-254.0132</Real>
+      <Real Name="Z">32.148266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-460.15817</Real>
+      <Real Name="Y">273.38</Real>
+      <Real Name="Z">-218.39685</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.14142</Real>
+      <Real Name="Y">188.88895</Real>
+      <Real Name="Z">21.105955</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">746.15997</Real>
+      <Real Name="Y">612.76343</Real>
+      <Real Name="Z">-42.128273</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-24.29751</Real>
+      <Real Name="Y">422.16827</Real>
+      <Real Name="Z">-169.40718</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-869.57709</Real>
+      <Real Name="Y">288.51993</Real>
+      <Real Name="Z">1096.8831</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-40.530876</Real>
+      <Real Name="Y">-295.73813</Real>
+      <Real Name="Z">-453.5372</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">275.31131</Real>
+      <Real Name="Y">-437.97052</Real>
+      <Real Name="Z">-229.88564</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">10.925804</Real>
+      <Real Name="Y">-30.976189</Real>
+      <Real Name="Z">20.380272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.0141983</Real>
+      <Real Name="Y">27.856314</Real>
+      <Real Name="Z">-12.695366</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.2674408</Real>
+      <Real Name="Y">14.475534</Real>
+      <Real Name="Z">-4.0459976</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.22610474</Real>
+      <Real Name="Y">23.698967</Real>
+      <Real Name="Z">-7.1104736</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.8458405</Real>
+      <Real Name="Y">-9.5181484</Real>
+      <Real Name="Z">4.7690716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.3456688</Real>
+      <Real Name="Y">-24.903942</Real>
+      <Real Name="Z">4.5986137</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.96179</Real>
+      <Real Name="Y">-59.604706</Real>
+      <Real Name="Z">-670.1571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.81146</Real>
+      <Real Name="Y">36.204243</Real>
+      <Real Name="Z">385.08865</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.8211288</Real>
+      <Real Name="Y">-11.299082</Real>
+      <Real Name="Z">206.28496</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">394.23376</Real>
+      <Real Name="Y">432.20389</Real>
+      <Real Name="Z">-97.832077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.851814</Real>
+      <Real Name="Y">-238.51105</Real>
+      <Real Name="Z">85.78244</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-370.92975</Real>
+      <Real Name="Y">-600.14075</Real>
+      <Real Name="Z">242.79077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">643.12964</Real>
+      <Real Name="Y">-492.27518</Real>
+      <Real Name="Z">-420.98615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.97176</Real>
+      <Real Name="Y">121.35711</Real>
+      <Real Name="Z">75.023636</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.06006</Real>
+      <Real Name="Y">438.33389</Real>
+      <Real Name="Z">218.99048</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.01516</Real>
+      <Real Name="Y">295.75125</Real>
+      <Real Name="Z">69.898956</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.252739</Real>
+      <Real Name="Y">-163.91307</Real>
+      <Real Name="Z">-112.35473</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.12119</Real>
+      <Real Name="Y">-215.1304</Real>
+      <Real Name="Z">-140.17712</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-591.88873</Real>
+      <Real Name="Y">297.67572</Real>
+      <Real Name="Z">826.32703</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">397.93842</Real>
+      <Real Name="Y">-70.315125</Real>
+      <Real Name="Z">-868.24725</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.87331</Real>
+      <Real Name="Y">-199.27788</Real>
+      <Real Name="Z">-58.264145</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1115.2428</Real>
+      <Real Name="Y">630.13123</Real>
+      <Real Name="Z">795.2749</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.5452</Real>
+      <Real Name="Y">-873.28632</Real>
+      <Real Name="Z">-339.25296</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-870.47675</Real>
+      <Real Name="Y">286.55475</Real>
+      <Real Name="Z">-544.52521</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">357.06409</Real>
+      <Real Name="Y">756.95288</Real>
+      <Real Name="Z">146.15199</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.1041641</Real>
+      <Real Name="Y">-651.95844</Real>
+      <Real Name="Z">-54.851482</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-90.345825</Real>
+      <Real Name="Y">-131.84966</Real>
+      <Real Name="Z">-37.247372</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.522749</Real>
+      <Real Name="Y">-138.26599</Real>
+      <Real Name="Z">-868.80994</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.01487</Real>
+      <Real Name="Y">7.3681002</Real>
+      <Real Name="Z">237.56253</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">409.19614</Real>
+      <Real Name="Y">-13.744324</Real>
+      <Real Name="Z">674.30859</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.658508</Real>
+      <Real Name="Y">50.113556</Real>
+      <Real Name="Z">-863.78394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-246.26122</Real>
+      <Real Name="Y">161.93843</Real>
+      <Real Name="Z">488.15155</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">20.568638</Real>
+      <Real Name="Y">-198.69077</Real>
+      <Real Name="Z">167.75015</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-614.59326</Real>
+      <Real Name="Y">104.52963</Real>
+      <Real Name="Z">354.02295</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">537.62415</Real>
+      <Real Name="Y">-78.613785</Real>
+      <Real Name="Z">-4.2482901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.13351</Real>
+      <Real Name="Y">-43.662785</Real>
+      <Real Name="Z">-245.23721</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-106.57373</Real>
+      <Real Name="Y">29.633194</Real>
+      <Real Name="Z">363.0675</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">246.89545</Real>
+      <Real Name="Y">-1.2883682</Real>
+      <Real Name="Z">-127.71266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">59.181488</Real>
+      <Real Name="Y">-111.70288</Real>
+      <Real Name="Z">-115.61073</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-356.38644</Real>
+      <Real Name="Y">-2083.3274</Real>
+      <Real Name="Z">-695.1687</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">338.5383</Real>
+      <Real Name="Y">1335.4231</Real>
+      <Real Name="Z">310.34689</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">276.91052</Real>
+      <Real Name="Y">426.71542</Real>
+      <Real Name="Z">68.866402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">408.59476</Real>
+      <Real Name="Y">979.02332</Real>
+      <Real Name="Z">-851.52771</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-141.87756</Real>
+      <Real Name="Y">-531.63434</Real>
+      <Real Name="Z">230.45869</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-250.61594</Real>
+      <Real Name="Y">-386.77625</Real>
+      <Real Name="Z">568.34503</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.995796</Real>
+      <Real Name="Y">98.713974</Real>
+      <Real Name="Z">5.9983025</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.455554</Real>
+      <Real Name="Y">-43.458389</Real>
+      <Real Name="Z">-3.6231194</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.239048</Real>
+      <Real Name="Y">-33.183907</Real>
+      <Real Name="Z">8.2902775</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-423.87347</Real>
+      <Real Name="Y">988.98425</Real>
+      <Real Name="Z">389.59784</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">598.97217</Real>
+      <Real Name="Y">-698.11646</Real>
+      <Real Name="Z">-450.10861</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.343487</Real>
+      <Real Name="Y">-160.39758</Real>
+      <Real Name="Z">-11.909855</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.3821</Real>
+      <Real Name="Y">-502.43747</Real>
+      <Real Name="Z">-1442.8588</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.964081</Real>
+      <Real Name="Y">370.46133</Real>
+      <Real Name="Z">1053.1552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-283.37589</Real>
+      <Real Name="Y">14.029888</Real>
+      <Real Name="Z">269.58932</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.54297</Real>
+      <Real Name="Y">-124.31937</Real>
+      <Real Name="Z">-117.58147</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-85.880951</Real>
+      <Real Name="Y">126.24585</Real>
+      <Real Name="Z">72.835114</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.938107</Real>
+      <Real Name="Y">40.618141</Real>
+      <Real Name="Z">150.70537</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.1973</Real>
+      <Real Name="Y">-338.35455</Real>
+      <Real Name="Z">-76.468491</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.71738</Real>
+      <Real Name="Y">414.147</Real>
+      <Real Name="Z">98.183876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.25122</Real>
+      <Real Name="Y">67.408051</Real>
+      <Real Name="Z">41.300266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">724.84436</Real>
+      <Real Name="Y">-281.00427</Real>
+      <Real Name="Z">-579.81964</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-344.99899</Real>
+      <Real Name="Y">205.72141</Real>
+      <Real Name="Z">640.11542</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-247.02272</Real>
+      <Real Name="Y">324.86865</Real>
+      <Real Name="Z">148.15186</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.68858337</Real>
+      <Real Name="Y">-10.919106</Real>
+      <Real Name="Z">9.5581284</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.5278492</Real>
+      <Real Name="Y">6.9944229</Real>
+      <Real Name="Z">-2.9469452</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.7969704</Real>
+      <Real Name="Y">4.9394989</Real>
+      <Real Name="Z">-7.0647907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-407.17618</Real>
+      <Real Name="Y">846.323</Real>
+      <Real Name="Z">253.05203</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">291.08258</Real>
+      <Real Name="Y">-480.01971</Real>
+      <Real Name="Z">-680.65302</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.95673</Real>
+      <Real Name="Y">-441.6496</Real>
+      <Real Name="Z">290.35007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.52753</Real>
+      <Real Name="Y">32.474689</Real>
+      <Real Name="Z">-151.86365</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.64911</Real>
+      <Real Name="Y">57.971031</Real>
+      <Real Name="Z">40.010414</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.314713</Real>
+      <Real Name="Y">-39.183544</Real>
+      <Real Name="Z">74.100586</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-596.40063</Real>
+      <Real Name="Y">-1292.3043</Real>
+      <Real Name="Z">-304.08658</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">218.88113</Real>
+      <Real Name="Y">615.97302</Real>
+      <Real Name="Z">-293.4668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">393.9408</Real>
+      <Real Name="Y">650.36456</Real>
+      <Real Name="Z">617.01917</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1193.564</Real>
+      <Real Name="Y">443.09839</Real>
+      <Real Name="Z">-792.07574</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">819.54901</Real>
+      <Real Name="Y">-311.52759</Real>
+      <Real Name="Z">572.28064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.21558</Real>
+      <Real Name="Y">-31.884295</Real>
+      <Real Name="Z">38.946358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">216.15796</Real>
+      <Real Name="Y">430.48004</Real>
+      <Real Name="Z">164.33713</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.185656</Real>
+      <Real Name="Y">-134.87021</Real>
+      <Real Name="Z">-70.851448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-108.02281</Real>
+      <Real Name="Y">-124.21425</Real>
+      <Real Name="Z">-17.181404</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.2062</Real>
+      <Real Name="Y">-1074.0594</Real>
+      <Real Name="Z">-860.1192</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.09605</Real>
+      <Real Name="Y">209.2016</Real>
+      <Real Name="Z">196.16069</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">575.92712</Real>
+      <Real Name="Y">691.03589</Real>
+      <Real Name="Z">682.34711</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.87489</Real>
+      <Real Name="Y">390.16568</Real>
+      <Real Name="Z">-1022.8798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.947098</Real>
+      <Real Name="Y">-318.27917</Real>
+      <Real Name="Z">753.25494</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.99823</Real>
+      <Real Name="Y">32.162571</Real>
+      <Real Name="Z">254.53839</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.83133</Real>
+      <Real Name="Y">-511.42389</Real>
+      <Real Name="Z">-930.75775</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.26678</Real>
+      <Real Name="Y">79.126808</Real>
+      <Real Name="Z">146.98911</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.370152</Real>
+      <Real Name="Y">256.5076</Real>
+      <Real Name="Z">804.46674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-438.49103</Real>
+      <Real Name="Y">-223.29904</Real>
+      <Real Name="Z">-199.42458</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">224.94597</Real>
+      <Real Name="Y">60.396034</Real>
+      <Real Name="Z">145.17058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">225.78027</Real>
+      <Real Name="Y">77.065506</Real>
+      <Real Name="Z">123.62689</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">568.84772</Real>
+      <Real Name="Y">813.66687</Real>
+      <Real Name="Z">-260.69568</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.33185</Real>
+      <Real Name="Y">-198.43544</Real>
+      <Real Name="Z">15.699821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.9657</Real>
+      <Real Name="Y">-861.01874</Real>
+      <Real Name="Z">-32.847767</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1744.7604</Real>
+      <Real Name="Y">-146.67123</Real>
+      <Real Name="Z">947.77057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-863.39832</Real>
+      <Real Name="Y">303.07144</Real>
+      <Real Name="Z">-704.97058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.04962</Real>
+      <Real Name="Y">-176.10233</Real>
+      <Real Name="Z">-382.00201</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-153.51324</Real>
+      <Real Name="Y">55.68367</Real>
+      <Real Name="Z">125.95101</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.0448</Real>
+      <Real Name="Y">-162.75539</Real>
+      <Real Name="Z">29.864437</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.87985</Real>
+      <Real Name="Y">-7.5582418</Real>
+      <Real Name="Z">25.019432</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">688.69794</Real>
+      <Real Name="Y">-329.95221</Real>
+      <Real Name="Z">47.698727</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-478.77271</Real>
+      <Real Name="Y">292.2962</Real>
+      <Real Name="Z">25.484558</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.64474</Real>
+      <Real Name="Y">328.12592</Real>
+      <Real Name="Z">-128.69678</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.71068</Real>
+      <Real Name="Y">-1811.1239</Real>
+      <Real Name="Z">50.012383</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">128.98105</Real>
+      <Real Name="Y">237.25067</Real>
+      <Real Name="Z">-41.168339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.38527</Real>
+      <Real Name="Y">1224.5275</Real>
+      <Real Name="Z">-63.084476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.39221</Real>
+      <Real Name="Y">1028.9681</Real>
+      <Real Name="Z">825.64862</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.67789</Real>
+      <Real Name="Y">-646.07843</Real>
+      <Real Name="Z">-555.45239</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.187481</Real>
+      <Real Name="Y">-238.70564</Real>
+      <Real Name="Z">-216.71841</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.875359</Real>
+      <Real Name="Y">-445.92899</Real>
+      <Real Name="Z">-491.63687</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-158.08191</Real>
+      <Real Name="Y">376.0954</Real>
+      <Real Name="Z">254.38142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-93.62793</Real>
+      <Real Name="Y">161.07248</Real>
+      <Real Name="Z">477.57007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">846.57996</Real>
+      <Real Name="Y">-283.94788</Real>
+      <Real Name="Z">-279.26974</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-199.05646</Real>
+      <Real Name="Y">452.46191</Real>
+      <Real Name="Z">22.097107</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-761.22772</Real>
+      <Real Name="Y">-163.09402</Real>
+      <Real Name="Z">188.54041</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1087.0238</Real>
+      <Real Name="Y">1521.0636</Real>
+      <Real Name="Z">-673.28796</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.66171</Real>
+      <Real Name="Y">-219.61053</Real>
+      <Real Name="Z">95.491249</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">620.03485</Real>
+      <Real Name="Y">-974.03101</Real>
+      <Real Name="Z">441.56821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.216</Real>
+      <Real Name="Y">29.393684</Real>
+      <Real Name="Z">149.5282</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.6862</Real>
+      <Real Name="Y">92.882713</Real>
+      <Real Name="Z">-36.452312</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-914.89764</Real>
+      <Real Name="Y">119.96918</Real>
+      <Real Name="Z">-128.94379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-822.57422</Real>
+      <Real Name="Y">-1221.0959</Real>
+      <Real Name="Z">114.10788</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">707.68378</Real>
+      <Real Name="Y">941.33618</Real>
+      <Real Name="Z">60.082314</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">68.118355</Real>
+      <Real Name="Y">251.30519</Real>
+      <Real Name="Z">-76.473816</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-136.65074</Real>
+      <Real Name="Y">-697.22198</Real>
+      <Real Name="Z">809.18854</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.8913</Real>
+      <Real Name="Y">509.84113</Real>
+      <Real Name="Z">-581.41943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.67886</Real>
+      <Real Name="Y">79.15834</Real>
+      <Real Name="Z">-119.01872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">69.135803</Real>
+      <Real Name="Y">-148.68272</Real>
+      <Real Name="Z">341.82617</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.626726</Real>
+      <Real Name="Y">255.32982</Real>
+      <Real Name="Z">-74.753937</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.06201</Real>
+      <Real Name="Y">369.95981</Real>
+      <Real Name="Z">26.579353</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">82.820259</Real>
+      <Real Name="Y">465.88309</Real>
+      <Real Name="Z">-66.908447</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.497044</Real>
+      <Real Name="Y">-170.16011</Real>
+      <Real Name="Z">64.423462</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.73671</Real>
+      <Real Name="Y">-177.85536</Real>
+      <Real Name="Z">21.967014</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.768799</Real>
+      <Real Name="Y">766.97827</Real>
+      <Real Name="Z">75.479279</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.588104</Real>
+      <Real Name="Y">-166.13193</Real>
+      <Real Name="Z">-66.487488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.568993</Real>
+      <Real Name="Y">-724.08154</Real>
+      <Real Name="Z">-12.754246</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.510574</Real>
+      <Real Name="Y">-723.00421</Real>
+      <Real Name="Z">-210.45476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.88062</Real>
+      <Real Name="Y">626.18372</Real>
+      <Real Name="Z">111.89783</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.519997</Real>
+      <Real Name="Y">176.58563</Real>
+      <Real Name="Z">44.482582</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">101.52298</Real>
+      <Real Name="Y">789.96918</Real>
+      <Real Name="Z">537.76044</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.387653</Real>
+      <Real Name="Y">-180.10947</Real>
+      <Real Name="Z">-82.72142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">139.17728</Real>
+      <Real Name="Y">-585.65979</Real>
+      <Real Name="Z">-443.16635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.18738</Real>
+      <Real Name="Y">299.44061</Real>
+      <Real Name="Z">-420.08798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">164.55542</Real>
+      <Real Name="Y">-252.1886</Real>
+      <Real Name="Z">436.97571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.03229</Real>
+      <Real Name="Y">-111.08784</Real>
+      <Real Name="Z">92.585892</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-885.46423</Real>
+      <Real Name="Y">-83.73024</Real>
+      <Real Name="Z">-57.06691</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.81207</Real>
+      <Real Name="Y">-69.788956</Real>
+      <Real Name="Z">3.6640511</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">312.90143</Real>
+      <Real Name="Y">42.177246</Real>
+      <Real Name="Z">7.3131104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1533.2124</Real>
+      <Real Name="Y">-268.10193</Real>
+      <Real Name="Z">-639.90228</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">298.84668</Real>
+      <Real Name="Y">-41.605171</Real>
+      <Real Name="Z">203.82086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1071.9005</Real>
+      <Real Name="Y">337.55316</Real>
+      <Real Name="Z">324.01285</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-414.60535</Real>
+      <Real Name="Y">1396.6963</Real>
+      <Real Name="Z">326.30008</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.269663</Real>
+      <Real Name="Y">-723.13293</Real>
+      <Real Name="Z">-590.00214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">383.87311</Real>
+      <Real Name="Y">-438.32825</Real>
+      <Real Name="Z">485.93811</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.20433</Real>
+      <Real Name="Y">440.55023</Real>
+      <Real Name="Z">126.63379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.497604</Real>
+      <Real Name="Y">-125.11058</Real>
+      <Real Name="Z">-82.321968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-223.47771</Real>
+      <Real Name="Y">-496.10773</Real>
+      <Real Name="Z">-108.89548</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1156.0057</Real>
+      <Real Name="Y">-1972.0508</Real>
+      <Real Name="Z">678.15936</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-122.95121</Real>
+      <Real Name="Y">1194.1389</Real>
+      <Real Name="Z">314.51654</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1030.7716</Real>
+      <Real Name="Y">372.63919</Real>
+      <Real Name="Z">-811.24615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">776.87927</Real>
+      <Real Name="Y">-1294.7695</Real>
+      <Real Name="Z">-289.79825</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-239.76584</Real>
+      <Real Name="Y">679.80609</Real>
+      <Real Name="Z">20.613815</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.59177</Real>
+      <Real Name="Y">693.43573</Real>
+      <Real Name="Z">319.41367</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">85.945656</Real>
+      <Real Name="Y">843.07758</Real>
+      <Real Name="Z">1349.7853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-105.1994</Real>
+      <Real Name="Y">-723.44421</Real>
+      <Real Name="Z">-1015.3097</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.93722</Real>
+      <Real Name="Y">7.4774971</Real>
+      <Real Name="Z">-260.68109</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_d.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_d.xml
new file mode 100644 (file)
index 0000000..18f3fd1
--- /dev/null
@@ -0,0 +1,1895 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVvdw/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-2.0825785243975439</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1.6903350438301135</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1.7480902443642317</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1.7949576248266288</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1.8261076167896624</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-2.2802515417416735</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1.8226240081408676</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1.782958218895812</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1.718884047768984</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1.6339280410837249</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-2.1007565042945711</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1.4226353979555975</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1.3086920210366906</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1.1978530595382113</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1.0959291434773475</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1.6449424356933131</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-0.93451200521814615</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-0.87796452736252129</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-0.8368587075915932</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-0.80984754328910979</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1.2692007990009915</Real>
+  </Energy>
+  <Energy Name="dVcoul/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">127.00456344643993</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">16.051558265889085</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">15.600471116560897</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">14.815487247001933</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">13.720242520953157</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">124.29227237903152</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">10.928317580067471</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">9.4519046985734292</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">8.0455454578104053</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">6.7523265839446012</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">119.38007293785503</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">4.4653090950911718</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">3.3886895186156636</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">2.3035378941684854</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">1.2038086563460979</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">115.12522481633846</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-0.88180765037721187</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1.7516126457092298</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-2.4643984630022189</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-3.0484358084132523</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">110.83970023529346</Real>
+  </Energy>
+  <Energy Name="dVremain/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">0</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">0</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">0</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">0</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">0</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">0</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">0</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">0</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">0</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">0</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">0</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">0</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">0</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">0</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">0</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">0</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">0</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">0</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">0</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">0</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">0</Real>
+  </Energy>
+  <Energy Name="dVbonded/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">46.033102414228267</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">0</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">0</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">0</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">0</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">6.3034853294409015</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">0</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">0</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">0</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">0</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-19.702625156671665</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">0</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">0</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">0</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">0</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-5.8027143642560448</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">0</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">0</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">0</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">0</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">23.934031805402221</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1400.7032621815663</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1405.4268584530507</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1409.056731368983</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1409.31984492843</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1405.1779492168014</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1397.2235052817937</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1387.2512612844826</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1377.4647473200346</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1369.7866938330969</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1365.3970510985405</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1364.4625010242635</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1366.0557367864949</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1368.3214845987518</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1368.9662888355783</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1366.0704830833149</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1359.0449840477579</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1349.2495819933795</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1339.7117534698345</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1333.9092625315723</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1334.2699604762242</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1341.1846170576828</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">424.19684856815138</Real>
+      <Real Name="Y">-929.00726905313013</Real>
+      <Real Name="Z">-218.01120050086291</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.90810581599254</Real>
+      <Real Name="Y">-268.18526514554134</Real>
+      <Real Name="Z">74.184229386412198</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-413.10808595746948</Real>
+      <Real Name="Y">284.20241726033896</Real>
+      <Real Name="Z">-165.78360428727649</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.380316841178541</Real>
+      <Real Name="Y">151.11454904033255</Real>
+      <Real Name="Z">152.46095611150241</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3730.4391169382088</Real>
+      <Real Name="Y">1969.7450412386809</Real>
+      <Real Name="Z">1181.4597869117197</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-351.27826221242674</Real>
+      <Real Name="Y">-121.78796653490272</Real>
+      <Real Name="Z">-418.010686411623</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4348.0147838020603</Real>
+      <Real Name="Y">-269.26091767937567</Real>
+      <Real Name="Z">85.655347338999974</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.24182382578573</Real>
+      <Real Name="Y">-475.64400887945072</Real>
+      <Real Name="Z">-614.24065373875521</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.90726597295122</Real>
+      <Real Name="Y">-579.73797668186774</Real>
+      <Real Name="Z">-237.68723098352743</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.2447628260391923</Real>
+      <Real Name="Y">-34.354563171569467</Real>
+      <Real Name="Z">23.923242116812929</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.4556090303903773</Real>
+      <Real Name="Y">29.482908810355923</Real>
+      <Real Name="Z">-14.059219652982144</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.5511925213670139</Real>
+      <Real Name="Y">16.100396534059932</Real>
+      <Real Name="Z">-5.9521652223676682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.7416155685928061</Real>
+      <Real Name="Y">20.842710307485945</Real>
+      <Real Name="Z">-6.4879131948126627</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.0200925732223425</Real>
+      <Real Name="Y">-8.3616783275454338</Real>
+      <Real Name="Z">4.3327439903187681</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.7705151354203394</Real>
+      <Real Name="Y">-23.385022034296831</Real>
+      <Real Name="Z">3.9191724313506882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">362.79405127356711</Real>
+      <Real Name="Y">-56.651787981708068</Real>
+      <Real Name="Z">-669.74037581615016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.68496463907741</Real>
+      <Real Name="Y">34.760023569827311</Real>
+      <Real Name="Z">384.98731691865208</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.0339906280499349</Real>
+      <Real Name="Y">-12.771758422265586</Real>
+      <Real Name="Z">205.99928050592615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">410.18919743092351</Real>
+      <Real Name="Y">464.39308541245958</Real>
+      <Real Name="Z">-110.42778660706271</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-59.870294516685711</Real>
+      <Real Name="Y">-251.50404475257753</Real>
+      <Real Name="Z">88.595929586063733</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-384.36361458856851</Real>
+      <Real Name="Y">-626.99968675518107</Real>
+      <Real Name="Z">249.60489373750028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">643.00085757044963</Real>
+      <Real Name="Y">-494.1671234949174</Real>
+      <Real Name="Z">-430.29467917723855</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.95393995315841</Real>
+      <Real Name="Y">122.95177076793873</Real>
+      <Real Name="Z">78.581570752473283</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.27073209181242</Real>
+      <Real Name="Y">440.16686592720157</Real>
+      <Real Name="Z">225.4059601241334</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.540738189435849</Real>
+      <Real Name="Y">305.03283002810321</Real>
+      <Real Name="Z">57.952823537500961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.081165963338634</Real>
+      <Real Name="Y">-168.25285262437063</Real>
+      <Real Name="Z">-107.57028192738989</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.5082850525139</Real>
+      <Real Name="Y">-218.75112331215394</Real>
+      <Real Name="Z">-135.96180174676019</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-606.1112738798181</Real>
+      <Real Name="Y">246.92795466341244</Real>
+      <Real Name="Z">825.49098709183227</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">403.71066109101213</Real>
+      <Real Name="Y">-44.356760448265703</Real>
+      <Real Name="Z">-860.78653799471283</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.75993991038928</Real>
+      <Real Name="Y">-180.96945982171042</Real>
+      <Real Name="Z">-58.708855998803386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1061.3410359444586</Real>
+      <Real Name="Y">551.21306372134325</Real>
+      <Real Name="Z">854.93370677378664</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-147.25706914526353</Real>
+      <Real Name="Y">-835.59019962922912</Real>
+      <Real Name="Z">-355.07345008005399</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-825.13008341259297</Real>
+      <Real Name="Y">327.5701152466101</Real>
+      <Real Name="Z">-570.35084572162748</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">350.70113505010289</Real>
+      <Real Name="Y">765.85583822497597</Real>
+      <Real Name="Z">138.52388162381308</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.65034853492583977</Real>
+      <Real Name="Y">-658.98113340606915</Real>
+      <Real Name="Z">-49.809148607512697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-87.496455254130865</Real>
+      <Real Name="Y">-135.70781929598905</Real>
+      <Real Name="Z">-34.035094773570442</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.767410865901866</Real>
+      <Real Name="Y">-120.23454148042386</Real>
+      <Real Name="Z">-943.27208631060637</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-78.316406982433108</Real>
+      <Real Name="Y">8.0977940854804231</Real>
+      <Real Name="Z">298.2366570988263</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">406.40822013211914</Real>
+      <Real Name="Y">-11.194958115070911</Real>
+      <Real Name="Z">705.65362098997366</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">108.87698446526662</Real>
+      <Real Name="Y">24.122077588311981</Real>
+      <Real Name="Z">-893.57139210304058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-260.11279189321982</Real>
+      <Real Name="Y">182.00899241013269</Real>
+      <Real Name="Z">499.39877059892035</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.558167844940527</Real>
+      <Real Name="Y">-188.92580237690495</Real>
+      <Real Name="Z">177.98302409422152</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-642.75740137040509</Real>
+      <Real Name="Y">79.996909922832259</Real>
+      <Real Name="Z">346.34769112124985</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">550.25546048566025</Real>
+      <Real Name="Y">-61.96311190296538</Real>
+      <Real Name="Z">-0.98826774934605766</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.91330584071713</Real>
+      <Real Name="Y">-34.979268311175524</Real>
+      <Real Name="Z">-237.92103827809831</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.27409597412958</Real>
+      <Real Name="Y">43.529886247595869</Real>
+      <Real Name="Z">359.45278140061663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">252.78075096108063</Real>
+      <Real Name="Y">-6.6674550292574679</Real>
+      <Real Name="Z">-124.66428260696625</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">63.506680119640137</Real>
+      <Real Name="Y">-117.28537902122117</Real>
+      <Real Name="Z">-113.51317444844173</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-308.457354431319</Real>
+      <Real Name="Y">-2131.4122221049765</Real>
+      <Real Name="Z">-637.20899097464621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">316.44193351944102</Real>
+      <Real Name="Y">1351.8123254040033</Real>
+      <Real Name="Z">272.17867017989141</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">250.45922870637332</Real>
+      <Real Name="Y">446.76581743163604</Real>
+      <Real Name="Z">49.137792411231629</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">456.77070840227879</Real>
+      <Real Name="Y">934.49195803767554</Real>
+      <Real Name="Z">-928.82374869197622</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.39957402642045</Real>
+      <Real Name="Y">-518.15260359964714</Real>
+      <Real Name="Z">256.07834536877596</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-291.35917399309511</Real>
+      <Real Name="Y">-339.49770128781756</Real>
+      <Real Name="Z">612.54168751954614</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.380949144522347</Real>
+      <Real Name="Y">99.348816469164433</Real>
+      <Real Name="Z">5.847899188033189</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.270093355213056</Real>
+      <Real Name="Y">-43.418768488814557</Real>
+      <Real Name="Z">-3.8916660475475755</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.805838199515168</Real>
+      <Real Name="Y">-33.153839743194069</Real>
+      <Real Name="Z">8.3257441518301114</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-451.38204623616565</Real>
+      <Real Name="Y">981.10170371540289</Real>
+      <Real Name="Z">389.99657695747231</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">613.29738036411766</Real>
+      <Real Name="Y">-691.22705330412282</Real>
+      <Real Name="Z">-449.54552227858312</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">41.709003659813689</Real>
+      <Real Name="Y">-156.46160198960365</Real>
+      <Real Name="Z">-14.127097601929506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">233.46250033200772</Real>
+      <Real Name="Y">-619.36534590542806</Real>
+      <Real Name="Z">-1448.7869052251717</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-82.947922148473907</Real>
+      <Real Name="Y">421.25102542244804</Real>
+      <Real Name="Z">1032.102707600716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-339.18607005657185</Real>
+      <Real Name="Y">107.47339882204822</Real>
+      <Real Name="Z">276.15360187778771</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.72922635668436</Real>
+      <Real Name="Y">-120.86280838095773</Real>
+      <Real Name="Z">-118.21985564016867</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-84.896833440254795</Real>
+      <Real Name="Y">123.81025794967974</Real>
+      <Real Name="Z">73.019353066886509</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.065192384321456</Real>
+      <Real Name="Y">39.107722537996608</Real>
+      <Real Name="Z">151.49538861465032</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">323.79285527327283</Real>
+      <Real Name="Y">-346.6108337302548</Real>
+      <Real Name="Z">-65.820365852436865</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-286.57569205713514</Real>
+      <Real Name="Y">418.98269539756251</Real>
+      <Real Name="Z">92.445113237226721</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.09483685577648</Real>
+      <Real Name="Y">70.806510800847263</Real>
+      <Real Name="Z">37.011821088061794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.57668801538352</Real>
+      <Real Name="Y">-854.87084314715707</Real>
+      <Real Name="Z">-613.39804468355771</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-324.74211639629254</Real>
+      <Real Name="Y">337.76010468219943</Real>
+      <Real Name="Z">608.74908028654568</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-337.20211222456311</Real>
+      <Real Name="Y">857.46719179197703</Real>
+      <Real Name="Z">242.82504709201382</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.000377307977104</Real>
+      <Real Name="Y">-4.4256625029919121</Real>
+      <Real Name="Z">16.162606564777086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.37001165777232</Real>
+      <Real Name="Y">4.8394040329845751</Real>
+      <Real Name="Z">-6.438760952042319</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.3211847099196135</Real>
+      <Real Name="Y">1.222325092845459</Real>
+      <Real Name="Z">-11.723038460027716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-445.03804005968408</Real>
+      <Real Name="Y">891.17165044286901</Real>
+      <Real Name="Z">239.04052787006057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">303.12734666751106</Real>
+      <Real Name="Y">-493.37318294793567</Real>
+      <Real Name="Z">-675.4239376294197</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.41891929244048</Real>
+      <Real Name="Y">-463.4326675580067</Real>
+      <Real Name="Z">304.97795409054243</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.65865193857229</Real>
+      <Real Name="Y">26.948273693470444</Real>
+      <Real Name="Z">-153.64102511042404</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-270.52435370655292</Real>
+      <Real Name="Y">60.771283043094982</Real>
+      <Real Name="Z">40.797944272297826</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-89.682305331183414</Real>
+      <Real Name="Y">-36.206375667604718</Real>
+      <Real Name="Z">75.430476645232545</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-651.49261589840421</Real>
+      <Real Name="Y">-1244.5880121244745</Real>
+      <Real Name="Z">-370.9975493446546</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.34317322337648</Real>
+      <Real Name="Y">609.01515469312858</Real>
+      <Real Name="Z">-251.63504642235392</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">414.07294202227013</Real>
+      <Real Name="Y">623.66224796670417</Real>
+      <Real Name="Z">628.4146985393761</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1188.7782766895791</Real>
+      <Real Name="Y">442.66975188748518</Real>
+      <Real Name="Z">-788.13699520554781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">817.78121866915455</Real>
+      <Real Name="Y">-311.03109747395752</Real>
+      <Real Name="Z">570.49508996447378</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.59108353831945</Real>
+      <Real Name="Y">-31.578633229504096</Real>
+      <Real Name="Z">36.63298544887293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">220.58657645430793</Real>
+      <Real Name="Y">411.63140530576811</Real>
+      <Real Name="Z">169.62712548380605</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.005389715929113</Real>
+      <Real Name="Y">-124.08432646845449</Real>
+      <Real Name="Z">-74.87217741029535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.66292720647944</Real>
+      <Real Name="Y">-114.64649927372922</Real>
+      <Real Name="Z">-20.036692205085409</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1048.8613376512781</Real>
+      <Real Name="Y">-1080.8731758645481</Real>
+      <Real Name="Z">-855.74512802247762</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.96553639814115</Real>
+      <Real Name="Y">212.71673618843263</Real>
+      <Real Name="Z">194.14800984596781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">577.57604770643559</Real>
+      <Real Name="Y">694.10472519592429</Real>
+      <Real Name="Z">680.54790921091296</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">195.12435752387609</Real>
+      <Real Name="Y">385.9102636952079</Real>
+      <Real Name="Z">-1022.7348792774148</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-98.501586888545091</Real>
+      <Real Name="Y">-315.8871917506425</Real>
+      <Real Name="Z">753.47953455494269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">57.215662141325573</Real>
+      <Real Name="Y">33.820335502158557</Real>
+      <Real Name="Z">254.47943806207562</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-275.425409704434</Real>
+      <Real Name="Y">-516.3349057487352</Real>
+      <Real Name="Z">-928.43422060161561</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">242.38329141163095</Real>
+      <Real Name="Y">81.441306986459239</Real>
+      <Real Name="Z">145.61677591660521</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.998187146089627</Real>
+      <Real Name="Y">258.66338696394973</Real>
+      <Real Name="Z">803.69161856440405</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-525.26166591540584</Real>
+      <Real Name="Y">-79.292331604673336</Real>
+      <Real Name="Z">-276.45941592529942</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">348.96275464285776</Real>
+      <Real Name="Y">-123.85874827898164</Real>
+      <Real Name="Z">198.63411948265446</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">256.39181137072762</Real>
+      <Real Name="Y">24.935123020089733</Real>
+      <Real Name="Z">148.64264036009445</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">549.39895276821119</Real>
+      <Real Name="Y">835.38124964672352</Real>
+      <Real Name="Z">-204.43879341418244</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-110.6453532834593</Real>
+      <Real Name="Y">-205.47015987913537</Real>
+      <Real Name="Z">-3.5253763870676096</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-438.21716503461164</Real>
+      <Real Name="Y">-867.13565400951927</Real>
+      <Real Name="Z">-68.387516102561563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1869.5232150705031</Real>
+      <Real Name="Y">-4.4329975376995776</Real>
+      <Real Name="Z">1000.0944804243272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-908.02994365244649</Real>
+      <Real Name="Y">182.1013641024166</Real>
+      <Real Name="Z">-744.57463679419095</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-436.42417071133292</Real>
+      <Real Name="Y">-250.97298223307163</Real>
+      <Real Name="Z">-376.80618281141119</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-151.76968523507779</Real>
+      <Real Name="Y">53.74377449438478</Real>
+      <Real Name="Z">129.17881816240291</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">162.30023001634703</Real>
+      <Real Name="Y">-161.22159435762234</Real>
+      <Real Name="Z">27.985001877323207</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.45821770014555</Real>
+      <Real Name="Y">-6.8146746388818027</Real>
+      <Real Name="Z">23.68396939181941</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">582.84686710530582</Real>
+      <Real Name="Y">-25.888942832811324</Real>
+      <Real Name="Z">-30.495140606351129</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-383.22635769171166</Real>
+      <Real Name="Y">184.49482980575152</Real>
+      <Real Name="Z">75.111995147161167</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-337.39917617800802</Real>
+      <Real Name="Y">287.73026815880996</Real>
+      <Real Name="Z">-52.398994882027907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.89638419117563</Real>
+      <Real Name="Y">-1787.40231393166</Real>
+      <Real Name="Z">53.697139856259966</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">126.93610545737448</Real>
+      <Real Name="Y">225.80882774901949</Real>
+      <Real Name="Z">-43.969257595254525</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.09735498073539</Real>
+      <Real Name="Y">1215.9065287753651</Real>
+      <Real Name="Z">-63.037282716512557</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">394.48057406765236</Real>
+      <Real Name="Y">1012.8258413748235</Real>
+      <Real Name="Z">893.48719999149159</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.42060934974461</Real>
+      <Real Name="Y">-626.96282770412938</Real>
+      <Real Name="Z">-600.47294606274943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.085720892501243</Real>
+      <Real Name="Y">-231.14744896068029</Real>
+      <Real Name="Z">-239.21674460465138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-212.9424065834817</Real>
+      <Real Name="Y">-279.55854821385174</Real>
+      <Real Name="Z">-324.83684409986478</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-70.590974246673667</Real>
+      <Real Name="Y">287.35314512922878</Real>
+      <Real Name="Z">254.84435712120518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-60.532722490709524</Real>
+      <Real Name="Y">120.69536739039116</Real>
+      <Real Name="Z">420.63288196845718</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">725.89069643053324</Real>
+      <Real Name="Y">-307.14182987449254</Real>
+      <Real Name="Z">-272.10453839552844</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-137.50946559924125</Real>
+      <Real Name="Y">441.85959065799284</Real>
+      <Real Name="Z">24.685829886633684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-727.15841489397917</Real>
+      <Real Name="Y">-155.63754304216673</Real>
+      <Real Name="Z">186.73110187024128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1093.96418406728</Real>
+      <Real Name="Y">1533.7141979470739</Real>
+      <Real Name="Z">-691.18888258757124</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">175.5713596063602</Real>
+      <Real Name="Y">-223.08215350693362</Real>
+      <Real Name="Z">103.29401807872563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">620.26683948104926</Real>
+      <Real Name="Y">-984.74612005677784</Real>
+      <Real Name="Z">450.36922575552882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">741.13327761551216</Real>
+      <Real Name="Y">38.880480725875842</Real>
+      <Real Name="Z">146.92354029556515</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.96563714345663</Real>
+      <Real Name="Y">89.769007462114089</Real>
+      <Real Name="Z">-35.344999616848995</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-913.02184236081052</Real>
+      <Real Name="Y">115.0889745055228</Real>
+      <Real Name="Z">-126.88047626939682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-857.20827327242478</Real>
+      <Real Name="Y">-1259.9251455446197</Real>
+      <Real Name="Z">42.608781081853067</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">722.15931228619013</Real>
+      <Real Name="Y">948.78032096854099</Real>
+      <Real Name="Z">84.146528236786182</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">74.563844284083743</Real>
+      <Real Name="Y">277.06266423325053</Real>
+      <Real Name="Z">-49.920272281682387</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.8541839742538</Real>
+      <Real Name="Y">-720.5026468952384</Real>
+      <Real Name="Z">837.90671010637743</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.87336206037145</Real>
+      <Real Name="Y">525.83381131930093</Real>
+      <Real Name="Z">-600.44767655945725</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.473131317472699</Real>
+      <Real Name="Y">87.925124225360477</Real>
+      <Real Name="Z">-131.07424489282357</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">109.19524064090176</Real>
+      <Real Name="Y">-146.4484418185454</Real>
+      <Real Name="Z">421.05289375597044</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.504550094051652</Real>
+      <Real Name="Y">248.29610526483913</Real>
+      <Real Name="Z">-104.3198092412765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-297.01364718548314</Real>
+      <Real Name="Y">363.84533469875481</Real>
+      <Real Name="Z">-22.354629233768804</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">148.74210126228604</Real>
+      <Real Name="Y">456.37712342892991</Real>
+      <Real Name="Z">-83.337368865701876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.161980254530093</Real>
+      <Real Name="Y">-172.15975240487953</Real>
+      <Real Name="Z">75.395028670823777</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.360962197908172</Real>
+      <Real Name="Y">-173.31647477517473</Real>
+      <Real Name="Z">29.129913342258753</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">102.00880506363499</Real>
+      <Real Name="Y">745.05529175910635</Real>
+      <Real Name="Z">64.491677819533379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.181331226432263</Real>
+      <Real Name="Y">-156.06081475301036</Real>
+      <Real Name="Z">-57.283299526860077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.893795593233946</Real>
+      <Real Name="Y">-712.18752664855788</Real>
+      <Real Name="Z">-7.4269912868936094</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.38937593714559</Real>
+      <Real Name="Y">-734.26937046196304</Real>
+      <Real Name="Z">-200.9215942302215</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">173.52509947284361</Real>
+      <Real Name="Y">630.88038338714762</Real>
+      <Real Name="Z">108.20328471183333</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">76.035783640983112</Real>
+      <Real Name="Y">183.68943352642037</Real>
+      <Real Name="Z">39.413285904545177</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">58.338279698810169</Real>
+      <Real Name="Y">872.56568330976438</Real>
+      <Real Name="Z">225.22942295712912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">111.15022337705804</Real>
+      <Real Name="Y">-181.75287588344531</Real>
+      <Real Name="Z">-2.5853354930909518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.35923387091816</Real>
+      <Real Name="Y">-607.7871068377799</Real>
+      <Real Name="Z">-286.12557064336085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-332.45852127564274</Real>
+      <Real Name="Y">337.43958576580081</Real>
+      <Real Name="Z">-374.35289698020642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.85998728650173</Real>
+      <Real Name="Y">-273.51732700681629</Real>
+      <Real Name="Z">423.52688743402774</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.40948852381536</Real>
+      <Real Name="Y">-137.62927803356089</Real>
+      <Real Name="Z">50.323107413175165</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-902.11190287684349</Real>
+      <Real Name="Y">-87.251112084887865</Real>
+      <Real Name="Z">-81.625888690389274</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">737.87640711375127</Real>
+      <Real Name="Y">-73.257410664809328</Real>
+      <Real Name="Z">14.834819617977068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">320.30240114869923</Real>
+      <Real Name="Y">47.346774694480857</Real>
+      <Real Name="Z">16.861284326305139</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1547.7750666257391</Real>
+      <Real Name="Y">-234.82827257568255</Real>
+      <Real Name="Z">-639.57165677717057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">304.23260498156816</Real>
+      <Real Name="Y">-54.692594710362386</Real>
+      <Real Name="Z">201.14463468925618</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1076.4939063213035</Real>
+      <Real Name="Y">319.56173278079052</Real>
+      <Real Name="Z">321.8192691688738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-528.41956130039432</Real>
+      <Real Name="Y">1446.4581993947179</Real>
+      <Real Name="Z">232.62244311463351</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">58.641963212832863</Real>
+      <Real Name="Y">-753.94064420918676</Real>
+      <Real Name="Z">-567.35706921379972</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">402.20923008473318</Real>
+      <Real Name="Y">-441.41896490835961</Real>
+      <Real Name="Z">552.20080029246674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.78465109136343</Real>
+      <Real Name="Y">430.12197149905023</Real>
+      <Real Name="Z">127.26781667339506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.720695330652717</Real>
+      <Real Name="Y">-119.7826517539508</Real>
+      <Real Name="Z">-81.974407425595402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.10103847482137</Real>
+      <Real Name="Y">-489.77284139292385</Real>
+      <Real Name="Z">-109.76592894470954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1150.6631859139054</Real>
+      <Real Name="Y">-2014.3072602579191</Real>
+      <Real Name="Z">628.69583076400488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-120.6349474378344</Real>
+      <Real Name="Y">1239.6338999781765</Real>
+      <Real Name="Z">350.85158851183718</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1025.91336490259</Real>
+      <Real Name="Y">384.27529799531607</Real>
+      <Real Name="Z">-785.1337495032642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">777.68198463484828</Real>
+      <Real Name="Y">-1300.9527006170331</Real>
+      <Real Name="Z">-314.34919413840151</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-240.3490515807685</Real>
+      <Real Name="Y">680.84177467865754</Real>
+      <Real Name="Z">36.351846271021451</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.8581740454693</Real>
+      <Real Name="Y">700.31460805730285</Real>
+      <Real Name="Z">334.14580085297223</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">79.337482261474548</Real>
+      <Real Name="Y">848.41241018771245</Real>
+      <Real Name="Z">1308.5198409927305</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.36395534293111</Real>
+      <Real Name="Y">-728.61225679380368</Real>
+      <Real Name="Z">-1002.1753065737439</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-112.31896811781378</Real>
+      <Real Name="Y">-3.2157583478410388</Real>
+      <Real Name="Z">-234.75411586390834</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.020000 Step 20 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-1941.1759548082312</Real>
+      <Real Name="Y">776.12440109197212</Real>
+      <Real Name="Z">330.16008677545835</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-24.26769299136366</Real>
+      <Real Name="Y">-79.619287184835741</Real>
+      <Real Name="Z">-95.267137211470825</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">433.67576230968371</Real>
+      <Real Name="Y">92.10446589646368</Real>
+      <Real Name="Z">-240.43336593194093</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">379.73694963650513</Real>
+      <Real Name="Y">-505.70728002991427</Real>
+      <Real Name="Z">-438.81106374269598</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4772.7764043573079</Real>
+      <Real Name="Y">475.20187159397022</Real>
+      <Real Name="Z">76.972241010284819</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2355.6620271898255</Real>
+      <Real Name="Y">-1425.5496083079349</Real>
+      <Real Name="Z">391.14095271642486</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2670.0491339819068</Real>
+      <Real Name="Y">1724.1138910826601</Real>
+      <Real Name="Z">375.11319682155852</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">881.10446168840997</Real>
+      <Real Name="Y">-506.73728256093665</Real>
+      <Real Name="Z">-74.867821937925257</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">457.43382200089269</Real>
+      <Real Name="Y">-743.58241727039501</Real>
+      <Real Name="Z">-311.70035611930416</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.86662951298936264</Real>
+      <Real Name="Y">1.0616540514717308</Real>
+      <Real Name="Z">18.66925309395107</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.0874301154087789</Real>
+      <Real Name="Y">8.1576742651472394</Real>
+      <Real Name="Z">-3.9787449541982483</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.3994010533579342</Real>
+      <Real Name="Y">2.3481341774483164</Real>
+      <Real Name="Z">-10.37396603124802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.986677110542246</Real>
+      <Real Name="Y">8.1366758137090898</Real>
+      <Real Name="Z">-26.080765688626926</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.1754936067894732</Real>
+      <Real Name="Y">-2.950340817501349</Real>
+      <Real Name="Z">12.54778601190986</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.733686028744998</Real>
+      <Real Name="Y">-13.705872992042188</Real>
+      <Real Name="Z">11.166948994416821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">285.2231066504649</Real>
+      <Real Name="Y">-117.17010822280861</Real>
+      <Real Name="Z">-749.9384130387208</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-291.22588285557623</Real>
+      <Real Name="Y">201.38268851798651</Real>
+      <Real Name="Z">404.1756627118113</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.765062018519117</Real>
+      <Real Name="Y">9.4226735002617836</Real>
+      <Real Name="Z">174.51484652066682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">484.07640582122792</Real>
+      <Real Name="Y">725.65641428686797</Real>
+      <Real Name="Z">-187.09730659329074</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.18264487682802</Real>
+      <Real Name="Y">-272.42050219714793</Real>
+      <Real Name="Z">54.884759421962329</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-453.1339536826992</Real>
+      <Real Name="Y">-693.54368022593519</Real>
+      <Real Name="Z">264.33472487090921</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">432.26182171032968</Real>
+      <Real Name="Y">-418.03197263993115</Real>
+      <Real Name="Z">-482.64079061726113</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.824043388546016</Real>
+      <Real Name="Y">143.91430091881676</Real>
+      <Real Name="Z">125.01095828064983</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-451.69957551674344</Real>
+      <Real Name="Y">414.49555795297272</Real>
+      <Real Name="Z">458.16557079352242</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">118.52556734301103</Real>
+      <Real Name="Y">228.38985617395582</Real>
+      <Real Name="Z">280.08085007014552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.102198908312012</Real>
+      <Real Name="Y">-166.67394177734354</Real>
+      <Real Name="Z">-133.81345770080989</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-183.87055829373367</Real>
+      <Real Name="Y">-246.14795827504591</Real>
+      <Real Name="Z">-144.73147199343396</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-423.55185234294368</Real>
+      <Real Name="Y">403.75994104585129</Real>
+      <Real Name="Z">871.1083791878898</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">396.4339412723715</Real>
+      <Real Name="Y">-255.30092936056033</Real>
+      <Real Name="Z">-819.23806091684969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.90696709884492</Real>
+      <Real Name="Y">-222.47056864135061</Real>
+      <Real Name="Z">-108.75068535830549</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1188.885658685876</Real>
+      <Real Name="Y">304.81806476192088</Real>
+      <Real Name="Z">857.43234269445554</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">51.090881963726694</Real>
+      <Real Name="Y">-528.06230354087961</Real>
+      <Real Name="Z">-201.68634391376818</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-930.64169350342752</Real>
+      <Real Name="Y">505.53011636197618</Real>
+      <Real Name="Z">-535.8222062377896</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">247.57834283572606</Real>
+      <Real Name="Y">1038.0072805956568</Real>
+      <Real Name="Z">165.73525867992225</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-80.842604698723477</Real>
+      <Real Name="Y">-398.05737140877807</Real>
+      <Real Name="Z">-39.635082218703523</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-92.46029247354231</Real>
+      <Real Name="Y">-194.44871013310262</Real>
+      <Real Name="Z">-56.568418467556967</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.14698977279721</Real>
+      <Real Name="Y">34.51456171291774</Real>
+      <Real Name="Z">-1026.3958329511681</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-126.25218869302574</Real>
+      <Real Name="Y">245.08489703455484</Real>
+      <Real Name="Z">331.36766377047394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">310.6563588352102</Real>
+      <Real Name="Y">121.24127216836132</Real>
+      <Real Name="Z">324.1084511174563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">496.2448278228822</Real>
+      <Real Name="Y">223.62709333650162</Real>
+      <Real Name="Z">-427.76073910927511</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-524.30806100840596</Real>
+      <Real Name="Y">66.807963892605756</Real>
+      <Real Name="Z">717.62628944107848</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-171.99773824783759</Real>
+      <Real Name="Y">-222.7128972455555</Real>
+      <Real Name="Z">71.10924852649552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-969.64408017329049</Real>
+      <Real Name="Y">189.9437667349371</Real>
+      <Real Name="Z">255.01123335301264</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">588.83138033525154</Real>
+      <Real Name="Y">-319.94265313460562</Real>
+      <Real Name="Z">-158.02133089428739</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">173.63532133746452</Real>
+      <Real Name="Y">-1.5034543485802931</Real>
+      <Real Name="Z">-167.89286930948737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-430.85377054125564</Real>
+      <Real Name="Y">90.414259500563617</Real>
+      <Real Name="Z">275.37286180496289</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">149.89701748967397</Real>
+      <Real Name="Y">-4.9835374955290206</Real>
+      <Real Name="Z">-53.836628116921254</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.08759182273775</Real>
+      <Real Name="Y">-196.664276917058</Real>
+      <Real Name="Z">-98.825768644037439</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-152.86143604338332</Real>
+      <Real Name="Y">-1401.9902492786503</Real>
+      <Real Name="Z">-713.83591705491779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">44.791232931778282</Real>
+      <Real Name="Y">1323.9336857445621</Real>
+      <Real Name="Z">475.15347116922214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">372.90756787135825</Real>
+      <Real Name="Y">371.76692282846921</Real>
+      <Real Name="Z">134.93881711074326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">419.75985833384919</Real>
+      <Real Name="Y">720.02890782941256</Real>
+      <Real Name="Z">-891.08423332170116</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.65379505279665</Real>
+      <Real Name="Y">-433.60667423350662</Real>
+      <Real Name="Z">14.835597851600021</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-381.95822853291293</Real>
+      <Real Name="Y">-136.63884198709394</Real>
+      <Real Name="Z">287.37676201931288</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">69.698254947490739</Real>
+      <Real Name="Y">129.90908105036968</Real>
+      <Real Name="Z">36.784285136727</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-16.914585943174181</Real>
+      <Real Name="Y">-52.865862479023747</Real>
+      <Real Name="Z">-14.9056208116865</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-32.420620967002769</Real>
+      <Real Name="Y">-43.806137795479948</Real>
+      <Real Name="Z">-13.548739092283864</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-378.64142240091223</Real>
+      <Real Name="Y">933.62719825396982</Real>
+      <Real Name="Z">233.91155640756267</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">445.80468464469971</Real>
+      <Real Name="Y">-707.50724950248241</Real>
+      <Real Name="Z">-411.16195017539587</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">27.57042786570468</Real>
+      <Real Name="Y">-160.97312822580696</Real>
+      <Real Name="Z">-22.34963383669173</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">559.09472126510184</Real>
+      <Real Name="Y">-789.64561702153401</Real>
+      <Real Name="Z">-1239.8214702941668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-415.87147993575195</Real>
+      <Real Name="Y">298.92950355748053</Real>
+      <Real Name="Z">973.78289283486652</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-406.52846501385397</Real>
+      <Real Name="Y">268.98905955016556</Real>
+      <Real Name="Z">83.304505655955552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">40.425300078535059</Real>
+      <Real Name="Y">-257.60884617258063</Real>
+      <Real Name="Z">24.789490030539866</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-25.435363752682534</Real>
+      <Real Name="Y">111.46169828615758</Real>
+      <Real Name="Z">34.233485706684966</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-37.192296912377614</Real>
+      <Real Name="Y">64.340528193166804</Real>
+      <Real Name="Z">129.5820764058243</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">329.89334503486674</Real>
+      <Real Name="Y">-311.4480312270004</Real>
+      <Real Name="Z">-95.808338846515142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-311.84798504862653</Real>
+      <Real Name="Y">378.97627081708549</Real>
+      <Real Name="Z">157.47570486527502</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.070334334992083</Real>
+      <Real Name="Y">47.359714203782154</Real>
+      <Real Name="Z">52.199639363582108</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">703.44570774137526</Real>
+      <Real Name="Y">-1082.22658306375</Real>
+      <Real Name="Z">-699.36066329427229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.00121871649736</Real>
+      <Real Name="Y">259.55965691354214</Real>
+      <Real Name="Z">739.4308014232862</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-135.48416732805558</Real>
+      <Real Name="Y">768.57440482083564</Real>
+      <Real Name="Z">234.70015819404682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">18.760980073593963</Real>
+      <Real Name="Y">-7.0275657781357097</Real>
+      <Real Name="Z">36.112636577296655</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.4273345211945045</Real>
+      <Real Name="Y">4.4838846930062086</Real>
+      <Real Name="Z">-14.09830657235706</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.8087274957790598</Real>
+      <Real Name="Y">3.4069991006665079</Real>
+      <Real Name="Z">-20.889890752149583</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.08790297750738</Real>
+      <Real Name="Y">799.8779058302473</Real>
+      <Real Name="Z">97.093051464054128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">484.28876551866153</Real>
+      <Real Name="Y">-393.5299063695108</Real>
+      <Real Name="Z">-514.38648673917669</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-206.13569687633873</Real>
+      <Real Name="Y">-364.7075149626063</Real>
+      <Real Name="Z">321.07127560626901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">337.78635395472304</Real>
+      <Real Name="Y">47.261878914472625</Real>
+      <Real Name="Z">-323.64633129830827</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-304.54961552817275</Real>
+      <Real Name="Y">45.694714375986443</Real>
+      <Real Name="Z">85.567718027653967</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-78.90759995654895</Real>
+      <Real Name="Y">-20.870393254599875</Real>
+      <Real Name="Z">105.97813598170256</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-440.42946825758747</Real>
+      <Real Name="Y">-1013.199620607159</Real>
+      <Real Name="Z">-484.39057983266065</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">187.23656022542346</Real>
+      <Real Name="Y">293.3184249329932</Real>
+      <Real Name="Z">-141.35663687819226</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">373.69147793021324</Real>
+      <Real Name="Y">708.79255026441217</Real>
+      <Real Name="Z">620.97602041652112</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-849.95464730794549</Real>
+      <Real Name="Y">325.53178036042095</Real>
+      <Real Name="Z">-503.02428413531777</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">677.97562424178432</Real>
+      <Real Name="Y">-267.60901934692731</Real>
+      <Real Name="Z">483.52007117793562</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">167.60005122958313</Real>
+      <Real Name="Y">-0.8415274332710112</Real>
+      <Real Name="Z">31.498922038638213</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">252.78810079878025</Real>
+      <Real Name="Y">416.96283744030967</Real>
+      <Real Name="Z">78.158651527759801</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.760431531725395</Real>
+      <Real Name="Y">-147.62949587454358</Real>
+      <Real Name="Z">-63.707351346289471</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.21573174953113</Real>
+      <Real Name="Y">-145.28452379947109</Real>
+      <Real Name="Z">11.769334554710872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-817.41779168428707</Real>
+      <Real Name="Y">-794.79266798033359</Real>
+      <Real Name="Z">-520.80338085402695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">149.6276906234711</Real>
+      <Real Name="Y">200.0328632303036</Real>
+      <Real Name="Z">153.95914778589423</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">706.39393792572912</Real>
+      <Real Name="Y">519.66616366623964</Real>
+      <Real Name="Z">395.47078467392856</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.768406227022467</Real>
+      <Real Name="Y">-74.093834854681916</Real>
+      <Real Name="Z">-781.5813016066005</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">12.577975395713395</Real>
+      <Real Name="Y">-195.03196570201149</Real>
+      <Real Name="Z">729.83302870256045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.670335675658869</Real>
+      <Real Name="Y">55.850614338749601</Real>
+      <Real Name="Z">154.12725368692085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-540.57742978074691</Real>
+      <Real Name="Y">-517.44320750745112</Real>
+      <Real Name="Z">-924.71839078425796</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">125.34364627798428</Real>
+      <Real Name="Y">77.018292118898174</Real>
+      <Real Name="Z">161.48990801353807</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">248.80509677917667</Real>
+      <Real Name="Y">494.17386357028033</Real>
+      <Real Name="Z">738.19049524742502</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-505.80110909940959</Real>
+      <Real Name="Y">-368.63720994020713</Real>
+      <Real Name="Z">-167.52718327828325</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">530.67381742010389</Real>
+      <Real Name="Y">128.27671687693473</Real>
+      <Real Name="Z">22.211067620248272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">283.55624474160925</Real>
+      <Real Name="Y">111.54415455226184</Real>
+      <Real Name="Z">67.448051501645665</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">107.69277254505064</Real>
+      <Real Name="Y">813.74740903664883</Real>
+      <Real Name="Z">-15.017971241544501</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.46309884071389718</Real>
+      <Real Name="Y">-170.53606783468524</Real>
+      <Real Name="Z">-16.404780378206695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-203.73639899411594</Real>
+      <Real Name="Y">-845.49895538544013</Real>
+      <Real Name="Z">-119.80699178885945</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1958.1656158395431</Real>
+      <Real Name="Y">-97.788860192498902</Real>
+      <Real Name="Z">894.52114475479038</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1018.0484061422765</Real>
+      <Real Name="Y">207.5841041304833</Real>
+      <Real Name="Z">-699.03637549752</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-513.06665614928306</Real>
+      <Real Name="Y">-185.79704767795005</Real>
+      <Real Name="Z">-379.97007717503163</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-303.68966637825775</Real>
+      <Real Name="Y">67.367369024160027</Real>
+      <Real Name="Z">-99.677414698126924</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">157.70037844919435</Real>
+      <Real Name="Y">-133.93625348009206</Real>
+      <Real Name="Z">51.648803941747587</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.20185522349107</Real>
+      <Real Name="Y">-4.4527842245191884</Real>
+      <Real Name="Z">36.903761868689216</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">624.66385639310181</Real>
+      <Real Name="Y">-49.835669567722107</Real>
+      <Real Name="Z">111.95239748567133</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-629.18831888808722</Real>
+      <Real Name="Y">209.81660519363118</Real>
+      <Real Name="Z">52.868040244618534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-288.95418316905636</Real>
+      <Real Name="Y">143.73459172340293</Real>
+      <Real Name="Z">-13.92936981587502</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-215.37908830223938</Real>
+      <Real Name="Y">-1123.2211733912914</Real>
+      <Real Name="Z">16.204716954797902</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">94.986163918893894</Real>
+      <Real Name="Y">162.68589036034373</Real>
+      <Real Name="Z">-52.75369934486379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">139.282918123282</Real>
+      <Real Name="Y">1106.8329345597108</Real>
+      <Real Name="Z">30.305596789664413</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">210.92336925363625</Real>
+      <Real Name="Y">1088.3483079538112</Real>
+      <Real Name="Z">799.6471631451127</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-175.87190167130956</Real>
+      <Real Name="Y">-663.58640417075287</Real>
+      <Real Name="Z">-643.90539619536116</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.280676436482267</Real>
+      <Real Name="Y">-284.59106978834222</Real>
+      <Real Name="Z">-145.57134058521802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-198.92929239596808</Real>
+      <Real Name="Y">-249.57751428488621</Real>
+      <Real Name="Z">-215.00992915737862</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-89.191190707775135</Real>
+      <Real Name="Y">203.14121296932183</Real>
+      <Real Name="Z">-15.531199017606241</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">188.18213896489499</Real>
+      <Real Name="Y">63.925232800506492</Real>
+      <Real Name="Z">340.21831151662053</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">744.07539390095064</Real>
+      <Real Name="Y">-346.15215096458525</Real>
+      <Real Name="Z">-232.91744585126955</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-403.0259359205744</Real>
+      <Real Name="Y">436.42165371641141</Real>
+      <Real Name="Z">76.996941643876639</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-626.65179276010645</Real>
+      <Real Name="Y">-103.12558196233445</Real>
+      <Real Name="Z">220.71748560453301</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-954.39957148689348</Real>
+      <Real Name="Y">1205.1136859591688</Real>
+      <Real Name="Z">-666.47710092105535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.6166426303904</Real>
+      <Real Name="Y">-188.8539977192743</Real>
+      <Real Name="Z">111.03216538309981</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">561.99384378233174</Real>
+      <Real Name="Y">-852.96466255973689</Real>
+      <Real Name="Z">533.53645473100698</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">941.01801537315157</Real>
+      <Real Name="Y">-106.6901335626109</Real>
+      <Real Name="Z">49.386598704304461</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.1182809821303</Real>
+      <Real Name="Y">83.240121858605065</Real>
+      <Real Name="Z">-32.69460159752569</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-892.92917185921169</Real>
+      <Real Name="Y">-66.413376361300706</Real>
+      <Real Name="Z">-97.31168666189464</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-635.93707276188559</Real>
+      <Real Name="Y">-684.65751767920824</Real>
+      <Real Name="Z">54.682285705538533</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">600.32050700434286</Real>
+      <Real Name="Y">696.63556363709222</Real>
+      <Real Name="Z">30.989976045605779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">51.795143768440056</Real>
+      <Real Name="Y">146.30651087818373</Real>
+      <Real Name="Z">-46.315527373691978</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-294.94739420958973</Real>
+      <Real Name="Y">-1116.2627087918067</Real>
+      <Real Name="Z">1220.180667871917</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">332.14559419223514</Real>
+      <Real Name="Y">618.31376909165681</Real>
+      <Real Name="Z">-559.74554195613177</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-70.886133356661006</Real>
+      <Real Name="Y">148.86396026736691</Real>
+      <Real Name="Z">-203.76932685613372</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">366.74099502206997</Real>
+      <Real Name="Y">-491.04238784343715</Real>
+      <Real Name="Z">57.914054439075102</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.863016287260493</Real>
+      <Real Name="Y">272.7411109832592</Real>
+      <Real Name="Z">-82.302704708251071</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-373.6920265957649</Real>
+      <Real Name="Y">357.33206518555716</Real>
+      <Real Name="Z">-110.56143738569946</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-81.763770526411918</Real>
+      <Real Name="Y">410.36972428655076</Real>
+      <Real Name="Z">-16.056492480057621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.973747787995748</Real>
+      <Real Name="Y">-213.79476210142556</Real>
+      <Real Name="Z">45.384239217506419</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.3766677930974431</Real>
+      <Real Name="Y">-118.51833522520934</Real>
+      <Real Name="Z">7.3906174428770868</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">11.625173827161849</Real>
+      <Real Name="Y">752.93290052163968</Real>
+      <Real Name="Z">54.499300522386555</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.943008871345267</Real>
+      <Real Name="Y">-172.1419773019642</Real>
+      <Real Name="Z">-51.709420298165092</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">168.2348341116533</Real>
+      <Real Name="Y">-648.1046281390627</Real>
+      <Real Name="Z">-40.975480223755341</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-118.74360494832592</Real>
+      <Real Name="Y">-686.90240238693718</Real>
+      <Real Name="Z">-273.01001041290749</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">209.63737610191572</Real>
+      <Real Name="Y">600.58337240861863</Real>
+      <Real Name="Z">157.6784723592269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">96.471981888884343</Real>
+      <Real Name="Y">156.75555468975492</Real>
+      <Real Name="Z">38.578231473609904</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">117.60322479990658</Real>
+      <Real Name="Y">902.57328897482557</Real>
+      <Real Name="Z">344.99022573328034</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">46.33118692044593</Real>
+      <Real Name="Y">-210.05437238348139</Real>
+      <Real Name="Z">-49.299324046176181</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-175.47152763914556</Real>
+      <Real Name="Y">-614.57437257357049</Real>
+      <Real Name="Z">-382.50604804340765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-262.52660457200761</Real>
+      <Real Name="Y">209.95163250501693</Real>
+      <Real Name="Z">-327.43947434650329</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">122.64062570143862</Real>
+      <Real Name="Y">-118.58073441827348</Real>
+      <Real Name="Z">347.05748602009692</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.69838750103676</Real>
+      <Real Name="Y">-88.513878530371287</Real>
+      <Real Name="Z">83.353040049326779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1006.6349869480131</Real>
+      <Real Name="Y">12.833767606336608</Real>
+      <Real Name="Z">-54.366786379716004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">734.45261422401177</Real>
+      <Real Name="Y">-140.95289998584144</Real>
+      <Real Name="Z">-16.022758596090824</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">337.96920786450482</Real>
+      <Real Name="Y">-9.0288219985982394</Real>
+      <Real Name="Z">16.621892946401942</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1542.6155986504136</Real>
+      <Real Name="Y">-877.97974077033336</Real>
+      <Real Name="Z">-445.95767314565512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">342.00122558142994</Real>
+      <Real Name="Y">66.372918202258916</Real>
+      <Real Name="Z">131.64697714320147</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1194.2873089018972</Real>
+      <Real Name="Y">209.62843867316627</Real>
+      <Real Name="Z">332.74968467165894</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-153.14077120002568</Real>
+      <Real Name="Y">1485.488770202569</Real>
+      <Real Name="Z">25.472926946757639</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-43.527488919599953</Real>
+      <Real Name="Y">-805.25314903477351</Real>
+      <Real Name="Z">-425.69172944116133</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">213.87126216042645</Real>
+      <Real Name="Y">-656.32258442814691</Real>
+      <Real Name="Z">610.04725523803927</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">242.15083039056557</Real>
+      <Real Name="Y">507.12273720049416</Real>
+      <Real Name="Z">160.39975190795326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.921807850243752</Real>
+      <Real Name="Y">-131.1843783984643</Real>
+      <Real Name="Z">-105.44838751938428</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-257.85334962408035</Real>
+      <Real Name="Y">-514.03880670257524</Real>
+      <Real Name="Z">-75.961044230113274</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">783.49144410227927</Real>
+      <Real Name="Y">-1645.1494683965764</Real>
+      <Real Name="Z">820.65514587334792</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-82.573620401297006</Real>
+      <Real Name="Y">1167.6663355335822</Real>
+      <Real Name="Z">104.31432991955822</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-938.88673227919071</Real>
+      <Real Name="Y">560.52073099688619</Real>
+      <Real Name="Z">-728.34237609349395</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">793.64907362064832</Real>
+      <Real Name="Y">-1959.0572131696465</Real>
+      <Real Name="Z">-662.17562941830715</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-54.908496409576699</Real>
+      <Real Name="Y">833.28351719681189</Real>
+      <Real Name="Z">201.93925863617034</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-547.12328819571894</Real>
+      <Real Name="Y">671.68554224342768</Real>
+      <Real Name="Z">427.73658168360629</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">101.8584887841489</Real>
+      <Real Name="Y">752.79725559868643</Real>
+      <Real Name="Z">1182.0955837521933</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.66333316361786</Real>
+      <Real Name="Y">-585.89318174083246</Real>
+      <Real Name="Z">-1009.0300755822834</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.505779592187579</Real>
+      <Real Name="Y">-94.696988438966372</Real>
+      <Real Name="Z">-263.45061678327289</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_position_restraints_d.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_position_restraints_d.xml
new file mode 100644 (file)
index 0000000..9fbe1bc
--- /dev/null
@@ -0,0 +1,1918 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVvdw/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-2.0825785243975439</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1.6903376934977123</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1.7480537149865598</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1.7947939397992074</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1.8257050545709455</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-2.2802875040909019</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1.821580118617167</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1.7817437907957452</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1.7178155893139617</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1.633505418399759</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-2.1026736793883574</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1.4257874754438724</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1.3151491105120272</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1.2087833360960889</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1.1124585376893867</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1.6538828147092663</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-0.96479363249057448</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-0.91594470410183315</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-0.88293197391368017</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-0.86440484459174582</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1.3098986228408522</Real>
+  </Energy>
+  <Energy Name="dVcoul/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">127.00456344643993</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">16.05155642263059</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">15.60138365383618</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">14.819771493603014</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">13.73240890144281</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">124.31328262369632</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">10.976898286221854</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">9.5295557894674801</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">8.1569479746759228</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">6.8984520917137573</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">119.52310233838725</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">4.669157427914655</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">3.6130768931058128</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">2.5472908574083348</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">1.4732978185966203</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">115.40237999500837</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-0.51742252359249363</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1.3216943254436799</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1.9718699243732729</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-2.5120504186762815</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">111.22526057316783</Real>
+  </Energy>
+  <Energy Name="dVremain/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">0</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">0</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">0</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">0</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">0</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">0</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">0</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">0</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">0</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">0</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">0</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">0</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">0</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">0</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">0</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">0</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">0</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">0</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">0</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">0</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">0</Real>
+  </Energy>
+  <Energy Name="dVrestraint/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-0.0017830721747314757</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-0.2065000186457476</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-0.88068813705267945</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-2.0502277452171076</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-3.6399843599932793</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-5.4802318100763463</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-7.3484094078594753</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-9.0255498567848793</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-10.344426397500968</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-11.217693184130896</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-11.644848190393621</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-11.701239170975322</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-11.512565207738382</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-11.217789583412303</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-10.925487225218061</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-10.675840747598006</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-10.428420362741718</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-10.090804930490794</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-9.5792273077949144</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-8.8795157032470815</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-8.0770548035150149</Real>
+  </Energy>
+  <Energy Name="dVbonded/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">46.033102414228267</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">0</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">0</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">0</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">0</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">6.1913708147037063</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">0</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">0</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">0</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">0</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-20.66643642251087</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">0</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">0</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">0</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">0</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-5.1297498191230595</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">0</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">0</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">0</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">0</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">23.32628899734577</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1400.7028857552182</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1405.3831937844852</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1408.8713123266882</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1408.9117248932484</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1404.5218945178465</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1396.3661183966726</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1386.2974106765055</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1376.5449421356184</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1369.0186700647685</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1364.8486314327656</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1364.119789494498</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1365.8037216139094</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1367.9523809908465</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1368.2398673800658</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1364.839696512229</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1357.4019234934408</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1347.5745219269704</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1338.53835460606</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1333.6701641900208</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1335.0839895445536</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1342.7892521267881</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">424.12428029457027</Real>
+      <Real Name="Y">-929.0463430359423</Real>
+      <Real Name="Z">-218.04047632648403</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.45815430525074</Real>
+      <Real Name="Y">-268.49871451257479</Real>
+      <Real Name="Z">74.148839941747013</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-412.97903361530717</Real>
+      <Real Name="Y">284.78315280006717</Real>
+      <Real Name="Z">-165.28173406775846</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.094620759562645</Real>
+      <Real Name="Y">151.31285573227692</Real>
+      <Real Name="Z">152.34331654848432</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3730.4391169382088</Real>
+      <Real Name="Y">1969.7450412386809</Real>
+      <Real Name="Z">1181.4597869117197</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-351.27826221242674</Real>
+      <Real Name="Y">-121.78796653490272</Real>
+      <Real Name="Z">-418.010686411623</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4348.0294216854618</Real>
+      <Real Name="Y">-269.29430755080205</Real>
+      <Real Name="Z">85.694226620704256</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.48928955725916</Real>
+      <Real Name="Y">-475.32104512820649</Real>
+      <Real Name="Z">-614.36228943727588</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.86320147306151</Real>
+      <Real Name="Y">-579.5969702822207</Real>
+      <Real Name="Z">-238.10584373247849</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.2447628260391923</Real>
+      <Real Name="Y">-34.354563171569467</Real>
+      <Real Name="Z">23.923242116812929</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.4556090303903773</Real>
+      <Real Name="Y">29.482908810355923</Real>
+      <Real Name="Z">-14.059219652982144</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.5511925213670139</Real>
+      <Real Name="Y">16.100396534059932</Real>
+      <Real Name="Z">-5.9521652223676682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.7416155685928061</Real>
+      <Real Name="Y">20.842710307485945</Real>
+      <Real Name="Z">-6.4879131948126627</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.0200925732223425</Real>
+      <Real Name="Y">-8.3616783275454338</Real>
+      <Real Name="Z">4.3327439903187681</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.7705151354203394</Real>
+      <Real Name="Y">-23.385022034296831</Real>
+      <Real Name="Z">3.9191724313506882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">362.79405127356711</Real>
+      <Real Name="Y">-56.651787981708068</Real>
+      <Real Name="Z">-669.74037581615016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.68496463907741</Real>
+      <Real Name="Y">34.760023569827311</Real>
+      <Real Name="Z">384.98731691865208</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.0339906280499349</Real>
+      <Real Name="Y">-12.771758422265586</Real>
+      <Real Name="Z">205.99928050592615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">410.18919743092351</Real>
+      <Real Name="Y">464.39308541245958</Real>
+      <Real Name="Z">-110.42778660706271</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-59.870294516685711</Real>
+      <Real Name="Y">-251.50404475257753</Real>
+      <Real Name="Z">88.595929586063733</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-384.36361458856851</Real>
+      <Real Name="Y">-626.99968675518107</Real>
+      <Real Name="Z">249.60489373750028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">643.00085757044963</Real>
+      <Real Name="Y">-494.1671234949174</Real>
+      <Real Name="Z">-430.29467917723855</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.95393995315841</Real>
+      <Real Name="Y">122.95177076793873</Real>
+      <Real Name="Z">78.581570752473283</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.27073209181242</Real>
+      <Real Name="Y">440.16686592720157</Real>
+      <Real Name="Z">225.4059601241334</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.540738189435849</Real>
+      <Real Name="Y">305.03283002810321</Real>
+      <Real Name="Z">57.952823537500961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.081165963338634</Real>
+      <Real Name="Y">-168.25285262437063</Real>
+      <Real Name="Z">-107.57028192738989</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.5082850525139</Real>
+      <Real Name="Y">-218.75112331215394</Real>
+      <Real Name="Z">-135.96180174676019</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-606.1112738798181</Real>
+      <Real Name="Y">246.92795466341244</Real>
+      <Real Name="Z">825.49098709183227</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">403.71066109101213</Real>
+      <Real Name="Y">-44.356760448265703</Real>
+      <Real Name="Z">-860.78653799471283</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.75993991038928</Real>
+      <Real Name="Y">-180.96945982171042</Real>
+      <Real Name="Z">-58.708855998803386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1061.3410359444586</Real>
+      <Real Name="Y">551.21306372134325</Real>
+      <Real Name="Z">854.93370677378664</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-147.25706914526353</Real>
+      <Real Name="Y">-835.59019962922912</Real>
+      <Real Name="Z">-355.07345008005399</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-825.13008341259297</Real>
+      <Real Name="Y">327.5701152466101</Real>
+      <Real Name="Z">-570.35084572162748</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">350.70113505010289</Real>
+      <Real Name="Y">765.85583822497597</Real>
+      <Real Name="Z">138.52388162381308</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.65034853492583977</Real>
+      <Real Name="Y">-658.98113340606915</Real>
+      <Real Name="Z">-49.809148607512697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-87.496455254130865</Real>
+      <Real Name="Y">-135.70781929598905</Real>
+      <Real Name="Z">-34.035094773570442</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.767410865901866</Real>
+      <Real Name="Y">-120.23454148042386</Real>
+      <Real Name="Z">-943.27208631060637</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-78.316406982433108</Real>
+      <Real Name="Y">8.0977940854804231</Real>
+      <Real Name="Z">298.2366570988263</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">406.40822013211914</Real>
+      <Real Name="Y">-11.194958115070911</Real>
+      <Real Name="Z">705.65362098997366</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">108.87698446526662</Real>
+      <Real Name="Y">24.122077588311981</Real>
+      <Real Name="Z">-893.57139210304058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-260.11279189321982</Real>
+      <Real Name="Y">182.00899241013269</Real>
+      <Real Name="Z">499.39877059892035</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.558167844940527</Real>
+      <Real Name="Y">-188.92580237690495</Real>
+      <Real Name="Z">177.98302409422152</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-642.75740137040509</Real>
+      <Real Name="Y">79.996909922832259</Real>
+      <Real Name="Z">346.34769112124985</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">550.25546048566025</Real>
+      <Real Name="Y">-61.96311190296538</Real>
+      <Real Name="Z">-0.98826774934605766</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.91330584071713</Real>
+      <Real Name="Y">-34.979268311175524</Real>
+      <Real Name="Z">-237.92103827809831</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.27409597412958</Real>
+      <Real Name="Y">43.529886247595869</Real>
+      <Real Name="Z">359.45278140061663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">252.78075096108063</Real>
+      <Real Name="Y">-6.6674550292574679</Real>
+      <Real Name="Z">-124.66428260696625</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">63.506680119640137</Real>
+      <Real Name="Y">-117.28537902122117</Real>
+      <Real Name="Z">-113.51317444844173</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-308.457354431319</Real>
+      <Real Name="Y">-2131.4122221049765</Real>
+      <Real Name="Z">-637.20899097464621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">316.44193351944102</Real>
+      <Real Name="Y">1351.8123254040033</Real>
+      <Real Name="Z">272.17867017989141</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">250.45922870637332</Real>
+      <Real Name="Y">446.76581743163604</Real>
+      <Real Name="Z">49.137792411231629</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">456.77070840227879</Real>
+      <Real Name="Y">934.49195803767554</Real>
+      <Real Name="Z">-928.82374869197622</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.39957402642045</Real>
+      <Real Name="Y">-518.15260359964714</Real>
+      <Real Name="Z">256.07834536877596</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-291.35917399309511</Real>
+      <Real Name="Y">-339.49770128781756</Real>
+      <Real Name="Z">612.54168751954614</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.380949144522347</Real>
+      <Real Name="Y">99.348816469164433</Real>
+      <Real Name="Z">5.847899188033189</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.270093355213056</Real>
+      <Real Name="Y">-43.418768488814557</Real>
+      <Real Name="Z">-3.8916660475475755</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.805838199515168</Real>
+      <Real Name="Y">-33.153839743194069</Real>
+      <Real Name="Z">8.3257441518301114</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-451.38204623616565</Real>
+      <Real Name="Y">981.10170371540289</Real>
+      <Real Name="Z">389.99657695747231</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">613.29738036411766</Real>
+      <Real Name="Y">-691.22705330412282</Real>
+      <Real Name="Z">-449.54552227858312</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">41.709003659813689</Real>
+      <Real Name="Y">-156.46160198960365</Real>
+      <Real Name="Z">-14.127097601929506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">233.46250033200772</Real>
+      <Real Name="Y">-619.36534590542806</Real>
+      <Real Name="Z">-1448.7869052251717</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-82.947922148473907</Real>
+      <Real Name="Y">421.25102542244804</Real>
+      <Real Name="Z">1032.102707600716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-339.18607005657185</Real>
+      <Real Name="Y">107.47339882204822</Real>
+      <Real Name="Z">276.15360187778771</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.72922635668436</Real>
+      <Real Name="Y">-120.86280838095773</Real>
+      <Real Name="Z">-118.21985564016867</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-84.896833440254795</Real>
+      <Real Name="Y">123.81025794967974</Real>
+      <Real Name="Z">73.019353066886509</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.065192384321456</Real>
+      <Real Name="Y">39.107722537996608</Real>
+      <Real Name="Z">151.49538861465032</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">323.79285527327283</Real>
+      <Real Name="Y">-346.6108337302548</Real>
+      <Real Name="Z">-65.820365852436865</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-286.57569205713514</Real>
+      <Real Name="Y">418.98269539756251</Real>
+      <Real Name="Z">92.445113237226721</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.09483685577648</Real>
+      <Real Name="Y">70.806510800847263</Real>
+      <Real Name="Z">37.011821088061794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.57668801538352</Real>
+      <Real Name="Y">-854.87084314715707</Real>
+      <Real Name="Z">-613.39804468355771</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-324.74211639629254</Real>
+      <Real Name="Y">337.76010468219943</Real>
+      <Real Name="Z">608.74908028654568</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-337.20211222456311</Real>
+      <Real Name="Y">857.46719179197703</Real>
+      <Real Name="Z">242.82504709201382</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.000377307977104</Real>
+      <Real Name="Y">-4.4256625029919121</Real>
+      <Real Name="Z">16.162606564777086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.37001165777232</Real>
+      <Real Name="Y">4.8394040329845751</Real>
+      <Real Name="Z">-6.438760952042319</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.3211847099196135</Real>
+      <Real Name="Y">1.222325092845459</Real>
+      <Real Name="Z">-11.723038460027716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-445.03804005968408</Real>
+      <Real Name="Y">891.17165044286901</Real>
+      <Real Name="Z">239.04052787006057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">303.12734666751106</Real>
+      <Real Name="Y">-493.37318294793567</Real>
+      <Real Name="Z">-675.4239376294197</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.41891929244048</Real>
+      <Real Name="Y">-463.4326675580067</Real>
+      <Real Name="Z">304.97795409054243</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.65865193857229</Real>
+      <Real Name="Y">26.948273693470444</Real>
+      <Real Name="Z">-153.64102511042404</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-270.52435370655292</Real>
+      <Real Name="Y">60.771283043094982</Real>
+      <Real Name="Z">40.797944272297826</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-89.682305331183414</Real>
+      <Real Name="Y">-36.206375667604718</Real>
+      <Real Name="Z">75.430476645232545</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-651.49261589840421</Real>
+      <Real Name="Y">-1244.5880121244745</Real>
+      <Real Name="Z">-370.9975493446546</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.34317322337648</Real>
+      <Real Name="Y">609.01515469312858</Real>
+      <Real Name="Z">-251.63504642235392</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">414.07294202227013</Real>
+      <Real Name="Y">623.66224796670417</Real>
+      <Real Name="Z">628.4146985393761</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1188.7782766895791</Real>
+      <Real Name="Y">442.66975188748518</Real>
+      <Real Name="Z">-788.13699520554781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">817.78121866915455</Real>
+      <Real Name="Y">-311.03109747395752</Real>
+      <Real Name="Z">570.49508996447378</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.59108353831945</Real>
+      <Real Name="Y">-31.578633229504096</Real>
+      <Real Name="Z">36.63298544887293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">220.58657645430793</Real>
+      <Real Name="Y">411.63140530576811</Real>
+      <Real Name="Z">169.62712548380605</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.005389715929113</Real>
+      <Real Name="Y">-124.08432646845449</Real>
+      <Real Name="Z">-74.87217741029535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.66292720647944</Real>
+      <Real Name="Y">-114.64649927372922</Real>
+      <Real Name="Z">-20.036692205085409</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1048.8613376512781</Real>
+      <Real Name="Y">-1080.8731758645481</Real>
+      <Real Name="Z">-855.74512802247762</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.96553639814115</Real>
+      <Real Name="Y">212.71673618843263</Real>
+      <Real Name="Z">194.14800984596781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">577.57604770643559</Real>
+      <Real Name="Y">694.10472519592429</Real>
+      <Real Name="Z">680.54790921091296</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">195.12435752387609</Real>
+      <Real Name="Y">385.9102636952079</Real>
+      <Real Name="Z">-1022.7348792774148</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-98.501586888545091</Real>
+      <Real Name="Y">-315.8871917506425</Real>
+      <Real Name="Z">753.47953455494269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">57.215662141325573</Real>
+      <Real Name="Y">33.820335502158557</Real>
+      <Real Name="Z">254.47943806207562</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-275.425409704434</Real>
+      <Real Name="Y">-516.3349057487352</Real>
+      <Real Name="Z">-928.43422060161561</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">242.38329141163095</Real>
+      <Real Name="Y">81.441306986459239</Real>
+      <Real Name="Z">145.61677591660521</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.998187146089627</Real>
+      <Real Name="Y">258.66338696394973</Real>
+      <Real Name="Z">803.69161856440405</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-525.26166591540584</Real>
+      <Real Name="Y">-79.292331604673336</Real>
+      <Real Name="Z">-276.45941592529942</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">348.96275464285776</Real>
+      <Real Name="Y">-123.85874827898164</Real>
+      <Real Name="Z">198.63411948265446</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">256.39181137072762</Real>
+      <Real Name="Y">24.935123020089733</Real>
+      <Real Name="Z">148.64264036009445</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">549.39895276821119</Real>
+      <Real Name="Y">835.38124964672352</Real>
+      <Real Name="Z">-204.43879341418244</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-110.6453532834593</Real>
+      <Real Name="Y">-205.47015987913537</Real>
+      <Real Name="Z">-3.5253763870676096</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-438.21716503461164</Real>
+      <Real Name="Y">-867.13565400951927</Real>
+      <Real Name="Z">-68.387516102561563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1869.5232150705031</Real>
+      <Real Name="Y">-4.4329975376995776</Real>
+      <Real Name="Z">1000.0944804243272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-908.02994365244649</Real>
+      <Real Name="Y">182.1013641024166</Real>
+      <Real Name="Z">-744.57463679419095</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-436.42417071133292</Real>
+      <Real Name="Y">-250.97298223307163</Real>
+      <Real Name="Z">-376.80618281141119</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-151.76968523507779</Real>
+      <Real Name="Y">53.74377449438478</Real>
+      <Real Name="Z">129.17881816240291</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">162.30023001634703</Real>
+      <Real Name="Y">-161.22159435762234</Real>
+      <Real Name="Z">27.985001877323207</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.45821770014555</Real>
+      <Real Name="Y">-6.8146746388818027</Real>
+      <Real Name="Z">23.68396939181941</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">582.84686710530582</Real>
+      <Real Name="Y">-25.888942832811324</Real>
+      <Real Name="Z">-30.495140606351129</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-383.22635769171166</Real>
+      <Real Name="Y">184.49482980575152</Real>
+      <Real Name="Z">75.111995147161167</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-337.39917617800802</Real>
+      <Real Name="Y">287.73026815880996</Real>
+      <Real Name="Z">-52.398994882027907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.89638419117563</Real>
+      <Real Name="Y">-1787.40231393166</Real>
+      <Real Name="Z">53.697139856259966</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">126.93610545737448</Real>
+      <Real Name="Y">225.80882774901949</Real>
+      <Real Name="Z">-43.969257595254525</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.09735498073539</Real>
+      <Real Name="Y">1215.9065287753651</Real>
+      <Real Name="Z">-63.037282716512557</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">394.48057406765236</Real>
+      <Real Name="Y">1012.8258413748235</Real>
+      <Real Name="Z">893.48719999149159</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.42060934974461</Real>
+      <Real Name="Y">-626.96282770412938</Real>
+      <Real Name="Z">-600.47294606274943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.085720892501243</Real>
+      <Real Name="Y">-231.14744896068029</Real>
+      <Real Name="Z">-239.21674460465138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-212.9424065834817</Real>
+      <Real Name="Y">-279.55854821385174</Real>
+      <Real Name="Z">-324.83684409986478</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-70.590974246673667</Real>
+      <Real Name="Y">287.35314512922878</Real>
+      <Real Name="Z">254.84435712120518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-60.532722490709524</Real>
+      <Real Name="Y">120.69536739039116</Real>
+      <Real Name="Z">420.63288196845718</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">725.89069643053324</Real>
+      <Real Name="Y">-307.14182987449254</Real>
+      <Real Name="Z">-272.10453839552844</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-137.50946559924125</Real>
+      <Real Name="Y">441.85959065799284</Real>
+      <Real Name="Z">24.685829886633684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-727.15841489397917</Real>
+      <Real Name="Y">-155.63754304216673</Real>
+      <Real Name="Z">186.73110187024128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1093.96418406728</Real>
+      <Real Name="Y">1533.7141979470739</Real>
+      <Real Name="Z">-691.18888258757124</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">175.5713596063602</Real>
+      <Real Name="Y">-223.08215350693362</Real>
+      <Real Name="Z">103.29401807872563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">620.26683948104926</Real>
+      <Real Name="Y">-984.74612005677784</Real>
+      <Real Name="Z">450.36922575552882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">741.13327761551216</Real>
+      <Real Name="Y">38.880480725875842</Real>
+      <Real Name="Z">146.92354029556515</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.96563714345663</Real>
+      <Real Name="Y">89.769007462114089</Real>
+      <Real Name="Z">-35.344999616848995</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-913.02184236081052</Real>
+      <Real Name="Y">115.0889745055228</Real>
+      <Real Name="Z">-126.88047626939682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-857.20827327242478</Real>
+      <Real Name="Y">-1259.9251455446197</Real>
+      <Real Name="Z">42.608781081853067</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">722.15931228619013</Real>
+      <Real Name="Y">948.78032096854099</Real>
+      <Real Name="Z">84.146528236786182</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">74.563844284083743</Real>
+      <Real Name="Y">277.06266423325053</Real>
+      <Real Name="Z">-49.920272281682387</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.8541839742538</Real>
+      <Real Name="Y">-720.5026468952384</Real>
+      <Real Name="Z">837.90671010637743</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.87336206037145</Real>
+      <Real Name="Y">525.83381131930093</Real>
+      <Real Name="Z">-600.44767655945725</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.473131317472699</Real>
+      <Real Name="Y">87.925124225360477</Real>
+      <Real Name="Z">-131.07424489282357</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">109.19524064090176</Real>
+      <Real Name="Y">-146.4484418185454</Real>
+      <Real Name="Z">421.05289375597044</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.504550094051652</Real>
+      <Real Name="Y">248.29610526483913</Real>
+      <Real Name="Z">-104.3198092412765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-297.01364718548314</Real>
+      <Real Name="Y">363.84533469875481</Real>
+      <Real Name="Z">-22.354629233768804</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">148.74210126228604</Real>
+      <Real Name="Y">456.37712342892991</Real>
+      <Real Name="Z">-83.337368865701876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.161980254530093</Real>
+      <Real Name="Y">-172.15975240487953</Real>
+      <Real Name="Z">75.395028670823777</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.360962197908172</Real>
+      <Real Name="Y">-173.31647477517473</Real>
+      <Real Name="Z">29.129913342258753</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">102.00880506363499</Real>
+      <Real Name="Y">745.05529175910635</Real>
+      <Real Name="Z">64.491677819533379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.181331226432263</Real>
+      <Real Name="Y">-156.06081475301036</Real>
+      <Real Name="Z">-57.283299526860077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.893795593233946</Real>
+      <Real Name="Y">-712.18752664855788</Real>
+      <Real Name="Z">-7.4269912868936094</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.38937593714559</Real>
+      <Real Name="Y">-734.26937046196304</Real>
+      <Real Name="Z">-200.9215942302215</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">173.52509947284361</Real>
+      <Real Name="Y">630.88038338714762</Real>
+      <Real Name="Z">108.20328471183333</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">76.035783640983112</Real>
+      <Real Name="Y">183.68943352642037</Real>
+      <Real Name="Z">39.413285904545177</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">58.338279698810169</Real>
+      <Real Name="Y">872.56568330976438</Real>
+      <Real Name="Z">225.22942295712912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">111.15022337705804</Real>
+      <Real Name="Y">-181.75287588344531</Real>
+      <Real Name="Z">-2.5853354930909518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.35923387091816</Real>
+      <Real Name="Y">-607.7871068377799</Real>
+      <Real Name="Z">-286.12557064336085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-332.45852127564274</Real>
+      <Real Name="Y">337.43958576580081</Real>
+      <Real Name="Z">-374.35289698020642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.85998728650173</Real>
+      <Real Name="Y">-273.51732700681629</Real>
+      <Real Name="Z">423.52688743402774</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.40948852381536</Real>
+      <Real Name="Y">-137.62927803356089</Real>
+      <Real Name="Z">50.323107413175165</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-902.11190287684349</Real>
+      <Real Name="Y">-87.251112084887865</Real>
+      <Real Name="Z">-81.625888690389274</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">737.87640711375127</Real>
+      <Real Name="Y">-73.257410664809328</Real>
+      <Real Name="Z">14.834819617977068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">320.30240114869923</Real>
+      <Real Name="Y">47.346774694480857</Real>
+      <Real Name="Z">16.861284326305139</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1547.7750666257391</Real>
+      <Real Name="Y">-234.82827257568255</Real>
+      <Real Name="Z">-639.57165677717057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">304.23260498156816</Real>
+      <Real Name="Y">-54.692594710362386</Real>
+      <Real Name="Z">201.14463468925618</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1076.4939063213035</Real>
+      <Real Name="Y">319.56173278079052</Real>
+      <Real Name="Z">321.8192691688738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-528.41956130039432</Real>
+      <Real Name="Y">1446.4581993947179</Real>
+      <Real Name="Z">232.62244311463351</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">58.641963212832863</Real>
+      <Real Name="Y">-753.94064420918676</Real>
+      <Real Name="Z">-567.35706921379972</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">402.20923008473318</Real>
+      <Real Name="Y">-441.41896490835961</Real>
+      <Real Name="Z">552.20080029246674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.78465109136343</Real>
+      <Real Name="Y">430.12197149905023</Real>
+      <Real Name="Z">127.26781667339506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.720695330652717</Real>
+      <Real Name="Y">-119.7826517539508</Real>
+      <Real Name="Z">-81.974407425595402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.10103847482137</Real>
+      <Real Name="Y">-489.77284139292385</Real>
+      <Real Name="Z">-109.76592894470954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1150.6631859139054</Real>
+      <Real Name="Y">-2014.3072602579191</Real>
+      <Real Name="Z">628.69583076400488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-120.6349474378344</Real>
+      <Real Name="Y">1239.6338999781765</Real>
+      <Real Name="Z">350.85158851183718</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1025.91336490259</Real>
+      <Real Name="Y">384.27529799531607</Real>
+      <Real Name="Z">-785.1337495032642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">777.68198463484828</Real>
+      <Real Name="Y">-1300.9527006170331</Real>
+      <Real Name="Z">-314.34919413840151</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-240.3490515807685</Real>
+      <Real Name="Y">680.84177467865754</Real>
+      <Real Name="Z">36.351846271021451</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.8581740454693</Real>
+      <Real Name="Y">700.31460805730285</Real>
+      <Real Name="Z">334.14580085297223</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">79.337482261474548</Real>
+      <Real Name="Y">848.41241018771245</Real>
+      <Real Name="Z">1308.5198409927305</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.36395534293111</Real>
+      <Real Name="Y">-728.61225679380368</Real>
+      <Real Name="Z">-1002.1753065737439</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-112.31896811781378</Real>
+      <Real Name="Y">-3.2157583478410388</Real>
+      <Real Name="Z">-234.75411586390834</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.020000 Step 20 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-1891.9394566968458</Real>
+      <Real Name="Y">757.82095705812981</Real>
+      <Real Name="Z">220.48137539895541</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-54.204150814551028</Real>
+      <Real Name="Y">-74.604901248140607</Real>
+      <Real Name="Z">-93.243834429820453</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">433.81152400704423</Real>
+      <Real Name="Y">109.65205420517029</Real>
+      <Real Name="Z">-225.86912684838856</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">380.71995040700602</Real>
+      <Real Name="Y">-516.03574581574094</Real>
+      <Real Name="Z">-402.60769965161813</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4684.0102508604778</Real>
+      <Real Name="Y">590.39022785891609</Real>
+      <Real Name="Z">159.37235338914786</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2298.3257642728045</Real>
+      <Real Name="Y">-1425.4687198160254</Real>
+      <Real Name="Z">352.89888369640335</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2595.1252395163497</Real>
+      <Real Name="Y">1658.8420930350433</Real>
+      <Real Name="Z">438.99020698132665</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">855.64968110656355</Real>
+      <Real Name="Y">-500.53373371841957</Real>
+      <Real Name="Z">-117.09879338587245</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">434.62052612778388</Real>
+      <Real Name="Y">-726.65268586966215</Real>
+      <Real Name="Z">-304.11430680485563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.85243049274995997</Real>
+      <Real Name="Y">1.0375024030322031</Real>
+      <Real Name="Z">18.678676429923371</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.0947971130371741</Real>
+      <Real Name="Y">8.1685788235255821</Real>
+      <Real Name="Z">-3.9822776765981871</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.3925189856835942</Real>
+      <Real Name="Y">2.3592603182710672</Real>
+      <Real Name="Z">-10.378485384174851</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.987900099500536</Real>
+      <Real Name="Y">8.1364544209930472</Real>
+      <Real Name="Z">-26.075856471803576</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.1761420965923435</Real>
+      <Real Name="Y">-2.9518878062288501</Real>
+      <Real Name="Z">12.545242859666278</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.736062764582925</Real>
+      <Real Name="Y">-13.704689935555768</Real>
+      <Real Name="Z">11.163359698051444</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">285.19341094023434</Real>
+      <Real Name="Y">-117.13614578162272</Real>
+      <Real Name="Z">-749.88935672823391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-291.21092248216866</Real>
+      <Real Name="Y">201.36438691687951</Real>
+      <Real Name="Z">404.15006078887018</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.777162448491488</Real>
+      <Real Name="Y">9.4071456814062397</Real>
+      <Real Name="Z">174.49178291758045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">484.36300736114504</Real>
+      <Real Name="Y">725.60847486196587</Real>
+      <Real Name="Z">-187.2037549648843</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.30047317954569</Real>
+      <Real Name="Y">-272.38920245720607</Real>
+      <Real Name="Z">54.91412039273753</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-453.39638371906608</Real>
+      <Real Name="Y">-693.51547991588313</Real>
+      <Real Name="Z">264.41885069102779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">432.25720376963505</Real>
+      <Real Name="Y">-417.88721233652171</Real>
+      <Real Name="Z">-482.61489889342687</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.825607913534014</Real>
+      <Real Name="Y">143.85530013492854</Real>
+      <Real Name="Z">125.00311850944694</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-451.7016962811764</Real>
+      <Real Name="Y">414.40838540219664</Real>
+      <Real Name="Z">458.15360104491856</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">118.61457263462032</Real>
+      <Real Name="Y">228.44057844107374</Real>
+      <Real Name="Z">280.1067419373905</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.142876493858227</Real>
+      <Real Name="Y">-166.70659854707191</Real>
+      <Real Name="Z">-133.82820976404031</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-183.91208902796492</Real>
+      <Real Name="Y">-246.16935755035556</Real>
+      <Real Name="Z">-144.74181760909386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-423.39906300337185</Real>
+      <Real Name="Y">403.75856878112029</Real>
+      <Real Name="Z">870.94937021261205</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">396.33627181105771</Real>
+      <Real Name="Y">-255.35124576712573</Real>
+      <Real Name="Z">-819.14441803555519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.85704987983553</Real>
+      <Real Name="Y">-222.48939722889861</Real>
+      <Real Name="Z">-108.69316291913795</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1189.0126735432498</Real>
+      <Real Name="Y">304.53235436816715</Real>
+      <Real Name="Z">857.31318707905075</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">51.024690974096139</Real>
+      <Real Name="Y">-527.96226844868056</Real>
+      <Real Name="Z">-201.61841748227124</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-930.69236355248677</Real>
+      <Real Name="Y">505.70294188783981</Real>
+      <Real Name="Z">-535.79525278539029</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">247.58850119898869</Real>
+      <Real Name="Y">1038.2257409748418</Real>
+      <Real Name="Z">165.85141076820679</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-80.851690618217248</Real>
+      <Real Name="Y">-398.20899859802114</Real>
+      <Real Name="Z">-39.707284652753486</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-92.446924756370763</Real>
+      <Real Name="Y">-194.54326373343622</Real>
+      <Real Name="Z">-56.636687484414253</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.15980786047126</Real>
+      <Real Name="Y">35.373465775739909</Real>
+      <Real Name="Z">-1026.3820204581375</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-126.40648172307088</Real>
+      <Real Name="Y">244.56104488780784</Real>
+      <Real Name="Z">331.43503334124432</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">310.78657018901129</Real>
+      <Real Name="Y">120.78383139870968</Real>
+      <Real Name="Z">324.15909556861754</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">496.06228136751582</Real>
+      <Real Name="Y">223.99174240351329</Real>
+      <Real Name="Z">-427.91402849993602</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-524.20253907255665</Real>
+      <Real Name="Y">66.610281258027356</Real>
+      <Real Name="Z">717.71988195841993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-171.95005645349465</Real>
+      <Real Name="Y">-222.89853103294828</Real>
+      <Real Name="Z">71.178616877916852</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-969.55568905579321</Real>
+      <Real Name="Y">189.84490138765221</Real>
+      <Real Name="Z">254.95626408409515</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">588.77607496581311</Real>
+      <Real Name="Y">-319.88817012970406</Real>
+      <Real Name="Z">-157.98576626475329</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">173.5775444852535</Real>
+      <Real Name="Y">-1.4581174175264895</Real>
+      <Real Name="Z">-167.85553863641843</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-430.96890545263318</Real>
+      <Real Name="Y">90.592018050124267</Real>
+      <Real Name="Z">275.5765391859278</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">149.9626358645703</Real>
+      <Real Name="Y">-5.0486883221433629</Real>
+      <Real Name="Z">-53.923004882142671</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.12534341465135</Real>
+      <Real Name="Y">-196.74377477568268</Real>
+      <Real Name="Z">-98.902473183747645</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-152.69325275725208</Real>
+      <Real Name="Y">-1402.1624712646542</Real>
+      <Real Name="Z">-714.20858754182404</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">44.668597775348516</Real>
+      <Real Name="Y">1324.090020801932</Real>
+      <Real Name="Z">475.2859371810863</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">372.88901379844566</Real>
+      <Real Name="Y">371.81733086217878</Real>
+      <Real Name="Z">135.18778718265611</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">418.85712697870417</Real>
+      <Real Name="Y">720.7989448311946</Real>
+      <Real Name="Z">-891.43090081405921</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.88139080878736</Real>
+      <Real Name="Y">-433.90254146594748</Real>
+      <Real Name="Z">14.937436411801912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-381.49789061179376</Real>
+      <Real Name="Y">-136.98160205380239</Real>
+      <Real Name="Z">287.72338628497977</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">69.725435814597532</Real>
+      <Real Name="Y">129.93254920247847</Real>
+      <Real Name="Z">36.793319031522401</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-16.925993523200791</Real>
+      <Real Name="Y">-52.877533114061492</Real>
+      <Real Name="Z">-14.909369765045774</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-32.433023208650695</Real>
+      <Real Name="Y">-43.815184323886889</Real>
+      <Real Name="Z">-13.553575489455664</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-378.65912717621563</Real>
+      <Real Name="Y">933.50945547636036</Real>
+      <Real Name="Z">233.91873929327278</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">445.79547507609925</Real>
+      <Real Name="Y">-707.44344850568405</Real>
+      <Real Name="Z">-411.15889554765067</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">27.577256823906804</Real>
+      <Real Name="Y">-160.92760742787763</Real>
+      <Real Name="Z">-22.352695060627248</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">559.03447010880484</Real>
+      <Real Name="Y">-789.11774614466151</Real>
+      <Real Name="Z">-1240.6047631120052</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-415.92935468842228</Real>
+      <Real Name="Y">298.86060943566878</Real>
+      <Real Name="Z">974.14687438558735</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-406.6929142629437</Real>
+      <Real Name="Y">268.57291653473459</Real>
+      <Real Name="Z">83.858502279176506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">40.409984504191755</Real>
+      <Real Name="Y">-257.55073274788913</Real>
+      <Real Name="Z">24.845574816570974</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-25.430989388871495</Real>
+      <Real Name="Y">111.42967783132526</Real>
+      <Real Name="Z">34.202707277547887</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-37.187638133964022</Real>
+      <Real Name="Y">64.307980287979447</Real>
+      <Real Name="Z">129.55773221335681</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">329.89740082259055</Real>
+      <Real Name="Y">-311.49653606572429</Real>
+      <Real Name="Z">-95.788239272291932</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-311.85292038812861</Real>
+      <Real Name="Y">378.99865186441525</Real>
+      <Real Name="Z">157.46995777679655</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.069607000345144</Real>
+      <Real Name="Y">47.379348419961303</Real>
+      <Real Name="Z">52.190990633287051</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">704.26850009392217</Real>
+      <Real Name="Y">-1082.5702944642071</Real>
+      <Real Name="Z">-699.75984105620807</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.26773718502125</Real>
+      <Real Name="Y">259.58429408645662</Real>
+      <Real Name="Z">739.66664646132563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-136.0141710474926</Real>
+      <Real Name="Y">768.93358162568688</Real>
+      <Real Name="Z">234.87663796418661</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">18.774328346191361</Real>
+      <Real Name="Y">-7.0451091656048188</Real>
+      <Real Name="Z">36.087594252565381</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.4320789311763047</Real>
+      <Real Name="Y">4.4885342353184781</Real>
+      <Real Name="Z">-14.088961074179906</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.8225258513782876</Real>
+      <Real Name="Y">3.4164700022950472</Real>
+      <Real Name="Z">-20.879562474713154</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.32665512417049</Real>
+      <Real Name="Y">800.42587406671612</Real>
+      <Real Name="Z">97.519633915344698</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">484.38109377362912</Real>
+      <Real Name="Y">-393.72560624977274</Real>
+      <Real Name="Z">-514.56601001599574</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-206.04832669464051</Real>
+      <Real Name="Y">-365.00776388304081</Real>
+      <Real Name="Z">320.8958881864304</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">337.80314753999562</Real>
+      <Real Name="Y">47.303733565942956</Real>
+      <Real Name="Z">-323.6645238332552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-304.55569493005498</Real>
+      <Real Name="Y">45.672301688139676</Real>
+      <Real Name="Z">85.5746452631534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-78.917880409169186</Real>
+      <Real Name="Y">-20.896145425360487</Real>
+      <Real Name="Z">105.98844987647719</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-441.00968650655085</Real>
+      <Real Name="Y">-1012.4472763071375</Real>
+      <Real Name="Z">-483.57085278046395</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">187.45971702232498</Real>
+      <Real Name="Y">292.87890213174387</Real>
+      <Real Name="Z">-141.58326023841943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">373.77420982176477</Real>
+      <Real Name="Y">708.52047420825465</Real>
+      <Real Name="Z">620.57113310272518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-849.95096179451593</Real>
+      <Real Name="Y">325.52766732987681</Real>
+      <Real Name="Z">-503.02872289239519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">677.97555337904259</Real>
+      <Real Name="Y">-267.60835279312408</Real>
+      <Real Name="Z">483.521100949774</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">167.59695270458835</Real>
+      <Real Name="Y">-0.83910502621127137</Real>
+      <Real Name="Z">31.503317498916545</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">252.79573063097715</Real>
+      <Real Name="Y">416.99283017956793</Real>
+      <Real Name="Z">78.096161022976673</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.768201885372818</Real>
+      <Real Name="Y">-147.63766885927373</Real>
+      <Real Name="Z">-63.675097648192661</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.21509717143408</Real>
+      <Real Name="Y">-145.30431861223673</Real>
+      <Real Name="Z">11.805134552553806</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-817.41257821154556</Real>
+      <Real Name="Y">-794.79709585168689</Real>
+      <Real Name="Z">-520.80945620019793</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">149.62448407822993</Real>
+      <Real Name="Y">200.03250937720293</Real>
+      <Real Name="Z">153.96489549045097</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">706.39248799546328</Real>
+      <Real Name="Y">519.66730029313794</Real>
+      <Real Name="Z">395.47085611646798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.765108615503031</Real>
+      <Real Name="Y">-74.071055845988838</Real>
+      <Real Name="Z">-781.58037616122101</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">12.573669372667936</Real>
+      <Real Name="Y">-195.04332775605346</Real>
+      <Real Name="Z">729.83768496557468</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.670241673933859</Real>
+      <Real Name="Y">55.835149670910432</Real>
+      <Real Name="Z">154.12534671589808</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-540.57909194429885</Real>
+      <Real Name="Y">-517.44118256422598</Real>
+      <Real Name="Z">-924.7160304753462</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">125.34558035603111</Real>
+      <Real Name="Y">77.019131391078588</Real>
+      <Real Name="Z">161.48670983853143</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">248.80406004771399</Real>
+      <Real Name="Y">494.16986281063458</Real>
+      <Real Name="Z">738.18872334234754</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-506.74756384975399</Real>
+      <Real Name="Y">-369.12100493418137</Real>
+      <Real Name="Z">-167.10990926711247</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">531.14795590185872</Real>
+      <Real Name="Y">128.84779051297599</Real>
+      <Real Name="Z">21.961181842131367</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">284.0112563366871</Real>
+      <Real Name="Y">111.68725716591436</Real>
+      <Real Name="Z">67.145824547277513</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">107.70065685330044</Real>
+      <Real Name="Y">813.56956415557727</Real>
+      <Real Name="Z">-14.606793991952912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.46480033965098499</Real>
+      <Real Name="Y">-170.48171415271506</Real>
+      <Real Name="Z">-16.544643027998973</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-203.73278407875557</Real>
+      <Real Name="Y">-845.37514096273037</Real>
+      <Real Name="Z">-119.98300804941496</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1959.5175596322701</Real>
+      <Real Name="Y">-98.09254956333973</Real>
+      <Real Name="Z">894.39790198709204</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1019.0241558157674</Real>
+      <Real Name="Y">207.3999768645792</Real>
+      <Real Name="Z">-699.31316576751692</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-513.61177606506453</Real>
+      <Real Name="Y">-185.30498371126978</Real>
+      <Real Name="Z">-379.52672216970842</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-303.69414501848382</Real>
+      <Real Name="Y">67.370921036619677</Real>
+      <Real Name="Z">-99.67806313999651</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">157.70213208374821</Real>
+      <Real Name="Y">-133.9369606565372</Real>
+      <Real Name="Z">51.650680165270934</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.20478351308785</Real>
+      <Real Name="Y">-4.4560473277191761</Real>
+      <Real Name="Z">36.901071418594228</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">626.89076582530015</Real>
+      <Real Name="Y">-42.71898089287518</Real>
+      <Real Name="Z">113.87462011312633</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-629.14128557549168</Real>
+      <Real Name="Y">208.35604483057892</Real>
+      <Real Name="Z">52.113820375361875</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.65847763081842</Real>
+      <Real Name="Y">142.59367060594184</Real>
+      <Real Name="Z">-13.655636304974394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-215.21661614162082</Real>
+      <Real Name="Y">-1123.0869601906084</Real>
+      <Real Name="Z">16.359552834925012</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">94.896768779078869</Real>
+      <Real Name="Y">162.63896807723251</Real>
+      <Real Name="Z">-52.814237590445728</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">139.21955154487148</Real>
+      <Real Name="Y">1106.7580921478668</Real>
+      <Real Name="Z">30.255351209506912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">210.93405479219942</Real>
+      <Real Name="Y">1088.0111267984892</Real>
+      <Real Name="Z">799.77483960925019</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-175.88660256121835</Real>
+      <Real Name="Y">-663.38733458972695</Real>
+      <Real Name="Z">-643.92471012168119</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.287455147010775</Real>
+      <Real Name="Y">-284.48262891109175</Real>
+      <Real Name="Z">-145.60813307370472</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-200.85348893853828</Real>
+      <Real Name="Y">-250.08159294195536</Real>
+      <Real Name="Z">-210.27646133354989</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-88.905878675553481</Real>
+      <Real Name="Y">202.86507134040329</Real>
+      <Real Name="Z">-16.656284621147663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">188.36830332810075</Real>
+      <Real Name="Y">64.221155191417694</Real>
+      <Real Name="Z">339.63690255668695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.99668629917085</Real>
+      <Real Name="Y">-346.6353024060254</Real>
+      <Real Name="Z">-232.89839505959074</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-402.84318728425967</Real>
+      <Real Name="Y">436.76256254662718</Real>
+      <Real Name="Z">76.947519031410593</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-626.6144236976354</Real>
+      <Real Name="Y">-102.98106656296204</Real>
+      <Real Name="Z">220.71201553304192</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-954.1827500085584</Real>
+      <Real Name="Y">1205.1632708794989</Real>
+      <Real Name="Z">-666.58099174714334</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.543221367439</Real>
+      <Real Name="Y">-188.85475622602382</Real>
+      <Real Name="Z">111.08522485189798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">561.87688925432167</Real>
+      <Real Name="Y">-852.97280720513561</Real>
+      <Real Name="Z">533.58517724687215</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">941.08754459334455</Real>
+      <Real Name="Y">-106.5669579008235</Real>
+      <Real Name="Z">49.442487055759287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.14692026905206</Real>
+      <Real Name="Y">83.187902279087226</Real>
+      <Real Name="Z">-32.71481584675216</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-892.95909055084257</Real>
+      <Real Name="Y">-66.484093373340713</Real>
+      <Real Name="Z">-97.347958960965173</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-636.02068210924585</Real>
+      <Real Name="Y">-683.75580876778952</Real>
+      <Real Name="Z">54.425838014594007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">600.27435515411685</Real>
+      <Real Name="Y">696.31609099919388</Real>
+      <Real Name="Z">31.070767701206265</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">51.859567782356606</Real>
+      <Real Name="Y">145.997944894851</Real>
+      <Real Name="Z">-46.177967466940757</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-294.87390729406769</Real>
+      <Real Name="Y">-1116.3385448699939</Real>
+      <Real Name="Z">1220.071583668856</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">332.0899456652445</Real>
+      <Real Name="Y">618.36132896881213</Real>
+      <Real Name="Z">-559.66628468244755</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-70.914997344981373</Real>
+      <Real Name="Y">148.89790300507281</Real>
+      <Real Name="Z">-203.72275156052262</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.09624473107704</Real>
+      <Real Name="Y">-491.38194639781943</Real>
+      <Real Name="Z">57.995332377932939</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-87.022960846561872</Real>
+      <Real Name="Y">272.85664251418018</Real>
+      <Real Name="Z">-82.340349648573635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-373.9722798007391</Real>
+      <Real Name="Y">357.57461333244072</Real>
+      <Real Name="Z">-110.77534534085243</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-81.982065373858475</Real>
+      <Real Name="Y">410.43820641429772</Real>
+      <Real Name="Z">-16.240842769280121</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.865428181301318</Real>
+      <Real Name="Y">-213.81045963009296</Real>
+      <Real Name="Z">45.446770980847873</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.4953008513433161</Real>
+      <Real Name="Y">-118.54950755934672</Real>
+      <Real Name="Z">7.5173818455674599</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">11.494272952258726</Real>
+      <Real Name="Y">753.03474371314064</Real>
+      <Real Name="Z">54.40049728562262</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.859102631939429</Real>
+      <Real Name="Y">-172.20817408249735</Real>
+      <Real Name="Z">-51.66908387967878</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">168.30898411668792</Real>
+      <Real Name="Y">-648.14778193227141</Real>
+      <Real Name="Z">-40.918754336625867</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-118.72860309751381</Real>
+      <Real Name="Y">-686.93236025444457</Real>
+      <Real Name="Z">-273.02133994662466</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">209.63310468263379</Real>
+      <Real Name="Y">600.59369253635873</Real>
+      <Real Name="Z">157.68339055046286</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">96.460742328895932</Real>
+      <Real Name="Y">156.77230368056684</Real>
+      <Real Name="Z">38.589189820790295</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">115.36104779381104</Real>
+      <Real Name="Y">899.46000203520339</Real>
+      <Real Name="Z">345.23461320234901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">46.607627195563516</Real>
+      <Real Name="Y">-209.23477789203304</Real>
+      <Real Name="Z">-49.144354504784275</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-174.27892672217502</Real>
+      <Real Name="Y">-613.89113963289583</Real>
+      <Real Name="Z">-382.73340482297647</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-262.51508625417591</Real>
+      <Real Name="Y">210.01961319601162</Real>
+      <Real Name="Z">-326.82733498530519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">122.68271949535773</Real>
+      <Real Name="Y">-118.67777628006155</Real>
+      <Real Name="Z">346.75019896630562</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.62549075426148</Real>
+      <Real Name="Y">-88.535269735872902</Real>
+      <Real Name="Z">82.891605310968174</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1006.4848078789181</Real>
+      <Real Name="Y">13.130887403042863</Real>
+      <Real Name="Z">-54.45743886624139</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">734.34436557488436</Real>
+      <Real Name="Y">-141.09032673258019</Real>
+      <Real Name="Z">-15.98380561074454</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">337.90508429785405</Real>
+      <Real Name="Y">-9.1309184494295792</Real>
+      <Real Name="Z">16.686119856725597</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1542.5174734093671</Real>
+      <Real Name="Y">-877.70177386482101</Real>
+      <Real Name="Z">-445.58193083405871</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">341.97771321472578</Real>
+      <Real Name="Y">66.288298798817593</Real>
+      <Real Name="Z">131.48667861700943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1194.2221908789656</Real>
+      <Real Name="Y">209.50408465054053</Real>
+      <Real Name="Z">332.60940407261876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.04472204740966</Real>
+      <Real Name="Y">1486.221798743353</Real>
+      <Real Name="Z">26.599568136828609</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-43.060083297055442</Real>
+      <Real Name="Y">-805.63340496780756</Real>
+      <Real Name="Z">-426.0867276270946</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">214.76532063940292</Real>
+      <Real Name="Y">-656.77137294518457</Real>
+      <Real Name="Z">609.76725836903188</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">242.15857154063585</Real>
+      <Real Name="Y">507.16539928210483</Real>
+      <Real Name="Z">160.3680011218774</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.92409880614683</Real>
+      <Real Name="Y">-131.21134570659163</Real>
+      <Real Name="Z">-105.4305896435794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-257.85787623549135</Real>
+      <Real Name="Y">-514.05652398033112</Real>
+      <Real Name="Z">-75.941349604679033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">783.77901759503368</Real>
+      <Real Name="Y">-1644.8860174895688</Real>
+      <Real Name="Z">820.56443919988442</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-82.77838621151588</Real>
+      <Real Name="Y">1167.5287930020056</Real>
+      <Real Name="Z">104.36502148784415</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-939.01249426181528</Real>
+      <Real Name="Y">560.46058684885054</Real>
+      <Real Name="Z">-728.32083923687651</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">793.73134515505103</Real>
+      <Real Name="Y">-1958.7679196499471</Real>
+      <Real Name="Z">-662.16450417275678</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-54.936134706823935</Real>
+      <Real Name="Y">833.0849057031204</Real>
+      <Real Name="Z">201.90545300810635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-547.19115834047375</Real>
+      <Real Name="Y">671.55485567541223</Real>
+      <Real Name="Z">427.74786991549757</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">102.09235175264436</Real>
+      <Real Name="Y">752.84299596751782</Real>
+      <Real Name="Z">1182.0637675150479</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.74438179584189</Real>
+      <Real Name="Y">-585.91654748504357</Real>
+      <Real Name="Z">-1009.0262778943103</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.644153602421454</Real>
+      <Real Name="Y">-94.712244173822214</Real>
+      <Real Name="Z">-263.43384115048605</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_position_restraints_s.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_position_restraints_s.xml
new file mode 100644 (file)
index 0000000..72e76db
--- /dev/null
@@ -0,0 +1,1030 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVvdw/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-2.082541</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1.690289</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1.7480047</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1.794722</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1.8256403</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-2.2802331</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1.8215176</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1.781659</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1.7177429</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1.6334397</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-2.1025834</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1.4256872</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1.3150027</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1.208648</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1.112336</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1.6537563</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-0.96472448</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-0.91591078</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-0.88288939</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-0.86436623</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1.3098637</Real>
+  </Energy>
+  <Energy Name="dVcoul/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">127.00418</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">16.050568</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">15.600407</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">14.819275</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">13.731117</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">124.31299</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">10.975838</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">9.5288887</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">8.1561584</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">6.8979454</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">119.52313</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">4.6690788</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">3.61306</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">2.5466003</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">1.4723396</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">115.40277</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-0.51757431</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1.3218994</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1.9717598</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-2.5118027</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">111.22563</Real>
+  </Energy>
+  <Energy Name="dVremain/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">0</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">0</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">0</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">0</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">0</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">0</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">0</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">0</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">0</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">0</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">0</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">0</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">0</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">0</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">0</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">0</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">0</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">0</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">0</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">0</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">0</Real>
+  </Energy>
+  <Energy Name="dVrestraint/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-0.0017845253</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-0.20649473</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-0.88067615</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-2.0502455</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-3.6400084</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-5.480237</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-7.3483477</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-9.025548</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-10.344435</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-11.217644</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-11.64473</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-11.701057</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-11.512264</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-11.217334</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-10.925056</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-10.675376</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-10.427979</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-10.09029</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-9.5785789</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-8.8789015</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-8.0765057</Real>
+  </Energy>
+  <Energy Name="dVbonded/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">46.032852</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">0</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">0</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">0</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">0</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">6.1912212</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">0</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">0</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">0</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">0</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-20.667356</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">0</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">0</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">0</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">0</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-5.12956</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">0</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">0</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">0</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">0</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">23.328701</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1400.7201</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1405.4028</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1408.8904</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1408.9294</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1404.5394</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1396.3817</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1386.3115</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1376.5608</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1369.0336</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1364.8645</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1364.1364</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1365.8198</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1367.9717</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1368.2578</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1364.8607</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1357.4196</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1347.5947</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1338.5616</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1333.6937</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1335.1055</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1342.8116</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">424.13037</Real>
+      <Real Name="Y">-929.06659</Real>
+      <Real Name="Z">-218.03624</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.4519</Real>
+      <Real Name="Y">-268.49139</Real>
+      <Real Name="Z">74.149826</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-412.97458</Real>
+      <Real Name="Y">284.78308</Real>
+      <Real Name="Z">-165.28256</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.100174</Real>
+      <Real Name="Y">151.32135</Real>
+      <Real Name="Z">152.34134</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3730.3813</Real>
+      <Real Name="Y">1969.6967</Real>
+      <Real Name="Z">1181.4695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-351.26654</Real>
+      <Real Name="Y">-121.77042</Real>
+      <Real Name="Z">-418.02386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4348</Real>
+      <Real Name="Y">-269.23038</Real>
+      <Real Name="Z">85.698532</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.49936</Real>
+      <Real Name="Y">-475.32684</Real>
+      <Real Name="Z">-614.36395</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.86035</Real>
+      <Real Name="Y">-579.61646</Real>
+      <Real Name="Z">-238.10861</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.2445602</Real>
+      <Real Name="Y">-34.355072</Real>
+      <Real Name="Z">23.921921</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.4559174</Real>
+      <Real Name="Y">29.482897</Real>
+      <Real Name="Z">-14.058651</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.5512276</Real>
+      <Real Name="Y">16.100752</Real>
+      <Real Name="Z">-5.951416</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.7422943</Real>
+      <Real Name="Y">20.844883</Real>
+      <Real Name="Z">-6.4865875</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.0197372</Real>
+      <Real Name="Y">-8.362711</Real>
+      <Real Name="Z">4.33218</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.7708168</Real>
+      <Real Name="Y">-23.386061</Real>
+      <Real Name="Z">3.9186287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">362.79291</Real>
+      <Real Name="Y">-56.650917</Real>
+      <Real Name="Z">-669.7403</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.68362</Real>
+      <Real Name="Y">34.759609</Real>
+      <Real Name="Z">384.98669</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.033577</Real>
+      <Real Name="Y">-12.772404</Real>
+      <Real Name="Z">205.99945</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">410.18701</Real>
+      <Real Name="Y">464.38705</Real>
+      <Real Name="Z">-110.42412</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-59.870171</Real>
+      <Real Name="Y">-251.50352</Real>
+      <Real Name="Z">88.595398</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-384.36209</Real>
+      <Real Name="Y">-626.99774</Real>
+      <Real Name="Z">249.60416</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">642.99744</Real>
+      <Real Name="Y">-494.17081</Real>
+      <Real Name="Z">-430.29688</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.95184</Real>
+      <Real Name="Y">122.95309</Real>
+      <Real Name="Z">78.58226</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.26801</Real>
+      <Real Name="Y">440.16956</Real>
+      <Real Name="Z">225.40709</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.541372</Real>
+      <Real Name="Y">305.03467</Real>
+      <Real Name="Z">57.957245</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.081581</Real>
+      <Real Name="Y">-168.25285</Real>
+      <Real Name="Z">-107.57098</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.50868</Real>
+      <Real Name="Y">-218.75137</Real>
+      <Real Name="Z">-135.96228</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-606.11365</Real>
+      <Real Name="Y">246.93079</Real>
+      <Real Name="Z">825.49219</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">403.71289</Real>
+      <Real Name="Y">-44.356895</Real>
+      <Real Name="Z">-860.78607</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.76076</Real>
+      <Real Name="Y">-180.97076</Real>
+      <Real Name="Z">-58.709053</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1061.3418</Real>
+      <Real Name="Y">551.20654</Real>
+      <Real Name="Z">854.93945</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-147.2556</Real>
+      <Real Name="Y">-835.58936</Real>
+      <Real Name="Z">-355.07379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-825.12958</Real>
+      <Real Name="Y">327.57089</Real>
+      <Real Name="Z">-570.35126</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">350.7019</Real>
+      <Real Name="Y">765.85352</Real>
+      <Real Name="Z">138.52423</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.64937592</Real>
+      <Real Name="Y">-658.98151</Real>
+      <Real Name="Z">-49.809052</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-87.496117</Real>
+      <Real Name="Y">-135.70905</Real>
+      <Real Name="Z">-34.035107</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.76944</Real>
+      <Real Name="Y">-120.23197</Real>
+      <Real Name="Z">-943.26862</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-78.316757</Real>
+      <Real Name="Y">8.0969143</Real>
+      <Real Name="Z">298.2359</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">406.40942</Real>
+      <Real Name="Y">-11.196533</Real>
+      <Real Name="Z">705.65143</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">108.87605</Real>
+      <Real Name="Y">24.124664</Real>
+      <Real Name="Z">-893.56519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-260.11182</Real>
+      <Real Name="Y">182.00764</Real>
+      <Real Name="Z">499.39603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.558397</Real>
+      <Real Name="Y">-188.92715</Real>
+      <Real Name="Z">177.98073</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-642.7525</Real>
+      <Real Name="Y">79.994873</Real>
+      <Real Name="Z">346.34875</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">550.25348</Real>
+      <Real Name="Y">-61.963356</Real>
+      <Real Name="Z">-0.9886117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.91251</Real>
+      <Real Name="Y">-34.978371</Real>
+      <Real Name="Z">-237.92163</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.27303</Real>
+      <Real Name="Y">43.531723</Real>
+      <Real Name="Z">359.45148</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">252.78021</Real>
+      <Real Name="Y">-6.6683807</Real>
+      <Real Name="Z">-124.66398</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">63.506512</Real>
+      <Real Name="Y">-117.28554</Real>
+      <Real Name="Z">-113.51183</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-308.45471</Real>
+      <Real Name="Y">-2131.4163</Real>
+      <Real Name="Z">-637.20618</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">316.44019</Real>
+      <Real Name="Y">1351.8142</Real>
+      <Real Name="Z">272.17737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">250.45816</Real>
+      <Real Name="Y">446.76724</Real>
+      <Real Name="Z">49.137497</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">456.77097</Real>
+      <Real Name="Y">934.49329</Real>
+      <Real Name="Z">-928.82465</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.3992</Real>
+      <Real Name="Y">-518.15363</Real>
+      <Real Name="Z">256.07693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-291.35953</Real>
+      <Real Name="Y">-339.49649</Real>
+      <Real Name="Z">612.54138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.381153</Real>
+      <Real Name="Y">99.349945</Real>
+      <Real Name="Z">5.8478966</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.270096</Real>
+      <Real Name="Y">-43.419426</Real>
+      <Real Name="Z">-3.891737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.806</Real>
+      <Real Name="Y">-33.154427</Real>
+      <Real Name="Z">8.3258266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-451.3829</Real>
+      <Real Name="Y">981.10052</Real>
+      <Real Name="Z">389.99854</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">613.2962</Real>
+      <Real Name="Y">-691.22693</Real>
+      <Real Name="Z">-449.54675</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">41.708832</Real>
+      <Real Name="Y">-156.46068</Real>
+      <Real Name="Z">-14.127186</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">233.46259</Real>
+      <Real Name="Y">-619.36865</Real>
+      <Real Name="Z">-1448.7887</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-82.950478</Real>
+      <Real Name="Y">421.2525</Real>
+      <Real Name="Z">1032.1033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-339.18326</Real>
+      <Real Name="Y">107.47547</Real>
+      <Real Name="Z">276.15564</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.72714</Real>
+      <Real Name="Y">-120.86164</Real>
+      <Real Name="Z">-118.21888</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-84.895645</Real>
+      <Real Name="Y">123.81021</Real>
+      <Real Name="Z">73.019348</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.06443</Real>
+      <Real Name="Y">39.107323</Real>
+      <Real Name="Z">151.49506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">323.79465</Real>
+      <Real Name="Y">-346.61063</Real>
+      <Real Name="Z">-65.820663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-286.57678</Real>
+      <Real Name="Y">418.9819</Real>
+      <Real Name="Z">92.445145</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.09538</Real>
+      <Real Name="Y">70.806259</Real>
+      <Real Name="Z">37.011559</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.57227</Real>
+      <Real Name="Y">-854.87329</Real>
+      <Real Name="Z">-613.3963</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-324.74033</Real>
+      <Real Name="Y">337.76242</Real>
+      <Real Name="Z">608.74756</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-337.20142</Real>
+      <Real Name="Y">857.46802</Real>
+      <Real Name="Z">242.82489</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.001099</Real>
+      <Real Name="Y">-4.4267044</Real>
+      <Real Name="Z">16.163025</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.370409</Real>
+      <Real Name="Y">4.8398285</Real>
+      <Real Name="Z">-6.4390068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.3214684</Real>
+      <Real Name="Y">1.2228508</Real>
+      <Real Name="Z">-11.723293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-445.03665</Real>
+      <Real Name="Y">891.17529</Real>
+      <Real Name="Z">239.0415</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">303.1264</Real>
+      <Real Name="Y">-493.37445</Real>
+      <Real Name="Z">-675.42316</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.41846</Real>
+      <Real Name="Y">-463.435</Real>
+      <Real Name="Z">304.97806</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.65903</Real>
+      <Real Name="Y">26.94772</Real>
+      <Real Name="Z">-153.63907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-270.52438</Real>
+      <Real Name="Y">60.771255</Real>
+      <Real Name="Z">40.796944</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-89.682449</Real>
+      <Real Name="Y">-36.206326</Real>
+      <Real Name="Z">75.430252</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-651.4967</Real>
+      <Real Name="Y">-1244.5917</Real>
+      <Real Name="Z">-371.00064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.34375</Real>
+      <Real Name="Y">609.0177</Real>
+      <Real Name="Z">-251.63367</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">414.07272</Real>
+      <Real Name="Y">623.66431</Real>
+      <Real Name="Z">628.41541</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1188.7797</Real>
+      <Real Name="Y">442.67041</Real>
+      <Real Name="Z">-788.13727</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">817.78217</Real>
+      <Real Name="Y">-311.03143</Real>
+      <Real Name="Z">570.49469</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.5914</Real>
+      <Real Name="Y">-31.578869</Real>
+      <Real Name="Z">36.633339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">220.58603</Real>
+      <Real Name="Y">411.63208</Real>
+      <Real Name="Z">169.6255</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.00457</Real>
+      <Real Name="Y">-124.08469</Real>
+      <Real Name="Z">-74.872147</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.66323</Real>
+      <Real Name="Y">-114.64688</Real>
+      <Real Name="Z">-20.035828</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1048.8573</Real>
+      <Real Name="Y">-1080.8726</Real>
+      <Real Name="Z">-855.7439</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.9653</Real>
+      <Real Name="Y">212.71719</Real>
+      <Real Name="Z">194.14815</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">577.57379</Real>
+      <Real Name="Y">694.10461</Real>
+      <Real Name="Z">680.54767</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">195.12628</Real>
+      <Real Name="Y">385.91028</Real>
+      <Real Name="Z">-1022.7339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-98.5019</Real>
+      <Real Name="Y">-315.88672</Real>
+      <Real Name="Z">753.47876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">57.214703</Real>
+      <Real Name="Y">33.820816</Real>
+      <Real Name="Z">254.48004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-275.42355</Real>
+      <Real Name="Y">-516.33722</Real>
+      <Real Name="Z">-928.43738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">242.38168</Real>
+      <Real Name="Y">81.442039</Real>
+      <Real Name="Z">145.61739</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.995934</Real>
+      <Real Name="Y">258.66385</Real>
+      <Real Name="Z">803.6911</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-525.26172</Real>
+      <Real Name="Y">-79.289062</Real>
+      <Real Name="Z">-276.45883</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">348.96231</Real>
+      <Real Name="Y">-123.86012</Real>
+      <Real Name="Z">198.63373</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">256.39117</Real>
+      <Real Name="Y">24.934326</Real>
+      <Real Name="Z">148.64253</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">549.39771</Real>
+      <Real Name="Y">835.38403</Real>
+      <Real Name="Z">-204.43706</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-110.64467</Real>
+      <Real Name="Y">-205.47003</Real>
+      <Real Name="Z">-3.5251312</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-438.21573</Real>
+      <Real Name="Y">-867.13654</Real>
+      <Real Name="Z">-68.388275</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1869.5262</Real>
+      <Real Name="Y">-4.4375153</Real>
+      <Real Name="Z">1000.0947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-908.02954</Real>
+      <Real Name="Y">182.10251</Real>
+      <Real Name="Z">-744.57355</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-436.42453</Real>
+      <Real Name="Y">-250.97232</Real>
+      <Real Name="Z">-376.80621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-151.77028</Real>
+      <Real Name="Y">53.74472</Real>
+      <Real Name="Z">129.17867</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">162.30051</Real>
+      <Real Name="Y">-161.2215</Real>
+      <Real Name="Z">27.985413</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.45837</Real>
+      <Real Name="Y">-6.8156757</Real>
+      <Real Name="Z">23.68433</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">582.84436</Real>
+      <Real Name="Y">-25.891251</Real>
+      <Real Name="Z">-30.497711</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-383.22455</Real>
+      <Real Name="Y">184.4962</Real>
+      <Real Name="Z">75.112961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-337.39856</Real>
+      <Real Name="Y">287.7326</Real>
+      <Real Name="Z">-52.399063</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.89984</Real>
+      <Real Name="Y">-1787.3977</Real>
+      <Real Name="Z">53.69838</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">126.9366</Real>
+      <Real Name="Y">225.80818</Real>
+      <Real Name="Z">-43.969959</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.09608</Real>
+      <Real Name="Y">1215.9032</Real>
+      <Real Name="Z">-63.037903</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">394.47723</Real>
+      <Real Name="Y">1012.8233</Real>
+      <Real Name="Z">893.48645</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.41998</Real>
+      <Real Name="Y">-626.96124</Real>
+      <Real Name="Z">-600.47321</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.085037</Real>
+      <Real Name="Y">-231.14702</Real>
+      <Real Name="Z">-239.21695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-212.94547</Real>
+      <Real Name="Y">-279.55511</Real>
+      <Real Name="Z">-324.83905</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-70.588692</Real>
+      <Real Name="Y">287.35043</Real>
+      <Real Name="Z">254.845</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-60.530048</Real>
+      <Real Name="Y">120.69285</Real>
+      <Real Name="Z">420.63379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">725.88751</Real>
+      <Real Name="Y">-307.1409</Real>
+      <Real Name="Z">-272.10919</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-137.50859</Real>
+      <Real Name="Y">441.86047</Real>
+      <Real Name="Z">24.686087</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-727.15839</Real>
+      <Real Name="Y">-155.63658</Real>
+      <Real Name="Z">186.7317</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1093.9655</Real>
+      <Real Name="Y">1533.715</Real>
+      <Real Name="Z">-691.18982</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">175.57156</Real>
+      <Real Name="Y">-223.08231</Real>
+      <Real Name="Z">103.29411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">620.26862</Real>
+      <Real Name="Y">-984.74414</Real>
+      <Real Name="Z">450.36731</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">741.13483</Real>
+      <Real Name="Y">38.879974</Real>
+      <Real Name="Z">146.92383</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.96646</Real>
+      <Real Name="Y">89.768219</Real>
+      <Real Name="Z">-35.34536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-913.02075</Real>
+      <Real Name="Y">115.08902</Real>
+      <Real Name="Z">-126.87984</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-857.20667</Real>
+      <Real Name="Y">-1259.9255</Real>
+      <Real Name="Z">42.607849</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">722.15955</Real>
+      <Real Name="Y">948.77881</Real>
+      <Real Name="Z">84.147682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">74.563354</Real>
+      <Real Name="Y">277.06317</Real>
+      <Real Name="Z">-49.920128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.85213</Real>
+      <Real Name="Y">-720.50562</Real>
+      <Real Name="Z">837.90741</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.87125</Real>
+      <Real Name="Y">525.83551</Real>
+      <Real Name="Z">-600.44781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.473778</Real>
+      <Real Name="Y">87.925758</Real>
+      <Real Name="Z">-131.0746</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">109.19949</Real>
+      <Real Name="Y">-146.44742</Real>
+      <Real Name="Z">421.05402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.505798</Real>
+      <Real Name="Y">248.29674</Real>
+      <Real Name="Z">-104.31991</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-297.01517</Real>
+      <Real Name="Y">363.84476</Real>
+      <Real Name="Z">-22.355301</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">148.74178</Real>
+      <Real Name="Y">456.37631</Real>
+      <Real Name="Z">-83.338066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.161804</Real>
+      <Real Name="Y">-172.15941</Real>
+      <Real Name="Z">75.395798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.360275</Real>
+      <Real Name="Y">-173.31651</Real>
+      <Real Name="Z">29.130035</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">102.00889</Real>
+      <Real Name="Y">745.05597</Real>
+      <Real Name="Z">64.491379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.181259</Real>
+      <Real Name="Y">-156.06075</Real>
+      <Real Name="Z">-57.283104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.893944</Real>
+      <Real Name="Y">-712.18811</Real>
+      <Real Name="Z">-7.4268026</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.3893</Real>
+      <Real Name="Y">-734.27057</Real>
+      <Real Name="Z">-200.9213</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">173.52431</Real>
+      <Real Name="Y">630.88098</Real>
+      <Real Name="Z">108.20341</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">76.035477</Real>
+      <Real Name="Y">183.69014</Real>
+      <Real Name="Z">39.413216</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">58.336136</Real>
+      <Real Name="Y">872.55988</Real>
+      <Real Name="Z">225.22746</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">111.15114</Real>
+      <Real Name="Y">-181.75214</Real>
+      <Real Name="Z">-2.5851746</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.3587</Real>
+      <Real Name="Y">-607.7851</Real>
+      <Real Name="Z">-286.12512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-332.45923</Real>
+      <Real Name="Y">337.44016</Real>
+      <Real Name="Z">-374.3541</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.85983</Real>
+      <Real Name="Y">-273.51819</Real>
+      <Real Name="Z">423.52649</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.40991</Real>
+      <Real Name="Y">-137.62949</Real>
+      <Real Name="Z">50.323502</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-902.11572</Real>
+      <Real Name="Y">-87.249344</Real>
+      <Real Name="Z">-81.627998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">737.87585</Real>
+      <Real Name="Y">-73.258194</Real>
+      <Real Name="Z">14.835564</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">320.30322</Real>
+      <Real Name="Y">47.346237</Real>
+      <Real Name="Z">16.861229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1547.77</Real>
+      <Real Name="Y">-234.82103</Real>
+      <Real Name="Z">-639.56757</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">304.23312</Real>
+      <Real Name="Y">-54.692505</Real>
+      <Real Name="Z">201.14395</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1076.4922</Real>
+      <Real Name="Y">319.56036</Real>
+      <Real Name="Z">321.81799</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-528.41687</Real>
+      <Real Name="Y">1446.4619</Real>
+      <Real Name="Z">232.62315</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">58.640671</Real>
+      <Real Name="Y">-753.94086</Real>
+      <Real Name="Z">-567.35651</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">402.20844</Real>
+      <Real Name="Y">-441.41986</Real>
+      <Real Name="Z">552.20056</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.78329</Real>
+      <Real Name="Y">430.12061</Real>
+      <Real Name="Z">127.26671</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.720016</Real>
+      <Real Name="Y">-119.7822</Real>
+      <Real Name="Z">-81.973885</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.10016</Real>
+      <Real Name="Y">-489.77142</Real>
+      <Real Name="Z">-109.76485</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1150.6566</Real>
+      <Real Name="Y">-2014.3123</Real>
+      <Real Name="Z">628.68512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-120.6366</Real>
+      <Real Name="Y">1239.6359</Real>
+      <Real Name="Z">350.8512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1025.9114</Real>
+      <Real Name="Y">384.27838</Real>
+      <Real Name="Z">-785.13135</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">777.67432</Real>
+      <Real Name="Y">-1300.9568</Real>
+      <Real Name="Z">-314.34891</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-240.34583</Real>
+      <Real Name="Y">680.84369</Real>
+      <Real Name="Z">36.351688</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.8537</Real>
+      <Real Name="Y">700.31494</Real>
+      <Real Name="Z">334.14511</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">79.341072</Real>
+      <Real Name="Y">848.40503</Real>
+      <Real Name="Z">1308.5204</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.36238</Real>
+      <Real Name="Y">-728.60944</Real>
+      <Real Name="Z">-1002.1747</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-112.31825</Real>
+      <Real Name="Y">-3.2144241</Real>
+      <Real Name="Z">-234.75362</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_s.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_relative_s.xml
new file mode 100644 (file)
index 0000000..8a9d13d
--- /dev/null
@@ -0,0 +1,1007 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVvdw/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-2.082541</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1.6902883</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1.748045</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1.7949219</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1.8260609</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-2.2802114</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1.8226143</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1.7829289</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1.7188692</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1.6339169</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-2.1007884</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1.4226649</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1.3087124</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1.1978852</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1.0959556</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1.6449119</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-0.93450248</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-0.8779279</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-0.8368327</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-0.80984849</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1.2692058</Real>
+  </Energy>
+  <Energy Name="dVcoul/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">127.00418</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">16.050507</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">15.599457</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">14.815212</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">13.718929</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">124.29243</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">10.927818</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">9.4517059</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">8.045948</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">6.7523727</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">119.38031</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">4.466011</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">3.3890419</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">2.3035088</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">1.2033424</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">115.1244</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-0.88214111</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1.7516479</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-2.4650345</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-3.0488167</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">110.83957</Real>
+  </Energy>
+  <Energy Name="dVremain/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">0</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">0</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">0</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">0</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">0</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">0</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">0</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">0</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">0</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">0</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">0</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">0</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">0</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">0</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">0</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">0</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">0</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">0</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">0</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">0</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">0</Real>
+  </Energy>
+  <Energy Name="dVbonded/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">46.032852</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">0</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">0</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">0</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">0</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">6.3042445</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">0</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">0</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">0</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">0</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-19.700371</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">0</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">0</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">0</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">0</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-5.8039846</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">0</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">0</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">0</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">0</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">23.937115</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1400.7205</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1405.4465</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1409.0756</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1409.3372</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1405.1937</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1397.2362</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1387.2661</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1377.4817</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1369.802</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1365.4136</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1364.4796</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1366.0717</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1368.3364</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1368.9821</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1366.0901</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1359.0613</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1349.2681</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1339.7316</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1333.927</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1334.2871</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1341.2007</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">424.20282</Real>
+      <Real Name="Y">-929.02716</Real>
+      <Real Name="Z">-218.00702</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.90218</Real>
+      <Real Name="Y">-268.17792</Real>
+      <Real Name="Z">74.185158</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-413.1037</Real>
+      <Real Name="Y">284.20233</Real>
+      <Real Name="Z">-165.78436</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.386009</Real>
+      <Real Name="Y">151.12294</Real>
+      <Real Name="Z">152.45912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3730.3813</Real>
+      <Real Name="Y">1969.6967</Real>
+      <Real Name="Z">1181.4695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-351.26654</Real>
+      <Real Name="Y">-121.77042</Real>
+      <Real Name="Z">-418.02386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4347.9854</Real>
+      <Real Name="Y">-269.19733</Real>
+      <Real Name="Z">85.659576</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.25156</Real>
+      <Real Name="Y">-475.65027</Real>
+      <Real Name="Z">-614.24231</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.9043</Real>
+      <Real Name="Y">-579.75732</Real>
+      <Real Name="Z">-237.68959</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.2445602</Real>
+      <Real Name="Y">-34.355072</Real>
+      <Real Name="Z">23.921921</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.4559174</Real>
+      <Real Name="Y">29.482897</Real>
+      <Real Name="Z">-14.058651</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.5512276</Real>
+      <Real Name="Y">16.100752</Real>
+      <Real Name="Z">-5.951416</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.7422943</Real>
+      <Real Name="Y">20.844883</Real>
+      <Real Name="Z">-6.4865875</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.0197372</Real>
+      <Real Name="Y">-8.362711</Real>
+      <Real Name="Z">4.33218</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.7708168</Real>
+      <Real Name="Y">-23.386061</Real>
+      <Real Name="Z">3.9186287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">362.79291</Real>
+      <Real Name="Y">-56.650917</Real>
+      <Real Name="Z">-669.7403</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.68362</Real>
+      <Real Name="Y">34.759609</Real>
+      <Real Name="Z">384.98669</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.033577</Real>
+      <Real Name="Y">-12.772404</Real>
+      <Real Name="Z">205.99945</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">410.18701</Real>
+      <Real Name="Y">464.38705</Real>
+      <Real Name="Z">-110.42412</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-59.870171</Real>
+      <Real Name="Y">-251.50352</Real>
+      <Real Name="Z">88.595398</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-384.36209</Real>
+      <Real Name="Y">-626.99774</Real>
+      <Real Name="Z">249.60416</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">642.99744</Real>
+      <Real Name="Y">-494.17081</Real>
+      <Real Name="Z">-430.29688</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.95184</Real>
+      <Real Name="Y">122.95309</Real>
+      <Real Name="Z">78.58226</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.26801</Real>
+      <Real Name="Y">440.16956</Real>
+      <Real Name="Z">225.40709</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.541372</Real>
+      <Real Name="Y">305.03467</Real>
+      <Real Name="Z">57.957245</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.081581</Real>
+      <Real Name="Y">-168.25285</Real>
+      <Real Name="Z">-107.57098</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.50868</Real>
+      <Real Name="Y">-218.75137</Real>
+      <Real Name="Z">-135.96228</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-606.11365</Real>
+      <Real Name="Y">246.93079</Real>
+      <Real Name="Z">825.49219</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">403.71289</Real>
+      <Real Name="Y">-44.356895</Real>
+      <Real Name="Z">-860.78607</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.76076</Real>
+      <Real Name="Y">-180.97076</Real>
+      <Real Name="Z">-58.709053</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1061.3418</Real>
+      <Real Name="Y">551.20654</Real>
+      <Real Name="Z">854.93945</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-147.2556</Real>
+      <Real Name="Y">-835.58936</Real>
+      <Real Name="Z">-355.07379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-825.12958</Real>
+      <Real Name="Y">327.57089</Real>
+      <Real Name="Z">-570.35126</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">350.7019</Real>
+      <Real Name="Y">765.85352</Real>
+      <Real Name="Z">138.52423</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.64937592</Real>
+      <Real Name="Y">-658.98151</Real>
+      <Real Name="Z">-49.809052</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-87.496117</Real>
+      <Real Name="Y">-135.70905</Real>
+      <Real Name="Z">-34.035107</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.76944</Real>
+      <Real Name="Y">-120.23197</Real>
+      <Real Name="Z">-943.26862</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-78.316757</Real>
+      <Real Name="Y">8.0969143</Real>
+      <Real Name="Z">298.2359</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">406.40942</Real>
+      <Real Name="Y">-11.196533</Real>
+      <Real Name="Z">705.65143</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">108.87605</Real>
+      <Real Name="Y">24.124664</Real>
+      <Real Name="Z">-893.56519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-260.11182</Real>
+      <Real Name="Y">182.00764</Real>
+      <Real Name="Z">499.39603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.558397</Real>
+      <Real Name="Y">-188.92715</Real>
+      <Real Name="Z">177.98073</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-642.7525</Real>
+      <Real Name="Y">79.994873</Real>
+      <Real Name="Z">346.34875</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">550.25348</Real>
+      <Real Name="Y">-61.963356</Real>
+      <Real Name="Z">-0.9886117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.91251</Real>
+      <Real Name="Y">-34.978371</Real>
+      <Real Name="Z">-237.92163</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.27303</Real>
+      <Real Name="Y">43.531723</Real>
+      <Real Name="Z">359.45148</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">252.78021</Real>
+      <Real Name="Y">-6.6683807</Real>
+      <Real Name="Z">-124.66398</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">63.506512</Real>
+      <Real Name="Y">-117.28554</Real>
+      <Real Name="Z">-113.51183</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-308.45471</Real>
+      <Real Name="Y">-2131.4163</Real>
+      <Real Name="Z">-637.20618</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">316.44019</Real>
+      <Real Name="Y">1351.8142</Real>
+      <Real Name="Z">272.17737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">250.45816</Real>
+      <Real Name="Y">446.76724</Real>
+      <Real Name="Z">49.137497</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">456.77097</Real>
+      <Real Name="Y">934.49329</Real>
+      <Real Name="Z">-928.82465</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.3992</Real>
+      <Real Name="Y">-518.15363</Real>
+      <Real Name="Z">256.07693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-291.35953</Real>
+      <Real Name="Y">-339.49649</Real>
+      <Real Name="Z">612.54138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.381153</Real>
+      <Real Name="Y">99.349945</Real>
+      <Real Name="Z">5.8478966</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.270096</Real>
+      <Real Name="Y">-43.419426</Real>
+      <Real Name="Z">-3.891737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.806</Real>
+      <Real Name="Y">-33.154427</Real>
+      <Real Name="Z">8.3258266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-451.3829</Real>
+      <Real Name="Y">981.10052</Real>
+      <Real Name="Z">389.99854</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">613.2962</Real>
+      <Real Name="Y">-691.22693</Real>
+      <Real Name="Z">-449.54675</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">41.708832</Real>
+      <Real Name="Y">-156.46068</Real>
+      <Real Name="Z">-14.127186</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">233.46259</Real>
+      <Real Name="Y">-619.36865</Real>
+      <Real Name="Z">-1448.7887</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-82.950478</Real>
+      <Real Name="Y">421.2525</Real>
+      <Real Name="Z">1032.1033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-339.18326</Real>
+      <Real Name="Y">107.47547</Real>
+      <Real Name="Z">276.15564</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.72714</Real>
+      <Real Name="Y">-120.86164</Real>
+      <Real Name="Z">-118.21888</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-84.895645</Real>
+      <Real Name="Y">123.81021</Real>
+      <Real Name="Z">73.019348</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.06443</Real>
+      <Real Name="Y">39.107323</Real>
+      <Real Name="Z">151.49506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">323.79465</Real>
+      <Real Name="Y">-346.61063</Real>
+      <Real Name="Z">-65.820663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-286.57678</Real>
+      <Real Name="Y">418.9819</Real>
+      <Real Name="Z">92.445145</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.09538</Real>
+      <Real Name="Y">70.806259</Real>
+      <Real Name="Z">37.011559</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.57227</Real>
+      <Real Name="Y">-854.87329</Real>
+      <Real Name="Z">-613.3963</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-324.74033</Real>
+      <Real Name="Y">337.76242</Real>
+      <Real Name="Z">608.74756</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-337.20142</Real>
+      <Real Name="Y">857.46802</Real>
+      <Real Name="Z">242.82489</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.001099</Real>
+      <Real Name="Y">-4.4267044</Real>
+      <Real Name="Z">16.163025</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.370409</Real>
+      <Real Name="Y">4.8398285</Real>
+      <Real Name="Z">-6.4390068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.3214684</Real>
+      <Real Name="Y">1.2228508</Real>
+      <Real Name="Z">-11.723293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-445.03665</Real>
+      <Real Name="Y">891.17529</Real>
+      <Real Name="Z">239.0415</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">303.1264</Real>
+      <Real Name="Y">-493.37445</Real>
+      <Real Name="Z">-675.42316</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.41846</Real>
+      <Real Name="Y">-463.435</Real>
+      <Real Name="Z">304.97806</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.65903</Real>
+      <Real Name="Y">26.94772</Real>
+      <Real Name="Z">-153.63907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-270.52438</Real>
+      <Real Name="Y">60.771255</Real>
+      <Real Name="Z">40.796944</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-89.682449</Real>
+      <Real Name="Y">-36.206326</Real>
+      <Real Name="Z">75.430252</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-651.4967</Real>
+      <Real Name="Y">-1244.5917</Real>
+      <Real Name="Z">-371.00064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.34375</Real>
+      <Real Name="Y">609.0177</Real>
+      <Real Name="Z">-251.63367</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">414.07272</Real>
+      <Real Name="Y">623.66431</Real>
+      <Real Name="Z">628.41541</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1188.7797</Real>
+      <Real Name="Y">442.67041</Real>
+      <Real Name="Z">-788.13727</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">817.78217</Real>
+      <Real Name="Y">-311.03143</Real>
+      <Real Name="Z">570.49469</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.5914</Real>
+      <Real Name="Y">-31.578869</Real>
+      <Real Name="Z">36.633339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">220.58603</Real>
+      <Real Name="Y">411.63208</Real>
+      <Real Name="Z">169.6255</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.00457</Real>
+      <Real Name="Y">-124.08469</Real>
+      <Real Name="Z">-74.872147</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.66323</Real>
+      <Real Name="Y">-114.64688</Real>
+      <Real Name="Z">-20.035828</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1048.8573</Real>
+      <Real Name="Y">-1080.8726</Real>
+      <Real Name="Z">-855.7439</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.9653</Real>
+      <Real Name="Y">212.71719</Real>
+      <Real Name="Z">194.14815</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">577.57379</Real>
+      <Real Name="Y">694.10461</Real>
+      <Real Name="Z">680.54767</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">195.12628</Real>
+      <Real Name="Y">385.91028</Real>
+      <Real Name="Z">-1022.7339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-98.5019</Real>
+      <Real Name="Y">-315.88672</Real>
+      <Real Name="Z">753.47876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">57.214703</Real>
+      <Real Name="Y">33.820816</Real>
+      <Real Name="Z">254.48004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-275.42355</Real>
+      <Real Name="Y">-516.33722</Real>
+      <Real Name="Z">-928.43738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">242.38168</Real>
+      <Real Name="Y">81.442039</Real>
+      <Real Name="Z">145.61739</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.995934</Real>
+      <Real Name="Y">258.66385</Real>
+      <Real Name="Z">803.6911</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-525.26172</Real>
+      <Real Name="Y">-79.289062</Real>
+      <Real Name="Z">-276.45883</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">348.96231</Real>
+      <Real Name="Y">-123.86012</Real>
+      <Real Name="Z">198.63373</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">256.39117</Real>
+      <Real Name="Y">24.934326</Real>
+      <Real Name="Z">148.64253</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">549.39771</Real>
+      <Real Name="Y">835.38403</Real>
+      <Real Name="Z">-204.43706</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-110.64467</Real>
+      <Real Name="Y">-205.47003</Real>
+      <Real Name="Z">-3.5251312</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-438.21573</Real>
+      <Real Name="Y">-867.13654</Real>
+      <Real Name="Z">-68.388275</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1869.5262</Real>
+      <Real Name="Y">-4.4375153</Real>
+      <Real Name="Z">1000.0947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-908.02954</Real>
+      <Real Name="Y">182.10251</Real>
+      <Real Name="Z">-744.57355</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-436.42453</Real>
+      <Real Name="Y">-250.97232</Real>
+      <Real Name="Z">-376.80621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-151.77028</Real>
+      <Real Name="Y">53.74472</Real>
+      <Real Name="Z">129.17867</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">162.30051</Real>
+      <Real Name="Y">-161.2215</Real>
+      <Real Name="Z">27.985413</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.45837</Real>
+      <Real Name="Y">-6.8156757</Real>
+      <Real Name="Z">23.68433</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">582.84436</Real>
+      <Real Name="Y">-25.891251</Real>
+      <Real Name="Z">-30.497711</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-383.22455</Real>
+      <Real Name="Y">184.4962</Real>
+      <Real Name="Z">75.112961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-337.39856</Real>
+      <Real Name="Y">287.7326</Real>
+      <Real Name="Z">-52.399063</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.89984</Real>
+      <Real Name="Y">-1787.3977</Real>
+      <Real Name="Z">53.69838</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">126.9366</Real>
+      <Real Name="Y">225.80818</Real>
+      <Real Name="Z">-43.969959</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.09608</Real>
+      <Real Name="Y">1215.9032</Real>
+      <Real Name="Z">-63.037903</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">394.47723</Real>
+      <Real Name="Y">1012.8233</Real>
+      <Real Name="Z">893.48645</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.41998</Real>
+      <Real Name="Y">-626.96124</Real>
+      <Real Name="Z">-600.47321</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.085037</Real>
+      <Real Name="Y">-231.14702</Real>
+      <Real Name="Z">-239.21695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-212.94547</Real>
+      <Real Name="Y">-279.55511</Real>
+      <Real Name="Z">-324.83905</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-70.588692</Real>
+      <Real Name="Y">287.35043</Real>
+      <Real Name="Z">254.845</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-60.530048</Real>
+      <Real Name="Y">120.69285</Real>
+      <Real Name="Z">420.63379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">725.88751</Real>
+      <Real Name="Y">-307.1409</Real>
+      <Real Name="Z">-272.10919</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-137.50859</Real>
+      <Real Name="Y">441.86047</Real>
+      <Real Name="Z">24.686087</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-727.15839</Real>
+      <Real Name="Y">-155.63658</Real>
+      <Real Name="Z">186.7317</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1093.9655</Real>
+      <Real Name="Y">1533.715</Real>
+      <Real Name="Z">-691.18982</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">175.57156</Real>
+      <Real Name="Y">-223.08231</Real>
+      <Real Name="Z">103.29411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">620.26862</Real>
+      <Real Name="Y">-984.74414</Real>
+      <Real Name="Z">450.36731</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">741.13483</Real>
+      <Real Name="Y">38.879974</Real>
+      <Real Name="Z">146.92383</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.96646</Real>
+      <Real Name="Y">89.768219</Real>
+      <Real Name="Z">-35.34536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-913.02075</Real>
+      <Real Name="Y">115.08902</Real>
+      <Real Name="Z">-126.87984</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-857.20667</Real>
+      <Real Name="Y">-1259.9255</Real>
+      <Real Name="Z">42.607849</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">722.15955</Real>
+      <Real Name="Y">948.77881</Real>
+      <Real Name="Z">84.147682</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">74.563354</Real>
+      <Real Name="Y">277.06317</Real>
+      <Real Name="Z">-49.920128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.85213</Real>
+      <Real Name="Y">-720.50562</Real>
+      <Real Name="Z">837.90741</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.87125</Real>
+      <Real Name="Y">525.83551</Real>
+      <Real Name="Z">-600.44781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-57.473778</Real>
+      <Real Name="Y">87.925758</Real>
+      <Real Name="Z">-131.0746</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">109.19949</Real>
+      <Real Name="Y">-146.44742</Real>
+      <Real Name="Z">421.05402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.505798</Real>
+      <Real Name="Y">248.29674</Real>
+      <Real Name="Z">-104.31991</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-297.01517</Real>
+      <Real Name="Y">363.84476</Real>
+      <Real Name="Z">-22.355301</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">148.74178</Real>
+      <Real Name="Y">456.37631</Real>
+      <Real Name="Z">-83.338066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.161804</Real>
+      <Real Name="Y">-172.15941</Real>
+      <Real Name="Z">75.395798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.360275</Real>
+      <Real Name="Y">-173.31651</Real>
+      <Real Name="Z">29.130035</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">102.00889</Real>
+      <Real Name="Y">745.05597</Real>
+      <Real Name="Z">64.491379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.181259</Real>
+      <Real Name="Y">-156.06075</Real>
+      <Real Name="Z">-57.283104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.893944</Real>
+      <Real Name="Y">-712.18811</Real>
+      <Real Name="Z">-7.4268026</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.3893</Real>
+      <Real Name="Y">-734.27057</Real>
+      <Real Name="Z">-200.9213</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">173.52431</Real>
+      <Real Name="Y">630.88098</Real>
+      <Real Name="Z">108.20341</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">76.035477</Real>
+      <Real Name="Y">183.69014</Real>
+      <Real Name="Z">39.413216</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">58.336136</Real>
+      <Real Name="Y">872.55988</Real>
+      <Real Name="Z">225.22746</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">111.15114</Real>
+      <Real Name="Y">-181.75214</Real>
+      <Real Name="Z">-2.5851746</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.3587</Real>
+      <Real Name="Y">-607.7851</Real>
+      <Real Name="Z">-286.12512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-332.45923</Real>
+      <Real Name="Y">337.44016</Real>
+      <Real Name="Z">-374.3541</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.85983</Real>
+      <Real Name="Y">-273.51819</Real>
+      <Real Name="Z">423.52649</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.40991</Real>
+      <Real Name="Y">-137.62949</Real>
+      <Real Name="Z">50.323502</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-902.11572</Real>
+      <Real Name="Y">-87.249344</Real>
+      <Real Name="Z">-81.627998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">737.87585</Real>
+      <Real Name="Y">-73.258194</Real>
+      <Real Name="Z">14.835564</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">320.30322</Real>
+      <Real Name="Y">47.346237</Real>
+      <Real Name="Z">16.861229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1547.77</Real>
+      <Real Name="Y">-234.82103</Real>
+      <Real Name="Z">-639.56757</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">304.23312</Real>
+      <Real Name="Y">-54.692505</Real>
+      <Real Name="Z">201.14395</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1076.4922</Real>
+      <Real Name="Y">319.56036</Real>
+      <Real Name="Z">321.81799</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-528.41687</Real>
+      <Real Name="Y">1446.4619</Real>
+      <Real Name="Z">232.62315</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">58.640671</Real>
+      <Real Name="Y">-753.94086</Real>
+      <Real Name="Z">-567.35651</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">402.20844</Real>
+      <Real Name="Y">-441.41986</Real>
+      <Real Name="Z">552.20056</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.78329</Real>
+      <Real Name="Y">430.12061</Real>
+      <Real Name="Z">127.26671</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.720016</Real>
+      <Real Name="Y">-119.7822</Real>
+      <Real Name="Z">-81.973885</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.10016</Real>
+      <Real Name="Y">-489.77142</Real>
+      <Real Name="Z">-109.76485</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1150.6566</Real>
+      <Real Name="Y">-2014.3123</Real>
+      <Real Name="Z">628.68512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-120.6366</Real>
+      <Real Name="Y">1239.6359</Real>
+      <Real Name="Z">350.8512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1025.9114</Real>
+      <Real Name="Y">384.27838</Real>
+      <Real Name="Z">-785.13135</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">777.67432</Real>
+      <Real Name="Y">-1300.9568</Real>
+      <Real Name="Z">-314.34891</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-240.34583</Real>
+      <Real Name="Y">680.84369</Real>
+      <Real Name="Z">36.351688</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.8537</Real>
+      <Real Name="Y">700.31494</Real>
+      <Real Name="Z">334.14511</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">79.341072</Real>
+      <Real Name="Y">848.40503</Real>
+      <Real Name="Z">1308.5204</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.36238</Real>
+      <Real Name="Y">-728.60944</Real>
+      <Real Name="Z">-1002.1747</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-112.31825</Real>
+      <Real Name="Y">-3.2144241</Real>
+      <Real Name="Z">-234.75362</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_restraints_d.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_restraints_d.xml
new file mode 100644 (file)
index 0000000..92a2bc3
--- /dev/null
@@ -0,0 +1,1826 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVrestraint/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">206.51491586754966</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">0</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">0</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">0</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">0</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">0</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">0</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">0</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">0</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">0</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">147.50181391851598</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">0</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">0</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">0</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">0</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">0</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">0</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">0</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">0</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">0</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">113.29750734216381</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1416.9335220698135</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1417.6089009965622</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1417.9156851747448</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1417.6469726193218</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1416.6563809710203</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1414.9064265331349</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1412.4693867404799</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1409.4737111440882</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1406.0417148111119</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1402.2698997768746</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1398.2627921195131</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1394.1852608504728</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1390.277758104507</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1386.8009457351041</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1383.9324330676918</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1381.6851141324387</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1379.9099341635401</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1378.3895776753482</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1376.9723449659491</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1375.6782789325248</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1374.7326984514398</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">1328.0782301566517</Real>
+      <Real Name="Y">-905.40026593535833</Real>
+      <Real Name="Z">-186.26254909732083</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-168.43846151068348</Real>
+      <Real Name="Y">-246.4445535343867</Real>
+      <Real Name="Z">-51.157662739194144</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1021.856018707977</Real>
+      <Real Name="Y">337.99332291913123</Real>
+      <Real Name="Z">-144.86272917587155</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-126.20429561523332</Real>
+      <Real Name="Y">187.1742852151848</Real>
+      <Real Name="Z">32.511296088747066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">501.33923428148978</Real>
+      <Real Name="Y">1182.2069519160882</Real>
+      <Real Name="Z">187.31201031942226</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">243.30090576021746</Real>
+      <Real Name="Y">-321.06753927735224</Real>
+      <Real Name="Z">-452.60576355240107</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1481.8512453034182</Real>
+      <Real Name="Y">77.073869257072005</Real>
+      <Real Name="Z">1126.9505632445921</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">371.73308825407327</Real>
+      <Real Name="Y">-259.90042208136748</Real>
+      <Real Name="Z">-593.56790349977041</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">299.85368628987186</Real>
+      <Real Name="Y">-271.17349075330577</Real>
+      <Real Name="Z">3.3112248758734779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.9439451133138306</Real>
+      <Real Name="Y">-34.118269995556616</Real>
+      <Real Name="Z">25.599744969965968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.4127790283902044</Real>
+      <Real Name="Y">29.789555476024951</Real>
+      <Real Name="Z">-15.15156229917072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.6926231254665893</Real>
+      <Real Name="Y">15.672428695211565</Real>
+      <Real Name="Z">-6.2336867483995704</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.9238407031470217</Real>
+      <Real Name="Y">18.173037328900413</Real>
+      <Real Name="Z">-5.9923174476106666</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.8274596594815193</Real>
+      <Real Name="Y">-7.2485714682317095</Real>
+      <Real Name="Z">4.4746057463956106</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.5938366310254963</Real>
+      <Real Name="Y">-22.387254569184783</Real>
+      <Real Name="Z">3.651938441219535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.84756794356167</Real>
+      <Real Name="Y">-59.015204675975824</Real>
+      <Real Name="Z">-670.2890616125344</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-396.07444656305222</Real>
+      <Real Name="Y">36.163795342919087</Real>
+      <Real Name="Z">385.36577562606055</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.5581609756780068</Real>
+      <Real Name="Y">-11.443622742309477</Real>
+      <Real Name="Z">206.57754853761458</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">350.33129727574658</Real>
+      <Real Name="Y">397.18309927522222</Real>
+      <Real Name="Z">-116.30426778100971</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.55877353223039</Real>
+      <Real Name="Y">-227.96761061071751</Real>
+      <Real Name="Z">94.897375348492346</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-339.725041659787</Real>
+      <Real Name="Y">-572.75604701314819</Real>
+      <Real Name="Z">260.37570479834221</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">651.81722389484867</Real>
+      <Real Name="Y">-500.93803435885354</Real>
+      <Real Name="Z">-426.18563110928528</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-115.29325310886911</Real>
+      <Real Name="Y">125.72006246379296</Real>
+      <Real Name="Z">76.886764579646169</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-650.86298197258623</Real>
+      <Real Name="Y">444.51723806895859</Real>
+      <Real Name="Z">222.33540054888451</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.491263427466443</Real>
+      <Real Name="Y">279.29682840185251</Real>
+      <Real Name="Z">51.271700489170328</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.384000186502675</Real>
+      <Real Name="Y">-157.07118696890899</Real>
+      <Real Name="Z">-104.353454542121</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.73842219709699</Real>
+      <Real Name="Y">-208.50665312588112</Real>
+      <Real Name="Z">-132.81752529619934</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-589.77948745280207</Real>
+      <Real Name="Y">237.27131788783461</Real>
+      <Real Name="Z">851.6462548078473</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">395.7235649930285</Real>
+      <Real Name="Y">-34.571300731264699</Real>
+      <Real Name="Z">-874.73194750969378</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">174.02702194182416</Real>
+      <Real Name="Y">-179.41755028393868</Real>
+      <Real Name="Z">-67.55398075479124</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1099.5656675582929</Real>
+      <Real Name="Y">553.65758978756492</Real>
+      <Real Name="Z">903.67462001587194</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-161.87576659787464</Real>
+      <Real Name="Y">-835.1549688126878</Real>
+      <Real Name="Z">-374.62710844607091</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-835.94693688352322</Real>
+      <Real Name="Y">336.87779175369201</Real>
+      <Real Name="Z">-602.7495003507895</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.31275576025774</Real>
+      <Real Name="Y">747.5609517160151</Real>
+      <Real Name="Z">136.28541693996794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.52815141159175</Real>
+      <Real Name="Y">-645.92083078507869</Real>
+      <Real Name="Z">-47.856831968977353</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.203383168403747</Real>
+      <Real Name="Y">-129.11641921631193</Real>
+      <Real Name="Z">-33.408224223678573</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">46.297276634282547</Real>
+      <Real Name="Y">-167.90639465351671</Real>
+      <Real Name="Z">-913.22718455332802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-121.78510656118857</Real>
+      <Real Name="Y">49.506685157977799</Real>
+      <Real Name="Z">273.49109958051969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">378.01977076983803</Real>
+      <Real Name="Y">-5.7666103656944614</Real>
+      <Real Name="Z">685.09668255517215</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.1249090561351</Real>
+      <Real Name="Y">49.892689437953464</Real>
+      <Real Name="Z">-861.93995160060967</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-268.05174251326491</Real>
+      <Real Name="Y">160.69175712099707</Real>
+      <Real Name="Z">483.45190357817785</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.8038878217764633</Real>
+      <Real Name="Y">-196.19771752634384</Real>
+      <Real Name="Z">167.55983040387557</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.92205347687684</Real>
+      <Real Name="Y">60.109704540778779</Real>
+      <Real Name="Z">367.77550141649056</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">548.89215039252178</Real>
+      <Real Name="Y">-52.442751526798261</Real>
+      <Real Name="Z">-12.277633369383906</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">145.01955364889372</Real>
+      <Real Name="Y">-23.075012254083816</Real>
+      <Real Name="Z">-246.45182283175731</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.209211352384756</Real>
+      <Real Name="Y">37.621917741800985</Real>
+      <Real Name="Z">360.18398367703071</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">243.9520333445858</Real>
+      <Real Name="Y">-5.7302963059085243</Real>
+      <Real Name="Z">-127.35871477621369</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.63297602425282</Real>
+      <Real Name="Y">-114.1010583032206</Real>
+      <Real Name="Z">-113.65029842318209</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-333.64286026475583</Real>
+      <Real Name="Y">-2020.2087991235414</Real>
+      <Real Name="Z">-705.45563304315579</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.10290126381415</Real>
+      <Real Name="Y">1302.4641117046338</Real>
+      <Real Name="Z">328.30615954819899</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">273.81685897658355</Real>
+      <Real Name="Y">399.74751691818801</Real>
+      <Real Name="Z">73.873078538190597</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">454.22345093669884</Real>
+      <Real Name="Y">1035.5548259648767</Real>
+      <Real Name="Z">-836.82946049132431</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-161.20939972328284</Real>
+      <Real Name="Y">-546.36833054763736</Real>
+      <Real Name="Z">227.77684042938836</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.52207431387234</Real>
+      <Real Name="Y">-418.6416135983302</Real>
+      <Real Name="Z">550.23312753786172</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">50.714789325748072</Real>
+      <Real Name="Y">92.955766361639391</Real>
+      <Real Name="Z">0.86146395837502965</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.6475267044582758</Real>
+      <Real Name="Y">-40.904310982005519</Real>
+      <Real Name="Z">-1.3788704079398766</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.013553100349917</Real>
+      <Real Name="Y">-30.333762922771918</Real>
+      <Real Name="Z">10.519660201864806</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-456.43828080629254</Real>
+      <Real Name="Y">971.59441538699821</Real>
+      <Real Name="Z">410.11333375785665</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">615.54966872128932</Real>
+      <Real Name="Y">-684.91310710051232</Real>
+      <Real Name="Z">-459.81045209672379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">41.963572465353444</Real>
+      <Real Name="Y">-153.75311435644716</Real>
+      <Real Name="Z">-22.296539084923268</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">267.69613291910838</Real>
+      <Real Name="Y">-462.20972553885025</Real>
+      <Real Name="Z">-1386.6549880842458</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-83.659524299427403</Real>
+      <Real Name="Y">332.90525639100446</Real>
+      <Real Name="Z">1034.3907072719985</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.44810650690323</Real>
+      <Real Name="Y">26.491368264502292</Real>
+      <Real Name="Z">210.3919696244912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.14880120112713</Real>
+      <Real Name="Y">-125.66105970663331</Real>
+      <Real Name="Z">-120.06423297323177</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.827402523511154</Real>
+      <Real Name="Y">126.72976836791881</Real>
+      <Real Name="Z">74.47788677433627</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-64.104823191551532</Real>
+      <Real Name="Y">41.735484753509901</Real>
+      <Real Name="Z">152.18178394481959</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">329.8207915722142</Real>
+      <Real Name="Y">-341.07862576525525</Real>
+      <Real Name="Z">-68.010948746061374</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.41910618583302</Real>
+      <Real Name="Y">415.60897603657895</Real>
+      <Real Name="Z">94.081379034441525</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.23125899335412</Real>
+      <Real Name="Y">68.751530900709412</Real>
+      <Real Name="Z">37.60063802250022</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">812.93114317837717</Real>
+      <Real Name="Y">-958.58248986053172</Real>
+      <Real Name="Z">-477.29548004378773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-357.81013403259101</Real>
+      <Real Name="Y">354.71740611054906</Real>
+      <Real Name="Z">551.77270365112679</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-385.34592702023622</Real>
+      <Real Name="Y">996.93443283686327</Real>
+      <Real Name="Z">106.35867694925787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.4972570037516704</Real>
+      <Real Name="Y">-8.3863157942697768</Real>
+      <Real Name="Z">1.8497342176687539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.71188944234359042</Real>
+      <Real Name="Y">5.5080332661960369</Real>
+      <Real Name="Z">0.19689298932893351</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.8263576675565787</Real>
+      <Real Name="Y">3.4827681101939802</Real>
+      <Real Name="Z">-3.0167475024415467</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-383.95063687221506</Real>
+      <Real Name="Y">868.9227266282461</Real>
+      <Real Name="Z">238.66045597483546</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">283.43681465318394</Real>
+      <Real Name="Y">-487.68568051238503</Real>
+      <Real Name="Z">-676.9122670752771</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-322.44047795440343</Real>
+      <Real Name="Y">-447.69237971630764</Real>
+      <Real Name="Z">303.06243358464866</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">371.00365357834602</Real>
+      <Real Name="Y">23.712435068959898</Real>
+      <Real Name="Z">-150.32694772084642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.17122040221471</Real>
+      <Real Name="Y">62.020282700484216</Real>
+      <Real Name="Z">39.462595552215447</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.651444546746177</Real>
+      <Real Name="Y">-34.116891640730081</Real>
+      <Real Name="Z">73.410202394066829</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-516.71970470194731</Real>
+      <Real Name="Y">-1285.8385028052051</Real>
+      <Real Name="Z">-331.91875038992481</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.48397678163582</Real>
+      <Real Name="Y">621.78505972069092</Real>
+      <Real Name="Z">-279.15721919261517</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.69271235304421</Real>
+      <Real Name="Y">655.15247152795018</Real>
+      <Real Name="Z">633.0993033390904</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1197.2887560844445</Real>
+      <Real Name="Y">443.27327545630243</Real>
+      <Real Name="Z">-794.67230970417893</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">821.09689595579675</Real>
+      <Real Name="Y">-311.37302028558099</Real>
+      <Real Name="Z">573.11342314254478</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.85905918015868</Real>
+      <Real Name="Y">-31.926825742624036</Real>
+      <Real Name="Z">40.545533972170205</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">221.95556051004601</Real>
+      <Real Name="Y">425.49471656361061</Real>
+      <Real Name="Z">167.55558796872697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-31.314578763474124</Real>
+      <Real Name="Y">-132.10540433847711</Real>
+      <Real Name="Z">-73.267317303153121</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.17690031743379</Real>
+      <Real Name="Y">-122.46980004374416</Real>
+      <Real Name="Z">-18.451708722219884</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1047.0210329734914</Real>
+      <Real Name="Y">-1077.947804708268</Real>
+      <Real Name="Z">-856.03338926619961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.24418264368239</Real>
+      <Real Name="Y">211.21737064049597</Real>
+      <Real Name="Z">194.52266876472817</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">576.43673794516087</Real>
+      <Real Name="Y">693.13059751907417</Real>
+      <Real Name="Z">680.25612389186017</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.80881891454794</Real>
+      <Real Name="Y">384.37109845012094</Real>
+      <Real Name="Z">-1020.7807082772521</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.925297410140118</Real>
+      <Real Name="Y">-314.84706560351884</Real>
+      <Real Name="Z">752.20608286893332</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.984669155814814</Real>
+      <Real Name="Y">34.677739903192041</Real>
+      <Real Name="Z">253.66542206161358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.83718812299639</Real>
+      <Real Name="Y">-515.93511901400825</Real>
+      <Real Name="Z">-926.9945428543956</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.02017603520483</Real>
+      <Real Name="Y">81.224366103127167</Real>
+      <Real Name="Z">144.74331924185299</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.715169974408134</Real>
+      <Real Name="Y">258.84781108499612</Real>
+      <Real Name="Z">802.93385163705273</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-594.66712165918773</Real>
+      <Real Name="Y">-66.325089243309151</Real>
+      <Real Name="Z">-122.32964157760213</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.96186505836829</Real>
+      <Real Name="Y">-123.46248581963616</Real>
+      <Real Name="Z">53.935692302088057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">255.68688049097204</Real>
+      <Real Name="Y">13.464085159173692</Real>
+      <Real Name="Z">84.235167762144897</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">611.06539484503014</Real>
+      <Real Name="Y">839.97798047474203</Real>
+      <Real Name="Z">-265.94087384298064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.76789585659597</Real>
+      <Real Name="Y">-206.92859092705817</Real>
+      <Real Name="Z">15.90339036830386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-474.36485611191563</Real>
+      <Real Name="Y">-878.24898538908951</Real>
+      <Real Name="Z">-40.946493997266842</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1588.7326707775553</Real>
+      <Real Name="Y">-232.76990946444641</Real>
+      <Real Name="Z">796.08421825554478</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-819.45420161255322</Real>
+      <Real Name="Y">397.16905292514792</Real>
+      <Real Name="Z">-589.52116040556916</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-208.16851530782614</Real>
+      <Real Name="Y">-145.08643483545109</Real>
+      <Real Name="Z">-321.92283806039802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.82936628635363</Real>
+      <Real Name="Y">54.6264150754714</Real>
+      <Real Name="Z">125.67863038979567</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">164.04200751770816</Real>
+      <Real Name="Y">-162.15258178673972</Real>
+      <Real Name="Z">29.635387842153442</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.01340719829753</Real>
+      <Real Name="Y">-6.9355041126986468</Real>
+      <Real Name="Z">24.988848785100132</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">850.55243387580356</Real>
+      <Real Name="Y">-484.9055590309473</Real>
+      <Real Name="Z">-96.15423590268999</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-585.08160346554382</Real>
+      <Real Name="Y">327.7796891730394</Real>
+      <Real Name="Z">75.856522974294251</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.38588003886213</Real>
+      <Real Name="Y">437.13622724030478</Real>
+      <Real Name="Z">-100.35365295441491</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.56843228581337</Real>
+      <Real Name="Y">-1829.8037562188697</Real>
+      <Real Name="Z">26.10707682119029</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">131.28175023698071</Real>
+      <Real Name="Y">246.10940459955864</Real>
+      <Real Name="Z">-29.212146237088106</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.6540077622532</Real>
+      <Real Name="Y">1232.6971279010374</Real>
+      <Real Name="Z">-54.417459108715647</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">450.02457168953839</Real>
+      <Real Name="Y">1041.5670317109762</Real>
+      <Real Name="Z">887.98954562720155</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-318.98758969461386</Real>
+      <Real Name="Y">-643.92833704371048</Real>
+      <Real Name="Z">-608.16691973538593</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.351767412849547</Real>
+      <Real Name="Y">-240.23185215146316</Real>
+      <Real Name="Z">-238.99600448688849</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">249.05371040816757</Real>
+      <Real Name="Y">-336.92560539562191</Real>
+      <Real Name="Z">-438.14979794230237</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-278.16594967575912</Real>
+      <Real Name="Y">346.29826142899947</Real>
+      <Real Name="Z">251.84859746085257</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-170.48285542157944</Real>
+      <Real Name="Y">134.62087571176835</Real>
+      <Real Name="Z">498.15600359470392</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">705.51954261240712</Real>
+      <Real Name="Y">-340.79341679891309</Real>
+      <Real Name="Z">-201.04226890621766</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.73381247856207</Real>
+      <Real Name="Y">455.21741872588905</Real>
+      <Real Name="Z">-16.030727084928291</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-721.95588068283894</Real>
+      <Real Name="Y">-145.64952783270559</Real>
+      <Real Name="Z">164.3680031091379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1105.4630845844667</Real>
+      <Real Name="Y">1483.2828061952187</Real>
+      <Real Name="Z">-676.70030248431476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.72410740546641</Real>
+      <Real Name="Y">-204.35449004260201</Real>
+      <Real Name="Z">94.394440893182392</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">633.7039957563029</Real>
+      <Real Name="Y">-954.1786043006482</Real>
+      <Real Name="Z">445.83100586222429</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">745.11085322719532</Real>
+      <Real Name="Y">16.527375356092875</Real>
+      <Real Name="Z">139.09829255271086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.33673438025286</Real>
+      <Real Name="Y">98.2357293773115</Real>
+      <Real Name="Z">-32.632121522339716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-917.26198475479077</Real>
+      <Real Name="Y">126.39815903522556</Real>
+      <Real Name="Z">-123.5306854139439</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-772.63204074639737</Real>
+      <Real Name="Y">-1270.5379767076522</Real>
+      <Real Name="Z">100.94620312459</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">692.72223182354651</Real>
+      <Real Name="Y">966.6550608851071</Real>
+      <Real Name="Z">65.164088371110822</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">48.212588526803437</Real>
+      <Real Name="Y">265.45582123101639</Real>
+      <Real Name="Z">-73.229099708546457</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.72663766487725</Real>
+      <Real Name="Y">-684.98518331108119</Real>
+      <Real Name="Z">816.4955318838164</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">138.89629850593212</Real>
+      <Real Name="Y">498.82070239736396</Real>
+      <Real Name="Z">-585.24448226716254</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.288430887531646</Real>
+      <Real Name="Y">73.167203198407478</Real>
+      <Real Name="Z">-119.95972545133425</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.338615353660401</Real>
+      <Real Name="Y">-109.94958020891366</Real>
+      <Real Name="Z">269.66393545104211</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.570104080770108</Real>
+      <Real Name="Y">246.46434897472355</Real>
+      <Real Name="Z">-44.672459556773646</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-294.81041910340667</Real>
+      <Real Name="Y">350.87235962512318</Real>
+      <Real Name="Z">70.188787386065954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">86.489577539044276</Real>
+      <Real Name="Y">493.2140575456051</Real>
+      <Real Name="Z">-79.517990189441534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.404751136179407</Real>
+      <Real Name="Y">-183.9239319405759</Real>
+      <Real Name="Z">70.391354279404737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">28.467653242584852</Real>
+      <Real Name="Y">-197.28305643184768</Real>
+      <Real Name="Z">32.172952939449345</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">84.057643553682936</Real>
+      <Real Name="Y">775.39303673834343</Real>
+      <Real Name="Z">72.336462008713198</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.154602794079381</Real>
+      <Real Name="Y">-171.12553946065486</Real>
+      <Real Name="Z">-64.66060360219754</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-71.929026004440573</Real>
+      <Real Name="Y">-730.57048386777126</Real>
+      <Real Name="Z">-10.302192447392038</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.22133453273274</Real>
+      <Real Name="Y">-726.92390619292257</Real>
+      <Real Name="Z">-203.60047435580407</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.04975256848707</Real>
+      <Real Name="Y">628.0849982866805</Real>
+      <Real Name="Z">109.01789927094087</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">72.976184854153317</Real>
+      <Real Name="Y">179.24290378873908</Real>
+      <Real Name="Z">40.431707061223804</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.25325424122956</Real>
+      <Real Name="Y">919.900675508115</Real>
+      <Real Name="Z">781.596820768817</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.556347832093024</Real>
+      <Real Name="Y">-161.97747087431986</Real>
+      <Real Name="Z">-198.03051659720242</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.21726153661876</Real>
+      <Real Name="Y">-711.06814233610021</Real>
+      <Real Name="Z">-526.51512503399397</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.40728445158015</Real>
+      <Real Name="Y">322.18329779905417</Real>
+      <Real Name="Z">-416.35754342514349</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.6896753296457</Real>
+      <Real Name="Y">-264.05310871220598</Real>
+      <Real Name="Z">437.78986360426376</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">143.48994735961313</Real>
+      <Real Name="Y">-127.52909249587275</Real>
+      <Real Name="Z">87.993111424136117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-877.77921424076146</Real>
+      <Real Name="Y">-122.87070523117205</Real>
+      <Real Name="Z">-62.018010522300273</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.67406295065484</Real>
+      <Real Name="Y">-48.830761792558853</Real>
+      <Real Name="Z">7.5397057122372289</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">310.36709663162941</Real>
+      <Real Name="Y">58.608269376894931</Real>
+      <Real Name="Z">7.0200211084501376</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1509.4549162390233</Real>
+      <Real Name="Y">-281.95542335968406</Real>
+      <Real Name="Z">-666.92286987146554</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">289.51425335202327</Real>
+      <Real Name="Y">-38.580689409552292</Real>
+      <Real Name="Z">213.93637934259488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1063.9161730088892</Real>
+      <Real Name="Y">348.3704868972597</Real>
+      <Real Name="Z">340.38184295032534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-388.64130560083879</Real>
+      <Real Name="Y">1486.1428111499931</Real>
+      <Real Name="Z">329.82321068317293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.94859687366937884</Real>
+      <Real Name="Y">-750.84151024100674</Real>
+      <Real Name="Z">-583.82788306772659</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">360.83171132378556</Real>
+      <Real Name="Y">-479.94584183716586</Real>
+      <Real Name="Z">486.13938008841836</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">203.0729036767024</Real>
+      <Real Name="Y">434.19350140595316</Real>
+      <Real Name="Z">127.7963417332098</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.191822901139972</Real>
+      <Real Name="Y">-121.64522034883683</Real>
+      <Real Name="Z">-82.763529192926569</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-225.25830130996664</Real>
+      <Real Name="Y">-493.06986194482653</Real>
+      <Real Name="Z">-109.82768825888647</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1168.5225205110919</Real>
+      <Real Name="Y">-2062.2699891699485</Real>
+      <Real Name="Z">655.58363348407443</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-135.16180599993987</Real>
+      <Real Name="Y">1272.9760662070801</Real>
+      <Real Name="Z">326.5162856095186</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1028.6082650836172</Real>
+      <Real Name="Y">412.54622885499202</Real>
+      <Real Name="Z">-793.30548842254802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">795.24671349164851</Real>
+      <Real Name="Y">-1321.1491414253871</Real>
+      <Real Name="Z">-304.05085765894177</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-253.04448317118801</Real>
+      <Real Name="Y">692.17326480223687</Real>
+      <Real Name="Z">29.826848076079745</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-505.58259903104988</Real>
+      <Real Name="Y">713.30553929012206</Real>
+      <Real Name="Z">327.35172109746907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.65641867282568</Real>
+      <Real Name="Y">794.48904059047879</Real>
+      <Real Name="Z">1309.0445011705986</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-100.49084349618526</Real>
+      <Real Name="Y">-707.99829332132731</Real>
+      <Real Name="Z">-999.84302052634655</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.230583529035982</Real>
+      <Real Name="Y">31.062533160861964</Real>
+      <Real Name="Z">-230.99983448174834</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.020000 Step 20 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-1028.6119723267088</Real>
+      <Real Name="Y">1034.8468806901517</Real>
+      <Real Name="Z">1310.6466582575199</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-239.93519292823333</Real>
+      <Real Name="Y">-105.1920032149894</Real>
+      <Real Name="Z">-101.64338579347918</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">231.72803273096036</Real>
+      <Real Name="Y">-186.76056997680934</Real>
+      <Real Name="Z">-197.06119916038966</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">440.33650688996238</Real>
+      <Real Name="Y">-640.03764086142837</Real>
+      <Real Name="Z">-380.98466108768923</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1514.6785827303338</Real>
+      <Real Name="Y">-572.31933531090681</Real>
+      <Real Name="Z">-1062.2969589559793</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-891.95172312091665</Real>
+      <Real Name="Y">80.2274919676141</Real>
+      <Real Name="Z">120.68377495201506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-759.80561911288021</Real>
+      <Real Name="Y">728.31317753312908</Real>
+      <Real Name="Z">116.96924969448057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">459.48438116196041</Real>
+      <Real Name="Y">-63.58244213194758</Real>
+      <Real Name="Z">-59.075695507967772</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.5355125647899874</Real>
+      <Real Name="Y">-370.49466108290397</Real>
+      <Real Name="Z">70.115356606497329</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6710307250378378</Real>
+      <Real Name="Y">0.66929223786402225</Real>
+      <Real Name="Z">20.791236043987311</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">9.7613464065405395</Real>
+      <Real Name="Y">8.5869593953477263</Real>
+      <Real Name="Z">-4.8954709097988669</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.387886860780057</Real>
+      <Real Name="Y">2.1974712075565881</Real>
+      <Real Name="Z">-11.037039216635161</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-23.990556990216959</Real>
+      <Real Name="Y">4.3136146784861182</Real>
+      <Real Name="Z">-25.884670441377516</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.6679408222953498</Real>
+      <Real Name="Y">-1.10935903617791</Real>
+      <Real Name="Z">12.760088578112523</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">16.069302066692231</Real>
+      <Real Name="Y">-12.246252659532356</Real>
+      <Real Name="Z">11.12327453215498</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">286.81023695763531</Real>
+      <Real Name="Y">-118.67535790050849</Real>
+      <Real Name="Z">-751.19224285268058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.56531884991142</Real>
+      <Real Name="Y">202.30212658787934</Real>
+      <Real Name="Z">406.1549253257121</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.38179790371592</Real>
+      <Real Name="Y">9.9726232660602534</Real>
+      <Real Name="Z">175.20457291112959</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">402.65683094498723</Real>
+      <Real Name="Y">606.98617139093062</Real>
+      <Real Name="Z">-237.73921571599874</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-90.014621820053293</Real>
+      <Real Name="Y">-246.84026258272743</Real>
+      <Real Name="Z">91.967280307937131</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-363.47962042680837</Real>
+      <Real Name="Y">-566.4983114901454</Real>
+      <Real Name="Z">315.34791557076665</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">438.2531939971359</Real>
+      <Real Name="Y">-426.55299867170311</Real>
+      <Real Name="Z">-484.63093422169163</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.310476273969897</Real>
+      <Real Name="Y">148.0563755094683</Real>
+      <Real Name="Z">126.09871561978649</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-453.29401142956738</Real>
+      <Real Name="Y">418.02891888119808</Real>
+      <Real Name="Z">462.51869077510526</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">120.99570994936929</Real>
+      <Real Name="Y">227.20086511115704</Real>
+      <Real Name="Z">270.90724015090166</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.21754210769269</Real>
+      <Real Name="Y">-161.00662060019724</Real>
+      <Real Name="Z">-129.7903040335932</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-183.74158606576717</Real>
+      <Real Name="Y">-239.38866823465779</Real>
+      <Real Name="Z">-138.93361972666241</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-418.06866164359013</Real>
+      <Real Name="Y">391.21469412571821</Real>
+      <Real Name="Z">899.06302726398405</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">402.04018202292457</Real>
+      <Real Name="Y">-242.71926952238937</Real>
+      <Real Name="Z">-833.5138774806519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.93260086633927</Real>
+      <Real Name="Y">-211.34247278509542</Real>
+      <Real Name="Z">-121.22765015563348</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1216.0217210086871</Real>
+      <Real Name="Y">304.96620039433526</Real>
+      <Real Name="Z">867.87500010960548</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">60.541193493939588</Real>
+      <Real Name="Y">-538.55424166611476</Real>
+      <Real Name="Z">-202.49281256579218</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-968.96586471841294</Real>
+      <Real Name="Y">520.4382330490937</Real>
+      <Real Name="Z">-558.92622863664872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">254.44879191540585</Real>
+      <Real Name="Y">1004.8987944916926</Real>
+      <Real Name="Z">168.05224037936492</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-80.955314295386998</Real>
+      <Real Name="Y">-373.11615901299297</Real>
+      <Real Name="Z">-46.789849935993125</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.8982713967563</Real>
+      <Real Name="Y">-177.74536661757855</Real>
+      <Real Name="Z">-52.048024655988243</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-78.401290602591672</Real>
+      <Real Name="Y">-5.6311550643109882</Real>
+      <Real Name="Z">-1007.8643013740375</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-150.81508604128575</Real>
+      <Real Name="Y">295.44378297065049</Real>
+      <Real Name="Z">328.05111218530999</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">273.28525549307466</Real>
+      <Real Name="Y">143.3635913271462</Real>
+      <Real Name="Z">298.14453361156723</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">501.44016024432244</Real>
+      <Real Name="Y">212.570379906651</Real>
+      <Real Name="Z">-393.86687735394378</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-483.18211794184373</Real>
+      <Real Name="Y">72.062972475881509</Real>
+      <Real Name="Z">660.95418605416376</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-186.45378037016243</Real>
+      <Real Name="Y">-230.69852317153288</Real>
+      <Real Name="Z">48.970046868765181</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-969.92469330993947</Real>
+      <Real Name="Y">174.14253095257078</Real>
+      <Real Name="Z">265.02073475507484</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">571.79728392675679</Real>
+      <Real Name="Y">-308.63426867209489</Real>
+      <Real Name="Z">-168.26618140313028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">177.0286747686734</Real>
+      <Real Name="Y">8.1785307605104673</Real>
+      <Real Name="Z">-175.96182663952618</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-421.85643318322957</Real>
+      <Real Name="Y">76.574229036556517</Real>
+      <Real Name="Z">258.86398201024025</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.91695285372197</Real>
+      <Real Name="Y">-1.889903028368451</Real>
+      <Real Name="Z">-49.877764266234678</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">155.80868376752176</Real>
+      <Real Name="Y">-192.09009094072968</Real>
+      <Real Name="Z">-94.430902461198443</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.69218823946807</Real>
+      <Real Name="Y">-1436.679493565829</Real>
+      <Real Name="Z">-646.56771067103239</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">38.608576640828204</Real>
+      <Real Name="Y">1346.9114042821045</Real>
+      <Real Name="Z">449.92531139991746</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">347.45544456110594</Real>
+      <Real Name="Y">360.54346968932322</Real>
+      <Real Name="Z">104.09442422035961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">469.29305862719309</Real>
+      <Real Name="Y">779.73082331305011</Real>
+      <Real Name="Z">-914.8974525281327</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.04637785306008</Real>
+      <Real Name="Y">-465.03426395814364</Real>
+      <Real Name="Z">38.813725148396756</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-453.77476803834708</Real>
+      <Real Name="Y">-185.72673284944426</Real>
+      <Real Name="Z">333.63601924620627</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">66.754314532137869</Real>
+      <Real Name="Y">124.18140916607246</Real>
+      <Real Name="Z">31.131335309606499</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-15.744306773975303</Real>
+      <Real Name="Y">-50.60291803492575</Real>
+      <Real Name="Z">-12.3146217868835</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.735410886611845</Real>
+      <Real Name="Y">-41.251348436976237</Real>
+      <Real Name="Z">-10.674988703209607</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-404.95133481658701</Real>
+      <Real Name="Y">912.58469517079709</Real>
+      <Real Name="Z">246.76367555618836</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">449.46301574446619</Real>
+      <Real Name="Y">-698.87977450245103</Real>
+      <Real Name="Z">-410.2629262895976</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">34.112585988059429</Real>
+      <Real Name="Y">-157.75202200211328</Real>
+      <Real Name="Z">-30.329588035003773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">655.02080242435738</Real>
+      <Real Name="Y">-700.21970432076841</Real>
+      <Real Name="Z">-1240.1299866313825</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-390.14289284221115</Real>
+      <Real Name="Y">290.51908624178532</Real>
+      <Real Name="Z">961.56783738962361</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-497.29668782267845</Real>
+      <Real Name="Y">223.30531344216385</Real>
+      <Real Name="Z">41.510241420025579</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">43.490017046484326</Real>
+      <Real Name="Y">-260.98493278357677</Real>
+      <Real Name="Z">20.599904528789253</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.888717015733164</Real>
+      <Real Name="Y">113.7921017822423</Real>
+      <Real Name="Z">36.104159628146306</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-38.340156067277697</Real>
+      <Real Name="Y">66.108494496287619</Real>
+      <Real Name="Z">130.72711844774975</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">334.25739110115376</Real>
+      <Real Name="Y">-307.68610518661774</Real>
+      <Real Name="Z">-95.779905659457512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-311.76812331362851</Real>
+      <Real Name="Y">377.48482999246585</Real>
+      <Real Name="Z">156.62863191134215</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-87.813633166409346</Real>
+      <Real Name="Y">46.219590608228941</Real>
+      <Real Name="Z">52.305517790368519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">776.24640181875816</Real>
+      <Real Name="Y">-1425.77456842061</Real>
+      <Real Name="Z">-544.21992513258044</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-443.11250351116007</Real>
+      <Real Name="Y">311.3413570582108</Real>
+      <Real Name="Z">689.10532436021333</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-98.18069021646977</Real>
+      <Real Name="Y">976.14646683327476</Real>
+      <Real Name="Z">137.33028101726592</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.0619236522087476</Real>
+      <Real Name="Y">-11.021996599567226</Real>
+      <Real Name="Z">24.164378391464965</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.2140837401815485</Real>
+      <Real Name="Y">5.9413811110617853</Real>
+      <Real Name="Z">-8.9460892334527813</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.3465506313874513</Real>
+      <Real Name="Y">5.936848957331696</Real>
+      <Real Name="Z">-13.963625155210188</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.41924415945621</Real>
+      <Real Name="Y">814.50237858092623</Real>
+      <Real Name="Z">99.641836135153454</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">479.09520705100931</Real>
+      <Real Name="Y">-383.78988570432779</Real>
+      <Real Name="Z">-506.68920466869776</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-209.80972582820348</Real>
+      <Real Name="Y">-361.44276364533169</Real>
+      <Real Name="Z">320.28172974434256</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">339.5930057111342</Real>
+      <Real Name="Y">40.399540906680045</Real>
+      <Real Name="Z">-319.68925585680722</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-307.02266822626774</Real>
+      <Real Name="Y">48.766977923391416</Real>
+      <Real Name="Z">84.305930189371963</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-80.163023893540554</Real>
+      <Real Name="Y">-16.124375810123279</Real>
+      <Real Name="Z">103.54972262658723</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-401.77020922675808</Real>
+      <Real Name="Y">-1139.9866721819058</Real>
+      <Real Name="Z">-534.72753389697891</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.73076328740251</Real>
+      <Real Name="Y">317.68804763354962</Real>
+      <Real Name="Z">-143.44809131767522</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">409.34185223409668</Real>
+      <Real Name="Y">693.05597520719721</Real>
+      <Real Name="Z">681.5807135700386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-855.25867825000375</Real>
+      <Real Name="Y">327.13300548143303</Real>
+      <Real Name="Z">-506.72575465306937</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">678.28336964670029</Real>
+      <Real Name="Y">-269.80549406684588</Real>
+      <Real Name="Z">485.46065815036332</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">170.82114732265259</Real>
+      <Real Name="Y">-1.982834661033742</Real>
+      <Real Name="Z">33.543308683045097</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">255.0746460941352</Real>
+      <Real Name="Y">425.54828663876566</Real>
+      <Real Name="Z">80.319374417003388</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.946888619097663</Real>
+      <Real Name="Y">-152.94986678788436</Real>
+      <Real Name="Z">-63.750139098591546</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-124.3293024099048</Real>
+      <Real Name="Y">-150.81722253057936</Real>
+      <Real Name="Z">10.557260322242259</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-817.71307806823643</Real>
+      <Real Name="Y">-793.10393523880407</Real>
+      <Real Name="Z">-517.87628237546585</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">149.04620081593018</Real>
+      <Real Name="Y">199.6207408494484</Real>
+      <Real Name="Z">153.02236055477556</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">706.26074126974959</Real>
+      <Real Name="Y">518.66337031331341</Real>
+      <Real Name="Z">393.90753665237742</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.223045160882108</Real>
+      <Real Name="Y">-78.002337014136003</Real>
+      <Real Name="Z">-777.82409900513653</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.901525001164281</Real>
+      <Real Name="Y">-193.22526901317087</Real>
+      <Real Name="Z">727.02726886243022</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.301822295246986</Real>
+      <Real Name="Y">58.138469056324034</Real>
+      <Real Name="Z">152.87480721173816</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-539.7200512202578</Real>
+      <Real Name="Y">-517.6596513051278</Real>
+      <Real Name="Z">-920.58707145874359</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">124.74492610469412</Real>
+      <Real Name="Y">77.088334762687367</Real>
+      <Real Name="Z">159.63517273973926</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">248.44624382000544</Real>
+      <Real Name="Y">494.72575450106399</Real>
+      <Real Name="Z">736.70977491315728</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-498.07651580539971</Real>
+      <Real Name="Y">-346.1743803138574</Real>
+      <Real Name="Z">-9.5622246002361315</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">538.46281081625398</Real>
+      <Real Name="Y">99.939503378421207</Real>
+      <Real Name="Z">-99.589703246246813</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">218.71282893968117</Real>
+      <Real Name="Y">87.576921834086988</Real>
+      <Real Name="Z">-15.327315728165466</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.47194454017472</Real>
+      <Real Name="Y">784.37034143531548</Real>
+      <Real Name="Z">89.675341830867026</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-13.505090886218113</Real>
+      <Real Name="Y">-167.82504428201705</Real>
+      <Real Name="Z">-24.906042732779198</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-198.36396111325519</Real>
+      <Real Name="Y">-819.62382583292083</Real>
+      <Real Name="Z">-146.83544541549236</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1754.6708203843812</Real>
+      <Real Name="Y">-227.03029025106258</Real>
+      <Real Name="Z">711.19112185230108</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1038.4483313951523</Real>
+      <Real Name="Y">300.12066463076326</Real>
+      <Real Name="Z">-575.61825979683704</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-367.95607240003193</Real>
+      <Real Name="Y">-184.5499910745699</Real>
+      <Real Name="Z">-291.89637086488744</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-304.59155516368895</Real>
+      <Real Name="Y">70.553735210285353</Real>
+      <Real Name="Z">-102.59990575392321</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.30044083186289</Real>
+      <Real Name="Y">-134.82218432359264</Real>
+      <Real Name="Z">52.440013109968248</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.2960009457218</Real>
+      <Real Name="Y">-5.2039549618652963</Real>
+      <Real Name="Z">38.031034410312259</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">933.65756655796758</Real>
+      <Real Name="Y">-223.41048634057969</Real>
+      <Real Name="Z">33.362162816100138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-729.37600614606231</Real>
+      <Real Name="Y">288.59868211447099</Real>
+      <Real Name="Z">103.6571153533192</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-435.13590104253024</Real>
+      <Real Name="Y">287.72489232448413</Real>
+      <Real Name="Z">-50.642572071196767</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-196.36429798736626</Real>
+      <Real Name="Y">-1133.9959355411797</Real>
+      <Real Name="Z">-9.1095946458732868</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">87.492256128192025</Real>
+      <Real Name="Y">175.41452525019028</Real>
+      <Real Name="Z">-31.133365730311944</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">140.08816737257544</Real>
+      <Real Name="Y">1125.1235996567757</Real>
+      <Real Name="Z">36.419938913132135</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">213.66660235340473</Real>
+      <Real Name="Y">1120.8113944183422</Real>
+      <Real Name="Z">793.95441415553273</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-177.70797368181022</Real>
+      <Real Name="Y">-688.73370733729041</Real>
+      <Real Name="Z">-650.60214185478731</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.087372172313977</Real>
+      <Real Name="Y">-295.33360342515181</Real>
+      <Real Name="Z">-146.11989362701348</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">190.90336827378727</Real>
+      <Real Name="Y">-272.43738454306265</Real>
+      <Real Name="Z">-322.25404681448668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.38983551710652</Real>
+      <Real Name="Y">371.12093546828351</Real>
+      <Real Name="Z">15.207012605395526</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">106.19089861347751</Real>
+      <Real Name="Y">21.427830659047853</Real>
+      <Real Name="Z">306.84270785386229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">703.22551000574435</Real>
+      <Real Name="Y">-426.99241724869921</Real>
+      <Real Name="Z">-173.08329946940722</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-337.78211916476653</Real>
+      <Real Name="Y">504.76424099569738</Real>
+      <Real Name="Z">48.432142383657805</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-593.88020029845416</Real>
+      <Real Name="Y">-79.379810483644007</Real>
+      <Real Name="Z">200.83137058225122</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-975.49712417614899</Real>
+      <Real Name="Y">1146.1702221222661</Real>
+      <Real Name="Z">-649.22596494058348</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.80612447668895</Real>
+      <Real Name="Y">-163.58341744921586</Real>
+      <Real Name="Z">97.450703684030998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">566.22552172764404</Real>
+      <Real Name="Y">-819.44624055365637</Real>
+      <Real Name="Z">521.10226830955639</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">939.99611937497536</Real>
+      <Real Name="Y">-129.65301481242707</Real>
+      <Real Name="Z">48.798023200180197</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.71185317815244</Real>
+      <Real Name="Y">92.367299214311288</Real>
+      <Real Name="Z">-29.284122822839773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-892.00791387951494</Real>
+      <Real Name="Y">-61.010049102258385</Real>
+      <Real Name="Z">-96.108834473804677</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-564.6142669241251</Real>
+      <Real Name="Y">-710.21740097797704</Real>
+      <Real Name="Z">116.97516601641139</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">591.23973250851714</Real>
+      <Real Name="Y">703.00325912177186</Real>
+      <Real Name="Z">26.756351480476141</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">26.015925302976427</Real>
+      <Real Name="Y">132.40757511101228</Real>
+      <Real Name="Z">-78.686492091563679</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-274.9734610645454</Real>
+      <Real Name="Y">-1095.3934055847362</Real>
+      <Real Name="Z">1208.1768369490671</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">314.31144975965799</Real>
+      <Real Name="Y">617.81508137300284</Real>
+      <Real Name="Z">-558.41182955014426</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-75.49579624524678</Real>
+      <Real Name="Y">138.58452045227494</Real>
+      <Real Name="Z">-196.70444850209418</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">401.89273278865727</Real>
+      <Real Name="Y">-479.97139693743554</Real>
+      <Real Name="Z">-8.1256617359909598</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.531290343953827</Real>
+      <Real Name="Y">262.73499802072314</Real>
+      <Real Name="Z">-46.896111797919801</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-422.72279848018843</Real>
+      <Real Name="Y">334.38077679418842</Real>
+      <Real Name="Z">-18.048730911625825</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-110.65855073859044</Real>
+      <Real Name="Y">446.16380739841111</Real>
+      <Real Name="Z">-5.5017072566913185</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-41.81411278321432</Real>
+      <Real Name="Y">-217.58875646375282</Real>
+      <Real Name="Z">40.890710779589831</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">36.885952314665857</Real>
+      <Real Name="Y">-138.23025022351135</Real>
+      <Real Name="Z">4.9915967201784817</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.921498200433255</Real>
+      <Real Name="Y">776.78569482845694</Real>
+      <Real Name="Z">71.517220851337299</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-23.697907195091616</Real>
+      <Real Name="Y">-181.6063660375367</Real>
+      <Real Name="Z">-62.874627698307037</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">157.47011869500756</Real>
+      <Real Name="Y">-673.49875528576842</Real>
+      <Real Name="Z">-46.818999285326093</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.89117857543677</Real>
+      <Real Name="Y">-681.12280312950372</Real>
+      <Real Name="Z">-271.52028678606905</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">210.18210227587682</Real>
+      <Real Name="Y">598.56166693407283</Real>
+      <Real Name="Z">155.49626233404675</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">94.389247006675788</Real>
+      <Real Name="Y">152.36348873317033</Real>
+      <Real Name="Z">37.7999670461421</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-223.27257464853221</Real>
+      <Real Name="Y">913.60912340352888</Real>
+      <Real Name="Z">856.19972536832881</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">66.129418539405677</Real>
+      <Real Name="Y">-168.33002614898518</Real>
+      <Real Name="Z">-196.03155391998945</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-98.490025715875873</Real>
+      <Real Name="Y">-693.71749432550155</Real>
+      <Real Name="Z">-520.1810033338935</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-218.8902392295785</Real>
+      <Real Name="Y">182.73177675045136</Real>
+      <Real Name="Z">-352.45055946031232</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">109.29829745773482</Real>
+      <Real Name="Y">-103.60872910237316</Real>
+      <Real Name="Z">349.34284712387574</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">123.53254815423713</Real>
+      <Real Name="Y">-65.899100924098448</Real>
+      <Real Name="Z">114.57321695164003</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-989.93222751627025</Real>
+      <Real Name="Y">0.32131278179797107</Real>
+      <Real Name="Z">-54.580062677869122</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">720.98669780624289</Real>
+      <Real Name="Y">-127.03827148760148</Real>
+      <Real Name="Z">-19.61467066270378</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">330.54785273765748</Real>
+      <Real Name="Y">7.55131257362018</Real>
+      <Real Name="Z">5.2487821539796613</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1553.5341879885473</Real>
+      <Real Name="Y">-896.85580963754694</Real>
+      <Real Name="Z">-475.39718648032084</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">336.73862083405834</Real>
+      <Real Name="Y">67.509719391752853</Real>
+      <Real Name="Z">142.24097720648791</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1182.1397884142104</Real>
+      <Real Name="Y">215.93057302404318</Real>
+      <Real Name="Z">333.33527776563449</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.241230890632352</Real>
+      <Real Name="Y">1441.1034073471924</Real>
+      <Real Name="Z">69.266826416832487</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-60.851323566136742</Real>
+      <Real Name="Y">-813.21378293810949</Real>
+      <Real Name="Z">-426.71777885127182</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.77779909627554</Real>
+      <Real Name="Y">-627.40397140026994</Real>
+      <Real Name="Z">577.39456733571569</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">246.07146392213943</Real>
+      <Real Name="Y">505.89224131871384</Real>
+      <Real Name="Z">164.38336038102923</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.099663086600202</Real>
+      <Real Name="Y">-130.20730042172201</Real>
+      <Real Name="Z">-107.66530114559478</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-259.98673360590806</Real>
+      <Real Name="Y">-514.18848000211096</Real>
+      <Real Name="Z">-78.672961572897989</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">758.17724582577057</Real>
+      <Real Name="Y">-1676.4017434226712</Real>
+      <Real Name="Z">823.14540714543296</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.197395566760306</Real>
+      <Real Name="Y">1184.3505637855685</Real>
+      <Real Name="Z">120.44241619936412</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-940.385244114952</Real>
+      <Real Name="Y">562.26058894088067</Real>
+      <Real Name="Z">-729.27359509686858</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">803.25497597773506</Real>
+      <Real Name="Y">-1992.3504096034076</Real>
+      <Real Name="Z">-668.95015336880419</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-61.976220994189049</Real>
+      <Real Name="Y">847.69165611555286</Real>
+      <Real Name="Z">214.57841933970928</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-548.56255413839983</Real>
+      <Real Name="Y">687.03838703733857</Real>
+      <Real Name="Z">431.63976782867377</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.600338338168939</Real>
+      <Real Name="Y">720.9594576326374</Real>
+      <Real Name="Z">1177.2719363856654</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-105.60744112666683</Real>
+      <Real Name="Y">-586.15699026829952</Real>
+      <Real Name="Z">-1005.3797830985541</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.360658216194906</Real>
+      <Real Name="Y">-64.863199768613953</Real>
+      <Real Name="Z">-259.6925317112532</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_restraints_s.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_restraints_s.xml
new file mode 100644 (file)
index 0000000..60a445e
--- /dev/null
@@ -0,0 +1,938 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVrestraint/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">206.51497</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">0</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">0</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">0</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">0</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">0</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">0</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">0</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">0</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">0</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">147.50229</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">0</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">0</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">0</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">0</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">0</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">0</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">0</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">0</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">0</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">113.29707</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1416.9506</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1417.6257</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1417.9313</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1417.6633</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1416.674</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1414.9219</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1412.4851</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1409.4904</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1406.0587</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1402.287</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1398.2808</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1394.2041</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1390.2968</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1386.8184</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1383.9486</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1381.7008</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1379.9247</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1378.4041</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1376.9861</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1375.6964</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1374.7496</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">1328.0801</Real>
+      <Real Name="Y">-905.41962</Real>
+      <Real Name="Z">-186.25797</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-168.43202</Real>
+      <Real Name="Y">-246.43636</Real>
+      <Real Name="Z">-51.156475</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1021.8512</Real>
+      <Real Name="Y">337.99335</Real>
+      <Real Name="Z">-144.86295</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-126.20997</Real>
+      <Real Name="Y">187.18262</Real>
+      <Real Name="Z">32.50943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">501.29007</Real>
+      <Real Name="Y">1182.1635</Real>
+      <Real Name="Z">187.31845</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">243.30983</Real>
+      <Real Name="Y">-321.04724</Real>
+      <Real Name="Z">-452.61737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1481.8146</Real>
+      <Real Name="Y">77.127632</Real>
+      <Real Name="Z">1126.9567</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">371.7384</Real>
+      <Real Name="Y">-259.90472</Real>
+      <Real Name="Z">-593.56976</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">299.84711</Real>
+      <Real Name="Y">-271.19147</Real>
+      <Real Name="Z">3.3099022</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.9437332</Real>
+      <Real Name="Y">-34.118774</Real>
+      <Real Name="Z">25.598434</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.4130936</Real>
+      <Real Name="Y">29.789534</Real>
+      <Real Name="Z">-15.151001</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.692646</Real>
+      <Real Name="Y">15.672777</Real>
+      <Real Name="Z">-6.2329483</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.92453</Real>
+      <Real Name="Y">18.175198</Real>
+      <Real Name="Z">-5.9909973</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.827095</Real>
+      <Real Name="Y">-7.2495995</Real>
+      <Real Name="Z">4.4740391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.5941353</Real>
+      <Real Name="Y">-22.388287</Real>
+      <Real Name="Z">3.6513939</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.84644</Real>
+      <Real Name="Y">-59.014282</Real>
+      <Real Name="Z">-670.28906</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-396.07312</Real>
+      <Real Name="Y">36.163364</Real>
+      <Real Name="Z">385.3652</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.5577545</Real>
+      <Real Name="Y">-11.444275</Real>
+      <Real Name="Z">206.57773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">350.32874</Real>
+      <Real Name="Y">397.17709</Real>
+      <Real Name="Z">-116.30058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.5585</Real>
+      <Real Name="Y">-227.96716</Real>
+      <Real Name="Z">94.896805</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-339.72333</Real>
+      <Real Name="Y">-572.75415</Real>
+      <Real Name="Z">260.37488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">651.81378</Real>
+      <Real Name="Y">-500.94174</Real>
+      <Real Name="Z">-426.18787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-115.29115</Real>
+      <Real Name="Y">125.72142</Real>
+      <Real Name="Z">76.887466</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-650.86029</Real>
+      <Real Name="Y">444.51999</Real>
+      <Real Name="Z">222.33656</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.49205</Real>
+      <Real Name="Y">279.29849</Real>
+      <Real Name="Z">51.27597</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.384453</Real>
+      <Real Name="Y">-157.0712</Real>
+      <Real Name="Z">-104.35411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.73877</Real>
+      <Real Name="Y">-208.50685</Real>
+      <Real Name="Z">-132.81796</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-589.78198</Real>
+      <Real Name="Y">237.27377</Real>
+      <Real Name="Z">851.64764</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">395.72586</Real>
+      <Real Name="Y">-34.571304</Real>
+      <Real Name="Z">-874.73145</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">174.02786</Real>
+      <Real Name="Y">-179.41884</Real>
+      <Real Name="Z">-67.554199</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1099.5664</Real>
+      <Real Name="Y">553.65118</Real>
+      <Real Name="Z">903.68042</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-161.87419</Real>
+      <Real Name="Y">-835.15405</Real>
+      <Real Name="Z">-374.62759</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-835.94647</Real>
+      <Real Name="Y">336.87842</Real>
+      <Real Name="Z">-602.75006</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.31354</Real>
+      <Real Name="Y">747.55859</Real>
+      <Real Name="Z">136.28569</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.529114</Real>
+      <Real Name="Y">-645.92108</Real>
+      <Real Name="Z">-47.856651</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.203033</Real>
+      <Real Name="Y">-129.11758</Real>
+      <Real Name="Z">-33.408222</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">46.295322</Real>
+      <Real Name="Y">-167.90427</Real>
+      <Real Name="Z">-913.22412</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-121.78538</Real>
+      <Real Name="Y">49.506058</Real>
+      <Real Name="Z">273.49045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">378.02087</Real>
+      <Real Name="Y">-5.7678871</Real>
+      <Real Name="Z">685.09454</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.12416</Real>
+      <Real Name="Y">49.895081</Real>
+      <Real Name="Z">-861.93378</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-268.05084</Real>
+      <Real Name="Y">160.69049</Real>
+      <Real Name="Z">483.4491</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.8040199</Real>
+      <Real Name="Y">-196.19897</Real>
+      <Real Name="Z">167.55754</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.91718</Real>
+      <Real Name="Y">60.107788</Real>
+      <Real Name="Z">367.77667</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">548.89014</Real>
+      <Real Name="Y">-52.443062</Real>
+      <Real Name="Z">-12.278031</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">145.01877</Real>
+      <Real Name="Y">-23.074142</Real>
+      <Real Name="Z">-246.45247</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.208099</Real>
+      <Real Name="Y">37.623688</Real>
+      <Real Name="Z">360.18265</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">243.95145</Real>
+      <Real Name="Y">-5.7312164</Real>
+      <Real Name="Z">-127.35837</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.632771</Real>
+      <Real Name="Y">-114.10121</Real>
+      <Real Name="Z">-113.64888</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-333.63986</Real>
+      <Real Name="Y">-2020.2126</Real>
+      <Real Name="Z">-705.45239</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.1012</Real>
+      <Real Name="Y">1302.4658</Real>
+      <Real Name="Z">328.3046</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">273.8157</Real>
+      <Real Name="Y">399.7489</Real>
+      <Real Name="Z">73.872589</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">454.22412</Real>
+      <Real Name="Y">1035.556</Real>
+      <Real Name="Z">-836.83051</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-161.20929</Real>
+      <Real Name="Y">-546.36926</Real>
+      <Real Name="Z">227.77548</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.52246</Real>
+      <Real Name="Y">-418.64044</Real>
+      <Real Name="Z">550.23285</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">50.714981</Real>
+      <Real Name="Y">92.956924</Real>
+      <Real Name="Z">0.86141014</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.6475163</Real>
+      <Real Name="Y">-40.904976</Real>
+      <Real Name="Z">-1.3789215</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.013699</Real>
+      <Real Name="Y">-30.334343</Real>
+      <Real Name="Z">10.519749</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-456.43924</Real>
+      <Real Name="Y">971.5932</Real>
+      <Real Name="Z">410.11536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">615.54852</Real>
+      <Real Name="Y">-684.91302</Real>
+      <Real Name="Z">-459.81165</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">41.963497</Real>
+      <Real Name="Y">-153.75214</Real>
+      <Real Name="Z">-22.296715</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">267.69629</Real>
+      <Real Name="Y">-462.2132</Real>
+      <Real Name="Z">-1386.6565</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-83.662354</Real>
+      <Real Name="Y">332.90689</Real>
+      <Real Name="Z">1034.3909</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.44519</Real>
+      <Real Name="Y">26.493641</Real>
+      <Real Name="Z">210.39384</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.14674</Real>
+      <Real Name="Y">-125.65987</Real>
+      <Real Name="Z">-120.06331</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.826218</Real>
+      <Real Name="Y">126.72969</Real>
+      <Real Name="Z">74.477928</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-64.104065</Real>
+      <Real Name="Y">41.735107</Real>
+      <Real Name="Z">152.18146</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">329.82257</Real>
+      <Real Name="Y">-341.07834</Real>
+      <Real Name="Z">-68.011322</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.4202</Real>
+      <Real Name="Y">415.60812</Real>
+      <Real Name="Z">94.081383</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.2318</Real>
+      <Real Name="Y">68.751266</Real>
+      <Real Name="Z">37.600391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">812.92633</Real>
+      <Real Name="Y">-958.58527</Real>
+      <Real Name="Z">-477.29324</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-357.80804</Real>
+      <Real Name="Y">354.71997</Real>
+      <Real Name="Z">551.77094</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-385.34528</Real>
+      <Real Name="Y">996.93488</Real>
+      <Real Name="Z">106.35823</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.4966354</Real>
+      <Real Name="Y">-8.3873444</Real>
+      <Real Name="Z">1.8501663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.71152878</Real>
+      <Real Name="Y">5.5084839</Real>
+      <Real Name="Z">0.19663811</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.8261223</Real>
+      <Real Name="Y">3.4832954</Real>
+      <Real Name="Z">-3.0169563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-383.9491</Real>
+      <Real Name="Y">868.92578</Real>
+      <Real Name="Z">238.66106</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">283.43579</Real>
+      <Real Name="Y">-487.68692</Real>
+      <Real Name="Z">-676.91138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-322.44012</Real>
+      <Real Name="Y">-447.69458</Real>
+      <Real Name="Z">303.06247</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">371.004</Real>
+      <Real Name="Y">23.711849</Real>
+      <Real Name="Z">-150.32498</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.17123</Real>
+      <Real Name="Y">62.020271</Real>
+      <Real Name="Z">39.461594</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.651581</Real>
+      <Real Name="Y">-34.116806</Real>
+      <Real Name="Z">73.409966</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-516.72321</Real>
+      <Real Name="Y">-1285.8428</Real>
+      <Real Name="Z">-331.92261</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.48422</Real>
+      <Real Name="Y">621.7879</Real>
+      <Real Name="Z">-279.15588</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.69226</Real>
+      <Real Name="Y">655.15479</Real>
+      <Real Name="Z">633.10016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1197.2902</Real>
+      <Real Name="Y">443.27393</Real>
+      <Real Name="Z">-794.67255</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">821.09784</Real>
+      <Real Name="Y">-311.37335</Real>
+      <Real Name="Z">573.11304</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.85938</Real>
+      <Real Name="Y">-31.927046</Real>
+      <Real Name="Z">40.545853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">221.95505</Real>
+      <Real Name="Y">425.49542</Real>
+      <Real Name="Z">167.55403</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-31.313751</Real>
+      <Real Name="Y">-132.10576</Real>
+      <Real Name="Z">-73.267296</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.17723</Real>
+      <Real Name="Y">-122.47018</Real>
+      <Real Name="Z">-18.45085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1047.017</Real>
+      <Real Name="Y">-1077.9473</Real>
+      <Real Name="Z">-856.0321</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.24394</Real>
+      <Real Name="Y">211.21786</Real>
+      <Real Name="Z">194.52278</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">576.43445</Real>
+      <Real Name="Y">693.13055</Real>
+      <Real Name="Z">680.25586</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.81073</Real>
+      <Real Name="Y">384.37109</Real>
+      <Real Name="Z">-1020.7798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.925613</Real>
+      <Real Name="Y">-314.84653</Real>
+      <Real Name="Z">752.20526</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.983704</Real>
+      <Real Name="Y">34.67823</Real>
+      <Real Name="Z">253.66602</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.83536</Real>
+      <Real Name="Y">-515.9375</Real>
+      <Real Name="Z">-926.99768</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.01855</Real>
+      <Real Name="Y">81.225128</Real>
+      <Real Name="Z">144.74394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.712914</Real>
+      <Real Name="Y">258.8483</Real>
+      <Real Name="Z">802.93335</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-594.66699</Real>
+      <Real Name="Y">-66.321411</Real>
+      <Real Name="Z">-122.3287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.96118</Real>
+      <Real Name="Y">-123.46442</Real>
+      <Real Name="Z">53.935078</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">255.68594</Real>
+      <Real Name="Y">13.463104</Real>
+      <Real Name="Z">84.234901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">611.06403</Real>
+      <Real Name="Y">839.98096</Real>
+      <Real Name="Z">-265.93948</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.76723</Real>
+      <Real Name="Y">-206.92859</Real>
+      <Real Name="Z">15.903809</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-474.36337</Real>
+      <Real Name="Y">-878.25006</Real>
+      <Real Name="Z">-40.947277</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1588.7339</Real>
+      <Real Name="Y">-232.77469</Real>
+      <Real Name="Z">796.08411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-819.45349</Real>
+      <Real Name="Y">397.17056</Real>
+      <Real Name="Z">-589.52014</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-208.16838</Real>
+      <Real Name="Y">-145.08554</Real>
+      <Real Name="Z">-321.92294</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.82996</Real>
+      <Real Name="Y">54.627377</Real>
+      <Real Name="Z">125.67847</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">164.04228</Real>
+      <Real Name="Y">-162.1525</Real>
+      <Real Name="Z">29.635811</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.01358</Real>
+      <Real Name="Y">-6.9365005</Real>
+      <Real Name="Z">24.989197</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">850.54883</Real>
+      <Real Name="Y">-484.90958</Real>
+      <Real Name="Z">-96.156876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-585.07996</Real>
+      <Real Name="Y">327.78162</Real>
+      <Real Name="Z">75.857475</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.38504</Real>
+      <Real Name="Y">437.1387</Real>
+      <Real Name="Z">-100.35383</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.57202</Real>
+      <Real Name="Y">-1829.7993</Real>
+      <Real Name="Z">26.108131</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">131.28232</Real>
+      <Real Name="Y">246.10884</Real>
+      <Real Name="Z">-29.212749</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.65274</Real>
+      <Real Name="Y">1232.6938</Real>
+      <Real Name="Z">-54.418034</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">450.02127</Real>
+      <Real Name="Y">1041.5649</Real>
+      <Real Name="Z">887.98877</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-318.98688</Real>
+      <Real Name="Y">-643.92682</Real>
+      <Real Name="Z">-608.16724</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.35104</Real>
+      <Real Name="Y">-240.23148</Real>
+      <Real Name="Z">-238.99628</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">249.05176</Real>
+      <Real Name="Y">-336.92123</Real>
+      <Real Name="Z">-438.15317</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-278.16458</Real>
+      <Real Name="Y">346.29578</Real>
+      <Real Name="Z">251.85017</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-170.48038</Real>
+      <Real Name="Y">134.61818</Real>
+      <Real Name="Z">498.15747</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">705.51678</Real>
+      <Real Name="Y">-340.79214</Real>
+      <Real Name="Z">-201.04681</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.73311</Real>
+      <Real Name="Y">455.21826</Real>
+      <Real Name="Z">-16.030602</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-721.95563</Real>
+      <Real Name="Y">-145.64851</Real>
+      <Real Name="Z">164.36844</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1105.4644</Real>
+      <Real Name="Y">1483.2834</Real>
+      <Real Name="Z">-676.70117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.7243</Real>
+      <Real Name="Y">-204.35464</Real>
+      <Real Name="Z">94.394508</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">633.70587</Real>
+      <Real Name="Y">-954.17651</Real>
+      <Real Name="Z">445.8291</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">745.11243</Real>
+      <Real Name="Y">16.52681</Real>
+      <Real Name="Z">139.09857</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.33755</Real>
+      <Real Name="Y">98.235008</Real>
+      <Real Name="Z">-32.63245</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-917.26093</Real>
+      <Real Name="Y">126.39825</Real>
+      <Real Name="Z">-123.53004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-772.63013</Real>
+      <Real Name="Y">-1270.5387</Real>
+      <Real Name="Z">100.9455</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">692.72235</Real>
+      <Real Name="Y">966.65375</Real>
+      <Real Name="Z">65.165123</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">48.212032</Real>
+      <Real Name="Y">265.45648</Real>
+      <Real Name="Z">-73.229019</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.72446</Real>
+      <Real Name="Y">-684.98834</Real>
+      <Real Name="Z">816.4967</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">138.89417</Real>
+      <Real Name="Y">498.82236</Real>
+      <Real Name="Z">-585.24475</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.289062</Real>
+      <Real Name="Y">73.16777</Real>
+      <Real Name="Z">-119.9601</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.342834</Real>
+      <Real Name="Y">-109.94833</Real>
+      <Real Name="Z">269.66519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.57128</Real>
+      <Real Name="Y">246.46478</Real>
+      <Real Name="Z">-44.672508</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-294.81183</Real>
+      <Real Name="Y">350.87155</Real>
+      <Real Name="Z">70.188057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">86.489182</Real>
+      <Real Name="Y">493.21359</Real>
+      <Real Name="Z">-79.518631</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.404449</Real>
+      <Real Name="Y">-183.92371</Real>
+      <Real Name="Z">70.392128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">28.468298</Real>
+      <Real Name="Y">-197.2832</Real>
+      <Real Name="Z">32.172958</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">84.057938</Real>
+      <Real Name="Y">775.39368</Real>
+      <Real Name="Z">72.336319</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.154591</Real>
+      <Real Name="Y">-171.12547</Real>
+      <Real Name="Z">-64.660454</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-71.92923</Real>
+      <Real Name="Y">-730.57117</Real>
+      <Real Name="Z">-10.302049</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.221291</Real>
+      <Real Name="Y">-726.92511</Real>
+      <Real Name="Z">-203.6001</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.04897</Real>
+      <Real Name="Y">628.08557</Real>
+      <Real Name="Z">109.01802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">72.975876</Real>
+      <Real Name="Y">179.24362</Real>
+      <Real Name="Z">40.431633</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.25566</Real>
+      <Real Name="Y">919.89496</Real>
+      <Real Name="Z">781.59467</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.557068</Real>
+      <Real Name="Y">-161.97707</Real>
+      <Real Name="Z">-198.03044</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.21704</Real>
+      <Real Name="Y">-711.06641</Real>
+      <Real Name="Z">-526.51447</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.40787</Real>
+      <Real Name="Y">322.18402</Real>
+      <Real Name="Z">-416.35892</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.68941</Real>
+      <Real Name="Y">-264.05405</Real>
+      <Real Name="Z">437.78967</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">143.49042</Real>
+      <Real Name="Y">-127.52937</Real>
+      <Real Name="Z">87.993553</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-877.78296</Real>
+      <Real Name="Y">-122.86922</Real>
+      <Real Name="Z">-62.020088</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.67346</Real>
+      <Real Name="Y">-48.831436</Real>
+      <Real Name="Z">7.5404282</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">310.36789</Real>
+      <Real Name="Y">58.607738</Real>
+      <Real Name="Z">7.0199661</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1509.45</Real>
+      <Real Name="Y">-281.94852</Real>
+      <Real Name="Z">-666.91913</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">289.51474</Real>
+      <Real Name="Y">-38.580463</Real>
+      <Real Name="Z">213.93591</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1063.9144</Real>
+      <Real Name="Y">348.36923</Real>
+      <Real Name="Z">340.38068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-388.63828</Real>
+      <Real Name="Y">1486.1469</Real>
+      <Real Name="Z">329.82358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.95001221</Real>
+      <Real Name="Y">-750.84192</Real>
+      <Real Name="Z">-583.82733</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">360.83084</Real>
+      <Real Name="Y">-479.94693</Real>
+      <Real Name="Z">486.13925</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">203.07156</Real>
+      <Real Name="Y">434.19211</Real>
+      <Real Name="Z">127.79526</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.191162</Real>
+      <Real Name="Y">-121.64478</Real>
+      <Real Name="Z">-82.762993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-225.25742</Real>
+      <Real Name="Y">-493.06845</Real>
+      <Real Name="Z">-109.82661</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1168.516</Real>
+      <Real Name="Y">-2062.2756</Real>
+      <Real Name="Z">655.573</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-135.16344</Real>
+      <Real Name="Y">1272.9781</Real>
+      <Real Name="Z">326.51572</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1028.6063</Real>
+      <Real Name="Y">412.54956</Real>
+      <Real Name="Z">-793.30304</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">795.23907</Real>
+      <Real Name="Y">-1321.1536</Real>
+      <Real Name="Z">-304.05066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-253.04129</Real>
+      <Real Name="Y">692.17523</Real>
+      <Real Name="Z">29.826693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-505.57803</Real>
+      <Real Name="Y">713.30597</Real>
+      <Real Name="Z">327.3511</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.65976</Real>
+      <Real Name="Y">794.48108</Real>
+      <Real Name="Z">1309.0448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-100.48924</Real>
+      <Real Name="Y">-707.99548</Real>
+      <Real Name="Z">-999.84241</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.229813</Real>
+      <Real Name="Y">31.06391</Real>
+      <Real Name="Z">-230.99925</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_simtemp_d.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_simtemp_d.xml
new file mode 100644 (file)
index 0000000..22cde15
--- /dev/null
@@ -0,0 +1,1876 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1540.8424715903325</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1538.6632065131557</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1535.0941818836291</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1530.3332101145202</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1524.7480961877195</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1518.8076583704619</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1513.1953718323971</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1508.2665830812261</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1504.2226867714646</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1501.0643203583074</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1498.6196011920215</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1496.6162274111266</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1494.776973577231</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1492.911966048493</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1490.9799337116874</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1489.0989478132631</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1487.2627397462475</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1485.675888193025</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1484.6437526218217</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1484.3910242299587</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1485.0139655547116</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">591.05883559713129</Real>
+      <Real Name="Y">-793.14769644731155</Real>
+      <Real Name="Z">-86.566135555337183</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.10416192897139</Real>
+      <Real Name="Y">-248.34787115051958</Real>
+      <Real Name="Z">33.731915643063168</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-447.87034327118334</Real>
+      <Real Name="Y">277.24871713631023</Real>
+      <Real Name="Z">-222.44607086802418</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-126.20429561523332</Real>
+      <Real Name="Y">187.1742852151848</Real>
+      <Real Name="Z">32.511296088747066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">956.18480324127597</Real>
+      <Real Name="Y">827.64671668504297</Real>
+      <Real Name="Z">-10.809290146259968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-124.9082646993632</Real>
+      <Real Name="Y">81.547629059058295</Real>
+      <Real Name="Z">-241.95578791522246</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1274.2180632275758</Real>
+      <Real Name="Y">-168.78058125408481</Real>
+      <Real Name="Z">906.90924989759571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">250.85991443674456</Real>
+      <Real Name="Y">-118.60223683584252</Real>
+      <Real Name="Z">-464.2734463429382</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">284.15669907608304</Real>
+      <Real Name="Y">-264.276804678394</Real>
+      <Real Name="Z">-25.473244337247451</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.9439451133138306</Real>
+      <Real Name="Y">-34.118269995556616</Real>
+      <Real Name="Z">25.599744969965968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.4127790283902044</Real>
+      <Real Name="Y">29.789555476024951</Real>
+      <Real Name="Z">-15.15156229917072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.6926231254665893</Real>
+      <Real Name="Y">15.672428695211565</Real>
+      <Real Name="Z">-6.2336867483995704</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.9238407031470217</Real>
+      <Real Name="Y">18.173037328900413</Real>
+      <Real Name="Z">-5.9923174476106666</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.8274596585862781</Real>
+      <Real Name="Y">-7.2485714703465902</Real>
+      <Real Name="Z">4.4746057398654564</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.5938366310254963</Real>
+      <Real Name="Y">-22.387254569184783</Real>
+      <Real Name="Z">3.651938441219535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.84756794356167</Real>
+      <Real Name="Y">-59.015204675975824</Real>
+      <Real Name="Z">-670.2890616125344</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-396.07444656305222</Real>
+      <Real Name="Y">36.163795342919087</Real>
+      <Real Name="Z">385.36577562606055</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.5581609756780068</Real>
+      <Real Name="Y">-11.443622742309477</Real>
+      <Real Name="Z">206.57754853761458</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">350.33129727574658</Real>
+      <Real Name="Y">397.18309927522222</Real>
+      <Real Name="Z">-116.30426778100971</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.55877353223039</Real>
+      <Real Name="Y">-227.96761061071751</Real>
+      <Real Name="Z">94.897375348492346</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-339.725041659787</Real>
+      <Real Name="Y">-572.75604701314819</Real>
+      <Real Name="Z">260.37570479834221</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">651.81722389484867</Real>
+      <Real Name="Y">-500.93803435885354</Real>
+      <Real Name="Z">-426.18563110928528</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-115.29325310886911</Real>
+      <Real Name="Y">125.72006246379296</Real>
+      <Real Name="Z">76.886764579646169</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-650.86298197258623</Real>
+      <Real Name="Y">444.51723806895859</Real>
+      <Real Name="Z">222.33540054888451</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.491263427466443</Real>
+      <Real Name="Y">279.29682840185251</Real>
+      <Real Name="Z">51.271700489170328</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.384000186502675</Real>
+      <Real Name="Y">-157.07118696890899</Real>
+      <Real Name="Z">-104.353454542121</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.73842219713687</Real>
+      <Real Name="Y">-208.50665312601731</Real>
+      <Real Name="Z">-132.81752529624674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-589.77948745251638</Real>
+      <Real Name="Y">237.27131788838076</Real>
+      <Real Name="Z">851.64625480706377</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">395.7235649930285</Real>
+      <Real Name="Y">-34.571300731264699</Real>
+      <Real Name="Z">-874.73194750969378</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">174.02702194182416</Real>
+      <Real Name="Y">-179.41755028393868</Real>
+      <Real Name="Z">-67.55398075479124</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1099.5656675582929</Real>
+      <Real Name="Y">553.65758978756492</Real>
+      <Real Name="Z">903.67462001587194</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-161.87576659787464</Real>
+      <Real Name="Y">-835.1549688126878</Real>
+      <Real Name="Z">-374.62710844607091</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-835.94693688510563</Real>
+      <Real Name="Y">336.87779174493983</Real>
+      <Real Name="Z">-602.74950034846518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.31275576025774</Real>
+      <Real Name="Y">747.5609517160151</Real>
+      <Real Name="Z">136.28541693996794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.528151408777603</Real>
+      <Real Name="Y">-645.9208307933244</Real>
+      <Real Name="Z">-47.856831969247217</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.203383168403747</Real>
+      <Real Name="Y">-129.11641921631193</Real>
+      <Real Name="Z">-33.408224223678573</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">46.297276634282547</Real>
+      <Real Name="Y">-167.90639465351671</Real>
+      <Real Name="Z">-913.22718455332802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-121.78510656118857</Real>
+      <Real Name="Y">49.506685157977799</Real>
+      <Real Name="Z">273.49109958051969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">378.01977076983803</Real>
+      <Real Name="Y">-5.7666103656944614</Real>
+      <Real Name="Z">685.09668255517215</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.1249090561351</Real>
+      <Real Name="Y">49.892689437953464</Real>
+      <Real Name="Z">-861.93995160060967</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-268.05174251326491</Real>
+      <Real Name="Y">160.69175712099707</Real>
+      <Real Name="Z">483.45190357817785</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.8038878217764633</Real>
+      <Real Name="Y">-196.19771752634384</Real>
+      <Real Name="Z">167.55983040387557</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.92205347687684</Real>
+      <Real Name="Y">60.109704540778779</Real>
+      <Real Name="Z">367.77550141649056</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">548.89215039252178</Real>
+      <Real Name="Y">-52.442751526798261</Real>
+      <Real Name="Z">-12.277633369383906</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">145.01955364889372</Real>
+      <Real Name="Y">-23.075012254083816</Real>
+      <Real Name="Z">-246.45182283175731</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.209211352384756</Real>
+      <Real Name="Y">37.621917741800985</Real>
+      <Real Name="Z">360.18398367703071</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">243.9520333445858</Real>
+      <Real Name="Y">-5.7302963059085243</Real>
+      <Real Name="Z">-127.35871477621369</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.63297602425282</Real>
+      <Real Name="Y">-114.1010583032206</Real>
+      <Real Name="Z">-113.65029842318209</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-333.64286026756992</Real>
+      <Real Name="Y">-2020.208799115296</Real>
+      <Real Name="Z">-705.45563304288589</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.10290126381415</Real>
+      <Real Name="Y">1302.4641117046338</Real>
+      <Real Name="Z">328.30615954819899</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">273.81685897658355</Real>
+      <Real Name="Y">399.74751691818801</Real>
+      <Real Name="Z">73.873078538190597</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">454.22345093669884</Real>
+      <Real Name="Y">1035.5548259648767</Real>
+      <Real Name="Z">-836.82946049132431</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-161.20939972328284</Real>
+      <Real Name="Y">-546.36833054763736</Real>
+      <Real Name="Z">227.77684042938836</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.52207431387234</Real>
+      <Real Name="Y">-418.6416135983302</Real>
+      <Real Name="Z">550.23312753786172</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">50.714789325748072</Real>
+      <Real Name="Y">92.955766361639391</Real>
+      <Real Name="Z">0.86146395837502965</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.6475267044582758</Real>
+      <Real Name="Y">-40.904310982005519</Real>
+      <Real Name="Z">-1.3788704079398766</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.013553102381358</Real>
+      <Real Name="Y">-30.33376292407295</Real>
+      <Real Name="Z">10.519660201159862</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-456.43828080629254</Real>
+      <Real Name="Y">971.59441538699821</Real>
+      <Real Name="Z">410.11333375785665</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">615.54966872128932</Real>
+      <Real Name="Y">-684.91310710051232</Real>
+      <Real Name="Z">-459.81045209672379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">41.963572461936486</Real>
+      <Real Name="Y">-153.75311435656704</Real>
+      <Real Name="Z">-22.296539085864111</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">267.69613291910838</Real>
+      <Real Name="Y">-462.20972553885025</Real>
+      <Real Name="Z">-1386.6549880842458</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-83.659524299427403</Real>
+      <Real Name="Y">332.90525639100446</Real>
+      <Real Name="Z">1034.3907072719985</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.44810650690323</Real>
+      <Real Name="Y">26.491368264502292</Real>
+      <Real Name="Z">210.3919696244912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.14880120112713</Real>
+      <Real Name="Y">-125.66105970663331</Real>
+      <Real Name="Z">-120.06423297323177</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.827402523511154</Real>
+      <Real Name="Y">126.72976836791881</Real>
+      <Real Name="Z">74.47788677433627</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-64.104823191551532</Real>
+      <Real Name="Y">41.735484753509901</Real>
+      <Real Name="Z">152.18178394481959</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">329.82079157197649</Real>
+      <Real Name="Y">-341.07862575903147</Real>
+      <Real Name="Z">-68.010948756918864</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.41910618583302</Real>
+      <Real Name="Y">415.60897603657895</Real>
+      <Real Name="Z">94.081379034441525</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.23125899410202</Real>
+      <Real Name="Y">68.751530904261472</Real>
+      <Real Name="Z">37.600638026379343</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">812.93114317837717</Real>
+      <Real Name="Y">-958.58248986053172</Real>
+      <Real Name="Z">-477.29548004378773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-357.81013403259101</Real>
+      <Real Name="Y">354.71740611054906</Real>
+      <Real Name="Z">551.77270365112679</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-385.34592702023622</Real>
+      <Real Name="Y">996.93443283686327</Real>
+      <Real Name="Z">106.35867694925787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.4972570070667217</Real>
+      <Real Name="Y">-8.3863157951333989</Real>
+      <Real Name="Z">1.8497342150090503</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.71188944234359042</Real>
+      <Real Name="Y">5.5080332661960369</Real>
+      <Real Name="Z">0.19689298932893351</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.8263576675565787</Real>
+      <Real Name="Y">3.4827681101939802</Real>
+      <Real Name="Z">-3.0167475024415467</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-383.95063687221506</Real>
+      <Real Name="Y">868.9227266282461</Real>
+      <Real Name="Z">238.66045597483546</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">283.43681465105055</Real>
+      <Real Name="Y">-487.68568051006434</Real>
+      <Real Name="Z">-676.91226707107944</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-322.44047795440343</Real>
+      <Real Name="Y">-447.69237971630764</Real>
+      <Real Name="Z">303.06243358464866</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">371.00365357834602</Real>
+      <Real Name="Y">23.712435068959898</Real>
+      <Real Name="Z">-150.32694772084642</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.17122040221471</Real>
+      <Real Name="Y">62.020282700484216</Real>
+      <Real Name="Z">39.462595552215447</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.651444546746177</Real>
+      <Real Name="Y">-34.116891640730081</Real>
+      <Real Name="Z">73.410202394066829</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-516.71970470194731</Real>
+      <Real Name="Y">-1285.8385028052051</Real>
+      <Real Name="Z">-331.91875038992481</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.48397678163582</Real>
+      <Real Name="Y">621.78505972069092</Real>
+      <Real Name="Z">-279.15721919261517</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.69271235304421</Real>
+      <Real Name="Y">655.15247152795018</Real>
+      <Real Name="Z">633.0993033390904</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1197.2887561027223</Real>
+      <Real Name="Y">443.27327543455351</Real>
+      <Real Name="Z">-794.67230969246327</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">821.09689595579675</Real>
+      <Real Name="Y">-311.37302028558099</Real>
+      <Real Name="Z">573.11342314254478</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.85905918015868</Real>
+      <Real Name="Y">-31.926825742624036</Real>
+      <Real Name="Z">40.545533972170205</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">221.95556051004601</Real>
+      <Real Name="Y">425.49471656361061</Real>
+      <Real Name="Z">167.55558796872697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-31.314578763474124</Real>
+      <Real Name="Y">-132.10540433847711</Real>
+      <Real Name="Z">-73.267317303153121</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.17690031582046</Real>
+      <Real Name="Y">-122.46980003035156</Real>
+      <Real Name="Z">-18.451708737931995</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1047.0210329734914</Real>
+      <Real Name="Y">-1077.947804708268</Real>
+      <Real Name="Z">-856.03338926619961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.24418264368239</Real>
+      <Real Name="Y">211.21737064049597</Real>
+      <Real Name="Z">194.52266876472817</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">576.43673794516087</Real>
+      <Real Name="Y">693.13059751907417</Real>
+      <Real Name="Z">680.25612389186017</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.80881891454794</Real>
+      <Real Name="Y">384.37109845012094</Real>
+      <Real Name="Z">-1020.7807082772521</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.925297405133477</Real>
+      <Real Name="Y">-314.84706559629922</Real>
+      <Real Name="Z">752.20608286490881</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.984669155814814</Real>
+      <Real Name="Y">34.677739903192041</Real>
+      <Real Name="Z">253.66542206161358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.83718812299639</Real>
+      <Real Name="Y">-515.93511901400825</Real>
+      <Real Name="Z">-926.9945428543956</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.02017603520483</Real>
+      <Real Name="Y">81.224366103127167</Real>
+      <Real Name="Z">144.74331924185299</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.715169974529339</Real>
+      <Real Name="Y">258.84781108548265</Real>
+      <Real Name="Z">802.93385163642961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-594.66712165918773</Real>
+      <Real Name="Y">-66.325089243309151</Real>
+      <Real Name="Z">-122.32964157760213</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.96186505836829</Real>
+      <Real Name="Y">-123.46248581963616</Real>
+      <Real Name="Z">53.935692302088057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">255.68688049097204</Real>
+      <Real Name="Y">13.464085159173692</Real>
+      <Real Name="Z">84.235167762144897</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">611.06539484503014</Real>
+      <Real Name="Y">839.97798047474203</Real>
+      <Real Name="Z">-265.94087384298064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.76789585659597</Real>
+      <Real Name="Y">-206.92859092705817</Real>
+      <Real Name="Z">15.90339036830386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-474.36485611191563</Real>
+      <Real Name="Y">-878.24898538908951</Real>
+      <Real Name="Z">-40.946493997266842</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1588.7326707775553</Real>
+      <Real Name="Y">-232.76990946444641</Real>
+      <Real Name="Z">796.08421825554478</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-819.45420161255322</Real>
+      <Real Name="Y">397.16905292514792</Real>
+      <Real Name="Z">-589.52116040556916</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-208.16851530782614</Real>
+      <Real Name="Y">-145.08643483545109</Real>
+      <Real Name="Z">-321.92283806039802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.82936628635363</Real>
+      <Real Name="Y">54.6264150754714</Real>
+      <Real Name="Z">125.67863038979567</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">164.04200751770816</Real>
+      <Real Name="Y">-162.15258178673972</Real>
+      <Real Name="Z">29.635387842153442</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.01340719829753</Real>
+      <Real Name="Y">-6.9355041126986468</Real>
+      <Real Name="Z">24.988848785100132</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">850.55243387580356</Real>
+      <Real Name="Y">-484.9055590309473</Real>
+      <Real Name="Z">-96.15423590268999</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-585.08160346750878</Real>
+      <Real Name="Y">327.77968916809743</Real>
+      <Real Name="Z">75.856522971974016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.38588004416397</Real>
+      <Real Name="Y">437.13622725410954</Real>
+      <Real Name="Z">-100.3536529624609</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.56843228703744</Real>
+      <Real Name="Y">-1829.8037562211175</Real>
+      <Real Name="Z">26.107076821400895</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">131.28175023698071</Real>
+      <Real Name="Y">246.10940459955864</Real>
+      <Real Name="Z">-29.212146237088106</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.6540077622532</Real>
+      <Real Name="Y">1232.6971279010374</Real>
+      <Real Name="Z">-54.417459108715647</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">450.02457168953839</Real>
+      <Real Name="Y">1041.5670317109762</Real>
+      <Real Name="Z">887.98954562720155</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-318.98758969461386</Real>
+      <Real Name="Y">-643.92833704371048</Real>
+      <Real Name="Z">-608.16691973538593</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.351767411625481</Real>
+      <Real Name="Y">-240.23185214921529</Real>
+      <Real Name="Z">-238.9960044870991</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">249.05371041030091</Real>
+      <Real Name="Y">-336.92560539794249</Real>
+      <Real Name="Z">-438.14979794650003</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-278.16594967575912</Real>
+      <Real Name="Y">346.29826142899947</Real>
+      <Real Name="Z">251.84859746085257</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-170.48285542157944</Real>
+      <Real Name="Y">134.62087571176835</Real>
+      <Real Name="Z">498.15600359470392</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">705.51954261240712</Real>
+      <Real Name="Y">-340.79341679891309</Real>
+      <Real Name="Z">-201.04226890621766</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.73381247856207</Real>
+      <Real Name="Y">455.21741872588905</Real>
+      <Real Name="Z">-16.030727084928291</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-721.95588068283894</Real>
+      <Real Name="Y">-145.64952783270559</Real>
+      <Real Name="Z">164.3680031091379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1105.4630845844667</Real>
+      <Real Name="Y">1483.2828061952187</Real>
+      <Real Name="Z">-676.70030248431476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.72410740546641</Real>
+      <Real Name="Y">-204.35449004260201</Real>
+      <Real Name="Z">94.394440893182392</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">633.7039957563029</Real>
+      <Real Name="Y">-954.1786043006482</Real>
+      <Real Name="Z">445.83100586222429</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">745.11085322719532</Real>
+      <Real Name="Y">16.527375356092875</Real>
+      <Real Name="Z">139.09829255271086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.33673438025286</Real>
+      <Real Name="Y">98.2357293773115</Real>
+      <Real Name="Z">-32.632121522339716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-917.26198475479077</Real>
+      <Real Name="Y">126.39815903522556</Real>
+      <Real Name="Z">-123.5306854139439</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-772.63204074639737</Real>
+      <Real Name="Y">-1270.5379767076522</Real>
+      <Real Name="Z">100.94620312459</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">692.72223182354651</Real>
+      <Real Name="Y">966.6550608851071</Real>
+      <Real Name="Z">65.164088371110822</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">48.212588523379175</Real>
+      <Real Name="Y">265.455821232549</Real>
+      <Real Name="Z">-73.22909970684627</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.72663766487725</Real>
+      <Real Name="Y">-684.98518331108119</Real>
+      <Real Name="Z">816.4955318838164</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">138.89629850593212</Real>
+      <Real Name="Y">498.82070239736396</Real>
+      <Real Name="Z">-585.24448226716254</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.288430867450145</Real>
+      <Real Name="Y">73.167203206883741</Real>
+      <Real Name="Z">-119.95972544639697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.338615353660401</Real>
+      <Real Name="Y">-109.94958020891366</Real>
+      <Real Name="Z">269.66393545104211</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.570104080770108</Real>
+      <Real Name="Y">246.46434897472355</Real>
+      <Real Name="Z">-44.672459556773646</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-294.81041910340667</Real>
+      <Real Name="Y">350.87235962512318</Real>
+      <Real Name="Z">70.188787386065954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">86.489577539044276</Real>
+      <Real Name="Y">493.2140575456051</Real>
+      <Real Name="Z">-79.517990189441534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.404751136179407</Real>
+      <Real Name="Y">-183.9239319405759</Real>
+      <Real Name="Z">70.391354279404737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">28.467653242584852</Real>
+      <Real Name="Y">-197.28305643184768</Real>
+      <Real Name="Z">32.172952939449345</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">84.057643553682936</Real>
+      <Real Name="Y">775.39303673834343</Real>
+      <Real Name="Z">72.336462008713198</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.154602794079381</Real>
+      <Real Name="Y">-171.12553946065486</Real>
+      <Real Name="Z">-64.66060360219754</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-71.929026004440573</Real>
+      <Real Name="Y">-730.57048386777126</Real>
+      <Real Name="Z">-10.302192447392038</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.22133453273274</Real>
+      <Real Name="Y">-726.92390619292257</Real>
+      <Real Name="Z">-203.60047435580407</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.04975256848707</Real>
+      <Real Name="Y">628.0849982866805</Real>
+      <Real Name="Z">109.01789927094087</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">72.976184853592486</Real>
+      <Real Name="Y">179.24290378600085</Real>
+      <Real Name="Z">40.431707063631421</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.25325424099179</Real>
+      <Real Name="Y">919.9006755018911</Real>
+      <Real Name="Z">781.59682077967443</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.556347831945686</Real>
+      <Real Name="Y">-161.97747087575704</Real>
+      <Real Name="Z">-198.03051659455139</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.21726153661876</Real>
+      <Real Name="Y">-711.06814233610021</Real>
+      <Real Name="Z">-526.51512503399397</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.40728445158015</Real>
+      <Real Name="Y">322.18329779905417</Real>
+      <Real Name="Z">-416.35754342514349</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.6896753296457</Real>
+      <Real Name="Y">-264.05310871220598</Real>
+      <Real Name="Z">437.78986360426376</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">143.48994735932743</Real>
+      <Real Name="Y">-127.5290924964191</Real>
+      <Real Name="Z">87.993111424919533</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-877.77921424076146</Real>
+      <Real Name="Y">-122.87070523117205</Real>
+      <Real Name="Z">-62.018010522300273</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.67406295053365</Real>
+      <Real Name="Y">-48.830761793045404</Real>
+      <Real Name="Z">7.5397057128602754</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">310.36709663162941</Real>
+      <Real Name="Y">58.608269376894931</Real>
+      <Real Name="Z">7.0200211084501376</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1509.4549162390233</Real>
+      <Real Name="Y">-281.95542335968406</Real>
+      <Real Name="Z">-666.92286987146554</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">289.51425335202327</Real>
+      <Real Name="Y">-38.580689409552292</Real>
+      <Real Name="Z">213.93637934259488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1063.9161730088892</Real>
+      <Real Name="Y">348.3704868972597</Real>
+      <Real Name="Z">340.38184295032534</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-388.64130560083879</Real>
+      <Real Name="Y">1486.1428111499931</Real>
+      <Real Name="Z">329.82321068317293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.94859687366937884</Real>
+      <Real Name="Y">-750.84151024100674</Real>
+      <Real Name="Z">-583.82788306772659</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">360.83171132378556</Real>
+      <Real Name="Y">-479.94584183716586</Real>
+      <Real Name="Z">486.13938008841836</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">203.07290368200421</Real>
+      <Real Name="Y">434.19350139214839</Real>
+      <Real Name="Z">127.79634174125577</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.191822901139972</Real>
+      <Real Name="Y">-121.64522034883683</Real>
+      <Real Name="Z">-82.763529192926569</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-225.25830130996664</Real>
+      <Real Name="Y">-493.06986194482653</Real>
+      <Real Name="Z">-109.82768825888647</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1168.5225205110919</Real>
+      <Real Name="Y">-2062.2699891699485</Real>
+      <Real Name="Z">655.58363348407443</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-135.16180599993987</Real>
+      <Real Name="Y">1272.9760662070801</Real>
+      <Real Name="Z">326.5162856095186</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1028.6082650836172</Real>
+      <Real Name="Y">412.54622885499202</Real>
+      <Real Name="Z">-793.30548842254802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">795.24671349164851</Real>
+      <Real Name="Y">-1321.1491414253871</Real>
+      <Real Name="Z">-304.05085765894177</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-253.04448316915654</Real>
+      <Real Name="Y">692.1732648035379</Real>
+      <Real Name="Z">29.826848076784689</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-505.58259903104988</Real>
+      <Real Name="Y">713.30553929012206</Real>
+      <Real Name="Z">327.35172109746907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.656418674790643</Real>
+      <Real Name="Y">794.48904059542076</Real>
+      <Real Name="Z">1309.0445011729187</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-100.49084349618526</Real>
+      <Real Name="Y">-707.99829332132731</Real>
+      <Real Name="Z">-999.84302052634655</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.230583529035982</Real>
+      <Real Name="Y">31.062533160861964</Real>
+      <Real Name="Z">-230.99983448174834</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.020000 Step 20 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-1208.2120151116783</Real>
+      <Real Name="Y">350.08059838387442</Real>
+      <Real Name="Z">1264.7485911579283</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-82.805786406568458</Real>
+      <Real Name="Y">-98.632195837305204</Real>
+      <Real Name="Z">-65.463446198663974</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">429.25712385401403</Real>
+      <Real Name="Y">3.1736887800499067</Real>
+      <Real Name="Z">-253.84844117721596</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">365.94682414305987</Real>
+      <Real Name="Y">-486.19016733796252</Real>
+      <Real Name="Z">-606.85284085232308</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1316.7989012513806</Real>
+      <Real Name="Y">321.26044396335726</Real>
+      <Real Name="Z">-557.80440178807396</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-985.6216592114298</Real>
+      <Real Name="Y">-321.67666535862668</Real>
+      <Real Name="Z">-35.022349510566301</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-492.46894624684791</Real>
+      <Real Name="Y">452.8924675056337</Real>
+      <Real Name="Z">160.33022135799234</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">413.69895724559126</Real>
+      <Real Name="Y">-44.679055209735765</Real>
+      <Real Name="Z">-60.351066841301375</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.144963655344739</Real>
+      <Real Name="Y">-354.21425422004052</Real>
+      <Real Name="Z">9.4888796155869599</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1.6894001483201464</Real>
+      <Real Name="Y">0.11885443474494206</Real>
+      <Real Name="Z">20.840530764672998</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">9.7952750981169032</Real>
+      <Real Name="Y">8.9382722810385999</Real>
+      <Real Name="Z">-4.8680600350168461</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.3642995209808078</Real>
+      <Real Name="Y">2.4372914169938795</Real>
+      <Real Name="Z">-11.081497748874693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-23.59894707323582</Real>
+      <Real Name="Y">4.527616398967993</Real>
+      <Real Name="Z">-25.812664597105666</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.4805772614083423</Real>
+      <Real Name="Y">-1.2206184335383341</Real>
+      <Real Name="Z">12.729905632652805</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.952614307453629</Real>
+      <Real Name="Y">-12.369336877770486</Real>
+      <Real Name="Z">11.032337996021315</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">287.29977082195961</Real>
+      <Real Name="Y">-117.49078535291309</Real>
+      <Real Name="Z">-749.85226398202713</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-294.10016057389618</Real>
+      <Real Name="Y">199.13259701802167</Real>
+      <Real Name="Z">403.28264959896705</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.741752348192897</Real>
+      <Real Name="Y">9.1362706391267849</Real>
+      <Real Name="Z">176.99348779169873</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">404.4570092195429</Real>
+      <Real Name="Y">599.90840774862863</Real>
+      <Real Name="Z">-231.83204747163001</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-90.231767440338174</Real>
+      <Real Name="Y">-247.57830892939518</Real>
+      <Real Name="Z">92.171831939556228</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-360.26030262270046</Real>
+      <Real Name="Y">-563.83114538879181</Real>
+      <Real Name="Z">313.7717061346255</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">438.30472964166586</Real>
+      <Real Name="Y">-423.44093275338344</Real>
+      <Real Name="Z">-483.53385002932771</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.371124657375233</Real>
+      <Real Name="Y">146.51785307969649</Real>
+      <Real Name="Z">125.7004720091996</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-455.03510689124653</Real>
+      <Real Name="Y">418.77529829666184</Real>
+      <Real Name="Z">458.90043375739634</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">120.83262104485398</Real>
+      <Real Name="Y">230.78216824331804</Real>
+      <Real Name="Z">270.77364930263343</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.231352999400396</Real>
+      <Real Name="Y">-162.06442736154276</Real>
+      <Real Name="Z">-130.53162485480246</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-184.89513077032706</Real>
+      <Real Name="Y">-240.74126218862224</Real>
+      <Real Name="Z">-139.61798861854578</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-426.74851650972823</Real>
+      <Real Name="Y">409.21698503531536</Real>
+      <Real Name="Z">887.70490469182164</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">402.00973909730453</Real>
+      <Real Name="Y">-248.76131103518156</Real>
+      <Real Name="Z">-832.53797845145743</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">151.78414810357367</Real>
+      <Real Name="Y">-216.01967672785142</Real>
+      <Real Name="Z">-117.15988151479635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1209.824460998851</Real>
+      <Real Name="Y">306.29142857134582</Real>
+      <Real Name="Z">860.80440301057138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">57.206471775794952</Real>
+      <Real Name="Y">-544.90159508144632</Real>
+      <Real Name="Z">-202.44162181688333</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-968.50416742624714</Real>
+      <Real Name="Y">520.10645433348873</Real>
+      <Real Name="Z">-555.30230422736258</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">261.68351965641978</Real>
+      <Real Name="Y">995.60331019312287</Real>
+      <Real Name="Z">171.84941630873539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-81.887945832688516</Real>
+      <Real Name="Y">-371.94900354037941</Real>
+      <Real Name="Z">-49.317708191215047</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.44248296252059</Real>
+      <Real Name="Y">-176.46781528225424</Real>
+      <Real Name="Z">-53.326469129207993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-82.799051649680393</Real>
+      <Real Name="Y">-4.9927882328297386</Real>
+      <Real Name="Z">-1005.4706313117363</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-147.5075900241138</Real>
+      <Real Name="Y">285.45265688647868</Real>
+      <Real Name="Z">327.15654934429318</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">282.21881303314996</Real>
+      <Real Name="Y">139.33002073391947</Real>
+      <Real Name="Z">305.08629619527858</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">489.80569616702246</Real>
+      <Real Name="Y">215.45385517693455</Real>
+      <Real Name="Z">-404.01751927862529</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-477.51483469848188</Real>
+      <Real Name="Y">69.607077249964561</Real>
+      <Real Name="Z">655.561485156814</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-183.66842807560542</Real>
+      <Real Name="Y">-233.7863636720098</Real>
+      <Real Name="Z">53.409759829201633</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-960.83171907726205</Real>
+      <Real Name="Y">169.92411954325439</Real>
+      <Real Name="Z">263.18754200611943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">571.05251407095773</Real>
+      <Real Name="Y">-309.32937532856499</Real>
+      <Real Name="Z">-160.14643162753373</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">173.82923554537615</Real>
+      <Real Name="Y">8.3166844511718381</Real>
+      <Real Name="Z">-176.20439147331697</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-422.10028276528811</Real>
+      <Real Name="Y">81.34321888232148</Real>
+      <Real Name="Z">266.9963130368817</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.61246966621837</Real>
+      <Real Name="Y">-2.6154665733906057</Real>
+      <Real Name="Z">-51.076348708440072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">155.42325345330136</Real>
+      <Real Name="Y">-194.13373219162116</Real>
+      <Real Name="Z">-95.86934914494833</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-167.92492901936563</Real>
+      <Real Name="Y">-1452.4024075204954</Real>
+      <Real Name="Z">-659.15580245936803</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">46.679955351537487</Real>
+      <Real Name="Y">1352.8863144498457</Real>
+      <Real Name="Z">453.99877622891006</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">349.79688575912348</Real>
+      <Real Name="Y">363.88885702921101</Real>
+      <Real Name="Z">107.22447504206023</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">471.90086763913359</Real>
+      <Real Name="Y">783.84376428945222</Real>
+      <Real Name="Z">-913.96603079179636</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">148.90976700128479</Real>
+      <Real Name="Y">-466.59858725209915</Real>
+      <Real Name="Z">39.860342303321801</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-456.07217732969269</Real>
+      <Real Name="Y">-189.40191236713076</Real>
+      <Real Name="Z">337.74222805011999</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">66.807306017673426</Real>
+      <Real Name="Y">124.63770456545285</Real>
+      <Real Name="Z">31.223918096502572</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-15.74831537643945</Real>
+      <Real Name="Y">-50.86149239622484</Real>
+      <Real Name="Z">-12.348861821869853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.751085891823664</Real>
+      <Real Name="Y">-41.469314579609815</Real>
+      <Real Name="Z">-10.639124469471161</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-403.27787878297704</Real>
+      <Real Name="Y">914.93793031120447</Real>
+      <Real Name="Z">247.73717138785031</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">454.19370779962111</Real>
+      <Real Name="Y">-696.41176696505238</Real>
+      <Real Name="Z">-410.30442808836744</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.827390652967338</Real>
+      <Real Name="Y">-157.50911367891004</Real>
+      <Real Name="Z">-29.840595531849196</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">649.93630087336794</Real>
+      <Real Name="Y">-692.67797568793185</Real>
+      <Real Name="Z">-1251.5982717328677</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-386.97037116851982</Real>
+      <Real Name="Y">289.97075062366645</Real>
+      <Real Name="Z">970.68570313448242</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-494.39903133424883</Real>
+      <Real Name="Y">216.76217664412502</Real>
+      <Real Name="Z">47.315056759556036</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">44.656231458114689</Real>
+      <Real Name="Y">-257.71402191547725</Real>
+      <Real Name="Z">20.401038232548771</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-27.261538070723631</Real>
+      <Real Name="Y">113.75186774725105</Real>
+      <Real Name="Z">36.637917538996724</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-38.575213261003697</Real>
+      <Real Name="Y">65.524886901838798</Real>
+      <Real Name="Z">130.26977845307181</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">333.58822223490171</Real>
+      <Real Name="Y">-306.14318566966182</Real>
+      <Real Name="Z">-95.204019513424868</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-309.26545203166154</Real>
+      <Real Name="Y">375.99346008045933</Real>
+      <Real Name="Z">155.54463745921339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-88.02593021245869</Real>
+      <Real Name="Y">46.060932524265723</Real>
+      <Real Name="Z">52.263822951740536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">783.16308053251169</Real>
+      <Real Name="Y">-1295.7769989460717</Real>
+      <Real Name="Z">-570.13637078825968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-440.71781323733757</Real>
+      <Real Name="Y">307.65237567808401</Real>
+      <Real Name="Z">695.8209368232217</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.63384058774619</Real>
+      <Real Name="Y">934.63064511428934</Real>
+      <Real Name="Z">160.18995638287774</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.2687048545604185</Real>
+      <Real Name="Y">-11.657906359250358</Real>
+      <Real Name="Z">23.685405727553928</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.2563001075979869</Real>
+      <Real Name="Y">6.1809644410115672</Real>
+      <Real Name="Z">-8.7198182510731641</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.1658984659725533</Real>
+      <Real Name="Y">6.253492819717982</Real>
+      <Real Name="Z">-13.809991399763476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.27269897456645</Real>
+      <Real Name="Y">808.70301679545173</Real>
+      <Real Name="Z">98.27618379688758</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">476.78506285246436</Real>
+      <Real Name="Y">-383.80313071860007</Real>
+      <Real Name="Z">-511.46838715242933</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-209.36886177839173</Real>
+      <Real Name="Y">-359.84179407937313</Real>
+      <Real Name="Z">318.79425156786408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">342.00329430827526</Real>
+      <Real Name="Y">39.713446978474479</Real>
+      <Real Name="Z">-314.74603943758973</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-307.29032101747731</Real>
+      <Real Name="Y">48.994326712106002</Real>
+      <Real Name="Z">81.400542117214258</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-80.814850864849106</Real>
+      <Real Name="Y">-16.17318511894144</Real>
+      <Real Name="Z">102.47541336091663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.14317385384817</Real>
+      <Real Name="Y">-1127.4247146137707</Real>
+      <Real Name="Z">-530.50786779329883</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">154.13940078130844</Real>
+      <Real Name="Y">314.80931750572029</Real>
+      <Real Name="Z">-145.66915936685851</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">408.79424096261482</Real>
+      <Real Name="Y">686.12912176192435</Real>
+      <Real Name="Z">680.62636968600179</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-862.51571577941786</Real>
+      <Real Name="Y">329.71307991215167</Real>
+      <Real Name="Z">-510.35175227668759</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">682.5128531089988</Real>
+      <Real Name="Y">-272.44017585803948</Real>
+      <Real Name="Z">485.48128399979009</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.24989617125675</Real>
+      <Real Name="Y">-1.9593348192203663</Real>
+      <Real Name="Z">33.388827330120655</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">255.534498118447</Real>
+      <Real Name="Y">425.98406354705685</Real>
+      <Real Name="Z">80.797435966738277</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.890780644932448</Real>
+      <Real Name="Y">-152.99357911615152</Real>
+      <Real Name="Z">-63.588428861757478</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-124.2151429526844</Real>
+      <Real Name="Y">-150.69736826001397</Real>
+      <Real Name="Z">10.646896061250798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-820.00338462015952</Real>
+      <Real Name="Y">-795.90854276800769</Real>
+      <Real Name="Z">-522.17460770042021</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.11021100237792</Real>
+      <Real Name="Y">199.44216816649219</Real>
+      <Real Name="Z">153.64002017568129</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">705.29320428141693</Real>
+      <Real Name="Y">522.28073957745983</Real>
+      <Real Name="Z">398.3821998584761</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.604865859310955</Real>
+      <Real Name="Y">-76.503767142983193</Real>
+      <Real Name="Z">-782.2613275678616</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.705901576229142</Real>
+      <Real Name="Y">-194.77187688986473</Real>
+      <Real Name="Z">724.67236679602991</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.850507278638247</Real>
+      <Real Name="Y">59.037860239358878</Real>
+      <Real Name="Z">153.26379803296999</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-536.53645427355718</Real>
+      <Real Name="Y">-519.44663436229825</Real>
+      <Real Name="Z">-917.88241338575631</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">126.63103825170927</Real>
+      <Real Name="Y">77.153734087341675</Real>
+      <Real Name="Z">157.21233342634741</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">243.32134311407975</Real>
+      <Real Name="Y">494.61383221383335</Real>
+      <Real Name="Z">741.07734834731502</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-528.91170871232782</Real>
+      <Real Name="Y">-344.25292535762139</Real>
+      <Real Name="Z">-0.31774538501818483</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">564.35351145670234</Real>
+      <Real Name="Y">101.12238077761465</Real>
+      <Real Name="Z">-104.19422346291236</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">231.23990642796565</Real>
+      <Real Name="Y">84.869789389274786</Real>
+      <Real Name="Z">-22.018695905217065</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">173.86362207631385</Real>
+      <Real Name="Y">781.88057709801603</Real>
+      <Real Name="Z">80.16635549418055</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-14.628286358356142</Real>
+      <Real Name="Y">-167.66328623533877</Real>
+      <Real Name="Z">-24.716255171095938</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-201.52045091952459</Real>
+      <Real Name="Y">-821.70197424696403</Real>
+      <Real Name="Z">-143.59977700403863</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1749.4892567088311</Real>
+      <Real Name="Y">-225.69355598657322</Real>
+      <Real Name="Z">709.37104666132393</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1033.9130886330688</Real>
+      <Real Name="Y">297.83012148040257</Real>
+      <Real Name="Z">-568.67773473576403</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-361.22751513628407</Real>
+      <Real Name="Y">-188.25771317150441</Real>
+      <Real Name="Z">-294.01072704176983</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-303.03020790415252</Real>
+      <Real Name="Y">71.662442339666867</Real>
+      <Real Name="Z">-98.728941781710674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.63416996004329</Real>
+      <Real Name="Y">-135.55025710960291</Real>
+      <Real Name="Z">52.536924069020692</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.99609801005502</Real>
+      <Real Name="Y">-5.6031608821922561</Real>
+      <Real Name="Z">37.964953011549738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">942.23752492777908</Real>
+      <Real Name="Y">-252.62193084535872</Real>
+      <Real Name="Z">3.9868194695199293</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-737.94092302790455</Real>
+      <Real Name="Y">287.07129299008329</Real>
+      <Real Name="Z">107.13008576416476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-443.94635447864073</Real>
+      <Real Name="Y">301.87385388029639</Real>
+      <Real Name="Z">-50.150374644195807</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-196.21667964293539</Real>
+      <Real Name="Y">-1139.9298672392133</Real>
+      <Real Name="Z">-10.750357299812919</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">87.725206410598858</Real>
+      <Real Name="Y">175.79722736116005</Real>
+      <Real Name="Z">-30.380804682092624</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.4978097382465</Real>
+      <Real Name="Y">1128.3153673546292</Real>
+      <Real Name="Z">34.530498039307787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">224.52193291021624</Real>
+      <Real Name="Y">1120.2734915900323</Real>
+      <Real Name="Z">800.89109164819502</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-181.57531326790397</Real>
+      <Real Name="Y">-689.73200512934989</Real>
+      <Real Name="Z">-650.68385455213638</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.728954161084232</Real>
+      <Real Name="Y">-293.75231703025281</Real>
+      <Real Name="Z">-148.98403090508822</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">194.99325234105336</Real>
+      <Real Name="Y">-280.74304751152488</Real>
+      <Real Name="Z">-298.62921750898897</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.53176469063615</Real>
+      <Real Name="Y">366.58703013807343</Real>
+      <Real Name="Z">5.7199063307417006</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">103.45201602766386</Real>
+      <Real Name="Y">28.751220438891195</Real>
+      <Real Name="Z">300.90614073816909</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">707.19598844302811</Real>
+      <Real Name="Y">-438.45926315684432</Real>
+      <Real Name="Z">-181.13022696984081</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-329.29196943581712</Real>
+      <Real Name="Y">514.66531763956868</Real>
+      <Real Name="Z">48.072063602037389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-597.10088036150978</Real>
+      <Real Name="Y">-79.930184208446292</Real>
+      <Real Name="Z">202.93334223365855</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-981.19446288537802</Real>
+      <Real Name="Y">1154.0741385567626</Real>
+      <Real Name="Z">-653.29451631364384</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.36846901118591</Real>
+      <Real Name="Y">-163.83085837913507</Real>
+      <Real Name="Z">97.1237723955403</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">568.13638112686726</Real>
+      <Real Name="Y">-821.48875879460536</Real>
+      <Real Name="Z">520.12658728334964</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">933.62933780698768</Real>
+      <Real Name="Y">-130.40051023538146</Real>
+      <Real Name="Z">51.67993690912563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.92370532623156</Real>
+      <Real Name="Y">92.602699302825314</Real>
+      <Real Name="Z">-29.648860660085184</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-894.03594912693598</Real>
+      <Real Name="Y">-54.821442719075868</Real>
+      <Real Name="Z">-97.082432933486032</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-557.48288165164854</Real>
+      <Real Name="Y">-708.95294902480589</Real>
+      <Real Name="Z">121.38949155537605</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">588.70125758203096</Real>
+      <Real Name="Y">705.04904069559507</Real>
+      <Real Name="Z">27.443994316545435</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">24.827273096140395</Real>
+      <Real Name="Y">129.60629420449888</Real>
+      <Real Name="Z">-80.130552545374172</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-273.67691274523975</Real>
+      <Real Name="Y">-1095.1320351681959</Real>
+      <Real Name="Z">1207.0875197139374</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">313.43219681518718</Real>
+      <Real Name="Y">618.27454660153694</Real>
+      <Real Name="Z">-558.16716482875006</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-75.519736239051184</Real>
+      <Real Name="Y">138.39905353171122</Real>
+      <Real Name="Z">-195.95829827065268</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">393.53030606613913</Real>
+      <Real Name="Y">-475.37782882525136</Real>
+      <Real Name="Z">-7.2729065695554453</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.144673378238323</Real>
+      <Real Name="Y">262.36807128986965</Real>
+      <Real Name="Z">-45.855841865673234</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-422.1202945157824</Real>
+      <Real Name="Y">339.57432839448654</Real>
+      <Real Name="Z">-17.79999336359527</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-110.01153160480962</Real>
+      <Real Name="Y">450.59654404097967</Real>
+      <Real Name="Z">-8.9665130986154438</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-40.84276141769238</Real>
+      <Real Name="Y">-216.88996612047958</Real>
+      <Real Name="Z">42.334970406633126</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">38.750466901191459</Real>
+      <Real Name="Y">-139.24828366878319</Real>
+      <Real Name="Z">6.956553445612812</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.817988625971662</Real>
+      <Real Name="Y">777.29065022163581</Real>
+      <Real Name="Z">71.097127309635908</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-23.286071824538595</Real>
+      <Real Name="Y">-182.84132482075245</Real>
+      <Real Name="Z">-62.957039514152498</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">155.05786363213156</Real>
+      <Real Name="Y">-676.64454965073082</Real>
+      <Real Name="Z">-45.73883814946182</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-120.0240671027647</Real>
+      <Real Name="Y">-679.19369131617646</Real>
+      <Real Name="Z">-270.00478003221792</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">209.27664008798752</Real>
+      <Real Name="Y">597.51403780323687</Real>
+      <Real Name="Z">154.68996636454656</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">94.748155033515545</Real>
+      <Real Name="Y">152.25442394870115</Real>
+      <Real Name="Z">37.388964935587396</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-189.52694166358145</Real>
+      <Real Name="Y">952.87767353882714</Real>
+      <Real Name="Z">832.34613194477856</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">63.959908820027138</Real>
+      <Real Name="Y">-176.96949130325407</Real>
+      <Real Name="Z">-195.89593952557738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-109.60273581388608</Real>
+      <Real Name="Y">-698.14377691890127</Real>
+      <Real Name="Z">-513.3427512368653</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-220.4106715955852</Real>
+      <Real Name="Y">182.13410921179477</Real>
+      <Real Name="Z">-347.95145704865183</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">108.59382066691076</Real>
+      <Real Name="Y">-103.55093072627932</Real>
+      <Real Name="Z">346.6970528519185</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">125.60736221853865</Real>
+      <Real Name="Y">-65.263638766196436</Real>
+      <Real Name="Z">110.97566065963566</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-986.52193325058772</Real>
+      <Real Name="Y">-2.9343312856992867</Real>
+      <Real Name="Z">-54.62767436064162</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">718.87176893148251</Real>
+      <Real Name="Y">-126.03636755526279</Real>
+      <Real Name="Z">-19.444718189506609</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">330.3962440842991</Real>
+      <Real Name="Y">8.5425679803116807</Real>
+      <Real Name="Z">4.4157222774026081</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1556.0017053659353</Real>
+      <Real Name="Y">-888.88965670121115</Real>
+      <Real Name="Z">-473.74085177452173</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">336.28889121649723</Real>
+      <Real Name="Y">64.977215552693593</Real>
+      <Real Name="Z">140.88374242576765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1182.0826351675648</Real>
+      <Real Name="Y">215.21706729805697</Real>
+      <Real Name="Z">331.94810796123386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">16.551081444908355</Real>
+      <Real Name="Y">1448.9503832586151</Real>
+      <Real Name="Z">77.940580754827437</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-59.583970468977313</Real>
+      <Real Name="Y">-814.45897322871656</Real>
+      <Real Name="Z">-429.2108242366225</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.06678445967773</Real>
+      <Real Name="Y">-625.11835075415775</Real>
+      <Real Name="Z">577.39441701638077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">245.15014348561743</Real>
+      <Real Name="Y">504.42871469083087</Real>
+      <Real Name="Z">163.46387320451629</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.189017081148464</Real>
+      <Real Name="Y">-130.34487882936082</Real>
+      <Real Name="Z">-107.2739744404804</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-259.72113558086244</Real>
+      <Real Name="Y">-513.07405356616277</Real>
+      <Real Name="Z">-78.742608632377028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">765.85465430423005</Real>
+      <Real Name="Y">-1666.6481084848756</Real>
+      <Real Name="Z">832.84277637929063</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.057596517475737</Real>
+      <Real Name="Y">1176.9735164718547</Real>
+      <Real Name="Z">113.50586307581159</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-946.22227235461446</Real>
+      <Real Name="Y">556.87657785389399</Real>
+      <Real Name="Z">-733.76244117423016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">809.46100391998095</Real>
+      <Real Name="Y">-1985.8709024388975</Real>
+      <Real Name="Z">-662.28837720129241</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-65.054496093190124</Real>
+      <Real Name="Y">844.55674220267178</Real>
+      <Real Name="Z">210.22439399741904</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-547.78153568700009</Real>
+      <Real Name="Y">686.70416243318789</Real>
+      <Real Name="Z">430.40125788111453</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">91.763155216752509</Real>
+      <Real Name="Y">726.96203524386499</Real>
+      <Real Name="Z">1181.8695152551281</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.40441685746197</Real>
+      <Real Name="Y">-591.46917700761401</Real>
+      <Real Name="Z">-1008.5474904802961</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-54.342046850808046</Real>
+      <Real Name="Y">-63.527102321442626</Real>
+      <Real Name="Z">-260.76962375840958</Real>
+    </Vector>
+  </Sequence>
+  <XvgLegend Name="Legend">
+    <String Name="XvgLegend"><![CDATA[
+title "dH/d\xl\f{} and \xD\f{}H"
+xaxis  label "Time (ps)"
+yaxis  label "dH/d\xl\f{} and \xD\f{}H (kJ/mol [\xl\f{}]\S-1\N)"
+TYPE xy
+subtitle "\xl\f{} state 1:  = "
+s0 legend "Thermodynamic state"
+s1 legend "Total Energy (kJ/mol)"
+s2 legend "\xD\f{}H \xl\f{} to T = 300 (K)"
+s3 legend "\xD\f{}H \xl\f{} to T = 311.787 (K)"
+s4 legend "\xD\f{}H \xl\f{} to T = 324.037 (K)"
+s5 legend "\xD\f{}H \xl\f{} to T = 336.768 (K)"
+s6 legend "\xD\f{}H \xl\f{} to T = 350 (K)"
+]]></String>
+  </XvgLegend>
+  <XvgData Name="Data">
+    <Sequence Name="Row0">
+      <Int Name="Length">8</Int>
+      <Real>0.0000</Real>
+      <Real>1</Real>
+      <Real>-1101.8130</Real>
+      <Real>-16.597310</Real>
+      <Real>0.0000000</Real>
+      <Real>17.249416</Real>
+      <Real>35.176560</Real>
+      <Real>53.808058</Real>
+    </Sequence>
+    <Sequence Name="Row1">
+      <Int Name="Length">8</Int>
+      <Real>0.0050</Real>
+      <Real>1</Real>
+      <Real>-1101.5730</Real>
+      <Real>-15.773367</Real>
+      <Real>0.0000000</Real>
+      <Real>16.393101</Real>
+      <Real>33.430284</Real>
+      <Real>51.136856</Real>
+    </Sequence>
+    <Sequence Name="Row2">
+      <Int Name="Length">8</Int>
+      <Real>0.0100</Real>
+      <Real>0</Real>
+      <Real>-1117.0939</Real>
+      <Real>0.0000000</Real>
+      <Real>14.990097</Real>
+      <Real>30.569153</Real>
+      <Real>46.760308</Real>
+      <Real>63.587611</Real>
+    </Sequence>
+    <Sequence Name="Row3">
+      <Int Name="Length">8</Int>
+      <Real>0.0150</Real>
+      <Real>0</Real>
+      <Real>-1116.7727</Real>
+      <Real>0.0000000</Real>
+      <Real>14.628651</Real>
+      <Real>29.832061</Real>
+      <Real>45.632810</Real>
+      <Real>62.054369</Real>
+    </Sequence>
+    <Sequence Name="Row4">
+      <Int Name="Length">8</Int>
+      <Real>0.0200</Real>
+      <Real>2</Real>
+      <Real>-1086.4449</Real>
+      <Real>-29.565815</Real>
+      <Real>-15.067721</Real>
+      <Real>0.0000000</Real>
+      <Real>15.659730</Real>
+      <Real>31.934730</Real>
+    </Sequence>
+  </XvgData>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_simtemp_s.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_simtemp_s.xml
new file mode 100644 (file)
index 0000000..d778526
--- /dev/null
@@ -0,0 +1,988 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1540.8595</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1538.6833</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1535.1133</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1530.3499</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1524.764</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1518.8235</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1513.2145</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1508.2859</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1504.241</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1501.0817</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1498.6368</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1496.6323</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1494.7946</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1492.9298</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1490.9951</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1489.115</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1487.2795</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1485.6947</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1484.662</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1484.4108</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1485.0343</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">591.06274</Real>
+      <Real Name="Y">-793.16522</Real>
+      <Real Name="Z">-86.561035</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.09843</Real>
+      <Real Name="Y">-248.34065</Real>
+      <Real Name="Z">33.733166</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-447.86597</Real>
+      <Real Name="Y">277.24841</Real>
+      <Real Name="Z">-222.44661</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-126.20997</Real>
+      <Real Name="Y">187.18262</Real>
+      <Real Name="Z">32.50943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">956.13464</Real>
+      <Real Name="Y">827.60181</Real>
+      <Real Name="Z">-10.803772</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-124.89921</Real>
+      <Real Name="Y">81.568703</Real>
+      <Real Name="Z">-241.96658</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1274.1816</Real>
+      <Real Name="Y">-168.72632</Real>
+      <Real Name="Z">906.91516</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">250.8654</Real>
+      <Real Name="Y">-118.60684</Real>
+      <Real Name="Z">-464.27521</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">284.15012</Real>
+      <Real Name="Y">-264.2948</Real>
+      <Real Name="Z">-25.474583</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.9437332</Real>
+      <Real Name="Y">-34.118774</Real>
+      <Real Name="Z">25.598434</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.4130936</Real>
+      <Real Name="Y">29.789534</Real>
+      <Real Name="Z">-15.151001</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.692646</Real>
+      <Real Name="Y">15.672777</Real>
+      <Real Name="Z">-6.2329483</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.92453</Real>
+      <Real Name="Y">18.175198</Real>
+      <Real Name="Z">-5.9909973</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.827095</Real>
+      <Real Name="Y">-7.2495995</Real>
+      <Real Name="Z">4.4740391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.5941353</Real>
+      <Real Name="Y">-22.388287</Real>
+      <Real Name="Z">3.6513939</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.84644</Real>
+      <Real Name="Y">-59.014282</Real>
+      <Real Name="Z">-670.28906</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-396.07312</Real>
+      <Real Name="Y">36.163364</Real>
+      <Real Name="Z">385.3652</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.5577545</Real>
+      <Real Name="Y">-11.444275</Real>
+      <Real Name="Z">206.57773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">350.32874</Real>
+      <Real Name="Y">397.17709</Real>
+      <Real Name="Z">-116.30058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.5585</Real>
+      <Real Name="Y">-227.96716</Real>
+      <Real Name="Z">94.896805</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-339.72333</Real>
+      <Real Name="Y">-572.75415</Real>
+      <Real Name="Z">260.37488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">651.81378</Real>
+      <Real Name="Y">-500.94174</Real>
+      <Real Name="Z">-426.18787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-115.29115</Real>
+      <Real Name="Y">125.72142</Real>
+      <Real Name="Z">76.887466</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-650.86029</Real>
+      <Real Name="Y">444.51999</Real>
+      <Real Name="Z">222.33656</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.49205</Real>
+      <Real Name="Y">279.29849</Real>
+      <Real Name="Z">51.27597</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.384453</Real>
+      <Real Name="Y">-157.0712</Real>
+      <Real Name="Z">-104.35411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.73877</Real>
+      <Real Name="Y">-208.50685</Real>
+      <Real Name="Z">-132.81796</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-589.78198</Real>
+      <Real Name="Y">237.27377</Real>
+      <Real Name="Z">851.64764</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">395.72586</Real>
+      <Real Name="Y">-34.571304</Real>
+      <Real Name="Z">-874.73145</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">174.02786</Real>
+      <Real Name="Y">-179.41884</Real>
+      <Real Name="Z">-67.554199</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1099.5664</Real>
+      <Real Name="Y">553.65118</Real>
+      <Real Name="Z">903.68042</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-161.87419</Real>
+      <Real Name="Y">-835.15405</Real>
+      <Real Name="Z">-374.62759</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-835.94647</Real>
+      <Real Name="Y">336.87842</Real>
+      <Real Name="Z">-602.75006</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">367.31354</Real>
+      <Real Name="Y">747.55859</Real>
+      <Real Name="Z">136.28569</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.529114</Real>
+      <Real Name="Y">-645.92108</Real>
+      <Real Name="Z">-47.856651</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.203033</Real>
+      <Real Name="Y">-129.11758</Real>
+      <Real Name="Z">-33.408222</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">46.295322</Real>
+      <Real Name="Y">-167.90427</Real>
+      <Real Name="Z">-913.22412</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-121.78538</Real>
+      <Real Name="Y">49.506058</Real>
+      <Real Name="Z">273.49045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">378.02087</Real>
+      <Real Name="Y">-5.7678871</Real>
+      <Real Name="Z">685.09454</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.12416</Real>
+      <Real Name="Y">49.895081</Real>
+      <Real Name="Z">-861.93378</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-268.05084</Real>
+      <Real Name="Y">160.69049</Real>
+      <Real Name="Z">483.4491</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.8040199</Real>
+      <Real Name="Y">-196.19897</Real>
+      <Real Name="Z">167.55754</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.91718</Real>
+      <Real Name="Y">60.107788</Real>
+      <Real Name="Z">367.77667</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">548.89014</Real>
+      <Real Name="Y">-52.443062</Real>
+      <Real Name="Z">-12.278031</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">145.01877</Real>
+      <Real Name="Y">-23.074142</Real>
+      <Real Name="Z">-246.45247</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.208099</Real>
+      <Real Name="Y">37.623688</Real>
+      <Real Name="Z">360.18265</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">243.95145</Real>
+      <Real Name="Y">-5.7312164</Real>
+      <Real Name="Z">-127.35837</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.632771</Real>
+      <Real Name="Y">-114.10121</Real>
+      <Real Name="Z">-113.64888</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-333.63986</Real>
+      <Real Name="Y">-2020.2126</Real>
+      <Real Name="Z">-705.45239</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.1012</Real>
+      <Real Name="Y">1302.4658</Real>
+      <Real Name="Z">328.3046</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">273.8157</Real>
+      <Real Name="Y">399.7489</Real>
+      <Real Name="Z">73.872589</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">454.22412</Real>
+      <Real Name="Y">1035.556</Real>
+      <Real Name="Z">-836.83051</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-161.20929</Real>
+      <Real Name="Y">-546.36926</Real>
+      <Real Name="Z">227.77548</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.52246</Real>
+      <Real Name="Y">-418.64044</Real>
+      <Real Name="Z">550.23285</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">50.714981</Real>
+      <Real Name="Y">92.956924</Real>
+      <Real Name="Z">0.86141014</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.6475163</Real>
+      <Real Name="Y">-40.904976</Real>
+      <Real Name="Z">-1.3789215</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.013699</Real>
+      <Real Name="Y">-30.334343</Real>
+      <Real Name="Z">10.519749</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-456.43924</Real>
+      <Real Name="Y">971.5932</Real>
+      <Real Name="Z">410.11536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">615.54852</Real>
+      <Real Name="Y">-684.91302</Real>
+      <Real Name="Z">-459.81165</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">41.963497</Real>
+      <Real Name="Y">-153.75214</Real>
+      <Real Name="Z">-22.296715</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">267.69629</Real>
+      <Real Name="Y">-462.2132</Real>
+      <Real Name="Z">-1386.6565</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-83.662354</Real>
+      <Real Name="Y">332.90689</Real>
+      <Real Name="Z">1034.3909</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.44519</Real>
+      <Real Name="Y">26.493641</Real>
+      <Real Name="Z">210.39384</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.14674</Real>
+      <Real Name="Y">-125.65987</Real>
+      <Real Name="Z">-120.06331</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.826218</Real>
+      <Real Name="Y">126.72969</Real>
+      <Real Name="Z">74.477928</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-64.104065</Real>
+      <Real Name="Y">41.735107</Real>
+      <Real Name="Z">152.18146</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">329.82257</Real>
+      <Real Name="Y">-341.07834</Real>
+      <Real Name="Z">-68.011322</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.4202</Real>
+      <Real Name="Y">415.60812</Real>
+      <Real Name="Z">94.081383</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.2318</Real>
+      <Real Name="Y">68.751266</Real>
+      <Real Name="Z">37.600391</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">812.92633</Real>
+      <Real Name="Y">-958.58527</Real>
+      <Real Name="Z">-477.29324</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-357.80804</Real>
+      <Real Name="Y">354.71997</Real>
+      <Real Name="Z">551.77094</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-385.34528</Real>
+      <Real Name="Y">996.93488</Real>
+      <Real Name="Z">106.35823</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.4966354</Real>
+      <Real Name="Y">-8.3873444</Real>
+      <Real Name="Z">1.8501663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.71152878</Real>
+      <Real Name="Y">5.5084839</Real>
+      <Real Name="Z">0.19663811</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.8261223</Real>
+      <Real Name="Y">3.4832954</Real>
+      <Real Name="Z">-3.0169563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-383.9491</Real>
+      <Real Name="Y">868.92578</Real>
+      <Real Name="Z">238.66106</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">283.43579</Real>
+      <Real Name="Y">-487.68692</Real>
+      <Real Name="Z">-676.91138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-322.44012</Real>
+      <Real Name="Y">-447.69458</Real>
+      <Real Name="Z">303.06247</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">371.004</Real>
+      <Real Name="Y">23.711849</Real>
+      <Real Name="Z">-150.32498</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.17123</Real>
+      <Real Name="Y">62.020271</Real>
+      <Real Name="Z">39.461594</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.651581</Real>
+      <Real Name="Y">-34.116806</Real>
+      <Real Name="Z">73.409966</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-516.72321</Real>
+      <Real Name="Y">-1285.8428</Real>
+      <Real Name="Z">-331.92261</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.48422</Real>
+      <Real Name="Y">621.7879</Real>
+      <Real Name="Z">-279.15588</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.69226</Real>
+      <Real Name="Y">655.15479</Real>
+      <Real Name="Z">633.10016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1197.2902</Real>
+      <Real Name="Y">443.27393</Real>
+      <Real Name="Z">-794.67255</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">821.09784</Real>
+      <Real Name="Y">-311.37335</Real>
+      <Real Name="Z">573.11304</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.85938</Real>
+      <Real Name="Y">-31.927046</Real>
+      <Real Name="Z">40.545853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">221.95505</Real>
+      <Real Name="Y">425.49542</Real>
+      <Real Name="Z">167.55403</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-31.313751</Real>
+      <Real Name="Y">-132.10576</Real>
+      <Real Name="Z">-73.267296</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.17723</Real>
+      <Real Name="Y">-122.47018</Real>
+      <Real Name="Z">-18.45085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1047.017</Real>
+      <Real Name="Y">-1077.9473</Real>
+      <Real Name="Z">-856.0321</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.24394</Real>
+      <Real Name="Y">211.21786</Real>
+      <Real Name="Z">194.52278</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">576.43445</Real>
+      <Real Name="Y">693.13055</Real>
+      <Real Name="Z">680.25586</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.81073</Real>
+      <Real Name="Y">384.37109</Real>
+      <Real Name="Z">-1020.7798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.925613</Real>
+      <Real Name="Y">-314.84653</Real>
+      <Real Name="Z">752.20526</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.983704</Real>
+      <Real Name="Y">34.67823</Real>
+      <Real Name="Z">253.66602</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.83536</Real>
+      <Real Name="Y">-515.9375</Real>
+      <Real Name="Z">-926.99768</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.01855</Real>
+      <Real Name="Y">81.225128</Real>
+      <Real Name="Z">144.74394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.712914</Real>
+      <Real Name="Y">258.8483</Real>
+      <Real Name="Z">802.93335</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-594.66699</Real>
+      <Real Name="Y">-66.321411</Real>
+      <Real Name="Z">-122.3287</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.96118</Real>
+      <Real Name="Y">-123.46442</Real>
+      <Real Name="Z">53.935078</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">255.68594</Real>
+      <Real Name="Y">13.463104</Real>
+      <Real Name="Z">84.234901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">611.06403</Real>
+      <Real Name="Y">839.98096</Real>
+      <Real Name="Z">-265.93948</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.76723</Real>
+      <Real Name="Y">-206.92859</Real>
+      <Real Name="Z">15.903809</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-474.36337</Real>
+      <Real Name="Y">-878.25006</Real>
+      <Real Name="Z">-40.947277</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1588.7339</Real>
+      <Real Name="Y">-232.77469</Real>
+      <Real Name="Z">796.08411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-819.45349</Real>
+      <Real Name="Y">397.17056</Real>
+      <Real Name="Z">-589.52014</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-208.16838</Real>
+      <Real Name="Y">-145.08554</Real>
+      <Real Name="Z">-321.92294</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.82996</Real>
+      <Real Name="Y">54.627377</Real>
+      <Real Name="Z">125.67847</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">164.04228</Real>
+      <Real Name="Y">-162.1525</Real>
+      <Real Name="Z">29.635811</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.01358</Real>
+      <Real Name="Y">-6.9365005</Real>
+      <Real Name="Z">24.989197</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">850.54883</Real>
+      <Real Name="Y">-484.90958</Real>
+      <Real Name="Z">-96.156876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-585.07996</Real>
+      <Real Name="Y">327.78162</Real>
+      <Real Name="Z">75.857475</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.38504</Real>
+      <Real Name="Y">437.1387</Real>
+      <Real Name="Z">-100.35383</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.57202</Real>
+      <Real Name="Y">-1829.7993</Real>
+      <Real Name="Z">26.108131</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">131.28232</Real>
+      <Real Name="Y">246.10884</Real>
+      <Real Name="Z">-29.212749</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.65274</Real>
+      <Real Name="Y">1232.6938</Real>
+      <Real Name="Z">-54.418034</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">450.02127</Real>
+      <Real Name="Y">1041.5649</Real>
+      <Real Name="Z">887.98877</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-318.98688</Real>
+      <Real Name="Y">-643.92682</Real>
+      <Real Name="Z">-608.16724</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.35104</Real>
+      <Real Name="Y">-240.23148</Real>
+      <Real Name="Z">-238.99628</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">249.05176</Real>
+      <Real Name="Y">-336.92123</Real>
+      <Real Name="Z">-438.15317</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-278.16458</Real>
+      <Real Name="Y">346.29578</Real>
+      <Real Name="Z">251.85017</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-170.48038</Real>
+      <Real Name="Y">134.61818</Real>
+      <Real Name="Z">498.15747</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">705.51678</Real>
+      <Real Name="Y">-340.79214</Real>
+      <Real Name="Z">-201.04681</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.73311</Real>
+      <Real Name="Y">455.21826</Real>
+      <Real Name="Z">-16.030602</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-721.95563</Real>
+      <Real Name="Y">-145.64851</Real>
+      <Real Name="Z">164.36844</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1105.4644</Real>
+      <Real Name="Y">1483.2834</Real>
+      <Real Name="Z">-676.70117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.7243</Real>
+      <Real Name="Y">-204.35464</Real>
+      <Real Name="Z">94.394508</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">633.70587</Real>
+      <Real Name="Y">-954.17651</Real>
+      <Real Name="Z">445.8291</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">745.11243</Real>
+      <Real Name="Y">16.52681</Real>
+      <Real Name="Z">139.09857</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.33755</Real>
+      <Real Name="Y">98.235008</Real>
+      <Real Name="Z">-32.63245</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-917.26093</Real>
+      <Real Name="Y">126.39825</Real>
+      <Real Name="Z">-123.53004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-772.63013</Real>
+      <Real Name="Y">-1270.5387</Real>
+      <Real Name="Z">100.9455</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">692.72235</Real>
+      <Real Name="Y">966.65375</Real>
+      <Real Name="Z">65.165123</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">48.212032</Real>
+      <Real Name="Y">265.45648</Real>
+      <Real Name="Z">-73.229019</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-119.72446</Real>
+      <Real Name="Y">-684.98834</Real>
+      <Real Name="Z">816.4967</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">138.89417</Real>
+      <Real Name="Y">498.82236</Real>
+      <Real Name="Z">-585.24475</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.289062</Real>
+      <Real Name="Y">73.16777</Real>
+      <Real Name="Z">-119.9601</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.342834</Real>
+      <Real Name="Y">-109.94833</Real>
+      <Real Name="Z">269.66519</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.57128</Real>
+      <Real Name="Y">246.46478</Real>
+      <Real Name="Z">-44.672508</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-294.81183</Real>
+      <Real Name="Y">350.87155</Real>
+      <Real Name="Z">70.188057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">86.489182</Real>
+      <Real Name="Y">493.21359</Real>
+      <Real Name="Z">-79.518631</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.404449</Real>
+      <Real Name="Y">-183.92371</Real>
+      <Real Name="Z">70.392128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">28.468298</Real>
+      <Real Name="Y">-197.2832</Real>
+      <Real Name="Z">32.172958</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">84.057938</Real>
+      <Real Name="Y">775.39368</Real>
+      <Real Name="Z">72.336319</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.154591</Real>
+      <Real Name="Y">-171.12547</Real>
+      <Real Name="Z">-64.660454</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-71.92923</Real>
+      <Real Name="Y">-730.57117</Real>
+      <Real Name="Z">-10.302049</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.221291</Real>
+      <Real Name="Y">-726.92511</Real>
+      <Real Name="Z">-203.6001</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.04897</Real>
+      <Real Name="Y">628.08557</Real>
+      <Real Name="Z">109.01802</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">72.975876</Real>
+      <Real Name="Y">179.24362</Real>
+      <Real Name="Z">40.431633</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.25566</Real>
+      <Real Name="Y">919.89496</Real>
+      <Real Name="Z">781.59467</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.557068</Real>
+      <Real Name="Y">-161.97707</Real>
+      <Real Name="Z">-198.03044</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">178.21704</Real>
+      <Real Name="Y">-711.06641</Real>
+      <Real Name="Z">-526.51447</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.40787</Real>
+      <Real Name="Y">322.18402</Real>
+      <Real Name="Z">-416.35892</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.68941</Real>
+      <Real Name="Y">-264.05405</Real>
+      <Real Name="Z">437.78967</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">143.49042</Real>
+      <Real Name="Y">-127.52937</Real>
+      <Real Name="Z">87.993553</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-877.78296</Real>
+      <Real Name="Y">-122.86922</Real>
+      <Real Name="Z">-62.020088</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.67346</Real>
+      <Real Name="Y">-48.831436</Real>
+      <Real Name="Z">7.5404282</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">310.36789</Real>
+      <Real Name="Y">58.607738</Real>
+      <Real Name="Z">7.0199661</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1509.45</Real>
+      <Real Name="Y">-281.94852</Real>
+      <Real Name="Z">-666.91913</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">289.51474</Real>
+      <Real Name="Y">-38.580463</Real>
+      <Real Name="Z">213.93591</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1063.9144</Real>
+      <Real Name="Y">348.36923</Real>
+      <Real Name="Z">340.38068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-388.63828</Real>
+      <Real Name="Y">1486.1469</Real>
+      <Real Name="Z">329.82358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.95001221</Real>
+      <Real Name="Y">-750.84192</Real>
+      <Real Name="Z">-583.82733</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">360.83084</Real>
+      <Real Name="Y">-479.94693</Real>
+      <Real Name="Z">486.13925</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">203.07156</Real>
+      <Real Name="Y">434.19211</Real>
+      <Real Name="Z">127.79526</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.191162</Real>
+      <Real Name="Y">-121.64478</Real>
+      <Real Name="Z">-82.762993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-225.25742</Real>
+      <Real Name="Y">-493.06845</Real>
+      <Real Name="Z">-109.82661</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1168.516</Real>
+      <Real Name="Y">-2062.2756</Real>
+      <Real Name="Z">655.573</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-135.16344</Real>
+      <Real Name="Y">1272.9781</Real>
+      <Real Name="Z">326.51572</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1028.6063</Real>
+      <Real Name="Y">412.54956</Real>
+      <Real Name="Z">-793.30304</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">795.23907</Real>
+      <Real Name="Y">-1321.1536</Real>
+      <Real Name="Z">-304.05066</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-253.04129</Real>
+      <Real Name="Y">692.17523</Real>
+      <Real Name="Z">29.826693</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-505.57803</Real>
+      <Real Name="Y">713.30597</Real>
+      <Real Name="Z">327.3511</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.65976</Real>
+      <Real Name="Y">794.48108</Real>
+      <Real Name="Z">1309.0448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-100.48924</Real>
+      <Real Name="Y">-707.99548</Real>
+      <Real Name="Z">-999.84241</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.229813</Real>
+      <Real Name="Y">31.06391</Real>
+      <Real Name="Z">-230.99925</Real>
+    </Vector>
+  </Sequence>
+  <XvgLegend Name="Legend">
+    <String Name="XvgLegend"><![CDATA[
+title "dH/d\xl\f{} and \xD\f{}H"
+xaxis  label "Time (ps)"
+yaxis  label "dH/d\xl\f{} and \xD\f{}H (kJ/mol [\xl\f{}]\S-1\N)"
+TYPE xy
+subtitle "\xl\f{} state 1:  = "
+s0 legend "Thermodynamic state"
+s1 legend "Total Energy (kJ/mol)"
+s2 legend "\xD\f{}H \xl\f{} to T = 300 (K)"
+s3 legend "\xD\f{}H \xl\f{} to T = 311.787 (K)"
+s4 legend "\xD\f{}H \xl\f{} to T = 324.037 (K)"
+s5 legend "\xD\f{}H \xl\f{} to T = 336.768 (K)"
+s6 legend "\xD\f{}H \xl\f{} to T = 350 (K)"
+]]></String>
+  </XvgLegend>
+  <XvgData Name="Data">
+    <Sequence Name="Row0">
+      <Int Name="Length">8</Int>
+      <Real>0.0000</Real>
+      <Real>1</Real>
+      <Real>-1101.8300</Real>
+      <Real>-16.597310</Real>
+      <Real>0.0000000</Real>
+      <Real>17.249396</Real>
+      <Real>35.176548</Real>
+      <Real>53.808043</Real>
+    </Sequence>
+    <Sequence Name="Row1">
+      <Int Name="Length">8</Int>
+      <Real>0.0050</Real>
+      <Real>1</Real>
+      <Real>-1101.5885</Real>
+      <Real>-15.773378</Real>
+      <Real>0.0000000</Real>
+      <Real>16.393093</Real>
+      <Real>33.430296</Real>
+      <Real>51.136877</Real>
+    </Sequence>
+    <Sequence Name="Row2">
+      <Int Name="Length">8</Int>
+      <Real>0.0100</Real>
+      <Real>0</Real>
+      <Real>-1117.1106</Real>
+      <Real>0.0000000</Real>
+      <Real>14.990100</Real>
+      <Real>30.569184</Real>
+      <Real>46.760359</Real>
+      <Real>63.587682</Real>
+    </Sequence>
+    <Sequence Name="Row3">
+      <Int Name="Length">8</Int>
+      <Real>0.0150</Real>
+      <Real>0</Real>
+      <Real>-1116.7891</Real>
+      <Real>0.0000000</Real>
+      <Real>14.628623</Real>
+      <Real>29.832028</Real>
+      <Real>45.632763</Real>
+      <Real>62.054306</Real>
+    </Sequence>
+    <Sequence Name="Row4">
+      <Int Name="Length">8</Int>
+      <Real>0.0200</Real>
+      <Real>2</Real>
+      <Real>-1086.4629</Real>
+      <Real>-29.565963</Real>
+      <Real>-15.067789</Real>
+      <Real>0.0000000</Real>
+      <Real>15.659854</Real>
+      <Real>31.934959</Real>
+    </Sequence>
+  </XvgData>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_transformAtoB_d.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_transformAtoB_d.xml
new file mode 100644 (file)
index 0000000..b7f9452
--- /dev/null
@@ -0,0 +1,1826 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVremain/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-403.06388813296985</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-391.75081681767278</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-395.84508606088968</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-399.4971111981983</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-402.74677704239906</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-423.05937046086996</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-408.15687727510931</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-410.32747684563219</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-412.12405813569495</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-413.54183669640037</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-432.77625930299911</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-415.29882001346039</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-415.74723766908124</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-416.04578818737451</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-416.33384272485938</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-434.73332698715899</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-417.5378692550878</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-418.79517740230193</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-420.69471368760065</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-423.36198227815095</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-443.39084205564234</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1422.8935145180094</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1420.1180127294692</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1415.4943018775439</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1409.1332896989074</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1401.220851589936</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1392.0284909289583</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1381.9028906834571</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1371.2485752773973</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1360.4930397414191</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1350.051795344204</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1340.2987812931583</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1331.5480719420229</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1324.0474659949109</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1317.9803902898489</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1313.4707651092456</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1310.5868203495836</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1309.3422763703925</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1309.6993541734587</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1311.5705502162987</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1314.8296979347133</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1319.3252555354456</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">584.42912700217789</Real>
+      <Real Name="Y">-802.93327485318798</Real>
+      <Real Name="Z">-65.147262142945323</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.04836959787693</Real>
+      <Real Name="Y">-226.12538165182306</Real>
+      <Real Name="Z">22.144344554761506</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-431.27126976854635</Real>
+      <Real Name="Y">297.40437117694302</Real>
+      <Real Name="Z">-293.57401884878539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.07141170243186</Real>
+      <Real Name="Y">152.7660985312331</Real>
+      <Real Name="Z">102.69186711650784</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">863.85047581489118</Real>
+      <Real Name="Y">720.35319856813464</Real>
+      <Real Name="Z">8.1459346479316075</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.83230672770941</Real>
+      <Real Name="Y">1948.343335393499</Real>
+      <Real Name="Z">-580.4979612980818</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1317.3159388164152</Real>
+      <Real Name="Y">-13.619697492176604</Real>
+      <Real Name="Z">790.99241452509261</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">336.62836332738829</Real>
+      <Real Name="Y">-65.773591346958739</Real>
+      <Real Name="Z">-441.96395874199686</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">397.35305597181537</Real>
+      <Real Name="Y">-205.45422110979331</Real>
+      <Real Name="Z">59.207551411910764</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.6545042479183536</Real>
+      <Real Name="Y">-32.590042851654871</Real>
+      <Real Name="Z">23.407334828319094</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.8890586628696013</Real>
+      <Real Name="Y">28.887327066206488</Real>
+      <Real Name="Z">-14.141517590114717</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.8310869848251343</Real>
+      <Real Name="Y">15.061368789327467</Real>
+      <Real Name="Z">-5.2951132025283272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.9545375094566566</Real>
+      <Real Name="Y">20.539982301044958</Real>
+      <Real Name="Z">-6.4641524607980543</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.6562228055929324</Real>
+      <Real Name="Y">-8.2100909929834458</Real>
+      <Real Name="Z">4.6308135389540226</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.626991578077579</Real>
+      <Real Name="Y">-23.494149200704676</Real>
+      <Real Name="Z">4.0615728324092686</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">365.04807947409932</Real>
+      <Real Name="Y">-59.824017447813176</Real>
+      <Real Name="Z">-670.57752922466773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-396.07560667731298</Real>
+      <Real Name="Y">36.513676593773887</Real>
+      <Real Name="Z">385.45508358394295</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.6777343272416374</Real>
+      <Real Name="Y">-11.108120095434527</Real>
+      <Real Name="Z">206.64494583848736</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">360.70645669790679</Real>
+      <Real Name="Y">403.99136319378601</Real>
+      <Real Name="Z">-109.62270720812887</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-34.834606988675219</Real>
+      <Real Name="Y">-229.90510904513684</Real>
+      <Real Name="Z">91.776638706844807</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-346.59190131507938</Real>
+      <Real Name="Y">-577.29855133800379</Real>
+      <Real Name="Z">255.04113878652666</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">648.43125270754524</Real>
+      <Real Name="Y">-499.55259341732955</Real>
+      <Real Name="Z">-423.7983961988204</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.96899033879508</Real>
+      <Real Name="Y">124.73053759550046</Real>
+      <Real Name="Z">75.965194617505261</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-648.48361422802668</Real>
+      <Real Name="Y">443.36252812824267</Real>
+      <Real Name="Z">220.66755741875011</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.568198838025303</Real>
+      <Real Name="Y">284.44668089121774</Real>
+      <Real Name="Z">59.024440628744188</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.992791960118495</Real>
+      <Real Name="Y">-158.97971951783376</Real>
+      <Real Name="Z">-107.52600385659274</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.12877152273165</Real>
+      <Real Name="Y">-210.62251426905868</Real>
+      <Real Name="Z">-135.84579192104306</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-591.04728926688051</Real>
+      <Real Name="Y">265.89551461067339</Real>
+      <Real Name="Z">843.69767398993065</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">397.41521183663201</Real>
+      <Real Name="Y">-50.412522099340265</Real>
+      <Real Name="Z">-873.99523133781281</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">175.37380857643055</Real>
+      <Real Name="Y">-188.69480060988295</Real>
+      <Real Name="Z">-64.692716977945395</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1111.3023743776725</Real>
+      <Real Name="Y">594.15158678055661</Real>
+      <Real Name="Z">855.99505362145885</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.55526589188051</Real>
+      <Real Name="Y">-854.67613925040439</Real>
+      <Real Name="Z">-360.1977514590742</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-854.41660549245671</Real>
+      <Real Name="Y">311.97216559277962</Real>
+      <Real Name="Z">-577.37849421218027</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">365.1155441980473</Real>
+      <Real Name="Y">750.57971657333553</Real>
+      <Real Name="Z">140.99503140543618</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.022571261605272</Real>
+      <Real Name="Y">-647.878292309637</Real>
+      <Real Name="Z">-51.309366492401281</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-94.022381466611677</Real>
+      <Real Name="Y">-129.98554007539417</Real>
+      <Real Name="Z">-35.349104767630116</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">18.738438867364991</Real>
+      <Real Name="Y">-179.00517742344277</Real>
+      <Real Name="Z">-895.93763169201884</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.87545981793302</Real>
+      <Real Name="Y">48.335315074445802</Real>
+      <Real Name="Z">256.00341354768341</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">388.31589437442977</Real>
+      <Real Name="Y">0.61097520345364131</Real>
+      <Real Name="Z">679.34811021722157</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">116.96506847151153</Real>
+      <Real Name="Y">43.417934737395655</Real>
+      <Real Name="Z">-856.37171021349582</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-260.24202405052853</Real>
+      <Real Name="Y">163.23364892580935</Real>
+      <Real Name="Z">480.82807014420598</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">11.523654195010508</Real>
+      <Real Name="Y">-195.12503282626531</Real>
+      <Real Name="Z">165.66528643367684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-631.49329022587688</Real>
+      <Real Name="Y">80.385437453374578</Real>
+      <Real Name="Z">363.6183589319528</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">543.81150818105084</Real>
+      <Real Name="Y">-64.416796643815943</Real>
+      <Real Name="Z">-9.880923416666441</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">138.09050803531656</Real>
+      <Real Name="Y">-32.084781741045077</Real>
+      <Real Name="Z">-246.87373857254798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.679932420464212</Real>
+      <Real Name="Y">32.066682842935194</Real>
+      <Real Name="Z">358.31453245636993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">243.5991081229181</Real>
+      <Real Name="Y">-3.0021752872295622</Real>
+      <Real Name="Z">-126.26571094931434</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">56.176687584122796</Real>
+      <Real Name="Y">-111.9756009159927</Real>
+      <Real Name="Z">-113.33375017417194</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-352.20406609886948</Real>
+      <Real Name="Y">-2029.4828683287856</Real>
+      <Real Name="Z">-705.51602218314702</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">339.34615030766321</Real>
+      <Real Name="Y">1308.0865872454574</Real>
+      <Real Name="Z">328.15681821006484</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">279.62704421613284</Real>
+      <Real Name="Y">404.54628768768578</Real>
+      <Real Name="Z">71.58103114093106</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">462.95091918831207</Real>
+      <Real Name="Y">1001.2935500763963</Real>
+      <Real Name="Z">-826.12598738319059</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-160.40024645120911</Real>
+      <Real Name="Y">-535.86442370202974</Real>
+      <Real Name="Z">224.32385795036768</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.77497356867929</Real>
+      <Real Name="Y">-395.13418814162287</Real>
+      <Real Name="Z">539.0824415999374</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">51.161268856274518</Real>
+      <Real Name="Y">94.902208138801541</Real>
+      <Real Name="Z">2.6118315267549335</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.7436290242049211</Real>
+      <Real Name="Y">-41.774149445662758</Real>
+      <Real Name="Z">-2.0966034684143313</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.306565032405079</Real>
+      <Real Name="Y">-31.340395482875991</Real>
+      <Real Name="Z">9.7634139134351052</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-440.98488193370707</Real>
+      <Real Name="Y">979.18008142863005</Real>
+      <Real Name="Z">402.18849020930139</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">607.75454322463304</Real>
+      <Real Name="Y">-690.81247865058424</Real>
+      <Real Name="Z">-456.20294326097803</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">36.325903225664412</Real>
+      <Real Name="Y">-156.71053146122455</Real>
+      <Real Name="Z">-18.140581033659799</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">219.72378080111312</Real>
+      <Real Name="Y">-475.61632295327433</Real>
+      <Real Name="Z">-1377.9934971302166</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-64.43464200411384</Real>
+      <Real Name="Y">337.87697234720457</Real>
+      <Real Name="Z">1034.0138030302453</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-343.41601244694436</Real>
+      <Real Name="Y">21.752399029155178</Real>
+      <Real Name="Z">210.55815495290591</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.61488599217165</Real>
+      <Real Name="Y">-126.06630870837074</Real>
+      <Real Name="Z">-119.62007154256754</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.483076394753652</Real>
+      <Real Name="Y">127.05940253129576</Real>
+      <Real Name="Z">74.182709255768941</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-63.700790284316255</Real>
+      <Real Name="Y">41.827726084882755</Real>
+      <Real Name="Z">151.84296581051655</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">330.80739233932974</Real>
+      <Real Name="Y">-339.24908944306873</Real>
+      <Real Name="Z">-71.749345875554553</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.72606668291957</Real>
+      <Real Name="Y">414.64439601522503</Real>
+      <Real Name="Z">95.911930010543003</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.83185565267458</Real>
+      <Real Name="Y">67.917883875168215</Real>
+      <Real Name="Z">39.231018412399138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">802.73593063331282</Real>
+      <Real Name="Y">-2492.8855155450169</Real>
+      <Real Name="Z">-233.75297991307821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-350.02759858807622</Real>
+      <Real Name="Y">279.52564325607233</Real>
+      <Real Name="Z">586.5546139555322</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.89800039136639</Real>
+      <Real Name="Y">656.92333900399638</Real>
+      <Real Name="Z">99.879611115550418</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.1857373748620432</Real>
+      <Real Name="Y">-9.6816395965907418</Real>
+      <Real Name="Z">4.3652785504241152</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.68221813676200327</Real>
+      <Real Name="Y">6.1738011333494285</Real>
+      <Real Name="Z">-0.777395069447266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.9510833038956008</Real>
+      <Real Name="Y">4.2157122893187449</Real>
+      <Real Name="Z">-4.2671838497133976</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-386.76749336420937</Real>
+      <Real Name="Y">850.50318916624406</Real>
+      <Real Name="Z">232.90191211758028</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">284.02659886564425</Real>
+      <Real Name="Y">-481.91656446229115</Real>
+      <Real Name="Z">-675.12269119925122</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-318.26825221103672</Real>
+      <Real Name="Y">-438.49907924232309</Real>
+      <Real Name="Z">302.73517239513973</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">371.11000009277996</Real>
+      <Real Name="Y">26.880953138224754</Real>
+      <Real Name="Z">-150.72054388979004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.08656114717297</Real>
+      <Real Name="Y">60.544028679105615</Real>
+      <Real Name="Z">39.596453218475901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.70629407593907</Real>
+      <Real Name="Y">-35.963412388029973</Real>
+      <Real Name="Z">73.546532423477515</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-540.86917791504311</Real>
+      <Real Name="Y">-1275.4473082649754</Real>
+      <Real Name="Z">-297.15566101022949</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">193.9374066867849</Real>
+      <Real Name="Y">610.37576194996473</Real>
+      <Real Name="Z">-292.53568113931954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.86864393677791</Real>
+      <Real Name="Y">652.82819394316675</Real>
+      <Real Name="Z">620.85081282501812</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1196.6177774044245</Real>
+      <Real Name="Y">443.25073823020369</Real>
+      <Real Name="Z">-794.11870353113943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">820.79110620823963</Real>
+      <Real Name="Y">-311.44932664652629</Real>
+      <Real Name="Z">572.98754677578336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.64047731398239</Real>
+      <Real Name="Y">-31.941282088836378</Real>
+      <Real Name="Z">40.208521878472794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">219.60527025605066</Real>
+      <Real Name="Y">428.50174999638023</Real>
+      <Real Name="Z">165.84096052900125</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.097502505502206</Real>
+      <Real Name="Y">-133.77816162204005</Real>
+      <Real Name="Z">-72.055362607127464</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-109.79493277415692</Real>
+      <Real Name="Y">-123.72008691605379</Real>
+      <Real Name="Z">-17.679456345670104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.5285502124211</Real>
+      <Real Name="Y">-1076.1776173613282</Real>
+      <Real Name="Z">-857.81540274319229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.13340750574793</Real>
+      <Real Name="Y">210.30851164421824</Real>
+      <Real Name="Z">195.26323794794121</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">576.12265572301021</Real>
+      <Real Name="Y">692.23171008653151</Real>
+      <Real Name="Z">681.1407253565352</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.07239973128088</Real>
+      <Real Name="Y">386.40570531406217</Real>
+      <Real Name="Z">-1021.567679909635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-100.05992929522451</Real>
+      <Real Name="Y">-316.07010122313181</Real>
+      <Real Name="Z">752.5628334878536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.873362286069451</Real>
+      <Real Name="Y">33.846888661357852</Real>
+      <Real Name="Z">254.00417126440217</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.17299246435903</Real>
+      <Real Name="Y">-514.17890448875244</Real>
+      <Real Name="Z">-928.53990406014384</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.56568954694905</Real>
+      <Real Name="Y">80.396056625859046</Real>
+      <Real Name="Z">145.66222060996984</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.452116396501907</Real>
+      <Real Name="Y">257.98761439356997</Real>
+      <Real Name="Z">803.56077588238702</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-601.33547552066977</Real>
+      <Real Name="Y">-126.15461087206535</Real>
+      <Real Name="Z">-114.83878958140259</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">348.24976075491787</Real>
+      <Real Name="Y">-14.94173673798516</Real>
+      <Real Name="Z">80.183049427886033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">248.37626960174811</Real>
+      <Real Name="Y">46.123949310645344</Real>
+      <Real Name="Z">92.700611666070088</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">601.93522343128836</Real>
+      <Real Name="Y">825.61544304359177</Real>
+      <Real Name="Z">-272.5843931035231</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-127.05384465558642</Real>
+      <Real Name="Y">-202.47392025384505</Real>
+      <Real Name="Z">18.54542478775133</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-469.16299129250234</Real>
+      <Real Name="Y">-870.67086972959919</Real>
+      <Real Name="Z">-32.410156744437685</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1610.9815473363126</Real>
+      <Real Name="Y">-227.84104959504677</Real>
+      <Real Name="Z">825.68894780587948</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-834.62445783652788</Real>
+      <Real Name="Y">388.16285290056896</Real>
+      <Real Name="Z">-612.35510120958236</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-226.9700087192351</Real>
+      <Real Name="Y">-150.77882760394107</Real>
+      <Real Name="Z">-335.41305800676992</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.27397013392203</Real>
+      <Real Name="Y">55.062080177132415</Real>
+      <Real Name="Z">125.46228783158915</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.79590800039134</Real>
+      <Real Name="Y">-162.44762295973135</Real>
+      <Real Name="Z">29.880675259899121</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">199.69794458816739</Real>
+      <Real Name="Y">-7.1478766117164731</Real>
+      <Real Name="Z">25.14433910271535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">870.42634269427253</Real>
+      <Real Name="Y">-524.54205043904381</Real>
+      <Real Name="Z">-75.500722340349284</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-583.05913237750519</Real>
+      <Real Name="Y">289.86033786763323</Real>
+      <Real Name="Z">28.795863252795364</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-459.29592624861073</Real>
+      <Real Name="Y">400.0208960064013</Real>
+      <Real Name="Z">-112.89439417346574</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-127.82446917958299</Real>
+      <Real Name="Y">-1826.7281515459617</Real>
+      <Real Name="Z">33.689033978581946</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">130.45096813433005</Real>
+      <Real Name="Y">244.80450010230484</Real>
+      <Real Name="Z">-32.721132405332298</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.39249843446072</Real>
+      <Real Name="Y">1231.0290608214777</Real>
+      <Real Name="Z">-57.34353911584909</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">445.96522014546298</Real>
+      <Real Name="Y">1039.7808378621846</Real>
+      <Real Name="Z">855.36904180601869</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-317.60689133181842</Real>
+      <Real Name="Y">-647.88712217648424</Real>
+      <Real Name="Z">-582.83810811578132</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-61.928989898005312</Real>
+      <Real Name="Y">-240.86105555353902</Real>
+      <Real Name="Z">-227.89485152555824</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">234.17245913813147</Real>
+      <Real Name="Y">-384.6088112121775</Real>
+      <Real Name="Z">-390.59532753251483</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-250.6838753260177</Real>
+      <Real Name="Y">369.66404510795843</Real>
+      <Real Name="Z">210.310811975246</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.15172724564906</Real>
+      <Real Name="Y">156.66308223174849</Real>
+      <Real Name="Z">496.3436975763459</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">771.94238919006125</Real>
+      <Real Name="Y">-318.32768233977134</Real>
+      <Real Name="Z">-231.63588779678585</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.19084424785174</Real>
+      <Real Name="Y">456.90435298191295</Real>
+      <Real Name="Z">-1.1647941600522458</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-740.93909996316052</Real>
+      <Real Name="Y">-153.06147510797229</Real>
+      <Real Name="Z">173.98872826112762</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1098.976008469961</Real>
+      <Real Name="Y">1492.8293026877757</Real>
+      <Real Name="Z">-674.09131142777017</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">177.6537645078775</Real>
+      <Real Name="Y">-208.44060585280346</Real>
+      <Real Name="Z">93.963446451315264</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">629.6728964237368</Real>
+      <Real Name="Y">-958.65628779485928</Real>
+      <Real Name="Z">443.77059669273774</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">745.25299955800369</Real>
+      <Real Name="Y">19.805301512593687</Real>
+      <Real Name="Z">142.92264379599908</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.38681712904753</Real>
+      <Real Name="Y">96.77801836627485</Real>
+      <Real Name="Z">-34.046112298980297</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-917.01088013901517</Real>
+      <Real Name="Y">124.65573092093754</Real>
+      <Real Name="Z">-125.66321381704738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-782.89157121988262</Real>
+      <Real Name="Y">-1239.0706664724755</Real>
+      <Real Name="Z">108.79185961208196</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">694.32872399131338</Real>
+      <Real Name="Y">955.20413247763474</Real>
+      <Real Name="Z">62.051215212001068</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.432363490173557</Real>
+      <Real Name="Y">254.96988701708548</Real>
+      <Real Name="Z">-74.992814232152398</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.05189812609478</Real>
+      <Real Name="Y">-685.02738286949045</Real>
+      <Real Name="Z">811.23197910216606</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">145.04546593014129</Real>
+      <Real Name="Y">499.45416196449935</Real>
+      <Real Name="Z">-582.26161460649075</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-53.173734342823394</Real>
+      <Real Name="Y">73.682628425153197</Real>
+      <Real Name="Z">-118.52530380032486</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">53.906752366964525</Real>
+      <Real Name="Y">-121.80151822858295</Real>
+      <Real Name="Z">277.68015329764211</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.812984311622262</Real>
+      <Real Name="Y">250.4387995898669</Real>
+      <Real Name="Z">-49.252895496487582</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-288.68871263149066</Real>
+      <Real Name="Y">358.51110229411131</Real>
+      <Real Name="Z">67.340128345627491</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">81.419712899915041</Real>
+      <Real Name="Y">482.9570700702792</Real>
+      <Real Name="Z">-75.56224563518937</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.232989188175829</Real>
+      <Real Name="Y">-178.42512581764908</Real>
+      <Real Name="Z">68.164948235951016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.254329748476657</Real>
+      <Real Name="Y">-189.97224147528752</Real>
+      <Real Name="Z">29.092059786649045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">77.860338780175994</Real>
+      <Real Name="Y">773.81031281907531</Real>
+      <Real Name="Z">73.098075191299543</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-47.710679985949653</Real>
+      <Real Name="Y">-170.22775248423648</Real>
+      <Real Name="Z">-65.341295021246324</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.47519813475634</Real>
+      <Real Name="Y">-728.8019071515107</Real>
+      <Real Name="Z">-10.852549619618758</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.06446926867531</Real>
+      <Real Name="Y">-724.7019685791438</Real>
+      <Real Name="Z">-206.62699244391624</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.8714693486082</Real>
+      <Real Name="Y">627.07826110760141</Real>
+      <Real Name="Z">110.28180722165348</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">72.996132277400818</Real>
+      <Real Name="Y">177.8023368815752</Real>
+      <Real Name="Z">42.16367284728473</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.35657942658924</Real>
+      <Real Name="Y">925.60549745252354</Real>
+      <Real Name="Z">804.71616277976545</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">65.628525823292662</Real>
+      <Real Name="Y">-180.52028438193366</Real>
+      <Real Name="Z">-180.24958861596974</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">117.49080163221282</Real>
+      <Real Name="Y">-716.27861925128411</Real>
+      <Real Name="Z">-492.81088156204379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-288.58042233578607</Real>
+      <Real Name="Y">312.54726505083386</Real>
+      <Real Name="Z">-426.96297412599461</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.868405129629</Real>
+      <Real Name="Y">-258.12022438431262</Real>
+      <Real Name="Z">442.67217342981684</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">146.57153127830688</Real>
+      <Real Name="Y">-121.17507783377674</Real>
+      <Real Name="Z">96.237926576940509</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-876.80955547193855</Real>
+      <Real Name="Y">-109.23819965924343</Real>
+      <Real Name="Z">-58.799833161276794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">729.17110327832529</Real>
+      <Real Name="Y">-55.10347131319508</Real>
+      <Real Name="Z">5.5009217768556056</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">309.9246781018465</Real>
+      <Real Name="Y">52.748011509702039</Real>
+      <Real Name="Z">6.5494817479104483</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1513.4929355652473</Real>
+      <Real Name="Y">-280.08161887643826</Real>
+      <Real Name="Z">-655.00420672695896</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">291.32489243560082</Real>
+      <Real Name="Y">-38.160652727776586</Real>
+      <Real Name="Z">209.88820368160674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1064.7297948868786</Real>
+      <Real Name="Y">346.75317317205537</Real>
+      <Real Name="Z">334.37324630810053</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-350.96319270239871</Real>
+      <Real Name="Y">1444.6238594353092</Real>
+      <Real Name="Z">296.7385575638379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.0788008279362238</Real>
+      <Real Name="Y">-732.56231221463202</Real>
+      <Real Name="Z">-573.16457014977709</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">342.0261765267748</Real>
+      <Real Name="Y">-457.06226942725795</Real>
+      <Real Name="Z">492.52804151393605</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">202.00512708654693</Real>
+      <Real Name="Y">437.12949779124216</Real>
+      <Real Name="Z">127.20881429814386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-67.580623015625278</Real>
+      <Real Name="Y">-123.24538483015286</Real>
+      <Real Name="Z">-82.558778091876661</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.58546248097377</Real>
+      <Real Name="Y">-494.5717484592796</Real>
+      <Real Name="Z">-109.3584858554326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1158.7684419558332</Real>
+      <Real Name="Y">-2026.2162943856424</Real>
+      <Real Name="Z">670.74178604015481</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-126.02302525361782</Real>
+      <Real Name="Y">1240.2567961946322</Real>
+      <Real Name="Z">316.36038955638509</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1027.4807572727946</Real>
+      <Real Name="Y">396.9925486053051</Real>
+      <Real Name="Z">-803.00283990913488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">786.51563112549127</Real>
+      <Real Name="Y">-1314.7776525764884</Real>
+      <Real Name="Z">-296.77078917732138</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-247.20659036321607</Real>
+      <Real Name="Y">690.65366534383213</Real>
+      <Real Name="Z">25.508129769527518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-500.82812750271682</Real>
+      <Real Name="Y">707.16604375145948</Real>
+      <Real Name="Z">322.86599002227075</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">72.411835521324321</Real>
+      <Real Name="Y">813.07091939320946</Real>
+      <Real Name="Z">1328.627981850598</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.22346871837269</Real>
+      <Real Name="Y">-713.35547136740331</Real>
+      <Real Name="Z">-1006.7721737452833</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.50746453314264</Real>
+      <Real Name="Y">22.401333031440306</Real>
+      <Real Name="Z">-244.71697231089888</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.020000 Step 20 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">1062.7177925934448</Real>
+      <Real Name="Y">645.03793012621009</Real>
+      <Real Name="Z">-1103.4159633207503</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.05912726846577</Real>
+      <Real Name="Y">663.25106844312722</Real>
+      <Real Name="Z">-1354.4303213418468</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1482.9273681848024</Real>
+      <Real Name="Y">-1297.8711411014071</Real>
+      <Real Name="Z">801.45477104717759</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">80.841902348120982</Real>
+      <Real Name="Y">1269.5237190565356</Real>
+      <Real Name="Z">1294.0836035749501</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.396353464847842</Real>
+      <Real Name="Y">-708.10997062466049</Real>
+      <Real Name="Z">-120.63885606631371</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-588.92674063772074</Real>
+      <Real Name="Y">-345.60107107136452</Real>
+      <Real Name="Z">243.93649340395646</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">847.66018933302303</Real>
+      <Real Name="Y">-1407.4518288169243</Real>
+      <Real Name="Z">-732.74203389772629</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">440.01598408773515</Real>
+      <Real Name="Y">720.92139105531282</Real>
+      <Real Name="Z">203.99969806831302</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-181.43553902588593</Real>
+      <Real Name="Y">607.35287818479514</Real>
+      <Real Name="Z">361.4594055016052</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1.0243373643140501</Real>
+      <Real Name="Y">0.42289134527016703</Real>
+      <Real Name="Z">18.746922154646889</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.3039182070369719</Real>
+      <Real Name="Y">8.687373450350595</Real>
+      <Real Name="Z">-4.0761695932880855</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.307658672862118</Real>
+      <Real Name="Y">2.4428114336227122</Real>
+      <Real Name="Z">-10.14904276029695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.251096884362369</Real>
+      <Real Name="Y">7.6530654227753985</Real>
+      <Real Name="Z">-25.430066740885735</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.5076806528585962</Real>
+      <Real Name="Y">-2.6339067869384429</Real>
+      <Real Name="Z">12.386527513844033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.94076599905523</Real>
+      <Real Name="Y">-13.679543743751665</Real>
+      <Real Name="Z">10.906784753702977</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">287.22465912917744</Real>
+      <Real Name="Y">-117.18343475825753</Real>
+      <Real Name="Z">-748.81692750497621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-293.65577693748878</Real>
+      <Real Name="Y">198.67707501327425</Real>
+      <Real Name="Z">400.89079638556495</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.943697118939312</Real>
+      <Real Name="Y">9.029506211393203</Real>
+      <Real Name="Z">176.69009182249096</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">430.01294841751832</Real>
+      <Real Name="Y">602.02962841339058</Real>
+      <Real Name="Z">-227.43426708793166</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-102.28402055233661</Real>
+      <Real Name="Y">-247.18769939521025</Real>
+      <Real Name="Z">87.161951776794311</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-384.76168631422036</Real>
+      <Real Name="Y">-570.11625634911968</Real>
+      <Real Name="Z">308.91481945665606</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">438.94813214287774</Real>
+      <Real Name="Y">-418.06949372589122</Real>
+      <Real Name="Z">-480.56433793409434</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-79.379867639432092</Real>
+      <Real Name="Y">143.81707023681349</Real>
+      <Real Name="Z">124.29116084810269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-457.6037974903399</Real>
+      <Real Name="Y">417.39077661782022</Real>
+      <Real Name="Z">455.25275536314331</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">120.84414067524182</Real>
+      <Real Name="Y">229.03072013970782</Real>
+      <Real Name="Z">276.86265831908588</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.833072398847586</Real>
+      <Real Name="Y">-163.32634685989566</Real>
+      <Real Name="Z">-133.24667914403108</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-184.48888231000083</Real>
+      <Real Name="Y">-241.59587058264671</Real>
+      <Real Name="Z">-143.53732909313203</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-433.27799440236583</Real>
+      <Real Name="Y">407.88916860294574</Real>
+      <Real Name="Z">880.6694996143774</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">401.07251555851758</Real>
+      <Real Name="Y">-263.15637960371771</Real>
+      <Real Name="Z">-832.3608464430157</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">153.89783265840964</Real>
+      <Real Name="Y">-228.45555796743565</Real>
+      <Real Name="Z">-117.18504244461154</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1257.8907087921018</Real>
+      <Real Name="Y">301.81297555887943</Real>
+      <Real Name="Z">825.04956062929523</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">18.457467524274801</Real>
+      <Real Name="Y">-539.09150673504234</Real>
+      <Real Name="Z">-188.75600624764709</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-974.80369728546395</Real>
+      <Real Name="Y">529.03562750027561</Real>
+      <Real Name="Z">-539.92071047031152</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">264.80597729013596</Real>
+      <Real Name="Y">1014.4929312105347</Real>
+      <Real Name="Z">180.61766147272635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-84.600183276489972</Real>
+      <Real Name="Y">-384.2346305757863</Real>
+      <Real Name="Z">-56.27174266122006</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-100.75042446583481</Real>
+      <Real Name="Y">-184.8327394542107</Real>
+      <Real Name="Z">-59.886660928939108</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-96.005410889823338</Real>
+      <Real Name="Y">37.265167369985129</Real>
+      <Real Name="Z">-997.45456926344093</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-137.19106375426557</Real>
+      <Real Name="Y">225.8982734844771</Real>
+      <Real Name="Z">317.81779117592419</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">296.31794648654471</Real>
+      <Real Name="Y">116.4408457210464</Real>
+      <Real Name="Z">309.95280144305036</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">484.1667221320904</Real>
+      <Real Name="Y">233.40472366792898</Real>
+      <Real Name="Z">-401.06160591913454</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-471.24823217078614</Real>
+      <Real Name="Y">57.451961919377965</Real>
+      <Real Name="Z">644.60289955436792</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-181.92224198845207</Real>
+      <Real Name="Y">-237.95805396646892</Real>
+      <Real Name="Z">51.403893386492662</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-947.08885897459936</Real>
+      <Real Name="Y">181.03599889966267</Real>
+      <Real Name="Z">267.35102230995079</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">588.13258301971052</Real>
+      <Real Name="Y">-317.35960784264313</Real>
+      <Real Name="Z">-153.64331149558535</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">168.23845310814059</Real>
+      <Real Name="Y">-0.5964313369309</Real>
+      <Real Name="Z">-177.55992315042459</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-422.47053491764706</Real>
+      <Real Name="Y">85.893014869017861</Real>
+      <Real Name="Z">279.49784648278518</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">147.7410950992741</Real>
+      <Real Name="Y">-3.5608692137304701</Real>
+      <Real Name="Z">-54.958698055025948</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">155.32896485946526</Real>
+      <Real Name="Y">-195.93206980228126</Real>
+      <Real Name="Z">-99.786234056647302</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-170.7779543010553</Real>
+      <Real Name="Y">-1474.3967965819645</Real>
+      <Real Name="Z">-694.44140964723442</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">42.606925052934358</Real>
+      <Real Name="Y">1369.8591958157549</Real>
+      <Real Name="Z">470.14247668597727</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.36669841160648</Real>
+      <Real Name="Y">370.01927634920128</Real>
+      <Real Name="Z">128.82649517973744</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.32081386425659</Real>
+      <Real Name="Y">811.22470183172413</Real>
+      <Real Name="Z">-898.0962145772736</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.01303424302515</Real>
+      <Real Name="Y">-475.9770824723484</Real>
+      <Real Name="Z">34.001805502476181</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-421.89712759355098</Real>
+      <Real Name="Y">-205.23908717988976</Real>
+      <Real Name="Z">328.63965267144135</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">67.458237727070184</Real>
+      <Real Name="Y">126.87094909562603</Real>
+      <Real Name="Z">33.158444902180875</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-15.992010770922999</Real>
+      <Real Name="Y">-51.857814850827125</Real>
+      <Real Name="Z">-13.103548355421733</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-31.230648199236981</Real>
+      <Real Name="Y">-42.450068068427043</Real>
+      <Real Name="Z">-11.568873700251464</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-383.33895827722375</Real>
+      <Real Name="Y">917.85565100353176</Real>
+      <Real Name="Z">244.47872260204133</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">450.40519421075567</Real>
+      <Real Name="Y">-696.72888570069233</Real>
+      <Real Name="Z">-413.23765289391088</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">26.44529702389142</Real>
+      <Real Name="Y">-156.32937017761296</Real>
+      <Real Name="Z">-26.54757237488203</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">594.26882078438291</Real>
+      <Real Name="Y">-626.17960605241888</Real>
+      <Real Name="Z">-1279.9742359891789</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-380.46789755477016</Real>
+      <Real Name="Y">276.49730325857155</Real>
+      <Real Name="Z">999.12822793131954</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-450.55107005031999</Real>
+      <Real Name="Y">132.29673968170653</Real>
+      <Real Name="Z">78.425545569727149</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">42.942276546858125</Real>
+      <Real Name="Y">-256.47122267193186</Real>
+      <Real Name="Z">24.78744634978807</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-26.60093720755161</Real>
+      <Real Name="Y">113.0514983420389</Real>
+      <Real Name="Z">35.150440489722143</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-38.375059238666907</Real>
+      <Real Name="Y">64.913510633526485</Real>
+      <Real Name="Z">128.97612544779741</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">334.26601787423931</Real>
+      <Real Name="Y">-307.63497126709865</Real>
+      <Real Name="Z">-97.690577243605333</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-308.0376600733502</Real>
+      <Real Name="Y">377.02088863021851</Real>
+      <Real Name="Z">156.37188221384866</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-88.726036213398359</Real>
+      <Real Name="Y">46.615855942960593</Real>
+      <Real Name="Z">53.734210542228041</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">843.36954786477281</Real>
+      <Real Name="Y">-528.01986745158911</Real>
+      <Real Name="Z">-478.38713337934126</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-461.6676282262161</Real>
+      <Real Name="Y">235.8187191296833</Real>
+      <Real Name="Z">773.22847175805202</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-212.14278630754595</Real>
+      <Real Name="Y">536.25792236913458</Real>
+      <Real Name="Z">194.96222844669842</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.4999213503735902</Real>
+      <Real Name="Y">-12.417492541978532</Real>
+      <Real Name="Z">25.331651250929184</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.7035942513504008</Real>
+      <Real Name="Y">6.2735450544708424</Real>
+      <Real Name="Z">-9.3370699543762186</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.48265848814845214</Real>
+      <Real Name="Y">6.623801442488741</Real>
+      <Real Name="Z">-14.803410324159003</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-165.60994849963799</Real>
+      <Real Name="Y">812.97773626737239</Real>
+      <Real Name="Z">122.73782057005712</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">476.16168776089626</Real>
+      <Real Name="Y">-387.39911885547252</Real>
+      <Real Name="Z">-523.541319461223</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-209.42257479300613</Real>
+      <Real Name="Y">-365.11565030369491</Real>
+      <Real Name="Z">306.04664832278968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">343.08045403915889</Real>
+      <Real Name="Y">45.193447721037614</Real>
+      <Real Name="Z">-315.77397705659041</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-307.27608976611623</Real>
+      <Real Name="Y">46.610364128085152</Real>
+      <Real Name="Z">81.716028352010483</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-81.251382323327533</Real>
+      <Real Name="Y">-19.596220429194954</Real>
+      <Real Name="Z">103.33780412764575</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.81144776271219</Real>
+      <Real Name="Y">-1135.0340171942078</Real>
+      <Real Name="Z">-486.62232340707726</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">201.80993161765079</Real>
+      <Real Name="Y">311.11744767995674</Real>
+      <Real Name="Z">-174.26038445097109</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">425.65121868239828</Real>
+      <Real Name="Y">684.48595940605185</Real>
+      <Real Name="Z">650.11868068079912</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-865.08221683428087</Real>
+      <Real Name="Y">329.38009733869654</Real>
+      <Real Name="Z">-511.94794045560116</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">684.64747103741661</Real>
+      <Real Name="Y">-270.72142728728852</Real>
+      <Real Name="Z">486.16697550087406</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.13918721580737</Real>
+      <Real Name="Y">-1.4770904663520739</Real>
+      <Real Name="Z">33.421720861233744</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">255.69856511304823</Real>
+      <Real Name="Y">427.0178411141884</Real>
+      <Real Name="Z">76.467018764852696</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.496573732339762</Real>
+      <Real Name="Y">-153.99982938601912</Real>
+      <Real Name="Z">-60.9405186174424</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-123.14992851508393</Real>
+      <Real Name="Y">-151.38366928857252</Real>
+      <Real Name="Z">13.000739952504571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-822.38835915395191</Real>
+      <Real Name="Y">-796.1415634971263</Real>
+      <Real Name="Z">-525.63665411858813</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.58344430051892</Real>
+      <Real Name="Y">199.64796344507371</Real>
+      <Real Name="Z">154.96404844471525</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">705.22648714037075</Real>
+      <Real Name="Y">522.71912146034299</Real>
+      <Real Name="Z">401.08930772985303</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.406356922894169</Real>
+      <Real Name="Y">-72.720700472002278</Real>
+      <Real Name="Z">-788.50434292355624</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">12.979822892122344</Real>
+      <Real Name="Y">-197.25736187536933</Real>
+      <Real Name="Z">726.19392375112295</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.898298286847016</Real>
+      <Real Name="Y">58.236692468596246</Real>
+      <Real Name="Z">154.50349057503715</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-535.43006962380127</Real>
+      <Real Name="Y">-518.75249022731987</Real>
+      <Real Name="Z">-919.2112744970899</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">127.03454119947151</Real>
+      <Real Name="Y">76.564405917278165</Real>
+      <Real Name="Z">157.16146509742092</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">241.7508807167354</Real>
+      <Real Name="Y">493.42231386850199</Real>
+      <Real Name="Z">745.45505468977933</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-630.48905908022186</Real>
+      <Real Name="Y">-383.38246256164871</Real>
+      <Real Name="Z">36.203960115359095</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">475.45623134945902</Real>
+      <Real Name="Y">170.90977960985128</Real>
+      <Real Name="Z">-63.574147728095745</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">242.43402158809667</Real>
+      <Real Name="Y">120.38885581491711</Real>
+      <Real Name="Z">-10.569613833899634</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.92538475242782</Real>
+      <Real Name="Y">765.82724704296515</Real>
+      <Real Name="Z">74.654449355955649</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-12.472132326273162</Real>
+      <Real Name="Y">-158.92893221121923</Real>
+      <Real Name="Z">-25.252671282327668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-197.43829957545799</Real>
+      <Real Name="Y">-813.41984667214376</Real>
+      <Real Name="Z">-149.76971808563422</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1881.0872756210924</Real>
+      <Real Name="Y">-269.33811621335303</Real>
+      <Real Name="Z">759.0100359861342</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1100.5421324051536</Real>
+      <Real Name="Y">293.03194683768572</Real>
+      <Real Name="Z">-617.81675255323592</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-409.70391041237798</Real>
+      <Real Name="Y">-156.61511817631302</Real>
+      <Real Name="Z">-296.27884712221606</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-302.81742593687665</Real>
+      <Real Name="Y">69.42623747042353</Real>
+      <Real Name="Z">-98.358821368496464</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.09887912060384</Real>
+      <Real Name="Y">-135.68286598278661</Real>
+      <Real Name="Z">53.346190205883062</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">172.84567110857779</Real>
+      <Real Name="Y">-5.3900278915307034</Real>
+      <Real Name="Z">38.085799017382882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">893.90259773877983</Real>
+      <Real Name="Y">-100.3201823019827</Real>
+      <Real Name="Z">109.1878307530971</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-652.73543148316548</Real>
+      <Real Name="Y">203.18183954710673</Real>
+      <Real Name="Z">20.31426142471571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-439.40982659632425</Real>
+      <Real Name="Y">174.99123301629129</Real>
+      <Real Name="Z">-62.107255172560258</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-186.95446045730051</Real>
+      <Real Name="Y">-1137.8282114500416</Real>
+      <Real Name="Z">2.9625691530359148</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">83.278285518382461</Real>
+      <Real Name="Y">173.44403059590869</Real>
+      <Real Name="Z">-38.445884558877324</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">141.17501521465186</Real>
+      <Real Name="Y">1122.0662038424236</Real>
+      <Real Name="Z">31.756865378755059</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">220.92510237300056</Real>
+      <Real Name="Y">1079.8631621674833</Real>
+      <Real Name="Z">787.12181673502835</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-180.7202714763483</Real>
+      <Real Name="Y">-657.48512327933724</Real>
+      <Real Name="Z">-642.38007160136965</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.773365160786838</Real>
+      <Real Name="Y">-284.47670805728666</Real>
+      <Real Name="Z">-137.81958294486415</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">114.78771129347044</Real>
+      <Real Name="Y">-312.6632497007505</Real>
+      <Real Name="Z">-134.40014017317839</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-131.59372888782559</Real>
+      <Real Name="Y">354.91257533989733</Real>
+      <Real Name="Z">-21.497646969639639</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">128.74442390780976</Real>
+      <Real Name="Y">70.91373846373591</Real>
+      <Real Name="Z">304.40090805187367</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">725.62813894091028</Real>
+      <Real Name="Y">-478.87069056967675</Real>
+      <Real Name="Z">-195.90951783541706</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-316.16096260804949</Real>
+      <Real Name="Y">567.23316592496349</Real>
+      <Real Name="Z">55.66239019817673</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-617.88956871720302</Real>
+      <Real Name="Y">-74.286346400054896</Real>
+      <Real Name="Z">206.5392423638059</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-965.04325921947418</Real>
+      <Real Name="Y">1169.2610679865336</Real>
+      <Real Name="Z">-660.16222861762981</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.80974036192933</Real>
+      <Real Name="Y">-168.72269909978098</Real>
+      <Real Name="Z">100.81757353987823</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">562.03230465401248</Real>
+      <Real Name="Y">-824.76894444037612</Real>
+      <Real Name="Z">524.01271254649976</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">936.00661684143597</Real>
+      <Real Name="Y">-117.45458735729861</Real>
+      <Real Name="Z">53.275615932562388</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.75486145873597</Real>
+      <Real Name="Y">87.472111398320479</Real>
+      <Real Name="Z">-31.939657050672196</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-897.64275247559578</Real>
+      <Real Name="Y">-59.925974319084446</Real>
+      <Real Name="Z">-97.789260667588863</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-607.3868151409597</Real>
+      <Real Name="Y">-659.6318019897833</Real>
+      <Real Name="Z">115.4721514248493</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">595.13856386582154</Real>
+      <Real Name="Y">676.25402119069156</Real>
+      <Real Name="Z">34.145359243129626</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">38.881398448520336</Real>
+      <Real Name="Y">115.28674075923813</Real>
+      <Real Name="Z">-71.839590746790861</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-275.42976906235111</Real>
+      <Real Name="Y">-1094.0843672913547</Real>
+      <Real Name="Z">1188.2133703698375</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">311.53245593623603</Real>
+      <Real Name="Y">622.43341929037251</Real>
+      <Real Name="Z">-551.8008629987379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-73.387348320893949</Real>
+      <Real Name="Y">138.79451122450993</Real>
+      <Real Name="Z">-187.52281283905813</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">406.49623447043882</Real>
+      <Real Name="Y">-511.75126469337334</Real>
+      <Real Name="Z">1.7471593984078027</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-98.595486793034979</Real>
+      <Real Name="Y">278.33289069239117</Real>
+      <Real Name="Z">-50.092012137279752</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-427.25986923665789</Real>
+      <Real Name="Y">360.35120989891027</Real>
+      <Real Name="Z">-34.066809827424706</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.17175346264683</Real>
+      <Real Name="Y">443.88372453592166</Real>
+      <Real Name="Z">-9.2223135193455654</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-40.16356672932708</Real>
+      <Real Name="Y">-211.48269722388216</Real>
+      <Real Name="Z">42.214152022197624</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">42.786128855540696</Real>
+      <Real Name="Y">-134.13896550384604</Real>
+      <Real Name="Z">5.6370105411184461</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.8597187351118869</Real>
+      <Real Name="Y">777.08449092149488</Real>
+      <Real Name="Z">66.717092908371228</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.792498700615258</Real>
+      <Real Name="Y">-183.05448019973335</Real>
+      <Real Name="Z">-60.972578733681416</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.22433001194869</Real>
+      <Real Name="Y">-678.00301099623152</Real>
+      <Real Name="Z">-42.949812405246817</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-121.20569937669416</Real>
+      <Real Name="Y">-678.75247827217333</Real>
+      <Real Name="Z">-271.83315968738725</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">210.25825679962463</Real>
+      <Real Name="Y">596.55314610282051</Real>
+      <Real Name="Z">154.62035738225751</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">94.807534484371502</Real>
+      <Real Name="Y">151.85683006323256</Real>
+      <Real Name="Z">38.932855649143875</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.7183970929168</Real>
+      <Real Name="Y">749.213038657384</Real>
+      <Real Name="Z">891.02498873054321</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.397252546521571</Real>
+      <Real Name="Y">-148.06182721579947</Real>
+      <Real Name="Z">-185.0570436470052</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-64.700002886698613</Real>
+      <Real Name="Y">-696.31547852706353</Real>
+      <Real Name="Z">-565.08099381708553</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.45280447145552</Real>
+      <Real Name="Y">184.85992496771706</Real>
+      <Real Name="Z">-336.78105995212201</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">109.46874592381414</Real>
+      <Real Name="Y">-108.14597932541</Real>
+      <Real Name="Z">342.60779878890162</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">127.27874494225711</Real>
+      <Real Name="Y">-67.953743914936425</Real>
+      <Real Name="Z">101.2233973417343</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-979.9903572072725</Real>
+      <Real Name="Y">28.259997997521438</Real>
+      <Real Name="Z">-61.795713955481574</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">713.27574137342481</Real>
+      <Real Name="Y">-140.53459241334212</Real>
+      <Real Name="Z">-17.068831953799524</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">329.26487816436969</Real>
+      <Real Name="Y">-2.8627293006810746</Real>
+      <Real Name="Z">9.6904071495541331</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1537.3315706188464</Real>
+      <Real Name="Y">-881.00107515566162</Real>
+      <Real Name="Z">-449.03102944968856</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">334.86027552783275</Real>
+      <Real Name="Y">64.381265219172846</Real>
+      <Real Name="Z">125.11681935125229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1175.924263713232</Real>
+      <Real Name="Y">211.97508184516647</Real>
+      <Real Name="Z">325.42527474202103</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.904418792082879</Real>
+      <Real Name="Y">1506.5572546799615</Real>
+      <Real Name="Z">123.41548760762653</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-55.560404831952525</Real>
+      <Real Name="Y">-819.68107887082886</Real>
+      <Real Name="Z">-445.73273554196567</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">191.23404033051565</Real>
+      <Real Name="Y">-642.88964405365357</Real>
+      <Real Name="Z">562.92009605825365</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">244.23646132451677</Real>
+      <Real Name="Y">506.73014108533744</Real>
+      <Real Name="Z">160.74089636012491</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.784640142439628</Real>
+      <Real Name="Y">-132.18021560325707</Real>
+      <Real Name="Z">-105.72419628896009</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-260.52220224800004</Real>
+      <Real Name="Y">-512.78633585308285</Real>
+      <Real Name="Z">-77.469207325786996</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">642.13052123281091</Real>
+      <Real Name="Y">-2210.2448351377056</Real>
+      <Real Name="Z">515.89069534910595</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">18.827182993770762</Real>
+      <Real Name="Y">1314.2740649564998</Real>
+      <Real Name="Z">241.7289254906494</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-942.28815704485089</Real>
+      <Real Name="Y">553.65154853629519</Real>
+      <Real Name="Z">-725.17719939299855</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">841.94860533219526</Real>
+      <Real Name="Y">-1993.9952186170294</Real>
+      <Real Name="Z">-672.06376971701468</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.169330963054904</Real>
+      <Real Name="Y">838.21257234635232</Real>
+      <Real Name="Z">208.3256067595072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-565.93531539819571</Real>
+      <Real Name="Y">691.0736434806779</Real>
+      <Real Name="Z">436.80862814315765</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">92.0340310966548</Real>
+      <Real Name="Y">757.93170111783877</Real>
+      <Real Name="Z">1191.0792416494639</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-107.8351761743983</Real>
+      <Real Name="Y">-595.60603383507896</Real>
+      <Real Name="Z">-1012.429697509874</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-59.739551494176006</Real>
+      <Real Name="Y">-74.788914038695822</Real>
+      <Real Name="Z">-271.3846935326406</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_transformAtoB_s.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_transformAtoB_s.xml
new file mode 100644 (file)
index 0000000..5f678b3
--- /dev/null
@@ -0,0 +1,938 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVremain/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-403.06406</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-391.75095</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-395.84671</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-399.49725</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-402.74741</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-423.06015</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-408.15778</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-410.32758</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-412.12503</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-413.54221</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-432.77591</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-415.29678</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-415.74673</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-416.04501</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-416.33514</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-434.73233</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-417.53702</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-418.79196</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-420.69351</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-423.36029</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-443.38919</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1422.9098</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1420.1342</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1415.5114</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1409.1486</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1401.2371</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1392.0459</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1381.9197</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1371.2649</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1360.51</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1350.0663</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1340.3165</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1331.563</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1324.0657</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1318.0005</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1313.4872</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1310.604</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1309.358</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1309.7142</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1311.5854</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1314.8431</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1319.3391</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">584.41461</Real>
+      <Real Name="Y">-802.96112</Real>
+      <Real Name="Z">-65.147827</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.04103</Real>
+      <Real Name="Y">-226.11559</Real>
+      <Real Name="Z">22.143492</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-431.26532</Real>
+      <Real Name="Y">297.40561</Real>
+      <Real Name="Z">-293.57727</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.076385</Real>
+      <Real Name="Y">152.77533</Real>
+      <Real Name="Z">102.69414</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">863.85315</Real>
+      <Real Name="Y">720.34546</Real>
+      <Real Name="Z">8.1655369</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.8206</Real>
+      <Real Name="Y">1948.3662</Real>
+      <Real Name="Z">-580.50934</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1317.3281</Real>
+      <Real Name="Y">-13.615286</Real>
+      <Real Name="Z">790.98242</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">336.63309</Real>
+      <Real Name="Y">-65.77504</Real>
+      <Real Name="Z">-441.96091</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">397.35587</Real>
+      <Real Name="Y">-205.45802</Real>
+      <Real Name="Z">59.208401</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">7.6542435</Real>
+      <Real Name="Y">-32.590561</Real>
+      <Real Name="Z">23.406075</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.8893852</Real>
+      <Real Name="Y">28.887316</Real>
+      <Real Name="Z">-14.140953</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.831089</Real>
+      <Real Name="Y">15.061724</Real>
+      <Real Name="Z">-5.2943878</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.955246</Real>
+      <Real Name="Y">20.54211</Real>
+      <Real Name="Z">-6.4628563</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.6558456</Real>
+      <Real Name="Y">-8.2111034</Real>
+      <Real Name="Z">4.6302338</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.6273079</Real>
+      <Real Name="Y">-23.495163</Real>
+      <Real Name="Z">4.0610199</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">365.04694</Real>
+      <Real Name="Y">-59.823116</Real>
+      <Real Name="Z">-670.57739</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-396.07428</Real>
+      <Real Name="Y">36.513245</Real>
+      <Real Name="Z">385.4545</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.67733</Real>
+      <Real Name="Y">-11.108765</Real>
+      <Real Name="Z">206.6451</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">360.70398</Real>
+      <Real Name="Y">403.9852</Real>
+      <Real Name="Z">-109.6192</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-34.834255</Real>
+      <Real Name="Y">-229.90448</Real>
+      <Real Name="Z">91.776199</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-346.59015</Real>
+      <Real Name="Y">-577.29645</Real>
+      <Real Name="Z">255.04054</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">648.42798</Real>
+      <Real Name="Y">-499.5563</Real>
+      <Real Name="Z">-423.80063</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.96693</Real>
+      <Real Name="Y">124.73186</Real>
+      <Real Name="Z">75.965889</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-648.4809</Real>
+      <Real Name="Y">443.36523</Real>
+      <Real Name="Z">220.66869</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-22.568874</Real>
+      <Real Name="Y">284.4483</Real>
+      <Real Name="Z">59.028786</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.99321</Real>
+      <Real Name="Y">-158.97961</Real>
+      <Real Name="Z">-107.52663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-114.12915</Real>
+      <Real Name="Y">-210.62267</Real>
+      <Real Name="Z">-135.84621</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-591.04944</Real>
+      <Real Name="Y">265.89801</Real>
+      <Real Name="Z">843.69922</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">397.41727</Real>
+      <Real Name="Y">-50.412521</Real>
+      <Real Name="Z">-873.99487</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">175.37456</Real>
+      <Real Name="Y">-188.69604</Real>
+      <Real Name="Z">-64.692955</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1111.3035</Real>
+      <Real Name="Y">594.14478</Real>
+      <Real Name="Z">856.00128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.55391</Real>
+      <Real Name="Y">-854.67511</Real>
+      <Real Name="Z">-360.19827</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-854.41614</Real>
+      <Real Name="Y">311.97308</Real>
+      <Real Name="Z">-577.37921</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">365.11642</Real>
+      <Real Name="Y">750.57745</Real>
+      <Real Name="Z">140.9953</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.023579</Real>
+      <Real Name="Y">-647.8786</Real>
+      <Real Name="Z">-51.309227</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-94.022064</Real>
+      <Real Name="Y">-129.98679</Real>
+      <Real Name="Z">-35.349117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">18.737085</Real>
+      <Real Name="Y">-179.0024</Real>
+      <Real Name="Z">-895.93408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.876175</Real>
+      <Real Name="Y">48.334332</Real>
+      <Real Name="Z">256.00256</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">388.31689</Real>
+      <Real Name="Y">0.60927582</Real>
+      <Real Name="Z">679.34576</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">116.96437</Real>
+      <Real Name="Y">43.420807</Real>
+      <Real Name="Z">-856.36536</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-260.24109</Real>
+      <Real Name="Y">163.23204</Real>
+      <Real Name="Z">480.82532</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">11.523792</Real>
+      <Real Name="Y">-195.1265</Real>
+      <Real Name="Z">165.66296</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-631.48853</Real>
+      <Real Name="Y">80.383057</Real>
+      <Real Name="Z">363.61957</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">543.80945</Real>
+      <Real Name="Y">-64.416908</Real>
+      <Real Name="Z">-9.8813734</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">138.08977</Real>
+      <Real Name="Y">-32.083717</Real>
+      <Real Name="Z">-246.8744</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.678711</Real>
+      <Real Name="Y">32.068558</Real>
+      <Real Name="Z">358.31329</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">243.59853</Real>
+      <Real Name="Y">-3.0031433</Real>
+      <Real Name="Z">-126.26546</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">56.17646</Real>
+      <Real Name="Y">-111.9758</Real>
+      <Real Name="Z">-113.33241</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-352.20108</Real>
+      <Real Name="Y">-2029.4865</Real>
+      <Real Name="Z">-705.51379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">339.3443</Real>
+      <Real Name="Y">1308.0884</Real>
+      <Real Name="Z">328.15576</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">279.62598</Real>
+      <Real Name="Y">404.54755</Real>
+      <Real Name="Z">71.580872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">462.95102</Real>
+      <Real Name="Y">1001.2958</Real>
+      <Real Name="Z">-826.12671</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-160.39987</Real>
+      <Real Name="Y">-535.86566</Real>
+      <Real Name="Z">224.32234</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-292.77533</Real>
+      <Real Name="Y">-395.1333</Real>
+      <Real Name="Z">539.08221</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">51.161449</Real>
+      <Real Name="Y">94.903313</Real>
+      <Real Name="Z">2.6117859</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.7436171</Real>
+      <Real Name="Y">-41.774796</Real>
+      <Real Name="Z">-2.0966578</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.306711</Real>
+      <Real Name="Y">-31.340946</Real>
+      <Real Name="Z">9.7635098</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-440.98587</Real>
+      <Real Name="Y">979.17871</Real>
+      <Real Name="Z">402.19061</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">607.75348</Real>
+      <Real Name="Y">-690.81219</Real>
+      <Real Name="Z">-456.20422</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">36.32579</Real>
+      <Real Name="Y">-156.70956</Real>
+      <Real Name="Z">-18.140755</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">219.72427</Real>
+      <Real Name="Y">-475.61902</Real>
+      <Real Name="Z">-1377.9955</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-64.437523</Real>
+      <Real Name="Y">337.8783</Real>
+      <Real Name="Z">1034.0143</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-343.41357</Real>
+      <Real Name="Y">21.754204</Real>
+      <Real Name="Z">210.56023</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">181.61285</Real>
+      <Real Name="Y">-126.06507</Real>
+      <Real Name="Z">-119.61913</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-86.481918</Real>
+      <Real Name="Y">127.05933</Real>
+      <Real Name="Z">74.182709</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-63.700058</Real>
+      <Real Name="Y">41.827347</Real>
+      <Real Name="Z">151.84262</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">330.80917</Real>
+      <Real Name="Y">-339.24884</Real>
+      <Real Name="Z">-71.749603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.72717</Real>
+      <Real Name="Y">414.64359</Real>
+      <Real Name="Z">95.911926</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.83241</Real>
+      <Real Name="Y">67.917625</Real>
+      <Real Name="Z">39.230751</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">802.73181</Real>
+      <Real Name="Y">-2492.8923</Real>
+      <Real Name="Z">-233.74968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-350.02612</Real>
+      <Real Name="Y">279.52823</Real>
+      <Real Name="Z">586.5528</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.8978</Real>
+      <Real Name="Y">656.92456</Real>
+      <Real Name="Z">99.879074</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-2.1851501</Real>
+      <Real Name="Y">-9.6826477</Real>
+      <Real Name="Z">4.3655548</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.68188667</Real>
+      <Real Name="Y">6.1742249</Real>
+      <Real Name="Z">-0.77758598</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.9508743</Real>
+      <Real Name="Y">4.2162399</Real>
+      <Real Name="Z">-4.2673473</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-386.76569</Real>
+      <Real Name="Y">850.50696</Real>
+      <Real Name="Z">232.90305</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">284.02548</Real>
+      <Real Name="Y">-481.91785</Real>
+      <Real Name="Z">-675.12195</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-318.26816</Real>
+      <Real Name="Y">-438.50137</Real>
+      <Real Name="Z">302.73517</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">371.11038</Real>
+      <Real Name="Y">26.880344</Real>
+      <Real Name="Z">-150.71854</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.08661</Real>
+      <Real Name="Y">60.54401</Real>
+      <Real Name="Z">39.595444</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.706451</Real>
+      <Real Name="Y">-35.96331</Real>
+      <Real Name="Z">73.54631</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-540.87262</Real>
+      <Real Name="Y">-1275.4509</Real>
+      <Real Name="Z">-297.15927</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">193.93759</Real>
+      <Real Name="Y">610.3783</Real>
+      <Real Name="Z">-292.53424</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.8681</Real>
+      <Real Name="Y">652.8302</Real>
+      <Real Name="Z">620.8515</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1196.6193</Real>
+      <Real Name="Y">443.25137</Real>
+      <Real Name="Z">-794.11902</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">820.79205</Real>
+      <Real Name="Y">-311.44965</Real>
+      <Real Name="Z">572.98718</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">185.64079</Real>
+      <Real Name="Y">-31.941507</Real>
+      <Real Name="Z">40.208893</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">219.60477</Real>
+      <Real Name="Y">428.50244</Real>
+      <Real Name="Z">165.8394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-30.096703</Real>
+      <Real Name="Y">-133.77853</Real>
+      <Real Name="Z">-72.055328</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-109.79527</Real>
+      <Real Name="Y">-123.7205</Real>
+      <Real Name="Z">-17.678581</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.5245</Real>
+      <Real Name="Y">-1076.177</Real>
+      <Real Name="Z">-857.81409</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.13316</Real>
+      <Real Name="Y">210.30899</Real>
+      <Real Name="Z">195.26335</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">576.12036</Real>
+      <Real Name="Y">692.23169</Real>
+      <Real Name="Z">681.14044</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.07437</Real>
+      <Real Name="Y">386.4057</Real>
+      <Real Name="Z">-1021.5668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-100.06026</Real>
+      <Real Name="Y">-316.06958</Real>
+      <Real Name="Z">752.56201</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.872387</Real>
+      <Real Name="Y">33.847389</Real>
+      <Real Name="Z">254.00476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-272.17114</Real>
+      <Real Name="Y">-514.18121</Real>
+      <Real Name="Z">-928.54303</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.56406</Real>
+      <Real Name="Y">80.39682</Real>
+      <Real Name="Z">145.66283</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.449856</Real>
+      <Real Name="Y">257.9881</Real>
+      <Real Name="Z">803.56024</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-601.33606</Real>
+      <Real Name="Y">-126.15092</Real>
+      <Real Name="Z">-114.83743</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">348.24927</Real>
+      <Real Name="Y">-14.943413</Real>
+      <Real Name="Z">80.182327</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">248.37558</Real>
+      <Real Name="Y">46.122959</Real>
+      <Real Name="Z">92.700134</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">601.93414</Real>
+      <Real Name="Y">825.61835</Real>
+      <Real Name="Z">-272.58258</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-127.05325</Real>
+      <Real Name="Y">-202.47385</Real>
+      <Real Name="Z">18.545692</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-469.16168</Real>
+      <Real Name="Y">-870.67175</Real>
+      <Real Name="Z">-32.411037</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1610.9838</Real>
+      <Real Name="Y">-227.84604</Real>
+      <Real Name="Z">825.68781</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-834.62445</Real>
+      <Real Name="Y">388.16418</Real>
+      <Real Name="Z">-612.35345</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-226.96977</Real>
+      <Real Name="Y">-150.77771</Real>
+      <Real Name="Z">-335.41248</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.27457</Real>
+      <Real Name="Y">55.063057</Real>
+      <Real Name="Z">125.46206</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.79619</Real>
+      <Real Name="Y">-162.44753</Real>
+      <Real Name="Z">29.881111</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">199.69812</Real>
+      <Real Name="Y">-7.1488819</Real>
+      <Real Name="Z">25.144703</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">870.4248</Real>
+      <Real Name="Y">-524.5437</Real>
+      <Real Name="Z">-75.504227</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-583.05798</Real>
+      <Real Name="Y">289.86148</Real>
+      <Real Name="Z">28.797047</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-459.29575</Real>
+      <Real Name="Y">400.02374</Real>
+      <Real Name="Z">-112.89407</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-127.82786</Real>
+      <Real Name="Y">-1826.7236</Real>
+      <Real Name="Z">33.69017</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">130.45145</Real>
+      <Real Name="Y">244.80392</Real>
+      <Real Name="Z">-32.721737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.39119</Real>
+      <Real Name="Y">1231.0259</Real>
+      <Real Name="Z">-57.344105</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">445.96216</Real>
+      <Real Name="Y">1039.7784</Real>
+      <Real Name="Z">855.36841</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-317.60645</Real>
+      <Real Name="Y">-647.88544</Real>
+      <Real Name="Z">-582.8385</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-61.928375</Real>
+      <Real Name="Y">-240.8606</Real>
+      <Real Name="Z">-227.89517</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">234.17003</Real>
+      <Real Name="Y">-384.6055</Real>
+      <Real Name="Z">-390.59793</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-250.68237</Real>
+      <Real Name="Y">369.66138</Real>
+      <Real Name="Z">210.31184</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.14937</Real>
+      <Real Name="Y">156.66057</Real>
+      <Real Name="Z">496.34451</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">771.93896</Real>
+      <Real Name="Y">-318.32712</Real>
+      <Real Name="Z">-231.63995</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-157.18983</Real>
+      <Real Name="Y">456.90533</Real>
+      <Real Name="Z">-1.1648636</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-740.93903</Real>
+      <Real Name="Y">-153.06035</Real>
+      <Real Name="Z">173.98909</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1098.9773</Real>
+      <Real Name="Y">1492.8296</Real>
+      <Real Name="Z">-674.09222</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">177.65401</Real>
+      <Real Name="Y">-208.44066</Real>
+      <Real Name="Z">93.963547</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">629.6748</Real>
+      <Real Name="Y">-958.65411</Real>
+      <Real Name="Z">443.76871</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">745.25464</Real>
+      <Real Name="Y">19.804726</Real>
+      <Real Name="Z">142.92282</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-164.38766</Real>
+      <Real Name="Y">96.777283</Real>
+      <Real Name="Z">-34.046444</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-917.00989</Real>
+      <Real Name="Y">124.65579</Real>
+      <Real Name="Z">-125.66254</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-782.88928</Real>
+      <Real Name="Y">-1239.0712</Real>
+      <Real Name="Z">108.79088</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">694.32874</Real>
+      <Real Name="Y">955.2027</Real>
+      <Real Name="Z">62.052364</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.431656</Real>
+      <Real Name="Y">254.97044</Real>
+      <Real Name="Z">-74.992645</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-128.04967</Real>
+      <Real Name="Y">-685.03027</Real>
+      <Real Name="Z">811.2326</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">145.04324</Real>
+      <Real Name="Y">499.45578</Real>
+      <Real Name="Z">-582.26172</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-53.174442</Real>
+      <Real Name="Y">73.683212</Real>
+      <Real Name="Z">-118.5256</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">53.911247</Real>
+      <Real Name="Y">-121.80061</Real>
+      <Real Name="Z">277.68085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.814289</Real>
+      <Real Name="Y">250.43936</Real>
+      <Real Name="Z">-49.252811</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-288.6904</Real>
+      <Real Name="Y">358.5105</Real>
+      <Real Name="Z">67.339531</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">81.419121</Real>
+      <Real Name="Y">482.95688</Real>
+      <Real Name="Z">-75.562988</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-48.232582</Real>
+      <Real Name="Y">-178.42497</Real>
+      <Real Name="Z">68.165749</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.255203</Real>
+      <Real Name="Y">-189.9726</Real>
+      <Real Name="Z">29.092224</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">77.860466</Real>
+      <Real Name="Y">773.81122</Real>
+      <Real Name="Z">73.097816</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-47.710613</Real>
+      <Real Name="Y">-170.2278</Real>
+      <Real Name="Z">-65.341125</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.475327</Real>
+      <Real Name="Y">-728.80267</Real>
+      <Real Name="Z">-10.852389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.064354</Real>
+      <Real Name="Y">-724.70319</Real>
+      <Real Name="Z">-206.62668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.87065</Real>
+      <Real Name="Y">627.07886</Real>
+      <Real Name="Z">110.28192</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">72.995796</Real>
+      <Real Name="Y">177.80307</Real>
+      <Real Name="Z">42.163574</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.35883</Real>
+      <Real Name="Y">925.59985</Real>
+      <Real Name="Z">804.7149</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">65.629074</Real>
+      <Real Name="Y">-180.51973</Real>
+      <Real Name="Z">-180.24982</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">117.49036</Real>
+      <Real Name="Y">-716.27698</Real>
+      <Real Name="Z">-492.81085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-288.58069</Real>
+      <Real Name="Y">312.54803</Real>
+      <Real Name="Z">-426.96436</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.86801</Real>
+      <Real Name="Y">-258.12112</Real>
+      <Real Name="Z">442.67175</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">146.57178</Real>
+      <Real Name="Y">-121.17542</Real>
+      <Real Name="Z">96.238281</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-876.81317</Real>
+      <Real Name="Y">-109.23676</Real>
+      <Real Name="Z">-58.80204</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">729.17047</Real>
+      <Real Name="Y">-55.104111</Real>
+      <Real Name="Z">5.5017052</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">309.92548</Real>
+      <Real Name="Y">52.747536</Real>
+      <Real Name="Z">6.5494308</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1513.4875</Real>
+      <Real Name="Y">-280.07446</Real>
+      <Real Name="Z">-655.00031</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">291.32529</Real>
+      <Real Name="Y">-38.160549</Real>
+      <Real Name="Z">209.88763</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1064.728</Real>
+      <Real Name="Y">346.75186</Real>
+      <Real Name="Z">334.37207</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-350.96021</Real>
+      <Real Name="Y">1444.6281</Real>
+      <Real Name="Z">296.73993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-7.0803518</Real>
+      <Real Name="Y">-732.56293</Real>
+      <Real Name="Z">-573.16449</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">342.02536</Real>
+      <Real Name="Y">-457.06354</Real>
+      <Real Name="Z">492.52759</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">202.0038</Real>
+      <Real Name="Y">437.12811</Real>
+      <Real Name="Z">127.20775</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-67.579971</Real>
+      <Real Name="Y">-123.24492</Real>
+      <Real Name="Z">-82.558266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-224.58459</Real>
+      <Real Name="Y">-494.57031</Real>
+      <Real Name="Z">-109.35741</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1158.7622</Real>
+      <Real Name="Y">-2026.2219</Real>
+      <Real Name="Z">670.73126</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-126.02495</Real>
+      <Real Name="Y">1240.259</Real>
+      <Real Name="Z">316.35974</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1027.479</Real>
+      <Real Name="Y">396.99591</Real>
+      <Real Name="Z">-803.00043</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">786.50818</Real>
+      <Real Name="Y">-1314.7819</Real>
+      <Real Name="Z">-296.77042</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-247.20352</Real>
+      <Real Name="Y">690.65558</Real>
+      <Real Name="Z">25.507927</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-500.82373</Real>
+      <Real Name="Y">707.1665</Real>
+      <Real Name="Z">322.86526</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">72.415321</Real>
+      <Real Name="Y">813.06281</Real>
+      <Real Name="Z">1328.6285</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-101.22188</Real>
+      <Real Name="Y">-713.35248</Real>
+      <Real Name="Z">-1006.7715</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.50671</Real>
+      <Real Name="Y">22.402899</Real>
+      <Real Name="Z">-244.71634</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_vdwalone_d.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_vdwalone_d.xml
new file mode 100644 (file)
index 0000000..3a7c8f0
--- /dev/null
@@ -0,0 +1,1826 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVremain/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-3.1579660938449625</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1.3188339027226335</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1.6902674141103669</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-2.065972216158511</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-2.4364635733905211</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-4.3398527356411449</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-3.1194658092876857</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-3.4042013082674414</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-3.6326936955919646</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-3.7937071042052284</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-5.7160414056179887</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-3.8891904319258441</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-3.8254887801389725</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-3.6971812947220655</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-3.5174690125367767</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-5.8137779498665916</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-3.0640599938031565</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-2.8225079022374975</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-2.58958906692069</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-2.3754573412968494</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-4.2074420039982288</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1342.197247453676</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1340.1193515354585</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1336.4419659194693</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1331.3324840628729</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1325.1262788268384</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1318.2667379995974</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1311.2390519530434</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1304.4879656284463</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1298.3435221437796</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1292.9810215843997</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1288.4325879034307</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1284.6440889509636</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1281.5505836671612</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1279.1436703540451</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1277.4969628840051</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1276.7434760175192</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1277.0073674605605</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1278.3267910826125</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1280.6001263613566</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1283.581956392816</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1286.9291987258705</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">560.16109862534142</Real>
+      <Real Name="Y">-775.81154927990303</Real>
+      <Real Name="Z">-164.17146970693486</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.64272674174074</Real>
+      <Real Name="Y">-235.30415713838252</Real>
+      <Real Name="Z">28.557614348936905</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-450.2787123936634</Real>
+      <Real Name="Y">281.00815490032716</Real>
+      <Real Name="Z">-214.39838573229162</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.051690558740631</Real>
+      <Real Name="Y">178.08587826965552</Real>
+      <Real Name="Z">107.30168062726091</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">745.8711316652732</Real>
+      <Real Name="Y">613.65429927310061</Real>
+      <Real Name="Z">-39.253085211301709</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.58337553351691</Real>
+      <Real Name="Y">184.72324556932995</Real>
+      <Real Name="Z">-230.00871215788808</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-828.07343321113592</Real>
+      <Real Name="Y">313.92592653244458</Real>
+      <Real Name="Z">1121.99114017946</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">38.102064875710141</Real>
+      <Real Name="Y">-203.46180744825858</Real>
+      <Real Name="Z">-499.97504860869992</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">447.39388750091888</Real>
+      <Real Name="Y">-341.65523061711485</Real>
+      <Real Name="Z">-146.87917408324716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">10.926011005091937</Real>
+      <Real Name="Y">-30.975674760252105</Real>
+      <Real Name="Z">20.381590937576163</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.0138701254607909</Real>
+      <Real Name="Y">27.856316975829493</Real>
+      <Real Name="Z">-12.695918110904778</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.2674211619326883</Real>
+      <Real Name="Y">14.475177408717883</Real>
+      <Real Name="Z">-4.0467350436091323</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.22538190930259816</Real>
+      <Real Name="Y">23.69679661203174</Real>
+      <Real Name="Z">-7.1117981649562125</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.8462065976892745</Real>
+      <Real Name="Y">-9.5171155293956566</Real>
+      <Real Name="Z">4.7696350465498654</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.3453596266597003</Real>
+      <Real Name="Y">-24.902907360873492</Real>
+      <Real Name="Z">4.5991521089141614</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.96295187530052</Real>
+      <Real Name="Y">-59.605635568030849</Real>
+      <Real Name="Z">-670.15721785214919</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.81279122705769</Real>
+      <Real Name="Y">36.20469042291834</Real>
+      <Real Name="Z">385.08925762579611</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.8215464435196651</Real>
+      <Real Name="Y">-11.298418225397187</Real>
+      <Real Name="Z">206.28479303022127</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">394.26569845477974</Real>
+      <Real Name="Y">432.23452105147396</Real>
+      <Real Name="Z">-97.824999888732904</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.85200933759662</Real>
+      <Real Name="Y">-238.51162915060058</Real>
+      <Real Name="Z">85.782972880969737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-370.93129038488684</Real>
+      <Real Name="Y">-600.14277237904264</Real>
+      <Real Name="Z">242.79153355662427</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">643.13334779716001</Real>
+      <Real Name="Y">-492.27256709661236</Real>
+      <Real Name="Z">-420.98431108358318</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.97384167216792</Real>
+      <Real Name="Y">121.35582381662744</Real>
+      <Real Name="Z">75.022959572071613</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.06277071622355</Real>
+      <Real Name="Y">438.33123420494866</Real>
+      <Real Name="Z">218.98937252178408</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.017448667151097</Real>
+      <Real Name="Y">295.74193246419281</Real>
+      <Real Name="Z">69.890484606494823</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.252333884320294</Real>
+      <Real Name="Y">-163.91312109375451</Real>
+      <Real Name="Z">-112.35407810341539</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.12078254896801</Real>
+      <Real Name="Y">-215.13020707664643</Real>
+      <Real Name="Z">-140.17665530901047</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-591.89696810874864</Real>
+      <Real Name="Y">297.62266322476302</Real>
+      <Real Name="Z">826.34279509007695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">397.93639892947715</Real>
+      <Real Name="Y">-70.314953968874306</Real>
+      <Real Name="Z">-868.24762476380488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.87258573868061</Real>
+      <Real Name="Y">-199.27650127733096</Real>
+      <Real Name="Z">-58.263933884350259</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1115.1482328008522</Real>
+      <Real Name="Y">629.9455901260186</Real>
+      <Real Name="Z">795.44682256751378</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.54644283236559</Real>
+      <Real Name="Y">-873.28714057360503</Real>
+      <Real Name="Z">-339.25254967654172</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-870.47703909844574</Real>
+      <Real Name="Y">286.55402923641446</Real>
+      <Real Name="Z">-544.52466870103444</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">357.06287427776584</Real>
+      <Real Name="Y">756.96075636090961</Real>
+      <Real Name="Z">146.15542885350052</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.1031911259369593</Real>
+      <Real Name="Y">-651.95807639425993</Real>
+      <Real Name="Z">-54.851563453446659</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-90.346160228676467</Real>
+      <Real Name="Y">-131.84844724474854</Real>
+      <Real Name="Z">-37.247322365720336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.40537172985966</Real>
+      <Real Name="Y">-138.54197376588451</Real>
+      <Real Name="Z">-868.91650756456249</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.014249513270272</Real>
+      <Real Name="Y">7.3692522628365786</Real>
+      <Real Name="Z">237.56345190659695</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">409.19492757353726</Real>
+      <Real Name="Y">-13.742542210541814</Real>
+      <Real Name="Z">674.31104622319117</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.695582918957427</Real>
+      <Real Name="Y">50.058915938401952</Real>
+      <Real Name="Z">-863.79031073954627</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-246.26232236317969</Real>
+      <Real Name="Y">161.94006229271525</Real>
+      <Real Name="Z">488.15427934452742</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">20.568406030418696</Real>
+      <Real Name="Y">-198.6892899661438</Real>
+      <Real Name="Z">167.75248290904577</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-614.61649596435848</Real>
+      <Real Name="Y">104.50707253816876</Real>
+      <Real Name="Z">354.02917735909625</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">537.6262257717641</Real>
+      <Real Name="Y">-78.613538027351382</Real>
+      <Real Name="Z">-4.2479072666175819</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.13435916894088</Real>
+      <Real Name="Y">-43.663731879204612</Real>
+      <Real Name="Z">-245.23655340698801</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-106.56956785769873</Real>
+      <Real Name="Y">29.625980741252619</Real>
+      <Real Name="Z">363.06226791298968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">246.89597123723604</Real>
+      <Real Name="Y">-1.2874127968376285</Real>
+      <Real Name="Z">-127.71291982473038</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">59.181669587130301</Real>
+      <Real Name="Y">-111.70268840757583</Real>
+      <Real Name="Z">-115.61203661582745</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-356.31511970426527</Real>
+      <Real Name="Y">-2083.3982890482171</Real>
+      <Real Name="Z">-695.05959915284905</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">338.53999378384719</Real>
+      <Real Name="Y">1335.4212804188292</Real>
+      <Real Name="Z">310.34795082844755</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">276.91145975674971</Real>
+      <Real Name="Y">426.71413088577714</Real>
+      <Real Name="Z">68.866527233272805</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">408.93032231238612</Real>
+      <Real Name="Y">978.77656784422391</Real>
+      <Real Name="Z">-851.54966909994505</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-141.87811546680328</Real>
+      <Real Name="Y">-531.63312668379092</Real>
+      <Real Name="Z">230.46019394509702</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-250.61603960596403</Real>
+      <Real Name="Y">-386.77706796128996</Real>
+      <Real Name="Z">568.34530693562147</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.995587717934299</Real>
+      <Real Name="Y">98.712841095074324</Real>
+      <Real Name="Z">5.9983147624240729</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.455554648307631</Real>
+      <Real Name="Y">-43.457716027921336</Real>
+      <Real Name="Z">-3.6230677277036509</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.238894805797361</Real>
+      <Real Name="Y">-33.183328509177713</Real>
+      <Real Name="Z">8.290184048624738</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-423.88543818284995</Real>
+      <Real Name="Y">988.97661246578377</Real>
+      <Real Name="Z">389.60272308055426</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">598.97332901184529</Real>
+      <Real Name="Y">-698.11664446353973</Real>
+      <Real Name="Z">-450.10730846376248</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.343645628273464</Real>
+      <Real Name="Y">-160.39853837320439</Real>
+      <Real Name="Z">-11.909701289496958</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.71991040684196</Real>
+      <Real Name="Y">-502.9306920338106</Real>
+      <Real Name="Z">-1442.6175600503611</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.961575921876957</Real>
+      <Real Name="Y">370.46015000533043</Real>
+      <Real Name="Z">1053.1544367911799</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-283.3786389780056</Real>
+      <Real Name="Y">14.028334421804736</Real>
+      <Real Name="Z">269.58718254533261</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.54500817120885</Real>
+      <Real Name="Y">-124.32060349534872</Real>
+      <Real Name="Z">-117.58242863217779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-85.882129825154834</Real>
+      <Real Name="Y">126.24593710256312</Real>
+      <Real Name="Z">72.835100905771725</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.93886218419312</Real>
+      <Real Name="Y">40.618534773196188</Real>
+      <Real Name="Z">150.70574056399448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.19553110118449</Real>
+      <Real Name="Y">-338.35470966050258</Real>
+      <Real Name="Z">-76.468324334614181</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.71627371711179</Real>
+      <Real Name="Y">414.14781220451323</Real>
+      <Real Name="Z">98.183831527550083</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.25066405747654</Real>
+      <Real Name="Y">67.408305033815282</Real>
+      <Real Name="Z">41.300523465622632</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">724.01347428416511</Real>
+      <Real Name="Y">-249.7084343919052</Real>
+      <Real Name="Z">-584.33777573684335</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.00030760178515</Real>
+      <Real Name="Y">205.71913222890811</Real>
+      <Real Name="Z">640.11714131550389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-247.02309440129915</Real>
+      <Real Name="Y">324.86740306678087</Real>
+      <Real Name="Z">148.15237130971008</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.68702139968868892</Real>
+      <Real Name="Y">-10.918295331211866</Real>
+      <Real Name="Z">9.5570140012964231</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.52749685515944478</Real>
+      <Real Name="Y">6.9939843698716828</Real>
+      <Real Name="Z">-2.9467326595193413</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.7972037103186267</Real>
+      <Real Name="Y">4.938949961419226</Real>
+      <Real Name="Z">-7.0645942266187021</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-407.11247041480487</Real>
+      <Real Name="Y">846.24469916768385</Real>
+      <Real Name="Z">252.9735052904079</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">291.08358489735321</Real>
+      <Real Name="Y">-480.01846139005033</Real>
+      <Real Name="Z">-680.65370051849561</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.95701713939837</Real>
+      <Real Name="Y">-441.64736765089043</Real>
+      <Real Name="Z">290.35016275333317</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.52710645366994</Real>
+      <Real Name="Y">32.475232520892938</Real>
+      <Real Name="Z">-151.8656151387286</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.64908440468173</Real>
+      <Real Name="Y">57.971079189980131</Real>
+      <Real Name="Z">40.011441768176269</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.314549130369073</Real>
+      <Real Name="Y">-39.18358876805712</Real>
+      <Real Name="Z">74.100816205379033</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-596.51912113731839</Real>
+      <Real Name="Y">-1291.9374813883123</Real>
+      <Real Name="Z">-303.77597114752854</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">218.88063888924594</Real>
+      <Real Name="Y">615.97043682708647</Real>
+      <Real Name="Z">-293.46806381886199</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">393.941095378147</Real>
+      <Real Name="Y">650.36246860577478</Real>
+      <Real Name="Z">617.0186070104761</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1193.5625541953978</Real>
+      <Real Name="Y">443.09774155104782</Real>
+      <Real Name="Z">-792.07554408218812</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">819.54805096295104</Real>
+      <Real Name="Y">-311.5272381428573</Real>
+      <Real Name="Z">572.28102205107109</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.21525938176103</Real>
+      <Real Name="Y">-31.884059360249722</Real>
+      <Real Name="Z">38.946004715384362</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">216.15807377313536</Real>
+      <Real Name="Y">430.4807775762564</Real>
+      <Real Name="Z">164.33810312346728</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.186488846853734</Real>
+      <Real Name="Y">-134.86982156614835</Real>
+      <Real Name="Z">-70.851499829831525</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-108.02250844410699</Real>
+      <Real Name="Y">-124.21383949030434</Real>
+      <Real Name="Z">-17.182284298308229</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.2102139643716</Real>
+      <Real Name="Y">-1074.0600988861561</Real>
+      <Real Name="Z">-860.12040003988045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.09631351027141</Real>
+      <Real Name="Y">209.20112844331436</Real>
+      <Real Name="Z">196.16055872121922</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">575.92938929566276</Real>
+      <Real Name="Y">691.03595203238672</Real>
+      <Real Name="Z">682.34735623050392</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.87293238339231</Real>
+      <Real Name="Y">390.16563378138687</Real>
+      <Real Name="Z">-1022.8807635907668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.946766957038406</Real>
+      <Real Name="Y">-318.27966099818241</Real>
+      <Real Name="Z">753.25575310306294</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.999201433446238</Real>
+      <Real Name="Y">32.162087183582138</Real>
+      <Real Name="Z">254.53780749288188</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.83319417526474</Real>
+      <Real Name="Y">-511.42160111938347</Real>
+      <Real Name="Z">-930.7545916378549</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.2684316090332</Real>
+      <Real Name="Y">79.126072631981458</Real>
+      <Real Name="Z">146.98848078531603</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.37241768883009</Real>
+      <Real Name="Y">256.5071378774756</Real>
+      <Real Name="Z">804.46725926185024</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-438.81442732591785</Real>
+      <Real Name="Y">-223.37086797446409</Real>
+      <Real Name="Z">-199.19111769232842</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">224.94665536147181</Real>
+      <Real Name="Y">60.397394961607503</Real>
+      <Real Name="Z">145.17132426402827</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">225.78108849706797</Real>
+      <Real Name="Y">77.066302993338425</Real>
+      <Real Name="Z">123.62725603169116</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">568.84046321630808</Real>
+      <Real Name="Y">813.6707590409867</Real>
+      <Real Name="Z">-260.60800316496653</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.33249425656366</Real>
+      <Real Name="Y">-198.43558046463738</Real>
+      <Real Name="Z">15.699489687745341</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.96701168161371</Real>
+      <Real Name="Y">-861.01786705400093</Real>
+      <Real Name="Z">-32.84708944114638</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1744.7418997631353</Real>
+      <Real Name="Y">-146.70697102950987</Real>
+      <Real Name="Z">947.75900398970748</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-863.39841375327842</Real>
+      <Real Name="Y">303.0700408385091</Real>
+      <Real Name="Z">-704.97142507446495</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.04914583004569</Real>
+      <Real Name="Y">-176.10333275396232</Real>
+      <Real Name="Z">-382.00197588191077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-153.51265365766068</Real>
+      <Real Name="Y">55.682711632940524</Real>
+      <Real Name="Z">125.95119344694072</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.04454259918046</Real>
+      <Real Name="Y">-162.75546905473379</Real>
+      <Real Name="Z">29.863997850178237</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.87969745224927</Real>
+      <Real Name="Y">-7.5572463100664482</Real>
+      <Real Name="Z">25.019076372669105</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">687.49177290234445</Real>
+      <Real Name="Y">-345.39256185766749</Real>
+      <Real Name="Z">42.986320614335249</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-478.77452411476037</Real>
+      <Real Name="Y">292.29483850177564</Real>
+      <Real Name="Z">25.483874925481288</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.64533271491956</Real>
+      <Real Name="Y">328.12365259750294</Real>
+      <Real Name="Z">-128.69673281129479</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.70324301278686</Real>
+      <Real Name="Y">-1811.1192698304044</Real>
+      <Real Name="Z">50.019779163536057</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">128.98055318112472</Real>
+      <Real Name="Y">237.2512769076682</Real>
+      <Real Name="Z">-41.16764066459227</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.38657318257609</Real>
+      <Real Name="Y">1224.5307775252847</Real>
+      <Real Name="Z">-63.083839118256932</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.37111950764688</Real>
+      <Real Name="Y">1028.9331929888397</Real>
+      <Real Name="Z">825.75265899818669</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.67824706426148</Real>
+      <Real Name="Y">-646.08000387039488</Real>
+      <Real Name="Z">-555.45222920819947</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.18806432884773</Real>
+      <Real Name="Y">-238.70602173311332</Real>
+      <Real Name="Z">-216.718232485985</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-54.444156335888458</Real>
+      <Real Name="Y">-447.62858839160384</Real>
+      <Real Name="Z">-500.97961250070557</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-158.08391737849212</Real>
+      <Real Name="Y">376.09770431398573</Real>
+      <Real Name="Z">254.38054752941696</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-93.630378338001904</Real>
+      <Real Name="Y">161.07479823713899</Real>
+      <Real Name="Z">477.56914648109358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">846.2526526292047</Real>
+      <Real Name="Y">-284.14095703480962</Real>
+      <Real Name="Z">-279.09587721787977</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-199.05726966148299</Real>
+      <Real Name="Y">452.46098622046281</Real>
+      <Real Name="Z">22.097044488990051</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-761.22769586733045</Real>
+      <Real Name="Y">-163.09510880542263</Real>
+      <Real Name="Z">188.53999117987485</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1087.0110809564792</Real>
+      <Real Name="Y">1521.079676070588</Real>
+      <Real Name="Z">-673.28465241401034</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.6615627106128</Real>
+      <Real Name="Y">-219.61038108924546</Real>
+      <Real Name="Z">95.491183339214487</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">620.03307835052578</Real>
+      <Real Name="Y">-974.0329995458751</Real>
+      <Real Name="Z">441.57011906625632</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.21457639064135</Real>
+      <Real Name="Y">29.395307239730116</Real>
+      <Real Name="Z">149.52853766933009</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.68534657016986</Real>
+      <Real Name="Y">92.883491557986673</Real>
+      <Real Name="Z">-36.451953545999842</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-914.89867906860502</Real>
+      <Real Name="Y">119.96913372915684</Real>
+      <Real Name="Z">-128.94440853053979</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-822.59109399043757</Real>
+      <Real Name="Y">-1220.8198047055221</Real>
+      <Real Name="Z">114.17475795055465</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">707.68357775193681</Real>
+      <Real Name="Y">941.3378086730138</Real>
+      <Real Name="Z">60.08123952200971</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">68.118872494929988</Real>
+      <Real Name="Y">251.30475317549264</Real>
+      <Real Name="Z">-76.473949582558873</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-136.64797125834735</Real>
+      <Real Name="Y">-697.22767202924115</Real>
+      <Real Name="Z">809.20019433069569</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.89338735627115</Real>
+      <Real Name="Y">509.83954744747388</Real>
+      <Real Name="Z">-581.41940130466764</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.67823961465966</Real>
+      <Real Name="Y">79.157760754683295</Real>
+      <Real Name="Z">-119.01844882242128</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">69.196763862633276</Real>
+      <Real Name="Y">-148.69455755724553</Real>
+      <Real Name="Z">341.95382208154984</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.625517994538882</Real>
+      <Real Name="Y">255.32914403115907</Real>
+      <Real Name="Z">-74.753944992517532</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.06049523514946</Real>
+      <Real Name="Y">369.96027004780274</Real>
+      <Real Name="Z">26.579877362345343</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">82.727159772725827</Real>
+      <Real Name="Y">465.92211322597603</Real>
+      <Real Name="Z">-66.921921163068021</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.497384168902684</Real>
+      <Real Name="Y">-170.16035372964217</Real>
+      <Real Name="Z">64.422695946654542</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.735839783873971</Real>
+      <Real Name="Y">-177.85511618815502</Real>
+      <Real Name="Z">21.9668699820024</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.752763378481063</Real>
+      <Real Name="Y">766.99244057673332</Real>
+      <Real Name="Z">75.478084396688757</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.58821232340248</Real>
+      <Real Name="Y">-166.13191453869467</Real>
+      <Real Name="Z">-66.48763753035945</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.568875992909028</Real>
+      <Real Name="Y">-724.08089225765798</Real>
+      <Real Name="Z">-12.754418581012846</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.510695019133635</Real>
+      <Real Name="Y">-723.00308685261155</Real>
+      <Real Name="Z">-210.45500936138853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.88143271417943</Real>
+      <Real Name="Y">626.18311519882616</Real>
+      <Real Name="Z">111.89769239385279</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.520329053469681</Real>
+      <Real Name="Y">176.58493843761451</Real>
+      <Real Name="Z">44.482642739153206</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">89.220002782860874</Real>
+      <Real Name="Y">786.17479923195265</Real>
+      <Real Name="Z">544.71531504238772</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.387112705658673</Real>
+      <Real Name="Y">-180.11031922451212</Real>
+      <Real Name="Z">-82.721588495514993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">139.17770138934267</Real>
+      <Real Name="Y">-585.66213346215727</Real>
+      <Real Name="Z">-443.16657208056012</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.15315723505114</Real>
+      <Real Name="Y">299.40875575576933</Real>
+      <Real Name="Z">-420.1735110720706</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">164.5557151620923</Real>
+      <Real Name="Y">-252.18778117176723</Real>
+      <Real Name="Z">436.97612822774579</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.03195801634098</Real>
+      <Real Name="Y">-111.08763640750979</Real>
+      <Real Name="Z">92.585505294879098</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-885.45399583779442</Real>
+      <Real Name="Y">-83.701414975767506</Real>
+      <Real Name="Z">-57.059826269920222</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.8127298383738</Real>
+      <Real Name="Y">-69.788124065908164</Real>
+      <Real Name="Z">3.6633299646789794</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">312.90062775162704</Real>
+      <Real Name="Y">42.177833602993232</Real>
+      <Real Name="Z">7.3131809492190882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1533.2174532923866</Real>
+      <Real Name="Y">-268.07907076731135</Real>
+      <Real Name="Z">-639.8744507052495</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">298.84618125819253</Real>
+      <Real Name="Y">-41.605345118276801</Real>
+      <Real Name="Z">203.82151757771993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1071.9022296426933</Real>
+      <Real Name="Y">337.55449647805744</Real>
+      <Real Name="Z">324.01417532920465</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-413.9401068351475</Real>
+      <Real Name="Y">1396.3547892488816</Real>
+      <Real Name="Z">325.87579171481752</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.270992502602493</Real>
+      <Real Name="Y">-723.13253896504341</Real>
+      <Real Name="Z">-590.00231110063214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">383.87370665683045</Real>
+      <Real Name="Y">-438.32728665511968</Real>
+      <Real Name="Z">485.93865538869841</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.20567418801926</Real>
+      <Real Name="Y">440.55155433582911</Real>
+      <Real Name="Z">126.63485402593076</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.498271116675255</Real>
+      <Real Name="Y">-125.11100292886805</Real>
+      <Real Name="Z">-82.322492614618866</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-223.47859284693843</Real>
+      <Real Name="Y">-496.10913637041182</Real>
+      <Real Name="Z">-108.89657536146126</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1155.9963971115176</Real>
+      <Real Name="Y">-1972.1948842758138</Real>
+      <Real Name="Z">678.15496418835085</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-122.94929937397528</Real>
+      <Real Name="Y">1194.1370171101557</Real>
+      <Real Name="Z">314.51716430902258</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1030.773302548535</Real>
+      <Real Name="Y">372.63604053007759</Real>
+      <Real Name="Z">-811.24852228744578</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">776.89054542695987</Real>
+      <Real Name="Y">-1294.7887769639517</Real>
+      <Real Name="Z">-289.80436237148166</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-239.76900063202052</Real>
+      <Real Name="Y">679.80430081036627</Real>
+      <Real Name="Z">20.614068686324089</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.5961873087308</Real>
+      <Real Name="Y">693.43540616696725</Real>
+      <Real Name="Z">319.41442387566809</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">85.911550216144121</Real>
+      <Real Name="Y">843.02166321267885</Real>
+      <Real Name="Z">1349.7603817656045</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-105.20094992303544</Real>
+      <Real Name="Y">-723.44706372341761</Real>
+      <Real Name="Z">-1015.3103937742756</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.93788989090284</Real>
+      <Real Name="Y">7.4760387114040654</Real>
+      <Real Name="Z">-260.68171216377664</Real>
+    </Vector>
+  </Sequence>
+  <Sequence Name="Time 0.020000 Step 20 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">-1113.4847436359596</Real>
+      <Real Name="Y">490.52424384248138</Real>
+      <Real Name="Z">1355.2302976944552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.099185377368372</Real>
+      <Real Name="Y">-116.38100785485305</Real>
+      <Real Name="Z">-44.211254027749305</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">395.48078574505507</Real>
+      <Real Name="Y">-73.286573431845326</Real>
+      <Real Name="Z">-176.52611899134902</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">341.34863027442998</Real>
+      <Real Name="Y">-463.87211988912486</Real>
+      <Real Name="Z">-606.6183617237873</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1046.8688172221528</Real>
+      <Real Name="Y">408.63556380314662</Real>
+      <Real Name="Z">-743.66873233174499</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-565.55772427493912</Real>
+      <Real Name="Y">-390.84509244265632</Real>
+      <Real Name="Z">48.473945324922099</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-89.800422256815153</Real>
+      <Real Name="Y">652.72742722039209</Real>
+      <Real Name="Z">125.11400843995425</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.73571380575771</Real>
+      <Real Name="Y">-130.92631886297235</Real>
+      <Real Name="Z">42.844902882033779</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-188.84604938343443</Real>
+      <Real Name="Y">-366.97211185868861</Real>
+      <Real Name="Z">-47.755363261860531</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.0315146735157157</Real>
+      <Real Name="Y">3.0197332943152091</Real>
+      <Real Name="Z">15.448303546506054</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">6.0038691139369575</Real>
+      <Real Name="Y">7.3055269912802512</Real>
+      <Real Name="Z">-2.6034536730857951</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-8.9336266464357976</Real>
+      <Real Name="Y">1.5120450259579803</Real>
+      <Real Name="Z">-8.9283074728476386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.085687526164492</Real>
+      <Real Name="Y">10.592166877844079</Real>
+      <Real Name="Z">-26.08100420741826</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.1002618140890519</Real>
+      <Real Name="Y">-3.9269558863386784</Real>
+      <Real Name="Z">12.462889569392903</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.452090004205999</Real>
+      <Real Name="Y">-14.801748091156055</Real>
+      <Real Name="Z">11.369448393865774</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">287.05352572708642</Real>
+      <Real Name="Y">-118.94851195394074</Real>
+      <Real Name="Z">-750.15731966041562</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-291.69098581488083</Real>
+      <Real Name="Y">200.32571245774599</Real>
+      <Real Name="Z">399.58894952773568</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">33.916899038240224</Real>
+      <Real Name="Y">9.6649994857309647</Real>
+      <Real Name="Z">176.62227522632449</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">447.15662485425264</Real>
+      <Real Name="Y">663.25691993747</Real>
+      <Real Name="Z">-193.51323821457828</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.99268242528683</Real>
+      <Real Name="Y">-262.48167546707145</Real>
+      <Real Name="Z">64.750241537177004</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-400.53118029139921</Real>
+      <Real Name="Y">-633.46228095086872</Real>
+      <Real Name="Z">276.30541906433024</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">431.12808093925906</Real>
+      <Real Name="Y">-415.77504014869282</Real>
+      <Real Name="Z">-477.94145304613608</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.698446347353197</Real>
+      <Real Name="Y">142.4789679640395</Real>
+      <Real Name="Z">123.36700633918025</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-451.22092721364379</Real>
+      <Real Name="Y">415.44467455710594</Real>
+      <Real Name="Z">453.05289370971099</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">120.89432218029242</Real>
+      <Real Name="Y">221.8895104665705</Real>
+      <Real Name="Z">289.34871698637301</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-11.10164153580326</Real>
+      <Real Name="Y">-163.28034686493541</Real>
+      <Real Name="Z">-137.97329786248957</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-185.18173413390224</Real>
+      <Real Name="Y">-242.15321024652587</Real>
+      <Real Name="Z">-149.34028380262663</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-436.29216959766268</Real>
+      <Real Name="Y">385.64222609360206</Real>
+      <Real Name="Z">865.45435011936092</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">402.73686924635786</Real>
+      <Real Name="Y">-253.5826453522503</Real>
+      <Real Name="Z">-822.31166722039336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.20185151293606</Real>
+      <Real Name="Y">-232.33324702104099</Real>
+      <Real Name="Z">-111.77677964238148</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1269.0233954951361</Real>
+      <Real Name="Y">339.17866888696193</Real>
+      <Real Name="Z">813.61276246086095</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">11.470759327965302</Real>
+      <Real Name="Y">-528.83013187174197</Real>
+      <Real Name="Z">-199.91083173113907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-955.00305864822383</Real>
+      <Real Name="Y">512.88083354430637</Real>
+      <Real Name="Z">-531.05485609056393</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">258.96861708269705</Real>
+      <Real Name="Y">1009.4980585158538</Real>
+      <Real Name="Z">175.98730846139227</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-83.909676774704266</Real>
+      <Real Name="Y">-372.87567383689111</Real>
+      <Real Name="Z">-55.45394191355247</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-98.376017083742283</Real>
+      <Real Name="Y">-183.29411252797988</Real>
+      <Real Name="Z">-58.701871478761078</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-145.86906259492309</Real>
+      <Real Name="Y">39.440499608028105</Real>
+      <Real Name="Z">-982.54957795172186</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-109.09728589580553</Real>
+      <Real Name="Y">238.63669041262258</Real>
+      <Real Name="Z">292.86648060707529</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">320.76311165313683</Real>
+      <Real Name="Y">125.43096262204153</Real>
+      <Real Name="Z">315.1634324601709</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">491.31074035069327</Real>
+      <Real Name="Y">215.90089125247837</Real>
+      <Real Name="Z">-398.02085543709626</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-484.9649926588192</Real>
+      <Real Name="Y">66.931807829474195</Real>
+      <Real Name="Z">647.87339672648659</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-174.30737621505793</Real>
+      <Real Name="Y">-227.64305953310532</Real>
+      <Real Name="Z">52.122161357846529</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-957.09428283645696</Real>
+      <Real Name="Y">189.88492104075848</Real>
+      <Real Name="Z">264.39201915850282</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">613.47070436854801</Real>
+      <Real Name="Y">-320.63439941331114</Real>
+      <Real Name="Z">-147.37841649637798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">168.39559594709752</Real>
+      <Real Name="Y">-5.8758024451637176</Real>
+      <Real Name="Z">-176.25194434049124</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-422.35415629921965</Real>
+      <Real Name="Y">83.562450621832824</Real>
+      <Real Name="Z">277.94488996907751</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">147.5757935377834</Real>
+      <Real Name="Y">-1.4091775042987962</Real>
+      <Real Name="Z">-53.848631412829185</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">155.80251045738407</Real>
+      <Real Name="Y">-193.75556728264746</Real>
+      <Real Name="Z">-98.568840257107098</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-166.8062048643757</Real>
+      <Real Name="Y">-1462.172395712686</Real>
+      <Real Name="Z">-714.31906310920533</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">48.246077036413965</Real>
+      <Real Name="Y">1359.943115111731</Real>
+      <Real Name="Z">473.36013057243787</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">382.50044023230919</Real>
+      <Real Name="Y">382.82311359943066</Real>
+      <Real Name="Z">141.83182122209911</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">426.42318669426828</Real>
+      <Real Name="Y">742.24262443577277</Real>
+      <Real Name="Z">-878.37265298653244</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">159.32545412616085</Real>
+      <Real Name="Y">-450.24770195060484</Real>
+      <Real Name="Z">20.990621507588706</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-392.88570948805409</Real>
+      <Real Name="Y">-157.82558865838385</Real>
+      <Real Name="Z">286.27109912065833</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">68.085214918389369</Real>
+      <Real Name="Y">129.47823613943359</Real>
+      <Real Name="Z">36.644801003619222</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-16.136202717733472</Real>
+      <Real Name="Y">-52.910897580776833</Real>
+      <Real Name="Z">-14.635851809248058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-31.688798703955918</Real>
+      <Real Name="Y">-43.574260894488376</Real>
+      <Real Name="Z">-13.450730161815699</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-358.77492971468519</Real>
+      <Real Name="Y">928.23166611961324</Real>
+      <Real Name="Z">236.69696781277264</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">446.72773491909084</Real>
+      <Real Name="Y">-699.17957380457483</Real>
+      <Real Name="Z">-413.98160877024782</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">18.835117534480524</Real>
+      <Real Name="Y">-158.22428322362364</Real>
+      <Real Name="Z">-22.030086561494389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">509.66221830097459</Real>
+      <Real Name="Y">-708.90474405533803</Real>
+      <Real Name="Z">-1253.6440098971723</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-394.91174842769016</Real>
+      <Real Name="Y">289.89428727340447</Real>
+      <Real Name="Z">1001.4076263176096</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-361.16904964128935</Real>
+      <Real Name="Y">170.71621519464315</Real>
+      <Real Name="Z">93.266536805103271</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">39.875195692098316</Real>
+      <Real Name="Y">-258.4118241282485</Real>
+      <Real Name="Z">28.406682563333803</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-25.473006881933813</Real>
+      <Real Name="Y">113.59854775110938</Real>
+      <Real Name="Z">34.495601282770785</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-37.661540435431689</Real>
+      <Real Name="Y">65.752040162671946</Real>
+      <Real Name="Z">128.08489647233819</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">333.94002148719568</Real>
+      <Real Name="Y">-304.45811442205314</Real>
+      <Real Name="Z">-101.48875177726441</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-307.56339615412958</Real>
+      <Real Name="Y">375.36911232066478</Real>
+      <Real Name="Z">157.53607232067355</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-89.065246062735454</Real>
+      <Real Name="Y">45.212197386090871</Real>
+      <Real Name="Z">55.957795473987545</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">698.37240074541728</Real>
+      <Real Name="Y">-498.0062313159537</Real>
+      <Real Name="Z">-670.3675476150114</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-450.77977569433637</Real>
+      <Real Name="Y">172.79489875794644</Real>
+      <Real Name="Z">774.70581762502411</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-183.32672109162331</Real>
+      <Real Name="Y">279.15478688023569</Real>
+      <Real Name="Z">223.53280019399142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">8.5298043991809394</Real>
+      <Real Name="Y">-12.292531428173341</Real>
+      <Real Name="Z">29.470537634136264</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-3.1429310088351059</Real>
+      <Real Name="Y">6.1700068108290784</Real>
+      <Real Name="Z">-10.94172285373952</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.37516149981994573</Real>
+      <Real Name="Y">6.5897063275257484</Real>
+      <Real Name="Z">-16.957060901067877</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-149.33699284189507</Real>
+      <Real Name="Y">785.16611806616129</Real>
+      <Real Name="Z">119.81730924350329</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">473.53406943649401</Real>
+      <Real Name="Y">-384.29437948740303</Real>
+      <Real Name="Z">-525.13092880913712</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-207.07893121383952</Real>
+      <Real Name="Y">-352.05706466192538</Real>
+      <Real Name="Z">300.68353970616778</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">341.08537625579271</Real>
+      <Real Name="Y">47.908776715107678</Real>
+      <Real Name="Z">-317.3299296850675</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.96468317956698</Real>
+      <Real Name="Y">44.87021478216645</Real>
+      <Real Name="Z">81.600449050847715</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-80.095427457868865</Real>
+      <Real Name="Y">-21.387842970709698</Real>
+      <Real Name="Z">103.90048194798266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-422.45416832026069</Real>
+      <Real Name="Y">-1049.9883428448716</Real>
+      <Real Name="Z">-498.11433035109724</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.63224559146369</Real>
+      <Real Name="Y">328.15104913312484</Real>
+      <Real Name="Z">-157.01192660300174</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">398.33632271643717</Real>
+      <Real Name="Y">701.13596253055425</Real>
+      <Real Name="Z">654.60393124367579</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-860.95910388038601</Real>
+      <Real Name="Y">327.9276107612792</Real>
+      <Real Name="Z">-507.17771723389666</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">683.63576003852643</Real>
+      <Real Name="Y">-269.40815542070277</Real>
+      <Real Name="Z">483.93193953901533</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">170.02747956872003</Real>
+      <Real Name="Y">-0.77792975685233046</Real>
+      <Real Name="Z">32.262296664403067</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">255.47011914365842</Real>
+      <Real Name="Y">425.21975175477365</Real>
+      <Real Name="Z">75.909484287000566</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.061819250585209</Real>
+      <Real Name="Y">-154.38656166002662</Real>
+      <Real Name="Z">-61.168895600763783</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-122.44278130518948</Real>
+      <Real Name="Y">-150.9322586738364</Real>
+      <Real Name="Z">12.699161904200396</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-819.58276605515721</Real>
+      <Real Name="Y">-792.50382578828328</Real>
+      <Real Name="Z">-523.06823010148798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.15809496529766</Real>
+      <Real Name="Y">199.4058711822708</Real>
+      <Real Name="Z">154.76248340737058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">704.55143900434439</Real>
+      <Real Name="Y">520.79313303600884</Real>
+      <Real Name="Z">397.62616337637064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.901678881353092</Real>
+      <Real Name="Y">-73.438330753811428</Real>
+      <Real Name="Z">-784.31051739726513</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">13.816891128185135</Real>
+      <Real Name="Y">-195.82312393611568</Real>
+      <Real Name="Z">726.13063967061305</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">62.914298773610632</Real>
+      <Real Name="Y">57.061170302050385</Real>
+      <Real Name="Z">154.27336781707183</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-536.68356361773454</Real>
+      <Real Name="Y">-517.71720787231482</Real>
+      <Real Name="Z">-919.03218259924438</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">125.98278669476781</Real>
+      <Real Name="Y">75.759654739822281</Real>
+      <Real Name="Z">158.29467985241305</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">242.35305165889631</Real>
+      <Real Name="Y">493.17543973448818</Real>
+      <Real Name="Z">742.32250188781074</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-429.93453781018655</Real>
+      <Real Name="Y">-480.26881083567139</Real>
+      <Real Name="Z">-95.892009171350196</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">321.91127362856389</Real>
+      <Real Name="Y">218.68034063172271</Real>
+      <Real Name="Z">19.622472858591635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">242.21148038358254</Real>
+      <Real Name="Y">152.21056209861294</Real>
+      <Real Name="Z">39.639248545714089</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">132.89161960789926</Real>
+      <Real Name="Y">779.633441322772</Real>
+      <Real Name="Z">-15.222268548296455</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.6338260947957792</Real>
+      <Real Name="Y">-158.1015228582211</Real>
+      <Real Name="Z">-10.049434764304522</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-200.91128094056637</Real>
+      <Real Name="Y">-817.50230189826664</Real>
+      <Real Name="Z">-130.51863571419824</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1865.892930053034</Real>
+      <Real Name="Y">-170.92108907568755</Real>
+      <Real Name="Z">838.88775745996554</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1042.6556902887573</Real>
+      <Real Name="Y">247.92604862499331</Real>
+      <Real Name="Z">-637.33436496769446</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-431.70303690150877</Real>
+      <Real Name="Y">-186.1766026767074</Real>
+      <Real Name="Z">-377.40815943906773</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-302.99142730334631</Real>
+      <Real Name="Y">68.520610953156094</Real>
+      <Real Name="Z">-99.620259167680416</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.82342580650015</Real>
+      <Real Name="Y">-135.48949754482152</Real>
+      <Real Name="Z">53.795883880667979</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.87401918187049</Real>
+      <Real Name="Y">-5.422599510601831</Real>
+      <Real Name="Z">38.070138148949901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">749.65205756266698</Real>
+      <Real Name="Y">-223.69187342540943</Real>
+      <Real Name="Z">128.38759301642918</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-674.84830390189666</Real>
+      <Real Name="Y">249.29390836812564</Real>
+      <Real Name="Z">52.423175424907235</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-337.91866613264733</Real>
+      <Real Name="Y">164.81253934563011</Real>
+      <Real Name="Z">-91.87282866784156</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-202.29260217606748</Real>
+      <Real Name="Y">-1131.7416809768995</Real>
+      <Real Name="Z">6.0762963563050363</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">91.622857634239793</Real>
+      <Real Name="Y">170.41734511845002</Real>
+      <Real Name="Z">-45.652802693796389</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">144.91890345652601</Real>
+      <Real Name="Y">1116.8239151285727</Real>
+      <Real Name="Z">32.714584604756169</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">223.22696396906159</Real>
+      <Real Name="Y">1077.3747137147275</Real>
+      <Real Name="Z">766.28411785953608</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-180.47116264611307</Real>
+      <Real Name="Y">-648.25520443560526</Real>
+      <Real Name="Z">-636.2898341230308</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.77102999472325</Real>
+      <Real Name="Y">-287.91063328694707</Real>
+      <Real Name="Z">-125.18672872556462</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-98.031208675572984</Real>
+      <Real Name="Y">-408.12026159339706</Real>
+      <Real Name="Z">-375.42187667791433</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-95.848299958430019</Real>
+      <Real Name="Y">333.39070818039181</Real>
+      <Real Name="Z">-4.609160764286969</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">152.54233223921591</Real>
+      <Real Name="Y">80.755871218605719</Real>
+      <Real Name="Z">374.31969485210789</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">752.07305367461254</Real>
+      <Real Name="Y">-434.34766229470961</Real>
+      <Real Name="Z">-214.31035391125414</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-343.17311629667324</Real>
+      <Real Name="Y">553.38204671454162</Real>
+      <Real Name="Z">68.01927901420872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-642.39799748870212</Real>
+      <Real Name="Y">-87.784232015889543</Real>
+      <Real Name="Z">211.36106231484843</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-970.81091792967879</Real>
+      <Real Name="Y">1191.1178421847569</Real>
+      <Real Name="Z">-661.6742905642011</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">177.44504714780058</Real>
+      <Real Name="Y">-180.1779640492918</Real>
+      <Real Name="Z">102.50354067216313</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">566.93733121364153</Real>
+      <Real Name="Y">-837.28827878434356</Real>
+      <Real Name="Z">531.08646389247906</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">932.74383319565641</Real>
+      <Real Name="Y">-114.56636487598655</Real>
+      <Real Name="Z">50.750330535193584</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-155.63212755008615</Real>
+      <Real Name="Y">86.518794058731501</Real>
+      <Real Name="Z">-33.157416671287422</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-893.28257119505315</Real>
+      <Real Name="Y">-61.236180646035947</Real>
+      <Real Name="Z">-96.100992186904435</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-629.06747090741271</Real>
+      <Real Name="Y">-662.00870005587501</Real>
+      <Real Name="Z">149.79792925181238</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">595.58632049600328</Real>
+      <Real Name="Y">679.88562063150482</Real>
+      <Real Name="Z">25.419904885722744</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">45.773568823798144</Real>
+      <Real Name="Y">120.40990391764734</Real>
+      <Real Name="Z">-76.372002200021242</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-294.48928840113388</Real>
+      <Real Name="Y">-1104.757005671031</Real>
+      <Real Name="Z">1200.0976425485662</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">325.05150014944604</Real>
+      <Real Name="Y">624.1626728472063</Real>
+      <Real Name="Z">-558.01053406042399</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-67.855710265621454</Real>
+      <Real Name="Y">141.74695233232129</Real>
+      <Real Name="Z">-189.86084370827604</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">365.12915564076371</Real>
+      <Real Name="Y">-503.04194839066349</Real>
+      <Real Name="Z">35.409107088644404</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-83.477727274986236</Real>
+      <Real Name="Y">278.95882078802759</Real>
+      <Real Name="Z">-66.875837497870975</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-376.02838157991931</Real>
+      <Real Name="Y">356.52460909886457</Real>
+      <Real Name="Z">-70.624391900156681</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-108.41683738778997</Real>
+      <Real Name="Y">432.04636700440346</Real>
+      <Real Name="Z">0.68204687902416339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.493220507196391</Real>
+      <Real Name="Y">-206.92856228882039</Real>
+      <Real Name="Z">37.164478306693255</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">38.805404723907699</Real>
+      <Real Name="Y">-126.78896516329164</Real>
+      <Real Name="Z">-2.9725720809346292</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">4.6901854253781785</Real>
+      <Real Name="Y">771.11049102416473</Real>
+      <Real Name="Z">66.670470616033114</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-18.813311343341166</Real>
+      <Real Name="Y">-180.64386309722553</Real>
+      <Real Name="Z">-61.382678036754847</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">158.86028934297019</Real>
+      <Real Name="Y">-672.36259207137141</Real>
+      <Real Name="Z">-41.635422842096069</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-122.17050399802608</Real>
+      <Real Name="Y">-676.03684111165671</Real>
+      <Real Name="Z">-273.40557177594883</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">211.0080395983961</Real>
+      <Real Name="Y">595.20219258972656</Real>
+      <Real Name="Z">154.80139096518786</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">95.608344323656013</Real>
+      <Real Name="Y">150.12608753886329</Real>
+      <Real Name="Z">39.933019886533202</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.82053087847197</Real>
+      <Real Name="Y">880.41486217048123</Real>
+      <Real Name="Z">600.85857898974257</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-5.198985632684753</Real>
+      <Real Name="Y">-213.73635476419463</Real>
+      <Real Name="Z">-115.49563524439449</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-175.79001523612402</Real>
+      <Real Name="Y">-679.06069374455615</Real>
+      <Real Name="Z">-459.33747803865703</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-226.65125499012419</Real>
+      <Real Name="Y">172.8751621033685</Real>
+      <Real Name="Z">-350.92569497574368</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">107.05860892994713</Real>
+      <Real Name="Y">-101.83270162843215</Real>
+      <Real Name="Z">348.16571501492984</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">128.9610280049119</Real>
+      <Real Name="Y">-61.461719340211943</Real>
+      <Real Name="Z">112.2945407136723</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-993.65705460264314</Real>
+      <Real Name="Y">18.768520856315703</Real>
+      <Real Name="Z">-59.185028818087915</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">725.3412218186503</Real>
+      <Real Name="Y">-139.84675161455874</Real>
+      <Real Name="Z">-18.228679392021583</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">336.57125455132564</Real>
+      <Real Name="Y">-4.0618247452246052</Real>
+      <Real Name="Z">9.3212123510936209</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1550.2693886064808</Real>
+      <Real Name="Y">-910.34807602852777</Real>
+      <Real Name="Z">-457.76814015317512</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">341.94231143700182</Real>
+      <Real Name="Y">73.945950655104014</Real>
+      <Real Name="Z">129.89336559559644</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1181.1936701961845</Real>
+      <Real Name="Y">219.10943379161571</Real>
+      <Real Name="Z">333.46383191220156</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-103.52647365129079</Real>
+      <Real Name="Y">1495.1970458051362</Real>
+      <Real Name="Z">78.130345458138208</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-51.378193918964193</Real>
+      <Real Name="Y">-804.67384367842726</Real>
+      <Real Name="Z">-440.46634964335078</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">209.29181365295429</Real>
+      <Real Name="Y">-612.51325461727572</Real>
+      <Real Name="Z">585.54055387336746</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">243.5939216738401</Real>
+      <Real Name="Y">507.99902929554816</Real>
+      <Real Name="Z">161.72059367033557</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-68.26695134010086</Real>
+      <Real Name="Y">-132.45636200881592</Real>
+      <Real Name="Z">-106.35234988313007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-261.09392222401112</Real>
+      <Real Name="Y">-511.76985324789825</Real>
+      <Real Name="Z">-76.962327078811199</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">759.01052594513817</Real>
+      <Real Name="Y">-1661.1331307946609</Real>
+      <Real Name="Z">827.05399144397336</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.529896760183561</Real>
+      <Real Name="Y">1139.089276854306</Real>
+      <Real Name="Z">100.92967864697897</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-943.43172751768952</Real>
+      <Real Name="Y">560.11060974474026</Real>
+      <Real Name="Z">-735.01675390156993</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">793.80649762239614</Real>
+      <Real Name="Y">-1988.3174642756364</Real>
+      <Real Name="Z">-647.08520946091744</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-54.400048234758188</Real>
+      <Real Name="Y">837.72475658761277</Real>
+      <Real Name="Z">202.14473734759386</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-539.04080885076928</Real>
+      <Real Name="Y">674.59825455737121</Real>
+      <Real Name="Z">425.48245726827935</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">105.22828627377334</Real>
+      <Real Name="Y">745.71529735753461</Real>
+      <Real Name="Z">1198.7578975322367</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-115.14778189646995</Real>
+      <Real Name="Y">-588.50926585763943</Real>
+      <Real Name="Z">-1018.2727265484992</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-58.325104563753186</Real>
+      <Real Name="Y">-81.868459052440855</Real>
+      <Real Name="Z">-276.16191759646398</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
diff --git a/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_vdwalone_s.xml b/src/programs/mdrun/tests/refdata/FreeEnergyCalculationsAreEquivalentToReference_FreeEnergyReferenceTest_WithinTolerances_vdwalone_s.xml
new file mode 100644 (file)
index 0000000..32c0b3b
--- /dev/null
@@ -0,0 +1,938 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="dVremain/dl">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-3.1578636</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1.3187587</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1.6902198</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-2.0658941</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-2.4364743</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-4.3400421</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-3.1195583</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-3.4044375</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-3.6330967</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-3.7941656</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-5.7163758</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-3.8896732</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-3.8260446</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-3.6977625</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-3.5181885</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-5.8145547</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-3.0647001</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-2.8231373</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-2.5900869</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-2.375895</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-4.2078023</Real>
+  </Energy>
+  <Energy Name="Potential">
+    <Real Name="Time 0.000000 Step 0 in frame 0">-1342.2134</Real>
+    <Real Name="Time 0.001000 Step 1 in frame 1">-1340.1353</Real>
+    <Real Name="Time 0.002000 Step 2 in frame 2">-1336.4592</Real>
+    <Real Name="Time 0.003000 Step 3 in frame 3">-1331.3503</Real>
+    <Real Name="Time 0.004000 Step 4 in frame 4">-1325.1421</Real>
+    <Real Name="Time 0.005000 Step 5 in frame 5">-1318.2819</Real>
+    <Real Name="Time 0.006000 Step 6 in frame 6">-1311.2549</Real>
+    <Real Name="Time 0.007000 Step 7 in frame 7">-1304.5017</Real>
+    <Real Name="Time 0.008000 Step 8 in frame 8">-1298.3589</Real>
+    <Real Name="Time 0.009000 Step 9 in frame 9">-1292.9956</Real>
+    <Real Name="Time 0.010000 Step 10 in frame 10">-1288.4463</Real>
+    <Real Name="Time 0.011000 Step 11 in frame 11">-1284.6593</Real>
+    <Real Name="Time 0.012000 Step 12 in frame 12">-1281.5671</Real>
+    <Real Name="Time 0.013000 Step 13 in frame 13">-1279.16</Real>
+    <Real Name="Time 0.014000 Step 14 in frame 14">-1277.5127</Real>
+    <Real Name="Time 0.015000 Step 15 in frame 15">-1276.7588</Real>
+    <Real Name="Time 0.016000 Step 16 in frame 16">-1277.0233</Real>
+    <Real Name="Time 0.017000 Step 17 in frame 17">-1278.345</Real>
+    <Real Name="Time 0.018000 Step 18 in frame 18">-1280.6171</Real>
+    <Real Name="Time 0.019000 Step 19 in frame 19">-1283.5985</Real>
+    <Real Name="Time 0.020000 Step 20 in frame 20">-1286.9448</Real>
+  </Energy>
+  <Sequence Name="Time 0.000000 Step 0 F">
+    <Int Name="Length">177</Int>
+    <Vector>
+      <Real Name="X">560.16492</Real>
+      <Real Name="Y">-775.82928</Real>
+      <Real Name="Z">-164.16643</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-162.63701</Real>
+      <Real Name="Y">-235.29694</Real>
+      <Real Name="Z">28.559114</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-450.27435</Real>
+      <Real Name="Y">281.00803</Real>
+      <Real Name="Z">-214.39891</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-76.057083</Real>
+      <Real Name="Y">178.09377</Real>
+      <Real Name="Z">107.30103</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">745.82031</Real>
+      <Real Name="Y">613.61011</Real>
+      <Real Name="Z">-39.247734</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.57391</Real>
+      <Real Name="Y">184.74165</Real>
+      <Real Name="Z">-230.01987</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-828.03705</Real>
+      <Real Name="Y">313.97644</Real>
+      <Real Name="Z">1121.9968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">38.106876</Real>
+      <Real Name="Y">-203.46571</Real>
+      <Real Name="Z">-499.97742</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">447.3858</Real>
+      <Real Name="Y">-341.67203</Real>
+      <Real Name="Z">-146.88185</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">10.925804</Real>
+      <Real Name="Y">-30.976189</Real>
+      <Real Name="Z">20.380272</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">2.0141983</Real>
+      <Real Name="Y">27.856314</Real>
+      <Real Name="Z">-12.695366</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-9.2674408</Real>
+      <Real Name="Y">14.475534</Real>
+      <Real Name="Z">-4.0459976</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.22610474</Real>
+      <Real Name="Y">23.698967</Real>
+      <Real Name="Z">-7.1104736</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.8458405</Real>
+      <Real Name="Y">-9.5181484</Real>
+      <Real Name="Z">4.7690716</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">5.3456688</Real>
+      <Real Name="Y">-24.903942</Real>
+      <Real Name="Z">4.5986137</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">364.96179</Real>
+      <Real Name="Y">-59.604706</Real>
+      <Real Name="Z">-670.1571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.81146</Real>
+      <Real Name="Y">36.204243</Real>
+      <Real Name="Z">385.08865</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-6.8211288</Real>
+      <Real Name="Y">-11.299082</Real>
+      <Real Name="Z">206.28496</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">394.26355</Real>
+      <Real Name="Y">432.22849</Real>
+      <Real Name="Z">-97.821304</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-49.851814</Real>
+      <Real Name="Y">-238.51105</Real>
+      <Real Name="Z">85.78244</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-370.92975</Real>
+      <Real Name="Y">-600.14075</Real>
+      <Real Name="Z">242.79077</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">643.13</Real>
+      <Real Name="Y">-492.27618</Real>
+      <Real Name="Z">-420.98648</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-111.97176</Real>
+      <Real Name="Y">121.35711</Real>
+      <Real Name="Z">75.023636</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-645.06006</Real>
+      <Real Name="Y">438.33389</Real>
+      <Real Name="Z">218.99048</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-19.018064</Real>
+      <Real Name="Y">295.74365</Real>
+      <Real Name="Z">69.894882</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">88.252739</Real>
+      <Real Name="Y">-163.91307</Real>
+      <Real Name="Z">-112.35473</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-116.12119</Real>
+      <Real Name="Y">-215.1304</Real>
+      <Real Name="Z">-140.17712</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-591.89911</Real>
+      <Real Name="Y">297.62549</Real>
+      <Real Name="Z">826.34424</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">397.93842</Real>
+      <Real Name="Y">-70.315125</Real>
+      <Real Name="Z">-868.24725</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">176.87331</Real>
+      <Real Name="Y">-199.27788</Real>
+      <Real Name="Z">-58.264145</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1115.1495</Real>
+      <Real Name="Y">629.93915</Real>
+      <Real Name="Z">795.45282</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.5452</Real>
+      <Real Name="Y">-873.28632</Real>
+      <Real Name="Z">-339.25296</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-870.47675</Real>
+      <Real Name="Y">286.55475</Real>
+      <Real Name="Z">-544.52521</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">357.06366</Real>
+      <Real Name="Y">756.95844</Real>
+      <Real Name="Z">146.15584</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-4.1041641</Real>
+      <Real Name="Y">-651.95844</Real>
+      <Real Name="Z">-54.851482</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-90.345825</Real>
+      <Real Name="Y">-131.84966</Real>
+      <Real Name="Z">-37.247372</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-20.407047</Real>
+      <Real Name="Y">-138.53905</Real>
+      <Real Name="Z">-868.91284</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-69.01487</Real>
+      <Real Name="Y">7.3681154</Real>
+      <Real Name="Z">237.56247</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">409.1962</Real>
+      <Real Name="Y">-13.744339</Real>
+      <Real Name="Z">674.30853</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">90.694618</Real>
+      <Real Name="Y">50.061829</Real>
+      <Real Name="Z">-863.78394</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-246.26122</Real>
+      <Real Name="Y">161.93843</Real>
+      <Real Name="Z">488.15155</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">20.568638</Real>
+      <Real Name="Y">-198.69077</Real>
+      <Real Name="Z">167.75015</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-614.61157</Real>
+      <Real Name="Y">104.50482</Real>
+      <Real Name="Z">354.03036</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">537.62415</Real>
+      <Real Name="Y">-78.613785</Real>
+      <Real Name="Z">-4.2482901</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">129.13351</Real>
+      <Real Name="Y">-43.662777</Real>
+      <Real Name="Z">-245.23721</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-106.56845</Real>
+      <Real Name="Y">29.627792</Real>
+      <Real Name="Z">363.06107</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">246.89545</Real>
+      <Real Name="Y">-1.2883682</Real>
+      <Real Name="Z">-127.71266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">59.181488</Real>
+      <Real Name="Y">-111.70288</Real>
+      <Real Name="Z">-115.61073</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-356.31265</Real>
+      <Real Name="Y">-2083.4021</Real>
+      <Real Name="Z">-695.05737</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">338.5383</Real>
+      <Real Name="Y">1335.4231</Real>
+      <Real Name="Z">310.34689</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">276.91052</Real>
+      <Real Name="Y">426.71542</Real>
+      <Real Name="Z">68.866402</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">408.92999</Real>
+      <Real Name="Y">978.77844</Real>
+      <Real Name="Z">-851.55042</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-141.87756</Real>
+      <Real Name="Y">-531.63434</Real>
+      <Real Name="Z">230.45872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-250.61594</Real>
+      <Real Name="Y">-386.77625</Real>
+      <Real Name="Z">568.34509</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">52.995796</Real>
+      <Real Name="Y">98.713974</Real>
+      <Real Name="Z">5.9983025</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-10.455554</Real>
+      <Real Name="Y">-43.458389</Real>
+      <Real Name="Z">-3.6231194</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-21.239048</Real>
+      <Real Name="Y">-33.183907</Real>
+      <Real Name="Z">8.2902775</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-423.88623</Real>
+      <Real Name="Y">988.97534</Real>
+      <Real Name="Z">389.60474</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">598.97217</Real>
+      <Real Name="Y">-698.11646</Real>
+      <Real Name="Z">-450.10861</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">30.343483</Real>
+      <Real Name="Y">-160.39758</Real>
+      <Real Name="Z">-11.909847</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">156.71974</Real>
+      <Real Name="Y">-502.93311</Real>
+      <Real Name="Z">-1442.6196</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-46.964081</Real>
+      <Real Name="Y">370.46133</Real>
+      <Real Name="Z">1053.1552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-283.37589</Real>
+      <Real Name="Y">14.029888</Real>
+      <Real Name="Z">269.58936</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">180.54297</Real>
+      <Real Name="Y">-124.31937</Real>
+      <Real Name="Z">-117.58147</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-85.880951</Real>
+      <Real Name="Y">126.24585</Real>
+      <Real Name="Z">72.835114</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-62.938107</Real>
+      <Real Name="Y">40.618141</Real>
+      <Real Name="Z">150.70537</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">331.19733</Real>
+      <Real Name="Y">-338.35446</Real>
+      <Real Name="Z">-76.468643</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-289.71738</Real>
+      <Real Name="Y">414.147</Real>
+      <Real Name="Z">98.183876</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-104.25122</Real>
+      <Real Name="Y">67.408051</Real>
+      <Real Name="Z">41.300266</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">724.01038</Real>
+      <Real Name="Y">-249.70982</Real>
+      <Real Name="Z">-584.33594</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-344.99899</Real>
+      <Real Name="Y">205.72139</Real>
+      <Real Name="Z">640.11542</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-247.02272</Real>
+      <Real Name="Y">324.86865</Real>
+      <Real Name="Z">148.15186</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">0.68766022</Real>
+      <Real Name="Y">-10.919334</Real>
+      <Real Name="Z">9.5573883</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-0.5278492</Real>
+      <Real Name="Y">6.9944229</Real>
+      <Real Name="Z">-2.9469452</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">3.7969704</Real>
+      <Real Name="Y">4.9394989</Real>
+      <Real Name="Z">-7.0647907</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-407.1109</Real>
+      <Real Name="Y">846.24829</Real>
+      <Real Name="Z">252.97472</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">291.08258</Real>
+      <Real Name="Y">-480.01971</Real>
+      <Real Name="Z">-680.65302</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.95667</Real>
+      <Real Name="Y">-441.6496</Real>
+      <Real Name="Z">290.35001</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">370.52753</Real>
+      <Real Name="Y">32.474689</Real>
+      <Real Name="Z">-151.86365</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.64911</Real>
+      <Real Name="Y">57.971031</Real>
+      <Real Name="Z">40.010414</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-91.314713</Real>
+      <Real Name="Y">-39.183544</Real>
+      <Real Name="Z">74.100586</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-596.52307</Real>
+      <Real Name="Y">-1291.9413</Real>
+      <Real Name="Z">-303.77917</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">218.88113</Real>
+      <Real Name="Y">615.97302</Real>
+      <Real Name="Z">-293.4668</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">393.9408</Real>
+      <Real Name="Y">650.36456</Real>
+      <Real Name="Z">617.01917</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1193.564</Real>
+      <Real Name="Y">443.09839</Real>
+      <Real Name="Z">-792.07574</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">819.54901</Real>
+      <Real Name="Y">-311.52759</Real>
+      <Real Name="Z">572.28064</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">184.21558</Real>
+      <Real Name="Y">-31.884295</Real>
+      <Real Name="Z">38.946358</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">216.15753</Real>
+      <Real Name="Y">430.48151</Real>
+      <Real Name="Z">164.33649</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.185656</Real>
+      <Real Name="Y">-134.87021</Real>
+      <Real Name="Z">-70.851448</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-108.02281</Real>
+      <Real Name="Y">-124.21425</Real>
+      <Real Name="Z">-17.181404</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1046.2062</Real>
+      <Real Name="Y">-1074.0594</Real>
+      <Real Name="Z">-860.1192</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">182.09605</Real>
+      <Real Name="Y">209.2016</Real>
+      <Real Name="Z">196.16069</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">575.92712</Real>
+      <Real Name="Y">691.03589</Real>
+      <Real Name="Z">682.34711</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">197.87489</Real>
+      <Real Name="Y">390.16568</Real>
+      <Real Name="Z">-1022.8798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-99.947098</Real>
+      <Real Name="Y">-318.27917</Real>
+      <Real Name="Z">753.25494</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">55.99823</Real>
+      <Real Name="Y">32.162571</Real>
+      <Real Name="Z">254.53839</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-271.83133</Real>
+      <Real Name="Y">-511.42389</Real>
+      <Real Name="Z">-930.75775</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">240.26678</Real>
+      <Real Name="Y">79.126808</Real>
+      <Real Name="Z">146.98911</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">54.370152</Real>
+      <Real Name="Y">256.5076</Real>
+      <Real Name="Z">804.46674</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-438.81412</Real>
+      <Real Name="Y">-223.36752</Real>
+      <Real Name="Z">-199.19012</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">224.94597</Real>
+      <Real Name="Y">60.396034</Real>
+      <Real Name="Z">145.17058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">225.78027</Real>
+      <Real Name="Y">77.065506</Real>
+      <Real Name="Z">123.62689</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">568.83929</Real>
+      <Real Name="Y">813.67346</Real>
+      <Real Name="Z">-260.60638</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-117.33185</Real>
+      <Real Name="Y">-198.43544</Real>
+      <Real Name="Z">15.699821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-449.9657</Real>
+      <Real Name="Y">-861.01874</Real>
+      <Real Name="Z">-32.847767</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1744.7449</Real>
+      <Real Name="Y">-146.71201</Real>
+      <Real Name="Z">947.75922</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-863.39832</Real>
+      <Real Name="Y">303.07144</Real>
+      <Real Name="Z">-704.97058</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-345.04962</Real>
+      <Real Name="Y">-176.10233</Real>
+      <Real Name="Z">-382.00201</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-153.51324</Real>
+      <Real Name="Y">55.68367</Real>
+      <Real Name="Z">125.95101</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">163.0448</Real>
+      <Real Name="Y">-162.75539</Real>
+      <Real Name="Z">29.864437</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">198.87985</Real>
+      <Real Name="Y">-7.5582418</Real>
+      <Real Name="Z">25.019432</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">687.48944</Real>
+      <Real Name="Y">-345.39349</Real>
+      <Real Name="Z">42.984257</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-478.77271</Real>
+      <Real Name="Y">292.2962</Real>
+      <Real Name="Z">25.484558</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-395.64474</Real>
+      <Real Name="Y">328.12592</Real>
+      <Real Name="Z">-128.69678</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-129.70667</Real>
+      <Real Name="Y">-1811.1146</Real>
+      <Real Name="Z">50.021095</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">128.98105</Real>
+      <Real Name="Y">237.25067</Real>
+      <Real Name="Z">-41.168339</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">183.38527</Real>
+      <Real Name="Y">1224.5275</Real>
+      <Real Name="Z">-63.084476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">423.36807</Real>
+      <Real Name="Y">1028.9308</Real>
+      <Real Name="Z">825.75165</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-305.67789</Real>
+      <Real Name="Y">-646.07843</Real>
+      <Real Name="Z">-555.45239</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-56.187481</Real>
+      <Real Name="Y">-238.70564</Real>
+      <Real Name="Z">-216.71841</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-54.446739</Real>
+      <Real Name="Y">-447.62561</Real>
+      <Real Name="Z">-500.98169</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-158.08191</Real>
+      <Real Name="Y">376.0954</Real>
+      <Real Name="Z">254.38142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-93.62793</Real>
+      <Real Name="Y">161.07248</Real>
+      <Real Name="Z">477.57007</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">846.24982</Real>
+      <Real Name="Y">-284.14038</Real>
+      <Real Name="Z">-279.10016</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-199.05643</Real>
+      <Real Name="Y">452.46204</Real>
+      <Real Name="Z">22.097107</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-761.2276</Real>
+      <Real Name="Y">-163.09406</Real>
+      <Real Name="Z">188.54044</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1087.0122</Real>
+      <Real Name="Y">1521.0803</Real>
+      <Real Name="Z">-673.28552</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.66171</Real>
+      <Real Name="Y">-219.61053</Real>
+      <Real Name="Z">95.491249</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">620.03485</Real>
+      <Real Name="Y">-974.03101</Real>
+      <Real Name="Z">441.56821</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">743.21619</Real>
+      <Real Name="Y">29.394814</Real>
+      <Real Name="Z">149.52885</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-163.6862</Real>
+      <Real Name="Y">92.882713</Real>
+      <Real Name="Z">-36.452312</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-914.89764</Real>
+      <Real Name="Y">119.96918</Real>
+      <Real Name="Z">-128.94379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-822.58942</Real>
+      <Real Name="Y">-1220.8199</Real>
+      <Real Name="Z">114.17398</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">707.68378</Real>
+      <Real Name="Y">941.33618</Real>
+      <Real Name="Z">60.082314</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">68.118355</Real>
+      <Real Name="Y">251.30519</Real>
+      <Real Name="Z">-76.473816</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-136.64589</Real>
+      <Real Name="Y">-697.23053</Real>
+      <Real Name="Z">809.20074</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.8913</Real>
+      <Real Name="Y">509.84113</Real>
+      <Real Name="Z">-581.41943</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-50.67886</Real>
+      <Real Name="Y">79.15834</Real>
+      <Real Name="Z">-119.01872</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">69.200943</Real>
+      <Real Name="Y">-148.69383</Real>
+      <Real Name="Z">341.95465</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-28.626726</Real>
+      <Real Name="Y">255.32982</Real>
+      <Real Name="Z">-74.753937</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-285.06201</Real>
+      <Real Name="Y">369.95981</Real>
+      <Real Name="Z">26.579353</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">82.726593</Real>
+      <Real Name="Y">465.92172</Real>
+      <Real Name="Z">-66.92263</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-52.497044</Real>
+      <Real Name="Y">-170.16011</Real>
+      <Real Name="Z">64.423462</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">32.73671</Real>
+      <Real Name="Y">-177.85538</Real>
+      <Real Name="Z">21.967012</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.752823</Real>
+      <Real Name="Y">766.99323</Real>
+      <Real Name="Z">75.477798</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-45.588104</Real>
+      <Real Name="Y">-166.13193</Real>
+      <Real Name="Z">-66.487488</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.568993</Real>
+      <Real Name="Y">-724.08154</Real>
+      <Real Name="Z">-12.754246</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-97.510574</Real>
+      <Real Name="Y">-723.00421</Real>
+      <Real Name="Z">-210.45476</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">171.88062</Real>
+      <Real Name="Y">626.18372</Real>
+      <Real Name="Z">111.89783</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">73.519997</Real>
+      <Real Name="Y">176.58563</Real>
+      <Real Name="Z">44.482582</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">89.218536</Real>
+      <Real Name="Y">786.16846</Real>
+      <Real Name="Z">544.71295</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">14.387653</Real>
+      <Real Name="Y">-180.10947</Real>
+      <Real Name="Z">-82.72142</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">139.17728</Real>
+      <Real Name="Y">-585.65979</Real>
+      <Real Name="Z">-443.16635</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-295.15359</Real>
+      <Real Name="Y">299.40933</Real>
+      <Real Name="Z">-420.1748</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">164.55542</Real>
+      <Real Name="Y">-252.1886</Real>
+      <Real Name="Z">436.97571</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">150.03229</Real>
+      <Real Name="Y">-111.08784</Real>
+      <Real Name="Z">92.585892</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-885.4577</Real>
+      <Real Name="Y">-83.699646</Real>
+      <Real Name="Z">-57.061874</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">730.81207</Real>
+      <Real Name="Y">-69.788956</Real>
+      <Real Name="Z">3.6640511</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">312.90143</Real>
+      <Real Name="Y">42.177246</Real>
+      <Real Name="Z">7.3131104</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1533.2124</Real>
+      <Real Name="Y">-268.07193</Real>
+      <Real Name="Z">-639.87036</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">298.84668</Real>
+      <Real Name="Y">-41.605171</Real>
+      <Real Name="Z">203.82086</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1071.9005</Real>
+      <Real Name="Y">337.55316</Real>
+      <Real Name="Z">324.01285</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-413.93738</Real>
+      <Real Name="Y">1396.3585</Real>
+      <Real Name="Z">325.87701</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">15.269648</Real>
+      <Real Name="Y">-723.13293</Real>
+      <Real Name="Z">-590.00214</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">383.87311</Real>
+      <Real Name="Y">-438.32825</Real>
+      <Real Name="Z">485.93811</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">200.20433</Real>
+      <Real Name="Y">440.55023</Real>
+      <Real Name="Z">126.63379</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-66.497604</Real>
+      <Real Name="Y">-125.11058</Real>
+      <Real Name="Z">-82.321968</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-223.47771</Real>
+      <Real Name="Y">-496.10773</Real>
+      <Real Name="Z">-108.89548</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">1155.9902</Real>
+      <Real Name="Y">-1972.2001</Real>
+      <Real Name="Z">678.14453</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-122.95121</Real>
+      <Real Name="Y">1194.139</Real>
+      <Real Name="Z">314.51654</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-1030.7716</Real>
+      <Real Name="Y">372.63919</Real>
+      <Real Name="Z">-811.24615</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">776.88306</Real>
+      <Real Name="Y">-1294.7927</Real>
+      <Real Name="Z">-289.80396</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-239.76584</Real>
+      <Real Name="Y">679.80609</Real>
+      <Real Name="Z">20.613815</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-496.59177</Real>
+      <Real Name="Y">693.43573</Real>
+      <Real Name="Z">319.41367</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">85.915276</Real>
+      <Real Name="Y">843.01392</Real>
+      <Real Name="Z">1349.7611</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-105.1994</Real>
+      <Real Name="Y">-723.44421</Real>
+      <Real Name="Z">-1015.3097</Real>
+    </Vector>
+    <Vector>
+      <Real Name="X">-113.93722</Real>
+      <Real Name="Y">7.4774971</Real>
+      <Real Name="Z">-260.68109</Real>
+    </Vector>
+  </Sequence>
+</ReferenceData>
index fd134f174250c7584a7f317d035f1e327740acde..fba9b916bb66265a0b9af451a319273261774c0b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 87d8719c45aa4e331ea3dcbc3a6eb125f536e75b..e358b9cd1ec12878815e88384df75eeec542f13a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "config.h"
 
 #include "gromacs/topology/ifunc.h"
-#include "gromacs/trajectory/energyframe.h"
 #include "gromacs/utility/stringutil.h"
 
+#include "testutils/mpitest.h"
+#include "testutils/simulationdatabase.h"
+
+#include "moduletest.h"
 #include "simulatorcomparison.h"
 
 namespace gmx
@@ -73,7 +77,7 @@ namespace
  * needed/useful.
  *
  * We should also not compare pressure, because with constraints the
- * non-search steps need a much larger tolerance, and per Redmine 1868
+ * non-search steps need a much larger tolerance, and per Issue #1868
  * we should stop computing pressure in reruns anyway.
  *
  * Similarly, per 1868, in the present implementation the kinetic
@@ -101,9 +105,62 @@ const TrajectoryFrameMatchSettings MdrunRerunTest::trajectoryMatchSettings = {
     true,
     ComparisonConditions::MustCompare,
     ComparisonConditions::NoComparison,
-    ComparisonConditions::MustCompare
+    ComparisonConditions::MustCompare,
+    MaxNumFrames::compareAllFrames()
 };
 
+void executeRerunTest(TestFileManager*            fileManager,
+                      SimulationRunner*           runner,
+                      const std::string&          simulationName,
+                      int                         numWarningsToTolerate,
+                      const MdpFieldValues&       mdpFieldValues,
+                      const EnergyTermsToCompare& energyTermsToCompare,
+                      const TrajectoryComparison& trajectoryComparison)
+{
+    // TODO At some point we should also test PME-only ranks.
+    int numRanksAvailable = getNumberOfTestMpiRanks();
+    if (!isNumberOfPpRanksSupported(simulationName, numRanksAvailable))
+    {
+        fprintf(stdout,
+                "Test system '%s' cannot run with %d ranks.\n"
+                "The supported numbers are: %s\n",
+                simulationName.c_str(), numRanksAvailable,
+                reportNumbersOfPpRanksSupported(simulationName).c_str());
+        return;
+    }
+
+    // Set file names
+    auto simulator1TrajectoryFileName = fileManager->getTemporaryFilePath("sim1.trr");
+    auto simulator1EdrFileName        = fileManager->getTemporaryFilePath("sim1.edr");
+    auto simulator2TrajectoryFileName = fileManager->getTemporaryFilePath("sim2.trr");
+    auto simulator2EdrFileName        = fileManager->getTemporaryFilePath("sim2.edr");
+
+    // Run grompp
+    runner->tprFileName_ = fileManager->getTemporaryFilePath("sim.tpr");
+    runner->useTopGroAndNdxFromDatabase(simulationName);
+    runner->useStringAsMdpFile(prepareMdpFileContents(mdpFieldValues));
+    auto options = std::vector<SimulationOptionTuple>();
+    if (numWarningsToTolerate > 0)
+    {
+        options.emplace_back(SimulationOptionTuple("-maxwarn", std::to_string(numWarningsToTolerate)));
+    }
+    runGrompp(runner, options);
+
+    // Do first mdrun
+    runner->fullPrecisionTrajectoryFileName_ = simulator1TrajectoryFileName;
+    runner->edrFileName_                     = simulator1EdrFileName;
+    runMdrun(runner);
+
+    // Do second mdrun
+    runner->fullPrecisionTrajectoryFileName_ = simulator2TrajectoryFileName;
+    runner->edrFileName_                     = simulator2EdrFileName;
+    runMdrun(runner, { SimulationOptionTuple("-rerun", simulator1TrajectoryFileName) });
+
+    // Compare simulation results
+    compareEnergies(simulator1EdrFileName, simulator2EdrFileName, energyTermsToCompare);
+    compareTrajectories(simulator1TrajectoryFileName, simulator2TrajectoryFileName, trajectoryComparison);
+}
+
 TEST_P(MdrunRerunTest, WithinTolerances)
 {
     auto params         = GetParam();
@@ -137,7 +194,7 @@ TEST_P(MdrunRerunTest, WithinTolerances)
 // TODO The time for OpenCL kernel compilation means these tests time
 // out. Once that compilation is cached for the whole process, these
 // tests can run in such configurations.
-#if GMX_GPU != GMX_GPU_OPENCL
+#if !GMX_GPU_OPENCL
 INSTANTIATE_TEST_CASE_P(
         NormalMdrunIsReproduced,
         MdrunRerunTest,
@@ -196,7 +253,7 @@ TEST_P(MdrunRerunFreeEnergyTest, WithinTolerances)
 // TODO The time for OpenCL kernel compilation means these tests time
 // out. Once that compilation is cached for the whole process, these
 // tests can run in such configurations.
-#if GMX_GPU != GMX_GPU_OPENCL
+#if !GMX_GPU_OPENCL
 INSTANTIATE_TEST_CASE_P(MdrunIsReproduced,
                         MdrunRerunFreeEnergyTest,
                         ::testing::Combine(::testing::Values("nonanol_vacuo"),
index 7e84a109a37f3097f6b812770d43d2ca4c775126..7d0bf8c017ccfc0781986fcf824787030009e2b4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -170,18 +170,19 @@ TEST_P(SimpleMdrunTest, WithinTolerances)
     }
 }
 
-//! Containers of systems to test.
-//! \{
-std::vector<std::string> systemsToTest_g = { "angles1" };
-std::vector<std::string> md_g            = { "md", "md-vv" };
-//! \}
-
 // The time for OpenCL kernel compilation means these tests might time
 // out. If that proves to be a problem, these can be disabled for
 // OpenCL builds. However, once that compilation is cached for the
 // lifetime of the whole test binary process, these tests should run in
 // such configurations.
 #if GMX_DOUBLE
+
+//! Containers of systems to test.
+//! \{
+std::vector<std::string> systemsToTest_g = { "angles1" };
+std::vector<std::string> md_g            = { "md", "md-vv" };
+//! \}
+
 INSTANTIATE_TEST_CASE_P(Angles1,
                         SimpleMdrunTest,
                         ::testing::Combine(::testing::ValuesIn(systemsToTest_g), ::testing::ValuesIn(md_g)));
index b1f9830931257e0285b34b34c88be63989064204..31be90d878ebe8ed8cc7a2039986f9e614e1b600 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "config.h"
 
 #include "gromacs/topology/ifunc.h"
-#include "gromacs/trajectory/energyframe.h"
 #include "gromacs/utility/stringutil.h"
 
+#include "testutils/mpitest.h"
+#include "testutils/setenv.h"
+#include "testutils/simulationdatabase.h"
+
+#include "moduletest.h"
 #include "simulatorcomparison.h"
 
 namespace gmx
@@ -78,13 +82,25 @@ class SimulatorComparisonTest :
 
 TEST_P(SimulatorComparisonTest, WithinTolerances)
 {
-    auto params         = GetParam();
-    auto mdpParams      = std::get<0>(params);
-    auto simulationName = std::get<0>(mdpParams);
-    auto integrator     = std::get<1>(mdpParams);
-    auto tcoupling      = std::get<2>(mdpParams);
-    auto pcoupling      = std::get<3>(mdpParams);
-    auto envVariable    = std::get<1>(params);
+    const auto& params              = GetParam();
+    const auto& mdpParams           = std::get<0>(params);
+    const auto& simulationName      = std::get<0>(mdpParams);
+    const auto& integrator          = std::get<1>(mdpParams);
+    const auto& tcoupling           = std::get<2>(mdpParams);
+    const auto& pcoupling           = std::get<3>(mdpParams);
+    const auto& environmentVariable = std::get<1>(params);
+
+    // TODO At some point we should also test PME-only ranks.
+    const int numRanksAvailable = getNumberOfTestMpiRanks();
+    if (!isNumberOfPpRanksSupported(simulationName, numRanksAvailable))
+    {
+        fprintf(stdout,
+                "Test system '%s' cannot run with %d ranks.\n"
+                "The supported numbers are: %s\n",
+                simulationName.c_str(), numRanksAvailable,
+                reportNumbersOfPpRanksSupported(simulationName).c_str());
+        return;
+    }
 
     if (integrator == "md-vv" && pcoupling == "Parrinello-Rahman")
     {
@@ -93,14 +109,17 @@ TEST_P(SimulatorComparisonTest, WithinTolerances)
         return;
     }
 
+    const auto hasConservedField = !(tcoupling == "no" && pcoupling == "no");
+
     SCOPED_TRACE(formatString(
             "Comparing two simulations of '%s' "
-            "with integrator '%s' and '%s' temperature coupling, "
+            "with integrator '%s', '%s' temperature coupling, and '%s' pressure coupling "
             "switching environment variable '%s'",
-            simulationName.c_str(), integrator.c_str(), tcoupling.c_str(), envVariable.c_str()));
+            simulationName.c_str(), integrator.c_str(), tcoupling.c_str(), pcoupling.c_str(),
+            environmentVariable.c_str()));
 
-    auto mdpFieldValues = prepareMdpFieldValues(simulationName.c_str(), integrator.c_str(),
-                                                tcoupling.c_str(), pcoupling.c_str());
+    const auto mdpFieldValues = prepareMdpFieldValues(simulationName.c_str(), integrator.c_str(),
+                                                      tcoupling.c_str(), pcoupling.c_str());
 
     EnergyTermsToCompare energyTermsToCompare{ {
             { interaction_function[F_EPOT].longname, relativeToleranceAsPrecisionDependentUlp(10.0, 100, 80) },
@@ -108,6 +127,11 @@ TEST_P(SimulatorComparisonTest, WithinTolerances)
             { interaction_function[F_PRES].longname,
               relativeToleranceAsPrecisionDependentFloatingPoint(10.0, 0.01, 0.001) },
     } };
+    if (hasConservedField)
+    {
+        energyTermsToCompare.emplace(interaction_function[F_ECONSERVED].longname,
+                                     relativeToleranceAsPrecisionDependentUlp(50.0, 100, 80));
+    }
 
     if (simulationName == "argon12")
     {
@@ -120,15 +144,21 @@ TEST_P(SimulatorComparisonTest, WithinTolerances)
                 { interaction_function[F_PRES].longname,
                   relativeToleranceAsPrecisionDependentFloatingPoint(10.0, 0.001, 0.0001) },
         } };
+        if (hasConservedField)
+        {
+            energyTermsToCompare.emplace(interaction_function[F_ECONSERVED].longname,
+                                         relativeToleranceAsPrecisionDependentUlp(10.0, 24, 80));
+        }
     }
 
     // Specify how trajectory frame matching must work.
-    TrajectoryFrameMatchSettings trajectoryMatchSettings{ true,
-                                                          true,
-                                                          true,
-                                                          ComparisonConditions::MustCompare,
-                                                          ComparisonConditions::MustCompare,
-                                                          ComparisonConditions::MustCompare };
+    const TrajectoryFrameMatchSettings trajectoryMatchSettings{ true,
+                                                                true,
+                                                                true,
+                                                                ComparisonConditions::MustCompare,
+                                                                ComparisonConditions::MustCompare,
+                                                                ComparisonConditions::MustCompare,
+                                                                MaxNumFrames::compareAllFrames() };
     TrajectoryTolerances trajectoryTolerances = TrajectoryComparison::s_defaultTrajectoryTolerances;
     if (simulationName != "argon12")
     {
@@ -137,12 +167,53 @@ TEST_P(SimulatorComparisonTest, WithinTolerances)
 
     // Build the functor that will compare reference and test
     // trajectory frames in the chosen way.
-    TrajectoryComparison trajectoryComparison{ trajectoryMatchSettings, trajectoryTolerances };
+    const TrajectoryComparison trajectoryComparison{ trajectoryMatchSettings, trajectoryTolerances };
+
+    // Set file names
+    const auto simulator1TrajectoryFileName = fileManager_.getTemporaryFilePath("sim1.trr");
+    const auto simulator1EdrFileName        = fileManager_.getTemporaryFilePath("sim1.edr");
+    const auto simulator2TrajectoryFileName = fileManager_.getTemporaryFilePath("sim2.trr");
+    const auto simulator2EdrFileName        = fileManager_.getTemporaryFilePath("sim2.edr");
+
+    // Run grompp
+    runner_.tprFileName_ = fileManager_.getTemporaryFilePath("sim.tpr");
+    runner_.useTopGroAndNdxFromDatabase(simulationName);
+    runner_.useStringAsMdpFile(prepareMdpFileContents(mdpFieldValues));
+    runGrompp(&runner_);
+
+    // Backup current state of environment variable and unset it
+    const char* environmentVariableBackup = getenv(environmentVariable.c_str());
+    gmxUnsetenv(environmentVariable.c_str());
+
+    // Do first mdrun
+    runner_.fullPrecisionTrajectoryFileName_ = simulator1TrajectoryFileName;
+    runner_.edrFileName_                     = simulator1EdrFileName;
+    runMdrun(&runner_);
+
+    // Set environment variable
+    const int overWriteEnvironmentVariable = 1;
+    gmxSetenv(environmentVariable.c_str(), "ON", overWriteEnvironmentVariable);
 
-    int numWarningsToTolerate = 0;
-    executeSimulatorComparisonTest(envVariable, &fileManager_, &runner_, simulationName,
-                                   numWarningsToTolerate, mdpFieldValues, energyTermsToCompare,
-                                   trajectoryComparison);
+    // Do second mdrun
+    runner_.fullPrecisionTrajectoryFileName_ = simulator2TrajectoryFileName;
+    runner_.edrFileName_                     = simulator2EdrFileName;
+    runMdrun(&runner_);
+
+    // Reset or unset environment variable to leave further tests undisturbed
+    if (environmentVariableBackup != nullptr)
+    {
+        // set environment variable
+        gmxSetenv(environmentVariable.c_str(), environmentVariableBackup, overWriteEnvironmentVariable);
+    }
+    else
+    {
+        // unset environment variable
+        gmxUnsetenv(environmentVariable.c_str());
+    }
+
+    // Compare simulation results
+    compareEnergies(simulator1EdrFileName, simulator2EdrFileName, energyTermsToCompare);
+    compareTrajectories(simulator1TrajectoryFileName, simulator2TrajectoryFileName, trajectoryComparison);
 }
 
 // TODO: The time for OpenCL kernel compilation means these tests time
@@ -150,36 +221,38 @@ TEST_P(SimulatorComparisonTest, WithinTolerances)
 //       tests can run in such configurations.
 // These tests are very sensitive, so we only run them in double precision.
 // As we change call ordering, they might actually become too strict to be useful.
-#if GMX_GPU != GMX_GPU_OPENCL && GMX_DOUBLE
-INSTANTIATE_TEST_CASE_P(SimulatorsAreEquivalentDefaultModular,
-                        SimulatorComparisonTest,
-                        ::testing::Combine(::testing::Combine(::testing::Values("argon12", "tip3p5"),
-                                                              ::testing::Values("md-vv"),
-                                                              ::testing::Values("no", "v-rescale"),
-                                                              ::testing::Values("no")),
-                                           ::testing::Values("GMX_DISABLE_MODULAR_SIMULATOR")));
+#if !GMX_GPU_OPENCL && GMX_DOUBLE
+INSTANTIATE_TEST_CASE_P(
+        SimulatorsAreEquivalentDefaultModular,
+        SimulatorComparisonTest,
+        ::testing::Combine(::testing::Combine(::testing::Values("argon12", "tip3p5"),
+                                              ::testing::Values("md-vv"),
+                                              ::testing::Values("no", "v-rescale", "berendsen"),
+                                              ::testing::Values("no")),
+                           ::testing::Values("GMX_DISABLE_MODULAR_SIMULATOR")));
 INSTANTIATE_TEST_CASE_P(
         SimulatorsAreEquivalentDefaultLegacy,
         SimulatorComparisonTest,
         ::testing::Combine(::testing::Combine(::testing::Values("argon12", "tip3p5"),
                                               ::testing::Values("md"),
-                                              ::testing::Values("no", "v-rescale"),
+                                              ::testing::Values("no", "v-rescale", "berendsen"),
                                               ::testing::Values("no", "Parrinello-Rahman")),
                            ::testing::Values("GMX_USE_MODULAR_SIMULATOR")));
 #else
-INSTANTIATE_TEST_CASE_P(DISABLED_SimulatorsAreEquivalentDefaultModular,
-                        SimulatorComparisonTest,
-                        ::testing::Combine(::testing::Combine(::testing::Values("argon12", "tip3p5"),
-                                                              ::testing::Values("md-vv"),
-                                                              ::testing::Values("no", "v-rescale"),
-                                                              ::testing::Values("no")),
-                                           ::testing::Values("GMX_DISABLE_MODULAR_SIMULATOR")));
+INSTANTIATE_TEST_CASE_P(
+        DISABLED_SimulatorsAreEquivalentDefaultModular,
+        SimulatorComparisonTest,
+        ::testing::Combine(::testing::Combine(::testing::Values("argon12", "tip3p5"),
+                                              ::testing::Values("md-vv"),
+                                              ::testing::Values("no", "v-rescale", "berendsen"),
+                                              ::testing::Values("no")),
+                           ::testing::Values("GMX_DISABLE_MODULAR_SIMULATOR")));
 INSTANTIATE_TEST_CASE_P(
         DISABLED_SimulatorsAreEquivalentDefaultLegacy,
         SimulatorComparisonTest,
         ::testing::Combine(::testing::Combine(::testing::Values("argon12", "tip3p5"),
                                               ::testing::Values("md"),
-                                              ::testing::Values("no", "v-rescale"),
+                                              ::testing::Values("no", "v-rescale", "berendsen"),
                                               ::testing::Values("no", "Parrinello-Rahman")),
                            ::testing::Values("GMX_USE_MODULAR_SIMULATOR")));
 #endif
index 816ce6fb6ba75ac318321c0e04a8e6033fa15f58..b4c72451aa4800ec58985e0c80c95ddb5c37760f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -34,7 +34,7 @@
  */
 /*! \internal \file
  * \brief
- * Helper classes for tests that compare the results of equivalent
+ * Helper functions for tests that compare the results of equivalent
  * simulation runs. Currently used for the rerun and the simulator
  * tests
  *
  */
 #include "gmxpre.h"
 
-#include "testutils/mpitest.h"
-#include "testutils/setenv.h"
-#include "testutils/simulationdatabase.h"
+#include "simulatorcomparison.h"
+
+#include "gromacs/trajectory/energyframe.h"
 
-#include "energycomparison.h"
 #include "energyreader.h"
 #include "mdruncomparison.h"
 #include "moduletest.h"
-#include "trajectorycomparison.h"
 #include "trajectoryreader.h"
 
 namespace gmx
 {
 namespace test
 {
-namespace
-{
 
-//! Run grompp and mdrun for both sets of mdp field values
-template<bool doEnvironmentVariable, bool doRerun>
-void executeSimulatorComparisonTestImpl(TestFileManager*            fileManager,
-                                        SimulationRunner*           runner,
-                                        const std::string&          simulationName,
-                                        int                         maxWarningsTolerated,
-                                        const MdpFieldValues&       mdpFieldValues,
-                                        const EnergyTermsToCompare& energyTermsToCompare,
-                                        const TrajectoryComparison& trajectoryComparison,
-                                        const std::string&          environmentVariable)
+void runGrompp(SimulationRunner* runner, const std::vector<SimulationOptionTuple>& options)
 {
-    // TODO At some point we should also test PME-only ranks.
-    int numRanksAvailable = getNumberOfTestMpiRanks();
-    if (!isNumberOfPpRanksSupported(simulationName, numRanksAvailable))
-    {
-        fprintf(stdout,
-                "Test system '%s' cannot run with %d ranks.\n"
-                "The supported numbers are: %s\n",
-                simulationName.c_str(), numRanksAvailable,
-                reportNumbersOfPpRanksSupported(simulationName).c_str());
-        return;
-    }
-
-    auto simulator1TrajectoryFileName = fileManager->getTemporaryFilePath("sim1.trr");
-    auto simulator1EdrFileName        = fileManager->getTemporaryFilePath("sim1.edr");
-    auto simulator2TrajectoryFileName = fileManager->getTemporaryFilePath("sim2.trr");
-    auto simulator2EdrFileName        = fileManager->getTemporaryFilePath("sim2.edr");
-    auto simulatorTprFileName         = fileManager->getTemporaryFilePath("sim.tpr");
+    CommandLine caller;
+    caller.append("grompp");
 
-    // prepare the .tpr file
+    for (const std::tuple<std::string, std::string>& option : options)
     {
-        // TODO evolve grompp to report the number of warnings issued, so
-        // tests always expect the right number.
-        CommandLine caller;
-        caller.append("grompp");
-        caller.addOption("-maxwarn", maxWarningsTolerated);
-        runner->tprFileName_ = simulatorTprFileName;
-        runner->useTopGroAndNdxFromDatabase(simulationName);
-        runner->useStringAsMdpFile(prepareMdpFileContents(mdpFieldValues));
-        EXPECT_EQ(0, runner->callGrompp(caller));
+        caller.addOption(std::get<0>(option).c_str(), std::get<1>(option));
     }
 
-    char* environmentVariableBackup = nullptr;
-    if (doEnvironmentVariable)
-    {
-        // save state of environment variable
-        environmentVariableBackup = getenv(environmentVariable.c_str());
-    }
+    EXPECT_EQ(0, runner->callGrompp(caller));
+}
 
-    // do the first mdrun
-    {
-        runner->fullPrecisionTrajectoryFileName_ = simulator1TrajectoryFileName;
-        runner->edrFileName_                     = simulator1EdrFileName;
-        runner->tprFileName_                     = simulatorTprFileName;
-        CommandLine simulator1Caller;
-        simulator1Caller.append("mdrun");
-        if (doEnvironmentVariable)
-        {
-            // unset environment variable
-            gmxUnsetenv(environmentVariable.c_str());
-        }
-        ASSERT_EQ(0, runner->callMdrun(simulator1Caller));
-    }
+void runMdrun(SimulationRunner* runner, const std::vector<SimulationOptionTuple>& options)
+{
+    CommandLine caller;
+    caller.append("mdrun");
 
-    // do the second mdrun
+    for (const std::tuple<std::string, std::string>& option : options)
     {
-        runner->fullPrecisionTrajectoryFileName_ = simulator2TrajectoryFileName;
-        runner->edrFileName_                     = simulator2EdrFileName;
-        runner->tprFileName_                     = simulatorTprFileName;
-        CommandLine simulator2Caller;
-        simulator2Caller.append("mdrun");
-        if (doEnvironmentVariable)
-        {
-            // set environment variable
-            gmxSetenv(environmentVariable.c_str(), "ON", true);
-        }
-        if (doRerun)
-        {
-            simulator2Caller.addOption("-rerun", simulator1TrajectoryFileName);
-        }
-        ASSERT_EQ(0, runner->callMdrun(simulator2Caller));
+        caller.addOption(std::get<0>(option).c_str(), std::get<1>(option));
     }
 
-    if (doEnvironmentVariable)
-    {
-        if (environmentVariableBackup != nullptr)
-        {
-            // set environment variable
-            gmxSetenv(environmentVariable.c_str(), environmentVariableBackup, true);
-        }
-        else
-        {
-            // unset environment variable
-            gmxUnsetenv(environmentVariable.c_str());
-        }
-    }
+    EXPECT_EQ(0, runner->callMdrun(caller));
+}
 
+void compareEnergies(const std::string&          edr1Name,
+                     const std::string&          edr2Name,
+                     const EnergyTermsToCompare& energyTermsToCompare,
+                     const MaxNumFrames          maxNumFrames)
+{
     // Build the functor that will compare energy frames on the chosen
     // energy terms.
-    EnergyComparison energyComparison(energyTermsToCompare);
+    EnergyComparison energyComparison(energyTermsToCompare, maxNumFrames);
 
     // Build the manager that will present matching pairs of frames to compare.
     //
@@ -169,31 +99,23 @@ void executeSimulatorComparisonTestImpl(TestFileManager*            fileManager,
     // names), for convenience. In the future, use a range.
     auto                                namesOfEnergiesToMatch = energyComparison.getEnergyNames();
     FramePairManager<EnergyFrameReader> energyManager(
-            openEnergyFileToReadTerms(simulator1EdrFileName, namesOfEnergiesToMatch),
-            openEnergyFileToReadTerms(simulator2EdrFileName, namesOfEnergiesToMatch));
+            openEnergyFileToReadTerms(edr1Name, namesOfEnergiesToMatch),
+            openEnergyFileToReadTerms(edr2Name, namesOfEnergiesToMatch));
     // Compare the energy frames.
     energyManager.compareAllFramePairs<EnergyFrame>(energyComparison);
+}
 
+void compareTrajectories(const std::string&          trajectory1Name,
+                         const std::string&          trajectory2Name,
+                         const TrajectoryComparison& trajectoryComparison)
+{
     // Build the manager that will present matching pairs of frames to compare
     FramePairManager<TrajectoryFrameReader> trajectoryManager(
-            std::make_unique<TrajectoryFrameReader>(simulator1TrajectoryFileName),
-            std::make_unique<TrajectoryFrameReader>(simulator2TrajectoryFileName));
+            std::make_unique<TrajectoryFrameReader>(trajectory1Name),
+            std::make_unique<TrajectoryFrameReader>(trajectory2Name));
     // Compare the trajectory frames.
     trajectoryManager.compareAllFramePairs<TrajectoryFrame>(trajectoryComparison);
 }
-} // namespace
-
-template<typename... Args>
-void executeSimulatorComparisonTest(const std::string& environmentVariable, Args&&... args)
-{
-    executeSimulatorComparisonTestImpl<true, false>(std::forward<Args>(args)..., environmentVariable);
-}
-
-template<typename... Args>
-void executeRerunTest(Args&&... args)
-{
-    executeSimulatorComparisonTestImpl<false, true>(std::forward<Args>(args)..., "");
-}
 
 } // namespace test
 } // namespace gmx
index b0e7843d96d80f7425bf38eabe87cb165d63ce0f..bc23c312c1204c3964625d94a569c29bd13ddc2c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include <string>
 
+#include "comparison_helpers.h"
+#include "energycomparison.h"
+#include "trajectorycomparison.h"
+
 namespace gmx
 {
 namespace test
 {
-/*!
- * \brief Run and compare a simulator run with and without an environment variable
- *
- * Run grompp, and repeat mdrun with and without the environment variable set.
- * Compare energies (via EnergyComparator) and trajectories.
- */
-template<typename... Args>
-void executeSimulatorComparisonTest(const std::string& environmentVariable, Args&&... args);
+class SimulationRunner;
 
-/*!
- * \brief Run and compare a simulator run to its rerun
- *
- * Run grompp, run mdrun and rerun the resulting trajectory.
- * Compare energies (via EnergyComparator) and trajectories.
- */
-template<typename... Args>
-void executeRerunTest(Args&&... args);
+typedef std::tuple<std::string, std::string> SimulationOptionTuple;
+
+void runGrompp(SimulationRunner*                         runner,
+               const std::vector<SimulationOptionTuple>& options = std::vector<SimulationOptionTuple>());
+
+void runMdrun(SimulationRunner*                         runner,
+              const std::vector<SimulationOptionTuple>& options = std::vector<SimulationOptionTuple>());
+
+void compareEnergies(const std::string&          edr1Name,
+                     const std::string&          edr2Name,
+                     const EnergyTermsToCompare& energyTermsToCompare,
+                     MaxNumFrames                maxNumFrams = MaxNumFrames::compareAllFrames());
+
+void compareTrajectories(const std::string&          trajectory1Name,
+                         const std::string&          trajectory2Name,
+                         const TrajectoryComparison& trajectoryComparison);
 
 } // namespace test
 } // namespace gmx
 
-// Including this here avoid having to put everything in the header file,
-// or to explicitly declare the templates (which would render the parameter
-// pack useless)
-#include "simulatorcomparison.cpp"
-
 #endif // GMX_PROGRAMS_MDRUN_TESTS_SIMULATORCOMPARISON_H
index 4be89e7fda996a4004baaff87ac01b5ea41c591d..7e108b027dbaac5d1319778ef270064626e63f14 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index beb6d33bd80a95b989b7fe4ec9fb94246c0ba430..105baba9107d215a03b8eb6ff0c356c91254f1a8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -228,7 +228,7 @@ TEST_F(MdrunTerminationTest, CheckpointRestartWithNoAppendWorksAndCannotLaterApp
         fourthPart.append("mdrun");
         fourthPart.addOption("-cpi", runner_.cptFileName_);
         fourthPart.addOption("-cpo", runner_.cptFileName_);
-        // TODO this is necessary, but ought not be. Is this the issue in Redmine #2804?
+        // TODO this is necessary, but ought not be. Is this the issue in Issue #2804?
         fourthPart.append("-noappend");
         runner_.edrFileName_ = fileManager_.getTemporaryFilePath(".part0004.edr");
         runner_.logFileName_ = fileManager_.getTemporaryFilePath(".part0004.log");
@@ -247,7 +247,7 @@ TEST_F(MdrunTerminationTest, CheckpointRestartWithNoAppendWorksAndCannotLaterApp
         fifthPart.append("mdrun");
         fifthPart.addOption("-cpi", runner_.cptFileName_);
         fifthPart.addOption("-cpo", runner_.cptFileName_);
-        // TODO this is necessary, but ought not be. Is this the issue in Redmine #2804?
+        // TODO this is necessary, but ought not be. Is this the issue in Issue #2804?
         fifthPart.append("-noappend");
         runner_.edrFileName_ = fileManager_.getTemporaryFilePath(".part0005.edr");
         runner_.logFileName_ = fileManager_.getTemporaryFilePath(".part0005.log");
index b3f9a7712dcf96bd5fa79354566dd4da86c976a8..90e6ed5125121523a1746e3b4ea01015ab296530 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index e70fd92ec072be78e72f65d8d034f7e588ec9ef8..72aea1dcef002cf2ed413ea73111962854be1ad4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,6 +38,7 @@
  * produced by mdrun.
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_mdrun_integration_tests
  */
 #include "gmxpre.h"
 #include "trajectorycomparison.h"
 
 #include <gmock/gmock.h>
+#include <gtest/gtest-spi.h>
 
 #include "gromacs/pbcutil/pbc.h"
 #include "gromacs/trajectory/trajectoryframe.h"
+#include "gromacs/utility/strconvert.h"
 
+#include "testutils/refdata.h"
 #include "testutils/testasserts.h"
 #include "testutils/testmatchers.h"
 
+#include "trajectoryreader.h"
+
 namespace gmx
 {
 namespace test
@@ -252,6 +258,11 @@ TrajectoryComparison::TrajectoryComparison(const TrajectoryFrameMatchSettings& m
 
 void TrajectoryComparison::operator()(const TrajectoryFrame& reference, const TrajectoryFrame& test) const
 {
+    if (numComparedFrames_ >= matchSettings_.maxNumTrajectoryFrames)
+    {
+        // Nothing should be compared
+        return;
+    }
     SCOPED_TRACE("Comparing trajectory reference frame " + reference.frameName()
                  + " and test frame " + test.frameName());
     EXPECT_EQ(reference.step(), test.step());
@@ -260,6 +271,163 @@ void TrajectoryComparison::operator()(const TrajectoryFrame& reference, const Tr
     compareCoordinates(reference, test, matchSettings_, tolerances_.coordinates);
     compareVelocities(reference, test, matchSettings_, tolerances_.velocities);
     compareForces(reference, test, matchSettings_, tolerances_.forces);
+    numComparedFrames_++;
+}
+
+/*! \brief Return whether the \c comparisonConditions and emptiness of
+ * the test frame means that a comparison should be attempted.
+ *
+ * This allows the framework to determine whether it is an error if a
+ * comparison cannot be made. */
+static bool shouldDoComparison(ArrayRef<const RVec> values, ComparisonConditions comparisonConditions)
+{
+    if (comparisonConditions == ComparisonConditions::NoComparison)
+    {
+        return false;
+    }
+    if (values.empty())
+    {
+        // Empty array ref is only acceptable if comparison is not required
+        if (comparisonConditions != ComparisonConditions::CompareIfTestFound
+            && comparisonConditions != ComparisonConditions::CompareIfBothFound)
+        {
+            ADD_FAILURE() << "Test frame lacked quantity for required comparison";
+        }
+        return false;
+    }
+    return true;
+}
+
+/*! \brief Compares the positions from \c frame to reference data
+ * according to the \c matchSettings and \c tolerance.
+ */
+static void checkPositionsAgainstReference(const TrajectoryFrame&              frame,
+                                           const TrajectoryFrameMatchSettings& matchSettings,
+                                           const TrajectoryTolerances&         tolerance,
+                                           TestReferenceChecker*               checker)
+{
+    SCOPED_TRACE("Comparing positions");
+    if (shouldDoComparison(frame.x(), matchSettings.coordinatesComparison))
+    {
+        auto positions = frame.x();
+        if (frame.hasBox() && (matchSettings.handlePbcIfPossible || matchSettings.requirePbcHandling))
+        {
+            positions = putAtomsInBox(frame);
+        }
+        if (!frame.hasBox() && matchSettings.requirePbcHandling)
+        {
+            ADD_FAILURE() << "Comparing positions required PBC handling, "
+                             "but the test frame did not have a box";
+            return;
+        }
+        checker->setDefaultTolerance(tolerance.coordinates);
+        std::string id = frame.frameName() + " X";
+        checker->checkSequence(std::begin(positions), std::end(positions), id.c_str());
+    }
+}
+
+/*! \brief Compares the velocities from \c frame to reference data
+ * according to the \c matchSettings and \c tolerance.
+ */
+static void checkVelocitiesAgainstReference(const TrajectoryFrame&              frame,
+                                            const TrajectoryFrameMatchSettings& matchSettings,
+                                            const TrajectoryTolerances&         tolerance,
+                                            TestReferenceChecker*               checker)
+{
+    SCOPED_TRACE("Comparing velocities");
+    if (shouldDoComparison(frame.v(), matchSettings.velocitiesComparison))
+    {
+        checker->setDefaultTolerance(tolerance.velocities);
+        std::string id = frame.frameName() + " V";
+        checker->checkSequence(std::begin(frame.v()), std::end(frame.v()), id.c_str());
+    }
+}
+
+/*! \brief Compares the forces from \c frame to reference data
+ * according to the \c matchSettings and \c tolerance.
+ */
+static void checkForcesAgainstReference(const TrajectoryFrame&              frame,
+                                        const TrajectoryFrameMatchSettings& matchSettings,
+                                        const TrajectoryTolerances&         tolerance,
+                                        TestReferenceChecker*               checker)
+{
+    SCOPED_TRACE("Comparing forces");
+    if (shouldDoComparison(frame.f(), matchSettings.forcesComparison))
+    {
+        checker->setDefaultTolerance(tolerance.forces);
+        std::string id = frame.frameName() + " F";
+        checker->checkSequence(std::begin(frame.f()), std::end(frame.f()), id.c_str());
+    }
+}
+
+/*! \brief Compares the box from \c frame to reference data
+ * according to the \c matchSettings and \c tolerance.
+ */
+static void checkBoxAgainstReference(const TrajectoryFrame&              frame,
+                                     const TrajectoryFrameMatchSettings& matchSettings,
+                                     const TrajectoryTolerances&         tolerance,
+                                     TestReferenceChecker*               checker)
+{
+    SCOPED_TRACE("Comparing box");
+    if (!matchSettings.mustCompareBox)
+    {
+        return;
+    }
+    if (!frame.hasBox())
+    {
+        ADD_FAILURE() << "Comparing the box was required, "
+                         "but the test frame did not have one";
+        return;
+    }
+    checker->setDefaultTolerance(tolerance.box);
+    // Do the comparison
+    for (int d = 0; d < DIM; ++d)
+    {
+        for (int dd = 0; dd < DIM; ++dd)
+        {
+            checker->checkReal(
+                    frame.box()[d][dd],
+                    (frame.frameName() + " Box[" + toString(d) + "][" + toString(dd) + "]").c_str());
+        }
+    }
+}
+
+void TrajectoryComparison::operator()(const TrajectoryFrame& frame, TestReferenceChecker* checker) const
+{
+    SCOPED_TRACE("Comparing trajectory frame " + frame.frameName() + " against reference data");
+    checkBoxAgainstReference(frame, matchSettings_, tolerances_, checker);
+    checkPositionsAgainstReference(frame, matchSettings_, tolerances_, checker);
+    checkVelocitiesAgainstReference(frame, matchSettings_, tolerances_, checker);
+    checkForcesAgainstReference(frame, matchSettings_, tolerances_, checker);
+}
+
+void checkTrajectoryAgainstReferenceData(const std::string&          trajectoryFilename,
+                                         const TrajectoryComparison& trajectoryComparison,
+                                         TestReferenceChecker*       checker)
+{
+    checkTrajectoryAgainstReferenceData(trajectoryFilename, trajectoryComparison, checker,
+                                        MaxNumFrames::compareAllFrames());
+}
+
+void checkTrajectoryAgainstReferenceData(const std::string&          trajectoryFilename,
+                                         const TrajectoryComparison& trajectoryComparison,
+                                         TestReferenceChecker*       checker,
+                                         MaxNumFrames                maxNumFrames)
+{
+    ;
+    TrajectoryFrameReader reader(trajectoryFilename);
+    unsigned int          counter = 0;
+    for (counter = 0; counter < maxNumFrames && reader.readNextFrame(); counter++)
+    {
+        auto frame = reader.frame();
+        trajectoryComparison(frame, checker);
+    }
+
+    if (counter == maxNumFrames && reader.readNextFrame())
+    {
+        // There would have been at least one more frame!
+        checker->disableUnusedEntriesCheck();
+    }
 }
 
 } // namespace test
index 444336efe0d6e43805a9f2a74110f1bdb40ed617..d2cc51495f0547c0b92f144478ee3a20f4116b09 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -38,6 +38,7 @@
  * produced by mdrun.
  *
  * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Pascal Merz <pascal.merz@me.com>
  * \ingroup module_mdrun_integration_tests
  */
 #ifndef GMX_PROGRAMS_MDRUN_TESTS_TRAJECTORYCOMPARISON_H
@@ -45,6 +46,8 @@
 
 #include "testutils/testasserts.h"
 
+#include "comparison_helpers.h"
+
 namespace gmx
 {
 
@@ -53,6 +56,8 @@ class TrajectoryFrame;
 namespace test
 {
 
+class TestReferenceChecker;
+
 /*! \internal
  * \brief Helper struct for testing different trajectory components with different tolerances. */
 struct TrajectoryTolerances
@@ -97,13 +102,15 @@ struct TrajectoryFrameMatchSettings
     ComparisonConditions velocitiesComparison = ComparisonConditions::CompareIfBothFound;
     //! Whether forces must be compared.
     ComparisonConditions forcesComparison = ComparisonConditions::CompareIfBothFound;
+    //! How many frames will be compared.
+    MaxNumFrames maxNumTrajectoryFrames = MaxNumFrames::compareAllFrames();
 };
 
 /*! \internal
- * \brief Function object to compare the fields of the two frames for
- * equality given the \c matchSettings_ and \c tolerances_.
+ * \brief Function object to compare the fields of two frames vs each others or one
+ * frame vs reference data for equality given the \c matchSettings_ and \c tolerances_.
  *
- * The two frames are required to have valid and matching values for
+ * If using two frames, they are required to have valid and matching values for
  * time and step. According to \c matchSettings_, box, position coordinates,
  * velocities and/or forces will be compared between frames, using the
  * \c tolerances_. Comparisons will only occur when both frames have
@@ -112,7 +119,16 @@ struct TrajectoryFrameMatchSettings
  * GoogleTest expectation failure will be given. If a comparison is
  * required by \c matchSettings_ but cannot be done because either (or
  * both) frames lack the requisite data, descriptive expectation
- * failures will be given. */
+ * failures will be given.
+ *
+ * When comparing a frame to reference data, according to \c matchSettings_,
+ * box, position coordinates, velocities and/or forces will be compared to
+ * reference data, using the \c tolerances_. If a comparison fails, a
+ * GoogleTest expectation failure will be given. If a comparison is
+ * required by \c matchSettings_ but cannot be done because the
+ * frame lacks the requisite data, descriptive expectation
+ * failures will be given.
+ */
 class TrajectoryComparison
 {
 public:
@@ -124,12 +140,45 @@ public:
     /*! \brief Compare reference with test given the \c
      * matchSettings_ within \c tolerances_ */
     void operator()(const TrajectoryFrame& reference, const TrajectoryFrame& test) const;
+    /*! \brief Compare frame to reference given the \c
+     * matchSettings_ within \c tolerances_ */
+    void operator()(const TrajectoryFrame& frame, TestReferenceChecker* checker) const;
+
+private:
     //! Specifies expected behavior in comparisons
     TrajectoryFrameMatchSettings matchSettings_;
     //! Trajectory fields to match with given tolerances.
     TrajectoryTolerances tolerances_;
+    /*! \brief The number of frames that have been compared until now
+     *
+     * This field is mutable because the need to update the flag
+     * when checking frames is merely an implementation detail,
+     * rather than a proper change of internal state triggered
+     * by the caller. */
+    mutable unsigned int numComparedFrames_ = 0;
 };
 
+/*! \internal
+ * \brief Helper function comparing a trajectory to reference.
+ *
+ * This opens a trajectory file, loops over its frames, and uses the
+ * \c trajectoryComparison and \c checker arguments to check each frame
+ * to reference data.
+ *
+ * Optionally, the max number of trajectory frames to be compared can
+ * be specified to allow to only test the first few frames of a
+ * simulation, e.g. to avoid failures due to numerical divergence.
+ */
+//! \{
+void checkTrajectoryAgainstReferenceData(const std::string&          trajectoryFilename,
+                                         const TrajectoryComparison& trajectoryComparison,
+                                         TestReferenceChecker*       checker);
+void checkTrajectoryAgainstReferenceData(const std::string&          trajectoryFilename,
+                                         const TrajectoryComparison& trajectoryComparison,
+                                         TestReferenceChecker*       checker,
+                                         MaxNumFrames                maxNumFrames);
+//! \}
+
 } // namespace test
 } // namespace gmx
 
index 3d7ea27ccf77eeafd06190b5f782f4c1382175f4..bf1cbc0335706189b258eed54aa5983a175a1f11 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2013, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 85506ef39f562915b2baee106e03e8a69fb16f08..940e84e5739f3d9af9421fc490c05fb2c42961a1 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2013, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 6d77a4ea271ad2801008ab09767e240ab3b88ca8..d5fbd842f365399b8f8365728134d6093229a1e5 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -226,7 +227,7 @@ void set_file(t_x11* x11, t_manager* man, const char* trajectory, const char* st
     snew(man->bHydro, sh.natoms);
     snew(bB, sh.natoms);
     read_tpx_top(status, nullptr, man->box, &man->natom, nullptr, nullptr, &man->top);
-    man->gpbc = gmx_rmpbc_init(&man->top.idef, -1, man->natom);
+    man->gpbc = gmx_rmpbc_init(&man->top.idef, PbcType::Unset, man->natom);
 
     man->natom = read_first_x(man->oenv, &man->status, trajectory, &(man->time), &(man->x), man->box);
     man->trajfile = gmx_strdup(trajectory);
@@ -363,7 +364,7 @@ static bool step_man(t_manager* man, int* nat)
                 put_atoms_in_triclinic_unitcell(ecenterDEF, man->box, atomsArrayRef);
                 break;
             case esbTrunc:
-                put_atoms_in_compact_unitcell(man->molw->ePBC, ecenterDEF, man->box, atomsArrayRef);
+                put_atoms_in_compact_unitcell(man->molw->pbcType, ecenterDEF, man->box, atomsArrayRef);
                 break;
             case esbRect:
             case esbNone:
@@ -621,7 +622,7 @@ t_manager* init_man(t_x11*            x11,
                     int               height,
                     unsigned long     fg,
                     unsigned long     bg,
-                    int               ePBC,
+                    PbcType           pbcType,
                     matrix            box,
                     gmx_output_env_t* oenv)
 {
@@ -640,7 +641,7 @@ t_manager* init_man(t_x11*            x11,
 
     /* The order of creating windows is important for the stacking order */
     /* Mol Window */
-    man->molw = init_mw(x11, man->wd.self, 0, 0, 1, 1, WHITE, BLUE, ePBC, box);
+    man->molw = init_mw(x11, man->wd.self, 0, 0, 1, 1, WHITE, BLUE, pbcType, box);
 
     /* Title Window */
     InitWin(&(man->title), 0, 0, 1, 1, 0, nullptr);
index d0461104d045781888d9321ec700d0f31c965fd7..bdd7727b3da62273fb60400f67c3b5fda23c7f4e 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,6 +52,7 @@
 #include "xutil.h"
 
 struct gmx_output_env_t;
+enum class PbcType : int;
 
 /* Some window sizes */
 #define EWIDTH 200
@@ -98,7 +99,7 @@ typedef struct
     t_windata wd;            /* Mol window structure                   */
     bool      bShowHydrogen; /* Show Hydrogens?                        */
     int       bond_type;     /* Show one of the above bondtypes      */
-    int       ePBC;          /* PBC type                             */
+    PbcType   pbcType;       /* PBC type                             */
     int       boxtype;       /* Rectangular, Tric, TruncOct (display)*/
     int       realbox;       /* Property of the real box             */
 } t_molwin;
@@ -178,7 +179,7 @@ extern t_manager* init_man(t_x11*            x11,
                            int               height,
                            unsigned long     fg,
                            unsigned long     bg,
-                           int               ePBC,
+                           PbcType           pbcType,
                            matrix            box,
                            gmx_output_env_t* oenv);
 /* Initiate the display manager */
index 21f502e29cb9c39a038fbb1931ec514c65ad3fec..1c1123ea6cc1e17de1e47703ee0483626be380b4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2013, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -97,11 +98,11 @@ static bool MWCallBack(t_x11* x11, XEvent* event, Window /*w*/, void* data)
     return false;
 }
 
-static void set_def(t_molwin* mw, int ePBC, matrix box)
+static void set_def(t_molwin* mw, PbcType pbcType, matrix box)
 {
     mw->bShowHydrogen = true;
     mw->bond_type     = eBFat;
-    mw->ePBC          = ePBC;
+    mw->pbcType       = pbcType;
     mw->boxtype       = esbRect;
     mw->realbox       = TRICLINIC(box) ? esbTri : esbRect;
 }
@@ -114,13 +115,13 @@ t_molwin* init_mw(t_x11*        x11,
                   int           height,
                   unsigned long fg,
                   unsigned long bg,
-                  int           ePBC,
+                  PbcType       pbcType,
                   matrix        box)
 {
     t_molwin* mw;
 
     snew(mw, 1);
-    set_def(mw, ePBC, box);
+    set_def(mw, pbcType, box);
 
     InitWin(&mw->wd, x, y, width, height, 1, "Mol Window");
 
index ab92eda497dbdceb63e3f6c2aaf02ea3004881b2..4ce99f45ff4a49f5f2598f51c1ec8bf4777432b8 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -50,7 +50,7 @@ extern t_molwin* init_mw(t_x11*        x11,
                          int           height,
                          unsigned long fg,
                          unsigned long bg,
-                         int           ePBC,
+                         PbcType       pbcType,
                          matrix        box);
 /* Create the molecule window using the x,y etc. */
 
index f3c5b5ce84669f7e8e03f6afb191901093ada1b4..15395ab0903629eb4ce50398b60866db74ad6180 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -130,7 +130,7 @@ static bool HandleClient(t_x11* x11, int ID, t_gmx* gmx)
         case IDEXPORT: ShowDlg(gmx->dlgs[edExport]); break;
         case IDDOEXPORT:
             write_sto_conf(gmx->confout, *gmx->man->top.name, &(gmx->man->top.atoms), gmx->man->x,
-                           nullptr, gmx->man->molw->ePBC, gmx->man->box);
+                           nullptr, gmx->man->molw->pbcType, gmx->man->box);
             break;
         case IDQUIT: show_mb(gmx, emQuit); break;
         case IDTERM: done_gmx(x11, gmx); return true;
@@ -241,7 +241,7 @@ static void init_gmx(t_x11* x11, char* program, int nfile, t_filenm fnm[], gmx_o
     int          w0, h0;
     int          natom, natom_trx;
     t_topology   top;
-    int          ePBC;
+    PbcType      pbcType;
     matrix       box;
     t_trxframe   fr;
     t_trxstatus* status;
@@ -249,7 +249,7 @@ static void init_gmx(t_x11* x11, char* program, int nfile, t_filenm fnm[], gmx_o
     snew(gmx, 1);
     snew(gmx->wd, 1);
 
-    ePBC = read_tpx_top(ftp2fn(efTPR, nfile, fnm), nullptr, box, &natom, nullptr, nullptr, &top);
+    pbcType = read_tpx_top(ftp2fn(efTPR, nfile, fnm), nullptr, box, &natom, nullptr, nullptr, &top);
 
     read_first_frame(oenv, &status, ftp2fn(efTRX, nfile, fnm), &fr, TRX_DONT_SKIP);
     close_trx(status);
@@ -275,7 +275,7 @@ static void init_gmx(t_x11* x11, char* program, int nfile, t_filenm fnm[], gmx_o
 
     /* The order of creating windows is important here! */
     /* Manager */
-    gmx->man  = init_man(x11, gmx->wd->self, 0, 0, 1, 1, WHITE, BLACK, ePBC, box, oenv);
+    gmx->man  = init_man(x11, gmx->wd->self, 0, 0, 1, 1, WHITE, BLACK, pbcType, box, oenv);
     gmx->logo = init_logo(x11, gmx->wd->self, false);
 
     /* Now put all windows in the proper place */
index bb8e8b777482a822563c4c991c0a2e7a87f72107..5e1c1ad329c173fa377735a5a20d4c2501716197 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2011,2012,2013,2014,2015, The GROMACS development team.
+# Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
 # Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
@@ -54,6 +54,8 @@ set(TESTUTILS_SOURCES
     testasserts.cpp
     testfilemanager.cpp
     testfileredirector.cpp
+    test_device.cpp
+    test_hardware_environment.cpp
     testinit.cpp
     testmatchers.cpp
     testoptions.cpp
@@ -66,7 +68,25 @@ if(NOT HAVE_TINYXML2)
     list(APPEND TESTUTILS_SOURCES ../external/tinyxml2/tinyxml2.cpp)
 endif()
 
-add_library(testutils STATIC ${UNITTEST_TARGET_OPTIONS} ${TESTUTILS_SOURCES})
+if (GMX_GPU_CUDA)
+    # Work around FindCUDA that prevents using target_link_libraries()
+    # with keywords otherwise...
+    set(CUDA_LIBRARIES PRIVATE ${CUDA_LIBRARIES})
+    if (NOT GMX_CLANG_CUDA)
+        gmx_cuda_add_library(testutils ${TESTUTILS_SOURCES})
+    else()
+        add_library(testutils STATIC ${TESTUTILS_SOURCES})
+    endif()
+    target_link_libraries(testutils PRIVATE ${CUDA_CUFFT_LIBRARIES})
+else()
+    add_library(testutils STATIC ${UNITTEST_TARGET_OPTIONS} ${TESTUTILS_SOURCES})
+endif()
+
+if (GMX_GPU_SYCL)
+    set_source_files_properties(test_device.cpp
+      PROPERTIES COMPILE_FLAGS "${SYCL_CXX_FLAGS}")
+endif()
+
 gmx_target_compile_options(testutils)
 target_compile_definitions(testutils PRIVATE HAVE_CONFIG_H)
 target_include_directories(testutils SYSTEM BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include)
index 1c4e0d71af97a5f7708fe24e4bf0ed80df9a5021..6e7586654a699f11590ab9be7532ca8ce45e7792 100644 (file)
@@ -1,7 +1,8 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2011,2012,2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+# Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -33,6 +34,7 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 include(CMakeParseArguments)
+include(gmxClangCudaUtils)
 
 function (gmx_add_unit_test_library NAME)
     if (GMX_BUILD_UNITTESTS AND BUILD_TESTING)
@@ -41,21 +43,52 @@ function (gmx_add_unit_test_library NAME)
         target_compile_definitions(${NAME} PRIVATE HAVE_CONFIG_H)
         target_include_directories(${NAME} SYSTEM BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include)
         target_link_libraries(${NAME} PRIVATE testutils gmock)
-        # clang-3.6 warns about a number of issues that are not reported by more modern compilers
-        # and we know they are not real issues. So we only check that it can compile without error
-        # but ignore all warnings.
-        if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION MATCHES "^3\.6")
-            target_compile_options(${NAME} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-w>)
+        if(GMX_CLANG_TIDY)
+            set_target_properties(${NAME} PROPERTIES CXX_CLANG_TIDY
+                "${CLANG_TIDY_EXE};-warnings-as-errors=*;-header-filter=.*")
+        endif()
+        if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7")
+            target_compile_options(${NAME} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Weverything ${IGNORED_CLANG_ALL_WARNINGS} -Wno-gnu-zero-variadic-macro-arguments -Wno-zero-as-null-pointer-constant -Wno-missing-variable-declarations>)
         endif()
 
     endif()
 endfunction ()
 
+# This function creates a GoogleTest test executable for a module.  It
+# hides all the complexity of how to treat different source files
+# under different configuration conditions. It should be extended
+# if we ever support another GPU compilation approach.
+#
+# It can be called with extra options and arguments:
+#   MPI
+#     To trigger the ctest runner to run this test with multiple ranks
+#   HARDWARE_DETECTION
+#     To trigger the test executable setup code to run hardware detection
+#   CPP_SOURCE_FILES          file1.cpp file2.cpp ...
+#     All the normal C++ .cpp source files
+#   GPU_CPP_SOURCE_FILES  file1.cpp file2.cpp ...
+#     All the C++ .cpp source files that are always needed, but must be
+#     compiled in the way that suits GMX_GPU.
+#   CUDA_CU_SOURCE_FILES      file1.cu  file2.cu  ...
+#     All the normal CUDA .cu source files
+#   OPENCL_CPP_SOURCE_FILES   file1.cpp file2.cpp ...
+#     All the other C++ .cpp source files needed only with OpenCL
+#   SYCL_CPP_SOURCE_FILES   file1.cpp file2.cpp ...
+#     All the C++ .cpp source files needed only with SYCL
+#   NON_GPU_CPP_SOURCE_FILES  file1.cpp file2.cpp ...
+#     All the other C++ .cpp source files needed only with neither OpenCL nor CUDA nor SYCL
 function (gmx_add_gtest_executable EXENAME)
     if (GMX_BUILD_UNITTESTS AND BUILD_TESTING)
         set(_options MPI HARDWARE_DETECTION)
-        cmake_parse_arguments(ARG "${_options}" "" "" ${ARGN})
-        set(_source_files ${ARG_UNPARSED_ARGUMENTS})
+        set(_multi_value_keywords
+            CPP_SOURCE_FILES
+            CUDA_CU_SOURCE_FILES
+            GPU_CPP_SOURCE_FILES
+            OPENCL_CPP_SOURCE_FILES
+            SYCL_CPP_SOURCE_FILES
+            NON_GPU_CPP_SOURCE_FILES
+            )
+        cmake_parse_arguments(ARG "${_options}" "" "${_multi_value_keywords}" ${ARGN})
 
         file(RELATIVE_PATH _input_files_path ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
         set(_temporary_files_path "${CMAKE_CURRENT_BINARY_DIR}/Testing/Temporary")
@@ -79,8 +112,48 @@ function (gmx_add_gtest_executable EXENAME)
                  TEST_USES_HARDWARE_DETECTION=true)
         endif()
 
-        add_executable(${EXENAME} ${UNITTEST_TARGET_OPTIONS}
-            ${_source_files} ${TESTUTILS_DIR}/unittest_main.cpp)
+        if (GMX_GPU_CUDA AND NOT GMX_CLANG_CUDA)
+            # Work around FindCUDA that prevents using target_link_libraries()
+            # with keywords otherwise...
+            set(CUDA_LIBRARIES PRIVATE ${CUDA_LIBRARIES})
+            cuda_add_executable(${EXENAME} ${UNITTEST_TARGET_OPTIONS}
+                ${ARG_CPP_SOURCE_FILES}
+                ${ARG_CUDA_CU_SOURCE_FILES}
+                ${ARG_GPU_CPP_SOURCE_FILES}
+                ${TESTUTILS_DIR}/unittest_main.cpp)
+        else()
+            add_executable(${EXENAME} ${UNITTEST_TARGET_OPTIONS}
+                ${ARG_CPP_SOURCE_FILES}
+                ${TESTUTILS_DIR}/unittest_main.cpp)
+        endif()
+
+        if (GMX_GPU_CUDA)
+            if (GMX_CLANG_CUDA)
+                target_sources(${EXENAME} PRIVATE
+                    ${ARG_CUDA_CU_SOURCE_FILES}
+                    ${ARG_GPU_CPP_SOURCE_FILES})
+                set_source_files_properties(${ARG_GPU_CPP_SOURCE_FILES} PROPERTIES CUDA_SOURCE_PROPERTY_FORMAT OBJ)
+                gmx_compile_cuda_file_with_clang(${ARG_CUDA_CU_SOURCE_FILES})
+                if(ARG_CUDA_CU_SOURCE_FILES OR ARG_GPU_CPP_SOURCE_FILES)
+                    target_link_libraries(${EXENAME} PRIVATE ${GMX_EXTRA_LIBRARIES})
+                endif()
+            endif()
+        elseif (GMX_GPU_OPENCL)
+            target_sources(${EXENAME} PRIVATE ${ARG_OPENCL_CPP_SOURCE_FILES} ${ARG_GPU_CPP_SOURCE_FILES})
+            if(ARG_OPENCL_CPP_SOURCE_FILES OR ARG_GPU_CPP_SOURCE_FILES)
+                target_link_libraries(${EXENAME} PRIVATE ${OpenCL_LIBRARIES})
+            endif()
+        elseif (GMX_GPU_SYCL)
+            target_sources(${EXENAME} PRIVATE ${ARG_SYCL_CPP_SOURCE_FILES} ${ARG_GPU_CPP_SOURCE_FILES})
+            set_source_files_properties(${ARG_GPU_CPP_SOURCE_FILES} PROPERTIES COMPILE_FLAGS "${SYCL_CXX_FLAGS}")
+            set_source_files_properties(${ARG_SYCL_CPP_SOURCE_FILES} PROPERTIES COMPILE_FLAGS "${SYCL_CXX_FLAGS}")
+            if(ARG_SYCL_CPP_SOURCE_FILES OR ARG_GPU_CPP_SOURCE_FILES)
+                target_link_libraries(${EXENAME} PRIVATE ${SYCL_CXX_FLAGS})
+            endif()
+        else()
+            target_sources(${EXENAME} PRIVATE ${ARG_NON_GPU_CPP_SOURCE_FILES} ${ARG_GPU_CPP_SOURCE_FILES})
+        endif()
+
         gmx_target_compile_options(${EXENAME})
         target_compile_definitions(${EXENAME} PRIVATE HAVE_CONFIG_H ${EXTRA_COMPILE_DEFINITIONS})
         target_include_directories(${EXENAME} SYSTEM BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include)
@@ -89,6 +162,10 @@ function (gmx_add_gtest_executable EXENAME)
         # use for gmx::compat::optional. These are included as system
         # headers so that no warnings are issued from them.
         target_include_directories(${EXENAME} SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/src/external)
+        if(CYGWIN)
+            # Ensure GoogleTest headers can find POSIX things needed
+            target_compile_definitions(${EXENAME} PRIVATE _POSIX_C_SOURCE=200809L)
+        endif()
 
         target_link_libraries(${EXENAME} PRIVATE
             testutils libgromacs gmock
@@ -98,7 +175,7 @@ function (gmx_add_gtest_executable EXENAME)
             set_target_properties(${EXENAME} PROPERTIES CXX_CLANG_TIDY
                 "${CLANG_TIDY_EXE};-warnings-as-errors=*;-header-filter=.*")
         endif()
-        if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION MATCHES "^6\.0")
+        if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7")
             target_compile_options(${EXENAME} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Weverything ${IGNORED_CLANG_ALL_WARNINGS} -Wno-gnu-zero-variadic-macro-arguments -Wno-zero-as-null-pointer-constant -Wno-missing-variable-declarations>)
         endif()
         # clang-3.6 warns about a number of issues that are not reported by more modern compilers
@@ -117,13 +194,14 @@ endfunction()
 #   INTEGRATION_TEST      requires the use of the IntegrationTest label in CTest
 #   SLOW_TEST             requires the use of the SlowTest label in CTest, and
 #                         increase the length of the ctest timeout.
+#   IGNORE_LEAKS          Skip some memory safety checks.
 #
 # TODO When a test case needs it, generalize the MPI_RANKS mechanism so
 # that ctest can run the test binary over a range of numbers of MPI
 # ranks.
 function (gmx_register_gtest_test NAME EXENAME)
     if (GMX_BUILD_UNITTESTS AND BUILD_TESTING)
-        set(_options INTEGRATION_TEST SLOW_TEST)
+        set(_options INTEGRATION_TEST SLOW_TEST IGNORE_LEAKS)
         set(_one_value_args MPI_RANKS OPENMP_THREADS)
         cmake_parse_arguments(ARG "${_options}" "${_one_value_args}" "" ${ARGN})
         set(_xml_path ${CMAKE_BINARY_DIR}/Testing/Temporary/${NAME}.xml)
@@ -135,22 +213,23 @@ function (gmx_register_gtest_test NAME EXENAME)
             # Both OpenCL (from JIT) and ThreadSanitizer (from how it
             # checks) can take signficantly more time than other
             # configurations.
-            if (GMX_USE_OPENCL)
+            if (GMX_GPU_OPENCL)
                 set(_timeout 240)
             elseif (${CMAKE_BUILD_TYPE} STREQUAL TSAN)
                 set(_timeout 300)
             else()
                 set(_timeout 120)
             endif()
-            gmx_get_test_prefix_cmd(_prefix_cmd IGNORE_LEAKS)
         elseif (ARG_SLOW_TEST)
             list(APPEND _labels SlowTest)
             set(_timeout 480)
-            gmx_get_test_prefix_cmd(_prefix_cmd IGNORE_LEAKS)
         else()
             list(APPEND _labels UnitTest)
             gmx_get_test_prefix_cmd(_prefix_cmd)
         endif()
+        if (ARG_IGNORE_LEAKS)
+            gmx_get_test_prefix_cmd(_prefix_cmd IGNORE_LEAKS)
+        endif ()
         set(_cmd ${_prefix_cmd} $<TARGET_FILE:${EXENAME}>)
         if (ARG_OPENMP_THREADS)
             if (GMX_OPENMP)
index 32051396b4926305866a823649085467dd7a645d..1c6bdc0f9f8b1b20f53199be6e238e7cc35305f8 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 1c2aa2c8677484fc103628bfd8ce8819c6c09892..a06ddc913eb4f50c371fc51b6124ec81205170ef 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 3b65ed95aa2fc3b5552f5aa22c23a2f7bbff3c3a..8c92d6754f5e4256d2bdd0ab53dee71690222523 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2011-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -53,6 +53,7 @@
 
 #include <gtest/gtest.h>
 
+#include "gromacs/math/vectypes.h"
 #include "gromacs/options/basicoptions.h"
 #include "gromacs/options/ioptionscontainer.h"
 #include "gromacs/utility/any.h"
@@ -154,7 +155,7 @@ typedef std::shared_ptr<internal::TestReferenceDataImpl> TestReferenceDataImplPo
  */
 TestReferenceDataImplPointer g_referenceData;
 //! Global reference data mode set with setReferenceDataMode().
-ReferenceDataMode g_referenceDataMode = erefdataCompare;
+ReferenceDataMode g_referenceDataMode = ReferenceDataMode::Compare;
 
 //! Returns the global reference data mode.
 ReferenceDataMode getReferenceDataMode()
@@ -268,10 +269,11 @@ void checkUnusedEntries(const ReferenceDataEntry& root, const std::string& rootP
 
 void initReferenceData(IOptionsContainer* options)
 {
-    // Needs to correspond to the enum order in refdata.h.
-    const char* const refDataEnum[] = { "check", "create", "update-changed", "update-all" };
+    static const gmx::EnumerationArray<ReferenceDataMode, const char*> s_refDataNames = {
+        { "check", "create", "update-changed", "update-all" }
+    };
     options->addOption(EnumOption<ReferenceDataMode>("ref-data")
-                               .enumValue(refDataEnum)
+                               .enumValue(s_refDataNames)
                                .store(&g_referenceDataMode)
                                .description("Operation mode for tests that use reference data"));
     ::testing::UnitTest::GetInstance()->listeners().Append(new ReferenceDataTestEventListener);
@@ -296,13 +298,13 @@ TestReferenceDataImpl::TestReferenceDataImpl(ReferenceDataMode mode, bool bSelfT
 
     switch (mode)
     {
-        case erefdataCompare:
+        case ReferenceDataMode::Compare:
             if (File::exists(fullFilename_, File::throwOnError))
             {
                 compareRootEntry_ = readReferenceDataFile(fullFilename_);
             }
             break;
-        case erefdataCreateMissing:
+        case ReferenceDataMode::CreateMissing:
             if (File::exists(fullFilename_, File::throwOnError))
             {
                 compareRootEntry_ = readReferenceDataFile(fullFilename_);
@@ -313,7 +315,7 @@ TestReferenceDataImpl::TestReferenceDataImpl(ReferenceDataMode mode, bool bSelfT
                 outputRootEntry_  = ReferenceDataEntry::createRoot();
             }
             break;
-        case erefdataUpdateChanged:
+        case ReferenceDataMode::UpdateChanged:
             if (File::exists(fullFilename_, File::throwOnError))
             {
                 compareRootEntry_ = readReferenceDataFile(fullFilename_);
@@ -325,10 +327,11 @@ TestReferenceDataImpl::TestReferenceDataImpl(ReferenceDataMode mode, bool bSelfT
             outputRootEntry_          = ReferenceDataEntry::createRoot();
             updateMismatchingEntries_ = true;
             break;
-        case erefdataUpdateAll:
+        case ReferenceDataMode::UpdateAll:
             compareRootEntry_ = ReferenceDataEntry::createRoot();
             outputRootEntry_  = ReferenceDataEntry::createRoot();
             break;
+        case ReferenceDataMode::Count: GMX_THROW(InternalError("Invalid reference data mode"));
     }
 }
 
@@ -418,19 +421,19 @@ public:
     std::string appendPath(const char* id) const;
 
     //! Creates an entry with given parameters and fills it with \p checker.
-    ReferenceDataEntry::EntryPointer createEntry(const char*                       type,
-                                                 const char*                       id,
-                                                 const IReferenceDataEntryChecker& checker) const
+    static ReferenceDataEntry::EntryPointer createEntry(const char*                       type,
+                                                        const char*                       id,
+                                                        const IReferenceDataEntryChecker& checker)
     {
         ReferenceDataEntry::EntryPointer entry(new ReferenceDataEntry(type, id));
         checker.fillEntry(entry.get());
         return entry;
     }
     //! Checks an entry for correct type and using \p checker.
-    ::testing::AssertionResult checkEntry(const ReferenceDataEntry&         entry,
-                                          const std::string&                fullId,
-                                          const char*                       type,
-                                          const IReferenceDataEntryChecker& checker) const
+    static ::testing::AssertionResult checkEntry(const ReferenceDataEntry&         entry,
+                                                 const std::string&                fullId,
+                                                 const char*                       type,
+                                                 const IReferenceDataEntryChecker& checker)
     {
         if (entry.type() != type)
         {
@@ -754,6 +757,14 @@ void TestReferenceChecker::checkUnusedEntries()
     }
 }
 
+void TestReferenceChecker::disableUnusedEntriesCheck()
+{
+    if (impl_->compareRootEntry_)
+    {
+        impl_->compareRootEntry_->setCheckedIncludingChildren();
+    }
+}
+
 
 bool TestReferenceChecker::checkPresent(bool bPresent, const char* id)
 {
@@ -943,7 +954,7 @@ void TestReferenceChecker::checkRealFromString(const std::string& value, const c
 }
 
 
-void TestReferenceChecker::checkVector(const int value[3], const char* id)
+void TestReferenceChecker::checkVector(const BasicVector<int>& value, const char* id)
 {
     TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
     compound.checkInteger(value[0], "X");
@@ -952,7 +963,7 @@ void TestReferenceChecker::checkVector(const int value[3], const char* id)
 }
 
 
-void TestReferenceChecker::checkVector(const float value[3], const char* id)
+void TestReferenceChecker::checkVector(const BasicVector<float>& value, const char* id)
 {
     TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
     compound.checkReal(value[0], "X");
@@ -961,7 +972,7 @@ void TestReferenceChecker::checkVector(const float value[3], const char* id)
 }
 
 
-void TestReferenceChecker::checkVector(const double value[3], const char* id)
+void TestReferenceChecker::checkVector(const BasicVector<double>& value, const char* id)
 {
     TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
     compound.checkReal(value[0], "X");
@@ -970,6 +981,24 @@ void TestReferenceChecker::checkVector(const double value[3], const char* id)
 }
 
 
+void TestReferenceChecker::checkVector(const int value[3], const char* id)
+{
+    checkVector(BasicVector<int>(value), id);
+}
+
+
+void TestReferenceChecker::checkVector(const float value[3], const char* id)
+{
+    checkVector(BasicVector<float>(value), id);
+}
+
+
+void TestReferenceChecker::checkVector(const double value[3], const char* id)
+{
+    checkVector(BasicVector<double>(value), id);
+}
+
+
 void TestReferenceChecker::checkAny(const Any& any, const char* id)
 {
     if (any.isType<bool>())
index a33069170b07ca4eb5d95f8b2e5b864177cdc023..78b0449b84035230e7e58aa75e0c9b79400b5f91 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2011-2018, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -61,6 +61,9 @@ class KeyValueTreeObject;
 class KeyValueTreeValue;
 class Any;
 
+template<typename ValueType>
+class BasicVector;
+
 namespace test
 {
 
@@ -73,7 +76,7 @@ class FloatingPointTolerance;
  *
  * \ingroup module_testutils
  */
-enum ReferenceDataMode
+enum class ReferenceDataMode : int
 {
     /*! \brief
      * Compare to existing reference data.
@@ -81,36 +84,38 @@ enum ReferenceDataMode
      * If reference data does not exist, or if the test results differ from
      * those in the reference data, the test fails.
      */
-    erefdataCompare,
+    Compare,
     /*! \brief
      * Create missing reference data.
      *
      * If reference data does not exist for a test, that test behaves as if
-     * ::erefdataUpdateAll had been specified.  Tests for which reference data
-     * exists, behave like with ::erefdataCompare.
+     * ReferenceDataMode::UpdateAll had been specified.  Tests for which reference data
+     * exists, behave like with ReferenceDataMode::Compare.
      */
-    erefdataCreateMissing,
+    CreateMissing,
     /*! \brief
      * Update reference data that does not pass comparison.
      *
      * Tests utilizing reference data should always pass in this mode unless
      * there is an I/O error.
      */
-    erefdataUpdateChanged,
+    UpdateChanged,
     /*! \brief
      * Update reference data, overwriting old data.
      *
      * Tests utilizing reference data should always pass in this mode unless
      * there is an I/O error.
      */
-    erefdataUpdateAll
+    UpdateAll,
+    //! Marks the end of the enum
+    Count
 };
 
 /*! \libinternal \brief
  * Initializes reference data handling.
  *
  * Adds command-line options to \p options to set the reference data mode.
- * By default, ::erefdataCompare is used, but ``--ref-data create`` or
+ * By default, ReferenceDataMode::Compare is used, but ``--ref-data create`` or
  * ``--ref-data update`` can be used to change it.
  *
  * This function is automatically called by initTestUtils().
@@ -150,19 +155,26 @@ class TestReferenceDataImpl;
  * \code
    int functionToTest(int param);
 
+   namespace gmx
+   {
+   namespace test
+   {
    TEST(MyTest, SimpleTest)
    {
-       gmx::test::TestReferenceData data;
+       TestReferenceData data;
 
-       gmx::test::TestReferenceChecker checker(data.rootChecker());
+       TestReferenceChecker checker(data.rootChecker());
        checker.checkInteger(functionToTest(3), "ValueWith3");
        checker.checkInteger(functionToTest(5), "ValueWith5");
-       gmx::test::TestReferenceChecker compound(
+       TestReferenceChecker compound(
                checker.checkCompound("CustomCompound", "Item"));
        compound.checkInteger(function2ToTest(3), "ValueWith3");
        compound.checkInteger(function2ToTest(5), "ValueWith5");
        checker.checkInteger(functionToTest(4), "ValueWith4");
+       checker.checkVector(functionProducingRVec(), "Describe The RVec");
    }
+   } // namespace test
+   } // namespace gmx
  * \endcode
  *
  * If rootChecker() is never called, no comparison is done (i.e., missing
@@ -218,7 +230,8 @@ private:
 /*! \libinternal \brief
  * Handles comparison to test reference data.
  *
- * Every check*() method takes an id string as the last parameter.  This id is
+ * Every check*() method takes an id string as th
+ * e last parameter.  This id is
  * used to uniquely identify the value in the reference data, and it makes the
  * output XML more human-friendly and more robust to errors.  The id can be
  * NULL; in this case, multiple elements with no id are created, and they will
@@ -294,6 +307,12 @@ public:
      */
     void checkUnusedEntries();
 
+    /*! \brief Disables checking for unused entries
+     *
+     * \see checkUnusedEntries()
+     */
+    void disableUnusedEntriesCheck();
+
     /*! \brief
      * Checks whether a data item is present.
      *
@@ -377,6 +396,12 @@ public:
     void checkVector(const float value[3], const char* id);
     //! Check a vector of three double-precision floating point values.
     void checkVector(const double value[3], const char* id);
+    //! Check a BasicVector of ints, ie. IVec
+    void checkVector(const BasicVector<int>& value, const char* id);
+    //! Check a BasicVector of floats, ie. RVec
+    void checkVector(const BasicVector<float>& value, const char* id);
+    //! Check a BasicVector of doubles, ie. DVec
+    void checkVector(const BasicVector<double>& value, const char* id);
     //! Check a single floating-point value from a string.
     void checkRealFromString(const std::string& value, const char* id);
     //! Checks a any value that contains a supported simple type.
@@ -444,6 +469,12 @@ public:
     void checkValue(const float value[3], const char* id) { checkVector(value, id); }
     //! Check a vector of three double-precision floating point values.
     void checkValue(const double value[3], const char* id) { checkVector(value, id); }
+    //! Check a BasicVector of integer values, ie. IVec.
+    void checkValue(const BasicVector<int>& value, const char* id) { checkVector(value, id); }
+    //! Check a BasicVector of float values, ie. RVec.
+    void checkValue(const BasicVector<float>& value, const char* id) { checkVector(value, id); }
+    //! Check a BasicVector of double values, ie. DVec.
+    void checkValue(const BasicVector<double>& value, const char* id) { checkVector(value, id); }
     //! Check a generic key-value tree value.
     void checkValue(const KeyValueTreeValue& value, const char* id)
     {
diff --git a/src/testutils/simulationdatabase/dipoles.gro b/src/testutils/simulationdatabase/dipoles.gro
new file mode 100644 (file)
index 0000000..26234ef
--- /dev/null
@@ -0,0 +1,7 @@
+Two simple dipoles
+ 4
+    1DIP     C     1   1.000  -0.140   1.000  0.0000  0.0000  0.0000
+    1DIP     C     2   1.000   0.140   1.200  0.0000  0.0000  0.0000
+    2DIP     C     3   3.900   2.000   3.000  0.0000  0.0000  0.0000
+    2DIP     C     4   4.100   2.000   3.200  0.0000  0.0000  0.0000
+   4.00000   4.00000   4.00000
diff --git a/src/testutils/simulationdatabase/dipoles.ndx b/src/testutils/simulationdatabase/dipoles.ndx
new file mode 100644 (file)
index 0000000..779e460
--- /dev/null
@@ -0,0 +1,2 @@
+[ System ]
+   1    2    3    4
diff --git a/src/testutils/simulationdatabase/dipoles.top b/src/testutils/simulationdatabase/dipoles.top
new file mode 100644 (file)
index 0000000..aa81777
--- /dev/null
@@ -0,0 +1,28 @@
+; Simple dipoles
+
+[ defaults ]
+; nbfunc comb-rule
+  1      1
+
+[ atomtypes ]
+; name  at.num       mass    charge   ptype     sigma      epsilon
+  C     6            12.01   0.0      A         0.3        1.0
+
+[ molecule_type ]
+Dipole   1
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge
+     1      C      1     DIP     C      1     -1
+     2      C      1     DIP     C      2     1
+
+[ bonds ]
+1  2  1  0.28 10000
+
+[ system ]
+; Name
+Dipoles
+
+[ molecules ]
+; Compound        #mols
+Dipole            2
diff --git a/src/testutils/simulationdatabase/freeenergy/README.txt b/src/testutils/simulationdatabase/freeenergy/README.txt
new file mode 100644 (file)
index 0000000..4c947af
--- /dev/null
@@ -0,0 +1,121 @@
+General information:
+
+Defaults for tests unless otherwise stated:
+integrator               = md
+dt                       = 0.001
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-print-energy        = no
+
+Test topology:
+
+  The molecule is acetamide in a water box, but far less than liquid
+  density (56 for a box of ~25 nm^3) to reduce expense. It would
+  likely collapse to a droplet eventually, but we run for far less
+  time.  There are 9 heavy molecules, 9 bonds, 10 1-4 pairs, 12
+  angles, 10 proper dihedrals, and 2 improper dihedrals defined.
+  Defaults use function 1 for bonds, pairs, angles, function 3 for
+  dihedrals, and function 1 for impropers.
+  
+  
+We test the separate-dhdl-file and dhdl-print-energy separately; we
+check with these test that the data is put in the correct place in the
+data structures, and then test with separate tests that it is exported
+correctly into the .dhdl.xvg files.
+
+Tests in this folder
+  - coulandvdwintramol
+    Tests turning off both vdw and coulomb while coupling intramolecular unteractions,
+    using only the mdp options; no explicitly defined A and B states in the topology. 
+    only a single init-lambda float is used, there is no array of lambda values, and therefore no init-lambda-state.
+    Other relevant settings that could affect free energy:
+         Constraints: h-bond with Lincs,
+         Coul: PME (Potential-shift-Verlet)
+         Vdw: Cutoff (Force-switch)
+         Dispersion: energy and pressure
+         Ensemble: constant T, berendsen
+         Frequencies: nstcalcenergy=1,nstenergy=1,nstdhdl=5,nsttcouple=1
+  - coulandvdwsequential_coul
+    Same as coulandvdwintramol, except it tests turning off both vdw and q, without
+    coupling intramolecular interactions.  A lambda
+    array is used, and the lambda value is in the region where vdw is turned on, and only coul
+    is changing.
+  - coulandvdwsequential_vdw
+    Same as coulandvdwintramol, except it tests turning off both vdw and q, without
+    coupling intramolecular interactions.  A lambda array is used, and the lambda value is
+    in the region where coul is turned off, and only vdw is changing.
+  - coulandvdwtogether
+    Same as coulandvdwintermol EXCEPT without coupling intramolecular interactions.
+  - expanded
+    Tests expanded ensemble, with nstexpanded = 5.  Tests turning on vdw and coulomb, using a
+    lambda array.  Only coul-lambda and vdw-lambda are specified, with the couple-intramol=no.
+    coul and vdew are turned off sequentially.  Energies at all lambda states are calculated.
+    Topology does not explicitly state A and B states.  Integrator is md-vv.
+    Other relevant settings that could affect free energy:
+         Constraints: h-bond with Lincs,
+         Coul: PME (Potential-shift-Verlet)
+         Vdw: shift (Potential-shift-Verlet)
+         Dispersion: energy and pressure
+         Ensemble: constant T, berendsen
+         Frequencies: nstcalcenergy=1,nstenergy=1,nstdhdl=10,nsttcouple=1  
+  - relative
+    Free energy changesare controlled by the .top file, not the .mdp file, as there is no couple_moltype,
+    and A and B states are defined explicitly in the topol.top file.
+    coul-lambdas, vdw-lambdas, and bonded-lambdas arrays vary from 0 to 1, with coulomb first being turned off.
+    - Topolgy changes:
+      - Atomtype opls_240 has a vdw parameter defined to accentuate the changes in lambda.
+      - improper dihedrals have A and B states.
+      - charges change on all atoms from state A to B.
+      - masses stay the same
+      - atom types from state A to B only change on atom 240.
+    TODO: if couple-moltype is zero, why are the couple-lambda0 defined?
+    Other relevant settings that could affect free energy:
+         Same as coulandvdwintramol
+
+  - relative-position-restraints
+      Like relative, but includes posres.itp, which have A and B states on the position restraints, so
+      should have lambda dependence. (also, does not have COM removal set, since it's unneeded,
+      and the restraint-lambdas array is used.
+  - restraints
+      Very imilar to relative, except for the free energy parameters.
+      Restraints are turned on. A and B states are the same except for
+      the addition of dihedral_restraints, angle_restraints, and distance_restraints,
+      each of which have A and B states.  The free energies are controlled in the topologies, but
+      the _restraint terms are the only ones with A and B states.
+  - simtemp
+      Similar to relative, except only simulated tempering and expanded ensemble options are set.
+      No topology B states are set.
+  - testcoul
+      uses md-vv, and uses the mdp to turn off both coul and vdw.
+      Uses lambda array, turns off vdw and coul sequentially, and tests the coul region.
+      otherwise fairly similar to coulandvdwtogether, and could possibly be merged later.
+  - transformAtoB
+      uses md-vv. Topology changes all atom types and charges with explicitly stated A and B states.
+      Bonded remain the same, lambda array used, set using fep-lambdas. 
+      Other relevant settings that could affect free energy: same as coulandvdwtogether.
+  - vdwalone
+      uses md-vv.  Turns off the interactions by defining B states with zero charge and vdw
+      epsilon in the topology.  ALSO uses couple-lambda0 and couple-lambda1=0, which is weird, but
+      there is no couple-moltype. Does not use a lambda array, but uses a single init-lambda.
+      TODO: Results seem to be the same with and without couple-lambda0 and couple-lamda1, but there is an
+      extra warning when they are not specified.
+
+TODOS:
+    - put in tests for individual bonded terms alone, of different
+functional types.  These tests can be done without a water box.
+    - put in tests for different types of long range cutoffs
+    - put in tests that check for free energy changes for different combinations
+      of nstenergy/nstcalcenergy/nstdhdl/nstexpanded/nsttemp/nstpres
+    - tests with different thermostats/barostats
+    - a bunch more ways to change from A to B
+    - tests without constraints
+    - are the energies printed out correctly at different dhdls.
+    - what about different sc-powers/sc-rpower
+    - add a separate test that makes sure the contents of nstdhdl get copied into the files correctly
+    - tests of expanded ensemble options (mostly movement options, some testing for weight accumulation options)
+    - tests of test of simulated tempering options
+    - tests with different settings for lambdas (including changing mass)
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwintramol/conf.gro b/src/testutils/simulationdatabase/freeenergy/coulandvdwintramol/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwintramol/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/coulandvdwintramol/grompp.mdp
new file mode 100644 (file)
index 0000000..28067a0
--- /dev/null
@@ -0,0 +1,147 @@
+;
+;      File 'mdout.mdp' was generated
+;      By user: mark (1302)
+;      On host: amd2
+;      At date: Wed Dec 19 13:44:25 2012
+;
+
+; VARIOUS PREPROCESSING OPTIONS
+; Preprocessor information: use cpp syntax.
+; e.g.: -I/home/joe/doe -I/home/mary/roe
+include                  = 
+; e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)
+define                   = 
+
+; RUN CONTROL PARAMETERS
+integrator               = md
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 20
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+; mode for center of mass motion removal
+comm-mode                = Linear
+; number of steps for center of mass motion removal
+nstcomm                  = 1
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 20
+nstcalcenergy            = 1
+nstenergy                = 1
+
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (group: using charge groups, Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy drift due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance      = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0.99
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = Cut-off
+vdw-modifier             = Force-switch
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Seperate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 1.0
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; ENERGY GROUP EXCLUSIONS
+; Pairs of energy groups for which all non-bonded interactions are excluded
+energygrp-excl           = 
+
+; Free energy variables
+free-energy              = yes
+couple_moltype           = ASN
+couple_lambda0           = none
+couple_lambda1           = vdw-q
+couple-intramol          = yes
+init-lambda              = 0.5
+nstdhdl                  = 5
+fep-lambdas              = 
+mass-lambdas             = 
+coul-lambdas             = 
+vdw-lambdas              = 
+bonded-lambdas           = 
+restraint-lambdas        = 
+temperature-lambdas      = 
+init-lambda-weights      = 
+dhdl-print-energy        = no
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-derivatives         = yes
+dh_hist_size             = 0
+dh_hist_spacing          = 0.1
+
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwintramol/topol.top b/src/testutils/simulationdatabase/freeenergy/coulandvdwintramol/topol.top
new file mode 100644 (file)
index 0000000..0ad5d81
--- /dev/null
@@ -0,0 +1,146 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0.0
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  0.0
+ opls_136d  CT 6   12.01100    -0.120       A    3.50000e-01  0.0
+ opls_140d  HC 1    1.00800     0.060       A    2.50000e-01  0.0
+ opls_235d  C   6   12.01100     0.500       A    3.75000e-01  0.0
+ opls_236d  O   8   15.99940    -0.500       A    2.96000e-01  0.0
+ opls_237d  N   7   14.00670    -0.760       A    3.25000e-01  0.0
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   ; CHARMM 22 parameter file
+  CT    C       1    0.15220   265266.0   ; CHARMM 22 parameter file
+  C     O       1    0.12290   476976.0   ; URAGUA,CYT,AA
+  C     N       1    0.13350   410032.0   ; AA
+  H     N       1    0.10100   363171.2   ;
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass  typeB    chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011
+     2   opls_140      1   ASN     HB1      1       0.06      1.008
+     3   opls_140      1   ASN     HB2      1       0.06      1.008
+     4   opls_140      1   ASN     HB3      1       0.06      1.008
+     5   opls_235      1   ASN      CG      2        0.5     12.011
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067
+     8   opls_240      1   ASN    HD21      3       0.38      1.008
+     9   opls_240      1   ASN    HD22      3       0.38      1.008
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2
+    5     9     8     7     1    180.0     62.76000   2
+
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_coul/conf.gro b/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_coul/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_coul/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_coul/grompp.mdp
new file mode 100644 (file)
index 0000000..e89c360
--- /dev/null
@@ -0,0 +1,148 @@
+;
+;      File 'mdout.mdp' was generated
+;      By user: mark (1302)
+;      On host: amd2
+;      At date: Wed Dec 19 13:44:25 2012
+;
+
+; VARIOUS PREPROCESSING OPTIONS
+; Preprocessor information: use cpp syntax.
+; e.g.: -I/home/joe/doe -I/home/mary/roe
+include                  = 
+; e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)
+define                   = 
+
+; RUN CONTROL PARAMETERS
+integrator               = md
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 20
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+; mode for center of mass motion removal
+comm-mode                = Linear
+; number of steps for center of mass motion removal
+nstcomm                  = 1
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 20
+nstcalcenergy            = 1
+nstenergy                = 1
+
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (group: using charge groups, Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy drift due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance      = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0.99
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = Cut-off
+vdw-modifier             = Force-switch
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Seperate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 1.0
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; ENERGY GROUP EXCLUSIONS
+; Pairs of energy groups for which all non-bonded interactions are excluded
+energygrp-excl           = 
+
+; Free energy variables
+free-energy              = yes
+couple_moltype           = ASN
+couple_lambda0           = none
+couple_lambda1           = vdw-q
+couple-intramol          = no
+init-lambda-state        = 7
+delta-lambda             = 0
+nstdhdl                  = 5
+fep-lambdas              = 
+mass-lambdas             = 
+coul-lambdas             = 0.0 0.0 0.0 0.0 0.0 0.0 0.4 0.8 1.0
+vdw-lambdas              = 0.0 0.2 0.4 0.6 0.8 1.0 1.0 1.0 1.0
+bonded-lambdas           = 
+restraint-lambdas        = 
+temperature-lambdas      = 
+init-lambda-weights      = 
+dhdl-print-energy        = no
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-derivatives         = yes
+dh_hist_size             = 0
+dh_hist_spacing          = 0.1
+
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_coul/topol.top b/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_coul/topol.top
new file mode 100644 (file)
index 0000000..0ad5d81
--- /dev/null
@@ -0,0 +1,146 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0.0
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  0.0
+ opls_136d  CT 6   12.01100    -0.120       A    3.50000e-01  0.0
+ opls_140d  HC 1    1.00800     0.060       A    2.50000e-01  0.0
+ opls_235d  C   6   12.01100     0.500       A    3.75000e-01  0.0
+ opls_236d  O   8   15.99940    -0.500       A    2.96000e-01  0.0
+ opls_237d  N   7   14.00670    -0.760       A    3.25000e-01  0.0
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   ; CHARMM 22 parameter file
+  CT    C       1    0.15220   265266.0   ; CHARMM 22 parameter file
+  C     O       1    0.12290   476976.0   ; URAGUA,CYT,AA
+  C     N       1    0.13350   410032.0   ; AA
+  H     N       1    0.10100   363171.2   ;
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass  typeB    chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011
+     2   opls_140      1   ASN     HB1      1       0.06      1.008
+     3   opls_140      1   ASN     HB2      1       0.06      1.008
+     4   opls_140      1   ASN     HB3      1       0.06      1.008
+     5   opls_235      1   ASN      CG      2        0.5     12.011
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067
+     8   opls_240      1   ASN    HD21      3       0.38      1.008
+     9   opls_240      1   ASN    HD22      3       0.38      1.008
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2
+    5     9     8     7     1    180.0     62.76000   2
+
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/ana.itp b/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/ana.itp
new file mode 100644 (file)
index 0000000..f9780c7
--- /dev/null
@@ -0,0 +1,82 @@
+;
+;      File 'ana.top' was generated
+;      By user: justin (502)
+;      On host: lynx
+;      At date: Sat Jun 15 16:25:48 2002
+;
+;      This is your topology file
+;      "I'll Master Your Language, and In the Meantime I'll Create My Own" (Tricky)
+;
+
+[ moleculetype ]
+; Name            nrexcl
+ASNJ             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass  typeB    chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011
+     2   opls_140      1   ASN     HB1      1       0.06      1.008
+     3   opls_140      1   ASN     HB2      1       0.06      1.008
+     4   opls_140      1   ASN     HB3      1       0.06      1.008
+     5   opls_235      1   ASN      CG      2        0.5     12.011
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067
+     8   opls_240      1   ASN    HD21      3       0.38      1.008
+     9   opls_240      1   ASN    HD22      3       0.38      1.008
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1 
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3
+    1     7     6     5     1    improper_O_C_X_Y
+    5     9     8     7     1    improper_Z_N_X_Y
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/conf.gro b/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/grompp.mdp
new file mode 100644 (file)
index 0000000..24fba24
--- /dev/null
@@ -0,0 +1,147 @@
+;
+;      File 'mdout.mdp' was generated
+;      By user: mark (1302)
+;      On host: amd2
+;      At date: Wed Dec 19 13:44:25 2012
+;
+
+; VARIOUS PREPROCESSING OPTIONS
+; Preprocessor information: use cpp syntax.
+; e.g.: -I/home/joe/doe -I/home/mary/roe
+include                  = 
+; e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)
+define                   = 
+
+; RUN CONTROL PARAMETERS
+integrator               = md
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 20
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+; mode for center of mass motion removal
+comm-mode                = Linear
+; number of steps for center of mass motion removal
+nstcomm                  = 1
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 20
+nstcalcenergy            = 1
+nstenergy                = 1
+
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (group: using charge groups, Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy drift due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance      = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0.99
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = Cut-off
+vdw-modifier             = Force-switch
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Seperate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 1.0
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; ENERGY GROUP EXCLUSIONS
+; Pairs of energy groups for which all non-bonded interactions are excluded
+energygrp-excl           = 
+
+; Free energy variables
+free-energy              = yes
+couple_moltype           = ASN
+couple_lambda0           = none
+couple_lambda1           = vdw-q
+couple-intramol          = no
+init-lambda-state        = 3
+delta-lambda             = 0
+nstdhdl                  = 5
+fep-lambdas              = 
+mass-lambdas             = 
+coul-lambdas             = 0.0 0.0 0.0 0.0 0.0 0.0 0.4 0.8 1.0
+vdw-lambdas              = 0.0 0.2 0.4 0.6 0.8 1.0 1.0 1.0 1.0
+bonded-lambdas           = 
+restraint-lambdas        = 
+temperature-lambdas      = 
+init-lambda-weights      = 
+dhdl-print-energy        = no
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-derivatives         = yes
+dh_hist_size             = 0
+dh_hist_spacing          = 0.1
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/topol.top b/src/testutils/simulationdatabase/freeenergy/coulandvdwsequential_vdw/topol.top
new file mode 100644 (file)
index 0000000..0ad5d81
--- /dev/null
@@ -0,0 +1,146 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0.0
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  0.0
+ opls_136d  CT 6   12.01100    -0.120       A    3.50000e-01  0.0
+ opls_140d  HC 1    1.00800     0.060       A    2.50000e-01  0.0
+ opls_235d  C   6   12.01100     0.500       A    3.75000e-01  0.0
+ opls_236d  O   8   15.99940    -0.500       A    2.96000e-01  0.0
+ opls_237d  N   7   14.00670    -0.760       A    3.25000e-01  0.0
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   ; CHARMM 22 parameter file
+  CT    C       1    0.15220   265266.0   ; CHARMM 22 parameter file
+  C     O       1    0.12290   476976.0   ; URAGUA,CYT,AA
+  C     N       1    0.13350   410032.0   ; AA
+  H     N       1    0.10100   363171.2   ;
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass  typeB    chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011
+     2   opls_140      1   ASN     HB1      1       0.06      1.008
+     3   opls_140      1   ASN     HB2      1       0.06      1.008
+     4   opls_140      1   ASN     HB3      1       0.06      1.008
+     5   opls_235      1   ASN      CG      2        0.5     12.011
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067
+     8   opls_240      1   ASN    HD21      3       0.38      1.008
+     9   opls_240      1   ASN    HD22      3       0.38      1.008
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2
+    5     9     8     7     1    180.0     62.76000   2
+
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwtogether/conf.gro b/src/testutils/simulationdatabase/freeenergy/coulandvdwtogether/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwtogether/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/coulandvdwtogether/grompp.mdp
new file mode 100644 (file)
index 0000000..144ba47
--- /dev/null
@@ -0,0 +1,146 @@
+;
+;      File 'mdout.mdp' was generated
+;      By user: mark (1302)
+;      On host: amd2
+;      At date: Wed Dec 19 13:44:25 2012
+;
+
+; VARIOUS PREPROCESSING OPTIONS
+; Preprocessor information: use cpp syntax.
+; e.g.: -I/home/joe/doe -I/home/mary/roe
+include                  = 
+; e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)
+define                   = 
+
+; RUN CONTROL PARAMETERS
+integrator               = md
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 20
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+; mode for center of mass motion removal
+comm-mode                = Linear
+; number of steps for center of mass motion removal
+nstcomm                  = 1
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 20
+nstcalcenergy            = 1
+nstenergy                = 1
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (group: using charge groups, Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy drift due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance      = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0.99
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = Cut-off
+vdw-modifier             = Force-switch
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Seperate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 1.0
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; ENERGY GROUP EXCLUSIONS
+; Pairs of energy groups for which all non-bonded interactions are excluded
+energygrp-excl           = 
+
+; Free energy variables
+free-energy              = yes
+couple_moltype           = ASN
+couple_lambda0           = none
+couple_lambda1           = vdw-q
+couple-intramol          = no
+init-lambda              = 0.5
+nstdhdl                  = 5
+fep-lambdas              = 
+mass-lambdas             = 
+coul-lambdas             = 
+vdw-lambdas              = 
+bonded-lambdas           = 
+restraint-lambdas        = 
+temperature-lambdas      = 
+init-lambda-weights      = 
+dhdl-print-energy        = no
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-derivatives         = yes
+dh_hist_size             = 0
+dh_hist_spacing          = 0.1
+
diff --git a/src/testutils/simulationdatabase/freeenergy/coulandvdwtogether/topol.top b/src/testutils/simulationdatabase/freeenergy/coulandvdwtogether/topol.top
new file mode 100644 (file)
index 0000000..0ad5d81
--- /dev/null
@@ -0,0 +1,146 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0.0
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  0.0
+ opls_136d  CT 6   12.01100    -0.120       A    3.50000e-01  0.0
+ opls_140d  HC 1    1.00800     0.060       A    2.50000e-01  0.0
+ opls_235d  C   6   12.01100     0.500       A    3.75000e-01  0.0
+ opls_236d  O   8   15.99940    -0.500       A    2.96000e-01  0.0
+ opls_237d  N   7   14.00670    -0.760       A    3.25000e-01  0.0
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   ; CHARMM 22 parameter file
+  CT    C       1    0.15220   265266.0   ; CHARMM 22 parameter file
+  C     O       1    0.12290   476976.0   ; URAGUA,CYT,AA
+  C     N       1    0.13350   410032.0   ; AA
+  H     N       1    0.10100   363171.2   ;
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass  typeB    chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011
+     2   opls_140      1   ASN     HB1      1       0.06      1.008
+     3   opls_140      1   ASN     HB2      1       0.06      1.008
+     4   opls_140      1   ASN     HB3      1       0.06      1.008
+     5   opls_235      1   ASN      CG      2        0.5     12.011
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067
+     8   opls_240      1   ASN    HD21      3       0.38      1.008
+     9   opls_240      1   ASN    HD22      3       0.38      1.008
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2
+    5     9     8     7     1    180.0     62.76000   2
+
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
diff --git a/src/testutils/simulationdatabase/freeenergy/expanded/conf.gro b/src/testutils/simulationdatabase/freeenergy/expanded/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/expanded/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/expanded/grompp.mdp
new file mode 100644 (file)
index 0000000..be618bb
--- /dev/null
@@ -0,0 +1,170 @@
+;
+;      File 'mdout.mdp' was generated
+;      By user: mark (1302)
+;      On host: amd2
+;      At date: Wed Dec 19 13:44:25 2012
+;
+
+; VARIOUS PREPROCESSING OPTIONS
+; Preprocessor information: use cpp syntax.
+; e.g.: -I/home/joe/doe -I/home/mary/roe
+include                  = 
+; e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)
+define                   = 
+
+; RUN CONTROL PARAMETERS
+integrator               = md-vv
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 100
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+; mode for center of mass motion removal
+comm-mode                = Linear
+; number of steps for center of mass motion removal
+nstcomm                  = 1
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 5
+nstcalcenergy            = 1
+nstenergy                = 1
+
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (group: using charge groups, Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy drift due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance  = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = shift
+vdw-modifier             = Potential-shift-Verlet
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Seperate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 1.0
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; ENERGY GROUP EXCLUSIONS
+; Pairs of energy groups for which all non-bonded interactions are excluded
+energygrp-excl           = 
+
+; Free energy variables
+free-energy              = expanded
+couple_moltype           = ASN
+couple_lambda0           = none
+couple_lambda1           = vdw-q
+couple-intramol          = no
+nstdhdl                  = 10
+init-lambda-state        = 4
+fep-lambdas              = 
+mass-lambdas             = 
+coul-lambdas             = 0.0 0.0 0.0 0.0 0.0 0.0 0.4 0.8 1.0
+vdw-lambdas              = 0.0 0.2 0.4 0.6 0.8 1.0 1.0 1.0 1.0
+bonded-lambdas           = 
+restraint-lambdas        = 
+temperature-lambdas      = 
+calc-lambda-neighbors    = -1
+init-lambda-weights      = 
+dhdl-print-energy        = no
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-derivatives         = yes
+dh_hist_size             = 0
+dh_hist_spacing          = 0.1
+
+; simulated tempering variables
+simulated-tempering      = no
+simulated-tempering-scaling = geometric
+sim-temp-low             = 300
+sim-temp-high            = 300
+
+; expanded ensemble variables
+nstexpanded              = 5
+lmc-stats                = wang-landau
+lmc-move                 = metropolized-gibbs
+lmc-weights-equil        = wl-delta
+weight-equil-wl-delta    = 0.001
+
+
+; Seed for Monte Carlo in lambda space
+lmc-seed                 = 1993
+lmc-repeats              = 1
+weight-c-range           = 3
+wl-scale                 = 0.6
+wl-ratio                 = 0.8
+init-wl-delta            = 5
+wl-oneovert              = yes
diff --git a/src/testutils/simulationdatabase/freeenergy/expanded/topol.top b/src/testutils/simulationdatabase/freeenergy/expanded/topol.top
new file mode 100644 (file)
index 0000000..0ad5d81
--- /dev/null
@@ -0,0 +1,146 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0.0
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  0.0
+ opls_136d  CT 6   12.01100    -0.120       A    3.50000e-01  0.0
+ opls_140d  HC 1    1.00800     0.060       A    2.50000e-01  0.0
+ opls_235d  C   6   12.01100     0.500       A    3.75000e-01  0.0
+ opls_236d  O   8   15.99940    -0.500       A    2.96000e-01  0.0
+ opls_237d  N   7   14.00670    -0.760       A    3.25000e-01  0.0
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   ; CHARMM 22 parameter file
+  CT    C       1    0.15220   265266.0   ; CHARMM 22 parameter file
+  C     O       1    0.12290   476976.0   ; URAGUA,CYT,AA
+  C     N       1    0.13350   410032.0   ; AA
+  H     N       1    0.10100   363171.2   ;
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass  typeB    chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011
+     2   opls_140      1   ASN     HB1      1       0.06      1.008
+     3   opls_140      1   ASN     HB2      1       0.06      1.008
+     4   opls_140      1   ASN     HB3      1       0.06      1.008
+     5   opls_235      1   ASN      CG      2        0.5     12.011
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067
+     8   opls_240      1   ASN    HD21      3       0.38      1.008
+     9   opls_240      1   ASN    HD22      3       0.38      1.008
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2
+    5     9     8     7     1    180.0     62.76000   2
+
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
diff --git a/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/README b/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/README
new file mode 100644 (file)
index 0000000..001e1e7
--- /dev/null
@@ -0,0 +1,5 @@
+This is the same as freeenergy/relative, with the
+addition of position restraints on all the atoms
+of the solute.
+
+grompp invoked as grompp -p topol.top -c conf.gro -r conf.gro -f grompp.mdp -maxwarn 10
\ No newline at end of file
diff --git a/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/conf.gro b/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/grompp.mdp
new file mode 100644 (file)
index 0000000..dfe4543
--- /dev/null
@@ -0,0 +1,142 @@
+;
+;      File 'mdout.mdp' was generated
+;      By user: mark (1302)
+;      On host: amd2
+;      At date: Wed Dec 19 13:44:25 2012
+;
+
+; VARIOUS PREPROCESSING OPTIONS
+; Preprocessor information: use cpp syntax.
+; e.g.: -I/home/joe/doe -I/home/mary/roe
+include                  = 
+; e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)
+define                   = -DPOSRES
+
+; RUN CONTROL PARAMETERS
+integrator               = md
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 20
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 20
+nstcalcenergy            = 1
+nstenergy                = 1
+
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (group: using charge groups, Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy drift due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance      = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0.99
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = Cut-off
+vdw-modifier             = Force-switch
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Seperate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 1.0
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; ENERGY GROUP EXCLUSIONS
+; Pairs of energy groups for which all non-bonded interactions are excluded
+energygrp-excl           = 
+
+; Free energy variables
+free-energy              = yes
+couple_moltype           = 
+couple_lambda0           = vdw-q
+couple_lambda1           = vdw-q
+init-lambda-state        = 12
+nstdhdl                  = 5
+fep-lambdas              = 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.0 0.00 0.0 0.00 0.0 0.0
+mass-lambdas             = 
+coul-lambdas             = 0.0 0.1 0.2 0.3 0.5 0.7 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.00 1.0 1.00 1.0 1.00 1.0 1.0
+vdw-lambdas              = 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.65 0.7 0.75 0.8 0.85 0.9 1.0
+bonded-lambdas           = 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.65 0.7 0.75 0.8 0.85 0.9 1.0
+restraint-lambdas        = 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.9 0.9 0.9 0.90 0.9 1.00 1.0 1.00 1.0 1.0
+temperature-lambdas      = 
+init-lambda-weights      = 
+dhdl-print-energy        = no
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-derivatives         = yes
+dh_hist_size             = 0
+dh_hist_spacing          = 0.1
+
diff --git a/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/no-nb-gpu-support b/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/no-nb-gpu-support
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/posre.itp b/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/posre.itp
new file mode 100644 (file)
index 0000000..f082093
--- /dev/null
@@ -0,0 +1,11 @@
+[ position_restraints ]
+;  i funct       fcx        fcy        fcz        fcxB     fcyB     fczB
+   1    1       10000       10000       10000     1000     1000     1000
+   2    1       10000       10000       10000     1000     1000     1000
+   3    1       10000       10000       10000     1000     1000     1000
+   4    1       10000       10000       10000     1000     1000     1000
+   5    1       10000       10000       10000     1000     1000     1000
+   6    1       10000       10000       10000     1000     1000     1000
+   7    1       10000       10000       10000     1000     1000     1000
+   8    1       10000       10000       10000     1000     1000     1000
+   9    1       10000       10000       10000     1000     1000     1000
diff --git a/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/topol.top b/src/testutils/simulationdatabase/freeenergy/relative-position-restraints/topol.top
new file mode 100644 (file)
index 0000000..0236c68
--- /dev/null
@@ -0,0 +1,150 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    1.00000e-01  1.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0.0
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  0.0
+ opls_136d  CT 6   12.01100    -0.120       A    3.50000e-01  0.0
+ opls_140d  HC 1    1.00800     0.060       A    2.50000e-01  0.0
+ opls_235d  C   6   12.01100     0.500       A    3.75000e-01  0.0
+ opls_236d  O   8   15.99940    -0.500       A    2.96000e-01  0.0
+ opls_237d  N   7   14.00670    -0.760       A    3.25000e-01  0.0
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   
+  CT    C       1    0.15220   265266.0   
+  C     O       1    0.12290   476976.0   
+  C     N       1    0.13350   410032.0    
+  H     N       1    0.10100   363171.2    
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass     typeB       chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011   opls_136       -0.10      12.011
+     2   opls_140      1   ASN     HB1      1       0.06      1.008   opls_140        0.10       1.008
+     3   opls_140      1   ASN     HB2      1       0.06      1.008   opls_140        0.10       1.008
+     4   opls_140      1   ASN     HB3      1       0.06      1.008   opls_140        0.10       1.008
+     5   opls_235      1   ASN      CG      2        0.5     12.011   opls_235        0.80      12.011
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994   opls_236       -0.50      15.994
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067   opls_236       -0.50      15.994 
+     8   opls_240      1   ASN    HD21      3       0.38      1.008   opls_240d       0.00       1.008
+     9   opls_240      1   ASN    HD22      3       0.38      1.008   opls_240d       0.00       1.008   
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2      180.0   400.0   2
+    5     9     8     7     1    180.0     62.76000   2      180.0   200.0   2
+
+#ifdef POSRES
+#include "posre.itp"
+#endif
+
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
diff --git a/src/testutils/simulationdatabase/freeenergy/relative/conf.gro b/src/testutils/simulationdatabase/freeenergy/relative/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/relative/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/relative/grompp.mdp
new file mode 100644 (file)
index 0000000..9bb6518
--- /dev/null
@@ -0,0 +1,146 @@
+;
+;      File 'mdout.mdp' was generated
+;      By user: mark (1302)
+;      On host: amd2
+;      At date: Wed Dec 19 13:44:25 2012
+;
+
+; VARIOUS PREPROCESSING OPTIONS
+; Preprocessor information: use cpp syntax.
+; e.g.: -I/home/joe/doe -I/home/mary/roe
+include                  = 
+; e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)
+define                   = 
+
+; RUN CONTROL PARAMETERS
+integrator               = md
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 20
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+; mode for center of mass motion removal
+comm-mode                = Linear
+; number of steps for center of mass motion removal
+nstcomm                  = 1
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 20
+nstcalcenergy            = 1
+nstenergy                = 1
+
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (group: using charge groups, Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy drift due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance      = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0.99
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = Cut-off
+vdw-modifier             = Force-switch
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Seperate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 1.0
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; ENERGY GROUP EXCLUSIONS
+; Pairs of energy groups for which all non-bonded interactions are excluded
+energygrp-excl           = 
+
+; Free energy variables
+free-energy              = yes
+couple_moltype           = 
+couple_lambda0           = vdw-q
+couple_lambda1           = vdw-q
+init-lambda-state        = 12
+nstdhdl                  = 5
+fep-lambdas              = 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.0 0.00 0.0 0.00 0.0 0.0
+mass-lambdas             = 
+coul-lambdas             = 0.0 0.1 0.2 0.3 0.5 0.7 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.00 1.0 1.00 1.0 1.00 1.0 1.0
+vdw-lambdas              = 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.65 0.7 0.75 0.8 0.85 0.9 1.0
+bonded-lambdas           = 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.65 0.7 0.75 0.8 0.85 0.9 1.0
+restraint-lambdas        = 
+temperature-lambdas      = 
+init-lambda-weights      = 
+dhdl-print-energy        = no
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-derivatives         = yes
+dh_hist_size             = 0
+dh_hist_spacing          = 0.1
+
diff --git a/src/testutils/simulationdatabase/freeenergy/relative/no-nb-gpu-support b/src/testutils/simulationdatabase/freeenergy/relative/no-nb-gpu-support
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/testutils/simulationdatabase/freeenergy/relative/topol.top b/src/testutils/simulationdatabase/freeenergy/relative/topol.top
new file mode 100644 (file)
index 0000000..c8a9899
--- /dev/null
@@ -0,0 +1,146 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    1.00000e-01  1.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0.0
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  0.0
+ opls_136d  CT 6   12.01100    -0.120       A    3.50000e-01  0.0
+ opls_140d  HC 1    1.00800     0.060       A    2.50000e-01  0.0
+ opls_235d  C   6   12.01100     0.500       A    3.75000e-01  0.0
+ opls_236d  O   8   15.99940    -0.500       A    2.96000e-01  0.0
+ opls_237d  N   7   14.00670    -0.760       A    3.25000e-01  0.0
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   
+  CT    C       1    0.15220   265266.0   
+  C     O       1    0.12290   476976.0   
+  C     N       1    0.13350   410032.0    
+  H     N       1    0.10100   363171.2    
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass     typeB       chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011   opls_136       -0.10      12.011
+     2   opls_140      1   ASN     HB1      1       0.06      1.008   opls_140        0.10       1.008
+     3   opls_140      1   ASN     HB2      1       0.06      1.008   opls_140        0.10       1.008
+     4   opls_140      1   ASN     HB3      1       0.06      1.008   opls_140        0.10       1.008
+     5   opls_235      1   ASN      CG      2        0.5     12.011   opls_235        0.80      12.011
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994   opls_236       -0.50      15.994
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067   opls_236       -0.50      15.994 
+     8   opls_240      1   ASN    HD21      3       0.38      1.008   opls_240d       0.00       1.008
+     9   opls_240      1   ASN    HD22      3       0.38      1.008   opls_240d       0.00       1.008   
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2      180.0   400.0   2
+    5     9     8     7     1    180.0     62.76000   2      180.0   200.0   2
+
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
diff --git a/src/testutils/simulationdatabase/freeenergy/restraints/conf.gro b/src/testutils/simulationdatabase/freeenergy/restraints/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/restraints/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/restraints/grompp.mdp
new file mode 100644 (file)
index 0000000..fa518a1
--- /dev/null
@@ -0,0 +1,148 @@
+;
+;      File 'mdout.mdp' was generated
+;      By user: mark (1302)
+;      On host: amd2
+;      At date: Wed Dec 19 13:44:25 2012
+;
+
+; VARIOUS PREPROCESSING OPTIONS
+; Preprocessor information: use cpp syntax.
+; e.g.: -I/home/joe/doe -I/home/mary/roe
+include                  = 
+; e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)
+define                   = 
+
+; RUN CONTROL PARAMETERS
+integrator               = md
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 20
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+; mode for center of mass motion removal
+comm-mode                = Linear
+; number of steps for center of mass motion removal
+nstcomm                  = 1
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 5
+nstcalcenergy            = 1
+nstenergy                = 1
+
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (group: using charge groups, Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy drift due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance  = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0.99
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = Cut-off
+vdw-modifier             = Force-switch
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Seperate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 1.0
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; ENERGY GROUP EXCLUSIONS
+; Pairs of energy groups for which all non-bonded interactions are excluded
+energygrp-excl           = 
+
+; Free energy variables
+free-energy              = yes
+couple_moltype           = 
+couple_lambda0           = vdw-q
+couple_lambda1           = vdw-q
+couple-intramol          = no
+nstdhdl                  = 10
+init-lambda-state        = 5
+fep-lambdas              = 
+mass-lambdas             = 
+coul-lambdas             = 
+vdw-lambdas              = 
+bonded-lambdas           = 
+restraint-lambdas        = 0.00 0.05 0.10 0.20 0.40 0.60 1.00
+temperature-lambdas      = 
+calc-lambda-neighbors    = -1
+init-lambda-weights      = 
+dhdl-print-energy        = no
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-derivatives         = yes
+dh_hist_size             = 0
+dh_hist_spacing          = 0.1
+
diff --git a/src/testutils/simulationdatabase/freeenergy/restraints/no-nb-gpu-support b/src/testutils/simulationdatabase/freeenergy/restraints/no-nb-gpu-support
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/testutils/simulationdatabase/freeenergy/restraints/topol.top b/src/testutils/simulationdatabase/freeenergy/restraints/topol.top
new file mode 100644 (file)
index 0000000..f74035e
--- /dev/null
@@ -0,0 +1,166 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0.0
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  0.0
+ opls_136d  CT 6   12.01100    -0.120       A    3.50000e-01  0.0
+ opls_140d  HC 1    1.00800     0.060       A    2.50000e-01  0.0
+ opls_235d  C   6   12.01100     0.500       A    3.75000e-01  0.0
+ opls_236d  O   8   15.99940    -0.500       A    2.96000e-01  0.0
+ opls_237d  N   7   14.00670    -0.760       A    3.25000e-01  0.0
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   ; CHARMM 22 parameter file
+  CT    C       1    0.15220   265266.0   ; CHARMM 22 parameter file
+  C     O       1    0.12290   476976.0   ; URAGUA,CYT,AA
+  C     N       1    0.13350   410032.0   ; AA
+  H     N       1    0.10100   363171.2   ;
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass  typeB    chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011
+     2   opls_140      1   ASN     HB1      1       0.06      1.008
+     3   opls_140      1   ASN     HB2      1       0.06      1.008
+     4   opls_140      1   ASN     HB3      1       0.06      1.008
+     5   opls_235      1   ASN      CG      2        0.5     12.011
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067
+     8   opls_240      1   ASN    HD21      3       0.38      1.008
+     9   opls_240      1   ASN    HD22      3       0.38      1.008
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2
+    5     9     8     7     1    180.0     62.76000   2
+
+[ dihedral_restraints ]
+;  i    j    k    l type phiA dphiA  kfacA  phiB  dphiB   kfacB
+2  1 5  6             1    38    0    0.00    38     0    41.84      
+3  1 5  6             1   111    0    0.00   111     0    41.84        
+1  5 7  8             1   -39    0    0.00   -39     0    41.84      
+
+[ angle_restraints ]
+;  i    j    k    l type theta0A fcA mult  theta0B    fcB 
+;mixed state  -- need to have MULT listed twice!!!
+2  1  5 6    1   62      0.0  1     62    41.84    1
+3  1  5 6     1   79      0.0  1     79    41.84    1
+; zero state
+1   5  7 8    1   62      0.0  1
+6   5  7 9    1   79      0.0  1
+
+; distance restraints
+[ bonds ]
+;  i    j type    r0A   r1A   r2A    fcA  r0B   r1B   r2B      fcB
+;mixed state
+   1    9    10   0.61  0.61  0.81    0.0  0.61  0.61  0.81  200.0
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
diff --git a/src/testutils/simulationdatabase/freeenergy/simtemp/conf.gro b/src/testutils/simulationdatabase/freeenergy/simtemp/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/simtemp/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/simtemp/grompp.mdp
new file mode 100644 (file)
index 0000000..85a3f18
--- /dev/null
@@ -0,0 +1,152 @@
+;
+;      File 'mdout.mdp' was generated
+;      By user: mark (1302)
+;      On host: amd2
+;      At date: Wed Dec 19 13:44:25 2012
+;
+
+; VARIOUS PREPROCESSING OPTIONS
+; Preprocessor information: use cpp syntax.
+; e.g.: -I/home/joe/doe -I/home/mary/roe
+include                  = 
+; e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)
+define                   = 
+
+; RUN CONTROL PARAMETERS
+integrator               = md-vv
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 20
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+; mode for center of mass motion removal
+comm-mode                = Linear
+; number of steps for center of mass motion removal
+nstcomm                  = 1
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 20
+nstcalcenergy            = 1
+nstenergy                = 1
+
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (group: using charge groups, Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy drift due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance      = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0.99
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = Cut-off
+vdw-modifier             = Force-switch
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Seperate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 1.0
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; ENERGY GROUP EXCLUSIONS
+; Pairs of energy groups for which all non-bonded interactions are excluded
+energygrp-excl           = 
+
+; simulated tempering variables
+simulated-tempering      = yes
+simulated-tempering-scaling = geometric
+sim-temp-low             = 300
+sim-temp-high            = 350
+
+; Free energy variables
+free-energy              = no
+init-lambda-state        = 1
+delta-lambda             = 0
+nstdhdl                  = 5
+temperature-lambdas      = 0.0 0.2 0.4 0.8 1.0
+
+; expanded ensemble variables
+nstexpanded              = 5
+lmc-stats                = wang-landau
+lmc-move                 = gibbs
+lmc-weights-equil        = wl-delta
+weight-equil-wl-delta    = 0.001
+
+; Seed for Monte Carlo in lambda space
+lmc-seed                 = 1993
+lmc-repeats              = 1
+lmc-forced-nstart        = 0
+symmetrized-transition-matrix = no
+nst-transition-matrix    = -1
+mininum-var-min          = 100
+weight-c-range           = 0
+wl-scale                 = 0.8
+wl-ratio                 = 0.8
+init-wl-delta            = 100
+wl-oneovert              = no
\ No newline at end of file
diff --git a/src/testutils/simulationdatabase/freeenergy/simtemp/no-nb-gpu-support b/src/testutils/simulationdatabase/freeenergy/simtemp/no-nb-gpu-support
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/testutils/simulationdatabase/freeenergy/simtemp/topol.top b/src/testutils/simulationdatabase/freeenergy/simtemp/topol.top
new file mode 100644 (file)
index 0000000..0ad5d81
--- /dev/null
@@ -0,0 +1,146 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0.0
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  0.0
+ opls_136d  CT 6   12.01100    -0.120       A    3.50000e-01  0.0
+ opls_140d  HC 1    1.00800     0.060       A    2.50000e-01  0.0
+ opls_235d  C   6   12.01100     0.500       A    3.75000e-01  0.0
+ opls_236d  O   8   15.99940    -0.500       A    2.96000e-01  0.0
+ opls_237d  N   7   14.00670    -0.760       A    3.25000e-01  0.0
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   ; CHARMM 22 parameter file
+  CT    C       1    0.15220   265266.0   ; CHARMM 22 parameter file
+  C     O       1    0.12290   476976.0   ; URAGUA,CYT,AA
+  C     N       1    0.13350   410032.0   ; AA
+  H     N       1    0.10100   363171.2   ;
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass  typeB    chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011
+     2   opls_140      1   ASN     HB1      1       0.06      1.008
+     3   opls_140      1   ASN     HB2      1       0.06      1.008
+     4   opls_140      1   ASN     HB3      1       0.06      1.008
+     5   opls_235      1   ASN      CG      2        0.5     12.011
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067
+     8   opls_240      1   ASN    HD21      3       0.38      1.008
+     9   opls_240      1   ASN    HD22      3       0.38      1.008
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2
+    5     9     8     7     1    180.0     62.76000   2
+
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
diff --git a/src/testutils/simulationdatabase/freeenergy/transformAtoB/conf.gro b/src/testutils/simulationdatabase/freeenergy/transformAtoB/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/transformAtoB/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/transformAtoB/grompp.mdp
new file mode 100644 (file)
index 0000000..aff5721
--- /dev/null
@@ -0,0 +1,144 @@
+; RUN CONTROL PARAMETERS
+integrator               = md-vv
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 20
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+; mode for center of mass motion removal
+comm-mode                = Linear
+; number of steps for center of mass motion removal
+nstcomm                  = 1
+; group(s) for center of mass motion removal
+comm-grps                = 
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 5
+nstcalcenergy            = 1
+nstenergy                = 1
+; Output frequency and precision for .xtc file
+nstxout-compressed       = 0
+compressed-x-precision   = 1000
+; This selects the subset of atoms for the compressed
+; trajectory file. You can select multiple groups. By
+; default, all atoms will be written.
+compressed-x-grps        = 
+; Selection of energy groups
+energygrps               = 
+
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy error due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance  = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+; long-range cut-off for switched potentials
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0.99
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = Cut-off
+vdw-modifier             = Force-switch
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Separate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+nh-chain-length          = 10
+print-nose-hoover-chain-variables = no
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 0.1
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; Free energy variables
+free-energy              = yes
+couple-moltype           = 
+couple-lambda0           = vdw-q
+couple-lambda1           = vdw-q
+couple-intramol          = no
+init-lambda-state        = 4
+delta-lambda             = 0
+nstdhdl                  = 5
+fep-lambdas              = 0.0 0.25 0.5 0.75 1.0
+mass-lambdas             = 
+coul-lambdas             = 
+vdw-lambdas              = 
+bonded-lambdas           = 
+restraint-lambdas        = 
+temperature-lambdas      = 
+calc-lambda-neighbors    = 1
+init-lambda-weights      = 
+dhdl-print-energy        = no
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-derivatives         = yes
+dh_hist_size             = 0
+dh_hist_spacing          = 0.1
+
diff --git a/src/testutils/simulationdatabase/freeenergy/transformAtoB/topol.top b/src/testutils/simulationdatabase/freeenergy/transformAtoB/topol.top
new file mode 100644 (file)
index 0000000..deb17f3
--- /dev/null
@@ -0,0 +1,146 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0.40000e+00
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  1.00000e+00
+ opls_136d  CT 6   12.01100    -0.120       A    3.00000e-01  0.5  
+ opls_140d  HC 1    1.00800     0.060       A    2.00000e-01  0.5
+ opls_235d  C   6   12.01100     0.500       A    3.0000e-01   0.5
+ opls_236d  O   8   15.99940    -0.500       A    4.0000e-01   0.5
+ opls_237d  N   7   14.00670    -0.760       A    3.0000e-01   0.5
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  4.00000e+00
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   ; CHARMM 22 parameter file
+  CT    C       1    0.15220   265266.0   ; CHARMM 22 parameter file
+  C     O       1    0.12290   476976.0   ; URAGUA,CYT,AA
+  C     N       1    0.13350   410032.0   ; AA
+  H     N       1    0.10100   363171.2   ;
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass  typeB    chargeB      massB
+     1   opls_136      1   ASN      CB      1      -0.18     12.011  opls_136d   -0.3   10.0000
+     2   opls_140      1   ASN     HB1      1       0.06      1.008  opls_140d    0.1   10.0000
+     3   opls_140      1   ASN     HB2      1       0.06      1.008  opls_140d    0.1   10.0000
+     4   opls_140      1   ASN     HB3      1       0.06      1.008  opls_140d    0.1   10.0000 
+     5   opls_235      1   ASN      CG      2        0.5     12.011  opls_235d    0.2   10.0000
+     6   opls_236      1   ASN     OD1      2       -0.5    15.9994  opls_236d   -0.2   10.0000
+     7   opls_237      1   ASN     ND2      3      -0.76    14.0067  opls_237d   -0.80   10.0000
+     8   opls_240      1   ASN    HD21      3       0.38      1.008  opls_240d    0.4   10.0000
+     9   opls_240      1   ASN    HD22      3       0.38      1.008  opls_240d    0.4   10.0000
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2     180.0     43.93200   2     
+    5     9     8     7     1    180.0     62.76000   2     180.0     62.76000   2     
+
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
diff --git a/src/testutils/simulationdatabase/freeenergy/vdwalone/conf.gro b/src/testutils/simulationdatabase/freeenergy/vdwalone/conf.gro
new file mode 100644 (file)
index 0000000..3bc7df1
--- /dev/null
@@ -0,0 +1,180 @@
+AA
+  177
+    1ASN     CB    1   2.394   2.264   1.057  0.4128 -0.1855 -0.0593
+    1ASN    HB1    2   2.305   2.326   1.064  0.3410 -0.4295  1.2844
+    1ASN    HB2    3   2.376   2.183   0.987  2.3230  1.2214 -2.2394
+    1ASN    HB3    4   2.479   2.323   1.022  1.1250 -2.2874 -1.9933
+    1ASN     CG    5   2.427   2.188   1.185 -0.1278 -0.4255 -0.0618
+    1ASN    OD1    6   2.356   2.099   1.231 -0.1236 -0.2117  0.3648
+    1ASN    ND2    7   2.539   2.228   1.246 -0.3891  0.2695 -0.0324
+    1ASN   HD21    8   2.598   2.305   1.217  1.3988 -1.7484 -1.9498
+    1ASN   HD22    9   2.549   2.196   1.341  0.7634 -1.3463 -0.6802
+    2SOL     OW   10   1.355   1.479   1.804 -0.0212 -0.0070  0.6222
+    2SOL    HW1   11   1.388   1.449   1.714 -2.8738  0.1320 -0.4925
+    2SOL    HW2   12   1.256   1.469   1.809 -0.0302  1.0050  3.3963
+    3SOL     OW   13   1.475   1.097   1.375 -0.2028  0.1889  0.6694
+    3SOL    HW1   14   1.400   1.052   1.326  1.1864  0.4158 -1.7793
+    3SOL    HW2   15   1.453   1.102   1.472 -3.5147  0.5953 -0.0314
+    4SOL     OW   16   2.871   1.138   0.118 -0.3445  0.4661 -0.1445
+    4SOL    HW1   17   2.877   1.147   0.217  0.5059 -3.1493  0.1793
+    4SOL    HW2   18   2.775   1.133   0.090 -0.5461 -0.2162  0.6749
+    5SOL     OW   19   2.992   2.638   1.384 -0.2202 -0.1375  0.5184
+    5SOL    HW1   20   3.064   2.598   1.441 -1.8208 -0.2514  2.5336
+    5SOL    HW2   21   2.909   2.585   1.392 -0.5218 -0.1250 -1.9710
+    6SOL     OW   22   2.696   1.260   0.895  0.2584  0.4543 -0.4854
+    6SOL    HW1   23   2.677   1.165   0.919  0.2357  0.2207 -1.3970
+    6SOL    HW2   24   2.646   1.320   0.957  1.1573 -0.1221  0.8101
+    7SOL     OW   25   2.125   1.459   0.774 -0.2665 -0.7842  0.7872
+    7SOL    HW1   26   2.200   1.412   0.728 -1.6604 -1.4692 -0.8565
+    7SOL    HW2   27   2.038   1.416   0.747 -1.2367 -0.1120  2.7644
+    8SOL     OW   28   2.270   1.547   1.413 -0.4601  0.0197  0.1857
+    8SOL    HW1   29   2.293   1.544   1.316 -2.7757 -0.9302 -0.3537
+    8SOL    HW2   30   2.301   1.463   1.457 -0.4078 -0.0434  0.0308
+    9SOL     OW   31   2.222   1.796   1.528 -0.3280  0.2262 -0.5296
+    9SOL    HW1   32   2.249   1.703   1.502 -0.2236 -0.8083  2.9895
+    9SOL    HW2   33   2.151   1.829   1.466 -0.1790 -2.0714 -1.9912
+   10SOL     OW   34   2.396   2.959   1.692  0.4658 -0.6434  0.3040
+   10SOL    HW1   35   2.412   2.867   1.655 -0.8905  0.8020 -4.0907
+   10SOL    HW2   36   2.312   2.958   1.746  0.5588 -1.9879  0.4629
+   11SOL     OW   37   2.601   1.736   1.016 -0.4495 -0.6060  0.6206
+   11SOL    HW1   38   2.533   1.772   1.079 -0.3516 -0.0958  0.4360
+   11SOL    HW2   39   2.687   1.720   1.065  0.5149  2.5032  0.0450
+   12SOL     OW   40   2.812   1.652   1.201 -0.0390 -0.0779 -0.3346
+   12SOL    HW1   41   2.792   1.677   1.296  1.2184  1.6248 -0.4965
+   12SOL    HW2   42   2.819   1.552   1.193 -4.7688 -0.5299  0.4114
+   13SOL     OW   43   1.986   1.600   1.365  0.1074  0.0443  0.0750
+   13SOL    HW1   44   2.078   1.562   1.373  1.3626  3.0752  0.8211
+   13SOL    HW2   45   1.951   1.587   1.272  2.0908  0.6538 -0.7625
+   14SOL     OW   46   2.884   1.742   0.586  0.2739  0.4085 -0.5677
+   14SOL    HW1   47   2.974   1.766   0.622  0.3342  0.0109 -0.4476
+   14SOL    HW2   48   2.879   1.643   0.574 -1.2317  0.0561  2.3847
+   15SOL     OW   49   2.735   1.922   1.624 -0.3136 -0.1454  0.0092
+   15SOL    HW1   50   2.714   2.006   1.674  1.1360 -0.2150  0.7535
+   15SOL    HW2   51   2.831   1.923   1.596 -0.7917 -0.6731 -1.7079
+   16SOL     OW   52   2.855   1.927   1.184 -0.2114  0.0481  0.3771
+   16SOL    HW1   53   2.881   1.833   1.163 -0.6035  0.0398 -0.0876
+   16SOL    HW2   54   2.773   1.927   1.243  1.6600  0.0378  3.0629
+   17SOL     OW   55   1.846   1.044   0.645  0.7327  0.0870 -0.2021
+   17SOL    HW1   56   1.889   0.962   0.606 -0.3359 -0.6578  0.1357
+   17SOL    HW2   57   1.775   1.016   0.710 -2.0482  1.1935 -2.6628
+   18SOL     OW   58   1.815   1.751   1.513 -0.3439 -0.1623  0.0660
+   18SOL    HW1   59   1.871   1.684   1.466  1.7527  0.7966  1.1602
+   18SOL    HW2   60   1.810   1.728   1.610 -1.2871  0.1613  0.1048
+   19SOL     OW   61   2.669   1.845   1.377  0.3129 -0.1997  0.0292
+   19SOL    HW1   62   2.682   1.872   1.472  0.5906 -2.0334  0.5199
+   19SOL    HW2   63   2.576   1.870   1.348 -0.1009 -0.5134  1.0749
+   20SOL     OW   64   2.738   1.193   0.360  0.6023  0.4415  0.0641
+   20SOL    HW1   65   2.700   1.283   0.337 -0.9455 -0.0899  0.4855
+   20SOL    HW2   66   2.731   1.179   0.459 -0.2226 -0.6075 -0.1332
+   21SOL     OW   67   2.742   2.818   0.198  0.3666  0.1387 -0.2742
+   21SOL    HW1   68   2.655   2.860   0.224 -0.9915 -1.2735 -2.4293
+   21SOL    HW2   69   2.786   2.872   0.127  0.3610 -0.1559 -0.5055
+   22SOL     OW   70   2.363   1.829   1.270  0.2182 -0.0902 -0.7573
+   22SOL    HW1   71   2.329   1.821   1.364  4.0700 -1.4893  0.6353
+   22SOL    HW2   72   2.371   1.925   1.245  0.2359  0.3296  0.8230
+   23SOL     OW   73   1.630   2.065   0.444 -0.2915  0.2230 -0.5267
+   23SOL    HW1   74   1.594   2.158   0.442 -1.5090 -0.2209 -5.1035
+   23SOL    HW2   75   1.720   2.063   0.401 -0.4265 -0.7703 -0.7783
+   24SOL     OW   76   2.727   1.862   0.776  0.0379 -0.2344  0.4481
+   24SOL    HW1   77   2.797   1.818   0.720  0.6471 -0.2941  1.2451
+   24SOL    HW2   78   2.687   1.794   0.838 -0.2802 -0.0571  0.4379
+   25SOL     OW   79   2.763   0.459   0.957 -0.1927  0.4021  0.1169
+   25SOL    HW1   80   2.696   0.529   0.932  0.4773  0.5694 -1.3175
+   25SOL    HW2   81   2.726   0.401   1.029 -0.5259  2.5953  1.7657
+   26SOL     OW   82   2.335   2.557   1.477 -0.0756  0.3180  0.4448
+   26SOL    HW1   83   2.342   2.626   1.405 -0.2985  2.9134  2.8416
+   26SOL    HW2   84   2.385   2.588   1.558  2.3096 -3.6960  0.6008
+   27SOL     OW   85   0.617   2.200   2.085 -0.0359  0.6455  0.2635
+   27SOL    HW1   86   0.697   2.172   2.137 -0.3261 -0.6615  0.0230
+   27SOL    HW2   87   0.536   2.196   2.143 -0.5012 -1.6570 -0.4687
+   28SOL     OW   88   2.143   0.083   0.691  0.0371  0.2147 -0.3748
+   28SOL    HW1   89   2.206   0.021   0.643 -0.3082  0.5503 -1.2547
+   28SOL    HW2   90   2.054   0.038   0.701  0.6855 -0.6694  1.7743
+   29SOL     OW   91   2.613   0.291   0.331 -0.4737 -0.4760  0.1711
+   29SOL    HW1   92   2.533   0.334   0.373 -0.6532 -0.9957  0.3702
+   29SOL    HW2   93   2.697   0.328   0.370 -0.6247 -0.1279  0.1629
+   30SOL     OW   94   2.850   0.539   0.706 -0.1248  0.0623 -0.3613
+   30SOL    HW1   95   2.841   0.498   0.796  0.5740  0.9496  0.1243
+   30SOL    HW2   96   2.924   0.607   0.707 -1.3879  1.4949 -1.0798
+   31SOL     OW   97   2.812   0.413   0.461  0.6407 -0.0066 -0.4345
+   31SOL    HW1   98   2.895   0.369   0.425  0.6614  1.2194 -1.9112
+   31SOL    HW2   99   2.839   0.482   0.529  0.8069  0.0459 -0.5491
+   32SOL     OW  100   2.063   2.221   1.363  0.0936  0.4397  0.0899
+   32SOL    HW1  101   2.149   2.191   1.323 -0.6726 -0.9597 -0.5503
+   32SOL    HW2  102   2.081   2.276   1.444  1.5606 -0.4190  0.3595
+   33SOL     OW  103   2.391   2.228   1.818  0.3625 -0.3541  0.1406
+   33SOL    HW1  104   2.369   2.233   1.915 -0.6157 -0.1208 -0.0900
+   33SOL    HW2  105   2.369   2.138   1.783 -1.0721  0.1555 -0.2896
+   34SOL     OW  106   2.787   2.409   1.424 -0.0638 -0.5060 -0.0370
+   34SOL    HW1  107   2.692   2.441   1.417  0.4423  1.2825  1.0010
+   34SOL    HW2  108   2.819   2.381   1.334 -0.0697  3.2835 -1.2882
+   35SOL     OW  109   2.341   0.602   0.079  0.3289  0.3826 -0.0256
+   35SOL    HW1  110   2.350   0.504   0.098 -0.5469  0.3913  0.4879
+   35SOL    HW2  111   2.428   0.647   0.095  0.5096 -0.1750  0.6148
+   36SOL     OW  112   2.563   2.509   1.335  0.2784 -0.4602  0.2326
+   36SOL    HW1  113   2.475   2.481   1.375 -0.1910  0.0135 -0.4622
+   36SOL    HW2  114   2.547   2.575   1.262  1.0213 -2.6007 -1.9336
+   37SOL     OW  115   2.718   2.774   1.730 -0.2630 -0.0929 -0.2009
+   37SOL    HW1  116   2.814   2.748   1.735 -0.3557 -0.4491 -0.1558
+   37SOL    HW2  117   2.710   2.874   1.729  0.1770 -0.0633 -1.5850
+   38SOL     OW  118   2.286   1.961   1.747  0.0327  0.0735 -0.5785
+   38SOL    HW1  119   2.282   1.922   1.655  0.3066 -0.7901 -0.2228
+   38SOL    HW2  120   2.242   1.900   1.812  0.4340  0.3683 -0.0244
+   39SOL     OW  121   2.391   2.260   1.519  0.2429  0.0935 -0.2536
+   39SOL    HW1  122   2.380   2.352   1.481  1.8483  0.9787  1.3625
+   39SOL    HW2  123   2.406   2.266   1.617 -4.1364 -2.1648  0.6885
+   40SOL     OW  124   2.024   1.925   1.393  0.3160  0.3695 -0.0318
+   40SOL    HW1  125   1.995   2.021   1.388  0.4145  0.3916 -0.2173
+   40SOL    HW2  126   1.954   1.872   1.442  0.0392  0.4518 -0.3275
+   41SOL     OW  127   2.898   2.850   1.264  0.0284 -0.7047 -0.1261
+   41SOL    HW1  128   2.956   2.871   1.185 -0.2533 -0.1391 -0.1873
+   41SOL    HW2  129   2.937   2.773   1.315  0.3343 -0.9184 -0.6838
+   42SOL     OW  130   2.680   0.003   1.719 -0.0834  0.1289  0.1791
+   42SOL    HW1  131   2.690   0.102   1.729  0.3451  0.3054 -1.7623
+   42SOL    HW2  132   2.583  -0.020   1.707 -0.2252  0.2658  1.0534
+   43SOL     OW  133   2.404   2.736   1.251  0.1212 -0.2045 -0.3250
+   43SOL    HW1  134   2.475   2.804   1.267 -1.1778  0.9204  0.7999
+   43SOL    HW2  135   2.323   2.781   1.213 -0.3591 -1.3084 -0.6282
+   44SOL     OW  136   2.687   1.749   1.826 -0.3355  0.4974 -0.3181
+   44SOL    HW1  137   2.688   1.805   1.743 -1.5153 -0.2892 -0.8629
+   44SOL    HW2  138   2.773   1.762   1.875 -0.0257  1.7219 -1.1527
+   45SOL     OW  139   2.737   2.163   1.715 -0.7137 -0.0278 -0.5263
+   45SOL    HW1  140   2.795   2.240   1.743 -0.3268 -0.7009  0.5508
+   45SOL    HW2  141   2.641   2.191   1.720 -0.5032  1.0263 -2.1256
+   46SOL     OW  142   1.847   2.467   1.022  0.2767  0.1470 -0.1817
+   46SOL    HW1  143   1.781   2.414   1.075 -0.5905  0.6201 -0.7880
+   46SOL    HW2  144   1.923   2.407   0.994 -0.8049 -0.8320 -1.0495
+   47SOL     OW  145   1.851   2.751   1.060 -0.2623  0.2615  0.1315
+   47SOL    HW1  146   1.875   2.751   1.156 -0.0807 -0.2087  0.0883
+   47SOL    HW2  147   1.855   2.657   1.024 -1.6121  0.2467 -0.0037
+   48SOL     OW  148   2.549   3.023   0.306 -0.2880 -0.4949 -0.3256
+   48SOL    HW1  149   2.570   3.119   0.289 -2.1562 -0.1244 -0.6982
+   48SOL    HW2  150   2.546   3.006   0.404 -1.1581  0.1097 -0.2459
+   49SOL     OW  151   2.761   2.321   1.065  0.3713  0.0626  0.4861
+   49SOL    HW1  152   2.737   2.397   1.004  1.4132  0.3887  0.4711
+   49SOL    HW2  153   2.727   2.235   1.026  0.4404  0.3477 -0.2227
+   50SOL     OW  154   2.613   2.040   0.566  0.4474  0.2492  0.2494
+   50SOL    HW1  155   2.667   1.975   0.619  0.5437  0.0712 -0.0611
+   50SOL    HW2  156   2.556   2.095   0.628 -0.3411 -0.7769  0.4481
+   51SOL     OW  157   2.598   2.926   1.267  0.1725  0.2854 -0.4032
+   51SOL    HW1  158   2.687   2.904   1.307 -0.0830  0.0411  0.0299
+   51SOL    HW2  159   2.612   2.981   1.184  0.7840 -0.8423 -1.0622
+   52SOL     OW  160   2.467   2.678   1.696 -0.2578  0.1688  0.6544
+   52SOL    HW1  161   2.443   2.668   1.792  1.0503 -0.1142  0.9633
+   52SOL    HW2  162   2.567   2.688   1.687 -0.3958  0.5140 -0.6382
+   53SOL     OW  163   2.765   2.064   0.946 -0.1162 -0.1091  0.3365
+   53SOL    HW1  164   2.742   1.988   0.885 -0.2348 -0.8852  1.3323
+   53SOL    HW2  165   2.805   2.028   1.030 -0.1032  0.9425  0.7926
+   54SOL     OW  166   2.232   0.365   0.784  0.0201 -0.2103  0.0516
+   54SOL    HW1  167   2.218   0.361   0.883  0.2123 -1.7510  0.0272
+   54SOL    HW2  168   2.211   0.276   0.743  1.9813 -0.2128 -1.0094
+   55SOL     OW  169   2.334   1.600   1.146  0.1149 -0.2684  0.1210
+   55SOL    HW1  170   2.349   1.688   1.191  0.8588  0.1063 -0.8344
+   55SOL    HW2  171   2.259   1.609   1.080  0.4483 -0.2511 -0.2565
+   56SOL     OW  172   2.525   1.454   1.007  0.2514  0.5684  0.5252
+   56SOL    HW1  173   2.583   1.533   0.990  0.1840  0.4123 -0.5096
+   56SOL    HW2  174   2.447   1.480   1.063 -0.1020  1.3765 -0.3338
+   57SOL     OW  175   2.137   1.630   0.975  0.0467  0.0519  0.1398
+   57SOL    HW1  176   2.133   1.571   0.895  1.5778 -1.4571  1.1319
+   57SOL    HW2  177   2.111   1.723   0.950  0.4725 -0.4071 -2.0914
+   3.03000   3.03000   2.14253   0.00000   0.00000   0.00000   0.00000   1.51500   1.51500
diff --git a/src/testutils/simulationdatabase/freeenergy/vdwalone/grompp.mdp b/src/testutils/simulationdatabase/freeenergy/vdwalone/grompp.mdp
new file mode 100644 (file)
index 0000000..2c1683e
--- /dev/null
@@ -0,0 +1,141 @@
+; RUN CONTROL PARAMETERS
+integrator               = md-vv
+; Start time and timestep in ps
+tinit                    = 0.0
+dt                       = 0.001
+nsteps                   = 20
+; For exact run continuation or redoing part of a run
+init-step                = 0
+; Part index is updated automatically on checkpointing (keeps files separate)
+simulation-part          = 1
+; mode for center of mass motion removal
+comm-mode                = Linear
+; number of steps for center of mass motion removal
+nstcomm                  = 1
+; group(s) for center of mass motion removal
+comm-grps                = 
+
+; OUTPUT CONTROL OPTIONS
+; Output frequency for coords (x), velocities (v) and forces (f)
+nstxout                  = 20
+nstvout                  = 20
+nstfout                  = 20
+; Output frequency for energies to log file and energy file
+nstlog                   = 5
+nstcalcenergy            = 1
+nstenergy                = 1
+; Output frequency and precision for .xtc file
+nstxout-compressed       = 0
+compressed-x-precision   = 1000
+; This selects the subset of atoms for the compressed
+; trajectory file. You can select multiple groups. By
+; default, all atoms will be written.
+compressed-x-grps        = 
+; Selection of energy groups
+energygrps               = 
+
+; NEIGHBORSEARCHING PARAMETERS
+; cut-off scheme (Verlet: particle based cut-offs)
+cutoff-scheme            = Verlet
+; nblist update frequency
+nstlist                  = 10
+; Periodic boundary conditions: xyz, no, xy
+pbc                      = xyz
+periodic-molecules       = no
+; Allowed energy error due to the Verlet buffer in kJ/mol/ps per atom,
+; a value of -1 means: use rlist
+verlet-buffer-tolerance  = 0.005
+; nblist cut-off        
+rlist                    = 1.0
+; long-range cut-off for switched potentials
+
+; OPTIONS FOR ELECTROSTATICS AND VDW
+; Method for doing electrostatics
+coulomb_type             = pme
+coulomb-modifier         = Potential-shift-Verlet
+rcoulomb_switch          = 0.99
+rcoulomb                 = 1.0
+; Relative dielectric constant for the medium and the reaction field
+epsilon_r                = 1.0
+epsilon-rf               = 0
+; Method for doing Van der Waals
+vdw_type                 = Cut-off
+vdw-modifier             = Force-switch
+; cut-off lengths       
+rvdw_switch              = 0.8
+rvdw                     = 1
+; Apply long range dispersion corrections for Energy and Pressure
+dispcorr                 = enerpres
+; Extension of the potential lookup tables beyond the cut-off
+table-extension          = 1
+; Separate tables between energy group pairs
+energygrp-table          = 
+; Spacing for the PME/PPPM FFT grid
+fourierspacing           = 0.12
+; EWALD/PME/PPPM parameters
+pme-order                = 4
+ewald_rtol               = 1e-6
+ewald-geometry           = 3d
+epsilon-surface          = 0
+
+; OPTIONS FOR WEAK COUPLING ALGORITHMS
+; Temperature coupling  
+tcoupl                   = berendsen
+nsttcouple               = 1
+; Groups to couple separately
+tc-grps                  = system
+; Time constant (ps) and reference temperature (K)
+tau_t                    = 0.1
+ref_t                    = 298
+; pressure coupling     
+Pcoupl                   = no
+
+; GENERATE VELOCITIES FOR STARTUP RUN
+gen-vel                  = no
+
+; OPTIONS FOR BONDS    
+constraints              = h-bonds
+; Type of constraint algorithm
+constraint-algorithm     = Lincs
+; Do not constrain the start configuration
+continuation             = no
+; Highest order in the expansion of the constraint coupling matrix
+lincs-order              = 8
+; Number of iterations in the final step of LINCS. 1 is fine for
+; normal simulations, but use 2 to conserve energy in NVE runs.
+; For energy minimization with constraints it should be 4 to 8.
+lincs-iter               = 2
+; Lincs will write a warning to the stderr if in one step a bond
+; rotates over more degrees than
+lincs-warnangle          = 30
+; Convert harmonic bonds to morse potentials
+morse                    = no
+
+; Free energy variables
+free-energy              = yes
+couple-moltype           = 
+couple-lambda0           = vdw
+couple-lambda1           = none
+couple-intramol          = no
+init-lambda              = 0.25
+nstdhdl                  = 5
+fep-lambdas              = 
+mass-lambdas             = 
+coul-lambdas             = 
+vdw-lambdas              = 
+bonded-lambdas           = 
+restraint-lambdas        = 
+temperature-lambdas      = 
+calc-lambda-neighbors    = 1
+init-lambda-weights      = 
+dhdl-print-energy        = no
+sc-alpha                 = 0.5
+sc-power                 = 1
+sc-r-power               = 6
+sc-sigma                 = 0.3
+sc-coul                  = no
+separate-dhdl-file       = no
+dhdl-derivatives         = yes
+dh_hist_size             = 0
+dh_hist_spacing          = 0.1
+
diff --git a/src/testutils/simulationdatabase/freeenergy/vdwalone/no-nb-gpu-support b/src/testutils/simulationdatabase/freeenergy/vdwalone/no-nb-gpu-support
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/testutils/simulationdatabase/freeenergy/vdwalone/topol.top b/src/testutils/simulationdatabase/freeenergy/vdwalone/topol.top
new file mode 100644 (file)
index 0000000..15b24f2
--- /dev/null
@@ -0,0 +1,146 @@
+[ defaults ]
+; nbfunc       comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1              3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_116   OW 8   15.99940    -0.820       A    3.16557e-01  6.50194e-01
+ opls_117   HW 1    1.00800     0.410       A    0.00000e+00  0.00000e+00
+ opls_136   CT 6   12.01100    -0.120       A    3.50000e-01  2.76144e-01
+ opls_140   HC 1    1.00800     0.060       A    2.50000e-01  1.25520e-01
+ opls_235   C   6   12.01100     0.500       A    3.75000e-01  4.39320e-01
+ opls_236   O   8   15.99940    -0.500       A    2.96000e-01  8.78640e-01
+ opls_237   N   7   14.00670    -0.760       A    3.25000e-01  7.11280e-01
+ opls_240   H   1    1.00800     0.380       A    0.00000e+00  0.00000e+00
+ opls_116d  OW 8   15.99940    -0.820       A    3.16557e-01  0
+ opls_117d  HW 1    1.00800     0.410       A    0.00000e+00  0
+ opls_136d  CT 6   12.01100    -0.120       A    3.00000e-01  0  
+ opls_140d  HC 1    1.00800     0.060       A    2.00000e-01  0
+ opls_235d  C   6   12.01100     0.500       A    3.0000e-01   0
+ opls_236d  O   8   15.99940    -0.500       A    4.0000e-01   0
+ opls_237d  N   7   14.00670    -0.760       A    3.0000e-01   0
+ opls_240d  H   1    1.00800     0.380       A    0.00000e+00  0
+
+
+[ bondtypes]
+  CT    HC      1    0.10900   284512.0   ; CHARMM 22 parameter file
+  CT    C       1    0.15220   265266.0   ; CHARMM 22 parameter file
+  C     O       1    0.12290   476976.0   ; URAGUA,CYT,AA
+  C     N       1    0.13350   410032.0   ; AA
+  H     N       1    0.10100   363171.2   ;
+
+[ angletypes ]
+  HC     CT     HC      1   107.800    276.144   
+  C      CT     HC      1   109.500    292.880   
+  CT     C      N       1   116.600    585.760   
+  CT     C      O       1   120.400    669.440   
+  C      N      H       1   119.800    292.880   
+  N      C      O       1   122.900    669.440   
+  H      N      H       1   120.000    292.880   
+
+[ dihedraltypes ]
+ HC    CT      C      O       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ HC    CT      C      N       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+  O     C      N      H       3     20.50160   0.00000 -20.50160   0.00000   0.00000   0.00000 ; amides C-C(O)-N-H
+ CT     N      O      C       1    180.0     43.93200   2
+  C     H      H      N       1    180.0     62.76000   2
+
+[ moleculetype ]
+; Name            nrexcl
+ASN             3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass     typeB  chargeB    massB
+     1   opls_136      1   ASN      CB      1       0.00     12.011  opls_136d    0.0   12.011
+     2   opls_140      1   ASN     HB1      1       0.00      1.008  opls_140d    0.0    1.008
+     3   opls_140      1   ASN     HB2      1       0.00      1.008  opls_140d    0.0    1.008
+     4   opls_140      1   ASN     HB3      1       0.00      1.008  opls_140d    0.0    1.008 
+     5   opls_235      1   ASN      CG      2       0.00     12.011  opls_235d    0.0   12.011
+     6   opls_236      1   ASN     OD1      2       0.00    15.9994  opls_236d    0.0   15.9994
+     7   opls_237      1   ASN     ND2      3       0.00    14.0067  opls_237d    0.0   14.0067
+     8   opls_240      1   ASN    HD21      3       0.00      1.008  opls_240d    0.0    1.008
+     9   opls_240      1   ASN    HD22      3       0.00      1.008  opls_240d    0.0    1.008
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1 
+    1     3     1 
+    1     4     1 
+    1     5     1 
+    5     6     1 
+    5     7     1 
+    7     8     1 
+    7     9     1 
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1 
+    1     9     1 
+    2     6     1 
+    2     7     1 
+    3     6     1 
+    3     7     1 
+    4     6     1 
+    4     7     1 
+    6     8     1 
+    6     9     1 
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1 
+    2     1     4     1 
+    2     1     5     1 
+    3     1     4     1 
+    3     1     5     1 
+    4     1     5     1 
+    1     5     6     1   
+    1     5     7     1 
+    6     5     7     1 
+    5     7     8     1 
+    5     7     9     1 
+    8     7     9     1 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3 
+    2     1     5     7     3 
+    3     1     5     6     3 
+    3     1     5     7     3 
+    4     1     5     6     3 
+    4     1     5     7     3 
+    1     5     7     8     3 
+    1     5     7     9     3 
+    6     5     7     8     3 
+    6     5     7     9     3 
+
+[ dihedrals ]
+;  ai    aj    ak    al funct    c0            c1            c2
+    1     7     6     5     1    180.0     43.93200   2      180.0     43.93200   2     
+    5     9     8     7     1    180.0     62.76000   2      180.0     62.76000   2     
+
+
+[ moleculetype ]
+; molname      nrexcl
+SOL            2
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge       mass
+     1  opls_116   1    SOL     OW      1      -0.8476
+     2  opls_117   1    SOL    HW1      1       0.4238
+     3  opls_117   1    SOL    HW2      1       0.4238
+
+[ settles ]
+; OW   funct   doh     dhh
+1      1       0.1     0.16330
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+AA
+
+[ molecules ]
+ASN 1
+SOL 56
index 28b0a3f77d1f5a07a0dd82f1ec431e023f7513c8..e16a99e8a155e797cd473e4af211cea805a474f5 100644 (file)
@@ -3,7 +3,7 @@
 ; original reference: [M. W. Mahoney and W. L. Jorgensen, J. Chem. Phys. 112 , 2000]
 ;
 ; Note that there are various issues with tip5p and the different forcefields.
-; Discussion is here: http://redmine.gromacs.org/issues/1348
+; Discussion is here: https://gitlab.com/gromacs/gromacs/-/issues/1348
 
 [ moleculetype ]
 ; molname       nrexcl
index dd766d12c56911fc8bd71b045ff4ce4e437c78e4..edf09974a114e239f027c10f8eeb26256844cce3 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
diff --git a/src/testutils/test_device.cpp b/src/testutils/test_device.cpp
new file mode 100644 (file)
index 0000000..8d3865e
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements test environment class which performs hardware enumeration for unit tests.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_testutils
+ */
+#include "gmxpre.h"
+
+#include "test_device.h"
+
+#include <memory>
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/hardware/detecthardware.h"
+#include "gromacs/hardware/hw_info.h"
+#include "gromacs/utility/basenetwork.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/loggerbuilder.h"
+#include "gromacs/utility/physicalnodecommunicator.h"
+
+namespace gmx
+{
+namespace test
+{
+
+class TestDevice::Impl
+{
+public:
+    Impl(const char* description);
+    Impl(const char* description, const DeviceInformation& deviceInfo);
+    ~Impl();
+    //! Returns a human-readable context description line
+    std::string description() const { return description_; }
+    //! Returns the device info pointer
+    const DeviceInformation& deviceInfo() const { return deviceContext_.deviceInfo(); }
+    //! Get the device context
+    const DeviceContext& deviceContext() const { return deviceContext_; }
+    //! Get the device stream
+    const DeviceStream& deviceStream() const { return deviceStream_; }
+
+private:
+    //! Readable description
+    std::string description_;
+    //! Device context
+    DeviceContext deviceContext_;
+    //! Device stream
+    DeviceStream deviceStream_;
+};
+
+TestDevice::Impl::Impl(const char* description, const DeviceInformation& deviceInfo) :
+    description_(description),
+    deviceContext_(deviceInfo),
+    deviceStream_(deviceContext_, DeviceStreamPriority::Normal, false)
+{
+}
+
+TestDevice::Impl::~Impl() = default;
+
+TestDevice::TestDevice(const char* description, const DeviceInformation& deviceInfo) :
+    impl_(new Impl(description, deviceInfo))
+{
+}
+
+TestDevice::~TestDevice() = default;
+
+std::string TestDevice::description() const
+{
+    return impl_->description();
+}
+
+const DeviceInformation& TestDevice::deviceInfo() const
+{
+    return impl_->deviceInfo();
+}
+
+const DeviceContext& TestDevice::deviceContext() const
+{
+    return impl_->deviceContext();
+}
+
+const DeviceStream& TestDevice::deviceStream() const
+{
+    return impl_->deviceStream();
+}
+
+} // namespace test
+} // namespace gmx
diff --git a/src/testutils/test_device.h b/src/testutils/test_device.h
new file mode 100644 (file)
index 0000000..3fa7915
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TESTUTILS_TEST_DEVICE_H
+#define GMX_TESTUTILS_TEST_DEVICE_H
+
+/*! \internal \file
+ * \brief
+ * Describes test environment class which performs GPU device enumeration for unit tests.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_testutils
+ */
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+
+class DeviceContext;
+struct DeviceInformation;
+class DeviceStream;
+
+namespace gmx
+{
+namespace test
+{
+
+/*! \internal \brief
+ * A structure to describe a hardware context that persists over the lifetime
+ * of the test binary.
+ */
+class TestDevice
+{
+public:
+    //! Returns a human-readable context description line
+    std::string description() const;
+    //! Returns the device info pointer
+    const DeviceInformation& deviceInfo() const;
+    //! Get the device context
+    const DeviceContext& deviceContext() const;
+    //! Get the device stream
+    const DeviceStream& deviceStream() const;
+    //! Creates the device context and stream for tests on the GPU
+    TestDevice(const char* description, const DeviceInformation& deviceInfo);
+    //! Destructor
+    ~TestDevice();
+
+private:
+    //! Implementation type.
+    class Impl;
+    //! Implementation object.
+    PrivateImplPointer<Impl> impl_;
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_TESTUTILS_TEST_DEVICE_H
similarity index 65%
rename from src/gromacs/ewald/tests/testhardwarecontexts.cpp
rename to src/testutils/test_hardware_environment.cpp
index 6e0a888402bd74c48183733319f5d904fea08146..a17aa980075829760013337cffceddee4db08775 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * Implements test environment class which performs hardware enumeration for unit tests.
  *
  * \author Aleksei Iupinov <a.yupinov@gmail.com>
- * \ingroup module_ewald
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_testutils
  */
 
 #include "gmxpre.h"
 
-#include "testhardwarecontexts.h"
+#include "test_hardware_environment.h"
 
 #include <memory>
 
-#include "gromacs/ewald/pme.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/hardware/detecthardware.h"
+#include "gromacs/hardware/device_management.h"
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/utility/basenetwork.h"
 #include "gromacs/utility/exceptions.h"
@@ -60,18 +62,6 @@ namespace gmx
 namespace test
 {
 
-TestHardwareContext::~TestHardwareContext() = default;
-
-const char* codePathToString(CodePath codePath)
-{
-    switch (codePath)
-    {
-        case CodePath::CPU: return "CPU";
-        case CodePath::GPU: return "GPU";
-        default: GMX_THROW(NotImplementedError("This CodePath should support codePathToString"));
-    }
-}
-
 /* Implements the "construct on first use" idiom to avoid any static
  * initialization order fiasco.
  *
@@ -82,21 +72,21 @@ const char* codePathToString(CodePath codePath)
  * so there is no deinitialization issue.  See
  * https://isocpp.org/wiki/faq/ctors for discussion of alternatives
  * and trade-offs. */
-const PmeTestEnvironment* getPmeTestEnv()
+const TestHardwareEnvironment* getTestHardwareEnvironment()
 {
-    static PmeTestEnvironment* pmeTestEnvironment = nullptr;
-    if (pmeTestEnvironment == nullptr)
+    static TestHardwareEnvironment* testHardwareEnvironment = nullptr;
+    if (testHardwareEnvironment == nullptr)
     {
         // Ownership of the TestEnvironment is taken by GoogleTest, so nothing can leak
-        pmeTestEnvironment = static_cast<PmeTestEnvironment*>(
-                ::testing::AddGlobalTestEnvironment(new PmeTestEnvironment));
+        testHardwareEnvironment = static_cast<TestHardwareEnvironment*>(
+                ::testing::AddGlobalTestEnvironment(new TestHardwareEnvironment));
     }
-    return pmeTestEnvironment;
+    return testHardwareEnvironment;
 }
 
 void callAddGlobalTestEnvironment()
 {
-    getPmeTestEnv();
+    getTestHardwareEnvironment();
 }
 
 //! Simple hardware initialization
@@ -106,29 +96,22 @@ static gmx_hw_info_t* hardwareInit()
     return gmx_detect_hardware(MDLogger{}, physicalNodeComm);
 }
 
-void PmeTestEnvironment::SetUp()
+void TestHardwareEnvironment::SetUp()
 {
-    hardwareContexts_.emplace_back(std::make_unique<TestHardwareContext>(CodePath::CPU, "", nullptr));
-
+    testDeviceList_.clear();
     hardwareInfo_ = hardwareInit();
-    if (!pme_gpu_supports_build(nullptr) || !pme_gpu_supports_hardware(*hardwareInfo_, nullptr))
-    {
-        // PME can only run on the CPU, so don't make any more test contexts.
-        return;
-    }
     // Constructing contexts for all compatible GPUs - will be empty on non-GPU builds
-    for (int gpuIndex : getCompatibleGpus(hardwareInfo_->gpu_info))
+    for (const DeviceInformation& compatibleDeviceInfo : getCompatibleDevices(hardwareInfo_->deviceInfoList))
     {
-        const gmx_device_info_t* deviceInfo = getDeviceInfo(hardwareInfo_->gpu_info, gpuIndex);
-        init_gpu(deviceInfo);
-
-        char stmp[200] = {};
-        get_gpu_device_info_string(stmp, hardwareInfo_->gpu_info, gpuIndex);
-        std::string description = "(GPU " + std::string(stmp) + ") ";
-        hardwareContexts_.emplace_back(std::make_unique<TestHardwareContext>(
-                CodePath::GPU, description.c_str(), deviceInfo));
+        std::string description = getDeviceInformationString(compatibleDeviceInfo);
+        testDeviceList_.emplace_back(std::make_unique<TestDevice>(description.c_str(), compatibleDeviceInfo));
     }
 }
 
+void TestHardwareEnvironment::TearDown()
+{
+    testDeviceList_.clear();
+}
+
 } // namespace test
 } // namespace gmx
similarity index 55%
rename from src/gromacs/ewald/tests/testhardwarecontexts.h
rename to src/testutils/test_hardware_environment.h
index 20d208d870bb0d76263d184bb09af189ec67161d..b012ae90d5c78749c34190ecf6058cfd77599837 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-#ifndef GMX_EWALD_TEST_HARDWARE_CONTEXTS_H
-#define GMX_EWALD_TEST_HARDWARE_CONTEXTS_H
+#ifndef GMX_TESTUTILS_TEST_HARDWARE_ENVIRONMENT_H
+#define GMX_TESTUTILS_TEST_HARDWARE_ENVIRONMENT_H
 
 /*! \internal \file
  * \brief
  * Describes test environment class which performs hardware enumeration for unit tests.
  *
  * \author Aleksei Iupinov <a.yupinov@gmail.com>
- * \ingroup module_ewald
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_testutils
  */
 
 #include <map>
@@ -48,8 +50,9 @@
 
 #include <gtest/gtest.h>
 
-#include "gromacs/ewald/pme_gpu_program.h"
-#include "gromacs/hardware/gpu_hw_info.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "testutils/test_device.h"
 
 struct gmx_hw_info_t;
 
@@ -57,76 +60,35 @@ namespace gmx
 {
 namespace test
 {
-//! Hardware code path being tested
-enum class CodePath
-{
-    CPU,
-    GPU
-};
-
-//! Return a string useful for human-readable messages describing a \c codePath.
-const char* codePathToString(CodePath codePath);
-
-/*! \internal \brief
- * A structure to describe a hardware context  that persists over the lifetime
- * of the test binary - an abstraction over PmeGpuProgram with a human-readable string.
- */
-struct TestHardwareContext
-{
-    //! Hardware path for the code being tested.
-    CodePath codePath_;
-    //! Readable description
-    std::string description_;
-    //! Device information pointer
-    const gmx_device_info_t* deviceInfo_;
-    //! Persistent compiled GPU kernels for PME.
-    PmeGpuProgramStorage program_;
-
-public:
-    //! Retuns the code path for this context.
-    CodePath getCodePath() const { return codePath_; }
-    //! Returns a human-readable context description line
-    std::string getDescription() const { return description_; }
-    //! Returns the device info pointer
-    const gmx_device_info_t* getDeviceInfo() const { return deviceInfo_; }
-    //! Returns the persistent PME GPU kernels
-    PmeGpuProgramHandle getPmeGpuProgram() const { return program_.get(); }
-    //! Constructs the context
-    TestHardwareContext(CodePath codePath, const char* description, const gmx_device_info_t* deviceInfo) :
-        codePath_(codePath),
-        description_(description),
-        deviceInfo_(deviceInfo),
-        program_(buildPmeGpuProgram(deviceInfo_))
-    {
-    }
-    ~TestHardwareContext();
-};
-
-//! A container of handles to hardware contexts
-typedef std::vector<std::unique_ptr<TestHardwareContext>> TestHardwareContexts;
 
 /*! \internal \brief
  * This class performs one-time test initialization (enumerating the hardware)
  */
-class PmeTestEnvironment : public ::testing::Environment
+class TestHardwareEnvironment : public ::testing::Environment
 {
 private:
     //! General hardware info
     gmx_hw_info_t* hardwareInfo_;
     //! Storage of hardware contexts
-    TestHardwareContexts hardwareContexts_;
+    std::vector<std::unique_ptr<TestDevice>> testDeviceList_;
 
 public:
     //! This is called by GTest framework once to query the hardware
     void SetUp() override;
+    //! This is called by GTest framework once release the hardware
+    void TearDown() override;
     //! Get available hardware contexts.
-    const TestHardwareContexts& getHardwareContexts() const { return hardwareContexts_; }
+    const std::vector<std::unique_ptr<TestDevice>>& getTestDeviceList() const
+    {
+        return testDeviceList_;
+    }
+    bool hasCompatibleDevices() const { return !testDeviceList_.empty(); }
     //! Get available hardware information.
     const gmx_hw_info_t* hwinfo() const { return hardwareInfo_; }
 };
 
 //! Get the test environment
-const PmeTestEnvironment* getPmeTestEnv();
+const TestHardwareEnvironment* getTestHardwareEnvironment();
 
 /*! \brief This constructs the test environment during setup of the
  * unit test so that they can use the hardware context. */
@@ -134,4 +96,4 @@ void callAddGlobalTestEnvironment();
 
 } // namespace test
 } // namespace gmx
-#endif
+#endif // GMX_TESTUTILS_TEST_HARDWARE_ENVIRONMENT_H
index 37096d143bdccf5aad0c10f45cfff1f29db434ee..a737f03e4c6be1920d869b0bd54387280e17bce8 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index b59434dd6d333d99ab6b14cea63d12eead03609b..cb0a1ceeb5b1203298b6762c562ff8c7f3187397 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index d415c28e5dcabea96bfe2aac0e4eb6fc1509cec8..53dbba16c63f556b6947a99b0ea5e4a7923cfbca 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
index 63b37f9118303299db1c06bc324d11c0c0e4588e..753b814a0f3669d90f5a31ef8b80b335b47d1ccc 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -69,6 +70,7 @@
 
 #include "testutils/mpi_printer.h"
 #include "testutils/refdata.h"
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testfilemanager.h"
 #include "testutils/testoptions.h"
 
index 3b5227bf0a206490af33b62cafb026c42f072190..4f7e3b40aa0a6cbed246f32f0ddeae24e9849073 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -88,19 +89,6 @@ void initTestUtils(const char* dataPath,
 void finalizeTestUtils();
 //! \endcond
 
-/*! \brief Declare a function that all unit test implementations can use
- * to set up any environment that they need.
- *
- * When registering the unit test in CMake, the HARDWARE_DETECTION
- * flag requires that the code for that unit test implements this
- * function.  Otherwise, a default stub implementation is provided.
- *
- * This approach conforms to the recommendation by GoogleTest to
- * arrange for the code that sets up the global test environment to be
- * called from main, rather than potentially rely on brittle static
- * initialization order. */
-void callAddGlobalTestEnvironment();
-
 } // namespace test
 } // namespace gmx
 
index ab2d4f914ac15ded3395c7b232b351c5f4426e19..328c9a7f310bbe3fab0bdabfd88e59ae22254065 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -170,7 +171,7 @@ void initTestOptions(IOptionsContainer* options);
         virtual void initOptions(::gmx::IOptionsContainer*(options));                                                  \
     };                                                                                                                 \
                                                                                                                        \
-    static name s_##name##Instance;                                                                                    \
+    static gmx_unused name s_##name##Instance;                                                                         \
                                                                                                                        \
     void name::initOptions(::gmx::IOptionsContainer* options) //NOLINT(misc-macro-parentheses,bugprone-macro-parentheses)
 
index 3070802cbf1f6398423ba507ab41f43d4a575b9a..7a6d651ab43e09df488764b9644772482ae9d6b7 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2011,2012,2014,2015,2016, by the GROMACS development team, led by
+# Copyright (c) 2011,2012,2014,2015,2016,2020, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
 # the research papers on the package. Check out http://www.gromacs.org.
 
 gmx_add_unit_test(TestUtilsUnitTests testutils-test
-                  interactivetest.cpp
-                  refdata_tests.cpp
-                  testasserts_tests.cpp
-                  xvgtest_tests.cpp)
+    CPP_SOURCE_FILES
+        interactivetest.cpp
+        refdata_tests.cpp
+        testasserts_tests.cpp
+        xvgtest_tests.cpp
+        )
 
 gmx_add_mpi_unit_test(TestUtilsMpiUnitTests testutils-mpi-test 2
-                      mpitest.cpp)
+    CPP_SOURCE_FILES
+        mpitest.cpp
+        )
index 21744bf4bdc884ff807eefb9e0fde7416f2eb151..93bfb2f1782bac45d86e15bc45b95482c60ab9f7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 
 #include "testutils/refdata.h"
 
+namespace gmx
+{
+namespace test
+{
 namespace
 {
 
 class InteractiveSession
 {
 public:
-    explicit InteractiveSession(gmx::test::ReferenceDataMode mode) :
+    explicit InteractiveSession(ReferenceDataMode mode) :
         data_(mode),
         helper_(data_.rootChecker()),
         nextInputLine_(0)
@@ -123,17 +127,17 @@ private:
     // The latter is the output string.
     typedef std::pair<EventType, const char*> Event;
 
-    gmx::test::TestReferenceData     data_;
-    gmx::test::InteractiveTestHelper helper_;
-    std::vector<const char*>         inputLines_;
-    size_t                           nextInputLine_;
-    std::vector<Event>               events_;
+    TestReferenceData        data_;
+    InteractiveTestHelper    helper_;
+    std::vector<const char*> inputLines_;
+    size_t                   nextInputLine_;
+    std::vector<Event>       events_;
 };
 
 TEST(InteractiveTestHelperTest, ChecksSimpleSession)
 {
     {
-        InteractiveSession session(gmx::test::erefdataUpdateAll);
+        InteractiveSession session(ReferenceDataMode::UpdateAll);
         session.addOutput("First line\n");
         session.addOutput("> ");
         session.addInput("input");
@@ -145,7 +149,7 @@ TEST(InteractiveTestHelperTest, ChecksSimpleSession)
         session.run();
     }
     {
-        InteractiveSession session(gmx::test::erefdataCompare);
+        InteractiveSession session(ReferenceDataMode::Compare);
         session.addOutput("First line\n");
         session.addOutput("> ");
         session.addInput("input");
@@ -161,7 +165,7 @@ TEST(InteractiveTestHelperTest, ChecksSimpleSession)
 TEST(InteractiveTestHelperTest, ChecksSessionWithoutLastNewline)
 {
     {
-        InteractiveSession session(gmx::test::erefdataUpdateAll);
+        InteractiveSession session(ReferenceDataMode::UpdateAll);
         session.addOutput("First line\n");
         session.addOutput("> ");
         session.addInput("input");
@@ -173,7 +177,7 @@ TEST(InteractiveTestHelperTest, ChecksSessionWithoutLastNewline)
         session.run();
     }
     {
-        InteractiveSession session(gmx::test::erefdataCompare);
+        InteractiveSession session(ReferenceDataMode::Compare);
         session.addOutput("First line\n");
         session.addOutput("> ");
         session.addInput("input");
@@ -189,7 +193,7 @@ TEST(InteractiveTestHelperTest, ChecksSessionWithoutLastNewline)
 TEST(InteractiveTestHelperTest, ChecksSessionWithMissingOutput)
 {
     {
-        InteractiveSession session(gmx::test::erefdataUpdateAll);
+        InteractiveSession session(ReferenceDataMode::UpdateAll);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addInput("input2");
@@ -199,7 +203,7 @@ TEST(InteractiveTestHelperTest, ChecksSessionWithMissingOutput)
         session.run();
     }
     {
-        InteractiveSession session(gmx::test::erefdataCompare);
+        InteractiveSession session(ReferenceDataMode::Compare);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addInput("input2");
@@ -213,7 +217,7 @@ TEST(InteractiveTestHelperTest, ChecksSessionWithMissingOutput)
 TEST(InteractiveTestHelperTest, ChecksSessionWithEquivalentOutput)
 {
     {
-        InteractiveSession session(gmx::test::erefdataUpdateAll);
+        InteractiveSession session(ReferenceDataMode::UpdateAll);
         session.addOutput("First line\n");
         session.addOutput("> ");
         session.addInput("input");
@@ -224,7 +228,7 @@ TEST(InteractiveTestHelperTest, ChecksSessionWithEquivalentOutput)
         session.run();
     }
     {
-        InteractiveSession session(gmx::test::erefdataCompare);
+        InteractiveSession session(ReferenceDataMode::Compare);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addOutput("Second line\n");
@@ -238,7 +242,7 @@ TEST(InteractiveTestHelperTest, ChecksSessionWithEquivalentOutput)
 TEST(InteractiveTestHelperTest, DetectsIncorrectOutput)
 {
     {
-        InteractiveSession session(gmx::test::erefdataUpdateAll);
+        InteractiveSession session(ReferenceDataMode::UpdateAll);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addOutput("Second line\n> ");
@@ -247,7 +251,7 @@ TEST(InteractiveTestHelperTest, DetectsIncorrectOutput)
         session.run();
     }
     {
-        InteractiveSession session(gmx::test::erefdataCompare);
+        InteractiveSession session(ReferenceDataMode::Compare);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addOutput("Incorrect line\n> ");
@@ -260,7 +264,7 @@ TEST(InteractiveTestHelperTest, DetectsIncorrectOutput)
 TEST(InteractiveTestHelperTest, DetectsMissingOutput)
 {
     {
-        InteractiveSession session(gmx::test::erefdataUpdateAll);
+        InteractiveSession session(ReferenceDataMode::UpdateAll);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addOutput("Second line\n> ");
@@ -271,7 +275,7 @@ TEST(InteractiveTestHelperTest, DetectsMissingOutput)
         session.run();
     }
     {
-        InteractiveSession session(gmx::test::erefdataCompare);
+        InteractiveSession session(ReferenceDataMode::Compare);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addInput("input2");
@@ -285,7 +289,7 @@ TEST(InteractiveTestHelperTest, DetectsMissingOutput)
 TEST(InteractiveTestHelperTest, DetectsMissingFinalOutput)
 {
     {
-        InteractiveSession session(gmx::test::erefdataUpdateAll);
+        InteractiveSession session(ReferenceDataMode::UpdateAll);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addOutput("Second line\n> ");
@@ -294,7 +298,7 @@ TEST(InteractiveTestHelperTest, DetectsMissingFinalOutput)
         session.run();
     }
     {
-        InteractiveSession session(gmx::test::erefdataCompare);
+        InteractiveSession session(ReferenceDataMode::Compare);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addOutput("Second line\n> ");
@@ -306,7 +310,7 @@ TEST(InteractiveTestHelperTest, DetectsMissingFinalOutput)
 TEST(InteractiveTestHelperTest, DetectsExtraOutput)
 {
     {
-        InteractiveSession session(gmx::test::erefdataUpdateAll);
+        InteractiveSession session(ReferenceDataMode::UpdateAll);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addInput("input2");
@@ -316,7 +320,7 @@ TEST(InteractiveTestHelperTest, DetectsExtraOutput)
         session.run();
     }
     {
-        InteractiveSession session(gmx::test::erefdataCompare);
+        InteractiveSession session(ReferenceDataMode::Compare);
         session.addOutput("First line\n> ");
         session.addInput("input");
         session.addOutput("Extra output\n> ");
@@ -331,14 +335,14 @@ TEST(InteractiveTestHelperTest, DetectsExtraOutput)
 TEST(InteractiveTestHelperTest, DetectsMissingInput)
 {
     {
-        InteractiveSession session(gmx::test::erefdataUpdateAll);
+        InteractiveSession session(ReferenceDataMode::UpdateAll);
         session.addInput("input");
         session.addInput("input2");
         session.addReadInput();
         session.run();
     }
     {
-        InteractiveSession session(gmx::test::erefdataCompare);
+        InteractiveSession session(ReferenceDataMode::Compare);
         session.addInputLine("input");
         session.addInputLine("input2");
         session.addReadInput();
@@ -350,14 +354,14 @@ TEST(InteractiveTestHelperTest, DetectsMissingInput)
 TEST(InteractiveTestHelperTest, DetectsExtraInput)
 {
     {
-        InteractiveSession session(gmx::test::erefdataUpdateAll);
+        InteractiveSession session(ReferenceDataMode::UpdateAll);
         session.addInput("input");
         session.addInput("input2");
         session.addReadInput();
         session.run();
     }
     {
-        InteractiveSession session(gmx::test::erefdataCompare);
+        InteractiveSession session(ReferenceDataMode::Compare);
         session.addInputLine("input");
         session.addInputLine("input2");
         session.addReadInput();
@@ -369,3 +373,5 @@ TEST(InteractiveTestHelperTest, DetectsExtraInput)
 }
 
 } // namespace
+} // namespace test
+} // namespace gmx
index 04a9848c7b1cba8ace300eea57acde7f7bfd2e69..7cac1ea95ecefe30660f07ddf2574e6909fdb8b9 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013,2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2017,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "testutils/testasserts.h"
 #include "testutils/testexceptions.h"
 
+
+namespace gmx
+{
+namespace test
+{
 namespace
 {
 
-using gmx::test::TestReferenceChecker;
-using gmx::test::TestReferenceData;
-
 TEST(ReferenceDataTest, HandlesSimpleData)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkBoolean(true, "bool");
         checker.checkInteger(1, "int");
@@ -77,7 +80,7 @@ TEST(ReferenceDataTest, HandlesSimpleData)
         checker.checkString("Test", "string");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkBoolean(true, "bool");
         checker.checkInteger(1, "int");
@@ -96,14 +99,14 @@ TEST(ReferenceDataTest, HandlesFloatingPointData)
     const double doubleValue = 4.0 / 3.0;
 
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkDouble(doubleValue, "double");
         checker.checkReal(doubleValue, "real");
         checker.checkFloat(floatValue, "float");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkDouble(doubleValue, "double");
         checker.checkReal(floatValue, "real");
@@ -115,14 +118,14 @@ TEST(ReferenceDataTest, HandlesFloatingPointData)
 TEST(ReferenceDataTest, HandlesPresenceChecks)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         EXPECT_TRUE(checker.checkPresent(true, "present"));
         checker.checkInteger(1, "present");
         EXPECT_FALSE(checker.checkPresent(false, "absent"));
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         // Assigned to avoid warnings about potentially uninitialized value.
         bool bRet = true;
@@ -140,13 +143,13 @@ TEST(ReferenceDataTest, HandlesPresenceChecks)
 TEST(ReferenceDataTest, HandlesStringBlockData)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkTextBlock("Line1\nLine2\n", "block");
         checker.checkString("Test", "string");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkTextBlock("Line1\nLine2\n", "block");
         checker.checkString("Line1\nLine2\n", "block");
@@ -162,14 +165,14 @@ TEST(ReferenceDataTest, HandlesVectorData)
     double vecd[3] = { -2.3, 1.43, 2.5 };
 
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkVector(veci, "ivec");
         checker.checkVector(vecf, "fvec");
         checker.checkVector(vecd, "dvec");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkVector(veci, "ivec");
         checker.checkVector(vecf, "fvec");
@@ -183,12 +186,12 @@ TEST(ReferenceDataTest, HandlesSequenceData)
     const int seq[5] = { -1, 3, 5, 2, 4 };
 
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkSequenceArray(5, seq, "seq");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkSequenceArray(5, seq, "seq");
     }
@@ -207,12 +210,12 @@ TEST(ReferenceDataTest, HandlesSequenceOfCustomData)
     const dvec seq[] = { { -3, 4, 5 }, { -2.3, 5, 0 } };
 
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkSequence(std::begin(seq), std::end(seq), "seq", checkCustomVector);
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkSequence(std::begin(seq), std::end(seq), "seq", checkCustomVector);
     }
@@ -224,7 +227,7 @@ TEST(ReferenceDataTest, HandlesIncorrectData)
     int seq[5] = { -1, 3, 5, 2, 4 };
 
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         checker.checkDouble(0.5, "real");
@@ -232,7 +235,7 @@ TEST(ReferenceDataTest, HandlesIncorrectData)
         checker.checkSequenceArray(5, seq, "seq");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         EXPECT_NONFATAL_FAILURE(checker.checkInteger(2, "int"), "");
         EXPECT_NONFATAL_FAILURE(checker.checkDouble(0.3, "real"), "");
@@ -246,13 +249,13 @@ TEST(ReferenceDataTest, HandlesIncorrectData)
 TEST(ReferenceDataTest, HandlesIncorrectDataType)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         checker.checkCompound("Compound", "compound");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         EXPECT_NONFATAL_FAILURE(checker.checkString("1", "int"), "");
         EXPECT_NONFATAL_FAILURE(checker.checkCompound("OtherCompound", "compound"), "");
@@ -265,13 +268,13 @@ TEST(ReferenceDataTest, HandlesMissingData)
     const int seq[5] = { -1, 3, 5, 2, 4 };
 
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         checker.checkSequenceArray(5, seq, "seq");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         EXPECT_NONFATAL_FAILURE(checker.checkInteger(1, "missing"), "");
         EXPECT_NONFATAL_FAILURE(checker.checkSequenceArray(5, seq, "missing"), "");
@@ -286,14 +289,14 @@ TEST(ReferenceDataTest, HandlesUncheckedData)
     const int seq[5] = { -1, 3, 5, 2, 4 };
 
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         checker.checkSequenceArray(5, seq, "seq");
         checker.checkUnusedEntries();
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         EXPECT_NONFATAL_FAILURE(checker.checkUnusedEntries(), "");
@@ -306,14 +309,14 @@ TEST(ReferenceDataTest, HandlesUncheckedDataInSequence)
     const int seq[5] = { -1, 3, 5, 2, 4 };
 
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         checker.checkSequenceArray(5, seq, "seq");
         checker.checkUnusedEntries();
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         EXPECT_NONFATAL_FAILURE(checker.checkSequenceArray(3, seq, "seq"), "");
@@ -327,7 +330,7 @@ TEST(ReferenceDataTest, HandlesUncheckedDataInSequence)
 TEST(ReferenceDataTest, HandlesUncheckedDataInCompound)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         TestReferenceChecker compound(checker.checkCompound("Compound", "Compound"));
         compound.checkInteger(1, "int1");
@@ -337,7 +340,7 @@ TEST(ReferenceDataTest, HandlesUncheckedDataInCompound)
         checker.checkUnusedEntries();
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         TestReferenceChecker compound(checker.checkCompound("Compound", "Compound"));
         compound.checkInteger(1, "int1");
@@ -352,7 +355,7 @@ TEST(ReferenceDataTest, HandlesAnys)
 {
     using gmx::Any;
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkAny(Any::create<bool>(true), "bool");
         checker.checkAny(Any::create<int>(1), "int");
@@ -360,7 +363,7 @@ TEST(ReferenceDataTest, HandlesAnys)
         checker.checkAny(Any::create<std::string>("foo"), "str");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkAny(Any::create<bool>(true), "bool");
         checker.checkAny(Any::create<int>(1), "int");
@@ -392,12 +395,12 @@ TEST(ReferenceDataTest, HandlesKeyValueTree)
 {
     gmx::KeyValueTreeObject tree = buildKeyValueTree(true);
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkKeyValueTreeObject(tree, "tree");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkKeyValueTreeObject(tree, "tree");
     }
@@ -407,12 +410,12 @@ TEST(ReferenceDataTest, HandlesKeyValueTree)
 TEST(ReferenceDataTest, HandlesKeyValueTreeExtraKey)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkKeyValueTreeObject(buildKeyValueTree(false), "tree");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         EXPECT_NONFATAL_FAILURE(checker.checkKeyValueTreeObject(buildKeyValueTree(true), "tree"),
                                 "");
@@ -423,12 +426,12 @@ TEST(ReferenceDataTest, HandlesKeyValueTreeExtraKey)
 TEST(ReferenceDataTest, HandlesKeyValueTreeMissingKey)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkKeyValueTreeObject(buildKeyValueTree(true), "tree");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         EXPECT_NONFATAL_FAILURE(checker.checkKeyValueTreeObject(buildKeyValueTree(false), "tree"),
                                 "");
@@ -440,7 +443,7 @@ TEST(ReferenceDataTest, HandlesAnysWithIncorrectValue)
 {
     using gmx::Any;
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkAny(Any::create<bool>(true), "bool");
         checker.checkAny(Any::create<int>(1), "int");
@@ -448,7 +451,7 @@ TEST(ReferenceDataTest, HandlesAnysWithIncorrectValue)
         checker.checkAny(Any::create<std::string>("foo"), "str");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         EXPECT_NONFATAL_FAILURE(checker.checkAny(Any::create<bool>(false), "bool"), "");
         EXPECT_NONFATAL_FAILURE(checker.checkAny(Any::create<int>(2), "int"), "");
@@ -462,14 +465,14 @@ TEST(ReferenceDataTest, HandlesAnysWithIncorrectType)
 {
     using gmx::Any;
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkAny(Any::create<bool>(true), "bool");
         checker.checkAny(Any::create<int>(1), "int");
         checker.checkAny(Any::create<double>(3.5), "real");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         EXPECT_NONFATAL_FAILURE(checker.checkAny(Any::create<int>(1), "bool"), "");
         EXPECT_NONFATAL_FAILURE(checker.checkAny(Any::create<bool>(true), "int"), "");
@@ -484,7 +487,7 @@ TEST(ReferenceDataTest, HandlesMissingReferenceDataFile)
 
     EXPECT_NONFATAL_FAILURE(
             {
-                TestReferenceData    data(gmx::test::erefdataCompare);
+                TestReferenceData    data(ReferenceDataMode::Compare);
                 TestReferenceChecker checker(data.rootChecker());
                 checker.checkInteger(1, "int");
                 checker.checkDouble(0.5, "real");
@@ -498,7 +501,7 @@ TEST(ReferenceDataTest, HandlesMissingReferenceDataFile)
 TEST(ReferenceDataTest, HandlesSpecialCharactersInStrings)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         // Note that '\r' is not handled correctly in string or
         // stringblock (see the TODO in createElementContents), so
@@ -507,7 +510,7 @@ TEST(ReferenceDataTest, HandlesSpecialCharactersInStrings)
         checker.checkTextBlock("\"<'>\n&\\/;", "stringblock");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkString("\"<'>\n&\\/;", "string");
         checker.checkTextBlock("\"<'>\n&\\/;", "stringblock");
@@ -519,7 +522,7 @@ TEST(ReferenceDataTest, HandlesStringsWithTextAndWhitespace)
     const char* strings[] = { "  test", "test  ",   "  test  ", "the test",
                               "\ntest", "\n\ntest", "test\n",   "test\n\n" };
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         for (const auto& s : strings)
         {
@@ -528,7 +531,7 @@ TEST(ReferenceDataTest, HandlesStringsWithTextAndWhitespace)
         }
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         for (const auto& s : strings)
         {
@@ -541,21 +544,21 @@ TEST(ReferenceDataTest, HandlesStringsWithTextAndWhitespace)
 TEST(ReferenceDataTest, HandlesEmptyStrings)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkString("", "Empty");
         // GROMACS cannot use an empty line in a reference data String
         // until https://github.com/leethomason/tinyxml2/issues/432 is
         // resolved.
-        EXPECT_THROW_GMX(checker.checkString("\n", "EmptyLine"), gmx::test::TestException);
+        EXPECT_THROW_GMX(checker.checkString("\n", "EmptyLine"), TestException);
         checker.checkTextBlock("", "EmptyBlock");
         checker.checkTextBlock("\n", "EmptyLineBlock");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkString("", "Empty");
-        EXPECT_THROW_GMX(checker.checkString("\n", "EmptyLine"), gmx::test::TestException);
+        EXPECT_THROW_GMX(checker.checkString("\n", "EmptyLine"), TestException);
         checker.checkTextBlock("", "EmptyBlock");
         checker.checkTextBlock("\n", "EmptyLineBlock");
     }
@@ -573,12 +576,12 @@ TEST(ReferenceDataTest, HandlesEmbeddedCdataEndTagInTextBlock)
        don't actually depend on this behaviour, but it might be nice
        to have / know about.) */
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkTextBlock(" ]]> ", "stringblock");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkTextBlock(" ]]> ", "stringblock");
     }
@@ -590,13 +593,13 @@ TEST(ReferenceDataTest, HandlesSequenceItemIndices)
     int seq[5] = { -1, 3, 5, 2, 4 };
 
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkSequenceArray(5, seq, "seq");
         checker.checkSequenceArray(5, seq, "seq2");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         seq[0] = 2;
         EXPECT_NONFATAL_FAILURE(checker.checkSequenceArray(5, seq, "seq"), "seq/[0]");
@@ -611,7 +614,7 @@ TEST(ReferenceDataTest, HandlesSequenceItemIndices)
 TEST(ReferenceDataTest, HandlesMultipleChecksAgainstSameData)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkString("Test", "string");
         EXPECT_NONFATAL_FAILURE(checker.checkString("Test2", "string"), "");
@@ -619,7 +622,7 @@ TEST(ReferenceDataTest, HandlesMultipleChecksAgainstSameData)
         EXPECT_NONFATAL_FAILURE(checker.checkTextBlock("TestString2", "stringblock"), "");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkString("Test", "string");
         EXPECT_NONFATAL_FAILURE(checker.checkString("Test2", "string"), "");
@@ -632,13 +635,13 @@ TEST(ReferenceDataTest, HandlesMultipleChecksAgainstSameData)
 TEST(ReferenceDataTest, HandlesMultipleNullIds)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkString("Test", nullptr);
         checker.checkString("Test2", nullptr);
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkString("Test", nullptr);
         checker.checkString("Test2", nullptr);
@@ -650,7 +653,7 @@ TEST(ReferenceDataTest, HandlesMultipleNullIds)
 TEST(ReferenceDataTest, HandlesMultipleComparisonsAgainstNullIds)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int1");
         checker.checkString("Test", nullptr);
@@ -660,7 +663,7 @@ TEST(ReferenceDataTest, HandlesMultipleComparisonsAgainstNullIds)
         checker.checkString("Test2", nullptr);
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int1");
         checker.checkString("Test", nullptr);
@@ -680,14 +683,14 @@ TEST(ReferenceDataTest, HandlesMultipleComparisonsAgainstNullIds)
 TEST(ReferenceDataTest, HandlesReadingValues)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkUChar('A', "char");
         checker.checkInteger(1, "int");
         checker.checkString("Test", "string");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         EXPECT_EQ('A', checker.readUChar("char"));
         EXPECT_EQ(1, checker.readInteger("int"));
@@ -699,7 +702,7 @@ TEST(ReferenceDataTest, HandlesReadingValues)
 TEST(ReferenceDataTest, HandlesUpdateChangedWithoutChanges)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         checker.checkString("Test", "string");
@@ -707,7 +710,7 @@ TEST(ReferenceDataTest, HandlesUpdateChangedWithoutChanges)
         compound.checkInteger(2, "int");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateChanged);
+        TestReferenceData    data(ReferenceDataMode::UpdateChanged);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         checker.checkString("Test", "string");
@@ -715,7 +718,7 @@ TEST(ReferenceDataTest, HandlesUpdateChangedWithoutChanges)
         compound.checkInteger(2, "int");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         checker.checkString("Test", "string");
@@ -727,19 +730,19 @@ TEST(ReferenceDataTest, HandlesUpdateChangedWithoutChanges)
 TEST(ReferenceDataTest, HandlesUpdateChangedWithValueChanges)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         checker.checkString("Test", "string");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateChanged);
+        TestReferenceData    data(ReferenceDataMode::UpdateChanged);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(2, "int");
         checker.checkString("Test", "string");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(2, "int");
         checker.checkString("Test", "string");
@@ -749,19 +752,19 @@ TEST(ReferenceDataTest, HandlesUpdateChangedWithValueChanges)
 TEST(ReferenceDataTest, HandlesUpdateChangedWithTypeChanges)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "foo");
         checker.checkString("Test", "string");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateChanged);
+        TestReferenceData    data(ReferenceDataMode::UpdateChanged);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkString("foo", "foo");
         checker.checkString("Test", "string");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkString("foo", "foo");
         checker.checkString("Test", "string");
@@ -771,21 +774,21 @@ TEST(ReferenceDataTest, HandlesUpdateChangedWithTypeChanges)
 TEST(ReferenceDataTest, HandlesUpdateChangedWithCompoundChanges)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "1");
         TestReferenceChecker compound(checker.checkCompound("Compound", "2"));
         compound.checkInteger(2, "int");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateChanged);
+        TestReferenceData    data(ReferenceDataMode::UpdateChanged);
         TestReferenceChecker checker(data.rootChecker());
         TestReferenceChecker compound(checker.checkCompound("Compound", "1"));
         compound.checkInteger(2, "int");
         checker.checkString("Test", "2");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         TestReferenceChecker compound(checker.checkCompound("Compound", "1"));
         compound.checkInteger(2, "int");
@@ -796,21 +799,23 @@ TEST(ReferenceDataTest, HandlesUpdateChangedWithCompoundChanges)
 TEST(ReferenceDataTest, HandlesUpdateChangedWithRemovedEntries)
 {
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateAll);
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(1, "int");
         checker.checkString("Test", "string");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataUpdateChanged);
+        TestReferenceData    data(ReferenceDataMode::UpdateChanged);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(2, "int");
     }
     {
-        TestReferenceData    data(gmx::test::erefdataCompare);
+        TestReferenceData    data(ReferenceDataMode::Compare);
         TestReferenceChecker checker(data.rootChecker());
         checker.checkInteger(2, "int");
     }
 }
 
 } // namespace
+} // namespace test
+} // namespace gmx
index f8381a8ca681dae0c6d63544d3584030664fa1ec..c8dc14164f13d5c16c3d8776db1c19a78db26ee6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2015,2016,2019, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include "testutils/refdata.h"
 #include "testutils/testasserts.h"
 
+namespace gmx
+{
+namespace test
+{
 namespace
 {
 
-using gmx::test::checkXvgFile;
-using gmx::test::XvgMatchSettings;
-
 //! Input testing data - an inline xvg file.
 const char* const input[] = { "0     2905.86    -410.199",   "0.2     6656.67    -430.437",
                               "0.4     5262.44    -409.399", "0.6     5994.69    -405.763",
@@ -69,16 +70,16 @@ TEST(XvgTests, CreateFile)
 {
     {
         // Create new data
-        gmx::test::TestReferenceData    data(gmx::test::erefdataUpdateAll);
-        gmx::test::TestReferenceChecker checker(data.rootChecker());
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
+        TestReferenceChecker checker(data.rootChecker());
         // Convert char array to a stream and add it to the checker
         gmx::StringInputStream sis(input);
         checkXvgFile(&sis, &checker, XvgMatchSettings());
     }
     {
         // Now read it back
-        gmx::test::TestReferenceData    data(gmx::test::erefdataCompare);
-        gmx::test::TestReferenceChecker checker(data.rootChecker());
+        TestReferenceData    data(ReferenceDataMode::Compare);
+        TestReferenceChecker checker(data.rootChecker());
         // Convert char array to a stream and add it to the checker
         gmx::StringInputStream sis(input);
         checkXvgFile(&sis, &checker, XvgMatchSettings());
@@ -89,8 +90,8 @@ TEST(XvgTests, CheckMissing)
 {
     {
         // Create new data
-        gmx::test::TestReferenceData    data(gmx::test::erefdataUpdateAll);
-        gmx::test::TestReferenceChecker checker(data.rootChecker());
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
+        TestReferenceChecker checker(data.rootChecker());
         // Convert char array to a stream and add it to the checker
         gmx::StringInputStream sis(input);
         checkXvgFile(&sis, &checker, XvgMatchSettings());
@@ -99,9 +100,9 @@ TEST(XvgTests, CheckMissing)
         const char* const input[] = { "0     2905.86    -410.199", "0.2     6656.67    -430.437",
                                       "0.4     5262.44    -409.399" };
         // Now check with missing data
-        gmx::test::TestReferenceData    data(gmx::test::erefdataCompare);
-        gmx::test::TestReferenceChecker checker(data.rootChecker());
-        gmx::StringInputStream          sis(input);
+        TestReferenceData      data(ReferenceDataMode::Compare);
+        TestReferenceChecker   checker(data.rootChecker());
+        gmx::StringInputStream sis(input);
         EXPECT_NONFATAL_FAILURE(checkXvgFile(&sis, &checker, XvgMatchSettings()),
                                 "not used in test");
     }
@@ -111,8 +112,8 @@ TEST(XvgTests, CheckExtra)
 {
     {
         // Create new data
-        gmx::test::TestReferenceData    data(gmx::test::erefdataUpdateAll);
-        gmx::test::TestReferenceChecker checker(data.rootChecker());
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
+        TestReferenceChecker checker(data.rootChecker());
         // Convert char array to a stream and add it to the checker
         gmx::StringInputStream sis(input);
         checkXvgFile(&sis, &checker, XvgMatchSettings());
@@ -123,9 +124,9 @@ TEST(XvgTests, CheckExtra)
                                       "0.8     5941.37    -408.337", "1     5869.87    -411.124",
                                       "1.2     5889.87    -413.124" };
         // Now check with missing data
-        gmx::test::TestReferenceData    data(gmx::test::erefdataCompare);
-        gmx::test::TestReferenceChecker checker(data.rootChecker());
-        gmx::StringInputStream          sis(input);
+        TestReferenceData      data(ReferenceDataMode::Compare);
+        TestReferenceChecker   checker(data.rootChecker());
+        gmx::StringInputStream sis(input);
         EXPECT_NONFATAL_FAILURE(checkXvgFile(&sis, &checker, XvgMatchSettings()), "Row6");
     }
 }
@@ -134,8 +135,8 @@ TEST(XvgTests, ReadIncorrect)
 {
     {
         // Create new data
-        gmx::test::TestReferenceData    data(gmx::test::erefdataUpdateAll);
-        gmx::test::TestReferenceChecker checker(data.rootChecker());
+        TestReferenceData    data(ReferenceDataMode::UpdateAll);
+        TestReferenceChecker checker(data.rootChecker());
         // Convert char array to a stream and add it to the checker
         gmx::StringInputStream sis(input);
         checkXvgFile(&sis, &checker, XvgMatchSettings());
@@ -145,11 +146,13 @@ TEST(XvgTests, ReadIncorrect)
                                       "0.4     5262.44    -409.399", "0.6     5994.69    -405.763",
                                       "0.8     5941.37    -408.337", "1     5869.87    -421.124" };
         // Now check with incorrect data
-        gmx::test::TestReferenceData    data(gmx::test::erefdataCompare);
-        gmx::test::TestReferenceChecker checker(data.rootChecker());
-        gmx::StringInputStream          sis(input);
+        TestReferenceData      data(ReferenceDataMode::Compare);
+        TestReferenceChecker   checker(data.rootChecker());
+        gmx::StringInputStream sis(input);
         EXPECT_NONFATAL_FAILURE(checkXvgFile(&sis, &checker, XvgMatchSettings()), "-411");
     }
 }
 
 } // namespace
+} // namespace test
+} // namespace gmx
index 1faf7979dd20cb7e1de2a3e1de0f83cd14621db6..e5bb19f76a82d1412fe04f1e2acaec63c22d952b 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2017, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #ifndef TEST_USES_HARDWARE_DETECTION
 //! Whether the test expects/supports running with knowledge of the hardware.
 #    define TEST_USES_HARDWARE_DETECTION false
-namespace gmx
-{
-namespace test
-{
-//! Implement a stub definition for tests that don't ask for a real one.
-void callAddGlobalTestEnvironment(){};
-} // namespace test
-} // namespace gmx
 #endif
 
 /*! \brief
index 7b3efa2a6c34f7bf7c02f42641b634e613e21732..0ac5cb04dfd72a92f00f35b55e83f551f31f5939 100644 (file)
@@ -290,10 +290,10 @@ if(GMX_PHYSICAL_VALIDATION)
         # End copied from regression tests.
         #
 
-        if (NOT PYTHONINTERP_FOUND)
+        if (NOT Python3_Interpreter_FOUND)
             message(FATAL_ERROR
                     "Python not found. Physical validation requires python. \
-                     Install python, set PYTHON_EXECUTABLE to a valid python location, \
+                     Install python, set Python3_ROOT_DIR or PYTHON_EXECUTABLE to a valid location, \
                      or set GMX_PHYSICAL_VALIDATION=OFF to disable the physical validation tests.")
         endif()
         #
index 5b7037d553126348b972f2eea4c51b0065fa0993..c1f238f25e85bbf80c3ce973aa510ec80ee32ee7 100644 (file)
@@ -33,6 +33,7 @@ GROMACS python interface.
    probably neither especially elegant nor especially safe. Use of this
    module in any remotely critical application is strongly discouraged.
 """
+import errno
 import os
 import sys
 import subprocess
@@ -93,11 +94,7 @@ class GromacsInterface(object):
 
     @includepath.setter
     def includepath(self, path):
-        try:  # py2/3 compatibility
-            basestring
-        except NameError:
-            basestring = str
-        if isinstance(path, basestring):
+        if isinstance(path, str):
             path = [path]
         self._includepath = path
 
@@ -421,10 +418,11 @@ class GromacsInterface(object):
         if exe is None:
             exe = self._exe
         try:
-            devnull = open(os.devnull)
-            exe_out = subprocess.check_output([exe, '--version'], stderr=devnull)
+            exe_out = subprocess.run([exe, '--version'],
+                                     stdout=subprocess.PIPE,
+                                     universal_newlines=True).stdout
         except OSError as e:
-            if e.errno == os.errno.ENOENT:
+            if hasattr(errno, 'ENOENT') and e.errno == errno.ENOENT:
                 # file not found error.
                 if not quiet:
                     print('ERROR: gmx executable not found')
@@ -433,7 +431,7 @@ class GromacsInterface(object):
             else:
                 raise e
         # check that output is as expected
-        return re.search(br':-\) GROMACS - gmx.* \(-:', exe_out)
+        return re.search(r':-\) GROMACS - gmx.* \(-:', exe_out)
 
     def _run(self, cmd, args, cwd=None, stdin=None, stdout=None, stderr=None, mpicmd=None):
         if self.exe is None:
index eb3601c94192e8d81d884c265b6ae2f3ce36d167..c10e59c01936c7b6dfbd6b15c28c4470959f3974 100644 (file)
       {"test": "kin_mb", "args": "-t 0.01"}
     ]
   },
+  {
+    "name": "ens_argon_md_verlet_pme_vr_cr",
+    "dir": "ens_argon_md_verlet_pme_vr_cr",
+    "tests": [
+      {"test": "ensemble", "args": "-t 4 --dtemp 3 0 3 --dpress 0 75 75"},
+      {"test": "kin_mb", "args": "-t 0.01"}
+    ]
+  },
   {
     "name": "ens_argon_md-vv_verlet_pme_nh_mttk",
     "dir": "ens_argon_md-vv_verlet_pme_nh_mttk",
       {"test": "kin_mb", "args": "-t 0.01"}
     ]
   },
+  {
+    "name": "ens_water_md_verlet_settle_pme_vr_cr",
+    "dir": "ens_water_md_verlet_settle_pme_vr_cr",
+    "tests": [
+      {"test": "ensemble", "args": "-t 4 --dtemp 8 0 8 --dpress 0 300 300"},
+      {"test": "kin_mb", "args": "-t 0.01"}
+    ]
+  },
   {
     "name": "ens_water_md-vv_verlet_none_pme_nh_mttk",
     "dir": "ens_water_md-vv_verlet_none_pme_nh_mttk",
diff --git a/tests/physicalvalidation/systems/ens_argon_md_verlet_pme_vr_cr/input/system.gro b/tests/physicalvalidation/systems/ens_argon_md_verlet_pme_vr_cr/input/system.gro
new file mode 100644 (file)
index 0000000..de02ad5
--- /dev/null
@@ -0,0 +1,1003 @@
+Argon
+ 1000
+    1Ar      Ar    1   2.533   1.244   3.506 -0.0749  0.2125 -0.0713
+    2Ar      Ar    2   0.830   2.544   3.448  0.0461 -0.2387  0.1631
+    3Ar      Ar    3   1.091   0.110   3.129 -0.1081 -0.0036  0.2684
+    4Ar      Ar    4   2.455   0.005   3.012 -0.0567  0.1248  0.1786
+    5Ar      Ar    5   2.714   1.353   0.553 -0.2272 -0.0971  0.0084
+    6Ar      Ar    6   3.051   2.893   2.691  0.1208 -0.0197  0.0394
+    7Ar      Ar    7   1.422   2.770   0.146  0.2114 -0.1777  0.2451
+    8Ar      Ar    8   2.223   1.211   3.268  0.1112  0.0773 -0.0010
+    9Ar      Ar    9   2.811   2.789   2.385  0.0333  0.2149  0.0648
+   10Ar      Ar   10   0.487   1.159   1.171 -0.0877  0.4255 -0.1060
+   11Ar      Ar   11   1.948   3.596   1.113 -0.1852  0.2282  0.0956
+   12Ar      Ar   12   0.287   1.483   1.288 -0.0050  0.0967  0.0553
+   13Ar      Ar   13   3.473   1.423   3.491  0.3454  0.1668  0.3127
+   14Ar      Ar   14   1.867   1.667   3.566  0.2642 -0.0392  0.0957
+   15Ar      Ar   15   3.270   0.640   2.913 -0.0228  0.0373  0.0717
+   16Ar      Ar   16   0.166   3.035   3.180 -0.0995  0.2913  0.0265
+   17Ar      Ar   17   2.789   0.382   0.246  0.0989 -0.2625  0.3344
+   18Ar      Ar   18   2.775   1.928   1.420 -0.0916  0.0197  0.1115
+   19Ar      Ar   19   0.175   1.376   0.463 -0.0497 -0.0436  0.1417
+   20Ar      Ar   20   1.597   1.275   3.507 -0.1063 -0.2090  0.0628
+   21Ar      Ar   21   1.535   1.147   0.297  0.0330 -0.0986 -0.0752
+   22Ar      Ar   22   1.674   1.890   0.180  0.0392 -0.2388  0.1145
+   23Ar      Ar   23   1.437   1.660   0.424  0.0764 -0.1178 -0.3344
+   24Ar      Ar   24   2.998   0.566   0.463 -0.1022  0.1264  0.0310
+   25Ar      Ar   25   1.927   0.415   3.214  0.1665 -0.1610  0.0644
+   26Ar      Ar   26   0.860   1.729   2.474 -0.0491 -0.0600 -0.0552
+   27Ar      Ar   27   2.491   2.905   1.239  0.0138  0.0215 -0.0270
+   28Ar      Ar   28   2.717   0.733   0.262  0.1447 -0.0472  0.0678
+   29Ar      Ar   29   2.103   1.782   3.249 -0.1029 -0.2082 -0.1089
+   30Ar      Ar   30   2.393   0.189   1.594  0.0959 -0.1750 -0.1390
+   31Ar      Ar   31   2.105   2.797   1.067 -0.2263 -0.2551 -0.0428
+   32Ar      Ar   32   1.812   3.375   0.834 -0.1092  0.0714 -0.0168
+   33Ar      Ar   33   2.015   3.135   2.218  0.0421  0.0626  0.0457
+   34Ar      Ar   34   0.315   2.332   3.096 -0.1319 -0.0921 -0.2353
+   35Ar      Ar   35   2.484   2.504   1.962 -0.0198  0.0860  0.0396
+   36Ar      Ar   36   0.591   1.319   0.329 -0.1499  0.2838  0.0744
+   37Ar      Ar   37   1.042   0.394   2.284  0.0557 -0.2661  0.1453
+   38Ar      Ar   38   2.544   1.970   2.454 -0.0680  0.2097 -0.0050
+   39Ar      Ar   39   2.064   1.298   2.670  0.1595  0.0820 -0.2762
+   40Ar      Ar   40   1.993   3.249   1.113 -0.1005  0.1012  0.1843
+   41Ar      Ar   41   2.177   2.682   0.611 -0.1297 -0.1616 -0.0014
+   42Ar      Ar   42   1.844   1.288   0.167 -0.0554 -0.0176 -0.0601
+   43Ar      Ar   43   3.317   1.902   2.451  0.1042 -0.0009  0.1511
+   44Ar      Ar   44   3.312   1.042   1.326 -0.0010 -0.0410 -0.0760
+   45Ar      Ar   45   0.387   3.520   2.838  0.2160  0.1082  0.1900
+   46Ar      Ar   46   0.998   2.654   2.703 -0.0893 -0.1752  0.0437
+   47Ar      Ar   47   2.486   0.876   1.551  0.0045  0.1747 -0.1327
+   48Ar      Ar   48   3.316   1.456   1.975  0.1589 -0.0295 -0.1114
+   49Ar      Ar   49   0.838   3.053   2.882  0.0435 -0.0018  0.0789
+   50Ar      Ar   50   1.773   2.952   0.987 -0.0380 -0.0561  0.2600
+   51Ar      Ar   51   0.779   0.470   0.196 -0.0461 -0.1151 -0.0412
+   52Ar      Ar   52   1.449   1.330   0.627 -0.1764 -0.2771 -0.0624
+   53Ar      Ar   53   2.926   0.025   0.799 -0.0068 -0.0498  0.1138
+   54Ar      Ar   54   2.117   2.902   3.524  0.1091 -0.0457 -0.0181
+   55Ar      Ar   55   2.089   2.473   0.944  0.1079  0.1839 -0.0097
+   56Ar      Ar   56   2.259   0.606   1.373  0.1068 -0.2380  0.1343
+   57Ar      Ar   57   1.090   1.220   1.856 -0.2366  0.0192 -0.0343
+   58Ar      Ar   58   1.544   2.364   2.987 -0.1069  0.1920 -0.2520
+   59Ar      Ar   59   2.489   3.536   2.061  0.1609 -0.0792  0.1900
+   60Ar      Ar   60   2.837   0.810   2.711  0.0216 -0.0826 -0.0739
+   61Ar      Ar   61   2.906   1.363   1.941  0.1090 -0.0278  0.0470
+   62Ar      Ar   62   0.297   3.228   3.448  0.1368 -0.0617 -0.3988
+   63Ar      Ar   63   1.157   1.861   1.736 -0.0056 -0.0222  0.1853
+   64Ar      Ar   64   0.839   2.062   0.355 -0.0216 -0.0037  0.1995
+   65Ar      Ar   65   0.582   3.588   0.624 -0.0985 -0.1411 -0.0879
+   66Ar      Ar   66   0.689   1.503   1.180 -0.0068  0.1235 -0.0196
+   67Ar      Ar   67   1.851   1.600   0.335 -0.1688  0.3419 -0.0698
+   68Ar      Ar   68   3.335   2.586   0.788 -0.1364  0.3006  0.1641
+   69Ar      Ar   69   1.587   0.564   1.762  0.0444 -0.0793  0.1548
+   70Ar      Ar   70   3.267   0.028   2.631  0.0118 -0.1130  0.0186
+   71Ar      Ar   71   2.165   0.325   3.555  0.0099 -0.2618  0.2124
+   72Ar      Ar   72   2.367   2.008   0.309  0.0450 -0.0875 -0.0133
+   73Ar      Ar   73   2.101   0.908   0.748  0.0888 -0.2402 -0.1440
+   74Ar      Ar   74   0.085   0.654   2.376 -0.0812  0.0426 -0.0091
+   75Ar      Ar   75   2.065   3.168   1.867  0.1022 -0.1702  0.0031
+   76Ar      Ar   76   3.271   0.907   1.679 -0.2444  0.2808  0.2807
+   77Ar      Ar   77   2.667   0.714   1.093 -0.1353  0.0163 -0.2375
+   78Ar      Ar   78   0.002   3.262   0.164 -0.0167  0.1076 -0.0954
+   79Ar      Ar   79   1.809   0.712   0.475 -0.1923 -0.0626 -0.0481
+   80Ar      Ar   80   3.156   2.483   2.502  0.0639 -0.0438 -0.0605
+   81Ar      Ar   81   3.139   1.549   1.228  0.1348  0.0872 -0.0531
+   82Ar      Ar   82   2.600   1.399   1.776 -0.0614  0.1016 -0.0274
+   83Ar      Ar   83   3.412   0.889   2.320  0.0382  0.0444 -0.0585
+   84Ar      Ar   84   0.984   0.171   2.008 -0.1707  0.1991  0.0613
+   85Ar      Ar   85   0.569   1.202   2.836  0.1104  0.1151  0.2225
+   86Ar      Ar   86   2.233   3.412   3.370  0.0190  0.0309  0.1996
+   87Ar      Ar   87   2.631   2.595   0.591  0.0441 -0.3122 -0.0233
+   88Ar      Ar   88   3.453   3.562   1.737  0.1032 -0.1397  0.1212
+   89Ar      Ar   89   2.194   1.210   0.424 -0.0223  0.1718 -0.0009
+   90Ar      Ar   90   1.530   2.681   0.836 -0.2199 -0.0124 -0.2109
+   91Ar      Ar   91   2.040   1.327   2.273  0.1754  0.1196  0.1501
+   92Ar      Ar   92   1.414   0.851   0.129 -0.1633  0.0950 -0.0447
+   93Ar      Ar   93   1.481   0.978   3.323 -0.1616 -0.2807  0.0552
+   94Ar      Ar   94   0.489   0.401   1.075  0.2383  0.1319  0.0540
+   95Ar      Ar   95   1.284   3.061   0.363  0.1622  0.0141 -0.1408
+   96Ar      Ar   96   1.695   3.539   2.321 -0.0538  0.0879 -0.1125
+   97Ar      Ar   97   0.824   1.447   2.768  0.0184 -0.2029 -0.0700
+   98Ar      Ar   98   0.982   2.280   3.150  0.0398  0.0700 -0.0267
+   99Ar      Ar   99   1.123   0.632   0.586 -0.0326  0.0746 -0.1378
+  100Ar      Ar  100   3.172   2.797   1.024 -0.0382 -0.1180 -0.3491
+  101Ar      Ar  101   0.667   0.338   0.688  0.0638 -0.1407  0.0922
+  102Ar      Ar  102   1.896   2.006   1.517  0.0599 -0.1564 -0.1114
+  103Ar      Ar  103   2.810   1.213   0.163 -0.1049  0.0989  0.0548
+  104Ar      Ar  104   2.244   2.694   0.185  0.2545  0.0700 -0.0211
+  105Ar      Ar  105   2.266   0.632   0.918 -0.2106  0.0821  0.1393
+  106Ar      Ar  106   0.133   2.603   2.783 -0.1697  0.0473  0.0031
+  107Ar      Ar  107   1.655   3.486   3.575 -0.1007 -0.0846 -0.0130
+  108Ar      Ar  108   2.105   3.112   3.225  0.1863 -0.2542 -0.0018
+  109Ar      Ar  109   1.034   3.206   2.140  0.0791 -0.0916  0.0480
+  110Ar      Ar  110   2.654   1.720   0.312 -0.0857  0.0024  0.0589
+  111Ar      Ar  111   1.451   2.015   0.427  0.1905  0.1229  0.2063
+  112Ar      Ar  112   0.383   2.109   3.452  0.2621  0.2684  0.1333
+  113Ar      Ar  113   1.848   1.991   3.441  0.0497  0.2942 -0.0935
+  114Ar      Ar  114   2.186   0.654   2.951  0.0180  0.1083 -0.0885
+  115Ar      Ar  115   3.127   0.029   0.083  0.0259 -0.0796  0.2566
+  116Ar      Ar  116   0.338   2.405   1.429 -0.0043 -0.0341 -0.0915
+  117Ar      Ar  117   0.492   2.902   1.755 -0.0616 -0.1002 -0.2206
+  118Ar      Ar  118   1.170   1.461   1.578 -0.0822  0.0728 -0.2015
+  119Ar      Ar  119   2.583   1.608   0.819  0.2193  0.1123 -0.2470
+  120Ar      Ar  120   0.699   2.079   2.550  0.1604 -0.2148 -0.0509
+  121Ar      Ar  121   1.076   2.057   2.256 -0.1748 -0.1709 -0.0985
+  122Ar      Ar  122   1.880   0.291   1.723  0.1891  0.1677 -0.0132
+  123Ar      Ar  123   3.106   1.751   1.543 -0.0233  0.1987  0.0587
+  124Ar      Ar  124   1.439   2.733   3.112  0.1172 -0.1423 -0.1773
+  125Ar      Ar  125   1.941   3.235   1.553 -0.1575 -0.1107 -0.3033
+  126Ar      Ar  126   1.041   2.959   0.947 -0.0616  0.0513 -0.1979
+  127Ar      Ar  127   3.279   0.901   0.452  0.1134  0.2595  0.2174
+  128Ar      Ar  128   1.466   1.414   3.108  0.1603  0.0571 -0.0031
+  129Ar      Ar  129   0.790   2.994   1.221  0.1406  0.1982 -0.0776
+  130Ar      Ar  130   0.096   2.599   0.807  0.0928 -0.1359  0.1040
+  131Ar      Ar  131   0.246   3.168   1.036  0.1414  0.1042  0.0422
+  132Ar      Ar  132   0.646   2.690   0.136 -0.0876 -0.2203 -0.0420
+  133Ar      Ar  133   1.805   1.050   1.452  0.0299 -0.0800  0.0791
+  134Ar      Ar  134   2.680   1.565   3.550 -0.1378  0.1958  0.1142
+  135Ar      Ar  135   1.078   1.558   3.123 -0.0364  0.0951  0.1912
+  136Ar      Ar  136   1.318   0.311   1.902  0.0563  0.1075 -0.1902
+  137Ar      Ar  137   0.760   2.090   2.137  0.1943  0.1011  0.2585
+  138Ar      Ar  138   2.148   3.500   0.450  0.0396  0.0292 -0.0063
+  139Ar      Ar  139   0.190   1.291   0.077  0.0612  0.1482  0.0060
+  140Ar      Ar  140   0.973   0.339   0.452  0.0580  0.0463  0.0477
+  141Ar      Ar  141   2.737   1.033   0.890  0.1256 -0.1668 -0.1147
+  142Ar      Ar  142   2.167   0.933   2.491  0.0875 -0.1183 -0.0030
+  143Ar      Ar  143   2.047   1.787   2.919  0.2607 -0.0939  0.0024
+  144Ar      Ar  144   3.274   0.920   0.839 -0.1171 -0.0446 -0.2806
+  145Ar      Ar  145   1.364   3.540   0.340 -0.0376  0.2738  0.0726
+  146Ar      Ar  146   0.531   0.648   1.823 -0.0625 -0.1097  0.1644
+  147Ar      Ar  147   3.393   0.649   0.077 -0.1207 -0.1858  0.0403
+  148Ar      Ar  148   0.769   3.466   2.947 -0.0106 -0.0452 -0.0248
+  149Ar      Ar  149   2.231   0.815   3.271 -0.1187  0.0810 -0.0382
+  150Ar      Ar  150   0.104   1.416   1.642 -0.0795 -0.0423  0.1962
+  151Ar      Ar  151   2.027   0.604   1.724  0.0368  0.2095  0.0752
+  152Ar      Ar  152   0.897   1.876   2.871 -0.0832  0.0202 -0.2575
+  153Ar      Ar  153   2.349   1.841   0.630 -0.1870  0.1069 -0.0616
+  154Ar      Ar  154   0.498   1.849   1.968  0.1339 -0.1877  0.1754
+  155Ar      Ar  155   1.806   0.533   1.460  0.0725 -0.1092  0.0020
+  156Ar      Ar  156   0.071   0.122   2.840 -0.0596  0.1463  0.2930
+  157Ar      Ar  157   2.952   2.961   1.787 -0.0827 -0.1748  0.1151
+  158Ar      Ar  158   2.203   0.740   2.009  0.0061 -0.3358 -0.3471
+  159Ar      Ar  159   1.773   1.122   2.433  0.3326 -0.0818 -0.2048
+  160Ar      Ar  160   0.280   1.198   0.756  0.0126  0.1049 -0.0584
+  161Ar      Ar  161   1.590   0.731   1.233 -0.0353  0.0494 -0.2069
+  162Ar      Ar  162   2.986   1.086   3.471 -0.1861 -0.0613 -0.2908
+  163Ar      Ar  163   1.106   1.082   1.481  0.0442  0.1456  0.1556
+  164Ar      Ar  164   0.788   0.629   2.148  0.0836  0.1618  0.0344
+  165Ar      Ar  165   1.801   2.139   2.460  0.0670  0.0495 -0.0848
+  166Ar      Ar  166   1.057   1.784   1.411 -0.1544 -0.2280  0.2349
+  167Ar      Ar  167   3.599   2.550   1.393 -0.0828 -0.0895 -0.0718
+  168Ar      Ar  168   2.127   0.891   0.281  0.1097 -0.1728 -0.1846
+  169Ar      Ar  169   0.480   1.651   1.586 -0.0530  0.1509 -0.2847
+  170Ar      Ar  170   0.337   0.970   0.143 -0.1076  0.0905  0.0728
+  171Ar      Ar  171   2.602   2.655   0.204  0.0918  0.3020  0.1643
+  172Ar      Ar  172   2.841   1.590   1.373  0.3828  0.0419  0.1547
+  173Ar      Ar  173   1.060   1.732   1.058  0.2372  0.2438  0.1438
+  174Ar      Ar  174   3.306   1.989   0.052 -0.1335 -0.0498  0.0813
+  175Ar      Ar  175   3.122   3.094   1.247 -0.2914  0.3106 -0.1361
+  176Ar      Ar  176   0.468   1.521   1.890 -0.1623  0.1552 -0.1455
+  177Ar      Ar  177   3.278   0.401   2.663 -0.0814  0.0735 -0.1181
+  178Ar      Ar  178   3.199   3.215   1.766 -0.1082  0.2998  0.0856
+  179Ar      Ar  179   2.938   1.185   2.508  0.1636  0.1028 -0.1936
+  180Ar      Ar  180   0.212   1.307   2.753  0.0279 -0.0552 -0.0825
+  181Ar      Ar  181   3.124   2.320   1.860 -0.2920  0.1096 -0.1793
+  182Ar      Ar  182   1.719   1.807   2.265 -0.1247  0.0978  0.1651
+  183Ar      Ar  183   0.948   0.467   2.602 -0.0014 -0.0121 -0.2507
+  184Ar      Ar  184   0.655   1.337   2.484  0.0858 -0.1501 -0.0315
+  185Ar      Ar  185   1.440   3.592   1.326  0.1219  0.0630 -0.0852
+  186Ar      Ar  186   0.560   0.773   1.198 -0.0378  0.0369 -0.0838
+  187Ar      Ar  187   1.528   0.868   2.431 -0.1013  0.2279 -0.0281
+  188Ar      Ar  188   0.319   0.363   2.303  0.0832  0.2163  0.0952
+  189Ar      Ar  189   1.517   2.585   2.751 -0.0696  0.1187  0.0852
+  190Ar      Ar  190   2.035   3.387   3.024  0.0230 -0.1239  0.1614
+  191Ar      Ar  191   3.222   3.412   0.762  0.0652 -0.0313 -0.0137
+  192Ar      Ar  192   2.517   0.411   0.475 -0.1049 -0.0835  0.0575
+  193Ar      Ar  193   2.937   0.255   2.845  0.3405 -0.1528 -0.0825
+  194Ar      Ar  194   3.150   2.996   3.026  0.1050  0.1129 -0.1547
+  195Ar      Ar  195   0.635   2.261   3.040  0.0995 -0.1058  0.0434
+  196Ar      Ar  196   0.636   1.198   2.038 -0.1159  0.2102  0.0686
+  197Ar      Ar  197   2.437   3.232   3.068  0.0507 -0.2243 -0.0930
+  198Ar      Ar  198   0.863   0.833   1.521 -0.1386 -0.2434  0.1023
+  199Ar      Ar  199   1.189   3.092   2.571  0.2493 -0.3856  0.0159
+  200Ar      Ar  200   0.503   2.049   0.544 -0.0478 -0.1911 -0.2353
+  201Ar      Ar  201   2.454   3.017   3.412  0.0410  0.1841 -0.1923
+  202Ar      Ar  202   0.680   3.149   1.569  0.0781  0.0613 -0.1196
+  203Ar      Ar  203   2.454   0.110   3.450  0.0821  0.2080  0.1157
+  204Ar      Ar  204   2.935   3.533   3.106 -0.0872 -0.2322 -0.0425
+  205Ar      Ar  205   0.731   0.070   0.965 -0.0839  0.0488 -0.0478
+  206Ar      Ar  206   3.582   0.833   2.709 -0.0653 -0.0010  0.0167
+  207Ar      Ar  207   2.802   2.198   1.143 -0.0345  0.0227 -0.1141
+  208Ar      Ar  208   3.516   1.163   0.733 -0.1713  0.1200  0.0072
+  209Ar      Ar  209   1.375   0.058   2.857  0.0574  0.1068  0.0312
+  210Ar      Ar  210   2.851   0.163   2.206 -0.1355  0.0587  0.0157
+  211Ar      Ar  211   2.652   1.345   2.210 -0.0688  0.0784  0.1077
+  212Ar      Ar  212   3.259   3.282   0.068 -0.2277  0.1086  0.1855
+  213Ar      Ar  213   2.602   2.550   1.244  0.0017 -0.1188 -0.0730
+  214Ar      Ar  214   1.510   3.571   3.185  0.2174 -0.0123  0.0523
+  215Ar      Ar  215   1.172   0.113   1.602 -0.1150 -0.1109  0.1234
+  216Ar      Ar  216   1.148   0.828   2.309 -0.2483  0.2681  0.0079
+  217Ar      Ar  217   1.829   0.047   1.445 -0.1038  0.0517  0.0987
+  218Ar      Ar  218   1.305   0.997   1.128  0.0583  0.1419  0.1957
+  219Ar      Ar  219   0.231   1.156   2.384  0.0735  0.0276  0.0522
+  220Ar      Ar  220   0.769   3.227   3.391 -0.0708 -0.1111  0.0615
+  221Ar      Ar  221   1.056   2.213   3.507  0.1695 -0.0990  0.1339
+  222Ar      Ar  222   0.985   3.555   3.483  0.1968  0.1633  0.1921
+  223Ar      Ar  223   3.260   1.137   0.123 -0.1033  0.3246 -0.2133
+  224Ar      Ar  224   1.676   1.002   0.986  0.0810 -0.4446 -0.1966
+  225Ar      Ar  225   0.964   1.181   2.534  0.1020  0.0880  0.0365
+  226Ar      Ar  226   3.079   1.980   1.169 -0.1290  0.1414 -0.0033
+  227Ar      Ar  227   2.651   2.062   2.067 -0.1578 -0.0612  0.2088
+  228Ar      Ar  228   1.160   1.842   0.364  0.2097  0.2974  0.0472
+  229Ar      Ar  229   0.323   0.302   3.271  0.2236 -0.0598 -0.1661
+  230Ar      Ar  230   0.734   3.328   0.402 -0.1028 -0.0186  0.1278
+  231Ar      Ar  231   3.565   0.123   3.263 -0.1946 -0.0365 -0.1423
+  232Ar      Ar  232   1.727   0.398   0.645 -0.0041 -0.3788  0.0503
+  233Ar      Ar  233   2.028   3.581   1.760  0.0867  0.0757 -0.1953
+  234Ar      Ar  234   2.737   2.500   2.557 -0.0368 -0.1178 -0.0832
+  235Ar      Ar  235   3.270   2.183   3.339  0.0142 -0.1703 -0.1667
+  236Ar      Ar  236   1.815   3.445   0.313 -0.1608  0.1962  0.0092
+  237Ar      Ar  237   3.028   1.240   2.847 -0.2249  0.1339  0.0394
+  238Ar      Ar  238   1.876   0.752   2.290  0.0725  0.0068  0.0269
+  239Ar      Ar  239   3.059   1.979   0.678 -0.1673  0.0563  0.0575
+  240Ar      Ar  240   1.229   2.363   2.634  0.0708  0.1681 -0.0234
+  241Ar      Ar  241   1.735   2.408   1.006 -0.0849  0.0113  0.1219
+  242Ar      Ar  242   1.738   2.346   3.289 -0.1186  0.0300  0.2035
+  243Ar      Ar  243   0.415   0.569   0.053 -0.2029  0.0503 -0.2183
+  244Ar      Ar  244   3.180   2.887   2.345  0.1838 -0.2714 -0.1704
+  245Ar      Ar  245   1.322   3.563   0.946  0.1157 -0.0133  0.1147
+  246Ar      Ar  246   2.072   1.948   0.876  0.0341 -0.2895  0.0707
+  247Ar      Ar  247   1.219   2.510   2.956  0.0702  0.0273  0.2218
+  248Ar      Ar  248   0.872   1.208   0.196 -0.0253 -0.1665 -0.0511
+  249Ar      Ar  249   2.234   1.472   1.733  0.2103  0.0270 -0.0001
+  250Ar      Ar  250   1.125   2.090   0.875  0.0278  0.0661 -0.1493
+  251Ar      Ar  251   1.494   1.331   1.015  0.0275 -0.0379  0.0595
+  252Ar      Ar  252   3.009   2.348   0.571 -0.1547 -0.0157  0.0774
+  253Ar      Ar  253   0.780   0.703   0.476 -0.0299 -0.0727 -0.1548
+  254Ar      Ar  254   1.403   3.317   2.199 -0.0959 -0.1316 -0.0009
+  255Ar      Ar  255   2.880   2.964   0.050 -0.0224  0.0515 -0.0066
+  256Ar      Ar  256   0.036   0.454   1.656  0.1766 -0.1231 -0.0055
+  257Ar      Ar  257   1.990   3.080   0.728 -0.0763 -0.0374  0.0242
+  258Ar      Ar  258   0.978   0.881   1.837  0.1228 -0.1251  0.1165
+  259Ar      Ar  259   0.872   2.067   1.165  0.2107  0.0591 -0.0278
+  260Ar      Ar  260   3.418   2.963   0.777  0.1980 -0.0738 -0.1187
+  261Ar      Ar  261   2.879   1.646   0.582  0.0223 -0.0196  0.1573
+  262Ar      Ar  262   0.222   0.558   1.955  0.1498 -0.0546  0.1113
+  263Ar      Ar  263   3.422   2.218   2.327  0.0659  0.1007 -0.0533
+  264Ar      Ar  264   0.080   0.863   0.448  0.0092 -0.0773 -0.0767
+  265Ar      Ar  265   1.057   2.952   0.574 -0.0158  0.0310  0.0743
+  266Ar      Ar  266   1.435   1.419   2.657  0.1135 -0.1981 -0.2033
+  267Ar      Ar  267   1.633   3.331   1.718  0.0522  0.2019  0.0565
+  268Ar      Ar  268   0.523   2.924   3.339  0.1897  0.1906  0.1074
+  269Ar      Ar  269   0.638   2.717   1.423  0.0702 -0.0151 -0.1701
+  270Ar      Ar  270   1.539   2.434   0.319 -0.0954 -0.0953 -0.2477
+  271Ar      Ar  271   3.591   1.771   3.478  0.0098  0.2185 -0.0599
+  272Ar      Ar  272   2.514   1.060   3.203 -0.0450 -0.0539  0.2200
+  273Ar      Ar  273   0.052   1.880   0.537 -0.0525  0.0612  0.0100
+  274Ar      Ar  274   0.960   2.403   0.687 -0.0543 -0.0433 -0.0258
+  275Ar      Ar  275   2.343   3.325   1.202 -0.0633 -0.1494  0.0067
+  276Ar      Ar  276   0.908   1.148   0.853  0.1182  0.2569 -0.0917
+  277Ar      Ar  277   3.068   0.258   3.254 -0.0722  0.3767 -0.0727
+  278Ar      Ar  278   3.235   3.335   3.026 -0.0717 -0.0758  0.1202
+  279Ar      Ar  279   3.478   1.444   1.371  0.0610  0.0014 -0.0588
+  280Ar      Ar  280   0.762   1.869   1.731 -0.0110 -0.1327  0.0091
+  281Ar      Ar  281   0.612   1.340   0.709 -0.0891 -0.1512  0.1210
+  282Ar      Ar  282   0.049   1.125   1.417  0.0336 -0.0723  0.0411
+  283Ar      Ar  283   3.599   2.238   1.983 -0.1265 -0.0075  0.0755
+  284Ar      Ar  284   3.471   1.129   1.814 -0.0841  0.0443  0.0037
+  285Ar      Ar  285   0.485   2.440   3.447  0.3550  0.0487 -0.1908
+  286Ar      Ar  286   0.704   2.212   0.881  0.1525  0.2014 -0.2512
+  287Ar      Ar  287   2.544   2.245   1.434 -0.1054 -0.0602 -0.0492
+  288Ar      Ar  288   2.701   0.730   1.856 -0.0827  0.2991 -0.1377
+  289Ar      Ar  289   0.309   0.206   0.302  0.0896  0.1132  0.1359
+  290Ar      Ar  290   1.756   3.462   2.014  0.0655 -0.0824 -0.0633
+  291Ar      Ar  291   1.032   2.848   2.305 -0.0777  0.0239 -0.0584
+  292Ar      Ar  292   3.416   1.781   1.334  0.0009 -0.1568 -0.0702
+  293Ar      Ar  293   1.513   2.779   0.473 -0.1477 -0.1735 -0.0217
+  294Ar      Ar  294   1.355   1.744   2.984  0.0885  0.0214 -0.1664
+  295Ar      Ar  295   2.420   2.326   2.658  0.3238  0.0160 -0.1162
+  296Ar      Ar  296   2.686   0.335   1.149 -0.0390 -0.1808 -0.1479
+  297Ar      Ar  297   0.797   0.671   0.823 -0.0456  0.0688  0.2837
+  298Ar      Ar  298   1.292   1.525   1.241 -0.0873  0.1290  0.0397
+  299Ar      Ar  299   0.437   0.038   1.135  0.0375 -0.1443  0.0573
+  300Ar      Ar  300   2.195   1.533   0.990 -0.2235 -0.0896  0.2008
+  301Ar      Ar  301   1.618   0.105   0.924 -0.0218  0.1085 -0.0008
+  302Ar      Ar  302   1.181   2.600   1.706 -0.1618  0.0794  0.1495
+  303Ar      Ar  303   0.926   2.940   0.256 -0.0525 -0.1396  0.0833
+  304Ar      Ar  304   1.430   1.991   0.794 -0.0254  0.0030  0.0356
+  305Ar      Ar  305   1.969   2.256   2.185 -0.0939 -0.1327 -0.2325
+  306Ar      Ar  306   3.057   1.209   0.442 -0.0248  0.1619 -0.0172
+  307Ar      Ar  307   2.864   3.493   2.733 -0.1355 -0.0678  0.0923
+  308Ar      Ar  308   0.739   2.895   2.560 -0.0995  0.0964  0.0538
+  309Ar      Ar  309   3.547   2.621   0.459 -0.0346  0.0023  0.2401
+  310Ar      Ar  310   1.700   0.223   1.997  0.0277 -0.0113  0.1684
+  311Ar      Ar  311   3.211   0.938   2.667  0.1409  0.0321  0.0080
+  312Ar      Ar  312   2.823   2.865   1.264  0.0551  0.0910  0.2440
+  313Ar      Ar  313   2.682   3.349   3.333  0.1166  0.1040 -0.0215
+  314Ar      Ar  314   0.213   0.165   1.776 -0.0765  0.1307 -0.0888
+  315Ar      Ar  315   1.324   1.208   2.330  0.0219  0.1077 -0.0138
+  316Ar      Ar  316   0.648   2.578   2.765  0.0379 -0.0065 -0.2623
+  317Ar      Ar  317   0.603   0.795   2.815  0.0014  0.1053 -0.1948
+  318Ar      Ar  318   2.759   3.320   2.098  0.0452 -0.3481 -0.1388
+  319Ar      Ar  319   2.653   2.262   0.825  0.2046 -0.0603 -0.1920
+  320Ar      Ar  320   1.647   2.880   2.296 -0.2388  0.0216  0.1445
+  321Ar      Ar  321   1.711   3.100   2.033 -0.0284 -0.0518 -0.0161
+  322Ar      Ar  322   3.526   1.235   2.157 -0.1738 -0.3112  0.0927
+  323Ar      Ar  323   1.471   1.549   0.071 -0.0613  0.0297  0.1846
+  324Ar      Ar  324   2.811   1.524   2.827 -0.1408  0.0872 -0.2802
+  325Ar      Ar  325   0.701   3.518   0.092 -0.1592 -0.1859  0.1041
+  326Ar      Ar  326   1.559   2.149   0.028 -0.0062 -0.1122 -0.0123
+  327Ar      Ar  327   1.944   1.028   2.134 -0.2960 -0.2788  0.2945
+  328Ar      Ar  328   2.901   1.805   0.022  0.2347  0.0599  0.2640
+  329Ar      Ar  329   1.757   2.848   3.312  0.0030  0.2814 -0.0952
+  330Ar      Ar  330   3.049   2.796   0.675 -0.1732  0.1245 -0.1581
+  331Ar      Ar  331   1.552   0.849   1.565  0.2460  0.0529 -0.0666
+  332Ar      Ar  332   0.422   3.207   0.685 -0.1616  0.0019  0.1057
+  333Ar      Ar  333   2.699   3.166   1.023 -0.1401 -0.1053 -0.1273
+  334Ar      Ar  334   0.924   0.967   2.817 -0.0291  0.2143 -0.0281
+  335Ar      Ar  335   2.256   2.569   1.271  0.0079 -0.0527 -0.2381
+  336Ar      Ar  336   3.508   2.666   2.414  0.0380  0.4444  0.0217
+  337Ar      Ar  337   3.164   1.753   3.372 -0.1589  0.0010  0.0391
+  338Ar      Ar  338   1.322   2.417   0.029  0.3737  0.0611 -0.0258
+  339Ar      Ar  339   0.817   1.051   0.521  0.0404 -0.0840  0.0352
+  340Ar      Ar  340   2.304   1.050   2.169 -0.0501  0.0725  0.1618
+  341Ar      Ar  341   2.927   2.334   3.166 -0.0217 -0.0106 -0.2871
+  342Ar      Ar  342   3.360   0.764   1.140  0.1381 -0.0166 -0.0356
+  343Ar      Ar  343   1.394   3.381   2.618  0.2438 -0.0886  0.2232
+  344Ar      Ar  344   0.003   1.665   1.059  0.0365  0.2781  0.0216
+  345Ar      Ar  345   2.335   1.366   2.022 -0.0270  0.0922 -0.0653
+  346Ar      Ar  346   1.145   2.631   3.314  0.0678  0.1113  0.0324
+  347Ar      Ar  347   1.396   1.651   0.901 -0.1575 -0.2051 -0.0023
+  348Ar      Ar  348   0.666   1.601   3.033 -0.0810  0.1854  0.1932
+  349Ar      Ar  349   1.182   3.465   1.894 -0.1653  0.1661 -0.0392
+  350Ar      Ar  350   3.306   0.527   2.304 -0.0480 -0.1123  0.0419
+  351Ar      Ar  351   0.586   2.987   0.906  0.1559 -0.0602 -0.0863
+  352Ar      Ar  352   0.395   0.948   1.450  0.1131 -0.1236 -0.1043
+  353Ar      Ar  353   0.158   3.547   1.485  0.1955  0.1335  0.2143
+  354Ar      Ar  354   1.379   1.860   1.144  0.0149  0.0719 -0.0618
+  355Ar      Ar  355   1.677   0.146   0.310  0.1241 -0.0660 -0.2245
+  356Ar      Ar  356   2.192   2.296   1.928  0.1264 -0.0896 -0.0253
+  357Ar      Ar  357   2.320   0.466   3.289 -0.1997  0.1873 -0.1052
+  358Ar      Ar  358   0.402   2.340   2.755  0.0094 -0.0682  0.0274
+  359Ar      Ar  359   1.876   2.718   0.763 -0.0602 -0.1893 -0.1273
+  360Ar      Ar  360   2.173   0.242   0.928  0.1566 -0.0386  0.1711
+  361Ar      Ar  361   0.269   1.608   3.026 -0.3331 -0.0309 -0.0071
+  362Ar      Ar  362   0.943   3.462   1.503  0.2326 -0.0509 -0.0475
+  363Ar      Ar  363   1.339   2.985   0.753 -0.1787 -0.1239 -0.0610
+  364Ar      Ar  364   0.644   2.991   0.488 -0.0494 -0.0535  0.0102
+  365Ar      Ar  365   1.157   1.970   2.646 -0.2544 -0.2327 -0.3646
+  366Ar      Ar  366   3.129   0.096   1.299 -0.1208 -0.2267  0.0677
+  367Ar      Ar  367   3.510   2.961   0.310  0.0381  0.1901  0.0190
+  368Ar      Ar  368   1.205   0.400   2.935 -0.0440 -0.0147  0.0435
+  369Ar      Ar  369   3.424   2.985   2.712  0.1109 -0.2234  0.1315
+  370Ar      Ar  370   1.791   1.132   2.767  0.0478 -0.0612  0.0931
+  371Ar      Ar  371   1.198   2.997   3.558  0.1984 -0.1224 -0.1370
+  372Ar      Ar  372   1.390   0.293   3.241  0.0241  0.0195  0.0420
+  373Ar      Ar  373   0.518   0.746   3.307 -0.0761 -0.1210 -0.0660
+  374Ar      Ar  374   3.092   2.682   1.632  0.1608 -0.1467 -0.0329
+  375Ar      Ar  375   2.426   1.239   1.507 -0.2231  0.1746  0.0274
+  376Ar      Ar  376   1.380   0.810   1.917 -0.0066 -0.0982  0.0684
+  377Ar      Ar  377   3.287   1.739   0.958 -0.0160 -0.1995 -0.0813
+  378Ar      Ar  378   0.049   0.509   2.695 -0.1687  0.0819 -0.1400
+  379Ar      Ar  379   0.557   1.699   2.673 -0.0694 -0.0332 -0.0003
+  380Ar      Ar  380   2.400   3.149   2.027 -0.0635  0.2839  0.0379
+  381Ar      Ar  381   1.804   2.977   1.335  0.0261 -0.1525  0.0437
+  382Ar      Ar  382   2.327   2.839   1.820  0.1049 -0.0370 -0.1615
+  383Ar      Ar  383   2.999   1.251   1.395 -0.0987  0.1470 -0.1154
+  384Ar      Ar  384   2.548   1.594   1.174 -0.2581 -0.2299  0.0113
+  385Ar      Ar  385   2.453   2.666   3.063 -0.0668 -0.2070 -0.1568
+  386Ar      Ar  386   2.532   0.271   0.809  0.2871  0.0029  0.0223
+  387Ar      Ar  387   0.006   0.973   0.057 -0.0366 -0.2394  0.1532
+  388Ar      Ar  388   1.772   2.843   0.057  0.0327  0.1302 -0.0774
+  389Ar      Ar  389   0.808   0.508   1.123  0.0976  0.2318  0.1174
+  390Ar      Ar  390   0.266   3.486   0.096 -0.0094  0.0513  0.0976
+  391Ar      Ar  391   3.229   0.230   0.619  0.0600 -0.1371 -0.0525
+  392Ar      Ar  392   1.801   0.907   0.129 -0.1419  0.1148 -0.1222
+  393Ar      Ar  393   3.389   2.262   2.701 -0.0580 -0.1728  0.1227
+  394Ar      Ar  394   0.908   1.848   3.573 -0.0867  0.0381  0.0953
+  395Ar      Ar  395   3.389   3.399   2.256 -0.0482 -0.0799 -0.0447
+  396Ar      Ar  396   0.257   2.792   1.499  0.1194  0.2002 -0.0863
+  397Ar      Ar  397   1.827   2.930   1.703  0.1229  0.1703 -0.0434
+  398Ar      Ar  398   0.587   0.519   2.489 -0.0267  0.2528  0.2250
+  399Ar      Ar  399   2.673   0.280   3.206  0.1003  0.0887 -0.0153
+  400Ar      Ar  400   1.050   3.358   3.135 -0.1199 -0.0156  0.0643
+  401Ar      Ar  401   1.836   1.100   0.491  0.2024 -0.0963  0.0754
+  402Ar      Ar  402   2.107   2.760   3.149 -0.0629 -0.0306  0.2479
+  403Ar      Ar  403   1.200   0.925   0.822  0.1515  0.0684  0.1617
+  404Ar      Ar  404   3.008   2.288   1.498  0.0546 -0.0893  0.2002
+  405Ar      Ar  405   3.396   3.216   1.049 -0.1847  0.1493 -0.0831
+  406Ar      Ar  406   1.580   0.119   1.688  0.2241  0.1777 -0.0923
+  407Ar      Ar  407   0.982   1.830   0.704  0.0629  0.0688 -0.0016
+  408Ar      Ar  408   2.030   0.504   1.120 -0.1768  0.2347  0.1318
+  409Ar      Ar  409   3.069   0.575   0.833  0.0987 -0.2289  0.1522
+  410Ar      Ar  410   0.725   2.196   3.588  0.2236  0.2017  0.0202
+  411Ar      Ar  411   2.467   1.276   0.956 -0.1676 -0.0840  0.0302
+  412Ar      Ar  412   1.195   0.751   1.333  0.1645  0.1301  0.0783
+  413Ar      Ar  413   2.121   1.374   3.579 -0.1538  0.0017  0.1215
+  414Ar      Ar  414   0.314   2.350   0.893  0.0456  0.0859  0.0692
+  415Ar      Ar  415   2.963   0.402   1.426  0.1354 -0.1858  0.0680
+  416Ar      Ar  416   1.096   0.057   1.182 -0.1146  0.0377 -0.1758
+  417Ar      Ar  417   2.660   1.101   1.265  0.1525 -0.2222 -0.0221
+  418Ar      Ar  418   0.816   0.006   2.565  0.1734  0.0141  0.0442
+  419Ar      Ar  419   1.673   1.710   3.281  0.0098  0.0049 -0.0543
+  420Ar      Ar  420   2.560   1.420   3.028  0.0349 -0.0891  0.1151
+  421Ar      Ar  421   1.661   1.283   1.671  0.0306  0.0407 -0.0547
+  422Ar      Ar  422   2.006   1.615   0.697  0.0823 -0.2593  0.0089
+  423Ar      Ar  423   1.021   2.430   1.108  0.4212 -0.0995  0.2735
+  424Ar      Ar  424   3.467   2.362   3.050  0.1978 -0.1435 -0.1243
+  425Ar      Ar  425   0.777   2.707   2.048 -0.1060 -0.0614 -0.0971
+  426Ar      Ar  426   0.590   1.003   2.398  0.0056 -0.1129  0.0379
+  427Ar      Ar  427   0.485   1.304   1.593 -0.0971  0.1830 -0.0939
+  428Ar      Ar  428   1.872   3.030   0.366  0.0970 -0.1276  0.0508
+  429Ar      Ar  429   2.289   0.280   0.263  0.0496 -0.0505  0.1475
+  430Ar      Ar  430   0.719   3.418   2.260 -0.0641 -0.2593 -0.2030
+  431Ar      Ar  431   2.053   0.573   0.646 -0.1567 -0.0648 -0.0135
+  432Ar      Ar  432   0.207   1.123   3.383  0.1397 -0.1612  0.0478
+  433Ar      Ar  433   2.407   2.666   2.571  0.0682 -0.1714 -0.0118
+  434Ar      Ar  434   1.791   2.090   0.789  0.0494 -0.0071 -0.2239
+  435Ar      Ar  435   1.138   1.335   3.363  0.0353 -0.1884  0.1132
+  436Ar      Ar  436   1.359   2.534   1.211  0.0372  0.1274  0.1133
+  437Ar      Ar  437   2.040   1.004   3.033 -0.0996 -0.0858 -0.2596
+  438Ar      Ar  438   1.538   3.265   1.047  0.1333 -0.0014  0.0807
+  439Ar      Ar  439   2.937   1.828   2.823 -0.0017  0.0760 -0.1590
+  440Ar      Ar  440   1.517   2.715   1.514  0.0869  0.1303 -0.0985
+  441Ar      Ar  441   0.433   0.964   0.495  0.0179  0.0537 -0.1138
+  442Ar      Ar  442   3.423   2.280   0.570 -0.3023 -0.0595 -0.2358
+  443Ar      Ar  443   1.704   1.768   0.640 -0.0051  0.0503  0.1047
+  444Ar      Ar  444   2.189   3.008   1.336  0.1134  0.0943  0.1220
+  445Ar      Ar  445   1.524   2.169   2.706  0.0256  0.0223  0.2183
+  446Ar      Ar  446   0.117   1.241   1.064 -0.0557 -0.2292 -0.0300
+  447Ar      Ar  447   2.184   0.116   2.760 -0.2160  0.0071 -0.0465
+  448Ar      Ar  448   1.732   0.202   3.481 -0.0144  0.0564  0.0773
+  449Ar      Ar  449   1.211   0.733   2.847  0.0569 -0.2349  0.1727
+  450Ar      Ar  450   2.659   2.011   0.601  0.0761  0.1058 -0.0987
+  451Ar      Ar  451   0.183   2.708   3.384 -0.0954  0.1649  0.0934
+  452Ar      Ar  452   3.394   2.353   0.016  0.0892 -0.2011 -0.0176
+  453Ar      Ar  453   2.847   0.677   3.382  0.1457 -0.0420  0.0299
+  454Ar      Ar  454   0.599   1.481   0.009  0.1518 -0.1001 -0.1050
+  455Ar      Ar  455   1.994   2.814   2.367 -0.1541  0.3403  0.1078
+  456Ar      Ar  456   1.821   0.759   3.171  0.0243  0.0124  0.0352
+  457Ar      Ar  457   3.523   0.845   3.104  0.0567 -0.0771 -0.0486
+  458Ar      Ar  458   1.505   2.767   1.873 -0.0022  0.2989  0.1032
+  459Ar      Ar  459   0.851   0.556   2.953  0.0690 -0.1880 -0.0610
+  460Ar      Ar  460   3.417   3.105   3.247 -0.1485  0.0798  0.2027
+  461Ar      Ar  461   2.371   1.534   2.674 -0.0125 -0.1743 -0.3612
+  462Ar      Ar  462   0.356   0.295   2.664 -0.1479  0.0196  0.0516
+  463Ar      Ar  463   0.033   2.367   1.098 -0.1928  0.1503  0.1642
+  464Ar      Ar  464   1.161   1.402   0.855  0.0559 -0.0921 -0.0656
+  465Ar      Ar  465   1.288   3.351   1.554  0.0581  0.2139 -0.1289
+  466Ar      Ar  466   1.082   3.312   0.500 -0.0662  0.0273 -0.1932
+  467Ar      Ar  467   2.755   2.330   0.228  0.0109  0.2684  0.0070
+  468Ar      Ar  468   2.889   3.178   2.575  0.1062  0.1600 -0.0178
+  469Ar      Ar  469   1.287   3.405   3.410  0.1462  0.0564  0.0789
+  470Ar      Ar  470   3.206   0.640   3.297 -0.2026 -0.0289 -0.1645
+  471Ar      Ar  471   3.021   0.323   1.066 -0.0890  0.1434 -0.0218
+  472Ar      Ar  472   0.828   2.706   0.561 -0.0357 -0.0031  0.1182
+  473Ar      Ar  473   2.706   1.149   2.792 -0.0053  0.0042  0.1441
+  474Ar      Ar  474   0.065   2.084   3.562 -0.2005  0.0114 -0.0910
+  475Ar      Ar  475   2.353   1.114   2.817 -0.1312  0.0722  0.0368
+  476Ar      Ar  476   3.283   1.209   2.407 -0.0824 -0.1107  0.0013
+  477Ar      Ar  477   3.443   2.726   0.063 -0.0850  0.0907  0.1129
+  478Ar      Ar  478   1.572   0.595   3.438 -0.2428  0.0627  0.0340
+  479Ar      Ar  479   3.276   1.622   0.574  0.0157 -0.1202 -0.0075
+  480Ar      Ar  480   2.803   0.953   0.493 -0.1170  0.0676  0.0412
+  481Ar      Ar  481   0.408   2.885   2.738 -0.0268 -0.1200  0.1064
+  482Ar      Ar  482   0.899   2.287   2.777 -0.0631 -0.2523  0.0386
+  483Ar      Ar  483   0.469   3.565   2.052 -0.2645  0.2281 -0.1096
+  484Ar      Ar  484   1.441   2.568   2.153 -0.2750 -0.0553 -0.2570
+  485Ar      Ar  485   1.095   2.700   0.060  0.0136 -0.2705 -0.1226
+  486Ar      Ar  486   2.883   3.213   1.497 -0.1332 -0.1251  0.0446
+  487Ar      Ar  487   3.227   2.506   2.130  0.1474  0.0430 -0.0847
+  488Ar      Ar  488   1.640   1.476   1.356 -0.1071  0.2731  0.0679
+  489Ar      Ar  489   3.330   0.124   2.984 -0.0347 -0.2169 -0.0219
+  490Ar      Ar  490   0.199   3.452   1.868  0.1214 -0.1134  0.2002
+  491Ar      Ar  491   1.153   2.600   0.396 -0.0347  0.0128  0.1749
+  492Ar      Ar  492   0.323   2.660   1.130  0.0532 -0.2201  0.0093
+  493Ar      Ar  493   0.814   2.899   3.499 -0.4200  0.0174 -0.0156
+  494Ar      Ar  494   2.771   3.018   3.257 -0.0446  0.0236 -0.0286
+  495Ar      Ar  495   1.094   2.788   1.284 -0.0339 -0.1892  0.1527
+  496Ar      Ar  496   2.703   2.739   1.570 -0.0228 -0.0178  0.0332
+  497Ar      Ar  497   1.604   1.010   2.101 -0.0457 -0.1146  0.0140
+  498Ar      Ar  498   0.878   0.673   3.442 -0.1224 -0.0250 -0.0234
+  499Ar      Ar  499   2.128   2.136   2.757 -0.0453 -0.0010  0.1204
+  500Ar      Ar  500   2.215   3.369   0.808 -0.1304  0.0007 -0.0096
+  501Ar      Ar  501   0.402   1.641   0.237  0.0298 -0.2017  0.0490
+  502Ar      Ar  502   2.918   1.943   2.409 -0.0775 -0.1300  0.0277
+  503Ar      Ar  503   0.540   3.369   1.347  0.0153  0.0537  0.2003
+  504Ar      Ar  504   1.487   1.153   1.350  0.0253  0.2292 -0.1962
+  505Ar      Ar  505   0.818   1.486   1.537 -0.2147 -0.1463  0.0370
+  506Ar      Ar  506   0.563   3.493   1.672 -0.0741  0.0714  0.0824
+  507Ar      Ar  507   0.657   0.195   3.139  0.0815 -0.0702 -0.0107
+  508Ar      Ar  508   1.973   0.641   3.511 -0.0876  0.0894  0.2324
+  509Ar      Ar  509   0.625   1.024   1.704  0.0285 -0.0735  0.1412
+  510Ar      Ar  510   0.091   3.130   1.906  0.0391 -0.0278  0.0776
+  511Ar      Ar  511   3.145   0.301   0.288 -0.1924  0.1736  0.0458
+  512Ar      Ar  512   2.360   0.337   2.950 -0.2611 -0.3061 -0.0326
+  513Ar      Ar  513   1.339   0.309   1.062  0.0088 -0.3096 -0.1839
+  514Ar      Ar  514   3.551   2.744   3.129  0.1050 -0.0996  0.1373
+  515Ar      Ar  515   2.771   0.100   1.413  0.0493  0.1460 -0.2551
+  516Ar      Ar  516   0.695   0.259   2.233 -0.0670  0.1752 -0.0303
+  517Ar      Ar  517   1.765   0.668   0.926 -0.1132 -0.1864 -0.0156
+  518Ar      Ar  518   0.915   2.423   0.209 -0.1443 -0.1254  0.0063
+  519Ar      Ar  519   2.387   2.856   2.231  0.0100  0.0722  0.1133
+  520Ar      Ar  520   0.520   2.160   1.181 -0.0412 -0.1773  0.1315
+  521Ar      Ar  521   1.898   0.747   2.765  0.1528 -0.1112 -0.0810
+  522Ar      Ar  522   0.037   0.806   0.812  0.2054  0.1012  0.0622
+  523Ar      Ar  523   1.729   2.670   1.224  0.0373  0.1481 -0.1489
+  524Ar      Ar  524   1.991   1.334   1.530 -0.0710 -0.1621 -0.0008
+  525Ar      Ar  525   3.497   0.280   0.326  0.0199 -0.1186 -0.0687
+  526Ar      Ar  526   2.767   2.838   0.905 -0.0328 -0.0731 -0.0007
+  527Ar      Ar  527   3.205   3.257   2.704  0.1417  0.1264 -0.0788
+  528Ar      Ar  528   2.384   1.920   1.322  0.1722 -0.0927 -0.0648
+  529Ar      Ar  529   3.053   1.305   0.758  0.2726 -0.0220 -0.1775
+  530Ar      Ar  530   1.545   2.342   1.439 -0.0371 -0.3024  0.1798
+  531Ar      Ar  531   0.969   3.276   0.088  0.1024  0.2626  0.1247
+  532Ar      Ar  532   2.371   2.344   0.066 -0.1036  0.0393  0.1314
+  533Ar      Ar  533   2.299   3.090   2.462 -0.2300  0.0532 -0.1694
+  534Ar      Ar  534   1.862   0.420   2.370  0.1020  0.0889 -0.2726
+  535Ar      Ar  535   2.965   2.106   3.484  0.0200 -0.0374  0.1738
+  536Ar      Ar  536   1.156   1.590   2.307  0.0107 -0.0524  0.2344
+  537Ar      Ar  537   1.135   2.836   1.981  0.0466  0.1414 -0.1459
+  538Ar      Ar  538   3.136   2.087   2.150  0.0622 -0.0304 -0.1800
+  539Ar      Ar  539   1.186   3.268   1.220  0.2102  0.0132 -0.0074
+  540Ar      Ar  540   0.099   0.664   3.393 -0.0816  0.1860  0.2068
+  541Ar      Ar  541   1.172   1.583   1.923  0.1190 -0.2111 -0.1517
+  542Ar      Ar  542   2.519   0.688   2.141  0.2087  0.0337  0.0625
+  543Ar      Ar  543   1.878   1.236   1.127  0.0410 -0.2033 -0.0249
+  544Ar      Ar  544   0.122   0.595   0.234  0.1948  0.1755 -0.0947
+  545Ar      Ar  545   3.197   0.156   2.314  0.2960 -0.1105 -0.0346
+  546Ar      Ar  546   2.719   2.341   3.476  0.0691  0.1825 -0.0538
+  547Ar      Ar  547   1.965   0.466   2.035 -0.1108 -0.1726  0.0095
+  548Ar      Ar  548   1.596   2.582   3.514  0.1451 -0.0797  0.1288
+  549Ar      Ar  549   3.329   0.298   3.532  0.2138 -0.2978 -0.1010
+  550Ar      Ar  550   1.163   2.949   1.558  0.1041  0.0506  0.0180
+  551Ar      Ar  551   0.004   1.621   0.208 -0.2024  0.1110  0.0927
+  552Ar      Ar  552   3.169   2.887   3.375  0.1557  0.0999  0.0568
+  553Ar      Ar  553   3.342   2.246   1.284 -0.1096 -0.2793 -0.3806
+  554Ar      Ar  554   3.250   1.669   0.131 -0.1467  0.1171 -0.1631
+  555Ar      Ar  555   0.073   1.217   3.077  0.0050  0.0530 -0.0351
+  556Ar      Ar  556   0.027   2.062   3.190 -0.2972  0.1257 -0.0241
+  557Ar      Ar  557   0.023   3.226   0.630 -0.0376  0.1000 -0.1891
+  558Ar      Ar  558   0.492   1.366   3.228 -0.1124  0.0135  0.1300
+  559Ar      Ar  559   2.460   2.018   1.731  0.0661  0.0937 -0.1974
+  560Ar      Ar  560   0.052   3.114   1.309  0.2490  0.0351  0.1246
+  561Ar      Ar  561   0.145   2.478   1.742  0.0952 -0.0451 -0.0957
+  562Ar      Ar  562   2.318   3.050   0.896 -0.1382  0.0459 -0.1475
+  563Ar      Ar  563   3.557   0.363   2.131  0.0863  0.0552 -0.0801
+  564Ar      Ar  564   0.870   0.512   1.771  0.0337  0.1995 -0.1886
+  565Ar      Ar  565   0.074   1.864   2.858 -0.1096  0.0917  0.0178
+  566Ar      Ar  566   2.192   3.521   1.449  0.0627 -0.1824 -0.0605
+  567Ar      Ar  567   0.698   1.574   2.229 -0.0481 -0.0439  0.0203
+  568Ar      Ar  568   1.010   0.013   0.294  0.1223 -0.2315  0.1369
+  569Ar      Ar  569   2.929   3.392   1.169  0.0217 -0.2874 -0.2680
+  570Ar      Ar  570   2.479   1.755   1.925 -0.0250  0.0445  0.0820
+  571Ar      Ar  571   2.490   1.004   0.259  0.0573  0.0739 -0.1134
+  572Ar      Ar  572   2.599   3.138   0.691 -0.0046 -0.0888 -0.1499
+  573Ar      Ar  573   3.212   1.128   2.040  0.0028 -0.0162  0.2109
+  574Ar      Ar  574   0.204   2.572   2.273  0.1334 -0.0723 -0.0980
+  575Ar      Ar  575   1.972   3.106   2.726 -0.1671  0.0181  0.0812
+  576Ar      Ar  576   2.667   3.575   1.033  0.0989  0.0481 -0.0942
+  577Ar      Ar  577   3.256   0.609   1.471 -0.1196  0.1307  0.1338
+  578Ar      Ar  578   0.157   2.486   0.096  0.0063  0.1377  0.0552
+  579Ar      Ar  579   2.629   2.017   0.019 -0.0199  0.2155 -0.0494
+  580Ar      Ar  580   0.351   2.901   0.055  0.0278 -0.2540 -0.1695
+  581Ar      Ar  581   2.131   0.969   1.447 -0.0057  0.1593 -0.1237
+  582Ar      Ar  582   1.709   0.378   1.162  0.0574  0.0233  0.0614
+  583Ar      Ar  583   3.248   3.200   0.492  0.1501 -0.2950  0.0508
+  584Ar      Ar  584   2.108   2.135   1.203 -0.1621 -0.1361 -0.0119
+  585Ar      Ar  585   0.440   0.195   1.496 -0.1960 -0.2335 -0.0892
+  586Ar      Ar  586   2.426   1.923   0.961  0.2363  0.2588  0.0480
+  587Ar      Ar  587   0.276   3.094   0.411 -0.1213 -0.0727 -0.0950
+  588Ar      Ar  588   0.564   2.557   1.742  0.1915 -0.1261 -0.1664
+  589Ar      Ar  589   2.115   2.157   3.505  0.0321 -0.0385  0.1320
+  590Ar      Ar  590   1.146   0.614   0.968  0.1012 -0.0808 -0.1208
+  591Ar      Ar  591   0.396   2.307   2.387 -0.1566  0.0343  0.1525
+  592Ar      Ar  592   2.381   1.535   3.333 -0.1327  0.1152  0.0392
+  593Ar      Ar  593   1.770   2.029   1.131 -0.0775 -0.0031  0.0707
+  594Ar      Ar  594   0.649   2.512   1.117 -0.2259 -0.0928 -0.0553
+  595Ar      Ar  595   2.277   1.776   3.592  0.1488  0.0530 -0.0578
+  596Ar      Ar  596   1.384   0.251   3.595 -0.1014  0.0420 -0.2608
+  597Ar      Ar  597   1.860   3.165   3.495 -0.2394 -0.1515  0.0041
+  598Ar      Ar  598   2.411   0.742   2.724  0.3017 -0.2367  0.1171
+  599Ar      Ar  599   1.360   0.144   0.668  0.0301  0.0605 -0.1875
+  600Ar      Ar  600   3.402   1.369   1.007  0.1406  0.0246  0.0916
+  601Ar      Ar  601   2.932   3.056   2.202 -0.0862  0.1944 -0.1226
+  602Ar      Ar  602   2.919   3.028   0.410  0.0925 -0.0854 -0.0877
+  603Ar      Ar  603   1.494   3.026   2.754 -0.1085 -0.1218 -0.2065
+  604Ar      Ar  604   3.024   0.460   3.559 -0.0208  0.0327  0.0177
+  605Ar      Ar  605   0.262   1.258   2.001 -0.1625  0.0566  0.1654
+  606Ar      Ar  606   1.358   3.135   1.868 -0.1020 -0.0103 -0.1408
+  607Ar      Ar  607   2.389   1.894   2.749  0.0919  0.0988  0.1149
+  608Ar      Ar  608   1.024   0.339   0.813 -0.1037 -0.0755 -0.2458
+  609Ar      Ar  609   2.467   0.281   2.260  0.0463 -0.0843  0.1339
+  610Ar      Ar  610   1.316   2.351   3.268 -0.1961  0.2579  0.1780
+  611Ar      Ar  611   0.139   3.444   3.125  0.0513 -0.0296  0.2520
+  612Ar      Ar  612   2.951   2.492   1.005 -0.1624  0.2638 -0.1224
+  613Ar      Ar  613   3.457   1.601   2.330  0.2738 -0.1474 -0.0943
+  614Ar      Ar  614   1.513   0.518   2.243 -0.2302  0.1798 -0.0516
+  615Ar      Ar  615   0.665   1.822   1.355 -0.0013 -0.0581  0.0467
+  616Ar      Ar  616   1.779   2.559   2.033 -0.2832 -0.0806  0.3007
+  617Ar      Ar  617   3.365   0.392   0.935 -0.1443 -0.0582 -0.1358
+  618Ar      Ar  618   2.234   1.262   1.221  0.1324  0.0128  0.0578
+  619Ar      Ar  619   1.505   1.023   2.981 -0.0185 -0.0495 -0.0014
+  620Ar      Ar  620   0.833   0.801   2.526  0.1242  0.0048  0.1105
+  621Ar      Ar  621   0.185   1.486   3.326 -0.0388  0.1152 -0.2672
+  622Ar      Ar  622   2.441   0.079   1.270 -0.0227  0.0958  0.0610
+  623Ar      Ar  623   2.927   3.292   0.642 -0.2373  0.1854 -0.1094
+  624Ar      Ar  624   1.193   2.888   2.902 -0.0566  0.1284  0.0750
+  625Ar      Ar  625   3.488   1.625   3.173 -0.0022  0.2499 -0.0339
+  626Ar      Ar  626   1.459   3.049   3.311 -0.0986 -0.2750 -0.0593
+  627Ar      Ar  627   1.589   1.837   1.413 -0.1846  0.1069 -0.1209
+  628Ar      Ar  628   1.379   0.324   0.363  0.0764  0.1558 -0.0032
+  629Ar      Ar  629   0.451   0.776   2.146 -0.0085 -0.0827  0.0801
+  630Ar      Ar  630   3.100   0.828   0.095 -0.0196 -0.0071  0.0532
+  631Ar      Ar  631   2.385   2.551   1.636 -0.2553 -0.0912  0.1777
+  632Ar      Ar  632   0.687   0.497   1.465  0.1954 -0.0537  0.0757
+  633Ar      Ar  633   0.823   1.279   3.099 -0.1699 -0.1697 -0.0370
+  634Ar      Ar  634   1.355   2.745   2.484  0.0942 -0.2115  0.3735
+  635Ar      Ar  635   1.363   2.373   0.814 -0.0342  0.0723 -0.0159
+  636Ar      Ar  636   0.272   1.958   0.873  0.2122 -0.0119 -0.2675
+  637Ar      Ar  637   1.784   1.472   2.863  0.3278  0.0295  0.1045
+  638Ar      Ar  638   3.561   3.337   3.436 -0.1909 -0.1496 -0.0688
+  639Ar      Ar  639   0.242   2.901   0.824  0.0605  0.0766 -0.0877
+  640Ar      Ar  640   2.628   0.428   3.543 -0.0035  0.1430  0.0459
+  641Ar      Ar  641   1.475   1.357   1.956  0.0936  0.0845 -0.2741
+  642Ar      Ar  642   1.905   1.447   3.275  0.1502  0.0533 -0.0463
+  643Ar      Ar  643   1.204   1.252   2.910 -0.0551 -0.1959 -0.4220
+  644Ar      Ar  644   0.960   1.556   0.118 -0.0615  0.1401  0.0190
+  645Ar      Ar  645   3.222   2.575   3.184  0.0199 -0.0680 -0.0873
+  646Ar      Ar  646   3.511   3.043   2.234  0.0134  0.0167 -0.0571
+  647Ar      Ar  647   2.660   0.684   0.701 -0.2185  0.0740 -0.0130
+  648Ar      Ar  648   1.466   1.662   1.680 -0.1893  0.1402  0.0501
+  649Ar      Ar  649   3.433   2.835   1.574 -0.1839  0.2828 -0.1695
+  650Ar      Ar  650   2.848   1.663   1.755 -0.0573  0.0121 -0.0901
+  651Ar      Ar  651   3.584   1.546   0.703  0.1196  0.0961  0.0898
+  652Ar      Ar  652   0.846   3.325   1.137 -0.0432  0.0220 -0.0348
+  653Ar      Ar  653   2.042   2.592   1.797 -0.0030  0.0316 -0.0313
+  654Ar      Ar  654   1.844   2.611   0.366  0.1615  0.1542  0.1215
+  655Ar      Ar  655   1.294   1.869   3.583 -0.0113  0.0613 -0.1065
+  656Ar      Ar  656   2.529   2.312   3.145  0.2356  0.0740  0.0891
+  657Ar      Ar  657   1.669   2.996   0.663 -0.0147 -0.1557 -0.1780
+  658Ar      Ar  658   0.500   2.709   0.727 -0.0489 -0.0229  0.0449
+  659Ar      Ar  659   3.037   0.677   1.182 -0.0584 -0.0201 -0.2194
+  660Ar      Ar  660   1.423   2.198   1.118  0.0848  0.2867 -0.0636
+  661Ar      Ar  661   2.203   0.151   3.185  0.0441  0.2516 -0.1221
+  662Ar      Ar  662   1.888   2.394   1.332  0.2863  0.0082 -0.1500
+  663Ar      Ar  663   2.579   0.104   2.633  0.0670 -0.0539  0.0885
+  664Ar      Ar  664   2.493   1.792   3.090 -0.2665  0.0171  0.0467
+  665Ar      Ar  665   3.508   2.044   0.864  0.0669 -0.0190 -0.0438
+  666Ar      Ar  666   0.917   2.140   1.543  0.2573  0.1606  0.1386
+  667Ar      Ar  667   2.442   3.433   0.045  0.1639  0.0095 -0.0220
+  668Ar      Ar  668   0.012   0.722   1.454 -0.1877 -0.1736 -0.0296
+  669Ar      Ar  669   1.443   0.577   0.767 -0.0470  0.0826 -0.0270
+  670Ar      Ar  670   0.812   1.479   1.928  0.2031  0.0795 -0.0951
+  671Ar      Ar  671   0.872   2.664   3.102 -0.1044 -0.0170 -0.0768
+  672Ar      Ar  672   3.597   0.481   3.022  0.0686 -0.0144  0.0509
+  673Ar      Ar  673   1.443   0.429   1.439  0.0479 -0.2103  0.1107
+  674Ar      Ar  674   2.927   2.641   0.263  0.1717  0.0014  0.0729
+  675Ar      Ar  675   1.645   1.416   2.272 -0.0892  0.1488  0.0410
+  676Ar      Ar  676   3.461   1.296   0.344  0.0322 -0.0499 -0.0176
+  677Ar      Ar  677   1.372   1.102   1.692  0.1599  0.0004  0.1529
+  678Ar      Ar  678   0.825   0.872   3.133 -0.1431  0.1004  0.2073
+  679Ar      Ar  679   3.088   1.470   2.285 -0.0760 -0.0782 -0.1570
+  680Ar      Ar  680   2.387   1.273   2.460 -0.0343 -0.0914 -0.0810
+  681Ar      Ar  681   1.125   0.607   2.029 -0.2184 -0.0358 -0.0939
+  682Ar      Ar  682   0.985   1.052   3.487 -0.2875  0.2324  0.3403
+  683Ar      Ar  683   1.798   1.635   1.949 -0.0025 -0.0721  0.1680
+  684Ar      Ar  684   0.454   2.671   2.470 -0.0466  0.1402 -0.2673
+  685Ar      Ar  685   2.567   3.095   1.714  0.0738  0.0393  0.1257
+  686Ar      Ar  686   1.528   0.706   2.753 -0.0127  0.2323  0.0418
+  687Ar      Ar  687   2.145   1.710   2.051 -0.0522  0.0267 -0.0340
+  688Ar      Ar  688   0.359   1.809   1.184  0.1152  0.0233  0.1658
+  689Ar      Ar  689   2.280   0.171   0.611 -0.1221  0.0868  0.0610
+  690Ar      Ar  690   1.078   1.869   3.247  0.0240  0.2377 -0.1127
+  691Ar      Ar  691   0.603   2.423   0.549  0.2792  0.0534  0.0695
+  692Ar      Ar  692   1.694   0.509   0.217  0.1352 -0.2623  0.1616
+  693Ar      Ar  693   1.675   3.404   2.817  0.1623  0.0085  0.0299
+  694Ar      Ar  694   1.820   2.609   3.067 -0.1803  0.1291  0.0791
+  695Ar      Ar  695   2.005   2.124   3.110 -0.1009  0.0273  0.1805
+  696Ar      Ar  696   0.923   1.468   0.469  0.0832  0.0645 -0.0540
+  697Ar      Ar  697   3.488   0.669   1.916 -0.1345  0.0686 -0.0945
+  698Ar      Ar  698   0.211   1.653   2.582  0.0004 -0.4432  0.0847
+  699Ar      Ar  699   2.390   2.651   3.456 -0.0265  0.0211  0.0043
+  700Ar      Ar  700   2.014   1.987   0.509 -0.1825 -0.0281  0.0094
+  701Ar      Ar  701   0.669   1.829   0.911 -0.1312 -0.0636 -0.1507
+  702Ar      Ar  702   1.443   0.686   3.123  0.1244 -0.1007 -0.1764
+  703Ar      Ar  703   0.163   3.025   2.534 -0.0478 -0.1823 -0.0557
+  704Ar      Ar  704   2.887   0.922   1.393 -0.0491 -0.0172  0.1073
+  705Ar      Ar  705   0.138   0.544   1.081 -0.3099  0.0230  0.2378
+  706Ar      Ar  706   1.000   0.400   3.289 -0.0785  0.0903 -0.1274
+  707Ar      Ar  707   2.826   3.373   0.158  0.0527 -0.2582  0.2373
+  708Ar      Ar  708   3.030   0.571   2.521  0.1443  0.0477 -0.3136
+  709Ar      Ar  709   1.941   0.059   0.700  0.0947 -0.2041  0.0179
+  710Ar      Ar  710   2.700   2.877   2.776  0.1666 -0.0975 -0.0368
+  711Ar      Ar  711   1.973   0.407   2.839 -0.0854 -0.2271  0.0517
+  712Ar      Ar  712   0.043   1.964   2.451  0.0333 -0.0771 -0.0447
+  713Ar      Ar  713   0.835   3.494   1.925  0.0391 -0.0209 -0.1187
+  714Ar      Ar  714   3.484   0.001   0.037  0.0768  0.0301  0.0948
+  715Ar      Ar  715   0.507   0.473   2.973  0.1602 -0.2241 -0.0980
+  716Ar      Ar  716   1.320   0.083   2.183  0.0009 -0.0288 -0.0491
+  717Ar      Ar  717   1.766   2.833   2.642  0.1365  0.0381 -0.0312
+  718Ar      Ar  718   3.560   0.058   0.676  0.1450 -0.0299  0.0435
+  719Ar      Ar  719   2.910   2.029   1.763 -0.0247  0.1639  0.0327
+  720Ar      Ar  720   3.126   2.051   3.036  0.2536  0.0612 -0.1075
+  721Ar      Ar  721   0.876   2.763   1.698  0.0185 -0.1434  0.0885
+  722Ar      Ar  722   0.486   3.542   3.390  0.2124 -0.2424  0.0593
+  723Ar      Ar  723   1.518   3.169   0.130 -0.2206 -0.1783  0.0166
+  724Ar      Ar  724   2.922   1.322   3.203 -0.1138  0.0318 -0.0199
+  725Ar      Ar  725   2.326   1.453   0.644  0.1870 -0.1521 -0.0309
+  726Ar      Ar  726   3.308   0.265   1.590 -0.0806  0.2886  0.1267
+  727Ar      Ar  727   0.115   3.503   0.388  0.0155 -0.0222  0.0701
+  728Ar      Ar  728   0.385   1.033   3.122  0.1341 -0.0390  0.1013
+  729Ar      Ar  729   1.026   0.772   0.237  0.1558  0.0757 -0.1017
+  730Ar      Ar  730   1.016   1.290   1.175  0.2277 -0.0451 -0.0558
+  731Ar      Ar  731   2.700   1.540   2.485  0.0721 -0.0252 -0.1655
+  732Ar      Ar  732   1.209   1.234   0.142  0.0383 -0.1166 -0.0937
+  733Ar      Ar  733   0.054   2.384   3.361 -0.0144  0.0820  0.0803
+  734Ar      Ar  734   1.787   0.843   1.852 -0.1052 -0.0870 -0.1266
+  735Ar      Ar  735   2.228   1.660   0.340  0.1038  0.0508 -0.1754
+  736Ar      Ar  736   2.642   0.474   2.842 -0.0507 -0.1618 -0.0339
+  737Ar      Ar  737   2.336   0.769   0.527 -0.0833  0.0974  0.3106
+  738Ar      Ar  738   0.066   0.514   0.649  0.2649  0.0307  0.0826
+  739Ar      Ar  739   0.297   2.889   2.189 -0.0667  0.2711  0.0953
+  740Ar      Ar  740   2.666   0.949   3.569  0.0963 -0.1446  0.0308
+  741Ar      Ar  741   0.448   2.274   2.004 -0.0450  0.0805  0.0471
+  742Ar      Ar  742   2.172   1.423   2.965  0.2406 -0.0873  0.0577
+  743Ar      Ar  743   2.419   2.904   0.478 -0.0887 -0.1441 -0.1593
+  744Ar      Ar  744   1.150   1.110   0.487  0.0608  0.0299 -0.1486
+  745Ar      Ar  745   2.173   2.567   2.868  0.0379  0.1183  0.0148
+  746Ar      Ar  746   0.728   2.414   2.414  0.2106 -0.0879  0.1057
+  747Ar      Ar  747   1.410   0.675   0.424 -0.1917  0.0668 -0.0058
+  748Ar      Ar  748   1.937   3.538   3.333  0.1677 -0.0576 -0.1633
+  749Ar      Ar  749   2.371   3.423   1.768 -0.0397 -0.0430 -0.1024
+  750Ar      Ar  750   3.455   1.636   1.694  0.1925 -0.0623  0.0364
+  751Ar      Ar  751   2.845   1.384   1.046  0.1874 -0.1748 -0.0745
+  752Ar      Ar  752   2.665   0.528   1.462  0.1027 -0.0843 -0.2493
+  753Ar      Ar  753   2.392   2.309   1.079 -0.2415  0.0982 -0.2251
+  754Ar      Ar  754   0.934   1.303   2.203  0.0364  0.1571 -0.0882
+  755Ar      Ar  755   2.095   3.459   2.080 -0.0565  0.0076 -0.0487
+  756Ar      Ar  756   2.380   0.684   0.116  0.0536  0.0077 -0.0118
+  757Ar      Ar  757   1.612   1.955   1.780  0.0205  0.0723  0.0452
+  758Ar      Ar  758   1.396   1.999   3.277 -0.0848  0.0490  0.2282
+  759Ar      Ar  759   1.255   2.055   1.426 -0.0989  0.0984 -0.0388
+  760Ar      Ar  760   3.370   2.459   1.664 -0.0626 -0.0921 -0.0859
+  761Ar      Ar  761   2.322   2.219   0.749 -0.0865 -0.0201  0.0141
+  762Ar      Ar  762   2.591   0.872   2.423 -0.0701 -0.1054  0.1563
+  763Ar      Ar  763   3.061   3.080   0.885  0.3224 -0.0572 -0.0539
+  764Ar      Ar  764   0.447   3.033   1.279  0.2217  0.0170  0.1190
+  765Ar      Ar  765   1.999   1.753   1.202 -0.0119  0.1600  0.0883
+  766Ar      Ar  766   0.228   0.739   2.935 -0.1217  0.0091  0.0341
+  767Ar      Ar  767   1.239   0.570   1.663  0.1169 -0.1221 -0.1999
+  768Ar      Ar  768   0.451   1.895   3.007 -0.0261  0.0741  0.0269
+  769Ar      Ar  769   2.495   3.014   0.152 -0.2599  0.0369  0.0230
+  770Ar      Ar  770   2.897   2.723   3.050  0.0376 -0.0431 -0.1169
+  771Ar      Ar  771   2.794   1.684   2.111 -0.1414  0.0134 -0.0443
+  772Ar      Ar  772   0.202   1.972   1.798  0.1597  0.2737  0.0910
+  773Ar      Ar  773   2.605   0.113   0.281  0.1603 -0.0259 -0.0198
+  774Ar      Ar  774   3.048   3.214   3.355  0.0411 -0.0506 -0.0405
+  775Ar      Ar  775   0.443   1.708   3.336 -0.3019 -0.2298  0.0498
+  776Ar      Ar  776   0.321   2.006   2.650  0.2818  0.0177 -0.0736
+  777Ar      Ar  777   1.741   1.191   3.142 -0.0525  0.0569  0.1212
+  778Ar      Ar  778   3.441   1.128   2.809  0.0248 -0.0942 -0.0383
+  779Ar      Ar  779   3.258   1.278   3.251  0.0473 -0.1164 -0.2941
+  780Ar      Ar  780   1.713   2.432   0.652  0.0937 -0.0145  0.0333
+  781Ar      Ar  781   1.640   2.449   2.461 -0.0582  0.1409 -0.1209
+  782Ar      Ar  782   1.151   0.965   3.141  0.2643  0.2678  0.1476
+  783Ar      Ar  783   3.579   2.658   2.027 -0.2182  0.0284  0.0067
+  784Ar      Ar  784   2.929   1.968   0.356 -0.1160  0.3577 -0.1998
+  785Ar      Ar  785   1.903   1.085   3.427 -0.0686  0.0971 -0.2750
+  786Ar      Ar  786   1.239   0.596   3.526 -0.2328  0.0274 -0.0942
+  787Ar      Ar  787   1.732   3.271   3.188 -0.0423  0.0502 -0.2027
+  788Ar      Ar  788   0.674   3.035   2.202  0.2784 -0.0229  0.1633
+  789Ar      Ar  789   2.859   1.760   0.982 -0.0928 -0.1196 -0.0399
+  790Ar      Ar  790   0.600   0.229   3.557  0.0998  0.2016  0.0330
+  791Ar      Ar  791   2.697   2.150   2.779  0.0645 -0.0833  0.2347
+  792Ar      Ar  792   0.345   1.698   0.585  0.0797  0.0048 -0.0809
+  793Ar      Ar  793   3.323   0.028   1.040  0.3623  0.1316 -0.1222
+  794Ar      Ar  794   2.190   1.806   1.706 -0.2985 -0.1337  0.0681
+  795Ar      Ar  795   0.531   2.104   1.544  0.0777  0.1744 -0.0492
+  796Ar      Ar  796   0.777   0.174   1.337  0.2786 -0.1099  0.0906
+  797Ar      Ar  797   3.323   1.970   0.418  0.1687 -0.1381  0.0420
+  798Ar      Ar  798   0.927   2.513   1.416 -0.0535  0.0611  0.0928
+  799Ar      Ar  799   0.031   2.991   3.543  0.0071 -0.0465 -0.1290
+  800Ar      Ar  800   2.519   2.309   2.312 -0.1104  0.1676 -0.0531
+  801Ar      Ar  801   3.066   3.357   2.343  0.0668 -0.1394  0.2132
+  802Ar      Ar  802   1.697   3.195   2.449  0.0106  0.0155 -0.0956
+  803Ar      Ar  803   2.944   1.532   0.248  0.2030 -0.0591  0.0130
+  804Ar      Ar  804   1.989   0.876   1.119 -0.0686  0.0780 -0.1686
+  805Ar      Ar  805   2.701   2.388   1.709  0.0173  0.1231 -0.2141
+  806Ar      Ar  806   1.200   2.240   0.369 -0.2160  0.2088  0.0148
+  807Ar      Ar  807   1.718   2.954   2.992  0.0387 -0.0857  0.0429
+  808Ar      Ar  808   0.063   2.198   1.533  0.0975  0.0638  0.0183
+  809Ar      Ar  809   1.941   2.333   0.197 -0.0460  0.2126  0.1782
+  810Ar      Ar  810   2.831   1.673   3.226  0.1720 -0.1335 -0.2853
+  811Ar      Ar  811   2.537   1.710   1.599  0.0177  0.1837  0.0299
+  812Ar      Ar  812   0.047   3.465   1.161  0.0870 -0.2289 -0.0610
+  813Ar      Ar  813   2.119   1.097   1.799 -0.0791 -0.0617 -0.0202
+  814Ar      Ar  814   0.181   0.902   1.751  0.0289  0.0919 -0.1373
+  815Ar      Ar  815   0.866   0.936   1.156 -0.0595  0.0652  0.1168
+  816Ar      Ar  816   0.573   1.877   0.058 -0.0302 -0.0161 -0.0848
+  817Ar      Ar  817   3.109   1.539   3.012  0.1739 -0.1206  0.0569
+  818Ar      Ar  818   0.142   1.587   1.936 -0.0124 -0.1229 -0.1850
+  819Ar      Ar  819   2.082   3.534   0.065 -0.1228 -0.0165  0.0199
+  820Ar      Ar  820   1.565   0.923   0.652  0.1072  0.2171 -0.2033
+  821Ar      Ar  821   3.105   2.219   0.189 -0.0139 -0.1009 -0.0576
+  822Ar      Ar  822   1.016   0.440   1.386  0.1078 -0.1007  0.0299
+  823Ar      Ar  823   2.306   2.064   2.158 -0.1384 -0.2523 -0.1334
+  824Ar      Ar  824   1.490   3.338   0.589 -0.1444 -0.1560  0.1173
+  825Ar      Ar  825   1.888   1.592   2.501 -0.0256 -0.2288  0.1107
+  826Ar      Ar  826   0.308   0.869   2.564 -0.0568 -0.1425 -0.1116
+  827Ar      Ar  827   0.146   1.763   1.474  0.0186  0.3166  0.0213
+  828Ar      Ar  828   0.174   3.214   2.852  0.1521  0.0166 -0.1067
+  829Ar      Ar  829   3.281   2.275   0.921  0.0654  0.1389  0.1243
+  830Ar      Ar  830   1.865   0.105   2.593 -0.0323 -0.1527 -0.0221
+  831Ar      Ar  831   3.259   3.505   3.371  0.1781 -0.0405  0.0406
+  832Ar      Ar  832   0.461   0.075   2.401  0.0200  0.0899  0.1358
+  833Ar      Ar  833   0.945   3.214   1.762 -0.1516 -0.0557  0.0422
+  834Ar      Ar  834   2.059   2.823   2.040 -0.0092 -0.2008  0.1225
+  835Ar      Ar  835   1.980   0.282   0.430 -0.2693 -0.0301 -0.0579
+  836Ar      Ar  836   1.394   3.283   3.013  0.0606  0.0065  0.2711
+  837Ar      Ar  837   1.671   2.047   3.081  0.0043 -0.1239  0.0086
+  838Ar      Ar  838   2.201   0.565   2.391 -0.0663  0.1979 -0.1961
+  839Ar      Ar  839   3.105   0.734   1.992 -0.2483 -0.2912  0.1674
+  840Ar      Ar  840   0.531   1.119   3.453 -0.0818  0.2591 -0.0915
+  841Ar      Ar  841   0.827   3.241   0.756  0.0886  0.1346  0.2305
+  842Ar      Ar  842   1.475   2.383   1.843  0.0963  0.0668 -0.0522
+  843Ar      Ar  843   1.847   1.664   1.578 -0.1474 -0.0738 -0.0866
+  844Ar      Ar  844   2.996   0.670   1.664 -0.0957 -0.0881  0.1143
+  845Ar      Ar  845   2.074   1.923   2.377 -0.1388 -0.2024  0.0571
+  846Ar      Ar  846   2.455   1.649   2.255  0.0829  0.2076  0.0242
+  847Ar      Ar  847   0.258   2.057   2.164 -0.1990  0.1343  0.2369
+  848Ar      Ar  848   0.263   1.982   0.251  0.2910  0.2057  0.1263
+  849Ar      Ar  849   2.590   3.249   2.734 -0.1575 -0.1080  0.1619
+  850Ar      Ar  850   0.693   1.739   0.439 -0.0423  0.0135  0.0215
+  851Ar      Ar  851   1.155   2.679   0.765  0.0279  0.0073 -0.1689
+  852Ar      Ar  852   0.381   0.610   0.535 -0.0664  0.2055  0.2027
+  853Ar      Ar  853   3.154   1.432   1.670  0.1585  0.1378 -0.0138
+  854Ar      Ar  854   0.883   3.218   2.498 -0.0819 -0.2476 -0.0873
+  855Ar      Ar  855   0.101   0.302   3.593 -0.0020  0.0667  0.0483
+  856Ar      Ar  856   2.428   2.043   3.341 -0.0770  0.2752 -0.1638
+  857Ar      Ar  857   0.564   1.016   0.848 -0.1316 -0.2929  0.1077
+  858Ar      Ar  858   1.873   1.296   1.953  0.0458  0.1664  0.0174
+  859Ar      Ar  859   3.524   3.352   2.692 -0.1428 -0.0917  0.0888
+  860Ar      Ar  860   1.989   2.568   3.550  0.1570 -0.0653 -0.1422
+  861Ar      Ar  861   1.342   1.623   3.335 -0.0108 -0.0682  0.0662
+  862Ar      Ar  862   0.855   1.529   0.844 -0.0571  0.1151 -0.0785
+  863Ar      Ar  863   1.287   1.004   2.639  0.0348  0.0372 -0.0253
+  864Ar      Ar  864   1.441   1.788   2.621 -0.1741 -0.0396 -0.1272
+  865Ar      Ar  865   0.839   0.180   1.669 -0.1742  0.0605  0.0312
+  866Ar      Ar  866   3.238   2.559   1.342 -0.1047  0.0066 -0.0494
+  867Ar      Ar  867   2.827   0.325   0.664  0.0056  0.0488  0.2385
+  868Ar      Ar  868   1.806   2.320   1.717  0.0139 -0.1035 -0.0130
+  869Ar      Ar  869   3.348   0.594   0.544 -0.0835  0.1935  0.2645
+  870Ar      Ar  870   0.177   3.442   0.827  0.0238 -0.1628  0.1732
+  871Ar      Ar  871   3.340   3.369   1.417 -0.0278  0.2277  0.0927
+  872Ar      Ar  872   2.781   1.986   3.106  0.0405  0.1595  0.1204
+  873Ar      Ar  873   0.389   1.538   0.882  0.0376  0.0851 -0.1626
+  874Ar      Ar  874   2.464   1.102   0.666  0.1779 -0.2532  0.0276
+  875Ar      Ar  875   0.843   2.684   0.898  0.1000 -0.0310 -0.1166
+  876Ar      Ar  876   1.664   3.334   1.358 -0.0734 -0.1330 -0.0306
+  877Ar      Ar  877   1.086   3.460   2.363 -0.0890 -0.1151 -0.0279
+  878Ar      Ar  878   2.885   2.620   2.018  0.0427  0.0653  0.2501
+  879Ar      Ar  879   0.292   2.749   0.389  0.0575 -0.0638 -0.0515
+  880Ar      Ar  880   0.330   0.540   1.475  0.0908 -0.0636  0.1163
+  881Ar      Ar  881   2.507   0.362   1.930 -0.0976 -0.0232  0.1077
+  882Ar      Ar  882   2.442   1.363   0.228  0.0103 -0.1116  0.1329
+  883Ar      Ar  883   3.335   2.014   1.687 -0.1978 -0.0767 -0.0147
+  884Ar      Ar  884   2.025   0.149   2.263  0.0706  0.0331 -0.0696
+  885Ar      Ar  885   1.584   2.182   2.095 -0.1102 -0.0582  0.0355
+  886Ar      Ar  886   1.069   2.428   2.248 -0.2269 -0.2211  0.0461
+  887Ar      Ar  887   0.988   3.582   0.661  0.0222 -0.0030  0.1042
+  888Ar      Ar  888   1.408   2.097   2.395  0.1341 -0.0047  0.1524
+  889Ar      Ar  889   1.072   2.989   3.254 -0.1444 -0.1909 -0.1712
+  890Ar      Ar  890   0.460   1.831   2.343  0.1122  0.0794  0.0356
+  891Ar      Ar  891   1.814   0.077   2.999  0.0498 -0.0945  0.0906
+  892Ar      Ar  892   0.907   1.782   2.078 -0.0254  0.0228  0.2131
+  893Ar      Ar  893   1.608   0.420   2.947 -0.0740  0.1309 -0.0202
+  894Ar      Ar  894   2.250   1.617   1.399 -0.1514 -0.0309  0.1226
+  895Ar      Ar  895   2.206   0.212   1.946 -0.0468  0.0011  0.0708
+  896Ar      Ar  896   2.896   0.558   2.219 -0.1738 -0.2578 -0.0413
+  897Ar      Ar  897   1.831   2.320   2.774  0.0504 -0.0520 -0.1524
+  898Ar      Ar  898   0.243   3.279   2.226  0.3652 -0.1958 -0.0441
+  899Ar      Ar  899   2.372   2.406   0.450 -0.0636 -0.2489  0.0310
+  900Ar      Ar  900   1.987   3.426   2.485  0.2245  0.0258  0.1257
+  901Ar      Ar  901   2.339   2.960   2.815  0.0266 -0.1109  0.0191
+  902Ar      Ar  902   2.596   3.405   1.417 -0.1282  0.2529  0.0271
+  903Ar      Ar  903   2.282   1.026   3.559 -0.1618  0.0650  0.0407
+  904Ar      Ar  904   3.368   1.482   2.711  0.0394  0.0865  0.0851
+  905Ar      Ar  905   2.890   0.049   0.428 -0.0026 -0.1072 -0.1976
+  906Ar      Ar  906   0.024   0.220   2.465  0.1918  0.0037 -0.1600
+  907Ar      Ar  907   0.720   1.978   3.277 -0.0997  0.1647  0.2716
+  908Ar      Ar  908   2.319   0.011   2.438  0.0673  0.0095 -0.2011
+  909Ar      Ar  909   2.408   0.605   1.736 -0.2710 -0.1761  0.2534
+  910Ar      Ar  910   1.168   0.182   2.624  0.3105  0.0193 -0.0781
+  911Ar      Ar  911   0.834   1.573   3.340  0.2756 -0.0165  0.0993
+  912Ar      Ar  912   2.919   0.937   3.107 -0.0321  0.0495  0.2746
+  913Ar      Ar  913   1.792   1.305   0.775  0.0082  0.1625 -0.0015
+  914Ar      Ar  914   1.515   3.060   1.542 -0.0699  0.0811 -0.1750
+  915Ar      Ar  915   3.177   1.775   1.942 -0.0734  0.0930  0.0171
+  916Ar      Ar  916   3.019   0.121   1.664  0.1066  0.0971  0.0822
+  917Ar      Ar  917   0.077   0.019   2.150  0.2099  0.2032  0.1642
+  918Ar      Ar  918   0.767   1.165   1.395  0.0895  0.1506  0.0876
+  919Ar      Ar  919   2.243   3.354   2.712 -0.1004 -0.1129  0.1499
+  920Ar      Ar  920   2.559   3.339   2.381 -0.0349  0.0354 -0.1090
+  921Ar      Ar  921   1.775   1.906   2.755  0.3587  0.2035  0.0629
+  922Ar      Ar  922   2.129   1.220   0.872  0.1049  0.1150 -0.0933
+  923Ar      Ar  923   3.076   2.411   2.887  0.0252 -0.0336  0.1440
+  924Ar      Ar  924   2.406   0.926   1.085  0.2435 -0.0028 -0.0541
+  925Ar      Ar  925   1.288   0.559   2.499 -0.1094  0.1740 -0.1154
+  926Ar      Ar  926   3.080   2.153   2.620 -0.0878  0.0197 -0.0440
+  927Ar      Ar  927   1.132   1.587   2.779  0.0770  0.0714  0.0493
+  928Ar      Ar  928   1.169   3.251   0.867  0.1902 -0.2108 -0.0228
+  929Ar      Ar  929   2.924   0.568   3.064 -0.0441 -0.0432 -0.0343
+  930Ar      Ar  930   1.698   1.669   1.078 -0.0684 -0.0184  0.3767
+  931Ar      Ar  931   1.254   2.103   2.938  0.1866  0.1472  0.1750
+  932Ar      Ar  932   2.011   2.357   0.531  0.0918  0.1544 -0.0243
+  933Ar      Ar  933   1.513   0.310   2.582 -0.2276 -0.0627  0.0143
+  934Ar      Ar  934   3.535   0.256   1.324 -0.0992  0.0496 -0.1173
+  935Ar      Ar  935   0.225   3.407   2.543 -0.1332 -0.0788  0.1146
+  936Ar      Ar  936   2.227   2.560   2.182 -0.0773  0.1351  0.0045
+  937Ar      Ar  937   1.415   2.904   1.149 -0.2336 -0.0292  0.1856
+  938Ar      Ar  938   0.546   3.365   0.967  0.0819 -0.0900  0.2513
+  939Ar      Ar  939   1.062   3.356   2.772 -0.0442 -0.1050 -0.1269
+  940Ar      Ar  940   1.376   1.881   2.106  0.1651 -0.0585 -0.1793
+  941Ar      Ar  941   3.101   1.415   3.556 -0.0415  0.0639 -0.0785
+  942Ar      Ar  942   0.222   2.397   0.500  0.0099  0.1678  0.0016
+  943Ar      Ar  943   2.774   1.011   2.097 -0.1225 -0.2291  0.3009
+  944Ar      Ar  944   2.492   1.065   1.869  0.0742 -0.2001 -0.0601
+  945Ar      Ar  945   3.557   2.841   1.082 -0.0945 -0.0802  0.0415
+  946Ar      Ar  946   2.659   2.892   1.953  0.1850  0.0840 -0.1018
+  947Ar      Ar  947   3.535   1.843   2.081 -0.2481  0.0224  0.1288
+  948Ar      Ar  948   3.569   2.190   0.291 -0.1002 -0.1067 -0.0250
+  949Ar      Ar  949   2.497   3.339   0.370 -0.0871  0.1613  0.0124
+  950Ar      Ar  950   3.335   2.645   2.781  0.1550 -0.1033 -0.2126
+  951Ar      Ar  951   0.506   3.136   2.972 -0.1707 -0.1265  0.0206
+  952Ar      Ar  952   2.962   1.013   1.800  0.0171 -0.0151 -0.0738
+  953Ar      Ar  953   0.445   2.305   0.245  0.0269  0.0367 -0.1791
+  954Ar      Ar  954   2.093   2.450   2.528 -0.0808  0.1310 -0.1833
+  955Ar      Ar  955   2.885   3.183   2.910  0.0192  0.0379 -0.1354
+  956Ar      Ar  956   2.143   2.272   1.557 -0.0469 -0.1383 -0.2807
+  957Ar      Ar  957   0.704   0.869   0.099 -0.1193  0.0273  0.1713
+  958Ar      Ar  958   2.012   2.727   1.468 -0.0432  0.1873  0.0799
+  959Ar      Ar  959   2.786   0.067   3.560  0.1067 -0.0452  0.0246
+  960Ar      Ar  960   0.066   2.329   2.510 -0.0169  0.1120 -0.0443
+  961Ar      Ar  961   0.057   2.037   1.203 -0.2210 -0.0818  0.0338
+  962Ar      Ar  962   0.318   0.268   0.686 -0.0461 -0.0091  0.0276
+  963Ar      Ar  963   0.406   0.714   0.877  0.2708  0.0424 -0.1496
+  964Ar      Ar  964   3.042   0.869   2.334  0.0868  0.0420 -0.0434
+  965Ar      Ar  965   0.440   3.189   1.980 -0.1903  0.1621 -0.1403
+  966Ar      Ar  966   2.160   3.223   0.245 -0.1355  0.0418  0.0887
+  967Ar      Ar  967   2.743   2.692   3.432  0.1654  0.3231  0.2564
+  968Ar      Ar  968   2.650   0.499   2.490  0.2273 -0.1392 -0.0771
+  969Ar      Ar  969   0.855   0.210   2.848  0.2084  0.1355  0.0304
+  970Ar      Ar  970   3.077   1.644   2.599 -0.1384 -0.0522 -0.0165
+  971Ar      Ar  971   3.324   1.840   2.817 -0.0744  0.0274  0.2168
+  972Ar      Ar  972   2.743   3.406   1.744 -0.1646  0.0524  0.0800
+  973Ar      Ar  973   2.098   0.287   1.354  0.0942 -0.1433  0.0429
+  974Ar      Ar  974   1.921   2.004   1.933 -0.3214  0.2349 -0.1584
+  975Ar      Ar  975   3.228   0.392   1.908  0.0867 -0.0068 -0.1045
+  976Ar      Ar  976   0.809   2.325   1.853  0.0465 -0.1623  0.1427
+  977Ar      Ar  977   0.535   3.214   2.651 -0.0767  0.0672 -0.0398
+  978Ar      Ar  978   3.072   1.104   1.078  0.0432  0.0414 -0.0064
+  979Ar      Ar  979   2.570   0.740   3.071 -0.0362 -0.0583  0.0512
+  980Ar      Ar  980   0.295   3.183   1.605  0.0903 -0.0588 -0.0969
+  981Ar      Ar  981   0.600   3.190   0.112 -0.0803 -0.3942  0.1161
+  982Ar      Ar  982   2.077   0.554   0.280 -0.0671 -0.0836 -0.0964
+  983Ar      Ar  983   3.136   0.016   1.999  0.1374  0.2705  0.1887
+  984Ar      Ar  984   0.261   1.509   2.247 -0.0157  0.1470 -0.0259
+  985Ar      Ar  985   0.677   0.155   0.362 -0.0285  0.2076  0.2265
+  986Ar      Ar  986   2.849   0.383   1.837  0.1665  0.0514  0.1716
+  987Ar      Ar  987   3.330   3.561   0.383 -0.1613 -0.0490 -0.1444
+  988Ar      Ar  988   0.131   0.180   0.975  0.0276 -0.0438  0.2744
+  989Ar      Ar  989   2.141   2.420   3.289 -0.0360 -0.0723  0.0098
+  990Ar      Ar  990   2.608   0.001   0.604  0.0625 -0.1102 -0.0081
+  991Ar      Ar  991   0.431   2.705   3.053 -0.1358 -0.1841  0.0014
+  992Ar      Ar  992   2.401   2.656   0.888 -0.0522  0.1965 -0.0737
+  993Ar      Ar  993   0.487   0.285   1.927 -0.2174 -0.1236 -0.0086
+  994Ar      Ar  994   0.112   0.953   2.103  0.1536  0.1321 -0.2835
+  995Ar      Ar  995   3.322   0.969   3.393 -0.1755 -0.2638 -0.1121
+  996Ar      Ar  996   1.184   2.194   1.950 -0.2495 -0.1934  0.1135
+  997Ar      Ar  997   2.001   1.966   0.169 -0.1451 -0.0308  0.0329
+  998Ar      Ar  998   3.295   2.890   1.989  0.2049  0.0138 -0.1468
+  999Ar      Ar  999   0.230   0.926   1.062 -0.0757  0.1106 -0.0219
+ 1000Ar      Ar 1000   3.071   2.495   3.525 -0.2655  0.1601  0.1471
+   3.60140   3.60140   3.60140
diff --git a/tests/physicalvalidation/systems/ens_argon_md_verlet_pme_vr_cr/input/system.mdp b/tests/physicalvalidation/systems/ens_argon_md_verlet_pme_vr_cr/input/system.mdp
new file mode 100644 (file)
index 0000000..95845e5
--- /dev/null
@@ -0,0 +1,11 @@
+nsteps                   = 500000
+dt                       = 0.002
+cutoff-scheme            = verlet
+tcoupl                   = v-rescale
+tc-grps                  = system
+ref-t                    = 87.0
+tau-t                    = 0.1
+pcoupl                   = C-rescale
+ref-p                    = 1.0
+compressibility          = 4.5e-5
+tau-p                    = 0.5
diff --git a/tests/physicalvalidation/systems/ens_argon_md_verlet_pme_vr_cr/input/system.top b/tests/physicalvalidation/systems/ens_argon_md_verlet_pme_vr_cr/input/system.top
new file mode 100644 (file)
index 0000000..c50802e
--- /dev/null
@@ -0,0 +1,31 @@
+; Argon force field and topology
+;
+; Lennard-Jones parameters for liquid argon taken from
+; White, J.A., Lennard-Jones as a model for argon and test of extended
+; renormalization group calculations. J. Chem. Phys. 111, 9352 (1999)
+;
+
+[ defaults ]
+; nbfunc comb-rule
+  1      3
+
+[ atomtypes ]
+; full atom descriptions are available in ffoplsaa.atp
+; name  bond_type    mass    charge   ptype     sigma      epsilon
+  Ar    Ar           39.948  0.0      A         0.3345     1.045128
+
+[ moleculetype ]
+Argon    1
+
+[ atoms ]
+;   nr   type  resnr residue  atom   cgnr     charge
+     1     Ar      1      Ar    Ar      1     0
+
+
+[ system ]
+; Name
+Argon
+
+[ molecules ]
+; Compound        #mols
+Argon             1000
diff --git a/tests/physicalvalidation/systems/ens_water_md_verlet_settle_pme_vr_cr/input/system.gro b/tests/physicalvalidation/systems/ens_water_md_verlet_settle_pme_vr_cr/input/system.gro
new file mode 100644 (file)
index 0000000..dc7d266
--- /dev/null
@@ -0,0 +1,2703 @@
+Pure Water
+ 2700
+    1SOL     OW    1   0.743   2.422   2.251 -0.1702  0.0546 -0.4651
+    1SOL    HW1    2   0.729   2.486   2.320 -0.7931 -1.2746  0.6459
+    1SOL    HW2    3   0.660   2.375   2.245  0.3543 -0.7902 -1.1351
+    2SOL     OW    4   0.944   1.432   1.518 -0.1151 -0.2173 -0.0013
+    2SOL    HW1    5   1.021   1.486   1.500 -0.4633 -0.2047 -1.4831
+    2SOL    HW2    6   0.876   1.468   1.460 -1.2969 -0.0451  1.5099
+    3SOL     OW    7   2.557   2.116   1.388 -0.4271 -0.4555  0.1402
+    3SOL    HW1    8   2.645   2.146   1.412 -0.5707 -1.1620  1.5548
+    3SOL    HW2    9   2.500   2.155   1.454 -1.1379 -0.2528 -0.5970
+    4SOL     OW   10   1.045   2.513   0.247  0.3999  0.6831 -0.5058
+    4SOL    HW1   11   1.018   2.423   0.229  2.4605  0.0747 -0.5077
+    4SOL    HW2   12   1.067   2.549   0.162  0.2786  0.9714 -0.4146
+    5SOL     OW   13   0.529   1.973   0.563  0.1724  0.2018 -0.6887
+    5SOL    HW1   14   0.559   1.882   0.555 -1.0851 -0.3145  0.4691
+    5SOL    HW2   15   0.587   2.010   0.630  1.8157  0.7353 -2.4076
+    6SOL     OW   16   0.605   1.617   1.625 -0.1547  0.5774 -0.0528
+    6SOL    HW1   17   0.531   1.647   1.678 -0.1878  1.6833 -0.7143
+    6SOL    HW2   18   0.660   1.695   1.615 -0.2766  0.4423 -1.8513
+    7SOL     OW   19   2.500   2.417   0.969 -0.3808  1.1237  0.8204
+    7SOL    HW1   20   2.408   2.405   0.990 -0.2199 -0.6807  0.4650
+    7SOL    HW2   21   2.544   2.424   1.054 -0.7596  1.3253  0.9974
+    8SOL     OW   22   2.969   1.283   2.411  0.0405 -0.0745  0.3622
+    8SOL    HW1   23   2.921   1.205   2.437 -0.7227 -0.5858 -2.5975
+    8SOL    HW2   24   3.060   1.254   2.403  0.3116  0.1548  2.8233
+    9SOL     OW   25   2.298   0.707   0.857  0.0293  0.1146  0.0823
+    9SOL    HW1   26   2.264   0.745   0.938  0.4903 -2.1302  1.3088
+    9SOL    HW2   27   2.373   0.654   0.884 -0.0346 -0.9202 -1.7681
+   10SOL     OW   28   2.909   2.737   1.093  0.7132 -0.4717  1.2365
+   10SOL    HW1   29   2.867   2.702   1.014 -2.1200 -0.8606  2.9072
+   10SOL    HW2   30   2.898   2.832   1.086 -0.0522 -0.5427  1.4341
+   11SOL     OW   31   2.857   1.705   0.620  0.5009  0.0652 -0.5792
+   11SOL    HW1   32   2.929   1.723   0.681 -0.8725  0.0686  1.0311
+   11SOL    HW2   33   2.834   1.790   0.584 -0.9708  0.5269  1.4415
+   12SOL     OW   34   2.824   1.077   2.516  0.2046 -0.1042 -0.0466
+   12SOL    HW1   35   2.813   1.043   2.605 -1.1200 -0.3778 -0.3127
+   12SOL    HW2   36   2.831   0.999   2.462 -0.9741  0.0566 -0.4303
+   13SOL     OW   37   0.557   2.058   2.385 -0.2029 -0.3370  0.2553
+   13SOL    HW1   38   0.593   1.970   2.399 -2.0267 -0.9375  1.1801
+   13SOL    HW2   39   0.481   2.062   2.443  0.4758  2.2140  0.9427
+   14SOL     OW   40   2.084   2.264   0.043 -0.2232  0.4748  0.0324
+   14SOL    HW1   41   2.127   2.350   0.042 -0.7555  0.7384  0.0319
+   14SOL    HW2   42   2.053   2.253  -0.047  0.6048 -0.0930 -0.1800
+   15SOL     OW   43   2.801   1.877   1.843  0.4426  0.0986 -0.4333
+   15SOL    HW1   44   2.860   1.815   1.799 -3.8853 -2.7741 -2.0938
+   15SOL    HW2   45   2.792   1.842   1.931  0.7427  0.2142 -0.3562
+   16SOL     OW   46   1.359   1.808   1.143 -0.0138  0.2507 -0.4776
+   16SOL    HW1   47   1.277   1.828   1.099 -0.6606 -0.3624  0.4406
+   16SOL    HW2   48   1.387   1.725   1.105  0.0395  0.3247 -0.5988
+   17SOL     OW   49   0.960   0.715   2.089 -0.7026 -0.4266  0.2923
+   17SOL    HW1   50   0.990   0.667   2.011 -0.5847 -2.8107  1.7980
+   17SOL    HW2   51   1.023   0.692   2.157 -1.2740  1.0199  1.3113
+   18SOL     OW   52   2.453   1.758   0.110 -0.3549 -0.0581  0.2951
+   18SOL    HW1   53   2.458   1.732   0.202  0.9244 -1.3316 -0.1384
+   18SOL    HW2   54   2.399   1.691   0.069  0.3071 -0.2962 -0.1851
+   19SOL     OW   55   2.729   1.440   2.447 -0.6483  0.4909  0.2144
+   19SOL    HW1   56   2.764   1.461   2.534 -0.4893 -0.1648  0.3089
+   19SOL    HW2   57   2.791   1.377   2.411 -1.2851 -0.0368  0.0468
+   20SOL     OW   58   2.305   1.174   0.898  0.0096 -0.0479 -0.2070
+   20SOL    HW1   59   2.299   1.264   0.866 -0.4201 -0.5462 -1.5519
+   20SOL    HW2   60   2.393   1.145   0.873  0.3211  0.1721  0.6577
+   21SOL     OW   61   0.687   0.108   2.776 -0.2447 -0.2707 -0.4695
+   21SOL    HW1   62   0.690   0.202   2.755  3.0564 -0.8739 -2.8379
+   21SOL    HW2   63   0.619   0.073   2.718  1.9426 -0.1884 -3.1103
+   22SOL     OW   64   1.831   0.938   2.190 -0.3532 -0.6591  0.0157
+   22SOL    HW1   65   1.735   0.933   2.189 -0.4962  1.8798 -0.2000
+   22SOL    HW2   66   1.851   0.979   2.274  0.4835  0.0673 -0.5377
+   23SOL     OW   67   2.078   0.556   2.364 -0.4333 -0.1994 -0.0002
+   23SOL    HW1   68   2.103   0.638   2.407 -0.2459  0.1200 -0.7196
+   23SOL    HW2   69   2.118   0.561   2.277 -0.8651 -0.8148 -0.2368
+   24SOL     OW   70   1.661   2.088   2.534 -0.1374  0.1423  0.3243
+   24SOL    HW1   71   1.589   2.033   2.563 -1.0202  0.8745 -0.4721
+   24SOL    HW2   72   1.694   2.129   2.614  0.8972 -2.0081  0.9831
+   25SOL     OW   73   2.323   2.225   1.267 -0.1923 -0.4397 -0.0057
+   25SOL    HW1   74   2.406   2.180   1.284  0.1357  0.7440  1.5923
+   25SOL    HW2   75   2.277   2.222   1.351 -0.1769  2.1434  0.0969
+   26SOL     OW   76   1.506   1.962   0.643 -0.2153  0.2955 -0.2004
+   26SOL    HW1   77   1.412   1.948   0.656  0.1265 -1.1410  0.6852
+   26SOL    HW2   78   1.520   2.052   0.672 -1.0778  0.0545  0.9881
+   27SOL     OW   79   1.966   2.343   1.889 -0.0507 -0.8648  0.8406
+   27SOL    HW1   80   2.046   2.366   1.937 -0.2184  0.8117  0.3153
+   27SOL    HW2   81   1.982   2.254   1.857  1.7773 -0.3425  0.3220
+   28SOL     OW   82   2.317   1.089   1.909  0.6126  0.1533 -0.0000
+   28SOL    HW1   83   2.305   1.112   1.817 -1.3374  0.1457  0.2628
+   28SOL    HW2   84   2.229   1.068   1.941  1.1075  1.3818  2.1977
+   29SOL     OW   85   0.537   2.686   0.628 -0.1181  0.5532 -0.5163
+   29SOL    HW1   86   0.573   2.724   0.548  2.4951 -0.7770  0.0291
+   29SOL    HW2   87   0.513   2.596   0.603  0.5051  0.2054  0.1464
+   30SOL     OW   88   1.006   0.913   1.804  0.1861  0.2905  0.1215
+   30SOL    HW1   89   1.082   0.952   1.847  0.0376  0.9906 -0.2541
+   30SOL    HW2   90   1.038   0.884   1.719  1.2121 -3.9033  1.9792
+   31SOL     OW   91   0.852   2.266   1.410  0.2381  0.4229 -0.2526
+   31SOL    HW1   92   0.857   2.361   1.424  0.4759  0.3678  0.0211
+   31SOL    HW2   93   0.788   2.236   1.475 -1.5447  0.7200 -1.8497
+   32SOL     OW   94   1.135   0.584   2.285  0.1861  0.4554 -0.3831
+   32SOL    HW1   95   1.063   0.535   2.325 -1.1325  1.3310 -1.6416
+   32SOL    HW2   96   1.182   0.623   2.358 -0.7478  0.0250  0.4434
+   33SOL     OW   97   1.395   0.948   1.279 -0.4022  0.3742 -0.3451
+   33SOL    HW1   98   1.314   0.975   1.237  0.4572  0.6736 -1.8060
+   33SOL    HW2   99   1.384   0.855   1.297 -0.9178  0.3281 -0.9117
+   34SOL     OW  100   1.877   2.558   0.154  0.3181 -0.1684 -0.1565
+   34SOL    HW1  101   1.796   2.602   0.179  0.1745 -0.5579  0.0620
+   34SOL    HW2  102   1.946   2.612   0.192  0.1561  0.1043 -0.2514
+   35SOL     OW  103   0.333   1.902   0.153  0.7925  0.4679 -0.0402
+   35SOL    HW1  104   0.283   1.896   0.071 -0.5333  3.2368  0.5407
+   35SOL    HW2  105   0.325   1.815   0.192 -1.1889  0.0487 -1.3545
+   36SOL     OW  106   2.718   2.724   1.959 -0.5335 -0.1666 -0.9838
+   36SOL    HW1  107   2.650   2.790   1.969 -0.5316  0.0484 -2.3513
+   36SOL    HW2  108   2.672   2.643   1.937 -0.4235 -0.2321 -0.9762
+   37SOL     OW  109   1.053   2.781   2.554  0.2209 -0.0393  0.1188
+   37SOL    HW1  110   0.966   2.741   2.550 -0.0283  0.5360 -0.3449
+   37SOL    HW2  111   1.045   2.849   2.621  0.0880  0.5511 -0.4970
+   38SOL     OW  112   0.310   1.630   0.216  0.0925 -0.0178  0.1209
+   38SOL    HW1  113   0.247   1.617   0.287 -0.0283 -0.1223 -0.0035
+   38SOL    HW2  114   0.394   1.602   0.252 -0.0276 -0.3850  0.1117
+   39SOL     OW  115   2.731   1.316   0.851 -0.0044 -0.1384  0.9197
+   39SOL    HW1  116   2.714   1.352   0.764  2.4894 -0.8450  0.1424
+   39SOL    HW2  117   2.700   1.225   0.846 -1.6750  0.4119  1.3594
+   40SOL     OW  118   1.274   1.943   1.824 -0.5294 -0.3913  0.1108
+   40SOL    HW1  119   1.306   1.863   1.866 -0.6124  1.2155  3.2565
+   40SOL    HW2  120   1.196   1.967   1.874 -2.3530 -0.1562 -2.8834
+   41SOL     OW  121   2.700   2.606   2.632 -0.7602  0.3423 -0.1571
+   41SOL    HW1  122   2.779   2.620   2.579 -0.8279  1.4759  0.0427
+   41SOL    HW2  123   2.636   2.573   2.569  0.5027 -2.6237  0.0918
+   42SOL     OW  124   2.725   1.337   0.215  0.0607  0.4986 -0.9628
+   42SOL    HW1  125   2.645   1.287   0.230  1.0559 -0.8357 -0.0950
+   42SOL    HW2  126   2.749   1.371   0.302 -0.6307  2.5235 -1.5444
+   43SOL     OW  127   0.669   0.190   1.525 -0.3572 -0.0638  0.0764
+   43SOL    HW1  128   0.576   0.198   1.506 -0.3155  0.7672  0.2441
+   43SOL    HW2  129   0.700   0.123   1.464 -0.7771  0.6717 -0.9379
+   44SOL     OW  130   1.374   2.655   0.785  0.2002 -0.2503  0.6371
+   44SOL    HW1  131   1.465   2.626   0.785  0.0774 -0.6247 -0.7351
+   44SOL    HW2  132   1.334   2.611   0.710 -1.0711  0.0345  1.1375
+   45SOL     OW  133   1.942   2.203   0.302  0.1821 -0.0595 -0.1502
+   45SOL    HW1  134   1.956   2.226   0.210  0.8950  1.0012  0.2096
+   45SOL    HW2  135   2.022   2.234   0.346 -0.0864 -0.7089  0.7945
+   46SOL     OW  136   2.511   1.671   0.777 -0.7862  0.0424  0.2265
+   46SOL    HW1  137   2.491   1.744   0.719  1.1487 -0.7328 -1.4159
+   46SOL    HW2  138   2.500   1.707   0.865  2.3408  2.9799 -0.5558
+   47SOL     OW  139   1.268   0.122   1.643 -0.3557  0.1095  0.0101
+   47SOL    HW1  140   1.240   0.084   1.726 -1.1172 -1.7042 -1.0856
+   47SOL    HW2  141   1.282   0.047   1.585 -1.4927  1.4689 -2.0360
+   48SOL     OW  142   2.665   0.535   2.874  0.1861  0.5734 -0.3122
+   48SOL    HW1  143   2.756   0.508   2.858  0.3303  0.5011  0.6458
+   48SOL    HW2  144   2.659   0.543   2.969 -0.7920  0.6561 -0.3859
+   49SOL     OW  145   0.130   1.438   0.637  0.0398 -0.0097  0.0058
+   49SOL    HW1  146   0.091   1.466   0.720 -1.1237  0.9785 -0.8643
+   49SOL    HW2  147   0.124   1.342   0.639 -0.9288  0.0608  0.6007
+   50SOL     OW  148   0.052   1.378   0.906 -0.7216  0.2342  0.4878
+   50SOL    HW1  149  -0.030   1.415   0.939 -1.0442 -1.2319  1.3370
+   50SOL    HW2  150   0.040   1.283   0.911  0.4005  0.0078 -0.9488
+   51SOL     OW  151   0.338   1.829   2.668 -0.1354 -0.7193  0.5150
+   51SOL    HW1  152   0.433   1.835   2.662 -0.0871 -0.1137  1.9236
+   51SOL    HW2  153   0.320   1.735   2.677  0.4057 -0.6854  1.8394
+   52SOL     OW  154   2.464   1.452   2.344 -0.0451 -0.0024 -0.4809
+   52SOL    HW1  155   2.486   1.405   2.263  0.9505  3.6371 -2.3253
+   52SOL    HW2  156   2.550   1.472   2.383 -0.5182 -1.4681  1.2883
+   53SOL     OW  157   2.023   2.166   2.289 -0.2020 -0.3459 -0.0801
+   53SOL    HW1  158   2.019   2.106   2.214 -0.5314  1.2804 -1.3757
+   53SOL    HW2  159   2.039   2.110   2.364  2.6114 -1.7687 -1.7218
+   54SOL     OW  160   1.521   0.348   1.933 -0.1896 -0.2359 -0.2637
+   54SOL    HW1  161   1.456   0.383   1.994 -0.0129  0.7329 -0.6469
+   54SOL    HW2  162   1.578   0.293   1.988  0.4303  1.2464  0.5862
+   55SOL     OW  163   0.475   0.500   2.211  0.1242 -0.1310  0.4072
+   55SOL    HW1  164   0.437   0.418   2.180 -0.0404  0.1195 -0.0541
+   55SOL    HW2  165   0.556   0.474   2.254  0.1378 -0.5185  0.1455
+   56SOL     OW  166   2.357   0.559   1.729 -0.1182  0.0900 -0.6280
+   56SOL    HW1  167   2.393   0.561   1.640  0.0629  3.6635 -0.4518
+   56SOL    HW2  168   2.281   0.501   1.722  0.9601 -1.0955 -2.7253
+   57SOL     OW  169   0.897   2.081   1.174  0.0657  0.0402  0.1612
+   57SOL    HW1  170   0.868   2.122   1.256 -1.3248  0.9448 -0.7881
+   57SOL    HW2  171   0.980   2.123   1.154 -0.6717  1.3872 -0.0614
+   58SOL     OW  172   1.199   2.721   2.813 -0.1477 -0.6115  0.6983
+   58SOL    HW1  173   1.229   2.752   2.727 -0.7826  0.9344  1.0396
+   58SOL    HW2  174   1.264   2.754   2.875 -0.8961  0.1665  1.0767
+   59SOL     OW  175   1.735   0.352   1.098  0.2888 -0.2507 -0.3288
+   59SOL    HW1  176   1.775   0.354   1.185 -0.3410 -0.4110 -0.0332
+   59SOL    HW2  177   1.643   0.331   1.115 -0.2195  1.5285 -0.8905
+   60SOL     OW  178   2.325   0.468   0.447 -0.1812  0.1198 -0.0522
+   60SOL    HW1  179   2.413   0.471   0.485  0.6212 -1.0433 -1.8370
+   60SOL    HW2  180   2.307   0.375   0.436 -1.2995  0.3896 -0.4780
+   61SOL     OW  181   2.364   1.965   2.686 -0.3664  0.0268 -0.6254
+   61SOL    HW1  182   2.342   1.874   2.706 -0.7091 -0.3233 -2.6306
+   61SOL    HW2  183   2.458   1.963   2.668 -0.2727  0.1498 -0.1355
+   62SOL     OW  184   2.309   1.929   1.089 -0.2201  0.1610  0.6244
+   62SOL    HW1  185   2.260   1.952   1.168  0.3722  1.4619  0.6217
+   62SOL    HW2  186   2.247   1.878   1.037 -0.8617 -0.0582  1.5977
+   63SOL     OW  187   0.751   1.179   0.038 -0.3105 -0.4360  0.0533
+   63SOL    HW1  188   0.778   1.185  -0.054  1.6676  0.9074  0.7163
+   63SOL    HW2  189   0.820   1.128   0.080 -1.6636 -1.7809  0.5998
+   64SOL     OW  190   0.588   1.651   0.817 -0.2661 -0.5804  0.2227
+   64SOL    HW1  191   0.628   1.597   0.885 -0.2704  1.1690  1.6256
+   64SOL    HW2  192   0.512   1.601   0.789 -0.1519 -1.4114  1.3639
+   65SOL     OW  193   1.008   1.666   2.400 -0.8477 -0.1210  0.2374
+   65SOL    HW1  194   1.089   1.641   2.356 -2.0171 -0.4996 -1.7408
+   65SOL    HW2  195   1.025   1.755   2.433  0.6722 -0.5513  0.6279
+   66SOL     OW  196   0.936   2.803   2.869 -0.2152 -0.3929 -0.0461
+   66SOL    HW1  197   1.018   2.755   2.858  0.0152  0.5335 -2.2936
+   66SOL    HW2  198   0.877   2.763   2.806 -0.3402  1.5880 -1.1684
+   67SOL     OW  199   2.277   3.011   1.656  0.3162 -0.2555  0.0150
+   67SOL    HW1  200   2.248   2.920   1.659 -2.0297  0.4983  0.1943
+   67SOL    HW2  201   2.308   3.024   1.567  1.0549 -1.3630  0.1192
+   68SOL     OW  202   1.217   0.986   2.736 -0.3171  0.5641  0.2684
+   68SOL    HW1  203   1.177   1.064   2.697  1.7489  1.4454 -0.1096
+   68SOL    HW2  204   1.206   0.998   2.830 -2.0054  0.2390  0.1028
+   69SOL     OW  205   0.211   2.876   2.311 -0.0078  0.3248  0.2110
+   69SOL    HW1  206   0.304   2.852   2.317  0.1248  0.6228 -0.6363
+   69SOL    HW2  207   0.176   2.858   2.398  0.8774  0.3217  0.5695
+   70SOL     OW  208   2.994   2.310   1.641 -0.1719  0.2965  0.6257
+   70SOL    HW1  209   3.072   2.332   1.691 -0.1220 -0.0603  0.7072
+   70SOL    HW2  210   2.923   2.356   1.685 -0.3515 -1.2224  1.9054
+   71SOL     OW  211   1.595   1.154   0.603  0.0004 -0.3004  0.2437
+   71SOL    HW1  212   1.631   1.085   0.659  0.2662  0.0650  0.5231
+   71SOL    HW2  213   1.661   1.167   0.536  0.2098  0.0869  0.5237
+   72SOL     OW  214   2.464   1.702   1.045  0.1188 -0.1477  0.4688
+   72SOL    HW1  215   2.401   1.774   1.053  0.4083  0.3601 -2.1055
+   72SOL    HW2  216   2.433   1.636   1.108 -1.5561  1.1718  1.0098
+   73SOL     OW  217   2.543   2.711   0.877 -0.3554  0.2278 -0.2638
+   73SOL    HW1  218   2.534   2.743   0.787 -1.5647 -0.3714 -0.3666
+   73SOL    HW2  219   2.637   2.698   0.889 -0.1110  1.2047 -1.1330
+   74SOL     OW  220   1.422   1.645   1.992  0.6996  0.2776 -0.1082
+   74SOL    HW1  221   1.427   1.739   2.009  0.5555  0.1421  0.6895
+   74SOL    HW2  222   1.507   1.611   2.019 -0.2002 -0.2218  2.1570
+   75SOL     OW  223   2.220   2.131   0.798  0.1851 -0.1303  0.0406
+   75SOL    HW1  224   2.268   2.072   0.856 -1.6140 -1.6160  0.0328
+   75SOL    HW2  225   2.177   2.193   0.857  0.5808  0.0437  0.1464
+   76SOL     OW  226   0.918   0.518   2.474 -0.5120 -0.0465  0.2454
+   76SOL    HW1  227   0.947   0.606   2.498 -0.2665  0.3015 -1.2886
+   76SOL    HW2  228   0.972   0.460   2.526 -1.1897  0.7290  1.8274
+   77SOL     OW  229   2.136   1.082   0.087 -0.2959  0.6062  0.6828
+   77SOL    HW1  230   2.188   1.050   0.014 -0.1087  1.8174  0.2884
+   77SOL    HW2  231   2.167   1.031   0.162 -0.1120 -0.2533  0.0255
+   78SOL     OW  232   0.284   1.212   1.378 -0.4073  0.2755 -0.1963
+   78SOL    HW1  233   0.336   1.281   1.418 -1.2620  0.7093  0.1530
+   78SOL    HW2  234   0.232   1.256   1.311  1.8648 -0.6192 -2.5656
+   79SOL     OW  235   0.606   1.747   1.951  0.3374  0.9035  0.1198
+   79SOL    HW1  236   0.659   1.728   1.874  1.7621  2.4309  0.7350
+   79SOL    HW2  237   0.536   1.805   1.919 -0.4735 -0.4307 -0.5156
+   80SOL     OW  238   0.110   0.366   2.679 -0.4230  0.3862 -0.0051
+   80SOL    HW1  239   0.181   0.398   2.623 -1.3922  0.8234 -0.9703
+   80SOL    HW2  240   0.093   0.277   2.649 -0.1473  0.2016  0.3720
+   81SOL     OW  241   0.628   1.241   2.469  0.2625  0.1589 -0.0474
+   81SOL    HW1  242   0.622   1.174   2.401  2.1979  1.7129 -1.7771
+   81SOL    HW2  243   0.656   1.194   2.547  0.5160 -1.2382 -0.9834
+   82SOL     OW  244   1.350   2.079   0.929  0.2853 -0.1513  0.2551
+   82SOL    HW1  245   1.369   2.109   1.018  1.0681 -0.5145  0.2068
+   82SOL    HW2  246   1.287   2.008   0.941 -0.3861  0.4686  0.4590
+   83SOL     OW  247   2.685   1.057   1.416  0.1331 -0.3800  0.0837
+   83SOL    HW1  248   2.694   1.030   1.507 -1.3365  0.8126  0.5882
+   83SOL    HW2  249   2.774   1.082   1.390  0.5938 -0.9265  1.1184
+   84SOL     OW  250   0.121   2.512   1.185  0.0676 -0.0403  0.9368
+   84SOL    HW1  251   0.140   2.545   1.273 -0.1195 -0.2978  1.0720
+   84SOL    HW2  252   0.057   2.574   1.151 -0.2053 -0.2049  1.1529
+   85SOL     OW  253   2.487   2.196   2.875 -0.0367 -0.0729  0.6108
+   85SOL    HW1  254   2.391   2.200   2.867  0.2362  3.4997 -0.6796
+   85SOL    HW2  255   2.518   2.192   2.784  1.0895 -0.4779  1.0174
+   86SOL     OW  256   0.008   2.256   0.227  0.8628  0.2754 -0.5671
+   86SOL    HW1  257   0.102   2.269   0.236  0.4783  1.5635  1.5789
+   86SOL    HW2  258  -0.020   2.219   0.311 -0.3791 -1.1176 -1.5736
+   87SOL     OW  259   1.941   1.788   2.727  0.3830  0.2326  0.8551
+   87SOL    HW1  260   1.926   1.864   2.783 -0.9738 -0.9928  2.1528
+   87SOL    HW2  261   1.998   1.731   2.779 -1.4754 -1.2374  1.2914
+   88SOL     OW  262   0.284   0.820   1.803  0.9142 -0.0744  0.3207
+   88SOL    HW1  263   0.230   0.853   1.731  1.3019  1.2150  0.6180
+   88SOL    HW2  264   0.371   0.853   1.784  0.7919  1.2229  1.9851
+   89SOL     OW  265   1.804   0.578   1.667  0.3902  0.3143  0.4478
+   89SOL    HW1  266   1.796   0.483   1.658  1.8242  0.3082 -0.8233
+   89SOL    HW2  267   1.722   0.606   1.709 -0.3803 -1.3725  0.0469
+   90SOL     OW  268   2.566   1.351   1.097 -0.6149 -0.3137 -0.0157
+   90SOL    HW1  269   2.610   1.377   1.016  0.3054  2.1861  1.2738
+   90SOL    HW2  270   2.519   1.430   1.125 -0.9977 -1.2024  1.8370
+   91SOL     OW  271   1.746   1.808   0.462  0.1668  0.1866 -0.1191
+   91SOL    HW1  272   1.678   1.867   0.493 -0.4882 -1.3547  1.3701
+   91SOL    HW2  273   1.792   1.858   0.394 -1.3474  1.8431  0.0752
+   92SOL     OW  274   2.141   1.089   1.116  0.1107 -0.1872  0.3779
+   92SOL    HW1  275   2.174   1.119   1.031  0.4688  0.6575  0.8094
+   92SOL    HW2  276   2.058   1.046   1.096 -0.3112  0.9229 -0.2888
+   93SOL     OW  277   2.002   0.489   1.845 -0.0719  0.6937  0.0660
+   93SOL    HW1  278   1.927   0.529   1.801  0.2958  0.9375 -0.3458
+   93SOL    HW2  279   2.027   0.553   1.912 -0.3638  0.5175  0.3420
+   94SOL     OW  280   2.935   2.597   2.461 -0.3149  0.3844 -0.5806
+   94SOL    HW1  281   2.996   2.523   2.466  0.2666  0.9167  0.2320
+   94SOL    HW2  282   2.909   2.601   2.369  0.6282 -0.0431 -0.8604
+   95SOL     OW  283   1.017   2.432   0.770  0.3181  0.4329  0.1767
+   95SOL    HW1  284   1.033   2.449   0.677 -0.6164  0.1603 -0.0407
+   95SOL    HW2  285   1.101   2.401   0.804  0.5575  0.1386 -0.6745
+   96SOL     OW  286   0.084   0.030   0.719 -0.3342  0.5579  0.0395
+   96SOL    HW1  287   0.091   0.004   0.811 -1.3832 -0.3085 -0.1336
+   96SOL    HW2  288   0.159  -0.011   0.677  0.2310  1.0756  0.5314
+   97SOL     OW  289   0.799   1.837   0.865  0.0121 -0.9847 -0.1039
+   97SOL    HW1  290   0.775   1.835   0.957  0.3116 -0.1506  0.0004
+   97SOL    HW2  291   0.718   1.816   0.818 -0.3547 -0.5104  0.3171
+   98SOL     OW  292   2.708  -0.019   2.509  0.0084  0.4777 -0.1512
+   98SOL    HW1  293   2.614  -0.029   2.493  0.1276 -0.6018 -0.1387
+   98SOL    HW2  294   2.714   0.052   2.573 -0.7700  0.9352 -0.5872
+   99SOL     OW  295   1.329   0.105   0.532 -0.0103 -0.1683  0.3093
+   99SOL    HW1  296   1.284   0.053   0.598 -0.9241 -1.6384 -1.4491
+   99SOL    HW2  297   1.294   0.194   0.543 -0.9904 -0.6486  1.0495
+  100SOL     OW  298   2.218   1.126   1.659  0.3361  0.6363  0.4844
+  100SOL    HW1  299   2.258   1.128   1.572 -1.1042  3.1058 -0.1087
+  100SOL    HW2  300   2.124   1.126   1.643  0.0696 -3.2625  1.9422
+  101SOL     OW  301   1.076   2.451   1.201 -0.1444 -0.4090  0.2293
+  101SOL    HW1  302   1.070   2.513   1.129  0.1129 -0.0487  0.5220
+  101SOL    HW2  303   0.986   2.417   1.211 -0.0217 -1.0369 -0.7706
+  102SOL     OW  304   2.407   2.238   0.677 -0.0059  0.8298  0.2311
+  102SOL    HW1  305   2.330   2.195   0.713  0.1399  1.2212  1.0023
+  102SOL    HW2  306   2.397   2.330   0.702  1.2811  0.6722  1.2908
+  103SOL     OW  307   1.996   2.243   2.684 -0.3318  0.7159  0.0901
+  103SOL    HW1  308   1.965   2.260   2.595 -0.1231  0.8147  0.0362
+  103SOL    HW2  309   1.918   2.254   2.739 -0.8391 -1.0776 -0.2723
+  104SOL     OW  310   2.001   0.932   0.533 -0.9679 -0.1357 -0.2389
+  104SOL    HW1  311   2.020   1.003   0.594  3.1015 -0.1139 -1.5161
+  104SOL    HW2  312   2.031   0.853   0.579 -1.7614 -0.5823 -0.4893
+  105SOL     OW  313   2.373   0.289   2.480  0.1776 -0.3633 -0.0220
+  105SOL    HW1  314   2.427   0.326   2.550  0.0686 -4.1480  2.0082
+  105SOL    HW2  315   2.291   0.265   2.522 -0.6936 -0.6476 -1.8749
+  106SOL     OW  316   1.378   2.733   2.512  0.1417 -0.2316  0.6698
+  106SOL    HW1  317   1.451   2.693   2.559 -0.0078  0.0018  1.0960
+  106SOL    HW2  318   1.337   2.660   2.466 -0.2793 -0.6084  1.6417
+  107SOL     OW  319   0.499   2.301   2.271 -0.3691  0.2231 -0.4649
+  107SOL    HW1  320   0.536   2.233   2.327 -2.1780 -0.4212 -0.0700
+  107SOL    HW2  321   0.414   2.322   2.310 -0.8831  0.9789 -1.9784
+  108SOL     OW  322   0.178   2.417   2.862 -0.1967  0.4616  0.2878
+  108SOL    HW1  323   0.209   2.500   2.900  0.2138 -0.1117  1.2053
+  108SOL    HW2  324   0.111   2.387   2.924 -1.0047  0.5704 -0.5285
+  109SOL     OW  325   0.056   1.955   2.155  0.9619  0.4841 -0.4953
+  109SOL    HW1  326   0.150   1.946   2.170  0.5473 -0.8837  1.2943
+  109SOL    HW2  327   0.027   2.024   2.215  0.9380  1.3069 -1.4592
+  110SOL     OW  328   2.568   1.666   2.020 -0.2913 -0.7739  0.1025
+  110SOL    HW1  329   2.513   1.700   2.091 -0.4460 -1.6946  0.4186
+  110SOL    HW2  330   2.590   1.577   2.047 -1.6868 -1.6669 -1.8042
+  111SOL     OW  331   1.412   0.169   2.296  0.0642 -0.2179 -0.1844
+  111SOL    HW1  332   1.454   0.095   2.340 -0.1745 -1.7763 -2.5561
+  111SOL    HW2  333   1.322   0.140   2.281  0.0110  0.5052 -1.2577
+  112SOL     OW  334   2.226   1.576   2.421  0.1048 -0.1434 -0.2255
+  112SOL    HW1  335   2.286   1.534   2.482 -0.2920 -1.4347 -0.7249
+  112SOL    HW2  336   2.269   1.570   2.336 -0.7237 -1.0486 -0.5815
+  113SOL     OW  337   0.022   2.079   2.745 -0.1795  0.1420  0.2085
+  113SOL    HW1  338   0.096   2.064   2.803 -0.0980 -0.3544 -0.0236
+  113SOL    HW2  339  -0.037   2.006   2.762 -1.0296  1.0213  1.0494
+  114SOL     OW  340   1.684   2.867   1.807 -0.4722  0.4635 -0.6864
+  114SOL    HW1  341   1.590   2.846   1.805 -0.4255  0.3409 -1.9167
+  114SOL    HW2  342   1.691   2.949   1.758  0.2264 -0.1985 -1.6975
+  115SOL     OW  343   2.430   2.547   1.519  0.3993 -0.7792 -0.1970
+  115SOL    HW1  344   2.457   2.606   1.448 -0.6310  0.8211  0.7546
+  115SOL    HW2  345   2.509   2.497   1.539  1.4453  0.7329 -0.5409
+  116SOL     OW  346   0.211   0.066   0.358 -0.1489 -0.0056  0.2374
+  116SOL    HW1  347   0.210  -0.009   0.300 -2.7022 -1.5891  2.3563
+  116SOL    HW2  348   0.274   0.042   0.427 -0.0805  0.2942  0.2807
+  117SOL     OW  349   1.882   1.150   0.203 -0.4670 -0.2249  0.2114
+  117SOL    HW1  350   1.906   1.240   0.225 -0.2081 -0.5343  1.1756
+  117SOL    HW2  351   1.965   1.110   0.177 -0.5844 -0.1877 -0.2257
+  118SOL     OW  352   1.635   2.625   0.712 -0.2512  0.4961  0.3887
+  118SOL    HW1  353   1.642   2.647   0.619  0.6883 -1.6426 -0.0365
+  118SOL    HW2  354   1.700   2.556   0.725  0.4896  1.7074  2.9135
+  119SOL     OW  355   0.897   2.437   2.890 -0.5635 -0.0374  0.4567
+  119SOL    HW1  356   0.902   2.396   2.803  0.2651 -0.2996  0.6335
+  119SOL    HW2  357   0.975   2.406   2.936 -0.2443  1.2681  0.7926
+  120SOL     OW  358   0.038   1.689   0.341 -0.7395 -0.0492  0.0537
+  120SOL    HW1  359   0.007   1.748   0.271 -1.4359  0.1918  0.5565
+  120SOL    HW2  360   0.037   1.744   0.419  1.0652 -0.0307  0.0442
+  121SOL     OW  361   1.992   1.015   2.667 -0.0118 -0.0461  0.6674
+  121SOL    HW1  362   2.031   1.099   2.645 -2.6485  0.8789 -0.4431
+  121SOL    HW2  363   1.942   1.031   2.747 -2.5335  0.0080 -0.9205
+  122SOL     OW  364   2.546   1.167   0.471  0.3016 -0.4348  0.4222
+  122SOL    HW1  365   2.470   1.110   0.478 -0.8429  1.1575  0.9649
+  122SOL    HW2  366   2.599   1.129   0.401 -0.9842 -1.5290  0.0559
+  123SOL     OW  367   0.990   0.291   1.087 -0.3744  0.0741 -0.0075
+  123SOL    HW1  368   0.951   0.377   1.103  1.4087  0.9005 -0.0972
+  123SOL    HW2  369   0.993   0.283   0.992 -1.4303 -0.4899  0.0087
+  124SOL     OW  370   0.580   0.106   2.136 -0.1442  0.4827  0.6347
+  124SOL    HW1  371   0.574   0.137   2.046 -1.1400  0.8060  0.8112
+  124SOL    HW2  372   0.589   0.011   2.128 -0.2607  0.5088  0.1970
+  125SOL     OW  373   0.111   2.593   0.544 -0.0038  0.5225 -0.7659
+  125SOL    HW1  374   0.115   2.498   0.535  2.5253  0.5543  0.1321
+  125SOL    HW2  375   0.151   2.611   0.629 -0.5810  2.3920 -0.8817
+  126SOL     OW  376   2.712   0.627   0.537 -0.6589  0.3567 -0.5993
+  126SOL    HW1  377   2.791   0.576   0.554 -0.4346  0.2773 -1.8733
+  126SOL    HW2  378   2.646   0.562   0.515 -1.2539  0.3759  1.1160
+  127SOL     OW  379   1.140   0.538   0.052 -0.1692 -0.7029  0.1229
+  127SOL    HW1  380   1.228   0.501   0.053  0.8687  1.7046 -0.5854
+  127SOL    HW2  381   1.106   0.523   0.141  1.0717 -1.3342  0.4839
+  128SOL     OW  382   0.893   0.448   1.423  0.0402  0.2694  0.6619
+  128SOL    HW1  383   0.825   0.480   1.482 -0.4877  1.3217 -0.5124
+  128SOL    HW2  384   0.944   0.386   1.476 -1.7554 -0.4380  1.5715
+  129SOL     OW  385   0.119   2.981   1.952  0.1568 -0.4223 -0.1875
+  129SOL    HW1  386   0.084   2.906   2.000 -2.1948 -0.1645 -1.5272
+  129SOL    HW2  387   0.117   3.052   2.016  0.3969 -0.9839  0.4441
+  130SOL     OW  388   1.112   0.396   2.625  0.4582 -0.7249  0.1096
+  130SOL    HW1  389   1.105   0.303   2.649 -0.0056 -1.3206 -2.3658
+  130SOL    HW2  390   1.206   0.408   2.607  0.2612 -1.1000 -1.1660
+  131SOL     OW  391   1.047   1.332   0.227  0.1956 -0.1577  0.0362
+  131SOL    HW1  392   1.044   1.424   0.251 -0.0682 -0.0308 -0.4864
+  131SOL    HW2  393   1.136   1.304   0.250  0.9829  0.7768 -1.8273
+  132SOL     OW  394   0.207   2.786   2.578  0.0301  0.4069  0.1369
+  132SOL    HW1  395   0.169   2.807   2.663  0.6336 -0.4170  0.6111
+  132SOL    HW2  396   0.221   2.691   2.580  0.7484  0.4933 -0.5855
+  133SOL     OW  397   1.250   0.961   0.190 -0.6050 -0.0202  0.0675
+  133SOL    HW1  398   1.304   0.968   0.269 -3.5534  4.0491  1.7167
+  133SOL    HW2  399   1.286   1.027   0.131 -0.9716 -0.3332 -0.5050
+  134SOL     OW  400   1.508   1.893   2.064  0.3560 -0.0004  0.4914
+  134SOL    HW1  401   1.543   1.981   2.048 -1.1355  0.2992 -1.1303
+  134SOL    HW2  402   1.539   1.871   2.152  2.4990  1.2868  0.0584
+  135SOL     OW  403   2.909   0.858   0.787 -0.0493 -0.0587 -0.2044
+  135SOL    HW1  404   2.936   0.766   0.789  0.0033 -0.0570 -0.7234
+  135SOL    HW2  405   2.901   0.878   0.694  1.5354  0.9363 -0.1133
+  136SOL     OW  406   0.528   1.586   1.123  0.1281 -0.3228  0.3599
+  136SOL    HW1  407   0.617   1.589   1.158 -0.6037  1.0294  2.1198
+  136SOL    HW2  408   0.504   1.678   1.111 -0.0341 -0.7738 -2.6280
+  137SOL     OW  409   2.767   2.140   0.669  0.0471  0.2409 -0.3352
+  137SOL    HW1  410   2.787   2.114   0.759 -1.2007 -1.0783 -0.4407
+  137SOL    HW2  411   2.752   2.058   0.623 -1.6812  1.0515 -1.1939
+  138SOL     OW  412   1.398   2.792   1.820 -0.1589 -0.2623  0.1209
+  138SOL    HW1  413   1.346   2.797   1.900 -1.4374  0.5923 -0.7563
+  138SOL    HW2  414   1.334   2.802   1.749  1.2457  1.1534 -0.9501
+  139SOL     OW  415   3.040   0.629   1.198 -0.1992 -0.5276 -0.3018
+  139SOL    HW1  416   3.002   0.713   1.172  1.4171  0.4914  0.5808
+  139SOL    HW2  417   2.972   0.564   1.178 -1.5363  1.0129 -0.7722
+  140SOL     OW  418   2.185   0.545   1.386  0.7282 -0.1842 -0.2281
+  140SOL    HW1  419   2.157   0.634   1.404  0.8685  0.0780 -1.2852
+  140SOL    HW2  420   2.279   0.552   1.369  0.5349 -0.3693 -1.3915
+  141SOL     OW  421   0.844   0.233   1.825 -0.1553 -0.2899 -0.1438
+  141SOL    HW1  422   0.775   0.177   1.861 -0.1982 -0.1514 -0.0071
+  141SOL    HW2  423   0.919   0.218   1.883 -0.3134  0.1535  0.1814
+  142SOL     OW  424   1.796   0.507   2.158  0.6559  0.0532 -0.3275
+  142SOL    HW1  425   1.877   0.486   2.111  2.0442  1.8335  1.2249
+  142SOL    HW2  426   1.745   0.427   2.156  1.8590 -0.6675 -2.5627
+  143SOL     OW  427   0.533   0.567   0.642 -0.3235  0.3353 -0.4885
+  143SOL    HW1  428   0.459   0.628   0.649 -0.2754  0.4302 -0.8094
+  143SOL    HW2  429   0.523   0.526   0.556  0.3374  0.7934 -0.7877
+  144SOL     OW  430   1.631   2.387   0.373  0.3134  0.2305  0.1900
+  144SOL    HW1  431   1.573   2.380   0.449 -1.1791  0.3696 -0.9353
+  144SOL    HW2  432   1.683   2.466   0.390 -1.3730  1.3555  0.1430
+  145SOL     OW  433   2.818   2.999   0.746  1.1079  0.4905  0.1449
+  145SOL    HW1  434   2.805   2.917   0.699  1.2819  0.2255  0.5537
+  145SOL    HW2  435   2.910   3.020   0.734  1.1862  0.3480  0.4807
+  146SOL     OW  436   2.788   2.752   0.603  0.2123 -0.3679  0.0948
+  146SOL    HW1  437   2.831   2.793   0.528  0.5107 -0.6618  0.1022
+  146SOL    HW2  438   2.808   2.658   0.593 -0.4164 -0.5043  0.1027
+  147SOL     OW  439   1.063   0.554   0.892  0.4361 -0.1779 -0.0475
+  147SOL    HW1  440   1.075   0.649   0.889 -1.0151 -0.0965 -2.6184
+  147SOL    HW2  441   0.993   0.540   0.956 -2.5430  0.7424 -3.1154
+  148SOL     OW  442   1.826   2.387   1.228 -0.2764 -0.1215  0.1799
+  148SOL    HW1  443   1.794   2.359   1.142 -1.3392  1.1125  0.1745
+  148SOL    HW2  444   1.862   2.475   1.212 -2.1362  0.8558  1.3847
+  149SOL     OW  445   2.310   1.682   2.741  0.2591  0.7090  0.2774
+  149SOL    HW1  446   2.290   1.641   2.825 -0.1642 -0.1487 -0.2495
+  149SOL    HW2  447   2.336   1.610   2.684  0.2930  1.2612 -0.4105
+  150SOL     OW  448   2.763   0.430   2.521 -0.0545 -0.2016 -0.5807
+  150SOL    HW1  449   2.848   0.390   2.505 -0.6894 -1.4749 -0.7457
+  150SOL    HW2  450   2.727   0.382   2.595 -1.3676 -0.8090 -1.6086
+  151SOL     OW  451   2.349   1.391   2.665  0.3391 -0.1341  0.5540
+  151SOL    HW1  452   2.383   1.313   2.623  1.0794 -0.6552  2.0991
+  151SOL    HW2  453   2.255   1.376   2.675  0.1131  0.1386 -1.2188
+  152SOL     OW  454   1.686   2.006   2.272  0.3532  0.0438  0.4239
+  152SOL    HW1  455   1.660   2.018   2.364 -1.9097 -0.9919 -0.0679
+  152SOL    HW2  456   1.677   2.092   2.233 -2.9559 -0.4359  0.1163
+  153SOL     OW  457   2.659   2.407   1.454  0.1204 -0.2252 -0.0322
+  153SOL    HW1  458   2.705   2.479   1.498  1.1311 -0.8716 -0.0278
+  153SOL    HW2  459   2.715   2.331   1.469  0.3966 -0.4526 -2.3576
+  154SOL     OW  460   0.203   1.852   1.840  0.4463  0.3894  0.5424
+  154SOL    HW1  461   0.134   1.789   1.860  1.7007 -1.1114  0.0782
+  154SOL    HW2  462   0.232   1.885   1.925 -0.4127  0.5684  0.7658
+  155SOL     OW  463   0.984   2.258   0.155  0.3933  0.5082 -0.2997
+  155SOL    HW1  464   0.896   2.232   0.129 -0.0749  1.5565  0.1985
+  155SOL    HW2  465   1.031   2.176   0.168 -1.0021 -0.0877  1.0661
+  156SOL     OW  466   1.183   1.050   1.124 -0.0675 -0.7137  0.2267
+  156SOL    HW1  467   1.118   1.117   1.101 -0.5598 -0.7833  1.4301
+  156SOL    HW2  468   1.191   0.996   1.046 -0.4580  0.1205 -0.3865
+  157SOL     OW  469   0.362   0.144   0.634 -0.4237  0.5799  0.3215
+  157SOL    HW1  470   0.429   0.151   0.566 -0.3515  1.8804  0.5139
+  157SOL    HW2  471   0.388   0.069   0.686 -1.3084 -1.3132 -1.9965
+  158SOL     OW  472   1.423   2.242   2.342 -0.0063 -0.2911  0.3158
+  158SOL    HW1  473   1.364   2.234   2.267 -1.0011  1.0236  0.9671
+  158SOL    HW2  474   1.508   2.265   2.304 -0.0365 -1.6001 -0.5668
+  159SOL     OW  475   0.760   0.834   0.652 -0.2248  0.0369  0.0101
+  159SOL    HW1  476   0.710   0.752   0.653 -1.6402  0.8908 -1.4844
+  159SOL    HW2  477   0.696   0.901   0.673  0.8373  0.8286  0.6917
+  160SOL     OW  478   0.568   0.580   2.670 -0.2835  0.3785 -0.2530
+  160SOL    HW1  479   0.613   0.502   2.704  0.3447  0.6124 -0.5466
+  160SOL    HW2  480   0.612   0.653   2.713 -0.0859  0.6304 -0.8878
+  161SOL     OW  481   1.680   0.408   0.279 -0.0705  0.7993 -0.5055
+  161SOL    HW1  482   1.647   0.325   0.315 -0.4825  1.3409  0.3625
+  161SOL    HW2  483   1.712   0.456   0.356  2.7739  0.2520 -1.3344
+  162SOL     OW  484   0.859   0.539   1.105 -0.0523 -0.1396 -0.0519
+  162SOL    HW1  485   0.777   0.567   1.063 -0.1599  0.3388  0.4790
+  162SOL    HW2  486   0.854   0.575   1.193  1.8203  1.8779 -0.8000
+  163SOL     OW  487   1.718   2.759   2.347 -0.0642 -0.2631 -0.4285
+  163SOL    HW1  488   1.791   2.699   2.330 -1.1479 -1.4053 -1.1498
+  163SOL    HW2  489   1.665   2.754   2.267 -0.3028  1.4508 -0.3742
+  164SOL     OW  490   1.522   0.594   2.449  0.4335  0.0053  0.2626
+  164SOL    HW1  491   1.586   0.664   2.437  0.2500 -0.0643 -1.1143
+  164SOL    HW2  492   1.447   0.637   2.490  0.6683  0.4384  0.2427
+  165SOL     OW  493   1.694   2.204   2.789  0.0914 -0.7320  0.7167
+  165SOL    HW1  494   1.599   2.213   2.797  0.2049  1.8129 -1.0293
+  165SOL    HW2  495   1.730   2.262   2.856  0.5000  1.3194 -1.2810
+  166SOL     OW  496   0.546   0.930   0.446 -0.3092 -0.2640  0.4392
+  166SOL    HW1  497   0.502   1.010   0.417 -1.4820 -0.6826  1.0485
+  166SOL    HW2  498   0.556   0.941   0.540  0.0578 -0.5694  0.4354
+  167SOL     OW  499   2.573   0.864   1.201  0.1626 -0.2844 -0.0937
+  167SOL    HW1  500   2.530   0.921   1.136  0.1160  0.0909  0.2635
+  167SOL    HW2  501   2.577   0.918   1.280 -0.6493 -1.0774  0.4926
+  168SOL     OW  502   1.822   1.834   0.169  0.0536  0.1283  0.1543
+  168SOL    HW1  503   1.752   1.872   0.117  0.0141  0.9049  0.7731
+  168SOL    HW2  504   1.902   1.874   0.135  0.0887  0.1548  0.2691
+  169SOL     OW  505   0.111   0.968   0.132 -0.4783 -0.0484  0.8205
+  169SOL    HW1  506   0.049   1.033   0.164 -1.1018  0.2418 -0.9404
+  169SOL    HW2  507   0.151   1.009   0.056  0.6636 -0.8925  0.9680
+  170SOL     OW  508   2.962   1.796   1.019  0.1380 -0.7163  0.2794
+  170SOL    HW1  509   3.039   1.808   1.074 -1.0933 -1.6852  2.2166
+  170SOL    HW2  510   2.935   1.706   1.034 -0.7344 -0.7095 -1.2201
+  171SOL     OW  511   2.710   1.685   0.331 -0.3002  0.2076 -0.1328
+  171SOL    HW1  512   2.717   1.776   0.304 -0.6780  0.1941 -0.2673
+  171SOL    HW2  513   2.799   1.661   0.357  0.0272  0.2730 -1.1887
+  172SOL     OW  514   1.598   0.209   0.437 -0.3228 -0.1194 -0.3531
+  172SOL    HW1  515   1.660   0.214   0.509 -1.0923  0.7554  0.2414
+  172SOL    HW2  516   1.517   0.177   0.476 -0.4177 -0.7202 -1.0277
+  173SOL     OW  517   1.955   2.917   1.368  0.3259 -0.1826 -0.1750
+  173SOL    HW1  518   1.884   2.853   1.358  0.1395 -0.0006 -0.0050
+  173SOL    HW2  519   1.935   2.985   1.303 -0.7750  0.9006  1.3001
+  174SOL     OW  520   1.470   2.875   0.404  0.1972  0.0390 -0.1280
+  174SOL    HW1  521   1.519   2.872   0.487 -2.3203 -1.0528  1.3080
+  174SOL    HW2  522   1.378   2.877   0.431 -0.6378 -1.8153 -2.9378
+  175SOL     OW  523   0.967  -0.022   0.967 -0.0481  0.2575  0.4156
+  175SOL    HW1  524   0.961   0.073   0.979 -0.6853  0.1351  1.1037
+  175SOL    HW2  525   0.918  -0.039   0.887 -0.6226  0.4218  0.7276
+  176SOL     OW  526   2.438   0.279   0.807 -0.1503 -0.0214  0.4664
+  176SOL    HW1  527   2.349   0.292   0.775 -0.3545 -0.5630  0.8256
+  176SOL    HW2  528   2.438   0.315   0.896  0.3062  1.0920  0.0180
+  177SOL     OW  529   1.379   0.640   1.778 -0.0810 -0.5749 -0.4052
+  177SOL    HW1  530   1.376   0.708   1.844 -1.9653 -1.2949  0.2616
+  177SOL    HW2  531   1.377   0.687   1.694 -1.8118  0.1741  0.0524
+  178SOL     OW  532   0.445   1.882   1.733 -0.4444 -0.0215  0.5215
+  178SOL    HW1  533   0.472   1.974   1.737 -0.9356  0.1631 -0.4292
+  178SOL    HW2  534   0.349   1.886   1.736 -0.4405 -0.5528  1.4390
+  179SOL     OW  535   0.722   1.548   2.811  0.2240 -0.3500 -0.1110
+  179SOL    HW1  536   0.784   1.607   2.769 -0.2711  0.4208  0.2441
+  179SOL    HW2  537   0.777   1.489   2.863  0.5089  1.8170  2.0384
+  180SOL     OW  538   0.026   0.419   1.602 -0.0214  0.1451 -0.4609
+  180SOL    HW1  539   0.023   0.409   1.697 -1.7911 -0.7596 -0.6166
+  180SOL    HW2  540   0.056   0.510   1.588 -0.4510  0.4950  0.9452
+  181SOL     OW  541   0.544   2.530   1.856  0.9396 -0.2637  0.7821
+  181SOL    HW1  542   0.622   2.478   1.876  1.1336 -0.2946 -0.0708
+  181SOL    HW2  543   0.576   2.601   1.801  0.3256 -0.4274  0.2093
+  182SOL     OW  544   1.813   2.311   2.417  0.0022 -0.2609 -0.2679
+  182SOL    HW1  545   1.733   2.268   2.450 -0.7467  0.0394 -1.6814
+  182SOL    HW2  546   1.857   2.243   2.367  0.0034 -0.0640 -0.5327
+  183SOL     OW  547   2.165   1.874   1.846  0.0475  0.4124 -0.0611
+  183SOL    HW1  548   2.182   1.805   1.782  0.7539  0.2944  0.2539
+  183SOL    HW2  549   2.251   1.902   1.875 -0.2821 -0.2836  1.6388
+  184SOL     OW  550   1.604   1.286   2.800 -0.5080 -0.0303  0.2756
+  184SOL    HW1  551   1.599   1.328   2.885 -1.0088 -0.7303  0.5929
+  184SOL    HW2  552   1.632   1.196   2.819  0.3127  0.0851 -0.3713
+  185SOL     OW  553   0.198   0.473   0.037 -0.4745 -0.5144 -0.4463
+  185SOL    HW1  554   0.224   0.565   0.033  1.2875 -0.9616  0.8023
+  185SOL    HW2  555   0.265   0.426  -0.012 -1.0976 -1.2034 -0.6476
+  186SOL     OW  556   2.558   0.657   1.006  0.0683  0.1486  0.2152
+  186SOL    HW1  557   2.645   0.620   0.990  0.0967  0.8030 -1.1091
+  186SOL    HW2  558   2.565   0.695   1.093  0.5252 -1.5418  0.9331
+  187SOL     OW  559   2.151   2.771   1.523 -0.1839 -0.3286  0.4761
+  187SOL    HW1  560   2.086   2.826   1.481 -0.9664 -1.7573 -0.1850
+  187SOL    HW2  561   2.209   2.744   1.452  1.8067  1.8885  1.2510
+  188SOL     OW  562   2.302   2.503   0.279 -0.3280  0.0381  0.2176
+  188SOL    HW1  563   2.388   2.525   0.316 -0.0210  0.5597 -0.8072
+  188SOL    HW2  564   2.321   2.481   0.188 -0.8985 -2.5409  0.7128
+  189SOL     OW  565   2.247   2.989   0.650  0.3867 -0.0102  0.1706
+  189SOL    HW1  566   2.314   3.057   0.642  0.1764  0.3180  1.1696
+  189SOL    HW2  567   2.251   2.963   0.742 -2.9412  2.1557  0.9364
+  190SOL     OW  568   2.667   0.461   1.735 -0.0030 -0.0612  0.2967
+  190SOL    HW1  569   2.700   0.381   1.774 -0.3241 -0.4234 -0.1675
+  190SOL    HW2  570   2.594   0.433   1.680  0.5314  0.4200 -0.6718
+  191SOL     OW  571   1.420   1.344   0.947  0.2214  0.3146 -0.3937
+  191SOL    HW1  572   1.447   1.432   0.976 -0.9121  0.3218  0.6731
+  191SOL    HW2  573   1.497   1.289   0.963 -0.1109  0.4986  1.8223
+  192SOL     OW  574   2.046   2.731   2.078  0.3406 -0.4130 -0.3393
+  192SOL    HW1  575   2.100   2.804   2.109 -0.0869  0.0869 -0.7551
+  192SOL    HW2  576   2.071   2.720   1.987  0.4524 -0.8340 -0.2575
+  193SOL     OW  577   0.344   1.449   0.876  0.0862  0.4580  0.2981
+  193SOL    HW1  578   0.339   1.492   0.961  1.2427 -1.8743  1.5499
+  193SOL    HW2  579   0.261   1.400   0.869  0.9857 -1.1629  1.0525
+  194SOL     OW  580   2.221   0.142   1.331 -0.4676  0.2617  0.8104
+  194SOL    HW1  581   2.294   0.204   1.330  0.1825 -0.5181 -1.5034
+  194SOL    HW2  582   2.223   0.103   1.244 -2.9707 -0.5634  1.1421
+  195SOL     OW  583   1.517   0.378   1.417  0.2909  0.2992 -0.2059
+  195SOL    HW1  584   1.569   0.298   1.423 -1.6143 -0.9831 -0.9569
+  195SOL    HW2  585   1.490   0.397   1.507  0.3812 -0.1332 -0.0899
+  196SOL     OW  586   1.721   2.726   2.053 -0.4058  0.0528 -0.1471
+  196SOL    HW1  587   1.698   2.813   2.023  1.1184  0.9228  1.1661
+  196SOL    HW2  588   1.812   2.714   2.025 -0.1228 -0.5170  1.0095
+  197SOL     OW  589   0.234   1.346   2.672  0.6447  0.4013  0.4027
+  197SOL    HW1  590   0.259   1.257   2.696 -0.5747  0.0553  0.4143
+  197SOL    HW2  591   0.161   1.367   2.730  1.1273  1.5207  0.6048
+  198SOL     OW  592   2.475   2.814   0.630 -0.0696 -0.6229 -0.2523
+  198SOL    HW1  593   2.387   2.798   0.595 -1.1416  0.1128  2.1548
+  198SOL    HW2  594   2.509   2.885   0.575 -2.3092  1.5194  1.1488
+  199SOL     OW  595   2.989   0.044   1.688  0.0433  0.7028  0.4515
+  199SOL    HW1  596   2.974  -0.025   1.752 -0.8058  1.1131  0.6835
+  199SOL    HW2  597   3.001  -0.002   1.605 -1.0634  0.3220  0.5157
+  200SOL     OW  598   2.977   2.037   1.321  0.2674 -0.2315  0.4294
+  200SOL    HW1  599   3.065   2.000   1.310  0.4840  0.7367 -1.0422
+  200SOL    HW2  600   2.919   1.960   1.323  1.0272 -0.8097  0.2433
+  201SOL     OW  601   0.145   0.335   0.711  0.1710  0.2406  0.7096
+  201SOL    HW1  602   0.200   0.329   0.633  2.3731 -1.2771  2.3706
+  201SOL    HW2  603   0.100   0.250   0.716  0.2594  0.2276  1.2674
+  202SOL     OW  604   0.921   0.233   0.097 -0.3673 -0.0408 -0.5261
+  202SOL    HW1  605   0.867   0.154   0.101 -0.2547 -0.2402 -2.6968
+  202SOL    HW2  606   0.861   0.303   0.122 -1.1848 -0.4913 -1.1739
+  203SOL     OW  607   2.081   1.828   0.598  0.0963  0.0049 -0.0595
+  203SOL    HW1  608   2.154   1.813   0.658 -0.3268  0.0052  0.4492
+  203SOL    HW2  609   2.123   1.842   0.513  0.8913  3.5943  0.8861
+  204SOL     OW  610   1.350   1.977   2.392  0.1912  0.0864  0.2713
+  204SOL    HW1  611   1.419   1.916   2.366  0.3616  0.7402 -0.8057
+  204SOL    HW2  612   1.380   2.062   2.361 -1.4361  0.2668 -0.8245
+  205SOL     OW  613   2.849   0.489   0.214 -0.2002 -0.0333 -0.6235
+  205SOL    HW1  614   2.758   0.476   0.187  0.4759 -0.8493 -2.4814
+  205SOL    HW2  615   2.870   0.578   0.188  0.1511 -0.5001 -1.9390
+  206SOL     OW  616   1.934   2.661   0.953 -0.3349 -0.0602 -0.4933
+  206SOL    HW1  617   1.960   2.684   1.042  0.8539  3.9994 -1.8457
+  206SOL    HW2  618   1.841   2.682   0.949 -0.8714 -2.3473  0.4955
+  207SOL     OW  619   1.058   0.260   1.972 -0.0080 -0.6209 -0.1605
+  207SOL    HW1  620   1.115   0.336   1.974 -0.9688  0.0870  0.3366
+  207SOL    HW2  621   1.112   0.188   2.004  0.9148 -0.1543 -0.6987
+  208SOL     OW  622   0.514   2.743   2.773 -0.1954  0.3747  0.0890
+  208SOL    HW1  623   0.598   2.697   2.782 -1.2091 -1.7042 -0.9026
+  208SOL    HW2  624   0.489   2.730   2.681 -1.7321  0.7492  0.4491
+  209SOL     OW  625   1.254   2.524   0.557 -0.4640  0.0018 -0.4717
+  209SOL    HW1  626   1.251   2.579   0.478 -1.0727 -1.2561 -1.3168
+  209SOL    HW2  627   1.320   2.457   0.537  1.1074  1.0888  0.9980
+  210SOL     OW  628   0.629   0.358   0.813  0.2794  0.0349 -0.3588
+  210SOL    HW1  629   0.601   0.392   0.728  1.9360  0.6137 -0.6843
+  210SOL    HW2  630   0.657   0.436   0.862  0.5383 -0.4400  0.2476
+  211SOL     OW  631   0.430   1.117   2.007  0.0609  0.6542 -0.2732
+  211SOL    HW1  632   0.438   1.087   1.916 -0.9158  1.0376 -0.4790
+  211SOL    HW2  633   0.401   1.039   2.055  0.2719  0.5360 -0.3329
+  212SOL     OW  634   2.238   2.875   0.408 -0.6204 -0.0844 -0.5052
+  212SOL    HW1  635   2.215   2.916   0.491  0.9452 -1.2249  0.5135
+  212SOL    HW2  636   2.284   2.943   0.360 -1.2175  0.4762 -0.2847
+  213SOL     OW  637   0.818   2.737   0.702 -0.3600 -0.2115 -0.1398
+  213SOL    HW1  638   0.753   2.714   0.635  0.3583  2.4915 -1.7884
+  213SOL    HW2  639   0.902   2.737   0.655  0.3960 -0.2503  1.2041
+  214SOL     OW  640   1.275   0.692   2.536  0.1096 -0.2767 -0.1559
+  214SOL    HW1  641   1.184   0.695   2.564  0.5524 -0.0816  1.2820
+  214SOL    HW2  642   1.321   0.747   2.600  1.3377 -0.9737 -0.4533
+  215SOL     OW  643   1.099   1.176   0.558  0.8767  0.4108  0.6171
+  215SOL    HW1  644   1.103   1.261   0.601 -1.0129  0.2930  0.9941
+  215SOL    HW2  645   1.140   1.191   0.473  0.0747  1.8081  0.4824
+  216SOL     OW  646   2.613   0.326   2.307 -0.4081 -0.1954 -0.1694
+  216SOL    HW1  647   2.537   0.280   2.342 -0.1444  0.6063  1.4896
+  216SOL    HW2  648   2.669   0.340   2.384  1.7080 -1.3793 -1.4908
+  217SOL     OW  649   0.281   2.172   2.048  0.4624 -0.7910 -0.0037
+  217SOL    HW1  650   0.335   2.095   2.065  0.4890 -0.2495  2.4124
+  217SOL    HW2  651   0.309   2.235   2.113  0.1415  1.1222 -1.7314
+  218SOL     OW  652   1.594   1.562   1.692 -0.4111 -0.7988  0.0171
+  218SOL    HW1  653   1.571   1.506   1.766 -0.3546  1.9937  2.1596
+  218SOL    HW2  654   1.688   1.575   1.700 -0.5679  0.3247  0.0423
+  219SOL     OW  655   2.314   1.042   0.524 -0.2005 -0.3333  0.2829
+  219SOL    HW1  656   2.267   1.093   0.590 -1.1036 -3.0959  1.7508
+  219SOL    HW2  657   2.245   1.009   0.466  0.3924  0.0422 -0.6449
+  220SOL     OW  658   2.773   1.264   2.952 -0.3287  0.1892 -0.5949
+  220SOL    HW1  659   2.777   1.275   3.047  2.2453  0.3451 -0.7150
+  220SOL    HW2  660   2.692   1.306   2.926 -0.8022  0.5585  1.4876
+  221SOL     OW  661   0.868   1.801   0.202  0.1062 -0.6112  0.0639
+  221SOL    HW1  662   0.834   1.888   0.180 -0.7816 -0.1771  3.0741
+  221SOL    HW2  663   0.820   1.741   0.145  0.5863  0.8784 -1.8911
+  222SOL     OW  664   1.524   2.598   2.990  0.3509 -0.4513  0.1711
+  222SOL    HW1  665   1.549   2.646   3.070  1.4793 -0.5003 -0.1559
+  222SOL    HW2  666   1.598   2.611   2.931 -0.1811 -1.0266 -0.6082
+  223SOL     OW  667   1.317   2.978   2.619 -0.0661  0.1554 -0.4337
+  223SOL    HW1  668   1.389   3.031   2.655 -0.2170  1.4208 -2.0119
+  223SOL    HW2  669   1.361   2.909   2.569  0.0750  0.0418 -0.1568
+  224SOL     OW  670   1.224   0.379   0.628 -0.1745  0.1147  0.2588
+  224SOL    HW1  671   1.206   0.454   0.571  3.2404  1.1415  0.5366
+  224SOL    HW2  672   1.245   0.417   0.713  0.9125 -1.2075  0.5811
+  225SOL     OW  673   2.485   0.088   2.873  0.8000 -0.0147 -0.4070
+  225SOL    HW1  674   2.429   0.033   2.819  0.1063 -0.7197  1.0091
+  225SOL    HW2  675   2.425   0.125   2.939  2.0049 -0.9018  1.2026
+  226SOL     OW  676   0.747   0.389   2.700 -0.2594 -0.3955 -0.0533
+  226SOL    HW1  677   0.792   0.348   2.627  0.2454  0.6384 -0.3223
+  226SOL    HW2  678   0.815   0.438   2.746 -0.0042 -3.4823  2.8249
+  227SOL     OW  679   1.596   0.358   2.215 -0.1991  0.9917 -1.0166
+  227SOL    HW1  680   1.608   0.417   2.289 -0.6751  0.3770 -0.4414
+  227SOL    HW2  681   1.524   0.300   2.243  0.7380 -0.2947 -1.2893
+  228SOL     OW  682   0.097   1.181   1.872  0.7940 -0.6636 -0.0206
+  228SOL    HW1  683   0.082   1.240   1.946  0.2449 -0.2549 -0.4546
+  228SOL    HW2  684   0.015   1.132   1.863  0.4683  0.0543 -1.0361
+  229SOL     OW  685   2.658   0.683   2.482 -0.2002  0.0099  0.4744
+  229SOL    HW1  686   2.726   0.731   2.528 -0.3560  0.7870 -0.1102
+  229SOL    HW2  687   2.686   0.591   2.487  0.7072  0.2903  0.5246
+  230SOL     OW  688   1.179   1.561   1.482 -0.1506 -0.4423 -0.4925
+  230SOL    HW1  689   1.209   1.643   1.445  0.3599 -0.6557 -0.5541
+  230SOL    HW2  690   1.259   1.509   1.494 -0.0942  0.4753  2.9875
+  231SOL     OW  691   2.623   2.211   0.122 -0.0676 -0.9213 -0.2260
+  231SOL    HW1  692   2.584   2.178   0.041 -0.3833  0.1477 -0.5097
+  231SOL    HW2  693   2.552   2.211   0.185 -0.0321 -0.9173 -0.1855
+  232SOL     OW  694   1.390   0.691   0.877  0.0928  0.5200 -0.2876
+  232SOL    HW1  695   1.408   0.754   0.946 -1.1286 -1.1086  1.5208
+  232SOL    HW2  696   1.298   0.704   0.854  0.1769  0.2664 -0.7775
+  233SOL     OW  697   0.346   1.107   1.741  0.4996  0.4402 -0.4688
+  233SOL    HW1  698   0.260   1.112   1.783 -0.3125  0.6860 -2.1215
+  233SOL    HW2  699   0.375   1.198   1.735  1.4420  0.2442  1.2531
+  234SOL     OW  700   2.534   2.218   2.033  0.4721  0.2963 -0.0730
+  234SOL    HW1  701   2.585   2.182   1.961  2.1081 -0.1919  1.3242
+  234SOL    HW2  702   2.595   2.277   2.079 -0.2130 -0.2803  1.5940
+  235SOL     OW  703   1.108   1.457   0.615  0.0302  0.1592 -0.7264
+  235SOL    HW1  704   1.039   1.496   0.560  0.1977 -0.3328 -1.2886
+  235SOL    HW2  705   1.190   1.487   0.575  0.1285  1.5447  0.5291
+  236SOL     OW  706   1.402   0.702   1.396 -0.5085 -0.3316  0.1723
+  236SOL    HW1  707   1.348   0.641   1.446 -1.0277 -1.0696 -1.2811
+  236SOL    HW2  708   1.427   0.769   1.459 -1.3277 -1.1480  1.3747
+  237SOL     OW  709   2.087   2.642   0.722 -0.7643 -0.4404  0.4847
+  237SOL    HW1  710   2.177   2.659   0.750 -0.7016  2.8521 -1.7226
+  237SOL    HW2  711   2.037   2.634   0.804  1.0412  0.3946  1.6539
+  238SOL     OW  712   2.227   1.683   1.673 -0.1039  0.0812  0.0036
+  238SOL    HW1  713   2.316   1.651   1.658  0.0844  0.7244 -0.2679
+  238SOL    HW2  714   2.172   1.630   1.616  0.0664 -0.5350  0.4093
+  239SOL     OW  715   2.034   1.972   2.485  0.3056  0.6722  0.5997
+  239SOL    HW1  716   2.124   1.950   2.509  0.4161  0.8206  0.3244
+  239SOL    HW2  717   1.983   1.895   2.510  0.0670  1.7260  3.4264
+  240SOL     OW  718   1.219   2.710   0.341 -0.3884  0.3143  0.0563
+  240SOL    HW1  719   1.163   2.638   0.310 -0.9088  0.6960  0.1017
+  240SOL    HW2  720   1.233   2.764   0.263  0.7655 -0.3344 -0.1867
+  241SOL     OW  721   2.072   2.854   2.561  0.1868  0.4985  0.6186
+  241SOL    HW1  722   2.072   2.924   2.495  1.0670  0.5348  0.6599
+  241SOL    HW2  723   2.004   2.793   2.531  0.7453  0.3938 -0.4466
+  242SOL     OW  724   2.161   0.806   1.438 -0.1101  0.0836 -0.6287
+  242SOL    HW1  725   2.238   0.852   1.472 -1.0557  1.7129 -0.7116
+  242SOL    HW2  726   2.098   0.808   1.509 -1.1612  1.2886 -1.6033
+  243SOL     OW  727   2.137   1.914   1.371  0.2865 -0.2252  0.1847
+  243SOL    HW1  728   2.086   1.995   1.375  0.0812 -0.2555 -1.8234
+  243SOL    HW2  729   2.214   1.931   1.425  0.6829  1.6228 -0.9612
+  244SOL     OW  730   0.452   2.862   0.278 -0.6593 -0.5901 -0.3790
+  244SOL    HW1  731   0.457   2.858   0.183  1.5696 -0.2033 -0.2788
+  244SOL    HW2  732   0.389   2.794   0.302 -0.9085 -0.9628 -2.0864
+  245SOL     OW  733   1.785   1.664   1.068  0.2594 -0.1184  0.0513
+  245SOL    HW1  734   1.787   1.578   1.025  1.8953 -0.2520  0.4023
+  245SOL    HW2  735   1.868   1.669   1.115 -0.1237  1.3949  0.5514
+  246SOL     OW  736   0.896   1.207   2.292  0.0456 -0.2710  0.4008
+  246SOL    HW1  737   0.803   1.229   2.290 -0.0263 -0.7739 -1.4180
+  246SOL    HW2  738   0.928   1.245   2.374 -1.8554 -0.4789  1.2292
+  247SOL     OW  739   1.569   1.379   0.399  0.0488  0.3131  0.0689
+  247SOL    HW1  740   1.585   1.459   0.350  0.8084  0.3191  0.3134
+  247SOL    HW2  741   1.567   1.407   0.491  1.1401 -0.0604  0.2150
+  248SOL     OW  742   1.781   2.587   0.477  0.3728 -0.1072  0.2584
+  248SOL    HW1  743   1.823   2.641   0.410 -0.4053 -2.2097 -1.8985
+  248SOL    HW2  744   1.853   2.535   0.514  0.4185 -0.5260 -0.4247
+  249SOL     OW  745   2.851   1.823   1.457  0.2519 -1.0698 -0.0567
+  249SOL    HW1  746   2.856   1.805   1.551  0.4591  0.6797  0.2602
+  249SOL    HW2  747   2.790   1.757   1.423  0.1887 -1.6981  1.2997
+  250SOL     OW  748   2.769   0.856   1.999  0.0948  0.9198  0.4296
+  250SOL    HW1  749   2.811   0.845   2.084  0.7031 -1.8601 -0.2227
+  250SOL    HW2  750   2.697   0.793   1.999  0.7582  0.1399 -1.5115
+  251SOL     OW  751   2.922   0.228   2.459  0.4481 -0.8810  0.1076
+  251SOL    HW1  752   2.891   0.175   2.385 -2.3605  0.1131  0.5796
+  251SOL    HW2  753   2.925   0.166   2.532 -0.6146 -0.9225  0.1164
+  252SOL     OW  754   0.740   2.386   0.812  0.2944 -0.7961 -0.7078
+  252SOL    HW1  755   0.719   2.462   0.757 -0.0580  0.1781  0.7868
+  252SOL    HW2  756   0.834   2.395   0.829  0.0405 -0.1312  0.3389
+  253SOL     OW  757   0.499   1.084   1.222 -0.3360 -0.0338  0.0826
+  253SOL    HW1  758   0.571   1.145   1.235 -0.8509  0.9077 -1.4590
+  253SOL    HW2  759   0.425   1.124   1.268  0.0987 -0.9896  1.6468
+  254SOL     OW  760   2.528   0.176   1.617  0.1173 -0.1782  0.2043
+  254SOL    HW1  761   2.600   0.204   1.673  1.5082 -1.7750 -0.7927
+  254SOL    HW2  762   2.457   0.157   1.678  0.1021  2.6654  1.0990
+  255SOL     OW  763   0.274   2.859   0.621  0.0436 -0.3608  0.6914
+  255SOL    HW1  764   0.233   2.779   0.587 -1.0535  0.8103 -0.7490
+  255SOL    HW2  765   0.368   2.844   0.610 -0.1830 -1.2253 -0.1312
+  256SOL     OW  766   2.708   0.110   2.018 -0.0322 -0.2984  0.3204
+  256SOL    HW1  767   2.641   0.047   1.992  0.9185 -0.6758 -1.2211
+  256SOL    HW2  768   2.754   0.069   2.091  0.0898 -1.0530 -0.1862
+  257SOL     OW  769   1.718   0.494   2.852  0.5176  0.1451  0.1396
+  257SOL    HW1  770   1.718   0.419   2.794  0.8768 -0.1571  0.5327
+  257SOL    HW2  771   1.761   0.564   2.802 -1.5082  0.6237 -0.9232
+  258SOL     OW  772   2.073   0.540   1.083  0.6249 -0.0898  0.3398
+  258SOL    HW1  773   2.065   0.626   1.124 -2.4893 -0.5244  0.6479
+  258SOL    HW2  774   2.109   0.484   1.152 -0.4966  0.1962  1.1640
+  259SOL     OW  775   0.544   2.169   2.950 -0.4648  0.0489  0.1110
+  259SOL    HW1  776   0.474   2.203   2.894 -0.0026  1.0557  0.1439
+  259SOL    HW2  777   0.596   2.113   2.892 -2.3536 -2.1390  0.5333
+  260SOL     OW  778   1.697   0.986   1.232  0.1944 -0.2458  0.4375
+  260SOL    HW1  779   1.633   0.961   1.298 -0.0121 -1.1776 -0.1147
+  260SOL    HW2  780   1.697   1.082   1.234 -0.1666 -0.2678  1.4404
+  261SOL     OW  781   2.808   0.046   0.305  0.0027 -0.4070 -0.5073
+  261SOL    HW1  782   2.782   0.031   0.214 -1.5701 -2.3946  0.2762
+  261SOL    HW2  783   2.839  -0.039   0.335 -2.3609 -0.4844  1.7553
+  262SOL     OW  784   0.460   2.625   0.963  0.6185 -0.1637  0.6497
+  262SOL    HW1  785   0.552   2.644   0.949  0.2478  2.4972  1.8147
+  262SOL    HW2  786   0.421   2.626   0.876  0.9385  2.6479  0.5348
+  263SOL     OW  787   2.841   0.281   1.481  0.0263 -0.4863  0.2744
+  263SOL    HW1  788   2.909   0.344   1.502 -0.0615 -0.5101  0.6219
+  263SOL    HW2  789   2.836   0.223   1.557 -2.1126  1.0400  1.3135
+  264SOL     OW  790   1.205   2.854   1.580 -0.1329 -0.2398  0.1414
+  264SOL    HW1  791   1.139   2.827   1.516  0.9664 -0.5007 -0.8758
+  264SOL    HW2  792   1.154   2.880   1.657 -1.4524  0.0866 -0.8442
+  265SOL     OW  793   1.505   1.281   2.530  0.5972 -0.3140 -0.0026
+  265SOL    HW1  794   1.535   1.293   2.620  0.1941 -2.1178  0.3785
+  265SOL    HW2  795   1.548   1.200   2.500 -0.8945 -0.8644 -0.7104
+  266SOL     OW  796   0.386   0.356   2.887  0.3812 -0.3358 -0.4061
+  266SOL    HW1  797   0.446   0.288   2.918 -1.2424 -1.6804 -0.1970
+  266SOL    HW2  798   0.324   0.309   2.831 -2.3907  0.5059  1.9209
+  267SOL     OW  799   0.333   0.798   0.022 -0.0582  0.6618 -0.3128
+  267SOL    HW1  800   0.380   0.881   0.010  0.3228  0.6790  1.2879
+  267SOL    HW2  801   0.357   0.770   0.110  1.5794 -2.2679 -1.6739
+  268SOL     OW  802   0.049   1.614   2.543  0.3526 -0.3815 -0.3364
+  268SOL    HW1  803  -0.028   1.580   2.588  0.9056  0.6679  1.3813
+  268SOL    HW2  804   0.113   1.543   2.551 -0.3183 -1.1065 -1.4444
+  269SOL     OW  805   0.341   1.587   0.603 -0.3251  0.2194  0.5722
+  269SOL    HW1  806   0.281   1.513   0.595 -0.6489  0.4878  0.5399
+  269SOL    HW2  807   0.287   1.659   0.636 -0.5012  1.0863 -1.6088
+  270SOL     OW  808   1.528   0.141   2.951 -1.2379 -0.1124  0.2124
+  270SOL    HW1  809   1.609   0.168   2.994 -1.3038  1.4312 -0.6170
+  270SOL    HW2  810   1.543   0.159   2.858 -2.1855 -0.7059 -0.0666
+  271SOL     OW  811   0.526   0.751   2.111 -0.3494  0.3824  0.2698
+  271SOL    HW1  812   0.513   0.663   2.147 -2.5048  0.4395 -0.3688
+  271SOL    HW2  813   0.604   0.783   2.156 -0.2088 -1.4195  1.3484
+  272SOL     OW  814   2.810   2.683   2.235 -0.0706  0.5259 -0.1664
+  272SOL    HW1  815   2.863   2.725   2.168 -0.6730  1.1343 -0.2664
+  272SOL    HW2  816   2.740   2.746   2.253 -1.0197 -0.4742 -0.3734
+  273SOL     OW  817   0.627   2.946   0.990 -0.0257 -0.2555 -0.0963
+  273SOL    HW1  818   0.604   2.968   0.900  0.7924 -1.4254 -0.5848
+  273SOL    HW2  819   0.686   3.017   1.017  0.4242 -0.3698 -0.7698
+  274SOL     OW  820   1.221   2.468   2.664  0.0632  0.0402 -0.2626
+  274SOL    HW1  821   1.218   2.548   2.717  0.0771  0.9033 -1.5532
+  274SOL    HW2  822   1.168   2.405   2.713 -1.1127  1.2670  0.0722
+  275SOL     OW  823   2.243   2.397   1.046  0.2020  0.1713 -0.2269
+  275SOL    HW1  824   2.271   2.322   1.099 -0.1813 -0.4058 -0.8344
+  275SOL    HW2  825   2.164   2.365   1.001  0.3225  0.7814 -0.8570
+  276SOL     OW  826   2.602   1.971   1.680  0.2453  0.0896  0.4214
+  276SOL    HW1  827   2.646   2.056   1.690 -0.6192  0.6887 -0.8983
+  276SOL    HW2  828   2.665   1.907   1.713  1.4724  1.1142  0.0605
+  277SOL     OW  829   0.699   2.148   1.607 -0.6941  0.4795 -0.3409
+  277SOL    HW1  830   0.713   2.053   1.610  0.6872  0.6924 -0.0031
+  277SOL    HW2  831   0.614   2.161   1.649  0.4137 -0.3400  2.1413
+  278SOL     OW  832   0.811   2.641   2.023 -0.4022 -0.4331  0.2988
+  278SOL    HW1  833   0.782   2.646   1.932 -0.5738  1.7561  0.4647
+  278SOL    HW2  834   0.743   2.590   2.067  0.2090 -2.3774 -1.0102
+  279SOL     OW  835   2.652   0.241   2.696 -0.4393 -0.0856  0.4473
+  279SOL    HW1  836   2.634   0.170   2.758 -1.7192 -0.1451  0.0027
+  279SOL    HW2  837   2.585   0.307   2.715 -0.7392 -0.0398 -0.7839
+  280SOL     OW  838   0.436   2.645   2.087  0.3073 -0.1359  0.5609
+  280SOL    HW1  839   0.458   2.596   2.008 -2.2277  1.2837 -1.0261
+  280SOL    HW2  840   0.378   2.587   2.136  1.3427 -0.7468  1.0645
+  281SOL     OW  841   2.287   2.992   2.693 -0.3287 -0.7788 -0.0097
+  281SOL    HW1  842   2.214   2.929   2.695 -0.3515 -0.8323 -2.4466
+  281SOL    HW2  843   2.250   3.069   2.648  0.6079 -0.1638  0.2841
+  282SOL     OW  844   2.326   2.022   1.569  0.2158 -0.1264  0.1444
+  282SOL    HW1  845   2.406   1.989   1.611  0.5112 -0.3402 -0.5807
+  282SOL    HW2  846   2.300   2.097   1.623  0.0465 -0.9922  1.2675
+  283SOL     OW  847   1.210   2.549   1.740  0.0390 -0.1116 -0.5487
+  283SOL    HW1  848   1.305   2.558   1.745 -0.1909  2.2491 -0.6378
+  283SOL    HW2  849   1.193   2.528   1.648  0.2424  1.8355 -1.0147
+  284SOL     OW  850   3.029   1.102   0.951 -0.4443  0.2449  0.1840
+  284SOL    HW1  851   3.005   1.033   0.888 -1.6645 -0.1905  1.1325
+  284SOL    HW2  852   2.954   1.109   1.009  0.5193  0.9524  1.3483
+  285SOL     OW  853   1.017   2.385   2.276 -0.1539 -0.3352 -0.1470
+  285SOL    HW1  854   1.066   2.402   2.196  0.6568 -2.9122 -0.1992
+  285SOL    HW2  855   0.929   2.417   2.258  0.1497 -0.4812 -1.8936
+  286SOL     OW  856   0.842   1.819   2.902 -1.0251  0.1193 -0.4142
+  286SOL    HW1  857   0.847   1.764   2.824  0.8577 -0.9064  0.4331
+  286SOL    HW2  858   0.749   1.819   2.926 -1.4159 -0.5359 -1.9557
+  287SOL     OW  859   2.782   0.216   1.767 -0.3124 -0.1444 -0.1248
+  287SOL    HW1  860   2.759   0.169   1.847 -1.5445 -1.9712 -1.5479
+  287SOL    HW2  861   2.863   0.174   1.737 -0.4122 -0.1482 -0.3907
+  288SOL     OW  862   0.022   0.440   1.908 -0.1933 -0.4165 -0.4436
+  288SOL    HW1  863   0.068   0.515   1.946 -0.1112 -0.3456 -0.6863
+  288SOL    HW2  864  -0.067   0.446   1.945 -0.5681  0.5506 -1.4942
+  289SOL     OW  865   1.807   2.476   1.731 -0.4322 -0.0785 -0.0801
+  289SOL    HW1  866   1.854   2.423   1.795  0.4293  0.3252 -0.3686
+  289SOL    HW2  867   1.795   2.417   1.656 -0.7755 -0.3161  0.1593
+  290SOL     OW  868   2.246   2.915   2.001  0.3550  0.3986  0.0297
+  290SOL    HW1  869   2.300   2.989   2.028  2.1807 -0.2728 -1.7623
+  290SOL    HW2  870   2.300   2.838   2.020  0.0983 -0.2642 -2.0149
+  291SOL     OW  871   0.945   2.849   0.418  0.1014  0.2221  0.4006
+  291SOL    HW1  872   1.007   2.909   0.377  0.1023  0.3767  0.6291
+  291SOL    HW2  873   0.989   2.764   0.417  0.1506  0.2508  0.1920
+  292SOL     OW  874   1.714   1.591   2.030  0.1339 -0.1043 -0.0659
+  292SOL    HW1  875   1.730   1.662   2.092 -0.4614 -0.7630  0.8422
+  292SOL    HW2  876   1.771   1.520   2.060 -0.1406 -0.4151 -0.2823
+  293SOL     OW  877   2.861   1.301   1.359 -0.0285  0.3101  0.5080
+  293SOL    HW1  878   2.796   1.358   1.401 -0.7232  0.7510 -1.1698
+  293SOL    HW2  879   2.932   1.360   1.336 -1.0830  0.6383 -1.9309
+  294SOL     OW  880   2.315   1.423   1.487  0.5299 -0.0220 -0.3008
+  294SOL    HW1  881   2.396   1.459   1.524  1.1720 -1.0642 -0.7085
+  294SOL    HW2  882   2.318   1.330   1.508 -0.5426 -0.0735 -0.3607
+  295SOL     OW  883   1.166   2.013   0.167  0.1581  0.2972  0.0675
+  295SOL    HW1  884   1.152   1.971   0.082  2.5204  0.4474 -0.4180
+  295SOL    HW2  885   1.235   1.961   0.208  0.0661  1.1157  1.2509
+  296SOL     OW  886   1.817   0.639   2.514  0.0205  0.1087 -0.1160
+  296SOL    HW1  887   1.890   0.644   2.454 -0.6282  2.9037 -0.6752
+  296SOL    HW2  888   1.852   0.671   2.598 -0.2928  0.4912 -0.1307
+  297SOL     OW  889   2.169   2.313   0.431 -0.3848 -0.8035  0.1696
+  297SOL    HW1  890   2.244   2.270   0.472  0.2722  1.0011  0.8431
+  297SOL    HW2  891   2.207   2.371   0.365 -1.2004 -0.0640  0.3452
+  298SOL     OW  892   1.910   0.364   1.335 -0.0725 -0.3401  0.3033
+  298SOL    HW1  893   1.895   0.280   1.378  0.5473 -0.7434 -0.2716
+  298SOL    HW2  894   2.005   0.377   1.339  0.0215 -0.5120 -1.1912
+  299SOL     OW  895   1.393   2.024   0.386  0.4325 -0.2324 -0.1930
+  299SOL    HW1  896   1.480   2.017   0.424  0.5574  0.3462 -0.3705
+  299SOL    HW2  897   1.333   2.013   0.460  0.6695  0.3485  0.0858
+  300SOL     OW  898   1.572   1.014   2.422 -0.5824 -0.8229  0.1908
+  300SOL    HW1  899   1.550   0.972   2.505 -0.6893 -0.7079  0.2197
+  300SOL    HW2  900   1.540   0.954   2.355 -0.4817 -0.9232  0.2338
+  301SOL     OW  901   1.290   0.953   1.806  0.0087 -0.1040  0.1454
+  301SOL    HW1  902   1.312   1.046   1.802 -2.7743  0.5020 -0.6763
+  301SOL    HW2  903   1.291   0.924   1.715 -1.8368 -0.2478  0.1655
+  302SOL     OW  904   2.186   1.102   2.454 -0.0278  0.1129 -0.6490
+  302SOL    HW1  905   2.244   1.131   2.384 -0.6662  1.9420 -0.4116
+  302SOL    HW2  906   2.245   1.079   2.526  0.4648  0.7768 -0.8439
+  303SOL     OW  907   1.316   0.354   1.225  0.0374  0.4232  0.0226
+  303SOL    HW1  908   1.257   0.280   1.244  0.3221 -0.1100 -1.1643
+  303SOL    HW2  909   1.369   0.363   1.304 -1.7003  0.9508  1.1286
+  304SOL     OW  910   0.668   1.755   0.483  0.2002  0.2737 -0.4770
+  304SOL    HW1  911   0.612   1.693   0.436 -0.3998  1.6036 -1.4941
+  304SOL    HW2  912   0.722   1.699   0.539 -0.0420 -1.2090 -1.7094
+  305SOL     OW  913   1.174   1.846   2.546 -0.4231 -0.1820 -1.1041
+  305SOL    HW1  914   1.240   1.874   2.483  0.8788 -0.5267  0.1042
+  305SOL    HW2  915   1.147   1.926   2.590  0.5049 -0.2796 -0.3445
+  306SOL     OW  916   0.802   2.745   2.591 -0.2431  0.3420  0.3494
+  306SOL    HW1  917   0.764   2.684   2.528 -0.2401  0.7577 -0.0562
+  306SOL    HW2  918   0.766   2.830   2.565  1.1665  0.7680 -0.1713
+  307SOL     OW  919   1.592   0.018   0.209  0.5773  0.1280 -0.0827
+  307SOL    HW1  920   1.518   0.062   0.167 -0.2128 -1.0322  0.1194
+  307SOL    HW2  921   1.553  -0.032   0.280  1.6556  0.1796  0.5430
+  308SOL     OW  922   0.174   1.485   1.645  0.4340  0.5258  0.1370
+  308SOL    HW1  923   0.247   1.434   1.680 -0.0428  0.5051  1.0949
+  308SOL    HW2  924   0.097   1.453   1.693 -0.3396 -0.1312 -1.5490
+  309SOL     OW  925   1.589   0.178   2.698  0.1087  0.3856  0.1895
+  309SOL    HW1  926   1.624   0.099   2.656  0.0350 -0.4554  1.6810
+  309SOL    HW2  927   1.631   0.251   2.652 -0.4302 -0.5234 -1.7231
+  310SOL     OW  928   2.083   0.329   2.068  0.5521 -0.1489  0.6984
+  310SOL    HW1  929   2.094   0.337   1.973  0.0908 -0.4347  0.6162
+  310SOL    HW2  930   2.162   0.283   2.097  0.3904 -0.6492  0.3600
+  311SOL     OW  931   1.543   1.927   1.763  0.2316 -0.2631  0.1330
+  311SOL    HW1  932   1.475   1.941   1.829 -0.1756  0.6012 -0.4651
+  311SOL    HW2  933   1.494   1.903   1.684  0.6852 -0.7707  0.0107
+  312SOL     OW  934   0.825   2.862   2.233  0.3961 -0.9464  0.5406
+  312SOL    HW1  935   0.914   2.854   2.267  0.6454  0.9331  0.3468
+  312SOL    HW2  936   0.824   2.806   2.155  2.0629  0.7408 -0.7270
+  313SOL     OW  937   0.533   0.236   1.798  0.3317  0.1369 -0.6461
+  313SOL    HW1  938   0.578   0.224   1.714  0.6118  0.5835 -0.5616
+  313SOL    HW2  939   0.504   0.327   1.797  0.2444  0.1123 -0.3022
+  314SOL     OW  940   0.170   1.881   2.891  0.8911  0.3296  0.2676
+  314SOL    HW1  941   0.225   1.869   2.814  0.4504 -0.7804  0.1328
+  314SOL    HW2  942   0.113   1.804   2.893 -1.2526  1.9007 -0.0824
+  315SOL     OW  943   2.435   0.011   0.239  0.1170  0.6012  0.5522
+  315SOL    HW1  944   2.420   0.080   0.303 -1.6993  0.3821  0.3652
+  315SOL    HW2  945   2.417   0.052   0.155 -4.2664 -1.0479  0.6956
+  316SOL     OW  946   0.535   1.423   0.039 -0.2983 -0.2573  0.1834
+  316SOL    HW1  947   0.546   1.336   0.001  1.4587 -0.0620  0.2609
+  316SOL    HW2  948   0.608   1.432   0.100  0.7584  2.1261 -1.4393
+  317SOL     OW  949   2.099   1.370   1.148 -0.3628 -0.4840  0.0778
+  317SOL    HW1  950   2.085   1.276   1.138  0.0216 -0.4841 -0.4236
+  317SOL    HW2  951   2.043   1.394   1.222 -1.2147 -0.9235 -0.4268
+  318SOL     OW  952   0.318   2.332   1.446 -0.8428  0.3334 -0.2766
+  318SOL    HW1  953   0.252   2.263   1.445 -0.3628 -0.1322  0.2422
+  318SOL    HW2  954   0.379   2.310   1.376  0.2230 -0.6573  0.9605
+  319SOL     OW  955   2.957   2.282   2.569 -0.5365 -0.4777 -0.0057
+  319SOL    HW1  956   2.991   2.213   2.627 -0.8132 -0.6805 -0.0798
+  319SOL    HW2  957   2.942   2.357   2.627 -0.2742 -0.4959  0.0842
+  320SOL     OW  958   3.002   2.178   2.329 -0.1885  0.2500 -0.3668
+  320SOL    HW1  959   2.992   2.224   2.413  1.6513  2.2652 -1.2622
+  320SOL    HW2  960   2.912   2.167   2.297 -0.8347  0.2685  1.4399
+  321SOL     OW  961   0.276   2.011   0.423  0.0105  0.2946  0.1754
+  321SOL    HW1  962   0.364   1.991   0.457  0.2333  1.4163  0.2741
+  321SOL    HW2  963   0.288   2.017   0.328  0.4287  2.3824  0.3557
+  322SOL     OW  964   2.589   0.639   1.948  0.1497 -0.0576  0.3526
+  322SOL    HW1  965   2.645   0.585   1.893 -0.9294 -2.5753  1.7333
+  322SOL    HW2  966   2.517   0.666   1.891  1.0306  0.4585 -0.5092
+  323SOL     OW  967   2.366   1.335   0.224 -0.0879  0.5499 -0.1899
+  323SOL    HW1  968   2.355   1.353   0.318  1.5949 -0.1921  0.1443
+  323SOL    HW2  969   2.372   1.422   0.184 -1.3582  0.8739  0.3361
+  324SOL     OW  970   0.063   2.270   0.963 -0.6337 -0.1231  0.4278
+  324SOL    HW1  971   0.101   2.356   0.978 -1.4562  0.0018  1.7496
+  324SOL    HW2  972  -0.017   2.269   1.015 -0.7439 -1.6011  0.2369
+  325SOL     OW  973   0.504   0.907   0.714 -0.0591 -0.0107  0.2098
+  325SOL    HW1  974   0.456   0.829   0.742  0.3420 -0.3015  0.0832
+  325SOL    HW2  975   0.493   0.969   0.786  0.3427 -0.4941  0.6894
+  326SOL     OW  976   2.822   1.529   2.699 -0.2037  0.0540 -0.1384
+  326SOL    HW1  977   2.748   1.574   2.739  0.4301  1.1053 -0.1245
+  326SOL    HW2  978   2.889   1.526   2.768 -0.4701 -1.4722  0.0587
+  327SOL     OW  979   0.028   2.763   2.112 -0.0115 -0.5063 -0.0628
+  327SOL    HW1  980   0.069   2.808   2.187 -0.4708 -0.7251  0.3138
+  327SOL    HW2  981   0.064   2.675   2.115 -0.1316 -0.5529 -0.0286
+  328SOL     OW  982   1.150   0.089   2.264  0.0673 -0.2533 -0.3961
+  328SOL    HW1  983   1.122   0.095   2.356 -2.7650  0.5511 -1.3326
+  328SOL    HW2  984   1.151  -0.005   2.245  0.6031 -0.4204  0.4172
+  329SOL     OW  985   0.670   2.011   2.743 -0.4942 -0.0896  0.0237
+  329SOL    HW1  986   0.674   2.081   2.678  2.5895 -0.4709 -0.2069
+  329SOL    HW2  987   0.694   1.932   2.695  1.8467 -0.3632  1.6186
+  330SOL     OW  988   0.179   1.013   1.154  0.3906 -0.2718 -0.0344
+  330SOL    HW1  989   0.116   1.041   1.089 -0.4657 -2.3111 -0.0829
+  330SOL    HW2  990   0.188   1.087   1.214 -1.5167  0.2443 -0.4214
+  331SOL     OW  991   1.392   0.412   0.976 -0.4925 -0.6098 -0.3649
+  331SOL    HW1  992   1.390   0.501   0.943 -0.4094  0.0829  1.4880
+  331SOL    HW2  993   1.353   0.418   1.063  1.4569 -2.0263  0.6083
+  332SOL     OW  994   2.175   0.301   0.768  0.0079  0.0203  0.3200
+  332SOL    HW1  995   2.128   0.247   0.704  1.4404 -0.8892  0.0426
+  332SOL    HW2  996   2.117   0.302   0.844 -1.7615  1.2147 -1.0168
+  333SOL     OW  997   1.432   1.699   2.334  0.3606  0.6956  0.9819
+  333SOL    HW1  998   1.427   1.649   2.416 -1.5866  2.4376  1.9208
+  333SOL    HW2  999   1.384   1.646   2.270  1.2440 -0.3042  1.1495
+  334SOL     OW 1000   0.380   0.427   1.362 -0.1470  0.2543  0.3537
+  334SOL    HW1 1001   0.292   0.394   1.376 -0.7707  1.4006 -0.8578
+  334SOL    HW2 1002   0.381   0.511   1.407 -1.3540  2.7596 -4.2353
+  335SOL     OW 1003   0.166   0.962   0.421 -0.3803 -0.1801 -0.0047
+  335SOL    HW1 1004   0.185   1.047   0.461 -0.2323 -0.7153  1.0631
+  335SOL    HW2 1005   0.174   0.978   0.327 -0.6274  1.0581  0.1795
+  336SOL     OW 1006   2.300   2.219   1.812  0.5993  0.0106  0.1601
+  336SOL    HW1 1007   2.350   2.195   1.890 -1.0455  1.3706  1.6350
+  336SOL    HW2 1008   2.220   2.258   1.847 -0.8042 -0.7781 -2.2031
+  337SOL     OW 1009   0.984   1.593   0.360  0.2599 -0.4091  0.4161
+  337SOL    HW1 1010   0.960   1.657   0.293 -0.5182 -2.3510 -1.1610
+  337SOL    HW2 1011   0.909   1.592   0.420  0.9589  1.3306  1.3122
+  338SOL     OW 1012   1.293   1.130   0.813  0.0667  0.3593 -0.0479
+  338SOL    HW1 1013   1.306   1.171   0.728  0.0524 -0.2225 -0.3288
+  338SOL    HW2 1014   1.337   1.188   0.875  0.2746  0.6557 -0.4792
+  339SOL     OW 1015   2.229   0.666   2.135  0.2497  0.0993 -0.0858
+  339SOL    HW1 1016   2.303   0.670   2.196 -0.1246  0.7844  0.3287
+  339SOL    HW2 1017   2.261   0.706   2.055  0.4944 -0.0676 -0.0688
+  340SOL     OW 1018   1.417   2.169   2.970  0.0473 -0.4413  0.3086
+  340SOL    HW1 1019   1.417   2.263   2.990  1.4904 -0.2172 -0.7424
+  340SOL    HW2 1020   1.457   2.128   3.047 -0.1661 -0.1491  0.5724
+  341SOL     OW 1021   2.476   0.339   1.067  0.2358 -0.1207 -0.1464
+  341SOL    HW1 1022   2.536   0.288   1.121  1.1891  0.5785 -0.5140
+  341SOL    HW2 1023   2.485   0.429   1.100 -0.4944  0.0090 -0.3060
+  342SOL     OW 1024   1.713   2.311   0.085  0.3556 -0.4060 -0.0845
+  342SOL    HW1 1025   1.778   2.371   0.047  1.4006 -1.9357 -0.6788
+  342SOL    HW2 1026   1.687   2.355   0.167  1.1519  0.8584 -0.4978
+  343SOL     OW 1027   0.048   1.925   0.491  0.5238 -0.6147 -0.0580
+  343SOL    HW1 1028   0.134   1.959   0.468 -0.2070  1.3245  0.1303
+  343SOL    HW2 1029   0.054   1.907   0.585  0.3861  0.4367  0.1428
+  344SOL     OW 1030   1.146   1.717   0.055  0.0036  0.1320 -0.5622
+  344SOL    HW1 1031   1.065   1.732   0.104  0.6785  0.4364  0.4470
+  344SOL    HW2 1032   1.116   1.682  -0.029 -1.1243 -0.7883  0.2148
+  345SOL     OW 1033   1.592   2.771   1.000 -0.2826 -0.1609 -0.1934
+  345SOL    HW1 1034   1.624   2.842   0.945  1.5159 -1.2635 -0.5696
+  345SOL    HW2 1035   1.501   2.759   0.974  0.1029  0.4016 -1.7956
+  346SOL     OW 1036   2.258   0.831   1.131 -0.1717  0.0415 -0.5433
+  346SOL    HW1 1037   2.253   0.926   1.128  0.0848  0.0977  0.6890
+  346SOL    HW2 1038   2.237   0.809   1.222  2.1602 -0.9287 -0.2383
+  347SOL     OW 1039   1.209   1.198   1.452 -0.1077 -0.2192  0.2234
+  347SOL    HW1 1040   1.123   1.201   1.409 -0.2819  1.4700  0.6937
+  347SOL    HW2 1041   1.270   1.230   1.385  0.1070 -1.0402  0.0238
+  348SOL     OW 1042   0.167   0.184   1.798  1.1519 -0.1971  0.0033
+  348SOL    HW1 1043   0.134   0.262   1.842  1.6678 -0.4317  0.8015
+  348SOL    HW2 1044   0.089   0.132   1.779  0.9800 -0.3121  1.0433
+  349SOL     OW 1045   1.093   2.824   1.131 -0.2625  0.2778  0.0649
+  349SOL    HW1 1046   1.036   2.884   1.083 -0.4101 -0.8267 -1.1348
+  349SOL    HW2 1047   1.093   2.744   1.079  1.9230  0.0870  0.3800
+  350SOL     OW 1048   2.022   2.299   0.896 -0.0572  0.1555 -0.0752
+  350SOL    HW1 1049   1.955   2.306   0.828  1.2677 -1.5776 -1.5412
+  350SOL    HW2 1050   1.984   2.243   0.963 -0.8451  0.7989  0.0132
+  351SOL     OW 1051   1.266   2.508   2.399 -0.1945  0.2763  0.0133
+  351SOL    HW1 1052   1.207   2.464   2.338  1.1523  2.8325 -3.0895
+  351SOL    HW2 1053   1.251   2.464   2.483 -1.3722 -3.7555 -2.3576
+  352SOL     OW 1054   1.184   2.425   1.494  0.5049 -0.6168  0.3935
+  352SOL    HW1 1055   1.242   2.460   1.427  0.7127 -1.4515  0.1402
+  352SOL    HW2 1056   1.232   2.352   1.533 -0.3113 -1.2270  0.2746
+  353SOL     OW 1057   2.004   1.408   0.173 -0.3346 -0.8038  0.0398
+  353SOL    HW1 1058   2.060   1.469   0.222  0.2160 -1.2037 -0.0862
+  353SOL    HW2 1059   1.915   1.436   0.191 -0.1077 -0.6265  0.8508
+  354SOL     OW 1060   0.174   0.944   2.038  0.1050 -0.2192  0.1392
+  354SOL    HW1 1061   0.211   0.898   1.962 -0.9363 -0.8259 -0.0044
+  354SOL    HW2 1062   0.112   0.881   2.075 -0.8829  0.8526  0.2892
+  355SOL     OW 1063   0.849   1.542   0.586 -0.4205 -0.1251 -0.4373
+  355SOL    HW1 1064   0.803   1.463   0.559 -1.7573 -0.1009  1.7367
+  355SOL    HW2 1065   0.854   1.535   0.682 -0.0391  2.1355 -0.2954
+  356SOL     OW 1066   0.656   0.599   0.942 -0.8953 -0.3260  0.0396
+  356SOL    HW1 1067   0.583   0.562   0.991 -1.5283  0.5343 -0.2605
+  356SOL    HW2 1068   0.616   0.656   0.876  0.0894  1.5235  1.0137
+  357SOL     OW 1069   1.481   2.518   1.053 -0.4587  0.1926 -0.1161
+  357SOL    HW1 1070   1.544   2.449   1.034 -0.7697 -0.6447  1.8774
+  357SOL    HW2 1071   1.523   2.598   1.020 -0.3860 -1.0253 -2.9705
+  358SOL     OW 1072   0.463   2.882   2.996 -0.1729 -0.3083  0.2311
+  358SOL    HW1 1073   0.432   2.962   2.952  0.5227  0.1383  0.5598
+  358SOL    HW2 1074   0.496   2.827   2.926  0.3646  0.1522  0.1192
+  359SOL     OW 1075   2.647   1.942   0.216 -0.1664  0.1421 -0.2147
+  359SOL    HW1 1076   2.576   1.918   0.156 -0.2870  1.2419 -0.5036
+  359SOL    HW2 1077   2.689   2.017   0.175 -0.7654  1.3991  1.4792
+  360SOL     OW 1078   0.187   2.010   1.030  0.2101 -0.5687  0.0892
+  360SOL    HW1 1079   0.141   2.092   1.011  1.0328 -0.7116 -2.4649
+  360SOL    HW2 1080   0.211   1.975   0.944  3.7755 -1.2519  1.3689
+  361SOL     OW 1081   1.258   2.842   0.623  0.6131 -0.3717 -0.2307
+  361SOL    HW1 1082   1.301   2.826   0.707  0.2373  0.6486  0.1608
+  361SOL    HW2 1083   1.253   2.756   0.581 -2.8309 -1.4905  2.5240
+  362SOL     OW 1084   0.775   0.215   0.402  0.3169  0.4325  0.7317
+  362SOL    HW1 1085   0.769   0.302   0.362  1.1625  0.3281  0.3652
+  362SOL    HW2 1086   0.864   0.211   0.438  0.8091 -0.8581 -0.6192
+  363SOL     OW 1087   0.673   0.553   1.559  0.0502 -0.4445 -0.0251
+  363SOL    HW1 1088   0.701   0.571   1.648  1.3835 -0.9860 -0.3482
+  363SOL    HW2 1089   0.627   0.469   1.564 -2.7847  1.1369  0.7654
+  364SOL     OW 1090   1.223   1.327   2.942 -0.0519  0.6138 -0.7585
+  364SOL    HW1 1091   1.249   1.391   3.008  0.2096  2.5901 -2.7816
+  364SOL    HW2 1092   1.283   1.253   2.955  1.5125  1.9696 -0.3276
+  365SOL     OW 1093   0.374   1.931   2.156 -0.0906  0.0483 -1.0929
+  365SOL    HW1 1094   0.461   1.965   2.135 -1.4523  2.5321 -2.7829
+  365SOL    HW2 1095   0.388   1.873   2.231  3.0138  0.4885 -1.3401
+  366SOL     OW 1096   2.574   2.797   2.788  0.5077  0.2012  0.1944
+  366SOL    HW1 1097   2.584   2.853   2.711  0.0632  0.1449  0.0938
+  366SOL    HW2 1098   2.619   2.716   2.765  2.1218  0.9415  0.7757
+  367SOL     OW 1099   1.293   2.343   2.060  0.3679 -1.0226 -0.3824
+  367SOL    HW1 1100   1.373   2.388   2.034 -1.1890  1.4701 -0.9280
+  367SOL    HW2 1101   1.223   2.406   2.041 -1.6667 -2.9953  0.5232
+  368SOL     OW 1102   2.617   2.907   1.488 -0.1051 -0.1164 -0.5060
+  368SOL    HW1 1103   2.584   2.992   1.517  0.8883 -0.0846  0.5455
+  368SOL    HW2 1104   2.593   2.902   1.396 -1.0010  0.9023 -0.3274
+  369SOL     OW 1105   2.094   2.678   0.256  0.1203  0.1059 -0.3317
+  369SOL    HW1 1106   2.157   2.607   0.273 -1.5295 -0.9000  1.5146
+  369SOL    HW2 1107   2.124   2.750   0.311 -0.6612 -0.5500  0.9443
+  370SOL     OW 1108   1.663   0.613   0.908 -0.2399 -0.7528  0.0866
+  370SOL    HW1 1109   1.580   0.656   0.884 -0.0376  0.0541  0.8146
+  370SOL    HW2 1110   1.640   0.557   0.982 -0.1242 -0.4532  0.3529
+  371SOL     OW 1111   2.179   0.677   0.345  0.2124 -0.0920  0.3933
+  371SOL    HW1 1112   2.240   0.607   0.367 -1.4388 -1.5843  0.1927
+  371SOL    HW2 1113   2.125   0.689   0.423  0.7022  1.1734  0.5430
+  372SOL     OW 1114   0.858   2.968   1.959 -0.1161 -0.0246  0.1909
+  372SOL    HW1 1115   0.817   2.971   1.872  1.2058  0.5880 -0.4056
+  372SOL    HW2 1116   0.787   2.942   2.017 -1.1930  0.2904 -0.9708
+  373SOL     OW 1117   1.635   1.880   1.132 -0.1254  0.6032 -0.0620
+  373SOL    HW1 1118   1.551   1.835   1.141  0.9140 -0.9930  1.6748
+  373SOL    HW2 1119   1.692   1.817   1.088  0.4382  1.3289 -0.3698
+  374SOL     OW 1120   1.498   0.148   1.076  0.4252  0.3561  0.6183
+  374SOL    HW1 1121   1.456   0.234   1.073 -0.6414 -0.2960 -3.4966
+  374SOL    HW2 1122   1.430   0.089   1.109 -0.4627  0.1649 -1.5235
+  375SOL     OW 1123   1.978   1.680   2.453 -0.0810 -0.2377 -0.1717
+  375SOL    HW1 1124   2.065   1.643   2.436 -0.1867 -1.4403  1.8484
+  375SOL    HW2 1125   1.966   1.670   2.547 -1.6758  1.5586 -0.1938
+  376SOL     OW 1126   1.007   1.979   0.703 -0.1501  0.3105 -0.0085
+  376SOL    HW1 1127   0.950   1.954   0.775 -0.6221  1.7225  0.1110
+  376SOL    HW2 1128   0.962   1.948   0.625 -0.6328  0.5931  0.1544
+  377SOL     OW 1129   0.704   0.465   1.880 -0.1769  0.5094 -0.5596
+  377SOL    HW1 1130   0.772   0.532   1.877  1.4945 -1.2213 -1.5002
+  377SOL    HW2 1131   0.752   0.382   1.883 -2.2405 -0.8007 -3.3598
+  378SOL     OW 1132   1.862   2.481   2.901  0.0494  0.2645 -0.6110
+  378SOL    HW1 1133   1.888   2.497   2.991 -1.3719 -0.0329 -0.1577
+  378SOL    HW2 1134   1.899   2.555   2.852 -3.3790  2.9658  0.9386
+  379SOL     OW 1135   0.471   2.259   0.914 -1.0492 -0.3412 -0.1947
+  379SOL    HW1 1136   0.429   2.305   0.987  0.9184  0.4041  0.4800
+  379SOL    HW2 1137   0.497   2.329   0.854  2.1144 -0.5577  0.9204
+  380SOL     OW 1138   2.334   1.519   1.203  0.7791 -0.4395  0.0447
+  380SOL    HW1 1139   2.267   1.461   1.167  0.4294 -0.1348  0.2037
+  380SOL    HW2 1140   2.338   1.496   1.296 -0.5691  0.9843  0.4420
+  381SOL     OW 1141   0.453   0.461   1.092 -0.0321 -0.0274 -0.1520
+  381SOL    HW1 1142   0.372   0.506   1.067  0.0384  0.3658  0.3256
+  381SOL    HW2 1143   0.443   0.442   1.185  0.5439  0.2803 -0.0230
+  382SOL     OW 1144   2.153   0.951   0.320 -0.1472 -0.0310  0.8001
+  382SOL    HW1 1145   2.081   0.960   0.382 -1.2493 -0.7181 -0.3689
+  382SOL    HW2 1146   2.186   0.862   0.334 -0.6835 -0.3396  0.0842
+  383SOL     OW 1147   2.436   2.659   2.139  0.3556 -0.0500  0.3514
+  383SOL    HW1 1148   2.505   2.712   2.178 -0.5618  0.5984  1.1127
+  383SOL    HW2 1149   2.415   2.594   2.207  1.9819 -1.8375 -0.8582
+  384SOL     OW 1150   1.812   2.260   0.712  0.7492 -0.1245 -0.4781
+  384SOL    HW1 1151   1.863   2.326   0.665  0.2164 -1.0928 -2.4099
+  384SOL    HW2 1152   1.855   2.177   0.689 -1.7167 -0.9452 -2.1083
+  385SOL     OW 1153   2.599   1.329   1.827 -0.0709  0.1031 -0.0153
+  385SOL    HW1 1154   2.580   1.391   1.897 -0.7754 -0.0121 -0.1003
+  385SOL    HW2 1155   2.554   1.249   1.854  0.4040 -0.2135 -0.1596
+  386SOL     OW 1156   1.859   0.225   0.001  0.2133 -0.0830 -0.1887
+  386SOL    HW1 1157   1.910   0.146   0.018  2.6148  1.5912  0.3036
+  386SOL    HW2 1158   1.853   0.268   0.086 -2.7882 -0.7274 -0.0616
+  387SOL     OW 1159   1.133   0.587   1.907 -0.1014  0.5479 -0.1473
+  387SOL    HW1 1160   1.173   0.555   1.826 -1.2082  1.7230 -1.1603
+  387SOL    HW2 1161   1.194   0.654   1.939  0.9053 -0.7299  0.5847
+  388SOL     OW 1162   0.711   1.004   2.566  0.5876 -0.3511  0.1721
+  388SOL    HW1 1163   0.756   0.965   2.492 -0.1619  0.6369 -0.8032
+  388SOL    HW2 1164   0.641   0.942   2.588  0.3314 -0.2715 -0.4311
+  389SOL     OW 1165   0.546   1.351   2.759  0.4560  0.2433  0.2105
+  389SOL    HW1 1166   0.586   1.437   2.774  0.7862  0.3656 -1.3973
+  389SOL    HW2 1167   0.459   1.371   2.724 -0.6560 -0.0396  2.7561
+  390SOL     OW 1168   2.557   0.524   0.173  0.3078  0.4813  0.6310
+  390SOL    HW1 1169   2.551   0.438   0.215  0.4190  0.4228  0.5258
+  390SOL    HW2 1170   2.502   0.581   0.227  0.6885  0.4730  1.0254
+  391SOL     OW 1171   2.303   0.614   0.059  0.8663 -0.5620  0.1654
+  391SOL    HW1 1172   2.396   0.626   0.042  0.9145 -0.8213  0.2421
+  391SOL    HW2 1173   2.290   0.642   0.149  0.1747  3.0621 -1.0649
+  392SOL     OW 1174   0.908   0.565   2.854 -0.3854 -0.0686 -0.9319
+  392SOL    HW1 1175   0.990   0.519   2.837 -0.1461  0.2998 -0.7602
+  392SOL    HW2 1176   0.911   0.585   2.948 -1.6895 -2.0870 -0.4488
+  393SOL     OW 1177   2.290   2.643   1.719 -0.4230  0.1300 -0.2114
+  393SOL    HW1 1178   2.352   2.608   1.655  0.0266  0.5647 -0.0132
+  393SOL    HW2 1179   2.227   2.693   1.667 -1.1816 -1.1405 -0.5168
+  394SOL     OW 1180   0.445   0.479   1.734  0.2340  0.1849 -0.6191
+  394SOL    HW1 1181   0.513   0.533   1.776 -0.4574  0.1183  0.6022
+  394SOL    HW2 1182   0.410   0.535   1.665 -0.8239  1.5276  0.9703
+  395SOL     OW 1183   1.302   1.133   2.330  0.0409 -0.4872  0.2284
+  395SOL    HW1 1184   1.354   1.193   2.277 -0.0890 -1.6242 -1.1771
+  395SOL    HW2 1185   1.363   1.103   2.398  0.3681  0.5132  0.3773
+  396SOL     OW 1186   1.767   0.169   0.745  0.3246  0.3393 -0.3596
+  396SOL    HW1 1187   1.686   0.219   0.758  1.1869  1.4248  0.9351
+  396SOL    HW2 1188   1.777   0.118   0.825  0.5112 -0.3791 -0.8412
+  397SOL     OW 1189   1.601   0.872   0.135 -0.1984 -0.0068 -0.2820
+  397SOL    HW1 1190   1.544   0.921   0.194 -0.3108  0.0194 -0.4128
+  397SOL    HW2 1191   1.653   0.817   0.193  0.4025  0.7614 -0.0847
+  398SOL     OW 1192   2.192   0.318   1.769 -0.4485 -0.0891  0.7591
+  398SOL    HW1 1193   2.119   0.379   1.777  1.4986  2.4787 -0.8164
+  398SOL    HW2 1194   2.159   0.248   1.713 -1.0359  2.1550 -1.7347
+  399SOL     OW 1195   1.926   1.622   0.492  0.0263 -0.3557  0.7511
+  399SOL    HW1 1196   1.870   1.699   0.494  0.6342  0.1324 -1.3089
+  399SOL    HW2 1197   2.004   1.648   0.542 -0.4529 -0.1630  1.3978
+  400SOL     OW 1198   1.862   1.412   1.705  0.0620 -0.0589 -0.0065
+  400SOL    HW1 1199   1.909   1.477   1.757 -0.3326 -0.3863  0.7496
+  400SOL    HW2 1200   1.790   1.382   1.761 -1.3769  0.8439 -1.3757
+  401SOL     OW 1201   2.518   0.825   0.293  0.4785  0.2386 -0.5144
+  401SOL    HW1 1202   2.531   0.918   0.277 -0.2199  0.3756 -0.2996
+  401SOL    HW2 1203   2.541   0.812   0.385 -0.5519  0.4215 -0.2227
+  402SOL     OW 1204   0.923   2.565   1.527 -0.1136  0.4505  0.0693
+  402SOL    HW1 1205   0.994   2.500   1.524  0.3623  0.8997  1.4751
+  402SOL    HW2 1206   0.948   2.632   1.464 -0.5425 -0.6704 -1.2989
+  403SOL     OW 1207   0.635   1.992   1.222 -0.1310  0.2333 -0.2750
+  403SOL    HW1 1208   0.607   2.071   1.268 -1.9786  0.1939 -1.2898
+  403SOL    HW2 1209   0.724   2.011   1.193  0.5801  0.7073  2.2839
+  404SOL     OW 1210   2.023   0.770   1.688 -0.2739  0.0244 -0.4903
+  404SOL    HW1 1211   1.941   0.723   1.674 -1.5584  1.8349  0.9726
+  404SOL    HW2 1212   2.047   0.751   1.779  0.0965  0.7838 -0.4275
+  405SOL     OW 1213   2.197   2.381   2.039 -0.7235  0.1473  0.3527
+  405SOL    HW1 1214   2.231   2.316   2.101  0.2554  0.1495 -0.1805
+  405SOL    HW2 1215   2.236   2.464   2.068  0.5268 -0.0124 -0.8647
+  406SOL     OW 1216   0.631   2.136   0.753  0.1848  0.7260 -0.6176
+  406SOL    HW1 1217   0.552   2.165   0.798 -0.8444  0.7324 -2.4381
+  406SOL    HW2 1218   0.702   2.183   0.796 -0.7308  0.1528  1.5019
+  407SOL     OW 1219   2.506   1.833   1.423  0.2443 -0.1931  0.0748
+  407SOL    HW1 1220   2.420   1.844   1.464  1.5678  0.7445  2.6514
+  407SOL    HW2 1221   2.542   1.922   1.419  1.2140 -0.6203 -0.6666
+  408SOL     OW 1222   2.965   2.872   2.418  0.3180  0.1111  0.3228
+  408SOL    HW1 1223   2.894   2.920   2.461  2.1918  1.4395  1.9676
+  408SOL    HW2 1224   2.954   2.782   2.448 -1.1218  0.1773 -0.0065
+  409SOL     OW 1225   1.863   0.023   0.383 -0.1814  0.2347  0.2069
+  409SOL    HW1 1226   1.776   0.050   0.351  0.1337  0.0127 -0.8416
+  409SOL    HW2 1227   1.895   0.100   0.430 -1.5914 -0.2165  1.8961
+  410SOL     OW 1228  -0.017   1.709   1.712  0.1555 -0.0340  0.4988
+  410SOL    HW1 1229  -0.037   1.632   1.766  1.5499 -0.0869  0.9287
+  410SOL    HW2 1230   0.057   1.682   1.658 -0.2851  0.5248 -0.3833
+  411SOL     OW 1231   1.941   0.388   0.928  0.1598  0.2695 -0.5687
+  411SOL    HW1 1232   1.852   0.403   0.962 -0.2040  0.8565 -1.7879
+  411SOL    HW2 1233   1.997   0.433   0.991 -0.3759 -0.5710  0.5108
+  412SOL     OW 1234   1.452   2.278   2.645 -0.3368 -0.7605  0.6261
+  412SOL    HW1 1235   1.429   2.266   2.553 -1.6321 -0.3546  0.9011
+  412SOL    HW2 1236   1.388   2.340   2.678  0.3044 -0.6873  1.7379
+  413SOL     OW 1237   2.016   3.016   0.120 -0.2102  0.4719  0.3668
+  413SOL    HW1 1238   1.932   2.973   0.104  1.6849 -2.8764 -0.6425
+  413SOL    HW2 1239   2.041   2.990   0.208  0.2042  0.7702  0.3406
+  414SOL     OW 1240   1.851   1.850   0.781 -0.5186 -0.4904  0.0598
+  414SOL    HW1 1241   1.870   1.933   0.737 -2.1080 -0.3320 -0.3259
+  414SOL    HW2 1242   1.935   1.805   0.782  0.5351  1.4728  1.0064
+  415SOL     OW 1243   0.371   0.262   0.893  0.8326  0.0058 -0.0022
+  415SOL    HW1 1244   0.349   0.263   0.800 -0.3638 -0.4976  0.2780
+  415SOL    HW2 1245   0.440   0.329   0.902  0.2778  0.7192 -1.0790
+  416SOL     OW 1246   1.451   1.537   2.812 -0.1750 -0.0408  0.3649
+  416SOL    HW1 1247   1.455   1.444   2.790  0.6111 -0.4233  2.1184
+  416SOL    HW2 1248   1.476   1.582   2.731 -0.6163 -1.3631 -0.4961
+  417SOL     OW 1249   1.527   1.147   2.079 -0.6137  0.8840 -0.1928
+  417SOL    HW1 1250   1.613   1.155   2.120 -1.2600  1.3160  1.0891
+  417SOL    HW2 1251   1.521   1.055   2.054 -0.6709  0.3536  1.7260
+  418SOL     OW 1252   0.191   2.574   2.276  0.7624  0.1577 -0.1423
+  418SOL    HW1 1253   0.202   2.518   2.353 -1.4745 -0.1337 -0.0483
+  418SOL    HW2 1254   0.181   2.662   2.311  2.5299  0.3966 -0.2205
+  419SOL     OW 1255   0.892   0.633   1.762  0.1146  0.1214 -0.5151
+  419SOL    HW1 1256   0.921   0.708   1.711 -0.3569  0.0026 -0.9552
+  419SOL    HW2 1257   0.971   0.603   1.808  0.5844  0.0914 -1.3344
+  420SOL     OW 1258   2.212   2.633   2.635 -0.1512  0.1351 -0.3090
+  420SOL    HW1 1259   2.148   2.701   2.617 -0.4794 -0.3899 -1.1427
+  420SOL    HW2 1260   2.276   2.674   2.693 -0.1826  1.2367 -1.0563
+  421SOL     OW 1261   2.844   1.524   1.036  0.5403 -0.4388 -0.0236
+  421SOL    HW1 1262   2.792   1.476   0.971 -1.0943  1.9906 -0.5116
+  421SOL    HW2 1263   2.779   1.570   1.088  2.1479  1.3794  0.3400
+  422SOL     OW 1264   1.558   2.494   1.959  0.2265  0.0375  0.4178
+  422SOL    HW1 1265   1.528   2.511   1.870  2.3120 -1.0498 -0.4903
+  422SOL    HW2 1266   1.627   2.559   1.974 -1.1583  1.2147  1.7841
+  423SOL     OW 1267   0.252   2.366   0.482 -0.4167  0.6655 -0.2874
+  423SOL    HW1 1268   0.270   2.329   0.396 -0.8290 -0.8879  0.2968
+  423SOL    HW2 1269   0.217   2.293   0.533  0.0029  1.5332  1.2524
+  424SOL     OW 1270   1.829   2.977   0.932  0.2249 -0.2508 -0.0943
+  424SOL    HW1 1271   1.874   2.950   0.852 -0.2689 -1.3984  0.0098
+  424SOL    HW2 1272   1.898   3.018   0.986  0.9041 -0.9645 -0.4140
+  425SOL     OW 1273   1.763   1.629   2.938  0.0100  0.4502  0.0576
+  425SOL    HW1 1274   1.692   1.693   2.934  0.6099  1.1476  0.7391
+  425SOL    HW2 1275   1.807   1.646   3.021 -0.4605 -1.5138  0.7021
+  426SOL     OW 1276   0.333   1.871   0.775  0.0247 -0.1808  0.1905
+  426SOL    HW1 1277   0.376   1.941   0.726  0.7700 -0.6109  0.2284
+  426SOL    HW2 1278   0.394   1.852   0.846 -0.2115 -0.8298  0.2111
+  427SOL     OW 1279   2.379   2.488   2.331 -0.2140 -0.6756 -0.4913
+  427SOL    HW1 1280   2.333   2.506   2.413  2.5446  1.3372  0.6223
+  427SOL    HW2 1281   2.425   2.406   2.347 -0.5245 -0.8307 -0.3886
+  428SOL     OW 1282   2.473   0.584   1.293  0.1582 -0.1862  0.6393
+  428SOL    HW1 1283   2.466   0.669   1.249 -1.2424 -0.3266  0.5867
+  428SOL    HW2 1284   2.566   0.574   1.311  0.8458 -0.5493 -3.1634
+  429SOL     OW 1285   1.059   1.963   1.570 -0.5140 -0.2757  0.0710
+  429SOL    HW1 1286   1.115   1.935   1.497 -0.8242  0.2129 -0.3539
+  429SOL    HW2 1287   1.121   1.984   1.640 -0.1738  0.0937 -0.3419
+  430SOL     OW 1288   2.510   2.248   2.406 -0.2969 -0.5465  0.3924
+  430SOL    HW1 1289   2.496   2.188   2.480  2.3183  1.6858  2.6912
+  430SOL    HW2 1290   2.577   2.205   2.354  1.0959  0.0778  1.6813
+  431SOL     OW 1291   0.256   0.595   1.980  0.2301 -0.5980 -0.0864
+  431SOL    HW1 1292   0.289   0.619   1.893 -1.0174 -0.8796 -0.6438
+  431SOL    HW2 1293   0.334   0.583   2.033  0.9596  1.9695 -0.5380
+  432SOL     OW 1294   1.582   1.257   1.813 -0.2175  0.0780  0.2235
+  432SOL    HW1 1295   1.572   1.290   1.903 -0.5615 -0.7929  0.5111
+  432SOL    HW2 1296   1.492   1.250   1.781 -0.1924 -1.5407  0.5129
+  433SOL     OW 1297   0.550   1.523   0.373  0.1488 -0.2217 -0.2277
+  433SOL    HW1 1298   0.494   1.484   0.440  1.5883  0.0517  1.1076
+  433SOL    HW2 1299   0.620   1.459   0.359 -0.4702 -0.5532 -1.8957
+  434SOL     OW 1300   1.033   0.805   0.538 -0.5784 -0.4541  0.1833
+  434SOL    HW1 1301   0.954   0.819   0.591  0.0924 -1.0107  1.3227
+  434SOL    HW2 1302   1.030   0.876   0.473 -0.6448  1.1052  1.8743
+  435SOL     OW 1303   2.357   1.042   2.672  0.0189  0.3975  0.3311
+  435SOL    HW1 1304   2.329   1.000   2.754 -0.9593  0.3140 -0.0459
+  435SOL    HW2 1305   2.408   1.118   2.701 -1.3973  1.0924  1.0251
+  436SOL     OW 1306   2.421   0.680   2.352  0.1573 -0.3954  0.6540
+  436SOL    HW1 1307   2.363   0.615   2.390 -1.5980 -0.3215 -1.9006
+  436SOL    HW2 1308   2.502   0.673   2.403 -1.3233 -1.9946  2.7735
+  437SOL     OW 1309   1.893   1.993   1.070  0.8744  0.2772 -0.1221
+  437SOL    HW1 1310   1.816   1.940   1.091  3.1274 -1.7958  2.9997
+  437SOL    HW2 1311   1.912   1.972   0.979 -0.9495 -0.8664 -0.2467
+  438SOL     OW 1312   0.748   1.375   1.702 -0.0587  0.7205 -0.1535
+  438SOL    HW1 1313   0.714   1.461   1.677 -2.5219 -0.5133 -1.0556
+  438SOL    HW2 1314   0.820   1.360   1.641  0.1396  1.9669 -0.2433
+  439SOL     OW 1315   1.164   2.801   2.304  0.4998  0.2910  0.1262
+  439SOL    HW1 1316   1.162   2.804   2.400  0.0790 -2.1543  0.1998
+  439SOL    HW2 1317   1.233   2.738   2.284  0.6736  0.8944 -1.1889
+  440SOL     OW 1318   2.626   0.823   0.797 -0.3677  0.5829  0.1050
+  440SOL    HW1 1319   2.718   0.799   0.794 -0.5117  0.0671 -0.3281
+  440SOL    HW2 1320   2.590   0.772   0.869 -0.5592 -0.1067 -0.4721
+  441SOL     OW 1321   2.039   1.488   0.887 -0.1400  0.6099  0.3564
+  441SOL    HW1 1322   1.969   1.441   0.842  2.3991 -3.0881  0.3120
+  441SOL    HW2 1323   2.038   1.455   0.976  1.1329  1.0079  0.5238
+  442SOL     OW 1324   2.988   1.599   2.909 -0.0579 -0.2454 -0.0737
+  442SOL    HW1 1325   2.928   1.597   2.983  2.8396  0.5920  2.2983
+  442SOL    HW2 1326   3.071   1.564   2.944  1.6438  1.2611 -2.5937
+  443SOL     OW 1327   0.041   0.594   0.763 -0.0751  0.2843 -0.1965
+  443SOL    HW1 1328   0.105   0.523   0.771  0.2036  0.4704 -0.8274
+  443SOL    HW2 1329   0.083   0.659   0.707  0.0025  1.3372  1.0696
+  444SOL     OW 1330   1.431   1.155   0.012  0.5967 -0.2764 -0.3688
+  444SOL    HW1 1331   1.496   1.084   0.021 -0.0174 -0.9317 -1.1050
+  444SOL    HW2 1332   1.483   1.235   0.019  1.2251 -0.8057  0.9659
+  445SOL     OW 1333   2.246   2.363   1.534  0.1532  0.2810  0.5880
+  445SOL    HW1 1334   2.280   2.362   1.623  2.9703  0.9865 -0.4730
+  445SOL    HW2 1335   2.284   2.442   1.495 -0.3483 -0.5239 -1.5293
+  446SOL     OW 1336   0.112   2.853   0.193 -0.1264 -0.1924  0.1528
+  446SOL    HW1 1337   0.043   2.828   0.131  0.6870  1.9062 -1.5819
+  446SOL    HW2 1338   0.168   2.775   0.198 -2.2947 -1.6975  1.1280
+  447SOL     OW 1339   0.591   2.459   2.951  0.1560 -0.1447 -0.6774
+  447SOL    HW1 1340   0.685   2.469   2.940  0.2454  0.2251  0.4512
+  447SOL    HW2 1341   0.576   2.365   2.947  0.6947 -0.2638  0.2253
+  448SOL     OW 1342   1.125   0.018   0.309  0.6625  0.6794 -0.1923
+  448SOL    HW1 1343   1.221   0.026   0.314  0.8406 -0.6813 -1.2308
+  448SOL    HW2 1344   1.105   0.032   0.217 -0.3630  3.2262  0.4295
+  449SOL     OW 1345   1.475   2.225   1.626  0.2531 -0.2797  0.3384
+  449SOL    HW1 1346   1.529   2.146   1.638 -0.4126 -0.9240 -0.9246
+  449SOL    HW2 1347   1.404   2.214   1.690  0.3378 -0.5431  0.3913
+  450SOL     OW 1348   0.774   1.733   1.127  0.6151  0.0909 -0.3893
+  450SOL    HW1 1349   0.740   1.800   1.186 -0.1165 -1.7192  1.2260
+  450SOL    HW2 1350   0.846   1.693   1.175  1.8067  0.5287 -1.8308
+  451SOL     OW 1351   1.102   1.269   2.713 -0.0571 -0.1812 -0.3911
+  451SOL    HW1 1352   1.146   1.306   2.789 -2.6189  0.1773  0.9040
+  451SOL    HW2 1353   1.138   1.317   2.638  3.1195 -1.1667  0.5067
+  452SOL     OW 1354   1.980   1.134   2.007  0.1985  0.3749 -0.0722
+  452SOL    HW1 1355   1.935   1.074   2.066  1.7845 -0.1610  0.6051
+  452SOL    HW2 1356   1.998   1.211   2.059 -1.3646  1.2259 -0.7949
+  453SOL     OW 1357   1.554   0.863   1.022  0.4876  0.2545 -0.0076
+  453SOL    HW1 1358   1.575   0.774   1.048 -1.9451 -0.7511 -1.5867
+  453SOL    HW2 1359   1.560   0.914   1.104  1.0439 -1.4760  1.0200
+  454SOL     OW 1360   1.274   0.369   2.138  0.3617  0.4656  0.1542
+  454SOL    HW1 1361   1.253   0.461   2.150  0.9720  0.8608 -1.8485
+  454SOL    HW2 1362   1.269   0.331   2.226  0.1445  2.4067  0.9660
+  455SOL     OW 1363   0.727   2.136   0.154 -0.1534 -0.3967 -0.4266
+  455SOL    HW1 1364   0.672   2.163   0.081  1.2925  0.0875 -1.3381
+  455SOL    HW2 1365   0.675   2.158   0.232 -1.3750 -0.2278 -1.2912
+  456SOL     OW 1366   0.658   1.553   2.120 -0.6320 -0.1404 -0.3683
+  456SOL    HW1 1367   0.570   1.520   2.102 -1.1338  0.3952  1.0859
+  456SOL    HW2 1368   0.665   1.631   2.065 -1.1202 -0.1042 -0.3835
+  457SOL     OW 1369   0.644   2.281   2.631 -0.0014  0.1695 -0.2228
+  457SOL    HW1 1370   0.629   2.335   2.553  0.4190 -0.1941 -0.5550
+  457SOL    HW2 1371   0.739   2.280   2.642  0.2060 -3.4508 -2.3256
+  458SOL     OW 1372   2.889   1.459   0.422 -0.0813  0.4471  0.1132
+  458SOL    HW1 1373   2.879   1.444   0.516  0.1577  0.3856  0.1297
+  458SOL    HW2 1374   2.963   1.519   0.415 -3.5502  4.7271  0.2577
+  459SOL     OW 1375   1.130   1.436   1.120 -0.0076  0.3311  0.2859
+  459SOL    HW1 1376   1.048   1.387   1.123  0.8718 -1.0386  1.9317
+  459SOL    HW2 1377   1.138   1.463   1.029 -0.2243 -3.0454 -0.7492
+  460SOL     OW 1378   0.174   2.894   1.004  0.1324 -0.2155  0.4146
+  460SOL    HW1 1379   0.138   2.841   1.075  0.9953 -0.5259  0.6234
+  460SOL    HW2 1380   0.238   2.951   1.046  0.6656 -0.4180 -0.1148
+  461SOL     OW 1381   2.954   0.755   0.205 -0.0835  0.6440  0.0916
+  461SOL    HW1 1382   3.007   0.718   0.276  0.5477  0.0435 -0.6982
+  461SOL    HW2 1383   2.983   0.846   0.199 -0.4982  0.8353  0.9501
+  462SOL     OW 1384   0.308   1.312   0.226  0.0555  0.4452  0.6502
+  462SOL    HW1 1385   0.371   1.355   0.169  0.9634  0.0998  1.3984
+  462SOL    HW2 1386   0.278   1.236   0.175 -1.0977  1.6182 -0.4229
+  463SOL     OW 1387   2.917   0.294   0.384 -0.0969  0.2017  0.3460
+  463SOL    HW1 1388   2.894   0.364   0.323 -0.2984  0.6195  0.9043
+  463SOL    HW2 1389   2.897   0.213   0.336  1.1345  0.5184 -0.6858
+  464SOL     OW 1390   0.062   0.742   2.173 -0.0866 -0.0752 -0.5147
+  464SOL    HW1 1391   0.033   0.725   2.262 -0.3377  0.7491 -0.4424
+  464SOL    HW2 1392   0.156   0.724   2.174 -0.0060  0.4116  0.0278
+  465SOL     OW 1393   1.409   1.324   0.666  0.4792  0.2853  0.1025
+  465SOL    HW1 1394   1.389   1.320   0.759  0.1761  1.1919  0.0834
+  465SOL    HW2 1395   1.481   1.263   0.653 -0.0115 -0.4253  0.7936
+  466SOL     OW 1396   1.361   2.813   1.364 -0.5101  0.3533  0.3207
+  466SOL    HW1 1397   1.353   2.905   1.336  1.3781  0.7404  1.0967
+  466SOL    HW2 1398   1.311   2.808   1.445 -1.0092  0.5864  0.0281
+  467SOL     OW 1399   0.904   2.188   2.407 -0.3130  0.0389 -0.1191
+  467SOL    HW1 1400   0.881   2.110   2.356  1.6059 -1.3222  1.0904
+  467SOL    HW2 1401   0.965   2.237   2.352 -1.3841  0.5994 -0.8067
+  468SOL     OW 1402   1.500   2.540   1.671 -0.4938  0.1210 -0.6569
+  468SOL    HW1 1403   1.589   2.506   1.661  0.1222  1.9010 -1.3151
+  468SOL    HW2 1404   1.492   2.608   1.604 -2.7053 -0.7450 -1.2859
+  469SOL     OW 1405   1.790   0.242   1.916 -0.0490  0.4735  0.2669
+  469SOL    HW1 1406   1.868   0.298   1.920  0.3356 -0.0159 -0.5969
+  469SOL    HW2 1407   1.764   0.244   1.824  0.3440 -1.3074  0.1170
+  470SOL     OW 1408   1.466   2.580   1.332  0.4109  0.0346 -0.0833
+  470SOL    HW1 1409   1.424   2.665   1.344 -2.1491 -1.2442 -0.0292
+  470SOL    HW2 1410   1.472   2.570   1.237 -0.7931 -0.4560 -0.1128
+  471SOL     OW 1411   0.386   1.424   2.107 -0.9193  0.0882 -0.1795
+  471SOL    HW1 1412   0.354   1.334   2.100  0.3904 -0.3199 -1.0070
+  471SOL    HW2 1413   0.309   1.478   2.091 -1.7716 -0.8851  0.6217
+  472SOL     OW 1414   0.365   1.168   0.492 -0.6582 -0.4068  0.1706
+  472SOL    HW1 1415   0.356   1.206   0.579  0.5446  0.1153  0.0614
+  472SOL    HW2 1416   0.384   1.242   0.435  0.8919 -1.0816 -0.1643
+  473SOL     OW 1417   1.771   0.336   2.567 -0.0786  0.2388  0.3121
+  473SOL    HW1 1418   1.753   0.430   2.557  1.1144  0.5183  0.8947
+  473SOL    HW2 1419   1.823   0.313   2.490 -1.5542 -0.2363 -0.5431
+  474SOL     OW 1420   0.231   0.622   1.046  0.2528 -0.0757 -0.6409
+  474SOL    HW1 1421   0.144   0.634   1.084  0.0101 -0.8924 -0.9400
+  474SOL    HW2 1422   0.288   0.677   1.099 -0.5696  1.0312 -0.9013
+  475SOL     OW 1423   1.930   1.411   2.395  0.0134  0.1862  0.3738
+  475SOL    HW1 1424   1.898   1.491   2.437 -1.3161 -1.4635  2.4664
+  475SOL    HW2 1425   1.912   1.341   2.458  2.0577 -1.6398 -1.0505
+  476SOL     OW 1426   0.951   1.232   1.113  0.3410  0.4211 -0.5304
+  476SOL    HW1 1427   0.932   1.202   1.202 -0.1321  1.6942 -0.2054
+  476SOL    HW2 1428   0.893   1.180   1.058 -1.1414  1.3816  0.1208
+  477SOL     OW 1429   0.831   1.016   1.599 -0.2497 -0.0520  0.1091
+  477SOL    HW1 1430   0.743   1.035   1.633 -0.4833  0.8043 -0.9901
+  477SOL    HW2 1431   0.883   0.998   1.677 -0.3445  3.7977  1.0692
+  478SOL     OW 1432   1.648   2.250   2.123  0.7085  0.0073  0.3409
+  478SOL    HW1 1433   1.644   2.345   2.112  1.0168  0.0778  0.7940
+  478SOL    HW2 1434   1.656   2.216   2.035  0.4382  0.3966  0.1637
+  479SOL     OW 1435   1.264   1.489   2.195  0.1279  0.6002  0.5353
+  479SOL    HW1 1436   1.219   1.420   2.145 -2.2971  2.6553 -0.0957
+  479SOL    HW2 1437   1.323   1.530   2.131  0.0861  0.4685  0.4114
+  480SOL     OW 1438   0.263   0.053   2.895 -0.3720  0.1971  0.2231
+  480SOL    HW1 1439   0.185   0.004   2.921  0.4169 -1.9479 -1.5253
+  480SOL    HW2 1440   0.276   0.116   2.966 -2.4348 -0.8721  1.5522
+  481SOL     OW 1441   2.091   0.275   2.856  0.7740 -0.0289  0.5951
+  481SOL    HW1 1442   2.003   0.289   2.893  0.3614 -0.1885 -0.3252
+  481SOL    HW2 1443   2.115   0.360   2.820  0.5461 -0.2604 -0.1114
+  482SOL     OW 1444   1.247   1.806   1.379 -0.2383  0.5765  0.5349
+  482SOL    HW1 1445   1.312   1.794   1.448 -0.1600  0.9188  0.5234
+  482SOL    HW2 1446   1.298   1.817   1.299 -0.4123 -2.4716  0.0198
+  483SOL     OW 1447   2.979   1.540   2.275 -0.0837 -0.3392  0.1832
+  483SOL    HW1 1448   2.985   1.580   2.362 -1.0588 -1.3937  0.7434
+  483SOL    HW2 1449   2.977   1.446   2.292 -1.4360 -0.5164 -0.8997
+  484SOL     OW 1450   1.234   2.278   1.779 -0.3614  0.1478  0.1964
+  484SOL    HW1 1451   1.232   2.368   1.809  1.1126 -0.3525  1.7931
+  484SOL    HW2 1452   1.147   2.264   1.740 -0.2565  2.5140 -0.8861
+  485SOL     OW 1453   2.326   1.726   2.169  0.7855  0.2867  0.7691
+  485SOL    HW1 1454   2.343   1.734   2.075  2.5869 -0.8198  1.0100
+  485SOL    HW2 1455   2.261   1.794   2.187  2.0204  1.9097 -0.8650
+  486SOL     OW 1456   2.027   1.975   0.056 -0.1914 -0.0776 -0.0359
+  486SOL    HW1 1457   2.056   2.065   0.070 -0.8954 -0.0120  0.9834
+  486SOL    HW2 1458   2.028   1.965  -0.039  0.0141  0.9682 -0.1505
+  487SOL     OW 1459   2.500   2.550   1.893 -0.2578 -0.2390 -0.5672
+  487SOL    HW1 1460   2.421   2.580   1.848  0.6336  0.0454 -1.9084
+  487SOL    HW2 1461   2.475   2.550   1.986 -1.2759  1.5437 -0.8383
+  488SOL     OW 1462   1.187   1.515   0.880 -0.0047 -0.0238 -0.2281
+  488SOL    HW1 1463   1.269   1.468   0.899  0.4503  1.0153  0.3680
+  488SOL    HW2 1464   1.167   1.492   0.790  0.4191 -1.0838 -0.0552
+  489SOL     OW 1465   1.021   1.686   1.251 -0.0912 -0.5348  0.7382
+  489SOL    HW1 1466   1.054   1.596   1.243 -0.7906 -0.8113  0.9476
+  489SOL    HW2 1467   1.071   1.724   1.323  3.0404  0.5168 -1.9893
+  490SOL     OW 1468   1.179   1.863   0.495  0.0534  0.2494 -0.7560
+  490SOL    HW1 1469   1.160   1.908   0.578 -1.5136  0.6069 -1.3009
+  490SOL    HW2 1470   1.095   1.864   0.448  0.6285 -1.0827 -1.8023
+  491SOL     OW 1471   1.201   1.648   2.793 -0.4307  0.0746 -0.4392
+  491SOL    HW1 1472   1.207   1.743   2.803  0.0800  0.1798 -1.7613
+  491SOL    HW2 1473   1.287   1.615   2.820 -1.2187 -0.1613  1.8001
+  492SOL     OW 1474   0.565   2.698   1.586 -0.0591 -0.0840  0.3690
+  492SOL    HW1 1475   0.565   2.669   1.495  0.4110 -0.7818  0.5975
+  492SOL    HW2 1476   0.490   2.758   1.592  0.6535  0.9260 -0.7786
+  493SOL     OW 1477   0.197   1.531   1.381 -0.0336 -0.0453  0.2618
+  493SOL    HW1 1478   0.167   1.619   1.358 -0.4074 -0.4536 -0.7809
+  493SOL    HW2 1479   0.168   1.520   1.472  0.1569  0.9704  0.4478
+  494SOL     OW 1480   2.529   2.747   0.176 -0.2448  0.2596 -0.0554
+  494SOL    HW1 1481   2.456   2.727   0.117 -1.9910  2.7128  1.2934
+  494SOL    HW2 1482   2.517   2.839   0.200  2.5578  0.3138  1.1951
+  495SOL     OW 1483   2.759   2.422   1.784  0.2514 -0.5900  0.1351
+  495SOL    HW1 1484   2.677   2.471   1.791  0.4480 -0.2070 -0.2946
+  495SOL    HW2 1485   2.736   2.333   1.811 -0.7314 -0.9453 -1.9112
+  496SOL     OW 1486   2.800   0.581   0.927 -0.5178  0.3119  0.1086
+  496SOL    HW1 1487   2.820   0.525   1.002 -2.5755  2.0019  1.9282
+  496SOL    HW2 1488   2.862   0.554   0.860  1.2775 -1.2818  2.3991
+  497SOL     OW 1489   0.162   2.217   0.697  0.0895 -0.0636 -0.6751
+  497SOL    HW1 1490   0.119   2.210   0.782  0.5973 -0.0989 -0.4208
+  497SOL    HW2 1491   0.191   2.128   0.677  0.7657  0.1114 -0.4706
+  498SOL     OW 1492   0.262   2.094   1.281 -0.2461  0.2877 -0.2528
+  498SOL    HW1 1493   0.243   2.055   1.196  0.2150  0.3318 -0.3765
+  498SOL    HW2 1494   0.346   2.138   1.269 -0.9461  1.6355 -0.2261
+  499SOL     OW 1495   2.111   1.748   0.937  0.1040 -0.1699 -0.0763
+  499SOL    HW1 1496   2.087   1.678   0.876  0.6214  0.1759 -0.6710
+  499SOL    HW2 1497   2.077   1.719   1.021  0.2628 -1.1096 -0.3392
+  500SOL     OW 1498   2.434   0.873   1.570 -0.5228  0.1673 -0.2320
+  500SOL    HW1 1499   2.527   0.882   1.591 -0.2253  0.8536 -1.7922
+  500SOL    HW2 1500   2.410   0.956   1.529 -1.4303 -0.5854 -1.2512
+  501SOL     OW 1501   0.482   0.450   0.380 -0.6889 -0.0809  0.0659
+  501SOL    HW1 1502   0.527   0.395   0.316  0.2124 -0.9762  1.4819
+  501SOL    HW2 1503   0.402   0.401   0.402 -1.0806  0.3040 -0.5002
+  502SOL     OW 1504   2.363   1.120   1.447 -0.2177 -0.6509  0.5514
+  502SOL    HW1 1505   2.446   1.162   1.469 -0.3525 -0.1554  0.1240
+  502SOL    HW2 1506   2.372   1.095   1.355 -0.5028 -0.6805  0.5297
+  503SOL     OW 1507   1.479   0.752   2.902 -0.4436  0.2221  0.2502
+  503SOL    HW1 1508   1.455   0.663   2.928  0.7187 -0.5962 -1.4883
+  503SOL    HW2 1509   1.512   0.793   2.983  3.0210 -2.2757  0.1208
+  504SOL     OW 1510   1.801   1.992   1.512  0.0781  0.8236  0.0538
+  504SOL    HW1 1511   1.873   2.033   1.465  1.5360 -1.2377  0.5030
+  504SOL    HW2 1512   1.796   2.039   1.595 -0.5850  2.4899 -0.9325
+  505SOL     OW 1513   1.383   2.361   0.836 -1.0967 -0.1203  0.2677
+  505SOL    HW1 1514   1.364   2.434   0.896 -1.3276 -1.0118  1.2870
+  505SOL    HW2 1515   1.386   2.284   0.893 -0.3451 -0.8332 -0.7401
+  506SOL     OW 1516   1.425   0.896   1.568  0.2655 -0.6466 -1.3439
+  506SOL    HW1 1517   1.504   0.925   1.612 -0.4347 -0.8886  0.0846
+  506SOL    HW2 1518   1.408   0.963   1.502  0.8736  0.1340 -0.7084
+  507SOL     OW 1519   0.487   2.406   0.654 -0.3029  0.5738 -0.1669
+  507SOL    HW1 1520   0.550   2.348   0.612  0.9827  2.1828 -0.4769
+  507SOL    HW2 1521   0.405   2.393   0.607  0.4099  0.2476 -1.2904
+  508SOL     OW 1522   0.867   1.163   2.772  0.4196  0.2368 -0.2583
+  508SOL    HW1 1523   0.838   1.150   2.681 -1.2572  2.4162 -0.0624
+  508SOL    HW2 1524   0.945   1.218   2.764  1.3895 -1.1693 -0.4858
+  509SOL     OW 1525   1.718   0.096   2.291  0.7483 -0.1663  0.5017
+  509SOL    HW1 1526   1.722   0.186   2.258  2.1262 -0.8528 -1.2171
+  509SOL    HW2 1527   1.723   0.042   2.212  2.8063 -1.8037  1.7771
+  510SOL     OW 1528   2.399   0.181   1.915  0.3614  0.3563 -0.2948
+  510SOL    HW1 1529   2.458   0.231   1.971 -0.8175 -1.3207  2.4473
+  510SOL    HW2 1530   2.334   0.245   1.884  0.2012  0.9915  1.3677
+  511SOL     OW 1531   1.614   2.607   2.638  0.3386 -0.2844  0.7787
+  511SOL    HW1 1532   1.601   2.522   2.597 -0.4399  0.6030 -0.7963
+  511SOL    HW2 1533   1.709   2.618   2.644  0.4451 -0.4565 -0.6685
+  512SOL     OW 1534   1.938   2.880   0.661 -0.0692 -0.2646 -0.5070
+  512SOL    HW1 1535   1.910   2.900   0.571 -1.6755 -2.5448 -0.5031
+  512SOL    HW2 1536   1.987   2.798   0.653 -0.7263 -0.7721  0.8179
+  513SOL     OW 1537   1.379   0.356   2.521 -0.8600  0.0863  0.4002
+  513SOL    HW1 1538   1.405   0.300   2.448 -0.4079  0.5397  0.2110
+  513SOL    HW2 1539   1.447   0.424   2.524 -0.7151 -0.1067  1.4293
+  514SOL     OW 1540   2.358   2.753   1.326 -0.1404 -0.6419  0.0228
+  514SOL    HW1 1541   2.451   2.751   1.302 -0.3604 -0.4890 -0.8214
+  514SOL    HW2 1542   2.312   2.721   1.248 -0.7561 -1.2711  0.6417
+  515SOL     OW 1543   1.763   2.344   1.500  0.2380  0.5938  0.4310
+  515SOL    HW1 1544   1.669   2.357   1.491  0.3122  0.3700 -0.6978
+  515SOL    HW2 1545   1.797   2.356   1.411  1.1084 -0.6850  0.5997
+  516SOL     OW 1546   2.411   1.148   1.171  0.2229 -0.4082  0.6787
+  516SOL    HW1 1547   2.485   1.204   1.149  0.6315 -0.9072  0.7827
+  516SOL    HW2 1548   2.336   1.207   1.176  0.5558  0.0460  0.2393
+  517SOL     OW 1549   2.937   2.415  -0.003 -0.9709 -0.7072 -0.4583
+  517SOL    HW1 1550   2.857   2.440   0.044 -0.3352 -2.2846  1.4811
+  517SOL    HW2 1551   2.975   2.346   0.051  3.4430  2.3793  0.3012
+  518SOL     OW 1552   0.441   0.701   1.536 -0.0517 -0.3822 -0.0452
+  518SOL    HW1 1553   0.521   0.662   1.502  2.5928  3.9657  1.1222
+  518SOL    HW2 1554   0.415   0.764   1.470 -1.6701 -0.7309  0.2666
+  519SOL     OW 1555   2.588   1.056   1.945  0.5785 -0.2314  0.1486
+  519SOL    HW1 1556   2.496   1.050   1.970  1.0820  1.1133  2.2439
+  519SOL    HW2 1557   2.619   0.965   1.944 -0.4388 -0.5751  0.5821
+  520SOL     OW 1558   1.302   1.211   1.706  0.1531  0.0842  0.1049
+  520SOL    HW1 1559   1.234   1.270   1.739 -1.4071 -2.1811  0.8606
+  520SOL    HW2 1560   1.270   1.183   1.620  1.1400  0.7274 -0.4758
+  521SOL     OW 1561   1.212   0.541   1.533 -0.3047  0.0162 -0.9997
+  521SOL    HW1 1562   1.134   0.488   1.515 -0.3506 -0.3220  0.2012
+  521SOL    HW2 1563   1.267   0.485   1.587 -0.2392  1.0969  0.0638
+  522SOL     OW 1564   2.546   1.182   2.513 -0.0672  0.0057  0.2076
+  522SOL    HW1 1565   2.608   1.114   2.542 -0.6853  0.6617  3.0466
+  522SOL    HW2 1566   2.596   1.263   2.518 -0.4150  0.2215  0.1307
+  523SOL     OW 1567   1.291   1.255   2.019 -0.5182 -0.2522  0.0288
+  523SOL    HW1 1568   1.375   1.212   2.036 -0.4290 -0.0605  0.0700
+  523SOL    HW2 1569   1.226   1.192   2.048 -0.3586  0.3238  1.6165
+  524SOL     OW 1570   1.990   1.436   1.380  0.2063 -0.2115 -0.3619
+  524SOL    HW1 1571   1.952   1.512   1.426  4.3117  2.2070 -0.9267
+  524SOL    HW2 1572   2.030   1.384   1.450 -1.4634 -1.1713 -0.1262
+  525SOL     OW 1573   2.095   0.778   2.515 -0.3180  0.1810  0.0889
+  525SOL    HW1 1574   2.124   0.869   2.522  0.8312 -0.2240  0.6893
+  525SOL    HW2 1575   2.017   0.773   2.571  0.2468  0.5347  0.9045
+  526SOL     OW 1576   0.713   1.318   0.530 -0.1570 -0.1675  0.0582
+  526SOL    HW1 1577   0.656   1.291   0.603  0.8048  0.3146  0.9853
+  526SOL    HW2 1578   0.784   1.254   0.531 -0.0269 -0.0216 -0.3155
+  527SOL     OW 1579   0.566   2.951   0.713  0.1663 -0.1444 -0.0700
+  527SOL    HW1 1580   0.647   2.987   0.677  0.6955 -0.9788  0.3059
+  527SOL    HW2 1581   0.572   2.857   0.700 -0.4677 -0.2750  0.5199
+  528SOL     OW 1582   0.189   1.224   2.976  0.3896  0.7920 -0.5500
+  528SOL    HW1 1583   0.196   1.320   2.981 -0.3721  0.8447 -0.4267
+  528SOL    HW2 1584   0.132   1.208   2.901  0.4383  0.4384 -0.5101
+  529SOL     OW 1585   0.747   0.159   1.108  0.2625 -0.2288 -0.5308
+  529SOL    HW1 1586   0.734   0.239   1.056  1.0356 -0.2802 -0.8066
+  529SOL    HW2 1587   0.831   0.173   1.152 -1.4034  0.7283  2.3695
+  530SOL     OW 1588   2.167   1.601   0.263 -0.4262  0.9675 -0.8818
+  530SOL    HW1 1589   2.133   1.690   0.275  0.5452  1.2483 -0.1398
+  530SOL    HW2 1590   2.258   1.607   0.291 -0.5761 -0.4439 -0.0923
+  531SOL     OW 1591   0.555   2.273   1.289  0.3143 -0.4253  0.1216
+  531SOL    HW1 1592   0.641   2.315   1.296  0.3306 -0.4168 -0.1298
+  531SOL    HW2 1593   0.512   2.318   1.217 -0.5763  0.7587  1.3970
+  532SOL     OW 1594   0.174   0.598   0.366 -0.2975 -0.5389 -0.6501
+  532SOL    HW1 1595   0.252   0.594   0.312 -0.4853 -1.1606 -0.8753
+  532SOL    HW2 1596   0.158   0.507   0.393  1.8151  0.2946  3.3172
+  533SOL     OW 1597   2.018   2.671   1.803 -0.0898 -0.3166  0.7211
+  533SOL    HW1 1598   2.099   2.624   1.781  0.5464  0.3805  1.5635
+  533SOL    HW2 1599   1.948   2.610   1.781  0.7361 -1.5501  1.5398
+  534SOL     OW 1600   1.552   0.552   1.211  0.2689  0.3670 -0.4767
+  534SOL    HW1 1601   1.552   0.480   1.274 -1.9179 -0.8500 -1.9097
+  534SOL    HW2 1602   1.531   0.629   1.263  0.0889 -0.4039  0.5887
+  535SOL     OW 1603   2.211   1.416   1.837  0.1455  0.5265  0.4643
+  535SOL    HW1 1604   2.166   1.339   1.804  1.0151 -0.7664  2.3230
+  535SOL    HW2 1605   2.252   1.455   1.760  1.8562 -2.2964 -0.0686
+  536SOL     OW 1606   1.178   2.204   1.211  0.0105 -0.0205  0.2160
+  536SOL    HW1 1607   1.270   2.216   1.235  0.7103 -0.4403 -2.2491
+  536SOL    HW2 1608   1.143   2.293   1.207  0.6647  0.2169 -0.2372
+  537SOL     OW 1609   2.736   2.214   2.244 -0.5990 -0.5882 -0.0489
+  537SOL    HW1 1610   2.738   2.283   2.177  0.0088 -1.8392 -1.3352
+  537SOL    HW2 1611   2.695   2.140   2.199 -2.8475 -0.4879  1.8739
+  538SOL     OW 1612   0.552   0.998   1.567 -0.2277  0.2467 -0.0751
+  538SOL    HW1 1613   0.514   1.067   1.621 -1.9512 -0.4126 -0.4480
+  538SOL    HW2 1614   0.565   0.924   1.627  1.2050  0.7410  0.2268
+  539SOL     OW 1615   2.949   2.232   0.495  0.5030  0.2530  0.4330
+  539SOL    HW1 1616   3.029   2.234   0.548 -0.0249 -0.8151  1.2659
+  539SOL    HW2 1617   2.881   2.204   0.556 -0.5020  1.1130 -0.2892
+  540SOL     OW 1618   1.735   0.043   1.184  0.0227  0.0588  0.1645
+  540SOL    HW1 1619   1.655   0.082   1.148  0.5297  0.8514 -0.1231
+  540SOL    HW2 1620   1.767  -0.014   1.114  0.5856  0.9941 -0.3363
+  541SOL     OW 1621   1.009   0.273   0.821  0.3595 -0.7763 -0.2490
+  541SOL    HW1 1622   1.066   0.224   0.762 -1.0908 -0.6129 -1.7547
+  541SOL    HW2 1623   1.041   0.363   0.815 -0.1739 -0.6550 -1.2223
+  542SOL     OW 1624   2.064   0.711   2.960 -0.2626 -0.3712 -0.2104
+  542SOL    HW1 1625   2.146   0.711   3.011 -0.4185  2.6237  0.0284
+  542SOL    HW2 1626   1.997   0.733   3.024 -1.2317 -2.4707 -0.4928
+  543SOL     OW 1627   0.837   2.223   2.034 -0.1359  0.0281  0.7132
+  543SOL    HW1 1628   0.816   2.258   2.120  0.3121 -1.6619  1.5178
+  543SOL    HW2 1629   0.910   2.163   2.049 -0.5703 -0.8706 -0.7065
+  544SOL     OW 1630   2.835   2.502   0.505  0.1773  0.2237  0.2537
+  544SOL    HW1 1631   2.839   2.409   0.483 -0.7268  0.2524 -0.0113
+  544SOL    HW2 1632   2.925   2.533   0.492  0.9082 -1.0389  2.2200
+  545SOL     OW 1633   1.141   1.021   2.134 -0.3213 -0.0122  0.2659
+  545SOL    HW1 1634   1.182   1.033   2.220 -1.2528 -2.6735  1.0925
+  545SOL    HW2 1635   1.047   1.016   2.154 -0.6469  0.9174 -1.0714
+  546SOL     OW 1636   2.577   1.078   0.826  0.1208 -0.1679 -0.1935
+  546SOL    HW1 1637   2.601   0.987   0.840 -0.1093  0.0486  1.5341
+  546SOL    HW2 1638   2.588   1.091   0.731  3.3387 -1.0715  0.0377
+  547SOL     OW 1639   1.083   2.104   2.660 -0.7669  0.2527 -0.4466
+  547SOL    HW1 1640   1.097   2.177   2.720 -0.1865 -0.0998 -0.1555
+  547SOL    HW2 1641   1.062   2.146   2.576  0.8878  0.7691 -0.6060
+  548SOL     OW 1642   2.904   2.320   1.210  0.5362 -0.4184 -0.1079
+  548SOL    HW1 1643   2.975   2.383   1.217  0.8059 -0.8022  0.6755
+  548SOL    HW2 1644   2.934   2.244   1.261  0.5487 -1.4785 -1.6845
+  549SOL     OW 1645   2.488   2.230   0.401  0.9333  0.4941 -0.3483
+  549SOL    HW1 1646   2.474   2.227   0.496 -0.3533 -0.4547 -0.5554
+  549SOL    HW2 1647   2.534   2.312   0.386  0.1850  1.1652  0.9912
+  550SOL     OW 1648   0.165   0.760   0.603  0.1196 -0.0135  0.2576
+  550SOL    HW1 1649   0.172   0.694   0.534 -2.5432  0.2246 -0.2473
+  550SOL    HW2 1650   0.164   0.843   0.555  0.6488  0.2528  0.7179
+  551SOL     OW 1651   2.876   1.742   2.116  0.1088  0.0096 -0.0103
+  551SOL    HW1 1652   2.890   1.665   2.171 -0.5702 -2.0550 -2.6992
+  551SOL    HW2 1653   2.939   1.806   2.147  0.7910 -1.7114  2.1252
+  552SOL     OW 1654   1.800   0.821   0.397  0.5737  0.4884  0.1863
+  552SOL    HW1 1655   1.762   0.758   0.458  1.0887 -0.0706 -0.0668
+  552SOL    HW2 1656   1.863   0.870   0.448  0.2517  0.7266  0.3568
+  553SOL     OW 1657   0.955   0.892   2.779 -0.1442 -0.1660  0.6634
+  553SOL    HW1 1658   0.939   0.986   2.770 -0.3585 -0.3246 -0.6837
+  553SOL    HW2 1659   1.002   0.883   2.861 -1.6590  0.9480  1.6551
+  554SOL     OW 1660   1.992   2.077   1.774  0.4842  0.2867 -0.2964
+  554SOL    HW1 1661   2.016   2.097   1.684  1.5856  1.5447  0.2759
+  554SOL    HW2 1662   2.051   2.006   1.799  1.0057  0.7690 -0.1612
+  555SOL     OW 1663   1.828   1.786   2.192 -0.2904 -0.0092 -0.9713
+  555SOL    HW1 1664   1.871   1.769   2.276 -1.0304 -0.1983 -0.6329
+  555SOL    HW2 1665   1.776   1.865   2.207  2.3620  1.9703 -2.2627
+  556SOL     OW 1666   0.808   0.583   0.114 -0.7108 -0.3554  0.6717
+  556SOL    HW1 1667   0.855   0.562   0.195 -1.7596 -1.0237  1.1055
+  556SOL    HW2 1668   0.790   0.677   0.121  0.1976 -0.1822  0.6950
+  557SOL     OW 1669   2.903   1.809   0.144  0.5733  0.9137 -0.2703
+  557SOL    HW1 1670   2.832   1.758   0.104 -1.2993  2.0306  1.6417
+  557SOL    HW2 1671   2.917   1.882   0.084 -0.3244  1.0457 -0.3148
+  558SOL     OW 1672   1.587   1.766   0.815 -0.0900  1.2862 -0.0808
+  558SOL    HW1 1673   1.674   1.804   0.811  0.4378 -0.1335 -2.1161
+  558SOL    HW2 1674   1.533   1.823   0.761 -0.2387  1.6460  0.4436
+  559SOL     OW 1675   0.805   0.712   1.347 -0.6140 -0.1327 -0.2832
+  559SOL    HW1 1676   0.878   0.737   1.404 -0.1107 -0.3819 -0.8149
+  559SOL    HW2 1677   0.748   0.658   1.402 -0.8130  0.7447  0.3637
+  560SOL     OW 1678   2.802   2.172   1.516 -0.8342  0.3518 -0.0597
+  560SOL    HW1 1679   2.860   2.209   1.583 -2.7528 -0.8570  2.2504
+  560SOL    HW2 1680   2.855   2.105   1.473 -0.0896  0.1936  1.1257
+  561SOL     OW 1681   0.426   0.984   0.958  0.3212  0.8909 -0.3853
+  561SOL    HW1 1682   0.336   0.972   0.988 -0.1900  2.9908 -1.0969
+  561SOL    HW2 1683   0.478   0.984   1.038 -0.1055 -2.1335 -0.0925
+  562SOL     OW 1684   0.283   2.648   2.942 -0.2844 -0.1491  0.1590
+  562SOL    HW1 1685   0.359   2.642   2.884 -0.1767  0.9457  0.1859
+  562SOL    HW2 1686   0.282   2.739   2.971 -0.6333 -0.5741  1.4856
+  563SOL     OW 1687   0.915   2.392   2.601 -0.2609  0.0208 -0.2048
+  563SOL    HW1 1688   0.923   2.324   2.534 -1.2507  0.4015 -0.7153
+  563SOL    HW2 1689   0.966   2.466   2.567 -2.0159  0.7551 -1.2450
+  564SOL     OW 1690   0.609   1.101   2.239  0.0697 -0.2942 -0.2948
+  564SOL    HW1 1691   0.664   1.057   2.175  0.0870  1.3172 -1.3932
+  564SOL    HW2 1692   0.536   1.136   2.188 -0.3167  0.3056  0.6707
+  565SOL     OW 1693   2.372   2.700   2.973  0.5374 -0.3717 -0.0621
+  565SOL    HW1 1694   2.283   2.736   2.971  0.5019 -0.3802  1.5158
+  565SOL    HW2 1695   2.420   2.752   2.908 -0.0743  1.0218  0.6017
+  566SOL     OW 1696   1.463   1.752   1.553 -0.0283  0.4846  0.0849
+  566SOL    HW1 1697   1.496   1.675   1.600  0.2717  0.2488 -0.5104
+  566SOL    HW2 1698   1.538   1.781   1.500 -0.0945  1.2729  0.4183
+  567SOL     OW 1699   0.875   0.196   2.218 -0.3101 -0.4592 -0.0876
+  567SOL    HW1 1700   0.960   0.166   2.187  0.9817  4.6199 -1.5116
+  567SOL    HW2 1701   0.817   0.188   2.142 -0.2633 -0.6081 -0.1060
+  568SOL     OW 1702   2.604   0.012   0.514 -0.7666 -0.0467  0.5935
+  568SOL    HW1 1703   2.687   0.054   0.536 -1.3170  1.1977  0.3422
+  568SOL    HW2 1704   2.606   0.003   0.419 -0.9875  0.1421  0.5698
+  569SOL     OW 1705   0.163   1.098   2.268  0.2405  0.3001  0.2166
+  569SOL    HW1 1706   0.197   1.048   2.194  0.8056 -3.2294  2.9002
+  569SOL    HW2 1707   0.239   1.114   2.323 -0.9119 -0.4086  2.0052
+  570SOL     OW 1708   2.691   0.988   2.848  0.4840  0.7853 -0.5632
+  570SOL    HW1 1709   2.743   1.063   2.876 -0.4139  0.8982  0.8087
+  570SOL    HW2 1710   2.712   0.919   2.911 -0.2722  0.5310 -0.5802
+  571SOL     OW 1711   0.041   2.111   1.927  0.0421  0.4662 -0.6132
+  571SOL    HW1 1712   0.128   2.140   1.955 -0.8448  1.4763  1.0880
+  571SOL    HW2 1713   0.020   2.038   1.986 -0.9078  1.2057 -0.0323
+  572SOL     OW 1714   1.774   1.264   2.229 -0.2528 -0.4676  0.8914
+  572SOL    HW1 1715   1.798   1.199   2.294  0.3701 -0.7176  0.4084
+  572SOL    HW2 1716   1.822   1.343   2.255 -0.0479 -0.5516  0.7685
+  573SOL     OW 1717   1.525   0.966   2.712  0.3645 -0.3487  0.2354
+  573SOL    HW1 1718   1.433   0.979   2.692  0.4844 -0.0000 -0.0889
+  573SOL    HW2 1719   1.529   0.881   2.755 -0.1252 -0.3010  0.3651
+  574SOL     OW 1720   1.700   2.290   1.005  0.3283 -0.3802 -0.0944
+  574SOL    HW1 1721   1.636   2.236   1.053 -1.4996  2.4499  0.6225
+  574SOL    HW2 1722   1.698   2.256   0.916 -0.3254 -1.9933  0.5341
+  575SOL     OW 1723   2.053   2.717   1.212  0.1795 -0.8881 -0.0927
+  575SOL    HW1 1724   2.037   2.784   1.277 -0.5194 -0.6592 -0.4951
+  575SOL    HW2 1725   2.136   2.742   1.170 -0.9165  0.6153 -1.3626
+  576SOL     OW 1726   2.516   0.012   1.182 -0.1846 -0.1633  0.2906
+  576SOL    HW1 1727   2.553   0.005   1.094  0.1277  0.7424  0.3375
+  576SOL    HW2 1728   2.429   0.049   1.169 -0.2748 -0.3650  0.3301
+  577SOL     OW 1729   0.445   0.115   2.625  0.0999  0.0093  0.2474
+  577SOL    HW1 1730   0.469   0.146   2.538  3.5813  2.7167  2.1979
+  577SOL    HW2 1731   0.349   0.123   2.627  0.1275  1.1794 -2.8547
+  578SOL     OW 1732   0.569   2.860   2.047  0.3815  0.6083  0.0418
+  578SOL    HW1 1733   0.502   2.799   2.078  2.3539 -1.8357 -0.5318
+  578SOL    HW2 1734   0.529   2.904   1.972 -0.1598 -1.6613 -0.9883
+  579SOL     OW 1735   2.987   2.764   1.806 -0.2661  0.6086 -0.2620
+  579SOL    HW1 1736   2.951   2.724   1.886 -1.8010  1.3579 -0.5837
+  579SOL    HW2 1737   3.081   2.770   1.824 -0.1883 -2.0539  0.2591
+  580SOL     OW 1738   2.142   0.199   2.578  0.3050  0.3565 -0.0003
+  580SOL    HW1 1739   2.071   0.221   2.517  0.1817 -0.7287 -0.2447
+  580SOL    HW2 1740   2.106   0.219   2.664 -0.1545  0.1707 -0.1503
+  581SOL     OW 1741   2.451   2.917   2.494 -0.2869 -0.2269 -0.2927
+  581SOL    HW1 1742   2.398   2.938   2.571  2.0036  2.1696  0.6168
+  581SOL    HW2 1743   2.398   2.946   2.420 -1.4494 -0.3521  0.4821
+  582SOL     OW 1744   0.280   0.706   2.346  0.1527 -0.2810 -0.2076
+  582SOL    HW1 1745   0.364   0.713   2.302  0.9409  0.0840  1.3553
+  582SOL    HW2 1746   0.280   0.619   2.386  0.3267  0.2068  0.8749
+  583SOL     OW 1747   0.967   1.024   0.155 -0.5091 -0.2352  0.4199
+  583SOL    HW1 1748   1.031   0.972   0.107 -0.5926  0.5100 -0.5195
+  583SOL    HW2 1749   1.014   1.057   0.231 -0.2483 -1.0683  0.6147
+  584SOL     OW 1750   1.877   0.731   2.757 -0.7023  0.5924  0.6161
+  584SOL    HW1 1751   1.948   0.727   2.821  0.2572  1.5482 -0.3907
+  584SOL    HW2 1752   1.810   0.785   2.798 -0.1209  1.1295  0.8623
+  585SOL     OW 1753   1.274   1.782   0.792 -0.6871  0.4263 -0.1950
+  585SOL    HW1 1754   1.261   1.687   0.788  0.2125  0.2642  0.7631
+  585SOL    HW2 1755   1.213   1.812   0.858  0.1477  0.3114  0.6338
+  586SOL     OW 1756   0.950   1.462   2.967  0.4608 -0.5825  0.5535
+  586SOL    HW1 1757   0.973   1.402   3.039  1.6219 -1.1177 -0.2750
+  586SOL    HW2 1758   1.029   1.468   2.914 -0.1565  0.4449 -0.2490
+  587SOL     OW 1759   1.901   1.100   2.401 -0.0860  0.9924 -0.2193
+  587SOL    HW1 1760   1.994   1.117   2.416  0.6948 -0.7086 -3.0398
+  587SOL    HW2 1761   1.874   1.047   2.475  1.0388  2.0135  0.9336
+  588SOL     OW 1762   1.416   0.339   1.666 -0.1365 -0.0662  0.4868
+  588SOL    HW1 1763   1.381   0.250   1.664 -1.3985  0.4347  0.3438
+  588SOL    HW2 1764   1.452   0.349   1.754 -0.9509 -0.3436  0.8512
+  589SOL     OW 1765   2.497   1.585   1.670  0.5740 -0.6659  0.3429
+  589SOL    HW1 1766   2.533   1.670   1.644 -2.5632  1.6071  3.5112
+  589SOL    HW2 1767   2.552   1.556   1.742  2.1117 -1.6775 -1.2320
+  590SOL     OW 1768   1.079   0.111   2.658  0.3089 -0.4157 -0.8026
+  590SOL    HW1 1769   1.158   0.061   2.636 -0.5999 -1.9373 -0.6174
+  590SOL    HW2 1770   1.087   0.129   2.751  0.2018 -0.9740 -0.6866
+  591SOL     OW 1771   2.883   0.012   1.099  0.0593  0.6845  0.8889
+  591SOL    HW1 1772   2.959   0.045   1.052 -0.3946 -0.7645 -0.8646
+  591SOL    HW2 1773   2.826   0.088   1.111  1.4632  1.6147  1.5583
+  592SOL     OW 1774   1.634   2.722   0.249 -0.2138 -0.9598  0.3556
+  592SOL    HW1 1775   1.576   2.739   0.323 -0.8331  0.3200 -0.4387
+  592SOL    HW2 1776   1.664   2.808   0.221 -0.6935 -1.3877 -1.4688
+  593SOL     OW 1777   2.657   1.337   1.568  0.1401  0.0759  0.0480
+  593SOL    HW1 1778   2.600   1.410   1.542 -1.5537 -1.3194 -0.1321
+  593SOL    HW2 1779   2.641   1.326   1.661  0.1218 -0.3163 -0.0013
+  594SOL     OW 1780   0.653   0.374   0.161  0.2976 -0.1299  0.0620
+  594SOL    HW1 1781   0.610   0.321   0.094 -0.1598 -1.2308  1.2259
+  594SOL    HW2 1782   0.683   0.452   0.115  1.7797 -1.8319 -1.8375
+  595SOL     OW 1783   2.987   1.178   0.277  0.4811  0.5404  0.1690
+  595SOL    HW1 1784   3.062   1.238   0.273 -0.5125  1.6267 -2.1210
+  595SOL    HW2 1785   2.913   1.234   0.299 -0.0171 -0.2177  0.4073
+  596SOL     OW 1786   0.287   2.667   0.337 -0.2073  0.0772 -0.5874
+  596SOL    HW1 1787   0.320   2.579   0.322 -0.4970 -0.2957  0.9388
+  596SOL    HW2 1788   0.207   2.655   0.388 -0.9129  1.1563 -1.4089
+  597SOL     OW 1789   0.300   0.314   2.042  0.1317  0.4780  0.0948
+  597SOL    HW1 1790   0.326   0.249   1.977  1.5891  0.2139  0.9385
+  597SOL    HW2 1791   0.234   0.368   1.998  2.1028  1.1487 -2.0355
+  598SOL     OW 1792   0.312   2.302   0.223 -0.2418 -0.2705  0.5273
+  598SOL    HW1 1793   0.341   2.249   0.148 -0.4379 -0.8839  0.8848
+  598SOL    HW2 1794   0.381   2.367   0.235  0.2066 -0.7177  0.3544
+  599SOL     OW 1795   1.257   1.424   2.496 -0.1915 -0.1473  0.0702
+  599SOL    HW1 1796   1.340   1.382   2.517 -0.6930 -0.2414  1.8357
+  599SOL    HW2 1797   1.240   1.398   2.405  2.5707  1.2850 -0.8886
+  600SOL     OW 1798   0.765   2.699   0.976 -0.1477 -0.0562 -0.6017
+  600SOL    HW1 1799   0.715   2.777   1.002  3.2538  1.4041  1.5720
+  600SOL    HW2 1800   0.777   2.710   0.881 -0.2429  1.7596 -0.4180
+  601SOL     OW 1801   2.997   0.043   2.949 -0.1436 -0.0762 -0.1958
+  601SOL    HW1 1802   2.910   0.022   2.984 -0.2858  2.0206  0.7459
+  601SOL    HW2 1803   3.034   0.104   3.013  1.2073  0.5953 -1.6183
+  602SOL     OW 1804   3.008   1.288   2.110  0.0133 -0.2473 -0.4904
+  602SOL    HW1 1805   3.058   1.232   2.170 -2.4734  0.3498  2.1314
+  602SOL    HW2 1806   2.920   1.249   2.108 -1.2353  2.5520 -0.0091
+  603SOL     OW 1807   0.847   1.942   0.455 -0.7575 -0.2541  0.2081
+  603SOL    HW1 1808   0.798   1.861   0.463 -0.3078 -0.6004 -0.4837
+  603SOL    HW2 1809   0.864   1.950   0.361 -1.1539  0.8468  0.2326
+  604SOL     OW 1810   2.645   2.361   1.197  0.0258 -0.3134  0.4274
+  604SOL    HW1 1811   2.645   2.376   1.291  0.4707 -0.8516  0.5113
+  604SOL    HW2 1812   2.736   2.342   1.175  0.3326  2.2036 -0.4838
+  605SOL     OW 1813   1.635   2.923   0.619 -0.3911 -0.2632 -0.1423
+  605SOL    HW1 1814   1.593   2.925   0.705 -2.2722  0.1022 -1.0786
+  605SOL    HW2 1815   1.714   2.870   0.633 -0.3500  0.3496  1.9585
+  606SOL     OW 1816   0.230   0.207   1.533  0.1947 -0.3736 -0.0442
+  606SOL    HW1 1817   0.205   0.261   1.609  1.7361  1.4213 -0.8323
+  606SOL    HW2 1818   0.293   0.145   1.569  0.1858 -0.1626  0.3465
+  607SOL     OW 1819   1.044   0.993   2.413 -0.2483 -0.0547  0.3037
+  607SOL    HW1 1820   0.983   1.002   2.339 -0.1594 -1.8484 -0.0071
+  607SOL    HW2 1821   1.121   1.043   2.387 -0.5624  0.0337 -0.4680
+  608SOL     OW 1822   1.623   1.241   1.281 -0.0264 -0.3672  0.0999
+  608SOL    HW1 1823   1.637   1.283   1.367  1.0827  1.1441 -0.8109
+  608SOL    HW2 1824   1.528   1.245   1.268 -0.0073  1.1443  0.3821
+  609SOL     OW 1825   0.768   1.439   1.300 -0.2400 -0.7603  0.1714
+  609SOL    HW1 1826   0.803   1.356   1.270 -1.1478 -0.4831 -1.6127
+  609SOL    HW2 1827   0.673   1.431   1.288 -0.0476  1.6651 -2.7013
+  610SOL     OW 1828   0.306   0.462   2.477 -0.0424 -0.1922  0.5725
+  610SOL    HW1 1829   0.399   0.476   2.494  0.2542 -0.4403 -0.8730
+  610SOL    HW2 1830   0.296   0.367   2.474  0.2642 -0.1135 -2.7002
+  611SOL     OW 1831   1.569   0.868   1.958  0.6661 -0.5274  0.6670
+  611SOL    HW1 1832   1.617   0.899   1.882 -1.6530  0.3660 -0.4188
+  611SOL    HW2 1833   1.583   0.773   1.957  1.1288 -0.4489 -0.5201
+  612SOL     OW 1834   1.662   0.965   0.803 -0.2395 -0.0012 -0.2270
+  612SOL    HW1 1835   1.624   0.930   0.884  0.2454 -0.0846 -0.0375
+  612SOL    HW2 1836   1.739   0.911   0.787 -0.1677  0.2062 -0.5901
+  613SOL     OW 1837   2.454   0.792   0.586  1.1724  0.5905 -0.0556
+  613SOL    HW1 1838   2.396   0.845   0.639  1.4028  0.0695  0.7100
+  613SOL    HW2 1839   2.529   0.773   0.643  0.2511 -2.0954  0.2693
+  614SOL     OW 1840   2.875   0.770   2.620 -0.3008 -0.4084  0.9604
+  614SOL    HW1 1841   2.893   0.710   2.692  0.5929 -2.2221 -0.7470
+  614SOL    HW2 1842   2.952   0.762   2.563  0.2729  3.0970  1.2213
+  615SOL     OW 1843   1.093   0.036   0.680 -0.2581 -0.6707 -0.0588
+  615SOL    HW1 1844   1.126  -0.051   0.661 -0.4348 -0.6830 -0.3109
+  615SOL    HW2 1845   0.998   0.029   0.672 -0.2674 -0.4314 -0.1880
+  616SOL     OW 1846   2.691   2.493   0.108  0.5137 -0.5225 -0.4023
+  616SOL    HW1 1847   2.636   2.415   0.102  0.4363 -0.4812 -0.2049
+  616SOL    HW2 1848   2.634   2.565   0.079  0.5070 -0.5009 -0.3354
+  617SOL     OW 1849   1.192   2.799   1.984 -0.2666 -0.2739 -0.0050
+  617SOL    HW1 1850   1.108   2.753   1.988 -1.0844  1.2713  0.5110
+  617SOL    HW2 1851   1.222   2.803   2.075 -0.6069  0.9569  0.0600
+  618SOL     OW 1852   0.680   1.526   2.390 -0.0170 -0.6356  0.4432
+  618SOL    HW1 1853   0.674   1.514   2.295  3.1169 -1.1868  0.3006
+  618SOL    HW2 1854   0.654   1.442   2.427  0.7970 -1.0785  0.0148
+  619SOL     OW 1855   1.452   2.318   0.570  0.2213 -0.3217  0.4040
+  619SOL    HW1 1856   1.448   2.330   0.665 -1.4491  2.5888 -0.0240
+  619SOL    HW2 1857   1.385   2.252   0.551 -0.3411  0.0513  1.0995
+  620SOL     OW 1858   2.091   0.709   0.657  0.5190 -0.3210 -0.2337
+  620SOL    HW1 1859   2.178   0.704   0.697 -0.0129 -0.2619  0.9212
+  620SOL    HW2 1860   2.053   0.622   0.669  0.4926 -0.3996 -0.8971
+  621SOL     OW 1861   1.030   2.615   0.980  0.1767 -0.5077  0.1781
+  621SOL    HW1 1862   1.054   2.596   0.889  0.0140  1.6735 -0.3122
+  621SOL    HW2 1863   0.934   2.612   0.980  0.1600  0.0956  0.1565
+  622SOL     OW 1864   1.426   1.740   2.626 -0.3550 -0.3340  0.3491
+  622SOL    HW1 1865   1.357   1.789   2.581  0.0936 -0.0344 -0.0040
+  622SOL    HW2 1866   1.459   1.801   2.692 -0.3735 -0.4491  0.4640
+  623SOL     OW 1867   1.569   1.392   0.068  0.3732  0.4368 -0.5465
+  623SOL    HW1 1868   1.634   1.459   0.087  0.3415  1.0383 -2.5106
+  623SOL    HW2 1869   1.486   1.431   0.095  0.7909 -0.1421  1.5106
+  624SOL     OW 1870   0.800   0.950   2.049 -0.2539 -0.8217  0.8142
+  624SOL    HW1 1871   0.864   0.881   2.033  0.8250  0.1007  1.1394
+  624SOL    HW2 1872   0.816   1.014   1.979  0.4603  0.3246  2.0234
+  625SOL     OW 1873   0.055   1.148   0.590 -0.5448  0.4962  0.3800
+  625SOL    HW1 1874  -0.002   1.163   0.514  0.4337 -0.4717 -0.5428
+  625SOL    HW2 1875   0.022   1.067   0.629  1.3076 -0.9865 -1.1563
+  626SOL     OW 1876   1.978   1.703   1.524  0.2663 -0.1907 -0.4754
+  626SOL    HW1 1877   1.957   1.723   1.615  0.0530 -0.2272 -0.5186
+  626SOL    HW2 1878   2.016   1.785   1.489 -0.9975  0.2624 -0.7760
+  627SOL     OW 1879   1.124   0.243   2.903 -0.2465  0.4698 -0.4429
+  627SOL    HW1 1880   1.057   0.238   2.972 -0.0453  1.4176 -0.1795
+  627SOL    HW2 1881   1.151   0.335   2.903  2.5875 -0.3707  0.9196
+  628SOL     OW 1882   2.845   2.038   0.920 -0.0324 -0.7239 -0.1850
+  628SOL    HW1 1883   2.771   2.061   0.978  1.1647 -0.8742  1.4025
+  628SOL    HW2 1884   2.894   1.972   0.969  0.7807 -1.1284 -1.5438
+  629SOL     OW 1885   1.071   0.183   1.324 -0.7599 -0.2342 -0.3543
+  629SOL    HW1 1886   1.032   0.230   1.398 -0.4175 -0.1559 -0.2233
+  629SOL    HW2 1887   1.026   0.218   1.247  0.5734  1.3904 -0.3851
+  630SOL     OW 1888   1.102   2.796   0.093 -0.6693  0.2066 -0.8998
+  630SOL    HW1 1889   1.011   2.803   0.066 -1.1919 -1.9623  0.3305
+  630SOL    HW2 1890   1.147   2.863   0.041 -2.8381  0.8492 -1.9701
+  631SOL     OW 1891   2.446   1.638   0.360 -0.5323  0.0532 -0.0349
+  631SOL    HW1 1892   2.540   1.650   0.351 -0.8293  1.4390 -1.3152
+  631SOL    HW2 1893   2.436   1.557   0.411  1.3429  0.1244  0.4273
+  632SOL     OW 1894   2.729   0.211   1.251 -0.3153 -0.3868 -0.2436
+  632SOL    HW1 1895   2.694   0.128   1.285 -1.6786 -0.0763 -0.8698
+  632SOL    HW2 1896   2.765   0.255   1.327 -2.8521  0.0621  0.6965
+  633SOL     OW 1897  -0.022   2.891   0.454  0.1265  0.3620 -0.5111
+  633SOL    HW1 1898   0.003   2.883   0.362 -0.3847  0.8567 -0.6900
+  633SOL    HW2 1899   0.024   2.969   0.485  1.7178 -0.8152  0.1222
+  634SOL     OW 1900   2.415   1.936   1.949  0.5605 -0.4299 -0.2472
+  634SOL    HW1 1901   2.485   1.880   1.913  1.3247 -0.1316  0.7640
+  634SOL    HW2 1902   2.449   1.964   2.034 -1.3943 -1.4033  0.8399
+  635SOL     OW 1903   0.442   1.355   1.719  0.5351  0.3652  0.1929
+  635SOL    HW1 1904   0.498   1.374   1.794  1.3656  1.7559 -0.7753
+  635SOL    HW2 1905   0.503   1.338   1.647 -0.2650  0.2251 -0.4574
+  636SOL     OW 1906   0.350   1.785   2.396  0.5575 -0.0776  0.0435
+  636SOL    HW1 1907   0.295   1.851   2.439  1.1694  0.5127 -0.0842
+  636SOL    HW2 1908   0.328   1.703   2.440 -2.6872 -0.3608 -2.0892
+  637SOL     OW 1909   0.225   2.289   2.384  0.1316  0.2124 -0.3757
+  637SOL    HW1 1910   0.247   2.213   2.438  0.2043  0.9903  0.6746
+  637SOL    HW2 1911   0.137   2.270   2.352 -0.3245  0.2343  0.8525
+  638SOL     OW 1912   0.052   2.813   2.812  0.2174 -0.1258 -0.5463
+  638SOL    HW1 1913   0.004   2.869   2.874 -0.8778 -2.1129  0.4179
+  638SOL    HW2 1914  -0.002   2.734   2.805  2.2990 -1.4940 -1.0804
+  639SOL     OW 1915   1.609   2.051   0.122 -0.0885  0.7996 -0.7406
+  639SOL    HW1 1916   1.636   2.142   0.110 -0.4631  1.0710  0.4251
+  639SOL    HW2 1917   1.576   2.048   0.212 -2.4422  0.1072 -1.6314
+  640SOL     OW 1918   0.809   2.507   1.773  0.8820  0.2983  0.9161
+  640SOL    HW1 1919   0.806   2.560   1.693  1.9147  0.7951  1.2034
+  640SOL    HW2 1920   0.861   2.431   1.750  0.8504  0.2490  1.0061
+  641SOL     OW 1921   2.359   2.452   3.022 -0.1350  0.3331 -0.4659
+  641SOL    HW1 1922   2.365   2.545   3.002 -2.4890  0.3694 -1.0127
+  641SOL    HW2 1923   2.396   2.408   2.945 -0.2243  0.7788 -0.7640
+  642SOL     OW 1924   1.899   1.005   1.781 -0.3712 -0.7133 -0.4354
+  642SOL    HW1 1925   1.943   0.921   1.788  1.1896 -0.0111 -1.7119
+  642SOL    HW2 1926   1.935   1.057   1.852 -0.6695 -0.8330 -0.1986
+  643SOL     OW 1927   1.268  -0.025   2.986 -0.0532  0.1317 -0.7383
+  643SOL    HW1 1928   1.348   0.021   3.011 -0.3732  0.2908 -0.0129
+  643SOL    HW2 1929   1.215   0.041   2.941 -1.2052  0.8930  1.7316
+  644SOL     OW 1930   1.100   1.849   1.040  0.2867 -0.3807 -0.3101
+  644SOL    HW1 1931   1.028   1.911   1.034  0.4476 -0.2619 -1.0373
+  644SOL    HW2 1932   1.068   1.781   1.099 -0.0171  0.2172  0.2155
+  645SOL     OW 1933   2.969   0.773   1.796 -0.1417 -0.3516  0.1664
+  645SOL    HW1 1934   2.894   0.801   1.848  0.9072  1.2371  0.8319
+  645SOL    HW2 1935   3.045   0.791   1.851  0.6347 -3.1602 -0.0005
+  646SOL     OW 1936   0.302   2.090   2.540  0.4677  0.3832  0.0672
+  646SOL    HW1 1937   0.274   2.008   2.581 -3.3157  1.6469 -0.0013
+  646SOL    HW2 1938   0.327   2.146   2.614  0.1042  0.2665  0.2793
+  647SOL     OW 1939   0.630   1.795   2.294  0.0443  0.5757  0.3880
+  647SOL    HW1 1940   0.672   1.710   2.302 -1.3282  0.0365  1.7012
+  647SOL    HW2 1941   0.536   1.777   2.297 -0.2609  1.9989 -0.5007
+  648SOL     OW 1942   0.120   0.216   0.102 -0.0285 -0.6049 -0.5802
+  648SOL    HW1 1943   0.103   0.311   0.101  1.6668 -0.3156 -0.8414
+  648SOL    HW2 1944   0.177   0.203   0.178 -2.4660 -1.7816  1.0541
+  649SOL     OW 1945   1.468   1.367   1.504  0.6475  0.2529  0.1781
+  649SOL    HW1 1946   1.527   1.415   1.562  0.5186 -1.1632  1.4816
+  649SOL    HW2 1947   1.413   1.315   1.562 -1.4377  1.0645 -1.0876
+  650SOL     OW 1948   1.645   0.990   1.714  0.0678  1.1828  0.3922
+  650SOL    HW1 1949   1.623   1.082   1.728 -0.9320  0.6430  2.2574
+  650SOL    HW2 1950   1.732   0.980   1.751  0.0280  1.2945  0.5166
+  651SOL     OW 1951   1.787   0.924   2.945  0.4236 -0.5346  0.0424
+  651SOL    HW1 1952   1.727   0.893   3.013  1.9867 -0.7520  1.3041
+  651SOL    HW2 1953   1.845   0.985   2.989  3.3517 -2.9640 -0.4774
+  652SOL     OW 1954   0.737   1.320   0.867 -0.2702  0.4419  0.3073
+  652SOL    HW1 1955   0.779   1.388   0.919  0.4523 -0.3623  0.7892
+  652SOL    HW2 1956   0.756   1.239   0.914  0.3342 -0.2777 -1.1757
+  653SOL     OW 1957   1.052   0.766   1.564  0.0601  0.0425 -0.1303
+  653SOL    HW1 1958   1.111   0.691   1.551  0.1807  0.3192 -1.1573
+  653SOL    HW2 1959   1.059   0.816   1.483  0.4726  1.5169  0.8161
+  654SOL     OW 1960   2.124   2.023   2.822 -0.0432  0.5764 -0.2770
+  654SOL    HW1 1961   2.061   2.057   2.759 -0.8675  0.4722  0.4973
+  654SOL    HW2 1962   2.208   2.025   2.775 -0.3245  2.3919 -0.6878
+  655SOL     OW 1963   0.030   0.953   2.800 -0.5668  0.2846  0.1607
+  655SOL    HW1 1964   0.041   0.891   2.872 -0.2373 -0.1570 -0.2727
+  655SOL    HW2 1965  -0.008   0.901   2.729  0.5772  0.3726 -0.5082
+  656SOL     OW 1966   2.993   1.286   2.784 -0.6442 -0.2810 -0.2436
+  656SOL    HW1 1967   2.916   1.281   2.842 -0.0876 -2.2354  0.3317
+  656SOL    HW2 1968   2.957   1.284   2.696 -1.3149 -0.4064  0.0318
+  657SOL     OW 1969   1.370   0.225   0.193  0.1905  0.2621  0.0413
+  657SOL    HW1 1970   1.419   0.254   0.270 -0.5583  1.6717 -0.0203
+  657SOL    HW2 1971   1.351   0.306   0.145  2.2988 -0.7913 -2.5630
+  658SOL     OW 1972   2.947   0.070   2.688  0.2855 -0.3928  0.1435
+  658SOL    HW1 1973   3.010  -0.001   2.703  1.3360  0.5818  0.3252
+  658SOL    HW2 1974   2.933   0.109   2.774 -0.5639 -0.5990  0.1034
+  659SOL     OW 1975   0.114   2.637   1.502 -0.2024 -0.3200 -0.0121
+  659SOL    HW1 1976   0.123   2.606   1.592 -2.1404 -1.4118 -0.2090
+  659SOL    HW2 1977   0.025   2.671   1.496 -0.2200 -0.6131 -1.6393
+  660SOL     OW 1978   2.818   0.362   2.058 -0.2865  0.2292 -0.3621
+  660SOL    HW1 1979   2.775   0.276   2.061 -1.0315  0.7299  3.0577
+  660SOL    HW2 1980   2.798   0.402   2.142 -0.1026  3.8786 -2.0622
+  661SOL     OW 1981   2.264   0.930   2.905 -0.1152  0.0508 -0.0559
+  661SOL    HW1 1982   2.209   0.870   2.854  0.5684 -0.8996  0.3062
+  661SOL    HW2 1983   2.350   0.887   2.906  0.4990  1.2800 -0.2079
+  662SOL     OW 1984   0.811   3.006   3.005 -0.4553  0.0377  0.5516
+  662SOL    HW1 1985   0.864   2.931   2.976 -3.0938 -1.9265  0.9068
+  662SOL    HW2 1986   0.780   3.045   2.923  2.0347  1.5611  0.3132
+  663SOL     OW 1987   1.279   0.041   1.237  0.2437 -0.0423 -0.0516
+  663SOL    HW1 1988   1.205   0.076   1.287 -0.4226  2.0200 -2.4646
+  663SOL    HW2 1989   1.240  -0.019   1.174  0.5624  1.5069 -1.7315
+  664SOL     OW 1990   0.693   0.754   2.842  0.0937  0.2726 -0.3281
+  664SOL    HW1 1991   0.708   0.761   2.937  0.4825  2.4576 -0.5476
+  664SOL    HW2 1992   0.772   0.789   2.802 -0.3920  0.0764 -1.4573
+  665SOL     OW 1993   1.397   0.439   3.018 -0.1863  0.9145  0.5552
+  665SOL    HW1 1994   1.401   0.397   2.931 -2.2523  1.8721  0.0104
+  665SOL    HW2 1995   1.483   0.480   3.027 -0.0128  0.9478 -1.1065
+  666SOL     OW 1996   0.618   1.771   2.569  1.2969  0.2947 -0.2809
+  666SOL    HW1 1997   0.697   1.763   2.515  1.2535  1.1698 -0.4786
+  666SOL    HW2 1998   0.570   1.690   2.553 -0.5069  2.1946 -4.4244
+  667SOL     OW 1999   0.125   2.468   2.000  0.0555  0.4812  0.2889
+  667SOL    HW1 2000   0.181   2.460   1.923  1.8965 -0.5947  1.7358
+  667SOL    HW2 2001   0.095   2.379   2.017 -1.1957  0.8895  0.1666
+  668SOL     OW 2002   2.559   2.065   1.034  0.2415  0.0919  0.5724
+  668SOL    HW1 2003   2.487   2.003   1.043  1.6863 -1.5775  0.7367
+  668SOL    HW2 2004   2.543   2.132   1.100 -1.6188  0.1792  0.0249
+  669SOL     OW 2005   0.783   2.425   1.117  0.5996 -0.0687 -0.1636
+  669SOL    HW1 2006   0.759   2.516   1.101  0.4127 -0.5655 -2.6365
+  669SOL    HW2 2007   0.723   2.374   1.062  1.9389 -1.6122 -0.1940
+  670SOL     OW 2008   1.640   0.594   1.918  0.5760  0.3715  0.0115
+  670SOL    HW1 2009   1.587   0.526   1.876  0.4050  0.7576 -0.3979
+  670SOL    HW2 2010   1.679   0.550   1.995 -0.7005  0.2105  0.5704
+  671SOL     OW 2011   0.465   1.459   1.440  0.3233 -0.5403  0.3805
+  671SOL    HW1 2012   0.533   1.509   1.485  0.6468 -1.9936  1.5144
+  671SOL    HW2 2013   0.395   1.522   1.423  1.1982  0.5445  0.8375
+  672SOL     OW 2014   0.409  -0.019   1.839  0.2652 -0.6541 -0.9026
+  672SOL    HW1 2015   0.435   0.069   1.812  0.5870 -0.5446 -0.2288
+  672SOL    HW2 2016   0.316  -0.011   1.860  0.3487 -0.4152 -0.6139
+  673SOL     OW 2017   1.062   2.486   2.018 -0.5153  0.2755  0.0109
+  673SOL    HW1 2018   1.079   2.516   1.929 -3.2740 -1.4832 -1.1228
+  673SOL    HW2 2019   0.972   2.514   2.036  1.0411  3.1763  3.2916
+  674SOL     OW 2020   2.489   0.940   2.332  0.2961  0.3399 -0.1832
+  674SOL    HW1 2021   2.450   0.853   2.323  0.0811  0.4812 -0.6220
+  674SOL    HW2 2022   2.475   0.963   2.424  1.5006 -0.6311  0.2440
+  675SOL     OW 2023   2.016   1.513   1.980  0.0264 -0.3996 -0.5805
+  675SOL    HW1 2024   2.037   1.456   2.053 -0.9420 -0.4792 -0.3736
+  675SOL    HW2 2025   2.093   1.508   1.923  0.4907 -0.8617  0.0915
+  676SOL     OW 2026   1.103   2.111   2.001  0.1966  0.3158  0.1354
+  676SOL    HW1 2027   1.152   2.192   1.988  1.2614 -0.5312 -1.0505
+  676SOL    HW2 2028   1.157   2.060   2.061  0.5877 -0.4666 -0.8862
+  677SOL     OW 2029   0.652   0.486   2.424  0.6110 -0.5398 -0.3369
+  677SOL    HW1 2030   0.741   0.465   2.453  0.6039  1.4860  1.1085
+  677SOL    HW2 2031   0.608   0.519   2.502 -0.1911  2.3043 -1.9932
+  678SOL     OW 2032   1.109   0.807  -0.011  0.4152 -0.0413 -0.3596
+  678SOL    HW1 2033   1.109   0.711  -0.016  0.1256 -0.0189 -0.7953
+  678SOL    HW2 2034   1.190   0.828   0.034  1.2189 -0.4214 -1.6147
+  679SOL     OW 2035   0.472   2.814   2.485 -0.9505 -0.3606  0.1131
+  679SOL    HW1 2036   0.550   2.869   2.485 -0.3563 -1.1923 -1.5890
+  679SOL    HW2 2037   0.404   2.868   2.524 -0.2023  1.3542 -0.9774
+  680SOL     OW 2038   0.073   0.697   1.575  0.1101  0.6831 -0.3121
+  680SOL    HW1 2039   0.114   0.767   1.525 -1.5204 -0.2370 -2.9467
+  680SOL    HW2 2040   0.021   0.742   1.642  0.0914  1.9398 -1.1780
+  681SOL     OW 2041   1.014   2.811   1.385  0.6768  0.6987  0.0742
+  681SOL    HW1 2042   0.918   2.816   1.387  0.6772  0.4861  0.6856
+  681SOL    HW2 2043   1.037   2.825   1.294 -0.0582 -1.4799 -0.4329
+  682SOL     OW 2044   1.033   0.297   1.617 -0.1610  0.4301  0.3483
+  682SOL    HW1 2045   0.964   0.289   1.683 -0.1992  0.2846  0.2897
+  682SOL    HW2 2046   1.091   0.223   1.633 -0.6371 -0.0953 -0.3291
+  683SOL     OW 2047   1.181   0.885   0.866 -0.4179  0.0667 -0.2420
+  683SOL    HW1 2048   1.250   0.950   0.852  0.0078  0.3232  3.2030
+  683SOL    HW2 2049   1.111   0.911   0.806  0.5201  2.5631 -0.2432
+  684SOL     OW 2050   2.389   1.407   0.508  1.0070 -0.6874 -0.1618
+  684SOL    HW1 2051   2.424   1.321   0.529 -0.8061 -1.4012 -0.0560
+  684SOL    HW2 2052   2.342   1.433   0.588  1.5839  0.4064 -0.1835
+  685SOL     OW 2053   1.088   1.355   1.770 -0.0361  0.3352  0.0125
+  685SOL    HW1 2054   1.031   1.304   1.828  1.1130  1.5526  2.2031
+  685SOL    HW2 2055   1.035   1.371   1.692 -2.1829  0.1750  1.4383
+  686SOL     OW 2056   2.771   1.012   0.283  0.0429 -0.2571  0.1283
+  686SOL    HW1 2057   2.858   1.044   0.259  1.4381 -3.8575  0.4416
+  686SOL    HW2 2058   2.720   1.018   0.202  1.2836 -0.5938 -0.6761
+  687SOL     OW 2059   0.069   1.802   0.760 -0.5383  0.0951 -0.3074
+  687SOL    HW1 2060   0.024   1.813   0.844 -1.0476 -2.1702 -0.2922
+  687SOL    HW2 2061   0.160   1.823   0.779 -0.8431  0.6286  0.5715
+  688SOL     OW 2062   0.870   2.008   2.214  0.3013 -0.1602 -0.8553
+  688SOL    HW1 2063   0.905   1.920   2.204 -1.5356 -1.0312  0.2023
+  688SOL    HW2 2064   0.777   1.999   2.193 -0.1252  1.2930  0.3888
+  689SOL     OW 2065   2.742   0.574   1.305 -0.3109  0.4574  0.5950
+  689SOL    HW1 2066   2.803   0.641   1.276 -1.0473  0.5400 -0.7504
+  689SOL    HW2 2067   2.791   0.523   1.370  1.0847  0.7229 -0.2609
+  690SOL     OW 2068   2.189   0.519   2.761 -0.0494 -0.4762 -0.0308
+  690SOL    HW1 2069   2.190   0.573   2.839  0.4961 -0.2782 -0.1732
+  690SOL    HW2 2070   2.182   0.581   2.688  1.2257 -0.5480 -0.2268
+  691SOL     OW 2071   1.129   3.016   1.837  0.6733  0.1636 -0.0107
+  691SOL    HW1 2072   1.186   2.958   1.888  0.1745 -0.3753 -0.0639
+  691SOL    HW2 2073   1.042   3.004   1.876 -0.1217 -1.9760 -2.4883
+  692SOL     OW 2074   1.417   2.315   1.365 -0.2905 -0.2515  0.3711
+  692SOL    HW1 2075   1.425   2.410   1.361  0.6490 -0.3390  0.2509
+  692SOL    HW2 2076   1.457   2.291   1.448  1.6025 -0.7697 -0.6766
+  693SOL     OW 2077   1.040   0.248   0.419  0.1110 -0.5315 -0.4889
+  693SOL    HW1 2078   1.086   0.167   0.397 -1.6590 -1.3119 -1.3054
+  693SOL    HW2 2079   1.072   0.271   0.506  1.5729 -1.4126 -0.7897
+  694SOL     OW 2080   2.808   2.679   1.566 -0.2954  0.0169  0.2407
+  694SOL    HW1 2081   2.802   2.713   1.656  0.4961  0.3046  0.1802
+  694SOL    HW2 2082   2.761   2.744   1.513 -2.5182 -1.1780  0.7252
+  695SOL     OW 2083   2.650   1.702   2.877  0.2923 -0.2246 -0.4689
+  695SOL    HW1 2084   2.702   1.651   2.939  0.7565 -0.9100 -1.4104
+  695SOL    HW2 2085   2.585   1.746   2.930  1.7970  0.5732  0.7018
+  696SOL     OW 2086   2.505   0.405   2.061 -0.1562  0.0769 -0.3826
+  696SOL    HW1 2087   2.542   0.483   2.020 -0.4814  1.2784  1.6454
+  696SOL    HW2 2088   2.557   0.393   2.140 -0.6395 -1.3084 -0.2756
+  697SOL     OW 2089   1.729   2.103   1.914 -0.2040 -0.3414 -0.5139
+  697SOL    HW1 2090   1.711   2.034   1.850 -1.1462 -0.8172  0.2796
+  697SOL    HW2 2091   1.823   2.098   1.929 -0.1780 -1.0507 -0.9217
+  698SOL     OW 2092   2.585   0.278   0.282  0.2127 -0.0328  0.5652
+  698SOL    HW1 2093   2.586   0.259   0.376  1.5251  1.0879  0.7804
+  698SOL    HW2 2094   2.662   0.232   0.248 -0.5199 -0.8110 -0.0287
+  699SOL     OW 2095   1.210   0.623   1.177 -0.0722  0.4696 -0.0212
+  699SOL    HW1 2096   1.166   0.539   1.166 -0.3939  0.5852  0.3816
+  699SOL    HW2 2097   1.291   0.603   1.223  0.5169  0.1941 -1.1800
+  700SOL     OW 2098   2.092   1.321   2.677  0.0353 -0.1872 -0.1457
+  700SOL    HW1 2099   2.096   1.307   2.772  1.3393  2.1747  0.1682
+  700SOL    HW2 2100   2.025   1.389   2.666  1.3615  1.0205 -0.7462
+  701SOL     OW 2101   1.740   2.771   1.301  0.1161 -0.1319  0.2676
+  701SOL    HW1 2102   1.654   2.789   1.262 -0.3365  0.0658  1.3349
+  701SOL    HW2 2103   1.719   2.722   1.382  1.1689 -2.7948 -1.0691
+  702SOL     OW 2104   2.765   0.877   2.303  0.2037  0.3119 -0.1814
+  702SOL    HW1 2105   2.686   0.930   2.318 -1.6172 -2.2173 -0.8902
+  702SOL    HW2 2106   2.740   0.789   2.329  2.4523 -0.7088 -1.5149
+  703SOL     OW 2107   1.779   2.725   1.561 -0.0561  0.0007 -0.5555
+  703SOL    HW1 2108   1.823   2.643   1.584  2.2162  1.4201  0.1813
+  703SOL    HW2 2109   1.751   2.761   1.645 -3.7615 -1.5313 -1.1282
+  704SOL     OW 2110   0.864   1.960   1.894  0.3045  0.1569 -0.4512
+  704SOL    HW1 2111   0.843   2.048   1.926  0.0460  0.3294 -1.0914
+  704SOL    HW2 2112   0.901   1.915   1.970 -3.6868 -0.6036  1.0328
+  705SOL     OW 2113   2.329   0.266   2.204  1.0041  0.3576 -0.5415
+  705SOL    HW1 2114   2.389   0.327   2.161 -0.0075  2.1428  0.5407
+  705SOL    HW2 2115   2.326   0.296   2.295 -2.6880  1.2282 -0.9070
+  706SOL     OW 2116   0.934   1.400   2.488 -0.3450 -0.2431  0.2377
+  706SOL    HW1 2117   1.008   1.450   2.454 -0.0265 -0.1805  1.0054
+  706SOL    HW2 2118   0.858   1.458   2.478  0.2663  0.9193  2.4140
+  707SOL     OW 2119   1.069   0.836   1.307  0.8456  0.1201 -0.7123
+  707SOL    HW1 2120   1.084   0.749   1.270 -0.2666 -0.0608 -0.7337
+  707SOL    HW2 2121   0.994   0.870   1.258  0.8875  0.8373 -0.2714
+  708SOL     OW 2122   2.668   1.422   2.107 -0.4945  0.1145  0.1710
+  708SOL    HW1 2123   2.657   1.331   2.134 -0.8720 -0.0114 -0.4103
+  708SOL    HW2 2124   2.744   1.453   2.156  1.5704 -1.4459 -2.0906
+  709SOL     OW 2125   0.857   1.657   1.830  0.3111 -0.5695 -0.2760
+  709SOL    HW1 2126   0.871   1.697   1.744 -0.4484  1.0242  0.3313
+  709SOL    HW2 2127   0.916   1.582   1.832  1.1698  0.0693 -1.7413
+  710SOL     OW 2128   2.843   0.822   1.161  0.1093  0.0784 -0.1416
+  710SOL    HW1 2129   2.857   0.861   1.075  0.1960 -0.1173 -0.2164
+  710SOL    HW2 2130   2.762   0.863   1.193  0.6517  0.9248  0.1356
+  711SOL     OW 2131   1.681   2.099   0.397 -0.1399 -0.8621 -0.2690
+  711SOL    HW1 2132   1.654   2.187   0.421  0.4872 -0.8841  0.5197
+  711SOL    HW2 2133   1.760   2.112   0.344 -0.5467 -0.9050 -0.8907
+  712SOL     OW 2134   2.071   1.953   2.112 -0.0542 -0.2297  0.1765
+  712SOL    HW1 2135   2.006   1.890   2.144  0.5835 -1.8285 -1.6369
+  712SOL    HW2 2136   2.093   1.921   2.024  0.5964  2.2890 -0.5784
+  713SOL     OW 2137   1.048   1.968   2.921  0.1211  0.6320  0.1989
+  713SOL    HW1 2138   0.967   1.917   2.931 -0.3903  1.6658  1.2708
+  713SOL    HW2 2139   1.054   1.984   2.826  0.0951 -1.1158 -0.1042
+  714SOL     OW 2140   1.367   1.253   1.219  0.1909 -0.1107 -0.0830
+  714SOL    HW1 2141   1.327   1.167   1.206  2.8124 -1.3631  0.1678
+  714SOL    HW2 2142   1.302   1.315   1.187 -1.7624 -2.1832 -0.1264
+  715SOL     OW 2143   0.162   1.345   1.170 -0.1686  0.2162  0.1060
+  715SOL    HW1 2144   0.174   1.430   1.213 -3.3089  0.4803  0.4871
+  715SOL    HW2 2145   0.129   1.366   1.083  1.8651 -0.4916 -0.8196
+  716SOL     OW 2146   1.683   0.067   1.469 -0.0762 -0.5805 -0.0410
+  716SOL    HW1 2147   1.743  -0.002   1.498 -0.6882 -1.8402 -1.7691
+  716SOL    HW2 2148   1.679   0.058   1.374 -0.6153  1.4723 -0.2245
+  717SOL     OW 2149   1.957   0.462   0.667 -0.1477  0.7255  0.0014
+  717SOL    HW1 2150   1.950   0.447   0.761  1.2760  1.3957  0.2138
+  717SOL    HW2 2151   2.034   0.412   0.640 -0.2848  0.9041 -0.7244
+  718SOL     OW 2152   0.144   1.570   2.042  0.9618 -0.2144 -0.6158
+  718SOL    HW1 2153   0.092   1.501   2.001  1.3853 -1.0578  0.2695
+  718SOL    HW2 2154   0.084   1.612   2.104  0.6876  0.2563 -1.1955
+  719SOL     OW 2155   2.958   1.483   1.867  0.4095 -0.2717  0.5718
+  719SOL    HW1 2156   2.913   1.466   1.950  0.4726 -0.8137  0.4950
+  719SOL    HW2 2157   2.928   1.415   1.808 -0.0048  0.4048 -0.0011
+  720SOL     OW 2158   1.996   0.720   2.004  0.3492 -0.1826 -0.4031
+  720SOL    HW1 2159   1.943   0.772   2.065 -1.8845 -1.8402 -0.9111
+  720SOL    HW2 2160   2.075   0.698   2.053 -0.0692  1.1038  0.8535
+  721SOL     OW 2161   0.790   1.870   1.649  0.2637  0.5284  0.3192
+  721SOL    HW1 2162   0.875   1.891   1.611  0.4944  0.6142  0.8690
+  721SOL    HW2 2163   0.801   1.890   1.742 -0.1886  0.0168  0.4799
+  722SOL     OW 2164   1.316   2.416   0.027 -0.9229  0.3417 -0.0237
+  722SOL    HW1 2165   1.361   2.500   0.014 -0.2183  0.2935  2.0426
+  722SOL    HW2 2166   1.335   2.393   0.119 -2.3819 -1.3169 -0.1447
+  723SOL     OW 2167   1.000   0.768   2.554 -0.0034  0.7184  0.3038
+  723SOL    HW1 2168   0.977   0.810   2.637  0.3913  0.8129  0.3627
+  723SOL    HW2 2169   1.020   0.840   2.495  0.5161  0.6166  0.3587
+  724SOL     OW 2170   0.756   1.366   0.218  0.3743 -0.0093 -0.0790
+  724SOL    HW1 2171   0.842   1.345   0.253  1.0939  0.9138 -1.2956
+  724SOL    HW2 2172   0.740   1.298   0.152  0.4317  0.1677 -0.2761
+  725SOL     OW 2173   2.697   0.778   0.034  0.1233  0.0326  0.0663
+  725SOL    HW1 2174   2.669   0.773   0.125 -0.0225 -2.3422 -0.1275
+  725SOL    HW2 2175   2.786   0.741   0.035  0.3544  0.5731 -0.6511
+  726SOL     OW 2176   2.656   1.927   2.146  0.1853  0.1005  0.5585
+  726SOL    HW1 2177   2.622   1.891   2.228 -0.4526 -1.2013 -0.2796
+  726SOL    HW2 2178   2.717   1.860   2.114 -0.5285  0.0448 -0.6792
+  727SOL     OW 2179   0.077   0.728   2.962  0.0622 -0.3846 -0.3225
+  727SOL    HW1 2180   0.043   0.720   3.051 -0.9842  0.7678 -0.6098
+  727SOL    HW2 2181   0.161   0.774   2.972 -0.1832 -0.0148  0.0425
+  728SOL     OW 2182   2.205   0.213   0.407 -0.5281 -0.6413 -0.0452
+  728SOL    HW1 2183   2.142   0.248   0.344 -0.1348 -0.6024 -0.4208
+  728SOL    HW2 2184   2.151   0.178   0.478 -0.9204  0.6446  0.2949
+  729SOL     OW 2185   0.222   1.507  -0.002 -0.5360  0.7903  0.1461
+  729SOL    HW1 2186   0.243   1.541   0.085 -0.8018 -0.7421  0.8080
+  729SOL    HW2 2187   0.282   1.554  -0.060  1.6393 -0.3095  1.5096
+  730SOL     OW 2188   2.952   0.549   2.772 -0.1005  0.2320  0.6554
+  730SOL    HW1 2189   3.008   0.475   2.747  0.4345  1.1850 -0.9395
+  730SOL    HW2 2190   3.009   0.604   2.827  0.2125  0.6628 -0.0993
+  731SOL     OW 2191   0.712   2.996   1.319  0.1951 -0.3029  0.2794
+  731SOL    HW1 2192   0.659   2.920   1.299 -0.6948  0.2710  0.4610
+  731SOL    HW2 2193   0.716   3.045   1.236 -1.6411  0.9351  0.9211
+  732SOL     OW 2194   0.187   0.337   0.413 -0.2867  0.1668  0.4412
+  732SOL    HW1 2195   0.221   0.247   0.418  0.4211  0.3793 -0.5080
+  732SOL    HW2 2196   0.095   0.327   0.391 -0.0321 -0.3026 -0.4025
+  733SOL     OW 2197   2.691   1.154   2.192  0.3085 -0.8662  0.9354
+  733SOL    HW1 2198   2.725   1.073   2.232 -1.0031 -1.8930 -0.0478
+  733SOL    HW2 2199   2.643   1.124   2.115 -1.4630  0.7520  1.4230
+  734SOL     OW 2200   1.478   0.809   2.244 -0.4590  0.5842 -0.2649
+  734SOL    HW1 2201   1.450   0.721   2.270  0.4146 -0.4463 -2.7843
+  734SOL    HW2 2202   1.506   0.799   2.153 -0.8093  3.5737 -0.6977
+  735SOL     OW 2203   2.127   1.853   0.334  0.6945  0.1285 -0.0968
+  735SOL    HW1 2204   2.186   1.919   0.297 -0.2357  2.1904  2.1269
+  735SOL    HW2 2205   2.044   1.867   0.288  0.4590  0.5733  0.4718
+  736SOL     OW 2206   0.546   2.477   0.298 -0.1726  0.2864 -1.2450
+  736SOL    HW1 2207   0.596   2.559   0.301 -1.5212  1.0124  1.2763
+  736SOL    HW2 2208   0.602   2.413   0.341  0.8954 -0.1417 -3.2426
+  737SOL     OW 2209   0.648   2.645   2.370 -0.7149 -0.2547 -0.7980
+  737SOL    HW1 2210   0.668   2.719   2.313 -1.6410 -0.9359 -2.0170
+  737SOL    HW2 2211   0.570   2.672   2.419  0.9411  0.9001  1.1910
+  738SOL     OW 2212   0.972   0.998   0.745 -0.3018  0.2455  0.1467
+  738SOL    HW1 2213   1.009   1.051   0.674 -1.2264  1.8137  0.8306
+  738SOL    HW2 2214   0.915   0.936   0.700 -1.8401  2.0412 -0.3849
+  739SOL     OW 2215   0.753   0.706   2.277 -0.3153  0.0443  0.0217
+  739SOL    HW1 2216   0.833   0.697   2.225  1.8299  1.7382  3.0115
+  739SOL    HW2 2217   0.750   0.627   2.330 -0.8447 -0.2245 -0.4131
+  740SOL     OW 2218   2.482   1.993   0.781  0.4580  0.2269  0.0192
+  740SOL    HW1 2219   2.443   2.071   0.742 -0.6648 -1.0276 -1.3686
+  740SOL    HW2 2220   2.531   2.026   0.857 -1.8488  2.0256  0.7374
+  741SOL     OW 2221   2.111   0.373   0.181  0.7206  0.0887  0.2938
+  741SOL    HW1 2222   2.159   0.348   0.102 -0.1304 -1.1258  0.1580
+  741SOL    HW2 2223   2.054   0.444   0.152 -0.2564 -0.9771 -0.3925
+  742SOL     OW 2224   2.912   1.308   1.635  0.1948  0.1126 -0.0703
+  742SOL    HW1 2225   2.966   1.284   1.560 -0.1241 -1.2772  0.1351
+  742SOL    HW2 2226   2.824   1.316   1.600  0.0008 -0.7977  0.1975
+  743SOL     OW 2227   0.294   0.671   2.772  0.2232 -0.0746  0.0687
+  743SOL    HW1 2228   0.290   0.699   2.864  1.1685  2.4573 -0.6704
+  743SOL    HW2 2229   0.346   0.591   2.775 -0.6649 -0.6038  1.8944
+  744SOL     OW 2230   2.756   2.428   2.077  0.3686 -0.0752 -0.2737
+  744SOL    HW1 2231   2.783   2.464   1.993  1.4738  0.5979  0.3839
+  744SOL    HW2 2232   2.760   2.501   2.138 -0.9383 -0.4451  0.2575
+  745SOL     OW 2233   2.528   2.946   1.878 -0.6892 -0.1400  0.3019
+  745SOL    HW1 2234   2.525   2.902   1.793  2.2033  1.4079 -0.5894
+  745SOL    HW2 2235   2.461   3.014   1.872 -1.4756 -0.9650 -0.2269
+  746SOL     OW 2236   0.688   0.879   1.142  0.5634 -0.3773 -0.2581
+  746SOL    HW1 2237   0.744   0.817   1.189  1.1888 -0.3643 -0.9854
+  746SOL    HW2 2238   0.612   0.890   1.199  0.3908 -1.9224 -0.1826
+  747SOL     OW 2239   1.628   2.847   2.780  0.0184  0.1460 -0.4381
+  747SOL    HW1 2240   1.638   2.904   2.704  0.4094  0.2489 -0.3151
+  747SOL    HW2 2241   1.604   2.762   2.744 -0.0948  0.2716 -0.6565
+  748SOL     OW 2242   2.315   0.783   1.891 -0.0041 -0.0853  0.2880
+  748SOL    HW1 2243   2.344   0.709   1.837 -0.5815  0.3253 -0.5839
+  748SOL    HW2 2244   2.322   0.859   1.833 -0.7383  0.3395  0.7653
+  749SOL     OW 2245   1.972   2.434   0.601 -0.4814  0.2019 -0.1490
+  749SOL    HW1 2246   2.021   2.506   0.639  0.4108 -1.1895  1.3388
+  749SOL    HW2 2247   2.038   2.381   0.556 -0.7881 -0.9325  0.7170
+  750SOL     OW 2248   2.127   1.348   2.184 -0.0475 -0.1476 -0.0555
+  750SOL    HW1 2249   2.082   1.377   2.263  0.9141 -1.8990  1.1645
+  750SOL    HW2 2250   2.187   1.280   2.215  1.9049  0.6983 -1.9662
+  751SOL     OW 2251   0.687   2.978   1.749  0.7655 -0.2299  0.1904
+  751SOL    HW1 2252   0.596   2.952   1.740  1.1628 -1.2995 -0.7572
+  751SOL    HW2 2253   0.695   3.058   1.697  1.2487 -2.1787 -2.7268
+  752SOL     OW 2254   1.383   2.009   2.763  0.3011  0.0095 -0.1389
+  752SOL    HW1 2255   1.306   2.051   2.724 -1.6163 -1.7372  1.7920
+  752SOL    HW2 2256   1.408   2.067   2.835 -1.7493 -1.6433  1.9008
+  753SOL     OW 2257   1.988   0.018   2.316 -0.1361  0.3166  0.3214
+  753SOL    HW1 2258   1.918   0.071   2.355 -1.2608  0.6723 -2.1657
+  753SOL    HW2 2259   1.962   0.008   2.224  1.6734 -1.8999  0.0492
+  754SOL     OW 2260   1.416   0.641   0.336  0.3061 -0.0063  0.3303
+  754SOL    HW1 2261   1.455   0.721   0.369  0.2805 -0.2772  1.0113
+  754SOL    HW2 2262   1.449   0.633   0.246 -0.1247  1.0508  0.0778
+  755SOL     OW 2263   0.443   1.090   2.996  0.1701  0.0313  0.3039
+  755SOL    HW1 2264   0.519   1.120   2.945  0.4928 -0.4700  0.4969
+  755SOL    HW2 2265   0.368   1.132   2.953  0.3997 -0.7447 -0.8576
+  756SOL     OW 2266   2.490   1.100   0.170 -0.1537  0.0050 -0.2398
+  756SOL    HW1 2267   2.438   1.179   0.188  0.6698  0.5648 -0.3196
+  756SOL    HW2 2268   2.433   1.047   0.115 -0.3199  1.0163 -1.0510
+  757SOL     OW 2269   2.706   0.939   1.681  0.1887 -0.1443 -0.5805
+  757SOL    HW1 2270   2.728   0.988   1.760 -0.5892  0.2992 -0.6347
+  757SOL    HW2 2271   2.773   0.871   1.675  0.0549 -0.3738  0.6318
+  758SOL     OW 2272   0.942   1.665   2.690 -0.4980  0.2713  0.5066
+  758SOL    HW1 2273   1.035   1.642   2.703 -0.4550  0.1871  0.0387
+  758SOL    HW2 2274   0.928   1.654   2.596 -1.3522 -1.0791  0.8048
+  759SOL     OW 2275   2.590   0.054   0.910  0.0864 -0.0195  0.1865
+  759SOL    HW1 2276   2.668   0.027   0.862 -1.3206 -1.3682 -1.3353
+  759SOL    HW2 2277   2.560   0.133   0.865 -1.2459 -1.2966 -1.1537
+  760SOL     OW 2278   1.355   1.591   0.449 -0.0603 -0.4742 -0.6878
+  760SOL    HW1 2279   1.311   1.674   0.469 -1.3057 -1.3364  0.1537
+  760SOL    HW2 2280   1.375   1.554   0.534  0.4457 -1.0716 -1.0702
+  761SOL     OW 2281   1.993   2.064   0.667  0.0032  0.1952  0.0877
+  761SOL    HW1 2282   2.081   2.089   0.697  1.2322 -0.6753 -2.7878
+  761SOL    HW2 2283   2.005   1.978   0.627 -2.5767 -0.9158  1.6829
+  762SOL     OW 2284   2.605   0.345   0.580  0.2047  0.6433  0.3158
+  762SOL    HW1 2285   2.696   0.340   0.609  0.1796 -2.9018 -0.1502
+  762SOL    HW2 2286   2.554   0.342   0.661  0.4002 -0.9978  0.3893
+  763SOL     OW 2287   2.872   0.018   2.223  0.0497  0.2334 -0.3795
+  763SOL    HW1 2288   2.911  -0.046   2.283 -1.7357 -0.2008  0.3548
+  763SOL    HW2 2289   2.944   0.074   2.197  1.1217 -0.4503  1.0898
+  764SOL     OW 2290   2.711   3.019   0.027 -0.2838  0.3062  0.9771
+  764SOL    HW1 2291   2.722   2.924   0.025  1.5441  0.5360  0.4546
+  764SOL    HW2 2292   2.628   3.034  -0.018 -0.2380 -0.9937  0.4543
+  765SOL     OW 2293   1.923   2.580   2.576  0.3437 -0.2057 -0.1374
+  765SOL    HW1 2294   1.938   2.575   2.482  4.1236  2.8387  0.3100
+  765SOL    HW2 2295   1.995   2.529   2.615  0.4500  0.9676  1.2146
+  766SOL     OW 2296   0.202   0.949   1.524 -0.6150  0.1700 -0.1834
+  766SOL    HW1 2297   0.248   0.996   1.593 -3.6357  1.1320  1.1647
+  766SOL    HW2 2298   0.253   0.967   1.445  1.9380  0.3358  1.4935
+  767SOL     OW 2299   2.711   1.748   1.169 -0.0605  0.0842 -0.3946
+  767SOL    HW1 2300   2.623   1.737   1.131  1.0075 -2.2806 -2.1601
+  767SOL    HW2 2301   2.754   1.810   1.111 -0.4021  0.8802  0.2034
+  768SOL     OW 2302   2.678   1.935   0.519 -1.1988 -0.7849  0.2783
+  768SOL    HW1 2303   2.598   1.988   0.519 -0.7319 -0.0631  0.6998
+  768SOL    HW2 2304   2.709   1.939   0.428 -1.1076 -0.2289  0.3285
+  769SOL     OW 2305   1.898   0.949   1.032  0.4490 -0.2189 -0.0350
+  769SOL    HW1 2306   1.881   0.891   0.957  1.4883 -0.9224  0.2794
+  769SOL    HW2 2307   1.828   0.928   1.094  1.3994 -1.8762  0.5071
+  770SOL     OW 2308   2.492   1.850   2.345  0.1958 -0.3868 -0.3476
+  770SOL    HW1 2309   2.438   1.783   2.303  0.2820  0.6510 -2.1573
+  770SOL    HW2 2310   2.430   1.906   2.390  0.0992 -1.5435  0.9541
+  771SOL     OW 2311   0.483   1.286   0.714 -0.2988  0.1129 -0.1765
+  771SOL    HW1 2312   0.423   1.351   0.751 -3.0193 -2.7493  0.4373
+  771SOL    HW2 2313   0.562   1.294   0.768 -1.7069  0.8611  1.8061
+  772SOL     OW 2314   0.564   1.830  -0.011  0.1477 -0.4982  0.0289
+  772SOL    HW1 2315   0.538   1.922  -0.017  1.8787  0.0387  0.8320
+  772SOL    HW2 2316   0.510   1.794   0.059  2.9469  1.0884  2.9976
+  773SOL     OW 2317   2.061   2.150   1.526  0.0159  0.1345  0.5862
+  773SOL    HW1 2318   2.014   2.187   1.451 -1.1635  0.7511  1.6211
+  773SOL    HW2 2319   2.138   2.205   1.536  0.6326 -0.5059 -0.7469
+  774SOL     OW 2320   0.216   1.840   1.471  0.7505  0.5266  0.2560
+  774SOL    HW1 2321   0.173   1.912   1.517 -1.1984 -0.8496  0.5532
+  774SOL    HW2 2322   0.295   1.821   1.523  0.6417  1.6429  0.8220
+  775SOL     OW 2323   1.618   0.579   0.088 -0.1556  0.1733 -0.4656
+  775SOL    HW1 2324   1.660   0.576   0.002 -0.0606 -0.2995 -0.4025
+  775SOL    HW2 2325   1.666   0.515   0.140 -0.8209 -0.2118 -0.3312
+  776SOL     OW 2326   0.281   1.068   2.648  0.5013 -1.0470  0.0739
+  776SOL    HW1 2327   0.364   1.023   2.665  0.2773 -1.0468  1.1535
+  776SOL    HW2 2328   0.215   0.999   2.658  0.2437 -0.7019  0.7584
+  777SOL     OW 2329   0.393   0.045   1.130 -0.0863 -0.2505 -0.1959
+  777SOL    HW1 2330   0.407   0.105   1.057  0.5560  0.6116  0.6325
+  777SOL    HW2 2331   0.466  -0.017   1.124  0.0482 -0.0915 -0.1882
+  778SOL     OW 2332   2.855   1.858   2.715 -0.0940  0.1392 -0.1848
+  778SOL    HW1 2333   2.828   1.796   2.782 -0.9837 -0.5097 -1.1371
+  778SOL    HW2 2334   2.864   1.806   2.635  1.0590  0.8845 -0.5427
+  779SOL     OW 2335   1.360   1.521   0.177 -0.1792  0.0303  0.2802
+  779SOL    HW1 2336   1.368   1.557   0.266 -0.0042  2.2237 -0.6206
+  779SOL    HW2 2337   1.308   1.587   0.130  0.5697 -0.6787 -1.5169
+  780SOL     OW 2338   0.146   2.622   0.861 -0.1905  0.3625 -0.3948
+  780SOL    HW1 2339   0.052   2.615   0.877 -0.1583 -1.7036 -1.1237
+  780SOL    HW2 2340   0.166   2.715   0.875 -1.5199 -0.0222  4.0753
+  781SOL     OW 2341   2.290   0.128   1.065 -0.3605 -0.3548  0.2358
+  781SOL    HW1 2342   2.305   0.067   0.993 -0.1319  0.1495 -0.1432
+  781SOL    HW2 2343   2.356   0.196   1.053 -1.3417  0.5617  0.0099
+  782SOL     OW 2344   2.463   0.427   2.744  0.8792 -0.1400  0.6540
+  782SOL    HW1 2345   2.517   0.488   2.795  0.0742 -0.0788  1.4423
+  782SOL    HW2 2346   2.378   0.472   2.736  0.2058 -1.2744  1.4555
+  783SOL     OW 2347   0.271   2.432   1.752  0.1431  1.1773  0.4598
+  783SOL    HW1 2348   0.282   2.396   1.664 -0.6426  0.7875  0.5228
+  783SOL    HW2 2349   0.358   2.466   1.775  0.8406 -0.4665  0.3077
+  784SOL     OW 2350   2.011   0.130   1.046  0.1584  0.0301 -0.6898
+  784SOL    HW1 2351   2.102   0.162   1.051  0.1849 -0.0413 -0.7301
+  784SOL    HW2 2352   1.958   0.209   1.061  0.1834  0.6742 -4.1244
+  785SOL     OW 2353   1.678   1.217   0.971 -0.2082 -0.2466  0.2904
+  785SOL    HW1 2354   1.678   1.130   0.930  0.0959 -0.6031  1.0537
+  785SOL    HW2 2355   1.722   1.204   1.055 -0.8243  0.5010  0.7231
+  786SOL     OW 2356   2.903   0.406   1.129 -0.0124 -0.5226 -0.3323
+  786SOL    HW1 2357   2.972   0.340   1.120  0.0621 -0.7006  1.4493
+  786SOL    HW2 2358   2.824   0.355   1.148 -0.6380 -0.3203 -2.3590
+  787SOL     OW 2359   0.894   1.562   0.875  0.3173  0.1802  0.3522
+  787SOL    HW1 2360   0.875   1.653   0.896 -0.5307 -0.1980  1.2047
+  787SOL    HW2 2361   0.967   1.539   0.932  0.6468  0.3756  0.0119
+  788SOL     OW 2362   2.507   0.794   2.793 -0.1251 -0.5708  0.0632
+  788SOL    HW1 2363   2.577   0.749   2.840  0.3311 -0.6762 -0.7147
+  788SOL    HW2 2364   2.550   0.872   2.756 -0.4482 -0.5650 -0.2943
+  789SOL     OW 2365   0.189   0.213   1.258  0.2960  0.2630 -0.6201
+  789SOL    HW1 2366   0.205   0.212   1.352  1.4338  1.7673 -0.7982
+  789SOL    HW2 2367   0.272   0.185   1.219 -0.4251 -1.1905 -1.1508
+  790SOL     OW 2368   0.668   2.756   0.408  0.2462 -0.4600  0.3462
+  790SOL    HW1 2369   0.747   2.810   0.402  0.1419 -0.1827  1.5264
+  790SOL    HW2 2370   0.605   2.800   0.351  0.4729  0.1245  0.5447
+  791SOL     OW 2371   2.808   2.647   0.843 -0.3780 -0.0449 -0.1896
+  791SOL    HW1 2372   2.803   2.557   0.811 -2.9954 -0.3044  0.9688
+  791SOL    HW2 2373   2.815   2.700   0.763  0.6406 -1.2237 -0.8889
+  792SOL     OW 2374   1.411   2.693   2.167 -0.1042  0.1447  0.6070
+  792SOL    HW1 2375   1.427   2.620   2.227 -0.9065 -0.1155  0.5016
+  792SOL    HW2 2376   1.478   2.684   2.100  0.3904  0.0560  1.1139
+  793SOL     OW 2377   2.710   2.376   0.796 -0.0998 -0.1745  0.3876
+  793SOL    HW1 2378   2.646   2.365   0.866 -1.1644  1.8898 -0.2562
+  793SOL    HW2 2379   2.701   2.297   0.742 -0.4394 -0.9842  1.6361
+  794SOL     OW 2380   1.027   0.489   0.295 -0.2153 -0.0011 -0.0715
+  794SOL    HW1 2381   1.079   0.527   0.365  1.4503 -0.4934 -1.0343
+  794SOL    HW2 2382   1.013   0.398   0.322 -0.9351  0.2481  0.3916
+  795SOL     OW 2383   2.256   0.117   3.002 -0.1344  0.0357 -0.0757
+  795SOL    HW1 2384   2.176   0.072   3.028 -1.4767  2.1202 -0.5563
+  795SOL    HW2 2385   2.225   0.193   2.953  2.0563  3.0938  3.3194
+  796SOL     OW 2386   1.671   1.882   2.915  0.0162 -0.2469 -0.3989
+  796SOL    HW1 2387   1.643   1.939   2.987 -0.5973  0.8396 -1.4752
+  796SOL    HW2 2388   1.643   1.929   2.836  1.2751 -1.1072 -1.3588
+  797SOL     OW 2389   2.424   0.316   1.405 -0.0519 -0.3966  0.4738
+  797SOL    HW1 2390   2.498   0.373   1.382 -1.3003  0.8754 -0.3322
+  797SOL    HW2 2391   2.459   0.259   1.474  1.9020 -0.2630 -0.4029
+  798SOL     OW 2392   2.376   2.509   0.704  0.2919 -0.5694  0.0571
+  798SOL    HW1 2393   2.430   2.531   0.628 -0.8151  0.5320 -0.4217
+  798SOL    HW2 2394   2.428   2.538   0.779  0.1632  0.9171 -0.4242
+  799SOL     OW 2395   0.899   1.383   2.089  0.2270  0.0882 -0.2680
+  799SOL    HW1 2396   0.829   1.448   2.098  0.2852  0.2523 -0.9962
+  799SOL    HW2 2397   0.904   1.341   2.175 -0.2874  0.5632 -0.0052
+  800SOL     OW 2398   1.969   2.176   1.279 -0.0230 -0.0765  0.7334
+  800SOL    HW1 2399   1.927   2.108   1.226 -0.5801  0.2900  0.7049
+  800SOL    HW2 2400   1.913   2.252   1.268 -0.3769 -0.1377  2.2250
+  801SOL     OW 2401   0.428   1.606   2.866  0.0223  0.4375 -0.1302
+  801SOL    HW1 2402   0.480   1.685   2.876 -2.8105  2.2502  0.1243
+  801SOL    HW2 2403   0.465   1.544   2.929  1.6402  1.8329  0.2713
+  802SOL     OW 2404   0.582   0.846   1.828  0.2385  0.0343 -0.3074
+  802SOL    HW1 2405   0.667   0.803   1.821  1.5574  2.4834  0.6219
+  802SOL    HW2 2406   0.554   0.828   1.918 -0.6337 -1.5424 -0.8985
+  803SOL     OW 2407   1.300   1.264   0.355 -0.1297 -0.0354 -0.3534
+  803SOL    HW1 2408   1.364   1.322   0.313 -0.5588  0.1692 -0.7221
+  803SOL    HW2 2409   1.329   1.258   0.446 -1.1654  2.2728  0.1226
+  804SOL     OW 2410   0.143   0.784   2.594  0.0219  0.0356  0.3530
+  804SOL    HW1 2411   0.202   0.769   2.520 -0.9494 -1.7748 -0.0546
+  804SOL    HW2 2412   0.176   0.727   2.664 -2.0480 -1.9940 -0.3192
+  805SOL     OW 2413   0.922   2.236   0.482 -0.1951 -0.0090 -0.1256
+  805SOL    HW1 2414   0.986   2.227   0.411  0.4399  0.8336  0.3354
+  805SOL    HW2 2415   0.915   2.149   0.519  0.0327 -0.2915 -0.7437
+  806SOL     OW 2416   1.850   0.001   2.059 -0.1833 -0.0537 -0.2502
+  806SOL    HW1 2417   1.870   0.082   2.012 -0.2054 -0.0041 -0.1755
+  806SOL    HW2 2418   1.842  -0.065   1.991 -0.4966  0.0736 -0.3393
+  807SOL     OW 2419   2.608   2.848   2.246  0.1633 -0.2513 -0.2262
+  807SOL    HW1 2420   2.549   2.859   2.320  1.2823 -0.5605  0.7157
+  807SOL    HW2 2421   2.683   2.903   2.267 -0.4899  1.3826 -2.1619
+  808SOL     OW 2422   0.803   1.063   0.963 -0.0685 -0.3137 -0.0398
+  808SOL    HW1 2423   0.831   1.040   0.874 -0.8457 -2.3704  0.2365
+  808SOL    HW2 2424   0.771   0.981   1.000 -2.4493  1.1476  1.1366
+  809SOL     OW 2425   1.249   2.119   0.644  0.4708 -0.3862 -0.2126
+  809SOL    HW1 2426   1.154   2.116   0.652  0.2505  1.9162 -2.0747
+  809SOL    HW2 2427   1.281   2.110   0.734 -1.4382 -3.9263  0.1185
+  810SOL     OW 2428   1.876   1.491   2.699 -0.2981 -0.2776 -0.1114
+  810SOL    HW1 2429   1.796   1.477   2.649  0.7751  0.2181 -1.9560
+  810SOL    HW2 2430   1.848   1.540   2.777 -2.0095 -1.3901 -0.0406
+  811SOL     OW 2431   0.467   0.716   0.268 -0.2808 -0.1123 -0.4498
+  811SOL    HW1 2432   0.484   0.653   0.338 -0.1296  0.5256  0.0889
+  811SOL    HW2 2433   0.483   0.801   0.308 -0.7068  0.2808 -1.1174
+  812SOL     OW 2434   0.348   0.688   0.811 -0.3598 -0.0464 -0.3956
+  812SOL    HW1 2435   0.311   0.637   0.883  0.0431  1.0321  0.5739
+  812SOL    HW2 2436   0.275   0.740   0.779 -0.6958 -1.0068 -1.1832
+  813SOL     OW 2437   1.952   0.305   2.353  0.3651  0.3642  0.1979
+  813SOL    HW1 2438   1.990   0.393   2.346  0.1113  0.5652  1.3399
+  813SOL    HW2 2439   1.992   0.256   2.281  0.5820  1.3879 -0.3878
+  814SOL     OW 2440   1.504   1.608   1.033  0.3203 -0.2906 -0.2639
+  814SOL    HW1 2441   1.574   1.593   1.098 -0.6586  1.7874  1.2491
+  814SOL    HW2 2442   1.544   1.667   0.969  0.1134  0.5147  0.3466
+  815SOL     OW 2443   2.059   0.145   1.568 -0.3515  1.2167  0.1232
+  815SOL    HW1 2444   2.109   0.083   1.623 -0.7880  0.4281 -0.3637
+  815SOL    HW2 2445   2.112   0.153   1.489 -0.0557  1.8380  0.3856
+  816SOL     OW 2446   0.377   2.420   1.121 -0.6048  0.1325  0.2008
+  816SOL    HW1 2447   0.403   2.497   1.070 -3.1104 -1.4767 -3.5081
+  816SOL    HW2 2448   0.286   2.437   1.146 -0.3087 -0.4176  1.6636
+  817SOL     OW 2449   0.155   1.781   1.215 -0.3857 -0.0652 -0.7582
+  817SOL    HW1 2450   0.159   1.802   1.308  1.9043 -1.5834 -0.5244
+  817SOL    HW2 2451   0.227   1.829   1.176 -0.1571 -0.9834 -1.4639
+  818SOL     OW 2452   0.027   0.200   2.107  0.0502  0.2466  0.0548
+  818SOL    HW1 2453   0.104   0.249   2.136  0.0381  0.3274 -0.0498
+  818SOL    HW2 2454  -0.022   0.262   2.052 -0.2575  0.3004  0.3908
+  819SOL     OW 2455   2.073   1.050   0.777 -0.1177  0.3216  0.5425
+  819SOL    HW1 2456   2.015   1.047   0.853 -0.6117 -1.5496  0.0941
+  819SOL    HW2 2457   2.159   1.072   0.812 -0.7827  1.5775  1.3898
+  820SOL     OW 2458   2.841   2.743   0.075 -0.1393 -0.1334 -0.6518
+  820SOL    HW1 2459   2.793   2.677   0.124  0.3563 -0.6790 -0.9051
+  820SOL    HW2 2460   2.881   2.695   0.002 -0.2279  0.5323 -1.1432
+  821SOL     OW 2461   0.940   1.173   1.399 -0.5022  0.2903 -0.4956
+  821SOL    HW1 2462   0.941   1.242   1.465 -0.2791  1.4429 -1.7190
+  821SOL    HW2 2463   0.895   1.100   1.442 -1.1472  1.3505  0.6297
+  822SOL     OW 2464   1.800   1.395   0.798  0.2371 -0.3543 -0.9502
+  822SOL    HW1 2465   1.719   1.441   0.775  0.1781  0.2913  0.4954
+  822SOL    HW2 2466   1.772   1.308   0.824  0.0932 -0.4213 -1.3355
+  823SOL     OW 2467   0.377   0.833   1.309 -0.1575 -0.0844  0.1858
+  823SOL    HW1 2468   0.310   0.866   1.249 -0.7302 -0.8072  0.4335
+  823SOL    HW2 2469   0.438   0.907   1.320 -1.2883  0.7893  0.5233
+  824SOL     OW 2470   1.288   0.788   2.032 -0.1591 -0.2886  0.0598
+  824SOL    HW1 2471   1.233   0.831   2.097 -1.4831  0.1431 -1.3569
+  824SOL    HW2 2472   1.310   0.857   1.969  0.6463 -0.8434 -0.2656
+  825SOL     OW 2473   2.327   1.154   2.214 -0.1821 -0.1916  0.0327
+  825SOL    HW1 2474   2.353   1.160   2.122 -0.6135 -0.4221 -0.1023
+  825SOL    HW2 2475   2.380   1.083   2.249  1.1553  0.9622  0.3587
+  826SOL     OW 2476   1.362   2.306   0.287  0.3806 -0.4267 -0.0926
+  826SOL    HW1 2477   1.453   2.323   0.311  0.2629  0.1047 -0.0236
+  826SOL    HW2 2478   1.350   2.212   0.302  0.2046 -0.0138  2.2329
+  827SOL     OW 2479   2.878   0.445   0.633  0.7016 -0.0221  0.1920
+  827SOL    HW1 2480   2.905   0.385   0.564  1.1916  2.0327 -1.4162
+  827SOL    HW2 2481   2.959   0.469   0.677  0.8444  1.6740 -0.9829
+  828SOL     OW 2482   0.475   0.883   2.712  0.1997 -0.0296 -0.2254
+  828SOL    HW1 2483   0.403   0.823   2.731 -0.5329  0.3376 -1.9152
+  828SOL    HW2 2484   0.540   0.863   2.780 -2.9813  2.0854  3.4471
+  829SOL     OW 2485   1.829   0.639   1.374  0.9488  0.3708 -0.3438
+  829SOL    HW1 2486   1.813   0.609   1.463  0.2478  0.2370 -0.5160
+  829SOL    HW2 2487   1.836   0.558   1.323  1.4690  0.4446 -0.3878
+  830SOL     OW 2488   0.526   0.223   2.394  0.0146 -0.0419 -0.3104
+  830SOL    HW1 2489   0.549   0.196   2.305 -1.0189 -0.0942 -0.5650
+  830SOL    HW2 2490   0.580   0.300   2.411  1.4411 -1.0274 -0.3708
+  831SOL     OW 2491   1.567   1.100   0.279  0.0500 -0.2599 -0.0435
+  831SOL    HW1 2492   1.544   1.191   0.298  0.1342 -0.3508  0.4916
+  831SOL    HW2 2493   1.663   1.100   0.272 -0.0377 -0.0592 -1.3474
+  832SOL     OW 2494   0.526   0.107   0.416  0.3085 -0.6643 -0.3591
+  832SOL    HW1 2495   0.517   0.016   0.387  0.6680 -1.0043  0.5946
+  832SOL    HW2 2496   0.612   0.134   0.384  0.4350 -0.8336 -0.1598
+  833SOL     OW 2497   1.862   0.771   0.826  0.3231  0.2897  0.0258
+  833SOL    HW1 2498   1.801   0.697   0.837 -1.8510  2.0983  0.1263
+  833SOL    HW2 2499   1.941   0.731   0.789 -1.8850 -1.6695 -2.6437
+  834SOL     OW 2500   1.518   2.161   1.180  0.4750 -0.1221 -0.1135
+  834SOL    HW1 2501   1.497   2.201   1.264 -0.7419 -0.1147 -0.4206
+  834SOL    HW2 2502   1.552   2.074   1.203  0.2958 -0.0587  0.3881
+  835SOL     OW 2503   2.097   1.294   2.952  0.0318  0.2650  0.0016
+  835SOL    HW1 2504   2.114   1.225   3.015  1.2950  1.5783  1.0793
+  835SOL    HW2 2505   2.017   1.337   2.983  0.3541  0.5660  0.4085
+  836SOL     OW 2506   3.010   0.018   1.353 -0.0039 -0.3769  0.1513
+  836SOL    HW1 2507   2.934  -0.006   1.299 -0.2390 -1.1337  0.8148
+  836SOL    HW2 2508   3.043   0.099   1.313 -0.5885 -0.5582 -0.7118
+  837SOL     OW 2509   1.189   0.599   0.488  0.5356  0.1615 -0.2536
+  837SOL    HW1 2510   1.254   0.641   0.433  0.3947 -0.0222 -0.5594
+  837SOL    HW2 2511   1.129   0.669   0.514  0.8260  0.3199 -0.0169
+  838SOL     OW 2512   1.676   1.583   2.537  0.2167  0.3417  0.2696
+  838SOL    HW1 2513   1.650   1.560   2.448 -1.0667  2.2947  0.1190
+  838SOL    HW2 2514   1.621   1.659   2.559 -0.7570 -1.2537  3.3879
+  839SOL     OW 2515   2.921   2.593   2.823  0.3981  0.0365  0.1130
+  839SOL    HW1 2516   2.834   2.568   2.792  1.0079 -0.7987 -0.9599
+  839SOL    HW2 2517   2.944   2.526   2.887 -0.6634  1.4938  2.0152
+  840SOL     OW 2518   0.701   0.855   0.082 -0.3890  0.1286  0.3083
+  840SOL    HW1 2519   0.620   0.814   0.113 -0.7772  0.6566 -0.0109
+  840SOL    HW2 2520   0.697   0.945   0.116 -0.3052  0.3870 -0.3624
+  841SOL     OW 2521   0.861   1.197   1.881 -0.5959 -0.1235  0.1033
+  841SOL    HW1 2522   0.864   1.251   1.960  1.9895  1.6736 -1.1910
+  841SOL    HW2 2523   0.804   1.245   1.821  1.3344  0.9611 -0.8795
+  842SOL     OW 2524   2.268   1.570   2.992  0.3417  0.2029 -0.2191
+  842SOL    HW1 2525   2.208   1.615   3.052  1.2650 -0.4054  1.1805
+  842SOL    HW2 2526   2.228   1.485   2.976 -0.2571  0.6417 -1.0152
+  843SOL     OW 2527   1.438   0.927   0.432  0.2761  0.4220  0.2776
+  843SOL    HW1 2528   1.491   0.989   0.381 -0.7320  1.8036  0.8997
+  843SOL    HW2 2529   1.446   0.958   0.523  0.1255 -0.6213  0.6463
+  844SOL     OW 2530   1.661   1.552   0.593 -0.1961 -0.0551  0.3928
+  844SOL    HW1 2531   1.754   1.533   0.584 -0.6221 -0.4102 -3.5380
+  844SOL    HW2 2532   1.655   1.647   0.588 -0.1706 -0.1061 -0.6045
+  845SOL     OW 2533   2.016   2.939   1.818 -0.2224  0.6029 -0.4594
+  845SOL    HW1 2534   1.995   2.846   1.804 -1.0008  0.7328 -0.1139
+  845SOL    HW2 2535   2.099   2.937   1.866 -0.4696  0.0828 -0.0491
+  846SOL     OW 2536   0.944   2.263   1.724  0.2770  0.0476  0.9362
+  846SOL    HW1 2537   0.964   2.192   1.785 -0.6489 -0.5714  0.5191
+  846SOL    HW2 2538   0.893   2.221   1.654  0.4721  0.9516  0.2586
+  847SOL     OW 2539   2.024   1.698   1.214  0.1452  0.2248 -0.0035
+  847SOL    HW1 2540   2.039   1.784   1.255 -0.3708 -0.4733  1.6330
+  847SOL    HW2 2541   2.012   1.638   1.287 -0.8187 -1.0177 -1.1822
+  848SOL     OW 2542   0.704   2.958   2.443  0.0404  0.0204  0.4656
+  848SOL    HW1 2543   0.689   3.052   2.431  2.5420  0.3323 -0.3275
+  848SOL    HW2 2544   0.724   2.926   2.355  1.8596 -0.7644  1.1704
+  849SOL     OW 2545   0.831   1.038   0.459  0.1440  0.5227 -0.0520
+  849SOL    HW1 2546   0.820   1.035   0.364  1.6417 -1.8207 -0.1257
+  849SOL    HW2 2547   0.784   0.961   0.491  1.9347 -0.1721  0.9129
+  850SOL     OW 2548   2.670   2.049   2.631  0.0877  0.0705 -0.6352
+  850SOL    HW1 2549   2.750   2.018   2.674 -0.1162  0.1760 -0.1837
+  850SOL    HW2 2550   2.668   2.002   2.547 -0.7245 -1.8418  0.4666
+  851SOL     OW 2551   2.270   0.009   2.314  0.0545 -0.7719 -0.3071
+  851SOL    HW1 2552   2.279   0.093   2.267 -1.1646 -0.2032  0.4685
+  851SOL    HW2 2553   2.176   0.000   2.328  0.3638 -1.3142  1.3765
+  852SOL     OW 2554   2.544   1.406   2.893 -0.5319 -0.2514 -0.3900
+  852SOL    HW1 2555   2.545   1.494   2.932 -1.4783 -0.2128 -0.4338
+  852SOL    HW2 2556   2.473   1.410   2.829  0.6411 -0.4912 -1.6978
+  853SOL     OW 2557   1.956   1.191   1.545  0.1106 -0.7910 -0.0718
+  853SOL    HW1 2558   1.911   1.270   1.573  1.3379 -0.1700  0.1769
+  853SOL    HW2 2559   1.907   1.119   1.584  0.9486 -0.0012  2.4084
+  854SOL     OW 2560   0.329   1.531   2.492  0.8799 -0.0120  1.0316
+  854SOL    HW1 2561   0.345   1.487   2.409 -0.3078  0.2084  0.6748
+  854SOL    HW2 2562   0.306   1.461   2.552 -0.6774  0.0817  0.5494
+  855SOL     OW 2563   0.374   2.239   2.765 -0.0451  0.2804 -0.1169
+  855SOL    HW1 2564   0.437   2.295   2.719  0.2386  0.2314  0.2134
+  855SOL    HW2 2565   0.305   2.299   2.793 -0.6847  0.1711 -1.4162
+  856SOL     OW 2566   1.652   2.973   2.520  0.1942 -0.1524  0.3550
+  856SOL    HW1 2567   1.666   3.024   2.440  1.3575 -0.7277  0.1993
+  856SOL    HW2 2568   1.685   2.886   2.500  1.1079 -0.1223  1.6838
+  857SOL     OW 2569   2.343   2.919   0.885 -0.0327 -0.1510  0.0433
+  857SOL    HW1 2570   2.338   2.832   0.924 -0.2056  0.2990  1.0492
+  857SOL    HW2 2571   2.435   2.932   0.865  0.2058 -0.0093  1.2046
+  858SOL     OW 2572   0.171   2.497   2.575  0.5357  1.0194 -0.0978
+  858SOL    HW1 2573   0.144   2.485   2.667  0.7883 -0.1834 -0.1783
+  858SOL    HW2 2574   0.175   2.408   2.540  0.4708  1.4854 -1.2725
+  859SOL     OW 2575   1.923   2.538   2.309  0.2239  1.0124  0.2007
+  859SOL    HW1 2576   1.881   2.457   2.338  2.3909 -0.1881 -0.0023
+  859SOL    HW2 2577   1.951   2.518   2.219  1.1946  1.8276  0.3309
+  860SOL     OW 2578   0.789   0.126   0.746 -0.4803 -0.1369 -0.4255
+  860SOL    HW1 2579   0.854   0.196   0.754  0.6452 -1.1769 -0.5717
+  860SOL    HW2 2580   0.705   0.171   0.757  0.2800  1.1036  0.3726
+  861SOL     OW 2581   0.500   2.179   1.766  0.5986  0.9268  0.6184
+  861SOL    HW1 2582   0.418   2.217   1.735  1.2378 -0.4296 -2.6775
+  861SOL    HW2 2583   0.503   2.202   1.859 -2.3193  3.0975  0.1790
+  862SOL     OW 2584   1.777   2.891   0.031 -0.1426 -0.0493  0.2211
+  862SOL    HW1 2585   1.710   2.928   0.089  0.9357  1.4845  0.4788
+  862SOL    HW2 2586   1.737   2.896  -0.056 -0.0044  1.6867  0.2611
+  863SOL     OW 2587   2.686   1.487   0.642  0.1669 -0.0631  0.7226
+  863SOL    HW1 2588   2.744   1.560   0.620 -0.6290 -0.1281 -1.6786
+  863SOL    HW2 2589   2.611   1.528   0.685 -0.1436  0.5193 -0.3615
+  864SOL     OW 2590   1.534  -0.006   0.848  0.5402  0.1391 -0.2917
+  864SOL    HW1 2591   1.527   0.016   0.941  1.3614  1.6439 -0.5887
+  864SOL    HW2 2592   1.525   0.078   0.803 -0.8735 -0.7192 -1.6115
+  865SOL     OW 2593   2.860   1.093   1.890 -0.3535  0.0175 -0.3493
+  865SOL    HW1 2594   2.773   1.132   1.901 -0.0794  0.5500 -0.0939
+  865SOL    HW2 2595   2.861   1.021   1.953 -0.3698  0.4181  0.1106
+  866SOL     OW 2596   2.619   2.725   1.230 -0.6618 -0.0667 -0.5982
+  866SOL    HW1 2597   2.678   2.794   1.198  0.7645 -1.4411 -0.8913
+  866SOL    HW2 2598   2.666   2.643   1.213 -1.0108 -0.9332  2.7407
+  867SOL     OW 2599   2.009   0.162   0.624 -0.1996 -0.2606 -0.1501
+  867SOL    HW1 2600   1.927   0.191   0.663  0.2980 -0.1276  0.8001
+  867SOL    HW2 2601   1.998   0.068   0.611 -0.5805 -0.2077 -0.1978
+  868SOL     OW 2602   0.178   2.800   1.281  0.4564  0.1469 -0.1940
+  868SOL    HW1 2603   0.166   2.753   1.364 -0.3862 -0.9076 -0.8995
+  868SOL    HW2 2604   0.140   2.886   1.296 -1.5781 -0.7560 -0.1383
+  869SOL     OW 2605   2.561   2.524   0.407  0.1714 -0.9408  0.1817
+  869SOL    HW1 2606   2.646   2.526   0.450 -0.0023 -0.3340  0.4878
+  869SOL    HW2 2607   2.573   2.574   0.326 -0.5896  1.3217  1.4659
+  870SOL     OW 2608   2.301   2.679   1.037  0.5326 -0.0810 -0.8300
+  870SOL    HW1 2609   2.381   2.660   0.988  1.1741  0.1256  0.1201
+  870SOL    HW2 2610   2.261   2.593   1.052  0.8519 -0.2313 -0.8493
+  871SOL     OW 2611   2.395   1.839   0.587  0.4417 -0.1595 -0.3632
+  871SOL    HW1 2612   2.422   1.903   0.654  1.5364 -0.8578 -0.1432
+  871SOL    HW2 2613   2.475   1.790   0.567 -0.0486 -0.4022 -1.6931
+  872SOL     OW 2614   0.934   2.524   0.484  0.6627  0.1843  0.2297
+  872SOL    HW1 2615   0.890   2.439   0.494  0.5108  0.2967  0.5154
+  872SOL    HW2 2616   0.965   2.524   0.394 -1.3879  0.8168 -0.4647
+  873SOL     OW 2617   0.854   0.202   2.521 -0.1760 -0.1619 -0.1070
+  873SOL    HW1 2618   0.875   0.182   2.430 -0.1055  0.2965 -0.1920
+  873SOL    HW2 2619   0.912   0.145   2.572  0.6028  0.4163 -0.3454
+  874SOL     OW 2620   2.739   1.574   0.074 -0.3849  0.1968 -0.0119
+  874SOL    HW1 2621   2.721   1.606   0.163 -1.2858  0.9930 -0.4908
+  874SOL    HW2 2622   2.754   1.480   0.086  1.3890  0.6182  1.2616
+  875SOL     OW 2623   2.756   2.168   1.873 -0.0762  0.0624  0.2777
+  875SOL    HW1 2624   2.843   2.172   1.911 -0.2678 -0.1349  0.7377
+  875SOL    HW2 2625   2.736   2.075   1.865  0.4732  0.0938 -1.4836
+  876SOL     OW 2626   2.293   2.386   2.751 -0.0343 -0.0558  0.0267
+  876SOL    HW1 2627   2.252   2.464   2.713 -0.1264  0.3285  0.9121
+  876SOL    HW2 2628   2.227   2.318   2.741  0.3109 -0.1797 -1.3671
+  877SOL     OW 2629   1.498   0.283   0.761 -0.2751 -0.2833 -0.5689
+  877SOL    HW1 2630   1.449   0.324   0.832  1.2157 -1.0357  0.8891
+  877SOL    HW2 2631   1.476   0.335   0.684 -2.9288 -0.2742  0.1865
+  878SOL     OW 2632   2.794   1.164   1.101  0.6008 -0.8436  0.0066
+  878SOL    HW1 2633   2.718   1.215   1.074  2.6523  1.5127 -1.4089
+  878SOL    HW2 2634   2.805   1.184   1.194  0.0603 -0.4447 -0.0169
+  879SOL     OW 2635   2.830   0.876   0.511  0.1087 -0.1881 -0.5208
+  879SOL    HW1 2636   2.812   0.909   0.423  0.9106 -1.5924 -1.2033
+  879SOL    HW2 2637   2.778   0.797   0.519  1.4470 -1.0546 -0.3871
+  880SOL     OW 2638   0.567   0.134   0.007 -0.4811  0.0209 -0.0163
+  880SOL    HW1 2639   0.609   0.082   0.076  1.4581  2.4879  0.6394
+  880SOL    HW2 2640   0.610   0.105  -0.074  1.2391  1.2797  0.4550
+  881SOL     OW 2641   1.795   0.305   1.623  0.1766 -0.3385 -0.5942
+  881SOL    HW1 2642   1.876   0.264   1.591 -0.0833 -1.8089  0.6732
+  881SOL    HW2 2643   1.725   0.254   1.582 -0.1196  0.1920 -0.7427
+  882SOL     OW 2644   1.951   0.804   0.170  0.2338 -0.1090  0.2141
+  882SOL    HW1 2645   1.887   0.783   0.237 -1.8653  2.8955 -0.8579
+  882SOL    HW2 2646   2.025   0.842   0.218 -0.7718  0.9366  0.9072
+  883SOL     OW 2647   0.342   1.232   2.440 -0.0400 -0.1921  0.2571
+  883SOL    HW1 2648   0.313   1.178   2.513  0.7970 -0.0398  0.7142
+  883SOL    HW2 2649   0.430   1.260   2.465 -0.4538  1.3556  0.0257
+  884SOL     OW 2650   0.485   2.787   1.269  0.0529 -0.0891 -0.3627
+  884SOL    HW1 2651   0.394   2.760   1.264  0.0428  0.1257 -1.3332
+  884SOL    HW2 2652   0.513   2.797   1.178  0.4727  1.7524 -0.0522
+  885SOL     OW 2653   2.100   2.737  -0.020 -0.4310  0.1463  0.5587
+  885SOL    HW1 2654   2.076   2.719   0.071 -0.3641 -1.9688  0.1616
+  885SOL    HW2 2655   2.035   2.800  -0.050 -2.8573 -2.2253  0.8449
+  886SOL     OW 2656   1.180   1.594   1.871 -0.2265  0.1634  0.3664
+  886SOL    HW1 2657   1.272   1.607   1.895 -0.4895  0.3300  1.3053
+  886SOL    HW2 2658   1.171   1.500   1.858 -0.4419 -0.0960  2.4215
+  887SOL     OW 2659   0.946   1.736   2.094 -0.3618  0.2745  0.0454
+  887SOL    HW1 2660   0.921   1.661   2.148  2.5657 -0.1309  0.8599
+  887SOL    HW2 2661   0.935   1.705   2.004 -0.3821 -0.7289  0.3853
+  888SOL     OW 2662   0.087   2.056   1.596 -0.5384  0.0637 -0.5828
+  888SOL    HW1 2663   0.060   2.017   1.679  2.0148  0.7363  0.5753
+  888SOL    HW2 2664   0.073   2.150   1.608 -0.2230  0.1526 -0.9097
+  889SOL     OW 2665   2.887   2.073   0.051 -0.2337 -0.0021 -0.8106
+  889SOL    HW1 2666   2.958   2.109   0.103 -1.1726 -1.3085  1.3879
+  889SOL    HW2 2667   2.811   2.126   0.074 -0.4530  0.0062 -1.5342
+  890SOL     OW 2668   1.744   1.582   0.260 -0.2299 -0.1649  0.3411
+  890SOL    HW1 2669   1.767   1.601   0.351 -1.5762 -0.4541  0.7309
+  890SOL    HW2 2670   1.747   1.667   0.216  1.0603 -0.1180  0.5119
+  891SOL     OW 2671   1.884   1.692   1.801  0.2295 -0.1286 -0.1626
+  891SOL    HW1 2672   1.941   1.689   1.877 -0.0546  2.2566  0.1705
+  891SOL    HW2 2673   1.796   1.703   1.837  0.1033  1.0692 -0.8273
+  892SOL     OW 2674   0.526   1.971   0.968  0.0903  0.3947 -0.0047
+  892SOL    HW1 2675   0.530   2.066   0.954  0.5942  0.4966  0.8033
+  892SOL    HW2 2676   0.568   1.958   1.053 -0.5041 -0.4974  0.1487
+  893SOL     OW 2677   1.747   0.592   0.531 -0.3102 -0.2927 -0.3387
+  893SOL    HW1 2678   1.822   0.546   0.569 -0.0556  2.0738  2.0043
+  893SOL    HW2 2679   1.671   0.547   0.569 -0.0697 -0.0700  0.4098
+  894SOL     OW 2680   0.068   0.217   0.992  0.0812 -0.3914 -0.2009
+  894SOL    HW1 2681   0.134   0.246   0.929  1.5946  1.0059  1.9985
+  894SOL    HW2 2682   0.118   0.202   1.073 -2.2984  0.5411  1.4236
+  895SOL     OW 2683   2.291   1.424   0.779 -0.2085  0.1601 -0.1671
+  895SOL    HW1 2684   2.203   1.441   0.813 -0.2751 -0.3553 -0.0792
+  895SOL    HW2 2685   2.340   1.505   0.797 -0.1081 -0.2968  1.6035
+  896SOL     OW 2686   0.205   0.166   2.437 -0.0572 -0.5920 -0.4001
+  896SOL    HW1 2687   0.215   0.113   2.358 -1.3284 -1.2961 -0.0933
+  896SOL    HW2 2688   0.111   0.179   2.445  0.1132 -0.0247  0.6663
+  897SOL     OW 2689   1.144   2.274   2.877 -0.6109  0.0057  0.8021
+  897SOL    HW1 2690   1.199   2.328   2.934  1.3689 -1.4822  0.3300
+  897SOL    HW2 2691   1.123   2.197   2.931  0.5805 -1.0176 -0.1780
+  898SOL     OW 2692   1.494   0.513   0.592  0.5957 -0.1071 -0.5549
+  898SOL    HW1 2693   1.443   0.503   0.511  1.6341  1.4414 -1.3967
+  898SOL    HW2 2694   1.454   0.587   0.637 -1.5533 -2.5560  1.5288
+  899SOL     OW 2695   0.645   2.259   0.469  0.8668 -0.2219  0.1797
+  899SOL    HW1 2696   0.739   2.255   0.487  0.7535 -2.9277  0.1895
+  899SOL    HW2 2697   0.612   2.174   0.499 -1.8530  1.4489  1.8433
+  900SOL     OW 2698   0.576   2.141   2.050 -0.2940  0.2177  0.1375
+  900SOL    HW1 2699   0.668   2.159   2.034 -0.1056 -0.7235  0.1393
+  900SOL    HW2 2700   0.560   2.171   2.139 -0.1891  1.1435 -0.1582
+   3.01125   3.01125   3.01125
diff --git a/tests/physicalvalidation/systems/ens_water_md_verlet_settle_pme_vr_cr/input/system.mdp b/tests/physicalvalidation/systems/ens_water_md_verlet_settle_pme_vr_cr/input/system.mdp
new file mode 100644 (file)
index 0000000..5c23ce0
--- /dev/null
@@ -0,0 +1,11 @@
+nsteps                   = 500000
+dt                       = 0.002
+cutoff-scheme            = verlet
+coulombtype              = PME
+tcoupl                   = v-rescale
+tc-grps                  = system
+ref-t                    = 298.15
+tau-t                    = 0.1
+pcoupl                   = C-rescale
+ref-p                    = 1.0
+compressibility          = 4.5e-5
diff --git a/tests/physicalvalidation/systems/ens_water_md_verlet_settle_pme_vr_cr/input/system.top b/tests/physicalvalidation/systems/ens_water_md_verlet_settle_pme_vr_cr/input/system.top
new file mode 100644 (file)
index 0000000..cfe250d
--- /dev/null
@@ -0,0 +1,34 @@
+[ defaults ]
+; nbfunc        comb-rule       gen-pairs       fudgeLJ fudgeQQ
+1               3               yes             0.5     0.5
+
+[ atomtypes ]
+ opls_111   OW  8     15.99940    -0.834       A    3.15061e-01  6.36386e-01
+ opls_112   HW  1      1.00800     0.417       A    0.00000e+00  0.00000e+00
+
+
+[ moleculetype ]
+; molname      nrexcl
+H2O            2
+
+[ atoms ]
+; id   at type res nr  residu name     at name         cg nr   charge
+1     opls_111  1       H2O              OW             1       -0.834
+2     opls_112  1       H2O             HW1             1        0.417
+3     opls_112  1       H2O             HW2             1        0.417
+
+[ settles ]
+; i    j       funct   length
+1      1       0.09572 0.15139
+
+[ exclusions ]
+1      2       3
+2      1       3
+3      1       2
+
+[ system ]
+Pure Water
+
+[ molecules ]
+; Compound             #mols
+H2O               900
index 9ca12e3b9a99655d177e07525586d302793f1fc5..39c518a79845e8123168f28f989bff27b5bba8cd 100644 (file)
       {"test": "kin_mb", "args": "-t 0.01"}
     ]
   },
+  {
+    "name": "ens_argon_md_verlet_pme_vr_cr",
+    "dir": "ens_argon_md_verlet_pme_vr_cr",
+    "tests": [
+      {"test": "ensemble", "args": "--dtemp 3 0 3 --dpress 0 75 75"},
+      {"test": "kin_mb", "args": "-t 0.01"}
+    ]
+  },
   {
     "name": "ens_argon_md-vv_verlet_pme_nh_mttk",
     "dir": "ens_argon_md-vv_verlet_pme_nh_mttk",
       {"test": "kin_mb", "args": "-t 0.01"}
     ]
   },
+  {
+    "name": "ens_water_md_verlet_settle_pme_vr_cr",
+    "dir": "ens_water_md_verlet_settle_pme_vr_cr",
+    "tests": [
+      {"test": "ensemble", "args": "--dtemp 8 0 8 --dpress 0 300 300"},
+      {"test": "kin_mb", "args": "-t 0.01"}
+    ]
+  },
   {
     "name": "ens_water_md-vv_verlet_none_pme_nh_mttk",
     "dir": "ens_water_md-vv_verlet_none_pme_nh_mttk",